|
@@ -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()))
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
-}
|