Fix pesky-ass bug in DCEL (I think). Debug statements remain.

This commit is contained in:
Chris Hodapp 2020-06-06 00:18:36 -04:00
parent dbaadea466
commit 39072e0c30
4 changed files with 165 additions and 70 deletions

View File

@ -2,12 +2,17 @@
## Highest priority:
- Work on my doubly-connected edge list so I can complete some other
things below!
- Implement the continuous parametric transformations from 2020-05-07
in my notes. This will require some new abstractions.
- Add better docs and possibly abstraction to `dcel.rs`, *before*
it's widely used. Possibly rename too.
- `dcel.rs` needs a helper method for subdivision.
- Redo `examples::parametric_mesh` along the following lines:
- Make a crappy mesh (as a DCEL) like my 'early' method.
- Use my method from around 2020-05-21, and my pile of loose notes,
to subdivide - with the help of DCEL.
- Document `parametric_mesh` better. It is pretty hairy and could
also benefit from some modularity.
- parametric_mesh: My err/max_err code seems to sometimes give very high
dt values, e.g. if I use just a translation as my transform
- Get identical or near-identical meshes to `ramhorn_branch` from
Python. (Should just be a matter of tweaking parameters.)
- Look at performance.

View File

@ -139,7 +139,12 @@ impl<V: Copy + std::fmt::Debug> DCELMesh<V> {
} else {
String::from("")
};
println!("Halfedge {}: vert {}, face {}, prev: {}, next: {}{}", i, h.vert, h.face, h.prev_halfedge, h.next_halfedge, 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);
}
}
@ -196,10 +201,14 @@ impl<V: Copy + std::fmt::Debug> DCELMesh<V> {
println!("Halfedge {}: twin {} says it has no twin",
i, edge.twin_halfedge);
pass = false;
} else if i != twin.twin_halfedge {
} 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;
@ -398,6 +407,7 @@ impl<V: Copy + std::fmt::Debug> DCELMesh<V> {
/// Also: halfedge `twin2_idx` must end at the vertex that starts
/// `twin1_idx`.
pub fn add_face_twin2(&mut self, twin1_idx: usize, twin2_idx: usize) -> (usize, [usize; 3]) {
println!("DEBUG: add_face_twin2({},{})", twin1_idx, twin2_idx);
// 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:
@ -417,8 +427,11 @@ impl<V: Copy + std::fmt::Debug> DCELMesh<V> {
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
// and the twin of twin2 must be: v1 -> v3
self.halfedges.push(DCELHalfEdge {
vert: v1,
vert: v2,
face: f_n,
has_twin: true,
twin_halfedge: twin1_idx,
@ -436,7 +449,7 @@ impl<V: Copy + std::fmt::Debug> DCELMesh<V> {
prev_halfedge: e_n,
}); // index e_n + 1
self.halfedges.push(DCELHalfEdge {
vert: v2,
vert: v1,
face: f_n,
has_twin: false,
twin_halfedge: 0,

View File

@ -12,6 +12,7 @@ use crate::xform::{Transform, Vertex, vertex, Mat4, id};
use crate::rule::{Rule, RuleFn, RuleEval, Child};
use crate::prim;
use crate::dcel;
use crate::dcel::{DCELMesh, VertSpec};
/*
pub fn cube_thing() -> Rule<()> {
@ -1262,10 +1263,10 @@ impl CurveHorn {
pub fn test_parametric() -> Mesh {
let base_verts: Vec<Vertex> = 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),
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);
@ -1273,9 +1274,9 @@ pub fn test_parametric() -> Mesh {
let t0 = 0.0;
let t1 = 16.0;
let xform = |t: f32| -> Transform {
id().translate(0.0, 0.0, t/5.0).
id().translate(0.0, 0.0, t/5.0)/*.
rotate(&Vector3::z_axis(), -t/2.0).
scale((0.8).powf(t))
scale((0.8).powf(t))*/
};
crate::rule::parametric_mesh(base_verts, xform, t0, t1, 0.005)
@ -1283,24 +1284,25 @@ pub fn test_parametric() -> Mesh {
pub fn test_dcel(fname: &str) {
let mut mesh: dcel::DCELMesh<Vertex> = dcel::DCELMesh::new();
let f1 = mesh.add_face([
vertex(-0.5, -0.5, 0.0),
vertex(-0.5, 0.5, 0.0),
vertex( 0.5, 0.5, 0.0),
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 = mesh.add_face_twin1(mesh.faces[f1].halfedge, vertex(0.0, 0.0, 1.0));
let (f2, edges) = mesh.add_face_twin1(mesh.faces[f1].halfedge, vertex(0.0, 0.0, 1.0));
mesh.check();
// Find the shared edge:
let mut shared: Option<(usize, usize)> = None;
for e in mesh.face_to_halfedges(f1) {
let he = &mesh.halfedges[e];
// 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 {
shared = Some((e, he.twin_halfedge));
he.twin_halfedge
} else {
panic!("Can't find shared edge!");
}
}
let (edge, twin) = shared.unwrap();
};
println!("Shared edges = {},{}", edge, twin);
let ep = mesh.halfedges[edge].prev_halfedge;
@ -1312,9 +1314,9 @@ pub fn test_dcel(fname: &str) {
println!("DCEL mesh = {}", mesh);
// As we're making *twin* halfedges, we go against the edge
// direction:
let f3 = mesh.add_face_twin2(en, tp);
let (f3, _) = mesh.add_face_twin2(en, tp);
mesh.check();
let f4 = mesh.add_face_twin2(tn, ep);
let (f4, _) = mesh.add_face_twin2(tn, ep);
mesh.check();
println!("f1 verts: {:?}", mesh.face_to_verts(f1));

View File

@ -1,10 +1,12 @@
use crate::mesh::{Mesh, MeshFunc, VertexUnion};
use crate::xform::{Transform, Vertex};
//use crate::prim;
use std::borrow::Borrow;
use std::rc::Rc;
use std::f32;
use crate::mesh::{Mesh, MeshFunc, VertexUnion};
use crate::xform::{Transform, Vertex};
use crate::dcel;
use crate::dcel::{DCELMesh, VertSpec};
pub type RuleFn<S> = Rc<dyn Fn(Rc<Rule<S>>) -> RuleEval<S>>;
/// Definition of a rule. In general, a `Rule`:
@ -364,19 +366,27 @@ pub fn parametric_mesh<F>(frame: Vec<Vertex>, f: F, t0: f32, t1: f32, max_err: f
panic!("frame must have at least 3 vertices");
}
let mut mesh: DCELMesh<Vertex> = DCELMesh::new();
#[derive(Clone, Debug)]
struct frontierVert {
vert: Vertex, // Vertex position
t: f32, // Parameter value; f(t) should equal vert
frame_vert: Vertex, // "Starting" vertex position, i.e. at f(t0).
// Always either a frame vertex, or a linear combination of two
// neighboring ones.
mesh_idx: usize, // Index of this vertex in the mesh
// (If it's in 'frontier', the vertex is in the mesh).
neighbor: [usize; 2], // Indices of 'frontier' of each neighbor
side_faces: [(usize,bool); 2], // The two faces (given as an index
// into 'faces' below) which contain the edge from this vertex to
// its neighbors. If the bool is false, there is no face.
// Vertex position
vert: Vertex,
// Parameter value; f(t) should equal vert
t: f32,
// "Starting" vertex position, i.e. at f(t0). Always either a frame
// vertex, or a linear combination of two neighboring ones.
frame_vert: Vertex,
// If the boundaries on either side of this vertex lie on a face
// (which is the case for all vertices *except* the initial ones),
// then this gives the halfedges of those boundaries. halfedges[0]
// connects the 'prior' vertex on the frontier to this, and
// halfedges[1] connect this to the 'next' vertex on the fronter.
// (Direction matters. If halfedges[0] is given, it must *end* at
// 'vert'. If halfedges[1] is given, it must *begin* at 'vert'.)
halfedges: [Option<usize>; 2],
// If this vertex is already in 'mesh', its vertex index there:
vert_idx: Option<usize>,
};
// A face that is still undergoing subdivision. This is used for a stack
@ -409,9 +419,8 @@ pub fn parametric_mesh<F>(frame: Vec<Vertex>, f: F, t0: f32, t1: f32, max_err: f
vert: *v,
t: t0,
frame_vert: *v,
mesh_idx: i,
neighbor: [(i - 1) % n, (i + 1) % n],
side_faces: [(0, false); 2], // initial vertices have no faces
halfedges: [None; 2],
vert_idx: None,
}).collect();
// Every vertex in 'frontier' has a trajectory it follows - which is
// simply the position as we transform the original vertex by f(t),
@ -421,18 +430,9 @@ pub fn parametric_mesh<F>(frame: Vec<Vertex>, f: F, t0: f32, t1: f32, max_err: f
// 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![];
// 'stack' is cleared at every iteration below, and contains faces that
// may still require subdivision.
let mut stack: Vec<tempFace> = vec![];
while !frontier.is_empty() {
for (i, f) in frontier.iter().enumerate() {
println!("DEBUG: frontier[{}]: vert={},{},{} t={} mesh_idx={} neighbor={:?}", i, f.vert.x, f.vert.y, f.vert.z, f.t, f.mesh_idx, f.neighbor);
println!("DEBUG: frontier[{}]: vert={},{},{} t={} halfedges={:?}", i, f.vert.x, f.vert.y, f.vert.z, f.t, f.halfedges);
}
// Pick a vertex to advance.
@ -451,7 +451,7 @@ pub fn parametric_mesh<F>(frame: Vec<Vertex>, f: F, t0: f32, t1: f32, max_err: f
// TODO: Fix boundary behavior here and make sure final topology
// is right.
println!("DEBUG: Moving vertex {}, {:?} (t={}, frame_vert={:?})", i, v.vert, v.t, v.frame_vert);
println!("DEBUG: Moving frontier vertex {}, {:?} (t={}, frame_vert={:?})", i, v.vert, v.t, v.frame_vert);
// 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
@ -459,7 +459,7 @@ pub fn parametric_mesh<F>(frame: Vec<Vertex>, f: F, t0: f32, t1: f32, max_err: f
// diverge from the trajectory of the continuous transformation).
let mut dt = (t1 - t0) / 100.0;
let vf = v.frame_vert;
for iter in 0..100 {
for iter in 0..0 /*100*/ { // DEBUG: Re-enable
// 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).
@ -502,30 +502,103 @@ pub fn parametric_mesh<F>(frame: Vec<Vertex>, f: F, t0: f32, t1: f32, max_err: f
println!("l1={} l2={} l3={}", l1, l2, l3);
*/
// Add this vertex to our mesh:
let v_next_idx = verts.len();
verts.push(v_next);
// Add two faces to our mesh. (They share two vertices, and thus
// the boundary in between those vertices.)
let (f1, edges1) = match v.halfedges[0] {
// However, the way we add the face depends on whether we are
// adding to an existing boundary or not:
None => {
let neighbor = &frontier[(i + n - 1) % n];
println!("DEBUG: add_face()");
let (f1, edges1) = mesh.add_face([
VertSpec::New(v_next),
match v.vert_idx {
None => VertSpec::New(v.vert),
Some(idx) => VertSpec::Idx(idx),
},
match neighbor.vert_idx {
None => VertSpec::New(neighbor.vert),
Some(idx) => VertSpec::Idx(idx),
},
]);
if neighbor.vert_idx.is_none() {
// If neighbor.vert_idx is None, then we had to
// add its vertex to the mesh for the face we just
// made - so mark it in the frontier:
frontier[(i + n - 1) % n].vert_idx = Some(mesh.halfedges[edges1[2]].vert);
// edges[2] is because this is the position of
// neighbor.vert_idx in add_face.
}
(f1, edges1)
},
Some(edge_idx) => {
println!("DEBUG: add_face_twin1({},{})", edge_idx, v_next);
mesh.add_face_twin1(edge_idx, v_next)
},
};
println!("DEBUG: edges1={:?}", edges1);
// DEBUG
mesh.check();
mesh.print();
// edge2 should be: the half-edge connecting the 'neighbor' frontier
// vertex to v_next
let (f2, edge2) = match v.halfedges[1] {
// Likewise, the way we add the second face depends on
// the same (but for the other side). Regardless,
// they share the boundary between v_next and v.vert - which
// is edges1[0].
None => {
let neighbor = &frontier[(i + 1) % n];
let (f2, edges) = mesh.add_face_twin1(edges1[0], neighbor.vert);
if neighbor.vert_idx.is_none() {
// Reasoning here is identical to "If neighbor.vert_idx
// is None..." above:
frontier[(i + 1) % n].vert_idx = Some(mesh.halfedges[edges[2]].vert);
}
(f2, edges[1])
},
Some(edge_idx) => {
let (f2, edges) = mesh.add_face_twin2(edge_idx, edges1[2]);
// TODO: Why edges1[2]?
(f2, edges[2])
},
};
println!("DEBUG: edge2={}", edge2);
// DEBUG
println!("DEBUG: 2nd face");
mesh.check();
mesh.print();
// The 'shared' half-edge should start at v.vert - hence edges[1].
// and add two faces:
let face_idx = faces.len();
/*
faces.append(&mut vec![
v_next_idx, v.mesh_idx, frontier[v.neighbor[0]].mesh_idx, // face_idx
v.mesh_idx, v_next_idx, frontier[v.neighbor[1]].mesh_idx, // face_idx + 1
]);
*/
// Replace this vertex in the frontier:
frontier[i] = frontierVert {
vert: v_next,
frame_vert: vf,
mesh_idx: v_next_idx,
t: t,
neighbor: v.neighbor,
side_faces: [(face_idx, true), (face_idx + 1, true)],
halfedges: [Some(edges1[2]), Some(edge2)],
// Note that edges1[2] *starts* at the new vertex, and
// edge2 *ends* at it.
// DEBUG: The above comment is wrong, but why?
vert_idx: Some(mesh.halfedges[edges1[2]].vert),
};
// Note above that we added two edges that include v_next_idx:
// (frontier[v.neighbor[0]].mesh_idx, v_next_idx)
// (frontier[v.neighbor[1]].mesh_idx, v_next_idx)
// hence, side_faces has the respective face for each.
/*
// Also add these faces to the stack of triangles to check for
// subdivision. They may be replaced.
let f0 = &frontier[v.neighbor[0]];
@ -548,7 +621,8 @@ pub fn parametric_mesh<F>(frame: Vec<Vertex>, f: F, t0: f32, t1: f32, max_err: f
// several vertices sit in the same trajectory (thus, same 'frame'
// vertex).
while !stack.is_empty() {
// TODO: Move this logic elsewhere
while false && !stack.is_empty() {
let face = stack.pop().unwrap();
println!("DEBUG: Examining face: {:?}", face);
let v0 = verts[face.verts[0]];
@ -636,7 +710,8 @@ pub fn parametric_mesh<F>(frame: Vec<Vertex>, f: F, t0: f32, t1: f32, max_err: f
// If they refer to the same face I may be invalidating
// something here!
}
*/
}
return Mesh { verts, faces };
return Mesh { faces: vec![], verts: vec![] };
}