# HG changeset patch # User Lucas Thompson # Date 1496254486 -3600 # Node ID 1241ca979fd97d8cf2963ee65e5a66ead4bbfc0f # Parent c1cedba9557bdff0a6d081f4d2d255b3d3f77657 Refactor based on pattern which emerged when implementing multiple components. Still some very obvious dupe regarding the ElementRef stuff, I don't think ViewChild decorated props are inherited.. but I haven't actually verified that. diff -r c1cedba9557b -r 1241ca979fd9 src/app/visualisations/grid/grid.component.ts --- a/src/app/visualisations/grid/grid.component.ts Wed May 31 17:33:23 2017 +0100 +++ b/src/app/visualisations/grid/grid.component.ts Wed May 31 19:14:46 2017 +0100 @@ -21,37 +21,29 @@ styleUrls: ['../waves-template.css'], changeDetection: ChangeDetectionStrategy.OnPush }) -export class GridComponent extends WavesComponent implements AfterViewInit { +export class GridComponent extends WavesComponent { + @ViewChild('track') trackDiv: ElementRef; - private mFeature: MatrixFeature; - private height: number; // As it stands, height is fixed. Store once onInit. - @Input() set grid(grid: MatrixFeature) { - this.mFeature = grid; - this.update(); + this.feature = grid; } - get grid(): MatrixFeature { - return this.mFeature; + protected get containerHeight(): number { + return this.trackDiv.nativeElement.getBoundingClientRect().height; } - ngAfterViewInit(): void { - this.height = this.trackDiv.nativeElement.getBoundingClientRect().height; - this.renderTimeline(this.trackDiv); - this.update(); + protected get trackContainer(): ElementRef { + return this.trackDiv; } - update(): void { - if (!this.waveTrack || !this.grid) { return; } - this.clearTimeline(this.trackDiv); - - const startTime = this.grid.startTime; // !!! + make use of - const stepDuration = this.grid.stepDuration; - const matrixData = this.grid.data; + protected get featureLayers(): Layer[] { + const startTime = this.feature.startTime; // !!! + make use of + const stepDuration = this.feature.stepDuration; + const matrixData = this.feature.data; if (matrixData.length === 0) { - return; + return []; } const targetValue = estimatePercentile(matrixData, 95); @@ -62,7 +54,7 @@ stepDuration ); - this.addLayer( + return [ new Waves.helpers.MatrixLayer( matrixEntity, { @@ -71,9 +63,7 @@ normalise: 'none', mapper: iceMapper() } - ), - this.waveTrack, - this.timeline.timeContext - ); + ) + ]; } } diff -r c1cedba9557b -r 1241ca979fd9 src/app/visualisations/instants/instants.component.ts --- a/src/app/visualisations/instants/instants.component.ts Wed May 31 17:33:23 2017 +0100 +++ b/src/app/visualisations/instants/instants.component.ts Wed May 31 19:14:46 2017 +0100 @@ -19,43 +19,33 @@ styleUrls: ['../waves-template.css'], changeDetection: ChangeDetectionStrategy.OnPush }) -export class InstantsComponent extends WavesComponent implements AfterViewInit { +export class InstantsComponent extends WavesComponent { + @ViewChild('track') trackDiv: ElementRef; - private mFeature: Instant[]; - private height: number; // As it stands, height is fixed. Store once onInit. - @Input() set instants(instants: Instant[]) { - this.mFeature = instants; - this.update(); + this.feature = instants; } - get instants(): Instant[] { - return this.mFeature; + protected get containerHeight(): number { + return this.trackDiv.nativeElement.getBoundingClientRect().height; } - ngAfterViewInit(): void { - this.height = this.trackDiv.nativeElement.getBoundingClientRect().height; - this.renderTimeline(this.trackDiv); - this.update(); + protected get trackContainer(): ElementRef { + return this.trackDiv; } - update(): void { - if (!this.waveTrack || !this.instants) { return; } - this.clearTimeline(this.trackDiv); - - this.addLayer( + protected get featureLayers(): Layer[] { + return [ new Waves.helpers.TickLayer( - this.instants, + this.feature, { height: this.height, color: this.colour, labelPosition: 'bottom', shadeSegments: true } - ), - this.waveTrack, - this.timeline.timeContext - ); + ) + ]; } } diff -r c1cedba9557b -r 1241ca979fd9 src/app/visualisations/notes/notes.component.ts --- a/src/app/visualisations/notes/notes.component.ts Wed May 31 17:33:23 2017 +0100 +++ b/src/app/visualisations/notes/notes.component.ts Wed May 31 19:14:46 2017 +0100 @@ -19,44 +19,32 @@ styleUrls: ['../waves-template.css'], changeDetection: ChangeDetectionStrategy.OnPush }) -export class NotesComponent extends WavesComponent implements AfterViewInit { - +export class NotesComponent extends WavesComponent { @ViewChild('track') trackDiv: ElementRef; - private mFeature: Note[]; - private height: number; // As it stands, height is fixed. Store once onInit. - @Input() set notes(notes: Note[]) { - this.mFeature = notes; - this.update(); + this.feature = notes; } - get notes(): Note[] { - return this.mFeature; + protected get containerHeight(): number { + return this.trackDiv.nativeElement.getBoundingClientRect().height; } - ngAfterViewInit(): void { - this.height = this.trackDiv.nativeElement.getBoundingClientRect().height; - this.renderTimeline(this.trackDiv); - this.update(); + protected get trackContainer(): ElementRef { + return this.trackDiv; } - update(): void { - if (!this.waveTrack || !this.notes) { return; } - this.clearTimeline(this.trackDiv); - - this.addLayer( + protected get featureLayers(): Layer[] { + return [ new Waves.helpers.PianoRollLayer( - this.notes, + this.feature, { height: this.height, color: this.colour, - yDomain: findVerticalRange(this.notes) + yDomain: findVerticalRange(this.feature) } - ), - this.waveTrack, - this.timeline.timeContext - ); + ) + ]; } } diff -r c1cedba9557b -r 1241ca979fd9 src/app/visualisations/tracks/tracks.components.ts --- a/src/app/visualisations/tracks/tracks.components.ts Wed May 31 17:33:23 2017 +0100 +++ b/src/app/visualisations/tracks/tracks.components.ts Wed May 31 19:14:46 2017 +0100 @@ -20,51 +20,40 @@ styleUrls: ['../waves-template.css'], changeDetection: ChangeDetectionStrategy.OnPush }) -export class TracksComponent extends WavesComponent implements AfterViewInit { +export class TracksComponent extends WavesComponent { @ViewChild('track') trackDiv: ElementRef; - private mFeature: TracksFeature; private currentState: PlotLayerData[]; - private height: number; // As it stands, height is fixed. Store once onInit. @Input() set tracks(input: TracksFeature) { - this.mFeature = input; - this.currentState = generatePlotData(input); - this.update(); + this.feature = input; } - get tracks(): TracksFeature { - return this.mFeature; + protected get containerHeight(): number { + return this.trackDiv.nativeElement.getBoundingClientRect().height; } - ngAfterViewInit(): void { - this.height = this.trackDiv.nativeElement.getBoundingClientRect().height; - this.renderTimeline(this.trackDiv); - this.update(); + protected get trackContainer(): ElementRef { + return this.trackDiv; } - update(): void { - if (this.waveTrack) { - this.clearTimeline(this.trackDiv); - for (const feature of this.currentState) { - const lineLayer = new Waves.helpers.LineLayer(feature.data, { - color: this.colour, - height: this.height, - yDomain: feature.yDomain - }); - this.addLayer( - lineLayer, - this.waveTrack, - this.timeline.timeContext - ); + protected get featureLayers(): Layer[] { + this.currentState = generatePlotData(this.feature); + return this.currentState.map(feature => new Waves.helpers.LineLayer( + feature.data, { + color: this.colour, + height: this.height, + yDomain: feature.yDomain + }) + ); + } - // Set start and duration so that the highlight layer can use - // them to determine which line to draw values from - lineLayer.start = feature.startTime; - lineLayer.duration = feature.duration; - lineLayer.update(); // TODO probably better to update after all added - } - } + protected get postAddMap() { + return (layer, index) => { + layer.start = this.currentState[index].startTime; + layer.duration = this.currentState[index].duration; + layer.update(); + }; } } diff -r c1cedba9557b -r 1241ca979fd9 src/app/visualisations/waveform/waveform.component.ts --- a/src/app/visualisations/waveform/waveform.component.ts Wed May 31 17:33:23 2017 +0100 +++ b/src/app/visualisations/waveform/waveform.component.ts Wed May 31 19:14:46 2017 +0100 @@ -1,7 +1,6 @@ import { Component, Input, - ChangeDetectorRef, ElementRef, ViewChild, ChangeDetectionStrategy @@ -16,54 +15,38 @@ styleUrls: ['../waves-template.css'], changeDetection: ChangeDetectionStrategy.OnPush }) -export class WaveformComponent extends WavesComponent { +export class WaveformComponent extends WavesComponent { + @ViewChild('track') trackDiv: ElementRef; @Input() set audioBuffer(buffer: AudioBuffer) { - this._audioBuffer = buffer || undefined; - if (this.audioBuffer) { - this.renderWaveform(this.audioBuffer); - } + this.duration = buffer.duration; + this.timeline.pixelsPerSecond = this.timeline.visibleWidth / buffer.duration; + this.feature = buffer; } - get audioBuffer(): AudioBuffer { - return this._audioBuffer; + protected get containerHeight(): number { + return this.trackDiv.nativeElement.getBoundingClientRect().height; } - private _audioBuffer: AudioBuffer; - - constructor(private ref: ChangeDetectorRef) { - super(); + protected get trackContainer(): ElementRef { + return this.trackDiv; } - renderWaveform(buffer: AudioBuffer): void { - const height = this.trackDiv.nativeElement.getBoundingClientRect().height; - if (this.timeline && this.waveTrack) { - // resize - const width = this.trackDiv.nativeElement.getBoundingClientRect().width; - this.clearTimeline(this.trackDiv); - this.timeline.visibleWidth = width; - this.timeline.pixelsPerSecond = width / buffer.duration; - this.waveTrack.height = height; - } else { - this.renderTimeline(this.trackDiv, buffer.duration); + protected get featureLayers(): Layer[] { + const nChannels = this.feature.numberOfChannels; + const totalWaveHeight = this.height * 0.9; + const waveHeight = totalWaveHeight / nChannels; + + const channelLayers: Layer[] = []; + for (let ch = 0; ch < nChannels; ++ch) { + channelLayers.push(new wavesUI.helpers.WaveformLayer(this.feature, { + top: (this.height - totalWaveHeight) / 2 + waveHeight * ch, + height: waveHeight, + color: this.colour, + channel: ch + }) + ); } - - const nchannels = buffer.numberOfChannels; - const totalWaveHeight = height * 0.9; - const waveHeight = totalWaveHeight / nchannels; - - for (let ch = 0; ch < nchannels; ++ch) { - const waveformLayer = new wavesUI.helpers.WaveformLayer(buffer, { - top: (height - totalWaveHeight) / 2 + waveHeight * ch, - height: waveHeight, - color: this.colour, - channel: ch - }); - this.addLayer(waveformLayer, this.waveTrack, this.timeline.timeContext); - } - - this.waveTrack.render(); - this.waveTrack.update(); - this.ref.markForCheck(); + return channelLayers; } } diff -r c1cedba9557b -r 1241ca979fd9 src/app/visualisations/waves-base.component.ts --- a/src/app/visualisations/waves-base.component.ts Wed May 31 17:33:23 2017 +0100 +++ b/src/app/visualisations/waves-base.component.ts Wed May 31 19:14:46 2017 +0100 @@ -1,15 +1,18 @@ /** * Created by lucast on 26/05/2017. */ -import {ElementRef, Input} from '@angular/core'; +import {AfterViewInit, ElementRef, Input} from '@angular/core'; import {OnSeekHandler} from '../playhead/PlayHeadHelpers'; import {attachTouchHandlerBodges} from './WavesJunk'; import Waves from 'waves-ui-piper'; import {countingIdProvider} from 'piper/client-stubs/WebWorkerStreamingClient'; +import {ShapedFeatureData} from './FeatureUtilities'; const trackIdGenerator = countingIdProvider(0); -export abstract class WavesComponent { +export abstract class WavesComponent + implements AfterViewInit { + @Input() set width(width: number) { if (this.timeline) { requestAnimationFrame(() => { @@ -21,11 +24,26 @@ @Input() timeline: Timeline; @Input() onSeek: OnSeekHandler; @Input() colour: string; + @Input() set feature(feature: T) { + this.mFeature = feature; + this.update(); + } + + get feature(): T { + return this.mFeature; + } protected layers: Layer[]; protected zoomOnMouseDown: number; protected offsetOnMouseDown: number; protected waveTrack: Track; + protected abstract get containerHeight(): number; + protected abstract get trackContainer(): ElementRef; + protected abstract get featureLayers(): Layer[]; + protected postAddMap: (value: Layer, index: number, array: Layer[]) => void; + protected height: number; + protected duration: number; + private mFeature: T; private id: string; constructor() { @@ -33,18 +51,38 @@ this.id = trackIdGenerator.next().value; } - protected renderTimeline($el: ElementRef, duration?: number): Timeline { + ngAfterViewInit(): void { + this.height = this.containerHeight; + this.renderTimeline(this.trackContainer); + this.update(); + } + + update(): void { + if (!this.waveTrack || !this.mFeature) { + return; + } + this.clearTimeline(this.trackContainer); + const layers = this.featureLayers; + for (const layer of layers) { + this.addLayer(layer, this.waveTrack, this.timeline.timeContext); + } + if (this.postAddMap) { + layers.forEach(this.postAddMap); + } + } + + + protected renderTimeline($el: ElementRef): Timeline { const track: HTMLElement = $el.nativeElement; track.innerHTML = ''; - const height: number = track.getBoundingClientRect().height; - if (duration >= 0) { + if (this.duration >= 0) { const width: number = track.getBoundingClientRect().width; - this.timeline.pixelsPerSecond = width / duration; + this.timeline.pixelsPerSecond = width / this.duration; this.timeline.visibleWidth = width; } this.waveTrack = this.timeline.createTrack( track, - height, + this.height, this.id ); @@ -54,7 +92,7 @@ this.timeline ); } - this.resetTimelineState($el); + this.resetTimelineState(); } // TODO can likely be removed, or use waves-ui methods @@ -77,15 +115,13 @@ } } } - this.resetTimelineState($el); + this.resetTimelineState(); } - private resetTimelineState($el: ElementRef): void { - const height = $el.nativeElement.getBoundingClientRect().height; - + private resetTimelineState(): void { // time axis const timeAxis = new Waves.helpers.TimeAxisLayer({ - height: height, + height: this.containerHeight, color: '#b0b0b0' }); this.addLayer(timeAxis, this.waveTrack, this.timeline.timeContext, true);