Простая генерация гексагональной (шестиугольной) сетки из центра + поворот матрицы на любой угол. Апгрейд 2019 года.
Автор: Виктор Исаев
Добрый день. Недавно я столкнулся с проблемой генерации шестиугольной сетки из центра. Есть методы генерации строками. Они годны для чего-угодно. А такого метода я не нашел. Может плохо искал, кто знает. На форуме мне предложили три кривых решения:
1. Костыли (вариант, который самый плохой).
2. Генерация построчно и выкидывание ненужного. В принципе самое правильное решение, но увы, мне нужно чтобы именно из центра. Потом удобно добавлять нужные ячейки будет.
3. Косые координаты. Это сложная мегажесть, на которую можно убить тучу времени. Я ее не смог осилить.
А теперь, перейдем к нашему алгоритму.
Суть на пальцах
Нам нужно получить ячеистую структуру аля эта.

Первым шагом будет генерация первого, центрального гексагона.

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

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

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

Видно, что на лишь осевые шестиугольники (зеленые) лежат на окружности. Центры остальных смещены к центру и лежат на матричном шестиугольнике. Поэтому, через радиальную симметрию такую сетку не задать.
Отсюда следует следующее решение.
Строим сначала лишь осевые шестиугольники.
Затем вычисляем векторы. На рисунке указано направление векторов. Оно может быть обратным.

Но нам нужен не этот вектор. Точнее не он сам, а его часть.

