Changeset - 7d2d20f06d1d
[Not reviewed]
default
0 3 0
Drew Perttula - 7 years ago 2018-05-12 23:02:26
drewp@bigasterisk.com
cleanup viewstate change notifications. Use ko.computed more.
Ignore-this: 3da1a8c7a09f05238ce3c640b05347b4
3 files changed with 54 insertions and 63 deletions:
0 comments (0 inline, 0 general)
light9/web/timeline/cursor_canvas.coffee
Show inline comments
 

	
 
Polymer
 
  is: 'light9-cursor-canvas'
 
  behaviors: [ Polymer.IronResizableBehavior ]
 
  listeners: 'iron-resize': 'update'
 

	
 
  properties:
 
    viewState: { type: Object, notify: true, observer: "onViewState" }
 
  ready: ->
 
    @mouseX = 0
 
    @mouseY = 0
 
    @cursorPath = null
 
    @ctx = @$.canvas.getContext('2d')
 

	
 
  onViewState: ->
 
    ko.computed(@redrawCursor.bind(@))
 

	
 
  update: (ev) ->
 
    @$.canvas.width = ev.target.offsetWidth
 
    @$.canvas.height = ev.target.offsetHeight
 
    @redraw()
 

	
 
  setMouse: (pos) ->
 
    @mouseX = pos.e(1)
 
    @mouseY = pos.e(2)
 
    @redraw()
 

	
 
  setCursor: (y1, h1, y2, h2, viewState) ->
 
    
 
    xZoomedOut = viewState.fullZoomX(viewState.latestMouseTime())
 
    xZoomedIn = viewState.mouse.pos().e(1)
 
  redrawCursor: ->
 
    vs = @viewState
 
    dependOn = [vs.zoomSpec.t1(), vs.zoomSpec.t2()]
 
    xZoomedOut = vs.fullZoomX(vs.latestMouseTime())
 
    xZoomedIn = vs.mouse.pos().e(1)
 

	
 
    @cursorPath = {
 
      top0: $V([xZoomedOut, y1])
 
      top1: $V([xZoomedOut, y1 + h1])
 
      mid0: $V([xZoomedIn + 2, y2 + h2])
 
      mid1: $V([xZoomedIn - 2, y2 + h2])
 
      mid2: $V([xZoomedOut - 1, y1 + h1])
 
      mid3: $V([xZoomedOut + 1, y1 + h1])
 
      bot0: $V([xZoomedIn, y2 + h2])
 
      top0: $V([xZoomedOut, vs.audioY()])
 
      top1: $V([xZoomedOut, vs.audioY() + vs.audioH()])
 
      mid0: $V([xZoomedIn + 2, vs.zoomedTimeY() + vs.zoomedTimeH()])
 
      mid1: $V([xZoomedIn - 2, vs.zoomedTimeY() + vs.zoomedTimeH()])
 
      mid2: $V([xZoomedOut - 1, vs.audioY() + vs.audioH()])
 
      mid3: $V([xZoomedOut + 1, vs.audioY() + vs.audioH()])
 
      bot0: $V([xZoomedIn, vs.zoomedTimeY() + vs.zoomedTimeH()])
 
      bot1: $V([xZoomedIn, @offsetParent.offsetHeight])
 
    }
 
    @redraw()
 

	
 
  redraw: ->
 
    return unless @ctx
 
    @ctx.clearRect(0, 0, @$.canvas.width, @$.canvas.height)
 

	
 
    @ctx.strokeStyle = '#fff'
 
    @ctx.lineWidth = 0.5
 
    @ctx.beginPath()
 
    Drawing.line(@ctx, $V([0, @mouseY]), $V([@$.canvas.width, @mouseY]))
light9/web/timeline/timeline-elements.html
Show inline comments
 
