Sfoglia il codice sorgente

fix(Cursor): show correctly for MultiRestMeasures, interpolated by MeasureNumber (progress)

MusicSheetCalculator: fix measures being wrongly put as isReducedToMultiRest or not
  fix sourceMeasure.allRests incorrectly true when one graphicalMeasure.isOnlyRests

related to #861 (fix allRests, isReducedToMultiRest)
sschmid 5 anni fa
parent
commit
f80697b1c8

+ 2 - 1
src/MusicalScore/Graphical/GraphicalMeasure.ts

@@ -60,7 +60,8 @@ export abstract class GraphicalMeasure extends GraphicalObject {
     public endInstructionsWidth: number;
     public hasError: boolean;
     /**
-     * Whether or not this measure is nothing but rest(s)
+     * Whether or not this measure is nothing but rest(s).
+     * Also see SourceMeasure.allRests, which is not the same, because a source measure can have multiple staffs/graphicalMeasures.
      */
     public hasOnlyRests: boolean = false;
 

+ 11 - 0
src/MusicalScore/Graphical/GraphicalMusicSheet.ts

@@ -202,6 +202,17 @@ export class GraphicalMusicSheet {
         return undefined;
     }
 
+    public findGraphicalMeasure(measureIndex: number, staffIndex: number): GraphicalMeasure {
+        for (let i: number = measureIndex; i >= 0; i--) {
+            const gMeasure: GraphicalMeasure = this.measureList[i][staffIndex];
+            if (gMeasure) {
+                return gMeasure;
+            }
+            // else look backwards (previous measures). this is only really valid for MultipleRestMeasures of course.
+        }
+        return undefined; // shouldn't happen
+    }
+
     /**
      * Search the MeasureList for a certain GraphicalStaffEntry with the given SourceStaffEntry,
      * at a certain verticalIndex (eg a corresponding Staff), starting at a specific horizontalIndex (eg specific GraphicalMeasure).

+ 31 - 13
src/MusicalScore/Graphical/MusicSheetCalculator.ts

@@ -168,13 +168,28 @@ export abstract class MusicSheetCalculator {
             );
             measureList.push(graphicalMeasures);
             if (sourceMeasure.multipleRestMeasures && this.rules.RenderMultipleRestMeasures) {
-                const measuresToSkip: number = sourceMeasure.multipleRestMeasures - 1;
-                // console.log(`skipping ${measuresToSkip} measures for measure #${sourceMeasure.MeasureNumber}.`);
-                idx += measuresToSkip;
-                for (let idx2: number = 0; idx2 < measuresToSkip; idx2++) {
-                    measureList.push([undefined]);
-                    // TODO we could push an object here or push nothing entirely,
-                    //   but then the index doesn't correspond to measure numbers anymore.
+                if (sourceMeasure.canBeReducedToMultiRest()) {
+                    sourceMeasure.isReducedToMultiRest = true;
+                    sourceMeasure.multipleRestMeasureNumber = 1;
+                    const measuresToSkip: number = sourceMeasure.multipleRestMeasures - 1;
+                    // console.log(`skipping ${measuresToSkip} measures for measure #${sourceMeasure.MeasureNumber}.`);
+                    idx += measuresToSkip;
+                    for (let idx2: number = 1; idx2 <= measuresToSkip; idx2++) {
+                        const nextSourceMeasure: SourceMeasure = musicSheet.SourceMeasures[sourceMeasure.MeasureNumber - 1 + idx2];
+                        // TODO handle the case that a measure after the first multiple rest measure can't be reduced
+                        nextSourceMeasure.multipleRestMeasureNumber = idx2 + 1;
+                        nextSourceMeasure.isReducedToMultiRest = true;
+                        measureList.push([undefined]);
+                        // TODO we could push an object here or push nothing entirely,
+                        //   but then the index doesn't correspond to measure numbers anymore.
+                    }
+                } else {
+                    // the original number of multiple rests given needs to be reduced,
+                    //   because the multirest needs to be "postponed" to the next possible location,
+                    //   because we could not do a multirest over all vertical measures
+                    sourceMeasure.multipleRestMeasures = 0;
+                    const nextMeasure: SourceMeasure = musicSheet.SourceMeasures[sourceMeasure.MeasureNumber]; // MeasureNumber - 1 = index, + 1 = next
+                    nextMeasure.multipleRestMeasures = sourceMeasure.multipleRestMeasures - 1;
                 }
             }
         }
@@ -186,10 +201,14 @@ export abstract class MusicSheetCalculator {
             //go through all source measures again. Need to calc auto-multi-rests
             for (let idx: number = 0, len: number = musicSheet.SourceMeasures.length; idx < len; ++idx) {
                 const sourceMeasure: SourceMeasure = musicSheet.SourceMeasures[idx];
-                if (sourceMeasure.canBeReducedToMultiRest()) {
+                if (sourceMeasure.canBeReducedToMultiRest() && !sourceMeasure.multipleRestMeasures) {
                     //we've already been initialized, we are in the midst of a multirest sequence
                     if (multiRestCount > 0) {
+                        beginMultiRestMeasure.isReducedToMultiRest = true;
+                        beginMultiRestMeasure.multipleRestMeasureNumber = 1;
                         multiRestCount++;
+                        sourceMeasure.multipleRestMeasureNumber = multiRestCount;
+                        sourceMeasure.isReducedToMultiRest = true;
                         //clear out these measures. We know now that we are in multirest mode
                         for (let idx2: number = 0; idx2 < measureList[idx].length; idx2++) {
                             measureList[idx][idx2] = undefined;
@@ -221,6 +240,7 @@ export abstract class MusicSheetCalculator {
             //If we reached the end of the sheet and have pending multirest measure, process
             if (multiRestCount > 1) {
                 beginMultiRestMeasure.multipleRestMeasures = multiRestCount;
+                beginMultiRestMeasure.isReducedToMultiRest = true;
                 //regen graphical measures for this source measure
                 const graphicalMeasures: GraphicalMeasure[] = this.createGraphicalMeasuresForSourceMeasure(
                     beginMultiRestMeasure,
@@ -2083,18 +2103,16 @@ export abstract class MusicSheetCalculator {
         const openBeams: Beam[] = [];
         const openTuplets: Tuplet[] = [];
         const staffEntryLinks: StaffEntryLink[] = [];
-        let allHaveRests: boolean = true;
+        let restInAllGraphicalMeasures: boolean = true;
         for (let staffIndex: number = 0; staffIndex < sourceMeasure.CompleteNumberOfStaves; staffIndex++) {
             const measure: GraphicalMeasure = this.createGraphicalMeasure( // (VexFlowMeasure)
                 sourceMeasure, openTuplets, openBeams,
                 accidentalCalculators[staffIndex], activeClefs, openOctaveShifts, openLyricWords, staffIndex, staffEntryLinks
             );
-            if (allHaveRests) {
-                allHaveRests = measure.hasOnlyRests;
-            }
+            restInAllGraphicalMeasures = restInAllGraphicalMeasures && measure.hasOnlyRests;
             verticalMeasureList.push(measure);
         }
-        sourceMeasure.allRests = allHaveRests;
+        sourceMeasure.allRests = restInAllGraphicalMeasures;
         sourceMeasure.VerticalMeasureList = verticalMeasureList; // much easier way to link sourceMeasure to graphicalMeasures than Dictionary
         //this.graphicalMusicSheet.sourceToGraphicalMeasureLinks.setValue(sourceMeasure, verticalMeasureList); // overwrites entries because:
         //this.graphicalMusicSheet.sourceToGraphicalMeasureLinks[sourceMeasure] = verticalMeasureList; // can't use SourceMeasure as key.

+ 6 - 0
src/MusicalScore/VoiceData/SourceMeasure.ts

@@ -67,7 +67,13 @@ export class SourceMeasure {
     private activeTimeSignature: Fraction;
     public hasLyrics: boolean = false;
     public hasMoodExpressions: boolean = false;
+    /** Whether the SourceMeasure only has rests, no other entries.
+     *  Not the same as GraphicalMeasure.hasOnlyRests, because one SourceMeasure can have many GraphicalMeasures (staffs).
+     */
     public allRests: boolean = false;
+    public isReducedToMultiRest: boolean = false;
+    /** If this measure is a MultipleRestMeasure, this is the number of the measure in that sequence of measures. */
+    public multipleRestMeasureNumber: number = 0;
     private staffLinkedExpressions: MultiExpression[][] = [];
     private tempoExpressions: MultiTempoExpression[] = [];
     private verticalSourceStaffEntryContainers: VerticalSourceStaffEntryContainer[] = [];

+ 28 - 19
src/OpenSheetMusicDisplay/Cursor.ts

@@ -11,6 +11,8 @@ import {Fraction} from "../Common/DataObjects/Fraction";
 import { EngravingRules } from "../MusicalScore/Graphical/EngravingRules";
 import { SourceMeasure } from "../MusicalScore/VoiceData/SourceMeasure";
 import { StaffLine } from "../MusicalScore/Graphical/StaffLine";
+import { GraphicalMeasure } from "../MusicalScore/Graphical/GraphicalMeasure";
+import { VexFlowMeasure } from "../MusicalScore/Graphical/VexFlow/VexFlowMeasure";
 
 /**
  * A cursor which can iterate through the music sheet.
@@ -117,36 +119,43 @@ export class Cursor {
       return;
     }
     let x: number = 0, y: number = 0, height: number = 0;
+    let musicSystem: MusicSystem;
+    if (iterator.CurrentMeasure.isReducedToMultiRest) {
+      const multiRestGMeasure: GraphicalMeasure = this.graphic.findGraphicalMeasure(iterator.CurrentMeasureIndex, 0);
+      const totalRestMeasures: number = multiRestGMeasure.parentSourceMeasure.multipleRestMeasures;
+      const currentRestMeasureNumber: number = iterator.CurrentMeasure.multipleRestMeasureNumber;
+      const progressRatio: number = currentRestMeasureNumber / (totalRestMeasures + 1);
+      const effectiveWidth: number = multiRestGMeasure.PositionAndShape.Size.width - (multiRestGMeasure as VexFlowMeasure).beginInstructionsWidth;
+      x = multiRestGMeasure.PositionAndShape.AbsolutePosition.x + (multiRestGMeasure as VexFlowMeasure).beginInstructionsWidth + progressRatio * effectiveWidth;
 
-    // get all staff entries inside the current voice entry
-    const gseArr: VexFlowStaffEntry[] = voiceEntries.map(ve => this.getStaffEntryFromVoiceEntry(ve));
-    // sort them by x position and take the leftmost entry
-    const gse: VexFlowStaffEntry =
-          gseArr.sort((a, b) => a?.PositionAndShape?.AbsolutePosition?.x <= b?.PositionAndShape?.AbsolutePosition?.x ? -1 : 1 )[0];
-    x = gse.PositionAndShape.AbsolutePosition.x;
-    const musicSystem: MusicSystem = gse.parentMeasure.ParentMusicSystem;
+      musicSystem = multiRestGMeasure.ParentMusicSystem;
+    } else {
+          // get all staff entries inside the current voice entry
+          const gseArr: VexFlowStaffEntry[] = voiceEntries.map(ve => this.getStaffEntryFromVoiceEntry(ve));
+          // sort them by x position and take the leftmost entry
+          const gse: VexFlowStaffEntry =
+                gseArr.sort((a, b) => a?.PositionAndShape?.AbsolutePosition?.x <= b?.PositionAndShape?.AbsolutePosition?.x ? -1 : 1 )[0];
+          x = gse.PositionAndShape.AbsolutePosition.x;
+          musicSystem = gse.parentMeasure.ParentMusicSystem;
+
+          // debug: change color of notes under cursor (needs re-render)
+          // for (const gve of gse.graphicalVoiceEntries) {
+          //   for (const note of gve.notes) {
+          //     note.sourceNote.NoteheadColor = "#0000FF";
+          //   }
+          // }
+    }
     if (!musicSystem) {
       return;
     }
 
+    // y is common for both multirest and non-multirest, given the MusicSystem
     y = musicSystem.PositionAndShape.AbsolutePosition.y + musicSystem.StaffLines[0].PositionAndShape.RelativePosition.y;
     const bottomStaffline: StaffLine = musicSystem.StaffLines[musicSystem.StaffLines.length - 1];
     const endY: number = musicSystem.PositionAndShape.AbsolutePosition.y +
     bottomStaffline.PositionAndShape.RelativePosition.y + bottomStaffline.StaffHeight;
     height = endY - y;
 
-    // The following code is not necessary (for now, but it could come useful later):
-    // it highlights the notes under the cursor.
-    //let vfNotes: { [voiceID: number]: Vex.Flow.StaveNote; } = gse.vfNotes;
-    //for (let voiceId in vfNotes) {
-    //    if (vfNotes.hasOwnProperty(voiceId)) {
-    //        vfNotes[voiceId].setStyle({
-    //            fillStyle: "red",
-    //            strokeStyle: "red",
-    //        });
-    //    }
-    //}
-
     // Update the graphical cursor
     // The following is the legacy cursor rendered on the canvas:
     // // let cursor: GraphicalLine = new GraphicalLine(new PointF2D(x, y), new PointF2D(x, y + height), 3, OutlineAndFillStyleEnum.PlaybackCursor);