import { MusicSheet } from "../MusicSheet" ; import { SourceMeasure } from "../VoiceData/SourceMeasure" ; import { GraphicalMeasure } from "./GraphicalMeasure" ; import { GraphicalMusicPage } from "./GraphicalMusicPage" ; import { VerticalGraphicalStaffEntryContainer } from "./VerticalGraphicalStaffEntryContainer" ; import { GraphicalLabel } from "./GraphicalLabel" ; import { GraphicalLine } from "./GraphicalLine" ; import { MusicSystem } from "./MusicSystem" ; import { GraphicalStaffEntry } from "./GraphicalStaffEntry" ; import { SourceStaffEntry } from "../VoiceData/SourceStaffEntry" ; import { PointF2D } from "../../Common/DataObjects/PointF2D" ; import { ClefInstruction } from "../VoiceData/Instructions/ClefInstruction" ; import { AbstractNotationInstruction } from "../VoiceData/Instructions/AbstractNotationInstruction" ; import { KeyInstruction } from "../VoiceData/Instructions/KeyInstruction" ; import { Fraction } from "../../Common/DataObjects/Fraction" ; import { GraphicalNote } from "./GraphicalNote" ; import { Instrument } from "../Instrument" ; import { BoundingBox } from "./BoundingBox" ; import { MusicSheetCalculator } from "./MusicSheetCalculator" ; import log from "loglevel" ; import { CollectionUtil } from "../../Util/CollectionUtil" ; import { SelectionStartSymbol } from "./SelectionStartSymbol" ; import { SelectionEndSymbol } from "./SelectionEndSymbol" ; import { OutlineAndFillStyleEnum } from "./DrawingEnums" ; import { MusicSheetDrawer } from "./MusicSheetDrawer" ; import { GraphicalVoiceEntry } from "./GraphicalVoiceEntry" ; import { GraphicalObject } from "./GraphicalObject" ; /**
 * The graphical counterpart of a [[MusicSheet]]
 */ export class GraphicalMusicSheet { constructor ( musicSheet , calculator ) { this . musicSheet = musicSheet ; this . numberOfStaves = this . musicSheet . Staves . length ; this . calculator = calculator ; this . calculator . initialize ( this ) ; } private musicSheet : MusicSheet ; private calculator : MusicSheetCalculator ; public drawer : MusicSheetDrawer ; private musicPages : GraphicalMusicPage [ ] = [ ] ; /** measures (i,j) where i is the measure number and j the staff index (e.g. staff indices 0, 1 for two piano parts) */ private measureList : GraphicalMeasure [ ] [ ] = [ ] ; private verticalGraphicalStaffEntryContainers : VerticalGraphicalStaffEntryContainer [ ] = [ ] ; private title : GraphicalLabel ; private subtitle : GraphicalLabel ; private composer : GraphicalLabel ; private lyricist : GraphicalLabel ; private cursors : GraphicalLine [ ] = [ ] ; private selectionStartSymbol : SelectionStartSymbol ; private selectionEndSymbol : SelectionEndSymbol ; private minAllowedSystemWidth : number ; private numberOfStaves : number ; private leadSheet : boolean = false ; public get ParentMusicSheet ( ) : MusicSheet { return this . musicSheet ; } public get GetCalculator ( ) : MusicSheetCalculator { return this . calculator ; } public get MusicPages ( ) : GraphicalMusicPage [ ] { return this . musicPages ; } public set MusicPages ( value ) { this . musicPages = value ; } public get MeasureList ( ) : GraphicalMeasure [ ] [ ] { return this . measureList ; } public set MeasureList ( value ) { this . measureList = value ; } public get VerticalGraphicalStaffEntryContainers ( ) : VerticalGraphicalStaffEntryContainer [ ] { return this . verticalGraphicalStaffEntryContainers ; } public set VerticalGraphicalStaffEntryContainers ( value ) { this . verticalGraphicalStaffEntryContainers = value ; } public get Title ( ) : GraphicalLabel { return this . title ; } public set Title ( value ) { this . title = value ; } public get Subtitle ( ) : GraphicalLabel { return this . subtitle ; } public set Subtitle ( value ) { this . subtitle = value ; } public get Composer ( ) : GraphicalLabel { return this . composer ; } public set Composer ( value ) { this . composer = value ; } public get Lyricist ( ) : GraphicalLabel { return this . lyricist ; } public set Lyricist ( value ) { this . lyricist = value ; } public get Cursors ( ) : GraphicalLine [ ] { return this . cursors ; } public get SelectionStartSymbol ( ) : SelectionStartSymbol { return this . selectionStartSymbol ; } public get SelectionEndSymbol ( ) : SelectionEndSymbol { return this . selectionEndSymbol ; } public get MinAllowedSystemWidth ( ) : number { return this . minAllowedSystemWidth ; } public set MinAllowedSystemWidth ( value ) { this . minAllowedSystemWidth = value ; } public get NumberOfStaves ( ) : number { return this . numberOfStaves ; } public get LeadSheet ( ) : boolean { return this . leadSheet ; } public set LeadSheet ( value ) { this . leadSheet = value ; } param graphicalMusicSheet public static transformRelativeToAbsolutePosition ( graphicalMusicSheet ) { for ( let i = 0 ; i < graphicalMusicSheet . MusicPages . length ; i ++ ) { const pageAbsolute = graphicalMusicSheet . MusicPages [ i ] . setMusicPageAbsolutePosition ( i , graphicalMusicSheet . ParentMusicSheet . Rules ) ; const page = graphicalMusicSheet . MusicPages [ i ] ; page . PositionAndShape . calculateAbsolutePositionsRecursive ( pageAbsolute . x , pageAbsolute . y ) ; } } public Initialize ( ) { this . verticalGraphicalStaffEntryContainers = [ ] ; this . musicPages = [ ] ; this . measureList = [ ] ; } public reCalculate ( ) { this . calculator . calculate ( ) ; } public EnforceRedrawOfMusicSystems ( ) { for ( let idx = 0 , len = this . musicPages . length ; idx < len ; ++ idx ) { const graphicalMusicPage = this . musicPages [ idx ] ; for ( let idx2 = 0 , len2 = graphicalMusicPage . MusicSystems . length ; idx2 < len2 ; ++ idx2 ) { const musicSystem = graphicalMusicPage . MusicSystems [ idx2 ] ; musicSystem . needsToBeRedrawn = true ; } } } public getClickedObject < T > ( positionOnMusicSheet ) { for ( let idx = 0 , len = this . MusicPages . length ; idx < len ; ++ idx ) { const graphicalMusicPage = this . MusicPages [ idx ] ; return graphicalMusicPage . PositionAndShape . getClickedObjectOfType < T > ( positionOnMusicSheet ) ; } return undefined ; } public findGraphicalMeasure ( measureIndex , staffIndex ) { for ( let i = measureIndex ; i >= 0 ; i -- ) { const gMeasure = this . measureList [ i ] [ staffIndex ] ; if ( gMeasure ) { return gMeasure ; } } return undefined ; } param staffIndex param measureIndex param sourceStaffEntry returns { any } public findGraphicalStaffEntryFromMeasureList ( staffIndex , measureIndex , sourceStaffEntry ) { for ( let i = measureIndex ; i < this . measureList . length ; i ++ ) { const graphicalMeasure = this . measureList [ i ] [ staffIndex ] ; if ( ! graphicalMeasure ) { continue ; } for ( let idx = 0 , len = graphicalMeasure . staffEntries . length ; idx < len ; ++ idx ) { const graphicalStaffEntry = graphicalMeasure . staffEntries [ idx ] ; if ( graphicalStaffEntry . sourceStaffEntry === sourceStaffEntry ) { return graphicalStaffEntry ; } } } return undefined ; } param staffIndex param measureIndex param graphicalStaffEntry returns { any } public findNextGraphicalStaffEntry ( staffIndex , measureIndex , graphicalStaffEntry ) { const graphicalMeasure = graphicalStaffEntry . parentMeasure ; const graphicalStaffEntryIndex = graphicalMeasure . staffEntries . indexOf ( graphicalStaffEntry ) ; if ( graphicalStaffEntryIndex < graphicalMeasure . staffEntries . length - 1 ) { return graphicalMeasure . staffEntries [ graphicalStaffEntryIndex + 1 ] ; } else if ( measureIndex < this . measureList . length - 1 ) { const nextMeasure = this . measureList [ measureIndex + 1 ] [ staffIndex ] ; if ( nextMeasure . staffEntries . length > 0 ) { return nextMeasure . staffEntries [ 0 ] ; } } return undefined ; } public getFirstVisibleMeasuresListFromIndices ( start , end ) { const graphicalMeasures = [ ] ; const numberOfStaves = this . measureList [ 0 ] . length ; for ( let i = start ; i <= end ; i ++ ) { for ( let j = 0 ; j < numberOfStaves ; j ++ ) { if ( this . measureList [ i ] [ j ] . isVisible ( ) ) { graphicalMeasures . push ( this . measureList [ i ] [ j ] ) ; break ; } } } return graphicalMeasures ; } public orderMeasuresByStaffLine ( measures ) { const orderedMeasures = [ ] ; let mList = [ ] ; orderedMeasures . push ( mList ) ; for ( let i = 0 ; i < measures . length ; i ++ ) { if ( i === 0 ) { mList . push ( measures [ 0 ] ) ; } else { if ( measures [ i ] . ParentStaffLine === measures [ i - 1 ] . ParentStaffLine ) { mList . push ( measures [ i ] ) ; } else { if ( orderedMeasures . indexOf ( mList ) === - 1 ) { orderedMeasures . push ( mList ) ; } mList = [ ] ; orderedMeasures . push ( mList ) ; mList . push ( measures [ i ] ) ; } } } return orderedMeasures ; } returns { ClefInstruction [ ] } public initializeActiveClefs ( ) { const activeClefs = [ ] ; const firstSourceMeasure = this . musicSheet . getFirstSourceMeasure ( ) ; if ( firstSourceMeasure ) { for ( let i = 0 ; i < firstSourceMeasure . CompleteNumberOfStaves ; i ++ ) { let clef = new ClefInstruction ( ) ; 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 ClefInstruction ) { clef = < ClefInstruction > abstractNotationInstruction ; } } } activeClefs . push ( clef ) ; } } return activeClefs ; } public GetMainKey ( ) { const firstSourceMeasure = this . musicSheet . getFirstSourceMeasure ( ) ; if ( firstSourceMeasure ) { for ( let i = 0 ; i < firstSourceMeasure . CompleteNumberOfStaves ; 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 ) { return < KeyInstruction > abstractNotationInstruction ; } } } } return undefined ; } param timestamp returns { any } public getOrCreateVerticalContainer ( timestamp ) { if ( this . verticalGraphicalStaffEntryContainers . length === 0 || ( CollectionUtil . getLastElement ( this . verticalGraphicalStaffEntryContainers ) . AbsoluteTimestamp ) . lt ( timestamp ) ) { const verticalGraphicalStaffEntryContainer = new VerticalGraphicalStaffEntryContainer ( this . numberOfStaves , timestamp ) ; this . verticalGraphicalStaffEntryContainers . push ( verticalGraphicalStaffEntryContainer ) ; return verticalGraphicalStaffEntryContainer ; } for ( let i = this . verticalGraphicalStaffEntryContainers . length - 1 ; i >= 0 ; i -- ) { if ( this . verticalGraphicalStaffEntryContainers [ i ] . AbsoluteTimestamp . lt ( timestamp ) ) { const verticalGraphicalStaffEntryContainer = new VerticalGraphicalStaffEntryContainer ( this . numberOfStaves , timestamp ) ; this . verticalGraphicalStaffEntryContainers . splice ( i + 1 , 0 , verticalGraphicalStaffEntryContainer ) ; return verticalGraphicalStaffEntryContainer ; } if ( this . verticalGraphicalStaffEntryContainers [ i ] . AbsoluteTimestamp . Equals ( timestamp ) ) { return this . verticalGraphicalStaffEntryContainers [ i ] ; } } return undefined ; } param timestamp param startIndex returns { any } constructor public GetVerticalContainerFromTimestamp ( timestamp , startIndex = 0 ) { const index = CollectionUtil . binarySearch ( this . verticalGraphicalStaffEntryContainers , new VerticalGraphicalStaffEntryContainer ( 0 , timestamp ) , VerticalGraphicalStaffEntryContainer . compareByTimestamp , startIndex ) ; if ( index >= 0 ) { return this . verticalGraphicalStaffEntryContainers [ index ] ; } return undefined ; } param musicTimestamp returns { number } constructor public GetInterpolatedIndexInVerticalContainers ( musicTimestamp ) { const containers = this . verticalGraphicalStaffEntryContainers ; let leftIndex = 0 ; let rightIndex = containers . length - 1 ; let leftTS = undefined ; let rightTS = undefined ; if ( musicTimestamp . lte ( containers [ containers . length - 1 ] . AbsoluteTimestamp ) ) { while ( rightIndex - leftIndex > 1 ) { const middleIndex = Math . floor ( ( rightIndex + leftIndex ) / 2 ) ; if ( containers [ leftIndex ] . AbsoluteTimestamp . Equals ( musicTimestamp ) ) { rightIndex = leftIndex ; break ; } else if ( containers [ rightIndex ] . AbsoluteTimestamp . Equals ( musicTimestamp ) ) { leftIndex = rightIndex ; break ; } else if ( containers [ middleIndex ] . AbsoluteTimestamp . Equals ( musicTimestamp ) ) { return this . verticalGraphicalStaffEntryContainers . indexOf ( containers [ middleIndex ] ) ; } else if ( musicTimestamp . lt ( containers [ middleIndex ] . AbsoluteTimestamp ) ) { rightIndex = middleIndex ; } else { leftIndex = middleIndex ; } } if ( leftIndex === rightIndex ) { return this . verticalGraphicalStaffEntryContainers . indexOf ( containers [ leftIndex ] ) ; } leftTS = containers [ leftIndex ] . AbsoluteTimestamp ; rightTS = containers [ rightIndex ] . AbsoluteTimestamp ; } else { leftTS = containers [ containers . length - 1 ] . AbsoluteTimestamp ; rightTS = Fraction . plus ( this . getLongestStaffEntryDuration ( containers . length - 1 ) , leftTS ) ; rightIndex = containers . length ; } const diff = rightTS . RealValue - leftTS . RealValue ; const diffTS = rightTS . RealValue - musicTimestamp . RealValue ; const foundIndex = rightIndex - ( diffTS / diff ) ; return Math . min ( foundIndex , this . verticalGraphicalStaffEntryContainers . length ) ; } param visibleMeasures returns { number [ ] } public getVisibleStavesIndicesFromSourceMeasure ( visibleMeasures ) { const visibleInstruments = [ ] ; const visibleStavesIndices = [ ] ; for ( let idx = 0 , len = visibleMeasures . length ; idx < len ; ++ idx ) { const graphicalMeasure = visibleMeasures [ idx ] ; const instrument = graphicalMeasure . ParentStaff . ParentInstrument ; if ( visibleInstruments . indexOf ( instrument ) === - 1 ) { visibleInstruments . push ( instrument ) ; } } for ( let idx = 0 , len = visibleInstruments . length ; idx < len ; ++ idx ) { const instrument = visibleInstruments [ idx ] ; const index = this . musicSheet . getGlobalStaffIndexOfFirstStaff ( instrument ) ; for ( let j = 0 ; j < instrument . Staves . length ; j ++ ) { visibleStavesIndices . push ( index + j ) ; } } return visibleStavesIndices ; } param sourceMeasure param staffIndex returns { any } public getGraphicalMeasureFromSourceMeasureAndIndex ( sourceMeasure , staffIndex ) { for ( let i = 0 ; i < this . measureList . length ; i ++ ) { if ( this . measureList [ i ] [ 0 ] ?. parentSourceMeasure === sourceMeasure ) { return this . measureList [ i ] [ staffIndex ] ; } } return undefined ; } public getLastGraphicalMeasureFromIndex ( staffIndex , lastRendered = true ) { let measureIndex = this . measureList . length - 1 ; if ( lastRendered ) { measureIndex = Math . min ( measureIndex , this . musicSheet . Rules . MaxMeasureToDrawIndex ) ; } return this . measureList [ measureIndex ] [ staffIndex ] ; } public getMeasureIndex ( graphicalMeasure , measureIndex , inListIndex ) { measureIndex = 0 ; inListIndex = 0 ; for ( ; measureIndex < this . measureList . length ; measureIndex ++ ) { for ( let idx = 0 , len = this . measureList [ measureIndex ] . length ; idx < len ; ++ idx ) { const measure = this . measureList [ measureIndex ] [ idx ] ; if ( measure === graphicalMeasure ) { return true ; } } } return false ; } param clickPosition param className param startSearchArea param maxSearchArea param searchAreaIncrement param shouldBeIncludedTest private GetNearestGraphicalObject < T extends GraphicalObject > ( clickPosition , className = GraphicalObject . name , startSearchArea = 5 , maxSearchArea = 20 , searchAreaIncrement = 5 , shouldBeIncludedTest = undefined ) { const foundEntries = [ ] ; while ( foundEntries . length === 0 && startSearchArea <= maxSearchArea ) { const region = new BoundingBox ( undefined ) ; region . BorderLeft = clickPosition . x - startSearchArea ; region . BorderTop = clickPosition . y - startSearchArea ; region . BorderRight = clickPosition . x + startSearchArea ; region . BorderBottom = clickPosition . y + startSearchArea ; region . AbsolutePosition = new PointF2D ( clickPosition . x , clickPosition . y ) ; region . calculateAbsolutePosition ( ) ; for ( let idx = 0 , len = this . MusicPages . length ; idx < len ; ++ idx ) { const graphicalMusicPage = this . MusicPages [ idx ] ; const entries = graphicalMusicPage . PositionAndShape . getObjectsInRegion < T > ( region , false , className ) ; if ( ! entries || entries . length === 0 ) { continue ; } else { for ( let idx2 = 0 , len2 = entries . length ; idx2 < len2 ; ++ idx2 ) { if ( ! shouldBeIncludedTest ) { foundEntries . push ( entries [ idx2 ] ) ; } else if ( shouldBeIncludedTest ( entries [ idx2 ] ) ) { foundEntries . push ( entries [ idx2 ] ) ; } } } } startSearchArea += searchAreaIncrement ; } let closest = undefined ; for ( let idx = 0 , len = foundEntries . length ; idx < len ; ++ idx ) { const object = foundEntries [ idx ] ; if ( closest === undefined ) { closest = object ; } else { const deltaNew = this . CalculateDistance ( object . PositionAndShape . AbsolutePosition , clickPosition ) ; const deltaOld = this . CalculateDistance ( closest . PositionAndShape . AbsolutePosition , clickPosition ) ; if ( deltaNew < deltaOld ) { closest = object ; } } } if ( closest ) { return closest ; } return undefined ; } public GetNearestVoiceEntry ( clickPosition ) { return this . GetNearestGraphicalObject < GraphicalVoiceEntry > ( clickPosition , GraphicalVoiceEntry . name , 5 , 20 , 5 , ( object ) => object . parentStaffEntry ?. relInMeasureTimestamp !== undefined ) ; } public GetNearestNote ( clickPosition , maxClickDist ) { const nearestVoiceEntry = this . GetNearestVoiceEntry ( clickPosition ) ; if ( ! nearestVoiceEntry ) { return undefined ; } let closestNote ; let closestDist = Number . MAX_SAFE_INTEGER ; for ( const note of nearestVoiceEntry . notes ) { const posY = note . PositionAndShape . AbsolutePosition . y ; const distX = Math . abs ( note . PositionAndShape . AbsolutePosition . x - clickPosition . x ) ; const distY = Math . abs ( posY - clickPosition . y ) ; if ( distX + distY < closestDist ) { closestNote = note ; closestDist = distX + distY ; } } return closestNote ; } public domToSvg ( point ) { return this . domToSvgTransform ( point , true ) ; } public svgToDom ( point ) { return this . domToSvgTransform ( point , false ) ; } public svgToOsmd ( point ) { const pt = new PointF2D ( point . x , point . y ) ; pt . x /= 10 ; pt . y /= 10 ; return pt ; } private domToSvgTransform ( point , inverse ) { const svgBackend = ( this . drawer as any ) . Backends [ 0 ] ; const svg = svgBackend . getSvgElement ( ) as SVGSVGElement ; const pt = svg . createSVGPoint ( ) ; pt . x = point . x ; pt . y = point . y ; let transformMatrix = svg . getScreenCTM ( ) ; if ( inverse ) { transformMatrix = transformMatrix . inverse ( ) ; } const sp = pt . matrixTransform ( transformMatrix ) ; return new PointF2D ( sp . x , sp . y ) ; } public GetClickableLabel ( clickPosition ) { const initialSearchAreaX = 4 ; const initialSearchAreaY = 4 ; const region = new BoundingBox ( ) ; region . BorderLeft = clickPosition . x - initialSearchAreaX ; region . BorderTop = clickPosition . y - initialSearchAreaY ; region . BorderRight = clickPosition . x + initialSearchAreaX ; region . BorderBottom = clickPosition . y + initialSearchAreaY ; region . AbsolutePosition = new PointF2D ( 0 , 0 ) ; for ( let idx = 0 , len = this . MusicPages . length ; idx < len ; ++ idx ) { const graphicalMusicPage = this . MusicPages [ idx ] ; const entries = graphicalMusicPage . PositionAndShape . getObjectsInRegion < GraphicalLabel > ( region ) ; if ( entries . length !== 1 ) { continue ; } else { for ( let idx2 = 0 , len2 = entries . length ; idx2 < len2 ; ++ idx2 ) { const clickedLabel = entries [ idx2 ] ; return clickedLabel ; } } } return undefined ; } public GetNearestStaffEntry ( clickPosition ) { const initialSearchArea = 10 ; const foundEntries = [ ] ; const region = new BoundingBox ( undefined ) ; region . BorderLeft = clickPosition . x - initialSearchArea ; region . BorderTop = clickPosition . y - initialSearchArea ; region . BorderRight = clickPosition . x + initialSearchArea ; region . BorderBottom = clickPosition . y + initialSearchArea ; region . AbsolutePosition = new PointF2D ( 0 , 0 ) ; for ( let idx = 0 , len = this . MusicPages . length ; idx < len ; ++ idx ) { const graphicalMusicPage = this . MusicPages [ idx ] ; const entries = graphicalMusicPage . PositionAndShape . getObjectsInRegion < GraphicalStaffEntry > ( region , false ) ; if ( ! entries || entries . length === 0 ) { continue ; } else { for ( let idx2 = 0 , len2 = entries . length ; idx2 < len2 ; ++ idx2 ) { const gse = entries [ idx2 ] ; foundEntries . push ( gse ) ; } } } let closest = undefined ; for ( let idx = 0 , len = foundEntries . length ; idx < len ; ++ idx ) { const gse = foundEntries [ idx ] ; if ( closest === undefined ) { closest = gse ; } else { if ( ! gse . relInMeasureTimestamp ) { continue ; } const deltaNew = this . CalculateDistance ( gse . PositionAndShape . AbsolutePosition , clickPosition ) ; const deltaOld = this . CalculateDistance ( closest . PositionAndShape . AbsolutePosition , clickPosition ) ; if ( deltaNew < deltaOld ) { closest = gse ; } } } if ( closest ) { return closest ; } return undefined ; } public GetPossibleCommentAnchor ( clickPosition ) { const entry = this . GetNearestStaffEntry ( clickPosition ) ; if ( ! entry ) { return undefined ; } return entry . sourceStaffEntry ; } public getClickedObjectOfType < T > ( positionOnMusicSheet ) { for ( let idx = 0 , len = this . musicPages . length ; idx < len ; ++ idx ) { const page = this . musicPages [ idx ] ; const o = page . PositionAndShape . getClickedObjectOfType < T > ( positionOnMusicSheet ) ; if ( o ) { return ( o as T ) ; } } return undefined ; } public tryGetTimestampFromPosition ( positionOnMusicSheet ) { const entry = this . getClickedObjectOfType < GraphicalStaffEntry > ( positionOnMusicSheet ) ; if ( ! entry ) { return undefined ; } return entry . getAbsoluteTimestamp ( ) ; } public tryGetClickableLabel ( positionOnMusicSheet ) { try { return this . GetClickableLabel ( positionOnMusicSheet ) ; } catch ( ex ) { log . info ( "GraphicalMusicSheet.tryGetClickableObject" , "positionOnMusicSheet: " + positionOnMusicSheet , ex ) ; } return undefined ; } public tryGetTimeStampFromPosition ( positionOnMusicSheet ) { try { const entry = this . GetNearestStaffEntry ( positionOnMusicSheet ) ; if ( ! entry ) { return undefined ; } return entry . getAbsoluteTimestamp ( ) ; } catch ( ex ) { log . info ( "GraphicalMusicSheet.tryGetTimeStampFromPosition" , "positionOnMusicSheet: " + positionOnMusicSheet , ex ) ; } return undefined ; } param index returns { GraphicalStaffEntry } public getStaffEntry ( index ) { const container = this . VerticalGraphicalStaffEntryContainers [ index ] ; let staffEntry = undefined ; try { for ( let idx = 0 , len = container . StaffEntries . length ; idx < len ; ++ idx ) { const entry = container . StaffEntries [ idx ] ; if ( ! entry || ! entry . sourceStaffEntry . ParentStaff . ParentInstrument . Visible ) { continue ; } if ( ! staffEntry ) { staffEntry = entry ; } else if ( entry . PositionAndShape && staffEntry . PositionAndShape ) { if ( staffEntry . PositionAndShape . RelativePosition . x > entry . PositionAndShape . RelativePosition . x ) { staffEntry = entry ; } } } } catch ( ex ) { log . info ( "GraphicalMusicSheet.getStaffEntry" , ex ) ; } return staffEntry ; } param index returns { number } constructor public GetPreviousVisibleContainerIndex ( index ) { for ( let i = index - 1 ; i >= 0 ; i -- ) { const entries = this . verticalGraphicalStaffEntryContainers [ i ] . StaffEntries ; for ( let idx = 0 , len = entries . length ; idx < len ; ++ idx ) { const entry = entries [ idx ] ; if ( entry && entry . sourceStaffEntry . ParentStaff . ParentInstrument . Visible ) { return i ; } } } return - 1 ; } param index returns { number } constructor public GetNextVisibleContainerIndex ( index ) { for ( let i = index + 1 ; i < this . verticalGraphicalStaffEntryContainers . length ; ++ i ) { const entries = this . verticalGraphicalStaffEntryContainers [ i ] . StaffEntries ; for ( let idx = 0 , len = entries . length ; idx < len ; ++ idx ) { const entry = entries [ idx ] ; if ( entry && entry . sourceStaffEntry . ParentStaff . ParentInstrument . Visible ) { return i ; } } } return - 1 ; } public findClosestLeftStaffEntry ( fractionalIndex , searchOnlyVisibleEntries ) { let foundEntry = undefined ; let leftIndex = Math . floor ( fractionalIndex ) ; leftIndex = Math . min ( this . VerticalGraphicalStaffEntryContainers . length - 1 , leftIndex ) ; for ( let i = leftIndex ; i >= 0 ; i -- ) { foundEntry = this . getStaffEntry ( i ) ; if ( foundEntry ) { if ( searchOnlyVisibleEntries ) { if ( foundEntry . sourceStaffEntry . ParentStaff . ParentInstrument . Visible ) { return foundEntry ; } } else { return foundEntry ; } } } return undefined ; } public findClosestRightStaffEntry ( fractionalIndex , returnOnlyVisibleEntries ) { let foundEntry = undefined ; const rightIndex = Math . max ( 0 , Math . ceil ( fractionalIndex ) ) ; for ( let i = rightIndex ; i < this . VerticalGraphicalStaffEntryContainers . length ; i ++ ) { foundEntry = this . getStaffEntry ( i ) ; if ( foundEntry ) { if ( returnOnlyVisibleEntries ) { if ( foundEntry . sourceStaffEntry . ParentStaff . ParentInstrument . Visible ) { return foundEntry ; } } else { return foundEntry ; } } } return undefined ; } public calculateCursorLineAtTimestamp ( musicTimestamp , styleEnum ) { const result = this . calculateXPositionFromTimestamp ( musicTimestamp ) ; const xPos = result [ 0 ] ; const correspondingMusicSystem = result [ 1 ] ; if ( ! correspondingMusicSystem || correspondingMusicSystem . StaffLines . length === 0 ) { return undefined ; } const yCoordinate = correspondingMusicSystem . PositionAndShape . AbsolutePosition . y ; const height = CollectionUtil . last ( correspondingMusicSystem . StaffLines ) . PositionAndShape . RelativePosition . y + 4 ; return new GraphicalLine ( new PointF2D ( xPos , yCoordinate ) , new PointF2D ( xPos , yCoordinate + height ) , 3 , styleEnum ) ; } public calculateXPositionFromTimestamp ( timeStamp ) { let currentMusicSystem = undefined ; const fractionalIndex = this . GetInterpolatedIndexInVerticalContainers ( timeStamp ) ; const previousStaffEntry = this . findClosestLeftStaffEntry ( fractionalIndex , true ) ; const nextStaffEntry = this . findClosestRightStaffEntry ( fractionalIndex , true ) ; const currentTimeStamp = timeStamp . RealValue ; if ( ! previousStaffEntry && ! nextStaffEntry ) { return [ 0 , undefined ] ; } let previousStaffEntryMusicSystem = undefined ; if ( previousStaffEntry ) { previousStaffEntryMusicSystem = previousStaffEntry . parentMeasure . ParentStaffLine ?. ParentMusicSystem ; } else { previousStaffEntryMusicSystem = nextStaffEntry . parentMeasure . ParentStaffLine ?. ParentMusicSystem ; } let nextStaffEntryMusicSystem = undefined ; if ( nextStaffEntry ) { nextStaffEntryMusicSystem = nextStaffEntry . parentMeasure . ParentStaffLine ?. ParentMusicSystem ; } else { nextStaffEntryMusicSystem = previousStaffEntry . parentMeasure . ParentStaffLine ?. ParentMusicSystem ; } if ( previousStaffEntryMusicSystem === nextStaffEntryMusicSystem ) { currentMusicSystem = previousStaffEntryMusicSystem ; let fraction ; let previousStaffEntryPositionX ; let nextStaffEntryPositionX ; if ( ! previousStaffEntry ) { previousStaffEntryPositionX = nextStaffEntryPositionX = nextStaffEntry . PositionAndShape . AbsolutePosition . x ; fraction = 0 ; } else if ( ! nextStaffEntry ) { previousStaffEntryPositionX = previousStaffEntry . PositionAndShape . AbsolutePosition . x ; nextStaffEntryPositionX = currentMusicSystem . GetRightBorderAbsoluteXPosition ( ) ; const sm = previousStaffEntry . parentMeasure . parentSourceMeasure ; fraction = ( currentTimeStamp - previousStaffEntry . getAbsoluteTimestamp ( ) . RealValue ) / ( Fraction . plus ( sm . AbsoluteTimestamp , sm . Duration ) . RealValue - previousStaffEntry . getAbsoluteTimestamp ( ) . RealValue ) ; } else { previousStaffEntryPositionX = previousStaffEntry . PositionAndShape . AbsolutePosition . x ; nextStaffEntryPositionX = nextStaffEntry . PositionAndShape . AbsolutePosition . x ; if ( previousStaffEntry === nextStaffEntry ) { fraction = 0 ; } else { fraction = ( currentTimeStamp - previousStaffEntry . getAbsoluteTimestamp ( ) . RealValue ) / ( nextStaffEntry . getAbsoluteTimestamp ( ) . RealValue - previousStaffEntry . getAbsoluteTimestamp ( ) . RealValue ) ; } } fraction = Math . min ( 1 , Math . max ( 0 , fraction ) ) ; const interpolatedXPosition = previousStaffEntryPositionX + fraction * ( nextStaffEntryPositionX - previousStaffEntryPositionX ) ; return [ interpolatedXPosition , currentMusicSystem ] ; } else { const nextSystemLeftBorderTimeStamp = nextStaffEntry . parentMeasure . parentSourceMeasure . AbsoluteTimestamp . RealValue ; let fraction ; let interpolatedXPosition ; if ( currentTimeStamp < nextSystemLeftBorderTimeStamp ) { currentMusicSystem = previousStaffEntryMusicSystem ; const previousStaffEntryPositionX = previousStaffEntry . PositionAndShape . AbsolutePosition . x ; const previousSystemRightBorderX = currentMusicSystem . GetRightBorderAbsoluteXPosition ( ) ; fraction = ( currentTimeStamp - previousStaffEntry . getAbsoluteTimestamp ( ) . RealValue ) / ( nextSystemLeftBorderTimeStamp - previousStaffEntry . getAbsoluteTimestamp ( ) . RealValue ) ; fraction = Math . min ( 1 , Math . max ( 0 , fraction ) ) ; interpolatedXPosition = previousStaffEntryPositionX + fraction * ( previousSystemRightBorderX - previousStaffEntryPositionX ) ; } else { currentMusicSystem = nextStaffEntryMusicSystem ; const nextStaffEntryPositionX = nextStaffEntry . PositionAndShape . AbsolutePosition . x ; const nextSystemLeftBorderX = currentMusicSystem . GetLeftBorderAbsoluteXPosition ( ) ; fraction = ( currentTimeStamp - nextSystemLeftBorderTimeStamp ) / ( nextStaffEntry . getAbsoluteTimestamp ( ) . RealValue - nextSystemLeftBorderTimeStamp ) ; fraction = Math . min ( 1 , Math . max ( 0 , fraction ) ) ; interpolatedXPosition = nextSystemLeftBorderX + fraction * ( nextStaffEntryPositionX - nextSystemLeftBorderX ) ; } return [ interpolatedXPosition , currentMusicSystem ] ; } } public GetNumberOfVisibleInstruments ( ) { let visibleInstrumentCount = 0 ; for ( let idx = 0 , len = this . musicSheet . Instruments . length ; idx < len ; ++ idx ) { const instrument = this . musicSheet . Instruments [ idx ] ; if ( instrument . Visible === true ) { visibleInstrumentCount ++ ; } } return visibleInstrumentCount ; } public GetNumberOfFollowedInstruments ( ) { let followedInstrumentCount = 0 ; for ( let idx = 0 , len = this . musicSheet . Instruments . length ; idx < len ; ++ idx ) { const instrument = this . musicSheet . Instruments [ idx ] ; if ( instrument . Following === true ) { followedInstrumentCount ++ ; } } return followedInstrumentCount ; } public GetGraphicalFromSourceStaffEntry ( sourceStaffEntry ) { if ( ! sourceStaffEntry . VerticalContainerParent . ParentMeasure ?. VerticalMeasureList ) { return undefined ; } const graphicalMeasure = sourceStaffEntry . VerticalContainerParent . ParentMeasure . VerticalMeasureList [ sourceStaffEntry . ParentStaff . idInMusicSheet ] ; return graphicalMeasure . findGraphicalStaffEntryFromTimestamp ( sourceStaffEntry . Timestamp ) ; } private CalculateDistance ( pt1 , pt2 ) { const deltaX = pt1 . x - pt2 . x ; const deltaY = pt1 . y - pt2 . y ; return ( deltaX * deltaX ) + ( deltaY * deltaY ) ; } param index returns { Fraction } private getLongestStaffEntryDuration ( index ) { let maxLength = new Fraction ( 0 , 1 ) ; for ( const graphicalStaffEntry of this . verticalGraphicalStaffEntryContainers [ index ] . StaffEntries ) { if ( ! graphicalStaffEntry ) { continue ; } const maxLengthInStaffEntry = graphicalStaffEntry . findStaffEntryMaxNoteLength ( ) ; if ( maxLength . lt ( maxLengthInStaffEntry ) ) { maxLength = maxLengthInStaffEntry ; } } return maxLength ; } } export class SystemImageProperties { public positionInPixels : PointF2D ; public systemImageId : number ; public system : MusicSystem ; }