import { SourceStaffEntry } from "../VoiceData/SourceStaffEntry" ; import { BoundingBox } from "./BoundingBox" ; import { Fraction } from "../../Common/DataObjects/Fraction" ; import { VerticalGraphicalStaffEntryContainer } from "./VerticalGraphicalStaffEntryContainer" ; import { Note } from "../VoiceData/Note" ; import { Slur } from "../VoiceData/Expressions/ContinuousExpressions/Slur" ; import { Voice } from "../VoiceData/Voice" ; import { VoiceEntry } from "../VoiceData/VoiceEntry" ; import { GraphicalTie } from "./GraphicalTie" ; import { GraphicalObject } from "./GraphicalObject" ; import { GraphicalMeasure } from "./GraphicalMeasure" ; import { GraphicalNote } from "./GraphicalNote" ; import { GraphicalChordSymbolContainer } from "./GraphicalChordSymbolContainer" ; import { GraphicalLyricEntry } from "./GraphicalLyricEntry" ; import { AbstractGraphicalInstruction } from "./AbstractGraphicalInstruction" ; import { GraphicalStaffEntryLink } from "./GraphicalStaffEntryLink" ; import { CollectionUtil } from "../../Util/CollectionUtil" ; import { GraphicalVoiceEntry } from "./GraphicalVoiceEntry" ; import { MusicSheetCalculator } from "./MusicSheetCalculator" ; import { Tie } from "../VoiceData/Tie" ; import { GraphicalLabel } from "./GraphicalLabel" ; import { SkyBottomLineCalculator } from "./SkyBottomLineCalculator" ; /**
 * The graphical counterpart of a [[SourceStaffEntry]].
 */ export abstract class GraphicalStaffEntry extends GraphicalObject { constructor ( parentMeasure , sourceStaffEntry = undefined , staffEntryParent = undefined ) { super ( ) ; this . parentMeasure = parentMeasure ; this . graphicalVoiceEntries = [ ] ; this . sourceStaffEntry = sourceStaffEntry ; if ( staffEntryParent ) { this . staffEntryParent = staffEntryParent ; this . parentVerticalContainer = staffEntryParent . parentVerticalContainer ; this . PositionAndShape = new BoundingBox ( this , staffEntryParent . PositionAndShape ) ; } else { this . PositionAndShape = new BoundingBox ( this , parentMeasure . PositionAndShape ) ; } if ( sourceStaffEntry ) { this . relInMeasureTimestamp = sourceStaffEntry . Timestamp ; } this . FingeringEntries = [ ] ; } public graphicalChordContainers : GraphicalChordSymbolContainer [ ] = [ ] ; public graphicalLink : GraphicalStaffEntryLink ; public relInMeasureTimestamp : Fraction ; public sourceStaffEntry : SourceStaffEntry ; public parentMeasure : GraphicalMeasure ; public graphicalVoiceEntries : GraphicalVoiceEntry [ ] ; public staffEntryParent : GraphicalStaffEntry ; public parentVerticalContainer : VerticalGraphicalStaffEntryContainer ; public tabStaffEntry : GraphicalStaffEntry = undefined ; public MaxAccidentals : number = 0 ; private graphicalInstructions : AbstractGraphicalInstruction [ ] = [ ] ; public ties : Tie [ ] = [ ] ; private graphicalTies : GraphicalTie [ ] = [ ] ; private lyricsEntries : GraphicalLyricEntry [ ] = [ ] ; public FingeringEntries : GraphicalLabel [ ] ; public get GraphicalInstructions ( ) : AbstractGraphicalInstruction [ ] { return this . graphicalInstructions ; } public get GraphicalTies ( ) : GraphicalTie [ ] { return this . graphicalTies ; } public get LyricsEntries ( ) : GraphicalLyricEntry [ ] { return this . lyricsEntries ; } public set LyricsEntries ( value ) { this . lyricsEntries = value ; } returns { Fraction } public getAbsoluteTimestamp ( ) { const result = this . parentMeasure . parentSourceMeasure . AbsoluteTimestamp . clone ( ) ; if ( this . relInMeasureTimestamp ) { result . Add ( this . relInMeasureTimestamp ) ; } return result ; } param tieNote returns { any } public findTieGraphicalNoteFromNote ( tieNote ) { for ( const gve of this . graphicalVoiceEntries ) { for ( const graphicalNote of gve . notes ) { const note = graphicalNote . sourceNote ; if ( ! note . isRest ( ) && note . Pitch . FundamentalNote === tieNote . Pitch . FundamentalNote && note . Pitch . Octave === tieNote . Pitch . Octave && note . getAbsoluteTimestamp ( ) . Equals ( tieNote . getAbsoluteTimestamp ( ) ) ) { return graphicalNote ; } } } return undefined ; } param tieNote param slur returns { any } public findEndTieGraphicalNoteFromNoteWithStartingSlur ( tieNote , slur ) { if ( ! tieNote ) { return undefined ; } for ( const gve of this . graphicalVoiceEntries ) { if ( gve . parentVoiceEntry !== tieNote . ParentVoiceEntry ) { continue ; } for ( const graphicalNote of gve . notes ) { const note = graphicalNote . sourceNote ; if ( note . NoteTie && note . NoteSlurs . indexOf ( slur ) !== - 1 ) { return graphicalNote ; } } } return undefined ; } public findGraphicalNoteFromGraceNote ( graceNote ) { if ( ! graceNote ) { return undefined ; } for ( const gve of this . graphicalVoiceEntries ) { if ( gve . parentVoiceEntry !== graceNote . ParentVoiceEntry ) { continue ; } for ( const graphicalNote of gve . notes ) { if ( graphicalNote . sourceNote === graceNote ) { return graphicalNote ; } } } return undefined ; } public findGraphicalNoteFromNote ( note ) { if ( ! note ) { return undefined ; } for ( const gve of this . graphicalVoiceEntries ) { if ( gve . parentVoiceEntry !== note . ParentVoiceEntry ) { continue ; } for ( const graphicalNote of gve . notes ) { if ( graphicalNote . sourceNote === note && this . getAbsoluteTimestamp ( ) . Equals ( note . getAbsoluteTimestamp ( ) ) ) { return graphicalNote ; } } } return undefined ; } public getGraphicalNoteDurationFromVoice ( voice ) { for ( const gve of this . graphicalVoiceEntries ) { if ( gve . parentVoiceEntry . ParentVoice !== voice ) { continue ; } return gve . notes [ 0 ] . graphicalNoteLength ; } return new Fraction ( 0 , 1 ) ; } param voiceEntry returns { any } public findVoiceEntryGraphicalNotes ( voiceEntry ) { for ( const gve of this . graphicalVoiceEntries ) { if ( gve . parentVoiceEntry === voiceEntry ) { return gve . notes ; } } return undefined ; } param voiceEntry returns { boolean } public isVoiceEntryPartOfLinkedVoiceEntry ( voiceEntry ) { if ( this . sourceStaffEntry . Link ) { for ( let idx = 0 , len = this . sourceStaffEntry . Link . LinkStaffEntries . length ; idx < len ; ++ idx ) { const sEntry = this . sourceStaffEntry . Link . LinkStaffEntries [ idx ] ; if ( sEntry . VoiceEntries . indexOf ( voiceEntry ) !== - 1 && sEntry !== this . sourceStaffEntry ) { return true ; } } } return false ; } returns { Fraction } public findStaffEntryMinNoteLength ( ) { let minLength = new Fraction ( Number . MAX_VALUE , 1 ) ; for ( const gve of this . graphicalVoiceEntries ) { for ( const graphicalNote of gve . notes ) { const calNoteLen = graphicalNote . graphicalNoteLength ; if ( calNoteLen . lt ( minLength ) && calNoteLen . GetExpandedNumerator ( ) > 0 ) { minLength = calNoteLen ; } } } return minLength ; } public findStaffEntryMaxNoteLength ( ) { let maxLength = new Fraction ( 0 , 1 ) ; for ( const gve of this . graphicalVoiceEntries ) { for ( const graphicalNote of gve . notes ) { const calNoteLen = graphicalNote . graphicalNoteLength ; if ( maxLength . lt ( calNoteLen ) && calNoteLen . GetExpandedNumerator ( ) > 0 ) { maxLength = calNoteLen ; } } } return maxLength ; } param voiceEntry returns { GraphicalNote [ ] } public findOrCreateGraphicalVoiceEntry ( voiceEntry ) { for ( const gve of this . graphicalVoiceEntries ) { if ( gve . parentVoiceEntry === voiceEntry ) { return gve ; } } const graphicalVoiceEntry = MusicSheetCalculator . symbolFactory . createVoiceEntry ( voiceEntry , this ) ; this . graphicalVoiceEntries . push ( graphicalVoiceEntry ) ; return graphicalVoiceEntry ; } param graphicalNote returns { GraphicalNote [ ] } public findOrCreateGraphicalVoiceEntryFromGraphicalNote ( graphicalNote ) { for ( const gve of this . graphicalVoiceEntries ) { if ( gve === graphicalNote . parentVoiceEntry ) { return gve ; } } const graphicalVoiceEntry = MusicSheetCalculator . symbolFactory . createVoiceEntry ( graphicalNote . sourceNote . ParentVoiceEntry , this ) ; this . graphicalVoiceEntries . push ( graphicalVoiceEntry ) ; return graphicalVoiceEntry ; } param graphicalNotes param graphicalNote public addGraphicalNoteToListAtCorrectYPosition ( gve , graphicalNote ) { const graphicalNotes = gve . notes ; if ( graphicalNotes . length === 0 || graphicalNote . PositionAndShape . RelativePosition . y < CollectionUtil . last ( graphicalNotes ) . PositionAndShape . RelativePosition . y ) { graphicalNotes . push ( graphicalNote ) ; } else { for ( let i = graphicalNotes . length - 1 ; i >= 0 ; i -- ) { if ( graphicalNotes [ i ] . PositionAndShape . RelativePosition . y > graphicalNote . PositionAndShape . RelativePosition . y ) { graphicalNotes . splice ( i + 1 , 0 , graphicalNote ) ; break ; } if ( i === 0 ) { graphicalNotes . splice ( 0 , 0 , graphicalNote ) ; break ; } } } } /**
     * Returns true if this staff entry has only rests
     */ public hasOnlyRests ( ) { const hasOnlyRests = true ; for ( const gve of this . graphicalVoiceEntries ) { for ( const graphicalNote of gve . notes ) { const note = graphicalNote . sourceNote ; if ( ! note . isRest ( ) ) { return false ; } } } return hasOnlyRests ; } public getSkylineMin ( ) { const skybottomcalculator = this . parentMeasure ?. ParentStaffLine . SkyBottomLineCalculator ; if ( ! skybottomcalculator ) { return undefined ; } const [ start , end ] = this . getAbsoluteStartAndEnd ( ) ; return skybottomcalculator . getSkyLineMinInRange ( start , end ) ; } /** Highest Y around the staff entry and notes in OSMD units (pixels / 10). Note that negative y is up. */ public getHighestYAtEntry ( ) { const baseY = this . parentMeasure . ParentStaffLine . PositionAndShape . AbsolutePosition . y ; return baseY + this . getSkylineMin ( ) ; } /** Lowest Y around the staff entry and notes in OSMD units (pixels / 10). Note that positive y is down. */ public getLowestYAtEntry ( ) { const baseY = this . parentMeasure . ParentStaffLine . PositionAndShape . AbsolutePosition . y ; return baseY + this . getBottomlineMax ( ) ; } public getBottomlineMax ( ) { const skybottomcalculator = this . parentMeasure ?. ParentStaffLine . SkyBottomLineCalculator ; if ( ! skybottomcalculator ) { return undefined ; } const [ start , end ] = this . getAbsoluteStartAndEnd ( ) ; return skybottomcalculator . getBottomLineMaxInRange ( start , end ) ; } public getAbsoluteStartAndEnd ( ) { let start = this . PositionAndShape . AbsolutePosition . x ; start -= this . parentMeasure . ParentStaffLine . PositionAndShape . AbsolutePosition . x ; const end = start + this . PositionAndShape . Size . width ; return [ start , end ] ; } }