@sudobility/music_drawing 0.0.1
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.
- package/dist/canvas-renderer.d.ts +180 -0
- package/dist/canvas-renderer.d.ts.map +1 -0
- package/dist/canvas-renderer.js +831 -0
- package/dist/canvas-renderer.js.map +1 -0
- package/dist/convert.d.ts +122 -0
- package/dist/convert.d.ts.map +1 -0
- package/dist/convert.js +426 -0
- package/dist/convert.js.map +1 -0
- package/dist/display-timing.d.ts +68 -0
- package/dist/display-timing.d.ts.map +1 -0
- package/dist/display-timing.js +192 -0
- package/dist/display-timing.js.map +1 -0
- package/dist/icon-canvas.d.ts +12 -0
- package/dist/icon-canvas.d.ts.map +1 -0
- package/dist/icon-canvas.js +62 -0
- package/dist/icon-canvas.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -0
- package/dist/layout.d.ts +159 -0
- package/dist/layout.d.ts.map +1 -0
- package/dist/layout.js +362 -0
- package/dist/layout.js.map +1 -0
- package/dist/measure-content.d.ts +114 -0
- package/dist/measure-content.d.ts.map +1 -0
- package/dist/measure-content.js +553 -0
- package/dist/measure-content.js.map +1 -0
- package/dist/note-color.d.ts +48 -0
- package/dist/note-color.d.ts.map +1 -0
- package/dist/note-color.js +81 -0
- package/dist/note-color.js.map +1 -0
- package/dist/pagination.d.ts +57 -0
- package/dist/pagination.d.ts.map +1 -0
- package/dist/pagination.js +152 -0
- package/dist/pagination.js.map +1 -0
- package/dist/percussion.d.ts +47 -0
- package/dist/percussion.d.ts.map +1 -0
- package/dist/percussion.js +172 -0
- package/dist/percussion.js.map +1 -0
- package/dist/playhead.d.ts +37 -0
- package/dist/playhead.d.ts.map +1 -0
- package/dist/playhead.js +89 -0
- package/dist/playhead.js.map +1 -0
- package/dist/test/canvas-stub.d.ts +21 -0
- package/dist/test/canvas-stub.d.ts.map +1 -0
- package/dist/test/canvas-stub.js +51 -0
- package/dist/test/canvas-stub.js.map +1 -0
- package/dist/test/fixtures.d.ts +47 -0
- package/dist/test/fixtures.d.ts.map +1 -0
- package/dist/test/fixtures.js +433 -0
- package/dist/test/fixtures.js.map +1 -0
- package/dist/types.d.ts +74 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/package.json +62 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import type { Score } from '@sudobility/music_types';
|
|
2
|
+
import type { LayoutPlan } from './layout.js';
|
|
3
|
+
import type { BBox, RenderOptions, RenderTheme } from './types.js';
|
|
4
|
+
/**
|
|
5
|
+
* Logical (unscaled) content-coordinate window to draw: typically
|
|
6
|
+
* `scrollTop/zoom` .. `(scrollTop+clientHeight)/zoom`. `left`/`right`
|
|
7
|
+
* (defaults: 0/∞ — full width, page-mode behavior) window the draw
|
|
8
|
+
* horizontally too: continuous mode lays every measure in ONE system, so
|
|
9
|
+
* without them each frame would draw the entire score.
|
|
10
|
+
*/
|
|
11
|
+
export type CanvasViewport = {
|
|
12
|
+
top: number;
|
|
13
|
+
bottom: number;
|
|
14
|
+
left?: number;
|
|
15
|
+
right?: number;
|
|
16
|
+
};
|
|
17
|
+
export type CanvasRenderOptions = RenderOptions & {
|
|
18
|
+
viewport: CanvasViewport;
|
|
19
|
+
/** Backing-store scale (window.devicePixelRatio); default 1. The canvas element's width/height must already be sized to cssSize × this. */
|
|
20
|
+
devicePixelRatio?: number;
|
|
21
|
+
};
|
|
22
|
+
export type CanvasRenderResult = {
|
|
23
|
+
/** Note/rest event id -> bbox, zoom-scaled CSS px, content coordinates (scroll NOT subtracted). Drawn window only. */
|
|
24
|
+
idToBBox: Map<string, BBox>;
|
|
25
|
+
/** Measure id -> its stave box (from the layout plan), same units. Drawn window only; one entry per (track, measure). */
|
|
26
|
+
measureIdToBBox: Map<string, BBox>;
|
|
27
|
+
drawnMeasureIndices: Set<number>;
|
|
28
|
+
/** The (cached) full-score layout this frame was drawn against. */
|
|
29
|
+
plan: LayoutPlan;
|
|
30
|
+
theme: RenderTheme;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Where the instrument row's text and icon go, relative to the stave's top line.
|
|
34
|
+
*
|
|
35
|
+
* Two rules, and they fix everything else:
|
|
36
|
+
*
|
|
37
|
+
* - the **top of the instrument name** sits on the stave's top line, and
|
|
38
|
+
* - the **icon's centre** sits on the name's centre.
|
|
39
|
+
*
|
|
40
|
+
* "Top of the name" means the top of its capitals, not the top of the font's
|
|
41
|
+
* em box — the em box carries room for accents that nothing here draws, so
|
|
42
|
+
* aligning to it leaves a visible gap that reads as misalignment. Hence
|
|
43
|
+
* `capHeight`, measured from the canvas rather than assumed: a baseline placed
|
|
44
|
+
* by arithmetic put the icon on the line and the text five pixels under it.
|
|
45
|
+
*
|
|
46
|
+
* Pure, and exported, because this is the part worth pinning exactly — the
|
|
47
|
+
* drawing around it is not.
|
|
48
|
+
*/
|
|
49
|
+
export declare function trackInfoRowLayout(staveTopLine: number, capHeight: number, iconSize: number): {
|
|
50
|
+
textBaseline: number;
|
|
51
|
+
iconTop: number;
|
|
52
|
+
rowCenter: number;
|
|
53
|
+
};
|
|
54
|
+
export declare class CanvasScoreRenderer {
|
|
55
|
+
private cache;
|
|
56
|
+
/**
|
|
57
|
+
* The last frame's built objects, reused when only colour changed.
|
|
58
|
+
*
|
|
59
|
+
* The single most expensive thing this renderer does is construct and format
|
|
60
|
+
* VexFlow objects, and playback asks it to redraw on every note boundary to
|
|
61
|
+
* move one notehead's colour. Keyed on everything geometry depends on —
|
|
62
|
+
* score identity, zoom, layout mode, width, track set, viewport — and
|
|
63
|
+
* deliberately *not* on the theme, the note colours, the active track or the
|
|
64
|
+
* selection, which are exactly what a repaint changes.
|
|
65
|
+
*
|
|
66
|
+
* One frame, not an LRU: during playback the viewport is still, so
|
|
67
|
+
* consecutive repaints hit it; scrolling misses it and rebuilds, which is the
|
|
68
|
+
* same work it did before. Bounded by construction, since it only ever holds
|
|
69
|
+
* what was last drawn.
|
|
70
|
+
*/
|
|
71
|
+
private frame;
|
|
72
|
+
private planFor;
|
|
73
|
+
render(score: Score, ctx: CanvasRenderingContext2D, options: CanvasRenderOptions): CanvasRenderResult;
|
|
74
|
+
/**
|
|
75
|
+
* The track-info gutter: name, instrument and mute/solo state beside every
|
|
76
|
+
* stave, for every visible system.
|
|
77
|
+
*
|
|
78
|
+
* Pinned to the viewport's left edge rather than drawn at content x=0. In
|
|
79
|
+
* continuous mode the score is one very wide system scrolled horizontally, so
|
|
80
|
+
* a gutter in content space would slide out of view — the one thing a
|
|
81
|
+
* permanent label column cannot do. The vertical scroll is kept, so the
|
|
82
|
+
* labels still track their staves.
|
|
83
|
+
*
|
|
84
|
+
* Reads `name`/`instrumentName`/`muted`/`solo` straight off the live `Track`,
|
|
85
|
+
* so there is no render option to keep in sync. Alignment is structural: the
|
|
86
|
+
* gutter is the space `layout.ts` reserved via `TRACK_INFO_WIDTH`, in the
|
|
87
|
+
* same coordinate space as the staves.
|
|
88
|
+
*/
|
|
89
|
+
private drawTrackInfoGutter;
|
|
90
|
+
/**
|
|
91
|
+
* Styles one VexFlow note by the highest-precedence role among the domain
|
|
92
|
+
* events it represents (>1 for a chord, or for one segment of a
|
|
93
|
+
* duration-decomposed long note).
|
|
94
|
+
*
|
|
95
|
+
* Both `fillStyle` and `strokeStyle` are set: noteheads fill, stems and
|
|
96
|
+
* flags stroke, and a note whose stem stayed the default color would read
|
|
97
|
+
* as half-highlighted.
|
|
98
|
+
*
|
|
99
|
+
* `lineWidth` carries the same state redundantly *without* color (spec §27)
|
|
100
|
+
* — see `noteEmphasisFor`, including why this is not a shadow.
|
|
101
|
+
*/
|
|
102
|
+
private styleNote;
|
|
103
|
+
/**
|
|
104
|
+
* Whether `trackId` is *the* active track. With no active track set, nothing
|
|
105
|
+
* is active, so every stave draws in `staveInactive` — the neutral state.
|
|
106
|
+
*/
|
|
107
|
+
private isActiveTrack;
|
|
108
|
+
/**
|
|
109
|
+
* Whether a track's notes should dim.
|
|
110
|
+
*
|
|
111
|
+
* Deliberately not `!isActiveTrack`. Dimming is *relative* — it says "this
|
|
112
|
+
* track is not the one you are working on" — so with no active track set
|
|
113
|
+
* there is nothing to be relative to and no note dims. Stave lines can sit in
|
|
114
|
+
* their neutral colour without looking wrong; every note in the score going
|
|
115
|
+
* grey would just look washed out.
|
|
116
|
+
*/
|
|
117
|
+
private notesDimmed;
|
|
118
|
+
/** The unstyled-note color for a track, which its beams and ties follow. */
|
|
119
|
+
private trackColor;
|
|
120
|
+
private recordEventBBox;
|
|
121
|
+
/**
|
|
122
|
+
* The contiguous slice of `system.measureIndices` whose stave boxes
|
|
123
|
+
* intersect `[left, right]`, found by binary search over the x-sorted
|
|
124
|
+
* measure layouts — O(log n), so a continuous-mode frame never scans the
|
|
125
|
+
* whole single-system score (the horizontal analogue of the y-based
|
|
126
|
+
* system filter).
|
|
127
|
+
*/
|
|
128
|
+
private visibleMeasureIndices;
|
|
129
|
+
/**
|
|
130
|
+
* Builds one system's VexFlow objects, without deciding what colour anything
|
|
131
|
+
* is.
|
|
132
|
+
*
|
|
133
|
+
* Split from painting so the result can be cached: constructing and
|
|
134
|
+
* formatting notes is what costs milliseconds (measured at 119ms for a
|
|
135
|
+
* twelve-track sixteenth-note score at zoom 0.5), and a colour change — a
|
|
136
|
+
* note starting, a selection moving — changes none of it. See `render`.
|
|
137
|
+
*/
|
|
138
|
+
private buildSystem;
|
|
139
|
+
/**
|
|
140
|
+
* Paints an already-built system.
|
|
141
|
+
*
|
|
142
|
+
* Everything here is a colour decision or a context write, so it is what runs
|
|
143
|
+
* again when a note starts sounding or the selection moves — cheap beside the
|
|
144
|
+
* building it no longer has to repeat.
|
|
145
|
+
*/
|
|
146
|
+
private paintSystem;
|
|
147
|
+
/**
|
|
148
|
+
* Tints every selected measure across the full height of the system.
|
|
149
|
+
*
|
|
150
|
+
* The whole bar, not a mark near it: a selection's *extent* is the thing that
|
|
151
|
+
* has to be legible — "these four bars, on every staff" — and the 2px rule
|
|
152
|
+
* this replaces stated that in the one strip of the sheet nobody is looking
|
|
153
|
+
* at. Spanning the system rather than one track's stave is deliberate, and
|
|
154
|
+
* matches what the selection means: the measure gutter is one band per
|
|
155
|
+
* system, and replacing a measure region acts on every track in it.
|
|
156
|
+
*
|
|
157
|
+
* Drawn **before** the staves and notes so it reads as paper the music sits
|
|
158
|
+
* on. Over the top it would tint the noteheads themselves, and a selected
|
|
159
|
+
* bar's notes would not match the same notes anywhere else.
|
|
160
|
+
*/
|
|
161
|
+
private paintSelectedMeasures;
|
|
162
|
+
/**
|
|
163
|
+
* Measure numbers in the band above the system's top stave, with any
|
|
164
|
+
* measure in `selectedMeasureIds` picked out in the selection colour.
|
|
165
|
+
*
|
|
166
|
+
* Extent is carried by `paintSelectedMeasures`, which tints the bar itself;
|
|
167
|
+
* this only has to say which number belongs to it. It used to draw a 2px rule
|
|
168
|
+
* under the number instead, which was the only thing showing how far a
|
|
169
|
+
* selection reached and was easy to miss entirely.
|
|
170
|
+
*
|
|
171
|
+
* Drawn straight to the 2D context: there is no VexFlow object for "the
|
|
172
|
+
* space above a stave", and a number plus a rect needs none. Measure
|
|
173
|
+
* geometry comes off track 0 because every track shares one measure grid
|
|
174
|
+
* (see `rebuildMeasureTicks`), which is what lets one gutter serve the
|
|
175
|
+
* whole system.
|
|
176
|
+
*/
|
|
177
|
+
private drawMeasureGutter;
|
|
178
|
+
dispose(): void;
|
|
179
|
+
}
|
|
180
|
+
//# sourceMappingURL=canvas-renderer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canvas-renderer.d.ts","sourceRoot":"","sources":["../src/canvas-renderer.ts"],"names":[],"mappings":"AA+BA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAC;AAkBrD,OAAO,KAAK,EAAE,UAAU,EAAgB,MAAM,aAAa,CAAC;AAE5D,OAAO,KAAK,EAAE,IAAI,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAGnE;;;;;;GAMG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,aAAa,GAAG;IAChD,QAAQ,EAAE,cAAc,CAAC;IACzB,2IAA2I;IAC3I,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,sHAAsH;IACtH,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC5B,yHAAyH;IACzH,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACnC,mBAAmB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACjC,mEAAmE;IACnE,IAAI,EAAE,UAAU,CAAC;IACjB,KAAK,EAAE,WAAW,CAAC;CACpB,CAAC;AAwEF;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,kBAAkB,CAChC,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,GACf;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAM9D;AAeD,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,KAAK,CAAgE;IAC7E;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,KAAK,CAA2B;IAExC,OAAO,CAAC,OAAO;IAcf,MAAM,CACJ,KAAK,EAAE,KAAK,EACZ,GAAG,EAAE,wBAAwB,EAC7B,OAAO,EAAE,mBAAmB,GAC3B,kBAAkB;IA8PrB;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,mBAAmB;IAgH3B;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,SAAS;IAejB;;;OAGG;IACH,OAAO,CAAC,aAAa;IAOrB;;;;;;;;OAQG;IACH,OAAO,CAAC,WAAW;IAInB,4EAA4E;IAC5E,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,eAAe;IAyBvB;;;;;;OAMG;IACH,OAAO,CAAC,qBAAqB;IAuC7B;;;;;;;;OAQG;IACH,OAAO,CAAC,WAAW;IAiInB;;;;;;OAMG;IACH,OAAO,CAAC,WAAW;IAwJnB;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,qBAAqB;IAuC7B;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,iBAAiB;IA4CzB,OAAO,IAAI,IAAI;CAIhB"}
|