# HG changeset patch
# User drewp@bigasterisk.com
# Date 1654247987 25200
# Node ID e2ed5ce362537b7b08b1cb1ebb0b0dd716ff0e25
# Parent 1dc96b97a544b43ba863485336a8da9d96ca4b5d
double spectrum views have a connected cursor
diff -r 1dc96b97a544 -r e2ed5ce36253 light9/ascoltami/Light9AscoltamiUi.ts
--- a/light9/ascoltami/Light9AscoltamiUi.ts Fri Jun 03 00:41:13 2022 -0700
+++ b/light9/ascoltami/Light9AscoltamiUi.ts Fri Jun 03 02:19:47 2022 -0700
@@ -1,14 +1,19 @@
import debug from "debug";
import { css, html, LitElement } from "lit";
import { customElement, property } from "lit/decorators.js";
+import { classMap } from "lit/directives/class-map.js";
import { NamedNode } from "n3";
+import Sylvester from "sylvester";
+import { Zoom } from "../web/light9-timeline-audio";
+import { PlainViewState } from "../web/Light9CursorCanvas";
import { getTopGraph } from "../web/RdfdbSyncedGraph";
import { SyncedGraph } from "../web/SyncedGraph";
-export { RdfdbSyncedGraph } from "../web/RdfdbSyncedGraph";
+import { TimingUpdate } from "./main";
export { Light9TimelineAudio } from "../web/light9-timeline-audio";
-import { classMap } from "lit/directives/class-map.js";
-import { TimingUpdate } from "./main";
-import { Zoom } from "../web/light9-timeline-audio";
+export { Light9CursorCanvas } from "../web/Light9CursorCanvas";
+export { RdfdbSyncedGraph } from "../web/RdfdbSyncedGraph";
+
+const $V = Sylvester.Vector.create;
debug.enable("*");
const log = debug("asco");
@@ -31,18 +36,30 @@
@property() isPlaying: boolean = false;
@property() show: NamedNode | null = null;
@property() song: NamedNode | null = null;
- @property() t: number = 0;
@property() currentDuration: number = 0;
@property() zoom: Zoom;
@property() overviewZoom: Zoom;
+ @property() viewState: PlainViewState | null = null;
static styles = [
css`
.timeRow {
margin: 14px;
+ position: relative;
}
- light9-timeline-audio {
+ #overview {
+ height: 60px;
+ }
+ #zoomed {
+ margin-top: 40px;
height: 80px;
}
+ #cursor {
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 100%;
+ }
`,
];
render() {
@@ -54,8 +71,9 @@
@@ -149,7 +167,22 @@
this.currentDuration = data.duration;
this.song = new NamedNode(data.song);
this.overviewZoom = { duration: data.duration, t1: 0, t2: data.duration };
- this.zoom = { duration: data.duration, t1: data.t - 2, t2: data.t + 20 };
+ const t1 = data.t - 2,
+ t2 = data.t + 20;
+ this.zoom = { duration: data.duration, t1, t2 };
+ const timeRow = this.shadowRoot!.querySelector(".timeRow") as HTMLDivElement;
+ const w = timeRow.offsetWidth;
+ this.viewState = {
+ zoomSpec: { t1: () => t1, t2: () => t2 },
+ cursor: { t: () => data.t },
+ audioY: () => 0,
+ audioH: () => 60,
+ zoomedTimeY: () => 60,
+ zoomedTimeH: () => 40,
+ fullZoomX: (sec: number) => (sec / data.duration) * w,
+ zoomInX: (sec: number) => ((sec - t1) / (t2 - t1)) * w,
+ mouse: { pos: () => $V([0, 0]) },
+ };
});
}
diff -r 1dc96b97a544 -r e2ed5ce36253 light9/web/Light9CursorCanvas.ts
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/light9/web/Light9CursorCanvas.ts Fri Jun 03 02:19:47 2022 -0700
@@ -0,0 +1,146 @@
+import debug from "debug";
+import { css, html, LitElement, PropertyValues } from "lit";
+import { customElement, property } from "lit/decorators.js";
+import Sylvester from "sylvester";
+import { line } from "./drawing";
+
+const $V = Sylvester.Vector.create;
+
+const log = debug("cursor");
+
+export interface PlainViewState {
+ zoomSpec: { t1: () => number; t2: () => number };
+ fullZoomX: (t: number) => number;
+ zoomInX: (t: number) => number;
+ cursor: { t: () => number };
+ audioY: () => number;
+ audioH: () => number;
+ zoomedTimeY: () => number; // not what you think- it's the zone in between
+ zoomedTimeH: () => number;
+ mouse: { pos: () => Vector };
+}
+
+// For cases where you have a zoomed-out view on top of a zoomed-in view,
+// overlay this element and it'll draw a time cursor on both views.
+@customElement("light9-cursor-canvas")
+export class Light9CursorCanvas extends LitElement {
+ cursorPath: null | {
+ top0: Vector;
+ top1: Vector;
+ mid0: Vector;
+ mid1: Vector;
+ mid2: Vector;
+ mid3: Vector;
+ bot0: Vector;
+ bot1: Vector;
+ } = null;
+ canvasEl!: HTMLCanvasElement;
+ ctx!: CanvasRenderingContext2D;
+ offsetWidth: any;
+ offsetHeight: any;
+ @property() viewState: PlainViewState | null = null;
+ static styles = [
+ css`
+ :host {
+ display: inline-block;
+ }
+ `,
+ ];
+ render() {
+ return html`
`;
+ }
+
+ updated(changedProperties: PropertyValues) {
+ if (changedProperties.has("viewState")) {
+ this.redrawCursor();
+ }
+ }
+ connectedCallback() {
+ super.connectedCallback();
+ window.addEventListener("resize", this.onResize);
+ this.onResize();
+ }
+
+ firstUpdated() {
+ this.canvasEl = this.shadowRoot!.firstElementChild as HTMLCanvasElement;
+ this.onResize();
+ this.ctx = this.canvasEl.getContext("2d")!;
+ }
+
+ disconnectedCallback() {
+ window.removeEventListener("resize", this.onResize);
+ super.disconnectedCallback();
+ }
+
+ // onViewState() {
+ // ko.computed(this.redrawCursor.bind(this));
+ // }
+
+ onResize() {
+ if (!this.canvasEl) {
+ return;
+ }
+ this.canvasEl.width = this.offsetWidth;
+ this.canvasEl.height = this.offsetHeight;
+ this.redrawCursor();
+ }
+
+ redrawCursor() {
+ const vs = this.viewState;
+ if (!vs) {
+ return;
+ }
+ const dependOn = [vs.zoomSpec.t1(), vs.zoomSpec.t2()];
+ const xZoomedOut = vs.fullZoomX(vs.cursor.t());
+ const xZoomedIn = vs.zoomInX(vs.cursor.t());
+
+ this.cursorPath = {
+ top0: $V([xZoomedOut, vs.audioY()]),
+ top1: $V([xZoomedOut, vs.audioY() + vs.audioH()]),
+ mid0: $V([xZoomedIn + 2, vs.zoomedTimeY() + vs.zoomedTimeH()]),
+ mid1: $V([xZoomedIn - 2, vs.zoomedTimeY() + vs.zoomedTimeH()]),
+ mid2: $V([xZoomedOut - 1, vs.audioY() + vs.audioH()]),
+ mid3: $V([xZoomedOut + 1, vs.audioY() + vs.audioH()]),
+ bot0: $V([xZoomedIn, vs.zoomedTimeY() + vs.zoomedTimeH()]),
+ bot1: $V([xZoomedIn, this.offsetHeight]),
+ };
+ this.redraw();
+ }
+
+ redraw() {
+ if (!this.ctx || !this.viewState) {
+ return;
+ }
+ this.ctx.clearRect(0, 0, this.canvasEl.width, this.canvasEl.height);
+
+ this.ctx.strokeStyle = "#fff";
+ this.ctx.lineWidth = 0.5;
+ this.ctx.beginPath();
+ const mouse = this.viewState.mouse.pos();
+ line(this.ctx, $V([0, mouse.e(2)]), $V([this.canvasEl.width, mouse.e(2)]));
+ line(this.ctx, $V([mouse.e(1), 0]), $V([mouse.e(1), this.canvasEl.height]));
+ this.ctx.stroke();
+
+ if (this.cursorPath) {
+ this.ctx.strokeStyle = "#ff0303";
+ this.ctx.lineWidth = 1.5;
+ this.ctx.beginPath();
+ line(this.ctx, this.cursorPath.top0, this.cursorPath.top1);
+ this.ctx.stroke();
+
+ this.ctx.fillStyle = "#9c0303";
+ this.ctx.beginPath();
+ this.ctx.moveTo(this.cursorPath.mid0.e(1), this.cursorPath.mid0.e(2));
+ for (let p of [this.cursorPath.mid1, this.cursorPath.mid2, this.cursorPath.mid3]) {
+ this.ctx.lineTo(p.e(1), p.e(2));
+ }
+ this.ctx.fill();
+
+ this.ctx.strokeStyle = "#ff0303";
+ this.ctx.lineWidth = 3;
+ this.ctx.beginPath();
+ line(this.ctx, this.cursorPath.bot0, this.cursorPath.bot1, "#ff0303", "3px");
+ this.ctx.stroke();
+ }
+ }
+}
diff -r 1dc96b97a544 -r e2ed5ce36253 light9/web/drawing.ts
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/light9/web/drawing.ts Fri Jun 03 02:19:47 2022 -0700
@@ -0,0 +1,64 @@
+
+export function svgPathFromPoints(pts: { forEach: (arg0: (p: any) => void) => void }) {
+ let out = "";
+ pts.forEach(function (p: Number[] | { elements: Number[] }) {
+ let x, y;
+ if ((p as any).elements) {
+ // for vec2
+ [x, y] = (p as any).elements;
+ } else {
+ [x, y] = p as Number[];
+ }
+ if (out.length === 0) {
+ out = "M ";
+ } else {
+ out += "L ";
+ }
+ out += "" + x + "," + y + " ";
+ });
+ return out;
+};
+
+export function line(
+ ctx: { moveTo: (arg0: any, arg1: any) => void; lineTo: (arg0: any, arg1: any) => any },
+ p1: { e: (arg0: number) => any },
+ p2: { e: (arg0: number) => any }
+) {
+ ctx.moveTo(p1.e(1), p1.e(2));
+ return ctx.lineTo(p2.e(1), p2.e(2));
+};
+
+// http://stackoverflow.com/a/4959890
+export function roundRect(
+ ctx: {
+ beginPath: () => void;
+ moveTo: (arg0: any, arg1: any) => void;
+ lineTo: (arg0: number, arg1: number) => void;
+ arc: (arg0: number, arg1: number, arg2: any, arg3: number, arg4: number, arg5: boolean) => void;
+ closePath: () => any;
+ },
+ sx: number,
+ sy: number,
+ ex: number,
+ ey: number,
+ r: number
+) {
+ const d2r = Math.PI / 180;
+ if (ex - sx - 2 * r < 0) {
+ r = (ex - sx) / 2;
+ } // ensure that the radius isn't too large for x
+ if (ey - sy - 2 * r < 0) {
+ r = (ey - sy) / 2;
+ } // ensure that the radius isn't too large for y
+ ctx.beginPath();
+ ctx.moveTo(sx + r, sy);
+ ctx.lineTo(ex - r, sy);
+ ctx.arc(ex - r, sy + r, r, d2r * 270, d2r * 360, false);
+ ctx.lineTo(ex, ey - r);
+ ctx.arc(ex - r, ey - r, r, d2r * 0, d2r * 90, false);
+ ctx.lineTo(sx + r, ey);
+ ctx.arc(sx + r, ey - r, r, d2r * 90, d2r * 180, false);
+ ctx.lineTo(sx, sy + r);
+ ctx.arc(sx + r, sy + r, r, d2r * 180, d2r * 270, false);
+ return ctx.closePath();
+};
diff -r 1dc96b97a544 -r e2ed5ce36253 light9/web/light9-timeline-audio.ts
--- a/light9/web/light9-timeline-audio.ts Fri Jun 03 00:41:13 2022 -0700
+++ b/light9/web/light9-timeline-audio.ts Fri Jun 03 02:19:47 2022 -0700
@@ -1,9 +1,7 @@
import { debug } from "debug";
-
-import { css, html, LitElement, PropertyValues, TemplateResult } from "lit";
+import { html, LitElement, PropertyValues } from "lit";
import { customElement, property, state } from "lit/decorators.js";
import { NamedNode } from "n3";
-import { loadConfigFromFile } from "vite";
import { getTopGraph } from "./RdfdbSyncedGraph";
import { SyncedGraph } from "./SyncedGraph";
@@ -46,8 +44,7 @@
img {
height: 100%;
position: relative;
- transition: left .1s linear;
-
+ transition: left 0.1s linear;
}
diff -r 1dc96b97a544 -r e2ed5ce36253 light9/web/timeline/adjusters.ts
--- a/light9/web/timeline/adjusters.ts Fri Jun 03 00:41:13 2022 -0700
+++ b/light9/web/timeline/adjusters.ts Fri Jun 03 02:19:47 2022 -0700
@@ -4,7 +4,7 @@
import { throttle } from "underscore";
import * as d3 from "d3";
import { Adjustable } from "./adjustable";
-import * as Drawing from "./drawing";
+import * as Drawing from "../drawing";
// https://www.npmjs.com/package/@types/sylvester Global values: $L, $M, $P, $V, Line, Matrix, Plane, Sylvester, Vector
const log = debug("adjusters");
diff -r 1dc96b97a544 -r e2ed5ce36253 light9/web/timeline/cursor_canvas.coffee
--- a/light9/web/timeline/cursor_canvas.coffee Fri Jun 03 00:41:13 2022 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,69 +0,0 @@
-coffeeElementSetup(class CursorCanvas extends Polymer.mixinBehaviors([Polymer.IronResizableBehavior], Polymer.Element)
- @is: 'light9-cursor-canvas'
- @getter_properties:
- viewState: { type: Object, notify: true, observer: "onViewState" }
- ready: ->
- super.ready()
- @cursorPath = null
- @ctx = @$.canvas.getContext('2d')
- @onResize()
- @addEventListener('iron-resize', @onResize.bind(@))
-
- onViewState: ->
- ko.computed(@redrawCursor.bind(@))
-
- onResize: (ev) ->
- @$.canvas.width = @offsetWidth
- @$.canvas.height = @offsetHeight
- @redrawCursor()
-
- redrawCursor: ->
- vs = @viewState
- dependOn = [vs.zoomSpec.t1(), vs.zoomSpec.t2()]
- xZoomedOut = vs.fullZoomX(vs.cursor.t())
- xZoomedIn = vs.zoomInX(vs.cursor.t())
-
- @cursorPath = {
- top0: $V([xZoomedOut, vs.audioY()])
- top1: $V([xZoomedOut, vs.audioY() + vs.audioH()])
- mid0: $V([xZoomedIn + 2, vs.zoomedTimeY() + vs.zoomedTimeH()])
- mid1: $V([xZoomedIn - 2, vs.zoomedTimeY() + vs.zoomedTimeH()])
- mid2: $V([xZoomedOut - 1, vs.audioY() + vs.audioH()])
- mid3: $V([xZoomedOut + 1, vs.audioY() + vs.audioH()])
- bot0: $V([xZoomedIn, vs.zoomedTimeY() + vs.zoomedTimeH()])
- bot1: $V([xZoomedIn, @offsetHeight])
- }
- @redraw()
-
- redraw: ->
- return unless @ctx
- @ctx.clearRect(0, 0, @$.canvas.width, @$.canvas.height)
-
- @ctx.strokeStyle = '#fff'
- @ctx.lineWidth = 0.5
- @ctx.beginPath()
- mouse = @viewState.mouse.pos()
- Drawing.line(@ctx, $V([0, mouse.e(2)]), $V([@$.canvas.width, mouse.e(2)]))
- Drawing.line(@ctx, $V([mouse.e(1), 0]), $V([mouse.e(1), @$.canvas.height]))
- @ctx.stroke()
-
- if @cursorPath
- @ctx.strokeStyle = '#ff0303'
- @ctx.lineWidth = 1.5
- @ctx.beginPath()
- Drawing.line(@ctx, @cursorPath.top0, @cursorPath.top1)
- @ctx.stroke()
-
- @ctx.fillStyle = '#9c0303'
- @ctx.beginPath()
- @ctx.moveTo(@cursorPath.mid0.e(1), @cursorPath.mid0.e(2))
- @ctx.lineTo(p.e(1), p.e(2)) for p in [
- @cursorPath.mid1, @cursorPath.mid2, @cursorPath.mid3]
- @ctx.fill()
-
- @ctx.strokeStyle = '#ff0303'
- @ctx.lineWidth = 3
- @ctx.beginPath()
- Drawing.line(@ctx, @cursorPath.bot0, @cursorPath.bot1, '#ff0303', '3px')
- @ctx.stroke()
-)
\ No newline at end of file
diff -r 1dc96b97a544 -r e2ed5ce36253 light9/web/timeline/drawing.ts
--- a/light9/web/timeline/drawing.ts Fri Jun 03 00:41:13 2022 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-
-export function svgPathFromPoints(pts: { forEach: (arg0: (p: any) => void) => void }) {
- let out = "";
- pts.forEach(function (p: Number[] | { elements: Number[] }) {
- let x, y;
- if ((p as any).elements) {
- // for vec2
- [x, y] = (p as any).elements;
- } else {
- [x, y] = p as Number[];
- }
- if (out.length === 0) {
- out = "M ";
- } else {
- out += "L ";
- }
- out += "" + x + "," + y + " ";
- });
- return out;
-};
-
-export function line(
- ctx: { moveTo: (arg0: any, arg1: any) => void; lineTo: (arg0: any, arg1: any) => any },
- p1: { e: (arg0: number) => any },
- p2: { e: (arg0: number) => any }
-) {
- ctx.moveTo(p1.e(1), p1.e(2));
- return ctx.lineTo(p2.e(1), p2.e(2));
-};
-
-// http://stackoverflow.com/a/4959890
-export function roundRect(
- ctx: {
- beginPath: () => void;
- moveTo: (arg0: any, arg1: any) => void;
- lineTo: (arg0: number, arg1: number) => void;
- arc: (arg0: number, arg1: number, arg2: any, arg3: number, arg4: number, arg5: boolean) => void;
- closePath: () => any;
- },
- sx: number,
- sy: number,
- ex: number,
- ey: number,
- r: number
-) {
- const d2r = Math.PI / 180;
- if (ex - sx - 2 * r < 0) {
- r = (ex - sx) / 2;
- } // ensure that the radius isn't too large for x
- if (ey - sy - 2 * r < 0) {
- r = (ey - sy) / 2;
- } // ensure that the radius isn't too large for y
- ctx.beginPath();
- ctx.moveTo(sx + r, sy);
- ctx.lineTo(ex - r, sy);
- ctx.arc(ex - r, sy + r, r, d2r * 270, d2r * 360, false);
- ctx.lineTo(ex, ey - r);
- ctx.arc(ex - r, ey - r, r, d2r * 0, d2r * 90, false);
- ctx.lineTo(sx + r, ey);
- ctx.arc(sx + r, ey - r, r, d2r * 90, d2r * 180, false);
- ctx.lineTo(sx, sy + r);
- ctx.arc(sx + r, sy + r, r, d2r * 180, d2r * 270, false);
- return ctx.closePath();
-};