Detect extreme points of a convex polygon - c++

How can i get the extreme points of a convex polygon looking from a determined point? I'm trying to make this by points angle, the smaller and bigger angles is the extreme points, but when observer is closer to points, this is not valid.
This is my code:
Vec2* bigger = &points[0]; // pointer to point with bigger angle
Vec2* smaller = &points[0]; // pointer to point with smaller angle
Vec2 observer = rayCenter;
// iterate through all points of polygon
for(unsigned u = 0 ; u < points.size() ; u++)
{
Vec2 distance = observer - points[u];
if(distance.angle() < (observer - *smaller).angle())
smaller = &points[u];
if(distance.angle() > (observer - *bigger).angle())
bigger = &points[u];
}
The result:
Where blue lines is the excluded points and yellow desirable points.
Is there a best way to resolve this?
Sorry for my english.

Polygon vertex A is extreme for the given location of the observer, iff all other points of the polygon lie on the same side of the observer-to-A line (or, possibly, lie on that line).
If the polygon is known to be convex, then the criterion is greatly simplified. There's no need to analyze all other points of the polygon. The extreme point can be easily recognized by analyzing the locations of its two immediate neighbors.
If A is our candidate point and P and N are its adjacent points in the polygon (previous and next), then A is an extreme point iff both P and N lie on the same side of observer-to-A line.
vec_of_A = A - observer; // observer-to-A vector
vec_of_P = P - observer;
vec_of_N = N - observer;
productP = vec_of_A.x * vec_of_P.y - vec_of_A.y * vec_of_P.x;
productN = vec_of_A.x * vec_of_N.y - vec_of_A.y * vec_of_N.x;
if (sign(productP) == sign(productN))
// A is an extreme point
else
// A is not an extreme point
Some extra decision making will be necessary if P and/or N lie exactly on the observer-to-A line (depends on what point you consider extreme in such cases).

Compute a new convex hull using the points from the existing convex hull plus the observer point. In the new convex hull, the points that are adjacent to the observer point are your "extreme" points.
Here is a matlab implementation. Below is a sample output where the blue point is the observer point and the green polygon is the convex hull of the red points. The implementation returns the points (0,0) and (2,0).

You shouldn't compare angles directly as there is a 360° wraparound.
Prefer to test "this point is more to the left" or "to the right" by computing the signed area of the triangle formed by the observer and two points.

Related

Determine if point is in frustum

I'm trying to work out the best way to determine whether a point is inside a frustum. I have something working, but not sure whether it is too cumbersome, and perhaps there is a more elegant / efficient way I should be doing this.
Suppose I want to find out whether point 'x' is inside a frustrum:
Once I have the locations of the 8 points of the frustrum (4 near points, four far points), I am calculating the normal for each plane of the frustum based on a triangle made from three of the points. For example (as in the diagram above), for the right side, I am making two vectors from three of the points:
Vector U = FBR - NBR
Vector V = FTR - NBR
Then I am making the cross product between these two vectors, ensuring that the winding order is correct for the normal to be pointing inside the frustum, in this case V x U will give the correct normal.
Right_normal = V x U
Once I have the normal for each plane, I am then checking whether point x is in front of or behind the plane by drawing a vector from x to one of the plane's points:
Vector xNBR = x - NBR
Then I am doing the dot product between this vector and the normal and testing whether the answer is positive, confirming whether point x is the correct side of that plane of the frustrum:
if ( xNBR . Right_normal < 0 )
{
return false;
}
else continue testing x against other planes...
If x is positive for all planes, then it is inside the frustum.
So this seems to work, but I'm just wondering whether I'm doing this in a stupid way. I didn't even know what 'cross product' meant until yesterday, so it's all rather new to me and I might be doing something rather silly.
To adapt the approach you have taken, rather than change it totally, you can make use of the fact that 2 of the pairs of planes are parallel. Create only one normal for that pair of planes. You already have the test for the point being "in front" of one of the planes, but assuming you know the depth of the frustum, you can use the same distance to test the point against the other parallel face.
double distancePastFrontPlane = xNBR . Right_normal;
if (distancePastFrontPlane < 0 )
{
// point is in front of front plane
return false;
if(distancePastFrontPlane > depthFaceRtoFaceL)
{
// point is behind back plane
return false;
}
}
If you have multiple points to test against the same frustum you can benefit because you only calculate the frustum depth once (per pair of parallel planes).

CGAL Intersection Circle and Vertical Lines (not segments)

