【问题标题】:Generating triangular/hexagonal coordinates (xyz)生成三角/六角坐标 (xyz)
【发布时间】:2010-01-12 13:24:22
【问题描述】:

我正在尝试提出一个迭代函数,为六边形网格生成 xyz 坐标。使用起始十六进制位置(为简单起见,例如 0,0,0),我想计算每个连续的六边形“环”的坐标,如下所示:

到目前为止,我想出的只是这个(javascript 中的示例):

var radius = 3
var xyz = [0,0,0];

// for each ring
for (var i = 0; i < radius; i++) {
    var tpRing = i*6;
    var tpVect = tpRing/3;
    // for each vector of ring
    for (var j = 0; j < 3; j++) {
        // for each tile in vector
        for(var k = 0; k < tpVect; k++) {
            xyz[0] = ???;
            xyz[1] = ???;
            xyz[2] = ???;
            console.log(xyz);
        }
    }
}

我知道每个环包含的点比前一个多 6 个,并且每个 120° 向量从中心开始每一步都包含一个额外的点。我也知道x + y + z = 0 适用于所有瓷砖。但是如何生成遵循以下顺序的坐标列表?

    0, 0, 0

    0,-1, 1
    1,-1, 0
    1, 0,-1
    0, 1,-1
   -1, 1, 0
   -1, 0, 1

    0,-2, 2
    1,-2, 1
    2,-2, 0
    2,-1,-1
    2, 0,-2
    1, 1,-2
    0, 2,-2
   -1, 2,-1
   -2, 2, 0
   -2, 1, 1
   -2, 0, 2
   -1,-1, 2

【问题讨论】:

  • 小修正。每个环包含 6*k 个点,或比前一个多 6*(k-1) 个点,其中 k 是从零开始的环索引。跨度>

标签: math coordinates vector coordinate-systems hexagonal-tiles


【解决方案1】:

不仅是x + y + z = 0,而且x、y和z的绝对值等于圆环半径的两倍。这应该足以识别每个连续环上的每个六边形:

var radius = 4;
for(var i = 0; i < radius; i++)
{
    for(var j = -i; j <= i; j++)
    for(var k = -i; k <= i; k++)
    for(var l = -i; l <= i; l++)
        if(Math.abs(j) + Math.abs(k) + Math.abs(l) == i*2 && j + k + l == 0)
            console.log(j + "," + k + "," + l);
    console.log("");
}

【讨论】:

  • 哇,这非常简单!我一直盯着序列试图找到这样的关系,但对几何数学了解不多,这让我很头疼。多亏了您的示例,我现在看到了这种关系(查看有关绝对值的 Wikipedia 文章进一步帮助了便士下降)。我会试一试,但它看起来已经是一个公认的答案。谢谢!
  • 我想这个方法即使起点不是0,0,0也可以用?我将如何输入起始坐标? - 实际上,别介意,我想我知道该怎么做。完成后将发布完整的解决方案。
  • 是的,你可能已经猜到了,从你想输出的最内环的半径处开始。
  • 我昨晚实现了这个,效果很好,谢谢!但是,我想不出一种方法将起始位置从 0,0,0 偏移到 5,-3,-2 并围绕该点绘制网格。有什么建议吗?
  • 你总是可以抵消你的输出。
【解决方案2】:

另一种可能的解决方案,在 O(radius2) 中运行, 不同于 O(radius4) tehMick's solution 的(牺牲了很多风格)是这样的:

radius = 4
for r in range(radius):
    print "radius %d" % r
    x = 0
    y = -r
    z = +r
    print x,y,z
    for i in range(r):
        x = x+1
        z = z-1
        print x,y,z
    for i in range(r):
        y = y+1
        z = z-1
        print x,y,z
    for i in range(r):
        x = x-1
        y = y+1
        print x,y,z
    for i in range(r):
        x = x-1
        z = z+1
        print x,y,z
    for i in range(r):
        y = y-1
        z = z+1
        print x,y,z
    for i in range(r-1):
        x = x+1
        y = y-1
        print x,y,z

或者写得更简洁一点:

radius = 4
deltas = [[1,0,-1],[0,1,-1],[-1,1,0],[-1,0,1],[0,-1,1],[1,-1,0]]
for r in range(radius):
    print "radius %d" % r
    x = 0
    y = -r
    z = +r
    print x,y,z
    for j in range(6):
        if j==5:
            num_of_hexas_in_edge = r-1
        else:
            num_of_hexas_in_edge = r
        for i in range(num_of_hexas_in_edge):
            x = x+deltas[j][0]
            y = y+deltas[j][1]
            z = z+deltas[j][2]            
            print x,y,z

