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

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.1639 (develop, build 1639)
2
+ * alphaTab v1.8.0-alpha.1643 (develop, build 1643)
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.1639';
207
- static date = '2025-12-09T02:16:01.440Z';
208
- static commit = 'af86866e4f9d89a3ccc34006d01473a549dbbe7f';
206
+ static version = '1.8.0-alpha.1643';
207
+ static date = '2025-12-13T02:07:47.380Z';
208
+ static commit = '6cdd7783a14244e9d7efb9f9b9dbad7a27b11eee';
209
209
  static print(print) {
210
210
  print(`alphaTab ${VersionInfo.version}`);
211
211
  print(`commit: ${VersionInfo.commit}`);
@@ -4715,6 +4715,24 @@ class ModelUtils {
4715
4715
  static toArticulationId(plain) {
4716
4716
  return plain.replace(/[^a-zA-Z0-9]/g, '').toLowerCase();
4717
4717
  }
4718
+ static minBoundingBox(a, b) {
4719
+ if (Number.isNaN(a)) {
4720
+ return b;
4721
+ }
4722
+ else if (Number.isNaN(b)) {
4723
+ return a;
4724
+ }
4725
+ return a < b ? a : b;
4726
+ }
4727
+ static maxBoundingBox(a, b) {
4728
+ if (Number.isNaN(a)) {
4729
+ return b;
4730
+ }
4731
+ else if (Number.isNaN(b)) {
4732
+ return a;
4733
+ }
4734
+ return a > b ? a : b;
4735
+ }
4718
4736
  }
4719
4737
 
4720
4738
  /**
@@ -48297,6 +48315,39 @@ class MidiFileGenerator {
48297
48315
  }
48298
48316
  }
48299
48317
 
48318
+ /**
48319
+ * Lists the different position modes for {@link BarRendererBase.getBeatX}
48320
+ * @internal
48321
+ */
48322
+ var BeatXPosition;
48323
+ (function (BeatXPosition) {
48324
+ /**
48325
+ * Gets the pre-notes position which is located before the accidentals
48326
+ */
48327
+ BeatXPosition[BeatXPosition["PreNotes"] = 0] = "PreNotes";
48328
+ /**
48329
+ * Gets the on-notes position which is located after the accidentals but before the note heads.
48330
+ */
48331
+ BeatXPosition[BeatXPosition["OnNotes"] = 1] = "OnNotes";
48332
+ /**
48333
+ * Gets the middle-notes position which is located after in the exact center of the note heads.
48334
+ */
48335
+ BeatXPosition[BeatXPosition["MiddleNotes"] = 2] = "MiddleNotes";
48336
+ /**
48337
+ * Gets position of the stem for this beat
48338
+ */
48339
+ BeatXPosition[BeatXPosition["Stem"] = 3] = "Stem";
48340
+ /**
48341
+ * Get the post-notes position which is located at after the note heads.
48342
+ */
48343
+ BeatXPosition[BeatXPosition["PostNotes"] = 4] = "PostNotes";
48344
+ /**
48345
+ * Get the end-beat position which is located at the end of the beat. This position is almost
48346
+ * equal to the pre-notes position of the next beat.
48347
+ */
48348
+ BeatXPosition[BeatXPosition["EndBeat"] = 5] = "EndBeat";
48349
+ })(BeatXPosition || (BeatXPosition = {}));
48350
+
48300
48351
  /**
48301
48352
  * A glyph is a single symbol which can be added to a GlyphBarRenderer for automated
48302
48353
  * layouting and drawing of stacked symbols.
@@ -48324,198 +48375,6 @@ class Glyph {
48324
48375
  }
48325
48376
  }
48326
48377
 
48327
- /**
48328
- * Effect-Glyphs implementing this public interface get notified
48329
- * as they are expanded over multiple beats.
48330
- * @internal
48331
- */
48332
- class EffectGlyph extends Glyph {
48333
- /**
48334
- * Gets or sets the beat where the glyph belongs to.
48335
- */
48336
- beat = null;
48337
- /**
48338
- * Gets or sets the next glyph of the same type in case
48339
- * the effect glyph is expanded when using {@link EffectBarGlyphSizing.groupedOnBeat}.
48340
- */
48341
- nextGlyph = null;
48342
- /**
48343
- * Gets or sets the previous glyph of the same type in case
48344
- * the effect glyph is expanded when using {@link EffectBarGlyphSizing.groupedOnBeat}.
48345
- */
48346
- previousGlyph = null;
48347
- constructor(x = 0, y = 0) {
48348
- super(x, y);
48349
- }
48350
- }
48351
-
48352
- /**
48353
- * @internal
48354
- */
48355
- class MusicFontGlyph extends EffectGlyph {
48356
- glyphScale = 0;
48357
- symbol;
48358
- center = false;
48359
- colorOverride;
48360
- offsetX = 0;
48361
- offsetY = 0;
48362
- constructor(x, y, glyphScale, symbol) {
48363
- super(x, y);
48364
- this.glyphScale = glyphScale;
48365
- this.symbol = symbol;
48366
- }
48367
- getBoundingBoxTop() {
48368
- const bBoxTop = this.renderer.smuflMetrics.glyphTop.get(this.symbol);
48369
- return this.y - this.offsetY - bBoxTop;
48370
- }
48371
- doLayout() {
48372
- this.width = this.renderer.smuflMetrics.glyphWidths.get(this.symbol) * this.glyphScale;
48373
- this.height = this.renderer.smuflMetrics.glyphHeights.get(this.symbol) * this.glyphScale;
48374
- }
48375
- paint(cx, cy, canvas) {
48376
- if (this.width === 0 && this.height === 0) {
48377
- return;
48378
- }
48379
- const c = canvas.color;
48380
- if (this.colorOverride) {
48381
- canvas.color = this.colorOverride;
48382
- }
48383
- canvas.fillMusicFontSymbol(cx + this.x + this.offsetX, cy + this.y + this.offsetY, this.glyphScale, this.symbol, this.center);
48384
- canvas.color = c;
48385
- }
48386
- }
48387
- /**
48388
- * @internal
48389
- */
48390
- class MusicFontTextGlyph extends EffectGlyph {
48391
- glyphScale = 0;
48392
- symbols;
48393
- center = false;
48394
- colorOverride;
48395
- offsetX = 0;
48396
- offsetY = 0;
48397
- constructor(x, y, glyphScale, symbols) {
48398
- super(x, y);
48399
- this.glyphScale = glyphScale;
48400
- this.symbols = symbols;
48401
- }
48402
- getBoundingBoxTop() {
48403
- let bBoxTop = 0;
48404
- for (let i = 0; i < this.symbols.length; i++) {
48405
- const gTop = this.renderer.smuflMetrics.glyphTop.get(this.symbols[i]);
48406
- if (i === 0 || gTop < bBoxTop) {
48407
- bBoxTop = gTop;
48408
- }
48409
- }
48410
- return this.y - this.offsetY - bBoxTop;
48411
- }
48412
- doLayout() {
48413
- this.width = 0;
48414
- this.height = 0;
48415
- for (let i = 0; i < this.symbols.length; i++) {
48416
- const gWidth = this.renderer.smuflMetrics.glyphWidths.get(this.symbols[i]) * this.glyphScale;
48417
- const gHeight = this.renderer.smuflMetrics.glyphHeights.get(this.symbols[i]) * this.glyphScale;
48418
- if (i === 0 || gWidth > this.width) {
48419
- this.width = gWidth;
48420
- }
48421
- if (i === 0 || gHeight > this.height) {
48422
- this.height = gHeight;
48423
- }
48424
- }
48425
- }
48426
- paint(cx, cy, canvas) {
48427
- if (this.width === 0 && this.height === 0) {
48428
- return;
48429
- }
48430
- const c = canvas.color;
48431
- if (this.colorOverride) {
48432
- canvas.color = this.colorOverride;
48433
- }
48434
- canvas.fillMusicFontSymbols(cx + this.x + this.offsetX, cy + this.y + this.offsetY, this.glyphScale, this.symbols, this.center);
48435
- canvas.color = c;
48436
- }
48437
- }
48438
-
48439
- /**
48440
- * @internal
48441
- */
48442
- class NoteHeadGlyph extends MusicFontGlyph {
48443
- // TODO: SmuFL
48444
- static GraceScale = 0.75;
48445
- centerOnStem = false;
48446
- constructor(x, y, duration, isGrace) {
48447
- super(x, y, isGrace ? NoteHeadGlyph.GraceScale : 1, NoteHeadGlyph.getSymbol(duration));
48448
- }
48449
- paint(cx, cy, canvas) {
48450
- if (this.centerOnStem) {
48451
- this.center = true;
48452
- }
48453
- super.paint(cx, cy, canvas);
48454
- }
48455
- static getSymbol(duration) {
48456
- switch (duration) {
48457
- case Duration.QuadrupleWhole:
48458
- return MusicFontSymbol.NoteheadDoubleWholeSquare;
48459
- case Duration.DoubleWhole:
48460
- return MusicFontSymbol.NoteheadDoubleWhole;
48461
- case Duration.Whole:
48462
- return MusicFontSymbol.NoteheadWhole;
48463
- case Duration.Half:
48464
- return MusicFontSymbol.NoteheadHalf;
48465
- default:
48466
- return MusicFontSymbol.NoteheadBlack;
48467
- }
48468
- }
48469
- }
48470
-
48471
- /**
48472
- * @internal
48473
- */
48474
- class FlagGlyph extends MusicFontGlyph {
48475
- constructor(x, y, duration, direction, isGrace) {
48476
- super(x, y, isGrace ? NoteHeadGlyph.GraceScale : 1, FlagGlyph.getSymbol(duration, direction, isGrace));
48477
- }
48478
- static getSymbol(duration, direction, isGrace) {
48479
- if (isGrace) {
48480
- duration = Duration.Eighth;
48481
- }
48482
- if (direction === BeamDirection.Up) {
48483
- switch (duration) {
48484
- case Duration.Eighth:
48485
- return MusicFontSymbol.Flag8thUp;
48486
- case Duration.Sixteenth:
48487
- return MusicFontSymbol.Flag16thUp;
48488
- case Duration.ThirtySecond:
48489
- return MusicFontSymbol.Flag32ndUp;
48490
- case Duration.SixtyFourth:
48491
- return MusicFontSymbol.Flag64thUp;
48492
- case Duration.OneHundredTwentyEighth:
48493
- return MusicFontSymbol.Flag128thUp;
48494
- case Duration.TwoHundredFiftySixth:
48495
- return MusicFontSymbol.Flag256thUp;
48496
- default:
48497
- return MusicFontSymbol.Flag8thUp;
48498
- }
48499
- }
48500
- switch (duration) {
48501
- case Duration.Eighth:
48502
- return MusicFontSymbol.Flag8thDown;
48503
- case Duration.Sixteenth:
48504
- return MusicFontSymbol.Flag16thDown;
48505
- case Duration.ThirtySecond:
48506
- return MusicFontSymbol.Flag32ndDown;
48507
- case Duration.SixtyFourth:
48508
- return MusicFontSymbol.Flag64thDown;
48509
- case Duration.OneHundredTwentyEighth:
48510
- return MusicFontSymbol.Flag128thDown;
48511
- case Duration.TwoHundredFiftySixth:
48512
- return MusicFontSymbol.Flag128thDown;
48513
- default:
48514
- return MusicFontSymbol.Flag8thDown;
48515
- }
48516
- }
48517
- }
48518
-
48519
48378
  /**
48520
48379
  * @internal
48521
48380
  */
@@ -48527,7 +48386,7 @@ class BeatContainerGlyph extends Glyph {
48527
48386
  onNotes;
48528
48387
  minWidth = 0;
48529
48388
  get onTimeX() {
48530
- return this.onNotes.x + this.onNotes.centerX;
48389
+ return this.onNotes.x + this.onNotes.onTimeX;
48531
48390
  }
48532
48391
  constructor(beat, voiceContainer) {
48533
48392
  super(0, 0);
@@ -48542,28 +48401,20 @@ class BeatContainerGlyph extends Glyph {
48542
48401
  this.renderer.registerTie(tie);
48543
48402
  }
48544
48403
  getBoundingBoxTop() {
48545
- return Math.min(this.preNotes.getBoundingBoxTop(), this.onNotes.getBoundingBoxTop());
48404
+ return ModelUtils.minBoundingBox(this.preNotes.getBoundingBoxTop(), this.onNotes.getBoundingBoxTop());
48546
48405
  }
48547
48406
  getBoundingBoxBottom() {
48548
- return Math.max(this.preNotes.getBoundingBoxBottom(), this.onNotes.getBoundingBoxBottom());
48407
+ return ModelUtils.maxBoundingBox(this.preNotes.getBoundingBoxBottom(), this.onNotes.getBoundingBoxBottom());
48549
48408
  }
48550
48409
  drawBeamHelperAsFlags(helper) {
48551
48410
  return helper.hasFlag(false, undefined);
48552
48411
  }
48412
+ get postBeatStretch() {
48413
+ return this.onNotes.computedWidth - this.onNotes.onTimeX;
48414
+ }
48553
48415
  registerLayoutingInfo(layoutings) {
48554
- const preBeatStretch = this.preNotes.computedWidth + this.onNotes.centerX;
48555
- let postBeatStretch = this.onNotes.computedWidth - this.onNotes.centerX;
48556
- // make space for flag
48557
- const helper = this.renderer.helpers.getBeamingHelperForBeat(this.beat);
48558
- if (this.beat.graceType !== GraceType.None) {
48559
- // always use flag size as spacing on grace notes
48560
- postBeatStretch +=
48561
- this.renderer.smuflMetrics.glyphWidths.get(MusicFontSymbol.Flag8thUp) * NoteHeadGlyph.GraceScale;
48562
- }
48563
- else if (helper && this.drawBeamHelperAsFlags(helper)) {
48564
- postBeatStretch +=
48565
- this.renderer.smuflMetrics.glyphWidths.get(MusicFontSymbol.Flag8thUp) * NoteHeadGlyph.GraceScale;
48566
- }
48416
+ const preBeatStretch = this.preNotes.computedWidth + this.onNotes.onTimeX;
48417
+ let postBeatStretch = this.postBeatStretch;
48567
48418
  for (const tie of this._ties) {
48568
48419
  const tg = tie;
48569
48420
  postBeatStretch += tg.width;
@@ -48578,7 +48429,6 @@ class BeatContainerGlyph extends Glyph {
48578
48429
  });
48579
48430
  }
48580
48431
  applyLayoutingInfo(_info) {
48581
- this.onNotes.updateBeamingHelper();
48582
48432
  this.updateWidth();
48583
48433
  }
48584
48434
  doLayout() {
@@ -48590,7 +48440,6 @@ class BeatContainerGlyph extends Glyph {
48590
48440
  this.onNotes.renderer = this.renderer;
48591
48441
  this.onNotes.container = this;
48592
48442
  this.onNotes.doLayout();
48593
- this.onNotes.updateBeamingHelper();
48594
48443
  let i = this.beat.notes.length - 1;
48595
48444
  while (i >= 0) {
48596
48445
  this.createTies(this.beat.notes[i--]);
@@ -48599,15 +48448,6 @@ class BeatContainerGlyph extends Glyph {
48599
48448
  }
48600
48449
  updateWidth() {
48601
48450
  this.minWidth = this.preNotes.width + this.onNotes.width;
48602
- if (!this.beat.isRest) {
48603
- if (this.onNotes.beamingHelper.beats.length === 1) {
48604
- // make space for flag
48605
- if (this.beat.duration >= Duration.Eighth) {
48606
- const symbol = FlagGlyph.getSymbol(this.beat.duration, this.onNotes.beamingHelper.direction, this.beat.graceType !== GraceType.None);
48607
- this.minWidth += this.renderer.smuflMetrics.glyphWidths.get(symbol);
48608
- }
48609
- }
48610
- }
48611
48451
  let tieWidth = 0;
48612
48452
  for (const tie of this._ties) {
48613
48453
  const tg = tie;
@@ -48618,10 +48458,6 @@ class BeatContainerGlyph extends Glyph {
48618
48458
  this.minWidth += tieWidth;
48619
48459
  this.width = this.minWidth;
48620
48460
  }
48621
- scaleToWidth(beatWidth) {
48622
- this.onNotes.updateBeamingHelper();
48623
- this.width = beatWidth;
48624
- }
48625
48461
  createTies(_n) {
48626
48462
  }
48627
48463
  static getGroupId(beat) {
@@ -48631,8 +48467,11 @@ class BeatContainerGlyph extends Glyph {
48631
48467
  // var c = canvas.color;
48632
48468
  // canvas.color = Color.random();
48633
48469
  // canvas.fillRect(cx + this.x, cy + this.y + this.preNotes.getBoundingBoxTop(), this.width, this.renderer.height);
48634
- // canvas.color = Color.random();
48635
48470
  // canvas.fillRect(cx + this.x, cy + this.y + this.onNotes.getBoundingBoxTop(), this.width, this.renderer.height);
48471
+ // canvas.color = Color.random();
48472
+ // const top = this.getBoundingBoxTop();
48473
+ // const bottom = this.getBoundingBoxBottom();
48474
+ // canvas.fillRect(cx + this.x, cy + this.y + top, this.width, bottom-top);
48636
48475
  // canvas.color = c;
48637
48476
  // var c = canvas.color;
48638
48477
  // var ta = canvas.textAlign;
@@ -48695,7 +48534,7 @@ class BeatContainerGlyph extends Glyph {
48695
48534
  beatBoundings.realBounds.y = barBounds.realBounds.y;
48696
48535
  beatBoundings.realBounds.w = this.width;
48697
48536
  beatBoundings.realBounds.h = barBounds.realBounds.h;
48698
- beatBoundings.onNotesX = cx + this.x + this.onNotes.centerX;
48537
+ beatBoundings.onNotesX = cx + this.x + this.onNotes.x + this.onNotes.onTimeX;
48699
48538
  }
48700
48539
  else {
48701
48540
  beatBoundings.visualBounds = new Bounds();
@@ -48711,7 +48550,7 @@ class BeatContainerGlyph extends Glyph {
48711
48550
  }
48712
48551
  let visualEndX = 0;
48713
48552
  if (!this.onNotes.isEmpty) {
48714
- visualEndX = cx + this.x + this.onNotes.x + this.onNotes.width;
48553
+ visualEndX = cx + this.x + this.onNotes.x + this.onNotes.onTimeX + this.postBeatStretch;
48715
48554
  }
48716
48555
  else if (!this.preNotes.isEmpty) {
48717
48556
  visualEndX = cx + this.x + this.preNotes.x + this.preNotes.width;
@@ -48720,12 +48559,6 @@ class BeatContainerGlyph extends Glyph {
48720
48559
  visualEndX = cx + this.x + this.width;
48721
48560
  }
48722
48561
  beatBoundings.visualBounds.w = visualEndX - beatBoundings.visualBounds.x;
48723
- const helper = this.renderer.helpers.getBeamingHelperForBeat(this.beat);
48724
- if ((helper && this.drawBeamHelperAsFlags(helper)) || this.beat.graceType !== GraceType.None) {
48725
- beatBoundings.visualBounds.w +=
48726
- this.renderer.smuflMetrics.glyphWidths.get(MusicFontSymbol.Flag8thUp) *
48727
- (this.beat.graceType !== GraceType.None ? NoteHeadGlyph.GraceScale : 1);
48728
- }
48729
48562
  beatBoundings.visualBounds.y = barBounds.visualBounds.y;
48730
48563
  beatBoundings.visualBounds.h = barBounds.visualBounds.h;
48731
48564
  beatBoundings.realBounds = new Bounds();
@@ -48733,13 +48566,33 @@ class BeatContainerGlyph extends Glyph {
48733
48566
  beatBoundings.realBounds.y = barBounds.realBounds.y;
48734
48567
  beatBoundings.realBounds.w = this.width;
48735
48568
  beatBoundings.realBounds.h = barBounds.realBounds.h;
48736
- beatBoundings.onNotesX = cx + this.x + this.onNotes.x + this.onNotes.centerX;
48569
+ beatBoundings.onNotesX = cx + this.x + this.onNotes.x + this.onNotes.onTimeX;
48737
48570
  }
48738
48571
  barBounds.addBeat(beatBoundings);
48739
48572
  if (this.renderer.settings.core.includeNoteBounds) {
48740
48573
  this.onNotes.buildBoundingsLookup(beatBoundings, cx + this.x, cy + this.y);
48741
48574
  }
48742
48575
  }
48576
+ getBeatX(requestedPosition, useSharedSizes = false) {
48577
+ switch (requestedPosition) {
48578
+ case BeatXPosition.PreNotes:
48579
+ return this.preNotes.x;
48580
+ case BeatXPosition.OnNotes:
48581
+ return this.onNotes.x;
48582
+ case BeatXPosition.MiddleNotes:
48583
+ return this.onNotes.x + this.onNotes.middleX;
48584
+ case BeatXPosition.Stem:
48585
+ return this.onNotes.x + this.onNotes.stemX;
48586
+ case BeatXPosition.PostNotes:
48587
+ const onNoteSize = useSharedSizes
48588
+ ? (this.renderer.layoutingInfo.getBeatSizes(this.beat)?.onBeatSize ?? this.onNotes.width)
48589
+ : this.onNotes.width;
48590
+ return this.onNotes.x + onNoteSize;
48591
+ case BeatXPosition.EndBeat:
48592
+ return this.width;
48593
+ }
48594
+ return this.preNotes.x;
48595
+ }
48743
48596
  }
48744
48597
 
48745
48598
  /**
@@ -54358,6 +54211,7 @@ class Cursors {
54358
54211
  class ScalableHtmlElementContainer extends HtmlElementContainer {
54359
54212
  _xscale;
54360
54213
  _yscale;
54214
+ centerAtPosition = false;
54361
54215
  constructor(element, xscale, yscale) {
54362
54216
  super(element);
54363
54217
  this._xscale = xscale;
@@ -54399,7 +54253,11 @@ class ScalableHtmlElementContainer extends HtmlElementContainer {
54399
54253
  else {
54400
54254
  h = h / this._yscale;
54401
54255
  }
54402
- this.element.style.transform = `translate(${x}px, ${y}px) scale(${w}, ${h})`;
54256
+ let transform = `translate(${x}px, ${y}px) scale(${w}, ${h})`;
54257
+ if (this.centerAtPosition) {
54258
+ transform += ` translateX(-50%)`;
54259
+ }
54260
+ this.element.style.transform = transform;
54403
54261
  this.element.style.transformOrigin = 'top left';
54404
54262
  this.lastBounds.x = x;
54405
54263
  this.lastBounds.y = y;
@@ -55260,6 +55118,7 @@ class BrowserUiFacade {
55260
55118
  beatCursor.style.willChange = 'transform';
55261
55119
  beatCursorContainer.width = 3;
55262
55120
  beatCursorContainer.height = 1;
55121
+ beatCursorContainer.centerAtPosition = true;
55263
55122
  beatCursorContainer.setBounds(0, 0, 1, 1);
55264
55123
  // add cursors to UI
55265
55124
  element.insertBefore(cursorWrapper, element.firstChild);
@@ -56492,6 +56351,31 @@ var EffectBarGlyphSizing;
56492
56351
  EffectBarGlyphSizing[EffectBarGlyphSizing["FullBar"] = 5] = "FullBar";
56493
56352
  })(EffectBarGlyphSizing || (EffectBarGlyphSizing = {}));
56494
56353
 
56354
+ /**
56355
+ * Effect-Glyphs implementing this public interface get notified
56356
+ * as they are expanded over multiple beats.
56357
+ * @internal
56358
+ */
56359
+ class EffectGlyph extends Glyph {
56360
+ /**
56361
+ * Gets or sets the beat where the glyph belongs to.
56362
+ */
56363
+ beat = null;
56364
+ /**
56365
+ * Gets or sets the next glyph of the same type in case
56366
+ * the effect glyph is expanded when using {@link EffectBarGlyphSizing.groupedOnBeat}.
56367
+ */
56368
+ nextGlyph = null;
56369
+ /**
56370
+ * Gets or sets the previous glyph of the same type in case
56371
+ * the effect glyph is expanded when using {@link EffectBarGlyphSizing.groupedOnBeat}.
56372
+ */
56373
+ previousGlyph = null;
56374
+ constructor(x = 0, y = 0) {
56375
+ super(x, y);
56376
+ }
56377
+ }
56378
+
56495
56379
  /**
56496
56380
  * @internal
56497
56381
  */
@@ -56619,39 +56503,6 @@ class AlternateEndingsEffectInfo extends EffectInfo {
56619
56503
  }
56620
56504
  }
56621
56505
 
56622
- /**
56623
- * Lists the different position modes for {@link BarRendererBase.getBeatX}
56624
- * @internal
56625
- */
56626
- var BeatXPosition;
56627
- (function (BeatXPosition) {
56628
- /**
56629
- * Gets the pre-notes position which is located before the accidentals
56630
- */
56631
- BeatXPosition[BeatXPosition["PreNotes"] = 0] = "PreNotes";
56632
- /**
56633
- * Gets the on-notes position which is located after the accidentals but before the note heads.
56634
- */
56635
- BeatXPosition[BeatXPosition["OnNotes"] = 1] = "OnNotes";
56636
- /**
56637
- * Gets the middle-notes position which is located after in the middle the note heads.
56638
- */
56639
- BeatXPosition[BeatXPosition["MiddleNotes"] = 2] = "MiddleNotes";
56640
- /**
56641
- * Gets position of the stem for this beat
56642
- */
56643
- BeatXPosition[BeatXPosition["Stem"] = 3] = "Stem";
56644
- /**
56645
- * Get the post-notes position which is located at after the note heads.
56646
- */
56647
- BeatXPosition[BeatXPosition["PostNotes"] = 4] = "PostNotes";
56648
- /**
56649
- * Get the end-beat position which is located at the end of the beat. This position is almost
56650
- * equal to the pre-notes position of the next beat.
56651
- */
56652
- BeatXPosition[BeatXPosition["EndBeat"] = 5] = "EndBeat";
56653
- })(BeatXPosition || (BeatXPosition = {}));
56654
-
56655
56506
  /**
56656
56507
  * @internal
56657
56508
  */
@@ -57250,6 +57101,93 @@ class DirectionsEffectInfo extends EffectInfo {
57250
57101
  }
57251
57102
  }
57252
57103
 
57104
+ /**
57105
+ * @internal
57106
+ */
57107
+ class MusicFontGlyph extends EffectGlyph {
57108
+ glyphScale = 0;
57109
+ symbol;
57110
+ center = false;
57111
+ colorOverride;
57112
+ offsetX = 0;
57113
+ offsetY = 0;
57114
+ constructor(x, y, glyphScale, symbol) {
57115
+ super(x, y);
57116
+ this.glyphScale = glyphScale;
57117
+ this.symbol = symbol;
57118
+ }
57119
+ getBoundingBoxTop() {
57120
+ const bBoxTop = this.renderer.smuflMetrics.glyphTop.get(this.symbol);
57121
+ return this.y - this.offsetY - bBoxTop;
57122
+ }
57123
+ doLayout() {
57124
+ this.width = this.renderer.smuflMetrics.glyphWidths.get(this.symbol) * this.glyphScale;
57125
+ this.height = this.renderer.smuflMetrics.glyphHeights.get(this.symbol) * this.glyphScale;
57126
+ }
57127
+ paint(cx, cy, canvas) {
57128
+ if (this.width === 0 && this.height === 0) {
57129
+ return;
57130
+ }
57131
+ const c = canvas.color;
57132
+ if (this.colorOverride) {
57133
+ canvas.color = this.colorOverride;
57134
+ }
57135
+ canvas.fillMusicFontSymbol(cx + this.x + this.offsetX, cy + this.y + this.offsetY, this.glyphScale, this.symbol, this.center);
57136
+ canvas.color = c;
57137
+ }
57138
+ }
57139
+ /**
57140
+ * @internal
57141
+ */
57142
+ class MusicFontTextGlyph extends EffectGlyph {
57143
+ glyphScale = 0;
57144
+ symbols;
57145
+ center = false;
57146
+ colorOverride;
57147
+ offsetX = 0;
57148
+ offsetY = 0;
57149
+ constructor(x, y, glyphScale, symbols) {
57150
+ super(x, y);
57151
+ this.glyphScale = glyphScale;
57152
+ this.symbols = symbols;
57153
+ }
57154
+ getBoundingBoxTop() {
57155
+ let bBoxTop = 0;
57156
+ for (let i = 0; i < this.symbols.length; i++) {
57157
+ const gTop = this.renderer.smuflMetrics.glyphTop.get(this.symbols[i]);
57158
+ if (i === 0 || gTop < bBoxTop) {
57159
+ bBoxTop = gTop;
57160
+ }
57161
+ }
57162
+ return this.y - this.offsetY - bBoxTop;
57163
+ }
57164
+ doLayout() {
57165
+ this.width = 0;
57166
+ this.height = 0;
57167
+ for (let i = 0; i < this.symbols.length; i++) {
57168
+ const gWidth = this.renderer.smuflMetrics.glyphWidths.get(this.symbols[i]) * this.glyphScale;
57169
+ const gHeight = this.renderer.smuflMetrics.glyphHeights.get(this.symbols[i]) * this.glyphScale;
57170
+ if (i === 0 || gWidth > this.width) {
57171
+ this.width = gWidth;
57172
+ }
57173
+ if (i === 0 || gHeight > this.height) {
57174
+ this.height = gHeight;
57175
+ }
57176
+ }
57177
+ }
57178
+ paint(cx, cy, canvas) {
57179
+ if (this.width === 0 && this.height === 0) {
57180
+ return;
57181
+ }
57182
+ const c = canvas.color;
57183
+ if (this.colorOverride) {
57184
+ canvas.color = this.colorOverride;
57185
+ }
57186
+ canvas.fillMusicFontSymbols(cx + this.x + this.offsetX, cy + this.y + this.offsetY, this.glyphScale, this.symbols, this.center);
57187
+ canvas.color = c;
57188
+ }
57189
+ }
57190
+
57253
57191
  /**
57254
57192
  * @internal
57255
57193
  */
@@ -57488,17 +57426,6 @@ class FermataEffectInfo extends EffectInfo {
57488
57426
  }
57489
57427
  }
57490
57428
 
57491
- /**
57492
- * This simple glyph allows to put an empty region in to a BarRenderer.
57493
- * @internal
57494
- */
57495
- class SpacingGlyph extends Glyph {
57496
- constructor(x, y, width) {
57497
- super(x, y);
57498
- this.width = width;
57499
- }
57500
- }
57501
-
57502
57429
  /**
57503
57430
  * This glyph allows to group several other glyphs to be
57504
57431
  * drawn at the same x position
@@ -57514,34 +57441,20 @@ class GlyphGroup extends Glyph {
57514
57441
  const glyphs = this.glyphs;
57515
57442
  if (glyphs) {
57516
57443
  for (const g of glyphs) {
57517
- // only count real visual glyphs
57518
- if (g instanceof SpacingGlyph) {
57519
- continue;
57520
- }
57521
- const gTop = g.getBoundingBoxTop();
57522
- if (Number.isNaN(top) || gTop < top) {
57523
- top = gTop;
57524
- }
57444
+ top = ModelUtils.minBoundingBox(top, g.getBoundingBoxTop());
57525
57445
  }
57526
57446
  }
57527
- return Number.isNaN(top) ? this.y : top;
57447
+ return top;
57528
57448
  }
57529
57449
  getBoundingBoxBottom() {
57530
57450
  let bottom = Number.NaN;
57531
57451
  const glyphs = this.glyphs;
57532
57452
  if (glyphs) {
57533
57453
  for (const g of glyphs) {
57534
- // only count real visual glyphs
57535
- if (g instanceof SpacingGlyph) {
57536
- continue;
57537
- }
57538
- const gBottom = g.getBoundingBoxBottom();
57539
- if (Number.isNaN(bottom) || gBottom > bottom) {
57540
- bottom = gBottom;
57541
- }
57454
+ bottom = ModelUtils.maxBoundingBox(bottom, g.getBoundingBoxBottom());
57542
57455
  }
57543
57456
  }
57544
- return Number.isNaN(bottom) ? this.y + this.height : bottom;
57457
+ return bottom;
57545
57458
  }
57546
57459
  doLayout() {
57547
57460
  if (!this.glyphs || this.glyphs.length === 0) {
@@ -57957,6 +57870,38 @@ class FreeTimeEffectInfo extends EffectInfo {
57957
57870
  }
57958
57871
  }
57959
57872
 
57873
+ /**
57874
+ * @internal
57875
+ */
57876
+ class NoteHeadGlyph extends MusicFontGlyph {
57877
+ // TODO: SmuFL
57878
+ static GraceScale = 0.75;
57879
+ centerOnStem = false;
57880
+ constructor(x, y, duration, isGrace) {
57881
+ super(x, y, isGrace ? NoteHeadGlyph.GraceScale : 1, NoteHeadGlyph.getSymbol(duration));
57882
+ }
57883
+ paint(cx, cy, canvas) {
57884
+ if (this.centerOnStem) {
57885
+ this.center = true;
57886
+ }
57887
+ super.paint(cx, cy, canvas);
57888
+ }
57889
+ static getSymbol(duration) {
57890
+ switch (duration) {
57891
+ case Duration.QuadrupleWhole:
57892
+ return MusicFontSymbol.NoteheadDoubleWholeSquare;
57893
+ case Duration.DoubleWhole:
57894
+ return MusicFontSymbol.NoteheadDoubleWhole;
57895
+ case Duration.Whole:
57896
+ return MusicFontSymbol.NoteheadWhole;
57897
+ case Duration.Half:
57898
+ return MusicFontSymbol.NoteheadHalf;
57899
+ default:
57900
+ return MusicFontSymbol.NoteheadBlack;
57901
+ }
57902
+ }
57903
+ }
57904
+
57960
57905
  /**
57961
57906
  * @internal
57962
57907
  */
@@ -59575,12 +59520,12 @@ class VoiceContainerGlyph extends GlyphGroup {
59575
59520
  // of the next glyph
59576
59521
  if (i > 0) {
59577
59522
  const beatWidth = currentBeatGlyph.x - beatGlyphs[i - 1].x;
59578
- beatGlyphs[i - 1].scaleToWidth(beatWidth);
59523
+ beatGlyphs[i - 1].width = beatWidth;
59579
59524
  }
59580
59525
  // for the last glyph size based on the full width
59581
59526
  if (i === j - 1) {
59582
59527
  const beatWidth = this.width - beatGlyphs[beatGlyphs.length - 1].x;
59583
- currentBeatGlyph.scaleToWidth(beatWidth);
59528
+ currentBeatGlyph.width = beatWidth;
59584
59529
  }
59585
59530
  }
59586
59531
  }
@@ -61280,7 +61225,7 @@ class StaffSystem {
61280
61225
  if (!masterBarBoundsLookup.has(renderer.bar.masterBar.index)) {
61281
61226
  masterBarBounds = new MasterBarBounds();
61282
61227
  masterBarBounds.index = renderer.bar.masterBar.index;
61283
- masterBarBounds.isFirstOfLine = renderer.isFirstOfLine;
61228
+ masterBarBounds.isFirstOfLine = renderer.isFirstOfStaff;
61284
61229
  masterBarBounds.realBounds = new Bounds();
61285
61230
  masterBarBounds.realBounds.x = x + renderer.x;
61286
61231
  masterBarBounds.realBounds.y = realTop;
@@ -61812,10 +61757,9 @@ class BeatGlyphBase extends GlyphGroup {
61812
61757
  * @internal
61813
61758
  */
61814
61759
  class BeatOnNoteGlyphBase extends BeatGlyphBase {
61815
- beamingHelper;
61816
- centerX = 0;
61817
- updateBeamingHelper() {
61818
- }
61760
+ onTimeX = 0;
61761
+ middleX = 0;
61762
+ stemX = 0;
61819
61763
  buildBoundingsLookup(_beatBounds, _cx, _cy) {
61820
61764
  }
61821
61765
  getNoteX(_note, _requestedPosition) {
@@ -61957,14 +61901,6 @@ class MultiBarRestBeatContainerGlyph extends BeatContainerGlyph {
61957
61901
  }
61958
61902
  }
61959
61903
 
61960
- /**
61961
- * @internal
61962
- */
61963
- class BeatLinePositions {
61964
- staffId = '';
61965
- up = 0;
61966
- down = 0;
61967
- }
61968
61904
  /**
61969
61905
  * @internal
61970
61906
  */
@@ -61995,10 +61931,7 @@ class BeamingHelperDrawInfo {
61995
61931
  */
61996
61932
  class BeamingHelper {
61997
61933
  _staff;
61998
- _beatLineXPositions = new Map();
61999
61934
  _renderer;
62000
- _firstNonRestBeat = null;
62001
- _lastNonRestBeat = null;
62002
61935
  voice = null;
62003
61936
  beats = [];
62004
61937
  shortestDuration = Duration.QuadrupleWhole;
@@ -62008,6 +61941,7 @@ class BeamingHelper {
62008
61941
  */
62009
61942
  hasTuplet = false;
62010
61943
  slashBeats = [];
61944
+ restBeats = [];
62011
61945
  lowestNoteInHelper = null;
62012
61946
  _lowestNoteCompareValueInHelper = -1;
62013
61947
  highestNoteInHelper = null;
@@ -62015,25 +61949,21 @@ class BeamingHelper {
62015
61949
  invertBeamDirection = false;
62016
61950
  preferredBeamDirection = null;
62017
61951
  graceType = GraceType.None;
62018
- minRestSteps = null;
62019
- beatOfMinRestSteps = null;
62020
- maxRestSteps = null;
62021
- beatOfMaxRestSteps = null;
62022
61952
  get isRestBeamHelper() {
62023
61953
  return this.beats.length === 1 && this.beats[0].isRest;
62024
61954
  }
62025
- hasLine(forceFlagOnSingleBeat, beat) {
62026
- return ((forceFlagOnSingleBeat && this._beatHasLine(beat)) ||
62027
- (!forceFlagOnSingleBeat && this.beats.length === 1 && this._beatHasLine(beat)));
61955
+ hasStem(forceFlagOnSingleBeat, beat) {
61956
+ return ((forceFlagOnSingleBeat && this._beatHasStem(beat)) ||
61957
+ (!forceFlagOnSingleBeat && this.beats.length === 1 && this._beatHasStem(beat)));
62028
61958
  }
62029
- _beatHasLine(beat) {
61959
+ _beatHasStem(beat) {
62030
61960
  return beat.duration > Duration.Whole;
62031
61961
  }
62032
61962
  hasFlag(forceFlagOnSingleBeat, beat) {
62033
- return ((forceFlagOnSingleBeat && this._beatHasFlag(beat)) ||
62034
- (!forceFlagOnSingleBeat && this.beats.length === 1 && this._beatHasFlag(this.beats[0])));
61963
+ return ((forceFlagOnSingleBeat && BeamingHelper.beatHasFlag(beat)) ||
61964
+ (!forceFlagOnSingleBeat && this.beats.length === 1 && BeamingHelper.beatHasFlag(this.beats[0])));
62035
61965
  }
62036
- _beatHasFlag(beat) {
61966
+ static beatHasFlag(beat) {
62037
61967
  return (!beat.deadSlapped && !beat.isRest && (beat.duration > Duration.Quarter || beat.graceType !== GraceType.None));
62038
61968
  }
62039
61969
  constructor(staff, renderer) {
@@ -62041,38 +61971,12 @@ class BeamingHelper {
62041
61971
  this._renderer = renderer;
62042
61972
  this.beats = [];
62043
61973
  }
62044
- getBeatLineX(beat, direction) {
62045
- direction = direction ?? this.direction;
62046
- if (this.hasBeatLineX(beat)) {
62047
- if (direction === BeamDirection.Up) {
62048
- return this._beatLineXPositions.get(beat.index).up;
62049
- }
62050
- return this._beatLineXPositions.get(beat.index).down;
62051
- }
62052
- return 0;
62053
- }
62054
- hasBeatLineX(beat) {
62055
- return this._beatLineXPositions.has(beat.index);
62056
- }
62057
- registerBeatLineX(staffId, beat, up, down) {
62058
- const positions = this._getOrCreateBeatPositions(beat);
62059
- positions.staffId = staffId;
62060
- positions.up = up;
62061
- positions.down = down;
61974
+ alignWithBeats() {
62062
61975
  for (const v of this.drawingInfos.values()) {
62063
- if (v.startBeat === beat) {
62064
- v.startX = this.getBeatLineX(beat);
62065
- }
62066
- else if (v.endBeat === beat) {
62067
- v.endX = this.getBeatLineX(beat);
62068
- }
62069
- }
62070
- }
62071
- _getOrCreateBeatPositions(beat) {
62072
- if (!this._beatLineXPositions.has(beat.index)) {
62073
- this._beatLineXPositions.set(beat.index, new BeatLinePositions());
61976
+ v.startX = this._renderer.getBeatX(v.startBeat, BeatXPosition.Stem);
61977
+ v.endX = this._renderer.getBeatX(v.endBeat, BeatXPosition.Stem);
61978
+ this.drawingInfos.clear();
62074
61979
  }
62075
- return this._beatLineXPositions.get(beat.index);
62076
61980
  }
62077
61981
  direction = BeamDirection.Up;
62078
61982
  finish() {
@@ -62138,36 +62042,6 @@ class BeamingHelper {
62138
62042
  }
62139
62043
  return [0, 0];
62140
62044
  }
62141
- /**
62142
- * Registers a rest beat within the accidental helper so the rest
62143
- * symbol is considered properly during beaming.
62144
- * @param beat The rest beat.
62145
- * @param steps The steps on which the rest symbol is placed
62146
- */
62147
- applyRest(beat, steps) {
62148
- // do not accept rests after the last beat which has notes
62149
- if ((this._lastNonRestBeat && beat.index >= this._lastNonRestBeat.index) ||
62150
- (this._firstNonRestBeat && beat.index <= this._firstNonRestBeat.index)) {
62151
- return;
62152
- }
62153
- // correct the line of the glyph to a note which would
62154
- // be placed at the upper / lower end of the glyph.
62155
- let aboveRest = steps;
62156
- let belowRest = steps;
62157
- const offsets = BeamingHelper.computeLineHeightsForRest(beat.duration);
62158
- aboveRest -= offsets[0];
62159
- belowRest += offsets[1];
62160
- const minRestSteps = this.minRestSteps;
62161
- const maxRestSteps = this.maxRestSteps;
62162
- if (minRestSteps === null || minRestSteps > aboveRest) {
62163
- this.minRestSteps = aboveRest;
62164
- this.beatOfMinRestSteps = beat;
62165
- }
62166
- if (maxRestSteps === null || maxRestSteps < belowRest) {
62167
- this.maxRestSteps = belowRest;
62168
- this.beatOfMaxRestSteps = beat;
62169
- }
62170
- }
62171
62045
  _invert(direction) {
62172
62046
  if (!this.invertBeamDirection) {
62173
62047
  return direction;
@@ -62231,14 +62105,13 @@ class BeamingHelper {
62231
62105
  if (this.shortestDuration < beat.duration) {
62232
62106
  this.shortestDuration = beat.duration;
62233
62107
  }
62234
- if (!this._firstNonRestBeat) {
62235
- this._firstNonRestBeat = beat;
62236
- }
62237
- this._lastNonRestBeat = beat;
62238
62108
  }
62239
62109
  else if (this.beats.length === 0) {
62240
62110
  this.beats.push(beat);
62241
62111
  }
62112
+ else {
62113
+ this.restBeats.push(beat);
62114
+ }
62242
62115
  if (beat.slashed) {
62243
62116
  this.slashBeats.push(beat);
62244
62117
  }
@@ -62347,19 +62220,6 @@ class BeamingHelper {
62347
62220
  get beatOfHighestNote() {
62348
62221
  return this.highestNoteInHelper.beat;
62349
62222
  }
62350
- /**
62351
- * Returns whether the the position of the given beat, was registered by the staff of the given ID
62352
- * @param staffId
62353
- * @param beat
62354
- * @returns
62355
- */
62356
- isPositionFrom(staffId, beat) {
62357
- if (!this._beatLineXPositions.has(beat.index)) {
62358
- return true;
62359
- }
62360
- return (this._beatLineXPositions.get(beat.index).staffId === staffId ||
62361
- !this._beatLineXPositions.get(beat.index).staffId);
62362
- }
62363
62223
  drawingInfos = new Map();
62364
62224
  }
62365
62225
 
@@ -62511,8 +62371,8 @@ class BarCollisionHelper {
62511
62371
  */
62512
62372
  class BarHelpers {
62513
62373
  _renderer;
62374
+ _beamHelperLookup = new Map();
62514
62375
  beamHelpers = [];
62515
- beamHelperLookup = [];
62516
62376
  collisionHelper;
62517
62377
  preferredBeamDirection = null;
62518
62378
  constructor(renderer) {
@@ -62527,7 +62387,6 @@ class BarHelpers {
62527
62387
  for (let i = 0, j = bar.voices.length; i < j; i++) {
62528
62388
  const v = bar.voices[i];
62529
62389
  this.beamHelpers.push([]);
62530
- this.beamHelperLookup.push(new Map());
62531
62390
  for (let k = 0, l = v.beats.length; k < l; k++) {
62532
62391
  const b = v.beats[k];
62533
62392
  let helperForBeat;
@@ -62536,6 +62395,9 @@ class BarHelpers {
62536
62395
  }
62537
62396
  else {
62538
62397
  helperForBeat = currentBeamHelper;
62398
+ if (currentGraceBeamHelper) {
62399
+ currentGraceBeamHelper.finish();
62400
+ }
62539
62401
  currentGraceBeamHelper = null;
62540
62402
  }
62541
62403
  // if a new beaming helper was started, we close our tuplet grouping as well
@@ -62556,7 +62418,7 @@ class BarHelpers {
62556
62418
  }
62557
62419
  this.beamHelpers[v.index].push(helperForBeat);
62558
62420
  }
62559
- this.beamHelperLookup[v.index].set(b.index, helperForBeat);
62421
+ this._beamHelperLookup.set(b.id, helperForBeat);
62560
62422
  }
62561
62423
  if (currentBeamHelper) {
62562
62424
  currentBeamHelper.finish();
@@ -62569,7 +62431,7 @@ class BarHelpers {
62569
62431
  }
62570
62432
  }
62571
62433
  getBeamingHelperForBeat(beat) {
62572
- return this.beamHelperLookup[beat.voice.index].get(beat.index);
62434
+ return this._beamHelperLookup.has(beat.id) ? this._beamHelperLookup.get(beat.id) : undefined;
62573
62435
  }
62574
62436
  }
62575
62437
 
@@ -62679,6 +62541,9 @@ class BarRendererBase {
62679
62541
  return this._contentBottomOverflow + this.bottomEffects.height;
62680
62542
  }
62681
62543
  helpers;
62544
+ get collisionHelper() {
62545
+ return this.helpers.collisionHelper;
62546
+ }
62682
62547
  /**
62683
62548
  * Gets or sets whether this renderer is linked to the next one
62684
62549
  * by some glyphs like a vibrato effect
@@ -62738,6 +62603,11 @@ class BarRendererBase {
62738
62603
  for (const container of this._voiceContainers.values()) {
62739
62604
  container.scaleToWidth(containerWidth);
62740
62605
  }
62606
+ for (const v of this.helpers.beamHelpers) {
62607
+ for (const h of v) {
62608
+ h.alignWithBeats();
62609
+ }
62610
+ }
62741
62611
  this._postBeatGlyphs.x = this._preBeatGlyphs.x + this._preBeatGlyphs.width + containerWidth;
62742
62612
  this.width = width;
62743
62613
  this.topEffects.alignGlyphs();
@@ -62766,10 +62636,13 @@ class BarRendererBase {
62766
62636
  get barDisplayWidth() {
62767
62637
  return this.staff.system.staves.length > 1 ? this.bar.masterBar.displayWidth : this.bar.displayWidth;
62768
62638
  }
62769
- wasFirstOfLine = false;
62770
- get isFirstOfLine() {
62639
+ wasFirstOfStaff = false;
62640
+ get isFirstOfStaff() {
62771
62641
  return this.index === 0;
62772
62642
  }
62643
+ get isLastOfStaff() {
62644
+ return this.index === this.staff.barRenderers.length - 1;
62645
+ }
62773
62646
  get isLast() {
62774
62647
  return !this.bar || this.bar.index === this.scoreRenderer.layout.lastBarIndex;
62775
62648
  }
@@ -62984,6 +62857,18 @@ class BarRendererBase {
62984
62857
  }
62985
62858
  }
62986
62859
  }
62860
+ for (const v of this._voiceContainers.values()) {
62861
+ for (const b of v.beatGlyphs) {
62862
+ const topY = b.getBoundingBoxTop();
62863
+ if (topY < 0) {
62864
+ this.registerOverflowTop(topY * -1);
62865
+ }
62866
+ const bottomY = b.getBoundingBoxBottom();
62867
+ if (bottomY > rendererBottom) {
62868
+ this.registerOverflowBottom(bottomY - rendererBottom);
62869
+ }
62870
+ }
62871
+ }
62987
62872
  const beatEffectsMinY = this.beatEffectsMinY;
62988
62873
  if (!Number.isNaN(beatEffectsMinY)) {
62989
62874
  const beatEffectTopOverflow = -beatEffectsMinY;
@@ -63037,7 +62922,6 @@ class BarRendererBase {
63037
62922
  g.renderer = this;
63038
62923
  g.preNotes.renderer = this;
63039
62924
  g.onNotes.renderer = this;
63040
- g.onNotes.beamingHelper = this.helpers.beamHelperLookup[g.beat.voice.index].get(g.beat.index);
63041
62925
  this.getVoiceContainer(g.beat.voice).addGlyph(g);
63042
62926
  }
63043
62927
  getVoiceContainer(voice) {
@@ -63116,7 +63000,7 @@ class BarRendererBase {
63116
63000
  this._postBeatGlyphs.addGlyph(g);
63117
63001
  }
63118
63002
  createPreBeatGlyphs() {
63119
- this.wasFirstOfLine = this.isFirstOfLine;
63003
+ this.wasFirstOfStaff = this.isFirstOfStaff;
63120
63004
  }
63121
63005
  createBeatGlyphs() {
63122
63006
  for (const voice of this.bar.voices) {
@@ -63143,26 +63027,7 @@ class BarRendererBase {
63143
63027
  getBeatX(beat, requestedPosition = BeatXPosition.PreNotes, useSharedSizes = false) {
63144
63028
  const container = this.getBeatContainer(beat);
63145
63029
  if (container) {
63146
- switch (requestedPosition) {
63147
- case BeatXPosition.PreNotes:
63148
- return container.voiceContainer.x + container.x;
63149
- case BeatXPosition.OnNotes:
63150
- return container.voiceContainer.x + container.x + container.onNotes.x;
63151
- case BeatXPosition.MiddleNotes:
63152
- return container.voiceContainer.x + container.x + container.onTimeX;
63153
- case BeatXPosition.Stem:
63154
- const offset = container.onNotes.beamingHelper
63155
- ? container.onNotes.beamingHelper.getBeatLineX(beat)
63156
- : container.onNotes.x + container.onNotes.width / 2;
63157
- return container.voiceContainer.x + offset;
63158
- case BeatXPosition.PostNotes:
63159
- const onNoteSize = useSharedSizes
63160
- ? (this.layoutingInfo.getBeatSizes(beat)?.onBeatSize ?? container.onNotes.width)
63161
- : container.onNotes.width;
63162
- return container.voiceContainer.x + container.x + container.onNotes.x + onNoteSize;
63163
- case BeatXPosition.EndBeat:
63164
- return container.voiceContainer.x + container.x + container.width;
63165
- }
63030
+ return container.voiceContainer.x + container.x + container.getBeatX(requestedPosition, useSharedSizes);
63166
63031
  }
63167
63032
  return 0;
63168
63033
  }
@@ -63197,7 +63062,7 @@ class BarRendererBase {
63197
63062
  this.updateSizes();
63198
63063
  // there are some glyphs which are shown only for renderers at the line start, so we simply recreate them
63199
63064
  // but we only need to recreate them for the renderers that were the first of the line or are now the first of the line
63200
- if ((this.wasFirstOfLine && !this.isFirstOfLine) || (!this.wasFirstOfLine && this.isFirstOfLine)) {
63065
+ if ((this.wasFirstOfStaff && !this.isFirstOfStaff) || (!this.wasFirstOfStaff && this.isFirstOfStaff)) {
63201
63066
  this.recreatePreBeatGlyphs();
63202
63067
  this._postBeatGlyphs.doLayout();
63203
63068
  }
@@ -63234,7 +63099,7 @@ class BarRendererBase {
63234
63099
  completeBeamingHelper(_helper) {
63235
63100
  }
63236
63101
  getBeatDirection(beat) {
63237
- return this.helpers.getBeamingHelperForBeat(beat).direction;
63102
+ return this.helpers.getBeamingHelperForBeat(beat)?.direction ?? BeamDirection.Up;
63238
63103
  }
63239
63104
  }
63240
63105
 
@@ -65601,8 +65466,18 @@ class BarLineGlyph extends LeftToRightLayoutingGlyphGroup {
65601
65466
  }
65602
65467
  const lineRenderer = this.renderer;
65603
65468
  const lineYOffset = lineRenderer.smuflMetrics.staffLineThickness;
65604
- const top = this.y - lineYOffset;
65605
- const bottom = this.y + this.renderer.height;
65469
+ let top = this.y;
65470
+ let bottom = this.y;
65471
+ if (lineRenderer.drawnLineCount < 2 ||
65472
+ (!this._isRight && lineRenderer.isFirstOfStaff) ||
65473
+ (this._isRight && lineRenderer.isLastOfStaff)) {
65474
+ top -= lineYOffset;
65475
+ bottom += lineRenderer.height;
65476
+ }
65477
+ else {
65478
+ top += lineRenderer.getLineY(0);
65479
+ bottom += lineRenderer.getLineY(lineRenderer.drawnLineCount - 1);
65480
+ }
65606
65481
  const h = bottom - top;
65607
65482
  // round up to have pixel-aligned bar lines, x-shift will be used during rendering
65608
65483
  // to avoid shifting again all glyphs
@@ -65688,6 +65563,54 @@ class BarNumberGlyph extends Glyph {
65688
65563
  }
65689
65564
  }
65690
65565
 
65566
+ /**
65567
+ * @internal
65568
+ */
65569
+ class FlagGlyph extends MusicFontGlyph {
65570
+ constructor(x, y, duration, direction, isGrace) {
65571
+ super(x, y, isGrace ? NoteHeadGlyph.GraceScale : 1, FlagGlyph.getSymbol(duration, direction, isGrace));
65572
+ }
65573
+ static getSymbol(duration, direction, isGrace) {
65574
+ if (isGrace) {
65575
+ duration = Duration.Eighth;
65576
+ }
65577
+ if (direction === BeamDirection.Up) {
65578
+ switch (duration) {
65579
+ case Duration.Eighth:
65580
+ return MusicFontSymbol.Flag8thUp;
65581
+ case Duration.Sixteenth:
65582
+ return MusicFontSymbol.Flag16thUp;
65583
+ case Duration.ThirtySecond:
65584
+ return MusicFontSymbol.Flag32ndUp;
65585
+ case Duration.SixtyFourth:
65586
+ return MusicFontSymbol.Flag64thUp;
65587
+ case Duration.OneHundredTwentyEighth:
65588
+ return MusicFontSymbol.Flag128thUp;
65589
+ case Duration.TwoHundredFiftySixth:
65590
+ return MusicFontSymbol.Flag256thUp;
65591
+ default:
65592
+ return MusicFontSymbol.Flag8thUp;
65593
+ }
65594
+ }
65595
+ switch (duration) {
65596
+ case Duration.Eighth:
65597
+ return MusicFontSymbol.Flag8thDown;
65598
+ case Duration.Sixteenth:
65599
+ return MusicFontSymbol.Flag16thDown;
65600
+ case Duration.ThirtySecond:
65601
+ return MusicFontSymbol.Flag32ndDown;
65602
+ case Duration.SixtyFourth:
65603
+ return MusicFontSymbol.Flag64thDown;
65604
+ case Duration.OneHundredTwentyEighth:
65605
+ return MusicFontSymbol.Flag128thDown;
65606
+ case Duration.TwoHundredFiftySixth:
65607
+ return MusicFontSymbol.Flag128thDown;
65608
+ default:
65609
+ return MusicFontSymbol.Flag8thDown;
65610
+ }
65611
+ }
65612
+ }
65613
+
65691
65614
  /**
65692
65615
  * @internal
65693
65616
  */
@@ -65723,6 +65646,23 @@ class RepeatCountGlyph extends Glyph {
65723
65646
  }
65724
65647
  }
65725
65648
 
65649
+ /**
65650
+ * This simple glyph allows to put an empty region in to a BarRenderer.
65651
+ * @internal
65652
+ */
65653
+ class SpacingGlyph extends Glyph {
65654
+ constructor(x, y, width) {
65655
+ super(x, y);
65656
+ this.width = width;
65657
+ }
65658
+ getBoundingBoxTop() {
65659
+ return Number.NaN;
65660
+ }
65661
+ getBoundingBoxBottom() {
65662
+ return Number.NaN;
65663
+ }
65664
+ }
65665
+
65726
65666
  /**
65727
65667
  * This is a base class for any bar renderer which renders music notation on a staff
65728
65668
  * with lines like Standard Notation, Guitar Tablatures and Slash Notation.
@@ -65846,7 +65786,7 @@ class LineBarRenderer extends BarRendererBase {
65846
65786
  if (this.hasVoiceContainer(voice)) {
65847
65787
  const container = this.getVoiceContainer(voice);
65848
65788
  for (const tupletGroup of container.tupletGroups) {
65849
- this._paintTupletHelper(cx + this.beatGlyphsStart, cy, canvas, tupletGroup, beatElement, bracketsAsArcs);
65789
+ this._paintTupletHelper(cx, cy, canvas, tupletGroup, beatElement, bracketsAsArcs);
65850
65790
  }
65851
65791
  }
65852
65792
  }
@@ -65920,26 +65860,27 @@ class LineBarRenderer extends BarRendererBase {
65920
65860
  // check if we need to paint simple footer
65921
65861
  const offset = this.tupletOffset;
65922
65862
  const size = this.tupletSize;
65863
+ const shift = offset + size * 0.5;
65923
65864
  const _ = ElementStyleHelper.beat(canvas, beatElement, h.beats[0]);
65924
65865
  try {
65925
65866
  const l = canvas.lineWidth;
65926
65867
  canvas.lineWidth = this.smuflMetrics.tupletBracketThickness;
65927
65868
  if (h.beats.length === 1 || !h.isFull) {
65928
65869
  for (const beat of h.beats) {
65929
- const beamingHelper = this.helpers.beamHelperLookup[h.voice.index].get(beat.index);
65870
+ const beamingHelper = this.helpers.getBeamingHelperForBeat(beat);
65930
65871
  if (!beamingHelper) {
65931
65872
  continue;
65932
65873
  }
65933
65874
  const direction = this.getTupletBeamDirection(beamingHelper);
65934
- const tupletX = beamingHelper.getBeatLineX(beat);
65875
+ const tupletX = this.getBeatX(beat, BeatXPosition.Stem);
65935
65876
  let tupletY = this.calculateBeamYWithDirection(beamingHelper, tupletX, direction);
65936
65877
  if (direction === BeamDirection.Down) {
65937
- tupletY += offset + size;
65878
+ tupletY += shift;
65938
65879
  }
65939
65880
  else {
65940
- tupletY -= offset + size;
65881
+ tupletY -= shift;
65941
65882
  }
65942
- canvas.fillMusicFontSymbols(cx + this.x + tupletX, cy + this.y + tupletY, 1, s, true);
65883
+ canvas.fillMusicFontSymbols(cx + this.x + tupletX, cy + this.y + tupletY + size * 0.5, 1, s, true);
65943
65884
  }
65944
65885
  }
65945
65886
  else {
@@ -65969,12 +65910,12 @@ class LineBarRenderer extends BarRendererBase {
65969
65910
  }
65970
65911
  //
65971
65912
  // Calculate the overall area of the tuplet bracket
65972
- const startX = this.getBeatX(firstBeat, BeatXPosition.OnNotes) - this.beatGlyphsStart;
65973
- const endX = this.getBeatX(lastBeat, BeatXPosition.PostNotes) - this.beatGlyphsStart;
65913
+ const startX = this.getBeatX(firstBeat, BeatXPosition.OnNotes);
65914
+ const endX = this.getBeatX(lastBeat, BeatXPosition.PostNotes);
65974
65915
  //
65975
65916
  // calculate the y positions for our bracket
65976
- const firstNonRestBeamingHelper = this.helpers.beamHelperLookup[h.voice.index].get(firstNonRestBeat.index);
65977
- const lastNonRestBeamingHelper = this.helpers.beamHelperLookup[h.voice.index].get(lastNonRestBeat.index);
65917
+ const firstNonRestBeamingHelper = this.helpers.getBeamingHelperForBeat(firstNonRestBeat);
65918
+ const lastNonRestBeamingHelper = this.helpers.getBeamingHelperForBeat(lastNonRestBeat);
65978
65919
  const direction = this.getTupletBeamDirection(firstNonRestBeamingHelper);
65979
65920
  let startY = this.calculateBeamYWithDirection(firstNonRestBeamingHelper, startX, direction);
65980
65921
  let endY = this.calculateBeamYWithDirection(lastNonRestBeamingHelper, endX, direction);
@@ -65983,7 +65924,6 @@ class LineBarRenderer extends BarRendererBase {
65983
65924
  endY = startY;
65984
65925
  }
65985
65926
  // align line centered in available space
65986
- const shift = offset + size * 0.5;
65987
65927
  if (direction === BeamDirection.Down) {
65988
65928
  startY += shift;
65989
65929
  endY += shift;
@@ -66048,14 +65988,24 @@ class LineBarRenderer extends BarRendererBase {
66048
65988
  paintBeams(cx, cy, canvas, flagsElement, beamsElement) {
66049
65989
  for (const v of this.helpers.beamHelpers) {
66050
65990
  for (const h of v) {
66051
- this._paintBeamHelper(cx + this.beatGlyphsStart, cy, canvas, h, flagsElement, beamsElement);
65991
+ this.paintBeamHelper(cx, cy, canvas, h, flagsElement, beamsElement);
66052
65992
  }
66053
65993
  }
66054
65994
  }
66055
65995
  drawBeamHelperAsFlags(h) {
66056
65996
  return h.beats.length === 1;
66057
65997
  }
66058
- _paintBeamHelper(cx, cy, canvas, h, flagsElement, beamsElement) {
65998
+ hasFlag(beat) {
65999
+ if (beat.isRest) {
66000
+ return false;
66001
+ }
66002
+ const helper = this.helpers.getBeamingHelperForBeat(beat);
66003
+ if (helper) {
66004
+ return helper.hasFlag(this.drawBeamHelperAsFlags(helper), beat);
66005
+ }
66006
+ return BeamingHelper.beatHasFlag(beat);
66007
+ }
66008
+ paintBeamHelper(cx, cy, canvas, h, flagsElement, beamsElement) {
66059
66009
  canvas.color = h.voice.index === 0 ? this.resources.mainGlyphColor : this.resources.secondaryGlyphColor;
66060
66010
  if (!h.isRestBeamHelper) {
66061
66011
  if (this.drawBeamHelperAsFlags(h)) {
@@ -66066,7 +66016,7 @@ class LineBarRenderer extends BarRendererBase {
66066
66016
  }
66067
66017
  }
66068
66018
  }
66069
- shouldPaintFlag(beat, h) {
66019
+ shouldPaintFlag(beat) {
66070
66020
  // no flags for bend grace beats
66071
66021
  if (beat.graceType === GraceType.BendGrace) {
66072
66022
  return false;
@@ -66074,10 +66024,6 @@ class LineBarRenderer extends BarRendererBase {
66074
66024
  if (beat.deadSlapped) {
66075
66025
  return false;
66076
66026
  }
66077
- // we don't have an X-position: cannot paint a flag
66078
- if (!h.hasBeatLineX(beat)) {
66079
- return false;
66080
- }
66081
66027
  // no flags for any grace notes on songbook mode
66082
66028
  if (beat.graceType !== GraceType.None && this.settings.notation.notationMode === NotationMode.SongBook) {
66083
66029
  return false;
@@ -66092,14 +66038,14 @@ class LineBarRenderer extends BarRendererBase {
66092
66038
  }
66093
66039
  paintFlag(cx, cy, canvas, h, flagsElement) {
66094
66040
  for (const beat of h.beats) {
66095
- if (!this.shouldPaintFlag(beat, h)) {
66041
+ if (!this.shouldPaintFlag(beat)) {
66096
66042
  continue;
66097
66043
  }
66098
66044
  const isGrace = beat.graceType !== GraceType.None;
66099
66045
  //
66100
66046
  // draw line
66101
66047
  //
66102
- const beatLineX = h.getBeatLineX(beat);
66048
+ const beatLineX = this.getBeatX(beat, BeatXPosition.Stem);
66103
66049
  const direction = this.getBeamDirection(h);
66104
66050
  const topY = cy + this.y + this.getFlagTopY(beat, direction);
66105
66051
  const bottomY = cy + this.y + this.getFlagBottomY(beat, direction);
@@ -66110,7 +66056,7 @@ class LineBarRenderer extends BarRendererBase {
66110
66056
  else {
66111
66057
  flagY = topY;
66112
66058
  }
66113
- if (!h.hasLine(true, beat)) {
66059
+ if (!h.hasStem(true, beat)) {
66114
66060
  continue;
66115
66061
  }
66116
66062
  this.paintBeamingStem(beat, cy + this.y, cx + this.x + beatLineX, topY, bottomY, canvas);
@@ -66196,10 +66142,10 @@ class LineBarRenderer extends BarRendererBase {
66196
66142
  }
66197
66143
  for (let i = 0, j = h.beats.length; i < j; i++) {
66198
66144
  const beat = h.beats[i];
66199
- if (!h.hasBeatLineX(beat) || beat.deadSlapped) {
66145
+ if (beat.deadSlapped) {
66200
66146
  continue;
66201
66147
  }
66202
- const beatLineX = h.getBeatLineX(beat);
66148
+ const beatLineX = this.getBeatX(beat, BeatXPosition.Stem);
66203
66149
  const y1 = cy + this.y + this.getBarLineStart(beat, direction);
66204
66150
  // ensure we are pixel aligned on the end of the stem to avoid anti-aliasing artifacts
66205
66151
  // when combining stems and beams on sub-pixel level
@@ -66237,7 +66183,7 @@ class LineBarRenderer extends BarRendererBase {
66237
66183
  barEndY = barY + this.calculateBeamY(h, barEndX);
66238
66184
  LineBarRenderer.paintSingleBar(canvas, cx + this.x + barStartX, barStartY, cx + this.x + barEndX, barEndY, barSize);
66239
66185
  // end part
66240
- barEndX = h.getBeatLineX(h.beats[i + 1]);
66186
+ barEndX = this.getBeatX(h.beats[i + 1], BeatXPosition.Stem);
66241
66187
  barStartX = barEndX - brokenBarOffset;
66242
66188
  barStartY = barY + this.calculateBeamY(h, barStartX);
66243
66189
  barEndY = barY + this.calculateBeamY(h, barEndX);
@@ -66247,7 +66193,7 @@ class LineBarRenderer extends BarRendererBase {
66247
66193
  if (isFullBarJoin) {
66248
66194
  // full bar?
66249
66195
  barStartX = beatLineX;
66250
- barEndX = h.getBeatLineX(h.beats[i + 1]);
66196
+ barEndX = this.getBeatX(h.beats[i + 1], BeatXPosition.Stem);
66251
66197
  }
66252
66198
  else if (i === 0 || !BeamingHelper.isFullBarJoin(h.beats[i - 1], beat, barIndex)) {
66253
66199
  barStartX = beatLineX;
@@ -66282,7 +66228,7 @@ class LineBarRenderer extends BarRendererBase {
66282
66228
  }
66283
66229
  }
66284
66230
  if (h.graceType === GraceType.BeforeBeat) {
66285
- const beatLineX = h.getBeatLineX(h.beats[0]);
66231
+ const beatLineX = this.getBeatX(h.beats[0], BeatXPosition.Stem);
66286
66232
  const flagWidth = this.smuflMetrics.glyphWidths.get(MusicFontSymbol.Flag8thUp) * NoteHeadGlyph.GraceScale;
66287
66233
  let slashY = (cy + this.y + this.calculateBeamY(h, beatLineX)) | 0;
66288
66234
  slashY += barSize + barSpacing;
@@ -66309,20 +66255,7 @@ class LineBarRenderer extends BarRendererBase {
66309
66255
  const noteOverflowPadding = this.getLineHeight(0.5);
66310
66256
  for (const v of this.helpers.beamHelpers) {
66311
66257
  for (const h of v) {
66312
- if (h.isRestBeamHelper) {
66313
- if (h.minRestSteps) {
66314
- const topY = this.getLineY(h.maxRestSteps / 2) - noteOverflowPadding;
66315
- if (topY < maxNoteY) {
66316
- maxNoteY = topY;
66317
- }
66318
- }
66319
- if (h.maxRestSteps) {
66320
- const bottomY = this.getLineY(h.maxRestSteps & 2) + noteOverflowPadding;
66321
- if (bottomY < maxNoteY) {
66322
- maxNoteY = bottomY;
66323
- }
66324
- }
66325
- }
66258
+ if (h.isRestBeamHelper) ;
66326
66259
  else if (h.beats.length === 1 && h.beats[0].duration >= Duration.Half) {
66327
66260
  if (h.direction === BeamDirection.Up) {
66328
66261
  let topY = this.getFlagTopY(h.beats[0], h.direction);
@@ -66373,17 +66306,6 @@ class LineBarRenderer extends BarRendererBase {
66373
66306
  }
66374
66307
  }
66375
66308
  }
66376
- const beatContainer = this.getBeatContainer(h.beats[0]);
66377
- if (beatContainer) {
66378
- const bBoxTop = beatContainer.getBoundingBoxTop();
66379
- const bBoxBottom = beatContainer.getBoundingBoxBottom();
66380
- if (bBoxBottom > minNoteY) {
66381
- minNoteY = bBoxBottom;
66382
- }
66383
- if (bBoxTop < maxNoteY) {
66384
- maxNoteY = bBoxTop;
66385
- }
66386
- }
66387
66309
  }
66388
66310
  }
66389
66311
  if (maxNoteY < rendererTop) {
@@ -66399,25 +66321,6 @@ class LineBarRenderer extends BarRendererBase {
66399
66321
  }
66400
66322
  const scale = h.graceType !== GraceType.None ? NoteHeadGlyph.GraceScale : 1;
66401
66323
  const barCount = ModelUtils.getIndex(h.shortestDuration) - 2;
66402
- let stemSize = this.smuflMetrics.standardStemLength * scale;
66403
- if (h.tremoloDuration) {
66404
- // for 16th and shorter beats we need more space for all tremolos
66405
- // for 8th beats we need only more space for 32nd tremolos
66406
- // the logic here is not perfect but there is no SMuFL guideline
66407
- // on how tremolos need to extend stems
66408
- const oneBeamSize = (this.smuflMetrics.beamThickness + this.smuflMetrics.beamSpacing) * scale;
66409
- if (h.shortestDuration > Duration.Eighth) {
66410
- if (h.tremoloDuration === Duration.Eighth) {
66411
- stemSize += oneBeamSize;
66412
- }
66413
- else {
66414
- stemSize += oneBeamSize * 1.5;
66415
- }
66416
- }
66417
- else if (h.tremoloDuration === Duration.ThirtySecond) {
66418
- stemSize += oneBeamSize * 1.5;
66419
- }
66420
- }
66421
66324
  const drawingInfo = new BeamingHelperDrawInfo();
66422
66325
  h.drawingInfos.set(direction, drawingInfo);
66423
66326
  // the beaming logic works like this:
@@ -66430,33 +66333,17 @@ class LineBarRenderer extends BarRendererBase {
66430
66333
  const isRest = h.isRestBeamHelper;
66431
66334
  // 1. put direct diagonal line.
66432
66335
  drawingInfo.startBeat = firstBeat;
66433
- drawingInfo.startX = h.getBeatLineX(firstBeat);
66434
- if (isRest) {
66435
- drawingInfo.startY =
66436
- direction === BeamDirection.Up
66437
- ? this.getLineY(h.minRestSteps / 2)
66438
- : this.getLineY(h.maxRestSteps / 2);
66439
- }
66440
- else {
66441
- drawingInfo.startY =
66442
- direction === BeamDirection.Up
66443
- ? this.getFlagTopY(firstBeat, direction)
66444
- : this.getFlagBottomY(firstBeat, direction);
66445
- }
66336
+ drawingInfo.startX = this.getBeatX(firstBeat, BeatXPosition.Stem);
66337
+ drawingInfo.startY =
66338
+ direction === BeamDirection.Up
66339
+ ? this.getFlagTopY(firstBeat, direction)
66340
+ : this.getFlagBottomY(firstBeat, direction);
66446
66341
  drawingInfo.endBeat = lastBeat;
66447
- drawingInfo.endX = h.getBeatLineX(lastBeat);
66448
- if (isRest) {
66449
- drawingInfo.endY =
66450
- direction === BeamDirection.Up
66451
- ? this.getLineY(h.minRestSteps / 2)
66452
- : this.getLineY(h.maxRestSteps / 2);
66453
- }
66454
- else {
66455
- drawingInfo.endY =
66456
- direction === BeamDirection.Up
66457
- ? this.getFlagTopY(lastBeat, direction)
66458
- : this.getFlagBottomY(lastBeat, direction);
66459
- }
66342
+ drawingInfo.endX = this.getBeatX(lastBeat, BeatXPosition.Stem);
66343
+ drawingInfo.endY =
66344
+ direction === BeamDirection.Up
66345
+ ? this.getFlagTopY(lastBeat, direction)
66346
+ : this.getFlagBottomY(lastBeat, direction);
66460
66347
  // 2. ensure max slope
66461
66348
  // we use the min/max notes to place the beam along their real position
66462
66349
  // we only want a maximum of 10 offset for their gradient
@@ -66481,12 +66368,41 @@ class LineBarRenderer extends BarRendererBase {
66481
66368
  drawingInfo.startY - drawingInfo.endY > maxSlope) {
66482
66369
  drawingInfo.startY = drawingInfo.endY + maxSlope;
66483
66370
  }
66484
- // 3. let middle elements shift up/down
66371
+ // 3. adjust beam drawing order
66372
+ // we can only draw up to 2 beams towards the noteheads, then we have to grow to the other side
66373
+ // here we shift accordingly
66374
+ let barDrawingShift = 0;
66375
+ if (barCount > 2 && !isRest) {
66376
+ const beamSpacing = this.smuflMetrics.beamSpacing * scale;
66377
+ const beamThickness = this.smuflMetrics.beamThickness * scale;
66378
+ const totalBarsHeight = barCount * beamThickness + (barCount - 1) * beamSpacing;
66379
+ if (direction === BeamDirection.Up) {
66380
+ const bottomBarY = drawingInfo.startY + 2 * beamThickness + beamSpacing;
66381
+ const barTopY = bottomBarY - totalBarsHeight;
66382
+ const diff = drawingInfo.startY - barTopY;
66383
+ if (diff > 0) {
66384
+ barDrawingShift = diff * -1;
66385
+ drawingInfo.startY -= diff;
66386
+ drawingInfo.endY -= diff;
66387
+ }
66388
+ }
66389
+ else {
66390
+ const topBarY = drawingInfo.startY - 2 * beamThickness + beamSpacing;
66391
+ const barBottomY = topBarY + totalBarsHeight;
66392
+ const diff = barBottomY - drawingInfo.startY;
66393
+ if (diff > 0) {
66394
+ barDrawingShift = diff;
66395
+ drawingInfo.startY += diff;
66396
+ drawingInfo.endY += diff;
66397
+ }
66398
+ }
66399
+ }
66400
+ // 4. let middle elements shift up/down
66485
66401
  if (h.beats.length > 1) {
66486
66402
  // check if highest note shifts bar up or down
66487
66403
  if (direction === BeamDirection.Up) {
66488
- const yNeededForHighestNote = this.getLineY(this.getMinLineOfBeat(h.beatOfHighestNote)) - stemSize;
66489
- const yGivenByCurrentValues = drawingInfo.calcY(h.getBeatLineX(h.beatOfHighestNote));
66404
+ const yNeededForHighestNote = barDrawingShift + this.getFlagTopY(h.beatOfHighestNote, direction);
66405
+ const yGivenByCurrentValues = drawingInfo.calcY(this.getBeatX(h.beatOfHighestNote, BeatXPosition.Stem));
66490
66406
  const diff = yGivenByCurrentValues - yNeededForHighestNote;
66491
66407
  if (diff > 0) {
66492
66408
  drawingInfo.startY -= diff;
@@ -66494,8 +66410,8 @@ class LineBarRenderer extends BarRendererBase {
66494
66410
  }
66495
66411
  }
66496
66412
  else {
66497
- const yNeededForLowestNote = this.getLineY(this.getMaxLineOfBeat(h.beatOfLowestNote)) + stemSize;
66498
- const yGivenByCurrentValues = drawingInfo.calcY(h.getBeatLineX(h.beatOfLowestNote));
66413
+ const yNeededForLowestNote = barDrawingShift + this.getFlagBottomY(h.beatOfLowestNote, direction);
66414
+ const yGivenByCurrentValues = drawingInfo.calcY(this.getBeatX(h.beatOfLowestNote, BeatXPosition.Stem));
66499
66415
  const diff = yNeededForLowestNote - yGivenByCurrentValues;
66500
66416
  if (diff > 0) {
66501
66417
  drawingInfo.startY += diff;
@@ -66503,33 +66419,39 @@ class LineBarRenderer extends BarRendererBase {
66503
66419
  }
66504
66420
  }
66505
66421
  // check if rest shifts bar up or down
66506
- if (h.minRestSteps !== null || h.maxRestSteps !== null) {
66422
+ let barSpacing = 0;
66423
+ if (h.restBeats.length > 0) {
66424
+ // space needed for the bars, rests need to be below them
66507
66425
  const scaleMod = h.graceType !== GraceType.None ? NoteHeadGlyph.GraceScale : 1;
66508
- let barSpacing = barCount * (this.smuflMetrics.beamSpacing + this.smuflMetrics.beamThickness) * scaleMod;
66509
- barSpacing += this.smuflMetrics.beamSpacing;
66510
- if (direction === BeamDirection.Up && h.minRestSteps !== null) {
66511
- const yNeededForRest = this.getLineY(h.minRestSteps / 2) - barSpacing;
66512
- const yGivenByCurrentValues = drawingInfo.calcY(h.getBeatLineX(h.beatOfMinRestSteps));
66513
- const diff = yGivenByCurrentValues - yNeededForRest;
66514
- if (diff > 0) {
66515
- drawingInfo.startY -= diff;
66516
- drawingInfo.endY -= diff;
66426
+ barSpacing = barCount * (this.smuflMetrics.beamSpacing + this.smuflMetrics.beamThickness) * scaleMod;
66427
+ }
66428
+ for (const b of h.restBeats) {
66429
+ // rest beats which are "under" the beam
66430
+ if (b.isRest && b.index < h.beats[h.beats.length - 1].index) {
66431
+ if (direction === BeamDirection.Up) {
66432
+ const yNeededForRest = this.getBeatContainer(b).getBoundingBoxTop() - barSpacing;
66433
+ const yGivenByCurrentValues = drawingInfo.calcY(this.getBeatX(b, BeatXPosition.Stem));
66434
+ const diff = yGivenByCurrentValues - yNeededForRest;
66435
+ if (diff > 0) {
66436
+ drawingInfo.startY -= diff;
66437
+ drawingInfo.endY -= diff;
66438
+ }
66517
66439
  }
66518
- }
66519
- else if (direction === BeamDirection.Down && h.maxRestSteps !== null) {
66520
- const yNeededForRest = this.getLineHeight(h.maxRestSteps / 2) + barSpacing;
66521
- const yGivenByCurrentValues = drawingInfo.calcY(h.getBeatLineX(h.beatOfMaxRestSteps));
66522
- const diff = yNeededForRest - yGivenByCurrentValues;
66523
- if (diff > 0) {
66524
- drawingInfo.startY += diff;
66525
- drawingInfo.endY += diff;
66440
+ else if (direction === BeamDirection.Down) {
66441
+ const yNeededForRest = this.getBeatContainer(b).getBoundingBoxBottom() + barSpacing;
66442
+ const yGivenByCurrentValues = drawingInfo.calcY(this.getBeatX(b, BeatXPosition.Stem));
66443
+ const diff = yNeededForRest - yGivenByCurrentValues;
66444
+ if (diff > 0) {
66445
+ drawingInfo.startY += diff;
66446
+ drawingInfo.endY += diff;
66447
+ }
66526
66448
  }
66527
66449
  }
66528
66450
  }
66529
66451
  // check if slash shifts bar up or down
66530
66452
  if (h.slashBeats.length > 0) {
66531
66453
  for (const b of h.slashBeats) {
66532
- const yGivenByCurrentValues = drawingInfo.calcY(h.getBeatLineX(b));
66454
+ const yGivenByCurrentValues = drawingInfo.calcY(this.getBeatX(b, BeatXPosition.Stem));
66533
66455
  const yNeededForSlash = h.direction === BeamDirection.Up
66534
66456
  ? this.getFlagTopY(b, h.direction)
66535
66457
  : this.getFlagBottomY(b, h.direction);
@@ -66541,31 +66463,6 @@ class LineBarRenderer extends BarRendererBase {
66541
66463
  }
66542
66464
  }
66543
66465
  }
66544
- // we can only draw up to 2 beams towards the noteheads, then we have to grow to the other side
66545
- // here we shift accordingly
66546
- if (barCount > 2 && !isRest) {
66547
- const beamSpacing = this.smuflMetrics.beamSpacing * scale;
66548
- const beamThickness = this.smuflMetrics.beamThickness * scale;
66549
- const totalBarsHeight = barCount * beamThickness + (barCount - 1) * beamSpacing;
66550
- if (direction === BeamDirection.Up) {
66551
- const bottomBarY = drawingInfo.startY + 2 * beamThickness + beamSpacing;
66552
- const barTopY = bottomBarY - totalBarsHeight;
66553
- const diff = drawingInfo.startY - barTopY;
66554
- if (diff > 0) {
66555
- drawingInfo.startY -= diff;
66556
- drawingInfo.endY -= diff;
66557
- }
66558
- }
66559
- else {
66560
- const topBarY = drawingInfo.startY - 2 * beamThickness + beamSpacing;
66561
- const barBottomY = topBarY + totalBarsHeight;
66562
- const diff = barBottomY - drawingInfo.startY;
66563
- if (diff > 0) {
66564
- drawingInfo.startY += diff;
66565
- drawingInfo.endY += diff;
66566
- }
66567
- }
66568
- }
66569
66466
  }
66570
66467
  getMinLineOfBeat(_beat) {
66571
66468
  return 0;
@@ -67086,20 +66983,6 @@ class NumberedBeatGlyph extends BeatOnNoteGlyphBase {
67086
66983
  }
67087
66984
  return 0;
67088
66985
  }
67089
- updateBeamingHelper() {
67090
- if (this.beamingHelper) {
67091
- let g = null;
67092
- if (this.noteHeads) {
67093
- g = this.noteHeads;
67094
- }
67095
- else if (this.deadSlapped) {
67096
- g = this.deadSlapped;
67097
- }
67098
- if (g) {
67099
- this.beamingHelper.registerBeatLineX('numbered', this.container.beat, this.container.x + this.x + g.x, this.container.x + this.x + g.x + g.width);
67100
- }
67101
- }
67102
- }
67103
66986
  static majorKeySignatureOneValues = [
67104
66987
  // Flats
67105
66988
  59, 66, 61, 68, 63, 58, 65,
@@ -67215,14 +67098,16 @@ class NumberedBeatGlyph extends BeatOnNoteGlyphBase {
67215
67098
  }
67216
67099
  super.doLayout();
67217
67100
  if (this.container.beat.isEmpty) {
67218
- this.centerX = this.width / 2;
67101
+ this.onTimeX = this.width / 2;
67219
67102
  }
67220
67103
  else if (this.noteHeads) {
67221
- this.centerX = this.noteHeads.x + this.noteHeads.width / 2;
67104
+ this.onTimeX = this.noteHeads.x + this.noteHeads.width / 2;
67222
67105
  }
67223
67106
  else if (this.deadSlapped) {
67224
- this.centerX = this.deadSlapped.x + this.deadSlapped.width / 2;
67107
+ this.onTimeX = this.deadSlapped.x + this.deadSlapped.width / 2;
67225
67108
  }
67109
+ this.middleX = this.onTimeX;
67110
+ this.stemX = this.middleX;
67226
67111
  }
67227
67112
  }
67228
67113
 
@@ -67635,7 +67520,7 @@ class NumberedBarRenderer extends LineBarRenderer {
67635
67520
  const barSize = this.smuflMetrics.numberedBarRendererBarSize;
67636
67521
  const barCount = ModelUtils.getIndex(beat.duration) - 2;
67637
67522
  const barStart = cy + this.y;
67638
- const beatLineX = this.getBeatX(beat, BeatXPosition.PreNotes) - this.beatGlyphsStart;
67523
+ const beatLineX = this.getBeatX(beat, BeatXPosition.PreNotes);
67639
67524
  const beamY = this.calculateBeamY(h, beatLineX);
67640
67525
  for (let barIndex = 0; barIndex < barCount; barIndex++) {
67641
67526
  let barStartX = 0;
@@ -67644,11 +67529,11 @@ class NumberedBarRenderer extends LineBarRenderer {
67644
67529
  const barY = barStart + barIndex * barSpacing;
67645
67530
  if (i === h.beats.length - 1) {
67646
67531
  barStartX = beatLineX;
67647
- barEndX = this.getBeatX(beat, BeatXPosition.PostNotes) - this.beatGlyphsStart;
67532
+ barEndX = this.getBeatX(beat, BeatXPosition.PostNotes);
67648
67533
  }
67649
67534
  else {
67650
67535
  barStartX = beatLineX;
67651
- barEndX = this.getBeatX(h.beats[i + 1], BeatXPosition.PreNotes) - this.beatGlyphsStart;
67536
+ barEndX = this.getBeatX(h.beats[i + 1], BeatXPosition.PreNotes);
67652
67537
  }
67653
67538
  barStartY = barY + beamY;
67654
67539
  canvas.fillRect(cx + this.x + barStartX, barStartY, barEndX - barStartX, barSize);
@@ -67666,7 +67551,7 @@ class NumberedBarRenderer extends LineBarRenderer {
67666
67551
  dotsY = barStart + beamY + barCount * (barSpacing + barSize);
67667
67552
  dotsOffset = dotSpacing;
67668
67553
  }
67669
- const dotX = this.getBeatX(beat, BeatXPosition.MiddleNotes) - this.beatGlyphsStart;
67554
+ const dotX = this.getBeatX(beat, BeatXPosition.MiddleNotes);
67670
67555
  dotCount = Math.abs(dotCount);
67671
67556
  for (let d = 0; d < dotCount; d++) {
67672
67557
  CanvasHelper.fillMusicFontSymbolSafe(canvas, cx + this.x + dotX, dotsY, 1, MusicFontSymbol.AugmentationDot, true);
@@ -67715,7 +67600,7 @@ class NumberedBarRenderer extends LineBarRenderer {
67715
67600
  return this.getLineY(0) - noteHeadHeight / 2;
67716
67601
  }
67717
67602
  createPreBeatGlyphs() {
67718
- this.wasFirstOfLine = this.isFirstOfLine;
67603
+ this.wasFirstOfStaff = this.isFirstOfStaff;
67719
67604
  if (this.index === 0 || (this.bar.masterBar.isRepeatStart && this._isOnlyNumbered)) {
67720
67605
  this.addPreBeatGlyph(new BarLineGlyph(false, this.bar.staff.track.score.stylesheet.extendBarLines));
67721
67606
  }
@@ -67771,6 +67656,11 @@ class NumberedBarRenderer extends LineBarRenderer {
67771
67656
  }
67772
67657
  paintBeamingStem(_beat, _cy, _x, _topY, _bottomY, _canvas) {
67773
67658
  }
67659
+ paintBeamHelper(cx, cy, canvas, h, flagsElement, beamsElement) {
67660
+ if (h.voice?.index === 0) {
67661
+ super.paintBeamHelper(cx, cy, canvas, h, flagsElement, beamsElement);
67662
+ }
67663
+ }
67774
67664
  }
67775
67665
 
67776
67666
  /**
@@ -68088,6 +67978,7 @@ class ScoreNoteChordGlyphBase extends Glyph {
68088
67978
  upLineX = 0;
68089
67979
  downLineX = 0;
68090
67980
  noteStartX = 0;
67981
+ onTimeX = 0;
68091
67982
  constructor() {
68092
67983
  super(0, 0);
68093
67984
  }
@@ -68174,6 +68065,8 @@ class ScoreNoteChordGlyphBase extends Glyph {
68174
68065
  // align all notes so that they align with the stem positions
68175
68066
  const stemPosition = anyDisplaced || direction === BeamDirection.Up ? stemUpX : stemDownX;
68176
68067
  let w = 0;
68068
+ let displacedWidth = 0;
68069
+ let nonDisplacedWidth = 0;
68177
68070
  for (let i = 0, j = this._infos.length; i < j; i++) {
68178
68071
  const g = this._infos[i].glyph;
68179
68072
  const alignDisplaced = displaced.get(i);
@@ -68189,7 +68082,14 @@ class ScoreNoteChordGlyphBase extends Glyph {
68189
68082
  }
68190
68083
  }
68191
68084
  g.x += this.noteStartX;
68192
- w = Math.max(w, g.x + g.width);
68085
+ const gw = g.x + g.width;
68086
+ w = Math.max(w, gw);
68087
+ if (alignDisplaced) {
68088
+ displacedWidth = Math.max(displacedWidth, gw);
68089
+ }
68090
+ else {
68091
+ nonDisplacedWidth = Math.max(nonDisplacedWidth, gw);
68092
+ }
68193
68093
  // after size calculation, re-align glyph to stem if needed
68194
68094
  if (g instanceof NoteHeadGlyph && g.centerOnStem) {
68195
68095
  g.x = stemPosition;
@@ -68203,6 +68103,23 @@ class ScoreNoteChordGlyphBase extends Glyph {
68203
68103
  this.upLineX = stemUpX;
68204
68104
  this.downLineX = stemDownX;
68205
68105
  }
68106
+ // the center of score notes, (used for aligning the beat to the right on-time position)
68107
+ // is always the center of the "correct note" position.
68108
+ // * If the stem is upwards, the center is the middle of the left hand side note head
68109
+ // * If the stem is downards, the center is the middle of the right-hand-side note head
68110
+ if (anyDisplaced) {
68111
+ if (direction === BeamDirection.Up) {
68112
+ this.onTimeX = nonDisplacedWidth / 2;
68113
+ }
68114
+ else {
68115
+ const displacedRawWith = displacedWidth - stemPosition;
68116
+ this.onTimeX = stemPosition + (displacedRawWith / 2);
68117
+ }
68118
+ }
68119
+ else {
68120
+ // for no displaced notes it is simply the center
68121
+ this.onTimeX = w / 2;
68122
+ }
68206
68123
  this.width = w;
68207
68124
  }
68208
68125
  paint(cx, cy, canvas) {
@@ -68280,9 +68197,8 @@ class ScoreNoteChordGlyph extends ScoreNoteChordGlyphBase {
68280
68197
  aboveBeatEffects = new Map();
68281
68198
  belowBeatEffects = new Map();
68282
68199
  beat;
68283
- beamingHelper;
68284
68200
  get direction() {
68285
- return this.beamingHelper.direction;
68201
+ return this.renderer.getBeatDirection(this.beat);
68286
68202
  }
68287
68203
  get scale() {
68288
68204
  return this.beat.graceType !== GraceType.None ? NoteHeadGlyph.GraceScale : 1;
@@ -68324,7 +68240,7 @@ class ScoreNoteChordGlyph extends ScoreNoteChordGlyphBase {
68324
68240
  : 0) * scale;
68325
68241
  // stem size according to duration
68326
68242
  pos -= this.renderer.smuflMetrics.standardStemLength * scale;
68327
- const topCenterY = this.renderer.centerStaffStemY(this.beamingHelper);
68243
+ const topCenterY = this.renderer.centerStaffStemY(this.direction);
68328
68244
  return Math.min(topCenterY, pos);
68329
68245
  case NoteYPosition.Top:
68330
68246
  pos -= n.height / 2;
@@ -68341,7 +68257,7 @@ class ScoreNoteChordGlyph extends ScoreNoteChordGlyphBase {
68341
68257
  : -this.renderer.smuflMetrics.glyphHeights.get(n.symbol) / 2) * scale;
68342
68258
  // stem size according to duration
68343
68259
  pos += this.renderer.smuflMetrics.standardStemLength * scale;
68344
- const bottomCenterY = this.renderer.centerStaffStemY(this.beamingHelper);
68260
+ const bottomCenterY = this.renderer.centerStaffStemY(this.direction);
68345
68261
  return Math.max(bottomCenterY, pos);
68346
68262
  case NoteYPosition.StemUp:
68347
68263
  pos -=
@@ -68366,11 +68282,6 @@ class ScoreNoteChordGlyph extends ScoreNoteChordGlyphBase {
68366
68282
  addEffectNoteGlyph(noteGlyph, noteLine) {
68367
68283
  super.add(noteGlyph, noteLine);
68368
68284
  }
68369
- updateBeamingHelper(cx) {
68370
- if (this.beamingHelper) {
68371
- this.beamingHelper.registerBeatLineX('score', this.beat, cx + this.x + this.upLineX, cx + this.x + this.downLineX);
68372
- }
68373
- }
68374
68285
  doLayout() {
68375
68286
  super.doLayout();
68376
68287
  const scoreRenderer = this.renderer;
@@ -68379,6 +68290,7 @@ class ScoreNoteChordGlyph extends ScoreNoteChordGlyphBase {
68379
68290
  this._deadSlapped.renderer = this.renderer;
68380
68291
  this._deadSlapped.doLayout();
68381
68292
  this.width = this._deadSlapped.width;
68293
+ this.onTimeX = this.width / 2;
68382
68294
  }
68383
68295
  let aboveBeatEffectsY = 0;
68384
68296
  let belowBeatEffectsY = 0;
@@ -68497,7 +68409,6 @@ class ScoreNoteChordGlyph extends ScoreNoteChordGlyphBase {
68497
68409
  * @internal
68498
68410
  */
68499
68411
  class ScoreRestGlyph extends MusicFontGlyph {
68500
- beamingHelper;
68501
68412
  constructor(x, y, duration) {
68502
68413
  super(x, y, 1, ScoreRestGlyph.getSymbol(duration));
68503
68414
  }
@@ -68529,11 +68440,6 @@ class ScoreRestGlyph extends MusicFontGlyph {
68529
68440
  return MusicFontSymbol.None;
68530
68441
  }
68531
68442
  }
68532
- updateBeamingHelper(cx) {
68533
- if (this.beamingHelper) {
68534
- this.beamingHelper.registerBeatLineX('score', this.beat, cx + this.x + this.width / 2, cx + this.x + this.width / 2);
68535
- }
68536
- }
68537
68443
  paint(cx, cy, canvas) {
68538
68444
  this.internalPaint(cx, cy, canvas, BeatSubElement.StandardNotationRests);
68539
68445
  }
@@ -69025,10 +68931,11 @@ class StringNumberContainerGlyph extends EffectGlyph {
69025
68931
  */
69026
68932
  class SlashNoteHeadGlyph extends MusicFontGlyph {
69027
68933
  beatEffects = new Map();
69028
- beamingHelper;
69029
68934
  noteHeadElement = NoteSubElement.SlashNoteHead;
69030
68935
  effectElement = BeatSubElement.SlashEffects;
69031
68936
  _symbol;
68937
+ upLineX = 0;
68938
+ downLineX = 0;
69032
68939
  constructor(x, y, duration, isGrace, beat) {
69033
68940
  super(x, y, isGrace ? NoteHeadGlyph.GraceScale : 1, SlashNoteHeadGlyph.getSymbol(duration));
69034
68941
  this._symbol = SlashNoteHeadGlyph.getSymbol(duration);
@@ -69079,6 +68986,15 @@ class SlashNoteHeadGlyph extends MusicFontGlyph {
69079
68986
  if (!Number.isNaN(minEffectY)) {
69080
68987
  this.renderer.registerBeatEffectOverflows(minEffectY, maxEffectY);
69081
68988
  }
68989
+ const symbol = this._symbol;
68990
+ const stemInfoUp = this.renderer.smuflMetrics.stemUp.has(symbol)
68991
+ ? this.renderer.smuflMetrics.stemUp.get(symbol).x
68992
+ : 0;
68993
+ this.upLineX = stemInfoUp;
68994
+ const stemInfoDown = this.renderer.smuflMetrics.stemDown.has(symbol)
68995
+ ? this.renderer.smuflMetrics.stemDown.get(symbol).x
68996
+ : 0;
68997
+ this.downLineX = stemInfoDown;
69082
68998
  }
69083
68999
  static getSymbol(duration) {
69084
69000
  switch (duration) {
@@ -69092,25 +69008,13 @@ class SlashNoteHeadGlyph extends MusicFontGlyph {
69092
69008
  return MusicFontSymbol.NoteheadSlashHorizontalEnds;
69093
69009
  }
69094
69010
  }
69095
- updateBeamingHelper(cx) {
69096
- if (this.beamingHelper) {
69097
- const symbol = this._symbol;
69098
- const stemInfoUp = this.renderer.smuflMetrics.stemUp.has(symbol)
69099
- ? this.renderer.smuflMetrics.stemUp.get(symbol).x
69100
- : 0;
69101
- const stemInfoDown = this.renderer.smuflMetrics.stemDown.has(symbol)
69102
- ? this.renderer.smuflMetrics.stemDown.get(symbol).x
69103
- : 0;
69104
- this.beamingHelper.registerBeatLineX('slash', this.beat, cx + this.x + stemInfoUp, cx + this.x + stemInfoDown);
69105
- }
69106
- }
69107
69011
  }
69108
69012
 
69109
69013
  /**
69110
69014
  * @internal
69111
69015
  */
69112
69016
  class ScoreBeatGlyph extends BeatOnNoteGlyphBase {
69113
- _collisionOffset = -1e3;
69017
+ _collisionOffset = Number.NaN;
69114
69018
  _skipPaint = false;
69115
69019
  _whammy;
69116
69020
  noteHeads = null;
@@ -69161,22 +69065,19 @@ class ScoreBeatGlyph extends BeatOnNoteGlyphBase {
69161
69065
  getNoteY(note, requestedPosition) {
69162
69066
  return this.noteHeads ? this.noteHeads.getNoteY(note, requestedPosition) : 0;
69163
69067
  }
69164
- updateBeamingHelper() {
69165
- if (this.noteHeads) {
69166
- this.noteHeads.updateBeamingHelper(this.container.x + this.x);
69068
+ applyRestCollisionOffset() {
69069
+ if (!this.restGlyph) {
69070
+ return;
69167
69071
  }
69168
- else if (this.restGlyph) {
69169
- this.restGlyph.updateBeamingHelper(this.container.x + this.x);
69170
- if (this.renderer.bar.isMultiVoice && this._collisionOffset === -1e3) {
69171
- this._collisionOffset = this.renderer.helpers.collisionHelper.applyRestCollisionOffset(this.container.beat, this.restGlyph.y, this.renderer.getScoreHeight(1));
69172
- this.y += this._collisionOffset;
69173
- const existingRests = this.renderer.helpers.collisionHelper.restDurationsByDisplayTime;
69174
- if (existingRests.has(this.container.beat.playbackStart) &&
69175
- existingRests.get(this.container.beat.playbackStart).has(this.container.beat.playbackDuration) &&
69176
- existingRests.get(this.container.beat.playbackStart).get(this.container.beat.playbackDuration) !==
69177
- this.container.beat.id) {
69178
- this._skipPaint = true;
69179
- }
69072
+ if (this.renderer.bar.isMultiVoice && Number.isNaN(this._collisionOffset)) {
69073
+ this._collisionOffset = this.renderer.collisionHelper.applyRestCollisionOffset(this.container.beat, this.restGlyph.y, this.renderer.getScoreHeight(1));
69074
+ this.y += this._collisionOffset;
69075
+ const existingRests = this.renderer.collisionHelper.restDurationsByDisplayTime;
69076
+ if (existingRests.has(this.container.beat.playbackStart) &&
69077
+ existingRests.get(this.container.beat.playbackStart).has(this.container.beat.playbackDuration) &&
69078
+ existingRests.get(this.container.beat.playbackStart).get(this.container.beat.playbackDuration) !==
69079
+ this.container.beat.id) {
69080
+ this._skipPaint = true;
69180
69081
  }
69181
69082
  }
69182
69083
  }
@@ -69196,7 +69097,6 @@ class ScoreBeatGlyph extends BeatOnNoteGlyphBase {
69196
69097
  const noteHeads = new ScoreNoteChordGlyph();
69197
69098
  this.noteHeads = noteHeads;
69198
69099
  noteHeads.beat = this.container.beat;
69199
- noteHeads.beamingHelper = this.beamingHelper;
69200
69100
  const ghost = new GhostNoteContainerGlyph(false);
69201
69101
  ghost.renderer = this.renderer;
69202
69102
  for (const note of this.container.beat.notes) {
@@ -69246,22 +69146,18 @@ class ScoreBeatGlyph extends BeatOnNoteGlyphBase {
69246
69146
  const restGlyph = new ScoreRestGlyph(0, sr.getScoreY(steps), this.container.beat.duration);
69247
69147
  this.restGlyph = restGlyph;
69248
69148
  restGlyph.beat = this.container.beat;
69249
- restGlyph.beamingHelper = this.beamingHelper;
69250
69149
  this.addNormal(restGlyph);
69251
69150
  if (this.renderer.bar.isMultiVoice) {
69252
69151
  if (this.container.beat.voice.index === 0) {
69253
69152
  const restSizes = BeamingHelper.computeLineHeightsForRest(this.container.beat.duration);
69254
69153
  const restTop = restGlyph.y - sr.getScoreHeight(restSizes[0]);
69255
69154
  const restBottom = restGlyph.y + sr.getScoreHeight(restSizes[1]);
69256
- this.renderer.helpers.collisionHelper.reserveBeatSlot(this.container.beat, restTop, restBottom);
69155
+ this.renderer.collisionHelper.reserveBeatSlot(this.container.beat, restTop, restBottom);
69257
69156
  }
69258
69157
  else {
69259
- this.renderer.helpers.collisionHelper.registerRest(this.container.beat);
69158
+ this.renderer.collisionHelper.registerRest(this.container.beat);
69260
69159
  }
69261
69160
  }
69262
- if (this.beamingHelper) {
69263
- this.beamingHelper.applyRest(this.container.beat, steps);
69264
- }
69265
69161
  //
69266
69162
  // Note dots
69267
69163
  //
@@ -69276,14 +69172,24 @@ class ScoreBeatGlyph extends BeatOnNoteGlyphBase {
69276
69172
  }
69277
69173
  }
69278
69174
  super.doLayout();
69175
+ this.applyRestCollisionOffset();
69279
69176
  if (this.container.beat.isEmpty) {
69280
- this.centerX = this.width / 2;
69177
+ this.onTimeX = this.width / 2;
69178
+ this.middleX = this.onTimeX;
69179
+ this.stemX = this.middleX;
69281
69180
  }
69282
69181
  else if (this.restGlyph) {
69283
- this.centerX = this.restGlyph.x + this.restGlyph.width / 2;
69182
+ this.onTimeX = this.restGlyph.x + this.restGlyph.width / 2;
69183
+ this.middleX = this.onTimeX;
69184
+ this.stemX = this.middleX;
69284
69185
  }
69285
69186
  else if (this.noteHeads) {
69286
- this.centerX = this.noteHeads.x + this.noteHeads.width / 2;
69187
+ this.onTimeX = this.noteHeads.x + this.noteHeads.onTimeX;
69188
+ this.middleX = this.noteHeads.x + this.noteHeads.width / 2;
69189
+ const direction = this.renderer.getBeatDirection(this.container.beat);
69190
+ this.stemX =
69191
+ this.noteHeads.x +
69192
+ (direction === BeamDirection.Up ? this.noteHeads.upLineX : this.noteHeads.downLineX);
69287
69193
  }
69288
69194
  }
69289
69195
  _createBeatDot(line, group) {
@@ -69355,7 +69261,7 @@ class ScoreBeatGlyph extends BeatOnNoteGlyphBase {
69355
69261
  }
69356
69262
  const belowBeatEffects = this.noteHeads.belowBeatEffects;
69357
69263
  const aboveBeatEffects = this.noteHeads.aboveBeatEffects;
69358
- const outsideBeatEffects = this.beamingHelper.direction === BeamDirection.Up
69264
+ const outsideBeatEffects = this.renderer.getBeatDirection(this.container.beat) === BeamDirection.Up
69359
69265
  ? this.noteHeads.belowBeatEffects
69360
69266
  : this.noteHeads.aboveBeatEffects;
69361
69267
  if (n.isStaccato && !belowBeatEffects.has('Staccato')) {
@@ -69791,7 +69697,7 @@ class ScoreBendGlyph extends ScoreHelperNotesBaseGlyph {
69791
69697
  // if we have a line break we draw only a line until the end
69792
69698
  if (!endNoteRenderer || endNoteRenderer.staff !== startNoteRenderer.staff) {
69793
69699
  const endX = cx + startNoteRenderer.x + startNoteRenderer.width;
69794
- const noteValueToDraw = note.tieDestination.realValue;
69700
+ const noteValueToDraw = note.tieDestination.displayValue;
69795
69701
  startNoteRenderer.accidentalHelper.applyAccidentalForValue(note.beat, noteValueToDraw, false, true);
69796
69702
  const endY = cy +
69797
69703
  startNoteRenderer.y +
@@ -70327,6 +70233,22 @@ class ScoreBeatContainerGlyph extends BeatContainerGlyph {
70327
70233
  doLayout() {
70328
70234
  this._effectSlur = null;
70329
70235
  this._effectEndSlur = null;
70236
+ // make space for flag
70237
+ const sr = this.renderer;
70238
+ const beat = this.beat;
70239
+ const isGrace = beat.graceType !== GraceType.None;
70240
+ if (sr.hasFlag(beat)) {
70241
+ const direction = this.renderer.getBeatDirection(beat);
70242
+ const scale = isGrace ? NoteHeadGlyph.GraceScale : 1;
70243
+ const symbol = FlagGlyph.getSymbol(beat.duration, direction, isGrace);
70244
+ const flagWidth = this.renderer.smuflMetrics.glyphWidths.get(symbol) * scale;
70245
+ this._flagStretch = flagWidth;
70246
+ }
70247
+ else if (isGrace) {
70248
+ // always use flag size as spacing on grace notes
70249
+ const graceSpacing = this.renderer.smuflMetrics.glyphWidths.get(MusicFontSymbol.Flag8thUp) * NoteHeadGlyph.GraceScale;
70250
+ this._flagStretch = graceSpacing;
70251
+ }
70330
70252
  super.doLayout();
70331
70253
  if (this._bend) {
70332
70254
  this._bend.renderer = this.renderer;
@@ -70336,7 +70258,7 @@ class ScoreBeatContainerGlyph extends BeatContainerGlyph {
70336
70258
  }
70337
70259
  getBoundingBoxTop() {
70338
70260
  if (this._bend !== null) {
70339
- return Math.min(this._bend.getBoundingBoxTop(), super.getBoundingBoxTop());
70261
+ return ModelUtils.minBoundingBox(this._bend.getBoundingBoxTop(), super.getBoundingBoxTop());
70340
70262
  }
70341
70263
  else {
70342
70264
  return super.getBoundingBoxTop();
@@ -70344,7 +70266,7 @@ class ScoreBeatContainerGlyph extends BeatContainerGlyph {
70344
70266
  }
70345
70267
  getBoundingBoxBottom() {
70346
70268
  if (this._bend !== null) {
70347
- return Math.max(this._bend.getBoundingBoxBottom(), super.getBoundingBoxTop());
70269
+ return ModelUtils.maxBoundingBox(this._bend.getBoundingBoxBottom(), super.getBoundingBoxTop());
70348
70270
  }
70349
70271
  else {
70350
70272
  return super.getBoundingBoxBottom();
@@ -70392,7 +70314,7 @@ class ScoreBeatContainerGlyph extends BeatContainerGlyph {
70392
70314
  }
70393
70315
  // end effect slur on last beat
70394
70316
  if (!this._effectEndSlur && n.beat.isEffectSlurDestination && n.beat.effectSlurOrigin) {
70395
- const direction = this.onNotes.beamingHelper.direction;
70317
+ const direction = this.renderer.getBeatDirection(n.beat);
70396
70318
  const startNote = direction === BeamDirection.Up ? n.beat.effectSlurOrigin.minNote : n.beat.effectSlurOrigin.maxNote;
70397
70319
  const endNote = direction === BeamDirection.Up ? n.beat.minNote : n.beat.maxNote;
70398
70320
  const effectEndSlur = new ScoreSlurGlyph(`score.slur.effect.${startNote.beat.id}`, startNote, endNote, true);
@@ -70431,6 +70353,15 @@ class ScoreBeatContainerGlyph extends BeatContainerGlyph {
70431
70353
  }
70432
70354
  }
70433
70355
  }
70356
+ _flagStretch = 0;
70357
+ get postBeatStretch() {
70358
+ return super.postBeatStretch + this._flagStretch;
70359
+ }
70360
+ updateWidth() {
70361
+ super.updateWidth();
70362
+ this.width += this._flagStretch;
70363
+ this.minWidth += this._flagStretch;
70364
+ }
70434
70365
  }
70435
70366
 
70436
70367
  /**
@@ -70465,7 +70396,7 @@ class ScoreBarRenderer extends LineBarRenderer {
70465
70396
  return this.smuflMetrics.oneStaffSpace;
70466
70397
  }
70467
70398
  get heightLineCount() {
70468
- return 5;
70399
+ return Math.max(5, this.bar.staff.standardNotationLineCount);
70469
70400
  }
70470
70401
  get drawnLineCount() {
70471
70402
  return this.bar.staff.standardNotationLineCount;
@@ -70522,7 +70453,9 @@ class ScoreBarRenderer extends LineBarRenderer {
70522
70453
  slashY -= this.smuflMetrics.stemUp.has(symbol)
70523
70454
  ? this.smuflMetrics.stemUp.get(symbol).bottomY * scale
70524
70455
  : 0;
70525
- slashY -= this.smuflMetrics.standardStemLength + scale;
70456
+ if (!beat.isRest) {
70457
+ slashY -= this.smuflMetrics.standardStemLength + scale;
70458
+ }
70526
70459
  }
70527
70460
  return slashY;
70528
70461
  }
@@ -70531,7 +70464,7 @@ class ScoreBarRenderer extends LineBarRenderer {
70531
70464
  return this.getBeatContainer(beat).onNotes.getNoteY(minNote, direction === BeamDirection.Up ? NoteYPosition.TopWithStem : NoteYPosition.StemDown);
70532
70465
  }
70533
70466
  let y = this.getScoreY(this.accidentalHelper.getMinSteps(beat));
70534
- if (direction === BeamDirection.Up) {
70467
+ if (direction === BeamDirection.Up && !beat.isRest) {
70535
70468
  const scale = beat.graceType !== GraceType.None ? NoteHeadGlyph.GraceScale : 1;
70536
70469
  y -= this.smuflMetrics.standardStemLength * scale;
70537
70470
  }
@@ -70569,14 +70502,14 @@ class ScoreBarRenderer extends LineBarRenderer {
70569
70502
  getBeamDirection(helper) {
70570
70503
  return helper.direction;
70571
70504
  }
70572
- centerStaffStemY(helper) {
70505
+ centerStaffStemY(direction) {
70573
70506
  const isStandardFive = this.bar.staff.standardNotationLineCount === Staff.DefaultStandardNotationLineCount;
70574
70507
  if (isStandardFive) {
70575
70508
  // center on the middle line for a standard 5-line staff
70576
70509
  return this.getScoreY(this.bar.staff.standardNotationLineCount - 1);
70577
70510
  }
70578
70511
  // for other staff line counts, we align the stem either on the top or bottom line
70579
- if (helper.direction === BeamDirection.Up) {
70512
+ if (direction === BeamDirection.Up) {
70580
70513
  return this.getScoreY(this.bar.staff.standardNotationLineCount * 2);
70581
70514
  }
70582
70515
  return this.getScoreY(0);
@@ -70678,7 +70611,7 @@ class ScoreBarRenderer extends LineBarRenderer {
70678
70611
  createLinePreBeatGlyphs() {
70679
70612
  // Clef
70680
70613
  let hasClef = false;
70681
- if (this.isFirstOfLine ||
70614
+ if (this.isFirstOfStaff ||
70682
70615
  this.bar.clef !== this.bar.previousBar.clef ||
70683
70616
  this.bar.clefOttava !== this.bar.previousBar.clefOttava) {
70684
70617
  // SMUFL: Clefs should be positioned such that the pitch the clef refers to is on the baseline
@@ -70883,6 +70816,25 @@ class SlashTieGlyph extends NoteTieGlyph {
70883
70816
  */
70884
70817
  class SlashBeatContainerGlyph extends BeatContainerGlyph {
70885
70818
  _tiedNoteTie = null;
70819
+ doLayout() {
70820
+ // make space for flag
70821
+ const sr = this.renderer;
70822
+ const beat = this.beat;
70823
+ const isGrace = beat.graceType !== GraceType.None;
70824
+ if (sr.hasFlag(beat)) {
70825
+ const direction = this.renderer.getBeatDirection(beat);
70826
+ const scale = isGrace ? NoteHeadGlyph.GraceScale : 1;
70827
+ const symbol = FlagGlyph.getSymbol(beat.duration, direction, isGrace);
70828
+ const flagWidth = this.renderer.smuflMetrics.glyphWidths.get(symbol) * scale;
70829
+ this._flagStretch = flagWidth;
70830
+ }
70831
+ else if (isGrace) {
70832
+ // always use flag size as spacing on grace notes
70833
+ const graceSpacing = this.renderer.smuflMetrics.glyphWidths.get(MusicFontSymbol.Flag8thUp) * NoteHeadGlyph.GraceScale;
70834
+ this._flagStretch = graceSpacing;
70835
+ }
70836
+ super.doLayout();
70837
+ }
70886
70838
  createTies(n) {
70887
70839
  // create a tie if any effect requires it
70888
70840
  if (!n.isVisible) {
@@ -70899,17 +70851,21 @@ class SlashBeatContainerGlyph extends BeatContainerGlyph {
70899
70851
  this.addTie(tie);
70900
70852
  }
70901
70853
  }
70854
+ _flagStretch = 0;
70855
+ get postBeatStretch() {
70856
+ return super.postBeatStretch + this._flagStretch;
70857
+ }
70858
+ updateWidth() {
70859
+ super.updateWidth();
70860
+ this.width += this._flagStretch;
70861
+ this.minWidth += this._flagStretch;
70862
+ }
70902
70863
  }
70903
70864
 
70904
70865
  /**
70905
70866
  * @internal
70906
70867
  */
70907
70868
  class SlashRestGlyph extends ScoreRestGlyph {
70908
- updateBeamingHelper(cx) {
70909
- if (this.beamingHelper) {
70910
- this.beamingHelper.registerBeatLineX('slash', this.beat, cx + this.x + this.width / 2, cx + this.x + this.width / 2);
70911
- }
70912
- }
70913
70869
  paint(cx, cy, canvas) {
70914
70870
  super.internalPaint(cx, cy, canvas, BeatSubElement.SlashRests);
70915
70871
  }
@@ -71005,19 +70961,6 @@ class SlashBeatGlyph extends BeatOnNoteGlyphBase {
71005
70961
  }
71006
70962
  return 0;
71007
70963
  }
71008
- updateBeamingHelper() {
71009
- if (this.noteHeads) {
71010
- this.noteHeads.updateBeamingHelper(this.container.x + this.x);
71011
- }
71012
- else if (this.deadSlapped) {
71013
- if (this.beamingHelper) {
71014
- this.beamingHelper.registerBeatLineX('slash', this.container.beat, this.container.x + this.x + this.deadSlapped.x + this.width, this.container.x + this.x + this.deadSlapped.x);
71015
- }
71016
- }
71017
- else if (this.restGlyph) {
71018
- this.restGlyph.updateBeamingHelper(this.container.x + this.x);
71019
- }
71020
- }
71021
70964
  doLayout() {
71022
70965
  // create glyphs
71023
70966
  const sr = this.renderer;
@@ -71035,18 +70978,13 @@ class SlashBeatGlyph extends BeatOnNoteGlyphBase {
71035
70978
  const noteHeadGlyph = new SlashNoteHeadGlyph(0, glyphY, this.container.beat.duration, isGrace, this.container.beat);
71036
70979
  this.noteHeads = noteHeadGlyph;
71037
70980
  noteHeadGlyph.beat = this.container.beat;
71038
- noteHeadGlyph.beamingHelper = this.beamingHelper;
71039
70981
  this.addNormal(noteHeadGlyph);
71040
70982
  }
71041
70983
  else {
71042
70984
  const restGlyph = new SlashRestGlyph(0, glyphY, this.container.beat.duration);
71043
70985
  this.restGlyph = restGlyph;
71044
70986
  restGlyph.beat = this.container.beat;
71045
- restGlyph.beamingHelper = this.beamingHelper;
71046
70987
  this.addNormal(restGlyph);
71047
- if (this.beamingHelper) {
71048
- this.beamingHelper.applyRest(this.container.beat, 0);
71049
- }
71050
70988
  }
71051
70989
  }
71052
70990
  //
@@ -71059,17 +70997,25 @@ class SlashBeatGlyph extends BeatOnNoteGlyphBase {
71059
70997
  }
71060
70998
  super.doLayout();
71061
70999
  if (this.container.beat.isEmpty) {
71062
- this.centerX = this.width / 2;
71000
+ this.onTimeX = this.width / 2;
71001
+ this.stemX = this.onTimeX;
71063
71002
  }
71064
71003
  else if (this.restGlyph) {
71065
- this.centerX = this.restGlyph.x + this.restGlyph.width / 2;
71004
+ this.onTimeX = this.restGlyph.x + this.restGlyph.width / 2;
71005
+ this.stemX = this.onTimeX;
71066
71006
  }
71067
71007
  else if (this.noteHeads) {
71068
- this.centerX = this.noteHeads.x + this.noteHeads.width / 2;
71008
+ this.onTimeX = this.noteHeads.x + this.noteHeads.width / 2;
71009
+ const direction = this.renderer.getBeatDirection(this.container.beat);
71010
+ this.stemX =
71011
+ this.noteHeads.x +
71012
+ (direction === BeamDirection.Up ? this.noteHeads.upLineX : this.noteHeads.downLineX);
71069
71013
  }
71070
71014
  else if (this.deadSlapped) {
71071
- this.centerX = this.deadSlapped.x + this.deadSlapped.width / 2;
71015
+ this.onTimeX = this.deadSlapped.x + this.deadSlapped.width / 2;
71016
+ this.stemX = this.onTimeX;
71072
71017
  }
71018
+ this.middleX = this.onTimeX;
71073
71019
  }
71074
71020
  }
71075
71021
 
@@ -71144,7 +71090,9 @@ class SlashBarRenderer extends LineBarRenderer {
71144
71090
  const symbol = SlashNoteHeadGlyph.getSymbol(beat.duration);
71145
71091
  const scale = beat.graceType !== GraceType.None ? NoteHeadGlyph.GraceScale : 1;
71146
71092
  slashY -= this.smuflMetrics.stemUp.has(symbol) ? this.smuflMetrics.stemUp.get(symbol).bottomY * scale : 0;
71147
- slashY -= this.smuflMetrics.standardStemLength + scale;
71093
+ if (!beat.isRest) {
71094
+ slashY -= this.smuflMetrics.standardStemLength + scale;
71095
+ }
71148
71096
  return slashY;
71149
71097
  }
71150
71098
  getFlagBottomY(beat, _direction) {
@@ -71228,6 +71176,11 @@ class SlashBarRenderer extends LineBarRenderer {
71228
71176
  _?.[Symbol.dispose]?.();
71229
71177
  }
71230
71178
  }
71179
+ paintBeamHelper(cx, cy, canvas, h, flagsElement, beamsElement) {
71180
+ if (h.voice?.index === 0) {
71181
+ super.paintBeamHelper(cx, cy, canvas, h, flagsElement, beamsElement);
71182
+ }
71183
+ }
71231
71184
  }
71232
71185
 
71233
71186
  /**
@@ -71684,7 +71637,6 @@ class TabNoteChordGlyph extends Glyph {
71684
71637
  _deadSlapped = null;
71685
71638
  _isGrace;
71686
71639
  beat;
71687
- beamingHelper;
71688
71640
  maxStringNote = null;
71689
71641
  minStringNote = null;
71690
71642
  beatEffects = new Map();
@@ -71833,11 +71785,6 @@ class TabNoteChordGlyph extends Glyph {
71833
71785
  }
71834
71786
  }
71835
71787
  }
71836
- updateBeamingHelper(cx) {
71837
- if (this.beamingHelper && this.beamingHelper.isPositionFrom('tab', this.beat)) {
71838
- this.beamingHelper.registerBeatLineX('tab', this.beat, cx + this.x + this.width / 2, cx + this.x + this.width / 2);
71839
- }
71840
- }
71841
71788
  }
71842
71789
 
71843
71790
  /**
@@ -71845,7 +71792,6 @@ class TabNoteChordGlyph extends Glyph {
71845
71792
  */
71846
71793
  class TabRestGlyph extends MusicFontGlyph {
71847
71794
  _isVisibleRest;
71848
- beamingHelper;
71849
71795
  constructor(x, y, isVisibleRest, duration) {
71850
71796
  super(x, y, 1, ScoreRestGlyph.getSymbol(duration));
71851
71797
  this._isVisibleRest = isVisibleRest;
@@ -71853,11 +71799,6 @@ class TabRestGlyph extends MusicFontGlyph {
71853
71799
  doLayout() {
71854
71800
  super.doLayout();
71855
71801
  }
71856
- updateBeamingHelper(cx) {
71857
- if (this.beamingHelper && this.beamingHelper.isPositionFrom('tab', this.beat)) {
71858
- this.beamingHelper.registerBeatLineX('tab', this.beat, cx + this.x + this.width / 2, cx + this.x + this.width / 2);
71859
- }
71860
- }
71861
71802
  paint(cx, cy, canvas) {
71862
71803
  if (this._isVisibleRest) {
71863
71804
  const _ = ElementStyleHelper.beat(canvas, BeatSubElement.GuitarTabRests, this.beat);
@@ -71914,7 +71855,6 @@ class TabBeatGlyph extends BeatOnNoteGlyphBase {
71914
71855
  slashNoteHead.effectElement = BeatSubElement.GuitarTabEffects;
71915
71856
  this.slash = slashNoteHead;
71916
71857
  slashNoteHead.beat = this.container.beat;
71917
- slashNoteHead.beamingHelper = this.beamingHelper;
71918
71858
  this.addNormal(slashNoteHead);
71919
71859
  beatEffects = slashNoteHead.beatEffects;
71920
71860
  }
@@ -71922,7 +71862,6 @@ class TabBeatGlyph extends BeatOnNoteGlyphBase {
71922
71862
  const tabNoteNumbers = new TabNoteChordGlyph(0, 0, isGrace);
71923
71863
  this.noteNumbers = tabNoteNumbers;
71924
71864
  tabNoteNumbers.beat = this.container.beat;
71925
- tabNoteNumbers.beamingHelper = this.beamingHelper;
71926
71865
  for (const note of this.container.beat.notes) {
71927
71866
  if (note.isVisible) {
71928
71867
  this._createNoteGlyph(note);
@@ -71944,9 +71883,9 @@ class TabBeatGlyph extends BeatOnNoteGlyphBase {
71944
71883
  // Note dots
71945
71884
  //
71946
71885
  if (this.container.beat.dots > 0 && tabRenderer.rhythmMode !== TabRhythmMode.Hidden) {
71886
+ const y = tabRenderer.getFlagAndBarPos();
71947
71887
  for (let i = 0; i < this.container.beat.dots; i++) {
71948
- this.addEffect(new AugmentationDotGlyph(0, tabRenderer.lineOffset * tabRenderer.bar.staff.tuning.length +
71949
- tabRenderer.settings.notation.rhythmHeight));
71888
+ this.addEffect(new AugmentationDotGlyph(0, y));
71950
71889
  }
71951
71890
  }
71952
71891
  }
@@ -71956,7 +71895,6 @@ class TabBeatGlyph extends BeatOnNoteGlyphBase {
71956
71895
  const restGlyph = new TabRestGlyph(0, y, tabRenderer.showRests, this.container.beat.duration);
71957
71896
  this.restGlyph = restGlyph;
71958
71897
  restGlyph.beat = this.container.beat;
71959
- restGlyph.beamingHelper = this.beamingHelper;
71960
71898
  this.addNormal(restGlyph);
71961
71899
  //
71962
71900
  // Note dots
@@ -71982,30 +71920,21 @@ class TabBeatGlyph extends BeatOnNoteGlyphBase {
71982
71920
  this.width = w;
71983
71921
  this.computedWidth = w;
71984
71922
  if (this.container.beat.isEmpty) {
71985
- this.centerX = this.width / 2;
71923
+ this.onTimeX = this.width / 2;
71986
71924
  }
71987
71925
  else if (this.restGlyph) {
71988
- this.centerX = this.restGlyph.x + this.restGlyph.width / 2;
71926
+ this.onTimeX = this.restGlyph.x + this.restGlyph.width / 2;
71989
71927
  }
71990
71928
  else if (this.noteNumbers) {
71991
- this.centerX = this.noteNumbers.x + this.noteNumbers.noteStringWidth / 2;
71929
+ this.onTimeX = this.noteNumbers.x + this.noteNumbers.noteStringWidth / 2;
71992
71930
  }
71993
71931
  else if (this.slash) {
71994
- this.centerX = this.slash.x + this.slash.width / 2;
71932
+ this.onTimeX = this.slash.x + this.slash.width / 2;
71995
71933
  }
71934
+ this.middleX = this.onTimeX;
71935
+ this.stemX = this.middleX;
71996
71936
  for (const g of centeredEffectGlyphs) {
71997
- g.x = this.centerX;
71998
- }
71999
- }
72000
- updateBeamingHelper() {
72001
- if (this.noteNumbers) {
72002
- this.noteNumbers.updateBeamingHelper(this.container.x + this.x);
72003
- }
72004
- else if (this.restGlyph) {
72005
- this.restGlyph.updateBeamingHelper(this.container.x + this.x);
72006
- }
72007
- else if (this.slash) {
72008
- this.slash.updateBeamingHelper(this.container.x + this.x);
71937
+ g.x = this.onTimeX;
72009
71938
  }
72010
71939
  }
72011
71940
  _createNoteGlyph(n) {
@@ -72018,7 +71947,7 @@ class TabBeatGlyph extends BeatOnNoteGlyphBase {
72018
71947
  this.noteNumbers.addNoteGlyph(noteNumberGlyph, n);
72019
71948
  const topY = noteNumberGlyph.y - noteNumberGlyph.height / 2;
72020
71949
  const bottomY = topY + noteNumberGlyph.height;
72021
- this.renderer.helpers.collisionHelper.reserveBeatSlot(this.container.beat, topY, bottomY);
71950
+ this.renderer.collisionHelper.reserveBeatSlot(this.container.beat, topY, bottomY);
72022
71951
  const minString = tr.minString;
72023
71952
  const maxString = tr.maxString;
72024
71953
  if (Number.isNaN(minString) || minString < n.string) {
@@ -72290,7 +72219,7 @@ class TabBarRenderer extends LineBarRenderer {
72290
72219
  }
72291
72220
  createLinePreBeatGlyphs() {
72292
72221
  // Clef
72293
- if (this.isFirstOfLine) {
72222
+ if (this.isFirstOfStaff) {
72294
72223
  const center = (this.bar.staff.tuning.length - 1) / 2;
72295
72224
  this.createStartSpacing();
72296
72225
  this.addPreBeatGlyph(new TabClefGlyph(0, this.getLineY(center)));
@@ -72381,14 +72310,14 @@ class TabBarRenderer extends LineBarRenderer {
72381
72310
  // currently only used for duplets
72382
72311
  return this.getFlagAndBarPos();
72383
72312
  }
72384
- shouldPaintFlag(beat, h) {
72385
- if (!super.shouldPaintFlag(beat, h)) {
72313
+ shouldPaintFlag(beat) {
72314
+ if (!super.shouldPaintFlag(beat)) {
72386
72315
  return false;
72387
72316
  }
72388
72317
  if (beat.graceType !== GraceType.None) {
72389
72318
  return false;
72390
72319
  }
72391
- return this.drawBeamHelperAsFlags(h);
72320
+ return true;
72392
72321
  }
72393
72322
  paintBeamingStem(beat, cy, x, topY, bottomY, canvas) {
72394
72323
  if (bottomY < topY) {