Creating cubic and/or quadratic Bezier curves to fit a path - algorithm

I am working on a 2D game in Lua.
I have a path which is made up of a number of points, for example, points A to L:
I want to move an object smoothly along this path. To accomplish this, I wanted to create quadratic or cubic Bezier curves based on the points, and interpolate these curves. However, how do I fit the curves properly, such that the path isn't broken if eg. a curve stops and another starts in point F?

Curve fitting is explained in this question: How can I fit a Bézier curve to a set of data?
However, there is a must easier method to interpolate the points. It goes this way:
Refine: Place a point in the middle of each edge.
Dual: Place a point in the middle of each edge and remove the old points.
Repeat the Dual step several times.
Repeat from step one until the curve is smooth enough.
You can easily see that it works on paper. The resulting curve begins to approximate a series of bezier splines.
It is described more formally in this PDF file (Section 3): http://www.cc.gatech.edu/~jarek/courses/handouts/curves.pdf
Here is some Javascript code to do it:
function bspline_smooth(points, order) {
// insert a point in the middle of each edge.
function _refine(points) {
var i, index, len, point, refined;
points = [points[0]].concat(points).concat(points[points.length-1]);
refined = [];
index = 0;
for (i = 0, len = points.length; i < len; i++) {
point = points[i];
refined[index * 2] = point;
if (points[index + 1]) {
refined[index * 2 + 1] = _mid(point, points[index + 1]);
}
index += 1;
}
return refined;
}
// insert point in the middle of each edge and remove the old points.
function _dual(points) {
var dualed, i, index, len, point;
dualed = [];
index = 0;
for (i = 0, len = points.length; i < len; i++) {
point = points[i];
if (points[index + 1]) {
dualed[index] = _mid(point, points[index + 1]);
}
index += 1;
}
return dualed;
}
function _mid(a, b) {
return new Point(
a.x + ((b.x - a.x) / 2),
a.y + ((b.y - a.y) / 2) );
}
if (!order) {
return points;
}
return bspline_smooth(_dual(_dual(_refine(points))), order - 1);
}

You don't need to know math to do this.
Connect each point with the next point with a separate Bezier curve.
Bezier curves have "handles" attached to their end points. These handles are tangent to the curve (at the end points). To make a "smooth" path all you need to do is make the handles of every two adjacent Bezier curves "sit" on one line.
To understand this, experiment with a drawing program like GIMP (but probably almost any other software will do). Such programs even have a special key to make the "handles" of adjacent curves sit on a straight line (and be of the same length).
The only "hard" mathematical decision you'll have to make is to determine the lengths of these handles. Experiment. You might want to make it dependent on how far each 3 consecutive points deviate from being on a straight line, or on the distance between the points.
Lastly, as for moving a point (your "object") along the curve at a seemingly constant speed: You can use an adaptation of a method like this one.

Related

Winding Number Algorithm Not Yielding Expected Result

So I've implemented a very unoptimized version of the Winding Number and Crossing Number algorithms found at http://geomalgorithms.com/a03-_inclusion.html, but I've come across a case where the Winding Number algorithm fails to yield the expected result.
I've created a polygon and three points as graphed here. For P0 and P2 both algorithms behave predictably. However, for point P1 (a point contained by a "null space" within the polygon's bounds) the crossing number algorithm succeeds while the winding number algorithm fails to recognize the point as not being contained by the polygon.
This is the algorithm as implemented:
int wn_PnPoly(Vector2 P, Vector2[] V)
{
int n = V.Length - 1;
int wn = 0; // the winding number counter
// loop through all edges of the polygon
for (int i = 0; i < n; i++)
{ // edge from V[i] to V[i+1]
if (V[i].y <= P.y)
{ // start y <= P.y
if (V[i + 1].y > P.y) // an upward crossing
if (isLeft(V[i], V[i + 1], P) > 0) // P left of edge
++wn; // have a valid up intersect
}
else
{ // start y > P.y (no test needed)
if (V[i + 1].y <= P.y) // a downward crossing
if (isLeft(V[i], V[i + 1], P) < 0) // P right of edge
--wn; // have a valid down intersect
}
}
return wn;
}
float isLeft(Vector2 P0, Vector2 P1, Vector2 P2)
{
return ((P1.x - P0.x) * (P2.y - P0.y)
- (P2.x - P0.x) * (P1.y - P0.y));
}
Am I missing something obvious here? Why does the crossing number algorithm succeed in this case while winding number fails?
These two methods are not the same criterion.
In the non-zero or winding rule, the directions of the line segments matter. The algorithm checks whether a line segment crosses the outgoing ray from the left or from the right and counts how often each case occurs. The point is considered ouside only if
The crossing-number or even-odd rule just counts how often a line is crossed. Every time you cross a line you go either from inside to outside or vice versa, so an even number of crossings means the point is outside.
If you go right from P1 in your example, you cross two lines, so the even-odd rule tells you P1 isn't in the polygon. But these two lines have the same orientation: If your overall shape is drawn in a clockwise fashion, both lines are drawn from top to bottom. In the words of the article you linked, the polygon winds around P1 twice. According to the winding rule, P1 is part of your polygon. Your program shows the correct behaviour.
Vector graphics programs and formats distinguish between these two rules. For example SVG has a fill-rule attribute, where you can set the behaviour. Postscript has fill and eofill operators for filling paths with the winding rule and even-odd rule respectively. The default is usually the winding rule, so that designers must take care to orient their paths correctly.

