Browse Source

fix(Moved expressions to Calculator): Ported more code from PS and moved base methods to Calculator

Benjamin Giesinger 6 năm trước cách đây
mục cha
commit
250429bd0c

+ 3 - 1
demo/index.js

@@ -6,7 +6,7 @@ import { OpenSheetMusicDisplay } from '../src/OpenSheetMusicDisplay/OpenSheetMus
     var openSheetMusicDisplay;
     // folder of the sample files
     var sampleFolder = process.env.STATIC_FILES_SUBFOLDER ? process.env.STATIC_FILES_SUBFOLDER + "/" : "",
-        samples = {
+    samples = {
             "Beethoven, L.v. - An die ferne Geliebte": "Beethoven_AnDieFerneGeliebte.xml",
             "Clementi, M. - Sonatina Op.36 No.1 Pt.1": "MuzioClementi_SonatinaOpus36No1_Part1.xml",
             "Clementi, M. - Sonatina Op.36 No.1 Pt.2": "MuzioClementi_SonatinaOpus36No1_Part2.xml",
@@ -209,6 +209,8 @@ import { OpenSheetMusicDisplay } from '../src/OpenSheetMusicDisplay/OpenSheetMus
         zoom = 1.0;
         openSheetMusicDisplay.load(str).then(
             function() {
+                // This gives you access to the osmd object in the console. Do not use in productive code
+                window.osmd = openSheetMusicDisplay;
                 return openSheetMusicDisplay.render();
             },
             function(e) {

+ 13 - 1
src/MusicalScore/Graphical/GraphicalInstantaniousDynamicExpression.ts

@@ -1,7 +1,19 @@
 import { GraphicalObject } from "./GraphicalObject";
+import { StaffLine } from "./StaffLine";
+import { InstantaniousDynamicExpression } from "../VoiceData/Expressions/InstantaniousDynamicExpression";
+import { GraphicalMeasure } from "./GraphicalMeasure";
+import { BoundingBox } from "./BoundingBox";
 
 export class GraphicalInstantaniousDynamicExpression extends GraphicalObject {
-    constructor() {
+    protected mInstantaniousDynamicExpression: InstantaniousDynamicExpression;
+    protected mParentStaffLine: StaffLine;
+    protected mMeasure: GraphicalMeasure;
+
+    constructor(instantaniousDynamic: InstantaniousDynamicExpression, staffLine: StaffLine, measure: GraphicalMeasure) {
         super();
+        this.boundingBox = new BoundingBox(this, staffLine.PositionAndShape);
+        this.mInstantaniousDynamicExpression = instantaniousDynamic;
+        this.mParentStaffLine = staffLine;
+        this.mMeasure = measure;
     }
 }

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

@@ -11,6 +11,13 @@ import {MusicSheetCalculator} from "./MusicSheetCalculator";
 export class GraphicalLabel extends Clickable {
     private label: Label;
 
+    /**
+     * Creates a new GraphicalLabel from a Label
+     * @param label  label object containing text
+     * @param textHeight Height of text
+     * @param alignment Alignement like left, right, top, ...
+     * @param parent Parent Bounding Box where the label is attached to
+     */
     constructor(label: Label, textHeight: number, alignment: TextAlignment, parent: BoundingBox = undefined) {
         super();
         this.label = label;

+ 2 - 6
src/MusicalScore/Graphical/MusicSheetCalculator.ts

@@ -56,8 +56,6 @@ import { Label } from "../Label";
 import { GraphicalVoiceEntry } from "./GraphicalVoiceEntry";
 import { VerticalSourceStaffEntryContainer } from "../VoiceData/VerticalSourceStaffEntryContainer";
 import { SkyBottomLineCalculator } from "./SkyBottomLineCalculator";
-// FIXME: This shoud not be here
-import { VexFlowMeasure } from "./VexFlow/VexFlowMeasure";
 
 /**
  * Class used to do all the calculations in a MusicSheet, which in the end populates a GraphicalMusicSheet.
@@ -865,7 +863,7 @@ export abstract class MusicSheetCalculator {
         return;
     }
 
-    protected calculateDynamicExpressionsForSingleMultiExpression(multiExpression: MultiExpression, measureIndex: number, staffIndex: number): void {
+    protected calculateDynamicExpressionsForMultiExpression(multiExpression: MultiExpression, measureIndex: number, staffIndex: number): void {
         return;
     }
 
@@ -1965,8 +1963,6 @@ export abstract class MusicSheetCalculator {
     private calculateDynamicExpressions(): void {
         for (let i: number = 0; i < this.graphicalMusicSheet.ParentMusicSheet.SourceMeasures.length; i++) {
             const sourceMeasure: SourceMeasure = this.graphicalMusicSheet.ParentMusicSheet.SourceMeasures[i];
-            // Clear list of all dynamic epxressions and fill again (needed if lines are removed)
-            this.graphicalMusicSheet.MeasureList[i].forEach(m => (m as VexFlowMeasure).instantaniousDynamics = []);
             for (let j: number = 0; j < sourceMeasure.StaffLinkedExpressions.length; j++) {
                 if (this.graphicalMusicSheet.MeasureList[i][j].ParentStaff.ParentInstrument.Visible) {
                     for (let k: number = 0; k < sourceMeasure.StaffLinkedExpressions[j].length; k++) {
@@ -1975,7 +1971,7 @@ export abstract class MusicSheetCalculator {
                                 sourceMeasure.StaffLinkedExpressions[j][k].StartingContinuousDynamic.StartMultiExpression ===
                                 sourceMeasure.StaffLinkedExpressions[j][k] && sourceMeasure.StaffLinkedExpressions[j][k].UnknownList.length === 0)
                         ) {
-                            this.calculateDynamicExpressionsForSingleMultiExpression(sourceMeasure.StaffLinkedExpressions[j][k], i, j);
+                            this.calculateDynamicExpressionsForMultiExpression(sourceMeasure.StaffLinkedExpressions[j][k], i, j);
                         }
                     }
                 }

+ 10 - 9
src/MusicalScore/Graphical/SkyBottomLineCalculator.ts

@@ -377,8 +377,9 @@ export class SkyBottomLineCalculator {
      * @returns Maximum value inside bounding box boundaries or undefined if not possible
      */
     public getBottomLineMaxInBoundingBox(boundingBox: BoundingBox): number {
-        const startPoint: number = Math.floor(boundingBox.AbsolutePosition.x + boundingBox.BorderMarginLeft);
-        const endPoint: number = Math.ceil(boundingBox.AbsolutePosition.x + boundingBox.BorderMarginRight);
+        //TODO: Actually it should be the margin. But that one is not implemented
+        const startPoint: number = Math.floor(boundingBox.AbsolutePosition.x + boundingBox.BorderLeft);
+        const endPoint: number = Math.ceil(boundingBox.AbsolutePosition.x + boundingBox.BorderRight);
         return this.getMaxInRange(this.mBottomLine, startPoint, endPoint);
     }
 
@@ -389,21 +390,21 @@ export class SkyBottomLineCalculator {
      * @param boundingBox Bounding box to be added
      * @param topBorder top
      */
-    private updateWithBoundingBoxRecursivly(boundingBox: BoundingBox): void {
+    public updateWithBoundingBoxRecursivly(boundingBox: BoundingBox): void {
         if (boundingBox.ChildElements && boundingBox.ChildElements.length > 0) {
             this.updateWithBoundingBoxRecursivly(boundingBox);
         } else {
-            const currentTopBorder: number = boundingBox.BorderMarginTop + boundingBox.AbsolutePosition.y;
-            const currentBottomBorder: number = boundingBox.BorderMarginBottom + boundingBox.AbsolutePosition.y;
+            const currentTopBorder: number = boundingBox.BorderTop + boundingBox.AbsolutePosition.y;
+            const currentBottomBorder: number = boundingBox.BorderBottom + boundingBox.AbsolutePosition.y;
 
             if (currentTopBorder < 0) {
-                const startPoint: number = Math.floor(boundingBox.AbsolutePosition.x + boundingBox.BorderMarginLeft);
-                const endPoint: number = Math.ceil(boundingBox.AbsolutePosition.x + boundingBox.BorderMarginRight) ;
+                const startPoint: number = Math.floor(boundingBox.AbsolutePosition.x + boundingBox.BorderLeft);
+                const endPoint: number = Math.ceil(boundingBox.AbsolutePosition.x + boundingBox.BorderRight) ;
 
                 this.updateInRange(this.mSkyLine, startPoint, endPoint, currentTopBorder);
             } else if (currentBottomBorder > this.mRules.StaffHeight) {
-                const startPoint: number = Math.floor(boundingBox.AbsolutePosition.x + boundingBox.BorderMarginLeft);
-                const endPoint: number = Math.ceil(boundingBox.AbsolutePosition.x + boundingBox.BorderMarginRight);
+                const startPoint: number = Math.floor(boundingBox.AbsolutePosition.x + boundingBox.BorderLeft);
+                const endPoint: number = Math.ceil(boundingBox.AbsolutePosition.x + boundingBox.BorderRight);
 
                 this.updateInRange(this.mBottomLine, startPoint, endPoint, currentBottomBorder);
             }

+ 9 - 0
src/MusicalScore/Graphical/StaffLine.ts

@@ -25,6 +25,7 @@ export abstract class StaffLine extends GraphicalObject {
     protected skyBottomLine: SkyBottomLineCalculator;
     protected lyricLines: GraphicalLine[] = [];
     protected lyricsDashes: GraphicalLabel[] = [];
+    protected abstractExpressions: GraphicalObject[] = [];
 
     constructor(parentSystem: MusicSystem, parentStaff: Staff) {
         super();
@@ -59,6 +60,14 @@ export abstract class StaffLine extends GraphicalObject {
         return this.lyricLines;
     }
 
+    public get AbstractExpressions(): GraphicalObject[] {
+        return this.abstractExpressions;
+    }
+
+    public set AbstractExpressions(value: GraphicalObject[]) {
+        this.abstractExpressions = value;
+    }
+
     public set LyricLines(value: GraphicalLine[]) {
         this.lyricLines = value;
     }

+ 8 - 43
src/MusicalScore/Graphical/VexFlow/VexFlowInstantaniousDynamicExpression.ts

@@ -1,66 +1,31 @@
 import { GraphicalInstantaniousDynamicExpression } from "../GraphicalInstantaniousDynamicExpression";
 import { InstantaniousDynamicExpression, DynamicEnum } from "../../VoiceData/Expressions/InstantaniousDynamicExpression";
-import { GraphicalStaffEntry } from "../GraphicalStaffEntry";
 import { GraphicalLabel } from "../GraphicalLabel";
 import { Label } from "../../Label";
 import { TextAlignment } from "../../../Common/Enums/TextAlignment";
 import { EngravingRules } from "../EngravingRules";
 import { FontStyles } from "../../../Common/Enums/FontStyles";
-import { SkyBottomLineCalculator } from "../SkyBottomLineCalculator";
 import { StaffLine } from "../StaffLine";
 import { GraphicalMeasure } from "../GraphicalMeasure";
-import { MusicSystem } from "../MusicSystem";
 
 export class VexFlowInstantaniousDynamicExpression extends GraphicalInstantaniousDynamicExpression {
-    private mInstantaniousDynamicExpression: InstantaniousDynamicExpression;
     private mLabel: GraphicalLabel;
 
-    constructor(instantaniousDynamicExpression: InstantaniousDynamicExpression, staffEntry: GraphicalStaffEntry) {
-        super();
-        this.mInstantaniousDynamicExpression = instantaniousDynamicExpression;
+    constructor(instantaniousDynamicExpression: InstantaniousDynamicExpression, staffLine: StaffLine, measure: GraphicalMeasure) {
+        super(instantaniousDynamicExpression, staffLine, measure);
+
         this.mLabel = new GraphicalLabel(new Label(this.Expression),
                                          EngravingRules.Rules.ContinuousDynamicTextHeight,
-                                         TextAlignment.LeftTop,
-                                         staffEntry ? staffEntry.PositionAndShape : undefined);
+                                         TextAlignment.CenterTop,
+                                         this.PositionAndShape);
 
-        let offset: number = staffEntry ? staffEntry.parentMeasure.ParentStaffLine
-                                     .SkyBottomLineCalculator.getBottomLineMaxInBoundingBox(staffEntry.parentMeasure.PositionAndShape) : 0;
-        // TODO: this should not happen: Bug in sbc?
-        offset = offset < 0 ? 0 : offset;
-        this.mLabel.PositionAndShape.RelativePosition.y += offset;
         this.mLabel.Label.fontStyle = FontStyles.BoldItalic;
         this.mLabel.setLabelPositionAndShapeBorders();
+        this.PositionAndShape.calculateBoundingBox();
     }
 
-    public calculcateBottomLine(measure: GraphicalMeasure): void {
-        const skyBottomLineCalculator: SkyBottomLineCalculator = measure.ParentStaffLine.SkyBottomLineCalculator;
-        const staffLine: StaffLine = measure.ParentStaffLine;
-        const musicSystem: MusicSystem = measure.parentMusicSystem;
-
-        // calculate LabelBoundingBox and set PSI parent
-        this.mLabel.setLabelPositionAndShapeBorders();
-        this.mLabel.PositionAndShape.Parent = musicSystem.PositionAndShape;
-
-        // calculate relative Position
-        const relativeX: number = staffLine.PositionAndShape.RelativePosition.x +
-        measure.PositionAndShape.RelativePosition.x - this.mLabel.PositionAndShape.BorderMarginLeft;
-        let relativeY: number;
-
-        // and the corresponding SkyLine indeces
-        let start: number = relativeX;
-        let end: number = relativeX - this.mLabel.PositionAndShape.BorderLeft + this.mLabel.PositionAndShape.BorderMarginRight;
-
-          // take into account the InstrumentNameLabel's at the beginning of the first MusicSystem
-        if (staffLine === musicSystem.StaffLines[0] && musicSystem === musicSystem.Parent.MusicSystems[0]) {
-              start -= staffLine.PositionAndShape.RelativePosition.x;
-              end -= staffLine.PositionAndShape.RelativePosition.x;
-          }
-
-          // get the minimum corresponding SkyLine value
-        const bottomLineMaxValue: number = skyBottomLineCalculator.getBottomLineMaxInRange(start, end);
-        relativeY = bottomLineMaxValue;
-        // console.log(start, end, relativeY, this.mLabel.PositionAndShape.BorderMarginBottom)
-        skyBottomLineCalculator.updateBottomLineInRange(start, end, relativeY + this.mLabel.PositionAndShape.BorderMarginBottom);
+    get InstantaniousDynamic(): InstantaniousDynamicExpression {
+        return this.mInstantaniousDynamicExpression;
     }
 
     get Expression(): string {

+ 1 - 0
src/MusicalScore/Graphical/VexFlow/VexFlowMeasure.ts

@@ -87,6 +87,7 @@ export class VexFlowMeasure extends GraphicalMeasure {
         this.connectors = [];
         // Clean up instructions
         this.resetLayout();
+        this.instantaniousDynamics = [];
     }
 
     /**

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

@@ -34,6 +34,12 @@ import { VexFlowOctaveShift } from "./VexFlowOctaveShift";
 import { VexFlowInstantaniousDynamicExpression } from "./VexFlowInstantaniousDynamicExpression";
 import {BoundingBox} from "../BoundingBox";
 import { EngravingRules } from "../EngravingRules";
+import { InstantaniousDynamicExpression } from "../../VoiceData/Expressions/InstantaniousDynamicExpression";
+import { PointF2D } from "../../../Common/DataObjects/PointF2D";
+import { GraphicalInstantaniousDynamicExpression } from "../GraphicalInstantaniousDynamicExpression";
+import { SkyBottomLineCalculator } from "../SkyBottomLineCalculator";
+import { PlacementEnum } from "../../VoiceData/Expressions/AbstractExpression";
+import { Staff } from "../../VoiceData/Staff";
 
 export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
 
@@ -348,18 +354,126 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
     }
   }
 
-  protected calculateDynamicExpressionsForSingleMultiExpression(multiExpression: MultiExpression, measureIndex: number, staffIndex: number): void {
+  protected calculateDynamicExpressionsForMultiExpression(multiExpression: MultiExpression, measureIndex: number, staffIndex: number): void {
+
+    // calculate absolute Timestamp
+    const absoluteTimestamp: Fraction = multiExpression.AbsoluteTimestamp;
+    const measures: GraphicalMeasure[] = this.graphicalMusicSheet.MeasureList[measureIndex];
+    const staffLine: StaffLine = measures[staffIndex].ParentStaffLine;
 
     if (multiExpression.InstantaniousDynamic) {
-        const timeStamp: Fraction = multiExpression.Timestamp;
+        const instantaniousDynamic: InstantaniousDynamicExpression = multiExpression.InstantaniousDynamic;
+
+        const startPosInStaffline: PointF2D = this.getRelativePositionInStaffLineFromTimestamp(
+          absoluteTimestamp,
+          staffIndex,
+          staffLine,
+          staffLine.isPartOfMultiStaffInstrument());
+        if (Math.abs(startPosInStaffline.x) === 0) {
+          startPosInStaffline.x = measures[staffIndex].beginInstructionsWidth + this.rules.RhythmRightMargin;
+        }
         const measure: GraphicalMeasure = this.graphicalMusicSheet.MeasureList[measureIndex][staffIndex];
-        const startStaffEntry: GraphicalStaffEntry = measure.findGraphicalStaffEntryFromTimestamp(timeStamp);
-        const idx: VexFlowInstantaniousDynamicExpression = new VexFlowInstantaniousDynamicExpression(multiExpression.InstantaniousDynamic, startStaffEntry);
-        // idx.calculcateBottomLine(measure);
-        (measure as VexFlowMeasure).instantaniousDynamics.push(idx);
+        const graphicalInstantaniousDynamic: VexFlowInstantaniousDynamicExpression = new VexFlowInstantaniousDynamicExpression(
+          instantaniousDynamic,
+          staffLine,
+          measure);
+        (measure as VexFlowMeasure).instantaniousDynamics.push(graphicalInstantaniousDynamic);
+        this.calculateSingleGraphicalInstantaniousDynamicExpression(graphicalInstantaniousDynamic, staffLine, startPosInStaffline);
     }
   }
 
+  public calculateSingleGraphicalInstantaniousDynamicExpression(graphicalInstantaniousDynamic: VexFlowInstantaniousDynamicExpression,
+                                                                staffLine: StaffLine,
+                                                                relative: PointF2D): void {
+    // // add to StaffLine and set PSI relations
+    staffLine.AbstractExpressions.push(graphicalInstantaniousDynamic);
+    staffLine.PositionAndShape.ChildElements.push(graphicalInstantaniousDynamic.PositionAndShape);
+    if (this.staffLinesWithGraphicalExpressions.indexOf(staffLine) === -1) {
+        this.staffLinesWithGraphicalExpressions.push(staffLine);
+    }
+
+    // get Margin Dimensions
+    const left: number = relative.x + graphicalInstantaniousDynamic.PositionAndShape.BorderLeft;
+    const right: number = relative.x + graphicalInstantaniousDynamic.PositionAndShape.BorderRight;
+    const skyBottomLineCalculator: SkyBottomLineCalculator = staffLine.SkyBottomLineCalculator;
+
+    // get possible previous Dynamic
+    let previousExpression: GraphicalInstantaniousDynamicExpression = undefined;
+    const expressionIndex: number = staffLine.AbstractExpressions.indexOf(graphicalInstantaniousDynamic);
+    if (expressionIndex > 0) {
+        previousExpression = (staffLine.AbstractExpressions[expressionIndex - 1] as GraphicalInstantaniousDynamicExpression);
+    }
+
+    // TODO: Not yet implemented
+    // // is previous a ContinuousDynamic?
+    // if (previousExpression && previousExpression instanceof GraphicalContinuousDynamicExpression)
+    // {
+    //     GraphicalContinuousDynamicExpression formerGraphicalContinuousDynamic =
+    //         (GraphicalContinuousDynamicExpression)previousExpression;
+
+    //     optimizeFormerContDynamicXPositionForInstDynamic(staffLine, skyBottomLineCalculator,
+    //                                                      graphicalInstantaniousDynamic,
+    //                                                      formerGraphicalContinuousDynamic, left, right);
+    // }
+    // // is previous a instantaniousDynamic?
+    // else
+    if (previousExpression && previousExpression instanceof GraphicalInstantaniousDynamicExpression) {
+        //const formerGraphicalInstantaniousDynamic: GraphicalInstantaniousDynamicExpression = previousExpression;
+
+        // optimizeFormerInstDynamicXPositionForInstDynamic(formerGraphicalInstantaniousDynamic,
+        //                                                  graphicalInstantaniousDynamic, ref relative, ref left, ref right);
+    }// End x-positioning overlap check
+
+    // calculate yPosition according to Placement
+    if (graphicalInstantaniousDynamic.InstantaniousDynamic.Placement === PlacementEnum.Above) {
+        const skyLineValue: number = skyBottomLineCalculator.getSkyLineMinInRange(left, right);
+        let yPosition: number = 0;
+
+        // if StaffLine part of multiStafff Instrument and not the first one, ideal yPosition middle of distance between Staves
+        if (staffLine.isPartOfMultiStaffInstrument() && staffLine.ParentStaff !== staffLine.ParentStaff.ParentInstrument.Staves[0]) {
+            const formerStaffLine: StaffLine = staffLine.ParentMusicSystem.StaffLines[staffLine.ParentMusicSystem.StaffLines.indexOf(staffLine) - 1];
+            const difference: number = staffLine.PositionAndShape.RelativePosition.y -
+                               formerStaffLine.PositionAndShape.RelativePosition.y - this.rules.StaffHeight;
+
+            // take always into account the size of the Dynamic
+            if (skyLineValue > -difference / 2) {
+                yPosition = -difference / 2;
+            } else {
+                yPosition = skyLineValue - graphicalInstantaniousDynamic.PositionAndShape.BorderBottom;
+            }
+        } else {
+            yPosition = skyLineValue - graphicalInstantaniousDynamic.PositionAndShape.BorderBottom;
+        }
+
+        graphicalInstantaniousDynamic.PositionAndShape.RelativePosition = new PointF2D(relative.x, yPosition);
+        skyBottomLineCalculator.updateSkyLineInRange(left, right, yPosition + graphicalInstantaniousDynamic.PositionAndShape.BorderTop);
+    } else if (graphicalInstantaniousDynamic.InstantaniousDynamic.Placement === PlacementEnum.Below) {
+        const bottomLineValue: number = skyBottomLineCalculator.getBottomLineMaxInRange(left, right);
+        let yPosition: number = 0;
+
+        // if StaffLine part of multiStafff Instrument and not the last one, ideal yPosition middle of distance between Staves
+        const lastStaff: Staff = staffLine.ParentStaff.ParentInstrument.Staves[staffLine.ParentStaff.ParentInstrument.Staves.length - 1];
+        if (staffLine.isPartOfMultiStaffInstrument() && staffLine.ParentStaff !== lastStaff) {
+            const nextStaffLine: StaffLine = staffLine.ParentMusicSystem.StaffLines[staffLine.ParentMusicSystem.StaffLines.indexOf(staffLine) + 1];
+            const difference: number = nextStaffLine.PositionAndShape.RelativePosition.y -
+                               staffLine.PositionAndShape.RelativePosition.y - this.rules.StaffHeight;
+            const border: number = graphicalInstantaniousDynamic.PositionAndShape.BorderBottom;
+
+            // take always into account the size of the Dynamic
+            if (bottomLineValue + border < this.rules.StaffHeight + difference / 2) {
+                yPosition = this.rules.StaffHeight + difference / 2;
+            } else {
+                yPosition = bottomLineValue - graphicalInstantaniousDynamic.PositionAndShape.BorderTop;
+            }
+        } else {
+            yPosition = bottomLineValue - graphicalInstantaniousDynamic.PositionAndShape.BorderTop;
+        }
+
+        graphicalInstantaniousDynamic.PositionAndShape.RelativePosition = new PointF2D(relative.x, yPosition);
+        skyBottomLineCalculator.updateBottomLineInRange(left, right, yPosition + graphicalInstantaniousDynamic.PositionAndShape.BorderBottom);
+    }
+}
+
   /**
    * Calculate a single OctaveShift for a [[MultiExpression]].
    * @param sourceMeasure