@sudobility/music_drawing 0.0.23 → 0.0.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/dist/canvas-renderer.d.ts +53 -0
  2. package/dist/canvas-renderer.d.ts.map +1 -1
  3. package/dist/canvas-renderer.js +123 -2
  4. package/dist/canvas-renderer.js.map +1 -1
  5. package/dist/hit-test.d.ts +101 -0
  6. package/dist/hit-test.d.ts.map +1 -0
  7. package/dist/hit-test.js +213 -0
  8. package/dist/hit-test.js.map +1 -0
  9. package/dist/index.d.ts +6 -0
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +6 -0
  12. package/dist/index.js.map +1 -1
  13. package/dist/layout.d.ts +15 -1
  14. package/dist/layout.d.ts.map +1 -1
  15. package/dist/layout.js +36 -3
  16. package/dist/layout.js.map +1 -1
  17. package/dist/measure-content.d.ts +3 -1
  18. package/dist/measure-content.d.ts.map +1 -1
  19. package/dist/measure-content.js +2 -2
  20. package/dist/measure-content.js.map +1 -1
  21. package/dist/note-colors.d.ts +55 -0
  22. package/dist/note-colors.d.ts.map +1 -0
  23. package/dist/note-colors.js +31 -0
  24. package/dist/note-colors.js.map +1 -0
  25. package/dist/note-drag.d.ts +29 -0
  26. package/dist/note-drag.d.ts.map +1 -0
  27. package/dist/note-drag.js +27 -0
  28. package/dist/note-drag.js.map +1 -0
  29. package/dist/playback-scroll.d.ts +40 -0
  30. package/dist/playback-scroll.d.ts.map +1 -0
  31. package/dist/playback-scroll.js +128 -0
  32. package/dist/playback-scroll.js.map +1 -0
  33. package/dist/playhead.d.ts +41 -7
  34. package/dist/playhead.d.ts.map +1 -1
  35. package/dist/playhead.js +153 -2
  36. package/dist/playhead.js.map +1 -1
  37. package/dist/render-theme.d.ts +30 -0
  38. package/dist/render-theme.d.ts.map +1 -0
  39. package/dist/render-theme.js +23 -0
  40. package/dist/render-theme.js.map +1 -0
  41. package/dist/skia/index.d.ts +1 -0
  42. package/dist/skia/index.d.ts.map +1 -1
  43. package/dist/skia/index.js +1 -0
  44. package/dist/skia/index.js.map +1 -1
  45. package/dist/skia/skia-api.d.ts +8 -0
  46. package/dist/skia/skia-api.d.ts.map +1 -1
  47. package/dist/skia/skia-context.d.ts +1 -0
  48. package/dist/skia/skia-context.d.ts.map +1 -1
  49. package/dist/skia/skia-context.js +104 -8
  50. package/dist/skia/skia-context.js.map +1 -1
  51. package/dist/stave-header.d.ts +29 -0
  52. package/dist/stave-header.d.ts.map +1 -0
  53. package/dist/stave-header.js +122 -0
  54. package/dist/stave-header.js.map +1 -0
  55. package/dist/test/fake-skia.d.ts +12 -1
  56. package/dist/test/fake-skia.d.ts.map +1 -1
  57. package/dist/test/fake-skia.js +8 -1
  58. package/dist/test/fake-skia.js.map +1 -1
  59. package/dist/track-gutter.d.ts +14 -0
  60. package/dist/track-gutter.d.ts.map +1 -0
  61. package/dist/track-gutter.js +37 -0
  62. package/dist/track-gutter.js.map +1 -0
  63. package/package.json +3 -3
@@ -0,0 +1,31 @@
1
+ export function buildNoteColors({ selectedIds, playingIds, regenerated, }) {
2
+ const colors = new Map();
3
+ const selectedRole = regenerated ? 'regenerated' : 'selected';
4
+ for (const id of selectedIds)
5
+ colors.set(id, selectedRole);
6
+ for (const id of playingIds)
7
+ colors.set(id, 'playing');
8
+ return colors;
9
+ }
10
+ /**
11
+ * The colours actually painted: the selection with the sounding notes over it.
12
+ *
13
+ * **Only the active track's sounding notes light up.** Every track's used to,
14
+ * on the reasoning that a state colour marks a note as needing attention
15
+ * wherever it is — which is right for selection, where a cross-track selection
16
+ * is something the reader made on purpose. It is wrong for playback: on a
17
+ * large score the lit notes then scatter across whichever parts happen to be
18
+ * sounding, which reads as random rather than as a playhead, and the one track
19
+ * being read goes dark whenever it rests. The piano keyboard already showed
20
+ * the active track alone, and this is what makes the notation agree with it.
21
+ */
22
+ export function noteColorsFor({ selectedIds, sounding, activeTrackId, regenerated, }) {
23
+ return buildNoteColors({
24
+ selectedIds,
25
+ playingIds: sounding
26
+ .filter(note => note.trackId === activeTrackId)
27
+ .map(note => note.noteId),
28
+ regenerated,
29
+ });
30
+ }
31
+ //# sourceMappingURL=note-colors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"note-colors.js","sourceRoot":"","sources":["../src/note-colors.ts"],"names":[],"mappings":"AA4BA,MAAM,UAAU,eAAe,CAAC,EAC9B,WAAW,EACX,UAAU,EACV,WAAW,GACW;IACtB,MAAM,MAAM,GAAG,IAAI,GAAG,EAAyB,CAAC;IAChD,MAAM,YAAY,GAAkB,WAAW,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,UAAU,CAAC;IAC7E,KAAK,MAAM,EAAE,IAAI,WAAW;QAAE,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,YAAY,CAAC,CAAC;IAC3D,KAAK,MAAM,EAAE,IAAI,UAAU;QAAE,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;IACvD,OAAO,MAAM,CAAC;AAChB,CAAC;AAiBD;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,aAAa,CAAC,EAC5B,WAAW,EACX,QAAQ,EACR,aAAa,EACb,WAAW,GACS;IACpB,OAAO,eAAe,CAAC;QACrB,WAAW;QACX,UAAU,EAAE,QAAQ;aACjB,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,OAAO,KAAK,aAAa,CAAC;aAC9C,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC;QAC3B,WAAW;KACZ,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * What an Option+drag resolves to: which track, and how far in time.
3
+ *
4
+ * Pure over a layout plan and a point — no store, no React — in the same shape
5
+ * as `pitch-drag.ts` and `playback-scroll.ts`, so the rules are testable
6
+ * without rendering anything.
7
+ */
8
+ import type { Score } from '@sudobility/music_types';
9
+ import type { LayoutPlan } from './layout.js';
10
+ import type { Point } from './hit-test.js';
11
+ /** The note the gesture is anchored to: the one under the pointer at press. */
12
+ export type NoteDrag = {
13
+ anchorId: string;
14
+ anchorTick: number;
15
+ };
16
+ /** Where a drop would put the selection. */
17
+ export type DropTarget = {
18
+ trackId: string;
19
+ deltaTicks: number;
20
+ };
21
+ /**
22
+ * The track and tick delta a drop at `point` would produce, or null when the
23
+ * pointer is not over a stave.
24
+ *
25
+ * The delta is relative to the **anchor**, so every other note in the
26
+ * selection keeps its offset and a phrase keeps its shape.
27
+ */
28
+ export declare function resolveDrop(plan: LayoutPlan, score: Score, drag: NoteDrag, point: Point, snapTicks: number): DropTarget | null;
29
+ //# sourceMappingURL=note-drag.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"note-drag.d.ts","sourceRoot":"","sources":["../src/note-drag.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAG9C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAE3C,+EAA+E;AAC/E,MAAM,MAAM,QAAQ,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC;AAEhE,4CAA4C;AAC5C,MAAM,MAAM,UAAU,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC;AAQjE;;;;;;GAMG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,UAAU,EAChB,KAAK,EAAE,KAAK,EACZ,IAAI,EAAE,QAAQ,EACd,KAAK,EAAE,KAAK,EACZ,SAAS,EAAE,MAAM,GAChB,UAAU,GAAG,IAAI,CAUnB"}
@@ -0,0 +1,27 @@
1
+ import { tickForPoint } from './playhead.js';
2
+ import { trackIdAtContentPoint } from './hit-test.js';
3
+ /** Rounds `tick` to the nearest multiple of `snapTicks`. */
4
+ function snap(tick, snapTicks) {
5
+ if (snapTicks <= 0)
6
+ return tick;
7
+ return Math.round(tick / snapTicks) * snapTicks;
8
+ }
9
+ /**
10
+ * The track and tick delta a drop at `point` would produce, or null when the
11
+ * pointer is not over a stave.
12
+ *
13
+ * The delta is relative to the **anchor**, so every other note in the
14
+ * selection keeps its offset and a phrase keeps its shape.
15
+ */
16
+ export function resolveDrop(plan, score, drag, point, snapTicks) {
17
+ const trackId = trackIdAtContentPoint(plan, point);
18
+ if (!trackId)
19
+ return null;
20
+ // `tickForPoint` needs the score as well as the plan: it reads measure
21
+ // timings, not just geometry. Same call the caret already makes.
22
+ const tick = tickForPoint(plan, score, point.x, point.y);
23
+ if (tick === null)
24
+ return null;
25
+ return { trackId, deltaTicks: snap(tick, snapTicks) - drag.anchorTick };
26
+ }
27
+ //# sourceMappingURL=note-drag.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"note-drag.js","sourceRoot":"","sources":["../src/note-drag.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAStD,4DAA4D;AAC5D,SAAS,IAAI,CAAC,IAAY,EAAE,SAAiB;IAC3C,IAAI,SAAS,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAChC,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,SAAS,CAAC,GAAG,SAAS,CAAC;AAClD,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CACzB,IAAgB,EAChB,KAAY,EACZ,IAAc,EACd,KAAY,EACZ,SAAiB;IAEjB,MAAM,OAAO,GAAG,qBAAqB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACnD,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAE1B,uEAAuE;IACvE,iEAAiE;IACjE,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;IACzD,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAE/B,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;AAC1E,CAAC"}
@@ -0,0 +1,40 @@
1
+ import type { LayoutPlan } from './layout.js';
2
+ export type PlaybackScrollParams = {
3
+ plan: LayoutPlan;
4
+ layoutMode: 'page' | 'continuous';
5
+ zoom: number;
6
+ /** Index of the measure now playing. */
7
+ measureIndex: number;
8
+ /** The measure's box in logical (unzoomed) content coordinates. */
9
+ measureX: number;
10
+ /** The measure's width in logical (unzoomed) content coordinates. */
11
+ measureWidth: number;
12
+ /** Current horizontal scroll position, in on-screen px. */
13
+ scrollLeft: number;
14
+ /** Visible width of the scroll box, in on-screen px. */
15
+ viewportWidth: number;
16
+ /** Current vertical scroll position, in on-screen px. */
17
+ scrollTop: number;
18
+ /** Visible height of the scroll box, in on-screen px. */
19
+ viewportHeight: number;
20
+ /** Breathing room left before the target, in on-screen px. */
21
+ margin: number;
22
+ };
23
+ export type PlaybackScrollTarget = {
24
+ left: number;
25
+ top: number;
26
+ };
27
+ /**
28
+ * Where to scroll for the measure now playing, or `null` to leave the scroll
29
+ * box alone.
30
+ *
31
+ * Every "no move needed" path returns `null` rather than the current scroll
32
+ * position, and that distinction is load-bearing: the caller scrolls with
33
+ * `behavior: 'smooth'`, and issuing *any* `scrollTo` — even one naming the
34
+ * position the box is already heading to — cancels the animation in flight and
35
+ * strands it wherever it had reached. Returning the current position once per
36
+ * measure therefore killed each scroll a moment after it started, and the sheet
37
+ * appeared not to follow the music at all.
38
+ */
39
+ export declare function playbackScrollTarget({ plan, layoutMode, zoom, measureIndex, measureX, measureWidth, scrollLeft, viewportWidth, scrollTop, viewportHeight, margin, }: PlaybackScrollParams): PlaybackScrollTarget | null;
40
+ //# sourceMappingURL=playback-scroll.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"playback-scroll.d.ts","sourceRoot":"","sources":["../src/playback-scroll.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,EAAE,UAAU,CAAC;IACjB,UAAU,EAAE,MAAM,GAAG,YAAY,CAAC;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,wCAAwC;IACxC,YAAY,EAAE,MAAM,CAAC;IACrB,mEAAmE;IACnE,QAAQ,EAAE,MAAM,CAAC;IACjB,qEAAqE;IACrE,YAAY,EAAE,MAAM,CAAC;IACrB,2DAA2D;IAC3D,UAAU,EAAE,MAAM,CAAC;IACnB,wDAAwD;IACxD,aAAa,EAAE,MAAM,CAAC;IACtB,yDAAyD;IACzD,SAAS,EAAE,MAAM,CAAC;IAClB,yDAAyD;IACzD,cAAc,EAAE,MAAM,CAAC;IACvB,8DAA8D;IAC9D,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AA4BjE;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAAC,EACnC,IAAI,EACJ,UAAU,EACV,IAAI,EACJ,YAAY,EACZ,QAAQ,EACR,YAAY,EACZ,UAAU,EACV,aAAa,EACb,SAAS,EACT,cAAc,EACd,MAAM,GACP,EAAE,oBAAoB,GAAG,oBAAoB,GAAG,IAAI,CA4EpD"}
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Where to scroll so the playing measure stays in view.
3
+ *
4
+ * Pure over the `LayoutPlan` — no DOM, no store — so the arithmetic is
5
+ * unit-testable, in the same shape as `autoscroll.ts` and `track-gutter.ts`.
6
+ *
7
+ * The rule that matters here is that following playback must not undo the
8
+ * reader's own vertical scroll. A multi-track score is taller than the viewport,
9
+ * so the reader picks a track to watch; scrolling to the top of each new system
10
+ * threw that away and snapped back to track 1 every time the music wrapped.
11
+ * Both modes therefore preserve vertical position, by different means:
12
+ *
13
+ * - **Page mode** scrolls only when the system being played is not already on
14
+ * screen, and then keeps the *offset within the system*: if track 3 sits at
15
+ * the top of the viewport, it still does after the wrap, because the new
16
+ * system's top replaces the old one's and the reader's offset into it is
17
+ * carried over.
18
+ * - **Continuous mode** is one very wide system, so following the music is
19
+ * purely horizontal and vertical scroll is left completely alone. It too
20
+ * moves only when the playing measure is reaching the edge of the viewport.
21
+ */
22
+ import { TRACK_INFO_WIDTH } from './layout.js';
23
+ /** The system containing `measureIndex`, or null if it is outside the plan. */
24
+ function systemOf(plan, measureIndex) {
25
+ return (plan.systems.find(s => s.measureIndices.includes(measureIndex)) ?? null);
26
+ }
27
+ /**
28
+ * The system the reader is currently looking at: the one spanning the top of
29
+ * the viewport, else the last one above it (which is where a scroll position
30
+ * inside an inter-system gap lands), else the first.
31
+ */
32
+ function systemAtViewportTop(plan, scrollTop, zoom) {
33
+ const y = scrollTop / zoom;
34
+ let best = plan.systems[0] ?? null;
35
+ for (const system of plan.systems) {
36
+ if (system.yTop <= y)
37
+ best = system;
38
+ if (system.yTop <= y && y < system.yBottom)
39
+ return system;
40
+ }
41
+ return best;
42
+ }
43
+ /**
44
+ * Where to scroll for the measure now playing, or `null` to leave the scroll
45
+ * box alone.
46
+ *
47
+ * Every "no move needed" path returns `null` rather than the current scroll
48
+ * position, and that distinction is load-bearing: the caller scrolls with
49
+ * `behavior: 'smooth'`, and issuing *any* `scrollTo` — even one naming the
50
+ * position the box is already heading to — cancels the animation in flight and
51
+ * strands it wherever it had reached. Returning the current position once per
52
+ * measure therefore killed each scroll a moment after it started, and the sheet
53
+ * appeared not to follow the music at all.
54
+ */
55
+ export function playbackScrollTarget({ plan, layoutMode, zoom, measureIndex, measureX, measureWidth, scrollLeft, viewportWidth, scrollTop, viewportHeight, margin, }) {
56
+ const target = systemOf(plan, measureIndex);
57
+ if (!target)
58
+ return null;
59
+ if (layoutMode === 'continuous') {
60
+ // The track-info gutter is pinned to the viewport's left edge and painted
61
+ // over the sheet, so the target has to clear its width as well as the
62
+ // margin — scrolling the playing measure to `margin` parked it, and the
63
+ // caret travelling through it, *behind* the track info for most of every
64
+ // measure. Gutter width scales with zoom: it is cleared in the zoom-scaled
65
+ // space.
66
+ //
67
+ // Vertical is left completely alone: one long system means there is no
68
+ // "next line" to follow, so any vertical move would be the caller fighting
69
+ // the reader for the scrollbar.
70
+ const gutter = TRACK_INFO_WIDTH * zoom;
71
+ const measureLeft = measureX * zoom;
72
+ const measureRight = (measureX + measureWidth) * zoom;
73
+ // Only move once the music is actually reaching the edge of what the reader
74
+ // can see. This used to re-target every measure, which both fought the
75
+ // reader for the scrollbar and meant a fresh smooth-scroll animation was
76
+ // started before the previous one had travelled anywhere.
77
+ const firstVisible = scrollLeft + gutter + margin;
78
+ const lastVisible = scrollLeft + viewportWidth - margin;
79
+ if (measureLeft >= firstVisible && measureRight <= lastVisible)
80
+ return null;
81
+ return { left: Math.max(0, measureLeft - gutter - margin), top: scrollTop };
82
+ }
83
+ // Page mode wraps every system to the viewport and does not scroll
84
+ // horizontally at all, so there is no horizontal following to do — and
85
+ // asking for one would scroll the sheet under the gutter with no way for the
86
+ // reader to bring it back.
87
+ const left = 0;
88
+ // Already on screen in full: leave it alone. Wrapping to a new system is not
89
+ // by itself a reason to scroll — a viewport tall enough to show several
90
+ // systems was being yanked up a line every time the music crossed into the
91
+ // next one, long before the caret was anywhere near the bottom of what the
92
+ // reader could see. Only a target the reader cannot actually see justifies
93
+ // moving the page under them.
94
+ //
95
+ // A system taller than the viewport can never satisfy this (a multi-track
96
+ // score is the normal case), so that falls through to the offset-carrying
97
+ // path below, which is what it is there for.
98
+ const targetTop = target.yTop * zoom;
99
+ const targetBottom = target.yBottom * zoom;
100
+ if (targetTop >= scrollTop && targetBottom <= scrollTop + viewportHeight)
101
+ return null;
102
+ const current = systemAtViewportTop(plan, scrollTop, zoom);
103
+ // Same system: the music has not wrapped, so nothing vertical needs to move.
104
+ if (!current || current === target)
105
+ return null;
106
+ // Carry the reader's offset into the system across to the new one, but never
107
+ // so far that the music leaves the screen.
108
+ //
109
+ // The clamp is what makes this safe. `systemAtViewportTop` answers with the
110
+ // system *above* when the scroll position has landed in the gap between two
111
+ // systems — which is exactly where the previous wrap tends to leave it — so
112
+ // the raw offset came out close to a whole system tall. The next wrap then
113
+ // overshot by that much and the line being played ended up above the top of
114
+ // the viewport, with the music audible but nowhere on screen. Worse, each
115
+ // overshoot parked the reader in another gap, so the error repeated on every
116
+ // wrap and the page raced a line ahead of the music.
117
+ //
118
+ // Bounding the offset to what the target system can actually give up keeps
119
+ // the intent — a reader watching track 3 goes on watching track 3 — while
120
+ // guaranteeing the system stays in view. A system shorter than the viewport
121
+ // has nothing to give up, so it simply aligns to the top.
122
+ const targetHeight = (target.yBottom - target.yTop) * zoom;
123
+ const maxOffset = Math.max(0, targetHeight - viewportHeight);
124
+ const rawOffset = scrollTop - current.yTop * zoom;
125
+ const offsetIntoSystem = Math.min(Math.max(rawOffset, 0), maxOffset);
126
+ return { left, top: Math.max(0, target.yTop * zoom + offsetIntoSystem) };
127
+ }
128
+ //# sourceMappingURL=playback-scroll.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"playback-scroll.js","sourceRoot":"","sources":["../src/playback-scroll.ts"],"names":[],"mappings":"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"}
@@ -6,14 +6,48 @@
6
6
  * re-based on the binary-search lookups so every call is O(log n) — part of
