Ver Fonte

Added new MusicSheetDrawer with more functionality.
Adapted VexFlow Drawer.
Added some needed classes.

Matthias há 9 anos atrás
pai
commit
0eb77c19c6

+ 26 - 0
src/MusicalScore/Graphical/DrawingMode.ts

@@ -0,0 +1,26 @@
+export enum DrawingMode {
+    All,
+    NoOverlays,
+    Leadsheet
+}
+
+export enum MusicSymbolDrawingStyle {
+    Normal,
+    Disabled,
+    Selected,
+    Clickable,
+    PlaybackSymbols,
+    FollowSymbols,
+    QFeedbackNotFound,
+    QFeedbackOk,
+    QFeedbackPerfect,
+    Debug1,
+    Debug2,
+    Debug3
+}
+
+export enum PhonicScoreModes {
+    Following,
+    Midi,
+    Manual
+}

+ 47 - 0
src/MusicalScore/Graphical/DrawingParameters.ts

@@ -0,0 +1,47 @@
+export class DrawingParameters {
+    public drawHighlights: boolean;
+    public drawErrors: boolean;
+    public drawSelectionStartSymbol: boolean;
+    public drawSelectionEndSymbol: boolean;
+    public drawCursors: boolean;
+    public drawActivitySymbols: boolean;
+    public drawScrollIndicator: boolean;
+    public drawComments: boolean;
+    public drawMarkedAreas: boolean;
+
+    public setForAllOn(): void {
+        this.drawHighlights = true;
+        this.drawErrors = true;
+        this.drawSelectionStartSymbol = true;
+        this.drawSelectionEndSymbol = true;
+        this.drawCursors = true;
+        this.drawActivitySymbols = true;
+        this.drawScrollIndicator = true;
+        this.drawComments = true;
+        this.drawMarkedAreas = true;
+    }
+
+    public setForThumbmail(): void {
+        this.drawHighlights = false;
+        this.drawErrors = false;
+        this.drawSelectionStartSymbol = false;
+        this.drawSelectionStartSymbol = false;
+        this.drawCursors = false;
+        this.drawActivitySymbols = false;
+        this.drawScrollIndicator = false;
+        this.drawComments = true;
+        this.drawMarkedAreas = true;
+    }
+
+    public setForLeadsheet(): void {
+        this.drawHighlights = false;
+        this.drawErrors = false;
+        this.drawSelectionStartSymbol = true;
+        this.drawSelectionEndSymbol = true;
+        this.drawCursors = true;
+        this.drawActivitySymbols = false;
+        this.drawScrollIndicator = true;
+        this.drawComments = true;
+        this.drawMarkedAreas = true;
+    }
+}

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

@@ -21,6 +21,8 @@ import {MusicSheetCalculator} from "./MusicSheetCalculator";
 import {Logging} from "../../Common/logging";
 import Dictionary from "typescript-collections/dist/lib/Dictionary";
 import {CollectionUtil} from "../../Util/collectionUtil";
