Fix warnings, add some more notes

This commit is contained in:
Chris Hodapp 2020-10-02 14:16:22 -04:00
parent 06dc2191fa
commit dce29003f1
4 changed files with 60 additions and 44 deletions

View File

@ -10,7 +10,7 @@ patch up or subdivide the meshes in post-processing.
These grammars by their nature worked in discrete steps, These grammars by their nature worked in discrete steps,
but at one point I tried (unsuccessfully) to extend this but at one point I tried (unsuccessfully) to extend this
system to working in a more continuous and parametric system to working in a more continuous and parametric
way. way. (See `parametric_mesh` and any DCEL code.)
I also ran into problems anytime I wanted to produce I also ran into problems anytime I wanted to produce
meshes in a way that was more "refining" than "generative". meshes in a way that was more "refining" than "generative".
@ -21,6 +21,16 @@ from a 'parent' rule, besides being able to connect to its
vertices - and sometimes the "refining" part of things vertices - and sometimes the "refining" part of things
required this in order to work right. required this in order to work right.
The problems with the parametric/continuous, and the
aforementioned "refining", were related. The issue is that
in order to get good meshes, I needed to be able to minimize
approximation error with the triangles and avoid triangles
with extreme angles, and there was seemingly no good way to
do this by incremental construction (like I was trying to
use elsewhere in my model) - and so its seems I just ended up
reinventing, badly, a lot of existing work with subdivision
and meshing.
I've also disliked how much my model felt like it tied me I've also disliked how much my model felt like it tied me
down to the "triangle mesh" representation. I haven't down to the "triangle mesh" representation. I haven't
found a good way to build up higher-level representations found a good way to build up higher-level representations
@ -36,6 +46,18 @@ consider how much those assume the presence of garbage
collection. Really, I wanted a Lisp, and then the presence of collection. Really, I wanted a Lisp, and then the presence of
a REPL would have been another bonus. a REPL would have been another bonus.
On top of this, my implementation is pretty slow when it is
using a large number of rules each producing small geometry
(which is almost literally the only way it *can* be used
if you want to produce a fairly complex mesh). I did some
profiling some months ago that showed I was spending the
vast majority of my time in `extend()` and `clone()` for
`Vec` - and so I could probably see some huge performance
gains if I could simply pre-allocate vectors and share geometry
more. Also, I'm pretty sure this code does some very task-parallel
elements (e.g. anytime a rule branches), and multithreading should
be able to exploit this if I care.
If I actually understood my goals enough to put better If I actually understood my goals enough to put better
constraints on my model, Rust probably would have been fine. constraints on my model, Rust probably would have been fine.
As it stands now, the lack of clarity in both my theory As it stands now, the lack of clarity in both my theory
@ -44,16 +66,11 @@ related to Rust.
## Highest priority: ## Highest priority:
- Fix `ramhorn_branch`.
- Once I've fixed that, see about a refactor that respects the
same model, but involves much less ceremony and boilerplate.
- Figure out the crash bug in `vec_indexed!` if I put a Vertex - Figure out the crash bug in `vec_indexed!` if I put a Vertex
*after* an Arg. *after* an Arg.
- Just scrap `parametric_mesh` as much as possible and use existing
tools (e.g. OpenSubdiv) because this DCEL method is just painful for
what it is and I have some questions on how it can even work
theoretically.
- Connect up the `parametric_mesh` stuff that remains, and worry about
perfect meshes later.
- Get identical or near-identical meshes to `ramhorn_branch` from
Python. (Should just be a matter of tweaking parameters.)
- Look at performance. - Look at performance.
- Start at `to_mesh_iter()`. The cost of small appends/connects - Start at `to_mesh_iter()`. The cost of small appends/connects
seems to be killing performance. seems to be killing performance.
@ -62,13 +79,6 @@ related to Rust.
like I should be able to share geometry with the `Rc` (like noted like I should be able to share geometry with the `Rc` (like noted
above), defer copying until actually needed, and pre-allocate the above), defer copying until actually needed, and pre-allocate the
vector to its size (which should be easy to compute). vector to its size (which should be easy to compute).
- See `automata_scratch/examples.py` and implement some of the tougher
examples.
- `twisty_torus`, `spiral_nested_2`, & `spiral_nested_3` are all
that remain. To do them, I need to compose transformations (not
in the matrix sense), but I also probably need to produce
RuleEvals which always have `xf` of identity transformation since
the Python code does not 'inherit' transforms unless I tell it to.
## Important but less critical: ## Important but less critical:
@ -85,7 +95,8 @@ related to Rust.
- Catch-alls: - Catch-alls:
- Grep for all TODOs in code, really. - Grep for all TODOs in code, really.
- Look at everything in `README.md` in `automata_scratch`. - Look at everything in `README.md` in `automata_scratch`,
my old Python code from around 2019-09.
## If I'm bored: ## If I'm bored:
@ -96,9 +107,6 @@ related to Rust.
- Would being able to name a rule node (perhaps conditionally under - Would being able to name a rule node (perhaps conditionally under
some compile-time flag) help for debugging? some compile-time flag) help for debugging?
- Use an actual logging framework. - Use an actual logging framework.
- Take a square. Wrap it around to a torus. Now add a twist (about
the axis that is normal to the square). This is simple, but it looks
pretty cool.
- How can I take tangled things like the cinquefoil and produce more - How can I take tangled things like the cinquefoil and produce more
'iterative' versions that still weave around? 'iterative' versions that still weave around?
@ -118,3 +126,4 @@ related to Rust.
- If you *pre* multiply a transformation: you are transforming the - If you *pre* multiply a transformation: you are transforming the
entire global space. If you *post* multiply: you are transforming entire global space. If you *post* multiply: you are transforming
the current local space. the current local space.
- Don't reinvent subdivision surfaces.

