Convert ramhorn_branch example

This commit is contained in:
Chris Hodapp 2020-10-06 15:47:40 -04:00
parent 2017d9a605
commit 7ba6bcd648
3 changed files with 188 additions and 381 deletions

View File

@ -1,16 +1,23 @@
# This needs a title # This needs a title
This work was started as an attempt to make meshes in a more This particular code base was started around 2019 December
"generative" style, described by recursive grammars and as an attempt to make meshes in a more "generative" style,
replacement rules. One goal was to make it easy to produce described by recursive grammars and replacement rules. One
manifold meshes by following certain rules, and do so in a main goal was to make it easy to produce manifold meshes by
following certain rules, and do so in a
"correct-by-construction" manner rather than by having to "correct-by-construction" manner rather than by having to
patch up or subdivide the meshes in post-processing. patch up or subdivide the meshes in post-processing.
(This particular notion came out of some older work in 2018.
See 2018-06-26 paper notes. Paper notes around 2019-09
developed this further still.)
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. (See `parametric_mesh` and any DCEL code.) way. (See `parametric_mesh` and any DCEL code.)
The Python code I had written around 2019 September used
something like a discrete approximation of this, and its
limitations are part of why I started on this new version.
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".
@ -72,26 +79,18 @@ As it stands now, the lack of clarity in both my theory
and in my implementation is a far bigger issue than anything and in my implementation is a far bigger issue than anything
related to Rust. related to Rust.
Around early October 2020 I decided to scrap almost all of this and
write everything simply as direct function calls, despite that this uses
more stack space than I'd like. This started in the `un_greenspun`
branch, thus named because I needed to get rid of my buggy
implementation of half of Common Lisp. It paid off quite quickly and
also was vastly faster at generating meshes.
## Highest priority: ## Highest priority:
- See about a refactor that respects the same model, but involves - Begin converting older examples.
much less ceremony and boilerplate. - Trash all the dead code.
- Look at performance.
- Start at `to_mesh_iter()`. The cost of small appends/connects
seems to be killing performance.
- `connect()` is a big performance hot-spot: 85% of total time in
one test, around 51% in `extend()`, 33% in `clone()`. It seems
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).
## Important but less critical:
- Docs on modules - Docs on modules
- Compute global scale factor, and perhaps pass it to a rule (to
eventually be used for, perhaps, adaptive subdivision). Note that
one can find the scale factors by taking the length of the first 3
columns of the transform matrix (supposedly).
- swept-isocontour stuff from - swept-isocontour stuff from
`/mnt/dev/graphics_misc/isosurfaces_2018_2019/spiral*.py`. This `/mnt/dev/graphics_misc/isosurfaces_2018_2019/spiral*.py`. This
will probably require that I figure out parametric curves will probably require that I figure out parametric curves
@ -109,8 +108,6 @@ related to Rust.
obtain". Can I fix this somehow? Looks like a French-ism that made obtain". Can I fix this somehow? Looks like a French-ism that made
its way in. its way in.
- Multithread! This looks very task-parallel anywhere that I branch. - Multithread! This looks very task-parallel anywhere that I branch.
- Would being able to name a rule node (perhaps conditionally under
some compile-time flag) help for debugging?
- Use an actual logging framework. - Use an actual logging framework.
- 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?
@ -132,3 +129,4 @@ related to Rust.
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. - Don't reinvent subdivision surfaces.
- Don't reinvent Lisp when you wanted a Lisp!

View File

