VexFlowVoiceEntry.ts 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. import Vex from "vexflow";
  2. import VF = Vex.Flow;
  3. import { VoiceEntry } from "../../VoiceData/VoiceEntry";
  4. import { GraphicalVoiceEntry } from "../GraphicalVoiceEntry";
  5. import { GraphicalStaffEntry } from "../GraphicalStaffEntry";
  6. import { unitInPixels } from "./VexFlowMusicSheetDrawer";
  7. import { NoteEnum } from "../../../Common/DataObjects/Pitch";
  8. import { Note } from "../../VoiceData/Note";
  9. import { ColoringModes } from "./../DrawingParameters";
  10. import { GraphicalNote } from "../GraphicalNote";
  11. import { EngravingRules } from "../EngravingRules";
  12. export class VexFlowVoiceEntry extends GraphicalVoiceEntry {
  13. private mVexFlowStaveNote: VF.StemmableNote;
  14. constructor(parentVoiceEntry: VoiceEntry, parentStaffEntry: GraphicalStaffEntry, rules?: EngravingRules) {
  15. super(parentVoiceEntry, parentStaffEntry, rules);
  16. }
  17. public applyBordersFromVexflow(): void {
  18. const staveNote: any = (this.vfStaveNote as any);
  19. if (!staveNote.getNoteHeadBeginX) {
  20. return;
  21. }
  22. const boundingBox: any = staveNote.getBoundingBox();
  23. const modifierWidth: number = staveNote.getNoteHeadBeginX() - boundingBox.x;
  24. this.PositionAndShape.RelativePosition.y = boundingBox.y / unitInPixels;
  25. this.PositionAndShape.BorderTop = 0;
  26. this.PositionAndShape.BorderBottom = boundingBox.h / unitInPixels;
  27. const halfStavenoteWidth: number = (staveNote.width - ((staveNote as any).paddingRight ?? 0)) / 2;
  28. this.PositionAndShape.BorderLeft = -(modifierWidth + halfStavenoteWidth) / unitInPixels; // Left of our X origin is the modifier
  29. this.PositionAndShape.BorderRight = (boundingBox.w - modifierWidth) / unitInPixels; // Right of x origin is the note
  30. }
  31. public set vfStaveNote(value: VF.StemmableNote) {
  32. this.mVexFlowStaveNote = value;
  33. }
  34. public get vfStaveNote(): VF.StemmableNote {
  35. return this.mVexFlowStaveNote;
  36. }
  37. /** (Re-)color notes and stems by setting their Vexflow styles.
  38. * Could be made redundant by a Vexflow PR, but Vexflow needs more solid and permanent color methods/variables for that
  39. * See VexFlowConverter.StaveNote()
  40. */
  41. public color(): void {
  42. const defaultColorNotehead: string = this.rules.DefaultColorNotehead;
  43. const defaultColorRest: string = this.rules.DefaultColorRest;
  44. const defaultColorStem: string = this.rules.DefaultColorStem;
  45. const transparentColor: string = "#00000000"; // transparent color in vexflow
  46. let noteheadColor: string; // if null: no noteheadcolor to set (stays black)
  47. let sourceNoteNoteheadColor: string;
  48. const vfStaveNote: any = (<VexFlowVoiceEntry>(this as any)).vfStaveNote;
  49. for (let i: number = 0; i < this.notes.length; i++) {
  50. const note: GraphicalNote = this.notes[i];
  51. sourceNoteNoteheadColor = note.sourceNote.NoteheadColor;
  52. noteheadColor = sourceNoteNoteheadColor;
  53. // Switch between XML colors and automatic coloring
  54. if (this.rules.ColoringMode === ColoringModes.AutoColoring ||
  55. this.rules.ColoringMode === ColoringModes.CustomColorSet) {
  56. if (note.sourceNote.isRest()) {
  57. noteheadColor = this.rules.ColoringSetCurrent.getValue(-1);
  58. } else {
  59. const fundamentalNote: NoteEnum = note.sourceNote.Pitch.FundamentalNote;
  60. noteheadColor = this.rules.ColoringSetCurrent.getValue(fundamentalNote);
  61. }
  62. }
  63. if (!note.sourceNote.PrintObject) {
  64. noteheadColor = transparentColor; // transparent
  65. } else if (!noteheadColor // revert transparency after PrintObject was set to false, then true again
  66. || noteheadColor === "#000000" // questionable, because you might want to set specific notes to black,
  67. // but unfortunately some programs export everything explicitly as black
  68. ) {
  69. noteheadColor = this.rules.DefaultColorNotehead;
  70. }
  71. // DEBUG runtime coloring test
  72. /*const testColor: string = "#FF0000";
  73. if (i === 2 && Math.random() < 0.1 && note.sourceNote.NoteheadColor !== testColor) {
  74. const measureNumber: number = note.parentVoiceEntry.parentStaffEntry.parentMeasure.MeasureNumber;
  75. noteheadColor = testColor;
  76. console.log("color changed to " + noteheadColor + " of this note:\n" + note.sourceNote.Pitch.ToString() +
  77. ", in measure #" + measureNumber);
  78. }*/
  79. if (!sourceNoteNoteheadColor && this.rules.ColoringMode === ColoringModes.XML && note.sourceNote.PrintObject) {
  80. if (!note.sourceNote.isRest() && defaultColorNotehead) {
  81. noteheadColor = defaultColorNotehead;
  82. } else if (note.sourceNote.isRest() && defaultColorRest) {
  83. noteheadColor = defaultColorRest;
  84. }
  85. }
  86. if (noteheadColor && note.sourceNote.PrintObject) {
  87. note.sourceNote.NoteheadColorCurrentlyRendered = noteheadColor;
  88. } else if (!noteheadColor) {
  89. continue;
  90. }
  91. // color notebeam if all noteheads have same color and stem coloring enabled
  92. if (this.rules.ColoringEnabled && note.sourceNote.NoteBeam && this.rules.ColorBeams) {
  93. const beamNotes: Note[] = note.sourceNote.NoteBeam.Notes;
  94. let colorBeam: boolean = true;
  95. for (let j: number = 0; j < beamNotes.length; j++) {
  96. if (beamNotes[j].NoteheadColorCurrentlyRendered !== noteheadColor) {
  97. colorBeam = false;
  98. }
  99. }
  100. if (colorBeam) {
  101. if (vfStaveNote?.beam?.setStyle) {
  102. vfStaveNote.beam.setStyle({ fillStyle: noteheadColor, strokeStyle: noteheadColor});
  103. }
  104. }
  105. }
  106. if (vfStaveNote) {
  107. if (vfStaveNote.note_heads) { // see VexFlowConverter, needs Vexflow PR
  108. const notehead: any = vfStaveNote.note_heads[i];
  109. if (notehead) {
  110. notehead.setStyle({ fillStyle: noteheadColor, strokeStyle: noteheadColor });
  111. }
  112. }
  113. // set ledger line color. TODO coordinate this with VexFlowConverter.StaveNote(), where there's also still code for this, maybe unnecessarily.
  114. if ((vfStaveNote as any).setLedgerLineStyle) { // setLedgerLineStyle doesn't exist on TabNote or rest, would throw error.
  115. if (noteheadColor === transparentColor) {
  116. (vfStaveNote as any).setLedgerLineStyle(
  117. { fillStyle: noteheadColor, strokeStyle: noteheadColor, lineWidth: this.rules.LedgerLineWidth });
  118. } else {
  119. (vfStaveNote as any).setLedgerLineStyle({
  120. fillStyle: this.rules.LedgerLineColorDefault,
  121. lineWidth: this.rules.LedgerLineWidth,
  122. strokeStyle: this.rules.LedgerLineColorDefault
  123. });
  124. // we could give the color (style) as noteheadColor, but then we need to figure out which note has the ledger line.
  125. // otherwise ledger lines get the color of the top note, see Function Test Color.
  126. }
  127. }
  128. }
  129. }
  130. // color stems
  131. let stemColor: string = defaultColorStem; // reset to black/default when coloring was disabled. maybe needed elsewhere too
  132. let setVoiceEntryStemColor: boolean = false;
  133. if (this.rules.ColoringEnabled) {
  134. stemColor = this.parentVoiceEntry.StemColor; // TODO: once coloringSetCustom gets stem color, respect it
  135. if (!stemColor
  136. || stemColor === "#000000") { // see above, noteheadColor === "#000000"
  137. stemColor = defaultColorStem;
  138. }
  139. if (this.rules.ColorStemsLikeNoteheads && noteheadColor) {
  140. // condition could be even more fine-grained by only recoloring if there was no custom StemColor set. will be more complex though
  141. stemColor = noteheadColor;
  142. setVoiceEntryStemColor = true;
  143. }
  144. }
  145. let stemTransparent: boolean = true;
  146. for (const note of this.parentVoiceEntry.Notes) {
  147. if (note.PrintObject) {
  148. stemTransparent = false;
  149. break;
  150. }
  151. }
  152. if (stemTransparent) {
  153. stemColor = transparentColor;
  154. }
  155. const stemStyle: Object = { fillStyle: stemColor, strokeStyle: stemColor };
  156. if (vfStaveNote && vfStaveNote.setStemStyle) {
  157. if (!stemTransparent && setVoiceEntryStemColor) {
  158. this.parentVoiceEntry.StemColor = stemColor; // this shouldn't be set by DefaultColorStem
  159. }
  160. vfStaveNote.setStemStyle(stemStyle);
  161. if (vfStaveNote.flag && vfStaveNote.setFlagStyle && this.rules.ColorFlags) {
  162. vfStaveNote.setFlagStyle(stemStyle);
  163. }
  164. }
  165. }
  166. }