@@ -73,13 +73,13 @@
 
                                   show="{{show}}"
 
                                   view-state="{{viewState}}">
 
      </light9-timeline-time-zoomed>
 
      <light9-timeline-diagram-layer id="dia" selection="{{selection}}"></light9-timeline-diagram-layer>
 
      <light9-adjusters-canvas id="adjustersCanvas" set-adjuster="{{setAdjuster}}">
 
      </light9-adjusters-canvas>
 
      <light9-cursor-canvas id="cursorCanvas"></light9-cursor-canvas>
 
      <light9-cursor-canvas id="cursorCanvas" view-state="{{viewState}}"></light9-cursor-canvas>
 
    </div>
 
  </template>
 
  
 
</dom-module>
 

	
 
<!-- the whole section that pans/zooms in time (most of the editor) -->
light9/web/timeline/timeline.coffee
Show inline comments
 
@@ -92,48 +92,51 @@ class Project
 
    if note in selection.selected()
 
      selection.selected(_.without(selection.selected(), note))
 

	
 
class ViewState
 
  constructor: () ->
 
    # caller updates all these observables
 
    @width = ko.observable(500)
 
    @zoomSpec =
 
      duration: ko.observable(100) # current song duration
 
      t1: ko.observable(0)
 
      t2: ko.observable(100)
 
    @cursor =
 
      t: ko.observable(20) # songTime
 
    @mouse =
 
      pos: ko.observable($V([0,0]))
 
    @width = ko.observable(500)
 
    @audioY = ko.observable(0)
 
    @audioH = ko.observable(0)
 
    @zoomedTimeY = ko.observable(0)
 
    @zoomedTimeH = ko.observable(0)
 
      
 
    @fullZoomX = d3.scaleLinear()
 
    @zoomInX = d3.scaleLinear()
 

	
 
    ko.computed(@zoomOrLayoutChanged.bind(@))    
 
    @zoomAnimSec = .1
 

	
 
    ko.computed(@maintainZoomLimitsAndScales.bind(@))    
 
 
 
  setWidth: (w) ->
 
    @width(w)
 
    @zoomOrLayoutChanged() # before other handleers run
 
    @maintainZoomLimitsAndScales() # before other handlers run
 
    
 
  zoomOrLayoutChanged: () ->
 
    log('zoomOrLayoutChanged')
 
  maintainZoomLimitsAndScales: () ->
 
    log('maintainZoomLimitsAndScales')
 
    # not for cursor updates
 

	
 
    window.debug_zoomOrLayoutChangedCount++
 

	
 
    if @zoomSpec.t1() < 0
 
      @zoomSpec.t1(0)
 
    if @zoomSpec.duration() and @zoomSpec.t2() > @zoomSpec.duration()
 
      @zoomSpec.t2(@zoomSpec.duration())
 

	
 
    @fullZoomX.domain([0, @zoomSpec.duration()])
 
    @fullZoomX.range([0, @width()])
 

	
 
    @zoomInX.domain([@zoomSpec.t1(), @zoomSpec.t2()])
 
    @zoomInX.range([0, @width()])
 
    log('update zoomInX')
 
    
 
  latestMouseTime: ->
 
    @zoomInX.invert(@mouse.pos().e(1))
 

	
 
  onMouseWheel: (deltaY) ->
 
    zs = @zoomSpec
 
@@ -144,13 +147,25 @@ class ViewState
 
    scale = Math.pow(1.005, deltaY)
 

	
 
    zs.t1(center - left * scale)
 
    zs.t2(center + right * scale)
 
    log('view to', ko.toJSON(@))
 

	
 
    
 
  frameCursor: ->
 
    zs = @zoomSpec
 
    visSeconds = zs.t2() - zs.t1()
 
    margin = visSeconds * .4
 
    # buggy: really needs t1/t2 to limit their ranges
 
    if @cursor.t() < zs.t1() or @cursor.t() > zs.t2() - visSeconds * .6
 
      newCenter = @cursor.t() + margin
 
      @animatedZoom(newCenter - visSeconds / 2,
 
                    newCenter + visSeconds / 2, @zoomAnimSec)
 
  frameToEnd: ->
 
    @animatedZoom(@cursor.t() - 2, @zoomSpec.duration(), @zoomAnimSec)
 
  frameAll: ->
 
    @animatedZoom(0, @zoomSpec.duration(), @zoomAnimSec)
 
  animatedZoom: (newT1, newT2, secs) ->
 
    fps = 30
 
    oldT1 = @zoomSpec.t1()
 
    oldT2 = @zoomSpec.t2()
 
    lastTime = 0
 
    for step in [0..secs * fps]
 
