parseArcCommand method
void
parseArcCommand()
- https://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
- https://mortoray.com/2017/02/16/rendering-an-svg-elliptical-arc-as-bezier-curves/ Appendix: Endpoint to center arc conversion
- From
- rx ry x-axis-rotation large-arc-flag sweep-flag x y
- To
- aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation
- https://mortoray.com/2017/02/16/rendering-an-svg-elliptical-arc-as-bezier-curves/ Appendix: Endpoint to center arc conversion
Implementation
void parseArcCommand(ShapePath path, double rx, double ry, double xAxisRotation, double largeArcFlag, double sweepFlag, Vector start, Vector end) {
if (rx == 0 || ry == 0) {
// draw a line if either of the radii == 0
path.lineTo(end.x, end.y);
return;
}
xAxisRotation = xAxisRotation * math.pi / 180;
// Ensure radii are positive
rx = rx.abs();
ry = ry.abs();
// Compute (x1', y1')
final dx2 = (start.x - end.x) / 2.0;
final dy2 = (start.y - end.y) / 2.0;
final x1p = math.cos(xAxisRotation) * dx2 + math.sin(xAxisRotation) * dy2;
final y1p = -math.sin(xAxisRotation) * dx2 + math.cos(xAxisRotation) * dy2;
// Compute (cx', cy')
double rxs = rx * rx;
double rys = ry * ry;
final x1ps = x1p * x1p;
final y1ps = y1p * y1p;
// Ensure radii are large enough
final cr = x1ps / rxs + y1ps / rys;
if (cr > 1) {
// scale up rx,ry equally so cr == 1
final s = math.sqrt(cr);
rx = s * rx;
ry = s * ry;
rxs = rx * rx;
rys = ry * ry;
}
final dq = (rxs * y1ps + rys * x1ps);
final pq = (rxs * rys - dq) / dq;
double q = math.sqrt(math.max(0, pq));
if (largeArcFlag == sweepFlag) q = -q;
final cxp = q * rx * y1p / ry;
final cyp = -q * ry * x1p / rx;
// Step 3: Compute (cx, cy) from (cx', cy')
final cx = math.cos(xAxisRotation) * cxp -
math.sin(xAxisRotation) * cyp +
(start.x + end.x) / 2;
final cy = math.sin(xAxisRotation) * cxp +
math.cos(xAxisRotation) * cyp +
(start.y + end.y) / 2;
// Step 4: Compute θ1 and Δθ
final theta = svgAngle(1, 0, (x1p - cxp) / rx, (y1p - cyp) / ry);
final delta = svgAngle((x1p - cxp) / rx, (y1p - cyp) / ry, (-x1p - cxp) / rx,
(-y1p - cyp) / ry) %
(math.pi * 2);
path.currentPath.absellipse(
cx, cy, rx, ry, theta, theta + delta, sweepFlag == 0, xAxisRotation);
}