forked from 1b-agent/fieldcraft-upstream
mesh.quality: a one-line summary, shown in the status bar
Quality from the ribbon printed its whole JSON report into the status
bar, cut off after the first metric's histogram, so a user saw braces
and no answer. The report now carries a `summary` line ("30676 elements,
all valid; scaled Jacobian min 0.305, mean 0.963", with the invalid and
undetermined counts when there are any), and the status bar shows a
reply's summary instead of its JSON whenever it has one. The CLI and MCP
get the same line.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm
This commit is contained in:
1 parent
e4e3c9a699
commit
4445be0b48
2 files changed
+54
No files matched your search
@@ -529,6 +529,7 @@ fn quality_report(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
})
|
||||
.collect();
|
||||
Ok(json!({
|
||||
"summary": quality_summary(&r),
|
||||
"measured": r.measured,
|
||||
"skipped": skipped,
|
||||
"valid": r.valid,
|
||||
@@ -540,6 +541,29 @@ fn quality_report(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
}))
|
||||
}
|
||||
|
||||
/// A quality report in a line, for the status bar and the CLI: the elements measured, how many
|
||||
/// are invalid or undetermined, and the scaled Jacobian's minimum and mean.
|
||||
fn quality_summary(r: &QualityReport) -> String {
|
||||
if r.measured == 0 {
|
||||
return "no elements to measure".into();
|
||||
}
|
||||
let mut text = format!("{} elements", r.measured);
|
||||
if r.invalid.is_empty() && r.undetermined.is_empty() {
|
||||
text.push_str(", all valid");
|
||||
}
|
||||
if !r.invalid.is_empty() {
|
||||
text.push_str(&format!(", {} invalid", r.invalid.len()));
|
||||
}
|
||||
if !r.undetermined.is_empty() {
|
||||
text.push_str(&format!(", {} undetermined", r.undetermined.len()));
|
||||
}
|
||||
let jacobian = r.metrics.iter().find(|m| m.metric == quality::Metric::ScaledJacobian && m.count > 0);
|
||||
if let Some((min, mean)) = jacobian.and_then(|m| m.mean.map(|mean| (m.min, mean))) {
|
||||
text.push_str(&format!("; scaled Jacobian min {min:.3}, mean {mean:.3}"));
|
||||
}
|
||||
text
|
||||
}
|
||||
|
||||
/// The surface named by `body` (tessellated at `tolerance`) or `surface` (an imported index).
|
||||
fn surface_input<'a>(s: &'a Session, p: &Value) -> Result<Cow<'a, SurfaceMesh>> {
|
||||
match (p.get("body"), p.get("surface")) {
|
||||
@@ -763,6 +787,7 @@ mod tests {
|
||||
// The certified quality report agrees.
|
||||
let q = s.execute("mesh.quality", &json!({})).unwrap();
|
||||
assert_eq!(q["valid"].as_u64().unwrap() as usize, elements);
|
||||
assert!(q["summary"].as_str().unwrap().starts_with(&format!("{elements} elements, all valid; scaled Jacobian min ")), "{q}");
|
||||
// A second run appends after the existing labels.
|
||||
let again = s.execute("mesh.tet.generate", &json!({"body": "Body1", "tolerance": 0.5})).unwrap();
|
||||
assert_eq!(again["first_element"].as_u64().unwrap() as usize, elements + 1);
|
||||
@@ -817,6 +842,20 @@ mod tests {
|
||||
assert!(e.starts_with("body `Box"), "{e}");
|
||||
}
|
||||
|
||||
/// The quality report's one-line summary counts the inverted elements.
|
||||
#[test]
|
||||
fn quality_summary_counts_invalid_elements() {
|
||||
let mut s = Session::default();
|
||||
assert_eq!(s.execute("mesh.quality", &json!({})).unwrap()["summary"], "no elements to measure");
|
||||
for (label, xyz) in [(1, [0, 0, 0]), (2, [1, 0, 0]), (3, [0, 1, 0]), (4, [0, 0, 1])] {
|
||||
s.execute("mesh.node.add", &json!({"label": label, "xyz": xyz})).unwrap();
|
||||
}
|
||||
s.execute("mesh.element.add", &json!({"label": 1, "kind": "tet4", "nodes": [1, 2, 3, 4]})).unwrap();
|
||||
s.execute("mesh.element.add", &json!({"label": 2, "kind": "tet4", "nodes": [1, 3, 2, 4]})).unwrap();
|
||||
let q = s.execute("mesh.quality", &json!({})).unwrap();
|
||||
assert!(q["summary"].as_str().unwrap().starts_with("2 elements, 1 invalid; scaled Jacobian min -"), "{q}");
|
||||
}
|
||||
|
||||
/// The session's caps are the defaults of `max_steiner` and `max_tets`; a parameter overrides them.
|
||||
#[test]
|
||||
fn tet_generate_honours_the_session_caps() {
|
||||
|
||||
Reference in new issue
Block a user