satie
Version:
A sheet music renderer for the web
159 lines (138 loc) • 5.29 kB
text/typescript
/**
* @source: https://github.com/jnetterf/satie/
*
* @license
* (C) Josh Netterfield <joshua@nettek.ca> 2015.
* Part of the Satie music engraver <https://github.com/jnetterf/satie>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file Renders a tuplet outside of a beam. Unbeamed tuplets are created
* by the beam postprocessor, since they share many similaraties.
*/
// Note that we use notehadBlack regardless of the notehead.
// This keeps spacing consistent, even in beam groups with rests.
import {AboveBelow} from "musicxml-interfaces";
import {createFactory, Component, DOM, PropTypes} from "react";
import {first, last} from "lodash";
import {bravura, getFontOffset} from "./private_smufl";
import TupletNumberView from "./implChord_tupletNumberView";
import {IBeamLayout} from "./implChord_beamLayout";
const $TupletNumberView = createFactory(TupletNumberView);
export interface IProps {
key?: string | number;
stroke: string;
stemWidth: number;
layout: IBeamLayout;
}
export default class UnbeamedTuplet extends Component<IProps, void> {
static contextTypes = {
originY: PropTypes.number.isRequired
} as any;
context: {
originY: number;
};
render(): any {
let {stroke, layout} = this.props;
let {tuplet, x} = layout;
let {placement} = tuplet;
let yOffset = placement === AboveBelow.Above ? 8 : -8;
let isSingleNote = x.length === 1;
let x1 = this._getX1();
let x2 = this._getX2();
let y1 = this._getY1(1);
let y2 = this._getY2(1);
let y1Low = this._getY1(0);
let y2Low = this._getY2(0);
let y1Near = placement === AboveBelow.Below ? y1 : y1Low;
let y1Far = placement === AboveBelow.Below ? y1Low : y1;
let y2Near = placement === AboveBelow.Below ? y2 : y2Low;
let y2Far = placement === AboveBelow.Below ? y2Low : y2;
return DOM.g(null,
!isSingleNote && DOM.polygon({
fill: stroke,
key: "p1",
points: x1 + "," + y1Low + " " +
x2 + "," + y2Low + " " +
x2 + "," + y2 + " " +
x1 + "," + y1,
stroke: stroke,
strokeWidth: 0
}),
!isSingleNote && DOM.line({
fill: stroke,
key: "p2",
stroke,
strokeWidth: bravura.engravingDefaults.tupletBracketThickness * 10,
x1: x1 + 0.5,
x2: x1 + 0.5,
y1: y1Near,
y2: y1Far + yOffset
}),
!isSingleNote && DOM.line({
fill: this.props.stroke,
key: "p3",
stroke,
strokeWidth: bravura.engravingDefaults.tupletBracketThickness * 10,
x1: x2 - 0.5,
x2: x2 - 0.5,
y1: y2Near,
y2: y2Far + yOffset
}),
$TupletNumberView({tuplet, x1, x2, y1, y2})
);
}
/**
* Offset because the note-head has a non-zero width.
*/
getLineXOffset() {
return this.direction() * -this.props.stemWidth / 2;
}
/**
* 1 if the notes go up,
* -1 if the notes go down.
*/
direction() {
return this.props.layout.tuplet.placement === AboveBelow.Above ? 1 : -1;
}
private _withXOffset(x: number) {
return x +
getFontOffset("noteheadBlack", this.direction())[0] * 10 + this.getLineXOffset();
}
private _getX1() {
let {x} = this.props.layout;
return this._withXOffset(first(x)) - 4;
}
private _getX2() {
let {x} = this.props.layout;
return this._withXOffset(last(x)) + 4;
}
private _getY1(incl: number) {
let {originY} = this.context;
let {layout} = this.props;
let {y1} = layout;
return originY - y1 -
this.direction() * getFontOffset("noteheadBlack", this.direction())[1] * 10 -
(incl || 0) * (bravura.engravingDefaults.tupletBracketThickness * 10);
}
private _getY2(incl: number) {
let {originY} = this.context;
let {layout} = this.props;
let {y2} = layout;
return originY - y2 -
this.direction() * getFontOffset("noteheadBlack", this.direction())[1] * 10 -
(incl || 0) * (bravura.engravingDefaults.tupletBracketThickness * 10);
}
};