Манхэттен Расстояние между плитками в гексагональной сетке

Для квадратной сетки евклидово расстояние между плитками A и B равно:

distance = sqrt(sqr(x1-x2)) + sqr(y1-y2))

Для актера, вынужденного двигаться по квадратной сетке, Манхэттенское расстояние является лучшим показателем фактического расстояния, которое мы должны пройти:

manhattanDistance = abs(x1-x2) + abs(y1-y2))

Как мне получить манхэттенское расстояние между двумя плитками в гексагональной сетке, как показано красной и синей линиями ниже?

введите описание изображения здесь


person Coyod    schedule 22.02.2011    source источник
comment
Я не уверен, что ваш вопрос имеет смысл. Вы имеете в виду, как вы измеряете длину красных или синих линий?   -  person Chris Pfohl    schedule 23.02.2011
comment
Вопрос не выглядит разумным, потому что он описывает евклидово расстояние в квадратной решетке, но, похоже, требует манхэттенского расстояния в гексагональной решетке.   -  person Svante    schedule 23.02.2011
comment
извините за путаницу, я имею в виду, сколько переходит от A к B по одному из кратчайших путей.   -  person Coyod    schedule 24.02.2011


Ответы (6)


Однажды я установил в игре гексагональную систему координат так, чтобы ось y находилась под углом 60 градусов к оси x. Это позволяет избежать разделения четных и нечетных строк.

Шестиугольная сетка
(источник: althenia.net)

Расстояние в этой системе координат:

dx = x1 - x0
dy = y1 - y0

if sign(dx) == sign(dy)
    abs(dx + dy)
else
    max(abs(dx), abs(dy))

Вы можете преобразовать (x ', y) из вашей системы координат в (x, y) в этой с использованием:

x = x' - floor(y/2)

Итак, dx становится:

dx = x1' - x0' - floor(y1/2) + floor(y0/2)

Будьте осторожны с округлением при использовании целочисленного деления. В C для int y floor(y/2) это (y%2 ? y-1 : y)/2.

person aaz    schedule 22.02.2011
comment
Я как раз писал похожий ответ, так что +1. Однако целочисленное деление не подходит для преобразования координат. y/2 должно быть floor (y / 2), где / дает рациональное значение, а floor округляет до отрицательной бесконечности. - person Svante; 23.02.2011
comment
@Svante - Спасибо. Это псевдокод «делай что я хочу», который, конечно же, округляется в меньшую сторону :) - person aaz; 23.02.2011
comment
Спасибо, aaz. Это именно то, что мне нужно. - person Coyod; 23.02.2011
comment
Если вы перевернете ось Y, вам нужно изменить abs (dx + dy) на abs (dx) + abs (dy). (Это также работает, если ось Y остается неизменной, поэтому это также более общий алгоритм.) - person Soren Johnson; 23.02.2012
comment
@SorenJohnson Недостаточно изменить абс (dx + dy), вам также нужно изменить оператор if на sign (dx)! = Sign (dy) (это меня просто укусило, вот откуда я знаю ...) Кроме того, на случай, если кому-то интересно, sign () - это функция, которая возвращает -1, если число отрицательное, ноль, если оно равно нулю, и 1, если оно положительное. - person Fylke; 10.05.2012
comment
довольно круто. эта модель демонстрирует, что координатное пространство в шестигранной сетке похоже на квадратную сетку, за исключением того, что любая ячейка примыкает к 6 ячейкам вместо 4 в квадратной сетке. Любите меня немного геометрии в моей информатике. - person Randy L; 11.05.2013
comment
См. Этот документ: Э. Д. Лучак и А. Розенфельд, «Расстояние на гексагональной сетке», IEEE Trans. Вычислит., Т. С – 25, вып. 5. С. 532–533, май 1976 г. - person lxg; 15.07.2020

Я предполагаю, что вам нужно евклидово расстояние в плоскости между центрами двух плиток, которые обозначены, как вы показали на рисунке. Думаю, это можно вывести из рисунка. Для любых x и y вектор от центра плитки (x, y) до центра плитки (x + dx, y) равен (dx, 0). Вектор из центра плитки (x, y) и (x, y + dy) равен (-dy / 2, dy * sqrt (3) / 2). Простое сложение векторов дает вектор (dx - (dy / 2), dy * sqrt (3) / 2) между (x, y) и (x + dx, y + dy) для любых x, y, dx, и ды. Общее расстояние тогда является нормой вектора: sqrt ((dx - (dy / 2)) ^ 2 + 3 * dy * dy / 4)

