feat(doc): persistent names follow the kernel's exact face history (#136)
Why: every reference a user makes to a face or edge (fillet and chamfer edges, sketches on faces, shell/draft/offset faces, joints, appearances) is stored as a name and found again after any upstream edit. Those names were re-derived by sampling points of each face and voting, and pieces of a split face were numbered by where their centres lay. After an upstream edit that splits, merges, mirrors or shrinks a face, a fillet or sketch could silently re-attach to the wrong face, and nothing told the user. The kernel now reports exact history for its operations, so names follow it and fail loudly instead of guessing. - crates/doc/src/lineage.rs: a feature's kernel histories composed into one lineage over nameable operands (bodies before the feature, moved copies, shell cavities, tools named by role: sketch curve, cap, box side, imported face). Instrumented: extrude/revolve/sweep/loft tools, join/cut/intersect, lumps after a cut, fillet/chamfer, shell, draft, split face/body, pattern/mirror (bodies and features), combine, boundary fill, move/scale, offset/delete face, thread, import. - crates/doc/src/history_names.rs: names from lineage; split pieces told apart by the faces only they touch (F2:top#[F2:side:l4,F4:side:l4]), merged faces answer to every name (aliases), lump bodies ordered by their faces' names instead of shell order. - crates/doc/src/name_lookup.rs, piece_names.rs: references resolve by exact name, alias or bounding faces, else nearest piece with a warning; names worked out from geometry (operations without history) are reported when only geometry decided. - Format 14: names stored by older formats get a legacy mark and are found as the build that saved them found them (some geometric names name a face history names differently: a tube's outer wall was named after its inner circle). New corpus file saved by 9a979cf. - kernel: Body::lumps carries each piece's history over the whole. - Adversarial tests: split, merge, mirrored twins, shrink to a sliver, moving cut with body pieces, history-less bodies, certified sketch. - docs/book/src/dev/naming.md describes the real mechanism and why. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J53sR6gEaVt56soFDpTck8
This commit is contained in:
1 parent
2bda050872
commit
08cf526fe2
29 files changed
+2917
-322
No files matched your search
@@ -146,7 +146,7 @@ fn shell_planar_anchor_rebuild_cold_geometry_and_missing_source_errors() {
|
||||
assert!((after.max.x - before.max.x - 9.).abs() < 1e-8, "{before:?} -> {after:?}");
|
||||
assert!((after.min.y - before.min.y).abs() < 1e-8 && (after.min.z - before.min.z).abs() < 1e-8);
|
||||
let saved = doc.to_json();
|
||||
assert!(saved.contains("solvecraft/13"));
|
||||
assert!(saved.contains(&crate::format::format_string(crate::format::FORMAT)));
|
||||
let loaded = crate::format::read(&saved).unwrap();
|
||||
let mut cold = Model::new();
|
||||
cold.evaluate(&loaded);
|
||||
@@ -176,10 +176,10 @@ fn shell_planar_format13_keeps_v12_documents_and_refuses_future() {
|
||||
legacy["format"] = json!("solvecraft/12");
|
||||
let loaded = crate::format::read(&legacy.to_string()).unwrap();
|
||||
let mut current: serde_json::Value = serde_json::from_str(&loaded.to_json()).unwrap();
|
||||
assert_eq!(current["format"], "solvecraft/13");
|
||||
assert_eq!(current["format"], crate::format::format_string(crate::format::FORMAT));
|
||||
current["format"] = json!("solvecraft/12");
|
||||
assert_eq!(legacy, current, "identity migration must preserve every prior field");
|
||||
legacy["format"] = json!("solvecraft/14");
|
||||
legacy["format"] = json!(crate::format::format_string(crate::format::FORMAT + 1));
|
||||
assert!(crate::format::read(&legacy.to_string()).is_err());
|
||||
}
|
||||
|
||||
@@ -253,8 +253,10 @@ fn saved_former_strip_projection_reports_lost_without_expanding_healthy_material
|
||||
depth: 1,
|
||||
mats: vec![],
|
||||
})));
|
||||
let former = crate::naming::face_name_at_body(&old_body, Vec3::new(20., 6., 1.)).unwrap();
|
||||
assert_eq!(former, format!("{stem}#1"), "historical actual strip capture records full piece name");
|
||||
let captured = crate::naming::face_name_at_body(&old_body, Vec3::new(20., 6., 1.)).unwrap();
|
||||
assert_eq!(crate::naming::parent_piece(&captured), Some(stem.as_str()), "actual strip capture records a piece name");
|
||||
// What the builds before format 14 saved for that piece: its number by centroid order.
|
||||
let former = format!("{stem}#1");
|
||||
let mut sketch = solvecraft_sketch::Sketch::new();
|
||||
let corners = [Vec2::new(0., 0.), Vec2::new(12., 0.), Vec2::new(12., 2.), Vec2::new(0., 2.)];
|
||||
let geometry: Vec<_> = (0..4).map(|i| solvecraft_sketch::LinkGeom::Line(corners[i], corners[(i + 1) % 4])).collect();
|
||||
|
||||
Reference in new issue
Block a user