Made an actual working example!
This commit is contained in:
parent
83053ca8bb
commit
7a6d29da81
@ -10,8 +10,6 @@ use crate::scratch;
|
|||||||
|
|
||||||
fn cube_thing() -> Rule {
|
fn cube_thing() -> Rule {
|
||||||
|
|
||||||
let mesh = prim::cube();
|
|
||||||
|
|
||||||
// Quarter-turn in radians:
|
// Quarter-turn in radians:
|
||||||
let qtr = std::f32::consts::FRAC_PI_2;
|
let qtr = std::f32::consts::FRAC_PI_2;
|
||||||
|
|
||||||
@ -28,24 +26,42 @@ fn cube_thing() -> Rule {
|
|||||||
geometry::Rotation3::from_axis_angle(z, -qtr).to_homogeneous(),
|
geometry::Rotation3::from_axis_angle(z, -qtr).to_homogeneous(),
|
||||||
];
|
];
|
||||||
|
|
||||||
let rec = || -> RuleEval {
|
let rec = move |self_: Rc<Rule>| -> RuleEval {
|
||||||
let gen_rulestep = |rot: &Mat4| -> Child {
|
let gen_rulestep = |rot: &Mat4| -> Child {
|
||||||
let m: Mat4 = rot *
|
let m: Mat4 = rot *
|
||||||
Matrix4::new_scaling(0.5) *
|
Matrix4::new_scaling(0.5) *
|
||||||
geometry::Translation3::new(6.0, 0.0, 0.0).to_homogeneous();
|
geometry::Translation3::new(6.0, 0.0, 0.0).to_homogeneous();
|
||||||
Child {
|
Child {
|
||||||
rule: Rule { eval: Box::new(rec) },
|
rule: self_.clone(),
|
||||||
xf: m,
|
xf: m,
|
||||||
vmap: vec![],
|
vmap: vec![],
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
RuleEval {
|
RuleEval {
|
||||||
geom: mesh.clone(),
|
geom: prim::cube(),
|
||||||
final_geom: prim::empty_mesh(),
|
final_geom: prim::empty_mesh(),
|
||||||
children: turns.iter().map(gen_rulestep).collect(),
|
children: turns.iter().map(gen_rulestep).collect(),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
// I can't really do *mutual* recursion with the above, can I? I'd
|
||||||
|
// need actual functions for that.
|
||||||
|
|
||||||
|
// Also: 'turns' above is a problem. I can't clone it *inside* the
|
||||||
|
// closure because it doesn't live long enough (it is out of scope
|
||||||
|
// when the closure runs). I can't move it, or Fn becomes
|
||||||
|
// FnOnce. It doesn't implement Copy, and really can't, because it
|
||||||
|
// has vectors inside.
|
||||||
|
//
|
||||||
|
// (I guess I could use Rc<OpenMesh> instead if I want cheap
|
||||||
|
// support for Copy. Or... just put prim::cube() right inside.)
|
||||||
|
//
|
||||||
|
// what did I learn from this? That "constants" outside the
|
||||||
|
// closure only work the way I think they should work if:
|
||||||
|
// - they're actually static
|
||||||
|
// - they implement Copy
|
||||||
|
// - the closure can move them
|
||||||
|
|
||||||
Rule { eval: Box::new(rec) }
|
Rule { eval: Box::new(rec) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -494,6 +510,15 @@ pub fn main() {
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
fn run_test_iter(r: &Rc<Rule>, iters: usize, name: &str) {
|
||||||
|
println!("Running {}...", name);
|
||||||
|
let (mesh, nodes) = Rule::to_mesh_iter(r.clone(), iters);
|
||||||
|
println!("Evaluated {} rules", nodes);
|
||||||
|
let fname = format!("{}.stl", name);
|
||||||
|
println!("Writing {}...", fname);
|
||||||
|
mesh.write_stl_file(&fname).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
run_test(CubeThing::init(), Rule { eval: CubeThing::rec }, 3, "cube_thing");
|
run_test(CubeThing::init(), Rule { eval: CubeThing::rec }, 3, "cube_thing");
|
||||||
// this can't work on its own because the resultant OpenMesh still
|
// this can't work on its own because the resultant OpenMesh still
|
||||||
@ -516,6 +541,11 @@ pub fn main() {
|
|||||||
// let f = 20;
|
// let f = 20;
|
||||||
// run_test_iter(Twist::init(f as f32, 32), 100*f, "twist2");
|
// run_test_iter(Twist::init(f as f32, 32), 100*f, "twist2");
|
||||||
|
|
||||||
|
let rule = Rc::new(cube_thing());
|
||||||
|
|
||||||
|
run_test_iter(&rule, 3, "cube_thing3");
|
||||||
|
|
||||||
|
if false
|
||||||
{
|
{
|
||||||
let a = vec![1,2,3];
|
let a = vec![1,2,3];
|
||||||
|
|
||||||
|
|||||||
17
src/rule.rs
17
src/rule.rs
@ -1,5 +1,6 @@
|
|||||||
use crate::openmesh::{OpenMesh, Tag, Mat4};
|
use crate::openmesh::{OpenMesh, Tag, Mat4};
|
||||||
//use crate::prim;
|
//use crate::prim;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
/// Definition of a rule. In general, a `Rule`:
|
/// Definition of a rule. In general, a `Rule`:
|
||||||
///
|
///
|
||||||
@ -7,7 +8,7 @@ use crate::openmesh::{OpenMesh, Tag, Mat4};
|
|||||||
/// - tells what other rules to invoke, and what to do with their
|
/// - tells what other rules to invoke, and what to do with their
|
||||||
/// geometry
|
/// geometry
|
||||||
pub struct Rule {
|
pub struct Rule {
|
||||||
pub eval: Box<dyn Fn() -> RuleEval>,
|
pub eval: Box<dyn Fn(Rc<Rule>) -> RuleEval>,
|
||||||
}
|
}
|
||||||
// TODO: It may be possible to have just a 'static' rule that requires
|
// TODO: It may be possible to have just a 'static' rule that requires
|
||||||
// no function call.
|
// no function call.
|
||||||
@ -51,7 +52,7 @@ pub struct RuleEval {
|
|||||||
pub struct Child {
|
pub struct Child {
|
||||||
|
|
||||||
/// Rule to evaluate to produce geometry
|
/// Rule to evaluate to produce geometry
|
||||||
pub rule: Rule,
|
pub rule: Rc<Rule>,
|
||||||
|
|
||||||
/// The transform to apply to all geometry produced by `rule`
|
/// The transform to apply to all geometry produced by `rule`
|
||||||
/// (including its own `geom` and `final_geom` if needed, as well
|
/// (including its own `geom` and `final_geom` if needed, as well
|
||||||
@ -79,11 +80,11 @@ impl Rule {
|
|||||||
/// Convert this `Rule` to mesh data, recursively (depth first).
|
/// Convert this `Rule` to mesh data, recursively (depth first).
|
||||||
/// `iters_left` sets the maximum recursion depth. This returns
|
/// `iters_left` sets the maximum recursion depth. This returns
|
||||||
/// (geometry, number of rule evaluations).
|
/// (geometry, number of rule evaluations).
|
||||||
pub fn to_mesh(&self, iters_left: u32) -> (OpenMesh, usize) {
|
pub fn to_mesh(s: Rc<Self>, iters_left: u32) -> (OpenMesh, usize) {
|
||||||
|
|
||||||
let mut evals = 1;
|
let mut evals = 1;
|
||||||
|
|
||||||
let rs: RuleEval = (self.eval)();
|
let rs: RuleEval = (s.eval)(s.clone());
|
||||||
if iters_left <= 0 {
|
if iters_left <= 0 {
|
||||||
return (rs.final_geom, 1);
|
return (rs.final_geom, 1);
|
||||||
// TODO: This is probably wrong because of the way that
|
// TODO: This is probably wrong because of the way that
|
||||||
@ -96,7 +97,7 @@ impl Rule {
|
|||||||
|
|
||||||
let subgeom: Vec<(OpenMesh, &Vec<usize>)> = rs.children.iter().map(|sub| {
|
let subgeom: Vec<(OpenMesh, &Vec<usize>)> = rs.children.iter().map(|sub| {
|
||||||
// Get sub-geometry (still un-transformed):
|
// Get sub-geometry (still un-transformed):
|
||||||
let (submesh, eval) = sub.rule.to_mesh(iters_left - 1);
|
let (submesh, eval) = Rule::to_mesh(sub.rule.clone(), iters_left - 1);
|
||||||
// Tally up eval count:
|
// Tally up eval count:
|
||||||
evals += eval;
|
evals += eval;
|
||||||
|
|
||||||
@ -112,7 +113,7 @@ impl Rule {
|
|||||||
/// This should be identical to to_mesh, but implemented
|
/// This should be identical to to_mesh, but implemented
|
||||||
/// iteratively with an explicit stack rather than with recursive
|
/// iteratively with an explicit stack rather than with recursive
|
||||||
/// function calls.
|
/// function calls.
|
||||||
pub fn to_mesh_iter(&self, max_depth: usize) -> (OpenMesh, usize) {
|
pub fn to_mesh_iter(s: Rc<Self>, max_depth: usize) -> (OpenMesh, usize) {
|
||||||
|
|
||||||
struct State {
|
struct State {
|
||||||
// The set of rules we're currently handling:
|
// The set of rules we're currently handling:
|
||||||
@ -133,7 +134,7 @@ impl Rule {
|
|||||||
// (usually because they involve multiple rules).
|
// (usually because they involve multiple rules).
|
||||||
//
|
//
|
||||||
// We evaluate our own rule to initialize the stack:
|
// We evaluate our own rule to initialize the stack:
|
||||||
let eval = (self.eval)();
|
let eval = (s.eval)(s.clone());
|
||||||
let mut stack: Vec<State> = vec![State {
|
let mut stack: Vec<State> = vec![State {
|
||||||
rules: eval.children,
|
rules: eval.children,
|
||||||
next: 0,
|
next: 0,
|
||||||
@ -162,7 +163,7 @@ impl Rule {
|
|||||||
|
|
||||||
// Evaluate the rule:
|
// Evaluate the rule:
|
||||||
let child = &s.rules[s.next];
|
let child = &s.rules[s.next];
|
||||||
let mut eval = (child.rule.eval)();
|
let mut eval = (child.rule.eval)(child.rule.clone());
|
||||||
eval_count += 1;
|
eval_count += 1;
|
||||||
|
|
||||||
// Make an updated world transform:
|
// Make an updated world transform:
|
||||||
|
|||||||
177
src/scratch.rs
177
src/scratch.rs
@ -118,3 +118,180 @@ impl Foo5 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is valid and I can recurse:
|
||||||
|
struct W {
|
||||||
|
b: Box<dyn Fn() -> W>,
|
||||||
|
}
|
||||||
|
struct Foo6 {}
|
||||||
|
impl Foo6 {
|
||||||
|
fn fn1(s: &Rc<Self>) -> W {
|
||||||
|
let s2 = Rc::clone(&s);
|
||||||
|
W { b: Box::new(move || Self::fn1(&s2)) }
|
||||||
|
}
|
||||||
|
fn fn2(s: &Rc<Self>) -> (W, W) {
|
||||||
|
let s2 = Rc::clone(&s);
|
||||||
|
let w2 = W { b: Box::new(move || Self::fn1(&s2)) };
|
||||||
|
let s3 = Rc::clone(&s);
|
||||||
|
let w3 = W { b: Box::new(move || Self::fn1(&s3)) };
|
||||||
|
(w2, w3)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn foo6() {
|
||||||
|
|
||||||
|
// Whatever (note that it doesn't automatically do Copy):
|
||||||
|
struct State {
|
||||||
|
v: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Purposely put state somewhere it goes out of scope:
|
||||||
|
let s = {
|
||||||
|
let s_orig = State {
|
||||||
|
v: 105,
|
||||||
|
};
|
||||||
|
Rc::new(s_orig)
|
||||||
|
};
|
||||||
|
/*
|
||||||
|
let fn1 = |f: &dyn Fn(&dyn Fn() -> W) -> (&dyn Fn() -> W)| -> (&dyn Fn() -> W) {
|
||||||
|
&(|| -> W {
|
||||||
|
let s2 = Rc::clone(&s);
|
||||||
|
W { b: Box::new(move || f(f)) }
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
let f2 = fn1(fn1);
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
fn foo7(t: impl Clone) -> impl Clone {
|
||||||
|
t.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn foo7b<T: Clone>(t: T) -> T {
|
||||||
|
t.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn foo7c<T>(t: T) -> T where T: Clone {
|
||||||
|
t.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
// A simple implementation of the Y Combinator
|
||||||
|
// λf.(λx.xx)(λx.f(xx))
|
||||||
|
// <=> λf.(λx.f(xx))(λx.f(xx))
|
||||||
|
|
||||||
|
// CREDITS: A better version of the previous code that was posted here, with detailed explanation.
|
||||||
|
// See <y> and also <y_apply>.
|
||||||
|
|
||||||
|
// A function type that takes its own type as an input is an infinite recursive type.
|
||||||
|
// We introduce a trait that will allow us to have an input with the same type as self, and break the recursion.
|
||||||
|
// The input is going to be a trait object that implements the desired function in the interface.
|
||||||
|
// NOTE: We will be coercing a reference to a closure into this trait object.
|
||||||
|
|
||||||
|
trait Apply<T, R> {
|
||||||
|
fn apply(&self, f: &dyn Apply<T, R>, t: T) -> R;
|
||||||
|
}
|
||||||
|
|
||||||
|
// In Rust, closures fall into three kinds: FnOnce, FnMut and Fn.
|
||||||
|
// FnOnce assumed to be able to be called just once if it is not Clone. It is impossible to
|
||||||
|
// write recursive FnOnce that is not Clone.
|
||||||
|
// All FnMut are also FnOnce, although you can call them multiple times, they are not allow to
|
||||||
|
// have a reference to themselves. So it is also not possible to write recursive FnMut closures
|
||||||
|
// that is not Clone.
|
||||||
|
// All Fn are also FnMut, and all closures of Fn are also Clone. However, programmers can create
|
||||||
|
// Fn objects that are not Clone
|
||||||
|
|
||||||
|
// This will work for all Fn objects, not just closures
|
||||||
|
// And it is a little bit more efficient for Fn closures as it do not clone itself.
|
||||||
|
impl<T, R, F> Apply<T, R> for F where F:
|
||||||
|
Fn(&dyn Apply<T, R>, T) -> R
|
||||||
|
{
|
||||||
|
fn apply(&self, f: &dyn Apply<T, R>, t: T) -> R {
|
||||||
|
self(f, t)
|
||||||
|
|
||||||
|
// NOTE: Each letter is an individual symbol.
|
||||||
|
// (λx.(λy.xxy))(λx.(λy.f(λz.xxz)y))t
|
||||||
|
// => (λx.xx)(λx.f(xx))t
|
||||||
|
// => (Yf)t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This works for all closures that is Clone, and those are Fn.
|
||||||
|
// impl<T, R, F> Apply<T, R> for F where F: FnOnce( &Apply<T, R>, T ) -> R + Clone {
|
||||||
|
// fn apply( &self, f: &Apply<T, R>, t: T ) -> R {
|
||||||
|
// (self.clone())( f, t )
|
||||||
|
|
||||||
|
// // If we were to pass in self as f, we get -
|
||||||
|
// // NOTE: Each letter is an individual symbol.
|
||||||
|
// // λf.λt.sft
|
||||||
|
// // => λs.λt.sst [s/f]
|
||||||
|
// // => λs.ss
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Before 1.26 we have some limitations and so we need some workarounds. But now impl Trait is stable and we can
|
||||||
|
// write the following:
|
||||||
|
|
||||||
|
fn y<T,R>(f:impl Fn(&dyn Fn(T) -> R, T) -> R) -> impl Fn(T) -> R {
|
||||||
|
move |t| (
|
||||||
|
|x: &dyn Apply<T,R>, y| x.apply(x, y)
|
||||||
|
) (
|
||||||
|
&|x: &dyn Apply<T,R>, y| f(
|
||||||
|
&|z| x.apply(x,z),
|
||||||
|
y
|
||||||
|
),
|
||||||
|
t
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// fn y<T,R>(f:impl FnOnce(&Fn(T) -> R, T) -> R + Clone) -> impl FnOnce(T) -> R {
|
||||||
|
// |t| (|x: &Apply<T,R>,y| x.apply(x,y))
|
||||||
|
// (&move |x:&Apply<T,R>,y| f(&|z| x.apply(x,z), y), t)
|
||||||
|
|
||||||
|
// // NOTE: Each letter is an individual symbol.
|
||||||
|
// // (λx.(λy.xxy))(λx.(λy.f(λz.xxz)y))t
|
||||||
|
// // => (λx.xx)(λx.f(xx))t
|
||||||
|
// // => (Yf)t
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Previous version removed as they are just hacks when impl Trait is not available.
|
||||||
|
|
||||||
|
fn fac(n: usize) -> usize {
|
||||||
|
let almost_fac = |f: &dyn Fn(usize) -> usize, x|
|
||||||
|
if x == 0 {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
x * f(x - 1)
|
||||||
|
}
|
||||||
|
;
|
||||||
|
let fac = y( almost_fac );
|
||||||
|
fac(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fib( n: usize ) -> usize {
|
||||||
|
let almost_fib = |f: &dyn Fn(usize) -> usize, x|
|
||||||
|
if x < 2 {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
f(x - 2) + f(x - 1)
|
||||||
|
};
|
||||||
|
let fib = y(almost_fib);
|
||||||
|
fib(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn optimal_fib( n: usize ) -> usize {
|
||||||
|
let almost_fib = |f: &dyn Fn((usize,usize,usize)) -> usize, (i0,i1,x)|
|
||||||
|
match x {
|
||||||
|
0 => i0,
|
||||||
|
1 => i1,
|
||||||
|
x => f((i1,i0+i1, x-1))
|
||||||
|
}
|
||||||
|
;
|
||||||
|
let fib = |x| y(almost_fib)((1,1,x));
|
||||||
|
fib(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_y() {
|
||||||
|
println!("{}", fac(10));
|
||||||
|
println!("{}", fib(10));
|
||||||
|
println!("{}", optimal_fib(10));
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user