Should point on the edge of polygon be inside polygon?

Recently I've faced with one little but majour problem: is point on the edge of polygon be inside polygon?
What I mean - currently I am trying to implement 2D geometry library in JS for custom needs and there is method, lets say polygon.contains(point).
So my question is - when point is situated on one of the polygon's edges - as result the point is inside or outside of the polygon? Additional question for vertices: if point is right on top of polygon's vertex - is it inside or outside?
Algo that I've used is taken from here and looks like:
int pnpoly(int nvert, float *vertx, float *verty, float testx, float testy)
{
int i, j, c = 0;
for (i = 0, j = nvert-1; i < nvert; j = i++) {
if ( ((verty[i]>testy) != (verty[j]>testy)) &&
(testx < (vertx[j]-vertx[i]) * (testy-verty[i]) / (verty[j] - verty[i]) + vertx[i]) )
c = !c;
}
return c;
}
Also, there is a quote from the site:
PNPOLY partitions the plane into points inside the polygon and points outside the polygon. Points that are on the boundary are classified as either inside or outside.
And it is total true, in some situations it returns TRUE, and in some FALSE.
Actually, this is not what I'm needed. So my question is beginning to expand - which behaviour is correct when point is on the edge of polygon - is it inside or outside. And do you have a better algo with predictable behaviour?
UPDATE:
Okay, I'm found another algo, which is called "winding number" and to test this I'm using only polygons and points with integer values:
function isLeft(p0, p1, p2){
return ( Math.round((p1.x - p0.x) * (p2.y - p0.y)) - Math.round((p2.x - p0.x) * (p1.y - p0.y)) );
}
function polygonContainsPoint(polygon, point){
var i, j, pointi, pointj;
var count = 0;
var vertices = polygon.vertices;
var length = vertices.length;
for (i = 0; i < length; i++){
j = (i + 1) % length;
pointi = vertices[i];
pointj = vertices[j];
if (pointi.y > point.y){
if (pointj.y <= point.y && (isLeft(pointi, pointj, point) < 0)){
--count;
}
} else if (pointj.y > point.y && (isLeft(pointi, pointj, point) > 0)){
++count;
}
}
return 0 !== count;
}
As you can see there is no division; multiplication is wrapped into round() method, so there is no way for floating point errors. Anyway, I'm getting same result as with even-odd algo.
And I think I started to see the "pattern" in this strange behaviour: the left and top edges may tell that point is inside of polygon, but when you're tryed to put point on one of the right or bottom edges - it may return false.
This is not good for me. Maybe some of you know some algo with more predictable behaviour?
The simple answer to the question is that a point on the edge is neither inside, nor outside of polygon, it is on the boundary of the polygon - the third option. But typically it does not matter (as supported by your quotations), and what matters is to avoid the errors in classification of points, which are really deep inside or outside.
Point-in-polygon problem is sometimes called the Parity Algorithm:
Let r be the horizontal half-line whose left endpoint is the test point.
Count the number of intersections between r and the edges of the polygon. If that number is odd, then the test point lies within the polygon, and if the number is even, then it lies outside the polygon.
Here are some examples:
(a), (b) - not-degenerate cases, where half-line either does not intersect the edge or crosses it.
(c), (d), (e), (f) - four degenerate cases that have to be taken into account.
A correct answer is obtained if cases (c) and (e) are counted as one crossing and cases (d) and (f) are not counted at all. If we write the code for the above algorithm, we realize that a substantial amount of the efforts is required to cover the four degenerated cases.
With more complex algorithms and especially in 3D, the amount of efforts to support degenerate cases increases dramatically.
The above problem and explanation appear in the introduction to Simulation of Simplicity article by Herbert Edelsbrunner and Ernst Peter Mucke. And proposed solution is to get rid of all degenerations by virtual minimal perturbation of input points. After that a tested point will never be on the edge, but only inside or outside of polygon.

Nearest neighbor using polar coordinates

Is there any method to order a set of (x,y) points with respect the origin depending on both distance and angle?
In my problem I have a set of points in the plane, all spread around the center (0,0). These represent some data feature, and nearest points represent similar data.
Then I need to order them and so I itarate until all points are considered, taking the NN with respect to the origin (0,0) considering just the distance between points.
These points are very closer, and although similar data are closer one each other, some different data are represented far from each other but in the same circumference around the origin (this means that they have the same distance with respect the center), thus often I take points at almost the same distance (the same radius) but that lie in the opposite sides of the (imaginary) circle around the center.
I want to use the polar coordiantes to order these points. So I want to consider first the radial distance, and then the angle. I can compute these tho measures for all the points, but I don't know how to obtain an unique index to order them.
This is the code I use: in avPositions I have a set of pre-computed (x,y) positions of a grid, for each available position I have to select the nearest point in DATA.
for(var i=0;i<avPositions.length; i++)
{
res = posed.indexOf(false);
if(res<0) break;
x = avPositions[i][0];
y = avPositions[i][1];
dist = null;
dist = DATA.map(function(obj) { x1 = obj[0];
y1 = obj[1];
S = (x-x1)*(x-x1) + (y-y1)*(y-y1);
return Math.sqrt(S);
});
nearestIndex = dist.indexOf(Math.min.apply(Math, dist));
while(posed[nearestIndex])
{
dist[nearestIndex] = 99999999;
nearestIndex = dist.indexOf(Math.min.apply(Math, dist));
}
if(nearestIndex < 0) break;
posed[nearestIndex] = true;
DATAgrid[nearestIndex][0] = x;
DATAgrid[nearestIndex][1] = y;
}

How to convert an image to a Box2D polygon using the alpha layer and triangulation?

I'm coding a game using Box2D and SFML, and I'd like to let my users import their own textures to use as physics polygons. The polygons are created using the images' alpha layer. It doesn't need to be pixel perfect, and this is where my problem is. If it's pixel-perfect, it's going to be way too buggy when the player gets stuck between two rather complex shapes. I have a working edge-detection algorithm, and it produces something like this. It's pixel per pixel (and the shape it's tracing is simply a square with an dip). After that, I have a simplifying algorithm that produces this. It works fine to me, but if every single corner is traced like that, I'm going to have some problems. The code for the vector-simplifying is this:
//borders is a std::vector containing simple Box2D b2Vec2 (2D vector class containing an x and a y)
//vector shortener
for(unsigned int i = 0; i < borders.size(); i++)
{
int x = 0, y = 0;
int counter = 0;
//get the values for x and y that need to be added to check whether in a line or not
x = borders[i].x - borders[i-1].x;
y = borders[i].y - borders[i-1].y;
//while points are aligned..
while((borders[i].x + x*counter == borders[i + counter].x) && (borders[i].y + y*counter == borders[i+counter].y))
{
counter++;
}
if(counter-1 > i)
{
borders.erase(borders.begin() + i, borders.begin() + i + counter -1);
}
}
So my question is, how can I transform the previous set of vectors into something a bit less precise? Are there any rounding algorithms out there? If so, which is best? Any tips you can give me? It doesn't matter whether the resulting polygon is convex or concave, I'm triangulating it anyways.
Thanks,
AsterAlff

