VexFlow - Copyright (c) Mohit Muthanna 2010.
A formatter for abstract tickable objects, such as notes, chords, tabs, etc.
import { Vex } from './vex';
import { Tickable } from './tickable';
import { Fraction } from './fraction';
export class TickContext extends Tickable {
static getNextContext(tContext) {
const contexts = tContext.tContexts;
const index = contexts.indexOf(tContext);
return contexts[index + 1];
}
constructor() {
super();
this.setAttribute('type', 'TickContext');
this.currentTick = new Fraction(0, 1);
this.maxTicks = new Fraction(0, 1);
this.minTicks = null;
this.padding = 3; // padding on each side (width += padding * 2)
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.tContexts = []; // Parent array of tick contexts
}
getX() { return this.x; }
setX(x) { this.x = x; return this; }
getWidth() { return this.width + (this.padding * 2); }
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. THIS METHOD IS DEPRECATED. Use the getMetrics() method instead!
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 || 0, extraLeftPx);
extraRightPx = Math.max(this.tickables[i].extraRightPx || 0, 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 this;
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;
}
}