#!/usr/bin/env python3 """ GPU-Resident Signed Distance Field (SDF) CSG & Infill Example ============================================================= Demonstrates chaining 3D mesh slicing, Boolean CSG operations (Union, Intersection, Difference, Smooth Blend), constant-distance offsetting, volume shelling, procedural 3D gyroid infill generation, and contour vectorization entirely in GPU VRAM without host-to-device memory roundtrips. """ import os import sys import time import numpy as np # Ensure local build is in path sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "python")) import cudaslicer import trimesh def main(): print("=" * 70) print(" CUDASlicer: GPU Signed Distance Field (SDF) & CSG Engine") print("=" * 70) if not cudaslicer.has_cuda(): print("[Error] CUDA-capable GPU is required for this demo.") return # 1. Load or generate 3D models stl_path = os.path.join(os.path.dirname(__file__), "..", "benchy.stl") if os.path.exists(stl_path): print(f"\n[1] Loading main model: {stl_path}") mesh_a = trimesh.load(stl_path) else: print("\n[1] Generating synthetic main cylinder...") mesh_a = trimesh.creation.cylinder(radius=15.0, height=30.0) # Generate a modifier box volume to subtract from the model print("[1] Generating modifier cutting box...") mesh_b = trimesh.creation.box(extents=(12.0, 60.0, 25.0)) mesh_b.apply_translation((0.0, 0.0, 15.0)) # 2. Direct GPU Slicing to SDF Stacks layer_height_mm = 0.20 pitch_mm = 0.10 t0 = time.perf_counter() sdf_a = cudaslicer.slice_mesh_to_sdf(mesh_a, layer_height=layer_height_mm, pitch_mm=pitch_mm) sdf_b = cudaslicer.slice_mesh_to_sdf(mesh_b, layer_height=layer_height_mm, pitch_mm=pitch_mm) t_slice = (time.perf_counter() - t0) * 1000.0 print(f"\n[2] Direct GPU Slicing & Signed Distance Generation ({t_slice:.2f} ms):") print(f" - Main Model Stack: {sdf_a}") print(f" Layer Z range (µm): {sdf_a.layer_z_um[0]} µm to {sdf_a.layer_z_um[-1]} µm ({len(sdf_a.layer_z_um)} layers)") print(f" - Modifier Box Stack: {sdf_b}") print(f" Layer Z range (µm): {sdf_b.layer_z_um[0]} µm to {sdf_b.layer_z_um[-1]} µm ({len(sdf_b.layer_z_um)} layers)") # 3. GPU-Resident Boolean Operations (Zero CPU transfers) t0 = time.perf_counter() # Chained expression: (Model A \ (Modifier B + 0.5mm offset)) modifier_expanded = sdf_b.offset(delta_mm=0.5) sdf_carved = sdf_a.difference(modifier_expanded) t_csg = (time.perf_counter() - t0) * 1000.0 print(f"\n[3] Chained GPU CSG (Carve / Subtraction) ({t_csg:.2f} ms):") print(f" - Result Stack: {sdf_carved}") print(f" - Automatic spatial bounding grid expansion: {sdf_carved.shape}") # 4. Procedural 3D Gyroid Infill Generation on GPU t0 = time.perf_counter() sdf_gyroid = sdf_carved.generate_infill( pattern="gyroid", cell_size_mm=4.0, density=0.25, wall_offset_mm=1.20 ) t_infill = (time.perf_counter() - t0) * 1000.0 print(f"\n[4] GPU Procedural Gyroid Infill Generation ({t_infill:.2f} ms):") print(f" - Infill Stack: {sdf_gyroid}") # 5. Vectorize directly to polygon perimeters and infill contours t0 = time.perf_counter() layer_results = sdf_gyroid.vectorize( perimeters_count=2, layer_width=0.45, export_infill_mask=True ) t_vec = (time.perf_counter() - t0) * 1000.0 total_contours = sum( sum(len(p["contours"]) for p in l.perimeters) + len(l.infill_contours) for l in layer_results ) print(f"\n[5] Vectorization to Slicer Contours ({t_vec:.2f} ms):") print(f" - Total Layers Output: {len(layer_results)}") print(f" - Total Vector Contours Extracted: {total_contours}") # Sample mid layer mid_idx = len(layer_results) // 2 sample_layer = layer_results[mid_idx] print(f"\n[6] Sample Mid Layer (Layer {mid_idx}):") print(f" - Z Height: {sample_layer.z:.3f} mm ({sample_layer.z_um} µm)") print(f" - Perimeters: {len(sample_layer.perimeters)}") for i, p in enumerate(sample_layer.perimeters): print(f" * Perimeter {i+1} (offset {p['offset_mm']:.2f} mm): {len(p['contours'])} contour loop(s)") print(f" - Infill Contours: {len(sample_layer.infill_contours)} contour loop(s)") print("\n" + "=" * 70) print(" Pipeline completed successfully with 100% GPU-resident computation!") print("=" * 70) if __name__ == "__main__": main()