Procházet zdrojové kódy

Better usage of VexFlow measure size

Andrea Condoluci před 9 roky
rodič
revize
10efd18524

+ 3 - 5
external/vexflow/vexflow.d.ts

@@ -3,7 +3,7 @@ declare namespace Vex {
     const RESOLUTION: any;
 
     export class Formatter {
-      constructor();
+      constructor(opts?: any);
 
       public hasMinTotalWidth: boolean;
       public minTotalWidth: number;
@@ -46,12 +46,9 @@ declare namespace Vex {
     export class Stave {
       constructor(x: number, y: number, width: number, options: any);
 
-      public x: number;
-      public start_x: number;
-      public end_x: number;
-
       public setX(x: number): Stave;
       public setY(y: number): Stave;
+      public getX(): number;
       public addClef(clefSpec: string, size: any, annotation: any, position: any): void;
       public setEndClef(clefSpec: string, size: any, annotation: any): void;
       public getModifiers(): StaveModifier[];
@@ -60,6 +57,7 @@ declare namespace Vex {
       public setWidth(width: number): Stave;
       public getNoteStartX(): number;
       public getNoteEndX(): number;
+      public setNoteStartX(x: number): Stave;
       public format(): void;
       public getSpacingBetweenLines(): number;
       public getNumLines(): number;

+ 0 - 216
src/MusicalScore/Calculation/MeasureSizeCalculator.ts

@@ -1,216 +0,0 @@
-import Vex = require("vexflow");
-import StaveNote = Vex.Flow.StaveNote;
-
-// The type PositionAndShapeInfo is still to be ported in TypeScript
-export type PositionAndShapeInfo = any;
-
-/* TODO
- * Complete support for StaveModifiers
- * Take into account Ties and Slurs
- */
-
-/* Measure Size Calculator
- *  Given a stave, voices and a formatter, calculates
- *  through VexFlow the size of a measure.
- *  !!! before using this, call the methods
- *  !!! joinVoices and preCalculateMinTotalWidth
- *  !!! of the formatter!
- *
- * Usage:
- *   let stave: Vex.Flow.Stave = ...;
- *   let formatter = new Vex.Flow.Formatter()
- *   let voices: Vex.Flor.Voice[] = ...;
- *   formatter.preCalculateMinTotalWidth(voices);
- *   let calc = new MeasureSizeCalculator(stave, voices, formatter);
- *   calc.???
- */
-export class MeasureSizeCalculator {
-  private stave: Vex.Flow.Stave;
-  private voices: Vex.Flow.Voice[];
-  private formatter: any;
-
-  private offsetLeft: number;
-  private offsetRight: number;
-  private voicesWidth: number;
-  private topBorder: number;
-  private bottomBorder: number;
-
-  constructor(
-    stave: Vex.Flow.Stave,
-    voices: Vex.Flow.Voice[],
-    formatter: Vex.Flow.Formatter
-  ) {
-    this.stave = stave;
-    this.voices = voices;
-    this.formatter = formatter;
-    // the stave must be initialized with width, x, y 0
-    // the voices must be already joined and (pre)formatted
-    if (!formatter.hasMinTotalWidth) {
-      throw "Must first call Formatter.preCalculateMinTotalWidth " +
-      "with all the voices in the measure (vertical)";
-    }
-    this.format();
-  }
-
-  // Returns the shape of the note head at position _index_ inside _note_.
-  // Remember: in VexFlow, StaveNote correspond to PhonicScore's VoiceEntries.
-  //  public static getVexFlowNoteHeadShape(note: StaveNote, index: number): PositionAndShapeInfo {
-  //  // note_heads is not public in StaveNote, but we access it anyway...
-  //  let bb = note.note_heads[index].getBoundingBox();
-  //  let info: any = new PositionAndShapeInfo();
-  //  let x: number = bb.getX();
-  //  let y: number = bb.getY();
-  //  let w: number = bb.getW();
-  //  let h: number = bb.getH();
-  //  info.Left = info.Right = bb.getW() / 2;
-  //  info.Top = info.Bottom = bb.getH() / 2;
-  //  info.X = bb.getX() + info.Left;
-  //  info.Y = bb.getY() + info.Bottom;
-  //  return info;
-  //}
-
-  // Returns the shape of all the note heads inside a StaveNote.
-  // Remember: in VexFlow, StaveNote correspond to PhonicScore's VoiceEntries.
-  public static getVexFlowStaveNoteShape(note: StaveNote): PositionAndShapeInfo {
-    let info: PositionAndShapeInfo = {};
-    let bounds: any = note.getNoteHeadBounds();
-    let beginX: number = note.getNoteHeadBeginX();
-    let endX: number = note.getNoteHeadEndX();
-
-    info.Left = info.Right = (endX - beginX) / 2;
-    info.Top = info.Bottom = (bounds.y_top - bounds.y_bottom) / 2;
-    info.X = beginX + info.Left;
-    info.Y = bounds.y_bottom + info.Bottom;
-    return info;
-  }
-
-
-  public static getClefBoundingBox(clef: Vex.Flow.Clef): Vex.Flow.BoundingBox {
-    let clef2: any = clef;
-    clef2.placeGlyphOnLine(clef2.glyph, clef2.stave, clef2.clef.line);
-    let glyph: any = clef.glyph;
-    let posX: number = clef.x + glyph.x_shift;
-    let posY: number = clef.stave.getYForGlyphs() + glyph.y_shift;
-    let scale: number = glyph.scale;
-    let outline: any[] = glyph.metrics.outline;
-    let xmin: number = 0, xmax: number = 0, ymin: number = 0, ymax: number = 0;
-
-    function update(i: number): void {
-      let x: number = outline[i + 1];
-      let y: number = outline[i + 2];
-      xmin = Math.min(xmin, x);
-      xmax = Math.max(xmax, x);
-      ymin = Math.min(ymin, y);
-      ymax = Math.max(ymax, y);
-    }
-
-    for (let i: number = 0, len: number = outline.length; i < len; i += 3) {
-      switch (outline[i] as string) {
-        case "m": update(i); break;
-        case "l": update(i); break;
-        case "q": i += 2; update(i); break;
-        case "b": i += 4; update(i); break;
-        default: break;
-      }
-
-    }
-    return new Vex.Flow.BoundingBox(
-        posX + xmin * scale,
-        posY - ymin * scale,
-        (xmax - xmin) * scale,
-        (ymin - ymax) * scale
-    );
-  }
-
-
-  public static getKeySignatureBoundingBox(sig: any): Vex.Flow.BoundingBox {
-    // FIXME: Maybe use Vex.Flow.keySignature(this.keySpec);
-    let stave: Vex.Flow.Stave = sig.getStave();
-    let width: number = sig.getWidth();
-    let maxLine: number = 1;
-    let minLine: number = 1;
-    for (let acc of sig.accList) {
-      maxLine = Math.max(acc.line, maxLine);
-      minLine = Math.min(acc.line, minLine);
-    }
-    let y: number = sig.getStave().getYForLine(minLine);
-    let height: number = stave.getSpacingBetweenLines() * (maxLine - minLine);
-    let x: number = 0; // FIXME
-    return new Vex.Flow.BoundingBox(x, y, width, height);
-  }
-
-
-  public getWidth(): number {
-    // begin_modifiers + voices + end_modifiers
-    return this.offsetLeft + this.voicesWidth + this.offsetRight;
-    // = stave.end_x - stave.x
-  }
-
-  public getHeight(): number {
-    // FIXME this formula does not take into account
-    // other things like staves and ties!
-    return this.stave.getSpacingBetweenLines()
-      * (this.topBorder - this.bottomBorder);
-  }
-
-  // The following methods return a number
-  // where 0 is the upper line of the stave.
-
-  public getTopBorder(): number {
-    return this.topBorder;
-  }
-
-  public getBottomBorder(): number {
-    return this.bottomBorder;
-  }
-
-  private format(): void {
-    let stave: Vex.Flow.Stave = this.stave;
-    let voices: Vex.Flow.Voice[] = this.voices;
-    let voicesBoundingBox: Vex.Flow.BoundingBox;
-    let bb: Vex.Flow.BoundingBox;
-    // Compute widths
-    this.voicesWidth = this.formatter.minTotalWidth;
-    stave.setWidth(this.voicesWidth);
-    stave.format();
-    this.offsetLeft = stave.getNoteStartX() - stave.x;
-    this.offsetRight = stave.end_x - stave.getWidth() - stave.start_x;
-    // Compute heights
-    // Height is:
-    //// height of StaveModifiers + BoundingBox of notes + height of NoteMod's
-    for (let i: number = 0; i < this.voices.length; i ++) {
-      voices[i].setStave(stave);
-      bb = voices[i].getBoundingBox();
-      if (voicesBoundingBox === undefined) {
-        voicesBoundingBox = bb;
-      } else {
-        voicesBoundingBox = voicesBoundingBox.mergeWith(bb);
-      }
-    }
-    // TODO voicesBoundingBox.getW() should be similar to this.voicesWidth?
-    //console.log("this.width", this.voicesWidth);
-    //console.log("voicesBB", voicesBoundingBox.getW());
-    //this.height = voicesBoundingBox.getH(); FIXME
-
-    // Consider clefs
-    let clefs: Vex.Flow.Clef[] = stave.getModifiers(
-      Vex.Flow.Modifier.Position.LEFT,
-      Vex.Flow.Clef.category
-    );
-    for (let clef of clefs) {
-      voicesBoundingBox = voicesBoundingBox.mergeWith(
-        MeasureSizeCalculator.getClefBoundingBox(clef)
-      );
-    }
-
-    this.topBorder = Math.min(
-      0,
-      Math.floor(stave.getLineForY(voicesBoundingBox.getY()))
-    );
-    this.bottomBorder = Math.max(
-      stave.getNumLines(),
-      Math.ceil(stave.getLineForY(voicesBoundingBox.getY() + voicesBoundingBox.getH()))
-    );
-  }
-
-}

+ 37 - 27
src/MusicalScore/Graphical/VexFlow/VexFlowMeasure.ts

@@ -15,6 +15,7 @@ import {GraphicalStaffEntry} from "../GraphicalStaffEntry";
 import StaveConnector = Vex.Flow.StaveConnector;
 import StaveModifier = Vex.Flow.StaveModifier;
 import StaveNote = Vex.Flow.StaveNote;
+import {Logging} from "../../../Common/logging";
 
 export class VexFlowMeasure extends StaffMeasure {
     constructor(staff: Staff, staffLine: StaffLine = undefined, sourceMeasure: SourceMeasure = undefined) {
@@ -29,8 +30,6 @@ export class VexFlowMeasure extends StaffMeasure {
     public vfVoices: { [voiceID: number]: Vex.Flow.Voice; } = {};
     // Call this function (if present) to x-format all the voices in the measure
     public formatVoices: (width: number) => void;
-    // The unit
-    public unit: number = 10.0;
 
     // The VexFlow Stave (one measure in one line)
     private stave: Vex.Flow.Stave;
@@ -53,19 +52,21 @@ export class VexFlowMeasure extends StaffMeasure {
     public resetLayout(): void {
         // Take into account some space for the begin and end lines of the stave
         // Will be changed when repetitions will be implemented
-        this.beginInstructionsWidth = 20 / this.unit;
-        this.endInstructionsWidth = 20 / this.unit;
+        //this.beginInstructionsWidth = 20 / 10.0;
+        //this.endInstructionsWidth = 20 / 10.0;
         this.stave = new Vex.Flow.Stave(0, 0, 0, {
             space_above_staff_ln: 0,
             space_below_staff_ln: 0,
         });
+        this.updateInstructionWidth();
     }
 
     public clean(): void {
         //this.beams = {};
         //this.vfbeams = undefined;
         this.connectors = [];
-        console.log("clean!");
+        // Clean up instructions
+        this.resetLayout();
     }
 
     /**
@@ -78,9 +79,9 @@ export class VexFlowMeasure extends StaffMeasure {
         let vfline: any = VexFlowConverter.line(line);
         switch (vfline) {
             case Vex.Flow.StaveConnector.type.SINGLE:
-                return 1.0 / this.unit;
+                return 1.0 / 10.0;
             case Vex.Flow.StaveConnector.type.DOUBLE:
-                return 3.0 / this.unit;
+                return 3.0 / 10.0;
             default:
                 return 0;
         }
@@ -95,7 +96,7 @@ export class VexFlowMeasure extends StaffMeasure {
         this.octaveOffset = clef.OctaveOffset;
         let vfclef: string = VexFlowConverter.Clef(clef);
         this.stave.addClef(vfclef, undefined, undefined, Vex.Flow.Modifier.Position.BEGIN);
-        this.increaseBeginInstructionWidth();
+        this.updateInstructionWidth();
     }
 
     /**
@@ -124,7 +125,7 @@ export class VexFlowMeasure extends StaffMeasure {
             timeSig,
             Vex.Flow.Modifier.Position.BEGIN
         );
-        this.increaseBeginInstructionWidth();
+        this.updateInstructionWidth();
     }
 
     /**
@@ -135,7 +136,7 @@ export class VexFlowMeasure extends StaffMeasure {
     public addClefAtEnd(clef: ClefInstruction): void {
         let vfclef: string = VexFlowConverter.Clef(clef);
         this.stave.setEndClef(vfclef, undefined, undefined);
-        this.increaseEndInstructionWidth();
+        this.updateInstructionWidth();
     }
 
     /**
@@ -145,12 +146,14 @@ export class VexFlowMeasure extends StaffMeasure {
     public setWidth(width: number): void {
         super.setWidth(width);
         // Set the width of the Vex.Flow.Stave
-        this.stave.setWidth(width * this.unit);
+        this.stave.setWidth(width * 10.0);
+        // Force the width of the Begin Instructions
+        this.stave.setNoteStartX(this.beginInstructionsWidth * 10.0);
         // If this is the first stave in the vertical measure, call the format
         // method to set the width of all the voices
         if (this.formatVoices) {
             // The width of the voices does not include the instructions (StaveModifiers)
-            this.formatVoices((width - this.beginInstructionsWidth - this.endInstructionsWidth) * this.unit);
+            this.formatVoices((width - this.beginInstructionsWidth - this.endInstructionsWidth) * 10.0);
         }
     }
 
@@ -248,7 +251,7 @@ export class VexFlowMeasure extends StaffMeasure {
                     if (notes.length > 1) {
                         vfbeams.push(new Vex.Flow.Beam(notes, true));
                     } else {
-                        console.log("Warning! Beam with no notes! Trying to ignore, but this is a serious problem.");
+                        Logging.log("Warning! Beam with no notes! Trying to ignore, but this is a serious problem.");
                     }
                 }
             }
@@ -289,20 +292,27 @@ export class VexFlowMeasure extends StaffMeasure {
         return this.stave;
     }
 
-    private increaseBeginInstructionWidth(): void {
-        let modifiers: StaveModifier[] = this.stave.getModifiers();
-        let modifier: StaveModifier = modifiers[modifiers.length - 1];
-        //let padding: number = modifier.getCategory() === "keysignatures" ? modifier.getPadding(2) : 0;
-        let padding: number = modifier.getPadding(20);
-        let width: number = modifier.getWidth();
-        this.beginInstructionsWidth += (padding + width) / this.unit;
-    }
+    //private increaseBeginInstructionWidth(): void {
+    //    let modifiers: StaveModifier[] = this.stave.getModifiers();
+    //    let modifier: StaveModifier = modifiers[modifiers.length - 1];
+    //    //let padding: number = modifier.getCategory() === "keysignatures" ? modifier.getPadding(2) : 0;
+    //    let padding: number = modifier.getPadding(20);
+    //    let width: number = modifier.getWidth();
+    //    this.beginInstructionsWidth += (padding + width) / 10.0;
+    //}
+    //
+    //private increaseEndInstructionWidth(): void {
+    //    let modifiers: StaveModifier[] = this.stave.getModifiers();
+    //    let modifier: StaveModifier = modifiers[modifiers.length - 1];
+    //    let padding: number = 0;
+    //    let width: number = modifier.getWidth();
+    //    this.endInstructionsWidth += (padding + width) / 10.0;
+    //
+    //}
 
-    private increaseEndInstructionWidth(): void {
-        let modifiers: StaveModifier[] = this.stave.getModifiers();
-        let modifier: StaveModifier = modifiers[modifiers.length - 1];
-        let padding: number = 0;
-        let width: number = modifier.getWidth();
-        this.endInstructionsWidth += (padding + width) / this.unit;
+    private updateInstructionWidth(): void {
+        this.stave.format();
+        this.beginInstructionsWidth = this.stave.getNoteStartX() / 10.0;
+        this.endInstructionsWidth = this.stave.getNoteEndX() / 10.0;
     }
 }

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

@@ -64,7 +64,9 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
         }
         // Format the voices
         let allVoices: Vex.Flow.Voice[] = [];
-        let formatter: Vex.Flow.Formatter = new Vex.Flow.Formatter();
+        let formatter: Vex.Flow.Formatter = new Vex.Flow.Formatter({
+            align_rests: true,
+        });
         for (let measure of measures) {
             let mvoices:  { [voiceID: number]: Vex.Flow.Voice; } = (measure as VexFlowMeasure).vfVoices;
             let voices: Vex.Flow.Voice[] = [];
@@ -81,7 +83,9 @@ export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
             formatter.joinVoices(voices);
         }
         let firstMeasure: VexFlowMeasure = measures[0] as VexFlowMeasure;
-        let width: number = formatter.preCalculateMinTotalWidth(allVoices) / firstMeasure.unit;
+        // FIXME: The following ``+ 5.0'' is temporary: it was added as a workaround for
+        // FIXME: a more relaxed formatting of voices
+        let width: number = formatter.preCalculateMinTotalWidth(allVoices) / 10.0 + 5.0;
         for (let measure of measures) {
             measure.minimumStaffEntriesWidth = width;
             (measure as VexFlowMeasure).formatVoices = undefined;

+ 0 - 90
test/MusicalScore/Calculation/MeasureSizeCalculator.ts

@@ -1,90 +0,0 @@
-import {
-  MeasureSizeCalculator,
-} from "../../../src/MusicalScore/Calculation/MeasureSizeCalculator.ts";
-
-import Vex = require("vexflow");
-
-describe("Measure Size Calculator Tests", () => {
-  // Initialization
-  let stave: Vex.Flow.Stave = new Vex.Flow.Stave(0, 0, 0, {});
-  let voices: Vex.Flow.Voice[];
-  let formatter: Vex.Flow.Formatter;
-  let voice: Vex.Flow.Voice;
-  let note: Vex.Flow.StaveNote;
-  let calc: MeasureSizeCalculator;
-
-  it("One note", (done: MochaDone) => {
-    formatter = new Vex.Flow.Formatter();
-    voice = new Vex.Flow.Voice(undefined);
-    note = new Vex.Flow.StaveNote({ keys: ["b/4"], "duration": "1" });
-    voice.addTickables([note]);
-    voices = [voice];
-
-    chai.expect(formatter.preCalculateMinTotalWidth(voices)).to.equal(22);
-
-    calc = new MeasureSizeCalculator(stave, voices, formatter);
-
-    chai.expect(calc.getBottomBorder()).to.equal(5);
-    done();
-  });
-
-  it("Four quarter notes", (done: MochaDone) => {
-    formatter = new Vex.Flow.Formatter();
-    voice = new Vex.Flow.Voice(undefined);
-    voice.addTickables([
-      new Vex.Flow.StaveNote({ keys: ["c/4"], "duration": "q" }),
-      new Vex.Flow.StaveNote({ keys: ["d/4"], "duration": "q" }),
-      new Vex.Flow.StaveNote({ keys: ["e/4"], "duration": "q" }),
-      new Vex.Flow.StaveNote({ keys: ["f/4"], "duration": "q" }),
-    ]);
-    voices = [voice];
-
-    chai.expect(formatter.preCalculateMinTotalWidth(voices)).to.equal(64);
-    calc = new MeasureSizeCalculator(stave, voices, formatter);
-
-    chai.expect(calc.getWidth()).to.equal(64);
-    chai.expect(calc.getBottomBorder()).to.equal(6);
-    chai.expect(calc.getTopBorder()).to.equal(0);
-    done();
-  });
-
-  it("Will certainly pass", (done: MochaDone) => {
-    let visual: (testfun: (r: any, ctx: any) => void ) => void;
-    visual = function(func: (r: any, ctx: any) => void): void {
-      let canvas: HTMLCanvasElement = document.createElement("canvas");
-      document.body.appendChild(canvas);
-      let renderer: any = new Vex.Flow.Renderer(
-        canvas,
-        Vex.Flow.Renderer.Backends.CANVAS
-      );
-      renderer.resize(300, 100);
-      let ctx: any = renderer.getContext();
-      ctx.setFont("Arial", 10, "").setBackgroundFillStyle("#eed");
-      func(renderer, ctx);
-    };
-
-    visual((renderer: any, ctx: any): void => {
-      renderer.resize(420, 120);
-      let stave2: Vex.Flow.Stave = new Vex.Flow.Stave(10, 0, 410, {});
-      stave2.setContext(ctx);
-      for (let t in Vex.Flow.Clef.types) {
-        if (Vex.Flow.Clef.types.hasOwnProperty(t)) {
-          let clef: Vex.Flow.Clef = new Vex.Flow.Clef(t);
-          stave2.addModifier(clef, Vex.Flow.Modifier.Position.BEGIN);
-          stave2.format();
-
-          clef.setStave(stave2);
-          let bb: Vex.Flow.BoundingBox =
-              MeasureSizeCalculator.getClefBoundingBox(clef);
-          //console.log(bb);
-          ctx.rect(bb.getX(), bb.getY(), bb.getW(), bb.getH());
-          ctx.stroke();
-        }
-      }
-      stave2.draw();
-    });
-
-    done();
-  });
-
-});