Consider the picture :
Given the black "irregular line" segments, I want to have the green boundary surrounding them.
My Attempt to solve :
Note, that I need to capture the concavity. Thus the convex hull algorithm will not work. I have used some interpolations, to draw the straight line segments indicated by overlayed red stripes. Then I attempted to run a sweep algorithm. These stripes are roughly parallel, if that helps.
That is, their headings lay within some small interval.
Heading H in { angle alpha - x , angle alpha +x } union { angle alpha - x + 180 , angle alpha +x+ 180 } for some small x
However, it needs to be guaranteed, that the green boundary should not intersect any red lines.
So, my naive attempt was :
Start at leftmost vertex of all lines.
Pick the line attached to the vertices. (if more vertices, then take the top most and bottom most)
Use this line to "partition" the plane. (or two lines or two "separatices", if 2 vertices)
Pick the next leftmost unvisited vertices.
See if the vertex lies above the top separatrix or below the bottom separatrix.
If above the top separatrix, add it to a set called "top chain", if below the bottom one, the add to bottom chain.
But this runs into problems.
The problem
To determine whether the newly picked point is at the top or at the bottom of a separatrix, I use the vector product method. But consider this :
Imagine you are at the green square. Your separatrix now is the black line, vector pointing to the left, as the arrow shows.
At this stage, you compare this with the green vector showing the next vertex to be picked on the blue line. The vector method, intended to measure angles between -Pi to Pi will consider this a negative value. Thus the point, although necessary, will not be picked up.
If you reverse the direction of the vector, then the Magenta line will not be picked up.
Question
How to efficiently solve this problem? I am using D, so bonus if there is a D library capable of this.
Filling concavities is a somewhat arbitrary operation. Have a look at Alpha-shapes, a generalization of the convex hulls.
https://en.wikipedia.org/wiki/Alpha_shape
Related
I have a set of arbitrary rotated filled rectangles plotted on a Cartesian grid (2D integer array, each cell is either 0 - empty space or 1 - rectangle pixel) and would like to test whether a particular rectangle has any obstacles around it given that the center of rectangle is known along with the coordinates of its four edges.
For example, let's say we want to test if rectangle is obstacle free 5 units from either of its edges.
Rectangles marked with green dot are ok, while the unmarked are clearly collide.
It seems trivial for non rotated rectangles however, I am having a hard time coming up with algorithm which can handle rotated rectangles.
Simply looping starting from the center till we hit empty space and then checking for obstacles in the empty space doesn't seem to work if rectangles are touching each other.
Since you seem to be operating with an image-oriented mindset, you can use image processing.
Apply a radius-2.5 dilation filter to your image.
Treat the image as a graph where there is an edge between two pixels if they both have a red value above some threshold.
Any rectangles that have a path between them are at most 5 apart. (Although, note that this will give you the transitive closure of the "too close" relationship.)
Please take a look at pic1 above first.
2 points combine a line, let's call it LineAB, and we can get a normal from our eye sight direction, let's call it view-direction, vector(lineAB) X view-direction, we can get a normal named plane-normal. in the pic1, plane-normal is directed to the top (green arrow), and the plane with plane-normal cut the map into 2 parts.
As the point C is on the same direction of the plane-normal, we regard it as inside, let's return true. Point D is on the anti-direction of plane-normal, it is outside, return false.
My problem is in the pic2 as following:
Now, there are many points A,A1,A2...A5,... An, build many lines such as lineAA1, line A1A2, ... LineAn-1An (one condition is: every angle between 2 adjacent lines are equal to or more than 90 degree) and plus with view direction (the direction from our eye sight), we can get many planes PAA1, PA1P2, ... PAn-1An which also cut the map to 2 parts.
I need to check one point is inside or outside.
for example, point C is inside but point D is outside.
Regarding one plane separating the dim(3)-space isn't difficult, to consider a piecewise assembled dim(2)-plane we need to dive deeper:
The problem may be reduced to separating the dim(2)-space.
If only for the calculation of the normal the 3rd dimension is considered, then that can solved in a different way:
Let v = (a,b) be the vector of a lineAB. The normal is (b, -a) or (-b, a) respectively.
If you want to check only if a point is within a polygon, just use the ray-casting-algorithm.
When it comes to dividing the dim(2)-space into two separate spaced by your polygonal chain, it won't be enough to check if the point is on the positive directon of the normals on each part line(A[i-1])(A[i]):
Polygonal chain
Point P is positive with respect to normal N0, but negative w.r.t. normal N1.
Also, the upper angles are all above 90° (some counter angles are also displayed), but the polygon chain isn't convex either w.r.t. the upward y-axis.
To solve your issue, you can use the ray-casting-algorithm, going towards negative y-direction, i.e. "downwards", and see if the amount of intersections is odd.
If the line ends at a higher x-coordinate than the start point, an odd amount of intersections means "true"
If the line ends at a lower x-coordinate than the start point, an odd amount of intersections means "false"
You can find if a ray from point C (with view direction) intersects with one of the segments AiAi+1. It could be done with binary search by X-coordinate (to find potential segment quickly)
Given the picture below:
The position of A and B is known, the rest of the picture is just raw pixel data (only red and white pixels). What algorithm can I use to erase the part to the right of AB?
Having two points define a line. The equation of line separates pixels that have to be removed. If you want to delete points in a way that new border is smoothly following the curve of the shape you need some way to interpolate the shape border (based on the information of other points from the border). This interpolation have to pass trough two black points. Now, if you have interpolation you can calculate the interpolation curve between two black points and set to white all points right from the curve. I can suggest to use at least one more point from the curve and use some cubic spline interpolation.
EDIT:
Based on you comment.
Then the algorithm is simple: Keep a pointer that starts from lower point (A) and moves along the border (A') until it reaches the other point. This can be done by checking the neighbors of current pointer location and comparing colors. Now, when you pointer moves remove the line of red points from pointer (A') to line (blue) defined between to points A-B (in green). There is a pointer position when the row is the same as the other black point (e.g A' and B are on the same row). Then start a new pointer from B point in the same way tracing the border and remove the red pixels between two pointers A' and B'.
You need to draw the line from A to B and then start a flood fill on one of the red pixels to the right of the line.
I'm struggling to find a rock solid solution to detecting collisions between a circle and a circle segment. Imagine a Field of View cone for a game enemy, with the circles representing objects of interest.
The diagram at the bottom is something I drew to try and work out some possible cases, but i'm sure there are more.
I understand how to quickly exlude extreme cases, I discard any targets that don't collide with the entire circle, and any cases where the center of the main circle is within the target circle are automatically true (E in the diagram).
I'm struggling to find a good way to check the rest of the cases. I've tried comparing distances between circle centers and the end points of the segments outer lines, and i've tried working out the angle of the center of the target circle from the center of the main circle and determining whether that is within the segment, but neither way seems to catch all cases.
Specifically it seems to go funky if the target circle is close to the center but not touching it (somewhere between E and B below), or if the segment is narrower than the target circle (so that the center is within the segment but both edges are outside it).
Is there a reliable method for doing this?
Extra info: The segment is described by position P, orientation O (whose magnitude is the circle radius), and a view size, S.
My most successful attempt to date involved determining the angles of the vectors ca1 and ca2, and checking if either of them lies between the angles of vectors a1 and a2. This works for some cases as explained above, but not situations where the target circle is larger than the segment.
Edit 2
After implementing the best suggestion from below, there is still a false positive which I am unsure how best to eliminate. See the pink diagram below. The circle in the lower right is reporting as colliding with the segment because it's bounds overlap both half spaces and the main circle.
Final Edit
After discovering another edge case (4th image), i've settled on an approach which combines the two top answers from below and seems to cover all bases. I'll describe it here for the sake of those who follow.
First exclude anything that fails a quick circle-to-circle test.
Then test for collision between the circle and the two outer lines of the segment. If it touches either, return true.
Finally, do a couple of point-to-halfspace tests using the center of the circle and the two outer lines (as described by Gareth below). If it passes both of those it's in, otherwise return false.
A. Check if it is intersecting the whole cirlce.
B. Check if it is intersecting either of the straight segment lines.
C. If not, check if the angle between the circle centres lies in the angular range of the segment (dot product is good for this).
Intersection requires A && (B || C)
A circular segment (with central angle less than 180°) is the intersection of three figures: a circle, and two half-planes:
So a figure intersects the circular segment only if it intersects all three of these figures. [That's only if but not if; see below.]
Circle/circle intersection is easy (compare the distance between their centres with the sum of their radii).
For circle/half-plane intersection, represent the half-plane in the form p · n ≤ k (where p is the point to be tested, n is a unit vector that's normal to the line defining the half-plane, and k is a constant). Then a circle with centre x and radius r intersects the half-plane if x · n ≤ k + r.
(If you need to handle a circular segment with central angle greater than 180°, split it into two segments with central angle less than 180°. If I understand your problem description correctly, you won't need to do this, since your field of view will always be less than 180°, but it's worth mentioning.)
Edited to add: as pointed out by beeglebug, a circle can intersect all three figures without intersecting their intersection. Oops. But I believe that this can only happen when the circle is behind the centre of the segment, as in the diagram below, and in this case we can apply the separating axis test for convex figures.
The separating axis theorem says that two convex figures fail to intersect if there exists a line such that one figure falls entirely on one side of the line, and the other figure on the other.
If any separating axis exists in this case, then the axis that's perpendicular to the line between the centre of the circle and the centre of the segment is a separating axis (as shown).
Let the centre of the segment be at the origin, let the circle have centre x and radius r, and let the two half-planes have (outward) normals n₁ and n₂. The circle is "behind" the segment if
x · n₁ > 0 and x · n₂ > 0
and the axis separates it from the segment if
|x| > r
I have a map of polygons, for example:
alt text http://img13.imageshack.us/img13/2808/output.png
(sorry the image isn't so great, I'll try and get better one later)
The green lines are the shortest path from between the connected shapes. What I need to do is find the green lines that form the outer edge and which direction I need to traverse them in to circle CCW.
The polygons will always be convex and the lines will never cross. The lines are defined in terms of there X,Y endpoints. I have an index from polygons to related lines and it is tagged with which end is attached.
The reason I need this is because I need to find the edges forming each of the interior section and my wall following solution fails/crashes for the exterior section.
0. Clarification: I am not sure whether the green connections between the convex polygons are provided as part of the input or whether the program has to determine the appropriate green connections as shortest paths between polygons itself. I just noticed that your image is missing a green edge at the top right corner (that would be part of outer border if allowed).
1. Solution: Always choose the next edge. If your input specifies which green lines are allowed and which not, then you simply traverse the boundary of the outside component by finding one start point (e.g. by taking the polygon corner with lowest x-coordinate), ordering the green edges that depart from the polygon of this current point in counter-clock-wise order and choosing the edge immediately following the current point in CCW order. Now, take the other end of that edge as "current point" and repeat the same method to find the next edge... until you return to the first polygon.
2. Solution: Start with the convex hull. If you allow all possible green lines, you don't need them as input. The convex hull of all polygon edges is a first approximation to your solution (details of finding --- see further below): The convex hull contains real polygon edges (let's think of them as "black": They are part of your final solution, already in correct order) and edges that connect polygons (Let's think of them as red: They need to be replaced by green edges and possibly parts of other polygons).
Completing the 2nd solution: divide and conquer: Now, we need to replace each red edge by a combination of green and black edges. We just focus on one red line at a time (and apply the same method for each red line that we may have).
So we have one red line that contains two polygons that have one green line (the shortest connection between them) --- the four edges of these two lines define a quadrangle. If none of the other polygon corners are in this quadrangle, you are done: replace the red edge by the green edge and any black edges on the polygons to get to the connecting points.
But if you find polygon in the quadrangle, select from them the closest to the red edge. Move the red edge towards that point --- so that the new point cuts the red edge into two red edges. These two new red edges replace the one old red edge: apply this method recursively to both of them. Their corresponding quadrangles are much smaller and contain less polygon edges.
As you keep applying this divide and conquer method you will eventually end up with no remaining red edges (because you loose one red edge each time you find an empty quadrangle).
Convex hull: This is a difficult problem in n dimensions but easy in two dimensions: If you search the net or browse SO, you surely find a better solution than I can think of right now, but here is one that comes to mind (again: divide and conquer): Find one point A with maximal x-coordinate and one point B with minimal x-coordinate, connect them by two directed "blue" edges: A->B and B->A. Divide your points into two sets: those at the right hand side of the edge A->B and those on the left hand side (which really is the right hand side of B->A). We repeatedly replace each blue edge until we find the convex hull:
Take one blue edge A->B and look at the points its right hand side. If there are none, then this blue edge is really black (part of your solution). Otherwise, take the point C furthest to the right of the blue edge and replace the edge A->B by two blue edges A->C and C->B. Divide the points that were at the right hand side of A->B into those that are at the right hand side of A->C, those that are at the right hand side of C->B and those that are neither (they are ignored). Repeat until all blue edges are replaced.