vector-score 1.1.0 → 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 +145 -28
- package/dist/classes/GuitarChord.d.ts +106 -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/index.d.ts +3 -0
- package/dist/types.d.ts +15 -0
- package/dist/vector-score.js +545 -245
- 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,16 +31,16 @@ const I = {
|
|
|
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
|
-
},
|
|
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
41
|
sharp: ["F", "C", "G", "D", "A", "E", "B"],
|
|
42
42
|
flat: ["B", "E", "A", "D", "G", "C", "F"]
|
|
43
|
-
},
|
|
43
|
+
}, k = {
|
|
44
44
|
C: { type: "sharp", count: 0 },
|
|
45
45
|
G: { type: "sharp", count: 1 },
|
|
46
46
|
D: { type: "sharp", count: 2 },
|
|
@@ -54,7 +54,7 @@ const I = {
|
|
|
54
54
|
Ab: { type: "flat", count: 4 },
|
|
55
55
|
Db: { type: "flat", count: 5 },
|
|
56
56
|
Gb: { type: "flat", count: 6 }
|
|
57
|
-
},
|
|
57
|
+
}, B = {
|
|
58
58
|
treble: {
|
|
59
59
|
sharp: [5, 5, 5, 5, 4, 5, 4],
|
|
60
60
|
// F5 C5 G5 D5 A4 E5 B4
|
|
@@ -75,22 +75,22 @@ const I = {
|
|
|
75
75
|
},
|
|
76
76
|
grand: { sharp: [], flat: [] }
|
|
77
77
|
};
|
|
78
|
-
function
|
|
79
|
-
const e =
|
|
80
|
-
if (!e) throw new Error(`Unknown key signature "${h}". Valid keys: ${Object.keys(
|
|
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
81
|
return e;
|
|
82
82
|
}
|
|
83
|
-
function
|
|
84
|
-
const r =
|
|
83
|
+
function ft(h, e, t, s) {
|
|
84
|
+
const r = dt(t);
|
|
85
85
|
if (r.count === 0) return 0;
|
|
86
|
-
const
|
|
87
|
-
return e.getKeySignatureYPositions(r.type, r.count).forEach((
|
|
88
|
-
|
|
89
|
-
h.drawGlyph(
|
|
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
90
|
});
|
|
91
|
-
}), r.count *
|
|
91
|
+
}), r.count * $;
|
|
92
92
|
}
|
|
93
|
-
const
|
|
93
|
+
const Et = {
|
|
94
94
|
CLEF_TREBLE: {
|
|
95
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",
|
|
96
96
|
xOffset: 0,
|
|
@@ -186,26 +186,26 @@ const $ = {
|
|
|
186
186
|
xOffset: 0,
|
|
187
187
|
yOffset: 0
|
|
188
188
|
}
|
|
189
|
-
},
|
|
190
|
-
function
|
|
191
|
-
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);
|
|
192
192
|
if (!e || !e.groups)
|
|
193
193
|
throw new Error(`Invalid note string format: ${h}. Expected format: [A-Ga-g][#|b]?[0-9][w|h|q|e].`);
|
|
194
|
-
let { name: t, accidental: s, octave: r, duration:
|
|
195
|
-
t = t.toUpperCase(),
|
|
196
|
-
const
|
|
194
|
+
let { name: t, accidental: s, octave: r, duration: o } = e.groups;
|
|
195
|
+
t = t.toUpperCase(), o = o.toLowerCase(), o || (o = "w");
|
|
196
|
+
const n = {
|
|
197
197
|
name: t,
|
|
198
198
|
octave: parseInt(r),
|
|
199
|
-
duration:
|
|
199
|
+
duration: o
|
|
200
200
|
};
|
|
201
|
-
return s && (
|
|
201
|
+
return s && (n.accidental = s), n;
|
|
202
202
|
}
|
|
203
|
-
function
|
|
204
|
-
const e = h.match(
|
|
203
|
+
function R(h) {
|
|
204
|
+
const e = h.match(gt);
|
|
205
205
|
if (!e) throw new Error(`Invalid note duration '${h}'. Use w | h | q | e.`);
|
|
206
206
|
return e[0].toString().toLowerCase();
|
|
207
207
|
}
|
|
208
|
-
function
|
|
208
|
+
function x(h) {
|
|
209
209
|
let e;
|
|
210
210
|
switch (h) {
|
|
211
211
|
case "treble":
|
|
@@ -221,22 +221,22 @@ function R(h) {
|
|
|
221
221
|
if (!e) throw new Error(`Invalid clef type: ${h}. Valid clef types are: treble, bass, alto.`);
|
|
222
222
|
return e;
|
|
223
223
|
}
|
|
224
|
-
function
|
|
225
|
-
let e =
|
|
224
|
+
function W(h) {
|
|
225
|
+
let e = H.indexOf(h.name);
|
|
226
226
|
return e += h.octave * 12, e;
|
|
227
227
|
}
|
|
228
|
-
function
|
|
229
|
-
return
|
|
228
|
+
function m(h, e) {
|
|
229
|
+
return H.indexOf(h.name) - H.indexOf(e.name) + (h.octave - e.octave) * 7;
|
|
230
230
|
}
|
|
231
|
-
const
|
|
232
|
-
class
|
|
231
|
+
const y = 48, Z = 40 + 30 / 2, V = 20, q = 90;
|
|
232
|
+
class tt {
|
|
233
233
|
params;
|
|
234
234
|
rendererRef;
|
|
235
235
|
width = 0;
|
|
236
236
|
trebleStaffHeight = 0;
|
|
237
237
|
constructor(e, t) {
|
|
238
238
|
this.rendererRef = e;
|
|
239
|
-
const s =
|
|
239
|
+
const s = S[t];
|
|
240
240
|
if (!s) throw new Error(`Staff type ${t} is not supported`);
|
|
241
241
|
this.params = s;
|
|
242
242
|
}
|
|
@@ -251,106 +251,106 @@ class x {
|
|
|
251
251
|
const t = this.rendererRef.getLayerByName("staff");
|
|
252
252
|
let s = this.drawStaffLines(0, t), r = this.drawStaffLines(s + 30, t);
|
|
253
253
|
this.rendererRef.drawLine(0, 0, 0, r, t), this.rendererRef.drawLine(this.width, 0, this.width, r, t), this.trebleStaffHeight = s;
|
|
254
|
-
let
|
|
255
|
-
const
|
|
256
|
-
this.rendererRef.drawGlyph(
|
|
254
|
+
let o = r, n = 1;
|
|
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);
|
|
257
257
|
};
|
|
258
258
|
shouldNoteFlip(e) {
|
|
259
|
-
const t = Math.abs(e -
|
|
260
|
-
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;
|
|
261
261
|
}
|
|
262
262
|
getLedgerLinesX(e, t) {
|
|
263
|
-
const s = [], r =
|
|
264
|
-
let
|
|
265
|
-
if (r ===
|
|
266
|
-
return s.push({ x1: -2, x2:
|
|
263
|
+
const s = [], r = W(e);
|
|
264
|
+
let o = e.duration === "w" ? 17 : 12.5;
|
|
265
|
+
if (r === y)
|
|
266
|
+
return s.push({ x1: -2, x2: o, yPos: 0 }), s;
|
|
267
267
|
if (t < this.params.topLineYPos) {
|
|
268
|
-
let
|
|
269
|
-
for (;
|
|
268
|
+
let n = this.params.topLineYPos - 10;
|
|
269
|
+
for (; n >= t; )
|
|
270
270
|
s.push({
|
|
271
271
|
x1: -2,
|
|
272
|
-
x2:
|
|
273
|
-
yPos:
|
|
274
|
-
}),
|
|
272
|
+
x2: o,
|
|
273
|
+
yPos: n - t
|
|
274
|
+
}), n -= 10;
|
|
275
275
|
} else if (t > this.params.bottomLineYPos) {
|
|
276
|
-
let
|
|
277
|
-
for (;
|
|
276
|
+
let n = this.params.bottomLineYPos + 10;
|
|
277
|
+
for (; n <= t; )
|
|
278
278
|
s.push({
|
|
279
279
|
x1: -2,
|
|
280
|
-
x2:
|
|
281
|
-
yPos:
|
|
282
|
-
}),
|
|
280
|
+
x2: o,
|
|
281
|
+
yPos: n - t
|
|
282
|
+
}), n += 10;
|
|
283
283
|
}
|
|
284
284
|
return s;
|
|
285
285
|
}
|
|
286
286
|
calculateNoteYPos = (e) => {
|
|
287
|
-
let s =
|
|
288
|
-
const r =
|
|
289
|
-
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;
|
|
290
290
|
};
|
|
291
291
|
getKeySignatureYPositions(e, t) {
|
|
292
|
-
const s =
|
|
293
|
-
(
|
|
294
|
-
),
|
|
295
|
-
(
|
|
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
296
|
);
|
|
297
|
-
return [
|
|
297
|
+
return [n, i];
|
|
298
298
|
}
|
|
299
299
|
}
|
|
300
|
-
const
|
|
301
|
-
class
|
|
300
|
+
const pt = 20;
|
|
301
|
+
class _ {
|
|
302
302
|
params;
|
|
303
303
|
rendererRef;
|
|
304
304
|
constructor(e, t) {
|
|
305
305
|
this.rendererRef = e;
|
|
306
|
-
const s =
|
|
306
|
+
const s = S[t];
|
|
307
307
|
if (!s) throw new Error(`Staff type ${t} is not supported`);
|
|
308
308
|
this.params = s;
|
|
309
309
|
}
|
|
310
310
|
drawStaff = (e) => {
|
|
311
311
|
const t = this.rendererRef.getLayerByName("staff");
|
|
312
312
|
let s = 0;
|
|
313
|
-
for (let
|
|
313
|
+
for (let i = 0; i < 5; i++)
|
|
314
314
|
this.rendererRef.drawLine(0, s, e, s, t), s += 10;
|
|
315
315
|
this.rendererRef.drawLine(0, 0, 0, s - 10, t), this.rendererRef.drawLine(e, 0, e, s - 10, t);
|
|
316
|
-
let r = s - 10 + 1,
|
|
317
|
-
const
|
|
318
|
-
this.rendererRef.drawGlyph(
|
|
316
|
+
let r = s - 10 + 1, o = 1;
|
|
317
|
+
const n = x(this.params.staffType);
|
|
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);
|
|
319
319
|
};
|
|
320
320
|
shouldNoteFlip(e) {
|
|
321
|
-
return e <=
|
|
321
|
+
return e <= pt;
|
|
322
322
|
}
|
|
323
323
|
getLedgerLinesX(e, t) {
|
|
324
324
|
const s = [];
|
|
325
325
|
let r = e.duration === "w" ? 17 : 12.5;
|
|
326
326
|
if (t < this.params.topLineYPos) {
|
|
327
|
-
let
|
|
328
|
-
for (;
|
|
327
|
+
let o = this.params.topLineYPos - 10;
|
|
328
|
+
for (; o >= t; )
|
|
329
329
|
s.push({
|
|
330
330
|
x1: -2,
|
|
331
331
|
x2: r,
|
|
332
|
-
yPos:
|
|
333
|
-
}),
|
|
332
|
+
yPos: o - t
|
|
333
|
+
}), o -= 10;
|
|
334
334
|
} else if (t > this.params.bottomLineYPos) {
|
|
335
|
-
let
|
|
336
|
-
for (;
|
|
335
|
+
let o = this.params.bottomLineYPos + 10;
|
|
336
|
+
for (; o <= t; )
|
|
337
337
|
s.push({
|
|
338
338
|
x1: -2,
|
|
339
339
|
x2: r,
|
|
340
|
-
yPos:
|
|
341
|
-
}),
|
|
340
|
+
yPos: o - t
|
|
341
|
+
}), o += 10;
|
|
342
342
|
}
|
|
343
343
|
return s;
|
|
344
344
|
}
|
|
345
|
-
calculateNoteYPos = (e) =>
|
|
345
|
+
calculateNoteYPos = (e) => m(this.params.topLineNote, e) * (10 / 2);
|
|
346
346
|
getKeySignatureYPositions(e, t) {
|
|
347
|
-
const s =
|
|
347
|
+
const s = J[e].slice(0, t), r = B[this.params.staffType][e];
|
|
348
348
|
return [s.map(
|
|
349
|
-
(
|
|
349
|
+
(n, i) => this.calculateNoteYPos({ name: n, octave: r[i] })
|
|
350
350
|
)];
|
|
351
351
|
}
|
|
352
352
|
}
|
|
353
|
-
class
|
|
353
|
+
class et {
|
|
354
354
|
svgRendererInstance;
|
|
355
355
|
strategyInstance;
|
|
356
356
|
constructor(e, t) {
|
|
@@ -361,12 +361,12 @@ class U {
|
|
|
361
361
|
}
|
|
362
362
|
chordOffsetConsecutiveAccidentals(e) {
|
|
363
363
|
let t = 0, s = 0, r = 0;
|
|
364
|
-
for (let
|
|
365
|
-
const
|
|
366
|
-
if (
|
|
367
|
-
const
|
|
368
|
-
if (!
|
|
369
|
-
|
|
364
|
+
for (let o = 0; o < e.length; o++) {
|
|
365
|
+
const n = e[o];
|
|
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) {
|
|
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)`);
|
|
370
370
|
}
|
|
371
371
|
}
|
|
372
372
|
return -s;
|
|
@@ -374,94 +374,94 @@ class U {
|
|
|
374
374
|
chordOffsetCloseNotes(e) {
|
|
375
375
|
let t = e[0], s = 0;
|
|
376
376
|
for (let r = 1; r < e.length; r++) {
|
|
377
|
-
const
|
|
378
|
-
if (-
|
|
379
|
-
s = 28 / 2,
|
|
380
|
-
const
|
|
381
|
-
if (
|
|
382
|
-
const c =
|
|
383
|
-
let
|
|
384
|
-
l && (
|
|
377
|
+
const o = e[r];
|
|
378
|
+
if (-m(t.noteObj, o.noteObj) === 1) {
|
|
379
|
+
s = 28 / 2, o.noteGroup.setAttribute("transform", `translate(${s}, ${o.noteYPos})`);
|
|
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)`);
|
|
385
385
|
}
|
|
386
386
|
r++, t = e[r];
|
|
387
387
|
continue;
|
|
388
388
|
}
|
|
389
|
-
t =
|
|
389
|
+
t = o;
|
|
390
390
|
}
|
|
391
391
|
return s;
|
|
392
392
|
}
|
|
393
393
|
// Handles drawing the glyphs to internal group, applies the xPositioning to note Cursor X
|
|
394
394
|
renderNote(e) {
|
|
395
|
-
const t = this.svgRendererInstance.createGroup("note"), s =
|
|
395
|
+
const t = this.svgRendererInstance.createGroup("note"), s = P(e), r = this.strategyInstance.calculateNoteYPos({
|
|
396
396
|
name: s.name,
|
|
397
397
|
octave: s.octave
|
|
398
398
|
});
|
|
399
|
-
let
|
|
399
|
+
let o = this.strategyInstance.shouldNoteFlip(r);
|
|
400
400
|
switch (s.duration) {
|
|
401
401
|
case "h":
|
|
402
|
-
this.svgRendererInstance.drawGlyph("NOTE_HEAD_HALF", t), this.drawStem(t,
|
|
402
|
+
this.svgRendererInstance.drawGlyph("NOTE_HEAD_HALF", t), this.drawStem(t, o);
|
|
403
403
|
break;
|
|
404
404
|
case "q":
|
|
405
|
-
this.svgRendererInstance.drawGlyph("NOTE_HEAD_QUARTER", t), this.drawStem(t,
|
|
405
|
+
this.svgRendererInstance.drawGlyph("NOTE_HEAD_QUARTER", t), this.drawStem(t, o);
|
|
406
406
|
break;
|
|
407
407
|
case "e":
|
|
408
|
-
|
|
408
|
+
o ? this.svgRendererInstance.drawGlyph("EIGHTH_NOTE_FLIPPED", t) : this.svgRendererInstance.drawGlyph("EIGHTH_NOTE", t);
|
|
409
409
|
break;
|
|
410
410
|
default:
|
|
411
411
|
this.svgRendererInstance.drawGlyph("NOTE_HEAD_WHOLE", t);
|
|
412
412
|
}
|
|
413
|
-
let
|
|
413
|
+
let n = 0;
|
|
414
414
|
switch (s.accidental) {
|
|
415
415
|
case "#":
|
|
416
|
-
this.svgRendererInstance.drawGlyph("ACCIDENTAL_SHARP", t),
|
|
416
|
+
this.svgRendererInstance.drawGlyph("ACCIDENTAL_SHARP", t), n -= -8;
|
|
417
417
|
break;
|
|
418
418
|
case "b":
|
|
419
|
-
this.svgRendererInstance.drawGlyph("ACCIDENTAL_FLAT", t),
|
|
419
|
+
this.svgRendererInstance.drawGlyph("ACCIDENTAL_FLAT", t), n -= -8;
|
|
420
420
|
break;
|
|
421
421
|
case "n":
|
|
422
|
-
this.svgRendererInstance.drawGlyph("ACCIDENTAL_NATURAL", t),
|
|
422
|
+
this.svgRendererInstance.drawGlyph("ACCIDENTAL_NATURAL", t), n -= -8;
|
|
423
423
|
break;
|
|
424
424
|
case "##":
|
|
425
|
-
this.svgRendererInstance.drawGlyph("ACCIDENTAL_DOUBLE_SHARP", t),
|
|
425
|
+
this.svgRendererInstance.drawGlyph("ACCIDENTAL_DOUBLE_SHARP", t), n -= -10;
|
|
426
426
|
break;
|
|
427
427
|
case "bb":
|
|
428
|
-
this.svgRendererInstance.drawGlyph("ACCIDENTAL_DOUBLE_FLAT", t),
|
|
428
|
+
this.svgRendererInstance.drawGlyph("ACCIDENTAL_DOUBLE_FLAT", t), n -= -14;
|
|
429
429
|
break;
|
|
430
430
|
}
|
|
431
|
-
return this.strategyInstance.getLedgerLinesX(s, r).forEach((
|
|
432
|
-
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);
|
|
433
433
|
}), {
|
|
434
434
|
noteGroup: t,
|
|
435
435
|
noteObj: s,
|
|
436
436
|
noteYPos: r,
|
|
437
|
-
accidentalOffset:
|
|
437
|
+
accidentalOffset: n,
|
|
438
438
|
cursorOffset: 0
|
|
439
439
|
};
|
|
440
440
|
}
|
|
441
441
|
renderChord(e) {
|
|
442
442
|
const t = this.svgRendererInstance.createGroup("chord"), s = [];
|
|
443
|
-
for (const
|
|
444
|
-
const
|
|
445
|
-
|
|
446
|
-
noteGroup:
|
|
447
|
-
noteObj:
|
|
448
|
-
noteYPos:
|
|
443
|
+
for (const n of e) {
|
|
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,
|
|
449
449
|
cursorOffset: 0,
|
|
450
450
|
accidentalOffset: 0
|
|
451
451
|
});
|
|
452
452
|
}
|
|
453
|
-
const r = this.chordOffsetConsecutiveAccidentals(s),
|
|
453
|
+
const r = this.chordOffsetConsecutiveAccidentals(s), o = this.chordOffsetCloseNotes(s);
|
|
454
454
|
return {
|
|
455
455
|
noteGroup: t,
|
|
456
456
|
noteObj: s[0].noteObj,
|
|
457
457
|
noteYPos: 0,
|
|
458
458
|
accidentalOffset: r,
|
|
459
|
-
cursorOffset:
|
|
459
|
+
cursorOffset: o
|
|
460
460
|
};
|
|
461
461
|
}
|
|
462
462
|
}
|
|
463
|
-
const
|
|
464
|
-
class
|
|
463
|
+
const g = "http://www.w3.org/2000/svg", At = 0.1, w = 1200;
|
|
464
|
+
class L {
|
|
465
465
|
rootElementRef;
|
|
466
466
|
svgElementRef;
|
|
467
467
|
// Layers
|
|
@@ -479,18 +479,18 @@ class b {
|
|
|
479
479
|
// from get rootSvgElement().
|
|
480
480
|
// HEIGHT and YOfsset should be set externally, values are calculated internally.
|
|
481
481
|
constructor(e, t) {
|
|
482
|
-
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);
|
|
483
483
|
}
|
|
484
484
|
// Creates SVG defs for all glyphs in GLYPH_ENTRIES, applies global scale and offsets, appends to root SVG
|
|
485
485
|
makeGlyphDefs(e) {
|
|
486
|
-
const t = document.createElementNS(
|
|
487
|
-
Object.entries(
|
|
488
|
-
const
|
|
489
|
-
|
|
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);
|
|
490
490
|
}), this.rootSvgElement.appendChild(t);
|
|
491
491
|
}
|
|
492
492
|
createGroup(e) {
|
|
493
|
-
const t = document.createElementNS(
|
|
493
|
+
const t = document.createElementNS(g, "g");
|
|
494
494
|
return e && t.classList.add(`vs-${e}`), t;
|
|
495
495
|
}
|
|
496
496
|
commitElementsToDOM(e, t = this.rootElementRef) {
|
|
@@ -514,6 +514,9 @@ class b {
|
|
|
514
514
|
addTotalRootSvgHeight(e) {
|
|
515
515
|
this.totalHeight += e;
|
|
516
516
|
}
|
|
517
|
+
setTotalRootSvgHeight(e) {
|
|
518
|
+
this.totalHeight = e;
|
|
519
|
+
}
|
|
517
520
|
addTotalRootSvgYOffset(e) {
|
|
518
521
|
this.totalYOffset += e;
|
|
519
522
|
}
|
|
@@ -530,13 +533,13 @@ class b {
|
|
|
530
533
|
return this.parentGroupContainer;
|
|
531
534
|
}
|
|
532
535
|
// Drawing Methods
|
|
533
|
-
drawLine(e, t, s, r,
|
|
534
|
-
const
|
|
535
|
-
|
|
536
|
+
drawLine(e, t, s, r, o) {
|
|
537
|
+
const n = document.createElementNS(g, "line");
|
|
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);
|
|
536
539
|
}
|
|
537
540
|
drawRect(e, t, s, r) {
|
|
538
|
-
const
|
|
539
|
-
return
|
|
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;
|
|
540
543
|
}
|
|
541
544
|
drawGlyph(e, t, s) {
|
|
542
545
|
s = {
|
|
@@ -544,14 +547,22 @@ class b {
|
|
|
544
547
|
yOffset: 0,
|
|
545
548
|
...s
|
|
546
549
|
};
|
|
547
|
-
const r = document.createElementNS(
|
|
550
|
+
const r = document.createElementNS(g, "use");
|
|
548
551
|
r.setAttribute("href", `#glyph-${e}`), (s.xOffset || s.yOffset) && r.setAttribute("transform", `translate(${s.xOffset}, ${s.yOffset})`), t.appendChild(r);
|
|
549
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
|
+
}
|
|
550
561
|
destroy() {
|
|
551
562
|
this.rootElementRef && this.svgElementRef && this.rootElementRef.contains(this.svgElementRef) && this.rootElementRef.removeChild(this.svgElementRef);
|
|
552
563
|
}
|
|
553
564
|
}
|
|
554
|
-
const
|
|
565
|
+
const It = [
|
|
555
566
|
"CLEF_TREBLE",
|
|
556
567
|
"CLEF_BASS",
|
|
557
568
|
"CLEF_ALTO",
|
|
@@ -566,7 +577,7 @@ const j = [
|
|
|
566
577
|
"ACCIDENTAL_DOUBLE_SHARP",
|
|
567
578
|
"ACCIDENTAL_DOUBLE_FLAT"
|
|
568
579
|
];
|
|
569
|
-
class
|
|
580
|
+
class Rt {
|
|
570
581
|
svgRendererInstance;
|
|
571
582
|
strategyInstance;
|
|
572
583
|
noteRendererInstance;
|
|
@@ -591,44 +602,44 @@ class et {
|
|
|
591
602
|
staffColor: "black",
|
|
592
603
|
staffBackgroundColor: "transparent",
|
|
593
604
|
...t
|
|
594
|
-
}, this.svgRendererInstance = new
|
|
605
|
+
}, this.svgRendererInstance = new L(e, {
|
|
595
606
|
width: this.options.width,
|
|
596
607
|
height: 100,
|
|
597
608
|
scale: this.options.scale,
|
|
598
609
|
staffColor: this.options.staffColor,
|
|
599
610
|
staffBackgroundColor: this.options.staffBackgroundColor,
|
|
600
|
-
useGlyphs:
|
|
611
|
+
useGlyphs: It
|
|
601
612
|
});
|
|
602
613
|
const s = this.svgRendererInstance.rootSvgElement;
|
|
603
614
|
switch (this.options.staffType) {
|
|
604
615
|
case "grand":
|
|
605
|
-
this.strategyInstance = new
|
|
616
|
+
this.strategyInstance = new tt(this.svgRendererInstance, "grand");
|
|
606
617
|
break;
|
|
607
618
|
case "bass":
|
|
608
|
-
this.strategyInstance = new
|
|
619
|
+
this.strategyInstance = new _(this.svgRendererInstance, "bass");
|
|
609
620
|
break;
|
|
610
621
|
case "treble":
|
|
611
|
-
this.strategyInstance = new
|
|
622
|
+
this.strategyInstance = new _(this.svgRendererInstance, "treble");
|
|
612
623
|
break;
|
|
613
624
|
case "alto":
|
|
614
|
-
this.strategyInstance = new
|
|
625
|
+
this.strategyInstance = new _(this.svgRendererInstance, "alto");
|
|
615
626
|
break;
|
|
616
627
|
default:
|
|
617
628
|
throw new Error(`The staff type ${this.options.staffType} is not supported. Please use "treble", "bass", "alto", or "grand".`);
|
|
618
629
|
}
|
|
619
|
-
if (this.strategyInstance.drawStaff(this.options.width), this.noteRendererInstance = new
|
|
620
|
-
const
|
|
621
|
-
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);
|
|
622
633
|
}
|
|
623
634
|
if (this.options.spaceBelow) {
|
|
624
|
-
let
|
|
625
|
-
this.options.staffType === "grand" && (
|
|
635
|
+
let o = this.options.spaceBelow * 10;
|
|
636
|
+
this.options.staffType === "grand" && (o -= 10 / 2), this.svgRendererInstance.addTotalRootSvgHeight(o);
|
|
626
637
|
}
|
|
627
638
|
let r = 0;
|
|
628
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);
|
|
629
640
|
}
|
|
630
641
|
createKeySignature(e) {
|
|
631
|
-
return
|
|
642
|
+
return ft(this.svgRendererInstance, this.strategyInstance, e, lt);
|
|
632
643
|
}
|
|
633
644
|
/**
|
|
634
645
|
* Draws a note on the staff.
|
|
@@ -652,20 +663,20 @@ class et {
|
|
|
652
663
|
*/
|
|
653
664
|
drawNote(e) {
|
|
654
665
|
const t = Array.isArray(e) ? e : [e], s = this.svgRendererInstance.getLayerByName("notes"), r = [];
|
|
655
|
-
for (const
|
|
656
|
-
let
|
|
666
|
+
for (const o of t) {
|
|
667
|
+
let n;
|
|
657
668
|
try {
|
|
658
|
-
|
|
659
|
-
} catch (
|
|
660
|
-
throw r.length > 0 && this.svgRendererInstance.commitElementsToDOM(r, s),
|
|
669
|
+
n = this.noteRendererInstance.renderNote(o);
|
|
670
|
+
} catch (i) {
|
|
671
|
+
throw r.length > 0 && this.svgRendererInstance.commitElementsToDOM(r, s), i;
|
|
661
672
|
}
|
|
662
|
-
|
|
663
|
-
gElement:
|
|
664
|
-
note:
|
|
665
|
-
xPos: this.noteCursorX +
|
|
666
|
-
yPos:
|
|
667
|
-
accidentalXOffset:
|
|
668
|
-
}), this.noteCursorX += 28 +
|
|
673
|
+
n.noteGroup.setAttribute("transform", `translate(${this.noteCursorX + n.accidentalOffset}, ${n.noteYPos})`), this.noteEntries.push({
|
|
674
|
+
gElement: n.noteGroup,
|
|
675
|
+
note: n.noteObj,
|
|
676
|
+
xPos: this.noteCursorX + n.accidentalOffset,
|
|
677
|
+
yPos: n.noteYPos,
|
|
678
|
+
accidentalXOffset: n.accidentalOffset
|
|
679
|
+
}), this.noteCursorX += 28 + n.accidentalOffset, r.push(n.noteGroup);
|
|
669
680
|
}
|
|
670
681
|
this.svgRendererInstance.commitElementsToDOM(r, s);
|
|
671
682
|
}
|
|
@@ -689,7 +700,7 @@ class et {
|
|
|
689
700
|
const t = this.svgRendererInstance.getLayerByName("notes"), s = this.noteRendererInstance.renderChord(e);
|
|
690
701
|
s.noteGroup.setAttribute("transform", `translate(${this.noteCursorX + s.accidentalOffset}, 0)`), this.noteEntries.push({
|
|
691
702
|
gElement: s.noteGroup,
|
|
692
|
-
note:
|
|
703
|
+
note: P(e[0]),
|
|
693
704
|
xPos: this.noteCursorX + s.accidentalOffset,
|
|
694
705
|
yPos: 0,
|
|
695
706
|
accidentalXOffset: s.accidentalOffset
|
|
@@ -703,16 +714,16 @@ class et {
|
|
|
703
714
|
const e = this.options.width - this.noteStartX, t = this.noteEntries.length;
|
|
704
715
|
if (t <= 0 || e <= 0) return;
|
|
705
716
|
const s = Math.round(e / t);
|
|
706
|
-
this.noteEntries.map((
|
|
707
|
-
const
|
|
717
|
+
this.noteEntries.map((o, n) => {
|
|
718
|
+
const i = (n + 0.5) * s, a = o.gElement.getBBox(), c = i - a.width / 2 - a.x, l = Math.round(c * 10) / 10;
|
|
708
719
|
return {
|
|
709
|
-
entry:
|
|
720
|
+
entry: o,
|
|
710
721
|
newX: l,
|
|
711
|
-
isChord:
|
|
722
|
+
isChord: o.gElement.classList.contains("chord")
|
|
712
723
|
};
|
|
713
|
-
}).forEach((
|
|
714
|
-
const { entry:
|
|
715
|
-
|
|
724
|
+
}).forEach((o) => {
|
|
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;
|
|
716
727
|
});
|
|
717
728
|
}
|
|
718
729
|
/**
|
|
@@ -740,11 +751,11 @@ class et {
|
|
|
740
751
|
*/
|
|
741
752
|
changeNoteByIndex(e, t) {
|
|
742
753
|
if (t >= this.noteEntries.length) throw new Error("Note index was out of bounds.");
|
|
743
|
-
const s = this.noteEntries[t], r = this.noteRendererInstance.renderNote(e),
|
|
744
|
-
r.noteGroup.setAttribute("transform", `translate(${
|
|
754
|
+
const s = this.noteEntries[t], r = this.noteRendererInstance.renderNote(e), n = s.xPos - s.accidentalXOffset + r.accidentalOffset;
|
|
755
|
+
r.noteGroup.setAttribute("transform", `translate(${n}, ${r.noteYPos})`), this.svgRendererInstance.getLayerByName("notes").replaceChild(r.noteGroup, s.gElement), this.noteEntries[t] = {
|
|
745
756
|
gElement: r.noteGroup,
|
|
746
757
|
note: r.noteObj,
|
|
747
|
-
xPos:
|
|
758
|
+
xPos: n,
|
|
748
759
|
yPos: r.noteYPos,
|
|
749
760
|
accidentalXOffset: r.accidentalOffset
|
|
750
761
|
};
|
|
@@ -768,11 +779,11 @@ class et {
|
|
|
768
779
|
changeChordByIndex(e, t) {
|
|
769
780
|
if (t >= this.noteEntries.length) throw new Error("Chord index was out of bounds.");
|
|
770
781
|
if (e.length < 2) throw new Error("Notes provided need to be more than one to be considered a chord.");
|
|
771
|
-
const s = this.noteEntries[t], r = this.noteRendererInstance.renderChord(e),
|
|
772
|
-
r.noteGroup.setAttribute("transform", `translate(${
|
|
782
|
+
const s = this.noteEntries[t], r = this.noteRendererInstance.renderChord(e), n = s.xPos - s.accidentalXOffset + r.accidentalOffset;
|
|
783
|
+
r.noteGroup.setAttribute("transform", `translate(${n}, 0)`), this.svgRendererInstance.getLayerByName("notes").replaceChild(r.noteGroup, s.gElement), this.noteEntries[t] = {
|
|
773
784
|
gElement: r.noteGroup,
|
|
774
|
-
note:
|
|
775
|
-
xPos:
|
|
785
|
+
note: P(e[0]),
|
|
786
|
+
xPos: n,
|
|
776
787
|
yPos: 0,
|
|
777
788
|
accidentalXOffset: r.accidentalOffset
|
|
778
789
|
};
|
|
@@ -807,7 +818,7 @@ class et {
|
|
|
807
818
|
this.noteEntries = [], this.svgRendererInstance.destroy();
|
|
808
819
|
}
|
|
809
820
|
}
|
|
810
|
-
const
|
|
821
|
+
const A = 30, z = 19, O = 12, _t = 4, Tt = 6, j = 1, b = 38, mt = [
|
|
811
822
|
"TIME_4",
|
|
812
823
|
"TIME_3",
|
|
813
824
|
"NOTE_HEAD_WHOLE",
|
|
@@ -819,7 +830,7 @@ const d = 30, H = 19, g = 12, K = 4, Q = 6, P = 1, m = 38, z = [
|
|
|
819
830
|
"REST_QUARTER",
|
|
820
831
|
"REST_EIGHTH"
|
|
821
832
|
];
|
|
822
|
-
class
|
|
833
|
+
class yt {
|
|
823
834
|
rendererInstance;
|
|
824
835
|
options;
|
|
825
836
|
barSpacing;
|
|
@@ -830,7 +841,7 @@ class st {
|
|
|
830
841
|
currentBeatCount = 0;
|
|
831
842
|
currentBeatUICount = 0;
|
|
832
843
|
currentBeatUIElement = null;
|
|
833
|
-
currentBeatUIXPos =
|
|
844
|
+
currentBeatUIXPos = b;
|
|
834
845
|
/**
|
|
835
846
|
* Creates an instance of a RhythmStaff, A single staff that will automatically apply positioning of elements based on the duration of a note.
|
|
836
847
|
*
|
|
@@ -850,13 +861,13 @@ class st {
|
|
|
850
861
|
staffBackgroundColor: "white",
|
|
851
862
|
currentBeatUIColor: "#24ff7450",
|
|
852
863
|
...t
|
|
853
|
-
}, this.rendererInstance = new
|
|
864
|
+
}, this.rendererInstance = new L(e, {
|
|
854
865
|
width: this.options.width,
|
|
855
866
|
height: 100,
|
|
856
867
|
scale: this.options.scale,
|
|
857
868
|
staffColor: this.options.staffColor,
|
|
858
869
|
staffBackgroundColor: this.options.staffBackgroundColor,
|
|
859
|
-
useGlyphs:
|
|
870
|
+
useGlyphs: mt
|
|
860
871
|
});
|
|
861
872
|
const s = this.rendererInstance.rootSvgElement;
|
|
862
873
|
let r = "TIME_4";
|
|
@@ -872,41 +883,41 @@ class st {
|
|
|
872
883
|
}
|
|
873
884
|
if (this.options.barsCount < 1 || this.options.barsCount > 3) throw new Error(`Bars count ${this.options.barsCount} not supported. Please use 1 - 3`);
|
|
874
885
|
if (this.options.spaceAbove) {
|
|
875
|
-
const
|
|
876
|
-
this.rendererInstance.addTotalRootSvgYOffset(
|
|
886
|
+
const u = this.options.spaceAbove * 10;
|
|
887
|
+
this.rendererInstance.addTotalRootSvgYOffset(u);
|
|
877
888
|
}
|
|
878
889
|
if (this.options.spaceBelow) {
|
|
879
|
-
let
|
|
880
|
-
this.rendererInstance.addTotalRootSvgHeight(
|
|
890
|
+
let u = this.options.spaceBelow * 10;
|
|
891
|
+
this.rendererInstance.addTotalRootSvgHeight(u);
|
|
881
892
|
}
|
|
882
|
-
const
|
|
883
|
-
this.rendererInstance.addTotalRootSvgHeight(
|
|
884
|
-
const
|
|
885
|
-
|
|
886
|
-
const
|
|
887
|
-
this.rendererInstance.drawGlyph(r,
|
|
888
|
-
let
|
|
889
|
-
this.options.barsCount > 1 && (
|
|
893
|
+
const o = this.rendererInstance.getLayerByName("staff");
|
|
894
|
+
this.rendererInstance.addTotalRootSvgHeight(A * 2);
|
|
895
|
+
const n = this.rendererInstance.createGroup("time-signature");
|
|
896
|
+
o.appendChild(n);
|
|
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;
|
|
890
901
|
let c = this.barSpacing + 38;
|
|
891
|
-
const l =
|
|
892
|
-
for (let
|
|
893
|
-
this.rendererInstance.drawLine(c, l, c,
|
|
894
|
-
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);
|
|
895
906
|
}
|
|
896
907
|
createBeatUIElement() {
|
|
897
908
|
const e = this.rendererInstance.getLayerByName("ui");
|
|
898
909
|
this.currentBeatUIElement = this.rendererInstance.drawRect(
|
|
899
910
|
this.quarterNoteSpacing / 2,
|
|
900
|
-
|
|
911
|
+
A * 2,
|
|
901
912
|
e,
|
|
902
913
|
{
|
|
903
|
-
x:
|
|
914
|
+
x: b,
|
|
904
915
|
fill: this.options.currentBeatUIColor
|
|
905
916
|
}
|
|
906
917
|
);
|
|
907
918
|
}
|
|
908
919
|
handleNewBar() {
|
|
909
|
-
this.noteCursorX +=
|
|
920
|
+
this.noteCursorX += O;
|
|
910
921
|
}
|
|
911
922
|
// Translates group, returns cursor increment amount
|
|
912
923
|
translateGroupByDuration(e, t) {
|
|
@@ -965,15 +976,15 @@ class st {
|
|
|
965
976
|
let s = e;
|
|
966
977
|
for (; s > 0; ) {
|
|
967
978
|
const r = this.rendererInstance.createGroup("rest");
|
|
968
|
-
let
|
|
969
|
-
s -
|
|
979
|
+
let o = 0;
|
|
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);
|
|
970
981
|
}
|
|
971
982
|
return t;
|
|
972
983
|
}
|
|
973
984
|
renderBeamRect(e, t, s, r) {
|
|
974
985
|
this.rendererInstance.drawRect(
|
|
975
986
|
e - t,
|
|
976
|
-
|
|
987
|
+
_t,
|
|
977
988
|
s,
|
|
978
989
|
{
|
|
979
990
|
x: 10,
|
|
@@ -1002,23 +1013,23 @@ class st {
|
|
|
1002
1013
|
*/
|
|
1003
1014
|
drawNote(e) {
|
|
1004
1015
|
const t = Array.isArray(e) ? e : [e], s = this.rendererInstance.getLayerByName("notes"), r = [];
|
|
1005
|
-
for (const
|
|
1006
|
-
let
|
|
1016
|
+
for (const o of t) {
|
|
1017
|
+
let n = "w";
|
|
1007
1018
|
try {
|
|
1008
|
-
|
|
1009
|
-
} catch (
|
|
1010
|
-
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;
|
|
1011
1022
|
}
|
|
1012
|
-
const
|
|
1023
|
+
const i = p[n];
|
|
1013
1024
|
if (this.currentBeatCount >= this.maxBeatCount)
|
|
1014
1025
|
throw r.length > 0 && this.rendererInstance.commitElementsToDOM(r, s), new Error("Max beat count reached. Can't add additional notes.");
|
|
1015
1026
|
this.checkAndCreateNewBar();
|
|
1016
|
-
const
|
|
1017
|
-
|
|
1018
|
-
r.push(
|
|
1027
|
+
const a = this.checkAndFillBarWithRests(i);
|
|
1028
|
+
a && a.forEach((d) => {
|
|
1029
|
+
r.push(d), this.noteEntries.push(d);
|
|
1019
1030
|
});
|
|
1020
|
-
const c = this.rendererInstance.createGroup("note"), l = this.translateGroupByDuration(
|
|
1021
|
-
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);
|
|
1022
1033
|
}
|
|
1023
1034
|
this.rendererInstance.commitElementsToDOM(r, s);
|
|
1024
1035
|
}
|
|
@@ -1042,21 +1053,21 @@ class st {
|
|
|
1042
1053
|
*/
|
|
1043
1054
|
drawRest(e) {
|
|
1044
1055
|
const t = Array.isArray(e) ? e : [e], s = this.rendererInstance.getLayerByName("notes"), r = [];
|
|
1045
|
-
for (const
|
|
1046
|
-
let
|
|
1056
|
+
for (const o of t) {
|
|
1057
|
+
let n = "w";
|
|
1047
1058
|
try {
|
|
1048
|
-
|
|
1049
|
-
} catch (
|
|
1050
|
-
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;
|
|
1051
1062
|
}
|
|
1052
|
-
const
|
|
1063
|
+
const i = this.rendererInstance.createGroup("rest"), a = p[n], c = a * this.quarterNoteSpacing;
|
|
1053
1064
|
if (this.currentBeatCount >= this.maxBeatCount)
|
|
1054
1065
|
throw r.length > 0 && this.rendererInstance.commitElementsToDOM(r, s), new Error("Max beat count reached. Can't add additional notes.");
|
|
1055
1066
|
this.checkAndCreateNewBar();
|
|
1056
|
-
const l = this.checkAndFillBarWithRests(
|
|
1057
|
-
l && l.forEach((
|
|
1058
|
-
r.push(
|
|
1059
|
-
}), this.renderRest(
|
|
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);
|
|
1060
1071
|
}
|
|
1061
1072
|
this.rendererInstance.commitElementsToDOM(r, s);
|
|
1062
1073
|
}
|
|
@@ -1083,13 +1094,13 @@ class st {
|
|
|
1083
1094
|
if (this.currentBeatCount >= this.maxBeatCount)
|
|
1084
1095
|
throw new Error("Max beat count reached. Can't add additional beamed note.");
|
|
1085
1096
|
let s = "s";
|
|
1086
|
-
e === "s" ? s = "s" : s =
|
|
1087
|
-
const r = this.rendererInstance.getLayerByName("notes"),
|
|
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");
|
|
1088
1099
|
c.setAttribute("transform", `translate(${this.noteCursorX}, 0)`);
|
|
1089
1100
|
let l = 0;
|
|
1090
|
-
for (let
|
|
1091
|
-
this.rendererInstance.drawGlyph("NOTE_HEAD_QUARTER", c, { xOffset: l }), this.drawStem(c, l), l +=
|
|
1092
|
-
this.renderBeamRect(l,
|
|
1101
|
+
for (let d = 0; d < a; d++)
|
|
1102
|
+
this.rendererInstance.drawGlyph("NOTE_HEAD_QUARTER", c, { xOffset: l }), this.drawStem(c, l), l += n, this.currentBeatCount += o;
|
|
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);
|
|
1093
1104
|
}
|
|
1094
1105
|
/**
|
|
1095
1106
|
* Will increment the UI showing the current beat in quarters. Once exceeded, must be reset with `resetCurrentBeatUI()`
|
|
@@ -1100,14 +1111,14 @@ class st {
|
|
|
1100
1111
|
this.currentBeatUIElement.setAttribute("display", "none");
|
|
1101
1112
|
return;
|
|
1102
1113
|
}
|
|
1103
|
-
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());
|
|
1104
1115
|
}
|
|
1105
1116
|
/**
|
|
1106
1117
|
* Resets the ui showing the current beat value.
|
|
1107
1118
|
* @returns void
|
|
1108
1119
|
*/
|
|
1109
1120
|
resetCurrentBeatUI() {
|
|
1110
|
-
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()));
|
|
1111
1122
|
}
|
|
1112
1123
|
/**
|
|
1113
1124
|
* Clears staff of notes and resets internal positioning.
|
|
@@ -1124,7 +1135,7 @@ class st {
|
|
|
1124
1135
|
this.noteEntries = [], this.rendererInstance.destroy();
|
|
1125
1136
|
}
|
|
1126
1137
|
}
|
|
1127
|
-
const
|
|
1138
|
+
const Ct = [
|
|
1128
1139
|
"CLEF_TREBLE",
|
|
1129
1140
|
"CLEF_BASS",
|
|
1130
1141
|
"CLEF_ALTO",
|
|
@@ -1138,8 +1149,8 @@ const J = [
|
|
|
1138
1149
|
"ACCIDENTAL_NATURAL",
|
|
1139
1150
|
"ACCIDENTAL_DOUBLE_SHARP",
|
|
1140
1151
|
"ACCIDENTAL_DOUBLE_FLAT"
|
|
1141
|
-
],
|
|
1142
|
-
class
|
|
1152
|
+
], N = 60, St = N;
|
|
1153
|
+
class wt {
|
|
1143
1154
|
svgRendererInstance;
|
|
1144
1155
|
strategyInstance;
|
|
1145
1156
|
noteRendererInstance;
|
|
@@ -1166,32 +1177,32 @@ class rt {
|
|
|
1166
1177
|
staffColor: "black",
|
|
1167
1178
|
staffBackgroundColor: "transparent",
|
|
1168
1179
|
...t
|
|
1169
|
-
}, this.svgRendererInstance = new
|
|
1180
|
+
}, this.svgRendererInstance = new L(e, {
|
|
1170
1181
|
width: this.options.width,
|
|
1171
1182
|
height: 100,
|
|
1172
1183
|
scale: this.options.scale,
|
|
1173
1184
|
staffColor: this.options.staffColor,
|
|
1174
1185
|
staffBackgroundColor: this.options.staffBackgroundColor,
|
|
1175
|
-
useGlyphs:
|
|
1186
|
+
useGlyphs: Ct
|
|
1176
1187
|
});
|
|
1177
1188
|
const s = this.svgRendererInstance.rootSvgElement;
|
|
1178
1189
|
switch (this.options.staffType) {
|
|
1179
1190
|
case "grand":
|
|
1180
|
-
this.strategyInstance = new
|
|
1191
|
+
this.strategyInstance = new tt(this.svgRendererInstance, "grand");
|
|
1181
1192
|
break;
|
|
1182
1193
|
case "bass":
|
|
1183
|
-
this.strategyInstance = new
|
|
1194
|
+
this.strategyInstance = new _(this.svgRendererInstance, "bass");
|
|
1184
1195
|
break;
|
|
1185
1196
|
case "treble":
|
|
1186
|
-
this.strategyInstance = new
|
|
1197
|
+
this.strategyInstance = new _(this.svgRendererInstance, "treble");
|
|
1187
1198
|
break;
|
|
1188
1199
|
case "alto":
|
|
1189
|
-
this.strategyInstance = new
|
|
1200
|
+
this.strategyInstance = new _(this.svgRendererInstance, "alto");
|
|
1190
1201
|
break;
|
|
1191
1202
|
default:
|
|
1192
1203
|
throw new Error(`The staff type ${this.options.staffType} is not supported. Please use "treble", "bass", "alto", or "grand".`);
|
|
1193
1204
|
}
|
|
1194
|
-
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) {
|
|
1195
1206
|
const r = this.options.spaceAbove * 10;
|
|
1196
1207
|
this.svgRendererInstance.addTotalRootSvgYOffset(r);
|
|
1197
1208
|
}
|
|
@@ -1202,10 +1213,10 @@ class rt {
|
|
|
1202
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);
|
|
1203
1214
|
}
|
|
1204
1215
|
renderFirstNoteGroups() {
|
|
1205
|
-
const e = this.options.width - this.options.noteStartX +
|
|
1216
|
+
const e = this.options.width - this.options.noteStartX + St;
|
|
1206
1217
|
for (; this.noteBuffer.length > 0 && this.noteCursorX < e; )
|
|
1207
|
-
this.renderNextNote(), this.noteCursorX +=
|
|
1208
|
-
this.activeEntries.length > 1 && (this.noteCursorX -=
|
|
1218
|
+
this.renderNextNote(), this.noteCursorX += N;
|
|
1219
|
+
this.activeEntries.length > 1 && (this.noteCursorX -= N);
|
|
1209
1220
|
}
|
|
1210
1221
|
renderNextNote() {
|
|
1211
1222
|
if (this.noteBuffer.length < 1) return;
|
|
@@ -1264,7 +1275,7 @@ class rt {
|
|
|
1264
1275
|
return;
|
|
1265
1276
|
}
|
|
1266
1277
|
this.activeEntries.forEach((t) => {
|
|
1267
|
-
t.xPos -=
|
|
1278
|
+
t.xPos -= N, t.noteWrapper.style.transform = `translate(${t.xPos}px, 0px)`;
|
|
1268
1279
|
});
|
|
1269
1280
|
const e = this.activeEntries[0];
|
|
1270
1281
|
e.xPos <= 0 && (this.notesLayer.removeChild(e.noteWrapper), this.activeEntries.shift()), this.renderNextNote();
|
|
@@ -1284,8 +1295,297 @@ class rt {
|
|
|
1284
1295
|
this.svgRendererInstance.destroy(), this.activeEntries = [], this.noteBuffer = [];
|
|
1285
1296
|
}
|
|
1286
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
|
+
}
|
|
1287
1586
|
export {
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1587
|
+
Ot as GuitarChord,
|
|
1588
|
+
Rt as MusicStaff,
|
|
1589
|
+
yt as RhythmStaff,
|
|
1590
|
+
wt as ScrollingStaff
|
|
1291
1591
|
};
|