123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501 |
- import {StaffLine} from "./StaffLine";
- import {Instrument} from "../Instrument";
- import {BoundingBox} from "./BoundingBox";
- import {Fraction} from "../../Common/DataObjects/Fraction";
- import {SourceMeasure} from "../VoiceData/SourceMeasure";
- import {InstrumentalGroup} from "../InstrumentalGroup";
- import {TextAlignmentEnum} from "../../Common/Enums/TextAlignment";
- import {GraphicalMusicPage} from "./GraphicalMusicPage";
- import {GraphicalLabel} from "./GraphicalLabel";
- import {GraphicalMeasure} from "./GraphicalMeasure";
- import {GraphicalObject} from "./GraphicalObject";
- import {EngravingRules} from "./EngravingRules";
- import {PointF2D} from "../../Common/DataObjects/PointF2D";
- import {GraphicalStaffEntry} from "./GraphicalStaffEntry";
- import {SystemLinesEnum} from "./SystemLinesEnum";
- import Dictionary from "typescript-collections/dist/lib/Dictionary";
- import {GraphicalComment} from "./GraphicalComment";
- import {GraphicalMarkedArea} from "./GraphicalMarkedArea";
- import {SystemLine} from "./SystemLine";
- import {SystemLinePosition} from "./SystemLinePosition";
- import {Staff} from "../VoiceData/Staff";
- import { Label } from "../Label";
- /**
- * A MusicSystem contains the [[StaffLine]]s for all instruments, until a line break
- */
- export abstract class MusicSystem extends GraphicalObject {
- public needsToBeRedrawn: boolean = true;
- public rules: EngravingRules;
- protected parent: GraphicalMusicPage;
- protected id: number;
- protected staffLines: StaffLine[] = [];
- protected graphicalMeasures: GraphicalMeasure[][] = [];
- /** Dictionary of (Instruments and) labels.
- * note that the key needs to be unique, GraphicalLabel is not unique yet.
- * That is why the labels are labels.values() and not labels.keys().
- */
- protected labels: Dictionary<Instrument, GraphicalLabel> = new Dictionary<Instrument, GraphicalLabel>();
- protected measureNumberLabels: GraphicalLabel[] = [];
- protected maxLabelLength: number;
- protected objectsToRedraw: [Object[], Object][] = [];
- protected instrumentBrackets: GraphicalObject[] = [];
- protected groupBrackets: GraphicalObject[] = [];
- protected graphicalMarkedAreas: GraphicalMarkedArea[] = [];
- protected graphicalComments: GraphicalComment[] = [];
- protected systemLines: SystemLine[] = [];
- public breaksPage: boolean = false;
- constructor(id: number) {
- super();
- this.id = id;
- this.boundingBox = new BoundingBox(this);
- this.maxLabelLength = 0.0;
- }
- public get Parent(): GraphicalMusicPage {
- return this.parent;
- }
- public set Parent(value: GraphicalMusicPage) {
- // remove from old page
- if (this.parent) {
- const index: number = this.parent.MusicSystems.indexOf(this, 0);
- if (index > -1) {
- this.parent.MusicSystems.splice(index, 1);
- }
- }
- this.parent = value;
- this.boundingBox.Parent = value.PositionAndShape;
- }
- public get NextSystem(): MusicSystem {
- const idxInParent: number = this.Parent.MusicSystems.indexOf(this);
- return idxInParent !== this.Parent.MusicSystems.length ? this.Parent.MusicSystems[idxInParent + 1] : undefined;
- }
- public get StaffLines(): StaffLine[] {
- return this.staffLines;
- }
- public get GraphicalMeasures(): GraphicalMeasure[][] {
- return this.graphicalMeasures;
- }
- public get MeasureNumberLabels(): GraphicalLabel[] {
- return this.measureNumberLabels;
- }
- public get Labels(): GraphicalLabel[] {
- return this.labels.values();
- }
- public get ObjectsToRedraw(): [Object[], Object][] {
- return this.objectsToRedraw;
- }
- public get InstrumentBrackets(): GraphicalObject[] {
- return this.instrumentBrackets;
- }
- public get GroupBrackets(): GraphicalObject[] {
- return this.groupBrackets;
- }
- public get GraphicalMarkedAreas(): GraphicalMarkedArea[] {
- return this.graphicalMarkedAreas;
- }
- public get GraphicalComments(): GraphicalComment[] {
- return this.graphicalComments;
- }
- public get SystemLines(): SystemLine[] {
- return this.systemLines;
- }
- public get Id(): number {
- return this.id;
- }
- /**
- * Create the left vertical Line connecting all staves of the [[MusicSystem]].
- * @param lineWidth
- * @param systemLabelsRightMargin
- */
- public createSystemLeftLine(lineWidth: number, systemLabelsRightMargin: number, isFirstSystem: boolean): void {
- let xPosition: number = -lineWidth / 2;
- if (isFirstSystem) {
- xPosition = this.maxLabelLength + systemLabelsRightMargin - lineWidth / 2;
- }
- const top: GraphicalMeasure = this.staffLines[0].Measures[0];
- let bottom: GraphicalMeasure = undefined;
- if (this.staffLines.length > 1) {
- bottom = this.staffLines[this.staffLines.length - 1].Measures[0];
- }
- const leftSystemLine: SystemLine = this.createSystemLine(xPosition, lineWidth, SystemLinesEnum.SingleThin,
- SystemLinePosition.MeasureBegin, this, top, bottom);
- this.SystemLines.push(leftSystemLine);
- leftSystemLine.PositionAndShape.RelativePosition = new PointF2D(xPosition, 0);
- leftSystemLine.PositionAndShape.BorderLeft = 0;
- leftSystemLine.PositionAndShape.BorderRight = lineWidth;
- leftSystemLine.PositionAndShape.BorderTop = 0;
- leftSystemLine.PositionAndShape.BorderBottom = this.boundingBox.Size.height;
- this.createLinesForSystemLine(leftSystemLine);
- }
- /**
- * Create the vertical Lines after the End of all [[StaffLine]]'s Measures
- * @param xPosition
- * @param lineWidth
- * @param lineType
- * @param linePosition indicates if the line belongs to start or end of measure
- * @param measureIndex the measure index within the staffline
- * @param measure
- */
- public createVerticalLineForMeasure(xPosition: number, lineWidth: number, lineType: SystemLinesEnum, linePosition: SystemLinePosition,
- measureIndex: number, measure: GraphicalMeasure): void {
- //return; // TODO check why there's a bold line here for the double barline sample
- const staffLine: StaffLine = measure.ParentStaffLine;
- const staffLineRelative: PointF2D = new PointF2D(staffLine.PositionAndShape.RelativePosition.x,
- staffLine.PositionAndShape.RelativePosition.y);
- const staves: Staff[] = staffLine.ParentStaff.ParentInstrument.Staves;
- if (staffLine.ParentStaff === staves[0]) {
- let bottomMeasure: GraphicalMeasure = undefined;
- if (staves.length > 1) {
- bottomMeasure = this.getBottomStaffLine(staffLine).Measures[measureIndex];
- }
- const singleVerticalLineAfterMeasure: SystemLine = this.createSystemLine(xPosition, lineWidth, lineType,
- linePosition, this, measure, bottomMeasure);
- const systemXPosition: number = staffLineRelative.x + xPosition;
- singleVerticalLineAfterMeasure.PositionAndShape.RelativePosition = new PointF2D(systemXPosition, 0);
- singleVerticalLineAfterMeasure.PositionAndShape.BorderLeft = 0;
- singleVerticalLineAfterMeasure.PositionAndShape.BorderRight = lineWidth;
- this.SystemLines.push(singleVerticalLineAfterMeasure);
- }
- }
- /**
- * Set the y-Positions of all the system lines in the system and creates the graphical Lines and dots within.
- * @param rules
- */
- public setYPositionsToVerticalLineObjectsAndCreateLines(rules: EngravingRules): void {
- // empty
- }
- public calculateBorders(rules: EngravingRules): void {
- // empty
- }
- public alignBeginInstructions(): void {
- // empty
- }
- public GetLeftBorderAbsoluteXPosition(): number {
- return this.StaffLines[0].PositionAndShape.AbsolutePosition.x + this.StaffLines[0].Measures[0].beginInstructionsWidth;
- }
- public GetRightBorderAbsoluteXPosition(): number {
- return this.StaffLines[0].PositionAndShape.AbsolutePosition.x + this.StaffLines[0].StaffLines[0].End.x;
- }
- public AddGraphicalMeasures(graphicalMeasures: GraphicalMeasure[]): void {
- for (let idx: number = 0, len: number = graphicalMeasures.length; idx < len; ++idx) {
- const graphicalMeasure: GraphicalMeasure = graphicalMeasures[idx];
- graphicalMeasure.ParentMusicSystem = this;
- }
- this.graphicalMeasures.push(graphicalMeasures);
- }
- public GetSystemsFirstTimeStamp(): Fraction {
- return this.graphicalMeasures[0][0].parentSourceMeasure.AbsoluteTimestamp;
- }
- public GetSystemsLastTimeStamp(): Fraction {
- const m: SourceMeasure = this.graphicalMeasures[this.graphicalMeasures.length - 1][0].parentSourceMeasure;
- return Fraction.plus(m.AbsoluteTimestamp, m.Duration);
- }
- /**
- * Create an InstrumentBracket for each multiStave Instrument.
- * @param instruments
- * @param staffHeight
- */
- public createInstrumentBrackets(instruments: Instrument[], staffHeight: number): void {
- for (let idx: number = 0, len: number = instruments.length; idx < len; ++idx) {
- const instrument: Instrument = instruments[idx];
- if (instrument.Staves.length > 1) {
- let firstStaffLine: StaffLine = undefined, lastStaffLine: StaffLine = undefined;
- for (let idx2: number = 0, len2: number = this.staffLines.length; idx2 < len2; ++idx2) {
- const staffLine: StaffLine = this.staffLines[idx2];
- if (staffLine.ParentStaff === instrument.Staves[0]) {
- firstStaffLine = staffLine;
- }
- if (staffLine.ParentStaff === instrument.Staves[instrument.Staves.length - 1]) {
- lastStaffLine = staffLine;
- }
- }
- if (firstStaffLine && lastStaffLine) {
- this.createInstrumentBracket(firstStaffLine, lastStaffLine);
- }
- }
- }
- }
- /**
- * Create a GroupBracket for an [[InstrumentalGroup]].
- * @param instrumentGroups
- * @param staffHeight
- * @param recursionDepth
- */
- public createGroupBrackets(instrumentGroups: InstrumentalGroup[], staffHeight: number, recursionDepth: number): void {
- for (let idx: number = 0, len: number = instrumentGroups.length; idx < len; ++idx) {
- const instrumentGroup: InstrumentalGroup = instrumentGroups[idx];
- if (instrumentGroup.InstrumentalGroups.length < 1) {
- continue;
- }
- const instrument1: Instrument = this.findFirstVisibleInstrumentInInstrumentalGroup(instrumentGroup);
- const instrument2: Instrument = this.findLastVisibleInstrumentInInstrumentalGroup(instrumentGroup);
- if (instrument1 === undefined || instrument2 === undefined) {
- continue;
- }
- let firstStaffLine: StaffLine = undefined;
- let lastStaffLine: StaffLine = undefined;
- for (let idx2: number = 0, len2: number = this.staffLines.length; idx2 < len2; ++idx2) {
- const staffLine: StaffLine = this.staffLines[idx2];
- if (staffLine.ParentStaff === instrument1.Staves[0]) {
- firstStaffLine = staffLine;
- }
- if (staffLine.ParentStaff === instrument2.Staves[0]) {
- lastStaffLine = staffLine;
- }
- }
- if (firstStaffLine && lastStaffLine) {
- this.createGroupBracket(firstStaffLine, lastStaffLine, recursionDepth);
- }
- if (instrumentGroup.InstrumentalGroups.length < 1) {
- continue;
- }
- this.createGroupBrackets(instrumentGroup.InstrumentalGroups, staffHeight, recursionDepth + 1);
- }
- }
- /**
- * Create the Instrument's Labels (only for the first [[MusicSystem]] of the first MusicPage).
- * @param instrumentLabelTextHeight
- * @param systemLabelsRightMargin
- * @param labelMarginBorderFactor
- */
- public createMusicSystemLabel( instrumentLabelTextHeight: number, systemLabelsRightMargin: number,
- labelMarginBorderFactor: number, isFirstSystem: boolean = false): void {
- for (let idx: number = 0, len: number = this.staffLines.length; idx < len; ++idx) {
- const instrument: Instrument = this.staffLines[idx].ParentStaff.ParentInstrument;
- let instrNameLabel: Label;
- if (isFirstSystem) {
- instrNameLabel = instrument.NameLabel;
- if (!this.rules.RenderPartNames) {
- instrNameLabel = new Label("", instrument.NameLabel.textAlignment, instrument.NameLabel.font);
- systemLabelsRightMargin = 0; // might affect lyricist/tempo placement. but without this there's still some extra x-spacing.
- }
- } else {
- if (!this.rules.RenderPartAbbreviations
- // don't render part abbreviations if there's only one instrument/part (could be an option in the future)
- || this.staffLines.length === 1
- || !instrument.PartAbbreviation
- || instrument.PartAbbreviation === "") {
- return;
- }
- const labelText: string = instrument.PartAbbreviation;
- // const labelText: string = instrument.NameLabel.text[0] + ".";
- instrNameLabel = new Label(labelText, instrument.NameLabel.textAlignment, instrument.NameLabel.font);
- }
- const graphicalLabel: GraphicalLabel = new GraphicalLabel(
- instrNameLabel, instrumentLabelTextHeight, TextAlignmentEnum.LeftCenter, this.rules, this.boundingBox
- );
- graphicalLabel.setLabelPositionAndShapeBorders();
- this.labels.setValue(instrument, graphicalLabel);
- // X-Position will be 0 (Label starts at the same PointF_2D with MusicSystem)
- // Y-Position will be calculated after the y-Spacing
- // graphicalLabel.PositionAndShape.RelativePosition = new PointF2D(0.0, 0.0);
- }
- // calculate maxLabelLength (needed for X-Spacing)
- this.maxLabelLength = 0.0;
- const labels: GraphicalLabel[] = this.labels.values();
- for (let idx: number = 0, len: number = labels.length; idx < len; ++idx) {
- const label: GraphicalLabel = labels[idx];
- if (label.PositionAndShape.Size.width > this.maxLabelLength) {
- this.maxLabelLength = label.PositionAndShape.Size.width;
- }
- }
- this.updateMusicSystemStaffLineXPosition(systemLabelsRightMargin);
- }
- /**
- * Set the Y-Positions for the MusicSystem's Labels.
- */
- public setMusicSystemLabelsYPosition(): void {
- this.labels.forEach((key: Instrument, value: GraphicalLabel): void => {
- let ypositionSum: number = 0;
- let staffCounter: number = 0;
- for (let i: number = 0; i < this.staffLines.length; i++) {
- if (this.staffLines[i].ParentStaff.ParentInstrument === key) {
- for (let j: number = i; j < this.staffLines.length; j++) {
- const staffLine: StaffLine = this.staffLines[j];
- if (staffLine.ParentStaff.ParentInstrument !== key) {
- break;
- }
- ypositionSum += staffLine.PositionAndShape.RelativePosition.y;
- staffCounter++;
- }
- break;
- }
- }
- if (staffCounter > 0) {
- value.PositionAndShape.RelativePosition = new PointF2D(0.0, ypositionSum / staffCounter + 2.0);
- }
- });
- }
- /**
- * Check if two "adjacent" StaffLines have BOTH a StaffEntry with a StaffEntryLink.
- * This is needed for the y-spacing algorithm.
- * @returns {boolean}
- */
- public checkStaffEntriesForStaffEntryLink(): boolean {
- let first: boolean = false;
- let second: boolean = false;
- for (let i: number = 0; i < this.staffLines.length - 1; i++) {
- for (let idx: number = 0, len: number = this.staffLines[i].Measures.length; idx < len; ++idx) {
- const measure: GraphicalMeasure = this.staffLines[i].Measures[idx];
- for (let idx2: number = 0, len2: number = measure.staffEntries.length; idx2 < len2; ++idx2) {
- const staffEntry: GraphicalStaffEntry = measure.staffEntries[idx2];
- if (staffEntry.sourceStaffEntry.Link) {
- first = true;
- }
- }
- }
- for (let idx: number = 0, len: number = this.staffLines[i + 1].Measures.length; idx < len; ++idx) {
- const measure: GraphicalMeasure = this.staffLines[i + 1].Measures[idx];
- for (let idx2: number = 0, len2: number = measure.staffEntries.length; idx2 < len2; ++idx2) {
- const staffEntry: GraphicalStaffEntry = measure.staffEntries[idx2];
- if (staffEntry.sourceStaffEntry.Link) {
- second = true;
- }
- }
- }
- }
- if (first && second) {
- return true;
- }
- return false;
- }
- public getBottomStaffLine(topStaffLine: StaffLine): StaffLine {
- const staves: Staff[] = topStaffLine.ParentStaff.ParentInstrument.Staves;
- const last: Staff = staves[staves.length - 1];
- for (const line of topStaffLine.ParentMusicSystem.staffLines) {
- if (line.ParentStaff === last) {
- return line;
- }
- }
- return undefined;
- }
- /**
- * Here the system line is generated, which acts as the container of graphical lines and dots that will be finally rendered.
- * It holds al the logical parameters of the system line.
- * @param xPosition The x position within the system
- * @param lineWidth The total x width
- * @param lineType The line type enum
- * @param linePosition indicates if the line belongs to start or end of measure
- * @param musicSystem
- * @param topMeasure
- * @param bottomMeasure
- */
- protected createSystemLine(xPosition: number, lineWidth: number, lineType: SystemLinesEnum, linePosition: SystemLinePosition,
- musicSystem: MusicSystem, topMeasure: GraphicalMeasure, bottomMeasure: GraphicalMeasure = undefined): SystemLine {
- throw new Error("not implemented");
- }
- /**
- * Create all the graphical lines and dots needed to render a system line (e.g. bold-thin-dots..).
- * @param systemLine
- */
- protected createLinesForSystemLine(systemLine: SystemLine): void {
- //Empty
- }
- /**
- * Calculates the summed x-width of a possibly given Instrument Brace and/or Group Bracket(s).
- * @returns {number} the x-width
- */
- protected calcBracketsWidth(): number {
- let width: number = 0;
- for (let idx: number = 0, len: number = this.GroupBrackets.length; idx < len; ++idx) {
- const groupBracket: GraphicalObject = this.GroupBrackets[idx];
- width = Math.max(width, groupBracket.PositionAndShape.Size.width);
- }
- for (let idx2: number = 0, len2: number = this.InstrumentBrackets.length; idx2 < len2; ++idx2) {
- const instrumentBracket: GraphicalObject = this.InstrumentBrackets[idx2];
- width = Math.max(width, instrumentBracket.PositionAndShape.Size.width);
- }
- return width;
- }
- protected createInstrumentBracket(firstStaffLine: StaffLine, lastStaffLine: StaffLine): void {
- // no impl here
- }
- protected createGroupBracket(firstStaffLine: StaffLine, lastStaffLine: StaffLine, recursionDepth: number): void {
- // no impl here
- }
- private findFirstVisibleInstrumentInInstrumentalGroup(instrumentalGroup: InstrumentalGroup): Instrument {
- for (let idx: number = 0, len: number = instrumentalGroup.InstrumentalGroups.length; idx < len; ++idx) {
- const groupOrInstrument: InstrumentalGroup = instrumentalGroup.InstrumentalGroups[idx];
- if (groupOrInstrument instanceof Instrument) {
- if ((<Instrument>groupOrInstrument).Visible === true) {
- return <Instrument>groupOrInstrument;
- }
- continue;
- }
- return this.findFirstVisibleInstrumentInInstrumentalGroup(groupOrInstrument);
- }
- return undefined;
- }
- private findLastVisibleInstrumentInInstrumentalGroup(instrumentalGroup: InstrumentalGroup): Instrument {
- let groupOrInstrument: InstrumentalGroup;
- for (let i: number = instrumentalGroup.InstrumentalGroups.length - 1; i >= 0; i--) {
- groupOrInstrument = instrumentalGroup.InstrumentalGroups[i];
- if (groupOrInstrument instanceof Instrument) {
- if ((<Instrument>groupOrInstrument).Visible === true) {
- return <Instrument>groupOrInstrument;
- }
- continue;
- }
- return this.findLastVisibleInstrumentInInstrumentalGroup(groupOrInstrument);
- }
- return undefined;
- }
- /**
- * Update the xPosition of the [[MusicSystem]]'s [[StaffLine]]'s due to [[Label]] positioning.
- * @param systemLabelsRightMargin
- */
- private updateMusicSystemStaffLineXPosition(systemLabelsRightMargin: number): void {
- for (let idx: number = 0, len: number = this.StaffLines.length; idx < len; ++idx) {
- const staffLine: StaffLine = this.StaffLines[idx];
- const relative: PointF2D = staffLine.PositionAndShape.RelativePosition;
- relative.x = this.maxLabelLength + systemLabelsRightMargin;
- staffLine.PositionAndShape.RelativePosition = relative;
- staffLine.PositionAndShape.BorderRight = this.boundingBox.Size.width - this.maxLabelLength - systemLabelsRightMargin;
- for (let i: number = 0; i < staffLine.StaffLines.length; i++) {
- const lineEnd: PointF2D = new PointF2D(staffLine.PositionAndShape.Size.width, staffLine.StaffLines[i].End.y);
- staffLine.StaffLines[i].End = lineEnd;
- }
- }
- }
- }
|