7
7
  * the canvas renderer's "no O(score) interaction work" rule.
8
8
  *
9
- * The tick <-> x mapping interpolates linearly across each measure's stave
10
- * box. VexFlow doesn't space notes perfectly linearly (and first-in-system
11
- * measures spend their left edge on clef/key/time), so the caret is a close
12
- * approximation rather than glyph-exact the same trade every DAW-style
13
- * ruler makes.
9
+ * **The caret reads the positions the notes were drawn at.** The renderer
10
+ * records, per measure, the x VexFlow placed each note at the value from
11
+ * `Note.getAbsoluteX()`, which is the same number the notehead is painted from
12
+ * and both directions here interpolate *between those notes*. At a note the
13
+ * answer is exactly where the notehead is; between two notes there is nothing
14
+ * left to disagree about.
15
+ *
16
+ * That is a different thing from what this used to do, and the difference is
17
+ * the whole accuracy of the file. It used to spread a measure's duration
18
+ * evenly across the measure's **stave box**, which runs barline to barline. A
19
+ * stave spends its left edge on whatever modifiers it carries — the clef and
20
+ * key signature every system restates, the time signature at the start of the
21
+ * piece — so the box is systematically wider than the span the notes occupy,
22
+ * and by more on the left than the right. The caret therefore sat *behind* the
23
+ * note it pointed at by a margin that grew with the furniture: measured on a
24
+ * simple score, 76px at the downbeat of bar 1, easing to 17px by bar 2 and
25
+ * 11px by bar 3, and sliding within every bar as it played. Which is exactly
26
+ * how it looked.
27
+ *
28
+ * Two consequences worth stating, because both look like bugs and are not:
29
+ *
30
+ * - **Before a measure's first note the caret is flat, sitting on that note.**
31
+ * Everything left of it is engraving furniture, and a caret drifting through
32
+ * the clef would be pointing at nothing. `tickForX` reads that region the
33
+ * same way, so clicking the clef seeks to the start of the bar.
34
+ * - **The positions cover the drawn window only.** That is the right coverage
35
+ * rather than a limitation — the caret is only visible where the music is
36
+ * drawn, and a click can only land there. Off it, and for the single frame
37
+ * before the first paint, both fall back to the stave box, which is what
38
+ * this always did.
14
39
  */
