Results: a static step first shows its contours undeformed

The first look at a solved static step draws the von Mises contours on the
true geometry; the deformed shape comes when asked for (Deformed,
results.deformed, results.animate), at the automatic scale. Frequency steps
still open on mode 1 deformed and explicit steps at true scale, as the
owner's notes in #77 ask; a 0.24 mm deflection drawn as 10 mm no longer
reads as a failed part.

Fixes #77

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
This commit is contained in:
jyvikoandClaude Opus 5.5 authored and Irakli Kourtis committed 2026-10-14 22:29:44 -04:00
1 parent 29af2aef91
commit 5758dc7911
6 files changed
+15 -11

No files matched your search

+3 -3
View File
@@ -346,8 +346,8 @@ mod tests {
fn the_display_follows_the_commands_and_is_not_an_edit() { fn the_display_follows_the_commands_and_is_not_an_edit() {
let mut s = solved(); let mut s = solved();
let (journal, revision) = (s.journal.len(), s.revision()); let (journal, revision) = (s.journal.len(), s.revision());
// Solving showed the first solved step. // Solving showed the first solved step, a static one: contours on the undeformed shape.
assert_eq!(s.results_view.as_ref().map(|v| (v.step.as_str(), v.contour)), Some(("Bend", Some(Field::VonMises)))); assert_eq!(s.results_view.as_ref().map(|v| (v.step.as_str(), v.contour, v.deformed)), Some(("Bend", Some(Field::VonMises), false)));
s.execute("results.clear", &json!({})).unwrap(); s.execute("results.clear", &json!({})).unwrap();
let r = s.execute("results.contour", &json!({"field": "von_mises"})).unwrap(); let r = s.execute("results.contour", &json!({"field": "von_mises"})).unwrap();
assert_eq!(r["view"]["step"], "Bend", "{r}"); assert_eq!(r["view"]["step"], "Bend", "{r}");
@@ -364,7 +364,7 @@ mod tests {
let r = s.execute("results.modes", &json!({"mode": 3})).unwrap(); let r = s.execute("results.modes", &json!({"mode": 3})).unwrap();
assert_eq!((r["view"]["step"].as_str(), r["view"]["mode"].as_u64()), (Some("Modes"), Some(3))); assert_eq!((r["view"]["step"].as_str(), r["view"]["mode"].as_u64()), (Some("Modes"), Some(3)));
assert_eq!(r["modes"].as_array().map(Vec::len), Some(4)); assert_eq!(r["modes"].as_array().map(Vec::len), Some(4));
assert_eq!(s.results_view.as_ref().and_then(|v| v.contour), Some(Field::Displacement)); assert_eq!(s.results_view.as_ref().map(|v| (v.contour, v.deformed)), Some((Some(Field::Displacement), true)), "a mode shape is deformed");
assert!(s.execute("results.contour", &json!({"field": "von_mises"})).is_err()); assert!(s.execute("results.contour", &json!({"field": "von_mises"})).is_err());
s.execute("results.contour", &json!({"field": "displacement", "bands": 6})).unwrap(); s.execute("results.contour", &json!({"field": "displacement", "bands": 6})).unwrap();
s.execute("results.animate", &json!({})).unwrap(); s.execute("results.animate", &json!({})).unwrap();
+6 -4
View File
@@ -268,9 +268,11 @@ pub struct ResultsView {
} }
impl ResultsView { impl ResultsView {
/// How a step's result is shown first: deformed, with von Mises contours for a static step /// How a step's result is shown first: a static step undeformed with von Mises contours, so
/// (and an explicit step that kept stresses), the first mode with displacement contours for /// they are read on the true geometry (`results.deformed` shows the deformed shape, at the
/// a frequency step; an explicit step at its last frame, to true scale. /// automatic scale); a frequency step's first mode deformed with displacement contours (a
/// mode shape is a deformation); an explicit step at its last frame, deformed to true scale
/// (with von Mises contours when it kept stresses); a heat step undeformed.
pub fn for_result(r: &StepResult) -> ResultsView { pub fn for_result(r: &StepResult) -> ResultsView {
let heat = matches!(r.kind(), Kind::Heat(_)); let heat = matches!(r.kind(), Kind::Heat(_));
let contour = if heat { let contour = if heat {
@@ -283,7 +285,7 @@ impl ResultsView {
let scale = matches!(r.kind(), Kind::Explicit(_)).then_some(1.0); let scale = matches!(r.kind(), Kind::Explicit(_)).then_some(1.0);
ResultsView { ResultsView {
step: r.step().to_string(), step: r.step().to_string(),
deformed: !heat, deformed: matches!(r.kind(), Kind::Frequency(_) | Kind::Explicit(_)),
scale, scale,
contour: Some(contour), contour: Some(contour),
bands: DEFAULT_BANDS, bands: DEFAULT_BANDS,
+2 -1
View File
@@ -266,7 +266,8 @@ mod tests {
s.execute("solve.static", &json!({})).unwrap(); s.execute("solve.static", &json!({})).unwrap();
let (plot, shown) = render_png(&s, &iso, 64, 48, &Palette::LIGHT).unwrap(); let (plot, shown) = render_png(&s, &iso, 64, 48, &Palette::LIGHT).unwrap();
assert_ne!(model, plot); assert_ne!(model, plot);
assert!(shown.scale > 0.0 && shown.step.is_some(), "{shown:?}"); // A static step first shows its contours on the undeformed shape.
assert!(shown.scale == 0.0 && shown.step.is_some(), "{shown:?}");
let (field, min, max) = shown.contour.unwrap(); let (field, min, max) = shown.contour.unwrap();
assert_eq!(field, "von_mises"); assert_eq!(field, "von_mises");
assert!(max.1 > min.1); assert!(max.1 > min.1);
+2 -1
View File
@@ -1693,7 +1693,8 @@ mod tests {
app.start("solve.static").unwrap(); app.start("solve.static").unwrap();
app.run_dialog().unwrap(); app.run_dialog().unwrap();
let view = app.session.results_view.clone().unwrap(); let view = app.session.results_view.clone().unwrap();
assert_eq!((view.step.as_str(), view.contour, view.deformed), ("Load", Some(fieldcraft_engine::results::Field::VonMises), true)); // A static step first shows its contours on the undeformed shape (#77).
assert_eq!((view.step.as_str(), view.contour, view.deformed), ("Load", Some(fieldcraft_engine::results::Field::VonMises), false));
app.logic(&egui::Context::default()); app.logic(&egui::Context::default());
assert_eq!(app.ui.tab, Tab::Results); assert_eq!(app.ui.tab, Tab::Results);
app.run("solve.frequency", json!({})).unwrap(); app.run("solve.frequency", json!({})).unwrap();
+1 -1
View File
@@ -30,7 +30,7 @@ checked and clamped (drag steps, wheel lines, window size); `ui.render` refuses
Commands never open forms or file pickers when run this way. `fieldcraft-cli commands` prints the Commands never open forms or file pickers when run this way. `fieldcraft-cli commands` prints the
same registry. `file.sample {name}` opens a sample (`parts`, `cantilever`, `impact`) and same registry. `file.sample {name}` opens a sample (`parts`, `cantilever`, `impact`) and
`solve.run {}` solves every step; the Results workspace comes up when a result appears. The `solve.run {}` solves every step; the Results workspace comes up when a result appears (a static step with its contours on the undeformed shape: `results.deformed {}` shows the deformed shape). The
ribbon follows the commands run here, as it does for scripts, MCP, the command search and the ribbon follows the commands run here, as it does for scripts, MCP, the command search and the
browser: a command that changes the model or the results display brings the tab where its browser: a command that changes the model or the results display brings the tab where its
toolbar entry lives (Mesh for `mesh.*`, Properties, Steps, Loads & BCs… for `model.*`, Solve, toolbar entry lives (Mesh for `mesh.*`, Properties, Steps, Loads & BCs… for `model.*`, Solve,
+1 -1
View File
@@ -105,7 +105,7 @@ foot's end. Linear static: about 2 s to open (the cells are rebuilt from the scr
solve. solve.
**Look at:** the mesh (Prepare ▸ Mesh) — mapped blocks meeting face to face with no pyramids or **Look at:** the mesh (Prepare ▸ Mesh) — mapped blocks meeting face to face with no pyramids or
tets — and the von Mises contours on the deformed shape: the stress runs round the fillet and tets — and the von Mises contours (tick Deformed for the deformed shape): the stress runs round the fillet and
peaks at 295 MPa at the upright's hole. peaks at 295 MPa at the upright's hole.
**Check:** the tip of the foot moves 0.509 mm down, within 1 % of a 48 016-element tet10 **Check:** the tip of the foot moves 0.509 mm down, within 1 % of a 48 016-element tet10