results.deformed: scale 0 draws the undeformed shape

A factor of 0 was refused, so a picture sequence swinging the deformation
out from the undeformed shape had to start at a small factor or switch
`show` off. 0 is now a factor like any other (the view stays deformed), and
the results panel no longer works around the refusal. The MCP show_results
docs say so, beside `deformed: false`.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm
This commit is contained in:
jyvikoandClaude Opus 5.5 authored and Irakli Kourtis committed 2026-10-14 00:15:50 -04:00
1 parent 89651b707c
commit 025c826dd4
4 files changed
+13 -8

No files matched your search

+8 -3
View File
@@ -11,7 +11,7 @@ use crate::{CommandKind, CommandSpec, EngineError, Result, Session};
pub const DEFORMED: CommandSpec = CommandSpec {
id: "results.deformed",
label: "Deformed Shape",
params: r#"{"step": "Load", "show": true, "scale": "auto"} — shows a solved step's deformed shape (`show: false` the undeformed mesh); `scale` is a factor or "auto" (the largest displacement drawn a tenth of the model's size); `step` defaults to the step shown or the first solved one"#,
params: r#"{"step": "Load", "show": true, "scale": "auto"} — shows a solved step's deformed shape (`show: false` the undeformed mesh); `scale` is a factor (0 draws the undeformed shape, so a sequence from 0 swings it out) or "auto" (the largest displacement drawn a tenth of the model's size); `step` defaults to the step shown or the first solved one"#,
kind: CommandKind::Session,
run: deformed,
};
@@ -141,8 +141,9 @@ fn deformed(s: &mut Session, p: &Value) -> Result<Value> {
Some(Value::String(a)) if a == "auto" => Some(None),
Some(v) => Some(Some(
v.as_f64()
.filter(|x| x.is_finite() && *x > 0.0 && *x <= MAX_SCALE)
.ok_or_else(|| EngineError::Params(format!("`scale` must be \"auto\" or a factor above 0 and at most {MAX_SCALE}")))?,
.filter(|x| x.is_finite() && (0.0..=MAX_SCALE).contains(x))
.map(f64::abs)
.ok_or_else(|| EngineError::Params(format!("`scale` must be \"auto\" or a factor from 0 (undeformed) to {MAX_SCALE}")))?,
)),
};
let v = view_of(s, &step)?;
@@ -336,6 +337,9 @@ mod tests {
assert!(r["contour"]["range"]["max"]["value"].as_f64().unwrap() > 0.0, "{r}");
let r = s.execute("results.deformed", &json!({"scale": 2.5})).unwrap();
assert_eq!((r["view"]["deformed"].as_bool(), r["view"]["scale"].as_f64()), (Some(true), Some(2.5)));
// Scale 0: the undeformed shape, still the deformed view (an animation's first picture).
let r = s.execute("results.deformed", &json!({"scale": 0})).unwrap();
assert_eq!((r["view"]["deformed"].as_bool(), r["view"]["scale"].as_f64()), (Some(true), Some(0.0)));
s.execute("results.deformed", &json!({"scale": "auto", "show": false})).unwrap();
assert_eq!(s.results_view.as_ref().map(|v| (v.deformed, v.scale)), Some((false, None)));
// Modes switch to the frequency step (its own first look) and list the frequencies.
@@ -418,6 +422,7 @@ mod tests {
json!({"field": "von_mises", "bands": 0}),
json!({"field": "von_mises", "bands": 25}),
json!({"scale": -1}),
json!({"scale": -0.0001}),
json!({"scale": 1e300}),
json!({"scale": "big"}),
json!({"scale": f64::NAN}),
+2 -2
View File
@@ -210,7 +210,7 @@ pub fn tool_definitions() -> Vec<Value> {
"show_results",
"Show results",
"Choose what the results display shows and read it back: the step, the deformed shape (`deformed`, `scale`: \
a factor or \"auto\"), contours of a nodal field (`field`: displacement, displacement_x/y/z, von_mises, \
a factor, 0 for the undeformed shape, or \"auto\"), contours of a nodal field (`field`: displacement, displacement_x/y/z, von_mises, \
max/mid/min_principal, stress_xx/yy/zz/xy/xz/yz, or none; `bands` 2–24), a frequency step's mode or an \
explicit step's frame. The reply has the contour's range: its smallest and largest value and their nodes \
(field displacement gives the largest displacement). Only `step`: show that step.",
@@ -220,7 +220,7 @@ pub fn tool_definitions() -> Vec<Value> {
"field": string("Contour field, or \"none\""),
"bands": integer("Contour bands, 2 to 24"),
"deformed": {"type": "boolean", "description": "Draw the deformed shape"},
"scale": {"type": ["number", "string"], "description": "Deformation scale factor or \"auto\""},
"scale": {"type": ["number", "string"], "description": "Deformation scale factor (0: undeformed) or \"auto\""},
"mode": integer("Mode of a frequency step, from 1"),
"frame": integer("Field frame of an explicit step, from 0"),
"time": {"type": "number", "description": "Show the explicit step's frame nearest this time"},
+2 -2
View File
@@ -143,10 +143,10 @@ pub fn controls(app: &mut ModelApp, plot: &Plot, ui: &mut egui::Ui, t: &Tokens)
}
let mut auto = plot.view.scale.is_none();
if ui.checkbox(&mut auto, "Auto").on_hover_text("Scale the largest displacement to a tenth of the model").changed() {
run.push(("results.deformed", json!({"scale": if auto { json!("auto") } else { json!(plot.scale.max(f64::MIN_POSITIVE)) }})));
run.push(("results.deformed", json!({"scale": if auto { json!("auto") } else { json!(plot.scale) }})));
}
let mut scale = plot.scale;
let drag = egui::DragValue::new(&mut scale).speed(plot.scale.abs().max(1e-6) * 0.01).range(1e-9..=1e12).prefix("× ");
let drag = egui::DragValue::new(&mut scale).speed(plot.scale.abs().max(1e-6) * 0.01).range(0.0..=1e12).prefix("× ");
if ui.add_enabled(plot.view.deformed, drag).changed() {
run.push(("results.deformed", json!({"scale": scale})));
}