Agent-built designs: stop redoing whole-design work on every command (#1148)

A design built over MCP is many small commands, and each one did work over the
whole design, so building or showing a 1,300-body design took minutes:

- moved components were transformed and meshed again after every change; world_state
  now reuses each placed body whose definition and placement are unchanged
- next_d_name searched the design once per candidate number
- Document::eval evaluated every parameter for every expression; now only when the
  expression names one
- param_rows' used_by was quadratic; the desktop no longer gathers completions on every
  change while no text box has focus

solidcraft-cli bench-agent builds a generated ~1,300-body bicycle command by command
and times it (241 s -> 16 s here). Eval output is unchanged apart from timings.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
bherbruckandClaude Opus 5.5 authored and GitHub committed 2026-10-14 13:48:39 -04:00
1 parent 37547f8eba
commit 6de8f6db95
11 files changed
+439 -21

No files matched your search

@@ -175,3 +175,41 @@ fn adopted_scratch_keeps_its_warm_geometry_and_history_stages_invalidate_it() {
run(&mut session, "timeline.suppress", json!({"feature":feature,"suppressed":false}));
assert!(session.world_state().body("Draft").is_some());
}
/// A moved component's bodies keep their placed geometry and display mesh while the design
/// changes elsewhere: each new body used to transform and mesh every body of every moved
/// component again. A body that changes, or a placement that moves, is placed afresh.
#[test]
fn moved_component_bodies_are_not_placed_again_when_something_else_changes() {
let mut session = Session::default();
let created = run(&mut session, "component.create", json!({"name":"Wheel"}));
let occurrence = created["occurrence"].as_u64().unwrap();
run(&mut session, "solid.box", json!({"length":60,"width":60,"height":5,"body_name":"Rim"}));
run(&mut session, "component.activate", json!({"component":"root"}));
run(&mut session, "occurrence.move", json!({"occurrence":occurrence,"translate":[100,0,0]}));
let placed = session.world_state();
let rim = placed.bodies.iter().find(|b| b.source_name.as_deref() == Some("Rim")).unwrap().clone();
let mesh = rim.mesh();
assert!(mesh.bounds().min.dist(Vec3::new(100., 0., 0.)) < 1e-6);
// A body added elsewhere: the rim is the same placed body, already meshed.
run(&mut session, "solid.box", json!({"length":5,"width":6,"height":7,"body_name":"Frame"}));
let after = session.world_state();
let again = after.body(&rim.name).unwrap();
assert!(Arc::ptr_eq(&mesh, &again.mesh()), "an unchanged placed body keeps its mesh");
assert!(again.body.same(&rim.body));
// The placement moves: placed and meshed again where it now is.
run(&mut session, "occurrence.move", json!({"occurrence":occurrence,"translate":[0,50,0]}));
let moved = session.world_state();
let moved_rim = moved.bodies.iter().find(|b| b.source_name.as_deref() == Some("Rim")).unwrap();
assert!(!Arc::ptr_eq(&mesh, &moved_rim.mesh()));
assert!(moved_rim.mesh().bounds().min.dist(Vec3::new(100., 50., 0.)) < 1e-6);
// The body itself changes: placed again too, and the old snapshot is untouched.
run(&mut session, "body.rename", json!({"body":"Frame","name":"Frame tube"}));
let source = session.model.state().body("Rim").unwrap().clone();
let world = session.world_state();
let kept = world.bodies.iter().find(|b| b.source_name.as_deref() == Some("Rim")).unwrap();
assert!(Arc::ptr_eq(&moved_rim.mesh(), &kept.mesh()));
assert_eq!(kept.source_transform, moved_rim.source_transform);
assert!(source.mesh().bounds().min.dist(Vec3::ZERO) < 1e-6);
assert!(mesh.bounds().min.dist(Vec3::new(100., 0., 0.)) < 1e-6);
}