mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-05 11:35:55 +00:00
Main's new callers of the placing calls that now return their error
Code main added since the last rebase reads the placed model, a camera or a scene as values; on this branch they return Result. Picks and the edit dialogs read the session's placed model through open_meshing::session_world, as the others do; the revolve and circular-pattern axes, the model axis of a command and the warming of a computed edit pass the error on; tests unwrap. A test meshing bodies uses Body::mesh, the one mesher. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HoZgkCdAVrscuw2kjQTWDs
This commit is contained in:
@@ -3598,11 +3598,12 @@ pub(crate) fn revolve_axis(
|
||||
}
|
||||
|
||||
/// The model a revolve axis is found in: what construction geometry of component `comp` is built
|
||||
/// on when the axis is an edge or face of it (only then is it worth placing).
|
||||
pub(super) fn axis_state<'a>(doc: &Document, st: &'a ModelState, comp: u64, axis: &AxisRef) -> std::borrow::Cow<'a, ModelState> {
|
||||
/// on when the axis is an edge or face of it (only then is it worth placing); a model that cannot
|
||||
/// be placed is the error.
|
||||
pub(super) fn axis_state<'a>(doc: &Document, st: &'a ModelState, comp: u64, axis: &AxisRef) -> Result<std::borrow::Cow<'a, ModelState>> {
|
||||
match axis {
|
||||
AxisRef::Model { .. } => construction_state(doc, st, comp),
|
||||
_ => std::borrow::Cow::Borrowed(st),
|
||||
_ => Ok(std::borrow::Cow::Borrowed(st)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4535,7 +4536,7 @@ fn feature_tools_with_constructor(
|
||||
)?;
|
||||
let regions = solidcraft_sketch::merge_regions(&select_profiles(&ss, profiles)?);
|
||||
let (o, d) =
|
||||
revolve_axis(&ss, axis, &axis_state(doc, st, f.component, axis), &|name| construction_axis_in(Some(doc), st, name, f.component))?;
|
||||
revolve_axis(&ss, axis, &*axis_state(doc, st, f.component, axis)?, &|name| construction_axis_in(Some(doc), st, name, f.component))?;
|
||||
// To Object is in this feature's component frame: the model as seen from there.
|
||||
let tst = if to.is_some() { state_in_frame(doc, st, f.component)? } else { std::borrow::Cow::Borrowed(st) };
|
||||
let angles = if to.is_some() {
|
||||
@@ -5371,7 +5372,7 @@ fn pattern_transforms(doc: &Document, vals: &BTreeMap<String, Value>, st: &Model
|
||||
crate::PatternKind::Circular { origin, axis, count: c, angle, alignment, reversed, axis_ref, axis_feature } => {
|
||||
// On an edge or round face: about it as it is now; on a construction axis: where it is.
|
||||
let (origin, axis) = match (axis_ref, axis_feature) {
|
||||
(Some(g), _) => crate::construct::model_axis(&construction_state(doc, st, component), g)?,
|
||||
(Some(g), _) => crate::construct::model_axis(&*construction_state(doc, st, component)?, g)?,
|
||||
(None, Some(name)) => construction_axis_in(Some(doc), st, name, component)?,
|
||||
(None, None) => (origin.0, axis.0),
|
||||
};
|
||||
|
||||
@@ -304,7 +304,7 @@ pub(super) fn eval(doc: &Document, vals: &BTreeMap<String, Value>, f: &Feature,
|
||||
st.sketch(sketch.0).ok_or_else(|| DocError::Unknown(format!("sketch {sketch} (it must come earlier in the timeline)")))?,
|
||||
f.component,
|
||||
)?;
|
||||
let (o, d) = revolve_axis(&ss, axis, &super::axis_state(doc, st, f.component, axis), &|name| {
|
||||
let (o, d) = revolve_axis(&ss, axis, &*super::axis_state(doc, st, f.component, axis)?, &|name| {
|
||||
construction_axis_in(Some(doc), st, name, f.component)
|
||||
})?;
|
||||
let a = val(vals, angle, Kind::Angle)?;
|
||||
|
||||
@@ -263,7 +263,7 @@ mod tests {
|
||||
let r = run(&mut s, "timeline.paste", json!({"component": a, "component_path": [at]}));
|
||||
assert_eq!(r["errors"].as_array().map(Vec::len), Some(0), "{r}");
|
||||
assert_eq!(r["pasted"][0]["name"], "S (2)");
|
||||
let world = s.world_state();
|
||||
let world = s.world_state().unwrap();
|
||||
let (original, copy) = (world.body("Block").unwrap().mesh().bounds(), world.body("Block (2)").unwrap().mesh().bounds());
|
||||
assert!(copy.min.dist(original.min) < 1e-6 && copy.max.dist(original.max) < 1e-6, "{original:?} vs {copy:?}");
|
||||
let state = s.model.state();
|
||||
|
||||
@@ -97,7 +97,7 @@ pub(super) fn model_axis_param(s: &Session, v: Option<&Value>, cmd: &str) -> Res
|
||||
let saved = |v: &Value| matches!(v.get("type").and_then(Value::as_str), Some("edge" | "face"));
|
||||
let Some(v) = v.filter(|v| v.get("edge").is_some() || v.get("face").is_some() || saved(v)) else { return Ok(None) };
|
||||
let g = geo(s, Some(v), cmd, "axis")?;
|
||||
let (o, d) = solidcraft_doc::construct::model_axis(&s.world_state(), &g).map_err(|e| bad(cmd, format!("`axis`: {e}")))?;
|
||||
let (o, d) = solidcraft_doc::construct::model_axis(&*s.world_state()?, &g).map_err(|e| bad(cmd, format!("`axis`: {e}")))?;
|
||||
Ok(Some((g, o, d)))
|
||||
}
|
||||
|
||||
|
||||
@@ -228,7 +228,7 @@ fn a_sketch_on_a_turned_sub_components_face_cuts_and_joins_it_from_the_top_level
|
||||
run(s, "sketch.circle.center", json!({"center": [c.x, c.y], "radius": 2}));
|
||||
});
|
||||
for (distance, expected) in [(-3.0, "cut"), (3.0, "join")] {
|
||||
assert_eq!(crate::auto_operation(&s, &json!({"distance": distance})), Some(expected), "base {base}, {distance}");
|
||||
assert_eq!(crate::auto_operation(&s, &json!({"distance": distance})).unwrap(), Some(expected), "base {base}, {distance}");
|
||||
for operation in ["auto", expected] {
|
||||
let mut t = s.scratch();
|
||||
run(&mut t, "solid.extrude", json!({"distance": distance, "operation": operation}));
|
||||
@@ -313,7 +313,7 @@ fn a_cut_from_the_top_level_shows_in_every_placement_and_a_later_placement_is_re
|
||||
run(s, "sketch.circle.center", json!({"center": [c.x, c.y], "radius": 2}));
|
||||
});
|
||||
run(&mut first, "solid.extrude", json!({"distance": -3, "operation": "cut"}));
|
||||
let world = first.world_state();
|
||||
let world = first.world_state().unwrap();
|
||||
let placements: Vec<f64> =
|
||||
world.bodies.iter().filter(|b| b.definition_name() == "Stock").map(|b| solidcraft_kernel::measure(&b.body).unwrap().volume).collect();
|
||||
let want = 1000.0 - std::f64::consts::PI * 4.0 * 3.0;
|
||||
@@ -342,8 +342,8 @@ fn a_sketch_on_a_top_level_body_beside_a_turned_part_cuts_only_that_body() {
|
||||
sketch_on_face(&mut s, solidcraft_geom::Vec3::new(-30.0, -30.0, 5.0), |s, c, _| {
|
||||
run(s, "sketch.circle.center", json!({"center": [c.x, c.y], "radius": 2}));
|
||||
});
|
||||
assert_eq!(crate::auto_operation(&s, &json!({"distance": -2})), Some("cut"));
|
||||
assert_eq!(crate::auto_operation(&s, &json!({"distance": 2})), Some("join"));
|
||||
assert_eq!(crate::auto_operation(&s, &json!({"distance": -2})).unwrap(), Some("cut"));
|
||||
assert_eq!(crate::auto_operation(&s, &json!({"distance": 2})).unwrap(), Some("join"));
|
||||
let made = run(&mut s, "solid.extrude", json!({"distance": -2, "operation": "auto"}));
|
||||
let feature = s.doc.feature(made["feature"].as_u64().unwrap()).unwrap();
|
||||
assert!(feature.participants.is_empty(), "{:?}", feature.participants);
|
||||
|
||||
@@ -31,7 +31,7 @@ pub(super) fn export_bodies(s: &Session, cmd: &str, named: &[String], format: Fo
|
||||
if bodies.is_empty() {
|
||||
return Err(bad(cmd, "select the bodies to send, or give `bodies`"));
|
||||
}
|
||||
let doc = s.doc.painted(&s.world_state());
|
||||
let doc = s.doc.painted(&*s.world_state()?);
|
||||
let write = |format| solidcraft_io::export_with_metadata(&doc, &bodies, format, &s.doc.name, &s.doc.metadata);
|
||||
let mut fell_back = false;
|
||||
let (bytes, ext) = match (format, write(format)) {
|
||||
|
||||
@@ -82,7 +82,7 @@ fn surface_revolve_about_a_circular_edge() {
|
||||
ring_profile(&mut s);
|
||||
run(&mut s, "surface.revolve", json!({"sketch": "Profile", "axis": {"edge": [45, 0, 0]}, "angle": "180 deg"}));
|
||||
run(&mut s, "parameters.change", json!({"name": "cx", "expression": "41 mm"}));
|
||||
let st = s.world_state();
|
||||
let st = s.world_state().unwrap();
|
||||
// A half turn about x = 41: the far side of the profile ends up at x = 41 − 7 ± 1.
|
||||
let b = st.bodies.last().map(|b| b.mesh().bounds()).unwrap();
|
||||
assert!((b.min.x - (41.0 - 7.0)).abs() < 1e-3, "{b:?}");
|
||||
@@ -98,14 +98,14 @@ fn circular_pattern_about_a_boss_wall_follows_the_boss() {
|
||||
run(&mut s, "solid.hole", json!({"position": [130, 100, 5], "diameter": 2, "name": "Hole"}));
|
||||
run(&mut s, "solid.pattern.circular", json!({"features": ["Hole"], "axis": {"face": [110, 100, 10]}, "count": 4, "name": "Ring"}));
|
||||
let holes = |s: &Session, cx: f64, r: f64| {
|
||||
let st = s.world_state();
|
||||
let st = s.world_state().unwrap();
|
||||
let body = &st.bodies[0].body;
|
||||
[(r, 0.0), (0.0, r), (-r, 0.0), (0.0, -r)].iter().all(|(dx, dy)| !body.contains(Vec3::new(cx + dx, 100.0 + dy, 2.5)))
|
||||
};
|
||||
assert!(holes(&s, 100.0, 30.0), "four holes round the boss");
|
||||
run(&mut s, "parameters.change", json!({"name": "cx", "expression": "90 mm"}));
|
||||
assert!(holes(&s, 90.0, 40.0), "the copies turn about the boss where it is now");
|
||||
let st = s.world_state();
|
||||
let st = s.world_state().unwrap();
|
||||
assert!(st.bodies[0].body.contains(Vec3::new(100.0, 130.0, 2.5)), "not where the boss was");
|
||||
}
|
||||
|
||||
@@ -158,7 +158,7 @@ fn the_axis_keeps_its_turn_whichever_way_the_edge_runs() {
|
||||
let mut s = shaft();
|
||||
ring_profile(&mut s);
|
||||
run(&mut s, "solid.revolve", json!({"sketch": "Profile", "axis": {"edge": edge}, "angle": "90 deg", "operation": "new"}));
|
||||
let st = s.world_state();
|
||||
let st = s.world_state().unwrap();
|
||||
st.bodies.last().map(|b| b.mesh().bounds()).unwrap()
|
||||
};
|
||||
let (top, bottom) = (quarter([45.0, 0.0, 10.0]), quarter([45.0, 0.0, 0.0]));
|
||||
|
||||
@@ -1351,10 +1351,10 @@ fn pasted_and_moved_things_survive_reopening() {
|
||||
run(&mut s, "solid.box", json!({"length": 1, "width": 1, "height": 1, "corner": [50, 0, 0], "body_name": "Pin"}));
|
||||
run(&mut s, "component.activate", json!({"component": 0}));
|
||||
run(&mut s, "occurrence.reparent", json!({"occurrence": b_at, "parent": a}));
|
||||
let before = s.world_state();
|
||||
let before = s.world_state().unwrap();
|
||||
let mut reopened = Session { doc: solidcraft_doc::Document::from_json(&s.doc.to_json()).unwrap().into(), ..Session::default() };
|
||||
reopened.refresh();
|
||||
let after = reopened.world_state();
|
||||
let after = reopened.world_state().unwrap();
|
||||
for name in ["Cube", "Cube (2)", "Pin"] {
|
||||
let (was, is) = (before.body(name).unwrap().mesh().bounds(), after.body(name).unwrap().mesh().bounds());
|
||||
assert!(was.min.dist(is.min) < 1e-6 && was.max.dist(is.max) < 1e-6, "{name}: {was:?} → {is:?}");
|
||||
@@ -1377,7 +1377,7 @@ fn a_body_seen_in_another_placement_copies_from_there() {
|
||||
assert_ne!(shown, "Cube");
|
||||
let out = run(&mut s, "component.copy_bodies", json!({"bodies": [shown.clone()], "component": 0}));
|
||||
assert_eq!(out["bodies"], json!(["Cube (2)"]));
|
||||
let world = s.world_state();
|
||||
let world = s.world_state().unwrap();
|
||||
let (seen, copy) = (world.body(&shown).unwrap().mesh().bounds(), world.body("Cube (2)").unwrap().mesh().bounds());
|
||||
assert!(seen.min.dist(copy.min) < 1e-6 && seen.max.dist(copy.max) < 1e-6, "{seen:?} vs {copy:?}");
|
||||
let state = s.model.state();
|
||||
|
||||
@@ -365,14 +365,14 @@ fn arc3d_follows_the_sketch_owner_placement() {
|
||||
near(arc.center, Vec3::new(0.0, 0.0, 5.0));
|
||||
assert!((arc.radius - 10.0).abs() < 1e-7, "radius {}", arc.radius);
|
||||
let _ = sk;
|
||||
let world = s.world_state();
|
||||
let world = s.world_state().unwrap();
|
||||
let ws = &world.sketch(sketch).unwrap().sketch;
|
||||
let arc = ws.wires[ws.wire_index(&id).unwrap()].arc.unwrap();
|
||||
near(arc.center, Vec3::new(20.0, 30.0, 45.0));
|
||||
near(ws.wires[ws.wire_index(&id).unwrap()].fit[0], Vec3::new(20.0, 40.0, 45.0));
|
||||
drop(world);
|
||||
run(&mut s, "occurrence.move", json!({"occurrence":second,"translate":[5,0,0]}));
|
||||
let world = s.world_state();
|
||||
let world = s.world_state().unwrap();
|
||||
let ws = &world.sketch(sketch).unwrap().sketch;
|
||||
let arc = ws.wires[ws.wire_index(&id).unwrap()].arc.unwrap();
|
||||
near(arc.center, Vec3::new(25.0, 30.0, 45.0));
|
||||
@@ -411,7 +411,7 @@ fn arc3d_pipes_along_the_arc() {
|
||||
let wire = run(&mut s, "sketch.arc3d", json!({"start":[20,0,0],"through":[d,d,0],"end":[0,20,0]}))["curves"][0].as_str().unwrap().to_string();
|
||||
run(&mut s, "sketch.finish", json!({}));
|
||||
run(&mut s, "solid.pipe", json!({"path_sketch":sketch,"path":[wire],"diameter":4}));
|
||||
let world = s.world_state();
|
||||
let world = s.world_state().unwrap();
|
||||
let body = world.bodies.last().unwrap();
|
||||
let measure = solidcraft_kernel::measure(&body.body).unwrap();
|
||||
let quarter = 20.0 * std::f64::consts::FRAC_PI_2;
|
||||
|
||||
@@ -16,15 +16,15 @@ fn acceptance_preserves_file_history_and_prepared_world_without_evaluation() {
|
||||
let checkpoint = session.checkpoint();
|
||||
let history = session.undo.len();
|
||||
let prepared = session.scratch().compute_commands(&commands(), None).unwrap();
|
||||
prepared.warm_meshes(|| true);
|
||||
let world = prepared.computed.world_state();
|
||||
prepared.warm_meshes(|| true).unwrap();
|
||||
let world = prepared.computed.world_state().unwrap();
|
||||
let before = session.evaluations;
|
||||
session.accept_commands(prepared).unwrap();
|
||||
assert_eq!(session.evaluations, before);
|
||||
assert_eq!(session.path.as_deref(), Some("working.solidcraft"));
|
||||
assert!(Arc::ptr_eq(&saved, &session.saved));
|
||||
assert_eq!(session.undo.len(), history + 1);
|
||||
assert!(Arc::ptr_eq(&world, &session.world_state()));
|
||||
assert!(Arc::ptr_eq(&world, &session.world_state().unwrap()));
|
||||
session.restore_checkpoint(checkpoint).unwrap();
|
||||
assert_eq!(session.undo.len(), history);
|
||||
assert_eq!(session.doc.features.len(), 1);
|
||||
@@ -65,14 +65,14 @@ fn revision_context_selection_and_replaced_document_reject_old_work() {
|
||||
fn editor_marker_restoration_reuses_the_pinned_model_with_an_empty_cache() {
|
||||
let mut session = Session::default();
|
||||
session.execute("solid.box", &json!({"length":2,"width":3,"height":4})).unwrap();
|
||||
let world = session.world_state();
|
||||
let world = session.world_state().unwrap();
|
||||
let original = session.scratch();
|
||||
session.execute("timeline.roll_to", &json!({"position":0})).unwrap();
|
||||
let source = session.scratch();
|
||||
*session.model.cache.lock().unwrap() = Default::default();
|
||||
let prepared = session.scratch().compute_editor_commands(&[], &source, original).unwrap();
|
||||
assert_eq!(prepared.computed.evaluations, 0, "restoring the marker must never evaluate the old suffix");
|
||||
assert!(Arc::ptr_eq(&world, &prepared.computed.world_state()));
|
||||
assert!(Arc::ptr_eq(&world, &prepared.computed.world_state().unwrap()));
|
||||
session.accept_commands(prepared).unwrap();
|
||||
assert_eq!(session.doc.marker, None);
|
||||
}
|
||||
|
||||
@@ -310,15 +310,17 @@ impl ComputedCommands {
|
||||
session.same_document_context(source) && session.selection == source.selection
|
||||
}
|
||||
|
||||
/// Prepare display tessellation before returning the transaction to the window.
|
||||
pub fn warm_meshes(&self, keep_going: impl Fn() -> bool + Sync) {
|
||||
self.warm_display(solidcraft_doc::display_quality::DisplayQuality::Normal, keep_going);
|
||||
/// Prepare display tessellation before returning the transaction to the window; a model that
|
||||
/// cannot be placed is the error.
|
||||
pub fn warm_meshes(&self, keep_going: impl Fn() -> bool + Sync) -> Result<()> {
|
||||
self.warm_display(solidcraft_doc::display_quality::DisplayQuality::Normal, keep_going)
|
||||
}
|
||||
|
||||
/// Prepare both the reference picking mesh and the GPU plan/highlight mesh at the
|
||||
/// selected quality. Warming only the reference mesh leaves cold planning on the UI.
|
||||
pub fn warm_display(&self, quality: solidcraft_doc::display_quality::DisplayQuality, keep_going: impl Fn() -> bool + Sync) {
|
||||
self.computed.world_state().warm_display_at(quality, keep_going);
|
||||
pub fn warm_display(&self, quality: solidcraft_doc::display_quality::DisplayQuality, keep_going: impl Fn() -> bool + Sync) -> Result<()> {
|
||||
self.computed.world_state()?.warm_display_at(quality, keep_going);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1980,7 +1980,7 @@ fn components_occurrences_and_world_placement() {
|
||||
assert_eq!(s.pending_moves, pending);
|
||||
// Placed by the first placement, the body stays where it is in the world there.
|
||||
run(&mut s, "component.move_bodies", json!({"bodies": ["Base"], "component": "Arm", "component_path": [occ]}));
|
||||
assert_eq!(s.world_state().body("Base").unwrap().component, cid);
|
||||
assert_eq!(s.world_state().unwrap().body("Base").unwrap().component, cid);
|
||||
assert_eq!(bbox(&mut s, "Base")["min"], json!([0.0, 0.0, 0.0]));
|
||||
run(&mut s, "edit.undo", json!({}));
|
||||
assert_eq!(s.doc.to_json(), document);
|
||||
@@ -3657,7 +3657,7 @@ fn an_exact_pick_of_a_region_cut_by_the_face_outline_takes_only_that_region() {
|
||||
let state = s2.model.state();
|
||||
let Some(solidcraft_doc::FeatureKind::Extrude { profiles, .. }) = s2.doc.features.last().map(|f| &f.kind) else { panic!("an extrude") };
|
||||
let count = solidcraft_doc::profile_selection_indices(state.sketch(id).unwrap(), profiles).unwrap().len();
|
||||
let v = state.bodies.iter().filter(|b| b.name.starts_with("Pad")).map(|b| b.body.tessellate(1e-2).unwrap().measure().volume).sum();
|
||||
let v = state.bodies.iter().filter(|b| b.name.starts_with("Pad")).map(|b| b.body.mesh(&solidcraft_kernel::MeshTolerance::chord(1e-2)).unwrap().measure().volume).sum();
|
||||
(v, count)
|
||||
};
|
||||
for (profiles, want, n) in [
|
||||
@@ -4498,7 +4498,7 @@ fn a_cut_leaves_hidden_bodies_alone() {
|
||||
run(&mut s, "solid.extrude", json!({"distance": 30, "direction": "negative", "operation": "cut"}));
|
||||
s
|
||||
};
|
||||
let vol = |s: &Session, name: &str| solidcraft_kernel::measure(&s.world_state().body(name).unwrap().body).unwrap().volume;
|
||||
let vol = |s: &Session, name: &str| solidcraft_kernel::measure(&s.world_state().unwrap().body(name).unwrap().body).unwrap().volume;
|
||||
let full = 20.0 * 20.0 * 10.0;
|
||||
let mut hidden = setup(true);
|
||||
assert!((vol(&hidden, "Below") - full).abs() < 1e-6, "the hidden body is not cut");
|
||||
|
||||
@@ -404,9 +404,9 @@ fn a_cut_hides_the_edges_behind_it() {
|
||||
let (w, h) = (400, 300);
|
||||
for (mode, c) in capturers() {
|
||||
for fov in [0.0, 0.6] {
|
||||
let mut cam = view::home_camera(&plain);
|
||||
let mut cam = view::home_camera(&plain).unwrap();
|
||||
cam.fov = fov;
|
||||
let radius = view::scene(&plain, &cam).radius;
|
||||
let radius = view::scene(&plain, &cam).unwrap().radius;
|
||||
// The cap on screen: the cut's rectangle, its outline left out (2 px in).
|
||||
let corner = |x: f64, z: f64| cam.to_screen(Vec3::new(x, -16.0, z), f64::from(w), f64::from(h), radius).map(|(x, y, _)| (x, y));
|
||||
let cap: Vec<(f64, f64)> = [(-20.0, 0.0), (20.0, 0.0), (20.0, 30.0), (-20.0, 30.0)].iter().filter_map(|(x, z)| corner(*x, *z)).collect();
|
||||
@@ -422,7 +422,7 @@ fn a_cut_hides_the_edges_behind_it() {
|
||||
};
|
||||
let inside = |x: f64, y: f64| inside_by(x, y, 2.0);
|
||||
// Without the ground grid, whose extent follows the design.
|
||||
let shot = |s: &Session| c.rgba(&Scene { grid: Vec::new(), ..view::scene(s, &cam) }, &cam, w, h).unwrap();
|
||||
let shot = |s: &Session| c.rgba(&Scene { grid: Vec::new(), ..view::scene(s, &cam).unwrap() }, &cam, w, h).unwrap();
|
||||
let (a, b) = (shot(&plain), shot(&cut_through));
|
||||
let (mut shown, mut all) = (0, 0);
|
||||
for (i, (p, q)) in pixels(&a).zip(pixels(&b)).enumerate() {
|
||||
|
||||
@@ -313,7 +313,7 @@ fn cancelling_an_edit_restores_pinned_geometry_without_evaluating_an_evicted_suf
|
||||
let (mut h, target) = cut_chain();
|
||||
h.app.run("solid.box", json!({"corner":[100,0,0],"length":2,"width":2,"height":2})).unwrap();
|
||||
h.app.run("edit.undo", json!({})).unwrap();
|
||||
let before = h.app.session.world_state();
|
||||
let before = h.app.session.world_state().unwrap();
|
||||
let history = (h.app.session.undo.len(), h.app.session.redo.len());
|
||||
let visibility = h.app.session.visibility.clone();
|
||||
h.call("ui.editFeature", json!({"feature":target}));
|
||||
@@ -336,7 +336,7 @@ fn cancelling_an_edit_restores_pinned_geometry_without_evaluating_an_evicted_suf
|
||||
assert_eq!(h.app.session.visibility, visibility, "temporary sketch display does not change stored visibility");
|
||||
assert_eq!(h.app.session.doc.marker, None);
|
||||
assert_eq!((h.app.session.undo.len(), h.app.session.redo.len()), history);
|
||||
assert!(std::sync::Arc::ptr_eq(&before, &h.app.session.world_state()));
|
||||
assert!(std::sync::Arc::ptr_eq(&before, &h.app.session.world_state().unwrap()));
|
||||
assert_eq!(h.app.session.selection, vec![solidcraft_engine::Sel::Axis { name: "X".into() }]);
|
||||
}
|
||||
|
||||
@@ -370,7 +370,7 @@ fn a_worker_error_then_cancel_preserves_original_world_history_redo_and_selectio
|
||||
let (mut h, target) = cut_chain();
|
||||
h.app.run("solid.box", json!({"corner":[100,0,0],"length":2,"width":2,"height":2})).unwrap();
|
||||
h.app.run("edit.undo", json!({})).unwrap();
|
||||
let world = h.app.session.world_state();
|
||||
let world = h.app.session.world_state().unwrap();
|
||||
let document = h.app.session.doc.clone();
|
||||
let history = (h.app.session.undo.len(), h.app.session.redo.len());
|
||||
h.app.session.selection = vec![solidcraft_engine::Sel::Feature { id: target }];
|
||||
@@ -395,7 +395,7 @@ fn a_worker_error_then_cancel_preserves_original_world_history_redo_and_selectio
|
||||
});
|
||||
assert_eq!(window.features(), (0, 0));
|
||||
assert!(std::sync::Arc::ptr_eq(&document, &h.app.session.doc));
|
||||
assert!(std::sync::Arc::ptr_eq(&world, &h.app.session.world_state()));
|
||||
assert!(std::sync::Arc::ptr_eq(&world, &h.app.session.world_state().unwrap()));
|
||||
assert_eq!((h.app.session.undo.len(), h.app.session.redo.len()), history);
|
||||
assert_eq!(h.app.session.selection, selection);
|
||||
}
|
||||
|
||||
@@ -616,7 +616,7 @@ fn start_applying(
|
||||
if watching.is_cancelled() {
|
||||
return Err("feature computation cancelled".into());
|
||||
}
|
||||
prepared.warm_display(quality, || !watching.is_cancelled());
|
||||
prepared.warm_display(quality, || !watching.is_cancelled()).map_err(|e| e.to_string())?;
|
||||
if watching.is_cancelled() {
|
||||
return Err("feature computation cancelled".into());
|
||||
}
|
||||
@@ -1955,7 +1955,7 @@ pub fn planar_face(s: &Session, body: &str, face: usize) -> Option<(Vec3, Vec3)>
|
||||
/// The fillet or chamfer feature whose blend a body face is (a piece or copy of one too).
|
||||
pub fn blend_feature(s: &Session, body: &str, face: usize) -> Option<u64> {
|
||||
use solidcraft_engine::doc::persistent_name;
|
||||
let st = s.world_state();
|
||||
let st = crate::open_meshing::session_world(s);
|
||||
let b = st.body(body)?;
|
||||
let names = solidcraft_engine::doc::naming::face_names(b);
|
||||
let name = names.get(face).filter(|n| persistent_name::is_blend(n))?;
|
||||
@@ -4672,7 +4672,7 @@ pub fn axis_of(app: &SolveApp, sel: &Sel) -> Option<(Vec3, Vec3)> {
|
||||
/// (centre, unit normal, radius). A round edge stands for its circle wherever a 3D pick wants
|
||||
/// an axis (through the centre, along the normal) or a point (the centre).
|
||||
pub(crate) fn edge_circle(s: &Session, body: &str, index: usize) -> Option<(Vec3, Vec3, f64)> {
|
||||
edge_circle_in(&s.world_state(), body, index)
|
||||
edge_circle_in(&crate::open_meshing::session_world(s), body, index)
|
||||
}
|
||||
|
||||
/// [`edge_circle`] in a given model state (a dialog editing a feature looks at the model before it).
|
||||
@@ -4692,7 +4692,7 @@ pub(crate) fn round_axis(s: &Session, sel: &Sel) -> Option<(Vec3, Vec3)> {
|
||||
let (o, d) = match sel {
|
||||
Sel::Edge { body, index, .. } => edge_circle(s, body, *index).map(|(c, n, _)| (c, n))?,
|
||||
Sel::Face { body, index, .. } => {
|
||||
let st = s.world_state();
|
||||
let st = crate::open_meshing::session_world(s);
|
||||
let b = st.body(body)?;
|
||||
match kernel::guard("axis of a face", || Ok(kernel::face_surface(&b.body, *index))).ok().flatten()? {
|
||||
FaceSurface::Cylinder { origin, axis, .. } => (origin, *axis),
|
||||
@@ -4720,7 +4720,7 @@ fn round_axis_ref(app: &SolveApp, sel: &Sel) -> Option<Value> {
|
||||
|
||||
fn is_straight_edge(s: &Session, sel: &Sel) -> bool {
|
||||
let Sel::Edge { body, index, .. } = sel else { return false };
|
||||
let Some(b) = s.world_state().body(body).cloned() else { return false };
|
||||
let Some(b) = crate::open_meshing::session_world(s).body(body).cloned() else { return false };
|
||||
let m = b.mesh();
|
||||
let Some(e) = m.edges.get(*index) else { return false };
|
||||
let (Some(a), Some(z)) = (e.first().copied(), e.last().copied()) else { return false };
|
||||
@@ -5027,7 +5027,7 @@ fn point_sel(s: &Session, g: &solidcraft_engine::doc::construct::GeoRef) -> Opti
|
||||
/// face where it is now.
|
||||
pub(crate) fn model_axis_sel(s: &Session, g: &solidcraft_engine::doc::construct::GeoRef) -> Option<Sel> {
|
||||
use solidcraft_engine::doc::construct::GeoRef;
|
||||
let at = g.picked_point(&s.world_state())?;
|
||||
let at = g.picked_point(&crate::open_meshing::session_world(s))?;
|
||||
match g {
|
||||
GeoRef::Edge { .. } => edge_sel(s, at),
|
||||
GeoRef::Face { .. } => face_sel(s, at),
|
||||
|
||||
@@ -543,7 +543,7 @@ fn top_dialog_buttons_apply_and_cancel_like_the_footer() {
|
||||
h.app.dialog = Some(representative(&h, "solid.revolve"));
|
||||
h.settle();
|
||||
assert_eq!(scenario::count_of("dialog:ok:enabled"), Some(1.0));
|
||||
let (design, bodies) = (h.app.session.doc.to_json(), h.app.session.world_state().bodies.len());
|
||||
let (design, bodies) = (h.app.session.doc.to_json(), h.app.session.world_state().unwrap().bodies.len());
|
||||
let step = match how {
|
||||
"click" => json!({"click":{"handle":"dialog:OK"}}),
|
||||
"enter" => json!({"key":"Enter"}),
|
||||
@@ -555,7 +555,7 @@ fn top_dialog_buttons_apply_and_cancel_like_the_footer() {
|
||||
if how == "escape" {
|
||||
assert_eq!(h.app.session.doc.to_json(), design, "Esc leaves the design as it was");
|
||||
} else {
|
||||
assert_eq!(h.app.session.world_state().bodies.len(), bodies + 1, "{how}: the revolve is made");
|
||||
assert_eq!(h.app.session.world_state().unwrap().bodies.len(), bodies + 1, "{how}: the revolve is made");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -634,11 +634,11 @@ fn top_dialog_buttons_and_the_view_corner_agree() {
|
||||
}
|
||||
for handle in ["dialog:ok", "view:confirm:ok"] {
|
||||
let mut h = open("solid.revolve");
|
||||
let bodies = h.app.session.world_state().bodies.len();
|
||||
let bodies = h.app.session.world_state().unwrap().bodies.len();
|
||||
h.step(&json!({"click":{"handle":handle}})).unwrap();
|
||||
h.settle();
|
||||
assert!(h.app.dialog.is_none(), "{handle}: the dialog closes");
|
||||
assert_eq!(h.app.session.world_state().bodies.len(), bodies + 1, "{handle}: the revolve is made");
|
||||
assert_eq!(h.app.session.world_state().unwrap().bodies.len(), bodies + 1, "{handle}: the revolve is made");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -906,7 +906,7 @@ fn extrude_builds_the_one_region_clicked_where_the_face_outline_cuts_a_profile()
|
||||
let sketch = h.app.run("sketch.create", json!({"plane": {"face": [0, 0, 10]}})).unwrap()["sketch"].as_u64().unwrap();
|
||||
h.app.run("sketch.circle.center", json!({"center": [10, 0], "radius": 6})).unwrap();
|
||||
h.app.run("sketch.finish", json!({})).unwrap();
|
||||
let state = h.app.session.world_state();
|
||||
let state = h.app.session.world_state().unwrap();
|
||||
let ss = state.sketch(sketch).unwrap();
|
||||
assert_eq!(ss.profiles.len(), 3, "the lens, the crescent past the face and the rest of the face");
|
||||
let index = ss.profiles.iter().position(|p| p.region.contains(solidcraft_engine::geom::Vec2::new(x, 0.0))).unwrap();
|
||||
@@ -924,7 +924,7 @@ fn extrude_builds_the_one_region_clicked_where_the_face_outline_cuts_a_profile()
|
||||
let before = h.app.run("inspect.measure", json!({})).unwrap()["total"]["volume_mm3"].as_f64().unwrap();
|
||||
let made = h.app.run(id, p.clone()).unwrap()["feature"].as_u64().unwrap();
|
||||
let Some(FeatureKind::Extrude { profiles, .. }) = h.app.session.doc.feature(made).map(|f| f.kind.clone()) else { panic!("an extrude") };
|
||||
let after = h.app.session.world_state();
|
||||
let after = h.app.session.world_state().unwrap();
|
||||
let used = solidcraft_engine::doc::profile_selection_indices(after.sketch(sketch).unwrap(), &profiles).unwrap();
|
||||
assert_eq!(used, vec![index], "the region clicked at x = {x}, and only it");
|
||||
let volume = h.app.run("inspect.measure", json!({})).unwrap()["total"]["volume_mm3"].as_f64().unwrap();
|
||||
|
||||
@@ -105,7 +105,7 @@ mod tests {
|
||||
}
|
||||
|
||||
fn lines(h: &Harness) -> Vec<(solidcraft_engine::geom::Vec2, solidcraft_engine::geom::Vec2)> {
|
||||
let st = h.app.session.world_state();
|
||||
let st = h.app.session.world_state().unwrap();
|
||||
let sk = &st.sketch(h.app.session.active_sketch.unwrap()).unwrap().sketch;
|
||||
(0..sk.curves.len())
|
||||
.filter_map(|i| match sk.shape(i) {
|
||||
|
||||
@@ -98,9 +98,12 @@ pub fn rows(ui: &mut egui::Ui, k: &mut Kind) -> bool {
|
||||
fn sel_point(app: &SolveApp, d: &Dialog, s: Option<&Sel>) -> Option<Vec3> {
|
||||
match s? {
|
||||
Sel::Edge { body, index, point } => {
|
||||
// As every pick reads the placed model: one that cannot be placed has no bodies here.
|
||||
let st = match d.editing {
|
||||
Some((id, _)) => std::sync::Arc::new(solidcraft_engine::doc::world_state(&app.session.doc, &app.session.model.state_before(id))),
|
||||
None => app.session.world_state(),
|
||||
Some((id, _)) => std::sync::Arc::new(
|
||||
solidcraft_engine::doc::world_state(&app.session.doc, &app.session.model.state_before(id)).unwrap_or_default(),
|
||||
),
|
||||
None => crate::open_meshing::session_world(&app.session),
|
||||
};
|
||||
Some(crate::dialogs::edge_circle_in(&st, body, *index).map_or(*point, |(c, ..)| c))
|
||||
}
|
||||
|
||||
@@ -376,7 +376,7 @@ fn transform_handles_and_fields_scale_move_and_turn_the_picks() {
|
||||
h.step(&json!({"key": "Enter"})).unwrap();
|
||||
h.settle();
|
||||
assert!(h.app.dialog.is_none(), "{:?}", h.app.dialog.as_ref().and_then(|d| d.error.clone()));
|
||||
let state = h.app.session.world_state();
|
||||
let state = h.app.session.world_state().unwrap();
|
||||
let sk = &state.sketch(h.app.session.active_sketch.unwrap()).unwrap().sketch;
|
||||
let (mut lo, mut hi) = ((f64::MAX, f64::MAX), (f64::MIN, f64::MIN));
|
||||
for p in (0..sk.curves.len()).flat_map(|i| sk.polyline(i)) {
|
||||
@@ -387,7 +387,7 @@ fn transform_handles_and_fields_scale_move_and_turn_the_picks() {
|
||||
assert!((hi.0 - 55.0).abs() < 1.5 && (hi.1 - 40.0).abs() < 1.5, "the far corner went to (55, 40): {hi:?}");
|
||||
h.app.run("edit.undo", json!({})).unwrap();
|
||||
h.settle();
|
||||
let state = h.app.session.world_state();
|
||||
let state = h.app.session.world_state().unwrap();
|
||||
let sk = &state.sketch(h.app.session.active_sketch.unwrap()).unwrap().sketch;
|
||||
let top = (0..sk.curves.len()).flat_map(|i| sk.polyline(i)).fold(f64::MIN, |m, p| m.max(p.x));
|
||||
assert!((top - 40.0).abs() < 1e-6, "one undo puts it back: {top}");
|
||||
|
||||
@@ -1160,7 +1160,7 @@ fn model_text(h: &Harness, param: &str) -> [f32; 2] {
|
||||
let sk = h
|
||||
.app
|
||||
.session
|
||||
.world_state()
|
||||
.world_state().unwrap()
|
||||
.sketch(h.app.session.doc.features.iter().find(|f| f.name.starts_with("Sketch")).unwrap().id)
|
||||
.unwrap()
|
||||
.sketch
|
||||
@@ -1172,7 +1172,7 @@ fn model_text(h: &Harness, param: &str) -> [f32; 2] {
|
||||
|
||||
/// The extruded plate's width and depth (x, y), rounded to 0.001 mm.
|
||||
fn plate_max(h: &Harness) -> [f64; 2] {
|
||||
let b = h.app.session.world_state().body("Plate").unwrap().mesh().bounds();
|
||||
let b = h.app.session.world_state().unwrap().body("Plate").unwrap().mesh().bounds();
|
||||
[((b.max.x - b.min.x) * 1000.0).round() / 1000.0, ((b.max.y - b.min.y) * 1000.0).round() / 1000.0]
|
||||
}
|
||||
|
||||
@@ -1238,7 +1238,7 @@ fn a_driven_shown_dimension_is_read_only() {
|
||||
h.app.run("sketch.dimension", json!({"entities":["l3"],"driven":true,"text_at":[10,14]})).unwrap();
|
||||
h.app.run("sketch.finish", json!({})).unwrap();
|
||||
h.settle();
|
||||
let id = h.app.session.world_state().sketch(sketch).unwrap().sketch.constraints.iter().find(|c| c.driven).unwrap().id.clone();
|
||||
let id = h.app.session.world_state().unwrap().sketch(sketch).unwrap().sketch.constraints.iter().find(|c| c.driven).unwrap().id.clone();
|
||||
let (_, _, p) = crate::dim_view::model_drawn().into_iter().find(|(_, d, _)| *d == id).unwrap();
|
||||
let doc = h.app.session.doc.to_json();
|
||||
h.step(&json!({"click":[p.x, p.y],"double":true})).unwrap();
|
||||
|
||||
@@ -641,7 +641,7 @@ fn a_second_placements_body_copies_but_is_not_cut() {
|
||||
select(&mut app, json!({"type": "body", "name": seen}));
|
||||
assert!(super::copy(&mut app));
|
||||
assert!(super::paste(&mut app, None), "{:?}", app.status);
|
||||
let world = app.session.world_state();
|
||||
let world = app.session.world_state().unwrap();
|
||||
let (there, copy) = (world.body(&seen).unwrap().mesh().bounds(), world.body("Cube (2)").expect("the copy").mesh().bounds());
|
||||
assert!(there.min.dist(copy.min) < 1e-6 && there.max.dist(copy.max) < 1e-6, "{there:?} vs {copy:?}");
|
||||
}
|
||||
|
||||
@@ -49,8 +49,8 @@ impl SolveApp {
|
||||
original.execute("timeline.roll_to", ¶ms).map_err(|error| error.to_string())?;
|
||||
let mut rolled = original.scratch();
|
||||
rolled.execute("timeline.roll_to", &serde_json::json!({"position": index})).map_err(|error| error.to_string())?;
|
||||
original.world_state().warm_display_at(quality, || true);
|
||||
rolled.world_state().warm_display_at(quality, || true);
|
||||
original.world_state().map_err(|error| error.to_string())?.warm_display_at(quality, || true);
|
||||
rolled.world_state().map_err(|error| error.to_string())?.warm_display_at(quality, || true);
|
||||
Ok((original, rolled))
|
||||
};
|
||||
let (tx, done) = std::sync::mpsc::channel();
|
||||
|
||||
@@ -173,7 +173,7 @@ fn a_solid_pattern_spacing_arrow_dragged_past_zero_goes_negative() {
|
||||
// Applied, the copies sit on the negative side, as a typed negative spacing puts them.
|
||||
h.step(&json!({"widget": "OK"})).unwrap();
|
||||
assert!(h.app.dialog.is_none(), "{:?}", h.app.status);
|
||||
let st = h.app.session.world_state();
|
||||
let st = h.app.session.world_state().unwrap();
|
||||
for (copy, k) in [("Body1 (2)", 1.0), ("Body1 (3)", 2.0)] {
|
||||
let x = st.body(copy).unwrap_or_else(|| panic!("no {copy}: {:?}", h.app.status)).mesh().bounds().center().x;
|
||||
assert!((x - (5.0 + k * v)).abs() < 1e-6, "{copy} centred at x = {x} for Spacing {spacing}");
|
||||
|
||||
@@ -27,7 +27,7 @@ fn shaft() -> Harness {
|
||||
/// The shaft's wall, top rim and top cap, as picks.
|
||||
fn picks(h: &Harness) -> (Sel, Sel, Sel) {
|
||||
use solidcraft_engine::kernel::{FaceSurface, face_surface};
|
||||
let st = h.app.session.world_state();
|
||||
let st = h.app.session.world_state().unwrap();
|
||||
let b = st.body("Shaft").unwrap();
|
||||
let face = |want: fn(&FaceSurface) -> bool, point: Vec3| {
|
||||
let index = (0..b.body.face_count()).find(|i| face_surface(&b.body, *i).is_some_and(|f| want(&f))).unwrap();
|
||||
|
||||
@@ -80,9 +80,9 @@ fn appearance_and_selection_rebuild_cost() {
|
||||
let rect = egui::Rect::from_min_size(egui::Pos2::ZERO, egui::vec2(800.0, 600.0));
|
||||
for n in [200usize, 1000, 5000] {
|
||||
let mut h = grid(n);
|
||||
h.app.session.world_state().warm_meshes(|| true);
|
||||
h.app.session.world_state().unwrap().warm_meshes(|| true);
|
||||
h.frames(2);
|
||||
let body = h.app.session.world_state().bodies[0].name.clone();
|
||||
let body = h.app.session.world_state().unwrap().bodies[0].name.clone();
|
||||
let cold = ms(|| drop(crate::viewport::build_scene(&h.app)));
|
||||
let again = ms(|| drop(crate::viewport::build_scene(&h.app)));
|
||||
let proj = crate::viewport::projection(&h.app, rect);
|
||||
@@ -123,11 +123,11 @@ fn large_body_scene_rebuild_cost() {
|
||||
.run("solid.torus", json!({"major": 100, "minor": 10, "center": [k as f64 * 220.0, 0.0, 0.0], "body_name": format!("T{k}")}))
|
||||
.unwrap();
|
||||
}
|
||||
h.app.session.world_state().warm_meshes(|| true);
|
||||
h.app.session.world_state().unwrap().warm_meshes(|| true);
|
||||
h.frames(2);
|
||||
let bodies = h.app.session.world_state().bodies.len();
|
||||
let triangles: usize = h.app.session.world_state().bodies.iter().map(|b| b.mesh().triangles.len()).sum();
|
||||
let body = h.app.session.world_state().bodies[0].name.clone();
|
||||
let bodies = h.app.session.world_state().unwrap().bodies.len();
|
||||
let triangles: usize = h.app.session.world_state().unwrap().bodies.iter().map(|b| b.mesh().triangles.len()).sum();
|
||||
let body = h.app.session.world_state().unwrap().bodies[0].name.clone();
|
||||
let cold = ms(|| drop(crate::viewport::build_scene(&h.app)));
|
||||
h.app.run("appearance.assign", json!({"bodies":[body.clone()],"color":"#336699"})).unwrap();
|
||||
let colour = ms(|| drop(crate::viewport::build_scene(&h.app)));
|
||||
@@ -164,7 +164,7 @@ fn assembly(unique: usize, placed: usize) -> Harness {
|
||||
json!({"components": ["Wheel"], "dir1": [1, 0, 0], "count1": p1, "spacing1": 100, "dir2": [0, 1, 0], "count2": p2, "spacing2": 100}),
|
||||
)
|
||||
.unwrap();
|
||||
h.app.session.world_state().warm_meshes(|| true);
|
||||
h.app.session.world_state().unwrap().warm_meshes(|| true);
|
||||
h.frames(3);
|
||||
h
|
||||
}
|
||||
@@ -178,7 +178,7 @@ fn assembly_scene_rebuild_cost() {
|
||||
let rect = egui::Rect::from_min_size(egui::Pos2::ZERO, egui::vec2(800.0, 600.0));
|
||||
for (unique, placed) in [(100, 100), (300, 500)] {
|
||||
let mut h = assembly(unique, placed);
|
||||
let st = h.app.session.world_state();
|
||||
let st = h.app.session.world_state().unwrap();
|
||||
let bodies = st.bodies.len();
|
||||
let triangles: usize = st.bodies.iter().map(|b| b.mesh().triangles.len()).sum();
|
||||
let part = st.bodies.iter().find(|b| b.component == 0).map(|b| b.name.clone()).unwrap();
|
||||
@@ -224,7 +224,7 @@ fn assembly_tree_hover_highlight_cost() {
|
||||
for (unique, placed) in [(100, 100), (300, 500)] {
|
||||
let mut h = assembly(unique, placed);
|
||||
let _ = crate::viewport::build_scene(&h.app);
|
||||
let st = h.app.session.world_state();
|
||||
let st = h.app.session.world_state().unwrap();
|
||||
let part = st.bodies.iter().find(|b| b.component == 0).map(|b| b.name.clone()).unwrap();
|
||||
let wheels: Vec<String> = st.bodies.iter().filter(|b| b.component != 0).map(|b| b.name.clone()).collect();
|
||||
let everything: Vec<String> = st.bodies.iter().map(|b| b.name.clone()).collect();
|
||||
@@ -297,7 +297,7 @@ pub(crate) fn view_frame(
|
||||
/// component, the whole design), each hover change building, uploading and drawing the highlight.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn hover_rows(h: &mut Harness, frames: &mut crate::gpu::OffscreenFrames, size: u32, model_key: u64, label: &str) -> String {
|
||||
let st = h.app.session.world_state();
|
||||
let st = h.app.session.world_state().unwrap();
|
||||
let part = st.bodies.iter().find(|b| b.component == 0).map(|b| b.name.clone()).unwrap();
|
||||
let wheels: Vec<String> = st.bodies.iter().filter(|b| b.component != 0).map(|b| b.name.clone()).collect();
|
||||
let everything: Vec<String> = st.bodies.iter().map(|b| b.name.clone()).collect();
|
||||
@@ -362,7 +362,7 @@ fn assembly_tree_hover_frame_cost() {
|
||||
h.app.run("component.activate", json!({"component": "Wheel"})).unwrap();
|
||||
println!("{}", hover_rows(&mut h, &mut frames, SIZE, base + 1_000_000, &format!("{label} mode=component_active")));
|
||||
h.app.run("component.activate", json!({"component": 0})).unwrap();
|
||||
let parts: Vec<String> = h.app.session.world_state().bodies.iter().filter(|b| b.component == 0).map(|b| b.name.clone()).collect();
|
||||
let parts: Vec<String> = h.app.session.world_state().unwrap().bodies.iter().filter(|b| b.component == 0).map(|b| b.name.clone()).collect();
|
||||
h.app.run("appearance.assign", json!({"bodies": parts, "color": "#88aacc", "opacity": 0.5})).unwrap();
|
||||
println!("{}", hover_rows(&mut h, &mut frames, SIZE, base + 2_000_000, &format!("{label} mode=glass_parts")));
|
||||
}
|
||||
@@ -448,11 +448,11 @@ fn step_file_cost() {
|
||||
};
|
||||
let import = ms(|| drop(h.app.run("file.insert_step", json!({"path": path.to_string_lossy()})).unwrap()));
|
||||
phase(&mut line, "import", import);
|
||||
let world = ms(|| drop(h.app.session.world_state()));
|
||||
let world = ms(|| drop(h.app.session.world_state().unwrap()));
|
||||
phase(&mut line, "world", world);
|
||||
let mesh = ms(|| h.app.session.world_state().warm_meshes(|| true));
|
||||
let mesh = ms(|| h.app.session.world_state().unwrap().warm_meshes(|| true));
|
||||
phase(&mut line, "mesh", mesh);
|
||||
let st = h.app.session.world_state();
|
||||
let st = h.app.session.world_state().unwrap();
|
||||
let bodies: Vec<String> = st.bodies.iter().map(|b| b.name.clone()).collect();
|
||||
let triangles: usize = st.bodies.iter().map(|b| b.mesh().triangles.len()).sum();
|
||||
let components = h.app.session.doc.components.len();
|
||||
|
||||
@@ -217,7 +217,7 @@ fn arc_tool_draws_an_exact_3d_arc_off_the_plane() {
|
||||
h.step(&json!({"click": {"world": p}})).unwrap();
|
||||
}
|
||||
h.step(&json!({"key": "Escape"})).unwrap();
|
||||
let state = h.app.session.world_state();
|
||||
let state = h.app.session.world_state().unwrap();
|
||||
let wires = &state.sketch(sketch).unwrap().sketch.wires;
|
||||
assert_eq!(wires.len(), 1, "three off-plane clicks draw one 3D arc");
|
||||
let w = &wires[0];
|
||||
@@ -245,7 +245,7 @@ fn arc3d_button_starts_its_own_tool() {
|
||||
for p in [[10.0, 0.0, 10.0], [10.0, 10.0, 10.0], [20.0, 10.0, 10.0]] {
|
||||
h.step(&json!({"click": {"world": p}})).unwrap();
|
||||
}
|
||||
let state = h.app.session.world_state();
|
||||
let state = h.app.session.world_state().unwrap();
|
||||
let wires = &state.sketch(sketch).unwrap().sketch.wires;
|
||||
assert_eq!(wires.len(), 1, "three clicks draw one exact 3D arc");
|
||||
let arc = wires[0].arc.expect("the drawn arc is stored exactly");
|
||||
|
||||
@@ -30,7 +30,7 @@ fn point(h: &Harness, id: &str) -> Vec2 {
|
||||
#[test]
|
||||
fn selecting_the_centre_and_an_edge_preselects_the_alignment() {
|
||||
let h = square_with_circle();
|
||||
let state = h.app.session.world_state();
|
||||
let state = h.app.session.world_state().unwrap();
|
||||
let sk = &state.sketch(h.app.session.active_sketch.unwrap()).unwrap().sketch;
|
||||
let edge = || SketchCurve { id: "l2".into(), sketch: None };
|
||||
let centre = || SketchPoint { id: "c1.center".into() };
|
||||
|
||||
@@ -397,7 +397,7 @@ fn a_midpoint_line_takes_a_typed_length_and_angle() {
|
||||
tab(&mut h, false);
|
||||
type_value(&mut h, "45 deg");
|
||||
h.step(&json!({"key": "Enter"})).unwrap();
|
||||
let state = h.app.session.world_state();
|
||||
let state = h.app.session.world_state().unwrap();
|
||||
let sk = &state.sketch(h.app.session.active_sketch.unwrap()).unwrap().sketch;
|
||||
assert_eq!(sk.curves.len(), 1, "{:?}", h.app.status);
|
||||
assert!(
|
||||
|
||||
@@ -76,7 +76,7 @@ fn pointer_on_sketch(plane: &Plane, proj: &Proj, at: Pos2) -> Option<Vec2> {
|
||||
/// The picked curves (as polylines on the sketch) and the box around them and the picked points:
|
||||
/// its middle and its size. None when nothing usable is picked or the box has no extent.
|
||||
fn picked_box(app: &SolveApp, d: &crate::dialogs::Dialog) -> Option<(Vec<Vec<Vec2>>, [f64; 2], [f64; 2])> {
|
||||
let state = app.session.world_state();
|
||||
let state = crate::open_meshing::session_world(&app.session);
|
||||
let sk = &state.sketch(app.session.active_sketch?)?.sketch;
|
||||
let mut lines: Vec<Vec<Vec2>> = Vec::new();
|
||||
let mut points: Vec<Vec2> = Vec::new();
|
||||
|
||||
@@ -268,7 +268,7 @@ pub fn on_click(app: &mut SolveApp, proj: &Proj, pos: Pos2, shift: bool) {
|
||||
let params = match first {
|
||||
None => Some(json!({"line": id})),
|
||||
Some(point) => {
|
||||
let state = app.session.world_state();
|
||||
let state = crate::open_meshing::session_world(&app.session);
|
||||
app.session.active_sketch.and_then(|sid| state.sketch(sid)).and_then(|ss| point_line_alignment(&ss.sketch, &point, id))
|
||||
}
|
||||
};
|
||||
|
||||
@@ -6175,7 +6175,7 @@ mod tests {
|
||||
let built: usize = planned.blocks.iter().map(|b| b.bytes.vertices.len()).sum();
|
||||
assert_eq!(built, 0, "and none is built on the CPU");
|
||||
// Hovered, a faded body drawn from its plan lights through the plan's triangles.
|
||||
let faded = app.session.world_state().bodies.iter().find(|b| b.name.starts_with("Faded")).map(|b| b.name.clone()).unwrap();
|
||||
let faded = app.session.world_state().unwrap().bodies.iter().find(|b| b.name.starts_with("Faded")).map(|b| b.name.clone()).unwrap();
|
||||
app.viewport.hover_bodies = vec![faded];
|
||||
assert_eq!(build_highlight(&app).hover_planned.len(), 1, "the faded body's plan is lit");
|
||||
app.viewport.hover_bodies.clear();
|
||||
@@ -6317,7 +6317,7 @@ mod tests {
|
||||
app.fit_view();
|
||||
const SIZE: u32 = 128;
|
||||
let rect = Rect::from_min_size(Pos2::ZERO, vec2(SIZE as f32, SIZE as f32));
|
||||
let body = app.session.world_state().bodies[0].clone();
|
||||
let body = app.session.world_state().unwrap().bodies[0].clone();
|
||||
let proj = projection(&app, rect);
|
||||
let e = proj.cam.eye().to_f32();
|
||||
let eye = [e[0], e[1], e[2], if proj.cam.fov > 0.0 { 1.0 } else { 0.0 }];
|
||||
@@ -8350,7 +8350,7 @@ mod tests {
|
||||
}
|
||||
|
||||
fn app_body(app: &SolveApp, name: &str) -> solidcraft_engine::doc::ModelBody {
|
||||
app.session.world_state().body(name).cloned().unwrap()
|
||||
app.session.world_state().unwrap().body(name).cloned().unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -161,7 +161,7 @@ fn selection_does_not_rebuild_the_model_scene() {
|
||||
let highlight = h.app.viewport.hl_key;
|
||||
// Clear the scene the first frame uploaded; an untouched model must not upload another.
|
||||
let _ = h.app.viewport.slot.lock().unwrap().take();
|
||||
let name = h.app.session.world_state().bodies[0].name.clone();
|
||||
let name = h.app.session.world_state().unwrap().bodies[0].name.clone();
|
||||
h.app.session.selection = vec![solidcraft_engine::Sel::Body { name }];
|
||||
h.frame();
|
||||
assert_eq!(h.app.viewport.key, key, "a selection does not change the model scene key");
|
||||
@@ -190,7 +190,7 @@ fn a_colour_edit_reuses_every_block_vertex() {
|
||||
app.ui.ground_shadow = false;
|
||||
app.set_origin_shown(false);
|
||||
app.run("solid.box", json!({"length": 20, "width": 15, "height": 10})).unwrap();
|
||||
let name = app.session.world_state().bodies[0].name.clone();
|
||||
let name = app.session.world_state().unwrap().bodies[0].name.clone();
|
||||
let first = build_scene(&app);
|
||||
let block = first.blocks.iter().find(|b| !b.bytes.vertices.is_empty()).expect("the box has a block");
|
||||
let (key, vertices) = (block.key, block.bytes.vertices.clone());
|
||||
@@ -207,7 +207,7 @@ fn an_opacity_class_change_keeps_vertices_and_rewrites_only_the_runs() {
|
||||
app.ui.ground_shadow = false;
|
||||
app.set_origin_shown(false);
|
||||
app.run("solid.box", json!({"length": 20, "width": 15, "height": 10})).unwrap();
|
||||
let name = app.session.world_state().bodies[0].name.clone();
|
||||
let name = app.session.world_state().unwrap().bodies[0].name.clone();
|
||||
let first = build_scene(&app);
|
||||
let block = first.blocks.iter().find(|b| !b.bytes.vertices.is_empty()).expect("the box has a block");
|
||||
let (key, bytes) = (block.key, block.bytes.clone());
|
||||
@@ -243,7 +243,7 @@ fn appearance_edits_upload_palette_rows_only() {
|
||||
Some(crate::gpu::GpuTarget { format: egui_wgpu::wgpu::TextureFormat::Rgba8Unorm, depth: None, samples: 1, tess: false, oit: false });
|
||||
app.run("solid.box", json!({"length": 20, "width": 15, "height": 10})).unwrap();
|
||||
app.fit_view();
|
||||
let name = app.session.world_state().bodies[0].name.clone();
|
||||
let name = app.session.world_state().unwrap().bodies[0].name.clone();
|
||||
let highlight: crate::gpu::SceneSlot = std::sync::Arc::new(std::sync::Mutex::new(None));
|
||||
let mut key = 0;
|
||||
let mut draw = |app: &SolveApp| {
|
||||
|
||||
Reference in New Issue
Block a user