When projecting a face's boundary loop onto its surface fails from an unhinted first point, the walk is retried once from the first point an unhinted search does find; a walk that succeeds is unchanged. A fillet strip on an extended surface now meshes, so the rim cut in the reported case builds in one boolean.
This commit is contained in:
1 parent
575c037f39
commit
e519fbd14d
5 files changed
+1597
-31
No files matched your search
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,57 @@
|
||||
//! #882: a cut through the open rim of a shelled case failed once an R1 round went on the outer
|
||||
//! loop of the other end face. One strip of that round (along a sloped side edge, far from the
|
||||
//! cut) was never meshed: its boundary was projected onto its surface starting from a corner
|
||||
//! that a search without a hint misses, so the face was dropped. Every boolean with the body
|
||||
//! then failed (truck's meshes both operands to classify faces), and the exact fallback refused
|
||||
//! it for the same face. The owner's design, `battery-tpu-case.solidcraft` (format 18).
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
use serde_json::json;
|
||||
|
||||
use super::run;
|
||||
use crate::Session;
|
||||
|
||||
fn open_at(feature: &str) -> Session {
|
||||
let path = Path::new(env!("CARGO_MANIFEST_DIR")).join("src/tests/fixtures/battery-tpu-case.solidcraft");
|
||||
let mut s = Session::default();
|
||||
run(&mut s, "doc.open", json!({"path": path.to_string_lossy()}));
|
||||
run(&mut s, "timeline.roll_to", json!({"feature": feature}));
|
||||
s
|
||||
}
|
||||
|
||||
/// The window cut through the front wall and its rim, 48 × 4 × 50 from below the rim.
|
||||
fn cut_window(s: &mut Session) -> serde_json::Value {
|
||||
run(s, "solid.box", json!({"corner": [-24, -0.5, -1], "length": 48, "width": 4, "height": 50, "operation": "cut", "targets": ["Case"]}))
|
||||
}
|
||||
|
||||
/// Every face of the case after the round meshes (the strip had no triangles at any tolerance).
|
||||
#[test]
|
||||
fn every_face_of_the_rounded_case_meshes() {
|
||||
let s = open_at("Fillet2");
|
||||
let case = s.model.state().body("Case").map(|b| b.body.clone()).unwrap();
|
||||
let faces = case.faces(0.05).unwrap();
|
||||
assert_eq!(faces.len(), case.face_count());
|
||||
let unmeshed: Vec<usize> = faces.iter().filter(|f| f.area <= 0.0).map(|f| f.index).collect();
|
||||
assert!(unmeshed.is_empty(), "faces with no mesh: {unmeshed:?}");
|
||||
}
|
||||
|
||||
/// The cut takes the same window after the round as before it (the round is 120 mm away), in one
|
||||
/// boolean with no retry.
|
||||
#[test]
|
||||
fn rim_cut_after_the_round_on_the_other_end() {
|
||||
let mut before = open_at("Fillet1");
|
||||
let mut after = open_at("Fillet2");
|
||||
let faces = |s: &Session| s.model.state().body("Case").map(|b| b.body.face_count()).unwrap();
|
||||
let (f1, f2) = (faces(&before), faces(&after));
|
||||
cut_window(&mut before);
|
||||
let id = cut_window(&mut after)["feature"].as_u64().unwrap();
|
||||
let result = after.model.result(id).unwrap();
|
||||
assert!(result.error.is_none() && result.warning.is_none(), "{result:?}");
|
||||
assert!(result.boolean_diagnostics.events.is_empty(), "{:?}", result.boolean_diagnostics);
|
||||
// The window's three faces (two sides and the top), at both points of the timeline.
|
||||
assert_eq!(faces(&before), f1 + 3);
|
||||
assert_eq!(faces(&after), f2 + 3);
|
||||
let case = after.model.state().body("Case").map(|b| b.body.clone()).unwrap();
|
||||
assert_eq!(case.shell_count(), 1);
|
||||
}
|
||||
Reference in new issue
Block a user