从坐标/点的数组中创建矩形

5

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

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

我已经开始了算法,但是我无法完成它,而且我感觉肯定有更简单的方法:

ArrayList叫做“allCoordinates”。
ArrayList“xMatch”和“yMatch”是所有坐标的子集。

算法:

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


所以在上面的图中:
(集合1=[x1, y1]-[x8, y8],
集合2=[x7, y7]-[x2, y2],
集合3=[x4, y4][x5, x5],
集合4=[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)。 这条线将从一个现有坐标开始,但不会以现有坐标结束。因此,该线将无效。


将一个直角多边形分解成矩形有无数种方法。你采用哪种方式是否重要? - Robert Cooper
有道理的假设是他想要最小的矩形集之一。 - jimpic
我不这么认为。我认为这并不重要,我也不确定它们会有什么不同? - cworner1
@cworner1 啊哈!现在问题不同了。如果你只是想找到多边形的面积,那就是一回事。如果你有特定尺寸的木板,并且想要计算出最少需要多少块木板来覆盖你的甲板,如果你可以锯开木板,我恐怕你正在尝试解决一个NP难问题。如果你只需要得到一个好的答案,而不一定是最好的答案,可能有一些简单的方法可以做到这一点。 - Robert Cooper
你为什么只想使用原多边形的顶点来形成矩形呢?这看起来似乎是一种简单的方法,可以避免计算交点,但实际上会使问题变得更加复杂。此外,你提供的大多数示例(http://stackoverflow.com/questions/13399666/java-fit-minimum-amount-of-rectangles-into-polygon和http://img29.imageshack.us/img29/6349/173zh.jpg)都无法在不引入额外顶点的情况下分解成矩形。如果可能的话,我建议放弃这个限制。 - Leon Bouquiet
显示剩余12条评论
4个回答

8
作为你们可能已经注意到的,我一直在处理这个问题。部分原因是因为我喜欢解决这种问题,但也因为我无法找到一个优雅的算法来解决这个看似简单的问题而感到有些恼火。
不要被骗了,这不是一个可以通过简单的点操作来解决的琐碎问题,尽管它看起来确实如此。其中一个原因是,尽管你认为你只是在处理点,但你隐含地也在操作线段和由它们所包围的区域,这些区域也有自己的限制(即线段应始终形成一个或多个封闭链,除非它们在我们定义它们的顶点处相交)。
另外,你的算法必须适用于任何你输入的数据。也就是说,它不能因为你输入的多边形朝向不合适而产生错误答案或没有答案。
然而,你可以限制算法接受的输入类型。要求输入多边形仅包含轴对齐线段是完全有效的。
(“朝向不正确”和“仅轴对齐线段”之间的区别在于前者是一个模糊的标准,除非在算法上进行测试,否则无法确定——而第二个要求则可以)。
总之,我们正在寻找一种方法将任何直角多边形(即仅由轴对齐线段组成的多边形)水平分割成矩形。
以下是计划:
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,一个二维布尔数组。以这种方式处理网格的每个单元,并将相应的位置标记为true(多边形内部)或false(多边形外部)。

Inside (T) or outside(F) based on the even-odd rule 根据奇偶规则判断内部(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坐标,因此可以视为一个矩形。如果需要,可以实现一个额外的步骤合并这些类型的矩形。

结论

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


好的,听起来不错。就未来需要进行的进一步计算而言,这样做效果更好。谢谢。 - cworner1
1
当然,如果您有任何问题,请随时告诉我。顺便说一下,如果您觉得这是最有帮助的帖子,您是否可以将其标记为答案? - Leon Bouquiet
完成。抱歉耽搁了。 - cworner1

4

感谢您的研究。但是我之前已经看过这篇文章了。您发布的解决方案以不同的方式分解了多边形(有很好的理由)。我已经保存了正确分解的坐标。问题是如何从我的坐标数组中创建矩形。 - cworner1
他们在上面的链接中是如何做到的?也许你可以用同样的方法来做。 - AlexWien
在上面的链接中,解剖是沿着X轴和Y轴两个方向进行的。我的算法只能沿着一个方向解剖,要么是沿着X轴平行,要么是沿着Y轴平行。不会同时进行。这样做的区别在于每个甲板板材的长度会比实际需要的短。 - cworner1
我的直觉也会建议在两个轴上进行平面扫描。 - AlexWien
可能很难解释,但我会尝试。请查看帖子中的第2张和第4张照片。您链接的算法会将走廊切成更多不必要的碎片。我想要做的是用最少的切割次数覆盖多边形,使用“甲板板”矩形。例如,甲板板宽32mm,长5.1m。如果我可以使用一块5.1m的板而不必切割,则比不必要地切割更好。 - cworner1

1
注意:我这里不是追求理论上最优的解决方案,而是采用一种能够快速处理100个顶点的输入多边形并得出正确答案的方法。同时,现在不考虑带洞的输入多边形等特殊情况。此外,非x-单调*的多边形需要预处理,但我暂时不讨论。
*意思是:从P中任何最左边的位置开始,您可以通过向上、向下或向右移动到达任何其他位置,但不能向左移动。

假设
如您早期发布的帖子所述,问题的一部分是确定铺设甲板板材(或“矩形”)的方向,以便最小化使用的甲板板数。我将假设您的输入多边形P主要由轴对齐线段组成,因此选择方向只能减少为水平或垂直。对于其余部分,我将假设单个甲板板始终垂直放置(即从上到下运行)。要使用水平甲板板计算结果,只需将P旋转90度即可。 问题陈述
我们将尝试用长度无限、最大宽度为W的甲板板覆盖P。更具体地说,我们正在寻找一种覆盖方式,以最小化使用的所有甲板板的总长度。被用于覆盖P之外的甲板板部分(即浪费部分)不能用于覆盖P的其他部分。 为了最小化浪费,有意义的是将甲板板的左边界或右边界与P的顶点对齐,或将甲板板放在另一个甲板板旁边。 解决方案
首先,将P划分为一组垂直板块:取P中所有顶点的不同x坐标并对它们进行排序:每对相邻的x坐标然后定义P的一个板块S。
请注意,对于每个板块S,我们有4种可能的方式将(一个或多个)铺板与其对齐:
*(SL)从左侧开始,即将铺板的左侧与板块的左侧对齐。
*(CL)向左继续,即按照左侧板块确定的铺板模式继续。
*(CR)向右继续,即按照右侧板块确定的铺板模式继续。
*(SR)从右侧开始,即将铺板的右侧与板块的右侧对齐。

因此,如果我们考虑所有可能的组合,对于每个板块S,使用4种对齐方式,我们就可以得到所有可能的铺板配置。请注意,并非所有对齐方式的组合都是允许的; SL和SR可用于任何板块,但只有在左侧的板块为SL或CL时才能使用CL,只有在右侧的板块为SR或CR时才能使用CR。

-剪辑-该问题似乎与此处尝试解决的问题显著不同,因此我不会完成这篇文章。


更具体地说,我们正在寻找一种覆盖方式,以最小化使用的所有甲板板材的总长度。- 不正确,不确定这是一个错误还是我读错了。我们正在尝试最小化使用的零件数量。因此,我宁愿使用1块300像素(或任何您想使用的单位测量)的板材,而不是使用3块100像素的甲板板材。这意味着减少员工的切割次数。 - cworner1
是的,其余的我都可以理解。这样你就可以了解规模等等……每个板块需要的不仅仅是一两块甲板板材:http://img29.imageshack.us/img29/6349/173zh.jpg 这是旧的手动绘图方法。 - cworner1
将总板长度最小化是我的一个假设,因为这样可以使使用的材料量最小化(这通常是这类问题的要求)。但我明白切割的数量也是一个标准。这是否是唯一的标准(即您只关心切割的数量,而不考虑必须扔掉多少材料),还是切割和使用材料量的组合? - Leon Bouquiet
是的,然而在这个程序的眼中并不重要。这是为了计算库存。如果你必须把一块木板切成两半,那么很可能另一半不能再使用,所以我们将其视为一整块木板。 - cworner1
这将涉及对我的代码进行完全重写。我告诉你这个是因为在这个阶段,我宁愿尝试完成我的代码,而不是重新实现你的代码。我不想被指责浪费了你的时间。 - cworner1
显示剩余7条评论

0

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


link

所以我们有了我们的坐标和之前提到的两个ArrayLists - xMatch和yMatch。

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


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


Point1 = 起点
Point2 = 终点。

现在,使用“for”循环,我将xMatch中的一个元素与yMatch中的一个元素相匹配,以Point1(起始点)为参考。将它们配对会给您以下形状:



Link2


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


现在我知道要查找哪些坐标,我可以搜索我的ArrayList“allCoordinates”以查看是否存在这些坐标。如果存在-我有一个矩形,如果不存在-那就是无用的!


你只是跟踪点,并隐式地定义边(作为点对)。你考虑过将多边形表示为“双连通边列表”(或“DCEL”)吗?它为您提供了更多操作多边形的方法(例如分割边和面),这使得解决问题更容易(或更可能,“可能”)。唯一的缺点:我还没有找到一个好的Java实现。 - Leon Bouquiet
等一下,我认为我有一个更好的解决方案,可以利用多边形的轴对齐特性... - Leon Bouquiet

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接