changeset 1325:4210bbaf528f

lots of adjuster work. zoom adjs are usable. Ignore-this: e7d49768f0f60ec2aa89f87af93003bf
author Drew Perttula <drewp@bigasterisk.com>
date Sat, 04 Jun 2016 05:51:59 +0000
parents bb4cd6d7d274
children e4c65310c050
files light9/web/timeline-elements.html light9/web/timeline.coffee
diffstat 2 files changed, 132 insertions(+), 47 deletions(-) [+]
line wrap: on
line diff
--- a/light9/web/timeline-elements.html	Sat Jun 04 01:41:25 2016 +0000
+++ b/light9/web/timeline-elements.html	Sat Jun 04 05:51:59 2016 +0000
@@ -45,7 +45,7 @@
     <light9-timeline-audio id="audio"></light9-timeline-audio>
     <light9-timeline-time-zoomed id="zoomed" zoom="{{viewState.zoomSpec}}"></light9-timeline-time-zoomed>
     <light9-timeline-diagram-layer id="dia"></light9-timeline-diagram-layer>
-    <light9-timeline-adjusters id="adjusters" adjs="{{adjs}}"></light9-timeline-adjusters>
+    <light9-timeline-adjusters id="adjusters" dia="{{dia}}" adjs="{{adjs}}"></light9-timeline-adjusters>
     <div id="debug">[[debug]]</div>
   </template>
  
@@ -128,21 +128,12 @@
      }
     </style>
     <svg
-        xmlns:dc="http://purl.org/dc/elements/1.1/"
-        xmlns:cc="http://creativecommons.org/ns#"
-        xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+        xmlns="http://www.w3.org/2000/svg"
         xmlns:svg="http://www.w3.org/2000/svg"
-        xmlns="http://www.w3.org/2000/svg"
         xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-        xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
         viewBox="0 0 1021 600"
     >
       <g id="layer1">
-        
-        <path
-            style="fill:none;stroke:#d4d4d4;stroke-width:0.9282527;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:2.78475821, 2.78475821;stroke-dashoffset:0;"
-            d="m 202.190957,301.51493 22.56802038,0"
-            id="adjConnector"/>
         <text
             xml:space="preserve"
             style="font-size:13px;line-height:125%;font-family:'Verana Sans';-inkscape-font-specification:'Verana Sans';text-align:start;text-anchor:start;fill:#000000;"
@@ -150,9 +141,9 @@
             y="631.3988"
             id="text4290"
             sodipodi:linespacing="125%" ><tspan sodipodi:role="line" id="tspan4292" x="-338.38403" y="631.3988">spotchase</tspan></text>
-        
-        <g id="notes">
-        </g>
+        <g id="mouse"></g>
+        <g id="notes"></g>
+        <g id="connectors"></g>
         <g id="cursor">
           <path id="cursor1" style="fill:none; stroke:#ff0303; stroke-width:1.5; stroke-linecap:butt;" />
           <path id="cursor2" style="fill:#9c0303;" />
@@ -257,14 +248,16 @@
     </style>
 
     <template is="dom-repeat" items="{{adjs}}">
-      <light9-timeline-adjuster adj="{{item}}"></light9-timeline-adjuster>
+      <light9-timeline-adjuster dia="{{dia}}" adj="{{item}}"></light9-timeline-adjuster>
     </template>
   </template>
   <script>
    Polymer({
        is: "light9-timeline-adjusters",
        properties: {
-           adjs: { type: Array }
+           adjs: { type: Array },
+           dia: { type: Object }
+           
        }
    });
   </script>
@@ -281,8 +274,16 @@
 <dom-module id="light9-timeline-adjuster">
   <template>
     <style>
+     #top {
+         position: absolute;
+         display: inline-block;
+         outline: 2px solid rgba(255, 0, 0, 0.25);
+     }
      table {
-         position: absolute;
+         position: relative;
+         left: -50%;
+         top: -40px; /* percent had no effect */
+         outline: 2px solid rgba(0, 0, 255, 0.19);
          z-index: 2;
          border-collapse: collapse;
      }
@@ -300,12 +301,20 @@
          cursor: ew-resize;
          -webkit-user-select: none;
      }
+     span.empty {
+         width: 30px;
+         height: 13px;
+         display: inline-block;
+         background: rgba(0,0,0,0);
+     }
     </style>
-    <table id="top" style$="left: [[centerStyle.x]]px; top: [[centerStyle.y]]px">
-      <tr><td></td><td>↑</td><td></td></tr>
-      <tr><td>←</td><td><span id="label">[[displayValue]]</span></td><td>→</td></tr>
-      <tr><td></td><td>↓</td><td></td></tr>
-    </table>
+    <div id="top" style$="left: [[centerStyle.x]]px; top: [[centerStyle.y]]px">
+      <table>
+        <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>
--- a/light9/web/timeline.coffee	Sat Jun 04 01:41:25 2016 +0000
+++ b/light9/web/timeline.coffee	Sat Jun 04 05:51:59 2016 +0000
@@ -9,7 +9,7 @@
 @prefix dev: <http://light9.bigasterisk.com/device/> .
 
 <http://example.com/> {
