Changeset - a73468c02bce
[Not reviewed]
default
0 3 0
Drew Perttula - 7 years ago 2018-05-27 02:20:42
drewp@bigasterisk.com
new adjuster layout algorithm
Ignore-this: 166c186d1932b6ad737f67135c95e131
3 files changed with 38 insertions and 29 deletions:
0 comments (0 inline, 0 general)
light9/web/timeline/adjustable.coffee
Show inline comments
 
@@ -11,38 +11,38 @@ class Adjustable
 
  # into stuff or to get out from under your finger.
 

	
 
  constructor: (@config) ->
 
    @ctor2()
 

	
 
  ctor2: () ->
 
    # config has:
 
    #   getTarget -> vec2 of current target position
 
    #   getSuggestedTargetOffset -> vec2 pixel offset from target
 
    #   emptyBox -> true if you want no value display
 

	
 
    # updated later by layout algoritm
 
    @centerOffset = $V([0, 0])
 
    @handle = $V([0, 0])
 

	
 
  getDisplayValue: () ->
 
    return '' if @config.emptyBox
 
    defaultFormat = d3.format(".4g")(@_getValue())
 
    if @config.getDisplayValue?
 
      return @config.getDisplayValue(@_getValue(), defaultFormat)
 
    defaultFormat
 

	
 
  getSuggestedCenter: () ->
 
  getSuggestedHandle: () ->
 
    @getTarget().add(@config.getSuggestedTargetOffset())
 

	
 
  getCenter: () -> # vec2 of pixels
 
    @getTarget().add(@centerOffset)
 
  getHandle: () -> # vec2 of pixels
 
    @handle
 

	
 
  getTarget: () -> # vec2 of pixels
 
    @config.getTarget()
 

	
 
  subscribe: (onChange) ->
 
    # change could be displayValue or center or target. This likely
 
    # calls onChange right away if there's any data yet.
 
    throw new Error('not implemented')
 

	
 
  startDrag: () ->
 
    @initialTarget = @getTarget()
 

	
light9/web/timeline/adjusters.coffee
Show inline comments
 
log = console.log
 
Drawing = window.Drawing
 

	
 
maxDist = 60
 

	
 
