MusicSheetCalculator.ts 120 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167
  1. import {GraphicalStaffEntry} from "./GraphicalStaffEntry";
  2. import {StaffLine} from "./StaffLine";
  3. import {GraphicalMusicSheet} from "./GraphicalMusicSheet";
  4. import {EngravingRules} from "./EngravingRules";
  5. import {Tie} from "../VoiceData/Tie";
  6. import {Fraction} from "../../Common/DataObjects/Fraction";
  7. import {Note} from "../VoiceData/Note";
  8. import {MusicSheet} from "../MusicSheet";
  9. import {StaffMeasure} from "./StaffMeasure";
  10. import {ClefInstruction} from "../VoiceData/Instructions/ClefInstruction";
  11. import {LyricWord} from "../VoiceData/Lyrics/LyricsWord";
  12. import {SourceMeasure} from "../VoiceData/SourceMeasure";
  13. import {GraphicalMusicPage} from "./GraphicalMusicPage";
  14. import {GraphicalNote} from "./GraphicalNote";
  15. import {Beam} from "../VoiceData/Beam";
  16. import {OctaveEnum} from "../VoiceData/Expressions/ContinuousExpressions/OctaveShift";
  17. import {VoiceEntry, StemDirectionType} from "../VoiceData/VoiceEntry";
  18. import {OrnamentContainer} from "../VoiceData/OrnamentContainer";
  19. import {ArticulationEnum} from "../VoiceData/VoiceEntry";
  20. import {Tuplet} from "../VoiceData/Tuplet";
  21. import {MusicSystem} from "./MusicSystem";
  22. import {GraphicalTie} from "./GraphicalTie";
  23. import {RepetitionInstruction} from "../VoiceData/Instructions/RepetitionInstruction";
  24. import {MultiExpression} from "../VoiceData/Expressions/MultiExpression";
  25. import {StaffEntryLink} from "../VoiceData/StaffEntryLink";
  26. import {MusicSystemBuilder} from "./MusicSystemBuilder";
  27. import {MultiTempoExpression} from "../VoiceData/Expressions/MultiTempoExpression";
  28. import {Repetition} from "../MusicSource/Repetition";
  29. import {PointF2D} from "../../Common/DataObjects/PointF2D";
  30. import {SourceStaffEntry} from "../VoiceData/SourceStaffEntry";
  31. import {BoundingBox} from "./BoundingBox";
  32. import {Instrument} from "../Instrument";
  33. import {GraphicalLabel} from "./GraphicalLabel";
  34. import {TextAlignment} from "../../Common/Enums/TextAlignment";
  35. import {VerticalGraphicalStaffEntryContainer} from "./VerticalGraphicalStaffEntryContainer";
  36. import {KeyInstruction} from "../VoiceData/Instructions/KeyInstruction";
  37. import {AbstractNotationInstruction} from "../VoiceData/Instructions/AbstractNotationInstruction";
  38. import {TechnicalInstruction} from "../VoiceData/Instructions/TechnicalInstruction";
  39. import {Pitch} from "../../Common/DataObjects/Pitch";
  40. import {LinkedVoice} from "../VoiceData/LinkedVoice";
  41. import {ColDirEnum} from "./BoundingBox";
  42. import {IGraphicalSymbolFactory} from "../Interfaces/IGraphicalSymbolFactory";
  43. import {ITextMeasurer} from "../Interfaces/ITextMeasurer";
  44. import {ITransposeCalculator} from "../Interfaces/ITransposeCalculator";
  45. import {OctaveShiftParams} from "./OctaveShiftParams";
  46. import {AccidentalCalculator} from "./AccidentalCalculator";
  47. import {MidiInstrument} from "../VoiceData/Instructions/ClefInstruction";
  48. import {Staff} from "../VoiceData/Staff";
  49. import {OctaveShift} from "../VoiceData/Expressions/ContinuousExpressions/OctaveShift";
  50. import {Logging} from "../../Common/Logging";
  51. import Dictionary from "typescript-collections/dist/lib/Dictionary";
  52. import {CollectionUtil} from "../../Util/CollectionUtil";
  53. import {GraphicalLyricEntry} from "./GraphicalLyricEntry";
  54. import {GraphicalLyricWord} from "./GraphicalLyricWord";
  55. import {GraphicalLine} from "./GraphicalLine";
  56. import {Label} from "../Label";
  57. /**
  58. * Class used to do all the calculations in a MusicSheet, which in the end populates a GraphicalMusicSheet.
  59. */
  60. export abstract class MusicSheetCalculator {
  61. public static transposeCalculator: ITransposeCalculator;
  62. protected static textMeasurer: ITextMeasurer;
  63. protected staffEntriesWithGraphicalTies: GraphicalStaffEntry[] = [];
  64. protected staffEntriesWithOrnaments: GraphicalStaffEntry[] = [];
  65. protected staffEntriesWithChordSymbols: GraphicalStaffEntry[] = [];
  66. protected staffLinesWithLyricWords: StaffLine[] = [];
  67. protected staffLinesWithGraphicalExpressions: StaffLine[] = [];
  68. protected graphicalLyricWords: GraphicalLyricWord[] = [];
  69. protected graphicalMusicSheet: GraphicalMusicSheet;
  70. protected rules: EngravingRules;
  71. protected symbolFactory: IGraphicalSymbolFactory;
  72. constructor(symbolFactory: IGraphicalSymbolFactory) {
  73. this.symbolFactory = symbolFactory;
  74. }
  75. public static get TextMeasurer(): ITextMeasurer {
  76. return MusicSheetCalculator.textMeasurer;
  77. }
  78. public static set TextMeasurer(value: ITextMeasurer) {
  79. MusicSheetCalculator.textMeasurer = value;
  80. }
  81. protected get leadSheet(): boolean {
  82. return this.graphicalMusicSheet.LeadSheet;
  83. }
  84. private static addTieToTieTimestampsDict(tieTimestampListDict: Dictionary<Tie, Fraction[]>, note: Note): void {
  85. note.NoteTie.initializeBoolList();
  86. const tieTimestampList: Fraction[] = [];
  87. for (let m: number = 0; m < note.NoteTie.Fractions.length; m++) {
  88. let musicTimestamp: Fraction;
  89. if (m === 0) {
  90. musicTimestamp = Fraction.plus(note.calculateNoteLengthWithoutTie(), note.getAbsoluteTimestamp());
  91. } else {
  92. musicTimestamp = Fraction.plus(tieTimestampList[m - 1], note.NoteTie.Fractions[m - 1]);
  93. }
  94. tieTimestampList.push(musicTimestamp);
  95. }
  96. tieTimestampListDict.setValue(note.NoteTie, tieTimestampList);
  97. }
  98. private static setMeasuresMinStaffEntriesWidth(measures: StaffMeasure[], minimumStaffEntriesWidth: number): void {
  99. for (let idx: number = 0, len: number = measures.length; idx < len; ++idx) {
  100. const measure: StaffMeasure = measures[idx];
  101. measure.minimumStaffEntriesWidth = minimumStaffEntriesWidth;
  102. }
  103. }
  104. public initialize(graphicalMusicSheet: GraphicalMusicSheet): void {
  105. this.graphicalMusicSheet = graphicalMusicSheet;
  106. this.rules = graphicalMusicSheet.ParentMusicSheet.rules;
  107. this.prepareGraphicalMusicSheet();
  108. //this.calculate();
  109. }
  110. /**
  111. * Build the 2D [[GraphicalMeasure]] ist needed for the [[MusicSheetCalculator]].
  112. * Internally it creates [[GraphicalMeasure]]s, [[GraphicalStaffEntry]]'s and [[GraphicalNote]]s.
  113. */
  114. public prepareGraphicalMusicSheet(): void {
  115. // Clear the stored system images dict - all systems have to be redrawn.
  116. // Not necessary now. TODO Check
  117. // this.graphicalMusicSheet.SystemImages.length = 0;
  118. const musicSheet: MusicSheet = this.graphicalMusicSheet.ParentMusicSheet;
  119. this.staffEntriesWithGraphicalTies = [];
  120. this.staffEntriesWithOrnaments = [];
  121. this.staffEntriesWithChordSymbols = [];
  122. this.staffLinesWithLyricWords = [];
  123. this.staffLinesWithGraphicalExpressions = [];
  124. this.graphicalMusicSheet.Initialize();
  125. const measureList: StaffMeasure[][] = this.graphicalMusicSheet.MeasureList;
  126. // one AccidentalCalculator for each Staff (regardless of Instrument)
  127. const accidentalCalculators: AccidentalCalculator[] = this.createAccidentalCalculators();
  128. // List of Active ClefInstructions
  129. const activeClefs: ClefInstruction[] = this.graphicalMusicSheet.initializeActiveClefs();
  130. // LyricWord - GraphicalLyricWord Lists
  131. const lyricWords: LyricWord[] = [];
  132. const completeNumberOfStaves: number = musicSheet.getCompleteNumberOfStaves();
  133. // Octave Shifts List
  134. const openOctaveShifts: OctaveShiftParams[] = [];
  135. // TieList - timestampsArray
  136. const tieTimestampListDictList: Dictionary<Tie, Fraction[]>[] = [];
  137. for (let i: number = 0; i < completeNumberOfStaves; i++) {
  138. const tieTimestampListDict: Dictionary<Tie, Fraction[]> = new Dictionary<Tie, Fraction[]>();
  139. tieTimestampListDictList.push(tieTimestampListDict);
  140. openOctaveShifts.push(undefined);
  141. }
  142. // go through all SourceMeasures (taking into account normal SourceMusicParts and Repetitions)
  143. for (let idx: number = 0, len: number = musicSheet.SourceMeasures.length; idx < len; ++idx) {
  144. const sourceMeasure: SourceMeasure = musicSheet.SourceMeasures[idx];
  145. const graphicalMeasures: StaffMeasure[] = this.createGraphicalMeasuresForSourceMeasure(
  146. sourceMeasure,
  147. accidentalCalculators,
  148. lyricWords,
  149. tieTimestampListDictList,
  150. openOctaveShifts,
  151. activeClefs
  152. );
  153. measureList.push(graphicalMeasures);
  154. }
  155. this.handleStaffEntries();
  156. this.calculateVerticalContainersList();
  157. this.setIndecesToVerticalGraphicalContainers();
  158. }
  159. /**
  160. * The main method for the Calculator.
  161. */
  162. public calculate(): void {
  163. this.clearSystemsAndMeasures();
  164. // delete graphicalObjects that will be recalculated and create the GraphicalObjects that strech over a single StaffEntry new.
  165. this.clearRecreatedObjects();
  166. this.createGraphicalTies();
  167. // calculate SheetLabelBoundingBoxes
  168. this.calculateSheetLabelBoundingBoxes();
  169. this.calculateXLayout(this.graphicalMusicSheet, this.maxInstrNameLabelLength());
  170. // create List<MusicPage>
  171. this.graphicalMusicSheet.MusicPages.length = 0;
  172. // create new MusicSystems and StaffLines (as many as necessary) and populate them with Measures from measureList
  173. this.calculateMusicSystems();
  174. this.formatMeasures();
  175. // calculate all LyricWords Positions
  176. this.calculateLyricsPosition();
  177. // Add some white space at the end of the piece:
  178. this.graphicalMusicSheet.MusicPages[0].PositionAndShape.BorderMarginBottom += 9;
  179. // transform Relative to Absolute Positions
  180. GraphicalMusicSheet.transformRelativeToAbsolutePosition(this.graphicalMusicSheet);
  181. }
  182. public calculateXLayout(graphicalMusicSheet: GraphicalMusicSheet, maxInstrNameLabelLength: number): void {
  183. // for each inner List in big Measure List calculate new Positions for the StaffEntries
  184. // and adjust Measures sizes
  185. // calculate max measure length for maximum zoom in.
  186. let minLength: number = 0;
  187. const maxInstructionsLength: number = this.rules.MaxInstructionsConstValue;
  188. if (this.graphicalMusicSheet.MeasureList.length > 0) {
  189. let measures: StaffMeasure[] = this.graphicalMusicSheet.MeasureList[0];
  190. let minimumStaffEntriesWidth: number = this.calculateMeasureXLayout(measures);
  191. MusicSheetCalculator.setMeasuresMinStaffEntriesWidth(measures, minimumStaffEntriesWidth);
  192. minLength = minimumStaffEntriesWidth * 1.2 + maxInstrNameLabelLength + maxInstructionsLength;
  193. for (let i: number = 1; i < this.graphicalMusicSheet.MeasureList.length; i++) {
  194. measures = this.graphicalMusicSheet.MeasureList[i];
  195. minimumStaffEntriesWidth = this.calculateMeasureXLayout(measures);
  196. MusicSheetCalculator.setMeasuresMinStaffEntriesWidth(measures, minimumStaffEntriesWidth);
  197. minLength = Math.max(minLength, minimumStaffEntriesWidth * 1.2 + maxInstructionsLength);
  198. }
  199. }
  200. this.graphicalMusicSheet.MinAllowedSystemWidth = minLength;
  201. }
  202. protected formatMeasures(): void {
  203. throw new Error("abstract, not implemented");
  204. }
  205. /**
  206. * Calculates the x layout of the staff entries within the staff measures belonging to one source measure.
  207. * All staff entries are x-aligned throughout all the measures.
  208. * @param measures - The minimum required x width of the source measure
  209. */
  210. protected calculateMeasureXLayout(measures: StaffMeasure[]): number {
  211. throw new Error("abstract, not implemented");
  212. }
  213. protected calculateSystemYLayout(): void {
  214. throw new Error("abstract, not implemented");
  215. }
  216. /**
  217. * Called for every source measure when generating the list of staff measures for it.
  218. */
  219. protected initStaffMeasuresCreation(): void {
  220. throw new Error("abstract, not implemented");
  221. }
  222. protected handleBeam(graphicalNote: GraphicalNote, beam: Beam, openBeams: Beam[]): void {
  223. throw new Error("abstract, not implemented");
  224. }
  225. /**
  226. * Check if the tied graphical note belongs to any beams or tuplets and react accordingly.
  227. * @param tiedGraphicalNote
  228. * @param beams
  229. * @param activeClef
  230. * @param octaveShiftValue
  231. * @param graphicalStaffEntry
  232. * @param duration
  233. * @param openTie
  234. * @param isLastTieNote
  235. */
  236. protected handleTiedGraphicalNote(tiedGraphicalNote: GraphicalNote, beams: Beam[], activeClef: ClefInstruction,
  237. octaveShiftValue: OctaveEnum, graphicalStaffEntry: GraphicalStaffEntry, duration: Fraction,
  238. openTie: Tie, isLastTieNote: boolean): void {
  239. throw new Error("abstract, not implemented");
  240. }
  241. protected handleVoiceEntryLyrics(voiceEntry: VoiceEntry, graphicalStaffEntry: GraphicalStaffEntry,
  242. openLyricWords: LyricWord[]): void {
  243. throw new Error("abstract, not implemented");
  244. }
  245. protected handleVoiceEntryOrnaments(ornamentContainer: OrnamentContainer, voiceEntry: VoiceEntry,
  246. graphicalStaffEntry: GraphicalStaffEntry): void {
  247. throw new Error("abstract, not implemented");
  248. }
  249. protected handleVoiceEntryArticulations(articulations: ArticulationEnum[],
  250. voiceEntry: VoiceEntry,
  251. staffEntry: GraphicalStaffEntry): void {
  252. throw new Error("abstract, not implemented");
  253. }
  254. /**
  255. * Adds a technical instruction at the given staff entry.
  256. * @param technicalInstructions
  257. * @param voiceEntry
  258. * @param staffEntry
  259. */
  260. protected handleVoiceEntryTechnicalInstructions(technicalInstructions: TechnicalInstruction[],
  261. voiceEntry: VoiceEntry, staffEntry: GraphicalStaffEntry): void {
  262. throw new Error("abstract, not implemented");
  263. }
  264. protected handleTuplet(graphicalNote: GraphicalNote, tuplet: Tuplet, openTuplets: Tuplet[]): void {
  265. throw new Error("abstract, not implemented");
  266. }
  267. protected layoutVoiceEntry(voiceEntry: VoiceEntry, graphicalNotes: GraphicalNote[],
  268. graphicalStaffEntry: GraphicalStaffEntry, hasPitchedNote: boolean, isGraceStaffEntry: boolean): void {
  269. throw new Error("abstract, not implemented");
  270. }
  271. protected layoutStaffEntry(graphicalStaffEntry: GraphicalStaffEntry): void {
  272. throw new Error("abstract, not implemented");
  273. }
  274. protected createGraphicalTie(tie: Tie, startGse: GraphicalStaffEntry, endGse: GraphicalStaffEntry, startNote: GraphicalNote,
  275. endNote: GraphicalNote): GraphicalTie {
  276. throw new Error("abstract, not implemented");
  277. }
  278. protected updateStaffLineBorders(staffLine: StaffLine): void {
  279. throw new Error("abstract, not implemented");
  280. }
  281. /**
  282. * Iterate through all Measures and calculates the MeasureNumberLabels.
  283. * @param musicSystem
  284. */
  285. protected calculateMeasureNumberPlacement(musicSystem: MusicSystem): void {
  286. throw new Error("abstract, not implemented");
  287. }
  288. /**
  289. * Calculate the shape (Bézier curve) for this tie.
  290. * @param tie
  291. * @param tieIsAtSystemBreak
  292. */
  293. protected layoutGraphicalTie(tie: GraphicalTie, tieIsAtSystemBreak: boolean): void {
  294. throw new Error("abstract, not implemented");
  295. }
  296. // FIXME: There are several HACKS in this function to make multiline lyrics work without the skyline.
  297. // These need to be reverted once the skyline is available
  298. /**
  299. * Calculate the Lyrics YPositions for a single [[StaffLine]].
  300. * @param staffLine
  301. * @param lyricVersesNumber
  302. */
  303. protected calculateSingleStaffLineLyricsPosition(staffLine: StaffLine, lyricVersesNumber: number[]): GraphicalStaffEntry[] {
  304. let numberOfVerses: number = 0;
  305. // FIXME: There is no class SkyBottomLineCalculator -> Fix value
  306. let lyricsStartYPosition: number = this.rules.StaffHeight + 6.0; // Add offset to prevent collision
  307. const lyricsStaffEntriesList: GraphicalStaffEntry[] = [];
  308. // const skyBottomLineCalculator: SkyBottomLineCalculator = new SkyBottomLineCalculator(this.rules);
  309. // first find maximum Ycoordinate for the whole StaffLine
  310. let len: number = staffLine.Measures.length;
  311. for (let idx: number = 0; idx < len; ++idx) {
  312. const measure: StaffMeasure = staffLine.Measures[idx];
  313. const measureRelativePosition: PointF2D = measure.PositionAndShape.RelativePosition;
  314. const len2: number = measure.staffEntries.length;
  315. for (let idx2: number = 0; idx2 < len2; ++idx2) {
  316. const staffEntry: GraphicalStaffEntry = measure.staffEntries[idx2];
  317. if (staffEntry.LyricsEntries.length > 0) {
  318. lyricsStaffEntriesList.push(staffEntry);
  319. numberOfVerses = Math.max(numberOfVerses, staffEntry.LyricsEntries.length);
  320. // Position of Staffentry relative to StaffLine
  321. const staffEntryPositionX: number = staffEntry.PositionAndShape.RelativePosition.x +
  322. measureRelativePosition.x;
  323. let minMarginLeft: number = Number.MAX_VALUE;
  324. let maxMarginRight: number = Number.MAX_VALUE;
  325. // if more than one LyricEntry in StaffEntry, find minMarginLeft, maxMarginRight of all corresponding Labels
  326. for (let i: number = 0; i < staffEntry.LyricsEntries.length; i++) {
  327. const lyricsEntryLabel: GraphicalLabel = staffEntry.LyricsEntries[i].GraphicalLabel;
  328. minMarginLeft = Math.min(minMarginLeft, staffEntryPositionX + lyricsEntryLabel.PositionAndShape.BorderMarginLeft);
  329. maxMarginRight = Math.max(maxMarginRight, staffEntryPositionX + lyricsEntryLabel.PositionAndShape.BorderMarginRight);
  330. }
  331. // check BottomLine in this range and take the maximum between the two values
  332. // FIXME: There is no class SkyBottomLineCalculator -> Fix value
  333. // float bottomLineMax = skyBottomLineCalculator.getBottomLineMaxInRange(staffLine, minMarginLeft, maxMarginRight);
  334. const bottomLineMax: number = 0.0;
  335. lyricsStartYPosition = Math.max(lyricsStartYPosition, bottomLineMax);
  336. }
  337. }
  338. }
  339. let maxPosition: number = 4.0;
  340. // iterate again through the Staffentries with LyricEntries
  341. len = lyricsStaffEntriesList.length;
  342. for (let idx: number = 0; idx < len; ++idx) {
  343. const staffEntry: GraphicalStaffEntry = lyricsStaffEntriesList[idx];
  344. // set LyricEntryLabel RelativePosition
  345. for (let i: number = 0; i < staffEntry.LyricsEntries.length; i++) {
  346. const lyricEntry: GraphicalLyricEntry = staffEntry.LyricsEntries[i];
  347. const lyricsEntryLabel: GraphicalLabel = lyricEntry.GraphicalLabel;
  348. // read the verseNumber and get index of this number in the sorted LyricVerseNumbersList of Instrument
  349. // eg verseNumbers: 2,3,4,6 => 1,2,3,4
  350. const verseNumber: number = lyricEntry.GetLyricsEntry.VerseNumber;
  351. const sortedLyricVerseNumberIndex: number = lyricVersesNumber.indexOf(verseNumber);
  352. const firstPosition: number = lyricsStartYPosition + this.rules.LyricsHeight;
  353. // Y-position calculated according to aforementioned mapping
  354. let position: number = firstPosition + (this.rules.VerticalBetweenLyricsDistance + this.rules.LyricsHeight) * (sortedLyricVerseNumberIndex);
  355. if (this.leadSheet) {
  356. position = 3.4 + (this.rules.VerticalBetweenLyricsDistance + this.rules.LyricsHeight) * (sortedLyricVerseNumberIndex);
  357. }
  358. lyricsEntryLabel.PositionAndShape.RelativePosition = new PointF2D(0, position);
  359. maxPosition = Math.max(maxPosition, position);
  360. }
  361. }
  362. // update BottomLine (on the whole StaffLine's length)
  363. if (lyricsStaffEntriesList.length > 0) {
  364. /**
  365. * HACK START
  366. */
  367. let additionalPageLength: number = 0;
  368. maxPosition -= this.rules.StaffHeight;
  369. let iterator: StaffLine = staffLine.NextStaffLine;
  370. let systemMaxCount: number = 0;
  371. while (iterator !== undefined) {
  372. iterator.PositionAndShape.RelativePosition.y += maxPosition;
  373. iterator = iterator.NextStaffLine;
  374. systemMaxCount += maxPosition;
  375. additionalPageLength += maxPosition;
  376. }
  377. systemMaxCount -= this.rules.BetweenStaffDistance;
  378. let systemIterator: MusicSystem = staffLine.ParentMusicSystem.NextSystem;
  379. while (systemIterator !== undefined) {
  380. systemIterator.PositionAndShape.RelativePosition.y += systemMaxCount;
  381. systemIterator = systemIterator.NextSystem;
  382. additionalPageLength += systemMaxCount;
  383. }
  384. staffLine.ParentMusicSystem.Parent.PositionAndShape.BorderBottom += additionalPageLength;
  385. // Update the instrument labels
  386. staffLine.ParentMusicSystem.setMusicSystemLabelsYPosition();
  387. /**
  388. * HACK END
  389. */
  390. // const endX: number = staffLine.PositionAndShape.Size.width;
  391. // const startX: number = lyricsStaffEntriesList[0].PositionAndShape.RelativePosition.x +
  392. // lyricsStaffEntriesList[0].PositionAndShape.BorderMarginLeft +
  393. // lyricsStaffEntriesList[0].parentMeasure.PositionAndShape.RelativePosition.x;
  394. // FIXME: There is no class SkyBottomLineCalculator. This call should update the positions according to the last run
  395. // skyBottomLineCalculator.updateBottomLineInRange(staffLine, startX, endX, maxPosition);
  396. }
  397. return lyricsStaffEntriesList;
  398. }
  399. /**
  400. * calculates the dashes of lyric words and the extending underscore lines of syllables sung on more than one note.
  401. * @param lyricsStaffEntries
  402. */
  403. protected calculateLyricsExtendsAndDashes(lyricsStaffEntries: GraphicalStaffEntry[]): void {
  404. // iterate again to create now the extend lines and dashes for words
  405. for (let idx: number = 0, len: number = lyricsStaffEntries.length; idx < len; ++idx) {
  406. const staffEntry: GraphicalStaffEntry = lyricsStaffEntries[idx];
  407. // set LyricEntryLabel RelativePosition
  408. for (let i: number = 0; i < staffEntry.LyricsEntries.length; i++) {
  409. const lyricEntry: GraphicalLyricEntry = staffEntry.LyricsEntries[i];
  410. // calculate LyricWord's Dashes and underscoreLine
  411. if (lyricEntry.ParentLyricWord !== undefined &&
  412. lyricEntry.ParentLyricWord.GraphicalLyricsEntries[lyricEntry.ParentLyricWord.GraphicalLyricsEntries.length - 1] !== lyricEntry) {
  413. this.calculateSingleLyricWord(lyricEntry);
  414. }
  415. // calculate the underscore line extend if needed
  416. if (lyricEntry.GetLyricsEntry.extend) {
  417. this.calculateLyricExtend(lyricEntry);
  418. }
  419. }
  420. }
  421. }
  422. /**
  423. * Calculate a single OctaveShift for a [[MultiExpression]].
  424. * @param sourceMeasure
  425. * @param multiExpression
  426. * @param measureIndex
  427. * @param staffIndex
  428. */
  429. protected calculateSingleOctaveShift(sourceMeasure: SourceMeasure, multiExpression: MultiExpression,
  430. measureIndex: number, staffIndex: number): void {
  431. throw new Error("abstract, not implemented");
  432. }
  433. /**
  434. * Calculate all the textual [[RepetitionInstruction]]s (e.g. dal segno) for a single [[SourceMeasure]].
  435. * @param repetitionInstruction
  436. * @param measureIndex
  437. */
  438. protected calculateWordRepetitionInstruction(repetitionInstruction: RepetitionInstruction,
  439. measureIndex: number): void {
  440. throw new Error("abstract, not implemented");
  441. }
  442. /**
  443. * Calculate all the Mood and Unknown Expressions for a single [[MultiExpression]].
  444. * @param multiExpression
  445. * @param measureIndex
  446. * @param staffIndex
  447. */
  448. protected calculateMoodAndUnknownExpression(multiExpression: MultiExpression, measureIndex: number, staffIndex: number): void {
  449. throw new Error("abstract, not implemented");
  450. }
  451. /**
  452. * Delete all Objects that must be recalculated.
  453. * If graphicalMusicSheet.reCalculate has been called, then this method will be called to reset or remove all flexible
  454. * graphical music symbols (e.g. Ornaments, Lyrics, Slurs) graphicalMusicSheet will have MusicPages, they will have MusicSystems etc...
  455. */
  456. protected clearRecreatedObjects(): void {
  457. // Clear StaffEntries with GraphicalTies
  458. for (let idx: number = 0, len: number = this.staffEntriesWithGraphicalTies.length; idx < len; ++idx) {
  459. const staffEntriesWithGraphicalTie: GraphicalStaffEntry = this.staffEntriesWithGraphicalTies[idx];
  460. staffEntriesWithGraphicalTie.GraphicalTies.length = 0;
  461. }
  462. this.staffEntriesWithGraphicalTies.length = 0;
  463. return;
  464. }
  465. /**
  466. * This method handles a [[StaffEntryLink]].
  467. * @param graphicalStaffEntry
  468. * @param staffEntryLinks
  469. */
  470. protected handleStaffEntryLink(graphicalStaffEntry: GraphicalStaffEntry,
  471. staffEntryLinks: StaffEntryLink[]): void {
  472. Logging.debug("handleStaffEntryLink not implemented");
  473. }
  474. /**
  475. * Store the newly computed [[Measure]]s in newly created [[MusicSystem]]s.
  476. */
  477. protected calculateMusicSystems(): void {
  478. if (this.graphicalMusicSheet.MeasureList === undefined) {
  479. return;
  480. }
  481. const allMeasures: StaffMeasure[][] = this.graphicalMusicSheet.MeasureList;
  482. if (allMeasures === undefined) {
  483. return;
  484. }
  485. // visible 2D-MeasureList
  486. const visibleMeasureList: StaffMeasure[][] = [];
  487. for (let idx: number = 0, len: number = allMeasures.length; idx < len; ++idx) {
  488. const staffMeasures: StaffMeasure[] = allMeasures[idx];
  489. const visibleStaffMeasures: StaffMeasure[] = [];
  490. for (let idx2: number = 0, len2: number = staffMeasures.length; idx2 < len2; ++idx2) {
  491. const staffMeasure: StaffMeasure = allMeasures[idx][idx2];
  492. if (staffMeasure.isVisible()) {
  493. visibleStaffMeasures.push(staffMeasure);
  494. }
  495. }
  496. visibleMeasureList.push(visibleStaffMeasures);
  497. }
  498. // find out how many StaffLine Instances we need
  499. let numberOfStaffLines: number = 0;
  500. for (let idx: number = 0, len: number = visibleMeasureList.length; idx < len; ++idx) {
  501. const gmlist: StaffMeasure[] = visibleMeasureList[idx];
  502. numberOfStaffLines = Math.max(gmlist.length, numberOfStaffLines);
  503. break;
  504. }
  505. if (numberOfStaffLines === 0) {
  506. return;
  507. }
  508. // build the MusicSystems
  509. const musicSystemBuilder: MusicSystemBuilder = new MusicSystemBuilder();
  510. musicSystemBuilder.initialize(this.graphicalMusicSheet, visibleMeasureList, numberOfStaffLines, this.symbolFactory);
  511. musicSystemBuilder.buildMusicSystems();
  512. // check for Measures with only WholeRestNotes and correct their X-Position (middle of Measure)
  513. this.checkMeasuresForWholeRestNotes();
  514. if (!this.leadSheet) {
  515. // calculate Beam Placement
  516. this.calculateBeams();
  517. // possible Displacement of RestNotes
  518. this.optimizeRestPlacement();
  519. // possible Displacement of RestNotes
  520. this.calculateStaffEntryArticulationMarks();
  521. // calculate Ties
  522. this.calculateTieCurves();
  523. }
  524. // calculate Sky- and BottomLine
  525. // will have reasonable values only between ObjectsBorders (eg StaffEntries)
  526. this.calculateSkyBottomLines();
  527. // calculate TupletsNumbers
  528. this.calculateTupletNumbers();
  529. // calculate MeasureNumbers
  530. for (let idx: number = 0, len: number = this.graphicalMusicSheet.MusicPages.length; idx < len; ++idx) {
  531. const graphicalMusicPage: GraphicalMusicPage = this.graphicalMusicSheet.MusicPages[idx];
  532. for (let idx2: number = 0, len2: number = graphicalMusicPage.MusicSystems.length; idx2 < len2; ++idx2) {
  533. const musicSystem: MusicSystem = graphicalMusicPage.MusicSystems[idx2];
  534. this.calculateMeasureNumberPlacement(musicSystem);
  535. }
  536. }
  537. // calculate Slurs
  538. if (!this.leadSheet) {
  539. this.calculateSlurs();
  540. }
  541. // calculate StaffEntry Ornaments
  542. // (must come after Slurs)
  543. if (!this.leadSheet) {
  544. this.calculateOrnaments();
  545. }
  546. // update Sky- and BottomLine with borderValues 0.0 and 4.0 respectively
  547. // (must also come after Slurs)
  548. this.updateSkyBottomLines();
  549. // calculate StaffEntry ChordSymbols
  550. this.calculateChordSymbols();
  551. if (!this.leadSheet) {
  552. // calculate all Instantanious/Continuous Dynamics Expressions
  553. this.calculateDynamicExpressions();
  554. // place neighbouring DynamicExpressions at the same height
  555. this.optimizeStaffLineDynamicExpressionsPositions();
  556. // calculate all Mood and Unknown Expression
  557. this.calculateMoodAndUnknownExpressions();
  558. // calculate all OctaveShifts
  559. this.calculateOctaveShifts();
  560. // calucalte RepetitionInstructions (Dal Segno, Coda, etc)
  561. this.calculateWordRepetitionInstructions();
  562. }
  563. // calculate endings last, so they appear above measure numbers
  564. this.calculateRepetitionEndings();
  565. // calcualte all Tempo Expressions
  566. if (!this.leadSheet) {
  567. this.calculateTempoExpressions();
  568. }
  569. // update all StaffLine's Borders
  570. // create temporary Object, just to call the methods (in order to avoid declaring them static)
  571. for (let idx: number = 0, len: number = this.graphicalMusicSheet.MusicPages.length; idx < len; ++idx) {
  572. const graphicalMusicPage: GraphicalMusicPage = this.graphicalMusicSheet.MusicPages[idx];
  573. for (let idx2: number = 0, len2: number = graphicalMusicPage.MusicSystems.length; idx2 < len2; ++idx2) {
  574. const musicSystem: MusicSystem = graphicalMusicPage.MusicSystems[idx2];
  575. for (let idx3: number = 0, len3: number = musicSystem.StaffLines.length; idx3 < len3; ++idx3) {
  576. const staffLine: StaffLine = musicSystem.StaffLines[idx3];
  577. this.updateStaffLineBorders(staffLine);
  578. }
  579. }
  580. }
  581. // calculate Comments for each Staffline
  582. this.calculateComments();
  583. // Y-spacing
  584. this.calculateSystemYLayout();
  585. // calculate marked Areas for Systems
  586. this.calculateMarkedAreas();
  587. // the following must be done after Y-spacing, when the MusicSystems's final Dimensions are set
  588. // set the final yPositions of Objects such as SystemLabels and SystemLinesContainers,
  589. // create all System Lines, Brackets and MeasureNumbers (for all systems and for all pages)
  590. for (let idx: number = 0, len: number = this.graphicalMusicSheet.MusicPages.length; idx < len; ++idx) {
  591. const graphicalMusicPage: GraphicalMusicPage = this.graphicalMusicSheet.MusicPages[idx];
  592. for (let idx2: number = 0, len2: number = graphicalMusicPage.MusicSystems.length; idx2 < len2; ++idx2) {
  593. const musicSystem: MusicSystem = graphicalMusicPage.MusicSystems[idx2];
  594. musicSystem.setMusicSystemLabelsYPosition();
  595. if (!this.leadSheet) {
  596. musicSystem.setYPositionsToVerticalLineObjectsAndCreateLines(this.rules);
  597. musicSystem.createSystemLeftLine(this.rules.SystemThinLineWidth, this.rules.SystemLabelsRightMargin);
  598. musicSystem.createInstrumentBrackets(this.graphicalMusicSheet.ParentMusicSheet.Instruments, this.rules.StaffHeight);
  599. musicSystem.createGroupBrackets(this.graphicalMusicSheet.ParentMusicSheet.InstrumentalGroups, this.rules.StaffHeight, 0);
  600. musicSystem.alignBeginInstructions();
  601. } else if (musicSystem === musicSystem.Parent.MusicSystems[0]) {
  602. musicSystem.createSystemLeftLine(this.rules.SystemThinLineWidth, this.rules.SystemLabelsRightMargin);
  603. }
  604. musicSystem.calculateBorders(this.rules);
  605. }
  606. const distance: number = graphicalMusicPage.MusicSystems[0].PositionAndShape.BorderTop;
  607. for (let idx2: number = 0, len2: number = graphicalMusicPage.MusicSystems.length; idx2 < len2; ++idx2) {
  608. const musicSystem: MusicSystem = graphicalMusicPage.MusicSystems[idx2];
  609. // let newPosition: PointF2D = new PointF2D(musicSystem.PositionAndShape.RelativePosition.x,
  610. // musicSystem.PositionAndShape.RelativePosition.y - distance);
  611. musicSystem.PositionAndShape.RelativePosition =
  612. new PointF2D(musicSystem.PositionAndShape.RelativePosition.x, musicSystem.PositionAndShape.RelativePosition.y - distance);
  613. }
  614. for (let idx2: number = 0, len2: number = graphicalMusicPage.MusicSystems.length; idx2 < len2; ++idx2) {
  615. const musicSystem: MusicSystem = graphicalMusicPage.MusicSystems[idx2];
  616. for (let idx3: number = 0, len3: number = musicSystem.StaffLines.length; idx3 < len3; ++idx3) {
  617. const staffLine: StaffLine = musicSystem.StaffLines[idx3];
  618. staffLine.addActivitySymbolClickArea();
  619. }
  620. }
  621. // calculate all Labels's Positions for the first Page
  622. if (graphicalMusicPage === this.graphicalMusicSheet.MusicPages[0]) {
  623. this.calculatePageLabels(graphicalMusicPage);
  624. }
  625. // calculate TopBottom Borders for all elements recursively
  626. graphicalMusicPage.PositionAndShape.calculateTopBottomBorders();
  627. }
  628. }
  629. protected updateSkyBottomLine(staffLine: StaffLine): void {
  630. //Logging.debug("updateSkyBottomLine not implemented");
  631. return;
  632. }
  633. protected calculateSkyBottomLine(staffLine: StaffLine): void {
  634. //Logging.debug("calculateSkyBottomLine not implemented");
  635. return;
  636. }
  637. protected calculateMarkedAreas(): void {
  638. //Logging.debug("calculateMarkedAreas not implemented");
  639. return;
  640. }
  641. protected calculateComments(): void {
  642. //Logging.debug("calculateComments not implemented");
  643. return;
  644. }
  645. /**
  646. * Iterate through all the [[StaffLine]]s in order to check for possible optimizations in the placement of the [[GraphicalExpression]]s.
  647. */
  648. protected optimizeStaffLineDynamicExpressionsPositions(): void {
  649. return;
  650. }
  651. protected calculateChordSymbols(): void {
  652. return;
  653. }
  654. /**
  655. * Do layout on staff measures with only consist of a full rest.
  656. * @param rest
  657. * @param gse
  658. * @param measure
  659. */
  660. protected layoutMeasureWithWholeRest(rest: GraphicalNote, gse: GraphicalStaffEntry,
  661. measure: StaffMeasure): void {
  662. return;
  663. }
  664. protected layoutBeams(staffEntry: GraphicalStaffEntry): void {
  665. return;
  666. }
  667. protected layoutArticulationMarks(articulations: ArticulationEnum[], voiceEntry: VoiceEntry, graphicalStaffEntry: GraphicalStaffEntry): void {
  668. return;
  669. }
  670. protected layoutOrnament(ornaments: OrnamentContainer, voiceEntry: VoiceEntry,
  671. graphicalStaffEntry: GraphicalStaffEntry): void {
  672. return;
  673. }
  674. protected calculateRestNotePlacementWithinGraphicalBeam(graphicalStaffEntry: GraphicalStaffEntry,
  675. restNote: GraphicalNote,
  676. previousNote: GraphicalNote,
  677. nextStaffEntry: GraphicalStaffEntry,
  678. nextNote: GraphicalNote): void {
  679. return;
  680. }
  681. protected calculateTupletNumbers(): void {
  682. return;
  683. }
  684. protected calculateSlurs(): void {
  685. return;
  686. }
  687. protected calculateDynamicExpressionsForSingleMultiExpression(multiExpression: MultiExpression, measureIndex: number, staffIndex: number): void {
  688. return;
  689. }
  690. protected calcGraphicalRepetitionEndingsRecursively(repetition: Repetition): void {
  691. return;
  692. }
  693. /**
  694. * Calculate a single GraphicalRepetition.
  695. * @param start
  696. * @param end
  697. * @param numberText
  698. * @param offset
  699. * @param leftOpen
  700. * @param rightOpen
  701. */
  702. protected layoutSingleRepetitionEnding(start: StaffMeasure, end: StaffMeasure, numberText: string,
  703. offset: number, leftOpen: boolean, rightOpen: boolean): void {
  704. return;
  705. }
  706. protected calculateTempoExpressionsForSingleMultiTempoExpression(sourceMeasure: SourceMeasure, multiTempoExpression: MultiTempoExpression,
  707. measureIndex: number): void {
  708. return;
  709. }
  710. protected staffMeasureCreatedCalculations(measure: StaffMeasure): void {
  711. return;
  712. }
  713. protected clearSystemsAndMeasures(): void {
  714. for (let idx: number = 0, len: number = this.graphicalMusicSheet.MusicPages.length; idx < len; ++idx) {
  715. const graphicalMusicPage: GraphicalMusicPage = this.graphicalMusicSheet.MusicPages[idx];
  716. for (let idx2: number = 0, len2: number = graphicalMusicPage.MusicSystems.length; idx2 < len2; ++idx2) {
  717. const musicSystem: MusicSystem = graphicalMusicPage.MusicSystems[idx2];
  718. for (let idx3: number = 0, len3: number = musicSystem.StaffLines.length; idx3 < len3; ++idx3) {
  719. const staffLine: StaffLine = musicSystem.StaffLines[idx3];
  720. for (let idx4: number = 0, len4: number = staffLine.Measures.length; idx4 < len4; ++idx4) {
  721. const graphicalMeasure: StaffMeasure = staffLine.Measures[idx4];
  722. if (graphicalMeasure.FirstInstructionStaffEntry !== undefined) {
  723. const index: number = graphicalMeasure.PositionAndShape.ChildElements.indexOf(
  724. graphicalMeasure.FirstInstructionStaffEntry.PositionAndShape
  725. );
  726. if (index > -1) {
  727. graphicalMeasure.PositionAndShape.ChildElements.splice(index, 1);
  728. }
  729. graphicalMeasure.FirstInstructionStaffEntry = undefined;
  730. graphicalMeasure.beginInstructionsWidth = 0.0;
  731. }
  732. if (graphicalMeasure.LastInstructionStaffEntry !== undefined) {
  733. const index: number = graphicalMeasure.PositionAndShape.ChildElements.indexOf(
  734. graphicalMeasure.LastInstructionStaffEntry.PositionAndShape
  735. );
  736. if (index > -1) {
  737. graphicalMeasure.PositionAndShape.ChildElements.splice(index, 1);
  738. }
  739. graphicalMeasure.LastInstructionStaffEntry = undefined;
  740. graphicalMeasure.endInstructionsWidth = 0.0;
  741. }
  742. }
  743. staffLine.Measures = [];
  744. staffLine.PositionAndShape.ChildElements = [];
  745. }
  746. musicSystem.StaffLines.length = 0;
  747. musicSystem.PositionAndShape.ChildElements = [];
  748. }
  749. graphicalMusicPage.MusicSystems = [];
  750. graphicalMusicPage.PositionAndShape.ChildElements = [];
  751. }
  752. this.graphicalMusicSheet.MusicPages = [];
  753. }
  754. protected handleVoiceEntry(voiceEntry: VoiceEntry, graphicalStaffEntry: GraphicalStaffEntry,
  755. accidentalCalculator: AccidentalCalculator, openLyricWords: LyricWord[],
  756. tieTimestampListDict: Dictionary<Tie, Fraction[]>, activeClef: ClefInstruction,
  757. openTuplets: Tuplet[], openBeams: Beam[],
  758. octaveShiftValue: OctaveEnum, grace: boolean = false, linkedNotes: Note[] = undefined,
  759. sourceStaffEntry: SourceStaffEntry = undefined): OctaveEnum {
  760. this.calculateStemDirectionFromVoices(voiceEntry);
  761. const graphicalNotes: GraphicalNote[] = graphicalStaffEntry.findOrCreateGraphicalNotesListFromVoiceEntry(voiceEntry);
  762. for (let idx: number = 0, len: number = voiceEntry.Notes.length; idx < len; ++idx) {
  763. const note: Note = voiceEntry.Notes[idx];
  764. if (sourceStaffEntry !== undefined && sourceStaffEntry.Link !== undefined && linkedNotes !== undefined && linkedNotes.indexOf(note) > -1) {
  765. continue;
  766. }
  767. let graphicalNote: GraphicalNote;
  768. if (grace) {
  769. graphicalNote = this.symbolFactory.createGraceNote(note, graphicalStaffEntry, activeClef, octaveShiftValue);
  770. } else {
  771. graphicalNote = this.symbolFactory.createNote(note, graphicalStaffEntry, activeClef, octaveShiftValue, undefined);
  772. }
  773. if (note.NoteTie !== undefined) {
  774. MusicSheetCalculator.addTieToTieTimestampsDict(tieTimestampListDict, note);
  775. }
  776. if (note.Pitch !== undefined) {
  777. this.checkNoteForAccidental(graphicalNote, accidentalCalculator, activeClef, octaveShiftValue, grace);
  778. }
  779. this.resetYPositionForLeadSheet(graphicalNote.PositionAndShape);
  780. graphicalStaffEntry.addGraphicalNoteToListAtCorrectYPosition(graphicalNotes, graphicalNote);
  781. graphicalNote.PositionAndShape.calculateBoundingBox();
  782. if (!this.leadSheet) {
  783. if (note.NoteBeam !== undefined) {
  784. this.handleBeam(graphicalNote, note.NoteBeam, openBeams);
  785. }
  786. if (note.NoteTuplet !== undefined) {
  787. this.handleTuplet(graphicalNote, note.NoteTuplet, openTuplets);
  788. }
  789. }
  790. }
  791. if (voiceEntry.Articulations.length > 0) {
  792. this.handleVoiceEntryArticulations(voiceEntry.Articulations, voiceEntry, graphicalStaffEntry);
  793. }
  794. if (voiceEntry.TechnicalInstructions.length > 0) {
  795. this.handleVoiceEntryTechnicalInstructions(voiceEntry.TechnicalInstructions, voiceEntry, graphicalStaffEntry);
  796. }
  797. if (voiceEntry.LyricsEntries.size() > 0) {
  798. this.handleVoiceEntryLyrics(voiceEntry, graphicalStaffEntry, openLyricWords);
  799. }
  800. if (voiceEntry.OrnamentContainer !== undefined) {
  801. this.handleVoiceEntryOrnaments(voiceEntry.OrnamentContainer, voiceEntry, graphicalStaffEntry);
  802. }
  803. return octaveShiftValue;
  804. }
  805. protected handleVoiceEntryGraceNotes(graceEntries: VoiceEntry[], graphicalGraceEntries: GraphicalStaffEntry[], graphicalStaffEntry: GraphicalStaffEntry,
  806. accidentalCalculator: AccidentalCalculator, activeClef: ClefInstruction,
  807. octaveShiftValue: OctaveEnum, lyricWords: LyricWord[],
  808. tieTimestampListDict: Dictionary<Tie, Fraction[]>,
  809. tuplets: Tuplet[], beams: Beam[]): void {
  810. if (graceEntries !== undefined) {
  811. for (let idx: number = 0, len: number = graceEntries.length; idx < len; ++idx) {
  812. const graceVoiceEntry: VoiceEntry = graceEntries[idx];
  813. const graceStaffEntry: GraphicalStaffEntry = this.symbolFactory.createGraceStaffEntry(
  814. graphicalStaffEntry,
  815. graphicalStaffEntry.parentMeasure
  816. );
  817. graphicalGraceEntries.push(graceStaffEntry);
  818. this.handleVoiceEntry(
  819. graceVoiceEntry, graceStaffEntry, accidentalCalculator, lyricWords,
  820. tieTimestampListDict, activeClef, tuplets,
  821. beams, octaveShiftValue, true
  822. );
  823. }
  824. }
  825. }
  826. protected handleOpenTies(measure: StaffMeasure, beams: Beam[], tieTimestampListDict: Dictionary<Tie, Fraction[]>,
  827. activeClef: ClefInstruction, octaveShiftParams: OctaveShiftParams): void {
  828. CollectionUtil.removeDictElementIfTrue( this, tieTimestampListDict,
  829. function (thisPointer: MusicSheetCalculator, openTie: Tie, tieTimestamps: Fraction[]): boolean {
  830. // for (let m: number = tieTimestampListDict.size() - 1; m >= 0; m--) {
  831. // let keyValuePair: KeyValuePair<Tie, Fraction[]> = tieTimestampListDict.ElementAt(m);
  832. // let openTie: Tie = keyValuePair.Key;
  833. // let tieTimestamps: Fraction[] = keyValuePair.Value;
  834. let absoluteTimestamp: Fraction = undefined;
  835. let removeTie: boolean = false;
  836. for (let k: number = 0; k < tieTimestamps.length; k++) {
  837. if (!openTie.NoteHasBeenCreated[k]) {
  838. absoluteTimestamp = tieTimestamps[k];
  839. if (Fraction.plus(measure.parentSourceMeasure.AbsoluteTimestamp, measure.parentSourceMeasure.Duration).lte(absoluteTimestamp)) {
  840. continue;
  841. }
  842. let graphicalStaffEntry: GraphicalStaffEntry = undefined;
  843. if (absoluteTimestamp !== undefined) {
  844. for (let idx: number = 0, len: number = measure.staffEntries.length; idx < len; ++idx) {
  845. const gse: GraphicalStaffEntry = measure.staffEntries[idx];
  846. if (gse.getAbsoluteTimestamp().Equals(absoluteTimestamp)) {
  847. graphicalStaffEntry = gse;
  848. break;
  849. }
  850. }
  851. if (graphicalStaffEntry === undefined) {
  852. graphicalStaffEntry = thisPointer.createStaffEntryForTieNote(measure, absoluteTimestamp, openTie);
  853. }
  854. }
  855. if (graphicalStaffEntry !== undefined) {
  856. let octaveShiftValue: OctaveEnum = OctaveEnum.NONE;
  857. if (octaveShiftParams !== undefined) {
  858. if (octaveShiftParams.getAbsoluteStartTimestamp.lte(graphicalStaffEntry.getAbsoluteTimestamp()) &&
  859. graphicalStaffEntry.getAbsoluteTimestamp().lte(octaveShiftParams.getAbsoluteEndTimestamp)) {
  860. octaveShiftValue = octaveShiftParams.getOpenOctaveShift.Type;
  861. }
  862. }
  863. const isLastTieNote: boolean = k === tieTimestamps.length - 1;
  864. const tieFraction: Fraction = openTie.Fractions[k];
  865. // GraphicalNote points to tieStartNote, but must get the correct Length (eg the correct Fraction of tieStartNote's Length)
  866. const tiedGraphicalNote: GraphicalNote = thisPointer.symbolFactory.createNote(openTie.Start, graphicalStaffEntry, activeClef,
  867. octaveShiftValue, tieFraction);
  868. const graphicalNotes: GraphicalNote[] =
  869. graphicalStaffEntry.findOrCreateGraphicalNotesListFromGraphicalNote(tiedGraphicalNote);
  870. graphicalStaffEntry.addGraphicalNoteToListAtCorrectYPosition(graphicalNotes, tiedGraphicalNote);
  871. thisPointer.handleTiedGraphicalNote(tiedGraphicalNote, beams, activeClef, octaveShiftValue, graphicalStaffEntry, tieFraction,
  872. openTie, isLastTieNote);
  873. const tieStartNote: Note = openTie.Start;
  874. if (isLastTieNote && tieStartNote.ParentVoiceEntry.Articulations.length === 1 &&
  875. tieStartNote.ParentVoiceEntry.Articulations[0] === ArticulationEnum.fermata) {
  876. thisPointer.symbolFactory.addFermataAtTiedEndNote(tieStartNote, graphicalStaffEntry);
  877. }
  878. openTie.NoteHasBeenCreated[k] = true;
  879. if (openTie.allGraphicalNotesHaveBeenCreated()) {
  880. removeTie = true;
  881. //tieTimestampListDict.remove(openTie);
  882. }
  883. }
  884. }
  885. }
  886. return removeTie;
  887. });
  888. }
  889. protected resetYPositionForLeadSheet(psi: BoundingBox): void {
  890. if (this.leadSheet) {
  891. psi.RelativePosition = new PointF2D(psi.RelativePosition.x, 0.0);
  892. }
  893. }
  894. protected layoutVoiceEntries(graphicalStaffEntry: GraphicalStaffEntry): void {
  895. graphicalStaffEntry.PositionAndShape.RelativePosition = new PointF2D(0.0, 0.0);
  896. if (!this.leadSheet) {
  897. const isGraceStaffEntry: boolean = graphicalStaffEntry.staffEntryParent !== undefined;
  898. const graphicalStaffEntryNotes: GraphicalNote[][] = graphicalStaffEntry.notes;
  899. for (let idx4: number = 0, len4: number = graphicalStaffEntryNotes.length; idx4 < len4; ++idx4) {
  900. const graphicalNotes: GraphicalNote[] = graphicalStaffEntryNotes[idx4];
  901. if (graphicalNotes.length === 0) {
  902. continue;
  903. }
  904. const voiceEntry: VoiceEntry = graphicalNotes[0].sourceNote.ParentVoiceEntry;
  905. const hasPitchedNote: boolean = graphicalNotes[0].sourceNote.Pitch !== undefined;
  906. this.layoutVoiceEntry(voiceEntry, graphicalNotes, graphicalStaffEntry, hasPitchedNote, isGraceStaffEntry);
  907. }
  908. }
  909. }
  910. protected maxInstrNameLabelLength(): number {
  911. let maxLabelLength: number = 0.0;
  912. for (const instrument of this.graphicalMusicSheet.ParentMusicSheet.Instruments) {
  913. if (instrument.Voices.length > 0 && instrument.Voices[0].Visible) {
  914. const graphicalLabel: GraphicalLabel = new GraphicalLabel(instrument.NameLabel, this.rules.InstrumentLabelTextHeight, TextAlignment.LeftCenter);
  915. graphicalLabel.setLabelPositionAndShapeBorders();
  916. maxLabelLength = Math.max(maxLabelLength, graphicalLabel.PositionAndShape.MarginSize.width);
  917. }
  918. }
  919. return maxLabelLength;
  920. }
  921. protected calculateSheetLabelBoundingBoxes(): void {
  922. const musicSheet: MusicSheet = this.graphicalMusicSheet.ParentMusicSheet;
  923. if (musicSheet.Title !== undefined) {
  924. const title: GraphicalLabel = new GraphicalLabel(musicSheet.Title, this.rules.SheetTitleHeight, TextAlignment.CenterBottom);
  925. this.graphicalMusicSheet.Title = title;
  926. title.setLabelPositionAndShapeBorders();
  927. }
  928. if (musicSheet.Subtitle !== undefined) {
  929. const subtitle: GraphicalLabel = new GraphicalLabel(musicSheet.Subtitle, this.rules.SheetSubtitleHeight, TextAlignment.CenterCenter);
  930. this.graphicalMusicSheet.Subtitle = subtitle;
  931. subtitle.setLabelPositionAndShapeBorders();
  932. }
  933. if (musicSheet.Composer !== undefined) {
  934. const composer: GraphicalLabel = new GraphicalLabel(musicSheet.Composer, this.rules.SheetComposerHeight, TextAlignment.RightCenter);
  935. this.graphicalMusicSheet.Composer = composer;
  936. composer.setLabelPositionAndShapeBorders();
  937. }
  938. if (musicSheet.Lyricist !== undefined) {
  939. const lyricist: GraphicalLabel = new GraphicalLabel(musicSheet.Lyricist, this.rules.SheetAuthorHeight, TextAlignment.LeftCenter);
  940. this.graphicalMusicSheet.Lyricist = lyricist;
  941. lyricist.setLabelPositionAndShapeBorders();
  942. }
  943. }
  944. protected checkMeasuresForWholeRestNotes(): void {
  945. for (let idx: number = 0, len: number = this.graphicalMusicSheet.MusicPages.length; idx < len; ++idx) {
  946. const musicPage: GraphicalMusicPage = this.graphicalMusicSheet.MusicPages[idx];
  947. for (let idx2: number = 0, len2: number = musicPage.MusicSystems.length; idx2 < len2; ++idx2) {
  948. const musicSystem: MusicSystem = musicPage.MusicSystems[idx2];
  949. for (let idx3: number = 0, len3: number = musicSystem.StaffLines.length; idx3 < len3; ++idx3) {
  950. const staffLine: StaffLine = musicSystem.StaffLines[idx3];
  951. for (let idx4: number = 0, len4: number = staffLine.Measures.length; idx4 < len4; ++idx4) {
  952. const measure: StaffMeasure = staffLine.Measures[idx4];
  953. if (measure.staffEntries.length === 1) {
  954. const gse: GraphicalStaffEntry = measure.staffEntries[0];
  955. if (gse.notes.length > 0 && gse.notes[0].length > 0) {
  956. const graphicalNote: GraphicalNote = gse.notes[0][0];
  957. if (graphicalNote.sourceNote.Pitch === undefined && (new Fraction(1, 2)).lt(graphicalNote.sourceNote.Length)) {
  958. this.layoutMeasureWithWholeRest(graphicalNote, gse, measure);
  959. }
  960. }
  961. }
  962. }
  963. }
  964. }
  965. }
  966. }
  967. protected optimizeRestNotePlacement(graphicalStaffEntry: GraphicalStaffEntry, measure: StaffMeasure): void {
  968. if (graphicalStaffEntry.notes.length === 0) {
  969. return;
  970. }
  971. const voice1Notes: GraphicalNote[] = graphicalStaffEntry.notes[0];
  972. if (voice1Notes.length === 0) {
  973. return;
  974. }
  975. const voice1Note1: GraphicalNote = voice1Notes[0];
  976. const voice1Note1IsRest: boolean = voice1Note1.sourceNote.Pitch === undefined;
  977. if (graphicalStaffEntry.notes.length === 2) {
  978. let voice2Note1IsRest: boolean = false;
  979. const voice2Notes: GraphicalNote[] = graphicalStaffEntry.notes[1];
  980. if (voice2Notes.length > 0) {
  981. const voice2Note1: GraphicalNote = voice2Notes[0];
  982. voice2Note1IsRest = voice2Note1.sourceNote.Pitch === undefined;
  983. }
  984. if (voice1Note1IsRest && voice2Note1IsRest) {
  985. this.calculateTwoRestNotesPlacementWithCollisionDetection(graphicalStaffEntry);
  986. } else if (voice1Note1IsRest || voice2Note1IsRest) {
  987. this.calculateRestNotePlacementWithCollisionDetectionFromGraphicalNote(graphicalStaffEntry);
  988. }
  989. } else if (voice1Note1IsRest && graphicalStaffEntry !== measure.staffEntries[0] &&
  990. graphicalStaffEntry !== measure.staffEntries[measure.staffEntries.length - 1]) {
  991. const staffEntryIndex: number = measure.staffEntries.indexOf(graphicalStaffEntry);
  992. const previousStaffEntry: GraphicalStaffEntry = measure.staffEntries[staffEntryIndex - 1];
  993. const nextStaffEntry: GraphicalStaffEntry = measure.staffEntries[staffEntryIndex + 1];
  994. if (previousStaffEntry.notes.length === 1) {
  995. const previousNote: GraphicalNote = previousStaffEntry.notes[0][0];
  996. if (previousNote.sourceNote.NoteBeam !== undefined && nextStaffEntry.notes.length === 1) {
  997. const nextNote: GraphicalNote = nextStaffEntry.notes[0][0];
  998. if (nextNote.sourceNote.NoteBeam !== undefined && previousNote.sourceNote.NoteBeam === nextNote.sourceNote.NoteBeam) {
  999. this.calculateRestNotePlacementWithinGraphicalBeam(
  1000. graphicalStaffEntry, voice1Note1, previousNote,
  1001. nextStaffEntry, nextNote
  1002. );
  1003. graphicalStaffEntry.PositionAndShape.calculateBoundingBox();
  1004. }
  1005. }
  1006. }
  1007. }
  1008. }
  1009. protected getRelativePositionInStaffLineFromTimestamp(timestamp: Fraction, verticalIndex: number, staffLine: StaffLine,
  1010. multiStaffInstrument: boolean, firstVisibleMeasureRelativeX: number = 0.0): PointF2D {
  1011. let relative: PointF2D = new PointF2D();
  1012. let leftStaffEntry: GraphicalStaffEntry = undefined;
  1013. let rightStaffEntry: GraphicalStaffEntry = undefined;
  1014. const numEntries: number = this.graphicalMusicSheet.VerticalGraphicalStaffEntryContainers.length;
  1015. const index: number = this.graphicalMusicSheet.GetInterpolatedIndexInVerticalContainers(timestamp);
  1016. const leftIndex: number = Math.min(Math.floor(index), numEntries - 1);
  1017. const rightIndex: number = Math.min(Math.ceil(index), numEntries - 1);
  1018. if (leftIndex < 0 || verticalIndex < 0) {
  1019. return relative;
  1020. }
  1021. leftStaffEntry = this.getFirstLeftNotNullStaffEntryFromContainer(leftIndex, verticalIndex, multiStaffInstrument);
  1022. rightStaffEntry = this.getFirstRightNotNullStaffEntryFromContainer(rightIndex, verticalIndex, multiStaffInstrument);
  1023. if (leftStaffEntry !== undefined && rightStaffEntry !== undefined) {
  1024. let measureRelativeX: number = leftStaffEntry.parentMeasure.PositionAndShape.RelativePosition.x;
  1025. if (firstVisibleMeasureRelativeX > 0) {
  1026. measureRelativeX = firstVisibleMeasureRelativeX;
  1027. }
  1028. let leftX: number = leftStaffEntry.PositionAndShape.RelativePosition.x + measureRelativeX;
  1029. let rightX: number = rightStaffEntry.PositionAndShape.RelativePosition.x + rightStaffEntry.parentMeasure.PositionAndShape.RelativePosition.x;
  1030. if (firstVisibleMeasureRelativeX > 0) {
  1031. rightX = rightStaffEntry.PositionAndShape.RelativePosition.x + measureRelativeX;
  1032. }
  1033. let timestampQuotient: number = 0.0;
  1034. if (leftStaffEntry !== rightStaffEntry) {
  1035. const leftTimestamp: Fraction = leftStaffEntry.getAbsoluteTimestamp();
  1036. const rightTimestamp: Fraction = rightStaffEntry.getAbsoluteTimestamp();
  1037. const leftDifference: Fraction = Fraction.minus(timestamp, leftTimestamp);
  1038. timestampQuotient = leftDifference.RealValue / Fraction.minus(rightTimestamp, leftTimestamp).RealValue;
  1039. }
  1040. if (leftStaffEntry.parentMeasure.ParentStaffLine !== rightStaffEntry.parentMeasure.ParentStaffLine) {
  1041. if (leftStaffEntry.parentMeasure.ParentStaffLine === staffLine) {
  1042. rightX = staffLine.PositionAndShape.Size.width;
  1043. } else {
  1044. leftX = staffLine.PositionAndShape.RelativePosition.x;
  1045. }
  1046. }
  1047. relative = new PointF2D(leftX + (rightX - leftX) * timestampQuotient, 0.0);
  1048. }
  1049. return relative;
  1050. }
  1051. protected getRelativeXPositionFromTimestamp(timestamp: Fraction): number {
  1052. const numEntries: number = this.graphicalMusicSheet.VerticalGraphicalStaffEntryContainers.length;
  1053. const index: number = this.graphicalMusicSheet.GetInterpolatedIndexInVerticalContainers(timestamp);
  1054. const discreteIndex: number = Math.max(0, Math.min(Math.round(index), numEntries - 1));
  1055. const gse: GraphicalStaffEntry = this.graphicalMusicSheet.VerticalGraphicalStaffEntryContainers[discreteIndex].getFirstNonNullStaffEntry();
  1056. const posX: number = gse.PositionAndShape.RelativePosition.x + gse.parentMeasure.PositionAndShape.RelativePosition.x;
  1057. return posX;
  1058. }
  1059. protected calculatePageLabels(page: GraphicalMusicPage): void {
  1060. // The PositionAndShape child elements of page need to be manually connected to the lyricist, composer, subtitle, etc.
  1061. // because the page are only available now
  1062. let firstSystemAbsoluteTopMargin: number = 10;
  1063. if (page.MusicSystems.length > 0) {
  1064. const firstMusicSystem: MusicSystem = page.MusicSystems[0];
  1065. firstSystemAbsoluteTopMargin = firstMusicSystem.PositionAndShape.RelativePosition.y + firstMusicSystem.PositionAndShape.BorderTop;
  1066. }
  1067. if (this.graphicalMusicSheet.Title !== undefined) {
  1068. const title: GraphicalLabel = this.graphicalMusicSheet.Title;
  1069. title.PositionAndShape.Parent = page.PositionAndShape;
  1070. const relative: PointF2D = new PointF2D();
  1071. relative.x = this.graphicalMusicSheet.ParentMusicSheet.pageWidth / 2;
  1072. relative.y = this.rules.TitleTopDistance + this.rules.SheetTitleHeight;
  1073. title.PositionAndShape.RelativePosition = relative;
  1074. page.Labels.push(title);
  1075. }
  1076. if (this.graphicalMusicSheet.Subtitle !== undefined) {
  1077. const subtitle: GraphicalLabel = this.graphicalMusicSheet.Subtitle;
  1078. subtitle.PositionAndShape.Parent = page.PositionAndShape;
  1079. const relative: PointF2D = new PointF2D();
  1080. relative.x = this.graphicalMusicSheet.ParentMusicSheet.pageWidth / 2;
  1081. relative.y = this.rules.TitleTopDistance + this.rules.SheetTitleHeight + this.rules.SheetMinimumDistanceBetweenTitleAndSubtitle;
  1082. subtitle.PositionAndShape.RelativePosition = relative;
  1083. page.Labels.push(subtitle);
  1084. }
  1085. if (this.graphicalMusicSheet.Composer !== undefined) {
  1086. const composer: GraphicalLabel = this.graphicalMusicSheet.Composer;
  1087. composer.PositionAndShape.Parent = page.PositionAndShape;
  1088. composer.setLabelPositionAndShapeBorders();
  1089. const relative: PointF2D = new PointF2D();
  1090. relative.x = this.graphicalMusicSheet.ParentMusicSheet.pageWidth - this.rules.PageRightMargin;
  1091. relative.y = firstSystemAbsoluteTopMargin - this.rules.SystemComposerDistance;
  1092. composer.PositionAndShape.RelativePosition = relative;
  1093. page.Labels.push(composer);
  1094. }
  1095. if (this.graphicalMusicSheet.Lyricist !== undefined) {
  1096. const lyricist: GraphicalLabel = this.graphicalMusicSheet.Lyricist;
  1097. lyricist.PositionAndShape.Parent = page.PositionAndShape;
  1098. lyricist.setLabelPositionAndShapeBorders();
  1099. const relative: PointF2D = new PointF2D();
  1100. relative.x = this.rules.PageLeftMargin;
  1101. relative.y = firstSystemAbsoluteTopMargin - this.rules.SystemComposerDistance;
  1102. lyricist.PositionAndShape.RelativePosition = relative;
  1103. page.Labels.push(lyricist);
  1104. }
  1105. }
  1106. protected createGraphicalTies(): void {
  1107. for (let measureIndex: number = 0; measureIndex < this.graphicalMusicSheet.ParentMusicSheet.SourceMeasures.length; measureIndex++) {
  1108. const sourceMeasure: SourceMeasure = this.graphicalMusicSheet.ParentMusicSheet.SourceMeasures[measureIndex];
  1109. for (let staffIndex: number = 0; staffIndex < sourceMeasure.CompleteNumberOfStaves; staffIndex++) {
  1110. for (let j: number = 0; j < sourceMeasure.VerticalSourceStaffEntryContainers.length; j++) {
  1111. const sourceStaffEntry: SourceStaffEntry = sourceMeasure.VerticalSourceStaffEntryContainers[j].StaffEntries[staffIndex];
  1112. if (sourceStaffEntry !== undefined) {
  1113. const startStaffEntry: GraphicalStaffEntry = this.graphicalMusicSheet.findGraphicalStaffEntryFromMeasureList(
  1114. staffIndex, measureIndex, sourceStaffEntry
  1115. );
  1116. for (let idx: number = 0, len: number = sourceStaffEntry.VoiceEntries.length; idx < len; ++idx) {
  1117. const voiceEntry: VoiceEntry = sourceStaffEntry.VoiceEntries[idx];
  1118. for (let idx2: number = 0, len2: number = voiceEntry.Notes.length; idx2 < len2; ++idx2) {
  1119. const note: Note = voiceEntry.Notes[idx2];
  1120. if (note.NoteTie !== undefined) {
  1121. const tie: Tie = note.NoteTie;
  1122. this.handleTie(tie, startStaffEntry, staffIndex, measureIndex);
  1123. }
  1124. }
  1125. }
  1126. }
  1127. }
  1128. }
  1129. }
  1130. }
  1131. private handleTie(tie: Tie, startGraphicalStaffEntry: GraphicalStaffEntry, staffIndex: number, measureIndex: number): void {
  1132. let startGse: GraphicalStaffEntry = startGraphicalStaffEntry;
  1133. let endGse: GraphicalStaffEntry = undefined;
  1134. let startNote: GraphicalNote = undefined;
  1135. let endNote: GraphicalNote = undefined;
  1136. for (let i: number = 0; i < tie.Fractions.length; i++) {
  1137. let verticalGraphicalStaffEntryContainer: VerticalGraphicalStaffEntryContainer;
  1138. let endTimestamp: Fraction;
  1139. const startContainerIndex: number = startGraphicalStaffEntry.parentVerticalContainer.Index;
  1140. if (i === 0) {
  1141. endTimestamp = Fraction.plus(startGraphicalStaffEntry.getAbsoluteTimestamp(), tie.Start.calculateNoteLengthWithoutTie());
  1142. } else {
  1143. endTimestamp = Fraction.plus(startGse.getAbsoluteTimestamp(), tie.Fractions[i - 1]);
  1144. }
  1145. verticalGraphicalStaffEntryContainer = this.graphicalMusicSheet.GetVerticalContainerFromTimestamp(endTimestamp, startContainerIndex + 1);
  1146. if (verticalGraphicalStaffEntryContainer !== undefined) {
  1147. endGse = verticalGraphicalStaffEntryContainer.StaffEntries[staffIndex];
  1148. startNote = startGse.findEndTieGraphicalNoteFromNote(tie.Start);
  1149. if (endGse !== undefined) {
  1150. endNote = endGse.findEndTieGraphicalNoteFromNote(tie.Start);
  1151. }
  1152. }
  1153. if (startNote !== undefined && endNote !== undefined && endGse !== undefined) {
  1154. const graphicalTie: GraphicalTie = this.createGraphicalTie(tie, startGse, endGse, startNote, endNote);
  1155. startGse.GraphicalTies.push(graphicalTie);
  1156. if (this.staffEntriesWithGraphicalTies.indexOf(startGse) >= 0) {
  1157. this.staffEntriesWithGraphicalTies.push(startGse);
  1158. }
  1159. }
  1160. if (endGse !== undefined) {
  1161. if (endGse.parentMeasure !== startGse.parentMeasure) {
  1162. measureIndex++;
  1163. }
  1164. startGse = endGse;
  1165. endGse = this.graphicalMusicSheet.findNextGraphicalStaffEntry(staffIndex, measureIndex, startGse);
  1166. }
  1167. }
  1168. }
  1169. private createAccidentalCalculators(): AccidentalCalculator[] {
  1170. const accidentalCalculators: AccidentalCalculator[] = [];
  1171. const firstSourceMeasure: SourceMeasure = this.graphicalMusicSheet.ParentMusicSheet.getFirstSourceMeasure();
  1172. if (firstSourceMeasure !== undefined) {
  1173. for (let i: number = 0; i < firstSourceMeasure.CompleteNumberOfStaves; i++) {
  1174. const accidentalCalculator: AccidentalCalculator = new AccidentalCalculator(this.symbolFactory);
  1175. accidentalCalculators.push(accidentalCalculator);
  1176. if (firstSourceMeasure.FirstInstructionsStaffEntries[i] !== undefined) {
  1177. for (let idx: number = 0, len: number = firstSourceMeasure.FirstInstructionsStaffEntries[i].Instructions.length; idx < len; ++idx) {
  1178. const abstractNotationInstruction: AbstractNotationInstruction = firstSourceMeasure.FirstInstructionsStaffEntries[i].Instructions[idx];
  1179. if (abstractNotationInstruction instanceof KeyInstruction) {
  1180. const keyInstruction: KeyInstruction = <KeyInstruction>abstractNotationInstruction;
  1181. accidentalCalculator.ActiveKeyInstruction = keyInstruction;
  1182. }
  1183. }
  1184. }
  1185. }
  1186. }
  1187. return accidentalCalculators;
  1188. }
  1189. private calculateVerticalContainersList(): void {
  1190. const numberOfEntries: number = this.graphicalMusicSheet.MeasureList[0].length;
  1191. for (let i: number = 0; i < this.graphicalMusicSheet.MeasureList.length; i++) {
  1192. for (let j: number = 0; j < numberOfEntries; j++) {
  1193. const measure: StaffMeasure = this.graphicalMusicSheet.MeasureList[i][j];
  1194. for (let idx: number = 0, len: number = measure.staffEntries.length; idx < len; ++idx) {
  1195. const graphicalStaffEntry: GraphicalStaffEntry = measure.staffEntries[idx];
  1196. const verticalContainer: VerticalGraphicalStaffEntryContainer =
  1197. this.graphicalMusicSheet.getOrCreateVerticalContainer(graphicalStaffEntry.getAbsoluteTimestamp());
  1198. if (verticalContainer !== undefined) {
  1199. verticalContainer.StaffEntries[j] = graphicalStaffEntry;
  1200. graphicalStaffEntry.parentVerticalContainer = verticalContainer;
  1201. }
  1202. }
  1203. }
  1204. }
  1205. }
  1206. private setIndecesToVerticalGraphicalContainers(): void {
  1207. for (let i: number = 0; i < this.graphicalMusicSheet.VerticalGraphicalStaffEntryContainers.length; i++) {
  1208. this.graphicalMusicSheet.VerticalGraphicalStaffEntryContainers[i].Index = i;
  1209. }
  1210. }
  1211. private createGraphicalMeasuresForSourceMeasure(sourceMeasure: SourceMeasure, accidentalCalculators: AccidentalCalculator[],
  1212. openLyricWords: LyricWord[],
  1213. tieTimestampListDictList: Dictionary<Tie, Fraction[]>[],
  1214. openOctaveShifts: OctaveShiftParams[], activeClefs: ClefInstruction[]): StaffMeasure[] {
  1215. this.initStaffMeasuresCreation();
  1216. const verticalMeasureList: StaffMeasure[] = [];
  1217. const openBeams: Beam[] = [];
  1218. const openTuplets: Tuplet[] = [];
  1219. const staffEntryLinks: StaffEntryLink[] = [];
  1220. for (let staffIndex: number = 0; staffIndex < sourceMeasure.CompleteNumberOfStaves; staffIndex++) {
  1221. const measure: StaffMeasure = this.createGraphicalMeasure(
  1222. sourceMeasure, tieTimestampListDictList[staffIndex], openTuplets, openBeams,
  1223. accidentalCalculators[staffIndex], activeClefs, openOctaveShifts, openLyricWords, staffIndex, staffEntryLinks
  1224. );
  1225. this.staffMeasureCreatedCalculations(measure);
  1226. verticalMeasureList.push(measure);
  1227. }
  1228. this.graphicalMusicSheet.sourceToGraphicalMeasureLinks.setValue(sourceMeasure, verticalMeasureList);
  1229. return verticalMeasureList;
  1230. }
  1231. private createGraphicalMeasure(sourceMeasure: SourceMeasure, tieTimestampListDict: Dictionary<Tie, Fraction[]>, openTuplets: Tuplet[], openBeams: Beam[],
  1232. accidentalCalculator: AccidentalCalculator, activeClefs: ClefInstruction[],
  1233. openOctaveShifts: OctaveShiftParams[], openLyricWords: LyricWord[], staffIndex: number,
  1234. staffEntryLinks: StaffEntryLink[]): StaffMeasure {
  1235. const staff: Staff = this.graphicalMusicSheet.ParentMusicSheet.getStaffFromIndex(staffIndex);
  1236. const measure: StaffMeasure = this.symbolFactory.createStaffMeasure(sourceMeasure, staff);
  1237. measure.hasError = sourceMeasure.getErrorInMeasure(staffIndex);
  1238. if (sourceMeasure.FirstInstructionsStaffEntries[staffIndex] !== undefined) {
  1239. for (let idx: number = 0, len: number = sourceMeasure.FirstInstructionsStaffEntries[staffIndex].Instructions.length; idx < len; ++idx) {
  1240. const instruction: AbstractNotationInstruction = sourceMeasure.FirstInstructionsStaffEntries[staffIndex].Instructions[idx];
  1241. if (instruction instanceof KeyInstruction) {
  1242. const key: KeyInstruction = KeyInstruction.copy(instruction);
  1243. if (this.graphicalMusicSheet.ParentMusicSheet.Transpose !== 0 &&
  1244. measure.ParentStaff.ParentInstrument.MidiInstrumentId !== MidiInstrument.Percussion &&
  1245. MusicSheetCalculator.transposeCalculator !== undefined) {
  1246. MusicSheetCalculator.transposeCalculator.transposeKey(
  1247. key, this.graphicalMusicSheet.ParentMusicSheet.Transpose
  1248. );
  1249. }
  1250. accidentalCalculator.ActiveKeyInstruction = key;
  1251. }
  1252. }
  1253. }
  1254. for (let idx: number = 0, len: number = sourceMeasure.StaffLinkedExpressions[staffIndex].length; idx < len; ++idx) {
  1255. const multiExpression: MultiExpression = sourceMeasure.StaffLinkedExpressions[staffIndex][idx];
  1256. if (multiExpression.OctaveShiftStart !== undefined) {
  1257. const openOctaveShift: OctaveShift = multiExpression.OctaveShiftStart;
  1258. openOctaveShifts[staffIndex] = new OctaveShiftParams(
  1259. openOctaveShift, multiExpression.AbsoluteTimestamp,
  1260. openOctaveShift.ParentEndMultiExpression.AbsoluteTimestamp
  1261. );
  1262. }
  1263. }
  1264. for (let entryIndex: number = 0; entryIndex < sourceMeasure.VerticalSourceStaffEntryContainers.length; entryIndex++) {
  1265. const sourceStaffEntry: SourceStaffEntry = sourceMeasure.VerticalSourceStaffEntryContainers[entryIndex].StaffEntries[staffIndex];
  1266. if (sourceStaffEntry !== undefined) {
  1267. for (let idx: number = 0, len: number = sourceStaffEntry.Instructions.length; idx < len; ++idx) {
  1268. const abstractNotationInstruction: AbstractNotationInstruction = sourceStaffEntry.Instructions[idx];
  1269. if (abstractNotationInstruction instanceof ClefInstruction) {
  1270. activeClefs[staffIndex] = <ClefInstruction>abstractNotationInstruction;
  1271. }
  1272. }
  1273. const graphicalStaffEntry: GraphicalStaffEntry = this.symbolFactory.createStaffEntry(sourceStaffEntry, measure);
  1274. if (measure.staffEntries.length > entryIndex) {
  1275. measure.addGraphicalStaffEntryAtTimestamp(graphicalStaffEntry);
  1276. } else {
  1277. measure.addGraphicalStaffEntry(graphicalStaffEntry);
  1278. }
  1279. const linkedNotes: Note[] = [];
  1280. if (sourceStaffEntry.Link !== undefined) {
  1281. sourceStaffEntry.findLinkedNotes(linkedNotes);
  1282. this.handleStaffEntryLink(graphicalStaffEntry, staffEntryLinks);
  1283. }
  1284. let octaveShiftValue: OctaveEnum = OctaveEnum.NONE;
  1285. if (openOctaveShifts[staffIndex] !== undefined) {
  1286. const octaveShiftParams: OctaveShiftParams = openOctaveShifts[staffIndex];
  1287. if (octaveShiftParams.getAbsoluteStartTimestamp.lte(sourceStaffEntry.AbsoluteTimestamp) &&
  1288. sourceStaffEntry.AbsoluteTimestamp.lte(octaveShiftParams.getAbsoluteEndTimestamp)) {
  1289. octaveShiftValue = octaveShiftParams.getOpenOctaveShift.Type;
  1290. }
  1291. }
  1292. for (let idx: number = 0, len: number = sourceStaffEntry.VoiceEntries.length; idx < len; ++idx) {
  1293. const voiceEntry: VoiceEntry = sourceStaffEntry.VoiceEntries[idx];
  1294. this.handleVoiceEntryGraceNotes(
  1295. voiceEntry.graceVoiceEntriesBefore, graphicalStaffEntry.graceStaffEntriesBefore, graphicalStaffEntry,
  1296. accidentalCalculator, activeClefs[staffIndex], octaveShiftValue, openLyricWords,
  1297. tieTimestampListDict, openTuplets, openBeams
  1298. );
  1299. octaveShiftValue = this.handleVoiceEntry(
  1300. voiceEntry, graphicalStaffEntry,
  1301. accidentalCalculator, openLyricWords,
  1302. tieTimestampListDict,
  1303. activeClefs[staffIndex], openTuplets,
  1304. openBeams, octaveShiftValue, false, linkedNotes,
  1305. sourceStaffEntry
  1306. );
  1307. this.handleVoiceEntryGraceNotes(
  1308. voiceEntry.graceVoiceEntriesAfter, graphicalStaffEntry.graceStaffEntriesAfter, graphicalStaffEntry,
  1309. accidentalCalculator, activeClefs[staffIndex], octaveShiftValue, openLyricWords,
  1310. tieTimestampListDict, openTuplets, openBeams
  1311. );
  1312. }
  1313. if (sourceStaffEntry.Instructions.length > 0) {
  1314. const clefInstruction: ClefInstruction = <ClefInstruction>sourceStaffEntry.Instructions[0];
  1315. this.symbolFactory.createInStaffClef(graphicalStaffEntry, clefInstruction);
  1316. }
  1317. if (sourceStaffEntry.ChordContainer !== undefined) {
  1318. sourceStaffEntry.ParentStaff.ParentInstrument.HasChordSymbols = true;
  1319. this.symbolFactory.createChordSymbol(sourceStaffEntry, graphicalStaffEntry, this.graphicalMusicSheet.ParentMusicSheet.Transpose);
  1320. }
  1321. }
  1322. }
  1323. if (tieTimestampListDict.size() > 0) {
  1324. this.handleOpenTies(
  1325. measure, openBeams,
  1326. tieTimestampListDict, activeClefs[staffIndex], openOctaveShifts[staffIndex]
  1327. );
  1328. }
  1329. accidentalCalculator.doCalculationsAtEndOfMeasure();
  1330. if (sourceMeasure.LastInstructionsStaffEntries[staffIndex] !== undefined) {
  1331. const lastStaffEntry: SourceStaffEntry = sourceMeasure.LastInstructionsStaffEntries[staffIndex];
  1332. for (let idx: number = 0, len: number = lastStaffEntry.Instructions.length; idx < len; ++idx) {
  1333. const abstractNotationInstruction: AbstractNotationInstruction = lastStaffEntry.Instructions[idx];
  1334. if (abstractNotationInstruction instanceof ClefInstruction) {
  1335. activeClefs[staffIndex] = <ClefInstruction>abstractNotationInstruction;
  1336. }
  1337. }
  1338. }
  1339. for (let idx: number = 0, len: number = sourceMeasure.StaffLinkedExpressions[staffIndex].length; idx < len; ++idx) {
  1340. const multiExpression: MultiExpression = sourceMeasure.StaffLinkedExpressions[staffIndex][idx];
  1341. if (multiExpression.OctaveShiftEnd !== undefined && openOctaveShifts[staffIndex] !== undefined &&
  1342. multiExpression.OctaveShiftEnd === openOctaveShifts[staffIndex].getOpenOctaveShift) {
  1343. openOctaveShifts[staffIndex] = undefined;
  1344. }
  1345. }
  1346. if (measure.staffEntries.length === 0) {
  1347. const sourceStaffEntry: SourceStaffEntry = new SourceStaffEntry(undefined, staff);
  1348. const note: Note = new Note(undefined, sourceStaffEntry, Fraction.createFromFraction(sourceMeasure.Duration), undefined);
  1349. const graphicalStaffEntry: GraphicalStaffEntry = this.symbolFactory.createStaffEntry(sourceStaffEntry, measure);
  1350. measure.addGraphicalStaffEntry(graphicalStaffEntry);
  1351. graphicalStaffEntry.relInMeasureTimestamp = new Fraction(0, 1);
  1352. const graphicalNotes: GraphicalNote[] = [];
  1353. graphicalStaffEntry.notes.push(graphicalNotes);
  1354. const graphicalNote: GraphicalNote = this.symbolFactory.createNote( note,
  1355. graphicalStaffEntry,
  1356. new ClefInstruction(),
  1357. OctaveEnum.NONE, undefined);
  1358. graphicalNotes.push(graphicalNote);
  1359. }
  1360. return measure;
  1361. }
  1362. private checkNoteForAccidental(graphicalNote: GraphicalNote, accidentalCalculator: AccidentalCalculator, activeClef: ClefInstruction,
  1363. octaveEnum: OctaveEnum, grace: boolean = false): void {
  1364. let pitch: Pitch = graphicalNote.sourceNote.Pitch;
  1365. const transpose: number = this.graphicalMusicSheet.ParentMusicSheet.Transpose;
  1366. if (transpose !== 0 && graphicalNote.sourceNote.ParentStaffEntry.ParentStaff.ParentInstrument.MidiInstrumentId !== MidiInstrument.Percussion) {
  1367. pitch = graphicalNote.Transpose(
  1368. accidentalCalculator.ActiveKeyInstruction, activeClef, transpose, octaveEnum
  1369. );
  1370. if (graphicalNote.sourceNote.NoteTie !== undefined) {
  1371. graphicalNote.sourceNote.NoteTie.BaseNoteYPosition = graphicalNote.PositionAndShape.RelativePosition.y;
  1372. }
  1373. }
  1374. graphicalNote.sourceNote.halfTone = pitch.getHalfTone();
  1375. let scalingFactor: number = 1.0;
  1376. if (grace) {
  1377. scalingFactor = this.rules.GraceNoteScalingFactor;
  1378. }
  1379. accidentalCalculator.checkAccidental(graphicalNote, pitch, grace, scalingFactor);
  1380. }
  1381. // needed to disable linter, as it doesn't recognize the existing usage of this method.
  1382. // ToDo: check if a newer version doesn't have the problem.
  1383. /* tslint:disable:no-unused-variable */
  1384. private createStaffEntryForTieNote(measure: StaffMeasure, absoluteTimestamp: Fraction, openTie: Tie): GraphicalStaffEntry {
  1385. /* tslint:enable:no-unused-variable */
  1386. let graphicalStaffEntry: GraphicalStaffEntry;
  1387. graphicalStaffEntry = this.symbolFactory.createStaffEntry(openTie.Start.ParentStaffEntry, measure);
  1388. graphicalStaffEntry.relInMeasureTimestamp = Fraction.minus(absoluteTimestamp, measure.parentSourceMeasure.AbsoluteTimestamp);
  1389. this.resetYPositionForLeadSheet(graphicalStaffEntry.PositionAndShape);
  1390. measure.addGraphicalStaffEntryAtTimestamp(graphicalStaffEntry);
  1391. return graphicalStaffEntry;
  1392. }
  1393. private updateSkyBottomLines(): void {
  1394. for (let idx: number = 0, len: number = this.graphicalMusicSheet.MusicPages.length; idx < len; ++idx) {
  1395. const graphicalMusicPage: GraphicalMusicPage = this.graphicalMusicSheet.MusicPages[idx];
  1396. for (let idx2: number = 0, len2: number = graphicalMusicPage.MusicSystems.length; idx2 < len2; ++idx2) {
  1397. const musicSystem: MusicSystem = graphicalMusicPage.MusicSystems[idx2];
  1398. for (let idx3: number = 0, len3: number = musicSystem.StaffLines.length; idx3 < len3; ++idx3) {
  1399. const staffLine: StaffLine = musicSystem.StaffLines[idx3];
  1400. this.updateSkyBottomLine(staffLine);
  1401. }
  1402. }
  1403. }
  1404. }
  1405. private handleStaffEntries(): void {
  1406. for (let idx: number = 0, len: number = this.graphicalMusicSheet.MeasureList.length; idx < len; ++idx) {
  1407. const measures: StaffMeasure[] = this.graphicalMusicSheet.MeasureList[idx];
  1408. for (let idx2: number = 0, len2: number = measures.length; idx2 < len2; ++idx2) {
  1409. const measure: StaffMeasure = measures[idx2];
  1410. for (let idx3: number = 0, len3: number = measure.staffEntries.length; idx3 < len3; ++idx3) {
  1411. const graphicalStaffEntry: GraphicalStaffEntry = measure.staffEntries[idx3];
  1412. if (graphicalStaffEntry.parentMeasure !== undefined && graphicalStaffEntry.notes.length > 0 && graphicalStaffEntry.notes[0].length > 0) {
  1413. this.layoutVoiceEntries(graphicalStaffEntry);
  1414. this.layoutStaffEntry(graphicalStaffEntry);
  1415. }
  1416. }
  1417. }
  1418. }
  1419. }
  1420. private calculateSkyBottomLines(): void {
  1421. for (let idx: number = 0, len: number = this.graphicalMusicSheet.MusicPages.length; idx < len; ++idx) {
  1422. const graphicalMusicPage: GraphicalMusicPage = this.graphicalMusicSheet.MusicPages[idx];
  1423. for (let idx2: number = 0, len2: number = graphicalMusicPage.MusicSystems.length; idx2 < len2; ++idx2) {
  1424. const musicSystem: MusicSystem = graphicalMusicPage.MusicSystems[idx2];
  1425. for (let idx3: number = 0, len3: number = musicSystem.StaffLines.length; idx3 < len3; ++idx3) {
  1426. const staffLine: StaffLine = musicSystem.StaffLines[idx3];
  1427. this.calculateSkyBottomLine(staffLine);
  1428. }
  1429. }
  1430. }
  1431. }
  1432. private calculateBeams(): void {
  1433. for (let idx: number = 0, len: number = this.graphicalMusicSheet.MusicPages.length; idx < len; ++idx) {
  1434. const musicPage: GraphicalMusicPage = this.graphicalMusicSheet.MusicPages[idx];
  1435. for (let idx2: number = 0, len2: number = musicPage.MusicSystems.length; idx2 < len2; ++idx2) {
  1436. const musicSystem: MusicSystem = musicPage.MusicSystems[idx2];
  1437. for (let idx3: number = 0, len3: number = musicSystem.StaffLines.length; idx3 < len3; ++idx3) {
  1438. const staffLine: StaffLine = musicSystem.StaffLines[idx3];
  1439. for (let idx4: number = 0, len4: number = staffLine.Measures.length; idx4 < len4; ++idx4) {
  1440. const measure: StaffMeasure = staffLine.Measures[idx4];
  1441. for (let idx5: number = 0, len5: number = measure.staffEntries.length; idx5 < len5; ++idx5) {
  1442. const staffEntry: GraphicalStaffEntry = measure.staffEntries[idx5];
  1443. this.layoutBeams(staffEntry);
  1444. }
  1445. }
  1446. }
  1447. }
  1448. }
  1449. }
  1450. private calculateStaffEntryArticulationMarks(): void {
  1451. for (let idx: number = 0, len: number = this.graphicalMusicSheet.MusicPages.length; idx < len; ++idx) {
  1452. const page: GraphicalMusicPage = this.graphicalMusicSheet.MusicPages[idx];
  1453. for (let idx2: number = 0, len2: number = page.MusicSystems.length; idx2 < len2; ++idx2) {
  1454. const system: MusicSystem = page.MusicSystems[idx2];
  1455. for (let idx3: number = 0, len3: number = system.StaffLines.length; idx3 < len3; ++idx3) {
  1456. const line: StaffLine = system.StaffLines[idx3];
  1457. for (let idx4: number = 0, len4: number = line.Measures.length; idx4 < len4; ++idx4) {
  1458. const measure: StaffMeasure = line.Measures[idx4];
  1459. for (let idx5: number = 0, len5: number = measure.staffEntries.length; idx5 < len5; ++idx5) {
  1460. const graphicalStaffEntry: GraphicalStaffEntry = measure.staffEntries[idx5];
  1461. for (let idx6: number = 0, len6: number = graphicalStaffEntry.sourceStaffEntry.VoiceEntries.length; idx6 < len6; ++idx6) {
  1462. const voiceEntry: VoiceEntry = graphicalStaffEntry.sourceStaffEntry.VoiceEntries[idx6];
  1463. if (voiceEntry.Articulations.length > 0) {
  1464. this.layoutArticulationMarks(voiceEntry.Articulations, voiceEntry, graphicalStaffEntry);
  1465. }
  1466. }
  1467. }
  1468. }
  1469. }
  1470. }
  1471. }
  1472. }
  1473. private calculateOrnaments(): void {
  1474. for (let idx: number = 0, len: number = this.graphicalMusicSheet.MusicPages.length; idx < len; ++idx) {
  1475. const page: GraphicalMusicPage = this.graphicalMusicSheet.MusicPages[idx];
  1476. for (let idx2: number = 0, len2: number = page.MusicSystems.length; idx2 < len2; ++idx2) {
  1477. const system: MusicSystem = page.MusicSystems[idx2];
  1478. for (let idx3: number = 0, len3: number = system.StaffLines.length; idx3 < len3; ++idx3) {
  1479. const line: StaffLine = system.StaffLines[idx3];
  1480. for (let idx4: number = 0, len4: number = line.Measures.length; idx4 < len4; ++idx4) {
  1481. const measure: StaffMeasure = line.Measures[idx4];
  1482. for (let idx5: number = 0, len5: number = measure.staffEntries.length; idx5 < len5; ++idx5) {
  1483. const graphicalStaffEntry: GraphicalStaffEntry = measure.staffEntries[idx5];
  1484. for (let idx6: number = 0, len6: number = graphicalStaffEntry.sourceStaffEntry.VoiceEntries.length; idx6 < len6; ++idx6) {
  1485. const voiceEntry: VoiceEntry = graphicalStaffEntry.sourceStaffEntry.VoiceEntries[idx6];
  1486. if (voiceEntry.OrnamentContainer !== undefined) {
  1487. if (voiceEntry.hasTie() && !graphicalStaffEntry.relInMeasureTimestamp.Equals(voiceEntry.Timestamp)) {
  1488. continue;
  1489. }
  1490. this.layoutOrnament(voiceEntry.OrnamentContainer, voiceEntry, graphicalStaffEntry);
  1491. if (!(this.staffEntriesWithOrnaments.indexOf(graphicalStaffEntry) !== -1)) {
  1492. this.staffEntriesWithOrnaments.push(graphicalStaffEntry);
  1493. }
  1494. }
  1495. }
  1496. }
  1497. }
  1498. }
  1499. }
  1500. }
  1501. }
  1502. private optimizeRestPlacement(): void {
  1503. for (let idx: number = 0, len: number = this.graphicalMusicSheet.MusicPages.length; idx < len; ++idx) {
  1504. const page: GraphicalMusicPage = this.graphicalMusicSheet.MusicPages[idx];
  1505. for (let idx2: number = 0, len2: number = page.MusicSystems.length; idx2 < len2; ++idx2) {
  1506. const system: MusicSystem = page.MusicSystems[idx2];
  1507. for (let idx3: number = 0, len3: number = system.StaffLines.length; idx3 < len3; ++idx3) {
  1508. const line: StaffLine = system.StaffLines[idx3];
  1509. for (let idx4: number = 0, len4: number = line.Measures.length; idx4 < len4; ++idx4) {
  1510. const measure: StaffMeasure = line.Measures[idx4];
  1511. for (let idx5: number = 0, len5: number = measure.staffEntries.length; idx5 < len5; ++idx5) {
  1512. const graphicalStaffEntry: GraphicalStaffEntry = measure.staffEntries[idx5];
  1513. this.optimizeRestNotePlacement(graphicalStaffEntry, measure);
  1514. }
  1515. }
  1516. }
  1517. }
  1518. }
  1519. }
  1520. private calculateTwoRestNotesPlacementWithCollisionDetection(graphicalStaffEntry: GraphicalStaffEntry): void {
  1521. const firstRestNote: GraphicalNote = graphicalStaffEntry.notes[0][0];
  1522. const secondRestNote: GraphicalNote = graphicalStaffEntry.notes[1][0];
  1523. secondRestNote.PositionAndShape.RelativePosition = new PointF2D(0.0, 2.5);
  1524. graphicalStaffEntry.PositionAndShape.calculateAbsolutePositionsRecursiveWithoutTopelement();
  1525. firstRestNote.PositionAndShape.computeNonOverlappingPositionWithMargin(
  1526. graphicalStaffEntry.PositionAndShape, ColDirEnum.Up,
  1527. new PointF2D(0.0, secondRestNote.PositionAndShape.RelativePosition.y)
  1528. );
  1529. const relative: PointF2D = firstRestNote.PositionAndShape.RelativePosition;
  1530. relative.y -= 1.0;
  1531. firstRestNote.PositionAndShape.RelativePosition = relative;
  1532. graphicalStaffEntry.PositionAndShape.calculateBoundingBox();
  1533. }
  1534. private calculateRestNotePlacementWithCollisionDetectionFromGraphicalNote(graphicalStaffEntry: GraphicalStaffEntry): void {
  1535. let restNote: GraphicalNote;
  1536. let graphicalNotes: GraphicalNote[];
  1537. if (graphicalStaffEntry.notes[0][0].sourceNote.Pitch === undefined) {
  1538. restNote = graphicalStaffEntry.notes[0][0];
  1539. graphicalNotes = graphicalStaffEntry.notes[1];
  1540. } else {
  1541. graphicalNotes = graphicalStaffEntry.notes[0];
  1542. restNote = graphicalStaffEntry.notes[1][0];
  1543. }
  1544. let collision: boolean = false;
  1545. graphicalStaffEntry.PositionAndShape.calculateAbsolutePositionsRecursiveWithoutTopelement();
  1546. for (let idx: number = 0, len: number = graphicalNotes.length; idx < len; ++idx) {
  1547. const graphicalNote: GraphicalNote = graphicalNotes[idx];
  1548. if (restNote.PositionAndShape.marginCollisionDetection(graphicalNote.PositionAndShape)) {
  1549. collision = true;
  1550. break;
  1551. }
  1552. }
  1553. if (collision) {
  1554. if (restNote.sourceNote.ParentVoiceEntry.ParentVoice instanceof LinkedVoice) {
  1555. const bottomBorder: number = graphicalNotes[0].PositionAndShape.BorderMarginBottom + graphicalNotes[0].PositionAndShape.RelativePosition.y;
  1556. restNote.PositionAndShape.RelativePosition = new PointF2D(0.0, bottomBorder - restNote.PositionAndShape.BorderMarginTop + 0.5);
  1557. } else {
  1558. const last: GraphicalNote = graphicalNotes[graphicalNotes.length - 1];
  1559. const topBorder: number = last.PositionAndShape.BorderMarginTop + last.PositionAndShape.RelativePosition.y;
  1560. if (graphicalNotes[0].sourceNote.ParentVoiceEntry.ParentVoice instanceof LinkedVoice) {
  1561. restNote.PositionAndShape.RelativePosition = new PointF2D(0.0, topBorder - restNote.PositionAndShape.BorderMarginBottom - 0.5);
  1562. } else {
  1563. const bottomBorder: number = graphicalNotes[0].PositionAndShape.BorderMarginBottom + graphicalNotes[0].PositionAndShape.RelativePosition.y;
  1564. if (bottomBorder < 2.0) {
  1565. restNote.PositionAndShape.RelativePosition = new PointF2D(0.0, bottomBorder - restNote.PositionAndShape.BorderMarginTop + 0.5);
  1566. } else {
  1567. restNote.PositionAndShape.RelativePosition = new PointF2D(0.0, topBorder - restNote.PositionAndShape.BorderMarginBottom - 0.0);
  1568. }
  1569. }
  1570. }
  1571. }
  1572. graphicalStaffEntry.PositionAndShape.calculateBoundingBox();
  1573. }
  1574. private calculateTieCurves(): void {
  1575. for (let idx: number = 0, len: number = this.graphicalMusicSheet.MusicPages.length; idx < len; ++idx) {
  1576. const graphicalMusicPage: GraphicalMusicPage = this.graphicalMusicSheet.MusicPages[idx];
  1577. for (let idx2: number = 0, len2: number = graphicalMusicPage.MusicSystems.length; idx2 < len2; ++idx2) {
  1578. const musicSystem: MusicSystem = graphicalMusicPage.MusicSystems[idx2];
  1579. for (let idx3: number = 0, len3: number = musicSystem.StaffLines.length; idx3 < len3; ++idx3) {
  1580. const staffLine: StaffLine = musicSystem.StaffLines[idx3];
  1581. for (let idx4: number = 0, len5: number = staffLine.Measures.length; idx4 < len5; ++idx4) {
  1582. const measure: StaffMeasure = staffLine.Measures[idx4];
  1583. for (let idx6: number = 0, len6: number = measure.staffEntries.length; idx6 < len6; ++idx6) {
  1584. const staffEntry: GraphicalStaffEntry = measure.staffEntries[idx6];
  1585. const graphicalTies: GraphicalTie[] = staffEntry.GraphicalTies;
  1586. for (let idx7: number = 0, len7: number = graphicalTies.length; idx7 < len7; ++idx7) {
  1587. const graphicalTie: GraphicalTie = graphicalTies[idx7];
  1588. if (graphicalTie.StartNote !== undefined && graphicalTie.StartNote.parentStaffEntry === staffEntry) {
  1589. const tieIsAtSystemBreak: boolean = (
  1590. graphicalTie.StartNote.parentStaffEntry.parentMeasure.ParentStaffLine !==
  1591. graphicalTie.EndNote.parentStaffEntry.parentMeasure.ParentStaffLine
  1592. );
  1593. this.layoutGraphicalTie(graphicalTie, tieIsAtSystemBreak);
  1594. }
  1595. }
  1596. }
  1597. }
  1598. }
  1599. }
  1600. }
  1601. }
  1602. private calculateLyricsPosition(): void {
  1603. const lyricStaffEntriesDict: Dictionary<StaffLine, GraphicalStaffEntry[]> = new Dictionary<StaffLine, GraphicalStaffEntry[]>();
  1604. // sort the lyriceVerseNumbers for every Instrument that has Lyrics
  1605. for (let idx: number = 0, len: number = this.graphicalMusicSheet.ParentMusicSheet.Instruments.length; idx < len; ++idx) {
  1606. const instrument: Instrument = this.graphicalMusicSheet.ParentMusicSheet.Instruments[idx];
  1607. if (instrument.HasLyrics && instrument.LyricVersesNumbers.length > 0) {
  1608. instrument.LyricVersesNumbers.sort();
  1609. }
  1610. }
  1611. // first calc lyrics text positions
  1612. for (let idx: number = 0, len: number = this.graphicalMusicSheet.MusicPages.length; idx < len; ++idx) {
  1613. const graphicalMusicPage: GraphicalMusicPage = this.graphicalMusicSheet.MusicPages[idx];
  1614. for (let idx2: number = 0, len2: number = graphicalMusicPage.MusicSystems.length; idx2 < len2; ++idx2) {
  1615. const musicSystem: MusicSystem = graphicalMusicPage.MusicSystems[idx2];
  1616. for (let idx3: number = 0, len3: number = musicSystem.StaffLines.length; idx3 < len3; ++idx3) {
  1617. const staffLine: StaffLine = musicSystem.StaffLines[idx3];
  1618. const lyricsStaffEntries: GraphicalStaffEntry[] =
  1619. this.calculateSingleStaffLineLyricsPosition(staffLine, staffLine.ParentStaff.ParentInstrument.LyricVersesNumbers);
  1620. lyricStaffEntriesDict.setValue(staffLine, lyricsStaffEntries);
  1621. this.calculateLyricsExtendsAndDashes(lyricStaffEntriesDict.getValue(staffLine));
  1622. }
  1623. }
  1624. }
  1625. // the fill in the lyric word dashes and lyrics extends/underscores
  1626. for (let idx: number = 0, len: number = this.graphicalMusicSheet.MusicPages.length; idx < len; ++idx) {
  1627. const graphicalMusicPage: GraphicalMusicPage = this.graphicalMusicSheet.MusicPages[idx];
  1628. for (let idx2: number = 0, len2: number = graphicalMusicPage.MusicSystems.length; idx2 < len2; ++idx2) {
  1629. const musicSystem: MusicSystem = graphicalMusicPage.MusicSystems[idx2];
  1630. for (let idx3: number = 0, len3: number = musicSystem.StaffLines.length; idx3 < len3; ++idx3) {
  1631. const staffLine: StaffLine = musicSystem.StaffLines[idx3];
  1632. this.calculateLyricsExtendsAndDashes(lyricStaffEntriesDict.getValue(staffLine));
  1633. }
  1634. }
  1635. }
  1636. }
  1637. /**
  1638. * This method calculates the dashes within the syllables of a LyricWord
  1639. * @param lyricEntry
  1640. */
  1641. private calculateSingleLyricWord(lyricEntry: GraphicalLyricEntry): void {
  1642. // const skyBottomLineCalculator: SkyBottomLineCalculator = new SkyBottomLineCalculator (this.rules);
  1643. const graphicalLyricWord: GraphicalLyricWord = lyricEntry.ParentLyricWord;
  1644. const index: number = graphicalLyricWord.GraphicalLyricsEntries.indexOf(lyricEntry);
  1645. let nextLyricEntry: GraphicalLyricEntry = undefined;
  1646. if (index >= 0) {
  1647. nextLyricEntry = graphicalLyricWord.GraphicalLyricsEntries[index + 1];
  1648. }
  1649. if (nextLyricEntry === undefined) {
  1650. return;
  1651. }
  1652. const startStaffLine: StaffLine = <StaffLine>lyricEntry.StaffEntryParent.parentMeasure.ParentStaffLine;
  1653. const nextStaffLine: StaffLine = <StaffLine>nextLyricEntry.StaffEntryParent.parentMeasure.ParentStaffLine;
  1654. const startStaffEntry: GraphicalStaffEntry = lyricEntry.StaffEntryParent;
  1655. const endStaffentry: GraphicalStaffEntry = nextLyricEntry.StaffEntryParent;
  1656. // if on the same StaffLine
  1657. if (lyricEntry.StaffEntryParent.parentMeasure.ParentStaffLine === nextLyricEntry.StaffEntryParent.parentMeasure.ParentStaffLine) {
  1658. // start- and End margins from the text Labels
  1659. const startX: number = startStaffEntry.parentMeasure.PositionAndShape.RelativePosition.x +
  1660. startStaffEntry.PositionAndShape.RelativePosition.x +
  1661. lyricEntry.GraphicalLabel.PositionAndShape.BorderMarginRight;
  1662. const endX: number = endStaffentry.parentMeasure.PositionAndShape.RelativePosition.x +
  1663. endStaffentry.PositionAndShape.RelativePosition.x +
  1664. nextLyricEntry.GraphicalLabel.PositionAndShape.BorderMarginLeft;
  1665. const y: number = lyricEntry.GraphicalLabel.PositionAndShape.RelativePosition.y;
  1666. let numberOfDashes: number = 1;
  1667. if ((endX - startX) > this.rules.BetweenSyllabelMaximumDistance) {
  1668. numberOfDashes = Math.ceil((endX - startX) / this.rules.BetweenSyllabelMaximumDistance);
  1669. }
  1670. // check distance and create the adequate number of Dashes
  1671. if (numberOfDashes === 1) {
  1672. // distance between the two GraphicalLyricEntries is big for only one Dash, position in the middle
  1673. this.calculateSingleDashForLyricWord(startStaffLine, startX, endX, y);
  1674. } else {
  1675. // distance is big enough for more Dashes
  1676. // calculate the adequate number of Dashes from the distance between the two LyricEntries
  1677. // distance between the Dashes should be equal
  1678. this.calculateDashes(startStaffLine, startX, endX, y);
  1679. }
  1680. } else {
  1681. // start and end on different StaffLines
  1682. // start margin from the text Label until the End of StaffLine
  1683. const startX: number = startStaffEntry.parentMeasure.PositionAndShape.RelativePosition.x +
  1684. startStaffEntry.PositionAndShape.RelativePosition.x +
  1685. lyricEntry.GraphicalLabel.PositionAndShape.BorderMarginRight;
  1686. const lastStaffMeasure: StaffMeasure = startStaffLine.Measures[startStaffLine.Measures.length - 1];
  1687. const endX: number = lastStaffMeasure.PositionAndShape.RelativePosition.x + lastStaffMeasure.PositionAndShape.Size.width;
  1688. let y: number = lyricEntry.GraphicalLabel.PositionAndShape.RelativePosition.y;
  1689. // calculate Dashes for the first StaffLine
  1690. this.calculateDashes(startStaffLine, startX, endX, y);
  1691. // calculate Dashes for the second StaffLine (only if endStaffEntry isn't the first StaffEntry of the StaffLine)
  1692. if (!(endStaffentry === endStaffentry.parentMeasure.staffEntries[0] &&
  1693. endStaffentry.parentMeasure === endStaffentry.parentMeasure.ParentStaffLine.Measures[0])) {
  1694. const secondStartX: number = nextStaffLine.Measures[0].staffEntries[0].PositionAndShape.RelativePosition.x;
  1695. const secondEndX: number = endStaffentry.parentMeasure.PositionAndShape.RelativePosition.x +
  1696. endStaffentry.PositionAndShape.RelativePosition.x +
  1697. nextLyricEntry.GraphicalLabel.PositionAndShape.BorderMarginLeft;
  1698. y = nextLyricEntry.GraphicalLabel.PositionAndShape.RelativePosition.y;
  1699. this.calculateDashes(nextStaffLine, secondStartX, secondEndX, y);
  1700. }
  1701. }
  1702. }
  1703. /**
  1704. * This method calculates Dashes for a LyricWord.
  1705. * @param staffLine
  1706. * @param startX
  1707. * @param endX
  1708. * @param y
  1709. */
  1710. private calculateDashes(staffLine: StaffLine, startX: number, endX: number, y: number): void {
  1711. let distance: number = endX - startX;
  1712. if (distance < this.rules.MinimumDistanceBetweenDashes) {
  1713. this.calculateSingleDashForLyricWord(staffLine, startX, endX, y);
  1714. } else {
  1715. // enough distance for more Dashes
  1716. const numberOfDashes: number = Math.floor(distance / this.rules.MinimumDistanceBetweenDashes);
  1717. const distanceBetweenDashes: number = distance / this.rules.MinimumDistanceBetweenDashes;
  1718. let counter: number = 0;
  1719. startX += distanceBetweenDashes / 2;
  1720. endX -= distanceBetweenDashes / 2;
  1721. while (counter <= Math.floor(numberOfDashes / 2.0) && endX > startX) {
  1722. distance = this.calculateRightAndLeftDashesForLyricWord(staffLine, startX, endX, y);
  1723. startX += distanceBetweenDashes;
  1724. endX -= distanceBetweenDashes;
  1725. counter++;
  1726. }
  1727. // if the remaining distance isn't big enough for two Dashes (another check would be if numberOfDashes is uneven),
  1728. // then put the last Dash in the middle of the remaining distance
  1729. if (distance > distanceBetweenDashes) {
  1730. this.calculateSingleDashForLyricWord(staffLine, startX, endX, y);
  1731. }
  1732. }
  1733. }
  1734. /**
  1735. * This method calculates a single Dash for a LyricWord, positioned in the middle of the given distance.
  1736. * @param {StaffLine} staffLine
  1737. * @param {number} startX
  1738. * @param {number} endX
  1739. * @param {number} y
  1740. */
  1741. private calculateSingleDashForLyricWord(staffLine: StaffLine, startX: number, endX: number, y: number): void {
  1742. const dash: GraphicalLabel = new GraphicalLabel(new Label("-"), this.rules.LyricsHeight, TextAlignment.CenterBottom);
  1743. dash.setLabelPositionAndShapeBorders();
  1744. staffLine.LyricsDashes.push(dash);
  1745. if (this.staffLinesWithLyricWords.indexOf(staffLine) === -1) {
  1746. this.staffLinesWithLyricWords.push(staffLine);
  1747. }
  1748. dash.PositionAndShape.Parent = staffLine.PositionAndShape;
  1749. const relative: PointF2D = new PointF2D(startX + (endX - startX) / 2, y);
  1750. dash.PositionAndShape.RelativePosition = relative;
  1751. }
  1752. /**
  1753. * Layouts the underscore line when a lyric entry is marked as extend
  1754. * @param {GraphicalLyricEntry} lyricEntry
  1755. */
  1756. private calculateLyricExtend(lyricEntry: GraphicalLyricEntry): void {
  1757. let startY: number = lyricEntry.GraphicalLabel.PositionAndShape.RelativePosition.y;
  1758. const startStaffEntry: GraphicalStaffEntry = lyricEntry.StaffEntryParent;
  1759. const startStaffLine: StaffLine = <StaffLine>lyricEntry.StaffEntryParent.parentMeasure.ParentStaffLine;
  1760. // find endstaffEntry and staffLine
  1761. let endStaffEntry: GraphicalStaffEntry = undefined;
  1762. let endStaffLine: StaffLine = undefined;
  1763. const staffIndex: number = startStaffEntry.parentMeasure.ParentStaff.idInMusicSheet;
  1764. for (let index: number = startStaffEntry.parentVerticalContainer.Index + 1;
  1765. index < this.graphicalMusicSheet.VerticalGraphicalStaffEntryContainers.length;
  1766. ++index) {
  1767. const gse: GraphicalStaffEntry = this.graphicalMusicSheet.VerticalGraphicalStaffEntryContainers[index].StaffEntries[staffIndex];
  1768. if (gse === undefined) {
  1769. continue;
  1770. }
  1771. if (gse.hasOnlyRests()) {
  1772. break;
  1773. }
  1774. if (gse.LyricsEntries.length > 0) {
  1775. break;
  1776. }
  1777. endStaffEntry = gse;
  1778. endStaffLine = <StaffLine>endStaffEntry.parentMeasure.ParentStaffLine;
  1779. }
  1780. if (endStaffEntry === undefined) {
  1781. return;
  1782. }
  1783. // if on the same StaffLine
  1784. if (startStaffLine === endStaffLine) {
  1785. // start- and End margins from the text Labels
  1786. const startX: number = startStaffEntry.parentMeasure.PositionAndShape.RelativePosition.x +
  1787. startStaffEntry.PositionAndShape.RelativePosition.x +
  1788. lyricEntry.GraphicalLabel.PositionAndShape.BorderMarginRight;
  1789. const endX: number = endStaffEntry.parentMeasure.PositionAndShape.RelativePosition.x +
  1790. endStaffEntry.PositionAndShape.RelativePosition.x +
  1791. endStaffEntry.PositionAndShape.BorderMarginRight;
  1792. // needed in order to line up with the Label's text bottom line (is the y psoition of the underscore)
  1793. startY -= lyricEntry.GraphicalLabel.PositionAndShape.Size.height / 4;
  1794. // create a Line (as underscope after the LyricLabel's End)
  1795. this.calculateSingleLyricWordWithUnderscore(startStaffLine, startX, endX, startY);
  1796. } else { // start and end on different StaffLines
  1797. // start margin from the text Label until the End of StaffLine
  1798. const lastMeasureBb: BoundingBox = startStaffLine.Measures[startStaffLine.Measures.length - 1].PositionAndShape;
  1799. const startX: number = startStaffEntry.parentMeasure.PositionAndShape.RelativePosition.x +
  1800. startStaffEntry.PositionAndShape.RelativePosition.x +
  1801. lyricEntry.GraphicalLabel.PositionAndShape.BorderMarginRight;
  1802. const endX: number = lastMeasureBb.RelativePosition.x +
  1803. lastMeasureBb.Size.width;
  1804. // needed in order to line up with the Label's text bottom line
  1805. startY -= lyricEntry.GraphicalLabel.PositionAndShape.Size.height / 4;
  1806. // first Underscore until the StaffLine's End
  1807. this.calculateSingleLyricWordWithUnderscore(startStaffLine, startX, endX, startY);
  1808. if (endStaffEntry === undefined) {
  1809. return;
  1810. }
  1811. // second Underscore in the endStaffLine until endStaffEntry (if endStaffEntry isn't the first StaffEntry of the StaffLine))
  1812. if (!(endStaffEntry === endStaffEntry.parentMeasure.staffEntries[0] &&
  1813. endStaffEntry.parentMeasure === endStaffEntry.parentMeasure.ParentStaffLine.Measures[0])) {
  1814. const secondStartX: number = endStaffLine.Measures[0].staffEntries[0].PositionAndShape.RelativePosition.x;
  1815. const secondEndX: number = endStaffEntry.parentMeasure.PositionAndShape.RelativePosition.x +
  1816. endStaffEntry.PositionAndShape.RelativePosition.x +
  1817. endStaffEntry.PositionAndShape.BorderMarginRight;
  1818. this.calculateSingleLyricWordWithUnderscore(endStaffLine, secondStartX, secondEndX, startY);
  1819. }
  1820. }
  1821. }
  1822. /**
  1823. * This method calculates a single underscoreLine.
  1824. * @param staffLine
  1825. * @param startX
  1826. * @param end
  1827. * @param y
  1828. */
  1829. private calculateSingleLyricWordWithUnderscore(staffLine: StaffLine, startX: number, endX: number, y: number): void {
  1830. const lineStart: PointF2D = new PointF2D(startX, y);
  1831. const lineEnd: PointF2D = new PointF2D(endX, y);
  1832. const graphicalLine: GraphicalLine = new GraphicalLine(lineStart, lineEnd, this.rules.LyricUnderscoreLineWidth);
  1833. staffLine.LyricLines.push(graphicalLine);
  1834. if (this.staffLinesWithLyricWords.indexOf(staffLine) === -1) {
  1835. this.staffLinesWithLyricWords.push(staffLine);
  1836. }
  1837. }
  1838. /**
  1839. * This method calculates two Dashes for a LyricWord, positioned at the the two ends of the given distance.
  1840. * @param {StaffLine} staffLine
  1841. * @param {number} startX
  1842. * @param {number} endX
  1843. * @param {number} y
  1844. * @returns {number}
  1845. */
  1846. private calculateRightAndLeftDashesForLyricWord (staffLine: StaffLine, startX: number, endX: number, y: number): number {
  1847. const leftDash: GraphicalLabel = new GraphicalLabel (new Label ("-"), this.rules.LyricsHeight, TextAlignment.CenterBottom);
  1848. leftDash.setLabelPositionAndShapeBorders();
  1849. staffLine.LyricsDashes.push(leftDash);
  1850. if (this.staffLinesWithLyricWords.indexOf(staffLine) === -1) {
  1851. this.staffLinesWithLyricWords.push(staffLine);
  1852. }
  1853. leftDash.PositionAndShape.Parent = staffLine.PositionAndShape;
  1854. const leftDashRelative: PointF2D = new PointF2D (startX, y);
  1855. leftDash.PositionAndShape.RelativePosition = leftDashRelative;
  1856. const rightDash: GraphicalLabel = new GraphicalLabel (new Label ("-"), this.rules.LyricsHeight, TextAlignment.CenterBottom);
  1857. rightDash.setLabelPositionAndShapeBorders();
  1858. staffLine.LyricsDashes.push(rightDash);
  1859. rightDash.PositionAndShape.Parent = staffLine.PositionAndShape;
  1860. const rightDashRelative: PointF2D = new PointF2D (endX, y);
  1861. rightDash.PositionAndShape.RelativePosition = rightDashRelative;
  1862. return (rightDash.PositionAndShape.RelativePosition.x - leftDash.PositionAndShape.RelativePosition.x);
  1863. }
  1864. private calculateDynamicExpressions(): void {
  1865. for (let i: number = 0; i < this.graphicalMusicSheet.ParentMusicSheet.SourceMeasures.length; i++) {
  1866. const sourceMeasure: SourceMeasure = this.graphicalMusicSheet.ParentMusicSheet.SourceMeasures[i];
  1867. for (let j: number = 0; j < sourceMeasure.StaffLinkedExpressions.length; j++) {
  1868. if (this.graphicalMusicSheet.MeasureList[i][j].ParentStaff.ParentInstrument.Visible) {
  1869. for (let k: number = 0; k < sourceMeasure.StaffLinkedExpressions[j].length; k++) {
  1870. if (sourceMeasure.StaffLinkedExpressions[j][k].InstantaniousDynamic !== undefined ||
  1871. (sourceMeasure.StaffLinkedExpressions[j][k].StartingContinuousDynamic !== undefined &&
  1872. sourceMeasure.StaffLinkedExpressions[j][k].StartingContinuousDynamic.StartMultiExpression ===
  1873. sourceMeasure.StaffLinkedExpressions[j][k] && sourceMeasure.StaffLinkedExpressions[j][k].UnknownList.length === 0)
  1874. ) {
  1875. this.calculateDynamicExpressionsForSingleMultiExpression(sourceMeasure.StaffLinkedExpressions[j][k], i, j);
  1876. }
  1877. }
  1878. }
  1879. }
  1880. }
  1881. }
  1882. private calculateOctaveShifts(): void {
  1883. for (let i: number = 0; i < this.graphicalMusicSheet.ParentMusicSheet.SourceMeasures.length; i++) {
  1884. const sourceMeasure: SourceMeasure = this.graphicalMusicSheet.ParentMusicSheet.SourceMeasures[i];
  1885. for (let j: number = 0; j < sourceMeasure.StaffLinkedExpressions.length; j++) {
  1886. if (this.graphicalMusicSheet.MeasureList[i][j].ParentStaff.ParentInstrument.Visible) {
  1887. for (let k: number = 0; k < sourceMeasure.StaffLinkedExpressions[j].length; k++) {
  1888. if ((sourceMeasure.StaffLinkedExpressions[j][k].OctaveShiftStart !== undefined)) {
  1889. this.calculateSingleOctaveShift(sourceMeasure, sourceMeasure.StaffLinkedExpressions[j][k], i, j);
  1890. }
  1891. }
  1892. }
  1893. }
  1894. }
  1895. }
  1896. private getFirstLeftNotNullStaffEntryFromContainer(horizontalIndex: number, verticalIndex: number, multiStaffInstrument: boolean): GraphicalStaffEntry {
  1897. if (this.graphicalMusicSheet.VerticalGraphicalStaffEntryContainers[horizontalIndex].StaffEntries[verticalIndex] !== undefined) {
  1898. return this.graphicalMusicSheet.VerticalGraphicalStaffEntryContainers[horizontalIndex].StaffEntries[verticalIndex];
  1899. }
  1900. for (let i: number = horizontalIndex - 1; i >= 0; i--) {
  1901. if (this.graphicalMusicSheet.VerticalGraphicalStaffEntryContainers[i].StaffEntries[verticalIndex] !== undefined) {
  1902. return this.graphicalMusicSheet.VerticalGraphicalStaffEntryContainers[i].StaffEntries[verticalIndex];
  1903. }
  1904. }
  1905. return undefined;
  1906. }
  1907. private getFirstRightNotNullStaffEntryFromContainer(horizontalIndex: number, verticalIndex: number, multiStaffInstrument: boolean): GraphicalStaffEntry {
  1908. if (this.graphicalMusicSheet.VerticalGraphicalStaffEntryContainers[horizontalIndex].StaffEntries[verticalIndex] !== undefined) {
  1909. return this.graphicalMusicSheet.VerticalGraphicalStaffEntryContainers[horizontalIndex].StaffEntries[verticalIndex];
  1910. }
  1911. for (let i: number = horizontalIndex + 1; i < this.graphicalMusicSheet.VerticalGraphicalStaffEntryContainers.length; i++) {
  1912. if (this.graphicalMusicSheet.VerticalGraphicalStaffEntryContainers[i].StaffEntries[verticalIndex] !== undefined) {
  1913. return this.graphicalMusicSheet.VerticalGraphicalStaffEntryContainers[i].StaffEntries[verticalIndex];
  1914. }
  1915. }
  1916. return undefined;
  1917. }
  1918. private calculateWordRepetitionInstructions(): void {
  1919. for (let i: number = 0; i < this.graphicalMusicSheet.ParentMusicSheet.SourceMeasures.length; i++) {
  1920. const sourceMeasure: SourceMeasure = this.graphicalMusicSheet.ParentMusicSheet.SourceMeasures[i];
  1921. for (let idx: number = 0, len: number = sourceMeasure.FirstRepetitionInstructions.length; idx < len; ++idx) {
  1922. const instruction: RepetitionInstruction = sourceMeasure.FirstRepetitionInstructions[idx];
  1923. this.calculateWordRepetitionInstruction(instruction, i);
  1924. }
  1925. for (let idx: number = 0, len: number = sourceMeasure.LastRepetitionInstructions.length; idx < len; ++idx) {
  1926. const instruction: RepetitionInstruction = sourceMeasure.LastRepetitionInstructions[idx];
  1927. this.calculateWordRepetitionInstruction(instruction, i);
  1928. }
  1929. }
  1930. }
  1931. private calculateRepetitionEndings(): void {
  1932. const musicsheet: MusicSheet = this.graphicalMusicSheet.ParentMusicSheet;
  1933. for (let idx: number = 0, len: number = musicsheet.Repetitions.length; idx < len; ++idx) {
  1934. const repetition: Repetition = musicsheet.Repetitions[idx];
  1935. this.calcGraphicalRepetitionEndingsRecursively(repetition);
  1936. }
  1937. }
  1938. private calculateTempoExpressions(): void {
  1939. for (let i: number = 0; i < this.graphicalMusicSheet.ParentMusicSheet.SourceMeasures.length; i++) {
  1940. const sourceMeasure: SourceMeasure = this.graphicalMusicSheet.ParentMusicSheet.SourceMeasures[i];
  1941. for (let j: number = 0; j < sourceMeasure.TempoExpressions.length; j++) {
  1942. this.calculateTempoExpressionsForSingleMultiTempoExpression(sourceMeasure, sourceMeasure.TempoExpressions[j], i);
  1943. }
  1944. }
  1945. }
  1946. private calculateMoodAndUnknownExpressions(): void {
  1947. for (let i: number = 0; i < this.graphicalMusicSheet.ParentMusicSheet.SourceMeasures.length; i++) {
  1948. const sourceMeasure: SourceMeasure = this.graphicalMusicSheet.ParentMusicSheet.SourceMeasures[i];
  1949. for (let j: number = 0; j < sourceMeasure.StaffLinkedExpressions.length; j++) {
  1950. if (this.graphicalMusicSheet.MeasureList[i][j].ParentStaff.ParentInstrument.Visible) {
  1951. for (let k: number = 0; k < sourceMeasure.StaffLinkedExpressions[j].length; k++) {
  1952. if ((sourceMeasure.StaffLinkedExpressions[j][k].MoodList.length > 0) ||
  1953. (sourceMeasure.StaffLinkedExpressions[j][k].UnknownList.length > 0)) {
  1954. this.calculateMoodAndUnknownExpression(sourceMeasure.StaffLinkedExpressions[j][k], i, j);
  1955. }
  1956. }
  1957. }
  1958. }
  1959. }
  1960. }
  1961. /**
  1962. * Calculates the desired stem direction depending on the number (or type) of voices.
  1963. * If more than one voice is there, the main voice (typically the first or upper voice) will get stem up direction.
  1964. * The others get stem down direction.
  1965. * @param voiceEntry the voiceEntry for which the stem direction has to be calculated
  1966. */
  1967. private calculateStemDirectionFromVoices(voiceEntry: VoiceEntry): void {
  1968. // Stem direction calculation:
  1969. const hasLink: boolean = voiceEntry.ParentSourceStaffEntry.Link !== undefined;
  1970. if (hasLink) {
  1971. // in case of StaffEntryLink don't check mainVoice / linkedVoice
  1972. if (voiceEntry === voiceEntry.ParentSourceStaffEntry.VoiceEntries[0]) {
  1973. // set stem up:
  1974. voiceEntry.StemDirection = StemDirectionType.Up;
  1975. return;
  1976. } else {
  1977. // set stem down:
  1978. voiceEntry.StemDirection = StemDirectionType.Down;
  1979. return;
  1980. }
  1981. } else {
  1982. if (voiceEntry.ParentVoice instanceof LinkedVoice) {
  1983. // Linked voice: set stem down:
  1984. voiceEntry.StemDirection = StemDirectionType.Down;
  1985. } else {
  1986. // if this voiceEntry belongs to the mainVoice:
  1987. // check first that there are also more voices present:
  1988. if (voiceEntry.ParentSourceStaffEntry.VoiceEntries.length > 1) {
  1989. // as this voiceEntry belongs to the mainVoice: stem Up
  1990. voiceEntry.StemDirection = StemDirectionType.Up;
  1991. }
  1992. }
  1993. }
  1994. // ToDo: shift code to end of measure to only check once for all beams
  1995. // check for a beam:
  1996. // if this voice entry currently has no desired direction yet:
  1997. if (voiceEntry.StemDirection === StemDirectionType.Undefined &&
  1998. voiceEntry.Notes.length > 0) {
  1999. const beam: Beam = voiceEntry.Notes[0].NoteBeam;
  2000. if (beam !== undefined) {
  2001. // if there is a beam, find any already set stemDirection in the beam:
  2002. for (const note of beam.Notes) {
  2003. if (note.ParentVoiceEntry === voiceEntry) {
  2004. continue;
  2005. } else if (note.ParentVoiceEntry.StemDirection !== StemDirectionType.Undefined) {
  2006. // set the stem direction
  2007. voiceEntry.StemDirection = note.ParentVoiceEntry.StemDirection;
  2008. break;
  2009. }
  2010. }
  2011. }
  2012. }
  2013. }
  2014. }