想象一下,我有4个点的坐标,形成一个多边形。这些点在C#中使用PointF表示。如果我有2个多边形(使用8个点),我怎么知道它们是否相交?
Rectangle类有一个名为IntersectsWith的方法,但我找不到类似于GraphicsPath或Region的方法。
任何建议都将不胜感激。
MOSH
答案 0 :(得分:5)
正如查理已经指出的那样你可以使用分离轴定理。 查看this article以获取C#实现和多边形碰撞检测示例。
我也回答了这个问题here,该问题涉及C#中的2D碰撞。
答案 1 :(得分:4)
严格地说,建议算法的其他答案可能是你最好的选择。但是除了性能之外,你提到你找不到像GraphicsPath或Region这样的IntersectsWith。但是,有一种Intersect方法可以将区域更新为自身与另一个区域或路径的交集。您可以创建两个区域Intersect()与另一个区域,然后测试Region.IsEmpty()。
但我想这可能是一种非常缓慢的方法,如果在循环中完成,可能会导致很多分配。
答案 2 :(得分:1)
如果您的多边形是凸的,那么您应该可以使用separating axis theorem。有一个演示here(它在动作脚本中,但代码应该很容易移植到c#)
这真的不是我的领域,但我希望无论如何都有帮助。
答案 3 :(得分:1)
这是一个老问题,但我想我也会分享我的解决方案。 Region.IsEmpty()需要一个Graphics上下文,据我所知,它仅用于执行像素精度命中测试。这对许多情况来说并不理想。更好的解决方案是使用Angus Johnson的Clipper库。根据我的经验,这是一个快速测试的库。您可以提供自己的精度,它可以处理极其复杂的多边形。
http://www.angusj.com/delphi/clipper.php
有一个C#实现。您需要做的是执行交叉操作,就像System.Drawing.Region方法一样。然后检查操作的结果。如果它是空的,则没有交叉点。如果它包含数据,则数据是交叉点。
http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Types/ClipType.htm
您会发现一些对此有用的方法。
private static int scale = 1000; //your desired precision
public static List<List<IntPoint>> ConvertToClipperPolygons(GraphicsPath path)
{
var Polygon = new List<IntPoint>();
var Polygons = new List<List<IntPoint>>();
var it = new GraphicsPathIterator(path);
it.Rewind();
bool isClosed;
int startIndex;
int endIndex;
for (int i = 0; i < it.SubpathCount; i++)
{
var PointCount = it.NextSubpath(out startIndex, out endIndex, out isClosed);
var Points = new PointF[PointCount];
var Types = new byte[PointCount];
it.CopyData(ref Points, ref Types, startIndex, endIndex);
Polygons.Add(new List<IntPoint>(Points.Select(x => new IntPoint(Convert.ToInt64(x.X * scale), Convert.ToInt64(x.Y * scale)))));
}
it.Dispose();
return Polygons;
}
并执行交叉点
public static GraphicsPath intersect(ref GraphicsPath p1, ref GraphicsPath p2)
{
List<List<IntPoint>> polygonB = ConvertToClipperPolygons(p1);
List<List<IntPoint>> polygons = new List<List<IntPoint>>();
List<List<IntPoint>> polygonA = ConvertToClipperPolygons(p2);
Clipper c = new Clipper();
c.AddPolygons(polygonB, PolyType.ptSubject);
c.AddPolygons(polygonA, PolyType.ptClip);
c.Execute(ClipType.ctIntersection, polygons, PolyFillType.pftEvenOdd, PolyFillType.pftEvenOdd);
return ConvertClipperToGraphicsPath(polygons);
}
public static GraphicsPath ConvertClipperToGraphicsPath(List<List<IntPoint>> path)
{
GraphicsPath returnPath = new GraphicsPath();
for (int i = 0; i < path.Count; i++)
{
returnPath.AddPolygon(ToPointList(path[i]).ToArray());
}
return returnPath;
}
private static List<PointF> ToPointList(List<IntPoint> pointList)
{
List<PointF> newList = new List<PointF>();
foreach (IntPoint pt in pointList)
{
newList.Add(new PointF(((float)pt.X / (float)scale), ((float)pt.Y / (float)scale)));
}
return newList;
}