3D Booleans
Constructive Solid Geometry (CSG) represents solid objects as binary trees where leaf nodes are primitive solids (boxes, spheres) and internal nodes are boolean operators (union, intersection, difference).
CSG Tree Structure:
1# Constructive Solid Geometry (CSG) Tree Node2class CSGNode:3 def __init__(self, operation=None, left=None, right=None, mesh=None):4 self.op = operation # UNION, INTERSECT, DIFFERENCE5 self.left = left # Left CSGNode subtree6 self.right = right # Right CSGNode subtree7 self.mesh = mesh # Actual solid mesh if leaf node8 self.is_leaf = mesh is not None
To evaluate CSG boundaries, we first locate all triangle-triangle intersections between the two solid meshes to identify split lines.
Intersection Detection:
1# Detect intersections between triangles of two meshes2def intersect_meshes(meshA, meshB):3 intersecting_pairs = []4 for triA in meshA.triangles:5 # Prune search using AABB box checks6 if not box_overlap(triA.bounds, meshB.bounds):7 continue8 for triB in meshB.triangles:9 if triangle_overlap_check(triA, triB):10 intersecting_pairs.append((triA, triB))11 return intersecting_pairs
Once intersection segments are identified, the triangles they cross are subdivided into smaller, planar-conformant triangles sharing the intersection boundary lines.
Subdivision Rules:
1# Split a triangle along a segment crossing it2def split_triangle(triangle, segment):3 # Find intersections of segment endpoints on triangle edges4 pts = get_edge_intersections(triangle, segment)56 # Subdivide triangle into smaller sub-triangles (triangulation)7 new_triangles = triangulate_polygon_points(triangle.vertices + pts)8 return new_triangles
Each vertex of Mesh A is classified relative to the solid volume of Mesh B. The parity of ray crossings determines if a coordinate is inside or outside the other shape.
Containment Check:
1# Classify vertices of mesh A against solid volume B2def classify_vertices(vertices_A, mesh_B):3 classification = []4 for pt in vertices_A:5 # Cast a ray in any direction, count boundary crossings6 crossings = count_ray_crossings(pt, Vector3(0, 1, 0), mesh_B)78 # Odd crossings = inside, Even crossings = outside9 if crossings % 2 == 1:10 classification.append((pt, "INSIDE"))11 else:12 classification.append((pt, "OUTSIDE"))1314 return classification
Triangles are selected or discarded based on their location (inside/outside the other volume) to satisfy the Boolean operation.
Trimming Logic:
1# Filter triangles based on Boolean operation type2def trim_mesh_faces(meshA, meshB, operation):3 output_faces = []45 if operation == "UNION":6 # Keep faces of A outside B, and faces of B outside A7 output_faces.extend([f for f in meshA.faces if f.classification == "OUTSIDE"])8 output_faces.extend([f for f in meshB.faces if f.classification == "OUTSIDE"])9 elif operation == "INTERSECT":10 # Keep faces of A inside B, and faces of B inside A11 output_faces.extend([f for f in meshA.faces if f.classification == "INSIDE"])12 output_faces.extend([f for f in meshB.faces if f.classification == "INSIDE"])13 elif operation == "DIFFERENCE":14 # Keep faces of A outside B, and faces of B inside A (flip normals)15 output_faces.extend([f for f in meshA.faces if f.classification == "OUTSIDE"])16 output_faces.extend([f.flipped() for f in meshB.faces if f.classification == "INSIDE"])1718 return output_faces
To assemble a watertight solid, duplicate vertices along the trimmed boundaries of both meshes are merged within a tolerance, stitching the open shells.
Seam Welding:
1# Weld open seams along triangle intersection boundaries2def weld_mesh_seams(vertices, faces, tolerance=1e-5):3 welded_vertices = []4 vertex_map = {}56 for i, v in enumerate(vertices):7 # Find if duplicate vertex exists within tolerance8 duplicate_idx = find_duplicate_vertex(v, welded_vertices, tolerance)9 if duplicate_idx is not None:10 vertex_map[i] = duplicate_idx11 else:12 new_idx = len(welded_vertices)13 welded_vertices.append(v)14 vertex_map[i] = new_idx1516 # Remap face indices17 welded_faces = [[vertex_map[idx] for idx in f] for f in faces]18 return welded_vertices, welded_faces
The remaining triangles of both meshes are combined into a single vertex-face index array, forming the final clipped solid.
Assembly Phase:
1# Compile final watertight solid mesh from trimmed parts2def assemble_csg(trimmed_faces_A, trimmed_faces_B):3 # Merge face lists4 combined_faces = trimmed_faces_A + trimmed_faces_B56 # Clean duplicates, weld vertices, rebuild normals7 vertices, faces = weld_vertices_and_rebuild(combined_faces)8 return SolidMesh(vertices, faces)
Standard float arithmetic leads to catastrophic CSG failures when boundaries are exactly coplanar or share a single touching vertex.Nef Polyhedra represent solid geometry using boundary cells and local neighborhoods (sphere maps) to guarantee topological consistency.
Exact Geometric Predicates:
1from CGAL.CGAL_Kernel import Exact_predicates_exact_constructions_kernel as Kernel2from CGAL.CGAL_Nef_3 import Nef_polyhedron_334# Define shapes using exact computation kernels5# Rather than floating-point coordinates, coordinates use arbitrary precision rationals6cube_a_nef = Nef_polyhedron_3(polyhedron_a)7cube_b_nef = Nef_polyhedron_3(polyhedron_b)89# Exact Boolean operations are guaranteed to be topologically closed10union_nef = cube_a_nef + cube_b_nef11difference_nef = cube_a_nef - cube_b_nef