fluencyCAD/mesh_modules/simple_mesh.py
2024-07-08 22:14:25 +02:00

214 lines
7.3 KiB
Python

import numpy as np
from scipy.spatial import Delaunay, ConvexHull
from shapely.geometry import Polygon, Point
def alpha_shape(points, alpha):
"""
Compute the alpha shape (concave hull) of a set of points.
"""
def add_edge(edges, edge_points, points, i, j):
"""Add a line between the i-th and j-th points if not in the list already"""
if (i, j) in edges or (j, i) in edges:
return
edges.add((i, j))
edge_points.append(points[[i, j]])
tri = Delaunay(points)
edges = set()
edge_points = []
# Loop over triangles:
for ia, ib, ic in tri.simplices:
pa = points[ia]
pb = points[ib]
pc = points[ic]
# Lengths of sides of triangle
a = np.sqrt((pa[0] - pb[0]) ** 2 + (pa[1] - pb[1]) ** 2)
b = np.sqrt((pb[0] - pc[0]) ** 2 + (pb[1] - pc[1]) ** 2)
c = np.sqrt((pc[0] - pa[0]) ** 2 + (pc[1] - pa[1]) ** 2)
# Semiperimeter of triangle
s = (a + b + c) / 2.0
# Area of triangle by Heron's formula
area = np.sqrt(s * (s - a) * (s - b) * (s - c))
circum_r = a * b * c / (4.0 * area)
# Here's the radius filter.
if circum_r < 1.0 / alpha:
add_edge(edges, edge_points, points, ia, ib)
add_edge(edges, edge_points, points, ib, ic)
add_edge(edges, edge_points, points, ic, ia)
m = np.array(edge_points)
return m
def generate_mesh(points, depth, alpha=0.1):
"""
Generate a mesh by extruding a 2D shape along the Z-axis, automatically detecting holes.
:param points: List of (x, y) tuples representing all points of the 2D shape, including potential holes.
:param depth: Extrusion depth along the Z-axis.
:param alpha: Alpha value for the alpha shape algorithm (controls the "tightness" of the boundary).
:return: Tuple of vertices and faces.
"""
# Convert points to a numpy array
points_2d = np.array(points)
# Compute the alpha shape (outer boundary)
boundary_edges = alpha_shape(points_2d, alpha)
# Create a Polygon from the boundary
boundary_polygon = Polygon(boundary_edges)
# Separate points into boundary and interior
boundary_points = []
interior_points = []
for point in points:
if Point(point).touches(boundary_polygon) or Point(point).within(boundary_polygon):
if Point(point).touches(boundary_polygon):
boundary_points.append(point)
else:
interior_points.append(point)
# Perform Delaunay triangulation on all points
tri = Delaunay(points_2d)
# Generate the top and bottom faces
bottom_face = np.hstack((tri.points, np.zeros((tri.points.shape[0], 1))))
top_face = np.hstack((tri.points, np.ones((tri.points.shape[0], 1)) * depth))
# Combine top and bottom vertices
vertices_array = np.vstack((bottom_face, top_face))
# Create faces
faces = []
# Bottom face triangulation
for simplex in tri.simplices:
faces.append(simplex.tolist())
# Top face triangulation (with an offset)
top_offset = len(tri.points)
for simplex in tri.simplices:
faces.append([i + top_offset for i in simplex])
# Side faces for the outer boundary
for i in range(len(boundary_points)):
next_i = (i + 1) % len(boundary_points)
current = points.index(boundary_points[i])
next_point = points.index(boundary_points[next_i])
faces.append([current, top_offset + current, top_offset + next_point])
faces.append([current, top_offset + next_point, next_point])
# Convert vertices to the desired format: list of tuples
vertices = [tuple(vertex) for vertex in vertices_array]
return vertices, faces
def generate_mesh_wholes(points, holes, depth):
"""
Generate a mesh by extruding a 2D shape along the Z-axis, including holes.
:param points: List of (x, y) tuples representing the outer boundary of the 2D shape.
:param holes: List of lists, where each inner list contains (x, y) tuples representing a hole.
:param depth: Extrusion depth along the Z-axis.
:return: Tuple of vertices and faces.
"""
# Convert points to a numpy array
points_2d = np.array(points)
# Prepare points for triangulation
triangulation_points = points_2d.tolist()
for hole in holes:
triangulation_points.extend(hole)
# Perform Delaunay triangulation
tri = Delaunay(np.array(triangulation_points))
# Generate the top and bottom faces
bottom_face = np.hstack((tri.points, np.zeros((tri.points.shape[0], 1))))
top_face = np.hstack((tri.points, np.ones((tri.points.shape[0], 1)) * depth))
# Combine top and bottom vertices
vertices_array = np.vstack((bottom_face, top_face))
# Create faces
faces = []
# Bottom face triangulation
for simplex in tri.simplices:
faces.append(simplex.tolist())
# Top face triangulation (with an offset)
top_offset = len(tri.points)
for simplex in tri.simplices:
faces.append([i + top_offset for i in simplex])
# Side faces
for i in range(len(points)):
next_i = (i + 1) % len(points)
faces.append([i, top_offset + i, top_offset + next_i])
faces.append([i, top_offset + next_i, next_i])
# Side faces for holes
start_index = len(points)
for hole in holes:
for i in range(len(hole)):
current = start_index + i
next_i = start_index + (i + 1) % len(hole)
faces.append([current, top_offset + next_i, top_offset + current])
faces.append([current, next_i, top_offset + next_i])
start_index += len(hole)
# Convert vertices to the desired format: list of tuples
vertices = [tuple(vertex) for vertex in vertices_array]
return vertices, faces
def generate_mesh_simple(points, depth):
"""
Generate a mesh by extruding a 2D shape along the Z-axis.
:param points: List of (x, y) tuples representing the 2D shape.
:param depth: Extrusion depth along the Z-axis.
:return: Tuple of vertices and faces.
"""
# Convert points to a numpy array
points_2d = np.array(points)
# Get the convex hull of the points to ensure they form a proper polygon
hull = ConvexHull(points_2d)
hull_points = points_2d[hull.vertices]
# Generate the top and bottom faces
bottom_face = np.hstack((hull_points, np.zeros((hull_points.shape[0], 1))))
top_face = np.hstack((hull_points, np.ones((hull_points.shape[0], 1)) * depth))
# Combine top and bottom vertices
vertices_array = np.vstack((bottom_face, top_face))
# Create faces
faces = []
# Bottom face triangulation (counter-clockwise)
for i in range(len(hull_points) - 2):
faces.append([0, i + 2, i + 1])
# Top face triangulation (counter-clockwise, with an offset)
top_offset = len(hull_points)
for i in range(len(hull_points) - 2):
faces.append([top_offset, top_offset + i + 1, top_offset + i + 2])
# Side faces (ensure counter-clockwise order)
for i in range(len(hull_points)):
next_i = (i + 1) % len(hull_points)
faces.append([i, top_offset + i, top_offset + next_i])
faces.append([i, top_offset + next_i, next_i])
# Convert vertices to the desired format: list of tuples
vertices = [tuple(vertex) for vertex in vertices_array]
return vertices, faces