Created
September 28, 2022 10:28
-
-
Save eeropic/61143810ddb2bfa0fa365b16a665ccbe to your computer and use it in GitHub Desktop.
solve y for x with unit beziers in range of x [0,1] for animation etc purposes
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
// http://svn.webkit.org/repository/webkit/trunk/Source/WebCore/platform/graphics/UnitBezier.h | |
class UnitBezier { | |
constructor(p1x,p1y,p2x,p2y){ | |
// Calculate the polynomial coefficients, implicit first and last control points are (0,0) and (1,1). | |
this.cx = 3.0 * p1x; | |
this.bx = 3.0 * (p2x - p1x) - this.cx; | |
this.ax = 1.0 - this.cx - this.bx; | |
this.cy = 3.0 * p1y; | |
this.by = 3.0 * (p2y - p1y) - this.cy; | |
this.ay = 1.0 - this.cy - this.by; | |
Object.assign(this, {p1x, p1y, p2x, p2y}) | |
} | |
sampleCurveX(t){ | |
// `ax t^3 + bx t^2 + cx t' expanded using Horner's rule. | |
return ((this.ax * t + this.bx) * t + this.cx) * t; | |
} | |
sampleCurveY(t){ | |
return ((this.ay * t + this.by) * t + this.cy) * t; | |
} | |
sampleCurveDerivativeX(t){ | |
return (3.0 * this.ax * t + 2.0 * this.bx) * t + this.cx; | |
} | |
solveCurveX(x, epsilon){ | |
if (epsilon === undefined) epsilon = 1e-6; | |
if (x < 0.0) return 0.0; | |
if (x > 1.0) return 1.0; | |
var t = x; | |
// First try a few iterations of Newton's method - normally very fast. | |
for (var i = 0; i < 8; i++) { | |
var x2 = this.sampleCurveX(t) - x; | |
if (Math.abs(x2) < epsilon) return t; | |
var d2 = this.sampleCurveDerivativeX(t); | |
if (Math.abs(d2) < 1e-6) break; | |
t = t - x2 / d2; | |
} | |
// Fall back to the bisection method for reliability. | |
var t0 = 0.0; | |
var t1 = 1.0; | |
t = x; | |
for (i = 0; i < 20; i++) { | |
x2 = this.sampleCurveX(t); | |
if (Math.abs(x2 - x) < epsilon) break; | |
if (x > x2) { | |
t0 = t; | |
} else { | |
t1 = t; | |
} | |
t = (t1 - t0) * 0.5 + t0; | |
} | |
return t; | |
} | |
solve(x, epsilon) { | |
return this.sampleCurveY(this.solveCurveX(x, epsilon)); | |
} | |
} | |
let testPath = new Path({ | |
segments: [ | |
new Segment({ | |
point: [180,200], | |
handleOut: [200,20] | |
}), | |
new Segment({ | |
point: [400,500], | |
handleIn: [-200,60] | |
}) | |
], | |
strokeColor: "red" | |
}) | |
function curveToUnit(curve){ | |
const [x0, y0, x1, y1, x2, y2, x3, y3] = curve.values | |
let w = x3 - x0 | |
let h = y3 - y0 | |
let p1x = (x1 - x0) / w | |
let p1y = (y1 - y0) / h | |
let p2x = (x2 - x0) / w | |
let p2y = (y2 - y0) / h | |
let kek2 = new UnitBezier(p1x, p1y, p2x, p2y) | |
let steps = 40; | |
for(let i = 0; i <= steps; i++){ | |
let xPos = i / steps | |
let yPos = kek2.solve(xPos) | |
let circ = new Shape.Circle({ | |
fillColor:"blue", | |
radius: 2, | |
position: [x0 + xPos * w, y0 + yPos * h] | |
}) | |
} | |
} | |
curveToUnit(testPath.curves[0]) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment