I have a library that draws regular bezier path figures (complex paths formed of a lot of bezier points), using midpoint approximation.
I can draw them without problem, but I need to add support for advanced geometry operations: Nearest point of a curve, intersection, figure contains point, and more importantly, path combinations: difference, intersection, exclusive-or, union, ...
Is there any good source to get all this?
Thanks
I've had to implement some of those operations on curves or closed paths. It mostly boils down to line and polygon operations. A few useful concepts:
The control points form a convex hull around the Bezier path, which is useful to short circuit intersection-related operations.
Your curve subdivision should be adaptive, stopping when the next subdivision won't be a significant difference, which means each "half" may divide to a different depth.
You can subdivide a curve at any point, not just the midpoint, which is useful for creating a Bezier subcurve ending at a found interestion point.
Example code for arbitrary subdivision:
static Point2D.Double[][] splitBezier(Point2D.Double[] p) {
return splitBezier(p, 0.5);
}
static Point2D.Double[][] splitBezier(Point2D.Double[] p, double t) {
Point2D.Double[][] parts = new Point2D.Double[2][4];
Point2D.Double ab = interpolate(t, p[0], p[1]);
Point2D.Double bc = interpolate(t, p[1], p[2]);
Point2D.Double cd = interpolate(t, p[2], p[3]);
Point2D.Double abc = interpolate(t, ab, bc);
Point2D.Double bcd = interpolate(t, bc, cd);
Point2D.Double abcd = interpolate(t, abc, bcd);
parts[0][0] = p[0];
parts[0][1] = ab;
parts[0][2] = abc;
parts[0][3] = abcd;
parts[1][0] = abcd;
parts[1][2] = bcd;
parts[1][2] = cd;
parts[1][3] = p[3];
return parts;
}
static Point2D.Double interpolate(double t, Point2D.Double a, Point2D.Double b) {
return new Point2D.Double((1 - t) * a.getX() + t * b.getX(),
(1 - t) * a.getY() + t * b.getY());
}
Some useful sites:
Details with applets
Discussion of more advanced intersection methods (PDF)
Related
I am currently developing a grid for a simple simulation and I have been tasked with interpolating some values tied to vertices of a triangle.
So far I have this:
let val1 = 10f
let val2 = 15f
let val3 = 12f
let point1 = Vector2(100f, 300f), val1
let point2 = Vector2(300f, 102f), val2
let point3 = Vector2(100f, 100f), val3
let points = [point1; point2; point3]
let find (points : (Vector2*float32) list) (pos : Vector2) =
let (minX, minXv) = points |> List.minBy (fun (v, valu) -> v.X)
let (maxX, maxXv) = points |> List.maxBy (fun (v, valu)-> v.X)
let (minY, minYv) = points |> List.minBy (fun (v, valu) -> v.Y)
let (maxY, maxYv) = points |> List.maxBy (fun (v, valu) -> v.Y)
let xy = (pos - minX)/(maxX - minX)*(maxX - minX)
let dx = ((maxXv - minXv)/(maxX.X - minX.X))
let dy = ((maxYv - minYv)/(maxY.Y - minY.Y))
((dx*xy.X + dy*xy.Y)) + minXv
Where you get a list of points forming a triangle. I find the minimum X and Y and the max X and Y with the corresponding values tied to them.
The problem is this approach only works with a right sided triangle. With an equilateral triangle the mid point will end up having a higher value at its vertex than the value that is set.
So I guess the approach is here to essentially project a right sided triangle and create some sort of transformation matrix between any triangle and this projected triangle?
Is this correct? If not, then any pointers would be most appreciated!
You probably want a linear interpolation where the interpolated value is the result of a function of the form
f(x, y) = a*x + b*y + c
If you consider this in 3d, with (x,y) a position on the ground and f(x,y) the height above it, this formula will give you a plane.
To obtain the parameters you can use the points you have:
f(x1, y1) = x1*a + y1*b * 1*c = v1 ⎛x1 y1 1⎞ ⎛a⎞ ⎛v1⎞
f(x2, y2) = x2*a + y2*b * 1*c = v2 ⎜x2 y2 1⎟ * ⎜b⎟ = ⎜v2⎟
f(x3, y3) = x3*a + y3*b * 1*c = v3 ⎝x3 y3 1⎠ ⎝c⎠ ⎝v3⎠
This is a 3×3 system of linear equations: three equations in three unknowns.
You can solve this in a number of ways, e.g. using Gaussian elimination, the inverse matrix, Cramer's rule or some linear algebra library. A numerics expert may tell you that there are differences in the numeric stability between these approaches, particularly if the corners of the triangle are close to lying on a single line. But as long as you're sufficiently far away from that degenerate situation, it probably doesn't make a huge practical difference for simple use cases. Note that if you want to interpolate values for multiple positions relative to a single triangle, you'd only compute a,b,c once and then just use the simple linear formula for each input position, which might lead to a considerable speed-up.
Advanced info: For some applications, linear interpolation is not good enough, but to find something more appropriate you would need to provide more data than your question suggests is available. One example that comes to my mind is triangle meshes for 3d rendering. If you use linear interpolation to map the triangles to texture coordinates, then they will line up along the edges but the direction of the mapping can change abruptly, leading to noticeable seams. A kind of projective interpolation or weighted interpolation can avoid this, as I learned from a paper on conformal equivalence of triangle meshes (Springborn, Schröder, Pinkall, 2008), but for that you need to know how the triangle in world coordinates maps to the triangle in texture coordinates, and your also need the triangle mesh and the correspondence to the texture to be compatible with this mapping. Then you'd map in such a way that you not only transport corners to corners, but also circumcircle to circumcircle.
I have a shape that moves along an open path and I'm wondering how could I get it's relative position, i.e. given the (x,y) coordinate of the shape and the length (L) of the path (p), the function should return the ratio (r) where the point is located.
For example, r = 0 would mean the point is at the very beginning of the path, 0.25 at the quarter, 0.5 half, etc.
One way I could do it would be to iterate over the function p.getPointAtLength() for different length increments until the given position is within an acceptable range from my original point (x, y). And then simply divide this value by p.getTotalLength().
But is there a more efficient and precise way to do it knowing that in my case the path is a 4 point bézier curve ?
Short answer: no.
Reparameterising a Bezier curve for distance, rather than time, is a non-trivial problem without a general solution, so by far the most efficient way to do this for "moving along a path on a computer screen" is a LUT-based approach like http://pomax.github.io/bezierinfo/#tracing
For those who might be interested, here's how I worked it around :
getLengthAtPoint = function(x, y, curve){
var len = curve.getTotalLength(),
tol = len/100,
inc = len/100,
err,
errMin = 99999, // infinity
cond = true,
lengthIter = 0,
lengthMin = 0;
do {
err = dist(curve.getPointAtLength(lengthIter), {x, y}); //pythagore
cond = err>tol;
if (err < errMin){
errMin = err;
lengthMin = lengthIter;
}
lengthIter = lengthIter + inc;
if (lengthIter > len) {cond = false;}
} while (cond);
return (lengthMin/len);
}
I searched around and it turns out the answer to this is surprising hard to find. Theres algorithm out there that can generate a random orientation in quaternion form but they involve sqrt and trig functions. I dont really need a uniformly distributed orientation. I just need to generate (many) quaternions such that their randomness in orientation is "good enough." I cant specify what is "good enough" except that I need to be able to do the generation quickly.
Quoted from http://planning.cs.uiuc.edu/node198.html:
Choose three points u, v, w ∈ [0,1] uniformly at random. A uniform, random quaternion is given by the simple expression:
h = ( sqrt(1-u) sin(2πv), sqrt(1-u) cos(2πv), sqrt(u) sin(2πw), sqrt(u) cos(2πw))
From Choosing a Point from the Surface of a Sphere by George Marsaglia:
Generate independent x, y uniformly in (-1..1) until z = x²+y² < 1.
Generate independent u, v uniformly in (-1..1) until w = u²+v² < 1.
Compute s = √((1-z) / w).
Return the quaternion (x, y, su, sv). It's already normalized.
This will generate a uniform random rotation because 4D spheres, unit quaternions and 3D rotations have equivalent measures.
The algorithm uses one square root, one division, and 16/π ≈ 5.09 random numbers on average. C++ code:
Quaternion random_quaternion() {
double x,y,z, u,v,w, s;
do { x = random(-1,1); y = random(-1,1); z = x*x + y*y; } while (z > 1);
do { u = random(-1,1); v = random(-1,1); w = u*u + v*v; } while (w > 1);
s = sqrt((1-z) / w);
return Quaternion(x, y, s*u, s*v);
}
Simplest way to generate it, just generate 4 random float and normalize it if required. If you want to produce rotation matrices later , than normalization can be skipped and convertion procedure should note nonunit quaternions.
I know there are various posts on stackoverflow about given two lines defined by two points each, being able to calculate the intersection, but typically those solutions extend the lines as opposed to treating their physical sizes.
Background search: The typical function used to calculate the intersection of two lines as defined by points is shown below, but credit goes to here.
// Finds the intersection of two lines, or returns false.
// The lines are defined by (o1, p1) and (o2, p2).
bool intersection(Point2f o1, Point2f p1, Point2f o2, Point2f p2,
Point2f &r)
{
Point2f x = o2 - o1;
Point2f d1 = p1 - o1;
Point2f d2 = p2 - o2;
float cross = d1.x*d2.y - d1.y*d2.x;
if (abs(cross) < /*EPS*/1e-8)
return false;
double t1 = (x.x * d2.y - x.y * d2.x)/cross;
r = o1 + d1 * t1;
return true;
}
Problem: While this code works phenomenally, I am wondering if anyone knows how to alter this function where if the lines as defined by the points do not physically intersect. Currently, the lines ultimately get extended.
ex:
o1 = 0,0
p1 = 10,0
o2 = 5,5
p2 = 5,-5
Naturally, this would intersect at 5,0.
ex:
o1 = 0,0
p1 = 10,0
o2 = 5,5
p2 = 5,2
While the function also says this intersection is at 5,0, lines defined by the points however do not physically intersect, so in this case I'd want the function to return false.
I've considered modifying this function to at the end, check if r is between the two points for each line and came across the suggested formula of here.. I'm wondering if there might be a better or easier solution?
I have the last two CGPoints from a Array which contains points of line drawn by the user . i need to extend the line upto a fixed distance at the same angle. so i first calculate the angle between the last two points with the help of following code
-(CGFloat)angleBetweenFirstPoint:(CGPoint)firstPoint ToSecondPoint:(CGPoint)secondPoint
{
CGPoint diff = ccpSub(secondPoint, firstPoint);
NSLog(#"difference point %f , %f",diff.x,diff.y);
CGFloat res = atan2(diff.y, diff.x);
/*if ( res < 0 )
{
res = (0.5 * M_PI) + res;
}
if ( dx<0 && dy>0 ) { // 2nd quadrant
res += 0.5 * M_PI;
} else if ( dx<0 && dy<0 ) { // 3rd quadrant
res += M_PI;
} else if ( dx>0 && dy<0 ) { // 4th quadrant
res += M_PI + (0.5 * M_PI);
}*/
//res=res*180/M_PI;
res = CC_RADIANS_TO_DEGREES(res);
return res;
}
After calculating the angle i find the extend point with the help of following maths
-(void)extendLine
{
lineAngle = [self angleBetweenFirstPoint:pointD ToSecondPoint:endPt];
extendEndPt.x = endPt.x - cos(lineAngle) * 200;
extendEndPt.y = endPt.y - sin(lineAngle) * 200;
// draw line unto extended point
}
But the point i am getting is not right to draw the extended line at the same angle as the original line.
I think it is because i am not getting the right angle between those last points.. what am i possibly doing wrong?? Do i need to consider the whole quadrant system while considering the angle and how? and m working in landscape mode. does that make any difference??
Ye gods, you are doing this in a way that is WILDLY INCREDIBLY over-complicated.
Skip all of the crapola with angles. You don't need it. Period. Do it all with vectors, and very simple ones. First of all, I'll assume that you are given two points, P1 and P2. You wish to find a new point P3, that is a known distance (d) from P2, along the line that connects the two points.
All you need do is first, compute a vector that points along the line in question.
V = P2 - P1;
I've written it as if I am writing in MATLAB, but all this means is to subtract the x and y coordinates of the two points.
Next, scale the vector V to have unit length.
V = V/sqrt(V(1)^2 + V(2)^2);
Dividing the components of the vector V by the length (or 2-norm if you prefer) of that vector creates a vector with unit norm. That norm is just the square root of the sum of squares of the elements of V, so it is clearly the length of the vector.
Now it is simple to compute P3.
P3 = P2 + d*V;
P3 will lie at a distance of d units from P2, in the direction of the line away from point P1. Nothing sophisticated required. No angles computed. No worry about quadrants.
Learn to use vectors. They are your friends, or at the least, they can be if you let them.