Changeset - 2ed61945d881
[Not reviewed]
default
0 2 0
Drew Perttula - 9 years ago 2016-06-12 11:25:27
drewp@bigasterisk.com
adjusters hide at zoomed-out views. start inline-attrs box. note delete command
Ignore-this: b4674195f31c5bb82ecd7c0b3b7c8f3d
2 files changed with 101 insertions and 25 deletions:
0 comments (0 inline, 0 general)
light9/web/timeline/timeline-elements.html
Show inline comments
 
@@ -206,25 +206,30 @@ background: rgba(126, 52, 245, 0.0784313
 
     This element has the right Y coords.
 
     We compute X coords from the zoom setting.
 
     diagram-layer draws the note body. -->
 
<dom-module id="light9-timeline-note">
 
  <template>
 
    <style>
 
     :host {
 
         display: block;
 
         background: green;
 
         /* outline: 2px solid red; */
 
     }
 
    </style>
 
    <light9-timeline-note-inline-attrs></light9-timeline-note-inline-attrs>
 
    <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.
 
     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>
 
@@ -293,33 +298,42 @@ background: rgba(126, 52, 245, 0.0784313
 
        <tr><td></td><td style="visibility: hidden">↑</td><td></td></tr>
 
        <tr><td>←</td><td><span id="label" class$="[[spanClass]]">[[displayValue]]</span></td><td>→</td></tr>
 
        <tr><td></td><td style="visibility: hidden">↓</td><td></td></tr>
 
      </table>
 
    </div>
 
    
 
  </template>
 
</dom-module>
 

	
 
<!-- sometimes we draw attrs within the shape of a note. -->
 
<dom-module id="light9-timeline-note-inline-attrs">
 
  <template>
 

	
 
    <style>
 
     #top {
 
         position: absolute;
 
         overflow: hidden;
 
         background: #7b7b7b;
 
         border-radius: 6px;
 
         box-shadow: 0 0 5px black;
 
         padding: 3px;
 
     }
 
    </style>
 
    <div id="top" style$="left: [[rect.left]]px; top: [[rect.top]]px; width: [[rect.width]]px; height: [[rect.height]]px; display: [[rect.display]]">
 
      <div>note [[noteLabel]] <button on-click="onDel">del</button></div>
 
      <table>
 
        <tr><th>effect:</th><td><edit-choice uri="{{effect}}" label="{{effectLabel}}"></edit-choice></td></tr>
 
        <!-- <tr><th>color:</th><td>[[color]]</td></tr> -->
 
      </table>
 
    </div>
 
  </template>
 
  <script>
 
   Polymer({
 
       is: "light9-timeline-note-inline-attrs",
 
       properties: {
 
       }
 
   });
 
  </script>
 
</dom-module>
 

	
 
<script src="/lib/sylvester/sylvester.js"></script>
 
<script src="/lib/d3/build/d3.min.js"></script>
 

	
 
<!-- version with https://github.com/RubenVerborgh/N3.js/pull/61 -->
 
<script src="/lib/N3.js-pull61/browser/n3-browser.js"></script>
 
<!-- master version -->
 
<xxscript src="/lib/N3.js/browser/n3-browser.js"></script>
 
  
 
<script src="/lib/knockout/dist/knockout.debug.js"></script>
 
<script src="/lib/shortcut/index.js"></script>
light9/web/timeline/timeline.coffee
Show inline comments
 
@@ -363,102 +363,156 @@ getCurvePoints = (graph, curve, xOffset)
 
  return worldPts
 

	
 
Polymer
 
  is: 'light9-timeline-note'
 
  behaviors: [ Polymer.IronResizableBehavior ]
 
  listeners: 'iron-resize': 'update'
 
  properties:
 
    graph: { type: Object, notify: true }
 
    dia: { type: Object, notify: true }
 
    uri: { type: String, notify: true }
 
    zoomInX: { type: Object, notify: true }
 
    setAdjuster: {type: Function, notify: true}
 
    inlineRect: { type: Object, notify: true }
 
  observers: [
 
    'onUri(graph, dia, uri, zoomInX, setAdjuster)'
 
    'update(graph, dia, uri, zoomInX, setAdjuster)'
 
    ]
 
  ready: ->
 
    @adjusterIds = {}
 

	
 
  detached: ->
 
    log('detatch', @uri)
 
    @dia.clearElem(@uri, ['/area', '/label'])
 
    @isDetached = true
 
    @clearAdjusters()
 

	
 
  clearAdjusters: ->
 
    for i in Object.keys(@adjusterIds)
 
      @setAdjuster(i, null)
 

	
 
  onUri: ->
 
    @graph.runHandler(@update.bind(@), "note updates #{@uri}")
 
    
 
  update: ->
 
    if @isDetached?
 
      log('skipping update', @uri)
 
      return 
 
    # update our note DOM and SVG elements based on the graph
 
    U = (x) -> @graph.Uri(x)
 
    worldPts = [] # (song time, value)
 

	
 
    yForV = (v) => @offsetTop + (1 - v) * @offsetHeight
 

	
 
    originTime = @graph.floatValue(@uri, U(':originTime'))
 
    for curve in @graph.objects(@uri, U(':curve'))
 
      if @graph.uriValue(curve, U(':attr')) == U(':strength')
 
        worldPts = getCurvePoints(@graph, curve, originTime)
 
        @updateStrengthCurveEtc(originTime, curve, yForV)
 
        
 
  updateStrengthCurveEtc: (originTime, curve, yForV) ->
 
    U = (x) -> @graph.Uri(x)
 
    worldPts = getCurvePoints(@graph, curve, originTime) # (song time, value)
 

	
 
        curveWidth = =>
 
          tMin = @graph.floatValue(worldPts[0].uri, U(':time'))
 
          tMax = @graph.floatValue(worldPts[3].uri, U(':time'))
 
          tMax - tMin            
 

	
 
    screenPts = ($V([@zoomInX(pt.e(1)), @offsetTop + (1 - pt.e(2)) * @offsetHeight]) for pt in worldPts)
 
    @dia.setNote(@uri, screenPts, label)
 

	
 
    leftX = screenPts[1].e(1) + 5
 
    rightX = screenPts[2].e(1) - 5
 
    w = 120
 
    h = 45
 
    @inlineRect = {
 
      left: leftX,
 
      top: @offsetTop + @offsetHeight - h - 5,
 
      width: w,
 
      height: h,
 
      display: if rightX - leftX > w then 'block' else 'none'
 
      }
 

	
 
    if screenPts[3].e(1) - screenPts[0].e(1) < 100
 
      @clearAdjusters()
 
      return
 

	
 
        @adjusterIds[@uri+'/offset'] = true
 
        @setAdjuster(@uri+'/offset', => new AdjustableFloatObject({
 
          graph: @graph
 
          subj: @uri
 
          pred: @graph.Uri(':originTime')
 
          ctx: @graph.Uri(@song)
 
          getDisplayValue: (v, dv) => "o=#{dv}"
 
          getTargetPosForValue: (value) =>
 
            # display bug: should be working from pt[0].t, not from origin
 
            $V([@zoomInX(value + curveWidth() / 2), yForV(.5)])
 
          getValueForPos: (pos) =>
 
            @zoomInX.invert(pos.e(1)) - curveWidth() / 2
 
          getSuggestedTargetOffset: () => $V([-10, 0])
 
        }))
 

	
 
        for pointNum in [0, 1, 2, 3]
 
      do (pointNum) =>
 
          @adjusterIds[@uri+'/p'+pointNum] = true
 
          @setAdjuster(@uri+'/p'+pointNum, =>
 
              adj = new AdjustableFloatObject({
 
                graph: @graph
 
                subj: worldPts[pointNum].uri
 
                pred: @graph.Uri(':time')
 
                ctx: @graph.Uri(@song)
 
                getTargetPosForValue: (value) => $V([@zoomInX(value), yForV(0)])
 
              getTargetPosForValue: (value) =>
 
                $V([@zoomInX(value),
 
                    yForV(worldPts[pointNum].e(2))])
 
                getValueForPos: (pos) =>
 
                  origin = @graph.floatValue(@uri, U(':originTime'))
 
                  (@zoomInX.invert(pos.e(1)) - origin)
 
                getSuggestedTargetOffset: () => $V([0, -80])
 
              getSuggestedTargetOffset: () => $V([0, (if worldPts[pointNum].e(2) > .5 then 30 else -30)])
 
              })
 
              adj._getValue = (=>
 
                # note: don't use originTime from the closure- we need the
 
                # graph dependency
 
                adj._currentValue + @graph.floatValue(@uri, U(':originTime'))
 
                )
 
              adj
 
            )
 

	
 
    screenPos = (pt) =>
 
      $V([@zoomInX(pt.e(1)), @offsetTop + (1 - pt.e(2)) * @offsetHeight])
 
    
 
    
 

	
 
    label = @graph.uriValue(@uri, U(':effectClass')).replace(/.*\//, '')
 
    @dia.setNote(@uri, (screenPos(pt) for pt in worldPts), label)
 
Polymer
 
  is: "light9-timeline-note-inline-attrs"
 
  properties:
 
    graph: { type: Object, notify: true }
 
    song: { type: String, notify: true }
 
    uri: { type: String, notify: true }
 
    rect: { type: Object, notify: true }
 
    effect: { type: String, notify: true }
 
    effectLabel: { type: String, notify: true }
 
    color: { type: String, notify: true }
 
    noteLabel: { type: String, notify: true }
 
  observers: [
 
    'addHandler(graph, uri)'
 
    ]
 
  addHandler: ->
 
    @graph.runHandler(@update.bind(@))
 
  update: ->
 
    U = (x) -> @graph.Uri(x)
 
    @effect = @graph.uriValue(@uri, U(':effectClass'))
 
    @effectLabel = @effect.replace(/.*\//, '')
 
    @noteLabel = @uri.replace(/.*\//, '')
 
    @color = '#ff0000'
 
  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
 
@@ -479,24 +533,25 @@ Polymer
 
        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]
 
@@ -520,64 +575,69 @@ Polymer
 
    @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 }
 
    visible: { type: Boolean, notify: true }
 
    
 
    displayValue: { type: String }
 
    centerStyle: { type: Object }
 
    spanClass: { type: String, value: '' }
 

	
 
  observer: [
 
    'onAdj(graph, adj, dia, id)'
 
    'onAdj(graph, adj, dia, id, visible)'
 
    ]
 
  onAdj:  ->
 
    log('onAdj', @id)
 
    @adj.subscribe(@updateDisplay.bind(this))
 
    @graph.runHandler(@updateDisplay.bind(@))
 

	
 
  updateDisplay: () ->
 
    go = =>
 
      if !@visible
 
        @clearElements()
 
        return
 
      window.debug_adjUpdateDisplay++
 
      @spanClass = if @adj.config.emptyBox then 'empty' else ''
 
      @displayValue = @adj.getDisplayValue()
 
      center = @adj.getCenter()
 
      target = @adj.getTarget()
 
      #log("adj updateDisplay center #{center.elements} target #{target.elements}")
 
      return if isNaN(center.e(1))
 
      @centerStyle = {x: center.e(1), y: center.e(2)}
 
      @dia.setAdjusterConnector(@adj.id + '/conn', center, target)
 
    @debounce('updateDisplay', go, 1)
 
        
 
  attached: ->
 
    drag = d3.drag()
 
    sel = d3.select(@$.label)
 
    sel.call(drag)
 
    drag.subject((d) -> {x: @offsetLeft, y: @offsetTop})
 
    drag.container(@offsetParent)
 
    drag.on('start', () => @adj?.startDrag())
 
    drag.on 'drag', () =>
 
      @adj?.continueDrag($V([d3.event.x, d3.event.y]))
 
    drag.on('end', () => @adj?.endDrag())
 

	
 
    @updateDisplay()
 

	
 
  detached: ->
 
  detached: -> @clearElements()
 
  clearElements: ->
 
    @dia.clearElem(@adj.id, ['/conn'])
 

	
 

	
 
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] + ' '
 
@@ -615,38 +675,40 @@ Polymer
 
    for suff in suffixes
 
      elem = @elemById[uri+suff]
 
      if elem
 
        elem.remove()
 
        delete @elemById[uri+suff]
 

	
 
  anyPointsInView: (pts) ->
 
    for pt in pts
 
      if pt.e(1) > -100 && pt.e(1) < 2500
 
        return true
 
    return false
 
    
 
  setNote: (uri, curvePts, effectLabel) ->
 
  setNote: (uri, curvePts) ->
 
    areaId = uri + '/area'
 
    labelId = uri + '/label'
 
    if not @anyPointsInView(curvePts)
 
      @clearElem(uri, ['/area', '/label'])
 
      return
 
    # for now these need to be pretty transparent since they're
 
    # drawing on top of the inline-attrs widget :(
 
    elem = @getOrCreateElem(areaId, 'notes', 'path',
 
      {style:"fill:#53774b; stroke:#000000; stroke-width:1.5;"})
 
      {style:"fill:#53774b50; stroke:#000000; stroke-width:1.5;"})
 
    elem.setAttribute('d', svgPathFromPoints(curvePts))
 

	
 
    elem = @getOrCreateElem(uri+'/label', 'noteLabels', 'text', {style: "font-size:13px;line-height:125%;font-family:'Verana Sans';text-align:start;text-anchor:start;fill:#000000;"})
 
    elem.setAttribute('x', curvePts[0].e(1)+20)
 
    elem.setAttribute('y', curvePts[0].e(2)-10)
 
    elem.innerHTML = effectLabel;
 
    #elem = @getOrCreateElem(uri+'/label', 'noteLabels', 'text', {style: "font-size:13px;line-height:125%;font-family:'Verana Sans';text-align:start;text-anchor:start;fill:#000000;"})
 
    #elem.setAttribute('x', curvePts[0].e(1)+20)
 
    #elem.setAttribute('y', curvePts[0].e(2)-10)
 
    #elem.innerHTML = effectLabel;
 

	
 
  setCursor: (y1, h1, y2, h2, fullZoomX, zoomInX, cursor) ->
 
    @cursorPath =
 
      top: @querySelector('#cursor1')
 
      mid: @querySelector('#cursor2')
 
      bot: @querySelector('#cursor3')
 
    return if !@cursorPath.top
 
    
 
    xZoomedOut = fullZoomX(cursor.t())
 
    xZoomedIn = zoomInX(cursor.t())
 
    @cursorPath.top.setAttribute 'd', svgPathFromPoints([
 
      [xZoomedOut, y1]
0 comments (0 inline, 0 general)