Remove lots of dead code. Convert Sierpinski example.
This commit is contained in:
parent
5603caa3c1
commit
e7206f4598
@ -88,8 +88,9 @@ also was vastly faster at generating meshes.
|
|||||||
|
|
||||||
## Highest priority:
|
## Highest priority:
|
||||||
|
|
||||||
- Begin converting older examples.
|
- Work on abstraction/composition. Particularly: factor out
|
||||||
- Trash all the dead code.
|
patterns I use, and be able to *compose* procedural meshes
|
||||||
|
somehow - e.g. the 'context' object I discussed.
|
||||||
- Docs on modules
|
- Docs on modules
|
||||||
- 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
|
||||||
|
|||||||
752
src/dcel.rs
752
src/dcel.rs
@ -1,752 +0,0 @@
|
|||||||
use std::fmt;
|
|
||||||
|
|
||||||
use crate::mesh::{Mesh};
|
|
||||||
use crate::xform::{Vertex};
|
|
||||||
|
|
||||||
/// Doubly-connected edge list mesh (or a half-edge mesh),
|
|
||||||
/// parametrized over some vertex type.
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct DCELMesh<V: Copy> {
|
|
||||||
pub verts: Vec<DCELVertex<V>>,
|
|
||||||
pub faces: Vec<DCELFace>,
|
|
||||||
pub halfedges: Vec<DCELHalfEdge>,
|
|
||||||
|
|
||||||
pub num_verts: usize,
|
|
||||||
pub num_faces: usize,
|
|
||||||
pub num_halfedges: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A vertex of a mesh, combined with an arbitrary half-edge that has
|
|
||||||
/// this vertex as its origin. This is always relative to some parent
|
|
||||||
/// Mesh<V>.
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct DCELVertex<V> {
|
|
||||||
/// The vertex itself.
|
|
||||||
pub v: V,
|
|
||||||
/// A half-edge (given as an index into 'halfedges');
|
|
||||||
/// arbitrary, but `mesh.halfedges[halfedge] = v` must be true
|
|
||||||
pub halfedge: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A face, given as a half-edge that lies on its boundary (and must
|
|
||||||
/// traverse it counter-clockwise). This is always relative to some
|
|
||||||
/// parent Mesh<V>, as in Vertex.
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct DCELFace {
|
|
||||||
/// A boundary half-edge of this face (given as an index into
|
|
||||||
/// 'halfedges').
|
|
||||||
pub halfedge: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A half-edge, given in terms of its origin vertex, the face that the
|
|
||||||
/// half-edge lies on the boundary of, its optional "twin" half-edge that
|
|
||||||
/// lies on an adjacent face, and previous and next half-edges (to
|
|
||||||
/// traverse the boundaries of the face). This is always relative to
|
|
||||||
/// some parent Mesh<V>, as in Vertex and Face.
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct DCELHalfEdge {
|
|
||||||
/// Origin vertex (given as an index into 'verts')
|
|
||||||
pub vert: usize,
|
|
||||||
/// Face this half-edge lies on the boundary of (given as an index
|
|
||||||
/// into 'faces')
|
|
||||||
pub face: usize,
|
|
||||||
/// If false, ignore twin_halfedge. (If this is true, then it must
|
|
||||||
/// also be true for the twin.)
|
|
||||||
pub has_twin: bool,
|
|
||||||
/// The twin half-edge (given as an index into 'halfedges').
|
|
||||||
/// The twin of the twin must point back to this HalfEdge.
|
|
||||||
pub twin_halfedge: usize,
|
|
||||||
/// The next half-edge on the boundary (given as an index into
|
|
||||||
/// 'halfedges'). 'prev_halfedge' of this half-edge must point
|
|
||||||
/// back to this same HalfEdge. Repeatedly following 'next_halfedge'
|
|
||||||
/// must also lead back to this same HalfEdge.
|
|
||||||
pub next_halfedge: usize,
|
|
||||||
/// The previous half-edge on the boundary (given as an index into
|
|
||||||
/// 'halfedges'). 'next_halfedge' of this half-edge must point
|
|
||||||
/// back to this HalfEdge. Repeatedly following 'prev_halfedge'
|
|
||||||
/// must also lead back to this same HalfEdge.
|
|
||||||
pub prev_halfedge: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum VertSpec<V> {
|
|
||||||
New(V),
|
|
||||||
Idx(usize),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: Copy + std::fmt::Debug> fmt::Display for DCELMesh<V> {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
|
|
||||||
let v_strs: Vec<String> = self.verts.iter().enumerate().map(|(i,v)| {
|
|
||||||
format!("V{}=e{} {:?}", i, v.halfedge, v.v)
|
|
||||||
}).collect();
|
|
||||||
let v_str = v_strs.join(",");
|
|
||||||
|
|
||||||
let f_strs: Vec<String> = self.faces.iter().enumerate().map(|(i,f)| {
|
|
||||||
format!("F{}=e{}", i, f.halfedge)
|
|
||||||
}).collect();
|
|
||||||
let f_str = f_strs.join(", ");
|
|
||||||
|
|
||||||
let e_strs: Vec<String> = self.halfedges.iter().enumerate().map(|(i,h)| {
|
|
||||||
let twin = if h.has_twin {
|
|
||||||
format!(" tw{}", h.twin_halfedge)
|
|
||||||
} else {
|
|
||||||
String::from("")
|
|
||||||
};
|
|
||||||
format!("E{}=v{} f{}{} n{} p{}", i, h.vert, h.face, twin, h.next_halfedge, h.prev_halfedge)
|
|
||||||
}).collect();
|
|
||||||
let e_str = e_strs.join(", ");
|
|
||||||
|
|
||||||
write!(f, "DCELMesh({} verts, {}; {} faces, {}; {} halfedges, {})",
|
|
||||||
self.num_verts, v_str,
|
|
||||||
self.num_faces, f_str,
|
|
||||||
self.num_halfedges, e_str)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: Copy + std::fmt::Debug> DCELMesh<V> {
|
|
||||||
|
|
||||||
pub fn new() -> DCELMesh<V> {
|
|
||||||
DCELMesh {
|
|
||||||
verts: vec![],
|
|
||||||
faces: vec![],
|
|
||||||
halfedges: vec![],
|
|
||||||
num_verts: 0,
|
|
||||||
num_faces: 0,
|
|
||||||
num_halfedges: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn print(&self) {
|
|
||||||
|
|
||||||
println!("DCELMesh has {} verts, {} faces, {} halfedges:",
|
|
||||||
self.num_verts,
|
|
||||||
self.num_faces,
|
|
||||||
self.num_halfedges);
|
|
||||||
|
|
||||||
for (i,v) in self.verts.iter().enumerate() {
|
|
||||||
println!("Vert {}: halfedge {}, {:?}", i, v.halfedge, v.v);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i,f) in self.faces.iter().enumerate() {
|
|
||||||
println!("Face {}: halfedge {} (halfedges {:?}, verts {:?})",
|
|
||||||
i, f.halfedge, self.face_to_halfedges(i), self.face_to_verts(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i,h) in self.halfedges.iter().enumerate() {
|
|
||||||
let twin = if h.has_twin { format!(", twin half-edge {}", h.twin_halfedge)
|
|
||||||
} else {
|
|
||||||
format!(", no twin")
|
|
||||||
};
|
|
||||||
let v1 = self.verts[h.vert].v;
|
|
||||||
let v2 = self.verts[self.halfedges[h.next_halfedge].vert].v;
|
|
||||||
println!("Halfedge {}: vert {} (to {}), face {}, prev: {}, next: {}{}, ({:?} to {:?})",
|
|
||||||
i, h.vert, self.halfedges[h.next_halfedge].vert, h.face,
|
|
||||||
h.prev_halfedge, h.next_halfedge, twin,
|
|
||||||
v1, v2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Runs various checks on the mesh. This will return true if the mesh
|
|
||||||
/// looks okay, and otherwise false. It will print messages as it
|
|
||||||
/// runs.
|
|
||||||
pub fn check(&self) -> bool {
|
|
||||||
let mut pass = true;
|
|
||||||
|
|
||||||
if self.num_halfedges != self.halfedges.len() {
|
|
||||||
pass = false;
|
|
||||||
println!("self.num_halfedges={} != self.halfedges.len()={}",
|
|
||||||
self.num_halfedges, self.halfedges.len());
|
|
||||||
} else {
|
|
||||||
println!("self.num_halfedges matches self.halfedges.len()");
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.num_faces != self.faces.len() {
|
|
||||||
pass = false;
|
|
||||||
println!("self.faces={} != self.faces.len()={}",
|
|
||||||
self.num_faces, self.faces.len());
|
|
||||||
} else {
|
|
||||||
println!("self.num_faces matches self.faces.len()");
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.num_verts != self.verts.len() {
|
|
||||||
pass = false;
|
|
||||||
println!("self.verts={} != self.verts.len()={}",
|
|
||||||
self.num_verts, self.verts.len());
|
|
||||||
} else {
|
|
||||||
println!("self.num_verts matches self.verts.len()");
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i,v) in self.verts.iter().enumerate() {
|
|
||||||
if v.halfedge >= self.halfedges.len() {
|
|
||||||
println!("Vertex {}: halfedge index {} out of range",
|
|
||||||
i, v.halfedge);
|
|
||||||
pass = false;
|
|
||||||
}
|
|
||||||
if self.halfedges[v.halfedge].vert != i {
|
|
||||||
println!("Vertex {} names halfedge {}, which has a different origin vertex ({})",
|
|
||||||
i, v.halfedge, self.halfedges[v.halfedge].vert);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i,edge) in self.halfedges.iter().enumerate() {
|
|
||||||
if edge.vert >= self.verts.len() {
|
|
||||||
println!("Halfedge {}: vertex index {} out of range", i, edge.vert);
|
|
||||||
pass = false;
|
|
||||||
}
|
|
||||||
if edge.has_twin {
|
|
||||||
let twin = &self.halfedges[edge.twin_halfedge];
|
|
||||||
if !twin.has_twin {
|
|
||||||
println!("Halfedge {}: twin {} says it has no twin",
|
|
||||||
i, edge.twin_halfedge);
|
|
||||||
pass = false;
|
|
||||||
} else if i != twin.twin_halfedge {
|
|
||||||
println!("Halfedge {} has twin {}, but reverse isn't true",
|
|
||||||
i, edge.twin_halfedge);
|
|
||||||
pass = false;
|
|
||||||
} else if edge.vert != self.halfedges[twin.next_halfedge].vert {
|
|
||||||
println!("Halfedge {} starts at vertex {} but twin {} ends at vertex {}",
|
|
||||||
i, edge.vert, edge.twin_halfedge, self.halfedges[twin.next_halfedge].vert);
|
|
||||||
pass = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let p = edge.prev_halfedge;
|
|
||||||
if p >= self.halfedges.len() {
|
|
||||||
println!("Halfedge {}: previous halfedge index {} out of range",
|
|
||||||
i, p);
|
|
||||||
pass = false;
|
|
||||||
}
|
|
||||||
let n = edge.next_halfedge;
|
|
||||||
if p >= self.halfedges.len() {
|
|
||||||
println!("Halfedge {}: next halfedge index {} out of range",
|
|
||||||
i, n);
|
|
||||||
pass = false;
|
|
||||||
}
|
|
||||||
let pn = self.halfedges[p].next_halfedge;
|
|
||||||
if pn != i {
|
|
||||||
println!("Halfedge {}: previous halfedge {} has next halfedge of {}, not {}",
|
|
||||||
i, p, pn, i);
|
|
||||||
pass = false;
|
|
||||||
}
|
|
||||||
let np = self.halfedges[n].prev_halfedge;
|
|
||||||
if np != i {
|
|
||||||
println!("Halfedge {}: next halfedge {} has previous halfedge of {}, not {}",
|
|
||||||
i, n, np, i);
|
|
||||||
pass = false;
|
|
||||||
}
|
|
||||||
// TODO: Check that following prev always leads back to start
|
|
||||||
// likewise following next
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i,face) in self.faces.iter().enumerate() {
|
|
||||||
if face.halfedge >= self.halfedges.len() {
|
|
||||||
println!("Face {}: halfedge index {} out of range",
|
|
||||||
i, face.halfedge);
|
|
||||||
pass = false;
|
|
||||||
}
|
|
||||||
let face2 = self.halfedges[face.halfedge].face;
|
|
||||||
if i != face2 {
|
|
||||||
println!("Face {} gives boundary halfedge {}, which gives different face ({})",
|
|
||||||
i, face.halfedge, face2);
|
|
||||||
pass = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Check that face never visits same vertex twice?
|
|
||||||
// This might belong in halfedge checking
|
|
||||||
}
|
|
||||||
|
|
||||||
if pass {
|
|
||||||
println!("Mesh OK")
|
|
||||||
} else {
|
|
||||||
println!("Mesh has errors!")
|
|
||||||
}
|
|
||||||
|
|
||||||
pass
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn face_to_halfedges(&self, face_idx: usize) -> Vec<usize> {
|
|
||||||
let mut edges: Vec<usize> = vec![];
|
|
||||||
let start_idx = self.faces[face_idx].halfedge;
|
|
||||||
edges.push(start_idx);
|
|
||||||
|
|
||||||
let mut idx = self.halfedges[start_idx].next_halfedge;
|
|
||||||
while start_idx != idx {
|
|
||||||
edges.push(idx);
|
|
||||||
idx = self.halfedges[idx].next_halfedge;
|
|
||||||
}
|
|
||||||
return edges;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn face_to_verts(&self, face_idx: usize) -> Vec<usize> {
|
|
||||||
self.face_to_halfedges(face_idx).iter().map(|e| {
|
|
||||||
self.halfedges[*e].vert
|
|
||||||
}).collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adds a face that shares no edges with anything else in the mesh.
|
|
||||||
/// Returns (face index, half-edge indices); half-edge indices are
|
|
||||||
/// given in the order of the vertices (i.e. the first half-edge's
|
|
||||||
/// origin is verts[0], second is verts[1], third is verts[2]).
|
|
||||||
pub fn add_face(&mut self, verts: [VertSpec<V>; 3]) -> (usize, [usize; 3]) {
|
|
||||||
// *New* vertices will be at index v_n onward.
|
|
||||||
let v_n = self.num_verts;
|
|
||||||
// The face will be at index f_n:
|
|
||||||
let f_n = self.num_faces;
|
|
||||||
// The half-edges will be at indices e_n, e_n+1, e_n+2:
|
|
||||||
let e_n = self.num_halfedges;
|
|
||||||
|
|
||||||
// Half-edges and vertices can be inserted both at once:
|
|
||||||
let mut new_verts: usize = 0;
|
|
||||||
for i in 0..3 {
|
|
||||||
let n = (i + 1) % 3;
|
|
||||||
let p = (i + 2) % 3;
|
|
||||||
// Either insert a new vertex, or use an existing one.
|
|
||||||
// In both cases, 'v' is its index.
|
|
||||||
let v = match verts[i] {
|
|
||||||
VertSpec::New(v) => {
|
|
||||||
self.verts.push(DCELVertex {
|
|
||||||
v: v,
|
|
||||||
halfedge: e_n + i,
|
|
||||||
});
|
|
||||||
let idx = v_n + new_verts;
|
|
||||||
new_verts += 1;
|
|
||||||
idx
|
|
||||||
},
|
|
||||||
VertSpec::Idx(v) => v,
|
|
||||||
};
|
|
||||||
// Note that its half-edge is e_n + i, which technically
|
|
||||||
// doesn't exist yet, but is inserted below:
|
|
||||||
self.halfedges.push(DCELHalfEdge {
|
|
||||||
vert: v,
|
|
||||||
face: f_n,
|
|
||||||
has_twin: false,
|
|
||||||
twin_halfedge: 0,
|
|
||||||
next_halfedge: e_n + n,
|
|
||||||
prev_halfedge: e_n + p,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
self.num_halfedges += 3;
|
|
||||||
self.num_verts += new_verts;
|
|
||||||
|
|
||||||
// Finally, add the face (any halfedge is fine):
|
|
||||||
self.faces.push(DCELFace { halfedge: e_n });
|
|
||||||
self.num_faces += 1;
|
|
||||||
|
|
||||||
(f_n, [e_n, e_n+1, e_n+2])
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add a face that lies on an existing boundary - i.e. one half-edge
|
|
||||||
/// has a twin half-edge already on the mesh. As this gives two
|
|
||||||
/// vertices, only one other vertex needs specified.
|
|
||||||
/// Returns (face index, halfedge indices). Halfedge indices begin
|
|
||||||
/// at the twin half-edge to the one specified.
|
|
||||||
pub fn add_face_twin1(&mut self, twin: usize, vert: V) -> (usize, [usize; 3]) {
|
|
||||||
// 'vert' will be at index v_n:
|
|
||||||
let v_n = self.num_verts;
|
|
||||||
// The half-edges will be at indices e_n, e_n+1, e_n+2:
|
|
||||||
let e_n = self.num_halfedges;
|
|
||||||
|
|
||||||
self.verts.push(DCELVertex {
|
|
||||||
v: vert,
|
|
||||||
halfedge: e_n + 2,
|
|
||||||
});
|
|
||||||
self.num_verts += 1;
|
|
||||||
self.add_face_twin1_ref(twin, v_n)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Like `add_face_twin1`, but for a vertex already present in the
|
|
||||||
/// mesh rather than a new one. All else is identical.
|
|
||||||
pub fn add_face_twin1_ref(&mut self, twin: usize, vert_idx: usize) -> (usize, [usize; 3]) {
|
|
||||||
// The face will be at index f_n:
|
|
||||||
let f_n = self.num_faces;
|
|
||||||
// The half-edges will be at indices e_n, e_n+1, e_n+2:
|
|
||||||
let e_n = self.num_halfedges;
|
|
||||||
|
|
||||||
// Note the reversal of direction
|
|
||||||
let twin_halfedge = &self.halfedges[twin];
|
|
||||||
let v1 = self.halfedges[twin_halfedge.next_halfedge].vert;
|
|
||||||
let v2 = twin_halfedge.vert;
|
|
||||||
// twin is: v2 -> v1
|
|
||||||
|
|
||||||
// Insert *its* twin, v1 -> v2, first:
|
|
||||||
self.halfedges.push(DCELHalfEdge {
|
|
||||||
vert: v1,
|
|
||||||
face: f_n,
|
|
||||||
has_twin: true,
|
|
||||||
twin_halfedge: twin,
|
|
||||||
next_halfedge: e_n + 1,
|
|
||||||
prev_halfedge: e_n + 2,
|
|
||||||
});
|
|
||||||
// DEBUG
|
|
||||||
if self.halfedges[twin].has_twin {
|
|
||||||
panic!("Trying to add twin to {}, which already has twin ({})",
|
|
||||||
twin, self.halfedges[twin].twin_halfedge);
|
|
||||||
}
|
|
||||||
self.halfedges[twin].has_twin = true;
|
|
||||||
self.halfedges[twin].twin_halfedge = e_n;
|
|
||||||
self.halfedges.push(DCELHalfEdge {
|
|
||||||
vert: v2,
|
|
||||||
face: f_n,
|
|
||||||
has_twin: false,
|
|
||||||
twin_halfedge: 0,
|
|
||||||
next_halfedge: e_n + 2,
|
|
||||||
prev_halfedge: e_n,
|
|
||||||
});
|
|
||||||
self.halfedges.push(DCELHalfEdge {
|
|
||||||
vert: vert_idx,
|
|
||||||
face: f_n,
|
|
||||||
has_twin: false,
|
|
||||||
twin_halfedge: 0,
|
|
||||||
next_halfedge: e_n,
|
|
||||||
prev_halfedge: e_n + 1,
|
|
||||||
});
|
|
||||||
|
|
||||||
self.num_halfedges += 3;
|
|
||||||
|
|
||||||
// Finally, add the face (any halfedge is fine):
|
|
||||||
self.faces.push(DCELFace { halfedge: e_n });
|
|
||||||
self.num_faces += 1;
|
|
||||||
|
|
||||||
(f_n, [e_n, e_n+1, e_n+2])
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add a face that lies on two connected boundaries - i.e. two of its
|
|
||||||
/// half-edges have twins already on the mesh.
|
|
||||||
///
|
|
||||||
/// Twin half-edges should be given in counter-clockwise order; that
|
|
||||||
/// is, for the resultant face, one half-edge's twin will be twin1, and
|
|
||||||
/// the next half-edge's twin will be twin2.
|
|
||||||
/// Also: halfedge `twin2_idx` must end at the vertex that starts
|
|
||||||
/// `twin1_idx`.
|
|
||||||
///
|
|
||||||
/// Returns (face index, halfedge indices). Halfedge indices begin
|
|
||||||
/// at the twin halfedge to twin1, then twin halfedge of
|
|
||||||
/// twin2, then the 'new' halfedge (which starts where twin2 starts,
|
|
||||||
/// and ends where twin1 ends).
|
|
||||||
pub fn add_face_twin2(&mut self, twin1_idx: usize, twin2_idx: usize) -> (usize, [usize; 3]) {
|
|
||||||
// The face will be at index f_n:
|
|
||||||
let f_n = self.num_faces;
|
|
||||||
// The half-edges will be at indices e_n, e_n+1, e_n+2:
|
|
||||||
let e_n = self.num_halfedges;
|
|
||||||
|
|
||||||
// The origin vertex is 'between' the two edges, but because their
|
|
||||||
// order is reversed (as twins), this is twin1's origin:
|
|
||||||
let twin1 = &self.halfedges[twin1_idx];
|
|
||||||
let twin2 = &self.halfedges[twin2_idx];
|
|
||||||
let v1 = twin1.vert;
|
|
||||||
let v2 = self.halfedges[twin1.next_halfedge].vert;
|
|
||||||
// Final vertex is back around to twin2's origin:
|
|
||||||
let v3 = twin2.vert;
|
|
||||||
|
|
||||||
if v1 != self.halfedges[twin2.next_halfedge].vert {
|
|
||||||
panic!("twin2 ({}) must end where twin1 ({}) begins, but does not (vertex {} vs. {})",
|
|
||||||
twin2_idx, twin1_idx, self.halfedges[twin2.next_halfedge].vert, v1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// twin1 is: v1 -> v2, twin2 is: v3 -> v1.
|
|
||||||
// so the twin of twin1 must be: v2 -> v1
|
|
||||||
self.halfedges.push(DCELHalfEdge {
|
|
||||||
vert: v2,
|
|
||||||
face: f_n,
|
|
||||||
has_twin: true,
|
|
||||||
twin_halfedge: twin1_idx,
|
|
||||||
next_halfedge: e_n + 1,
|
|
||||||
prev_halfedge: e_n + 2,
|
|
||||||
}); // index e_n
|
|
||||||
self.halfedges[twin1_idx].has_twin = true;
|
|
||||||
self.halfedges[twin1_idx].twin_halfedge = e_n;
|
|
||||||
// and the twin of twin2 must be: v1 -> v3
|
|
||||||
self.halfedges.push(DCELHalfEdge {
|
|
||||||
vert: v1,
|
|
||||||
face: f_n,
|
|
||||||
has_twin: true,
|
|
||||||
twin_halfedge: twin2_idx,
|
|
||||||
next_halfedge: e_n + 2,
|
|
||||||
prev_halfedge: e_n,
|
|
||||||
}); // index e_n + 1
|
|
||||||
self.halfedges[twin2_idx].has_twin = true;
|
|
||||||
self.halfedges[twin2_idx].twin_halfedge = e_n + 1;
|
|
||||||
// and final edge must be v3 -> v2:
|
|
||||||
self.halfedges.push(DCELHalfEdge {
|
|
||||||
vert: v3,
|
|
||||||
face: f_n,
|
|
||||||
has_twin: false,
|
|
||||||
twin_halfedge: 0,
|
|
||||||
next_halfedge: e_n,
|
|
||||||
prev_halfedge: e_n + 1,
|
|
||||||
}); // index e_n + 2
|
|
||||||
self.num_halfedges += 3;
|
|
||||||
|
|
||||||
// Finally, add the face (any halfedge is fine):
|
|
||||||
self.faces.push(DCELFace { halfedge: e_n });
|
|
||||||
self.num_faces += 1;
|
|
||||||
|
|
||||||
(f_n, [e_n, e_n+1, e_n+2])
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Splits a face (assumed to be a triangle) in this mesh by splitting
|
|
||||||
/// its half-edges, in order, at the vertices given, and then creating
|
|
||||||
/// half-edges between each pair of them.
|
|
||||||
///
|
|
||||||
/// This returns; (new face indices, updated face indices). That is,
|
|
||||||
/// the first vector is new face indices that were created, and the
|
|
||||||
/// second is existing face indices that were reused (but the faces
|
|
||||||
/// were updated).
|
|
||||||
///
|
|
||||||
/// This splits the given face into 4 smaller faces, and it splits the
|
|
||||||
/// three *bordering* faces each into 2 faces. That is, this
|
|
||||||
/// increases the number of faces by 6 and the number of vertices
|
|
||||||
/// by 3.
|
|
||||||
///
|
|
||||||
/// Right now, this requires that every halfedge of `face` has a twin.
|
|
||||||
/// If this is not the case, this returns `None` and does not subdivide.
|
|
||||||
/// This behavior will likely be changed in the future.
|
|
||||||
///
|
|
||||||
/// Disclaimer: This code is completely awful; just don't use it.
|
|
||||||
pub fn full_subdiv_face(&mut self, face: usize, verts: Vec<V>) -> Option<(Vec<usize>, Vec<usize>)> {
|
|
||||||
// 'verts' maps 1:1 to vertices for 'face' (i.e. face_to_verts).
|
|
||||||
|
|
||||||
let mut edge_idx = self.faces[face].halfedge;
|
|
||||||
let n = verts.len();
|
|
||||||
|
|
||||||
let mut faces_new = vec![];
|
|
||||||
let mut faces_upd = vec![];
|
|
||||||
|
|
||||||
let mut fail = false;
|
|
||||||
let new_edges: Vec<(usize, usize)> = verts.iter().map(|v| {
|
|
||||||
|
|
||||||
// As we iterate over every vertex, we walk the half-edges:
|
|
||||||
let mut edge = self.halfedges[edge_idx].clone();
|
|
||||||
if !edge.has_twin {
|
|
||||||
println!("Halfedge {} has no twin, and split_face (for now) requires twins", edge_idx);
|
|
||||||
fail = true;
|
|
||||||
return (0,0);
|
|
||||||
}
|
|
||||||
// TODO: Remove the above limitation and just don't try to split
|
|
||||||
// nonexistent twins. I think all logic works the same way.
|
|
||||||
|
|
||||||
let next_idx = edge.next_halfedge;
|
|
||||||
let twin_idx = edge.twin_halfedge;
|
|
||||||
let mut twin = self.halfedges[twin_idx].clone();
|
|
||||||
|
|
||||||
// This half-edge, and its twin, are both split (at this
|
|
||||||
// vertex). Half-edges i & j will be the new ones created.
|
|
||||||
let i_edge = self.num_halfedges;
|
|
||||||
let j_edge = i_edge + 1;
|
|
||||||
// Half of edge_idx is split into j_edge.
|
|
||||||
// Half of twin_idx (its twin) is split into i_edge.
|
|
||||||
|
|
||||||
// This is where the vertex will be inserted:
|
|
||||||
let v_idx = self.num_verts;
|
|
||||||
self.verts.push(DCELVertex {
|
|
||||||
v: *v,
|
|
||||||
halfedge: i_edge, // j_edge is also fine
|
|
||||||
});
|
|
||||||
self.num_verts += 1;
|
|
||||||
|
|
||||||
edge.twin_halfedge = i_edge;
|
|
||||||
let j_next = edge.next_halfedge;
|
|
||||||
|
|
||||||
twin.twin_halfedge = j_edge;
|
|
||||||
let i_next = twin.next_halfedge;
|
|
||||||
|
|
||||||
self.halfedges.push(DCELHalfEdge {
|
|
||||||
vert: v_idx,
|
|
||||||
face: 0, // This is set properly in the next loop
|
|
||||||
has_twin: true,
|
|
||||||
twin_halfedge: edge_idx,
|
|
||||||
next_halfedge: i_next,
|
|
||||||
prev_halfedge: twin_idx,
|
|
||||||
}); // i_edge
|
|
||||||
self.halfedges.push(DCELHalfEdge {
|
|
||||||
vert: v_idx,
|
|
||||||
face: 0, // This is set properly in the next loop
|
|
||||||
has_twin: true,
|
|
||||||
twin_halfedge: twin_idx,
|
|
||||||
next_halfedge: j_next,
|
|
||||||
prev_halfedge: edge_idx,
|
|
||||||
}); // j_edge
|
|
||||||
|
|
||||||
self.num_halfedges += 2;
|
|
||||||
|
|
||||||
self.halfedges[edge_idx] = edge;
|
|
||||||
self.halfedges[twin_idx] = twin;
|
|
||||||
|
|
||||||
let r = (edge_idx, j_edge);
|
|
||||||
|
|
||||||
edge_idx = next_idx;
|
|
||||||
|
|
||||||
r
|
|
||||||
}).collect();
|
|
||||||
|
|
||||||
if fail {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We then must connect some edges up 'across' vertices
|
|
||||||
// in order to form the smaller face at each vertex.
|
|
||||||
//
|
|
||||||
// This is outside the loop because we need one iteration's
|
|
||||||
// value (any iteration, doesn't matter) to reassign a face:
|
|
||||||
let mut e_twin_idx = 0;
|
|
||||||
let twin_edges: Vec<usize> = (0..n).map(|i0| {
|
|
||||||
let i1 = (i0 + 1) % n;
|
|
||||||
|
|
||||||
let (_, ep_idx) = new_edges[i0];
|
|
||||||
let (en_idx, _) = new_edges[i1];
|
|
||||||
|
|
||||||
// Halfedges will be inserted here:
|
|
||||||
let e_cross_idx = self.num_halfedges;
|
|
||||||
e_twin_idx = e_cross_idx + 1;
|
|
||||||
// And the face here:
|
|
||||||
let face_new = self.num_faces;
|
|
||||||
|
|
||||||
// So, the vertex for i0 had two halfedges (one pointing in,
|
|
||||||
// one pointing out). We split both those half-edges earlier.
|
|
||||||
// en_idx & ep_idx are the halves that are nearest the
|
|
||||||
// vertex. The point of below is to form a smaller triangle
|
|
||||||
// (which includes this vertex, and the point at which both
|
|
||||||
// edges were split). This requires a half-edge from ep_idx
|
|
||||||
// (which *starts* where the other was split) to en_idx (which
|
|
||||||
// *ends* where one edge was split).
|
|
||||||
self.halfedges.push(DCELHalfEdge {
|
|
||||||
vert: self.halfedges[self.halfedges[en_idx].twin_halfedge].vert,
|
|
||||||
face: face_new,
|
|
||||||
has_twin: true,
|
|
||||||
twin_halfedge: e_twin_idx,
|
|
||||||
next_halfedge: ep_idx,
|
|
||||||
prev_halfedge: en_idx,
|
|
||||||
}); // e_cross_idx
|
|
||||||
|
|
||||||
// It also requires a twin half-edge. These all form a single
|
|
||||||
// central face with each edge sharing a boundary with the
|
|
||||||
// 'cross' edge we just created.
|
|
||||||
self.halfedges.push(DCELHalfEdge {
|
|
||||||
vert: self.halfedges[ep_idx].vert,
|
|
||||||
face: face, // Reuse index for the smaller *central* face
|
|
||||||
has_twin: true,
|
|
||||||
twin_halfedge: e_cross_idx,
|
|
||||||
next_halfedge: 0, // TODO
|
|
||||||
prev_halfedge: 0, // TODO
|
|
||||||
}); // e_twin_idx
|
|
||||||
self.num_halfedges += 2;
|
|
||||||
|
|
||||||
// en/ep also need directed to 'new' edges and each other
|
|
||||||
self.halfedges[en_idx].prev_halfedge = ep_idx;
|
|
||||||
self.halfedges[en_idx].next_halfedge = e_cross_idx;
|
|
||||||
|
|
||||||
self.halfedges[ep_idx].next_halfedge = en_idx;
|
|
||||||
self.halfedges[ep_idx].prev_halfedge = e_cross_idx;
|
|
||||||
|
|
||||||
self.halfedges[ep_idx].face = face_new;
|
|
||||||
|
|
||||||
self.faces.push(DCELFace {
|
|
||||||
halfedge: e_cross_idx, // en_idx or ep_idx is fine too
|
|
||||||
}); // face_new
|
|
||||||
faces_new.push(face_new);
|
|
||||||
self.num_faces += 1;
|
|
||||||
|
|
||||||
// We also need to split the opposite side to make the two
|
|
||||||
// 'side' triangles (which means two new half-edges).
|
|
||||||
// First, we have to find the halfedge that starts the
|
|
||||||
// 'opposite' vertex (outer2):
|
|
||||||
let base1 = self.halfedges[en_idx].twin_halfedge;
|
|
||||||
let base2 = self.halfedges[base1].prev_halfedge;
|
|
||||||
let outer1 = self.halfedges[base1].next_halfedge;
|
|
||||||
let outer2 = self.halfedges[outer1].next_halfedge;
|
|
||||||
let v_opp = self.halfedges[outer2].vert;
|
|
||||||
// One face will reuse the old index:
|
|
||||||
let face1 = self.halfedges[outer2].face;
|
|
||||||
// Another will be inserted at this index:
|
|
||||||
let face2 = self.num_faces;
|
|
||||||
// Half-edges will be inserted here:
|
|
||||||
let edge_side1 = self.num_halfedges;
|
|
||||||
let edge_side2 = edge_side1 + 1;
|
|
||||||
self.halfedges.push(DCELHalfEdge {
|
|
||||||
vert: v_opp,
|
|
||||||
face: face1,
|
|
||||||
has_twin: true,
|
|
||||||
twin_halfedge: edge_side2,
|
|
||||||
next_halfedge: base1,
|
|
||||||
prev_halfedge: outer1,
|
|
||||||
}); // edge_side1
|
|
||||||
self.halfedges.push(DCELHalfEdge {
|
|
||||||
vert: self.halfedges[base1].vert,
|
|
||||||
face: face2,
|
|
||||||
has_twin: true,
|
|
||||||
twin_halfedge: edge_side1,
|
|
||||||
next_halfedge: outer2,
|
|
||||||
prev_halfedge: base2,
|
|
||||||
}); // edge_side2
|
|
||||||
self.num_halfedges += 2;
|
|
||||||
|
|
||||||
self.faces.push(DCELFace {
|
|
||||||
halfedge: outer2, // base2 or edge_side2 is fine too
|
|
||||||
});
|
|
||||||
faces_new.push(self.num_faces);
|
|
||||||
self.num_faces += 1;
|
|
||||||
self.faces[face1].halfedge = outer1; // base1 or edge_side1 is fine too
|
|
||||||
faces_upd.push(face1);
|
|
||||||
|
|
||||||
self.halfedges[outer1].next_halfedge = edge_side1;
|
|
||||||
self.halfedges[outer1].prev_halfedge = base1;
|
|
||||||
self.halfedges[outer1].face = face1;
|
|
||||||
self.halfedges[base1].face = face1;
|
|
||||||
self.halfedges[base1].prev_halfedge = edge_side1;
|
|
||||||
self.halfedges[base1].next_halfedge = outer1;
|
|
||||||
|
|
||||||
self.halfedges[outer2].next_halfedge = base2;
|
|
||||||
self.halfedges[outer2].prev_halfedge = edge_side2;
|
|
||||||
self.halfedges[outer2].face = face2;
|
|
||||||
self.halfedges[base2].face = face2;
|
|
||||||
self.halfedges[base2].prev_halfedge = outer2;
|
|
||||||
self.halfedges[base2].next_halfedge = edge_side2;
|
|
||||||
|
|
||||||
e_twin_idx
|
|
||||||
}).collect();
|
|
||||||
|
|
||||||
for i0 in 0..n {
|
|
||||||
let i1 = (i0 + 1) % n;
|
|
||||||
|
|
||||||
self.halfedges[twin_edges[i0]].next_halfedge = twin_edges[i1];
|
|
||||||
self.halfedges[twin_edges[i1]].prev_halfedge = twin_edges[i0];
|
|
||||||
}
|
|
||||||
|
|
||||||
// We split up original 'face' completely and created four new faces,
|
|
||||||
// We need something at this index, and the other three already have
|
|
||||||
// indices, so reuse it for the smaller central face:
|
|
||||||
self.faces[face].halfedge = e_twin_idx;
|
|
||||||
faces_upd.push(face);
|
|
||||||
|
|
||||||
return Some((faces_new, faces_upd));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn convert_mesh<F>(&self, f: F) -> Mesh
|
|
||||||
where F: Fn(V) -> Vertex,
|
|
||||||
{
|
|
||||||
let n = self.faces.len();
|
|
||||||
let mut faces: Vec<usize> = vec![0; 3 * n];
|
|
||||||
|
|
||||||
for i in 0..n {
|
|
||||||
|
|
||||||
let e0 = self.faces[i].halfedge;
|
|
||||||
let h0 = &self.halfedges[e0];
|
|
||||||
faces[3*i + 0] = h0.vert;
|
|
||||||
let e1 = h0.next_halfedge;
|
|
||||||
let h1 = &self.halfedges[e1];
|
|
||||||
faces[3*i + 1] = h1.vert;
|
|
||||||
let e2 = h1.next_halfedge;
|
|
||||||
let h2 = &self.halfedges[e2];
|
|
||||||
faces[3*i + 2] = h2.vert;
|
|
||||||
if h2.next_halfedge != e0 {
|
|
||||||
panic!("Face {}: half-edges {},{},{} return to {}, not {}",
|
|
||||||
i, e0, e1, e2, h2.next_halfedge, e0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Mesh {
|
|
||||||
verts: self.verts.iter().map(|e| f(e.v)).collect(),
|
|
||||||
faces: faces,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
868
src/examples.rs
868
src/examples.rs
@ -1,4 +1,3 @@
|
|||||||
use std::rc::Rc;
|
|
||||||
use std::f32::consts::{FRAC_PI_2, FRAC_PI_3};
|
use std::f32::consts::{FRAC_PI_2, FRAC_PI_3};
|
||||||
use std::f32;
|
use std::f32;
|
||||||
|
|
||||||
@ -7,12 +6,8 @@ use nalgebra::*;
|
|||||||
|
|
||||||
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};
|
||||||
use crate::xform::{Transform, Vertex, vertex, id};
|
use crate::xform::{Transform, Vertex, vertex, id};
|
||||||
use crate::rule::{Rule, RuleEval, Child};
|
|
||||||
use crate::prim;
|
|
||||||
use crate::dcel;
|
|
||||||
use crate::dcel::{VertSpec};
|
|
||||||
|
|
||||||
pub struct Barbs {
|
pub struct Barbs {
|
||||||
base_incr: Transform,
|
base_incr: Transform,
|
||||||
@ -275,826 +270,99 @@ impl TreeThing {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sierpinski() -> Rule<()> {
|
pub struct Sierpinski {
|
||||||
|
splits: [Transform; 3],
|
||||||
// Initial height step:
|
base: Vec<Vertex>,
|
||||||
let dz = 0.10;
|
verts: Vec<Vertex>,
|
||||||
// 'Extra' z rotation (0.0 for normal Sierpinski)
|
faces: Vec<usize>,
|
||||||
let dr = 0.1;
|
}
|
||||||
// Scale factor (0.5 for normal Sierpinski)
|
|
||||||
let s = 0.51;
|
|
||||||
|
|
||||||
|
impl Sierpinski {
|
||||||
|
// s = Scale factor (0.5 for normal Sierpinski)
|
||||||
|
// dz = Initial height step:
|
||||||
|
// dr = 'Extra' z rotation (0.0 for normal Sierpinski)
|
||||||
|
pub fn new(s: f32, dz: f32, dr: f32) -> Sierpinski {
|
||||||
|
let mut splits: [Transform; 3] = [id(); 3];
|
||||||
let rt3 = (3.0).sqrt();
|
let rt3 = (3.0).sqrt();
|
||||||
|
for i in 0..3 {
|
||||||
|
let angle = 2.0 * FRAC_PI_3 * (i as f32) + dr;
|
||||||
|
splits[i] = id().
|
||||||
|
rotate(&Vector3::z_axis(), angle).
|
||||||
|
translate(rt3/12.0, 0.0, 0.0).
|
||||||
|
scale(s).
|
||||||
|
translate(0.0, 0.0, dz);
|
||||||
|
}
|
||||||
|
|
||||||
// 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, n);
|
|
||||||
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);
|
||||||
let v2 = vertex(-rt3/6.0, -1.0/2.0, 0.0);
|
let v2 = vertex(-rt3/6.0, -1.0/2.0, 0.0);
|
||||||
let v0b = v0 + vertex(0.0, 0.0, dz);
|
let v0b = v0 + vertex(0.0, 0.0, dz);
|
||||||
let v1b = v1 + vertex(0.0, 0.0, dz);
|
let v1b = v1 + vertex(0.0, 0.0, dz);
|
||||||
let v2b = v2 + vertex(0.0, 0.0, dz);
|
let v2b = v2 + vertex(0.0, 0.0, dz);
|
||||||
vec_indexed![
|
|
||||||
@b VertexUnion::Vertex(v0),
|
|
||||||
VertexUnion::Vertex(v1),
|
|
||||||
VertexUnion::Vertex(v2),
|
|
||||||
@t VertexUnion::Vertex(v0b),
|
|
||||||
VertexUnion::Vertex(v1b),
|
|
||||||
VertexUnion::Vertex(v2b),
|
|
||||||
@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 base = vec![
|
||||||
let rt3 = (3.0).sqrt();
|
v0, v1, v2,
|
||||||
let angle = 2.0 * FRAC_PI_3 * (i as f32) + dr;
|
v0b, v1b, v2b,
|
||||||
id().
|
(v0b+v1b)/2.0,
|
||||||
rotate(&Vector3::z_axis(), angle).
|
(v1b+v2b)/2.0,
|
||||||
translate(rt3/12.0, 0.0, 0.0).
|
(v2b+v0b)/2.0,
|
||||||
scale(s).
|
];
|
||||||
translate(0.0, 0.0, dz)
|
|
||||||
};
|
|
||||||
|
|
||||||
let split = rule_fn!(() => |_s, base_verts| {
|
Sierpinski {
|
||||||
|
splits: splits,
|
||||||
let mut next_verts = base_verts.clone();
|
base: base,
|
||||||
let (a0, _) = next_verts.append_indexed(vert_args(0..3));
|
|
||||||
|
|
||||||
RuleEval {
|
|
||||||
geom: Rc::new(MeshFunc {
|
|
||||||
verts: next_verts,
|
|
||||||
faces: vec![
|
|
||||||
//a0, a0+1, a0+2,
|
|
||||||
// Outer:
|
|
||||||
tm01, a0+1, t+1,
|
|
||||||
tm01, t+0, a0+0,
|
|
||||||
tm01, a0+0, a0+1,
|
|
||||||
tm12, a0+2, t+2,
|
|
||||||
tm12, t+1, a0+1,
|
|
||||||
tm12, a0+1, a0+2,
|
|
||||||
tm20, a0+0, t+0,
|
|
||||||
tm20, t+2, a0+2,
|
|
||||||
tm20, a0+2, a0+0,
|
|
||||||
// Inner:
|
|
||||||
tm01, tm12, tm20,
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
final_geom: Rc::new(MeshFunc {
|
|
||||||
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),
|
|
||||||
child!(_s, tri_split(1), t+1, tm12, tm01),
|
|
||||||
child!(_s, tri_split(2), t+2, tm20, tm12),
|
|
||||||
],
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let base = rule_fn!(() => |_s, base_verts| {
|
|
||||||
RuleEval {
|
|
||||||
geom: Rc::new(MeshFunc {
|
|
||||||
verts: base_verts,
|
|
||||||
faces: vec![
|
|
||||||
// Outer:
|
|
||||||
tm01, b+1, t+1,
|
|
||||||
tm01, t+0, b+0,
|
|
||||||
tm01, b+0, b+1,
|
|
||||||
tm12, b+2, t+2,
|
|
||||||
tm12, t+1, b+1,
|
|
||||||
tm12, b+1, b+2,
|
|
||||||
tm20, b+0, t+0,
|
|
||||||
tm20, t+2, b+2,
|
|
||||||
tm20, b+2, b+0,
|
|
||||||
// Inner:
|
|
||||||
tm01, tm12, tm20,
|
|
||||||
// Bottom:
|
|
||||||
b+2, b+1, b+0,
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
final_geom: Rc::new(MeshFunc {
|
|
||||||
verts: vec![],
|
verts: vec![],
|
||||||
faces: vec![],
|
faces: vec![],
|
||||||
}),
|
|
||||||
children: vec![
|
|
||||||
child!(rule!(split, ()), tri_split(0), t+0, tm01, tm20),
|
|
||||||
child!(rule!(split, ()), tri_split(1), t+1, tm12, tm01),
|
|
||||||
child!(rule!(split, ()), tri_split(2), t+2, tm20, tm12),
|
|
||||||
],
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Rule { eval: base, ctxt: () }
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
// Meant to be a copy of twist_from_gen from Python &
|
|
||||||
// automata_scratch, but has since acquired a sort of life of its own
|
|
||||||
pub fn twist(f: f32, subdiv: usize) -> Rule<()> {
|
|
||||||
// TODO: Clean this code up. It was a very naive conversion from
|
|
||||||
// the non-closure version.
|
|
||||||
let xf = Transform::new().rotate(&Vector3::x_axis(), -0.7);
|
|
||||||
let seed = {
|
|
||||||
let s = vec![vertex(-0.5, 0.0, -0.5),
|
|
||||||
vertex( 0.5, 0.0, -0.5),
|
|
||||||
vertex( 0.5, 0.0, 0.5),
|
|
||||||
vertex(-0.5, 0.0, 0.5)];
|
|
||||||
util::subdivide_cycle(&xf.transform(&s), subdiv)
|
|
||||||
};
|
|
||||||
let n = seed.len();
|
|
||||||
let dx0: f32 = 2.0;
|
|
||||||
let dy: f32 = 0.1/f;
|
|
||||||
let ang: f32 = 0.1/f;
|
|
||||||
let count: usize = 4;
|
|
||||||
|
|
||||||
// Quarter-turn in radians:
|
|
||||||
let qtr = FRAC_PI_2;
|
|
||||||
let y = Vector3::y_axis();
|
|
||||||
|
|
||||||
let incr_inner = Transform::new().translate(-dx0, 0.0, 0.0).rotate(&y, ang).translate(dx0, dy, 0.0);
|
|
||||||
let incr_outer = Transform::new().translate(-dx0*2.0, 0.0, 0.0).rotate(&y, ang/2.0).translate(dx0*2.0, dy, 0.0);
|
|
||||||
|
|
||||||
let seed2 = seed.clone();
|
|
||||||
// TODO: Why do I need the above?
|
|
||||||
// TODO: Could a macro get rid of some of this or would it just be
|
|
||||||
// equally cumbersome because I'd have to sort of pass 'seed'
|
|
||||||
// explicitly?
|
|
||||||
let recur = move |incr: Transform| -> RuleFn<()> {
|
|
||||||
|
|
||||||
let seed_next = incr.transform(&seed2);
|
|
||||||
|
|
||||||
//let vc = util::centroid(&seed_next);
|
|
||||||
//let faces = util::connect_convex(0..n, n, true);
|
|
||||||
let geom = util::parallel_zigzag(seed_next, 0..n, 0..n);
|
|
||||||
let final_geom = MeshFunc {
|
|
||||||
verts: vec![],
|
|
||||||
faces: vec![],
|
|
||||||
// TODO: get actual verts here
|
|
||||||
};
|
|
||||||
|
|
||||||
let c = move |self_: Rc<Rule<()>>| -> RuleEval<()> {
|
|
||||||
RuleEval {
|
|
||||||
geom: geom.clone(),
|
|
||||||
final_geom: final_geom.clone(),
|
|
||||||
children: vec![
|
|
||||||
Child {
|
|
||||||
rule: self_.clone(),
|
|
||||||
xf: incr,
|
|
||||||
arg_vals: (0..n).collect(),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Rc::new(c)
|
|
||||||
};
|
|
||||||
// TODO: Can a macro do anything to clean up some of the
|
|
||||||
// repetition with HOFs & closures?
|
|
||||||
|
|
||||||
let start = move |_| -> RuleEval<()> {
|
|
||||||
|
|
||||||
let child = |incr, dx, i, ang0, div| -> (MeshFunc, Child<()>) {
|
|
||||||
let xform = Transform::new().
|
|
||||||
rotate(&y, ang0 + (qtr / div * (i as f32))).
|
|
||||||
translate(dx, 0.0, 0.0);
|
|
||||||
|
|
||||||
let c = Child {
|
|
||||||
rule: Rc::new(Rule { eval: (recur.clone())(incr), ctxt: () }),
|
|
||||||
// TODO: Cleanliness fix - can macros clean up above?
|
|
||||||
xf: xform,
|
|
||||||
arg_vals: (0..(n+1)).collect(),
|
|
||||||
// N.B. n+1, not n. the +1 is for the centroid below.
|
|
||||||
};
|
|
||||||
let mut vs = xform.transform(&seed);
|
|
||||||
// and in the process, generate faces for these seeds:
|
|
||||||
let (centroid, f) = util::connect_convex(&vs, false);
|
|
||||||
vs.push(centroid);
|
|
||||||
(MeshFunc { verts: vs, faces: f }, c)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Generate 'count' children, shifted/rotated differently:
|
|
||||||
let inner = (0..count).map(|i| child(incr_inner, dx0, i, 0.0, 1.0));
|
|
||||||
//let outer = (0..count).map(|i| child(incr_outer, dx0*2.0, i, qtr/2.0, 2.0));
|
|
||||||
let outer = (0..0).map(|i| child(incr_outer, dx0*2.0, i, qtr/2.0, 2.0));
|
|
||||||
|
|
||||||
RuleEval::from_pairs(inner.chain(outer), prim::empty_mesh())
|
|
||||||
};
|
|
||||||
|
|
||||||
Rule { eval: Rc::new(start), ctxt: () }
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct NestSpiral2Ctxt {
|
|
||||||
init: bool,
|
|
||||||
stack: [Transform; 2],
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn nest_spiral_2() -> Rule<NestSpiral2Ctxt> {
|
|
||||||
let subdiv = 8;
|
|
||||||
let seed = 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),
|
|
||||||
];
|
|
||||||
let seed = util::subdivide_cycle(&seed, subdiv);
|
|
||||||
|
|
||||||
let n = seed.len();
|
|
||||||
let geom = Rc::new(util::zigzag_to_parent(seed.clone(), n));
|
|
||||||
let (vc, faces) = util::connect_convex(&seed, true);
|
|
||||||
let final_geom = Rc::new(OpenMesh {
|
|
||||||
verts: vec![vc],
|
|
||||||
alias_verts: vec![],
|
|
||||||
faces: faces,
|
|
||||||
});
|
|
||||||
|
|
||||||
let rad = 1.0;
|
|
||||||
let dz = 0.1;
|
|
||||||
let rz = 0.1;
|
|
||||||
let rad2 = 4.0;
|
|
||||||
let rz2 = 0.1;
|
|
||||||
|
|
||||||
let recur = move |self_: Rc<Rule<NestSpiral2Ctxt>>| -> RuleEval<NestSpiral2Ctxt> {
|
|
||||||
//let x = &Vector3::x_axis();
|
|
||||||
let z = &Vector3::z_axis();
|
|
||||||
let stack = self_.ctxt.stack;
|
|
||||||
let next_rule = Rule {
|
|
||||||
eval: self_.eval.clone(),
|
|
||||||
ctxt: NestSpiral2Ctxt {
|
|
||||||
init: false,
|
|
||||||
stack: [
|
|
||||||
Transform::new().rotate(z, rz2) * stack[0],
|
|
||||||
Transform::new().translate(0.0, 0.0, dz).rotate(z, rz) * stack[1],
|
|
||||||
],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
let xf = stack.iter().fold(Transform::new(), |acc,m| acc * (*m));
|
|
||||||
if self_.ctxt.init {
|
|
||||||
let mut s2 = seed.clone();
|
|
||||||
let (centroid, f) = util::connect_convex(&s2, false);
|
|
||||||
s2.push(centroid);
|
|
||||||
let n2 = s2.len();
|
|
||||||
let g = OpenMesh { verts: s2, faces: f, alias_verts: vec![] };
|
|
||||||
RuleEval {
|
|
||||||
geom: Rc::new(g.transform(&xf)),
|
|
||||||
final_geom: Rc::new(prim::empty_mesh()),
|
|
||||||
children: vec![
|
|
||||||
Child {
|
|
||||||
rule: Rc::new(next_rule),
|
|
||||||
xf: Transform::new(),
|
|
||||||
arg_vals: (0..n2).collect(),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
RuleEval {
|
|
||||||
geom: Rc::new(geom.transform(&xf)),
|
|
||||||
final_geom: Rc::new(final_geom.transform(&xf)),
|
|
||||||
children: vec![
|
|
||||||
Child {
|
|
||||||
rule: Rc::new(next_rule),
|
|
||||||
xf: Transform::new(),
|
|
||||||
arg_vals: (0..n).collect(),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let count = 3;
|
|
||||||
|
|
||||||
let r = Rc::new(recur);
|
|
||||||
let start = move |self_: Rc<Rule<NestSpiral2Ctxt>>| -> RuleEval<NestSpiral2Ctxt> {
|
|
||||||
let z = &Vector3::z_axis();
|
|
||||||
let child = |i: usize| -> Child<NestSpiral2Ctxt> {
|
|
||||||
let ang = PI * 2.0 * (i as f32) / (count as f32);
|
|
||||||
Child {
|
|
||||||
rule: Rc::new(Rule {
|
|
||||||
eval: r.clone(),
|
|
||||||
ctxt: NestSpiral2Ctxt {
|
|
||||||
init: true,
|
|
||||||
stack: [
|
|
||||||
Transform::new().translate(rad2, 0.0, 0.0),
|
|
||||||
Transform::new().rotate(z, ang).translate(rad, 0.0, 0.0),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
xf: Transform::new(),
|
|
||||||
arg_vals: vec![], // no parent vertices
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
RuleEval {
|
|
||||||
geom: Rc::new(prim::empty_mesh()),
|
|
||||||
final_geom: Rc::new(prim::empty_mesh()),
|
|
||||||
children: (0..count).map(child).collect(),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Rule {
|
|
||||||
eval: Rc::new(start),
|
|
||||||
ctxt: NestSpiral2Ctxt {
|
|
||||||
init: true,
|
|
||||||
stack: [ // doesn't matter
|
|
||||||
Transform::new(),
|
|
||||||
Transform::new(),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct TorusCtxt {
|
|
||||||
init: bool,
|
|
||||||
count: usize,
|
|
||||||
stack: [Transform; 3],
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn twisty_torus() -> Rule<TorusCtxt> {
|
pub fn run(mut self) -> Mesh {
|
||||||
let subdiv = 8;
|
self.verts.append(&mut self.base.clone());
|
||||||
let seed = vec![
|
self.faces.extend_from_slice(&[2, 1, 0]);
|
||||||
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),
|
|
||||||
];
|
|
||||||
let xf = Transform::new().rotate(&Vector3::x_axis(), -0.9);
|
|
||||||
let seed = util::subdivide_cycle(&xf.transform(&seed), subdiv);
|
|
||||||
|
|
||||||
let n = seed.len();
|
self.tri_split(id(), [0, 1, 2]);
|
||||||
let geom = util::parallel_zigzag(seed, 0..n, n..(2*n));
|
return Mesh {
|
||||||
// TODO: where are parent Args?
|
verts: self.verts,
|
||||||
let geom = Rc::new(util::zigzag_to_parent(seed.clone(), n));
|
faces: self.faces,
|
||||||
let (vc, faces) = util::connect_convex(&seed, true);
|
|
||||||
let final_geom = Rc::new(OpenMesh {
|
|
||||||
verts: vec![vc],
|
|
||||||
alias_verts: (0..(n+1)).collect(),
|
|
||||||
faces: faces,
|
|
||||||
});
|
|
||||||
|
|
||||||
let rad = 1.0;
|
|
||||||
let rad2 = 8.0;
|
|
||||||
let rad3 = 24.0;
|
|
||||||
let rz3 = 0.0004;
|
|
||||||
let dx = 0.00;
|
|
||||||
let rx = 0.01;
|
|
||||||
let rz = 0.30;
|
|
||||||
let ang = 0.1;
|
|
||||||
|
|
||||||
let recur = move |self_: Rc<Rule<TorusCtxt>>| -> RuleEval<TorusCtxt> {
|
|
||||||
let x = &Vector3::x_axis();
|
|
||||||
let z = &Vector3::z_axis();
|
|
||||||
let stack = self_.ctxt.stack;
|
|
||||||
let count = self_.ctxt.count;
|
|
||||||
let next_rule = Rule {
|
|
||||||
eval: self_.eval.clone(),
|
|
||||||
ctxt: TorusCtxt {
|
|
||||||
init: false,
|
|
||||||
count: count + 1,
|
|
||||||
stack: [
|
|
||||||
Transform::new().rotate(z, rz3) * stack[0],
|
|
||||||
Transform::new().translate(dx, 0.0, 0.0).rotate(x, rx) * stack[1],
|
|
||||||
Transform::new().rotate(z, rz) * stack[2],
|
|
||||||
],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
let xf = stack.iter().fold(Transform::new(), |acc,m| acc * (*m));
|
|
||||||
if self_.ctxt.init {
|
|
||||||
let mut s2 = seed.clone();
|
|
||||||
let (centroid, f) = util::connect_convex(&s2, false);
|
|
||||||
s2.push(centroid);
|
|
||||||
let n2 = s2.len();
|
|
||||||
let g = OpenMesh { verts: s2, faces: f, alias_verts: vec![] };
|
|
||||||
RuleEval {
|
|
||||||
geom: Rc::new(g.transform(&xf)),
|
|
||||||
final_geom: Rc::new(prim::empty_mesh()),
|
|
||||||
children: vec![
|
|
||||||
Child {
|
|
||||||
rule: Rc::new(next_rule),
|
|
||||||
xf: Transform::new(),
|
|
||||||
arg_vals: (0..n2).collect(),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
RuleEval {
|
|
||||||
geom: Rc::new(geom.transform(&xf)),
|
|
||||||
final_geom: Rc::new(final_geom.transform(&xf)),
|
|
||||||
children: vec![
|
|
||||||
Child {
|
|
||||||
rule: Rc::new(next_rule),
|
|
||||||
xf: Transform::new(),
|
|
||||||
arg_vals: (0..n).collect(),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Rule {
|
|
||||||
eval: Rc::new(recur),
|
|
||||||
ctxt: TorusCtxt {
|
|
||||||
init: true,
|
|
||||||
count: 0,
|
|
||||||
stack: [
|
|
||||||
Transform::new().translate(0.0, rad3, 0.0),
|
|
||||||
Transform::new().translate(0.0, rad2, 0.0),
|
|
||||||
Transform::new().translate(rad, 0.0, 0.0),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
pub fn twisty_torus_hardcode() -> Rule<()> {
|
|
||||||
let subdiv = 8;
|
|
||||||
let seed = 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),
|
|
||||||
];
|
|
||||||
let xf = Transform::new().rotate(&Vector3::x_axis(), -0.9);
|
|
||||||
let seed = util::subdivide_cycle(&xf.transform(&seed), subdiv);
|
|
||||||
let incr = Transform { mtx: Mat4::from_vec(vec![
|
|
||||||
0.955234, 0.29576725, -0.0070466697, 0.0,
|
|
||||||
-0.29581502, 0.9552189, -0.007100463, 0.0,
|
|
||||||
0.004630968, 0.008867174, 0.99994993, 0.0,
|
|
||||||
-0.034161568, 0.290308, 0.07295418, 0.9999999,
|
|
||||||
])};
|
|
||||||
|
|
||||||
let n = seed.len();
|
|
||||||
|
|
||||||
let next = incr.transform(&seed);
|
|
||||||
let geom = Rc::new(util::zigzag_to_parent(next.clone(), n));
|
|
||||||
let (vc, faces) = util::connect_convex(&next, true);
|
|
||||||
let final_geom = Rc::new(OpenMesh {
|
|
||||||
verts: vec![vc],
|
|
||||||
alias_verts: (0..(n+1)).collect(), // TODO: Fix parent/connect_convex
|
|
||||||
faces: faces,
|
|
||||||
});
|
|
||||||
|
|
||||||
let rad = 1.0;
|
|
||||||
let rad2 = 8.0;
|
|
||||||
let rad3 = 24.0;
|
|
||||||
|
|
||||||
let start = Transform::new().translate(0.0, rad3, 0.0) * Transform::new().translate(0.0, rad2, 0.0) * Transform::new().translate(rad, 0.0, 0.0);
|
|
||||||
|
|
||||||
let recur = move |self_: Rc<Rule<()>>| -> RuleEval<()> {
|
|
||||||
RuleEval {
|
|
||||||
geom: geom.clone(),
|
|
||||||
final_geom: final_geom.clone(),
|
|
||||||
children: vec![
|
|
||||||
Child {
|
|
||||||
rule: self_.clone(),
|
|
||||||
xf: incr,
|
|
||||||
arg_vals: (0..n).collect(),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let start = move |self_: Rc<Rule<()>>| -> RuleEval<()> {
|
|
||||||
let mut s2 = seed.clone();
|
|
||||||
let (centroid, f) = util::connect_convex(&s2, false);
|
|
||||||
s2.push(centroid);
|
|
||||||
let n2 = s2.len();
|
|
||||||
let g = OpenMesh { verts: s2, faces: f, alias_verts: vec![] };
|
|
||||||
RuleEval {
|
|
||||||
geom: Rc::new(g.transform(&xf)),
|
|
||||||
final_geom: Rc::new(prim::empty_mesh()),
|
|
||||||
children: vec![
|
|
||||||
Child {
|
|
||||||
rule: Rc::new(Rule { eval: Rc::new(recur.clone()), ctxt: () }),
|
|
||||||
xf: incr,
|
|
||||||
arg_vals: (0..n2).collect(),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Rule {
|
|
||||||
eval: Rc::new(start),
|
|
||||||
ctxt: (),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This was a mistake that I'd like to understand later:
|
pub fn tri_split(&mut self, xform: Transform, b: [usize; 3]) {
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct WindChimeCtxt {
|
if self.limit_check(&xform) {
|
||||||
init: bool,
|
self.faces.extend_from_slice(&b);
|
||||||
count: usize,
|
return;
|
||||||
stack: [Transform; 3],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wind_chime_mistake_thing() -> Rule<WindChimeCtxt> {
|
let g = xform.transform(&self.base);
|
||||||
let subdiv = 8;
|
let (a, _) = self.verts.append_indexed(g);
|
||||||
let seed = vec![
|
let t = a+3;
|
||||||
vertex(-0.5, -0.5, 0.0),
|
let (tm01, tm12, tm20) = (t+3, t+4, t+5);
|
||||||
vertex(-0.5, 0.5, 0.0),
|
|
||||||
vertex( 0.5, 0.5, 0.0),
|
|
||||||
vertex( 0.5, -0.5, 0.0),
|
|
||||||
];
|
|
||||||
let seed = util::subdivide_cycle(&seed, subdiv);
|
|
||||||
|
|
||||||
let n = seed.len();
|
self.faces.extend_from_slice(&[
|
||||||
let geom = Rc::new(util::zigzag_to_parent(seed.clone(), n));
|
// Outer:
|
||||||
let (vc, faces) = util::connect_convex(&seed, true);
|
tm01, b[1], t+1,
|
||||||
let final_geom = Rc::new(OpenMesh {
|
tm01, t+0, b[0],
|
||||||
verts: vec![vc],
|
tm01, b[0], b[1],
|
||||||
alias_verts: (0..(n + 1)).collect(), // TODO: Check with parents (zigzag/connect_convex)
|
tm12, b[2], t+2,
|
||||||
faces: faces,
|
tm12, t+1, b[1],
|
||||||
});
|
tm12, b[1], b[2],
|
||||||
|
tm20, b[0], t+0,
|
||||||
let rad = 1.0;
|
tm20, t+2, b[2],
|
||||||
let rad2 = 8.0;
|
tm20, b[2], b[0],
|
||||||
let dx0 = 2.0;
|
// Inner:
|
||||||
let ang = 0.1;
|
tm01, tm12, tm20,
|
||||||
|
|
||||||
let recur = move |self_: Rc<Rule<WindChimeCtxt>>| -> RuleEval<WindChimeCtxt> {
|
|
||||||
let x = &Vector3::x_axis();
|
|
||||||
let z = &Vector3::z_axis();
|
|
||||||
let stack = self_.ctxt.stack;
|
|
||||||
let count = self_.ctxt.count;
|
|
||||||
let next_rule = Rule {
|
|
||||||
eval: self_.eval.clone(),
|
|
||||||
ctxt: WindChimeCtxt {
|
|
||||||
init: false,
|
|
||||||
count: count + 1,
|
|
||||||
stack: [
|
|
||||||
Transform::new().rotate(x, 0.01) * stack[0],
|
|
||||||
// stack[0], //Transform::new().rotate(z, 0.05 * (count as f32)).translate(0.0, rad2, 0.0),
|
|
||||||
Transform::new().rotate(z, 0.30) * stack[1],
|
|
||||||
Transform::new().translate(0.1, 0.0, 0.0) * stack[2],
|
|
||||||
],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
let xf = stack.iter().fold(Transform::new(), |acc,m| acc * (*m));
|
|
||||||
if self_.ctxt.init {
|
|
||||||
let mut s2 = seed.clone();
|
|
||||||
let (centroid, f) = util::connect_convex(&s2, false);
|
|
||||||
s2.push(centroid);
|
|
||||||
let n2 = s2.len();
|
|
||||||
let g = OpenMesh { verts: s2, faces: f, alias_verts: vec![] };
|
|
||||||
RuleEval {
|
|
||||||
geom: Rc::new(g.transform(&xf)),
|
|
||||||
final_geom: Rc::new(prim::empty_mesh()),
|
|
||||||
children: vec![
|
|
||||||
Child {
|
|
||||||
rule: Rc::new(next_rule),
|
|
||||||
xf: Transform::new(),
|
|
||||||
arg_vals: (0..n2).collect(),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
RuleEval {
|
|
||||||
geom: Rc::new(geom.transform(&xf)),
|
|
||||||
final_geom: Rc::new(final_geom.transform(&xf)),
|
|
||||||
children: vec![
|
|
||||||
Child {
|
|
||||||
rule: Rc::new(next_rule),
|
|
||||||
xf: Transform::new(),
|
|
||||||
arg_vals: (0..n).collect(),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Rule {
|
|
||||||
eval: Rc::new(recur),
|
|
||||||
ctxt: WindChimeCtxt {
|
|
||||||
init: true,
|
|
||||||
count: 0,
|
|
||||||
stack: [
|
|
||||||
Transform::new().translate(0.0, rad2, 0.0),
|
|
||||||
Transform::new().translate(rad, 0.0, 0.0),
|
|
||||||
Transform::new(), // .translate(dx0, 0.0, 0.0),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
pub fn ramhorn() -> Rule<()> {
|
|
||||||
|
|
||||||
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).
|
|
||||||
rotate(&v, 0.3).
|
|
||||||
scale(0.9);
|
|
||||||
|
|
||||||
let (a0, a1, a2, a3, s4, s5, s6, s7);
|
|
||||||
let seed = vec_indexed![
|
|
||||||
@a0 VertexUnion::Arg(0),
|
|
||||||
@a1 VertexUnion::Arg(1),
|
|
||||||
@a2 VertexUnion::Arg(2),
|
|
||||||
@a3 VertexUnion::Arg(3),
|
|
||||||
@s4 VertexUnion::Vertex(vertex(-0.5, -0.5, 1.0)),
|
|
||||||
@s5 VertexUnion::Vertex(vertex(-0.5, 0.5, 1.0)),
|
|
||||||
@s6 VertexUnion::Vertex(vertex( 0.5, 0.5, 1.0)),
|
|
||||||
@s7 VertexUnion::Vertex(vertex( 0.5, -0.5, 1.0)),
|
|
||||||
];
|
|
||||||
let geom = MeshFunc {
|
|
||||||
verts: seed,
|
|
||||||
faces: vec![
|
|
||||||
s5, a0, s4,
|
|
||||||
a1, a0, s5,
|
|
||||||
s6, a1, s5,
|
|
||||||
a2, a1, s6,
|
|
||||||
s7, a2, s6,
|
|
||||||
a3, a2, s7,
|
|
||||||
s4, a3, s7,
|
|
||||||
a0, a3, s4,
|
|
||||||
],
|
|
||||||
};
|
|
||||||
let final_geom = MeshFunc {
|
|
||||||
verts: vert_args(s4..s7),
|
|
||||||
// TODO: Factor out this repetition
|
|
||||||
faces: vec![
|
|
||||||
0, 2, 1,
|
|
||||||
0, 3, 2,
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
let geom2 = Rc::new(geom.transform(&incr));
|
|
||||||
let fgeom2 = Rc::new(final_geom);
|
|
||||||
let recur = rule_fn!(() => |self_, geom2, fgeom2| {
|
|
||||||
RuleEval {
|
|
||||||
geom: geom2,
|
|
||||||
final_geom: fgeom2,
|
|
||||||
children: vec![
|
|
||||||
child!(self_, incr, s4, s5, s6, s7),
|
|
||||||
],
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
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, 1.0).
|
|
||||||
scale(0.5).
|
|
||||||
translate(0.0, 0.0, -1.0)
|
|
||||||
};
|
|
||||||
|
|
||||||
let start = move |_| -> RuleEval<()> {
|
|
||||||
|
|
||||||
RuleEval {
|
|
||||||
geom: Rc::new(MeshFunc {
|
|
||||||
verts: vec![
|
|
||||||
// 'Top' vertices:
|
|
||||||
VertexUnion::Vertex(vertex(-0.5, -0.5, 1.0)), // 0 (above 9)
|
|
||||||
VertexUnion::Vertex(vertex(-0.5, 0.5, 1.0)), // 1 (above 10)
|
|
||||||
VertexUnion::Vertex(vertex( 0.5, 0.5, 1.0)), // 2 (above 11)
|
|
||||||
VertexUnion::Vertex(vertex( 0.5, -0.5, 1.0)), // 3 (above 12)
|
|
||||||
// Top edge midpoints:
|
|
||||||
VertexUnion::Vertex(vertex(-0.5, 0.0, 1.0)), // 4 (connects 0-1)
|
|
||||||
VertexUnion::Vertex(vertex( 0.0, 0.5, 1.0)), // 5 (connects 1-2)
|
|
||||||
VertexUnion::Vertex(vertex( 0.5, 0.0, 1.0)), // 6 (connects 2-3)
|
|
||||||
VertexUnion::Vertex(vertex( 0.0, -0.5, 1.0)), // 7 (connects 3-0)
|
|
||||||
// Top middle:
|
|
||||||
VertexUnion::Vertex(vertex( 0.0, 0.0, 1.0)), // 8
|
|
||||||
// 'Bottom' vertices:
|
|
||||||
VertexUnion::Vertex(vertex(-0.5, -0.5, 0.0)), // 9
|
|
||||||
VertexUnion::Vertex(vertex(-0.5, 0.5, 0.0)), // 10
|
|
||||||
VertexUnion::Vertex(vertex( 0.5, 0.5, 0.0)), // 11
|
|
||||||
VertexUnion::Vertex(vertex( 0.5, -0.5, 0.0)), // 12
|
|
||||||
],
|
|
||||||
faces: vec![
|
|
||||||
// bottom face:
|
|
||||||
9, 10, 11,
|
|
||||||
9, 11, 12,
|
|
||||||
// two faces straddling edge from vertex 0:
|
|
||||||
9, 0, 4,
|
|
||||||
9, 7, 0,
|
|
||||||
// two faces straddling edge from vertex 1:
|
|
||||||
10, 1, 5,
|
|
||||||
10, 4, 1,
|
|
||||||
// two faces straddling edge from vertex 2:
|
|
||||||
11, 2, 6,
|
|
||||||
11, 5, 2,
|
|
||||||
// two faces straddling edge from vertex 3:
|
|
||||||
12, 3, 7,
|
|
||||||
12, 6, 3,
|
|
||||||
// four faces from edge (0,1), (1,2), (2,3), (3,0):
|
|
||||||
9, 4, 10,
|
|
||||||
10, 5, 11,
|
|
||||||
11, 6, 12,
|
|
||||||
12, 7, 9,
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
final_geom: Rc::new(prim::empty_mesh().to_meshfunc()),
|
|
||||||
children: vec![
|
|
||||||
child!(rule!(recur, ()), opening_xform(0), 5, 2, 6, 8),
|
|
||||||
child!(rule!(recur, ()), opening_xform(1), 4, 1, 5, 8),
|
|
||||||
child!(rule!(recur, ()), opening_xform(2), 7, 0, 4, 8),
|
|
||||||
child!(rule!(recur, ()), opening_xform(3), 6, 3, 7, 8),
|
|
||||||
// TODO: These vertex mappings appear to be right.
|
|
||||||
// Explain *why* they are right.
|
|
||||||
],
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Rule { eval: Rc::new(start), ctxt: () }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn test_parametric() -> Mesh {
|
|
||||||
|
|
||||||
let base_verts: Vec<Vertex> = vec![
|
|
||||||
vertex(-1.0, -1.0, 0.0),
|
|
||||||
vertex(-1.0, 1.0, 0.0),
|
|
||||||
vertex( 1.0, 1.0, 0.0),
|
|
||||||
vertex( 1.0, -1.0, 0.0),
|
|
||||||
];
|
|
||||||
let base_verts = util::subdivide_cycle(&base_verts, 2);
|
|
||||||
//let base_verts = util::subdivide_cycle(&base_verts, 16);
|
|
||||||
|
|
||||||
let t0 = 0.0;
|
|
||||||
let t1 = 15.0;
|
|
||||||
let xform = |t: f32| -> Transform {
|
|
||||||
id().
|
|
||||||
translate(0.0, 0.0, t/5.0).
|
|
||||||
rotate(&Vector3::z_axis(), -t/2.0).
|
|
||||||
scale((0.8).powf(t))
|
|
||||||
};
|
|
||||||
|
|
||||||
crate::rule::parametric_mesh(base_verts, xform, t0, t1, 0.01)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn test_dcel(fname: &str) {
|
|
||||||
let mut mesh: dcel::DCELMesh<Vertex> = dcel::DCELMesh::new();
|
|
||||||
let (f1, _) = mesh.add_face([
|
|
||||||
VertSpec::New(vertex(-0.5, -0.5, 0.0)),
|
|
||||||
VertSpec::New(vertex(-0.5, 0.5, 0.0)),
|
|
||||||
VertSpec::New(vertex( 0.5, 0.5, 0.0)),
|
|
||||||
]);
|
]);
|
||||||
mesh.check();
|
|
||||||
let (f2, edges) = mesh.add_face_twin1(mesh.faces[f1].halfedge, vertex(0.0, 0.0, 1.0));
|
|
||||||
mesh.check();
|
|
||||||
|
|
||||||
// From add_face_twin1, edges[0] is always the 'shared' edge:
|
self.tri_split(xform * self.splits[0], [t+0, tm01, tm20]);
|
||||||
let edge = edges[0];
|
self.tri_split(xform * self.splits[1], [t+1, tm12, tm01]);
|
||||||
let twin = {
|
self.tri_split(xform * self.splits[2], [t+2, tm20, tm12]);
|
||||||
let he = &mesh.halfedges[edge];
|
|
||||||
if he.has_twin {
|
|
||||||
he.twin_halfedge
|
|
||||||
} else {
|
|
||||||
panic!("Can't find shared edge!");
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
println!("Shared edges = {},{}", edge, twin);
|
|
||||||
|
|
||||||
let ep = mesh.halfedges[edge].prev_halfedge;
|
fn limit_check(&self, xform: &Transform) -> bool {
|
||||||
let en = mesh.halfedges[edge].next_halfedge;
|
// Assume all scales are the same (for now)
|
||||||
let tp = mesh.halfedges[twin].prev_halfedge;
|
let (s, _, _) = xform.get_scale();
|
||||||
let tn = mesh.halfedges[twin].next_halfedge;
|
return s < 0.01;
|
||||||
println!("Connecting halfedges: {} and {}, {} and {}", en, tp, tn, ep);
|
}
|
||||||
|
|
||||||
println!("DCEL mesh = {}", mesh);
|
|
||||||
// As we're making *twin* halfedges, we go against the edge
|
|
||||||
// direction:
|
|
||||||
let (f3, _) = mesh.add_face_twin2(en, tp);
|
|
||||||
mesh.check();
|
|
||||||
let (f4, _) = mesh.add_face_twin2(tn, ep);
|
|
||||||
mesh.check();
|
|
||||||
|
|
||||||
println!("f1 verts: {:?}", mesh.face_to_verts(f1));
|
|
||||||
println!("f2 verts: {:?}", mesh.face_to_verts(f2));
|
|
||||||
println!("f3 verts: {:?}", mesh.face_to_verts(f3));
|
|
||||||
println!("f4 verts: {:?}", mesh.face_to_verts(f4));
|
|
||||||
|
|
||||||
//println!("DCEL mesh: ");
|
|
||||||
//mesh.print();
|
|
||||||
|
|
||||||
let faces = mesh.full_subdiv_face(f1, vec![
|
|
||||||
vertex(-0.5, 0.0, 0.0),
|
|
||||||
vertex(0.0, 0.5, 0.0),
|
|
||||||
vertex(0.0, 0.0, 0.0),
|
|
||||||
]);
|
|
||||||
println!("full_subdiv_face returned: {:?}", faces);
|
|
||||||
|
|
||||||
//println!("DCEL mesh after subdiv");
|
|
||||||
//mesh.check();
|
|
||||||
//mesh.print();
|
|
||||||
|
|
||||||
let mesh_conv = mesh.convert_mesh(|i| i);
|
|
||||||
|
|
||||||
println!("Mesh = {:?}", mesh_conv);
|
|
||||||
|
|
||||||
mesh_conv.write_stl_file(fname).unwrap();
|
|
||||||
}
|
}
|
||||||
95
src/lib.rs
95
src/lib.rs
@ -1,12 +1,10 @@
|
|||||||
pub mod mesh;
|
pub mod mesh;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod rule;
|
|
||||||
pub mod prim;
|
pub mod prim;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod util;
|
pub mod util;
|
||||||
pub mod xform;
|
pub mod xform;
|
||||||
pub mod examples;
|
pub mod examples;
|
||||||
pub mod dcel;
|
|
||||||
|
|
||||||
//pub use crate::examples;
|
//pub use crate::examples;
|
||||||
//pub use crate::openmesh::test_thing;
|
//pub use crate::openmesh::test_thing;
|
||||||
@ -16,31 +14,8 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use rule::Rule;
|
|
||||||
use nalgebra::*;
|
use nalgebra::*;
|
||||||
|
|
||||||
fn run_test<S>(rule: Rule<S>, iters: usize, name: &str, use_old: bool) {
|
|
||||||
let r = Rc::new(rule);
|
|
||||||
println!("---------------------------------------------------");
|
|
||||||
println!("Running {} with {}...",
|
|
||||||
name, if use_old { "to_mesh" } else { "to_mesh_iter" });
|
|
||||||
if false {
|
|
||||||
let start = Instant::now();
|
|
||||||
let n = 5;
|
|
||||||
for _ in 0..n {
|
|
||||||
Rule::to_mesh_iter(r.clone(), iters);
|
|
||||||
}
|
|
||||||
let elapsed = start.elapsed();
|
|
||||||
println!("DEBUG: {} ms per run", elapsed.as_millis() / n);
|
|
||||||
}
|
|
||||||
let mesh_fn = if use_old { Rule::to_mesh } else { Rule::to_mesh_iter };
|
|
||||||
let (mesh, nodes) = mesh_fn(r.clone(), iters);
|
|
||||||
println!("Evaluated {} rules to {} verts", nodes, mesh.verts.len());
|
|
||||||
let fname = format!("{}.stl", name);
|
|
||||||
println!("Writing {}...", fname);
|
|
||||||
mesh.to_mesh().write_stl_file(&fname).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn xform_order() {
|
fn xform_order() {
|
||||||
let geom = prim::cube();
|
let geom = prim::cube();
|
||||||
@ -114,69 +89,19 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn sierpinski() { run_test(examples::sierpinski(), 6, "sierpinski", false); }
|
fn sierpinski() {
|
||||||
/*
|
let name = "sierpinski";
|
||||||
#[test]
|
println!("---------------------------------------------------");
|
||||||
fn twist() {
|
let b = examples::Sierpinski::new(0.50, 0.10, 0.0);
|
||||||
run_test(examples::twist(1.0, 2), 200, "screw", false);
|
//let b = examples::Sierpinski::new(0.51, 0.10, 0.1);
|
||||||
}
|
let m = b.run();
|
||||||
|
|
||||||
#[test]
|
println!("Got {} verts...", m.verts.len());
|
||||||
fn twisty_torus() {
|
|
||||||
run_test(examples::twisty_torus(), 3000, "twisty_torus", false);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
let fname = format!("{}.stl", name);
|
||||||
fn twisty_torus_hardcode() {
|
println!("Writing {}...", fname);
|
||||||
run_test(examples::twisty_torus_hardcode(), 1000, "twisty_torus_hardcode", false);
|
m.write_stl_file(&fname).unwrap();
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[ignore]
|
|
||||||
fn twisty_torus_full() {
|
|
||||||
run_test(examples::twisty_torus(), 40000, "twisty_torus_full", false);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[ignore]
|
|
||||||
fn wind_chime_mistake_thing() {
|
|
||||||
run_test(examples::wind_chime_mistake_thing(), 400, "wind_chime_mistake_thing", false);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn nest_spiral_2() {
|
|
||||||
run_test(examples::nest_spiral_2(), 200, "nest_spiral_2", false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This one is very time-consuming to run:
|
|
||||||
#[test]
|
|
||||||
#[ignore]
|
|
||||||
fn twist_full() {
|
|
||||||
let f = 40;
|
|
||||||
run_test(examples::twist(f as f32, 128), 100*f, "screw_full", false);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn ramhorn() {
|
|
||||||
run_test(examples::ramhorn(), 100, "ram_horn3", false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
#[test]
|
|
||||||
fn ramhorn_branch_random() {
|
|
||||||
run_test(examples::ramhorn_branch_random(24, 0.25), 32, "ram_horn_branch_random", false);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_parametric() {
|
|
||||||
examples::test_parametric().write_stl_file("test_parametric.stl").unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_dcel() {
|
|
||||||
examples::test_dcel("test_dcel.stl");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// need this for now:
|
// need this for now:
|
||||||
|
|||||||
182
src/mesh.rs
182
src/mesh.rs
@ -1,6 +1,5 @@
|
|||||||
use std::fs::OpenOptions;
|
use std::fs::OpenOptions;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::borrow::Borrow;
|
|
||||||
|
|
||||||
use crate::xform::{Vertex, Transform};
|
use crate::xform::{Vertex, Transform};
|
||||||
|
|
||||||
@ -61,185 +60,4 @@ impl Mesh {
|
|||||||
|
|
||||||
stl_io::write_stl(writer, triangles.iter())
|
stl_io::write_stl(writer, triangles.iter())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_meshfunc(&self) -> MeshFunc {
|
|
||||||
MeshFunc {
|
|
||||||
faces: self.faces.clone(),
|
|
||||||
verts: self.verts.iter().map(|v| VertexUnion::Vertex(*v)).collect(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub enum VertexUnion {
|
|
||||||
/// A concrete vertex.
|
|
||||||
Vertex(Vertex),
|
|
||||||
/// A vertex argument - something like an argument to a function with
|
|
||||||
/// the given positional index.
|
|
||||||
///
|
|
||||||
/// The job of `MeshFunc.connect` is to bind these arguments to concrete
|
|
||||||
/// vertices.
|
|
||||||
Arg(usize),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn vert_args<T: IntoIterator<Item = usize>>(v: T) -> Vec<VertexUnion> {
|
|
||||||
v.into_iter().map(|i| VertexUnion::Arg(i)).collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A face-vertex mesh, some of whose vertices may be 'vertex arguments'
|
|
||||||
/// rather than concrete vertices. The job of `connect()` is to resolve
|
|
||||||
/// these.
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct MeshFunc {
|
|
||||||
pub verts: Vec<VertexUnion>,
|
|
||||||
/// Indices of triangles (taken as every 3 values).
|
|
||||||
pub faces: Vec<usize>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MeshFunc {
|
|
||||||
pub fn to_mesh(&self) -> Mesh {
|
|
||||||
Mesh {
|
|
||||||
faces: self.faces.clone(),
|
|
||||||
verts: self.verts.iter().map(|v| match *v {
|
|
||||||
VertexUnion::Vertex(v) => v,
|
|
||||||
VertexUnion::Arg(_) => panic!("Mesh still has vertex arguments!"),
|
|
||||||
}).collect(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a new `MeshFunc` whose concrete vertices have
|
|
||||||
/// been transformed. Note that alias vertices are left untouched.
|
|
||||||
pub fn transform(&self, xfm: &Transform) -> MeshFunc {
|
|
||||||
let v = self.verts.iter().map(|v| {
|
|
||||||
match v {
|
|
||||||
VertexUnion::Vertex(v) => VertexUnion::Vertex(xfm.mtx * v),
|
|
||||||
a @ _ => a.clone(),
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
MeshFunc {
|
|
||||||
verts: v.collect(),
|
|
||||||
faces: self.faces.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Appends any number of meshes together. Returns both a single
|
|
||||||
/// mesh, and a vector which gives the offset by which each
|
|
||||||
/// corresponding input mesh was shifted. That is, for the i'th
|
|
||||||
/// index in `meshes`, all of its triangle indices were shifted by
|
|
||||||
/// the i'th offset in the resultant mesh.
|
|
||||||
pub fn append<T, U>(meshes: T) -> (MeshFunc, Vec<usize>)
|
|
||||||
where U: Borrow<MeshFunc>,
|
|
||||||
T: IntoIterator<Item=U>
|
|
||||||
{
|
|
||||||
let mut offsets: Vec<usize> = vec![];
|
|
||||||
let mut v: Vec<VertexUnion> = vec![];
|
|
||||||
let mut f: Vec<usize> = vec![];
|
|
||||||
for mesh_ in meshes {
|
|
||||||
let mesh = mesh_.borrow();
|
|
||||||
|
|
||||||
// Position in 'verts' at which we're appending
|
|
||||||
// mesh.verts, which we need to know to shift indices:
|
|
||||||
let offset = v.len();
|
|
||||||
offsets.push(offset);
|
|
||||||
|
|
||||||
// Copy all vertices:
|
|
||||||
v.append(&mut mesh.verts.clone());
|
|
||||||
// Append its faces, applying offset:
|
|
||||||
f.extend(mesh.faces.iter().map(|n| n + offset));
|
|
||||||
}
|
|
||||||
|
|
||||||
(MeshFunc { verts: v, faces: f }, offsets)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Treat this mesh as a 'parent' mesh to connect with any number
|
|
||||||
/// of 'child' meshes, all of them paired with their respective
|
|
||||||
/// vertex argument values (i.e. `arg_vals` from `Child`).
|
|
||||||
///
|
|
||||||
/// This returns a tuple of (new `MeshFunc`, new `arg_vals`), where
|
|
||||||
/// `arg_vals[i]` is the new index of `self.verts[i]` in the
|
|
||||||
/// returned `MeshFunc`.
|
|
||||||
pub fn connect<T, U>(&self, children: T) -> (MeshFunc, Vec<Vec<usize>>)
|
|
||||||
where U: Borrow<MeshFunc>,
|
|
||||||
T: IntoIterator<Item=(U, Vec<usize>)>
|
|
||||||
{
|
|
||||||
// TODO: Clean up this description a bit
|
|
||||||
// TODO: Clean up Vec<usize> stuff
|
|
||||||
|
|
||||||
// Copy body vertices & faces:
|
|
||||||
let mut verts: Vec<VertexUnion> = self.verts.clone();
|
|
||||||
let mut faces = self.faces.clone();
|
|
||||||
|
|
||||||
let mut remaps: Vec<Vec<usize>> = vec![];
|
|
||||||
|
|
||||||
for (child_, arg_vals) in children {
|
|
||||||
let child = child_.borrow();
|
|
||||||
|
|
||||||
// 'offset' corresponds to the position in 'verts' at
|
|
||||||
// which we begin appending everything in 'child.verts'.
|
|
||||||
let offset = verts.len();
|
|
||||||
|
|
||||||
// 'remap[i]' - if 'child.verts[i]' is a Vertex, not an Arg -
|
|
||||||
// will contain the index in 'verts' that this vertex was
|
|
||||||
// copied to. (This is needed because below we copy only
|
|
||||||
// the Vertex elements, not the Arg ones.)
|
|
||||||
let mut remap = vec![0; child.verts.len()];
|
|
||||||
let mut j = 0;
|
|
||||||
|
|
||||||
// Like mentioned, copy just the Vertex in 'child.verts':
|
|
||||||
verts.extend(child.verts.iter().enumerate().filter_map(|(i,v)| {
|
|
||||||
match v {
|
|
||||||
VertexUnion::Vertex(_) => {
|
|
||||||
// TODO: A less-side-effectful way?
|
|
||||||
remap[i] = offset + j;
|
|
||||||
j += 1;
|
|
||||||
Some(v.clone())
|
|
||||||
},
|
|
||||||
VertexUnion::Arg(_) => None,
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
// So, note that:
|
|
||||||
// 1st Vertex in 'child.verts' went to 'verts[offset + 0]'.
|
|
||||||
// 2nd Vertex in 'child.verts' went to 'verts[offset + 1]'.
|
|
||||||
// 3rd Vertex in 'child.verts' went to 'verts[offset + 2]'.
|
|
||||||
// and so forth.
|
|
||||||
// Since this skips Arg elements, we used 'remap' to
|
|
||||||
// store the mapping: 'child.verts[i]' was copied to
|
|
||||||
// 'verts[remap[i]].'
|
|
||||||
|
|
||||||
// We then use 'remap' below to update the indices in
|
|
||||||
// 'child.faces' to vertices in the returned mesh.
|
|
||||||
//
|
|
||||||
// Also, we didn't copy Arg elements from 'child.verts', but
|
|
||||||
// we do use them below - if 'child.faces' makes a
|
|
||||||
// reference to this Arg, we resolve it with the help of
|
|
||||||
// 'arg_vals', passed in by the caller.
|
|
||||||
faces.extend(child.faces.iter().map(|n| {
|
|
||||||
let f = match child.verts[*n] {
|
|
||||||
VertexUnion::Vertex(_) => remap[*n],
|
|
||||||
VertexUnion::Arg(m) => {
|
|
||||||
println!("remap face: vert {} arg {} = vert {} ({:?})",
|
|
||||||
*n, m, arg_vals[m], verts[arg_vals[m]]
|
|
||||||
);
|
|
||||||
arg_vals[m]
|
|
||||||
},
|
|
||||||
};
|
|
||||||
if f >= verts.len() {
|
|
||||||
panic!("face >= num_verts")
|
|
||||||
}
|
|
||||||
f
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Since the caller might need this same remapping (there
|
|
||||||
// could be an Arg that referred to these vertices), we
|
|
||||||
// save this information to return it later:
|
|
||||||
remaps.push(remap);
|
|
||||||
}
|
|
||||||
|
|
||||||
let m = MeshFunc {
|
|
||||||
verts: verts,
|
|
||||||
faces: faces,
|
|
||||||
};
|
|
||||||
(m, remaps)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
10
src/prim.rs
10
src/prim.rs
@ -1,4 +1,4 @@
|
|||||||
use crate::mesh::{Mesh, MeshFunc};
|
use crate::mesh::{Mesh};
|
||||||
use crate::xform::{vertex, Transform};
|
use crate::xform::{vertex, Transform};
|
||||||
|
|
||||||
/// Returns an empty mesh (no vertices, no faces).
|
/// Returns an empty mesh (no vertices, no faces).
|
||||||
@ -9,14 +9,6 @@ pub fn empty_mesh() -> Mesh {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an empty MeshFn (no vertices, no faces, thus no args).
|
|
||||||
pub fn empty_meshfunc() -> MeshFunc {
|
|
||||||
MeshFunc {
|
|
||||||
verts: vec![],
|
|
||||||
faces: vec![],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a cube of sidelength one centered at (0,0,0).
|
/// Returns a cube of sidelength one centered at (0,0,0).
|
||||||
pub fn cube() -> Mesh {
|
pub fn cube() -> Mesh {
|
||||||
Mesh {
|
Mesh {
|
||||||
|
|||||||
498
src/rule.rs
498
src/rule.rs
@ -1,498 +0,0 @@
|
|||||||
use std::borrow::Borrow;
|
|
||||||
use std::rc::Rc;
|
|
||||||
use std::f32;
|
|
||||||
|
|
||||||
use crate::mesh::{Mesh, MeshFunc, VertexUnion};
|
|
||||||
use crate::xform::{Transform, Vertex};
|
|
||||||
|
|
||||||
pub type RuleFn<S> = Rc<dyn Fn(Rc<Rule<S>>) -> RuleEval<S>>;
|
|
||||||
|
|
||||||
/// Definition of a rule. In general, a `Rule`:
|
|
||||||
///
|
|
||||||
/// - produces geometry when it is evaluated
|
|
||||||
/// - tells what other rules to invoke, and what to do with their
|
|
||||||
/// geometry
|
|
||||||
pub struct Rule<S> {
|
|
||||||
pub eval: RuleFn<S>,
|
|
||||||
pub ctxt: S,
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: It may be possible to have just a 'static' rule that requires
|
|
||||||
// no function call.
|
|
||||||
// TODO: Do I benefit with Rc<Rule> below so Rule can be shared?
|
|
||||||
|
|
||||||
// TODO: Why *can't* I make this FnOnce?
|
|
||||||
|
|
||||||
// The above looks like it is going to require a lifetime parameter
|
|
||||||
// regardless, in which case I don't really need Box.
|
|
||||||
|
|
||||||
/// `RuleEval` supplies the results of evaluating some `Rule` for one
|
|
||||||
/// iteration: it contains the geometry produced at this step
|
|
||||||
/// (`geom`), and it tells what to do next depending on whether
|
|
||||||
/// recursion continues further, or is stopped here (due to hitting
|
|
||||||
/// some limit of iterations or some lower limit on overall scale).
|
|
||||||
///
|
|
||||||
/// That is:
|
|
||||||
/// - if recursion stops, `final_geom` is connected with `geom`.
|
|
||||||
/// - if recursion continues, the rules of `children` are evaluated,
|
|
||||||
/// and the resultant geometry is transformed and then connected with
|
|
||||||
/// `geom`.
|
|
||||||
pub struct RuleEval<S> {
|
|
||||||
/// The geometry generated at just this iteration
|
|
||||||
pub geom: Rc<MeshFunc>,
|
|
||||||
|
|
||||||
/// The "final" geometry that is merged with `geom` via
|
|
||||||
/// `connect()` in the event that recursion stops. This must be
|
|
||||||
/// in the same coordinate space as `geom`.
|
|
||||||
///
|
|
||||||
/// Parent vertex references will be resolved directly to `geom`
|
|
||||||
/// with no mapping.
|
|
||||||
/// (TODO: Does this make sense? Nowhere else do I treat Arg(n) as
|
|
||||||
/// an index - it's always a positional argument.)
|
|
||||||
pub final_geom: Rc<MeshFunc>,
|
|
||||||
|
|
||||||
/// The child invocations (used if recursion continues). The
|
|
||||||
/// 'parent' mesh, from the perspective of all geometry produced
|
|
||||||
/// by `children`, is `geom`.
|
|
||||||
pub children: Vec<Child<S>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `Child` evaluations, pairing another `Rule` with the
|
|
||||||
/// transformations and parent vertex mappings that should be applied
|
|
||||||
/// to it.
|
|
||||||
pub struct Child<S> {
|
|
||||||
|
|
||||||
/// Rule to evaluate to produce geometry
|
|
||||||
pub rule: Rc<Rule<S>>,
|
|
||||||
|
|
||||||
/// The transform to apply to all geometry produced by `rule`
|
|
||||||
/// (including its own `geom` and `final_geom` if needed, as well
|
|
||||||
/// as all sub-geometry produced recursively).
|
|
||||||
pub xf: Transform,
|
|
||||||
|
|
||||||
/// The 'argument values' to apply to vertex arguments of a `MeshFunc`
|
|
||||||
/// from `geom` and `final_geom` that `rule` produces when evaluated.
|
|
||||||
/// The values of this are treated as indices into the parent
|
|
||||||
/// `RuleEval` that produced this `Child`.
|
|
||||||
///
|
|
||||||
/// In specific: if `arg_vals[i] = j` and `rule` produces some `geom` or
|
|
||||||
/// `final_geom`, then any vertex of `VertexUnion::Arg(i)` will be mapped
|
|
||||||
/// to `geom.verts[j]` in the *parent* geometry.
|
|
||||||
pub arg_vals: Vec<usize>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! child {
|
|
||||||
( $Rule:expr, $Xform:expr, $( $Arg:expr ),* ) => {
|
|
||||||
Child {
|
|
||||||
rule: /*std::rc::Rc::new*/($Rule).clone(),
|
|
||||||
xf: $Xform,
|
|
||||||
arg_vals: vec![$($Arg,)*],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! child_iter {
|
|
||||||
( $Rule:expr, $Xform:expr, $Args:expr ) => {
|
|
||||||
Child {
|
|
||||||
rule: /*std::rc::Rc::new*/($Rule).clone(),
|
|
||||||
xf: $Xform,
|
|
||||||
arg_vals: $Args.collect(), // does this even need a macro?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! rule {
|
|
||||||
( $RuleFn:expr, $Ctxt:expr ) => {
|
|
||||||
std::rc::Rc::new(Rule {
|
|
||||||
eval: $RuleFn.clone(),
|
|
||||||
ctxt: $Ctxt,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! rule_fn {
|
|
||||||
( $Ty:ty => |$Self:ident $(,$x:ident)*| $Body:expr ) => {
|
|
||||||
{
|
|
||||||
$(let $x = $x.clone();)*
|
|
||||||
std::rc::Rc::new(move |$Self: std::rc::Rc<Rule<$Ty>>| -> RuleEval<$Ty> {
|
|
||||||
$(let $x = $x.clone();)*
|
|
||||||
let $Self = $Self.clone();
|
|
||||||
$Body
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// TODO: Shouldn't I fully-qualify Rule & RuleEval?
|
|
||||||
// TODO: Document all of the above macros
|
|
||||||
// TODO: Why must I clone twice?
|
|
||||||
|
|
||||||
impl<S> Rule<S> {
|
|
||||||
|
|
||||||
/// Convert this `Rule` to mesh data, recursively (depth first).
|
|
||||||
/// `iters_left` sets the maximum recursion depth. This returns
|
|
||||||
/// (geometry, number of rule evaluations).
|
|
||||||
pub fn to_mesh(s: Rc<Rule<S>>, iters_left: usize) -> (MeshFunc, usize) {
|
|
||||||
|
|
||||||
let mut evals = 1;
|
|
||||||
|
|
||||||
let rs: RuleEval<S> = (s.eval)(s.clone());
|
|
||||||
if iters_left <= 0 {
|
|
||||||
return ((*rs.final_geom).clone(), 1);
|
|
||||||
// TODO: This is probably wrong because of the way that
|
|
||||||
// sub.arg_vals is used below. final_geom is not supposed to
|
|
||||||
// have any vertex mapping applied.
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: This logic is more or less right, but it
|
|
||||||
// could perhaps use some un-tupling or something.
|
|
||||||
|
|
||||||
let subgeom: Vec<(MeshFunc, Vec<usize>)> = rs.children.iter().map(|sub| {
|
|
||||||
// Get sub-geometry (still un-transformed):
|
|
||||||
let (submesh, eval) = Rule::to_mesh(sub.rule.clone(), iters_left - 1);
|
|
||||||
// Tally up eval count:
|
|
||||||
evals += eval;
|
|
||||||
|
|
||||||
let m2 = submesh.transform(&sub.xf);
|
|
||||||
|
|
||||||
(m2, sub.arg_vals.clone())
|
|
||||||
// TODO: Fix clone?
|
|
||||||
}).collect();
|
|
||||||
|
|
||||||
// Connect geometry from this rule (not child rules):
|
|
||||||
return (rs.geom.connect(subgeom).0, evals);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This should be identical to to_mesh, but implemented
|
|
||||||
/// iteratively with an explicit stack rather than with recursive
|
|
||||||
/// function calls.
|
|
||||||
pub fn to_mesh_iter(s: Rc<Rule<S>>, max_depth: usize) -> (MeshFunc, usize) {
|
|
||||||
|
|
||||||
struct State<S> {
|
|
||||||
// The set of rules we're currently handling:
|
|
||||||
rules: Vec<Child<S>>,
|
|
||||||
// The next element of 'children' to handle:
|
|
||||||
next: usize,
|
|
||||||
// World transform of the *parent* of 'rules', that is,
|
|
||||||
// not including any transform of any element of 'rules'.
|
|
||||||
xf: Transform,
|
|
||||||
// How many levels 'deeper' can we recurse?
|
|
||||||
depth: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
// 'stack' stores at its last element our "current" State in
|
|
||||||
// terms of a current world transform and which Child should
|
|
||||||
// be processed next. Every element prior to this is previous
|
|
||||||
// states which must be kept around for further backtracking
|
|
||||||
// (usually because they involve multiple rules).
|
|
||||||
//
|
|
||||||
// We evaluate our own rule to initialize the stack:
|
|
||||||
let eval = (s.eval)(s.clone());
|
|
||||||
let mut stack: Vec<State<S>> = vec![State {
|
|
||||||
rules: eval.children,
|
|
||||||
next: 0,
|
|
||||||
xf: Transform::new(),
|
|
||||||
depth: max_depth,
|
|
||||||
}];
|
|
||||||
let mut geom = (*eval.geom).clone();
|
|
||||||
|
|
||||||
// Number of times we've evaluated a Rule:
|
|
||||||
let mut eval_count = 1;
|
|
||||||
|
|
||||||
// Stack depth (update at every push & pop):
|
|
||||||
let mut n = stack.len();
|
|
||||||
|
|
||||||
while !stack.is_empty() {
|
|
||||||
|
|
||||||
// s = the 'current' state:
|
|
||||||
let s = &mut stack[n-1];
|
|
||||||
let depth = s.depth;
|
|
||||||
|
|
||||||
if s.rules.is_empty() {
|
|
||||||
stack.pop();
|
|
||||||
n -= 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Evaluate the rule:
|
|
||||||
let child = &s.rules[s.next];
|
|
||||||
let mut eval = (child.rule.eval)(child.rule.clone());
|
|
||||||
eval_count += 1;
|
|
||||||
|
|
||||||
// Make an updated world transform:
|
|
||||||
let xf = s.xf * child.xf;
|
|
||||||
|
|
||||||
// This rule produced some geometry which we'll
|
|
||||||
// combine with the 'global' geometry:
|
|
||||||
let new_geom = eval.geom.transform(&xf);
|
|
||||||
|
|
||||||
// See if we can still recurse further:
|
|
||||||
if depth <= 0 {
|
|
||||||
// As we're stopping recursion, we need to connect
|
|
||||||
// final_geom with all else in order to actually close
|
|
||||||
// geometry properly:
|
|
||||||
let final_geom = eval.final_geom.transform(&xf);
|
|
||||||
// TODO: Fix the awful hack below. I do this only to
|
|
||||||
// generate an identity mapping for arg_vals when I don't
|
|
||||||
// actually need arg_vals.
|
|
||||||
let m = {
|
|
||||||
let mut m_ = 0;
|
|
||||||
for v in &final_geom.verts {
|
|
||||||
match *v {
|
|
||||||
VertexUnion::Arg(a) => {
|
|
||||||
if a > m_ {
|
|
||||||
m_ = a;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
VertexUnion::Vertex(_) => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m_ + 1
|
|
||||||
};
|
|
||||||
let arg_vals: Vec<usize> = (0..m).collect();
|
|
||||||
let (geom2, _) = new_geom.connect(vec![(final_geom, arg_vals)]);
|
|
||||||
|
|
||||||
geom = geom.connect(vec![(geom2, child.arg_vals.clone())]).0;
|
|
||||||
// TODO: Fix clone?
|
|
||||||
|
|
||||||
// If we end recursion on one child, we must end it
|
|
||||||
// similarly on every sibling (i.e. get its geometry &
|
|
||||||
// final geometry, and merge it in) - so we increment
|
|
||||||
// s.next and let the loop re-run.
|
|
||||||
s.next += 1;
|
|
||||||
if s.next >= s.rules.len() {
|
|
||||||
// Backtrack only at the last child:
|
|
||||||
stack.pop();
|
|
||||||
n -= 1;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let (g, remaps) = geom.connect(vec![(new_geom, child.arg_vals.clone())]);
|
|
||||||
geom = g;
|
|
||||||
|
|
||||||
// 'eval.children' may contain (via 'arg_vals') references to
|
|
||||||
// indices of 'new_geom'. However, we don't connect() to
|
|
||||||
// 'new_geom', but to the global geometry we just merged it
|
|
||||||
// into. To account for this, we must remap 'arg_vals' by the
|
|
||||||
// mapping connect() gave us:
|
|
||||||
let remap = &remaps[0];
|
|
||||||
// (We pass a one-element vector to geom.connect() above
|
|
||||||
// so remaps always has just one element.)
|
|
||||||
for child in eval.children.iter_mut() {
|
|
||||||
child.arg_vals = child.arg_vals.iter().map(|n| remap[*n]).collect();
|
|
||||||
}
|
|
||||||
|
|
||||||
// We're done evaluating this rule, so increment 'next'.
|
|
||||||
// If that was the last rule at this level (i.e. ignoring
|
|
||||||
// eval.children), remove it - we're done with it.
|
|
||||||
s.next += 1;
|
|
||||||
if s.next >= s.rules.len() {
|
|
||||||
stack.pop();
|
|
||||||
n -= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if !eval.children.is_empty() {
|
|
||||||
// Recurse further (i.e. put more onto stack):
|
|
||||||
stack.push(State {
|
|
||||||
rules: eval.children,
|
|
||||||
next: 0,
|
|
||||||
xf: xf,
|
|
||||||
depth: depth - 1,
|
|
||||||
});
|
|
||||||
n += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (geom, eval_count);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S> RuleEval<S> {
|
|
||||||
/// Turn an iterator of (MeshFunc, Child) into a single RuleEval.
|
|
||||||
/// All meshes are merged, and the `arg_vals` in each child has the
|
|
||||||
/// correct offsets applied to account for this merge.
|
|
||||||
///
|
|
||||||
/// (`final_geom` is passed through to the RuleEval unmodified.)
|
|
||||||
pub fn from_pairs<T, U>(m: T, final_geom: MeshFunc) -> RuleEval<S>
|
|
||||||
where U: Borrow<MeshFunc>,
|
|
||||||
T: IntoIterator<Item = (U, Child<S>)>
|
|
||||||
{
|
|
||||||
let (meshes, children): (Vec<_>, Vec<_>) = m.into_iter().unzip();
|
|
||||||
let (mesh, offsets) = MeshFunc::append(meshes);
|
|
||||||
|
|
||||||
// Patch up arg_vals in each child, and copy everything else:
|
|
||||||
let children2: Vec<Child<S>> = children.iter().zip(offsets.iter()).map(|(c,off)| {
|
|
||||||
Child {
|
|
||||||
rule: c.rule.clone(),
|
|
||||||
xf: c.xf.clone(),
|
|
||||||
// simply add offset:
|
|
||||||
arg_vals: c.arg_vals.iter().map(|i| i + off).collect(),
|
|
||||||
}
|
|
||||||
}).collect();
|
|
||||||
|
|
||||||
RuleEval {
|
|
||||||
geom: Rc::new(mesh),
|
|
||||||
final_geom: Rc::new(final_geom),
|
|
||||||
children: children2,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// fa001f47d40de989da6963e442f31c278c88abc8
|
|
||||||
|
|
||||||
/// Produce a mesh from a starting frame, and a function `f` which produces
|
|
||||||
/// transformations that change continuously over its argument (the range
|
|
||||||
/// of which is given by `t0` and `t1`). By convention, `f(t0)` should
|
|
||||||
/// always produce an identity transformation.
|
|
||||||
///
|
|
||||||
/// Facetization is guided by the given error, `max_err`, which is treated
|
|
||||||
/// as a distance in 3D space.
|
|
||||||
pub fn parametric_mesh<F>(frame: Vec<Vertex>, f: F, t0: f32, t1: f32, max_err: f32) -> Mesh
|
|
||||||
where F: Fn(f32) -> Transform
|
|
||||||
{
|
|
||||||
let n = frame.len();
|
|
||||||
|
|
||||||
// Sanity checks:
|
|
||||||
if t1 <= t0 {
|
|
||||||
panic!("t1 must be greater than t0");
|
|
||||||
}
|
|
||||||
if n < 3 {
|
|
||||||
panic!("frame must have at least 3 vertices");
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
mesh_idx: usize, // Index of this vertex in the mesh
|
|
||||||
neighbor1: usize, // Index of 'frontier' of one neighbor
|
|
||||||
neighbor2: usize, // Index of 'frontier' of other neighbor
|
|
||||||
};
|
|
||||||
|
|
||||||
// Init 'frontier' with each 'frame' vertex, and start it at t=t0.
|
|
||||||
let mut frontier: Vec<FrontierVert> = frame.iter().enumerate().map(|(i,v)| FrontierVert {
|
|
||||||
vert: *v,
|
|
||||||
t: t0,
|
|
||||||
frame_idx: i,
|
|
||||||
mesh_idx: i,
|
|
||||||
neighbor1: (i - 1) % n,
|
|
||||||
neighbor2: (i + 1) % n,
|
|
||||||
}).collect();
|
|
||||||
// Every vertex in 'frontier' has a trajectory it follows - which is
|
|
||||||
// simply the position as we transform the original vertex by f(t),
|
|
||||||
// and increment t through the range [t0, t1].
|
|
||||||
//
|
|
||||||
// The goal is to advance the vertices, one at a time, building up
|
|
||||||
// new triangles every time we advance one, until each vertex
|
|
||||||
// reaches t=t1 - in a way that forms the mesh we want.
|
|
||||||
|
|
||||||
// That mesh will be built up here, starting with frame vertices:
|
|
||||||
// (note initial value of mesh_idx)
|
|
||||||
let mut verts: Vec<Vertex> = frame.clone();
|
|
||||||
let mut faces: Vec<usize> = vec![];
|
|
||||||
|
|
||||||
while !frontier.is_empty() {
|
|
||||||
|
|
||||||
// Pick a vertex to advance.
|
|
||||||
//
|
|
||||||
// Heuristic for now: pick the 'furthest back' (lowest t)
|
|
||||||
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?
|
|
||||||
|
|
||||||
if v.t >= t1 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("DEBUG: Moving vertex {}, {:?} (t={}, frame_idx={})", i, v.vert, v.t, v.frame_idx);
|
|
||||||
|
|
||||||
let mut dt = (t1 - t0) / 100.0;
|
|
||||||
let vf = frame[v.frame_idx];
|
|
||||||
for iter in 0..100 {
|
|
||||||
// Consider an edge from f(v.t)*vf to f(v.t + dt)*vf.
|
|
||||||
// These two endpoints have zero error from the trajectory
|
|
||||||
// (because they are directly on it).
|
|
||||||
//
|
|
||||||
// If we assume some continuity in f, then we can guess that
|
|
||||||
// the worst error occurs at the midpoint of the edge:
|
|
||||||
let edge_mid = 0.5*(f(v.t).mtx + f(v.t + dt).mtx)*vf;
|
|
||||||
// ...relative to the trajectory midpoint:
|
|
||||||
let traj_mid = f(v.t + dt/2.0).mtx * vf;
|
|
||||||
let err = (edge_mid - traj_mid).norm();
|
|
||||||
|
|
||||||
println!("DEBUG iter {}: dt={}, edge_mid={:?}, traj_mid={:?}, err={}", iter, dt, edge_mid, traj_mid, err);
|
|
||||||
|
|
||||||
let r = (err - max_err).abs() / max_err;
|
|
||||||
if r < 0.10 {
|
|
||||||
println!("err close enough");
|
|
||||||
break;
|
|
||||||
} else if err > max_err {
|
|
||||||
dt = dt / 2.0;
|
|
||||||
println!("err > max_err, reducing dt to {}", dt);
|
|
||||||
} else {
|
|
||||||
dt = dt * 1.2;
|
|
||||||
println!("err < max_err, increasing dt to {}", dt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let t = v.t + dt;
|
|
||||||
let v_next = f(t).mtx * vf;
|
|
||||||
|
|
||||||
// Add this vertex to our mesh:
|
|
||||||
let pos = verts.len();
|
|
||||||
verts.push(v_next);
|
|
||||||
// There are 3 other vertices of interest: the one we started
|
|
||||||
// from (v) and its two neighbors. We make two edges - one on
|
|
||||||
// each side of the edge (v, v_next).
|
|
||||||
faces.append(&mut vec![
|
|
||||||
v.mesh_idx, pos, frontier[v.neighbor1].mesh_idx,
|
|
||||||
pos, v.mesh_idx, frontier[v.neighbor2].mesh_idx,
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Replace this vertex in the frontier:
|
|
||||||
frontier[i] = FrontierVert {
|
|
||||||
vert: v_next,
|
|
||||||
frame_idx: v.frame_idx,
|
|
||||||
mesh_idx: pos,
|
|
||||||
t: t,
|
|
||||||
neighbor1: v.neighbor1,
|
|
||||||
neighbor2: v.neighbor2,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move this vertex further along, i.e. t + dt. (dt is set by
|
|
||||||
// the furthest we can go while remaining within 'err', i.e. when we
|
|
||||||
// make our connections we look at how far points on the *edges*
|
|
||||||
// diverge from the trajectory of the continuous transformation).
|
|
||||||
|
|
||||||
// Add this vertex to the mesh, and connect it to: the vertex we
|
|
||||||
// started with, and the two neighbors of that vertex.
|
|
||||||
|
|
||||||
// Repeat at "Pick a vertex...".
|
|
||||||
|
|
||||||
// Don't move t + dt past t1. Once a frontier vertex is placed at
|
|
||||||
// that value of t, remove it.
|
|
||||||
|
|
||||||
|
|
||||||
// Missing: Anything about when to subdivide an edge.
|
|
||||||
// If I assume a good criterion of "when" to subdivide an edge, the
|
|
||||||
// "how" is straightforward: find the edge's two neighbors in the
|
|
||||||
// frontier. Trace them back to their 'original' vertices at t=t0
|
|
||||||
// (these should just be stored alongside each frontier member),
|
|
||||||
// produce an interpolated vertex. Produce an interpolated t from
|
|
||||||
// respective t of the two neighbors in the frontier; use that t
|
|
||||||
// to move the 'interpolated' vertex along its trajectory.
|
|
||||||
//
|
|
||||||
// Add new vertex to mesh (and make the necessary connections)
|
|
||||||
// and to frontier.
|
|
||||||
|
|
||||||
// But still missing from that: When do I collapse a subdivision
|
|
||||||
// back down?
|
|
||||||
return Mesh { verts, faces };
|
|
||||||
}
|
|
||||||
@ -1,5 +1,5 @@
|
|||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
use crate::mesh::{Mesh, MeshFunc, VertexUnion};
|
use crate::mesh::{Mesh};
|
||||||
use crate::xform::{Vertex};
|
use crate::xform::{Vertex};
|
||||||
|
|
||||||
/// This is like `vec!`, but it can handle elements that are given
|
/// This is like `vec!`, but it can handle elements that are given
|
||||||
@ -78,13 +78,6 @@ pub fn parallel_zigzag_faces(r1: Range<usize>, r2: Range<usize>) -> Vec<usize> {
|
|||||||
}).flatten().collect()
|
}).flatten().collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parallel_zigzag(verts: Vec<VertexUnion>, main: Range<usize>, parent: Range<usize>) -> MeshFunc {
|
|
||||||
MeshFunc {
|
|
||||||
verts: verts,
|
|
||||||
faces: parallel_zigzag_faces(main, parent),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parallel_zigzag_mesh(verts: Vec<Vertex>, main: Range<usize>, parent: Range<usize>) -> Mesh {
|
pub fn parallel_zigzag_mesh(verts: Vec<Vertex>, main: Range<usize>, parent: Range<usize>) -> Mesh {
|
||||||
Mesh {
|
Mesh {
|
||||||
verts: verts,
|
verts: verts,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user