Files
@ e82a8a48120c
Branch filter:
Location: light9/light9/web/timeline/adjustable.coffee
e82a8a48120c
5.6 KiB
text/coffeescript
update note drawings correctly. rerender less.
Ignore-this: f673b26f919d98b5ee18c1dd94a877f2
Ignore-this: f673b26f919d98b5ee18c1dd94a877f2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 | log = console.log
class Adjustable
# 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) ->
@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])
getDisplayValue: () ->
return '' if @config.emptyBox
defaultFormat = d3.format(".4g")(@_getValue())
if @config.getDisplayValue?
return @config.getDisplayValue(@_getValue(), defaultFormat)
defaultFormat
getSuggestedCenter: () ->
@getTarget().add(@config.getSuggestedTargetOffset())
getCenter: () -> # vec2 of pixels
@getTarget().add(@centerOffset)
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()
continueDrag: (pos) ->
## pos is vec2 of pixels relative to the drag start
@targetDraggedTo = pos.add(@initialTarget)
endDrag: () ->
# override
_editorCoordinates: () -> # vec2 of mouse relative to <l9-t-editor>
return @targetDraggedTo
ev = d3.event.sourceEvent
if ev.target.tagName == "LIGHT9-TIMELINE-EDITOR"
rootElem = ev.target
else
rootElem = ev.target.closest('light9-timeline-editor')
if ev.touches?.length
ev = ev.touches[0]
# storing root on the object to remember it across calls in case
# you drag outside the editor.
@root = rootElem.getBoundingClientRect() if rootElem
offsetParentPos = $V([ev.pageX - @root.left, ev.pageY - @root.top])
return offsetParentPos
class window.AdjustableFloatObservable extends Adjustable
constructor: (@config) ->
# config also has:
# observable -> ko.observable we will read and write
# getValueForPos(pos) -> what should we set to if the user
# moves target to this coord?
super()
@ctor2()
_getValue: () ->
@config.observable()
continueDrag: (pos) ->
# pos is vec2 of pixels relative to the drag start.
super(pos)
epos = @_editorCoordinates()
newValue = @config.getValueForPos(epos)
@config.observable(newValue)
subscribe: (onChange) ->
log('AdjustableFloatObservable subscribe', @config)
ko.computed =>
@config.observable()
onChange()
class window.AdjustableFloatObject extends Adjustable
constructor: (@config) ->
# config also has:
# graph
# subj
# pred
# ctx
# getTargetPosForValue(value) -> getTarget result for value
# getValueForPos
super()
@ctor2()
if not @config.ctx?
throw new Error("missing ctx")
@config.graph.runHandler(@_syncValue.bind(@),
"adj sync #{@config.subj.value}")
_syncValue: () ->
@_currentValue = @config.graph.floatValue(@config.subj, @config.pred)
@_onChange() if @_onChange
_getValue: () ->
# this is a big speedup- callers use _getValue about 4x as much as
# the graph changes and graph.floatValue is slow
@_currentValue
getTarget: () ->
@config.getTargetPosForValue(@_getValue())
subscribe: (onChange) ->
# only works on one subscription at a time
throw new Error('multi subscribe not implemented') if @_onChange
@_onChange = onChange
continueDrag: (pos) ->
# pos is vec2 of pixels relative to the drag start
super(pos)
newValue = @config.getValueForPos(@_editorCoordinates())
@config.graph.patchObject(@config.subj, @config.pred,
@config.graph.LiteralRoundedFloat(newValue),
@config.ctx)
class window.AdjustableFade extends Adjustable
constructor: (@yForV, @i0, @i1, @note, offset, ctx) ->
super()
@config = {
getSuggestedTargetOffset: -> offset
getTarget: @getTarget.bind(@)
ctx: ctx
}
@ctor2()
getTarget: ->
mid = @note.worldPts[@i0].x(.5).add(@note.worldPts[@i1].x(.5))
$V([@note.zoomInX(mid.e(1)), @yForV(mid.e(2))])
_getValue: ->
mid = @note.worldPts[@i0].x(.5).add(@note.worldPts[@i1].x(.5))
mid.e(1)
continueDrag: (pos) ->
# pos is vec2 of pixels relative to the drag start
super(pos)
graph = @note.graph
U = (x) -> graph.Uri(x)
goalCenterSec = @note.zoomInX.invert(@initialTarget.e(1) + pos.e(1))
diamSec = @note.worldPts[@i1].e(1) - @note.worldPts[@i0].e(1)
newSec0 = goalCenterSec - diamSec / 2
newSec1 = goalCenterSec + diamSec / 2
originSec = graph.floatValue(@note.uri, U(':originTime'))
p0 = @_makePatch(graph, @i0, newSec0, originSec, @config.ctx)
p1 = @_makePatch(graph, @i1, newSec1, originSec, @config.ctx)
graph.applyAndSendPatch(@_addPatches(p0, p1))
_makePatch: (graph, idx, newSec, originSec, ctx) ->
graph.getObjectPatch(@note.worldPts[idx].uri,
graph.Uri(':time'),
graph.LiteralRoundedFloat(newSec - originSec), ctx)
_addPatches: (p0, p1) ->
{
addQuads: p0.addQuads.concat(p1.addQuads),
delQuads: p0.delQuads.concat(p1.delQuads)
}
|