diff --git a/crates/engine/src/cmd/inspect.rs b/crates/engine/src/cmd/inspect.rs index 6d1b0d3b..9c2bce00 100644 --- a/crates/engine/src/cmd/inspect.rs +++ b/crates/engine/src/cmd/inspect.rs @@ -303,6 +303,14 @@ fn appearance(s: &mut Session, p: &Value) -> Result { use solvecraft_doc::appearance::{FaceLook, Look, library}; let cmd = "AppearanceCommand"; let look: Option = 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()) { diff --git a/crates/ui-egui/src/dialogs_appearance.rs b/crates/ui-egui/src/dialogs_appearance.rs index 1ec7726c..010450e1 100644 --- a/crates/ui-egui/src/dialogs_appearance.rs +++ b/crates/ui-egui/src/dialogs_appearance.rs @@ -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)]); } diff --git a/crates/ui-egui/src/dialogs_assembly.rs b/crates/ui-egui/src/dialogs_assembly.rs index eb54fa29..403ebedc 100644 --- a/crates/ui-egui/src/dialogs_assembly.rs +++ b/crates/ui-egui/src/dialogs_assembly.rs @@ -196,7 +196,7 @@ pub fn edit_joint(app: &SolveApp, id: u64) -> Option { 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 = 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 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, .. } => { diff --git a/crates/ui-egui/src/dialogs_assembly_tests.rs b/crates/ui-egui/src/dialogs_assembly_tests.rs index a9a76b61..3c798905 100644 --- a/crates/ui-egui/src/dialogs_assembly_tests.rs +++ b/crates/ui-egui/src/dialogs_assembly_tests.rs @@ -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(); }