【问题标题】:Find polygons from set of lines从一组线中查找多边形
【发布时间】:2013-04-08 03:41:01
【问题描述】:

我在 2D 中工作。

我的用户可以绘制一些线和线段,我将它们存储在自定义对象类中,但基本上存储在 startX-Y 和 endX-Y 中。

现在我想找到,实际上只有球在里面的多边形,但是在阅读和研究了一些算法等之后。我想我必须找到所有的多边形,然后找到正确的那个。

有没有人有一些示例代码,c#,java objective-c !? 用伪代码解释了好几次,没看懂。

【问题讨论】:

  • 添加新行是否总是将 1 个区域分割成两个区域,或者是否可以在屏幕中间有一条随机线,它不会将屏幕分割成两个完整的部分?如果是第一种情况,那么这个问题就容易多了。
  • 一行总是将一个区域一分为二,所以它总是以视图的边界或另一行结束
  • 用户是随着游戏的进行而添加台词,还是台数和位置是固定的?
  • 他在进行中添加它,所以在我找到多边形后我计算多边形的面积 - 这就是我需要的......你有一个例子,一些我可以使用的代码或什么别的?请!
  • 我认为算法还不错,但我只想确认,当用户绘制新线时,您如何确保端点位于现有线上或另一行?

标签: objective-c algorithm geometry 2d polygon


【解决方案1】:

概述

这里有许多不同的有趣问题:

1.假设您在屏幕上有一组线,并且用户从任意点放置手指并将其拖动到任意端点,我们需要形成一条不交叉线的新线以及起点和终点的位置实际上位于现有线或边界上。

2。下一个问题是我们如何维护一组活动的“相关”线段,这些线段形成了球所在的凸包。

3.最后,鉴于我们有一组活动的线段,我们如何找到这个形状的面积。

现在看来您已经回答了第 1 部分。所以我们专注于第 2 部分和第 3 部分。

对于第 2 部分,我们也有兴趣询问:

4.如果我们有一个凸包和一个点,我们如何确定该点是否在凸包中。

4Find if a point is inside a convex hull for a set of points without computing the hull itself的解决方法参考这里

如果您小心使用所使用的数据结构,则可以有效地完成完整的实现。这是一个简单的练习。如果我在这里做错了什么或者你不明白,请告诉我。我期待在您的游戏准备就绪后玩它!

第 3 部分的解决方案

实际上 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,y) 点围绕正 x 轴的角度对所有点进行排序,那么我们只需固定第一个点,然后连续遍历剩余的点对并找到这些三角形的面积。作为一个简单的练习,为什么需要这个排序步骤(提示:画一张图片)。

第 2 部分的解决方案

现在最难的部分是 2。鉴于我们有一组围绕球的活动线段,我们如何添加一条新线,然后调整活动集中线段的大小和数量。请注意,在游戏开始时,活动集中恰好有 4 行是屏幕的边界(如果您喜欢归纳,这将是我们的基本情况)。

现在假设我们有一个包含球的任意活动集,并且用户添加了一条新线,我们假设它正好碰到两个现有的线段并且不跨越任何线段(通过第 1 部分算法)。现在我们需要修改活动集。通过算法 1,我们知道该点击中了哪些线段。所以活动集可以改变的唯一方法是,如果这点命中的两条线段都在活动集中(画一张图来看看这个事实)。

现在假设新的线段在活动集中命中两条线(这意味着它实质上将活动集分成两个凸包)。如果我们以旋转顺序维护我们的活动集(也就是说,我们确保它总是按关于正 x 轴的角度排序),我们只需以保持这种排序顺序的方式添加我们的新点。因此,例如假设我们的活动集(将线段折叠为单线)的点是:

[(x1, y1), (x2, y2), (x3, y3), ..., (xn, yn), (x1, y1)]

而我们要添加新的线段 ((x', y'), (x'', y'')),我们的新集合看起来像:

[(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。

第 2 部分的数据结构

首先我们需要类线段和点(为了简单起见,我避免实现诸如等于、哈希码之类的东西):

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 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)); 

在代码中确实很难理解,但上面所有内容的顺序很重要,因为我们希望保持按逆时针顺序排序。

这是一个练习,您可以如何加快速度(实际上您不需要构建两个活动集,只需首先确定球是在新段的上方还是下方,然后相应地构建左侧或右侧的活动集) .

【讨论】:

  • (1)和(3)的解决方案完全清楚。在第 2 部分中,我不太确定.. 我必须如何“存储”我的活动集?我明白了,如果添加了一条新行,将活动集分成两部分,依此类推,但是您有一些代码可以让我更好地解释一下吗? “简单地使用算法 4”是什么意思??
  • 好的,第 2 部分现在也很清楚了,谢谢,代码和绘图有很大帮助 - 但是通过搜索球在里面的正确集合,您指的是什么“答案”?
  • stackoverflow.com/questions/15870888/… ?这是天真的方法。对于您的问题,它完全没有必要。您只需要检查球是高于还是低于新闻片段stackoverflow.com/questions/3838319/…。请注意,您还需要注意坡度,以确保选择正确的区域。
  • 哦,好的,我会尝试一切然后回复..非常感谢,我很感激!
猜你喜欢
  • 1970-01-01
  • 2013-07-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-21
  • 2014-01-22
  • 1970-01-01
相关资源
最近更新 更多