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,
|
||||
but at one point I tried (unsuccessfully) to extend this
|
||||
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
|
||||
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
|
||||
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
|
||||
down to the "triangle mesh" representation. I haven't
|
||||
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
|
||||
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
|
||||
constraints on my model, Rust probably would have been fine.
|
||||
As it stands now, the lack of clarity in both my theory
|
||||
@ -44,16 +66,11 @@ related to Rust.
|
||||
|
||||
## 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
|
||||
*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.
|
||||
- Start at `to_mesh_iter()`. The cost of small appends/connects
|
||||
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
|
||||
above), defer copying until actually needed, and pre-allocate the
|
||||
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:
|
||||
|
||||
@ -85,7 +95,8 @@ related to Rust.
|
||||
|
||||
- Catch-alls:
|
||||
- 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:
|
||||
|
||||
@ -96,9 +107,6 @@ related to Rust.
|
||||
- Would being able to name a rule node (perhaps conditionally under
|
||||
some compile-time flag) help for debugging?
|
||||
- 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
|
||||
'iterative' versions that still weave around?
|
||||
|
||||
@ -118,3 +126,4 @@ related to Rust.
|
||||
- If you *pre* multiply a transformation: you are transforming the
|
||||
entire global space. If you *post* multiply: you are transforming
|
||||
the current local space.
|
||||
- Don't reinvent subdivision surfaces.
|
||||
@ -1,5 +1,5 @@
|
||||
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 nalgebra::*;
|
||||
@ -8,11 +8,11 @@ use rand::Rng;
|
||||
use crate::util;
|
||||
use crate::util::VecExt;
|
||||
use crate::mesh::{Mesh, MeshFunc, VertexUnion, vert_args};
|
||||
use crate::xform::{Transform, Vertex, vertex, Mat4, id};
|
||||
use crate::rule::{Rule, RuleFn, RuleEval, Child};
|
||||
use crate::xform::{Transform, Vertex, vertex, id};
|
||||
use crate::rule::{Rule, RuleEval, Child};
|
||||
use crate::prim;
|
||||
use crate::dcel;
|
||||
use crate::dcel::{DCELMesh, VertSpec};
|
||||
use crate::dcel::{VertSpec};
|
||||
|
||||
pub fn cube_thing() -> Rule<()> {
|
||||
|
||||
@ -106,7 +106,7 @@ pub fn barbs(random: bool) -> Rule<()> {
|
||||
};
|
||||
let main_incr = |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 rz = rand::thread_rng().gen_range(0.05, 0.25);
|
||||
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 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.
|
||||
let geom = MeshFunc { verts: next_verts.clone(), faces: vec![] };
|
||||
@ -164,17 +164,22 @@ pub fn barbs(random: bool) -> Rule<()> {
|
||||
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 dz = 0.10;
|
||||
|
||||
// Indices:
|
||||
// b+0,b+1,b+2 = base vertices
|
||||
// t+0,t+1,t+2 = 'top' vertices above base
|
||||
// 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 v0 = vertex(rt3/3.0, 0.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),
|
||||
@tm12 VertexUnion::Vertex((v1b+v2b)/2.0),
|
||||
@tm20 VertexUnion::Vertex((v2b+v0b)/2.0),
|
||||
@n,
|
||||
]
|
||||
};
|
||||
|
||||
let tri_split = move |i| {
|
||||
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().
|
||||
rotate(&Vector3::z_axis(), angle).
|
||||
translate(rt3/12.0, 0.0, 0.0).
|
||||
scale(0.49).
|
||||
scale(s).
|
||||
translate(0.0, 0.0, dz)
|
||||
};
|
||||
|
||||
let split = rule_fn!(() => |_s, base_verts| {
|
||||
|
||||
let mut next_verts = base_verts.clone();
|
||||
let mut final_verts = base_verts;
|
||||
let (a0, a1) = next_verts.append_indexed(vert_args(0..3));
|
||||
let (a0, _) = next_verts.append_indexed(vert_args(0..3));
|
||||
|
||||
RuleEval {
|
||||
geom: Rc::new(MeshFunc {
|
||||
@ -231,8 +236,12 @@ pub fn pyramid() -> Rule<()> {
|
||||
],
|
||||
}),
|
||||
final_geom: Rc::new(MeshFunc {
|
||||
verts: vert_args(t..(t+3)),
|
||||
faces: vec![ 0, 1, 2 ],
|
||||
verts: vert_args(0..n), // just duplicate same verts
|
||||
faces: vec![
|
||||
t+0, tm01, tm20,
|
||||
t+1, tm12, tm01,
|
||||
t+2, tm20, tm12,
|
||||
],
|
||||
}),
|
||||
children: vec![
|
||||
child!(_s, tri_split(0), t+0, tm01, tm20),
|
||||
@ -494,7 +503,6 @@ pub fn nest_spiral_2() -> Rule<NestSpiral2Ctxt> {
|
||||
},
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct TorusCtxt {
|
||||
@ -503,7 +511,6 @@ pub struct TorusCtxt {
|
||||
stack: [Transform; 3],
|
||||
}
|
||||
|
||||
/*
|
||||
pub fn twisty_torus() -> Rule<TorusCtxt> {
|
||||
let subdiv = 8;
|
||||
let seed = vec![
|
||||
|
||||
@ -80,7 +80,7 @@ mod tests {
|
||||
fn barbs_random() { run_test(examples::barbs(true), 80, "barbs_random", false); }
|
||||
|
||||
#[test]
|
||||
fn pyramid() { run_test(examples::pyramid2(), 3, "pyramid2", false); }
|
||||
fn sierpinski() { run_test(examples::sierpinski(), 6, "sierpinski", false); }
|
||||
/*
|
||||
#[test]
|
||||
fn twist() {
|
||||
|
||||
10
src/rule.rs
10
src/rule.rs
@ -96,7 +96,7 @@ macro_rules! child {
|
||||
macro_rules! child_iter {
|
||||
( $Rule:expr, $Xform:expr, $Args:expr ) => {
|
||||
Child {
|
||||
rule: /*std::rc::Rc::new*/($Rule),
|
||||
rule: /*std::rc::Rc::new*/($Rule).clone(),
|
||||
xf: $Xform,
|
||||
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");
|
||||
}
|
||||
|
||||
struct frontierVert {
|
||||
struct FrontierVert {
|
||||
vert: Vertex, // Vertex position
|
||||
t: f32, // Parameter value; f(t) should equal vert
|
||||
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.
|
||||
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,
|
||||
t: t0,
|
||||
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.
|
||||
//
|
||||
// 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();
|
||||
// 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:
|
||||
frontier[i] = frontierVert {
|
||||
frontier[i] = FrontierVert {
|
||||
vert: v_next,
|
||||
frame_idx: v.frame_idx,
|
||||
mesh_idx: pos,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user