Расчет расстояния с шестигранной сеткой (с плоской вершиной)

Я работал над созданием гексагональной сетки (с плоским верхом) для моделирования, над которым я работаю. Я попытался определить расстояние между шестиугольниками от заданного целевого шестиугольника.

Решение, которое у меня есть, работает большую часть времени, за исключением того, что каждый нечетный столбец целевого шестиугольника к северу от цели смещен на 1. Я знаю, что это звучит сбивающе, но я прикрепил изображение, чтобы объяснить, что я имею в виду:

шестиугольная игровая сетка

Как вы, ребята, видите, нижняя половина сетки под целевым шестиугольником и все остальные столбцы над целевым шестиугольником правильные. Я не могу понять почему: S

Вот объяснение Axial & Cube Co-ords.

http://www.redblobgames.com/grids/hexagons/#coordinates

Вот код, отвечающий за преобразование осевых коордов в кубические коорды.

public void setQR(int theQ, int theR){

    this.q = theQ;
    this.r = theR;

    this.x = this.q;
    this.z = this.r - (this.q - (this.q&1)) /2;
    this.y = -(this.x + this.z);
}

И вот код для определения расстояния.

К вашему сведению, шестиугольники создаются из CentrePoint (CPx, CPy).

    private double distance = 0;

public double workOutDistance(Hexagon hexagon, HexagonFood target){

    double targetX = target.getCPX();
    double targetY = target.getCPY();

    double hexagonX = hexagon.getCPX();
    double hexagonY = hexagon.getCPY();

    double deltaX = (targetX-hexagonX)*-1;
    double deltaY = (targetY-hexagonY)*-1;

    double deltaXRadius = (deltaX/(SimField.hexSize)/1.5);
    double deltaYApothem = (deltaY/(SimField.hexSize/1.155)/2);

    hexagon.setQR((int)deltaXRadius, (int)deltaYApothem);


    ArrayList<Integer> coords = new ArrayList<>();

    coords.add(
    Math.abs(hexagon.getX() - target.getX())
    );

    coords.add(
    Math.abs(hexagon.getZ() - target.getZ())
    );

    coords.add(
    Math.abs(hexagon.getY() - target.getY())
    );

    System.out.println(coords);
    distance = Collections.max(coords);

    return distance;
}

Кто-нибудь может сказать мне, почему это происходит? Буду очень признателен.

РЕДАКТИРОВАТЬ:

После изменения Int на Double, как было предложено Тимом, я получил это.

https://i.stack.imgur.com/javZb.png

**

РЕШЕНИЕ

**

После экспериментов с полученными ответами эта небольшая настройка решает проблему.

меняя это ..

public void setQR(int theQ, int theR){

    this.q = theQ;
    this.r = theR;

    this.x = this.q;
    this.z = this.r - (this.q - (this.q&1)) /2;
    this.y = -(this.x + this.z);
}

к этому..

public void setQR(int theQ, int theR){

    this.q = theQ;
    this.r = theR;

    this.x = this.q;
    if (this.r>0){
        this.z = this.r - (this.q - (this.q&1))/2;
    }
    else {
        this.z = this.r - (this.q + (this.q&1))/2;
    }
    this.y = -(this.x + this.z);
}

person Tomousee    schedule 15.07.2014    source источник
comment
Я бы подошел к этому, закрасив начало координат 0, затем все смежные шестиугольники с 1 и так далее, пока всем не будут присвоены номера.   -  person David Conrad    schedule 15.07.2014
comment
Вы можете объяснить свою кубическую систему координат? Ваша картина представляет собой двухмерное изображение; как у вас трехмерная система координат?   -  person Tim    schedule 15.07.2014
comment
Это была моя мысль, но мне нужно иметь возможность перемещать целевой шестиугольник в любую точку сетки динамически во время выполнения, поэтому необходимо иметь возможность перенумеровать сетку в любом месте, где она находится.   -  person Tomousee    schedule 15.07.2014
comment
Вот ссылка, объясняющая, что я имею в виду. redblobgames.com/grids/hexagons/#coordinates   -  person Tomousee    schedule 15.07.2014
comment
Изменение вычисления на double не поможет, если причина в том, что предложил Тим. Вместо этого вам придется Math.round. Вы должны убедиться, что значения, которые передаются в setQR, действительно верны (и желательно опубликовать MVCE). В любом случае деление с помощью магических констант типа .../1.155 выглядит весьма сомнительным ...   -  person Marco13    schedule 15.07.2014
comment
Вычисление апофемы шестиугольника из радиуса r = 25. a = sqrt (25 ^ 2- (25/2) ^ 2) = 21,65 (2dp). или сокращенно a = r / 1,155 = 21,65 (2dp).   -  person Tomousee    schedule 16.07.2014


Ответы (3)


Вы приводите двойное значение к типу int при вызове setQR (); Вы уверены, что делаете то, что ожидаете? Двойники используют математику с плавающей запятой, поэтому число, которое вы ожидали бы равняться 2,0, на самом деле может быть 1,9999999989, которое затем будет округлено до 1 при преобразовании в int.

Я также скептически отношусь к строке, которая читается как this.z = this.r - (this.q - (this.q&1)) /2;. Вы добавляете 1, когда число нечетное, что, по-видимому, является случаем отказа, с которым вы столкнулись; Я бы тоже убедился, что эта линия делает то, что вы ожидаете.

Если вы не выполните это с помощью отладчика и не исследуете значения, вы делаете это неправильно.

person Tim    schedule 15.07.2014
comment
Тим, я изменил то, что вы предлагали, и все время использовал дубликаты, однако это создало новую, но похожую проблему. - person Tomousee; 15.07.2014
comment
Я не предлагал вам изменить тип, я предлагал вам использовать отладчик, чтобы проверить, что происходит при запуске этих строк, и посмотреть, ожидаете ли вы этого. - person Tim; 15.07.2014
comment
Он не делал того, что я ожидал, но я не думал, что проблема заключалась в том, что после изменения типа на Double возникла другая проблема с моим расчетом расстояния. - person Tomousee; 15.07.2014
comment
Итак, у вас есть повторяющийся случай отказа; поздравляем, их легче всего отлаживать. Выберите один из неработающих и напишите тест JUnit, который просто вызывает workOutDistance() для этих двух шестиугольников. Затем установите точку останова в первой строке workOutDistance() и выполните отладку теста JUnit. (В Eclipse это можно сделать, щелкнув правой кнопкой мыши тестовый класс JUnit и выбрав Debug As- ›JUnit Test.) Построчно выполните код и после каждой строки изучите переменные, устанавливаемые в этой строке, и выполните следующие действия. убедитесь, что они соответствуют вашим ожиданиям, и обнаружите, где происходит что-то неожиданное. - person Tim; 15.07.2014
comment
Кроме того, я бы вернулся к исходному коду, вместо того, чтобы пытаться отлаживать двойную версию. В любом случае нет смысла устранять то, что вам не нужно. - person Tim; 16.07.2014
comment
Тим, спасибо за вашу помощь - person Tomousee; 16.07.2014

Вы также можете использовать совершенно другой подход к этой проблеме. Вы знаете (декартовы) координаты X / Y ваших двух шестиугольников, что означает, что вы можете получить кубические координаты каждого шестиугольника относительно начала вашего шестиугольного пространства. Расстояние между двумя шестиугольниками - это просто сумма абсолютных значений разностей между кубическими координатами X, Y и Z двух шестиугольников. (То есть dist = |h2.X - h1.X| + |h2.Y - h1.Y| + |h2.Z - h1.Z|) Поэтому вместо того, чтобы пытаться вычислить вектор между двумя центральными точками, а затем преобразовать его в кубические координаты, вы можете просто вычислить расстояние непосредственно в кубических координатах (точно так же, как если бы это были квадраты в декартовых координатах). ..

Тем не менее, даже если вы воспользуетесь этим подходом, я настоятельно рекомендую вам отладить то, что происходит, с помощью вашего исходного подхода. Даже если вы в конечном итоге выбросите код, упражнение по отладке, вероятно, преподнесет вам ценные уроки, которые вы сможете применить в будущем.

Примечание для читателей: «кубические» координаты не являются трехмерными декартовыми координатами, это система координат, специфичная для шестиугольника, для которой OP предоставил ссылку.

person Tim    schedule 15.07.2014

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

Вы должны попробовать изменить строку

hexagon.setQR((int)deltaXRadius, (int)deltaYApothem);

из исходного кода во что-то вроде

hexagon.setQR((int)Math.round(deltaXRadius), (int)Math.round(deltaYApothem));

Что могло бы решить проблему в этом случае.

Если нет ... или ... в любом случае, вот небольшой пример, который в основном делает то же самое, что и вы, но как MVCE ...

import java.awt.Point;

public class HexagonsTest
{
    public static void main(String[] args)
    {
        // Above and below
        test(8,6, 8,5,  1);
        test(8,6, 8,7,  1);

        // Left
        test(8,6, 7,5,  1);
        test(8,6, 7,6,  1);

        // Right
        test(8,6, 9,5,  1);
        test(8,6, 9,6,  1);

        // The first one that was wrong:
        test(8,6, 7,4,  2);
    }

    private static void test(int x0, int y0, int x1, int y1, int expected)
    {
        int distance = computeStepsDistance(x0, y0, x1, y1);
        System.out.println(
            "Distance of (" + x0 + "," + y0 + ") to " + 
            "(" + x1 + "," + y1 + ") is " + distance + 
            ", expected " + expected);
    }

    private static int computeStepsDistance(int x0, int y0, int x1, int y1)
    {
        Point cp0 = convertOffsetToCubeCoordinates(x0, y0, null);
        Point cp1 = convertOffsetToCubeCoordinates(x1, y1, null);
        int cx0 = cp0.x;
        int cy0 = cp0.y;
        int cz0 = -cx0-cy0;
        int cx1 = cp1.x;
        int cy1 = cp1.y;
        int cz1 = -cx1-cy1;
        int dx = Math.abs(cx0 - cx1); 
        int dy = Math.abs(cy0 - cy1); 
        int dz = Math.abs(cz0 - cz1); 
        return Math.max(dx, Math.max(dy, dz));
    }

    private static Point convertOffsetToCubeCoordinates(
        int ox, int oy, Point p) 
    {
        int cx = ox;
        int cz = oy - (ox - (ox&1)) / 2;
        int cy = -cx-cz;
        if (p == null)
        {
            p = new Point();
        }
        p.x = cx;
        p.y = cy;
        return p;
    }


}
person Marco13    schedule 15.07.2014
comment
Марко, спасибо за помощь, я просмотрю предоставленный вами пример. - person Tomousee; 16.07.2014