-  :demoResource :startTime 0.5; :endTime 1.6 .
+  :demoResource :startTime 110; :endTime 120 .
 }
     ")
 
@@ -19,9 +19,10 @@
   # have a <light9-timeline-adjuster> associated. This object does the
   # layout and positioning.
   constructor: (@config) ->
-    # config has getTarget, getSuggestedTargetOffset, getValue
+    # config has getTarget, getSuggestedTargetOffset, getValue, emptyBox
 
   getDisplayValue: () ->
+    return '' if @config.emptyBox
     d3.format(".4g")(@_getValue())
 
   getCenter: () -> # vec2 of pixels
@@ -51,7 +52,7 @@
 
 class AdjustableFloatObservable extends Adjustable
   constructor: (@config) ->
-    # config has observable, valueLow, targetLow, valueHigh, targetHigh, getSuggestedTargetOffset
+    # config has observable, getValueForPos, valueLow, targetLow, valueHigh, targetHigh, getSuggestedTargetOffset
     ko.computed =>
       @_normalizedValue = d3.scaleLinear().domain([
         ko.unwrap(@config.valueLow),
@@ -65,13 +66,34 @@
     [lo, hi] = [ko.unwrap(@config.targetLow),
                 ko.unwrap(@config.targetHigh)]
     return lo.add(hi.subtract(lo).multiply(f))
+
+  _editorCoordinates: () -> # vec2 of mouse relative to <l9-t-editor>
+    ev = d3.event.sourceEvent
+
+    if ev.target.tagName == "LIGHT9-TIMELINE-EDITOR"
+      rootElem = ev.target
+    else
+      rootElem = ev.target.closest('light9-timeline-editor')
+    
+    @root = rootElem.getBoundingClientRect() if rootElem
+    offsetParentPos = $V([ev.pageX - @root.left, ev.pageY - @root.top])
+
+    setMouse(offsetParentPos)
+    return offsetParentPos 
     
   continueDrag: (pos) ->
-    # pos is vec2 of pixels relative to the drag start
+    # pos is vec2 of pixels relative to the drag start.
+
+    epos = @_editorCoordinates()
+    log('offsetParentPos', epos.elements)
     
-    newValue = @dragStartValue + pos.e(1) * .2
+    newValue = @config.getValueForPos(epos)
     @config.observable(newValue)
 
+  subscribe: (onChange) ->
+    ko.computed =>
+      @config.observable()
+      onChange()
 
 class AdjustableFloatObject extends Adjustable
   constructor: (@config) ->
@@ -103,6 +125,7 @@
     debug: {type: String}
     
   attached: ->
+    @dia = @$.dia
     @viewState =
       zoomSpec:
         duration: ko.observable(190)
@@ -142,7 +165,7 @@
         pred: graph.Uri(':startTime')
         ctx: ctx
         getTarget: () => $V([200, 300])
-        getSuggestedTargetOffset: () => $V([-30, 0])
+        getSuggestedTargetOffset: () => $V([-30, 80])
       })
       new AdjustableFloatObject({
         graph: graph
@@ -150,31 +173,60 @@
         pred: graph.Uri(':endTime')
         ctx: ctx
         getTarget: () => $V([300, 300])
-        getSuggestedTargetOffset: () => $V([30, 0])
+        getSuggestedTargetOffset: () => $V([30, 100])
       })
       ]
 
   makeZoomAdjs: ->
-    
+    dur = @viewState.zoomSpec.duration
+    valForPos = (pos) =>
+        x = pos.e(1)
+        t = @fullZoomX.invert(x)
     left = new AdjustableFloatObservable({
       observable: @viewState.zoomSpec.t1,
       valueLow: 0
-      valueHigh: @viewState.zoomSpec.duration
+      valueHigh: dur
       targetLow: $V([0, 30])  # y = @$.audio.offsetTop + @$.audio.offsetHeight / 2]
       targetHigh: $V([@offsetWidth, 30])
       getSuggestedTargetOffset: () => $V([-30, 0])
+      getValueForPos: valForPos
     })
 
     right = new AdjustableFloatObservable({
       observable: @viewState.zoomSpec.t2,
       valueLow: 0
-      valueHigh: @viewState.zoomSpec.duration
+      valueHigh: dur
       targetLow: $V([0, 30])  # y = @$.audio.offsetTop + @$.audio.offsetHeight / 2]
       targetHigh: $V([@offsetWidth, 30])
       getSuggestedTargetOffset: () => $V([30, 0])
+      getValueForPos: valForPos
     })
