Changeset - 37dd11031bcf
[Not reviewed]
default
0 3 0
Drew Perttula - 8 years ago 2017-04-06 10:04:27
drewp@bigasterisk.com
draw timeline adjusters in canvas
Ignore-this: e9e518e7a0700da1e7771dd559ddc7ed
3 files changed with 195 insertions and 105 deletions:
0 comments (0 inline, 0 general)
light9/web/timeline/adjustable.coffee
Show inline comments
 
log = console.log
 

	
 
class Adjustable
 
  # Some value you can edit in the UI, probably by dragging stuff. May
 
  # have a <light9-timeline-adjuster> associated. This object does the
 
  # Some value you can edit in the UI, probably by dragging
 
  # stuff. Drawn by light9-adjusters-canvas. This object does the
 
  # layout and positioning.
 
  #
 
  # The way dragging should work is that you start in the yellow *adj
 
  # widget*, wherever it is, but your drag is moving the *target*. The
 
  # adj will travel around too, but it may do extra moves to not bump
 
  # into stuff or to get out from under your finger.
 

	
 
  constructor: (@config) ->
 
    # config has:
 
    #   getTarget -> vec2 of current target position
 
    #   getSuggestedTargetOffset -> vec2 pixel offset from target
 
    #   emptyBox -> true if you want no value display
light9/web/timeline/timeline-elements.html
Show inline comments
 
@@ -19,25 +19,25 @@
 
         flex-direction: column;
 
         position: relative;
 
         border: 1px solid black;
 
         overflow: hidden;
 
     }
 
     light9-timeline-audio {
 
         width: 100%;
 
         height: 30px;
 
     }
 
     light9-timeline-time-zoomed {
 
         flex-grow: 1;
 
     }
 
     #dia, #adjusters, #cursorCanvas {
 
     #dia, #adjusters, #cursorCanvas, #adjustersCanvas {
 
         position: absolute;
 
         left: 0; top: 0; right: 0; bottom: 0;
 
     }
 
     #debug {
 
      background: white;
 
      font-family: monospace;
 
      font-size: 125%;
 
      height: 15px;
 
     }
 
    </style>
 
    <div>
 
      <rdfdb-synced-graph graph="{{graph}}"></rdfdb-synced-graph>
 
@@ -56,31 +56,26 @@
 
                           show="{{show}}"
 
                           song="{{song}}"></light9-timeline-audio>
 
    <light9-timeline-time-zoomed id="zoomed"
 
                                 graph="{{graph}}"
 
                                 dia="{{dia}}"
 
                                 set-adjuster="{{setAdjuster}}"
 
                                 song="{{song}}"
 
                                 show="{{show}}"
 
                                 zoom="{{viewState.zoomSpec}}"
 
                                 zoom-in-x="{{zoomInX}}">
 
    </light9-timeline-time-zoomed>
 
    <light9-timeline-diagram-layer id="dia"></light9-timeline-diagram-layer>
 
    <light9-timeline-adjusters id="adjusters"
 
                               dia="{{dia}}"
 
                               graph="{{graph}}"
 
                               song="{{song}}"
 
                               set-adjuster="{{setAdjuster}}"
 
                               zoom-in-x="{{zoomInX}}">
 
    </light9-timeline-adjusters>
 
    <light9-adjusters-canvas id="adjustersCanvas" set-adjuster="{{setAdjuster}}">
 
    </light9-adjusters-canvas>
 
    <light9-cursor-canvas id="cursorCanvas"></light9-cursor-canvas>
 
  </template>
 
 
 
</dom-module>
 

	
 
<!-- the whole section that pans/zooms in time (most of the editor) -->
 
