Skip to content

Instantly share code, notes, and snippets.

@steveruizok
Last active May 6, 2021 20:26
Show Gist options
  • Save steveruizok/d2d43fb766926bf3a04a3f002cd8e714 to your computer and use it in GitHub Desktop.
Save steveruizok/d2d43fb766926bf3a04a3f002cd8e714 to your computer and use it in GitHub Desktop.
Get cubic bezier curve segments that connect an array of points.
function getBezierCurveSegments(points: number[][], tension = 0.4) {
const len = points.length,
cpoints: number[][] = points.slice(0)
if (len < 2) {
throw Error('Curve must have at least two points.')
}
for (let i = 1; i < len - 1; i++) {
let pp = points[i - 1],
pi = points[i],
pn = points[i + 1]
const pdx = pn[0] - pp[0],
pdy = pn[1] - pp[1],
pd = Math.hypot(pdx, pdy),
dx = pdx / pd,
dy = pdy / pd,
dp = Math.hypot(pi[0] - pp[0], pi[1] - pp[1]),
dn = Math.hypot(pi[0] - pn[0], pi[1] - pn[1])
cpoints[i] = [
pi[0] - dx * dp * tension,
pi[1] - dy * dp * tension,
pi[0] + dx * dn * tension,
pi[1] + dy * dn * tension,
]
}
// TODO: Reflect the nearest control points, not average them
cpoints[0][2] = (points[0][0] + cpoints[1][0]) / 2
cpoints[0][3] = (points[0][1] + cpoints[1][1]) / 2
cpoints[len - 1][0] = (points[len - 1][0] + cpoints[len - 2][2]) / 2
cpoints[len - 1][1] = (points[len - 1][1] + cpoints[len - 2][3]) / 2
const results: {
start: number[]
end: number[]
tangentStart: number[]
tangentEnd: number[]
}[] = []
for (let i = 1; i < cpoints.length; i++) {
results.push({
start: points[i - 1],
tangentStart: cpoints[i - 1].slice(2),
tangentEnd: cpoints[i].slice(0, 2),
end: points[i],
})
}
return results
}
function pointOnBezierCurve(segment: BezierCurveSegment, t: number) {
let {
start: [ax, ay],
tangentStart: [bx, by],
tangentEnd: [cx, cy],
end: [dx, dy],
} = segment
ax += (bx - ax) * t
bx += (cx - bx) * t
cx += (dx - cx) * t
ax += (bx - ax) * t
bx += (cx - bx) * t
ay += (by - ay) * t
by += (cy - by) * t
cy += (dy - cy) * t
ay += (by - ay) * t
by += (cy - by) * t
return vec.add([ax, ay], vec.mul(vec.sub([bx, by], [ax, ay]), t))
}
function getBezierCurveLength(segment: BezierCurveSegment, ptCount = 10) {
let totDist = 0
let prev = segment.start
for (var i = 1; i < ptCount; i++) {
var pt = pointOnBezierCurve(segment, i / ptCount)
totDist += vec.dist(pt, prev)
prev = pt
}
totDist += vec.dist(prev, segment.end)
return totDist
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment