Published
Edited
Mar 21, 2021
Importers
Insert cell
md`# Svg EllipticalArc`
Insert cell
/**
* svg Arc to canvas 2d
*
* canvas ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise)
* svg cmd A rx ry x-axis-rotation large-arc-flag sweep-flag x y
*
*/

/**
* https://svgwg.org/svg2-draft/implnote.html#ArcConversionEndpointToCenter
* @param {*} x1
* @param {*} y1
* @param {*} x2
* @param {*} y2
* @param {*} largeArcFlag large-arc-flag
* @param {*} sweepFlag sweep-flag
* @param {*} srx 椭圆x半径
* @param {*} sry 椭圆y半径
* @param {*} xAxisRotationDeg x-axis-rotation
*/
function conversionEndPointToCenterPointParameterization(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,
xAxisRotation,
startAngle,
endAngle,
// xAxisRotation,
deltaAngle < 0
]
// return {
// cx,
// cy,
// rx,
// ry,
// startAngle,
// endAngle,
// xAxisRotation,
// anticlockwise: deltaAngle < 0
// };
}
Insert cell
function mat2DotVec2([m00, m01, m10, m11], [vx, vy]) {
return [m00 * vx + m01 * vy, m10 * vx + m11 * vy];
}
Insert cell
function vec2Add([ux, uy], [vx, vy]) {
return [ux + vx, uy + vy];
}
Insert cell
function vec2Scale([a0, a1], scalar) {
return [a0 * scalar, a1 * scalar];
}
Insert cell
function vec2Mag([ux, uy]) {
return Math.sqrt(ux ** 2 + uy ** 2);
}
Insert cell
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)));
}
Insert cell
function vec2Dot([ux, uy], [vx, vy]) {
return ux * vx + uy * vy;
}
Insert cell
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];
}
Insert cell
function pow(n) {
return Math.pow(n, 2);
}
Insert cell
/**
* degree to Radian
* @param {*} deg
* @returns
*/
function degToRad(deg) {
return (deg * Math.PI) / 180;
}
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more