Selaa lähdekoodia

feat(Expressions): Render "unknown" expressions (like "espressivo"), merge #766

Merge pull request #766 from opensheetmusicdisplay/fix/RenderUnknownExpressions

Fix: added rendering uncategorized/unknown expressions
Simon 5 vuotta sitten
vanhempi
commit
3f43660f2c

+ 1 - 1
src/MusicalScore/Graphical/AbstractGraphicalExpression.ts

@@ -13,7 +13,7 @@ export abstract class AbstractGraphicalExpression extends GraphicalObject {
     /** EngravingRules for positioning */
     protected rules: EngravingRules;
 
-    constructor(parentStaffline: StaffLine, expression: AbstractExpression) {
+    constructor(parentStaffline: StaffLine, expression: AbstractExpression = undefined) {
         super();
         this.expression = expression;
         this.boundingBox = new BoundingBox(this, parentStaffline.PositionAndShape);

+ 33 - 0
src/MusicalScore/Graphical/GraphicalUnknownExpression.ts

@@ -0,0 +1,33 @@
+
+import { StaffLine } from "./StaffLine";
+import { GraphicalLabel } from "./GraphicalLabel";
+import { AbstractGraphicalExpression } from "./AbstractGraphicalExpression";
+import { PlacementEnum } from "../VoiceData/Expressions";
+import { SkyBottomLineCalculator } from "./SkyBottomLineCalculator";
+import log from "loglevel";
+
+export class GraphicalUnknownExpression extends AbstractGraphicalExpression {
+    constructor(staffLine: StaffLine, label: GraphicalLabel) {
+        super(staffLine);
+        this.label = label;
+    }
+
+    public updateSkyBottomLine(): void {
+        // update Sky-BottomLine
+        const skyBottomLineCalculator: SkyBottomLineCalculator = this.parentStaffLine.SkyBottomLineCalculator;
+        const left: number = this.label.PositionAndShape.RelativePosition.x + this.label.PositionAndShape.BorderMarginLeft;
+        const right: number = this.label.PositionAndShape.RelativePosition.x + this.label.PositionAndShape.BorderMarginRight;
+        switch (this.Placement) {
+            case PlacementEnum.Above:
+                const yValueAbove: number = this.label.PositionAndShape.BorderMarginTop + this.label.PositionAndShape.RelativePosition.y;
+                skyBottomLineCalculator.updateSkyLineInRange(left, right, yValueAbove);
+                break;
+            case PlacementEnum.Below:
+                const yValueBelow: number = this.label.PositionAndShape.BorderMarginBottom + this.label.PositionAndShape.RelativePosition.y;
+                skyBottomLineCalculator.updateBottomLineInRange(left, right, yValueBelow);
+                break;
+            default:
+                log.error("Placement for GraphicalUnknownExpression is unknown");
+        }
+    }
+}

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

@@ -21,7 +21,7 @@ import { Tuplet } from "../VoiceData/Tuplet";
 import { MusicSystem } from "./MusicSystem";
 import { GraphicalTie } from "./GraphicalTie";
 import { RepetitionInstruction } from "../VoiceData/Instructions/RepetitionInstruction";
-import { MultiExpression } from "../VoiceData/Expressions/MultiExpression";
+import { MultiExpression, MultiExpressionEntry } from "../VoiceData/Expressions/MultiExpression";
 import { StaffEntryLink } from "../VoiceData/StaffEntryLink";
 import { MusicSystemBuilder } from "./MusicSystemBuilder";
 import { MultiTempoExpression } from "../VoiceData/Expressions/MultiTempoExpression";
@@ -67,6 +67,7 @@ import { GraphicalInstantaneousDynamicExpression } from "./GraphicalInstantaneou
 import { ContDynamicEnum } from "../VoiceData/Expressions/ContinuousExpressions/ContinuousDynamicExpression";
 import { GraphicalContinuousDynamicExpression } from "./GraphicalContinuousDynamicExpression";
 import { FillEmptyMeasuresWithWholeRests } from "../../OpenSheetMusicDisplay/OSMDOptions";
+import { GraphicalUnknownExpression } from "./GraphicalUnknownExpression";
 
 /**
  * Class used to do all the calculations in a MusicSheet, which in the end populates a GraphicalMusicSheet.
@@ -543,7 +544,46 @@ export abstract class MusicSheetCalculator {
      * @param staffIndex
      */
     protected calculateMoodAndUnknownExpression(multiExpression: MultiExpression, measureIndex: number, staffIndex: number): void {
-        throw new Error("abstract, not implemented");
+        // calculate absolute Timestamp
+        const absoluteTimestamp: Fraction = multiExpression.AbsoluteTimestamp;
+        const measures: GraphicalMeasure[] = this.graphicalMusicSheet.MeasureList[measureIndex];
+        let relative: PointF2D = new PointF2D();
+
+        if ((multiExpression.MoodList.length > 0) || (multiExpression.UnknownList.length > 0)) {
+        let combinedExprString: string  = "";
+        for (let idx: number = 0, len: number = multiExpression.EntriesList.length; idx < len; ++idx) {
+            const entry: MultiExpressionEntry = multiExpression.EntriesList[idx];
+            if (entry.prefix !== "") {
+                if (combinedExprString === "") {
+                    combinedExprString += entry.prefix;
+                } else {
+                    combinedExprString += " " + entry.prefix;
+                }
+            }
+            if (combinedExprString === "") {
+                combinedExprString += entry.label;
+            } else {
+                combinedExprString += " " + entry.label;
+            }
+        }
+        const staffLine: StaffLine = measures[staffIndex].ParentStaffLine;
+        relative = this.getRelativePositionInStaffLineFromTimestamp(absoluteTimestamp, staffIndex, staffLine, staffLine.isPartOfMultiStaffInstrument());
+
+        if (Math.abs(relative.x - 0) < 0.0001) {
+            relative.x = measures[staffIndex].beginInstructionsWidth + this.rules.RhythmRightMargin;
+        }
+
+        const fontHeight: number = this.rules.UnknownTextHeight;
+
+        const graphLabel: GraphicalLabel  = this.calculateLabel(staffLine,
+                                                                relative, combinedExprString,
+                                                                multiExpression.getFontstyleOfFirstEntry(),
+                                                                multiExpression.getPlacementOfFirstEntry(),
+                                                                fontHeight);
+
+        const gue: GraphicalUnknownExpression = new GraphicalUnknownExpression(staffLine, graphLabel);
+        staffLine.AbstractExpressions.push(gue);
+        }
     }
 
     /**

+ 9 - 12
src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetCalculator.ts

@@ -691,26 +691,23 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
     }
   }
 
-  protected calculateMoodAndUnknownExpression(multiExpression: MultiExpression, measureIndex: number, staffIndex: number): void {
-    return;
-  }
-
   /**
    * Re-adjust the x positioning of expressions. Update the skyline afterwards
    */
   protected calculateExpressionAlignements(): void {
     for (const musicSystem of this.musicSystems) {
-            for (const staffLine of musicSystem.StaffLines) {
-              try {
-                (<VexFlowStaffLine>staffLine).AlignmentManager.alignDynamicExpressions();
-                staffLine.AbstractExpressions.forEach(ae => ae.updateSkyBottomLine());
-              } catch (e) {
-                // TODO still necessary when calculation of expression fails, see calculateDynamicExpressionsForMultiExpression()
-                //   see calculateGraphicalContinuousDynamic(), also in MusicSheetCalculator.
+      for (const staffLine of musicSystem.StaffLines) {
+        try {
+          (<VexFlowStaffLine>staffLine).AlignmentManager.alignDynamicExpressions();
+          staffLine.AbstractExpressions.forEach(ae => ae.updateSkyBottomLine());
+        } catch (e) {
+          // TODO still necessary when calculation of expression fails, see calculateDynamicExpressionsForMultiExpression()
+          //   see calculateGraphicalContinuousDynamic(), also in MusicSheetCalculator.
         }
+      }
     }
   }
-  }
+
 
   /**
    * Check if the tied graphical note belongs to any beams or tuplets and react accordingly.

+ 5 - 7
src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetDrawer.ts

@@ -27,6 +27,7 @@ import { VexFlowContinuousDynamicExpression } from "./VexFlowContinuousDynamicEx
 import { DrawingParameters } from "../DrawingParameters";
 import { GraphicalMusicPage } from "../GraphicalMusicPage";
 import { GraphicalMusicSheet } from "../GraphicalMusicSheet";
+import { GraphicalUnknownExpression } from "../GraphicalUnknownExpression";
 
 /**
  * This is a global constant which denotes the height in pixels of the space between two lines of the stave
@@ -356,13 +357,10 @@ export class VexFlowMusicSheetDrawer extends MusicSheetDrawer {
                 // // Draw Mood
                 // } else if (abstractGraphicalExpression instanceof GraphicalMoodExpression) {
                 //     GraphicalMoodExpression; graphicalMood = (GraphicalMoodExpression); abstractGraphicalExpression;
-                //     drawLabel(graphicalMood.GetGraphicalLabel, (int)GraphicalLayers.Notes);
-                // // Draw Unknown
-                // } else if (abstractGraphicalExpression instanceof GraphicalUnknownExpression) {
-                //     GraphicalUnknownExpression; graphicalUnknown =
-                //         (GraphicalUnknownExpression); abstractGraphicalExpression;
-                //     drawLabel(graphicalUnknown.GetGraphicalLabel, (int)GraphicalLayers.Notes);
-                // }
+                //     drawLabel(graphicalMood.GetGraphicalLabel, <number>GraphicalLayers.Notes);
+            // Draw Unknown
+            } else if (abstractGraphicalExpression instanceof GraphicalUnknownExpression) {
+                this.drawLabel(abstractGraphicalExpression.Label, <number>GraphicalLayers.Notes);
             } else {
                 log.warn("Unkown type of expression!");
             }

+ 10 - 7
src/MusicalScore/ScoreIO/MusicSymbolModules/ExpressionReader.ts

@@ -419,11 +419,11 @@ export class ExpressionReader {
         }
         const tmpInputString: string = inputString.trim();
         // split string at enumerating words or signs
-        const splitStrings: string[] = tmpInputString.split(/([\s,\r\n]and[\s,\r\n]|[\s,\r\n]und[\s,\r\n]|[\s,\r\n]e[\s,\r\n]|[\s,\r\n])+/g);
+        //const splitStrings: string[] = tmpInputString.split(/([\s,\r\n]and[\s,\r\n]|[\s,\r\n]und[\s,\r\n]|[\s,\r\n]e[\s,\r\n]|[\s,\r\n])+/g);
 
-        for (const splitStr of splitStrings) {
-            this.createExpressionFromString("", splitStr, currentMeasure, inputString);
-        }
+        //for (const splitStr of splitStrings) {
+        this.createExpressionFromString("", tmpInputString, currentMeasure, inputString);
+        //}
     }
     /*
     private splitStringRecursive(input: [string, string], stringSeparators: string[]): [string, string][] {
@@ -528,6 +528,7 @@ export class ExpressionReader {
 
         // create unknown:
         this.createNewMultiExpressionIfNeeded(currentMeasure);
+        // check here first if there might be a tempo expression doublette:
         if (currentMeasure.TempoExpressions.length > 0) {
             for (let idx: number = 0, len: number = currentMeasure.TempoExpressions.length; idx < len; ++idx) {
                 const multiTempoExpression: MultiTempoExpression = currentMeasure.TempoExpressions[idx];
@@ -535,9 +536,11 @@ export class ExpressionReader {
                     multiTempoExpression.InstantaneousTempo !== undefined &&
                     multiTempoExpression.EntriesList.length > 0 &&
                     !this.hasDigit(stringTrimmed)) {
-                    if (this.globalStaffIndex > 0) {
-                        if (multiTempoExpression.EntriesList[0].label.indexOf(stringTrimmed) >= 0) {
-                            return false;
+                        // if at other parts of the score
+                        if (this.globalStaffIndex > 0) {
+                            // don't add duplicate TempoExpression
+                            if (multiTempoExpression.EntriesList[0].label.indexOf(stringTrimmed) >= 0) {
+                                return false;
                         } else {
                             break;
                         }

+ 15 - 13
src/MusicalScore/VoiceData/Expressions/MultiExpression.ts

@@ -7,6 +7,7 @@ import {MoodExpression} from "./MoodExpression";
 import {UnknownExpression} from "./UnknownExpression";
 import {AbstractExpression} from "./AbstractExpression";
 import {PlacementEnum} from "./AbstractExpression";
+import { FontStyles } from "../../../Common/Enums/FontStyles";
 
 export class MultiExpression {
 
@@ -109,19 +110,20 @@ export class MultiExpression {
         }
         return placement;
     }
-    // (*)
-    //public getFontstyleOfFirstEntry(): PSFontStyles {
-    //    let fontStyle: PSFontStyles = PSFontStyles.Regular;
-    //    if (this.expressions.length > 0) {
-    //        if (this.expressions[0].expression instanceof ContinuousDynamicExpression)
-    //            fontStyle = PSFontStyles.Italic;
-    //        else if (this.expressions[0].expression instanceof MoodExpression)
-    //            fontStyle = PSFontStyles.Italic;
-    //        else if (this.expressions[0].expression instanceof UnknownExpression)
-    //            fontStyle = PSFontStyles.Regular;
-    //    }
-    //    return fontStyle;
-    //}
+
+    public getFontstyleOfFirstEntry(): FontStyles {
+       let fontStyle: FontStyles = FontStyles.Regular;
+       if (this.expressions.length > 0) {
+           if (this.expressions[0].expression instanceof ContinuousDynamicExpression) {
+            fontStyle = FontStyles.Italic;
+           } else if (this.expressions[0].expression instanceof MoodExpression) {
+            fontStyle = FontStyles.Italic;
+           } else if (this.expressions[0].expression instanceof UnknownExpression) {
+            fontStyle = FontStyles.Regular;
+           }
+       }
+       return fontStyle;
+    }
     //public getFirstEntry(staffLine: StaffLine, graphLabel: GraphicalLabel): AbstractGraphicalExpression {
     //    let indexOfFirstNotInstDynExpr: number = 0;
     //    if (this.expressions[0].expression instanceof InstantaneousDynamicExpression)

+ 14 - 0
src/MusicalScore/VoiceData/Expressions/MultiTempoExpression.ts

@@ -81,6 +81,10 @@ export class MultiTempoExpression {
     //    return undefined;
     //}
     public addExpression(abstractTempoExpression: AbstractTempoExpression, prefix: string): void {
+        if (this.checkIfAlreadyExists(abstractTempoExpression)) {
+            return;
+        }
+
         if (abstractTempoExpression instanceof InstantaneousTempoExpression) {
             this.instantaneousTempo = <InstantaneousTempoExpression>abstractTempoExpression;
         } else if (abstractTempoExpression instanceof ContinuousTempoExpression) {
@@ -102,6 +106,16 @@ export class MultiTempoExpression {
             return 0;
         }
     }
+
+    private checkIfAlreadyExists(abstractTempoExpression: AbstractTempoExpression ): boolean {
+        for (const entry of this.expressions) {
+            if (entry.label === abstractTempoExpression.Label) {
+                return true;
+            }
+        }
+
+        return false;
+    }
 }
 
 export class TempoExpressionEntry {