fix(3d): seat axis steps on 3D snaps; raised bases follow the cursor (#1030)

Height and distance steps (PUSHPULL, CYLINDER, CONE, PYRAMID, BOX,
EXTRUDE) replaced the point with the cursor's screen mapping onto the
command axis, discarding an acquired 3D object snap. A genuine snap now
supplies its own coordinate along the axis, as axis grips already do.

Base steps after the first base point (BOX/WEDGE opposite corner and cube
size, CYLINDER/CONE/PYRAMID radius) read the cursor on the base plane
through that point. A base started on a raised face intersected the ray
with world XY instead, so in oblique views the footprint ran far past the
mouse.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Hakan SevenandClaude Opus 5.5 committed 2026-09-25 21:46:47 +03:00
1 parent 89512c2f20
commit eb84bae779
3 files changed
+61 -2

No files matched your search

+10
View File
@@ -462,6 +462,16 @@ impl CadCommand for CylinderCommand {
self.plane = plane;
}
/// The radius step reads the cursor on the base plane through the
/// centre, so a base started on a raised face follows the mouse.
fn cursor_plane(&self) -> Option<(DVec3, DVec3)> {
if !matches!(self.step, Step::BaseRadius | Step::BaseDiameter) {
return None;
}
let frame = self.frame?;
Some((frame.z.normalize_or_zero(), frame.origin))
}
fn cursor_axis(&self) -> Option<(DVec3, DVec3)> {
matches!(self.step, Step::Height).then(|| {
let frame = self.frame.unwrap_or(self.plane);
+19
View File
@@ -2098,6 +2098,25 @@ impl CadCommand for PrimitiveCommand {
self.plane = plane;
}
/// Base steps after the first base point read the cursor on the base
/// plane through that point, so a base started on a raised face follows
/// the mouse instead of the ray's hit on the working plane below.
fn cursor_plane(&self) -> Option<(DVec3, DVec3)> {
let origin = if self.shape == Shape::Cone {
matches!(self.cone_step, ConeStep::BaseRadius | ConeStep::BaseDiameter)
.then_some(self.cone_frame?.origin)
} else if self.shape == Shape::Pyramid {
(self.pyramid_step == PyramidStep::BaseRadius).then_some(self.pyramid_frame?.origin)
} else if self.shape.rectangular() {
matches!(self.box_step, BoxStep::OppositeCorner | BoxStep::CubeSize)
.then(|| self.pts.first().map(|&first| self.plane.to_world(first)))
.flatten()
} else {
None
}?;
Some((self.plane.z.normalize_or_zero(), origin))
}
fn cursor_axis(&self) -> Option<(DVec3, DVec3)> {
if self.shape == Shape::Cone {
return matches!(self.cone_step, ConeStep::Height | ConeStep::HeightAfterTopRadius)