In CGAL I need to compute the exact intersection points between a set of lines and a set o circles. Starting from the circles (which can have irrational radius but rational squared_radius) I should compute the vertical line passing through the x_extremal_points of each circle (not segment but lines) and calculate the intersection point of each circle with each line.
I’m using CircularKernel and Circle_2 for the circles and Line_2 for the lines.
Here’s an example of how I compute the circles and the lines and how I check if they intersect.
int main()
{
Point_2 a = Point_2(250.5, 98.5);
Point_2 b = Point_2(156, 139);
//Radius is half distance ab
Circular_k::FT aRad = CGAL::squared_distance(a, b);
Circle_2 circle_a = Circle_2(a, aRad/4);
Circular_arc_point_2 a_left_point = CGAL::x_extremal_point(circle_a, false);
Circular_arc_point_2 a_right_point = CGAL::x_extremal_point(circle_a, true);
//for example use only left extremal point of circle a
CGAL::Bbox_2 a_left_point_bb = a_left_point.bbox();
Line_2 a_left_line = Line_2(Point_2(a_left_point_bb.xmin(), a_left_point_bb.ymin()),
Point_2(a_left_point_bb.xmin(), a_left_point_bb.ymax()));
if ( do_intersect(a_left_line, circle_a) ) {
std::cout << "intersect";
}
else {
std::cout << " do not intersect ";
}
return 0;
}
This flow rises this exception:
CGAL error: precondition violation!
Expression : y != 0
File : c:\dev\cgal-4.7\include\cgal\gmp\gmpq_type.h
Line : 371
Explanation:
Refer to the bug-reporting instructions at http://www.cgal.org/bug_report.html
I can’t figure out how I can calculate the intersection points.
Also, Is there a better way to compute the lines? I know abot the x_extremal_point function but it returns the Circular_arc_point point and I’m not able to construct a vertical line passing through them directly without using Bounding box.
In your code, you seem to compute the intersection of a circle with the vertical line that passes through the extremal point of the circle (I forget the bounding box). Well, then the (double) intersection is the extremal point itself...
More globally, you say in your text of introduction that you want to compute exact intersections. Then you should certainly not use bounding boxes, which by definition introduce some approximation.
If I understand your text correctly,
* for testing the intersection of your vertical lines with the other circles, you don't need to construct the lines, you only need to compare the abscissae of the extremal points of two circles, which you can do with the CGAL circular kernel.
* for computing the intersection of a vertical line that has non-rational coefficients (since its equation is of the form x= +-sqrt(r)) with another circle, then the CGAL circular kernel will not give you a pre-cooked solution. That kernel will help, but you must still compute a few things by hand.
If you don't want to bother, then you can also just take a standard CGAL kernel with Core::Expr as underlying number type. It can do "anything", but it will be slower.
For efficiency, you should look at the underlying 1D problem: projecting the lines and the circle on the X axis, you have a set of points and a set of intervals [Xc-R, Xc+R].
If the L points are sorted increasingly, you can locate the left bound of an interval in time Lg(L) by dichotomy, and scan the list of points until the right bound. This results in a O(Lg(L).C + I) behavior (C circle intervals), where I is the number of intersections reported.
I guess that with a merge-like process using an active list, if the interval bounds are also sorted you can lower to O(L + C + I).
The extension to 2D is elementary.

How does the algorithm of merging two convex hulls by using their tangents work in practice?

