@stringsync/vexml
Version:
MusicXML to Vexflow
58 lines (57 loc) • 2.13 kB
JavaScript
import * as util from '../util';
import { Rect } from '../spatial';
/**
* After a system is rendered, we may learn there is excess height from its components. This class recursivley moves
* all the rects by the excess height such that we can honor the SYSTEM_MARGIN_BOTTOM configuration without
* re-rendering. This is much faster than re-rendering the system at a different position.
*/
export class SystemRenderMover {
moveBy(systemRender, dy) {
const seen = new Set(); // avoid circular references
const move = (obj) => {
if (seen.has(obj)) {
return;
}
seen.add(obj);
if (this.isMovable(obj)) {
obj.rect = obj.rect.translate({ dy });
}
if (this.hasIntrinsicRect(obj)) {
obj.intrinsicRect = obj.intrinsicRect.translate({ dy });
}
if (this.hasPlayableRect(obj)) {
obj.playableRect = obj.playableRect.translate({ dy });
}
if (Array.isArray(obj)) {
for (const item of obj) {
move(item);
}
}
else if (util.isPOJO(obj)) {
for (const key in obj) {
move(obj[key]);
}
}
};
move(systemRender);
// Before finishing, we move the vexflow staves. Since everything is linked to them, this should complete the move.
// Any future supported vexflow object not connected to a stave will need to be moved here.
systemRender.measureRenders
.flatMap((m) => m.fragmentRenders)
.flatMap((e) => e.partRenders)
.flatMap((p) => p.staveRenders)
.map((s) => s.vexflowStave)
.forEach((s) => {
s.setY(s.getY() + dy);
});
}
isMovable(obj) {
return !!obj && obj.rect instanceof Rect;
}
hasIntrinsicRect(obj) {
return !!obj && obj.intrinsicRect instanceof Rect;
}
hasPlayableRect(obj) {
return !!obj && obj.playableRect instanceof Rect;
}
}