@@ -206,13 +221,12 @@ class TimelineEditor extends Polymer.mix
 
    @setAdjuster = (adjId, makeAdjustable) =>
 
      ac = @$.adjustersCanvas
 
      setTimeout((()=>ac.setAdjuster(adjId, makeAdjustable)),10)
 

	
 
    ko.computed(@zoomOrLayoutChanged.bind(@))
 
    setTimeout =>
 
      ko.computed(@songTimeChanged.bind(@))
 

	
 
      @trackMouse()
 
      @bindKeys()
 
      @bindWheelZoom(@dia)
 
      setTimeout => # depends on child node being ready
 
          @forwardMouseEventsToAdjustersCanvas()
 
@@ -225,22 +239,26 @@ class TimelineEditor extends Polymer.mix
 
                zoomed.$.rows, @, zoomed.onDrop.bind(zoomed))
 

	
 
      setInterval(@updateDebugSummary.bind(@), 100)
 
    , 500
 

	
 
    @addEventListener('iron-resize', @_onIronResize.bind(@))
 
    @_onIronResize()
 
    setTimeout(@_onIronResize.bind(@), 1000) # when children are packed
 
    
 
    #if anchor == loadtest
 
    #  add note and delete it repeatedly
 
    #  disconnect the graph, make many notes, drag a point over many steps, measure lag somewhere
 

	
 
  _onIronResize: ->
 
    @viewState.setWidth(@offsetWidth)
 

	
 
    log('changed width')
 
    @viewState.audioY(@$.audio.offsetTop)
 
    @viewState.audioH(@$.audio.offsetHeight)
 
    @viewState.zoomedTimeY(@$.zoomed.$.time.offsetTop) if @$.zoomed?.$?.time?
 
    @viewState.zoomedTimeH(30) #@$.zoomed.$.time.offsetHeight)
 
      
 
    log('editor resized')
 
  _onSongTime: (t) ->
 
    @viewState.cursor.t(t)
 
  _onSongDuration: (d) ->
 
    d = 700 if d < 1 # bug is that asco isn't giving duration, but 0 makes the scale corrupt
 
    @viewState.zoomSpec.duration(d)
 
    
 
@@ -257,33 +275,21 @@ class TimelineEditor extends Polymer.mix
 
     #{elemCount('light9-timeline-graph-row')} rows,
 
     #{window.debug_adjsCount} adjuster items registered,
 
     #{window.debug_adjUpdateDisplay} adjuster updateDisplay calls,
 
    "
 

	
 
  zoomOrLayoutChanged: ->
 
  
 
    vs = @viewState
 
    
 
    vs.width()
 
  
 
    # todo: these run a lot of work purely for a time change
 
    if @$.zoomed?.$?.audio?
 
      vs.zoomSpec.t1()
 
      #@dia.setTimeAxis(vs.width(), @$.zoomed.$.audio.offsetTop, vs.zoomInX)
 
      @$.adjustersCanvas.updateAllCoords()
 

	
 
    # cursor needs update when layout changes, but I don't want
 
    # zoom/layout to depend on the playback time
 
    setTimeout(@songTimeChanged.bind(@), 1)
 

	
 
  songTimeChanged: ->
 
    return unless @$.zoomed?.$?.time?
 
    @$.cursorCanvas.setCursor(@$.audio.offsetTop, @$.audio.offsetHeight,
 
                              @$.zoomed.$.time.offsetTop,
 
                              30,#@$.zoomed.$.time.offsetHeight,
 
                              @viewState)
 
    
 
  trackMouse: ->
 
    # not just for show- we use the mouse pos sometimes
 
    for evName in ['mousemove', 'touchmove']
 
      @addEventListener evName, (ev) =>
 
        ev.preventDefault()
 

	
 
