如何高效获取两个六边形网格范围的相交六边形?
嘿,我太懂你这种痛点了——生成两个全量坐标列表再暴力匹配找交集,等范围半径稍微大一点,那效率简直低到让人抓狂😅。之前我在做六边形网格的地图系统时,也踩过一模一样的坑,刚好对Red Blob Games里的范围交集方法有实操经验,给你梳理下核心思路、实操步骤,还有容易踩的坑:
为什么原来的方法效率拉胯?
先掰扯清楚根源:单个六边形范围的坐标数量是O(r²)级别的(r是半径),两个范围就是O(r1² + r2²)的存储量,再加上遍历匹配的O(r1²*r2²)时间复杂度,数据量上去后完全是灾难级的。而Red Blob Games的思路核心就是跳过全量生成,直接通过数学约束找到交集区域。
核心思路(对应原指南的核心逻辑)
不管用哪种六边形坐标系统(轴向、立方体、偏移),两个范围的交集都是「同时满足两个范围约束的所有六边形坐标」。我们不需要生成所有点再筛选,而是:
- 先判断两个范围是否存在交集(或者是否完全包含)
- 只在可能重叠的区域内遍历,同时验证是否满足两个范围的约束
下面以最常用的**轴向坐标(q, r)**为例,给你拆解实操步骤:
第一步:先搞定「点是否在范围内」的判断函数
这是基础,轴向坐标下的距离公式是:两个点(q1, r1)和(q2, r2)的距离为(abs(q1-q2) + abs(q1+r1 - q2 - r2) + abs(r1-r2)) // 2。基于这个,判断点是否在范围内的函数可以写成:
def is_in_hex_range(q, r, center_q, center_r, radius): dq = abs(q - center_q) dr = abs(r - center_r) ds = abs(-q - r + center_q + center_r) # 轴向坐标中s = -q - r,这里计算s方向的差值 return (dq + dr + ds) // 2 <= radius
第二步:高效计算交集的流程
- 快速判断特殊情况:
- 如果两个中心的距离 > 两个半径之和:完全无交集,直接返回空列表
- 如果两个中心的距离 + 较小半径 ≤ 较大半径:小范围完全被包含在大范围里,直接返回小范围的所有坐标(用你原来的生成函数就行)
- 遍历重叠区域并筛选:
对于部分重叠的情况,我们不需要遍历整个小范围,而是先算出两个范围在q、r轴上的重叠区间,再在这个区间内遍历,同时验证是否满足两个范围的约束:def get_hex_range_intersection(center1, radius1, center2, radius2): q1, r1 = center1 q2, r2 = center2 # 计算两个中心的距离 dq = abs(q1 - q2) dr = abs(r1 - r2) ds = abs(-q1 - r1 + q2 + r2) center_distance = (dq + dr + ds) // 2 # 情况1:无交集 if center_distance > radius1 + radius2: return [] # 情况2:一个范围完全包含另一个 if center_distance + min(radius1, radius2) <= max(radius1, radius2): if radius1 <= radius2: return your_existing_generate_range_function(center1, radius1) else: return your_existing_generate_range_function(center2, radius2) # 情况3:部分重叠,遍历重叠区间 # 先确定q的遍历范围(取两个范围q区间的交集) q_min = max(q1 - radius1, q2 - radius2) q_max = min(q1 + radius1, q2 + radius2) intersection = [] for q in range(q_min, q_max + 1): # 计算当前q下,在两个范围内的r的有效区间 # 第一个范围的r约束 r_low1 = max(r1 - (radius1 - abs(q - q1)), r1 - radius1) r_high1 = min(r1 + (radius1 - abs(q - q1)), r1 + radius1) # 第二个范围的r约束 r_low2 = max(r2 - (radius2 - abs(q - q2)), r2 - radius2) r_high2 = min(r2 + (radius2 - abs(q - q2)), r2 + radius2) # 取两个r区间的交集 current_r_min = max(r_low1, r_low2) current_r_max = min(r_high1, r_high2) # 遍历r并验证 for r in range(current_r_min, current_r_max + 1): if is_in_hex_range(q, r, q1, r1, radius1) and is_in_hex_range(q, r, q2, r2, radius2): intersection.append((q, r)) return intersection
容易踩的坑(我之前掉过的坑)
- 坐标系统搞混:如果你用的是偏移坐标(比如行偏移、列偏移),一定要先转成轴向/立方体坐标再处理,不然距离和范围约束的公式完全不对。
- 边界条件写错:比如把
<=写成<,导致边缘的六边形被漏掉;或者计算中心距离时忘了除以2,导致判断交集的逻辑出错。 - 遍历范围没优化:如果直接遍历整个小范围而不是重叠区间,虽然结果对,但效率还是会打折扣,尤其是两个范围重叠部分很小的时候。
额外优化建议
- 如果用立方体坐标(x, y, z,满足x+y+z=0),距离计算会更直观,约束条件也更容易推导,你可以试试转成立方体坐标来处理。
- 如果你需要频繁计算交集,可以把每个范围的q、r边界预存起来,避免重复计算。
内容的提问来源于stack exchange,提问作者WDUK
相关产品推荐
相关产品推荐

