5

http://img853.imageshack.us/img853/2475/picture1eu.jpg

我有一个点/坐标的 ArrayList,它代表一个直线多边形。我现在想使用 ArrayList 中存储的点将这个形状分解为矩形。

我已经开始了一个算法,但我无法完成它,我觉得必须有一个更简单的方法:

ArrayList 称为“allCoordinates”。
ArrayList "xMatch" 和 "yMatch" 是 allCoordinates 的子集。

算法:

ArrayList yMatch = All matching Coordinates in respect to 'y'


所以在上图的情况下:
(Set 1=[x1, y1]-[x8, y8],
Set2=[x7, y7]-[x2, y2],
Set3=[x4, y4][x5, x5 ],
Set4=[x3, y3][x6, x6])

ArrayList xMatch = All matching Coordinates in respect to 'x'


所以在上图的情况下:
(Set 1=[x1, y1]-[x2, y2],
Set2=[x3, y3]-[x4, y4],
Set3=[x5, y5][x6, x6 ],
Set4=[x7, y7][x8, x8])



所以现在我有两个arrayLists,所有垂直边缘和所有水平边缘。现在我需要一些方法来检查它们是否都连接在一起?就像我说的那样,必须有一个更简单的方法......?

编辑:

我能否澄清一下,必须使用在现有坐标上开始和结束的相交线来形成矩形。例如,可以从 (x6, y6) 水平绘制一条线,并在边缘 (x1,y1)-(x8,y8) 上结束。这条线将从现有坐标开始,但不会在现有坐标上结束。因此,该行将无效。

4

4 回答 4

7

正如您现在可能已经注意到的那样,我不断地回到这个问题。部分原因是我喜欢解决这类问题,但也因为我找不到一个看起来如此简单的优雅算法,这让我有点恼火。

好吧,不要被愚弄了,这不是一个可以通过一些简单的点操作来解决的小问题,尽管看起来确实是这样。造成这种情况的部分原因是,尽管您认为您只是在处理点,但您隐含地也在操作线段和由它们包围的区域,它们也有自己的约束(即线段应该始终形成一个或更闭合的链,并且除了在我们定义它们的顶点之外不能相交)。

此外,您的算法必须适用于您提供的任何类型的输入。也就是说,不允许仅仅因为您输入的多边形以您的算法不喜欢的方式定向而产生没有答案或错误答案。
但是,您可以做的是限制算法接受的输入类型。因此,要求输入多边形仅包含轴对齐的线段是完全有效的。
(“以错误的方式定向”和“仅轴对齐的段”之间的区别在于,第一个是一个模糊的标准,如果不对算法进行测试就无法确定 - 而第二个要求可以)。

概括地说,我们正在寻找一种将任何直线多边形(即仅由轴对齐的线段组成)水平划分为矩形的方法。

直线多边形示例 直线多边形示例

这是计划:

  1. 选择我们的积木
  2. 允许额外的顶点
  3. 在网格上对齐。
  4. 使用大小不等的网格单元。
  5. 哪些单元格在您的多边形内?
  6. 构造矩形。

选择我们的积木

尽管这些是实现细节,但提前弄清楚这一点可能会帮助您更好地了解算法的工作原理。

