From a0d11f1c2260fa2c2a904d50a1482ff1f47e64ed Mon Sep 17 00:00:00 2001 From: jyviko <2501648+jyviko@users.noreply.github.com> Date: Thu, 15 Oct 2026 00:33:31 -0400 Subject: [PATCH] Partitions hide their datum plane geometry.partition hides the plane it cuts by (view state, saved with the model): the cut shows as edges on the cells. The eye or selecting it in the browser shows it again. The crank samples so open without their planes. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo --- crates/engine/src/cmd/datum.rs | 23 ++++++++++++++++++----- crates/engine/src/cmd/file.rs | 1 + crates/engine/src/sample.rs | 3 +++ crates/ui-egui/src/menu.rs | 2 ++ 4 files changed, 24 insertions(+), 5 deletions(-) diff --git a/crates/engine/src/cmd/datum.rs b/crates/engine/src/cmd/datum.rs index e837bd9..aa7bf7b 100644 --- a/crates/engine/src/cmd/datum.rs +++ b/crates/engine/src/cmd/datum.rs @@ -13,6 +13,7 @@ use crate::schema::ProblemKind; use std::sync::Arc; use fieldcraft_geometry::{Cut, Design}; +use fieldcraft_io::{Item, ItemKind, MAX_HIDDEN}; use fieldcraft_model::{Datum, Model, Partition}; use serde::Deserialize; use serde_json::{Value, json}; @@ -443,7 +444,7 @@ fn partition(s: &mut Session, p: &Value) -> Result { let name = super::name(p)?.to_string(); let datum: String = super::req(p, "datum")?; let bodies: Vec = super::opt(p, "bodies")?.unwrap_or_default(); - s.model.add_partition(Partition { name: name.clone(), datum, bodies })?; + s.model.add_partition(Partition { name: name.clone(), datum: datum.clone(), bodies })?; recut(s)?; let design = s.design.as_deref().ok_or_else(|| EngineError::Params("open a design first".into()))?; let feature = format!("partition {name}"); @@ -456,13 +457,19 @@ fn partition(s: &mut Session, p: &Value) -> Result { if split.is_empty() { return Err(EngineError::Params(format!("partition `{name}`: the plane divides no body of the design"))); } - let cells: Vec<&str> = design.bodies.iter().map(|b| b.name.as_str()).filter(|b| split.iter().any(|x| b.starts_with(&format!("{x}/")))).collect(); + let cells: Vec = design.bodies.iter().map(|b| b.name.clone()).filter(|b| split.iter().any(|x| b.starts_with(&format!("{x}/")))).collect(); + let (faces, bodies): (usize, Vec) = (report.map_or(0, |r| r.faces), design.bodies.iter().map(|b| b.name.clone()).collect()); + // The cut shows as edges on the cells, so the plane steps out of the way (view state, #295): + // selecting it in the browser or its eye shows it again. + if s.view.hidden.len() < MAX_HIDDEN { + s.view.hidden.insert(Item { kind: ItemKind::Datum, name: datum }); + } Ok(json!({ "name": name, "split": split, "cells": cells, - "cut_faces": report.map_or(0, |r| r.faces), - "bodies": design.bodies.iter().map(|b| b.name.as_str()).collect::>(), + "cut_faces": faces, + "bodies": bodies, })) } @@ -550,8 +557,13 @@ pub(crate) mod tests { let body = r["split"][0].as_str().unwrap().to_string(); assert_eq!(r["cells"], json!([format!("{body}/a"), format!("{body}/b")])); assert_eq!(r["cut_faces"], json!(4)); - // The datum is in use. + // The datum is in use, and hidden: the cut shows as edges on the cells (#295). Its eye + // shows it again. assert!(s.execute("geometry.datum.delete", &json!({"name": "Mid"})).is_err()); + assert!(!s.shows(ItemKind::Datum, "Mid")); + s.execute("view.show", &json!({"items": [{"kind": "datum", "name": "Mid"}]})).unwrap(); + assert!(s.shows(ItemKind::Datum, "Mid")); + s.execute("view.hide", &json!({"items": [{"kind": "datum", "name": "Mid"}]})).unwrap(); // A set on a cut face selects the cut face's pieces. let r = s.execute("geometry.bodies", &json!({})).unwrap(); @@ -564,6 +576,7 @@ pub(crate) mod tests { let mut t = Session::default(); t.execute("file.open", &json!({"path": file.to_str().unwrap()})).unwrap(); assert_eq!(bodies(&t), bodies(&s)); + assert!(!t.shows(ItemKind::Datum, "Mid"), "still hidden when opened again"); // geometry.open again keeps the model, so its partition. let design = dir.join("plate.solidcraft"); let r = t.execute("geometry.open", &json!({"path": design.to_str().unwrap()})).unwrap(); diff --git a/crates/engine/src/cmd/file.rs b/crates/engine/src/cmd/file.rs index a00a370..d14c44a 100644 --- a/crates/engine/src/cmd/file.rs +++ b/crates/engine/src/cmd/file.rs @@ -105,6 +105,7 @@ fn open_sample(s: &mut Session, p: &Value) -> Result { opened.run_script(&script)?; // A sample just opened has nothing to save: it opens again the same way. opened.saved_revision = Some(opened.revision); + opened.saved_view = opened.view.clone(); s.replace_with(opened); let mut reply = summary(s, &[]); reply["sample"] = json!(sample.name); diff --git a/crates/engine/src/sample.rs b/crates/engine/src/sample.rs index e1316ed..0b0f957 100644 --- a/crates/engine/src/sample.rs +++ b/crates/engine/src/sample.rs @@ -879,6 +879,9 @@ mod tests { let mut cells: Vec = CRANK_CELLS.iter().map(|c| c.to_string()).collect(); cells.push("CrankArm/b".into()); assert_eq!(bodies, cells); + // The partitions' datum planes are hidden: the crank opens without a cage of planes. + assert!(s.model.datums().len() >= 7 && s.model.datums().keys().all(|d| !s.shows(crate::ItemKind::Datum, d)), "datums shown"); + assert!(!s.is_dirty(), "a sample just opened has nothing to save"); assert!(s.model.ties().is_empty(), "every cut merged"); let elements = s.model.elements().values().filter(|e| e.kind == fieldcraft_model::ElementKind::Hex20).count(); assert_eq!(elements, s.model.elements().len()); diff --git a/crates/ui-egui/src/menu.rs b/crates/ui-egui/src/menu.rs index a837afe..8b9e317 100644 --- a/crates/ui-egui/src/menu.rs +++ b/crates/ui-egui/src/menu.rs @@ -222,6 +222,8 @@ mod tests { app.run("geometry.open", json!({"path": design.to_string_lossy()})).unwrap(); app.run("geometry.datum", json!({"name": "Mid", "by": {"type": "offset", "plane": "yz", "offset": 15}})).unwrap(); app.run("geometry.partition", json!({"name": "Half", "datum": "Mid"})).unwrap(); + // The partition hides its plane (#295); shown again here to be picked. + app.run("view.show", json!({"items": [{"kind": "datum", "name": "Mid"}]})).unwrap(); app.run("geometry.datum.point", json!({"name": "P", "by": {"type": "coordinates", "point": [0, 0, 0]}})).unwrap(); app.viewport.rect = Some(egui::Rect::from_min_size(egui::pos2(100.0, 50.0), egui::vec2(800.0, 600.0))); app.set_view(StandardView::Iso);