- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
import { degToRad, pow } from "./../math/math.js";
function correctRadii(signedRx, signedRy, x1p, y1p) {
const prx = Math.abs(signedRx);
const pry = Math.abs(signedRy);
const A = pow(x1p) / pow(prx) + pow(y1p) / pow(pry);
const rx = A > 1 ? Math.sqrt(A) * prx : prx;
const ry = A > 1 ? Math.sqrt(A) * pry : pry;
return [rx, ry];
}
function mat2DotVec2([m00, m01, m10, m11], [vx, vy]) {
return [m00 * vx + m01 * vy, m10 * vx + m11 * vy];
}
function vec2Add([ux, uy], [vx, vy]) {
return [ux + vx, uy + vy];
}
function vec2Scale([a0, a1], scalar) {
return [a0 * scalar, a1 * scalar];
}
function vec2Dot([ux, uy], [vx, vy]) {
return ux * vx + uy * vy;
}
function vec2Mag([ux, uy]) {
return Math.sqrt(ux ** 2 + uy ** 2);
}
function vec2Angle(u, v) {
const [ux, uy] = u;
const [vx, vy] = v;
const sign = ux * vy - uy * vx >= 0 ? 1 : -1;
return sign * Math.acos(vec2Dot(u, v) / (vec2Mag(u) * vec2Mag(v)));
}
// From https://svgwg.org/svg2-draft/implnote.html#ArcConversionEndpointToCenter
export function endpointToCenterParameterization(x1, y1, x2, y2, largeArcFlag, sweepFlag, srx, sry, xAxisRotationDeg) {
const xAxisRotation = degToRad(xAxisRotationDeg);
const cosphi = Math.cos(xAxisRotation);
const sinphi = Math.sin(xAxisRotation);
const [x1p, y1p] = mat2DotVec2(
[cosphi, sinphi, -sinphi, cosphi],
[(x1 - x2) / 2, (y1 - y2) / 2]
);
const [rx, ry] = correctRadii(srx, sry, x1p, y1p);
const sign = largeArcFlag !== sweepFlag ? 1 : -1;
const n = pow(rx) * pow(ry) - pow(rx) * pow(y1p) - pow(ry) * pow(x1p);
const d = pow(rx) * pow(y1p) + pow(ry) * pow(x1p);
const [cxp, cyp] = vec2Scale(
[(rx * y1p) / ry, (-ry * x1p) / rx],
sign * Math.sqrt(Math.abs(n / d))
);
const [cx, cy] = vec2Add(
mat2DotVec2([cosphi, -sinphi, sinphi, cosphi], [cxp, cyp]),
[(x1 + x2) / 2, (y1 + y2) / 2]
);
const a = [(x1p - cxp) / rx, (y1p - cyp) / ry];
const b = [(-x1p - cxp) / rx, (-y1p - cyp) / ry];
const startAngle = vec2Angle([1, 0], a);
const deltaAngle0 = vec2Angle(a, b) % (2 * Math.PI);
const deltaAngle =
!sweepFlag && deltaAngle0 > 0
? deltaAngle0 - 2 * Math.PI
: sweepFlag && deltaAngle0 < 0
? deltaAngle0 + 2 * Math.PI
: deltaAngle0;
const endAngle = startAngle + deltaAngle;
return {
cx,
cy,
rx,
ry,
startAngle,
endAngle,
xAxisRotation,
anticlockwise: deltaAngle < 0
};
}