Шестиугольные сетки, как вы находите, в каком шестиугольнике находится точка?
У меня есть карта, состоящая из строк и столбцов шестиугольников ![]()
Это не фактическое изображение гексаграммы, которую я использую, но использует шестиугольники размера и формы
Мне нужно знать, с какой мышью заканчивается щелчок пользователя,
Каждый шестиугольник представлен экземпляром класса "Плитка", однако он не содержит данных о местоположении или даже многоугольника, поэтому в основном единственный способ сказать, где находится конкретный шестиугольник, - это знать его положение в 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();
}
Но я даже не знаю, с чего начать с Hexagons, есть ли у кого-нибудь опыт?
Я не могу использовать полигоны (Java), так как, когда я нахожусь для перемещения карты по экрану и увеличения ее размера, у меня возникнут проблемы с обновлением огромного количества полигонов в каждом кадре. Хотя тогда я мог бы просто проверить, включена ли точка в любой из полигонов плитки карты!
В настоящий момент отображаемые шестиугольники - это просто BufferedImages.
Если вы хотите узнать больше информации, пожалуйста, спросите,
Спасибо за ваше время: D
Ответы
Ответ 1
(ОБНОВЛЕНО: обновленный код, чтобы сделать его более понятным и более эффективным)
(ОБНОВЛЕНО: уменьшенная длина ответа, исправлены ошибки в коде, улучшенное качество изображений)
![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 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
Ответ 2
EDIT: этот вопрос сложнее, чем я думал поначалу, я буду переписывать свой ответ с некоторыми работами, однако я не уверен, что путь решения - это улучшение других ответов.
Вопрос можно перефразировать: если любые х, у найдут шестиугольник, центр которого ближе всего к х, у
то есть. минимизировать dist_squared (Hex [n].center, (x, y)) над n (квадрат означает, что вам не нужно беспокоиться о квадратных корнях, которые сохраняют некоторый процессор)
Однако сначала нужно сузить число шестиугольников для проверки - мы можем сузить его до максимума 5 следующим способом:
![enter image description here]()
Итак, первый шаг. Выразите свою точку (x, y) в UV-пространстве
(x, y) = lambdaU + muV, so = (lambda, mu) в УФ-пространстве
Это просто двумерное матричное преобразование (http://playtechs.blogspot.co.uk/2007/04/hex-grids.html может оказаться полезным, если вы не понимаете линейные преобразования).
Теперь, учитывая точку (lambda, mu), если мы округлим оба до ближайшего целого числа, мы получим следующее:
![enter image description here]()
Всюду в зеленой площади возвращается обратно (2,1)
Таким образом, большинство точек в пределах этой Зеленого квадрата будут правильными, т.е. они находятся в шестиугольнике (2,1).
Но некоторые точки должны возвращать шестиугольник # (2,2), т.е.
![enter image description here]()
Аналогичным образом некоторые должны возвращать шестиугольник # (3,1). А затем на противоположном углу этого зеленого параллелограмма будет еще две области.
Итак, если int (lambda, mu) = (p, q), то мы, вероятно, находимся внутри шестиугольника (p, q), но мы также можем быть внутри шестиугольников (p + 1, q), (p, q +1), (p-1, q) или (p, q-1)
Несколько способов определить, какой из них имеет место. Проще всего было бы преобразовать центры всех этих 5 шестиугольников обратно в исходную систему координат и найти наиболее близкую к нашей точке.
Но оказывается, что вы можете сузить это до ~ 50% времени, не проводя дистанционных проверок, ~ 25% времени, выполняющего одну дистанционную проверку, а оставшиеся ~ 25% времени делают 2 дистанционных проверки (I "Угадывая цифры, просматривая области, на которые каждая проверка работает):
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) еще больше упростит процесс.
Ответ 3
Я начал с просмотра @pi answer fooobar.com/questions/192504/... и подумал, что было бы интересно попробовать что-то подобное в координатах куба с 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. Однако существует серьезная проблема...
В ответе выше, он отметил, что округление в УФ-пространстве будет иметь несколько областей, которые неверно отображают:
![введите описание изображения здесь]()
Это также происходит при использовании координат куба:
![введите описание изображения здесь]()
Любая область оранжевых треугольников составляет > 0,5 единицы от центра шестиугольника, а при округлении будет округлять AWAY от центра. Это показано выше, поскольку что-либо в красном треугольнике (слева от линии u = 1,5) будет неправильно округлено u до 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);
Если они не суммируются с нулем, потому что проблемные области не перекрываются, будет только одна координата, которая будет округлена неправильно. Эта координата также является координатой, которая была округлена больше всего.
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
Это дополнение к ответу СебастьянТрой. Я бы оставил его в качестве комментария, но пока еще недостаточно репутации.
Если вы хотите реализовать осевую систему координат, как описано здесь:
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).
Ответ 5
У меня был другой взгляд на 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
Ответ 6
Я не знаю, поможет ли это кому-либо, но я придумал гораздо более простое решение. Когда я создаю свой Hexagon im, просто давая им среднюю точку, и, найдя ближайшую среднюю точку с помощью мышиной координаты, я могу найти, как один из них!