【问题标题】:Efficient algorithm for finding which hexagon a point belongs to查找点属于哪个六边形的高效算法
【发布时间】:2019-05-22 16:44:07
【问题描述】:

我正在尝试从以下方法中找到一种更有效的方法来确定一个点属于哪个六边形:

  1. 一个点数组 - 为了论证,10000 个点。
  2. 六边形中心点阵列,大约 1000 个六边形。
  3. 每个点都属于一个六边形,一些(大部分)六边形是空的。
  4. 六边形形成一个完美的网格,其中一个六边形的点从左上角开始(它将与整个区域的边缘重叠)。

我目前的解决方案有效,但我认为 n * (m log m) 相当慢,n=length(points)m=length(hexagons)

我怀疑我可以做得比这更好,想到的一种解决方案是(仅一次)将点和六边形按它们到某个任意点(可能是中间,可能是一个角)的距离进行排序,然后迭代在点和六边形的子集上,从到该点的距离大于等于的第一个六边形到最后一个匹配的六边形。同样,一旦 (point -> ref point) 和 (hexagon center -> ref point) 之间的距离差大于六边形的“半径”,我们就可以停止查看六边形。理论上,既然我们知道每个点都属于一个六边形,我什至不必考虑这种可能性。

我的问题是:有没有比这更多更好的方法呢?就复杂性而言,我认为最坏的情况会稍微好一点n * m,但平均情况应该非常好,可能在n * 20 的范围内(例如,我们每点只需要查看 20 个六边形)。以下是我目前的低效解决方案供参考。

points.forEach((p) => {
  p.hex = _.sortBy(hexes, (hex) => {
    const xDist = Math.abs(hex.middle.x - p.x);
    const yDist = Math.abs(hex.middle.y - p.y);
    return Math.sqrt((xDist * xDist) + (yDist * yDist));
  })[0];
});

【问题讨论】:

  • redblobgames.com/grids/hexagons/#pixel-to-hex 是否通过公式演示方形网格坐标到十六进制映射来解决您正在查看的内容?
  • 这非常酷 - 我不是 100% 肯定,但我绝对会看看 - 我开始研究 QuadTrees,因为它可能会这样做,但它看起来可能效率较低
  • @JasonAller 哇,这个资源太棒了!
  • 1000 个六边形中的 10000 个点和大多数空的六边形听起来不正确。
  • 如果网格是规则的,您可以在 O(1) 时间内计算任意点的图块索引。因此,对于 n 个点,O(n)。无需预处理。

标签: javascript sorting computational-geometry


【解决方案1】:

对于任意一点,你可以分两步找到最近的六边形中心(假设与Futurologist的排列相同):

  • 将横坐标除以中心之间的水平间距,然后四舍五入到最接近的整数。

  • 将纵坐标除以垂直间距的一半,并根据上面找到的奇偶性四舍五入到最接近的偶数或奇数。

  • 考虑这个中心和它周围的六个,并保持最接近目标点。

这会以恒定的时间为您提供图块的索引。

