查找点属于哪个六角形的高效算法

时间:2019-05-22 16:44:07

标签: javascript sorting computational-geometry

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

  1. 点的数组-为争辩起见,为10000点。
  2. 六边形的中心点数组,大约为1000个六边形。
  3. 每个点将完全属于一个六边形,一些(大多数)六边形将为空。
  4. 六边形形成一个完美的网格,一个六边形的点从左上角开始(它将与整个区域的边缘重叠)。

我当前的解决方案有效,但是我认为n * (m log m)的速度很慢,在n=length(points)m=length(hexagons)处。

我怀疑我可以做得比这更好,我想到的一个解决方案是根据点和六边形到任意点(也许是中间,也许是一个角)的距离对它们(仅一次)进行排序(仅一次)从这些点的距离大于等于的第一个六边形开始,到匹配的最后一个六边形,从六边形的这些点开始,并在六边形的一个子集上。类似地,一旦(点->参照点)和(六边形中心->参照点)之间的距离差大于六边形的“半径”,我们可以停止查看六边形。从理论上讲,由于我们知道每个点都将属于一个六角形,所以我什至不必考虑这种可能性。

我的问题是:有没有比这更好的方法了?就复杂性而言,我认为最坏的情况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];
});

3 个答案:

答案 0 :(得分:1)

对于任意点,您可以分两步找到最接近的六边形中心(假定与Futurologist的布置相同):

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

  • 将纵坐标除以垂直间距的一半,然后四舍五入为最接近的偶数或奇数整数,具体取决于上面的奇偶校验。

  • 考虑此中心及其周围的六个中心,并保持最接近目标点。

这将为您提供固定时间的图块索引。

答案 1 :(得分:0)

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

我现在正在使用此处提供的代码: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.floor的{​​{1}}和Math.ceil

这样做可以检查6个六边形,从这一点出发,解决方案与问题中的解决方案相同。

理论上,这可以用于通过x / y位置查找正确的六边形。但是在实践中,这对我来说约有5%的时间不起作用,出现了1个错误,这很可能是舍入问题。

我看过的一些其他东西:

  1. 根据@ jason-aller的建议,https://www.redblobgames.com/grids/hexagons/#rounding。不幸的是,这似乎在十六进制网格(旋转)上采取了某种形式的变换,并且不容易遵循-不断引用尚未定义的函数。

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

答案 2 :(得分:0)

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

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

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

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]在四个六角形中的哪个六角形中。

我希望这会有所帮助。