Parse
File Parse curve-knife.js
This tree is parsed live from the source file.
Classes
-
{{ item.name }}
- {{ key }}
Not Classes
{{ getTree() }}
Comments
{{ getTreeComments() }}
Source
/**
* Linearly interpolate between two points a and b at parameter t
* Returns a new point { x, y }
*/
function lerp(a, b, t) {
return {
x: a.x + (b.x - a.x) * t,
y: a.y + (b.y - a.y) * t
};
}
function subdivideCubicBezier(P0, cP1, cP2, P3, t) {
/**
* Subdivide a cubic Bézier at parameter t using De Casteljau’s algorithm.
* @param {Object} P0 - Starting control point {x, y}
* @param {Object} P1 - 1st control point {x, y}
* @param {Object} P2 - 2nd control point {x, y}
* @param {Object} P3 - Ending control point {x, y}
* @param {number} t - Split parameter, 0 < t < 1
* @returns {Object} An object with two arrays:
* left: [P0, A, D, F]
* right: [F, E, C, P3]
*/
// Step 1: interpolate P0->P1, P1->P2, P2->P3
const A = lerp(P0, cP1, t);
const B = lerp(cP1, cP2, t);
const C = lerp(cP2, P3, t);
// Step 2: interpolate A->B, B->C
const D = lerp(A, B, t);
const E = lerp(B, C, t);
// Step 3: final interpolation between D->E
const F = lerp(D, E, t);
// Return two new sets of control points
return {
left: [P0, A, D, F]
, right: [F, E, C, P3]
, t
};
}
const findNearestPoint = function(bezierStack, p, divisor=100) {
// Example usage:
// const t = 0.5; // subdivide at halfway
// const { left, right } = subdivideCubicBezier(P0, P1, P2, P3, t);
// console.log("Left subdivided curve:", left);
// console.log("Right subdivided curve:", right);
let minDist = Infinity;
let closestPoint = null;
let pointA = bezierStack.line.a
let pointB = bezierStack.line.b
let controlPointA = bezierStack.controlPoints.a
let controlPointB = bezierStack.controlPoints.b
// Sample points along the Bezier curve
for (let i = 0; i <= divisor; i++) {
let t = i / divisor;
let oneMt = 1 - t;
let omtp3 = Math.pow(oneMt, 3)
let tpow3 = Math.pow(t, 3)
let tpow2 = Math.pow(t, 2)
let omtp2 = Math.pow(oneMt, 2)
// Calculate the point on the cubic Bezier curve at parameter t
let x = omtp3 * pointA.x + 3
* omtp2 * t * controlPointA.x + 3
* (oneMt) * tpow2 * controlPointB.x
+ tpow3 * pointB.x
;
let y = omtp3 * pointA.y + 3
* omtp2 * t * controlPointA.y + 3
* (oneMt) * tpow2 * controlPointB.y
+ tpow3 * pointB.y
;
let dx = p.x - x;
let dy = p.y - y;
let distSq = dx * dx + dy * dy;
if (distSq < minDist) {
minDist = distSq;
closestPoint = { x: x, y: y, t:t };
}
}
return closestPoint
}
Polypoint.head.installFunctions('BezierCurve', {
nearestPoint(other, divisor=100) {
let cps = this.getControlPoints()
const stack = {
line: this
, controlPoints: {a: cps[0], b: cps[1]}
};
return findNearestPoint(stack, other, divisor)
}
, splitAt(t) {
let l = this
let cps = this.getControlPoints()
let v = subdivideCubicBezier(
l.a.copy()
, cps[0]
, cps[1]
, l.b.copy()
, t)
// console.log(v)
this.lineA = this._makeLine(v.left)
this.lineB = this._makeLine(v.right)
return [
this.lineA
, this.lineB
];
}
, _makeLine(lA) {
let a = new Point(lA[0])
let c1 = new Point(lA[1])
let c2 = new Point(lA[2])
let b = new Point(lA[3])
a.lookAt(c1)
b.lookAt(c2)
a.radius = a.distanceTo(c1)
b.radius = b.distanceTo(c2)
return {line: new BezierCurve(a,b,), controls: [c1, c2]}
}
});
copy