From e7206f459897a5a8406ba609f6b21b3dbcea67a6 Mon Sep 17 00:00:00 2001 From: Chris Hodapp Date: Fri, 9 Oct 2020 23:26:32 -0400 Subject: [PATCH] Remove lots of dead code. Convert Sierpinski example. --- README.md | 5 +- src/dcel.rs | 752 ---------------------------------------- src/examples.rs | 886 +++++------------------------------------------- src/lib.rs | 95 +----- src/mesh.rs | 182 ---------- src/prim.rs | 10 +- src/rule.rs | 498 --------------------------- src/util.rs | 9 +- 8 files changed, 92 insertions(+), 2345 deletions(-) delete mode 100644 src/dcel.rs delete mode 100644 src/rule.rs diff --git a/README.md b/README.md index 83c53b0..28ea284 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/src/dcel.rs b/src/dcel.rs deleted file mode 100644 index 2940028..0000000 --- a/src/dcel.rs +++ /dev/null @@ -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 { - pub verts: Vec>, - pub faces: Vec, - pub halfedges: Vec, - - 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. -#[derive(Clone, Debug)] -pub struct DCELVertex { - /// 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, 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, 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 { - New(V), - Idx(usize), -} - -impl fmt::Display for DCELMesh { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - - let v_strs: Vec = 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 = self.faces.iter().enumerate().map(|(i,f)| { - format!("F{}=e{}", i, f.halfedge) - }).collect(); - let f_str = f_strs.join(", "); - - let e_strs: Vec = 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 DCELMesh { - - pub fn new() -> DCELMesh { - 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 { - let mut edges: Vec = 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 { - 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; 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) -> Option<(Vec, Vec)> { - // '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 = (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(&self, f: F) -> Mesh - where F: Fn(V) -> Vertex, - { - let n = self.faces.len(); - let mut faces: Vec = 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, - } - } -} diff --git a/src/examples.rs b/src/examples.rs index e627e98..34a145f 100644 --- a/src/examples.rs +++ b/src/examples.rs @@ -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<()> { +pub struct Sierpinski { + splits: [Transform; 3], + base: Vec, + verts: Vec, + faces: Vec, +} - // 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; +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); + } - let rt3 = (3.0).sqrt(); - - // 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 = { 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 { - 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 { + Sierpinski { + splits: splits, + base: base, verts: vec![], faces: vec![], - // TODO: get actual verts here - }; - - let c = move |self_: Rc>| -> 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 { - 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>| -> RuleEval { - //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>| -> RuleEval { - let z = &Vector3::z_axis(); - let child = |i: usize| -> Child { - 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 - } - }; + pub fn run(mut self) -> Mesh { + self.verts.append(&mut self.base.clone()); + self.faces.extend_from_slice(&[2, 1, 0]); - RuleEval { - geom: Rc::new(prim::empty_mesh()), - final_geom: Rc::new(prim::empty_mesh()), - children: (0..count).map(child).collect(), + self.tri_split(id(), [0, 1, 2]); + return Mesh { + verts: self.verts, + faces: self.faces, } - }; + } - Rule { - eval: Rc::new(start), - ctxt: NestSpiral2Ctxt { - init: true, - stack: [ // doesn't matter - Transform::new(), - Transform::new(), - ], - }, + pub fn tri_split(&mut self, xform: Transform, b: [usize; 3]) { + + if self.limit_check(&xform) { + self.faces.extend_from_slice(&b); + return; + } + + 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); + + 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, + ]); + + 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]); + } + + fn limit_check(&self, xform: &Transform) -> bool { + // Assume all scales are the same (for now) + let (s, _, _) = xform.get_scale(); + return s < 0.01; } } - -#[derive(Copy, Clone)] -pub struct TorusCtxt { - init: bool, - count: usize, - stack: [Transform; 3], -} - -pub fn twisty_torus() -> 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 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>| -> RuleEval { - 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>| -> 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>| -> 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: -#[derive(Copy, Clone)] -pub struct WindChimeCtxt { - init: bool, - count: usize, - stack: [Transform; 3], -} - -pub fn wind_chime_mistake_thing() -> 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 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: (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>| -> RuleEval { - 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 = 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 = 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: - 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!"); - } - }; - 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(); -} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 22d4860..fad2d77 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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(rule: Rule, 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); - } - - #[test] - #[ignore] - fn twisty_torus_full() { - run_test(examples::twisty_torus(), 40000, "twisty_torus_full", false); - } + let fname = format!("{}.stl", name); + println!("Writing {}...", fname); + m.write_stl_file(&fname).unwrap(); - #[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: diff --git a/src/mesh.rs b/src/mesh.rs index 844a4ff..9d99e0b 100644 --- a/src/mesh.rs +++ b/src/mesh.rs @@ -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>(v: T) -> Vec { - 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, - /// Indices of triangles (taken as every 3 values). - pub faces: Vec, -} - -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(meshes: T) -> (MeshFunc, Vec) - where U: Borrow, - T: IntoIterator - { - let mut offsets: Vec = vec![]; - let mut v: Vec = vec![]; - let mut f: Vec = 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(&self, children: T) -> (MeshFunc, Vec>) - where U: Borrow, - T: IntoIterator)> - { - // TODO: Clean up this description a bit - // TODO: Clean up Vec stuff - - // Copy body vertices & faces: - let mut verts: Vec = self.verts.clone(); - let mut faces = self.faces.clone(); - - let mut remaps: Vec> = 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) - } } diff --git a/src/prim.rs b/src/prim.rs index b34b530..e8e2f1d 100644 --- a/src/prim.rs +++ b/src/prim.rs @@ -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 { diff --git a/src/rule.rs b/src/rule.rs deleted file mode 100644 index cea4a54..0000000 --- a/src/rule.rs +++ /dev/null @@ -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 = Rc>) -> RuleEval>; - -/// 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 { - pub eval: RuleFn, - 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 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 { - /// The geometry generated at just this iteration - pub geom: Rc, - - /// 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, - - /// 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` evaluations, pairing another `Rule` with the -/// transformations and parent vertex mappings that should be applied -/// to it. -pub struct Child { - - /// Rule to evaluate to produce geometry - pub rule: Rc>, - - /// 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, -} - -#[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>| -> 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 Rule { - - /// 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>, iters_left: usize) -> (MeshFunc, usize) { - - let mut evals = 1; - - let rs: RuleEval = (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)> = 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>, max_depth: usize) -> (MeshFunc, usize) { - - struct State { - // The set of rules we're currently handling: - rules: Vec>, - // 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> = 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 = (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 RuleEval { - /// 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(m: T, final_geom: MeshFunc) -> RuleEval - where U: Borrow, - T: IntoIterator)> - { - 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> = 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(frame: Vec, 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 = 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 = frame.clone(); - let mut faces: Vec = 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 }; -} diff --git a/src/util.rs b/src/util.rs index efb22df..8667a15 100644 --- a/src/util.rs +++ b/src/util.rs @@ -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, r2: Range) -> Vec { }).flatten().collect() } -pub fn parallel_zigzag(verts: Vec, main: Range, parent: Range) -> MeshFunc { - MeshFunc { - verts: verts, - faces: parallel_zigzag_faces(main, parent), - } -} - pub fn parallel_zigzag_mesh(verts: Vec, main: Range, parent: Range) -> Mesh { Mesh { verts: verts,