<dom-module id="light9-timeline-time-zoomed">
 
  <template>
 
    <style>
 
     :host {
 
         display: flex;
 
         height: 100%;
 
@@ -173,24 +168,35 @@ background: rgba(126, 52, 245, 0.0784313
 

	
 
<dom-module id="light9-cursor-canvas">
 
  <template>
 
    <style>
 
     #canvas, :host {
 
         pointer-events: none;
 
     }
 
    </style>
 
    <canvas id="canvas"></canvas>
 
  </template>
 
</dom-module>
 
      
 
<dom-module id="light9-adjusters-canvas">
 
  <template>
 
    <style>
 
     #canvas, :host {
 
         pointer-events: none;
 
     }
 
    </style>
 
    <canvas id="canvas"></canvas>
 
  </template>
 
</dom-module>
 
      
 

	
 
<!-- seconds labels -->
 
<dom-module id="light9-timeline-time-axis">
 
  <template>
 
    <style>
 
     div {
 
         width: 100%;
 
         height: 31px;
 
     }
 
    </style>
 
    <div></div>
 
  </template>
 
@@ -222,41 +228,38 @@ background: rgba(126, 52, 245, 0.0784313
 
         /* outline: 2px solid red; */
 
     }
 
    </style>
 
    <light9-timeline-note-inline-attrs rect="{{inlineRect}}"
 
                                       graph="{{graph}}"
 
                                       song="{{song}}"
 
                                       uri="{{uri}}"
 
    >
 
    </light9-timeline-note-inline-attrs>
 
  </template>
 
</dom-module>
 

	
 
<!-- All the adjusters you can edit or select.
 
<!-- All the adjusters you can edit or select. Tells a light9-adjusters-canvas how to draw them. Probabaly doesn't need to be an element.
 
     This element manages their layout and suppresion.
 
     Owns the selection.
 
     Maybe includes selecting things that don't even have adjusters.
 
     Maybe manages the layout of other labels and text too, to avoid overlaps.
 
   -->
 
<dom-module id="light9-timeline-adjusters">
 
  <template>
 
    <style>
 
     :host {
 
         pointer-events: none; /* restored on the individual adjusters */
 
     }
 

	
 
    </style>
 
    <div id="all">
 
      <!-- light9-timeline-adjuster repeated here  -->
 
    </div>
 
  </template>
 
</dom-module>
 

	
 
<!-- Yellow dotted handle that you can adjust to edit something.
 
     Knows an attribute to edit and the true screen location, even if
 
     parent <light9-timeline-adjusters> has offset us a bit to avoid a
 
     text overlap.
 
     Draws affordance arrows and a connector line if we're far
 
     away from the point that we edit.
 
     May grow to a bigger editor when you click or drag.
 
   -->
 
<dom-module id="light9-timeline-adjuster">
light9/web/timeline/timeline.coffee
Show inline comments
 
