110 lines
3.6 KiB
Python
Executable File
110 lines
3.6 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
import sys
|
|
import numpy
|
|
import stl.mesh
|
|
|
|
# TODO:
|
|
# - This is a very naive triangulation strategy. It needs fixing - the
|
|
# way it handles 'flatter' areas isn't optimal at all, even if the
|
|
# sharper areas are much better than from CGAL or libfive.
|
|
# - Generate just part of the mesh and then copy. It is rotationally
|
|
# symmetric, as well as translationally symmetric at its period.
|
|
|
|
fname = "spiral_outer0.stl"
|
|
freq = 20
|
|
phase = 0
|
|
scale = 1/16 # from libfive
|
|
inner = 0.4 * scale
|
|
outer = 2.0 * scale
|
|
rad = 0.3 * scale
|
|
|
|
angle = lambda z: freq*z + phase
|
|
|
|
# z starting & ending point:
|
|
z0 = -20*scale
|
|
z1 = 20*scale
|
|
# Number of z divisions:
|
|
m = 1600
|
|
# Number of circle points:
|
|
n = 1000
|
|
|
|
dz = (z1 - z0) / (m-1)
|
|
|
|
data = numpy.zeros((m-1)*n*2 + 2*n, dtype=stl.mesh.Mesh.dtype)
|
|
# Vertex count:
|
|
# From z0 to z0+dz is n circle points joined with 2 triangles to next -> n*2
|
|
# z0+dz to z0+dz*2 is likewise... up through (m-1) of these -> (m-1)*n*2
|
|
# Two endcaps each have circle points & center point -> 2*n
|
|
# Thus: (m-1)*n*2 + 2*n
|
|
v = data["vectors"]
|
|
print("Vertex count: {}".format(m*n*2 + 2*n))
|
|
|
|
verts = numpy.zeros((n, 3), dtype=numpy.float32)
|
|
|
|
# For every z cross-section...
|
|
for z_idx in range(m):
|
|
#sys.stdout.write(".")
|
|
# z value:
|
|
z = z0 + dz*z_idx
|
|
# Angle of center point of circle (radians):
|
|
# (we don't actually need to normalize this)
|
|
rad = angle(z)
|
|
c,s = numpy.cos(rad), numpy.sin(rad)
|
|
# Center point of circle:
|
|
cx, cy = (inner + outer)*numpy.cos(rad), (inner + outer)*numpy.sin(rad)
|
|
# For every division of the circular cross-section...
|
|
if z_idx == 0:
|
|
# Begin with z0 endcap as a special case:
|
|
verts_last = numpy.zeros((n, 3), dtype=numpy.float32)
|
|
verts_last[:, 0] = cx
|
|
verts_last[:, 1] = cy
|
|
verts_last[:, 2] = z
|
|
else:
|
|
verts_last = verts
|
|
verts = numpy.zeros((n, 3), dtype=numpy.float32)
|
|
for ang_idx in range(n):
|
|
# Step around starting angle (the 'far' intersection of the
|
|
# line at angle 'rad' and this circle):
|
|
rad2 = rad + 2*ang_idx*numpy.pi/n
|
|
# ...and generate points on the circle:
|
|
xi = cx + outer*numpy.cos(rad2)
|
|
yi = cy + outer*numpy.sin(rad2)
|
|
verts[ang_idx, :] = [xi, yi, z]
|
|
#print("i={}, z={}, rad={}, cx={}, cy={}, rad2={}, xi={}, yi={}".format(i,z,rad,cx,cy, rad2, xi, yi))
|
|
if z_idx == 0:
|
|
for i in range(n):
|
|
v[i][0,:] = verts[(i + 1) % n,:]
|
|
v[i][1,:] = verts[i,:]
|
|
v[i][2,:] = verts_last[i,:]
|
|
#print("Write vertex {}".format(i))
|
|
else:
|
|
for i in range(n):
|
|
# Vertex index:
|
|
vi = z_idx*n*2 + i*2 - n
|
|
v[vi][0,:] = verts[(i + 1) % n,:]
|
|
v[vi][1,:] = verts[i,:]
|
|
v[vi][2,:] = verts_last[i,:]
|
|
#print("Write vertex {}".format(vi))
|
|
v[vi+1][0,:] = verts_last[(i + 1) % n,:]
|
|
v[vi+1][1,:] = verts[(i + 1) % n,:]
|
|
v[vi+1][2,:] = verts_last[i,:]
|
|
#print("Write vertex {} (2nd half)".format(vi+1))
|
|
|
|
# then handle z1 endcap:
|
|
for i in range(n):
|
|
# See vi definition above. z_idx ends at m-1, i ends at n-1, and
|
|
# so evaluate vi+1 (final index it wrote), add 1 for the next, and
|
|
# then use 'i' to step one at a time:
|
|
vi = (m-1)*n*2 + (n-1)*2 - n + 2 + i
|
|
v[vi][0,:] = verts[i,:]
|
|
v[vi][1,:] = verts[(i + 1) % n,:]
|
|
v[vi][2,:] = [cx, cy, z]
|
|
# Note winding order (1 & 2 flipped from other endcap)
|
|
#print("Write vertex {} (endcap)".format(vi))
|
|
|
|
print("Writing {}...".format(fname))
|
|
mesh = stl.mesh.Mesh(data, remove_empty_areas=False)
|
|
mesh.save(fname)
|
|
print("Done.")
|