Joint dialog after the engine's mating default: the dialog's Flip is the command's flip (no double flip with 4c3a5d9); Appearance dialog on the new appearance model; AppearanceCommand takes a colour with any name (the dialog's palette names)

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AS5Tn3xieewUSg3uxrMVdL
This commit is contained in:
bherbruckandClaude Opus 5.5 committed 2026-10-08 11:37:33 +00:00
1 parent 2c383460e9
commit 6653db806e
4 files changed
+19 -11

No files matched your search

+8
View File
@@ -303,6 +303,14 @@ fn appearance(s: &mut Session, p: &Value) -> Result<Value> {
use solvecraft_doc::appearance::{FaceLook, Look, library};
let cmd = "AppearanceCommand";
let look: Option<Look> = match (str_(p, "appearance"), p.get("color")) {
// A colour with a name: that colour, called that (a name not in the library is fine).
(Some(n), Some(v)) if !v.is_null() => {
let c = color_param(v).ok_or_else(|| bad(cmd, "`color` must be \"#rrggbb\" or [r, g, b] with values 0–255"))?;
let o = p.get("opacity").and_then(Value::as_f64).filter(|x| x.is_finite()).unwrap_or(1.0);
let mut l = Look::custom(c, o);
l.name = n.chars().take(128).collect();
Some(l)
}
(Some(n), _) => {
let mut l = library(n).ok_or_else(|| bad(cmd, format!("no appearance `{n}` in the library (appearance.library)")))?;
if let Some(o) = p.get("opacity").and_then(Value::as_f64).filter(|x| x.is_finite()) {
+4 -4
View File
@@ -177,10 +177,10 @@ fn in_design(app: &mut SolveApp) -> Vec<([u8; 3], usize)> {
.collect();
}
let mut out: Vec<([u8; 3], usize)> = Vec::new();
for c in app.session.doc.appearances.values() {
match out.iter_mut().find(|(x, _)| x == c) {
for c in app.session.doc.appearances.bodies.values().map(|l| l.color) {
match out.iter_mut().find(|(x, _)| *x == c) {
Some(e) => e.1 += 1,
None => out.push((*c, 1)),
None => out.push((c, 1)),
}
}
out
@@ -391,7 +391,7 @@ mod tests {
for (id, p) in c {
a.run(&id, p).unwrap();
}
assert_eq!(a.session.doc.appearances.get("Body2"), Some(&[196, 40, 36]));
assert_eq!(a.session.doc.appearances.bodies.get("Body2").map(|l| l.color), Some([196, 40, 36]));
assert_eq!(in_design(&mut a), vec![([196, 40, 36], 2)]);
}
+6 -6
View File
@@ -196,7 +196,7 @@ pub fn edit_joint(app: &SolveApp, id: u64) -> Option<Dialog> {
let mut f = JointForm::new(false);
f.editing = Some(id);
f.kind = TYPES.iter().position(|t| JointKind::parse(t) == Some(j.kind)).unwrap_or(0);
f.flip = !j.flip;
f.flip = j.flip;
f.angle = num(j.angle.to_degrees(), "deg");
f.offset[2] = num(j.offset, "mm");
f.fit_limits();
@@ -657,7 +657,7 @@ pub fn commands(app: &SolveApp, k: &Asm, inputs: &[SelInput]) -> Result<Vec<(Str
let limits: Vec<Value> =
f.limits.iter().take(jk.dofs().len()).map(|(on, lo, hi)| if *on { json!([lo, hi]) } else { Value::Null }).collect();
if let Some(id) = f.editing {
let mut out = cmd("joint.edit", json!({"joint": id, "type": kind, "flip": !f.flip, "offset": f.offset[2], "angle": f.angle}));
let mut out = cmd("joint.edit", json!({"joint": id, "type": kind, "flip": f.flip, "offset": f.offset[2], "angle": f.angle}));
for (i, l) in limits.iter().enumerate() {
let p = match l.as_array() {
Some(a) => json!({"joint": id, "index": i, "min": a.first(), "max": a.get(1)}),
@@ -684,10 +684,10 @@ pub fn commands(app: &SolveApp, k: &Asm, inputs: &[SelInput]) -> Result<Vec<(Str
}
let (x, y) = (eval(&f.offset[0], ValueKind::Length)?, eval(&f.offset[1], ValueKind::Length)?);
let a_param = if x.abs() > 1e-12 || y.abs() > 1e-12 { shifted(&a, x, y) } else { a.param };
// Picked origins mate as in Fusion: B's z opposite A's (faces meet face to face, a pin
// goes into its hole); the dialog's Flip turns B the other way. The command's `flip`
// turns B over from frames that coincide.
let p = json!({"type": kind, "a": a_param, "b": b.param, "flip": !f.flip, "offset": f.offset[2], "angle": f.angle, "limits": limits});
// Picked origins mate as in Fusion (the engine's default: B's z opposite A's, faces
// meet face to face, a pin goes into its hole); the dialog's Flip is the command's
// `flip`, which turns B the other way.
let p = json!({"type": kind, "a": a_param, "b": b.param, "flip": f.flip, "offset": f.offset[2], "angle": f.angle, "limits": limits});
cmd("JointAssembleCmdNew", p)
}
Asm::JointOrigin { name, .. } => {
+1 -1
View File
@@ -217,7 +217,7 @@ fn face_snaps_mate_face_to_face() {
d.pick(&app.session, face_sel(&app.session, Vec3::new(10.0, 10.0, 10.0)).unwrap());
d.pick(&app.session, face_sel(&app.session, Vec3::new(40.0, 10.0, 10.0)).unwrap());
let c = apply_commands(&app, &d).unwrap();
assert_eq!(c[0].1["flip"], true, "the mate turns B over");
assert_eq!(c[0].1["flip"], false, "mating is the engine's default; no flip");
for (id, p) in c {
app.run(&id, p).unwrap();
}