【问题标题】:Create rectangles from an array of Coordinates/Points从坐标/点数组创建矩形
【发布时间】:2012-11-26 16:47:24
【问题描述】:

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



所以现在我有两个数组列表,所有的垂直边和所有水平的边。现在我需要一些方法来检查它们是否都连接在一起?就像我说的,一定有更简单的方法...?

编辑:

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

【问题讨论】:

  • 有无数种方法可以将直线多边形分割成矩形。你用哪种方式做这件事有关系吗?
  • 假设他想要(其中一个)最小的矩形集是有意义的。
  • 我不这么认为。我说没关系我不确定它们会如何变化?
  • @jimpic 好吧,也许他想要最少数量的矩形。但也许他只是想用矩形绘制一个形状,在这种情况下,做其他事情可能会更好(例如,不关心重叠的矩形,或者只使用库绘制多边形)。
  • @Robert Cooper 好的,直线多边形代表了一个甲板区域。我需要计算出甲板区域需要多少甲板长度。我的第一步是将多边形分成矩形。

标签: algorithm rectangles


【解决方案1】:

您现在可能已经注意到,我一直在讨论这个问题。部分是因为我喜欢解开这类问题,但也因为我无法为看似如此简单的事情找到一个优雅的算法,这让我有点恼火。

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

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

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

直线多边形示例

计划如下:

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

挑选我们的积木

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

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

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

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

允许额外的顶点。

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

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

在网格上对齐。

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

网格对齐的多边形

好主意,除了:

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

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

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

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

获取网格水平轴的宽度:

  • 收集构成多边形的顶点的所有 x 坐标。
  • 重复数据删除并按递增值排序。
  • 两个后续值之间的差异然后定义该列的宽度。

网格与多边形对齐

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

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

多边形内有哪些单元格?

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

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

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

内(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 坐标,因此可以被视为一个矩形。如果你愿意,你可以实现一个额外的通道来合并这些类型的矩形。

结论

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

【讨论】:

  • 好的,听起来不错。对于将来需要进行的进一步计算,这更有效。谢谢。
  • 当然,如果您有任何问题,请告诉我。顺便说一句,如果您觉得这是对您最有帮助的帖子,您是否愿意将其标记为答案?
  • 完成。对延误表示歉意。
【解决方案2】:

这并不容易:

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

更多信息见

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

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

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

Algorithm for finding the fewest rectangles to cover a set of rectangles without overlapping

【讨论】:

  • 感谢您的研究。不过我以前看过这个帖子。您发布的解决方案以与我剖析多边形的方式不同的方式剖析多边形(这是有充分理由的)。我保存的坐标已经包含正确的解剖。问题是如何从我的坐标数组创建矩形。
  • 他们如何在上面的链接中做到这一点?也许你也可以这样做。
  • 在上面的链接中,解剖是在两个方向上进行的 - 平行于 X 轴和平行于 Y 轴。我的算法只分析一个方向,X 轴平行或 Y 轴平行。不是都。不同之处在于每个装饰板的长度将比他们需要的要短。
  • 我的直觉也会说在两个轴上进行平面扫描。
  • 这可能很难解释,但我会尝试。看看 [this]stackoverflow.com/questions/13399666/… 帖子中的照片 2 和 4。您链接的算法会将阳台分解成更多必要的部分。我要做的是用最少的切口覆盖“甲板”矩形中的多边形。例如。甲板板宽 32 毫米,长 5.1 米。如果我可以使用 5.1m 的电路板而不必切割电路板,那总比不必要地切割它要好。
【解决方案3】:

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

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

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

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

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

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

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

【讨论】:

  • “更具体地说,我们正在寻找一种覆盖范围,以最大限度地减少所有使用的铺面板的总长度。” - 不正确,不确定这是一个错误还是我读错了。我们正在考虑尽量减少使用的部件数量。因此,与其使用 3 个 100 像素的甲板板,我宁愿使用 1 个 300 像素的板(或您想要使用的任何单位测量)。这意味着减少员工的裁减。
  • 你能解释一下为什么第三块平板(从左边开始)被标记为“SR”。我假设我们从左侧开始并向右移动,在这种情况下,它应该被标记为“CR”——从前一个平板继续。
  • 是的,其余的我能理解。只是为了让您对比例等有一个透视图……每块楼板将比一两个甲板板花费更多:img29.imageshack.us/img29/6349/173zh.jpg 这是旧方法或手动绘图。
  • 最小化电路板总长度是我的一个假设,因为这可以最大限度地减少材料的使用量(这通常是这类问题的要求)。但我理解切割量也是一个标准。它是唯一的标准(即您是否只关心完成了多少切割,而不管您必须丢弃多少材料),还是它是切割和使用材料量的组合?
  • 首先要简化一些事情——我的目标是垂直铺设地板,然后显示切割清单。水平铺设地板 - 显示切割清单。然后让用户根据两组切割清单来决定哪个布局更好。所以不涉及“最佳”算法。
【解决方案4】:

我找到了一个解决方案,但它可能不是最理想的。




所以这里我们有我们的坐标和我前面提到的两个 ArrayList——xMatch 和 yMatch。

xMatch = ArrayList of Coordinates 与匹配的 x 坐标
yMatch = 具有匹配 y 坐标的坐标对的 ArrayList

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


Point1 = 起点
Point2 = 终点。

所以现在我使用“for”循环将 xMatch 中的一个元素与 yMatch 中的一个元素相对于 Point1(起点)进行匹配。将它们配对会得到以下形状:




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


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

【讨论】:

  • 您只是在跟踪点,并隐式定义边缘(作为点对)。您是否考虑将多边形表示为doubly-connected edge list(或“DCEL”)?它为您提供了更多操作多边形的方法(例如分割边和面),这使得解决您的问题更容易(或者更有可能,“可能”)。唯一的缺点:我还没有找到一个好的 java 实现。
  • 等等,我想我有一个更好的解决方案,可以利用多边形的轴对齐特性......
猜你喜欢
  • 1970-01-01
  • 2013-08-28
  • 1970-01-01
  • 1970-01-01
  • 2018-04-01
  • 1970-01-01
  • 2017-07-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多