Skip to content

Instantly share code, notes, and snippets.

@marcbln
Created May 7, 2021 21:14
Show Gist options
  • Save marcbln/e704c7d431c222f6d058fd473237c82c to your computer and use it in GitHub Desktop.
Save marcbln/e704c7d431c222f6d058fd473237c82c to your computer and use it in GitHub Desktop.
# 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