Created
May 7, 2021 21:14
-
-
Save marcbln/e704c7d431c222f6d058fd473237c82c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 05/2021 created | |
# | |
# hex mesh generation | |
# | |
# nce == numColsEven | |
# | |
# ====== EVEN ====== | |
# | |
# patternEven_1: +1 +2 +2 -1 -2 | |
# patternEven_2: +2 +3 +3 -2 -3 | |
# patternEven_3: +3 +4 +4 -3 -4 | |
# | |
# patternEven_nce: offsetEven_nce(idxRow) + idxCol + [ (0) (nce), (nce+1), (nce+1), (-nce), (-nce-1)] | |
# | |
# rowsIdx: | 0 1 2 3 | |
# --------| --------------------------- | |
# offsetEven_1 = nce = 1 | 0 6 12 18 = 6 * idxRow = (4 * nce + 2) * idxRow | |
# offsetEven_2 = nce = 2 | 0 10 20 30 = 10 * idxRow = (4 * nce + 2) * idxRow | |
# offsetEven_3 = nce = 3 | 0 14 28 42 = 14 * idxRow = (4 * nce + 2) * idxRow | |
# | |
# ===================================================== | |
# || offsetEven_nce(idxRow) = (4 * nce + 2) * idxRow || | |
# ===================================================== | |
# | |
# | |
# | |
# ===== ODD ===== | |
# | |
# patternOdd_2: 0 +2 +2 +3 -2 -2 | |
# patternOdd_3: 0 +3 +3 +4 -3 -3 | |
# patternOdd_nce = [0, +nce, +nce, +(nce+1), -nce, -nce] | |
# | |
# | |
# rowsIdx: | 0 1 2 3 | |
# ---------| --------------------------- | |
# offsetOdd_2 = nce = 2 | 6 16 26 = 10 * idxRow + 6 = (4 * nce + 2) * idxRow + (nce+1) * 2 | |
# offsetOdd_3 = nce = 3 | 8 22 36 = 14 * idxRow + 8 = (4 * nce + 2) * idxRow + (nce+1) * 2 | |
# offsetOdd_4 = nce = 4 | 10 28 46 = 18 * idxRow + 10 = (4 * nce + 2) * idxRow + (nce+1) * 2 | |
# | |
# ================================================================== | |
# || offsetOdd_nce(idxRow) = (4 * nce + 2) * idxRow + (nce+1) * 2 || | |
# ================================================================== | |
# | |
# | |
# | |
# | |
# 0 | |
# | |
# 1 5 | |
# | |
# 2 4 | |
# | |
# 3 | |
# | |
from GenArt.DataTypes.SVG.Geometry.Point import Point | |
from typing import List, Set, Dict, Tuple, Optional | |
import math | |
import numpy as np | |
class MeshCell: | |
vertexIndices: List[int] | |
centerOfGravity: Point # maybe not needed? | |
def __init__(self): | |
self.vertexIndices = [] | |
def __repr__(self): | |
return f"MeshCell<{self.vertexIndices}>" | |
class MESH_2D: | |
vertices: List[Point] | |
cells: List[MeshCell] | |
def __init__(self): | |
self.vertices = [] | |
self.cells = [] | |
class HexMeshGenerator: | |
mesh: MESH_2D | |
numColsEven: int | |
numColsOdd: int | |
numRows: int | |
cellSize: float | |
cellWidth: float | |
cellHeight: float | |
def __init__(self, numColsEven: int, numRows: int, cellSize: float): | |
self.numColsEven = numColsEven | |
self.numColsOdd = numColsEven - 1 | |
self.numRows = numRows | |
self.cellSize = cellSize | |
self.cellWidth = math.sqrt(3) * cellSize # sqrt(3) comes from sin(60°) | |
self.cellHeight = 2 * cellSize | |
def calculatePoint(self, angleDeg, offsetX, offsetY) -> Point: | |
""" | |
private helper | |
""" | |
p = Point( | |
offsetX + math.cos(math.radians(angleDeg)) * self.cellSize, | |
offsetY + -math.sin(math.radians(angleDeg)) * self.cellSize | |
) | |
print(p) | |
return p | |
def generate(self) -> MESH_2D: | |
self.mesh = MESH_2D() | |
# ---- generate vertices | |
self.mesh.vertices = [] | |
""" | |
pointy topped hexagon | |
0 | |
1 5 | |
2 4 | |
3 | |
""" | |
# ---- create vertices grid | |
for idxRow in range(self.numRows): | |
self._addRowVertices(idxRow) | |
# ---- create cells | |
for idxRow in range(self.numRows): | |
isEvenRow = idxRow % 2 == 0 | |
if isEvenRow: | |
# ---- even rows | |
idxRowEven = (idxRow + 1) // 2 | |
print(f"idxRowEven: {idxRowEven}") | |
for idxCol in range(self.numColsEven): | |
print(f"idxCol: {idxCol}") | |
cell = MeshCell() | |
idxVertex = idxCol + self.offsetEven_nce(idxRowEven) | |
patternDiff = [ | |
0, | |
self.numColsEven, | |
self.numColsEven + 1, | |
self.numColsEven + 1, | |
-self.numColsEven, | |
-self.numColsEven - 1 | |
] | |
for d in patternDiff: | |
idxVertex += d | |
cell.vertexIndices.append(idxVertex) | |
self.mesh.cells.append(cell) | |
print(f"cellEven: {cell}") | |
else: | |
# ---- odd rows | |
idxRowOdd = idxRow // 2 | |
print(f"idxRowOdd: {idxRowOdd}") | |
for idxCol in range(self.numColsEven - 1): | |
print(f"idxCol: {idxCol}") | |
cell = MeshCell() | |
idxVertex = idxCol + self.offsetOdd_nce(idxRowOdd) | |
# patternOdd_nce = [0, +nce, +nce, +(nce+1), -nce, -nce] | |
patternDiff = [ | |
0, | |
self.numColsEven, | |
self.numColsEven, | |
self.numColsEven + 1, | |
-self.numColsEven, | |
-self.numColsEven | |
] | |
for d in patternDiff: | |
idxVertex += d | |
cell.vertexIndices.append(idxVertex) | |
self.mesh.cells.append(cell) | |
print(f"cellOdd: {cell}") | |
return self.mesh | |
def _addRowVertices(self, idxRow: int): | |
""" | |
private helper | |
""" | |
# The vertical distance between adjacent hexagon centers is h * 3/4. | |
offsetY = self.cellHeight * idxRow * 3 / 4 | |
isEvenRow = idxRow % 2 == 0 | |
# numCols = self.numColsEven if isEvenCol else self.numColsOdd | |
if idxRow == 0: | |
print("==== T 1 ====") | |
for idxCol in range(self.numColsEven): | |
offsetX = self.cellWidth * idxCol | |
self.mesh.vertices.append(self.calculatePoint(90, offsetX, offsetY)) # 0 | |
if idxRow == 0: | |
print("==== T 2 ====") | |
for idxCol in range(self.numColsEven + 1): | |
offsetX = self.cellWidth * idxCol | |
self.mesh.vertices.append(self.calculatePoint(90 + 60, offsetX, offsetY)) # 1 | |
# self.mesh.vertices.append(self.calculatePoint(90 + 5 * 60 - 360, offsetX, offsetY)) # 5 | |
print("==== B 1 ====") | |
for idxCol in range(self.numColsEven + 1 if isEvenRow else self.numColsOdd + 1): | |
offsetX = self.cellWidth * idxCol | |
if not isEvenRow: | |
offsetX += self.cellWidth/2 | |
self.mesh.vertices.append(self.calculatePoint(90 + 2 * 60, offsetX, offsetY)) # 2 | |
# self.mesh.vertices.append(self.calculatePoint(90 + 4 * 60, offsetX, offsetY)) # 4 | |
print("==== B 2 ====") | |
for idxCol in range(self.numColsEven + 1 if not isEvenRow else self.numColsOdd + 1): | |
offsetX = self.cellWidth * idxCol | |
if not isEvenRow: | |
offsetX -= self.cellWidth/2 | |
self.mesh.vertices.append(self.calculatePoint(90 + 3 * 60, offsetX, offsetY)) # 3 | |
print("====================================================\n") | |
# ===================================================== | |
# || offsetEven_nce(idxRow) = (4 * nce + 2) * idxRow || | |
# ===================================================== | |
def offsetEven_nce(self, idxRow): | |
""" | |
private helper | |
""" | |
return (4 * self.numColsEven + 2) * idxRow | |
# ================================================================== | |
# || offsetOdd_nce(idxRow) = (4 * nce + 2) * idxRow + (nce+1) * 2 || | |
# ================================================================== | |
def offsetOdd_nce(self, idxRow): | |
""" | |
private helper | |
""" | |
return (4 * self.numColsEven + 2) * idxRow + (self.numColsEven + 1) * 2 | |
######## MAIN ######## MAIN ######## MAIN ######## MAIN ######## MAIN ######## MAIN ######## MAIN ######## | |
######## MAIN ######## MAIN ######## MAIN ######## MAIN ######## MAIN ######## MAIN ######## MAIN ######## | |
######## MAIN ######## MAIN ######## MAIN ######## MAIN ######## MAIN ######## MAIN ######## MAIN ######## | |
######## MAIN ######## MAIN ######## MAIN ######## MAIN ######## MAIN ######## MAIN ######## MAIN ######## | |
######## MAIN ######## MAIN ######## MAIN ######## MAIN ######## MAIN ######## MAIN ######## MAIN ######## | |
hexMeshGenerator = HexMeshGenerator(numColsEven=4, numRows=4, cellSize=10) | |
mesh = hexMeshGenerator.generate() | |
# ---- plot ---- | |
import matplotlib.pyplot as plt | |
# ---- plot cells | |
for cell in mesh.cells: | |
points = [mesh.vertices[idx] for idx in cell.vertexIndices] | |
xs, ys = zip(*[(pt.x, pt.y) for pt in points]) # create lists of x and y values | |
plt.plot(xs, ys) | |
# ---- plot vertices | |
plt.rcParams['xtick.bottom'] = plt.rcParams['xtick.labelbottom'] = False | |
plt.rcParams['xtick.top'] = plt.rcParams['xtick.labeltop'] = True | |
xs, ys = zip(*[(pt.x, pt.y) for pt in mesh.vertices]) # create lists of x and y values | |
plt.scatter(xs, ys) | |
plt.xlim(min(xs) - 2, max(xs) + 2) | |
plt.ylim(max(ys) + 2, min(ys) - 2) | |
plt.gca().set_aspect('equal', adjustable='box') | |
# idx as label for each vertex | |
for i, txt in enumerate(range(len(xs))): | |
plt.annotate(txt, (xs[i], ys[i])) | |
plt.show() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment