cudaslicer/examples/gpu_sdf_csg_example.py
2026-09-26 14:20:07 +03:00

118 lines
4.5 KiB
Python

#!/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()