Skip to content

Commit

Permalink
Manually align the first note of all staves
Browse files Browse the repository at this point in the history
  • Loading branch information
jaredjj3 committed Jan 13, 2025
1 parent dc1eb3e commit db6b28c
Show file tree
Hide file tree
Showing 11 changed files with 24 additions and 9 deletions.
2 changes: 1 addition & 1 deletion src/rendering/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ export const CONFIG = {
help: 'SHOW_TAB_TIME_SIGNATURE specifies whether to show the time signature for tab staves.',
}),
SHOW_TAB_STEMS: t.boolean({
defaultValue: true,
defaultValue: false,
help: 'SHOW_TAB_STEMS specifies whether to show stems for tab staves.',
}),
MEASURE_LABELING_SCHEME: t.enum({
Expand Down
31 changes: 23 additions & 8 deletions src/rendering/ensemble.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ export class Ensemble {
const vexflowVoices = voiceRenders.flatMap((v) => v.vexflowVoices).filter((v) => v.getTickables().length > 0);
const entryRenders = voiceRenders.flatMap((v) => v.entryRenders);

if (staveRenders.length === 0) {
return;
}

// Calculate the non-voice width.
const nonVoiceWidth =
vexflow.Stave.defaultPadding +
Expand All @@ -47,19 +51,24 @@ export class Ensemble {
this.getKeyWidth(staveRenders) +
this.getTimeWidth(staveRenders);

// Align the starting notes of each stave to the same x position.
const maxNoteStartX = Math.max(...vexflowStaves.map((v) => v.getNoteStartX()));
const noteStartOffsetXes = vexflowStaves.map((v) => maxNoteStartX - v.getNoteStartX());

// Calculate stave width.
let staveWidth: number;
if (width) {
staveWidth = width;
} else {
staveWidth = this.getVoiceWidth(staveRenders, voiceRenders, entryRenders) + nonVoiceWidth;
staveWidth = this.getVoiceWidth(noteStartOffsetXes, staveRenders, voiceRenders, entryRenders) + nonVoiceWidth;
}
staveWidth -= paddingLeft;
staveWidth -= paddingRight;

// Set the width on the vexflow staves.
for (const vexflowStave of vexflowStaves) {
vexflowStave.setWidth(staveWidth);
vexflowStave.setNoteStartX(maxNoteStartX);
}

// Assign each voice to a stave.
Expand All @@ -75,7 +84,7 @@ export class Ensemble {
// Format _all_ the voices. The voice width must be smaller than the stave or the stave won't contain it.
const voiceWidth = staveWidth - nonVoiceWidth;
if (vexflowVoices.length > 0) {
vexflowFormatter.format(vexflowVoices, voiceWidth);
vexflowFormatter.format(vexflowVoices, voiceWidth, { autoBeam: true });
}

// Populate the rects by either using the cache or drawing.
Expand Down Expand Up @@ -229,6 +238,7 @@ export class Ensemble {
}

private getVoiceWidth(
noteStartOffsetXes: number[],
staveRenders: StaveRender[],
voiceRenders: VoiceRender[],
entryRenders: VoiceEntryRender[]
Expand All @@ -239,7 +249,7 @@ export class Ensemble {
if (multiRestCount > 0) {
width += this.getMultiRestStaveWidth();
} else {
width += this.getMinRequiredStaveWidth(staveRenders);
width += this.getMinRequiredStaveWidth(noteStartOffsetXes, staveRenders);
}

width += this.getTupletExtraWidth(voiceRenders);
Expand All @@ -252,18 +262,23 @@ export class Ensemble {
return this.config.BASE_MULTI_REST_MEASURE_WIDTH;
}

private getMinRequiredStaveWidth(staveRenders: StaveRender[]): number {
private getMinRequiredStaveWidth(noteStartOffsetXes: number[], staveRenders: StaveRender[]): number {
const fragmentCount = this.document.getFragmentCount(this.key);
return this.config.BASE_VOICE_WIDTH / fragmentCount + this.getMinRequiredVoiceWidth(staveRenders);
return (
this.config.BASE_VOICE_WIDTH / fragmentCount + this.getMinRequiredVoiceWidth(noteStartOffsetXes, staveRenders)
);
}

private getMinRequiredVoiceWidth(staveRenders: StaveRender[]): number {
const widths = staveRenders.map((s) => {
private getMinRequiredVoiceWidth(noteStartOffsetXes: number[], staveRenders: StaveRender[]): number {
const widths = staveRenders.map((s, index) => {
const vexflowVoices = s.voiceRenders.flatMap((v) => v.vexflowVoices).filter((v) => v.getTickables().length > 0);
if (vexflowVoices.length === 0) {
return 0;
} else {
return new vexflow.Formatter().joinVoices(vexflowVoices).preCalculateMinTotalWidth(vexflowVoices);
return (
new vexflow.Formatter().joinVoices(vexflowVoices).preCalculateMinTotalWidth(vexflowVoices) +
noteStartOffsetXes[index]
);
}
});
if (widths.length > 0) {
Expand Down
Binary file modified tests/integration/__image_snapshots__/02e-Rests-NoType_900px.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/integration/__image_snapshots__/43a-PianoStaff_900px.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/integration/__image_snapshots__/71e-TabStaves_900px.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/integration/__image_snapshots__/complex_formatting_900px.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/integration/__image_snapshots__/tabs_basic_900px.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit db6b28c

Please sign in to comment.