Skip to content

Commit

Permalink
Fix meshing edge case (#54)
Browse files Browse the repository at this point in the history
  • Loading branch information
mkeeter authored Mar 26, 2024
1 parent e5de01a commit be7c1b5
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 1 deletion.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
change bounds for meshing (by translating + scaling the underlying model).
- Move `Interval` and `Grad` to `fidget::types` module, instead of
`fidget::eval::types`.
- Fix an edge case in meshing where nearly-planar surfaces could produce
vertexes far from the desired position.

# 0.2.2
- Added many transcendental functions: `sin`, `cos`, `tan`, `asin`, `acos`,
Expand Down
19 changes: 19 additions & 0 deletions fidget/src/mesh/octree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1870,4 +1870,23 @@ mod test {
"bad accumulated QEF in center"
);
}

#[test]
fn test_qef_near_planar() {
let ctx = BoundContext::new();
let shape = sphere(&ctx, [0.0; 3], 0.75);

let shape: VmShape = shape.convert();
let settings = Settings {
min_depth: 4,
max_depth: 4,
threads: 0,
};

let octree = Octree::build(&shape, settings).walk_dual(settings);
for v in octree.vertices.iter() {
let n = v.norm();
assert!(n > 0.7 && n < 0.8, "invalid vertex at {v:?}: {n}");
}
}
}
14 changes: 13 additions & 1 deletion fidget/src/mesh/qef.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,25 @@ impl QuadraticErrorSolver {
let atb = self.atb - self.ata * center;

let svd = nalgebra::linalg::SVD::new(self.ata, true, true);

// Skip any eigenvalues that are **extremely** small relative to the
// maximum eigenvalue. Without this filter, we can see failures in
// near-planar situations.
const EIGENVALUE_CUTOFF_RELATIVE: f32 = 1e-12;
let cutoff = svd.singular_values[0].abs() * EIGENVALUE_CUTOFF_RELATIVE;
let start = (0..3)
.filter(|i| svd.singular_values[*i].abs() < cutoff)
.last()
.unwrap_or(0);

// "Dual Contouring: The Secret Sauce" recomments a threshold of 0.1
// when using normalized gradients, but I've found that fails on
// things like the cone model. Instead, we'll be a little more
// clever: we'll pick the smallest epsilon that keeps the feature in
// the cell without dramatically increasing QEF error.
let mut prev = None;
for i in 0..4 {
for i in start..4 {
// i is the number of singular values to ignore
let epsilon = if i == 3 {
f32::INFINITY
} else {
Expand Down

0 comments on commit be7c1b5

Please sign in to comment.