View File

@ -1,5 +1,5 @@
use std::rc::Rc; use std::rc::Rc;
use std::f32::consts::{FRAC_PI_2, FRAC_PI_3, FRAC_PI_6, PI}; use std::f32::consts::{FRAC_PI_2, FRAC_PI_3};
use std::f32; use std::f32;
use nalgebra::*; use nalgebra::*;
@ -8,11 +8,11 @@ use rand::Rng;
use crate::util; use crate::util;
use crate::util::VecExt; use crate::util::VecExt;
use crate::mesh::{Mesh, MeshFunc, VertexUnion, vert_args}; use crate::mesh::{Mesh, MeshFunc, VertexUnion, vert_args};
use crate::xform::{Transform, Vertex, vertex, Mat4, id}; use crate::xform::{Transform, Vertex, vertex, id};
use crate::rule::{Rule, RuleFn, RuleEval, Child}; use crate::rule::{Rule, RuleEval, Child};
use crate::prim; use crate::prim;
use crate::dcel; use crate::dcel;
use crate::dcel::{DCELMesh, VertSpec}; use crate::dcel::{VertSpec};
pub fn cube_thing() -> Rule<()> { pub fn cube_thing() -> Rule<()> {
@ -106,7 +106,7 @@ pub fn barbs(random: bool) -> Rule<()> {
}; };
let main_incr = |random| { let main_incr = |random| {
if random { if random {
let t = rand::thread_rng().gen_range(0.75, 1.25); //let t = rand::thread_rng().gen_range(0.75, 1.25);
let s = rand::thread_rng().gen_range(0.85, 1.10); let s = rand::thread_rng().gen_range(0.85, 1.10);
let rz = rand::thread_rng().gen_range(0.05, 0.25); let rz = rand::thread_rng().gen_range(0.05, 0.25);
let rx = rand::thread_rng().gen_range(0.08, 0.12); let rx = rand::thread_rng().gen_range(0.08, 0.12);
@ -124,7 +124,7 @@ pub fn barbs(random: bool) -> Rule<()> {
let main = rule_fn!(() => |self_, base_verts| { let main = rule_fn!(() => |self_, base_verts| {
let mut next_verts = base_verts; let mut next_verts = base_verts;
let (a0, a1) = next_verts.append_indexed(vert_args(0..4)); let (a0, _) = next_verts.append_indexed(vert_args(0..4));
// This contributes no faces of its own - just vertices. // This contributes no faces of its own - just vertices.
let geom = MeshFunc { verts: next_verts.clone(), faces: vec![] }; let geom = MeshFunc { verts: next_verts.clone(), faces: vec![] };
@ -164,17 +164,22 @@ pub fn barbs(random: bool) -> Rule<()> {
Rule { eval: base, ctxt: () } Rule { eval: base, ctxt: () }
} }
pub fn pyramid() -> Rule<()> { pub fn sierpinski() -> Rule<()> {
// Initial height step:
let dz = 0.10;
// 'Extra' z rotation (0.0 for normal Sierpinski)
let dr = 0.1;
// Scale factor (0.5 for normal Sierpinski)
let s = 0.51;
let rt3 = (3.0).sqrt(); let rt3 = (3.0).sqrt();
let dz = 0.10;
// Indices: // Indices:
// b+0,b+1,b+2 = base vertices // b+0,b+1,b+2 = base vertices
// t+0,t+1,t+2 = 'top' vertices above base // t+0,t+1,t+2 = 'top' vertices above base
// tm01, tm12, tm20 = midpoints of (t0,t1), (t1,t2), (t2,t0). // tm01, tm12, tm20 = midpoints of (t0,t1), (t1,t2), (t2,t0).
let (b, t, tm01, tm12, tm20); let (b, t, tm01, tm12, tm20, n);
let base_verts: Vec<VertexUnion> = { let base_verts: Vec<VertexUnion> = {
let v0 = vertex(rt3/3.0, 0.0, 0.0); let v0 = vertex(rt3/3.0, 0.0, 0.0);
let v1 = vertex(-rt3/6.0, 1.0/2.0, 0.0); let v1 = vertex(-rt3/6.0, 1.0/2.0, 0.0);
@ -192,24 +197,24 @@ pub fn pyramid() -> Rule<()> {
@tm01 VertexUnion::Vertex((v0b+v1b)/2.0), @tm01 VertexUnion::Vertex((v0b+v1b)/2.0),
@tm12 VertexUnion::Vertex((v1b+v2b)/2.0), @tm12 VertexUnion::Vertex((v1b+v2b)/2.0),
@tm20 VertexUnion::Vertex((v2b+v0b)/2.0), @tm20 VertexUnion::Vertex((v2b+v0b)/2.0),
@n,
] ]
}; };
let tri_split = move |i| { let tri_split = move |i| {
let rt3 = (3.0).sqrt(); let rt3 = (3.0).sqrt();
let angle = 2.0 * FRAC_PI_3 * (i as f32); let angle = 2.0 * FRAC_PI_3 * (i as f32) + dr;
id(). id().
rotate(&Vector3::z_axis(), angle). rotate(&Vector3::z_axis(), angle).
translate(rt3/12.0, 0.0, 0.0). translate(rt3/12.0, 0.0, 0.0).
scale(0.49). scale(s).
translate(0.0, 0.0, dz) translate(0.0, 0.0, dz)
}; };
let split = rule_fn!(() => |_s, base_verts| { let split = rule_fn!(() => |_s, base_verts| {
let mut next_verts = base_verts.clone(); let mut next_verts = base_verts.clone();
let mut final_verts = base_verts; let (a0, _) = next_verts.append_indexed(vert_args(0..3));
let (a0, a1) = next_verts.append_indexed(vert_args(0..3));
RuleEval { RuleEval {
geom: Rc::new(MeshFunc { geom: Rc::new(MeshFunc {
@ -231,8 +236,12 @@ pub fn pyramid() -> Rule<()> {
], ],
}), }),
final_geom: Rc::new(MeshFunc { final_geom: Rc::new(MeshFunc {
verts: vert_args(t..(t+3)), verts: vert_args(0..n), // just duplicate same verts
faces: vec![ 0, 1, 2 ], faces: vec![
t+0, tm01, tm20,
t+1, tm12, tm01,
t+2, tm20, tm12,
],
}), }),
children: vec![ children: vec![
child!(_s, tri_split(0), t+0, tm01, tm20), child!(_s, tri_split(0), t+0, tm01, tm20),
@ -494,7 +503,6 @@ pub fn nest_spiral_2() -> Rule<NestSpiral2Ctxt> {
}, },
} }
} }
*/
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub struct TorusCtxt { pub struct TorusCtxt {
@ -503,7 +511,6 @@ pub struct TorusCtxt {
stack: [Transform; 3], stack: [Transform; 3],
} }
/*
pub fn twisty_torus() -> Rule<TorusCtxt> { pub fn twisty_torus() -> Rule<TorusCtxt> {
let subdiv = 8; let subdiv = 8;
let seed = vec![ let seed = vec![

View File

@ -80,7 +80,7 @@ mod tests {
fn barbs_random() { run_test(examples::barbs(true), 80, "barbs_random", false); } fn barbs_random() { run_test(examples::barbs(true), 80, "barbs_random", false); }
#[test] #[test]
fn pyramid() { run_test(examples::pyramid2(), 3, "pyramid2", false); } fn sierpinski() { run_test(examples::sierpinski(), 6, "sierpinski", false); }
/* /*
#[test] #[test]
fn twist() { fn twist() {

View File

@ -96,7 +96,7 @@ macro_rules! child {
macro_rules! child_iter { macro_rules! child_iter {
( $Rule:expr, $Xform:expr, $Args:expr ) => { ( $Rule:expr, $Xform:expr, $Args:expr ) => {
Child { Child {
rule: /*std::rc::Rc::new*/($Rule), rule: /*std::rc::Rc::new*/($Rule).clone(),
xf: $Xform, xf: $Xform,
arg_vals: $Args.collect(), // does this even need a macro? arg_vals: $Args.collect(), // does this even need a macro?
} }
@ -366,7 +366,7 @@ pub fn parametric_mesh<F>(frame: Vec<Vertex>, f: F, t0: f32, t1: f32, max_err: f
panic!("frame must have at least 3 vertices"); panic!("frame must have at least 3 vertices");
} }
struct frontierVert { struct FrontierVert {
vert: Vertex, // Vertex position vert: Vertex, // Vertex position
t: f32, // Parameter value; f(t) should equal vert t: f32, // Parameter value; f(t) should equal vert
frame_idx: usize, // Index of 'frame' this sits in the trajectory of frame_idx: usize, // Index of 'frame' this sits in the trajectory of
@ -376,7 +376,7 @@ pub fn parametric_mesh<F>(frame: Vec<Vertex>, f: F, t0: f32, t1: f32, max_err: f
}; };
// Init 'frontier' with each 'frame' vertex, and start it at t=t0. // Init 'frontier' with each 'frame' vertex, and start it at t=t0.
let mut frontier: Vec<frontierVert> = frame.iter().enumerate().map(|(i,v)| frontierVert { let mut frontier: Vec<FrontierVert> = frame.iter().enumerate().map(|(i,v)| FrontierVert {
vert: *v, vert: *v,
t: t0, t: t0,
frame_idx: i, frame_idx: i,
@ -402,7 +402,7 @@ pub fn parametric_mesh<F>(frame: Vec<Vertex>, f: F, t0: f32, t1: f32, max_err: f
// Pick a vertex to advance. // Pick a vertex to advance.
// //
// Heuristic for now: pick the 'furthest back' (lowest t) // Heuristic for now: pick the 'furthest back' (lowest t)
let (i,v) = frontier.iter().enumerate().min_by(|(i,f), (j, g)| let (i,v) = frontier.iter().enumerate().min_by(|(_,f), (_, g)|
f.t.partial_cmp(&g.t).unwrap_or(std::cmp::Ordering::Equal)).unwrap(); f.t.partial_cmp(&g.t).unwrap_or(std::cmp::Ordering::Equal)).unwrap();
// TODO: Make this less ugly? // TODO: Make this less ugly?
@ -456,7 +456,7 @@ pub fn parametric_mesh<F>(frame: Vec<Vertex>, f: F, t0: f32, t1: f32, max_err: f
]); ]);
// Replace this vertex in the frontier: // Replace this vertex in the frontier:
frontier[i] = frontierVert { frontier[i] = FrontierVert {
vert: v_next, vert: v_next,
frame_idx: v.frame_idx, frame_idx: v.frame_idx,
mesh_idx: pos, mesh_idx: pos,