Changeset - d7e4d5b0d61e
[Not reviewed]
default
0 1 0
Drew Perttula - 7 years ago 2018-05-11 10:11:29
drewp@bigasterisk.com
move animatedZoom. other viewstate api fixes.
Ignore-this: 6d1b59e21f5097c50b6c88a87d3e83da
1 file changed with 31 insertions and 30 deletions:
0 comments (0 inline, 0 general)
light9/web/timeline/timeline.coffee
Show inline comments
 
@@ -92,25 +92,25 @@ 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)
 
      t: ko.observable(20) # songTime
 
    @mouse =
 
      pos: ko.observable($V([0,0]))
 
      
 
    @fullZoomX = d3.scaleLinear()
 
    @zoomInX = d3.scaleLinear()
 

	
 
    ko.computed(@zoomOrLayoutChanged.bind(@))    
 
    
 
  zoomOrLayoutChanged: () ->
 
    log('zoomOrLayoutChanged')
 
    # not for cursor updates
 

	
 
@@ -137,54 +137,73 @@ class ViewState
 
    zs = @zoomSpec
 

	
 
    center = @latestMouseTime()
 
    left = center - zs.t1()
 
    right = zs.t2() - center
 
    scale = Math.pow(1.005, deltaY)
 

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

	
 
    
 
  animatedZoom: (newT1, newT2, secs) ->
 
    fps = 30
 
    oldT1 = @zoomSpec.t1()
 
    oldT2 = @zoomSpec.t2()
 
    lastTime = 0
 
    for step in [0..secs * fps]
 
      frac = step / (secs * fps)
 
      do (frac) =>
 
        gotoStep = =>
 
          @zoomSpec.t1((1 - frac) * oldT1 + frac * newT1)
 
          @zoomSpec.t2((1 - frac) * oldT2 + frac * newT2)
 
        delay = frac * secs * 1000
 
        setTimeout(gotoStep, delay)
 
        lastTime = delay
 
    setTimeout(=>
 
        @zoomSpec.t1(newT1)
 
        @zoomSpec.t2(newT2)
 
      , lastTime + 10)  
 
    
 
class TimelineEditor extends Polymer.Element
 
  @is: 'light9-timeline-editor'
 
  @behaviors: [ Polymer.IronResizableBehavior ]
 
  @properties:
 
    viewState: { type: Object }
 
    debug: {type: String}
 
    graph: {type: Object, notify: true}
 
    project: {type: Object}
 
    setAdjuster: {type: Function, notify: true}
 
    playerSong: {type: String, notify: true}
 
    followPlayerSong: {type: Boolean, notify: true, value: true}
 
    song: {type: String, notify: true}
 
    show: {value: 'http://light9.bigasterisk.com/show/dance2017'}
 
    songTime: {type: Number, notify: true, observer: '_onSongTime'}
 
    songDuration: {type: Number, notify: true, observer: '_onSongDuration'}
 
    songPlaying: {type: Boolean, notify: true}
 
    selection: {type: Object, notify: true}
 
  width: ko.observable(1)
 
  @listeners:
 
    'iron-resize': '_onIronResize'
 
  @observers: [
 
    'setSong(playerSong, followPlayerSong)',
 
    'onGraph(graph)',
 
    ]
 
    
 
  connectedCallback: ->
 
    super.connectedCallback()
 
    ko.options.deferUpdates = true;
 

	
 
    @dia = @$.dia
 
    
 

	
 
    @selection = {hover: ko.observable(null), selected: ko.observable([])}
 

	
 
    window.debug_zoomOrLayoutChangedCount = 0
 
    window.debug_adjUpdateDisplay = 0
 
    
 
    @viewState = new ViewState()
 
    window.viewState = @viewState
 
    @setAdjuster = (adjId, makeAdjustable) =>
 
      ac = @$.adjustersCanvas
 
      setTimeout((()=>ac.setAdjuster(adjId, makeAdjustable)),10)
 

	
 
@@ -204,54 +223,55 @@ class TimelineEditor extends Polymer.Ele
 
      zoomed = @$.zoomed
 
      setupDrop(@$.dia.shadowRoot.querySelector('svg'),
 
                zoomed.$.rows, @, zoomed.onDrop.bind(zoomed))
 

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

	
 
    #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: ->
 
    @width(@offsetWidth)
 
    log('set w to',   @offsetWidth)
 
    @viewState.width(@offsetWidth)
 
  _onSongTime: (t) ->
 
    #@viewState.cursor.t(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)
 
    @viewState.zoomSpec.duration(d)
 
    
 
  setSong: (s) ->
 
    @song = @playerSong if @followPlayerSong
 
  onGraph: (graph) ->
 
    @project = new Project(graph)
 

	
 
  updateDebugSummary: ->
 
    elemCount = (tag) -> document.getElementsByTagName(tag).length
 
    @debug = "#{window.debug_zoomOrLayoutChangedCount} layout change,
 
     #{elemCount('light9-timeline-note')} notes,
 
     #{@selection.selected().length} selected
 
     #{elemCount('light9-timeline-graph-row')} rows,
 
     #{window.debug_adjsCount} adjuster items registered,
 
     #{window.debug_adjUpdateDisplay} adjuster updateDisplay calls,
 
    "
 

	
 
  zoomOrLayoutChanged: ->
 
  
 
    vs = @viewState
 
    
 
  
 
    # todo: these run a lot of work purely for a time change
 
    if @$.zoomed?.$?.audio?
 
      @dia.setTimeAxis(@width(), @$.zoomed.$.audio.offsetTop, vs.zoomInX)
 
      @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,
 
                              @$.zoomed.$.time.offsetHeight,
 
                              @viewState)
 
