Files @ 48e4e25f37a3
Branch filter:

Location: light9/light9/web/timeline/viewstate.coffee

drewp@bigasterisk.com
starting BrickLayout. WIP
Ignore-this: 4c6ff2700352e5f28fab6f9a7a5f5a3e
class window.ViewState
  constructor: () ->
    # caller updates all these observables
    @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)
    @coveredByDiagramTop = ko.observable(0) # page coords
    # all these are relative to #coveredByDiagram:
    @audioY = ko.observable(0)
    @audioH = ko.observable(0)
    @zoomedTimeY = ko.observable(0)
    @zoomedTimeH = ko.observable(0)
    @rowsY = ko.observable(0)
      
    @fullZoomX = d3.scaleLinear()
    @zoomInX = d3.scaleLinear()

    @zoomAnimSec = .1

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

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

    rightPad = 5 # don't let time adjuster fall off right edge
    @fullZoomX.domain([0, @zoomSpec.duration()])
    @fullZoomX.range([0, @width() - rightPad])

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

  onMouseWheel: (deltaY) ->
    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(@))

  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]
      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)