【讨论】:

    【解决方案2】:

    只是一个建议:假设您从正六边形网格中获得每个正六边形的中心(如果我理解正确,那是您所拥有信息的一部分)。

             -----
           /       \
               -     -----        -----------> x - axis
           \       /       \
             -----     -    
           /       \       /
               -     -----
           \       /       \
             -----     -    
               |   \       /
               |     ----- 
               |  
               |          
               V
            y - axis
    

    您可以认为您的坐标系从左上角六边形的中心开始,y 坐标轴垂直向下,而x 轴水平从左到右。正六边形网格中六边形的中心形成正方网格的图像,其中正方形网格的整数顶点通过简单地将正方形网格中的点的坐标乘以 2 x 转换为多边形的中心2 方阵(一个纯粹的矩阵)

    A = a*[ sqrt(3)/2    0;
    
              1/2        1 ]
    

    其中a 是六边形网格的参数,即两个边相邻六边形的中心之间的距离。这提供了一种将整数索引[m n] 分配给由六边形中心形成的网格的方法。之后,如果在六边形网格中给定一个坐标为[x y] 的点,则可以应用 A 的逆矩阵

     [u; v] = A^(-1)*[x; y] 
    
    where 
    
    A^(-1) = (2/(a*sqrt(3)))*[  1           0   ;
    
                               -1/2   sqrt(3)/2 ]
    
    

    ([x; y] 和 [u; v] 是列向量) 然后取m = floor(u)n = floor(v) 确定正方形左上角的整数坐标(也是索引)[m = floor(u), n = floor(v)]方形网格中的单元格(注意我们选择了两个网格的坐标从左上角开始)。因此,您的点 [u, v] 位于顶点为 [m,n] [m+1, n] [m, n+1] [m+1, n+1] 的正方形中 这意味着原点[x y] 位于中心具有索引[m,n] [m+1, n] [m, n+1] [m+1, n+1] 的四个六边形之一中。因此,您可以使用它来检查点[x y] 是四个六边形中的哪一个。

    我希望这会有所帮助。

    【讨论】:

      【解决方案3】:

      更新:为后代留下以下评论

      我现在使用这里提供的代码:https://www.redblobgames.com/grids/hexagons/

      一个非常重要的注意事项是,您的六边形网格必须从 (0, 0) 处的第一个六边形中点开始 - 如果不是这样,您会得到非常奇怪的结果,乍一看似乎是舍入错误 (即使在考虑了预期的偏移量之后)。对我来说,第一个六边形的位置并不重要,所以我只是将它设置为 (0, 0) 并且效果很好。

      旧解决方案

      我仍然希望有一个最佳解决方案,但我最终推出了自己的解决方案,每个点只需要检查 6 个六边形,另外还需要一点开销(大约 sqrt(m))。

      大约有 3000 个点和 768 个六边形(其中 310 个已填充),它在 100% 的时间内正确地将点分配给六边形(根据蛮力方法进行检查),花费了 29 毫秒,相比之下,大约为 840用蛮力。

      首先,我将六边形存储在密钥为"${column},${row}" 的地图中。列在技术上是重叠的,因此对于第 0 行,第 0 列从 -0.5 * hexWidth 开始,对于第 1 行,第 0 列从 0px 开始。

      接下来,我从左上角六边形的位置开始,项目 "0,0",它也应该在位置 0,然后将 y 增加六边形的高度,或者相应地增加六边形的边长.当y > 点y 时,我找到了可能的行,然后检查上下行。

      对于行内的列,我同时采用Math.floorMath.ceilx / hexWidth

      这样做会检查 6 个六边形,从这一点来看,解决方案与问题中的解决方案相同。

      理论上,这可以用来查找正确的六边形,使用 x/y 位置。然而在实践中,这在大约 5% 的时间里对我没有用,而且有 1 个错误,这可能是一个舍入问题。

      我看过的其他一些东西:

      1. 正如@jason-aller 建议的那样,https://www.redblobgames.com/grids/hexagons/#rounding。不幸的是,这似乎假设了六角网格(旋转)上的某种形式的转换并且不容易遵循 - 不断引用尚未定义的函数。

      2. QuadTree(各种实现)不幸的是,这为每个点返回了大约 100 个“潜在匹配”——因此性能改进并不好。我知道插入顺序改变了 QuadTree 的有用性,我尝试了自然顺序,按距顶部、左侧和随机排列的距离排序,它们都表现同样糟糕。 QuadTree 的最佳解决方案可能涉及使用最接近中点的项目填充树,然后递归地从中点到每个角的项目 1/2 填充树。对我来说太辛苦了!

      【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-12-04
      • 2012-08-26
      • 2017-09-13
      • 2017-08-30
      • 2023-03-29
      • 1970-01-01
      • 2012-12-06
      相关资源
      最近更新 更多