Cord/examples/gen_test_meshes.py

116 lines
4.3 KiB
Python

"""Generate test STL meshes for decomposition quality testing."""
import struct
import math
def write_stl(path, triangles):
with open(path, "wb") as f:
f.write(b'\0' * 80)
f.write(struct.pack('<I', len(triangles)))
for tri in triangles:
v0, v1, v2 = tri
e1 = [v1[i]-v0[i] for i in range(3)]
e2 = [v2[i]-v0[i] for i in range(3)]
n = [e1[1]*e2[2]-e1[2]*e2[1], e1[2]*e2[0]-e1[0]*e2[2], e1[0]*e2[1]-e1[1]*e2[0]]
l = sum(x*x for x in n)**0.5
n = [x/l for x in n] if l > 0 else [0,0,0]
f.write(struct.pack('<3f', *n))
for v in [v0, v1, v2]:
f.write(struct.pack('<3f', *[float(x) for x in v]))
f.write(struct.pack('<H', 0))
def sphere_mesh(center, radius, subdivisions=16):
tris = []
for i in range(subdivisions):
theta0 = math.pi * i / subdivisions
theta1 = math.pi * (i + 1) / subdivisions
for j in range(subdivisions * 2):
phi0 = 2 * math.pi * j / (subdivisions * 2)
phi1 = 2 * math.pi * (j + 1) / (subdivisions * 2)
def pt(t, p):
return (
center[0] + radius * math.sin(t) * math.cos(p),
center[1] + radius * math.sin(t) * math.sin(p),
center[2] + radius * math.cos(t),
)
p00 = pt(theta0, phi0)
p10 = pt(theta1, phi0)
p01 = pt(theta0, phi1)
p11 = pt(theta1, phi1)
if i > 0:
tris.append((p00, p10, p01))
if i < subdivisions - 1:
tris.append((p01, p10, p11))
return tris
def cylinder_mesh(center, radius, height, segments=32):
tris = []
half_h = height / 2.0
for i in range(segments):
a0 = 2 * math.pi * i / segments
a1 = 2 * math.pi * (i + 1) / segments
x0, y0 = center[0] + radius * math.cos(a0), center[1] + radius * math.sin(a0)
x1, y1 = center[0] + radius * math.cos(a1), center[1] + radius * math.sin(a1)
top_z = center[2] + half_h
bot_z = center[2] - half_h
# Side
tris.append(((x0, y0, bot_z), (x1, y1, bot_z), (x0, y0, top_z)))
tris.append(((x1, y1, bot_z), (x1, y1, top_z), (x0, y0, top_z)))
# Top cap
tris.append(((center[0], center[1], top_z), (x0, y0, top_z), (x1, y1, top_z)))
# Bottom cap
tris.append(((center[0], center[1], bot_z), (x1, y1, bot_z), (x0, y0, bot_z)))
return tris
def box_mesh(center, hx, hy, hz):
cx, cy, cz = center
v = [
(cx-hx, cy-hy, cz-hz), (cx+hx, cy-hy, cz-hz),
(cx+hx, cy+hy, cz-hz), (cx-hx, cy+hy, cz-hz),
(cx-hx, cy-hy, cz+hz), (cx+hx, cy-hy, cz+hz),
(cx+hx, cy+hy, cz+hz), (cx-hx, cy+hy, cz+hz),
]
faces = [
(0,2,1), (0,3,2),
(4,5,6), (4,6,7),
(0,1,5), (0,5,4),
(2,3,7), (2,7,6),
(0,4,7), (0,7,3),
(1,2,6), (1,6,5),
]
return [(v[a], v[b], v[c]) for a, b, c in faces]
# Test 1: Sphere (t=1)
print("generating sphere.stl...")
write_stl("examples/sphere.stl", sphere_mesh((0, 0, 0), 3.0, 24))
# Test 2: Cylinder (t=1)
print("generating cylinder.stl...")
write_stl("examples/cylinder.stl", cylinder_mesh((0, 0, 0), 2.0, 6.0, 48))
# Test 3: Two-box union (t=3: 2 boxes + 1 union)
# Separated so no overlapping faces
print("generating two_boxes.stl...")
tris = box_mesh((-3, 0, 0), 2, 1, 1) + box_mesh((3, 0, 0), 1, 2, 1)
write_stl("examples/two_boxes.stl", tris)
# Test 4: Box with sphere cut (t=3: box + sphere + difference)
print("generating box_minus_sphere.stl...")
# Approximate by densely sampling the SDF and marching-cubes-like output
# For simplicity, just use the box mesh (the cut won't show in a naive mesh union)
# Instead generate a box mesh - the decompiler should detect it as a box
tris = box_mesh((0, 0, 0), 3, 3, 3)
write_stl("examples/box3.stl", tris)
# Test 5: Translated sphere (t=2: sphere + translate)
print("generating translated_sphere.stl...")
write_stl("examples/translated_sphere.stl", sphere_mesh((5, 3, -2), 2.0, 20))
# Test 6: Box + cylinder (t=3: 2 objects + 1 union)
# Separated with gap
print("generating box_and_cylinder.stl...")
tris = box_mesh((-4, 0, 0), 2, 2, 2) + cylinder_mesh((4, 0, 0), 1.5, 4.0, 48)
write_stl("examples/box_and_cylinder.stl", tris)
print("done.")