Преглед на файлове

feat(GraphicalNote): add static GraphicalNote.FromNote(note, osmd.rules) (#660, #659)

fix #660
close #660

replaces a potential reference to the GraphicalNote in Note,
because we want to keep the data sheet model separate from the graphical model,
so we don't want a reference to GraphicalNote in Note.
sschmid преди 4 години
родител
ревизия
acf1c6ee64

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

@@ -9,6 +9,8 @@ import { Dictionary } from "typescript-collections";
 import { FontStyles } from "../../Common/Enums";
 import { NoteEnum } from "../../Common/DataObjects/Pitch";
 import { ChordSymbolEnum, CustomChord, DegreesInfo } from "../../MusicalScore/VoiceData/ChordSymbolContainer";
+import { GraphicalNote } from "./GraphicalNote";
+import { Note } from "../VoiceData/Note";
 
 export class EngravingRules {
     /** A unit of distance. 1.0 is the distance between lines of a stave for OSMD, which is 10 pixels in Vexflow. */
@@ -270,6 +272,10 @@ export class EngravingRules {
     public StretchLastSystemLine: boolean;
     public SpacingBetweenTextLines: number;
 
+    public NoteToGraphicalNoteMap: Dictionary<number, GraphicalNote>;
+    // this is basically a WeakMap, except we save the id in the Note instead of using a WeakMap.
+    public NoteToGraphicalNoteMapObjectCount: number = 0;
+
     public static FixStafflineBoundingBox: boolean; // TODO temporary workaround
 
     constructor() {
@@ -558,6 +564,8 @@ export class EngravingRules {
         this.RenderSingleHorizontalStaffline = false;
         this.SpacingBetweenTextLines = 0;
 
+        this.NoteToGraphicalNoteMap = new Dictionary<number, GraphicalNote>();
+
         // this.populateDictionaries(); // these values aren't used currently
         try {
             this.MaxInstructionsConstValue = this.ClefLeftMargin + this.ClefRightMargin + this.KeyRightMargin + this.RhythmRightMargin + 11;
@@ -571,6 +579,16 @@ export class EngravingRules {
         }
     }
 
+    public addGraphicalNoteToNoteMap(note: Note, graphicalNote: GraphicalNote): void {
+        note.NoteToGraphicalNoteObjectId = this.NoteToGraphicalNoteMapObjectCount;
+        this.NoteToGraphicalNoteMap.setValue(note.NoteToGraphicalNoteObjectId, graphicalNote);
+        this.NoteToGraphicalNoteMapObjectCount++;
+    }
+
+    public clearObjects(): void {
+        this.NoteToGraphicalNoteMap = new Dictionary<number, GraphicalNote>();
+    }
+
     public setChordSymbolLabelText(key: ChordSymbolEnum, value: string): void {
         this.ChordSymbolLabelTexts.setValue(key, value);
     }

+ 7 - 0
src/MusicalScore/Graphical/GraphicalNote.ts

@@ -9,6 +9,7 @@ import {MusicSheetCalculator} from "./MusicSheetCalculator";
 import {BoundingBox} from "./BoundingBox";
 import {GraphicalVoiceEntry} from "./GraphicalVoiceEntry";
 import {GraphicalMusicPage} from "./GraphicalMusicPage";
+import { EngravingRules } from "./EngravingRules";
 
 /**
  * The graphical counterpart of a [[Note]]
@@ -33,6 +34,7 @@ export class GraphicalNote extends GraphicalObject {
     public graphicalNoteLength: Fraction;
     public parentVoiceEntry: GraphicalVoiceEntry;
     public numberOfDots: number;
+    public rules: EngravingRules;
 
     public Transpose(keyInstruction: KeyInstruction, activeClef: ClefInstruction, halfTones: number, octaveEnum: OctaveEnum): Pitch {
         let transposedPitch: Pitch = this.sourceNote.Pitch;
@@ -63,4 +65,9 @@ export class GraphicalNote extends GraphicalObject {
     public get ParentMusicPage(): GraphicalMusicPage {
       return this.parentVoiceEntry.parentStaffEntry.parentMeasure.ParentMusicSystem.Parent;
     }
+
+    /** Get a GraphicalNote from a Note. Use osmd.rules as the second parameter (instance reference). */
+    public static FromNote(note: Note, rules: EngravingRules): GraphicalNote {
+      return rules.NoteToGraphicalNoteMap.getValue(note.NoteToGraphicalNoteObjectId);
+    }
 }

+ 14 - 11
src/MusicalScore/Graphical/MusicSheetCalculator.ts

@@ -1651,9 +1651,9 @@ export abstract class MusicSheetCalculator {
             }
             let graphicalNote: GraphicalNote;
             if (voiceEntry.IsGrace) {
-                graphicalNote = MusicSheetCalculator.symbolFactory.createGraceNote(note, gve, activeClef, octaveShiftValue);
+                graphicalNote = MusicSheetCalculator.symbolFactory.createGraceNote(note, gve, activeClef, this.rules, octaveShiftValue);
             } else {
-                graphicalNote = MusicSheetCalculator.symbolFactory.createNote(note, gve, activeClef, octaveShiftValue, undefined);
+                graphicalNote = MusicSheetCalculator.symbolFactory.createNote(note, gve, activeClef, octaveShiftValue, this.rules, undefined);
                 MusicSheetCalculator.stafflineNoteCalculator.trackNote(graphicalNote);
             }
             if (note.Pitch) {
@@ -1674,11 +1674,13 @@ export abstract class MusicSheetCalculator {
             // handle TabNotes:
             if (graphicalTabVoiceEntry) {
                 // notes should be either TabNotes or RestNotes -> add all:
-                const graphicalTabNote: GraphicalNote = MusicSheetCalculator.symbolFactory.createNote(  note,
-                                                                                                        graphicalTabVoiceEntry,
-                                                                                                        activeClef,
-                                                                                                        octaveShiftValue,
-                                                                                                        undefined);
+                const graphicalTabNote: GraphicalNote = MusicSheetCalculator.symbolFactory.createNote(
+                    note,
+                    graphicalTabVoiceEntry,
+                    activeClef,
+                    octaveShiftValue,
+                    this.rules,
+                    undefined);
                 tabStaffEntry.addGraphicalNoteToListAtCorrectYPosition(graphicalTabVoiceEntry, graphicalTabNote);
                 graphicalTabNote.PositionAndShape.calculateBoundingBox();
 
@@ -2296,10 +2298,11 @@ export abstract class MusicSheetCalculator {
                 graphicalStaffEntry.relInMeasureTimestamp = voiceEntry.Timestamp;
                 const gve: GraphicalVoiceEntry = MusicSheetCalculator.symbolFactory.createVoiceEntry(voiceEntry, graphicalStaffEntry);
                 graphicalStaffEntry.graphicalVoiceEntries.push(gve);
-                const graphicalNote: GraphicalNote = MusicSheetCalculator.symbolFactory.createNote(note,
-                                                                                                   gve,
-                                                                                                   new ClefInstruction(),
-                                                                                                   OctaveEnum.NONE, undefined);
+                const graphicalNote: GraphicalNote = MusicSheetCalculator.symbolFactory.createNote(
+                    note,
+                    gve,
+                    new ClefInstruction(),
+                    OctaveEnum.NONE, undefined);
                 MusicSheetCalculator.stafflineNoteCalculator.trackNote(graphicalNote);
                 gve.notes.push(graphicalNote);
             }

+ 5 - 1
src/MusicalScore/Graphical/VexFlow/VexFlowGraphicalNote.ts

@@ -8,16 +8,19 @@ import {Fraction} from "../../../Common/DataObjects/Fraction";
 import {OctaveEnum, OctaveShift} from "../../VoiceData/Expressions/ContinuousExpressions/OctaveShift";
 import { GraphicalVoiceEntry } from "../GraphicalVoiceEntry";
 import { KeyInstruction } from "../../VoiceData/Instructions/KeyInstruction";
+import { EngravingRules } from "../EngravingRules";
 
 /**
  * The VexFlow version of a [[GraphicalNote]].
  */
 export class VexFlowGraphicalNote extends GraphicalNote {
     constructor(note: Note, parent: GraphicalVoiceEntry, activeClef: ClefInstruction,
-                octaveShift: OctaveEnum = OctaveEnum.NONE,  graphicalNoteLength: Fraction = undefined) {
+                octaveShift: OctaveEnum = OctaveEnum.NONE, rules: EngravingRules,
+                graphicalNoteLength: Fraction = undefined) {
         super(note, parent, graphicalNoteLength);
         this.clef = activeClef;
         this.octaveShift = octaveShift;
+        this.rules = rules;
         if (note.Pitch) {
             // TODO: Maybe shift to Transpose function when available
             const drawPitch: Pitch = note.isRest() ? note.Pitch : OctaveShift.getPitchFromOctaveShift(note.Pitch, octaveShift);
@@ -33,6 +36,7 @@ export class VexFlowGraphicalNote extends GraphicalNote {
     public vfnote: [Vex.Flow.StemmableNote, number];
     // The current clef
     private clef: ClefInstruction;
+    public rules: EngravingRules;
 
     /**
      * Update the pitch of this note. Necessary in order to display accidentals correctly.

+ 7 - 6
src/MusicalScore/Graphical/VexFlow/VexFlowGraphicalSymbolFactory.ts

@@ -114,10 +114,10 @@ export class VexFlowGraphicalSymbolFactory implements IGraphicalSymbolFactory {
      * @param octaveShift   The currently active octave transposition enum, needed for positioning the note vertically
      * @returns {GraphicalNote}
      */
-    public createNote(note: Note, graphicalVoiceEntry: GraphicalVoiceEntry,
-                      activeClef: ClefInstruction, octaveShift: OctaveEnum = OctaveEnum.NONE,  graphicalNoteLength: Fraction = undefined): GraphicalNote {
-        // Creates and returns the note:
-        return new VexFlowGraphicalNote(note, graphicalVoiceEntry, activeClef, octaveShift, graphicalNoteLength);
+    public createNote(note: Note, graphicalVoiceEntry: GraphicalVoiceEntry, activeClef: ClefInstruction,
+        octaveShift: OctaveEnum = OctaveEnum.NONE, rules: EngravingRules,
+        graphicalNoteLength: Fraction = undefined): GraphicalNote {
+        return new VexFlowGraphicalNote(note, graphicalVoiceEntry, activeClef, octaveShift, rules, graphicalNoteLength);
     }
 
     /**
@@ -130,8 +130,9 @@ export class VexFlowGraphicalSymbolFactory implements IGraphicalSymbolFactory {
      * @returns {GraphicalNote}
      */
     public createGraceNote(note: Note, graphicalVoiceEntry: GraphicalVoiceEntry,
-                           activeClef: ClefInstruction, octaveShift: OctaveEnum = OctaveEnum.NONE): GraphicalNote {
-        return new VexFlowGraphicalNote(note, graphicalVoiceEntry, activeClef, octaveShift);
+                           activeClef: ClefInstruction, rules: EngravingRules,
+                           octaveShift: OctaveEnum = OctaveEnum.NONE): GraphicalNote {
+        return new VexFlowGraphicalNote(note, graphicalVoiceEntry, activeClef, octaveShift, rules);
     }
 
     /**

+ 6 - 2
src/MusicalScore/Interfaces/IGraphicalSymbolFactory.ts

@@ -40,13 +40,17 @@ export interface IGraphicalSymbolFactory {
         graphicalVoiceEntry: GraphicalVoiceEntry,
         activeClef: ClefInstruction,
         octaveShift: OctaveEnum,
-        graphicalNoteLength: Fraction): GraphicalNote;
+        rules: EngravingRules,
+        graphicalNoteLength?: Fraction,
+    ): GraphicalNote;
 
     createGraceNote(
         note: Note,
         graphicalVoiceEntry: GraphicalVoiceEntry,
         activeClef: ClefInstruction,
-        octaveShift: OctaveEnum): GraphicalNote;
+        rules: EngravingRules,
+        octaveShift?: OctaveEnum,
+    ): GraphicalNote;
 
     addGraphicalAccidental(graphicalNote: GraphicalNote, pitch: Pitch): void;
 

+ 2 - 0
src/MusicalScore/VoiceData/Note.ts

@@ -94,6 +94,8 @@ export class Note {
     private noteheadColor: string;
     private noteheadColorCurrentlyRendered: string;
     public Fingering: TechnicalInstruction; // this is also stored in VoiceEntry.TechnicalInstructions
+    /** Used by GraphicalNote.FromNote() to get a GraphicalNote from a Note. */
+    public NoteToGraphicalNoteObjectId: number; // used with EngravingRules.NoteToGraphicalNoteMap
 
     public get ParentVoiceEntry(): VoiceEntry {
         return this.voiceEntry;

+ 1 - 0
src/OpenSheetMusicDisplay/OpenSheetMusicDisplay.ts

@@ -191,6 +191,7 @@ export class OpenSheetMusicDisplay {
         this.drawer?.clear(); // clear canvas before setting width
         // this.graphic.GetCalculator.clearSystemsAndMeasures(); // maybe?
         // this.graphic.GetCalculator.clearRecreatedObjects();
+        this.rules.clearObjects();
 
         // Set page width
         let width: number = this.container.offsetWidth;