Fix warnings, add some more notes
This commit is contained in:
parent
06dc2191fa
commit
dce29003f1
49
README.md
49
README.md
@ -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.
|
||||||
@ -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![
|
||||||
|
|||||||
@ -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() {
|
||||||
|
|||||||
10
src/rule.rs
10
src/rule.rs
@ -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,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user