@coderline/alphatab 1.9.0-alpha.1768 → 1.9.0-alpha.1785
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -3
- package/dist/alphaTab.core.min.mjs +2 -2
- package/dist/alphaTab.core.mjs +454 -66
- package/dist/alphaTab.d.ts +35 -61
- package/dist/alphaTab.js +454 -66
- package/dist/alphaTab.min.js +2 -2
- package/dist/alphaTab.min.mjs +1 -1
- package/dist/alphaTab.mjs +1 -1
- package/dist/alphaTab.worker.min.mjs +1 -1
- package/dist/alphaTab.worker.mjs +1 -1
- package/dist/alphaTab.worklet.min.mjs +1 -1
- package/dist/alphaTab.worklet.mjs +1 -1
- package/package.json +9 -14
package/dist/alphaTab.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* alphaTab v1.9.0-alpha.
|
|
2
|
+
* alphaTab v1.9.0-alpha.1785 (develop, build 1785)
|
|
3
3
|
*
|
|
4
4
|
* Copyright © 2026, Daniel Kuschny and Contributors, All rights reserved.
|
|
5
5
|
*
|
|
@@ -209,9 +209,9 @@
|
|
|
209
209
|
* @internal
|
|
210
210
|
*/
|
|
211
211
|
class VersionInfo {
|
|
212
|
-
static version = '1.9.0-alpha.
|
|
213
|
-
static date = '2026-04-
|
|
214
|
-
static commit = '
|
|
212
|
+
static version = '1.9.0-alpha.1785';
|
|
213
|
+
static date = '2026-04-27T03:55:02.662Z';
|
|
214
|
+
static commit = '760ed909a3d8dc36b159d23b4ff6780e95a3daf1';
|
|
215
215
|
static print(print) {
|
|
216
216
|
print(`alphaTab ${VersionInfo.version}`);
|
|
217
217
|
print(`commit: ${VersionInfo.commit}`);
|
|
@@ -32634,9 +32634,16 @@
|
|
|
32634
32634
|
*
|
|
32635
32635
|
* The page layout does not use `displayWidth`. The use of absolute widths would break the proper alignments needed for this kind of display.
|
|
32636
32636
|
*
|
|
32637
|
-
*
|
|
32638
|
-
*
|
|
32639
|
-
*
|
|
32637
|
+
* In both modes, prefix and postfix glyphs (clef, key signature, time signature, barlines) are treated as fixed overhead: they keep their
|
|
32638
|
+
* natural size and the remaining staff width is distributed across bars by a per-bar weight. This matches the convention used by
|
|
32639
|
+
* Guitar Pro, Dorico, Finale, Sibelius and MuseScore. Bars that carry a system-start prefix or a mid-line clef/key/time-signature change
|
|
32640
|
+
* are therefore visibly wider than plain bars with the same weight. The weight source depends on the mode:
|
|
32641
|
+
*
|
|
32642
|
+
* * `Automatic` (default for `page` layout): weights come from the built-in spacing engine (the natural content width of each bar).
|
|
32643
|
+
* `displayScale` on the model is ignored.
|
|
32644
|
+
* * `UseModelLayout` (and the `parchment` layout): weights come from `bar.displayScale` / `masterBar.displayScale`. An unset
|
|
32645
|
+
* `displayScale` defaults to `1` and behaves identically to an explicit `1`, matching Guitar Pro (which omits the value when the
|
|
32646
|
+
* author hasn't customized it).
|
|
32640
32647
|
*
|
|
32641
32648
|
* ### Horizontal Layout
|
|
32642
32649
|
*
|
|
@@ -38835,14 +38842,25 @@
|
|
|
38835
38842
|
*/
|
|
38836
38843
|
boundsLookup;
|
|
38837
38844
|
/**
|
|
38838
|
-
*
|
|
38845
|
+
* Whether this system's bounds have already been scaled via `finish`. Prevents double-scaling
|
|
38846
|
+
* when the parent `BoundsLookup` is preserved across partial renders and `finish` is invoked
|
|
38847
|
+
* again on a mix of already-scaled (preserved) and newly-registered (natural-coordinate) systems.
|
|
38848
|
+
*/
|
|
38849
|
+
isFinished = false;
|
|
38850
|
+
/**
|
|
38851
|
+
* Finished the lookup for optimized access. Idempotent: once finished, further calls are no-ops
|
|
38852
|
+
* so preserved systems survive partial renders without being re-scaled.
|
|
38839
38853
|
*/
|
|
38840
38854
|
finish(scale = 1) {
|
|
38855
|
+
if (this.isFinished) {
|
|
38856
|
+
return;
|
|
38857
|
+
}
|
|
38841
38858
|
this.realBounds.scaleWith(scale);
|
|
38842
38859
|
this.visualBounds.scaleWith(scale);
|
|
38843
38860
|
for (const t of this.bars) {
|
|
38844
38861
|
t.finish(scale);
|
|
38845
38862
|
}
|
|
38863
|
+
this.isFinished = true;
|
|
38846
38864
|
}
|
|
38847
38865
|
/**
|
|
38848
38866
|
* Adds a new master bar to this lookup.
|
|
@@ -39015,6 +39033,58 @@
|
|
|
39015
39033
|
}
|
|
39016
39034
|
this.isFinished = true;
|
|
39017
39035
|
}
|
|
39036
|
+
/**
|
|
39037
|
+
* Re-opens the lookup for registrations without discarding previously registered bounds.
|
|
39038
|
+
* Used by the renderer when it preserves this lookup across a partial render so that new
|
|
39039
|
+
* bounds for the re-layouted range can be added while preserved systems stay intact.
|
|
39040
|
+
* @internal
|
|
39041
|
+
*/
|
|
39042
|
+
resetForPartialUpdate() {
|
|
39043
|
+
this.isFinished = false;
|
|
39044
|
+
}
|
|
39045
|
+
/**
|
|
39046
|
+
* Removes all entries belonging to the given master bar index and any bars after it.
|
|
39047
|
+
* Used before a partial render re-registers bounds for the re-layouted range, so the
|
|
39048
|
+
* preserved lookup ends up with only the unchanged entries when registration begins.
|
|
39049
|
+
*
|
|
39050
|
+
* Assumes the layout aligns its re-layouted range to system boundaries - i.e. the first
|
|
39051
|
+
* system to clear starts exactly at `masterBarIndex`. Caller is responsible for passing
|
|
39052
|
+
* the first master-bar-index of the first re-layouted system.
|
|
39053
|
+
* @internal
|
|
39054
|
+
*/
|
|
39055
|
+
clearFromMasterBar(masterBarIndex) {
|
|
39056
|
+
// drop staff systems whose bars start at or after the cleared range.
|
|
39057
|
+
let firstRemovedSystem = -1;
|
|
39058
|
+
for (let i = 0; i < this.staffSystems.length; i++) {
|
|
39059
|
+
const systemBars = this.staffSystems[i].bars;
|
|
39060
|
+
if (systemBars.length > 0 && systemBars[0].index >= masterBarIndex) {
|
|
39061
|
+
firstRemovedSystem = i;
|
|
39062
|
+
break;
|
|
39063
|
+
}
|
|
39064
|
+
}
|
|
39065
|
+
if (firstRemovedSystem !== -1) {
|
|
39066
|
+
this.staffSystems.splice(firstRemovedSystem, this.staffSystems.length - firstRemovedSystem);
|
|
39067
|
+
}
|
|
39068
|
+
// drop master bar entries at or beyond the cleared range.
|
|
39069
|
+
for (const key of Array.from(this._masterBarLookup.keys())) {
|
|
39070
|
+
if (key >= masterBarIndex) {
|
|
39071
|
+
this._masterBarLookup.delete(key);
|
|
39072
|
+
}
|
|
39073
|
+
}
|
|
39074
|
+
// drop beat entries whose beats belong to cleared bars.
|
|
39075
|
+
for (const key of Array.from(this._beatLookup.keys())) {
|
|
39076
|
+
const list = this._beatLookup.get(key);
|
|
39077
|
+
const filtered = list.filter(b => b.beat.voice.bar.index < masterBarIndex);
|
|
39078
|
+
if (filtered.length === 0) {
|
|
39079
|
+
this._beatLookup.delete(key);
|
|
39080
|
+
}
|
|
39081
|
+
else if (filtered.length !== list.length) {
|
|
39082
|
+
this._beatLookup.set(key, filtered);
|
|
39083
|
+
}
|
|
39084
|
+
}
|
|
39085
|
+
// drop the in-progress pointer - the next addStaffSystem call will replace it.
|
|
39086
|
+
this._currentStaffSystem = null;
|
|
39087
|
+
}
|
|
39018
39088
|
/**
|
|
39019
39089
|
* Adds a new staff sytem to the lookup.
|
|
39020
39090
|
* @param bounds The staff system bounds to add.
|
|
@@ -46846,7 +46916,15 @@
|
|
|
46846
46916
|
Logger.warning('Rendering', 'AlphaTab skipped rendering because of width=0 (element invisible)', null);
|
|
46847
46917
|
return;
|
|
46848
46918
|
}
|
|
46849
|
-
|
|
46919
|
+
// For partial renders we preserve the existing lookup so bars outside the re-layouted
|
|
46920
|
+
// range keep their already-scaled bounds - the layout will clear the changed range
|
|
46921
|
+
// before the paint pass re-registers fresh entries for it.
|
|
46922
|
+
if (renderHints?.firstChangedMasterBar !== undefined && this.boundsLookup) {
|
|
46923
|
+
this.boundsLookup.resetForPartialUpdate();
|
|
46924
|
+
}
|
|
46925
|
+
else {
|
|
46926
|
+
this.boundsLookup = new BoundsLookup();
|
|
46927
|
+
}
|
|
46850
46928
|
this._recreateCanvas();
|
|
46851
46929
|
this.canvas.lineWidth = 1;
|
|
46852
46930
|
this.canvas.settings = this.settings;
|
|
@@ -48720,9 +48798,6 @@
|
|
|
48720
48798
|
* @param beat The beat to add.
|
|
48721
48799
|
*/
|
|
48722
48800
|
highlightBeat(beat, playbackStart) {
|
|
48723
|
-
if (beat.isEmpty && !beat.voice.isEmpty) {
|
|
48724
|
-
return;
|
|
48725
|
-
}
|
|
48726
48801
|
if (!this._highlightedBeats.has(beat.id)) {
|
|
48727
48802
|
this._highlightedBeats.set(beat.id, true);
|
|
48728
48803
|
this.highlightedBeats.push(new BeatTickLookupItem(beat, playbackStart));
|
|
@@ -50190,7 +50265,13 @@
|
|
|
50190
50265
|
let beatStart = beat.playbackStart;
|
|
50191
50266
|
let audioDuration = beat.playbackDuration;
|
|
50192
50267
|
const masterBarDuration = beat.voice.bar.masterBar.calculateDuration();
|
|
50193
|
-
|
|
50268
|
+
// For a bar whose voice contains a single empty beat (the typical "whole-bar rest"
|
|
50269
|
+
// placeholder inserted during score.finish), extend the beat's audio duration to cover
|
|
50270
|
+
// the full bar so cursor navigation has a beat to follow across the whole bar. Don't
|
|
50271
|
+
// apply this when the voice has multiple beats: those represent explicit rhythmic
|
|
50272
|
+
// subdivisions even when each beat is empty (e.g. a recording grid of placeholder
|
|
50273
|
+
// slots), and overriding would make every beat overlap the whole bar.
|
|
50274
|
+
if (beat.voice.bar.isEmpty && beat.voice.beats.length === 1) {
|
|
50194
50275
|
audioDuration = masterBarDuration;
|
|
50195
50276
|
}
|
|
50196
50277
|
else if (beat.voice.bar.masterBar.tripletFeel !== TripletFeel.NoTripletFeel &&
|
|
@@ -54811,7 +54892,8 @@
|
|
|
54811
54892
|
this._isInitialBeatCursorUpdate ||
|
|
54812
54893
|
barBounds.y !== previousBeatBounds.barBounds.masterBarBounds.visualBounds.y ||
|
|
54813
54894
|
startBeatX < previousBeatBounds.onNotesX ||
|
|
54814
|
-
barBoundings.index > previousBeatBounds.barBounds.masterBarBounds.index + 1
|
|
54895
|
+
barBoundings.index > previousBeatBounds.barBounds.masterBarBounds.index + 1 ||
|
|
54896
|
+
barBounds.h !== previousBeatBounds.barBounds.masterBarBounds.visualBounds.h;
|
|
54815
54897
|
if (jumpCursor) {
|
|
54816
54898
|
cursorHandler.placeBeatCursor(beatCursor, beatBoundings, startBeatX);
|
|
54817
54899
|
}
|
|
@@ -58051,8 +58133,9 @@
|
|
|
58051
58133
|
break;
|
|
58052
58134
|
case 'alphaTab.renderScore':
|
|
58053
58135
|
this._updateFontSizes(data.fontSizes);
|
|
58136
|
+
const renderHints = data.renderHints;
|
|
58054
58137
|
const score = data.score == null ? null : JsonConverter.jsObjectToScore(data.score, this._renderer.settings);
|
|
58055
|
-
this._renderMultiple(score, data.trackIndexes);
|
|
58138
|
+
this._renderMultiple(score, data.trackIndexes, renderHints);
|
|
58056
58139
|
break;
|
|
58057
58140
|
case 'alphaTab.updateSettings':
|
|
58058
58141
|
this._updateSettings(data.settings);
|
|
@@ -62925,6 +63008,15 @@
|
|
|
62925
63008
|
}
|
|
62926
63009
|
return false;
|
|
62927
63010
|
}
|
|
63011
|
+
/**
|
|
63012
|
+
* The fixed-overhead width of this renderer: glyphs that do not stretch when
|
|
63013
|
+
* the bar is scaled (clef, key signature, time signature, barlines, courtesy
|
|
63014
|
+
* accidentals, etc). Treated as a fixed allocation by the system-level layout
|
|
63015
|
+
* before distributing remaining width across bars by {@link Bar.displayScale}.
|
|
63016
|
+
*/
|
|
63017
|
+
get fixedOverhead() {
|
|
63018
|
+
return this._preBeatGlyphs.width + this._postBeatGlyphs.width;
|
|
63019
|
+
}
|
|
62928
63020
|
scaleToWidth(width) {
|
|
62929
63021
|
// preBeat and postBeat glyphs do not get resized
|
|
62930
63022
|
const containerWidth = width - this._preBeatGlyphs.width - this._postBeatGlyphs.width;
|
|
@@ -65483,6 +65575,22 @@
|
|
|
65483
65575
|
postBeatSize = 0;
|
|
65484
65576
|
minStretchForce = 0;
|
|
65485
65577
|
totalSpringConstant = 0;
|
|
65578
|
+
/**
|
|
65579
|
+
* The smallest note duration encountered within this bar's springs, used as the reference in
|
|
65580
|
+
* the Gourlay stretch formula. Read by the owning {@link StaffSystem} so that the system can
|
|
65581
|
+
* aggregate a shared minimum across all bars and trigger a reconcile if an added bar introduces
|
|
65582
|
+
* a shorter duration than previously seen.
|
|
65583
|
+
*/
|
|
65584
|
+
get localMinDuration() {
|
|
65585
|
+
return this._minDuration;
|
|
65586
|
+
}
|
|
65587
|
+
/**
|
|
65588
|
+
* The minimum-duration reference against which the spring constants currently held by this info
|
|
65589
|
+
* were computed. Set by {@link finish} and {@link recomputeSpringConstants}. The owning
|
|
65590
|
+
* StaffSystem compares this against its system-wide minimum to decide whether spring constants
|
|
65591
|
+
* need re-derivation.
|
|
65592
|
+
*/
|
|
65593
|
+
computedWithMinDuration = 0;
|
|
65486
65594
|
_updateMinStretchForce(force) {
|
|
65487
65595
|
if (this.minStretchForce < force) {
|
|
65488
65596
|
this.minStretchForce = force;
|
|
@@ -65652,10 +65760,26 @@
|
|
|
65652
65760
|
this._incompleteGraceRodsWidth += sp.preBeatWidth + sp.postSpringWidth;
|
|
65653
65761
|
}
|
|
65654
65762
|
}
|
|
65655
|
-
this._calculateSpringConstants();
|
|
65763
|
+
this._calculateSpringConstants(this._minDuration);
|
|
65764
|
+
this.computedWithMinDuration = this._minDuration;
|
|
65656
65765
|
this.version++;
|
|
65657
65766
|
}
|
|
65658
|
-
|
|
65767
|
+
/**
|
|
65768
|
+
* Re-derives the spring constants (and {@link minStretchForce} / {@link totalSpringConstant})
|
|
65769
|
+
* using a caller-supplied minimum-duration reference rather than this bar's local minimum.
|
|
65770
|
+
*
|
|
65771
|
+
* Called by {@link StaffSystem.reconcileMinDurationIfDirty} when a bar added later to the
|
|
65772
|
+
* system introduced a shorter note than previously seen, invalidating this bar's spring
|
|
65773
|
+
* constants. Grace-rod data is not recomputed — it is independent of the minimum-duration
|
|
65774
|
+
* reference. The internal {@link version} is bumped so downstream consumers (e.g.
|
|
65775
|
+
* {@link BarRendererBase.applyLayoutingInfo}) pick up the refreshed positions.
|
|
65776
|
+
*/
|
|
65777
|
+
recomputeSpringConstants(minDuration) {
|
|
65778
|
+
this._calculateSpringConstants(minDuration);
|
|
65779
|
+
this.computedWithMinDuration = minDuration;
|
|
65780
|
+
this.version++;
|
|
65781
|
+
}
|
|
65782
|
+
_calculateSpringConstants(minDuration) {
|
|
65659
65783
|
let totalSpringConstant = 0;
|
|
65660
65784
|
const sortedSprings = this._timeSortedSprings;
|
|
65661
65785
|
if (sortedSprings.length === 0) {
|
|
@@ -65673,7 +65797,7 @@
|
|
|
65673
65797
|
const nextSpring = sortedSprings[i + 1];
|
|
65674
65798
|
duration = Math.abs(nextSpring.timePosition - currentSpring.timePosition);
|
|
65675
65799
|
}
|
|
65676
|
-
currentSpring.springConstant = this._calculateSpringConstant(currentSpring, duration);
|
|
65800
|
+
currentSpring.springConstant = this._calculateSpringConstant(currentSpring, duration, minDuration);
|
|
65677
65801
|
totalSpringConstant += 1 / currentSpring.springConstant;
|
|
65678
65802
|
}
|
|
65679
65803
|
this.totalSpringConstant = 1 / totalSpringConstant;
|
|
@@ -65733,7 +65857,7 @@
|
|
|
65733
65857
|
// springX += this.calculateWidth(force, spring.springConstant);
|
|
65734
65858
|
// }
|
|
65735
65859
|
// }
|
|
65736
|
-
_calculateSpringConstant(spring, duration) {
|
|
65860
|
+
_calculateSpringConstant(spring, duration, minDuration) {
|
|
65737
65861
|
if (duration <= 0) {
|
|
65738
65862
|
duration = MidiUtils.toTicks(Duration.TwoHundredFiftySixth);
|
|
65739
65863
|
}
|
|
@@ -65741,7 +65865,6 @@
|
|
|
65741
65865
|
spring.smallestDuration = duration;
|
|
65742
65866
|
}
|
|
65743
65867
|
const smallestDuration = spring.smallestDuration;
|
|
65744
|
-
const minDuration = this._minDuration;
|
|
65745
65868
|
const minDurationWidth = BarLayoutingInfo._defaultMinDurationWidth;
|
|
65746
65869
|
const phi = 1 + 0.85 * Math.log2(duration / minDuration);
|
|
65747
65870
|
return (smallestDuration / duration) * (1 / (phi * minDurationWidth));
|
|
@@ -65804,6 +65927,18 @@
|
|
|
65804
65927
|
canWrap = true;
|
|
65805
65928
|
masterBar;
|
|
65806
65929
|
additionalMultiBarRestIndexes = null;
|
|
65930
|
+
/**
|
|
65931
|
+
* Max fixed overhead (prefix + postfix glyph width) across all staves of this bar.
|
|
65932
|
+
* Used by the layout-mode horizontal scaling pass to carve out the fixed-overhead bucket
|
|
65933
|
+
* before distributing staff width across bars.
|
|
65934
|
+
*/
|
|
65935
|
+
maxFixedOverhead = 0;
|
|
65936
|
+
/**
|
|
65937
|
+
* Max natural content width (computedWidth - fixedOverhead) across all staves of this bar.
|
|
65938
|
+
* Used as the bar weight when the layout ignores {@link MasterBar.displayScale} (e.g.
|
|
65939
|
+
* Page layout with `SystemsLayoutMode.Automatic`).
|
|
65940
|
+
*/
|
|
65941
|
+
maxContentWidth = 0;
|
|
65807
65942
|
get lastMasterBarIndex() {
|
|
65808
65943
|
if (this.additionalMultiBarRestIndexes) {
|
|
65809
65944
|
return this.additionalMultiBarRestIndexes[this.additionalMultiBarRestIndexes.length - 1];
|
|
@@ -65985,6 +66120,45 @@
|
|
|
65985
66120
|
* This value is mainly used in the parchment style layout for correct scaling of the bars.
|
|
65986
66121
|
*/
|
|
65987
66122
|
totalBarDisplayScale = 0;
|
|
66123
|
+
/**
|
|
66124
|
+
* Sum of per-bar {@link MasterBarsRenderers.maxFixedOverhead} across the system. The layout-mode
|
|
66125
|
+
* horizontal scaling pass subtracts this from the available staff width before distributing the
|
|
66126
|
+
* remainder across bars.
|
|
66127
|
+
*/
|
|
66128
|
+
totalFixedOverhead = 0;
|
|
66129
|
+
/**
|
|
66130
|
+
* Sum of per-bar {@link MasterBarsRenderers.maxContentWidth} across the system. Used as the
|
|
66131
|
+
* denominator when distributing staff width in modes that weight bars by natural content width
|
|
66132
|
+
* (Page layout with `SystemsLayoutMode.Automatic`).
|
|
66133
|
+
*/
|
|
66134
|
+
totalContentWidth = 0;
|
|
66135
|
+
/**
|
|
66136
|
+
* Shortest note duration (in ticks) across every bar that has been added to this system, used
|
|
66137
|
+
* as the common reference in the Gourlay stretch formula so that rhythmically-equivalent beats
|
|
66138
|
+
* in different bars of the same system align column-wise.
|
|
66139
|
+
*
|
|
66140
|
+
* `-1` means "no bar added yet". The value only moves downward during system assembly; when a
|
|
66141
|
+
* new bar introduces a shorter minimum, {@link isMinDurationDirty} is set so that
|
|
66142
|
+
* {@link reconcileMinDurationIfDirty} can re-derive spring constants on the previously-added
|
|
66143
|
+
* bars before layout distribution runs.
|
|
66144
|
+
*/
|
|
66145
|
+
minDuration = -1;
|
|
66146
|
+
/**
|
|
66147
|
+
* Set when a bar added to this system introduced a shorter {@link minDuration} than previously
|
|
66148
|
+
* seen, leaving earlier bars' spring constants stale. Consumed by
|
|
66149
|
+
* {@link reconcileMinDurationIfDirty} which is called from `VerticalLayoutBase._fitSystem`
|
|
66150
|
+
* once the system is fully assembled.
|
|
66151
|
+
*/
|
|
66152
|
+
isMinDurationDirty = false;
|
|
66153
|
+
/**
|
|
66154
|
+
* Whether this system coordinates a shared minimum-duration reference across its bars for the
|
|
66155
|
+
* Gourlay stretch formula. Defaults to `true` for page-style and parchment layouts where bars
|
|
66156
|
+
* of a system fight for a common staff width. Set to `false` for horizontal layouts where each
|
|
66157
|
+
* bar is sized independently (by `bar.displayWidth` or its intrinsic width) and there is no
|
|
66158
|
+
* column-alignment concern - each bar keeps its local minimum so pre-existing rendering is
|
|
66159
|
+
* preserved.
|
|
66160
|
+
*/
|
|
66161
|
+
shareMinDurationAcrossBars = true;
|
|
65988
66162
|
isLast = false;
|
|
65989
66163
|
masterBarsRenderers = [];
|
|
65990
66164
|
staves = [];
|
|
@@ -66046,6 +66220,9 @@
|
|
|
66046
66220
|
}
|
|
66047
66221
|
this.firstVisibleStaff = firstVisibleStaff;
|
|
66048
66222
|
this._calculateAccoladeSpacing(tracks);
|
|
66223
|
+
// On the resize path the layoutingInfo was finalized in a previous layout pass, so we
|
|
66224
|
+
// only need to check whether its min-duration reference still matches the new system's.
|
|
66225
|
+
this._trackSystemMinDuration(renderers.layoutingInfo);
|
|
66049
66226
|
this._applyLayoutAndUpdateWidth();
|
|
66050
66227
|
return renderers;
|
|
66051
66228
|
}
|
|
@@ -66101,10 +66278,89 @@
|
|
|
66101
66278
|
this.firstVisibleStaff = firstVisibleStaff;
|
|
66102
66279
|
this._calculateAccoladeSpacing(tracks);
|
|
66103
66280
|
barLayoutingInfo.finish();
|
|
66281
|
+
// Reconcile against the system-wide minimum-duration reference now that springs are
|
|
66282
|
+
// finalized. If this bar introduced a shorter note, earlier bars become stale (flagged
|
|
66283
|
+
// for bulk reconcile at fit time). If the system already had a shorter min than this
|
|
66284
|
+
// bar's local one, this bar's spring constants are recomputed immediately so the width
|
|
66285
|
+
// we return below reflects the shared reference.
|
|
66286
|
+
this._trackSystemMinDuration(barLayoutingInfo);
|
|
66104
66287
|
// ensure same widths of new renderer
|
|
66105
66288
|
result.width = this._applyLayoutAndUpdateWidth();
|
|
66106
66289
|
return result;
|
|
66107
66290
|
}
|
|
66291
|
+
/**
|
|
66292
|
+
* Updates {@link minDuration} and {@link isMinDurationDirty} when a bar is added, and brings
|
|
66293
|
+
* the just-added bar's {@link BarLayoutingInfo} in line with the current system minimum if the
|
|
66294
|
+
* system already saw a shorter reference. The bulk reconcile over previously-added bars is
|
|
66295
|
+
* deferred to {@link reconcileMinDurationIfDirty} (called from `_fitSystem`) to avoid
|
|
66296
|
+
* re-iterating the system every time a bar is appended.
|
|
66297
|
+
*/
|
|
66298
|
+
_trackSystemMinDuration(info) {
|
|
66299
|
+
if (!this.shareMinDurationAcrossBars) {
|
|
66300
|
+
return;
|
|
66301
|
+
}
|
|
66302
|
+
const localMin = info.localMinDuration;
|
|
66303
|
+
if (this.minDuration === -1 || localMin < this.minDuration) {
|
|
66304
|
+
// this bar shortens the system minimum; earlier bars (if any) are now stale
|
|
66305
|
+
if (this.masterBarsRenderers.length > 1 && localMin !== this.minDuration) {
|
|
66306
|
+
this.isMinDurationDirty = true;
|
|
66307
|
+
}
|
|
66308
|
+
this.minDuration = localMin;
|
|
66309
|
+
}
|
|
66310
|
+
if (info.computedWithMinDuration > this.minDuration) {
|
|
66311
|
+
// this bar was initialized against a larger (local) min than the system carries; pull
|
|
66312
|
+
// it down to the system reference so its computedWidth reflects the shared spacing.
|
|
66313
|
+
info.recomputeSpringConstants(this.minDuration);
|
|
66314
|
+
}
|
|
66315
|
+
}
|
|
66316
|
+
/**
|
|
66317
|
+
* Re-derives spring constants on bars whose {@link BarLayoutingInfo.computedWithMinDuration}
|
|
66318
|
+
* is out of sync with the current {@link minDuration}, and rebuilds the cached system totals
|
|
66319
|
+
* (widths, {@link totalFixedOverhead}, {@link totalContentWidth}) from the refreshed bar
|
|
66320
|
+
* widths. Called from `VerticalLayoutBase._fitSystem` after the system is fully assembled and
|
|
66321
|
+
* before distribution runs. No-op when {@link isMinDurationDirty} is false.
|
|
66322
|
+
*/
|
|
66323
|
+
reconcileMinDurationIfDirty() {
|
|
66324
|
+
if (!this.isMinDurationDirty) {
|
|
66325
|
+
return;
|
|
66326
|
+
}
|
|
66327
|
+
let systemWidth = this.accoladeWidth;
|
|
66328
|
+
let totalFixedOverhead = 0;
|
|
66329
|
+
let totalContentWidth = 0;
|
|
66330
|
+
for (const mb of this.masterBarsRenderers) {
|
|
66331
|
+
if (mb.layoutingInfo.computedWithMinDuration > this.minDuration) {
|
|
66332
|
+
mb.layoutingInfo.recomputeSpringConstants(this.minDuration);
|
|
66333
|
+
}
|
|
66334
|
+
let maxPrefix = 0;
|
|
66335
|
+
let maxContent = 0;
|
|
66336
|
+
let realWidth = 0;
|
|
66337
|
+
for (const r of mb.renderers) {
|
|
66338
|
+
r.applyLayoutingInfo();
|
|
66339
|
+
if (r.computedWidth > realWidth) {
|
|
66340
|
+
realWidth = r.computedWidth;
|
|
66341
|
+
}
|
|
66342
|
+
const overhead = r.fixedOverhead;
|
|
66343
|
+
if (overhead > maxPrefix) {
|
|
66344
|
+
maxPrefix = overhead;
|
|
66345
|
+
}
|
|
66346
|
+
const content = Math.max(0, r.computedWidth - overhead);
|
|
66347
|
+
if (content > maxContent) {
|
|
66348
|
+
maxContent = content;
|
|
66349
|
+
}
|
|
66350
|
+
}
|
|
66351
|
+
mb.maxFixedOverhead = maxPrefix;
|
|
66352
|
+
mb.maxContentWidth = maxContent;
|
|
66353
|
+
mb.width = realWidth;
|
|
66354
|
+
systemWidth += realWidth;
|
|
66355
|
+
totalFixedOverhead += maxPrefix;
|
|
66356
|
+
totalContentWidth += maxContent;
|
|
66357
|
+
}
|
|
66358
|
+
this.width = systemWidth;
|
|
66359
|
+
this.computedWidth = systemWidth;
|
|
66360
|
+
this.totalFixedOverhead = totalFixedOverhead;
|
|
66361
|
+
this.totalContentWidth = totalContentWidth;
|
|
66362
|
+
this.isMinDurationDirty = false;
|
|
66363
|
+
}
|
|
66108
66364
|
getBarDisplayScale(renderer) {
|
|
66109
66365
|
return this.staves.length > 1 ? renderer.bar.masterBar.displayScale : renderer.bar.displayScale;
|
|
66110
66366
|
}
|
|
@@ -66143,12 +66399,16 @@
|
|
|
66143
66399
|
this.width -= width;
|
|
66144
66400
|
this.computedWidth -= width;
|
|
66145
66401
|
this.totalBarDisplayScale -= barDisplayScale;
|
|
66402
|
+
this.totalFixedOverhead -= toRemove.maxFixedOverhead;
|
|
66403
|
+
this.totalContentWidth -= toRemove.maxContentWidth;
|
|
66146
66404
|
return toRemove;
|
|
66147
66405
|
}
|
|
66148
66406
|
return null;
|
|
66149
66407
|
}
|
|
66150
66408
|
_applyLayoutAndUpdateWidth() {
|
|
66151
66409
|
let realWidth = 0;
|
|
66410
|
+
let maxFixedOverhead = 0;
|
|
66411
|
+
let maxContentWidth = 0;
|
|
66152
66412
|
let barDisplayScale = 0;
|
|
66153
66413
|
for (const s of this.allStaves) {
|
|
66154
66414
|
const last = s.barRenderers[s.barRenderers.length - 1];
|
|
@@ -66157,8 +66417,21 @@
|
|
|
66157
66417
|
if (last.computedWidth > realWidth) {
|
|
66158
66418
|
realWidth = last.computedWidth;
|
|
66159
66419
|
}
|
|
66420
|
+
const overhead = last.fixedOverhead;
|
|
66421
|
+
if (overhead > maxFixedOverhead) {
|
|
66422
|
+
maxFixedOverhead = overhead;
|
|
66423
|
+
}
|
|
66424
|
+
const content = Math.max(0, last.computedWidth - overhead);
|
|
66425
|
+
if (content > maxContentWidth) {
|
|
66426
|
+
maxContentWidth = content;
|
|
66427
|
+
}
|
|
66160
66428
|
}
|
|
66429
|
+
const renderers = this.masterBarsRenderers[this.masterBarsRenderers.length - 1];
|
|
66430
|
+
renderers.maxFixedOverhead = maxFixedOverhead;
|
|
66431
|
+
renderers.maxContentWidth = maxContentWidth;
|
|
66161
66432
|
this.totalBarDisplayScale += barDisplayScale;
|
|
66433
|
+
this.totalFixedOverhead += maxFixedOverhead;
|
|
66434
|
+
this.totalContentWidth += maxContentWidth;
|
|
66162
66435
|
this.width += realWidth;
|
|
66163
66436
|
this.computedWidth += realWidth;
|
|
66164
66437
|
return realWidth;
|
|
@@ -66656,17 +66929,6 @@
|
|
|
66656
66929
|
}
|
|
66657
66930
|
}
|
|
66658
66931
|
|
|
66659
|
-
/**
|
|
66660
|
-
* @internal
|
|
66661
|
-
*/
|
|
66662
|
-
class LazyPartial {
|
|
66663
|
-
args;
|
|
66664
|
-
renderCallback;
|
|
66665
|
-
constructor(args, renderCallback) {
|
|
66666
|
-
this.args = args;
|
|
66667
|
-
this.renderCallback = renderCallback;
|
|
66668
|
-
}
|
|
66669
|
-
}
|
|
66670
66932
|
/**
|
|
66671
66933
|
* This is the base class for creating new layouting engines for the score renderer.
|
|
66672
66934
|
* @internal
|
|
@@ -66697,15 +66959,21 @@
|
|
|
66697
66959
|
this.doResize();
|
|
66698
66960
|
}
|
|
66699
66961
|
layoutAndRender(renderHints) {
|
|
66700
|
-
this._lazyPartials.clear();
|
|
66701
66962
|
this.slurRegistry.clear();
|
|
66702
|
-
this.beamingRuleLookups.clear();
|
|
66703
|
-
this._barRendererLookup.clear();
|
|
66704
|
-
this.profile = Environment.staveProfiles.get(this.renderer.settings.display.staveProfile);
|
|
66705
66963
|
const score = this.renderer.score;
|
|
66706
66964
|
this.firstBarIndex = ModelUtils.computeFirstDisplayedBarIndex(score, this.renderer.settings);
|
|
66707
66965
|
this.lastBarIndex = ModelUtils.computeLastDisplayedBarIndex(score, this.renderer.settings, this.firstBarIndex);
|
|
66708
66966
|
this.multiBarRestInfo = ModelUtils.buildMultiBarRestInfo(this.renderer.tracks, this.firstBarIndex, this.lastBarIndex);
|
|
66967
|
+
const firstChangedMasterBar = renderHints?.firstChangedMasterBar;
|
|
66968
|
+
if (firstChangedMasterBar !== undefined) {
|
|
66969
|
+
if (this.doUpdateForBars(renderHints)) {
|
|
66970
|
+
return;
|
|
66971
|
+
}
|
|
66972
|
+
}
|
|
66973
|
+
this._lazyPartials.clear();
|
|
66974
|
+
this.beamingRuleLookups.clear();
|
|
66975
|
+
this._barRendererLookup.clear();
|
|
66976
|
+
this.profile = Environment.staveProfiles.get(this.renderer.settings.display.staveProfile);
|
|
66709
66977
|
this.pagePadding = this.renderer.settings.display.padding.map(p => p / this.renderer.settings.display.scale);
|
|
66710
66978
|
if (!this.pagePadding) {
|
|
66711
66979
|
this.pagePadding = [0, 0, 0, 0];
|
|
@@ -66720,6 +66988,9 @@
|
|
|
66720
66988
|
this.doLayoutAndRender(renderHints);
|
|
66721
66989
|
}
|
|
66722
66990
|
_lazyPartials = new Map();
|
|
66991
|
+
getExistingPartialArgs(id) {
|
|
66992
|
+
return this._lazyPartials.has(id) ? this._lazyPartials.get(id).args : undefined;
|
|
66993
|
+
}
|
|
66723
66994
|
registerPartial(args, callback) {
|
|
66724
66995
|
if (args.height === 0) {
|
|
66725
66996
|
return;
|
|
@@ -66738,7 +67009,11 @@
|
|
|
66738
67009
|
}
|
|
66739
67010
|
else {
|
|
66740
67011
|
// in case of lazy loading -> first register lazy, then notify
|
|
66741
|
-
|
|
67012
|
+
const partial = {
|
|
67013
|
+
args,
|
|
67014
|
+
renderCallback: callback
|
|
67015
|
+
};
|
|
67016
|
+
this._lazyPartials.set(args.id, partial);
|
|
66742
67017
|
this.renderer.partialLayoutFinished.trigger(args);
|
|
66743
67018
|
}
|
|
66744
67019
|
}
|
|
@@ -67033,7 +67308,7 @@
|
|
|
67033
67308
|
glyph.textAlign = TextAlign.Left;
|
|
67034
67309
|
}
|
|
67035
67310
|
}
|
|
67036
|
-
|
|
67311
|
+
_layoutAndRenderAnnotation(y) {
|
|
67037
67312
|
// attention, you are not allowed to remove change this notice within any version of this library without permission!
|
|
67038
67313
|
const msg = 'rendered by alphaTab';
|
|
67039
67314
|
const resources = this.renderer.settings.display.resources;
|
|
@@ -67097,6 +67372,12 @@
|
|
|
67097
67372
|
}
|
|
67098
67373
|
doResize() {
|
|
67099
67374
|
}
|
|
67375
|
+
doUpdateForBars(_renderHints) {
|
|
67376
|
+
// not supported yet, modifications likely cause anyhow full updates
|
|
67377
|
+
// as we do not optimize effect bands yet. with effect bands being more
|
|
67378
|
+
// isolated in bars we could try updating dynamically
|
|
67379
|
+
return false;
|
|
67380
|
+
}
|
|
67100
67381
|
doLayoutAndRender(renderHints) {
|
|
67101
67382
|
const score = this.renderer.score;
|
|
67102
67383
|
let startIndex = this.renderer.settings.display.startBar;
|
|
@@ -67110,6 +67391,11 @@
|
|
|
67110
67391
|
endBarIndex = startIndex + endBarIndex - 1; // map count to array index
|
|
67111
67392
|
endBarIndex = Math.min(score.masterBars.length - 1, Math.max(0, endBarIndex));
|
|
67112
67393
|
this._system = this.createEmptyStaffSystem(0);
|
|
67394
|
+
// Each bar in horizontal layout is sized independently (by bar.displayWidth or the bar's
|
|
67395
|
+
// intrinsic width), so there is no shared staff width to distribute across bars. Keep each
|
|
67396
|
+
// bar's spring constants referenced against its own local minimum-duration so rendering
|
|
67397
|
+
// matches the historical per-bar behaviour.
|
|
67398
|
+
this._system.shareMinDurationAcrossBars = false;
|
|
67113
67399
|
this._system.isLast = true;
|
|
67114
67400
|
this._system.x = this.pagePadding[0];
|
|
67115
67401
|
this._system.y = this.pagePadding[1];
|
|
@@ -67171,7 +67457,7 @@
|
|
|
67171
67457
|
currentBarIndex += partial.masterBars.length;
|
|
67172
67458
|
}
|
|
67173
67459
|
this.height = this.layoutAndRenderBottomScoreInfo(this.height);
|
|
67174
|
-
this.height = this.
|
|
67460
|
+
this.height = this._layoutAndRenderAnnotation(this.height);
|
|
67175
67461
|
this.height += this.pagePadding[3];
|
|
67176
67462
|
this.height *= this.renderer.settings.display.scale;
|
|
67177
67463
|
}
|
|
@@ -67238,11 +67524,16 @@
|
|
|
67238
67524
|
_allMasterBarRenderers = [];
|
|
67239
67525
|
_barsFromPreviousSystem = [];
|
|
67240
67526
|
_reuseViewPort = false;
|
|
67527
|
+
_preSystemPartialIds = [];
|
|
67528
|
+
_systemPartialIds = [];
|
|
67241
67529
|
doLayoutAndRender(renderHints) {
|
|
67242
67530
|
let y = this.pagePadding[1];
|
|
67243
67531
|
this.width = this.renderer.width;
|
|
67244
67532
|
this._allMasterBarRenderers = [];
|
|
67533
|
+
this._preSystemPartialIds = [];
|
|
67534
|
+
this._systemPartialIds = [];
|
|
67245
67535
|
this._reuseViewPort = renderHints?.reuseViewport ?? false;
|
|
67536
|
+
this._systems = [];
|
|
67246
67537
|
//
|
|
67247
67538
|
// 1. Score Info
|
|
67248
67539
|
y = this._layoutAndRenderScoreInfo(y, -1);
|
|
@@ -67254,15 +67545,23 @@
|
|
|
67254
67545
|
y = this._layoutAndRenderChordDiagrams(y, -1);
|
|
67255
67546
|
//
|
|
67256
67547
|
// 4. One result per StaffSystem
|
|
67257
|
-
y = this._layoutAndRenderScore(y);
|
|
67548
|
+
y = this._layoutAndRenderScore(y, this.firstBarIndex);
|
|
67258
67549
|
y = this.layoutAndRenderBottomScoreInfo(y);
|
|
67259
|
-
y = this.
|
|
67550
|
+
y = this._layoutAndRenderAnnotation(y);
|
|
67260
67551
|
this.height = (y + this.pagePadding[3]) * this.renderer.settings.display.scale;
|
|
67261
67552
|
}
|
|
67262
67553
|
registerPartial(args, callback) {
|
|
67263
67554
|
args.reuseViewport = this._reuseViewPort;
|
|
67264
67555
|
super.registerPartial(args, callback);
|
|
67265
67556
|
}
|
|
67557
|
+
reregisterPartial(id) {
|
|
67558
|
+
const args = this.getExistingPartialArgs(id);
|
|
67559
|
+
if (!args) {
|
|
67560
|
+
return;
|
|
67561
|
+
}
|
|
67562
|
+
args.reuseViewport = this._reuseViewPort;
|
|
67563
|
+
this.renderer.partialLayoutFinished.trigger(args);
|
|
67564
|
+
}
|
|
67266
67565
|
get supportsResize() {
|
|
67267
67566
|
return true;
|
|
67268
67567
|
}
|
|
@@ -67273,6 +67572,47 @@
|
|
|
67273
67572
|
}
|
|
67274
67573
|
return x;
|
|
67275
67574
|
}
|
|
67575
|
+
doUpdateForBars(renderHints) {
|
|
67576
|
+
this._reuseViewPort = renderHints.reuseViewport ?? false;
|
|
67577
|
+
const firstModifiedMasterBar = renderHints.firstChangedMasterBar;
|
|
67578
|
+
// first update existing systems as needed
|
|
67579
|
+
const systemIndex = this._systems.findIndex(s => {
|
|
67580
|
+
const first = s.masterBarsRenderers[0].masterBar.index;
|
|
67581
|
+
const last = s.masterBarsRenderers[s.masterBarsRenderers.length - 1].masterBar.index;
|
|
67582
|
+
return first <= firstModifiedMasterBar && firstModifiedMasterBar <= last;
|
|
67583
|
+
});
|
|
67584
|
+
if (systemIndex === -1 || !this.renderer.settings.core.enableLazyLoading) {
|
|
67585
|
+
return false;
|
|
67586
|
+
}
|
|
67587
|
+
// Bars from the start of the re-layouted system onward will be re-registered during the
|
|
67588
|
+
// paint pass. Clear their old entries from the preserved BoundsLookup so registration
|
|
67589
|
+
// produces a clean, complete lookup after this render finishes.
|
|
67590
|
+
const firstRebuiltBarIndex = this._systems[systemIndex].masterBarsRenderers[0].masterBar.index;
|
|
67591
|
+
this.renderer.boundsLookup.clearFromMasterBar(firstRebuiltBarIndex);
|
|
67592
|
+
// for now we do a full relayout from the first modified masterbar
|
|
67593
|
+
// there is a lot of room for even more performant updates, but they come
|
|
67594
|
+
// at a risk that features break.
|
|
67595
|
+
// e.g. we could only shift systems where the content didn't change,
|
|
67596
|
+
// but we might still have ties/slurs which have to be updated.
|
|
67597
|
+
const removeSystems = this._systems.splice(systemIndex, this._systems.length - systemIndex);
|
|
67598
|
+
this._systemPartialIds.splice(systemIndex, this._systemPartialIds.length - systemIndex);
|
|
67599
|
+
const system = removeSystems[0];
|
|
67600
|
+
let y = system.y;
|
|
67601
|
+
const firstBarIndex = system.masterBarsRenderers[0].masterBar.index;
|
|
67602
|
+
// signal all partials which didn't change
|
|
67603
|
+
for (const preSystemPartial of this._preSystemPartialIds) {
|
|
67604
|
+
this.reregisterPartial(preSystemPartial);
|
|
67605
|
+
}
|
|
67606
|
+
for (let i = 0; i < systemIndex; i++) {
|
|
67607
|
+
this.reregisterPartial(this._systemPartialIds[i]);
|
|
67608
|
+
}
|
|
67609
|
+
// new partials for all other prats
|
|
67610
|
+
y = this._layoutAndRenderScore(y, firstBarIndex);
|
|
67611
|
+
y = this.layoutAndRenderBottomScoreInfo(y);
|
|
67612
|
+
y = this._layoutAndRenderAnnotation(y);
|
|
67613
|
+
this.height = (y + this.pagePadding[3]) * this.renderer.settings.display.scale;
|
|
67614
|
+
return true;
|
|
67615
|
+
}
|
|
67276
67616
|
doResize() {
|
|
67277
67617
|
let y = this.pagePadding[1];
|
|
67278
67618
|
this.width = this.renderer.width;
|
|
@@ -67291,7 +67631,7 @@
|
|
|
67291
67631
|
// 4. One result per StaffSystem
|
|
67292
67632
|
y = this._resizeAndRenderScore(y, oldHeight);
|
|
67293
67633
|
y = this.layoutAndRenderBottomScoreInfo(y);
|
|
67294
|
-
y = this.
|
|
67634
|
+
y = this._layoutAndRenderAnnotation(y);
|
|
67295
67635
|
this.height = (y + this.pagePadding[3]) * this.renderer.settings.display.scale;
|
|
67296
67636
|
}
|
|
67297
67637
|
_layoutAndRenderTunings(y, totalHeight = -1) {
|
|
@@ -67315,6 +67655,7 @@
|
|
|
67315
67655
|
canvas.textAlign = TextAlign.Center;
|
|
67316
67656
|
this.tuningGlyph.paint(0, 0, canvas);
|
|
67317
67657
|
});
|
|
67658
|
+
this._preSystemPartialIds.push(e.id);
|
|
67318
67659
|
return y + tuningHeight;
|
|
67319
67660
|
}
|
|
67320
67661
|
_layoutAndRenderChordDiagrams(y, totalHeight = -1) {
|
|
@@ -67338,6 +67679,7 @@
|
|
|
67338
67679
|
canvas.textAlign = TextAlign.Center;
|
|
67339
67680
|
this.chordDiagrams.paint(0, 0, canvas);
|
|
67340
67681
|
});
|
|
67682
|
+
this._preSystemPartialIds.push(e.id);
|
|
67341
67683
|
return y + diagramHeight;
|
|
67342
67684
|
}
|
|
67343
67685
|
_layoutAndRenderScoreInfo(y, totalHeight = -1) {
|
|
@@ -67380,12 +67722,14 @@
|
|
|
67380
67722
|
g.paint(0, 0, canvas);
|
|
67381
67723
|
}
|
|
67382
67724
|
});
|
|
67725
|
+
this._preSystemPartialIds.push(e.id);
|
|
67383
67726
|
}
|
|
67384
67727
|
return y + infoHeight;
|
|
67385
67728
|
}
|
|
67386
67729
|
_resizeAndRenderScore(y, oldHeight) {
|
|
67387
67730
|
// if we have a fixed number of bars per row, we only need to refit them.
|
|
67388
67731
|
const barsPerRowActive = this.getBarsPerSystem(0) > 0;
|
|
67732
|
+
this._systemPartialIds = [];
|
|
67389
67733
|
if (barsPerRowActive) {
|
|
67390
67734
|
for (let i = 0; i < this._systems.length; i++) {
|
|
67391
67735
|
const system = this._systems[i];
|
|
@@ -67448,11 +67792,9 @@
|
|
|
67448
67792
|
}
|
|
67449
67793
|
return y;
|
|
67450
67794
|
}
|
|
67451
|
-
_layoutAndRenderScore(y) {
|
|
67452
|
-
const startIndex = this.firstBarIndex;
|
|
67795
|
+
_layoutAndRenderScore(y, startIndex) {
|
|
67453
67796
|
let currentBarIndex = startIndex;
|
|
67454
67797
|
const endBarIndex = this.lastBarIndex;
|
|
67455
|
-
this._systems = [];
|
|
67456
67798
|
while (currentBarIndex <= endBarIndex) {
|
|
67457
67799
|
// create system and align set proper coordinates
|
|
67458
67800
|
const system = this._createStaffSystem(currentBarIndex, endBarIndex);
|
|
@@ -67487,6 +67829,7 @@
|
|
|
67487
67829
|
// since we use partial drawing
|
|
67488
67830
|
system.paint(0, -(args.y / this.renderer.settings.display.scale), canvas);
|
|
67489
67831
|
});
|
|
67832
|
+
this._systemPartialIds.push(args.id);
|
|
67490
67833
|
// calculate coordinates for next system
|
|
67491
67834
|
return height;
|
|
67492
67835
|
}
|
|
@@ -67494,6 +67837,10 @@
|
|
|
67494
67837
|
* Realignes the bars in this line according to the available space
|
|
67495
67838
|
*/
|
|
67496
67839
|
_fitSystem(system) {
|
|
67840
|
+
// If a bar added late in the assembly introduced a shorter note than earlier bars, the
|
|
67841
|
+
// earlier bars' spring constants (and the cached system widths / totals) are stale.
|
|
67842
|
+
// Reconcile now - it's a no-op when nothing changed.
|
|
67843
|
+
system.reconcileMinDurationIfDirty();
|
|
67497
67844
|
if (system.isFull || system.width > this._maxWidth || this.renderer.settings.display.justifyLastSystem) {
|
|
67498
67845
|
this._scaleToWidth(system, this._maxWidth);
|
|
67499
67846
|
}
|
|
@@ -67505,29 +67852,35 @@
|
|
|
67505
67852
|
_scaleToWidth(system, width) {
|
|
67506
67853
|
const staffWidth = width - system.accoladeWidth;
|
|
67507
67854
|
const shouldApplyBarScale = this.shouldApplyBarScale;
|
|
67508
|
-
|
|
67509
|
-
//
|
|
67510
|
-
//
|
|
67511
|
-
//
|
|
67512
|
-
//
|
|
67513
|
-
//
|
|
67514
|
-
|
|
67515
|
-
|
|
67855
|
+
// Industry fixed-overhead model (Behind Bars, Dorico, Finale, Sibelius, MuseScore, Guitar Pro):
|
|
67856
|
+
// prefix/postfix glyphs (clef, key sig, time sig, barlines) are treated as fixed overhead and the
|
|
67857
|
+
// remaining staff width is distributed across bars by a per-bar weight.
|
|
67858
|
+
//
|
|
67859
|
+
// distributable = staffWidth - totalFixedOverhead
|
|
67860
|
+
// contentShare = distributable / sum(weight)
|
|
67861
|
+
// bar.width = bar.maxFixedOverhead + weight * contentShare
|
|
67862
|
+
//
|
|
67863
|
+
// The weight depends on the layout mode:
|
|
67864
|
+
// - shouldApplyBarScale=true -> weight = bar.displayScale (model-driven, matches Guitar Pro)
|
|
67865
|
+
// displayScale defaults to 1, so an unset value behaves identically
|
|
67866
|
+
// to an explicit 1 (GP omits the property when not customized).
|
|
67867
|
+
// - shouldApplyBarScale=false -> weight = natural content width (automatic, ignores displayScale)
|
|
67868
|
+
//
|
|
67869
|
+
// Per-bar maxFixedOverhead / maxContentWidth and the system-wide totals are maintained incrementally
|
|
67870
|
+
// in StaffSystem._applyLayoutAndUpdateWidth / revertLastBar so this pass can apply directly.
|
|
67871
|
+
const weightTotal = shouldApplyBarScale ? system.totalBarDisplayScale : system.totalContentWidth;
|
|
67872
|
+
const distributable = Math.max(0, staffWidth - system.totalFixedOverhead);
|
|
67873
|
+
const contentShare = weightTotal > 0 ? distributable / weightTotal : 0;
|
|
67516
67874
|
for (const s of system.allStaves) {
|
|
67517
67875
|
s.resetSharedLayoutData();
|
|
67518
|
-
// scale the bars by keeping their respective ratio size
|
|
67519
67876
|
let w = 0;
|
|
67520
|
-
for (
|
|
67877
|
+
for (let i = 0; i < s.barRenderers.length; i++) {
|
|
67878
|
+
const renderer = s.barRenderers[i];
|
|
67879
|
+
const mb = system.masterBarsRenderers[i];
|
|
67521
67880
|
renderer.x = w;
|
|
67522
67881
|
renderer.y = s.topPadding + s.topOverflow;
|
|
67523
|
-
|
|
67524
|
-
|
|
67525
|
-
const barDisplayScale = system.getBarDisplayScale(renderer);
|
|
67526
|
-
actualBarWidth = (barDisplayScale * staffWidth) / totalScale;
|
|
67527
|
-
}
|
|
67528
|
-
else {
|
|
67529
|
-
actualBarWidth = renderer.computedWidth + spacePerBar;
|
|
67530
|
-
}
|
|
67882
|
+
const weight = shouldApplyBarScale ? system.getBarDisplayScale(renderer) : mb.maxContentWidth;
|
|
67883
|
+
const actualBarWidth = mb.maxFixedOverhead + weight * contentShare;
|
|
67531
67884
|
renderer.scaleToWidth(actualBarWidth);
|
|
67532
67885
|
w += renderer.width;
|
|
67533
67886
|
}
|
|
@@ -69351,12 +69704,24 @@
|
|
|
69351
69704
|
const firstNonRestBeamingHelper = this.helpers.getBeamingHelperForBeat(firstNonRestBeat);
|
|
69352
69705
|
const lastNonRestBeamingHelper = this.helpers.getBeamingHelperForBeat(lastNonRestBeat);
|
|
69353
69706
|
const direction = this.getTupletBeamDirection(firstNonRestBeamingHelper);
|
|
69354
|
-
let startY
|
|
69355
|
-
let endY
|
|
69707
|
+
let startY;
|
|
69708
|
+
let endY;
|
|
69356
69709
|
if (isRestOnly) {
|
|
69357
|
-
|
|
69710
|
+
// rests have no stems, so anchor to the actual rest glyph bounds
|
|
69711
|
+
// instead of a stem-adjusted flag position (which would place the bracket
|
|
69712
|
+
// a full quarter-stem length away from the rests).
|
|
69713
|
+
if (direction === BeamDirection.Up) {
|
|
69714
|
+
startY = Math.min(this.getRestY(firstNonRestBeat, NoteYPosition.Top), this.getRestY(lastNonRestBeat, NoteYPosition.Top));
|
|
69715
|
+
}
|
|
69716
|
+
else {
|
|
69717
|
+
startY = Math.max(this.getRestY(firstNonRestBeat, NoteYPosition.Bottom), this.getRestY(lastNonRestBeat, NoteYPosition.Bottom));
|
|
69718
|
+
}
|
|
69358
69719
|
endY = startY;
|
|
69359
69720
|
}
|
|
69721
|
+
else {
|
|
69722
|
+
startY = this.calculateBeamYWithDirection(firstNonRestBeamingHelper, startX, direction);
|
|
69723
|
+
endY = this.calculateBeamYWithDirection(lastNonRestBeamingHelper, endX, direction);
|
|
69724
|
+
}
|
|
69360
69725
|
// align line centered in available space
|
|
69361
69726
|
if (direction === BeamDirection.Down) {
|
|
69362
69727
|
startY += shift;
|
|
@@ -69726,7 +70091,30 @@
|
|
|
69726
70091
|
let minNoteY = 0;
|
|
69727
70092
|
for (const v of this.helpers.beamHelpers) {
|
|
69728
70093
|
for (const h of v) {
|
|
69729
|
-
if (!this.shouldPaintBeamingHelper(h))
|
|
70094
|
+
if (!this.shouldPaintBeamingHelper(h)) {
|
|
70095
|
+
// beam is not drawn, but a rest-only tuplet still draws a bracket
|
|
70096
|
+
// anchored to the rest glyph bounds and needs overflow reserved.
|
|
70097
|
+
if (h.hasTuplet && h.isRestBeamHelper) {
|
|
70098
|
+
const tupletGroup = h.beats[0].tupletGroup;
|
|
70099
|
+
const tupletFirst = tupletGroup.beats[0];
|
|
70100
|
+
const tupletLast = tupletGroup.beats[tupletGroup.beats.length - 1];
|
|
70101
|
+
const tupletDirection = this.getTupletBeamDirection(h);
|
|
70102
|
+
if (tupletDirection === BeamDirection.Up) {
|
|
70103
|
+
const restTop = Math.min(this.getRestY(tupletFirst, NoteYPosition.Top), this.getRestY(tupletLast, NoteYPosition.Top));
|
|
70104
|
+
const topY = restTop - this.tupletSize - this.tupletOffset;
|
|
70105
|
+
if (topY < maxNoteY) {
|
|
70106
|
+
maxNoteY = topY;
|
|
70107
|
+
}
|
|
70108
|
+
}
|
|
70109
|
+
else {
|
|
70110
|
+
const restBottom = Math.max(this.getRestY(tupletFirst, NoteYPosition.Bottom), this.getRestY(tupletLast, NoteYPosition.Bottom));
|
|
70111
|
+
const bottomY = restBottom + this.tupletSize + this.tupletOffset;
|
|
70112
|
+
if (bottomY > minNoteY) {
|
|
70113
|
+
minNoteY = bottomY;
|
|
70114
|
+
}
|
|
70115
|
+
}
|
|
70116
|
+
}
|
|
70117
|
+
}
|
|
69730
70118
|
else if (h.beats.length === 1 && h.beats[0].duration >= Duration.Half) {
|
|
69731
70119
|
const tupletDirection = this.getTupletBeamDirection(h);
|
|
69732
70120
|
const direction = this.getBeamDirection(h);
|