@@ -61,69 +61,69 @@ Polymer
 
    
 
    @viewState =
 
      zoomSpec:
 
        duration: ko.observable(100)
 
        t1: ko.observable(0) # need validation to stay in bounds and not go too close
 
        t2: ko.observable(100)
 
      cursor:
 
        t: ko.observable(20)
 
      mouse:
 
        pos: ko.observable($V([0,0]))
 
    @fullZoomX = d3.scaleLinear()
 
    @zoomInX = d3.scaleLinear()
 
    @setAdjuster = @$.adjusters.setAdjuster.bind(@$.adjusters)
 
    @setAdjuster = @$.adjustersCanvas.setAdjuster.bind(@$.adjustersCanvas)
 

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

	
 
    #if anchor == loadtest
 
    #  add note and delete it repeatedly
 
    #  disconnect the graph, make many notes, drag a point over many steps, measure lag somewhere
 

	
 
  updateDebugSummary: ->
 
    elemCount = (tag) -> document.getElementsByTagName(tag).length
 
    @debug = "#{window.debug_zoomOrLayoutChangedCount} layout change,
 
     #{elemCount('light9-timeline-note')} notes,
 
     #{elemCount('light9-timeline-adjuster')} adjusters,
 
     #{elemCount('light9-timeline-graph-row')} rows,
 
     #{window.debug_adjsCount} adjuster items registered,
 
     #{window.debug_adjUpdateDisplay} adjuster updateDisplay calls,
 
    "
 
    
 
  attached: ->
 
    @dia = @$.dia
 
    ko.computed(@zoomOrLayoutChanged.bind(@)).extend({rateLimit: 5})
 
    ko.computed(@songTimeChanged.bind(@))
 

	
 
    @trackMouse()
 
    @bindKeys()
 
    @bindWheelZoom()
 
    @forwardMouseEventsToAdjustersCanvas()
 

	
 
    @makeZoomAdjs()
 

	
 
  zoomOrLayoutChanged: ->
 
    # not for cursor updates
 

	
 
    window.debug_zoomOrLayoutChangedCount++
 
    @fullZoomX.domain([0, @viewState.zoomSpec.duration()])
 
    @fullZoomX.range([0, @width()])
 

	
 
    # had trouble making notes update when this changes
 
    zoomInX = d3.scaleLinear()
 
    zoomInX.domain([@viewState.zoomSpec.t1(), @viewState.zoomSpec.t2()])
 
    zoomInX.range([0, @width()])
 
    @zoomInX = zoomInX
 

	
 
    # todo: these run a lot of work purely for a time change    
 
    @dia.setTimeAxis(@width(), @$.zoomed.$.audio.offsetTop, @zoomInX)
 
    @$.adjusters.updateAllCoords()
 
    @$.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: ->
 
    @$.cursorCanvas.setCursor(@$.audio.offsetTop, @$.audio.offsetHeight,
 
                              @$.zoomed.$.time.offsetTop,
 
                              @$.zoomed.$.time.offsetHeight,
 
                              @fullZoomX, @zoomInX, @viewState.cursor)
 
    
 
  trackMouse: ->
 
@@ -155,24 +155,30 @@ Polymer
 
  bindWheelZoom: ->
 
    @$.zoomed.addEventListener 'mousewheel', (ev) =>
 
      zs = @viewState.zoomSpec
 

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

	
 
      zs.t1(center - left * scale)
 
      zs.t2(center + right * scale)
 

	
 
  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
 
@@ -193,25 +199,25 @@ Polymer
 
    shortcut.add "Shift+Escape", =>
 
      @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)
 
    shortcut.add "L", =>
 
      @$.adjusters.updateAllCoords()
 
      @$.adjustersCanvas.updateAllCoords()
 

	
 
  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,
 
      getTarget: () =>
 
        $V([@fullZoomX(@viewState.zoomSpec.t1()), yMid()])
 
@@ -565,104 +571,27 @@ Polymer
 
        @colorScaleFromGraph = value
 
        @colorScale = value
 
        @existingColorScaleSetting = setting
 
    if @existingColorScaleSetting == null
 
      @colorScaleFromGraph = '#ffffff'
 
      @colorScale = '#ffffff'
 

	
 

	
 
  onDel: ->
 
    patch = {delQuads: [{subject: @song, predicate: @graph.Uri(':note'), object: @uri, graph: @song}], addQuads: []}
 
    @graph.applyAndSendPatch(patch)
 

	
 

	
 
