I would like to do a collision detection between circle and section of a circular ring. The circle is defined by it's position position and it's radius. The other object is defined by inner and outer radius and then a startPoint and endPoint both [x, y] points.
In the examples below, this is the circle and other is the ring section.
First I just check if it's colliding with the full ring. This works without a problem.
float mag = this.position.Magnitude();
if (mag < other.InnerRadius() - this.radius ||
mag > other.OuterRadius() + this.radius) {
return false;
}
But then I need to check if the circle is inside or outside of the section defined by the two points. Closest I was able to get was to check if it isn't colliding with the start and end vectors, but this returns wrong results when the circle is fully inside the ring section.
auto dot1 = Vector::Dot(position, other.StartPoint());
auto projected1 = dot1 / Vector::Dot(other.StartPoint(), other.StartPoint()) * other.StartPoint();
auto distance1 = Vector::Distance(position, projected1);
auto dot2 = Vector::Dot(position, other.EndPoint());
auto projected2 = dot2 / Vector::Dot(other.EndPoint(), other.EndPoint()) * other.EndPoint();
auto distance2 = Vector::Distance(position, projected2);
return distance1 < radius || distance2 < radius;
What is the easiest way to check if a circle is colliding with a object defined by these two vectors?
Edit: all the point objects I'm using here are my custom Vector class that has implemented all the vector operations.
Edit2: just to clarify, the ring object has it's origin in [0, 0]
Here is a simple algorithm.
First, let's agree on variable names:
Here r1 ≤ r2, -π/2 ≤ a1 ≤ a2 ≤ π/2.
(As I was reminded in comments, you have start and end points rather than angles, but I'm going to use angles as they seem more convenient. You can easily obtain angles from points via atan2(y-ry, x-rx), just make sure that a1 ≤ a2. Or you can rewrite the algorithm to not use angles at all.)
We need to consider 3 different cases. The case depends on where the circle center is located relative to the ring segment:
In the 1st case, as you already figured, collision occurs if length of vector (cx-rx, cy-ry) is greater than r1-rc and less than r2+rc.
In the 2nd case collision occurs if the distane between the circle center and the closest straight edge is less than rc.
In the 3rd case collision occurs if the distance between the circle center and the closest of 4 corners is less than rc.
Here's some pseudocode:
rpos = vec2(rx,ry); // Ring segment center coordinates
cpos = vec2(cx,cy); // Circle coordinates
a = atan2(cy-ry, cx-rx); // Relative angle
r = length(cpos - rpos); // Distance between centers
if (a > a1 && a < a2) // Case 1
{
does_collide = (r+rc > a1 && r-rc < a2);
}
else
{
// Ring segment corners:
p11 = vec2(cos(a1), sin(a1)) * r1;
p12 = vec2(cos(a1), sin(a1)) * r2;
p21 = vec2(cos(a2), sin(a2)) * r1;
p22 = vec2(cos(a2), sin(a2)) * r2;
if (((cpos-p11) · (p12-p11) > 0 && (cpos-p12) · (p11-p12) > 0) ||
((cpos-p21) · (p22-p21) > 0 && (cpos-p22) · (p21-p22) > 0)) // Case 2
{
// Normals of straight edges:
n1 = normalize(vec2(p12.y - p11.y, p11.x - p12.x));
n2 = normalize(vec2(p21.y - p22.y, p22.x - p21.x));
// Distances to edges:
d1 = n1 · (cpos - p11);
d2 = n2 · (cpos - p21);
does_collide = (min(d1, d2) < rc);
}
else // Case 3
{
// Squared distances to corners
c1 = length_sqr(cpos-p11);
c2 = length_sqr(cpos-p12);
c3 = length_sqr(cpos-p21);
c4 = length_sqr(cpos-p22);
does_collide = (sqrt(min(c1, c2, c3, c4)) < rc);
}
}
To compare the small circle to a ray:
First check to see whether the circle encloses the origin; if it does, then it intersects the ray. Otherwise, read on.
Consider the vector v from the origin to the center of the circle. Normalize that, normalize the ray R, and take the cross product Rxv. If it's positive, v is counterclockwise from R, otherwise it's clockwise from R. Either way, take acos to get the angle between them.
If the circle has radius r and its center is a distance d from the origin, then the angular half-width of the circle (as seen from the origin) is asin(r/d). If the angle between R and v is less than that, then the circle intersects the ray.
Assume that you know whether the object extends clockwise or counterclockwise from Start to End. (The numbers won't tell you that, you must know it already or the problem is unsolvable.) In your example, it's clockwise. Now you have to be careful; if the angular length of the arc is <= pi, then you can proceed, otherwise it is easier to determine whether the circle is in the smaller sector outside the sector of the object. But assuming the object spans less that pi, the circle is inside the sector of the object (i.e. between the rays) if and only if it is clockwise from the Start and counterclockwise from the End.
Related
Given an integer 3D coordinate system, a center point P, a vector in some direction V, and a max sphere radius R:
I want to iterate over only integer points in a fashion that starts at P and goes along direction V until reaching the max radius R.
Then, for some small angle T iterate all points within the cone (or spherical sector) around V.
Incrementally expand T until T is pi/2 radians and every point within the sphere has been iterated.
I need to do this with O(1) space complexity. So the order of the points can't be precomputed/sorted but must result naturally from some math.
Example:
// Vector3 represents coordinates x, y, z
// where (typically) x is left/right, y is up/down, z is depth
Vector3 center = Vector3(0, 0, 0); // could be anything
Vector3 direction = Vector3(0, 100, 0); // could be anything
int radius = 4;
double piHalf = acos(0.0); // half of pi
std::queue<Vector3> list;
for (double angle = 0; angle < piHalf; angle+= .1)
{
int x = // confusion begins here
int y = // ..
int z = // ..
list.push(Vector3(x, y, z));
}
See picture for this example
The first coordinates that should be caught are:
A(0,0,0), C(0,1,0), D(0,2,0), E(0,3,0), B(0,4,0)
Then, expanding the angle somewhat (orange cone):
K(-1,0,3), X(1,0,3), (0,1,3), (0,-1,3)
Expanding the angle a bit more (green cone):
F(1,1,3), (-1,-1,3), (1,-1,3) (-1,1,3)
My guess for what would be next is:
L(1,0,2), (-1,0,2), (0,1,2), (0,-1,2)
M(2,0,3) would be hit somewhat after
Extra notes and observations:
A cone will hit a max of four points at its base, if the vector is perpendicular to an axis and originates at an integer point. It may also hit points along the cone wall depending on the angle
I am trying to do this in c++
I am aware of how to check whether a point X is within any given cone or spherical vector by comparing the angle between V and PX with T and am currently using this knowledge for a lesser solution.
This is not a homework question, I am working on a 3D video game~
iterate all integer positions Q in your sphere
simple 3x nested for loops through x,y,z in range <P-R,P+R> will do. Just check inside sphere so
u=(x,y,z)-P;
dot(u,u) <= R*R
test if point Q is exactly on V
simply by checking angle between PQ and V by dot product:
u = Q-P
u = u/|u|
v = V/|V|
if (dot(u,v)==1) point Q is on V
test if points is exactly on surface of "cone"
simply by checking angle between PQ and V by dot product:
u = Q-P
u = u/|u|
v = V/|V|
if (dot(u,v)==cos(T/2)) point Q is on "cone"
where I assume T is full "cone" angle not the half one.
Beware you need to use floats/double for this and make the comparison with some margin for error like:
if (fabs(dot(u,v)-1.0 )<1e-6) point Q is on V
if (fabs(dot(u,v)-cos(T/2))<1e-6) point Q is on "cone"
I have been reading a lot about the sutherland hodgman polygon clipping algorithm and understand the general idea. However, when I see the actual implementation of it (like the one below), I get confused about the coordinate comparisons such as those in the intersection and inside methods. Therefore, I was wondering if someone could elaborate on what and why? I see a ton of videos and articles explaining the general concepts but I really have trouble finding some explanation of the actual details regarding the implementation.
bool inside(b2Vec2 cp1, b2Vec2 cp2, b2Vec2 p) {
return (cp2.x-cp1.x)*(p.y-cp1.y) > (cp2.y-cp1.y)*(p.x-cp1.x);
}
b2Vec2 intersection(b2Vec2 cp1, b2Vec2 cp2, b2Vec2 s, b2Vec2 e) {
b2Vec2 dc( cp1.x - cp2.x, cp1.y - cp2.y );
b2Vec2 dp( s.x - e.x, s.y - e.y );
float n1 = cp1.x * cp2.y - cp1.y * cp2.x;
float n2 = s.x * e.y - s.y * e.x;
float n3 = 1.0 / (dc.x * dp.y - dc.y * dp.x);
return b2Vec2( (n1*dp.x - n2*dc.x) * n3, (n1*dp.y - n2*dc.y) * n3);
}
//http://rosettacode.org/wiki/Sutherland-Hodgman_polygon_clipping#JavaScript
//Note that this only works when fB is a convex polygon, but we know all
//fixtures in Box2D are convex, so that will not be a problem
bool findIntersectionOfFixtures(b2Fixture* fA, b2Fixture* fB, vector<b2Vec2>& outputVertices)
{
//currently this only handles polygon vs polygon
if ( fA->GetShape()->GetType() != b2Shape::e_polygon ||
fB->GetShape()->GetType() != b2Shape::e_polygon )
return false;
b2PolygonShape* polyA = (b2PolygonShape*)fA->GetShape();
b2PolygonShape* polyB = (b2PolygonShape*)fB->GetShape();
//fill subject polygon from fixtureA polygon
for (int i = 0; i < polyA->GetVertexCount(); i++)
outputVertices.push_back( fA->GetBody()->GetWorldPoint( polyA->GetVertex(i) ) );
//fill clip polygon from fixtureB polygon
vector<b2Vec2> clipPolygon;
for (int i = 0; i < polyB->GetVertexCount(); i++)
clipPolygon.push_back( fB->GetBody()->GetWorldPoint( polyB->GetVertex(i) ) );
b2Vec2 cp1 = clipPolygon[clipPolygon.size()-1];
for (int j = 0; j < clipPolygon.size(); j++) {
b2Vec2 cp2 = clipPolygon[j];
if ( outputVertices.empty() )
return false;
vector<b2Vec2> inputList = outputVertices;
outputVertices.clear();
b2Vec2 s = inputList[inputList.size() - 1]; //last on the input list
for (int i = 0; i < inputList.size(); i++) {
b2Vec2 e = inputList[i];
if (inside(cp1, cp2, e)) {
if (!inside(cp1, cp2, s)) {
outputVertices.push_back( intersection(cp1, cp2, s, e) );
}
outputVertices.push_back(e);
}
else if (inside(cp1, cp2, s)) {
outputVertices.push_back( intersection(cp1, cp2, s, e) );
}
s = e;
}
cp1 = cp2;
}
return !outputVertices.empty();
}
(code stolen from iforce2d :) )
You say you understand the general idea, presumably from reading something like Sutherland Hodgman Algorithm. That explains at a high level exactly what inside and intersection do.
As to the details of how they achieve their objectives, that is all just straight up text book linear algebra.
inside is testing the sign of (cp2 - cp2) cross (p - cp1) and returning true iff the sign is strictly greater than zero. You could rewrite the return statement as:
return (cp2.x-cp1.x)*(p.y-cp1.y) - (cp2.y-cp1.y)*(p.x-cp1.x) > 0;
by moving the second term to the left of the > which gives you exactly the cross product on the left.
Note that a cross product is typically a vec3 cross vec3 operation and requires computation of all three terms. However, we're doing this in 2d meaning the vec3s have the form (x, y, 0). Therefore we only need to compute the z output term, since the cross must be perpendicular to the xy plane, and therefore be of the form (0, 0, value).
intersection finds the point at which two vectors intersect using exactly the algorithm listed here: Line Intersection from Two Points. In particular, we care about the formula immediately following the text "The determinants can be written out as:"
In the context of that formula n1 is (x1 y2 - y1 x2), n2 is (x3 y4 - y3 x4) and n3 is 1 / ((x1 - x2) (y3 - y4) - (y1 - y2) (x3 - x4))
-- EDIT --
To cover the issue raised in the comments, here is as full an explanation as I can give for why the return value from inside() is a test of the sign of the cross product.
I'm going to go off on a slight tangent, show my age, and note that the cross product formula has a very simple memory aid. You just need to remember the first magic word from Woods and Crowther's text adventure game Colossal Cave. xyzzy.
If you have two vectors in three dimensions: (x1, y1, z1) and (x2, y2, z2), their cross product (xc, yc, zc) is evaluated thus:
xc = y1 * z2 - z1 * y2;
yc = z1 * x2 - x1 * z2;
zc = x1 * y2 - y1 * x2;
Now, look at the first line, remove the c, 1 and 2 suffixes from the terms, all the spaces and operators and just look at the remaining letters. It's the magic word. Then you just go vertically down, replacing x with y, y with z and z with x as you go from line to line.
Getting back to business, both the terms on the right of the expansions of xc and yc contain either z1 or z2. But we know that both of these are zero since our input vectors are in the xy plane, and therefore have a zero z component. That's why we can completely elide computing those two terms, because we know they'll be zero.
This is 100% consistent with the definition of what the cross product does, the resulting vector is always perpendicular to both input vectors. Hence if both input vectors are in the xy plane, we know the output vector must be perpendicular to the xy plane, and therefore have the form (0, 0, z)
So, what do we have for the z term?
zc = x1 * y2 - y1 * x2;
in this case vector 1 is cp2-cp1 and vector 2 is p-cp1. So plugging that into the above we get:
zc = (cp2.x-cp1.x)*(p.y-cp1.y) - (cp2.y-cp1.y)*(p.x-cp1.x);
But as noted, we don't care about its value, only it's sign. We want to know if that is greater than zero. Hence:
return (cp2.x-cp1.x)*(p.y-cp1.y) - (cp2.y-cp1.y)*(p.x-cp1.x) > 0;
which is then rewritten as:
return (cp2.x-cp1.x)*(p.y-cp1.y) > (cp2.y-cp1.y)*(p.x-cp1.x);
Finally, what does the sign of that term have to do with whether the point p is inside or outside the clipping polygon? You are quite correct that all the clipping takes place in the 2d xy plane, so why are we involving 3d operations at all?
The important thing to realize is that the cross product formula in 3d is not commutative. The order of the two vector operands is significant, in terms of the angle between them. The first image on the Wikipedia page for Cross Product shows it perfectly. In that diagram, if you look down from above, when evaluating a cross b, the shortest angular direction from a to b is counter-clockwise. In that instance, that leads to a cross product with a positive z value, assuming positive z goes up the page. However if you evaluate b cross a, the shotest angular distance from b to a is clockwise, and the cross product has a negative z value.
Thinking back to the Wikipedia page for the algorithm itself, you've got the blue "clipping" line that works its way counter-clockwise around the clipping polygon. If you think of that vector as always having positive magnitude in the counter-clockwise direction it'll always be cp2 - cp1 for any pair of adjacent vertices in the clipping polygon.
Keeping this in mind, imagine what you'd see if you stood at cp1, with your nose pointed straight at cp2. The interior of the clipping polygon will be on your left, and the exterior on the right. Now consider two points p1 and p2. We'll say p1 is inside the clipping poly, and p2 is outside. That means that the quickest way to point yourt nose at p1 is to rotate counter-clockwise, and the quickest way to point at p2 is to rotate clockwise.
So by studying the sign of the cross product, we're really asking 'Do we rotate clockwise or counter-clockwise from the current edge to look at the point' which equates to asking if the point is inside the clipping polygon or outside.
I'll add one final suggestion. If you're at all interested in this sort of thing, or 3d rendering, or any programming that involves modelling the mathematical representation of the real world, taking a good solid course in linear algebra that covers the likes of cross products, dot products, vectors, matrices and the interactions between them all will be one of the best things you can ever do. It will provide a very strong foundation for a fair amount of what is done with computers.
Ok, I'm having a bit of trouble finding a solution for this that seems to be a simple geometry problem.
I have a list of triple coordinates that form a square angle.
Between all these triple-coordinates I want to find a pair that forms up a square.
I believe the best I can do to exemplify is show an image:
and 2. are irrelevant. 3. and 4. are what I'm looking for.
For each triple coordinate I have the midle point, where the angle is, and two other points that describe the two segments that form the angle.
Summing it up, given six points, 2 for the diagonal + 4 other points, how can I find if these make a square?
obs: the two lines that make the angle are consistent but don't have the same size.
obs2:the lines from different triples may not intersect
Thank you for time and any help and insight provided.
If any term I used is incorrect or just plain hard to understand let me know, I'm not a native english speaker.
Edit: The code as it stands.
//for all triples
for (size_t i = 0; i < toTry.size() - 1; i++) {
Vec2i center_i = toTry[i].avg;
//NormalizedDiagonal = ((Side1 - Center) + (Side2 - Center));
Vec2i a = toTry[i].p, b = toTry[i].q;
Vec2f normalized_i = normalizedDiagonal(center_i, toTry[i].p, toTry[i].q);
for (size_t j = i + 1; j < toTry.size(); j++) {
Vec2i center_j = toTry[j].avg;
//Se os pontos sao proximos, nao importam
if (areClose(center_i, center_j, 25))
continue;
Vec2f normalized_j = normalizedDiagonal(center_j, toTry[j].p, toTry[j].q);
line(src, Point(center_i[0], center_i[1]), Point(center_i[0] + 1 * normalized_i[0], center_i[1] + 1 * normalized_i[1]), Scalar(255, 255, 255), 1);
//test if antiparallel
if (abs(normalized_i[0] - normalized_j[0]) > 0.1 || abs(normalized_i[1] - normalized_j[1] > 0.1))
continue;
Vec2f delta;
delta[0] = center_j[0] - center_i[0]; delta[1] = center_j[1] - center_i[1];
double dd = sqrt(pow((center_i[0] - center_j[0]), 2) + pow((center_i[1] - center_j[1]), 2));
//delta[0] = delta[0] / dd;
//delta[1] = delta[1] / dd;
float dotProduct = normalized_i[0] * delta[0] + normalized_i[1] * delta[1];
//test if do product < 0
if (dotProduct < 0)
continue;
float deltaDotDiagonal = delta[0] * normalized_i[0] + delta[1] * normalized_i[1];
menor_d[0] = delta[0] - deltaDotDiagonal * normalized_i[0];
menor_d[1] = delta[1] - deltaDotDiagonal * normalized_i[1];
dd = sqrt(pow((center_j[0] - menor_d[0]), 2) + pow((center_j[1] - menor_d[1]), 2));
if(dd < 25)
[...]
Just to be clear, the actual lengths of the side segments is irrelevant, right? All you care about is whether the semi-infinite lines formed by the side segments of two triples form a square? Or do the actual segments need to intersect?
Assuming the former, a method to check whether two triples form a square is as follows. Let's use the Point3D and Vector3D from the System.Windows.Media.Media3D namespace to define some terminology, since these are decent general-purpose 3d double precision points and vectors that support basic linear algebra methods. These are c# so you can't use them directly but I'd like to be able to refer to some of the basic methods mentioned there.
Here is the basic method to check if two triples intersect:
Define a triple as follows: Center, Side1 and Side2 as three Point3D structures.
For each triple, define the normalized diagonal vector as
NormalizedDiagonal = ((Side1 - Center) + (Side2 - Center));
NormalizedDiagonal.Normalize()
(You might want to cache this for performance.)
Check if the two centers are equal within some linear tolerance you define. If equal, return false -- it's a degenerate case.
Check if the two diagonal vectors are antiparallel within some angular tolerance you define. (I.e. NormalizedDiagonal1 == -NormalizedDiagonal2 with some tolerance.) If not, return false, not a square.
Compute the vector from triple2.Center to triple2.Center: delta = triple2.Center - triple1.Center.
If double deltaDotDiagonal = DotProduct(delta, triple1.NormalizedDiagonal) < 0, return false - the two triples point away from each other.
Finally, compute the distance from the center of triple2 to the (infinite) diagonal line passing through the center triple1. If zero (within your linear tolerance) they form a square.
To compute that distance: distance = (delta - deltaDotDiagonal*triple1.NormalizedDiagonal).Length
Note: deltaDotDiagonal*triple1.NormalizedDiagonal is the projection of the delta vector onto triple1.NormalizedDiagonal, and thus delta - deltaDotDiagonal*triple1.NormalizedDiagonal is the component of delta that is perpendicular to that diagonal. Its length is the distance we seek.
Finally, If your definition of a square requires that the actual side segments intersect, you can add an extra check that the lengths of all the side segments are less than sqrt(2) * delta.Length.
This method checks if two triples form a square. Finding all triples that form squares is, of course, O(N-squared). If this is a problem, you can put them in an array and sort then by angle = Atan2(NormalizedDiagonal.Y, NormalizedDiagonal.X). Having done that, you can find triples that potentially form squares with a given triple by binary-searching the array for triples with angles = +/- π from the angle of the current triple, within your angular tolerance. (When the angle is near π you will need to check both the beginning and end of the array.)
Update
OK, let's see if I can do this with your classes. I don't have definitions for Vec2i and Vec2f so I could get this wrong...
double getLength(Vec2f vector)
{
return sqrt(pow(vector[0], 2) + pow(vector[1], 2));
}
Vec2f scaleVector(Vec2f vec, float scale)
{
Vec2f scaled;
scaled[0] = vec[0] * scale;
scaled[1] = vec[1] * scale;
return scaled;
}
Vec2f subtractVectorsAsFloat(Vec2i first, Vec2i second)
{
// return first - second as float.
Vec2f diff;
diff[0] = first[0] - second[0];
diff[1] = first[1] - second[1];
return diff;
}
Vec2f subtractVectorsAsFloat(Vec2f first, Vec2f second)
{
// return first - second as float.
Vec2f diff;
diff[0] = first[0] - second[0];
diff[1] = first[1] - second[1];
return diff;
}
double dot(Vec2f first, Vec2f second)
{
return first[0] * second[0] + first[1] * second[1];
}
//for all triples
for (size_t i = 0; i < toTry.size() - 1; i++) {
Vec2i center_i = toTry[i].avg;
//NormalizedDiagonal = ((Side1 - Center) + (Side2 - Center));
Vec2i a = toTry[i].p, b = toTry[i].q;
Vec2f normalized_i = normalizedDiagonal(center_i, toTry[i].p, toTry[i].q);
for (size_t j = i + 1; j < toTry.size(); j++) {
Vec2i center_j = toTry[j].avg;
//Se os pontos sao proximos, nao importam
if (areClose(center_i, center_j, 25))
continue;
Vec2f normalized_j = normalizedDiagonal(center_j, toTry[j].p, toTry[j].q);
//test if antiparallel
if (abs(normalized_i[0] - normalized_j[0]) > 0.1 || abs(normalized_i[1] - normalized_j[1] > 0.1))
continue;
// get a vector pointing from center_i to center_j.
Vec2f delta = subtractVectorsAsFloat(center_j, center_i);
//test if do product < 0
float deltaDotDiagonal = dot(delta, normalized_i);
if (deltaDotDiagonal < 0)
continue;
Vec2f deltaProjectedOntoDiagonal = scaleVector(normalized_i, deltaDotDiagonal);
// Subtracting the dot product of delta projected onto normalized_i will leave the component
// of delta which is perpendicular to normalized_i...
Vec2f distanceVec = subtractVectorsAsFloat(deltaProjectedOntoDiagonal, center_j);
// ... the length of which is the distance from center_j
// to the diagonal through center_i.
double distance = getLength(distanceVec);
if(distance < 25) {
}
}
There are two approaches to solving this. One is a very direct approach that involves finding the intersection of two line segments.
You just use the triple coordinates to figure out the midpoint, and the two line segments that protrude from it (trivial). Do this for both triple-sets.
Now calculate the intersection points, if they exist, for all four possible permutations of the extending line segments. From the original answer to a similar question:
You might look at the code I wrote for Computational Geometry in C,
which discusses this question in detail (Chapter 1, Section 5). The
code is available as SegSegInt from the links at that web site.
In a nutshell, I recommend a different approach, using signed area of
triangles. Then comparing appropriate triples of points, one can
distinguish proper from improper intersections, and all degenerate
cases. Once they are distinguished, finding the point of intersection
is easy.
An alternate, image processing approach, would be to render the lines, define one unique color for the lines, and then apply an seed/flood fill algorithm to the first white zone found, applying a new unique color to future zones, until you flood fill an enclosed area that doesn't touch the border of the image.
Good luck!
References
finding the intersection of two line segments in 2d (with potential degeneracies), Accessed 2014-08-18, <https://math.stackexchange.com/questions/276735/finding-the-intersection-of-two-line-segments-in-2d-with-potential-degeneracies>
In a pair of segments, call one "the base segment" and one that is obtained by rotating the base segment by π/2 counterclockwise is "the other segment".
For each triple, compute the angle between the base segment and the X axis. Call this its principal angle.
Sort triples by the principal angle.
Now for each triple with the principal angle of α any potential square-forming mate has the principal angle of α+π (mod 2π). This is easy to find by binary search.
Furthermore, for two candidate triples with vertices a and a' and principal angles α and α+π, the angle of vector aa' should be α+π/4.
Finally, if each of the four segments is at least |aa'|/√2 long, we have a square.
I have two of these:
bool isPointOnShape(int a, int b)
{
}
bool isPointInShape(int a, int b)
{
}
Say I have a square, first point (bottom left corner) is x,y (0,0) second point (top left) is (0,2), third is (2,2) and fourth is (0,2).
The Points on shape would be (0,1) (1,2) (2,1) (1,0) and Points in shape is (1,1)
How do I find out the points on shape / in shape and return a true value so that I can store it somewhere?
I'll offer a general solution for any shape that can be divided in straight segments.
So, as you may have guessed, I'll start by consider your "shape" as a list of segments that completes a loop. Or simply put a circular list of points that represents a loop, for example, your square would be this list of points:
0, 0
0, 2
2, 2
2, 0
Note that we consider that there are segments from each point to the next and that the final point connects to the first. Also, we require that no consecutive points are equal, nor the first and last. If there are any, those must be removed before proceeding.
Now, for each segment we can determinate the bounding box. For example given this segment:
a = (0, 2)
b = (2, 2)
Then the range of values in x is [0, 2] and in y is [2, 2] and that is your bounding box for that segment.
The next thing you need is the director vector of the line of the segment. To get that, first calculate the length of the segment:
length = sqrt((a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y - b.y))
And then:
director.x = (a.x - b.x)/length
director.y = (a.y - b.y)/length
Note 1: when then length is 0, then you have an invalid segment. That's why we don't want repeated points.
Note 2: Using the director vector instead of using the equation of the line will make things easier.
Now, given a point p, you can determinate if that point is in a segment (if it is one of the points in the list). For the rest of cases we start by looking if it is inside of the axis aligned bounding box. This is done simply by checking the range:
if
(
(p.x >= box.left && p.x <= box.right) &&
(p.y >= box.top && p.y <= box.bottom) // with origin at the top-left corner
)
{
//It is inside of the bounding box
}
And if it is, then we calculate the distance from the point to the line, if it is
0 then it is on the line. Now, because of floating point arithmetics, you could test if the distance is less or equal to epsilon, where epsilon is a very small number.
We use this formula:
distance vector = (a - p) - ((a - p) · director) * director
distance = the norm of distance vector
Where "·" denotes a dot product and "*" denotes an scalar product.
All that rests is to iterate over the segments, for each one calculate the distance and if for anyone the distance is less than epsilon then the point is "on the shape".
Ok, but what about "in the shape"?
Well, with a little help of a trick from topology we can determinate if a point is inside or not. This is the same algorithm Windows would use to fill a polygon or polyline (such as deciding what is inside a selected area with free hand in Microsoft Paint).
It goes like this:
Count the number of segments you have to cross to get to the point from outside. If the number is pair, then it is outside, if it is odd then inside.
You can choose from what direction to reach the point. I choose left.
Once more, you are going to iterate over the segments. For each one we need to decide if it is at the vertical range. For that use the bounding box:
if ((p.y >= box.top && p.y <= box.bottom))
{
//In the vertical range
}
Now, determinate if the segment is at left, or right:
if (p.x < box.left)
{
//The segment is at the left
}
else if (p.x > box.right)
{
//The segment is at the right
}
else
{
//The segment is close, need further calculation
}
In the case that the segment is close we need to calculate the vector distance to that segment and check it's direction.
The vector distance? Well, we already have it, we are taking its norm to determinate the distance. Now, instead of taking the norm, verify the sign of the x coordinate. If it is less than 0, it is right, if it is more than 0 then it is left. If it is 0... it means that the segment is horizontal (because the distance vector is always perpendicular to the segment), you can skip that segment*.
*: In fact, if the segment is horizontal and it is in vertical range, it means that it is at the segment. Are segments "in shape" or not?
Now, you need to count the number of segments at the left, and if it odd, the point is inside the shape. It is out otherwise. This can also be done with the segments that are up, or right, or below. I just picked left.
For large shapes where iterating over all the segments is expensive, you can store the segments in some space partitioning data structure. That is beyond the scope of this post.
If I suppose you have a Rectangle class and that this class has members bottomLeft and topRight, you can write something like this:
bool Rectangle::isPointOnShape(int x, int y) {
if (x == bottomLeft.x || x == topRight.x)
if (y > bottomLeft.y && y < topRight.y)
return true;
if (y == bottomLeft.y || y == topRight.y)
if (x > bottomLeft.x && x < topRight.x)
return true;
}
bool Rectangle::isPointInShape(int x, int y) {
bool inX = false;
bool inY = false;
if (x > bottomLeft.x && x < topRight.x)
inX = true;
if (y > bottomLeft.y && y < topRight.y)
inY = true;
return (inX && inY);
}
If your shape is not a rectangle, this functions will be different of course.
I'm trying to implement at line-plane intersection algorithm. According to Wikipedia I need three non-colinear points on the plane to do that.
I therefore tried implementing this algorithm in C++, however. Something is definitely wrong, cause it makes no sense that I can choose whatever x and y coordinates and they'll fit in the plane. What if the plane is vertical and along the x-axis? No point with y=1 would then be in the plane.
I realize that this problem has been posted a lot on StackOverflow, and I see lots of solutions where the plane is defined by 3 points. But I only have a normal and a position. And I can't test my line-plane intersection algorithm before I sort out my non-colinear point finder.
The problem right now, is that I'm dividing by normal.z, and that obviously won't work when normal.z is 0.
I'm testing with this plane: Plane* p = new Plane(Color(), Vec3d(0.0,0.0,0.0), Vec3d(0.0,1.0,0.0)); // second parameter : position, third parameter : normal
The current code gives this incorrect answer:
{0 , 0 , 0} // alright, this is the original
{12.8377 , 17.2728 , -inf} // obviously this is not a non-colinear point on the given plane
Here's my code:
std::vector<Vec3d>* Plane::getThreeNonColinearPoints() {
std::vector<Vec3d>* v = new std::vector<Vec3d>();
v->push_back(Vec3d(position.x, position.y, position.z)); // original position can serve as one of the three non-colinear points.
srandom(time(NULL));
double rx, ry, rz, start;
rx = Plane::fRand(10.0, 20.0);
ry = Plane::fRand(10.0, 20.0);
// Formula from here: http://en.wikipedia.org/wiki/Plane_(geometry)#Definition_with_a_point_and_a_normal_vector
// nx(x-x0) + ny(y-y0) + nz(z-z0) = 0
// |-----------------| <- this is "start"
//I'll try to insert position as x0,y0,z0 and normal as nx,ny,nz, and solve the equation
start = normal.x * (rx - position.x) + normal.y * (ry - position.y);
// nz(z-z0) = -start
start = -start;
// (z-z0) = start/nz
start /= normal.z; // division by zero
// z = start+z0
start += position.z;
rz = start;
v->push_back(Vec3d(rx, ry, rz));
// TODO one more point
return v;
}
I realize that I might be trying to solve this totally wrong. If so, please link a concrete implementation of this. I'm sure it must exist, when I see so many line-plane intersection implementations.
Thanks in advance.
A plane can be defined with several ways. Typically a point on the plane and a normal vector is used. To get the normal vector from three points (P1, P2, P3 ) take the cross product of the side of the triangle
P1 = {x1, y1, z1};
P2 = {x2, y2, z2};
P3 = {x3, y3, z3};
N = UNIT( CROSS( P2-P1, P3-P1 ) );
Plane P = { P1, N }
The reverse, to go from a point P1 and normal N to three points, you start from any direction G not along the normal N such that DOT(G,N)!=0. The two orthogonal directions along the plane are then
//try G={0,0,1} or {0,1,0} or {1,0,0}
G = {0,0,1};
if( MAG(CROSS(G,N))<TINY ) { G = {0,1,0}; }
if( MAG(CROSS(G,N))<TINY ) { G = {1,0,0}; }
U = UNIT( CROSS(N, G) );
V = CROSS(U,N);
P2 = P1 + U;
P3 = P1 + V;
A line is defined by a point and a direction. Typically two points (Q1, Q2) define the line
Q1 = {x1, y1, z1};
Q2 = {x2, y2, z2};
E = UNIT( Q2-Q1 );
Line L = { Q1, E }
The intersection of the line and plane are defined by the point on the line r=Q1+t*E that intersects the plane such that DOT(r-P1,N)=0. This is solved for the scalar distance t along the line as
t = DOT(P1-Q1,N)/DOT(E,N);
and the location as
r = Q1+(t*E);
NOTE: The DOT() returns the dot-product of two vector, CROSS() the cross-product, and UNIT() the unit vector (with magnitude=1).
DOT(P,Q) = P[0]*Q[0]+P[1]*Q[1]+P[2]*Q[2];
CROSS(P,Q) = { P[1]*Q[2]-P[2]*Q[1], P[2]*Q[0]-P[0]*Q[2], P[0]*Q[1]-P[1]*Q[0] };
UNIT(P) = {P[0]/sqrt(DOT(P,P)), P[1]/sqrt(DOT(P,P)), P[2]/sqrt(DOT(P,P))};
t*P = { t*P[0], t*P[1], t*P[2] };
MAG(P) = sqrt(P[0]*P[0]+P[1]*P[1]+P[2]*P[2]);
One approach you may find easy to implement is to see where the plane intersects the coordinate axes. For the plane given by the equationaX + bY + cZ - d = 0 hold two variables at 0 and solve for the third. So the solutions would be (assuming a, b, c, and d are all non-zero):
(d/a, 0, 0)
(0, d/b, 0)
(0, 0, d/c)
You will need to consider the cases where one or more of the coefficients are 0 so you don't get a degenerate or colinear solutions. As an example if exactly one of the coefficients is 0 (say a=0) you instead use
(1, d/b, 0)
(0, d/b, 0)
(0, 0, d/c)
If exactly two of the coefficients are 0 (say a=0 and b=0) you can use:
(1, 0, d/c)
(0, 1, d/c)
(0, 0, d/c)
If d=0, the plane intersects the three axes at the origin, and so you can use:
(1, 0, -a/c)
(0, -c/b, 1)
(-b/a, 1, 0)
You will need to work out simular cases for d and exactly one other coefficient being 0, as well as d and two others being 0. There should be a total of 16 cases, but there are a few things that come to mind which should make that somewhat more manageable.
Where N=(Nx,Ny,Nz) is the normal, you could project the points N, (Ny,Nz,Nx), (Nz,Nx,Ny) onto the plane: they're guaranteed to be distinct.
Alternatively, if P and Q are on the plane, P+t(Q-P)xN is also on the plane for any t!=0 where x is the cross product.
Alternatively if M!=N is an arbitrary vector, K=MxN and L=KxN are colinear with the plane and any point p on the plane can be written as p=Origin+sK+tL for some s,t.