@@ -283,61 +303,42 @@ class TimelineEditor extends Polymer.Ele
 
  
 

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

	
 
  forwardMouseEventsToAdjustersCanvas: ->
 
    ac = @$.adjustersCanvas
 
    @addEventListener('mousedown', ac.onDown.bind(ac))
 
    @addEventListener('mousemove', ac.onMove.bind(ac))
 
    @addEventListener('mouseup', ac.onUp.bind(ac))
 

	
 
  animatedZoom: (newT1, newT2, secs) ->
 
    fps = 30
 
    oldT1 = @viewState.zoomSpec.t1()
 
    oldT2 = @viewState.zoomSpec.t2()
 
    lastTime = 0
 
    for step in [0..secs * fps]
 
      frac = step / (secs * fps)
 
      do (frac) =>
 
        gotoStep = =>
 
          @viewState.zoomSpec.t1((1 - frac) * oldT1 + frac * newT1)
 
          @viewState.zoomSpec.t2((1 - frac) * oldT2 + frac * newT2)
 
        delay = frac * secs * 1000
 
        setTimeout(gotoStep, delay)
 
        lastTime = delay
 
    setTimeout(=>
 
        @viewState.zoomSpec.t1(newT1)
 
        @viewState.zoomSpec.t2(newT2)
 
      , lastTime + 10)  
 
    
 
  bindKeys: ->
 
    shortcut.add "Ctrl+P", (ev) =>
 
      @$.music.seekPlayOrPause(@latestMouseTime())
 

	
 
    zoomAnimSec = .1
 
    shortcut.add "Ctrl+Escape", =>
 
      @animatedZoom(0, @viewState.zoomSpec.duration(), zoomAnimSec)
 
      @viewState.animatedZoom(0, @viewState.zoomSpec.duration(), zoomAnimSec)
 
    shortcut.add "Shift+Escape", =>
 
      @animatedZoom(@songTime - 2, @viewState.zoomSpec.duration(), zoomAnimSec)
 
      @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
 
        @animatedZoom(newCenter - visSeconds / 2,
 
                      newCenter + visSeconds / 2, zoomAnimSec)
 
        @viewState.animatedZoom(newCenter - visSeconds / 2,
 
                                newCenter + visSeconds / 2, zoomAnimSec)
 
    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)
 
@@ -426,25 +427,25 @@ class TimeZoomed extends Polymer.Element
 
    
 
  onGraph: ->
 
    @graph.runHandler(@gatherNotes.bind(@), 'zoom notes')
 
  gatherNotes: ->
 
    U = (x) => @graph.Uri(x)
 

	
 
    log('assign rows',@song, 'graph has', @graph.quads().length)
 
    graphics = new PIXI.Graphics({nativeLines: true})
 

	
 
    for uri in _.sortBy(@graph.objects(@song, U(':note')), 'uri')
 
      #should only make new ones
 
      # 
 
      child = new Note(@graph, @selection, @dia, uri, @setAdjuster, @song, @viewState.zoomInX)
 
      #child = new Note(@graph, @selection, @dia, uri, @setAdjuster, @song, @viewState.zoomInX)
 
      originTime = @graph.floatValue(uri, U(':originTime'))
 
      effect = @graph.uriValue(uri, U(':effectClass'))
 
      for curve in @graph.objects(uri, U(':curve'))
 
        if @graph.uriValue(curve, U(':attr')).equals(U(':strength'))
 

	
 
          [@pointUris, @worldPts] = @project.getCurvePoints(curve, originTime)
 
          curveWidthCalc = () => @_curveWidth(@worldPts)
 

	
 
          h = 150 #@offsetHeight
 
          screenPts = ($V([@viewState.zoomInX(pt.e(1)), @offsetTop + (1 - pt.e(2)) * h]) for pt in @worldPts)
 
          graphics.beginFill(0xFF3300);
 
          graphics.lineStyle(4, 0xffd900, 1)
0 comments (0 inline, 0 general)