MusicSystemBuilder.ts 70 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308
  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. import { MusicSheet } from "../MusicSheet";
  24. export class MusicSystemBuilder {
  25. protected measureList: GraphicalMeasure[][];
  26. protected graphicalMusicSheet: GraphicalMusicSheet;
  27. protected currentSystemParams: SystemBuildParameters;
  28. protected numberOfVisibleStaffLines: number;
  29. protected rules: EngravingRules;
  30. protected measureListIndex: number;
  31. protected musicSystems: MusicSystem[] = [];
  32. /**
  33. * Does the mapping from the currently visible staves to the global staff-list of the music sheet.
  34. */
  35. protected visibleStaffIndices: number[];
  36. protected activeRhythm: RhythmInstruction[];
  37. protected activeKeys: KeyInstruction[];
  38. protected activeClefs: ClefInstruction[];
  39. protected globalSystemIndex: number = 0;
  40. protected 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.numberOfVisibleStaffLines = numberOfStaffLines;
  48. this.activeRhythm = new Array(this.numberOfVisibleStaffLines);
  49. this.activeKeys = new Array(this.numberOfVisibleStaffLines);
  50. this.activeClefs = new Array(this.numberOfVisibleStaffLines);
  51. this.initializeActiveInstructions(this.measureList[0]);
  52. }
  53. public buildMusicSystems(): MusicSystem[] {
  54. const systemMaxWidth: number = this.getFullPageSystemWidth();
  55. let prevMeasureEndsPart: boolean = false;
  56. this.measureListIndex = 0;
  57. this.currentSystemParams = new SystemBuildParameters();
  58. // the first System - create also its Labels
  59. this.currentSystemParams.currentSystem = this.initMusicSystem();
  60. // let numberOfMeasures: number = 0;
  61. // for (let idx: number = 0, len: number = this.measureList.length; idx < len; ++idx) {
  62. // if (this.measureList[idx].length > 0) {
  63. // numberOfMeasures++;
  64. // }
  65. // }
  66. // console.log(`numberOfMeasures: ${numberOfMeasures}`);
  67. // go through measures and add to system until system gets too long -> finish system and start next system [line break, new system].
  68. while (this.measureListIndex < this.measureList.length) {
  69. const graphicalMeasures: GraphicalMeasure[] = this.measureList[this.measureListIndex];
  70. if (!graphicalMeasures || !graphicalMeasures[0]) {
  71. this.measureListIndex++;
  72. continue; // previous measure was probably multi-rest, skip this one
  73. }
  74. for (let idx: number = 0, len: number = graphicalMeasures.length; idx < len; ++idx) {
  75. // graphicalMeasures[idx].InitiallyActiveClef = this.activeClefs[idx]; // too early to know clef
  76. graphicalMeasures[idx].resetLayout();
  77. }
  78. const sourceMeasure: SourceMeasure = graphicalMeasures[0].parentSourceMeasure;
  79. const sourceMeasureEndsPart: boolean = sourceMeasure.HasEndLine;
  80. const sourceMeasureBreaksSystem: boolean = sourceMeasureEndsPart && this.rules.NewPartAndSystemAfterFinalBarline;
  81. const isSystemStartMeasure: boolean = this.currentSystemParams.IsSystemStartMeasure();
  82. sourceMeasure.IsSystemStartMeasure = isSystemStartMeasure;
  83. const isFirstSourceMeasure: boolean = sourceMeasure === this.graphicalMusicSheet.ParentMusicSheet.getFirstSourceMeasure();
  84. let currentMeasureBeginInstructionsWidth: number = this.rules.MeasureLeftMargin;
  85. let currentMeasureEndInstructionsWidth: number = 0;
  86. // calculate the current Measure Width:
  87. // The width of a measure is build up from
  88. // 1. the begin instructions (clef, Key, Rhythm),
  89. // 2. the staff entries (= notes) and
  90. // 3. the end instructions (actually only clefs)
  91. const measureStartLine: SystemLinesEnum = this.getMeasureStartLine();
  92. currentMeasureBeginInstructionsWidth += this.getLineWidth(graphicalMeasures[0], measureStartLine, isSystemStartMeasure);
  93. if (!this.leadSheet) {
  94. let forceShowRhythm: boolean = false;
  95. if (prevMeasureEndsPart && this.rules.ShowRhythmAgainAfterPartEndOrFinalBarline) {
  96. forceShowRhythm = true;
  97. }
  98. currentMeasureBeginInstructionsWidth += this.addBeginInstructions( graphicalMeasures,
  99. isSystemStartMeasure,
  100. isFirstSourceMeasure || forceShowRhythm);
  101. // forceShowRhythm could be a fourth parameter instead in addBeginInstructions, but only affects RhythmInstruction for now.
  102. currentMeasureEndInstructionsWidth += this.addEndInstructions(graphicalMeasures);
  103. }
  104. let currentMeasureVarWidth: number = 0;
  105. for (let i: number = 0; i < this.numberOfVisibleStaffLines; i++) {
  106. currentMeasureVarWidth = Math.max(currentMeasureVarWidth, graphicalMeasures[i].minimumStaffEntriesWidth);
  107. }
  108. // take into account the LineWidth after each Measure
  109. const measureEndLine: SystemLinesEnum = this.getMeasureEndLine();
  110. currentMeasureEndInstructionsWidth += this.getLineWidth(graphicalMeasures[0], measureEndLine, isSystemStartMeasure);
  111. let nextMeasureBeginInstructionWidth: number = this.rules.MeasureLeftMargin;
  112. // Check if there are key or rhythm change instructions within the next measure:
  113. let nextSourceMeasure: SourceMeasure = undefined;
  114. if (this.measureListIndex + 1 < this.measureList.length) {
  115. const nextGraphicalMeasures: GraphicalMeasure[] = this.measureList[this.measureListIndex + 1];
  116. // TODO: consider multirest. then the next graphical measure may not exist. but there shouldn't be hidden changes here.
  117. nextSourceMeasure = nextGraphicalMeasures[0]?.parentSourceMeasure;
  118. if (nextSourceMeasure?.hasBeginInstructions()) {
  119. nextMeasureBeginInstructionWidth += this.addBeginInstructions(nextGraphicalMeasures, false, false);
  120. }
  121. }
  122. let totalMeasureWidth: number = currentMeasureBeginInstructionsWidth + currentMeasureEndInstructionsWidth + currentMeasureVarWidth;
  123. if (graphicalMeasures[0]?.parentSourceMeasure?.multipleRestMeasures) {
  124. totalMeasureWidth = this.rules.MultipleRestMeasureDefaultWidth; // default 4 (12 seems too large)
  125. }
  126. const measureFitsInSystem: boolean = this.currentSystemParams.currentWidth + totalMeasureWidth + nextMeasureBeginInstructionWidth < systemMaxWidth;
  127. const doXmlPageBreak: boolean = this.rules.NewPageAtXMLNewPageAttribute && sourceMeasure.printNewPageXml;
  128. const impliedSystemBreak: boolean = doXmlPageBreak || // also create new system if doing page break
  129. (this.rules.NewSystemAtXMLNewPageAttribute && sourceMeasure.printNewPageXml);
  130. const doXmlLineBreak: boolean = impliedSystemBreak ||
  131. (this.rules.NewSystemAtXMLNewSystemAttribute && sourceMeasure.printNewSystemXml);
  132. if (isSystemStartMeasure || (measureFitsInSystem && !doXmlLineBreak)) {
  133. this.addMeasureToSystem(
  134. graphicalMeasures, measureStartLine, measureEndLine, totalMeasureWidth,
  135. currentMeasureBeginInstructionsWidth, currentMeasureVarWidth, currentMeasureEndInstructionsWidth
  136. );
  137. this.updateActiveClefs(sourceMeasure, graphicalMeasures);
  138. this.measureListIndex++;
  139. if (sourceMeasureBreaksSystem) {
  140. if (this.rules.MaxSystemToDrawNumber === this.musicSystems.length) {
  141. this.finalizeCurrentSystem(graphicalMeasures, !this.rules.StretchLastSystemLine, false);
  142. return this.musicSystems;
  143. }
  144. this.finalizeCurrentAndCreateNewSystem(graphicalMeasures, !this.rules.StretchLastSystemLine, false);
  145. }
  146. prevMeasureEndsPart = sourceMeasureEndsPart;
  147. } else {
  148. if (this.rules.MaxSystemToDrawNumber === this.musicSystems.length) {
  149. this.finalizeCurrentSystem(graphicalMeasures, false, true, doXmlPageBreak);
  150. return this.musicSystems;
  151. }
  152. // finalize current system and prepare a new one
  153. this.finalizeCurrentAndCreateNewSystem(graphicalMeasures, false, true, doXmlPageBreak);
  154. // don't increase measure index to check this measure now again
  155. // don't set prevMeasureEndsPart in this case! because we will loop with the same measure again.
  156. }
  157. }
  158. if (this.currentSystemParams.systemMeasures.length > 0) {
  159. if (this.rules.MaxSystemToDrawNumber === this.musicSystems.length) {
  160. this.finalizeCurrentSystem(this.measureList[this.measureList.length - 1], !this.rules.StretchLastSystemLine, false);
  161. return this.musicSystems;
  162. }
  163. // TODO FixedMeasureWidth: last measure will have a different stretch factor, misaligning measures and widths. use previous stretch factor instead
  164. this.finalizeCurrentAndCreateNewSystem(this.measureList[this.measureList.length - 1], !this.rules.StretchLastSystemLine, false);
  165. }
  166. return this.musicSystems;
  167. }
  168. /**
  169. * calculates the y positions of the staff lines within a system and
  170. * furthermore the y positions of the systems themselves.
  171. */
  172. public calculateSystemYLayout(): void {
  173. for (const musicSystem of this.musicSystems) {
  174. this.optimizeDistanceBetweenStaffLines(musicSystem);
  175. }
  176. // set y positions of systems using the previous system and a fixed distance.
  177. this.calculateMusicSystemsRelativePositions();
  178. }
  179. /**
  180. * Set the Width of the staff-Measures of one source measure.
  181. * @param graphicalMeasures
  182. * @param width
  183. * @param beginInstrWidth
  184. * @param endInstrWidth
  185. */
  186. protected setMeasureWidth(graphicalMeasures: GraphicalMeasure[], width: number, beginInstrWidth: number, endInstrWidth: number): void {
  187. for (let idx: number = 0, len: number = graphicalMeasures.length; idx < len; ++idx) {
  188. const measure: GraphicalMeasure = graphicalMeasures[idx];
  189. measure.setWidth(width);
  190. if (beginInstrWidth > 0) {
  191. measure.beginInstructionsWidth = beginInstrWidth;
  192. }
  193. if (endInstrWidth > 0) {
  194. measure.endInstructionsWidth = endInstrWidth;
  195. }
  196. }
  197. }
  198. /**
  199. * When the actual source measure doesn't fit any more, this method finalizes the current system and
  200. * opens up a new empty system, where the actual measure will be added in the next iteration.
  201. * @param measures
  202. * @param isPartEndingSystem
  203. */
  204. protected finalizeCurrentAndCreateNewSystem(measures: GraphicalMeasure[],
  205. systemEndsPart: boolean = false,
  206. checkExtraInstructionMeasure: boolean = true,
  207. startNewPage: boolean = false): void {
  208. this.finalizeCurrentSystem(measures, systemEndsPart, checkExtraInstructionMeasure, startNewPage);
  209. this.currentSystemParams = new SystemBuildParameters(); // this and the following used to be in finalizeCurrentSystem after stretchMusicSystem
  210. if (measures !== undefined &&
  211. this.measureListIndex < this.measureList.length) {
  212. this.currentSystemParams.currentSystem = this.initMusicSystem();
  213. }
  214. }
  215. protected finalizeCurrentSystem(measures: GraphicalMeasure[],
  216. systemEndsPart: boolean = false,
  217. checkExtraInstructionMeasure: boolean = true,
  218. startNewPage: boolean = false): void {
  219. this.currentSystemParams.currentSystem.breaksPage = startNewPage;
  220. this.adaptRepetitionLineWithIfNeeded();
  221. if (measures !== undefined &&
  222. checkExtraInstructionMeasure) {
  223. this.checkAndCreateExtraInstructionMeasure(measures);
  224. }
  225. this.stretchMusicSystem(systemEndsPart);
  226. }
  227. /**
  228. * If a line repetition is ending and a new line repetition is starting at the end of the system,
  229. * the double repetition line has to be split into two: one at the currently ending system and
  230. * one at the next system.
  231. * (this should be refactored at some point to not use a combined end/start line but always separated lines)
  232. */
  233. protected adaptRepetitionLineWithIfNeeded(): void {
  234. const systemMeasures: MeasureBuildParameters[] = this.currentSystemParams.systemMeasures;
  235. if (systemMeasures.length >= 1) {
  236. const measures: GraphicalMeasure[] =
  237. this.currentSystemParams.currentSystem.GraphicalMeasures[this.currentSystemParams.currentSystem.GraphicalMeasures.length - 1];
  238. let diff: number = 0.0;
  239. const measureParams: MeasureBuildParameters = systemMeasures[systemMeasures.length - 1];
  240. if (measureParams.endLine === SystemLinesEnum.DotsBoldBoldDots) {
  241. measureParams.endLine = SystemLinesEnum.DotsThinBold;
  242. diff = measures[0].getLineWidth(SystemLinesEnum.DotsBoldBoldDots) / 2 - measures[0].getLineWidth(SystemLinesEnum.DotsThinBold);
  243. }
  244. this.currentSystemParams.currentSystemFixWidth -= diff;
  245. for (let idx: number = 0, len: number = measures.length; idx < len; ++idx) {
  246. const measure: GraphicalMeasure = measures[idx];
  247. measure.endInstructionsWidth -= diff;
  248. }
  249. }
  250. }
  251. protected addMeasureToSystem(
  252. graphicalMeasures: GraphicalMeasure[], measureStartLine: SystemLinesEnum, measureEndLine: SystemLinesEnum,
  253. totalMeasureWidth: number, currentMeasureBeginInstructionsWidth: number, currentVarWidth: number, currentMeasureEndInstructionsWidth: number
  254. ): void {
  255. this.currentSystemParams.systemMeasures.push({beginLine: measureStartLine, endLine: measureEndLine});
  256. this.setMeasureWidth(
  257. graphicalMeasures, totalMeasureWidth, currentMeasureBeginInstructionsWidth, currentMeasureEndInstructionsWidth
  258. );
  259. this.addStaveMeasuresToSystem(graphicalMeasures);
  260. this.currentSystemParams.currentWidth += totalMeasureWidth;
  261. this.currentSystemParams.currentSystemFixWidth += currentMeasureBeginInstructionsWidth + currentMeasureEndInstructionsWidth;
  262. this.currentSystemParams.currentSystemVarWidth += currentVarWidth;
  263. this.currentSystemParams.systemMeasureIndex++;
  264. }
  265. /**
  266. * Initialize a new [[MusicSystem]].
  267. * @returns {MusicSystem}
  268. */
  269. protected initMusicSystem(): MusicSystem {
  270. const musicSystem: MusicSystem = MusicSheetCalculator.symbolFactory.createMusicSystem(this.globalSystemIndex++, this.rules);
  271. this.musicSystems.push(musicSystem);
  272. this.layoutSystemStaves(musicSystem);
  273. musicSystem.createMusicSystemLabel(
  274. this.rules.InstrumentLabelTextHeight,
  275. this.rules.SystemLabelsRightMargin,
  276. this.rules.LabelMarginBorderFactor,
  277. this.musicSystems.length === 1
  278. );
  279. return musicSystem;
  280. }
  281. /**
  282. * Get the width the system should have for a given page width.
  283. * @returns {number}
  284. */
  285. protected getFullPageSystemWidth(): number {
  286. return this.graphicalMusicSheet.ParentMusicSheet.pageWidth - this.rules.PageLeftMargin
  287. - this.rules.PageRightMargin - this.rules.SystemLeftMargin - this.rules.SystemRightMargin;
  288. }
  289. protected layoutSystemStaves(musicSystem: MusicSystem): void {
  290. const systemWidth: number = this.getFullPageSystemWidth();
  291. const boundingBox: BoundingBox = musicSystem.PositionAndShape;
  292. boundingBox.BorderLeft = 0.0;
  293. boundingBox.BorderRight = systemWidth;
  294. boundingBox.BorderTop = 0.0;
  295. const staffList: Staff[] = [];
  296. const instruments: Instrument[] = this.graphicalMusicSheet.ParentMusicSheet.Instruments;
  297. for (let idx: number = 0, len: number = instruments.length; idx < len; ++idx) {
  298. const instrument: Instrument = instruments[idx];
  299. if (!instrument.Visible || instrument.Voices.length === 0) {
  300. continue;
  301. }
  302. for (let idx2: number = 0, len2: number = instrument.Staves.length; idx2 < len2; ++idx2) {
  303. const staff: Staff = instrument.Staves[idx2];
  304. staffList.push(staff);
  305. }
  306. }
  307. let multiLyrics: boolean = false;
  308. if (this.leadSheet) {
  309. for (let idx: number = 0, len: number = staffList.length; idx < len; ++idx) {
  310. const staff: Staff = staffList[idx];
  311. if (staff.ParentInstrument.LyricVersesNumbers.length > 1) {
  312. multiLyrics = true;
  313. break;
  314. }
  315. }
  316. }
  317. let yOffsetSum: number = 0;
  318. for (let i: number = 0; i < staffList.length; i++) {
  319. this.addStaffLineToMusicSystem(musicSystem, yOffsetSum, staffList[i]);
  320. yOffsetSum += this.rules.StaffHeight;
  321. if (i + 1 < staffList.length) {
  322. let yOffset: number = 0;
  323. if (this.leadSheet && !multiLyrics) {
  324. yOffset = 2.5;
  325. } else {
  326. if (staffList[i].ParentInstrument === staffList[i + 1].ParentInstrument) {
  327. yOffset = this.rules.BetweenStaffDistance;
  328. } else {
  329. yOffset = this.rules.StaffDistance;
  330. }
  331. }
  332. yOffsetSum += yOffset;
  333. }
  334. }
  335. boundingBox.BorderBottom = yOffsetSum;
  336. }
  337. /**
  338. * Calculate the [[StaffLine]](s) needed for a [[MusicSystem]].
  339. * @param musicSystem
  340. * @param relativeYPosition
  341. * @param staff
  342. */
  343. protected addStaffLineToMusicSystem(musicSystem: MusicSystem, relativeYPosition: number, staff: Staff): void {
  344. if (musicSystem) {
  345. const staffLine: StaffLine = MusicSheetCalculator.symbolFactory.createStaffLine(musicSystem, staff);
  346. musicSystem.StaffLines.push(staffLine);
  347. const boundingBox: BoundingBox = staffLine.PositionAndShape;
  348. const relativePosition: PointF2D = new PointF2D();
  349. relativePosition.x = 0.0;
  350. boundingBox.BorderRight = musicSystem.PositionAndShape.Size.width;
  351. relativePosition.y = relativeYPosition;
  352. boundingBox.RelativePosition = relativePosition;
  353. boundingBox.BorderLeft = 0.0;
  354. boundingBox.BorderTop = 0.0;
  355. boundingBox.BorderBottom = this.rules.StaffHeight;
  356. for (let i: number = 0; i < 5; i++) {
  357. const start: PointF2D = new PointF2D();
  358. start.x = 0.0;
  359. start.y = i * this.rules.StaffHeight / 4;
  360. const end: PointF2D = new PointF2D();
  361. end.x = staffLine.PositionAndShape.Size.width;
  362. end.y = i * this.rules.StaffHeight / 4;
  363. if (this.leadSheet) {
  364. start.y = end.y = 0;
  365. }
  366. staffLine.StaffLines[i] = new GraphicalLine(start, end, this.rules.StaffLineWidth);
  367. }
  368. }
  369. }
  370. /**
  371. * Initialize the active Instructions from the first [[SourceMeasure]] of first [[SourceMusicPart]].
  372. * @param measureList
  373. */
  374. protected initializeActiveInstructions(measureList: GraphicalMeasure[]): void {
  375. const firstSourceMeasure: SourceMeasure = this.graphicalMusicSheet.ParentMusicSheet.getFirstSourceMeasure();
  376. if (firstSourceMeasure) {
  377. this.visibleStaffIndices = this.graphicalMusicSheet.getVisibleStavesIndicesFromSourceMeasure(measureList);
  378. for (let i: number = 0, len: number = this.visibleStaffIndices.length; i < len; i++) {
  379. const staffIndex: number = this.visibleStaffIndices[i];
  380. const graphicalMeasure: GraphicalMeasure = this.graphicalMusicSheet
  381. .getGraphicalMeasureFromSourceMeasureAndIndex(firstSourceMeasure, staffIndex);
  382. this.activeClefs[i] = <ClefInstruction>firstSourceMeasure.FirstInstructionsStaffEntries[staffIndex].Instructions[0];
  383. graphicalMeasure.InitiallyActiveClef = this.activeClefs[i]; // TODO ClefInstruction.copy? doesn't exist
  384. const firstKeyInstruction: KeyInstruction = <KeyInstruction>firstSourceMeasure.FirstInstructionsStaffEntries[staffIndex].Instructions[1];
  385. if (firstKeyInstruction) {
  386. let keyInstruction: KeyInstruction = KeyInstruction.copy(firstKeyInstruction);
  387. keyInstruction = this.transposeKeyInstruction(keyInstruction, graphicalMeasure);
  388. this.activeKeys[i] = keyInstruction;
  389. }
  390. const firstRhythmInstruction: RhythmInstruction = <RhythmInstruction>
  391. firstSourceMeasure.FirstInstructionsStaffEntries[staffIndex].Instructions[2];
  392. // if (firstRhythmInstruction) {
  393. this.activeRhythm[i] = firstRhythmInstruction;
  394. }
  395. }
  396. }
  397. protected transposeKeyInstruction(keyInstruction: KeyInstruction, graphicalMeasure: GraphicalMeasure): KeyInstruction {
  398. const transposeHalftones: number = graphicalMeasure.getTransposedHalftones();
  399. if (transposeHalftones !== keyInstruction.isTransposedBy
  400. && graphicalMeasure.ParentStaff.ParentInstrument.MidiInstrumentId !== MidiInstrument.Percussion
  401. && MusicSheetCalculator.transposeCalculator !== undefined
  402. ) {
  403. MusicSheetCalculator.transposeCalculator.transposeKey(
  404. keyInstruction,
  405. transposeHalftones
  406. );
  407. }
  408. return keyInstruction;
  409. // TODO we probably need to call initializeActiveInstructions() after this has been executed
  410. // (though we need to call it from where this is called)
  411. // since the accidentals are messed up after changing from transpose to 1 then to 0 again,
  412. // probably because this.activeKeys doesn't get updated. or maybe the issue is somewhere else
  413. }
  414. /**
  415. * Calculate the width needed for Instructions (Key, Clef, Rhythm, Repetition) for the measure.
  416. * @param measures
  417. * @param isSystemFirstMeasure
  418. * @param isFirstSourceMeasure
  419. * @returns {number}
  420. */
  421. protected addBeginInstructions(measures: GraphicalMeasure[], isSystemFirstMeasure: boolean, isFirstSourceMeasure: boolean): number {
  422. const measureCount: number = measures.length;
  423. if (measureCount === 0) {
  424. return 0;
  425. }
  426. let totalBeginInstructionLengthX: number = 0.0;
  427. const sourceMeasure: SourceMeasure = measures[0].parentSourceMeasure;
  428. const staves: any[] = []; // VF.Stave. generally don't want to reference Vexflow classes here.
  429. for (let idx: number = 0; idx < measureCount; ++idx) {
  430. const measure: GraphicalMeasure = measures[idx];
  431. if (measure) { // MultiRestMeasure may be undefined
  432. staves.push((measure as any).getVFStave()); // as VexFlowMeasure
  433. }
  434. const staffIndex: number = this.visibleStaffIndices[idx];
  435. const beginInstructionsStaffEntry: SourceStaffEntry = sourceMeasure.FirstInstructionsStaffEntries[staffIndex];
  436. const beginInstructionLengthX: number = this.AddInstructionsAtMeasureBegin(
  437. beginInstructionsStaffEntry, measure,
  438. idx, isFirstSourceMeasure,
  439. isSystemFirstMeasure
  440. );
  441. totalBeginInstructionLengthX = Math.max(totalBeginInstructionLengthX, beginInstructionLengthX);
  442. }
  443. staves[0].formatBegModifiers(staves); // x-align notes / beginning modifiers like time signatures, e.g. for transposing instruments
  444. return totalBeginInstructionLengthX;
  445. }
  446. /**
  447. * Calculates the width needed for Instructions (Clef, Repetition) for the measure.
  448. * @param measures
  449. * @returns {number}
  450. */
  451. protected addEndInstructions(measures: GraphicalMeasure[]): number {
  452. const measureCount: number = measures.length;
  453. if (measureCount === 0) {
  454. return 0;
  455. }
  456. let totalEndInstructionLengthX: number = 0.5;
  457. const sourceMeasure: SourceMeasure = measures[0].parentSourceMeasure;
  458. for (let idx: number = 0; idx < measureCount; idx++) {
  459. const measure: GraphicalMeasure = measures[idx];
  460. const staffIndex: number = this.visibleStaffIndices[idx];
  461. const endInstructionsStaffEntry: SourceStaffEntry = sourceMeasure.LastInstructionsStaffEntries[staffIndex];
  462. const endInstructionLengthX: number = this.addInstructionsAtMeasureEnd(endInstructionsStaffEntry, measure, measures);
  463. totalEndInstructionLengthX = Math.max(totalEndInstructionLengthX, endInstructionLengthX);
  464. }
  465. return totalEndInstructionLengthX;
  466. }
  467. protected AddInstructionsAtMeasureBegin(firstEntry: SourceStaffEntry, measure: GraphicalMeasure,
  468. visibleStaffIdx: number, isFirstSourceMeasure: boolean, isSystemStartMeasure: boolean): number {
  469. let instructionsLengthX: number = 0;
  470. let currentClef: ClefInstruction = undefined;
  471. let currentKey: KeyInstruction = undefined;
  472. let currentRhythm: RhythmInstruction = undefined;
  473. if (firstEntry) {
  474. for (let idx: number = 0, len: number = firstEntry.Instructions.length; idx < len; ++idx) {
  475. const abstractNotationInstruction: AbstractNotationInstruction = firstEntry.Instructions[idx];
  476. if (abstractNotationInstruction instanceof ClefInstruction) {
  477. currentClef = <ClefInstruction>abstractNotationInstruction;
  478. } else if (abstractNotationInstruction instanceof KeyInstruction) {
  479. currentKey = <KeyInstruction>abstractNotationInstruction;
  480. } else if (abstractNotationInstruction instanceof RhythmInstruction) {
  481. currentRhythm = <RhythmInstruction>abstractNotationInstruction;
  482. }
  483. }
  484. }
  485. measure.InitiallyActiveClef = currentClef ?? this.activeClefs[visibleStaffIdx];
  486. // measure.resetLayout(); // this "fixes" a key signature change accidentals issue (extended 104),
  487. // but is actually not really a fix, it just sets the stave.clef to treble probably, and causes a bug in x-alignment
  488. if (isSystemStartMeasure) {
  489. if (!currentClef) {
  490. currentClef = this.activeClefs[visibleStaffIdx];
  491. }
  492. if (!currentKey) {
  493. currentKey = KeyInstruction.copy(this.activeKeys[visibleStaffIdx]);
  494. }
  495. if (isFirstSourceMeasure && !currentRhythm) {
  496. currentRhythm = this.activeRhythm[visibleStaffIdx];
  497. }
  498. }
  499. let clefAdded: boolean = false;
  500. let keyAdded: boolean = false;
  501. let rhythmAdded: boolean = false;
  502. if (currentClef) {
  503. measure.addClefAtBegin(currentClef);
  504. clefAdded = true;
  505. } else {
  506. currentClef = this.activeClefs[visibleStaffIdx];
  507. }
  508. if (currentKey) {
  509. currentKey = this.transposeKeyInstruction(currentKey, measure);
  510. const previousKey: KeyInstruction = isSystemStartMeasure ? undefined : this.activeKeys[visibleStaffIdx];
  511. measure.addKeyAtBegin(currentKey, previousKey, currentClef);
  512. keyAdded = true;
  513. }
  514. if (currentRhythm !== undefined && currentRhythm.PrintObject && this.rules.RenderTimeSignatures) {
  515. let printRhythm: boolean = true;
  516. // check for previous pickup measure
  517. // TODO this does not need to be a pickup measure, see #1069
  518. const pickupMeasureIndex: number = measure.MeasureNumber - 1;
  519. const measureIndex: number = pickupMeasureIndex - this.rules.MinMeasureToDrawIndex;
  520. if (measure.MeasureNumber - 1 >= 0 && this.measureList[measureIndex]) {
  521. const previousMeasureList: GraphicalMeasure[] = this.measureList[measureIndex];
  522. const previousMeasure: SourceMeasure = previousMeasureList[0]?.parentSourceMeasure;
  523. if (previousMeasure?.ImplicitMeasure && previousMeasure?.RhythmPrinted) {
  524. printRhythm = false;
  525. }
  526. }
  527. if (printRhythm) {
  528. measure.addRhythmAtBegin(currentRhythm);
  529. measure.parentSourceMeasure.RhythmPrinted = currentRhythm;
  530. rhythmAdded = true;
  531. }
  532. }
  533. if (clefAdded || keyAdded || rhythmAdded) {
  534. instructionsLengthX += measure.beginInstructionsWidth;
  535. if (rhythmAdded) {
  536. instructionsLengthX += this.rules.RhythmRightMargin;
  537. }
  538. }
  539. return instructionsLengthX;
  540. }
  541. protected addInstructionsAtMeasureEnd(lastEntry: SourceStaffEntry, measure: GraphicalMeasure,
  542. measures: GraphicalMeasure[]): number {
  543. if (!lastEntry || !lastEntry.Instructions || lastEntry.Instructions.length === 0) {
  544. return 0;
  545. }
  546. for (let idx: number = 0, len: number = lastEntry.Instructions.length; idx < len; ++idx) {
  547. const abstractNotationInstruction: AbstractNotationInstruction = lastEntry.Instructions[idx];
  548. if (abstractNotationInstruction instanceof ClefInstruction) {
  549. const activeClef: ClefInstruction = <ClefInstruction>abstractNotationInstruction;
  550. measure.addClefAtEnd(activeClef);
  551. for (const otherVerticalMeasure of measures) {
  552. if (otherVerticalMeasure !== measure) {
  553. otherVerticalMeasure.addClefAtEnd(activeClef, false);
  554. }
  555. }
  556. }
  557. }
  558. return this.rules.MeasureRightMargin + measure.endInstructionsWidth;
  559. }
  560. /**
  561. * Track down and update the active ClefInstruction in Measure's StaffEntries.
  562. * This has to be done after the measure is added to a system
  563. * (otherwise already the check if the measure fits to the system would update the active clefs..)
  564. * @param measure
  565. * @param graphicalMeasures
  566. */
  567. protected updateActiveClefs(measure: SourceMeasure, graphicalMeasures: GraphicalMeasure[]): void {
  568. for (let visStaffIdx: number = 0, len: number = graphicalMeasures.length; visStaffIdx < len; visStaffIdx++) {
  569. const staffIndex: number = this.visibleStaffIndices[visStaffIdx];
  570. const firstEntry: SourceStaffEntry = measure.FirstInstructionsStaffEntries[staffIndex];
  571. if (firstEntry) {
  572. for (let idx: number = 0, len2: number = firstEntry.Instructions.length; idx < len2; ++idx) {
  573. const abstractNotationInstruction: AbstractNotationInstruction = firstEntry.Instructions[idx];
  574. if (abstractNotationInstruction instanceof ClefInstruction) {
  575. this.activeClefs[visStaffIdx] = <ClefInstruction>abstractNotationInstruction;
  576. } else if (abstractNotationInstruction instanceof KeyInstruction) {
  577. this.activeKeys[visStaffIdx] = <KeyInstruction>abstractNotationInstruction;
  578. } else if (abstractNotationInstruction instanceof RhythmInstruction) {
  579. this.activeRhythm[visStaffIdx] = <RhythmInstruction>abstractNotationInstruction;
  580. }
  581. }
  582. }
  583. // graphicalMeasures[visStaffIdx].InitiallyActiveClef = this.activeClefs[visStaffIdx];
  584. // already done at MusicSystemBuilder.AddInstructionsAtMeasureBegin
  585. const entries: SourceStaffEntry[] = measure.getEntriesPerStaff(staffIndex);
  586. for (let idx: number = 0, len2: number = entries.length; idx < len2; ++idx) {
  587. const staffEntry: SourceStaffEntry = entries[idx];
  588. if (staffEntry.Instructions) {
  589. for (let idx2: number = 0, len3: number = staffEntry.Instructions.length; idx2 < len3; ++idx2) {
  590. const abstractNotationInstruction: AbstractNotationInstruction = staffEntry.Instructions[idx2];
  591. if (abstractNotationInstruction instanceof ClefInstruction) {
  592. this.activeClefs[visStaffIdx] = <ClefInstruction>abstractNotationInstruction;
  593. }
  594. }
  595. }
  596. }
  597. const lastEntry: SourceStaffEntry = measure.LastInstructionsStaffEntries[staffIndex];
  598. if (lastEntry) {
  599. const instructions: AbstractNotationInstruction[] = lastEntry.Instructions;
  600. for (let idx: number = 0, len3: number = instructions.length; idx < len3; ++idx) {
  601. const abstractNotationInstruction: AbstractNotationInstruction = instructions[idx];
  602. if (abstractNotationInstruction instanceof ClefInstruction) {
  603. this.activeClefs[visStaffIdx] = <ClefInstruction>abstractNotationInstruction;
  604. }
  605. }
  606. }
  607. }
  608. }
  609. /**
  610. * Check if an extra Instruction [[Measure]] is needed.
  611. * @param measures
  612. */
  613. protected checkAndCreateExtraInstructionMeasure(measures: GraphicalMeasure[]): void {
  614. const firstStaffEntries: SourceStaffEntry[] = measures[0].parentSourceMeasure.FirstInstructionsStaffEntries;
  615. const visibleInstructionEntries: SourceStaffEntry[] = [];
  616. for (let idx: number = 0, len: number = measures.length; idx < len; ++idx) {
  617. const measure: GraphicalMeasure = measures[idx];
  618. visibleInstructionEntries.push(firstStaffEntries[measure.ParentStaff.idInMusicSheet]);
  619. }
  620. let maxMeasureWidth: number = 0;
  621. for (let visStaffIdx: number = 0, len: number = visibleInstructionEntries.length; visStaffIdx < len; ++visStaffIdx) {
  622. const sse: SourceStaffEntry = visibleInstructionEntries[visStaffIdx];
  623. if (!sse) {
  624. continue;
  625. }
  626. const instructions: AbstractNotationInstruction[] = sse.Instructions;
  627. let keyInstruction: KeyInstruction = undefined;
  628. let rhythmInstruction: RhythmInstruction = undefined;
  629. for (let idx2: number = 0, len2: number = instructions.length; idx2 < len2; ++idx2) {
  630. const instruction: AbstractNotationInstruction = instructions[idx2];
  631. if (instruction instanceof KeyInstruction && (<KeyInstruction>instruction).Key !== this.activeKeys[visStaffIdx].Key) {
  632. keyInstruction = <KeyInstruction>instruction;
  633. }
  634. if (instruction instanceof RhythmInstruction && (<RhythmInstruction>instruction) !== this.activeRhythm[visStaffIdx]) {
  635. rhythmInstruction = <RhythmInstruction>instruction;
  636. }
  637. }
  638. if (keyInstruction !== undefined || rhythmInstruction) {
  639. const measureWidth: number = this.addExtraInstructionMeasure(visStaffIdx, keyInstruction, rhythmInstruction);
  640. maxMeasureWidth = Math.max(maxMeasureWidth, measureWidth);
  641. }
  642. }
  643. if (maxMeasureWidth > 0) {
  644. this.currentSystemParams.systemMeasures.push({
  645. beginLine: SystemLinesEnum.None,
  646. endLine: SystemLinesEnum.None,
  647. });
  648. this.currentSystemParams.currentWidth += maxMeasureWidth;
  649. this.currentSystemParams.currentSystemFixWidth += maxMeasureWidth;
  650. }
  651. }
  652. protected addExtraInstructionMeasure(visStaffIdx: number, keyInstruction: KeyInstruction, rhythmInstruction: RhythmInstruction): number {
  653. const currentSystem: MusicSystem = this.currentSystemParams.currentSystem;
  654. const measures: GraphicalMeasure[] = [];
  655. const measure: GraphicalMeasure = MusicSheetCalculator.symbolFactory.createExtraGraphicalMeasure(currentSystem.StaffLines[visStaffIdx]);
  656. measures.push(measure);
  657. if (keyInstruction) {
  658. measure.addKeyAtBegin(keyInstruction, this.activeKeys[visStaffIdx], this.activeClefs[visStaffIdx]);
  659. }
  660. if (rhythmInstruction !== undefined && rhythmInstruction.PrintObject) {
  661. measure.addRhythmAtBegin(rhythmInstruction);
  662. }
  663. measure.PositionAndShape.BorderLeft = 0.0;
  664. measure.PositionAndShape.BorderTop = 0.0;
  665. measure.PositionAndShape.BorderBottom = this.rules.StaffHeight;
  666. const width: number = this.rules.MeasureLeftMargin + measure.beginInstructionsWidth + this.rules.MeasureRightMargin;
  667. measure.PositionAndShape.BorderRight = width;
  668. currentSystem.StaffLines[visStaffIdx].Measures.push(measure);
  669. return width;
  670. }
  671. /**
  672. * Add all current vertical Measures to currentSystem.
  673. * @param graphicalMeasures
  674. */
  675. protected addStaveMeasuresToSystem(graphicalMeasures: GraphicalMeasure[]): void {
  676. if (graphicalMeasures[0]) {
  677. const gmeasures: GraphicalMeasure[] = [];
  678. for (let i: number = 0; i < graphicalMeasures.length; i++) {
  679. gmeasures.push(graphicalMeasures[i]);
  680. }
  681. const currentSystem: MusicSystem = this.currentSystemParams.currentSystem;
  682. for (let visStaffIdx: number = 0; visStaffIdx < this.numberOfVisibleStaffLines; visStaffIdx++) {
  683. const measure: GraphicalMeasure = gmeasures[visStaffIdx];
  684. currentSystem.StaffLines[visStaffIdx].Measures.push(measure);
  685. measure.ParentStaffLine = currentSystem.StaffLines[visStaffIdx];
  686. }
  687. currentSystem.AddGraphicalMeasures(gmeasures);
  688. }
  689. }
  690. /**
  691. * Return the width of the corresponding [[SystemLine]] and set the corresponding [[SystemLineEnum]].
  692. * @returns {SystemLinesEnum}
  693. */
  694. protected getMeasureStartLine(): SystemLinesEnum {
  695. const thisMeasureBeginsLineRep: boolean = this.thisMeasureBeginsLineRepetition();
  696. if (thisMeasureBeginsLineRep) {
  697. const isGlobalFirstMeasure: boolean = this.measureListIndex === 0;
  698. if (isGlobalFirstMeasure && this.rules.RepetitionAllowFirstMeasureBeginningRepeatBarline) {
  699. return SystemLinesEnum.BoldThinDots;
  700. }
  701. const isSystemStartMeasure: boolean = this.currentSystemParams.IsSystemStartMeasure();
  702. if (this.previousMeasureEndsLineRepetition() && !isSystemStartMeasure) {
  703. return SystemLinesEnum.DotsBoldBoldDots;
  704. }
  705. if (!isGlobalFirstMeasure) {
  706. return SystemLinesEnum.BoldThinDots;
  707. }
  708. }
  709. return SystemLinesEnum.None;
  710. }
  711. protected getMeasureEndLine(): SystemLinesEnum {
  712. let sourceMeasure: SourceMeasure = undefined;
  713. try {
  714. sourceMeasure = this.measureList[this.measureListIndex][0].parentSourceMeasure;
  715. if (this.rules.RenderMultipleRestMeasures && sourceMeasure.multipleRestMeasures > 1) {
  716. const newIndex: number = Math.min(
  717. this.graphicalMusicSheet.ParentMusicSheet.SourceMeasures.length - 1, // safety check
  718. sourceMeasure.measureListIndex + sourceMeasure.multipleRestMeasures - 1
  719. // check the bar line of the last sourcemeasure in the multiple measure rest sequence
  720. );
  721. sourceMeasure = this.graphicalMusicSheet.ParentMusicSheet.SourceMeasures[newIndex];
  722. // sourceMeasure = this.measureList[this.measureListIndex + sourceMeasure.multipleRestMeasures - 1][0].parentSourceMeasure;
  723. // this will be possible when the other GraphicalMeasures in the measureList aren't undefined anymore
  724. }
  725. } finally {
  726. // do nothing
  727. }
  728. if (this.nextMeasureBeginsLineRepetition() && this.thisMeasureEndsLineRepetition()) {
  729. return SystemLinesEnum.DotsBoldBoldDots;
  730. }
  731. if (this.thisMeasureEndsLineRepetition()) {
  732. return SystemLinesEnum.DotsThinBold;
  733. }
  734. // always end piece with final barline: not a good idea. user should be able to override final barline.
  735. // also, selecting range of measures to draw would always end with final barline, even if extract is from the middle of the piece
  736. // this was probably done before we parsed the barline type from XML.
  737. /*if (this.measureListIndex === this.measureList.length - 1 || this.measureList[this.measureListIndex][0].parentSourceMeasure.endsPiece) {
  738. return SystemLinesEnum.ThinBold;
  739. }*/
  740. if (this.nextMeasureHasKeyInstructionChange() || this.thisMeasureEndsWordRepetition() || this.nextMeasureBeginsWordRepetition()) {
  741. return SystemLinesEnum.DoubleThin;
  742. }
  743. if (!sourceMeasure) {
  744. return SystemLinesEnum.SingleThin;
  745. }
  746. if (sourceMeasure.endingBarStyleEnum !== undefined) {
  747. return sourceMeasure.endingBarStyleEnum;
  748. }
  749. // TODO: print an error message if the default fallback is used.
  750. return SystemLinesEnum.SingleThin;
  751. }
  752. /**
  753. * Return the width of the corresponding [[SystemLine]] and sets the corresponding [[SystemLineEnum]].
  754. * @param measure
  755. * @param systemLineEnum
  756. * @param isSystemStartMeasure
  757. * @returns {number}
  758. */
  759. protected getLineWidth(measure: GraphicalMeasure, systemLineEnum: SystemLinesEnum, isSystemStartMeasure: boolean): number {
  760. let width: number = measure.getLineWidth(systemLineEnum);
  761. if (systemLineEnum === SystemLinesEnum.DotsBoldBoldDots) {
  762. width /= 2;
  763. }
  764. if (isSystemStartMeasure && systemLineEnum === SystemLinesEnum.BoldThinDots) {
  765. width += this.rules.DistanceBetweenLastInstructionAndRepetitionBarline;
  766. }
  767. return width;
  768. }
  769. protected previousMeasureEndsLineRepetition(): boolean {
  770. if (this.measureListIndex === 0) {
  771. return false;
  772. }
  773. for (let idx: number = 0, len: number = this.measureList[this.measureListIndex - 1].length; idx < len; ++idx) {
  774. const measure: GraphicalMeasure = this.measureList[this.measureListIndex - 1][idx];
  775. if (measure.endsWithLineRepetition()) {
  776. return true;
  777. }
  778. }
  779. return false;
  780. }
  781. /**
  782. * Check if at this [[Measure]] starts a [[Repetition]].
  783. * @returns {boolean}
  784. */
  785. protected thisMeasureBeginsLineRepetition(): boolean {
  786. for (let idx: number = 0, len: number = this.measureList[this.measureListIndex].length; idx < len; ++idx) {
  787. const measure: GraphicalMeasure = this.measureList[this.measureListIndex][idx];
  788. if (measure.beginsWithLineRepetition()) {
  789. return true;
  790. }
  791. }
  792. return false;
  793. }
  794. /**
  795. * Check if a [[Repetition]] starts at the next [[Measure]].
  796. * @returns {boolean}
  797. */
  798. protected nextMeasureBeginsLineRepetition(): boolean {
  799. const nextMeasureIndex: number = this.measureListIndex + 1;
  800. if (nextMeasureIndex >= this.graphicalMusicSheet.ParentMusicSheet.SourceMeasures.length
  801. || !this.measureList[nextMeasureIndex]) {
  802. return false;
  803. }
  804. for (let idx: number = 0, len: number = this.measureList[nextMeasureIndex].length; idx < len; ++idx) {
  805. const measure: GraphicalMeasure = this.measureList[nextMeasureIndex][idx];
  806. if (measure.beginsWithLineRepetition()) {
  807. return true;
  808. }
  809. }
  810. return false;
  811. }
  812. /**
  813. * Check if this [[Measure]] is a [[Repetition]] ending.
  814. * @returns {boolean}
  815. */
  816. protected thisMeasureEndsLineRepetition(): boolean {
  817. for (let idx: number = 0, len: number = this.measureList[this.measureListIndex].length; idx < len; ++idx) {
  818. const measure: GraphicalMeasure = this.measureList[this.measureListIndex][idx];
  819. if (measure.endsWithLineRepetition()) {
  820. return true;
  821. } else if (measure.parentSourceMeasure?.isReducedToMultiRest) {
  822. // check if the last measure in the multiple rest measure sequence has a repeat ending
  823. const sheet: MusicSheet = this.graphicalMusicSheet.ParentMusicSheet;
  824. let currentMultirestMeasure: SourceMeasure = measure.parentSourceMeasure;
  825. const startMeasureIndex: number = sheet.SourceMeasures.indexOf(currentMultirestMeasure);
  826. let currentMultirestMeasureNumber: number = currentMultirestMeasure.multipleRestMeasureNumber;
  827. for (let i: number = startMeasureIndex + 1; i < sheet.SourceMeasures.length; i++) {
  828. const nextMultirestMeasure: SourceMeasure = sheet.SourceMeasures[i];
  829. if (nextMultirestMeasure.multipleRestMeasureNumber >= currentMultirestMeasureNumber) {
  830. currentMultirestMeasure = nextMultirestMeasure;
  831. currentMultirestMeasureNumber = nextMultirestMeasure.multipleRestMeasureNumber;
  832. } else {
  833. break; // end of current multirest chain = last multirest measure
  834. }
  835. }
  836. return currentMultirestMeasure.endsWithLineRepetition();
  837. }
  838. }
  839. return false;
  840. }
  841. /**
  842. * Check if a [[Repetition]] starts at the next [[Measure]].
  843. * @returns {boolean}
  844. */
  845. protected nextMeasureBeginsWordRepetition(): boolean {
  846. const nextMeasureIndex: number = this.measureListIndex + 1;
  847. if (nextMeasureIndex >= this.graphicalMusicSheet.ParentMusicSheet.SourceMeasures.length ||
  848. nextMeasureIndex > this.measureList.length - 1) {
  849. return false;
  850. }
  851. for (let idx: number = 0, len: number = this.measureList[nextMeasureIndex].length; idx < len; ++idx) {
  852. const measure: GraphicalMeasure = this.measureList[nextMeasureIndex][idx];
  853. if (measure.beginsWithWordRepetition()) {
  854. return true;
  855. }
  856. }
  857. return false;
  858. }
  859. /**
  860. * Check if this [[Measure]] is a [[Repetition]] ending.
  861. * @returns {boolean}
  862. */
  863. protected thisMeasureEndsWordRepetition(): boolean {
  864. for (let idx: number = 0, len: number = this.measureList[this.measureListIndex].length; idx < len; ++idx) {
  865. const measure: GraphicalMeasure = this.measureList[this.measureListIndex][idx];
  866. if (measure.endsWithWordRepetition()) {
  867. return true;
  868. }
  869. }
  870. return false;
  871. }
  872. /**
  873. * Check if the next [[Measure]] has a [[KeyInstruction]] change.
  874. * @returns {boolean}
  875. */
  876. protected nextMeasureHasKeyInstructionChange(): boolean {
  877. return this.getNextMeasureKeyInstruction() !== undefined;
  878. }
  879. protected getNextMeasureKeyInstruction(): KeyInstruction {
  880. if (this.measureListIndex < this.measureList.length - 1) {
  881. for (let visIndex: number = 0; visIndex < this.measureList[this.measureListIndex].length; visIndex++) {
  882. const sourceMeasure: SourceMeasure = this.measureList[this.measureListIndex + 1][visIndex]?.parentSourceMeasure;
  883. if (!sourceMeasure) {
  884. return undefined;
  885. }
  886. return sourceMeasure.getKeyInstruction(this.visibleStaffIndices[visIndex]);
  887. }
  888. }
  889. return undefined;
  890. }
  891. /**
  892. * Calculate the X ScalingFactor in order to strech the whole System.
  893. * @param systemFixWidth
  894. * @param systemVarWidth
  895. * @returns {number}
  896. */
  897. protected calculateXScalingFactor(systemFixWidth: number, systemVarWidth: number): number {
  898. if (Math.abs(systemVarWidth - 0) < 0.00001 || Math.abs(systemFixWidth - 0) < 0.00001) {
  899. return 1.0;
  900. }
  901. const currentSystem: MusicSystem = this.currentSystemParams.currentSystem;
  902. const systemEndX: number = currentSystem.StaffLines[0].PositionAndShape.Size.width;
  903. const scalingFactor: number = (systemEndX - systemFixWidth) / systemVarWidth;
  904. return scalingFactor;
  905. }
  906. /**
  907. * Stretch the whole System so that no white space is left at the end.
  908. * @param systemEndsPart
  909. */
  910. protected stretchMusicSystem(systemEndsPart: boolean): void {
  911. let scalingFactor: number = this.calculateXScalingFactor(
  912. this.currentSystemParams.currentSystemFixWidth, this.currentSystemParams.currentSystemVarWidth
  913. );
  914. if (systemEndsPart) {
  915. scalingFactor = Math.min(scalingFactor, this.rules.LastSystemMaxScalingFactor);
  916. }
  917. const currentSystem: MusicSystem = this.currentSystemParams.currentSystem;
  918. for (let visStaffIdx: number = 0, len: number = currentSystem.StaffLines.length; visStaffIdx < len; ++visStaffIdx) {
  919. const staffLine: StaffLine = currentSystem.StaffLines[visStaffIdx];
  920. let currentXPosition: number = 0.0;
  921. for (let measureIndex: number = 0; measureIndex < staffLine.Measures.length; measureIndex++) {
  922. const measure: GraphicalMeasure = staffLine.Measures[measureIndex];
  923. measure.setPositionInStaffline(currentXPosition);
  924. const beginInstructionsWidth: number = measure.beginInstructionsWidth;
  925. // if (measureIndex === 0 && measure.staffEntries) {
  926. // if (!measure.parentSourceMeasure.hasLyrics) {
  927. // beginInstructionsWidth *= 1; // TODO the first measure in a system is always slightly too big. why? try e.g. 0.6
  928. // }
  929. // }
  930. measure.setWidth(beginInstructionsWidth + measure.minimumStaffEntriesWidth * scalingFactor + measure.endInstructionsWidth);
  931. if (measureIndex < this.currentSystemParams.systemMeasures.length) {
  932. const startLine: SystemLinesEnum = this.currentSystemParams.systemMeasures[measureIndex].beginLine;
  933. const lineWidth: number = measure.getLineWidth(SystemLinesEnum.BoldThinDots);
  934. switch (startLine) {
  935. case SystemLinesEnum.BoldThinDots:
  936. let xPosition: number = currentXPosition;
  937. if (measureIndex === 0) {
  938. xPosition = currentXPosition + measure.beginInstructionsWidth - lineWidth;
  939. }
  940. currentSystem.createVerticalLineForMeasure(xPosition, lineWidth, startLine, SystemLinePosition.MeasureBegin, measureIndex, measure);
  941. break;
  942. default:
  943. }
  944. }
  945. measure.staffEntriesScaleFactor = scalingFactor;
  946. measure.layoutSymbols();
  947. const nextMeasureHasRepStartLine: boolean = measureIndex + 1 < this.currentSystemParams.systemMeasures.length
  948. && this.currentSystemParams.systemMeasures[measureIndex + 1].beginLine === SystemLinesEnum.BoldThinDots;
  949. if (!nextMeasureHasRepStartLine) {
  950. let endLine: SystemLinesEnum = SystemLinesEnum.SingleThin;
  951. if (measureIndex < this.currentSystemParams.systemMeasures.length) {
  952. endLine = this.currentSystemParams.systemMeasures[measureIndex].endLine;
  953. }
  954. const lineWidth: number = measure.getLineWidth(endLine);
  955. let xPos: number = measure.PositionAndShape.RelativePosition.x + measure.PositionAndShape.BorderRight - lineWidth;
  956. if (endLine === SystemLinesEnum.DotsBoldBoldDots) {
  957. xPos -= lineWidth / 2;
  958. }
  959. currentSystem.createVerticalLineForMeasure(xPos, lineWidth, endLine, SystemLinePosition.MeasureEnd, measureIndex, measure);
  960. }
  961. currentXPosition = measure.PositionAndShape.RelativePosition.x + measure.PositionAndShape.BorderRight;
  962. }
  963. }
  964. if (systemEndsPart) {
  965. this.decreaseMusicSystemBorders();
  966. }
  967. }
  968. /**
  969. * If the last [[MusicSystem]] doesn't need stretching, then this method decreases the System's Width,
  970. * the [[StaffLine]]'s Width and the 5 [[StaffLine]]s length.
  971. */
  972. protected decreaseMusicSystemBorders(): void {
  973. const currentSystem: MusicSystem = this.currentSystemParams.currentSystem;
  974. const bb: BoundingBox = CollectionUtil.last(currentSystem.StaffLines[0].Measures).PositionAndShape;
  975. const width: number = bb.RelativePosition.x + bb.Size.width;
  976. for (let idx: number = 0, len: number = currentSystem.StaffLines.length; idx < len; ++idx) {
  977. const staffLine: StaffLine = currentSystem.StaffLines[idx];
  978. staffLine.PositionAndShape.BorderRight = width;
  979. for (let idx2: number = 0, len2: number = staffLine.StaffLines.length; idx2 < len2; ++idx2) {
  980. const graphicalLine: GraphicalLine = staffLine.StaffLines[idx2];
  981. graphicalLine.End = new PointF2D(width, graphicalLine.End.y);
  982. }
  983. }
  984. currentSystem.PositionAndShape.BorderRight = width + this.currentSystemParams.maxLabelLength + this.rules.SystemLabelsRightMargin;
  985. }
  986. /**
  987. * This method updates the System's StaffLine's RelativePosition (starting from the given index).
  988. * @param musicSystem
  989. * @param index
  990. * @param value
  991. */
  992. protected updateStaffLinesRelativePosition(musicSystem: MusicSystem, index: number, value: number): void {
  993. for (let i: number = index; i < musicSystem.StaffLines.length; i++) {
  994. musicSystem.StaffLines[i].PositionAndShape.RelativePosition.y = value;
  995. }
  996. musicSystem.PositionAndShape.BorderBottom += value;
  997. }
  998. /**
  999. * Create a new [[GraphicalMusicPage]]
  1000. * (for now only one long page is used per music sheet, as we scroll down and have no page flips)
  1001. * @returns {GraphicalMusicPage}
  1002. */
  1003. protected createMusicPage(): GraphicalMusicPage {
  1004. // const previousPage: GraphicalMusicPage = this.graphicalMusicSheet.MusicPages.last();
  1005. // const previousSizeY: number = previousPage ? previousPage.PositionAndShape.Size.height : 0;
  1006. const page: GraphicalMusicPage = new GraphicalMusicPage(this.graphicalMusicSheet);
  1007. this.graphicalMusicSheet.MusicPages.push(page);
  1008. page.PageNumber = this.graphicalMusicSheet.MusicPages.length; // caution: page number = page index + 1
  1009. page.PositionAndShape.BorderLeft = 0.0;
  1010. page.PositionAndShape.BorderRight = this.graphicalMusicSheet.ParentMusicSheet.pageWidth;
  1011. page.PositionAndShape.BorderTop = 0.0;
  1012. page.PositionAndShape.BorderBottom = this.rules.PageHeight;
  1013. page.PositionAndShape.RelativePosition = new PointF2D(0.0, 0.0);
  1014. return page;
  1015. }
  1016. protected addSystemToPage(page: GraphicalMusicPage, system: MusicSystem): void {
  1017. page.MusicSystems.push(system);
  1018. system.Parent = page;
  1019. }
  1020. /**
  1021. * This method checks the distances between any two consecutive StaffLines of a System and if needed, shifts the lower one down.
  1022. * @param musicSystem
  1023. */
  1024. protected optimizeDistanceBetweenStaffLines(musicSystem: MusicSystem): void {
  1025. // don't perform any y-spacing in case of a StaffEntryLink (in both StaffLines)
  1026. if (!musicSystem.checkStaffEntriesForStaffEntryLink()) {
  1027. for (let i: number = 0; i < musicSystem.StaffLines.length - 1; i++) {
  1028. const upperBottomLine: number[] = musicSystem.StaffLines[i].BottomLine;
  1029. const lowerSkyLine: number[] = musicSystem.StaffLines[i + 1].SkyLine;
  1030. // 1. Find maximum required space for sky bottom line touching each other
  1031. let maxDistance: number = 0;
  1032. for (let j: number = 0; j < upperBottomLine.length; j++) {
  1033. const bottomLineValue: number = upperBottomLine[j];
  1034. // look at a range of +/- 2 Units to also ensure that objects are also not too close in x-direction:
  1035. const startIdx: number = Math.max(0, j - 6);
  1036. const endIdx: number = Math.min(lowerSkyLine.length - 1, j + 6);
  1037. let skylineValue: number = 0;
  1038. for (let lowerIdx: number = startIdx; lowerIdx <= endIdx; lowerIdx++) {
  1039. skylineValue = Math.min(skylineValue, lowerSkyLine[lowerIdx]);
  1040. }
  1041. const distance: number = bottomLineValue - skylineValue;
  1042. maxDistance = Math.max(distance, maxDistance);
  1043. }
  1044. // 2. Add user defined distance between sky bottom line
  1045. maxDistance += this.rules.MinSkyBottomDistBetweenStaves;
  1046. // 3. Take the maximum between previous value and user defined value for staff line minimum distance
  1047. maxDistance = Math.max(maxDistance, this.rules.StaffHeight + this.rules.MinimumStaffLineDistance);
  1048. const lowerStafflineYPos: number = maxDistance + musicSystem.StaffLines[i].PositionAndShape.RelativePosition.y;
  1049. this.updateStaffLinesRelativePosition(musicSystem, i + 1, lowerStafflineYPos);
  1050. }
  1051. }
  1052. const firstStaffLine: StaffLine = musicSystem.StaffLines[0];
  1053. musicSystem.PositionAndShape.BorderTop = firstStaffLine.PositionAndShape.RelativePosition.y + firstStaffLine.PositionAndShape.BorderTop;
  1054. const lastStaffLine: StaffLine = musicSystem.StaffLines[musicSystem.StaffLines.length - 1];
  1055. musicSystem.PositionAndShape.BorderBottom = lastStaffLine.PositionAndShape.RelativePosition.y + lastStaffLine.PositionAndShape.BorderBottom;
  1056. }
  1057. /** Calculates the relative Positions of all MusicSystems.
  1058. *
  1059. */
  1060. protected calculateMusicSystemsRelativePositions(): void {
  1061. let currentPage: GraphicalMusicPage = this.createMusicPage();
  1062. let currentYPosition: number = 0;
  1063. // xPosition is always fixed
  1064. let currentSystem: MusicSystem = this.musicSystems[0];
  1065. let timesPageCouldntFitSingleSystem: number = 0;
  1066. for (let i: number = 0; i < this.musicSystems.length; i++) {
  1067. currentSystem = this.musicSystems[i];
  1068. if (currentPage.MusicSystems.length === 0) {
  1069. // if this is the first system on the current page:
  1070. // take top margins into account
  1071. this.addSystemToPage(currentPage, currentSystem);
  1072. if (this.rules.CompactMode) {
  1073. currentYPosition = this.rules.PageTopMarginNarrow;
  1074. } else {
  1075. currentYPosition = this.rules.PageTopMargin;
  1076. }
  1077. if (this.graphicalMusicSheet.MusicPages.length === 1) {
  1078. /*
  1079. Only need this in the event that lyricist or composer text intersects with title,
  1080. which seems exceedingly rare.
  1081. Leaving here just in case for future needs.
  1082. Prefer to use skyline calculator in MusicSheetCalculator.calculatePageLabels
  1083. let maxLineCount: number = this.graphicalMusicSheet.Composer?.TextLines?.length;
  1084. let maxFontHeight: number = this.graphicalMusicSheet.Composer?.Label?.fontHeight;
  1085. let lyricistLineCount: number = this.graphicalMusicSheet.Lyricist?.TextLines?.length;
  1086. let lyricistFontHeight: number = this.graphicalMusicSheet.Lyricist?.Label?.fontHeight;
  1087. maxLineCount = maxLineCount ? maxLineCount : 0;
  1088. maxFontHeight = maxFontHeight ? maxFontHeight : 0;
  1089. lyricistLineCount = lyricistLineCount ? lyricistLineCount : 0;
  1090. lyricistFontHeight = lyricistFontHeight ? lyricistFontHeight : 0;
  1091. let maxHeight: number = maxLineCount * maxFontHeight;
  1092. const totalLyricist: number = lyricistLineCount * lyricistFontHeight;
  1093. if (totalLyricist > maxHeight) {
  1094. maxLineCount = lyricistLineCount;
  1095. maxFontHeight = lyricistFontHeight;
  1096. maxHeight = totalLyricist;
  1097. } */
  1098. if (this.rules.RenderTitle) {
  1099. // if it is the first System on the FIRST page: Add Title height and gap-distance
  1100. currentYPosition += this.rules.TitleTopDistance + this.rules.SheetTitleHeight +
  1101. this.rules.TitleBottomDistance;
  1102. }
  1103. /*
  1104. see comment above - only needed for rare case of composer/lyricist being
  1105. wide enough to be below the title (or title wide enough to be above)
  1106. if (maxLineCount > 2) {
  1107. currentYPosition += maxFontHeight * (maxLineCount - 2);
  1108. }*/
  1109. }
  1110. // now add the border-top: everything that stands out above the staffline:
  1111. // note: this is unnecessary. We have PageTopMargin and TitleBottomDistance for this.
  1112. // also, this creates a sudden margin spike from PageTopMargin = 0.1 to PageTopMargin = 0.
  1113. // if (!this.rules.CompactMode) { // don't add extra margins/borders in compact mode
  1114. // if (this.rules.PageTopMargin > 0) { // don't add extra margins with PageTopMargin == 0
  1115. // currentYPosition += -currentSystem.PositionAndShape.BorderTop;
  1116. // }
  1117. // }
  1118. const relativePosition: PointF2D = new PointF2D(this.rules.PageLeftMargin + this.rules.SystemLeftMargin,
  1119. currentYPosition);
  1120. currentSystem.PositionAndShape.RelativePosition = relativePosition;
  1121. // check if the first system doesn't even fit on the page -> would lead to truncation at bottom end:
  1122. if (currentYPosition + currentSystem.PositionAndShape.BorderBottom > this.rules.PageHeight - this.rules.PageBottomMargin) {
  1123. // can't fit single system on page, maybe PageFormat too small
  1124. timesPageCouldntFitSingleSystem++;
  1125. if (timesPageCouldntFitSingleSystem <= 4) { // only warn once with detailed info
  1126. console.log(`warning: could not fit a single system on page ${currentPage.PageNumber}` +
  1127. ` and measure number ${currentSystem.GraphicalMeasures[0][0].MeasureNumber}.
  1128. The PageFormat may be too small for this sheet."
  1129. Will not give further warnings for all pages, only total.`
  1130. );
  1131. }
  1132. }
  1133. } else {
  1134. // if this is not the first system on the page:
  1135. // find optimum distance between Systems
  1136. const previousSystem: MusicSystem = this.musicSystems[i - 1];
  1137. const prevSystemLastStaffline: StaffLine = previousSystem.StaffLines[previousSystem.StaffLines.length - 1];
  1138. const prevSystemLastStaffLineBB: BoundingBox = prevSystemLastStaffline.PositionAndShape;
  1139. let distance: number = this.findRequiredDistanceWithSkyBottomLine(previousSystem, currentSystem);
  1140. // make sure the optical distance is the user-defined min distance:
  1141. distance += this.rules.MinSkyBottomDistBetweenSystems;
  1142. distance = Math.max(distance, this.rules.MinimumDistanceBetweenSystems + prevSystemLastStaffline.StaffHeight);
  1143. const newYPosition: number = currentYPosition +
  1144. prevSystemLastStaffLineBB.RelativePosition.y +
  1145. distance;
  1146. // calculate the needed height for placing the current system on the page,
  1147. // to see if it still fits:
  1148. const currSystemBottomYPos: number = newYPosition +
  1149. currentSystem.PositionAndShape.BorderMarginBottom;
  1150. const doXmlPageBreak: boolean = this.rules.NewPageAtXMLNewPageAttribute && previousSystem.breaksPage;
  1151. if (!doXmlPageBreak &&
  1152. (currSystemBottomYPos < this.rules.PageHeight - this.rules.PageBottomMargin)) {
  1153. // enough space on this page:
  1154. this.addSystemToPage(currentPage, currentSystem);
  1155. currentYPosition = newYPosition;
  1156. const relativePosition: PointF2D = new PointF2D(this.rules.PageLeftMargin + this.rules.SystemLeftMargin,
  1157. currentYPosition);
  1158. currentSystem.PositionAndShape.RelativePosition = relativePosition;
  1159. } else {
  1160. // new page needed:
  1161. currentPage = this.createMusicPage();
  1162. // re-check this system again:
  1163. i -= 1;
  1164. continue;
  1165. }
  1166. }
  1167. }
  1168. if (timesPageCouldntFitSingleSystem > 0) {
  1169. console.log(`total amount of pages that couldn't fit a single music system: ${timesPageCouldntFitSingleSystem} of ${currentPage.PageNumber}`);
  1170. }
  1171. }
  1172. /**
  1173. * Finds the minimum required distance between two systems
  1174. * with the help of the sky- and bottom lines
  1175. * @param upperSystem
  1176. * @param lowerSystem
  1177. */
  1178. private findRequiredDistanceWithSkyBottomLine(upperSystem: MusicSystem, lowerSystem: MusicSystem): number {
  1179. const upperSystemLastStaffLine: StaffLine = upperSystem.StaffLines[upperSystem.StaffLines.length - 1];
  1180. const lowerSystemFirstStaffLine: StaffLine = lowerSystem.StaffLines[0];
  1181. const upperBottomLineArray: number[] = upperSystemLastStaffLine.BottomLine;
  1182. const lowerSkyLineArray: number[] = lowerSystemFirstStaffLine.SkyLine;
  1183. const upperStaffLineBB: BoundingBox = upperSystemLastStaffLine.PositionAndShape;
  1184. const lowerStaffLineBB: BoundingBox = lowerSystemFirstStaffLine.PositionAndShape;
  1185. const skylinePixelWidth: number = 1 / this.rules.SamplingUnit;
  1186. // Find maximum required space for sky and bottom line touching each other
  1187. let maxDistance: number = 0;
  1188. for (let upperIdx: number = 0; upperIdx < upperBottomLineArray.length; upperIdx++) {
  1189. const bottomLineValue: number = upperBottomLineArray[upperIdx];
  1190. // find index of the same x-position in lower skyline:
  1191. const lowerCenterIdx: number = upperIdx +
  1192. Math.round((upperStaffLineBB.RelativePosition.x - lowerStaffLineBB.RelativePosition.x) * skylinePixelWidth);
  1193. if (lowerCenterIdx < 0) {
  1194. // should actually not happen..
  1195. continue;
  1196. }
  1197. if (lowerCenterIdx >= lowerSkyLineArray.length) {
  1198. // lower system ends earlier x-wise than upper system (e.g. at last system, if it is not stretched)
  1199. break;
  1200. }
  1201. // look at a range of +/- 2 Units to also ensure that objects are also not too close in x-direction:
  1202. const startIdx: number = Math.max(0, lowerCenterIdx - 6);
  1203. const endIdx: number = Math.min(lowerSkyLineArray.length - 1, lowerCenterIdx + 6);
  1204. let skylineValue: number = 0;
  1205. for (let lowerIdx: number = startIdx; lowerIdx <= endIdx; lowerIdx++) {
  1206. skylineValue = Math.min(skylineValue, lowerSkyLineArray[lowerIdx]);
  1207. }
  1208. const distance: number = bottomLineValue - skylineValue;
  1209. maxDistance = Math.max(distance, maxDistance);
  1210. }
  1211. if (maxDistance === 0) {
  1212. // can only happen when the bottom- and skyline have no x-overlap at all:
  1213. // fall back to borders:
  1214. maxDistance = upperStaffLineBB.BorderBottom - lowerStaffLineBB.BorderTop;
  1215. }
  1216. return maxDistance;
  1217. }
  1218. }
  1219. export class SystemBuildParameters {
  1220. public currentSystem: MusicSystem;
  1221. public systemMeasures: MeasureBuildParameters[] = [];
  1222. public systemMeasureIndex: number = 0;
  1223. public currentWidth: number = 0;
  1224. public currentSystemFixWidth: number = 0;
  1225. public currentSystemVarWidth: number = 0;
  1226. public maxLabelLength: number = 0;
  1227. public IsSystemStartMeasure(): boolean {
  1228. return this.systemMeasureIndex === 0;
  1229. }
  1230. }
  1231. export class MeasureBuildParameters {
  1232. public beginLine: SystemLinesEnum;
  1233. public endLine: SystemLinesEnum;
  1234. }