1
0
mirror of https://github.com/Pomax/BezierInfo-2.git synced 2025-08-26 01:44:03 +02:00

Typos in curvefitting (#149)

This commit is contained in:
Steve Nicholson
2018-07-03 21:46:19 -07:00
committed by Pomax
parent 8165357a1c
commit a036c42cd2

View File

@@ -4,11 +4,11 @@ Given the previous section, one question you might have is "what if I don't want
And really this is just a variation on the question "how do I get the curve through these X points?", so let's look at that. Specifically, let's look at the answer: "curve fitting". This is in fact a rather rich field in geometry, applying to anything from data modelling to path abstraction to "drawing", so there's a fair number of ways to do curve fitting, but we'll look at one of the most common approaches: something called a [least squares](https://en.wikipedia.org/wiki/Least_squares) [polynomial regression](https://en.wikipedia.org/wiki/Polynomial_regression). In this approach, we look at the number of points we have in our data set, roughly determine what would be an appropriate order for a curve that would fit these points, and then tackle the question "given that we want an `nth` order curve, what are the coordinates we can find such that our curve is "off" by the least amount?".
Now, there are many ways to determine how "off" points are from the curve, which is where that "least squares" term comes in. The most common tool in the toolbox is to minimise the _squared distance_ between each point we have, and the corrsponding point on the curve we end up "inventing". A curve with a snug fit will have zero distance between those two, and a bad fit will have non-zero distances between every such pairs. It's a workable metric. You might wonder why we'd need to square, rather than just ensure that distance is a positive value (so that the total error is easy to compute by just summing distances) and the answer really is "because it tends to be a little better". There's lots of literature on the web if you want to deep dive the specific merits of least squared error metrics version least absolute error metrics, but those are <em>well</em> beyond the scope of this material.
Now, there are many ways to determine how "off" points are from the curve, which is where that "least squares" term comes in. The most common tool in the toolbox is to minimise the _squared distance_ between each point we have, and the corrsponding point on the curve we end up "inventing". A curve with a snug fit will have zero distance between those two, and a bad fit will have non-zero distances between every such pair. It's a workable metric. You might wonder why we'd need to square, rather than just ensure that distance is a positive value (so that the total error is easy to compute by just summing distances) and the answer really is "because it tends to be a little better". There's lots of literature on the web if you want to deep dive the specific merits of least squared error metrics versus least absolute error metrics, but those are <em>well</em> beyond the scope of this material.
So let's look at what we end up with in terms of curve fitting if we start with idea of performing least squares Bézier fitting. We're going to follow a procedure similar to the one described by Jim Herold over on his ["Least Squares Bézier Fit"](http://jimherold.com/2012/04/20/least-squares-bezier-fit/) article, and end with some nice interactive graphics for doing some curve fitting.
So let's look at what we end up with in terms of curve fitting if we start with the idea of performing least squares Bézier fitting. We're going to follow a procedure similar to the one described by Jim Herold over on his ["Least Squares Bézier Fit"](http://jimherold.com/2012/04/20/least-squares-bezier-fit/) article, and end with some nice interactive graphics for doing some curve fitting.
Before we begin, we're going to use the curve in matrix form. In the [section on matrices](#matrix), I mentioned that some things can are easier if we use the matrix representation of a Bézier curve rather than its calculus form, and this is one of those things.
Before we begin, we're going to use the curve in matrix form. In the [section on matrices](#matrix), I mentioned that some things are easier if we use the matrix representation of a Bézier curve rather than its calculus form, and this is one of those things.
As such, the first step in the process is expressing our Bézier curve as powers/coefficients/coordinate matrix **T x M x C**, by expanding the Bézier functions.
@@ -136,7 +136,7 @@ Next, we need to figure out appropriate `t` values for each point in the curve,
The first one is really simple: if we have `n` points, then we'll just assign each point `i` a `t` value of `(i-1)/(n-1)`. So if we have four points, the first point will have `t=(1-1)/(4-1)=0/3`, the second point will have `t=(2-1)/(4-1)=1/3`, the third point will have `t=2/3`, and the last point will be `t=1`. We're just straight up spacing the `t` values to match the number of points we have.
The second one is a little more intersting,: since we're doing polynomial regression, we might as well exploit the fact that our base coordinates just constitute a collection of line segments. At the first point, we're fixing t=0, and the last point, we want t=1, and anywhere in between we're simply going to say that `t` is equal to the distance along the polygon, scaled to the [0,1] domain.
The second one is a little more interesting: since we're doing polynomial regression, we might as well exploit the fact that our base coordinates just constitute a collection of line segments. At the first point, we're fixing t=0, and the last point, we want t=1, and anywhere in between we're simply going to say that `t` is equal to the distance along the polygon, scaled to the [0,1] domain.
To get these values, we first compute the general "distance along the polygon" matrix:
@@ -234,7 +234,7 @@ Now, given the above derivative, we can rearrange the terms (following the rules
Here, the "to the power negative one" is the notation for the [matrix inverse](https://en.wikipedia.org/wiki/Invertible_matrix). But that's all we have to do: we're done. Starting with **P** and inventing some `t` values based on the polygon the coordinates in **P** define, we can compute the corresponding Bézier coordinates **C** that specify a curve that goes through our points. Or, if it can't go through them exactly, as near as possible.
So before we try that out, how much code is involved in implementing this? Honestly, that answer depends on how much you're going to be writing yourself. If you already have a matrix maths library available, then really not that much code at all. On the other hand, if you writing this from scratch, you're going to have to write some utility functions for doing your matrix work for you, so it's really anywhere from 50 lines of code to maybe 200 lines of code. Not a bad price to pay for being able to fit curves to prespecified coordinates.
So before we try that out, how much code is involved in implementing this? Honestly, that answer depends on how much you're going to be writing yourself. If you already have a matrix maths library available, then really not that much code at all. On the other hand, if you are writing this from scratch, you're going to have to write some utility functions for doing your matrix work for you, so it's really anywhere from 50 lines of code to maybe 200 lines of code. Not a bad price to pay for being able to fit curves to prespecified coordinates.
So let's try it out! The following graphic lets you place points, and will start computing exact-fit curves once you've placed at least three. You can click for more points, and the code will simply try to compute an exact fit using a Bezier curve of the appropriate order. Four points? Cubic Bezier. Five points? Quartic. And so on. Of course, this does break down at some point: depending on where you place your points, it might become mighty hard for the fitter to find an exact fit, and things might actually start looking horribly off once you hit 10<sup>th</sup> or higher order curves. But it might not!