Polymer
 
  is: "light9-timeline-adjusters"
 
  properties:
 
    adjs: { type: Object, notify: true }, # adjId: Adjustable
 
    dia: { type: Object }
 

	
 
  ready: ->
 
    @adjs = {}
 
    
 
  setAdjuster: (adjId, makeAdjustable) ->
 
    # callers register/unregister the Adjustables they want us to make
 
    # adjuster elements for. Caller invents adjId.  makeAdjustable is
 
    # a function returning the Adjustable or it is null to clear any
 
    # adjusters with this id.
 

	
 
    if not @adjs[adjId] or not makeAdjustable?
 
      if not makeAdjustable?
 
        delete @adjs[adjId]
 
      else
 
        adj = makeAdjustable()
 
        @adjs[adjId] = adj
 
        adj.id = adjId
 
      @debounce('adjsChanged', @adjsChanged.bind(@), 1)
 
    else
 
      for e in @$.all.children
 
        if e.id == adjId
 
          e.updateDisplay()
 

	
 
    window.debug_adjsCount = Object.keys(@adjs).length
 
    
 
  adjsChanged: ->
 
    updateChildren @$.all, Object.keys(@adjs), (newUri) =>
 
      child = document.createElement('light9-timeline-adjuster')
 
      child.dia = @dia
 
      child.graph = @graph
 
      child.uri = newUri
 
      child.id = newUri
 
      child.visible = true
 
      child.adj = @adjs[newUri]
 
      return child
 
    @updateAllCoords()
 

	
 
  layoutCenters: ->
 
    # push Adjustable centers around to avoid overlaps
 
    qt = d3.quadtree()
 
    qt.extent([[0,0], [8000,8000]])
 
    for _, adj of @adjs
 
      desired = adj.getSuggestedCenter()
 
      output = desired
 
      for tries in [0...2]
 
        nearest = qt.find(output.e(1), output.e(2))
 
        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)))
 

	
 
      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())
 
      qt.add(output.elements)
 

	
 
  updateAllCoords: ->
 
    @layoutCenters()
 
    
 
    for elem in @querySelectorAll('light9-timeline-adjuster')
 
      elem.updateDisplay()
 
    
 

	
 
Polymer
 
  is: 'light9-timeline-adjuster'
 
  properties:
 
    graph: { type: Object, notify: true }
 
    adj: { type: Object, notify: true }
 
    id: { type: String, notify: true }
 
class deleteme
 
  go: ->
 
    visible: { type: Boolean, notify: true }
 
    
 
    displayValue: { type: String }
 
    centerStyle: { type: Object }
 
    spanClass: { type: String, value: '' }
 

	
 
  observer: [
 
    'onAdj(graph, adj, dia, id, visible)'
 
    ]
 
  onAdj:  ->
 
    log('onAdj', @id)
 
    @adj.subscribe(@updateDisplay.bind(this))
 
@@ -705,24 +634,46 @@ Polymer
 
svgPathFromPoints = (pts) ->
 
  out = ''
 
  pts.forEach (p) ->
 
    p = p.elements if p.elements # for vec2
 
    if out.length == 0
 
      out = 'M '
 
    else
 
      out += 'L '
 
    out += '' + p[0] + ',' + p[1] + ' '
 
    return
 
  out
 

	
 
_line = (ctx, p1, p2) ->
 
  ctx.moveTo(p1.e(1), p1.e(2))
 
  ctx.lineTo(p2.e(1), p2.e(2))
 

	
 
# http://stackoverflow.com/a/4959890
 
_roundRect = (ctx, sx,sy,ex,ey,r) ->
 
    d2r = Math.PI/180
 
    r = ( ( ex - sx ) / 2 ) if ( ex - sx ) - ( 2 * r ) < 0 # ensure that the radius isn't too large for x
 
    r = ( ( ey - sy ) / 2 ) if ( ey - sy ) - ( 2 * r ) < 0 # ensure that the radius isn't too large for y
 
    ctx.beginPath();
 
    ctx.moveTo(sx+r,sy);
 
    ctx.lineTo(ex-r,sy);
 
    ctx.arc(ex-r,sy+r,r,d2r*270,d2r*360,false);
 
    ctx.lineTo(ex,ey-r);
 
    ctx.arc(ex-r,ey-r,r,d2r*0,d2r*90,false);
 
    ctx.lineTo(sx+r,ey);
 
    ctx.arc(sx+r,ey-r,r,d2r*90,d2r*180,false);
 
    ctx.lineTo(sx,sy+r);
 
    ctx.arc(sx+r,sy+r,r,d2r*180,d2r*270,false);
 
    ctx.closePath();
 

	
 

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

	
 
  ready: ->
 
    @mouseX = 0
 
    @mouseY = 0
 
    @cursorPath = null
 
    @ctx = @$.canvas.getContext('2d')
 

	
 
  update: (ev) ->
 