在代码中,我们将使用以下类型的对象作为基本构建块来表示我们的多边形:

  • 点,由 x 和 y 坐标组成。(例如使用Point2D.Double
  • 段,由起点和终点组成(例如使用Line2D.Double
  • 多边形,由一个封闭的段链组成(例如,使用 Line2D.Double 的 ArrayList),其中一个段的结束点与下一个段的起点相同。

此外,我们将使用网格,它可以建模为二维数组。

允许额外的顶点。

您说“必须使用在现有坐标上开始和结束的相交线来形成矩形。 ”。但是,请注意,仅使用现有顶点无法将大多数直线多边形划分为矩形 - 请参阅上面的示例(以及您之前提供的大篷车示例)。

因此,这个约束将不得不消失——即使我们实际上不会显式地创建新顶点。

在网格上对齐。

思想实验:如果您的多边形仅存在长度为 10、20、30 或 40...(即 10 的倍数)的(轴对齐)段,该怎么办?然后我们可以将多边形投影到网格上,并使用位于多边形内部的网格单元来组成矩形。此外,确定这些矩形的尺寸是一件轻而易举的事:只需计算位于多边形内的水平连续单元的数量并乘以 10;那是你的宽度。

网格对齐的多边形 网格对齐的多边形

好主意,除了:

  • 多边形不仅包含相同或多个长度的段
  • 我们如何确定哪些网格单元位于多边形内?

接下来我们将解决这些问题。

使用大小不等的网格单元。

如果您考虑一下,没有真正的理由让所有网格单元具有相同的宽度和高度。因此,我们可以设计一个网格,以使我们的多边形完美对齐的方式间隔开。

要获取网格水平轴的宽度:

  • 收集组成多边形的顶点的所有 x 坐标。
  • 对它们进行重复数据删除和排序,以增加价值。
  • 然后两个后续值之间的差异定义了该列的宽度。

网格与多边形对齐 网格与多边形对齐

显然,单元格的高度可以用相同的方式确定。您应该确定这些宽度和高度,并将它们分别存储到两个名为gridWidthsgridHeights的数组中。

现在我们知道了单元格的数量及其尺寸,我们可以开始对网格内容进行建模。

哪些单元格在您的多边形内?

请记住,我们的多边形存储为线段链。要确定网格单元是否位于该多边形内,我们可以使用奇偶规则:从多边形外部*到我们要测试的单元的中心构造一条线段,并计算该线段与多边形的段(即使用Line2D.Double 的 intersectsLine()方法)。如果数字是偶数,它位于多边形之外,如果它是奇数,它在多边形内。

*-最好只在单元格中心之间构建水平线段(如示例所示),这样您就不会冒险与线段端点相交 - 这可能算作 0 或 2 个交叉点,从而弄乱了您的总数.

因此,我们将把这个网格建模为grid一个二维布尔数组。以这种方式处理网格的每个单元格,并将相应的点标记grid为 true(内部多边形)或 false(外部多边形)。

基于奇偶规则的内 (T) 或外 (F) 基于奇偶规则的内 (T) 或外 (F)

构造矩形。

现在我们有了多边形的网格表示,以及每个单元格的实际宽度和高度,我们可以开始构建矩形了。我假设您只对每个矩形的宽度和高度感兴趣,而不是对形成矩形的实际顶点坐标感兴趣(尽管现在这也很容易)。

Foreach row in grid
  run_start = null
  Foreach cell C in row
    if C is marked as true and run_start = null
      //Found the start of a new run
      run_start = C
    else if C is marked as false and run_start != null
      //Found the end of a run
      The cells [run_start...C] form a horizontal rectangle.
      Use the gridWidths and gridHeights arrays to determine the 
      dimensions, and report this rectangle as a result
      
      //Prepare for the next run
      run_start = null

多边形,分割成矩形。 多边形,分割成矩形。

请注意,左上角的两个矩形共享相同的起始和结束 x 坐标,因此可以被视为一个矩形。如果你愿意,你可以实现一个额外的通道来合并这些类型的矩形。

结论

通过将直线多边形映射到网格上,我们可以轻松地将其水平划分为矩形,而无需求助于更高级的几何数据结构。
请注意,可以进行一些优化以使该算法运行得更快,但我认为这对于当前输入大小并不重要,并且会使实现更加困难。

于 2012-12-01T13:11:33.260 回答
4

这并不容易:

我认为你不会自己成功解决这个问题:

更多信息见

Preparata,Shamos:计算几何:第 8 章:矩形的几何。

您首先应该熟悉平面扫描算法和区间树。

如果我发现更多,我会更新。发现更多:

寻找最少矩形以覆盖一组矩形而不重叠的算法

于 2012-11-26T18:11:41.210 回答
1

注意:我不是在这里寻求理论上的最佳解决方案,而只是寻求一种能够产生正确答案并且在具有 100 个顶点的输入多边形上工作得足够快的方法。此外,现在不考虑特殊情况,例如其中有孔的输入多边形。此外,不是 x-monotone* 的多边形需要预处理,我还不会讨论。
*含义:从P中的任何最左边的位置开始,您可以通过向上、向下或向右移动到达任何其他位置,但永远不会向左。

假设
如您之前的帖子中所述,部分问题是在哪个方向铺设装饰板(或“矩形”)以尽量减少使用的装饰板数量。我将假设您的输入多边形 P 将主要由轴对齐的段组成,因此方向的选择减少到水平或垂直。对于其余部分,我将假设单个装饰板始终垂直定向(即从上到下运行)。为了计算水平铺面板的结果,只需将 P 旋转 90 度。

问题陈述
我们将尝试用铺面板覆盖 P,每个铺面板的长度不受限制,最大宽度为 W。更具体地说,我们正在寻找一种覆盖范围,使所使用的所有铺面板的总长度最小化。落在 P 之外的已用铺面板的部分(即浪费)不能用于覆盖 P 的其他部分。为了最大限度地减少浪费,将铺面板的左边界或右边界对齐是有意义的P 的一个顶点,或将一块铺板放置在另一个铺板旁边。

解决方案
第一步是将 P 划分为垂直板的集合:获取 P 中所有顶点的不同 x 坐标并对它们进行排序:每对连续的 x 坐标然后定义 P 的板 S。 图1

接下来要认识到,对于每个楼板 S,我们有 4 种可能的方式来对齐(一个或多个)铺面板:
* (SL) 从左开始,即将铺面板的左侧与楼板的左侧对齐.
* (CL) 继续向左,即继续铺面板的图案,由其左侧的板确定。
* (CR) 继续向右,即继续铺面板的图案,由其右侧的板确定。
* (SR) 从右开始,即将铺面板的右侧与楼板的右侧对齐。

因此,如果我们考虑每个楼板 S 的 4 种对齐方式的所有可能组合,我们就有所有可能的铺面板配置。请注意,并非所有对齐组合都是允许的;SL和SR可以用于任何slab,但CL只能在它左边的slab是SL或CL的情况下使用,而CR只能在它右边的slab是SR或CR的情况下使用。

-Snip-这个问题似乎与这里试图解决的问题有很大不同,所以我不会完成这篇文章。

于 2012-11-27T22:47:35.310 回答
0

我找到了一个解决方案,但它可能不是最优化的。 所以这里我们有我们的坐标和我前面提到的两个 ArrayList——xMatch 和 yMatch。 xMatch = 具有匹配 x 坐标 的坐标对的 ArrayList yMatch = 具有匹配 y 坐标的坐标对的 ArrayList


关联






我创建了一个名为“Point2Point”的类,它以特定顺序保存了两个坐标。xMatch 和 yMatch 都属于“Point2Point”类型。像任何向量一样,顺序很重要。我使用的顺序是:


Point1 = 起点
Point2 = 终点。

因此,现在使用“for”循环,我将 xMatch 中的一个元素与 yMatch 中的一个元素相对于 Point1(起点)进行匹配。将它们配对将为您提供以下形状:



链接2


现在这个图你可以看到,为了让这两个向量成为矩形的一部分,坐标 (?, ?) 必须存在。使用矩形的属性我知道 (?, ?) 等于 (yMatch.Point2.x, xMatch.Point2.y) 或相对于该图 (x3, y2)。


所以现在我知道要查找哪些坐标,我可以搜索我的 ArrayList“allCoordinates”来查看该坐标是否存在。如果是 - 我有一个矩形,如果不是 - 它是一个哑巴!

于 2012-11-30T12:23:30.470 回答