import { MusicSystem } from "./MusicSystem" ; import { GraphicalStaffEntry } from "./GraphicalStaffEntry" ; import { SourceMeasure } from "../VoiceData/SourceMeasure" ; import { StaffLine } from "./StaffLine" ; import { Staff } from "../VoiceData/Staff" ; import { GraphicalObject } from "./GraphicalObject" ; import { ClefInstruction } from "../VoiceData/Instructions/ClefInstruction" ; import { KeyInstruction } from "../VoiceData/Instructions/KeyInstruction" ; import { RhythmInstruction } from "../VoiceData/Instructions/RhythmInstruction" ; import { Fraction } from "../../Common/DataObjects/Fraction" ; import { Voice } from "../VoiceData/Voice" ; import { VoiceEntry } from "../VoiceData/VoiceEntry" ; import { SystemLinesEnum } from "./SystemLinesEnum" ; import { BoundingBox } from "./BoundingBox" ; import { PointF2D } from "../../Common/DataObjects/PointF2D" ; /**
 * Represents a measure in the music sheet (one measure in one staff line)
 */ export abstract class GraphicalMeasure extends GraphicalObject { protected firstInstructionStaffEntry : GraphicalStaffEntry ; protected lastInstructionStaffEntry : GraphicalStaffEntry ; constructor ( staff = undefined , parentSourceMeasure = undefined , staffLine = undefined ) { super ( ) ; this . parentStaff = staff ; this . parentSourceMeasure = parentSourceMeasure ; this . parentStaffLine = staffLine ; if ( staffLine ) { this . parentStaff = staffLine . ParentStaff ; this . PositionAndShape = new BoundingBox ( this , staffLine . PositionAndShape ) ; } else { this . PositionAndShape = new BoundingBox ( this ) ; } this . PositionAndShape . BorderBottom = 4 ; if ( this . parentSourceMeasure ) { this . measureNumber = this . parentSourceMeasure . MeasureNumber ; } this . staffEntries = [ ] ; } public parentSourceMeasure : SourceMeasure ; public staffEntries : GraphicalStaffEntry [ ] ; /**
     * The x-width of possibly existing: repetition start line, clef, key, rhythm.
     */ public beginInstructionsWidth : number ; /**
     * The minimum possible x-width of all staff entries without overlapping.
     */ public minimumStaffEntriesWidth : number ; /**
     * Will be set by music system builder while building systems.
     */ public staffEntriesScaleFactor : number ; /**
     * The x-width of possibly existing: repetition end line, clef.
     */ public endInstructionsWidth : number ; public hasError : boolean ; /**
     * Whether or not this measure is nothing but rest(s).
     * Also see SourceMeasure.allRests, which is not the same, because a source measure can have multiple staffs/graphicalMeasures.
     */ public hasOnlyRests : boolean = false ; private parentStaff : Staff ; private parentMusicSystem : MusicSystem ; private measureNumber : number = - 1 ; private parentStaffLine : StaffLine ; /** Used to show key, rhythm changes at the end of the system, has MeasureNumber < 0, because never set. */ public IsExtraGraphicalMeasure : boolean ; public ExtraGraphicalMeasurePreviousMeasure : GraphicalMeasure ; public get ParentStaff ( ) : Staff { return this . parentStaff ; } public get ParentMusicSystem ( ) : MusicSystem { return this . parentMusicSystem ; } public set ParentMusicSystem ( value ) { this . parentMusicSystem = value ; } public get MeasureNumber ( ) : number { return this . measureNumber ; } public get FirstInstructionStaffEntry ( ) : GraphicalStaffEntry { return this . firstInstructionStaffEntry ; } public set FirstInstructionStaffEntry ( value ) { this . firstInstructionStaffEntry = value ; } public get LastInstructionStaffEntry ( ) : GraphicalStaffEntry { return this . lastInstructionStaffEntry ; } public set LastInstructionStaffEntry ( value ) { this . lastInstructionStaffEntry = value ; } public get ParentStaffLine ( ) : StaffLine { return this . parentStaffLine ; } public set ParentStaffLine ( value ) { this . parentStaffLine = value ; if ( this . parentStaffLine ) { this . PositionAndShape . Parent = this . parentStaffLine . PositionAndShape ; } } /**
     * Reset all the geometric values and parameters of this measure and put it in an initialized state.
     * This is needed to evaluate a measure a second time by system builder.
     */ public resetLayout ( ) { throw new Error ( "not implemented" ) ; } param line public getLineWidth ( line ) { throw new Error ( "not implemented" ) ; } param clef public addClefAtBegin ( clef ) { throw new Error ( "not implemented" ) ; } param currentKey param previousKey param currentClef public addKeyAtBegin ( currentKey , previousKey , currentClef ) { throw new Error ( "not implemented" ) ; } param rhythm public addRhythmAtBegin ( rhythm ) { throw new Error ( "not implemented" ) ; } param clef public addClefAtEnd ( clef , visible = true ) { throw new Error ( "not implemented" ) ; } param xPos public setPositionInStaffline ( xPos ) { this . PositionAndShape . RelativePosition = new PointF2D ( xPos , 0 ) ; } param width public setWidth ( width ) { this . PositionAndShape . BorderRight = width ; } /**
     * This method is called after the StaffEntriesScaleFactor has been set.
     * Here the final x-positions of the staff entries have to be set.
     * (multiply the minimal positions with the scaling factor, considering the BeginInstructionsWidth).
     */ public layoutSymbols ( ) { throw new Error ( "not implemented" ) ; } public findGraphicalStaffEntryFromTimestamp ( relativeTimestamp ) { for ( let idx = 0 , len = this . staffEntries . length ; idx < len ; ++ idx ) { const graphicalStaffEntry = this . staffEntries [ idx ] ; if ( graphicalStaffEntry . relInMeasureTimestamp ?. Equals ( relativeTimestamp ) ) { return graphicalStaffEntry ; } } return undefined ; } param absoluteTimestamp returns { any } public findGraphicalStaffEntryFromVerticalContainerTimestamp ( absoluteTimestamp ) { for ( let idx = 0 , len = this . staffEntries . length ; idx < len ; ++ idx ) { const graphicalStaffEntry = this . staffEntries [ idx ] ; if ( graphicalStaffEntry . sourceStaffEntry . VerticalContainerParent . getAbsoluteTimestamp ( ) . Equals ( absoluteTimestamp ) ) { return graphicalStaffEntry ; } } return undefined ; } returns { boolean } public hasSameDurationWithSourceMeasureParent ( ) { const duration = new Fraction ( 0 , 1 ) ; for ( let idx = 0 , len = this . staffEntries . length ; idx < len ; ++ idx ) { const graphicalStaffEntry = this . staffEntries [ idx ] ; duration . Add ( graphicalStaffEntry . findStaffEntryMinNoteLength ( ) ) ; } return duration . Equals ( this . parentSourceMeasure . Duration ) ; } returns { boolean } public hasMultipleVoices ( ) { if ( this . staffEntries . length === 0 ) { return false ; } const voices = [ ] ; for ( let idx = 0 , len = this . staffEntries . length ; idx < len ; ++ idx ) { const staffEntry = this . staffEntries [ idx ] ; for ( let idx2 = 0 , len2 = staffEntry . sourceStaffEntry . VoiceEntries . length ; idx2 < len2 ; ++ idx2 ) { const voiceEntry = staffEntry . sourceStaffEntry . VoiceEntries [ idx2 ] ; if ( voices . indexOf ( voiceEntry . ParentVoice ) < 0 ) { voices . push ( voiceEntry . ParentVoice ) ; } } } if ( voices . length > 1 ) { return true ; } return false ; } public isVisible ( ) { return this . ParentStaff . ParentInstrument . Visible ; } public getGraphicalMeasureDurationFromStaffEntries ( ) { let duration = new Fraction ( 0 , 1 ) ; const voices = [ ] ; for ( let idx = 0 , len = this . staffEntries . length ; idx < len ; ++ idx ) { const graphicalStaffEntry = this . staffEntries [ idx ] ; for ( let idx2 = 0 , len2 = graphicalStaffEntry . sourceStaffEntry . VoiceEntries . length ; idx2 < len2 ; ++ idx2 ) { const voiceEntry = graphicalStaffEntry . sourceStaffEntry . VoiceEntries [ idx2 ] ; if ( voices . indexOf ( voiceEntry . ParentVoice ) < 0 ) { voices . push ( voiceEntry . ParentVoice ) ; } } } for ( let idx = 0 , len = voices . length ; idx < len ; ++ idx ) { const voice = voices [ idx ] ; const voiceDuration = new Fraction ( 0 , 1 ) ; for ( const graphicalStaffEntry of this . staffEntries ) { for ( const gve of graphicalStaffEntry . graphicalVoiceEntries ) { if ( gve . parentVoiceEntry . ParentVoice === voice && gve . notes . length > 0 ) { voiceDuration . Add ( gve . notes [ 0 ] . graphicalNoteLength ) ; } } } if ( duration . lt ( voiceDuration ) ) { duration = Fraction . createFromFraction ( voiceDuration ) ; } } return duration ; } public addGraphicalStaffEntry ( graphicalStaffEntry ) { this . staffEntries . push ( graphicalStaffEntry ) ; } param staffEntry public addGraphicalStaffEntryAtTimestamp ( staffEntry ) { if ( staffEntry ) { if ( this . staffEntries . length === 0 || this . staffEntries [ this . staffEntries . length - 1 ] . relInMeasureTimestamp . lt ( staffEntry . relInMeasureTimestamp ) ) { this . staffEntries . push ( staffEntry ) ; } else { for ( let i = this . staffEntries . length - 1 ; i >= 0 ; i -- ) { if ( this . staffEntries [ i ] . relInMeasureTimestamp . lt ( staffEntry . relInMeasureTimestamp ) ) { this . staffEntries . splice ( i + 1 , 0 , staffEntry ) ; break ; } if ( i === 0 ) { this . staffEntries . splice ( i , 0 , staffEntry ) ; } } } } } public isPianoRightHand ( ) { return this . isUpperStaffOfInstrument ( ) ; } public isPianoLeftHand ( ) { return this . isLowerStaffOfInstrument ( ) ; } public isUpperStaffOfInstrument ( ) { if ( this . parentStaff . ParentInstrument . Staves . length === 1 ) { return true ; } return this . ParentStaff === this . parentStaff . ParentInstrument . Staves [ 0 ] ; } public isLowerStaffOfInstrument ( ) { if ( this . parentStaff . ParentInstrument . Staves . length === 1 ) { return false ; } return this . ParentStaff === this . ParentStaff . ParentInstrument . Staves . last ( ) ; } public beginsWithLineRepetition ( ) { const sourceMeasure = this . parentSourceMeasure ; if ( ! sourceMeasure ) { return false ; } return sourceMeasure . beginsWithLineRepetition ( ) ; } returns { boolean } public endsWithLineRepetition ( ) { const sourceMeasure = this . parentSourceMeasure ; if ( ! sourceMeasure ) { return false ; } return sourceMeasure . endsWithLineRepetition ( ) ; } returns { boolean } public beginsWithWordRepetition ( ) { const sourceMeasure = this . parentSourceMeasure ; if ( ! sourceMeasure ) { return false ; } return sourceMeasure . beginsWithWordRepetition ( ) ; } /**
     * Check if this Measure is a Repetition Ending.
     */ public endsWithWordRepetition ( ) { const sourceMeasure = this . parentSourceMeasure ; if ( ! sourceMeasure ) { return false ; } return sourceMeasure . endsWithWordRepetition ( ) ; } public getTransposedHalftones ( ) { const transposeHalftones = this . parentStaff . ParentInstrument . GetMusicSheet . Transpose + this . parentStaff . ParentInstrument . Transpose ; return transposeHalftones ; } }