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 <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 01:01:43 -04:00
1 parent dddb2d0584
commit a0d11f1c22
4 files changed
+24 -5

No files matched your search

+18 -5
View File
@@ -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<Value> {
let name = super::name(p)?.to_string();
let datum: String = super::req(p, "datum")?;
let bodies: Vec<String> = 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<Value> {
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<String> = 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<String>) = (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::<Vec<_>>(),
"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();
+1
View File
@@ -105,6 +105,7 @@ fn open_sample(s: &mut Session, p: &Value) -> Result<Value> {
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);
+3
View File
@@ -879,6 +879,9 @@ mod tests {
let mut cells: Vec<String> = 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());
+2
View File
@@ -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);