From 2d5dfb2137398de032633eb878dfb3bc2cb8e57d Mon Sep 17 00:00:00 2001 From: jyviko <2501648+jyviko@users.noreply.github.com> Date: Wed, 14 Oct 2026 22:30:24 -0400 Subject: [PATCH] Forms: a one-line note per choice; the hex Method says what each needs Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo --- .../{mesh.hex.blocks.svg => mesh.cells.svg} | 0 ...cks.vectorcraft => mesh.cells.vectorcraft} | Bin ...h.hex.mapped.svg => mesh.hex.generate.svg} | 0 ...torcraft => mesh.hex.generate.vectorcraft} | Bin assets/icons/mesh.hex.sweep.svg | 10 ------- assets/icons/mesh.hex.sweep.vectorcraft | Bin 1789 -> 0 bytes .../{mesh.hex.blocks.json => mesh.cells.json} | 0 ...hex.mapped.json => mesh.hex.generate.json} | 0 assets/icons/src/mesh.hex.sweep.json | 1 - crates/engine/src/cmd/forms.rs | 26 ++++++++++++++++-- crates/engine/src/schema.rs | 15 +++++++++- crates/ui-egui/src/form.rs | 3 ++ 12 files changed, 40 insertions(+), 15 deletions(-) rename assets/icons/{mesh.hex.blocks.svg => mesh.cells.svg} (100%) rename assets/icons/{mesh.hex.blocks.vectorcraft => mesh.cells.vectorcraft} (100%) rename assets/icons/{mesh.hex.mapped.svg => mesh.hex.generate.svg} (100%) rename assets/icons/{mesh.hex.mapped.vectorcraft => mesh.hex.generate.vectorcraft} (100%) delete mode 100644 assets/icons/mesh.hex.sweep.svg delete mode 100644 assets/icons/mesh.hex.sweep.vectorcraft rename assets/icons/src/{mesh.hex.blocks.json => mesh.cells.json} (100%) rename assets/icons/src/{mesh.hex.mapped.json => mesh.hex.generate.json} (100%) delete mode 100644 assets/icons/src/mesh.hex.sweep.json diff --git a/assets/icons/mesh.hex.blocks.svg b/assets/icons/mesh.cells.svg similarity index 100% rename from assets/icons/mesh.hex.blocks.svg rename to assets/icons/mesh.cells.svg diff --git a/assets/icons/mesh.hex.blocks.vectorcraft b/assets/icons/mesh.cells.vectorcraft similarity index 100% rename from assets/icons/mesh.hex.blocks.vectorcraft rename to assets/icons/mesh.cells.vectorcraft diff --git a/assets/icons/mesh.hex.mapped.svg b/assets/icons/mesh.hex.generate.svg similarity index 100% rename from assets/icons/mesh.hex.mapped.svg rename to assets/icons/mesh.hex.generate.svg diff --git a/assets/icons/mesh.hex.mapped.vectorcraft b/assets/icons/mesh.hex.generate.vectorcraft similarity index 100% rename from assets/icons/mesh.hex.mapped.vectorcraft rename to assets/icons/mesh.hex.generate.vectorcraft diff --git a/assets/icons/mesh.hex.sweep.svg b/assets/icons/mesh.hex.sweep.svg deleted file mode 100644 index 4a5494e..0000000 --- a/assets/icons/mesh.hex.sweep.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - mesh.hex.sweep - - - - - - - diff --git a/assets/icons/mesh.hex.sweep.vectorcraft b/assets/icons/mesh.hex.sweep.vectorcraft deleted file mode 100644 index 82fb4de6a5cf4d43495a74c78183b3a5797660eb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1789 zcmV&57x=&L_~uxNlTozOb(jIe;){5X?;akHN8UM0%GRiJ&YWwUca-G}EjwolCDsyJ zJ7?k4S>YNp1dY(^v*>lsdrjO~HglnGLGyl%YZ&D2{ylH%bfl{=ytBq_IV)scJy(e1Z!JaOgOWZ`dx_Mt*O)>E0kYZ^2AjHaTPVprC#8| zQfB67A@ET<7f-U$R|vdNa5Xgs@&TsW3Qvx}6Gj|Ds6&^C<2w?LEtFV$S^*`}F_na} z=XZ^uyjm|r{Ah(F4FfOGHi|RX%VJLf@p+3MMaDpW$5lmdtdT$<8EH?J1>zHGohUAi zgfe-k^xzKc;+FvF_2?4;`?&aXe4q*Yn}L# zJjO^~uVJHD6-Ros2p9?q(m{(a)nU%r-!Q6Rel0*1cU(eSh z5PTi6==Zh=Jh9w*daLebf|xou)bqv|$=kXhD{6~H<_RKAR5@dUL|Z0M;Ed6HCbYtx zB^r&w8@N(2YZG&YeC%0MSv&PX8pRWC)k}9sgnrB??k@<^Zk??YiNS4yz~n)U7;uD< zIaMzp47XisWuy^5#SR&&nOla`wt@1)pmj)*FcB`*eD*SWW9!NiIeqU#Gf@t-Pc>iQ ztevSgM2ir}neiZI*D<1`#n_nryrdutb=JFWt%#KjH3qA`4AacP zd_uKt?2`BNW3kAJfTGsfZQNQS-x{ z>HXu`F4Og%)Q~WPwzi{xwK9I_tUdw%Rg z$&u&B=1DS{q*tBI*bVwX)#NA*$}#@08vNAP?ZKGurD$iE9yy&J4dbAfQ0|8u9}E;; zPPb-zwbLora6fc9^nAr=-wYDbZy4I}!|X#z>UTe6?ig6U|QUYr!!3Zh7wG@AZ6wg}9KZQP*gWVii zmEBXbR<>H7Pk6gU)bnJHXcsVw7&{uV=@dYYNXFk|5euH1jy^R~7*3H(96|_XW^pW# zW725DWlE(0#-H9vAV=k`(n0!N2V0rZagjJG1?D#;$>(jisfO(R2`^D!af^u_lTW;d z^Kuu74jzP_>QwyTY3KcQ60wM@yIK5p*?d87)t#INr>&C@eOEz<<9r{d{igrRf98Mv zjM4RG@>V3sUhtNfalZFk=qB9LP4J9v_y(gq#@wenaXpng(?65W0q#E diff --git a/assets/icons/src/mesh.hex.blocks.json b/assets/icons/src/mesh.cells.json similarity index 100% rename from assets/icons/src/mesh.hex.blocks.json rename to assets/icons/src/mesh.cells.json diff --git a/assets/icons/src/mesh.hex.mapped.json b/assets/icons/src/mesh.hex.generate.json similarity index 100% rename from assets/icons/src/mesh.hex.mapped.json rename to assets/icons/src/mesh.hex.generate.json diff --git a/assets/icons/src/mesh.hex.sweep.json b/assets/icons/src/mesh.hex.sweep.json deleted file mode 100644 index 571b39a..0000000 --- a/assets/icons/src/mesh.hex.sweep.json +++ /dev/null @@ -1 +0,0 @@ -{"shapes": [{"rect": [3, 8, 9, 9], "fill": "soft", "stroke": "line"}, {"d": "M7.5 8 L7.5 17 M3 12.5 L12 12.5", "stroke": "line", "width": 1}, {"d": "M3 8 L8 4 L21 4 L21 13 L12 17 M12 8 L21 4", "stroke": "line", "width": 1.25}, {"d": "M14 13 L20 10 M18.17 12.63 L20 10 L16.8 9.88", "stroke": "accent", "width": 1.5}]} diff --git a/crates/engine/src/cmd/forms.rs b/crates/engine/src/cmd/forms.rs index ccbf518..59ec434 100644 --- a/crates/engine/src/cmd/forms.rs +++ b/crates/engine/src/cmd/forms.rs @@ -999,7 +999,11 @@ pub const HEX_GENERATE: &[Field] = &[ "Method", &[("auto", "Automatic"), ("swept", "Any sweep"), ("extrude", "Extrude"), ("sweep", "Sweep"), ("revolve", "Revolve"), ("mapped", "Mapped")], ) - .help("How to build the hexes: automatic, or a sweep, extrusion, revolution or mapped mesh.") + .help( + "How to build the hexes. Automatic tries, in order, an extrusion, a sweep, a revolution and a mapped block, and \ + uses the first that fits; Any sweep tries the first three.", + ) + .notes(HEX_METHOD_NOTES) .def("\"auto\""), TOLERANCE, WELD, @@ -1007,6 +1011,16 @@ pub const HEX_GENERATE: &[Field] = &[ FEATURE_ANGLE, ]; +/// What each structured hex method needs of the body, one line each. +const HEX_METHOD_NOTES: &[(&str, &str)] = &[ + ("auto", "Automatic: the first of Extrude, Sweep, Revolve and Mapped that fits the body."), + ("swept", "Any sweep: the first of Extrude, Sweep and Revolve that fits the body."), + ("extrude", "Extrude: the body must be one cross-section carried along a straight direction (levels allowed)."), + ("sweep", "Sweep: a source face carried to a target face through four-sided linking faces."), + ("revolve", "Revolve: the body must be one cross-section turned around an axis."), + ("mapped", "Mapped: the body must have six sides, each with four edges (curved faces allowed)."), +]; + pub const HEX_SWEEP: &[Field] = &[ BODY, SURFACE, @@ -1014,6 +1028,7 @@ pub const HEX_SWEEP: &[Field] = &[ HEX_ORDER, choice("method", "Method", &[("swept", "Any sweep"), ("extrude", "Extrude"), ("sweep", "Sweep"), ("revolve", "Revolve")]) .help("How to sweep: any sweep, extrusion, sweep along a path or revolution.") + .notes(HEX_METHOD_NOTES) .def("\"swept\""), TOLERANCE, WELD, @@ -1039,8 +1054,13 @@ pub const CELLS: &[Field] = &[ json("methods", "Methods") .help("The meshing method per cell, as JSON.") .hint(r#"{"cell": "auto | hex | swept | extrude | sweep | revolve | mapped | tet"}"#), - choice("default", "Other cells", &[("auto", "Auto (hex, else tet)"), ("hex", "Hex"), ("swept", "Swept"), ("mapped", "Mapped"), ("tet", "Tet")]) - .help("The method for cells not listed in Methods.") + choice( + "default", + "Other cells", + &[("auto", "Auto (hex, else tet)"), ("hex", "Hex"), ("swept", "Swept"), ("mapped", "Mapped"), ("tet", "Tet"), ("blocks", "All hex (conforming blocks)")], + ) + .help("The method for cells not listed in Methods. All hex meshes every cell as a mapped block, with the division counts of all cells assigned together so shared faces match (mesh.hex.blocks).") + .notes(&[("blocks", "All hex: every cell must have six sides, each with four edges; leave Methods empty.")]) .def("\"auto\""), references("bodies", "Cells", C::Body).help("The cells (bodies) to mesh; every body of the design when empty.").hint("every body of the design"), HEX_SIZE, diff --git a/crates/engine/src/schema.rs b/crates/engine/src/schema.rs index bc60f1f..17154d9 100644 --- a/crates/engine/src/schema.rs +++ b/crates/engine/src/schema.rs @@ -36,6 +36,9 @@ pub struct Field { /// What the field means, in a sentence or two, with a typical value: the form's tooltip and /// the JSON Schema description. The unit is added from the field's dimension ([`Field::help_text`]). pub help: &'static str, + /// For a [`Kind::Choice`]: a one-line note per option value (what it needs), shown under the + /// field for the option chosen. + pub notes: &'static [(&'static str, &'static str)], } /// What a field holds. @@ -306,7 +309,7 @@ impl Dim { // ---- Building fields (const, so schemas are static tables) ---- const fn field(key: &'static str, label: &'static str, kind: Kind) -> Field { - Field { key, label, kind, required: false, default: None, hint: "", help: "" } + Field { key, label, kind, required: false, default: None, hint: "", help: "", notes: &[] } } impl Field { @@ -327,6 +330,16 @@ impl Field { Field { help, ..self } } + /// A one-line note per option value ([`Field::notes`]). + pub const fn notes(self, notes: &'static [(&'static str, &'static str)]) -> Field { + Field { notes, ..self } + } + /// The note for this option value (the default when `None`). + pub fn note(&self, value: Option<&str>) -> Option<&'static str> { + let value = value.or_else(|| self.default.map(|d| d.trim_matches('"')))?; + self.notes.iter().find(|(v, _)| *v == value).map(|(_, n)| *n) + } + /// Its physical dimension, for a number or a vector of numbers. pub fn dim(&self) -> Option { match self.kind { diff --git a/crates/ui-egui/src/form.rs b/crates/ui-egui/src/form.rs index 0b83e24..6656034 100644 --- a/crates/ui-egui/src/form.rs +++ b/crates/ui-egui/src/form.rs @@ -226,6 +226,9 @@ impl Form<'_> { }; self.touch(&path); } + if let Some(note) = f.note(obj.get(f.key).and_then(Value::as_str)) { + fields::note(ui, &self.style, note, self.style.dim); + } if let Kind::Either(options) = f.kind { self.either_value(ui, f, options, obj, &path); }