+import {SelectionStartSymbol} from "./SelectionStartSymbol";
+import {SelectionEndSymbol} from "./SelectionEndSymbol";
 
 export class GraphicalMusicSheet {
     constructor(musicSheet: MusicSheet, calculator: MusicSheetCalculator) {
@@ -44,6 +46,8 @@ export class GraphicalMusicSheet {
     private composer: GraphicalLabel;
     private lyricist: GraphicalLabel;
     private cursors: GraphicalLine[] = [];
+    private selectionStartSymbol: SelectionStartSymbol;
+    private selectionEndSymbol: SelectionEndSymbol;
     private minAllowedSystemWidth: number;
     //private systemImages: Dictionary<MusicSystem, SystemImageProperties> = new Dictionary<MusicSystem, SystemImageProperties>();
     private numberOfStaves: number;
@@ -121,6 +125,14 @@ export class GraphicalMusicSheet {
         return this.cursors;
     }
 
+    public get SelectionStartSymbol(): SelectionStartSymbol {
+        return this.selectionStartSymbol;
+    }
+
+    public get SelectionEndSymbol(): SelectionEndSymbol {
+        return this.selectionEndSymbol;
+    }
+
     public get MinAllowedSystemWidth(): number {
         return this.minAllowedSystemWidth;
     }

+ 51 - 0
src/MusicalScore/Graphical/GraphicalOctaveShift.ts

@@ -0,0 +1,51 @@
+import {GraphicalObject} from "./GraphicalObject";
+import {OctaveShift, OctaveEnum} from "../VoiceData/Expressions/ContinuousExpressions/octaveShift";
+import {BoundingBox} from "./BoundingBox";
+import {MusicSymbol} from "./MusicSymbol";
+import {ArgumentOutOfRangeException} from "../Exceptions";
+import {PointF2D} from "../../Common/DataObjects/PointF2D";
+export class GraphicalOctaveShift extends GraphicalObject {
+    constructor(octaveShift: OctaveShift, parent: BoundingBox) {
+        super();
+        this.getOctaveShift = octaveShift;
+        this.setSymbol();
+        // ToDo: set the size again due to the given symbol...
+        //this.PositionAndShape = new BoundingBox(parent, this.octaveSymbol, this);
+        this.PositionAndShape = new BoundingBox(this, parent);
+    }
+
+    public getOctaveShift: OctaveShift;
+    public octaveSymbol: MusicSymbol;
+    public dashesStart: PointF2D;
+    public dashesEnd: PointF2D;
+    public endsOnDifferentStaffLine: boolean;
+    public isFirstPart: boolean;
+    public isSecondPart: boolean;
+
+    private setSymbol(): void {
+        switch (this.getOctaveShift.Type) {
+            case OctaveEnum.VA8:
+            {
+                this.octaveSymbol = MusicSymbol.VA8;
+                break;
+            }
+            case OctaveEnum.VB8:
+            {
+                this.octaveSymbol = MusicSymbol.VB8;
+                break;
+            }
+            case OctaveEnum.MA15:
+            {
+                this.octaveSymbol = MusicSymbol.MA15;
+                break;
+            }
+            case OctaveEnum.MB15:
+            {
+                this.octaveSymbol = MusicSymbol.MB15;
+                break;
+            }
+            default:
+                throw new ArgumentOutOfRangeException("");
+        }
+    }
+}

+ 460 - 18
src/MusicalScore/Graphical/MusicSheetDrawer.ts

@@ -1,47 +1,489 @@
-import {GraphicalMusicSheet} from "./GraphicalMusicSheet";
-import {StaffMeasure} from "./StaffMeasure";
-import {StaffLine} from "./StaffLine";
+import {EngravingRules} from "./EngravingRules";
+import {ITextMeasurer} from "../Interfaces/ITextMeasurer";
+import {GraphicalMusicSheet} from "./GraphicalMusicSheet";
+import {BoundingBox} from "./BoundingBox";
+import {GraphicalLayers, OutlineAndFillStyleEnum} from "./DrawingEnums";
+import {DrawingParameters} from "./DrawingParameters";
+import {GraphicalLine} from "./GraphicalLine";
 import {RectangleF2D} from "../../Common/DataObjects/RectangleF2D";
+import {PointF2D} from "../../Common/DataObjects/PointF2D";
+import {GraphicalRectangle} from "./GraphicalRectangle";
+import {GraphicalLabel} from "./GraphicalLabel";
+import {Label} from "../Label";
+import {TextAlignment} from "../../Common/Enums/TextAlignment";
+import {ArgumentOutOfRangeException} from "../Exceptions";
+import {SelectionStartSymbol} from "./SelectionStartSymbol";
+import {SelectionEndSymbol} from "./SelectionEndSymbol";
 import {MusicSystem} from "./MusicSystem";
+import {StaffMeasure} from "./StaffMeasure";
+import {StaffLine} from "./StaffLine";
+import {SystemLine} from "./SystemLine";
+import {MusicSymbol} from "./MusicSymbol";
 import {GraphicalMusicPage} from "./GraphicalMusicPage";
+import {Instrument} from "../Instrument";
+import {MusicSymbolDrawingStyle, PhonicScoreModes} from "./DrawingMode";
+import {GraphicalOctaveShift} from "./GraphicalOctaveShift";
+import {GraphicalObject} from "./GraphicalObject";
 
-export class MusicSheetDrawer {
+export abstract class MusicSheetDrawer {
+    public drawingParameters: DrawingParameters = new DrawingParameters();
+    public SplitScreenLineColor: number;
+    public MidiPlaybackAvailable: boolean;
+
+    protected rules: EngravingRules;
     protected graphicalMusicSheet: GraphicalMusicSheet;
+    protected textMeasurer: ITextMeasurer;
+    private phonicScoreMode: PhonicScoreModes = PhonicScoreModes.Manual;
+
+    constructor(textMeasurer: ITextMeasurer,
+                isPreviewImageDrawer: boolean = false) {
+        this.textMeasurer = textMeasurer;
+        this.SplitScreenLineColor = -1;
+        if (isPreviewImageDrawer) {
+            this.drawingParameters.setForThumbmail();
+        } else {
+            this.drawingParameters.setForAllOn();
+        }
+    }
+
+    public set Mode(value: PhonicScoreModes) {
+        this.phonicScoreMode = value;
+    }
 
     public drawSheet(graphicalMusicSheet: GraphicalMusicSheet): void {
         this.graphicalMusicSheet = graphicalMusicSheet;
-        for (let idx: number = 0, len: number = this.graphicalMusicSheet.MusicPages.length; idx < len; ++idx) {
-            let page: GraphicalMusicPage = this.graphicalMusicSheet.MusicPages[idx];
+        this.rules = graphicalMusicSheet.ParentMusicSheet.Rules;
+        this.drawSplitScreenLine();
+        if (this.drawingParameters.drawCursors) {
+            for (let line of graphicalMusicSheet.Cursors) {
+                let psi: BoundingBox = new BoundingBox(line);
+                psi.AbsolutePosition = line.Start;
+                psi.BorderBottom = line.End.y - line.Start.y;
+                psi.BorderRight = line.Width / 2.0;
+                psi.BorderLeft = -line.Width / 2.0;
+                if (this.isVisible(psi)) {
+                    this.drawLineAsVerticalRectangle(line, <number>GraphicalLayers.Cursor);
+                }
+            }
+        }
+        if (this.drawingParameters.drawScrollIndicator) {
+            this.drawScrollIndicator();
+        }
+        for (let page of this.graphicalMusicSheet.MusicPages) {
             this.drawPage(page);
         }
     }
 
-    protected drawMeasure(measure: StaffMeasure): void {
+    public drawLineAsHorizontalRectangle(line: GraphicalLine, layer: number): void {
+        let rectangle: RectangleF2D = new RectangleF2D(line.Start.x, line.End.y - line.Width / 2, line.End.x - line.Start.x, line.Width);
+        rectangle = this.applyScreenTransformationForRect(rectangle);
+        this.renderRectangle(rectangle, layer, line.styleId);
+    }
+
+    public drawLineAsVerticalRectangle(line: GraphicalLine, layer: number): void {
+        let lineStart: PointF2D = line.Start;
+        let lineWidth: number = line.Width;
+        let rectangle: RectangleF2D = new RectangleF2D(lineStart.x - lineWidth / 2, lineStart.y, lineWidth, line.End.y - lineStart.y);
+        rectangle = this.applyScreenTransformationForRect(rectangle);
+        this.renderRectangle(rectangle, layer, line.styleId);
+    }
+
+    public drawLineAsHorizontalRectangleWithOffset(line: GraphicalLine, offset: PointF2D, layer: number): void {
+        let start: PointF2D = new PointF2D(line.Start.x + offset.x, line.Start.y + offset.y);
+        let end: PointF2D = new PointF2D(line.End.x + offset.x, line.End.y + offset.y);
+        let width: number = line.Width;
+        let rectangle: RectangleF2D = new RectangleF2D(start.x, end.y - width / 2, end.x - start.x, width);
+        rectangle = this.applyScreenTransformationForRect(rectangle);
+        this.renderRectangle(rectangle, layer, line.styleId);
+    }
+
+    public drawLineAsVerticalRectangleWithOffset(line: GraphicalLine, offset: PointF2D, layer: number): void {
+        let start: PointF2D = new PointF2D(line.Start.x + offset.x, line.Start.y + offset.y);
+        let end: PointF2D = new PointF2D(line.End.x + offset.x, line.End.y + offset.y);
+        let width: number = line.Width;
+        let rectangle: RectangleF2D = new RectangleF2D(start.x, start.y, width, end.y - start.y);
+        rectangle = this.applyScreenTransformationForRect(rectangle);
+        this.renderRectangle(rectangle, layer, line.styleId);
+    }
+
+    public drawRectangle(rect: GraphicalRectangle, layer: number): void {
+        let psi: BoundingBox = rect.PositionAndShape;
+        let rectangle: RectangleF2D = new RectangleF2D(psi.AbsolutePosition.x, psi.AbsolutePosition.y, psi.BorderRight, psi.BorderBottom);
+        rectangle = this.applyScreenTransformationForRect(rectangle);
+        this.renderRectangle(rectangle, layer, <number>rect.style);
+    }
+
+    public calculatePixelDistance(unitDistance: number): number {
         throw new Error("not implemented");
     }
 
-    protected applyScreenTransformation(rectangle: RectangleF2D): RectangleF2D {
+    public drawLabel(graphicalLabel: GraphicalLabel, layer: number): void {
+        if (!this.isVisible(graphicalLabel.PositionAndShape)) {
+            return;
+        }
+        let label: Label = graphicalLabel.Label;
+        if (label.text.trim() === "") {
+            return;
+        }
+        let screenPosition: PointF2D = this.applyScreenTransformation(graphicalLabel.PositionAndShape.AbsolutePosition);
+        let heightInPixel: number = this.calculatePixelDistance(label.fontHeight);
+        let widthInPixel: number = heightInPixel * this.textMeasurer.computeTextWidthToHeightRatio(label.text, label.font, label.fontStyle);
+        let bitmapWidth: number = <number>Math.ceil(widthInPixel);
+        let bitmapHeight: number = <number>Math.ceil(heightInPixel * 1.2);
+        switch (label.textAlignment) {
+            case TextAlignment.LeftTop:
+                break;
+            case TextAlignment.LeftCenter:
+                screenPosition.y -= <number>bitmapHeight / 2;
+                break;
+            case TextAlignment.LeftBottom:
+                screenPosition.y -= bitmapHeight;
+                break;
+            case TextAlignment.CenterTop:
+                screenPosition.x -= <number>bitmapWidth / 2;
+                break;
+            case TextAlignment.CenterCenter:
+                screenPosition.x -= <number>bitmapWidth / 2;
+                screenPosition.y -= <number>bitmapHeight / 2;
+                break;
+            case TextAlignment.CenterBottom:
+                screenPosition.x -= <number>bitmapWidth / 2;
+                screenPosition.y -= bitmapHeight;
+                break;
+            case TextAlignment.RightTop:
+                screenPosition.x -= bitmapWidth;
+                break;
+            case TextAlignment.RightCenter:
+                screenPosition.x -= bitmapWidth;
+                screenPosition.y -= <number>bitmapHeight / 2;
+                break;
+            case TextAlignment.RightBottom:
+                screenPosition.x -= bitmapWidth;
+                screenPosition.y -= bitmapHeight;
+                break;
+            default:
+                throw new ArgumentOutOfRangeException("");
+        }
+        this.renderLabel(graphicalLabel, layer, bitmapWidth, bitmapHeight, heightInPixel, screenPosition);
+    }
+
+    protected applyScreenTransformation(point: PointF2D): PointF2D {
         throw new Error("not implemented");
     }
 
-    private drawPage(page: GraphicalMusicPage): void {
-        for (let idx: number = 0, len: number = page.MusicSystems.length; idx < len; ++idx) {
-            let system: MusicSystem = page.MusicSystems[idx];
-            this.drawMusicSystem(system);
+    protected applyScreenTransformations(points: PointF2D[]): PointF2D[] {
+        let transformedPoints: PointF2D[] = [];
+        for (let point of points) {
+            transformedPoints.push(this.applyScreenTransformation(point));
         }
+        return transformedPoints;
+    }
+
+    protected applyScreenTransformationForRect(rectangle: RectangleF2D): RectangleF2D {
+        throw new Error("not implemented");
+    }
+
+    protected drawSplitScreenLine(): void {
+        // empty
+    }
+
+    protected renderRectangle(rectangle: RectangleF2D, layer: number, styleId: number): void {
+        throw new Error("not implemented");
+    }
+
+    protected drawScrollIndicator(): void {
+        // empty
+    }
+
+    protected drawSelectionStartSymbol(symbol: SelectionStartSymbol): void {
+        // empty
+    }
+
+    protected drawSelectionEndSymbol(symbol: SelectionEndSymbol): void {
+        // empty
+    }
+
+    protected renderLabel(graphicalLabel: GraphicalLabel, layer: number, bitmapWidth: number,
+                          bitmapHeight: number, heightInPixel: number, screenPosition: PointF2D): void {
+        throw new Error("not implemented");
+    }
+
+    protected renderSystemToScreen(system: MusicSystem, systemBoundingBoxInPixels: RectangleF2D,
+                                   absBoundingRectWithMargin: RectangleF2D): void {
+        // empty
+    }
+
+    protected drawMeasure(measure: StaffMeasure): void {
+        throw new Error("not implemented");
+    }
+
+    protected drawSkyLine(staffLine: StaffLine): void {
+        // empty
+    }
+
+    protected drawBottomLine(staffLine: StaffLine): void {
+        // empty
+    }
+
+    protected drawInstrumentBracket(bracket: GraphicalObject, system: MusicSystem): void {
+        // empty
+    }
+
+    protected drawGroupBracket(bracket: GraphicalObject, system: MusicSystem): void {
+        // empty
+    }
+
+    protected isVisible(psi: BoundingBox): boolean {
+        return true;
+    }
+
+    protected drawMusicSystem(system: MusicSystem): void {
+        let absBoundingRectWithMargin: RectangleF2D = this.getSystemAbsBoundingRect(system);
+        let systemBoundingBoxInPixels: RectangleF2D = this.getSytemBoundingBoxInPixels(absBoundingRectWithMargin);
+        this.drawMusicSystemComponents(system, systemBoundingBoxInPixels, absBoundingRectWithMargin);
     }
 
-    private drawMusicSystem(musicSystem: MusicSystem): void {
-        for (let idx: number = 0, len: number = musicSystem.StaffLines.length; idx < len; ++idx) {
-            let staffLine: StaffLine = musicSystem.StaffLines[idx];
+    protected getSytemBoundingBoxInPixels(absBoundingRectWithMargin: RectangleF2D): RectangleF2D {
+        let systemBoundingBoxInPixels: RectangleF2D = this.applyScreenTransformationForRect(absBoundingRectWithMargin);
+        systemBoundingBoxInPixels.x = Math.round(systemBoundingBoxInPixels.x);
+        systemBoundingBoxInPixels.y = Math.round(systemBoundingBoxInPixels.y);
+        return systemBoundingBoxInPixels;
+    }
+
+    protected getSystemAbsBoundingRect(system: MusicSystem): RectangleF2D {
+        let relBoundingRect: RectangleF2D = system.PositionAndShape.BoundingRectangle;
+        let absBoundingRectWithMargin: RectangleF2D = new RectangleF2D(system.PositionAndShape.AbsolutePosition.x + system.PositionAndShape.BorderLeft - 1,
+            system.PositionAndShape.AbsolutePosition.y + system.PositionAndShape.BorderTop - 1,
+            (relBoundingRect.width + 6), (relBoundingRect.height + 2));
+        return absBoundingRectWithMargin;
+    }
+
+    protected drawMusicSystemComponents(musicSystem: MusicSystem, systemBoundingBoxInPixels: RectangleF2D,
+                                        absBoundingRectWithMargin: RectangleF2D): void {
+        let selectStartSymb: SelectionStartSymbol = this.graphicalMusicSheet.SelectionStartSymbol;
+        let selectEndSymb: SelectionEndSymbol = this.graphicalMusicSheet.SelectionEndSymbol;
+        if (this.drawingParameters.drawSelectionStartSymbol) {
+            if (selectStartSymb !== undefined && this.isVisible(selectStartSymb.PositionAndShape)) {
+                this.drawSelectionStartSymbol(selectStartSymb);
+            }
+        }
+        if (this.drawingParameters.drawSelectionEndSymbol) {
+            if (selectEndSymb !== undefined && this.isVisible(selectEndSymb.PositionAndShape)) {
+                this.drawSelectionEndSymbol(selectEndSymb);
+            }
+        }
+        for (let staffLine of musicSystem.StaffLines) {
             this.drawStaffLine(staffLine);
         }
+        for (let systemLine of musicSystem.SystemLines) {
+            this.drawSystemLineObject(systemLine);
+        }
+        if (musicSystem === musicSystem.Parent.MusicSystems[0] && musicSystem.Parent === musicSystem.Parent.Parent.MusicPages[0]) {
+            for (let label of musicSystem.Labels) {
+                this.drawLabel(label, <number>GraphicalLayers.Notes);
+            }
+        }
+        for (let bracket of musicSystem.InstrumentBrackets) {
+            this.drawInstrumentBracket(bracket, musicSystem);
+        }
+        for (let bracket of musicSystem.GroupBrackets) {
+            this.drawGroupBracket(bracket, musicSystem);
+        }
+        if (!this.leadSheet) {
+            for (let measureNumberLabel of musicSystem.MeasureNumberLabels) {
+                this.drawLabel(measureNumberLabel, <number>GraphicalLayers.Notes);
+            }
+        }
+        for (let staffLine of musicSystem.StaffLines) {
+            this.drawStaffLineSymbols(staffLine);
+        }
+        if (this.drawingParameters.drawMarkedAreas) {
+            this.drawMarkedAreas(musicSystem);
+        }
+        if (this.drawingParameters.drawComments) {
+            this.drawComment(musicSystem);
+        }
+    }
+
+    protected activateSystemRendering(systemId: number, absBoundingRect: RectangleF2D,
+                                      systemBoundingBoxInPixels: RectangleF2D, createNewImage: boolean): boolean {
+        return true;
+    }
+
+    protected drawSystemLineObject(systemLine: SystemLine): void {
+        // empty
     }
 
-    private drawStaffLine(staffLine: StaffLine): void {
-        for (let idx: number = 0, len: number = staffLine.Measures.length; idx < len; ++idx) {
-            let measure: StaffMeasure = staffLine.Measures[idx];
+    protected drawStaffLine(staffLine: StaffLine): void {
+        for (let measure of staffLine.Measures) {
             this.drawMeasure(measure);
         }
     }
+
+    // protected drawSlur(slur: GraphicalSlur, abs: PointF2D): void {
+    //
+    // }
+
+    protected drawOctaveShift(staffLine: StaffLine, graphicalOctaveShift: GraphicalOctaveShift): void {
+        this.drawSymbol(graphicalOctaveShift.octaveSymbol, MusicSymbolDrawingStyle.Normal, graphicalOctaveShift.PositionAndShape.AbsolutePosition);
+        let absolutePos: PointF2D = staffLine.PositionAndShape.AbsolutePosition;
+        if (graphicalOctaveShift.dashesStart.x < graphicalOctaveShift.dashesEnd.x) {
+            let horizontalLine: GraphicalLine = new GraphicalLine(graphicalOctaveShift.dashesStart, graphicalOctaveShift.dashesEnd, this.rules.OctaveShiftLineWidth);
+            this.drawLineAsHorizontalRectangleWithOffset(horizontalLine, absolutePos, <number>GraphicalLayers.Notes);
+        }
+        if (!graphicalOctaveShift.endsOnDifferentStaffLine || graphicalOctaveShift.isSecondPart) {
+            let verticalLine: GraphicalLine;
+            let dashEnd: PointF2D = graphicalOctaveShift.dashesEnd;
+            let octShiftVertLineLength: number = this.rules.OctaveShiftVerticalLineLength;
+            let octShiftLineWidth: number = this.rules.OctaveShiftLineWidth;
+            if (graphicalOctaveShift.octaveSymbol === MusicSymbol.VA8 || graphicalOctaveShift.octaveSymbol === MusicSymbol.MA15) {
+                verticalLine = new GraphicalLine(dashEnd, new PointF2D(dashEnd.x, dashEnd.y + octShiftVertLineLength), octShiftLineWidth);
+            } else {
+                verticalLine = new GraphicalLine(new PointF2D(dashEnd.x, dashEnd.y - octShiftVertLineLength), dashEnd, octShiftLineWidth);
+            }
+            this.drawLineAsVerticalRectangleWithOffset(verticalLine, absolutePos, <number>GraphicalLayers.Notes);
+        }
+    }
+
+    protected drawStaffLines(staffLine: StaffLine): void {
+        if (staffLine.StaffLines !== undefined) {
+            let position: PointF2D = staffLine.PositionAndShape.AbsolutePosition;
+            for (let i: number = 0; i < 5; i++)
+                this.drawLineAsHorizontalRectangleWithOffset(staffLine.StaffLines[i], position, <number>GraphicalLayers.Notes);
+        }
+    }
+
+    // protected drawEnding(ending: GraphicalRepetitionEnding, absolutePosition: PointF2D): void {
+    //     if (undefined !== ending.Left)
+    //         drawLineAsVerticalRectangle(ending.Left, absolutePosition, <number>GraphicalLayers.Notes);
+    //     this.drawLineAsHorizontalRectangle(ending.Top, absolutePosition, <number>GraphicalLayers.Notes);
+    //     if (undefined !== ending.Right)
+    //         drawLineAsVerticalRectangle(ending.Right, absolutePosition, <number>GraphicalLayers.Notes);
+    //     this.drawLabel(ending.Label, <number>GraphicalLayers.Notes);
+    // }
+    // protected drawInstantaniousDynamic(expression: GraphicalInstantaniousDynamicExpression): void {
+    //     expression.ExpressionSymbols.forEach(function (expressionSymbol) {
+    //         let position: PointF2D = expressionSymbol.PositionAndShape.AbsolutePosition;
+    //         let symbol: MusicSymbol = expressionSymbol.GetSymbol;
+    //         drawSymbol(symbol, MusicSymbolDrawingStyle.Normal, position);
+    //     });
+    // }
+    // protected drawContinuousDynamic(expression: GraphicalContinuousDynamicExpression,
+    //     absolute: PointF2D): void {
+    //     throw new Error("not implemented");
+    // }
+    protected drawSymbol(symbol: MusicSymbol, symbolStyle: MusicSymbolDrawingStyle, position: PointF2D,
+                         scalingFactor: number = 1, layer: number = <number>GraphicalLayers.Notes): void {
+        //empty
+    }
+
+    protected get leadSheet(): boolean {
+        return this.graphicalMusicSheet.LeadSheet;
+    }
+
+    protected set leadSheet(value: boolean) {
+        this.graphicalMusicSheet.LeadSheet = value;
+    }
+
+    private drawPage(page: GraphicalMusicPage): void {
+        if (!this.isVisible(page.PositionAndShape)) {
+            return;
+        }
+        for (let system of page.MusicSystems) {
+            if (this.isVisible(system.PositionAndShape)) {
+                this.drawMusicSystem(system);
+            }
+        }
+        if (page === page.Parent.MusicPages[0]) {
+            for (let label of page.Labels) {
+                this.drawLabel(label, <number>GraphicalLayers.Notes);
+            }
+        }
+    }
+
+    private drawMarkedAreas(system: MusicSystem): void {
+        for (let markedArea of system.GraphicalMarkedAreas) {
+            if (markedArea !== undefined) {
+                if (markedArea.systemRectangle !== undefined) {
+                    this.drawRectangle(markedArea.systemRectangle, <number>GraphicalLayers.Background);
+                }
+                if (markedArea.settings !== undefined) {
+                    this.drawLabel(markedArea.settings, <number>GraphicalLayers.Comment);
+                }
+                if (markedArea.labelRectangle !== undefined) {
+                    this.drawRectangle(markedArea.labelRectangle, <number>GraphicalLayers.Background);
+                }
+                if (markedArea.label !== undefined) {
+                    this.drawLabel(markedArea.label, <number>GraphicalLayers.Comment);
+                }
+            }
+        }
+    }
+
+    private drawComment(system: MusicSystem): void {
+        for (let comment of system.GraphicalComments) {
+            if (comment !== undefined) {
+                if (comment.settings !== undefined) {
+                    this.drawLabel(comment.settings, <number>GraphicalLayers.Comment);
+                }
+                if (comment.label !== undefined) {
+                    this.drawLabel(comment.label, <number>GraphicalLayers.Comment);
+                }
+            }
+        }
+    }
+
+    private drawStaffLineSymbols(staffLine: StaffLine): void {
+        let parentInst: Instrument = staffLine.ParentStaff.ParentInstrument;
+        let absX: number = staffLine.PositionAndShape.AbsolutePosition.x;
+        let absY: number = staffLine.PositionAndShape.AbsolutePosition.y + 2;
+        let borderRight: number = staffLine.PositionAndShape.BorderRight;
+        if (parentInst.highlight && this.drawingParameters.drawHighlights) {
+            this.drawLineAsHorizontalRectangle(new GraphicalLine(new PointF2D(absX, absY),
+                new PointF2D(absX + borderRight, absY), 4,
+                OutlineAndFillStyleEnum.Highlighted), <number>GraphicalLayers.Highlight);
+        }
+        let style: MusicSymbolDrawingStyle = MusicSymbolDrawingStyle.Disabled;
+        let symbol: MusicSymbol = MusicSymbol.PLAY;
+        let drawSymbols: boolean = this.drawingParameters.drawActivitySymbols;
+        switch (this.phonicScoreMode) {
+            case PhonicScoreModes.Midi:
+            {
+                symbol = MusicSymbol.PLAY;
+                if (this.MidiPlaybackAvailable && staffLine.ParentStaff.audible) {
+                    style = MusicSymbolDrawingStyle.PlaybackSymbols;
+                }
+                break;
+            }
+            case PhonicScoreModes.Following:
+            {
+                symbol = MusicSymbol.MIC;
+                if (staffLine.ParentStaff.following) {
+                    style = MusicSymbolDrawingStyle.FollowSymbols;
+                }
+                break;
+            }
+            default:
+            {
+                drawSymbols = false;
+                break;
+            }
+        }
+        if (drawSymbols) {
+            let p: PointF2D = new PointF2D(absX + borderRight + 2, absY);
+            this.drawSymbol(symbol, style, p);
+        }
+        if (this.drawingParameters.drawErrors) {
+            for (let measure of staffLine.Measures) {
+                let measurePSI: BoundingBox = measure.PositionAndShape;
+                let absXPSI: number = measurePSI.AbsolutePosition.x;
+                let absYPSI: number = measurePSI.AbsolutePosition.y + 2;
+                if (measure.hasError && this.graphicalMusicSheet.ParentMusicSheet.DrawErroneousMeasures) {
+                    this.drawLineAsHorizontalRectangle(new GraphicalLine(new PointF2D(absXPSI, absYPSI),
+                        new PointF2D(absXPSI + measurePSI.BorderRight, absYPSI), 4,
+                        OutlineAndFillStyleEnum.ErrorUnderlay), <number>GraphicalLayers.MeasureError);
+                }
+            }
+        }
+    }
 }

+ 62 - 0
src/MusicalScore/Graphical/SelectionEndSymbol.ts

@@ -0,0 +1,62 @@
+import {GraphicalObject} from "./GraphicalObject";
+import {MusicSystem} from "./MusicSystem";
+import {OutlineAndFillStyleEnum} from "./DrawingEnums";
+import {StaffLine} from "./StaffLine";
+import {PointF2D} from "../../Common/DataObjects/PointF2D";
+import {BoundingBox} from "./BoundingBox";
+import {GraphicalLine} from "./GraphicalLine";
+import {CollectionUtil} from "../../Util/collectionUtil";
+export class SelectionEndSymbol extends GraphicalObject {
+    constructor(system: MusicSystem, xPosition: number) {
+        super();
+        let xCoordinate: number = xPosition;
+        let yCoordinate: number = system.PositionAndShape.AbsolutePosition.y;
+        let lineThickness: number = 0.4;
+        let height: number = CollectionUtil.last(system.StaffLines).PositionAndShape.RelativePosition.y + 4;
+        this.verticalLine = new GraphicalLine(new PointF2D(xCoordinate, yCoordinate), new PointF2D(xCoordinate, yCoordinate + height), lineThickness, OutlineAndFillStyleEnum.SelectionSymbol);
+        for (let idx: number = 0, len: number = system.StaffLines.length; idx < len; ++idx) {
+            let staffLine: StaffLine = system.StaffLines[idx];
+            let anchor: PointF2D = new PointF2D(xCoordinate, yCoordinate + staffLine.PositionAndShape.RelativePosition.y);
+            let arrowPoints: PointF2D[] = new Array(3);
+            anchor.y -= .2;
+            arrowPoints[0].x = anchor.x - 3;
+            arrowPoints[0].y = anchor.y + 1.2;
+            arrowPoints[1].x = anchor.x - 2;
+            arrowPoints[1].y = anchor.y + 0.4;
+            arrowPoints[2].x = anchor.x - 2;
+            arrowPoints[2].y = anchor.y + 2;
+            this.arrows.push(arrowPoints);
+            let linePoints: PointF2D[] = new Array(8);
+            let arrowThickness: number = .8;
+            anchor.x -= .1;
+            anchor.y += .3;
+            let hilfsVar: number = .2;
+            linePoints[0].x = anchor.x - 2;
+            linePoints[0].y = anchor.y + 1.5 - hilfsVar;
+            linePoints[1].x = anchor.x - 1;
+            linePoints[1].y = anchor.y + 1.5 - hilfsVar;
+            linePoints[2].x = anchor.x - 1;
+            linePoints[2].y = anchor.y + 2.5;
+            linePoints[3].x = anchor.x - 2;
+            linePoints[3].y = anchor.y + 2.5;
+            linePoints[4].x = linePoints[0].x;
+            linePoints[4].y = linePoints[0].y - arrowThickness;
+            linePoints[5].x = linePoints[4].x + arrowThickness + 1;
+            linePoints[5].y = linePoints[4].y;
+            linePoints[6].x = linePoints[5].x;
+            linePoints[6].y = linePoints[3].y + arrowThickness;
+            linePoints[7].x = linePoints[3].x;
+            linePoints[7].y = linePoints[6].y;
+            this.arrowlines.push(linePoints);
+        }
+        this.boundingBox = new BoundingBox(this);
+        this.boundingBox.AbsolutePosition = new PointF2D(xCoordinate, yCoordinate);
+        this.boundingBox.BorderLeft = -lineThickness;
+        this.boundingBox.BorderRight = 4;
+        this.boundingBox.BorderBottom = height;
+    }
+
+    public verticalLine: GraphicalLine;
+    public arrows: PointF2D[][];
+    public arrowlines: PointF2D[][];
+}

+ 46 - 0
src/MusicalScore/Graphical/SelectionStartSymbol.ts

@@ -0,0 +1,46 @@
+import {PointF2D} from "../../Common/DataObjects/PointF2D";
+import {StaffLine} from "./StaffLine";
+import {OutlineAndFillStyleEnum} from "./DrawingEnums";
+import {GraphicalLine} from "./GraphicalLine";
+import {MusicSystem} from "./MusicSystem";
+import {GraphicalObject} from "./GraphicalObject";
+import {BoundingBox} from "./BoundingBox";
+import {CollectionUtil} from "../../Util/collectionUtil";
+export class SelectionStartSymbol extends GraphicalObject {
+    constructor(system: MusicSystem, xPosition: number) {
+        super();
+        let xCoordinate: number = xPosition;
+        let yCoordinate: number = system.PositionAndShape.AbsolutePosition.y;
+        let lineThickness: number = 0.4;
+        let height: number = CollectionUtil.last(system.StaffLines).PositionAndShape.RelativePosition.y + 4;
+        this.verticalLine = new GraphicalLine(new PointF2D(xCoordinate, yCoordinate), new PointF2D(xCoordinate, yCoordinate + height), lineThickness, OutlineAndFillStyleEnum.SelectionSymbol);
+        for (let idx: number = 0, len = system.StaffLines.length; idx < len; ++idx) {
+            let staffLine: StaffLine = system.StaffLines[idx];
+            let anchor: PointF2D = new PointF2D(xCoordinate, yCoordinate + staffLine.PositionAndShape.RelativePosition.y);
+            let arrowPoints: PointF2D[] = new Array(7);
+            arrowPoints[0].x = anchor.x + 4;
+            arrowPoints[0].y = anchor.y + 2;
+            arrowPoints[1].x = anchor.x + 2.5;
+            arrowPoints[1].y = anchor.y + 0.5;
+            arrowPoints[2].x = anchor.x + 2.5;
+            arrowPoints[2].y = anchor.y + 1.3;
+            arrowPoints[3].x = anchor.x + 1;
+            arrowPoints[3].y = anchor.y + 1.3;
+            arrowPoints[4].x = anchor.x + 1;
+            arrowPoints[4].y = anchor.y + 2.7;
+            arrowPoints[5].x = anchor.x + 2.5;
+            arrowPoints[5].y = anchor.y + 2.7;
+            arrowPoints[6].x = anchor.x + 2.5;
+            arrowPoints[6].y = anchor.y + 3.5;
+            this.arrows.push(arrowPoints);
+        }
+        this.boundingBox = new BoundingBox(this);
+        this.boundingBox.AbsolutePosition = new PointF2D(xCoordinate, yCoordinate);
+        this.boundingBox.BorderLeft = -lineThickness;
+        this.boundingBox.BorderRight = 4;
+        this.boundingBox.BorderBottom = height;
+    }
+
+    public verticalLine: GraphicalLine;
+    public arrows: PointF2D[][];
+}

+ 61 - 8
src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetDrawer.ts

@@ -3,12 +3,18 @@ import {MusicSheetDrawer} from "../MusicSheetDrawer";
 import {RectangleF2D} from "../../../Common/DataObjects/RectangleF2D";
 import {VexFlowMeasure} from "./VexFlowMeasure";
 import {GraphicalMusicSheet} from "../GraphicalMusicSheet";
+import {ITextMeasurer} from "../../Interfaces/ITextMeasurer";
+import {PointF2D} from "../../../Common/DataObjects/PointF2D";
+import {GraphicalLabel} from "../GraphicalLabel";
 /**
  * Created by Matthias on 22.06.2016.
  */
 export class VexFlowMusicSheetDrawer extends MusicSheetDrawer {
-    constructor() {
-        super();
+    private renderer: Vex.Flow.Renderer;
+    private ctx: Vex.Flow.CanvasContext;
+
+    constructor(textMeasurer: ITextMeasurer, isPreviewImageDrawer: boolean = false) {
+        super(textMeasurer, isPreviewImageDrawer);
         // Create heading (FIXME)
         let h1: Element = document.createElement("h1");
         h1.textContent = "VexFlowMusicSheetDrawer Output";
@@ -18,12 +24,8 @@ export class VexFlowMusicSheetDrawer extends MusicSheetDrawer {
         document.body.appendChild(canvas);
         this.renderer = new Vex.Flow.Renderer(canvas, Vex.Flow.Renderer.Backends.CANVAS);
         this.ctx = this.renderer.getContext();
-
     }
 
-    private renderer: Vex.Flow.Renderer;
-    private ctx: Vex.Flow.CanvasContext;
-
     public drawSheet(graphicalMusicSheet: GraphicalMusicSheet): void {
         // FIXME units
         // FIXME actual page size
@@ -35,6 +37,16 @@ export class VexFlowMusicSheetDrawer extends MusicSheetDrawer {
         super.drawSheet(graphicalMusicSheet);
     }
 
+    /**
+     * Converts a distance from unit to pixel space.
+     * @param unitDistance the distance in units
+     * @returns {number} the distance in pixels
+     */
+    public calculatePixelDistance(unitDistance: number): number {
+        // ToDo: implement!
+        return unitDistance;
+    }
+
     protected drawMeasure(measure: VexFlowMeasure): void {
         measure.setAbsoluteCoordinates(
             measure.PositionAndShape.AbsolutePosition.x * (measure as VexFlowMeasure).unit,
@@ -43,7 +55,48 @@ export class VexFlowMusicSheetDrawer extends MusicSheetDrawer {
         return measure.draw(this.ctx);
     }
 
-    protected applyScreenTransformation(rectangle: RectangleF2D): RectangleF2D {
-        throw new Error("not implemented");
+    /**
+     * 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
+     * @param layer is the current rendering layer. There are many layers on top of each other to which can be rendered. Not needed for now.
+     * @param bitmapWidth Not needed for now.
+     * @param bitmapHeight Not needed for now.
+     * @param heightInPixel the height of the text in screen coordinates
+     * @param screenPosition the position of the lower left corner of the text in screen coordinates
+     */
+    protected renderLabel(graphicalLabel: GraphicalLabel, layer: number, bitmapWidth: number,
+                          bitmapHeight: number, heightInPixel: number, screenPosition: PointF2D): void {
+        // ToDo: implement!
+    }
+
+    /**
+     * Renders a rectangle with the given style to the screen.
+     * It is given in screen coordinates.
+     * @param rectangle the rect in screen coordinates
+     * @param layer is the current rendering layer. There are many layers on top of each other to which can be rendered. Not needed for now.
+     * @param styleId the style id
+     */
+    protected renderRectangle(rectangle: RectangleF2D, layer: number, styleId: number): void {
+        // ToDo: implement!
+    }
+
+    /**
+     * Converts a point from unit to pixel space.
+     * @param point
+     * @returns {PointF2D}
+     */
+    protected applyScreenTransformation(point: PointF2D): PointF2D {
+        // ToDo: implement!
+        return point;
+    }
+
+    /**
+     * Converts a rectangle from unit to pixel space.
+     * @param rectangle
+     * @returns {RectangleF2D}
+     */
+    protected applyScreenTransformationForRect(rectangle: RectangleF2D): RectangleF2D {
+        // ToDo: implement!
+        return rectangle;
     }
 }

+ 2 - 1
test/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetDrawer.ts

@@ -5,6 +5,7 @@ import {MusicSheetReader} from "../../../../src/MusicalScore/ScoreIO/MusicSheetR
 import {VexFlowMusicSheetCalculator} from "../../../../src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetCalculator";
 import {TestUtils} from "../../../Util/TestUtils";
 import {IXmlElement} from "../../../../src/Common/FileIO/Xml";
+import {VexFlowTextMeasurer} from "../../../../src/MusicalScore/Graphical/VexFlow/VexFlowTextMeasurer";
 
 describe("VexFlow Music Sheet Drawer", () => {
 
@@ -17,7 +18,7 @@ describe("VexFlow Music Sheet Drawer", () => {
         let reader: MusicSheetReader = new MusicSheetReader();
         let sheet: MusicSheet = reader.createMusicSheet(score, path);
         let gms: GraphicalMusicSheet = new GraphicalMusicSheet(sheet, calc);
-        (new VexFlowMusicSheetDrawer()).drawSheet(gms);
+        (new VexFlowMusicSheetDrawer(new VexFlowTextMeasurer())).drawSheet(gms);
         done();
     });