Forráskód Böngészése

feat(Tempo Expressions): All tempo expressions

#309
Benjamin Giesinger 6 éve
szülő
commit
83d59c8241

+ 8 - 8
src/MusicalScore/Graphical/BoundingBox.ts

@@ -145,7 +145,7 @@ export class BoundingBox {
     }
 
     public get BorderMarginLeft(): number {
-        return this.borderMarginLeft;
+        return this.borderMarginLeft > this.borderLeft ? this.borderLeft : this.borderMarginLeft;
     }
 
     public set BorderMarginLeft(value: number) {
@@ -154,7 +154,7 @@ export class BoundingBox {
     }
 
     public get BorderMarginRight(): number {
-        return this.borderMarginRight;
+        return this.borderMarginRight < this.borderRight ? this.borderRight : this.borderMarginRight;
     }
 
     public set BorderMarginRight(value: number) {
@@ -163,7 +163,7 @@ export class BoundingBox {
     }
 
     public get BorderMarginTop(): number {
-        return this.borderMarginTop;
+        return this.borderMarginTop > this.borderTop ? this.borderTop : this.borderMarginTop;
     }
 
     public set BorderMarginTop(value: number) {
@@ -172,7 +172,7 @@ export class BoundingBox {
     }
 
     public get BorderMarginBottom(): number {
-        return this.borderMarginBottom;
+        return this.borderMarginBottom < this.borderBottom ? this.borderBottom : this.borderMarginBottom;
     }
 
     public set BorderMarginBottom(value: number) {
@@ -569,14 +569,14 @@ export class BoundingBox {
     }
 
     protected calculateRectangle(): void {
-        this.upperLeftCorner = new PointF2D(this.borderLeft, this.borderTop);
-        this.size = new SizeF2D(this.borderRight - this.borderLeft, this.borderBottom - this.borderTop);
+        this.upperLeftCorner = new PointF2D(this.BorderLeft, this.BorderTop);
+        this.size = new SizeF2D(this.BorderRight - this.BorderLeft, this.BorderBottom - this.BorderTop);
         this.boundingRectangle = RectangleF2D.createFromLocationAndSize(this.upperLeftCorner, this.size);
     }
 
     protected calculateMarginRectangle(): void {
-        this.upperLeftMarginCorner = new PointF2D(this.borderMarginLeft, this.borderMarginTop);
-        this.marginSize = new SizeF2D(this.borderMarginRight - this.borderMarginLeft, this.borderMarginBottom - this.borderMarginTop);
+        this.upperLeftMarginCorner = new PointF2D(this.BorderMarginLeft, this.BorderMarginTop);
+        this.marginSize = new SizeF2D(this.BorderMarginRight - this.BorderMarginLeft, this.BorderMarginBottom - this.BorderMarginTop);
         this.boundingMarginRectangle = RectangleF2D.createFromLocationAndSize(this.upperLeftMarginCorner, this.marginSize);
     }
 

+ 25 - 0
src/MusicalScore/Graphical/GraphicalInstantaneousTempoExpression.ts

@@ -0,0 +1,25 @@
+import { GraphicalObject } from "./GraphicalObject";
+import { StaffLine } from "./StaffLine";
+import { AbstractTempoExpression } from "../VoiceData/Expressions/AbstractTempoExpression";
+import { GraphicalLabel } from "./GraphicalLabel";
+
+export class GraphicalInstantaneousTempoExpression extends GraphicalObject {
+    protected mTempoExpresssion: AbstractTempoExpression;
+    protected mParentStaffLine: StaffLine;
+    protected mLabel: GraphicalLabel;
+
+    constructor(tempoExpresssion: AbstractTempoExpression, label: GraphicalLabel) {
+        super();
+        // this.boundingBox = new BoundingBox(this, staffLine.PositionAndShape);
+        this.mTempoExpresssion = tempoExpresssion;
+        this.mLabel = label;
+    }
+
+    public get InstantaneousTempoExpression(): AbstractTempoExpression {
+        return this.mTempoExpresssion;
+    }
+
+    public get GraphicalLabel(): GraphicalLabel {
+        return this.mLabel;
+    }
+}

+ 161 - 4
src/MusicalScore/Graphical/MusicSheetCalculator.ts

@@ -56,6 +56,13 @@ import { Label } from "../Label";
 import { GraphicalVoiceEntry } from "./GraphicalVoiceEntry";
 import { VerticalSourceStaffEntryContainer } from "../VoiceData/VerticalSourceStaffEntryContainer";
 import { SkyBottomLineCalculator } from "./SkyBottomLineCalculator";
+import { PlacementEnum } from "../VoiceData/Expressions/AbstractExpression";
+import { AbstractGraphicalInstruction } from "./AbstractGraphicalInstruction";
+import { GraphicalObject } from "./GraphicalObject";
+import { GraphicalInstantaneousTempoExpression } from "./GraphicalInstantaneousTempoExpression";
+import { InstantaneousTempoExpression, TempoEnum } from "../VoiceData/Expressions/InstantaneousTempoExpression";
+import { ContinuousTempoExpression } from "../VoiceData/Expressions/ContinuousExpressions/ContinuousTempoExpression";
+import { FontStyles } from "../../Common/Enums/FontStyles";
 
 /**
  * Class used to do all the calculations in a MusicSheet, which in the end populates a GraphicalMusicSheet.
@@ -885,9 +892,159 @@ export abstract class MusicSheetCalculator {
         return;
     }
 
-    protected calculateTempoExpressionsForSingleMultiTempoExpression(sourceMeasure: SourceMeasure, multiTempoExpression: MultiTempoExpression,
-                                                                     measureIndex: number): void {
-        return;
+    protected calculateLabel(staffLine: StaffLine,
+                             relative: PointF2D,
+                             combinedString: string,
+                             style: FontStyles,
+                             placement: PlacementEnum,
+                             fontHeight: number): GraphicalLabel {
+        const label: Label = new Label(combinedString);
+        label.fontHeight = fontHeight;
+
+        // TODO_RR: TextHeight from first Entry
+        const graphLabel: GraphicalLabel = new GraphicalLabel(label, fontHeight, TextAlignment.CenterBottom, staffLine.PositionAndShape);
+        graphLabel.Label.fontStyle = style;
+        const marginFactor: number = 1.1;
+
+        if (placement === PlacementEnum.Below) {
+            graphLabel.Label.textAlignment = TextAlignment.LeftTop;
+        }
+
+        graphLabel.setLabelPositionAndShapeBorders();
+        graphLabel.PositionAndShape.BorderMarginBottom *= marginFactor;
+        graphLabel.PositionAndShape.BorderMarginTop *= marginFactor;
+        graphLabel.PositionAndShape.BorderMarginLeft *= marginFactor;
+        graphLabel.PositionAndShape.BorderMarginRight *= marginFactor;
+
+        let left: number = relative.x + graphLabel.PositionAndShape.BorderMarginLeft;
+        let right: number = relative.x + graphLabel.PositionAndShape.BorderMarginRight;
+
+        // check if GraphicalLabel exceeds the StaffLine's borders.
+        if (right > staffLine.PositionAndShape.Size.width) {
+            right = staffLine.PositionAndShape.Size.width - this.rules.MeasureRightMargin;
+            left = right - graphLabel.PositionAndShape.MarginSize.width;
+            relative.x = left - graphLabel.PositionAndShape.BorderMarginLeft;
+        }
+
+        // find allowed position (where the Label can be positioned) from Sky- BottomLine
+        let drawingHeight: number;
+        const skyBottomLineCalculator: SkyBottomLineCalculator = staffLine.SkyBottomLineCalculator;
+        if (placement === PlacementEnum.Below) {
+            drawingHeight = skyBottomLineCalculator.getBottomLineMaxInRange(left, right);
+        } else {
+            drawingHeight = skyBottomLineCalculator.getSkyLineMinInRange(left, right);
+        }
+
+        // set RelativePosition
+        graphLabel.PositionAndShape.RelativePosition = new PointF2D(relative.x, drawingHeight);
+
+        // update Sky- BottomLine
+        if (placement === PlacementEnum.Below) {
+            skyBottomLineCalculator.updateBottomLineInRange(left, right, graphLabel.PositionAndShape.BorderMarginBottom + drawingHeight);
+        } else {
+            skyBottomLineCalculator.updateSkyLineInRange(left, right, graphLabel.PositionAndShape.BorderMarginTop + drawingHeight);
+        }
+        return graphLabel;
+    }
+
+    protected calculateTempoExpressionsForMultiTempoExpression(sourceMeasure: SourceMeasure, multiTempoExpression: MultiTempoExpression,
+                                                               measureIndex: number): void {
+        // calculate absolute Timestamp
+        const absoluteTimestamp: Fraction = Fraction.plus(sourceMeasure.AbsoluteTimestamp, multiTempoExpression.Timestamp);
+        const measures: GraphicalMeasure[] = this.graphicalMusicSheet.MeasureList[measureIndex];
+        let relative: PointF2D = new PointF2D();
+
+        if (multiTempoExpression.ContinuousTempo || multiTempoExpression.InstantaneousTempo) {
+            // TempoExpressions always on the first visible System's StaffLine
+            let staffLine: StaffLine = measures[0].ParentStaffLine;
+            let firstVisibleMeasureX: number = measures[0].PositionAndShape.RelativePosition.x;
+            let verticalIndex: number = 0;
+            for (let j: number = 0; j < measures.length; j++) {
+                if (!measures[j].ParentStaffLine || measures[j].ParentStaffLine.Measures.length === 0) {
+                    continue;
+                }
+
+                if (measures[j].ParentStaffLine.Measures.length > 0) {
+                    staffLine = measures[j].ParentStaffLine;
+                    firstVisibleMeasureX = measures[j].PositionAndShape.RelativePosition.x;
+                    verticalIndex = j;
+                    break;
+                }
+            }
+            relative = this.getRelativePositionInStaffLineFromTimestamp(absoluteTimestamp,
+                                                                        verticalIndex,
+                                                                        staffLine,
+                                                                        staffLine.isPartOfMultiStaffInstrument(),
+                                                                        firstVisibleMeasureX);
+
+            // also placement Above
+            if (multiTempoExpression.EntriesList.length > 0 &&
+                multiTempoExpression.EntriesList[0].Expression instanceof InstantaneousTempoExpression) {
+                const instantaniousTempo: InstantaneousTempoExpression = (multiTempoExpression.EntriesList[0].Expression as InstantaneousTempoExpression);
+                instantaniousTempo.Placement = PlacementEnum.Above;
+
+                // if an InstantaniousTempoExpression exists at the very beginning then
+                // check if expression is positioned at ever first StaffEntry and
+                // check if MusicSystem is first MusicSystem
+                if (staffLine.Measures[0].staffEntries.length > 0 &&
+                    Math.abs(relative.x - staffLine.Measures[0].staffEntries[0].PositionAndShape.RelativePosition.x) === 0 &&
+                    staffLine.ParentMusicSystem === staffLine.ParentMusicSystem.Parent.MusicSystems[0]) {
+                    const firstInstructionEntry: GraphicalStaffEntry = staffLine.Measures[0].FirstInstructionStaffEntry;
+                    if (firstInstructionEntry) {
+                        const lastIntruction: AbstractGraphicalInstruction = firstInstructionEntry.GraphicalInstructions.last();
+                        relative.x = lastIntruction.PositionAndShape.RelativePosition.x;
+                    }
+                }
+            }
+
+            // const addAtLastList: GraphicalObject[] = [];
+            for (const entry of multiTempoExpression.EntriesList) {
+                const graphLabel: GraphicalLabel = this.calculateLabel(staffLine,
+                                                                       relative,
+                                                                       entry.label,
+                                                                       multiTempoExpression.getFontstyleOfFirstEntry(),
+                                                                       entry.Expression.Placement,
+                                                                       EngravingRules.Rules.UnknownTextHeight);
+
+                if (entry.Expression instanceof InstantaneousTempoExpression) {
+                    let alreadyAdded: boolean = false;
+                    for (const expr of staffLine.AbstractExpressions) {
+                        if (expr instanceof GraphicalInstantaneousTempoExpression &&
+                           (expr as GraphicalInstantaneousTempoExpression).InstantaneousTempoExpression.Label === entry.Expression.Label) {
+                            alreadyAdded = true;
+                        }
+                    }
+
+                    if (alreadyAdded) {
+                        continue;
+                    }
+
+                    const graphicalTempoExpr: GraphicalInstantaneousTempoExpression = new GraphicalInstantaneousTempoExpression(entry.Expression, graphLabel);
+                    // in case of metronome mark:
+                    if ((entry.Expression as InstantaneousTempoExpression).Enum === TempoEnum.metronomeMark) {
+                        // use smaller font:
+                        graphLabel.Label.fontHeight = 1.2;
+                    }
+
+                    staffLine.AbstractExpressions.push(graphicalTempoExpr);
+                } else if (entry.Expression instanceof ContinuousTempoExpression) {
+                    // FIXME: Not yet implemented
+                    // let alreadyAdded: boolean = false;
+                    // for (const expr of staffLine.AbstractExpressions) {
+                    //     if (expr instanceof GraphicalContinuousTempoExpression &&
+                    //         expr.GetContinuousTempoExpression.Label === entry.Expression.Label) {
+                    //         alreadyAdded = true;
+                    //     }
+                    // }
+
+                    // if (alreadyAdded) {
+                    //     continue;
+                    // }
+
+                    // staffLine.AbstractExpressions.push(new GraphicalContinuousTempoExpression((ContinuousTempoExpression)(entry.Expression), graphLabel));
+                }
+            }
+        }
     }
 
     protected graphicalMeasureCreatedCalculations(measure: GraphicalMeasure): void {
@@ -2044,7 +2201,7 @@ export abstract class MusicSheetCalculator {
         for (let i: number = 0; i < this.graphicalMusicSheet.ParentMusicSheet.SourceMeasures.length; i++) {
             const sourceMeasure: SourceMeasure = this.graphicalMusicSheet.ParentMusicSheet.SourceMeasures[i];
             for (let j: number = 0; j < sourceMeasure.TempoExpressions.length; j++) {
-                this.calculateTempoExpressionsForSingleMultiTempoExpression(sourceMeasure, sourceMeasure.TempoExpressions[j], i);
+                this.calculateTempoExpressionsForMultiTempoExpression(sourceMeasure, sourceMeasure.TempoExpressions[j], i);
             }
         }
     }

+ 12 - 7
src/MusicalScore/Graphical/MusicSheetDrawer.ts

@@ -23,6 +23,7 @@ import {GraphicalMusicPage} from "./GraphicalMusicPage";
 import {Instrument} from "../Instrument";
 import {MusicSymbolDrawingStyle, PhonicScoreModes} from "./DrawingMode";
 import {GraphicalObject} from "./GraphicalObject";
+import { GraphicalInstantaneousDynamicExpression } from "./GraphicalInstantaneousDynamicExpression";
 
 /**
  * Draw a [[GraphicalMusicSheet]] (through the .drawSheet method)
@@ -354,7 +355,7 @@ export abstract class MusicSheetDrawer {
 
         this.drawOctaveShifts(staffLine);
 
-        this.drawInstantaneousDynamic(staffLine);
+        this.drawExpressions(staffLine);
 
         if (this.skyLineVisible) {
             this.drawSkyLine(staffLine);
@@ -378,6 +379,10 @@ export abstract class MusicSheetDrawer {
         });
     }
 
+    protected drawExpressions(staffline: StaffLine): void {
+        // implemented by subclass (VexFlowMusicSheetDrawer)
+    }
+
     protected drawGraphicalLine(graphicalLine: GraphicalLine, lineWidth: number, colorOrStyle: string = "black"): void {
         /* TODO similar checks as in drawLabel
         if (!this.isVisible(new BoundingBox(graphicalLine.Start,)) {
@@ -425,7 +430,7 @@ export abstract class MusicSheetDrawer {
     //         drawLineAsVerticalRectangle(ending.Right, absolutePosition, <number>GraphicalLayers.Notes);
     //     this.drawLabel(ending.Label, <number>GraphicalLayers.Notes);
     // }
-    protected drawInstantaneousDynamic(staffline: StaffLine): void {
+    protected drawInstantaneousDynamic(instantaneousDynamic: GraphicalInstantaneousDynamicExpression): void {
         // expression.ExpressionSymbols.forEach(function (expressionSymbol) {
         //     let position: PointF2D = expressionSymbol.PositionAndShape.AbsolutePosition;
         //     let symbol: MusicSymbol = expressionSymbol.GetSymbol;
@@ -480,11 +485,11 @@ export abstract class MusicSheetDrawer {
      */
     private drawBoundingBoxes(startBox: BoundingBox, layer: number = 0, type: string = "all"): void {
         const dataObjectString: string = (startBox.DataObject.constructor as any).name;
-        if (startBox.BoundingRectangle !== undefined && (dataObjectString === type || type === "all")) {
-            const relBoundingRect: RectangleF2D = startBox.BoundingRectangle;
-            let tmpRect: RectangleF2D = new RectangleF2D(startBox.AbsolutePosition.x + startBox.BorderLeft,
-                                                         startBox.AbsolutePosition.y + startBox.BorderTop ,
-                                                         (relBoundingRect.width + 0), (relBoundingRect.height + 0));
+        if (dataObjectString === type || type === "all") {
+            let tmpRect: RectangleF2D = new RectangleF2D(startBox.AbsolutePosition.x + startBox.BorderMarginLeft,
+                                                         startBox.AbsolutePosition.y + startBox.BorderMarginTop,
+                                                         startBox.BorderMarginRight - startBox.BorderMarginLeft,
+                                                         startBox.BorderMarginBottom - startBox.BorderMarginTop);
             this.drawLineAsHorizontalRectangle(new GraphicalLine(
                                                              new PointF2D(startBox.AbsolutePosition.x - 1, startBox.AbsolutePosition.y),
                                                              new PointF2D(startBox.AbsolutePosition.x + 1, startBox.AbsolutePosition.y),

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

@@ -329,6 +329,7 @@ export class VexFlowMeasure extends GraphicalMeasure {
             if (this.vfVoices.hasOwnProperty(voiceID)) {
                 this.vfVoices[voiceID].draw(ctx, this.stave);
                 // this.vfVoices[voiceID].tickables.forEach(t => t.getBoundingBox().draw(ctx));
+                // this.vfVoices[voiceID].tickables.forEach(t => t.getBoundingBox().draw(ctx));
             }
         }
         // Draw beams

+ 13 - 13
src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetCalculator.ts

@@ -379,13 +379,13 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
           staffLine,
           measure);
         (measure as VexFlowMeasure).instantaneousDynamics.push(graphicalInstantaneousDynamic);
-        this.calculateSingleGraphicalInstantaneousDynamicExpression(graphicalInstantaneousDynamic, staffLine, startPosInStaffline);
+        this.calculateGraphicalInstantaneousDynamicExpression(graphicalInstantaneousDynamic, staffLine, startPosInStaffline);
     }
   }
 
-  public calculateSingleGraphicalInstantaneousDynamicExpression(graphicalInstantaneousDynamic: VexFlowInstantaneousDynamicExpression,
-                                                                staffLine: StaffLine,
-                                                                relative: PointF2D): void {
+  public calculateGraphicalInstantaneousDynamicExpression(graphicalInstantaneousDynamic: VexFlowInstantaneousDynamicExpression,
+                                                          staffLine: StaffLine,
+                                                          relative: PointF2D): void {
     // // add to StaffLine and set PSI relations
     staffLine.AbstractExpressions.push(graphicalInstantaneousDynamic);
     staffLine.PositionAndShape.ChildElements.push(graphicalInstantaneousDynamic.PositionAndShape);
@@ -394,8 +394,8 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
     }
 
     // get Margin Dimensions
-    const left: number = relative.x + graphicalInstantaneousDynamic.PositionAndShape.BorderLeft;
-    const right: number = relative.x + graphicalInstantaneousDynamic.PositionAndShape.BorderRight;
+    const left: number = relative.x + graphicalInstantaneousDynamic.PositionAndShape.BorderMarginLeft;
+    const right: number = relative.x + graphicalInstantaneousDynamic.PositionAndShape.BorderMarginRight;
     const skyBottomLineCalculator: SkyBottomLineCalculator = staffLine.SkyBottomLineCalculator;
 
     // get possible previous Dynamic
@@ -440,14 +440,14 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
             if (skyLineValue > -difference / 2) {
                 yPosition = -difference / 2;
             } else {
-                yPosition = skyLineValue - graphicalInstantaneousDynamic.PositionAndShape.BorderBottom;
+                yPosition = skyLineValue - graphicalInstantaneousDynamic.PositionAndShape.BorderMarginBottom;
             }
         } else {
-            yPosition = skyLineValue - graphicalInstantaneousDynamic.PositionAndShape.BorderBottom;
+            yPosition = skyLineValue - graphicalInstantaneousDynamic.PositionAndShape.BorderMarginBottom;
         }
 
         graphicalInstantaneousDynamic.PositionAndShape.RelativePosition = new PointF2D(relative.x, yPosition);
-        skyBottomLineCalculator.updateSkyLineInRange(left, right, yPosition + graphicalInstantaneousDynamic.PositionAndShape.BorderTop);
+        skyBottomLineCalculator.updateSkyLineInRange(left, right, yPosition + graphicalInstantaneousDynamic.PositionAndShape.BorderMarginTop);
     } else if (graphicalInstantaneousDynamic.InstantaneousDynamic.Placement === PlacementEnum.Below) {
         const bottomLineValue: number = skyBottomLineCalculator.getBottomLineMaxInRange(left, right);
         let yPosition: number = 0;
@@ -458,20 +458,20 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
             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 = graphicalInstantaneousDynamic.PositionAndShape.BorderBottom;
+            const border: number = graphicalInstantaneousDynamic.PositionAndShape.BorderMarginBottom;
 
             // 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 - graphicalInstantaneousDynamic.PositionAndShape.BorderTop;
+                yPosition = bottomLineValue - graphicalInstantaneousDynamic.PositionAndShape.BorderMarginTop;
             }
         } else {
-            yPosition = bottomLineValue - graphicalInstantaneousDynamic.PositionAndShape.BorderTop;
+            yPosition = bottomLineValue - graphicalInstantaneousDynamic.PositionAndShape.BorderMarginTop;
         }
 
         graphicalInstantaneousDynamic.PositionAndShape.RelativePosition = new PointF2D(relative.x, yPosition);
-        skyBottomLineCalculator.updateBottomLineInRange(left, right, yPosition + graphicalInstantaneousDynamic.PositionAndShape.BorderBottom);
+        skyBottomLineCalculator.updateBottomLineInRange(left, right, yPosition + graphicalInstantaneousDynamic.PositionAndShape.BorderMarginBottom);
     }
 }
 

+ 34 - 4
src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetDrawer.ts

@@ -17,6 +17,9 @@ import {VexFlowInstrumentBrace} from "./VexFlowInstrumentBrace";
 import {GraphicalLyricEntry} from "../GraphicalLyricEntry";
 import {StaffLine} from "../StaffLine";
 import {EngravingRules} from "../EngravingRules";
+import {GraphicalInstantaneousTempoExpression} from "../GraphicalInstantaneousTempoExpression";
+import {GraphicalInstantaneousDynamicExpression} from "../GraphicalInstantaneousDynamicExpression";
+import log = require("loglevel");
 
 /**
  * This is a global constant which denotes the height in pixels of the space between two lines of the stave
@@ -219,14 +222,41 @@ export class VexFlowMusicSheetDrawer extends MusicSheetDrawer {
         }
     }
 
-    protected drawInstantaneousDynamic(staffline: StaffLine): void {
-        for (const m of staffline.Measures as VexFlowMeasure[]) {
-            for (const idx of m.instantaneousDynamics as VexFlowInstantaneousDynamicExpression[]) {
-                this.drawLabel(idx.Label, <number>GraphicalLayers.Notes);
+    protected drawExpressions(staffline: StaffLine): void {
+        // Draw all Expressions
+        for (const abstractGraphicalExpression of staffline.AbstractExpressions) {
+            // Draw InstantaniousDynamics
+            if (abstractGraphicalExpression instanceof GraphicalInstantaneousDynamicExpression) {
+                this.drawInstantaneousDynamic((abstractGraphicalExpression as VexFlowInstantaneousDynamicExpression));
+            // Draw InstantaniousTempo
+            } else if (abstractGraphicalExpression instanceof GraphicalInstantaneousTempoExpression) {
+                this.drawLabel((abstractGraphicalExpression as GraphicalInstantaneousTempoExpression).GraphicalLabel, GraphicalLayers.Notes);
+            // // Draw ContinuousDynamics
+            // } else if (abstractGraphicalExpression instanceof GraphicalContinuousDynamicExpression) {
+            // //     drawContinuousDynamic((GraphicalContinuousDynamicExpression)abstractGraphicalExpression, absolutePos);
+            // // Draw ContinuousTempo
+            // } else if (abstractGraphicalExpression instanceof GraphicalContinuousTempoExpression) {
+            //     this.drawLabel((abstractGraphicalExpression as GraphicalContinuousTempoExpression).GraphicalLabel, GraphicalLayers.Notes);
+            // // 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);
+            // }
+            } else {
+                log.warn("Unkown type of expression!");
             }
         }
     }
 
+    protected drawInstantaneousDynamic(instantaneousDynamic: GraphicalInstantaneousDynamicExpression): void {
+        this.drawLabel((instantaneousDynamic as VexFlowInstantaneousDynamicExpression).Label, <number>GraphicalLayers.Notes);
+    }
+
     /**
      * Renders a Label to the screen (e.g. Title, composer..)
      * @param graphicalLabel holds the label string, the text height in units and the font parameters

+ 1 - 6
src/MusicalScore/ScoreIO/MusicSymbolModules/ExpressionReader.ts

@@ -12,16 +12,11 @@ import {SourceMeasure} from "../../VoiceData/SourceMeasure";
 import {InstantaneousTempoExpression} from "../../VoiceData/Expressions/InstantaneousTempoExpression";
 import {MoodExpression} from "../../VoiceData/Expressions/MoodExpression";
 import {UnknownExpression} from "../../VoiceData/Expressions/UnknownExpression";
+import {PlacementEnum} from "../../VoiceData/Expressions/AbstractExpression";
 import {TextAlignment} from "../../../Common/Enums/TextAlignment";
 import {ITextTranslation} from "../../Interfaces/ITextTranslation";
 import * as log from "loglevel";
 
-export enum PlacementEnum {
-    Above = 0,
-    Below = 1,
-    NotYetDefined = 2
-}
-
 export class ExpressionReader {
     private musicSheet: MusicSheet;
     private placement: PlacementEnum;

+ 12 - 11
src/MusicalScore/VoiceData/Expressions/InstantaneousTempoExpression.ts

@@ -20,7 +20,7 @@ export class InstantaneousTempoExpression extends AbstractTempoExpression {
     private static listInstantaneousTempoAndanteModerato: string[] = ["Andante moderato"]; //  }), TempoEnum.andanteModerato);
     private static listInstantaneousTempoAndante: string[] = ["Andante", "Gehend", "Schreitend", "at a walking pace"]; //  }), TempoEnum.andante);
     private static listInstantaneousTempoAndantino: string[] = ["Andantino"]; //  }), TempoEnum.andantino);
-    private static listInstantaneousTempoModerato: string[] = ["Moderato", "M��ig", "Mod�r�", "moderately"]; //  }), TempoEnum.moderato);
+    private static listInstantaneousTempoModerato: string[] = ["Moderato", "Mäßig", "Mod�r�", "moderately"]; //  }), TempoEnum.moderato);
     private static listInstantaneousTempoAllegretto: string[] = ["Allegretto", "fast"]; //  }), TempoEnum.allegretto);
     private static listInstantaneousTempoAllegroModerato: string[] = ["Allegro moderato"]; //  }), TempoEnum.allegroModerato);
     private static listInstantaneousTempoAllegro: string[] = ["Allegro", "Rapide", "Vite", "Rasch", "Schnell", "Fr�hlich"]; //  }), TempoEnum.allegro);
@@ -28,7 +28,7 @@ export class InstantaneousTempoExpression extends AbstractTempoExpression {
     private static listInstantaneousTempoVivacissimo: string[] = ["Vivacissimo", "Sehr lebhaft", "Sehr lebendig"]; //  }), TempoEnum.vivacissimo);
     private static listInstantaneousTempoAllegrissimo: string[] = ["Allegrissimo", "very fast"]; //  }), TempoEnum.allegrissimo);
     private static listInstantaneousTempoPresto: string[] = ["Presto", "Sehr schnell", "Geschwind"]; //  }), TempoEnum.presto);
-    private static listInstantaneousTempoPrestissimo: string[] = ["Prestissimo", "�u�erst schnell"]; //  }), TempoEnum.prestissimo);
+    private static listInstantaneousTempoPrestissimo: string[] = ["Prestissimo", "äußerst schnell"]; //  }), TempoEnum.prestissimo);
     private static listInstantaneousTempoChangesGeneral: string[] = [
         "tempo primo",
         "a tempo",
@@ -75,7 +75,7 @@ export class InstantaneousTempoExpression extends AbstractTempoExpression {
         "liebevoll",
         "mit Leidenschaft",
         "mit Liebe",
-        "amours",
+        "amourös",
         "gesanglich",
         "mit Schwung",
         "mit Feuer",
@@ -87,16 +87,16 @@ export class InstantaneousTempoExpression extends AbstractTempoExpression {
         "freudig",
         "verspielt",
         "angemessen",
-        "grazis",
+        "graziös",
         "mit Grazie",
         "flink",
-        "behnde",
+        "behände",
         "traurig",
         "klagend",
-        "majesttisch",
+        "majestätisch",
         "aber nicht zu sehr",
         "markant",
-        "gem��igt",
+        "gemäßigt",
         "viel",
         "sehr",
         "ersterbend",
@@ -108,12 +108,12 @@ export class InstantaneousTempoExpression extends AbstractTempoExpression {
         "entschlossen",
         "zupackend",
         "heiter",
-        "nachdrcklich",
+        "nachdrücklich",
         "getragen",
         "gewichtig",
         "zart",
-        "zrtlich",
-        "im angemessenen Zeitma",
+        "zärtlich",
+        "im angemessenen Zeitmaß",
         "ruhig",
         "ein wenig",
         "alla marcia",
@@ -449,5 +449,6 @@ export enum TempoEnum {
     prestissimo,
     lastRealTempo,
     addon,
-    changes
+    changes,
+    metronomeMark
 }

+ 20 - 12
src/MusicalScore/VoiceData/Expressions/MultiTempoExpression.ts

@@ -50,19 +50,19 @@ export class MultiTempoExpression /*implements IComparable<MultiTempoExpression>
     public getPlacementOfFirstEntry(): PlacementEnum {
         let placement: PlacementEnum = PlacementEnum.Above;
         if (this.expressions.length > 0) {
-            if (this.expressions[0].expression instanceof InstantaneousTempoExpression) {
-                placement = (<InstantaneousTempoExpression>(this.expressions[0].expression)).Placement;
-            } else if (this.expressions[0].expression instanceof ContinuousTempoExpression) {
-                placement = (<ContinuousTempoExpression>(this.expressions[0].expression)).Placement;
+            if (this.expressions[0].Expression instanceof InstantaneousTempoExpression) {
+                placement = (<InstantaneousTempoExpression>(this.expressions[0].Expression)).Placement;
+            } else if (this.expressions[0].Expression instanceof ContinuousTempoExpression) {
+                placement = (<ContinuousTempoExpression>(this.expressions[0].Expression)).Placement;
             }
         }
         return placement;
     }
     public getFontstyleOfFirstEntry(): FontStyles {
         let fontStyle: FontStyles = FontStyles.Regular;
-        if (this.expressions[0].expression instanceof InstantaneousTempoExpression) {
+        if (this.expressions[0].Expression instanceof InstantaneousTempoExpression) {
             fontStyle = FontStyles.Bold;
-        } else if (this.expressions[0].expression instanceof ContinuousTempoExpression) {
+        } else if (this.expressions[0].Expression instanceof ContinuousTempoExpression) {
             fontStyle = FontStyles.Italic;
         }
         return fontStyle;
@@ -70,12 +70,12 @@ export class MultiTempoExpression /*implements IComparable<MultiTempoExpression>
     //public getFirstEntry(graphicalLabel: GraphicalLabel): AbstractGraphicalExpression {
     //    let indexOfFirstNotInstDynExpr: number = 0;
     //    if (this.expressions.length > 0) {
-    //        if (this.expressions[indexOfFirstNotInstDynExpr].expression instanceof InstantaneousTempoExpression)
+    //        if (this.expressions[indexOfFirstNotInstDynExpr].Expression instanceof InstantaneousTempoExpression)
     //            return new GraphicalInstantaneousTempoExpression(
-    // <InstantaneousTempoExpression>(this.expressions[indexOfFirstNotInstDynExpr].expression), graphicalLabel);
-    //        else if (this.expressions[indexOfFirstNotInstDynExpr].expression instanceof ContinuousTempoExpression)
+    // <InstantaneousTempoExpression>(this.expressions[indexOfFirstNotInstDynExpr].Expression), graphicalLabel);
+    //        else if (this.expressions[indexOfFirstNotInstDynExpr].Expression instanceof ContinuousTempoExpression)
     //            return new GraphicalContinuousTempoExpression(
-    // <ContinuousTempoExpression>(this.expressions[indexOfFirstNotInstDynExpr].expression), graphicalLabel);
+    // <ContinuousTempoExpression>(this.expressions[indexOfFirstNotInstDynExpr].Expression), graphicalLabel);
     //        else return undefined;
     //    }
     //    return undefined;
@@ -88,7 +88,7 @@ export class MultiTempoExpression /*implements IComparable<MultiTempoExpression>
         }
         const tempoExpressionEntry: TempoExpressionEntry = new TempoExpressionEntry();
         tempoExpressionEntry.prefix = prefix;
-        tempoExpressionEntry.expression = abstractTempoExpression;
+        tempoExpressionEntry.Expression = abstractTempoExpression;
         tempoExpressionEntry.label = abstractTempoExpression.Label;
         this.expressions.push(tempoExpressionEntry);
     }
@@ -110,6 +110,14 @@ export class MultiTempoExpression /*implements IComparable<MultiTempoExpression>
 
 export class TempoExpressionEntry {
     public prefix: string;
-    public expression: AbstractTempoExpression;
+    protected expression: AbstractTempoExpression;
     public label: string;
+
+    public get Expression(): AbstractTempoExpression {
+        return this.expression;
+    }
+
+    public set Expression(value: AbstractTempoExpression) {
+        this.expression = value;
+    }
 }

+ 19 - 0
src/Util/CollectionUtil.ts

@@ -1,5 +1,24 @@
 import Dictionary from "typescript-collections/dist/lib/Dictionary";
 
+declare global {
+    interface Array<T> {
+        last(): T;
+        contains(elem: T): boolean;
+    }
+}
+
+if (!Array.prototype.last) {
+    Array.prototype.last = function<T>(): T {
+        return this[this.length - 1];
+    };
+}
+
+if (!Array.prototype.contains) {
+    Array.prototype.contains = function<T>(elem: T): boolean {
+        return this.indexOf(elem) !== -1;
+    };
+}
+
 /**
  * This class implements static methods to perform useful operations on lists, dictionaries, ...
  */