Last active
April 13, 2022 01:09
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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 }; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Demo at https://codesandbox.io/s/find-handle-for-cubic-curve-single-fn-nirsnv