MusicSystemBuilder.ts 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935
  1. import {StaffMeasure} from "./StaffMeasure";
  2. import {GraphicalMusicPage} from "./GraphicalMusicPage";
  3. import {EngravingRules} from "./EngravingRules";
  4. import {RhythmInstruction} from "../VoiceData/Instructions/RhythmInstruction";
  5. import {KeyInstruction} from "../VoiceData/Instructions/KeyInstruction";
  6. import {ClefInstruction} from "../VoiceData/Instructions/ClefInstruction";
  7. import {SourceMeasure} from "../VoiceData/SourceMeasure";
  8. import {MusicSystem} from "./MusicSystem";
  9. import {BoundingBox} from "./BoundingBox";
  10. import {Staff} from "../VoiceData/Staff";
  11. import {Instrument} from "../Instrument";
  12. import {PointF2D} from "../../Common/DataObjects/PointF2D";
  13. import {StaffLine} from "./StaffLine";
  14. import {GraphicalLine} from "./GraphicalLine";
  15. import {SourceStaffEntry} from "../VoiceData/SourceStaffEntry";
  16. import {AbstractNotationInstruction} from "../VoiceData/Instructions/AbstractNotationInstruction";
  17. import {SystemLinesEnum} from "./SystemLinesEnum";
  18. import {GraphicalMusicSheet} from "./GraphicalMusicSheet";
  19. import {IGraphicalSymbolFactory} from "../Interfaces/IGraphicalSymbolFactory";
  20. import {MusicSheetCalculator} from "./MusicSheetCalculator";
  21. import {MidiInstrument} from "../VoiceData/Instructions/ClefInstruction";
  22. import {CollectionUtil} from "../../Util/CollectionUtil";
  23. import {SystemLinePosition} from "./SystemLinePosition";
  24. export class MusicSystemBuilder {
  25. private measureList: StaffMeasure[][];
  26. private graphicalMusicSheet: GraphicalMusicSheet;
  27. private currentMusicPage: GraphicalMusicPage;
  28. private currentPageHeight: number;
  29. private currentSystemParams: SystemBuildParameters;
  30. private numberOfVisibleStaffLines: number;
  31. private rules: EngravingRules;
  32. private measureListIndex: number;
  33. /**
  34. * Does the mapping from the currently visible staves to the global staff-list of the music sheet.
  35. */
  36. private visibleStaffIndices: number[];
  37. private activeRhythm: RhythmInstruction[];
  38. private activeKeys: KeyInstruction[];
  39. private activeClefs: ClefInstruction[];
  40. private globalSystemIndex: number = 0;
  41. private leadSheet: boolean = false;
  42. private symbolFactory: IGraphicalSymbolFactory;
  43. public initialize(
  44. graphicalMusicSheet: GraphicalMusicSheet, measureList: StaffMeasure[][], numberOfStaffLines: number, symbolFactory: IGraphicalSymbolFactory
  45. ): void {
  46. this.leadSheet = graphicalMusicSheet.LeadSheet;
  47. this.graphicalMusicSheet = graphicalMusicSheet;
  48. this.rules = this.graphicalMusicSheet.ParentMusicSheet.rules;
  49. this.measureList = measureList;
  50. this.symbolFactory = symbolFactory;
  51. this.currentMusicPage = this.createMusicPage();
  52. this.currentPageHeight = 0.0;
  53. this.numberOfVisibleStaffLines = numberOfStaffLines;
  54. this.activeRhythm = new Array(this.numberOfVisibleStaffLines);
  55. this.activeKeys = new Array(this.numberOfVisibleStaffLines);
  56. this.activeClefs = new Array(this.numberOfVisibleStaffLines);
  57. this.initializeActiveInstructions(this.measureList[0]);
  58. }
  59. public buildMusicSystems(): void {
  60. let previousMeasureEndsSystem: boolean = false;
  61. const systemMaxWidth: number = this.getFullPageSystemWidth();
  62. this.measureListIndex = 0;
  63. this.currentSystemParams = new SystemBuildParameters();
  64. // the first System - create also its Labels
  65. this.currentSystemParams.currentSystem = this.initMusicSystem();
  66. this.layoutSystemStaves();
  67. this.currentSystemParams.currentSystem.createMusicSystemLabel(
  68. this.rules.InstrumentLabelTextHeight,
  69. this.rules.SystemLabelsRightMargin,
  70. this.rules.LabelMarginBorderFactor
  71. );
  72. this.currentPageHeight += this.currentSystemParams.currentSystem.PositionAndShape.RelativePosition.y;
  73. let numberOfMeasures: number = 0;
  74. for (let idx: number = 0, len: number = this.measureList.length; idx < len; ++idx) {
  75. if (this.measureList[idx].length > 0) {
  76. numberOfMeasures++;
  77. }
  78. }
  79. // go through measures and add to system until system gets too long -> finish system and start next system.
  80. while (this.measureListIndex < numberOfMeasures) {
  81. const staffMeasures: StaffMeasure[] = this.measureList[this.measureListIndex];
  82. for (let idx: number = 0, len: number = staffMeasures.length; idx < len; ++idx) {
  83. staffMeasures[idx].resetLayout();
  84. }
  85. const sourceMeasure: SourceMeasure = staffMeasures[0].parentSourceMeasure;
  86. const sourceMeasureEndsSystem: boolean = sourceMeasure.BreakSystemAfter;
  87. const isSystemStartMeasure: boolean = this.currentSystemParams.IsSystemStartMeasure();
  88. const isFirstSourceMeasure: boolean = sourceMeasure === this.graphicalMusicSheet.ParentMusicSheet.getFirstSourceMeasure();
  89. let currentMeasureBeginInstructionsWidth: number = this.rules.MeasureLeftMargin;
  90. let currentMeasureEndInstructionsWidth: number = 0;
  91. // calculate the current Measure Width:
  92. // The width of a measure is build up from
  93. // 1. the begin instructions (clef, Key, Rhythm),
  94. // 2. the staff entries (= notes) and
  95. // 3. the end instructions (actually only clefs)
  96. const measureStartLine: SystemLinesEnum = this.getMeasureStartLine();
  97. currentMeasureBeginInstructionsWidth += this.getLineWidth(staffMeasures[0], measureStartLine, isSystemStartMeasure);
  98. if (!this.leadSheet) {
  99. currentMeasureBeginInstructionsWidth += this.addBeginInstructions(staffMeasures, isSystemStartMeasure, isFirstSourceMeasure);
  100. currentMeasureEndInstructionsWidth += this.addEndInstructions(staffMeasures);
  101. }
  102. let currentMeasureVarWidth: number = 0;
  103. for (let i: number = 0; i < this.numberOfVisibleStaffLines; i++) {
  104. currentMeasureVarWidth = Math.max(currentMeasureVarWidth, staffMeasures[i].minimumStaffEntriesWidth);
  105. }
  106. // take into account the LineWidth after each Measure
  107. const measureEndLine: SystemLinesEnum = this.getMeasureEndLine();
  108. currentMeasureEndInstructionsWidth += this.getLineWidth(staffMeasures[0], measureEndLine, isSystemStartMeasure);
  109. let nextMeasureBeginInstructionWidth: number = this.rules.MeasureLeftMargin;
  110. // Check if there are key or rhythm change instructions within the next measure:
  111. if (this.measureListIndex + 1 < this.measureList.length) {
  112. const nextStaffMeasures: StaffMeasure[] = this.measureList[this.measureListIndex + 1];
  113. const nextSourceMeasure: SourceMeasure = nextStaffMeasures[0].parentSourceMeasure;
  114. if (nextSourceMeasure.hasBeginInstructions()) {
  115. nextMeasureBeginInstructionWidth += this.addBeginInstructions(nextStaffMeasures, false, false);
  116. }
  117. }
  118. const totalMeasureWidth: number = currentMeasureBeginInstructionsWidth + currentMeasureEndInstructionsWidth + currentMeasureVarWidth;
  119. const measureFitsInSystem: boolean = this.currentSystemParams.currentWidth + totalMeasureWidth + nextMeasureBeginInstructionWidth < systemMaxWidth;
  120. if (isSystemStartMeasure || measureFitsInSystem) {
  121. this.addMeasureToSystem(
  122. staffMeasures, measureStartLine, measureEndLine, totalMeasureWidth,
  123. currentMeasureBeginInstructionsWidth, currentMeasureVarWidth, currentMeasureEndInstructionsWidth
  124. );
  125. this.updateActiveClefs(sourceMeasure, staffMeasures);
  126. this.measureListIndex++;
  127. } else {
  128. // finalize current system and prepare a new one
  129. this.finalizeCurrentAndCreateNewSystem(staffMeasures, previousMeasureEndsSystem);
  130. // don't increase measure index to check this measure now again
  131. }
  132. previousMeasureEndsSystem = sourceMeasureEndsSystem;
  133. }
  134. this.finalizeCurrentAndCreateNewSystem(this.measureList[this.measureList.length - 1], true);
  135. }
  136. /**
  137. * Set the Width of the staff-Measures of one source measure.
  138. * @param staffMeasures
  139. * @param width
  140. * @param beginInstrWidth
  141. * @param endInstrWidth
  142. */
  143. private setMeasureWidth(staffMeasures: StaffMeasure[], width: number, beginInstrWidth: number, endInstrWidth: number): void {
  144. for (let idx: number = 0, len: number = staffMeasures.length; idx < len; ++idx) {
  145. const measure: StaffMeasure = staffMeasures[idx];
  146. measure.setWidth(width);
  147. if (beginInstrWidth > 0) {
  148. measure.beginInstructionsWidth = beginInstrWidth;
  149. }
  150. if (endInstrWidth > 0) {
  151. measure.endInstructionsWidth = endInstrWidth;
  152. }
  153. }
  154. }
  155. /**
  156. * When the actual source measure doesn't fit any more, this method finalizes the current system and
  157. * opens up a new empty system, where the actual measure will be added in the next iteration.
  158. * @param measures
  159. * @param isPartEndingSystem
  160. */
  161. private finalizeCurrentAndCreateNewSystem(measures: StaffMeasure[], isPartEndingSystem: boolean = false): void {
  162. this.adaptRepetitionLineWithIfNeeded();
  163. if (!isPartEndingSystem) {
  164. this.checkAndCreateExtraInstructionMeasure(measures);
  165. }
  166. this.stretchMusicSystem(isPartEndingSystem);
  167. if (this.currentPageHeight + this.currentSystemParams.currentSystem.PositionAndShape.Size.height + this.rules.SystemDistance <= this.rules.PageHeight) {
  168. this.currentPageHeight += this.currentSystemParams.currentSystem.PositionAndShape.Size.height + this.rules.SystemDistance;
  169. if (
  170. this.currentPageHeight + this.currentSystemParams.currentSystem.PositionAndShape.Size.height
  171. + this.rules.SystemDistance >= this.rules.PageHeight
  172. ) {
  173. this.currentMusicPage = this.createMusicPage();
  174. this.currentPageHeight = this.rules.PageTopMargin + this.rules.TitleTopDistance;
  175. }
  176. } else {
  177. this.currentMusicPage = this.createMusicPage();
  178. this.currentPageHeight = this.rules.PageTopMargin + this.rules.TitleTopDistance;
  179. }
  180. this.currentSystemParams = new SystemBuildParameters();
  181. if (this.measureListIndex < this.measureList.length) {
  182. this.currentSystemParams.currentSystem = this.initMusicSystem();
  183. this.layoutSystemStaves();
  184. }
  185. }
  186. /**
  187. * If a line repetition is ending and a new line repetition is starting at the end of the system,
  188. * the double repetition line has to be split into two: one at the currently ending system and
  189. * one at the next system.
  190. * (this should be refactored at some point to not use a combined end/start line but always separated lines)
  191. */
  192. private adaptRepetitionLineWithIfNeeded(): void {
  193. const systemMeasures: MeasureBuildParameters[] = this.currentSystemParams.systemMeasures;
  194. if (systemMeasures.length >= 1) {
  195. const measures: StaffMeasure[] =
  196. this.currentSystemParams.currentSystem.GraphicalMeasures[this.currentSystemParams.currentSystem.GraphicalMeasures.length - 1];
  197. const measureParams: MeasureBuildParameters = systemMeasures[systemMeasures.length - 1];
  198. let diff: number = 0.0;
  199. if (measureParams.endLine === SystemLinesEnum.DotsBoldBoldDots) {
  200. measureParams.endLine = SystemLinesEnum.DotsThinBold;
  201. diff = measures[0].getLineWidth(SystemLinesEnum.DotsBoldBoldDots) / 2 - measures[0].getLineWidth(SystemLinesEnum.DotsThinBold);
  202. }
  203. this.currentSystemParams.currentSystemFixWidth -= diff;
  204. for (let idx: number = 0, len: number = measures.length; idx < len; ++idx) {
  205. const measure: StaffMeasure = measures[idx];
  206. measure.endInstructionsWidth -= diff;
  207. }
  208. }
  209. }
  210. private addMeasureToSystem(
  211. staffMeasures: StaffMeasure[], measureStartLine: SystemLinesEnum, measureEndLine: SystemLinesEnum,
  212. totalMeasureWidth: number, currentMeasureBeginInstructionsWidth: number, currentVarWidth: number, currentMeasureEndInstructionsWidth: number
  213. ): void {
  214. this.currentSystemParams.systemMeasures.push({beginLine: measureStartLine, endLine: measureEndLine});
  215. this.setMeasureWidth(
  216. staffMeasures, totalMeasureWidth, currentMeasureBeginInstructionsWidth, currentMeasureEndInstructionsWidth
  217. );
  218. this.addStaveMeasuresToSystem(staffMeasures);
  219. this.currentSystemParams.currentWidth += totalMeasureWidth;
  220. this.currentSystemParams.currentSystemFixWidth += currentMeasureBeginInstructionsWidth + currentMeasureEndInstructionsWidth;
  221. this.currentSystemParams.currentSystemVarWidth += currentVarWidth;
  222. this.currentSystemParams.systemMeasureIndex++;
  223. }
  224. /**
  225. * Create a new [[GraphicalMusicPage]]
  226. * (for now only one long page is used per music sheet, as we scroll down and have no page flips)
  227. * @returns {GraphicalMusicPage}
  228. */
  229. private createMusicPage(): GraphicalMusicPage {
  230. const page: GraphicalMusicPage = new GraphicalMusicPage(this.graphicalMusicSheet);
  231. this.graphicalMusicSheet.MusicPages.push(page);
  232. page.PositionAndShape.BorderLeft = 0.0;
  233. page.PositionAndShape.BorderRight = this.graphicalMusicSheet.ParentMusicSheet.pageWidth;
  234. page.PositionAndShape.BorderTop = 0.0;
  235. page.PositionAndShape.BorderBottom = this.rules.PageHeight;
  236. page.PositionAndShape.RelativePosition = new PointF2D(0.0, 0.0);
  237. return page;
  238. }
  239. /**
  240. * Initialize a new [[MusicSystem]].
  241. * @returns {MusicSystem}
  242. */
  243. private initMusicSystem(): MusicSystem {
  244. const musicSystem: MusicSystem = this.symbolFactory.createMusicSystem(this.currentMusicPage, this.globalSystemIndex++);
  245. this.currentMusicPage.MusicSystems.push(musicSystem);
  246. const boundingBox: BoundingBox = musicSystem.PositionAndShape;
  247. this.currentMusicPage.PositionAndShape.ChildElements.push(boundingBox);
  248. return musicSystem;
  249. }
  250. /**
  251. * Get the width the system should have for a given page width.
  252. * @returns {number}
  253. */
  254. private getFullPageSystemWidth(): number {
  255. return this.currentMusicPage.PositionAndShape.Size.width - this.rules.PageLeftMargin
  256. - this.rules.PageRightMargin - this.rules.SystemLeftMargin - this.rules.SystemRightMargin;
  257. }
  258. private layoutSystemStaves(): void {
  259. const systemWidth: number = this.getFullPageSystemWidth();
  260. const musicSystem: MusicSystem = this.currentSystemParams.currentSystem;
  261. const boundingBox: BoundingBox = musicSystem.PositionAndShape;
  262. boundingBox.BorderLeft = 0.0;
  263. boundingBox.BorderRight = systemWidth;
  264. boundingBox.BorderTop = 0.0;
  265. const staffList: Staff[] = [];
  266. const instruments: Instrument[] = this.graphicalMusicSheet.ParentMusicSheet.Instruments;
  267. for (let idx: number = 0, len: number = instruments.length; idx < len; ++idx) {
  268. const instrument: Instrument = instruments[idx];
  269. if (instrument.Voices.length === 0 || !instrument.Visible) {
  270. continue;
  271. }
  272. for (let idx2: number = 0, len2: number = instrument.Staves.length; idx2 < len2; ++idx2) {
  273. const staff: Staff = instrument.Staves[idx2];
  274. staffList.push(staff);
  275. }
  276. }
  277. let multiLyrics: boolean = false;
  278. if (this.leadSheet) {
  279. for (let idx: number = 0, len: number = staffList.length; idx < len; ++idx) {
  280. const staff: Staff = staffList[idx];
  281. if (staff.ParentInstrument.LyricVersesNumbers.length > 1) {
  282. multiLyrics = true;
  283. break;
  284. }
  285. }
  286. }
  287. let yOffsetSum: number = 0;
  288. for (let i: number = 0; i < staffList.length; i++) {
  289. this.addStaffLineToMusicSystem(musicSystem, yOffsetSum, staffList[i]);
  290. yOffsetSum += this.rules.StaffHeight;
  291. if (i + 1 < staffList.length) {
  292. let yOffset: number = 0;
  293. if (this.leadSheet && !multiLyrics) {
  294. yOffset = 2.5;
  295. } else {
  296. if (staffList[i].ParentInstrument === staffList[i + 1].ParentInstrument) {
  297. yOffset = this.rules.BetweenStaffDistance;
  298. } else {
  299. yOffset = this.rules.StaffDistance;
  300. }
  301. }
  302. yOffsetSum += yOffset;
  303. }
  304. }
  305. boundingBox.BorderBottom = yOffsetSum;
  306. }
  307. /**
  308. * Calculate the [[StaffLine]](s) needed for a [[MusicSystem]].
  309. * @param musicSystem
  310. * @param relativeYPosition
  311. * @param staff
  312. */
  313. private addStaffLineToMusicSystem(musicSystem: MusicSystem, relativeYPosition: number, staff: Staff): void {
  314. if (musicSystem !== undefined) {
  315. const staffLine: StaffLine = this.symbolFactory.createStaffLine(musicSystem, staff);
  316. musicSystem.StaffLines.push(staffLine);
  317. const boundingBox: BoundingBox = staffLine.PositionAndShape;
  318. musicSystem.PositionAndShape.ChildElements.push(boundingBox);
  319. const relativePosition: PointF2D = new PointF2D();
  320. if (musicSystem.Parent.MusicSystems[0] === musicSystem && musicSystem.Parent === musicSystem.Parent.Parent.MusicPages[0]) {
  321. relativePosition.x = this.rules.FirstSystemMargin;
  322. } else {
  323. relativePosition.x = 0.0;
  324. }
  325. relativePosition.y = relativeYPosition;
  326. boundingBox.RelativePosition = relativePosition;
  327. if (musicSystem.Parent.MusicSystems[0] === musicSystem && musicSystem.Parent === musicSystem.Parent.Parent.MusicPages[0]) {
  328. boundingBox.BorderRight = musicSystem.PositionAndShape.Size.width - this.rules.FirstSystemMargin;
  329. } else {
  330. boundingBox.BorderRight = musicSystem.PositionAndShape.Size.width;
  331. }
  332. boundingBox.BorderLeft = 0.0;
  333. boundingBox.BorderTop = 0.0;
  334. boundingBox.BorderBottom = this.rules.StaffHeight;
  335. for (let i: number = 0; i < 5; i++) {
  336. const start: PointF2D = new PointF2D();
  337. start.x = 0.0;
  338. start.y = i * this.rules.StaffHeight / 4;
  339. const end: PointF2D = new PointF2D();
  340. end.x = staffLine.PositionAndShape.Size.width;
  341. end.y = i * this.rules.StaffHeight / 4;
  342. if (this.leadSheet) {
  343. start.y = end.y = 0;
  344. }
  345. staffLine.StaffLines[i] = new GraphicalLine(start, end, this.rules.StaffLineWidth);
  346. }
  347. }
  348. }
  349. /**
  350. * Initialize the active Instructions from the first [[SourceMeasure]] of first [[SourceMusicPart]].
  351. * @param measureList
  352. */
  353. private initializeActiveInstructions(measureList: StaffMeasure[]): void {
  354. const firstSourceMeasure: SourceMeasure = this.graphicalMusicSheet.ParentMusicSheet.getFirstSourceMeasure();
  355. if (firstSourceMeasure !== undefined) {
  356. this.visibleStaffIndices = this.graphicalMusicSheet.getVisibleStavesIndecesFromSourceMeasure(measureList);
  357. for (let i: number = 0, len: number = this.visibleStaffIndices.length; i < len; i++) {
  358. const staffIndex: number = this.visibleStaffIndices[i];
  359. const graphicalMeasure: StaffMeasure = this.graphicalMusicSheet.getGraphicalMeasureFromSourceMeasureAndIndex(firstSourceMeasure, staffIndex);
  360. this.activeClefs[i] = <ClefInstruction>firstSourceMeasure.FirstInstructionsStaffEntries[staffIndex].Instructions[0];
  361. let keyInstruction: KeyInstruction = KeyInstruction.copy(
  362. <KeyInstruction>firstSourceMeasure.FirstInstructionsStaffEntries[staffIndex].Instructions[1]);
  363. keyInstruction = this.transposeKeyInstruction(keyInstruction, graphicalMeasure);
  364. this.activeKeys[i] = keyInstruction;
  365. this.activeRhythm[i] = <RhythmInstruction>firstSourceMeasure.FirstInstructionsStaffEntries[staffIndex].Instructions[2];
  366. }
  367. }
  368. }
  369. private transposeKeyInstruction(keyInstruction: KeyInstruction, graphicalMeasure: StaffMeasure): KeyInstruction {
  370. if (this.graphicalMusicSheet.ParentMusicSheet.Transpose !== 0
  371. && graphicalMeasure.ParentStaff.ParentInstrument.MidiInstrumentId !== MidiInstrument.Percussion
  372. && MusicSheetCalculator.transposeCalculator !== undefined
  373. ) {
  374. MusicSheetCalculator.transposeCalculator.transposeKey(
  375. keyInstruction,
  376. this.graphicalMusicSheet.ParentMusicSheet.Transpose
  377. );
  378. }
  379. return keyInstruction;
  380. }
  381. /**
  382. * Calculate the width needed for Instructions (Key, Clef, Rhythm, Repetition) for the measure.
  383. * @param measures
  384. * @param isSystemFirstMeasure
  385. * @param isFirstSourceMeasure
  386. * @returns {number}
  387. */
  388. private addBeginInstructions(measures: StaffMeasure[], isSystemFirstMeasure: boolean, isFirstSourceMeasure: boolean): number {
  389. const measureCount: number = measures.length;
  390. if (measureCount === 0) {
  391. return 0;
  392. }
  393. let totalBeginInstructionLengthX: number = 0.0;
  394. const sourceMeasure: SourceMeasure = measures[0].parentSourceMeasure;
  395. for (let idx: number = 0; idx < measureCount; ++idx) {
  396. const measure: StaffMeasure = measures[idx];
  397. const staffIndex: number = this.visibleStaffIndices[idx];
  398. const beginInstructionsStaffEntry: SourceStaffEntry = sourceMeasure.FirstInstructionsStaffEntries[staffIndex];
  399. const beginInstructionLengthX: number = this.AddInstructionsAtMeasureBegin(
  400. beginInstructionsStaffEntry, measure,
  401. idx, isFirstSourceMeasure,
  402. isSystemFirstMeasure
  403. );
  404. totalBeginInstructionLengthX = Math.max(totalBeginInstructionLengthX, beginInstructionLengthX);
  405. }
  406. return totalBeginInstructionLengthX;
  407. }
  408. /**
  409. * Calculates the width needed for Instructions (Clef, Repetition) for the measure.
  410. * @param measures
  411. * @returns {number}
  412. */
  413. private addEndInstructions(measures: StaffMeasure[]): number {
  414. const measureCount: number = measures.length;
  415. if (measureCount === 0) {
  416. return 0;
  417. }
  418. let totalEndInstructionLengthX: number = 0.5;
  419. const sourceMeasure: SourceMeasure = measures[0].parentSourceMeasure;
  420. for (let idx: number = 0; idx < measureCount; idx++) {
  421. const measure: StaffMeasure = measures[idx];
  422. const staffIndex: number = this.visibleStaffIndices[idx];
  423. const endInstructionsStaffEntry: SourceStaffEntry = sourceMeasure.LastInstructionsStaffEntries[staffIndex];
  424. const endInstructionLengthX: number = this.addInstructionsAtMeasureEnd(endInstructionsStaffEntry, measure);
  425. totalEndInstructionLengthX = Math.max(totalEndInstructionLengthX, endInstructionLengthX);
  426. }
  427. return totalEndInstructionLengthX;
  428. }
  429. private AddInstructionsAtMeasureBegin(firstEntry: SourceStaffEntry, measure: StaffMeasure,
  430. visibleStaffIdx: number, isFirstSourceMeasure: boolean, isSystemStartMeasure: boolean): number {
  431. let instructionsLengthX: number = 0;
  432. let currentClef: ClefInstruction = undefined;
  433. let currentKey: KeyInstruction = undefined;
  434. let currentRhythm: RhythmInstruction = undefined;
  435. if (firstEntry !== undefined) {
  436. for (let idx: number = 0, len: number = firstEntry.Instructions.length; idx < len; ++idx) {
  437. const abstractNotationInstruction: AbstractNotationInstruction = firstEntry.Instructions[idx];
  438. if (abstractNotationInstruction instanceof ClefInstruction) {
  439. currentClef = <ClefInstruction>abstractNotationInstruction;
  440. } else if (abstractNotationInstruction instanceof KeyInstruction) {
  441. currentKey = <KeyInstruction>abstractNotationInstruction;
  442. } else if (abstractNotationInstruction instanceof RhythmInstruction) {
  443. currentRhythm = <RhythmInstruction>abstractNotationInstruction;
  444. }
  445. }
  446. }
  447. if (isSystemStartMeasure) {
  448. if (currentClef === undefined) {
  449. currentClef = this.activeClefs[visibleStaffIdx];
  450. }
  451. if (currentKey === undefined) {
  452. currentKey = this.activeKeys[visibleStaffIdx];
  453. }
  454. if (isFirstSourceMeasure && currentRhythm === undefined) {
  455. currentRhythm = this.activeRhythm[visibleStaffIdx];
  456. }
  457. }
  458. let clefAdded: boolean = false;
  459. let keyAdded: boolean = false;
  460. let rhythmAdded: boolean = false;
  461. if (currentClef !== undefined) {
  462. measure.addClefAtBegin(currentClef);
  463. clefAdded = true;
  464. } else {
  465. currentClef = this.activeClefs[visibleStaffIdx];
  466. }
  467. if (currentKey !== undefined) {
  468. currentKey = this.transposeKeyInstruction(currentKey, measure);
  469. const previousKey: KeyInstruction = isSystemStartMeasure ? undefined : this.activeKeys[visibleStaffIdx];
  470. measure.addKeyAtBegin(currentKey, previousKey, currentClef);
  471. keyAdded = true;
  472. }
  473. if (currentRhythm !== undefined) {
  474. measure.addRhythmAtBegin(currentRhythm);
  475. rhythmAdded = true;
  476. }
  477. if (clefAdded || keyAdded || rhythmAdded) {
  478. instructionsLengthX += measure.beginInstructionsWidth;
  479. if (rhythmAdded) {
  480. instructionsLengthX += this.rules.RhythmRightMargin;
  481. }
  482. }
  483. return instructionsLengthX;
  484. }
  485. private addInstructionsAtMeasureEnd(lastEntry: SourceStaffEntry, measure: StaffMeasure): number {
  486. if (lastEntry === undefined || lastEntry.Instructions === undefined || lastEntry.Instructions.length === 0) {
  487. return 0;
  488. }
  489. for (let idx: number = 0, len: number = lastEntry.Instructions.length; idx < len; ++idx) {
  490. const abstractNotationInstruction: AbstractNotationInstruction = lastEntry.Instructions[idx];
  491. if (abstractNotationInstruction instanceof ClefInstruction) {
  492. const activeClef: ClefInstruction = <ClefInstruction>abstractNotationInstruction;
  493. measure.addClefAtEnd(activeClef);
  494. }
  495. }
  496. return this.rules.MeasureRightMargin + measure.endInstructionsWidth;
  497. }
  498. /**
  499. * Track down and update the active ClefInstruction in Measure's StaffEntries.
  500. * This has to be done after the measure is added to a system
  501. * (otherwise already the check if the measure fits to the system would update the active clefs..)
  502. * @param measure
  503. * @param staffMeasures
  504. */
  505. private updateActiveClefs(measure: SourceMeasure, staffMeasures: StaffMeasure[]): void {
  506. for (let visStaffIdx: number = 0, len: number = staffMeasures.length; visStaffIdx < len; visStaffIdx++) {
  507. const staffIndex: number = this.visibleStaffIndices[visStaffIdx];
  508. const firstEntry: SourceStaffEntry = measure.FirstInstructionsStaffEntries[staffIndex];
  509. if (firstEntry !== undefined) {
  510. for (let idx: number = 0, len2: number = firstEntry.Instructions.length; idx < len2; ++idx) {
  511. const abstractNotationInstruction: AbstractNotationInstruction = firstEntry.Instructions[idx];
  512. if (abstractNotationInstruction instanceof ClefInstruction) {
  513. this.activeClefs[visStaffIdx] = <ClefInstruction>abstractNotationInstruction;
  514. } else if (abstractNotationInstruction instanceof KeyInstruction) {
  515. this.activeKeys[visStaffIdx] = <KeyInstruction>abstractNotationInstruction;
  516. } else if (abstractNotationInstruction instanceof RhythmInstruction) {
  517. this.activeRhythm[visStaffIdx] = <RhythmInstruction>abstractNotationInstruction;
  518. }
  519. }
  520. }
  521. const entries: SourceStaffEntry[] = measure.getEntriesPerStaff(staffIndex);
  522. for (let idx: number = 0, len2: number = entries.length; idx < len2; ++idx) {
  523. const staffEntry: SourceStaffEntry = entries[idx];
  524. if (staffEntry.Instructions !== undefined) {
  525. for (let idx2: number = 0, len3: number = staffEntry.Instructions.length; idx2 < len3; ++idx2) {
  526. const abstractNotationInstruction: AbstractNotationInstruction = staffEntry.Instructions[idx2];
  527. if (abstractNotationInstruction instanceof ClefInstruction) {
  528. this.activeClefs[visStaffIdx] = <ClefInstruction>abstractNotationInstruction;
  529. }
  530. }
  531. }
  532. }
  533. const lastEntry: SourceStaffEntry = measure.LastInstructionsStaffEntries[staffIndex];
  534. if (lastEntry !== undefined) {
  535. const instructions: AbstractNotationInstruction[] = lastEntry.Instructions;
  536. for (let idx: number = 0, len3: number = instructions.length; idx < len3; ++idx) {
  537. const abstractNotationInstruction: AbstractNotationInstruction = instructions[idx];
  538. if (abstractNotationInstruction instanceof ClefInstruction) {
  539. this.activeClefs[visStaffIdx] = <ClefInstruction>abstractNotationInstruction;
  540. }
  541. }
  542. }
  543. }
  544. }
  545. /**
  546. * Check if an extra Instruction [[Measure]] is needed.
  547. * @param measures
  548. */
  549. private checkAndCreateExtraInstructionMeasure(measures: StaffMeasure[]): void {
  550. const firstStaffEntries: SourceStaffEntry[] = measures[0].parentSourceMeasure.FirstInstructionsStaffEntries;
  551. const visibleInstructionEntries: SourceStaffEntry[] = [];
  552. for (let idx: number = 0, len: number = measures.length; idx < len; ++idx) {
  553. const measure: StaffMeasure = measures[idx];
  554. visibleInstructionEntries.push(firstStaffEntries[measure.ParentStaff.idInMusicSheet]);
  555. }
  556. let maxMeasureWidth: number = 0;
  557. for (let visStaffIdx: number = 0, len: number = visibleInstructionEntries.length; visStaffIdx < len; ++visStaffIdx) {
  558. const sse: SourceStaffEntry = visibleInstructionEntries[visStaffIdx];
  559. if (sse === undefined) {
  560. continue;
  561. }
  562. const instructions: AbstractNotationInstruction[] = sse.Instructions;
  563. let keyInstruction: KeyInstruction = undefined;
  564. let rhythmInstruction: RhythmInstruction = undefined;
  565. for (let idx2: number = 0, len2: number = instructions.length; idx2 < len2; ++idx2) {
  566. const instruction: AbstractNotationInstruction = instructions[idx2];
  567. if (instruction instanceof KeyInstruction && (<KeyInstruction>instruction).Key !== this.activeKeys[visStaffIdx].Key) {
  568. keyInstruction = <KeyInstruction>instruction;
  569. }
  570. if (instruction instanceof RhythmInstruction && (<RhythmInstruction>instruction) !== this.activeRhythm[visStaffIdx]) {
  571. rhythmInstruction = <RhythmInstruction>instruction;
  572. }
  573. }
  574. if (keyInstruction !== undefined || rhythmInstruction !== undefined) {
  575. const measureWidth: number = this.addExtraInstructionMeasure(visStaffIdx, keyInstruction, rhythmInstruction);
  576. maxMeasureWidth = Math.max(maxMeasureWidth, measureWidth);
  577. }
  578. }
  579. if (maxMeasureWidth > 0) {
  580. this.currentSystemParams.systemMeasures.push({
  581. beginLine: SystemLinesEnum.None,
  582. endLine: SystemLinesEnum.None,
  583. });
  584. this.currentSystemParams.currentWidth += maxMeasureWidth;
  585. this.currentSystemParams.currentSystemFixWidth += maxMeasureWidth;
  586. }
  587. }
  588. private addExtraInstructionMeasure(visStaffIdx: number, keyInstruction: KeyInstruction, rhythmInstruction: RhythmInstruction): number {
  589. const currentSystem: MusicSystem = this.currentSystemParams.currentSystem;
  590. const measures: StaffMeasure[] = [];
  591. const measure: StaffMeasure = this.symbolFactory.createExtraStaffMeasure(currentSystem.StaffLines[visStaffIdx]);
  592. measures.push(measure);
  593. if (keyInstruction !== undefined) {
  594. measure.addKeyAtBegin(keyInstruction, this.activeKeys[visStaffIdx], this.activeClefs[visStaffIdx]);
  595. }
  596. if (rhythmInstruction !== undefined) {
  597. measure.addRhythmAtBegin(rhythmInstruction);
  598. }
  599. measure.PositionAndShape.BorderLeft = 0.0;
  600. measure.PositionAndShape.BorderTop = 0.0;
  601. measure.PositionAndShape.BorderBottom = this.rules.StaffHeight;
  602. const width: number = this.rules.MeasureLeftMargin + measure.beginInstructionsWidth + this.rules.MeasureRightMargin;
  603. measure.PositionAndShape.BorderRight = width;
  604. currentSystem.StaffLines[visStaffIdx].Measures.push(measure);
  605. measure.ParentStaffLine = currentSystem.StaffLines[visStaffIdx];
  606. currentSystem.StaffLines[visStaffIdx].PositionAndShape.ChildElements.push(measure.PositionAndShape);
  607. return width;
  608. }
  609. /**
  610. * Add all current vertical Measures to currentSystem.
  611. * @param staffMeasures
  612. */
  613. private addStaveMeasuresToSystem(staffMeasures: StaffMeasure[]): void {
  614. if (staffMeasures[0] !== undefined) {
  615. const gmeasures: StaffMeasure[] = [];
  616. for (let i: number = 0; i < staffMeasures.length; i++) {
  617. gmeasures.push(staffMeasures[i]);
  618. }
  619. const currentSystem: MusicSystem = this.currentSystemParams.currentSystem;
  620. for (let visStaffIdx: number = 0; visStaffIdx < this.numberOfVisibleStaffLines; visStaffIdx++) {
  621. const measure: StaffMeasure = gmeasures[visStaffIdx];
  622. currentSystem.StaffLines[visStaffIdx].Measures.push(measure);
  623. measure.ParentStaffLine = currentSystem.StaffLines[visStaffIdx];
  624. currentSystem.StaffLines[visStaffIdx].PositionAndShape.ChildElements.push(measure.PositionAndShape);
  625. }
  626. currentSystem.AddStaffMeasures(gmeasures);
  627. }
  628. }
  629. /**
  630. * Return the width of the corresponding [[SystemLine]] and set the corresponding [[SystemLineEnum]].
  631. * @returns {SystemLinesEnum}
  632. */
  633. private getMeasureStartLine(): SystemLinesEnum {
  634. const thisMeasureBeginsLineRep: boolean = this.thisMeasureBeginsLineRepetition();
  635. if (thisMeasureBeginsLineRep) {
  636. const isSystemStartMeasure: boolean = this.currentSystemParams.IsSystemStartMeasure();
  637. const isGlobalFirstMeasure: boolean = this.measureListIndex === 0;
  638. if (this.previousMeasureEndsLineRepetition() && !isSystemStartMeasure) {
  639. return SystemLinesEnum.DotsBoldBoldDots;
  640. }
  641. if (!isGlobalFirstMeasure) {
  642. return SystemLinesEnum.BoldThinDots;
  643. }
  644. }
  645. return SystemLinesEnum.None;
  646. }
  647. private getMeasureEndLine(): SystemLinesEnum {
  648. if (this.nextMeasureBeginsLineRepetition() && this.thisMeasureEndsLineRepetition()) {
  649. return SystemLinesEnum.DotsBoldBoldDots;
  650. }
  651. if (this.thisMeasureEndsLineRepetition()) {
  652. return SystemLinesEnum.DotsThinBold;
  653. }
  654. if (this.measureListIndex === this.measureList.length - 1 || this.measureList[this.measureListIndex][0].parentSourceMeasure.endsPiece) {
  655. return SystemLinesEnum.ThinBold;
  656. }
  657. if (this.nextMeasureHasKeyInstructionChange() || this.thisMeasureEndsWordRepetition() || this.nextMeasureBeginsWordRepetition()) {
  658. return SystemLinesEnum.DoubleThin;
  659. }
  660. return SystemLinesEnum.SingleThin;
  661. }
  662. /**
  663. * Return the width of the corresponding [[SystemLine]] and sets the corresponding [[SystemLineEnum]].
  664. * @param measure
  665. * @param systemLineEnum
  666. * @param isSystemStartMeasure
  667. * @returns {number}
  668. */
  669. private getLineWidth(measure: StaffMeasure, systemLineEnum: SystemLinesEnum, isSystemStartMeasure: boolean): number {
  670. let width: number = measure.getLineWidth(systemLineEnum);
  671. if (systemLineEnum === SystemLinesEnum.DotsBoldBoldDots) {
  672. width /= 2;
  673. }
  674. if (isSystemStartMeasure && systemLineEnum === SystemLinesEnum.BoldThinDots) {
  675. width += this.rules.DistanceBetweenLastInstructionAndRepetitionBarline;
  676. }
  677. return width;
  678. }
  679. private previousMeasureEndsLineRepetition(): boolean {
  680. if (this.measureListIndex === 0) {
  681. return false;
  682. }
  683. for (let idx: number = 0, len: number = this.measureList[this.measureListIndex - 1].length; idx < len; ++idx) {
  684. const measure: StaffMeasure = this.measureList[this.measureListIndex - 1][idx];
  685. if (measure.endsWithLineRepetition()) {
  686. return true;
  687. }
  688. }
  689. return false;
  690. }
  691. /**
  692. * Check if at this [[Measure]] starts a [[Repetition]].
  693. * @returns {boolean}
  694. */
  695. private thisMeasureBeginsLineRepetition(): boolean {
  696. for (let idx: number = 0, len: number = this.measureList[this.measureListIndex].length; idx < len; ++idx) {
  697. const measure: StaffMeasure = this.measureList[this.measureListIndex][idx];
  698. if (measure.beginsWithLineRepetition()) {
  699. return true;
  700. }
  701. }
  702. return false;
  703. }
  704. /**
  705. * Check if a [[Repetition]] starts at the next [[Measure]].
  706. * @returns {boolean}
  707. */
  708. private nextMeasureBeginsLineRepetition(): boolean {
  709. const nextMeasureIndex: number = this.measureListIndex + 1;
  710. if (nextMeasureIndex >= this.graphicalMusicSheet.ParentMusicSheet.SourceMeasures.length) {
  711. return false;
  712. }
  713. for (let idx: number = 0, len: number = this.measureList[nextMeasureIndex].length; idx < len; ++idx) {
  714. const measure: StaffMeasure = this.measureList[nextMeasureIndex][idx];
  715. if (measure.beginsWithLineRepetition()) {
  716. return true;
  717. }
  718. }
  719. return false;
  720. }
  721. /**
  722. * Check if this [[Measure]] is a [[Repetition]] ending.
  723. * @returns {boolean}
  724. */
  725. private thisMeasureEndsLineRepetition(): boolean {
  726. for (let idx: number = 0, len: number = this.measureList[this.measureListIndex].length; idx < len; ++idx) {
  727. const measure: StaffMeasure = this.measureList[this.measureListIndex][idx];
  728. if (measure.endsWithLineRepetition()) {
  729. return true;
  730. }
  731. }
  732. return false;
  733. }
  734. /**
  735. * Check if a [[Repetition]] starts at the next [[Measure]].
  736. * @returns {boolean}
  737. */
  738. private nextMeasureBeginsWordRepetition(): boolean {
  739. const nextMeasureIndex: number = this.measureListIndex + 1;
  740. if (nextMeasureIndex >= this.graphicalMusicSheet.ParentMusicSheet.SourceMeasures.length) {
  741. return false;
  742. }
  743. for (let idx: number = 0, len: number = this.measureList[nextMeasureIndex].length; idx < len; ++idx) {
  744. const measure: StaffMeasure = this.measureList[nextMeasureIndex][idx];
  745. if (measure.beginsWithWordRepetition()) {
  746. return true;
  747. }
  748. }
  749. return false;
  750. }
  751. /**
  752. * Check if this [[Measure]] is a [[Repetition]] ending.
  753. * @returns {boolean}
  754. */
  755. private thisMeasureEndsWordRepetition(): boolean {
  756. for (let idx: number = 0, len: number = this.measureList[this.measureListIndex].length; idx < len; ++idx) {
  757. const measure: StaffMeasure = this.measureList[this.measureListIndex][idx];
  758. if (measure.endsWithWordRepetition()) {
  759. return true;
  760. }
  761. }
  762. return false;
  763. }
  764. /**
  765. * Check if the next [[Measure]] has a [[KeyInstruction]] change.
  766. * @returns {boolean}
  767. */
  768. private nextMeasureHasKeyInstructionChange(): boolean {
  769. return this.getNextMeasureKeyInstruction() !== undefined;
  770. }
  771. private getNextMeasureKeyInstruction(): KeyInstruction {
  772. if (this.measureListIndex < this.measureList.length - 1) {
  773. for (let visIndex: number = 0; visIndex < this.measureList[this.measureListIndex].length; visIndex++) {
  774. const sourceMeasure: SourceMeasure = this.measureList[this.measureListIndex + 1][visIndex].parentSourceMeasure;
  775. if (sourceMeasure === undefined) {
  776. return undefined;
  777. }
  778. return sourceMeasure.getKeyInstruction(this.visibleStaffIndices[visIndex]);
  779. }
  780. }
  781. return undefined;
  782. }
  783. /**
  784. * Calculate the X ScalingFactor in order to strech the whole System.
  785. * @param systemFixWidth
  786. * @param systemVarWidth
  787. * @returns {number}
  788. */
  789. private calculateXScalingFactor(systemFixWidth: number, systemVarWidth: number): number {
  790. if (Math.abs(systemVarWidth - 0) < 0.00001 || Math.abs(systemFixWidth - 0) < 0.00001) {
  791. return 1.0;
  792. }
  793. let systemEndX: number;
  794. const currentSystem: MusicSystem = this.currentSystemParams.currentSystem;
  795. systemEndX = currentSystem.StaffLines[0].PositionAndShape.Size.width;
  796. const scalingFactor: number = (systemEndX - systemFixWidth) / systemVarWidth;
  797. return scalingFactor;
  798. }
  799. /**
  800. * Stretch the whole System so that no white space is left at the end.
  801. * @param isPartEndingSystem
  802. */
  803. private stretchMusicSystem(isPartEndingSystem: boolean): void {
  804. let scalingFactor: number = this.calculateXScalingFactor(
  805. this.currentSystemParams.currentSystemFixWidth, this.currentSystemParams.currentSystemVarWidth
  806. );
  807. if (isPartEndingSystem) {
  808. scalingFactor = Math.min(scalingFactor, this.rules.LastSystemMaxScalingFactor);
  809. }
  810. const currentSystem: MusicSystem = this.currentSystemParams.currentSystem;
  811. for (let visStaffIdx: number = 0, len: number = currentSystem.StaffLines.length; visStaffIdx < len; ++visStaffIdx) {
  812. const staffLine: StaffLine = currentSystem.StaffLines[visStaffIdx];
  813. let currentXPosition: number = 0.0;
  814. for (let measureIndex: number = 0; measureIndex < staffLine.Measures.length; measureIndex++) {
  815. const measure: StaffMeasure = staffLine.Measures[measureIndex];
  816. measure.setPositionInStaffline(currentXPosition);
  817. measure.setWidth(measure.beginInstructionsWidth + measure.minimumStaffEntriesWidth * scalingFactor + measure.endInstructionsWidth);
  818. if (measureIndex < this.currentSystemParams.systemMeasures.length) {
  819. const startLine: SystemLinesEnum = this.currentSystemParams.systemMeasures[measureIndex].beginLine;
  820. const lineWidth: number = measure.getLineWidth(SystemLinesEnum.BoldThinDots);
  821. switch (startLine) {
  822. case SystemLinesEnum.BoldThinDots:
  823. let xPosition: number = currentXPosition;
  824. if (measureIndex === 0) {
  825. xPosition = currentXPosition + measure.beginInstructionsWidth - lineWidth;
  826. }
  827. currentSystem.createVerticalLineForMeasure(xPosition, lineWidth, startLine, SystemLinePosition.MeasureBegin, measureIndex, measure);
  828. break;
  829. default:
  830. }
  831. }
  832. measure.staffEntriesScaleFactor = scalingFactor;
  833. measure.layoutSymbols();
  834. const nextMeasureHasRepStartLine: boolean = measureIndex + 1 < this.currentSystemParams.systemMeasures.length
  835. && this.currentSystemParams.systemMeasures[measureIndex + 1].beginLine === SystemLinesEnum.BoldThinDots;
  836. if (!nextMeasureHasRepStartLine) {
  837. let endLine: SystemLinesEnum = SystemLinesEnum.SingleThin;
  838. if (measureIndex < this.currentSystemParams.systemMeasures.length) {
  839. endLine = this.currentSystemParams.systemMeasures[measureIndex].endLine;
  840. }
  841. const lineWidth: number = measure.getLineWidth(endLine);
  842. let xPos: number = measure.PositionAndShape.RelativePosition.x + measure.PositionAndShape.BorderRight - lineWidth;
  843. if (endLine === SystemLinesEnum.DotsBoldBoldDots) {
  844. xPos -= lineWidth / 2;
  845. }
  846. currentSystem.createVerticalLineForMeasure(xPos, lineWidth, endLine, SystemLinePosition.MeasureEnd, measureIndex, measure);
  847. }
  848. currentXPosition = measure.PositionAndShape.RelativePosition.x + measure.PositionAndShape.BorderRight;
  849. }
  850. }
  851. if (isPartEndingSystem) {
  852. this.decreaseMusicSystemBorders();
  853. }
  854. }
  855. /**
  856. * If the last [[MusicSystem]] doesn't need stretching, then this method decreases the System's Width,
  857. * the [[StaffLine]]'s Width and the 5 [[StaffLine]]s length.
  858. */
  859. private decreaseMusicSystemBorders(): void {
  860. const currentSystem: MusicSystem = this.currentSystemParams.currentSystem;
  861. const bb: BoundingBox = CollectionUtil.last(currentSystem.StaffLines[0].Measures).PositionAndShape;
  862. const width: number = bb.RelativePosition.x + bb.Size.width;
  863. for (let idx: number = 0, len: number = currentSystem.StaffLines.length; idx < len; ++idx) {
  864. const staffLine: StaffLine = currentSystem.StaffLines[idx];
  865. staffLine.PositionAndShape.BorderRight = width;
  866. for (let idx2: number = 0, len2: number = staffLine.StaffLines.length; idx2 < len2; ++idx2) {
  867. const graphicalLine: GraphicalLine = staffLine.StaffLines[idx2];
  868. graphicalLine.End = new PointF2D(width, graphicalLine.End.y);
  869. }
  870. }
  871. currentSystem.PositionAndShape.BorderRight = width + this.currentSystemParams.maxLabelLength + this.rules.SystemLabelsRightMargin;
  872. }
  873. }
  874. export class SystemBuildParameters {
  875. public currentSystem: MusicSystem;
  876. public systemMeasures: MeasureBuildParameters[] = [];
  877. public systemMeasureIndex: number = 0;
  878. public currentWidth: number = 0;
  879. public currentSystemFixWidth: number = 0;
  880. public currentSystemVarWidth: number = 0;
  881. public maxLabelLength: number = 0;
  882. public IsSystemStartMeasure(): boolean {
  883. return this.systemMeasureIndex === 0;
  884. }
  885. }
  886. export class MeasureBuildParameters {
  887. public beginLine: SystemLinesEnum;
  888. public endLine: SystemLinesEnum;
  889. }