15
40
  import type { Score } from '@sudobility/music_types';
41
+ import type { MeasureNotePositions } from './canvas-renderer.js';
16
42
  import type { LayoutPlan } from './layout.js';
43
+ /**
44
+ * Measure id -> where its notes were drawn, from the last frame.
45
+ *
46
+ * Optional at every call site: without it these fall back to the stave box,
47
+ * so a caller that has not wired the renderer up gets the old approximation
48
+ * rather than nothing.
49
+ */
50
+ export type NotePositions = ReadonlyMap<string, MeasureNotePositions>;
17
51
  export type CaretPosition = {
18
52
  /** Logical x of the caret line. */
19
53
  x: number;
@@ -26,12 +60,12 @@ export type CaretPosition = {
26
60
  * nothing to draw against (empty score / no layout). Ticks past the end of
27
61
  * the score clamp to the final measure's right edge.
28
62
  */
29
- export declare function caretPositionForTick(plan: LayoutPlan, score: Score, tick: number): CaretPosition | null;
63
+ export declare function caretPositionForTick(plan: LayoutPlan, score: Score, tick: number, notePositions?: NotePositions): CaretPosition | null;
30
64
  /**
31
65
  * The tick a canvas click at logical `(x, y)` should seek to, or `null`
32
66
  * when the point is in dead space between/outside systems. Horizontal
33
67
  * positions left/right of a system's measures clamp to that system's
34
68
  * first/last measure, so clicking the clef area seeks to the system start.
35
69
  */
36
- export declare function tickForPoint(plan: LayoutPlan, score: Score, x: number, y: number): number | null;
70
+ export declare function tickForPoint(plan: LayoutPlan, score: Score, x: number, y: number, notePositions?: NotePositions): number | null;
37
71
  //# sourceMappingURL=playhead.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"playhead.d.ts","sourceRoot":"","sources":["../src/playhead.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAC;AAErD,OAAO,KAAK,EAAE,UAAU,EAAgB,MAAM,aAAa,CAAC;AAE5D,MAAM,MAAM,aAAa,GAAG;IAC1B,mCAAmC;IACnC,CAAC,EAAE,MAAM,CAAC;IACV,yGAAyG;IACzG,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAwDF;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,UAAU,EAChB,KAAK,EAAE,KAAK,EACZ,IAAI,EAAE,MAAM,GACX,aAAa,GAAG,IAAI,CAuBtB;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,UAAU,EAChB,KAAK,EAAE,KAAK,EACZ,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,GACR,MAAM,GAAG,IAAI,CAkBf"}
1
+ {"version":3,"file":"playhead.d.ts","sourceRoot":"","sources":["../src/playhead.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,KAAK,EACV,oBAAoB,EAGrB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,KAAK,EAAE,UAAU,EAAgB,MAAM,aAAa,CAAC;AAE5D;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GAAG,WAAW,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;AAEtE,MAAM,MAAM,aAAa,GAAG;IAC1B,mCAAmC;IACnC,CAAC,EAAE,MAAM,CAAC;IACV,yGAAyG;IACzG,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAqMF;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,UAAU,EAChB,KAAK,EAAE,KAAK,EACZ,IAAI,EAAE,MAAM,EACZ,aAAa,CAAC,EAAE,aAAa,GAC5B,aAAa,GAAG,IAAI,CAiDtB;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,UAAU,EAChB,KAAK,EAAE,KAAK,EACZ,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,aAAa,CAAC,EAAE,aAAa,GAC5B,MAAM,GAAG,IAAI,CA2Cf"}
package/dist/playhead.js CHANGED
@@ -49,12 +49,131 @@ function systemForMeasureIndex(plan, measureIndex) {
49
49
  }
50
50
  return null;
51
51
  }
52
+ /** Straight-line interpolation between two known points. */
53
+ function lerp(tick, a, b) {
54
+ if (b.tick === a.tick)
55
+ return a.x;
56
+ return a.x + ((tick - a.tick) / (b.tick - a.tick)) * (b.x - a.x);
57
+ }
58
+ /**
59
+ * The x for `tick` inside a measure whose drawn notes are known.
60
+ *
61
+ * Interpolates **between the notes themselves**, which is what makes this
62
+ * agree with the page rather than approximate it: VexFlow does not space a bar
63
+ * proportionally to ticks — a bar of `q q h` is engraved, not measured — so any
64
+ * mapping that spread a bar's duration evenly across some span disagreed with
65
+ * the page between the notes by however much engraving differs from
66
+ * arithmetic. Between two adjacent notes there is nothing to disagree about,
67
+ * and at a note the answer is exactly where the notehead is.
68
+ *
69
+ * The two ends are where the notes stop being able to say anything, and each
70
+ * is deliberate:
71
+ *
72
+ * - **Before the first note the caret sits at it**, flat, rather than sliding
73
+ * in from the barline. Everything to the left of the first note is clef, key
74
+ * and time signature, and a caret drifting through those is pointing at
75
+ * engraving furniture rather than at music. This is also what makes a click
76
+ * to the left of the clef land on the first note, which is the only thing
77
+ * such a click can sensibly mean.
78
+ * - **After the last note it runs on to `endX`**, so the tail of a bar whose
79
+ * last note is short still carries the caret to the barline rather than
80
+ * parking it on that note.
81
+ */
82
+ function xForTick(tick, positions, area, measureStartTick, endTick, endX) {
83
+ const first = positions[0];
84
+ /*
85
+ Nothing drawn: a measure standing in for a run of silent ones, or one whose
86
+ events were all dropped. There is no note to point at, so the caret crosses
87
+ the note area evenly — which for a multi-measure rest is exactly right, and
88
+ is what `tickForX` reads back.
89
+ */
90
+ if (!first) {
91
+ const span = endTick - measureStartTick;
92
+ if (span <= 0)
93
+ return area.startX;
94
+ const fraction = Math.min(1, Math.max(0, (tick - measureStartTick) / span));
95
+ return area.startX + fraction * (area.endX - area.startX);
96
+ }
97
+ if (tick <= first.tick)
98
+ return first.x;
99
+ for (let index = 1; index < positions.length; index += 1) {
100
+ const next = positions[index];
101
+ if (tick <= next.tick)
102
+ return lerp(tick, positions[index - 1], next);
103
+ }
104
+ const last = positions[positions.length - 1];
105
+ if (endTick <= last.tick)
106
+ return last.x;
107
+ return lerp(tick, last, { tick: endTick, x: endX });
108
+ }
109
+ /**
110
+ * The tick at `x`, inside a measure whose drawn notes are known.
111
+ *
112
+ * The exact inverse of `xForTick`, and written as one rather than derived from
113
+ * it, because the two are used in opposite directions and a mismatch between
114
+ * them is invisible until somebody clicks: the caret would land somewhere
115
+ * other than the pointer. The tests assert the round trip rather than either
116
+ * one alone.
117
+ *
118
+ * Left of the first note answers that note's tick — the flat region
119
+ * `xForTick` draws there, read backwards. That is what makes a click on the
120
+ * clef seek to the start of the bar rather than to nothing.
121
+ */
122
+ function tickForX(x, positions, area, measureStartTick, endTick, endX) {
123
+ const first = positions[0];
124
+ if (!first) {
125
+ const width = area.endX - area.startX;
126
+ if (width <= 0)
127
+ return measureStartTick;
128
+ const fraction = Math.min(1, Math.max(0, (x - area.startX) / width));
129
+ return measureStartTick + fraction * (endTick - measureStartTick);
130
+ }
131
+ if (x <= first.x)
132
+ return first.tick;
133
+ for (let index = 1; index < positions.length; index += 1) {
134
+ const next = positions[index];
135
+ if (x <= next.x) {
136
+ const previous = positions[index - 1];
137
+ if (next.x === previous.x)
138
+ return previous.tick;
139
+ return (previous.tick +
140
+ ((x - previous.x) / (next.x - previous.x)) * (next.tick - previous.tick));
141
+ }
142
+ }
143
+ const last = positions[positions.length - 1];
144
+ if (endX <= last.x || endTick <= last.tick)
145
+ return last.tick;
146
+ const ratio = Math.min(1, (x - last.x) / (endX - last.x));
147
+ return last.tick + ratio * (endTick - last.tick);
148
+ }
149
+ /**
150
+ * Where this measure's music ends on screen.
151
+ *
152
+ * The next measure's first note when there is one on the same system: a bar's
153
+ * ticks run from its own first note to the next bar's, so using the next
154
+ * origin is what keeps the mapping continuous across a barline instead of
155
+ * kinking at every one. Not across a system break, where the next origin is
156
+ * back at the left of the page and the distance is a wrap rather than a bar.
157
+ * Otherwise the end of this measure's own note area.
158
+ */
159
+ function endAnchorFor(plan, system, measureIndex, area, notePositions) {
160
+ const lastInSystem = system.measureIndices[system.measureIndices.length - 1];
161
+ if (notePositions &&
162
+ lastInSystem !== undefined &&
163
+ measureIndex < lastInSystem) {
164
+ const nextId = plan.trackLayouts[0]?.track.measures[measureIndex + 1]?.id;
165
+ const next = nextId ? notePositions.get(nextId) : undefined;
166
+ if (next)
167
+ return next.positions[0]?.x ?? next.area.startX;
168
+ }
169
+ return area.endX;
170
+ }
52
171
  /**
53
172
  * Where the caret for `tick` sits on the canvas, or `null` when there is
54
173
  * nothing to draw against (empty score / no layout). Ticks past the end of
55
174
  * the score clamp to the final measure's right edge.
56
175
  */
57
- export function caretPositionForTick(plan, score, tick) {
176
+ export function caretPositionForTick(plan, score, tick, notePositions) {
58
177
  if (Number.isNaN(tick))
59
178
  return null;
60
179
  const timings = measureTimings(score);
@@ -66,6 +185,23 @@ export function caretPositionForTick(plan, score, tick) {
66
185
  const system = systemForMeasureIndex(plan, measureIndex);
67
186
  if (!layout || !system)
68
187
  return null;
188
+ const measureId = plan.trackLayouts[0]?.track.measures[measureIndex]?.id;
189
+ const drawn = measureId ? notePositions?.get(measureId) : undefined;
190
+ if (drawn) {
191
+ return {
192
+ x: xForTick(tick, drawn.positions, drawn.area, timing.startTick, timing.startTick + timing.durationTicks, endAnchorFor(plan, system, measureIndex, drawn.area, notePositions)),
193
+ yTop: system.yTop,
194
+ yBottom: system.yBottom,
195
+ };
196
+ }
197
+ /*
198
+ Not drawn: the stave box, spread evenly.
199
+
200
+ A coarse answer, and the one this always gave — the box runs barline to
201
+ barline, so it puts the caret left of the note by whatever the clef and key
202
+ signature occupy. It only applies off the drawn window, where there is no
203
+ caret to see, and for the frame before the first paint.
204
+ */
69
205
  const fraction = timing.durationTicks > 0
70
206
  ? Math.min(1, Math.max(0, (tick - timing.startTick) / timing.durationTicks))
71
207
  : 0;
@@ -81,7 +217,7 @@ export function caretPositionForTick(plan, score, tick) {
81
217
  * positions left/right of a system's measures clamp to that system's
82
218
  * first/last measure, so clicking the clef area seeks to the system start.
83
219
  */
84
- export function tickForPoint(plan, score, x, y) {
220
+ export function tickForPoint(plan, score, x, y, notePositions) {
85
221
  if (!Number.isFinite(x) || !Number.isFinite(y))
86
222
  return null;
87
223
  const timings = measureTimings(score);
@@ -96,6 +232,21 @@ export function tickForPoint(plan, score, x, y) {
96
232
  const timing = timings[hit.measureIndex];
97
233
  if (!timing)
98
234
  return null;
235
+ /*
236
+ The same positions the caret uses, so the two are inverses of each other.
237
+
238
+ They have to be: clicking the sheet seeks, and the caret then draws where
239
+ the click landed. Two different mappings would move the caret away from the
240
+ pointer on every click — by the width of the clef, in the first bar of a
241
+ system.
242
+ */
243
+ const measureId = plan.trackLayouts[0]?.track.measures[hit.measureIndex]?.id;
244
+ const drawn = measureId ? notePositions?.get(measureId) : undefined;
245
+ if (drawn) {
246
+ const endTick = timing.startTick + timing.durationTicks;
247
+ const endX = endAnchorFor(plan, system, hit.measureIndex, drawn.area, notePositions);
248
+ return Math.round(tickForX(x, drawn.positions, drawn.area, timing.startTick, endTick, endX));
249
+ }
99
250
  const fraction = hit.box.width > 0
100
251
  ? Math.min(1, Math.max(0, (x - hit.box.x) / hit.box.width))
101
252
  : 0;
@@ -1 +1 @@
1
- {"version":3,"file":"playhead.js","sourceRoot":"","sources":["../src/playhead.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"playhead.js","sourceRoot":"","sources":["../src/playhead.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * VexFlow render colors, one set per resolved light/dark color scheme.
3
+ *
4
+ * Literal color strings, not CSS custom properties: VexFlow draws straight to
5
+ * the canvas context, which never resolves `var(--...)` (and jsdom doesn't
6
+ * process CSS for tests anyway).
7
+ *
8
+ * Note *state* is carried by these colors — the highlight overlay and its
9
+ * second canvas are gone, so `noteNormal`/`noteSelected`/`noteRegenerated`/
10
+ * `notePlaying` are what a reader actually reads state from. Each value
11
+ * clears 4.5:1 against its mode's stave background. (Color is not the *only*
12
+ * cue: `noteEmphasisFor` thickens and haloes non-normal notes, and the piano
13
+ * roll thickens their borders — spec §27.)
14
+ *
15
+ * `noteInactive` is the one value that does NOT clear 4.5:1 — it sits at ~3:1,
16
+ * deliberately. It marks the tracks you are *not* editing, and its whole job is
17
+ * to recede; at 4.5:1 against the same background it was indistinguishable from
18
+ * `noteNormal` and the active track stopped standing out at all. The music it
19
+ * draws stays legible (large glyphs, not body text), and no state is carried by
20
+ * it alone: a note you need to act on is selected, regenerated or playing, and
21
+ * those keep their full-contrast colours on every track.
22
+ *
23
+ * Its own module rather than a `ScoreEditorView` export: both editors draw
24
+ * from this one palette, so a note means the same thing in either, and a
25
+ * component file exporting shared constants breaks Fast Refresh.
26
+ */
27
+ import type { RenderTheme } from './types.js';
28
+ export declare const LIGHT_RENDER_THEME: RenderTheme;
29
+ export declare const DARK_RENDER_THEME: RenderTheme;
30
+ //# sourceMappingURL=render-theme.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-theme.d.ts","sourceRoot":"","sources":["../src/render-theme.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,eAAO,MAAM,kBAAkB,EAAE,WAUhC,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,WAU/B,CAAC"}
@@ -0,0 +1,23 @@
1
+ export const LIGHT_RENDER_THEME = {
2
+ foreground: '#3f3f46',
3
+ noteNormal: '#3f3f46',
4
+ noteInactive: '#8e8e97',
5
+ noteSelected: '#6d28d9',
6
+ noteRegenerated: '#8b5a2b',
7
+ notePlaying: '#1565c0',
8
+ staveActive: '#000000',
9
+ staveInactive: '#71717a',
10
+ caret: '#d32f2f',
11
+ };
12
+ export const DARK_RENDER_THEME = {
13
+ foreground: '#d4d4d8',
14
+ noteNormal: '#d4d4d8',
15
+ noteInactive: '#82828c',
16
+ noteSelected: '#a78bfa',
17
+ noteRegenerated: '#d9a066',
18
+ notePlaying: '#64b5f6',
19
+ staveActive: '#ffffff',
20
+ staveInactive: '#8a8a93',
21
+ caret: '#ef5350',
22
+ };
23
+ //# sourceMappingURL=render-theme.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-theme.js","sourceRoot":"","sources":["../src/render-theme.ts"],"names":[],"mappings":"AA4BA,MAAM,CAAC,MAAM,kBAAkB,GAAgB;IAC7C,UAAU,EAAE,SAAS;IACrB,UAAU,EAAE,SAAS;IACrB,YAAY,EAAE,SAAS;IACvB,YAAY,EAAE,SAAS;IACvB,eAAe,EAAE,SAAS;IAC1B,WAAW,EAAE,SAAS;IACtB,WAAW,EAAE,SAAS;IACtB,aAAa,EAAE,SAAS;IACxB,KAAK,EAAE,SAAS;CACjB,CAAC;AAEF,MAAM,CAAC,MAAM,iBAAiB,GAAgB;IAC5C,UAAU,EAAE,SAAS;IACrB,UAAU,EAAE,SAAS;IACrB,YAAY,EAAE,SAAS;IACvB,YAAY,EAAE,SAAS;IACvB,eAAe,EAAE,SAAS;IAC1B,WAAW,EAAE,SAAS;IACtB,WAAW,EAAE,SAAS;IACtB,aAAa,EAAE,SAAS;IACxB,KAAK,EAAE,SAAS;CACjB,CAAC"}