An algorithm for inflating/deflating (offsetting, buffering) polygons

How would I "inflate" a polygon? That is, I want to do something similar to this:
The requirement is that the new (inflated) polygon's edges/points are all at the same constant distance from the old (original) polygon's (on the example picture they are not, since then it would have to use arcs for inflated vertices, but let's forget about that for now ;) ).
The mathematical term for what I'm looking for is actually inward/outward polygon offseting. +1 to balint for pointing this out. The alternative naming is polygon buffering.
Results of my search:
Here are some links:
A Survey of Polygon Offseting Strategies
Polygon offset, PROBLEM
Buffering Polygon Data
I thought I might briefly mention my own polygon clipping and offsetting library - Clipper.
While Clipper is primarily designed for polygon clipping operations, it does polygon offsetting too. The library is open source freeware written in Delphi, C++ and C#. It has a very unencumbered Boost license allowing it to be used in both freeware and commercial applications without charge.
Polygon offsetting can be performed using one of three offset styles - squared, round and mitered.
August 2022:
Clipper2 has now been formally released and it supercedes Clipper (aka Clipper1).
The polygon you are looking for is called inward/outward offset polygon in computational geometry and it is closely related to the straight skeleton.
These are several offset polygons for a complicated polygon:
And this is the straight skeleton for another polygon:
As pointed out in other comments, as well, depending on how far you plan to "inflate/deflate" your polygon you can end up with different connectivity for the output.
From computation point of view: once you have the straight skeleton one should be able to construct the offset polygons relatively easily. The open source and (free for non-commercial) CGAL library has a package implementing these structures. See this code example to compute offset polygons using CGAL.
The package manual should give you a good starting point on how to construct these structures even if you are not going to use CGAL, and contains references to the papers with the mathematical definitions and properties:
CGAL manual: 2D Straight Skeleton and Polygon Offsetting
For these types of things I usually use JTS. For demonstration purposes I created this jsFiddle that uses JSTS (JavaScript port of JTS). You just need to convert the coordinates you have to JSTS coordinates:
function vectorCoordinates2JTS (polygon) {
var coordinates = [];
for (var i = 0; i < polygon.length; i++) {
coordinates.push(new jsts.geom.Coordinate(polygon[i].x, polygon[i].y));
}
return coordinates;
}
The result is something like this:
Additional info: I usually use this type of inflating/deflating (a little modified for my purposes) for setting boundaries with radius on polygons that are drawn on a map (with Leaflet or Google maps). You just convert (lat,lng) pairs to JSTS coordinates and everything else is the same. Example:
Sounds to me like what you want is:
Starting at a vertex, face anti-clockwise along an adjacent edge.
Replace the edge with a new, parallel edge placed at distance d to the "left" of the old one.
Repeat for all edges.
Find the intersections of the new edges to get the new vertices.
Detect if you've become a crossed polygon and decide what to do about it. Probably add a new vertex at the crossing-point and get rid of some old ones. I'm not sure whether there's a better way to detect this than just to compare every pair of non-adjacent edges to see if their intersection lies between both pairs of vertices.
The resulting polygon lies at the required distance from the old polygon "far enough" from the vertices. Near a vertex, the set of points at distance d from the old polygon is, as you say, not a polygon, so the requirement as stated cannot be fulfilled.
I don't know if this algorithm has a name, example code on the web, or a fiendish optimisation, but I think it describes what you want.
In the GIS world one uses negative buffering for this task:
http://www-users.cs.umn.edu/~npramod/enc_pdf.pdf
The JTS library should do this for you. See the documentation for the buffer operation: http://tsusiatsoftware.net/jts/javadoc/com/vividsolutions/jts/operation/buffer/package-summary.html
For a rough overview see also the Developer Guide:
http://www.vividsolutions.com/jts/bin/JTS%20Developer%20Guide.pdf
Each line should split the plane to "inside" and "outline"; you can find this out using the usual inner-product method.
Move all lines outward by some distance.
Consider all pair of neighbor lines (lines, not line segment), find the intersection. These are the new vertex.
Cleanup the new vertex by removing any intersecting parts. -- we have a few case here
(a) Case 1:
0--7 4--3
| | | |
| 6--5 |
| |
1--------2
if you expend it by one, you got this:
0----a----3
| | |
| | |
| b |
| |
| |
1---------2
7 and 4 overlap.. if you see this, you remove this point and all points in between.
(b) case 2
0--7 4--3
| | | |
| 6--5 |
| |
1--------2
if you expend it by two, you got this:
0----47----3
| || |
| || |
| || |
| 56 |
| |
| |
| |
1----------2
to resolve this, for each segment of line, you have to check if it overlap with latter segments.
(c) case 3
4--3
0--X9 | |
| 78 | |
| 6--5 |
| |
1--------2
expend by 1. this is a more general case for case 1.
(d) case 4
same as case3, but expend by two.
Actually, if you can handle case 4. All other cases are just special case of it with some line or vertex overlapping.
To do case 4, you keep a stack of vertex.. you push when you find lines overlapping with latter line, pop it when you get the latter line. -- just like what you do in convex-hull.
Here is an alternative solution, see if you like this better.
Do a triangulation, it don't have to be delaunay -- any triangulation would do.
Inflate each triangle -- this should be trivial. if you store the triangle in the anti-clockwise order, just move the lines to right-hand-side and do intersection.
Merge them using a modified Weiler-Atherton clipping algorithm
Big thanks to Angus Johnson for his clipper library.
There are good code samples for doing the clipping stuff at the clipper homepage at http://www.angusj.com/delphi/clipper.php#code
but I did not see an example for polygon offsetting. So I thought that maybe it is of use for someone if I post my code:
public static List<Point> GetOffsetPolygon(List<Point> originalPath, double offset)
{
List<Point> resultOffsetPath = new List<Point>();
List<ClipperLib.IntPoint> polygon = new List<ClipperLib.IntPoint>();
foreach (var point in originalPath)
{
polygon.Add(new ClipperLib.IntPoint(point.X, point.Y));
}
ClipperLib.ClipperOffset co = new ClipperLib.ClipperOffset();
co.AddPath(polygon, ClipperLib.JoinType.jtRound, ClipperLib.EndType.etClosedPolygon);
List<List<ClipperLib.IntPoint>> solution = new List<List<ClipperLib.IntPoint>>();
co.Execute(ref solution, offset);
foreach (var offsetPath in solution)
{
foreach (var offsetPathPoint in offsetPath)
{
resultOffsetPath.Add(new Point(Convert.ToInt32(offsetPathPoint.X), Convert.ToInt32(offsetPathPoint.Y)));
}
}
return resultOffsetPath;
}
One further option is to use boost::polygon - the documentation is somewhat lacking, but you should find that the methods resize and bloat, and also the overloaded += operator, which actually implement buffering. So for example increasing the size of a polygon (or a set of polygons) by some value can be as simple as:
poly += 2; // buffer polygon by 2
Based on advice from #JoshO'Brian, it appears the rGeos package in the R language implements this algorithm. See rGeos::gBuffer .
I use simple geometry: Vectors and/or Trigonometry
At each corner find the mid vector, and mid angle. Mid vector is the arithmetic average of the two unit vectors defined by the edges of the corner. Mid Angle is the half of the angle defined by the edges.
If you need to expand (or contract) your polygon by the amount of d from each edge; you should go out (in) by the amount d/sin(midAngle) to get the new corner point.
Repeat this for all the corners
*** Be careful about your direction. Make CounterClockWise Test using the three points defining the corner; to find out which way is out, or in.
There are a couple of libraries one can use (Also usable for 3D data sets).
https://github.com/otherlab/openmesh
https://github.com/alecjacobson/nested_cages
http://homepage.tudelft.nl/h05k3/Projects/MeshThickeningProj.htm
One can also find corresponding publications for these libraries to understand the algorithms in more detail.
The last one has the least dependencies and is self-contained and can read in .obj files.
This is a C# implementation of the algorithm explained in here . It is using Unity math library and collection package as well.
public static NativeArray<float2> ExpandPoly(NativeArray<float2> oldPoints, float offset, int outer_ccw = 1)
{
int num_points = oldPoints.Length;
NativeArray<float2> newPoints = new NativeArray<float2>(num_points, Allocator.Temp);
for (int curr = 0; curr < num_points; curr++)
{
int prev = (curr + num_points - 1) % num_points;
int next = (curr + 1) % num_points;
float2 vn = oldPoints[next] - oldPoints[curr];
float2 vnn = math.normalize(vn);
float nnnX = vnn.y;
float nnnY = -vnn.x;
float2 vp = oldPoints[curr] - oldPoints[prev];
float2 vpn = math.normalize(vp);
float npnX = vpn.y * outer_ccw;
float npnY = -vpn.x * outer_ccw;
float bisX = (nnnX + npnX) * outer_ccw;
float bisY = (nnnY + npnY) * outer_ccw;
float2 bisn = math.normalize(new float2(bisX, bisY));
float bislen = offset / math.sqrt((1 + nnnX * npnX + nnnY * npnY) / 2);
newPoints[curr] = new float2(oldPoints[curr].x + bislen * bisn.x, oldPoints[curr].y + bislen * bisn.y);
}
return newPoints;
}
Thanks for the help in this topic, here's the code in C++ if anyone interested. Tested it, its working. If you give offset = -1.5, it will shrink the polygon.
typedef struct {
double x;
double y;
} Point2D;
double Hypot(double x, double y)
{
return std::sqrt(x * x + y * y);
}
Point2D NormalizeVector(const Point2D& p)
{
double h = Hypot(p.x, p.y);
if (h < 0.0001)
return Point2D({ 0.0, 0.0 });
double inverseHypot = 1 / h;
return Point2D({ (double)p.x * inverseHypot, (double)p.y * inverseHypot });
}
void offsetPolygon(std::vector<Point2D>& polyCoords, std::vector<Point2D>& newPolyCoords, double offset, int outer_ccw)
{
if (offset == 0.0 || polyCoords.size() < 3)
return;
Point2D vnn, vpn, bisn;
double vnX, vnY, vpX, vpY;
double nnnX, nnnY;
double npnX, npnY;
double bisX, bisY, bisLen;
unsigned int nVerts = polyCoords.size() - 1;
for (unsigned int curr = 0; curr < polyCoords.size(); curr++)
{
int prev = (curr + nVerts - 1) % nVerts;
int next = (curr + 1) % nVerts;
vnX = polyCoords[next].x - polyCoords[curr].x;
vnY = polyCoords[next].y - polyCoords[curr].y;
vnn = NormalizeVector({ vnX, vnY });
nnnX = vnn.y;
nnnY = -vnn.x;
vpX = polyCoords[curr].x - polyCoords[prev].x;
vpY = polyCoords[curr].y - polyCoords[prev].y;
vpn = NormalizeVector({ vpX, vpY });
npnX = vpn.y * outer_ccw;
npnY = -vpn.x * outer_ccw;
bisX = (nnnX + npnX) * outer_ccw;
bisY = (nnnY + npnY) * outer_ccw;
bisn = NormalizeVector({ bisX, bisY });
bisLen = offset / std::sqrt((1 + nnnX * npnX + nnnY * npnY) / 2);
newPolyCoords.push_back({ polyCoords[curr].x + bisLen * bisn.x, polyCoords[curr].y + bisLen * bisn.y });
}
}
To inflate a polygon, one can implement the algorithm from "Polygon Offsetting by Computing Winding Numbers" article.
The steps of the algorithm are as follows:
Construct outer offset curve by taking every edge from input polygon and shifting it outside, then connecting shifted edged with circular arches in convex vertices of input polygon and two line segments in concave vertices of input polygon.
An example. Here input polygon is dashed blue, and red on the left - shifted edges, on the right - after connecting them in continuous self-intersecting curve:
The curve divides the plane on a number of connected components, and one has to compute the winding number in each of them, then take the boundary of all connected components with positive winding numbers:
The article proofs that the algorithm is very fast compared to competitors and robust at the same time.
To avoid implementing winding number computation, one can pass self-intersecting offset curve to OpenGL Utility library (GLU) tessellator and activate the settings GLU_TESS_BOUNDARY_ONLY=GL_TRUE (to skip triangulation) and GLU_TESS_WINDING_RULE=GLU_TESS_WINDING_POSITIVE (to output the boundary of positive winding number components).

Resources