@coderline/alphatab 1.8.0-alpha.1637 → 1.8.0-alpha.1639

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.
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * alphaTab v1.8.0-alpha.1637 (develop, build 1637)
2
+ * alphaTab v1.8.0-alpha.1639 (develop, build 1639)
3
3
  *
4
4
  * Copyright © 2025, Daniel Kuschny and Contributors, All rights reserved.
5
5
  *
@@ -203,9 +203,9 @@ class AlphaTabError extends Error {
203
203
  * @internal
204
204
  */
205
205
  class VersionInfo {
206
- static version = '1.8.0-alpha.1637';
207
- static date = '2025-12-07T02:26:27.382Z';
208
- static commit = 'aa2c8101456d3ddfc777491468bc483789cc6c12';
206
+ static version = '1.8.0-alpha.1639';
207
+ static date = '2025-12-09T02:16:01.440Z';
208
+ static commit = 'af86866e4f9d89a3ccc34006d01473a549dbbe7f';
209
209
  static print(print) {
210
210
  print(`alphaTab ${VersionInfo.version}`);
211
211
  print(`commit: ${VersionInfo.commit}`);
@@ -56664,12 +56664,12 @@ class GroupedEffectGlyph extends EffectGlyph {
56664
56664
  this.endPosition = endPosition;
56665
56665
  }
56666
56666
  get isLinkedWithPrevious() {
56667
- return !!this.previousGlyph && this.previousGlyph.renderer.staff.system === this.renderer.staff.system;
56667
+ return !!this.previousGlyph && this.previousGlyph.renderer.staff?.system === this.renderer.staff.system;
56668
56668
  }
56669
56669
  get isLinkedWithNext() {
56670
56670
  return (!!this.nextGlyph &&
56671
56671
  this.nextGlyph.renderer.isFinalized &&
56672
- this.nextGlyph.renderer.staff.system === this.renderer.staff.system);
56672
+ this.nextGlyph.renderer.staff?.system === this.renderer.staff.system);
56673
56673
  }
56674
56674
  paint(cx, cy, canvas) {
56675
56675
  // if we are linked with the previous, the first glyph of the group will also render this one.
@@ -59057,6 +59057,437 @@ class LeftToRightLayoutingGlyphGroup extends GlyphGroup {
59057
59057
  }
59058
59058
  }
59059
59059
 
59060
+ /**
59061
+ * @internal
59062
+ */
59063
+ class TieGlyph extends Glyph {
59064
+ tieDirection = BeamDirection.Up;
59065
+ slurEffectId;
59066
+ isForEnd;
59067
+ constructor(slurEffectId, forEnd) {
59068
+ super(0, 0);
59069
+ this.slurEffectId = slurEffectId;
59070
+ this.isForEnd = forEnd;
59071
+ }
59072
+ _startX = 0;
59073
+ _startY = 0;
59074
+ _endX = 0;
59075
+ _endY = 0;
59076
+ _tieHeight = 0;
59077
+ _boundingBox;
59078
+ _shouldPaint = false;
59079
+ get checkForOverflow() {
59080
+ return this._shouldPaint && this._boundingBox !== undefined;
59081
+ }
59082
+ getBoundingBoxTop() {
59083
+ if (this._boundingBox) {
59084
+ return this._boundingBox.y;
59085
+ }
59086
+ return this._startY;
59087
+ }
59088
+ getBoundingBoxBottom() {
59089
+ if (this._boundingBox) {
59090
+ return this._boundingBox.y + this._boundingBox.h;
59091
+ }
59092
+ return this._startY;
59093
+ }
59094
+ doLayout() {
59095
+ this.width = 0;
59096
+ const startNoteRenderer = this.lookupStartBeatRenderer();
59097
+ const endNoteRenderer = this.lookupEndBeatRenderer();
59098
+ this._startX = 0;
59099
+ this._endX = 0;
59100
+ this._startY = 0;
59101
+ this._endY = 0;
59102
+ this.height = 0;
59103
+ // if we are on the tie start, we check if we
59104
+ // either can draw till the end note, or we just can draw till the bar end
59105
+ this.tieDirection = this.calculateTieDirection();
59106
+ const forEnd = this.isForEnd;
59107
+ this._shouldPaint = false;
59108
+ if (!forEnd) {
59109
+ if (startNoteRenderer !== endNoteRenderer) {
59110
+ this._startX = this.calculateStartX();
59111
+ this._startY = this.calculateStartY();
59112
+ if (!endNoteRenderer || startNoteRenderer.staff !== endNoteRenderer.staff) {
59113
+ const lastRendererInStaff = startNoteRenderer.staff.barRenderers[startNoteRenderer.staff.barRenderers.length - 1];
59114
+ this._endX = lastRendererInStaff.x + lastRendererInStaff.width;
59115
+ this._endY = this._startY;
59116
+ startNoteRenderer.scoreRenderer.layout.slurRegistry.startMultiSystemSlur(this);
59117
+ }
59118
+ else {
59119
+ this._endX = this.calculateEndX();
59120
+ this._endY = this.caclculateEndY();
59121
+ }
59122
+ }
59123
+ else {
59124
+ this._shouldPaint = true;
59125
+ this._startX = this.calculateStartX();
59126
+ this._endX = this.calculateEndX();
59127
+ this._startY = this.calculateStartY();
59128
+ this._endY = this.caclculateEndY();
59129
+ }
59130
+ this._shouldPaint = true;
59131
+ }
59132
+ else if (startNoteRenderer.staff !== endNoteRenderer.staff) {
59133
+ const firstRendererInStaff = startNoteRenderer.staff.barRenderers[0];
59134
+ this._startX = firstRendererInStaff.x;
59135
+ this._endX = this.calculateEndX();
59136
+ const startGlyph = startNoteRenderer.scoreRenderer.layout.slurRegistry.completeMultiSystemSlur(this);
59137
+ if (startGlyph) {
59138
+ this._startY = startGlyph.calculateMultiSystemSlurY(endNoteRenderer);
59139
+ }
59140
+ else {
59141
+ this._startY = this.caclculateEndY();
59142
+ }
59143
+ this._endY = this.caclculateEndY();
59144
+ this._shouldPaint = startNoteRenderer.staff !== endNoteRenderer.staff;
59145
+ }
59146
+ this._boundingBox = undefined;
59147
+ this.y = Math.min(this._startY, this._endY);
59148
+ if (this.shouldDrawBendSlur()) {
59149
+ this._tieHeight = 0; // TODO: Bend slur height to be considered?
59150
+ }
59151
+ else {
59152
+ this._tieHeight = this.getTieHeight(this._startX, this._startY, this._endX, this._endY);
59153
+ const tieBoundingBox = TieGlyph.calculateActualTieHeight(1, this._startX, this._startY, this._endX, this._endY, this.tieDirection === BeamDirection.Down, this._tieHeight, this.renderer.smuflMetrics.tieMidpointThickness);
59154
+ this._boundingBox = tieBoundingBox;
59155
+ this.height = tieBoundingBox.h;
59156
+ if (this.tieDirection === BeamDirection.Up) {
59157
+ // the tie might go above `this.y` due to its shape
59158
+ // here we calculate how much this is so we can consider the
59159
+ // respective overflow
59160
+ const overlap = this.y - tieBoundingBox.y;
59161
+ if (overlap > 0) {
59162
+ this.y -= overlap;
59163
+ }
59164
+ }
59165
+ }
59166
+ }
59167
+ paint(cx, cy, canvas) {
59168
+ if (!this._shouldPaint) {
59169
+ return;
59170
+ }
59171
+ if (this.shouldDrawBendSlur()) {
59172
+ TieGlyph.drawBendSlur(canvas, cx + this._startX, cy + this._startY, cx + this._endX, cy + this._endY, this.tieDirection === BeamDirection.Down, 1, this.renderer.smuflMetrics.tieHeight);
59173
+ }
59174
+ else {
59175
+ TieGlyph.paintTie(canvas, 1, cx + this._startX, cy + this._startY, cx + this._endX, cy + this._endY, this.tieDirection === BeamDirection.Down, this._tieHeight, this.renderer.smuflMetrics.tieMidpointThickness);
59176
+ }
59177
+ }
59178
+ getTieHeight(_startX, _startY, _endX, _endY) {
59179
+ return this.renderer.smuflMetrics.tieHeight;
59180
+ }
59181
+ calculateMultiSystemSlurY(renderer) {
59182
+ const startRenderer = this.lookupStartBeatRenderer();
59183
+ const startY = this.calculateStartY();
59184
+ const relY = startY - startRenderer.y;
59185
+ return renderer.y + relY;
59186
+ }
59187
+ shouldCreateMultiSystemSlur(renderer) {
59188
+ const endStaff = this.lookupEndBeatRenderer()?.staff;
59189
+ if (!endStaff) {
59190
+ return true;
59191
+ }
59192
+ return renderer.staff.system.index < endStaff.system.index;
59193
+ }
59194
+ static calculateActualTieHeight(scale, x1, y1, x2, y2, down, offset, size) {
59195
+ const cp = TieGlyph._computeBezierControlPoints(scale, x1, y1, x2, y2, down, offset, size);
59196
+ if (cp.length === 0) {
59197
+ return new Bounds(x1, y1, x2 - x1, y2 - y1);
59198
+ }
59199
+ // For a musical tie/slur, the extrema occur predictably near the midpoint
59200
+ // Evaluate at midpoint (t=0.5) and check endpoints
59201
+ const p0x = cp[0];
59202
+ const p0y = cp[1];
59203
+ const c1x = cp[2];
59204
+ const c1y = cp[3];
59205
+ const c2x = cp[4];
59206
+ const c2y = cp[5];
59207
+ const p1x = cp[6];
59208
+ const p1y = cp[7];
59209
+ // Evaluate at t=0.5 for midpoint
59210
+ const midX = 0.125 * p0x + 0.375 * c1x + 0.375 * c2x + 0.125 * p1x;
59211
+ const midY = 0.125 * p0y + 0.375 * c1y + 0.375 * c2y + 0.125 * p1y;
59212
+ // Bounds are simply min/max of start, end, and midpoint
59213
+ const xMin = Math.min(p0x, p1x, midX);
59214
+ const xMax = Math.max(p0x, p1x, midX);
59215
+ let yMin = Math.min(p0y, p1y, midY);
59216
+ let yMax = Math.max(p0y, p1y, midY);
59217
+ // Account for thickness of the tie/slur
59218
+ if (down) {
59219
+ yMax += size;
59220
+ }
59221
+ else {
59222
+ yMin -= size;
59223
+ }
59224
+ const b = new Bounds();
59225
+ b.x = xMin;
59226
+ b.y = yMin;
59227
+ b.w = xMax - xMin;
59228
+ b.h = yMax - yMin;
59229
+ return b;
59230
+ }
59231
+ static _computeBezierControlPoints(scale, x1, y1, x2, y2, down, offset, size) {
59232
+ if (x1 === x2 && y1 === y2) {
59233
+ return [];
59234
+ }
59235
+ // ensure endX > startX
59236
+ if (x2 < x1) {
59237
+ let t = x1;
59238
+ x1 = x2;
59239
+ x2 = t;
59240
+ t = y1;
59241
+ y1 = y2;
59242
+ y2 = t;
59243
+ }
59244
+ //
59245
+ // calculate control points
59246
+ //
59247
+ offset *= scale;
59248
+ size *= scale;
59249
+ if (down) {
59250
+ offset *= -1;
59251
+ size *= -1;
59252
+ }
59253
+ if (scale >= 1) {
59254
+ size *= 1.2;
59255
+ }
59256
+ // calculate control points on horizontal axis then rotate:
59257
+ /*
59258
+ cp1x/cpy1 cp2x/cpy2
59259
+ *----------------*
59260
+ / \
59261
+ / \
59262
+ x1/y1 * * x2/y2
59263
+
59264
+ cp3 and cp4 are simply with lower height
59265
+ */
59266
+ const dY = y2 - y1;
59267
+ const dX = x2 - x1;
59268
+ const length = Math.sqrt(dX * dX + dY * dY);
59269
+ let cp1x = x1 + length * 0.25;
59270
+ let cp1y = y1 - offset;
59271
+ let cp2x = x1 + length * 0.75;
59272
+ let cp2y = y1 - offset;
59273
+ let cp3x = x1 + length * 0.75;
59274
+ let cp3y = y1 - offset - size;
59275
+ let cp4x = x1 + length * 0.25;
59276
+ let cp4y = y1 - offset - size;
59277
+ const angle = Math.atan2(dY, dX);
59278
+ [cp1x, cp1y] = TieGlyph._rotate(cp1x, cp1y, x1, y1, angle);
59279
+ [cp2x, cp2y] = TieGlyph._rotate(cp2x, cp2y, x1, y1, angle);
59280
+ [cp3x, cp3y] = TieGlyph._rotate(cp3x, cp3y, x1, y1, angle);
59281
+ [cp4x, cp4y] = TieGlyph._rotate(cp4x, cp4y, x1, y1, angle);
59282
+ return [x1, y1, cp1x, cp1y, cp2x, cp2y, x2, y2, cp3x, cp3y, cp4x, cp4y, x1, y1];
59283
+ }
59284
+ static _rotate(x, y, rotateX, rotateY, angle) {
59285
+ const dx = x - rotateX;
59286
+ const dy = y - rotateY;
59287
+ const rx = dx * Math.cos(angle) - dy * Math.sin(angle);
59288
+ const ry = dx * Math.sin(angle) + dy * Math.cos(angle);
59289
+ return [rotateX + rx, rotateY + ry];
59290
+ }
59291
+ static paintTie(canvas, scale, x1, y1, x2, y2, down /*= false*/, offset /*= 22*/, size /*= 4*/) {
59292
+ const cps = TieGlyph._computeBezierControlPoints(scale, x1, y1, x2, y2, down, offset, size);
59293
+ canvas.beginPath();
59294
+ canvas.moveTo(cps[0], cps[1]);
59295
+ canvas.bezierCurveTo(cps[2], cps[3], cps[4], cps[5], cps[6], cps[7]);
59296
+ canvas.bezierCurveTo(cps[8], cps[9], cps[10], cps[11], cps[12], cps[13]);
59297
+ canvas.closePath();
59298
+ canvas.fill();
59299
+ }
59300
+ static calculateBendSlurTopY(x1, y1, x2, y2, down, scale, bendSlurHeight) {
59301
+ let normalVectorX = y2 - y1;
59302
+ let normalVectorY = x2 - x1;
59303
+ const length = Math.sqrt(normalVectorX * normalVectorX + normalVectorY * normalVectorY);
59304
+ if (down) {
59305
+ normalVectorX *= -1;
59306
+ }
59307
+ else {
59308
+ normalVectorY *= -1;
59309
+ }
59310
+ // make to unit vector
59311
+ normalVectorX /= length;
59312
+ normalVectorY /= length;
59313
+ let offset = bendSlurHeight * scale;
59314
+ if (x2 - x1 < 20) {
59315
+ offset /= 2;
59316
+ }
59317
+ const centerY = (y2 + y1) / 2;
59318
+ const cp1Y = centerY + offset * normalVectorY;
59319
+ return cp1Y;
59320
+ }
59321
+ static drawBendSlur(canvas, x1, y1, x2, y2, down, scale, bendSlurHeight, slurText) {
59322
+ let normalVectorX = y2 - y1;
59323
+ let normalVectorY = x2 - x1;
59324
+ const length = Math.sqrt(normalVectorX * normalVectorX + normalVectorY * normalVectorY);
59325
+ if (down) {
59326
+ normalVectorX *= -1;
59327
+ }
59328
+ else {
59329
+ normalVectorY *= -1;
59330
+ }
59331
+ // make to unit vector
59332
+ normalVectorX /= length;
59333
+ normalVectorY /= length;
59334
+ // center of connection
59335
+ // TODO: should be 1/3
59336
+ const centerX = (x2 + x1) / 2;
59337
+ const centerY = (y2 + y1) / 2;
59338
+ let offset = bendSlurHeight * scale;
59339
+ if (x2 - x1 < 20) {
59340
+ offset /= 2;
59341
+ }
59342
+ const cp1X = centerX + offset * normalVectorX;
59343
+ const cp1Y = centerY + offset * normalVectorY;
59344
+ canvas.beginPath();
59345
+ canvas.moveTo(x1, y1);
59346
+ canvas.lineTo(cp1X, cp1Y);
59347
+ canvas.lineTo(x2, y2);
59348
+ canvas.stroke();
59349
+ if (slurText) {
59350
+ const w = canvas.measureText(slurText).width;
59351
+ const textOffset = down ? 0 : -canvas.font.size;
59352
+ canvas.fillText(slurText, cp1X - w / 2, cp1Y + textOffset);
59353
+ }
59354
+ }
59355
+ }
59356
+ /**
59357
+ * A common tie implementation using note details for positioning
59358
+ * @internal
59359
+ */
59360
+ class NoteTieGlyph extends TieGlyph {
59361
+ startNote;
59362
+ endNote;
59363
+ startNoteRenderer = null;
59364
+ endNoteRenderer = null;
59365
+ constructor(slurEffectId, startNote, endNote, forEnd) {
59366
+ super(slurEffectId, forEnd);
59367
+ this.startNote = startNote;
59368
+ this.endNote = endNote;
59369
+ }
59370
+ get isLeftHandTap() {
59371
+ return this.startNote === this.endNote;
59372
+ }
59373
+ getTieHeight(startX, startY, endX, endY) {
59374
+ if (this.isLeftHandTap) {
59375
+ return this.renderer.smuflMetrics.tieHeight;
59376
+ }
59377
+ return super.getTieHeight(startX, startY, endX, endY);
59378
+ }
59379
+ calculateTieDirection() {
59380
+ // invert direction (if stems go up, ties go down to not cross them)
59381
+ switch (this.lookupStartBeatRenderer().getBeatDirection(this.startNote.beat)) {
59382
+ case BeamDirection.Up:
59383
+ return BeamDirection.Down;
59384
+ default:
59385
+ return BeamDirection.Up;
59386
+ }
59387
+ }
59388
+ calculateStartX() {
59389
+ const startNoteRenderer = this.lookupStartBeatRenderer();
59390
+ if (this.isLeftHandTap) {
59391
+ return this.calculateEndX() - startNoteRenderer.smuflMetrics.leftHandTabTieWidth;
59392
+ }
59393
+ return startNoteRenderer.x + startNoteRenderer.getNoteX(this.startNote, this.getStartNotePosition());
59394
+ }
59395
+ getStartNotePosition() {
59396
+ return NoteXPosition.Center;
59397
+ }
59398
+ calculateStartY() {
59399
+ const startNoteRenderer = this.lookupStartBeatRenderer();
59400
+ if (this.isLeftHandTap) {
59401
+ return startNoteRenderer.y + startNoteRenderer.getNoteY(this.startNote, NoteYPosition.Center);
59402
+ }
59403
+ switch (this.tieDirection) {
59404
+ case BeamDirection.Up:
59405
+ return startNoteRenderer.y + startNoteRenderer.getNoteY(this.startNote, NoteYPosition.Top);
59406
+ default:
59407
+ return startNoteRenderer.y + startNoteRenderer.getNoteY(this.startNote, NoteYPosition.Bottom);
59408
+ }
59409
+ }
59410
+ calculateEndX() {
59411
+ const endNoteRenderer = this.lookupEndBeatRenderer();
59412
+ if (!endNoteRenderer) {
59413
+ return this.calculateStartY() + this.renderer.smuflMetrics.leftHandTabTieWidth;
59414
+ }
59415
+ if (this.isLeftHandTap) {
59416
+ return endNoteRenderer.x + endNoteRenderer.getNoteX(this.endNote, NoteXPosition.Left);
59417
+ }
59418
+ return endNoteRenderer.x + endNoteRenderer.getNoteX(this.endNote, NoteXPosition.Center);
59419
+ }
59420
+ getEndNotePosition() {
59421
+ return NoteXPosition.Center;
59422
+ }
59423
+ caclculateEndY() {
59424
+ const endNoteRenderer = this.lookupEndBeatRenderer();
59425
+ if (!endNoteRenderer) {
59426
+ return this.calculateStartY();
59427
+ }
59428
+ if (this.isLeftHandTap) {
59429
+ return endNoteRenderer.y + endNoteRenderer.getNoteY(this.endNote, NoteYPosition.Center);
59430
+ }
59431
+ switch (this.tieDirection) {
59432
+ case BeamDirection.Up:
59433
+ return endNoteRenderer.y + endNoteRenderer.getNoteY(this.endNote, NoteYPosition.Top);
59434
+ default:
59435
+ return endNoteRenderer.y + endNoteRenderer.getNoteY(this.endNote, NoteYPosition.Bottom);
59436
+ }
59437
+ }
59438
+ lookupEndBeatRenderer() {
59439
+ if (!this.endNoteRenderer) {
59440
+ this.endNoteRenderer = this.renderer.scoreRenderer.layout.getRendererForBar(this.renderer.staff.staffId, this.endNote.beat.voice.bar);
59441
+ }
59442
+ return this.endNoteRenderer;
59443
+ }
59444
+ lookupStartBeatRenderer() {
59445
+ if (!this.startNoteRenderer) {
59446
+ this.startNoteRenderer = this.renderer.scoreRenderer.layout.getRendererForBar(this.renderer.staff.staffId, this.startNote.beat.voice.bar);
59447
+ }
59448
+ return this.startNoteRenderer;
59449
+ }
59450
+ shouldDrawBendSlur() {
59451
+ return false;
59452
+ }
59453
+ }
59454
+ /**
59455
+ * A tie glyph for continued multi-system ties/slurs
59456
+ * @internal
59457
+ */
59458
+ class ContinuationTieGlyph extends TieGlyph {
59459
+ _startTie;
59460
+ constructor(startTie) {
59461
+ super(startTie.slurEffectId, false);
59462
+ this._startTie = startTie;
59463
+ }
59464
+ lookupStartBeatRenderer() {
59465
+ return this.renderer;
59466
+ }
59467
+ lookupEndBeatRenderer() {
59468
+ return this.renderer;
59469
+ }
59470
+ shouldDrawBendSlur() {
59471
+ return false;
59472
+ }
59473
+ calculateTieDirection() {
59474
+ return this._startTie.tieDirection;
59475
+ }
59476
+ calculateStartY() {
59477
+ return this._startTie.calculateMultiSystemSlurY(this.renderer);
59478
+ }
59479
+ caclculateEndY() {
59480
+ return this.calculateStartY();
59481
+ }
59482
+ calculateStartX() {
59483
+ return this.renderer.staff.barRenderers[0].x;
59484
+ }
59485
+ calculateEndX() {
59486
+ const last = this.renderer.staff.barRenderers[this.renderer.staff.barRenderers.length - 1];
59487
+ return last.x + last.width;
59488
+ }
59489
+ }
59490
+
59060
59491
  /**
59061
59492
  * This glyph acts as container for handling
59062
59493
  * multiple voice rendering
@@ -59527,6 +59958,63 @@ class TuningGlyph extends GlyphGroup {
59527
59958
  }
59528
59959
  }
59529
59960
 
59961
+ /**
59962
+ * This registry keeps track of which slurs and ties were started and needs completion.
59963
+ * Slurs might span multiple systems, and in such cases we need to create additional
59964
+ * slur/ties in the intermediate and end system.
59965
+ *
59966
+ * @internal
59967
+ *
59968
+ */
59969
+ class SlurRegistry {
59970
+ _staffLookup = new Map();
59971
+ clear() {
59972
+ this._staffLookup.clear();
59973
+ }
59974
+ startMultiSystemSlur(startGlyph) {
59975
+ const staffId = SlurRegistry._staffId(startGlyph.renderer.staff);
59976
+ let container;
59977
+ if (!this._staffLookup.has(staffId)) {
59978
+ container = {
59979
+ startedSlurs: new Map()
59980
+ };
59981
+ this._staffLookup.set(staffId, container);
59982
+ }
59983
+ else {
59984
+ container = this._staffLookup.get(staffId);
59985
+ }
59986
+ container.startedSlurs.set(startGlyph.slurEffectId, { startGlyph });
59987
+ }
59988
+ static _staffId(staff) {
59989
+ return `${staff.modelStaff.index}.${staff.modelStaff.track.index}.${staff.staffId}`;
59990
+ }
59991
+ completeMultiSystemSlur(endGlyph) {
59992
+ const staffId = SlurRegistry._staffId(endGlyph.renderer.staff);
59993
+ if (!this._staffLookup.has(staffId)) {
59994
+ return undefined;
59995
+ }
59996
+ const container = this._staffLookup.get(staffId);
59997
+ if (container.startedSlurs.has(endGlyph.slurEffectId)) {
59998
+ const info = container.startedSlurs.get(endGlyph.slurEffectId);
59999
+ info.endGlyph = endGlyph;
60000
+ return info.startGlyph;
60001
+ }
60002
+ return undefined;
60003
+ }
60004
+ *getAllContinuations(renderer) {
60005
+ const staffId = SlurRegistry._staffId(renderer.staff);
60006
+ if (!this._staffLookup.has(staffId) || renderer.index > 0) {
60007
+ return;
60008
+ }
60009
+ const container = this._staffLookup.get(staffId);
60010
+ for (const g of container.startedSlurs.values()) {
60011
+ if (g.startGlyph.shouldCreateMultiSystemSlur(renderer)) {
60012
+ yield g.startGlyph;
60013
+ }
60014
+ }
60015
+ }
60016
+ }
60017
+
59530
60018
  /**
59531
60019
  * A Staff represents a single line within a StaffSystem.
59532
60020
  * It stores BarRenderer instances created from a given factory.
@@ -59648,7 +60136,6 @@ class RenderStaff {
59648
60136
  this._sharedLayoutData = new Map();
59649
60137
  const lastBar = this.barRenderers[this.barRenderers.length - 1];
59650
60138
  this.barRenderers.splice(this.barRenderers.length - 1, 1);
59651
- this.system.layout.unregisterBarRenderer(this.staffId, lastBar);
59652
60139
  this.topOverflow = 0;
59653
60140
  this.bottomOverflow = 0;
59654
60141
  for (const r of this.barRenderers) {
@@ -59741,23 +60228,23 @@ class RenderStaff {
59741
60228
  // changes in the overflows
59742
60229
  let needsSecondPass = false;
59743
60230
  let topOverflow = this.topOverflow;
59744
- for (let i = 0; i < this.barRenderers.length; i++) {
59745
- this.barRenderers[i].y = this.topPadding + topOverflow;
59746
- if (this.barRenderers[i].finalizeRenderer()) {
60231
+ for (const renderer of this.barRenderers) {
60232
+ renderer.registerMultiSystemSlurs(this.system.layout.slurRegistry.getAllContinuations(renderer));
60233
+ if (renderer.finalizeRenderer()) {
59747
60234
  needsSecondPass = true;
59748
60235
  }
59749
- this.height = Math.max(this.height, this.barRenderers[i].height);
60236
+ this.height = Math.max(this.height, renderer.height);
59750
60237
  }
59751
60238
  // 2nd pass: move renderers to correct position respecting the new overflows
59752
60239
  if (needsSecondPass) {
59753
60240
  topOverflow = this.topOverflow;
59754
60241
  // shift all the renderers to the new position to match required spacing
59755
- for (let i = 0; i < this.barRenderers.length; i++) {
59756
- this.barRenderers[i].y = this.topPadding + topOverflow;
60242
+ for (const renderer of this.barRenderers) {
60243
+ renderer.y = this.topPadding + topOverflow;
59757
60244
  }
59758
60245
  // finalize again (to align ties)
59759
- for (let i = 0; i < this.barRenderers.length; i++) {
59760
- this.barRenderers[i].finalizeRenderer();
60246
+ for (const renderer of this.barRenderers) {
60247
+ renderer.finalizeRenderer();
59761
60248
  }
59762
60249
  }
59763
60250
  if (this.height > 0) {
@@ -60363,6 +60850,7 @@ class StaffSystem {
60363
60850
  if (newBarDisplayScale > barDisplayScale) {
60364
60851
  barDisplayScale = newBarDisplayScale;
60365
60852
  }
60853
+ lastBar.afterReverted();
60366
60854
  }
60367
60855
  this.width -= width;
60368
60856
  this.computedWidth -= width;
@@ -60882,12 +61370,16 @@ class ScoreLayout {
60882
61370
  constructor(renderer) {
60883
61371
  this.renderer = renderer;
60884
61372
  }
61373
+ slurRegistry = new SlurRegistry();
60885
61374
  resize() {
60886
61375
  this._lazyPartials.clear();
61376
+ this.slurRegistry.clear();
60887
61377
  this.doResize();
60888
61378
  }
60889
61379
  layoutAndRender() {
60890
61380
  this._lazyPartials.clear();
61381
+ this.slurRegistry.clear();
61382
+ this._barRendererLookup.clear();
60891
61383
  this.profile = Environment.staveProfiles.get(this.renderer.settings.display.staveProfile);
60892
61384
  const score = this.renderer.score;
60893
61385
  this.firstBarIndex = ModelUtils.computeFirstDisplayedBarIndex(score, this.renderer.settings);
@@ -61156,17 +61648,6 @@ class ScoreLayout {
61156
61648
  }
61157
61649
  }
61158
61650
  }
61159
- unregisterBarRenderer(key, renderer) {
61160
- if (this._barRendererLookup.has(key)) {
61161
- const lookup = this._barRendererLookup.get(key);
61162
- lookup.delete(renderer.bar.id);
61163
- if (renderer.additionalMultiRestBars) {
61164
- for (const b of renderer.additionalMultiRestBars) {
61165
- lookup.delete(b.id);
61166
- }
61167
- }
61168
- }
61169
- }
61170
61651
  getRendererForBar(key, bar) {
61171
61652
  const barRendererId = bar.id;
61172
61653
  if (this._barRendererLookup.has(key) && this._barRendererLookup.get(key).has(barRendererId)) {
@@ -62155,6 +62636,7 @@ class BarRendererBase {
62155
62636
  _voiceContainers = new Map();
62156
62637
  _postBeatGlyphs = new LeftToRightLayoutingGlyphGroup();
62157
62638
  _ties = [];
62639
+ _multiSystemSlurs;
62158
62640
  topEffects;
62159
62641
  bottomEffects;
62160
62642
  get nextRenderer() {
@@ -62307,6 +62789,11 @@ class BarRendererBase {
62307
62789
  }
62308
62790
  }
62309
62791
  _appliedLayoutingInfo = 0;
62792
+ afterReverted() {
62793
+ this.staff = undefined;
62794
+ this.registerMultiSystemSlurs(undefined);
62795
+ this.isFinalized = false;
62796
+ }
62310
62797
  afterStaffBarReverted() {
62311
62798
  this.topEffects.afterStaffBarReverted();
62312
62799
  this.bottomEffects.afterStaffBarReverted();
@@ -62352,13 +62839,26 @@ class BarRendererBase {
62352
62839
  return true;
62353
62840
  }
62354
62841
  isFinalized = false;
62355
- finalizeRenderer() {
62356
- this.isFinalized = true;
62842
+ registerMultiSystemSlurs(startedTies) {
62843
+ if (!startedTies) {
62844
+ this._multiSystemSlurs = undefined;
62845
+ return;
62846
+ }
62847
+ let ties = undefined;
62848
+ for (const g of startedTies) {
62849
+ const continuation = new ContinuationTieGlyph(g);
62850
+ continuation.renderer = this;
62851
+ continuation.tieDirection = g.tieDirection;
62852
+ if (!ties) {
62853
+ ties = [];
62854
+ }
62855
+ ties.push(continuation);
62856
+ }
62857
+ this._multiSystemSlurs = ties;
62858
+ }
62859
+ _finalizeTies(ties, barTop, barBottom) {
62357
62860
  let didChangeOverflows = false;
62358
- // allow spacing to be used for tie overflows
62359
- const barTop = this.y;
62360
- const barBottom = this.y + this.height;
62361
- for (const t of this._ties) {
62861
+ for (const t of ties) {
62362
62862
  const tie = t;
62363
62863
  tie.doLayout();
62364
62864
  if (t.checkForOverflow) {
@@ -62379,6 +62879,21 @@ class BarRendererBase {
62379
62879
  }
62380
62880
  }
62381
62881
  }
62882
+ return didChangeOverflows;
62883
+ }
62884
+ finalizeRenderer() {
62885
+ this.isFinalized = true;
62886
+ let didChangeOverflows = false;
62887
+ // allow spacing to be used for tie overflows
62888
+ const barTop = this.y;
62889
+ const barBottom = this.y + this.height;
62890
+ if (this._finalizeTies(this._ties, barTop, barBottom)) {
62891
+ didChangeOverflows = true;
62892
+ }
62893
+ const multiSystemSlurs = this._multiSystemSlurs;
62894
+ if (multiSystemSlurs && this._finalizeTies(multiSystemSlurs, barTop, barBottom)) {
62895
+ didChangeOverflows = true;
62896
+ }
62382
62897
  const topHeightChanged = this.topEffects.finalizeEffects();
62383
62898
  const bottomHeightChanged = this.bottomEffects.finalizeEffects();
62384
62899
  if (topHeightChanged || bottomHeightChanged) {
@@ -62559,6 +63074,16 @@ class BarRendererBase {
62559
63074
  }
62560
63075
  canvas.color = this.resources.mainGlyphColor;
62561
63076
  this._postBeatGlyphs.paint(cx + this.x, cy + this.y, canvas);
63077
+ this._paintMultiSystemSlurs(cx, cy, canvas);
63078
+ }
63079
+ _paintMultiSystemSlurs(cx, cy, canvas) {
63080
+ const multiSystemSlurs = this._multiSystemSlurs;
63081
+ if (!multiSystemSlurs) {
63082
+ return;
63083
+ }
63084
+ for (const slur of multiSystemSlurs) {
63085
+ slur.paint(cx, cy, canvas);
63086
+ }
62562
63087
  }
62563
63088
  paintBackground(cx, cy, canvas) {
62564
63089
  this.layoutingInfo.paint(cx + this.x + this._preBeatGlyphs.x + this._preBeatGlyphs.width, cy + this.y + this.height, canvas);
@@ -64637,6 +65162,13 @@ class PageViewLayout extends ScoreLayout {
64637
65162
  }
64638
65163
  }
64639
65164
  else {
65165
+ // clear out staves during re-layout, this info is outdated during
65166
+ // re-layout of the bars
65167
+ for (const r of this._allMasterBarRenderers) {
65168
+ for (const b of r.renderers) {
65169
+ b.afterReverted();
65170
+ }
65171
+ }
64640
65172
  this._systems = [];
64641
65173
  let currentIndex = 0;
64642
65174
  const maxWidth = this._maxWidth;
@@ -65098,7 +65630,7 @@ class BarLineGlyph extends LeftToRightLayoutingGlyphGroup {
65098
65630
  // as during layout things are still moving
65099
65631
  let actualLineHeight = this.height;
65100
65632
  const thisStaff = renderer.staff;
65101
- const allStaves = renderer.staff.system.allStaves;
65633
+ const allStaves = thisStaff.system.allStaves;
65102
65634
  let isExtended = false;
65103
65635
  if (this._extendToNextStaff && thisStaff.index < allStaves.length - 1) {
65104
65636
  const nextStaff = allStaves[thisStaff.index + 1];
@@ -65280,7 +65812,7 @@ class LineBarRenderer extends BarRendererBase {
65280
65812
  }
65281
65813
  // during system fitting it can happen that we have fraction widths
65282
65814
  // but to have lines until the full end-pixel we round up.
65283
- // this way we avoid holes,
65815
+ // this way we avoid holes,
65284
65816
  const lineWidth = this.width;
65285
65817
  // we want the lines to be exactly virtually aligned with the respective Y-position
65286
65818
  // for note heads to align correctly
@@ -66046,375 +66578,23 @@ class LineBarRenderer extends BarRendererBase {
66046
66578
  /**
66047
66579
  * @internal
66048
66580
  */
66049
- class TieGlyph extends Glyph {
66050
- startBeat;
66051
- endBeat;
66052
- yOffset = 0;
66053
- forEnd;
66054
- startNoteRenderer = null;
66055
- endNoteRenderer = null;
66056
- tieDirection = BeamDirection.Up;
66057
- constructor(startBeat, endBeat, forEnd) {
66058
- super(0, 0);
66059
- this.startBeat = startBeat;
66060
- this.endBeat = endBeat;
66061
- this.forEnd = forEnd;
66062
- }
66063
- _startX = 0;
66064
- _startY = 0;
66065
- _endX = 0;
66066
- _endY = 0;
66067
- _tieHeight = 0;
66068
- _shouldDraw = false;
66069
- _boundingBox;
66070
- get checkForOverflow() {
66071
- return this._boundingBox !== undefined;
66072
- }
66073
- getBoundingBoxTop() {
66074
- if (this._boundingBox) {
66075
- return this._boundingBox.y;
66076
- }
66077
- return this._startY;
66078
- }
66079
- getBoundingBoxBottom() {
66080
- if (this._boundingBox) {
66081
- return this._boundingBox.y + this._boundingBox.h;
66082
- }
66083
- return this._startY;
66084
- }
66085
- doLayout() {
66086
- this.width = 0;
66087
- // TODO fix nullability of start/end beat,
66088
- if (!this.endBeat) {
66089
- this._shouldDraw = false;
66090
- return;
66091
- }
66092
- const startNoteRenderer = this.renderer.scoreRenderer.layout.getRendererForBar(this.renderer.staff.staffId, this.startBeat.voice.bar);
66093
- this.startNoteRenderer = startNoteRenderer;
66094
- const endNoteRenderer = this.renderer.scoreRenderer.layout.getRendererForBar(this.renderer.staff.staffId, this.endBeat.voice.bar);
66095
- this.endNoteRenderer = endNoteRenderer;
66096
- this._startX = 0;
66097
- this._endX = 0;
66098
- this._startY = 0;
66099
- this._endY = 0;
66100
- this.height = 0;
66101
- this._shouldDraw = false;
66102
- // if we are on the tie start, we check if we
66103
- // either can draw till the end note, or we just can draw till the bar end
66104
- this.tieDirection = !startNoteRenderer
66105
- ? this.getBeamDirection(this.endBeat, endNoteRenderer)
66106
- : this.getBeamDirection(this.startBeat, startNoteRenderer);
66107
- if (!this.forEnd && startNoteRenderer) {
66108
- // line break or bar break
66109
- if (startNoteRenderer !== endNoteRenderer) {
66110
- this._startX = startNoteRenderer.x + this.getStartX();
66111
- this._startY = startNoteRenderer.y + this.getStartY() + this.yOffset;
66112
- // line break: to bar end
66113
- if (!endNoteRenderer || startNoteRenderer.staff !== endNoteRenderer.staff) {
66114
- this._endX = startNoteRenderer.x + startNoteRenderer.width;
66115
- this._endY = this._startY;
66116
- }
66117
- else {
66118
- this._endX = endNoteRenderer.x + this.getEndX();
66119
- this._endY = endNoteRenderer.y + this.getEndY() + this.yOffset;
66120
- }
66121
- }
66122
- else {
66123
- this._startX = startNoteRenderer.x + this.getStartX();
66124
- this._endX = endNoteRenderer.x + this.getEndX();
66125
- this._startY = startNoteRenderer.y + this.getStartY() + this.yOffset;
66126
- this._endY = endNoteRenderer.y + this.getEndY() + this.yOffset;
66127
- }
66128
- this._shouldDraw = true;
66129
- }
66130
- else if (!startNoteRenderer || startNoteRenderer.staff !== endNoteRenderer.staff) {
66131
- this._startX = endNoteRenderer.x;
66132
- this._endX = endNoteRenderer.x + this.getEndX();
66133
- this._startY = endNoteRenderer.y + this.getEndY() + this.yOffset;
66134
- this._endY = this._startY;
66135
- this._shouldDraw = true;
66136
- }
66137
- this._boundingBox = undefined;
66138
- if (this._shouldDraw) {
66139
- this.y = Math.min(this._startY, this._endY);
66140
- if (this.shouldDrawBendSlur()) {
66141
- this._tieHeight = 0; // TODO: Bend slur height to be considered?
66142
- }
66143
- else {
66144
- this._tieHeight = this.getTieHeight(this._startX, this._startY, this._endX, this._endY);
66145
- const tieBoundingBox = TieGlyph.calculateActualTieHeight(1, this._startX, this._startY, this._endX, this._endY, this.tieDirection === BeamDirection.Down, this._tieHeight, this.renderer.smuflMetrics.tieMidpointThickness);
66146
- this._boundingBox = tieBoundingBox;
66147
- this.height = tieBoundingBox.h;
66148
- if (this.tieDirection === BeamDirection.Up) {
66149
- // the tie might go above `this.y` due to its shape
66150
- // here we calculate how much this is so we can consider the
66151
- // respective overflow
66152
- const overlap = this.y - tieBoundingBox.y;
66153
- if (overlap > 0) {
66154
- this.y -= overlap;
66155
- }
66156
- }
66157
- }
66158
- }
66159
- }
66160
- paint(cx, cy, canvas) {
66161
- if (this._shouldDraw) {
66162
- if (this.shouldDrawBendSlur()) {
66163
- TieGlyph.drawBendSlur(canvas, cx + this._startX, cy + this._startY, cx + this._endX, cy + this._endY, this.tieDirection === BeamDirection.Down, 1, this.renderer.smuflMetrics.tieHeight);
66164
- }
66165
- else {
66166
- TieGlyph.paintTie(canvas, 1, cx + this._startX, cy + this._startY, cx + this._endX, cy + this._endY, this.tieDirection === BeamDirection.Down, this._tieHeight, this.renderer.smuflMetrics.tieMidpointThickness);
66167
- }
66168
- }
66169
- }
66170
- shouldDrawBendSlur() {
66171
- return false;
66172
- }
66173
- getTieHeight(_startX, _startY, _endX, _endY) {
66174
- return this.renderer.smuflMetrics.tieHeight;
66175
- }
66176
- getBeamDirection(_beat, _noteRenderer) {
66177
- return BeamDirection.Down;
66178
- }
66179
- getStartY() {
66180
- return 0;
66181
- }
66182
- getEndY() {
66183
- return 0;
66184
- }
66185
- getStartX() {
66186
- return 0;
66187
- }
66188
- getEndX() {
66189
- return 0;
66190
- }
66191
- static calculateActualTieHeight(scale, x1, y1, x2, y2, down, offset, size) {
66192
- const cp = TieGlyph._computeBezierControlPoints(scale, x1, y1, x2, y2, down, offset, size);
66193
- // For a musical tie/slur, the extrema occur predictably near the midpoint
66194
- // Evaluate at midpoint (t=0.5) and check endpoints
66195
- const p0x = cp[0];
66196
- const p0y = cp[1];
66197
- const c1x = cp[2];
66198
- const c1y = cp[3];
66199
- const c2x = cp[4];
66200
- const c2y = cp[5];
66201
- const p1x = cp[6];
66202
- const p1y = cp[7];
66203
- // Evaluate at t=0.5 for midpoint
66204
- const midX = 0.125 * p0x + 0.375 * c1x + 0.375 * c2x + 0.125 * p1x;
66205
- const midY = 0.125 * p0y + 0.375 * c1y + 0.375 * c2y + 0.125 * p1y;
66206
- // Bounds are simply min/max of start, end, and midpoint
66207
- const xMin = Math.min(p0x, p1x, midX);
66208
- const xMax = Math.max(p0x, p1x, midX);
66209
- let yMin = Math.min(p0y, p1y, midY);
66210
- let yMax = Math.max(p0y, p1y, midY);
66211
- // Account for thickness of the tie/slur
66212
- if (down) {
66213
- yMax += size;
66214
- }
66215
- else {
66216
- yMin -= size;
66217
- }
66218
- const b = new Bounds();
66219
- b.x = xMin;
66220
- b.y = yMin;
66221
- b.w = xMax - xMin;
66222
- b.h = yMax - yMin;
66223
- return b;
66224
- }
66225
- static _computeBezierControlPoints(scale, x1, y1, x2, y2, down, offset, size) {
66226
- if (x1 === x2 && y1 === y2) {
66227
- return [];
66228
- }
66229
- // ensure endX > startX
66230
- if (x2 < x1) {
66231
- let t = x1;
66232
- x1 = x2;
66233
- x2 = t;
66234
- t = y1;
66235
- y1 = y2;
66236
- y2 = t;
66237
- }
66238
- //
66239
- // calculate control points
66240
- //
66241
- offset *= scale;
66242
- size *= scale;
66243
- if (down) {
66244
- offset *= -1;
66245
- size *= -1;
66246
- }
66247
- if (scale >= 1) {
66248
- size *= 1.2;
66249
- }
66250
- // calculate control points on horizontal axis then rotate:
66251
- /*
66252
- cp1x/cpy1 cp2x/cpy2
66253
- *----------------*
66254
- / \
66255
- / \
66256
- x1/y1 * * x2/y2
66257
-
66258
- cp3 and cp4 are simply with lower height
66259
- */
66260
- const dY = y2 - y1;
66261
- const dX = x2 - x1;
66262
- const length = Math.sqrt(dX * dX + dY * dY);
66263
- let cp1x = x1 + length * 0.25;
66264
- let cp1y = y1 - offset;
66265
- let cp2x = x1 + length * 0.75;
66266
- let cp2y = y1 - offset;
66267
- let cp3x = x1 + length * 0.75;
66268
- let cp3y = y1 - offset - size;
66269
- let cp4x = x1 + length * 0.25;
66270
- let cp4y = y1 - offset - size;
66271
- const angle = Math.atan2(dY, dX);
66272
- [cp1x, cp1y] = TieGlyph._rotate(cp1x, cp1y, x1, y1, angle);
66273
- [cp2x, cp2y] = TieGlyph._rotate(cp2x, cp2y, x1, y1, angle);
66274
- [cp3x, cp3y] = TieGlyph._rotate(cp3x, cp3y, x1, y1, angle);
66275
- [cp4x, cp4y] = TieGlyph._rotate(cp4x, cp4y, x1, y1, angle);
66276
- return [x1, y1, cp1x, cp1y, cp2x, cp2y, x2, y2, cp3x, cp3y, cp4x, cp4y, x1, y1];
66277
- }
66278
- static _rotate(x, y, rotateX, rotateY, angle) {
66279
- const dx = x - rotateX;
66280
- const dy = y - rotateY;
66281
- const rx = dx * Math.cos(angle) - dy * Math.sin(angle);
66282
- const ry = dx * Math.sin(angle) + dy * Math.cos(angle);
66283
- return [rotateX + rx, rotateY + ry];
66284
- }
66285
- static paintTie(canvas, scale, x1, y1, x2, y2, down /*= false*/, offset /*= 22*/, size /*= 4*/) {
66286
- const cps = TieGlyph._computeBezierControlPoints(scale, x1, y1, x2, y2, down, offset, size);
66287
- canvas.beginPath();
66288
- canvas.moveTo(cps[0], cps[1]);
66289
- canvas.bezierCurveTo(cps[2], cps[3], cps[4], cps[5], cps[6], cps[7]);
66290
- canvas.bezierCurveTo(cps[8], cps[9], cps[10], cps[11], cps[12], cps[13]);
66291
- canvas.closePath();
66292
- canvas.fill();
66293
- }
66294
- static calculateBendSlurTopY(x1, y1, x2, y2, down, scale, bendSlurHeight) {
66295
- let normalVectorX = y2 - y1;
66296
- let normalVectorY = x2 - x1;
66297
- const length = Math.sqrt(normalVectorX * normalVectorX + normalVectorY * normalVectorY);
66298
- if (down) {
66299
- normalVectorX *= -1;
66300
- }
66301
- else {
66302
- normalVectorY *= -1;
66303
- }
66304
- // make to unit vector
66305
- normalVectorX /= length;
66306
- normalVectorY /= length;
66307
- let offset = bendSlurHeight * scale;
66308
- if (x2 - x1 < 20) {
66309
- offset /= 2;
66310
- }
66311
- const centerY = (y2 + y1) / 2;
66312
- const cp1Y = centerY + offset * normalVectorY;
66313
- return cp1Y;
66314
- }
66315
- static drawBendSlur(canvas, x1, y1, x2, y2, down, scale, bendSlurHeight, slurText) {
66316
- let normalVectorX = y2 - y1;
66317
- let normalVectorY = x2 - x1;
66318
- const length = Math.sqrt(normalVectorX * normalVectorX + normalVectorY * normalVectorY);
66319
- if (down) {
66320
- normalVectorX *= -1;
66321
- }
66322
- else {
66323
- normalVectorY *= -1;
66324
- }
66325
- // make to unit vector
66326
- normalVectorX /= length;
66327
- normalVectorY /= length;
66328
- // center of connection
66329
- // TODO: should be 1/3
66330
- const centerX = (x2 + x1) / 2;
66331
- const centerY = (y2 + y1) / 2;
66332
- let offset = bendSlurHeight * scale;
66333
- if (x2 - x1 < 20) {
66334
- offset /= 2;
66335
- }
66336
- const cp1X = centerX + offset * normalVectorX;
66337
- const cp1Y = centerY + offset * normalVectorY;
66338
- canvas.beginPath();
66339
- canvas.moveTo(x1, y1);
66340
- canvas.lineTo(cp1X, cp1Y);
66341
- canvas.lineTo(x2, y2);
66342
- canvas.stroke();
66343
- if (slurText) {
66344
- const w = canvas.measureText(slurText).width;
66345
- const textOffset = down ? 0 : -canvas.font.size;
66346
- canvas.fillText(slurText, cp1X - w / 2, cp1Y + textOffset);
66347
- }
66348
- }
66349
- }
66350
-
66351
- /**
66352
- * @internal
66353
- */
66354
- class NumberedTieGlyph extends TieGlyph {
66355
- startNote;
66356
- endNote;
66357
- constructor(startNote, endNote, forEnd = false) {
66358
- super(!startNote ? null : startNote.beat, !endNote ? null : endNote.beat, forEnd);
66359
- this.startNote = startNote;
66360
- this.endNote = endNote;
66361
- }
66362
- get _isLeftHandTap() {
66363
- return this.startNote === this.endNote;
66364
- }
66581
+ class NumberedTieGlyph extends NoteTieGlyph {
66365
66582
  shouldDrawBendSlur() {
66366
66583
  return (this.renderer.settings.notation.extendBendArrowsOnTiedNotes &&
66367
66584
  !!this.startNote.bendOrigin &&
66368
66585
  this.startNote.isTieOrigin);
66369
66586
  }
66370
- doLayout() {
66371
- super.doLayout();
66372
- }
66373
- getBeamDirection(_beat, _noteRenderer) {
66587
+ calculateTieDirection() {
66374
66588
  return BeamDirection.Up;
66375
66589
  }
66376
- getStartY() {
66377
- return this.startNoteRenderer.getNoteY(this.startNote, NoteYPosition.Top);
66378
- }
66379
- getEndY() {
66380
- return this.getStartY();
66381
- }
66382
- getStartX() {
66383
- if (this._isLeftHandTap) {
66384
- return this.getEndX() - this.startNoteRenderer.smuflMetrics.leftHandTabTieWidth;
66385
- }
66386
- return this.startNoteRenderer.getNoteX(this.startNote, NoteXPosition.Center);
66387
- }
66388
- getEndX() {
66389
- if (this._isLeftHandTap) {
66390
- return this.endNoteRenderer.getNoteX(this.endNote, NoteXPosition.Left);
66391
- }
66392
- return this.endNoteRenderer.getNoteX(this.endNote, NoteXPosition.Center);
66393
- }
66394
66590
  }
66395
66591
 
66396
66592
  /**
66397
66593
  * @internal
66398
66594
  */
66399
- class TabTieGlyph extends TieGlyph {
66400
- startNote;
66401
- endNote;
66402
- constructor(startNote, endNote, forEnd = false) {
66403
- super(startNote.beat, endNote.beat, forEnd);
66404
- this.startNote = startNote;
66405
- this.endNote = endNote;
66406
- }
66407
- get _isLeftHandTap() {
66408
- return this.startNote === this.endNote;
66409
- }
66410
- getTieHeight(startX, startY, endX, endY) {
66411
- if (this._isLeftHandTap) {
66412
- return this.startNoteRenderer.smuflMetrics.tieHeight;
66413
- }
66414
- return super.getTieHeight(startX, startY, endX, endY);
66415
- }
66416
- getBeamDirection(_beat, _noteRenderer) {
66417
- if (this._isLeftHandTap) {
66595
+ class TabTieGlyph extends NoteTieGlyph {
66596
+ calculateTieDirection() {
66597
+ if (this.isLeftHandTap) {
66418
66598
  return BeamDirection.Up;
66419
66599
  }
66420
66600
  return TabTieGlyph.getBeamDirectionForNote(this.startNote);
@@ -66422,54 +66602,25 @@ class TabTieGlyph extends TieGlyph {
66422
66602
  static getBeamDirectionForNote(note) {
66423
66603
  return note.string > 3 ? BeamDirection.Up : BeamDirection.Down;
66424
66604
  }
66425
- getStartY() {
66426
- if (this._isLeftHandTap) {
66427
- return this.startNoteRenderer.getNoteY(this.startNote, NoteYPosition.Center);
66428
- }
66429
- if (this.tieDirection === BeamDirection.Up) {
66430
- return this.startNoteRenderer.getNoteY(this.startNote, NoteYPosition.Top);
66431
- }
66432
- return this.startNoteRenderer.getNoteY(this.startNote, NoteYPosition.Bottom);
66433
- }
66434
- getEndY() {
66435
- return this.getStartY();
66436
- }
66437
- getStartX() {
66438
- if (this._isLeftHandTap) {
66439
- return this.getEndX() - this.renderer.smuflMetrics.leftHandTabTieWidth;
66440
- }
66441
- return this.startNoteRenderer.getNoteX(this.startNote, NoteXPosition.Center);
66442
- }
66443
- getEndX() {
66444
- if (this._isLeftHandTap) {
66445
- return this.endNoteRenderer.getNoteX(this.endNote, NoteXPosition.Left);
66446
- }
66447
- return this.endNoteRenderer.getNoteX(this.endNote, NoteXPosition.Center);
66448
- }
66449
66605
  }
66450
66606
 
66451
66607
  /**
66452
66608
  * @internal
66453
66609
  */
66454
- class NumberedSlurGlyph extends TabTieGlyph {
66455
- _direction;
66610
+ class TabSlurGlyph extends TabTieGlyph {
66456
66611
  _forSlide;
66457
- constructor(startNote, endNote, forSlide, forEnd = false) {
66458
- super(startNote, endNote, forEnd);
66459
- this._direction = BeamDirection.Up;
66612
+ constructor(slurEffectId, startNote, endNote, forSlide, forEnd) {
66613
+ super(slurEffectId, startNote, endNote, forEnd);
66460
66614
  this._forSlide = forSlide;
66461
66615
  }
66462
66616
  getTieHeight(startX, _startY, endX, _endY) {
66463
- return Math.log(endX - startX + 1) * this.renderer.settings.notation.slurHeight / 2;
66617
+ return (Math.log(endX - startX + 1) * this.renderer.settings.notation.slurHeight) / 2;
66464
66618
  }
66465
66619
  tryExpand(startNote, endNote, forSlide, forEnd) {
66466
66620
  // same type required
66467
66621
  if (this._forSlide !== forSlide) {
66468
66622
  return false;
66469
66623
  }
66470
- if (this.forEnd !== forEnd) {
66471
- return false;
66472
- }
66473
66624
  // same start and endbeat
66474
66625
  if (this.startNote.beat.id !== startNote.beat.id) {
66475
66626
  return false;
@@ -66477,41 +66628,43 @@ class NumberedSlurGlyph extends TabTieGlyph {
66477
66628
  if (this.endNote.beat.id !== endNote.beat.id) {
66478
66629
  return false;
66479
66630
  }
66631
+ const isForEnd = this.renderer === this.lookupEndBeatRenderer();
66632
+ if (isForEnd !== forEnd) {
66633
+ return false;
66634
+ }
66635
+ // same draw direction
66636
+ if (this.tieDirection !== TabTieGlyph.getBeamDirectionForNote(startNote)) {
66637
+ return false;
66638
+ }
66480
66639
  // if we can expand, expand in correct direction
66481
- switch (this._direction) {
66640
+ switch (this.tieDirection) {
66482
66641
  case BeamDirection.Up:
66483
66642
  if (startNote.realValue > this.startNote.realValue) {
66484
66643
  this.startNote = startNote;
66485
- this.startBeat = startNote.beat;
66486
66644
  }
66487
66645
  if (endNote.realValue > this.endNote.realValue) {
66488
66646
  this.endNote = endNote;
66489
- this.endBeat = endNote.beat;
66490
66647
  }
66491
66648
  break;
66492
66649
  case BeamDirection.Down:
66493
66650
  if (startNote.realValue < this.startNote.realValue) {
66494
66651
  this.startNote = startNote;
66495
- this.startBeat = startNote.beat;
66496
66652
  }
66497
66653
  if (endNote.realValue < this.endNote.realValue) {
66498
66654
  this.endNote = endNote;
66499
- this.endBeat = endNote.beat;
66500
66655
  }
66501
66656
  break;
66502
66657
  }
66503
66658
  return true;
66504
66659
  }
66505
- paint(cx, cy, canvas) {
66506
- const startNoteRenderer = this.renderer.scoreRenderer.layout.getRendererForBar(this.renderer.staff.staffId, this.startBeat.voice.bar);
66507
- const direction = this.getBeamDirection(this.startBeat, startNoteRenderer);
66508
- const slurId = `numbered.slur.${this.startNote.beat.id}.${this.endNote.beat.id}.${direction}`;
66509
- const renderer = this.renderer;
66510
- const isSlurRendered = renderer.staff.getSharedLayoutData(slurId, false);
66511
- if (!isSlurRendered) {
66512
- renderer.staff.setSharedLayoutData(slurId, true);
66513
- super.paint(cx, cy, canvas);
66514
- }
66660
+ }
66661
+
66662
+ /**
66663
+ * @internal
66664
+ */
66665
+ class NumberedSlurGlyph extends TabSlurGlyph {
66666
+ calculateTieDirection() {
66667
+ return BeamDirection.Up;
66515
66668
  }
66516
66669
  }
66517
66670
 
@@ -66519,23 +66672,31 @@ class NumberedSlurGlyph extends TabTieGlyph {
66519
66672
  * @internal
66520
66673
  */
66521
66674
  class NumberedBeatContainerGlyph extends BeatContainerGlyph {
66675
+ _slurs = new Map();
66522
66676
  _effectSlurs = [];
66677
+ doLayout() {
66678
+ this._slurs.clear();
66679
+ this._effectSlurs = [];
66680
+ super.doLayout();
66681
+ }
66523
66682
  createTies(n) {
66524
66683
  // create a tie if any effect requires it
66525
66684
  if (!n.isVisible) {
66526
66685
  return;
66527
66686
  }
66528
- if (n.isTieOrigin && n.tieDestination.isVisible) {
66529
- const tie = new NumberedTieGlyph(n, n.tieDestination, false);
66687
+ if (n.isTieOrigin && n.tieDestination.isVisible && !this._slurs.has('numbered.tie')) {
66688
+ const tie = new NumberedTieGlyph(`numbered.tie.${n.beat.id}`, n, n.tieDestination, false);
66530
66689
  this.addTie(tie);
66690
+ this._slurs.set(tie.slurEffectId, tie);
66531
66691
  }
66532
66692
  if (n.isTieDestination) {
66533
- const tie = new NumberedTieGlyph(n.tieOrigin, n, true);
66693
+ const tie = new NumberedTieGlyph(`numbered.tie.${n.tieOrigin.beat.id}`, n.tieOrigin, n, true);
66534
66694
  this.addTie(tie);
66535
66695
  }
66536
- if (n.isLeftHandTapped && !n.isHammerPullDestination) {
66537
- const tapSlur = new NumberedTieGlyph(n, n, false);
66696
+ if (n.isLeftHandTapped && !n.isHammerPullDestination && !this._slurs.has(`numbered.tie.leftHandTap.${n.beat.id}`)) {
66697
+ const tapSlur = new NumberedTieGlyph(`numbered.tie.leftHandTap.${n.beat.id}`, n, n, false);
66538
66698
  this.addTie(tapSlur);
66699
+ this._slurs.set(tapSlur.slurEffectId, tapSlur);
66539
66700
  }
66540
66701
  // start effect slur on first beat
66541
66702
  if (n.isEffectSlurOrigin && n.effectSlurDestination) {
@@ -66547,9 +66708,11 @@ class NumberedBeatContainerGlyph extends BeatContainerGlyph {
66547
66708
  }
66548
66709
  }
66549
66710
  if (!expanded) {
66550
- const effectSlur = new NumberedSlurGlyph(n, n.effectSlurDestination, false, false);
66711
+ const effectSlur = new NumberedSlurGlyph(`numbered.slur.effect`, n, n.effectSlurDestination, false, false);
66551
66712
  this._effectSlurs.push(effectSlur);
66552
66713
  this.addTie(effectSlur);
66714
+ this._slurs.set(effectSlur.slurEffectId, effectSlur);
66715
+ this._slurs.set('numbered.slur.effect', effectSlur);
66553
66716
  }
66554
66717
  }
66555
66718
  // end effect slur on last beat
@@ -66562,9 +66725,11 @@ class NumberedBeatContainerGlyph extends BeatContainerGlyph {
66562
66725
  }
66563
66726
  }
66564
66727
  if (!expanded) {
66565
- const effectSlur = new NumberedSlurGlyph(n.effectSlurOrigin, n, false, true);
66728
+ const effectSlur = new NumberedSlurGlyph(`numbered.slur.effect`, n.effectSlurOrigin, n, false, true);
66566
66729
  this._effectSlurs.push(effectSlur);
66567
66730
  this.addTie(effectSlur);
66731
+ this._slurs.set(effectSlur.slurEffectId, effectSlur);
66732
+ this._slurs.set('numbered.slur.effect', effectSlur);
66568
66733
  }
66569
66734
  }
66570
66735
  }
@@ -66957,10 +67122,11 @@ class NumberedBeatGlyph extends BeatOnNoteGlyphBase {
66957
67122
  if (sr.shortestDuration < this.container.beat.duration) {
66958
67123
  sr.shortestDuration = this.container.beat.duration;
66959
67124
  }
66960
- const glyphY = sr.getLineY(sr.getNoteLine());
66961
67125
  if (!this.container.beat.isEmpty) {
67126
+ const glyphY = sr.getLineY(0);
66962
67127
  let numberWithinOctave = '0';
66963
67128
  if (this.container.beat.notes.length > 0) {
67129
+ const note = this.container.beat.notes[0];
66964
67130
  const kst = this.renderer.bar.keySignatureType;
66965
67131
  const ks = this.renderer.bar.keySignature;
66966
67132
  const ksi = ks + 7;
@@ -66968,7 +67134,6 @@ class NumberedBeatGlyph extends BeatOnNoteGlyphBase {
66968
67134
  ? NumberedBeatGlyph.minorKeySignatureOneValues
66969
67135
  : NumberedBeatGlyph.majorKeySignatureOneValues;
66970
67136
  const oneNoteValue = oneNoteValues[ksi];
66971
- const note = this.container.beat.notes[0];
66972
67137
  if (note.isDead) {
66973
67138
  numberWithinOctave = 'X';
66974
67139
  }
@@ -67015,7 +67180,7 @@ class NumberedBeatGlyph extends BeatOnNoteGlyphBase {
67015
67180
  // Note dots
67016
67181
  if (this.container.beat.dots > 0 && this.container.beat.duration >= Duration.Quarter) {
67017
67182
  for (let i = 0; i < this.container.beat.dots; i++) {
67018
- const dot = new AugmentationDotGlyph(0, sr.getLineY(0));
67183
+ const dot = new AugmentationDotGlyph(0, glyphY);
67019
67184
  dot.renderer = this.renderer;
67020
67185
  this.addEffect(dot);
67021
67186
  }
@@ -67043,7 +67208,7 @@ class NumberedBeatGlyph extends BeatOnNoteGlyphBase {
67043
67208
  numberOfQuarterNotes += numberOfAddedQuarters;
67044
67209
  }
67045
67210
  for (let i = 0; i < numberOfQuarterNotes - 1; i++) {
67046
- const dash = new NumberedDashGlyph(0, sr.getLineY(0), this.container.beat);
67211
+ const dash = new NumberedDashGlyph(0, glyphY, this.container.beat);
67047
67212
  dash.renderer = this.renderer;
67048
67213
  this.addNormal(dash);
67049
67214
  }
@@ -67513,7 +67678,7 @@ class NumberedBarRenderer extends LineBarRenderer {
67513
67678
  }
67514
67679
  }
67515
67680
  }
67516
- getNoteLine() {
67681
+ getNoteLine(_note) {
67517
67682
  return 0;
67518
67683
  }
67519
67684
  get tupletOffset() {
@@ -69728,87 +69893,128 @@ class ScoreBendGlyph extends ScoreHelperNotesBaseGlyph {
69728
69893
  * @internal
69729
69894
  */
69730
69895
  class ScoreLegatoGlyph extends TieGlyph {
69731
- constructor(startBeat, endBeat, forEnd = false) {
69732
- super(startBeat, endBeat, forEnd);
69896
+ startBeat;
69897
+ endBeat;
69898
+ startBeatRenderer = null;
69899
+ endBeatRenderer = null;
69900
+ constructor(slurEffectId, startBeat, endBeat, forEnd) {
69901
+ super(slurEffectId, forEnd);
69902
+ this.startBeat = startBeat;
69903
+ this.endBeat = endBeat;
69733
69904
  }
69734
69905
  doLayout() {
69735
69906
  super.doLayout();
69736
69907
  }
69737
- getBeamDirection(beat, noteRenderer) {
69738
- if (beat.isRest) {
69908
+ lookupStartBeatRenderer() {
69909
+ if (!this.startBeatRenderer) {
69910
+ this.startBeatRenderer = this.renderer.scoreRenderer.layout.getRendererForBar(this.renderer.staff.staffId, this.startBeat.voice.bar);
69911
+ }
69912
+ return this.startBeatRenderer;
69913
+ }
69914
+ lookupEndBeatRenderer() {
69915
+ if (!this.endBeatRenderer) {
69916
+ this.endBeatRenderer = this.renderer.scoreRenderer.layout.getRendererForBar(this.renderer.staff.staffId, this.endBeat.voice.bar);
69917
+ }
69918
+ return this.endBeatRenderer;
69919
+ }
69920
+ shouldDrawBendSlur() {
69921
+ return false;
69922
+ }
69923
+ calculateTieDirection() {
69924
+ if (this.startBeat.isRest) {
69739
69925
  return BeamDirection.Up;
69740
69926
  }
69741
69927
  // invert direction (if stems go up, ties go down to not cross them)
69742
- switch (noteRenderer.getBeatDirection(beat)) {
69928
+ switch (this.lookupStartBeatRenderer().getBeatDirection(this.startBeat)) {
69743
69929
  case BeamDirection.Up:
69744
69930
  return BeamDirection.Down;
69745
69931
  default:
69746
69932
  return BeamDirection.Up;
69747
69933
  }
69748
69934
  }
69749
- getStartY() {
69935
+ calculateStartX() {
69936
+ const startBeatRenderer = this.lookupStartBeatRenderer();
69937
+ return startBeatRenderer.x + startBeatRenderer.getBeatX(this.startBeat, BeatXPosition.MiddleNotes);
69938
+ }
69939
+ calculateStartY() {
69940
+ const startBeatRenderer = this.lookupStartBeatRenderer();
69750
69941
  if (this.startBeat.isRest) {
69751
- // below all lines
69752
- return this.startNoteRenderer.getScoreY(9);
69942
+ switch (this.tieDirection) {
69943
+ case BeamDirection.Up:
69944
+ return (startBeatRenderer.y +
69945
+ startBeatRenderer.getBeatContainer(this.startBeat).onNotes.getBoundingBoxTop());
69946
+ default:
69947
+ return (startBeatRenderer.y +
69948
+ startBeatRenderer.getBeatContainer(this.startBeat).onNotes.getBoundingBoxBottom());
69949
+ }
69753
69950
  }
69754
69951
  switch (this.tieDirection) {
69755
69952
  case BeamDirection.Up:
69756
69953
  // below lowest note
69757
- return this.startNoteRenderer.getNoteY(this.startBeat.maxNote, NoteYPosition.Top);
69954
+ return startBeatRenderer.y + startBeatRenderer.getNoteY(this.startBeat.maxNote, NoteYPosition.Top);
69758
69955
  default:
69759
- return this.startNoteRenderer.getNoteY(this.startBeat.minNote, NoteYPosition.Bottom);
69956
+ return startBeatRenderer.y + startBeatRenderer.getNoteY(this.startBeat.minNote, NoteYPosition.Bottom);
69957
+ }
69958
+ }
69959
+ calculateEndX() {
69960
+ const endBeatRenderer = this.lookupEndBeatRenderer();
69961
+ if (!endBeatRenderer) {
69962
+ return this.calculateStartX() + this.renderer.smuflMetrics.leftHandTabTieWidth;
69760
69963
  }
69964
+ const endBeamDirection = endBeatRenderer.getBeatDirection(this.endBeat);
69965
+ return (endBeatRenderer.x +
69966
+ endBeatRenderer.getBeatX(this.endBeat, this.endBeat.duration > Duration.Whole && endBeamDirection === this.tieDirection
69967
+ ? BeatXPosition.Stem
69968
+ : BeatXPosition.MiddleNotes));
69761
69969
  }
69762
- getEndY() {
69763
- const endNoteScoreRenderer = this.endNoteRenderer;
69970
+ caclculateEndY() {
69971
+ const endBeatRenderer = this.lookupEndBeatRenderer();
69972
+ if (!endBeatRenderer) {
69973
+ return this.calculateStartY();
69974
+ }
69764
69975
  if (this.endBeat.isRest) {
69765
69976
  switch (this.tieDirection) {
69766
69977
  case BeamDirection.Up:
69767
- return endNoteScoreRenderer.getScoreY(9);
69978
+ return (endBeatRenderer.y + endBeatRenderer.getBeatContainer(this.endBeat).onNotes.getBoundingBoxTop());
69768
69979
  default:
69769
- return endNoteScoreRenderer.getScoreY(0);
69980
+ return (endBeatRenderer.y +
69981
+ endBeatRenderer.getBeatContainer(this.endBeat).onNotes.getBoundingBoxBottom());
69770
69982
  }
69771
69983
  }
69772
- const startBeamDirection = this.startNoteRenderer.getBeatDirection(this.startBeat);
69773
- const endBeamDirection = endNoteScoreRenderer.getBeatDirection(this.endBeat);
69984
+ const startBeamDirection = this.lookupStartBeatRenderer().getBeatDirection(this.startBeat);
69985
+ const endBeamDirection = endBeatRenderer.getBeatDirection(this.endBeat);
69774
69986
  if (startBeamDirection !== endBeamDirection && this.startBeat.graceType === GraceType.None) {
69775
69987
  if (endBeamDirection === this.tieDirection) {
69776
69988
  switch (this.tieDirection) {
69777
69989
  case BeamDirection.Up:
69778
69990
  // stem upper end
69779
- return endNoteScoreRenderer.getNoteY(this.endBeat.maxNote, NoteYPosition.TopWithStem);
69991
+ return (endBeatRenderer.y +
69992
+ endBeatRenderer.getNoteY(this.endBeat.maxNote, NoteYPosition.TopWithStem));
69780
69993
  default:
69781
69994
  // stem lower end
69782
- return endNoteScoreRenderer.getNoteY(this.endBeat.minNote, NoteYPosition.BottomWithStem);
69995
+ return (endBeatRenderer.y +
69996
+ endBeatRenderer.getNoteY(this.endBeat.minNote, NoteYPosition.BottomWithStem));
69783
69997
  }
69784
69998
  }
69785
69999
  switch (this.tieDirection) {
69786
70000
  case BeamDirection.Up:
69787
70001
  // stem upper end
69788
- return endNoteScoreRenderer.getNoteY(this.endBeat.maxNote, NoteYPosition.BottomWithStem);
70002
+ return (endBeatRenderer.y +
70003
+ endBeatRenderer.getNoteY(this.endBeat.maxNote, NoteYPosition.BottomWithStem));
69789
70004
  default:
69790
70005
  // stem lower end
69791
- return endNoteScoreRenderer.getNoteY(this.endBeat.minNote, NoteYPosition.TopWithStem);
70006
+ return (endBeatRenderer.y + endBeatRenderer.getNoteY(this.endBeat.minNote, NoteYPosition.TopWithStem));
69792
70007
  }
69793
70008
  }
69794
70009
  switch (this.tieDirection) {
69795
70010
  case BeamDirection.Up:
69796
70011
  // below lowest note
69797
- return endNoteScoreRenderer.getNoteY(this.endBeat.maxNote, NoteYPosition.Top);
70012
+ return endBeatRenderer.y + endBeatRenderer.getNoteY(this.endBeat.maxNote, NoteYPosition.Top);
69798
70013
  default:
69799
70014
  // above highest note
69800
- return endNoteScoreRenderer.getNoteY(this.endBeat.minNote, NoteYPosition.Bottom);
70015
+ return endBeatRenderer.y + endBeatRenderer.getNoteY(this.endBeat.minNote, NoteYPosition.Bottom);
69801
70016
  }
69802
70017
  }
69803
- getStartX() {
69804
- return this.startNoteRenderer.getBeatX(this.startBeat, BeatXPosition.MiddleNotes);
69805
- }
69806
- getEndX() {
69807
- const endBeamDirection = this.endNoteRenderer.getBeatDirection(this.endBeat);
69808
- return this.endNoteRenderer.getBeatX(this.endBeat, this.endBeat.duration > Duration.Whole && endBeamDirection === this.tieDirection
69809
- ? BeatXPosition.Stem
69810
- : BeatXPosition.MiddleNotes);
69811
- }
69812
70018
  }
69813
70019
 
69814
70020
  /**
@@ -70008,142 +70214,106 @@ class ScoreSlideLineGlyph extends Glyph {
70008
70214
  /**
70009
70215
  * @internal
70010
70216
  */
70011
- class ScoreSlurGlyph extends ScoreLegatoGlyph {
70012
- _startNote;
70013
- _endNote;
70014
- constructor(startNote, endNote, forEnd = false) {
70015
- super(startNote.beat, endNote.beat, forEnd);
70016
- this._startNote = startNote;
70017
- this._endNote = endNote;
70217
+ class ScoreTieGlyph extends NoteTieGlyph {
70218
+ shouldDrawBendSlur() {
70219
+ return (this.renderer.settings.notation.extendBendArrowsOnTiedNotes &&
70220
+ !!this.startNote.bendOrigin &&
70221
+ this.startNote.isTieOrigin);
70222
+ }
70223
+ calculateStartX() {
70224
+ if (this.isLeftHandTap) {
70225
+ return this.calculateEndX() - this.renderer.smuflMetrics.leftHandTabTieWidth;
70226
+ }
70227
+ return this.renderer.x + this.renderer.getBeatX(this.startNote.beat, BeatXPosition.PostNotes);
70228
+ }
70229
+ calculateEndX() {
70230
+ const endNoteRenderer = this.lookupEndBeatRenderer();
70231
+ if (!endNoteRenderer) {
70232
+ return this.calculateStartX() + this.renderer.smuflMetrics.leftHandTabTieWidth;
70233
+ }
70234
+ if (this.isLeftHandTap) {
70235
+ return endNoteRenderer.x + endNoteRenderer.getNoteX(this.endNote, NoteXPosition.Left);
70236
+ }
70237
+ return endNoteRenderer.x + endNoteRenderer.getBeatX(this.endNote.beat, BeatXPosition.PreNotes);
70018
70238
  }
70239
+ }
70240
+
70241
+ /**
70242
+ * @internal
70243
+ */
70244
+ class ScoreSlurGlyph extends ScoreTieGlyph {
70019
70245
  getTieHeight(startX, _startY, endX, _endY) {
70020
- return Math.log2(endX - startX + 1) * this.renderer.settings.notation.slurHeight / 2;
70246
+ return (Math.log2(endX - startX + 1) * this.renderer.settings.notation.slurHeight) / 2;
70021
70247
  }
70022
- getStartY() {
70248
+ calculateStartX() {
70249
+ return (this.renderer.x +
70250
+ (this._isStartCentered()
70251
+ ? this.renderer.getBeatX(this.startNote.beat, BeatXPosition.MiddleNotes)
70252
+ : this.renderer.getNoteX(this.startNote, NoteXPosition.Right)));
70253
+ }
70254
+ calculateStartY() {
70023
70255
  if (this._isStartCentered()) {
70024
70256
  switch (this.tieDirection) {
70025
70257
  case BeamDirection.Up:
70026
- // below lowest note
70027
- return this.startNoteRenderer.getNoteY(this._startNote, NoteYPosition.Top);
70258
+ return this.renderer.y + this.renderer.getNoteY(this.startNote, NoteYPosition.Top);
70028
70259
  default:
70029
- return this.startNoteRenderer.getNoteY(this._startNote, NoteYPosition.Bottom);
70260
+ return this.renderer.y + this.renderer.getNoteY(this.startNote, NoteYPosition.Bottom);
70261
+ }
70262
+ }
70263
+ return this.renderer.y + this.renderer.getNoteY(this.startNote, NoteYPosition.Center);
70264
+ }
70265
+ calculateEndX() {
70266
+ const endNoteRenderer = this.lookupEndBeatRenderer();
70267
+ if (!endNoteRenderer) {
70268
+ return this.calculateStartX() + this.renderer.smuflMetrics.leftHandTabTieWidth;
70269
+ }
70270
+ if (this._isEndCentered()) {
70271
+ if (this._isEndOnStem()) {
70272
+ return endNoteRenderer.x + endNoteRenderer.getBeatX(this.endNote.beat, BeatXPosition.Stem);
70030
70273
  }
70274
+ return endNoteRenderer.x + endNoteRenderer.getNoteX(this.endNote, NoteXPosition.Center);
70031
70275
  }
70032
- return this.startNoteRenderer.getNoteY(this._startNote, NoteYPosition.Center);
70276
+ return endNoteRenderer.x + endNoteRenderer.getBeatX(this.endNote.beat, BeatXPosition.PreNotes);
70033
70277
  }
70034
- getEndY() {
70278
+ caclculateEndY() {
70279
+ const endNoteRenderer = this.lookupEndBeatRenderer();
70280
+ if (!endNoteRenderer) {
70281
+ return this.calculateStartY();
70282
+ }
70035
70283
  if (this._isEndCentered()) {
70036
70284
  if (this._isEndOnStem()) {
70037
70285
  switch (this.tieDirection) {
70038
70286
  case BeamDirection.Up:
70039
- return this.endNoteRenderer.getNoteY(this._endNote, NoteYPosition.TopWithStem);
70287
+ return endNoteRenderer.y + endNoteRenderer.getNoteY(this.endNote, NoteYPosition.TopWithStem);
70040
70288
  default:
70041
- return this.endNoteRenderer.getNoteY(this._endNote, NoteYPosition.BottomWithStem);
70289
+ return endNoteRenderer.y + endNoteRenderer.getNoteY(this.endNote, NoteYPosition.BottomWithStem);
70042
70290
  }
70043
70291
  }
70044
70292
  switch (this.tieDirection) {
70045
70293
  case BeamDirection.Up:
70046
- return this.endNoteRenderer.getNoteY(this._endNote, NoteYPosition.Top);
70294
+ return endNoteRenderer.y + endNoteRenderer.getNoteY(this.endNote, NoteYPosition.Top);
70047
70295
  default:
70048
- return this.endNoteRenderer.getNoteY(this._endNote, NoteYPosition.Bottom);
70296
+ return endNoteRenderer.y + endNoteRenderer.getNoteY(this.endNote, NoteYPosition.Bottom);
70049
70297
  }
70050
70298
  }
70051
- return this.endNoteRenderer.getNoteY(this._endNote, NoteYPosition.Center);
70299
+ return endNoteRenderer.y + endNoteRenderer.getNoteY(this.endNote, NoteYPosition.Center);
70052
70300
  }
70053
70301
  _isStartCentered() {
70054
- return ((this._startNote === this._startNote.beat.maxNote && this.tieDirection === BeamDirection.Up) ||
70055
- (this._startNote === this._startNote.beat.minNote && this.tieDirection === BeamDirection.Down));
70302
+ return ((this.startNote === this.startNote.beat.maxNote && this.tieDirection === BeamDirection.Up) ||
70303
+ (this.startNote === this.startNote.beat.minNote && this.tieDirection === BeamDirection.Down));
70056
70304
  }
70057
70305
  _isEndCentered() {
70058
- return (this._startNote.beat.graceType === GraceType.None &&
70059
- ((this._endNote === this._endNote.beat.maxNote && this.tieDirection === BeamDirection.Up) ||
70060
- (this._endNote === this._endNote.beat.minNote && this.tieDirection === BeamDirection.Down)));
70306
+ return (this.startNote.beat.graceType === GraceType.None &&
70307
+ ((this.endNote === this.endNote.beat.maxNote && this.tieDirection === BeamDirection.Up) ||
70308
+ (this.endNote === this.endNote.beat.minNote && this.tieDirection === BeamDirection.Down)));
70061
70309
  }
70062
70310
  _isEndOnStem() {
70063
- const endNoteScoreRenderer = this.endNoteRenderer;
70064
- const startBeamDirection = this.startNoteRenderer.getBeatDirection(this.startBeat);
70065
- const endBeamDirection = endNoteScoreRenderer.getBeatDirection(this.endBeat);
70066
- return startBeamDirection !== endBeamDirection && this.startBeat.graceType === GraceType.None;
70067
- }
70068
- getStartX() {
70069
- return this._isStartCentered()
70070
- ? this.startNoteRenderer.getBeatX(this._startNote.beat, BeatXPosition.MiddleNotes)
70071
- : this.startNoteRenderer.getNoteX(this._startNote, NoteXPosition.Right);
70072
- }
70073
- getEndX() {
70074
- if (this._isEndCentered()) {
70075
- if (this._isEndOnStem()) {
70076
- return this.endNoteRenderer.getBeatX(this._endNote.beat, BeatXPosition.Stem);
70077
- }
70078
- return this.endNoteRenderer.getNoteX(this._endNote, NoteXPosition.Center);
70079
- }
70080
- return this.endNoteRenderer.getBeatX(this._endNote.beat, BeatXPosition.PreNotes);
70081
- }
70082
- }
70083
-
70084
- /**
70085
- * @internal
70086
- */
70087
- class ScoreTieGlyph extends TieGlyph {
70088
- startNote;
70089
- endNote;
70090
- constructor(startNote, endNote, forEnd = false) {
70091
- super(!startNote ? null : startNote.beat, !endNote ? null : endNote.beat, forEnd);
70092
- this.startNote = startNote;
70093
- this.endNote = endNote;
70094
- }
70095
- shouldDrawBendSlur() {
70096
- return (this.renderer.settings.notation.extendBendArrowsOnTiedNotes &&
70097
- !!this.startNote.bendOrigin &&
70098
- this.startNote.isTieOrigin);
70099
- }
70100
- doLayout() {
70101
- super.doLayout();
70102
- }
70103
- getBeamDirection(beat, noteRenderer) {
70104
- // invert direction (if stems go up, ties go down to not cross them)
70105
- switch (noteRenderer.getBeatDirection(beat)) {
70106
- case BeamDirection.Up:
70107
- return BeamDirection.Down;
70108
- default:
70109
- return BeamDirection.Up;
70110
- }
70111
- }
70112
- getStartY() {
70113
- if (this.startBeat.isRest) {
70114
- // below all lines
70115
- return this.startNoteRenderer.getScoreY(9);
70116
- }
70117
- switch (this.tieDirection) {
70118
- case BeamDirection.Up:
70119
- // below lowest note
70120
- return this.startNoteRenderer.getNoteY(this.startNote, NoteYPosition.Top);
70121
- default:
70122
- return this.startNoteRenderer.getNoteY(this.startNote, NoteYPosition.Bottom);
70123
- }
70124
- }
70125
- getEndY() {
70126
- const endNoteScoreRenderer = this.endNoteRenderer;
70127
- if (this.endBeat.isRest) {
70128
- switch (this.tieDirection) {
70129
- case BeamDirection.Up:
70130
- return endNoteScoreRenderer.getScoreY(9);
70131
- default:
70132
- return endNoteScoreRenderer.getScoreY(0);
70133
- }
70134
- }
70135
- switch (this.tieDirection) {
70136
- case BeamDirection.Up:
70137
- return endNoteScoreRenderer.getNoteY(this.endNote, NoteYPosition.Top);
70138
- default:
70139
- return endNoteScoreRenderer.getNoteY(this.endNote, NoteYPosition.Bottom);
70140
- }
70141
- }
70142
- getStartX() {
70143
- return this.startNoteRenderer.getBeatX(this.startNote.beat, BeatXPosition.PostNotes);
70144
- }
70145
- getEndX() {
70146
- return this.endNoteRenderer.getBeatX(this.endNote.beat, BeatXPosition.PreNotes);
70311
+ const startBeamDirection = this.lookupStartBeatRenderer().getBeatDirection(this.startNote.beat);
70312
+ const endBeatRenderer = this.lookupEndBeatRenderer();
70313
+ const endBeamDirection = endBeatRenderer
70314
+ ? endBeatRenderer.getBeatDirection(this.endNote.beat)
70315
+ : startBeamDirection;
70316
+ return startBeamDirection !== endBeamDirection && this.startNote.beat.graceType === GraceType.None;
70147
70317
  }
70148
70318
  }
70149
70319
 
@@ -70193,12 +70363,11 @@ class ScoreBeatContainerGlyph extends BeatContainerGlyph {
70193
70363
  n.beat.graceType !== GraceType.BendGrace &&
70194
70364
  n.tieDestination &&
70195
70365
  n.tieDestination.isVisible) {
70196
- // tslint:disable-next-line: no-unnecessary-type-assertion
70197
- const tie = new ScoreTieGlyph(n, n.tieDestination, false);
70366
+ const tie = new ScoreTieGlyph(`score.tie.${n.id}`, n, n.tieDestination, false);
70198
70367
  this.addTie(tie);
70199
70368
  }
70200
70369
  if (n.isTieDestination && !n.tieOrigin.hasBend && !n.beat.hasWhammyBar) {
70201
- const tie = new ScoreTieGlyph(n.tieOrigin, n, true);
70370
+ const tie = new ScoreTieGlyph(`score.tie.${n.tieOrigin.id}`, n.tieOrigin, n, true);
70202
70371
  this.addTie(tie);
70203
70372
  }
70204
70373
  // TODO: depending on the type we have other positioning
@@ -70208,17 +70377,16 @@ class ScoreBeatContainerGlyph extends BeatContainerGlyph {
70208
70377
  this.addTie(l);
70209
70378
  }
70210
70379
  if (n.isSlurOrigin && n.slurDestination && n.slurDestination.isVisible) {
70211
- // tslint:disable-next-line: no-unnecessary-type-assertion
70212
- const tie = new ScoreSlurGlyph(n, n.slurDestination, false);
70380
+ const tie = new ScoreSlurGlyph(`score.slur.${n.id}`, n, n.slurDestination, false);
70213
70381
  this.addTie(tie);
70214
70382
  }
70215
70383
  if (n.isSlurDestination) {
70216
- const tie = new ScoreSlurGlyph(n.slurOrigin, n, true);
70384
+ const tie = new ScoreSlurGlyph(`score.slur.${n.slurOrigin.id}`, n.slurOrigin, n, true);
70217
70385
  this.addTie(tie);
70218
70386
  }
70219
70387
  // start effect slur on first beat
70220
70388
  if (!this._effectSlur && n.isEffectSlurOrigin && n.effectSlurDestination) {
70221
- const effectSlur = new ScoreSlurGlyph(n, n.effectSlurDestination, false);
70389
+ const effectSlur = new ScoreSlurGlyph(`score.slur.effect.${n.beat.id}`, n, n.effectSlurDestination, false);
70222
70390
  this._effectSlur = effectSlur;
70223
70391
  this.addTie(effectSlur);
70224
70392
  }
@@ -70227,7 +70395,7 @@ class ScoreBeatContainerGlyph extends BeatContainerGlyph {
70227
70395
  const direction = this.onNotes.beamingHelper.direction;
70228
70396
  const startNote = direction === BeamDirection.Up ? n.beat.effectSlurOrigin.minNote : n.beat.effectSlurOrigin.maxNote;
70229
70397
  const endNote = direction === BeamDirection.Up ? n.beat.minNote : n.beat.maxNote;
70230
- const effectEndSlur = new ScoreSlurGlyph(startNote, endNote, true);
70398
+ const effectEndSlur = new ScoreSlurGlyph(`score.slur.effect.${startNote.beat.id}`, startNote, endNote, true);
70231
70399
  this._effectEndSlur = effectEndSlur;
70232
70400
  this.addTie(effectEndSlur);
70233
70401
  }
@@ -70249,7 +70417,7 @@ class ScoreBeatContainerGlyph extends BeatContainerGlyph {
70249
70417
  while (destination.nextBeat && destination.nextBeat.isLegatoDestination) {
70250
70418
  destination = destination.nextBeat;
70251
70419
  }
70252
- this.addTie(new ScoreLegatoGlyph(this.beat, destination, false));
70420
+ this.addTie(new ScoreLegatoGlyph(`score.legato.${this.beat.id}`, this.beat, destination, false));
70253
70421
  }
70254
70422
  }
70255
70423
  else if (this.beat.isLegatoDestination) {
@@ -70259,7 +70427,7 @@ class ScoreBeatContainerGlyph extends BeatContainerGlyph {
70259
70427
  while (origin.previousBeat && origin.previousBeat.isLegatoOrigin) {
70260
70428
  origin = origin.previousBeat;
70261
70429
  }
70262
- this.addTie(new ScoreLegatoGlyph(origin, this.beat, true));
70430
+ this.addTie(new ScoreLegatoGlyph(`score.legato.${origin.id}`, origin, this.beat, true));
70263
70431
  }
70264
70432
  }
70265
70433
  }
@@ -70639,6 +70807,9 @@ class ScoreBarRenderer extends LineBarRenderer {
70639
70807
  this.addBeatGlyph(container);
70640
70808
  }
70641
70809
  }
70810
+ getNoteLine(note) {
70811
+ return this.accidentalHelper.getNoteSteps(note) / 2;
70812
+ }
70642
70813
  getNoteSteps(n) {
70643
70814
  return this.accidentalHelper.getNoteSteps(n);
70644
70815
  }
@@ -70695,43 +70866,15 @@ class ScoreBarRendererFactory extends BarRendererFactory {
70695
70866
  /**
70696
70867
  * @internal
70697
70868
  */
70698
- class SlashTieGlyph extends TieGlyph {
70699
- startNote;
70700
- endNote;
70701
- constructor(startNote, endNote, forEnd = false) {
70702
- super(startNote.beat, endNote.beat, forEnd);
70703
- this.startNote = startNote;
70704
- this.endNote = endNote;
70705
- }
70706
- get _isLeftHandTap() {
70707
- return this.startNote === this.endNote;
70708
- }
70709
- getTieHeight(startX, startY, endX, endY) {
70710
- if (this._isLeftHandTap) {
70711
- return this.startNoteRenderer.smuflMetrics.tieHeight;
70712
- }
70713
- return super.getTieHeight(startX, startY, endX, endY);
70714
- }
70715
- getBeamDirection(_beat, _noteRenderer) {
70869
+ class SlashTieGlyph extends NoteTieGlyph {
70870
+ calculateTieDirection() {
70716
70871
  return BeamDirection.Down;
70717
70872
  }
70718
- static getBeamDirectionForNote(_note) {
70719
- return BeamDirection.Down;
70720
- }
70721
- getStartY() {
70722
- return this.startNoteRenderer.getNoteY(this.startNote, NoteYPosition.Center);
70723
- }
70724
- getEndY() {
70725
- return this.getStartY();
70726
- }
70727
- getStartX() {
70728
- if (this._isLeftHandTap) {
70729
- return this.getEndX() - this.renderer.smuflMetrics.leftHandTabTieWidth;
70730
- }
70731
- return this.startNoteRenderer.getNoteX(this.startNote, NoteXPosition.Right);
70873
+ getStartNotePosition() {
70874
+ return NoteXPosition.Right;
70732
70875
  }
70733
- getEndX() {
70734
- return this.endNoteRenderer.getNoteX(this.endNote, NoteXPosition.Left);
70876
+ getEndNotePosition() {
70877
+ return NoteXPosition.Left;
70735
70878
  }
70736
70879
  }
70737
70880
 
@@ -70746,12 +70889,12 @@ class SlashBeatContainerGlyph extends BeatContainerGlyph {
70746
70889
  return;
70747
70890
  }
70748
70891
  if (!this._tiedNoteTie && n.isTieOrigin && n.tieDestination.isVisible) {
70749
- const tie = new SlashTieGlyph(n, n.tieDestination, false);
70892
+ const tie = new SlashTieGlyph('slash.tie', n, n.tieDestination, false);
70750
70893
  this._tiedNoteTie = tie;
70751
70894
  this.addTie(tie);
70752
70895
  }
70753
70896
  if (!this._tiedNoteTie && n.isTieDestination) {
70754
- const tie = new SlashTieGlyph(n.tieOrigin, n, true);
70897
+ const tie = new SlashTieGlyph('slash.tie', n.tieOrigin, n, true);
70755
70898
  this._tiedNoteTie = tie;
70756
70899
  this.addTie(tie);
70757
70900
  }
@@ -70878,8 +71021,7 @@ class SlashBeatGlyph extends BeatOnNoteGlyphBase {
70878
71021
  doLayout() {
70879
71022
  // create glyphs
70880
71023
  const sr = this.renderer;
70881
- const line = sr.getNoteLine();
70882
- const glyphY = sr.getLineY(line);
71024
+ const glyphY = sr.getLineY(0);
70883
71025
  if (this.container.beat.deadSlapped) {
70884
71026
  const deadSlapped = new DeadSlappedBeatGlyph();
70885
71027
  deadSlapped.renderer = this.renderer;
@@ -70912,7 +71054,7 @@ class SlashBeatGlyph extends BeatOnNoteGlyphBase {
70912
71054
  //
70913
71055
  if (this.container.beat.dots > 0) {
70914
71056
  for (let i = 0; i < this.container.beat.dots; i++) {
70915
- this.addEffect(new AugmentationDotGlyph(0, sr.getLineY(sr.getNoteLine()) - sr.getLineHeight(0.5)));
71057
+ this.addEffect(new AugmentationDotGlyph(0, glyphY - sr.getLineHeight(0.5)));
70916
71058
  }
70917
71059
  }
70918
71060
  super.doLayout();
@@ -70994,7 +71136,7 @@ class SlashBarRenderer extends LineBarRenderer {
70994
71136
  this.registerOverflowTop(this.tupletSize);
70995
71137
  }
70996
71138
  }
70997
- getNoteLine() {
71139
+ getNoteLine(_note) {
70998
71140
  return 0;
70999
71141
  }
71000
71142
  getFlagTopY(beat, _direction) {
@@ -71331,77 +71473,6 @@ class TabSlideLineGlyph extends Glyph {
71331
71473
  }
71332
71474
  }
71333
71475
 
71334
- /**
71335
- * @internal
71336
- */
71337
- class TabSlurGlyph extends TabTieGlyph {
71338
- _direction;
71339
- _forSlide;
71340
- constructor(startNote, endNote, forSlide, forEnd = false) {
71341
- super(startNote, endNote, forEnd);
71342
- this._direction = TabTieGlyph.getBeamDirectionForNote(startNote);
71343
- this._forSlide = forSlide;
71344
- }
71345
- getTieHeight(startX, _startY, endX, _endY) {
71346
- return Math.log(endX - startX + 1) * this.renderer.settings.notation.slurHeight / 2;
71347
- }
71348
- tryExpand(startNote, endNote, forSlide, forEnd) {
71349
- // same type required
71350
- if (this._forSlide !== forSlide) {
71351
- return false;
71352
- }
71353
- if (this.forEnd !== forEnd) {
71354
- return false;
71355
- }
71356
- // same start and endbeat
71357
- if (this.startNote.beat.id !== startNote.beat.id) {
71358
- return false;
71359
- }
71360
- if (this.endNote.beat.id !== endNote.beat.id) {
71361
- return false;
71362
- }
71363
- // same draw direction
71364
- if (this._direction !== TabTieGlyph.getBeamDirectionForNote(startNote)) {
71365
- return false;
71366
- }
71367
- // if we can expand, expand in correct direction
71368
- switch (this._direction) {
71369
- case BeamDirection.Up:
71370
- if (startNote.realValue > this.startNote.realValue) {
71371
- this.startNote = startNote;
71372
- this.startBeat = startNote.beat;
71373
- }
71374
- if (endNote.realValue > this.endNote.realValue) {
71375
- this.endNote = endNote;
71376
- this.endBeat = endNote.beat;
71377
- }
71378
- break;
71379
- case BeamDirection.Down:
71380
- if (startNote.realValue < this.startNote.realValue) {
71381
- this.startNote = startNote;
71382
- this.startBeat = startNote.beat;
71383
- }
71384
- if (endNote.realValue < this.endNote.realValue) {
71385
- this.endNote = endNote;
71386
- this.endBeat = endNote.beat;
71387
- }
71388
- break;
71389
- }
71390
- return true;
71391
- }
71392
- paint(cx, cy, canvas) {
71393
- const startNoteRenderer = this.renderer.scoreRenderer.layout.getRendererForBar(this.renderer.staff.staffId, this.startBeat.voice.bar);
71394
- const direction = this.getBeamDirection(this.startBeat, startNoteRenderer);
71395
- const slurId = `tab.slur.${this.startNote.beat.id}.${this.endNote.beat.id}.${direction}`;
71396
- const renderer = this.renderer;
71397
- const isSlurRendered = renderer.staff.getSharedLayoutData(slurId, false);
71398
- if (!isSlurRendered) {
71399
- renderer.staff.setSharedLayoutData(slurId, true);
71400
- super.paint(cx, cy, canvas);
71401
- }
71402
- }
71403
- }
71404
-
71405
71476
  /**
71406
71477
  * @internal
71407
71478
  */
@@ -71426,15 +71497,15 @@ class TabBeatContainerGlyph extends BeatContainerGlyph {
71426
71497
  }
71427
71498
  const renderer = this.renderer;
71428
71499
  if (n.isTieOrigin && renderer.showTiedNotes && n.tieDestination.isVisible) {
71429
- const tie = new TabTieGlyph(n, n.tieDestination, false);
71500
+ const tie = new TabTieGlyph(`tab.tie.${n.id}`, n, n.tieDestination, false);
71430
71501
  this.addTie(tie);
71431
71502
  }
71432
71503
  if (n.isTieDestination && renderer.showTiedNotes) {
71433
- const tie = new TabTieGlyph(n.tieOrigin, n, true);
71504
+ const tie = new TabTieGlyph(`tab.tie.${n.tieOrigin.id}`, n.tieOrigin, n, true);
71434
71505
  this.addTie(tie);
71435
71506
  }
71436
71507
  if (n.isLeftHandTapped && !n.isHammerPullDestination) {
71437
- const tapSlur = new TabTieGlyph(n, n, false);
71508
+ const tapSlur = new TabTieGlyph(`tab.tie.leftHandTap.${n.id}`, n, n, false);
71438
71509
  this.addTie(tapSlur);
71439
71510
  }
71440
71511
  // start effect slur on first beat
@@ -71447,7 +71518,7 @@ class TabBeatContainerGlyph extends BeatContainerGlyph {
71447
71518
  }
71448
71519
  }
71449
71520
  if (!expanded) {
71450
- const effectSlur = new TabSlurGlyph(n, n.effectSlurDestination, false, false);
71521
+ const effectSlur = new TabSlurGlyph(`tab.slur.effect.${n.id}`, n, n.effectSlurDestination, false, false);
71451
71522
  this._effectSlurs.push(effectSlur);
71452
71523
  this.addTie(effectSlur);
71453
71524
  }
@@ -71462,7 +71533,7 @@ class TabBeatContainerGlyph extends BeatContainerGlyph {
71462
71533
  }
71463
71534
  }
71464
71535
  if (!expanded) {
71465
- const effectSlur = new TabSlurGlyph(n.effectSlurOrigin, n, false, true);
71536
+ const effectSlur = new TabSlurGlyph(`tab.slur.effect.${n.effectSlurOrigin.id}`, n.effectSlurOrigin, n, false, true);
71466
71537
  this._effectSlurs.push(effectSlur);
71467
71538
  this.addTie(effectSlur);
71468
71539
  }
@@ -71881,7 +71952,7 @@ class TabBeatGlyph extends BeatOnNoteGlyphBase {
71881
71952
  }
71882
71953
  else {
71883
71954
  const line = Math.floor((this.renderer.bar.staff.tuning.length - 1) / 2);
71884
- const y = tabRenderer.getTabY(line);
71955
+ const y = tabRenderer.getLineY(line);
71885
71956
  const restGlyph = new TabRestGlyph(0, y, tabRenderer.showRests, this.container.beat.duration);
71886
71957
  this.restGlyph = restGlyph;
71887
71958
  restGlyph.beat = this.container.beat;
@@ -71940,8 +72011,8 @@ class TabBeatGlyph extends BeatOnNoteGlyphBase {
71940
72011
  _createNoteGlyph(n) {
71941
72012
  const tr = this.renderer;
71942
72013
  const noteNumberGlyph = new NoteNumberGlyph(0, 0, n);
71943
- const l = n.beat.voice.bar.staff.tuning.length - n.string;
71944
- noteNumberGlyph.y = tr.getTabY(l);
72014
+ const l = tr.getNoteLine(n);
72015
+ noteNumberGlyph.y = tr.getLineY(l);
71945
72016
  noteNumberGlyph.renderer = this.renderer;
71946
72017
  noteNumberGlyph.doLayout();
71947
72018
  this.noteNumbers.addNoteGlyph(noteNumberGlyph, n);
@@ -72132,17 +72203,8 @@ class TabBarRenderer extends LineBarRenderer {
72132
72203
  }
72133
72204
  return mode;
72134
72205
  }
72135
- /**
72136
- * Gets the relative y position of the given steps relative to first line.
72137
- * @param line the line of the particular string where 0 is the most top line
72138
- * @param correction
72139
- * @returns
72140
- */
72141
- getTabY(line) {
72142
- return super.getLineY(line);
72143
- }
72144
- getTabHeight(line) {
72145
- return super.getLineHeight(line);
72206
+ getNoteLine(note) {
72207
+ return this.bar.staff.tuning.length - note.string;
72146
72208
  }
72147
72209
  minString = Number.NaN;
72148
72210
  maxString = Number.NaN;
@@ -72231,7 +72293,7 @@ class TabBarRenderer extends LineBarRenderer {
72231
72293
  if (this.isFirstOfLine) {
72232
72294
  const center = (this.bar.staff.tuning.length - 1) / 2;
72233
72295
  this.createStartSpacing();
72234
- this.addPreBeatGlyph(new TabClefGlyph(0, this.getTabY(center)));
72296
+ this.addPreBeatGlyph(new TabClefGlyph(0, this.getLineY(center)));
72235
72297
  }
72236
72298
  // Time Signature
72237
72299
  if (this.showTimeSignature &&
@@ -72252,7 +72314,7 @@ class TabBarRenderer extends LineBarRenderer {
72252
72314
  _createTimeSignatureGlyphs() {
72253
72315
  this.addPreBeatGlyph(new SpacingGlyph(0, 0, this.smuflMetrics.oneStaffSpace));
72254
72316
  const lines = (this.bar.staff.tuning.length + 1) / 2 - 1;
72255
- this.addPreBeatGlyph(new TabTimeSignatureGlyph(0, this.getTabY(lines), this.bar.masterBar.timeSignatureNumerator, this.bar.masterBar.timeSignatureDenominator, this.bar.masterBar.timeSignatureCommon, this.bar.masterBar.isFreeTime));
72317
+ this.addPreBeatGlyph(new TabTimeSignatureGlyph(0, this.getLineY(lines), this.bar.masterBar.timeSignatureNumerator, this.bar.masterBar.timeSignatureDenominator, this.bar.masterBar.timeSignatureCommon, this.bar.masterBar.isFreeTime));
72256
72318
  }
72257
72319
  createVoiceGlyphs(v) {
72258
72320
  super.createVoiceGlyphs(v);