MusicSystemBuilder.ts 47 KB

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