@coderline/alphatab 1.3.0-alpha.411 → 1.3.0-alpha.418

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/alphaTab.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * alphaTab v1.3.0-alpha.411 (develop, build 411)
2
+ * alphaTab v1.3.0-alpha.418 (develop, build 418)
3
3
  *
4
4
  * Copyright © 2022, Daniel Kuschny and Contributors, All rights reserved.
5
5
  *
@@ -24144,6 +24144,10 @@ class BeatContainerGlyph extends Glyph {
24144
24144
  get onTimeX() {
24145
24145
  return this.onNotes.x + this.onNotes.centerX;
24146
24146
  }
24147
+ addTie(tie) {
24148
+ tie.renderer = this.renderer;
24149
+ this.ties.push(tie);
24150
+ }
24147
24151
  registerLayoutingInfo(layoutings) {
24148
24152
  let preBeatStretch = this.preNotes.computedWidth + this.onNotes.centerX;
24149
24153
  if (this.beat.graceGroup && !this.beat.graceGroup.isComplete) {
@@ -24152,8 +24156,11 @@ class BeatContainerGlyph extends Glyph {
24152
24156
  let postBeatStretch = this.onNotes.computedWidth - this.onNotes.centerX;
24153
24157
  // make space for flag
24154
24158
  const helper = this.renderer.helpers.getBeamingHelperForBeat(this.beat);
24155
- if (helper && helper.hasFlag || this.beat.graceType !== GraceType.None) {
24156
- postBeatStretch += (FlagGlyph.FlagWidth * this.scale * (this.beat.graceType !== GraceType.None ? NoteHeadGlyph.GraceScale : 1));
24159
+ if ((helper && helper.hasFlag) || this.beat.graceType !== GraceType.None) {
24160
+ postBeatStretch +=
24161
+ FlagGlyph.FlagWidth *
24162
+ this.scale *
24163
+ (this.beat.graceType !== GraceType.None ? NoteHeadGlyph.GraceScale : 1);
24157
24164
  }
24158
24165
  for (const tie of this.ties) {
24159
24166
  postBeatStretch += tie.width;
@@ -24193,6 +24200,7 @@ class BeatContainerGlyph extends Glyph {
24193
24200
  while (i >= 0) {
24194
24201
  this.createTies(this.beat.notes[i--]);
24195
24202
  }
24203
+ this.renderer.registerTies(this.ties);
24196
24204
  this.updateWidth();
24197
24205
  }
24198
24206
  updateWidth() {
@@ -24224,9 +24232,6 @@ class BeatContainerGlyph extends Glyph {
24224
24232
  this.width = this.minWidth;
24225
24233
  }
24226
24234
  scaleToWidth(beatWidth) {
24227
- for (let tie of this.ties) {
24228
- tie.doLayout();
24229
- }
24230
24235
  this.onNotes.updateBeamingHelper();
24231
24236
  this.width = beatWidth;
24232
24237
  }
@@ -25024,6 +25029,7 @@ class AlphaTabApiBase {
25024
25029
  this._playerState = PlayerState.Paused;
25025
25030
  // we need to update our position caches if we render a tablature
25026
25031
  this.renderer.postRenderFinished.on(() => {
25032
+ this._currentBeat = null;
25027
25033
  this.cursorUpdateTick(this._previousTick, false, this._previousTick > 10);
25028
25034
  });
25029
25035
  if (this.player) {
@@ -25747,7 +25753,7 @@ class BrowserMouseEventArgs {
25747
25753
  class HtmlElementContainer {
25748
25754
  constructor(element) {
25749
25755
  this._resizeListeners = 0;
25750
- this._lastBounds = new Bounds();
25756
+ this.lastBounds = new Bounds();
25751
25757
  this.element = element;
25752
25758
  this.mouseDown = {
25753
25759
  on: (value) => {
@@ -25838,23 +25844,23 @@ class HtmlElementContainer {
25838
25844
  }
25839
25845
  setBounds(x, y, w, h) {
25840
25846
  if (isNaN(x)) {
25841
- x = this._lastBounds.x;
25847
+ x = this.lastBounds.x;
25842
25848
  }
25843
25849
  if (isNaN(y)) {
25844
- y = this._lastBounds.y;
25850
+ y = this.lastBounds.y;
25845
25851
  }
25846
25852
  if (isNaN(w)) {
25847
- w = this._lastBounds.w;
25853
+ w = this.lastBounds.w;
25848
25854
  }
25849
25855
  if (isNaN(h)) {
25850
- h = this._lastBounds.h;
25856
+ h = this.lastBounds.h;
25851
25857
  }
25852
25858
  this.element.style.transform = `translate(${x}px, ${y}px) scale(${w}, ${h})`;
25853
25859
  this.element.style.transformOrigin = 'top left';
25854
- this._lastBounds.x = x;
25855
- this._lastBounds.y = y;
25856
- this._lastBounds.w = w;
25857
- this._lastBounds.h = h;
25860
+ this.lastBounds.x = x;
25861
+ this.lastBounds.y = y;
25862
+ this.lastBounds.w = w;
25863
+ this.lastBounds.h = h;
25858
25864
  }
25859
25865
  appendChild(child) {
25860
25866
  this.element.appendChild(child.element);
@@ -26933,6 +26939,71 @@ class AlphaSynthAudioWorkletOutput extends AlphaSynthWebAudioOutputBase {
26933
26939
  }
26934
26940
  }
26935
26941
 
26942
+ /**
26943
+ * An IContainer implementation which can be used for cursors and select ranges
26944
+ * where browser scaling is relevant.
26945
+ *
26946
+ * The problem is that with having 1x1 pixel elements which are sized then to the actual size with a
26947
+ * scale transform this cannot be combined properly with a browser zoom.
26948
+ *
26949
+ * The browser will apply first the browser zoom to the 1x1px element and then apply the scale leaving it always
26950
+ * at full scale instead of a 50% browser zoom.
26951
+ *
26952
+ * This is solved in this container by scaling the element first up to a higher degree (as specified)
26953
+ * so that the browser can do a scaling according to typical zoom levels and then the scaling will work.
26954
+ * @target web
26955
+ */
26956
+ class ScalableHtmlElementContainer extends HtmlElementContainer {
26957
+ constructor(element, xscale, yscale) {
26958
+ super(element);
26959
+ this._xscale = xscale;
26960
+ this._yscale = yscale;
26961
+ }
26962
+ get width() {
26963
+ return this.element.offsetWidth / this._xscale;
26964
+ }
26965
+ set width(value) {
26966
+ this.element.style.width = value * this._xscale + 'px';
26967
+ }
26968
+ get height() {
26969
+ return this.element.offsetHeight / this._yscale;
26970
+ }
26971
+ set height(value) {
26972
+ if (value >= 0) {
26973
+ this.element.style.height = value * this._yscale + 'px';
26974
+ }
26975
+ else {
26976
+ this.element.style.height = '100%';
26977
+ }
26978
+ }
26979
+ setBounds(x, y, w, h) {
26980
+ if (isNaN(x)) {
26981
+ x = this.lastBounds.x;
26982
+ }
26983
+ if (isNaN(y)) {
26984
+ y = this.lastBounds.y;
26985
+ }
26986
+ if (isNaN(w)) {
26987
+ w = this.lastBounds.w;
26988
+ }
26989
+ else {
26990
+ w = w / this._xscale;
26991
+ }
26992
+ if (isNaN(h)) {
26993
+ h = this.lastBounds.h;
26994
+ }
26995
+ else {
26996
+ h = h / this._yscale;
26997
+ }
26998
+ this.element.style.transform = `translate(${x}px, ${y}px) scale(${w}, ${h})`;
26999
+ this.element.style.transformOrigin = 'top left';
27000
+ this.lastBounds.x = x;
27001
+ this.lastBounds.y = y;
27002
+ this.lastBounds.w = w;
27003
+ this.lastBounds.h = h;
27004
+ }
27005
+ }
27006
+
26936
27007
  /**
26937
27008
  * @target web
26938
27009
  */
@@ -27417,9 +27488,11 @@ class BrowserUiFacade {
27417
27488
  cursorWrapper.classList.add('at-cursors');
27418
27489
  let selectionWrapper = document.createElement('div');
27419
27490
  selectionWrapper.classList.add('at-selection');
27420
- let barCursor = document.createElement('div');
27491
+ const barCursorContainer = this.createScalingElement();
27492
+ const beatCursorContainer = this.createScalingElement();
27493
+ let barCursor = barCursorContainer.element;
27421
27494
  barCursor.classList.add('at-cursor-bar');
27422
- let beatCursor = document.createElement('div');
27495
+ let beatCursor = beatCursorContainer.element;
27423
27496
  beatCursor.classList.add('at-cursor-beat');
27424
27497
  // required css styles
27425
27498
  element.style.position = 'relative';
@@ -27433,21 +27506,23 @@ class BrowserUiFacade {
27433
27506
  barCursor.style.left = '0';
27434
27507
  barCursor.style.top = '0';
27435
27508
  barCursor.style.willChange = 'transform';
27436
- barCursor.style.width = '1px';
27437
- barCursor.style.height = '1px';
27509
+ barCursorContainer.width = 1;
27510
+ barCursorContainer.height = 1;
27511
+ barCursorContainer.setBounds(0, 0, 1, 1);
27438
27512
  beatCursor.style.position = 'absolute';
27439
27513
  beatCursor.style.transition = 'all 0s linear';
27440
27514
  beatCursor.style.left = '0';
27441
27515
  beatCursor.style.top = '0';
27442
27516
  beatCursor.style.willChange = 'transform';
27443
- beatCursor.style.width = '3px';
27444
- beatCursor.style.height = '1px';
27517
+ beatCursorContainer.width = 3;
27518
+ beatCursorContainer.height = 1;
27519
+ beatCursorContainer.setBounds(0, 0, 1, 1);
27445
27520
  // add cursors to UI
27446
27521
  element.insertBefore(cursorWrapper, element.firstChild);
27447
27522
  cursorWrapper.appendChild(selectionWrapper);
27448
27523
  cursorWrapper.appendChild(barCursor);
27449
27524
  cursorWrapper.appendChild(beatCursor);
27450
- return new Cursors(new HtmlElementContainer(cursorWrapper), new HtmlElementContainer(barCursor), new HtmlElementContainer(beatCursor), new HtmlElementContainer(selectionWrapper));
27525
+ return new Cursors(new HtmlElementContainer(cursorWrapper), barCursorContainer, beatCursorContainer, new HtmlElementContainer(selectionWrapper));
27451
27526
  }
27452
27527
  getOffset(scrollContainer, container) {
27453
27528
  let element = container.element;
@@ -27501,11 +27576,20 @@ class BrowserUiFacade {
27501
27576
  return this._scrollContainer;
27502
27577
  }
27503
27578
  createSelectionElement() {
27504
- let element = document.createElement('div');
27579
+ return this.createScalingElement();
27580
+ }
27581
+ createScalingElement() {
27582
+ const element = document.createElement('div');
27505
27583
  element.style.position = 'absolute';
27506
- element.style.width = '1px';
27507
- element.style.height = '1px';
27508
- return new HtmlElementContainer(element);
27584
+ // to typical browser zoom levels are:
27585
+ // Chromium: 25,33,50,67,75,80,90, 100, 110, 125, 150, 175, 200, 250, 300, 400, 500
27586
+ // Firefox: 30, 50, 67, 80, 90, 100, 110, 120, 133, 150, 170, 200, 240, 300, 400, 500
27587
+ // with having a 100x100 scaling container we should be able to provide appropriate scaling
27588
+ const container = new ScalableHtmlElementContainer(element, 100, 100);
27589
+ container.width = 1;
27590
+ container.height = 1;
27591
+ container.setBounds(0, 0, 1, 1);
27592
+ return container;
27509
27593
  }
27510
27594
  scrollToY(element, scrollTargetY, speed) {
27511
27595
  this.internalScrollToY(element.element, scrollTargetY, speed);
@@ -29420,6 +29504,7 @@ class BarRendererBase {
29420
29504
  this._preBeatGlyphs = new LeftToRightLayoutingGlyphGroup();
29421
29505
  this._voiceContainers = new Map();
29422
29506
  this._postBeatGlyphs = new LeftToRightLayoutingGlyphGroup();
29507
+ this._ties = [];
29423
29508
  this.x = 0;
29424
29509
  this.y = 0;
29425
29510
  this.width = 0;
@@ -29470,18 +29555,25 @@ class BarRendererBase {
29470
29555
  }
29471
29556
  return this.scoreRenderer.layout.getRendererForBar(this.staff.staveId, this.bar.previousBar);
29472
29557
  }
29558
+ registerTies(ties) {
29559
+ this._ties.push(...ties);
29560
+ }
29473
29561
  get middleYPosition() {
29474
29562
  return 0;
29475
29563
  }
29476
29564
  registerOverflowTop(topOverflow) {
29477
29565
  if (topOverflow > this.topOverflow) {
29478
29566
  this.topOverflow = topOverflow;
29567
+ return true;
29479
29568
  }
29569
+ return false;
29480
29570
  }
29481
29571
  registerOverflowBottom(bottomOverflow) {
29482
29572
  if (bottomOverflow > this.bottomOverflow) {
29483
29573
  this.bottomOverflow = bottomOverflow;
29574
+ return true;
29484
29575
  }
29576
+ return false;
29485
29577
  }
29486
29578
  scaleToWidth(width) {
29487
29579
  // preBeat and postBeat glyphs do not get resized
@@ -29547,12 +29639,35 @@ class BarRendererBase {
29547
29639
  }
29548
29640
  finalizeRenderer() {
29549
29641
  this.isFinalized = true;
29642
+ let didChangeOverflows = false;
29643
+ // allow spacing to be used for tie overflows
29644
+ const barTop = this.y - this.staff.topSpacing;
29645
+ const barBottom = this.y + this.height + this.staff.bottomSpacing;
29646
+ for (const tie of this._ties) {
29647
+ tie.doLayout();
29648
+ if (tie.height > 0) {
29649
+ const bottomOverflow = tie.y + tie.height - barBottom;
29650
+ if (bottomOverflow > 0) {
29651
+ if (this.registerOverflowBottom(bottomOverflow)) {
29652
+ didChangeOverflows = true;
29653
+ }
29654
+ }
29655
+ const topOverflow = tie.y - barTop;
29656
+ if (topOverflow < 0) {
29657
+ if (this.registerOverflowTop(topOverflow * -1)) {
29658
+ didChangeOverflows = true;
29659
+ }
29660
+ }
29661
+ }
29662
+ }
29663
+ return didChangeOverflows;
29550
29664
  }
29551
29665
  doLayout() {
29552
29666
  if (!this.bar) {
29553
29667
  return;
29554
29668
  }
29555
29669
  this.helpers.initialize();
29670
+ this._ties = [];
29556
29671
  this._preBeatGlyphs = new LeftToRightLayoutingGlyphGroup();
29557
29672
  this._preBeatGlyphs.renderer = this;
29558
29673
  this._voiceContainers.clear();
@@ -29641,7 +29756,7 @@ class BarRendererBase {
29641
29756
  paintBackground(cx, cy, canvas) {
29642
29757
  this.layoutingInfo.paint(cx + this.x + this._preBeatGlyphs.x + this._preBeatGlyphs.width, cy + this.y + this.height, canvas);
29643
29758
  // canvas.color = Color.random();
29644
- // canvas.fillRect(cx + this.x + this._preBeatGlyphs.x, cy + this.y, this._preBeatGlyphs.width, this.height);
29759
+ // canvas.fillRect(cx + this.x, cy + this.y, this.width, this.height);
29645
29760
  }
29646
29761
  buildBoundingsLookup(masterBarBounds, cx, cy) {
29647
29762
  let barBounds = new BarBounds();
@@ -30114,12 +30229,15 @@ class EffectBarRenderer extends BarRendererBase {
30114
30229
  super.updateSizes();
30115
30230
  }
30116
30231
  finalizeRenderer() {
30117
- super.finalizeRenderer();
30118
- this.updateHeight();
30232
+ let didChange = super.finalizeRenderer();
30233
+ if (this.updateHeight()) {
30234
+ didChange = true;
30235
+ }
30236
+ return didChange;
30119
30237
  }
30120
30238
  updateHeight() {
30121
30239
  if (!this.sizingInfo) {
30122
- return;
30240
+ return false;
30123
30241
  }
30124
30242
  let y = 0;
30125
30243
  for (let slot of this.sizingInfo.slots) {
@@ -30130,7 +30248,11 @@ class EffectBarRenderer extends BarRendererBase {
30130
30248
  }
30131
30249
  y += slot.shared.height;
30132
30250
  }
30133
- this.height = y;
30251
+ if (y !== this.height) {
30252
+ this.height = y;
30253
+ return true;
30254
+ }
30255
+ return false;
30134
30256
  }
30135
30257
  applyLayoutingInfo() {
30136
30258
  if (!super.applyLayoutingInfo()) {
@@ -31400,20 +31522,20 @@ class TrillGlyph extends EffectGlyph {
31400
31522
  }
31401
31523
  doLayout() {
31402
31524
  super.doLayout();
31403
- this.height = 20 * this.scale;
31525
+ this.height = this.renderer.resources.markerFont.size * this.scale;
31404
31526
  }
31405
31527
  paint(cx, cy, canvas) {
31406
31528
  let res = this.renderer.resources;
31407
31529
  canvas.font = res.markerFont;
31408
31530
  let textw = canvas.measureText('tr');
31409
- canvas.fillText('tr', cx + this.x, cy + this.y + canvas.font.size / 2);
31531
+ canvas.fillText('tr', cx + this.x, cy + this.y);
31410
31532
  let startX = textw + 3 * this.scale;
31411
31533
  let endX = this.width - startX;
31412
31534
  let waveScale = 1.2;
31413
31535
  let step = 11 * this.scale * waveScale;
31414
31536
  let loops = Math.max(1, (endX - startX) / step);
31415
31537
  let loopX = startX;
31416
- let loopY = cy + this.y + this.height;
31538
+ let loopY = cy + this.y + this.height * 1.2;
31417
31539
  for (let i = 0; i < loops; i++) {
31418
31540
  canvas.fillMusicFontSymbol(cx + this.x + loopX, loopY, waveScale, MusicFontSymbol.WiggleTrill, false);
31419
31541
  loopX += step;
@@ -31972,8 +32094,15 @@ class RenderStaff {
31972
32094
  // the space over the bar renderers, for now we evenly apply the space to all bars
31973
32095
  let difference = width - this.staveGroup.width;
31974
32096
  let spacePerBar = difference / this.barRenderers.length;
32097
+ let x = 0;
32098
+ let topOverflow = this.topOverflow;
31975
32099
  for (let i = 0, j = this.barRenderers.length; i < j; i++) {
31976
- this.barRenderers[i].scaleToWidth(this.barRenderers[i].width + spacePerBar);
32100
+ this.barRenderers[i].x = x;
32101
+ this.barRenderers[i].y = this.topSpacing + topOverflow;
32102
+ if (difference !== 0) {
32103
+ this.barRenderers[i].scaleToWidth(this.barRenderers[i].width + spacePerBar);
32104
+ }
32105
+ x += this.barRenderers[i].width;
31977
32106
  }
31978
32107
  }
31979
32108
  get topOverflow() {
@@ -31997,19 +32126,29 @@ class RenderStaff {
31997
32126
  return m;
31998
32127
  }
31999
32128
  finalizeStaff() {
32000
- let x = 0;
32001
32129
  this.height = 0;
32130
+ // 1st pass: let all renderers finalize themselves, this might cause
32131
+ // changes in the overflows
32132
+ let needsSecondPass = false;
32002
32133
  let topOverflow = this.topOverflow;
32003
- let bottomOverflow = this.bottomOverflow;
32004
32134
  for (let i = 0; i < this.barRenderers.length; i++) {
32005
- this.barRenderers[i].x = x;
32006
32135
  this.barRenderers[i].y = this.topSpacing + topOverflow;
32007
32136
  this.height = Math.max(this.height, this.barRenderers[i].height);
32008
- this.barRenderers[i].finalizeRenderer();
32009
- x += this.barRenderers[i].width;
32137
+ if (this.barRenderers[i].finalizeRenderer()) {
32138
+ needsSecondPass = true;
32139
+ }
32140
+ }
32141
+ // 2nd pass: move renderers to correct position respecting the new overflows
32142
+ if (needsSecondPass) {
32143
+ topOverflow = this.topOverflow;
32144
+ for (let i = 0; i < this.barRenderers.length; i++) {
32145
+ this.barRenderers[i].y = this.topSpacing + topOverflow;
32146
+ this.height = Math.max(this.height, this.barRenderers[i].height);
32147
+ this.barRenderers[i].finalizeRenderer();
32148
+ }
32010
32149
  }
32011
32150
  if (this.height > 0) {
32012
- this.height += this.topSpacing + topOverflow + bottomOverflow + this.bottomSpacing;
32151
+ this.height += this.topSpacing + topOverflow + this.bottomOverflow + this.bottomSpacing;
32013
32152
  }
32014
32153
  }
32015
32154
  paint(cx, cy, canvas, startIndex, count) {
@@ -33199,7 +33338,7 @@ class HorizontalScreenLayout extends ScoreLayout {
33199
33338
  ' to ' +
33200
33339
  currentPartial.masterBars[currentPartial.masterBars.length - 1].index, null);
33201
33340
  }
33202
- this._group.finalizeGroup();
33341
+ this.finalizeGroup();
33203
33342
  this.height = Math.floor(this._group.y + this._group.height);
33204
33343
  this.width = this._group.x + this._group.width + this._pagePadding[2];
33205
33344
  currentBarIndex = 0;
@@ -33237,6 +33376,10 @@ class HorizontalScreenLayout extends ScoreLayout {
33237
33376
  }
33238
33377
  this.height = this.layoutAndRenderAnnotation(this.height) + this._pagePadding[3];
33239
33378
  }
33379
+ finalizeGroup() {
33380
+ this._group.scaleToWidth(this._group.width);
33381
+ this._group.finalizeGroup();
33382
+ }
33240
33383
  }
33241
33384
  HorizontalScreenLayout.PagePadding = [20, 20, 20, 20];
33242
33385
  HorizontalScreenLayout.GroupSpacing = 20;
@@ -33433,7 +33576,6 @@ class PageViewLayout extends ScoreLayout {
33433
33576
  for (let i = 0; i < this._groups.length; i++) {
33434
33577
  let group = this._groups[i];
33435
33578
  this.fitGroup(group);
33436
- group.finalizeGroup();
33437
33579
  y += this.paintGroup(group, oldHeight);
33438
33580
  }
33439
33581
  }
@@ -33466,7 +33608,6 @@ class PageViewLayout extends ScoreLayout {
33466
33608
  group.isLast = this.lastBarIndex === group.lastBarIndex;
33467
33609
  this._groups.push(group);
33468
33610
  this.fitGroup(group);
33469
- group.finalizeGroup();
33470
33611
  y += this.paintGroup(group, oldHeight);
33471
33612
  // note: we do not increase currentIndex here to have it added to the next group
33472
33613
  group = this.createEmptyStaveGroup();
@@ -33478,7 +33619,6 @@ class PageViewLayout extends ScoreLayout {
33478
33619
  group.isLast = this.lastBarIndex === group.lastBarIndex;
33479
33620
  // don't forget to finish the last group
33480
33621
  this.fitGroup(group);
33481
- group.finalizeGroup();
33482
33622
  y += this.paintGroup(group, oldHeight);
33483
33623
  }
33484
33624
  return y;
@@ -33497,7 +33637,6 @@ class PageViewLayout extends ScoreLayout {
33497
33637
  currentBarIndex = group.lastBarIndex + 1;
33498
33638
  // finalize group (sizing etc).
33499
33639
  this.fitGroup(group);
33500
- group.finalizeGroup();
33501
33640
  Logger.debug(this.name, 'Rendering partial from bar ' + group.firstBarIndex + ' to ' + group.lastBarIndex, null);
33502
33641
  y += this.paintGroup(group, y);
33503
33642
  }
@@ -33534,6 +33673,10 @@ class PageViewLayout extends ScoreLayout {
33534
33673
  if (group.isFull || group.width > this.maxWidth) {
33535
33674
  group.scaleToWidth(this.maxWidth);
33536
33675
  }
33676
+ else {
33677
+ group.scaleToWidth(group.width);
33678
+ }
33679
+ group.finalizeGroup();
33537
33680
  }
33538
33681
  createStaveGroup(currentBarIndex, endIndex) {
33539
33682
  let group = this.createEmptyStaveGroup();
@@ -33958,27 +34101,33 @@ class TieGlyph extends Glyph {
33958
34101
  this.startNoteRenderer = null;
33959
34102
  this.endNoteRenderer = null;
33960
34103
  this.tieDirection = BeamDirection.Up;
34104
+ this._startX = 0;
34105
+ this._startY = 0;
34106
+ this._endX = 0;
34107
+ this._endY = 0;
34108
+ this._tieHeight = 0;
34109
+ this._shouldDraw = false;
33961
34110
  this.startBeat = startBeat;
33962
34111
  this.endBeat = endBeat;
33963
34112
  this.forEnd = forEnd;
33964
34113
  }
33965
34114
  doLayout() {
33966
34115
  this.width = 0;
33967
- }
33968
- paint(cx, cy, canvas) {
34116
+ // TODO fix nullability of start/end beat,
33969
34117
  if (!this.endBeat) {
34118
+ this._shouldDraw = false;
33970
34119
  return;
33971
34120
  }
33972
- // TODO fix nullability of start/end beat,
33973
34121
  let startNoteRenderer = this.renderer.scoreRenderer.layout.getRendererForBar(this.renderer.staff.staveId, this.startBeat.voice.bar);
33974
34122
  this.startNoteRenderer = startNoteRenderer;
33975
34123
  let endNoteRenderer = this.renderer.scoreRenderer.layout.getRendererForBar(this.renderer.staff.staveId, this.endBeat.voice.bar);
33976
34124
  this.endNoteRenderer = endNoteRenderer;
33977
- let startX = 0;
33978
- let endX = 0;
33979
- let startY = 0;
33980
- let endY = 0;
33981
- let shouldDraw = false;
34125
+ this._startX = 0;
34126
+ this._endX = 0;
34127
+ this._startY = 0;
34128
+ this._endY = 0;
34129
+ this.height = 0;
34130
+ this._shouldDraw = false;
33982
34131
  // if we are on the tie start, we check if we
33983
34132
  // either can draw till the end note, or we just can draw till the bar end
33984
34133
  this.tieDirection = !startNoteRenderer
@@ -33987,39 +34136,54 @@ class TieGlyph extends Glyph {
33987
34136
  if (!this.forEnd && startNoteRenderer) {
33988
34137
  // line break or bar break
33989
34138
  if (startNoteRenderer !== endNoteRenderer) {
33990
- startX = cx + startNoteRenderer.x + this.getStartX();
33991
- startY = cy + startNoteRenderer.y + this.getStartY() + this.yOffset;
34139
+ this._startX = startNoteRenderer.x + this.getStartX();
34140
+ this._startY = startNoteRenderer.y + this.getStartY() + this.yOffset;
33992
34141
  // line break: to bar end
33993
34142
  if (!endNoteRenderer || startNoteRenderer.staff !== endNoteRenderer.staff) {
33994
- endX = cx + startNoteRenderer.x + startNoteRenderer.width;
33995
- endY = startY;
34143
+ this._endX = startNoteRenderer.x + startNoteRenderer.width;
34144
+ this._endY = this._startY;
33996
34145
  }
33997
34146
  else {
33998
- endX = cx + endNoteRenderer.x + this.getEndX();
33999
- endY = cy + endNoteRenderer.y + this.getEndY() + this.yOffset;
34147
+ this._endX = endNoteRenderer.x + this.getEndX();
34148
+ this._endY = endNoteRenderer.y + this.getEndY() + this.yOffset;
34000
34149
  }
34001
34150
  }
34002
34151
  else {
34003
- startX = cx + startNoteRenderer.x + this.getStartX();
34004
- endX = cx + endNoteRenderer.x + this.getEndX();
34005
- startY = cy + startNoteRenderer.y + this.getStartY() + this.yOffset;
34006
- endY = cy + endNoteRenderer.y + this.getEndY() + this.yOffset;
34152
+ this._startX = startNoteRenderer.x + this.getStartX();
34153
+ this._endX = endNoteRenderer.x + this.getEndX();
34154
+ this._startY = startNoteRenderer.y + this.getStartY() + this.yOffset;
34155
+ this._endY = endNoteRenderer.y + this.getEndY() + this.yOffset;
34007
34156
  }
34008
- shouldDraw = true;
34157
+ this._shouldDraw = true;
34009
34158
  }
34010
34159
  else if (!startNoteRenderer || startNoteRenderer.staff !== endNoteRenderer.staff) {
34011
- startX = cx + endNoteRenderer.x;
34012
- endX = cx + endNoteRenderer.x + this.getEndX();
34013
- startY = cy + endNoteRenderer.y + this.getEndY() + this.yOffset;
34014
- endY = startY;
34015
- shouldDraw = true;
34160
+ this._startX = endNoteRenderer.x;
34161
+ this._endX = endNoteRenderer.x + this.getEndX();
34162
+ this._startY = endNoteRenderer.y + this.getEndY() + this.yOffset;
34163
+ this._endY = this._startY;
34164
+ this._shouldDraw = true;
34165
+ }
34166
+ if (this._shouldDraw) {
34167
+ this.y = Math.min(this._startY, this._endY);
34168
+ if (this.shouldDrawBendSlur()) {
34169
+ this._tieHeight = 0; // TODO: Bend slur height to be considered?
34170
+ }
34171
+ else {
34172
+ this._tieHeight = this.getTieHeight(this._startX, this._startY, this._endX, this._endY);
34173
+ this.height = TieGlyph.calculateActualTieHeight(this.renderer.scale, this._startX, this._startY, this._endX, this._endY, this.tieDirection === BeamDirection.Down, this._tieHeight, 4).h;
34174
+ }
34175
+ if (this.tieDirection === BeamDirection.Up) {
34176
+ this.y -= this.height;
34177
+ }
34016
34178
  }
34017
- if (shouldDraw) {
34179
+ }
34180
+ paint(cx, cy, canvas) {
34181
+ if (this._shouldDraw) {
34018
34182
  if (this.shouldDrawBendSlur()) {
34019
- TieGlyph.drawBendSlur(canvas, startX, startY, endX, endY, this.tieDirection === BeamDirection.Down, this.scale);
34183
+ TieGlyph.drawBendSlur(canvas, cx + this._startX, cy + this._startY, cx + this._endX, cy + this._endY, this.tieDirection === BeamDirection.Down, this.scale);
34020
34184
  }
34021
34185
  else {
34022
- TieGlyph.paintTie(canvas, this.scale, startX, startY, endX, endY, this.tieDirection === BeamDirection.Down, this.getTieHeight(startX, startY, endX, endY), 4);
34186
+ TieGlyph.paintTie(canvas, this.scale, cx + this._startX, cy + this._startY, cx + this._endX, cy + this._endY, this.tieDirection === BeamDirection.Down, this._tieHeight, 4);
34023
34187
  }
34024
34188
  }
34025
34189
  }
@@ -34044,9 +34208,42 @@ class TieGlyph extends Glyph {
34044
34208
  getEndX() {
34045
34209
  return 0;
34046
34210
  }
34047
- static paintTie(canvas, scale, x1, y1, x2, y2, down = false, offset = 22, size = 4) {
34211
+ static calculateActualTieHeight(scale, x1, y1, x2, y2, down, offset, size) {
34212
+ const cp = TieGlyph.computeBezierControlPoints(scale, x1, y1, x2, y2, down, offset, size);
34213
+ x1 = cp[0];
34214
+ y1 = cp[1];
34215
+ const cpx = cp[2];
34216
+ const cpy = cp[3];
34217
+ x2 = cp[6];
34218
+ y2 = cp[7];
34219
+ const tx = (x1 - cpx) / (x1 - 2 * cpx + x2);
34220
+ const ex = TieGlyph.calculateExtrema(x1, y1, cpx, cpy, x2, y2, tx);
34221
+ const xMin = ex.length > 0 ? Math.min(x1, x2, ex[0]) : Math.min(x1, x2);
34222
+ const xMax = ex.length > 0 ? Math.max(x1, x2, ex[0]) : Math.max(x1, x2);
34223
+ const ty = (y1 - cpy) / (y1 - 2 * cpy + y2);
34224
+ const ey = TieGlyph.calculateExtrema(x1, y1, cpx, cpy, x2, y2, ty);
34225
+ const yMin = ey.length > 0 ? Math.min(y1, y2, ey[1]) : Math.min(y1, y2);
34226
+ const yMax = ey.length > 0 ? Math.max(y1, y2, ey[1]) : Math.max(y1, y2);
34227
+ const b = new Bounds();
34228
+ b.x = xMin;
34229
+ b.y = yMin;
34230
+ b.w = xMax - xMin;
34231
+ b.h = yMax - yMin;
34232
+ return b;
34233
+ }
34234
+ static calculateExtrema(x1, y1, cpx, cpy, x2, y2, t) {
34235
+ if (t <= 0 || 1 <= t) {
34236
+ return [];
34237
+ }
34238
+ const c1x = x1 + (cpx - x1) * t;
34239
+ const c1y = y1 + (cpy - y1) * t;
34240
+ const c2x = cpx + (x2 - cpx) * t;
34241
+ const c2y = cpy + (y2 - cpy) * t;
34242
+ return [c1x + (c2x - c1x) * t, c1y + (c2y - c1y) * t];
34243
+ }
34244
+ static computeBezierControlPoints(scale, x1, y1, x2, y2, down, offset, size) {
34048
34245
  if (x1 === x2 && y1 === y2) {
34049
- return;
34246
+ return [];
34050
34247
  }
34051
34248
  // ensure endX > startX
34052
34249
  if (x2 < x1) {
@@ -34083,12 +34280,26 @@ class TieGlyph extends Glyph {
34083
34280
  let cp1Y = centerY + offset * normalVectorY;
34084
34281
  let cp2X = centerX + (offset - size) * normalVectorX;
34085
34282
  let cp2Y = centerY + (offset - size) * normalVectorY;
34283
+ return [x1, y1, cp1X, cp1Y, cp2X, cp2Y, x2, y2];
34284
+ }
34285
+ static paintTie(canvas, scale, x1, y1, x2, y2, down = false, offset = 22, size = 4) {
34286
+ const cps = TieGlyph.computeBezierControlPoints(scale, x1, y1, x2, y2, down, offset, size);
34086
34287
  canvas.beginPath();
34087
- canvas.moveTo(x1, y1);
34088
- canvas.quadraticCurveTo(cp1X, cp1Y, x2, y2);
34089
- canvas.quadraticCurveTo(cp2X, cp2Y, x1, y1);
34288
+ canvas.moveTo(cps[0], cps[1]);
34289
+ canvas.quadraticCurveTo(cps[2], cps[3], cps[6], cps[7]);
34290
+ canvas.quadraticCurveTo(cps[4], cps[5], cps[0], cps[1]);
34090
34291
  canvas.closePath();
34091
34292
  canvas.fill();
34293
+ // const c = canvas.color;
34294
+ // canvas.color = Color.random(100);
34295
+ // canvas.fillCircle(cps[0], cps[1], 4);
34296
+ // canvas.fillCircle(cps[2], cps[3], 4);
34297
+ // canvas.fillCircle(cps[4], cps[5], 4);
34298
+ // canvas.fillCircle(cps[7], cps[6], 4);
34299
+ // canvas.color = Color.random(100);
34300
+ // const bbox = TieGlyph.calculateActualTieHeight(scale, x1, y1, x2, y2, down, offset, size);
34301
+ // canvas.fillRect(bbox.x, bbox.y, bbox.w, bbox.h);
34302
+ // canvas.color = c;
34092
34303
  }
34093
34304
  static drawBendSlur(canvas, x1, y1, x2, y2, down, scale, slurText) {
34094
34305
  let normalVectorX = y2 - y1;
@@ -36910,7 +37121,7 @@ class ScoreBeatContainerGlyph extends BeatContainerGlyph {
36910
37121
  while (destination.nextBeat && destination.nextBeat.isLegatoDestination) {
36911
37122
  destination = destination.nextBeat;
36912
37123
  }
36913
- this.ties.push(new ScoreLegatoGlyph(this.beat, destination, false));
37124
+ this.addTie(new ScoreLegatoGlyph(this.beat, destination, false));
36914
37125
  }
36915
37126
  }
36916
37127
  else if (this.beat.isLegatoDestination) {
@@ -36920,7 +37131,7 @@ class ScoreBeatContainerGlyph extends BeatContainerGlyph {
36920
37131
  while (origin.previousBeat && origin.previousBeat.isLegatoOrigin) {
36921
37132
  origin = origin.previousBeat;
36922
37133
  }
36923
- this.ties.push(new ScoreLegatoGlyph(origin, this.beat, true));
37134
+ this.addTie(new ScoreLegatoGlyph(origin, this.beat, true));
36924
37135
  }
36925
37136
  }
36926
37137
  if (this._bend) {
@@ -36944,32 +37155,32 @@ class ScoreBeatContainerGlyph extends BeatContainerGlyph {
36944
37155
  n.tieDestination.isVisible) {
36945
37156
  // tslint:disable-next-line: no-unnecessary-type-assertion
36946
37157
  let tie = new ScoreTieGlyph(n, n.tieDestination, false);
36947
- this.ties.push(tie);
37158
+ this.addTie(tie);
36948
37159
  }
36949
37160
  if (n.isTieDestination && !n.tieOrigin.hasBend && !n.beat.hasWhammyBar) {
36950
37161
  let tie = new ScoreTieGlyph(n.tieOrigin, n, true);
36951
- this.ties.push(tie);
37162
+ this.addTie(tie);
36952
37163
  }
36953
37164
  // TODO: depending on the type we have other positioning
36954
37165
  // we should place glyphs in the preNotesGlyph or postNotesGlyph if needed
36955
37166
  if (n.slideInType !== SlideInType.None || n.slideOutType !== SlideOutType.None) {
36956
37167
  let l = new ScoreSlideLineGlyph(n.slideInType, n.slideOutType, n, this);
36957
- this.ties.push(l);
37168
+ this.addTie(l);
36958
37169
  }
36959
37170
  if (n.isSlurOrigin && n.slurDestination && n.slurDestination.isVisible) {
36960
37171
  // tslint:disable-next-line: no-unnecessary-type-assertion
36961
37172
  let tie = new ScoreSlurGlyph(n, n.slurDestination, false);
36962
- this.ties.push(tie);
37173
+ this.addTie(tie);
36963
37174
  }
36964
37175
  if (n.isSlurDestination) {
36965
37176
  let tie = new ScoreSlurGlyph(n.slurOrigin, n, true);
36966
- this.ties.push(tie);
37177
+ this.addTie(tie);
36967
37178
  }
36968
37179
  // start effect slur on first beat
36969
37180
  if (!this._effectSlur && n.isEffectSlurOrigin && n.effectSlurDestination) {
36970
37181
  const effectSlur = new ScoreSlurGlyph(n, n.effectSlurDestination, false);
36971
37182
  this._effectSlur = effectSlur;
36972
- this.ties.push(effectSlur);
37183
+ this.addTie(effectSlur);
36973
37184
  }
36974
37185
  // end effect slur on last beat
36975
37186
  if (!this._effectEndSlur && n.beat.isEffectSlurDestination && n.beat.effectSlurOrigin) {
@@ -36978,14 +37189,14 @@ class ScoreBeatContainerGlyph extends BeatContainerGlyph {
36978
37189
  let endNote = direction === BeamDirection.Up ? n.beat.minNote : n.beat.maxNote;
36979
37190
  const effectEndSlur = new ScoreSlurGlyph(startNote, endNote, true);
36980
37191
  this._effectEndSlur = effectEndSlur;
36981
- this.ties.push(effectEndSlur);
37192
+ this.addTie(effectEndSlur);
36982
37193
  }
36983
37194
  if (n.hasBend) {
36984
37195
  if (!this._bend) {
36985
37196
  const bend = new ScoreBendGlyph(n.beat);
36986
37197
  this._bend = bend;
36987
37198
  bend.renderer = this.renderer;
36988
- this.ties.push(bend);
37199
+ this.addTie(bend);
36989
37200
  }
36990
37201
  // tslint:disable-next-line: no-unnecessary-type-assertion
36991
37202
  this._bend.addBends(n);
@@ -38127,15 +38338,15 @@ class TabBeatContainerGlyph extends BeatContainerGlyph {
38127
38338
  let renderer = this.renderer;
38128
38339
  if (n.isTieOrigin && renderer.showTiedNotes && n.tieDestination.isVisible) {
38129
38340
  let tie = new TabTieGlyph(n, n.tieDestination, false);
38130
- this.ties.push(tie);
38341
+ this.addTie(tie);
38131
38342
  }
38132
38343
  if (n.isTieDestination && renderer.showTiedNotes) {
38133
38344
  let tie = new TabTieGlyph(n.tieOrigin, n, true);
38134
- this.ties.push(tie);
38345
+ this.addTie(tie);
38135
38346
  }
38136
38347
  if (n.isLeftHandTapped && !n.isHammerPullDestination) {
38137
38348
  let tapSlur = new TabTieGlyph(n, n, false);
38138
- this.ties.push(tapSlur);
38349
+ this.addTie(tapSlur);
38139
38350
  }
38140
38351
  // start effect slur on first beat
38141
38352
  if (n.isEffectSlurOrigin && n.effectSlurDestination) {
@@ -38149,7 +38360,7 @@ class TabBeatContainerGlyph extends BeatContainerGlyph {
38149
38360
  if (!expanded) {
38150
38361
  let effectSlur = new TabSlurGlyph(n, n.effectSlurDestination, false, false);
38151
38362
  this._effectSlurs.push(effectSlur);
38152
- this.ties.push(effectSlur);
38363
+ this.addTie(effectSlur);
38153
38364
  }
38154
38365
  }
38155
38366
  // end effect slur on last beat
@@ -38164,19 +38375,19 @@ class TabBeatContainerGlyph extends BeatContainerGlyph {
38164
38375
  if (!expanded) {
38165
38376
  let effectSlur = new TabSlurGlyph(n.effectSlurOrigin, n, false, true);
38166
38377
  this._effectSlurs.push(effectSlur);
38167
- this.ties.push(effectSlur);
38378
+ this.addTie(effectSlur);
38168
38379
  }
38169
38380
  }
38170
38381
  if (n.slideInType !== SlideInType.None || n.slideOutType !== SlideOutType.None) {
38171
38382
  let l = new TabSlideLineGlyph(n.slideInType, n.slideOutType, n, this);
38172
- this.ties.push(l);
38383
+ this.addTie(l);
38173
38384
  }
38174
38385
  if (n.hasBend) {
38175
38386
  if (!this._bend) {
38176
38387
  const bend = new TabBendGlyph();
38177
38388
  this._bend = bend;
38178
38389
  bend.renderer = this.renderer;
38179
- this.ties.push(bend);
38390
+ this.addTie(bend);
38180
38391
  }
38181
38392
  this._bend.addBends(n);
38182
38393
  }
@@ -41167,8 +41378,8 @@ class CoreSettings {
41167
41378
  // </auto-generated>
41168
41379
  class VersionInfo {
41169
41380
  }
41170
- VersionInfo.version = '1.3.0-alpha.411';
41171
- VersionInfo.date = '2022-10-04T01:07:09.329Z';
41381
+ VersionInfo.version = '1.3.0-alpha.418';
41382
+ VersionInfo.date = '2022-10-11T00:58:31.859Z';
41172
41383
 
41173
41384
  var index$5 = /*#__PURE__*/Object.freeze({
41174
41385
  __proto__: null,