Remove lots of dead code. Convert Sierpinski example.

This commit is contained in:
Chris Hodapp 2020-10-09 23:26:32 -04:00
parent 5603caa3c1
commit e7206f4598
8 changed files with 92 additions and 2345 deletions

View File

@ -88,8 +88,9 @@ also was vastly faster at generating meshes.
## Highest priority:
- Begin converting older examples.
- Trash all the dead code.
- Work on abstraction/composition. Particularly: factor out
patterns I use, and be able to *compose* procedural meshes
somehow - e.g. the 'context' object I discussed.
- Docs on modules
- swept-isocontour stuff from
`/mnt/dev/graphics_misc/isosurfaces_2018_2019/spiral*.py`. This

View File

@ -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,
}
}
}

View File

@ -1,4 +1,3 @@
use std::rc::Rc;
use std::f32::consts::{FRAC_PI_2, FRAC_PI_3};
use std::f32;
@ -7,12 +6,8 @@ use nalgebra::*;
use crate::util;
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::rule::{Rule, RuleEval, Child};
use crate::prim;
use crate::dcel;
use crate::dcel::{VertSpec};
pub struct Barbs {
base_incr: Transform,
@ -275,826 +270,99 @@ impl TreeThing {
}
}
pub fn sierpinski() -> Rule<()> {
// Initial height step:
let dz = 0.10;
// 'Extra' z rotation (0.0 for normal Sierpinski)
let dr = 0.1;
// Scale factor (0.5 for normal Sierpinski)
let s = 0.51;
pub struct Sierpinski {
splits: [Transform; 3],
base: Vec<Vertex>,
verts: Vec<Vertex>,
faces: Vec<usize>,
}
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();
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 v1 = 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 v1b = v1 + 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 rt3 = (3.0).sqrt();
let angle = 2.0 * FRAC_PI_3 * (i as f32) + dr;
id().
rotate(&Vector3::z_axis(), angle).
translate(rt3/12.0, 0.0, 0.0).
scale(s).
translate(0.0, 0.0, dz)
};
let base = vec![
v0, v1, v2,
v0b, v1b, v2b,
(v0b+v1b)/2.0,
(v1b+v2b)/2.0,
(v2b+v0b)/2.0,
];
let split = rule_fn!(() => |_s, base_verts| {
let mut next_verts = base_verts.clone();
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 {
Sierpinski {
splits: splits,
base: base,
verts: 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> {
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);
pub fn run(mut self) -> Mesh {
self.verts.append(&mut self.base.clone());
self.faces.extend_from_slice(&[2, 1, 0]);
let n = seed.len();
let geom = util::parallel_zigzag(seed, 0..n, n..(2*n));
// TODO: where are parent Args?
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: (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: (),
self.tri_split(id(), [0, 1, 2]);
return Mesh {
verts: self.verts,
faces: self.faces,
}
}
// This was a mistake that I'd like to understand later:
#[derive(Copy, Clone)]
pub struct WindChimeCtxt {
init: bool,
count: usize,
stack: [Transform; 3],
pub fn tri_split(&mut self, xform: Transform, b: [usize; 3]) {
if self.limit_check(&xform) {
self.faces.extend_from_slice(&b);
return;
}
pub fn wind_chime_mistake_thing() -> Rule<WindChimeCtxt> {
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 g = xform.transform(&self.base);
let (a, _) = self.verts.append_indexed(g);
let t = a+3;
let (tm01, tm12, tm20) = (t+3, t+4, t+5);
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: (0..(n + 1)).collect(), // TODO: Check with parents (zigzag/connect_convex)
faces: faces,
});
let rad = 1.0;
let rad2 = 8.0;
let dx0 = 2.0;
let ang = 0.1;
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)),
self.faces.extend_from_slice(&[
// 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,
]);
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:
let edge = edges[0];
let twin = {
let he = &mesh.halfedges[edge];
if he.has_twin {
he.twin_halfedge
} else {
panic!("Can't find shared edge!");
self.tri_split(xform * self.splits[0], [t+0, tm01, tm20]);
self.tri_split(xform * self.splits[1], [t+1, tm12, tm01]);
self.tri_split(xform * self.splits[2], [t+2, tm20, tm12]);
}
};
println!("Shared edges = {},{}", edge, twin);
let ep = mesh.halfedges[edge].prev_halfedge;
let en = mesh.halfedges[edge].next_halfedge;
let tp = mesh.halfedges[twin].prev_halfedge;
let tn = mesh.halfedges[twin].next_halfedge;
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();
fn limit_check(&self, xform: &Transform) -> bool {
// Assume all scales are the same (for now)
let (s, _, _) = xform.get_scale();
return s < 0.01;
}
}

View File

@ -1,12 +1,10 @@
pub mod mesh;
#[macro_use]
pub mod rule;
pub mod prim;
#[macro_use]
pub mod util;
pub mod xform;
pub mod examples;
pub mod dcel;
//pub use crate::examples;
//pub use crate::openmesh::test_thing;
@ -16,31 +14,8 @@ mod tests {
use super::*;
use std::rc::Rc;
use std::time::Instant;
use rule::Rule;
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]
fn xform_order() {
let geom = prim::cube();
@ -114,69 +89,19 @@ mod tests {
}
#[test]
fn sierpinski() { run_test(examples::sierpinski(), 6, "sierpinski", false); }
/*
#[test]
fn twist() {
run_test(examples::twist(1.0, 2), 200, "screw", false);
}
fn sierpinski() {
let name = "sierpinski";
println!("---------------------------------------------------");
let b = examples::Sierpinski::new(0.50, 0.10, 0.0);
//let b = examples::Sierpinski::new(0.51, 0.10, 0.1);
let m = b.run();
#[test]
fn twisty_torus() {
run_test(examples::twisty_torus(), 3000, "twisty_torus", false);
}
println!("Got {} verts...", m.verts.len());
#[test]
fn twisty_torus_hardcode() {
run_test(examples::twisty_torus_hardcode(), 1000, "twisty_torus_hardcode", false);
}
let fname = format!("{}.stl", name);
println!("Writing {}...", fname);
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:

View File

@ -1,6 +1,5 @@
use std::fs::OpenOptions;
use std::io;
use std::borrow::Borrow;
use crate::xform::{Vertex, Transform};
@ -61,185 +60,4 @@ impl Mesh {
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)
}
}

View File

@ -1,4 +1,4 @@
use crate::mesh::{Mesh, MeshFunc};
use crate::mesh::{Mesh};
use crate::xform::{vertex, Transform};
/// 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).
pub fn cube() -> Mesh {
Mesh {

View File

@ -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 };
}

View File

@ -1,5 +1,5 @@
use std::ops::Range;
use crate::mesh::{Mesh, MeshFunc, VertexUnion};
use crate::mesh::{Mesh};
use crate::xform::{Vertex};
/// 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()
}
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 {
Mesh {
verts: verts,