Extrude: a symmetric distance is the total thickness (#1260)
A symmetric solid extrude of 10 mm is now 10 mm thick, 5 mm each side of the sketch plane, and switching from One Side to Symmetric keeps the thickness entered. Under format 21 the extent records its convention; designs saved without it keep the per-side reading, so their geometry and parameters don't change. Fixes #1236. Co-authored-by: Karl-VonRichter <[email protected]>
This commit is contained in:
1 parent
faeeff1c64
commit
1ec33fcdc7
19 files changed
+323
-18
No files matched your search
@@ -134,7 +134,7 @@ pub enum Direction {
|
||||
#[default]
|
||||
Positive,
|
||||
Negative,
|
||||
/// Both sides; `distance` per side.
|
||||
/// Both sides; the extent convention determines whether `distance` is total or per side.
|
||||
Symmetric,
|
||||
}
|
||||
|
||||
@@ -186,12 +186,33 @@ pub enum RevolveConvention {
|
||||
SketchNormal,
|
||||
}
|
||||
|
||||
/// Symmetric distance semantics. Missing markers preserve existing designs' thickness.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ExtrudeConvention {
|
||||
#[default]
|
||||
LegacyPerSide,
|
||||
TotalDistance,
|
||||
}
|
||||
|
||||
impl ExtrudeConvention {
|
||||
/// Distance from the mid-plane to either cap for a distance-limited extent.
|
||||
pub fn symmetric_half_distance(self, distance: f64) -> f64 {
|
||||
match self {
|
||||
Self::LegacyPerSide => distance,
|
||||
Self::TotalDistance => distance * 0.5,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Extent of an extrude.
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, solidcraft_derive::Walk)]
|
||||
pub struct Extent {
|
||||
#[walk(length = "Distance")]
|
||||
pub distance: String,
|
||||
#[serde(default)]
|
||||
pub convention: ExtrudeConvention,
|
||||
#[serde(default)]
|
||||
pub direction: Direction,
|
||||
/// Optional second side distance (two-sided extrude), along the negative normal.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
|
||||
@@ -3291,7 +3291,11 @@ pub(crate) fn extent_sides(vals: &BTreeMap<String, Value>, extent: &crate::Exten
|
||||
(Some(d2), _) => (-val(vals, d2, Kind::Length)?, d),
|
||||
(None, crate::Direction::Positive) => (0.0, d),
|
||||
(None, crate::Direction::Negative) => (-d, 0.0),
|
||||
(None, crate::Direction::Symmetric) => (-d, d),
|
||||
(None, crate::Direction::Symmetric) => {
|
||||
// Through All is a reach computed from target bodies, not an entered total.
|
||||
let half = if extent.through_all { d } else { extent.convention.symmetric_half_distance(d) };
|
||||
(-half, half)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -179,6 +179,11 @@
|
||||
//! neighbouring edges as they are (`neighbours: false`). An Extend without them builds the
|
||||
//! planar extension it always did and saves as before; 0.11.0 asks for an update instead of
|
||||
//! dropping them and extending a plane by the distance.
|
||||
//! Under the same version, an extrude's extent records how a symmetric `distance` is read
|
||||
//! (`convention`: `total_distance`, split evenly around the start plane, which new solid
|
||||
//! extrudes save; or `legacy_per_side`). An extent without it keeps the per-side reading
|
||||
//! designs always had, so their thickness and parameters don't change; 0.11.0 asks for an
|
||||
//! update instead of doubling a total-distance extrude.
|
||||
|
||||
use serde_json::Value;
|
||||
|
||||
@@ -1131,6 +1136,7 @@ mod tests {
|
||||
sketch,
|
||||
profiles: ProfileSel::All,
|
||||
extent: Extent {
|
||||
convention: crate::ExtrudeConvention::LegacyPerSide,
|
||||
distance: "10".into(),
|
||||
direction: Direction::Negative,
|
||||
distance2: None,
|
||||
|
||||
@@ -427,6 +427,7 @@ mod tests {
|
||||
sketch: if missing_sketch { 999_999 } else { id },
|
||||
profiles: ProfileSel::OffsetBoundaries { associations: vec!["deleted-o1".into()] },
|
||||
extent: crate::Extent {
|
||||
convention: crate::ExtrudeConvention::LegacyPerSide,
|
||||
distance: "2".into(),
|
||||
direction: crate::Direction::Positive,
|
||||
distance2: None,
|
||||
|
||||
@@ -312,6 +312,7 @@ mod tests {
|
||||
sketch: feature,
|
||||
profiles: selection.clone(),
|
||||
extent: crate::Extent {
|
||||
convention: crate::ExtrudeConvention::LegacyPerSide,
|
||||
distance: "2".into(),
|
||||
direction: crate::Direction::Positive,
|
||||
distance2: None,
|
||||
@@ -438,6 +439,7 @@ mod tests {
|
||||
sketch: feature,
|
||||
profiles: selection.clone(),
|
||||
extent: crate::Extent {
|
||||
convention: crate::ExtrudeConvention::LegacyPerSide,
|
||||
distance: "2".into(),
|
||||
direction: crate::Direction::Positive,
|
||||
distance2: None,
|
||||
|
||||
@@ -33,6 +33,7 @@ fn plate_doc() -> (Document, u64) {
|
||||
sketch: s,
|
||||
profiles: ProfileSel::All,
|
||||
extent: Extent {
|
||||
convention: crate::ExtrudeConvention::LegacyPerSide,
|
||||
distance: "20".into(),
|
||||
direction: Direction::Positive,
|
||||
distance2: None,
|
||||
@@ -125,6 +126,7 @@ fn box_fillet_cut_timeline() {
|
||||
sketch: s2,
|
||||
profiles: ProfileSel::Points { points: vec![Vec2::new(20.0, 15.0)] },
|
||||
extent: Extent {
|
||||
convention: crate::ExtrudeConvention::LegacyPerSide,
|
||||
distance: "20".into(),
|
||||
direction: Direction::Positive,
|
||||
distance2: None,
|
||||
|
||||
@@ -53,6 +53,7 @@ impl Walk for crate::document::ThinSide {}
|
||||
impl Walk for crate::document::ExtendMode {}
|
||||
impl Walk for crate::document::RuledMode {}
|
||||
impl Walk for crate::document::RuledCorners {}
|
||||
impl Walk for crate::document::ExtrudeConvention {}
|
||||
|
||||
/// Containers walk what they hold, in order.
|
||||
macro_rules! walk_each {
|
||||
|
||||
Reference in new issue
Block a user