changeset 394:f45a916eb5b1

Use the cross hair layer for notes, tracks and curve. This involved bodging in unit to ShapedFeatureData, which isn't particularly easy to do because this isn't an encapsulated type. Need to come back to improving this, as I am monkey-patching a unit property onto Arrays etc.
author Lucas Thompson <dev@lucas.im>
date Thu, 01 Jun 2017 18:55:55 +0100
parents a02e6ec4a9d2
children de9864aeacf0
files src/app/analysis-item/analysis-item.component.html src/app/visualisations/FeatureUtilities.ts src/app/visualisations/curve/curve.component.ts src/app/visualisations/notes/notes.component.ts src/app/visualisations/waves-base.component.ts
diffstat 5 files changed, 83 insertions(+), 54 deletions(-) [+]
line wrap: on
line diff
--- a/src/app/analysis-item/analysis-item.component.html	Thu Jun 01 16:26:52 2017 +0100
+++ b/src/app/analysis-item/analysis-item.component.html	Thu Jun 01 18:55:55 2017 +0100
@@ -40,7 +40,7 @@
                 [onSeek]="onSeek"
                 [curve]="item.collected"
               ></ugly-curve>
-              <ugly-vertical-scale *ngSwitchCase="'tracks'">
+              <ugly-cross-hair-inspector *ngSwitchCase="'tracks'">
                 <ugly-tracks
                   [colour]="getNextColour()"
                   [timeline]="timeline"
@@ -48,8 +48,8 @@
                   [onSeek]="onSeek"
                   [tracks]="item.collected"
                 ></ugly-tracks>
-              </ugly-vertical-scale>
-              <ugly-vertical-scale *ngSwitchCase="'notes'">
+              </ugly-cross-hair-inspector>
+              <ugly-cross-hair-inspector *ngSwitchCase="'notes'">
                 <ugly-notes
                   [colour]="getNextColour()"
                   [timeline]="timeline"
@@ -57,7 +57,7 @@
                   [onSeek]="onSeek"
                   [notes]="item.collected"
                 ></ugly-notes>
-              </ugly-vertical-scale>
+              </ugly-cross-hair-inspector>
               <ugly-instants
                 *ngSwitchCase="'instants'"
                 [colour]="getNextColour()"
--- a/src/app/visualisations/FeatureUtilities.ts	Thu Jun 01 16:26:52 2017 +0100
+++ b/src/app/visualisations/FeatureUtilities.ts	Thu Jun 01 18:55:55 2017 +0100
@@ -74,17 +74,19 @@
 type ShapeDeducedFromList = 'instants' | 'notes';
 export type HigherLevelFeatureShape = CollectedShape | ShapeDeducedFromList;
 
-export type ShapedFeatureData = VectorFeature
+export type ShapedFeatureData = {unit?: string} & (
+  VectorFeature
   | MatrixFeature
   | TracksFeature
   | Note[]
-  | Instant[];
+  | Instant[]
+  );
 
 // These needn't be classes (could just be interfaces), just experimenting
 export abstract class ShapedFeature<Shape extends HigherLevelFeatureShape,
