UNPKG

satie

Version:

A sheet music renderer for the web

196 lines (180 loc) 7.5 kB
/** * This file is part of Satie music engraver <https://github.com/jnetterf/satie>. * Copyright (C) Joshua Netterfield <joshua.ca> 2015 - present. * * Satie 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. * * Satie 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 Satie. If not, see <http://www.gnu.org/licenses/>. */ import {Note, Lyric, Syllabic, SyllabicType, Text, StemType} from "musicxml-interfaces"; import {createFactory, Component, DOM, PropTypes, ReactElement} from "react"; import {map, some, chain, maxBy} from "lodash"; import {bboxes, bravura, getRight} from "./private_smufl"; import BeamView from "./implChord_beamView"; import ChordModel from "./implChord_chordModel"; import FlagView from "./implChord_flagView"; import LedgerLineView from "./implChord_ledgerLineView"; import {DEFAULT_LYRIC_SIZE, DEFAULT_FONT} from "./implChord_lyrics"; import NoteView from "./implChord_noteView"; import NotationView from "./implChord_notationView"; import RestView from "./implChord_restView"; import StemView from "./implChord_stemView"; import UnbeamedTupletView from "./implChord_unbeamedTupletView"; const stemThickness: number = bravura.engravingDefaults.stemThickness * 10; const $BeamView = createFactory(BeamView); const $FlagView = createFactory(FlagView); const $LedgerLineView = createFactory(LedgerLineView); const $NoteView = createFactory(NoteView); const $NotationView = createFactory(NotationView); const $RestView = createFactory(RestView); const $StemView = createFactory(StemView); const $UnbeamedTupletView = createFactory(UnbeamedTupletView); export interface IProps { layout: ChordModel.IChordLayout; } /** * Renders notes and their notations. */ export default class ChordView extends Component<IProps, {}> { static contextTypes = { originY: PropTypes.number.isRequired } as any; context: { originY: number; }; render(): ReactElement<any> { let layout = this.props.layout; let spec = layout.model; let maxNotehead = maxBy(spec.noteheadGlyph, glyph => getRight(glyph)); let anyVisible = some(spec, note => note.printObject !== false); if (!anyVisible) { return null; } let lyKey = 0; let lyrics = chain(<Note[]><any>spec) .map(n => n.lyrics) .filter(l => !!l) .flatten(true) .filter(l => !!l) .map((l: Lyric) => { let text: any[] = []; let currSyllabic = SyllabicType.Single; for (let i = 0; i < l.lyricParts.length; ++i) { switch (l.lyricParts[i]._class) { case "Syllabic": let syllabic = <Syllabic> l.lyricParts[i]; currSyllabic = syllabic.data; break; case "Text": let textPt = <Text> l.lyricParts[i]; let width = bboxes[maxNotehead][0] * 10; text.push(DOM.text({ fontFamily: textPt.fontFamily || DEFAULT_FONT, fontSize: textPt.fontSize || DEFAULT_LYRIC_SIZE, key: ++lyKey, textAnchor: "middle", x: this.props.layout.x + width / 2, y: this.context.originY + 60 }, textPt.data)); break; case "Extend": // TODO break; case "Elision": // TODO break; default: throw new Error(`Unknown class ${l.lyricParts[i]._class}`); } }; return text; }) .flatten() .value(); if (!!spec[0].rest) { return $RestView({ multipleRest: spec.satieMultipleRest, notehead: spec.noteheadGlyph[0], spec: spec[0] }); } const stemX = spec.stemX(); return DOM.g(null, map(spec, (noteSpec: Note, idx: number) => { if (!spec[idx]) { return null; } return $NoteView({ key: "n" + idx, noteheadGlyph: spec.noteheadGlyph[idx], spec: spec[idx], defaultX: spec[idx].defaultX }); }), layout.satieStem && $StemView({ bestHeight: layout.satieStem.stemHeight, tremolo: layout.satieStem.tremolo, key: "s", notehead: maxNotehead, spec: { color: spec[0].stem.color || "#000000", defaultX: stemX, defaultY: (layout.satieStem.stemStart - 3) * 10, type: layout.satieStem.direction === 1 ? StemType.Up : StemType.Down }, width: stemThickness }), map(spec.satieLedger, lineNumber => $LedgerLineView({ key: "l" + lineNumber, notehead: maxNotehead, spec: { color: "#000000", defaultX: stemX, defaultY: (lineNumber - 3) * 10 } })), layout.satieFlag && layout.satieStem && $FlagView({ key: "f", notehead: maxNotehead, spec: { color: spec[0].stem.color || "$000000", defaultX: stemX, defaultY: (layout.satieStem.stemStart - 3) * 10 + (layout.satieStem.stemHeight - 7) * layout.satieStem.direction, direction: layout.satieStem.direction, flag: layout.satieFlag }, stemHeight: layout.satieStem.stemHeight, stemWidth: stemThickness }), this.props.layout.satieBeam && $BeamView({ key: "b", layout: this.props.layout.satieBeam, stemWidth: stemThickness, stroke: "black" }), spec.satieUnbeamedTuplet && $UnbeamedTupletView({ key: "ut", layout: spec.satieUnbeamedTuplet, stemWidth: stemThickness, stroke: "black" }), map(spec, (note, idx) => map(note.notations, (notation, jdx) => $NotationView({ key: `N${idx}_${jdx}`, layout: this.props.layout, defaultY: note.defaultY, spec: notation }))), lyrics ); } }