diff --git a/src.compiler/csharp/CSharpEmitterContext.ts b/src.compiler/csharp/CSharpEmitterContext.ts index 6d394c78f..db0a2895c 100644 --- a/src.compiler/csharp/CSharpEmitterContext.ts +++ b/src.compiler/csharp/CSharpEmitterContext.ts @@ -535,11 +535,14 @@ export default class CSharpEmitterContext { } else if (actualType == null) { actualType = t; } else if (actualType != null && actualType.flags !== t.flags) { - this.addCsNodeDiagnostics( - parent, - 'Union type covering multiple types detected, fallback to dynamic', - ts.DiagnosticCategory.Warning - ); + let isEmitted = this.isNodeEmitted(parent); + if (isEmitted) { + this.addCsNodeDiagnostics( + parent, + 'Union type covering multiple types detected, fallback to dynamic', + ts.DiagnosticCategory.Warning + ); + } fallbackToObject = true; } else { actualType = t; @@ -568,6 +571,15 @@ export default class CSharpEmitterContext { isOptional: isOptional } as cs.TypeReference; } + + private isNodeEmitted(node: cs.Node): boolean { + if ('skipEmit' in node && node.skipEmit as boolean) { + return false; + } else if (node.parent) { + return this.isNodeEmitted(node.parent); + } + return true; + } public isDefaultValueNull(tsType: ts.Type): boolean { tsType = this.typeChecker.getNonNullableType(tsType); diff --git a/src/model/Bar.ts b/src/model/Bar.ts index 92d01cd07..a4f894c2b 100644 --- a/src/model/Bar.ts +++ b/src/model/Bar.ts @@ -63,6 +63,13 @@ export class Bar { */ public simileMark: SimileMark = SimileMark.None; + /** + * Gets a value indicating whether this bar contains multiple voices with notes. + * @json_ignore + */ + public isMultiVoice: boolean = false; + + public get masterBar(): MasterBar { return this.staff.track.score.masterBars[this.index]; } @@ -83,9 +90,13 @@ export class Bar { } public finish(settings: Settings): void { + this.isMultiVoice = false; for (let i: number = 0, j: number = this.voices.length; i < j; i++) { let voice: Voice = this.voices[i]; voice.finish(settings); + if(i > 0 && !voice.isEmpty) { + this.isMultiVoice = true; + } } } diff --git a/src/platform/ICanvas.ts b/src/platform/ICanvas.ts index 83301511b..ce587a5b5 100644 --- a/src/platform/ICanvas.ts +++ b/src/platform/ICanvas.ts @@ -29,10 +29,12 @@ export enum TextBaseline { Top, /** * Text is aligned middle - */ Middle, + */ + Middle, /** * Text is aligend on the bottom. - */ Bottom + */ + Bottom } /** diff --git a/src/platform/svg/SvgCanvas.ts b/src/platform/svg/SvgCanvas.ts index e279c9f07..d032d2591 100644 --- a/src/platform/svg/SvgCanvas.ts +++ b/src/platform/svg/SvgCanvas.ts @@ -26,6 +26,7 @@ export abstract class SvgCanvas implements ICanvas { }px" class="at-surface-svg">\n`; this._currentPath = ''; this._currentPathIsEmpty = true; + this.textBaseline = TextBaseline.Top; } public beginGroup(identifier: string): void { @@ -164,10 +165,11 @@ export abstract class SvgCanvas implements ICanvas { protected getSvgBaseLine(): string { switch (this.textBaseline) { case TextBaseline.Top: - return `dy="1.25ex"`; + return `dominant-baseline="hanging"`; case TextBaseline.Middle: - return `dy="0.5ex"`; + return `dominant-baseline="central"`; case TextBaseline.Bottom: + return `dominant-baseline="bottom"`; default: return ''; } diff --git a/src/rendering/BarRendererBase.ts b/src/rendering/BarRendererBase.ts index 980b833e3..95a1b28e3 100644 --- a/src/rendering/BarRendererBase.ts +++ b/src/rendering/BarRendererBase.ts @@ -21,6 +21,7 @@ import { MasterBarBounds } from '@src/rendering/utils/MasterBarBounds'; import { RenderingResources } from '@src/RenderingResources'; import { Settings } from '@src/Settings'; import { BeatOnNoteGlyphBase } from './glyphs/BeatOnNoteGlyphBase'; +import { BeamingHelper } from './utils/BeamingHelper'; /** * Lists the different position modes for {@link BarRendererBase.getNoteY} @@ -486,4 +487,8 @@ export class BarRendererBase { break; } } + + public completeBeamingHelper(helper: BeamingHelper) { + // nothing by default + } } diff --git a/src/rendering/ScoreBarRenderer.ts b/src/rendering/ScoreBarRenderer.ts index 1385af456..209ed0d75 100644 --- a/src/rendering/ScoreBarRenderer.ts +++ b/src/rendering/ScoreBarRenderer.ts @@ -9,7 +9,7 @@ import { Note } from '@src/model/Note'; import { TupletGroup } from '@src/model/TupletGroup'; import { Voice } from '@src/model/Voice'; import { FingeringMode, NotationMode } from '@src/NotationSettings'; -import { ICanvas, TextAlign } from '@src/platform/ICanvas'; +import { ICanvas, TextAlign, TextBaseline } from '@src/platform/ICanvas'; import { BarRendererBase, NoteYPosition } from '@src/rendering/BarRendererBase'; import { AccidentalGlyph } from '@src/rendering/glyphs/AccidentalGlyph'; import { BarNumberGlyph } from '@src/rendering/glyphs/BarNumberGlyph'; @@ -84,11 +84,11 @@ export class ScoreBarRenderer extends BarRendererBase { this.updateFirstLineY(); super.doLayout(); if (!this.bar.isEmpty && this.accidentalHelper.maxLineBeat) { - let top: number = this.getScoreY(-2, 0); - let bottom: number = this.getScoreY(6, 0); + let top: number = this.getScoreY(-2); + let bottom: number = this.getScoreY(6); let whammyOffset: number = this.simpleWhammyOverflow; this.registerOverflowTop(whammyOffset); - let maxNoteY: number = this.getScoreY(this.accidentalHelper.maxLine, 0); + let maxNoteY: number = this.getScoreY(this.accidentalHelper.maxLine); let maxNoteHelper: BeamingHelper = this.helpers.getBeamingHelperForBeat(this.accidentalHelper.maxLineBeat); if (maxNoteHelper.direction === BeamDirection.Up) { maxNoteY -= this.getStemSize(maxNoteHelper); @@ -103,7 +103,7 @@ export class ScoreBarRenderer extends BarRendererBase { if (maxNoteY < top) { this.registerOverflowTop(Math.abs(maxNoteY) + whammyOffset); } - let minNoteY: number = this.getScoreY(this.accidentalHelper.minLine, 0); + let minNoteY: number = this.getScoreY(this.accidentalHelper.minLine); let minNoteHelper: BeamingHelper = this.helpers.getBeamingHelperForBeat(this.accidentalHelper.minLineBeat!); if (minNoteHelper.direction === BeamDirection.Down) { minNoteY += this.getStemSize(minNoteHelper); @@ -146,18 +146,22 @@ export class ScoreBarRenderer extends BarRendererBase { canvas.color = h.voice!.index === 0 ? this.resources.mainGlyphColor : this.resources.secondaryGlyphColor; // TODO: draw stem at least at the center of the score staff. // check if we need to paint simple footer - if (h.beats.length === 1) { - this.paintFlag(cx, cy, canvas, h); - } else { - this.paintBar(cx, cy, canvas, h); + if (!h.isRestBeamHelper) { + if (h.beats.length === 1) { + this.paintFlag(cx, cy, canvas, h); + } else { + this.paintBar(cx, cy, canvas, h); + } } } private paintTupletHelper(cx: number, cy: number, canvas: ICanvas, h: TupletGroup): void { let res: RenderingResources = this.resources; let oldAlign: TextAlign = canvas.textAlign; + let oldBaseLine = canvas.textBaseline; canvas.color = h.voice.index === 0 ? this.resources.mainGlyphColor : this.resources.secondaryGlyphColor; canvas.textAlign = TextAlign.Center; + canvas.textBaseline = TextBaseline.Middle; let s: string; let num: number = h.beats[0].tupletNumerator; let den: number = h.beats[0].tupletDenominator; @@ -188,6 +192,9 @@ export class ScoreBarRenderer extends BarRendererBase { s = num + ':' + den; } // check if we need to paint simple footer + let offset: number = 10 * this.scale; + let size: number = 5 * this.scale; + if (h.beats.length === 1 || !h.isFull) { for (let i: number = 0, j: number = h.beats.length; i < j; i++) { let beat: Beat = h.beats[i]; @@ -197,77 +204,115 @@ export class ScoreBarRenderer extends BarRendererBase { } let direction: BeamDirection = beamingHelper.direction; let tupletX: number = beamingHelper.getBeatLineX(beat); - let tupletY: number = cy + this.y + this.calculateBeamY(beamingHelper, tupletX); - let offset: number = direction === BeamDirection.Up ? res.effectFont.size * 1.5 : -3 * this.scale; + let tupletY: number = this.calculateBeamYWithDirection( + beamingHelper, + tupletX, + direction + ); + if (direction === BeamDirection.Down) { + offset *= -1; + size *= -1; + } canvas.font = res.effectFont; - canvas.fillText(s, cx + this.x + tupletX, tupletY - offset); + canvas.fillText(s, cx + this.x + tupletX, cy + this.y + tupletY - offset - size); } } else { let firstBeat: Beat = h.beats[0]; let lastBeat: Beat = h.beats[h.beats.length - 1]; - let firstBeamingHelper: BeamingHelper = this.helpers.beamHelperLookup[h.voice.index].get(firstBeat.index)!; - let lastBeamingHelper: BeamingHelper = this.helpers.beamHelperLookup[h.voice.index].get(lastBeat.index)!; - if (firstBeamingHelper && lastBeamingHelper) { - let direction: BeamDirection = firstBeamingHelper.direction; - // - // Calculate the overall area of the tuplet bracket - let startX: number = firstBeamingHelper.getBeatLineX(firstBeat); - let endX: number = lastBeamingHelper.getBeatLineX(lastBeat); - // - // Calculate how many space the text will need - canvas.font = res.effectFont; - let sw: number = canvas.measureText(s); - let sp: number = 3 * this.scale; - // - // Calculate the offsets where to break the bracket - let middleX: number = (startX + endX) / 2; - let offset1X: number = middleX - sw / 2 - sp; - let offset2X: number = middleX + sw / 2 + sp; - // - // calculate the y positions for our bracket - let startY: number = this.calculateBeamYWithDirection( - firstBeamingHelper, - startX, - firstBeamingHelper.direction - ); - let endY: number = this.calculateBeamYWithDirection( - lastBeamingHelper, - endX, - firstBeamingHelper.direction - ); - let k: number = (endY - startY) / (endX - startX); - let d: number = startY - k * startX; - let offset1Y: number = k * offset1X + d; - let middleY: number = k * middleX + d; - let offset2Y: number = k * offset2X + d; - let offset: number = 10 * this.scale; - let size: number = 5 * this.scale; - if (direction === BeamDirection.Down) { - offset *= -1; - size *= -1; + let firstNonRestBeat: Beat | null = null; + let lastNonRestBeat: Beat | null = null; + for (let i = 0; i < h.beats.length; i++) { + if (!h.beats[i].isRest) { + firstNonRestBeat = h.beats[i]; + break; } - // - // draw the bracket - canvas.beginPath(); - canvas.moveTo(cx + this.x + startX, (cy + this.y + startY - offset) | 0); - canvas.lineTo(cx + this.x + startX, (cy + this.y + startY - offset - size) | 0); - canvas.lineTo(cx + this.x + offset1X, (cy + this.y + offset1Y - offset - size) | 0); - canvas.stroke(); - canvas.beginPath(); - canvas.moveTo(cx + this.x + offset2X, (cy + this.y + offset2Y - offset - size) | 0); - canvas.lineTo(cx + this.x + endX, (cy + this.y + endY - offset - size) | 0); - canvas.lineTo(cx + this.x + endX, (cy + this.y + endY - offset) | 0); - canvas.stroke(); - // - // Draw the string - canvas.fillText( - s, - cx + this.x + middleX, - cy + this.y + middleY - offset - size - res.effectFont.size / 2 - ); } + for (let i = h.beats.length - 1; i >= 0; i--) { + if (!h.beats[i].isRest) { + lastNonRestBeat = h.beats[i]; + break; + } + } + + let isRestOnly = false; + if (!firstNonRestBeat) { + firstNonRestBeat = firstBeat; + isRestOnly = true; + } + + if (!lastNonRestBeat) { + lastNonRestBeat = lastBeat; + } + + // + // Calculate the overall area of the tuplet bracket + let firstBeamingHelper = this.helpers.beamHelperLookup[h.voice.index].get(firstBeat.index)!; + let lastBeamingHelper = this.helpers.beamHelperLookup[h.voice.index].get(lastBeat.index)!; + let startX: number = firstBeamingHelper.getBeatLineX(firstBeat); + let endX: number = lastBeamingHelper.getBeatLineX(lastBeat); + + // + // calculate the y positions for our bracket + let firstNonRestBeamingHelper = this.helpers.beamHelperLookup[h.voice.index].get(firstNonRestBeat.index)!; + let lastNonRestBeamingHelper = this.helpers.beamHelperLookup[h.voice.index].get(lastNonRestBeat.index)!; + let direction = firstBeamingHelper.direction; + let startY: number = this.calculateBeamYWithDirection( + firstNonRestBeamingHelper, + startX, + direction + ); + let endY: number = this.calculateBeamYWithDirection( + lastNonRestBeamingHelper, + endX, + direction + ); + if(isRestOnly) { + startY = Math.max(startY, endY); + endY = startY; + } + + // + // Calculate how many space the text will need + canvas.font = res.effectFont; + let sw: number = canvas.measureText(s); + let sp: number = 3 * this.scale; + // + // Calculate the offsets where to break the bracket + let middleX: number = (startX + endX) / 2; + let offset1X: number = middleX - sw / 2 - sp; + let offset2X: number = middleX + sw / 2 + sp; + + let k: number = (endY - startY) / (endX - startX); + let d: number = startY - k * startX; + let offset1Y: number = k * offset1X + d; + let middleY: number = k * middleX + d; + let offset2Y: number = k * offset2X + d; + if (direction === BeamDirection.Down) { + offset *= -1; + size *= -1; + } + // + // draw the bracket + canvas.beginPath(); + canvas.moveTo(cx + this.x + startX, (cy + this.y + startY - offset) | 0); + canvas.lineTo(cx + this.x + startX, (cy + this.y + startY - offset - size) | 0); + canvas.lineTo(cx + this.x + offset1X, (cy + this.y + offset1Y - offset - size) | 0); + canvas.stroke(); + canvas.beginPath(); + canvas.moveTo(cx + this.x + offset2X, (cy + this.y + offset2Y - offset - size) | 0); + canvas.lineTo(cx + this.x + endX, (cy + this.y + endY - offset - size) | 0); + canvas.lineTo(cx + this.x + endX, (cy + this.y + endY - offset) | 0); + canvas.stroke(); + // + // Draw the string + canvas.fillText( + s, + cx + this.x + middleX, + cy + this.y + middleY - offset - size + ); } canvas.textAlign = oldAlign; + canvas.textBaseline = oldBaseLine; } public getStemSize(helper: BeamingHelper): number { @@ -351,6 +396,23 @@ export class ScoreBarRenderer extends BarRendererBase { return this.calculateBeamYWithDirection(h, x, h.direction); } + public applyLayoutingInfo(): boolean { + const result = super.applyLayoutingInfo(); + if (result && this.bar.isMultiVoice) { + // consider rest overflows + let top: number = this.getScoreY(-2); + let bottom: number = this.getScoreY(6); + let minMax = this.helpers.collisionHelper.getBeatMinMaxY(); + if (minMax[0] < top) { + this.registerOverflowTop(Math.abs(minMax[0])); + } + if (minMax[1] > bottom) { + this.registerOverflowBottom(Math.abs(minMax[1]) - bottom); + } + } + return result; + } + private calculateBeamYWithDirection(h: BeamingHelper, x: number, direction: BeamDirection): number { let stemSize: number = this.getStemSize(h); @@ -367,19 +429,34 @@ export class ScoreBarRenderer extends BarRendererBase { const firstBeat = h.beats[0]; const lastBeat = h.beats[h.beats.length - 1]; - // 1. put direct diagonal line. + let isRest = h.isRestBeamHelper; + + // 1. put direct diagonal line. + drawingInfo.startBeat = firstBeat; drawingInfo.startX = h.getBeatLineX(firstBeat); - drawingInfo.startY = - direction === BeamDirection.Up + if (isRest) { + drawingInfo.startY = direction === BeamDirection.Up + ? this.getScoreY(h.minRestLine!) + : this.getScoreY(h.maxRestLine!); + } else { + drawingInfo.startY = direction === BeamDirection.Up ? this.getScoreY(this.accidentalHelper.getMinLine(firstBeat)) - stemSize : this.getScoreY(this.accidentalHelper.getMaxLine(firstBeat)) + stemSize; + } - drawingInfo.endX = h.getBeatLineX(lastBeat); - drawingInfo.endY = - direction === BeamDirection.Up - ? this.getScoreY(this.accidentalHelper.getMinLine(lastBeat)) - stemSize - : this.getScoreY(this.accidentalHelper.getMaxLine(lastBeat)) + stemSize; + drawingInfo.endBeat = lastBeat; + drawingInfo.endX = h.getBeatLineX(lastBeat); + if (isRest) { + drawingInfo.endY = direction === BeamDirection.Up + ? this.getScoreY(h.minRestLine!) + : this.getScoreY(h.maxRestLine!); + } else { + drawingInfo.endY = + direction === BeamDirection.Up + ? this.getScoreY(this.accidentalHelper.getMinLine(lastBeat)) - stemSize + : this.getScoreY(this.accidentalHelper.getMaxLine(lastBeat)) + stemSize; + } // 2. ensure max height // we use the min/max notes to place the beam along their real position // we only want a maximum of 10 offset for their gradient @@ -670,7 +747,6 @@ export class ScoreBarRenderer extends BarRendererBase { this.bar.clefOttava !== this.bar.previousBar!.clefOttava ) { let offset: number = 0; - let correction: number = 0.5; switch (this.bar.clef) { case Clef.Neutral: offset = this.bar.staff.standardNotationLineCount - 1; @@ -691,7 +767,7 @@ export class ScoreBarRenderer extends BarRendererBase { this.createStartSpacing(); this.addPreBeatGlyph( - new ClefGlyph(0, this.getScoreY(offset, correction), this.bar.clef, this.bar.clefOttava) + new ClefGlyph(0, this.getScoreY(offset) + 0.5 * BarRendererBase.StaffLineThickness, this.bar.clef, this.bar.clefOttava) ); } // Key signature @@ -730,7 +806,7 @@ export class ScoreBarRenderer extends BarRendererBase { if (this.bar.masterBar.isRepeatEnd) { this.addPostBeatGlyph(new RepeatCloseGlyph(this.x, 0)); if (this.bar.masterBar.repeatCount > 2) { - this.addPostBeatGlyph(new RepeatCountGlyph(0, this.getScoreY(-4), this.bar.masterBar.repeatCount)); + this.addPostBeatGlyph(new RepeatCountGlyph(0, this.getScoreHeight(-0.5), this.bar.masterBar.repeatCount)); } } else { this.addPostBeatGlyph(new BarSeperatorGlyph(0, 0)); @@ -777,13 +853,13 @@ export class ScoreBarRenderer extends BarRendererBase { if (ModelUtils.keySignatureIsSharp(currentKey)) { for (let i: number = 0; i < Math.abs(currentKey); i++) { let step: number = ScoreBarRenderer.SharpKsSteps[i] + offsetClef; - newGlyphs.push(new AccidentalGlyph(0, this.getScoreY(step, 0), AccidentalType.Sharp, false)); + newGlyphs.push(new AccidentalGlyph(0, this.getScoreY(step), AccidentalType.Sharp, false)); newLines.set(step, true); } } else { for (let i: number = 0; i < Math.abs(currentKey); i++) { let step: number = ScoreBarRenderer.FlatKsSteps[i] + offsetClef; - newGlyphs.push(new AccidentalGlyph(0, this.getScoreY(step, 0), AccidentalType.Flat, false)); + newGlyphs.push(new AccidentalGlyph(0, this.getScoreY(step), AccidentalType.Flat, false)); newLines.set(step, true); } } @@ -798,7 +874,7 @@ export class ScoreBarRenderer extends BarRendererBase { this.addPreBeatGlyph( new AccidentalGlyph( 0, - this.getScoreY(previousKeyPositions[i] + offsetClef, 0), + this.getScoreY(previousKeyPositions[i] + offsetClef), AccidentalType.Natural, false ) @@ -817,7 +893,7 @@ export class ScoreBarRenderer extends BarRendererBase { this.addPreBeatGlyph( new ScoreTimeSignatureGlyph( 0, - this.getScoreY(lines, 0), + this.getScoreY(lines), this.bar.masterBar.timeSignatureNumerator, this.bar.masterBar.timeSignatureDenominator, this.bar.masterBar.timeSignatureCommon @@ -842,15 +918,13 @@ export class ScoreBarRenderer extends BarRendererBase { /** * Gets the relative y position of the given steps relative to first line. * @param steps the amount of steps while 2 steps are one line - * @param correction * @returns */ - public getScoreY(steps: number, correction: number = 0): number { + public getScoreY(steps: number): number { return ( this._firstLineY + this.lineOffset + - this.getScoreHeight(steps) + - correction * this.scale * BarRendererBase.StaffLineThickness + this.getScoreHeight(steps) ); } @@ -883,4 +957,28 @@ export class ScoreBarRenderer extends BarRendererBase { this.paintSimileMark(cx, cy, canvas); } + + public completeBeamingHelper(helper: BeamingHelper) { + // for multi-voice bars we need to register the positions + // for multi-voice rest displacement to avoid collisions + if (this.bar.isMultiVoice && helper.highestNoteInHelper && helper.lowestNoteInHelper) { + let highestNotePosition = this.getNoteY(helper.highestNoteInHelper, NoteYPosition.Center); + let lowestNotePosition = this.getNoteY(helper.lowestNoteInHelper, NoteYPosition.Center); + + let offset = this.getStemSize(helper); + if (helper.hasTuplet) { + offset += this.resources.effectFont.size * 2; + } + + if (helper.direction == BeamDirection.Up) { + highestNotePosition -= offset; + } else { + lowestNotePosition += offset; + } + + for (const beat of helper.beats) { + this.helpers.collisionHelper.reserveBeatSlot(beat, highestNotePosition, lowestNotePosition); + } + } + } } diff --git a/src/rendering/TabBarRenderer.ts b/src/rendering/TabBarRenderer.ts index 5ad131f33..4daa791b5 100644 --- a/src/rendering/TabBarRenderer.ts +++ b/src/rendering/TabBarRenderer.ts @@ -5,12 +5,11 @@ import { GraceType } from '@src/model/GraceType'; import { TupletGroup } from '@src/model/TupletGroup'; import { Voice } from '@src/model/Voice'; import { TabRhythmMode } from '@src/NotationSettings'; -import { ICanvas, TextAlign } from '@src/platform/ICanvas'; +import { ICanvas, TextAlign, TextBaseline } from '@src/platform/ICanvas'; import { BarRendererBase, NoteYPosition } from '@src/rendering/BarRendererBase'; import { BarNumberGlyph } from '@src/rendering/glyphs/BarNumberGlyph'; import { BarSeperatorGlyph } from '@src/rendering/glyphs/BarSeperatorGlyph'; import { FlagGlyph } from '@src/rendering/glyphs/FlagGlyph'; -import { BeatGlyphBase } from '@src/rendering/glyphs/BeatGlyphBase'; import { RepeatCloseGlyph } from '@src/rendering/glyphs/RepeatCloseGlyph'; import { RepeatCountGlyph } from '@src/rendering/glyphs/RepeatCountGlyph'; import { RepeatOpenGlyph } from '@src/rendering/glyphs/RepeatOpenGlyph'; @@ -27,6 +26,7 @@ import { BeamDirection } from '@src/rendering/utils/BeamDirection'; import { BeamingHelper } from '@src/rendering/utils/BeamingHelper'; import { RenderingResources } from '@src/RenderingResources'; import { ModelUtils } from '@src/model/ModelUtils'; +import { ReservedLayoutAreaSlot } from './utils/BarCollisionHelper'; /** * This BarRenderer renders a bar using guitar tablature notation @@ -35,6 +35,8 @@ export class TabBarRenderer extends BarRendererBase { public static readonly StaffId: string = 'tab'; public static readonly TabLineSpacing: number = 10; + private _firstLineY: number = 0; + private _tupletSize: number = 0; public showTimeSignature: boolean = false; @@ -59,10 +61,19 @@ export class TabBarRenderer extends BarRendererBase { this.height += this.settings.notation.rhythmHeight * this.settings.display.scale; this.bottomPadding += this.settings.notation.rhythmHeight * this.settings.display.scale; } + + this.updateFirstLineY(); + super.updateSizes(); } + private updateFirstLineY() { + let res: RenderingResources = this.resources; + this._firstLineY = (res.tablatureFont.size / 2 + res.tablatureFont.size * 0.2) * this.scale; + } + public doLayout(): void { + this.updateFirstLineY(); super.doLayout(); if (this.settings.notation.rhythmMode !== TabRhythmMode.Hidden) { let hasTuplets: boolean = false; @@ -89,8 +100,8 @@ export class TabBarRenderer extends BarRendererBase { } // Clef if (this.isFirstOfLine) { - let center: number = (this.bar.staff.tuning.length + 1) / 2; - this.addPreBeatGlyph(new TabClefGlyph(5 * this.scale, this.getTabY(center, 0))); + let center: number = (this.bar.staff.tuning.length - 1) / 2; + this.addPreBeatGlyph(new TabClefGlyph(5 * this.scale, this.getTabY(center))); } // Time Signature if ( @@ -98,15 +109,15 @@ export class TabBarRenderer extends BarRendererBase { (!this.bar.previousBar || (this.bar.previousBar && this.bar.masterBar.timeSignatureNumerator !== - this.bar.previousBar.masterBar.timeSignatureNumerator) || + this.bar.previousBar.masterBar.timeSignatureNumerator) || (this.bar.previousBar && this.bar.masterBar.timeSignatureDenominator !== - this.bar.previousBar.masterBar.timeSignatureDenominator)) + this.bar.previousBar.masterBar.timeSignatureDenominator)) ) { this.createStartSpacing(); this.createTimeSignatureGlyphs(); } - this.addPreBeatGlyph(new BarNumberGlyph(0, this.getTabY(-0.5, 0), this.bar.index + 1)); + this.addPreBeatGlyph(new BarNumberGlyph(0, this.getTabHeight(-0.5), this.bar.index + 1)); } private _startSpacing: boolean = false; @@ -122,11 +133,11 @@ export class TabBarRenderer extends BarRendererBase { private createTimeSignatureGlyphs(): void { this.addPreBeatGlyph(new SpacingGlyph(0, 0, 5 * this.scale)); - const lines = (this.bar.staff.tuning.length + 1) / 2; + const lines = ((this.bar.staff.tuning.length + 1) / 2) - 1; this.addPreBeatGlyph( new TabTimeSignatureGlyph( 0, - this.getTabY(lines, 0), + this.getTabY(lines), this.bar.masterBar.timeSignatureNumerator, this.bar.masterBar.timeSignatureDenominator, this.bar.masterBar.timeSignatureCommon @@ -149,7 +160,7 @@ export class TabBarRenderer extends BarRendererBase { if (this.bar.masterBar.isRepeatEnd) { this.addPostBeatGlyph(new RepeatCloseGlyph(this.x, 0)); if (this.bar.masterBar.repeatCount > 2) { - this.addPostBeatGlyph(new RepeatCountGlyph(0, this.getTabY(-0.5, -3), this.bar.masterBar.repeatCount)); + this.addPostBeatGlyph(new RepeatCountGlyph(0, this.getTabY(-1), this.bar.masterBar.repeatCount)); } } else { this.addPostBeatGlyph(new BarSeperatorGlyph(0, 0)); @@ -158,12 +169,16 @@ export class TabBarRenderer extends BarRendererBase { /** * Gets the relative y position of the given steps relative to first line. - * @param line the amount of steps while 2 steps are one line + * @param line the line of the particular string where 0 is the most top line * @param correction * @returns */ - public getTabY(line: number, correction: number = 0): number { - return this.lineOffset * line + correction * this.scale; + public getTabY(line: number): number { + return this._firstLineY + this.getTabHeight(line); + } + + public getTabHeight(line: number): number { + return this.lineOffset * line; } public get middleYPosition(): number { @@ -177,13 +192,13 @@ export class TabBarRenderer extends BarRendererBase { // draw string lines // canvas.color = res.staffLineColor; - let lineY: number = cy + this.y + this.topPadding; let padding: number = this.scale; // collect tab note position for spaces let tabNotes: Float32Array[][] = []; for (let i: number = 0, j: number = this.bar.staff.tuning.length; i < j; i++) { tabNotes.push([]); } + for (let voice of this.bar.voices) { if (this.hasVoiceContainer(voice)) { let vc: VoiceContainerGlyph = this.getVoiceContainer(voice)!; @@ -212,18 +227,16 @@ export class TabBarRenderer extends BarRendererBase { return a[0] > b[0] ? 1 : a[0] < b[0] ? -1 : 0; }); } - let lineOffset: number = this.lineOffset; for (let i: number = 0, j: number = this.bar.staff.tuning.length; i < j; i++) { - if (i > 0) { - lineY += lineOffset; - } + const lineY = this.getTabY(i); let lineX: number = 0; for (let line of tabNotes[i]) { - canvas.fillRect(cx + this.x + lineX, lineY | 0, line[0] - lineX, this.scale * BarRendererBase.StaffLineThickness); + canvas.fillRect(cx + this.x + lineX, cy + this.y + lineY | 0, line[0] - lineX, this.scale * BarRendererBase.StaffLineThickness); lineX = line[0] + line[1]; } - canvas.fillRect(cx + this.x + lineX, lineY | 0, this.width - lineX, this.scale * BarRendererBase.StaffLineThickness); + canvas.fillRect(cx + this.x + lineX, cy + this.y + lineY | 0, this.width - lineX, this.scale * BarRendererBase.StaffLineThickness); } + canvas.color = res.mainGlyphColor; this.paintSimileMark(cx, cy, canvas); } @@ -260,10 +273,12 @@ export class TabBarRenderer extends BarRendererBase { private paintBeamHelper(cx: number, cy: number, canvas: ICanvas, h: BeamingHelper): void { canvas.color = h.voice!.index === 0 ? this.resources.mainGlyphColor : this.resources.secondaryGlyphColor; // check if we need to paint simple footer - if (h.beats.length === 1 || this.settings.notation.rhythmMode === TabRhythmMode.ShowWithBeams) { - this.paintFooter(cx, cy, canvas, h); - } else { - this.paintBar(cx, cy, canvas, h); + if (!h.isRestBeamHelper) { + if (h.beats.length === 1 || this.settings.notation.rhythmMode === TabRhythmMode.ShowWithBeams) { + this.paintFooter(cx, cy, canvas, h); + } else { + this.paintBar(cx, cy, canvas, h); + } } } @@ -288,15 +303,8 @@ export class TabBarRenderer extends BarRendererBase { startGlyph.noteNumbers.getNoteY(startGlyph.noteNumbers.minStringNote!, NoteYPosition.Bottom) + this.lineOffset / 2; } - if (h.direction === BeamDirection.Up) { - beatLineX -= startGlyph.width / 2; - } else { - beatLineX += startGlyph.width / 2; - } - canvas.beginPath(); - canvas.moveTo(cx + this.x + beatLineX, y1); - canvas.lineTo(cx + this.x + beatLineX, y2); - canvas.stroke(); + + this.paintBeamingStem(beat, cy + this.y, cx + this.x + beatLineX, y1, y2, canvas); let brokenBarOffset: number = 6 * this.scale; let barSpacing: number = -6 * this.scale; let barSize: number = 3 * this.scale; @@ -329,12 +337,6 @@ export class TabBarRenderer extends BarRendererBase { if (BeamingHelper.isFullBarJoin(beat, h.beats[i + 1], barIndex)) { barStartX = beatLineX; barEndX = h.getBeatLineX(h.beats[i + 1]); - let endGlyph: BeatGlyphBase = this.getOnNotesGlyphForBeat(h.beats[i + 1])!; - if (h.direction === BeamDirection.Up) { - barEndX -= endGlyph.width / 2; - } else { - barEndX += endGlyph.width / 2; - } } else if (i === 0 || !BeamingHelper.isFullBarJoin(h.beats[i - 1], beat, barIndex)) { barStartX = beatLineX; barEndX = barStartX + brokenBarOffset; @@ -373,8 +375,10 @@ export class TabBarRenderer extends BarRendererBase { private paintTupletHelper(cx: number, cy: number, canvas: ICanvas, h: TupletGroup): void { let res: RenderingResources = this.resources; let oldAlign: TextAlign = canvas.textAlign; + let oldBaseLine = canvas.textBaseline; canvas.color = h.voice.index === 0 ? this.resources.mainGlyphColor : this.resources.secondaryGlyphColor; canvas.textAlign = TextAlign.Center; + canvas.textBaseline = TextBaseline.Middle; let s: string; let num: number = h.beats[0].tupletNumerator; let den: number = h.beats[0].tupletDenominator; @@ -413,12 +417,6 @@ export class TabBarRenderer extends BarRendererBase { continue; } let tupletX: number = beamingHelper.getBeatLineX(beat); - let startGlyph: TabBeatGlyph = this.getOnNotesGlyphForBeat(beat) as TabBeatGlyph; - if (beamingHelper.direction === BeamDirection.Up) { - tupletX -= startGlyph.width / 2; - } else { - tupletX += startGlyph.width / 2; - } let tupletY: number = cy + this.y + this.height - this._tupletSize + res.effectFont.size * 0.5; canvas.font = res.effectFont; canvas.fillText(s, cx + this.x + tupletX, tupletY); @@ -433,15 +431,6 @@ export class TabBarRenderer extends BarRendererBase { // Calculate the overall area of the tuplet bracket let startX: number = firstBeamingHelper.getBeatLineX(firstBeat); let endX: number = lastBeamingHelper.getBeatLineX(lastBeat); - let startGlyph: TabBeatGlyph = this.getOnNotesGlyphForBeat(firstBeat) as TabBeatGlyph; - let endGlyph: TabBeatGlyph = this.getOnNotesGlyphForBeat(firstBeat) as TabBeatGlyph; - if (firstBeamingHelper.direction === BeamDirection.Up) { - startX -= startGlyph.width / 2; - endX -= endGlyph.width / 2; - } else { - startX += startGlyph.width / 2; - endX += endGlyph.width / 2; - } // // Calculate how many space the text will need canvas.font = res.effectFont; @@ -471,10 +460,11 @@ export class TabBarRenderer extends BarRendererBase { canvas.stroke(); // // Draw the string - canvas.fillText(s, cx + this.x + middleX, startY); + canvas.fillText(s, cx + this.x + middleX, startY - offset - size); } } canvas.textAlign = oldAlign; + canvas.textBaseline = oldBaseLine; } private static paintSingleBar(canvas: ICanvas, x1: number, y1: number, x2: number, y2: number, size: number): void { @@ -487,6 +477,34 @@ export class TabBarRenderer extends BarRendererBase { canvas.fill(); } + private paintBeamingStem(beat: Beat, cy: number, x: number, topY: number, bottomY: number, canvas: ICanvas) { + canvas.beginPath(); + + let holes: ReservedLayoutAreaSlot[] = []; + if (this.helpers.collisionHelper.reservedLayoutAreasByDisplayTime.has(beat.displayStart)) { + holes = this.helpers.collisionHelper.reservedLayoutAreasByDisplayTime.get(beat.displayStart)!.slots.slice(); + holes.sort((a, b) => a.topY - b.topY); + } + + let y = bottomY; + while (y > topY) { + canvas.moveTo(x, y); + + let lineY = topY; + // draw until next hole + if (holes.length > 0) { + const bottomHole = holes.pop()!; + lineY = cy + bottomHole.bottomY; + canvas.lineTo(x, lineY); + y = cy + bottomHole.topY; + } else { + canvas.lineTo(x, topY); + break; + } + } + canvas.stroke(); + } + private paintFooter(cx: number, cy: number, canvas: ICanvas, h: BeamingHelper): void { for (let beat of h.beats) { if ( @@ -511,15 +529,9 @@ export class TabBarRenderer extends BarRendererBase { y1 += startGlyph.noteNumbers.getNoteY(startGlyph.noteNumbers.minStringNote!, NoteYPosition.Bottom); } - if (h.direction === BeamDirection.Up) { - beatLineX -= startGlyph.width / 2; - } else { - beatLineX += startGlyph.width / 2; - } - canvas.beginPath(); - canvas.moveTo(cx + this.x + beatLineX, y1); - canvas.lineTo(cx + this.x + beatLineX, y2); - canvas.stroke(); + + this.paintBeamingStem(beat, cy + this.y, cx + this.x + beatLineX, y1, y2, canvas); + // // Draw Flag // diff --git a/src/rendering/glyphs/BeatContainerGlyph.ts b/src/rendering/glyphs/BeatContainerGlyph.ts index db283abdf..472f886a7 100644 --- a/src/rendering/glyphs/BeatContainerGlyph.ts +++ b/src/rendering/glyphs/BeatContainerGlyph.ts @@ -96,7 +96,7 @@ export class BeatContainerGlyph extends Glyph { this.minWidth = this.preNotes.width + this.onNotes.width; if (!this.beat.isRest) { if (this.onNotes.beamingHelper.beats.length === 1) { - // make space for footer + // make space for flag if (this.beat.duration >= Duration.Eighth) { this.minWidth += 20 * this.scale; } diff --git a/src/rendering/glyphs/BendNoteHeadGroupGlyph.ts b/src/rendering/glyphs/BendNoteHeadGroupGlyph.ts index 3112f6034..4cade0a10 100644 --- a/src/rendering/glyphs/BendNoteHeadGroupGlyph.ts +++ b/src/rendering/glyphs/BendNoteHeadGroupGlyph.ts @@ -62,7 +62,7 @@ export class BendNoteHeadGroupGlyph extends ScoreNoteChordGlyphBase { true ); let line: number = sr.accidentalHelper.getNoteLineForValue(noteValue, false); - noteHeadGlyph.y = sr.getScoreY(line, 0); + noteHeadGlyph.y = sr.getScoreY(line); if (this._showParenthesis) { this._preNoteParenthesis!.renderer = this.renderer; this._postNoteParenthesis!.renderer = this.renderer; diff --git a/src/rendering/glyphs/GhostNoteContainerGlyph.ts b/src/rendering/glyphs/GhostNoteContainerGlyph.ts index f4d998a20..840577a82 100644 --- a/src/rendering/glyphs/GhostNoteContainerGlyph.ts +++ b/src/rendering/glyphs/GhostNoteContainerGlyph.ts @@ -66,13 +66,13 @@ export class GhostNoteContainerGlyph extends Glyph { } else if (!previousGlyph) { g = new GhostParenthesisGlyph(this._isOpen); g.renderer = this.renderer; - g.y = sr.getScoreY(this._infos[i].line, 0) - sizePerLine; + g.y = sr.getScoreY(this._infos[i].line) - sizePerLine; g.height = sizePerLine * 2; g.doLayout(); this._glyphs.push(g); previousGlyph = g; } else { - let y: number = sr.getScoreY(this._infos[i].line, 0) + sizePerLine; + let y: number = sr.getScoreY(this._infos[i].line) + sizePerLine; previousGlyph.height = y - previousGlyph.y; } } diff --git a/src/rendering/glyphs/ScoreBeatGlyph.ts b/src/rendering/glyphs/ScoreBeatGlyph.ts index 67e568fbf..0d63973ee 100644 --- a/src/rendering/glyphs/ScoreBeatGlyph.ts +++ b/src/rendering/glyphs/ScoreBeatGlyph.ts @@ -24,13 +24,17 @@ import { PercussionNoteHeadGlyph } from './PercussionNoteHeadGlyph'; import { Logger } from '@src/alphatab'; import { ArticStaccatoAboveGlyph } from './ArticStaccatoAboveGlyph'; import { MusicFontSymbol } from '../../model/MusicFontSymbol'; -import { TextBaseline } from '@src/platform/ICanvas'; +import { ICanvas, TextBaseline } from '@src/platform/ICanvas'; import { PictEdgeOfCymbalGlyph } from './PictEdgeOfCymbalGlyph'; import { PickStrokeGlyph } from './PickStrokeGlyph'; import { PickStroke } from '@src/model/PickStroke'; import { GuitarGolpeGlyph } from './GuitarGolpeGlyph'; +import { BeamingHelper } from '../utils/BeamingHelper'; export class ScoreBeatGlyph extends BeatOnNoteGlyphBase { + private _collisionOffset: number = -1000; + private _skipPaint: boolean = false; + public noteHeads: ScoreNoteChordGlyph | null = null; public restGlyph: ScoreRestGlyph | null = null; @@ -53,6 +57,24 @@ export class ScoreBeatGlyph extends BeatOnNoteGlyphBase { this.noteHeads.updateBeamingHelper(this.container.x + this.x); } else if (this.restGlyph) { this.restGlyph.updateBeamingHelper(this.container.x + this.x); + if (this.renderer.bar.isMultiVoice && this._collisionOffset === -1000) { + this._collisionOffset = this.renderer.helpers.collisionHelper.applyRestCollisionOffset(this.container.beat, this.restGlyph.y, + (this.renderer as ScoreBarRenderer).getScoreHeight(1)); + this.y += this._collisionOffset; + const existingRests = this.renderer.helpers.collisionHelper.restDurationsByDisplayTime; + if (existingRests.has(this.container.beat.playbackStart) && + existingRests.get(this.container.beat.playbackStart)!.has(this.container.beat.playbackDuration) && + existingRests.get(this.container.beat.playbackStart)!.get(this.container.beat.playbackDuration) !== this.container.beat.id + ) { + this._skipPaint = true; + } + } + } + } + + public paint(cx: number, cy: number, canvas: ICanvas): void { + if (!this._skipPaint) { + super.paint(cx, cy, canvas); } } @@ -110,7 +132,6 @@ export class ScoreBeatGlyph extends BeatOnNoteGlyphBase { } } } else { - let offset: number = 0; let line = Math.ceil((this.renderer.bar.staff.standardNotationLineCount - 1) / 2) * 2; // this positioning is quite strange, for most staff line counts @@ -124,11 +145,22 @@ export class ScoreBeatGlyph extends BeatOnNoteGlyphBase { line -= 2; } - let y: number = sr.getScoreY(line, offset); - this.restGlyph = new ScoreRestGlyph(0, y, this.container.beat.duration); + this.restGlyph = new ScoreRestGlyph(0, sr.getScoreY(line), this.container.beat.duration); this.restGlyph.beat = this.container.beat; this.restGlyph.beamingHelper = this.beamingHelper; this.addGlyph(this.restGlyph); + + if (this.renderer.bar.isMultiVoice) { + if (this.container.beat.voice.index === 0) { + const restSizes = BeamingHelper.computeLineHeightsForRest(this.container.beat.duration); + let restTop = this.restGlyph.y - sr.getScoreHeight(restSizes[0]); + let restBottom = this.restGlyph.y + sr.getScoreHeight(restSizes[1]); + this.renderer.helpers.collisionHelper.reserveBeatSlot(this.container.beat, restTop, restBottom); + } else { + this.renderer.helpers.collisionHelper.registerRest(this.container.beat); + } + } + if (this.beamingHelper) { this.beamingHelper.applyRest(this.container.beat, line); } @@ -158,7 +190,7 @@ export class ScoreBeatGlyph extends BeatOnNoteGlyphBase { private createBeatDot(line: number, group: GlyphGroup): void { let sr: ScoreBarRenderer = this.renderer as ScoreBarRenderer; - group.addGlyph(new CircleGlyph(0, sr.getScoreY(line, 0), 1.5 * this.scale)); + group.addGlyph(new CircleGlyph(0, sr.getScoreY(line), 1.5 * this.scale)); } private createNoteHeadGlyph(n: Note): EffectGlyph { @@ -194,7 +226,7 @@ export class ScoreBeatGlyph extends BeatOnNoteGlyphBase { let noteHeadGlyph: EffectGlyph = this.createNoteHeadGlyph(n); // calculate y position let line: number = sr.getNoteLine(n); - noteHeadGlyph.y = sr.getScoreY(line, 0); + noteHeadGlyph.y = sr.getScoreY(line); this.noteHeads!.addNoteGlyph(noteHeadGlyph, n, line); if (n.harmonicType !== HarmonicType.None && n.harmonicType !== HarmonicType.Natural) { // create harmonic note head. @@ -206,7 +238,7 @@ export class ScoreBeatGlyph extends BeatOnNoteGlyphBase { this.container.beat.graceType !== GraceType.None ); line = sr.accidentalHelper.getNoteLineForValue(harmonicFret, false); - noteHeadGlyph.y = sr.getScoreY(line, 0); + noteHeadGlyph.y = sr.getScoreY(line); this.noteHeads!.addNoteGlyph(noteHeadGlyph, n, line); } if (n.isStaccato && !this.noteHeads!.aboveBeatEffects.has('Staccato')) { diff --git a/src/rendering/glyphs/ScoreBeatPreNotesGlyph.ts b/src/rendering/glyphs/ScoreBeatPreNotesGlyph.ts index a6a0b3c77..f97b52d1c 100644 --- a/src/rendering/glyphs/ScoreBeatPreNotesGlyph.ts +++ b/src/rendering/glyphs/ScoreBeatPreNotesGlyph.ts @@ -111,7 +111,7 @@ export class ScoreBeatPreNotesGlyph extends BeatGlyphBase { let noteLine: number = sr.getNoteLine(n); let isGrace: boolean = this.container.beat.graceType !== GraceType.None; if (accidental !== AccidentalType.None) { - let g = new AccidentalGlyph(0, sr.getScoreY(noteLine, 0), accidental, isGrace); + let g = new AccidentalGlyph(0, sr.getScoreY(noteLine), accidental, isGrace); g.renderer = this.renderer; accidentals.addGlyph(g); } @@ -119,7 +119,7 @@ export class ScoreBeatPreNotesGlyph extends BeatGlyphBase { let harmonicFret: number = n.displayValue + n.harmonicPitch; accidental = sr.accidentalHelper.applyAccidentalForValue(n.beat, harmonicFret, isGrace, false); noteLine = sr.accidentalHelper.getNoteLineForValue(harmonicFret, false); - let g = new AccidentalGlyph(0, sr.getScoreY(noteLine, 0), accidental, isGrace); + let g = new AccidentalGlyph(0, sr.getScoreY(noteLine), accidental, isGrace); g.renderer = this.renderer; accidentals.addGlyph(g); } diff --git a/src/rendering/glyphs/ScoreBendGlyph.ts b/src/rendering/glyphs/ScoreBendGlyph.ts index 9469a270f..4968feaec 100644 --- a/src/rendering/glyphs/ScoreBendGlyph.ts +++ b/src/rendering/glyphs/ScoreBendGlyph.ts @@ -147,8 +147,7 @@ export class ScoreBendGlyph extends ScoreHelperNotesBaseGlyph { cy + startNoteRenderer.y + startNoteRenderer.getScoreY( - startNoteRenderer.accidentalHelper.getNoteLineForValue(noteValueToDraw, false), - 0 + startNoteRenderer.accidentalHelper.getNoteLineForValue(noteValueToDraw, false) ); if (note.bendType === BendType.Hold || note.bendType === BendType.Prebend) { TieGlyph.paintTie( @@ -221,8 +220,7 @@ export class ScoreBendGlyph extends ScoreHelperNotesBaseGlyph { startNoteRenderer.accidentalHelper.getNoteLineForValue( note.displayValue - ((note.bendPoints[0].value / 2) | 0), false - ), - 0 + ) ) + heightOffset; this.drawBendSlur( @@ -313,8 +311,7 @@ export class ScoreBendGlyph extends ScoreHelperNotesBaseGlyph { startNoteRenderer.accidentalHelper.getNoteLineForValue( note.displayValue - ((note.bendPoints[0].value / 2) | 0), false - ), - 0 + ) ) + heightOffset; this.drawBendSlur( diff --git a/src/rendering/glyphs/ScoreLegatoGlyph.ts b/src/rendering/glyphs/ScoreLegatoGlyph.ts index 726aa334e..19e255670 100644 --- a/src/rendering/glyphs/ScoreLegatoGlyph.ts +++ b/src/rendering/glyphs/ScoreLegatoGlyph.ts @@ -32,7 +32,7 @@ export class ScoreLegatoGlyph extends TieGlyph { protected getStartY(): number { if (this.startBeat!.isRest) { // below all lines - return (this.startNoteRenderer as ScoreBarRenderer).getScoreY(9, 0); + return (this.startNoteRenderer as ScoreBarRenderer).getScoreY(9); } switch (this.tieDirection) { case BeamDirection.Up: @@ -48,9 +48,9 @@ export class ScoreLegatoGlyph extends TieGlyph { if (this.endBeat!.isRest) { switch (this.tieDirection) { case BeamDirection.Up: - return endNoteScoreRenderer.getScoreY(9, 0); + return endNoteScoreRenderer.getScoreY(9); default: - return endNoteScoreRenderer.getScoreY(0, 0); + return endNoteScoreRenderer.getScoreY(0); } } diff --git a/src/rendering/glyphs/ScoreNoteChordGlyphBase.ts b/src/rendering/glyphs/ScoreNoteChordGlyphBase.ts index 6f5f96003..6f00bcf73 100644 --- a/src/rendering/glyphs/ScoreNoteChordGlyphBase.ts +++ b/src/rendering/glyphs/ScoreNoteChordGlyphBase.ts @@ -115,7 +115,7 @@ export abstract class ScoreNoteChordGlyphBase extends Glyph { let l: number = -2; while (l >= this.minNote!.line) { // + 1 Because we want to place the line in the center of the note, not at the top - let lY: number = cy + scoreRenderer.getScoreY(l, 0); + let lY: number = cy + scoreRenderer.getScoreY(l); canvas.fillRect(cx - linePadding + this.noteStartX, lY, lineWidth, this.scale); l -= 2; } @@ -126,7 +126,7 @@ export abstract class ScoreNoteChordGlyphBase extends Glyph { canvas.color = scoreRenderer.resources.staffLineColor; let l: number = 10; while (l <= this.maxNote!.line) { - let lY: number = cy + scoreRenderer.getScoreY(l, 0); + let lY: number = cy + scoreRenderer.getScoreY(l); canvas.fillRect(cx - linePadding + this.noteStartX, lY, lineWidth, this.scale); l += 2; } diff --git a/src/rendering/glyphs/ScoreTieGlyph.ts b/src/rendering/glyphs/ScoreTieGlyph.ts index 8e39cffc8..b9b4ab16e 100644 --- a/src/rendering/glyphs/ScoreTieGlyph.ts +++ b/src/rendering/glyphs/ScoreTieGlyph.ts @@ -37,7 +37,7 @@ export class ScoreTieGlyph extends TieGlyph { protected getStartY(): number { if (this.startBeat!.isRest) { // below all lines - return (this.startNoteRenderer as ScoreBarRenderer).getScoreY(9, 0); + return (this.startNoteRenderer as ScoreBarRenderer).getScoreY(9); } switch (this.tieDirection) { case BeamDirection.Up: @@ -53,9 +53,9 @@ export class ScoreTieGlyph extends TieGlyph { if (this.endBeat!.isRest) { switch (this.tieDirection) { case BeamDirection.Up: - return endNoteScoreRenderer.getScoreY(9, 0); + return endNoteScoreRenderer.getScoreY(9); default: - return endNoteScoreRenderer.getScoreY(0, 0); + return endNoteScoreRenderer.getScoreY(0); } } diff --git a/src/rendering/glyphs/ScoreWhammyBarGlyph.ts b/src/rendering/glyphs/ScoreWhammyBarGlyph.ts index 43a5c2b08..b9cbc0e1e 100644 --- a/src/rendering/glyphs/ScoreWhammyBarGlyph.ts +++ b/src/rendering/glyphs/ScoreWhammyBarGlyph.ts @@ -325,8 +325,7 @@ export class ScoreWhammyBarGlyph extends ScoreHelperNotesBaseGlyph { startNoteRenderer.accidentalHelper.getNoteLineForValue( note.displayValue - ((note.beat.whammyBarPoints[0].value / 2) | 0), false - ), - 0 + ) ) + heightOffset; this.drawBendSlur( diff --git a/src/rendering/glyphs/TabBeatGlyph.ts b/src/rendering/glyphs/TabBeatGlyph.ts index c01d3e37c..e6a535fb4 100644 --- a/src/rendering/glyphs/TabBeatGlyph.ts +++ b/src/rendering/glyphs/TabBeatGlyph.ts @@ -95,46 +95,8 @@ export class TabBeatGlyph extends BeatOnNoteGlyphBase { } } } else { - let line: number = 0; - let offset: number = 0; - switch (this.container.beat.duration) { - case Duration.QuadrupleWhole: - line = 3; - break; - case Duration.DoubleWhole: - line = 3; - break; - case Duration.Whole: - line = 2; - break; - case Duration.Half: - line = 3; - break; - case Duration.Quarter: - line = 3; - break; - case Duration.Eighth: - line = 2; - offset = 5; - break; - case Duration.Sixteenth: - line = 2; - offset = 5; - break; - case Duration.ThirtySecond: - line = 3; - break; - case Duration.SixtyFourth: - line = 3; - break; - case Duration.OneHundredTwentyEighth: - line = 3; - break; - case Duration.TwoHundredFiftySixth: - line = 3; - break; - } - let y: number = tabRenderer.getTabY(line, offset); + let line = Math.floor((this.renderer.bar.staff.tuning.length - 1) / 2) ; + let y: number = tabRenderer.getTabY(line); this.restGlyph = new TabRestGlyph(0, y, tabRenderer.showRests, this.container.beat.duration); this.restGlyph.beat = this.container.beat; this.restGlyph.beamingHelper = this.beamingHelper; @@ -182,10 +144,13 @@ export class TabBeatGlyph extends BeatOnNoteGlyphBase { private createNoteGlyph(n: Note): void { let tr: TabBarRenderer = this.renderer as TabBarRenderer; let noteNumberGlyph: NoteNumberGlyph = new NoteNumberGlyph(0, 0, n); - let l: number = n.beat.voice.bar.staff.tuning.length - n.string + 1; - noteNumberGlyph.y = tr.getTabY(l, -2); + let l: number = n.beat.voice.bar.staff.tuning.length - n.string; + noteNumberGlyph.y = tr.getTabY(l); noteNumberGlyph.renderer = this.renderer; noteNumberGlyph.doLayout(); this.noteNumbers!.addNoteGlyph(noteNumberGlyph, n); + let topY = noteNumberGlyph.y - noteNumberGlyph.height / 2; + let bottomY = topY + noteNumberGlyph.height; + this.renderer.helpers.collisionHelper.reserveBeatSlot(this.container.beat, topY, bottomY); } } diff --git a/src/rendering/glyphs/TabClefGlyph.ts b/src/rendering/glyphs/TabClefGlyph.ts index 9f31390fa..096f9ce7a 100644 --- a/src/rendering/glyphs/TabClefGlyph.ts +++ b/src/rendering/glyphs/TabClefGlyph.ts @@ -13,12 +13,11 @@ export class TabClefGlyph extends Glyph { public paint(cx: number, cy: number, canvas: ICanvas): void { let strings: number = this.renderer.bar.staff.tuning.length; - let correction: number = strings * this.scale * 0.5; let symbol: MusicFontSymbol = strings <= 4 ? MusicFontSymbol.FourStringTabClef : MusicFontSymbol.SixStringTabClef; let scale: number = strings <= 4 ? strings / 4.5 : strings / 6.5; canvas.fillMusicFontSymbol( cx + this.x + 5 * this.scale, - cy + this.y - correction, + cy + this.y, scale * this.scale, symbol, false diff --git a/src/rendering/glyphs/TabNoteChordGlyph.ts b/src/rendering/glyphs/TabNoteChordGlyph.ts index 4e631fa41..df9bce8ad 100644 --- a/src/rendering/glyphs/TabNoteChordGlyph.ts +++ b/src/rendering/glyphs/TabNoteChordGlyph.ts @@ -24,8 +24,8 @@ export class TabNoteChordGlyph extends Glyph { this._isGrace = isGrace; } - public buildBoundingsLookup(beatBounds:BeatBounds, cx:number, cy:number) { - for(const note of this._notes) { + public buildBoundingsLookup(beatBounds: BeatBounds, cx: number, cy: number) { + for (const note of this._notes) { note.buildBoundingsLookup(beatBounds, cx + this.x, cy + this.y); } } @@ -117,6 +117,7 @@ export class TabNoteChordGlyph extends Glyph { let oldBaseLine: TextBaseline = canvas.textBaseline; canvas.textBaseline = TextBaseline.Middle; canvas.font = this._isGrace ? res.graceFont : res.tablatureFont; + let notes: NoteNumberGlyph[] = this._notes; let w: number = this.width; for (let g of notes) { @@ -132,7 +133,10 @@ export class TabNoteChordGlyph extends Glyph { public updateBeamingHelper(cx: number): void { if (this.beamingHelper && this.beamingHelper.isPositionFrom('tab', this.beat)) { - this.beamingHelper.registerBeatLineX('tab', this.beat, cx + this.x + this.width, cx + this.x); + this.beamingHelper.registerBeatLineX('tab', this.beat, + cx + this.x + this.width / 2, + cx + this.x + this.width / 2 + ); } } } diff --git a/src/rendering/glyphs/TabRestGlyph.ts b/src/rendering/glyphs/TabRestGlyph.ts index c950d57d3..6bf2d185d 100644 --- a/src/rendering/glyphs/TabRestGlyph.ts +++ b/src/rendering/glyphs/TabRestGlyph.ts @@ -25,7 +25,11 @@ export class TabRestGlyph extends MusicFontGlyph { public updateBeamingHelper(cx: number): void { if (this.beamingHelper && this.beamingHelper.isPositionFrom('tab', this.beat)) { - this.beamingHelper.registerBeatLineX('tab', this.beat, cx + this.x + this.width, cx + this.x); + this.beamingHelper.registerBeatLineX('tab', + this.beat, + cx + this.x + this.width / 2, + cx + this.x + this.width / 2 + ); } } diff --git a/src/rendering/staves/BarLayoutingInfo.ts b/src/rendering/staves/BarLayoutingInfo.ts index 50ce6c04f..afc9c645b 100644 --- a/src/rendering/staves/BarLayoutingInfo.ts +++ b/src/rendering/staves/BarLayoutingInfo.ts @@ -367,7 +367,7 @@ export class BarLayoutingInfo { } public buildOnTimePositions(force: number): Map { - if(this.totalSpringConstant === -1) { + if (this.totalSpringConstant === -1) { return new Map(); } if (ModelUtils.isAlmostEqualTo(this._onTimePositionsForce, force) && this._onTimePositions) { diff --git a/src/rendering/utils/BarCollisionHelper.ts b/src/rendering/utils/BarCollisionHelper.ts new file mode 100644 index 000000000..d865f6d49 --- /dev/null +++ b/src/rendering/utils/BarCollisionHelper.ts @@ -0,0 +1,150 @@ +import { Beat } from "@src/model/Beat"; +import { BeamingHelper } from "./BeamingHelper"; + +export class ReservedLayoutAreaSlot { + public topY: number = 0; + public bottomY: number = 0; + public constructor(topY: number, bottomY: number) { + this.topY = topY; + this.bottomY = bottomY; + } +} + +export class ReservedLayoutArea { + public beat: Beat; + public topY: number = -1000; + public bottomY: number = -1000; + public slots: ReservedLayoutAreaSlot[] = []; + + public constructor(beat: Beat) { + this.beat = beat; + } + + public addSlot(topY: number, bottomY: number) { + this.slots.push(new ReservedLayoutAreaSlot(topY, bottomY)); + if (this.topY === -1000) { + this.topY = topY; + this.bottomY = bottomY; + } else { + const min = Math.min(topY, bottomY); + const max = Math.max(topY, bottomY); + if (min < this.topY) { + this.topY = min; + } + if (max > this.bottomY) { + this.bottomY = max; + } + } + } +} + +export class BarCollisionHelper { + public reservedLayoutAreasByDisplayTime: Map = new Map(); + public restDurationsByDisplayTime: Map> = new Map(); + + public getBeatMinMaxY(): number[] { + let minY = -1000; + let maxY = -1000; + this.reservedLayoutAreasByDisplayTime.forEach((v, k) => { + if (minY === -1000) { + minY = v.topY; + maxY = v.bottomY; + } else { + if (minY > v.topY) { + minY = v.topY; + } + if (maxY < v.bottomY) { + maxY = v.bottomY; + } + } + }); + + if (minY === -1000) { + return [0, 0]; + } + return [minY, maxY]; + } + + public reserveBeatSlot(beat: Beat, topY: number, bottomY: number): void { + if (topY == bottomY) { + return; + } + if (!this.reservedLayoutAreasByDisplayTime.has(beat.displayStart)) { + this.reservedLayoutAreasByDisplayTime.set(beat.displayStart, new ReservedLayoutArea(beat)); + } + this.reservedLayoutAreasByDisplayTime.get(beat.displayStart)!.addSlot(topY, bottomY); + if (beat.isRest) { + this.registerRest(beat); + } + } + + public registerRest(beat: Beat) { + if (!this.restDurationsByDisplayTime.has(beat.displayStart)) { + this.restDurationsByDisplayTime.set(beat.displayStart, new Map()); + } + if (!this.restDurationsByDisplayTime.get(beat.displayStart)!.has(beat.playbackDuration)) { + this.restDurationsByDisplayTime.get(beat.displayStart)!.set(beat.playbackDuration, beat.id); + } + } + + public applyRestCollisionOffset(beat: Beat, currentY: number, linesToPixel: number): number { + // for the first voice we do not need collision detection on rests + // we just place it normally + if (beat.voice.index > 0) { + // From the Spring-Rod poisitioning we have the guarantee + // that 2 timewise subsequent elements can never collide + // on the horizontal axis. So we only need to check for collisions + // of elements at the current time position + // if there are none, we can just use the line + if (this.reservedLayoutAreasByDisplayTime.has(beat.playbackStart)) { + // do check for collisions we need to obtain the range on which the + // restglyph is placed + // rest glyphs have their ancor + const restSizes = BeamingHelper.computeLineHeightsForRest(beat.duration).map(i => i * linesToPixel); + let oldRestTopY = currentY - restSizes[0]; + let oldRestBottomY = currentY + restSizes[1]; + let newRestTopY = oldRestTopY; + + const reservedSlots = this.reservedLayoutAreasByDisplayTime.get(beat.playbackStart)!; + let hasCollision = false; + for (const slot of reservedSlots.slots) { + if ((oldRestTopY >= slot.topY && oldRestTopY <= slot.bottomY) || + (oldRestBottomY >= slot.topY && oldRestBottomY <= slot.bottomY)) { + hasCollision = true; + break; + } + } + + if (hasCollision) { + // second voice above, the others below + if (beat.voice.index == 1) { + // move rest above top position + // TODO: rest must align with note lines + newRestTopY = reservedSlots.topY - restSizes[1] - restSizes[0]; + } else { + // move rest above top position + // TODO: rest must align with note lines + newRestTopY = reservedSlots.bottomY; + } + + let newRestBottomY = newRestTopY + restSizes[0] + restSizes[1]; + + // moving always happens in full stave spaces + const staveSpace = linesToPixel * 2; + let distanceInLines = Math.ceil(Math.abs(newRestTopY - oldRestTopY) / staveSpace); + + // register new min/max offsets + reservedSlots.addSlot(newRestTopY, newRestBottomY); + + if (newRestTopY < oldRestTopY) { + return distanceInLines * -staveSpace; + } else { + return distanceInLines * staveSpace; + } + } + } + } + + return 0; + } +} \ No newline at end of file diff --git a/src/rendering/utils/BarHelpers.ts b/src/rendering/utils/BarHelpers.ts index 1a30136c4..4042ba2b4 100644 --- a/src/rendering/utils/BarHelpers.ts +++ b/src/rendering/utils/BarHelpers.ts @@ -3,14 +3,17 @@ import { GraceType } from '@src/model/GraceType'; import { Voice } from '@src/model/Voice'; import { BeamingHelper } from '@src/rendering/utils/BeamingHelper'; import { BarRendererBase } from '../BarRendererBase'; +import { BarCollisionHelper } from './BarCollisionHelper'; export class BarHelpers { - private _renderer:BarRendererBase; + private _renderer: BarRendererBase; public beamHelpers: BeamingHelper[][] = []; public beamHelperLookup: Map[] = []; + public collisionHelper: BarCollisionHelper; - public constructor(renderer:BarRendererBase) { + public constructor(renderer: BarRendererBase) { this._renderer = renderer; + this.collisionHelper = new BarCollisionHelper(); } public initialize() { @@ -33,22 +36,20 @@ export class BarHelpers { currentGraceBeamHelper = null; } // if a new beaming helper was started, we close our tuplet grouping as well - if (!b.isRest) { - // try to fit beam to current beamhelper - if (!helperForBeat || !helperForBeat.checkBeat(b)) { - if (helperForBeat) { - helperForBeat.finish(); - } - // if not possible, create the next beaming helper - helperForBeat = new BeamingHelper(bar.staff, barRenderer); - helperForBeat.checkBeat(b); - if (b.graceType !== GraceType.None) { - currentGraceBeamHelper = helperForBeat; - } else { - currentBeamHelper = helperForBeat; - } - this.beamHelpers[v.index].push(helperForBeat); + // try to fit beam to current beamhelper + if (!helperForBeat || !helperForBeat.checkBeat(b)) { + if (helperForBeat) { + helperForBeat.finish(); } + // if not possible, create the next beaming helper + helperForBeat = new BeamingHelper(bar.staff, barRenderer); + helperForBeat.checkBeat(b); + if (b.graceType !== GraceType.None) { + currentGraceBeamHelper = helperForBeat; + } else { + currentBeamHelper = helperForBeat; + } + this.beamHelpers[v.index].push(helperForBeat); } this.beamHelperLookup[v.index].set(b.index, helperForBeat!); } diff --git a/src/rendering/utils/BeamingHelper.ts b/src/rendering/utils/BeamingHelper.ts index 31f470842..5d1e9ab99 100644 --- a/src/rendering/utils/BeamingHelper.ts +++ b/src/rendering/utils/BeamingHelper.ts @@ -20,9 +20,11 @@ class BeatLinePositions { } export class BeamingHelperDrawInfo { + public startBeat: Beat | null = null; public startX: number = 0; public startY: number = 0; + public endBeat: Beat | null = null; public endX: number = 0; public endY: number = 0; @@ -49,6 +51,7 @@ export class BeamingHelper { private _staff: Staff; private _beatLineXPositions: Map = new Map(); private _renderer: BarRendererBase; + private _firstNonRestBeat: Beat | null = null; private _lastNonRestBeat: Beat | null = null; public voice: Voice | null = null; @@ -70,10 +73,10 @@ export class BeamingHelper { private _lastBeatLowestNoteCompareValue: number = -1; private _lastBeatHighestNoteCompareValue: number = -1; - private _lowestNoteInHelper: Note | null = null; + public lowestNoteInHelper: Note | null = null; private _lowestNoteCompareValueInHelper: number = -1; - private _highestNoteInHelper: Note | null = null; + public highestNoteInHelper: Note | null = null; private _highestNoteCompareValueInHelper: number = -1; public invertBeamDirection: boolean = false; @@ -86,6 +89,10 @@ export class BeamingHelper { public maxRestLine: number | null = null; public beatOfMaxRestLine: Beat | null = null; + public get isRestBeamHelper(): boolean { + return this.beats.length === 1 && this.beats[0].isRest; + } + public get hasLine(): boolean { return this.beats.length === 1 && this.beats[0].duration > Duration.Whole; } @@ -93,6 +100,7 @@ export class BeamingHelper { public get hasFlag(): boolean { return ( this.beats.length === 1 && + !this.beats[0].isRest && (this.beats[0].duration > Duration.Quarter || this.beats[0].graceType !== GraceType.None) ); } @@ -122,6 +130,13 @@ export class BeamingHelper { positions.staffId = staffId; positions.up = up; positions.down = down; + this.drawingInfos.forEach((v, _) => { + if (v.startBeat == beat) { + v.startX = this.getBeatLineX(beat); + } else if (v.endBeat == beat) { + v.endX = this.getBeatLineX(beat); + } + }); } private getOrCreateBeatPositions(beat: Beat): BeatLinePositions { @@ -137,38 +152,71 @@ export class BeamingHelper { } private calculateDirection(): BeamDirection { - let preferredBeamDirection = this.preferredBeamDirection; - if (preferredBeamDirection !== null) { - return preferredBeamDirection; - } - + let direction: BeamDirection | null = null; if (!this.voice) { - return BeamDirection.Up; - } - - // multivoice handling - if (this.voice.index > 0) { - return this.invert(BeamDirection.Down); - } - if (this.voice.bar.voices.length > 1) { - for (let v: number = 1; v < this.voice.bar.voices.length; v++) { - if (!this.voice.bar.voices[v].isEmpty) { - return this.invert(BeamDirection.Up); - } - } - } - if (this.beats[0].graceType !== GraceType.None) { - return this.invert(BeamDirection.Up); + // no proper voice (should not happen usually) + direction = BeamDirection.Up; + } else if (this.preferredBeamDirection !== null) { + // we have a preferred direction + direction = this.preferredBeamDirection; + } else if (this.voice.index > 0) { + // on multi-voice setups secondary voices are always down + direction = this.invert(BeamDirection.Down); + } else if (this.voice.bar.isMultiVoice) { + // on multi-voice setups primary voices are always up + direction = this.invert(BeamDirection.Up); + } else if (this.beats[0].graceType !== GraceType.None) { + // grace notes are always up + direction = this.invert(BeamDirection.Up); } // the average line is used for determination // key lowerequal than middle line -> up // key higher than middle line -> down - const highestNotePosition = this._renderer.getNoteY(this._highestNoteInHelper!, NoteYPosition.Center); - const lowestNotePosition = this._renderer.getNoteY(this._lowestNoteInHelper!, NoteYPosition.Center); - const avg = (highestNotePosition + lowestNotePosition) / 2; + if (this.highestNoteInHelper && this.lowestNoteInHelper) { + let highestNotePosition = this._renderer.getNoteY(this.highestNoteInHelper, NoteYPosition.Center); + let lowestNotePosition = this._renderer.getNoteY(this.lowestNoteInHelper, NoteYPosition.Center); + + if (direction === null) { + const avg = (highestNotePosition + lowestNotePosition) / 2; + direction = this.invert(this._renderer.middleYPosition < avg ? BeamDirection.Up : BeamDirection.Down); + } + + this._renderer.completeBeamingHelper(this); + } else { + direction = this.invert(BeamDirection.Up); + this._renderer.completeBeamingHelper(this); + } + + return direction; + } - return this.invert(this._renderer.middleYPosition < avg ? BeamDirection.Up : BeamDirection.Down); + public static computeLineHeightsForRest(duration: Duration): number[] { + switch (duration) { + case Duration.QuadrupleWhole: + return [2, 2]; + case Duration.DoubleWhole: + return [2, 2]; + case Duration.Whole: + return [0, 1]; + case Duration.Half: + return [1, 0]; + case Duration.Quarter: + return [3, 3]; + case Duration.Eighth: + return [2, 2]; + case Duration.Sixteenth: + return [2, 4]; + case Duration.ThirtySecond: + return [4, 4]; + case Duration.SixtyFourth: + return [4, 6]; + case Duration.OneHundredTwentyEighth: + return [6, 6]; + case Duration.TwoHundredFiftySixth: + return [6, 8]; + } + return [0, 0]; } /** @@ -179,7 +227,8 @@ export class BeamingHelper { */ public applyRest(beat: Beat, line: number): void { // do not accept rests after the last beat which has notes - if (this._lastNonRestBeat && beat.index >= this._lastNonRestBeat.index) { + if (this._lastNonRestBeat && beat.index >= this._lastNonRestBeat.index || + this._firstNonRestBeat && beat.index <= this._firstNonRestBeat.index) { return; } @@ -187,52 +236,9 @@ export class BeamingHelper { // be placed at the upper / lower end of the glyph. let aboveRest = line; let belowRest = line; - switch (beat.duration) { - case Duration.QuadrupleWhole: - aboveRest -= 2; - belowRest += 2; - break; - case Duration.DoubleWhole: - aboveRest -= 2; - belowRest -= 2; - break; - case Duration.Whole: - aboveRest += 2; - belowRest += 2; - break; - case Duration.Half: - aboveRest -= 2; - belowRest -= 2; - break; - case Duration.Quarter: - aboveRest -= 4; - belowRest += 2; - break; - case Duration.Eighth: - aboveRest -= 2; - belowRest += 2; - break; - case Duration.Sixteenth: - aboveRest -= 2; - belowRest += 4; - break; - case Duration.ThirtySecond: - aboveRest -= 4; - belowRest += 4; - break; - case Duration.SixtyFourth: - aboveRest -= 4; - belowRest += 6; - break; - case Duration.OneHundredTwentyEighth: - aboveRest -= 6; - belowRest += 6; - break; - case Duration.TwoHundredFiftySixth: - aboveRest -= 6; - belowRest += 8; - break; - } + const offsets = BeamingHelper.computeLineHeightsForRest(beat.duration); + aboveRest -= offsets[0]; + belowRest += offsets[1]; if (this.minRestLine === null || this.minRestLine > aboveRest) { this.minRestLine = aboveRest; this.beatOfMinRestLine = beat; @@ -280,39 +286,49 @@ export class BeamingHelper { break; } } + if (add) { if (beat.preferredBeamDirection !== null) { this.preferredBeamDirection = beat.preferredBeamDirection; } - this.beats.push(beat); - if (beat.graceType !== GraceType.None) { - this.isGrace = true; - } - if (beat.hasTuplet) { - this.hasTuplet = true; - } - let fingeringCount: number = 0; - for (let n: number = 0; n < beat.notes.length; n++) { - let note: Note = beat.notes[n]; - if (note.leftHandFinger !== Fingers.Unknown || note.rightHandFinger !== Fingers.Unknown) { - fingeringCount++; + if (!beat.isRest) { + if (this.isRestBeamHelper) { + this.beats = []; } - } - if (fingeringCount > this.fingeringCount) { - this.fingeringCount = fingeringCount; - } - this.checkNote(beat.minNote); - this.checkNote(beat.maxNote); - if (this.shortestDuration < beat.duration) { - this.shortestDuration = beat.duration; + this.beats.push(beat); + + if (beat.graceType !== GraceType.None) { + this.isGrace = true; + } + if (beat.hasTuplet) { + this.hasTuplet = true; + } + let fingeringCount: number = 0; + for (let n: number = 0; n < beat.notes.length; n++) { + let note: Note = beat.notes[n]; + if (note.leftHandFinger !== Fingers.Unknown || note.rightHandFinger !== Fingers.Unknown) { + fingeringCount++; + } + } + if (fingeringCount > this.fingeringCount) { + this.fingeringCount = fingeringCount; + } + this.checkNote(beat.minNote); + this.checkNote(beat.maxNote); + if (this.shortestDuration < beat.duration) { + this.shortestDuration = beat.duration; + } + if(!this._firstNonRestBeat) { + this._firstNonRestBeat = beat; + } + this._lastNonRestBeat = beat; + } else if (this.beats.length === 0) { + this.beats.push(beat); } if (beat.hasTuplet) { this.hasTuplet = true; } - if (!beat.isRest) { - this._lastNonRestBeat = beat; - } } return add; } @@ -359,12 +375,12 @@ export class BeamingHelper { this._lastBeatHighestNoteCompareValue = highestValueForNote; } - if (!this._lowestNoteInHelper || lowestValueForNote < this._lowestNoteCompareValueInHelper) { - this._lowestNoteInHelper = note; + if (!this.lowestNoteInHelper || lowestValueForNote < this._lowestNoteCompareValueInHelper) { + this.lowestNoteInHelper = note; this._lowestNoteCompareValueInHelper = lowestValueForNote; } - if (!this._highestNoteInHelper || highestValueForNote > this._highestNoteCompareValueInHelper) { - this._highestNoteInHelper = note; + if (!this.highestNoteInHelper || highestValueForNote > this._highestNoteCompareValueInHelper) { + this.highestNoteInHelper = note; this._highestNoteCompareValueInHelper = highestValueForNote; } } @@ -376,8 +392,6 @@ export class BeamingHelper { if ( !b1 || !b2 || - b1.isRest || - b2.isRest || b1.graceType !== b2.graceType || b1.graceType === GraceType.BendGrace || b2.graceType === GraceType.BendGrace @@ -443,11 +457,11 @@ export class BeamingHelper { } public get beatOfLowestNote(): Beat { - return this._lowestNoteInHelper!.beat; + return this.lowestNoteInHelper!.beat; } public get beatOfHighestNote(): Beat { - return this._highestNoteInHelper!.beat; + return this.highestNoteInHelper!.beat; } /** diff --git a/test-data/visual-tests/effects-and-annotations/bends.png b/test-data/visual-tests/effects-and-annotations/bends.png index 8fcdb9441..e54dfbbe4 100644 Binary files a/test-data/visual-tests/effects-and-annotations/bends.png and b/test-data/visual-tests/effects-and-annotations/bends.png differ diff --git a/test-data/visual-tests/effects-and-annotations/brush.png b/test-data/visual-tests/effects-and-annotations/brush.png index d85016854..33a7736ae 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/effects-and-annotations/chords.png b/test-data/visual-tests/effects-and-annotations/chords.png index aac8f5447..893762caf 100644 Binary files a/test-data/visual-tests/effects-and-annotations/chords.png and b/test-data/visual-tests/effects-and-annotations/chords.png differ diff --git a/test-data/visual-tests/effects-and-annotations/dynamics.png b/test-data/visual-tests/effects-and-annotations/dynamics.png index f3ed736fc..8c7dae180 100644 Binary files a/test-data/visual-tests/effects-and-annotations/dynamics.png and b/test-data/visual-tests/effects-and-annotations/dynamics.png differ diff --git a/test-data/visual-tests/effects-and-annotations/fade-in.png b/test-data/visual-tests/effects-and-annotations/fade-in.png index 23530fa39..a98fa6245 100644 Binary files a/test-data/visual-tests/effects-and-annotations/fade-in.png and b/test-data/visual-tests/effects-and-annotations/fade-in.png differ diff --git a/test-data/visual-tests/effects-and-annotations/fingering.png b/test-data/visual-tests/effects-and-annotations/fingering.png index c76e1d555..74fcbeb59 100644 Binary files a/test-data/visual-tests/effects-and-annotations/fingering.png and b/test-data/visual-tests/effects-and-annotations/fingering.png differ diff --git a/test-data/visual-tests/effects-and-annotations/let-ring.png b/test-data/visual-tests/effects-and-annotations/let-ring.png index 1c402599c..4891ccea1 100644 Binary files a/test-data/visual-tests/effects-and-annotations/let-ring.png and b/test-data/visual-tests/effects-and-annotations/let-ring.png differ diff --git a/test-data/visual-tests/effects-and-annotations/markers.png b/test-data/visual-tests/effects-and-annotations/markers.png index f4031df3d..48793aea5 100644 Binary files a/test-data/visual-tests/effects-and-annotations/markers.png and b/test-data/visual-tests/effects-and-annotations/markers.png differ diff --git a/test-data/visual-tests/effects-and-annotations/palm-mute.png b/test-data/visual-tests/effects-and-annotations/palm-mute.png index 80d576975..048e8899b 100644 Binary files a/test-data/visual-tests/effects-and-annotations/palm-mute.png and b/test-data/visual-tests/effects-and-annotations/palm-mute.png differ diff --git a/test-data/visual-tests/effects-and-annotations/pick-stroke.png b/test-data/visual-tests/effects-and-annotations/pick-stroke.png index 823bf3281..c0873d9c5 100644 Binary files a/test-data/visual-tests/effects-and-annotations/pick-stroke.png and b/test-data/visual-tests/effects-and-annotations/pick-stroke.png differ diff --git a/test-data/visual-tests/effects-and-annotations/slides.png b/test-data/visual-tests/effects-and-annotations/slides.png index 203d13d4b..fa4e7bee3 100644 Binary files a/test-data/visual-tests/effects-and-annotations/slides.png and b/test-data/visual-tests/effects-and-annotations/slides.png differ diff --git a/test-data/visual-tests/effects-and-annotations/tempo.png b/test-data/visual-tests/effects-and-annotations/tempo.png index e75bd453d..d1dfa1407 100644 Binary files a/test-data/visual-tests/effects-and-annotations/tempo.png and b/test-data/visual-tests/effects-and-annotations/tempo.png differ diff --git a/test-data/visual-tests/effects-and-annotations/text.png b/test-data/visual-tests/effects-and-annotations/text.png index 4295e406c..93c02a140 100644 Binary files a/test-data/visual-tests/effects-and-annotations/text.png and b/test-data/visual-tests/effects-and-annotations/text.png differ diff --git a/test-data/visual-tests/effects-and-annotations/tremolo-bar.png b/test-data/visual-tests/effects-and-annotations/tremolo-bar.png index fbba39c68..25c54e04f 100644 Binary files a/test-data/visual-tests/effects-and-annotations/tremolo-bar.png and b/test-data/visual-tests/effects-and-annotations/tremolo-bar.png differ diff --git a/test-data/visual-tests/effects-and-annotations/tremolo-picking.png b/test-data/visual-tests/effects-and-annotations/tremolo-picking.png index 12f382619..efbe3d7b8 100644 Binary files a/test-data/visual-tests/effects-and-annotations/tremolo-picking.png and b/test-data/visual-tests/effects-and-annotations/tremolo-picking.png differ diff --git a/test-data/visual-tests/effects-and-annotations/trill.png b/test-data/visual-tests/effects-and-annotations/trill.png index 426c5814c..ba729fa00 100644 Binary files a/test-data/visual-tests/effects-and-annotations/trill.png and b/test-data/visual-tests/effects-and-annotations/trill.png differ diff --git a/test-data/visual-tests/effects-and-annotations/triplet-feel.png b/test-data/visual-tests/effects-and-annotations/triplet-feel.png index 30aae89b2..60e97ced2 100644 Binary files a/test-data/visual-tests/effects-and-annotations/triplet-feel.png and b/test-data/visual-tests/effects-and-annotations/triplet-feel.png differ diff --git a/test-data/visual-tests/effects-and-annotations/tuplets-advanced.png b/test-data/visual-tests/effects-and-annotations/tuplets-advanced.png index 31212481f..f05335bd8 100644 Binary files a/test-data/visual-tests/effects-and-annotations/tuplets-advanced.png and b/test-data/visual-tests/effects-and-annotations/tuplets-advanced.png differ diff --git a/test-data/visual-tests/effects-and-annotations/tuplets.gp b/test-data/visual-tests/effects-and-annotations/tuplets.gp index 28682a495..69850c8f1 100644 Binary files a/test-data/visual-tests/effects-and-annotations/tuplets.gp and b/test-data/visual-tests/effects-and-annotations/tuplets.gp differ diff --git a/test-data/visual-tests/effects-and-annotations/tuplets.png b/test-data/visual-tests/effects-and-annotations/tuplets.png index e2e480d97..d656ae064 100644 Binary files a/test-data/visual-tests/effects-and-annotations/tuplets.png and b/test-data/visual-tests/effects-and-annotations/tuplets.png differ diff --git a/test-data/visual-tests/effects-and-annotations/vibrato.png b/test-data/visual-tests/effects-and-annotations/vibrato.png index 4b72e02cd..3320e46a9 100644 Binary files a/test-data/visual-tests/effects-and-annotations/vibrato.png and b/test-data/visual-tests/effects-and-annotations/vibrato.png differ diff --git a/test-data/visual-tests/general/alternate-endings.png b/test-data/visual-tests/general/alternate-endings.png index 01250ebb2..ece1fd8aa 100644 Binary files a/test-data/visual-tests/general/alternate-endings.png and b/test-data/visual-tests/general/alternate-endings.png differ diff --git a/test-data/visual-tests/general/repeats.png b/test-data/visual-tests/general/repeats.png index 8d6b89d57..9fb03fbea 100644 Binary files a/test-data/visual-tests/general/repeats.png and b/test-data/visual-tests/general/repeats.png differ diff --git a/test-data/visual-tests/general/song-details.png b/test-data/visual-tests/general/song-details.png index f5c47b7aa..eeee81b44 100644 Binary files a/test-data/visual-tests/general/song-details.png and b/test-data/visual-tests/general/song-details.png differ diff --git a/test-data/visual-tests/general/tuning.png b/test-data/visual-tests/general/tuning.png index 406739134..57fc4650f 100644 Binary files a/test-data/visual-tests/general/tuning.png and b/test-data/visual-tests/general/tuning.png differ diff --git a/test-data/visual-tests/guitar-tabs/multivoice-rhythm.gp b/test-data/visual-tests/guitar-tabs/multivoice-rhythm.gp new file mode 100644 index 000000000..7e49deb68 Binary files /dev/null and b/test-data/visual-tests/guitar-tabs/multivoice-rhythm.gp differ diff --git a/test-data/visual-tests/guitar-tabs/rhythm-with-beams.png b/test-data/visual-tests/guitar-tabs/rhythm-with-beams.png index a5d2da4eb..faef258c7 100644 Binary files a/test-data/visual-tests/guitar-tabs/rhythm-with-beams.png and b/test-data/visual-tests/guitar-tabs/rhythm-with-beams.png differ diff --git a/test-data/visual-tests/guitar-tabs/rhythm.png b/test-data/visual-tests/guitar-tabs/rhythm.png index 665e4c3d0..6d827ecb7 100644 Binary files a/test-data/visual-tests/guitar-tabs/rhythm.png and b/test-data/visual-tests/guitar-tabs/rhythm.png differ diff --git a/test-data/visual-tests/guitar-tabs/string-variations.png b/test-data/visual-tests/guitar-tabs/string-variations.png index c5247fa6f..1da344240 100644 Binary files a/test-data/visual-tests/guitar-tabs/string-variations.png and b/test-data/visual-tests/guitar-tabs/string-variations.png differ diff --git a/test-data/visual-tests/layout/horizontal-layout-5to8.png b/test-data/visual-tests/layout/horizontal-layout-5to8.png index 8f652c067..608dceabe 100644 Binary files a/test-data/visual-tests/layout/horizontal-layout-5to8.png and b/test-data/visual-tests/layout/horizontal-layout-5to8.png differ diff --git a/test-data/visual-tests/layout/horizontal-layout.png b/test-data/visual-tests/layout/horizontal-layout.png index dbcbb681f..e7ec0fd08 100644 Binary files a/test-data/visual-tests/layout/horizontal-layout.png and b/test-data/visual-tests/layout/horizontal-layout.png differ diff --git a/test-data/visual-tests/layout/multi-track.png b/test-data/visual-tests/layout/multi-track.png index 61a3d7e2d..d402bc1c8 100644 Binary files a/test-data/visual-tests/layout/multi-track.png and b/test-data/visual-tests/layout/multi-track.png differ diff --git a/test-data/visual-tests/layout/multi-voice.png b/test-data/visual-tests/layout/multi-voice.png index 46f6709b0..1fb421836 100644 Binary files a/test-data/visual-tests/layout/multi-voice.png and b/test-data/visual-tests/layout/multi-voice.png differ diff --git a/test-data/visual-tests/layout/page-layout-5barsperrow.png b/test-data/visual-tests/layout/page-layout-5barsperrow.png index 051c20e85..f0161248c 100644 Binary files a/test-data/visual-tests/layout/page-layout-5barsperrow.png and b/test-data/visual-tests/layout/page-layout-5barsperrow.png differ diff --git a/test-data/visual-tests/layout/page-layout-5to8.png b/test-data/visual-tests/layout/page-layout-5to8.png index 41376ce75..5062046b1 100644 Binary files a/test-data/visual-tests/layout/page-layout-5to8.png and b/test-data/visual-tests/layout/page-layout-5to8.png differ diff --git a/test-data/visual-tests/layout/page-layout.png b/test-data/visual-tests/layout/page-layout.png index 29aade2b4..f4216f59f 100644 Binary files a/test-data/visual-tests/layout/page-layout.png and b/test-data/visual-tests/layout/page-layout.png differ diff --git a/test-data/visual-tests/music-notation/beams-advanced.png b/test-data/visual-tests/music-notation/beams-advanced.png index 674e0fb8b..aab1ad07b 100644 Binary files a/test-data/visual-tests/music-notation/beams-advanced.png and b/test-data/visual-tests/music-notation/beams-advanced.png differ diff --git a/test-data/visual-tests/music-notation/notes-rests-beams.gp b/test-data/visual-tests/music-notation/notes-rests-beams.gp index bd81310d3..50007ecaf 100644 Binary files a/test-data/visual-tests/music-notation/notes-rests-beams.gp and b/test-data/visual-tests/music-notation/notes-rests-beams.gp differ diff --git a/test-data/visual-tests/music-notation/notes-rests-beams.png b/test-data/visual-tests/music-notation/notes-rests-beams.png index 96546fcb0..082aea766 100644 Binary files a/test-data/visual-tests/music-notation/notes-rests-beams.png and b/test-data/visual-tests/music-notation/notes-rests-beams.png differ diff --git a/test-data/visual-tests/music-notation/rest-collisions.gp b/test-data/visual-tests/music-notation/rest-collisions.gp new file mode 100644 index 000000000..0fba44b3c Binary files /dev/null and b/test-data/visual-tests/music-notation/rest-collisions.gp differ diff --git a/test-data/visual-tests/music-notation/rest-collisions.png b/test-data/visual-tests/music-notation/rest-collisions.png new file mode 100644 index 000000000..e93f20e72 Binary files /dev/null and b/test-data/visual-tests/music-notation/rest-collisions.png differ diff --git a/test-data/visual-tests/notation-elements/chord-diagrams-off.png b/test-data/visual-tests/notation-elements/chord-diagrams-off.png index 83cf22619..819f9698a 100644 Binary files a/test-data/visual-tests/notation-elements/chord-diagrams-off.png and b/test-data/visual-tests/notation-elements/chord-diagrams-off.png differ diff --git a/test-data/visual-tests/notation-elements/chord-diagrams-on.png b/test-data/visual-tests/notation-elements/chord-diagrams-on.png index 2c3f34b23..04a42f74d 100644 Binary files a/test-data/visual-tests/notation-elements/chord-diagrams-on.png and b/test-data/visual-tests/notation-elements/chord-diagrams-on.png differ diff --git a/test-data/visual-tests/notation-elements/effects-off.png b/test-data/visual-tests/notation-elements/effects-off.png index 3a8277809..7e7449439 100644 Binary files a/test-data/visual-tests/notation-elements/effects-off.png and b/test-data/visual-tests/notation-elements/effects-off.png differ diff --git a/test-data/visual-tests/notation-elements/effects-on.png b/test-data/visual-tests/notation-elements/effects-on.png index 555a06a29..a7fd48cb0 100644 Binary files a/test-data/visual-tests/notation-elements/effects-on.png and b/test-data/visual-tests/notation-elements/effects-on.png differ diff --git a/test-data/visual-tests/notation-elements/guitar-tuning-off.png b/test-data/visual-tests/notation-elements/guitar-tuning-off.png index bf322d830..9384b4a17 100644 Binary files a/test-data/visual-tests/notation-elements/guitar-tuning-off.png and b/test-data/visual-tests/notation-elements/guitar-tuning-off.png differ diff --git a/test-data/visual-tests/notation-elements/guitar-tuning-on.png b/test-data/visual-tests/notation-elements/guitar-tuning-on.png index 08aea68f8..cc50eed8c 100644 Binary files a/test-data/visual-tests/notation-elements/guitar-tuning-on.png and b/test-data/visual-tests/notation-elements/guitar-tuning-on.png differ diff --git a/test-data/visual-tests/notation-elements/parenthesis-on-tied-bends-off.png b/test-data/visual-tests/notation-elements/parenthesis-on-tied-bends-off.png index 75128b31a..abb26bf0a 100644 Binary files a/test-data/visual-tests/notation-elements/parenthesis-on-tied-bends-off.png and b/test-data/visual-tests/notation-elements/parenthesis-on-tied-bends-off.png differ diff --git a/test-data/visual-tests/notation-elements/parenthesis-on-tied-bends-on.png b/test-data/visual-tests/notation-elements/parenthesis-on-tied-bends-on.png index 866ee605e..1b260d84e 100644 Binary files a/test-data/visual-tests/notation-elements/parenthesis-on-tied-bends-on.png and b/test-data/visual-tests/notation-elements/parenthesis-on-tied-bends-on.png differ diff --git a/test-data/visual-tests/notation-elements/score-info-album.png b/test-data/visual-tests/notation-elements/score-info-album.png index 1bf049a3e..888343a2c 100644 Binary files a/test-data/visual-tests/notation-elements/score-info-album.png and b/test-data/visual-tests/notation-elements/score-info-album.png differ diff --git a/test-data/visual-tests/notation-elements/score-info-all.png b/test-data/visual-tests/notation-elements/score-info-all.png index b2abe4b91..521b58c4b 100644 Binary files a/test-data/visual-tests/notation-elements/score-info-all.png and b/test-data/visual-tests/notation-elements/score-info-all.png differ diff --git a/test-data/visual-tests/notation-elements/score-info-artist.png b/test-data/visual-tests/notation-elements/score-info-artist.png index ad2d6afcb..760c6edc5 100644 Binary files a/test-data/visual-tests/notation-elements/score-info-artist.png and b/test-data/visual-tests/notation-elements/score-info-artist.png differ diff --git a/test-data/visual-tests/notation-elements/score-info-copyright.png b/test-data/visual-tests/notation-elements/score-info-copyright.png index 9de1b1cf0..f9cb2e441 100644 Binary files a/test-data/visual-tests/notation-elements/score-info-copyright.png and b/test-data/visual-tests/notation-elements/score-info-copyright.png differ diff --git a/test-data/visual-tests/notation-elements/score-info-music.png b/test-data/visual-tests/notation-elements/score-info-music.png index 2240a4b62..602b28c4c 100644 Binary files a/test-data/visual-tests/notation-elements/score-info-music.png and b/test-data/visual-tests/notation-elements/score-info-music.png differ diff --git a/test-data/visual-tests/notation-elements/score-info-subtitle.png b/test-data/visual-tests/notation-elements/score-info-subtitle.png index 632b1102d..16ddc419d 100644 Binary files a/test-data/visual-tests/notation-elements/score-info-subtitle.png and b/test-data/visual-tests/notation-elements/score-info-subtitle.png differ diff --git a/test-data/visual-tests/notation-elements/score-info-title.png b/test-data/visual-tests/notation-elements/score-info-title.png index 5310c2945..f1f99e330 100644 Binary files a/test-data/visual-tests/notation-elements/score-info-title.png and b/test-data/visual-tests/notation-elements/score-info-title.png differ diff --git a/test-data/visual-tests/notation-elements/score-info-words-and-music.png b/test-data/visual-tests/notation-elements/score-info-words-and-music.png index 40f1d9bd7..5249f5d9f 100644 Binary files a/test-data/visual-tests/notation-elements/score-info-words-and-music.png and b/test-data/visual-tests/notation-elements/score-info-words-and-music.png differ diff --git a/test-data/visual-tests/notation-elements/score-info-words.png b/test-data/visual-tests/notation-elements/score-info-words.png index 4ef6fd27e..6b8d43b81 100644 Binary files a/test-data/visual-tests/notation-elements/score-info-words.png and b/test-data/visual-tests/notation-elements/score-info-words.png differ diff --git a/test-data/visual-tests/notation-elements/tab-notes-on-tied-bends-off.png b/test-data/visual-tests/notation-elements/tab-notes-on-tied-bends-off.png index 41b41da20..8e9b5b2ac 100644 Binary files a/test-data/visual-tests/notation-elements/tab-notes-on-tied-bends-off.png and b/test-data/visual-tests/notation-elements/tab-notes-on-tied-bends-off.png differ diff --git a/test-data/visual-tests/notation-elements/tab-notes-on-tied-bends-on.png b/test-data/visual-tests/notation-elements/tab-notes-on-tied-bends-on.png index 4a99c8e6e..83f5e33c6 100644 Binary files a/test-data/visual-tests/notation-elements/tab-notes-on-tied-bends-on.png and b/test-data/visual-tests/notation-elements/tab-notes-on-tied-bends-on.png differ diff --git a/test-data/visual-tests/notation-elements/track-names-off.png b/test-data/visual-tests/notation-elements/track-names-off.png index 33e10d249..269fc5326 100644 Binary files a/test-data/visual-tests/notation-elements/track-names-off.png and b/test-data/visual-tests/notation-elements/track-names-off.png differ diff --git a/test-data/visual-tests/notation-elements/track-names-on.png b/test-data/visual-tests/notation-elements/track-names-on.png index aaca0d58b..388c70a92 100644 Binary files a/test-data/visual-tests/notation-elements/track-names-on.png and b/test-data/visual-tests/notation-elements/track-names-on.png differ diff --git a/test-data/visual-tests/notation-elements/zeros-on-dive-whammys-off.png b/test-data/visual-tests/notation-elements/zeros-on-dive-whammys-off.png index 218f605ef..059608ff3 100644 Binary files a/test-data/visual-tests/notation-elements/zeros-on-dive-whammys-off.png and b/test-data/visual-tests/notation-elements/zeros-on-dive-whammys-off.png differ diff --git a/test-data/visual-tests/notation-elements/zeros-on-dive-whammys-on.png b/test-data/visual-tests/notation-elements/zeros-on-dive-whammys-on.png index 47c1c1790..75dde14a6 100644 Binary files a/test-data/visual-tests/notation-elements/zeros-on-dive-whammys-on.png and b/test-data/visual-tests/notation-elements/zeros-on-dive-whammys-on.png differ diff --git a/test-data/visual-tests/notation-legend/accentuations-default.png b/test-data/visual-tests/notation-legend/accentuations-default.png index 9a05b1765..d5c9ab56a 100644 Binary files a/test-data/visual-tests/notation-legend/accentuations-default.png and b/test-data/visual-tests/notation-legend/accentuations-default.png differ diff --git a/test-data/visual-tests/notation-legend/accentuations-songbook.png b/test-data/visual-tests/notation-legend/accentuations-songbook.png index 0e2d10ec7..24d4aafcf 100644 Binary files a/test-data/visual-tests/notation-legend/accentuations-songbook.png and b/test-data/visual-tests/notation-legend/accentuations-songbook.png differ diff --git a/test-data/visual-tests/notation-legend/arpeggio-default.png b/test-data/visual-tests/notation-legend/arpeggio-default.png index 8f7a95de5..f149ae8e9 100644 Binary files a/test-data/visual-tests/notation-legend/arpeggio-default.png and b/test-data/visual-tests/notation-legend/arpeggio-default.png differ diff --git a/test-data/visual-tests/notation-legend/arpeggio-songbook.png b/test-data/visual-tests/notation-legend/arpeggio-songbook.png index 8f7a95de5..f149ae8e9 100644 Binary files a/test-data/visual-tests/notation-legend/arpeggio-songbook.png and b/test-data/visual-tests/notation-legend/arpeggio-songbook.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 ca71d1481..7437db481 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 14d1b7986..3e313af9c 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/chords-default.png b/test-data/visual-tests/notation-legend/chords-default.png index c89633196..6bfd5fe23 100644 Binary files a/test-data/visual-tests/notation-legend/chords-default.png and b/test-data/visual-tests/notation-legend/chords-default.png differ diff --git a/test-data/visual-tests/notation-legend/chords-songbook.png b/test-data/visual-tests/notation-legend/chords-songbook.png index c89633196..6bfd5fe23 100644 Binary files a/test-data/visual-tests/notation-legend/chords-songbook.png and b/test-data/visual-tests/notation-legend/chords-songbook.png differ diff --git a/test-data/visual-tests/notation-legend/crescendo-default.png b/test-data/visual-tests/notation-legend/crescendo-default.png index 0a0cde59e..0613f28c8 100644 Binary files a/test-data/visual-tests/notation-legend/crescendo-default.png and b/test-data/visual-tests/notation-legend/crescendo-default.png differ diff --git a/test-data/visual-tests/notation-legend/crescendo-songbook.png b/test-data/visual-tests/notation-legend/crescendo-songbook.png index 0a0cde59e..0613f28c8 100644 Binary files a/test-data/visual-tests/notation-legend/crescendo-songbook.png and b/test-data/visual-tests/notation-legend/crescendo-songbook.png differ diff --git a/test-data/visual-tests/notation-legend/dead-default.png b/test-data/visual-tests/notation-legend/dead-default.png index c10da03a0..b089aa7f1 100644 Binary files a/test-data/visual-tests/notation-legend/dead-default.png and b/test-data/visual-tests/notation-legend/dead-default.png differ diff --git a/test-data/visual-tests/notation-legend/dead-songbook.png b/test-data/visual-tests/notation-legend/dead-songbook.png index c10da03a0..b089aa7f1 100644 Binary files a/test-data/visual-tests/notation-legend/dead-songbook.png and b/test-data/visual-tests/notation-legend/dead-songbook.png differ diff --git a/test-data/visual-tests/notation-legend/dynamics-default.png b/test-data/visual-tests/notation-legend/dynamics-default.png index e6dcdc65f..229069642 100644 Binary files a/test-data/visual-tests/notation-legend/dynamics-default.png and b/test-data/visual-tests/notation-legend/dynamics-default.png differ diff --git a/test-data/visual-tests/notation-legend/dynamics-songbook.png b/test-data/visual-tests/notation-legend/dynamics-songbook.png index e6dcdc65f..229069642 100644 Binary files a/test-data/visual-tests/notation-legend/dynamics-songbook.png and b/test-data/visual-tests/notation-legend/dynamics-songbook.png differ diff --git a/test-data/visual-tests/notation-legend/fingering-default.png b/test-data/visual-tests/notation-legend/fingering-default.png index 789748a09..d50d7acd5 100644 Binary files a/test-data/visual-tests/notation-legend/fingering-default.png and b/test-data/visual-tests/notation-legend/fingering-default.png differ diff --git a/test-data/visual-tests/notation-legend/fingering-songbook.png b/test-data/visual-tests/notation-legend/fingering-songbook.png index edea9785b..6513e4cde 100644 Binary files a/test-data/visual-tests/notation-legend/fingering-songbook.png and b/test-data/visual-tests/notation-legend/fingering-songbook.png differ diff --git a/test-data/visual-tests/notation-legend/full-default.png b/test-data/visual-tests/notation-legend/full-default.png index 3f687e32c..fc6d7605c 100644 Binary files a/test-data/visual-tests/notation-legend/full-default.png and b/test-data/visual-tests/notation-legend/full-default.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 caa57694f..b301244bc 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 ab235629c..4cfbc09e4 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 58469777c..c12e0c013 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/hammer-default.png b/test-data/visual-tests/notation-legend/hammer-default.png index 583e1ebfb..2d735918d 100644 Binary files a/test-data/visual-tests/notation-legend/hammer-default.png and b/test-data/visual-tests/notation-legend/hammer-default.png differ diff --git a/test-data/visual-tests/notation-legend/hammer-songbook.png b/test-data/visual-tests/notation-legend/hammer-songbook.png index 583e1ebfb..2d735918d 100644 Binary files a/test-data/visual-tests/notation-legend/hammer-songbook.png and b/test-data/visual-tests/notation-legend/hammer-songbook.png differ diff --git a/test-data/visual-tests/notation-legend/harmonics-default.png b/test-data/visual-tests/notation-legend/harmonics-default.png index 533ba57b5..a4770a083 100644 Binary files a/test-data/visual-tests/notation-legend/harmonics-default.png and b/test-data/visual-tests/notation-legend/harmonics-default.png differ diff --git a/test-data/visual-tests/notation-legend/harmonics-songbook.png b/test-data/visual-tests/notation-legend/harmonics-songbook.png index 533ba57b5..a4770a083 100644 Binary files a/test-data/visual-tests/notation-legend/harmonics-songbook.png and b/test-data/visual-tests/notation-legend/harmonics-songbook.png differ diff --git a/test-data/visual-tests/notation-legend/let-ring-default.png b/test-data/visual-tests/notation-legend/let-ring-default.png index 5d9159d77..0aa915292 100644 Binary files a/test-data/visual-tests/notation-legend/let-ring-default.png and b/test-data/visual-tests/notation-legend/let-ring-default.png differ diff --git a/test-data/visual-tests/notation-legend/let-ring-songbook.png b/test-data/visual-tests/notation-legend/let-ring-songbook.png index 853536238..01ae57353 100644 Binary files a/test-data/visual-tests/notation-legend/let-ring-songbook.png and b/test-data/visual-tests/notation-legend/let-ring-songbook.png differ diff --git a/test-data/visual-tests/notation-legend/mixed-default.png b/test-data/visual-tests/notation-legend/mixed-default.png index f6a5fadd9..7a6279591 100644 Binary files a/test-data/visual-tests/notation-legend/mixed-default.png and b/test-data/visual-tests/notation-legend/mixed-default.png differ diff --git a/test-data/visual-tests/notation-legend/mixed-songbook.png b/test-data/visual-tests/notation-legend/mixed-songbook.png index 392e62153..e9d0b433b 100644 Binary files a/test-data/visual-tests/notation-legend/mixed-songbook.png and b/test-data/visual-tests/notation-legend/mixed-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 22ddc0213..52052e4c6 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 5b58bd1ac..18aa8974f 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/notation-legend/multi-voice-default.png b/test-data/visual-tests/notation-legend/multi-voice-default.png index 8e9c05549..1988b4f29 100644 Binary files a/test-data/visual-tests/notation-legend/multi-voice-default.png and b/test-data/visual-tests/notation-legend/multi-voice-default.png differ diff --git a/test-data/visual-tests/notation-legend/multi-voice-songbook.png b/test-data/visual-tests/notation-legend/multi-voice-songbook.png index 8e9c05549..1988b4f29 100644 Binary files a/test-data/visual-tests/notation-legend/multi-voice-songbook.png and b/test-data/visual-tests/notation-legend/multi-voice-songbook.png differ diff --git a/test-data/visual-tests/notation-legend/ottavia-default.png b/test-data/visual-tests/notation-legend/ottavia-default.png index 93509f95a..d017f3a31 100644 Binary files a/test-data/visual-tests/notation-legend/ottavia-default.png and b/test-data/visual-tests/notation-legend/ottavia-default.png differ diff --git a/test-data/visual-tests/notation-legend/ottavia-songbook.png b/test-data/visual-tests/notation-legend/ottavia-songbook.png index 93509f95a..d017f3a31 100644 Binary files a/test-data/visual-tests/notation-legend/ottavia-songbook.png and b/test-data/visual-tests/notation-legend/ottavia-songbook.png differ diff --git a/test-data/visual-tests/notation-legend/pick-stroke-default.png b/test-data/visual-tests/notation-legend/pick-stroke-default.png index 8784d0040..617fcf880 100644 Binary files a/test-data/visual-tests/notation-legend/pick-stroke-default.png and b/test-data/visual-tests/notation-legend/pick-stroke-default.png differ diff --git a/test-data/visual-tests/notation-legend/pick-stroke-songbook.png b/test-data/visual-tests/notation-legend/pick-stroke-songbook.png index 8784d0040..617fcf880 100644 Binary files a/test-data/visual-tests/notation-legend/pick-stroke-songbook.png and b/test-data/visual-tests/notation-legend/pick-stroke-songbook.png differ diff --git a/test-data/visual-tests/notation-legend/slash-default.png b/test-data/visual-tests/notation-legend/slash-default.png index d4ab5585d..7b5ce7c79 100644 Binary files a/test-data/visual-tests/notation-legend/slash-default.png and b/test-data/visual-tests/notation-legend/slash-default.png differ diff --git a/test-data/visual-tests/notation-legend/slash-songbook.png b/test-data/visual-tests/notation-legend/slash-songbook.png index d4ab5585d..7b5ce7c79 100644 Binary files a/test-data/visual-tests/notation-legend/slash-songbook.png and b/test-data/visual-tests/notation-legend/slash-songbook.png differ diff --git a/test-data/visual-tests/notation-legend/slides-default.png b/test-data/visual-tests/notation-legend/slides-default.png index b384d3b70..10ef1045c 100644 Binary files a/test-data/visual-tests/notation-legend/slides-default.png and b/test-data/visual-tests/notation-legend/slides-default.png differ diff --git a/test-data/visual-tests/notation-legend/slides-songbook.png b/test-data/visual-tests/notation-legend/slides-songbook.png index b384d3b70..10ef1045c 100644 Binary files a/test-data/visual-tests/notation-legend/slides-songbook.png and b/test-data/visual-tests/notation-legend/slides-songbook.png differ diff --git a/test-data/visual-tests/notation-legend/staccatissimo-default.png b/test-data/visual-tests/notation-legend/staccatissimo-default.png index 7684151e2..bd9ce7bb5 100644 Binary files a/test-data/visual-tests/notation-legend/staccatissimo-default.png and b/test-data/visual-tests/notation-legend/staccatissimo-default.png differ diff --git a/test-data/visual-tests/notation-legend/staccatissimo-songbook.png b/test-data/visual-tests/notation-legend/staccatissimo-songbook.png index 7684151e2..bd9ce7bb5 100644 Binary files a/test-data/visual-tests/notation-legend/staccatissimo-songbook.png and b/test-data/visual-tests/notation-legend/staccatissimo-songbook.png differ diff --git a/test-data/visual-tests/notation-legend/sweep-default.png b/test-data/visual-tests/notation-legend/sweep-default.png index fbfc43065..1eb930b9e 100644 Binary files a/test-data/visual-tests/notation-legend/sweep-default.png and b/test-data/visual-tests/notation-legend/sweep-default.png differ diff --git a/test-data/visual-tests/notation-legend/sweep-songbook.png b/test-data/visual-tests/notation-legend/sweep-songbook.png index e6dcdc65f..229069642 100644 Binary files a/test-data/visual-tests/notation-legend/sweep-songbook.png and b/test-data/visual-tests/notation-legend/sweep-songbook.png differ diff --git a/test-data/visual-tests/notation-legend/tap-riff-default.png b/test-data/visual-tests/notation-legend/tap-riff-default.png index 4a9903eed..a927a4fb6 100644 Binary files a/test-data/visual-tests/notation-legend/tap-riff-default.png and b/test-data/visual-tests/notation-legend/tap-riff-default.png differ diff --git a/test-data/visual-tests/notation-legend/tap-riff-songbook.png b/test-data/visual-tests/notation-legend/tap-riff-songbook.png index 4a9903eed..a927a4fb6 100644 Binary files a/test-data/visual-tests/notation-legend/tap-riff-songbook.png and b/test-data/visual-tests/notation-legend/tap-riff-songbook.png differ diff --git a/test-data/visual-tests/notation-legend/tempo-change-default.png b/test-data/visual-tests/notation-legend/tempo-change-default.png index 5a66a0f5a..a1f138b8c 100644 Binary files a/test-data/visual-tests/notation-legend/tempo-change-default.png and b/test-data/visual-tests/notation-legend/tempo-change-default.png differ diff --git a/test-data/visual-tests/notation-legend/tempo-change-songbook.png b/test-data/visual-tests/notation-legend/tempo-change-songbook.png index 24f3c12ca..09d788505 100644 Binary files a/test-data/visual-tests/notation-legend/tempo-change-songbook.png and b/test-data/visual-tests/notation-legend/tempo-change-songbook.png differ diff --git a/test-data/visual-tests/notation-legend/text-default.png b/test-data/visual-tests/notation-legend/text-default.png index 2d897bb3f..af5603a68 100644 Binary files a/test-data/visual-tests/notation-legend/text-default.png and b/test-data/visual-tests/notation-legend/text-default.png differ diff --git a/test-data/visual-tests/notation-legend/text-songbook.png b/test-data/visual-tests/notation-legend/text-songbook.png index 2d897bb3f..af5603a68 100644 Binary files a/test-data/visual-tests/notation-legend/text-songbook.png and b/test-data/visual-tests/notation-legend/text-songbook.png differ diff --git a/test-data/visual-tests/notation-legend/tied-note-accidentals-default.png b/test-data/visual-tests/notation-legend/tied-note-accidentals-default.png index 08373b548..80e4dd77c 100644 Binary files a/test-data/visual-tests/notation-legend/tied-note-accidentals-default.png and b/test-data/visual-tests/notation-legend/tied-note-accidentals-default.png differ diff --git a/test-data/visual-tests/notation-legend/tied-note-accidentals-songbook.png b/test-data/visual-tests/notation-legend/tied-note-accidentals-songbook.png index 380d71a35..356c60b6d 100644 Binary files a/test-data/visual-tests/notation-legend/tied-note-accidentals-songbook.png and b/test-data/visual-tests/notation-legend/tied-note-accidentals-songbook.png differ diff --git a/test-data/visual-tests/notation-legend/trill-default.png b/test-data/visual-tests/notation-legend/trill-default.png index bb97da02d..efc00b867 100644 Binary files a/test-data/visual-tests/notation-legend/trill-default.png and b/test-data/visual-tests/notation-legend/trill-default.png differ diff --git a/test-data/visual-tests/notation-legend/trill-songbook.png b/test-data/visual-tests/notation-legend/trill-songbook.png index bb97da02d..efc00b867 100644 Binary files a/test-data/visual-tests/notation-legend/trill-songbook.png and b/test-data/visual-tests/notation-legend/trill-songbook.png differ diff --git a/test-data/visual-tests/notation-legend/triplet-feel-default.png b/test-data/visual-tests/notation-legend/triplet-feel-default.png index 8d498871e..6a14f54e5 100644 Binary files a/test-data/visual-tests/notation-legend/triplet-feel-default.png and b/test-data/visual-tests/notation-legend/triplet-feel-default.png differ diff --git a/test-data/visual-tests/notation-legend/triplet-feel-songbook.png b/test-data/visual-tests/notation-legend/triplet-feel-songbook.png index 8d498871e..6a14f54e5 100644 Binary files a/test-data/visual-tests/notation-legend/triplet-feel-songbook.png and b/test-data/visual-tests/notation-legend/triplet-feel-songbook.png differ diff --git a/test-data/visual-tests/notation-legend/vibrato-default.png b/test-data/visual-tests/notation-legend/vibrato-default.png index bdca1a7fc..80ea0be7c 100644 Binary files a/test-data/visual-tests/notation-legend/vibrato-default.png and b/test-data/visual-tests/notation-legend/vibrato-default.png differ diff --git a/test-data/visual-tests/notation-legend/vibrato-songbook.png b/test-data/visual-tests/notation-legend/vibrato-songbook.png index bdca1a7fc..80ea0be7c 100644 Binary files a/test-data/visual-tests/notation-legend/vibrato-songbook.png and b/test-data/visual-tests/notation-legend/vibrato-songbook.png differ diff --git a/test-data/visual-tests/notation-legend/wah-default.png b/test-data/visual-tests/notation-legend/wah-default.png index 6f2fb8236..3af7a70ea 100644 Binary files a/test-data/visual-tests/notation-legend/wah-default.png and b/test-data/visual-tests/notation-legend/wah-default.png differ diff --git a/test-data/visual-tests/notation-legend/wah-songbook.png b/test-data/visual-tests/notation-legend/wah-songbook.png index 6f2fb8236..3af7a70ea 100644 Binary files a/test-data/visual-tests/notation-legend/wah-songbook.png and b/test-data/visual-tests/notation-legend/wah-songbook.png differ diff --git a/test-data/visual-tests/notation-legend/whammy-default.png b/test-data/visual-tests/notation-legend/whammy-default.png index c4a702952..fa784903d 100644 Binary files a/test-data/visual-tests/notation-legend/whammy-default.png and b/test-data/visual-tests/notation-legend/whammy-default.png differ diff --git a/test-data/visual-tests/notation-legend/whammy-songbook.png b/test-data/visual-tests/notation-legend/whammy-songbook.png index 62a4d92a6..8b3106eaa 100644 Binary files a/test-data/visual-tests/notation-legend/whammy-songbook.png and b/test-data/visual-tests/notation-legend/whammy-songbook.png differ diff --git a/test-data/visual-tests/special-notes/dead-notes.png b/test-data/visual-tests/special-notes/dead-notes.png index 519499354..10b35a839 100644 Binary files a/test-data/visual-tests/special-notes/dead-notes.png and b/test-data/visual-tests/special-notes/dead-notes.png differ diff --git a/test-data/visual-tests/special-notes/ghost-notes.png b/test-data/visual-tests/special-notes/ghost-notes.png index a3adb7270..a756ba0f9 100644 Binary files a/test-data/visual-tests/special-notes/ghost-notes.png and b/test-data/visual-tests/special-notes/ghost-notes.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 092cf103f..8cc340de9 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.png b/test-data/visual-tests/special-notes/grace-notes.png index 90e280aa4..2cac0f878 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-data/visual-tests/special-notes/tied-notes.png b/test-data/visual-tests/special-notes/tied-notes.png index 00b91b2fe..584dfa4cb 100644 Binary files a/test-data/visual-tests/special-notes/tied-notes.png and b/test-data/visual-tests/special-notes/tied-notes.png differ diff --git a/test/visualTests/VisualTestHelper.ts b/test/visualTests/VisualTestHelper.ts index c4e644621..a85a9d095 100644 --- a/test/visualTests/VisualTestHelper.ts +++ b/test/visualTests/VisualTestHelper.ts @@ -331,7 +331,7 @@ export class VisualTestHelper { let totalPixels = match.totalPixels - match.transparentPixels; let percentDifference = (match.differentPixels / totalPixels) * 100; result.pass = percentDifference < 1; - // result.pass = match.differentPixels < 30; + // result.pass = match.differentPixels < 5; if (!result.pass) { let percentDifferenceText = percentDifference.toFixed(2); diff --git a/test/visualTests/features/MusicNotation.test.ts b/test/visualTests/features/MusicNotation.test.ts index 3dab3bc7c..597a40dcc 100644 --- a/test/visualTests/features/MusicNotation.test.ts +++ b/test/visualTests/features/MusicNotation.test.ts @@ -54,4 +54,8 @@ describe('MusicNotationTests', () => { settings.display.barsPerRow = 4; await VisualTestHelper.runVisualTest('music-notation/beams-advanced.gp', settings); }); + + it('rest-collisions', async () => { + await VisualTestHelper.runVisualTest('music-notation/rest-collisions.gp'); + }); });