@@ -743,60 +694,196 @@ Polymer
 
    @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])
 
      bot1: $V([xZoomedIn, @offsetParent.offsetHeight])
 
    }
 
    @redraw()
 

	
 
  _line: (p1, p2) ->
 
    @ctx.moveTo(p1.e(1), p1.e(2))
 
    @ctx.lineTo(p2.e(1), p2.e(2))
 

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

	
 
    @ctx.strokeStyle = '#fff'
 
    @ctx.lineWidth = 0.5
 
    @ctx.beginPath()
 
    @_line($V([0, @mouseY]), $V([@$.canvas.width, @mouseY]), '#fff', '0.5px')
 
    @_line($V([@mouseX, 0]), $V([@mouseX, @$.canvas.height]), '#fff', '0.5px')
 
    _line(@ctx, $V([0, @mouseY]), $V([@$.canvas.width, @mouseY]))
 
    _line(@ctx, $V([@mouseX, 0]), $V([@mouseX, @$.canvas.height]))
 
    @ctx.stroke()
 

	
 
    if @cursorPath
 
      @ctx.strokeStyle = '#ff0303'
 
      @ctx.lineWidth = 1.5
 
      @ctx.beginPath()
 
      @_line(@cursorPath.top0, @cursorPath.top1, '#ff0303', 1.5)
 
      _line(@ctx, @cursorPath.top0, @cursorPath.top1)
 
      @ctx.stroke()
 

	
 
      @ctx.fillStyle = '#9c0303'
 
      @ctx.beginPath()
 
      @ctx.moveTo(@cursorPath.mid0.e(1), @cursorPath.mid0.e(2))
 
      @ctx.lineTo(p.e(1), p.e(2)) for p in [
 
        @cursorPath.mid1, @cursorPath.mid2, @cursorPath.mid3]
 
      @ctx.fill()
 
      
 
      @ctx.strokeStyle = '#ff0303'
 
      @ctx.lineWidth = 3
 
      @ctx.beginPath()
 
      @_line(@cursorPath.bot0, @cursorPath.bot1, '#ff0303', '3px')
 
      _line(@ctx, @cursorPath.bot0, @cursorPath.bot1, '#ff0303', '3px')
 
      @ctx.stroke()
 
    
 
    
 
