diff --git a/light9/web/timeline/cursor_canvas.coffee b/light9/web/timeline/cursor_canvas.coffee --- a/light9/web/timeline/cursor_canvas.coffee +++ b/light9/web/timeline/cursor_canvas.coffee @@ -20,10 +20,10 @@ Polymer @mouseY = pos.e(2) @redraw() - setCursor: (y1, h1, y2, h2, fullZoomX, zoomInX, cursor) -> + setCursor: (y1, h1, y2, h2, viewState) -> - xZoomedOut = fullZoomX(cursor.t()) - xZoomedIn = zoomInX(cursor.t()) + xZoomedOut = viewState.fullZoomX(viewState.latestMouseTime()) + xZoomedIn = viewState.mouse.pos().e(1) @cursorPath = { top0: $V([xZoomedOut, y1]) diff --git a/light9/web/timeline/timeline-elements.html b/light9/web/timeline/timeline-elements.html --- a/light9/web/timeline/timeline-elements.html +++ b/light9/web/timeline/timeline-elements.html @@ -71,8 +71,7 @@ set-adjuster="{{setAdjuster}}" song="{{song}}" show="{{show}}" - zoom="{{viewState.zoomSpec}}" - zoom-in-x="{{zoomInX}}"> + view-state="{{viewState}}"> diff --git a/light9/web/timeline/timeline.coffee b/light9/web/timeline/timeline.coffee --- a/light9/web/timeline/timeline.coffee +++ b/light9/web/timeline/timeline.coffee @@ -91,7 +91,61 @@ class Project @graph.applyAndSendPatch(patch) if note in selection.selected() selection.selected(_.without(selection.selected(), note)) - + +class ViewState + constructor: () -> + # caller updates all these observables + @width = ko.observable(500) + @zoomSpec = + duration: ko.observable(100) # current song duration + t1: ko.observable(0) + t2: ko.observable(100) + @cursor = + t: ko.observable(20) + @mouse = + pos: ko.observable($V([0,0])) + + @fullZoomX = d3.scaleLinear() + @zoomInX = d3.scaleLinear() + + ko.computed(@zoomOrLayoutChanged.bind(@)) + + zoomOrLayoutChanged: () -> + log('zoomOrLayoutChanged') + # not for cursor updates + + window.debug_zoomOrLayoutChangedCount++ + + if @zoomSpec.t1() < 0 + @zoomSpec.t1(0) + if @zoomSpec.duration() and @zoomSpec.t2() > @zoomSpec.duration() + @zoomSpec.t2(@zoomSpec.duration()) + + @fullZoomX.domain([0, @zoomSpec.duration()]) + @fullZoomX.range([0, @width()]) + + # had trouble making notes update when this changes + zoomInX = d3.scaleLinear() + zoomInX.domain([@zoomSpec.t1(), @zoomSpec.t2()]) + zoomInX.range([0, @width()]) + @zoomInX = zoomInX + + latestMouseTime: -> + @zoomInX.invert(@mouse.pos().e(1)) + + onMouseWheel: (deltaY) -> + zs = @zoomSpec + + center = @latestMouseTime() + left = center - zs.t1() + right = zs.t2() - center + scale = Math.pow(1.005, deltaY) + + zs.t1(center - left * scale) + zs.t2(center + right * scale) + log('view to', ko.toJSON(@)) + + class TimelineEditor extends Polymer.Element @is: 'light9-timeline-editor' @behaviors: [ Polymer.IronResizableBehavior ] @@ -108,8 +162,6 @@ class TimelineEditor extends Polymer.Ele songTime: {type: Number, notify: true, observer: '_onSongTime'} songDuration: {type: Number, notify: true, observer: '_onSongDuration'} songPlaying: {type: Boolean, notify: true} - fullZoomX: {type: Object, notify: true} - zoomInX: {type: Object, notify: true} selection: {type: Object, notify: true} width: ko.observable(1) @listeners: @@ -130,18 +182,8 @@ class TimelineEditor extends Polymer.Ele window.debug_zoomOrLayoutChangedCount = 0 window.debug_adjUpdateDisplay = 0 - @viewState = - zoomSpec: - duration: ko.observable(100) # current song duration - 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])) + @viewState = new ViewState() window.viewState = @viewState - @fullZoomX = d3.scaleLinear() - @zoomInX = d3.scaleLinear() @setAdjuster = (adjId, makeAdjustable) => ac = @$.adjustersCanvas setTimeout((()=>ac.setAdjuster(adjId, makeAdjustable)),10) @@ -194,29 +236,13 @@ class TimelineEditor extends Polymer.Ele " zoomOrLayoutChanged: -> - log('zoomOrLayoutChanged') - # not for cursor updates - + vs = @viewState - if vs.zoomSpec.t1() < 0 - vs.zoomSpec.t1(0) - if vs.zoomSpec.duration() and vs.zoomSpec.t2() > vs.zoomSpec.duration() - vs.zoomSpec.t2(vs.zoomSpec.duration()) - - window.debug_zoomOrLayoutChangedCount++ - @fullZoomX.domain([0, vs.zoomSpec.duration()]) - @fullZoomX.range([0, @width()]) - - # had trouble making notes update when this changes - zoomInX = d3.scaleLinear() - zoomInX.domain([vs.zoomSpec.t1(), vs.zoomSpec.t2()]) - zoomInX.range([0, @width()]) - @zoomInX = zoomInX - + # todo: these run a lot of work purely for a time change if @$.zoomed?.$?.audio? - @dia.setTimeAxis(@width(), @$.zoomed.$.audio.offsetTop, @zoomInX) + @dia.setTimeAxis(@width(), @$.zoomed.$.audio.offsetTop, vs.zoomInX) @$.adjustersCanvas.updateAllCoords() # cursor needs update when layout changes, but I don't want @@ -228,7 +254,7 @@ class TimelineEditor extends Polymer.Ele @$.cursorCanvas.setCursor(@$.audio.offsetTop, @$.audio.offsetHeight, @$.zoomed.$.time.offsetTop, @$.zoomed.$.time.offsetHeight, - @fullZoomX, @zoomInX, @viewState.cursor) + @viewState) trackMouse: -> # not just for show- we use the mouse pos sometimes @@ -254,21 +280,11 @@ class TimelineEditor extends Polymer.Ele @$.vidrefTime.generateRequest() @$.vidrefLastSent = now - latestMouseTime: -> - @zoomInX.invert(@viewState.mouse.pos().e(1)) + bindWheelZoom: (elem) -> elem.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) - log('view to', ko.toJSON(@viewState)) + @viewState.onMouseWheel(ev.deltaY) forwardMouseEventsToAdjustersCanvas: -> ac = @$.adjustersCanvas @@ -376,11 +392,10 @@ class TimeZoomed extends Polymer.Element selection: { type: Object, notify: true } dia: { type: Object, notify: true } song: { type: String, notify: true } - zoomInX: { type: Object, notify: true } - zoom: { type: Object, notify: true, observer: 'onZoom' } # viewState.zoomSpec - zoomFlattened: { type: Object, notify: true } + viewState: { type: Object, notify: true } @observers: [ - 'onGraph(graph, setAdjuster, song, zoomInX, project)' + 'onGraph(graph, setAdjuster, song, viewState, project)', + 'onZoom(viewState)', ] @listeners: {'iron-resize': 'update'} update: -> @@ -390,7 +405,7 @@ class TimeZoomed extends Polymer.Element onZoom: -> updateZoomFlattened = -> log('updateZoomFlattened') - @zoomFlattened = ko.toJS(@zoom) + @zoomFlattened = ko.toJS(@viewState.zoomSpec) ko.computed(updateZoomFlattened.bind(@)) constructor: -> @@ -399,7 +414,6 @@ class TimeZoomed extends Polymer.Element @renderer = PIXI.autoDetectRenderer({ backgroundColor: 0xff6060, - }) connectedCallback: -> @@ -421,7 +435,7 @@ class TimeZoomed extends Polymer.Element for uri in _.sortBy(@graph.objects(@song, U(':note')), 'uri') #should only make new ones # - child = new Note(@graph, @selection, @dia, uri, @setAdjuster, @song, @zoomInX) + child = new Note(@graph, @selection, @dia, uri, @setAdjuster, @song, @viewState.zoomInX) originTime = @graph.floatValue(uri, U(':originTime')) effect = @graph.uriValue(uri, U(':effectClass')) for curve in @graph.objects(uri, U(':curve')) @@ -431,7 +445,7 @@ class TimeZoomed extends Polymer.Element curveWidthCalc = () => @_curveWidth(@worldPts) h = 150 #@offsetHeight - screenPts = ($V([@zoomInX(pt.e(1)), @offsetTop + (1 - pt.e(2)) * h]) for pt in @worldPts) + screenPts = ($V([@viewState.zoomInX(pt.e(1)), @offsetTop + (1 - pt.e(2)) * h]) for pt in @worldPts) graphics.beginFill(0xFF3300); graphics.lineStyle(4, 0xffd900, 1) @@ -442,13 +456,10 @@ class TimeZoomed extends Polymer.Element @rows = []#(new NoteRow(@graph, @dia, @song, @zoomInX, @noteUris, i, @selection) for i in [0...ROW_COUNT]) - - @stage.children.splice(0) @stage.addChild(graphics) @renderer.render(@stage) - onDrop: (effect, pos) -> U = (x) => @graph.Uri(x) @@ -464,10 +475,10 @@ class TimeZoomed extends Polymer.Element log("drop #{effect} is not an effect") return - dropTime = @zoomInX.invert(pos.e(1)) + dropTime = @viewState.zoomInX.invert(pos.e(1)) desiredWidthX = @offsetWidth * .3 - desiredWidthT = @zoomInX.invert(desiredWidthX) - @zoomInX.invert(0) + desiredWidthT = @viewState.zoomInX.invert(desiredWidthX) - @viewState.zoomInX.invert(0) desiredWidthT = Math.min(desiredWidthT, @zoom.duration() - dropTime) @project.makeNewNote(effect, dropTime, desiredWidthT)