Sweep: orientation, twist and end scale (#1269)

Part of #771 and #772. solid.sweep and surface.sweep take orientation (perpendicular, parallel or a direction), twist (up to 4 turns) and end scale (0.01 to 100), applied about the profile's normal through the path's start; default options run the old sweep, so existing sweeps are unchanged. New fields are optional, left out when unset and noted under format 21; twist and scale are model parameters. Both sweep dialogs get Orientation, Twist and End Scale rows; labels in every catalog. Checked on main + this branch: cargo check --workspace, kernel/engine/UI sweep tests, the doc suite and the robustness goldens.
This commit is contained in:
Irakli Kourtis authored and GitHub committed 2026-10-15 06:24:31 -04:00
1 parent 0872eb959a
commit 5fa8e7d60a
30 files changed
+1120 -71

No files matched your search

+28
View File
@@ -191,6 +191,10 @@
//! A bend point can also lie on a bend modelled with its radius (a cylinder between two flat
//! faces tangent to it), which then is one bend at the two faces' virtual sharp with the
//! rule's radius; 0.11.0 found no edge there or refused the curved face.
//! Also under 21, a Sweep and a Surface Sweep can carry `orientation` (`parallel`, or
//! `{direction: [x, y, z]}`; absent: perpendicular), `twist` (an angle expression) and `scale`
//! (the end scale, an expression). A sweep without them saves as before; 0.11.x refuses
//! format 21, so no released build sweeps a twisted or scaled profile straight.
use serde_json::Value;
@@ -1763,4 +1767,28 @@ mod tests {
let json = edited(&d, |v| v["components"][1]["id"] = u64::MAX.into());
assert!(read(&json).unwrap_err().to_string().contains("damaged design"));
}
/// A sweep saved before its options existed loads as a plain sweep and saves as it was; the
/// options round-trip.
#[test]
fn sweep_options_are_optional_and_round_trip() {
use crate::{FeatureKind, SweepOrientation};
let old =
serde_json::json!({"type": "sweep", "sketch": 1, "profiles": {"type": "all"}, "path_sketch": 2, "path": ["l1"], "operation": "new_body"});
let k: FeatureKind = serde_json::from_value(old.clone()).unwrap();
let FeatureKind::Sweep { orientation, twist, scale, .. } = &k else { panic!("{k:?}") };
assert!(orientation.is_perpendicular() && twist.is_none() && scale.is_none());
assert_eq!(serde_json::to_value(&k).unwrap(), old);
let old = serde_json::json!({"type": "surface_sweep", "sketch": 1, "path_sketch": 2, "path": ["l1"]});
let k: FeatureKind = serde_json::from_value(old.clone()).unwrap();
assert_eq!(serde_json::to_value(&k).unwrap(), old);
let new = serde_json::json!({"type": "surface_sweep", "sketch": 1, "path_sketch": 2, "path": ["l1"],
"orientation": {"direction": [0.0, 1.0, 0.0]}, "twist": "90 deg", "scale": "0.5"});
let k: FeatureKind = serde_json::from_value(new.clone()).unwrap();
let FeatureKind::SurfaceSweep { orientation, .. } = &k else { panic!("{k:?}") };
assert_eq!(*orientation, SweepOrientation::Direction(solidcraft_geom::Vec3::Y));
assert_eq!(serde_json::to_value(&k).unwrap(), new);
let parallel: SweepOrientation = serde_json::from_value(serde_json::json!("parallel")).unwrap();
assert_eq!(parallel, SweepOrientation::Parallel);
}
}