Polymer
 
  is: 'light9-adjusters-canvas'
 
  behaviors: [ Polymer.IronResizableBehavior ]
 
  properties:
 
    adjs: { type: Object, notify: true }, # adjId: Adjustable
 
  listeners: 'iron-resize': 'update'
 
  ready: ->
 
    @adjs = {}
 
    @ctx = @$.canvas.getContext('2d')
 
    
 
    @redraw()
 
   
 
  onDown: (ev) ->
 
    if ev.buttons == 1
 
      ev.stopPropagation()
 
      start = $V([ev.x, ev.y])
 
      adj = @adjAtPoint(start)
 
      if adj
 
        @currentDrag = {start: start, adj: adj}
 
        adj.startDrag()
 

	
 
  onMove: (ev) ->
 
    pos = $V([ev.x, ev.y])
 
    if @currentDrag
 
      @currentDrag.cur = pos
 
      @currentDrag.adj.continueDrag(@currentDrag.cur.subtract(@currentDrag.start))
 

	
 
  onUp: (ev) ->
 
    return unless @currentDrag
 
    @currentDrag.adj.endDrag()
 
    @currentDrag = null
 
    
 
  setAdjuster: (adjId, makeAdjustable) ->
 
    # callers register/unregister the Adjustables they want us to make
 
    # adjuster elements for. Caller invents adjId.  makeAdjustable is
 
    # a function returning the Adjustable or it is null to clear any
 
    # adjusters with this id.
 
    if not @adjs[adjId] or not makeAdjustable?
 
      if not makeAdjustable?
 
        delete @adjs[adjId]
 
      else
 
        adj = makeAdjustable()
 
        @adjs[adjId] = adj
 
        adj.id = adjId
 

	
 
    # this is relying on makeCurveAdjusters always calling setAdjuster
 
    # whenever the values may have changed
 
    @debounce('adjsChanged', @adjsChanged.bind(@), 10)
 

	
 
    window.debug_adjsCount = Object.keys(@adjs).length
 
    
 
  adjsChanged: ->
 
    @updateAllCoords()
 

	
 
  layoutCenters: ->
 
    # push Adjustable centers around to avoid overlaps
 
    # Todo: also don't overlap inlineattr boxes
 
    @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...2]
 
        nearest = @qt.find(output.e(1), output.e(2))
 
        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)))
 

	
 
      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)
 

	
 
  adjAtPoint: (pt) ->
 
    nearest = @qt.find(pt.e(1), pt.e(2))
 
    if not nearest? or nearest.distanceFrom(pt) > 50
 
      return null
 
    return nearest?.adj
 

	
 
  updateAllCoords: ->
 
    @layoutCenters()
 
    @redraw()
 

	
 
  update: (ev) ->
 
    @$.canvas.width = ev.target.offsetWidth
 
    @$.canvas.height = ev.target.offsetHeight
 
    @redraw()
 
    
 
  redraw: (adjs) ->
 
    @ctx.clearRect(0, 0, @$.canvas.width, @$.canvas.height)
 

	
 
    for adjId, adj of @adjs
 
      ctr = adj.getCenter()
 
      target = adj.getTarget()
 
      @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)
 

	
 

	
 
  drawConnector: (ctr, target) ->
 
    @ctx.strokeStyle = '#aaa'
 
    @ctx.lineWidth = 2
 
    @ctx.beginPath()
 
    _line(@ctx, ctr, target)
 
    @ctx.stroke()
 
    
 
  drawAdjuster: (label, x1, y1, x2, y2) ->
 
    radius = 8
 
    @ctx.fillStyle = 'rgba(255, 255, 0, 0.5)'
 
    @ctx.beginPath()
 
    _roundRect(@ctx, x1, y1, x2, y2, radius)
 
    @ctx.fill()
 
    
 
    @ctx.strokeStyle = 'yellow'
 
    @ctx.lineWidth = 3
 
    @ctx.setLineDash([3, 3])
 
    @ctx.beginPath()
 
    _roundRect(@ctx, x1, y1, x2, y2, radius)
 
    @ctx.stroke()
 
    @ctx.setLineDash([])
 

	
 
    @ctx.font = "12px sans"
 
    @ctx.fillStyle = '#000'
 
    @ctx.fillText(label, x1 + 5, y2 - 5, x2 - x1 - 10)
 

	
 
    # coords from a center that's passed in
 
    # # special layout for the thaeter ones with middinh 
 
    # l/r arrows
 
    # connector
 

	
 
  
 
Polymer
 
  is: 'light9-timeline-diagram-layer'
 
  properties: {}
 
  ready: ->
 
    @elemById = {}
 

	
 
  setTimeAxis: (width, yTop, scale) ->
 
    pxPerTick = 50
 
    axis = d3.axisTop(scale).ticks(width / pxPerTick)
 
    d3.select(@$.timeAxis).attr('transform', 'translate(0,'+yTop+')').call(axis)
 

	
 
  getOrCreateElem: (uri, groupId, tag, attrs) ->
 
    elem = @elemById[uri]
0 comments (0 inline, 0 general)