我正在使用 2D 。
我的用户可以绘制一些线条&线段,我存储在自定义对象类中,但基本上在startX-Y& endX-Y。
现在我想找到,实际上只是球在里面的多边形,但是在阅读和研究了一些算法之后,我想我必须找到所有的多边形并在那之后找到合适的多边形。
有没有人有一些示例代码,c#,java objective-c! 我尝试了几次伪代码解释,我不明白。
答案 0 :(得分:2)
这里有许多不同的有趣问题:
<强> 1。假设您在屏幕上有一组线条,并且用户将手指从任意点放置并将其拖动到任意终点,我们需要形成一条新线,该线不会越过线以及起点和终点实际上位于现有线路或边界上。
<强> 2。接下来的问题是我们如何维持一组有效的&#34;相关的&#34;形成球所在的凸包的线段。
第3。最后,鉴于我们有一组有效的线段,我们如何找到这种形状的区域。
现在看来你已经回答了第1部分。所以我们关注第2和第3部分。
对于第2部分,我们也有兴趣询问:
<强> 4。如果我们有一个凸包和一个点,我们如何确定该点是否在船体中。
我们在这里指的是4 Find if a point is inside a convex hull for a set of points without computing the hull itself
的解决方案如果您对所使用的数据结构非常小心,可以有效地完成全部实现。这是一个简单的练习。如果我在这里做了一些不正确的事情或者您不理解某些事情,请告诉我。我很期待在准备好的时候玩你的游戏!
实际上3很容易从2开始,因为如果我们知道包含球的活动线段集(这是一对元组对((x_1start,y_1start),(x_1end,y1_end))),那么两种计算面积的方法。第一个是做一个简单的算法来计算由这个元组列表中的所有起点和终点形成的凸包的面积。查找更复杂的区域算法,但如果找不到,请注意n边的船体有n-2个不重叠的三角形,三角形的面积很容易计算(http://www.mathopenref.com/polygontriangles.html)。注意:
area = abs((x1-x2)*(y1-y3)-(y1-y2)*(x1-x3)) for triangle (x1,y1)(x2,y2)(x3,y3)
现在,如果按照关于正x轴的角度对所有(x,y)点进行排序,那么我们只需修复第一个点,然后连续遍历剩余的对并找到这些三角形的区域。作为一个简单的练习,为什么需要这个排序步骤(提示:画一幅画)。
现在困难的部分是2.鉴于我们有一组活动的线段包围球,我们如何添加一个新线,然后调整我们的活动集内的线段的大小和数量。请注意,在游戏开始时,活动集中恰好有4行是屏幕的边界(如果您喜欢感应,这将是我们的基本情况)。
现在假设我们有一个包含球的任意活动集,并且用户添加了一个新行,我们假设它恰好击中了两个现有的线段,并且没有穿过任何线段(通过第1部分算法)。现在我们需要修改活动集。通过算法1,我们知道这一点击中了哪些线段。因此,活动集可以改变的唯一方法是,如果此点击中的两个线段都在活动集中(绘制图片以查看此事实)。
现在假设新线段击中活动集内的两条线(这意味着它基本上将活动集拆分为两个凸包)。如果我们以旋转顺序维持我们的活动集(也就是说我们确保它总是按正x轴的角度排序),我们只需要以维持这种排序顺序的方式添加我们的新点。因此,例如假设我们的活动集的点(将线段折叠为单行)是:
[(x1, y1), (x2, y2), (x3, y3), ..., (xn, yn), (x1, y1)]
我们要添加新的细分受众群((x&#39;,y&#39;),(x&#39;&#39;,y&#39;&#39;))以及我们的新细分看起来像:
[(x1, y1), (x2, y2), (x', y'), (x3, y3), ..., (xn, yn), (x'', y''), (x1, y1)]
然后现在形成了两个凸包:
1. [(x1, y1), (x2, y2), (x', y'), (x'', y''), (x1, y1)]
2. [(x', y'), (x3, y3), ..., (xn, yn), (x'', y'')]
所以唯一剩下的问题是我们的新活动集。只需使用算法4。
首先我们需要类线段和点(为了简单起见,我要避免实现equals,hashcode之类的东西):
class Point {
public int x;
public int y;
}
class LineSegment {
public Point lineStart;
public Point lineEnd;
}
现在我们可以代表我们的活动集数据结构:
class ActiveSet {
public List<Line> thesortedlist;
}
现在我们首先用四行初始化我们的活动集,并将屏幕中心表示为(0,0):
LineSegment TopBorder = new LineSegment(new Point(10, 10), new Point(-10, 10));
LineSegment LftBorder = new LineSegment(new Point(-10, 10), new Point(-10, -10));
LineSegment BtmBorder = new LineSegment(new Point(-10, -10), new Point(10, -10));
LineSegment RightBorder = new LineSegment(new Point(10, -10), new Point(10, 10));
ActiveSet activeset = new ActiveSet();
activeSet.theActiveLines.add(TopBorder);
activeSet.theActiveLines.add(LftBorder);
activeSet.theActiveLines.add(BtmBorder);
activeSet.theActiveLines.add(RightBorder);
现在说用户添加一条从点(0,10)到(10,0)的线,所以这是一个对角线(称之为newSegment),新的有效集将如下所示:
------
- -
- -
- -
- -
- -
- B -
- -
-----------
注意右上角的切口,B表示球。现在通过算法1,我们知道TopBorder和RightBorder是被击中的。现在这两个都在activeset中(我们可以通过使用hashmap来更快地测试成员资格)。现在我们需要形成两个活动集,如下所示:
ActiveSet activesetLeft = new ActiveSet();
ActiveSet activesetRight = new ActiveSet();
现在,为了构建这些集,请按以下步骤进行操作:
List<LineSegment> leftsegments = activeset.GetSegmentsBetween(TopBorder,
RightBorder);
List<RightSegment> rightsegments = activeset.GetSegmentsBetween(RightBorder,
TopBorder);
此函数GetSegmentsBetween(LineSegment firstline,LineSegment secondline)应该只在列表中找到第一行,然后返回所有元素,直到找到第二行(这可能需要在列表中进行两次传递)。它的返回值不应包括这些第一行或第二行。现在假设我们有activesetLeft和activesetright,我们需要执行以下操作:
activesetLeft.append(new Line(secondLine.lineStart, newSegment.lineStart));
activesetLeft.append(newSegment);
activesetLeft.append(new Line(newSegment.lineEnd, firstLine.lineEnd));
activesetRight.append(new Line(firstLine.lineStart, newSegment.lineEnd));
activesetRight.append(new Line(newSegment.lineEnd, newSegment.lineStart));
activesetRight.append(new Line(newSegment.lineStart, secondLine.lineEnd));
在代码中真的很难理解,但是上面所有内容的顺序很重要,我们希望按逆时针顺序保持排序。
这是一个练习如何加快速度(事实上你不需要建立两个活动集,只是首先弄清楚球是在新段之上还是之下并相应地构建左或右主动集)