log = debug('timeline') debug.enable('*') Drawing = window.Drawing ROW_COUNT = 7 # plan: in here, turn all the notes into simple js objects with all # their timing data and whatever's needed for adjusters. From that, do # the brick layout. update only changing adjusters. @customElement('light9-timeline-time-zoomed') class TimeZoomed extends LitElement @getter_properties: graph: { type: Object, notify: true } project: { type: Object } selection: { type: Object, notify: true } song: { type: String, notify: true } viewState: { type: Object, notify: true } inlineAttrConfigs: { type: Array, value: [] } # only for inlineattrs that should be displayed imageSamples: { type: Array, value: [] } @getter_observers: [ '_onGraph(graph, setAdjuster, song, viewState, project)', 'onZoom(viewState)', '_onViewState(viewState)', ] constructor: -> super() @numRows = 6 @noteByUriStr = new Map() @stage = new PIXI.Container() @stage.interactive=true @renderer = PIXI.autoDetectRenderer({ backgroundColor: 0x606060, antialias: true, forceCanvas: true, }) @bg = new PIXI.Container() @stage.addChild(@bg) @dirty = _.debounce(@_repaint.bind(@), 10) ready: -> super.ready() @imageSamples = ['one'] @addEventListener('iron-resize', @_onResize.bind(@)) Polymer.RenderStatus.afterNextRender(this, @_onResize.bind(@)) @$.rows.appendChild(@renderer.view) # This works for display, but pixi hit events didn't correctly # move with the objects, so as a workaround, I extended the top of # the canvas in _onResize. # #ko.computed => # @stage.setTransform(0, -(@viewState.rowsY()), 1, 1, 0, 0, 0, 0, 0) _onResize: -> @$.rows.firstChild.style.position = 'relative' @$.rows.firstChild.style.top = -@viewState.rowsY() + 'px' @renderer.resize(@clientWidth, @clientHeight + @viewState.rowsY()) @dirty() _onGraph: (graph, setAdjuster, song, viewState, project)-> return unless @song # polymer will call again @graph.runHandler(@gatherNotes.bind(@), 'zoom notes') _onViewState: (viewState) -> @brickLayout = new BrickLayout(@viewState, @numRows) noteDirty: -> @dirty() onZoom: -> updateZoomFlattened = -> @zoomFlattened = ko.toJS(@viewState.zoomSpec) ko.computed(updateZoomFlattened.bind(@)) gatherNotes: -> U = (x) => @graph.Uri(x) return unless @song? songNotes = @graph.objects(U(@song), U(':note')) toRemove = new Set(@noteByUriStr.keys()) for uri in @graph.sortedUris(songNotes) had = toRemove.delete(uri.value) if not had @_addNote(uri) toRemove.forEach @_delNote.bind(@) @dirty() isActiveNote: (note) -> @noteByUriStr.has(note.value) _repaint: -> @_drawGrid() @renderer.render(@stage) _drawGrid: -> # maybe someday this has snappable timing markers too @bg.removeChildren() gfx = new PIXI.Graphics() @bg.addChild(gfx) gfx.lineStyle(1, 0x222222, 1) for row in [0...@numRows] y = @brickLayout.rowBottom(row) gfx.moveTo(0, y) gfx.lineTo(@clientWidth, y) _addNote: (uri) -> U = (x) => @graph.Uri(x) con = new PIXI.Container() con.interactive=true @stage.addChild(con) note = new Note(@, con, @project, @graph, @selection, uri, @setAdjuster, U(@song), @viewState, @brickLayout) # this must come before the first Note.draw @noteByUriStr.set(uri.value, note) @brickLayout.addNote(note, note.onRowChange.bind(note)) note.initWatchers() _delNote: (uriStr) -> n = @noteByUriStr.get(uriStr) @brickLayout.delNote(n) @stage.removeChild(n.container) n.destroy() @noteByUriStr.delete(uriStr) onDrop: (effect, pos) -> U = (x) => @graph.Uri(x) return unless effect and effect.match(/^http/) # we could probably accept some initial overrides right on the # effect uri, maybe as query params if not @graph.contains(effect, U('rdf:type'), U(':Effect')) if @graph.contains(effect, U('rdf:type'), U(':LightSample')) effect = @project.makeEffect(effect) else log("drop #{effect} is not an effect") return dropTime = @viewState.zoomInX.invert(pos.e(1)) desiredWidthX = @offsetWidth * .3 desiredWidthT = @viewState.zoomInX.invert(desiredWidthX) - @viewState.zoomInX.invert(0) desiredWidthT = Math.min(desiredWidthT, @viewState.zoomSpec.duration() - dropTime) @project.makeNewNote(U(@song), U(effect), dropTime, desiredWidthT) updateInlineAttrs: (note, config) -> if not config? index = 0 for c in @inlineAttrConfigs if c.uri.equals(note) @splice('inlineAttrConfigs', index) return index += 1 else index = 0 for c in @inlineAttrConfigs if c.uri.equals(note) @splice('inlineAttrConfigs', index, 1, config) return index += 1 @push('inlineAttrConfigs', config)