-    return [left, right]
+
+    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)
+      })
 
+    pan = new AdjustableFloatObservable({
+      observable: panObs
+      emptyBox: true
+      valueLow: 0
+      valueHigh: dur
+      # not right- the sides shouldn't be able to go offscreen
+      targetLow: $V([0, 30])  # y = @$.audio.offsetTop + @$.audio.offsetHeight / 2]
+      targetHigh: $V([@offsetWidth, 30])
+      getSuggestedTargetOffset: () => $V([0, 0])
+      getValueForPos: valForPos
+      })
+      
+    return [left, right, pan]
+
+_adjusterSerial = 0
 
 Polymer
   is: 'light9-timeline-adjuster'
@@ -190,14 +242,23 @@
       type: String
     centerStyle:
       type: Object
-      
+    spanClass:
+      type: String
+      value: ''
+
   onAdj: (adj) ->
     @adj.subscribe () =>
+      @spanClass = if @adj.config.emptyBox then 'empty' else ''
       @displayValue = @adj.getDisplayValue()
       center = @adj.getCenter()
       @centerStyle = {x: center.e(1), y: center.e(2)}
+      @dia?.setAdjusterConnector(@myId, @adj.getCenter(),
+                                @adj.getTarget())
         
   attached: ->
+    @myId = 'adjuster-' + _adjusterSerial
+    _adjusterSerial += 1
+    
     drag = d3.drag()
     sel = d3.select(@$.label)
     sel.call(drag)
@@ -212,6 +273,7 @@
 svgPathFromPoints = (pts) ->
   out = ''
   pts.forEach (p) ->
+    p = p.elements if p.elements # for vec2
     if out.length == 0
       out = 'M '
     else
@@ -225,26 +287,40 @@
   properties: {}
   ready: ->
     window.setNote = @setNote.bind(this)
+    window.setMouse = @setMouse.bind(this)
     @cursorPath =
       top: @querySelector('#cursor1')
       mid: @querySelector('#cursor2')
       bot: @querySelector('#cursor3')
-    @noteById = {}
-    return
+    @elemById = {}
+
+  setMouse: (pos) ->
+    elem = @getOrCreateElem('mouse-x', 'mouse', 'path', {style: "fill:none;stroke:#333;stroke-width:0.5;"})
+    elem.setAttribute('d', svgPathFromPoints([[-999, pos.e(2)], [999, pos.e(2)]]))
+    elem = @getOrCreateElem('mouse-y', 'mouse', 'path', {style: "fill:none;stroke:#333;stroke-width:0.5;"})
+    elem.setAttribute('d', svgPathFromPoints([[pos.e(1), -999], [pos.e(1), 999]]))
+    
+
+  getOrCreateElem: (uri, groupId, tag, attrs) ->
+    elem = @elemById[uri]
+    if !elem
+      elem = @elemById[uri] = document.createElementNS("http://www.w3.org/2000/svg", tag)
+      @$[groupId].appendChild(elem)
+      elem.setAttribute('id', uri)
+      for k,v of attrs
+        elem.setAttribute(k, v)
+    return elem
+    
   setNote: (uri, x1, x2, y1, y2) ->
-    elem = @noteById[uri]
-    if !elem
-      s = '<path id="' + uri + '" style="fill:#53774b; stroke:#000000; stroke-width:1.5;"/>'
-      @$.notes.innerHTML += s
-      elem = @noteById[uri] = @$.notes.lastChild
+    elem = @getOrCreateElem(uri, 'notes', 'path', {style:"fill:#53774b; stroke:#000000; stroke-width:1.5;"})
     d = svgPathFromPoints([
       [x1, y2]
       [x1 * .75 + x2 * .25, y1]
       [x1 * .25 + x2 * .75, y1]
       [x2, y2]
     ])
-    elem.setAttribute 'd', d
-    return
+    elem.setAttribute('d', d)
+
   setCursor: (y1, h1, y2, h2, fullZoomX, zoomInX, cursor) ->
     xZoomedOut = fullZoomX(cursor.t)
     xZoomedIn = zoomInX(cursor.t)
@@ -262,7 +338,7 @@
       [xZoomedIn, y2 + h2]
       [xZoomedIn, @offsetParent.offsetHeight]
     ])
-    return
-  setAdjusterConnector: (id, center, target) ->
-    console.log 'setAdjusterConnector', id, center, target
-    return
+
+  setAdjusterConnector: (uri, center, target) ->
+    elem = @getOrCreateElem(uri, 'connectors', 'path', {style: "fill:none;stroke:#d4d4d4;stroke-width:0.9282527;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:2.78475821, 2.78475821;stroke-dashoffset:0;"})
+    elem.setAttribute('d', svgPathFromPoints([center, target]))