diff --git a/src/rendering/glyphs/ScoreBrushGlyph.ts b/src/rendering/glyphs/ScoreBrushGlyph.ts index 99dd596ac..930e8b368 100644 --- a/src/rendering/glyphs/ScoreBrushGlyph.ts +++ b/src/rendering/glyphs/ScoreBrushGlyph.ts @@ -16,17 +16,23 @@ export class ScoreBrushGlyph extends Glyph { } public override doLayout(): void { - this.width = 10 * this.scale; + this.width = + this._beat.brushType === BrushType.ArpeggioUp || this._beat.brushType === BrushType.ArpeggioDown + ? 10 * this.scale + : 0; } public override paint(cx: number, cy: number, canvas: ICanvas): void { - let scoreBarRenderer: ScoreBarRenderer = this.renderer as ScoreBarRenderer; - let lineSize: number = scoreBarRenderer.lineOffset; - let startY: number = cy + this.y + (scoreBarRenderer.getNoteY(this._beat.maxNote!, NoteYPosition.Bottom) - lineSize); - let endY: number = cy + this.y + scoreBarRenderer.getNoteY(this._beat.minNote!, NoteYPosition.Top) + lineSize; - let arrowX: number = cx + this.x + this.width / 2; - let arrowSize: number = 8 * this.scale; - if (this._beat.brushType !== BrushType.None) { + if (this._beat.brushType === BrushType.ArpeggioUp || this._beat.brushType === BrushType.ArpeggioDown) { + let scoreBarRenderer: ScoreBarRenderer = this.renderer as ScoreBarRenderer; + let lineSize: number = scoreBarRenderer.lineOffset; + let startY: number = + cy + this.y + (scoreBarRenderer.getNoteY(this._beat.maxNote!, NoteYPosition.Bottom) - lineSize); + let endY: number = + cy + this.y + scoreBarRenderer.getNoteY(this._beat.minNote!, NoteYPosition.Top) + lineSize; + let arrowX: number = cx + this.x + this.width / 2; + let arrowSize: number = 8 * this.scale; + let glyph: NoteVibratoGlyph = new NoteVibratoGlyph(0, 0, VibratoType.Slight, 1.2, true); glyph.renderer = this.renderer; glyph.doLayout(); @@ -34,11 +40,10 @@ export class ScoreBrushGlyph extends Glyph { let waveOffset = -glyph.height / 2; if (this._beat.brushType === BrushType.ArpeggioUp) { - let lineStartY: number = startY + arrowSize; let lineEndY: number = endY - arrowSize; glyph.width = Math.abs(lineEndY - lineStartY); - + canvas.beginRotate(cx + this.x + 5 * this.scale, lineEndY, -90); glyph.paint(0, waveOffset, canvas); canvas.endRotate(); @@ -50,7 +55,6 @@ export class ScoreBrushGlyph extends Glyph { canvas.closePath(); canvas.fill(); } else if (this._beat.brushType === BrushType.ArpeggioDown) { - let lineStartY: number = startY + arrowSize; let lineEndY: number = endY; glyph.width = Math.abs(lineEndY - lineStartY); diff --git a/src/rendering/glyphs/VoiceContainerGlyph.ts b/src/rendering/glyphs/VoiceContainerGlyph.ts index 6eabc93ec..f0df3b16f 100644 --- a/src/rendering/glyphs/VoiceContainerGlyph.ts +++ b/src/rendering/glyphs/VoiceContainerGlyph.ts @@ -42,7 +42,8 @@ export class VoiceContainerGlyph extends GlyphGroup { switch (currentBeatGlyph.beat.graceType) { case GraceType.None: - currentBeatGlyph.x = positions.get(currentBeatGlyph.beat.absoluteDisplayStart)! * scale - currentBeatGlyph.onTimeX; + currentBeatGlyph.x = + positions.get(currentBeatGlyph.beat.absoluteDisplayStart)! * scale - currentBeatGlyph.onTimeX; break; default: const graceDisplayStart = currentBeatGlyph.beat.graceGroup!.beats[0].absoluteDisplayStart; @@ -50,37 +51,49 @@ export class VoiceContainerGlyph extends GlyphGroup { // placement for proper grace notes which have a following note if (currentBeatGlyph.beat.graceGroup!.isComplete && positions.has(graceDisplayStart)) { currentBeatGlyph.x = positions.get(graceDisplayStart)! * scale - currentBeatGlyph.onTimeX; + let graceSprings = this.renderer.layoutingInfo.allGraceRods.get(graceGroupId)!; - let graceTargetPreBeat = this.renderer.layoutingInfo.springs.get(graceDisplayStart)!.preBeatWidth; + + // get the pre beat stretch of this voice/staff, not the + // shared space. This way we use the potentially empty space (see discussions/1092). + const afterGraceBeat = + currentBeatGlyph.beat.graceGroup!.beats[currentBeatGlyph.beat.graceGroup!.beats.length - 1] + .nextBeat; + const preBeatStretch = afterGraceBeat + ? this.renderer.layoutingInfo.getPreBeatSize(afterGraceBeat) + + BeatContainerGlyph.GraceBeatPadding * this.renderer.scale + : 0; + // move right in front to the note - currentBeatGlyph.x -= graceTargetPreBeat; + currentBeatGlyph.x -= preBeatStretch; // respect the post beat width of the grace note currentBeatGlyph.x -= graceSprings[currentBeatGlyph.beat.graceIndex].postSpringWidth; // shift to right position of the particular grace note currentBeatGlyph.x += graceSprings[currentBeatGlyph.beat.graceIndex].graceBeatWidth; - // move the whole group again forward for cases where another track has e.g. 3 beats and here we have only 2. + // move the whole group again forward for cases where another track has e.g. 3 beats and here we have only 2. // so we shift the whole group of this voice to stick to the end of the group. const lastGraceSpring = graceSprings[currentBeatGlyph.beat.graceGroup!.beats.length - 1]; currentBeatGlyph.x -= lastGraceSpring.graceBeatWidth; - } else { // placement for improper grace beats where no beat in the same bar follows let graceSpring = this.renderer.layoutingInfo.incompleteGraceRods.get(graceGroupId)!; - const relativeOffset = graceSpring[currentBeatGlyph.beat.graceIndex].postSpringWidth - - graceSpring[currentBeatGlyph.beat.graceIndex].preSpringWidth + const relativeOffset = + graceSpring[currentBeatGlyph.beat.graceIndex].postSpringWidth - + graceSpring[currentBeatGlyph.beat.graceIndex].preSpringWidth; if (i > 0) { if (currentBeatGlyph.beat.graceIndex === 0) { // we place the grace beat directly after the previous one - // otherwise this causes flickers on resizing + // otherwise this causes flickers on resizing currentBeatGlyph.x = beatGlyphs[i - 1].x + beatGlyphs[i - 1].width; } else { // for the multiple grace glyphs we take the width of the grace rod // this width setting is aligned with the positioning logic below - currentBeatGlyph.x = beatGlyphs[i - 1].x - + graceSpring[currentBeatGlyph.beat.graceIndex - 1].postSpringWidth - - graceSpring[currentBeatGlyph.beat.graceIndex - 1].preSpringWidth - - relativeOffset; + currentBeatGlyph.x = + beatGlyphs[i - 1].x + + graceSpring[currentBeatGlyph.beat.graceIndex - 1].postSpringWidth - + graceSpring[currentBeatGlyph.beat.graceIndex - 1].preSpringWidth - + relativeOffset; } } else { currentBeatGlyph.x = -relativeOffset; @@ -134,8 +147,7 @@ export class VoiceContainerGlyph extends GlyphGroup { } } - public override doLayout(): void { - } + public override doLayout(): void {} public override paint(cx: number, cy: number, canvas: ICanvas): void { // canvas.color = Color.random(); diff --git a/src/rendering/staves/BarLayoutingInfo.ts b/src/rendering/staves/BarLayoutingInfo.ts index 8cca82afd..12a1c2c89 100644 --- a/src/rendering/staves/BarLayoutingInfo.ts +++ b/src/rendering/staves/BarLayoutingInfo.ts @@ -44,43 +44,43 @@ export class BarLayoutingInfo { } public setPreBeatSize(beat: Beat, size: number): void { - if (!this.preBeatSizes.has(beat.index) || this.preBeatSizes.get(beat.index)! < size) { - this.preBeatSizes.set(beat.index, size); + if (!this.preBeatSizes.has(beat.id) || this.preBeatSizes.get(beat.id)! < size) { + this.preBeatSizes.set(beat.id, size); this.version++; } } public getPreBeatSize(beat: Beat): number { - if (this.preBeatSizes.has(beat.index)) { - return this.preBeatSizes.get(beat.index)!; + if (this.preBeatSizes.has(beat.id)) { + return this.preBeatSizes.get(beat.id)!; } return 0; } public setOnBeatSize(beat: Beat, size: number): void { - if (!this.onBeatSizes.has(beat.index) || this.onBeatSizes.get(beat.index)! < size) { - this.onBeatSizes.set(beat.index, size); + if (!this.onBeatSizes.has(beat.id) || this.onBeatSizes.get(beat.id)! < size) { + this.onBeatSizes.set(beat.id, size); this.version++; } } public getOnBeatSize(beat: Beat): number { - if (this.onBeatSizes.has(beat.index)) { - return this.onBeatSizes.get(beat.index)!; + if (this.onBeatSizes.has(beat.id)) { + return this.onBeatSizes.get(beat.id)!; } return 0; } public getBeatCenterX(beat: Beat): number { - if (this.onBeatCenterX.has(beat.index)) { - return this.onBeatCenterX.get(beat.index)!; + if (this.onBeatCenterX.has(beat.id)) { + return this.onBeatCenterX.get(beat.id)!; } return 0; } public setBeatCenterX(beat: Beat, x: number): void { - if (!this.onBeatCenterX.has(beat.index) || this.onBeatCenterX.get(beat.index)! < x) { - this.onBeatCenterX.set(beat.index, x); + if (!this.onBeatCenterX.has(beat.id) || this.onBeatCenterX.get(beat.id)! < x) { + this.onBeatCenterX.set(beat.id, x); this.version++; } } diff --git a/test-data/visual-tests/effects-and-annotations/brush.png b/test-data/visual-tests/effects-and-annotations/brush.png index 95b189d5e..a8c2fd8e1 100644 Binary files a/test-data/visual-tests/effects-and-annotations/brush.png and b/test-data/visual-tests/effects-and-annotations/brush.png differ diff --git a/test-data/visual-tests/music-notation/brushes-ukulele.png b/test-data/visual-tests/music-notation/brushes-ukulele.png index d5c091fa7..20398ac3d 100644 Binary files a/test-data/visual-tests/music-notation/brushes-ukulele.png and b/test-data/visual-tests/music-notation/brushes-ukulele.png differ diff --git a/test-data/visual-tests/notation-legend/bends-default.png b/test-data/visual-tests/notation-legend/bends-default.png index 727110852..5e79d8f5a 100644 Binary files a/test-data/visual-tests/notation-legend/bends-default.png and b/test-data/visual-tests/notation-legend/bends-default.png differ diff --git a/test-data/visual-tests/notation-legend/bends-songbook.png b/test-data/visual-tests/notation-legend/bends-songbook.png index fd66fac21..7fb1a1f94 100644 Binary files a/test-data/visual-tests/notation-legend/bends-songbook.png and b/test-data/visual-tests/notation-legend/bends-songbook.png differ diff --git a/test-data/visual-tests/notation-legend/full-songbook.png b/test-data/visual-tests/notation-legend/full-songbook.png index 167ba0ffd..86958e0ad 100644 Binary files a/test-data/visual-tests/notation-legend/full-songbook.png and b/test-data/visual-tests/notation-legend/full-songbook.png differ diff --git a/test-data/visual-tests/notation-legend/grace-default.png b/test-data/visual-tests/notation-legend/grace-default.png index ad0c6d1c6..d5bfb6d7f 100644 Binary files a/test-data/visual-tests/notation-legend/grace-default.png and b/test-data/visual-tests/notation-legend/grace-default.png differ diff --git a/test-data/visual-tests/notation-legend/grace-songbook.png b/test-data/visual-tests/notation-legend/grace-songbook.png index 37d3a3c58..cbeb75636 100644 Binary files a/test-data/visual-tests/notation-legend/grace-songbook.png and b/test-data/visual-tests/notation-legend/grace-songbook.png differ diff --git a/test-data/visual-tests/notation-legend/multi-grace-default.png b/test-data/visual-tests/notation-legend/multi-grace-default.png index ecff75ad0..0ea5c40d2 100644 Binary files a/test-data/visual-tests/notation-legend/multi-grace-default.png and b/test-data/visual-tests/notation-legend/multi-grace-default.png differ diff --git a/test-data/visual-tests/notation-legend/multi-grace-songbook.png b/test-data/visual-tests/notation-legend/multi-grace-songbook.png index 8855c2303..6662c69d4 100644 Binary files a/test-data/visual-tests/notation-legend/multi-grace-songbook.png and b/test-data/visual-tests/notation-legend/multi-grace-songbook.png differ diff --git a/test-data/visual-tests/special-notes/grace-notes-advanced.png b/test-data/visual-tests/special-notes/grace-notes-advanced.png index 52a6f3cf5..ede47922d 100644 Binary files a/test-data/visual-tests/special-notes/grace-notes-advanced.png and b/test-data/visual-tests/special-notes/grace-notes-advanced.png differ diff --git a/test-data/visual-tests/special-notes/grace-notes-alignment.gp b/test-data/visual-tests/special-notes/grace-notes-alignment.gp new file mode 100644 index 000000000..844f3398e Binary files /dev/null and b/test-data/visual-tests/special-notes/grace-notes-alignment.gp differ diff --git a/test-data/visual-tests/special-notes/grace-notes-alignment.png b/test-data/visual-tests/special-notes/grace-notes-alignment.png new file mode 100644 index 000000000..f00910bc2 Binary files /dev/null and b/test-data/visual-tests/special-notes/grace-notes-alignment.png differ diff --git a/test-data/visual-tests/special-notes/grace-notes.png b/test-data/visual-tests/special-notes/grace-notes.png index d5cdd2b05..b70aaddea 100644 Binary files a/test-data/visual-tests/special-notes/grace-notes.png and b/test-data/visual-tests/special-notes/grace-notes.png differ diff --git a/test/visualTests/features/SpecialNotes.test.ts b/test/visualTests/features/SpecialNotes.test.ts index a1fe8a897..98d12a6c2 100644 --- a/test/visualTests/features/SpecialNotes.test.ts +++ b/test/visualTests/features/SpecialNotes.test.ts @@ -20,6 +20,10 @@ describe('SpecialNotesTests', () => { await VisualTestHelper.runVisualTest('special-notes/grace-notes-advanced.gp', undefined, [0, 1], undefined, 1, true); }); + it('grace-alignment', async () => { + await VisualTestHelper.runVisualTest('special-notes/grace-notes-alignment.gp', undefined, [0, 1]); + }); + it('dead-notes', async () => { await VisualTestHelper.runVisualTest('special-notes/dead-notes.gp'); });