在2d平铺世界中的矩形联盟

时间:2013-10-04 10:14:45

标签: c++ stl computational-geometry 2d-games

我试图在二维平铺世界中找到一组相邻单元格(row,col)的边界多边形(可转换为矩形)。

处理for循环中的单元格并使用相邻单元格的邻域属性,我可以消除所有内部边缘并存储其余边缘。 边缘存储在std :: vector;

现在我需要合并有共同顶点的边,斜率是相同的。 合并边后,我需要制作边界多边形,从逆时针旋转的顶点开始。

请帮助找到使其成为可能的方法。

2 个答案:

答案 0 :(得分:2)

我认为这是一个实现这一目标的简单算法。

考虑我们将此作为输入:

 |   |   |   |   |   |
-+---+---+---+---+---+-
 |   |   |   |   |   |
-+---+---+---+---+---+-
 |   |   | a |   |   |
-+---+---+---+---+---+-
 |   | b | c | d |   |
-+---+---+---+---+---+-
 |   |   | e |   |   |
-+---+---+---+---+---+-
 |   |   |   |   |   |

abcde是我们的输入磁贴,存储为成对的对象(坐标):

std::vector<std::pair<unsigned,unsigned>> tiles;

我们想要的是:

 |   |   |   |   |   |
-+---+---+---+---+---+-
 |   |   |   |   |   |
-+---+---*---*---+---+-
 |   |   |   |   |   |
-+---*---*   *---*---+-
 |   |           |   |
-+---*---*   *---*---+-
 |   |   |   |   |   |
-+---+---*---*---+---+-
 |   |   |   |   |   |

