Dialogs for contact sets, motion studies, exploded views and configurations; Drive scenario

Wired to solvecraft-params' contact, motion, explode and configuration commands (verified under Xvfb: a study keyed 0 to 60 deg plays, an explosion previews and OK keeps it, Activate switches configurations, Check collisions lists overlaps):
- Contact Set: components to a new set; the sets with suppress and delete; contact on/off;
  Check collisions (contact.check).
- Motion Study: tracks of keyed joint values on a strip under the steps, keys added and edited
  at the playhead, a step slider with Play; the pose at the step is shown (motion.play on a
  scratch copy, as pending moves) and put back when the dialog closes.
- Exploded View: a scale slider showing the explosion live; OK creates and shows the view.
- Configurations: the table (rows by columns of parameters and suppressed features), cells
  edited in place, Activate per row, rows and columns added and removed.
The ASSEMBLE panel lists the motion and contact commands once they exist. Tests for the pose,
table and study mappings.

Scenario s13: Drive Joints takes typed input without a click.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AS5Tn3xieewUSg3uxrMVdL
This commit is contained in:
bherbruckandClaude Opus 5.5 committed 2026-10-08 12:02:55 +00:00
1 parent 46f3ff713d
commit 5096bf2442
6 files changed
+982 -4

No files matched your search

+20 -3
View File
@@ -206,6 +206,8 @@ pub enum Kind {
Plastic(crate::dialogs_plastic::Pl),
/// Appearance (`dialogs_appearance`).
Appearance(crate::dialogs_appearance::Ap),
/// Contact sets, motion studies, exploded views, configurations (`dialogs_motion`).
Motion(crate::dialogs_motion::Mo),
}
/// The rest of the Hole dialog: placement, extents, tap type and drill point.
@@ -405,7 +407,8 @@ impl Dialog {
let (kind, inputs) = crate::dialogs_assembly::start(app, id)
.or_else(|| crate::dialogs_sheet::start(app, id))
.or_else(|| crate::dialogs_plastic::start(app, id))
.or_else(|| crate::dialogs_appearance::start(app, id))?;
.or_else(|| crate::dialogs_appearance::start(app, id))
.or_else(|| crate::dialogs_motion::start(app, id))?;
Dialog::new(kind, inputs)
}
};
@@ -756,6 +759,7 @@ fn title(k: &Kind) -> &'static str {
Kind::Sheet(k) => k.title(),
Kind::Plastic(k) => k.title(),
Kind::Appearance(_) => "APPEARANCE",
Kind::Motion(k) => k.title(),
}
}
@@ -1037,9 +1041,10 @@ pub fn show(app: &mut SolveApp, ctx: &egui::Context) {
let anchor = egui::pos2(vp.right(), vp.top() + crate::viewport::VIEW_CUBE_CLEARANCE);
let mut keep = true;
let mut ok = false;
let mut applied = false;
let mut cancel = false;
let mut enter = false;
let wide = matches!(d.kind, Kind::Params { .. });
let wide = matches!(d.kind, Kind::Params { .. }) || matches!(&d.kind, Kind::Motion(k) if k.wide());
let heading = if d.editing.is_some() { format!("EDIT {}", title(&d.kind)) } else { title(&d.kind).to_string() };
let frame = egui::Frame::window(&ctx.global_style())
.fill(t.dialog_bg)
@@ -1404,6 +1409,7 @@ pub fn show(app: &mut SolveApp, ctx: &egui::Context) {
Kind::Sheet(k) => enter |= crate::dialogs_sheet::rows(app, ui, k, &mut d.inputs),
Kind::Plastic(k) => enter |= crate::dialogs_plastic::rows(app, ui, k, &d.inputs),
Kind::Appearance(k) => enter |= crate::dialogs_appearance::rows(app, ui, k, &mut d.inputs),
Kind::Motion(k) => enter |= crate::dialogs_motion::rows(app, ui, k, &mut d.inputs),
Kind::ConfirmDelete { with, fail, .. } => {
if !with.is_empty() {
row_label(ui, "Also deletes");
@@ -1476,10 +1482,16 @@ pub fn show(app: &mut SolveApp, ctx: &egui::Context) {
}
if ok {
match run_dialog(app, &d) {
Ok(()) => cancel = true,
Ok(()) => {
cancel = true;
applied = true;
}
Err(e) => d.error = Some(e),
}
}
if !(keep && !cancel) {
crate::dialogs_motion::closed(app, &mut d, applied);
}
if keep && !cancel {
app.dialog = Some(d);
} else if let Some((_, marker)) = d.editing {
@@ -1490,6 +1502,10 @@ pub fn show(app: &mut SolveApp, ctx: &egui::Context) {
/// Close the dialog without applying it (Esc, Cancel). An edit puts the timeline marker back.
pub fn cancel(app: &mut SolveApp) {
if let Some(mut d) = app.dialog.take() {
crate::dialogs_motion::closed(app, &mut d, false);
app.dialog = Some(d);
}
if let Some(d) = app.dialog.take()
&& let Some((_, marker)) = d.editing
{
@@ -2028,6 +2044,7 @@ fn dialog_commands(app: &SolveApp, d: &Dialog) -> Result<Vec<(String, Value)>, S
Kind::Sheet(k) => return crate::dialogs_sheet::commands(app, k, &d.inputs, &d.extra),
Kind::Plastic(k) => return crate::dialogs_plastic::commands(app, k, &d.inputs, &d.extra),
Kind::Appearance(k) => return Ok(crate::dialogs_appearance::commands(k, &d.inputs)),
Kind::Motion(k) => return crate::dialogs_motion::commands(app, k, &d.inputs),
};
let mut params = params;
if let Value::Object(m) = &mut params {