它的灵感来自于六边形实际上位于六边形本身的外部,因此您可以找到其中 1 个点的坐标,然后通过在其 6 条边上移动来计算其他点。

【讨论】:

  • 这也是一个有趣的解决方案,谢谢!我实际上已经在我的项目中实现了这两种方法,并且能够在它们之间进行切换,并且目前正在运行一些基准测试以查看哪种方法更快。另一个因素是将“开始”位置从 0,0,0 更改为网格上的另一个点(例如 5、-3、-2)并围绕该点生成网格是多么容易。对此有何想法?
  • 很简单:在第一行x=0, y=-r, z=+r 行中,加上起始位置,比如:x=x0, y=y0-r, z=z0 +r,你就完成了:)
  • 最后,这是我接受的答案,因为它更快更容易提供偏移起始位置。请参阅下面的答案以了解最终实施。谢谢奥弗里!
【解决方案3】:

这是一个有趣的谜题。

O(radius2) 但(希望)比Ofri's solution. 风格多一点我突然想到可以生成坐标,就好像你在“走路”一样" 使用方向(移动)向量围绕圆环,并且转一圈相当于围绕移动向量移动零。

这个版本也比Eric's solution 有优势,因为它从不涉及无效坐标(Eric 拒绝它们,但这个版本甚至不需要测试它们)。

# enumerate coords in rings 1..n-1; this doesn't work for the origin
for ring in range(1,4):
    # start in the upper right corner ...
    (x,y,z) = (0,-ring,ring)
    # ... moving clockwise (south-east, or +x,-z)
    move = [1,0,-1]         

    # each ring has six more coordinates than the last
    for i in range(6*ring):
        # print first to get the starting hex for this ring
        print "%d/%d: (%d,%d,%d) " % (ring,i,x,y,z)
        # then move to the next hex
        (x,y,z) = map(sum, zip((x,y,z), move))

        # when a coordinate has a zero in it, we're in a corner of
        # the ring, so we need to turn right
        if 0 in (x,y,z):
            # left shift the zero through the move vector for a
            # right turn
            i = move.index(0)
            (move[i-1],move[i]) = (move[i],move[i-1])

    print # blank line between rings

为python的序列切片喝彩三声。

【讨论】:

    【解决方案4】:

    const canvas = document.getElementById('canvas');
    const ctx = canvas.getContext('2d');
    ctx.textAlign = "center";
    
    const radius = 20;
    const altitude = Math.sqrt(3) * radius;
    const total = 3;
    for (let x = -total; x <= total; x++) {
        let y1 = Math.max(-total, -x-total);
        let y2 = Math.min(total, -x+total);
        for (let y = y1; y <= y2; y++) {
            let xx = x * altitude + Math.cos(1/3*Math.PI) * y * altitude;
            let yy = y * radius * 1.5;
            xx += canvas.width/2;
            yy += canvas.height/2;
            drawHex(xx, yy, radius);
            ctx.fillText(x+","+y, xx, yy);
        }
    }
    
    function drawHex(x, y, radius){
        ctx.beginPath();
        for(let a = 0; a < Math.PI*2; a+=Math.PI/3){
            let xx = Math.sin(a) * radius + x;
            let yy = Math.cos(a) * radius + y;
            if(a == 0) ctx.moveTo(xx, yy);
            else ctx.lineTo(xx, yy);
        }
        ctx.stroke();
    }
    &lt;canvas id="canvas" width=250 height=250&gt;

    【讨论】:

      【解决方案5】:

      在尝试了这两个选项之后,我选择了 Ofri 的解决方案,因为它稍微快一点,并且可以很容易地提供初始偏移值。我的代码现在看起来像这样:

      var xyz = [-2,2,0];
      var radius = 16;
      var deltas = [[1,0,-1],[0,1,-1],[-1,1,0],[-1,0,1],[0,-1,1],[1,-1,0]];
      for(var i = 0; i < radius; i++) {
              var x = xyz[0];
              var y = xyz[1]-i;
              var z = xyz[2]+i;
              for(var j = 0; j < 6; j++) {
                      for(var k = 0; k < i; k++) {
                              x = x+deltas[j][0]
                              y = y+deltas[j][1]
                              z = z+deltas[j][2]
                              placeTile([x,y,z]);
                      }
              }
      }
      

      placeTile 方法使用cloneNode 复制预定义的 SVG 元素,执行每个图块大约需要 0.5 毫秒,这已经足够了。非常感谢 tehMick 和 Ofri 的帮助!

      【讨论】:

      • 这不适用于半径 = 1。您在第二个 for 循环之前缺少 placeTile([x,y,z]);
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-11-18
      • 2016-10-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-13
      相关资源
      最近更新 更多