Fix (stupid) bug from last commit; small refactor
This commit is contained in:
parent
13b0809320
commit
ea64900fef
49
README.md
Normal file
49
README.md
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
To-do items, wanted features, bugs:
|
||||||
|
|
||||||
|
- Examples of branching. This will probably need recursion via functions
|
||||||
|
(or an explicit stack some other way).
|
||||||
|
- I need to figure out winding order. It is consistent through seemingly
|
||||||
|
everything, except for reflection and close_boundary_simple.
|
||||||
|
(When there are two parallel boundaries joined with something like
|
||||||
|
join_boundary_simple, traversing these boundaries in their actual order
|
||||||
|
to generate triangles - like in close_boundary_simple - will produce
|
||||||
|
opposite winding order on each. Imagine a transparent clock: seen from the
|
||||||
|
front, it moves clockwise, but seen from the back, it moves
|
||||||
|
counter-clockwise.)
|
||||||
|
- Make it easier to build up meshes a bit at a time?
|
||||||
|
- Factor out recursive/iterative stuff to be a bit more concise
|
||||||
|
- Embed this in Blender?
|
||||||
|
- File that bug that I've seen in trimesh/three.js
|
||||||
|
(see trimesh_fail.ipynb)
|
||||||
|
|
||||||
|
- Parametrize gen_twisted_boundary over boundaries and
|
||||||
|
do my nested spiral
|
||||||
|
- Encode the notions of "generator which transforms an
|
||||||
|
existing list of boundaries", "generator which transforms
|
||||||
|
another generator"
|
||||||
|
- This has a lot of functions parametrized over a lot
|
||||||
|
of functions. Need to work with this somehow.
|
||||||
|
- Work directly with lists of boundaries. The only thing
|
||||||
|
I ever do with them is apply transforms to all of them, or
|
||||||
|
join adjacent ones with corresponding elements.
|
||||||
|
- Why do I get the weird zig-zag pattern on the triangles,
|
||||||
|
despite larger numbers of them? Is it something in how I
|
||||||
|
twist the frames?
|
||||||
|
- How can I compute the *torsion* on a quad? I think it
|
||||||
|
comes down to this: torsion applied across the quad I'm
|
||||||
|
triangulating leading to neither diagonal being a
|
||||||
|
particularly good choice. Subdividing the boundary seems
|
||||||
|
to help, but other triangulation methods (e.g. turning a
|
||||||
|
quad to 4 triangles by adding the centroid) could be good
|
||||||
|
too.
|
||||||
|
- Facets/edges are just oriented the wrong way...
|
||||||
|
- I need an actual example of branching/forking. If I simply
|
||||||
|
split a boundary into sub-boundaries per the rules I already
|
||||||
|
have in my notes, then this still lets me split any way I want
|
||||||
|
to without having to worry about joining N boundaries instead
|
||||||
|
of 2, doesn't it?
|
||||||
|
|
||||||
|
Other notes:
|
||||||
|
- Picking at random the diagonal on the quad to triangulate with
|
||||||
|
does seem to turn 'error' just to noise, and in its own way this
|
||||||
|
is preferable.
|
||||||
@ -1,43 +1,5 @@
|
|||||||
{
|
{
|
||||||
"cells": [
|
"cells": [
|
||||||
{
|
|
||||||
"cell_type": "markdown",
|
|
||||||
"metadata": {},
|
|
||||||
"source": [
|
|
||||||
"To do:\n",
|
|
||||||
"\n",
|
|
||||||
"- Parametrize gen_twisted_boundary over boundaries and\n",
|
|
||||||
"do my nested spiral\n",
|
|
||||||
"- Rewrite ram_horn in terms of newer abstractions\n",
|
|
||||||
"- Encode the notions of \"generator which transforms an\n",
|
|
||||||
"existing list of boundaries\", \"generator which transforms\n",
|
|
||||||
"another generator\"\n",
|
|
||||||
"- Work directly with lists of boundaries. The only thing\n",
|
|
||||||
"I ever do with them is apply transforms to all of them, or\n",
|
|
||||||
"join adjacent ones with corresponding elements.\n",
|
|
||||||
"- Why do I get the weird zig-zag pattern on the triangles,\n",
|
|
||||||
"despite larger numbers of them? Is it something in how I\n",
|
|
||||||
"twist the frames?\n",
|
|
||||||
" - How can I compute the *torsion* on a quad? I think it\n",
|
|
||||||
" comes down to this: torsion applied across the quad I'm\n",
|
|
||||||
" triangulating leading to neither diagonal being a\n",
|
|
||||||
" particularly good choice. Subdividing the boundary seems\n",
|
|
||||||
" to help, but other triangulation methods (e.g. turning a\n",
|
|
||||||
" quad to 4 triangles by adding the centroid) could be good\n",
|
|
||||||
" too.\n",
|
|
||||||
" - Facets/edges are just oriented the wrong way...\n",
|
|
||||||
"- I need an actual example of branching/forking. If I simply\n",
|
|
||||||
"split a boundary into sub-boundaries per the rules I already\n",
|
|
||||||
"have in my notes, then this still lets me split any way I want\n",
|
|
||||||
"to without having to worry about joining N boundaries instead\n",
|
|
||||||
"of 2, doesn't it?\n",
|
|
||||||
"\n",
|
|
||||||
"Other notes:\n",
|
|
||||||
"- Picking at random the diagonal on the quad to triangulate with\n",
|
|
||||||
" does seem to turn 'error' just to noise, and in its own way this\n",
|
|
||||||
" is preferable. "
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 1,
|
"execution_count": 1,
|
||||||
@ -50,6 +12,7 @@
|
|||||||
"import random\n",
|
"import random\n",
|
||||||
"\n",
|
"\n",
|
||||||
"import meshutil\n",
|
"import meshutil\n",
|
||||||
|
"import meshgen\n",
|
||||||
"import examples"
|
"import examples"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
115
examples.py
115
examples.py
@ -1,10 +1,12 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
import meshutil
|
|
||||||
import stl.mesh
|
import stl.mesh
|
||||||
import numpy
|
import numpy
|
||||||
import trimesh
|
import trimesh
|
||||||
|
|
||||||
|
import meshutil
|
||||||
|
import meshgen
|
||||||
|
|
||||||
# I should be moving some of these things out into more of a
|
# I should be moving some of these things out into more of a
|
||||||
# standard library than an 'examples' script
|
# standard library than an 'examples' script
|
||||||
|
|
||||||
@ -59,7 +61,7 @@ def ram_horn():
|
|||||||
def ram_horn_gen(b, xf):
|
def ram_horn_gen(b, xf):
|
||||||
while True:
|
while True:
|
||||||
b1 = xf.apply_to(b)
|
b1 = xf.apply_to(b)
|
||||||
yield b1
|
yield [b1]
|
||||||
incr = meshutil.Transform() \
|
incr = meshutil.Transform() \
|
||||||
.scale(0.9) \
|
.scale(0.9) \
|
||||||
.rotate([-1,0,1], 0.3) \
|
.rotate([-1,0,1], 0.3) \
|
||||||
@ -76,7 +78,8 @@ def ram_horn2():
|
|||||||
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)
|
||||||
meshes = []
|
meshes = []
|
||||||
#meshes.append(meshutil.join_boundary_simple(b0, b1))
|
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:
|
||||||
xf = meshutil.Transform() \
|
xf = meshutil.Transform() \
|
||||||
@ -84,13 +87,9 @@ def ram_horn2():
|
|||||||
.scale(0.5) \
|
.scale(0.5) \
|
||||||
.translate(0.25,0.25,1) \
|
.translate(0.25,0.25,1) \
|
||||||
.rotate([0,0,1], i*numpy.pi/2)
|
.rotate([0,0,1], i*numpy.pi/2)
|
||||||
b = xf.apply_to(b1)
|
gen = ram_horn_gen(b1, xf)
|
||||||
gen = ram_horn_gen(b, xf)
|
mesh = meshgen.gen2mesh(gen, count=128, close_last=True)
|
||||||
mesh = gen2mesh(gen, count=128)
|
|
||||||
print(mesh)
|
|
||||||
meshes.append(mesh)
|
meshes.append(mesh)
|
||||||
# Close final boundary:
|
|
||||||
meshes.append(meshutil.close_boundary_simple(b_sub1[::-1,:]))
|
|
||||||
mesh = meshutil.FaceVertexMesh.concat_many(meshes)
|
mesh = meshutil.FaceVertexMesh.concat_many(meshes)
|
||||||
return mesh
|
return mesh
|
||||||
|
|
||||||
@ -161,103 +160,19 @@ def twist_nonlinear(dx0 = 2, dz=0.2, count=3, scale=0.99, layers=100):
|
|||||||
mesh = meshutil.FaceVertexMesh.concat_many(meshes)
|
mesh = meshutil.FaceVertexMesh.concat_many(meshes)
|
||||||
return mesh
|
return mesh
|
||||||
|
|
||||||
# Generate a frame with 'count' boundaries in the XZ plane.
|
|
||||||
# Each one rotates by 'ang' as it moves by 'dz'.
|
|
||||||
# dx0 is center-point distance from each to the origin.
|
|
||||||
def gen_twisted_boundary(count=4, dx0=2, dz=0.2, ang=0.1):
|
|
||||||
b = numpy.array([
|
|
||||||
[0, 0, 0],
|
|
||||||
[1, 0, 0],
|
|
||||||
[1, 0, 1],
|
|
||||||
[0, 0, 1],
|
|
||||||
], dtype=numpy.float64) - [0.5, 0, 0.5]
|
|
||||||
b = meshutil.subdivide_boundary(b)
|
|
||||||
b = meshutil.subdivide_boundary(b)
|
|
||||||
b = meshutil.subdivide_boundary(b)
|
|
||||||
# Generate 'seed' transformations:
|
|
||||||
xfs = [meshutil.Transform().translate(dx0, 0, 0).rotate([0,1,0], numpy.pi * 2 * i / count)
|
|
||||||
for i in range(count)]
|
|
||||||
# (we'll increment the transforms in xfs as we go)
|
|
||||||
while True:
|
|
||||||
xfs_new = []
|
|
||||||
bs = []
|
|
||||||
for i, xf in enumerate(xfs):
|
|
||||||
# Generate a boundary from running transform:
|
|
||||||
b_i = xf.apply_to(b)
|
|
||||||
bs.append(b_i)
|
|
||||||
# Increment transform i:
|
|
||||||
xf2 = xf.rotate([0,1,0], ang)
|
|
||||||
xfs_new.append(xf2)
|
|
||||||
xfs = xfs_new
|
|
||||||
yield bs
|
|
||||||
|
|
||||||
# This is to see how well it works to compose generators:
|
|
||||||
def gen_inc_y(gen, dy=0.1):
|
|
||||||
xf = meshutil.Transform()
|
|
||||||
for bs in gen:
|
|
||||||
bs2 = [xf.apply_to(b) for b in bs]
|
|
||||||
yield bs2
|
|
||||||
xf = xf.translate(0, dy, 0)
|
|
||||||
|
|
||||||
# Wrap a boundary generator around a (sorta) torus that is along XY.
|
|
||||||
# producing a mesh.
|
|
||||||
# 'frames' sets resolution, 'rad' sets radius (the boundary's origin
|
|
||||||
# sweeps through this radius - it's not 'inner' or 'outer' radius).
|
|
||||||
#
|
|
||||||
# generator should produce lists of boundaries which are oriented
|
|
||||||
# roughly in XZ. This will get 'frames' elements from it if
|
|
||||||
# possible.
|
|
||||||
def gen_torus_xy(gen, rad=2, frames=100):
|
|
||||||
ang = numpy.pi*2 / frames
|
|
||||||
xf = meshutil.Transform().translate(rad, 0, 0)
|
|
||||||
for i,bs in enumerate(gen):
|
|
||||||
if i >= frames:
|
|
||||||
break
|
|
||||||
bs2 = [xf.apply_to(b) for b in bs]
|
|
||||||
yield bs2
|
|
||||||
xf = xf.rotate([0,0,1], ang)
|
|
||||||
|
|
||||||
# String together boundaries from a generator.
|
|
||||||
# If count is nonzero, run only this many iterations.
|
|
||||||
def gen2mesh(gen, count=0, flip_order=False, loop=False, join_fn=meshutil.join_boundary_optim):
|
|
||||||
# Get first list of boundaries:
|
|
||||||
bs_first = next(gen)
|
|
||||||
bs_last = bs_first
|
|
||||||
# TODO: Begin and end with close_boundary_simple
|
|
||||||
meshes = []
|
|
||||||
for i,bs_cur in enumerate(gen):
|
|
||||||
if count > 0 and i >= count:
|
|
||||||
break
|
|
||||||
for j,b in enumerate(bs_cur):
|
|
||||||
if flip_order:
|
|
||||||
m = join_fn(b, bs_last[j])
|
|
||||||
else:
|
|
||||||
m = join_fn(bs_last[j], b)
|
|
||||||
meshes.append(m)
|
|
||||||
bs_last = bs_cur
|
|
||||||
if loop:
|
|
||||||
for b0,b1 in zip(bs_last, bs_first):
|
|
||||||
if flip_order:
|
|
||||||
m = join_fn(b1, b0)
|
|
||||||
else:
|
|
||||||
m = join_fn(b0, b1)
|
|
||||||
meshes.append(m)
|
|
||||||
mesh = meshutil.FaceVertexMesh.concat_many(meshes)
|
|
||||||
return mesh
|
|
||||||
|
|
||||||
def twist_from_gen():
|
def twist_from_gen():
|
||||||
gen = gen_inc_y(gen_twisted_boundary())
|
gen = meshgen.gen_inc_y(meshgen.gen_twisted_boundary())
|
||||||
mesh = gen2mesh(gen, 100, True)
|
mesh = meshgen.gen2mesh(gen, 100, True)
|
||||||
return mesh
|
return mesh
|
||||||
|
|
||||||
# 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 = 5000, turns = 4, count = 4, rad = 4):
|
def twisty_torus(frames = 200, 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 = meshgen.gen_torus_xy(meshgen.gen_twisted_boundary(count=count, ang=angle), rad=rad, frames=frames)
|
||||||
return gen2mesh(gen, 0, flip_order=True, loop=True)
|
return meshgen.gen2mesh(gen, 0, flip_order=True, loop=True)
|
||||||
|
|
||||||
# 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
|
||||||
@ -265,8 +180,8 @@ def twisty_torus(frames = 5000, turns = 4, count = 4, rad = 4):
|
|||||||
def twisty_torus_opt(frames = 200, turns = 4, count = 4, rad = 4):
|
def twisty_torus_opt(frames = 200, 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 = meshgen.gen_torus_xy(meshgen.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)
|
return meshgen.gen2mesh(gen, 0, flip_order=True, loop=True, join_fn=meshutil.join_boundary_optim)
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
fns = {
|
fns = {
|
||||||
|
|||||||
97
meshgen.py
Normal file
97
meshgen.py
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
import meshutil
|
||||||
|
import stl.mesh
|
||||||
|
import numpy
|
||||||
|
import trimesh
|
||||||
|
|
||||||
|
# Generate a frame with 'count' boundaries in the XZ plane.
|
||||||
|
# Each one rotates by 'ang' as it moves by 'dz'.
|
||||||
|
# dx0 is center-point distance from each to the origin.
|
||||||
|
def gen_twisted_boundary(count=4, dx0=2, dz=0.2, ang=0.1):
|
||||||
|
b = numpy.array([
|
||||||
|
[0, 0, 0],
|
||||||
|
[1, 0, 0],
|
||||||
|
[1, 0, 1],
|
||||||
|
[0, 0, 1],
|
||||||
|
], dtype=numpy.float64) - [0.5, 0, 0.5]
|
||||||
|
b = meshutil.subdivide_boundary(b)
|
||||||
|
b = meshutil.subdivide_boundary(b)
|
||||||
|
b = meshutil.subdivide_boundary(b)
|
||||||
|
# Generate 'seed' transformations:
|
||||||
|
xfs = [meshutil.Transform().translate(dx0, 0, 0).rotate([0,1,0], numpy.pi * 2 * i / count)
|
||||||
|
for i in range(count)]
|
||||||
|
# (we'll increment the transforms in xfs as we go)
|
||||||
|
while True:
|
||||||
|
xfs_new = []
|
||||||
|
bs = []
|
||||||
|
for i, xf in enumerate(xfs):
|
||||||
|
# Generate a boundary from running transform:
|
||||||
|
b_i = xf.apply_to(b)
|
||||||
|
bs.append(b_i)
|
||||||
|
# Increment transform i:
|
||||||
|
xf2 = xf.rotate([0,1,0], ang)
|
||||||
|
xfs_new.append(xf2)
|
||||||
|
xfs = xfs_new
|
||||||
|
yield bs
|
||||||
|
|
||||||
|
# This is to see how well it works to compose generators:
|
||||||
|
def gen_inc_y(gen, dy=0.1):
|
||||||
|
xf = meshutil.Transform()
|
||||||
|
for bs in gen:
|
||||||
|
bs2 = [xf.apply_to(b) for b in bs]
|
||||||
|
yield bs2
|
||||||
|
xf = xf.translate(0, dy, 0)
|
||||||
|
|
||||||
|
# Wrap a boundary generator around a (sorta) torus that is along XY.
|
||||||
|
# producing a mesh.
|
||||||
|
# 'frames' sets resolution, 'rad' sets radius (the boundary's origin
|
||||||
|
# sweeps through this radius - it's not 'inner' or 'outer' radius).
|
||||||
|
#
|
||||||
|
# generator should produce lists of boundaries which are oriented
|
||||||
|
# roughly in XZ. This will get 'frames' elements from it if
|
||||||
|
# possible.
|
||||||
|
def gen_torus_xy(gen, rad=2, frames=100):
|
||||||
|
ang = numpy.pi*2 / frames
|
||||||
|
xf = meshutil.Transform().translate(rad, 0, 0)
|
||||||
|
for i,bs in enumerate(gen):
|
||||||
|
if i >= frames:
|
||||||
|
break
|
||||||
|
bs2 = [xf.apply_to(b) for b in bs]
|
||||||
|
yield bs2
|
||||||
|
xf = xf.rotate([0,0,1], ang)
|
||||||
|
|
||||||
|
# String together boundaries from a generator.
|
||||||
|
# If count is nonzero, run only this many iterations.
|
||||||
|
def gen2mesh(gen, count=0, flip_order=False, loop=False,
|
||||||
|
close_first = False,
|
||||||
|
close_last = False,
|
||||||
|
join_fn=meshutil.join_boundary_optim):
|
||||||
|
# Get first list of boundaries:
|
||||||
|
bs_first = next(gen)
|
||||||
|
bs_last = bs_first
|
||||||
|
# TODO: Begin and end with close_boundary_simple
|
||||||
|
meshes = []
|
||||||
|
if close_first:
|
||||||
|
for b in bs_first:
|
||||||
|
meshes.append(meshutil.close_boundary_simple(b))
|
||||||
|
for i,bs_cur in enumerate(gen):
|
||||||
|
if count > 0 and i >= count:
|
||||||
|
break
|
||||||
|
for j,b in enumerate(bs_cur):
|
||||||
|
if flip_order:
|
||||||
|
m = join_fn(b, bs_last[j])
|
||||||
|
else:
|
||||||
|
m = join_fn(bs_last[j], b)
|
||||||
|
meshes.append(m)
|
||||||
|
bs_last = bs_cur
|
||||||
|
if loop:
|
||||||
|
for b0,b1 in zip(bs_last, bs_first):
|
||||||
|
if flip_order:
|
||||||
|
m = join_fn(b1, b0)
|
||||||
|
else:
|
||||||
|
m = join_fn(b0, b1)
|
||||||
|
meshes.append(m)
|
||||||
|
if close_last:
|
||||||
|
for b in bs_last:
|
||||||
|
meshes.append(meshutil.close_boundary_simple(b))
|
||||||
|
mesh = meshutil.FaceVertexMesh.concat_many(meshes)
|
||||||
|
return mesh
|
||||||
@ -203,7 +203,7 @@ def subdivide_boundary(bound):
|
|||||||
b2[2*i+1,:] = mids[i,:]
|
b2[2*i+1,:] = mids[i,:]
|
||||||
return b2
|
return b2
|
||||||
|
|
||||||
def join_boundary_simple(bound1, bound2):
|
def join_boundary_simple(bound1, bound2, random_diag=False):
|
||||||
# bound1 & bound2 are both arrays of shape (N,3), representing
|
# bound1 & bound2 are both arrays of shape (N,3), representing
|
||||||
# the points of a boundary. This joins the two boundaries by
|
# the points of a boundary. This joins the two boundaries by
|
||||||
# simply connecting quads (made of 2 triangles) straight across.
|
# simply connecting quads (made of 2 triangles) straight across.
|
||||||
@ -218,7 +218,7 @@ def join_boundary_simple(bound1, bound2):
|
|||||||
for i in range(n):
|
for i in range(n):
|
||||||
v0 = i
|
v0 = i
|
||||||
v1 = (i + 1) % n
|
v1 = (i + 1) % n
|
||||||
if random.random() < 0.5:
|
if random_diag and random.random() < 0.5:
|
||||||
fs[2*i] = [n + v1, n + v0, v0]
|
fs[2*i] = [n + v1, n + v0, v0]
|
||||||
fs[2*i + 1] = [v1, n + v1, v0]
|
fs[2*i + 1] = [v1, n + v1, v0]
|
||||||
else:
|
else:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user