Нам нужен синий вектор. Это смещение, которое прибавляется к осевому шестиугольнику для получения точных координат. Желтый вектор делится на число частей, равное количество шестиугольников между осевыми шестиугольниками + 1 (или номер слоя).
После этого, можно генерировать сколь угодно большие матрицы из центра.
Алгоритм-часть
А теперь приведу код, этого безобразия. Я программировал на Xors3D, но думаю, что кому надо, тот поймет как эта штука работает (тем более, код простой, а суть объяснил выше). Нам нужна всего одна функция – функция создания ячейки.
Вот ее код.
Function CreateCell(X#,Z#,Order%) Limit% = 6 If Order = 0 Then Limit = 1 For i = 0 To Limit-1 Angle# = 360/Limit*i Cell.Cell = New Cell Cell\X = X + CellRadius*Sin(Angle)*Order Cell\Z = Z + CellRadius*Cos(Angle)*Order Cell\Sprite = xCopyEntity(Cell_Image) xSpriteViewMode Cell\Sprite,2 xRotateEntity Cell\Sprite,90,0,0 xPositionEntity Cell\Sprite,Cell\X,0,Cell\Z ;Vector calculating If Order > 0 Then Vector(i,0) =((CellRadius*Sin(Angle+60)*Order-CellRadius*Sin(Angle)*Order))/(Order) Vector(i,1) =((CellRadius*Cos(Angle+60)*Order-CellRadius*Cos(Angle)*Order))/(Order) End If Next For i = 0 To Limit-1 Angle# = 360/Limit*i For j = 0 To Order-2 Cell.Cell = New Cell Cell\X = X + CellRadius*Sin(Angle)*Order + Vector(i,0)*(j+1) Cell\Z = Z + CellRadius*Cos(Angle)*Order + Vector(i,1)*(j+1) Cell\Sprite = xCopyEntity(Cell_Image) xSpriteViewMode Cell\Sprite,2 xRotateEntity Cell\Sprite,90,0,0 xPositionEntity Cell\Sprite,Cell\X,0,Cell\Z Next Next End Function
Разберем его.
X#, Z# - координаты центра
Order% - порядок создаваемого слоя
CellRadius – расстояние между шестиугольниками
Limit% = 6 – количество осевых шестиугольников.
If Order = 0 Then Limit = 1 – если слой первый, то шестиугольник 1.
For i = 0 To Limit-1 - создаем осевые шестиугольники
Angle# = 360/Limit*i – вычисляем угол для каждого осевого шестиугольника
Cell.Cell = New Cell – создаем шестиугольник
Cell\X = X + CellRadius*Sin(Angle)*Order
Cell\Z = Z + CellRadius*Cos(Angle)*Order - задаем его координаты
Cell\Sprite = xCopyEntity(Cell_Image) – это спрайт, не важно для кода
xSpriteViewMode Cell\Sprite,2
xRotateEntity Cell\Sprite,90,0,0
xPositionEntity Cell\Sprite,Cell\X,0,Cell\Z – задаем позицию
;Vector calculating
If Order > 0 Then – вычисляем вектора, если ряд не нулевой (где центральный гексагон)
Vector(i,0) =((CellRadius*Sin(Angle+60)*Order-CellRadius*Sin(Angle)*Order))/(Order) – 0 = X, первая координата вектора
Vector(i,1) =((CellRadius*Cos(Angle+60)*Order-CellRadius*Cos(Angle)*Order))/(Order) – 1 = Z, вторая координата вектора
End If
Next
For i = 0 To Limit-1 – строим остальные шестиугольники
Angle# = 360/Limit*i – угол осевого шестиугольника
For j = 0 To Order-2 – строим шестиугольники в каждой стороне.
Cell.Cell = New Cell – новый шестиугольник
Cell\X = X + CellRadius*Sin(Angle)*Order + Vector(i,0)*(j+1)
Cell\Z = Z + CellRadius*Cos(Angle)*Order + Vector(i,1)*(j+1) –добавляем к координатам смещение (вектор) умноженный на порядок добавляемого шестиугольника (в 3 и последующих рядах их больше 1)
Cell\Sprite = xCopyEntity(Cell_Image)
xSpriteViewMode Cell\Sprite,2
xRotateEntity Cell\Sprite,90,0,0
xPositionEntity Cell\Sprite,Cell\X,0,Cell\Z
Next
Next
Что имеем на выходе:
Апгрейд 1.0 - Поворот на любой угол матрицы
Могут возникнуть ситуации, когда нашу гексагональную сетку надо куда-то примкнуть под нужным углом. Я подумал над этим, и модернизовал функцию - теперь можно поворачивать всю матрицу на произвольный угол!
Итак, код:
Function CreateCell(X#,Z#,Order%,AddAngle%) Limit% = 6 If Order = 0 Then Limit = 1 For i = 0 To Limit-1 Angle# = 360/Limit*i+AddAngle Cell.Cell = New Cell Cell\X = X + CellRadius*Sin(Angle)*Order Cell\Z = Z + CellRadius*Cos(Angle)*Order Cell\Sprite = xCopyEntity(Cell_Image) xPositionEntity Cell\Sprite,Cell\X,0,Cell\Z Cell\Cyl = xCreateCylinder(16,1,Cell\Sprite) Cell\KPD = Rnd(0.01,1) xRotateEntity Cell\Sprite,90,-AddAngle,0 ;Vector calculating If Order > 0 Then Vector(i,0) =((CellRadius*Sin(Angle+60)*Order-CellRadius*Sin(Angle)*Order))/(Order) Vector(i,1) =((CellRadius*Cos(Angle+60)*Order-CellRadius*Cos(Angle)*Order))/(Order) End If Next For i = 0 To Limit-1 Angle# = 360/Limit*i+AddAngle For j = 0 To Order-2 Cell.Cell = New Cell Cell\X = X + CellRadius*Sin(Angle)*Order + Vector(i,0)*(j+1) Cell\Z = Z + CellRadius*Cos(Angle)*Order + Vector(i,1)*(j+1) Cell\Sprite = xCopyEntity(Cell_Image) Cell\Cyl = xCreateCylinder(16,1,Cell\Sprite) Cell\KPD = Rnd(0.01,1) xPositionEntity Cell\Sprite,Cell\X,0,Cell\Z xRotateEntity Cell\Sprite,90,-AddAngle,0 Next Next End Function
Важные отличия:
Function CreateCell(X#,Z#,Order%,AddAngle%) - появился новый аргумент, это и есть произвольный угол - AddAngle.
Angle# = 360/Limit*i+AddAngle - добавляем смещение для расчета углов и векторов.
xRotateEntity Cell\Sprite,90,-AddAngle,0 - поворот спрайта вокруг вертикальной оси в противоположную сторону.
Что имеем на выходе:
17 градусов.
30 градусов.
10 градусов.
Спасибо за внимание. Надеюсь, эта статья будет полезной.
#программирование, #Xors3D, #алгоритмы, #гексагоны, #шестиугольники
10 января 2014 (Обновление: 26 мар 2019)