说在前面
- 原文地址:Hexagonal Grids,强烈建议原文体验一下
- 上一篇:【算法记录/六边形网格】(四)距离
- 友链:kwen.page
- 其他:不说了,我就是个渣渣
步骤
-
怎样在两个正六边形格点间绘制一条直线呢?通常,我们可以使用线性插值方法来绘制直线。首先均匀地在直线上采样 N + 1 N+1 N+1个点,然后计算这些点在哪个六边形格点中。
- 计算两个六边形格点之间的距离,见【算法记录/六边形网格】(四)距离。假设距离为 N N N,起点为 A A A,终点为 B B B。
- 然后均匀地在 A A A、 B B B之间选择 N + 1 N+1 N+1个点。在使用线性插值的情况下,每个点为 A + ( B − A ) ∗ 1.0 / N ∗ i A+(B-A)*1.0/N*i A+(B−A)∗1.0/N∗i, i ∈ [ 0 , N ] i\in[0,N] i∈[0,N]。整个运算结果为浮点数。
- 将每个采样点(浮点数)转换为对应的六边形格点坐标(整型)。
-
伪代码
// 浮点坐标转为整型坐标 function cube_round(cube): // 四舍五入取整 var rx = round(cube.x) var ry = round(cube.y) var rz = round(cube.z) // 小数位 var x_diff = abs(rx - cube.x) var y_diff = abs(ry - cube.y) var z_diff = abs(rz - cube.z) // 小数位最大的重新进行计算 // 例如 (2.5 -1.2 -1.3) // 四舍五入 (3, -1, -1) 但是这个坐标不满足x+y+z=0 // 由于0.5 > 0.2、0.3 所以重新计算x // 最终结果应该是(2, -1, -1) if x_diff > y_diff and x_diff > z_diff: rx = -ry-rz else if y_diff > z_diff: ry = -rx-rz else: rz = -rx-ry return Cube(rx, ry, rz) // 浮点插值函数 function lerp(a, b, t): # for floats return a + (b - a) * t // 立方体坐标插值,此时返回的立方体坐标为浮点数 function cube_lerp(a, b, t): # for hexes return Cube(lerp(a.x, b.x, t), lerp(a.y, b.y, t), lerp(a.z, b.z, t)) function cube_linedraw(a, b): // 计算两个格点之间的六边形距离 var N = cube_distance(a, b) var results = [] // 计算采样点 for each 0 ≤ i ≤ N: // 采样点转换为六边形坐标(整型) results.append(cube_round(cube_lerp(a, b, 1.0/N * i))) return results
补充说明
-
有时候,在计算后得到的点有可能刚好在六边形的边上。但是
cube_round
的结果会比较随机,如下图,有时候会是1,有时候会是2。如果我们将其放在同一边,那么绘制的线会好看一点。
我们可以通过在循环开始前给其中一端(起点或终点)增加一丢丢偏移(或者两端同时), C u b e ( 1 e − 6 , 2 e − 6 , − 3 e − 6 ) Cube(1e-6, 2e-6, -3e-6) Cube(1e−6,2e−6,−3e−6)。这样可以将线稍稍偏移,避免点在边上。 -
在方形网格DDA算法中,我们将 N N N设置为两点之间各方向轴上的最大距离。在六边形网格中也是如此。
-
cube_lerp
算法需要返回浮点数据。如果使用的是静态类型语言,我们可以自己定义一个FloatCube
结构/方法来代替上面的Cube
,或者直接将cube_lerp
函数去掉,将其内置到cube_linedraw
函数中。function cube_round(x, y, z): // 四舍五入取整 var rx = round(x) var ry = round(y) var rz = round(z) // ...... function cube_linedraw(a, b): // 计算两个格点之间的六边形距离 var N = cube_distance(a, b) var results = [] // 计算采样点 for each 0 ≤ i ≤ N: // 采样点转换为六边形坐标(整型) results.append(cube_round(lerp(a.x, b.x, 1.0/N * i), lerp(a.y, b.y, 1.0/N * i), lerp(a.z, b.z, 1.0/N * i))) return results
-
若想要在偏移坐标下绘制直线,可以参考这个