vector-score 1.0.3 → 1.2.0
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/README.md +150 -30
- package/dist/classes/GuitarChord.d.ts +106 -0
- package/dist/classes/MusicStaff.d.ts +2 -0
- package/dist/classes/SVGRenderer.d.ts +15 -0
- package/dist/constants.d.ts +19 -0
- package/dist/helpers/guitarHelpers.d.ts +5 -0
- package/dist/helpers/keySignatures.d.ts +9 -0
- package/dist/helpers/notehelpers.d.ts +1 -2
- package/dist/index.d.ts +3 -0
- package/dist/strategies/GrandStaffStrategy.d.ts +3 -1
- package/dist/strategies/SingleStaffStrategy.d.ts +2 -1
- package/dist/strategies/StrategyInterface.d.ts +2 -1
- package/dist/types.d.ts +20 -0
- package/dist/vector-score.js +522 -159
- package/dist/vector-score.umd.cjs +1 -1
- package/package.json +2 -1
package/dist/vector-score.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const
|
|
1
|
+
const S = {
|
|
2
2
|
treble: {
|
|
3
3
|
staffType: "treble",
|
|
4
4
|
paddingTop: 13,
|
|
@@ -31,13 +31,66 @@ const B = {
|
|
|
31
31
|
topLineYPos: 0,
|
|
32
32
|
bottomLineYPos: 110
|
|
33
33
|
}
|
|
34
|
-
},
|
|
34
|
+
}, p = {
|
|
35
35
|
w: 4,
|
|
36
36
|
h: 2,
|
|
37
37
|
q: 1,
|
|
38
38
|
e: 0.5,
|
|
39
39
|
s: 0.25
|
|
40
|
-
}, X = {
|
|
40
|
+
}, st = 6, rt = 5, I = 22, T = 30, E = 10, f = 6, nt = 4, ot = E * 2, it = 40, at = 16, X = 1, F = 4, U = 1, Y = f * 2 + F + 4, D = 16, C = 14, ct = 14, M = D, ht = E + 2, $ = 8, lt = 40, J = {
|
|
41
|
+
sharp: ["F", "C", "G", "D", "A", "E", "B"],
|
|
42
|
+
flat: ["B", "E", "A", "D", "G", "C", "F"]
|
|
43
|
+
}, k = {
|
|
44
|
+
C: { type: "sharp", count: 0 },
|
|
45
|
+
G: { type: "sharp", count: 1 },
|
|
46
|
+
D: { type: "sharp", count: 2 },
|
|
47
|
+
A: { type: "sharp", count: 3 },
|
|
48
|
+
E: { type: "sharp", count: 4 },
|
|
49
|
+
B: { type: "sharp", count: 5 },
|
|
50
|
+
"F#": { type: "sharp", count: 6 },
|
|
51
|
+
F: { type: "flat", count: 1 },
|
|
52
|
+
Bb: { type: "flat", count: 2 },
|
|
53
|
+
Eb: { type: "flat", count: 3 },
|
|
54
|
+
Ab: { type: "flat", count: 4 },
|
|
55
|
+
Db: { type: "flat", count: 5 },
|
|
56
|
+
Gb: { type: "flat", count: 6 }
|
|
57
|
+
}, B = {
|
|
58
|
+
treble: {
|
|
59
|
+
sharp: [5, 5, 5, 5, 4, 5, 4],
|
|
60
|
+
// F5 C5 G5 D5 A4 E5 B4
|
|
61
|
+
flat: [4, 5, 4, 5, 4, 5, 4]
|
|
62
|
+
// B4 E5 A4 D5 G4 C5 F4
|
|
63
|
+
},
|
|
64
|
+
bass: {
|
|
65
|
+
sharp: [3, 3, 3, 3, 2, 3, 2],
|
|
66
|
+
// F3 C3 G3 D3 A2 E3 B2
|
|
67
|
+
flat: [2, 3, 2, 3, 2, 3, 3]
|
|
68
|
+
// B2 E3 A2 D3 G2 C3 F3
|
|
69
|
+
},
|
|
70
|
+
alto: {
|
|
71
|
+
sharp: [4, 4, 4, 4, 3, 4, 4],
|
|
72
|
+
// F5 C5 G5 D5 A4 E5 B4
|
|
73
|
+
flat: [4, 5, 4, 5, 4, 5, 4]
|
|
74
|
+
// B4 E5 A4 D5 G4 C5 F4
|
|
75
|
+
},
|
|
76
|
+
grand: { sharp: [], flat: [] }
|
|
77
|
+
};
|
|
78
|
+
function dt(h) {
|
|
79
|
+
const e = k[h];
|
|
80
|
+
if (!e) throw new Error(`Unknown key signature "${h}". Valid keys: ${Object.keys(k).join(", ")}`);
|
|
81
|
+
return e;
|
|
82
|
+
}
|
|
83
|
+
function ft(h, e, t, s) {
|
|
84
|
+
const r = dt(t);
|
|
85
|
+
if (r.count === 0) return 0;
|
|
86
|
+
const o = r.type === "sharp" ? "ACCIDENTAL_SHARP" : "ACCIDENTAL_FLAT", n = h.getLayerByName("staff");
|
|
87
|
+
return e.getKeySignatureYPositions(r.type, r.count).forEach((a) => {
|
|
88
|
+
a.forEach((c, l) => {
|
|
89
|
+
h.drawGlyph(o, n, { xOffset: s + l * $, yOffset: c });
|
|
90
|
+
});
|
|
91
|
+
}), r.count * $;
|
|
92
|
+
}
|
|
93
|
+
const Et = {
|
|
41
94
|
CLEF_TREBLE: {
|
|
42
95
|
path: "m150 273 10 58c2 7 2 7 12 7 59 0 96 46 96 97 0 45-26 79-67 95-5 2-6 2-5 7 4 25 12 63 12 85 0 68-52 80-79 80-61 0-76-39-76-65 0-25 16-46 43-46 24 0 38 19 38 41 0 23-14 34-27 38-9 2-13 4-13 6 0 6 11 12 32 12 24 0 64-7 64-66 0-19-6-54-11-81-1-5-1-4-6-3l-27 2C48 540 0 474 0 404c0-80 61-138 119-185 5-4 4-5 3-10-2-16-5-42-5-65 0-42 9-92 39-125 8-9 20-19 26-19 4 0 15 11 21 20 16 24 26 58 26 93 0 61-33 112-76 153-3 2-3 2-3 7Zm38-211c-24 0-53 38-53 101l2 37c1 5 3 5 5 3 32-28 70-64 70-108 0-22-11-33-24-33Zm-44 272-8-51c-1-4-2-5-6-1-18 15-37 30-61 56-33 38-37 70-37 93 0 56 45 95 115 95l23-2c6-2 6-2 5-6l-20-119c-1-5-1-5-8-3-24 6-40 24-40 46 0 19 12 36 29 43 3 1 6 3 6 5 0 3-2 5-5 5l-11-3c-27-9-46-34-46-70 0-34 23-66 58-78 8-2 8-2 6-10Zm28 64 20 114c0 5 1 5 6 2 22-11 38-31 38-56 0-36-27-63-60-66-4 0-5 1-4 6Z",
|
|
43
96
|
xOffset: 0,
|
|
@@ -133,17 +186,13 @@ const B = {
|
|
|
133
186
|
xOffset: 0,
|
|
134
187
|
yOffset: 0
|
|
135
188
|
}
|
|
136
|
-
},
|
|
137
|
-
function
|
|
138
|
-
const e = h.match(
|
|
189
|
+
}, ut = /^(?<name>[A-Ga-g])(?<accidental>##|bb|[#bn]?)(?<octave>\d)(?<duration>[whqeWHQE]?)$/, gt = /^[whqeWHQE]$/, H = ["C", "D", "E", "F", "G", "A", "B"];
|
|
190
|
+
function P(h) {
|
|
191
|
+
const e = h.match(ut);
|
|
139
192
|
if (!e || !e.groups)
|
|
140
193
|
throw new Error(`Invalid note string format: ${h}. Expected format: [A-Ga-g][#|b]?[0-9][w|h|q|e].`);
|
|
141
194
|
let { name: t, accidental: s, octave: r, duration: o } = e.groups;
|
|
142
|
-
|
|
143
|
-
throw new Error(`Invalid note name: ${t}. Valid note names are: C, D, E, F, G, A, B.`);
|
|
144
|
-
if (!r)
|
|
145
|
-
throw new Error(`Invalid octave: ${r}. Octave must be a number between 0 and 9.`);
|
|
146
|
-
o || (o = "w");
|
|
195
|
+
t = t.toUpperCase(), o = o.toLowerCase(), o || (o = "w");
|
|
147
196
|
const n = {
|
|
148
197
|
name: t,
|
|
149
198
|
octave: parseInt(r),
|
|
@@ -151,12 +200,12 @@ function g(h) {
|
|
|
151
200
|
};
|
|
152
201
|
return s && (n.accidental = s), n;
|
|
153
202
|
}
|
|
154
|
-
function
|
|
155
|
-
const e = h.match(
|
|
203
|
+
function R(h) {
|
|
204
|
+
const e = h.match(gt);
|
|
156
205
|
if (!e) throw new Error(`Invalid note duration '${h}'. Use w | h | q | e.`);
|
|
157
|
-
return e.toString().toLowerCase();
|
|
206
|
+
return e[0].toString().toLowerCase();
|
|
158
207
|
}
|
|
159
|
-
function
|
|
208
|
+
function x(h) {
|
|
160
209
|
let e;
|
|
161
210
|
switch (h) {
|
|
162
211
|
case "treble":
|
|
@@ -172,26 +221,22 @@ function S(h) {
|
|
|
172
221
|
if (!e) throw new Error(`Invalid clef type: ${h}. Valid clef types are: treble, bass, alto.`);
|
|
173
222
|
return e;
|
|
174
223
|
}
|
|
175
|
-
function
|
|
176
|
-
|
|
177
|
-
let s = h.octave - e.octave;
|
|
178
|
-
return s *= 7, t + s;
|
|
179
|
-
}
|
|
180
|
-
function R(h) {
|
|
181
|
-
let e = I.indexOf(h.name);
|
|
224
|
+
function W(h) {
|
|
225
|
+
let e = H.indexOf(h.name);
|
|
182
226
|
return e += h.octave * 12, e;
|
|
183
227
|
}
|
|
184
|
-
function
|
|
185
|
-
return
|
|
228
|
+
function m(h, e) {
|
|
229
|
+
return H.indexOf(h.name) - H.indexOf(e.name) + (h.octave - e.octave) * 7;
|
|
186
230
|
}
|
|
187
|
-
const
|
|
188
|
-
class
|
|
231
|
+
const y = 48, Z = 40 + 30 / 2, V = 20, q = 90;
|
|
232
|
+
class tt {
|
|
189
233
|
params;
|
|
190
234
|
rendererRef;
|
|
191
235
|
width = 0;
|
|
236
|
+
trebleStaffHeight = 0;
|
|
192
237
|
constructor(e, t) {
|
|
193
238
|
this.rendererRef = e;
|
|
194
|
-
const s =
|
|
239
|
+
const s = S[t];
|
|
195
240
|
if (!s) throw new Error(`Staff type ${t} is not supported`);
|
|
196
241
|
this.params = s;
|
|
197
242
|
}
|
|
@@ -205,19 +250,19 @@ class H {
|
|
|
205
250
|
this.width = e;
|
|
206
251
|
const t = this.rendererRef.getLayerByName("staff");
|
|
207
252
|
let s = this.drawStaffLines(0, t), r = this.drawStaffLines(s + 30, t);
|
|
208
|
-
this.rendererRef.drawLine(0, 0, 0, r, t), this.rendererRef.drawLine(this.width, 0, this.width, r, t);
|
|
253
|
+
this.rendererRef.drawLine(0, 0, 0, r, t), this.rendererRef.drawLine(this.width, 0, this.width, r, t), this.trebleStaffHeight = s;
|
|
209
254
|
let o = r, n = 1;
|
|
210
|
-
const
|
|
211
|
-
this.rendererRef.drawGlyph(
|
|
255
|
+
const i = x("treble"), a = x("bass");
|
|
256
|
+
this.rendererRef.drawGlyph(i, t), this.rendererRef.drawGlyph(a, t, { yOffset: s + 30 }), o += this.params.paddingBottom + 1, n += this.params.paddingTop, this.rendererRef.addTotalRootSvgHeight(o), this.rendererRef.addTotalRootSvgYOffset(n);
|
|
212
257
|
};
|
|
213
258
|
shouldNoteFlip(e) {
|
|
214
|
-
const t = Math.abs(e -
|
|
215
|
-
return e ===
|
|
259
|
+
const t = Math.abs(e - q), s = Math.abs(e - V);
|
|
260
|
+
return e === Z ? !1 : t <= s ? !(e >= q) : e <= V;
|
|
216
261
|
}
|
|
217
262
|
getLedgerLinesX(e, t) {
|
|
218
|
-
const s = [], r =
|
|
263
|
+
const s = [], r = W(e);
|
|
219
264
|
let o = e.duration === "w" ? 17 : 12.5;
|
|
220
|
-
if (r ===
|
|
265
|
+
if (r === y)
|
|
221
266
|
return s.push({ x1: -2, x2: o, yPos: 0 }), s;
|
|
222
267
|
if (t < this.params.topLineYPos) {
|
|
223
268
|
let n = this.params.topLineYPos - 10;
|
|
@@ -239,33 +284,41 @@ class H {
|
|
|
239
284
|
return s;
|
|
240
285
|
}
|
|
241
286
|
calculateNoteYPos = (e) => {
|
|
242
|
-
let s =
|
|
243
|
-
const r =
|
|
244
|
-
return r <
|
|
287
|
+
let s = m(this.params.topLineNote, e) * (10 / 2);
|
|
288
|
+
const r = W(e);
|
|
289
|
+
return r < y ? s += 10 : r === y && (s = Z), s;
|
|
245
290
|
};
|
|
291
|
+
getKeySignatureYPositions(e, t) {
|
|
292
|
+
const s = J[e].slice(0, t), r = B.treble[e], o = B.bass[e], n = s.map(
|
|
293
|
+
(a, c) => m(S.treble.topLineNote, { name: a, octave: r[c] }) * (10 / 2)
|
|
294
|
+
), i = s.map(
|
|
295
|
+
(a, c) => m(S.bass.topLineNote, { name: a, octave: o[c] }) * (10 / 2) + this.trebleStaffHeight + 30
|
|
296
|
+
);
|
|
297
|
+
return [n, i];
|
|
298
|
+
}
|
|
246
299
|
}
|
|
247
|
-
const
|
|
248
|
-
class
|
|
300
|
+
const pt = 20;
|
|
301
|
+
class _ {
|
|
249
302
|
params;
|
|
250
303
|
rendererRef;
|
|
251
304
|
constructor(e, t) {
|
|
252
305
|
this.rendererRef = e;
|
|
253
|
-
const s =
|
|
306
|
+
const s = S[t];
|
|
254
307
|
if (!s) throw new Error(`Staff type ${t} is not supported`);
|
|
255
308
|
this.params = s;
|
|
256
309
|
}
|
|
257
310
|
drawStaff = (e) => {
|
|
258
311
|
const t = this.rendererRef.getLayerByName("staff");
|
|
259
312
|
let s = 0;
|
|
260
|
-
for (let
|
|
313
|
+
for (let i = 0; i < 5; i++)
|
|
261
314
|
this.rendererRef.drawLine(0, s, e, s, t), s += 10;
|
|
262
315
|
this.rendererRef.drawLine(0, 0, 0, s - 10, t), this.rendererRef.drawLine(e, 0, e, s - 10, t);
|
|
263
316
|
let r = s - 10 + 1, o = 1;
|
|
264
|
-
const n =
|
|
317
|
+
const n = x(this.params.staffType);
|
|
265
318
|
this.rendererRef.drawGlyph(n, t), r += this.params.paddingTop + this.params.paddingBottom, o += this.params.paddingTop, this.rendererRef.addTotalRootSvgHeight(r), this.rendererRef.addTotalRootSvgYOffset(o);
|
|
266
319
|
};
|
|
267
320
|
shouldNoteFlip(e) {
|
|
268
|
-
return e <=
|
|
321
|
+
return e <= pt;
|
|
269
322
|
}
|
|
270
323
|
getLedgerLinesX(e, t) {
|
|
271
324
|
const s = [];
|
|
@@ -289,9 +342,15 @@ class A {
|
|
|
289
342
|
}
|
|
290
343
|
return s;
|
|
291
344
|
}
|
|
292
|
-
calculateNoteYPos = (e) =>
|
|
345
|
+
calculateNoteYPos = (e) => m(this.params.topLineNote, e) * (10 / 2);
|
|
346
|
+
getKeySignatureYPositions(e, t) {
|
|
347
|
+
const s = J[e].slice(0, t), r = B[this.params.staffType][e];
|
|
348
|
+
return [s.map(
|
|
349
|
+
(n, i) => this.calculateNoteYPos({ name: n, octave: r[i] })
|
|
350
|
+
)];
|
|
351
|
+
}
|
|
293
352
|
}
|
|
294
|
-
class
|
|
353
|
+
class et {
|
|
295
354
|
svgRendererInstance;
|
|
296
355
|
strategyInstance;
|
|
297
356
|
constructor(e, t) {
|
|
@@ -305,9 +364,9 @@ class P {
|
|
|
305
364
|
for (let o = 0; o < e.length; o++) {
|
|
306
365
|
const n = e[o];
|
|
307
366
|
if (n.noteObj.accidental && r < 3 ? (t += -8, s = Math.min(s, t), r++) : n.noteObj.accidental && r <= 3 ? (t = -8, r = 1) : (t = 0, r = 0), t !== 0) {
|
|
308
|
-
const
|
|
309
|
-
if (!
|
|
310
|
-
|
|
367
|
+
const a = Array.from(n.noteGroup.getElementsByTagName("use")).find((c) => c.getAttribute("href")?.includes("ACCIDENTAL"));
|
|
368
|
+
if (!a) continue;
|
|
369
|
+
a.setAttribute("transform", `translate(${t + 8}, 0)`);
|
|
311
370
|
}
|
|
312
371
|
}
|
|
313
372
|
return -s;
|
|
@@ -316,13 +375,13 @@ class P {
|
|
|
316
375
|
let t = e[0], s = 0;
|
|
317
376
|
for (let r = 1; r < e.length; r++) {
|
|
318
377
|
const o = e[r];
|
|
319
|
-
if (
|
|
378
|
+
if (-m(t.noteObj, o.noteObj) === 1) {
|
|
320
379
|
s = 28 / 2, o.noteGroup.setAttribute("transform", `translate(${s}, ${o.noteYPos})`);
|
|
321
|
-
const
|
|
322
|
-
if (
|
|
323
|
-
const c =
|
|
324
|
-
let
|
|
325
|
-
l && (
|
|
380
|
+
const a = Array.from(o.noteGroup.getElementsByTagName("use")).find((c) => c.getAttribute("href")?.includes("ACCIDENTAL"));
|
|
381
|
+
if (a) {
|
|
382
|
+
const c = a.getAttribute("transform")?.match(/([-]?\d+)/), l = c && c[0];
|
|
383
|
+
let d = -s;
|
|
384
|
+
l && (d += Number(l)), a.setAttribute("transform", `translate(${d}, 0)`);
|
|
326
385
|
}
|
|
327
386
|
r++, t = e[r];
|
|
328
387
|
continue;
|
|
@@ -333,7 +392,7 @@ class P {
|
|
|
333
392
|
}
|
|
334
393
|
// Handles drawing the glyphs to internal group, applies the xPositioning to note Cursor X
|
|
335
394
|
renderNote(e) {
|
|
336
|
-
const t = this.svgRendererInstance.createGroup("note"), s =
|
|
395
|
+
const t = this.svgRendererInstance.createGroup("note"), s = P(e), r = this.strategyInstance.calculateNoteYPos({
|
|
337
396
|
name: s.name,
|
|
338
397
|
octave: s.octave
|
|
339
398
|
});
|
|
@@ -369,8 +428,8 @@ class P {
|
|
|
369
428
|
this.svgRendererInstance.drawGlyph("ACCIDENTAL_DOUBLE_FLAT", t), n -= -14;
|
|
370
429
|
break;
|
|
371
430
|
}
|
|
372
|
-
return this.strategyInstance.getLedgerLinesX(s, r).forEach((
|
|
373
|
-
this.svgRendererInstance.drawLine(
|
|
431
|
+
return this.strategyInstance.getLedgerLinesX(s, r).forEach((a) => {
|
|
432
|
+
this.svgRendererInstance.drawLine(a.x1, a.yPos, a.x2, a.yPos, t);
|
|
374
433
|
}), {
|
|
375
434
|
noteGroup: t,
|
|
376
435
|
noteObj: s,
|
|
@@ -382,11 +441,11 @@ class P {
|
|
|
382
441
|
renderChord(e) {
|
|
383
442
|
const t = this.svgRendererInstance.createGroup("chord"), s = [];
|
|
384
443
|
for (const n of e) {
|
|
385
|
-
const
|
|
386
|
-
|
|
387
|
-
noteGroup:
|
|
388
|
-
noteObj:
|
|
389
|
-
noteYPos:
|
|
444
|
+
const i = this.renderNote(n);
|
|
445
|
+
i.noteGroup.setAttribute("transform", `translate(0, ${i.noteYPos})`), t.appendChild(i.noteGroup), s.push({
|
|
446
|
+
noteGroup: i.noteGroup,
|
|
447
|
+
noteObj: i.noteObj,
|
|
448
|
+
noteYPos: i.noteYPos,
|
|
390
449
|
cursorOffset: 0,
|
|
391
450
|
accidentalOffset: 0
|
|
392
451
|
});
|
|
@@ -401,8 +460,8 @@ class P {
|
|
|
401
460
|
};
|
|
402
461
|
}
|
|
403
462
|
}
|
|
404
|
-
const
|
|
405
|
-
class
|
|
463
|
+
const g = "http://www.w3.org/2000/svg", At = 0.1, w = 1200;
|
|
464
|
+
class L {
|
|
406
465
|
rootElementRef;
|
|
407
466
|
svgElementRef;
|
|
408
467
|
// Layers
|
|
@@ -420,18 +479,18 @@ class O {
|
|
|
420
479
|
// from get rootSvgElement().
|
|
421
480
|
// HEIGHT and YOfsset should be set externally, values are calculated internally.
|
|
422
481
|
constructor(e, t) {
|
|
423
|
-
this.rootElementRef = e, this.width = t.width, this.scale = t.scale, this.width >
|
|
482
|
+
this.rootElementRef = e, this.width = t.width, this.scale = t.scale, this.width > w && (this.width = w, console.warn(`Width provided for the staff ${this.width} exceeds the limit ${w}. Please use this value to fix positioning issues.`)), this.svgElementRef = document.createElementNS(g, "svg"), this.svgElementRef.classList.add("vs-svg-renderer-root"), this.svgElementRef.style.maxWidth = "100%", this.svgElementRef.style.height = "auto", this.svgElementRef.style.display = "block", this.svgElementRef.setAttribute("color", t.staffColor), this.svgElementRef.style.backgroundColor = t.staffBackgroundColor, this.makeGlyphDefs(t.useGlyphs), this.parentGroupContainer = this.createGroup("svg-renderer-parent"), this.svgElementRef.appendChild(this.parentGroupContainer), this.musicStaffLayer = this.createGroup("music-staff-layer"), this.musicNotesLayer = this.createGroup("music-notes-layer"), this.musicUILayer = this.createGroup("music-ui-layer"), this.parentGroupContainer.appendChild(this.musicStaffLayer), this.parentGroupContainer.appendChild(this.musicNotesLayer), this.parentGroupContainer.appendChild(this.musicUILayer);
|
|
424
483
|
}
|
|
425
484
|
// Creates SVG defs for all glyphs in GLYPH_ENTRIES, applies global scale and offsets, appends to root SVG
|
|
426
485
|
makeGlyphDefs(e) {
|
|
427
|
-
const t = document.createElementNS(
|
|
428
|
-
Object.entries(
|
|
429
|
-
const n = document.createElementNS(
|
|
430
|
-
n.setAttribute("id", `glyph-${r}`), n.setAttribute("d", o.path), n.setAttribute("fill", "currentColor"), n.setAttribute("transform", `translate(${o.xOffset}, ${o.yOffset}) scale(${
|
|
486
|
+
const t = document.createElementNS(g, "defs");
|
|
487
|
+
Object.entries(Et).filter(([r]) => e.includes(r)).forEach(([r, o]) => {
|
|
488
|
+
const n = document.createElementNS(g, "path");
|
|
489
|
+
n.setAttribute("id", `glyph-${r}`), n.setAttribute("d", o.path), n.setAttribute("fill", "currentColor"), n.setAttribute("transform", `translate(${o.xOffset}, ${o.yOffset}) scale(${At})`), t.appendChild(n);
|
|
431
490
|
}), this.rootSvgElement.appendChild(t);
|
|
432
491
|
}
|
|
433
492
|
createGroup(e) {
|
|
434
|
-
const t = document.createElementNS(
|
|
493
|
+
const t = document.createElementNS(g, "g");
|
|
435
494
|
return e && t.classList.add(`vs-${e}`), t;
|
|
436
495
|
}
|
|
437
496
|
commitElementsToDOM(e, t = this.rootElementRef) {
|
|
@@ -455,6 +514,9 @@ class O {
|
|
|
455
514
|
addTotalRootSvgHeight(e) {
|
|
456
515
|
this.totalHeight += e;
|
|
457
516
|
}
|
|
517
|
+
setTotalRootSvgHeight(e) {
|
|
518
|
+
this.totalHeight = e;
|
|
519
|
+
}
|
|
458
520
|
addTotalRootSvgYOffset(e) {
|
|
459
521
|
this.totalYOffset += e;
|
|
460
522
|
}
|
|
@@ -472,12 +534,12 @@ class O {
|
|
|
472
534
|
}
|
|
473
535
|
// Drawing Methods
|
|
474
536
|
drawLine(e, t, s, r, o) {
|
|
475
|
-
const n = document.createElementNS(
|
|
537
|
+
const n = document.createElementNS(g, "line");
|
|
476
538
|
n.setAttribute("x1", e.toString()), n.setAttribute("y1", t.toString()), n.setAttribute("x2", s.toString()), n.setAttribute("y2", r.toString()), n.setAttribute("stroke", "currentColor"), n.setAttribute("stroke-width", "1"), o.appendChild(n);
|
|
477
539
|
}
|
|
478
540
|
drawRect(e, t, s, r) {
|
|
479
|
-
const o = document.createElementNS(
|
|
480
|
-
return o.setAttribute("width", e.toString()), o.setAttribute("height", t.toString()), o.setAttribute("x", (r?.x ?? 0).toString()), o.setAttribute("y", (r?.y ?? 0).toString()), r?.fill ? o.setAttribute("fill", r.fill) : o.setAttribute("fill", "currentColor"), s.appendChild(o), o;
|
|
541
|
+
const o = document.createElementNS(g, "rect");
|
|
542
|
+
return o.setAttribute("width", e.toString()), o.setAttribute("height", t.toString()), o.setAttribute("x", (r?.x ?? 0).toString()), o.setAttribute("y", (r?.y ?? 0).toString()), r?.fill ? o.setAttribute("fill", r.fill) : o.setAttribute("fill", "currentColor"), r?.rx && o.setAttribute("rx", r.rx.toString()), s.appendChild(o), o;
|
|
481
543
|
}
|
|
482
544
|
drawGlyph(e, t, s) {
|
|
483
545
|
s = {
|
|
@@ -485,14 +547,22 @@ class O {
|
|
|
485
547
|
yOffset: 0,
|
|
486
548
|
...s
|
|
487
549
|
};
|
|
488
|
-
const r = document.createElementNS(
|
|
550
|
+
const r = document.createElementNS(g, "use");
|
|
489
551
|
r.setAttribute("href", `#glyph-${e}`), (s.xOffset || s.yOffset) && r.setAttribute("transform", `translate(${s.xOffset}, ${s.yOffset})`), t.appendChild(r);
|
|
490
552
|
}
|
|
553
|
+
drawCircle(e, t, s, r, o) {
|
|
554
|
+
const n = document.createElementNS(g, "circle");
|
|
555
|
+
return n.setAttribute("cx", e.toString()), n.setAttribute("cy", t.toString()), n.setAttribute("r", s.toString()), o?.filled === !1 ? (n.setAttribute("fill", "none"), n.setAttribute("stroke", "currentColor"), n.setAttribute("stroke-width", (o?.strokeWidth ?? 1.5).toString())) : n.setAttribute("fill", o?.fill ?? "currentColor"), r.appendChild(n), n;
|
|
556
|
+
}
|
|
557
|
+
drawText(e, t, s, r, o) {
|
|
558
|
+
const n = document.createElementNS(g, "text");
|
|
559
|
+
return n.setAttribute("x", t.toString()), n.setAttribute("y", s.toString()), n.setAttribute("text-anchor", o?.anchor ?? "middle"), n.setAttribute("font-size", (o?.fontSize ?? 10).toString()), n.setAttribute("fill", o?.fill ?? "currentColor"), o?.fontWeight && n.setAttribute("font-weight", o.fontWeight), n.textContent = e, r.appendChild(n), n;
|
|
560
|
+
}
|
|
491
561
|
destroy() {
|
|
492
562
|
this.rootElementRef && this.svgElementRef && this.rootElementRef.contains(this.svgElementRef) && this.rootElementRef.removeChild(this.svgElementRef);
|
|
493
563
|
}
|
|
494
564
|
}
|
|
495
|
-
const
|
|
565
|
+
const It = [
|
|
496
566
|
"CLEF_TREBLE",
|
|
497
567
|
"CLEF_BASS",
|
|
498
568
|
"CLEF_ALTO",
|
|
@@ -507,7 +577,7 @@ const $ = [
|
|
|
507
577
|
"ACCIDENTAL_DOUBLE_SHARP",
|
|
508
578
|
"ACCIDENTAL_DOUBLE_FLAT"
|
|
509
579
|
];
|
|
510
|
-
class
|
|
580
|
+
class Rt {
|
|
511
581
|
svgRendererInstance;
|
|
512
582
|
strategyInstance;
|
|
513
583
|
noteRendererInstance;
|
|
@@ -532,40 +602,44 @@ class j {
|
|
|
532
602
|
staffColor: "black",
|
|
533
603
|
staffBackgroundColor: "transparent",
|
|
534
604
|
...t
|
|
535
|
-
}, this.svgRendererInstance = new
|
|
605
|
+
}, this.svgRendererInstance = new L(e, {
|
|
536
606
|
width: this.options.width,
|
|
537
607
|
height: 100,
|
|
538
608
|
scale: this.options.scale,
|
|
539
609
|
staffColor: this.options.staffColor,
|
|
540
610
|
staffBackgroundColor: this.options.staffBackgroundColor,
|
|
541
|
-
useGlyphs:
|
|
611
|
+
useGlyphs: It
|
|
542
612
|
});
|
|
543
613
|
const s = this.svgRendererInstance.rootSvgElement;
|
|
544
614
|
switch (this.options.staffType) {
|
|
545
615
|
case "grand":
|
|
546
|
-
this.strategyInstance = new
|
|
616
|
+
this.strategyInstance = new tt(this.svgRendererInstance, "grand");
|
|
547
617
|
break;
|
|
548
618
|
case "bass":
|
|
549
|
-
this.strategyInstance = new
|
|
619
|
+
this.strategyInstance = new _(this.svgRendererInstance, "bass");
|
|
550
620
|
break;
|
|
551
621
|
case "treble":
|
|
552
|
-
this.strategyInstance = new
|
|
622
|
+
this.strategyInstance = new _(this.svgRendererInstance, "treble");
|
|
553
623
|
break;
|
|
554
624
|
case "alto":
|
|
555
|
-
this.strategyInstance = new
|
|
625
|
+
this.strategyInstance = new _(this.svgRendererInstance, "alto");
|
|
556
626
|
break;
|
|
557
627
|
default:
|
|
558
628
|
throw new Error(`The staff type ${this.options.staffType} is not supported. Please use "treble", "bass", "alto", or "grand".`);
|
|
559
629
|
}
|
|
560
|
-
if (this.strategyInstance.drawStaff(this.options.width), this.noteRendererInstance = new
|
|
561
|
-
const
|
|
562
|
-
this.svgRendererInstance.addTotalRootSvgYOffset(
|
|
630
|
+
if (this.strategyInstance.drawStaff(this.options.width), this.noteRendererInstance = new et(this.svgRendererInstance, this.strategyInstance), this.options.spaceAbove) {
|
|
631
|
+
const o = this.options.spaceAbove * 10;
|
|
632
|
+
this.svgRendererInstance.addTotalRootSvgYOffset(o);
|
|
563
633
|
}
|
|
564
634
|
if (this.options.spaceBelow) {
|
|
565
|
-
let
|
|
566
|
-
this.options.staffType === "grand" && (
|
|
635
|
+
let o = this.options.spaceBelow * 10;
|
|
636
|
+
this.options.staffType === "grand" && (o -= 10 / 2), this.svgRendererInstance.addTotalRootSvgHeight(o);
|
|
567
637
|
}
|
|
568
|
-
|
|
638
|
+
let r = 0;
|
|
639
|
+
this.options.keySignature && (r = this.createKeySignature(this.options.keySignature)), this.noteStartX = 38 + this.options.noteStartX + r, this.svgRendererInstance.getLayerByName("notes").setAttribute("transform", `translate(${this.noteStartX}, 0)`), this.svgRendererInstance.applySizingToRootSvg(), this.svgRendererInstance.commitElementsToDOM(s);
|
|
640
|
+
}
|
|
641
|
+
createKeySignature(e) {
|
|
642
|
+
return ft(this.svgRendererInstance, this.strategyInstance, e, lt);
|
|
569
643
|
}
|
|
570
644
|
/**
|
|
571
645
|
* Draws a note on the staff.
|
|
@@ -593,8 +667,8 @@ class j {
|
|
|
593
667
|
let n;
|
|
594
668
|
try {
|
|
595
669
|
n = this.noteRendererInstance.renderNote(o);
|
|
596
|
-
} catch (
|
|
597
|
-
throw r.length > 0 && this.svgRendererInstance.commitElementsToDOM(r, s),
|
|
670
|
+
} catch (i) {
|
|
671
|
+
throw r.length > 0 && this.svgRendererInstance.commitElementsToDOM(r, s), i;
|
|
598
672
|
}
|
|
599
673
|
n.noteGroup.setAttribute("transform", `translate(${this.noteCursorX + n.accidentalOffset}, ${n.noteYPos})`), this.noteEntries.push({
|
|
600
674
|
gElement: n.noteGroup,
|
|
@@ -626,7 +700,7 @@ class j {
|
|
|
626
700
|
const t = this.svgRendererInstance.getLayerByName("notes"), s = this.noteRendererInstance.renderChord(e);
|
|
627
701
|
s.noteGroup.setAttribute("transform", `translate(${this.noteCursorX + s.accidentalOffset}, 0)`), this.noteEntries.push({
|
|
628
702
|
gElement: s.noteGroup,
|
|
629
|
-
note:
|
|
703
|
+
note: P(e[0]),
|
|
630
704
|
xPos: this.noteCursorX + s.accidentalOffset,
|
|
631
705
|
yPos: 0,
|
|
632
706
|
accidentalXOffset: s.accidentalOffset
|
|
@@ -637,19 +711,19 @@ class j {
|
|
|
637
711
|
* @returns Returns early if no notes are on the staff
|
|
638
712
|
*/
|
|
639
713
|
justifyNotes() {
|
|
640
|
-
const e = this.options.width -
|
|
714
|
+
const e = this.options.width - this.noteStartX, t = this.noteEntries.length;
|
|
641
715
|
if (t <= 0 || e <= 0) return;
|
|
642
716
|
const s = Math.round(e / t);
|
|
643
717
|
this.noteEntries.map((o, n) => {
|
|
644
|
-
const
|
|
718
|
+
const i = (n + 0.5) * s, a = o.gElement.getBBox(), c = i - a.width / 2 - a.x, l = Math.round(c * 10) / 10;
|
|
645
719
|
return {
|
|
646
720
|
entry: o,
|
|
647
721
|
newX: l,
|
|
648
722
|
isChord: o.gElement.classList.contains("chord")
|
|
649
723
|
};
|
|
650
724
|
}).forEach((o) => {
|
|
651
|
-
const { entry: n, newX:
|
|
652
|
-
|
|
725
|
+
const { entry: n, newX: i, isChord: a } = o;
|
|
726
|
+
a ? n.gElement.setAttribute("transform", `translate(${i}, 0)`) : n.gElement.setAttribute("transform", `translate(${i}, ${n.yPos})`), n.xPos = i;
|
|
653
727
|
});
|
|
654
728
|
}
|
|
655
729
|
/**
|
|
@@ -708,7 +782,7 @@ class j {
|
|
|
708
782
|
const s = this.noteEntries[t], r = this.noteRendererInstance.renderChord(e), n = s.xPos - s.accidentalXOffset + r.accidentalOffset;
|
|
709
783
|
r.noteGroup.setAttribute("transform", `translate(${n}, 0)`), this.svgRendererInstance.getLayerByName("notes").replaceChild(r.noteGroup, s.gElement), this.noteEntries[t] = {
|
|
710
784
|
gElement: r.noteGroup,
|
|
711
|
-
note:
|
|
785
|
+
note: P(e[0]),
|
|
712
786
|
xPos: n,
|
|
713
787
|
yPos: 0,
|
|
714
788
|
accidentalXOffset: r.accidentalOffset
|
|
@@ -744,7 +818,7 @@ class j {
|
|
|
744
818
|
this.noteEntries = [], this.svgRendererInstance.destroy();
|
|
745
819
|
}
|
|
746
820
|
}
|
|
747
|
-
const
|
|
821
|
+
const A = 30, z = 19, O = 12, _t = 4, Tt = 6, j = 1, b = 38, mt = [
|
|
748
822
|
"TIME_4",
|
|
749
823
|
"TIME_3",
|
|
750
824
|
"NOTE_HEAD_WHOLE",
|
|
@@ -756,7 +830,7 @@ const E = 30, v = 19, C = 12, k = 4, W = 6, G = 1, L = 38, Z = [
|
|
|
756
830
|
"REST_QUARTER",
|
|
757
831
|
"REST_EIGHTH"
|
|
758
832
|
];
|
|
759
|
-
class
|
|
833
|
+
class yt {
|
|
760
834
|
rendererInstance;
|
|
761
835
|
options;
|
|
762
836
|
barSpacing;
|
|
@@ -767,7 +841,7 @@ class Q {
|
|
|
767
841
|
currentBeatCount = 0;
|
|
768
842
|
currentBeatUICount = 0;
|
|
769
843
|
currentBeatUIElement = null;
|
|
770
|
-
currentBeatUIXPos =
|
|
844
|
+
currentBeatUIXPos = b;
|
|
771
845
|
/**
|
|
772
846
|
* Creates an instance of a RhythmStaff, A single staff that will automatically apply positioning of elements based on the duration of a note.
|
|
773
847
|
*
|
|
@@ -787,13 +861,13 @@ class Q {
|
|
|
787
861
|
staffBackgroundColor: "white",
|
|
788
862
|
currentBeatUIColor: "#24ff7450",
|
|
789
863
|
...t
|
|
790
|
-
}, this.rendererInstance = new
|
|
864
|
+
}, this.rendererInstance = new L(e, {
|
|
791
865
|
width: this.options.width,
|
|
792
866
|
height: 100,
|
|
793
867
|
scale: this.options.scale,
|
|
794
868
|
staffColor: this.options.staffColor,
|
|
795
869
|
staffBackgroundColor: this.options.staffBackgroundColor,
|
|
796
|
-
useGlyphs:
|
|
870
|
+
useGlyphs: mt
|
|
797
871
|
});
|
|
798
872
|
const s = this.rendererInstance.rootSvgElement;
|
|
799
873
|
let r = "TIME_4";
|
|
@@ -809,41 +883,41 @@ class Q {
|
|
|
809
883
|
}
|
|
810
884
|
if (this.options.barsCount < 1 || this.options.barsCount > 3) throw new Error(`Bars count ${this.options.barsCount} not supported. Please use 1 - 3`);
|
|
811
885
|
if (this.options.spaceAbove) {
|
|
812
|
-
const
|
|
813
|
-
this.rendererInstance.addTotalRootSvgYOffset(
|
|
886
|
+
const u = this.options.spaceAbove * 10;
|
|
887
|
+
this.rendererInstance.addTotalRootSvgYOffset(u);
|
|
814
888
|
}
|
|
815
889
|
if (this.options.spaceBelow) {
|
|
816
|
-
let
|
|
817
|
-
this.rendererInstance.addTotalRootSvgHeight(
|
|
890
|
+
let u = this.options.spaceBelow * 10;
|
|
891
|
+
this.rendererInstance.addTotalRootSvgHeight(u);
|
|
818
892
|
}
|
|
819
893
|
const o = this.rendererInstance.getLayerByName("staff");
|
|
820
|
-
this.rendererInstance.addTotalRootSvgHeight(
|
|
894
|
+
this.rendererInstance.addTotalRootSvgHeight(A * 2);
|
|
821
895
|
const n = this.rendererInstance.createGroup("time-signature");
|
|
822
896
|
o.appendChild(n);
|
|
823
|
-
const
|
|
824
|
-
this.rendererInstance.drawGlyph(r, n), this.rendererInstance.drawGlyph("TIME_4", n, { yOffset:
|
|
825
|
-
let
|
|
826
|
-
this.options.barsCount > 1 && (
|
|
897
|
+
const i = A - z;
|
|
898
|
+
this.rendererInstance.drawGlyph(r, n), this.rendererInstance.drawGlyph("TIME_4", n, { yOffset: z }), n.setAttribute("transform", `translate(0, ${i})`);
|
|
899
|
+
let a = this.options.width - 38;
|
|
900
|
+
this.options.barsCount > 1 && (a -= (this.options.barsCount - 1) * O), a -= j, this.rendererInstance.drawLine(0, A, this.options.width - j, A, o), this.barSpacing = a / this.options.barsCount, this.quarterNoteSpacing = Math.round(this.barSpacing / this.options.topNumber), this.maxBeatCount = this.options.barsCount * this.options.topNumber;
|
|
827
901
|
let c = this.barSpacing + 38;
|
|
828
|
-
const l =
|
|
829
|
-
for (let
|
|
830
|
-
this.rendererInstance.drawLine(c, l, c,
|
|
831
|
-
this.rendererInstance.getLayerByName("notes").setAttribute("transform", `translate(38, ${
|
|
902
|
+
const l = A / 2, d = A + l;
|
|
903
|
+
for (let u = 0; u < this.options.barsCount - 1; u++)
|
|
904
|
+
this.rendererInstance.drawLine(c, l, c, d, o), c += this.barSpacing;
|
|
905
|
+
this.rendererInstance.getLayerByName("notes").setAttribute("transform", `translate(38, ${A})`), this.rendererInstance.applySizingToRootSvg(), this.rendererInstance.commitElementsToDOM(s);
|
|
832
906
|
}
|
|
833
907
|
createBeatUIElement() {
|
|
834
908
|
const e = this.rendererInstance.getLayerByName("ui");
|
|
835
909
|
this.currentBeatUIElement = this.rendererInstance.drawRect(
|
|
836
910
|
this.quarterNoteSpacing / 2,
|
|
837
|
-
|
|
911
|
+
A * 2,
|
|
838
912
|
e,
|
|
839
913
|
{
|
|
840
|
-
x:
|
|
914
|
+
x: b,
|
|
841
915
|
fill: this.options.currentBeatUIColor
|
|
842
916
|
}
|
|
843
917
|
);
|
|
844
918
|
}
|
|
845
919
|
handleNewBar() {
|
|
846
|
-
this.noteCursorX +=
|
|
920
|
+
this.noteCursorX += O;
|
|
847
921
|
}
|
|
848
922
|
// Translates group, returns cursor increment amount
|
|
849
923
|
translateGroupByDuration(e, t) {
|
|
@@ -903,14 +977,14 @@ class Q {
|
|
|
903
977
|
for (; s > 0; ) {
|
|
904
978
|
const r = this.rendererInstance.createGroup("rest");
|
|
905
979
|
let o = 0;
|
|
906
|
-
s -
|
|
980
|
+
s - p.h >= 0 ? (this.rendererInstance.drawGlyph("REST_HALF", r), o = p.h) : s - p.q >= 0 ? (this.rendererInstance.drawGlyph("REST_QUARTER", r), o = p.q) : (this.rendererInstance.drawGlyph("REST_EIGHTH", r), o = p.e), s -= o, this.currentBeatCount += o, r.setAttribute("transform", `translate(${this.noteCursorX}, 0)`), this.noteCursorX += o * this.quarterNoteSpacing, t.push(r);
|
|
907
981
|
}
|
|
908
982
|
return t;
|
|
909
983
|
}
|
|
910
984
|
renderBeamRect(e, t, s, r) {
|
|
911
985
|
this.rendererInstance.drawRect(
|
|
912
986
|
e - t,
|
|
913
|
-
|
|
987
|
+
_t,
|
|
914
988
|
s,
|
|
915
989
|
{
|
|
916
990
|
x: 10,
|
|
@@ -942,20 +1016,20 @@ class Q {
|
|
|
942
1016
|
for (const o of t) {
|
|
943
1017
|
let n = "w";
|
|
944
1018
|
try {
|
|
945
|
-
n =
|
|
946
|
-
} catch (
|
|
947
|
-
throw r.length > 0 && this.rendererInstance.commitElementsToDOM(r, s),
|
|
1019
|
+
n = R(o);
|
|
1020
|
+
} catch (d) {
|
|
1021
|
+
throw r.length > 0 && this.rendererInstance.commitElementsToDOM(r, s), d;
|
|
948
1022
|
}
|
|
949
|
-
const
|
|
1023
|
+
const i = p[n];
|
|
950
1024
|
if (this.currentBeatCount >= this.maxBeatCount)
|
|
951
1025
|
throw r.length > 0 && this.rendererInstance.commitElementsToDOM(r, s), new Error("Max beat count reached. Can't add additional notes.");
|
|
952
1026
|
this.checkAndCreateNewBar();
|
|
953
|
-
const
|
|
954
|
-
|
|
955
|
-
r.push(
|
|
1027
|
+
const a = this.checkAndFillBarWithRests(i);
|
|
1028
|
+
a && a.forEach((d) => {
|
|
1029
|
+
r.push(d), this.noteEntries.push(d);
|
|
956
1030
|
});
|
|
957
|
-
const c = this.rendererInstance.createGroup("note"), l = this.translateGroupByDuration(
|
|
958
|
-
this.noteCursorX += l, this.currentBeatCount +=
|
|
1031
|
+
const c = this.rendererInstance.createGroup("note"), l = this.translateGroupByDuration(i, c);
|
|
1032
|
+
this.noteCursorX += l, this.currentBeatCount += i, this.renderNote(n, c), r.push(c), this.noteEntries.push(c);
|
|
959
1033
|
}
|
|
960
1034
|
this.rendererInstance.commitElementsToDOM(r, s);
|
|
961
1035
|
}
|
|
@@ -982,18 +1056,18 @@ class Q {
|
|
|
982
1056
|
for (const o of t) {
|
|
983
1057
|
let n = "w";
|
|
984
1058
|
try {
|
|
985
|
-
n =
|
|
986
|
-
} catch (
|
|
987
|
-
throw r.length > 0 && this.rendererInstance.commitElementsToDOM(r, s),
|
|
1059
|
+
n = R(o);
|
|
1060
|
+
} catch (d) {
|
|
1061
|
+
throw r.length > 0 && this.rendererInstance.commitElementsToDOM(r, s), d;
|
|
988
1062
|
}
|
|
989
|
-
const
|
|
1063
|
+
const i = this.rendererInstance.createGroup("rest"), a = p[n], c = a * this.quarterNoteSpacing;
|
|
990
1064
|
if (this.currentBeatCount >= this.maxBeatCount)
|
|
991
1065
|
throw r.length > 0 && this.rendererInstance.commitElementsToDOM(r, s), new Error("Max beat count reached. Can't add additional notes.");
|
|
992
1066
|
this.checkAndCreateNewBar();
|
|
993
|
-
const l = this.checkAndFillBarWithRests(
|
|
994
|
-
l && l.forEach((
|
|
995
|
-
r.push(
|
|
996
|
-
}), this.renderRest(n,
|
|
1067
|
+
const l = this.checkAndFillBarWithRests(a);
|
|
1068
|
+
l && l.forEach((d) => {
|
|
1069
|
+
r.push(d), this.noteEntries.push(d);
|
|
1070
|
+
}), this.renderRest(n, i), i.setAttribute("transform", `translate(${this.noteCursorX}, 0)`), this.noteCursorX += c, this.currentBeatCount += a, r.push(i), this.noteEntries.push(i);
|
|
997
1071
|
}
|
|
998
1072
|
this.rendererInstance.commitElementsToDOM(r, s);
|
|
999
1073
|
}
|
|
@@ -1020,13 +1094,13 @@ class Q {
|
|
|
1020
1094
|
if (this.currentBeatCount >= this.maxBeatCount)
|
|
1021
1095
|
throw new Error("Max beat count reached. Can't add additional beamed note.");
|
|
1022
1096
|
let s = "s";
|
|
1023
|
-
e === "s" ? s = "s" : s =
|
|
1024
|
-
const r = this.rendererInstance.getLayerByName("notes"), o =
|
|
1097
|
+
e === "s" ? s = "s" : s = R(e), this.checkAndCreateNewBar();
|
|
1098
|
+
const r = this.rendererInstance.getLayerByName("notes"), o = p[s], n = o * this.quarterNoteSpacing, i = this.options.topNumber - this.currentBeatCount % this.options.topNumber, a = Math.min(t, i / o), c = this.rendererInstance.createGroup("beamed-note");
|
|
1025
1099
|
c.setAttribute("transform", `translate(${this.noteCursorX}, 0)`);
|
|
1026
1100
|
let l = 0;
|
|
1027
|
-
for (let
|
|
1101
|
+
for (let d = 0; d < a; d++)
|
|
1028
1102
|
this.rendererInstance.drawGlyph("NOTE_HEAD_QUARTER", c, { xOffset: l }), this.drawStem(c, l), l += n, this.currentBeatCount += o;
|
|
1029
|
-
this.renderBeamRect(l, n, c), e === "s" && this.renderBeamRect(l, n, c,
|
|
1103
|
+
this.renderBeamRect(l, n, c), e === "s" && this.renderBeamRect(l, n, c, Tt), this.noteCursorX += l, this.noteEntries.push(c), this.rendererInstance.commitElementsToDOM(c, r);
|
|
1030
1104
|
}
|
|
1031
1105
|
/**
|
|
1032
1106
|
* Will increment the UI showing the current beat in quarters. Once exceeded, must be reset with `resetCurrentBeatUI()`
|
|
@@ -1037,14 +1111,14 @@ class Q {
|
|
|
1037
1111
|
this.currentBeatUIElement.setAttribute("display", "none");
|
|
1038
1112
|
return;
|
|
1039
1113
|
}
|
|
1040
|
-
this.currentBeatUIElement?.getAttribute("display") === "none" && this.currentBeatUIElement.removeAttribute("display"), this.currentBeatUICount++, this.currentBeatUICount > this.options.topNumber && this.currentBeatUICount % this.options.topNumber === 1 && (this.currentBeatUIXPos +=
|
|
1114
|
+
this.currentBeatUIElement?.getAttribute("display") === "none" && this.currentBeatUIElement.removeAttribute("display"), this.currentBeatUICount++, this.currentBeatUICount > this.options.topNumber && this.currentBeatUICount % this.options.topNumber === 1 && (this.currentBeatUIXPos += O), this.currentBeatUICount > 1 && (this.currentBeatUIXPos += this.quarterNoteSpacing), this.currentBeatUIElement.setAttribute("x", this.currentBeatUIXPos.toString());
|
|
1041
1115
|
}
|
|
1042
1116
|
/**
|
|
1043
1117
|
* Resets the ui showing the current beat value.
|
|
1044
1118
|
* @returns void
|
|
1045
1119
|
*/
|
|
1046
1120
|
resetCurrentBeatUI() {
|
|
1047
|
-
this.currentBeatUICount = 0, this.currentBeatUIXPos =
|
|
1121
|
+
this.currentBeatUICount = 0, this.currentBeatUIXPos = b, this.currentBeatUIElement && (this.currentBeatUIElement.setAttribute("display", "none"), this.currentBeatUIElement.setAttribute("x", this.currentBeatUIXPos.toString()));
|
|
1048
1122
|
}
|
|
1049
1123
|
/**
|
|
1050
1124
|
* Clears staff of notes and resets internal positioning.
|
|
@@ -1061,7 +1135,7 @@ class Q {
|
|
|
1061
1135
|
this.noteEntries = [], this.rendererInstance.destroy();
|
|
1062
1136
|
}
|
|
1063
1137
|
}
|
|
1064
|
-
const
|
|
1138
|
+
const Ct = [
|
|
1065
1139
|
"CLEF_TREBLE",
|
|
1066
1140
|
"CLEF_BASS",
|
|
1067
1141
|
"CLEF_ALTO",
|
|
@@ -1075,8 +1149,8 @@ const V = [
|
|
|
1075
1149
|
"ACCIDENTAL_NATURAL",
|
|
1076
1150
|
"ACCIDENTAL_DOUBLE_SHARP",
|
|
1077
1151
|
"ACCIDENTAL_DOUBLE_FLAT"
|
|
1078
|
-
],
|
|
1079
|
-
class
|
|
1152
|
+
], N = 60, St = N;
|
|
1153
|
+
class wt {
|
|
1080
1154
|
svgRendererInstance;
|
|
1081
1155
|
strategyInstance;
|
|
1082
1156
|
noteRendererInstance;
|
|
@@ -1103,32 +1177,32 @@ class z {
|
|
|
1103
1177
|
staffColor: "black",
|
|
1104
1178
|
staffBackgroundColor: "transparent",
|
|
1105
1179
|
...t
|
|
1106
|
-
}, this.svgRendererInstance = new
|
|
1180
|
+
}, this.svgRendererInstance = new L(e, {
|
|
1107
1181
|
width: this.options.width,
|
|
1108
1182
|
height: 100,
|
|
1109
1183
|
scale: this.options.scale,
|
|
1110
1184
|
staffColor: this.options.staffColor,
|
|
1111
1185
|
staffBackgroundColor: this.options.staffBackgroundColor,
|
|
1112
|
-
useGlyphs:
|
|
1186
|
+
useGlyphs: Ct
|
|
1113
1187
|
});
|
|
1114
1188
|
const s = this.svgRendererInstance.rootSvgElement;
|
|
1115
1189
|
switch (this.options.staffType) {
|
|
1116
1190
|
case "grand":
|
|
1117
|
-
this.strategyInstance = new
|
|
1191
|
+
this.strategyInstance = new tt(this.svgRendererInstance, "grand");
|
|
1118
1192
|
break;
|
|
1119
1193
|
case "bass":
|
|
1120
|
-
this.strategyInstance = new
|
|
1194
|
+
this.strategyInstance = new _(this.svgRendererInstance, "bass");
|
|
1121
1195
|
break;
|
|
1122
1196
|
case "treble":
|
|
1123
|
-
this.strategyInstance = new
|
|
1197
|
+
this.strategyInstance = new _(this.svgRendererInstance, "treble");
|
|
1124
1198
|
break;
|
|
1125
1199
|
case "alto":
|
|
1126
|
-
this.strategyInstance = new
|
|
1200
|
+
this.strategyInstance = new _(this.svgRendererInstance, "alto");
|
|
1127
1201
|
break;
|
|
1128
1202
|
default:
|
|
1129
1203
|
throw new Error(`The staff type ${this.options.staffType} is not supported. Please use "treble", "bass", "alto", or "grand".`);
|
|
1130
1204
|
}
|
|
1131
|
-
if (this.strategyInstance.drawStaff(this.options.width), this.noteRendererInstance = new
|
|
1205
|
+
if (this.strategyInstance.drawStaff(this.options.width), this.noteRendererInstance = new et(this.svgRendererInstance, this.strategyInstance), this.options.spaceAbove) {
|
|
1132
1206
|
const r = this.options.spaceAbove * 10;
|
|
1133
1207
|
this.svgRendererInstance.addTotalRootSvgYOffset(r);
|
|
1134
1208
|
}
|
|
@@ -1139,10 +1213,10 @@ class z {
|
|
|
1139
1213
|
this.notesLayer = this.svgRendererInstance.getLayerByName("notes"), this.notesLayer.classList.add("vs-scrolling-notes-layer"), this.noteStartX = 38 + this.options.noteStartX, this.svgRendererInstance.getLayerByName("notes").setAttribute("transform", `translate(${this.noteStartX}, 0)`), this.svgRendererInstance.applySizingToRootSvg(), this.svgRendererInstance.commitElementsToDOM(s);
|
|
1140
1214
|
}
|
|
1141
1215
|
renderFirstNoteGroups() {
|
|
1142
|
-
const e = this.options.width - this.options.noteStartX +
|
|
1216
|
+
const e = this.options.width - this.options.noteStartX + St;
|
|
1143
1217
|
for (; this.noteBuffer.length > 0 && this.noteCursorX < e; )
|
|
1144
|
-
this.renderNextNote(), this.noteCursorX +=
|
|
1145
|
-
this.activeEntries.length > 1 && (this.noteCursorX -=
|
|
1218
|
+
this.renderNextNote(), this.noteCursorX += N;
|
|
1219
|
+
this.activeEntries.length > 1 && (this.noteCursorX -= N);
|
|
1146
1220
|
}
|
|
1147
1221
|
renderNextNote() {
|
|
1148
1222
|
if (this.noteBuffer.length < 1) return;
|
|
@@ -1201,7 +1275,7 @@ class z {
|
|
|
1201
1275
|
return;
|
|
1202
1276
|
}
|
|
1203
1277
|
this.activeEntries.forEach((t) => {
|
|
1204
|
-
t.xPos -=
|
|
1278
|
+
t.xPos -= N, t.noteWrapper.style.transform = `translate(${t.xPos}px, 0px)`;
|
|
1205
1279
|
});
|
|
1206
1280
|
const e = this.activeEntries[0];
|
|
1207
1281
|
e.xPos <= 0 && (this.notesLayer.removeChild(e.noteWrapper), this.activeEntries.shift()), this.renderNextNote();
|
|
@@ -1221,8 +1295,297 @@ class z {
|
|
|
1221
1295
|
this.svgRendererInstance.destroy(), this.activeEntries = [], this.noteBuffer = [];
|
|
1222
1296
|
}
|
|
1223
1297
|
}
|
|
1298
|
+
const Nt = /^[xX\da-zA-Z]+$/, Lt = /^[\d]+$/;
|
|
1299
|
+
function K(h, e) {
|
|
1300
|
+
const t = [];
|
|
1301
|
+
for (let s = 0; s < h.length; s++)
|
|
1302
|
+
t.push({
|
|
1303
|
+
fret: h[s],
|
|
1304
|
+
finger: e[s]
|
|
1305
|
+
});
|
|
1306
|
+
return t;
|
|
1307
|
+
}
|
|
1308
|
+
function Q(h, e) {
|
|
1309
|
+
const t = h.filter((o) => o.toLowerCase() !== "x" && o !== "0").map((o) => parseInt(o, 36));
|
|
1310
|
+
if (t.length === 0) return 1;
|
|
1311
|
+
const s = Math.min(...t);
|
|
1312
|
+
return Math.max(...t) <= e ? 1 : s;
|
|
1313
|
+
}
|
|
1314
|
+
function v(h) {
|
|
1315
|
+
if (!Nt.test(h)) throw new Error("Invalid frets string. Only 'x', 'X', and digits are allowed.");
|
|
1316
|
+
return h.split("");
|
|
1317
|
+
}
|
|
1318
|
+
function G(h) {
|
|
1319
|
+
if (!Lt.test(h)) throw new Error("Invalid fingers string. Only digits are allowed.");
|
|
1320
|
+
return h.split("");
|
|
1321
|
+
}
|
|
1322
|
+
class Ot {
|
|
1323
|
+
svgRendererInstance;
|
|
1324
|
+
options;
|
|
1325
|
+
chordEntries = [];
|
|
1326
|
+
cursorX = 0;
|
|
1327
|
+
cursorY = 0;
|
|
1328
|
+
// Fixed per-instance sizing, derived once from options
|
|
1329
|
+
diagramWidth;
|
|
1330
|
+
diagramHeight;
|
|
1331
|
+
gridTopY;
|
|
1332
|
+
rowHeight;
|
|
1333
|
+
/**
|
|
1334
|
+
* Creates an instance of a GuitarChord diagram renderer.
|
|
1335
|
+
*
|
|
1336
|
+
* @param rootElementCtx - The element (div) reference that the chord diagram(s) will be appended to.
|
|
1337
|
+
* @param options - Optional configuration. Can adjust total width, scale, string/fret count, and coloring.
|
|
1338
|
+
* All config options are in the type GuitarChordOptions.
|
|
1339
|
+
*/
|
|
1340
|
+
constructor(e, t) {
|
|
1341
|
+
if (this.options = {
|
|
1342
|
+
width: 300,
|
|
1343
|
+
scale: 1,
|
|
1344
|
+
stringCount: st,
|
|
1345
|
+
fretCount: rt,
|
|
1346
|
+
stringLabels: [],
|
|
1347
|
+
color: "black",
|
|
1348
|
+
backgroundColor: "transparent",
|
|
1349
|
+
...t
|
|
1350
|
+
}, this.diagramWidth = (this.options.stringCount - 1) * I, this.diagramHeight = this.options.fretCount * T, this.gridTopY = M + Y, this.rowHeight = this.gridTopY + this.diagramHeight + X, this.options.stringLabels.length >= 1 && (this.rowHeight += ot + nt), this.diagramWidth > this.options.width)
|
|
1351
|
+
throw new Error(`The configured width (${this.options.width}) is too small to fit a single ${this.options.stringCount}-string diagram (needs at least ${this.diagramWidth}). Increase 'width'.`);
|
|
1352
|
+
this.svgRendererInstance = new L(e, {
|
|
1353
|
+
width: this.options.width,
|
|
1354
|
+
height: 100,
|
|
1355
|
+
scale: this.options.scale,
|
|
1356
|
+
staffColor: this.options.color,
|
|
1357
|
+
staffBackgroundColor: this.options.backgroundColor,
|
|
1358
|
+
useGlyphs: []
|
|
1359
|
+
});
|
|
1360
|
+
const s = this.svgRendererInstance.rootSvgElement;
|
|
1361
|
+
this.svgRendererInstance.setTotalRootSvgHeight(this.rowHeight), this.svgRendererInstance.applySizingToRootSvg(), this.svgRendererInstance.commitElementsToDOM(s);
|
|
1362
|
+
}
|
|
1363
|
+
drawStringDots(e, t, s, r) {
|
|
1364
|
+
const o = this.gridTopY - Y / 2;
|
|
1365
|
+
for (let n = 0; n < this.options.stringCount; n++) {
|
|
1366
|
+
const i = n * I, a = e[n].toLowerCase(), c = t[n];
|
|
1367
|
+
if (a === "x")
|
|
1368
|
+
this.svgRendererInstance.drawLine(i - f, o - f, i + f, o + f, r), this.svgRendererInstance.drawLine(i - f, o + f, i + f, o - f, r);
|
|
1369
|
+
else if (a === "0")
|
|
1370
|
+
this.svgRendererInstance.drawCircle(i, o, f, r, { filled: !1 });
|
|
1371
|
+
else {
|
|
1372
|
+
const l = parseInt(a, 36) - s + 1;
|
|
1373
|
+
if (l < 1 || l > this.options.fretCount)
|
|
1374
|
+
throw new Error(`Fret ${a} on string ${n + 1} is outside the visible range (${s}-${s + this.options.fretCount - 1}). Adjust 'startFret' or 'fretCount'.`);
|
|
1375
|
+
const d = this.gridTopY + (l - 0.5) * T;
|
|
1376
|
+
if (this.svgRendererInstance.drawCircle(i, d, E, r), c !== "0") {
|
|
1377
|
+
const u = this.options.backgroundColor === "transparent" ? "white" : this.options.backgroundColor;
|
|
1378
|
+
this.svgRendererInstance.drawText(c, i, d + C / 3, r, {
|
|
1379
|
+
fontSize: C,
|
|
1380
|
+
fill: u
|
|
1381
|
+
});
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
drawStringLabels(e, t) {
|
|
1387
|
+
if (!t || t.length === 0) return;
|
|
1388
|
+
const s = E - 1, r = this.rowHeight - s - X;
|
|
1389
|
+
for (let o = 0; o < this.options.stringCount; o++) {
|
|
1390
|
+
const n = t[o];
|
|
1391
|
+
if (!n) continue;
|
|
1392
|
+
const i = o * I, a = r + s / 2;
|
|
1393
|
+
this.svgRendererInstance.drawCircle(i, r, s, e, { filled: !1 }), this.svgRendererInstance.drawText(n, i, a, e, { fontSize: D });
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
// Passed in 'from' and 'to' index values are zero-based
|
|
1397
|
+
drawBarre(e, t) {
|
|
1398
|
+
const s = t.fret;
|
|
1399
|
+
if (t.fromIndex < 0 || t.toIndex >= this.options.stringCount || t.fromIndex >= t.toIndex)
|
|
1400
|
+
throw new Error(`Barre fret error: Invalid string range ${t.fromIndex + 1}:${t.toIndex + 1}.`);
|
|
1401
|
+
if (s < t.startingFret || s > t.startingFret + (this.options.fretCount - 1))
|
|
1402
|
+
throw new Error(`Barre fret error: ${t.fret} is outside the visible range.`);
|
|
1403
|
+
const r = s - t.startingFret, o = t.fromIndex * I, n = t.toIndex * I, i = E * 2, a = n - o + i, c = this.gridTopY + E / 2 + T * r;
|
|
1404
|
+
if (this.svgRendererInstance.drawRect(a, i, e, {
|
|
1405
|
+
x: o - E,
|
|
1406
|
+
y: c,
|
|
1407
|
+
rx: E
|
|
1408
|
+
}), t.finger) {
|
|
1409
|
+
const l = this.options.backgroundColor === "transparent" ? "white" : this.options.backgroundColor;
|
|
1410
|
+
this.svgRendererInstance.drawText(t.finger.toString(), (o + n) / 2, c + C, e, {
|
|
1411
|
+
fontSize: C,
|
|
1412
|
+
fill: l
|
|
1413
|
+
});
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
// Builds a single chord diagram's group element: grid, nut/fret-position label, and per-string markers.
|
|
1417
|
+
renderChordDiagram(e, t, s) {
|
|
1418
|
+
const r = this.svgRendererInstance.createGroup("chord"), o = s?.startFret ?? 1;
|
|
1419
|
+
s?.label && this.svgRendererInstance.drawText(s.label, this.diagramWidth / 2, M - 5, r, {
|
|
1420
|
+
fontSize: D,
|
|
1421
|
+
fontWeight: "bold"
|
|
1422
|
+
});
|
|
1423
|
+
for (let n = 0; n <= this.options.fretCount; n++) {
|
|
1424
|
+
const i = this.gridTopY + n * T;
|
|
1425
|
+
n === 0 && o === 1 ? this.svgRendererInstance.drawRect(this.diagramWidth + U * 2, F, r, {
|
|
1426
|
+
x: -U,
|
|
1427
|
+
y: i - F / 2
|
|
1428
|
+
}) : this.svgRendererInstance.drawLine(0, i, this.diagramWidth, i, r);
|
|
1429
|
+
}
|
|
1430
|
+
o > 1 && this.svgRendererInstance.drawText(`${o}fr`, this.diagramWidth + ht, this.gridTopY + T / 2 + 4, r, {
|
|
1431
|
+
anchor: "start",
|
|
1432
|
+
fontSize: ct,
|
|
1433
|
+
fontWeight: "bold"
|
|
1434
|
+
});
|
|
1435
|
+
for (let n = 0; n < this.options.stringCount; n++) {
|
|
1436
|
+
const i = n * I;
|
|
1437
|
+
this.svgRendererInstance.drawLine(i, this.gridTopY, i, this.gridTopY + this.diagramHeight, r);
|
|
1438
|
+
}
|
|
1439
|
+
return this.drawStringDots(e, t, o, r), this.options.stringLabels.length >= 1 && this.drawStringLabels(r, this.options.stringLabels), s?.barres?.forEach((n) => {
|
|
1440
|
+
this.drawBarre(r, {
|
|
1441
|
+
fromIndex: n.fromString - 1,
|
|
1442
|
+
toIndex: n.toString - 1,
|
|
1443
|
+
fret: n.fret,
|
|
1444
|
+
finger: n.finger,
|
|
1445
|
+
startingFret: o
|
|
1446
|
+
});
|
|
1447
|
+
}), r;
|
|
1448
|
+
}
|
|
1449
|
+
// Repositions every existing chord diagram from scratch, in order, wrapping rows as needed.
|
|
1450
|
+
// Called after any add/remove/change so the layout never has to track incremental deltas.
|
|
1451
|
+
relayoutChords() {
|
|
1452
|
+
if (this.cursorX = E, this.cursorY = 0, this.chordEntries.length === 1) {
|
|
1453
|
+
const t = this.chordEntries[0];
|
|
1454
|
+
this.cursorX = this.options.width / 2 - this.diagramWidth / 2, t.xPos = this.cursorX, t.yPos = this.cursorY, t.gElement.setAttribute("transform", `translate(${this.cursorX}, ${this.cursorY})`);
|
|
1455
|
+
} else
|
|
1456
|
+
this.chordEntries.forEach((t, s) => {
|
|
1457
|
+
s > 0 && this.cursorX + this.diagramWidth > this.options.width && (this.cursorX = E, this.cursorY += this.rowHeight + at), t.xPos = this.cursorX, t.yPos = this.cursorY, t.gElement.setAttribute("transform", `translate(${this.cursorX}, ${this.cursorY})`), this.cursorX += this.diagramWidth + it;
|
|
1458
|
+
});
|
|
1459
|
+
const e = this.cursorY + this.rowHeight;
|
|
1460
|
+
this.svgRendererInstance.setTotalRootSvgHeight(e), this.svgRendererInstance.applySizingToRootSvg();
|
|
1461
|
+
}
|
|
1462
|
+
/**
|
|
1463
|
+
* Adds a new chord diagram. Diagrams are placed left-to-right in the order added, wrapping to a
|
|
1464
|
+
* new row automatically once the configured width is exceeded.
|
|
1465
|
+
*
|
|
1466
|
+
* @param frets - A string, with each value being ordered low-to-high (e.g. first position in string 0 is the low E string)
|
|
1467
|
+
* * `x` - muted string
|
|
1468
|
+
* * `0` - open string
|
|
1469
|
+
* * `1...n` - fretted at the given absolute fret number (e.g. `3`)
|
|
1470
|
+
* @param fingers - A string, with each value being ordered low-to-high (e.g. first position in string 0 is the low E string)
|
|
1471
|
+
* * `0` - No finger labeled
|
|
1472
|
+
* * `1...n` - Finger labeled as number provided
|
|
1473
|
+
*
|
|
1474
|
+
* @param options - Optional per-chord settings: `startFret` (default `1`, for diagrams higher up the neck)
|
|
1475
|
+
* and `label` (a chord name drawn above the diagram).
|
|
1476
|
+
*
|
|
1477
|
+
* @returns The index of the newly added chord, for later use with the CRUD-by-index methods.
|
|
1478
|
+
*
|
|
1479
|
+
* @throws {Error} If fret or finger's string length doesn't match the configured string count.
|
|
1480
|
+
*
|
|
1481
|
+
* @example
|
|
1482
|
+
* // Draw an open C major chord
|
|
1483
|
+
* guitarChord.addChord("x32010", "032010", { label: "C" });
|
|
1484
|
+
*/
|
|
1485
|
+
addChord(e, t, s) {
|
|
1486
|
+
const r = v(e), o = G(t);
|
|
1487
|
+
if (r.length !== this.options.stringCount) throw new Error("Invalid number of frets provided, entries should be equal to number of strings in diagram.");
|
|
1488
|
+
if (o.length !== this.options.stringCount) throw new Error("Invalid number of fingers provided, entries should be equal to number of strings in diagram.");
|
|
1489
|
+
const n = s?.startFret ?? Q(r, this.options.fretCount), i = s?.label, a = s?.barres, c = { startFret: n, label: i, barres: a }, l = this.renderChordDiagram(r, o, c), d = this.svgRendererInstance.getLayerByName("notes");
|
|
1490
|
+
return this.svgRendererInstance.commitElementsToDOM(l, d), this.chordEntries.push({
|
|
1491
|
+
gElement: l,
|
|
1492
|
+
strings: K(r, o),
|
|
1493
|
+
...c,
|
|
1494
|
+
xPos: 0,
|
|
1495
|
+
yPos: 0
|
|
1496
|
+
}), this.relayoutChords(), this.chordEntries.length - 1;
|
|
1497
|
+
}
|
|
1498
|
+
/**
|
|
1499
|
+
* Replaces the chord diagram at the specified index with a new one, in place.
|
|
1500
|
+
*
|
|
1501
|
+
* @param frets - A string, with each value being ordered low-to-high (e.g. first position in string 0 is the low E string)
|
|
1502
|
+
* * `x` - muted string
|
|
1503
|
+
* * `0` - open string
|
|
1504
|
+
* * `1...n` - fretted at the given absolute fret number (e.g. `3`)
|
|
1505
|
+
* @param fingers - A string, with each value being ordered low-to-high (e.g. first position in string 0 is the low E string)
|
|
1506
|
+
* * `0` - No finger labeled
|
|
1507
|
+
* * `1...n` - Finger labeled as number provided
|
|
1508
|
+
*
|
|
1509
|
+
* @param chordIndex - The index of the chord to replace (as returned by addChord).
|
|
1510
|
+
*
|
|
1511
|
+
* @param options - Optional per-chord settings, see addChord.
|
|
1512
|
+
*
|
|
1513
|
+
* @returns void
|
|
1514
|
+
*
|
|
1515
|
+
* @throws {Error} If the index is out of bounds, or if chordDef is invalid (see addChord).
|
|
1516
|
+
*
|
|
1517
|
+
* @example
|
|
1518
|
+
* // Change chord at index 0 to a D major chord
|
|
1519
|
+
* guitarChord.modifyChordByIndex("xx0232", "000132", 0, { label: "D" });
|
|
1520
|
+
*
|
|
1521
|
+
*/
|
|
1522
|
+
modifyChordByIndex(e, t, s, r) {
|
|
1523
|
+
if (s >= this.chordEntries.length || s < 0) throw new Error("Chord index was out of bounds.");
|
|
1524
|
+
const o = v(e), n = G(t);
|
|
1525
|
+
if (o.length !== this.options.stringCount) throw new Error("Invalid number of frets provided, entries should be equal to number of strings in diagram.");
|
|
1526
|
+
if (n.length !== this.options.stringCount) throw new Error("Invalid number of fingers provided, entries should be equal to number of strings in diagram.");
|
|
1527
|
+
const i = this.chordEntries[s], a = r && "label" in r ? r.label === "" ? void 0 : r.label : i.label, c = r?.startFret ?? Q(o, this.options.fretCount), l = { ...r, label: a, startFret: c }, d = this.renderChordDiagram(o, n, l);
|
|
1528
|
+
this.svgRendererInstance.getLayerByName("notes").replaceChild(d, i.gElement), this.chordEntries[s] = {
|
|
1529
|
+
gElement: d,
|
|
1530
|
+
strings: K(o, n),
|
|
1531
|
+
...l,
|
|
1532
|
+
xPos: i.xPos,
|
|
1533
|
+
yPos: i.yPos
|
|
1534
|
+
}, this.relayoutChords();
|
|
1535
|
+
}
|
|
1536
|
+
/**
|
|
1537
|
+
* Removes the chord diagram at the specified index. Remaining chords are re-flowed to close the gap.
|
|
1538
|
+
* @param chordIndex - The index of the chord to remove.
|
|
1539
|
+
* @returns void
|
|
1540
|
+
* @throws {Error} If the index is out of bounds.
|
|
1541
|
+
*/
|
|
1542
|
+
removeChordByIndex(e) {
|
|
1543
|
+
if (e >= this.chordEntries.length || e < 0) throw new Error("Chord index was out of bounds.");
|
|
1544
|
+
this.chordEntries[e].gElement.remove(), this.chordEntries.splice(e, 1), this.relayoutChords();
|
|
1545
|
+
}
|
|
1546
|
+
/**
|
|
1547
|
+
* Removes all chord diagrams and resets internal positioning.
|
|
1548
|
+
* @returns void
|
|
1549
|
+
*/
|
|
1550
|
+
clearAllChords() {
|
|
1551
|
+
this.svgRendererInstance.getLayerByName("notes").replaceChildren(), this.chordEntries = [], this.cursorX = 0, this.cursorY = 0, this.svgRendererInstance.setTotalRootSvgHeight(this.rowHeight), this.svgRendererInstance.applySizingToRootSvg();
|
|
1552
|
+
}
|
|
1553
|
+
/**
|
|
1554
|
+
* * Used to automatically determine options for creating barre lines.
|
|
1555
|
+
* * The returned value then can be used in addChord and modifyChord methods.
|
|
1556
|
+
* @returns GuitarBarreDef[]
|
|
1557
|
+
*/
|
|
1558
|
+
determineBarreOptions(e, t, s) {
|
|
1559
|
+
const r = v(e), o = G(t), n = [];
|
|
1560
|
+
return s.forEach((i) => {
|
|
1561
|
+
const a = {};
|
|
1562
|
+
for (let c = 0; c < r.length; c++) {
|
|
1563
|
+
if (r[c].toLowerCase() === "x") continue;
|
|
1564
|
+
if (parseInt(r[c], 36) === i && o[c] !== "0") {
|
|
1565
|
+
const d = o[c];
|
|
1566
|
+
a[d] || (a[d] = []), a[d].push(c);
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
for (const [c, l] of Object.entries(a))
|
|
1570
|
+
l.length > 1 && n.push({
|
|
1571
|
+
fret: i,
|
|
1572
|
+
fromString: Math.min(...l) + 1,
|
|
1573
|
+
toString: Math.max(...l) + 1,
|
|
1574
|
+
finger: Number(c)
|
|
1575
|
+
});
|
|
1576
|
+
}), n;
|
|
1577
|
+
}
|
|
1578
|
+
/**
|
|
1579
|
+
* Removes the root svg element and cleans up arrays.
|
|
1580
|
+
* @returns void
|
|
1581
|
+
*/
|
|
1582
|
+
destroy() {
|
|
1583
|
+
this.chordEntries = [], this.svgRendererInstance.destroy();
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1224
1586
|
export {
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1587
|
+
Ot as GuitarChord,
|
|
1588
|
+
Rt as MusicStaff,
|
|
1589
|
+
yt as RhythmStaff,
|
|
1590
|
+
wt as ScrollingStaff
|
|
1228
1591
|
};
|