该算法的工作原理如下:

  1. 构建一组包含整个图块的布尔值。您必须遍历该集以查找该矩形的边界。将数组的位置设置为true,表示该组的图块,否则为false 示例中的输出将是(T是true,f是false):

    +---+---+---+  
    | f | T | f |  
    +---+---+---+  
    | T | T | T |  
    +---+---+---+  
    | f | T | f ]  
    +---+---+---+  
    
  2. 现在你必须遍历船体多边形的边界。从标记数组中标记为true的第一个元素开始,沿相同方向移动顶点,直到再次到达第一个顶点,使用以下规则:

    • 如果当前方向/位置前面的两个图块为false,则顺时针旋转并将顶点添加到输出列表(多边形): (*是添加到多边形的顶点,X当前顶点,箭头是当前方向)

      +---+---+---+  
      | f | f | f |  
      *---+---X---+ --->   
      | T | T | f |  
      *---+---+---+  
      | f | T | f ]  
      +---+---+---+  
      
      goes to
      
      +---+---+---+  
      | f | f | f |  |
      *---+---*---+  |
      | T | T | f |  v
      *---+---X---+  
      | f | T | f ]  
      +---+---+---+  
      
    • 如果一个图块为假而一个为真,则向同一方向行进(请注意,true-false或false-true表示您处于边框内):

      +---+---+---+  
      | f | f | f | |   
      *---+---*---+ |  
      | T | T | f | v  
      *---+---X---+  
      | f | T | f ]  
      +---+---+---+  
      
      goes to
      
      +---+---+---+  
      | f | f | f |  |
      *---+---*---+  |
      | T | T | f |  v
      *---+---+---+  
      | f | T | f ]  
      +---+---X---+  
      
    • 如果两个图块均为true,则逆时针旋转并将顶点添加到输出列表(请注意,true-true表示您已到达部分图块,a& #34;壁&#34):

      +---+---+---+  
      | f | f | f | |   
      *---+---*---+ |  
      | T | T | f | v  
      *---+---X---+  
      | f | T | T ]  
      +---+---+---+  
      
      goes to
      
      +---+---+---+  
      | f | f | f |  
      +---+---*---+  
      | T | T | f |  --->
      +---+---*---X  
      | f | T | T ]  
      +---+---+---+  
      
  3. 注意事项:

    tilemap coordinates vs flag-array coordinates

    flag-array表示tilemap放置的tilemap的矩形区域。因此,其第一个元素(tile)的tilemap坐标为(left,top),其中left是所选tile集的最小x坐标,top是最小y坐标选定的一组图块。

    在算法的第二步中,您将使用数组作为指导,遍历该组图块的边界(边界)。请注意,您真正想要的是该数组,因此您必须将坐标从flag-coordinates(逻辑坐标)转换为tilemap-coordinates(物理坐标)来存储多边形的顶点。当然那很容易。

    另请注意,算法抽象步骤会横切边缘的顶点(物理图块坐标),而非逻辑坐标。你必须确定&#34;我在那个顶点&#34; 意味着什么&#34;提前&#34; 和&#34 ; &#34;以标志数组坐标表示。

    边界条件和前方瓦片检查

    我们已经定义了三个规则来沿着这组瓷砖的边界前进。我们使用旗形阵列作为指导来决定做什么(前进,顺时针转动或逆时针转动)。请注意,当当前顶点位于数组的边框中时,您可以(您应该)认为它具有错误值的邻居图块

    例如:

    +---+---+---+  
    | f | f | f |  |  
    *---+---*---+  |  
    | T | T | f |  v
    *---+---+---+  
    | f | T | f ]  
    +---+---X---+  
    

    转到

    +---+---+---+  
    | f | f | f |  
    *---+---*---+  <--
    | T | T | f |  
    *---+---+---+  
    | f | T | f ]  
    +---X---*---+  
    

    就像是这样:

    +---+---+---+  
    | f | f | f |  |  
    *---+---*---+  |  
    | T | T | f |  v
    *---+---+---+  
    | f | T | f ]  
    +---+---X---+
    | f | f | f |  
    *---*---*---+ 
    

    可能的优化

    第一步计算标志数组,因为算法将选择的一组平铺作为向量。如果您的磁贴引擎支持它,您可以向磁贴添加属性(bool selected)并直接传递tilemap,避免计算和顶点协调转换

    实施例

    给定这个flag-array:

    +---+---+---+  
    | T | f | f |    
    +---+---+---+    
    | T | T | T |
    +---+---+---+  
    | f | T | T |  
    +---+---+---+  
    

    执行如下(请注意,图纸是执行步骤后的状态)

    1. 找到第一个true磁贴。在这种情况下(0,0)。所以我们从它的一个顶点开始(左下顶点,向上看,例如。注意,因为它是第一个真正的平铺,你可以使用该顶点确保它属于多边形。所以首先添加顶点到多边形):

      Current position: (0,1)
      Polygon: {(0,1)} 
      
      +---+---+---+ ^ 
      | T | f | f | |  
      X---+---+---+ |    
      | T | T | T |
      +---+---+---+  
      | f | T | T |  
      +---+---+---+  
      
    2. 开始横向移动。在这种情况下,前方图块为false-true,因此提前

      Current position: (0,0)
      Polygon: {(0,1)} 
      
      X---+---+---+ ^ 
      | T | f | f | |  
      *---+---+---+ |    
      | T | T | T |
      +---+---+---+  
      | f | T | T |  
      +---+---+---+  
      
    3. 前面的瓷砖是false-false(我们在边框中),所以顺时针旋转并添加顶点

      Current position: (1,0)
      Polygon: {(0,1),(0,0)} 
      
      *---X---+---+ 
      | T | f | f |   
      *---+---+---+     
      | T | T | T | --->
      +---+---+---+  
      | f | T | T |  
      +---+---+---+ 
      
    4. 现在fron-tiles是false-false(一个是数组之外,另一个是false)。 顺时针旋转并添加顶点

      Current position: (1,1)
      Polygon: {(0,1),(0,0),(1,0)} 
      
      *---*---+---+ 
      | T | f | f | | 
      *---X---+---+ |   
      | T | T | T | v
      +---+---+---+  
      | f | T | T |  
      +---+---+---+ 
      
    5. 两个前贴片为true逆时针旋转并添加顶点

      Current position: (1,2)
      Polygon: {(0,1),(0,0),(1,0),(1,1)} 
      
      *---*---+---+ 
      | T | f | f |   
      *---*---X---+     
      | T | T | T | --->
      +---+---+---+  
      | f | T | T |  
      +---+---+---+ 
      
    6. 一个图块为false,另一个图块为true高级

      Current position: (1,3)
      Polygon: {(0,1),(0,0),(1,0),(1,1)} 
      
      *---*---+---+ 
      | T | f | f |   
      *---*---+---X     
      | T | T | T | --->
      +---+---+---+  
      | f | T | T |  
      +---+---+---+ 
      
    7. 两个false个图块(两个都不在数组中):顺时针旋转并添加顶点

      Current position: (2,3)
      Polygon: {(0,1),(0,0),(1,0),(1,1),(3,1)} 
      
      *---*---+---+ 
      | T | f | f | | 
      *---*---+---* |   
      | T | T | T | v
      +---+---+---X  
      | f | T | T |  
      +---+---+---+ 
      
    8. 一个true和一个false(数组外):提前

      Current position: (3,3)
      Polygon: {(0,1),(0,0),(1,0),(1,1),(3,1)} 
      
      *---*---+---+ 
      | T | f | f | | 
      *---*---+---* |   
      | T | T | T | v
      +---+---+---+  
      | f | T | T |  
      +---+---+---X 
      
    9. 两个false(数组外)前贴片:顺时针旋转并添加顶点

      Current position: (2,3)
      Polygon: {(0,1),(0,0),(1,0),(1,1),(3,1),(3,3)} 
      
      *---*---+---+ 
      | T | f | f |  
      *---*---+---*    
      | T | T | T | <---
      +---+---+---+  
      | f | T | T |  
      +---+---X---* 
      
    10. true-false(一个true和一个超出范围):提前

      Current position: (1,3)
      Polygon: {(0,1),(0,0),(1,0),(1,1),(3,1),(3,3)} 
      
      *---*---+---+ 
      | T | f | f |  
      *---*---+---*    
      | T | T | T | <---
      +---+---+---+  
      | f | T | T |  
      +---X---+---* 
      
    11. false-false(一个false和一个超出范围):顺时针旋转并添加顶点

      Current position: (1,2)
      Polygon: {(0,1),(0,0),(1,0),(1,1),(3,1),(3,3),(1,3)} 
      
      *---*---+---+ 
      | T | f | f | ^ 
      *---*---+---* |   
      | T | T | T | |
      +---X---+---+  
      | f | T | T |  
      +---*---+---* 
      
    12. true-true前贴片:逆时针旋转并添加顶点

      Current position: (0,2)
      Polygon: {(0,1),(0,0),(1,0),(1,1),(3,1),(3,3),(1,3),(1,2)} 
      
      *---*---+---+ 
      | T | f | f | 
      *---*---+---*    
      | T | T | T | <---
      X---*---+---+  
      | f | T | T |  
      +---*---+---* 
      
    13. false-false前贴片:顺时针旋转并添加顶点

      Current position: (0,1)
      Polygon: {(0,1),(0,0),(1,0),(1,1),(3,1),(3,3),(1,3),(1,2),(0,2)} 
      
      *---*---+---+ 
      | T | f | f | ^ 
      X---*---+---* |   
      | T | T | T | |
      *---*---+---+  
      | f | T | T |  
      +---*---+---* 
      
    14. 当前顶点是多边形的第一个顶点:执行已完成。结果如下:

      Polygon: {(0,1),(0,0),(1,0),(1,1),(3,1),(3,3),(1,3),(1,2),(0,2)} 
      
      *---* 
      |   |  
      *   *-------*    
      |           | 
      *---*       |  
          |       |  
          *-------* 
      

答案 1 :(得分:0)

由于瓷砖,这是寻找边界多边形的“特例”。我会采用一些简单的方法。

平铺(x,y)由顶点[(x,y),(x + 1,y),(x + 1,y + 1),(x,y + 1)]组成。如果边界多边形位于1,2或3个图块中,则它是边界多边形的一部分。要在边界上找到顶点,就足以计算它所在的平铺数量。为此,它足以通过平铺并增加平铺4顶点的顶点外观计数。平铺计数为1,2或3的顶点位于边界多边形上。

要对顶点进行排序,从边界上的某个顶点开始并查找也在边界上的相邻顶点就足够了。要在相同方向上遍历顶点,重要的是要检查相邻顶点的方向顺序以进行检查。例如。如果最后一个顶点在-x上,则检查方向的顺序是+ y,+ x,-y。

由于最后一条边的方向是已知的,如果下一条边与该顶点的方向相同,则用于移除。如果区域是简单连接的,那么也可以从瓦片计数中知道移除。如果顶点的平铺计数为2,则顶点用于删除。

目前我们无法保证订单顺时针方向。可以通过检查(某些)最上边缘(顺时针方向)来检查。如果不是,则反向多边形。