-  Data extends ShapedFeatureData> {
+  Data extends ShapedFeatureData & {unit?: string}> {
   shape: Shape;
-  collected: Data;
+  collected: Data & {unit?: string};
 }
 
 export class Vector extends ShapedFeature<'vector', VectorFeature> {}
@@ -143,32 +145,46 @@
 
 export function toKnownShape(response: SimpleResponse): KnownShapedFeature {
   const deducedShape = deduceHigherLevelFeatureShape(response);
-  switch (deducedShape) {
-    case 'vector':
-      return response.features as Vector;
-    case 'matrix':
-      return response.features as Matrix;
-    case 'tracks':
-      return response.features as Tracks;
-    case 'notes':
-      return {
-        shape: deducedShape,
-        collected: mapFeaturesToNotes(
+  const shaped: KnownShapedFeature | null = (() => {
+    switch (deducedShape) {
+      case 'vector':
+        return response.features as Vector;
+      case 'matrix':
+        return response.features as Matrix;
+      case 'tracks':
+        return response.features as Tracks;
+      case 'notes':
+        // TODO refactor
+        const notes: Note[] & {unit?: string} = mapFeaturesToNotes(
           response.features.collected as FeatureList,
           response.outputDescriptor
-        )
-      };
-    case 'instants':
-      const featureData = response.features.collected as FeatureList;
-      return {
-        shape: deducedShape,
-        collected: featureData.map(feature => ({
-          time: toSeconds(feature.timestamp),
-          label: feature.label
-        }))
-      };
+        );
+        notes.unit = 'MIDI';
+        return {
+          shape: deducedShape,
+          collected: notes,
+        };
+      case 'instants':
+        const featureData = response.features.collected as FeatureList;
+        return {
+          shape: deducedShape,
+          collected: featureData.map(feature => ({
+            time: toSeconds(feature.timestamp),
+            label: feature.label
+          }))
+        };
+    }
+  })();
+  const unit = response.outputDescriptor.configured.unit;
+  if (shaped) {
+    const bodgeUnit = (shaped: KnownShapedFeature) => {
+      (shaped.collected as any).unit = unit;
+      return shaped;
+    };
+    return unit && !shaped.collected.unit ? bodgeUnit(shaped) : shaped;
+  } else {
+    throwShapeError();
   }
-  throwShapeError();
 }
 
 export interface PlotData {
--- a/src/app/visualisations/curve/curve.component.ts	Thu Jun 01 16:26:52 2017 +0100
+++ b/src/app/visualisations/curve/curve.component.ts	Thu Jun 01 18:55:55 2017 +0100
@@ -7,7 +7,7 @@
   Input
 } from '@angular/core';
 import {OnSeekHandler} from '../../playhead/PlayHeadHelpers';
-import {VectorFeature} from 'piper/HigherLevelUtilities';
+import {TracksFeature, VectorFeature} from 'piper/HigherLevelUtilities';
 
 @Component({
   selector: 'ugly-curve',
@@ -17,7 +17,7 @@
       [width]="width"
       [onSeek]="onSeek"
       [colour]="colour"
-      [tracks]="[curve]"
+      [tracks]="tracks"
     ></ugly-tracks>
   </ugly-cross-hair-inspector>`,
   changeDetection: ChangeDetectionStrategy.OnPush
@@ -26,6 +26,13 @@
   @Input() timeline: Timeline; // TODO refactor WaveComponents to have own Timeline, sharing a TimeContext
   @Input() onSeek: OnSeekHandler;
   @Input() width: number;
-  @Input() curve: VectorFeature;
+  @Input() set curve(curve: VectorFeature & {unit?: string}) {
+    const tempTracks: TracksFeature & {unit?: string} = [curve];
+    tempTracks.unit = curve.unit;
+    this.tracks = tempTracks;
+  }
+
+  private tracks: TracksFeature & {unit?: string};
+
   @Input() colour: string;
 }
--- a/src/app/visualisations/notes/notes.component.ts	Thu Jun 01 16:26:52 2017 +0100
+++ b/src/app/visualisations/notes/notes.component.ts	Thu Jun 01 18:55:55 2017 +0100
@@ -2,9 +2,10 @@
  * Created by lucast on 31/05/2017.
  */
 import {
+  InspectableVerticallyBoundedComponent,
   VerticallyBounded,
-  VerticallyBoundedWavesComponent,
-  VerticalScaleRenderer
+  VerticalScaleRenderer,
+  VerticalValueInspectorRenderer
 } from '../waves-base.component';
 import {
   ChangeDetectionStrategy,
@@ -21,10 +22,11 @@
   changeDetection: ChangeDetectionStrategy.OnPush,
   providers: [
     { provide: VerticallyBounded, useExisting: NotesComponent },
-    { provide: VerticalScaleRenderer, useExisting: NotesComponent }
+    { provide: VerticalScaleRenderer, useExisting: NotesComponent },
+    {provide: VerticalValueInspectorRenderer, useExisting: NotesComponent }
   ]
 })
-export class NotesComponent extends VerticallyBoundedWavesComponent<Note[]> {
+export class NotesComponent extends InspectableVerticallyBoundedComponent<Note[]> {
   private currentVerticalRange: [number, number];
 
   get range(): [number, number] {
--- a/src/app/visualisations/waves-base.component.ts	Thu Jun 01 16:26:52 2017 +0100
+++ b/src/app/visualisations/waves-base.component.ts	Thu Jun 01 18:55:55 2017 +0100
@@ -22,7 +22,7 @@
 export abstract class VerticalValueInspectorRenderer
   extends VerticalScaleRenderer {
   // TODO how do I know these layers are actually 'describable'?
-  abstract renderInspector(range: [number, number]): void;
+  abstract renderInspector(range: [number, number], unit?: string): void;
 }
 
 export abstract class WavesComponent<T extends ShapedFeatureData | AudioBuffer>
@@ -209,8 +209,10 @@
   @Input() set onSeek(handler: OnSeekHandler) {
     this.wrappedSeekHandler = (x: number) => {
       handler(x);
-      this.highlight.currentPosition = x;
-      this.highlight.update();
+      if (this.highlight) {
+        this.highlight.currentPosition = x;
+        this.highlight.update();
+      }
     };
   }
 
@@ -219,18 +221,20 @@
   }
 
 
-  renderInspector(range: [number, number]): void {
-    this.highlight = new Waves.helpers.HighlightLayer(
-      this.cachedFeatureLayers,
-      {
-        opacity: 0.7,
-        height: this.height,
-        color: '#c33c54', // TODO pass in?
-        labelOffset: 38,
-        yDomain: range,
-        unit: ''// TODO
-      }
-    );
-    this.addLayer(this.highlight);
+  renderInspector(range: [number, number], unit?: string): void {
+    if (range) {
+      this.highlight = new Waves.helpers.HighlightLayer(
+        this.cachedFeatureLayers,
+        {
+          opacity: 0.7,
+          height: this.height,
+          color: '#c33c54', // TODO pass in?
+          labelOffset: 38,
+          yDomain: range,
+          unit: unit || this.feature.unit || ''
+        }
+      );
+      this.addLayer(this.highlight);
+    }
   }
 }