engine: pick-driven points, cells' methods per cell, rigid walls as interactions

- geometry.bodies lists each body's vertices; geometry.datum.point by
  vertex takes the design's vertex names (labelled Vertex, not Mesh
  vertex) (#346).
- geometry.centre: the point for what is picked: a vertex, nodes, a
  hole's centre (cylindrical faces or circular edges), else a centroid
  (#351).
- mesh.cells' form: the cells first, and a method drop-down per cell
  (schema kind RefChoices) instead of JSON (#344).
- interaction.rigid_wall(.delete): rigid walls of an explicit step from
  the Interactions tab, no longer coming soon (#350).

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-15 09:27:39 -04:00
1 parent e8e0b92d87
commit 1372094f8a
10 files changed
+564 -27

No files matched your search

+20 -1
View File
@@ -74,6 +74,9 @@ pub enum Kind {
Ref(Collection),
/// A list of such names.
RefList(Collection),
/// A choice per item: an object from the names of a collection's items to one of these
/// strings (value, label); an item left out takes the command's default.
RefChoices(Collection, &'static [(&'static str, &'static str)]),
/// A nested object.
Group(&'static [Field]),
/// A nested object (or, with an empty key, the parent object) tagged by `tag`: the tag's value
@@ -429,6 +432,10 @@ pub const fn reference(key: &'static str, label: &'static str, c: Collection) ->
pub const fn references(key: &'static str, label: &'static str, c: Collection) -> Field {
field(key, label, Kind::RefList(c))
}
pub const fn ref_choices(key: &'static str, label: &'static str, c: Collection, options: &'static [(&'static str, &'static str)]) -> Field {
field(key, label, Kind::RefChoices(c, options))
}
pub const fn group(key: &'static str, label: &'static str, fields: &'static [Field]) -> Field {
field(key, label, Kind::Group(fields))
}
@@ -659,7 +666,9 @@ impl Problem {
ProblemKind::NotText => "text".into(),
ProblemKind::NotABool => "true or false".into(),
ProblemKind::NotAChoice => match f.map(|f| f.kind) {
Some(Kind::Choice(options)) => format!("one of {}", options.iter().map(|(o, _)| *o).collect::<Vec<_>>().join(", ")),
Some(Kind::Choice(options) | Kind::RefChoices(_, options)) => {
format!("one of {}", options.iter().map(|(o, _)| *o).collect::<Vec<_>>().join(", "))
}
Some(Kind::Either(options)) => format!("one of {}", options.iter().map(|o| o.key).collect::<Vec<_>>().join(", ")),
_ => "one of the choices".into(),
},
@@ -914,6 +923,11 @@ fn value_problem(f: &'static Field, v: &Value, path: &str, out: &mut Vec<Problem
_ => Some(ProblemKind::NotAVector { len: labels.len() }),
},
Kind::RefList(_) => (!v.as_array().is_some_and(|a| a.iter().all(Value::is_string))).then_some(ProblemKind::NotNames),
Kind::RefChoices(_, options) => match v.as_object() {
Some(o) if o.values().all(|x| x.as_str().is_some_and(|s| options.iter().any(|(opt, _)| *opt == s))) => None,
Some(_) => Some(ProblemKind::NotAChoice),
None => Some(ProblemKind::NotAnObject),
},
Kind::Group(fields) => match v.as_object() {
Some(o) => {
check(fields, o, path, out, depth + 1);
@@ -1035,6 +1049,11 @@ fn field_schema(f: &Field, depth: usize) -> Value {
}
Kind::Ref(c) => json!({"type": "string", "x-references": c.label()}),
Kind::RefList(c) => json!({"type": "array", "items": {"type": "string", "x-references": c.label()}}),
Kind::RefChoices(c, options) => json!({
"type": "object",
"x-references": c.label(),
"additionalProperties": {"enum": options.iter().map(|(o, _)| *o).collect::<Vec<_>>()},
}),
Kind::Group(fields) if depth <= MAX_DEPTH => object_schema(fields, depth + 1),
Kind::Variants { tag, variants } if depth <= MAX_DEPTH => json!({"oneOf": variant_schemas(tag, variants, depth + 1)}),
Kind::Either(options) if depth <= MAX_DEPTH => json!({"oneOf": options.iter().map(|o| match o.kind {