I'm trying to implement in C++ the divide and conquer algorithm of finding the convex hull from a set of two dimensional points. For simplicity let's assume that all the points are described with integers.
The most important part of the algorithm is merging the two convex hulls that you have computed from previous recursive calls. This part involves finding the lower and upper tangents of the two convex hulls and then proceeding with the merging.
The merging is trivial, if you have found the four points that describe the tangents, then the points that aren't inside the polygon defined by these four points will be part of the new convex hull.
However, I have no idea how to find these four points.
Here is the pseudocode that most sources (this one is from http://www.cs.wustl.edu/~pless/506/l3.html) suggest for finding the lower tangent of convex hull HA and convex hull HB.
Finding the Lower Tangent
LowerTangent(HA ; HB ) :
(1) Let a be the rightmost point of HA .
(2) Let b be the leftmost point of HB .
(3) While ab is not a lower tangent for HA and HB do
(a) While ab is not a lower tangent to HA do a = a - 1 (move a clockwise).
(b) While ab is not a lower tangent to HB do b = b + 1 (move b counterCW).
(4) Return ab.
(1), (2)
The points are initially sorted by their x coordinate, so finding the rightmost point of HA and the leftmost point of HB can be done in O(1).
a = HA[HA.size-1]
b = HB[0]
Now I can't understand the next steps.
Having chosen this ab line segment, how can we check if ab is not a lower tangent so we can either enter the first while loop or not?
And then, how do we move the point a to a-1 by following a clockwise direction? The points are sorted by their x coordinate, and doing just a = a-1 will lead to wrong results.
Thanks!
Your reference only briefly states:
Lower tangency is a condition that can be tested locally by an
orientation test of the two vertices and neighboring vertices on the
hull. (This is a simple exercise.)
and doesn't appear to give any more details. I found this reference which goes into further details of how to find the lower tangency including some example code.
Also note that your should not be sorting the X-coordinates for this purpose. This algorithm relies on the points being in their normal consecutive order that defines the polygon. Sorting by X only helps you in finding the initial points.
Pseudo-code of the lower tangency algorithm from that reference is:
idx1 = (Rightmost point index of Poly1)
idx2 = (Leftmost point index of Poly2)
while (TRUE)
while (isLeft(Poly1[idx2], Poly2[idx1], Poly2[idx1+1]) <= 0)
++idx1;
end while
while (isLeft(Poly2[idx1], Poly1[idx2], Poly1[idx2-1]) >= 0)
--idx2;
done = FALSE;
end while
end while
// idx1/idx2 are now the two indices that form the lower tangent
The isLeft() code from the same reference is:
float isLeft (Point P0, Point P1, Point P2)
{
return (P1.x - P0.x)*(P2.y - P0.y) - (P2.x - P0.x)*(P1.y - P0.y);
}
If the two convex polygons are intersecting then it is incorrect to assume that there are only two tangents that connect the two polygons.
It depends upon how exactly the points are divided. If division ensures that the resultant convex hulls never intersect then it is fine to make such assumption. But if the division of points does not guarantee the convex hulls to be non intersecting then algorithm needs to be modified to find multiple tangents.

How to find out whether a triangle mesh is concave or not?

Given a three dimensional triangle mesh, how can I find out whether it is convex or concave? Is there an algorithm to check that? If so it would be useful to define a tolerance range to ignore small concavities.
Image Source: http://www.rustycode.com/tutorials/convex.html
A convex polyhedron may be defined as an intersection of a finite number of half-spaces. These half-spaces are in fact the facet-defining half-space.
EDIT: Assuming your mesh actually defines a polyhedron (i.e. there is an "inside" and an "outside")
You can do something like this (pseudocode):
for each triangle
p = triangle plane
n = normal of p (pointing outside)
d = distance from the origin of p
//Note: '*' is the dot product.
//so that X*N + d = 0 is the plane equation
//if you write a plane equation like (X-P)*n = 0 (where P is any point which lays in the plane), then d = -P*n (it's a scalar).
for each vertex v in the mesh
h = v*N + d
if (h > Tolerance) return NOT CONVEX
//Notice that when v is a vertex of the triangle from which n and d come from,
//h is always zero, so the tolerance is required (or you can avoid testing those vertices)
end
end
return CONVEX
For a simple polygon as you described it, you can check for every inner angle at every vertice and check if the angle is below 180 degrees. If so, there is no way it is concave. If a single vertice is over 180°, it is concave.
Edit: for 3D meshes the same idea applies, but you have to test at every vertex every single triangle to each other whether the angle between the triangles is higher or lower than 180°

Enlarge and restrict a quadrilateral polygon in opencv 2.3 with C++

I can't find this answer anywhere, I hope somebody could help me.
I have an image (all black) with a white generic quadrilateral polygon inside it, and the correspondent 4 corners coordinates of such polygon.
I need to find the corners of a slightly enlarged quadrilateral and the same for a slightly reduced one (the shape must be the same, just a resize of the quadrilateral inside the image).
Is there a function which allows me to do that, or should I compute manually some geometry?
Thank you for your help.
Consider a vertex p of the polygon, with its predecessor p1 and successor p2.
The vectors between these points are
v1 = p1 - p
v2 = p2 - p
(The computation is componentwise for the x and y coordinates respectively).
In the shrunk polygon the vertex p is moved to p' along the line
which halves the angle a between the vectors v1 and v2.
The vector w in this direction is
w = v1 + v2
and the unit vector v in this direction is
v = w / |w| = (w_x, w_y) / sqrt(w_x*w_x + w_y*w_y)
The new point p' is
p' = p + k * v , i.e. :
p_x' = p_x + k * v_x
p_y' = p_y + k * v_y
where k is the shifting distance (a scalar).
If the vertex p is convex (as in the figure), then k >= 0 means
shrinking and k <= 0 means expanding.
If the vertex p is concave, then k >= 0 means
expanding and k <= 0 means shrinking.
What you want is polygon offset. If you want to use an existing library. Consider using Clipper
void OffsetPolygons(const Polygons &in_polys,
Polygons &out_polys,
double delta,
JoinType jointype = jtSquare, double MiterLimit = 2.0);
This function offsets the 'polys' polygons parameter by the 'delta' amount. Positive delta values expand outer polygons and contract inner 'hole' polygons. Negative deltas do the reverse.
Although I must add for a simple geometry like a Quadrilateral it is easy to do it from scratch.
Identity all four infinite lines that form the Quadrilateral
Offset the lines parallel to themselves
Compute intersection of these new lines
Just be careful of corner cases. When you offset a quadrilateral which has one very small edge. It will become a triangle on offset.
I agree with the answer of parapura rajkumar. I wanted to add that the solution of Jiri is not 100% correct because the vertex centroid of a quadrilateral is different to the area centroid of a quadrilateral, as it is written here. For the enlargement one would have to use the area centroid - or the much more elegant solution with the parallel lines mentioned by parapura rajkumar. I just want to add the following to this answer:
You can simply determine the outer points of the enlarged quadrilateral by computing the normal vectors of the vectors between the points of the original quadrilateral. Afterwards, normalize the normal vectors, multiply them with the offset and add them to the points of the original quadrilateral. Given these outer points you can now compute the intersection of the parallel lines with this formula.