coffeeElementSetup(class AdjustersCanvas extends Polymer.mixinBehaviors([Polymer.IronResizableBehavior], Polymer.Element)
 
  @is: 'light9-adjusters-canvas'
 
  @getter_properties:
 
    setAdjuster: {type: Function, notify: true }
 
  @getter_observers: [
 
    'updateAllCoords(adjs)'
 
  ]
 
  constructor: ->
 
    super()
 
    @redraw = _.throttle(@_throttledRedraw.bind(@), 60, {leading: false})
 
    @adjs = {}
 
    
 
@@ -62,77 +64,86 @@ coffeeElementSetup(class AdjustersCanvas
 

	
 
    @redraw()
 

	
 
    window.debug_adjsCount = Object.keys(@adjs).length
 

	
 
  updateAllCoords: ->
 
    @redraw()
 

	
 
  _adjAtPoint: (pt) ->
 
    nearest = @qt.find(pt.e(1), pt.e(2))
 
    if nearest?
 
      log('near', nearest.distanceFrom(pt))
 
    if not nearest? or nearest.distanceFrom(pt) > 70
 
    if not nearest? or nearest.distanceFrom(pt) > maxDist
 
      return null
 
    return nearest?.adj
 

	
 
  resizeUpdate: (ev) ->
 
    @$.canvas.width = ev.target.offsetWidth
 
    @$.canvas.height = ev.target.offsetHeight
 
    @canvasCenter = $V([@$.canvas.width / 2, @$.canvas.height / 2])
 
    @redraw()
 

	
 
  _throttledRedraw: () ->
 
    return unless @ctx?
 
    console.time('adjs redraw')
 
    @_layoutCenters()
 
    
 
    @ctx.clearRect(0, 0, @$.canvas.width, @$.canvas.height)
 

	
 
    for adjId, adj of @adjs
 
      ctr = adj.getCenter()
 
      ctr = adj.getHandle()
 
      target = adj.getTarget()
 
      if target.e(1) < 0 or target.e(1) > @$.canvas.width or target.e(2) < 0 or target.e(2) > @$.canvas.height
 
        continue
 
      @_drawConnector(ctr, target)
 
      
 
      @_drawAdjuster(adj.getDisplayValue(),
 
                     Math.floor(ctr.e(1)) - 20, Math.floor(ctr.e(2)) - 10,
 
                     Math.floor(ctr.e(1)) + 20, Math.floor(ctr.e(2)) + 10)
 
    console.timeEnd('adjs redraw')
 

	
 
  _layoutCenters: ->
 
    # push Adjustable centers around to avoid overlaps
 
    # Todo: also don't overlap inlineattr boxes
 
    # Todo: don't let their connector lines cross each other
 
    @qt = d3.quadtree([], ((d)->d.e(1)), ((d)->d.e(2)))
 
    @qt.extent([[0,0], [8000,8000]])
 

	
 
    for _, adj of @adjs
 
      desired = adj.getSuggestedCenter()
 
      output = desired
 
      for tries in [0...4]
 
        nearest = @qt.find(output.e(1), output.e(2))
 
      adj.handle = adj.getSuggestedHandle()
 
      
 
    for tries in [0...5]
 
      for _, adj of @adjs
 
        current = adj.handle
 
        @qt.remove(current)
 
        nearest = @qt.find(current.e(1), current.e(2), maxDist)
 
        if nearest
 
          dist = output.distanceFrom(nearest)
 
          if dist < 60
 
            away = output.subtract(nearest).toUnitVector()
 
            toScreenCenter = $V([500,200]).subtract(output).toUnitVector()
 
            output = output.add(away.x(60).add(toScreenCenter.x(10)))
 
          dist = current.distanceFrom(nearest)
 
          if dist < maxDist
 
            away = current.subtract(nearest).toUnitVector()
 
            toScreenCenter = @canvasCenter.subtract(current).toUnitVector()
 
            current = current.add(away.x(20).add(toScreenCenter.x(2)))
 
            marg = 10
 
            current = $V([Math.max(marg, Math.min(@$.canvas.width - marg, current.e(1))),
 
                         Math.max(marg, Math.min(@$.canvas.height - marg, current.e(2)))])
 
            adj.handle = current
 
        current.adj = adj
 
        @qt.add(current)
 

	
 
      if -50 < output.e(1) < 20 # mostly for zoom-left
 
        output.setElements([
 
          Math.max(20, output.e(1)),
 
          output.e(2)])
 
      #if -50 < output.e(1) < 20 # mostly for zoom-left
 
      #  output.setElements([
 
      #    Math.max(20, output.e(1)),
 
      #    output.e(2)])
 
        
 
      adj.centerOffset = output.subtract(adj.getTarget())
 
      output.adj = adj
 
      @qt.add(output)
 

	
 
  _drawConnector: (ctr, target) ->
 
    @ctx.strokeStyle = '#aaa'
 
    @ctx.lineWidth = 2
 
    @ctx.beginPath()
 
    Drawing.line(@ctx, ctr, target)
 
    @ctx.stroke()
 
    
 
  _drawAdjuster: (label, x1, y1, x2, y2) ->
 
    radius = 8
 

	
 
    @ctx.shadowColor = 'black'
 
    @ctx.shadowBlur = 15
light9/web/timeline/timeline.coffee
Show inline comments
 
@@ -43,38 +43,36 @@ class Project
 
    newCurve = @graph.nextNumberedResource("#{newNote.value}c")
 
    points = @graph.nextNumberedResources("#{newCurve.value}p", 4)
 

	
 
    curveQuads = [
 
        quad(song, U(':note'), newNote)
 
        quad(newNote, U('rdf:type'), U(':Note'))
 
        quad(newNote, U(':originTime'), @graph.LiteralRoundedFloat(dropTime))
 
        quad(newNote, U(':effectClass'), effect)
 
        quad(newNote, U(':curve'), newCurve)
 
        quad(newCurve, U('rdf:type'), U(':Curve'))
 
        quad(newCurve, U(':attr'), U(':strength'))
 
      ]
 

	
 
    pointQuads = []
 

	
 

	
 

	
 
    for i in [0...4]
 
      pt = points[i]
 
      pointQuads.push(quad(newCurve, U(':point'), pt))
 
      pointQuads.push(quad(pt, U(':time'), @graph.LiteralRoundedFloat(i/3 * desiredWidthT)))
 
      pointQuads.push(quad(pt, U(':value'), @graph.LiteralRoundedFloat(i == 1 or i == 2)))
 

	
 
    patch = {
 
      delQuads: []
 
      addQuads: curveQuads.concat(pointQuads)
 
      }
 
    }
 
    @graph.applyAndSendPatch(patch)
 

	
 
  getCurvePoints: (curve, xOffset) ->
 
    worldPts = []
 
    uris = @graph.objects(curve, @graph.Uri(':point'))
 
    for pt in uris
 
      tm = @graph.floatValue(pt, @graph.Uri(':time'))
 
      val = @graph.floatValue(pt, @graph.Uri(':value'))
 
      v = $V([xOffset + tm, val])
 
      v.uri = pt
 
      worldPts.push(v)
 
    worldPts.sort((a,b) -> a.e(1) > b.e(1))
 
@@ -236,33 +234,33 @@ coffeeElementSetup(class TimelineEditor 
 
        @project.deleteNote(@graph.Uri(@song), note, @selection)
 

	
 
  makeZoomAdjs: ->
 
    yMid = => @$.audio.offsetTop + @$.audio.offsetHeight / 2
 

	
 
    valForPos = (pos) =>
 
      x = pos.e(1)
 
      t = @viewState.fullZoomX.invert(x)
 
    @setAdjuster('zoom-left', => new AdjustableFloatObservable({
 
      observable: @viewState.zoomSpec.t1,
 
      getTarget: () =>
 
        $V([@viewState.fullZoomX(@viewState.zoomSpec.t1()), yMid()])
 
      getSuggestedTargetOffset: () => $V([50, 0])
 
      getSuggestedTargetOffset: () => $V([50, 10])
 
      getValueForPos: valForPos
 
    }))
 

	
 
    @setAdjuster('zoom-right', => new AdjustableFloatObservable({
 
      observable: @viewState.zoomSpec.t2,
 
      getTarget: () =>
 
        $V([@viewState.fullZoomX(@viewState.zoomSpec.t2()), yMid()])
 
      getSuggestedTargetOffset: () => $V([-50, 0])
 
      getSuggestedTargetOffset: () => $V([-50, 10])
 
      getValueForPos: valForPos
 
    }))
 

	
 
    panObs = ko.pureComputed({
 
      read: () =>
 
        (@viewState.zoomSpec.t1() + @viewState.zoomSpec.t2()) / 2
 
      write: (value) =>
 
        zs = @viewState.zoomSpec
 
        span = zs.t2() - zs.t1()
 
        zs.t1(value - span / 2)
 
        zs.t2(value + span / 2)
 
    })
0 comments (0 inline, 0 general)