VexFlow - Copyright (c) Mohit Muthanna 2010.
A formatter for abstract tickable objects, such as notes, chords, tabs, etc.
import { Vex } from './vex';
import { Fraction } from './fraction';
export class TickContext {
static getNextContext(tContext) {
const contexts = tContext.tContexts;
const index = contexts.indexOf(tContext);
return contexts[index + 1];
}
constructor() {
this.currentTick = new Fraction(0, 1);
this.maxTicks = new Fraction(0, 1);
this.minTicks = null;
this.width = 0;
this.padding = 3; // padding on each side (width += padding * 2)
this.pixelsUsed = 0;
this.x = 0;
this.tickables = []; // Notes, tabs, chords, lyrics.
this.notePx = 0; // width of widest note in this context
this.extraLeftPx = 0; // Extra left pixels for modifers & displace notes
this.extraRightPx = 0; // Extra right pixels for modifers & displace notes
this.align_center = false;
this.tContexts = []; // Parent array of tick contextsIgnore this tick context for formatting and justification
this.ignore_ticks = true;
this.preFormatted = false;
this.postFormatted = false;
this.context = null; // Rendering context
}
setContext(context) { this.context = context; return this; }
getContext() { return this.context; }
shouldIgnoreTicks() { return this.ignore_ticks; }
getWidth() { return this.width + (this.padding * 2); }
getX() { return this.x; }
setX(x) { this.x = x; return this; }
getPixelsUsed() { return this.pixelsUsed; }
setPixelsUsed(pixelsUsed) { this.pixelsUsed = pixelsUsed; return this; }
setPadding(padding) { this.padding = padding; return this; }
getMaxTicks() { return this.maxTicks; }
getMinTicks() { return this.minTicks; }
getTickables() { return this.tickables; }
getCenterAlignedTickables() {
return this.tickables.filter(tickable => tickable.isCenterAligned());
}Get widths context, note and left/right modifiers for formatting
getMetrics() {
return { width: this.width, notePx: this.notePx,
extraLeftPx: this.extraLeftPx, extraRightPx: this.extraRightPx };
}
getCurrentTick() { return this.currentTick; }
setCurrentTick(tick) {
this.currentTick = tick;
this.preFormatted = false;
}Get left & right pixels used for modifiers
getExtraPx() {
let left_shift = 0;
let right_shift = 0;
let extraLeftPx = 0;
let extraRightPx = 0;
for (let i = 0; i < this.tickables.length; i++) {
extraLeftPx = Math.max(this.tickables[i].extraLeftPx, extraLeftPx);
extraRightPx = Math.max(this.tickables[i].extraRightPx, extraRightPx);
const mContext = this.tickables[i].modifierContext;
if (mContext && mContext != null) {
left_shift = Math.max(left_shift, mContext.state.left_shift);
right_shift = Math.max(right_shift, mContext.state.right_shift);
}
}
return { left: left_shift, right: right_shift,
extraLeft: extraLeftPx, extraRight: extraRightPx };
}
addTickable(tickable) {
if (!tickable) {
throw new Vex.RERR('BadArgument', 'Invalid tickable added.');
}
if (!tickable.shouldIgnoreTicks()) {
this.ignore_ticks = false;
const ticks = tickable.getTicks();
if (ticks.greaterThan(this.maxTicks)) {
this.maxTicks = ticks.clone();
}
if (this.minTicks == null) {
this.minTicks = ticks.clone();
} else if (ticks.lessThan(this.minTicks)) {
this.minTicks = ticks.clone();
}
}
tickable.setTickContext(this);
this.tickables.push(tickable);
this.preFormatted = false;
return this;
}
preFormat() {
if (this.preFormatted) return;
for (let i = 0; i < this.tickables.length; ++i) {
const tickable = this.tickables[i];
tickable.preFormat();
const metrics = tickable.getMetrics();Maintain max extra pixels from all tickables in the context
this.extraLeftPx = Math.max(this.extraLeftPx,
metrics.extraLeftPx + metrics.modLeftPx);
this.extraRightPx = Math.max(this.extraRightPx,
metrics.extraRightPx + metrics.modRightPx);Maintain the widest note for all tickables in the context
this.notePx = Math.max(this.notePx, metrics.noteWidth);Recalculate the tick context total width
this.width = this.notePx +
this.extraLeftPx +
this.extraRightPx;
}
return this;
}
postFormat() {
if (this.postFormatted) return this;
this.postFormatted = true;
return this;
}
}