Recently, I needed a robust way to calculate the curvature for a cubic bezier curve. Now there are plenty of guides on how to do this, but when I implemented the usual methods I got some weird results.
Here is, I think we can agree, what seems like a straight line, with the cubic control points marked. When we calculate the curvature however we get something a bit weird:
At first I thought I had implemented this wrong. (It's correct.) Then I started to wonder if maybe the "curvature of a line" is a silly notion and I should special-case it in my code. That's when I found this:
The meaning of these charts are that the curvature of the cubic is strongest at the origin. But if you imagine yourself traveling along the cubic like an arc, it doesn't "feel" like you're turning sharply at the origin. It "feels" like you're turning sharply about 3/4 of the way along. And indeed, the curvature graph has that too as a local maxima; it's just dwarfed by the curvature at the very beginning.
It turns out that the curvature formula has a very nasty discontinuity. To see this, let's look at the formula:
It's clear something bad can happen if the denominator gets very small. Now we can expand this out in control point notation easily enough, but it's a nasty denominator that doesn't really have an obvious meaning to me. So, let's convert to another notation recommended by a friend instead:
In this notation a cubic is parameterized with
What do we gain with this notation? One advantage is that at $t=0$, the curvature equation has an "easy" solution:
Therefore, the curvature will be infinite with
So, make sure your control points and endpoints aren't the same and they won't have zero distance and you don't divide by zero. Not so tough, right?
Turns out it's a little more complicated than that. Let's consider what happens when
Here, we might expect that
It's true as long as you check at $t>0.2$ or so. But if you judge the lines by their greatest curvature, it's at the endpoints.
The problem is not really the undefined behavior when
I am a bit surprised that I can't google up some prior art on how to deal with this. I did find one stackexchange answer suggesting such curves are not "regular", but "this is unlikely to happen", and googling that terminology didn't help.
I did notice just playing around that I can make a "similar" cubic with larger $r_{0}$ that has nice curvature:
This suggests that I can pick a new control point for a larger $r_{0},r_{1}$ and get a nice curvature that way.
There is some prior art on this general topic. For example, I found this paper which says
A reason for one to get undesired shapes is unsuitable magnitudes of the given tangent vectors. Usually, the larger the magnitudes of the tangent vectors, the more likely the occurrence of a loop in the resulting curve. On the other hand, the smaller the magnitudes of the tangent vectors, the closer the resulting curve to the base line segment. Therefore, the problem is how to choose suitable magnitudes for the endpoint tangent vectors.
That sounds very promising. Unfortunately, their solution is not overly interested in a nice curvature and it also involves assembling multiple cubics into a megazord cubic ("composite optimized geometric Hermite cubic" for short) and then using that, which is not ideal for my case.
Instead, I noticed that if we take the curvature equation from earlier, and let
The idea here is that we have a line that is "nearly straight" (curved a little by $err$), and then we hold the resulting curvature below some $k$:
Now we just pick an $err$ that seems straight (a few degrees), a $k$ that seems small ($0.02$), and crank out an
Here we see that I wanted
If you want the "smallest possible" $k$ to address this problem, the practical limit of this expression in
So the analytical limit looks indeterminate. Still, the
However,
An implementation of this function appears in blitcurve, my general-purpose bezier geometry library.
So if this how we get the $r_{0},$ what do we do with it? In the case of the line it is pretty easy; any control point along the line will produce "the same" line, so we just pick control points along the line with a new $r_{0}$ and we're done.
In general, however, a cubic with moved control points will be a different path. This situation was not important to my immediate problem, so I did not look into it extensively. However, I believe it can be done by reparameterizing to control-point notation and solving for the missing points.