@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.
package/dist/alphaTab.js CHANGED
@@ -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
  *
@@ -209,9 +209,9 @@
209
209
  * @internal
210
210
  */
211
211
  class VersionInfo {
212
- static version = '1.8.0-alpha.1637';
213
- static date = '2025-12-07T02:26:27.382Z';
214
- static commit = 'aa2c8101456d3ddfc777491468bc483789cc6c12';
212
+ static version = '1.8.0-alpha.1639';
213
+ static date = '2025-12-09T02:16:01.440Z';
214
+ static commit = 'af86866e4f9d89a3ccc34006d01473a549dbbe7f';
215
215
  static print(print) {
216
216
  print(`alphaTab ${VersionInfo.version}`);
217
217
  print(`commit: ${VersionInfo.commit}`);
@@ -56670,12 +56670,12 @@
56670
56670
  this.endPosition = endPosition;
56671
56671
  }
56672
56672
  get isLinkedWithPrevious() {
56673
- return !!this.previousGlyph && this.previousGlyph.renderer.staff.system === this.renderer.staff.system;
56673
+ return !!this.previousGlyph && this.previousGlyph.renderer.staff?.system === this.renderer.staff.system;
56674
56674
  }
56675
56675
  get isLinkedWithNext() {
56676
56676
  return (!!this.nextGlyph &&
56677
56677
  this.nextGlyph.renderer.isFinalized &&
56678
- this.nextGlyph.renderer.staff.system === this.renderer.staff.system);
56678
+ this.nextGlyph.renderer.staff?.system === this.renderer.staff.system);
56679
56679
  }
56680
56680
  paint(cx, cy, canvas) {
56681
56681
  // if we are linked with the previous, the first glyph of the group will also render this one.
@@ -59063,6 +59063,437 @@
59063
59063
  }
59064
59064
  }
59065
59065
 
