Edge names: a seam names its one face once; the curve-id test reads every edge kernel-neutrally

A seam's two sides are the same face, and edge names list each face an edge
bounds once, so SMLib's extruded circle names its side's seam F2:side:c1, with
no separator. The name grammar now says one face also means a seam. The #5858
test no longer counts one '|' an edge: every part of every edge name is a
whole face name of the edge, one per face it bounds, two for the rims and one
only for the side's seam (checked to run bottom to top on the circle).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SdcgEu4Lr3moCCisdZShKz
This commit is contained in:
brett19
2026-11-01 18:06:17 -07:00
parent dc88b80e24
commit 8762036213
3 changed files with 44 additions and 7 deletions
+4 -2
View File
@@ -5,12 +5,14 @@
//! (`F3:inner:<face>`), or, without history, a body and an index (`Body1:face:3`). Suffixes
//! follow: a pattern copy (`@F7.2`), a piece of a split face told apart by the faces around it
//! (`#[<face>,…]`, `#k` after it for a tie), an older design's numbered piece (`#k`). An edge is
//! the two faces it separates, `~k` for one of several between them.
//! the two faces it separates, `~k` for one of several between them; a seam, whose two sides are
//! the same face, names that face once.
//!
//! The text form, read by [`FaceName::parse`] and [`EdgeName::parse`] and written by `Display`:
//!
//! ```text
//! edge = face { "|" face } [TIE] one face for an open boundary, more where several meet
//! edge = face { "|" face } [TIE] one face for an open boundary or a seam (both of an
//! edge's sides on one face), more where several meet
//! face = base { suffix }
//! base = FEATURE ":" "blend" ":" ( edge | NUMBER )
//! | FEATURE ":" "inner" ":" face
+40 -4
View File
@@ -376,9 +376,45 @@ fn a_curve_id_that_cannot_be_written_into_a_name_is_refused() {
run(&mut s, "sketch.finish", json!({}));
run(&mut s, "solid.extrude", json!({"distance": 10}));
let edges = run(&mut s, "model.edges", json!({"body": "Body1"}));
let names: Vec<String> = edges["edges"].as_array().unwrap().iter().filter_map(|e| e["name"].as_str().map(str::to_string)).collect();
assert!(!names.is_empty(), "{edges}");
for name in &names {
assert_eq!(name.matches('|').count(), 1, "an edge names two faces and separates them once: {name}");
let listed: Vec<String> = edges["edges"].as_array().unwrap().iter().filter_map(|e| e["name"].as_str().map(str::to_string)).collect();
assert!(!listed.is_empty(), "{edges}");
// Every edge names each face it bounds once, whole, separated by one `|` each: two faces
// where it separates two (the rims), one where both its sides are the same face (the side's
// seam, on a kernel that keeps the side whole). The id is never cut.
let world = s.world_state().unwrap();
let body = world.body("Body1").unwrap();
let faces = solidcraft_doc::naming::face_names(body);
let names = solidcraft_doc::naming::edge_names(body);
let mesh = body.mesh();
assert_eq!(names.len(), mesh.edge_faces.len());
let mut sorted = names.clone();
sorted.sort();
let mut shown = listed.clone();
shown.sort();
assert_eq!(sorted, shown, "model.edges lists the body's edge names");
assert!(faces.iter().any(|f| f == "F2:side:c1" || f.starts_with("F2:side:c1#")), "the side is named with the id whole: {faces:?}");
let mut rims = 0;
for ((name, between), points) in names.iter().zip(&mesh.edge_faces).zip(&mesh.edges) {
let whole = name.rsplit_once('~').filter(|(_, k)| k.parse::<u32>().is_ok()).map_or(name.as_str(), |(n, _)| n);
let parts: Vec<&str> = whole.split('|').collect();
let own: Vec<&str> = between.iter().map(|f| faces[*f as usize].as_str()).collect();
assert_eq!(parts.len(), own.len(), "an edge names each face it bounds, separated once each: {name}");
for part in &parts {
assert!(own.contains(part), "`{part}` of {name} is a whole face name among {own:?}");
}
match own.as_slice() {
[_, _] => rims += 1,
[side] => {
// A seam: along the side from the bottom to the top, at one place on the circle.
assert!(side.starts_with("F2:side:c1"), "only the side has a seam: {name}");
let (Some(a), Some(b)) = (points.first(), points.last()) else { panic!("an empty edge: {name}") };
assert!(
(a.z - b.z).abs() > 9.999 && (a.x - b.x).hypot(a.y - b.y) < 1e-6 && (a.x.hypot(a.y) - 5.).abs() < 1e-6,
"{name}: {a:?} {b:?}"
);
}
other => panic!("{name} bounds {other:?}"),
}
}
assert!(rims >= 2, "the top and bottom rims: {names:?}");
}
-1
View File
@@ -10,7 +10,6 @@
# Baseline: develop 38fd4a4f6 (CI run 41157648345, x86_64 Linux), after the SMLib eaa998a pin.
solidcraft-cli::action_matrix::action_matrix_sample 3 baseline actions fail on SMLib: 2 shells (Bad Loop Orient on issue-616-lid-ribs; status 1001 on filleted-shelled-box, shelled again open at an inner wall), 1 fillet (SmSurfOfRevolution Test #7 on oblique_cut_of_cylinder)
solidcraft-cli::naming_corpus::corpus_designs_name_alike_every_build_and_fail_where_a_reference_is_gone oracle/83-boundary-loop-thicken does not load: step 8, SMLib thicken status 1001
solidcraft-engine::cmd::sketch::ids_tests::a_curve_id_that_cannot_be_written_into_a_name_is_refused main's test: an extruded circle on SMLib has a seam edge, the side face on both its sides, named F2:side:c1 with no second face
solidcraft-engine::tests::fillet_fit_taper_closes_to_a_point SMLib fillet status 1001 on the taper fit (radius 16 closing to 0)
solidcraft-kernel::blend::corner_tests::a_round_ending_in_an_earlier_round_matches_both_together SMLib fillet status 1001 on the round that ends in the earlier one
solidcraft-kernel::blend::corner_tests::corner_blends_hold_at_any_scale at 0.01x the arc case's rounds remove 72.783 (scaled back) against 72.713 at 1x (9.6e-4 off, 1e-4 allowed)