123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345 |
- import { GraphicalLine } from "./GraphicalLine";
- import { StaffLine } from "./StaffLine";
- import { GraphicalMeasure } from "./GraphicalMeasure";
- import { ContDynamicEnum, ContinuousDynamicExpression } from "../VoiceData/Expressions/ContinuousExpressions/ContinuousDynamicExpression";
- import { PointF2D } from "../../Common/DataObjects/PointF2D";
- import { AbstractGraphicalExpression } from "./AbstractGraphicalExpression";
- import { PlacementEnum } from "../VoiceData/Expressions/AbstractExpression";
- import { SkyBottomLineCalculator } from "./SkyBottomLineCalculator";
- import { ISqueezable } from "./ISqueezable";
- import * as log from "loglevel";
- /**
- * This class prepares the graphical elements for a continuous expression. It calculates the wedges and
- * wrappings if they are split over system breaks.
- */
- export class GraphicalContinuousDynamicExpression extends AbstractGraphicalExpression implements ISqueezable {
- /** True if expression is split over system borders */
- private isSplittedPart: boolean;
- /** True if this expression should not be removed if re-rendered */
- private notToBeRemoved: boolean;
- /** Holds the line objects that can be drawn via implementation */
- private lines: GraphicalLine[] = [];
- private startMeasure: GraphicalMeasure;
- private endMeasure: GraphicalMeasure;
- /**
- * Create a new instance of the GraphicalContinuousDynamicExpression
- * @param continuousDynamic The continuous dynamic instruction read via ExpressionReader
- * @param staffLine The staffline where the exoression is attached
- */
- constructor(continuousDynamic: ContinuousDynamicExpression, staffLine: StaffLine) {
- super(staffLine, continuousDynamic);
- this.isSplittedPart = false;
- this.notToBeRemoved = false;
- }
- //#region Getter / Setter
- /** The graphical measure where the parent continuous dynamic expression starts */
- public get StartMeasure(): GraphicalMeasure { return this.startMeasure; }
- public set StartMeasure(value: GraphicalMeasure) { this.startMeasure = value; }
- /** The graphical measure where the parent continuous dynamic expression ends */
- public get EndMeasure(): GraphicalMeasure { return this.endMeasure; }
- public set EndMeasure(value: GraphicalMeasure) { this.endMeasure = value; }
- /** The staff lin where the graphical dynamic expressions ends */
- public get EndStaffLine(): StaffLine { return this.endMeasure ? this.endMeasure.ParentStaffLine : undefined; }
- /** Is true if this continuous expression is a wedge, that reaches over a system border and needs to be split into two. */
- public get IsSplittedPart(): boolean { return this.isSplittedPart; }
- public set IsSplittedPart(value: boolean) { this.isSplittedPart = value; }
- /** Is true if the dynamic is not a symbol but a text instruction. E.g. "decrescendo" */
- public get IsVerbal(): boolean { return this.ContinuousDynamic.Label !== undefined && this.ContinuousDynamic.Label.length > 0; }
- /** True if this expression should not be removed if re-rendered */
- public get NotToBeRemoved(): boolean { return this.notToBeRemoved; }
- public set NotToBeRemoved(value: boolean) { this.notToBeRemoved = value; }
- /** Holds the line objects that can be drawn via implementation */
- public get Lines(): GraphicalLine[] { return this.lines; }
- public get ContinuousDynamic(): ContinuousDynamicExpression { return this.SourceExpression as ContinuousDynamicExpression; }
- //#endregion
- //#region Public methods
- public updateSkyBottomLine(): void {
- // update Sky-BottomLine
- const skyBottomLineCalculator: SkyBottomLineCalculator = this.parentStaffLine.SkyBottomLineCalculator;
- const left: number = this.IsVerbal ? this.label.PositionAndShape.RelativePosition.x + this.label.PositionAndShape.BorderMarginLeft : 0;
- const right: number = this.IsVerbal ? this.label.PositionAndShape.RelativePosition.x + this.label.PositionAndShape.BorderMarginRight : 0;
- if (!this.IsVerbal && this.lines.length < 2) {
- log.warn("Not enough lines for SkyBottomLine calculation");
- }
- switch (this.Placement) {
- case PlacementEnum.Above:
- if (!this.IsVerbal) {
- skyBottomLineCalculator.updateSkyLineWithWedge(this.lines[0].Start, this.lines[0].End);
- } else {
- const yValue: number = this.label.PositionAndShape.BorderMarginTop + this.label.PositionAndShape.RelativePosition.y;
- skyBottomLineCalculator.updateSkyLineInRange(left, right, yValue);
- }
- break;
- case PlacementEnum.Below:
- if (!this.IsVerbal) {
- skyBottomLineCalculator.updateBottomLineWithWedge(this.lines[1].Start, this.lines[1].End);
- } else {
- const yValue: number = this.label.PositionAndShape.BorderMarginBottom + this.label.PositionAndShape.RelativePosition.y;
- skyBottomLineCalculator.updateBottomLineInRange(left, right, yValue);
- }
- break;
- default:
- log.error("Placement for GraphicalContinuousDynamicExpression is unknown");
- }
- }
- /**
- * Calculate crescendo lines for (full).
- * @param startX left most starting point
- * @param endX right mist ending point
- * @param y y placement
- * @param wedgeOpeningLength length of the opening
- * @param wedgeLineWidth line width of the wedge
- */
- public createCrescendoLines(startX: number, endX: number, y: number,
- wedgeOpeningLength: number = this.rules.WedgeOpeningLength, wedgeLineWidth: number = this.rules.WedgeLineWidth): void {
- const lineStart: PointF2D = new PointF2D(startX, y);
- const upperLineEnd: PointF2D = new PointF2D(endX, y - wedgeOpeningLength / 2);
- const lowerLineEnd: PointF2D = new PointF2D(endX, y + wedgeOpeningLength / 2);
- this.addWedgeLines(lineStart, upperLineEnd, lowerLineEnd, wedgeLineWidth);
- }
- /**
- * Calculate crescendo lines for system break (first part).
- * @param startX left most starting point
- * @param endX right mist ending point
- * @param y y placement
- * @param wedgeMeasureEndOpeningLength length of opening at measure end
- * @param wedgeOpeningLength length of the opening
- * @param wedgeLineWidth line width of the wedge
- */
- public createFirstHalfCrescendoLines(startX: number, endX: number, y: number,
- wedgeMeasureEndOpeningLength: number = this.rules.WedgeMeasureEndOpeningLength,
- wedgeLineWidth: number = this.rules.WedgeLineWidth): void {
- const lineStart: PointF2D = new PointF2D(startX, y);
- const upperLineEnd: PointF2D = new PointF2D(endX, y - wedgeMeasureEndOpeningLength / 2);
- const lowerLineEnd: PointF2D = new PointF2D(endX, y + wedgeMeasureEndOpeningLength / 2);
- this.addWedgeLines(lineStart, upperLineEnd, lowerLineEnd, wedgeLineWidth);
- }
- /**
- * Calculate crescendo lines for system break (second part).
- * @param startX left most starting point
- * @param endX right mist ending point
- * @param y y placement
- * @param wedgeMeasureBeginOpeningLength length of opening at measure start
- * @param wedgeOpeningLength length of the opening
- * @param wedgeLineWidth line width of the wedge
- */
- public createSecondHalfCrescendoLines(startX: number, endX: number, y: number,
- wedgeMeasureBeginOpeningLength: number = this.rules.WedgeMeasureBeginOpeningLength,
- wedgeOpeningLength: number = this.rules.WedgeOpeningLength,
- wedgeLineWidth: number = this.rules.WedgeLineWidth): void {
- const upperLineStart: PointF2D = new PointF2D(startX, y - wedgeMeasureBeginOpeningLength / 2);
- const lowerLineStart: PointF2D = new PointF2D(startX, y + wedgeMeasureBeginOpeningLength / 2);
- const upperLineEnd: PointF2D = new PointF2D(endX, y - wedgeOpeningLength / 2);
- const lowerLineEnd: PointF2D = new PointF2D(endX, y + wedgeOpeningLength / 2);
- this.addDoubleLines(upperLineStart, upperLineEnd, lowerLineStart, lowerLineEnd, wedgeLineWidth);
- }
- /**
- * This method recalculates the Crescendo Lines (for all cases).
- * @param startX left most starting point
- * @param endX right most ending point
- * @param y y placement
- */
- public recalculateCrescendoLines(startX: number, endX: number, y: number): void {
- const isSecondHalfSplit: boolean = Math.abs(this.lines[0].Start.y - this.lines[1].Start.y) > 0.0001;
- this.lines.clear();
- if (isSecondHalfSplit) {
- this.createSecondHalfCrescendoLines(startX, endX, y);
- } else if (this.isSplittedPart) {
- this.createFirstHalfCrescendoLines(startX, endX, y);
- } else {
- this.createCrescendoLines(startX, endX, y);
- }
- }
- /**
- * Calculate diminuendo lines for system break (full).
- * @param startX left most starting point
- * @param endX right mist ending point
- * @param y y placement
- * @param wedgeOpeningLength length of the opening
- * @param wedgeLineWidth line width of the wedge
- */
- public createDiminuendoLines(startX: number, endX: number, y: number,
- wedgeOpeningLength: number = this.rules.WedgeOpeningLength, wedgeLineWidth: number = this.rules.WedgeLineWidth): void {
- const upperWedgeStart: PointF2D = new PointF2D(startX, y - wedgeOpeningLength / 2);
- const lowerWedgeStart: PointF2D = new PointF2D(startX, y + wedgeOpeningLength / 2);
- const wedgeEnd: PointF2D = new PointF2D(endX, y);
- this.addWedgeLines(wedgeEnd, upperWedgeStart, lowerWedgeStart, wedgeLineWidth);
- }
- /**
- * Calculate diminuendo lines for system break (first part).
- * @param startX left most starting point
- * @param endX right mist ending point
- * @param y y placement
- * @param wedgeOpeningLength length of the opening
- * @param wedgeMeasureEndOpeningLength length of opening at measure end
- * @param wedgeLineWidth line width of the wedge
- */
- public createFirstHalfDiminuendoLines(startX: number, endX: number, y: number,
- wedgeOpeningLength: number = this.rules.WedgeOpeningLength,
- wedgeMeasureEndOpeningLength: number = this.rules.WedgeMeasureEndOpeningLength,
- wedgeLineWidth: number = this.rules.WedgeLineWidth): void {
- const upperLineStart: PointF2D = new PointF2D(startX, y - wedgeOpeningLength / 2);
- const lowerLineStart: PointF2D = new PointF2D(startX, y + wedgeOpeningLength / 2);
- const upperLineEnd: PointF2D = new PointF2D(endX, y - wedgeMeasureEndOpeningLength / 2);
- const lowerLineEnd: PointF2D = new PointF2D(endX, y + wedgeMeasureEndOpeningLength / 2);
- this.addDoubleLines(upperLineStart, upperLineEnd, lowerLineStart, lowerLineEnd, wedgeLineWidth);
- }
- /**
- * Calculate diminuendo lines for system break (second part).
- * @param startX left most starting point
- * @param endX right mist ending point
- * @param y y placement
- * @param wedgeMeasureBeginOpeningLength length of opening at measure start
- * @param wedgeLineWidth line width of the wedge
- */
- public createSecondHalfDiminuendoLines(startX: number, endX: number, y: number,
- wedgeMeasureBeginOpeningLength: number = this.rules.WedgeMeasureBeginOpeningLength,
- wedgeLineWidth: number = this.rules.WedgeLineWidth): void {
- const upperLineStart: PointF2D = new PointF2D(startX, y - wedgeMeasureBeginOpeningLength / 2);
- const lowerLineStart: PointF2D = new PointF2D(startX, y + wedgeMeasureBeginOpeningLength / 2);
- const lineEnd: PointF2D = new PointF2D(endX, y);
- this.addWedgeLines(lineEnd, upperLineStart, lowerLineStart, wedgeLineWidth);
- }
- /**
- * This method recalculates the diminuendo lines (for all cases).
- * @param startX left most starting point
- * @param endX right most ending point
- * @param y y placement
- */
- public recalculateDiminuendoLines(startX: number, endX: number, yPosition: number): void {
- const isFirstHalfSplit: boolean = Math.abs(this.lines[0].End.y - this.lines[1].End.y) > 0.0001;
- this.lines.clear();
- if (isFirstHalfSplit) {
- this.createFirstHalfDiminuendoLines(startX, endX, yPosition);
- } else if (this.isSplittedPart) {
- this.createSecondHalfDiminuendoLines(startX, endX, yPosition);
- } else {
- this.createDiminuendoLines(startX, endX, yPosition);
- }
- }
- /**
- * Calculate the BoundingBox (as a box around the Wedge).
- */
- public calcPsi(): void {
- if (this.IsVerbal) {
- this.PositionAndShape.calculateBoundingBox();
- return;
- }
- this.PositionAndShape.RelativePosition = this.lines[0].Start;
- this.PositionAndShape.BorderMarginTop = this.lines[0].End.y - this.lines[0].Start.y;
- this.PositionAndShape.BorderMarginBottom = this.lines[1].End.y - this.lines[1].Start.y;
- if (this.ContinuousDynamic.DynamicType === ContDynamicEnum.crescendo) {
- this.PositionAndShape.BorderMarginLeft = 0;
- this.PositionAndShape.BorderMarginRight = this.lines[0].End.x - this.lines[0].Start.x;
- } else {
- this.PositionAndShape.BorderMarginLeft = this.lines[0].End.x - this.lines[0].Start.x;
- this.PositionAndShape.BorderMarginRight = 0;
- }
- }
- /**
- * Clear Lines
- */
- public cleanUp(): void {
- this.lines.clear();
- }
- /**
- * Shift wedge in y position
- * @param shift Number to shift
- */
- public shiftYPosition(shift: number): void {
- if (this.IsVerbal) {
- this.PositionAndShape.RelativePosition.y += shift;
- this.PositionAndShape.calculateBoundingBox();
- } else {
- this.lines[0].Start.y += shift;
- this.lines[0].End.y += shift;
- this.lines[1].End.y += shift;
- }
- }
- public squeeze(value: number): void {
- // Verbal expressions are not squeezable and squeezing below the width is also not possible
- if (this.IsVerbal) {
- return;
- }
- const width: number = Math.abs(this.lines[0].End.x - this.lines[0].Start.x);
- if (width < Math.abs(value)) {
- return;
- }
- if (this.ContinuousDynamic.DynamicType === ContDynamicEnum.crescendo) {
- if (value > 0) {
- this.lines[0].Start.x += value;
- } else {
- this.lines[0].End.x += value;
- this.lines[1].End.x += value;
- }
- } else {
- if (value < 0) {
- this.lines[0].Start.x += value;
- } else {
- this.lines[0].End.x += value;
- this.lines[1].End.x += value;
- }
- }
- this.calcPsi();
- }
- //#endregion
- //#region Private methods
- /**
- * Create lines from points and add them to the memory
- * @param wedgePoint start of the expression
- * @param upperWedgeEnd end of the upper line
- * @param lowerWedgeEnd end of lower line
- * @param wedgeLineWidth line width
- */
- private addWedgeLines(wedgePoint: PointF2D, upperWedgeEnd: PointF2D, lowerWedgeEnd: PointF2D, wedgeLineWidth: number): void {
- const upperLine: GraphicalLine = new GraphicalLine(wedgePoint, upperWedgeEnd, wedgeLineWidth);
- const lowerLine: GraphicalLine = new GraphicalLine(wedgePoint, lowerWedgeEnd, wedgeLineWidth);
- this.lines.push(upperLine);
- this.lines.push(lowerLine);
- }
- /**
- * Create top and bottom lines for continuing wedges
- * @param upperLineStart start of the upper line
- * @param upperLineEnd end of the upper line
- * @param lowerLineStart start of the lower line
- * @param lowerLineEnd end of lower line
- * @param wedgeLineWidth line width
- */
- private addDoubleLines(upperLineStart: PointF2D, upperLineEnd: PointF2D, lowerLineStart: PointF2D, lowerLineEnd: PointF2D, wedgeLineWidth: number): void {
- const upperLine: GraphicalLine = new GraphicalLine(upperLineStart, upperLineEnd, wedgeLineWidth);
- const lowerLine: GraphicalLine = new GraphicalLine(lowerLineStart, lowerLineEnd, wedgeLineWidth);
- this.lines.push(upperLine);
- this.lines.push(lowerLine);
- }
- //#endregion
- }
|