Joints mate by default (Fusion's convention): origin B's z is turned against origin A's, so two picked faces meet face to face and B sits on A instead of inside it; Flip now means don't mate. File format 4 inverts older joints' flip so they keep their placement; as-built joints and rigid groups stay where they are
Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01AS5Tn3xieewUSg3uxrMVdL
This commit is contained in:
1 parent
d12c955220
commit
2c383460e9
5 files changed
+46
-14
No files matched your search
@@ -13,13 +13,15 @@
|
||||
//! - 3: appearances per body, face and component with names and opacity
|
||||
//! (`"appearances": {"bodies": {name: {name, color, opacity}}, "faces": […], "components": {…}}`);
|
||||
//! before, `"appearances"` mapped body names to bare `[r, g, b]` colours.
|
||||
//! - 4: joint origins mate by default (B's z against A's, as Fusion does); `flip` now means
|
||||
//! "don't mate". Older joints keep their placement: their `flip` is inverted.
|
||||
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::{DocError, Document, Result};
|
||||
|
||||
/// The format this build writes.
|
||||
pub const FORMAT: u32 = 3;
|
||||
pub const FORMAT: u32 = 4;
|
||||
|
||||
pub fn format_string(n: u32) -> String {
|
||||
format!("solvecraft/{n}")
|
||||
@@ -32,7 +34,20 @@ pub fn version_of(s: &str) -> Option<u32> {
|
||||
|
||||
/// JSON-level upgrades: entry `i` turns format `i + 1` into `i + 2`.
|
||||
type JsonStep = fn(&mut Value) -> Result<()>;
|
||||
const JSON_STEPS: [JsonStep; 2] = [|_| Ok(()), v2_to_v3];
|
||||
const JSON_STEPS: [JsonStep; 3] = [|_| Ok(()), v2_to_v3, v3_to_v4];
|
||||
|
||||
/// Joints placed with the old convention (aligned unless flipped) keep their placement.
|
||||
fn v3_to_v4(v: &mut Value) -> Result<()> {
|
||||
if let Some(Value::Array(js)) = v.get_mut("assembly").and_then(|a| a.get_mut("joints")) {
|
||||
for j in js.iter_mut() {
|
||||
if let Some(o) = j.as_object_mut() {
|
||||
let f = o.get("flip").and_then(Value::as_bool).unwrap_or(false);
|
||||
o.insert("flip".into(), Value::Bool(!f));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Body colours `{name: [r, g, b]}` become named, opaque body appearances.
|
||||
fn v2_to_v3(v: &mut Value) -> Result<()> {
|
||||
@@ -182,6 +197,12 @@ mod tests {
|
||||
old["appearances"] = serde_json::json!({"Body1": [200, 10, 20]});
|
||||
let d = read(&old.to_string()).unwrap();
|
||||
let l = d.appearances.bodies.get("Body1").unwrap();
|
||||
// Format 3 joints (aligned unless flipped) keep their placement.
|
||||
let mut j: Value = serde_json::from_str(&Document::new("A").to_json()).unwrap();
|
||||
j["format"] = Value::from("solvecraft/3");
|
||||
j["assembly"] = serde_json::json!({"joints": [{"id": 1, "name": "J", "kind": "rigid", "a": {"occurrence": 0, "snap": {"type": "point", "point": [0, 0, 0]}}, "b": {"occurrence": 0, "snap": {"type": "point", "point": [0, 0, 0]}}}]});
|
||||
let dj = read(&j.to_string()).unwrap();
|
||||
assert!(dj.assembly.joints[0].flip, "aligned before = not mated now");
|
||||
assert_eq!((l.color, l.opacity, l.name.as_str()), ([200, 10, 20], 1.0, "Custom"));
|
||||
let mut v: Value = serde_json::from_str(&Document::new("A").to_json()).unwrap();
|
||||
v["huge"] = Value::from(1e300);
|
||||
|
||||
@@ -122,7 +122,8 @@ pub struct Joint {
|
||||
/// Per value: (min, max) limits.
|
||||
#[serde(default)]
|
||||
pub limits: Vec<Option<(f64, f64)>>,
|
||||
/// Turn B over (its z opposite to A's).
|
||||
/// Don't mate: B's z along A's instead of against it (by default the origins mate, as two
|
||||
/// faces meeting).
|
||||
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
|
||||
pub flip: bool,
|
||||
/// Distance from A's frame to B's along z, and a turn about it (radians).
|
||||
@@ -404,7 +405,8 @@ pub fn solve(doc: &Document, st: &ModelState) -> Solution {
|
||||
let flip_m = |j: &Joint| {
|
||||
let mut m = rot_z(j.angle);
|
||||
m[3][2] = j.offset;
|
||||
if j.flip { mat_mul(&m, &[[1.0, 0.0, 0.0, 0.0], [0.0, -1.0, 0.0, 0.0], [0.0, 0.0, -1.0, 0.0], [0.0, 0.0, 0.0, 1.0]]) } else { m }
|
||||
// Origins mate (B's z against A's, as faces meet) unless flipped.
|
||||
if !j.flip { mat_mul(&m, &[[1.0, 0.0, 0.0, 0.0], [0.0, -1.0, 0.0, 0.0], [0.0, 0.0, -1.0, 0.0], [0.0, 0.0, 0.0, 1.0]]) } else { m }
|
||||
};
|
||||
// B's world transform from A's (or A's from B's, going the other way).
|
||||
let through = |j: &Joint, from_a: bool, t: &Mat| -> Option<Mat> {
|
||||
|
||||
Reference in new issue
Block a user