forked from 1b-agent/fieldcraft-upstream
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 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm
This commit is contained in:
1 parent
89651b707c
commit
025c826dd4
4 files changed
+13
-8
No files matched your search
@@ -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}),
|
||||
|
||||
@@ -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"},
|
||||
|
||||
@@ -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})));
|
||||
}
|
||||
|
||||
+1
-1
@@ -101,7 +101,7 @@ software on the computer.
|
||||
| `write_deck` | `format` (abaqus, nastran, lsdyna), `path`, `steps?` | `deck.write`. |
|
||||
| `mesh` | `method`, `params?` | Runs the mesher (`tet`: `mesh.tet.generate`; `planar`: `mesh.planar`; the hex and block meshers as they land) and replies with its result, `model.info` and `mesh.quality`. |
|
||||
| `solve` | `analysis` (static, frequency, explicit, fluid; heat as it lands), `step?`, `options?` | `solve.<analysis>` with the step and the solver's caps and settings. |
|
||||
| `show_results` | `step?`, `field?`, `bands?`, `deformed?`, `scale?`, `mode?`, `frame?`, `time?` | The `results.*` commands its arguments ask for; the reply has the contour range (smallest and largest value and their nodes: `field: "displacement"` gives the largest displacement), modes or frames. |
|
||||
| `show_results` | `step?`, `field?`, `bands?`, `deformed?`, `scale?`, `mode?`, `frame?`, `time?` | The `results.*` commands its arguments ask for (`deformed: false` draws the undeformed mesh; `scale` is a factor, 0 for the undeformed shape so a sequence from 0 swings the deformation out, or `"auto"`); the reply has the contour range (smallest and largest value and their nodes: `field: "displacement"` gives the largest displacement), modes or frames. |
|
||||
| `probe` | `step?`, `nodes?`, `elements?`, `points?`, `forces?`, `mode?`, `frame?` | `solve.probe`: stored results at nodes, elements (explicit) or points (fluid). |
|
||||
| `reactions` | `step?`, `sets?`, `about?` | `results.reactions`: reaction force and moment per node set and in total. |
|
||||
| `screenshot` | `view?`, `camera?`, `source?` (window, model), `width?`, `height?`, `path?` | A PNG image content block and what it shows. Headless: a CPU render framing the model, the geometry and the results display (deformed shape, contour bands). Bridged: the whole app window drawn offscreen (`ui.capture`, the default) or a model render with the app's camera (`ui.render`). `view` turns the camera to a standard view first; `camera` `{yaw, pitch, distance?}` to any orientation, in degrees (yaw 0 looks along +Y, −45 is the iso view's; pitch is the elevation, clamped to ±90) and model units (`ui.camera`; headless, the picture is framed unless `distance` is given). Give one of the two; the orientation is kept for the next picture, so a sequence of `camera` angles orbits smoothly. |
|
||||
|
||||
Reference in new issue
Block a user