To elaborate on MBo's answer, C(t) = X' * Y'' - X'' * Y' = 0
is pretty much the pseudocode you want already, because the first and second derivatives are really easy to calculate.
Following this explanation of Bezier derivatives, and a generic Bezier function with coordinates (x1,y1)...(x4,y4) we get:
fx(t) = x1 (1-t)³ + 3·x2·(1-t)²·t + 3·x3·(1-t)·t² + x4·t³
fx'(t) = a·(1-t)² + 2·b·(1-t)·t + c·t²
where a = 3(x2-x1), b = 3(x3-x2), and c = 3(x4-x3), and:
fx''(t) = u·(1-t) + v·t
where u = 2(b-a) and v = 2(c-b). And of course the same goes for the y component:
fy(t) = y1 (1-t)³ + 3·y2·(1-t)²·t + 3·y3·(1-t)·t² + y4·t³
fy'(t) = a'·(1-t)² + 2·b'·(1-t)·t + c'·t²
fy''(t) = u'·(1-t) + v'·t
where a'
is the same as a
but with y
values, etc.
Working out the maths for C(t) = fx'(t)·fy''(t) - fx''(t)·fy'(t)
is annoying, but that's why we own computers. If you own a raspberry pi, you own a license for Mathematica, so let's put that to use:
That's a massive formula, but finding the inflections for an "arbitrary" curve is a bit silly, because Bezier curves are invariant to linear affine transforms, so the the t
value of the inflection point stays the same whether we check "the real curve" or whether we rotate/translate/scale the curve so that it has more convenient coordinates. Like translating it so that (x1,y1) ends up being (0,0), and (x4,y4) lies on the x-axis so that y4 is zero.
If we do that, then we get a vastly simpler formula:
How much simpler is this? well:
18 times:
- x3 * y2
+ 3 * x3 * y2 * t
- 3 * x3 * y2 * t^2
- x4 * y2 * t
+ 2 * x4 * y2 * t^2
+ x2 * y3
- 3 * x2 * y3 * t
+ 3 * x2 * y3 * t^2
- x4 * y3 * t^2
Which, since we're programming, is a ton of cacheable values. Taking:
a = x3 * y2
b = x4 * y2
c = x2 * y3
d = x4 * y3
we can simplify C(t)
as:
1/18 * C(t) = -a + 3at - 3at^2 - bt + 2bt^2 + c - 3ct + 3ct^2 - dt^2
= -3at^2 + 2bt^2 + 3ct^2 - dt^2 + 3at - bt - 3ct - a + c
= (-3a + 2b + 3c - d)t^2 + (3a - b - 3c)t + (c - a)
Putting that factor 18 back in, this is just a simple quadratic formula, for which we can find the roots using the quadratic root identity with more simple values:
v1 = (-3a + 2b + 3c - d) * 18
v2 = (3a - b - 3c) * 18
v3 = (c - a) * 18
And, provided 3a +d
is not equal to 2b+3c
(because there are no roots if that is the case), we get:
sqr = sqrt(v2^2 - 4 * v1 * v3)
d = 2 * v1
root1 = (sqr - v2) / d
root2 = -(sqr + v2) / d
Throw away roots that don't fall in the Bezier interval [0,1], and what you're left with are the t
values for which your original curve inflects.
It's just that I am not able to put things together in a way so that I end up having some nice pseudo-code. That is also why I gave some points and coordinates. I'd like to see someone calculating this with real numbers
Especially on Stackoverflow, it's better not to be lazy up front and instead commit to probably having to learn a new thing.