Some performance improvements

This commit is contained in:
Chris Hodapp 2019-10-08 03:47:26 +02:00
parent a8c78287f1
commit 43e5a282e6
3 changed files with 319 additions and 55 deletions

File diff suppressed because one or more lines are too long

View File

@ -19,8 +19,9 @@ def ram_horn():
], dtype=numpy.float64) - [0.5, 0.5, 0] ], dtype=numpy.float64) - [0.5, 0.5, 0]
xf0_to_1 = meshutil.Transform().translate(0,0,1) xf0_to_1 = meshutil.Transform().translate(0,0,1)
b1 = xf0_to_1.apply_to(b0) b1 = xf0_to_1.apply_to(b0)
mesh = meshutil.join_boundary_simple(b0, b1) meshes = []
mesh = mesh.concat(meshutil.close_boundary_simple(b0)) meshes.append(meshutil.join_boundary_simple(b0, b1))
meshes.append(meshutil.close_boundary_simple(b0))
for i in range(4): for i in range(4):
# Opening boundary: # Opening boundary:
b = b1 b = b1
@ -37,10 +38,10 @@ def ram_horn():
.translate(0,0,0.8) .translate(0,0,0.8)
b_sub1 = incr.compose(xf).apply_to(b) b_sub1 = incr.compose(xf).apply_to(b)
m = meshutil.join_boundary_simple(b_sub0, b_sub1) m = meshutil.join_boundary_simple(b_sub0, b_sub1)
mesh = mesh.concat(m) meshes.append(m)
xf = incr.compose(xf) xf = incr.compose(xf)
# Close final boundary: # Close final boundary:
mesh = mesh.concat(meshutil.close_boundary_simple(b_sub1[::-1,:])) meshes.append(meshutil.close_boundary_simple(b_sub1[::-1,:]))
# ::-1 is to reverse the boundary's order to fix winding order. # ::-1 is to reverse the boundary's order to fix winding order.
# Not sure of the "right" way to fix winding order here. # Not sure of the "right" way to fix winding order here.
# The boundary vertices go in an identical order... it's just # The boundary vertices go in an identical order... it's just
@ -51,6 +52,7 @@ def ram_horn():
# I don't need to subdivide *geometry*. # I don't need to subdivide *geometry*.
# I need to subdivide *space* and then put geometry in it. # I need to subdivide *space* and then put geometry in it.
mesh = meshutil.FaceVertexMesh.concat_many(meshes)
return mesh return mesh
# Interlocking twists. # Interlocking twists.
@ -63,17 +65,13 @@ def twist(ang=0.1, dz=0.2, dx0=2, count=4, scale=0.98):
[1, 1, 0], [1, 1, 0],
[0, 1, 0], [0, 1, 0],
], dtype=numpy.float64) - [0.5, 0.5, 0] ], dtype=numpy.float64) - [0.5, 0.5, 0]
mesh = None meshes = []
for i in range(count): for i in range(count):
xf = meshutil.Transform() \ xf = meshutil.Transform() \
.translate(dx0, 0, 0) \ .translate(dx0, 0, 0) \
.rotate([0,0,1], numpy.pi * 2 * i / count) .rotate([0,0,1], numpy.pi * 2 * i / count)
b0 = xf.apply_to(b) b0 = xf.apply_to(b)
m = meshutil.close_boundary_simple(b0) meshes.append(meshutil.close_boundary_simple(b0))
if mesh is None:
mesh = m
else:
mesh = mesh.concat(m)
for layer in range(256): for layer in range(256):
b_sub0 = xf.apply_to(b) b_sub0 = xf.apply_to(b)
incr = meshutil.Transform() \ incr = meshutil.Transform() \
@ -82,10 +80,11 @@ def twist(ang=0.1, dz=0.2, dx0=2, count=4, scale=0.98):
.scale(scale) .scale(scale)
b_sub1 = xf.compose(incr).apply_to(b) b_sub1 = xf.compose(incr).apply_to(b)
m = meshutil.join_boundary_simple(b_sub0, b_sub1) m = meshutil.join_boundary_simple(b_sub0, b_sub1)
mesh = mesh.concat(m) meshes.append(m)
xf = xf.compose(incr) xf = xf.compose(incr)
# Close final boundary: # Close final boundary:
mesh = mesh.concat(meshutil.close_boundary_simple(b_sub1[::-1,:])) meshes.append(meshutil.close_boundary_simple(b_sub1[::-1,:]))
mesh = meshutil.FaceVertexMesh.concat_many(meshes)
return mesh return mesh
def twist_nonlinear(dx0 = 2, dz=0.2, count=3, scale=0.99, layers=100): def twist_nonlinear(dx0 = 2, dz=0.2, count=3, scale=0.99, layers=100):
@ -100,17 +99,13 @@ def twist_nonlinear(dx0 = 2, dz=0.2, count=3, scale=0.99, layers=100):
[1, 1, 0], [1, 1, 0],
[0, 1, 0], [0, 1, 0],
], dtype=numpy.float64) - [0.5, 0.5, 0] ], dtype=numpy.float64) - [0.5, 0.5, 0]
mesh = None meshes = []
for i in range(count): for i in range(count):
xf = meshutil.Transform() \ xf = meshutil.Transform() \
.translate(dx0, 0, 0) \ .translate(dx0, 0, 0) \
.rotate([0,0,1], numpy.pi * 2 * i / count) .rotate([0,0,1], numpy.pi * 2 * i / count)
b0 = xf.apply_to(b) b0 = xf.apply_to(b)
m = meshutil.close_boundary_simple(b0) meshes.append(meshutil.close_boundary_simple(b0))
if mesh is None:
mesh = m
else:
mesh = mesh.concat(m)
for layer in range(layers): for layer in range(layers):
b_sub0 = xf.apply_to(b) b_sub0 = xf.apply_to(b)
ang = ang_fn(layer) ang = ang_fn(layer)
@ -120,10 +115,11 @@ def twist_nonlinear(dx0 = 2, dz=0.2, count=3, scale=0.99, layers=100):
.scale(scale) .scale(scale)
b_sub1 = xf.compose(incr).apply_to(b) b_sub1 = xf.compose(incr).apply_to(b)
m = meshutil.join_boundary_simple(b_sub0, b_sub1) m = meshutil.join_boundary_simple(b_sub0, b_sub1)
mesh = mesh.concat(m) meshes.append(m)
xf = xf.compose(incr) xf = xf.compose(incr)
# Close final boundary: # Close final boundary:
mesh = mesh.concat(meshutil.close_boundary_simple(b_sub1[::-1,:])) meshes.append(meshutil.close_boundary_simple(b_sub1[::-1,:]))
mesh = meshutil.FaceVertexMesh.concat_many(meshes)
return mesh return mesh
# Generate a frame with 'count' boundaries in the XZ plane. # Generate a frame with 'count' boundaries in the XZ plane.
@ -138,6 +134,7 @@ def gen_twisted_boundary(count=4, dx0=2, dz=0.2, ang=0.1):
], dtype=numpy.float64) - [0.5, 0, 0.5] ], dtype=numpy.float64) - [0.5, 0, 0.5]
b = meshutil.subdivide_boundary(b) b = meshutil.subdivide_boundary(b)
b = meshutil.subdivide_boundary(b) b = meshutil.subdivide_boundary(b)
b = meshutil.subdivide_boundary(b)
# Generate 'seed' transformations: # Generate 'seed' transformations:
xfs = [meshutil.Transform().translate(dx0, 0, 0).rotate([0,1,0], numpy.pi * 2 * i / count) xfs = [meshutil.Transform().translate(dx0, 0, 0).rotate([0,1,0], numpy.pi * 2 * i / count)
for i in range(count)] for i in range(count)]
@ -183,29 +180,30 @@ def gen_torus_xy(gen, rad=2, frames=100):
# String together boundaries from a generator. # String together boundaries from a generator.
# If count is nonzero, run only this many iterations. # If count is nonzero, run only this many iterations.
def gen2mesh(gen, count=0, flip_order=False, loop=False): def gen2mesh(gen, count=0, flip_order=False, loop=False, join_fn=meshutil.join_boundary_optim):
# Get first list of boundaries: # Get first list of boundaries:
bs_first = next(gen) bs_first = next(gen)
bs_last = bs_first bs_last = bs_first
# TODO: Begin and end with close_boundary_simple # TODO: Begin and end with close_boundary_simple
mesh = meshutil.FaceVertexMesh.Empty() meshes = []
for i,bs_cur in enumerate(gen): for i,bs_cur in enumerate(gen):
if count > 0 and i >= count: if count > 0 and i >= count:
break break
for j,b in enumerate(bs_cur): for j,b in enumerate(bs_cur):
if flip_order: if flip_order:
m = meshutil.join_boundary_simple(b, bs_last[j]) m = join_fn(b, bs_last[j])
else: else:
m = meshutil.join_boundary_simple(bs_last[j], b) m = join_fn(bs_last[j], b)
mesh = mesh.concat(m) meshes.append(m)
bs_last = bs_cur bs_last = bs_cur
if loop: if loop:
for b0,b1 in zip(bs_last, bs_first): for b0,b1 in zip(bs_last, bs_first):
if flip_order: if flip_order:
m = meshutil.join_boundary_simple(b1, b0) m = join_fn(b1, b0)
else: else:
m = meshutil.join_boundary_simple(b0, b1) m = join_fn(b0, b1)
mesh = mesh.concat(m) meshes.append(m)
mesh = meshutil.FaceVertexMesh.concat_many(meshes)
return mesh return mesh
def twist_from_gen(): def twist_from_gen():
@ -216,12 +214,21 @@ def twist_from_gen():
# frames = How many step to build this from: # frames = How many step to build this from:
# turn = How many full turns to make in inner twist # turn = How many full turns to make in inner twist
# count = How many inner twists to have # count = How many inner twists to have
def twisty_torus(frames = 200, turns = 4, count = 4, rad = 4): def twisty_torus(frames = 5000, turns = 4, count = 4, rad = 4):
# In order to make this line up properly: # In order to make this line up properly:
angle = numpy.pi * 2 * turns / frames angle = numpy.pi * 2 * turns / frames
gen = gen_torus_xy(gen_twisted_boundary(count=count, ang=angle), rad=rad, frames=frames) gen = gen_torus_xy(gen_twisted_boundary(count=count, ang=angle), rad=rad, frames=frames)
return gen2mesh(gen, 0, flip_order=True, loop=True) return gen2mesh(gen, 0, flip_order=True, loop=True)
# frames = How many step to build this from:
# turn = How many full turns to make in inner twist
# count = How many inner twists to have
def twisty_torus_opt(frames = 200, turns = 4, count = 4, rad = 4):
# In order to make this line up properly:
angle = numpy.pi * 2 * turns / frames
gen = gen_torus_xy(gen_twisted_boundary(count=count, ang=angle), rad=rad, frames=frames)
return gen2mesh(gen, 0, flip_order=True, loop=True, join_fn=meshutil.join_boundary_optim)
def main(): def main():
fns = { fns = {
ram_horn: "ramhorn.stl", ram_horn: "ramhorn.stl",

View File

@ -38,8 +38,27 @@ class FaceVertexMesh(object):
v[i] = [self.v[iv0], self.v[iv1], self.v[iv2]] v[i] = [self.v[iv0], self.v[iv1], self.v[iv2]]
return stl.mesh.Mesh(data) return stl.mesh.Mesh(data)
@classmethod @classmethod
def Empty(self): def Empty(cls):
return FaceVertexMesh(numpy.zeros((0,3)), numpy.zeros((0,3), dtype=int)) return FaceVertexMesh(numpy.zeros((0,3)), numpy.zeros((0,3), dtype=int))
@classmethod
def concat_many(cls, meshes):
nv = 0
nf = 0
for m in meshes:
nv += m.v.shape[0]
nf += m.f.shape[0]
v = numpy.zeros((nv,3), dtype=numpy.float64)
f = numpy.zeros((nf,3), dtype=int)
vi = 0
fi = 0
for m in meshes:
vj = vi + m.v.shape[0]
fj = fi + m.f.shape[0]
v[vi:vj,:] = m.v
f[fi:fj,:] = m.f + vi
vi = vj
fi = fj
return FaceVertexMesh(v, f)
class Transform(object): class Transform(object):
def __init__(self, mtx=None): def __init__(self, mtx=None):
@ -207,6 +226,16 @@ def join_boundary_simple(bound1, bound2):
fs[2*i + 1] = [v1, n + v0, v0] fs[2*i + 1] = [v1, n + v0, v0]
return FaceVertexMesh(vs, fs) return FaceVertexMesh(vs, fs)
def join_boundary_optim(bound1, bound2):
# bound1 and bound2 must stay in order, but we can rotate
# the starting point to whatever we want. Use distance as
# a metric:
errs = [numpy.linalg.norm(bound1 - numpy.roll(bound2, i, axis=0))
for i,_ in enumerate(bound1)]
# What shift gives the lowest distance?
i = numpy.argmin(errs)
return join_boundary_simple(bound1, numpy.roll(bound2, i, axis=0))
def close_boundary_simple(bound): def close_boundary_simple(bound):
# This will fail for any non-convex boundary! # This will fail for any non-convex boundary!
centroid = numpy.mean(bound, axis=0) centroid = numpy.mean(bound, axis=0)