import { GraphicalStaffEntry } from "./GraphicalStaffEntry" ; import { StaffLine } from "./StaffLine" ; import { GraphicalMusicSheet } from "./GraphicalMusicSheet" ; import { EngravingRules } from "./EngravingRules" ; import { Tie } from "../VoiceData/Tie" ; import { Fraction } from "../../Common/DataObjects/Fraction" ; import { Note } from "../VoiceData/Note" ; import { MusicSheet } from "../MusicSheet" ; import { GraphicalMeasure } from "./GraphicalMeasure" ; import { ClefInstruction , ClefEnum } from "../VoiceData/Instructions/ClefInstruction" ; import { LyricWord } from "../VoiceData/Lyrics/LyricsWord" ; import { SourceMeasure } from "../VoiceData/SourceMeasure" ; import { GraphicalMusicPage } from "./GraphicalMusicPage" ; import { GraphicalNote } from "./GraphicalNote" ; import { Beam } from "../VoiceData/Beam" ; import { OctaveEnum } from "../VoiceData/Expressions/ContinuousExpressions/OctaveShift" ; import { VoiceEntry , StemDirectionType } from "../VoiceData/VoiceEntry" ; import { OrnamentContainer } from "../VoiceData/OrnamentContainer" ; import { Articulation } from "../VoiceData/Articulation" ; import { Tuplet } from "../VoiceData/Tuplet" ; import { MusicSystem } from "./MusicSystem" ; import { GraphicalTie } from "./GraphicalTie" ; import { RepetitionInstruction } from "../VoiceData/Instructions/RepetitionInstruction" ; import { MultiExpression , MultiExpressionEntry } from "../VoiceData/Expressions/MultiExpression" ; import { StaffEntryLink } from "../VoiceData/StaffEntryLink" ; import { MusicSystemBuilder } from "./MusicSystemBuilder" ; import { MultiTempoExpression } from "../VoiceData/Expressions/MultiTempoExpression" ; import { Repetition } from "../MusicSource/Repetition" ; import { PointF2D } from "../../Common/DataObjects/PointF2D" ; import { SourceStaffEntry } from "../VoiceData/SourceStaffEntry" ; import { BoundingBox } from "./BoundingBox" ; import { Instrument } from "../Instrument" ; import { GraphicalLabel } from "./GraphicalLabel" ; import { TextAlignmentEnum } from "../../Common/Enums/TextAlignment" ; import { VerticalGraphicalStaffEntryContainer } from "./VerticalGraphicalStaffEntryContainer" ; import { KeyInstruction } from "../VoiceData/Instructions/KeyInstruction" ; import { AbstractNotationInstruction } from "../VoiceData/Instructions/AbstractNotationInstruction" ; import { TechnicalInstruction } from "../VoiceData/Instructions/TechnicalInstruction" ; import { Pitch } from "../../Common/DataObjects/Pitch" ; import { LinkedVoice } from "../VoiceData/LinkedVoice" ; import { ColDirEnum } from "./BoundingBox" ; import { IGraphicalSymbolFactory } from "../Interfaces/IGraphicalSymbolFactory" ; import { ITextMeasurer } from "../Interfaces/ITextMeasurer" ; import { ITransposeCalculator } from "../Interfaces/ITransposeCalculator" ; import { OctaveShiftParams } from "./OctaveShiftParams" ; import { AccidentalCalculator } from "./AccidentalCalculator" ; import { MidiInstrument } from "../VoiceData/Instructions/ClefInstruction" ; import { Staff } from "../VoiceData/Staff" ; import { OctaveShift } from "../VoiceData/Expressions/ContinuousExpressions/OctaveShift" ; import log from "loglevel" ; import { Dictionary } from "typescript-collections" ; import { GraphicalLyricEntry } from "./GraphicalLyricEntry" ; import { GraphicalLyricWord } from "./GraphicalLyricWord" ; import { GraphicalLine } from "./GraphicalLine" ; import { Label } from "../Label" ; import { GraphicalVoiceEntry } from "./GraphicalVoiceEntry" ; import { VerticalSourceStaffEntryContainer } from "../VoiceData/VerticalSourceStaffEntryContainer" ; import { SkyBottomLineCalculator } from "./SkyBottomLineCalculator" ; import { PlacementEnum } from "../VoiceData/Expressions/AbstractExpression" ; import { AbstractGraphicalInstruction } from "./AbstractGraphicalInstruction" ; import { GraphicalInstantaneousTempoExpression } from "./GraphicalInstantaneousTempoExpression" ; import { InstantaneousTempoExpression , TempoEnum } from "../VoiceData/Expressions/InstantaneousTempoExpression" ; import { ContinuousTempoExpression } from "../VoiceData/Expressions/ContinuousExpressions/ContinuousTempoExpression" ; import { FontStyles } from "../../Common/Enums/FontStyles" ; import { AbstractTempoExpression } from "../VoiceData/Expressions/AbstractTempoExpression" ; import { GraphicalInstantaneousDynamicExpression } from "./GraphicalInstantaneousDynamicExpression" ; import { ContDynamicEnum } from "../VoiceData/Expressions/ContinuousExpressions/ContinuousDynamicExpression" ; import { GraphicalContinuousDynamicExpression } from "./GraphicalContinuousDynamicExpression" ; import { FillEmptyMeasuresWithWholeRests } from "../../OpenSheetMusicDisplay/OSMDOptions" ; import { IStafflineNoteCalculator } from "../Interfaces/IStafflineNoteCalculator" ; import { GraphicalUnknownExpression } from "./GraphicalUnknownExpression" ; import { GraphicalChordSymbolContainer } from "." ; /**
 * Class used to do all the calculations in a MusicSheet, which in the end populates a GraphicalMusicSheet.
 */ export abstract class MusicSheetCalculator { public static symbolFactory : IGraphicalSymbolFactory ; public static transposeCalculator : ITransposeCalculator ; public static stafflineNoteCalculator : IStafflineNoteCalculator ; protected static textMeasurer : ITextMeasurer ; protected staffEntriesWithGraphicalTies : GraphicalStaffEntry [ ] = [ ] ; protected staffEntriesWithOrnaments : GraphicalStaffEntry [ ] = [ ] ; protected staffEntriesWithChordSymbols : GraphicalStaffEntry [ ] = [ ] ; protected staffLinesWithLyricWords : StaffLine [ ] = [ ] ; protected graphicalLyricWords : GraphicalLyricWord [ ] = [ ] ; protected graphicalMusicSheet : GraphicalMusicSheet ; protected rules : EngravingRules ; protected musicSystems : MusicSystem [ ] ; private abstractNotImplementedErrorMessage : string = "abstract, not implemented" ; public static get TextMeasurer ( ) : ITextMeasurer { return MusicSheetCalculator . textMeasurer ; } public static set TextMeasurer ( value ) { MusicSheetCalculator . textMeasurer = value ; } protected get leadSheet ( ) : boolean { return this . graphicalMusicSheet . LeadSheet ; } protected static setMeasuresMinStaffEntriesWidth ( measures , minimumStaffEntriesWidth ) { for ( let idx = 0 , len = measures . length ; idx < len ; ++ idx ) { const measure = measures [ idx ] ; if ( measure ) { measure . minimumStaffEntriesWidth = minimumStaffEntriesWidth ; } } } public initialize ( graphicalMusicSheet ) { this . graphicalMusicSheet = graphicalMusicSheet ; this . rules = graphicalMusicSheet . ParentMusicSheet . Rules ; this . rules . clearMusicSheetObjects ( ) ; this . prepareGraphicalMusicSheet ( ) ; } /**
     * Build the 2D [[GraphicalMeasure]] list needed for the [[MusicSheetCalculator]].
     * Internally it creates [[GraphicalMeasure]]s, [[GraphicalStaffEntry]]'s and [[GraphicalNote]]s.
     */ public prepareGraphicalMusicSheet ( ) { const musicSheet = this . graphicalMusicSheet . ParentMusicSheet ; this . staffEntriesWithGraphicalTies = [ ] ; this . staffEntriesWithOrnaments = [ ] ; this . staffEntriesWithChordSymbols = [ ] ; this . staffLinesWithLyricWords = [ ] ; this . graphicalMusicSheet . Initialize ( ) ; const measureList = this . graphicalMusicSheet . MeasureList ; const accidentalCalculators = this . createAccidentalCalculators ( ) ; const activeClefs = this . graphicalMusicSheet . initializeActiveClefs ( ) ; const lyricWords = [ ] ; const completeNumberOfStaves = musicSheet . getCompleteNumberOfStaves ( ) ; const openOctaveShifts = [ ] ; for ( let i = 0 ; i < completeNumberOfStaves ; i ++ ) { openOctaveShifts . push ( undefined ) ; } for ( let idx = 0 , len = musicSheet . SourceMeasures . length ; idx < len ; ++ idx ) { const sourceMeasure = musicSheet . SourceMeasures [ idx ] ; const graphicalMeasures = this . createGraphicalMeasuresForSourceMeasure ( sourceMeasure , accidentalCalculators , lyricWords , openOctaveShifts , activeClefs ) ; measureList . push ( graphicalMeasures ) ; if ( sourceMeasure . multipleRestMeasures > 0 && this . rules . RenderMultipleRestMeasures ) { sourceMeasure . isReducedToMultiRest = true ; sourceMeasure . multipleRestMeasureNumber = 1 ; const measuresToSkip = sourceMeasure . multipleRestMeasures - 1 ; idx += measuresToSkip ; for ( let idx2 = 1 ; idx2 <= measuresToSkip ; idx2 ++ ) { const nextSourceMeasure = musicSheet . SourceMeasures [ sourceMeasure . MeasureNumber - 1 + idx2 ] ; nextSourceMeasure . multipleRestMeasureNumber = idx2 + 1 ; nextSourceMeasure . isReducedToMultiRest = true ; measureList . push ( [ undefined ] ) ; } } } if ( this . rules . AutoGenerateMutipleRestMeasuresFromRestMeasures && this . rules . RenderMultipleRestMeasures ) { let beginMultiRestMeasure = undefined ; let multiRestCount = 0 ; for ( let idx = 0 , len = musicSheet . SourceMeasures . length ; idx < len ; ++ idx ) { const sourceMeasure = musicSheet . SourceMeasures [ idx ] ; if ( ! sourceMeasure . isReducedToMultiRest && sourceMeasure . canBeReducedToMultiRest ( ) ) { if ( multiRestCount > 0 ) { beginMultiRestMeasure . isReducedToMultiRest = true ; beginMultiRestMeasure . multipleRestMeasureNumber = 1 ; multiRestCount ++ ; sourceMeasure . multipleRestMeasureNumber = multiRestCount ; sourceMeasure . isReducedToMultiRest = true ; for ( let idx2 = 0 ; idx2 < measureList [ idx ] . length ; idx2 ++ ) { measureList [ idx ] [ idx2 ] = undefined ; } } else { beginMultiRestMeasure = sourceMeasure ; multiRestCount = 1 ; } } else { if ( multiRestCount > 1 ) { beginMultiRestMeasure . multipleRestMeasures = multiRestCount ; const graphicalMeasures = this . createGraphicalMeasuresForSourceMeasure ( beginMultiRestMeasure , accidentalCalculators , lyricWords , openOctaveShifts , activeClefs ) ; measureList [ beginMultiRestMeasure . measureListIndex ] = graphicalMeasures ; multiRestCount = 0 ; beginMultiRestMeasure = undefined ; } else { multiRestCount = 0 ; beginMultiRestMeasure = undefined ; } } } if ( multiRestCount > 1 ) { beginMultiRestMeasure . multipleRestMeasures = multiRestCount ; beginMultiRestMeasure . isReducedToMultiRest = true ; const graphicalMeasures = this . createGraphicalMeasuresForSourceMeasure ( beginMultiRestMeasure , accidentalCalculators , lyricWords , openOctaveShifts , activeClefs ) ; measureList [ beginMultiRestMeasure . measureListIndex ] = graphicalMeasures ; multiRestCount = 0 ; beginMultiRestMeasure = undefined ; } } const staffIsPercussionArray = activeClefs . map ( clef => ( clef . ClefType === ClefEnum . percussion ) ) ; this . handleStaffEntries ( staffIsPercussionArray ) ; this . calculateVerticalContainersList ( ) ; this . setIndicesToVerticalGraphicalContainers ( ) ; } /**
     * The main method for the Calculator.
     */ public calculate ( ) { this . musicSystems = [ ] ; this . clearSystemsAndMeasures ( ) ; this . clearRecreatedObjects ( ) ; this . createGraphicalTies ( ) ; this . calculateSheetLabelBoundingBoxes ( ) ; this . calculateXLayout ( this . graphicalMusicSheet , this . maxInstrNameLabelLength ( ) ) ; this . graphicalMusicSheet . MusicPages . length = 0 ; this . calculateMusicSystems ( ) ; GraphicalMusicSheet . transformRelativeToAbsolutePosition ( this . graphicalMusicSheet ) ; } public calculateXLayout ( graphicalMusicSheet , maxInstrNameLabelLength ) { if ( this . graphicalMusicSheet . MeasureList . length > 0 ) { /** list of vertically ordered measures belonging to one bar */ let measures = this . graphicalMusicSheet . MeasureList [ 0 ] ; let minimumStaffEntriesWidth = this . calculateMeasureXLayout ( measures ) ; minimumStaffEntriesWidth = this . calculateMeasureWidthFromStaffEntries ( measures , minimumStaffEntriesWidth ) ; MusicSheetCalculator . setMeasuresMinStaffEntriesWidth ( measures , minimumStaffEntriesWidth ) ; for ( let i = 1 ; i < this . graphicalMusicSheet . MeasureList . length ; i ++ ) { measures = this . graphicalMusicSheet . MeasureList [ i ] ; minimumStaffEntriesWidth = this . calculateMeasureXLayout ( measures ) ; minimumStaffEntriesWidth = this . calculateMeasureWidthFromStaffEntries ( measures , minimumStaffEntriesWidth ) ; MusicSheetCalculator . setMeasuresMinStaffEntriesWidth ( measures , minimumStaffEntriesWidth ) ; } } } public calculateMeasureWidthFromStaffEntries ( measuresVertical , oldMinimumStaffEntriesWidth ) { throw new Error ( this . abstractNotImplementedErrorMessage ) ; } protected formatMeasures ( ) { throw new Error ( this . abstractNotImplementedErrorMessage ) ; } param measures protected calculateMeasureXLayout ( measures ) { throw new Error ( this . abstractNotImplementedErrorMessage ) ; } /**
     * Called for every source measure when generating the list of staff measures for it.
     */ protected initGraphicalMeasuresCreation ( ) { throw new Error ( this . abstractNotImplementedErrorMessage ) ; } protected handleBeam ( graphicalNote , beam , openBeams ) { throw new Error ( this . abstractNotImplementedErrorMessage ) ; } param tiedGraphicalNote param beams param activeClef param octaveShiftValue param graphicalStaffEntry param duration param openTie param isLastTieNote protected handleTiedGraphicalNote ( tiedGraphicalNote , beams , activeClef , octaveShiftValue , graphicalStaffEntry , duration , openTie , isLastTieNote ) { throw new Error ( this . abstractNotImplementedErrorMessage ) ; } protected handleVoiceEntryLyrics ( voiceEntry , graphicalStaffEntry , openLyricWords ) { throw new Error ( this . abstractNotImplementedErrorMessage ) ; } protected handleVoiceEntryOrnaments ( ornamentContainer , voiceEntry , graphicalStaffEntry ) { throw new Error ( this . abstractNotImplementedErrorMessage ) ; } protected handleVoiceEntryArticulations ( articulations , voiceEntry , staffEntry ) { throw new Error ( this . abstractNotImplementedErrorMessage ) ; } param technicalInstructions param voiceEntry param staffEntry protected handleVoiceEntryTechnicalInstructions ( technicalInstructions , voiceEntry , staffEntry ) { throw new Error ( this . abstractNotImplementedErrorMessage ) ; } protected handleTuplet ( graphicalNote , tuplet , openTuplets ) { throw new Error ( this . abstractNotImplementedErrorMessage ) ; } protected layoutVoiceEntry ( voiceEntry , graphicalNotes , graphicalStaffEntry , hasPitchedNote ) { throw new Error ( this . abstractNotImplementedErrorMessage ) ; } protected layoutStaffEntry ( graphicalStaffEntry ) { throw new Error ( this . abstractNotImplementedErrorMessage ) ; } protected createGraphicalTie ( tie , startGse , endGse , startNote , endNote ) { throw new Error ( this . abstractNotImplementedErrorMessage ) ; } protected updateStaffLineBorders ( staffLine ) { throw new Error ( this . abstractNotImplementedErrorMessage ) ; } param musicSystem protected calculateMeasureNumberPlacement ( musicSystem ) { const staffLine = musicSystem . StaffLines [ 0 ] ; if ( ! staffLine || ! staffLine . Measures [ 0 ] ) { log . warn ( "calculateMeasureNumberPlacement: measure undefined for system.Id " + musicSystem . Id ) ; return ; } let previousMeasureNumber = staffLine . Measures [ 0 ] . MeasureNumber ; let labelOffsetX = 0 ; for ( let i = 0 ; i < staffLine . Measures . length ; i ++ ) { if ( this . rules . RenderMeasureNumbersOnlyAtSystemStart && i > 0 ) { return ; } const measure = staffLine . Measures [ i ] ; if ( measure . MeasureNumber === 0 || measure . MeasureNumber === 1 ) { previousMeasureNumber = measure . MeasureNumber ; } if ( measure !== staffLine . Measures [ 0 ] && this . rules . MeasureNumberLabelXOffset ) { labelOffsetX = this . rules . MeasureNumberLabelXOffset ; } else { labelOffsetX = 0 ; } const isFirstMeasureAndNotPrintedOne = this . rules . UseXMLMeasureNumbers && measure . MeasureNumber === 1 && measure . parentSourceMeasure . getPrintedMeasureNumber ( ) !== 1 ; if ( ( measure . MeasureNumber === previousMeasureNumber || measure . MeasureNumber >= previousMeasureNumber + this . rules . MeasureNumberLabelOffset ) && ! measure . parentSourceMeasure . ImplicitMeasure || isFirstMeasureAndNotPrintedOne ) { if ( measure . MeasureNumber !== 1 || ( measure . MeasureNumber === 1 && measure !== staffLine . Measures [ 0 ] ) || isFirstMeasureAndNotPrintedOne ) { this . calculateSingleMeasureNumberPlacement ( measure , staffLine , musicSystem , labelOffsetX ) ; } previousMeasureNumber = measure . MeasureNumber ; } } } private calculateSingleMeasureNumberPlacement ( measure , staffLine , musicSystem , labelOffsetX = 0 ) { const labelNumber = measure . parentSourceMeasure . getPrintedMeasureNumber ( ) . toString ( ) ; const label = new Label ( labelNumber ) ; const graphicalLabel = new GraphicalLabel ( label , this . rules . MeasureNumberLabelHeight , TextAlignmentEnum . LeftBottom , this . rules ) ; const skyBottomLineCalculator = staffLine . SkyBottomLineCalculator ; graphicalLabel . setLabelPositionAndShapeBorders ( ) ; graphicalLabel . PositionAndShape . Parent = musicSystem . PositionAndShape ; const relativeX = staffLine . PositionAndShape . RelativePosition . x + measure . PositionAndShape . RelativePosition . x - graphicalLabel . PositionAndShape . BorderMarginLeft + labelOffsetX ; let relativeY ; let start = relativeX ; let end = relativeX - graphicalLabel . PositionAndShape . BorderLeft + graphicalLabel . PositionAndShape . BorderRight ; start -= staffLine . PositionAndShape . RelativePosition . x ; end -= staffLine . PositionAndShape . RelativePosition . x ; const startCollisionCheck = start + 0.5 ; const endCollisionCheck = end - 0.5 ; const skyLineMinValue = skyBottomLineCalculator . getSkyLineMinInRange ( startCollisionCheck , endCollisionCheck ) ; if ( measure === staffLine . Measures [ 0 ] ) { let minBracketTopBorder = 0 ; if ( musicSystem . GroupBrackets . length > 0 ) { for ( const groupBracket of musicSystem . GroupBrackets ) { minBracketTopBorder = Math . min ( minBracketTopBorder , groupBracket . PositionAndShape . BorderTop ) ; } } relativeY = Math . min ( skyLineMinValue , minBracketTopBorder ) ; } else { relativeY = skyLineMinValue ; } relativeY = Math . min ( 0 , relativeY ) ; graphicalLabel . PositionAndShape . RelativePosition = new PointF2D ( relativeX , relativeY ) ; musicSystem . MeasureNumberLabels . push ( graphicalLabel ) ; } private calculateMeasureNumberSkyline ( musicSystem ) { const staffLine = musicSystem . StaffLines [ 0 ] ; for ( const measureNumberLabel of musicSystem . MeasureNumberLabels ) { let start = measureNumberLabel . PositionAndShape . RelativePosition . x ; let end = start - measureNumberLabel . PositionAndShape . BorderLeft + measureNumberLabel . PositionAndShape . BorderRight ; start -= staffLine . PositionAndShape . RelativePosition . x ; end -= staffLine . PositionAndShape . RelativePosition . x ; staffLine . SkyBottomLineCalculator . updateSkyLineInRange ( start , end , measureNumberLabel . PositionAndShape . RelativePosition . y + measureNumberLabel . PositionAndShape . BorderMarginTop ) ; } } param tie param tieIsAtSystemBreak protected layoutGraphicalTie ( tie , tieIsAtSystemBreak , isTab ) { throw new Error ( this . abstractNotImplementedErrorMessage ) ; } param staffLine param lyricVersesNumber protected calculateSingleStaffLineLyricsPosition ( staffLine , lyricVersesNumber ) { let numberOfVerses = 0 ; let lyricsStartYPosition = this . rules . StaffHeight ; const lyricsStaffEntriesList = [ ] ; const skyBottomLineCalculator = staffLine . SkyBottomLineCalculator ; let len = staffLine . Measures . length ; for ( let idx = 0 ; idx < len ; ++ idx ) { const measure = staffLine . Measures [ idx ] ; const measureRelativePosition = measure . PositionAndShape . RelativePosition ; const len2 = measure . staffEntries . length ; for ( let idx2 = 0 ; idx2 < len2 ; ++ idx2 ) { const staffEntry = measure . staffEntries [ idx2 ] ; if ( staffEntry . LyricsEntries . length > 0 ) { lyricsStaffEntriesList . push ( staffEntry ) ; numberOfVerses = Math . max ( numberOfVerses , staffEntry . LyricsEntries . length ) ; const staffEntryPositionX = staffEntry . PositionAndShape . RelativePosition . x + measureRelativePosition . x ; let minMarginLeft = Number . MAX_VALUE ; let maxMarginRight = Number . MIN_VALUE ; for ( let i = 0 ; i < staffEntry . LyricsEntries . length ; i ++ ) { const lyricsEntryLabel = staffEntry . LyricsEntries [ i ] . GraphicalLabel ; minMarginLeft = Math . min ( minMarginLeft , staffEntryPositionX + lyricsEntryLabel . PositionAndShape . BorderMarginLeft ) ; maxMarginRight = Math . max ( maxMarginRight , staffEntryPositionX + lyricsEntryLabel . PositionAndShape . BorderMarginRight ) ; } const bottomLineMax = skyBottomLineCalculator . getBottomLineMaxInRange ( minMarginLeft , maxMarginRight ) ; lyricsStartYPosition = Math . max ( lyricsStartYPosition , bottomLineMax ) ; } } } let maxPosition = 0 ; len = lyricsStaffEntriesList . length ; for ( const staffEntry of lyricsStaffEntriesList ) { for ( let i = 0 ; i < staffEntry . LyricsEntries . length ; i ++ ) { const lyricEntry = staffEntry . LyricsEntries [ i ] ; const lyricsEntryLabel = lyricEntry . GraphicalLabel ; const verseNumber = lyricEntry . LyricsEntry . VerseNumber ; const sortedLyricVerseNumberIndex = lyricVersesNumber . indexOf ( verseNumber ) ; const firstPosition = lyricsStartYPosition + this . rules . LyricsHeight + this . rules . VerticalBetweenLyricsDistance + this . rules . LyricsYOffsetToStaffHeight ; let position = firstPosition + ( this . rules . VerticalBetweenLyricsDistance + this . rules . LyricsHeight ) * sortedLyricVerseNumberIndex ; if ( this . leadSheet ) { position = 3.4 + ( this . rules . VerticalBetweenLyricsDistance + this . rules . LyricsHeight ) * ( sortedLyricVerseNumberIndex ) ; } const previousRelativeX = lyricsEntryLabel . PositionAndShape . RelativePosition . x ; lyricsEntryLabel . PositionAndShape . RelativePosition = new PointF2D ( previousRelativeX , position ) ; maxPosition = Math . max ( maxPosition , position ) ; } } if ( lyricsStaffEntriesList . length > 0 ) { const endX = staffLine . PositionAndShape . Size . width ; let startX = lyricsStaffEntriesList [ 0 ] . PositionAndShape . RelativePosition . x + lyricsStaffEntriesList [ 0 ] . PositionAndShape . BorderMarginLeft + lyricsStaffEntriesList [ 0 ] . parentMeasure . PositionAndShape . RelativePosition . x ; startX = startX > endX ? endX : startX ; skyBottomLineCalculator . updateBottomLineInRange ( startX , endX , maxPosition ) ; } return lyricsStaffEntriesList ; } param lyricsStaffEntries protected calculateLyricsExtendsAndDashes ( lyricsStaffEntries ) { for ( let idx = 0 , len = lyricsStaffEntries . length ; idx < len ; ++ idx ) { const staffEntry = lyricsStaffEntries [ idx ] ; for ( let i = 0 ; i < staffEntry . LyricsEntries . length ; i ++ ) { const lyricEntry = staffEntry . LyricsEntries [ i ] ; if ( lyricEntry . ParentLyricWord && lyricEntry . ParentLyricWord . GraphicalLyricsEntries [ lyricEntry . ParentLyricWord . GraphicalLyricsEntries . length - 1 ] !== lyricEntry ) { this . calculateSingleLyricWord ( lyricEntry ) ; } if ( lyricEntry . LyricsEntry . extend ) { this . calculateLyricExtend ( lyricEntry ) ; } } } } param sourceMeasure param multiExpression param measureIndex param staffIndex protected calculateSingleOctaveShift ( sourceMeasure , multiExpression , measureIndex , staffIndex ) { throw new Error ( this . abstractNotImplementedErrorMessage ) ; } param repetitionInstruction param measureIndex protected calculateWordRepetitionInstruction ( repetitionInstruction , measureIndex ) { throw new Error ( this . abstractNotImplementedErrorMessage ) ; } param multiExpression param measureIndex param staffIndex protected calculateMoodAndUnknownExpression ( multiExpression , measureIndex , staffIndex ) { const absoluteTimestamp = multiExpression . AbsoluteTimestamp ; const measures = this . graphicalMusicSheet . MeasureList [ measureIndex ] ; let relative = new PointF2D ( ) ; if ( ( multiExpression . MoodList . length > 0 ) || ( multiExpression . UnknownList . length > 0 ) ) { let combinedExprString = "" ; for ( let idx = 0 , len = multiExpression . EntriesList . length ; idx < len ; ++ idx ) { const entry = multiExpression . EntriesList [ idx ] ; if ( entry . prefix !== "" ) { if ( combinedExprString === "" ) { combinedExprString += entry . prefix ; } else { combinedExprString += " " + entry . prefix ; } } if ( combinedExprString === "" ) { combinedExprString += entry . label ; } else { combinedExprString += " " + entry . label ; } } const staffLine = measures [ staffIndex ] . ParentStaffLine ; if ( ! staffLine ) { log . debug ( "MusicSheetCalculator.calculateMoodAndUnknownExpression: staffLine undefined. Returning." ) ; return ; } relative = this . getRelativePositionInStaffLineFromTimestamp ( absoluteTimestamp , staffIndex , staffLine , staffLine ?. isPartOfMultiStaffInstrument ( ) ) ; if ( Math . abs ( relative . x - 0 ) < 0.0001 ) { relative . x = measures [ staffIndex ] . beginInstructionsWidth + this . rules . RhythmRightMargin ; } const fontHeight = this . rules . UnknownTextHeight ; const placement = multiExpression . getPlacementOfFirstEntry ( ) ; const graphLabel = this . calculateLabel ( staffLine , relative , combinedExprString , multiExpression . getFontstyleOfFirstEntry ( ) , placement , fontHeight ) ; const gue = new GraphicalUnknownExpression ( staffLine , graphLabel , placement , measures [ staffIndex ] ?. parentSourceMeasure , multiExpression ) ; staffLine . AbstractExpressions . push ( gue ) ; } } /**
     * Delete all Objects that must be recalculated.
     * If graphicalMusicSheet.reCalculate has been called, then this method will be called to reset or remove all flexible
     * graphical music symbols (e.g. Ornaments, Lyrics, Slurs) graphicalMusicSheet will have MusicPages, they will have MusicSystems etc...
     */ protected clearRecreatedObjects ( ) { for ( let idx = 0 , len = this . staffEntriesWithGraphicalTies . length ; idx < len ; ++ idx ) { const staffEntriesWithGraphicalTie = this . staffEntriesWithGraphicalTies [ idx ] ; staffEntriesWithGraphicalTie . GraphicalTies . length = 0 ; } this . staffEntriesWithGraphicalTies . length = 0 ; return ; } param graphicalStaffEntry param staffEntryLinks protected handleStaffEntryLink ( graphicalStaffEntry , staffEntryLinks ) { log . debug ( "handleStaffEntryLink not implemented" ) ; } /**
     * Store the newly computed [[Measure]]s in newly created [[MusicSystem]]s.
     */ protected calculateMusicSystems ( ) { if ( ! this . graphicalMusicSheet . MeasureList ) { return ; } const allMeasures = this . graphicalMusicSheet . MeasureList ; if ( ! allMeasures ) { return ; } if ( this . rules . MinMeasureToDrawIndex > allMeasures . length - 1 ) { log . debug ( "minimum measure to draw index out of range. resetting min measure index to limit." ) ; this . rules . MinMeasureToDrawIndex = allMeasures . length - 1 ; } const visibleMeasureList = [ ] ; for ( let idx = this . rules . MinMeasureToDrawIndex , len = allMeasures . length ; idx < len && idx <= this . rules . MaxMeasureToDrawIndex ; ++ idx ) { const graphicalMeasures = allMeasures [ idx ] ; const visiblegraphicalMeasures = [ ] ; for ( let idx2 = 0 , len2 = graphicalMeasures . length ; idx2 < len2 ; ++ idx2 ) { const graphicalMeasure = allMeasures [ idx ] [ idx2 ] ; if ( graphicalMeasure ?. isVisible ( ) ) { visiblegraphicalMeasures . push ( graphicalMeasure ) ; if ( this . rules . ColoringEnabled ) { for ( const staffEntry of graphicalMeasure . staffEntries ) { for ( const gve of staffEntry . graphicalVoiceEntries ) { gve . color ( ) ; } } } } } visibleMeasureList . push ( visiblegraphicalMeasures ) ; } let numberOfStaffLines = 0 ; for ( let idx = 0 , len = visibleMeasureList . length ; idx < len ; ++ idx ) { const gmlist = visibleMeasureList [ idx ] ; numberOfStaffLines = Math . max ( gmlist . length , numberOfStaffLines ) ; break ; } if ( numberOfStaffLines === 0 ) { return ; } const musicSystemBuilder = new MusicSystemBuilder ( ) ; musicSystemBuilder . initialize ( this . graphicalMusicSheet , visibleMeasureList , numberOfStaffLines ) ; this . musicSystems = musicSystemBuilder . buildMusicSystems ( ) ; this . formatMeasures ( ) ; if ( ! this . leadSheet ) { this . optimizeRestPlacement ( ) ; this . calculateStaffEntryArticulationMarks ( ) ; if ( this . rules . RenderSlurs ) { this . calculateTieCurves ( ) ; } } this . calculateSkyBottomLines ( ) ; this . calculateTupletNumbers ( ) ; if ( this . rules . RenderMeasureNumbers ) { for ( let idx = 0 , len = this . musicSystems . length ; idx < len ; ++ idx ) { const musicSystem = this . musicSystems [ idx ] ; this . calculateMeasureNumberPlacement ( musicSystem ) ; } } this . calculateFingerings ( ) ; if ( ! this . leadSheet && this . rules . RenderSlurs ) { this . calculateSlurs ( ) ; } if ( this . rules . RenderMeasureNumbers ) { for ( let idx = 0 , len = this . musicSystems . length ; idx < len ; ++ idx ) { const musicSystem = this . musicSystems [ idx ] ; this . calculateMeasureNumberSkyline ( musicSystem ) ; } } if ( ! this . leadSheet ) { this . calculateOrnaments ( ) ; } this . calculateChordSymbols ( ) ; if ( ! this . leadSheet ) { this . calculateDynamicExpressions ( ) ; this . calculateMoodAndUnknownExpressions ( ) ; this . calculateExpressionAlignements ( ) ; this . calculateOctaveShifts ( ) ; this . calculateWordRepetitionInstructions ( ) ; } this . calculateRepetitionEndings ( ) ; if ( ! this . leadSheet ) { this . calculateTempoExpressions ( ) ; } this . calculateRehearsalMarks ( ) ; this . calculateLyricsPosition ( ) ; for ( let idx2 = 0 , len2 = this . musicSystems . length ; idx2 < len2 ; ++ idx2 ) { const musicSystem = this . musicSystems [ idx2 ] ; for ( let idx3 = 0 , len3 = musicSystem . StaffLines . length ; idx3 < len3 ; ++ idx3 ) { const staffLine = musicSystem . StaffLines [ idx3 ] ; this . updateStaffLineBorders ( staffLine ) ; } } musicSystemBuilder . calculateSystemYLayout ( ) ; this . calculateComments ( ) ; this . calculateMarkedAreas ( ) ; for ( let idx = 0 , len = this . graphicalMusicSheet . MusicPages . length ; idx < len ; ++ idx ) { const graphicalMusicPage = this . graphicalMusicSheet . MusicPages [ idx ] ; for ( let idx2 = 0 , len2 = graphicalMusicPage . MusicSystems . length ; idx2 < len2 ; ++ idx2 ) { const isFirstSystem = idx === 0 && idx2 === 0 ; const musicSystem = graphicalMusicPage . MusicSystems [ idx2 ] ; musicSystem . setMusicSystemLabelsYPosition ( ) ; if ( ! this . leadSheet ) { musicSystem . setYPositionsToVerticalLineObjectsAndCreateLines ( this . rules ) ; musicSystem . createSystemLeftLine ( this . rules . SystemThinLineWidth , this . rules . SystemLabelsRightMargin , isFirstSystem ) ; musicSystem . createInstrumentBrackets ( this . graphicalMusicSheet . ParentMusicSheet . Instruments , this . rules . StaffHeight ) ; musicSystem . createGroupBrackets ( this . graphicalMusicSheet . ParentMusicSheet . InstrumentalGroups , this . rules . StaffHeight , 0 ) ; musicSystem . alignBeginInstructions ( ) ; } else if ( musicSystem === musicSystem . Parent . MusicSystems [ 0 ] ) { musicSystem . createSystemLeftLine ( this . rules . SystemThinLineWidth , this . rules . SystemLabelsRightMargin , isFirstSystem ) ; } musicSystem . calculateBorders ( this . rules ) ; } const distance = graphicalMusicPage . MusicSystems [ 0 ] . PositionAndShape . BorderTop ; for ( let idx2 = 0 , len2 = graphicalMusicPage . MusicSystems . length ; idx2 < len2 ; ++ idx2 ) { const musicSystem = graphicalMusicPage . MusicSystems [ idx2 ] ; musicSystem . PositionAndShape . RelativePosition = new PointF2D ( musicSystem . PositionAndShape . RelativePosition . x , musicSystem . PositionAndShape . RelativePosition . y - distance ) ; } for ( let idx2 = 0 , len2 = graphicalMusicPage . MusicSystems . length ; idx2 < len2 ; ++ idx2 ) { const musicSystem = graphicalMusicPage . MusicSystems [ idx2 ] ; for ( let idx3 = 0 , len3 = musicSystem . StaffLines . length ; idx3 < len3 ; ++ idx3 ) { const staffLine = musicSystem . StaffLines [ idx3 ] ; staffLine . addActivitySymbolClickArea ( ) ; } } graphicalMusicPage . PositionAndShape . calculateTopBottomBorders ( ) ; if ( graphicalMusicPage === this . graphicalMusicSheet . MusicPages [ 0 ] ) { this . calculatePageLabels ( graphicalMusicPage ) ; } graphicalMusicPage . PositionAndShape . calculateTopBottomBorders ( ) ; } } protected calculateMarkedAreas ( ) { return ; } protected calculateComments ( ) { return ; } protected calculateChordSymbols ( ) { for ( const musicSystem of this . musicSystems ) { for ( const staffLine of musicSystem . StaffLines ) { const skybottomcalculator = staffLine . SkyBottomLineCalculator ; let minimumOffset = Number . MAX_SAFE_INTEGER ; if ( this . rules . ChordSymbolYAlignment && this . rules . ChordSymbolYAlignmentScope === "staffline" ) { const alignmentScopedStaffEntries = [ ] ; for ( const measure of staffLine . Measures ) { alignmentScopedStaffEntries . push ( ... measure . staffEntries ) ; } minimumOffset = this . calculateAlignedChordSymbolsOffset ( alignmentScopedStaffEntries , skybottomcalculator ) ; } for ( const measure of staffLine . Measures ) { if ( this . rules . ChordSymbolYAlignment && this . rules . ChordSymbolYAlignmentScope === "measure" ) { minimumOffset = this . calculateAlignedChordSymbolsOffset ( measure . staffEntries , skybottomcalculator ) ; } for ( const staffEntry of measure . staffEntries ) { if ( ! staffEntry . graphicalChordContainers || staffEntry . graphicalChordContainers . length === 0 ) { continue ; } for ( let i = 0 ; i < staffEntry . graphicalChordContainers . length ; i ++ ) { const graphicalChordContainer = staffEntry . graphicalChordContainers [ i ] ; const sps = staffEntry . PositionAndShape ; const gps = graphicalChordContainer . PositionAndShape ; const start = gps . BorderMarginLeft + sps . AbsolutePosition . x ; const end = gps . BorderMarginRight + sps . AbsolutePosition . x ; if ( ! this . rules . ChordSymbolYAlignment || minimumOffset > 0 ) { minimumOffset = skybottomcalculator . getSkyLineMinInRange ( start , end ) ; } let yShift = 0 ; if ( i === 0 ) { yShift += this . rules . ChordSymbolYOffset ; yShift += 0.1 ; } else { yShift += this . rules . ChordSymbolYPadding ; } yShift *= - 1 ; const gLabel = graphicalChordContainer . GraphicalLabel ; gLabel . PositionAndShape . RelativePosition . y = minimumOffset + yShift ; gLabel . setLabelPositionAndShapeBorders ( ) ; gLabel . PositionAndShape . calculateBoundingBox ( ) ; skybottomcalculator . updateSkyLineInRange ( start , end , minimumOffset + gLabel . PositionAndShape . BorderMarginTop ) ; } } } } } } protected calculateAlignedChordSymbolsOffset ( staffEntries , sbc ) { let minimumOffset = Number . MAX_SAFE_INTEGER ; for ( const staffEntry of staffEntries ) { for ( const graphicalChordContainer of staffEntry . graphicalChordContainers ) { const sps = staffEntry . PositionAndShape ; const gps = graphicalChordContainer . PositionAndShape ; const start = gps . BorderMarginLeft + sps . AbsolutePosition . x ; const end = gps . BorderMarginRight + sps . AbsolutePosition . x ; minimumOffset = Math . min ( minimumOffset , sbc . getSkyLineMinInRange ( start , end ) ) ; } } return minimumOffset ; } param rest param gse param measure protected layoutMeasureWithWholeRest ( rest , gse , measure ) { return ; } protected layoutBeams ( staffEntry ) { return ; } protected layoutArticulationMarks ( articulations , voiceEntry , graphicalStaffEntry ) { return ; } protected layoutOrnament ( ornaments , voiceEntry , graphicalStaffEntry ) { return ; } protected calculateRestNotePlacementWithinGraphicalBeam ( graphicalStaffEntry , restNote , previousNote , nextStaffEntry , nextNote ) { return ; } protected calculateTupletNumbers ( ) { return ; } protected calculateSlurs ( ) { return ; } protected calculateDynamicExpressionsForMultiExpression ( multiExpression , measureIndex , staffIndex ) { return ; } param graphicalContinuousDynamic param startPosInStaffline protected calculateGraphicalVerbalContinuousDynamic ( graphicalContinuousDynamic , startPosInStaffline ) { const graphLabel = graphicalContinuousDynamic . Label ; const left = startPosInStaffline . x + graphLabel . PositionAndShape . BorderMarginLeft ; const right = startPosInStaffline . x + graphLabel . PositionAndShape . BorderMarginRight ; const placement = graphicalContinuousDynamic . ContinuousDynamic . Placement ; const staffLine = graphicalContinuousDynamic . ParentStaffLine ; const skyBottomLineCalculator = staffLine . SkyBottomLineCalculator ; let drawingHeight ; if ( placement === PlacementEnum . Below ) { drawingHeight = skyBottomLineCalculator . getBottomLineMaxInRange ( left , right ) ; graphLabel . PositionAndShape . RelativePosition = new PointF2D ( startPosInStaffline . x , drawingHeight - graphLabel . PositionAndShape . BorderMarginTop ) ; } else { drawingHeight = skyBottomLineCalculator . getSkyLineMinInRange ( left , right ) ; graphLabel . PositionAndShape . RelativePosition = new PointF2D ( startPosInStaffline . x , drawingHeight - graphLabel . PositionAndShape . BorderMarginBottom ) ; } } param graphicalContinuousDynamic param startPosInStaffline public calculateGraphicalContinuousDynamic ( graphicalContinuousDynamic , startPosInStaffline ) { const staffIndex = graphicalContinuousDynamic . ParentStaffLine . ParentStaff . idInMusicSheet ; const endMeasure = this . graphicalMusicSheet . getGraphicalMeasureFromSourceMeasureAndIndex ( graphicalContinuousDynamic . ContinuousDynamic . EndMultiExpression . SourceMeasureParent , staffIndex ) ; if ( ! endMeasure ) { log . warn ( "MusicSheetCalculator.calculateGraphicalContinuousDynamic: No endMeasure found" ) ; return ; } graphicalContinuousDynamic . EndMeasure = endMeasure ; const staffLine = graphicalContinuousDynamic . ParentStaffLine ; const endStaffLine = endMeasure . ParentStaffLine ; const sameStaffLine = endStaffLine && staffLine === endStaffLine ; let isPartOfMultiStaffInstrument = false ; if ( endStaffLine ) { isPartOfMultiStaffInstrument = endStaffLine ?. isPartOfMultiStaffInstrument ( ) ; } else if ( staffLine ) { isPartOfMultiStaffInstrument = staffLine ?. isPartOfMultiStaffInstrument ( ) ; } const endAbsoluteTimestamp = Fraction . createFromFraction ( graphicalContinuousDynamic . ContinuousDynamic . EndMultiExpression . AbsoluteTimestamp ) ; const container = this . graphicalMusicSheet . GetVerticalContainerFromTimestamp ( endAbsoluteTimestamp ) ; const parentMeasure = container . getFirstNonNullStaffEntry ( ) . parentMeasure ; const endOfMeasure = parentMeasure . PositionAndShape . AbsolutePosition . x + parentMeasure . PositionAndShape . BorderRight ; let maxNoteLength = new Fraction ( 0 , 0 , 0 ) ; for ( const staffEntry of container . StaffEntries ) { const currentMaxLength = staffEntry ?. sourceStaffEntry ?. calculateMaxNoteLength ( ) ; if ( currentMaxLength ?. gt ( maxNoteLength ) ) { maxNoteLength = currentMaxLength ; } } const endPosInStaffLine = this . getRelativePositionInStaffLineFromTimestamp ( endAbsoluteTimestamp , staffIndex , endStaffLine , isPartOfMultiStaffInstrument , 0 ) ; const beginOfNextNote = Fraction . plus ( endAbsoluteTimestamp , maxNoteLength ) ; const nextNotePosInStaffLine = this . getRelativePositionInStaffLineFromTimestamp ( beginOfNextNote , staffIndex , endStaffLine , isPartOfMultiStaffInstrument , 0 ) ; if ( nextNotePosInStaffLine . x > endPosInStaffLine . x && nextNotePosInStaffLine . x < endOfMeasure ) { endPosInStaffLine . x += ( nextNotePosInStaffLine . x - endPosInStaffLine . x ) / this . rules . WedgeEndDistanceBetweenTimestampsFactor ; } else { endPosInStaffLine . x = endOfMeasure - this . rules . WedgeHorizontalMargin ; } const startCollideBox = this . dynamicExpressionMap . get ( graphicalContinuousDynamic . ContinuousDynamic . StartMultiExpression . AbsoluteTimestamp . RealValue ) ; if ( startCollideBox ) { startPosInStaffline . x = startCollideBox . RelativePosition . x + startCollideBox . BorderMarginRight + this . rules . WedgeHorizontalMargin ; } const musicSystem = staffLine . ParentMusicSystem ; const currentStaffLineIndex = musicSystem . StaffLines . indexOf ( staffLine ) ; const skyBottomLineCalculator = staffLine . SkyBottomLineCalculator ; const placement = graphicalContinuousDynamic . ContinuousDynamic . Placement ; let secondGraphicalContinuousDynamic = undefined ; if ( sameStaffLine && endPosInStaffLine . x - startPosInStaffline . x < this . rules . WedgeMinLength ) { endPosInStaffLine . x = startPosInStaffline . x + this . rules . WedgeMinLength ; } const upperStartX = startPosInStaffline . x ; const lowerStartX = endStaffLine . Measures [ 0 ] . beginInstructionsWidth - this . rules . WedgeHorizontalMargin - 2 ; let upperEndX = 0 ; let lowerEndX = 0 ; if ( ! sameStaffLine ) { upperEndX = staffLine . PositionAndShape . Size . width ; lowerEndX = endPosInStaffLine . x ; secondGraphicalContinuousDynamic = new GraphicalContinuousDynamicExpression ( graphicalContinuousDynamic . ContinuousDynamic , endStaffLine , endMeasure . parentSourceMeasure ) ; secondGraphicalContinuousDynamic . IsSplittedPart = true ; graphicalContinuousDynamic . IsSplittedPart = true ; } else { upperEndX = endPosInStaffLine . x ; } let idealY = 0 ; let secondIdealY = 0 ; if ( placement === PlacementEnum . Below ) { let nextStaffLineIndex = 0 ; if ( currentStaffLineIndex < musicSystem . StaffLines . length - 1 ) { nextStaffLineIndex = currentStaffLineIndex + 1 ; } if ( nextStaffLineIndex > currentStaffLineIndex ) { const nextStaffLine = musicSystem . StaffLines [ nextStaffLineIndex ] ; const distanceBetweenStaffLines = nextStaffLine . PositionAndShape . RelativePosition . y - staffLine . PositionAndShape . RelativePosition . y - this . rules . StaffHeight ; idealY = this . rules . StaffHeight + distanceBetweenStaffLines / 2 ; } else { idealY = this . rules . WedgePlacementBelowY ; } idealY -= this . rules . WedgeOpeningLength / 2 ; if ( ! sameStaffLine ) { secondIdealY = idealY ; } let maxBottomLineValueForExpressionLength = skyBottomLineCalculator . getBottomLineMaxInRange ( upperStartX , upperEndX ) ; if ( maxBottomLineValueForExpressionLength > idealY ) { idealY = maxBottomLineValueForExpressionLength ; } const withinCrossedBeam = false ; if ( currentStaffLineIndex < musicSystem . StaffLines . length - 1 ) { const closestToEndStaffEntry = staffLine . findClosestStaffEntry ( upperEndX ) ; const closestToStartStaffEntry = staffLine . findClosestStaffEntry ( upperStartX ) ; if ( closestToStartStaffEntry && closestToEndStaffEntry ) { const startVerticalContainer = closestToStartStaffEntry . parentVerticalContainer ; if ( startVerticalContainer ) { } if ( withinCrossedBeam ) { const nextStaffLine = musicSystem . StaffLines [ nextStaffLineIndex ] ; const nextStaffLineMinSkyLineValue = nextStaffLine . SkyBottomLineCalculator . getSkyLineMinInRange ( upperStartX , upperEndX ) ; const distanceBetweenStaffLines = nextStaffLine . PositionAndShape . RelativePosition . y - staffLine . PositionAndShape . RelativePosition . y ; const relativeSkyLineHeight = distanceBetweenStaffLines + nextStaffLineMinSkyLineValue ; if ( relativeSkyLineHeight - this . rules . WedgeOpeningLength > this . rules . StaffHeight ) { idealY = relativeSkyLineHeight - this . rules . WedgeVerticalMargin ; } else { idealY = this . rules . StaffHeight + this . rules . WedgeOpeningLength ; } graphicalContinuousDynamic . NotToBeRemoved = true ; } } } if ( ! sameStaffLine ) { maxBottomLineValueForExpressionLength = endStaffLine . SkyBottomLineCalculator . getBottomLineMaxInRange ( lowerStartX , lowerEndX ) ; if ( maxBottomLineValueForExpressionLength > secondIdealY ) { secondIdealY = maxBottomLineValueForExpressionLength ; } secondIdealY += this . rules . WedgeOpeningLength / 2 ; secondIdealY += this . rules . WedgeVerticalMargin ; } if ( ! withinCrossedBeam ) { idealY += this . rules . WedgeOpeningLength / 2 ; idealY += this . rules . WedgeVerticalMargin ; } } else if ( placement === PlacementEnum . Above ) { if ( staffLine . ParentStaff . ParentInstrument . Staves . length === 1 ) { idealY = this . rules . WedgePlacementAboveY ; } else { let previousStaffLineIndex = 0 ; if ( currentStaffLineIndex > 0 ) { previousStaffLineIndex = currentStaffLineIndex - 1 ; } const previousStaffLine = musicSystem . StaffLines [ previousStaffLineIndex ] ; const distanceBetweenStaffLines = staffLine . PositionAndShape . RelativePosition . y - previousStaffLine . PositionAndShape . RelativePosition . y - this . rules . StaffHeight ; idealY = - distanceBetweenStaffLines / 2 ; } idealY += this . rules . WedgeOpeningLength / 2 ; if ( ! sameStaffLine ) { secondIdealY = idealY ; } let minSkyLineValueForExpressionLength = skyBottomLineCalculator . getSkyLineMinInRange ( upperStartX , upperEndX ) ; if ( minSkyLineValueForExpressionLength < idealY ) { idealY = minSkyLineValueForExpressionLength ; } const withinCrossedBeam = false ; if ( staffLine . ParentStaff . ParentInstrument . Staves . length > 1 && currentStaffLineIndex > 0 ) { const closestToStartStaffEntry = staffLine . findClosestStaffEntry ( upperStartX ) ; const closestToEndStaffEntry = staffLine . findClosestStaffEntry ( upperEndX ) ; if ( closestToStartStaffEntry && closestToEndStaffEntry ) { const startVerticalContainer = closestToStartStaffEntry . parentVerticalContainer ; const formerStaffLineIndex = currentStaffLineIndex - 1 ; if ( startVerticalContainer ) { } if ( withinCrossedBeam ) { const formerStaffLine = musicSystem . StaffLines [ formerStaffLineIndex ] ; const formerStaffLineMaxBottomLineValue = formerStaffLine . SkyBottomLineCalculator . getBottomLineMaxInRange ( upperStartX , upperEndX ) ; const distanceBetweenStaffLines = staffLine . PositionAndShape . RelativePosition . y - formerStaffLine . PositionAndShape . RelativePosition . y ; const relativeSkyLineHeight = distanceBetweenStaffLines - formerStaffLineMaxBottomLineValue ; idealY = ( relativeSkyLineHeight - this . rules . StaffHeight ) / 2 + this . rules . StaffHeight ; } } } if ( ! sameStaffLine ) { minSkyLineValueForExpressionLength = endStaffLine . SkyBottomLineCalculator . getSkyLineMinInRange ( lowerStartX , lowerEndX ) ; if ( minSkyLineValueForExpressionLength < secondIdealY ) { secondIdealY = minSkyLineValueForExpressionLength ; } secondIdealY -= this . rules . WedgeOpeningLength / 2 ; } if ( ! withinCrossedBeam ) { idealY -= this . rules . WedgeOpeningLength / 2 ; idealY -= this . rules . WedgeVerticalMargin ; } if ( ! sameStaffLine ) { secondIdealY -= this . rules . WedgeVerticalMargin ; } } graphicalContinuousDynamic . Lines . clear ( ) ; if ( graphicalContinuousDynamic . ContinuousDynamic . DynamicType === ContDynamicEnum . crescendo ) { if ( sameStaffLine ) { graphicalContinuousDynamic . createCrescendoLines ( upperStartX , upperEndX , idealY ) ; graphicalContinuousDynamic . calcPsi ( ) ; } else { graphicalContinuousDynamic . createFirstHalfCrescendoLines ( upperStartX , upperEndX , idealY ) ; graphicalContinuousDynamic . calcPsi ( ) ; secondGraphicalContinuousDynamic . createSecondHalfCrescendoLines ( lowerStartX , lowerEndX , secondIdealY ) ; secondGraphicalContinuousDynamic . calcPsi ( ) ; } } else if ( graphicalContinuousDynamic . ContinuousDynamic . DynamicType === ContDynamicEnum . diminuendo ) { if ( sameStaffLine ) { graphicalContinuousDynamic . createDiminuendoLines ( upperStartX , upperEndX , idealY ) ; graphicalContinuousDynamic . calcPsi ( ) ; } else { graphicalContinuousDynamic . createFirstHalfDiminuendoLines ( upperStartX , upperEndX , idealY ) ; graphicalContinuousDynamic . calcPsi ( ) ; secondGraphicalContinuousDynamic . createSecondHalfDiminuendoLines ( lowerStartX , lowerEndX , secondIdealY ) ; secondGraphicalContinuousDynamic . calcPsi ( ) ; } } this . dynamicExpressionMap . set ( endAbsoluteTimestamp . RealValue , graphicalContinuousDynamic . PositionAndShape ) ; } param graphicalInstantaneousDynamic param startPosInStaffline protected calculateGraphicalInstantaneousDynamicExpression ( graphicalInstantaneousDynamic , startPosInStaffline , timestamp ) { const staffLine = graphicalInstantaneousDynamic . ParentStaffLine ; if ( ! staffLine ) { return ; } const left = startPosInStaffline . x + graphicalInstantaneousDynamic . PositionAndShape . BorderMarginLeft ; const right = startPosInStaffline . x + graphicalInstantaneousDynamic . PositionAndShape . BorderMarginRight ; const skyBottomLineCalculator = staffLine . SkyBottomLineCalculator ; let yPosition = 0 ; if ( graphicalInstantaneousDynamic . Placement === PlacementEnum . Above ) { const skyLineValue = skyBottomLineCalculator . getSkyLineMinInRange ( left , right ) ; if ( staffLine . isPartOfMultiStaffInstrument ( ) && staffLine . ParentStaff !== staffLine . ParentStaff . ParentInstrument . Staves [ 0 ] ) { const formerStaffLine = staffLine . ParentMusicSystem . StaffLines [ staffLine . ParentMusicSystem . StaffLines . indexOf ( staffLine ) - 1 ] ; const difference = staffLine . PositionAndShape . RelativePosition . y - formerStaffLine . PositionAndShape . RelativePosition . y - this . rules . StaffHeight ; if ( skyLineValue > - difference / 2 ) { yPosition = - difference / 2 ; } else { yPosition = skyLineValue - graphicalInstantaneousDynamic . PositionAndShape . BorderMarginBottom ; } } else { yPosition = skyLineValue - graphicalInstantaneousDynamic . PositionAndShape . BorderMarginBottom ; } graphicalInstantaneousDynamic . PositionAndShape . RelativePosition = new PointF2D ( startPosInStaffline . x , yPosition ) ; } else if ( graphicalInstantaneousDynamic . Placement === PlacementEnum . Below ) { const bottomLineValue = skyBottomLineCalculator . getBottomLineMaxInRange ( left , right ) ; const lastStaff = staffLine . ParentStaff . ParentInstrument . Staves [ staffLine . ParentStaff . ParentInstrument . Staves . length - 1 ] ; if ( staffLine . isPartOfMultiStaffInstrument ( ) && staffLine . ParentStaff !== lastStaff ) { const nextStaffLine = staffLine . ParentMusicSystem . StaffLines [ staffLine . ParentMusicSystem . StaffLines . indexOf ( staffLine ) + 1 ] ; const difference = nextStaffLine . PositionAndShape . RelativePosition . y - staffLine . PositionAndShape . RelativePosition . y - this . rules . StaffHeight ; const border = graphicalInstantaneousDynamic . PositionAndShape . BorderMarginBottom ; if ( bottomLineValue + border < this . rules . StaffHeight + difference / 2 ) { yPosition = this . rules . StaffHeight + difference / 2 ; } else { yPosition = bottomLineValue - graphicalInstantaneousDynamic . PositionAndShape . BorderMarginTop ; } } else { yPosition = bottomLineValue - graphicalInstantaneousDynamic . PositionAndShape . BorderMarginTop ; } graphicalInstantaneousDynamic . PositionAndShape . RelativePosition = new PointF2D ( startPosInStaffline . x , yPosition ) ; } graphicalInstantaneousDynamic . updateSkyBottomLine ( ) ; } protected calcGraphicalRepetitionEndingsRecursively ( repetition ) { return ; } param start param end param numberText param offset param leftOpen param rightOpen protected layoutSingleRepetitionEnding ( start , end , numberText , offset , leftOpen , rightOpen ) { return ; } protected calculateLabel ( staffLine , relative , combinedString , style , placement , fontHeight , textAlignment = TextAlignmentEnum . CenterBottom ) { const label = new Label ( combinedString , textAlignment ) ; label . fontStyle = style ; label . fontHeight = fontHeight ; const graphLabel = new GraphicalLabel ( label , fontHeight , label . textAlignment , this . rules , staffLine . PositionAndShape ) ; const marginFactor = 1.1 ; if ( placement === PlacementEnum . Below ) { graphLabel . Label . textAlignment = TextAlignmentEnum . LeftTop ; } graphLabel . setLabelPositionAndShapeBorders ( ) ; graphLabel . PositionAndShape . BorderMarginBottom *= marginFactor ; graphLabel . PositionAndShape . BorderMarginTop *= marginFactor ; graphLabel . PositionAndShape . BorderMarginLeft *= marginFactor ; graphLabel . PositionAndShape . BorderMarginRight *= marginFactor ; let left = relative . x + graphLabel . PositionAndShape . BorderMarginLeft ; let right = relative . x + graphLabel . PositionAndShape . BorderMarginRight ; if ( right > staffLine . PositionAndShape . Size . width ) { right = staffLine . PositionAndShape . Size . width - this . rules . MeasureRightMargin ; left = right - graphLabel . PositionAndShape . MarginSize . width ; relative . x = left - graphLabel . PositionAndShape . BorderMarginLeft ; } let drawingHeight ; const skyBottomLineCalculator = staffLine . SkyBottomLineCalculator ; if ( placement === PlacementEnum . Below ) { drawingHeight = skyBottomLineCalculator . getBottomLineMaxInRange ( left , right ) ; } else { drawingHeight = skyBottomLineCalculator . getSkyLineMinInRange ( left , right ) ; } graphLabel . PositionAndShape . RelativePosition = new PointF2D ( relative . x , drawingHeight ) ; if ( placement === PlacementEnum . Below ) { skyBottomLineCalculator . updateBottomLineInRange ( left , right , graphLabel . PositionAndShape . BorderMarginBottom + drawingHeight ) ; } else { skyBottomLineCalculator . updateSkyLineInRange ( left , right , graphLabel . PositionAndShape . BorderMarginTop + drawingHeight ) ; } return graphLabel ; } protected calculateTempoExpressionsForMultiTempoExpression ( sourceMeasure , multiTempoExpression , measureIndex ) { const absoluteTimestamp = Fraction . plus ( sourceMeasure . AbsoluteTimestamp , multiTempoExpression . Timestamp ) ; const measures = this . graphicalMusicSheet . MeasureList [ measureIndex ] ; let relative = new PointF2D ( ) ; if ( multiTempoExpression . ContinuousTempo || multiTempoExpression . InstantaneousTempo ) { if ( this . rules . MinMeasureToDrawIndex > 0 ) { return ; } if ( ! measures [ 0 ] ) { return ; } let staffLine = measures [ 0 ] . ParentStaffLine ; let firstVisibleMeasureX = measures [ 0 ] . PositionAndShape . RelativePosition . x ; let verticalIndex = 0 ; for ( let j = 0 ; j < measures . length ; j ++ ) { if ( ! measures [ j ] . ParentStaffLine || measures [ j ] . ParentStaffLine . Measures . length === 0 ) { continue ; } if ( measures [ j ] . ParentStaffLine . Measures . length > 0 ) { staffLine = measures [ j ] . ParentStaffLine ; firstVisibleMeasureX = measures [ j ] . PositionAndShape . RelativePosition . x ; verticalIndex = j ; break ; } } relative = this . getRelativePositionInStaffLineFromTimestamp ( absoluteTimestamp , verticalIndex , staffLine , staffLine . isPartOfMultiStaffInstrument ( ) , firstVisibleMeasureX ) ; if ( multiTempoExpression . EntriesList . length > 0 && multiTempoExpression . EntriesList [ 0 ] . Expression instanceof InstantaneousTempoExpression ) { const instantaniousTempo = ( multiTempoExpression . EntriesList [ 0 ] . Expression as InstantaneousTempoExpression ) ; instantaniousTempo . Placement = PlacementEnum . Above ; if ( staffLine . Measures [ 0 ] . staffEntries . length > 0 && Math . abs ( relative . x - staffLine . Measures [ 0 ] . staffEntries [ 0 ] . PositionAndShape . RelativePosition . x ) === 0 && staffLine . ParentMusicSystem === this . musicSystems [ 0 ] ) { const firstInstructionEntry = staffLine . Measures [ 0 ] . FirstInstructionStaffEntry ; if ( firstInstructionEntry ) { const lastInstruction = firstInstructionEntry . GraphicalInstructions . last ( ) ; relative . x = lastInstruction . PositionAndShape . RelativePosition . x ; } if ( this . rules . CompactMode ) { relative . x = staffLine . PositionAndShape . RelativePosition . x + staffLine . Measures [ 0 ] . PositionAndShape . RelativePosition . x ; } } } for ( const entry of multiTempoExpression . EntriesList ) { let textAlignment = TextAlignmentEnum . CenterBottom ; if ( this . rules . CompactMode ) { textAlignment = TextAlignmentEnum . LeftBottom ; } const graphLabel = this . calculateLabel ( staffLine , relative , entry . label , multiTempoExpression . getFontstyleOfFirstEntry ( ) , entry . Expression . Placement , this . rules . UnknownTextHeight , textAlignment ) ; if ( entry . Expression instanceof InstantaneousTempoExpression ) { for ( const expr of staffLine . AbstractExpressions ) { if ( expr instanceof GraphicalInstantaneousTempoExpression && ( expr . SourceExpression as AbstractTempoExpression ) . Label === entry . Expression . Label ) { continue ; } } const graphicalTempoExpr = new GraphicalInstantaneousTempoExpression ( entry . Expression , graphLabel ) ; if ( ! graphicalTempoExpr . ParentStaffLine ) { log . warn ( "Adding staffline didn't work" ) ; } if ( this . rules . MetronomeMarksDrawn ) { if ( ( entry . Expression as InstantaneousTempoExpression ) . Enum === TempoEnum . metronomeMark ) { this . createMetronomeMark ( ( entry . Expression as InstantaneousTempoExpression ) ) ; continue ; } } } else if ( entry . Expression instanceof ContinuousTempoExpression ) { } } } } protected createMetronomeMark ( metronomeExpression ) { throw new Error ( this . abstractNotImplementedErrorMessage ) ; } protected graphicalMeasureCreatedCalculations ( measure ) { return ; } protected clearSystemsAndMeasures ( ) { for ( let idx = 0 , len = this . graphicalMusicSheet . MusicPages . length ; idx < len ; ++ idx ) { const graphicalMusicPage = this . graphicalMusicSheet . MusicPages [ idx ] ; for ( let idx2 = 0 , len2 = graphicalMusicPage . MusicSystems . length ; idx2 < len2 ; ++ idx2 ) { const musicSystem = graphicalMusicPage . MusicSystems [ idx2 ] ; for ( let idx3 = 0 , len3 = musicSystem . StaffLines . length ; idx3 < len3 ; ++ idx3 ) { const staffLine = musicSystem . StaffLines [ idx3 ] ; for ( let idx4 = 0 , len4 = staffLine . Measures . length ; idx4 < len4 ; ++ idx4 ) { const graphicalMeasure = staffLine . Measures [ idx4 ] ; if ( graphicalMeasure . FirstInstructionStaffEntry ) { const index = graphicalMeasure . PositionAndShape . ChildElements . indexOf ( graphicalMeasure . FirstInstructionStaffEntry . PositionAndShape ) ; if ( index > - 1 ) { graphicalMeasure . PositionAndShape . ChildElements . splice ( index , 1 ) ; } graphicalMeasure . FirstInstructionStaffEntry = undefined ; graphicalMeasure . beginInstructionsWidth = 0.0 ; } if ( graphicalMeasure . LastInstructionStaffEntry ) { const index = graphicalMeasure . PositionAndShape . ChildElements . indexOf ( graphicalMeasure . LastInstructionStaffEntry . PositionAndShape ) ; if ( index > - 1 ) { graphicalMeasure . PositionAndShape . ChildElements . splice ( index , 1 ) ; } graphicalMeasure . LastInstructionStaffEntry = undefined ; graphicalMeasure . endInstructionsWidth = 0.0 ; } } staffLine . Measures = [ ] ; staffLine . PositionAndShape . ChildElements = [ ] ; } musicSystem . StaffLines . length = 0 ; musicSystem . PositionAndShape . ChildElements = [ ] ; } graphicalMusicPage . MusicSystems = [ ] ; graphicalMusicPage . PositionAndShape . ChildElements = [ ] ; } this . graphicalMusicSheet . MusicPages = [ ] ; } protected handleVoiceEntry ( voiceEntry , graphicalStaffEntry , accidentalCalculator , openLyricWords , activeClef , openTuplets , openBeams , octaveShiftValue , staffIndex , linkedNotes = undefined , sourceStaffEntry = undefined ) { if ( voiceEntry . StemDirectionXml !== StemDirectionType . Undefined && this . rules . SetWantedStemDirectionByXml && voiceEntry . StemDirectionXml !== undefined ) { voiceEntry . WantedStemDirection = voiceEntry . StemDirectionXml ; } else { this . calculateStemDirectionFromVoices ( voiceEntry ) ; } const gve = graphicalStaffEntry . findOrCreateGraphicalVoiceEntry ( voiceEntry ) ; gve . octaveShiftValue = octaveShiftValue ; const tabStaffEntry = graphicalStaffEntry . tabStaffEntry ; let graphicalTabVoiceEntry ; if ( tabStaffEntry ) { graphicalTabVoiceEntry = tabStaffEntry . findOrCreateGraphicalVoiceEntry ( voiceEntry ) ; } for ( let idx = 0 , len = voiceEntry . Notes . length ; idx < len ; ++ idx ) { const note = voiceEntry . Notes [ idx ] ; if ( ! note ) { continue ; } if ( sourceStaffEntry !== undefined && sourceStaffEntry . Link !== undefined && linkedNotes !== undefined && linkedNotes . indexOf ( note ) > - 1 ) { continue ; } let graphicalNote ; if ( voiceEntry . IsGrace ) { graphicalNote = MusicSheetCalculator . symbolFactory . createGraceNote ( note , gve , activeClef , this . rules , octaveShiftValue ) ; } else { graphicalNote = MusicSheetCalculator . symbolFactory . createNote ( note , gve , activeClef , octaveShiftValue , this . rules , undefined ) ; MusicSheetCalculator . stafflineNoteCalculator . trackNote ( graphicalNote ) ; } if ( note . Pitch ) { this . checkNoteForAccidental ( graphicalNote , accidentalCalculator , activeClef , octaveShiftValue ) ; } this . resetYPositionForLeadSheet ( graphicalNote . PositionAndShape ) ; graphicalStaffEntry . addGraphicalNoteToListAtCorrectYPosition ( gve , graphicalNote ) ; graphicalNote . PositionAndShape . calculateBoundingBox ( ) ; if ( ! this . leadSheet ) { if ( note . NoteBeam !== undefined && note . PrintObject ) { this . handleBeam ( graphicalNote , note . NoteBeam , openBeams ) ; } if ( note . NoteTuplet !== undefined && note . PrintObject ) { this . handleTuplet ( graphicalNote , note . NoteTuplet , openTuplets ) ; } } if ( graphicalTabVoiceEntry ) { const graphicalTabNote = MusicSheetCalculator . symbolFactory . createNote ( note , graphicalTabVoiceEntry , activeClef , octaveShiftValue , this . rules , undefined ) ; tabStaffEntry . addGraphicalNoteToListAtCorrectYPosition ( graphicalTabVoiceEntry , graphicalTabNote ) ; graphicalTabNote . PositionAndShape . calculateBoundingBox ( ) ; if ( ! this . leadSheet ) { if ( note . NoteTuplet ) { this . handleTuplet ( graphicalTabNote , note . NoteTuplet , openTuplets ) ; } } } } if ( voiceEntry . Articulations . length > 0 ) { this . handleVoiceEntryArticulations ( voiceEntry . Articulations , voiceEntry , graphicalStaffEntry ) ; } if ( voiceEntry . TechnicalInstructions . length > 0 ) { this . handleVoiceEntryTechnicalInstructions ( voiceEntry . TechnicalInstructions , voiceEntry , graphicalStaffEntry ) ; } if ( voiceEntry . LyricsEntries . size ( ) > 0 ) { this . handleVoiceEntryLyrics ( voiceEntry , graphicalStaffEntry , openLyricWords ) ; } if ( voiceEntry . OrnamentContainer ) { this . handleVoiceEntryOrnaments ( voiceEntry . OrnamentContainer , voiceEntry , graphicalStaffEntry ) ; } return octaveShiftValue ; } protected resetYPositionForLeadSheet ( psi ) { if ( this . leadSheet ) { psi . RelativePosition = new PointF2D ( psi . RelativePosition . x , 0.0 ) ; } } protected layoutVoiceEntries ( graphicalStaffEntry , staffIndex ) { graphicalStaffEntry . PositionAndShape . RelativePosition = new PointF2D ( 0.0 , 0.0 ) ; if ( ! this . leadSheet ) { for ( const gve of graphicalStaffEntry . graphicalVoiceEntries ) { const graphicalNotes = gve . notes ; if ( graphicalNotes . length === 0 ) { continue ; } const voiceEntry = graphicalNotes [ 0 ] . sourceNote . ParentVoiceEntry ; const hasPitchedNote = graphicalNotes [ 0 ] . sourceNote . Pitch !== undefined ; this . layoutVoiceEntry ( voiceEntry , graphicalNotes , graphicalStaffEntry , hasPitchedNote ) ; } } } protected maxInstrNameLabelLength ( ) { let maxLabelLength = 0.0 ; for ( const instrument of this . graphicalMusicSheet . ParentMusicSheet . Instruments ) { if ( instrument . NameLabel ?. print && instrument . Voices . length > 0 && instrument . Voices [ 0 ] . Visible ) { let renderedLabel = instrument . NameLabel ; if ( ! this . rules . RenderPartNames ) { renderedLabel = new Label ( "" , renderedLabel . textAlignment , renderedLabel . font ) ; } const graphicalLabel = new GraphicalLabel ( renderedLabel , this . rules . InstrumentLabelTextHeight , TextAlignmentEnum . LeftCenter , this . rules ) ; graphicalLabel . setLabelPositionAndShapeBorders ( ) ; maxLabelLength = Math . max ( maxLabelLength , graphicalLabel . PositionAndShape . MarginSize . width ) ; } } if ( ! this . rules . RenderPartNames ) { return 0 ; } return maxLabelLength ; } protected calculateSheetLabelBoundingBoxes ( ) { const musicSheet = this . graphicalMusicSheet . ParentMusicSheet ; const defaultColorTitle = this . rules . DefaultColorTitle ; if ( musicSheet . Title !== undefined && this . rules . RenderTitle ) { const title = new GraphicalLabel ( musicSheet . Title , this . rules . SheetTitleHeight , TextAlignmentEnum . CenterBottom , this . rules ) ; title . Label . IsCreditLabel = true ; title . Label . colorDefault = defaultColorTitle ; this . graphicalMusicSheet . Title = title ; title . setLabelPositionAndShapeBorders ( ) ; } else if ( ! this . rules . RenderTitle ) { this . graphicalMusicSheet . Title = undefined ; } if ( musicSheet . Subtitle !== undefined && this . rules . RenderSubtitle ) { const subtitle = new GraphicalLabel ( musicSheet . Subtitle , this . rules . SheetSubtitleHeight , TextAlignmentEnum . CenterCenter , this . rules ) ; subtitle . Label . IsCreditLabel = true ; subtitle . Label . colorDefault = defaultColorTitle ; this . graphicalMusicSheet . Subtitle = subtitle ; subtitle . setLabelPositionAndShapeBorders ( ) ; } else if ( ! this . rules . RenderSubtitle ) { this . graphicalMusicSheet . Subtitle = undefined ; } if ( musicSheet . Composer !== undefined && this . rules . RenderComposer ) { const composer = new GraphicalLabel ( musicSheet . Composer , this . rules . SheetComposerHeight , TextAlignmentEnum . RightCenter , this . rules ) ; composer . Label . IsCreditLabel = true ; composer . Label . colorDefault = defaultColorTitle ; this . graphicalMusicSheet . Composer = composer ; composer . setLabelPositionAndShapeBorders ( ) ; } else if ( ! this . rules . RenderComposer ) { this . graphicalMusicSheet . Composer = undefined ; } if ( musicSheet . Lyricist !== undefined && this . rules . RenderLyricist ) { const lyricist = new GraphicalLabel ( musicSheet . Lyricist , this . rules . SheetAuthorHeight , TextAlignmentEnum . LeftCenter , this . rules ) ; lyricist . Label . IsCreditLabel = true ; lyricist . Label . colorDefault = defaultColorTitle ; this . graphicalMusicSheet . Lyricist = lyricist ; lyricist . setLabelPositionAndShapeBorders ( ) ; } else if ( ! this . rules . RenderLyricist ) { this . graphicalMusicSheet . Lyricist = undefined ; } } protected checkMeasuresForWholeRestNotes ( ) { for ( let idx2 = 0 , len2 = this . musicSystems . length ; idx2 < len2 ; ++ idx2 ) { const musicSystem = this . musicSystems [ idx2 ] ; for ( let idx3 = 0 , len3 = musicSystem . StaffLines . length ; idx3 < len3 ; ++ idx3 ) { const staffLine = musicSystem . StaffLines [ idx3 ] ; for ( let idx4 = 0 , len4 = staffLine . Measures . length ; idx4 < len4 ; ++ idx4 ) { const measure = staffLine . Measures [ idx4 ] ; if ( measure . staffEntries . length === 1 ) { const gse = measure . staffEntries [ 0 ] ; if ( gse . graphicalVoiceEntries . length > 0 && gse . graphicalVoiceEntries [ 0 ] . notes . length === 1 ) { const graphicalNote = gse . graphicalVoiceEntries [ 0 ] . notes [ 0 ] ; if ( ! graphicalNote . sourceNote . Pitch && ( new Fraction ( 1 , 2 ) ) . lt ( graphicalNote . sourceNote . Length ) ) { this . layoutMeasureWithWholeRest ( graphicalNote , gse , measure ) ; } } } } } } } protected optimizeRestNotePlacement ( graphicalStaffEntry , measure ) { if ( graphicalStaffEntry . graphicalVoiceEntries . length === 0 ) { return ; } const voice1Notes = graphicalStaffEntry . graphicalVoiceEntries [ 0 ] . notes ; if ( voice1Notes . length === 0 ) { return ; } const voice1Note1 = voice1Notes [ 0 ] ; const voice1Note1IsRest = voice1Note1 . sourceNote . isRest ( ) ; if ( graphicalStaffEntry . graphicalVoiceEntries . length === 2 ) { let voice2Note1IsRest = false ; const voice2Notes = graphicalStaffEntry . graphicalVoiceEntries [ 1 ] . notes ; if ( voice2Notes . length > 0 ) { const voice2Note1 = voice2Notes [ 0 ] ; voice2Note1IsRest = voice2Note1 . sourceNote . isRest ( ) ; } if ( voice1Note1IsRest && voice2Note1IsRest ) { this . calculateTwoRestNotesPlacementWithCollisionDetection ( graphicalStaffEntry ) ; } else if ( voice1Note1IsRest || voice2Note1IsRest ) { this . calculateRestNotePlacementWithCollisionDetectionFromGraphicalNote ( graphicalStaffEntry ) ; } } else if ( voice1Note1IsRest && graphicalStaffEntry !== measure . staffEntries [ 0 ] && graphicalStaffEntry !== measure . staffEntries [ measure . staffEntries . length - 1 ] ) { const staffEntryIndex = measure . staffEntries . indexOf ( graphicalStaffEntry ) ; const previousStaffEntry = measure . staffEntries [ staffEntryIndex - 1 ] ; const nextStaffEntry = measure . staffEntries [ staffEntryIndex + 1 ] ; if ( previousStaffEntry . graphicalVoiceEntries . length === 1 ) { const previousNote = previousStaffEntry . graphicalVoiceEntries [ 0 ] . notes [ 0 ] ; if ( previousNote . sourceNote . NoteBeam !== undefined && nextStaffEntry . graphicalVoiceEntries . length === 1 ) { const nextNote = nextStaffEntry . graphicalVoiceEntries [ 0 ] . notes [ 0 ] ; if ( nextNote . sourceNote . NoteBeam !== undefined && previousNote . sourceNote . NoteBeam === nextNote . sourceNote . NoteBeam ) { this . calculateRestNotePlacementWithinGraphicalBeam ( graphicalStaffEntry , voice1Note1 , previousNote , nextStaffEntry , nextNote ) ; graphicalStaffEntry . PositionAndShape . calculateBoundingBox ( ) ; } } } } } protected getRelativePositionInStaffLineFromTimestamp ( timestamp , verticalIndex , staffLine , multiStaffInstrument , firstVisibleMeasureRelativeX = 0.0 ) { let relative = new PointF2D ( ) ; let leftStaffEntry = undefined ; let rightStaffEntry = undefined ; const numEntries = this . graphicalMusicSheet . VerticalGraphicalStaffEntryContainers . length ; const index = this . graphicalMusicSheet . GetInterpolatedIndexInVerticalContainers ( timestamp ) ; const leftIndex = Math . min ( Math . floor ( index ) , numEntries - 1 ) ; const rightIndex = Math . min ( Math . ceil ( index ) , numEntries - 1 ) ; if ( leftIndex < 0 || verticalIndex < 0 ) { return relative ; } leftStaffEntry = this . getFirstLeftNotNullStaffEntryFromContainer ( leftIndex , verticalIndex , multiStaffInstrument ) ; rightStaffEntry = this . getFirstRightNotNullStaffEntryFromContainer ( rightIndex , verticalIndex , multiStaffInstrument ) ; if ( leftStaffEntry && rightStaffEntry ) { let measureRelativeX = leftStaffEntry . parentMeasure . PositionAndShape . RelativePosition . x ; if ( firstVisibleMeasureRelativeX > 0 ) { measureRelativeX = firstVisibleMeasureRelativeX ; } let leftX = leftStaffEntry . PositionAndShape . RelativePosition . x + measureRelativeX ; let rightX = rightStaffEntry . PositionAndShape . RelativePosition . x + rightStaffEntry . parentMeasure . PositionAndShape . RelativePosition . x ; if ( firstVisibleMeasureRelativeX > 0 ) { rightX = rightStaffEntry . PositionAndShape . RelativePosition . x + measureRelativeX ; } let timestampQuotient = 0.0 ; if ( leftStaffEntry !== rightStaffEntry ) { const leftTimestamp = leftStaffEntry . getAbsoluteTimestamp ( ) ; const rightTimestamp = rightStaffEntry . getAbsoluteTimestamp ( ) ; const leftDifference = Fraction . minus ( timestamp , leftTimestamp ) ; timestampQuotient = leftDifference . RealValue / Fraction . minus ( rightTimestamp , leftTimestamp ) . RealValue ; } if ( leftStaffEntry . parentMeasure . ParentStaffLine !== rightStaffEntry . parentMeasure . ParentStaffLine ) { if ( leftStaffEntry . parentMeasure . ParentStaffLine === staffLine ) { rightX = staffLine . PositionAndShape . Size . width ; } else { leftX = staffLine . PositionAndShape . RelativePosition . x ; } } relative = new PointF2D ( leftX + ( rightX - leftX ) * timestampQuotient , 0.0 ) ; } return relative ; } protected getRelativeXPositionFromTimestamp ( timestamp ) { const numEntries = this . graphicalMusicSheet . VerticalGraphicalStaffEntryContainers . length ; const index = this . graphicalMusicSheet . GetInterpolatedIndexInVerticalContainers ( timestamp ) ; const discreteIndex = Math . max ( 0 , Math . min ( Math . round ( index ) , numEntries - 1 ) ) ; const gse = this . graphicalMusicSheet . VerticalGraphicalStaffEntryContainers [ discreteIndex ] . getFirstNonNullStaffEntry ( ) ; const posX = gse . PositionAndShape . RelativePosition . x + gse . parentMeasure . PositionAndShape . RelativePosition . x ; return posX ; } protected calculatePageLabels ( page ) { if ( this . rules . RenderSingleHorizontalStaffline ) { page . PositionAndShape . BorderRight = page . PositionAndShape . Size . width ; page . PositionAndShape . calculateBoundingBox ( ) ; this . graphicalMusicSheet . ParentMusicSheet . pageWidth = page . PositionAndShape . Size . width ; } let firstSystemAbsoluteTopMargin = 10 ; if ( page . MusicSystems . length > 0 ) { const firstMusicSystem = page . MusicSystems [ 0 ] ; firstSystemAbsoluteTopMargin = firstMusicSystem . PositionAndShape . RelativePosition . y + firstMusicSystem . PositionAndShape . BorderTop ; } if ( this . graphicalMusicSheet . Title ) { const title = this . graphicalMusicSheet . Title ; title . PositionAndShape . Parent = page . PositionAndShape ; const relative = new PointF2D ( ) ; relative . x = this . graphicalMusicSheet . ParentMusicSheet . pageWidth / 2 ; relative . y = this . rules . TitleTopDistance + this . rules . SheetTitleHeight ; title . PositionAndShape . RelativePosition = relative ; page . Labels . push ( title ) ; } if ( this . graphicalMusicSheet . Subtitle ) { const subtitle = this . graphicalMusicSheet . Subtitle ; subtitle . PositionAndShape . Parent = page . PositionAndShape ; const relative = new PointF2D ( ) ; relative . x = this . graphicalMusicSheet . ParentMusicSheet . pageWidth / 2 ; relative . y = this . rules . TitleTopDistance + this . rules . SheetTitleHeight + this . rules . SheetMinimumDistanceBetweenTitleAndSubtitle ; subtitle . PositionAndShape . RelativePosition = relative ; page . Labels . push ( subtitle ) ; } const composer = this . graphicalMusicSheet . Composer ; let composerRelativeY ; if ( composer ) { composer . PositionAndShape . Parent = page . PositionAndShape ; composer . setLabelPositionAndShapeBorders ( ) ; const relative = new PointF2D ( ) ; relative . x = this . graphicalMusicSheet . ParentMusicSheet . pageWidth - this . rules . PageRightMargin ; relative . y = firstSystemAbsoluteTopMargin ; relative . y -= this . rules . SystemComposerDistance ; const lines = composer . TextLines ?. length ; if ( lines > 1 ) { relative . y -= composer . PositionAndShape . BorderBottom * ( lines - 1 ) / ( lines ) ; } composerRelativeY = relative . y ; composer . PositionAndShape . RelativePosition = relative ; page . Labels . push ( composer ) ; } const lyricist = this . graphicalMusicSheet . Lyricist ; if ( lyricist ) { lyricist . PositionAndShape . Parent = page . PositionAndShape ; lyricist . setLabelPositionAndShapeBorders ( ) ; const relative = new PointF2D ( ) ; relative . x = this . rules . PageLeftMargin ; relative . y = firstSystemAbsoluteTopMargin ; relative . y += lyricist . PositionAndShape . BorderBottom ; relative . y = Math . min ( relative . y , composerRelativeY ) ; lyricist . PositionAndShape . RelativePosition = relative ; page . Labels . push ( lyricist ) ; } } protected createGraphicalTies ( ) { for ( let measureIndex = 0 ; measureIndex < this . graphicalMusicSheet . ParentMusicSheet . SourceMeasures . length ; measureIndex ++ ) { const sourceMeasure = this . graphicalMusicSheet . ParentMusicSheet . SourceMeasures [ measureIndex ] ; for ( let staffIndex = 0 ; staffIndex < sourceMeasure . CompleteNumberOfStaves ; staffIndex ++ ) { for ( let j = 0 ; j < sourceMeasure . VerticalSourceStaffEntryContainers . length ; j ++ ) { const sourceStaffEntry = sourceMeasure . VerticalSourceStaffEntryContainers [ j ] . StaffEntries [ staffIndex ] ; if ( sourceStaffEntry ) { const startStaffEntry = this . graphicalMusicSheet . findGraphicalStaffEntryFromMeasureList ( staffIndex , measureIndex , sourceStaffEntry ) ; for ( let idx = 0 , len = sourceStaffEntry . VoiceEntries . length ; idx < len ; ++ idx ) { const voiceEntry = sourceStaffEntry . VoiceEntries [ idx ] ; for ( let idx2 = 0 , len2 = voiceEntry . Notes . length ; idx2 < len2 ; ++ idx2 ) { const note = voiceEntry . Notes [ idx2 ] ; if ( note . NoteTie ) { const tie = note . NoteTie ; this . handleTie ( tie , startStaffEntry , staffIndex , measureIndex ) ; } } } this . setTieDirections ( startStaffEntry ) ; } } } } } private handleTie ( tie , startGraphicalStaffEntry , staffIndex , measureIndex ) { if ( ! startGraphicalStaffEntry ) { return ; } startGraphicalStaffEntry . ties . push ( tie ) ; let startGse = startGraphicalStaffEntry ; let startNote = undefined ; let endGse = undefined ; let endNote = undefined ; for ( let i = 1 ; i < tie . Notes . length ; i ++ ) { startNote = startGse . findTieGraphicalNoteFromNote ( tie . Notes [ i - 1 ] ) ; endGse = this . graphicalMusicSheet . GetGraphicalFromSourceStaffEntry ( tie . Notes [ i ] . ParentStaffEntry ) ; if ( ! endGse ) { continue ; } endNote = endGse . findTieGraphicalNoteFromNote ( tie . Notes [ i ] ) ; if ( startNote !== undefined && endNote !== undefined && endGse ) { if ( ! startNote . sourceNote . PrintObject || ! endNote . sourceNote . PrintObject ) { continue ; } const graphicalTie = this . createGraphicalTie ( tie , startGse , endGse , startNote , endNote ) ; startGse . GraphicalTies . push ( graphicalTie ) ; if ( this . staffEntriesWithGraphicalTies . indexOf ( startGse ) >= 0 ) { this . staffEntriesWithGraphicalTies . push ( startGse ) ; } } startGse = endGse ; } } private setTieDirections ( staffEntry ) { if ( ! staffEntry ) { return ; } const ties = staffEntry . ties ; if ( ties . length > 1 ) { let highestNote = undefined ; for ( const gseTie of ties ) { const tieNote = gseTie . Notes [ 0 ] ; if ( ! highestNote || tieNote . Pitch . getHalfTone ( ) > highestNote . Pitch . getHalfTone ( ) ) { highestNote = tieNote ; } } for ( const gseTie of ties ) { if ( gseTie . TieDirection === PlacementEnum . NotYetDefined ) { if ( gseTie . Notes [ 0 ] === highestNote ) { gseTie . TieDirection = PlacementEnum . Above ; } else { gseTie . TieDirection = PlacementEnum . Below ; } } } } } private createAccidentalCalculators ( ) { const accidentalCalculators = [ ] ; const firstSourceMeasure = this . graphicalMusicSheet . ParentMusicSheet . getFirstSourceMeasure ( ) ; if ( firstSourceMeasure ) { for ( let i = 0 ; i < firstSourceMeasure . CompleteNumberOfStaves ; i ++ ) { const accidentalCalculator = new AccidentalCalculator ( ) ; accidentalCalculators . push ( accidentalCalculator ) ; if ( firstSourceMeasure . FirstInstructionsStaffEntries [ i ] ) { for ( let idx = 0 , len = firstSourceMeasure . FirstInstructionsStaffEntries [ i ] . Instructions . length ; idx < len ; ++ idx ) { const abstractNotationInstruction = firstSourceMeasure . FirstInstructionsStaffEntries [ i ] . Instructions [ idx ] ; if ( abstractNotationInstruction instanceof KeyInstruction ) { const keyInstruction = < KeyInstruction > abstractNotationInstruction ; accidentalCalculator . ActiveKeyInstruction = keyInstruction ; } } } } } return accidentalCalculators ; } private calculateVerticalContainersList ( ) { const numberOfEntries = this . graphicalMusicSheet . MeasureList [ 0 ] . length ; for ( let i = 0 ; i < this . graphicalMusicSheet . MeasureList . length ; i ++ ) { for ( let j = 0 ; j < numberOfEntries ; j ++ ) { const measure = this . graphicalMusicSheet . MeasureList [ i ] [ j ] ; if ( ! measure ) { continue ; } for ( let idx = 0 , len = measure . staffEntries . length ; idx < len ; ++ idx ) { const graphicalStaffEntry = measure . staffEntries [ idx ] ; const verticalContainer = this . graphicalMusicSheet . getOrCreateVerticalContainer ( graphicalStaffEntry . getAbsoluteTimestamp ( ) ) ; if ( verticalContainer ) { verticalContainer . StaffEntries [ j ] = graphicalStaffEntry ; graphicalStaffEntry . parentVerticalContainer = verticalContainer ; } } } } } private setIndicesToVerticalGraphicalContainers ( ) { for ( let i = 0 ; i < this . graphicalMusicSheet . VerticalGraphicalStaffEntryContainers . length ; i ++ ) { this . graphicalMusicSheet . VerticalGraphicalStaffEntryContainers [ i ] . Index = i ; } } private createGraphicalMeasuresForSourceMeasure ( sourceMeasure , accidentalCalculators , openLyricWords , openOctaveShifts , activeClefs ) { this . initGraphicalMeasuresCreation ( ) ; const verticalMeasureList = [ ] ; const openBeams = [ ] ; const openTuplets = [ ] ; const staffEntryLinks = [ ] ; let restInAllGraphicalMeasures = true ; for ( let staffIndex = 0 ; staffIndex < sourceMeasure . CompleteNumberOfStaves ; staffIndex ++ ) { const measure = this . createGraphicalMeasure ( sourceMeasure , openTuplets , openBeams , accidentalCalculators [ staffIndex ] , activeClefs , openOctaveShifts , openLyricWords , staffIndex , staffEntryLinks ) ; restInAllGraphicalMeasures = restInAllGraphicalMeasures && measure . hasOnlyRests ; verticalMeasureList . push ( measure ) ; } sourceMeasure . allRests = restInAllGraphicalMeasures ; sourceMeasure . VerticalMeasureList = verticalMeasureList ; return verticalMeasureList ; } private createGraphicalMeasure ( sourceMeasure , openTuplets , openBeams , accidentalCalculator , activeClefs , openOctaveShifts , openLyricWords , staffIndex , staffEntryLinks ) { const staff = this . graphicalMusicSheet . ParentMusicSheet . getStaffFromIndex ( staffIndex ) ; let measure = undefined ; if ( activeClefs [ staffIndex ] . ClefType === ClefEnum . TAB ) { staff . isTab = true ; measure = MusicSheetCalculator . symbolFactory . createTabStaffMeasure ( sourceMeasure , staff ) ; } else if ( sourceMeasure . multipleRestMeasures && this . rules . RenderMultipleRestMeasures ) { measure = MusicSheetCalculator . symbolFactory . createMultiRestMeasure ( sourceMeasure , staff ) ; } else if ( sourceMeasure . multipleRestMeasureNumber > 1 ) { return undefined ; } else { measure = MusicSheetCalculator . symbolFactory . createGraphicalMeasure ( sourceMeasure , staff ) ; } measure . hasError = sourceMeasure . getErrorInMeasure ( staffIndex ) ; if ( sourceMeasure . FirstInstructionsStaffEntries [ staffIndex ] ) { for ( let idx = 0 , len = sourceMeasure . FirstInstructionsStaffEntries [ staffIndex ] . Instructions . length ; idx < len ; ++ idx ) { const instruction = sourceMeasure . FirstInstructionsStaffEntries [ staffIndex ] . Instructions [ idx ] ; if ( instruction instanceof KeyInstruction ) { const key = KeyInstruction . copy ( instruction ) ; const transposeHalftones = measure . getTransposedHalftones ( ) ; if ( transposeHalftones !== 0 && measure . ParentStaff . ParentInstrument . MidiInstrumentId !== MidiInstrument . Percussion && MusicSheetCalculator . transposeCalculator ) { MusicSheetCalculator . transposeCalculator . transposeKey ( key , transposeHalftones ) ; } accidentalCalculator . ActiveKeyInstruction = key ; } } } const octaveShifts = [ ] ; for ( let idx = 0 , len = sourceMeasure . StaffLinkedExpressions [ staffIndex ] . length ; idx < len ; ++ idx ) { const multiExpression = sourceMeasure . StaffLinkedExpressions [ staffIndex ] [ idx ] ; if ( multiExpression . OctaveShiftStart ) { octaveShifts . push ( multiExpression ) ; const openOctaveShift = multiExpression . OctaveShiftStart ; let absoluteEnd = openOctaveShift ?. ParentEndMultiExpression ?. AbsoluteTimestamp ; if ( ! openOctaveShift ?. ParentEndMultiExpression ) { const measureEndTimestamp = Fraction . plus ( sourceMeasure . AbsoluteTimestamp , sourceMeasure . Duration ) ; absoluteEnd = measureEndTimestamp ; } openOctaveShifts [ staffIndex ] = new OctaveShiftParams ( openOctaveShift , multiExpression ?. AbsoluteTimestamp , absoluteEnd ) ; } } for ( let entryIndex = 0 ; entryIndex < sourceMeasure . VerticalSourceStaffEntryContainers . length ; entryIndex ++ ) { const sourceStaffEntry = sourceMeasure . VerticalSourceStaffEntryContainers [ entryIndex ] . StaffEntries [ staffIndex ] ; if ( sourceStaffEntry ) { for ( let idx = 0 , len = sourceStaffEntry . Instructions . length ; idx < len ; ++ idx ) { const abstractNotationInstruction = sourceStaffEntry . Instructions [ idx ] ; if ( abstractNotationInstruction instanceof ClefInstruction ) { activeClefs [ staffIndex ] = < ClefInstruction > abstractNotationInstruction ; } } const graphicalStaffEntry = MusicSheetCalculator . symbolFactory . createStaffEntry ( sourceStaffEntry , measure ) ; if ( entryIndex < measure . staffEntries . length ) { measure . addGraphicalStaffEntryAtTimestamp ( graphicalStaffEntry ) ; } else { measure . addGraphicalStaffEntry ( graphicalStaffEntry ) ; } const linkedNotes = [ ] ; if ( sourceStaffEntry . Link ) { sourceStaffEntry . findLinkedNotes ( linkedNotes ) ; this . handleStaffEntryLink ( graphicalStaffEntry , staffEntryLinks ) ; } let octaveShiftValue = OctaveEnum . NONE ; if ( openOctaveShifts [ staffIndex ] ) { if ( openOctaveShifts [ staffIndex ] . getAbsoluteStartTimestamp . lte ( sourceStaffEntry . AbsoluteTimestamp ) && sourceStaffEntry . AbsoluteTimestamp . lte ( openOctaveShifts [ staffIndex ] . getAbsoluteEndTimestamp ) ) { octaveShiftValue = openOctaveShifts [ staffIndex ] . getOpenOctaveShift . Type ; } } if ( octaveShiftValue === OctaveEnum . NONE ) { for ( const octaveShift of octaveShifts ) { if ( octaveShift . OctaveShiftStart ?. ParentStartMultiExpression ?. AbsoluteTimestamp . lte ( sourceStaffEntry . AbsoluteTimestamp ) && ! octaveShift . OctaveShiftStart ?. ParentEndMultiExpression ?. AbsoluteTimestamp . lt ( sourceStaffEntry . AbsoluteTimestamp ) ) { octaveShiftValue = octaveShift . OctaveShiftStart . Type ; break ; } } } for ( let idx = 0 , len = sourceStaffEntry . VoiceEntries . length ; idx < len ; ++ idx ) { const voiceEntry = sourceStaffEntry . VoiceEntries [ idx ] ; octaveShiftValue = this . handleVoiceEntry ( voiceEntry , graphicalStaffEntry , accidentalCalculator , openLyricWords , activeClefs [ staffIndex ] , openTuplets , openBeams , octaveShiftValue , staffIndex , linkedNotes , sourceStaffEntry ) ; } if ( sourceStaffEntry . Instructions . length > 0 ) { const clefInstruction = < ClefInstruction > sourceStaffEntry . Instructions [ 0 ] ; MusicSheetCalculator . symbolFactory . createInStaffClef ( graphicalStaffEntry , clefInstruction ) ; } if ( this . rules . RenderChordSymbols && sourceStaffEntry . ChordContainers ?. length > 0 ) { sourceStaffEntry . ParentStaff . ParentInstrument . HasChordSymbols = true ; MusicSheetCalculator . symbolFactory . createChordSymbols ( sourceStaffEntry , graphicalStaffEntry , accidentalCalculator . ActiveKeyInstruction , this . graphicalMusicSheet . ParentMusicSheet . Transpose ) ; } } } accidentalCalculator . doCalculationsAtEndOfMeasure ( ) ; if ( sourceMeasure . LastInstructionsStaffEntries [ staffIndex ] ) { const lastStaffEntry = sourceMeasure . LastInstructionsStaffEntries [ staffIndex ] ; for ( let idx = 0 , len = lastStaffEntry . Instructions . length ; idx < len ; ++ idx ) { const abstractNotationInstruction = lastStaffEntry . Instructions [ idx ] ; if ( abstractNotationInstruction instanceof ClefInstruction ) { activeClefs [ staffIndex ] = < ClefInstruction > abstractNotationInstruction ; } } } for ( let idx = 0 , len = sourceMeasure . StaffLinkedExpressions [ staffIndex ] . length ; idx < len ; ++ idx ) { const multiExpression = sourceMeasure . StaffLinkedExpressions [ staffIndex ] [ idx ] ; if ( multiExpression . OctaveShiftEnd !== undefined && openOctaveShifts [ staffIndex ] !== undefined && multiExpression . OctaveShiftEnd === openOctaveShifts [ staffIndex ] . getOpenOctaveShift ) { openOctaveShifts [ staffIndex ] = undefined ; } } for ( const staffEntry of measure . staffEntries ) { for ( const voiceEntry of staffEntry . graphicalVoiceEntries ) { this . setBeamNotesWantedStemDirections ( voiceEntry . parentVoiceEntry ) ; } } if ( this . rules . FillEmptyMeasuresWithWholeRest >= 1 ) { if ( measure . staffEntries . length === 0 ) { const sourceStaffEntry = new SourceStaffEntry ( new VerticalSourceStaffEntryContainer ( measure . parentSourceMeasure , measure . parentSourceMeasure . AbsoluteTimestamp , measure . parentSourceMeasure . CompleteNumberOfStaves ) , staff ) ; const voiceEntry = new VoiceEntry ( new Fraction ( 0 , 1 ) , staff . Voices [ 0 ] , sourceStaffEntry ) ; const note = new Note ( voiceEntry , sourceStaffEntry , Fraction . createFromFraction ( sourceMeasure . Duration ) , undefined , sourceMeasure ) ; note . PrintObject = this . rules . FillEmptyMeasuresWithWholeRest === FillEmptyMeasuresWithWholeRests . YesVisible ; voiceEntry . Notes . push ( note ) ; const graphicalStaffEntry = MusicSheetCalculator . symbolFactory . createStaffEntry ( sourceStaffEntry , measure ) ; measure . addGraphicalStaffEntry ( graphicalStaffEntry ) ; graphicalStaffEntry . relInMeasureTimestamp = voiceEntry . Timestamp ; const gve = MusicSheetCalculator . symbolFactory . createVoiceEntry ( voiceEntry , graphicalStaffEntry ) ; graphicalStaffEntry . graphicalVoiceEntries . push ( gve ) ; const graphicalNote = MusicSheetCalculator . symbolFactory . createNote ( note , gve , new ClefInstruction ( ) , OctaveEnum . NONE , undefined ) ; MusicSheetCalculator . stafflineNoteCalculator . trackNote ( graphicalNote ) ; gve . notes . push ( graphicalNote ) ; } } measure . hasOnlyRests = true ; for ( const graphicalStaffEntry of measure . staffEntries ) { measure . hasOnlyRests = graphicalStaffEntry . hasOnlyRests ( ) ; if ( ! measure . hasOnlyRests ) { break ; } } return measure ; } private checkNoteForAccidental ( graphicalNote , accidentalCalculator , activeClef , octaveEnum ) { let pitch = graphicalNote . sourceNote . Pitch ; const transposeHalftones = graphicalNote . parentVoiceEntry . parentStaffEntry . parentMeasure . getTransposedHalftones ( ) ; if ( transposeHalftones !== 0 && graphicalNote . sourceNote . ParentStaffEntry . ParentStaff . ParentInstrument . MidiInstrumentId !== MidiInstrument . Percussion ) { pitch = graphicalNote . Transpose ( accidentalCalculator . ActiveKeyInstruction , activeClef , transposeHalftones , octaveEnum ) ; graphicalNote . sourceNote . TransposedPitch = pitch ; } graphicalNote . sourceNote . halfTone = pitch . getHalfTone ( ) ; accidentalCalculator . checkAccidental ( graphicalNote , pitch ) ; } private handleStaffEntries ( staffIsPercussionArray ) { for ( let idx = 0 , len = this . graphicalMusicSheet . MeasureList . length ; idx < len ; ++ idx ) { const measures = this . graphicalMusicSheet . MeasureList [ idx ] ; for ( let idx2 = 0 , len2 = measures . length ; idx2 < len2 ; ++ idx2 ) { const measure = measures [ idx2 ] ; if ( ! measure ) { continue ; } if ( this . rules . PercussionOneLineCutoff !== undefined && this . rules . PercussionOneLineCutoff !== 0 ) { if ( staffIsPercussionArray [ idx2 ] ) { if ( this . rules . PercussionOneLineCutoff === - 1 || MusicSheetCalculator . stafflineNoteCalculator . getStafflineUniquePositionCount ( idx2 ) < this . rules . PercussionOneLineCutoff ) { measure . ParentStaff . StafflineCount = 1 ; } } } for ( const graphicalStaffEntry of measure . staffEntries ) { if ( graphicalStaffEntry . parentMeasure !== undefined && graphicalStaffEntry . graphicalVoiceEntries . length > 0 && graphicalStaffEntry . graphicalVoiceEntries [ 0 ] . notes . length > 0 ) { this . layoutVoiceEntries ( graphicalStaffEntry , idx2 ) ; this . layoutStaffEntry ( graphicalStaffEntry ) ; } } this . graphicalMeasureCreatedCalculations ( measure ) ; } } } private calculateSkyBottomLines ( ) { for ( const musicSystem of this . musicSystems ) { for ( const staffLine of musicSystem . StaffLines ) { staffLine . SkyBottomLineCalculator . calculateLines ( ) ; } } } /**
     * Re-adjust the x positioning of expressions.
     */ protected calculateExpressionAlignements ( ) { } private calculateStaffEntryArticulationMarks ( ) { for ( let idx2 = 0 , len2 = this . musicSystems . length ; idx2 < len2 ; ++ idx2 ) { const system = this . musicSystems [ idx2 ] ; for ( let idx3 = 0 , len3 = system . StaffLines . length ; idx3 < len3 ; ++ idx3 ) { const line = system . StaffLines [ idx3 ] ; for ( let idx4 = 0 , len4 = line . Measures . length ; idx4 < len4 ; ++ idx4 ) { const measure = line . Measures [ idx4 ] ; for ( let idx5 = 0 , len5 = measure . staffEntries . length ; idx5 < len5 ; ++ idx5 ) { const graphicalStaffEntry = measure . staffEntries [ idx5 ] ; for ( let idx6 = 0 , len6 = graphicalStaffEntry . sourceStaffEntry . VoiceEntries . length ; idx6 < len6 ; ++ idx6 ) { const voiceEntry = graphicalStaffEntry . sourceStaffEntry . VoiceEntries [ idx6 ] ; if ( voiceEntry . Articulations . length > 0 ) { this . layoutArticulationMarks ( voiceEntry . Articulations , voiceEntry , graphicalStaffEntry ) ; } } } } } } } private calculateOrnaments ( ) { for ( let idx2 = 0 , len2 = this . musicSystems . length ; idx2 < len2 ; ++ idx2 ) { const system = this . musicSystems [ idx2 ] ; for ( let idx3 = 0 , len3 = system . StaffLines . length ; idx3 < len3 ; ++ idx3 ) { const line = system . StaffLines [ idx3 ] ; for ( let idx4 = 0 , len4 = line . Measures . length ; idx4 < len4 ; ++ idx4 ) { const measure = line . Measures [ idx4 ] ; for ( let idx5 = 0 , len5 = measure . staffEntries . length ; idx5 < len5 ; ++ idx5 ) { const graphicalStaffEntry = measure . staffEntries [ idx5 ] ; for ( let idx6 = 0 , len6 = graphicalStaffEntry . sourceStaffEntry . VoiceEntries . length ; idx6 < len6 ; ++ idx6 ) { const voiceEntry = graphicalStaffEntry . sourceStaffEntry . VoiceEntries [ idx6 ] ; if ( voiceEntry . OrnamentContainer ) { if ( voiceEntry . hasTie ( ) && ! graphicalStaffEntry . relInMeasureTimestamp . Equals ( voiceEntry . Timestamp ) ) { continue ; } this . layoutOrnament ( voiceEntry . OrnamentContainer , voiceEntry , graphicalStaffEntry ) ; if ( ! ( this . staffEntriesWithOrnaments . indexOf ( graphicalStaffEntry ) !== - 1 ) ) { this . staffEntriesWithOrnaments . push ( graphicalStaffEntry ) ; } } } } } } } } public calculateFingerings ( ) { if ( this . rules . FingeringPosition === PlacementEnum . Left || this . rules . FingeringPosition === PlacementEnum . Right ) { return ; } for ( const system of this . musicSystems ) { for ( const line of system . StaffLines ) { for ( const measure of line . Measures ) { const placement = measure . isUpperStaffOfInstrument ( ) ? PlacementEnum . Above : PlacementEnum . Below ; for ( const gse of measure . staffEntries ) { gse . FingeringEntries = [ ] ; const skybottomcalculator = line . SkyBottomLineCalculator ; const staffEntryPositionX = gse . PositionAndShape . RelativePosition . x + measure . PositionAndShape . RelativePosition . x ; const fingerings = [ ] ; for ( const voiceEntry of gse . graphicalVoiceEntries ) { for ( const note of voiceEntry . notes ) { const sourceNote = note . sourceNote ; if ( sourceNote . Fingering && ! sourceNote . IsGraceNote ) { fingerings . push ( sourceNote . Fingering ) ; } } } if ( placement === PlacementEnum . Below ) { fingerings . reverse ( ) ; } for ( let i = 0 ; i < fingerings . length ; i ++ ) { const fingering = fingerings [ i ] ; const alignment = placement === PlacementEnum . Above ? TextAlignmentEnum . CenterBottom : TextAlignmentEnum . CenterTop ; const label = new Label ( fingering . value , alignment ) ; const gLabel = new GraphicalLabel ( label , this . rules . FingeringTextSize , label . textAlignment , this . rules , line . PositionAndShape ) ; const marginLeft = staffEntryPositionX + gLabel . PositionAndShape . BorderMarginLeft ; const marginRight = staffEntryPositionX + gLabel . PositionAndShape . BorderMarginRight ; let skybottomFurthest = undefined ; if ( placement === PlacementEnum . Above ) { skybottomFurthest = skybottomcalculator . getSkyLineMinInRange ( marginLeft , marginRight ) ; } else { skybottomFurthest = skybottomcalculator . getBottomLineMaxInRange ( marginLeft , marginRight ) ; } let yShift = 0 ; if ( i === 0 ) { yShift += this . rules . FingeringOffsetY ; if ( placement === PlacementEnum . Above ) { yShift += 0.1 ; } } else { yShift += this . rules . FingeringPaddingY ; } if ( placement === PlacementEnum . Above ) { yShift *= - 1 ; } gLabel . PositionAndShape . RelativePosition . y += skybottomFurthest + yShift ; gLabel . PositionAndShape . RelativePosition . x = staffEntryPositionX ; gLabel . setLabelPositionAndShapeBorders ( ) ; gLabel . PositionAndShape . calculateBoundingBox ( ) ; gse . FingeringEntries . push ( gLabel ) ; const start = gLabel . PositionAndShape . RelativePosition . x + gLabel . PositionAndShape . BorderLeft ; const end = start - gLabel . PositionAndShape . BorderLeft + gLabel . PositionAndShape . BorderRight ; if ( placement === PlacementEnum . Above ) { skybottomcalculator . updateSkyLineInRange ( start , end , gLabel . PositionAndShape . RelativePosition . y + gLabel . PositionAndShape . BorderTop ) ; } else if ( placement === PlacementEnum . Below ) { skybottomcalculator . updateBottomLineInRange ( start , end , gLabel . PositionAndShape . RelativePosition . y + gLabel . PositionAndShape . BorderBottom ) ; } } } } } } } private optimizeRestPlacement ( ) { for ( let idx2 = 0 , len2 = this . musicSystems . length ; idx2 < len2 ; ++ idx2 ) { const system = this . musicSystems [ idx2 ] ; for ( let idx3 = 0 , len3 = system . StaffLines . length ; idx3 < len3 ; ++ idx3 ) { const line = system . StaffLines [ idx3 ] ; for ( let idx4 = 0 , len4 = line . Measures . length ; idx4 < len4 ; ++ idx4 ) { const measure = line . Measures [ idx4 ] ; for ( let idx5 = 0 , len5 = measure . staffEntries . length ; idx5 < len5 ; ++ idx5 ) { const graphicalStaffEntry = measure . staffEntries [ idx5 ] ; this . optimizeRestNotePlacement ( graphicalStaffEntry , measure ) ; } } } } } private calculateTwoRestNotesPlacementWithCollisionDetection ( graphicalStaffEntry ) { const firstRestNote = graphicalStaffEntry . graphicalVoiceEntries [ 0 ] . notes [ 0 ] ; const secondRestNote = graphicalStaffEntry . graphicalVoiceEntries [ 1 ] . notes [ 0 ] ; secondRestNote . PositionAndShape . RelativePosition = new PointF2D ( 0.0 , 2.5 ) ; graphicalStaffEntry . PositionAndShape . calculateAbsolutePositionsRecursiveWithoutTopelement ( ) ; firstRestNote . PositionAndShape . computeNonOverlappingPositionWithMargin ( graphicalStaffEntry . PositionAndShape , ColDirEnum . Up , new PointF2D ( 0.0 , secondRestNote . PositionAndShape . RelativePosition . y ) ) ; const relative = firstRestNote . PositionAndShape . RelativePosition ; relative . y -= 1.0 ; firstRestNote . PositionAndShape . RelativePosition = relative ; graphicalStaffEntry . PositionAndShape . calculateBoundingBox ( ) ; } private calculateRestNotePlacementWithCollisionDetectionFromGraphicalNote ( graphicalStaffEntry ) { let restNote ; let graphicalNotes ; if ( graphicalStaffEntry . graphicalVoiceEntries [ 0 ] . notes [ 0 ] . sourceNote . isRest ( ) ) { restNote = graphicalStaffEntry . graphicalVoiceEntries [ 0 ] . notes [ 0 ] ; graphicalNotes = graphicalStaffEntry . graphicalVoiceEntries [ 1 ] . notes ; } else { graphicalNotes = graphicalStaffEntry . graphicalVoiceEntries [ 0 ] . notes ; restNote = graphicalStaffEntry . graphicalVoiceEntries [ 1 ] . notes [ 0 ] ; } let collision = false ; graphicalStaffEntry . PositionAndShape . calculateAbsolutePositionsRecursiveWithoutTopelement ( ) ; for ( let idx = 0 , len = graphicalNotes . length ; idx < len ; ++ idx ) { const graphicalNote = graphicalNotes [ idx ] ; if ( restNote . PositionAndShape . marginCollisionDetection ( graphicalNote . PositionAndShape ) ) { collision = true ; break ; } } if ( collision ) { if ( restNote . sourceNote . ParentVoiceEntry . ParentVoice instanceof LinkedVoice ) { const bottomBorder = graphicalNotes [ 0 ] . PositionAndShape . BorderMarginBottom + graphicalNotes [ 0 ] . PositionAndShape . RelativePosition . y ; restNote . PositionAndShape . RelativePosition = new PointF2D ( 0.0 , bottomBorder - restNote . PositionAndShape . BorderMarginTop + 0.5 ) ; } else { const last = graphicalNotes [ graphicalNotes . length - 1 ] ; const topBorder = last . PositionAndShape . BorderMarginTop + last . PositionAndShape . RelativePosition . y ; if ( graphicalNotes [ 0 ] . sourceNote . ParentVoiceEntry . ParentVoice instanceof LinkedVoice ) { restNote . PositionAndShape . RelativePosition = new PointF2D ( 0.0 , topBorder - restNote . PositionAndShape . BorderMarginBottom - 0.5 ) ; } else { const bottomBorder = graphicalNotes [ 0 ] . PositionAndShape . BorderMarginBottom + graphicalNotes [ 0 ] . PositionAndShape . RelativePosition . y ; if ( bottomBorder < 2.0 ) { restNote . PositionAndShape . RelativePosition = new PointF2D ( 0.0 , bottomBorder - restNote . PositionAndShape . BorderMarginTop + 0.5 ) ; } else { restNote . PositionAndShape . RelativePosition = new PointF2D ( 0.0 , topBorder - restNote . PositionAndShape . BorderMarginBottom - 0.0 ) ; } } } } graphicalStaffEntry . PositionAndShape . calculateBoundingBox ( ) ; } private calculateTieCurves ( ) { for ( const musicSystem of this . musicSystems ) { for ( const staffLine of musicSystem . StaffLines ) { for ( const measure of staffLine . Measures ) { for ( const staffEntry of measure . staffEntries ) { for ( const graphicalTie of staffEntry . GraphicalTies ) { if ( graphicalTie . StartNote !== undefined && graphicalTie . StartNote . parentVoiceEntry . parentStaffEntry === staffEntry ) { const tieIsAtSystemBreak = ( graphicalTie . StartNote . parentVoiceEntry . parentStaffEntry . parentMeasure . ParentStaffLine !== graphicalTie . EndNote . parentVoiceEntry . parentStaffEntry . parentMeasure . ParentStaffLine ) ; this . layoutGraphicalTie ( graphicalTie , tieIsAtSystemBreak , measure . ParentStaff . isTab ) ; } } } } } } } private calculateLyricsPosition ( ) { const lyricStaffEntriesDict = new Dictionary < StaffLine , GraphicalStaffEntry [ ] > ( ) ; for ( let idx = 0 , len = this . graphicalMusicSheet . ParentMusicSheet . Instruments . length ; idx < len ; ++ idx ) { const instrument = this . graphicalMusicSheet . ParentMusicSheet . Instruments [ idx ] ; if ( instrument . HasLyrics && instrument . LyricVersesNumbers . length > 0 ) { instrument . LyricVersesNumbers . sort ( ) ; } } for ( let idx2 = 0 , len2 = this . musicSystems . length ; idx2 < len2 ; ++ idx2 ) { const musicSystem = this . musicSystems [ idx2 ] ; for ( let idx3 = 0 , len3 = musicSystem . StaffLines . length ; idx3 < len3 ; ++ idx3 ) { const staffLine = musicSystem . StaffLines [ idx3 ] ; const lyricsStaffEntries = this . calculateSingleStaffLineLyricsPosition ( staffLine , staffLine . ParentStaff . ParentInstrument . LyricVersesNumbers ) ; lyricStaffEntriesDict . setValue ( staffLine , lyricsStaffEntries ) ; this . calculateLyricsExtendsAndDashes ( lyricStaffEntriesDict . getValue ( staffLine ) ) ; } } for ( let idx2 = 0 , len2 = this . musicSystems . length ; idx2 < len2 ; ++ idx2 ) { const musicSystem = this . musicSystems [ idx2 ] ; for ( let idx3 = 0 , len3 = musicSystem . StaffLines . length ; idx3 < len3 ; ++ idx3 ) { const staffLine = musicSystem . StaffLines [ idx3 ] ; this . calculateLyricsExtendsAndDashes ( lyricStaffEntriesDict . getValue ( staffLine ) ) ; } } } param lyricEntry private calculateSingleLyricWord ( lyricEntry ) { const graphicalLyricWord = lyricEntry . ParentLyricWord ; const index = graphicalLyricWord . GraphicalLyricsEntries . indexOf ( lyricEntry ) ; let nextLyricEntry = undefined ; if ( index >= 0 ) { nextLyricEntry = graphicalLyricWord . GraphicalLyricsEntries [ index + 1 ] ; } if ( ! nextLyricEntry ) { return ; } const startStaffLine = < StaffLine > lyricEntry . StaffEntryParent . parentMeasure . ParentStaffLine ; const nextStaffLine = < StaffLine > nextLyricEntry . StaffEntryParent . parentMeasure . ParentStaffLine ; const startStaffEntry = lyricEntry . StaffEntryParent ; const endStaffentry = nextLyricEntry . StaffEntryParent ; if ( lyricEntry . StaffEntryParent . parentMeasure . ParentStaffLine === nextLyricEntry . StaffEntryParent . parentMeasure . ParentStaffLine ) { const startX = startStaffEntry . parentMeasure . PositionAndShape . RelativePosition . x + startStaffEntry . PositionAndShape . RelativePosition . x + lyricEntry . GraphicalLabel . PositionAndShape . RelativePosition . x + lyricEntry . GraphicalLabel . PositionAndShape . BorderMarginRight ; const endX = endStaffentry . parentMeasure . PositionAndShape . RelativePosition . x + endStaffentry . PositionAndShape . RelativePosition . x + lyricEntry . GraphicalLabel . PositionAndShape . RelativePosition . x + nextLyricEntry . GraphicalLabel . PositionAndShape . BorderMarginLeft ; const y = lyricEntry . GraphicalLabel . PositionAndShape . RelativePosition . y ; let numberOfDashes = 1 ; if ( ( endX - startX ) > this . rules . MinimumDistanceBetweenDashes * 3 ) { numberOfDashes = Math . floor ( ( endX - startX ) / this . rules . MinimumDistanceBetweenDashes ) - 1 ; } if ( numberOfDashes === 1 ) { this . calculateSingleDashForLyricWord ( startStaffLine , startX , endX , y ) ; } else { this . calculateDashes ( startStaffLine , startX , endX , y ) ; } } else { const startX = startStaffEntry . parentMeasure . PositionAndShape . RelativePosition . x + startStaffEntry . PositionAndShape . RelativePosition . x + lyricEntry . GraphicalLabel . PositionAndShape . BorderMarginRight ; const lastGraphicalMeasure = startStaffLine . Measures [ startStaffLine . Measures . length - 1 ] ; const endX = lastGraphicalMeasure . PositionAndShape . RelativePosition . x + lastGraphicalMeasure . PositionAndShape . Size . width ; let y = lyricEntry . GraphicalLabel . PositionAndShape . RelativePosition . y ; this . calculateDashes ( startStaffLine , startX , endX , y ) ; if ( nextStaffLine && nextStaffLine . Measures [ 0 ] && endStaffentry . parentMeasure . ParentStaffLine && ! ( endStaffentry === endStaffentry . parentMeasure . staffEntries [ 0 ] && endStaffentry . parentMeasure === endStaffentry . parentMeasure . ParentStaffLine . Measures [ 0 ] ) ) { const secondStartX = nextStaffLine . Measures [ 0 ] . staffEntries [ 0 ] . PositionAndShape . RelativePosition . x ; const secondEndX = endStaffentry . parentMeasure . PositionAndShape . RelativePosition . x + endStaffentry . PositionAndShape . RelativePosition . x + nextLyricEntry . GraphicalLabel . PositionAndShape . BorderMarginLeft ; y = nextLyricEntry . GraphicalLabel . PositionAndShape . RelativePosition . y ; this . calculateDashes ( nextStaffLine , secondStartX , secondEndX , y ) ; } } } param staffLine param startX param endX param y private calculateDashes ( staffLine , startX , endX , y ) { let distance = endX - startX ; if ( distance < this . rules . MinimumDistanceBetweenDashes * 3 ) { this . calculateSingleDashForLyricWord ( staffLine , startX , endX , y ) ; } else { const numberOfDashes = Math . floor ( distance / this . rules . MinimumDistanceBetweenDashes ) - 1 ; const distanceBetweenDashes = distance / ( numberOfDashes + 1 ) ; let counter = 0 ; startX += distanceBetweenDashes ; endX -= distanceBetweenDashes ; while ( counter <= Math . floor ( numberOfDashes / 2.0 ) && endX > startX ) { distance = this . calculateRightAndLeftDashesForLyricWord ( staffLine , startX , endX , y ) ; startX += distanceBetweenDashes ; endX -= distanceBetweenDashes ; counter ++ ; } if ( distance > distanceBetweenDashes * 2 ) { this . calculateSingleDashForLyricWord ( staffLine , startX , endX , y ) ; } } } param { StaffLine } staffLine param { number } startX param { number } endX param { number } y private calculateSingleDashForLyricWord ( staffLine , startX , endX , y ) { const label = new Label ( "-" ) ; const dash = new GraphicalLabel ( label , this . rules . LyricsHeight , TextAlignmentEnum . CenterBottom , this . rules ) ; dash . setLabelPositionAndShapeBorders ( ) ; staffLine . LyricsDashes . push ( dash ) ; if ( this . staffLinesWithLyricWords . indexOf ( staffLine ) === - 1 ) { this . staffLinesWithLyricWords . push ( staffLine ) ; } dash . PositionAndShape . Parent = staffLine . PositionAndShape ; const relative = new PointF2D ( startX + ( endX - startX ) / 2 , y ) ; dash . PositionAndShape . RelativePosition = relative ; } param { GraphicalLyricEntry } lyricEntry private calculateLyricExtend ( lyricEntry ) { let startY = lyricEntry . GraphicalLabel . PositionAndShape . RelativePosition . y ; const startStaffEntry = lyricEntry . StaffEntryParent ; const startStaffLine = startStaffEntry . parentMeasure . ParentStaffLine ; let endStaffEntry = undefined ; let endStaffLine = undefined ; const staffIndex = startStaffEntry . parentMeasure . ParentStaff . idInMusicSheet ; for ( let index = startStaffEntry . parentVerticalContainer . Index + 1 ; index < this . graphicalMusicSheet . VerticalGraphicalStaffEntryContainers . length ; ++ index ) { const gse = this . graphicalMusicSheet . VerticalGraphicalStaffEntryContainers [ index ] . StaffEntries [ staffIndex ] ; if ( ! gse ) { continue ; } if ( gse . hasOnlyRests ( ) ) { break ; } if ( gse . LyricsEntries . length > 0 ) { break ; } endStaffEntry = gse ; endStaffLine = endStaffEntry . parentMeasure . ParentStaffLine ; if ( ! endStaffLine ) { endStaffLine = startStaffEntry . parentMeasure . ParentStaffLine ; } } if ( ! endStaffEntry || ! endStaffLine ) { return ; } if ( startStaffLine === endStaffLine && endStaffEntry . parentMeasure . ParentStaffLine ) { const startX = startStaffEntry . parentMeasure . PositionAndShape . RelativePosition . x + startStaffEntry . PositionAndShape . RelativePosition . x + lyricEntry . GraphicalLabel . PositionAndShape . BorderMarginRight ; const endX = endStaffEntry . parentMeasure . PositionAndShape . RelativePosition . x + endStaffEntry . PositionAndShape . RelativePosition . x + endStaffEntry . PositionAndShape . BorderMarginRight ; startY -= lyricEntry . GraphicalLabel . PositionAndShape . Size . height / 4 ; this . calculateSingleLyricWordWithUnderscore ( startStaffLine , startX , endX , startY ) ; } else { const lastMeasureBb = startStaffLine . Measures [ startStaffLine . Measures . length - 1 ] . PositionAndShape ; const startX = startStaffEntry . parentMeasure . PositionAndShape . RelativePosition . x + startStaffEntry . PositionAndShape . RelativePosition . x + lyricEntry . GraphicalLabel . PositionAndShape . BorderMarginRight ; const endX = lastMeasureBb . RelativePosition . x + lastMeasureBb . Size . width ; startY -= lyricEntry . GraphicalLabel . PositionAndShape . Size . height / 4 ; this . calculateSingleLyricWordWithUnderscore ( startStaffLine , startX , endX , startY ) ; if ( ! endStaffEntry ) { return ; } if ( endStaffEntry . parentMeasure . ParentStaffLine && endStaffEntry . parentMeasure . staffEntries && ! ( endStaffEntry === endStaffEntry . parentMeasure . staffEntries [ 0 ] && endStaffEntry . parentMeasure === endStaffEntry . parentMeasure . ParentStaffLine . Measures [ 0 ] ) ) { const secondStartX = endStaffLine . Measures [ 0 ] . staffEntries [ 0 ] . PositionAndShape . RelativePosition . x ; const secondEndX = endStaffEntry . parentMeasure . PositionAndShape . RelativePosition . x + endStaffEntry . PositionAndShape . RelativePosition . x + endStaffEntry . PositionAndShape . BorderMarginRight ; this . calculateSingleLyricWordWithUnderscore ( endStaffLine , secondStartX , secondEndX , startY ) ; } } } param staffLine param startX param end param y private calculateSingleLyricWordWithUnderscore ( staffLine , startX , endX , y ) { const lineStart = new PointF2D ( startX , y ) ; const lineEnd = new PointF2D ( endX , y ) ; const graphicalLine = new GraphicalLine ( lineStart , lineEnd , this . rules . LyricUnderscoreLineWidth ) ; staffLine . LyricLines . push ( graphicalLine ) ; if ( this . staffLinesWithLyricWords . indexOf ( staffLine ) === - 1 ) { this . staffLinesWithLyricWords . push ( staffLine ) ; } } param { StaffLine } staffLine param { number } startX param { number } endX param { number } y returns { number } private calculateRightAndLeftDashesForLyricWord ( staffLine , startX , endX , y ) { const leftLabel = new Label ( "-" ) ; const leftDash = new GraphicalLabel ( leftLabel , this . rules . LyricsHeight , TextAlignmentEnum . CenterBottom , this . rules ) ; leftDash . setLabelPositionAndShapeBorders ( ) ; staffLine . LyricsDashes . push ( leftDash ) ; if ( this . staffLinesWithLyricWords . indexOf ( staffLine ) === - 1 ) { this . staffLinesWithLyricWords . push ( staffLine ) ; } leftDash . PositionAndShape . Parent = staffLine . PositionAndShape ; const leftDashRelative = new PointF2D ( startX , y ) ; leftDash . PositionAndShape . RelativePosition = leftDashRelative ; const rightLabel = new Label ( "-" ) ; const rightDash = new GraphicalLabel ( rightLabel , this . rules . LyricsHeight , TextAlignmentEnum . CenterBottom , this . rules ) ; rightDash . setLabelPositionAndShapeBorders ( ) ; staffLine . LyricsDashes . push ( rightDash ) ; rightDash . PositionAndShape . Parent = staffLine . PositionAndShape ; const rightDashRelative = new PointF2D ( endX , y ) ; rightDash . PositionAndShape . RelativePosition = rightDashRelative ; return ( rightDash . PositionAndShape . RelativePosition . x - leftDash . PositionAndShape . RelativePosition . x ) ; } protected dynamicExpressionMap : Map < number , BoundingBox > = new Map < number , BoundingBox > ( ) ; private calculateDynamicExpressions ( ) { const maxIndex = Math . min ( this . graphicalMusicSheet . ParentMusicSheet . SourceMeasures . length - 1 , this . rules . MaxMeasureToDrawIndex ) ; const minIndex = Math . min ( this . rules . MinMeasureToDrawIndex , this . graphicalMusicSheet . ParentMusicSheet . SourceMeasures . length ) ; for ( let i = minIndex ; i <= maxIndex ; i ++ ) { const sourceMeasure = this . graphicalMusicSheet . ParentMusicSheet . SourceMeasures [ i ] ; this . dynamicExpressionMap . clear ( ) ; for ( let j = 0 ; j < sourceMeasure . StaffLinkedExpressions . length ; j ++ ) { if ( ! this . graphicalMusicSheet . MeasureList [ i ] || ! this . graphicalMusicSheet . MeasureList [ i ] [ j ] ) { continue ; } if ( this . graphicalMusicSheet . MeasureList [ i ] [ j ] . ParentStaff . ParentInstrument . Visible ) { for ( let k = 0 ; k < sourceMeasure . StaffLinkedExpressions [ j ] . length ; k ++ ) { if ( sourceMeasure . StaffLinkedExpressions [ j ] [ k ] . InstantaneousDynamic !== undefined || ( sourceMeasure . StaffLinkedExpressions [ j ] [ k ] . StartingContinuousDynamic !== undefined && sourceMeasure . StaffLinkedExpressions [ j ] [ k ] . StartingContinuousDynamic . StartMultiExpression === sourceMeasure . StaffLinkedExpressions [ j ] [ k ] && sourceMeasure . StaffLinkedExpressions [ j ] [ k ] . UnknownList . length === 0 ) ) { this . calculateDynamicExpressionsForMultiExpression ( sourceMeasure . StaffLinkedExpressions [ j ] [ k ] , i , j ) ; } } } } } this . dynamicExpressionMap . clear ( ) ; } private calculateOctaveShifts ( ) { for ( let i = 0 ; i < this . graphicalMusicSheet . ParentMusicSheet . SourceMeasures . length ; i ++ ) { const sourceMeasure = this . graphicalMusicSheet . ParentMusicSheet . SourceMeasures [ i ] ; for ( let j = 0 ; j < sourceMeasure . StaffLinkedExpressions . length ; j ++ ) { if ( ! this . graphicalMusicSheet . MeasureList [ i ] || ! this . graphicalMusicSheet . MeasureList [ i ] [ j ] ) { continue ; } if ( this . graphicalMusicSheet . MeasureList [ i ] [ j ] . ParentStaff . ParentInstrument . Visible ) { for ( let k = 0 ; k < sourceMeasure . StaffLinkedExpressions [ j ] . length ; k ++ ) { if ( ( sourceMeasure . StaffLinkedExpressions [ j ] [ k ] . OctaveShiftStart ) ) { this . calculateSingleOctaveShift ( sourceMeasure , sourceMeasure . StaffLinkedExpressions [ j ] [ k ] , i , j ) ; } } } } } } private getFirstLeftNotNullStaffEntryFromContainer ( horizontalIndex , verticalIndex , multiStaffInstrument ) { if ( this . graphicalMusicSheet . VerticalGraphicalStaffEntryContainers [ horizontalIndex ] . StaffEntries [ verticalIndex ] ) { return this . graphicalMusicSheet . VerticalGraphicalStaffEntryContainers [ horizontalIndex ] . StaffEntries [ verticalIndex ] ; } for ( let i = horizontalIndex - 1 ; i >= 0 ; i -- ) { if ( this . graphicalMusicSheet . VerticalGraphicalStaffEntryContainers [ i ] . StaffEntries [ verticalIndex ] ) { return this . graphicalMusicSheet . VerticalGraphicalStaffEntryContainers [ i ] . StaffEntries [ verticalIndex ] ; } } return undefined ; } private getFirstRightNotNullStaffEntryFromContainer ( horizontalIndex , verticalIndex , multiStaffInstrument ) { if ( this . graphicalMusicSheet . VerticalGraphicalStaffEntryContainers [ horizontalIndex ] . StaffEntries [ verticalIndex ] ) { return this . graphicalMusicSheet . VerticalGraphicalStaffEntryContainers [ horizontalIndex ] . StaffEntries [ verticalIndex ] ; } for ( let i = horizontalIndex + 1 ; i < this . graphicalMusicSheet . VerticalGraphicalStaffEntryContainers . length ; i ++ ) { if ( this . graphicalMusicSheet . VerticalGraphicalStaffEntryContainers [ i ] . StaffEntries [ verticalIndex ] ) { return this . graphicalMusicSheet . VerticalGraphicalStaffEntryContainers [ i ] . StaffEntries [ verticalIndex ] ; } } return undefined ; } private calculateWordRepetitionInstructions ( ) { for ( let i = 0 ; i < this . graphicalMusicSheet . ParentMusicSheet . SourceMeasures . length ; i ++ ) { const sourceMeasure = this . graphicalMusicSheet . ParentMusicSheet . SourceMeasures [ i ] ; for ( let idx = 0 , len = sourceMeasure . FirstRepetitionInstructions . length ; idx < len ; ++ idx ) { const instruction = sourceMeasure . FirstRepetitionInstructions [ idx ] ; this . calculateWordRepetitionInstruction ( instruction , i ) ; } for ( let idx = 0 , len = sourceMeasure . LastRepetitionInstructions . length ; idx < len ; ++ idx ) { const instruction = sourceMeasure . LastRepetitionInstructions [ idx ] ; this . calculateWordRepetitionInstruction ( instruction , i ) ; } } } private calculateRepetitionEndings ( ) { const musicsheet = this . graphicalMusicSheet . ParentMusicSheet ; for ( let idx = 0 , len = musicsheet . Repetitions . length ; idx < len ; ++ idx ) { const repetition = musicsheet . Repetitions [ idx ] ; this . calcGraphicalRepetitionEndingsRecursively ( repetition ) ; } } private calculateTempoExpressions ( ) { const maxIndex = Math . min ( this . graphicalMusicSheet . ParentMusicSheet . SourceMeasures . length - 1 , this . rules . MaxMeasureToDrawIndex ) ; const minIndex = this . rules . MinMeasureToDrawIndex ; for ( let i = minIndex ; i <= maxIndex ; i ++ ) { const sourceMeasure = this . graphicalMusicSheet . ParentMusicSheet . SourceMeasures [ i ] ; for ( let j = 0 ; j < sourceMeasure . TempoExpressions . length ; j ++ ) { this . calculateTempoExpressionsForMultiTempoExpression ( sourceMeasure , sourceMeasure . TempoExpressions [ j ] , i ) ; } } } private calculateRehearsalMarks ( ) { if ( ! this . rules . RenderRehearsalMarks ) { return ; } for ( const measure of this . graphicalMusicSheet . ParentMusicSheet . SourceMeasures ) { this . calculateRehearsalMark ( measure ) ; } } protected calculateRehearsalMark ( measure ) { throw new Error ( this . abstractNotImplementedErrorMessage ) ; } private calculateMoodAndUnknownExpressions ( ) { for ( let i = 0 ; i < this . graphicalMusicSheet . ParentMusicSheet . SourceMeasures . length ; i ++ ) { const sourceMeasure = this . graphicalMusicSheet . ParentMusicSheet . SourceMeasures [ i ] ; for ( let j = 0 ; j < sourceMeasure . StaffLinkedExpressions . length ; j ++ ) { if ( ! this . graphicalMusicSheet . MeasureList [ i ] || ! this . graphicalMusicSheet . MeasureList [ i ] [ j ] ) { continue ; } if ( this . graphicalMusicSheet . MeasureList [ i ] [ j ] . ParentStaff . ParentInstrument . Visible ) { for ( let k = 0 ; k < sourceMeasure . StaffLinkedExpressions [ j ] . length ; k ++ ) { if ( ( sourceMeasure . StaffLinkedExpressions [ j ] [ k ] . MoodList . length > 0 ) || ( sourceMeasure . StaffLinkedExpressions [ j ] [ k ] . UnknownList . length > 0 ) ) { this . calculateMoodAndUnknownExpression ( sourceMeasure . StaffLinkedExpressions [ j ] [ k ] , i , j ) ; } } } } } } param voiceEntry private calculateStemDirectionFromVoices ( voiceEntry ) { const hasLink = voiceEntry . ParentSourceStaffEntry . Link !== undefined ; if ( hasLink ) { if ( voiceEntry === voiceEntry . ParentSourceStaffEntry . VoiceEntries [ 0 ] ) { voiceEntry . WantedStemDirection = StemDirectionType . Up ; return ; } else { voiceEntry . WantedStemDirection = StemDirectionType . Down ; return ; } } else { if ( voiceEntry . ParentVoice instanceof LinkedVoice ) { voiceEntry . WantedStemDirection = StemDirectionType . Down ; } else { if ( voiceEntry . ParentSourceStaffEntry . VoiceEntries . length > 1 ) { voiceEntry . WantedStemDirection = StemDirectionType . Up ; } } } } /** Sets a voiceEntry's stem direction to one already set in other notes in its beam, if it has one. */ private setBeamNotesWantedStemDirections ( voiceEntry ) { if ( ! ( voiceEntry . Notes . length > 0 ) ) { return ; } const beam = voiceEntry . Notes [ 0 ] . NoteBeam ; if ( beam ) { for ( const note of beam . Notes ) { if ( note . ParentVoiceEntry . WantedStemDirection !== StemDirectionType . Undefined ) { if ( note . ParentVoiceEntry . ParentSourceStaffEntry . ParentStaff . Id === voiceEntry . ParentSourceStaffEntry . ParentStaff . Id ) { voiceEntry . WantedStemDirection = note . ParentVoiceEntry . WantedStemDirection ; break ; } } } } } }