Browse Source

feat(options): add option to align rests and avoid rest collisions, which also aligns rests with voices (#621)

still have to check whether these two effects can be separated in Vexflow,
so rests only avoid collisions and don't change position when there's no simultaneous notes in other voices.

part of #621
sschmid 5 years ago
parent
commit
ca6d730ffc

+ 8 - 0
src/MusicalScore/Graphical/EngravingRules.ts

@@ -179,6 +179,7 @@ export class EngravingRules {
     private durationDistanceDict: {[_: number]: number; } = {};
     private durationScalingDistanceDict: {[_: number]: number; } = {};
 
+    private alignRests: boolean;
     private coloringMode: ColoringMode;
     private coloringEnabled: boolean;
     private colorStemsLikeNoteheads: boolean;
@@ -401,6 +402,7 @@ export class EngravingRules {
         this.metronomeMarkYShift = -0.5;
 
         // Render options (whether to render specific or invisible elements)
+        this.alignRests = false;
         this.coloringMode = ColoringMode.XML;
         this.coloringEnabled = true;
         this.colorStemsLikeNoteheads = false;
@@ -1348,6 +1350,12 @@ export class EngravingRules {
     public get DurationScalingDistanceDict(): {[_: number]: number; } {
         return this.durationScalingDistanceDict;
     }
+    public get AlignRests(): boolean {
+        return this.alignRests;
+    }
+    public set AlignRests(value: boolean) {
+        this.alignRests = value;
+    }
     public get ColoringMode(): ColoringMode {
         return this.coloringMode;
     }

+ 10 - 6
src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetCalculator.ts

@@ -136,14 +136,18 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
         if (measure === measures[0]) {
           const vexflowMeasure: VexFlowMeasure = (measure as VexFlowMeasure);
           // prepare format function for voices, will be called later for formatting measure again
-          vexflowMeasure.formatVoices = (w: number) => {
+          let formatVoices: (w: number) => void = (w) => {
             formatter.format(allVoices, w);
-            // formatter.format(allVoices, w, {
-            //   align_rests: false, // TODO
-            //   // align_rests = true causes a Vexflow Exception for Mozart - An Chloe
-            //   // align_rests = false still aligns rests with beams according to Vexflow, but doesn't seem to do anything
-            // });
           };
+          if (EngravingRules.Rules.AlignRests) { // aligns rests and avoids rest collisions. has to be enabled in OSMDOptions.alignRests
+            formatVoices = (w) => {
+              formatter.format(allVoices, w, {
+              align_rests: true,
+              context: undefined
+              });
+            };
+          }
+          vexflowMeasure.formatVoices = formatVoices;
           // format now for minimum width, calculateMeasureWidthFromLyrics later
           vexflowMeasure.formatVoices(minStaffEntriesWidth * unitInPixels);
         } else {

+ 5 - 0
src/OpenSheetMusicDisplay/OSMDOptions.ts

@@ -5,6 +5,11 @@ import { DrawingParametersEnum, ColoringModes } from "../MusicalScore/Graphical/
  *  Example: osmd.setOptions({defaultColorRest: "#AAAAAA", drawSubtitle: false}); osmd.render();
  */
 export interface IOSMDOptions {
+    /** Whether to let Vexflow align rests to voices and avoid rest collisions with notes. Default false.
+     * Unfortunately, rest note collision can not be enabled without aligning rests yet,
+     * which also changes the position of rests when there is no simultaneous note at the same x-coordinate.
+     */
+    alignRests?: boolean;
     /** Whether to automatically create beams for notes that don't have beams set in XML. */
     autoBeam?: boolean;
     /** Options for autoBeaming like whether to beam over rests. See AutoBeamOptions interface. */

+ 3 - 0
src/OpenSheetMusicDisplay/OpenSheetMusicDisplay.ts

@@ -263,6 +263,9 @@ export class OpenSheetMusicDisplay {
             }
         }
 
+        if (options.alignRests !== undefined) {
+            EngravingRules.Rules.AlignRests = options.alignRests;
+        }
         if (options.coloringMode !== undefined) {
             this.setColoringMode(options);
         }