MusicSystemBuilder.ts 49 KB

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