Генерация треугольных / гексагональных координат (xyz) - PullRequest
27 голосов
/ 12 января 2010

Я пытаюсь придумать итеративную функцию, которая генерирует координаты XYZ для гексагональной сетки. С начальной шестнадцатеричной позицией (скажем, 0,0,0 для простоты) я хочу вычислить координаты для каждого последующего «кольца» шестиугольников, как показано здесь:

image

So far, all I've managed to come up with is this (example in 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);
        }
    }
}

Я знаю, что каждое кольцо содержит на шесть пунктов больше, чем предыдущее, и каждое 120 & deg; вектор содержит одну дополнительную точку для каждого шага от центра. Я также знаю, что 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

Ответы [ 4 ]

13 голосов
/ 12 января 2010

Другое возможное решение, которое работает в O (радиус 2 ), в отличие от O (радиус 4 ) из * 1009 Решение * tehMick (за счет большого количества стиля) таково:

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 ребрам.

12 голосов
/ 12 января 2010

Не только 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("");
}
5 голосов
/ 14 мая 2011

Это была забавная головоломка.

O (радиус 2 ) , но с (надеюсь) немного большим стилем, чем решение Офри. Мне пришло в голову, что координаты могут быть сгенерированы, как если бы вы «шли» по кольцу, используя вектор направления (движения), и поворот был эквивалентен смещению нуля вокруг вектора движения.

Эта версия также имеет преимущество перед решением Эрика в том, что оно никогда не затрагивает недопустимые координаты (Эрик отвергает их, но эта никогда даже не должна их проверять).

# 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.

1 голос
/ 19 января 2010

Хорошо, попробовав обе опции, я остановился на решении 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 за вашу помощь!

JS

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...