59066
+ /**
59067
+ * @internal
59068
+ */
59069
+ class TieGlyph extends Glyph {
59070
+ tieDirection = BeamDirection.Up;
59071
+ slurEffectId;
59072
+ isForEnd;
59073
+ constructor(slurEffectId, forEnd) {
59074
+ super(0, 0);
59075
+ this.slurEffectId = slurEffectId;
59076
+ this.isForEnd = forEnd;
59077
+ }
59078
+ _startX = 0;
59079
+ _startY = 0;
59080
+ _endX = 0;
59081
+ _endY = 0;
59082
+ _tieHeight = 0;
59083
+ _boundingBox;
59084
+ _shouldPaint = false;
59085
+ get checkForOverflow() {
59086
+ return this._shouldPaint && this._boundingBox !== undefined;
59087
+ }
59088
+ getBoundingBoxTop() {
59089
+ if (this._boundingBox) {
59090
+ return this._boundingBox.y;
59091
+ }
59092
+ return this._startY;
59093
+ }
59094
+ getBoundingBoxBottom() {
59095
+ if (this._boundingBox) {
59096
+ return this._boundingBox.y + this._boundingBox.h;
59097
+ }
59098
+ return this._startY;
59099
+ }
59100
+ doLayout() {
59101
+ this.width = 0;
59102
+ const startNoteRenderer = this.lookupStartBeatRenderer();
59103
+ const endNoteRenderer = this.lookupEndBeatRenderer();
59104
+ this._startX = 0;
59105
+ this._endX = 0;
59106
+ this._startY = 0;
59107
+ this._endY = 0;
59108
+ this.height = 0;
59109
+ // if we are on the tie start, we check if we
59110
+ // either can draw till the end note, or we just can draw till the bar end
59111
+ this.tieDirection = this.calculateTieDirection();
59112
+ const forEnd = this.isForEnd;
59113
+ this._shouldPaint = false;
59114
+ if (!forEnd) {
59115
+ if (startNoteRenderer !== endNoteRenderer) {
59116
+ this._startX = this.calculateStartX();
59117
+ this._startY = this.calculateStartY();
59118
+ if (!endNoteRenderer || startNoteRenderer.staff !== endNoteRenderer.staff) {
59119
+ const lastRendererInStaff = startNoteRenderer.staff.barRenderers[startNoteRenderer.staff.barRenderers.length - 1];
59120
+ this._endX = lastRendererInStaff.x + lastRendererInStaff.width;
59121
+ this._endY = this._startY;
59122
+ startNoteRenderer.scoreRenderer.layout.slurRegistry.startMultiSystemSlur(this);
59123
+ }
59124
+ else {
59125
+ this._endX = this.calculateEndX();
59126
+ this._endY = this.caclculateEndY();
59127
+ }
59128
+ }
59129
+ else {
59130
+ this._shouldPaint = true;
59131
+ this._startX = this.calculateStartX();
59132
+ this._endX = this.calculateEndX();
59133
+ this._startY = this.calculateStartY();
59134
+ this._endY = this.caclculateEndY();
59135
+ }
59136
+ this._shouldPaint = true;
59137
+ }
59138
+ else if (startNoteRenderer.staff !== endNoteRenderer.staff) {
59139
+ const firstRendererInStaff = startNoteRenderer.staff.barRenderers[0];
59140
+ this._startX = firstRendererInStaff.x;
59141
+ this._endX = this.calculateEndX();
59142
+ const startGlyph = startNoteRenderer.scoreRenderer.layout.slurRegistry.completeMultiSystemSlur(this);
59143
+ if (startGlyph) {
59144
+ this._startY = startGlyph.calculateMultiSystemSlurY(endNoteRenderer);
59145
+ }
59146
+ else {
59147
+ this._startY = this.caclculateEndY();
59148
+ }
59149
+ this._endY = this.caclculateEndY();
59150
+ this._shouldPaint = startNoteRenderer.staff !== endNoteRenderer.staff;
59151
+ }
59152
+ this._boundingBox = undefined;
59153
+ this.y = Math.min(this._startY, this._endY);
59154
+ if (this.shouldDrawBendSlur()) {
59155
+ this._tieHeight = 0; // TODO: Bend slur height to be considered?
59156
+ }
59157
+ else {
59158
+ this._tieHeight = this.getTieHeight(this._startX, this._startY, this._endX, this._endY);
59159
+ const tieBoundingBox = TieGlyph.calculateActualTieHeight(1, this._startX, this._startY, this._endX, this._endY, this.tieDirection === BeamDirection.Down, this._tieHeight, this.renderer.smuflMetrics.tieMidpointThickness);
59160
+ this._boundingBox = tieBoundingBox;
59161
+ this.height = tieBoundingBox.h;
59162
+ if (this.tieDirection === BeamDirection.Up) {
59163
+ // the tie might go above `this.y` due to its shape
59164
+ // here we calculate how much this is so we can consider the
59165
+ // respective overflow
59166
+ const overlap = this.y - tieBoundingBox.y;
59167
+ if (overlap > 0) {
59168
+ this.y -= overlap;
59169
+ }
59170
+ }
59171
+ }
59172
+ }
59173
+ paint(cx, cy, canvas) {
59174
+ if (!this._shouldPaint) {
59175
+ return;
59176
+ }
59177
+ if (this.shouldDrawBendSlur()) {
59178
+ TieGlyph.drawBendSlur(canvas, cx + this._startX, cy + this._startY, cx + this._endX, cy + this._endY, this.tieDirection === BeamDirection.Down, 1, this.renderer.smuflMetrics.tieHeight);
59179
+ }
59180
+ else {
59181
+ 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);
59182
+ }
59183
+ }
59184
+ getTieHeight(_startX, _startY, _endX, _endY) {
59185
+ return this.renderer.smuflMetrics.tieHeight;
59186
+ }
59187
+ calculateMultiSystemSlurY(renderer) {
59188
+ const startRenderer = this.lookupStartBeatRenderer();
59189
+ const startY = this.calculateStartY();
59190
+ const relY = startY - startRenderer.y;
59191
+ return renderer.y + relY;
59192
+ }
59193
+ shouldCreateMultiSystemSlur(renderer) {
59194
+ const endStaff = this.lookupEndBeatRenderer()?.staff;
59195
+ if (!endStaff) {
59196
+ return true;
59197
+ }
59198
+ return renderer.staff.system.index < endStaff.system.index;
59199
+ }
59200
+ static calculateActualTieHeight(scale, x1, y1, x2, y2, down, offset, size) {
59201
+ const cp = TieGlyph._computeBezierControlPoints(scale, x1, y1, x2, y2, down, offset, size);
59202
+ if (cp.length === 0) {
59203
+ return new Bounds(x1, y1, x2 - x1, y2 - y1);
59204
+ }
59205
+ // For a musical tie/slur, the extrema occur predictably near the midpoint
59206
+ // Evaluate at midpoint (t=0.5) and check endpoints
59207
+ const p0x = cp[0];
59208
+ const p0y = cp[1];
59209
+ const c1x = cp[2];
59210
+ const c1y = cp[3];
59211
+ const c2x = cp[4];
59212
+ const c2y = cp[5];
59213
+ const p1x = cp[6];
59214
+ const p1y = cp[7];
59215
+ // Evaluate at t=0.5 for midpoint
59216
+ const midX = 0.125 * p0x + 0.375 * c1x + 0.375 * c2x + 0.125 * p1x;
59217
+ const midY = 0.125 * p0y + 0.375 * c1y + 0.375 * c2y + 0.125 * p1y;
59218
+ // Bounds are simply min/max of start, end, and midpoint
59219
+ const xMin = Math.min(p0x, p1x, midX);
59220
+ const xMax = Math.max(p0x, p1x, midX);
59221
+ let yMin = Math.min(p0y, p1y, midY);
59222
+ let yMax = Math.max(p0y, p1y, midY);
59223
+ // Account for thickness of the tie/slur
59224
+ if (down) {
59225
+ yMax += size;
59226
+ }
59227
+ else {
59228
+ yMin -= size;
59229
+ }
59230
+ const b = new Bounds();
59231
+ b.x = xMin;
59232
+ b.y = yMin;
59233
+ b.w = xMax - xMin;
59234
+ b.h = yMax - yMin;
59235
+ return b;
59236
+ }
59237
+ static _computeBezierControlPoints(scale, x1, y1, x2, y2, down, offset, size) {
59238
+ if (x1 === x2 && y1 === y2) {
59239
+ return [];
59240
+ }
59241
+ // ensure endX > startX
59242
+ if (x2 < x1) {
59243
+ let t = x1;
59244
+ x1 = x2;
59245
+ x2 = t;
59246
+ t = y1;
59247
+ y1 = y2;
59248
+ y2 = t;
59249
+ }
59250
+ //
59251
+ // calculate control points
59252
+ //
59253
+ offset *= scale;
59254
+ size *= scale;
59255
+ if (down) {
59256
+ offset *= -1;
59257
+ size *= -1;
59258
+ }
59259
+ if (scale >= 1) {
59260
+ size *= 1.2;
59261
+ }
59262
+ // calculate control points on horizontal axis then rotate:
59263
+ /*
59264
+ cp1x/cpy1 cp2x/cpy2
59265
+ *----------------*
59266
+ / \
59267
+ / \
59268
+ x1/y1 * * x2/y2
59269
+
59270
+ cp3 and cp4 are simply with lower height
59271
+ */
59272
+ const dY = y2 - y1;
59273
+ const dX = x2 - x1;
59274
+ const length = Math.sqrt(dX * dX + dY * dY);
59275
+ let cp1x = x1 + length * 0.25;
59276
+ let cp1y = y1 - offset;
59277
+ let cp2x = x1 + length * 0.75;
59278
+ let cp2y = y1 - offset;
59279
+ let cp3x = x1 + length * 0.75;
59280
+ let cp3y = y1 - offset - size;
59281
+ let cp4x = x1 + length * 0.25;
59282
+ let cp4y = y1 - offset - size;
59283
+ const angle = Math.atan2(dY, dX);
59284
+ [cp1x, cp1y] = TieGlyph._rotate(cp1x, cp1y, x1, y1, angle);
59285
+ [cp2x, cp2y] = TieGlyph._rotate(cp2x, cp2y, x1, y1, angle);
59286
+ [cp3x, cp3y] = TieGlyph._rotate(cp3x, cp3y, x1, y1, angle);
59287
+ [cp4x, cp4y] = TieGlyph._rotate(cp4x, cp4y, x1, y1, angle);
59288
+ return [x1, y1, cp1x, cp1y, cp2x, cp2y, x2, y2, cp3x, cp3y, cp4x, cp4y, x1, y1];
59289
+ }
59290
+ static _rotate(x, y, rotateX, rotateY, angle) {
59291
+ const dx = x - rotateX;
59292
+ const dy = y - rotateY;
59293
+ const rx = dx * Math.cos(angle) - dy * Math.sin(angle);
59294
+ const ry = dx * Math.sin(angle) + dy * Math.cos(angle);
59295
+ return [rotateX + rx, rotateY + ry];
59296
+ }
59297
+ static paintTie(canvas, scale, x1, y1, x2, y2, down /*= false*/, offset /*= 22*/, size /*= 4*/) {
59298
+ const cps = TieGlyph._computeBezierControlPoints(scale, x1, y1, x2, y2, down, offset, size);
59299
+ canvas.beginPath();
59300
+ canvas.moveTo(cps[0], cps[1]);
59301
+ canvas.bezierCurveTo(cps[2], cps[3], cps[4], cps[5], cps[6], cps[7]);
59302
+ canvas.bezierCurveTo(cps[8], cps[9], cps[10], cps[11], cps[12], cps[13]);
59303
+ canvas.closePath();
59304
+ canvas.fill();
59305
+ }
59306
+ static calculateBendSlurTopY(x1, y1, x2, y2, down, scale, bendSlurHeight) {
59307
+ let normalVectorX = y2 - y1;
59308
+ let normalVectorY = x2 - x1;
59309
+ const length = Math.sqrt(normalVectorX * normalVectorX + normalVectorY * normalVectorY);
59310
+ if (down) {
59311
+ normalVectorX *= -1;
59312
+ }
59313
+ else {
59314
+ normalVectorY *= -1;
59315
+ }
59316
+ // make to unit vector
59317
+ normalVectorX /= length;
59318
+ normalVectorY /= length;
59319
+ let offset = bendSlurHeight * scale;
59320
+ if (x2 - x1 < 20) {
59321
+ offset /= 2;
59322
+ }
59323
+ const centerY = (y2 + y1) / 2;
59324
+ const cp1Y = centerY + offset * normalVectorY;
59325
+ return cp1Y;
59326
+ }
59327
+ static drawBendSlur(canvas, x1, y1, x2, y2, down, scale, bendSlurHeight, slurText) {
59328
+ let normalVectorX = y2 - y1;
59329
+ let normalVectorY = x2 - x1;
59330
+ const length = Math.sqrt(normalVectorX * normalVectorX + normalVectorY * normalVectorY);
59331
+ if (down) {
59332
+ normalVectorX *= -1;
59333
+ }
59334
+ else {
59335
+ normalVectorY *= -1;
59336
+ }
59337
+ // make to unit vector
59338
+ normalVectorX /= length;
59339
+ normalVectorY /= length;
59340
+ // center of connection
59341
+ // TODO: should be 1/3
59342
+ const centerX = (x2 + x1) / 2;
59343
+ const centerY = (y2 + y1) / 2;
59344
+ let offset = bendSlurHeight * scale;
59345
+ if (x2 - x1 < 20) {
59346
+ offset /= 2;
59347
+ }
59348
+ const cp1X = centerX + offset * normalVectorX;
59349
+ const cp1Y = centerY + offset * normalVectorY;
59350
+ canvas.beginPath();
59351
+ canvas.moveTo(x1, y1);
59352
+ canvas.lineTo(cp1X, cp1Y);
59353
+ canvas.lineTo(x2, y2);
59354
+ canvas.stroke();
59355
+ if (slurText) {
59356
+ const w = canvas.measureText(slurText).width;
59357
+ const textOffset = down ? 0 : -canvas.font.size;
59358
+ canvas.fillText(slurText, cp1X - w / 2, cp1Y + textOffset);
59359
+ }
59360
+ }
59361
+ }
59362
+ /**
59363
+ * A common tie implementation using note details for positioning
59364
+ * @internal
59365
+ */
59366
+ class NoteTieGlyph extends TieGlyph {
59367
+ startNote;
59368
+ endNote;
59369
+ startNoteRenderer = null;
59370
+ endNoteRenderer = null;
59371
+ constructor(slurEffectId, startNote, endNote, forEnd) {
59372
+ super(slurEffectId, forEnd);
59373
+ this.startNote = startNote;
59374
+ this.endNote = endNote;
59375
+ }
59376
+ get isLeftHandTap() {
59377
+ return this.startNote === this.endNote;
59378
+ }
59379
+ getTieHeight(startX, startY, endX, endY) {
59380
+ if (this.isLeftHandTap) {
59381
+ return this.renderer.smuflMetrics.tieHeight;
59382
+ }
59383
+ return super.getTieHeight(startX, startY, endX, endY);
59384
+ }
59385
+ calculateTieDirection() {
59386
+ // invert direction (if stems go up, ties go down to not cross them)
59387
+ switch (this.lookupStartBeatRenderer().getBeatDirection(this.startNote.beat)) {
59388
+ case BeamDirection.Up:
59389
+ return BeamDirection.Down;
59390
+ default:
59391
+ return BeamDirection.Up;
59392
+ }
59393
+ }
59394
+ calculateStartX() {
59395
+ const startNoteRenderer = this.lookupStartBeatRenderer();
59396
+ if (this.isLeftHandTap) {
59397
+ return this.calculateEndX() - startNoteRenderer.smuflMetrics.leftHandTabTieWidth;
59398
+ }
59399
+ return startNoteRenderer.x + startNoteRenderer.getNoteX(this.startNote, this.getStartNotePosition());
59400
+ }
59401
+ getStartNotePosition() {
59402
+ return NoteXPosition.Center;
59403
+ }
59404
+ calculateStartY() {
59405
+ const startNoteRenderer = this.lookupStartBeatRenderer();
59406
+ if (this.isLeftHandTap) {
59407
+ return startNoteRenderer.y + startNoteRenderer.getNoteY(this.startNote, NoteYPosition.Center);
59408
+ }
59409
+ switch (this.tieDirection) {
59410
+ case BeamDirection.Up:
59411
+ return startNoteRenderer.y + startNoteRenderer.getNoteY(this.startNote, NoteYPosition.Top);
59412
+ default:
59413
+ return startNoteRenderer.y + startNoteRenderer.getNoteY(this.startNote, NoteYPosition.Bottom);
59414
+ }
59415
+ }
59416
+ calculateEndX() {
59417
+ const endNoteRenderer = this.lookupEndBeatRenderer();
59418
+ if (!endNoteRenderer) {
59419
+ return this.calculateStartY() + this.renderer.smuflMetrics.leftHandTabTieWidth;
59420
+ }
59421
+ if (this.isLeftHandTap) {
59422
+ return endNoteRenderer.x + endNoteRenderer.getNoteX(this.endNote, NoteXPosition.Left);
59423
+ }
59424
+ return endNoteRenderer.x + endNoteRenderer.getNoteX(this.endNote, NoteXPosition.Center);
59425
+ }
59426
+ getEndNotePosition() {
59427
+ return NoteXPosition.Center;
59428
+ }
59429
+ caclculateEndY() {
59430
+ const endNoteRenderer = this.lookupEndBeatRenderer();
59431
+ if (!endNoteRenderer) {
59432
+ return this.calculateStartY();
59433
+ }
59434
+ if (this.isLeftHandTap) {
59435
+ return endNoteRenderer.y + endNoteRenderer.getNoteY(this.endNote, NoteYPosition.Center);
59436
+ }
59437
+ switch (this.tieDirection) {
59438
+ case BeamDirection.Up:
59439
+ return endNoteRenderer.y + endNoteRenderer.getNoteY(this.endNote, NoteYPosition.Top);
59440
+ default:
59441
+ return endNoteRenderer.y + endNoteRenderer.getNoteY(this.endNote, NoteYPosition.Bottom);
59442
+ }
59443
+ }
59444
+ lookupEndBeatRenderer() {
59445
+ if (!this.endNoteRenderer) {
59446
+ this.endNoteRenderer = this.renderer.scoreRenderer.layout.getRendererForBar(this.renderer.staff.staffId, this.endNote.beat.voice.bar);
59447
+ }
59448
+ return this.endNoteRenderer;
59449
+ }
59450
+ lookupStartBeatRenderer() {
59451
+ if (!this.startNoteRenderer) {
59452
+ this.startNoteRenderer = this.renderer.scoreRenderer.layout.getRendererForBar(this.renderer.staff.staffId, this.startNote.beat.voice.bar);
59453
+ }
59454
+ return this.startNoteRenderer;
59455
+ }
59456
+ shouldDrawBendSlur() {
59457
+ return false;
59458
+ }
59459
+ }
59460
+ /**
59461
+ * A tie glyph for continued multi-system ties/slurs
59462
+ * @internal
59463
+ */
59464
+ class ContinuationTieGlyph extends TieGlyph {
59465
+ _startTie;
59466
+ constructor(startTie) {
59467
+ super(startTie.slurEffectId, false);
59468
+ this._startTie = startTie;
59469
+ }
59470
+ lookupStartBeatRenderer() {
59471
+ return this.renderer;
59472
+ }
59473
+ lookupEndBeatRenderer() {
59474
+ return this.renderer;
59475
+ }
59476
+ shouldDrawBendSlur() {
59477
+ return false;
59478
+ }
59479
+ calculateTieDirection() {
59480
+ return this._startTie.tieDirection;
59481
+ }
59482
+ calculateStartY() {
59483
+ return this._startTie.calculateMultiSystemSlurY(this.renderer);
59484
+ }
59485
+ caclculateEndY() {
59486
+ return this.calculateStartY();
59487
+ }
59488
+ calculateStartX() {
59489
+ return this.renderer.staff.barRenderers[0].x;
59490
+ }
59491
+ calculateEndX() {
59492
+ const last = this.renderer.staff.barRenderers[this.renderer.staff.barRenderers.length - 1];
59493
+ return last.x + last.width;
59494
+ }
59495
+ }
59496
+
59066
59497
  /**
59067
59498
  * This glyph acts as container for handling
59068
59499
  * multiple voice rendering
@@ -59533,6 +59964,63 @@
59533
59964
  }
59534
59965
  }
59535
59966
 
59967
+ /**
59968
+ * This registry keeps track of which slurs and ties were started and needs completion.
59969
+ * Slurs might span multiple systems, and in such cases we need to create additional
59970
+ * slur/ties in the intermediate and end system.
59971
+ *
59972
+ * @internal
59973
+ *
59974
+ */
59975
+ class SlurRegistry {
59976
+ _staffLookup = new Map();
59977
+ clear() {
59978
+ this._staffLookup.clear();
59979
+ }
59980
+ startMultiSystemSlur(startGlyph) {
59981
+ const staffId = SlurRegistry._staffId(startGlyph.renderer.staff);
59982
+ let container;
59983
+ if (!this._staffLookup.has(staffId)) {
59984
+ container = {
59985
+ startedSlurs: new Map()
59986
+ };
59987
+ this._staffLookup.set(staffId, container);
59988
+ }
59989
+ else {
59990
+ container = this._staffLookup.get(staffId);
59991
+ }
59992
+ container.startedSlurs.set(startGlyph.slurEffectId, { startGlyph });
59993
+ }
59994
+ static _staffId(staff) {
59995
+ return `${staff.modelStaff.index}.${staff.modelStaff.track.index}.${staff.staffId}`;
59996
+ }
59997
+ completeMultiSystemSlur(endGlyph) {
59998
+ const staffId = SlurRegistry._staffId(endGlyph.renderer.staff);
59999
+ if (!this._staffLookup.has(staffId)) {
60000
+ return undefined;
60001
+ }
60002
+ const container = this._staffLookup.get(staffId);
60003
+ if (container.startedSlurs.has(endGlyph.slurEffectId)) {
60004
+ const info = container.startedSlurs.get(endGlyph.slurEffectId);
60005
+ info.endGlyph = endGlyph;
60006
+ return info.startGlyph;
60007
+ }
60008
+ return undefined;
60009
+ }
60010
+ *getAllContinuations(renderer) {
60011
+ const staffId = SlurRegistry._staffId(renderer.staff);
60012
+ if (!this._staffLookup.has(staffId) || renderer.index > 0) {
60013
+ return;
60014
+ }
60015
+ const container = this._staffLookup.get(staffId);
60016
+ for (const g of container.startedSlurs.values()) {
60017
+ if (g.startGlyph.shouldCreateMultiSystemSlur(renderer)) {
60018
+ yield g.startGlyph;
60019
+ }
60020
+ }
60021
+ }
60022
+ }
60023
+
59536
60024
  /**
59537
60025
  * A Staff represents a single line within a StaffSystem.
59538
60026
  * It stores BarRenderer instances created from a given factory.
@@ -59654,7 +60142,6 @@
59654
60142
  this._sharedLayoutData = new Map();
59655
60143
  const lastBar = this.barRenderers[this.barRenderers.length - 1];
59656
60144
  this.barRenderers.splice(this.barRenderers.length - 1, 1);
59657
- this.system.layout.unregisterBarRenderer(this.staffId, lastBar);
59658
60145
  this.topOverflow = 0;
59659
60146
  this.bottomOverflow = 0;
59660
60147
  for (const r of this.barRenderers) {
@@ -59747,23 +60234,23 @@
59747
60234
  // changes in the overflows
59748
60235
  let needsSecondPass = false;
59749
60236
  let topOverflow = this.topOverflow;
59750
- for (let i = 0; i < this.barRenderers.length; i++) {
59751
- this.barRenderers[i].y = this.topPadding + topOverflow;
59752
- if (this.barRenderers[i].finalizeRenderer()) {
60237
+ for (const renderer of this.barRenderers) {
60238
+ renderer.registerMultiSystemSlurs(this.system.layout.slurRegistry.getAllContinuations(renderer));
60239
+ if (renderer.finalizeRenderer()) {
59753
60240
  needsSecondPass = true;
59754
60241
  }
59755
- this.height = Math.max(this.height, this.barRenderers[i].height);
60242
+ this.height = Math.max(this.height, renderer.height);
59756
60243
  }
59757
60244
  // 2nd pass: move renderers to correct position respecting the new overflows
59758
60245
  if (needsSecondPass) {
59759
60246
  topOverflow = this.topOverflow;
59760
60247
  // shift all the renderers to the new position to match required spacing
59761
- for (let i = 0; i < this.barRenderers.length; i++) {
59762
- this.barRenderers[i].y = this.topPadding + topOverflow;
60248
+ for (const renderer of this.barRenderers) {
60249
+ renderer.y = this.topPadding + topOverflow;
59763
60250
  }
59764
60251
  // finalize again (to align ties)
59765
- for (let i = 0; i < this.barRenderers.length; i++) {
59766
- this.barRenderers[i].finalizeRenderer();
60252
+ for (const renderer of this.barRenderers) {
60253
+ renderer.finalizeRenderer();
59767
60254
  }
59768
60255
  }
59769
60256
  if (this.height > 0) {
@@ -60369,6 +60856,7 @@
60369
60856
  if (newBarDisplayScale > barDisplayScale) {
60370
60857
  barDisplayScale = newBarDisplayScale;
60371
60858
  }
60859
+ lastBar.afterReverted();
60372
60860
  }
60373
60861
  this.width -= width;
60374
60862
  this.computedWidth -= width;
@@ -60888,12 +61376,16 @@
60888
61376
  constructor(renderer) {
60889
61377
  this.renderer = renderer;
60890
61378
  }
61379
+ slurRegistry = new SlurRegistry();
60891
61380
  resize() {
60892
61381
  this._lazyPartials.clear();
61382
+ this.slurRegistry.clear();
60893
61383
  this.doResize();
60894
61384
  }
60895
61385
  layoutAndRender() {
60896
61386
  this._lazyPartials.clear();
61387
+ this.slurRegistry.clear();
61388
+ this._barRendererLookup.clear();
60897
61389
  this.profile = Environment.staveProfiles.get(this.renderer.settings.display.staveProfile);
60898
61390
  const score = this.renderer.score;
60899
61391
  this.firstBarIndex = ModelUtils.computeFirstDisplayedBarIndex(score, this.renderer.settings);
@@ -61162,17 +61654,6 @@
61162
61654
  }
61163
61655
  }
61164
61656
  }
61165
- unregisterBarRenderer(key, renderer) {
61166
- if (this._barRendererLookup.has(key)) {
61167
- const lookup = this._barRendererLookup.get(key);
61168
- lookup.delete(renderer.bar.id);
61169
- if (renderer.additionalMultiRestBars) {
61170
- for (const b of renderer.additionalMultiRestBars) {
61171
- lookup.delete(b.id);
61172
- }
61173
- }
61174
- }
61175
- }
61176
61657
  getRendererForBar(key, bar) {
61177
61658
  const barRendererId = bar.id;
61178
61659
  if (this._barRendererLookup.has(key) && this._barRendererLookup.get(key).has(barRendererId)) {
@@ -62161,6 +62642,7 @@
62161
62642
  _voiceContainers = new Map();
62162
62643
  _postBeatGlyphs = new LeftToRightLayoutingGlyphGroup();
62163
62644
  _ties = [];
62645
+ _multiSystemSlurs;
62164
62646
  topEffects;
62165
62647
  bottomEffects;
62166
62648
  get nextRenderer() {
@@ -62313,6 +62795,11 @@
62313
62795
  }
62314
62796
  }
62315
62797
  _appliedLayoutingInfo = 0;
62798
+ afterReverted() {
62799
+ this.staff = undefined;
62800
+ this.registerMultiSystemSlurs(undefined);
62801
+ this.isFinalized = false;
62802
+ }
62316
62803
  afterStaffBarReverted() {
62317
62804
  this.topEffects.afterStaffBarReverted();
62318
62805
  this.bottomEffects.afterStaffBarReverted();
@@ -62358,13 +62845,26 @@
62358
62845
  return true;
62359
62846
  }
62360
62847
  isFinalized = false;
62361
- finalizeRenderer() {
62362
- this.isFinalized = true;
62848
+ registerMultiSystemSlurs(startedTies) {
62849
+ if (!startedTies) {
62850
+ this._multiSystemSlurs = undefined;
62851
+ return;
62852
+ }
62853
+ let ties = undefined;
62854
+ for (const g of startedTies) {
62855
+ const continuation = new ContinuationTieGlyph(g);
62856
+ continuation.renderer = this;
62857
+ continuation.tieDirection = g.tieDirection;
62858
+ if (!ties) {
62859
+ ties = [];
62860
+ }
62861
+ ties.push(continuation);
62862
+ }
62863
+ this._multiSystemSlurs = ties;
62864
+ }
62865
+ _finalizeTies(ties, barTop, barBottom) {
62363
62866
  let didChangeOverflows = false;
62364
- // allow spacing to be used for tie overflows
62365
- const barTop = this.y;
62366
- const barBottom = this.y + this.height;
62367
- for (const t of this._ties) {
62867
+ for (const t of ties) {
62368
62868
  const tie = t;
62369
62869
  tie.doLayout();
62370
62870
  if (t.checkForOverflow) {
@@ -62385,6 +62885,21 @@
62385
62885
  }
62386
62886
  }
62387
62887
  }
62888
+ return didChangeOverflows;
62889
+ }
62890
+ finalizeRenderer() {
62891
+ this.isFinalized = true;
62892
+ let didChangeOverflows = false;
62893
+ // allow spacing to be used for tie overflows
62894
+ const barTop = this.y;
62895
+ const barBottom = this.y + this.height;
62896
+ if (this._finalizeTies(this._ties, barTop, barBottom)) {
62897
+ didChangeOverflows = true;
62898
+ }
62899
+ const multiSystemSlurs = this._multiSystemSlurs;
62900
+ if (multiSystemSlurs && this._finalizeTies(multiSystemSlurs, barTop, barBottom)) {
62901
+ didChangeOverflows = true;
62902
+ }
62388
62903
  const topHeightChanged = this.topEffects.finalizeEffects();
62389
62904
  const bottomHeightChanged = this.bottomEffects.finalizeEffects();
62390
62905
  if (topHeightChanged || bottomHeightChanged) {
@@ -62565,6 +63080,16 @@
62565
63080
  }
62566
63081
  canvas.color = this.resources.mainGlyphColor;
62567
63082
  this._postBeatGlyphs.paint(cx + this.x, cy + this.y, canvas);
63083
+ this._paintMultiSystemSlurs(cx, cy, canvas);
63084
+ }
63085
+ _paintMultiSystemSlurs(cx, cy, canvas) {
63086
+ const multiSystemSlurs = this._multiSystemSlurs;
63087
+ if (!multiSystemSlurs) {
63088
+ return;
63089
+ }
63090
+ for (const slur of multiSystemSlurs) {
63091
+ slur.paint(cx, cy, canvas);
63092
+ }
62568
63093
  }
62569
63094
  paintBackground(cx, cy, canvas) {
62570
63095
  this.layoutingInfo.paint(cx + this.x + this._preBeatGlyphs.x + this._preBeatGlyphs.width, cy + this.y + this.height, canvas);
@@ -64643,6 +65168,13 @@
64643
65168
  }
64644
65169
  }
64645
65170
  else {
65171
+ // clear out staves during re-layout, this info is outdated during
65172
+ // re-layout of the bars
65173
+ for (const r of this._allMasterBarRenderers) {
65174
+ for (const b of r.renderers) {
65175
+ b.afterReverted();
65176
+ }
65177
+ }
64646
65178
  this._systems = [];
64647
65179
  let currentIndex = 0;
64648
65180
  const maxWidth = this._maxWidth;
@@ -65104,7 +65636,7 @@
65104
65636
  // as during layout things are still moving
65105
65637
  let actualLineHeight = this.height;
65106
65638
  const thisStaff = renderer.staff;
65107
- const allStaves = renderer.staff.system.allStaves;
65639
+ const allStaves = thisStaff.system.allStaves;
65108
65640
  let isExtended = false;
65109
65641
  if (this._extendToNextStaff && thisStaff.index < allStaves.length - 1) {
65110
65642
  const nextStaff = allStaves[thisStaff.index + 1];
@@ -65286,7 +65818,7 @@
65286
65818
  }
65287
65819
  // during system fitting it can happen that we have fraction widths
65288
65820
  // but to have lines until the full end-pixel we round up.
65289
- // this way we avoid holes,
65821
+ // this way we avoid holes,
65290
65822
  const lineWidth = this.width;
65291
65823
  // we want the lines to be exactly virtually aligned with the respective Y-position
65292
65824
  // for note heads to align correctly
@@ -66052,375 +66584,23 @@
66052
66584
  /**
66053
66585
  * @internal
66054
66586
  */
66055
- class TieGlyph extends Glyph {
66056
- startBeat;
66057
- endBeat;
66058
- yOffset = 0;
66059
- forEnd;
66060
- startNoteRenderer = null;
66061
- endNoteRenderer = null;
66062
- tieDirection = BeamDirection.Up;
66063
- constructor(startBeat, endBeat, forEnd) {
66064
- super(0, 0);
66065
- this.startBeat = startBeat;
66066
- this.endBeat = endBeat;
66067
- this.forEnd = forEnd;
66068
- }
66069
- _startX = 0;
66070
- _startY = 0;
66071
- _endX = 0;
66072
- _endY = 0;
66073
- _tieHeight = 0;
66074
- _shouldDraw = false;
66075
- _boundingBox;
66076
- get checkForOverflow() {
66077
- return this._boundingBox !== undefined;
66078
- }
66079
- getBoundingBoxTop() {
66080
- if (this._boundingBox) {
66081
- return this._boundingBox.y;
66082
- }
66083
- return this._startY;
66084
- }
66085
- getBoundingBoxBottom() {
66086
- if (this._boundingBox) {
66087
- return this._boundingBox.y + this._boundingBox.h;
66088
- }
66089
- return this._startY;
66090
- }
66091
- doLayout() {
66092
- this.width = 0;
66093
- // TODO fix nullability of start/end beat,
66094
- if (!this.endBeat) {
66095
- this._shouldDraw = false;
66096
- return;
66097
- }
66098
- const startNoteRenderer = this.renderer.scoreRenderer.layout.getRendererForBar(this.renderer.staff.staffId, this.startBeat.voice.bar);
66099
- this.startNoteRenderer = startNoteRenderer;
66100
- const endNoteRenderer = this.renderer.scoreRenderer.layout.getRendererForBar(this.renderer.staff.staffId, this.endBeat.voice.bar);
66101
- this.endNoteRenderer = endNoteRenderer;
66102
- this._startX = 0;
66103
- this._endX = 0;
66104
- this._startY = 0;
66105
- this._endY = 0;
66106
- this.height = 0;
66107
- this._shouldDraw = false;
66108
- // if we are on the tie start, we check if we
66109
- // either can draw till the end note, or we just can draw till the bar end
66110
- this.tieDirection = !startNoteRenderer
66111
- ? this.getBeamDirection(this.endBeat, endNoteRenderer)
66112
- : this.getBeamDirection(this.startBeat, startNoteRenderer);
66113
- if (!this.forEnd && startNoteRenderer) {
66114
- // line break or bar break
66115
- if (startNoteRenderer !== endNoteRenderer) {
66116
- this._startX = startNoteRenderer.x + this.getStartX();
66117
- this._startY = startNoteRenderer.y + this.getStartY() + this.yOffset;
66118
- // line break: to bar end
66119
- if (!endNoteRenderer || startNoteRenderer.staff !== endNoteRenderer.staff) {
66120
- this._endX = startNoteRenderer.x + startNoteRenderer.width;
66121
- this._endY = this._startY;
66122
- }
66123
- else {
66124
- this._endX = endNoteRenderer.x + this.getEndX();
66125
- this._endY = endNoteRenderer.y + this.getEndY() + this.yOffset;
66126
- }
66127
- }
66128
- else {
66129
- this._startX = startNoteRenderer.x + this.getStartX();
66130
- this._endX = endNoteRenderer.x + this.getEndX();
66131
- this._startY = startNoteRenderer.y + this.getStartY() + this.yOffset;
66132
- this._endY = endNoteRenderer.y + this.getEndY() + this.yOffset;
66133
- }
66134
- this._shouldDraw = true;
66135
- }
66136
- else if (!startNoteRenderer || startNoteRenderer.staff !== endNoteRenderer.staff) {
66137
- this._startX = endNoteRenderer.x;
66138
- this._endX = endNoteRenderer.x + this.getEndX();
66139
- this._startY = endNoteRenderer.y + this.getEndY() + this.yOffset;
66140
- this._endY = this._startY;
66141
- this._shouldDraw = true;
66142
- }
66143
- this._boundingBox = undefined;
66144
- if (this._shouldDraw) {
66145
- this.y = Math.min(this._startY, this._endY);
66146
- if (this.shouldDrawBendSlur()) {
66147
- this._tieHeight = 0; // TODO: Bend slur height to be considered?
66148
- }
66149
- else {
66150
- this._tieHeight = this.getTieHeight(this._startX, this._startY, this._endX, this._endY);
66151
- const tieBoundingBox = TieGlyph.calculateActualTieHeight(1, this._startX, this._startY, this._endX, this._endY, this.tieDirection === BeamDirection.Down, this._tieHeight, this.renderer.smuflMetrics.tieMidpointThickness);
66152
- this._boundingBox = tieBoundingBox;
66153
- this.height = tieBoundingBox.h;
66154
- if (this.tieDirection === BeamDirection.Up) {
66155
- // the tie might go above `this.y` due to its shape
66156
- // here we calculate how much this is so we can consider the
66157
- // respective overflow
66158
- const overlap = this.y - tieBoundingBox.y;
66159
- if (overlap > 0) {
66160
- this.y -= overlap;
66161
- }
66162
- }
66163
- }
66164
- }
66165
- }
66166
- paint(cx, cy, canvas) {
66167
- if (this._shouldDraw) {
66168
- if (this.shouldDrawBendSlur()) {
66169
- TieGlyph.drawBendSlur(canvas, cx + this._startX, cy + this._startY, cx + this._endX, cy + this._endY, this.tieDirection === BeamDirection.Down, 1, this.renderer.smuflMetrics.tieHeight);
66170
- }
66171
- else {
66172
- 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);
66173
- }
66174
- }
66175
- }
66176
- shouldDrawBendSlur() {
66177
- return false;
66178
- }
66179
- getTieHeight(_startX, _startY, _endX, _endY) {
66180
- return this.renderer.smuflMetrics.tieHeight;
66181
- }
66182
- getBeamDirection(_beat, _noteRenderer) {
66183
- return BeamDirection.Down;
66184
- }
66185
- getStartY() {
66186
- return 0;
66187
- }
66188
- getEndY() {
66189
- return 0;
66190
- }
66191
- getStartX() {
66192
- return 0;
66193
- }
66194
- getEndX() {
66195
- return 0;
66196
- }
66197
- static calculateActualTieHeight(scale, x1, y1, x2, y2, down, offset, size) {
66198
- const cp = TieGlyph._computeBezierControlPoints(scale, x1, y1, x2, y2, down, offset, size);
66199
- // For a musical tie/slur, the extrema occur predictably near the midpoint
66200
- // Evaluate at midpoint (t=0.5) and check endpoints
66201
- const p0x = cp[0];
66202
- const p0y = cp[1];
66203
- const c1x = cp[2];
66204
- const c1y = cp[3];
66205
- const c2x = cp[4];
66206
- const c2y = cp[5];
66207
- const p1x = cp[6];
66208
- const p1y = cp[7];
66209
- // Evaluate at t=0.5 for midpoint
66210
- const midX = 0.125 * p0x + 0.375 * c1x + 0.375 * c2x + 0.125 * p1x;
66211
- const midY = 0.125 * p0y + 0.375 * c1y + 0.375 * c2y + 0.125 * p1y;
66212
- // Bounds are simply min/max of start, end, and midpoint
66213
- const xMin = Math.min(p0x, p1x, midX);
66214
- const xMax = Math.max(p0x, p1x, midX);
66215
- let yMin = Math.min(p0y, p1y, midY);
66216
- let yMax = Math.max(p0y, p1y, midY);
66217
- // Account for thickness of the tie/slur
66218
- if (down) {
66219
- yMax += size;
66220
- }
66221
- else {
66222
- yMin -= size;
66223
- }
66224
- const b = new Bounds();
66225
- b.x = xMin;
66226
- b.y = yMin;
66227
- b.w = xMax - xMin;
66228
- b.h = yMax - yMin;
66229
- return b;
66230
- }
66231
- static _computeBezierControlPoints(scale, x1, y1, x2, y2, down, offset, size) {
66232
- if (x1 === x2 && y1 === y2) {
66233
- return [];
66234
- }
66235
- // ensure endX > startX
66236
- if (x2 < x1) {
66237
- let t = x1;
66238
- x1 = x2;
66239
- x2 = t;
66240
- t = y1;
66241
- y1 = y2;
66242
- y2 = t;
66243
- }
66244
- //
66245
- // calculate control points
66246
- //
66247
- offset *= scale;
66248
- size *= scale;
66249
- if (down) {
66250
- offset *= -1;
66251
- size *= -1;
66252
- }
66253
- if (scale >= 1) {
66254
- size *= 1.2;
66255
- }
66256
- // calculate control points on horizontal axis then rotate:
66257
- /*
66258
- cp1x/cpy1 cp2x/cpy2
66259
- *----------------*
66260
- / \
66261
- / \
66262
- x1/y1 * * x2/y2
66263
-
66264
- cp3 and cp4 are simply with lower height
66265
- */
66266
- const dY = y2 - y1;
66267
- const dX = x2 - x1;
66268
- const length = Math.sqrt(dX * dX + dY * dY);
66269
- let cp1x = x1 + length * 0.25;
66270
- let cp1y = y1 - offset;
66271
- let cp2x = x1 + length * 0.75;
66272
- let cp2y = y1 - offset;
66273
- let cp3x = x1 + length * 0.75;
66274
- let cp3y = y1 - offset - size;
66275
- let cp4x = x1 + length * 0.25;
66276
- let cp4y = y1 - offset - size;
66277
- const angle = Math.atan2(dY, dX);
66278
- [cp1x, cp1y] = TieGlyph._rotate(cp1x, cp1y, x1, y1, angle);
66279
- [cp2x, cp2y] = TieGlyph._rotate(cp2x, cp2y, x1, y1, angle);
66280
- [cp3x, cp3y] = TieGlyph._rotate(cp3x, cp3y, x1, y1, angle);
66281
- [cp4x, cp4y] = TieGlyph._rotate(cp4x, cp4y, x1, y1, angle);
66282
- return [x1, y1, cp1x, cp1y, cp2x, cp2y, x2, y2, cp3x, cp3y, cp4x, cp4y, x1, y1];
66283
- }
66284
- static _rotate(x, y, rotateX, rotateY, angle) {
66285
- const dx = x - rotateX;
66286
- const dy = y - rotateY;
66287
- const rx = dx * Math.cos(angle) - dy * Math.sin(angle);
66288
- const ry = dx * Math.sin(angle) + dy * Math.cos(angle);
66289
- return [rotateX + rx, rotateY + ry];
66290
- }
66291
- static paintTie(canvas, scale, x1, y1, x2, y2, down /*= false*/, offset /*= 22*/, size /*= 4*/) {
66292
- const cps = TieGlyph._computeBezierControlPoints(scale, x1, y1, x2, y2, down, offset, size);
66293
- canvas.beginPath();
66294
- canvas.moveTo(cps[0], cps[1]);
66295
- canvas.bezierCurveTo(cps[2], cps[3], cps[4], cps[5], cps[6], cps[7]);
66296
- canvas.bezierCurveTo(cps[8], cps[9], cps[10], cps[11], cps[12], cps[13]);
66297
- canvas.closePath();
66298
- canvas.fill();
66299
- }
66300
- static calculateBendSlurTopY(x1, y1, x2, y2, down, scale, bendSlurHeight) {
66301
- let normalVectorX = y2 - y1;
66302
- let normalVectorY = x2 - x1;
66303
- const length = Math.sqrt(normalVectorX * normalVectorX + normalVectorY * normalVectorY);
66304
- if (down) {
66305
- normalVectorX *= -1;
66306
- }
66307
- else {
66308
- normalVectorY *= -1;
66309
- }
66310
- // make to unit vector
66311
- normalVectorX /= length;
66312
- normalVectorY /= length;
66313
- let offset = bendSlurHeight * scale;
66314
- if (x2 - x1 < 20) {
66315
- offset /= 2;
66316
- }
66317
- const centerY = (y2 + y1) / 2;
66318
- const cp1Y = centerY + offset * normalVectorY;
66319
- return cp1Y;
66320
- }
66321
- static drawBendSlur(canvas, x1, y1, x2, y2, down, scale, bendSlurHeight, slurText) {
66322
- let normalVectorX = y2 - y1;
66323
- let normalVectorY = x2 - x1;
66324
- const length = Math.sqrt(normalVectorX * normalVectorX + normalVectorY * normalVectorY);
66325
- if (down) {
66326
- normalVectorX *= -1;
66327
- }
66328
- else {
66329
- normalVectorY *= -1;
66330
- }
66331
- // make to unit vector
66332
- normalVectorX /= length;
66333
- normalVectorY /= length;
66334
- // center of connection
66335
- // TODO: should be 1/3
66336
- const centerX = (x2 + x1) / 2;
66337
- const centerY = (y2 + y1) / 2;
66338
- let offset = bendSlurHeight * scale;
66339
- if (x2 - x1 < 20) {
66340
- offset /= 2;
66341
- }
66342
- const cp1X = centerX + offset * normalVectorX;
66343
- const cp1Y = centerY + offset * normalVectorY;
66344
- canvas.beginPath();
66345
- canvas.moveTo(x1, y1);
66346
- canvas.lineTo(cp1X, cp1Y);
66347
- canvas.lineTo(x2, y2);
66348
- canvas.stroke();
66349
- if (slurText) {
66350
- const w = canvas.measureText(slurText).width;
66351
- const textOffset = down ? 0 : -canvas.font.size;
66352
- canvas.fillText(slurText, cp1X - w / 2, cp1Y + textOffset);
66353
- }
66354
- }
66355
- }
66356
-
66357
- /**
66358
- * @internal
66359
- */
66360
- class NumberedTieGlyph extends TieGlyph {
66361
- startNote;
66362
- endNote;
66363
- constructor(startNote, endNote, forEnd = false) {
66364
- super(!startNote ? null : startNote.beat, !endNote ? null : endNote.beat, forEnd);
66365
- this.startNote = startNote;
66366
- this.endNote = endNote;
66367
- }
66368
- get _isLeftHandTap() {
66369
- return this.startNote === this.endNote;
66370
- }
66587
+ class NumberedTieGlyph extends NoteTieGlyph {
66371
66588
  shouldDrawBendSlur() {
66372
66589
  return (this.renderer.settings.notation.extendBendArrowsOnTiedNotes &&
66373
66590
  !!this.startNote.bendOrigin &&
66374
66591
  this.startNote.isTieOrigin);
66375
66592
  }
66376
- doLayout() {
66377
- super.doLayout();
66378
- }
66379
- getBeamDirection(_beat, _noteRenderer) {
66593
+ calculateTieDirection() {
66380
66594
  return BeamDirection.Up;
66381
66595
  }
66382
- getStartY() {
66383
- return this.startNoteRenderer.getNoteY(this.startNote, NoteYPosition.Top);
66384
- }
66385
- getEndY() {
66386
- return this.getStartY();
66387
- }
66388
- getStartX() {
66389
- if (this._isLeftHandTap) {
66390
- return this.getEndX() - this.startNoteRenderer.smuflMetrics.leftHandTabTieWidth;
66391
- }
66392
- return this.startNoteRenderer.getNoteX(this.startNote, NoteXPosition.Center);
66393
- }
66394
- getEndX() {
66395
- if (this._isLeftHandTap) {
66396
- return this.endNoteRenderer.getNoteX(this.endNote, NoteXPosition.Left);
66397
- }
66398
- return this.endNoteRenderer.getNoteX(this.endNote, NoteXPosition.Center);
66399
- }
66400
66596
  }
66401
66597
 
66402
66598
  /**
66403
66599
  * @internal
66404
66600
  */
66405
- class TabTieGlyph extends TieGlyph {
66406
- startNote;
66407
- endNote;
66408
- constructor(startNote, endNote, forEnd = false) {
66409
- super(startNote.beat, endNote.beat, forEnd);
66410
- this.startNote = startNote;
66411
- this.endNote = endNote;
66412
- }
66413
- get _isLeftHandTap() {
66414
- return this.startNote === this.endNote;
66415
- }
66416
- getTieHeight(startX, startY, endX, endY) {
66417
- if (this._isLeftHandTap) {
66418
- return this.startNoteRenderer.smuflMetrics.tieHeight;
66419
- }
66420
- return super.getTieHeight(startX, startY, endX, endY);
66421
- }
66422
- getBeamDirection(_beat, _noteRenderer) {
66423
- if (this._isLeftHandTap) {
66601
+ class TabTieGlyph extends NoteTieGlyph {
66602
+ calculateTieDirection() {
66603
+ if (this.isLeftHandTap) {
66424
66604
  return BeamDirection.Up;
66425
66605
  }
66426
66606
  return TabTieGlyph.getBeamDirectionForNote(this.startNote);
@@ -66428,54 +66608,25 @@
66428
66608
  static getBeamDirectionForNote(note) {
66429
66609
  return note.string > 3 ? BeamDirection.Up : BeamDirection.Down;
66430
66610
  }
66431
- getStartY() {
66432
- if (this._isLeftHandTap) {
66433
- return this.startNoteRenderer.getNoteY(this.startNote, NoteYPosition.Center);
66434
- }
66435
- if (this.tieDirection === BeamDirection.Up) {
66436
- return this.startNoteRenderer.getNoteY(this.startNote, NoteYPosition.Top);
66437
- }
66438
- return this.startNoteRenderer.getNoteY(this.startNote, NoteYPosition.Bottom);
66439
- }
66440
- getEndY() {
66441
- return this.getStartY();
66442
- }
66443
- getStartX() {
66444
- if (this._isLeftHandTap) {
66445
- return this.getEndX() - this.renderer.smuflMetrics.leftHandTabTieWidth;
66446
- }
66447
- return this.startNoteRenderer.getNoteX(this.startNote, NoteXPosition.Center);
66448
- }
66449
- getEndX() {
66450
- if (this._isLeftHandTap) {
66451
- return this.endNoteRenderer.getNoteX(this.endNote, NoteXPosition.Left);
66452
- }
66453
- return this.endNoteRenderer.getNoteX(this.endNote, NoteXPosition.Center);
66454
- }
66455
66611
  }
66456
66612
 
66457
66613
  /**
66458
66614
  * @internal
66459
66615
  */
66460
- class NumberedSlurGlyph extends TabTieGlyph {
66461
- _direction;
66616
+ class TabSlurGlyph extends TabTieGlyph {
66462
66617
  _forSlide;
66463
- constructor(startNote, endNote, forSlide, forEnd = false) {
66464
- super(startNote, endNote, forEnd);
66465
- this._direction = BeamDirection.Up;
66618
+ constructor(slurEffectId, startNote, endNote, forSlide, forEnd) {
66619
+ super(slurEffectId, startNote, endNote, forEnd);
66466
66620
  this._forSlide = forSlide;
66467
66621
  }
66468
66622
  getTieHeight(startX, _startY, endX, _endY) {
66469
- return Math.log(endX - startX + 1) * this.renderer.settings.notation.slurHeight / 2;
66623
+ return (Math.log(endX - startX + 1) * this.renderer.settings.notation.slurHeight) / 2;
66470
66624
  }
66471
66625
  tryExpand(startNote, endNote, forSlide, forEnd) {
66472
66626
  // same type required
66473
66627
  if (this._forSlide !== forSlide) {
66474
66628
  return false;
66475
66629
  }
66476
- if (this.forEnd !== forEnd) {
66477
- return false;
66478
- }
66479
66630
  // same start and endbeat
66480
66631
  if (this.startNote.beat.id !== startNote.beat.id) {
66481
66632
  return false;
@@ -66483,41 +66634,43 @@
66483
66634
  if (this.endNote.beat.id !== endNote.beat.id) {
66484
66635
  return false;
66485
66636
  }
66637
+ const isForEnd = this.renderer === this.lookupEndBeatRenderer();
66638
+ if (isForEnd !== forEnd) {
66639
+ return false;
66640
+ }
66641
+ // same draw direction
66642
+ if (this.tieDirection !== TabTieGlyph.getBeamDirectionForNote(startNote)) {
66643
+ return false;
66644
+ }
66486
66645
  // if we can expand, expand in correct direction
66487
- switch (this._direction) {
66646
+ switch (this.tieDirection) {
66488
66647
  case BeamDirection.Up:
66489
66648
  if (startNote.realValue > this.startNote.realValue) {
66490
66649
  this.startNote = startNote;
66491
- this.startBeat = startNote.beat;
66492
66650
  }
66493
66651
  if (endNote.realValue > this.endNote.realValue) {
66494
66652
  this.endNote = endNote;
66495
- this.endBeat = endNote.beat;
66496
66653
  }
66497
66654
  break;
66498
66655
  case BeamDirection.Down:
66499
66656
  if (startNote.realValue < this.startNote.realValue) {
66500
66657
  this.startNote = startNote;
66501
- this.startBeat = startNote.beat;
66502
66658
  }
66503
66659
  if (endNote.realValue < this.endNote.realValue) {
66504
66660
  this.endNote = endNote;
66505
- this.endBeat = endNote.beat;
66506
66661
  }
66507
66662
  break;
66508
66663
  }
66509
66664
  return true;
66510
66665
  }
66511
- paint(cx, cy, canvas) {
66512
- const startNoteRenderer = this.renderer.scoreRenderer.layout.getRendererForBar(this.renderer.staff.staffId, this.startBeat.voice.bar);
66513
- const direction = this.getBeamDirection(this.startBeat, startNoteRenderer);
66514
- const slurId = `numbered.slur.${this.startNote.beat.id}.${this.endNote.beat.id}.${direction}`;
66515
- const renderer = this.renderer;
66516
- const isSlurRendered = renderer.staff.getSharedLayoutData(slurId, false);
66517
- if (!isSlurRendered) {
66518
- renderer.staff.setSharedLayoutData(slurId, true);
66519
- super.paint(cx, cy, canvas);
66520
- }
66666
+ }
66667
+
66668
+ /**
66669
+ * @internal
66670
+ */
66671
+ class NumberedSlurGlyph extends TabSlurGlyph {
66672
+ calculateTieDirection() {
66673
+ return BeamDirection.Up;
66521
66674
  }
66522
66675
  }
66523
66676
 
@@ -66525,23 +66678,31 @@
66525
66678
  * @internal
66526
66679
  */
66527
66680
  class NumberedBeatContainerGlyph extends BeatContainerGlyph {
66681
+ _slurs = new Map();
66528
66682
  _effectSlurs = [];
66683
+ doLayout() {
66684
+ this._slurs.clear();
66685
+ this._effectSlurs = [];
66686
+ super.doLayout();
66687
+ }
66529
66688
  createTies(n) {
66530
66689
  // create a tie if any effect requires it
66531
66690
  if (!n.isVisible) {
66532
66691
  return;
66533
66692
  }
66534
- if (n.isTieOrigin && n.tieDestination.isVisible) {
66535
- const tie = new NumberedTieGlyph(n, n.tieDestination, false);
66693
+ if (n.isTieOrigin && n.tieDestination.isVisible && !this._slurs.has('numbered.tie')) {
66694
+ const tie = new NumberedTieGlyph(`numbered.tie.${n.beat.id}`, n, n.tieDestination, false);
66536
66695
  this.addTie(tie);
66696
+ this._slurs.set(tie.slurEffectId, tie);
66537
66697
  }
66538
66698
  if (n.isTieDestination) {
66539
- const tie = new NumberedTieGlyph(n.tieOrigin, n, true);
66699
+ const tie = new NumberedTieGlyph(`numbered.tie.${n.tieOrigin.beat.id}`, n.tieOrigin, n, true);
66540
66700
  this.addTie(tie);
66541
66701
  }
66542
- if (n.isLeftHandTapped && !n.isHammerPullDestination) {
66543
- const tapSlur = new NumberedTieGlyph(n, n, false);
66702
+ if (n.isLeftHandTapped && !n.isHammerPullDestination && !this._slurs.has(`numbered.tie.leftHandTap.${n.beat.id}`)) {
66703
+ const tapSlur = new NumberedTieGlyph(`numbered.tie.leftHandTap.${n.beat.id}`, n, n, false);
66544
66704
  this.addTie(tapSlur);
66705
+ this._slurs.set(tapSlur.slurEffectId, tapSlur);
66545
66706
  }
66546
66707
  // start effect slur on first beat
66547
66708
  if (n.isEffectSlurOrigin && n.effectSlurDestination) {
@@ -66553,9 +66714,11 @@
66553
66714
  }
66554
66715
  }
66555
66716
  if (!expanded) {
66556
- const effectSlur = new NumberedSlurGlyph(n, n.effectSlurDestination, false, false);
66717
+ const effectSlur = new NumberedSlurGlyph(`numbered.slur.effect`, n, n.effectSlurDestination, false, false);
66557
66718
  this._effectSlurs.push(effectSlur);
66558
66719
  this.addTie(effectSlur);
66720
+ this._slurs.set(effectSlur.slurEffectId, effectSlur);
66721
+ this._slurs.set('numbered.slur.effect', effectSlur);
66559
66722
  }
66560
66723
  }
66561
66724
  // end effect slur on last beat
@@ -66568,9 +66731,11 @@
66568
66731
  }
66569
66732
  }
66570
66733
  if (!expanded) {
66571
- const effectSlur = new NumberedSlurGlyph(n.effectSlurOrigin, n, false, true);
66734
+ const effectSlur = new NumberedSlurGlyph(`numbered.slur.effect`, n.effectSlurOrigin, n, false, true);
66572
66735
  this._effectSlurs.push(effectSlur);
66573
66736
  this.addTie(effectSlur);
66737
+ this._slurs.set(effectSlur.slurEffectId, effectSlur);
66738
+ this._slurs.set('numbered.slur.effect', effectSlur);
66574
66739
  }
66575
66740
  }
66576
66741
  }
@@ -66963,10 +67128,11 @@
66963
67128
  if (sr.shortestDuration < this.container.beat.duration) {
66964
67129
  sr.shortestDuration = this.container.beat.duration;
66965
67130
  }
66966
- const glyphY = sr.getLineY(sr.getNoteLine());
66967
67131
  if (!this.container.beat.isEmpty) {
67132
+ const glyphY = sr.getLineY(0);
66968
67133
  let numberWithinOctave = '0';
66969
67134
  if (this.container.beat.notes.length > 0) {
67135
+ const note = this.container.beat.notes[0];
66970
67136
  const kst = this.renderer.bar.keySignatureType;
66971
67137
  const ks = this.renderer.bar.keySignature;
66972
67138
  const ksi = ks + 7;
@@ -66974,7 +67140,6 @@
66974
67140
  ? NumberedBeatGlyph.minorKeySignatureOneValues
66975
67141
  : NumberedBeatGlyph.majorKeySignatureOneValues;
66976
67142
  const oneNoteValue = oneNoteValues[ksi];
66977
- const note = this.container.beat.notes[0];
66978
67143
  if (note.isDead) {
66979
67144
  numberWithinOctave = 'X';
66980
67145
  }
@@ -67021,7 +67186,7 @@
67021
67186
  // Note dots
67022
67187
  if (this.container.beat.dots > 0 && this.container.beat.duration >= Duration.Quarter) {
67023
67188
  for (let i = 0; i < this.container.beat.dots; i++) {
67024
- const dot = new AugmentationDotGlyph(0, sr.getLineY(0));
67189
+ const dot = new AugmentationDotGlyph(0, glyphY);
67025
67190
  dot.renderer = this.renderer;
67026
67191
  this.addEffect(dot);
67027
67192
  }
@@ -67049,7 +67214,7 @@
67049
67214
  numberOfQuarterNotes += numberOfAddedQuarters;
67050
67215
  }
67051
67216
  for (let i = 0; i < numberOfQuarterNotes - 1; i++) {
67052
- const dash = new NumberedDashGlyph(0, sr.getLineY(0), this.container.beat);
67217
+ const dash = new NumberedDashGlyph(0, glyphY, this.container.beat);
67053
67218
  dash.renderer = this.renderer;
67054
67219
  this.addNormal(dash);
67055
67220
  }
@@ -67519,7 +67684,7 @@
67519
67684
  }
67520
67685
  }
67521
67686
  }
67522
- getNoteLine() {
67687
+ getNoteLine(_note) {
67523
67688
  return 0;
67524
67689
  }
67525
67690
  get tupletOffset() {
@@ -69734,87 +69899,128 @@
69734
69899
  * @internal
69735
69900
  */
69736
69901
  class ScoreLegatoGlyph extends TieGlyph {
69737
- constructor(startBeat, endBeat, forEnd = false) {
69738
- super(startBeat, endBeat, forEnd);
69902
+ startBeat;
69903
+ endBeat;
69904
+ startBeatRenderer = null;
69905
+ endBeatRenderer = null;
69906
+ constructor(slurEffectId, startBeat, endBeat, forEnd) {
69907
+ super(slurEffectId, forEnd);
69908
+ this.startBeat = startBeat;
69909
+ this.endBeat = endBeat;
69739
69910
  }
69740
69911
  doLayout() {
69741
69912
  super.doLayout();
69742
69913
  }
69743
- getBeamDirection(beat, noteRenderer) {
69744
- if (beat.isRest) {
69914
+ lookupStartBeatRenderer() {
69915
+ if (!this.startBeatRenderer) {
69916
+ this.startBeatRenderer = this.renderer.scoreRenderer.layout.getRendererForBar(this.renderer.staff.staffId, this.startBeat.voice.bar);
69917
+ }
69918
+ return this.startBeatRenderer;
69919
+ }
69920
+ lookupEndBeatRenderer() {
69921
+ if (!this.endBeatRenderer) {
69922
+ this.endBeatRenderer = this.renderer.scoreRenderer.layout.getRendererForBar(this.renderer.staff.staffId, this.endBeat.voice.bar);
69923
+ }
69924
+ return this.endBeatRenderer;
69925
+ }
69926
+ shouldDrawBendSlur() {
69927
+ return false;
69928
+ }
69929
+ calculateTieDirection() {
69930
+ if (this.startBeat.isRest) {
69745
69931
  return BeamDirection.Up;
69746
69932
  }
69747
69933
  // invert direction (if stems go up, ties go down to not cross them)
69748
- switch (noteRenderer.getBeatDirection(beat)) {
69934
+ switch (this.lookupStartBeatRenderer().getBeatDirection(this.startBeat)) {
69749
69935
  case BeamDirection.Up:
69750
69936
  return BeamDirection.Down;
69751
69937
  default:
69752
69938
  return BeamDirection.Up;
69753
69939
  }
69754
69940
  }
69755
- getStartY() {
69941
+ calculateStartX() {
69942
+ const startBeatRenderer = this.lookupStartBeatRenderer();
69943
+ return startBeatRenderer.x + startBeatRenderer.getBeatX(this.startBeat, BeatXPosition.MiddleNotes);
69944
+ }
69945
+ calculateStartY() {
69946
+ const startBeatRenderer = this.lookupStartBeatRenderer();
69756
69947
  if (this.startBeat.isRest) {
69757
- // below all lines
69758
- return this.startNoteRenderer.getScoreY(9);
69948
+ switch (this.tieDirection) {
69949
+ case BeamDirection.Up:
69950
+ return (startBeatRenderer.y +
69951
+ startBeatRenderer.getBeatContainer(this.startBeat).onNotes.getBoundingBoxTop());
69952
+ default:
69953
+ return (startBeatRenderer.y +
69954
+ startBeatRenderer.getBeatContainer(this.startBeat).onNotes.getBoundingBoxBottom());
69955
+ }
69759
69956
  }
69760
69957
  switch (this.tieDirection) {
69761
69958
  case BeamDirection.Up:
69762
69959
  // below lowest note
69763
- return this.startNoteRenderer.getNoteY(this.startBeat.maxNote, NoteYPosition.Top);
69960
+ return startBeatRenderer.y + startBeatRenderer.getNoteY(this.startBeat.maxNote, NoteYPosition.Top);
69764
69961
  default:
69765
- return this.startNoteRenderer.getNoteY(this.startBeat.minNote, NoteYPosition.Bottom);
69962
+ return startBeatRenderer.y + startBeatRenderer.getNoteY(this.startBeat.minNote, NoteYPosition.Bottom);
69963
+ }
69964
+ }
69965
+ calculateEndX() {
69966
+ const endBeatRenderer = this.lookupEndBeatRenderer();
69967
+ if (!endBeatRenderer) {
69968
+ return this.calculateStartX() + this.renderer.smuflMetrics.leftHandTabTieWidth;
69766
69969
  }
69970
+ const endBeamDirection = endBeatRenderer.getBeatDirection(this.endBeat);
69971
+ return (endBeatRenderer.x +
69972
+ endBeatRenderer.getBeatX(this.endBeat, this.endBeat.duration > Duration.Whole && endBeamDirection === this.tieDirection
69973
+ ? BeatXPosition.Stem
69974
+ : BeatXPosition.MiddleNotes));
69767
69975
  }
69768
- getEndY() {
69769
- const endNoteScoreRenderer = this.endNoteRenderer;
69976
+ caclculateEndY() {
69977
+ const endBeatRenderer = this.lookupEndBeatRenderer();
69978
+ if (!endBeatRenderer) {
69979
+ return this.calculateStartY();
69980
+ }
69770
69981
  if (this.endBeat.isRest) {
69771
69982
  switch (this.tieDirection) {
69772
69983
  case BeamDirection.Up:
69773
- return endNoteScoreRenderer.getScoreY(9);
69984
+ return (endBeatRenderer.y + endBeatRenderer.getBeatContainer(this.endBeat).onNotes.getBoundingBoxTop());
69774
69985
  default:
69775
- return endNoteScoreRenderer.getScoreY(0);
69986
+ return (endBeatRenderer.y +
69987
+ endBeatRenderer.getBeatContainer(this.endBeat).onNotes.getBoundingBoxBottom());
69776
69988
  }
69777
69989
  }
69778
- const startBeamDirection = this.startNoteRenderer.getBeatDirection(this.startBeat);
69779
- const endBeamDirection = endNoteScoreRenderer.getBeatDirection(this.endBeat);
69990
+ const startBeamDirection = this.lookupStartBeatRenderer().getBeatDirection(this.startBeat);
69991
+ const endBeamDirection = endBeatRenderer.getBeatDirection(this.endBeat);
69780
69992
  if (startBeamDirection !== endBeamDirection && this.startBeat.graceType === GraceType.None) {
69781
69993
  if (endBeamDirection === this.tieDirection) {
69782
69994
  switch (this.tieDirection) {
69783
69995
  case BeamDirection.Up:
69784
69996
  // stem upper end
69785
- return endNoteScoreRenderer.getNoteY(this.endBeat.maxNote, NoteYPosition.TopWithStem);
69997
+ return (endBeatRenderer.y +
69998
+ endBeatRenderer.getNoteY(this.endBeat.maxNote, NoteYPosition.TopWithStem));
69786
69999
  default:
69787
70000
  // stem lower end
69788
- return endNoteScoreRenderer.getNoteY(this.endBeat.minNote, NoteYPosition.BottomWithStem);
70001
+ return (endBeatRenderer.y +
70002
+ endBeatRenderer.getNoteY(this.endBeat.minNote, NoteYPosition.BottomWithStem));
69789
70003
  }
69790
70004
  }
69791
70005
  switch (this.tieDirection) {
69792
70006
  case BeamDirection.Up:
69793
70007
  // stem upper end
69794
- return endNoteScoreRenderer.getNoteY(this.endBeat.maxNote, NoteYPosition.BottomWithStem);
70008
+ return (endBeatRenderer.y +
70009
+ endBeatRenderer.getNoteY(this.endBeat.maxNote, NoteYPosition.BottomWithStem));
69795
70010
  default:
69796
70011
  // stem lower end
69797
- return endNoteScoreRenderer.getNoteY(this.endBeat.minNote, NoteYPosition.TopWithStem);
70012
+ return (endBeatRenderer.y + endBeatRenderer.getNoteY(this.endBeat.minNote, NoteYPosition.TopWithStem));
69798
70013
  }
69799
70014
  }
69800
70015
  switch (this.tieDirection) {
69801
70016
  case BeamDirection.Up:
69802
70017
  // below lowest note
69803
- return endNoteScoreRenderer.getNoteY(this.endBeat.maxNote, NoteYPosition.Top);
70018
+ return endBeatRenderer.y + endBeatRenderer.getNoteY(this.endBeat.maxNote, NoteYPosition.Top);
69804
70019
  default:
69805
70020
  // above highest note
69806
- return endNoteScoreRenderer.getNoteY(this.endBeat.minNote, NoteYPosition.Bottom);
70021
+ return endBeatRenderer.y + endBeatRenderer.getNoteY(this.endBeat.minNote, NoteYPosition.Bottom);
69807
70022
  }
69808
70023
  }
69809
- getStartX() {
69810
- return this.startNoteRenderer.getBeatX(this.startBeat, BeatXPosition.MiddleNotes);
69811
- }
69812
- getEndX() {
69813
- const endBeamDirection = this.endNoteRenderer.getBeatDirection(this.endBeat);
69814
- return this.endNoteRenderer.getBeatX(this.endBeat, this.endBeat.duration > Duration.Whole && endBeamDirection === this.tieDirection
69815
- ? BeatXPosition.Stem
69816
- : BeatXPosition.MiddleNotes);
69817
- }
69818
70024
  }
69819
70025
 
69820
70026
  /**
@@ -70014,142 +70220,106 @@
70014
70220
  /**
70015
70221
  * @internal
70016
70222
  */
70017
- class ScoreSlurGlyph extends ScoreLegatoGlyph {
70018
- _startNote;
70019
- _endNote;
70020
- constructor(startNote, endNote, forEnd = false) {
70021
- super(startNote.beat, endNote.beat, forEnd);
70022
- this._startNote = startNote;
70023
- this._endNote = endNote;
70223
+ class ScoreTieGlyph extends NoteTieGlyph {
70224
+ shouldDrawBendSlur() {
70225
+ return (this.renderer.settings.notation.extendBendArrowsOnTiedNotes &&
70226
+ !!this.startNote.bendOrigin &&
70227
+ this.startNote.isTieOrigin);
70228
+ }
70229
+ calculateStartX() {
70230
+ if (this.isLeftHandTap) {
70231
+ return this.calculateEndX() - this.renderer.smuflMetrics.leftHandTabTieWidth;
70232
+ }
70233
+ return this.renderer.x + this.renderer.getBeatX(this.startNote.beat, BeatXPosition.PostNotes);
70234
+ }
70235
+ calculateEndX() {
70236
+ const endNoteRenderer = this.lookupEndBeatRenderer();
70237
+ if (!endNoteRenderer) {
70238
+ return this.calculateStartX() + this.renderer.smuflMetrics.leftHandTabTieWidth;
70239
+ }
70240
+ if (this.isLeftHandTap) {
70241
+ return endNoteRenderer.x + endNoteRenderer.getNoteX(this.endNote, NoteXPosition.Left);
70242
+ }
70243
+ return endNoteRenderer.x + endNoteRenderer.getBeatX(this.endNote.beat, BeatXPosition.PreNotes);
70024
70244
  }
70245
+ }
70246
+
70247
+ /**
70248
+ * @internal
70249
+ */
70250
+ class ScoreSlurGlyph extends ScoreTieGlyph {
70025
70251
  getTieHeight(startX, _startY, endX, _endY) {
70026
- return Math.log2(endX - startX + 1) * this.renderer.settings.notation.slurHeight / 2;
70252
+ return (Math.log2(endX - startX + 1) * this.renderer.settings.notation.slurHeight) / 2;
70027
70253
  }
70028
- getStartY() {
70254
+ calculateStartX() {
70255
+ return (this.renderer.x +
70256
+ (this._isStartCentered()
70257
+ ? this.renderer.getBeatX(this.startNote.beat, BeatXPosition.MiddleNotes)
70258
+ : this.renderer.getNoteX(this.startNote, NoteXPosition.Right)));
70259
+ }
70260
+ calculateStartY() {
70029
70261
  if (this._isStartCentered()) {
70030
70262
  switch (this.tieDirection) {
70031
70263
  case BeamDirection.Up:
70032
- // below lowest note
70033
- return this.startNoteRenderer.getNoteY(this._startNote, NoteYPosition.Top);
70264
+ return this.renderer.y + this.renderer.getNoteY(this.startNote, NoteYPosition.Top);
70034
70265
  default:
70035
- return this.startNoteRenderer.getNoteY(this._startNote, NoteYPosition.Bottom);
70266
+ return this.renderer.y + this.renderer.getNoteY(this.startNote, NoteYPosition.Bottom);
70267
+ }
70268
+ }
70269
+ return this.renderer.y + this.renderer.getNoteY(this.startNote, NoteYPosition.Center);
70270
+ }
70271
+ calculateEndX() {
70272
+ const endNoteRenderer = this.lookupEndBeatRenderer();
70273
+ if (!endNoteRenderer) {
70274
+ return this.calculateStartX() + this.renderer.smuflMetrics.leftHandTabTieWidth;
70275
+ }
70276
+ if (this._isEndCentered()) {
70277
+ if (this._isEndOnStem()) {
70278
+ return endNoteRenderer.x + endNoteRenderer.getBeatX(this.endNote.beat, BeatXPosition.Stem);
70036
70279
  }
70280
+ return endNoteRenderer.x + endNoteRenderer.getNoteX(this.endNote, NoteXPosition.Center);
70037
70281
  }
70038
- return this.startNoteRenderer.getNoteY(this._startNote, NoteYPosition.Center);
70282
+ return endNoteRenderer.x + endNoteRenderer.getBeatX(this.endNote.beat, BeatXPosition.PreNotes);
70039
70283
  }
70040
- getEndY() {
70284
+ caclculateEndY() {
70285
+ const endNoteRenderer = this.lookupEndBeatRenderer();
70286
+ if (!endNoteRenderer) {
70287
+ return this.calculateStartY();
70288
+ }
70041
70289
  if (this._isEndCentered()) {
70042
70290
  if (this._isEndOnStem()) {
70043
70291
  switch (this.tieDirection) {
70044
70292
  case BeamDirection.Up:
70045
- return this.endNoteRenderer.getNoteY(this._endNote, NoteYPosition.TopWithStem);
70293
+ return endNoteRenderer.y + endNoteRenderer.getNoteY(this.endNote, NoteYPosition.TopWithStem);
70046
70294
  default:
70047
- return this.endNoteRenderer.getNoteY(this._endNote, NoteYPosition.BottomWithStem);
70295
+ return endNoteRenderer.y + endNoteRenderer.getNoteY(this.endNote, NoteYPosition.BottomWithStem);
70048
70296
  }
70049
70297
  }
70050
70298
  switch (this.tieDirection) {
70051
70299
  case BeamDirection.Up:
70052
- return this.endNoteRenderer.getNoteY(this._endNote, NoteYPosition.Top);
70300
+ return endNoteRenderer.y + endNoteRenderer.getNoteY(this.endNote, NoteYPosition.Top);
70053
70301
  default:
70054
- return this.endNoteRenderer.getNoteY(this._endNote, NoteYPosition.Bottom);
70302
+ return endNoteRenderer.y + endNoteRenderer.getNoteY(this.endNote, NoteYPosition.Bottom);
70055
70303
  }
70056
70304
  }
70057
- return this.endNoteRenderer.getNoteY(this._endNote, NoteYPosition.Center);
70305
+ return endNoteRenderer.y + endNoteRenderer.getNoteY(this.endNote, NoteYPosition.Center);
70058
70306
  }
70059
70307
  _isStartCentered() {
70060
- return ((this._startNote === this._startNote.beat.maxNote && this.tieDirection === BeamDirection.Up) ||
70061
- (this._startNote === this._startNote.beat.minNote && this.tieDirection === BeamDirection.Down));
70308
+ return ((this.startNote === this.startNote.beat.maxNote && this.tieDirection === BeamDirection.Up) ||
70309
+ (this.startNote === this.startNote.beat.minNote && this.tieDirection === BeamDirection.Down));
70062
70310
  }
70063
70311
  _isEndCentered() {
70064
- return (this._startNote.beat.graceType === GraceType.None &&
70065
- ((this._endNote === this._endNote.beat.maxNote && this.tieDirection === BeamDirection.Up) ||
70066
- (this._endNote === this._endNote.beat.minNote && this.tieDirection === BeamDirection.Down)));
70312
+ return (this.startNote.beat.graceType === GraceType.None &&
70313
+ ((this.endNote === this.endNote.beat.maxNote && this.tieDirection === BeamDirection.Up) ||
70314
+ (this.endNote === this.endNote.beat.minNote && this.tieDirection === BeamDirection.Down)));
70067
70315
  }
70068
70316
  _isEndOnStem() {
70069
- const endNoteScoreRenderer = this.endNoteRenderer;
70070
- const startBeamDirection = this.startNoteRenderer.getBeatDirection(this.startBeat);
70071
- const endBeamDirection = endNoteScoreRenderer.getBeatDirection(this.endBeat);
70072
- return startBeamDirection !== endBeamDirection && this.startBeat.graceType === GraceType.None;
70073
- }
70074
- getStartX() {
70075
- return this._isStartCentered()
70076
- ? this.startNoteRenderer.getBeatX(this._startNote.beat, BeatXPosition.MiddleNotes)
70077
- : this.startNoteRenderer.getNoteX(this._startNote, NoteXPosition.Right);
70078
- }
70079
- getEndX() {
70080
- if (this._isEndCentered()) {
70081
- if (this._isEndOnStem()) {
70082
- return this.endNoteRenderer.getBeatX(this._endNote.beat, BeatXPosition.Stem);
70083
- }
70084
- return this.endNoteRenderer.getNoteX(this._endNote, NoteXPosition.Center);
70085
- }
70086
- return this.endNoteRenderer.getBeatX(this._endNote.beat, BeatXPosition.PreNotes);
70087
- }
70088
- }
70089
-
70090
- /**
70091
- * @internal
70092
- */
70093
- class ScoreTieGlyph extends TieGlyph {
70094
- startNote;
70095
- endNote;
70096
- constructor(startNote, endNote, forEnd = false) {
70097
- super(!startNote ? null : startNote.beat, !endNote ? null : endNote.beat, forEnd);
70098
- this.startNote = startNote;
70099
- this.endNote = endNote;
70100
- }
70101
- shouldDrawBendSlur() {
70102
- return (this.renderer.settings.notation.extendBendArrowsOnTiedNotes &&
70103
- !!this.startNote.bendOrigin &&
70104
- this.startNote.isTieOrigin);
70105
- }
70106
- doLayout() {
70107
- super.doLayout();
70108
- }
70109
- getBeamDirection(beat, noteRenderer) {
70110
- // invert direction (if stems go up, ties go down to not cross them)
70111
- switch (noteRenderer.getBeatDirection(beat)) {
70112
- case BeamDirection.Up:
70113
- return BeamDirection.Down;
70114
- default:
70115
- return BeamDirection.Up;
70116
- }
70117
- }
70118
- getStartY() {
70119
- if (this.startBeat.isRest) {
70120
- // below all lines
70121
- return this.startNoteRenderer.getScoreY(9);
70122
- }
70123
- switch (this.tieDirection) {
70124
- case BeamDirection.Up:
70125
- // below lowest note
70126
- return this.startNoteRenderer.getNoteY(this.startNote, NoteYPosition.Top);
70127
- default:
70128
- return this.startNoteRenderer.getNoteY(this.startNote, NoteYPosition.Bottom);
70129
- }
70130
- }
70131
- getEndY() {
70132
- const endNoteScoreRenderer = this.endNoteRenderer;
70133
- if (this.endBeat.isRest) {
70134
- switch (this.tieDirection) {
70135
- case BeamDirection.Up:
70136
- return endNoteScoreRenderer.getScoreY(9);
70137
- default:
70138
- return endNoteScoreRenderer.getScoreY(0);
70139
- }
70140
- }
70141
- switch (this.tieDirection) {
70142
- case BeamDirection.Up:
70143
- return endNoteScoreRenderer.getNoteY(this.endNote, NoteYPosition.Top);
70144
- default:
70145
- return endNoteScoreRenderer.getNoteY(this.endNote, NoteYPosition.Bottom);
70146
- }
70147
- }
70148
- getStartX() {
70149
- return this.startNoteRenderer.getBeatX(this.startNote.beat, BeatXPosition.PostNotes);
70150
- }
70151
- getEndX() {
70152
- return this.endNoteRenderer.getBeatX(this.endNote.beat, BeatXPosition.PreNotes);
70317
+ const startBeamDirection = this.lookupStartBeatRenderer().getBeatDirection(this.startNote.beat);
70318
+ const endBeatRenderer = this.lookupEndBeatRenderer();
70319
+ const endBeamDirection = endBeatRenderer
70320
+ ? endBeatRenderer.getBeatDirection(this.endNote.beat)
70321
+ : startBeamDirection;
70322
+ return startBeamDirection !== endBeamDirection && this.startNote.beat.graceType === GraceType.None;
70153
70323
  }
70154
70324
  }
70155
70325
 
@@ -70199,12 +70369,11 @@
70199
70369
  n.beat.graceType !== GraceType.BendGrace &&
70200
70370
  n.tieDestination &&
70201
70371
  n.tieDestination.isVisible) {
70202
- // tslint:disable-next-line: no-unnecessary-type-assertion
70203
- const tie = new ScoreTieGlyph(n, n.tieDestination, false);
70372
+ const tie = new ScoreTieGlyph(`score.tie.${n.id}`, n, n.tieDestination, false);
70204
70373
  this.addTie(tie);
70205
70374
  }
70206
70375
  if (n.isTieDestination && !n.tieOrigin.hasBend && !n.beat.hasWhammyBar) {
70207
- const tie = new ScoreTieGlyph(n.tieOrigin, n, true);
70376
+ const tie = new ScoreTieGlyph(`score.tie.${n.tieOrigin.id}`, n.tieOrigin, n, true);
70208
70377
  this.addTie(tie);
70209
70378
  }
70210
70379
  // TODO: depending on the type we have other positioning
@@ -70214,17 +70383,16 @@
70214
70383
  this.addTie(l);
70215
70384
  }
70216
70385
  if (n.isSlurOrigin && n.slurDestination && n.slurDestination.isVisible) {
70217
- // tslint:disable-next-line: no-unnecessary-type-assertion
70218
- const tie = new ScoreSlurGlyph(n, n.slurDestination, false);
70386
+ const tie = new ScoreSlurGlyph(`score.slur.${n.id}`, n, n.slurDestination, false);
70219
70387
  this.addTie(tie);
70220
70388
  }
70221
70389
  if (n.isSlurDestination) {
70222
- const tie = new ScoreSlurGlyph(n.slurOrigin, n, true);
70390
+ const tie = new ScoreSlurGlyph(`score.slur.${n.slurOrigin.id}`, n.slurOrigin, n, true);
70223
70391
  this.addTie(tie);
70224
70392
  }
70225
70393
  // start effect slur on first beat
70226
70394
  if (!this._effectSlur && n.isEffectSlurOrigin && n.effectSlurDestination) {
70227
- const effectSlur = new ScoreSlurGlyph(n, n.effectSlurDestination, false);
70395
+ const effectSlur = new ScoreSlurGlyph(`score.slur.effect.${n.beat.id}`, n, n.effectSlurDestination, false);
70228
70396
  this._effectSlur = effectSlur;
70229
70397
  this.addTie(effectSlur);
70230
70398
  }
@@ -70233,7 +70401,7 @@
70233
70401
  const direction = this.onNotes.beamingHelper.direction;
70234
70402
  const startNote = direction === BeamDirection.Up ? n.beat.effectSlurOrigin.minNote : n.beat.effectSlurOrigin.maxNote;
70235
70403
  const endNote = direction === BeamDirection.Up ? n.beat.minNote : n.beat.maxNote;
70236
- const effectEndSlur = new ScoreSlurGlyph(startNote, endNote, true);
70404
+ const effectEndSlur = new ScoreSlurGlyph(`score.slur.effect.${startNote.beat.id}`, startNote, endNote, true);
70237
70405
  this._effectEndSlur = effectEndSlur;
70238
70406
  this.addTie(effectEndSlur);
70239
70407
  }
@@ -70255,7 +70423,7 @@
70255
70423
  while (destination.nextBeat && destination.nextBeat.isLegatoDestination) {
70256
70424
  destination = destination.nextBeat;
70257
70425
  }
70258
- this.addTie(new ScoreLegatoGlyph(this.beat, destination, false));
70426
+ this.addTie(new ScoreLegatoGlyph(`score.legato.${this.beat.id}`, this.beat, destination, false));
70259
70427
  }
70260
70428
  }
70261
70429
  else if (this.beat.isLegatoDestination) {
@@ -70265,7 +70433,7 @@
70265
70433
  while (origin.previousBeat && origin.previousBeat.isLegatoOrigin) {
70266
70434
  origin = origin.previousBeat;
70267
70435
  }
70268
- this.addTie(new ScoreLegatoGlyph(origin, this.beat, true));
70436
+ this.addTie(new ScoreLegatoGlyph(`score.legato.${origin.id}`, origin, this.beat, true));
70269
70437
  }
70270
70438
  }
70271
70439
  }
@@ -70645,6 +70813,9 @@
70645
70813
  this.addBeatGlyph(container);
70646
70814
  }
70647
70815
  }
70816
+ getNoteLine(note) {
70817
+ return this.accidentalHelper.getNoteSteps(note) / 2;
70818
+ }
70648
70819
  getNoteSteps(n) {
70649
70820
  return this.accidentalHelper.getNoteSteps(n);
70650
70821
  }
@@ -70701,43 +70872,15 @@
70701
70872
  /**
70702
70873
  * @internal
70703
70874
  */
70704
- class SlashTieGlyph extends TieGlyph {
70705
- startNote;
70706
- endNote;
70707
- constructor(startNote, endNote, forEnd = false) {
70708
- super(startNote.beat, endNote.beat, forEnd);
70709
- this.startNote = startNote;
70710
- this.endNote = endNote;
70711
- }
70712
- get _isLeftHandTap() {
70713
- return this.startNote === this.endNote;
70714
- }
70715
- getTieHeight(startX, startY, endX, endY) {
70716
- if (this._isLeftHandTap) {
70717
- return this.startNoteRenderer.smuflMetrics.tieHeight;
70718
- }
70719
- return super.getTieHeight(startX, startY, endX, endY);
70720
- }
70721
- getBeamDirection(_beat, _noteRenderer) {
70875
+ class SlashTieGlyph extends NoteTieGlyph {
70876
+ calculateTieDirection() {
70722
70877
  return BeamDirection.Down;
70723
70878
  }
70724
- static getBeamDirectionForNote(_note) {
70725
- return BeamDirection.Down;
70726
- }
70727
- getStartY() {
70728
- return this.startNoteRenderer.getNoteY(this.startNote, NoteYPosition.Center);
70729
- }
70730
- getEndY() {
70731
- return this.getStartY();
70732
- }
70733
- getStartX() {
70734
- if (this._isLeftHandTap) {
70735
- return this.getEndX() - this.renderer.smuflMetrics.leftHandTabTieWidth;
70736
- }
70737
- return this.startNoteRenderer.getNoteX(this.startNote, NoteXPosition.Right);
70879
+ getStartNotePosition() {
70880
+ return NoteXPosition.Right;
70738
70881
  }
70739
- getEndX() {
70740
- return this.endNoteRenderer.getNoteX(this.endNote, NoteXPosition.Left);
70882
+ getEndNotePosition() {
70883
+ return NoteXPosition.Left;
70741
70884
  }
70742
70885
  }
70743
70886
 
@@ -70752,12 +70895,12 @@
70752
70895
  return;
70753
70896
  }
70754
70897
  if (!this._tiedNoteTie && n.isTieOrigin && n.tieDestination.isVisible) {
70755
- const tie = new SlashTieGlyph(n, n.tieDestination, false);
70898
+ const tie = new SlashTieGlyph('slash.tie', n, n.tieDestination, false);
70756
70899
  this._tiedNoteTie = tie;
70757
70900
  this.addTie(tie);
70758
70901
  }
70759
70902
  if (!this._tiedNoteTie && n.isTieDestination) {
70760
- const tie = new SlashTieGlyph(n.tieOrigin, n, true);
70903
+ const tie = new SlashTieGlyph('slash.tie', n.tieOrigin, n, true);
70761
70904
  this._tiedNoteTie = tie;
70762
70905
  this.addTie(tie);
70763
70906
  }
@@ -70884,8 +71027,7 @@
70884
71027
  doLayout() {
70885
71028
  // create glyphs
70886
71029
  const sr = this.renderer;
70887
- const line = sr.getNoteLine();
70888
- const glyphY = sr.getLineY(line);
71030
+ const glyphY = sr.getLineY(0);
70889
71031
  if (this.container.beat.deadSlapped) {
70890
71032
  const deadSlapped = new DeadSlappedBeatGlyph();
70891
71033
  deadSlapped.renderer = this.renderer;
@@ -70918,7 +71060,7 @@
70918
71060
  //
70919
71061
  if (this.container.beat.dots > 0) {
70920
71062
  for (let i = 0; i < this.container.beat.dots; i++) {
70921
- this.addEffect(new AugmentationDotGlyph(0, sr.getLineY(sr.getNoteLine()) - sr.getLineHeight(0.5)));
71063
+ this.addEffect(new AugmentationDotGlyph(0, glyphY - sr.getLineHeight(0.5)));
70922
71064
  }
70923
71065
  }
70924
71066
  super.doLayout();
@@ -71000,7 +71142,7 @@
71000
71142
  this.registerOverflowTop(this.tupletSize);
71001
71143
  }
71002
71144
  }
71003
- getNoteLine() {
71145
+ getNoteLine(_note) {
71004
71146
  return 0;
71005
71147
  }
71006
71148
  getFlagTopY(beat, _direction) {
@@ -71337,77 +71479,6 @@
71337
71479
  }
71338
71480
  }
71339
71481
 
71340
- /**
71341
- * @internal
71342
- */
71343
- class TabSlurGlyph extends TabTieGlyph {
71344
- _direction;
71345
- _forSlide;
71346
- constructor(startNote, endNote, forSlide, forEnd = false) {
71347
- super(startNote, endNote, forEnd);
71348
- this._direction = TabTieGlyph.getBeamDirectionForNote(startNote);
71349
- this._forSlide = forSlide;
71350
- }
71351
- getTieHeight(startX, _startY, endX, _endY) {
71352
- return Math.log(endX - startX + 1) * this.renderer.settings.notation.slurHeight / 2;
71353
- }
71354
- tryExpand(startNote, endNote, forSlide, forEnd) {
71355
- // same type required
71356
- if (this._forSlide !== forSlide) {
71357
- return false;
71358
- }
71359
- if (this.forEnd !== forEnd) {
71360
- return false;
71361
- }
71362
- // same start and endbeat
71363
- if (this.startNote.beat.id !== startNote.beat.id) {
71364
- return false;
71365
- }
71366
- if (this.endNote.beat.id !== endNote.beat.id) {
71367
- return false;
71368
- }
71369
- // same draw direction
71370
- if (this._direction !== TabTieGlyph.getBeamDirectionForNote(startNote)) {
71371
- return false;
71372
- }
71373
- // if we can expand, expand in correct direction
71374
- switch (this._direction) {
71375
- case BeamDirection.Up:
71376
- if (startNote.realValue > this.startNote.realValue) {
71377
- this.startNote = startNote;
71378
- this.startBeat = startNote.beat;
71379
- }
71380
- if (endNote.realValue > this.endNote.realValue) {
71381
- this.endNote = endNote;
71382
- this.endBeat = endNote.beat;
71383
- }
71384
- break;
71385
- case BeamDirection.Down:
71386
- if (startNote.realValue < this.startNote.realValue) {
71387
- this.startNote = startNote;
71388
- this.startBeat = startNote.beat;
71389
- }
71390
- if (endNote.realValue < this.endNote.realValue) {
71391
- this.endNote = endNote;
71392
- this.endBeat = endNote.beat;
71393
- }
71394
- break;
71395
- }
71396
- return true;
71397
- }
71398
- paint(cx, cy, canvas) {
71399
- const startNoteRenderer = this.renderer.scoreRenderer.layout.getRendererForBar(this.renderer.staff.staffId, this.startBeat.voice.bar);
71400
- const direction = this.getBeamDirection(this.startBeat, startNoteRenderer);
71401
- const slurId = `tab.slur.${this.startNote.beat.id}.${this.endNote.beat.id}.${direction}`;
71402
- const renderer = this.renderer;
71403
- const isSlurRendered = renderer.staff.getSharedLayoutData(slurId, false);
71404
- if (!isSlurRendered) {
71405
- renderer.staff.setSharedLayoutData(slurId, true);
71406
- super.paint(cx, cy, canvas);
71407
- }
71408
- }
71409
- }
71410
-
71411
71482
  /**
71412
71483
  * @internal
71413
71484
  */
@@ -71432,15 +71503,15 @@
71432
71503
  }
71433
71504
  const renderer = this.renderer;
71434
71505
  if (n.isTieOrigin && renderer.showTiedNotes && n.tieDestination.isVisible) {
71435
- const tie = new TabTieGlyph(n, n.tieDestination, false);
71506
+ const tie = new TabTieGlyph(`tab.tie.${n.id}`, n, n.tieDestination, false);
71436
71507
  this.addTie(tie);
71437
71508
  }
71438
71509
  if (n.isTieDestination && renderer.showTiedNotes) {
71439
- const tie = new TabTieGlyph(n.tieOrigin, n, true);
71510
+ const tie = new TabTieGlyph(`tab.tie.${n.tieOrigin.id}`, n.tieOrigin, n, true);
71440
71511
  this.addTie(tie);
71441
71512
  }
71442
71513
  if (n.isLeftHandTapped && !n.isHammerPullDestination) {
71443
- const tapSlur = new TabTieGlyph(n, n, false);
71514
+ const tapSlur = new TabTieGlyph(`tab.tie.leftHandTap.${n.id}`, n, n, false);
71444
71515
  this.addTie(tapSlur);
71445
71516
  }
71446
71517
  // start effect slur on first beat
@@ -71453,7 +71524,7 @@
71453
71524
  }
71454
71525
  }
71455
71526
  if (!expanded) {
71456
- const effectSlur = new TabSlurGlyph(n, n.effectSlurDestination, false, false);
71527
+ const effectSlur = new TabSlurGlyph(`tab.slur.effect.${n.id}`, n, n.effectSlurDestination, false, false);
71457
71528
  this._effectSlurs.push(effectSlur);
71458
71529
  this.addTie(effectSlur);
71459
71530
  }
@@ -71468,7 +71539,7 @@
71468
71539
  }
71469
71540
  }
71470
71541
  if (!expanded) {
71471
- const effectSlur = new TabSlurGlyph(n.effectSlurOrigin, n, false, true);
71542
+ const effectSlur = new TabSlurGlyph(`tab.slur.effect.${n.effectSlurOrigin.id}`, n.effectSlurOrigin, n, false, true);
71472
71543
  this._effectSlurs.push(effectSlur);
71473
71544
  this.addTie(effectSlur);
71474
71545
  }
@@ -71887,7 +71958,7 @@
71887
71958
  }
71888
71959
  else {
71889
71960
  const line = Math.floor((this.renderer.bar.staff.tuning.length - 1) / 2);
71890
- const y = tabRenderer.getTabY(line);
71961
+ const y = tabRenderer.getLineY(line);
71891
71962
  const restGlyph = new TabRestGlyph(0, y, tabRenderer.showRests, this.container.beat.duration);
71892
71963
  this.restGlyph = restGlyph;
71893
71964
  restGlyph.beat = this.container.beat;
@@ -71946,8 +72017,8 @@
71946
72017
  _createNoteGlyph(n) {
71947
72018
  const tr = this.renderer;
71948
72019
  const noteNumberGlyph = new NoteNumberGlyph(0, 0, n);
71949
- const l = n.beat.voice.bar.staff.tuning.length - n.string;
71950
- noteNumberGlyph.y = tr.getTabY(l);
72020
+ const l = tr.getNoteLine(n);
72021
+ noteNumberGlyph.y = tr.getLineY(l);
71951
72022
  noteNumberGlyph.renderer = this.renderer;
71952
72023
  noteNumberGlyph.doLayout();
71953
72024
  this.noteNumbers.addNoteGlyph(noteNumberGlyph, n);
@@ -72138,17 +72209,8 @@
72138
72209
  }
72139
72210
  return mode;
72140
72211
  }
72141
- /**
72142
- * Gets the relative y position of the given steps relative to first line.
72143
- * @param line the line of the particular string where 0 is the most top line
72144
- * @param correction
72145
- * @returns
72146
- */
72147
- getTabY(line) {
72148
- return super.getLineY(line);
72149
- }
72150
- getTabHeight(line) {
72151
- return super.getLineHeight(line);
72212
+ getNoteLine(note) {
72213
+ return this.bar.staff.tuning.length - note.string;
72152
72214
  }
72153
72215
  minString = Number.NaN;
72154
72216
  maxString = Number.NaN;
@@ -72237,7 +72299,7 @@
72237
72299
  if (this.isFirstOfLine) {
72238
72300
  const center = (this.bar.staff.tuning.length - 1) / 2;
72239
72301
  this.createStartSpacing();
72240
- this.addPreBeatGlyph(new TabClefGlyph(0, this.getTabY(center)));
72302
+ this.addPreBeatGlyph(new TabClefGlyph(0, this.getLineY(center)));
72241
72303
  }
72242
72304
  // Time Signature
72243
72305
  if (this.showTimeSignature &&
@@ -72258,7 +72320,7 @@
72258
72320
  _createTimeSignatureGlyphs() {
72259
72321
  this.addPreBeatGlyph(new SpacingGlyph(0, 0, this.smuflMetrics.oneStaffSpace));
72260
72322
  const lines = (this.bar.staff.tuning.length + 1) / 2 - 1;
72261
- this.addPreBeatGlyph(new TabTimeSignatureGlyph(0, this.getTabY(lines), this.bar.masterBar.timeSignatureNumerator, this.bar.masterBar.timeSignatureDenominator, this.bar.masterBar.timeSignatureCommon, this.bar.masterBar.isFreeTime));
72323
+ this.addPreBeatGlyph(new TabTimeSignatureGlyph(0, this.getLineY(lines), this.bar.masterBar.timeSignatureNumerator, this.bar.masterBar.timeSignatureDenominator, this.bar.masterBar.timeSignatureCommon, this.bar.masterBar.isFreeTime));
72262
72324
  }
72263
72325
  createVoiceGlyphs(v) {
72264
72326
  super.createVoiceGlyphs(v);