Skip to content

Commit

Permalink
fix(geom): update arcFrom2Points()
Browse files Browse the repository at this point in the history
  • Loading branch information
postspectacular committed Jan 19, 2019
1 parent 910529d commit 62ec49f
Showing 1 changed file with 63 additions and 36 deletions.
99 changes: 63 additions & 36 deletions packages/geom3/src/ctors/arc.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import { isNumber } from "@thi.ng/checks";
import { TAU } from "@thi.ng/math";
import { EPS, TAU } from "@thi.ng/math";
import {
abs2,
angleBetween2,
div2,
eqDelta2,
mulN2,
neg,
powN2,
ReadonlyVec,
sub2,
submN2,
Vec,
X2
Expand All @@ -21,49 +26,71 @@ export const arc = (
clockwise = false
) => new Arc(pos, isNumber(r) ? [r, r] : r, axis, start, end, xl, clockwise);

/**
* https://svgwg.org/svg2-draft/implnote.html#ArcConversionEndpointToCenter
*
* Returns undefined if `a` & `b` are equal or any `radii` component is
* zero.
*
* @param a start point
* @param b end point
* @param radii ellipse radii
* @param axisTheta in radians
* @param xl large arc flag
* @param cw clockwise flag
*/
export const arcFrom2Points = (
a: ReadonlyVec,
b: ReadonlyVec,
radii: ReadonlyVec,
axisTheta = 0,
large = false,
clockwise = false
xl = false,
cw = false
) => {

const r = abs2([], radii);
const co = Math.cos(axisTheta);
const si = Math.sin(axisTheta);
const m = submN2([], a, b, 0.5);
// const m = mulN2(null, sub2([], a, b), 0.5);
const px = co * m[0] + si * m[1];
const py = -si * m[0] + co * m[1];
const px2 = px * px;
const py2 = py * py;

const l = px2 / (r[0] * r[0]) + py2 / (r[1] * r[1]);
l > 1 && mulN2(null, r, Math.sqrt(l));

const rx2 = r[0] * r[0];
const ry2 = r[1] * r[1];
const rxpy = rx2 * py2;
const rypx = ry2 * px2;
const rad = ((large === clockwise) ? -1 : 1) *
Math.sqrt(Math.max(0, rx2 * ry2 - rxpy - rypx) / (rxpy + rypx));

const tx = rad * r[0] / r[1] * py;
const ty = -rad * r[1] / r[0] * px;
const c = [co * tx - si * ty + (a[0] + b[0]) / 2, si * tx + co * ty + (a[1] + b[1]) / 2];
const d1 = [(px - tx) / r[0], (py - ty) / r[1]];
const d2 = [(-px - tx) / r[0], (-py - ty) / r[1]];

const theta = angleBetween2(X2, d1);
let delta = angleBetween2(d1, d2);

if (clockwise && delta < 0) {
delta += TAU;
} else if (!clockwise && delta > 0) {
delta -= TAU;
if (eqDelta2(a, b) || r[0] < EPS || r[1] < EPS) {
return;
}
axisTheta %= TAU;
const d = submN2([], a, b, 0.5);
const c = Math.cos(axisTheta);
const s = Math.sin(axisTheta);
// transformed point
const tp = [
c * d[0] + s * d[1],
-s * d[0] + c * d[1]
];
const [tx2, ty2] = powN2([], tp, 2);
// ensure radii
const rc = tx2 / (r[0] * r[0]) + ty2 / (r[1] * r[1]);
rc > 1 && mulN2(r, r, Math.sqrt(rc));
const [rx, ry] = r;
const rx2 = rx * rx;
const ry2 = ry * ry;
// transformed center
const radicant = Math.max(0, (rx2 * ry2 - rx2 * ty2 - ry2 * tx2) / (rx2 * ty2 + ry2 * tx2));
const coeff = (xl !== cw ? 1 : -1) * Math.sqrt(radicant);
const tc = [
coeff * ((rx * tp[1]) / ry),
coeff * (-(ry * tp[0]) / rx)
];
// actual center
const center = [
c * tc[0] - s * tc[1] + (a[0] + b[0]) / 2,
s * tc[0] + c * tc[1] + (a[1] + b[1]) / 2
];
// transformed end points & angles
const ta = div2(null, sub2([], tp, tc), r);
const tb = div2(null, sub2(null, neg([], tp), tc), r);
const start = angleBetween2(X2, ta);
let sweep = angleBetween2(ta, tb);
if (!cw && sweep > 0) {
sweep -= TAU;
} else if (cw && sweep < 0) {
sweep += TAU;
}
sweep %= TAU;

return new Arc(c, r, axisTheta, theta, theta + delta, large, clockwise);
return new Arc(center, r, axisTheta, start, start + sweep, xl, cw);
};

0 comments on commit 62ec49f

Please sign in to comment.