I have a canvas where there are several polygons, what I want to do is try detect whether the polygons are overlapping. I'v looked around on various websites and most of what i'v found is to do with object collision - this for example, my polygons aren't moving so that's not going to be an issue.
I was wondering if someone could point me in the right direction on how to detect if they are overlapping. Is there a method that can calculate the space that's used on screen? or the region of the Polygon to compare the two?
So for example like the mock up here, the red shape overlaps the green one.
essentially all i want is to say yes they are overlapping or no they are not.
http://peterfleming.net84.net/Slice%201.png
Thanks in advance.
Pete
This library here (free and open source) will show polygon clipping: http://www.angusj.com/delphi/clipper.php
That said, if by polygons overlapping you mean at least one point of one is inside the other, you can test each polygon's point against the others by either looking at the point in point polygon problem or checking each polygons lines to see if it cuts across another polygon.
These methods will all work with different efficiency, try and see what's best for your situation.
However, your diagram seems to suggest you want to see if these polygons are 'side by side' or something similar. It would help to get clarification on this. Overlapping generally needs some coordinate plan to determine overlap against.
Assuming that each polygon is a Shape (either Path or Polygon) you could use the FillContainsWithDetail method of their RenderedGeometry to pairwise check interscetion.
I was having the same problem too and I used this implementation (which is heavenly inspired by this: C# Point in polygon ):
bool DoesPolygonsOverlap(IList<Point> firstPolygon, IList<Point> secondPolygon)
{
foreach (var item in firstPolygon)
{
if (IsPointInPolygon(secondPolygon, item))
{
return true;
}
}
foreach (var item in secondPolygon)
{
if (IsPointInPolygon(firstPolygon, item))
{
return true;
}
}
return false;
}
bool IsPointInPolygon(IList<Point> polygon, Point testPoint)
{
bool result = false;
int j = polygon.Count() - 1;
for (int i = 0; i < polygon.Count(); i++)
{
if (polygon[i].Y < testPoint.Y && polygon[j].Y >= testPoint.Y || polygon[j].Y < testPoint.Y && polygon[i].Y >= testPoint.Y)
{
if (polygon[i].X + (testPoint.Y - polygon[i].Y) / (polygon[j].Y - polygon[i].Y) * (polygon[j].X - polygon[i].X) < testPoint.X)
{
result = !result;
}
}
j = i;
}
return result;
}
Attention: The function was not very much tested and has a big potential for improvement. Please tell me if you find a bug/problem.
Related
I'm trying to get the corners of the following shape:
By corners I mean this (red dots):
The minimum quantity of points that can define this shape.
And I have implemented the following:
public Shape Optimize()
{
// If the vertices are null or empty this can't be executed
if (vertices.IsNullOrEmpty())
return this; // In this case, return the same instance.
if (!edges.IsNullOrEmpty())
edges = null; //Reset edges, because a recalculation was requested
// The corners available on each iteration
var corners = new Point[] { Point.upperLeft, Point.upperRight, Point.downLeft, Point.downRight };
//The idea is to know if any of the following or previous vertice is inside of the the array from upside, if it is true then we can add it.
Point[] vs = vertices.ToArray();
for (int i = 0; i < vertices.Count - 1; ++i)
{
Point backPos = i > 0 ? vs[i - 1] : vs[vertices.Count - 1],
curPos = vs[i], //Punto actual
nextPos = i < vertices.Count - 1 ? vs[i + 1] : vs[0];
// We get the difference point between the actual point and the back & next point
Point backDiff = backPos - curPos,
nextDiff = nextPos - curPos,
totalDiff = nextPos - backPos;
if (corners.Contains(backDiff) || corners.Contains(nextDiff) || corners.Contains(totalDiff))
AddEdge(curPos, center); // If any of the two points are defined in the corners of the point of before or after it means that the actual vertice is a edge/corner
}
return this;
}
This works rectangled shapes, but rotated shapes are very sharp, so, this code doesn't work well:
Blue pixels (in this photo and the following) are the vertices variable processed on Optimize method.
Green pixels are the detected corners/edges (on both photos).
But sharpness in a shape only defines the side inclination, so what can I do to improve this?
Also, I have tested Accord.NET BaseCornersDetector inherited classes, but the best result is obtained with HarrisCornersDetector, but:
Many edges/corners are innecesary, and they aren't in the needed place (see first photo).
Well, after hours of research I found a library called Simplify.NET that internally runs the Ramer–Douglas–Peucker algorithm.
Also, you maybe interested on the Bresenham algorithm, with this algorithm you can draw a line using two Points.
With this algorithm, you can check if your tolerance is too high, comparing the actual points and the points that this algorithm outputs and making some kind of percentage calculator of similarity.
Finally, is interesting to mention Concave Hull and Convex Hull algorithms.
All this is related to Unity3D.
My outputs:
And my implementation.
It's very important to say, that points needs to be sorted forcing them to be connected. If the shape is concave as you can see on the second photo maybe you need to iterate walls of the shape.
You can see an example of implementation here. Thanks to #Bunny83.
I'm creating a 2D game in Unity which has procedurally placed tiles. I want to simplify the collision geometry using Angus Johnson's Clipper library (specifically the union function), but I'm running into an issue with the library returning empty solutions and I'm not sure why.
For reference, here are the Polygon Colliders I've been using to test.
And here is a simplified version of the function I'm using to combine the geometry:
List<List<Vector2>> unitedPolygons = new List<List<Vector2>>();
Clipper clipper = new Clipper();
Paths solution = new Paths();
ClipperOffset offset = new ClipperOffset();
//Use a scaling factor for floats and convert the Polygon Colliders' points to Clipper's desired format
int scalingFactor = 10000;
for (int i = 0; i < polygons.Count; i++)
{
Path allPolygonsPath = new Path(polygons[i].points.Length);
for (int j = 0; j < polygons[i].points.Length; j++)
{
allPolygonsPath.Add(new IntPoint(Mathf.Floor(polygons[i].points[j].x * scalingFactor), Mathf.Floor(polygons[i].points[j].y * scalingFactor)));
}
bool succeeded = clipper.AddPath(allPolygonsPath, PolyType.ptSubject, true);
}
//Execute the union
bool success = clipper.Execute(ClipType.ctUnion, solution);
Debug.Log("Polygons after union: " + solution.Count);
//Offset the polygons
offset.AddPaths(solution, JoinType.jtMiter, EndType.etClosedPolygon);
offset.Execute(ref solution, 5f);
//Convert back to a format Unity can use
foreach (Path path in solution)
{
List<Vector2> unitedPolygon = new List<Vector2>();
foreach (IntPoint point in path)
{
unitedPolygon.Add(new Vector2(point.X / (float)scalingFactor, point.Y / (float)scalingFactor));
}
unitedPolygons.Add(unitedPolygon);
}
return unitedPolygons;
What I've discovered through debugging is that the first Execute (for the union) is returning an empty solution. I've figured out that the "BuildResult" function in the "Clipper" class is indeed running, and "m_PolyOuts" has data in it, but the "Pts" property of the "OutRec"s in that list are all null. I can't figure out where this happens or if they were ever set in the first place.
I'm convinced this is proper behavior and I'm just using the library wrong, but I can't find any documentation or examples explaining what I need to change to make the union succeed.
Thanks.
EDIT: I've narrowed it down a bit more. During "ExecuteInteral" in the Clipper class, the "Pts" lists aren't null until the "FixupOutPolygon" function is run. After that, all of the lists are null. "JoinCommonEdges" also makes a couple of the lists null, but not all of them.
I've been working on my own game project as well and stumbled upon similar problem with Clipper. What worked for me in this case was instead of writing this:
clipper.Execute(ClipType.ctUnion, solution);
... I specified PolyFillType for Execute method:
clipper.Execute(ClipType.ctUnion, solution, PolyFillType.pftNonZero, PolyFillType.pftNonZero);
I'm not sure why it worked for me but I think it's due to the fact that some Paths can share common edges so with the default pftEvenOdd filling rule it gets cut out.
I have a following problem and I will try to explain it.
I have a huge, political world map. I want to get a shape of selected country (in example Ghana, Nepal or Poland). How can I do that?
It depends what other information you are able to provide in advance. From your question and comments it sounds like you plan to predefine your shapes so the problem isn't too difficult. If you define each region by a set of points it's just a matter of checking if the selection (presumably a mouse click or similar) is within a polygon. There are a number of ways to do this. I think I've used one of the answers to the following question:
C# Point in polygon
Something like:
public static bool IsInPolygon(Point[] poly, Point clickedPoint)
{
if (poly.Length < 3)
{
return false;
}
Point p1, p2;
bool inside = false;
Point oldPoint = new Point(poly[poly.Length - 1].X, poly[poly.Length - 1].Y);
for (int i = 0; i < poly.Length; i++)
{
Point newPoint = new Point(poly[i].X, poly[i].Y);
if (newPoint.X > oldPoint.X)
{
p1 = oldPoint;
p2 = newPoint;
}
else
{
p1 = newPoint;
p2 = oldPoint;
}
if ((newPoint.X < clickedPoint.X) == (clickedPoint.X <= oldPoint.X)
&& (clickedPoint.Y - (long)p1.Y) * (p2.X - p1.X) < (p2.Y - (long)p1.Y) *(clickedPoint.X - p1.X))
{
inside = !inside;
}
oldPoint = newPoint;
}
return inside;
}
I haven't tested the above code so I'd make sure to test it properly if you use it.
If you can't predefine the shapes then you'll probably have to use some sort of analysis method to pick out the shapes. If you use a clean map with solid lines it won't be too difficult. Any kind of flood fill algorithm should be able to pick out individual countries (obviously you'd have to deal with special cases like when a country is made of two distinct regions). From there getting a set of points from each shape can be done using a simple marching squares algorithm. If desired, you can then reduce the number of points depending on the level of accuracy you need.
We are currently using the following algorithm to detect if a geographic point is inside a complex polygon or not. This works fine, except when the polygon crosses the 180° longitude line.
For example the point (-170, 60) is not detected in polygon 160,65,0 160,15,0 -160,15,0 -160,65,0 160,65,0
Look at the following image:
[Img]http://tinypic.com/r/14x2xl1[/img]
I want everything in the red box. Not the yellow box!
public static bool IsCoordinateInPolygon(IList<KMLCoordinate> polygon, KMLCoordinate testPoint)
{
bool result = false;
int j = polygon.Count - 1;
for (int i = 0; i < polygon.Count; i++)
{
if (polygon[i].Latitude < testPoint.Latitude && polygon[j].Latitude >= testPoint.Latitude || polygon[j].Latitude < testPoint.Latitude && polygon[i].Latitude >= testPoint.Latitude)
{
if (polygon[i].Longitude + (testPoint.Latitude - polygon[i].Latitude) / (polygon[j].Latitude - polygon[i].Latitude) * (polygon[j].Longitude - polygon[i].Longitude) < testPoint.Longitude)
{
result = !result;
}
}
j = i;
}
return result;
}
Does anybody have a better algorithm?
Spheric coordinate system has it quirks
To avoid them use 3D orthogonal/orthonormal cartesian coordinate system instead
convert your polygon vertexes and geolocation
so (long,lat,alt) -> (x,y,z). here you find how to do it. You do not need to apply local transform just the first spheric to 3D cartesian transformation (bullet #1.)
use any inside polygon test ...
I usually count the number of intersections between line cast from your geolocation to any direction and polygon boundary lines.
if it is odd then point is inside
if it is even then the point is outside
if point lies on any line of polygon then it is inside
if your casted line hits any vertex then either take in mind (do not count multiple hits at this vertex) or change the direction a bit and try again
[Notes]
Do not forget to handle all as 3D vectors not 2D !!!
I've got a WPF component showing a collection of Equipments. Each of these Equipments contains one or more Coordinate. On MouseMove over my component a function is called checking if the mouse currently hovers within the bounds of any Coordinate of any Equipment. If so the Equipment in question is returned indication that a pop-up window with text information about the Equipment should be shown.
Aside from this, all Coordinates contain an ImageDrawing that shows a symbol for each Equipment (the symbol the user hovers the mouse over to see the text pop-up). These are put in a separate DrawingGroup to speed up rendering. Another benefit of this is that if some Equipments are to be hidden, we can just remove their Coordinates' ImageDrawing from the DrawingGroup, and they will still be left in the list of Equipments (as they should).
However, this does not hinder the pop-up from showing when the mouse hovers over the Coordinate, as this is separate from the DrawingGroup. So, to check if a text pop-up is to be shown, I have to check both if the mouse is within the bounds of any Coordinate, and also check if the ImageDrawing for that Coordinate is in the DrawingGroup.
On to my question (tl;dr): which way to iterate through all these items would be fastest? I've got :
List<Equipment> equipments;
And for each of these (this list 9 times out of 10 holds one item, and never more than five)
List<Coordinate> coordinates;
For each of these Coordinates I've got to check if their ImageDrawing is in the DrawingCollection (which in this case is DrawingGroup.Children) which, according to msdn, is an ordered collection of Drawing objects.
To do this, I started out with this:
foreach (Equipment equipment in equipments)
{
foreach (Coordinate coordinate in equipment.Coordinates)
{
ImageDrawing image = coordinate.ImageDrawing;
if (image != null)
{
if (currentDrawingGroup.Children.Contains(image))
{
if (image.Rect.Bottom > y &&
image.Rect.Top < y &&
image.Rect.Left < x &&
image.Rect.Right > x)
{
return equipment;
}
}
}
}
}
But thought that this becomes to many iterations in the cases where a lot of equipments are hidden, and that I could change it to this (since Children.Contains(image) brobably iterates behind the scenes anyway):
foreach (var child in currentDrawingGroup.Children)
{
foreach (Equipment equipment in equipments)
{
foreach (Coordinate coordinate in equipment.Coordinates)
{
ImageDrawing image = coordinate.ImageDrawing;
if (image != null)
{
if (image == child)
{
if (image.Rect.Bottom > y &&
image.Rect.Top < y &&
image.Rect.Left < x &&
image.Rect.Right > x)
{
return equipment;
}
}
}
}
}
}
I know this question is way too long and the optimizations I'm looking at here probably don't matter much in the long run. But is there a way this could be done without so many loops in loops? I feel like there should be a LINQ-expression helping me somewhere, though I don't know whether any of those are faster than a foreach loop. The way things are sorted (Equipments being hid by removing them from the DrawingGroup and such) is beyond my control and hard to change. Thanks a lot in advance.
if some equipments are to be hidden, wouldn't it be cleaner to let the equipment himself manage its visibility? This way you just ask the equipment if it is visible in the first place and skip it if not?
Thus your equipments are also always present, but visible or not and when you mouseover it you won't have to check if the coordinates are in the DrawingGroup or not... it's already handled for you.
Regarding iterating vs Contains(), the contains function is a linear search so it will be the same as iterating through the list.