Sketch on an origin plane faces the side you clicked (#1587)

Fixes #1586.
This commit is contained in:
bherbruck authored and GitHub committed 2026-10-16 03:00:24 -04:00
1 parent 3707a67cee
commit 26bf1af93b
23 files changed
+351 -27

No files matched your search

+1 -1
View File
@@ -145,7 +145,7 @@ fn map_plane_ref(m: &Mat, p: &mut PlaneRef) {
*plane = map_plane(m, plane);
*at = apply_point(m, *at);
}
PlaneRef::Offset { base, .. } => map_plane_ref(m, base),
PlaneRef::Offset { base, .. } | PlaneRef::Flipped { base, .. } => map_plane_ref(m, base),
PlaneRef::AtAngle { base, axis_origin, axis_dir, .. } => {
map_plane_ref(m, base);
*axis_origin = apply_point(m, *axis_origin);
+4 -2
View File
@@ -113,7 +113,9 @@ fn remap_plane(
return Err(crate::DocError::Invalid("clone plane reference depth exceeded".into()));
}
match p {
PlaneRef::Offset { base, .. } | PlaneRef::AtAngle { base, .. } => remap_plane(base, ids, names, bodies, depth + 1)?,
PlaneRef::Offset { base, .. } | PlaneRef::AtAngle { base, .. } | PlaneRef::Flipped { base, .. } => {
remap_plane(base, ids, names, bodies, depth + 1)?
}
PlaneRef::Construction { name } => mapped_name(&mut name.0, names),
PlaneRef::Face { name, anchor, .. } => {
if let Some(name) = name {
@@ -283,7 +285,7 @@ fn checked_plane(p: &PlaneRef, depth: usize) -> crate::Result<()> {
return Err(crate::DocError::Invalid("copy reference nesting exceeds 64".into()));
}
match p {
PlaneRef::Offset { base, .. } | PlaneRef::AtAngle { base, .. } => checked_plane(base, depth + 1)?,
PlaneRef::Offset { base, .. } | PlaneRef::AtAngle { base, .. } | PlaneRef::Flipped { base, .. } => checked_plane(base, depth + 1)?,
PlaneRef::Midplane { a, b } | PlaneRef::TwoEdges { a, b } => {
checked_geo(a, depth + 1)?;
checked_geo(b, depth + 1)?;
+1 -1
View File
@@ -633,7 +633,7 @@ impl PlaneRef {
PlaneRef::ThreePoints { a, b, c } => vec![a.as_mut(), b.as_mut(), c.as_mut()],
PlaneRef::Tangent { face, .. } => vec![face.as_mut()],
PlaneRef::AlongPath { path, .. } => vec![path.as_mut()],
PlaneRef::Offset { base, .. } | PlaneRef::AtAngle { base, .. } => base.refs_mut(),
PlaneRef::Offset { base, .. } | PlaneRef::AtAngle { base, .. } | PlaneRef::Flipped { base, .. } => base.refs_mut(),
_ => Vec::new(),
}
}
+9
View File
@@ -58,6 +58,14 @@ pub enum PlaneRef {
AtAngle { base: Box<PlaneRef>, axis_origin: Vec3, axis_dir: Vec3, angle: Expr },
/// A construction plane feature (by name).
Construction { name: FeatureName },
/// `base` seen from its other side: the normal reversed by a half turn about the base's
/// own x axis (y reversed), or about its y axis (x reversed) when `about_y`. A sketch
/// started from behind a plane faces the viewer this way.
Flipped {
base: Box<PlaneRef>,
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
about_y: bool,
},
/// Halfway between two planes (or planar faces); bisecting them when they meet at an angle.
Midplane { a: Box<GeoRef>, b: Box<GeoRef> },
/// Through two straight edges (lines) that lie in one plane.
@@ -2884,6 +2892,7 @@ impl Document {
let o = *axis_origin + rot(b.origin - *axis_origin);
Plane::new(o, rot(b.x), rot(b.y)).ok_or_else(|| DocError::Invalid("degenerate plane".into()))
}
PlaneRef::Flipped { base, about_y } => Ok(self.resolve_plane_in(vals, base, depth + 1, st)?.flipped(*about_y)),
PlaneRef::Construction { name } => {
// Evaluated already: as it came out.
if let Some(c) = st.and_then(|st| st.construct.iter().find(|c| &c.name == name))
+3
View File
@@ -3998,6 +3998,9 @@ pub(crate) fn sheet_face_plane_in_frame(doc: &Document, st: &ModelState, comp: u
axis_dir: *axis_dir,
angle: angle.clone(),
},
PlaneRef::Flipped { base, about_y } => {
PlaneRef::Flipped { base: Box::new(sheet_face_plane_in_frame(doc, st, comp, base)?), about_y: *about_y }
}
other => other.clone(),
})
}
+5 -3
View File
@@ -82,7 +82,7 @@ impl FaceAnchor {
pub fn plane_face_anchor(plane: &PlaneRef) -> Option<&FaceAnchor> {
match plane {
PlaneRef::Face { anchor, .. } => anchor.as_deref(),
PlaneRef::Offset { base, .. } | PlaneRef::AtAngle { base, .. } => plane_face_anchor(base),
PlaneRef::Offset { base, .. } | PlaneRef::AtAngle { base, .. } | PlaneRef::Flipped { base, .. } => plane_face_anchor(base),
_ => None,
}
}
@@ -90,7 +90,7 @@ pub fn plane_face_anchor(plane: &PlaneRef) -> Option<&FaceAnchor> {
fn has_face_reference(plane: &PlaneRef) -> bool {
match plane {
PlaneRef::Face { .. } => true,
PlaneRef::Offset { base, .. } | PlaneRef::AtAngle { base, .. } => has_face_reference(base),
PlaneRef::Offset { base, .. } | PlaneRef::AtAngle { base, .. } | PlaneRef::Flipped { base, .. } => has_face_reference(base),
_ => false,
}
}
@@ -110,7 +110,9 @@ impl Document {
}
match plane {
PlaneRef::Face { anchor: Some(anchor), .. } => *plane = PlaneRef::Custom { plane: doc.resolve_face_anchor(anchor, author, state)? },
PlaneRef::Offset { base, .. } | PlaneRef::AtAngle { base, .. } => replace(doc, base, author, state, depth + 1)?,
PlaneRef::Offset { base, .. } | PlaneRef::AtAngle { base, .. } | PlaneRef::Flipped { base, .. } => {
replace(doc, base, author, state, depth + 1)?
}
_ => {}
}
Ok(())
@@ -0,0 +1,5 @@
//! A sketch can sit on a plane seen from its other side: `{"type": "flipped", "base": <plane>}`
//! (with `"about_y": true` when it turns about the plane's y axis rather than its x axis). New
//! sketches started from behind an origin or construction plane save this, so they face the
//! viewer. Older designs have no flipped planes and open unchanged; the last release refuses a
//! design that has one (it asks for an update) instead of putting the sketch on the wrong side.
+6 -2
View File
@@ -4257,7 +4257,9 @@ pub fn rename_plane_sheet_body(plane: &mut crate::PlaneRef, old: &str, new: &str
*name = renamed;
}
}
crate::PlaneRef::Offset { base, .. } | crate::PlaneRef::AtAngle { base, .. } => rename_plane_sheet_body(base, old, new),
crate::PlaneRef::Offset { base, .. } | crate::PlaneRef::AtAngle { base, .. } | crate::PlaneRef::Flipped { base, .. } => {
rename_plane_sheet_body(base, old, new)
}
_ => {}
}
}
@@ -4267,7 +4269,9 @@ pub fn rename_plane_sheet_body(plane: &mut crate::PlaneRef, old: &str, new: &str
pub fn plane_sheet_face(plane: &crate::PlaneRef) -> Option<&str> {
match plane {
crate::PlaneRef::Face { name: Some(name), anchor: None, .. } if split_sheet_face_name(name).is_some() => Some(name),
crate::PlaneRef::Offset { base, .. } | crate::PlaneRef::AtAngle { base, .. } => plane_sheet_face(base),
crate::PlaneRef::Offset { base, .. } | crate::PlaneRef::AtAngle { base, .. } | crate::PlaneRef::Flipped { base, .. } => {
plane_sheet_face(base)
}
_ => None,
}
}
+2 -2
View File
@@ -364,7 +364,7 @@ impl Walk for PlaneRef {
let mut p = self;
for _ in 0..64 {
match p {
PlaneRef::Offset { base, .. } | PlaneRef::AtAngle { base, .. } => p = base,
PlaneRef::Offset { base, .. } | PlaneRef::AtAngle { base, .. } | PlaneRef::Flipped { base, .. } => p = base,
PlaneRef::Construction { name } => return f(Ref::Feature(name)),
_ => return,
}
@@ -374,7 +374,7 @@ impl Walk for PlaneRef {
let mut p = self;
for _ in 0..64 {
match p {
PlaneRef::Offset { base, .. } | PlaneRef::AtAngle { base, .. } => p = base,
PlaneRef::Offset { base, .. } | PlaneRef::AtAngle { base, .. } | PlaneRef::Flipped { base, .. } => p = base,
PlaneRef::Construction { name } => return f(Ref::Feature(name)),
_ => return,
}