|
@@ -94,14 +94,11 @@ export class GraphicalSlur extends GraphicalCurve {
|
|
let endY: number = startEndPoints.endY;
|
|
let endY: number = startEndPoints.endY;
|
|
const minAngle: number = rules.SlurTangentMinAngle;
|
|
const minAngle: number = rules.SlurTangentMinAngle;
|
|
const maxAngle: number = rules.SlurTangentMaxAngle;
|
|
const maxAngle: number = rules.SlurTangentMaxAngle;
|
|
- let start: PointF2D, end: PointF2D;
|
|
|
|
let points: PointF2D[];
|
|
let points: PointF2D[];
|
|
|
|
|
|
if (this.placement === PlacementEnum.Above) {
|
|
if (this.placement === PlacementEnum.Above) {
|
|
startY -= rules.SlurNoteHeadYOffset;
|
|
startY -= rules.SlurNoteHeadYOffset;
|
|
endY -= rules.SlurNoteHeadYOffset;
|
|
endY -= rules.SlurNoteHeadYOffset;
|
|
- start = new PointF2D(startX, startY);
|
|
|
|
- end = new PointF2D(endX, endY);
|
|
|
|
const startUpperRight: PointF2D = new PointF2D(this.staffEntries[0].parentMeasure.PositionAndShape.RelativePosition.x
|
|
const startUpperRight: PointF2D = new PointF2D(this.staffEntries[0].parentMeasure.PositionAndShape.RelativePosition.x
|
|
+ this.staffEntries[0].PositionAndShape.RelativePosition.x,
|
|
+ this.staffEntries[0].PositionAndShape.RelativePosition.x,
|
|
startY);
|
|
startY);
|
|
@@ -161,51 +158,80 @@ export class GraphicalSlur extends GraphicalCurve {
|
|
|
|
|
|
// calculate tangent Lines maximum Slopes between StartPoint and EndPoint to all Points in SkyLine
|
|
// calculate tangent Lines maximum Slopes between StartPoint and EndPoint to all Points in SkyLine
|
|
// and tangent Lines characteristica
|
|
// and tangent Lines characteristica
|
|
- const leftLineSlope: number = this.calculateMaxLeftSlope(transformedPoints, start2, end2);
|
|
|
|
- const rightLineSlope: number = this.calculateMaxRightSlope(transformedPoints, start2, end2);
|
|
|
|
- const leftLineD: number = start2.y - start2.x * leftLineSlope;
|
|
|
|
- const rightLineD: number = end2.y - end2.x * rightLineSlope;
|
|
|
|
|
|
+ const startLineSlope: number = this.calculateMaxLeftSlope(transformedPoints, start2, end2);
|
|
|
|
+ const endLineSlope: number = this.calculateMaxRightSlope(transformedPoints, start2, end2);
|
|
|
|
+ const startLineD: number = start2.y - start2.x * startLineSlope;
|
|
|
|
+ const endLineD: number = end2.y - end2.x * endLineSlope;
|
|
|
|
|
|
// calculate IntersectionPoint of the 2 Lines
|
|
// calculate IntersectionPoint of the 2 Lines
|
|
// if same Slope, then Point.X between Start and End and Point.Y fixed
|
|
// if same Slope, then Point.X between Start and End and Point.Y fixed
|
|
const intersectionPoint: PointF2D = new PointF2D();
|
|
const intersectionPoint: PointF2D = new PointF2D();
|
|
let sameSlope: boolean = false;
|
|
let sameSlope: boolean = false;
|
|
- if (Math.abs(Math.abs(leftLineSlope) - Math.abs(rightLineSlope)) < 0.0001) {
|
|
|
|
|
|
+ if (Math.abs(Math.abs(startLineSlope) - Math.abs(endLineSlope)) < 0.0001) {
|
|
intersectionPoint.x = end2.x / 2;
|
|
intersectionPoint.x = end2.x / 2;
|
|
intersectionPoint.y = 0;
|
|
intersectionPoint.y = 0;
|
|
sameSlope = true;
|
|
sameSlope = true;
|
|
} else {
|
|
} else {
|
|
- intersectionPoint.x = (rightLineD - leftLineD) / (leftLineSlope - rightLineSlope);
|
|
|
|
- intersectionPoint.y = leftLineSlope * intersectionPoint.x + leftLineD;
|
|
|
|
|
|
+ intersectionPoint.x = (endLineD - startLineD) / (startLineSlope - endLineSlope);
|
|
|
|
+ intersectionPoint.y = startLineSlope * intersectionPoint.x + startLineD;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ // calculate HeightWidthRatio between the MaxYpoint (from the points between StartPoint and EndPoint)
|
|
|
|
+ // and the X-distance from StartPoint to EndPoint
|
|
|
|
+ const heightWidthRatio: number = this.calculateHeightWidthRatio(end2.x, transformedPoints);
|
|
|
|
+
|
|
|
|
+ // Shift start- or endPoint and corresponding controlPoint away from note, if needed:
|
|
|
|
+ // e.g. if there is a close object creating a high slope, better shift it away to reduce the slope:
|
|
|
|
+ // idea is to compare the half heightWidthRatio of the bounding box of the skyline points with the slope (which is also a ratio: k/1)
|
|
|
|
+ // if the slope is greater than the half heightWidthRatio (which will 99% be the case),
|
|
|
|
+ // then add a y-offset to reduce the slope to the same value as the half heightWidthRatio of the bounding box
|
|
|
|
+ const startYOffset: number = 0;
|
|
|
|
+ const endYOffset: number = 0;
|
|
|
|
+ /*if (Math.abs(heightWidthRatio) > 0.001) {
|
|
|
|
+ // 1. start side:
|
|
|
|
+ const startSlopeRatio: number = Math.abs(startLineSlope / (heightWidthRatio * 2));
|
|
|
|
+ const maxLeftYOffset: number = Math.abs(startLineSlope);
|
|
|
|
+ startYOffset = Math.max(0, maxLeftYOffset * (Math.min(10, startSlopeRatio - 1) / 10));
|
|
|
|
+ // slope has to be adapted now due to the y-offset:
|
|
|
|
+ startLineSlope -= startYOffset;
|
|
|
|
+
|
|
|
|
+ // 2. end side:
|
|
|
|
+ const endSlopeRatio: number = Math.abs(endLineSlope / (heightWidthRatio * 2));
|
|
|
|
+ const maxRightYOffset: number = Math.abs(endLineSlope);
|
|
|
|
+ endYOffset = Math.max(0, maxRightYOffset * (Math.min(10, endSlopeRatio - 1) / 10));
|
|
|
|
+ // slope has to be adapted now due to the y-offset:
|
|
|
|
+ endLineSlope += endYOffset;
|
|
|
|
+ }*/
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
// calculate tangent Lines Angles
|
|
// calculate tangent Lines Angles
|
|
// (using the calculated Slopes and the Ratio from the IntersectionPoint's distance to the MaxPoint in the SkyLine)
|
|
// (using the calculated Slopes and the Ratio from the IntersectionPoint's distance to the MaxPoint in the SkyLine)
|
|
- let leftAngle: number = minAngle;
|
|
|
|
- let rightAngle: number = -minAngle;
|
|
|
|
- // if the calculated Slopes (left and right) are equal, then Angles have fixed values
|
|
|
|
|
|
+ let startAngle: number = minAngle;
|
|
|
|
+ let endAngle: number = -minAngle;
|
|
|
|
+ // if the calculated Slopes (start and end) are equal, then Angles have fixed values
|
|
if (!sameSlope) {
|
|
if (!sameSlope) {
|
|
- const result: {leftAngle: number, rightAngle: number} =
|
|
|
|
- this.calculateAngles(minAngle, leftLineSlope, rightLineSlope, maxAngle);
|
|
|
|
- leftAngle = result.leftAngle;
|
|
|
|
- rightAngle = result.rightAngle;
|
|
|
|
|
|
+ const result: {startAngle: number, endAngle: number} =
|
|
|
|
+ this.calculateAngles(minAngle, startLineSlope, endLineSlope, maxAngle);
|
|
|
|
+ startAngle = result.startAngle;
|
|
|
|
+ endAngle = result.endAngle;
|
|
}
|
|
}
|
|
|
|
|
|
// calculate Curve's Control Points
|
|
// calculate Curve's Control Points
|
|
- const controlPoints: {leftControlPoint: PointF2D, rightControlPoint: PointF2D} =
|
|
|
|
- this.calculateControlPoints(end2.x, leftAngle, rightAngle, transformedPoints);
|
|
|
|
|
|
+ const controlPoints: {startControlPoint: PointF2D, endControlPoint: PointF2D} =
|
|
|
|
+ this.calculateControlPoints(end2.x, startAngle, endAngle, transformedPoints, heightWidthRatio);
|
|
|
|
|
|
- let leftControlPoint: PointF2D = controlPoints.leftControlPoint;
|
|
|
|
- let rightControlPoint: PointF2D = controlPoints.rightControlPoint;
|
|
|
|
|
|
+ let startControlPoint: PointF2D = controlPoints.startControlPoint;
|
|
|
|
+ let endControlPoint: PointF2D = controlPoints.endControlPoint;
|
|
|
|
|
|
// transform ControlPoints to original Coordinate System
|
|
// transform ControlPoints to original Coordinate System
|
|
// (rotate back and translate back)
|
|
// (rotate back and translate back)
|
|
- leftControlPoint = transposeMatrix.vectorMultiplication(leftControlPoint);
|
|
|
|
- leftControlPoint.x += startX;
|
|
|
|
- leftControlPoint.y = -leftControlPoint.y + startY;
|
|
|
|
- rightControlPoint = transposeMatrix.vectorMultiplication(rightControlPoint);
|
|
|
|
- rightControlPoint.x += startX;
|
|
|
|
- rightControlPoint.y = -rightControlPoint.y + startY;
|
|
|
|
|
|
+ startControlPoint = transposeMatrix.vectorMultiplication(startControlPoint);
|
|
|
|
+ startControlPoint.x += startX;
|
|
|
|
+ startControlPoint.y = -startControlPoint.y + startY;
|
|
|
|
+ endControlPoint = transposeMatrix.vectorMultiplication(endControlPoint);
|
|
|
|
+ endControlPoint.x += startX;
|
|
|
|
+ endControlPoint.y = -endControlPoint.y + startY;
|
|
|
|
|
|
/* for DEBUG only */
|
|
/* for DEBUG only */
|
|
// this.intersection = transposeMatrix.vectorMultiplication(intersectionPoint);
|
|
// this.intersection = transposeMatrix.vectorMultiplication(intersectionPoint);
|
|
@@ -214,12 +240,12 @@ export class GraphicalSlur extends GraphicalCurve {
|
|
/* for DEBUG only */
|
|
/* for DEBUG only */
|
|
|
|
|
|
// set private members
|
|
// set private members
|
|
- this.bezierStartPt = start;
|
|
|
|
- this.bezierStartControlPt = leftControlPoint;
|
|
|
|
- this.bezierEndControlPt = rightControlPoint;
|
|
|
|
- this.bezierEndPt = end;
|
|
|
|
|
|
+ this.bezierStartPt = new PointF2D(startX, startY - startYOffset);
|
|
|
|
+ this.bezierStartControlPt = new PointF2D(startControlPoint.x, startControlPoint.y - startYOffset);
|
|
|
|
+ this.bezierEndControlPt = new PointF2D(endControlPoint.x, endControlPoint.y - endYOffset);
|
|
|
|
+ this.bezierEndPt = new PointF2D(endX, endY - endYOffset);
|
|
|
|
|
|
- // calculate CurvePoints
|
|
|
|
|
|
+ // calculate slur Curvepoints and update Skyline
|
|
const length: number = staffLine.SkyLine.length;
|
|
const length: number = staffLine.SkyLine.length;
|
|
const startIndex: number = skyBottomLineCalculator.getLeftIndexForPointX(this.bezierStartPt.x, length);
|
|
const startIndex: number = skyBottomLineCalculator.getLeftIndexForPointX(this.bezierStartPt.x, length);
|
|
const endIndex: number = skyBottomLineCalculator.getLeftIndexForPointX(this.bezierEndPt.x, length);
|
|
const endIndex: number = skyBottomLineCalculator.getLeftIndexForPointX(this.bezierEndPt.x, length);
|
|
@@ -244,8 +270,6 @@ export class GraphicalSlur extends GraphicalCurve {
|
|
} else {
|
|
} else {
|
|
startY += rules.SlurNoteHeadYOffset;
|
|
startY += rules.SlurNoteHeadYOffset;
|
|
endY += rules.SlurNoteHeadYOffset;
|
|
endY += rules.SlurNoteHeadYOffset;
|
|
- start = new PointF2D(startX, startY);
|
|
|
|
- end = new PointF2D(endX, endY);
|
|
|
|
|
|
|
|
// firstStaffEntry startLowerRightPoint and lastStaffentry endLowerLeftPoint
|
|
// firstStaffEntry startLowerRightPoint and lastStaffentry endLowerLeftPoint
|
|
const startLowerRight: PointF2D = new PointF2D(this.staffEntries[0].parentMeasure.PositionAndShape.RelativePosition.x
|
|
const startLowerRight: PointF2D = new PointF2D(this.staffEntries[0].parentMeasure.PositionAndShape.RelativePosition.x
|
|
@@ -305,56 +329,82 @@ export class GraphicalSlur extends GraphicalCurve {
|
|
|
|
|
|
// calculate tangent Lines maximum Slopes between StartPoint and EndPoint to all Points in BottomLine
|
|
// calculate tangent Lines maximum Slopes between StartPoint and EndPoint to all Points in BottomLine
|
|
// and tangent Lines characteristica
|
|
// and tangent Lines characteristica
|
|
- const leftLineSlope: number = this.calculateMaxLeftSlope(transformedPoints, start2, end2);
|
|
|
|
- const rightLineSlope: number = this.calculateMaxRightSlope(transformedPoints, start2, end2);
|
|
|
|
- const leftLineD: number = start2.y - start2.x * leftLineSlope;
|
|
|
|
- const rightLineD: number = end2.y - end2.x * rightLineSlope;
|
|
|
|
|
|
+ const startLineSlope: number = this.calculateMaxLeftSlope(transformedPoints, start2, end2);
|
|
|
|
+ const endLineSlope: number = this.calculateMaxRightSlope(transformedPoints, start2, end2);
|
|
|
|
+ const startLineD: number = start2.y - start2.x * startLineSlope;
|
|
|
|
+ const endLineD: number = end2.y - end2.x * endLineSlope;
|
|
|
|
|
|
// calculate IntersectionPoint of the 2 Lines
|
|
// calculate IntersectionPoint of the 2 Lines
|
|
// if same Slope, then Point.X between Start and End and Point.Y fixed
|
|
// if same Slope, then Point.X between Start and End and Point.Y fixed
|
|
const intersectionPoint: PointF2D = new PointF2D();
|
|
const intersectionPoint: PointF2D = new PointF2D();
|
|
let sameSlope: boolean = false;
|
|
let sameSlope: boolean = false;
|
|
- if (Math.abs(Math.abs(leftLineSlope) - Math.abs(rightLineSlope)) < 0.0001) {
|
|
|
|
|
|
+ if (Math.abs(Math.abs(startLineSlope) - Math.abs(endLineSlope)) < 0.0001) {
|
|
intersectionPoint.x = end2.x / 2;
|
|
intersectionPoint.x = end2.x / 2;
|
|
intersectionPoint.y = 0;
|
|
intersectionPoint.y = 0;
|
|
sameSlope = true;
|
|
sameSlope = true;
|
|
} else {
|
|
} else {
|
|
- intersectionPoint.x = (rightLineD - leftLineD) / (leftLineSlope - rightLineSlope);
|
|
|
|
- intersectionPoint.y = leftLineSlope * intersectionPoint.x + leftLineD;
|
|
|
|
|
|
+ intersectionPoint.x = (endLineD - startLineD) / (startLineSlope - endLineSlope);
|
|
|
|
+ intersectionPoint.y = startLineSlope * intersectionPoint.x + startLineD;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ // calculate HeightWidthRatio between the MaxYpoint (from the points between StartPoint and EndPoint)
|
|
|
|
+ // and the X-distance from StartPoint to EndPoint
|
|
|
|
+ const heightWidthRatio: number = this.calculateHeightWidthRatio(end2.x, transformedPoints);
|
|
|
|
+
|
|
|
|
+ // Shift start- or endPoint and corresponding controlPoint away from note, if needed:
|
|
|
|
+ // e.g. if there is a close object creating a high slope, better shift it away to reduce the slope:
|
|
|
|
+ // idea is to compare the half heightWidthRatio of the bounding box of the skyline points with the slope (which is also a ratio: k/1)
|
|
|
|
+ // if the slope is greater than the half heightWidthRatio (which will 99% be the case),
|
|
|
|
+ // then add a y-offset to reduce the slope to the same value as the half heightWidthRatio of the bounding box
|
|
|
|
+ const startYOffset: number = 0;
|
|
|
|
+ const endYOffset: number = 0;
|
|
|
|
+ /*if (Math.abs(heightWidthRatio) > 0.001) {
|
|
|
|
+ // 1. start side:
|
|
|
|
+ const startSlopeRatio: number = Math.abs(startLineSlope / (heightWidthRatio * 2));
|
|
|
|
+ const maxLeftYOffset: number = Math.abs(startLineSlope);
|
|
|
|
+ startYOffset = Math.max(0, maxLeftYOffset * (Math.min(10, startSlopeRatio - 1) / 10));
|
|
|
|
+ // slope has to be adapted now due to the y-offset:
|
|
|
|
+ startLineSlope -= startYOffset;
|
|
|
|
+ // 2. end side:
|
|
|
|
+ const endSlopeRatio: number = Math.abs(endLineSlope / (heightWidthRatio * 2));
|
|
|
|
+ const maxRightYOffset: number = Math.abs(endLineSlope);
|
|
|
|
+ endYOffset = Math.max(0, maxRightYOffset * (Math.min(10, endSlopeRatio - 1) / 10));
|
|
|
|
+ // slope has to be adapted now due to the y-offset:
|
|
|
|
+ endLineSlope += endYOffset;
|
|
|
|
+ } */
|
|
|
|
+
|
|
// calculate tangent Lines Angles
|
|
// calculate tangent Lines Angles
|
|
// (using the calculated Slopes and the Ratio from the IntersectionPoint's distance to the MaxPoint in the SkyLine)
|
|
// (using the calculated Slopes and the Ratio from the IntersectionPoint's distance to the MaxPoint in the SkyLine)
|
|
- let leftAngle: number = minAngle;
|
|
|
|
- let rightAngle: number = -minAngle;
|
|
|
|
- // if the calculated Slopes (left and right) are equal, then Angles have fixed values
|
|
|
|
|
|
+ let startAngle: number = minAngle;
|
|
|
|
+ let endAngle: number = -minAngle;
|
|
|
|
+ // if the calculated Slopes (start and end) are equal, then Angles have fixed values
|
|
if (!sameSlope) {
|
|
if (!sameSlope) {
|
|
- const result: {leftAngle: number, rightAngle: number} =
|
|
|
|
- this.calculateAngles(minAngle, leftLineSlope, rightLineSlope, maxAngle);
|
|
|
|
- leftAngle = result.leftAngle;
|
|
|
|
- rightAngle = result.rightAngle;
|
|
|
|
|
|
+ const result: {startAngle: number, endAngle: number} =
|
|
|
|
+ this.calculateAngles(minAngle, startLineSlope, endLineSlope, maxAngle);
|
|
|
|
+ startAngle = result.startAngle;
|
|
|
|
+ endAngle = result.endAngle;
|
|
}
|
|
}
|
|
|
|
|
|
// calculate Curve's Control Points
|
|
// calculate Curve's Control Points
|
|
- const controlPoints: {leftControlPoint: PointF2D, rightControlPoint: PointF2D} =
|
|
|
|
- this.calculateControlPoints(end2.x, leftAngle, rightAngle, transformedPoints);
|
|
|
|
- let leftControlPoint: PointF2D = controlPoints.leftControlPoint;
|
|
|
|
- let rightControlPoint: PointF2D = controlPoints.rightControlPoint;
|
|
|
|
|
|
+ const controlPoints: {startControlPoint: PointF2D, endControlPoint: PointF2D} =
|
|
|
|
+ this.calculateControlPoints(end2.x, startAngle, endAngle, transformedPoints, heightWidthRatio);
|
|
|
|
+ let startControlPoint: PointF2D = controlPoints.startControlPoint;
|
|
|
|
+ let endControlPoint: PointF2D = controlPoints.endControlPoint;
|
|
|
|
|
|
// transform ControlPoints to original Coordinate System
|
|
// transform ControlPoints to original Coordinate System
|
|
// (rotate back and translate back)
|
|
// (rotate back and translate back)
|
|
- leftControlPoint = transposeMatrix.vectorMultiplication(leftControlPoint);
|
|
|
|
- leftControlPoint.x += startX;
|
|
|
|
- leftControlPoint.y += startY;
|
|
|
|
- rightControlPoint = transposeMatrix.vectorMultiplication(rightControlPoint);
|
|
|
|
- rightControlPoint.x += startX;
|
|
|
|
- rightControlPoint.y += startY;
|
|
|
|
|
|
+ startControlPoint = transposeMatrix.vectorMultiplication(startControlPoint);
|
|
|
|
+ startControlPoint.x += startX;
|
|
|
|
+ startControlPoint.y += startY;
|
|
|
|
+ endControlPoint = transposeMatrix.vectorMultiplication(endControlPoint);
|
|
|
|
+ endControlPoint.x += startX;
|
|
|
|
+ endControlPoint.y += startY;
|
|
|
|
|
|
// set private members
|
|
// set private members
|
|
- this.bezierStartPt = start;
|
|
|
|
- this.bezierStartControlPt = leftControlPoint;
|
|
|
|
- this.bezierEndControlPt = rightControlPoint;
|
|
|
|
- this.bezierEndPt = end;
|
|
|
|
|
|
+ this.bezierStartPt = new PointF2D(startX, startY + startYOffset);
|
|
|
|
+ this.bezierStartControlPt = new PointF2D(startControlPoint.x, startControlPoint.y + startYOffset);
|
|
|
|
+ this.bezierEndControlPt = new PointF2D(endControlPoint.x, endControlPoint.y + endYOffset);
|
|
|
|
+ this.bezierEndPt = new PointF2D(endX, endY + endYOffset);
|
|
|
|
|
|
/* for DEBUG only */
|
|
/* for DEBUG only */
|
|
// this.intersection = transposeMatrix.vectorMultiplication(intersectionPoint);
|
|
// this.intersection = transposeMatrix.vectorMultiplication(intersectionPoint);
|
|
@@ -373,7 +423,7 @@ export class GraphicalSlur extends GraphicalCurve {
|
|
const diff: number = i / samplingUnit - this.bezierStartPt.x;
|
|
const diff: number = i / samplingUnit - this.bezierStartPt.x;
|
|
const curvePoint: PointF2D = this.calculateCurvePointAtIndex(Math.abs(diff) / distance);
|
|
const curvePoint: PointF2D = this.calculateCurvePointAtIndex(Math.abs(diff) / distance);
|
|
|
|
|
|
- // update left- and rightIndex for better accuracy
|
|
|
|
|
|
+ // update start- and endIndex for better accuracy
|
|
let index: number = skyBottomLineCalculator.getLeftIndexForPointX(curvePoint.x, length);
|
|
let index: number = skyBottomLineCalculator.getLeftIndexForPointX(curvePoint.x, length);
|
|
// update BottomLine with final slur curve:
|
|
// update BottomLine with final slur curve:
|
|
if (index >= startIndex) {
|
|
if (index >= startIndex) {
|
|
@@ -532,6 +582,14 @@ export class GraphicalSlur extends GraphicalCurve {
|
|
} else { endY += rules.SlursStartingAtSameStaffEntryYOffset; }
|
|
} else { endY += rules.SlursStartingAtSameStaffEntryYOffset; }
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ if (this.placement === PlacementEnum.Above) {
|
|
|
|
+ startY = Math.min(startY, 1.5);
|
|
|
|
+ endY = Math.min(endY, 1.5);
|
|
|
|
+ } else {
|
|
|
|
+ startY = Math.max(startY, staffLine.StaffHeight - 1.5);
|
|
|
|
+ endY = Math.max(endY, staffLine.StaffHeight - 1.5);
|
|
|
|
+ }
|
|
|
|
+
|
|
return {startX, startY, endX, endY};
|
|
return {startX, startY, endX, endY};
|
|
}
|
|
}
|
|
|
|
|
|
@@ -623,8 +681,12 @@ export class GraphicalSlur extends GraphicalCurve {
|
|
}
|
|
}
|
|
|
|
|
|
for (let i: number = startIndex; i < endIndex; i++) {
|
|
for (let i: number = startIndex; i < endIndex; i++) {
|
|
- const point: PointF2D = new PointF2D((0.5 + i) / skyBottomLineCalculator.SamplingUnit, staffLine.SkyLine[i]);
|
|
|
|
- points.push(point);
|
|
|
|
|
|
+ const skylineValue: number = staffLine.SkyLine[i];
|
|
|
|
+ // ignore default value (= 0) which is upper border of staffline
|
|
|
|
+ if (skylineValue !== 0) {
|
|
|
|
+ const point: PointF2D = new PointF2D((0.5 + i) / skyBottomLineCalculator.SamplingUnit, skylineValue);
|
|
|
|
+ points.push(point);
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
return points;
|
|
return points;
|
|
@@ -651,8 +713,13 @@ export class GraphicalSlur extends GraphicalCurve {
|
|
}
|
|
}
|
|
|
|
|
|
for (let i: number = startIndex; i < endIndex; i++) {
|
|
for (let i: number = startIndex; i < endIndex; i++) {
|
|
- const point: PointF2D = new PointF2D((0.5 + i) / skyBottomLineCalculator.SamplingUnit, staffLine.BottomLine[i]);
|
|
|
|
- points.push(point);
|
|
|
|
|
|
+ const bottomLineValue: number = staffLine.BottomLine[i];
|
|
|
|
+
|
|
|
|
+ // ignore default value (= 4) which is lower border of staffline
|
|
|
|
+ if (bottomLineValue !== 0) {
|
|
|
|
+ const point: PointF2D = new PointF2D((0.5 + i) / skyBottomLineCalculator.SamplingUnit, bottomLineValue);
|
|
|
|
+ points.push(point);
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
return points;
|
|
return points;
|
|
@@ -678,6 +745,8 @@ export class GraphicalSlur extends GraphicalCurve {
|
|
|
|
|
|
// in case all Points don't have a meaningful value or the slope between Start- and EndPoint is just bigger
|
|
// in case all Points don't have a meaningful value or the slope between Start- and EndPoint is just bigger
|
|
slope = Math.max(slope, Math.abs(end.y - y) / (end.x - x));
|
|
slope = Math.max(slope, Math.abs(end.y - y) / (end.x - x));
|
|
|
|
+ //limit to 80 degrees
|
|
|
|
+ slope = Math.min(slope, 5.6713);
|
|
|
|
|
|
return slope;
|
|
return slope;
|
|
}
|
|
}
|
|
@@ -702,6 +771,8 @@ export class GraphicalSlur extends GraphicalCurve {
|
|
|
|
|
|
// in case no Point has a meaningful value or the slope between Start- and EndPoint is just smaller
|
|
// in case no Point has a meaningful value or the slope between Start- and EndPoint is just smaller
|
|
slope = Math.min(slope, (y - start.y) / (x - start.x));
|
|
slope = Math.min(slope, (y - start.y) / (x - start.x));
|
|
|
|
+ //limit to 80 degrees
|
|
|
|
+ slope = Math.max(slope, -5.6713);
|
|
|
|
|
|
return slope;
|
|
return slope;
|
|
}
|
|
}
|
|
@@ -787,65 +858,62 @@ export class GraphicalSlur extends GraphicalCurve {
|
|
/**
|
|
/**
|
|
* This method calculates the 2 ControlPoints of the SlurCurve.
|
|
* This method calculates the 2 ControlPoints of the SlurCurve.
|
|
* @param endX
|
|
* @param endX
|
|
- * @param leftAngle
|
|
|
|
- * @param rightAngle
|
|
|
|
|
|
+ * @param startAngle
|
|
|
|
+ * @param endAngle
|
|
* @param points
|
|
* @param points
|
|
*/
|
|
*/
|
|
- private calculateControlPoints(endX: number,
|
|
|
|
- leftAngle: number, rightAngle: number, points: PointF2D[]): { leftControlPoint: PointF2D, rightControlPoint: PointF2D } {
|
|
|
|
|
|
+ private calculateControlPoints(endX: number, startAngle: number, endAngle: number,
|
|
|
|
+ points: PointF2D[], heightWidthRatio: number): { startControlPoint: PointF2D, endControlPoint: PointF2D } {
|
|
// calculate HeightWidthRatio between the MaxYpoint (from the points between StartPoint and EndPoint)
|
|
// calculate HeightWidthRatio between the MaxYpoint (from the points between StartPoint and EndPoint)
|
|
- // and the X-distance from StartPoint to EndPoint
|
|
|
|
- // use this HeightWidthRatio to get a "normalized" Factor (based on tested parameters)
|
|
|
|
- // this Factor denotes the Length of the TangentLine of the Curve (a proportion of the X-distance from StartPoint to EndPoint)
|
|
|
|
- // finally from this Length and the calculated Angles we get the coordinates of the Control Points
|
|
|
|
- const heightWidthRatio: number = this.calculateHeightWidthRatio(endX, points);
|
|
|
|
- const factor: number = GraphicalSlur.k * heightWidthRatio + GraphicalSlur.d;
|
|
|
|
-
|
|
|
|
- const relativeLength: number = endX * factor;
|
|
|
|
- const leftControlPoint: PointF2D = new PointF2D();
|
|
|
|
- leftControlPoint.x = relativeLength * Math.cos(leftAngle * GraphicalSlur.degreesToRadiansFactor);
|
|
|
|
- leftControlPoint.y = relativeLength * Math.sin(leftAngle * GraphicalSlur.degreesToRadiansFactor);
|
|
|
|
-
|
|
|
|
- const rightControlPoint: PointF2D = new PointF2D();
|
|
|
|
- rightControlPoint.x = endX - (relativeLength * Math.cos(rightAngle * GraphicalSlur.degreesToRadiansFactor));
|
|
|
|
- rightControlPoint.y = -(relativeLength * Math.sin(rightAngle * GraphicalSlur.degreesToRadiansFactor));
|
|
|
|
- return {leftControlPoint, rightControlPoint};
|
|
|
|
|
|
+ // and the X-distance from StartPoint to EndPoint
|
|
|
|
+ // use this HeightWidthRatio to get a "normalized" Factor (based on tested parameters)
|
|
|
|
+ // this Factor denotes the Length of the TangentLine of the Curve (a proportion of the X-distance from StartPoint to EndPoint)
|
|
|
|
+ // finally from this Length and the calculated Angles we get the coordinates of the Control Points
|
|
|
|
+ const factorStart: number = Math.min(0.5, Math.max(0.1, 1.7 * (startAngle / 80) * Math.pow(Math.max(heightWidthRatio, 0.05), 0.4)));
|
|
|
|
+ const factorEnd: number = Math.min(0.5, Math.max(0.1, 1.7 * (-endAngle / 80) * Math.pow(Math.max(heightWidthRatio, 0.05), 0.4)));
|
|
|
|
+
|
|
|
|
+ const startControlPoint: PointF2D = new PointF2D();
|
|
|
|
+ startControlPoint.x = endX * factorStart * Math.cos(startAngle * GraphicalSlur.degreesToRadiansFactor);
|
|
|
|
+ startControlPoint.y = endX * factorStart * Math.sin(startAngle * GraphicalSlur.degreesToRadiansFactor);
|
|
|
|
+
|
|
|
|
+ const endControlPoint: PointF2D = new PointF2D();
|
|
|
|
+ endControlPoint.x = endX - (endX * factorEnd * Math.cos(endAngle * GraphicalSlur.degreesToRadiansFactor));
|
|
|
|
+ endControlPoint.y = -(endX * factorEnd * Math.sin(endAngle * GraphicalSlur.degreesToRadiansFactor));
|
|
|
|
+ return {startControlPoint: startControlPoint, endControlPoint: endControlPoint};
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
* This method calculates the angles for the Curve's Tangent Lines.
|
|
* This method calculates the angles for the Curve's Tangent Lines.
|
|
* @param leftAngle
|
|
* @param leftAngle
|
|
* @param rightAngle
|
|
* @param rightAngle
|
|
- * @param leftLineSlope
|
|
|
|
- * @param rightLineSlope
|
|
|
|
|
|
+ * @param startLineSlope
|
|
|
|
+ * @param endLineSlope
|
|
* @param maxAngle
|
|
* @param maxAngle
|
|
*/
|
|
*/
|
|
- private calculateAngles(minAngle: number, leftLineSlope: number, rightLineSlope: number, maxAngle: number):
|
|
|
|
- {leftAngle: number, rightAngle: number} {
|
|
|
|
|
|
+ private calculateAngles(minAngle: number, startLineSlope: number, endLineSlope: number, maxAngle: number):
|
|
|
|
+ {startAngle: number, endAngle: number} {
|
|
// calculate Angles from the calculated Slopes, adding also a given angle
|
|
// calculate Angles from the calculated Slopes, adding also a given angle
|
|
const angle: number = 20;
|
|
const angle: number = 20;
|
|
|
|
|
|
- let calculatedLeftAngle: number = Math.atan(leftLineSlope) / GraphicalSlur.degreesToRadiansFactor;
|
|
|
|
- if (leftLineSlope > 0) {
|
|
|
|
- calculatedLeftAngle += angle;
|
|
|
|
|
|
+ let calculatedStartAngle: number = Math.atan(startLineSlope) / GraphicalSlur.degreesToRadiansFactor;
|
|
|
|
+ if (startLineSlope > 0) {
|
|
|
|
+ calculatedStartAngle += angle;
|
|
} else {
|
|
} else {
|
|
- calculatedLeftAngle -= angle;
|
|
|
|
|
|
+ calculatedStartAngle -= angle;
|
|
}
|
|
}
|
|
|
|
|
|
- let calculatedRightAngle: number = Math.atan(rightLineSlope) / GraphicalSlur.degreesToRadiansFactor;
|
|
|
|
- if (rightLineSlope < 0) {
|
|
|
|
- calculatedRightAngle -= angle;
|
|
|
|
|
|
+ let calculatedEndAngle: number = Math.atan(endLineSlope) / GraphicalSlur.degreesToRadiansFactor;
|
|
|
|
+ if (endLineSlope < 0) {
|
|
|
|
+ calculatedEndAngle -= angle;
|
|
} else {
|
|
} else {
|
|
- calculatedRightAngle += angle;
|
|
|
|
|
|
+ calculatedEndAngle += angle;
|
|
}
|
|
}
|
|
|
|
|
|
// +/- 80 is the max/min allowed Angle
|
|
// +/- 80 is the max/min allowed Angle
|
|
- const leftAngle: number = Math.min(Math.max(minAngle, calculatedLeftAngle), maxAngle);
|
|
|
|
- const rightAngle: number = Math.max(Math.min(-minAngle, calculatedRightAngle), -maxAngle);
|
|
|
|
- return {"leftAngle": leftAngle, "rightAngle": rightAngle};
|
|
|
|
|
|
+ const leftAngle: number = Math.min(Math.max(minAngle, calculatedStartAngle), maxAngle);
|
|
|
|
+ const rightAngle: number = Math.max(Math.min(-minAngle, calculatedEndAngle), -maxAngle);
|
|
|
|
+ return {"startAngle": leftAngle, "endAngle": rightAngle};
|
|
}
|
|
}
|
|
|
|
|
|
private static degreesToRadiansFactor: number = Math.PI / 180;
|
|
private static degreesToRadiansFactor: number = Math.PI / 180;
|
|
- private static k: number = 0.9;
|
|
|
|
- private static d: number = 0.2;
|
|
|
|
}
|
|
}
|