person Ted Hopp    schedule 22.02.2011

Если вы хотите расстояние по прямой:

double dy = y2 - y1;
double dx = x2 - x1;
// if the height is odd
if ((int)dy & 1){
    // whether the upper x coord is displaced left or right
    // depends on whether the y1 coordinate is odd
    dx += ((y1 & 1) ? -0.5 : 0.5);
}
double dis = sqrt(dx*dx + dy*dy);

Я пытаюсь сказать, что если dy чётно, это просто прямоугольное пространство. Если dy нечетно, положение верхнего правого угла составляет 1/2 единицы влево или вправо.

person Mike Dunlavey    schedule 22.02.2011
comment
Есть ли смысл в том, чтобы число с плавающей запятой было четным? Является ли битовое и допустимым способом проверки четности числа с плавающей запятой? - person Svante; 23.02.2011
comment
@Svante: Хороший момент. Я наложил гипс. Единственная причина, по которой я вставил double, заключалась в том, что 1/2 могла войти в изображение. - person Mike Dunlavey; 23.02.2011
comment
числа с плавающей запятой обычно плохо заменяют рациональные числа. Вы уверены, что (int)1.0 равно 1 в любом компиляторе, который вы собираетесь использовать? Что, если это (int)0.999999 или (int)1.00000001? Если вам абсолютно необходимо использовать числа с плавающей запятой, производите их как можно позже. - person Svante; 23.02.2011
comment
@Svante: Еще раз хороший момент. Вы можете предположить, что числа с плавающей запятой точны для целых чисел, но все же, вероятно, не стоит полагаться на нее. В этом случае разумнее было бы работать с полуцелыми числами. Я просто не хотел запутывать код. - person Mike Dunlavey; 23.02.2011
comment
Спасибо за ваше внимание. Моя идея заключалась бы в том, чтобы не указывать какие-либо детали реализации в псевдокоде, то есть без типов, без приведения, без перебора битов, без неточных функций, а = обозначает равенство, а не присваивание. - person Svante; 23.02.2011

Прямого ответа на этот вопрос невозможно. Ответ на этот вопрос во многом зависит от того, как вы организуете свои плитки в памяти. Я использую вертикальную компоновку odd-q, и следующий код Matlab всегда дает мне правильный ответ.

function f = offset_distance(x1,y1,x2,y2)
    ac = offset_to_cube(x1,y1);
    bc = offset_to_cube(x2,y2);
    f = cube_distance(ac, bc);
end

function f = offset_to_cube(row,col)
    %x = col - (row - (row&1)) / 2;
    x = col - (row - mod(row,2)) / 2;
    z = row;
    y = -x-z;
    f = [x,z,y];
end

function f= cube_distance(p1,p2)
    a = abs( p1(1,1) - p2(1,1));
    b = abs( p1(1,2) - p2(1,2));
    c = abs( p1(1,3) - p2(1,3));
    f =  max([a,b,c]);
end

Вот код тестирования Matlab

sx = 6;
sy = 1;
for i = 0:7
    for j = 0:5
        k = offset_distance(sx,sy,i,j);
        disp(['(',num2str(sx),',',num2str(sy),')->(',num2str(i),',',num2str(j),')=',num2str(k)])
    end
end

Для получения математических подробностей этого решения посетите: http://www.redblobgames.com/grids/hexagons/ < / а>. Вы можете получить полную библиотеку hextile по адресу: http://www.redblobgames.com/grids/hexagons/implementation.html

person Md Monjur Ul Hasan    schedule 12.11.2016

Похоже, это работа для линейного алгоритма Брезенхема. Вы можете использовать это, чтобы подсчитать количество сегментов, которые нужно пройти от A до B, и это покажет вам расстояние пути.

person dthorpe    schedule 22.02.2011

Если вы определяете различные шестиугольники как граф, вы можете получить кратчайший путь от узла A к узлу B. Поскольку расстояние от центров шестиугольников постоянно, установите его как вес ребра.

Однако это, вероятно, будет неэффективно для больших полей.

person Argote    schedule 22.02.2011