Browse Source

fix(backendSelection): can now create and remove canvas backend (again), backend option only changed when given, improve backend creation code (#662)

OpenSheetMusicDisplay: backendType is now not any but BackendType (enum from OSMDOptions)
prevent unnecessary backend removal and creation when no option was changed
fix CanvasBackend not being removed properly

VexFlowBackend: removeFromContainer didn't work for CanvasVexFlowBackend, because the wrong child was trying to be removed.
renamed getBackendType to getVexflowBackendType which is now a Vexflow enum and not number,
added getOSMDBackendType (currently unused) so you don't have to use a Vexflow enum/import.

fix #662

TODO: only call drawer.resize when necessary (width/height changed)
sschmid 5 years ago
parent
commit
c0a522cf68

+ 1 - 0
demo/index.js

@@ -241,6 +241,7 @@ import { OpenSheetMusicDisplay } from '../src/OpenSheetMusicDisplay/OpenSheetMus
         openSheetMusicDisplay = new OpenSheetMusicDisplay(canvas, {
             autoResize: true,
             backend: backendSelect.value,
+            //backend: "canvas",
             disableCursor: false,
             drawingParameters: "default", // try compact (instead of default)
             drawPartNames: true, // try false

+ 8 - 4
src/MusicalScore/Graphical/VexFlow/CanvasVexFlowBackend.ts

@@ -6,13 +6,17 @@ import {Fonts} from "../../../Common/Enums/Fonts";
 import {RectangleF2D} from "../../../Common/DataObjects/RectangleF2D";
 import {PointF2D} from "../../../Common/DataObjects/PointF2D";
 import {VexFlowConverter} from "./VexFlowConverter";
+import {BackendType} from "../../../OpenSheetMusicDisplay";
 
 export class CanvasVexFlowBackend extends VexFlowBackend {
-
-    public getBackendType(): number {
+    public getVexflowBackendType(): Vex.Flow.Renderer.Backends {
         return Vex.Flow.Renderer.Backends.CANVAS;
     }
 
+    public getOSMDBackendType(): BackendType {
+        return BackendType.Canvas;
+    }
+
     public initialize(container: HTMLElement): void {
         this.canvas = document.createElement("canvas");
         this.inner = document.createElement("div");
@@ -20,7 +24,7 @@ export class CanvasVexFlowBackend extends VexFlowBackend {
         this.canvas.style.zIndex = "0";
         this.inner.appendChild(this.canvas);
         container.appendChild(this.inner);
-        this.renderer = new Vex.Flow.Renderer(this.canvas, this.getBackendType());
+        this.renderer = new Vex.Flow.Renderer(this.canvas, this.getVexflowBackendType());
         this.ctx = <Vex.Flow.CanvasContext>this.renderer.getContext();
     }
 
@@ -33,7 +37,7 @@ export class CanvasVexFlowBackend extends VexFlowBackend {
         this.canvas = document.createElement("canvas");
         (this.canvas as any).width = width;
         (this.canvas as any).height = height;
-        this.renderer = new Vex.Flow.Renderer(this.canvas, this.getBackendType());
+        this.renderer = new Vex.Flow.Renderer(this.canvas, this.getVexflowBackendType());
         this.ctx = <Vex.Flow.CanvasContext>this.renderer.getContext();
     }
 

+ 8 - 3
src/MusicalScore/Graphical/VexFlow/SvgVexFlowBackend.ts

@@ -6,23 +6,28 @@ import {FontStyles} from "../../../Common/Enums/FontStyles";
 import {Fonts} from "../../../Common/Enums/Fonts";
 import {RectangleF2D} from "../../../Common/DataObjects/RectangleF2D";
 import {PointF2D} from "../../../Common/DataObjects/PointF2D";
-import { EngravingRules } from "..";
+import {EngravingRules} from "..";
+import {BackendType} from "../../../OpenSheetMusicDisplay";
 
 export class SvgVexFlowBackend extends VexFlowBackend {
 
     private ctx: Vex.Flow.SVGContext;
 
-    public getBackendType(): number {
+    public getVexflowBackendType(): Vex.Flow.Renderer.Backends {
         return Vex.Flow.Renderer.Backends.SVG;
     }
 
+    public getOSMDBackendType(): BackendType {
+        return BackendType.SVG;
+    }
+
     public initialize(container: HTMLElement): void {
         this.canvas = document.createElement("div");
         this.inner = this.canvas;
         this.inner.style.position = "relative";
         this.canvas.style.zIndex = "0";
         container.appendChild(this.inner);
-        this.renderer = new Vex.Flow.Renderer(this.canvas, this.getBackendType());
+        this.renderer = new Vex.Flow.Renderer(this.canvas, this.getVexflowBackendType());
         this.ctx = <Vex.Flow.SVGContext>this.renderer.getContext();
     }
 

+ 15 - 2
src/MusicalScore/Graphical/VexFlow/VexFlowBackend.ts

@@ -4,6 +4,7 @@ import {Fonts} from "../../../Common/Enums/Fonts";
 import {RectangleF2D} from "../../../Common/DataObjects/RectangleF2D";
 import {PointF2D} from "../../../Common/DataObjects/PointF2D";
 import {GraphicalMusicPage} from "..";
+import {BackendType} from "../../../OpenSheetMusicDisplay";
 
 export class VexFlowBackends {
   public static CANVAS: 0;
@@ -33,7 +34,14 @@ export abstract class VexFlowBackend {
   }
 
   public removeFromContainer(container: HTMLElement): void {
-    container.removeChild(this.canvas);
+    //console.log("backend type: " + this.getVexflowBackendType());
+    if (this.getVexflowBackendType() === Vex.Flow.Renderer.Backends.SVG) {
+      container.removeChild(this.canvas);
+    } else if (this.getVexflowBackendType() === Vex.Flow.Renderer.Backends.CANVAS) {
+      container.removeChild(this.inner);
+      // for SVG, this.canvas === this.inner, but for Canvas, removing this.canvas causes an error because it's not a child of container,
+      // so we have to remove this.inner instead.
+    }
   }
 
 public abstract getContext(): Vex.IRenderContext;
@@ -66,7 +74,12 @@ public abstract getContext(): Vex.IRenderContext;
 
   public abstract renderCurve(points: PointF2D[]): void;
 
-  public abstract getBackendType(): number;
+  public abstract getVexflowBackendType(): Vex.Flow.Renderer.Backends;
+
+  /** The general type of backend: Canvas or SVG.
+   * This is not used for now (only VexflowBackendType used), but it may be useful when we don't want to use a Vexflow class.
+   */
+  public abstract getOSMDBackendType(): BackendType;
 
   protected renderer: Vex.Flow.Renderer;
   protected inner: HTMLElement;

+ 13 - 0
src/OpenSheetMusicDisplay/OSMDOptions.ts

@@ -128,6 +128,11 @@ export enum FillEmptyMeasuresWithWholeRests {
     YesInvisible = 2
 }
 
+export enum BackendType {
+    SVG = 0,
+    Canvas = 1
+}
+
 /** Handles [[IOSMDOptions]], e.g. returning default options with OSMDOptionsStandard() */
 export class OSMDOptions {
     /** Returns the default options for OSMD.
@@ -140,6 +145,14 @@ export class OSMDOptions {
             drawingParameters: DrawingParametersEnum.default,
         };
     }
+
+    public static BackendTypeFromString(value: string): BackendType {
+        if (value && value.toLowerCase() === "canvas") {
+            return BackendType.Canvas;
+        } else {
+            return BackendType.SVG;
+        }
+    }
 }
 
 export interface AutoBeamOptions {

+ 48 - 41
src/OpenSheetMusicDisplay/OpenSheetMusicDisplay.ts

@@ -14,7 +14,7 @@ import { Promise } from "es6-promise";
 import { AJAX } from "./AJAX";
 import * as log from "loglevel";
 import { DrawingParametersEnum, DrawingParameters, ColoringModes } from "../MusicalScore/Graphical/DrawingParameters";
-import { IOSMDOptions, OSMDOptions, AutoBeamOptions } from "./OSMDOptions";
+import { IOSMDOptions, OSMDOptions, AutoBeamOptions, BackendType } from "./OSMDOptions";
 import { EngravingRules, PageFormat } from "../MusicalScore/Graphical/EngravingRules";
 import { AbstractExpression } from "../MusicalScore/VoiceData/Expressions/AbstractExpression";
 import { Dictionary } from "typescript-collections";
@@ -58,6 +58,7 @@ export class OpenSheetMusicDisplay {
         if (options.autoResize === undefined) {
             options.autoResize = true;
         }
+        this.backendType = BackendType.SVG; // default, can be changed by options
         this.setOptions(options);
     }
 
@@ -65,7 +66,8 @@ export class OpenSheetMusicDisplay {
     public zoom: number = 1.0;
 
     private container: HTMLElement;
-    private backendType: any;
+    private backendType: BackendType;
+    private needBackendUpdate: boolean;
     private sheet: MusicSheet;
     private drawer: VexFlowMusicSheetDrawer;
     private graphic: GraphicalMusicSheet;
@@ -173,7 +175,11 @@ export class OpenSheetMusicDisplay {
         if (!this.graphic) {
             throw new Error("OpenSheetMusicDisplay: Before rendering a music sheet, please load a MusicXML file");
         }
-        this.drawer.clear(); // clear canvas before setting width
+        if (this.drawer) {
+            this.drawer.clear(); // clear canvas before setting width
+        }
+        // musicSheetCalculator.clearSystemsAndMeasures() // maybe? don't have reference though
+        // musicSheetCalculator.clearRecreatedObjects();
 
         // Set page width
         const width: number = this.container.offsetWidth;
@@ -197,22 +203,11 @@ export class OpenSheetMusicDisplay {
             this.graphic.Cursors.length = 0;
         }
 
-        // Remove old backends
-        for (const backend of this.drawer.Backends) {
-            backend.removeFromContainer(this.container);
+        if (this.needBackendUpdate) {
+            this.createOrRefreshRenderBackend();
+            this.needBackendUpdate = false;
         }
-        this.drawer.Backends.clear();
 
-        // create new backends
-        for (const page of this.graphic.MusicPages) {
-            const backend: VexFlowBackend = this.createBackend(this.backendType);
-            if (EngravingRules.Rules.PageFormat && !EngravingRules.Rules.PageFormat.IsUndefined) {
-                backend.resize(width, width / EngravingRules.Rules.PageFormat.aspectRatio);
-            } else {
-                backend.resize(width, (page.PositionAndShape.Size.height + 15) * this.zoom * 10.0);
-            }
-            this.drawer.Backends.push(backend);
-        }
         this.drawer.setZoom(this.zoom);
         // Finally, draw
         this.drawer.drawSheet(this.graphic);
@@ -225,7 +220,33 @@ export class OpenSheetMusicDisplay {
         }
     }
 
+    private createOrRefreshRenderBackend(): void {
+        //console.log("render: need update");
+        // Remove old backends
+        if (this.drawer && this.drawer.Backends) {
+            for (const backend of this.drawer.Backends) {
+                backend.removeFromContainer(this.container);
+            }
+            this.drawer.Backends.clear();
+        }
+        // Create the drawer
+        this.drawer = new VexFlowMusicSheetDrawer(this.drawingParameters);
+
+        // Set page width
+        const width: number = this.container.offsetWidth;
+        // TODO may need to be coordinated with render() where width is also used
 
+        // TODO check if resize is necessary. set needResize or something when size was changed
+        for (const page of this.graphic.MusicPages) {
+            const backend: VexFlowBackend = this.createBackend(this.backendType);
+            if (EngravingRules.Rules.PageFormat && !EngravingRules.Rules.PageFormat.IsUndefined) {
+                backend.resize(width, width / EngravingRules.Rules.PageFormat.aspectRatio);
+            } else {
+                backend.resize(width, (page.PositionAndShape.Size.height + 15) * this.zoom * 10.0);
+            }
+            this.drawer.Backends.push(backend);
+        }
+    }
 
     /** States whether the render() function can be safely called. */
     public IsReadyToRender(): boolean {
@@ -256,29 +277,15 @@ export class OpenSheetMusicDisplay {
                 (<any>DrawingParametersEnum)[options.drawingParameters.toLowerCase()];
         }
 
-        this.backendType = options.backend;
-        // const updateExistingBackend: boolean = this.backend !== undefined;
-        // if (options.backend !== undefined || this.backend === undefined) {
-        //     if (updateExistingBackend) {
-        //         // TODO doesn't work yet, still need to create a new OSMD object
-
-        //         this.drawer.clear();
-
-        //         // musicSheetCalculator.clearSystemsAndMeasures() // maybe? don't have reference though
-        //         // musicSheetCalculator.clearRecreatedObjects();
-        //     }
-        // }
-
-        // Create the drawer
-        if (this.drawer) {
-            // Remove old backends
-            for (const backend of this.drawer.Backends) {
-                backend.removeFromContainer(this.container);
-            }
-            this.drawer.Backends.clear();
+        const backendNotInitialized: boolean = !this.drawer || !this.drawer.Backends || this.drawer.Backends.length < 1;
+        let needBackendUpdate: boolean = backendNotInitialized;
+        if (options.backend !== undefined) {
+            const backendTypeGiven: BackendType = OSMDOptions.BackendTypeFromString(options.backend);
+            needBackendUpdate = needBackendUpdate || this.backendType !== backendTypeGiven;
+            this.backendType = backendTypeGiven;
         }
-
-        this.drawer = new VexFlowMusicSheetDrawer(this.drawingParameters);
+        this.needBackendUpdate = needBackendUpdate;
+        // TODO this is a necessary step during the OSMD constructor. Maybe move this somewhere else
 
         // individual drawing parameters options
         if (options.autoBeam !== undefined) { // only change an option if it was given in options, otherwise it will be undefined
@@ -596,9 +603,9 @@ export class OpenSheetMusicDisplay {
         }
     }
 
-    public createBackend(type: any): VexFlowBackend {
+    public createBackend(type: BackendType): VexFlowBackend {
         let backend: VexFlowBackend;
-        if (type === undefined || type.toLowerCase() === "svg") {
+        if (type === undefined || type === BackendType.SVG) {
             backend = new SvgVexFlowBackend();
         } else {
             backend = new CanvasVexFlowBackend();