Skip to content

Instantly share code, notes, and snippets.

@steveruizok
Last active April 13, 2022 01:09
Show Gist options
  • Save steveruizok/4d7e2e41260ebc77a6466ee1ec1f7f14 to your computer and use it in GitHub Desktop.
Save steveruizok/4d7e2e41260ebc77a6466ee1ec1f7f14 to your computer and use it in GitHub Desktop.
Find the control points for a cubic bezier curve segment from point a to point c passing through point b.
/**
* Find the control points for a cubic segment from point a to point c passing through point b.
* @param a The curve's start point
* @param b The point to curve through
* @param c The curve's end point
*/
export function findCubicControlPoints(
a: { x: number; y: number },
b: { x: number; y: number },
c: { x: number; y: number }
) {
// center of the circle ABC
const ctr = {
x:
((a.x * a.x + a.y * a.y) * (c.y - b.y) +
(b.x * b.x + b.y * b.y) * (a.y - c.y) +
(c.x * c.x + c.y * c.y) * (b.y - a.y)) /
((a.x * (b.y - c.y) - a.y * (b.x - c.x) + b.x * c.y - c.x * b.y) * -2),
y:
((a.x * a.x + a.y * a.y) * (b.x - c.x) +
(b.x * b.x + b.y * b.y) * (c.x - a.x) +
(c.x * c.x + c.y * c.y) * (a.x - b.x)) /
((a.x * (b.y - c.y) - a.y * (b.x - c.x) + b.x * c.y - c.x * b.y) * -2)
};
// tangent line through b
const tan1 = { x: b.x - (b.y - ctr.y), y: b.y + (b.x - ctr.x) };
const tan2 = { x: b.x + (b.y - ctr.y), y: b.y - (b.x - ctr.x) };
// normalized slope of tangent line
const tlength = Math.hypot(tan1.y - tan2.y, tan1.x - tan2.x);
const dx = (tan2.x - tan1.x) / tlength;
const dy = (tan2.y - tan1.y) / tlength;
// find t
const dAB = Math.hypot(a.y - b.y, a.x - b.x);
const dBC = Math.hypot(c.y - b.y, c.x - b.x);
const dAC = Math.hypot(a.y - c.y, a.x - c.x);
const t = dAB / (dAB + dBC);
// points e1 and e2 on tangent line from circle (through b)
const aCA = Math.atan2(c.y - a.y, c.x - a.x);
const aBA = Math.atan2(b.y - a.y, b.x - a.x);
const ang = aCA - aBA;
const bc = ((ang < 0 || ang > Math.PI ? -1 : 1) * dAC) / 3;
const de1 = t * bc;
const de2 = (1 - t) * bc;
const e1 = { x: b.x + de1 * dx, y: b.y + de1 * dy };
const e2 = { x: b.x - de2 * dx, y: b.y - de2 * dy };
// point d
const t1 = Math.pow(1 - t, 3);
const b1 = Math.pow(t, 3) + t1;
const u = t1 / b1;
const s = Math.abs((b1 - 1) / b1);
const d = {
x: b.x + (b.x - (u * a.x + (1 - u) * c.x)) / s,
y: b.y + (b.y - (u * a.y + (1 - u) * c.y)) / s
};
// interpolated point on line D/e1
const v1 = {
x: d.x + (e1.x - d.x) / (1 - t),
y: d.y + (e1.y - d.y) / (1 - t)
};
// interpolated point on line D/e2
const v2 = {
x: d.x + (e2.x - d.x) / t,
y: d.y + (e2.y - d.y) / t
};
// control point1, interpolated point on line a/v1
const c1 = {
x: a.x + (v1.x - a.x) / t,
y: a.y + (v1.y - a.y) / t
};
// control point2, interpolated point on line c/v2
const c2 = {
x: c.x + (v2.x - c.x) / (1 - t),
y: c.y + (v2.y - c.y) / (1 - t)
};
return { a, c1, c2, b };
}
@steveruizok
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment