Шестиугольные сетки, как вы находите, в каком шестиугольнике находится точка? - PullRequest
31 голосов
/ 09 октября 2011

У меня есть карта, состоящая из строк и столбцов шестиугольников

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

Мне нужно знать, над какой мышью щелкает пользователь,

Каждый шестиугольник представлен экземпляром класса "Плитка", однако в нем нет места.конкретные данные, или даже многоугольник, так что, по сути, единственный способ узнать, где находится конкретный шестиугольник, это узнать его положение в массиве 2D.

Ранее я использовал квадратную сетку, и это было относительно легкочтобы выяснить, какой квадрат был выбран, потому что пиксели тоже квадратные,

        // example where each square is 10 by 10 pixels:

private void getClickedSquare(MouseEvent me)
    {
        int mouseX = me.getX();// e.g. 25
        int mouseY = me.getY();// e.g. 70

        int squareX= (int) (mouseX / 10);// in this case 2
        int squareY= (int) (mouseY / 10);// in this case 7

//then to access the tile I would do

        map.squares[squareX][squareY].whatever();
    }

Но я даже не уверен, с чего начать с шестиугольников, у кого-нибудь есть опыт?

Не могуиспользовать многоугольники (Java), так как когда я перемещаю карту по экрану и увеличиваю ее размер, у меня возникают проблемы с обновлением огромного количества многоугольников в каждом кадре.Хотя тогда я мог бы просто проверить, включена ли точка в какой-либо из полигонов тайла карты!

В настоящий момент отображаемые шестиугольники являются просто BufferedImages.

Если вы хотите узнать большеинформацию, пожалуйста, спросите, спасибо за ваше время: D

Ответы [ 6 ]

54 голосов
/ 10 октября 2011

(ОБНОВЛЕНО: переработан код, чтобы сделать его более понятным и эффективным) (ОБНОВЛЕНО: уменьшена длина ответа, исправлены ошибки в коде, улучшено качество изображений)

Hexagonal grid with an overlayed square grid

Это изображение показывает верхний левый угол гексагональной сетки и наложена синяя квадратная сетка. Легко определить, какой из квадратов находится внутри, и это дало бы грубое приближение к какому шестиугольнику тоже. Белые части шестиугольников показывают, где квадрат и гексагональная сетка имеют одинаковые координаты, а серые части шестиугольников показывают, где их нет.

Решение теперь так же просто, как найти, в каком поле находится точка, затем проверить, находится ли точка в каком-либо из треугольников, и исправить ответ, если необходимо.

private final Hexagon getSelectedHexagon(int x, int y)
{
    // Find the row and column of the box that the point falls in.
    int row = (int) (y / gridHeight);
    int column;

    boolean rowIsOdd = row % 2 == 1;

    // Is the row an odd number?
    if (rowIsOdd)// Yes: Offset x to match the indent of the row
        column = (int) ((x - halfWidth) / gridWidth);
    else// No: Calculate normally
        column = (int) (x / gridWidth);

В этот момент у нас есть строка и столбец прямоугольника, в котором находится наша точка, затем нам нужно проверить нашу точку на двух верхних краях шестиугольника, чтобы увидеть, находится ли наша точка в каком-либо из шестиугольников выше:

    // Work out the position of the point relative to the box it is in
    double relY = y - (row * gridHeight);
    double relX;

    if (rowIsOdd)
        relX = (x - (column * gridWidth)) - halfWidth;
    else
        relX = x - (column * gridWidth);

Наличие относительных координат облегчает следующий шаг.

Generic equation for a straight line

Как и на изображении выше, если y нашей точки равен > mx + c , мы знаем, что наша точка лежит выше линии, а в нашем случае шестиугольник выше и слева от текущей строки и столбца. Обратите внимание, что система координат в java имеет y, начинающийся с 0 в верхнем левом углу экрана, а не нижний левый, как обычно в математике, следовательно, отрицательный градиент, используемый для левого края, и положительный градиент, используемый для правой .

    // Work out if the point is above either of the hexagon's top edges
    if (relY < (-m * relX) + c) // LEFT edge
        {
            row--;
            if (!rowIsOdd)
                column--;
        }
    else if (relY < (m * relX) - c) // RIGHT edge
        {
            row--;
            if (rowIsOdd)
                column++;
        }

    return hexagons[column][row];
}

Краткое объяснение переменных, использованных в приведенном выше примере:

enter image description here enter image description here

m - градиент, поэтому m = c / halfWidth

9 голосов
/ 29 апреля 2014

РЕДАКТИРОВАТЬ: этот вопрос сложнее, чем я думал вначале, я перепишу свой ответ с некоторыми рабочими, однако я не уверен, что путь решения какой-либо улучшения по другим ответам.

вопрос можно перефразировать: при любом x, y найдите шестиугольник, центр которого ближе всего к x, y

, то есть минимизируйте dist_squared (Hex [n] .center, (x, y)) над n (квадрат означает, что выне нужно беспокоиться о квадратных корнях, которые экономят некоторый процессор)

Однако сначала мы должны сузить число шестиугольников для проверки - мы можем сузить их максимум до 5 следующим способом:

enter image description here

Итак, первый шаг - это выразить вашу точку (x, y) в УФ-пространстве, т.е. (x, y) = лямбда U + mu V, то есть = (лямбда, мю) в УФ-пространстве

Это просто двумерное матричное преобразование (http://playtechs.blogspot.co.uk/2007/04/hex-grids.html может быть полезно, если вы не понимаете линейные преобразования).

Теперь с учетом точки (лямбда, мю), если мы округляем оба до ближайшего целого числа, то имеем:

enter image description here

Везде в пределах Зеленого квадрата отображается обратно в (2,1)

Таким образом, большинство точек в этом Зеленом квадрате будут правильными, т.е. они находятся в шестиугольнике (2,1).

Но некоторые точки должны возвращать шестиугольник # (2,2), то есть:

enter image description here

Точно так же некоторые должны возвращать шестиугольник # (3,1).И затем в противоположном углу этого зеленого параллелограмма будут еще 2 области.

Итак, подведем итог: если int (lambda, mu) = (p, q), то мы, вероятно, находимся внутри шестиугольника (p,q) но мы также можем быть внутри шестиугольников (p + 1, q), (p, q + 1), (p-1, q) или (p, q-1)

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

Но оказывается, что вы можете сузить это до ~ 50%время без проверки расстояния, ~ 25% времени - одна проверка расстояния, а оставшиеся ~ 25% времени - 2 проверки расстояния (я угадываю цифры, глядя на области, на которых работает каждая проверка):

p,q = int(lambda,mu)

if lambda * mu < 0.0:
    // opposite signs, so we are guaranteed to be inside hexagon (p,q)
    // look at the picture to understand why; we will be in the green regions
    outPQ = p,q

enter image description here

else:
    // circle check
    distSquared = dist2( Hex2Rect(p,q), Hex2Rect(lambda, mu) )

    if distSquared < .5^2:
        // inside circle, so guaranteed inside hexagon (p,q)
        outPQ = p,q

enter image description here

    else:
        if lambda > 0.0:
            candHex = (lambda>mu) ? (p+1,q): (p,q+1)
        else:
            candHex = (lambda<mu) ? (p-1,q) : (p,q-1)

И этот последний тест можно привести в порядок:

     else:
        // same sign, but which end of the parallelogram are we?
        sign = (lambda<0) ? -1 : +1
        candHex = ( abs(lambda) > abs(mu) ) ? (p+sign,q) : (p,q+sign)

Теперь мы сузили его до еще одного возможного шестиугольника, нам просто нужно найти, который ближе:

        dist2_cand = dist2( Hex2Rect(lambda, mu), Hex2Rect(candHex) )

        outPQ = ( distSquared < dist2_cand ) ? (p,q) : candHex

Функция Dist2_hexSpace (A, B) привела бы в порядок вещи.

4 голосов
/ 13 мая 2016

Я начал с просмотра ответа @pi https://stackoverflow.com/a/23370350/5776618 и подумал, что было бы интересно попробовать что-то похожее в координатах куба с UVW-пространством (а не с 2D-осевым УФ-пространством).

Карта следующих уравнений (x, y) => (u, v, w)

u = (2/3)*x;
v = -(1/3)*x + (1/2)*y;
w = -(1/3)*x - (1/2)*y;

Тогда это так же просто, как округление u,v и w до ближайшего целого числа и преобразование обратно в x, y .Однако есть большая загвоздка ...

В ответе выше отмечается, что при округлении в УФ-пространстве будет несколько областей, которые отображаются неправильно:

enter image description here

Это также происходит при использовании кубических координат:

enter image description here

Любая область в оранжевых треугольниках составляет> 0,5 единицы отцентр шестиугольника и, когда закруглено, будет округляться прочь от центра.Это показано выше, поскольку все, что находится в красном треугольнике (слева от линии u = 1.5), будет неправильно округлено до u = 1, а не до u = 2.

Здесь приведены некоторые ключевые наблюдения ...

1.Оранжевые / красные проблемные области не перекрываются

2.В координатах куба действительные шестнадцатеричные центры имеют u + v + w = ​​0

. В приведенном ниже коде u, v и w все округляются с начала как округление только в проблеме, еслиокругленные координаты не суммируются с нулем.

uR = Math.round(u);
vR = Math.round(v);
wR = Math.round(w);

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

arr = [ Math.abs(u-uR), Math.abs(v-vR), Math.abs(w-wR) ];
var i = arr.indexOf(Math.max(...arr));

После того, как координатная задача найдена, она округляется в другом направлении.Итоговые значения (x, y) затем рассчитываются по округленному / исправленному (u, v, w).

nearestHex = function(x,y){

  u = (2/3)*x;
  v = -(1/3)*x + (1/2)*y;
  w = -(1/3)*x - (1/2)*y;

  uR = Math.round(u);
  vR = Math.round(v);
  wR = Math.round(w);

  if(uR+vR+wR !== 0){
    arr = [ Math.abs(u-uR), Math.abs(v-vR), Math.abs(w-wR) ];
    var i = arr.indexOf(Math.max(...arr));

    switch(i){
      case 0:
        Math.round(u)===Math.floor(u) ? u = Math.ceil(u) : u = Math.floor(u);
        v = vR; w = wR;
        break;

      case 1:
        Math.round(v)===Math.floor(v) ? v = Math.ceil(v) : v = Math.floor(v);
        u = uR; w = wR;
        break;

      case 2:
        Math.round(w)===Math.floor(w) ? w = Math.ceil(w) : w = Math.floor(w);
        u = uR; v = vR;
        break;
    }
  }

  return {x: (3/2)*u, y: v-w};

}
4 голосов
/ 15 октября 2014

Это дополнение к ответу Себастьяна Троя.Я бы оставил это как комментарий, но мне пока не хватает репутации.

Если вы хотите реализовать осевую систему координат, как описано здесь: http://www.redblobgames.com/grids/hexagons/

Вы можете внести небольшую модификациюк коду.

Вместо

// Is the row an odd number?
if (rowIsOdd)// Yes: Offset x to match the indent of the row
    column = (int) ((x - halfWidth) / gridWidth);
else// No: Calculate normally
    column = (int) (x / gridWidth);

используйте это

float columnOffset = row * halfWidth;
column = (int)(x + columnOffset)/gridWidth; //switch + to - to align the grid the other way

Это сделает координату (0, 2) в том же диагональном столбце, что и (0, 0) и (0, 1) вместо того, чтобы быть непосредственно ниже (0, 0).

3 голосов
/ 02 мая 2014

Я еще раз взглянул на http://playtechs.blogspot.co.uk/2007/04/hex-grids.html, и это математически очень аккуратно.

Однако подход Себастьяна, похоже, подходит к погоне и выполняет задачу в удивительно небольшом количестве строк кода.,

Если вы прочитаете раздел комментариев, то обнаружите, что кто-то написал реализацию Python на http://gist.github.com/583180

. Я приведу это здесь для потомков:

# copyright 2010 Eric Gradman
# free to use for any purpose, with or without attribution
# from an algorithm by James McNeill at
# http://playtechs.blogspot.com/2007/04/hex-grids.html

# the center of hex (0,0) is located at cartesian coordinates (0,0)

import numpy as np

# R ~ center of hex to edge
# S ~ edge length, also center to vertex
# T ~ "height of triangle"

real_R = 75. # in my application, a hex is 2*75 pixels wide
R = 2.
S = 2.*R/np.sqrt(3.)
T = S/2.
SCALE = real_R/R

# XM*X = I
# XM = Xinv
X = np.array([
    [ 0, R],
    [-S, S/2.]
])
XM = np.array([
    [1./(2.*R),  -1./S],
    [1./R,        0.  ]
])
# YM*Y = I
# YM = Yinv
Y = np.array([
    [R,    -R],
    [S/2.,  S/2.]
])
YM = np.array([
    [ 1./(2.*R), 1./S],
    [-1./(2.*R), 1./S],
])

def cartesian2hex(cp):
    """convert cartesian point cp to hex coord hp"""
    cp = np.multiply(cp, 1./SCALE)
    Mi = np.floor(np.dot(XM, cp))
    xi, yi = Mi
    i = np.floor((xi+yi+2.)/3.)

    Mj = np.floor(np.dot(YM, cp))
    xj, yj = Mj
    j = np.floor((xj+yj+2.)/3.)

    hp = i,j
    return hp

def hex2cartesian(hp):
    """convert hex center coordinate hp to cartesian centerpoint cp"""
    i,j = hp
    cp = np.array([
        i*(2*R) + j*R,
        j*(S+T)
    ])
    cp = np.multiply(cp, SCALE)

return cp
2 голосов
/ 17 июня 2015

Я не знаю, поможет ли это кому-нибудь, но я нашел гораздо более простое решение.Когда я создаю свой шестиугольник, я просто даю им среднюю точку и, находя ближайшую среднюю точку с помощью координаты мыши, я могу найти, какая из них включена!

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