@ -3,66 +3,28 @@ use std::f32::consts::{FRAC_PI_2, FRAC_PI_3};
use std::f32; use std::f32;
use nalgebra::*; use nalgebra::*;
use rand::Rng; //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, 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::{VertSpec}; use crate::dcel::{VertSpec};
pub fn cube_thing() -> Rule<()> { pub struct Barbs {
// Quarter-turn in radians:
let qtr = FRAC_PI_2;
//let x = &Vector3::x_axis();
let y = &Vector3::y_axis();
let z = &Vector3::z_axis();
// Each element of this turns to a branch for the recursion:
let id = Transform::new();
let turns: Vec<Transform> = vec![
id.clone(),
id.rotate(y, qtr),
id.rotate(y, qtr * 2.0),
id.rotate(y, qtr * 3.0),
id.rotate(z, qtr),
id.rotate(z, -qtr),
];
let rec = move |self_: Rc<Rule<()>>| -> RuleEval<()> {
let xforms = turns.iter().map(|xf| xf.scale(0.5).translate(6.0, 0.0, 0.0));
RuleEval {
geom: Rc::new(prim::cube().to_meshfunc()),
final_geom: Rc::new(prim::empty_mesh().to_meshfunc()),
children: xforms.map(move |xf| Child {
rule: self_.clone(),
xf: xf,
arg_vals: vec![],
}).collect(),
}
};
Rule { eval: Rc::new(rec), ctxt: () }
}
#[derive(Clone)]
pub struct BarbsCtxt {
base_incr: Transform, base_incr: Transform,
barb_incr: Transform, barb_incr: Transform,
sides: [Transform; 4], sides: [Transform; 4],
base: Vec<Vertex>, base: Vec<Vertex>,
verts: Vec<Vertex>, verts: Vec<Vertex>,
faces: Vec<usize>, faces: Vec<usize>,
} }
impl BarbsCtxt { impl Barbs {
pub fn new() -> BarbsCtxt { pub fn new() -> Barbs {
// Incremental transform from each stage to the next: // Incremental transform from each stage to the next:
let base_incr = id().translate(0.0, 0.0, 1.0). let base_incr = id().translate(0.0, 0.0, 1.0).
rotate(&Vector3::z_axis(), 0.15). rotate(&Vector3::z_axis(), 0.15).
@ -87,7 +49,7 @@ impl BarbsCtxt {
rotate(&Vector3::y_axis(), -FRAC_PI_2). rotate(&Vector3::y_axis(), -FRAC_PI_2).
translate(0.5, 0.0, 0.5);// * barb_incr; translate(0.5, 0.0, 0.5);// * barb_incr;
} }
BarbsCtxt { Barbs {
base_incr: base_incr, base_incr: base_incr,
barb_incr: barb_incr, barb_incr: barb_incr,
base: base, base: base,
@ -97,38 +59,28 @@ impl BarbsCtxt {
} }
} }
pub fn depth_check(&self, xform: &Transform, iters: usize) -> bool { pub fn run(mut self, iters: usize) -> Mesh {
// Make seed vertices, use them for 'bottom' face, and recurse:
self.verts.append(&mut self.base.clone());
self.faces.extend_from_slice(&[0, 1, 2, 0, 2, 3]);
self.main(iters, id(), [0,1,2,3]);
return Mesh {
verts: self.verts,
faces: self.faces,
}
}
fn limit_check(&self, xform: &Transform, iters: usize) -> bool {
// Assume all scales are the same (for now) // Assume all scales are the same (for now)
let (s, _, _) = xform.get_scale(); let (s, _, _) = xform.get_scale();
return s < 0.005; return s < 0.005;
} }
pub fn barb(&mut self, iters: usize, xform: Transform, bound: [usize; 4]) { fn main(&mut self, iters: usize, xform: Transform, bound: [usize; 4]) {
if self.depth_check(&xform, iters) { if self.limit_check(&xform, iters) {
self.faces.extend_from_slice(&[ self.faces.extend_from_slice(&[bound[0], bound[2], bound[1],
bound[0], bound[2], bound[1], bound[0], bound[3], bound[2]]);
bound[0], bound[3], bound[2],
]);
return;
}
let xform2 = xform * self.barb_incr;
let g = xform2.transform(&self.base);
let (a0, a1) = self.verts.append_indexed(g);
self.faces.append(&mut util::parallel_zigzag2(bound.to_vec(), a0..a1));
self.barb(iters - 1, xform2, [a0, a0+1, a0+2, a0+3]);
}
pub fn main(&mut self, iters: usize, xform: Transform, bound: [usize; 4]) {
if self.depth_check(&xform, iters) {
self.faces.extend_from_slice(&[
bound[0], bound[2], bound[1],
bound[0], bound[3], bound[2],
]);
return; return;
} }
@ -144,134 +96,146 @@ impl BarbsCtxt {
self.barb(iters - 1, xform * self.sides[3], [bound[3], bound[0], a0+0, a0+3]); self.barb(iters - 1, xform * self.sides[3], [bound[3], bound[0], a0+0, a0+3]);
} }
pub fn run(mut self, iters: usize) -> Mesh { fn barb(&mut self, iters: usize, xform: Transform, bound: [usize; 4]) {
self.verts.append(&mut self.base.clone());
self.faces.extend_from_slice(&[
0, 1, 2,
0, 2, 3,
]);
self.main(iters, id(), [0,1,2,3]); if self.limit_check(&xform, iters) {
self.faces.extend_from_slice(&[bound[0], bound[2], bound[1],
bound[0], bound[3], bound[2]]);
return;
}
let xform2 = xform * self.barb_incr;
let g = xform2.transform(&self.base);
let (a0, a1) = self.verts.append_indexed(g);
self.faces.append(&mut util::parallel_zigzag2(bound.to_vec(), a0..a1));
self.barb(iters - 1, xform2, [a0, a0+1, a0+2, a0+3]);
}
}
pub struct RamHorn {
incr: Transform,
splits: [Transform; 4],
base: Vec<Vertex>,
trans: Vec<Vertex>,
verts: Vec<Vertex>,
faces: Vec<usize>,
depth: usize,
}
impl RamHorn {
pub fn new(f: f32, depth: usize) -> RamHorn {
// Incremental transform for each layer:
let v = Unit::new_normalize(Vector3::new(-1.0, 0.0, 1.0));
let incr: Transform = Transform::new().
translate(0.0, 0.0, 0.8 * f).
rotate(&v, 0.4 * f).
scale(1.0 - (1.0 - 0.95) * f);
// 'Base' vertices, used throughout:
let base = vec![
vertex(-0.5, -0.5, 0.0),
vertex(-0.5, 0.5, 0.0),
vertex( 0.5, 0.5, 0.0),
vertex( 0.5, -0.5, 0.0),
];
// 'Transition' vertices:
let trans = vec![
// Top edge midpoints:
vertex(-0.5, 0.0, 0.0), // 0 - connects b0-b1
vertex( 0.0, 0.5, 0.0), // 1 - connects b1-b2
vertex( 0.5, 0.0, 0.0), // 2 - connects b2-b3
vertex( 0.0, -0.5, 0.0), // 3 - connects b3-b0
// Top middle:
vertex( 0.0, 0.0, 0.0), // 4 - midpoint/centroid of all
];
// splits[i] gives transformation from a 'base' layer to the
// i'th split (0 to 3):
let mut splits: [Transform; 4] = [id(); 4];
for i in 0..4 {
let r = FRAC_PI_2 * (i as f32);
splits[i] = id().
rotate(&nalgebra::Vector3::z_axis(), r).
translate(0.25, 0.25, 0.0).
scale(0.5);
}
RamHorn {
incr: incr,
splits: splits,
base: base,
trans: trans,
verts: vec![],
faces: vec![],
depth: depth,
}
}
pub fn run(mut self) -> Mesh {
// Make seed vertices, use them for 'bottom' face, and recurse:
let xf = id().translate(0.0, 0.0, -0.5);
self.verts.append(&mut xf.transform(&self.base));
self.faces.extend_from_slice(&[0, 1, 2, 0, 2, 3]);
self.trans(id(), [0,1,2,3]);
return Mesh { return Mesh {
verts: self.verts, verts: self.verts,
faces: self.faces, faces: self.faces,
} }
} }
}
pub fn barbs(random: bool) -> Rule<()> { fn limit_check(&self, xform: &Transform) -> bool {
// Assume all scales are the same (for now)
let (s, _, _) = xform.get_scale();
return s < 0.005;
}
let (b0, bn); // 'Transition' stage (which splits from base to 4 parts):
let base_verts: Vec<VertexUnion> = vec_indexed![ fn trans(&mut self, xform: Transform, b: [usize; 4]) {
@b0 VertexUnion::Vertex(vertex(-0.5, -0.5, 0.0)),
VertexUnion::Vertex(vertex(-0.5, 0.5, 0.0)),
VertexUnion::Vertex(vertex( 0.5, 0.5, 0.0)),
VertexUnion::Vertex(vertex( 0.5, -0.5, 0.0)),
@bn,
];
let barb_incr = |random| { let (n, _) = self.verts.append_indexed(xform.transform(&self.base));
if random { let (m01, _) = self.verts.append_indexed(xform.transform(&self.trans));
let t = rand::thread_rng().gen_range(0.45, 0.55); let (m12, m23, m30, c) = (m01 + 1, m01 + 2, m01 + 3, m01 + 4);
let s = rand::thread_rng().gen_range(0.7, 0.9); self.faces.extend_from_slice(&[
let ry = rand::thread_rng().gen_range(-0.3, -0.1); // two faces straddling edge from vertex 0:
let rx = rand::thread_rng().gen_range(-0.04, 0.04); b[0], n+0, m01,
let rz = rand::thread_rng().gen_range(-0.04, 0.04); b[0], m30, n+0,
id().translate(0.0, 0.0, t). // two faces straddling edge from vertex 1:
rotate(&Vector3::y_axis(), ry). b[1], n+1, m12,
rotate(&Vector3::x_axis(), rx). b[1], m01, n+1,
rotate(&Vector3::z_axis(), rz). // two faces straddling edge from vertex 2:
scale(s) b[2], n+2, m23,
b[2], m12, n+2,
// two faces straddling edge from vertex 3:
b[3], n+3, m30,
b[3], m23, n+3,
// four faces from edge (0,1), (1,2), (2,3), (3,0):
b[0], m01, b[1],
b[1], m12, b[2],
b[2], m23, b[3],
b[3], m30, b[0],
]);
self.child(xform * self.splits[0], self.depth,[c, m12, n+2, m23]);
self.child(xform * self.splits[1], self.depth,[c, m01, n+1, m12]);
self.child(xform * self.splits[2], self.depth,[c, m30, n+0, m01]);
self.child(xform * self.splits[3], self.depth,[c, m23, n+3, m30]);
}
fn child(&mut self, xform: Transform, depth: usize, b: [usize; 4]) {
if self.limit_check(&xform) {
self.faces.extend_from_slice(&[b[0], b[2], b[1], b[0], b[3], b[2]]);
return;
}
if depth <= 0 {
self.trans(xform, b);
} else { } else {
id().translate(0.0, 0.0, 0.5). let xform2 = xform * self.incr;
rotate(&Vector3::y_axis(), -0.2). let (n0, n1) = self.verts.append_indexed(xform2.transform(&self.base));
scale(0.8) self.faces.append(&mut util::parallel_zigzag2(n0..n1, b.to_vec()));
self.child(xform2, depth - 1, [n0, n0 + 1, n0 + 2, n0 + 3]);
} }
};
let barb = rule_fn!(() => |self_, base_verts| {
let mut next_verts = base_verts;
let (a0, a1) = next_verts.append_indexed(vert_args(0..4));
let geom = util::parallel_zigzag(next_verts, b0..bn, a0..a1);
let final_geom = MeshFunc {
verts: vert_args(0..4),
faces: vec![ 0, 2, 1, 0, 3, 2 ],
};
let b = barb_incr(random);
RuleEval {
geom: Rc::new(geom.transform(&b)),
final_geom: Rc::new(final_geom), // no transform needed (no vertices)
children: vec![ child_iter!(self_, b, b0..bn) ],
} }
});
let main_barb_xf = |i| {
id().rotate(&Vector3::z_axis(), -FRAC_PI_2 * (i as f32)).
rotate(&Vector3::y_axis(), -FRAC_PI_2).
translate(0.5, 0.0, 0.5)
};
let main_incr = |random| {
if random {
//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);
id().translate(0.0, 0.0, 1.0).
rotate(&Vector3::z_axis(), rz).
rotate(&Vector3::x_axis(), rx).
scale(s)
} else {
id().translate(0.0, 0.0, 1.0).
rotate(&Vector3::z_axis(), 0.15).
rotate(&Vector3::x_axis(), 0.1).
scale(0.95)
}
};
let main = rule_fn!(() => |self_, base_verts| {
let mut next_verts = base_verts;
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![] };
// (unless recursion ends here, of course)
let final_geom = MeshFunc {
verts: vert_args(0..4),
faces: vec![ 0, 2, 1, 0, 3, 2 ],
};
RuleEval {
geom: Rc::new(geom),
final_geom: Rc::new(final_geom),
children: vec![
child_iter!(self_, main_incr(random), b0..bn),
child!(rule!(barb, ()), main_barb_xf(0), b0 + 0, b0 + 1, a0 + 1, a0 + 0),
child!(rule!(barb, ()), main_barb_xf(1), b0 + 1, b0 + 2, a0 + 2, a0 + 1),
child!(rule!(barb, ()), main_barb_xf(2), b0 + 2, b0 + 3, a0 + 3, a0 + 2),
child!(rule!(barb, ()), main_barb_xf(3), b0 + 3, b0 + 0, a0 + 0, a0 + 3),
// TODO: Factor out repetition?
],
}
});
let base = rule_fn!(() => |_s, base_verts| {
RuleEval {
geom: Rc::new(MeshFunc {
verts: base_verts,
faces: vec![ b0, b0 + 1, b0 + 2, b0, b0 + 2, b0 + 3 ],
}),
// TODO: This might be buggy and leave some vertices lying around
final_geom: Rc::new(prim::empty_meshfunc()),
children: vec![ child_iter!(rule!(main, ()), id(), b0..bn) ],
}
});
//rule!(Rc::new(base), ())
Rule { eval: base, ctxt: () }
} }
pub fn sierpinski() -> Rule<()> { pub fn sierpinski() -> Rule<()> {
@ -1012,160 +976,6 @@ pub fn ramhorn() -> Rule<()> {
Rule { eval: Rc::new(start), ctxt: () } Rule { eval: Rc::new(start), ctxt: () }
} }
#[derive(Copy, Clone)]
pub struct RamHornCtxt {
depth: usize,
}
pub fn ramhorn_branch(depth: usize, f: f32) -> Rule<RamHornCtxt> {
let v = Unit::new_normalize(Vector3::new(-1.0, 0.0, 1.0));
let incr: Transform = Transform::new().
translate(0.0, 0.0, 0.8 * f).
rotate(&v, 0.4 * f).
scale(1.0 - (1.0 - 0.95) * f);
let (a0, s0, sn);
let seed = vec_indexed![
@a0 VertexUnion::Arg(0),
VertexUnion::Arg(1),
VertexUnion::Arg(2),
VertexUnion::Arg(3),
@s0 VertexUnion::Vertex(vertex(-0.5, -0.5, 0.0)),
VertexUnion::Vertex(vertex(-0.5, 0.5, 0.0)),
VertexUnion::Vertex(vertex( 0.5, 0.5, 0.0)),
VertexUnion::Vertex(vertex( 0.5, -0.5, 0.0)),
@sn,
];
let geom = util::parallel_zigzag(seed.clone(), s0..sn, a0..s0).transform(&incr);
let final_geom = MeshFunc {
verts: seed.clone(),
faces: vec![
s0 + 0, s0 + 2, s0 + 1,
s0 + 0, s0 + 3, s0 + 2,
],
}.transform(&incr);
// TODO: Why is this redundant transform needed?
let opening_xform = |i| {
let r = FRAC_PI_2 * (i as f32);
Transform::new().
rotate(&nalgebra::Vector3::z_axis(), r).
translate(0.25, 0.25, 0.0).
scale(0.5)
};
// 'transition' geometry (when something splits):
let (v0, v1, v2, v3, m01, m12, m23, m30, mid);
let trans_verts = vec_indexed![
VertexUnion::Arg(0),
VertexUnion::Arg(1),
VertexUnion::Arg(2),
VertexUnion::Arg(3),
// 'Top' vertices:
@v0 VertexUnion::Vertex(vertex(-0.5, -0.5, 0.0)), // 0 (above 9)
@v1 VertexUnion::Vertex(vertex(-0.5, 0.5, 0.0)), // 1 (above 10)
@v2 VertexUnion::Vertex(vertex( 0.5, 0.5, 0.0)), // 2 (above 11)
@v3 VertexUnion::Vertex(vertex( 0.5, -0.5, 0.0)), // 3 (above 12)
// Top edge midpoints:
@m01 VertexUnion::Vertex(vertex(-0.5, 0.0, 0.0)), // 4 (connects 0-1)
@m12 VertexUnion::Vertex(vertex( 0.0, 0.5, 0.0)), // 5 (connects 1-2)
@m23 VertexUnion::Vertex(vertex( 0.5, 0.0, 0.0)), // 6 (connects 2-3)
@m30 VertexUnion::Vertex(vertex( 0.0, -0.5, 0.0)), // 7 (connects 3-0)
// Top middle:
@mid VertexUnion::Vertex(vertex( 0.0, 0.0, 0.0)), // 8
];
let trans_faces = vec![
// two faces straddling edge from vertex 0:
0, 4, 8,
0, 11, 4,
// two faces straddling edge from vertex 1:
1, 5, 9,
1, 8, 5,
// two faces straddling edge from vertex 2:
2, 6, 10,
2, 9, 6,
// two faces straddling edge from vertex 3:
3, 7, 11,
3, 10, 7,
// four faces from edge (0,1), (1,2), (2,3), (3,0):
0, 8, 1,
1, 9, 2,
2, 10, 3,
3, 11, 0,
];
let trans_geom = MeshFunc {
verts: trans_verts.clone(),
faces: trans_faces.clone(),
};
let trans_children = move |recur: RuleFn<RamHornCtxt>, ctxt: RamHornCtxt| {
vec![
child!(rule!(recur, ctxt), opening_xform(0), m12, v2, m23, mid),
child!(rule!(recur, ctxt), opening_xform(1), m01, v1, m12, mid),
child!(rule!(recur, ctxt), opening_xform(2), m30, v0, m01, mid),
child!(rule!(recur, ctxt), opening_xform(3), m23, v3, m30, mid),
// TODO: These vertex mappings appear to be right.
// Explain *why* they are right.
]
};
let tg = Rc::new(trans_geom);
let fg = Rc::new(final_geom);
let g = Rc::new(geom);
// TODO: Why is that necessary?
let recur = rule_fn!(RamHornCtxt => |self_, tg| {
if self_.ctxt.depth <= 0 {
RuleEval {
geom: tg,
final_geom: fg.clone(),
// This final_geom will leave midpoint/centroid
// vertices, but stopping here means none are
// connected anyway - so they can just be ignored.
children: trans_children(self_.eval.clone(), RamHornCtxt { depth }),
}
} else {
let next_rule = Rule {
eval: self_.eval.clone(),
ctxt: RamHornCtxt { depth: self_.ctxt.depth - 1 },
};
RuleEval {
geom: g.clone(),
final_geom: fg.clone(),
children: vec![
child!(Rc::new(next_rule), incr, s0, s0+1, s0+2, s0+3),
],
}
}
});
let trans = rule_fn!(RamHornCtxt => |self_| {
RuleEval {
geom: tg.clone(),
final_geom: Rc::new(prim::empty_mesh().to_meshfunc()),
children: trans_children(recur.clone(), self_.ctxt),
}
});
let start = rule_fn!(RamHornCtxt => |self_, seed| {
let g = MeshFunc {
verts: seed[s0..sn].to_vec(),
// FIXME (use names for indices)
faces: vec![
0, 1, 2,
0, 2, 3,
],
}.transform(&id().translate(0.0, 0.0, -0.5));
RuleEval {
geom: Rc::new(g),
final_geom: Rc::new(prim::empty_mesh().to_meshfunc()),
children: vec![
child!(rule!(trans, self_.ctxt), id(), 0, 1, 2, 3),
],
}
});
Rule { eval: start, ctxt: RamHornCtxt { depth } }
}
pub fn test_parametric() -> Mesh { pub fn test_parametric() -> Mesh {
let base_verts: Vec<Vertex> = vec![ let base_verts: Vec<Vertex> = vec![

View File

@ -69,18 +69,10 @@ mod tests {
// TODO: These tests don't test any conditions, so this is useful // TODO: These tests don't test any conditions, so this is useful
// short-hand to run, but not very meaningful as a test. // short-hand to run, but not very meaningful as a test.
#[test] #[test]
fn cube_thing() { fn barbs() {
run_test(examples::cube_thing(), 3, "cube_thing3", false); let name = "barbs";
}
#[test]
fn barbs() { run_test(examples::barbs(false), 80, "barbs", false); }
#[test]
fn barbs_direct() {
let name = "barbs_test";
println!("---------------------------------------------------"); println!("---------------------------------------------------");
let b = examples::BarbsCtxt::new(); let b = examples::Barbs::new();
let m = b.run(100); let m = b.run(100);
println!("Got {} verts...", m.verts.len()); println!("Got {} verts...", m.verts.len());
@ -92,7 +84,19 @@ mod tests {
} }
#[test] #[test]
fn barbs_random() { run_test(examples::barbs(true), 80, "barbs_random", false); } fn ramhorn_branch() {
let name = "ramhorn_branch";
println!("---------------------------------------------------");
let b = examples::RamHorn::new(0.6, 12);
let m = b.run();
println!("Got {} verts...", m.verts.len());
let fname = format!("{}.stl", name);
println!("Writing {}...", fname);
m.write_stl_file(&fname).unwrap();
}
#[test] #[test]
fn sierpinski() { run_test(examples::sierpinski(), 6, "sierpinski", false); } fn sierpinski() { run_test(examples::sierpinski(), 6, "sierpinski", false); }
@ -143,11 +147,6 @@ mod tests {
run_test(examples::ramhorn(), 100, "ram_horn3", false); run_test(examples::ramhorn(), 100, "ram_horn3", false);
} }
#[test]
fn ramhorn_branch() {
run_test(examples::ramhorn_branch(12, 0.6), 64, "ram_horn_branch", false);
}
/* /*
#[test] #[test]
fn ramhorn_branch_random() { fn ramhorn_branch_random() {