MusicSheetDrawer.ts 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540
  1. import {EngravingRules} from "./EngravingRules";
  2. import {ITextMeasurer} from "../Interfaces/ITextMeasurer";
  3. import {GraphicalMusicSheet} from "./GraphicalMusicSheet";
  4. import {BoundingBox} from "./BoundingBox";
  5. import {GraphicalLayers, OutlineAndFillStyleEnum} from "./DrawingEnums";
  6. import {DrawingParameters} from "./DrawingParameters";
  7. import {GraphicalLine} from "./GraphicalLine";
  8. import {RectangleF2D} from "../../Common/DataObjects/RectangleF2D";
  9. import {PointF2D} from "../../Common/DataObjects/PointF2D";
  10. import {GraphicalRectangle} from "./GraphicalRectangle";
  11. import {GraphicalLabel} from "./GraphicalLabel";
  12. import {Label} from "../Label";
  13. import {TextAlignment} from "../../Common/Enums/TextAlignment";
  14. import {ArgumentOutOfRangeException} from "../Exceptions";
  15. import {SelectionStartSymbol} from "./SelectionStartSymbol";
  16. import {SelectionEndSymbol} from "./SelectionEndSymbol";
  17. import {MusicSystem} from "./MusicSystem";
  18. import {StaffMeasure} from "./StaffMeasure";
  19. import {StaffLine} from "./StaffLine";
  20. import {SystemLine} from "./SystemLine";
  21. import {MusicSymbol} from "./MusicSymbol";
  22. import {GraphicalMusicPage} from "./GraphicalMusicPage";
  23. import {Instrument} from "../Instrument";
  24. import {MusicSymbolDrawingStyle, PhonicScoreModes} from "./DrawingMode";
  25. import {GraphicalObject} from "./GraphicalObject";
  26. /**
  27. * Draw a [[GraphicalMusicSheet]] (through the .drawSheet method)
  28. *
  29. * The drawing is implemented with a top-down approach, starting from a music sheet, going through pages, systems, staffs...
  30. * ... and ending in notes, beams, accidentals and other symbols.
  31. * It's worth to say, that this class just draws the symbols and graphical elements, using the positions that have been computed before.
  32. * But in any case, some of these previous positioning algorithms need the sizes of the concrete symbols (NoteHeads, sharps, flats, keys...).
  33. * Therefore, there are some static functions on the 'Bounding Boxes' section used to compute these symbol boxes at the
  34. * beginning for the later use in positioning algorithms.
  35. *
  36. * This class also includes the resizing and positioning of the symbols due to user interaction like zooming or panning.
  37. */
  38. export abstract class MusicSheetDrawer {
  39. public drawingParameters: DrawingParameters = new DrawingParameters();
  40. public splitScreenLineColor: number;
  41. public midiPlaybackAvailable: boolean;
  42. protected rules: EngravingRules;
  43. protected graphicalMusicSheet: GraphicalMusicSheet;
  44. protected textMeasurer: ITextMeasurer;
  45. private phonicScoreMode: PhonicScoreModes = PhonicScoreModes.Manual;
  46. constructor(textMeasurer: ITextMeasurer,
  47. isPreviewImageDrawer: boolean = false) {
  48. this.textMeasurer = textMeasurer;
  49. this.splitScreenLineColor = -1;
  50. if (isPreviewImageDrawer) {
  51. this.drawingParameters.setForThumbmail();
  52. } else {
  53. this.drawingParameters.setForAllOn();
  54. }
  55. }
  56. public set Mode(value: PhonicScoreModes) {
  57. this.phonicScoreMode = value;
  58. }
  59. public drawSheet(graphicalMusicSheet: GraphicalMusicSheet): void {
  60. this.graphicalMusicSheet = graphicalMusicSheet;
  61. this.rules = graphicalMusicSheet.ParentMusicSheet.Rules;
  62. this.drawSplitScreenLine();
  63. if (this.drawingParameters.drawCursors) {
  64. for (const line of graphicalMusicSheet.Cursors) {
  65. const psi: BoundingBox = new BoundingBox(line);
  66. psi.AbsolutePosition = line.Start;
  67. psi.BorderBottom = line.End.y - line.Start.y;
  68. psi.BorderRight = line.Width / 2.0;
  69. psi.BorderLeft = -line.Width / 2.0;
  70. if (this.isVisible(psi)) {
  71. this.drawLineAsVerticalRectangle(line, <number>GraphicalLayers.Cursor);
  72. }
  73. }
  74. }
  75. // Draw the vertical ScrollIndicator
  76. if (this.drawingParameters.drawScrollIndicator) {
  77. this.drawScrollIndicator();
  78. }
  79. // Draw all the pages
  80. for (const page of this.graphicalMusicSheet.MusicPages) {
  81. this.drawPage(page);
  82. }
  83. }
  84. public drawLineAsHorizontalRectangle(line: GraphicalLine, layer: number): void {
  85. let rectangle: RectangleF2D = new RectangleF2D(line.Start.x, line.End.y - line.Width / 2, line.End.x - line.Start.x, line.Width);
  86. rectangle = this.applyScreenTransformationForRect(rectangle);
  87. this.renderRectangle(rectangle, layer, line.styleId);
  88. }
  89. public drawLineAsVerticalRectangle(line: GraphicalLine, layer: number): void {
  90. const lineStart: PointF2D = line.Start;
  91. const lineWidth: number = line.Width;
  92. let rectangle: RectangleF2D = new RectangleF2D(lineStart.x - lineWidth / 2, lineStart.y, lineWidth, line.End.y - lineStart.y);
  93. rectangle = this.applyScreenTransformationForRect(rectangle);
  94. this.renderRectangle(rectangle, layer, line.styleId);
  95. }
  96. public drawLineAsHorizontalRectangleWithOffset(line: GraphicalLine, offset: PointF2D, layer: number): void {
  97. const start: PointF2D = new PointF2D(line.Start.x + offset.x, line.Start.y + offset.y);
  98. const end: PointF2D = new PointF2D(line.End.x + offset.x, line.End.y + offset.y);
  99. const width: number = line.Width;
  100. let rectangle: RectangleF2D = new RectangleF2D(start.x, end.y - width / 2, end.x - start.x, width);
  101. rectangle = this.applyScreenTransformationForRect(rectangle);
  102. this.renderRectangle(rectangle, layer, line.styleId);
  103. }
  104. public drawLineAsVerticalRectangleWithOffset(line: GraphicalLine, offset: PointF2D, layer: number): void {
  105. const start: PointF2D = new PointF2D(line.Start.x + offset.x, line.Start.y + offset.y);
  106. const end: PointF2D = new PointF2D(line.End.x + offset.x, line.End.y + offset.y);
  107. const width: number = line.Width;
  108. let rectangle: RectangleF2D = new RectangleF2D(start.x, start.y, width, end.y - start.y);
  109. rectangle = this.applyScreenTransformationForRect(rectangle);
  110. this.renderRectangle(rectangle, layer, line.styleId);
  111. }
  112. public drawRectangle(rect: GraphicalRectangle, layer: number): void {
  113. const psi: BoundingBox = rect.PositionAndShape;
  114. let rectangle: RectangleF2D = new RectangleF2D(psi.AbsolutePosition.x, psi.AbsolutePosition.y, psi.BorderRight, psi.BorderBottom);
  115. rectangle = this.applyScreenTransformationForRect(rectangle);
  116. this.renderRectangle(rectangle, layer, <number>rect.style);
  117. }
  118. public calculatePixelDistance(unitDistance: number): number {
  119. throw new Error("not implemented");
  120. }
  121. public drawLabel(graphicalLabel: GraphicalLabel, layer: number): void {
  122. if (!this.isVisible(graphicalLabel.PositionAndShape)) {
  123. return;
  124. }
  125. const label: Label = graphicalLabel.Label;
  126. if (label.text.trim() === "") {
  127. return;
  128. }
  129. const screenPosition: PointF2D = this.applyScreenTransformation(graphicalLabel.PositionAndShape.AbsolutePosition);
  130. const heightInPixel: number = this.calculatePixelDistance(label.fontHeight);
  131. const widthInPixel: number = heightInPixel * this.textMeasurer.computeTextWidthToHeightRatio(label.text, label.font, label.fontStyle);
  132. const bitmapWidth: number = <number>Math.ceil(widthInPixel);
  133. const bitmapHeight: number = <number>Math.ceil(heightInPixel * 1.2);
  134. switch (label.textAlignment) {
  135. case TextAlignment.LeftTop:
  136. break;
  137. case TextAlignment.LeftCenter:
  138. screenPosition.y -= <number>bitmapHeight / 2;
  139. break;
  140. case TextAlignment.LeftBottom:
  141. screenPosition.y -= bitmapHeight;
  142. break;
  143. case TextAlignment.CenterTop:
  144. screenPosition.x -= <number>bitmapWidth / 2;
  145. break;
  146. case TextAlignment.CenterCenter:
  147. screenPosition.x -= <number>bitmapWidth / 2;
  148. screenPosition.y -= <number>bitmapHeight / 2;
  149. break;
  150. case TextAlignment.CenterBottom:
  151. screenPosition.x -= <number>bitmapWidth / 2;
  152. screenPosition.y -= bitmapHeight;
  153. break;
  154. case TextAlignment.RightTop:
  155. screenPosition.x -= bitmapWidth;
  156. break;
  157. case TextAlignment.RightCenter:
  158. screenPosition.x -= bitmapWidth;
  159. screenPosition.y -= <number>bitmapHeight / 2;
  160. break;
  161. case TextAlignment.RightBottom:
  162. screenPosition.x -= bitmapWidth;
  163. screenPosition.y -= bitmapHeight;
  164. break;
  165. default:
  166. throw new ArgumentOutOfRangeException("");
  167. }
  168. this.renderLabel(graphicalLabel, layer, bitmapWidth, bitmapHeight, heightInPixel, screenPosition);
  169. }
  170. protected applyScreenTransformation(point: PointF2D): PointF2D {
  171. throw new Error("not implemented");
  172. }
  173. protected applyScreenTransformations(points: PointF2D[]): PointF2D[] {
  174. const transformedPoints: PointF2D[] = [];
  175. for (const point of points) {
  176. transformedPoints.push(this.applyScreenTransformation(point));
  177. }
  178. return transformedPoints;
  179. }
  180. protected applyScreenTransformationForRect(rectangle: RectangleF2D): RectangleF2D {
  181. throw new Error("not implemented");
  182. }
  183. protected drawSplitScreenLine(): void {
  184. // empty
  185. }
  186. protected renderRectangle(rectangle: RectangleF2D, layer: number, styleId: number, alpha: number = 1): void {
  187. throw new Error("not implemented");
  188. }
  189. protected drawScrollIndicator(): void {
  190. // empty
  191. }
  192. protected drawSelectionStartSymbol(symbol: SelectionStartSymbol): void {
  193. // empty
  194. }
  195. protected drawSelectionEndSymbol(symbol: SelectionEndSymbol): void {
  196. // empty
  197. }
  198. protected renderLabel(graphicalLabel: GraphicalLabel, layer: number, bitmapWidth: number,
  199. bitmapHeight: number, heightInPixel: number, screenPosition: PointF2D): void {
  200. throw new Error("not implemented");
  201. }
  202. protected renderSystemToScreen(system: MusicSystem, systemBoundingBoxInPixels: RectangleF2D,
  203. absBoundingRectWithMargin: RectangleF2D): void {
  204. // empty
  205. }
  206. protected drawMeasure(measure: StaffMeasure): void {
  207. throw new Error("not implemented");
  208. }
  209. protected drawSkyLine(staffLine: StaffLine): void {
  210. // empty
  211. }
  212. protected drawBottomLine(staffLine: StaffLine): void {
  213. // empty
  214. }
  215. protected drawInstrumentBrace(brace: GraphicalObject, system: MusicSystem): void {
  216. // empty
  217. }
  218. protected drawGroupBracket(bracket: GraphicalObject, system: MusicSystem): void {
  219. // empty
  220. }
  221. protected isVisible(psi: BoundingBox): boolean {
  222. return true;
  223. }
  224. protected drawMusicSystem(system: MusicSystem): void {
  225. const absBoundingRectWithMargin: RectangleF2D = this.getSystemAbsBoundingRect(system);
  226. const systemBoundingBoxInPixels: RectangleF2D = this.getSytemBoundingBoxInPixels(absBoundingRectWithMargin);
  227. this.drawMusicSystemComponents(system, systemBoundingBoxInPixels, absBoundingRectWithMargin);
  228. }
  229. protected getSytemBoundingBoxInPixels(absBoundingRectWithMargin: RectangleF2D): RectangleF2D {
  230. const systemBoundingBoxInPixels: RectangleF2D = this.applyScreenTransformationForRect(absBoundingRectWithMargin);
  231. systemBoundingBoxInPixels.x = Math.round(systemBoundingBoxInPixels.x);
  232. systemBoundingBoxInPixels.y = Math.round(systemBoundingBoxInPixels.y);
  233. return systemBoundingBoxInPixels;
  234. }
  235. protected getSystemAbsBoundingRect(system: MusicSystem): RectangleF2D {
  236. const relBoundingRect: RectangleF2D = system.PositionAndShape.BoundingRectangle;
  237. const absBoundingRectWithMargin: RectangleF2D = new RectangleF2D(
  238. system.PositionAndShape.AbsolutePosition.x + system.PositionAndShape.BorderLeft - 1,
  239. system.PositionAndShape.AbsolutePosition.y + system.PositionAndShape.BorderTop - 1,
  240. (relBoundingRect.width + 6), (relBoundingRect.height + 2)
  241. );
  242. return absBoundingRectWithMargin;
  243. }
  244. protected drawMusicSystemComponents(musicSystem: MusicSystem, systemBoundingBoxInPixels: RectangleF2D,
  245. absBoundingRectWithMargin: RectangleF2D): void {
  246. const selectStartSymb: SelectionStartSymbol = this.graphicalMusicSheet.SelectionStartSymbol;
  247. const selectEndSymb: SelectionEndSymbol = this.graphicalMusicSheet.SelectionEndSymbol;
  248. if (this.drawingParameters.drawSelectionStartSymbol) {
  249. if (selectStartSymb !== undefined && this.isVisible(selectStartSymb.PositionAndShape)) {
  250. this.drawSelectionStartSymbol(selectStartSymb);
  251. }
  252. }
  253. if (this.drawingParameters.drawSelectionEndSymbol) {
  254. if (selectEndSymb !== undefined && this.isVisible(selectEndSymb.PositionAndShape)) {
  255. this.drawSelectionEndSymbol(selectEndSymb);
  256. }
  257. }
  258. for (const staffLine of musicSystem.StaffLines) {
  259. this.drawStaffLine(staffLine);
  260. }
  261. for (const systemLine of musicSystem.SystemLines) {
  262. this.drawSystemLineObject(systemLine);
  263. }
  264. if (musicSystem === musicSystem.Parent.MusicSystems[0] && musicSystem.Parent === musicSystem.Parent.Parent.MusicPages[0]) {
  265. for (const label of musicSystem.Labels) {
  266. this.drawLabel(label, <number>GraphicalLayers.Notes);
  267. }
  268. }
  269. for (const bracket of musicSystem.InstrumentBrackets) {
  270. this.drawInstrumentBrace(bracket, musicSystem);
  271. }
  272. for (const bracket of musicSystem.GroupBrackets) {
  273. this.drawGroupBracket(bracket, musicSystem);
  274. }
  275. if (!this.leadSheet) {
  276. for (const measureNumberLabel of musicSystem.MeasureNumberLabels) {
  277. this.drawLabel(measureNumberLabel, <number>GraphicalLayers.Notes);
  278. }
  279. }
  280. for (const staffLine of musicSystem.StaffLines) {
  281. this.drawStaffLineSymbols(staffLine);
  282. }
  283. if (this.drawingParameters.drawMarkedAreas) {
  284. this.drawMarkedAreas(musicSystem);
  285. }
  286. if (this.drawingParameters.drawComments) {
  287. this.drawComment(musicSystem);
  288. }
  289. }
  290. protected activateSystemRendering(systemId: number, absBoundingRect: RectangleF2D,
  291. systemBoundingBoxInPixels: RectangleF2D, createNewImage: boolean): boolean {
  292. return true;
  293. }
  294. protected drawSystemLineObject(systemLine: SystemLine): void {
  295. // empty
  296. }
  297. protected drawStaffLine(staffLine: StaffLine): void {
  298. for (const measure of staffLine.Measures) {
  299. this.drawMeasure(measure);
  300. }
  301. if (staffLine.LyricsDashes.length > 0) {
  302. this.drawDashes(staffLine.LyricsDashes);
  303. }
  304. this.drawOctaveShifts(staffLine);
  305. this.drawInstantaniousDynamic(staffLine);
  306. }
  307. /**
  308. * Draw all dashes to the canvas
  309. * @param lyricsDashes Array of lyric dashes to be drawn
  310. * @param layer Number of the layer that the lyrics should be drawn in
  311. */
  312. protected drawDashes(lyricsDashes: GraphicalLabel[]): void {
  313. lyricsDashes.forEach(dash => this.drawLabel(dash, <number>GraphicalLayers.Notes));
  314. }
  315. // protected drawSlur(slur: GraphicalSlur, abs: PointF2D): void {
  316. //
  317. // }
  318. protected drawOctaveShifts(staffLine: StaffLine): void {
  319. return;
  320. }
  321. protected drawStaffLines(staffLine: StaffLine): void {
  322. if (staffLine.StaffLines !== undefined) {
  323. const position: PointF2D = staffLine.PositionAndShape.AbsolutePosition;
  324. for (let i: number = 0; i < 5; i++) {
  325. this.drawLineAsHorizontalRectangleWithOffset(staffLine.StaffLines[i], position, <number>GraphicalLayers.Notes);
  326. }
  327. }
  328. }
  329. // protected drawEnding(ending: GraphicalRepetitionEnding, absolutePosition: PointF2D): void {
  330. // if (undefined !== ending.Left)
  331. // drawLineAsVerticalRectangle(ending.Left, absolutePosition, <number>GraphicalLayers.Notes);
  332. // this.drawLineAsHorizontalRectangle(ending.Top, absolutePosition, <number>GraphicalLayers.Notes);
  333. // if (undefined !== ending.Right)
  334. // drawLineAsVerticalRectangle(ending.Right, absolutePosition, <number>GraphicalLayers.Notes);
  335. // this.drawLabel(ending.Label, <number>GraphicalLayers.Notes);
  336. // }
  337. protected drawInstantaniousDynamic(staffline: StaffLine): void {
  338. // expression.ExpressionSymbols.forEach(function (expressionSymbol) {
  339. // let position: PointF2D = expressionSymbol.PositionAndShape.AbsolutePosition;
  340. // let symbol: MusicSymbol = expressionSymbol.GetSymbol;
  341. // drawSymbol(symbol, MusicSymbolDrawingStyle.Normal, position);
  342. // });
  343. }
  344. // protected drawContinuousDynamic(expression: GraphicalContinuousDynamicExpression,
  345. // absolute: PointF2D): void {
  346. // throw new Error("not implemented");
  347. // }
  348. protected drawSymbol(symbol: MusicSymbol, symbolStyle: MusicSymbolDrawingStyle, position: PointF2D,
  349. scalingFactor: number = 1, layer: number = <number>GraphicalLayers.Notes): void {
  350. //empty
  351. }
  352. protected get leadSheet(): boolean {
  353. return this.graphicalMusicSheet.LeadSheet;
  354. }
  355. protected set leadSheet(value: boolean) {
  356. this.graphicalMusicSheet.LeadSheet = value;
  357. }
  358. private drawPage(page: GraphicalMusicPage): void {
  359. if (!this.isVisible(page.PositionAndShape)) {
  360. return;
  361. }
  362. for (const system of page.MusicSystems) {
  363. if (this.isVisible(system.PositionAndShape)) {
  364. this.drawMusicSystem(system);
  365. }
  366. }
  367. if (page === page.Parent.MusicPages[0]) {
  368. for (const label of page.Labels) {
  369. this.drawLabel(label, <number>GraphicalLayers.Notes);
  370. }
  371. }
  372. // Draw bounding boxes for debug purposes. This has to be at the end because only
  373. // then all the calculations and recalculations are done
  374. if (process.env.DRAW_BOUNDING_BOX_ELEMENT) {
  375. this.drawBoundingBoxes(page.PositionAndShape, 0, process.env.DRAW_BOUNDING_BOX_ELEMENT);
  376. }
  377. }
  378. /**
  379. * Draw bounding boxes aroung GraphicalObjects
  380. * @param startBox Bounding Box that is used as a staring point to recursively go through all child elements
  381. * @param layer Layer to draw to
  382. * @param type Type of element to show bounding boxes for as string.
  383. */
  384. private drawBoundingBoxes(startBox: BoundingBox, layer: number = 0, type: string = "all"): void {
  385. const dataObjectString: string = (startBox.DataObject.constructor as any).name;
  386. if (startBox.BoundingRectangle !== undefined && (dataObjectString === type || type === "all")) {
  387. const relBoundingRect: RectangleF2D = startBox.BoundingRectangle;
  388. let tmpRect: RectangleF2D = new RectangleF2D(startBox.AbsolutePosition.x + startBox.BorderLeft,
  389. startBox.AbsolutePosition.y + startBox.BorderTop ,
  390. (relBoundingRect.width + 0), (relBoundingRect.height + 0));
  391. tmpRect = this.applyScreenTransformationForRect(tmpRect);
  392. this.renderRectangle(tmpRect, <number>GraphicalLayers.Background, layer, 0.5);
  393. this.renderLabel(new GraphicalLabel(new Label(dataObjectString), 1.2, TextAlignment.CenterCenter),
  394. layer, tmpRect.width, tmpRect.height, tmpRect.height, new PointF2D(tmpRect.x, tmpRect.y + 12));
  395. }
  396. layer++;
  397. startBox.ChildElements.forEach(bb => this.drawBoundingBoxes(bb, layer, type));
  398. }
  399. private drawMarkedAreas(system: MusicSystem): void {
  400. for (const markedArea of system.GraphicalMarkedAreas) {
  401. if (markedArea !== undefined) {
  402. if (markedArea.systemRectangle !== undefined) {
  403. this.drawRectangle(markedArea.systemRectangle, <number>GraphicalLayers.Background);
  404. }
  405. if (markedArea.settings !== undefined) {
  406. this.drawLabel(markedArea.settings, <number>GraphicalLayers.Comment);
  407. }
  408. if (markedArea.labelRectangle !== undefined) {
  409. this.drawRectangle(markedArea.labelRectangle, <number>GraphicalLayers.Background);
  410. }
  411. if (markedArea.label !== undefined) {
  412. this.drawLabel(markedArea.label, <number>GraphicalLayers.Comment);
  413. }
  414. }
  415. }
  416. }
  417. private drawComment(system: MusicSystem): void {
  418. for (const comment of system.GraphicalComments) {
  419. if (comment !== undefined) {
  420. if (comment.settings !== undefined) {
  421. this.drawLabel(comment.settings, <number>GraphicalLayers.Comment);
  422. }
  423. if (comment.label !== undefined) {
  424. this.drawLabel(comment.label, <number>GraphicalLayers.Comment);
  425. }
  426. }
  427. }
  428. }
  429. private drawStaffLineSymbols(staffLine: StaffLine): void {
  430. const parentInst: Instrument = staffLine.ParentStaff.ParentInstrument;
  431. const absX: number = staffLine.PositionAndShape.AbsolutePosition.x;
  432. const absY: number = staffLine.PositionAndShape.AbsolutePosition.y + 2;
  433. const borderRight: number = staffLine.PositionAndShape.BorderRight;
  434. if (parentInst.highlight && this.drawingParameters.drawHighlights) {
  435. this.drawLineAsHorizontalRectangle(
  436. new GraphicalLine(
  437. new PointF2D(absX, absY),
  438. new PointF2D(absX + borderRight, absY),
  439. 4,
  440. OutlineAndFillStyleEnum.Highlighted
  441. ),
  442. <number>GraphicalLayers.Highlight
  443. );
  444. }
  445. let style: MusicSymbolDrawingStyle = MusicSymbolDrawingStyle.Disabled;
  446. let symbol: MusicSymbol = MusicSymbol.PLAY;
  447. let drawSymbols: boolean = this.drawingParameters.drawActivitySymbols;
  448. switch (this.phonicScoreMode) {
  449. case PhonicScoreModes.Midi:
  450. symbol = MusicSymbol.PLAY;
  451. if (this.midiPlaybackAvailable && staffLine.ParentStaff.audible) {
  452. style = MusicSymbolDrawingStyle.PlaybackSymbols;
  453. }
  454. break;
  455. case PhonicScoreModes.Following:
  456. symbol = MusicSymbol.MIC;
  457. if (staffLine.ParentStaff.following) {
  458. style = MusicSymbolDrawingStyle.FollowSymbols;
  459. }
  460. break;
  461. default:
  462. drawSymbols = false;
  463. break;
  464. }
  465. if (drawSymbols) {
  466. const p: PointF2D = new PointF2D(absX + borderRight + 2, absY);
  467. this.drawSymbol(symbol, style, p);
  468. }
  469. if (this.drawingParameters.drawErrors) {
  470. for (const measure of staffLine.Measures) {
  471. const measurePSI: BoundingBox = measure.PositionAndShape;
  472. const absXPSI: number = measurePSI.AbsolutePosition.x;
  473. const absYPSI: number = measurePSI.AbsolutePosition.y + 2;
  474. if (measure.hasError && this.graphicalMusicSheet.ParentMusicSheet.DrawErroneousMeasures) {
  475. this.drawLineAsHorizontalRectangle(
  476. new GraphicalLine(
  477. new PointF2D(absXPSI, absYPSI),
  478. new PointF2D(absXPSI + measurePSI.BorderRight, absYPSI),
  479. 4,
  480. OutlineAndFillStyleEnum.ErrorUnderlay
  481. ),
  482. <number>GraphicalLayers.MeasureError
  483. );
  484. }
  485. }
  486. }
  487. }
  488. }