@@ -291,25 +297,22 @@ class TimelineEditor extends Polymer.mix
 
        if ev.touches?.length
 
          ev = ev.touches[0]
 

	
 
        root = @$.cursorCanvas.getBoundingClientRect()
 
        @viewState.mouse.pos($V([ev.pageX - root.left, ev.pageY - root.top]))
 

	
 
        @$.cursorCanvas.setMouse(@viewState.mouse.pos())
 
        # should be controlled by a checkbox next to follow-player-song-choice
 
        @sendMouseToVidref() unless window.location.hash.match(/novidref/)
 

	
 
  sendMouseToVidref: ->
 
    now = Date.now()
 
    if (!@$.vidrefLastSent? || @$.vidrefLastSent < now - 200) && !@songPlaying
 
      @$.vidrefTime.body = {t: @latestMouseTime(), source: 'timeline'}
 
      @$.vidrefTime.generateRequest()
 
      @$.vidrefLastSent = now
 

	
 
  
 

	
 
  bindWheelZoom: (elem) ->
 
    elem.addEventListener 'mousewheel', (ev) =>
 
      @viewState.onMouseWheel(ev.deltaY)
 

	
 
  forwardMouseEventsToAdjustersCanvas: ->
 
    ac = @$.adjustersCanvas
 
@@ -317,36 +320,23 @@ class TimelineEditor extends Polymer.mix
 
    @addEventListener('mousemove', ac.onMove.bind(ac))
 
    @addEventListener('mouseup', ac.onUp.bind(ac))
 

	
 
  bindKeys: ->
 
    shortcut.add "Ctrl+P", (ev) =>
 
      @$.music.seekPlayOrPause(@latestMouseTime())
 

	
 
    zoomAnimSec = .1
 
    shortcut.add "Ctrl+Escape", =>
 
      @viewState.animatedZoom(0, @viewState.zoomSpec.duration(), zoomAnimSec)
 
    shortcut.add "Shift+Escape", =>
 
      @viewState.animatedZoom(@songTime - 2, @viewState.zoomSpec.duration(), zoomAnimSec)
 
    shortcut.add "Escape", =>
 
      zs = @viewState.zoomSpec
 
      visSeconds = zs.t2() - zs.t1()
 
      margin = visSeconds * .4
 
      # buggy: really needs t1/t2 to limit their ranges
 
      if @songTime < zs.t1() or @songTime > zs.t2() - visSeconds * .6
 
        newCenter = @songTime + margin
 
        @viewState.animatedZoom(newCenter - visSeconds / 2,
 
                                newCenter + visSeconds / 2, zoomAnimSec)
 
    shortcut.add "Ctrl+Escape", => @viewState.frameAll()
 
    shortcut.add "Shift+Escape", => @viewState.frameToEnd()
 
    shortcut.add "Escape", => @viewState.frameCursor()
 
    shortcut.add "L", =>
 
      @$.adjustersCanvas.updateAllCoords()
 
    shortcut.add 'Delete', =>
 
      for note in @selection.selected()
 
        @project.deleteNote(@song, note, @selection)
 

	
 
  makeZoomAdjs: ->
 
    yMid = => @$.audio.offsetTop + @$.audio.offsetHeight / 2
 
    dur = @viewState.zoomSpec.duration
 
    
 
    valForPos = (pos) =>
 
        x = pos.e(1)
 
        t = @fullZoomX.invert(x)
 
    @setAdjuster('zoom-left', => new AdjustableFloatObservable({
 
      observable: @viewState.zoomSpec.t1,
0 comments (0 inline, 0 general)