feat: query directional elastic modulus from validated material cards (#998)
* feat: validate physical material cards with sources and conditions * feat: query directional elastic modulus from physical material cards * fix: register physical material validation labels in complete locales * fix: register directional elasticity labels in complete locales * Add German and Korean labels for the material property commands --------- Co-authored-by: bherbruck <[email protected]>
This commit is contained in:
1 parent
377e12a059
commit
52fb5ac0e6
11 files changed
+228
-7
No files matched your search
@@ -8,7 +8,28 @@ pub static COMMANDS: &[CommandSpec] = &[CommandSpec::new("material.properties.va
|
||||
.icon("material")
|
||||
.noundo()
|
||||
.doc_only()
|
||||
.params("card: schema_version 1 engineering material card in SI units. Returns numerical consistency, derived isotropic moduli, missing conditions and sources needing review. Read-only; does not assign/save properties or provide an analysis solver. See docs/physical-material-cards.md")];
|
||||
.params("card: schema_version 1 engineering material card in SI units. Returns numerical consistency, derived isotropic moduli, missing conditions and sources needing review. Read-only; does not assign/save properties or provide an analysis solver. See docs/physical-material-cards.md"),
|
||||
CommandSpec::new("material.properties.directional", "Directional Elastic Modulus", directional).icon("material").noundo().doc_only()
|
||||
.params("card: schema_version 1 engineering material card; direction: three finite nonzero-vector components in the card's material-local axes. Returns normalized direction, Young modulus in Pa and source/condition review. Read-only constitutive query; no structural solver, persistence or body-axis assignment. See docs/physical-material-cards.md")];
|
||||
|
||||
fn directional(_: &mut Session, params: &Value) -> Result<Value> {
|
||||
let cmd = "material.properties.directional";
|
||||
let object = params
|
||||
.as_object()
|
||||
.filter(|o| o.len() == 2 && o.contains_key("card") && o.contains_key("direction"))
|
||||
.ok_or_else(|| bad(cmd, "expected {card: ..., direction: [x,y,z]}"))?;
|
||||
let value = object.get("card").ok_or_else(|| bad(cmd, "missing card"))?;
|
||||
let direction = object
|
||||
.get("direction")
|
||||
.and_then(Value::as_array)
|
||||
.filter(|v| v.len() == 3)
|
||||
.ok_or_else(|| bad(cmd, "direction: expected exactly three numeric components"))?;
|
||||
let component = |i| direction.get(i).and_then(Value::as_f64).ok_or_else(|| bad(cmd, "direction: expected numeric components"));
|
||||
let direction = [component(0)?, component(1)?, component(2)?];
|
||||
let card = solidcraft_doc::physical_material::admit_card(value).map_err(|e| bad(cmd, e.to_string()))?;
|
||||
let response = card.directional_modulus(direction).map_err(|e| bad(cmd, e.to_string()))?;
|
||||
serde_json::to_value(response).map_err(|e| bad(cmd, e.to_string()))
|
||||
}
|
||||
|
||||
fn validate(_: &mut Session, params: &Value) -> Result<Value> {
|
||||
let object = params
|
||||
@@ -25,6 +46,25 @@ mod tests {
|
||||
use super::*;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[test]
|
||||
fn directional_command_reports_modulus_and_preserves_state_on_refusal() {
|
||||
let mut s = Session::default();
|
||||
let card = json!({"schema_version":1,"designation":"Synthetic","family":"other",
|
||||
"sources":[{"id":"test","title":"Original synthetic fixture","kind":"illustrative"}],
|
||||
"properties":[{"source_id":"test","data":{"kind":"elasticity","model":{"symmetry":"isotropic","young_modulus_pa":12e9,"poisson_ratio":0.25}}}]});
|
||||
let doc = s.doc.clone();
|
||||
let before = (s.revision, s.evaluations, s.undo.len(), s.redo.len(), s.camera, s.selection.clone());
|
||||
let response = s.execute("material.properties.directional", &json!({"card":card,"direction":[1,2,3]})).unwrap();
|
||||
assert_eq!(response["young_modulus_pa"], 12e9);
|
||||
assert_eq!(response["validation"]["source_review_required"], json!(["test"]));
|
||||
for direction in [json!([0, 0, 0]), json!([1, 2]), json!([1, 2, 3, 4]), json!([true, 0, 0]), json!(["1", 0, 0]), Value::Null] {
|
||||
assert!(s.execute("material.properties.directional", &json!({"card":card,"direction":direction})).is_err());
|
||||
}
|
||||
assert!(s.execute("material.properties.directional", &json!({"card":card,"direction":[1,0,0],"assign":true})).is_err());
|
||||
assert!(Arc::ptr_eq(&doc, &s.doc));
|
||||
assert_eq!(before, (s.revision, s.evaluations, s.undo.len(), s.redo.len(), s.camera, s.selection.clone()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn command_reports_analytic_moduli_and_refuses_unstable_data() {
|
||||
let mut s = Session::default();
|
||||
@@ -85,6 +125,11 @@ mod tests {
|
||||
let card = json!({"schema_version":1,"designation":"Synthetic","family":"other","sources":[],"properties":[]});
|
||||
s.execute("material.properties.validate", &json!({"card":card})).unwrap();
|
||||
assert!(s.execute("material.properties.validate", &json!({"card":false})).is_err());
|
||||
let elastic = json!({"schema_version":1,"designation":"Synthetic","family":"other",
|
||||
"sources":[{"id":"test","title":"Original fixture","kind":"illustrative"}],
|
||||
"properties":[{"source_id":"test","data":{"kind":"elasticity","model":{"symmetry":"isotropic","young_modulus_pa":12e9,"poisson_ratio":0.25}}}]});
|
||||
s.execute("material.properties.directional", &json!({"card":elastic,"direction":[1,2,3]})).unwrap();
|
||||
assert!(s.execute("material.properties.directional", &json!({"card":elastic,"direction":[0,0,0]})).is_err());
|
||||
assert!(Arc::ptr_eq(&doc, &s.doc));
|
||||
assert!(s.model.is_stale());
|
||||
assert_eq!(before, (s.revision, s.evaluations, s.active_sketch, s.undo.len(), s.redo.len()));
|
||||
|
||||
Reference in new issue
Block a user