fix(offset): allow OFFSET of XLINE (and Ellipse/Spline) objects (#296)
OFFSET rejected XLINE targets: on_entity_pick only accepted Line/Circle/Arc/LwPolyline, compute_offset had no XLine arm, and entity_wire_pts returned no points for it (so hover/preview/through-distance failed too). Add offset_xline (parallel infinite line: same direction, base point shifted perpendicular toward the pick side), a compute_offset arm, and an entity_wire_pts case (a long segment standing in for the infinite line during hit-test and preview; the committed XLine renders true). Also widen the pick filter to every type compute_offset supports — this additionally reaches Ellipse and Spline, whose offset functions already existed but weren't selectable. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
e015fbe328
commit
07eb9dcb9a
1 changed files with 47 additions and 2 deletions
|
|
@ -15,7 +15,7 @@ use crate::modules::draw::modify::spline_ops::{spline_pts_wire, spline_sample_xy
|
||||||
use acadrust::entities::LwVertex;
|
use acadrust::entities::LwVertex;
|
||||||
use acadrust::entities::{
|
use acadrust::entities::{
|
||||||
Arc as ArcEnt, Circle as CircleEnt, Ellipse as EllipseEnt, Line as LineEnt, LwPolyline,
|
Arc as ArcEnt, Circle as CircleEnt, Ellipse as EllipseEnt, Line as LineEnt, LwPolyline,
|
||||||
Spline as SplineEnt,
|
Spline as SplineEnt, XLine as XLineEnt,
|
||||||
};
|
};
|
||||||
use acadrust::{EntityType, Handle};
|
use acadrust::{EntityType, Handle};
|
||||||
use glam::{DVec3, Vec3};
|
use glam::{DVec3, Vec3};
|
||||||
|
|
@ -86,6 +86,33 @@ fn offset_line(l: &LineEnt, dist: f64, side_pt: Vec3) -> Option<EntityType> {
|
||||||
Some(EntityType::Line(new_l))
|
Some(EntityType::Line(new_l))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── XLine (infinite construction line) offset ────────────────────────────────
|
||||||
|
// A parallel infinite line: same direction, base point shifted perpendicular by
|
||||||
|
// `dist` toward the side the cursor is on. (#296)
|
||||||
|
|
||||||
|
fn offset_xline(x: &XLineEnt, dist: f64, side_pt: Vec3) -> Option<EntityType> {
|
||||||
|
let dx = x.direction.x;
|
||||||
|
let dy = x.direction.y;
|
||||||
|
let len = (dx * dx + dy * dy).sqrt();
|
||||||
|
if len < 1e-12 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let nx = -dy / len; // left-perpendicular
|
||||||
|
let ny = dx / len;
|
||||||
|
|
||||||
|
let vx = side_pt.x as f64 - x.base_point.x;
|
||||||
|
let vy = side_pt.y as f64 - x.base_point.y;
|
||||||
|
let cross = dx * vy - dy * vx;
|
||||||
|
let sign = if cross >= 0.0 { 1.0 } else { -1.0 };
|
||||||
|
|
||||||
|
let mut new_x = x.clone();
|
||||||
|
new_x.common.handle = Handle::NULL;
|
||||||
|
new_x.base_point.x += sign * nx * dist;
|
||||||
|
new_x.base_point.y += sign * ny * dist;
|
||||||
|
Some(EntityType::XLine(new_x))
|
||||||
|
}
|
||||||
|
|
||||||
// ── Circle offset ──────────────────────────────────────────────────────────
|
// ── Circle offset ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
fn offset_circle(c: &CircleEnt, dist: f64, side_pt: Vec3) -> Option<EntityType> {
|
fn offset_circle(c: &CircleEnt, dist: f64, side_pt: Vec3) -> Option<EntityType> {
|
||||||
|
|
@ -424,6 +451,7 @@ fn compute_offset(entity: &EntityType, dist: f64, side_pt: Vec3) -> Option<Entit
|
||||||
EntityType::LwPolyline(p) => offset_lwpolyline(p, dist, side_pt),
|
EntityType::LwPolyline(p) => offset_lwpolyline(p, dist, side_pt),
|
||||||
EntityType::Ellipse(e) => offset_ellipse(e, dist, side_pt),
|
EntityType::Ellipse(e) => offset_ellipse(e, dist, side_pt),
|
||||||
EntityType::Spline(s) => offset_spline(s, dist, side_pt),
|
EntityType::Spline(s) => offset_spline(s, dist, side_pt),
|
||||||
|
EntityType::XLine(x) => offset_xline(x, dist, side_pt),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -540,6 +568,18 @@ fn entity_wire_pts(e: &EntityType) -> Vec<[f32; 3]> {
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
EntityType::Spline(s) => spline_pts_wire(s),
|
EntityType::Spline(s) => spline_pts_wire(s),
|
||||||
|
EntityType::XLine(x) => {
|
||||||
|
// Infinite in both directions — represent it as a very long segment
|
||||||
|
// for hit-testing and the offset preview. Long enough to read as
|
||||||
|
// infinite at any working zoom; the committed XLine renders true.
|
||||||
|
const HL: f64 = 1.0e6;
|
||||||
|
let (bx, by, bz) = (x.base_point.x, x.base_point.y, x.base_point.z);
|
||||||
|
let (dx, dy, dz) = (x.direction.x, x.direction.y, x.direction.z);
|
||||||
|
vec![
|
||||||
|
[(bx - dx * HL) as f32, (by - dy * HL) as f32, (bz - dz * HL) as f32],
|
||||||
|
[(bx + dx * HL) as f32, (by + dy * HL) as f32, (bz + dz * HL) as f32],
|
||||||
|
]
|
||||||
|
}
|
||||||
_ => vec![],
|
_ => vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -690,11 +730,16 @@ impl CadCommand for OffsetCommand {
|
||||||
.find(|e| e.common().handle == handle)
|
.find(|e| e.common().handle == handle)
|
||||||
.cloned();
|
.cloned();
|
||||||
|
|
||||||
|
// Accept every type compute_offset can offset — including XLine (#296),
|
||||||
|
// and Ellipse/Spline whose offset functions existed but weren't reachable.
|
||||||
match entity {
|
match entity {
|
||||||
Some(e @ EntityType::Line(_))
|
Some(e @ EntityType::Line(_))
|
||||||
| Some(e @ EntityType::Circle(_))
|
| Some(e @ EntityType::Circle(_))
|
||||||
| Some(e @ EntityType::Arc(_))
|
| Some(e @ EntityType::Arc(_))
|
||||||
| Some(e @ EntityType::LwPolyline(_)) => {
|
| Some(e @ EntityType::LwPolyline(_))
|
||||||
|
| Some(e @ EntityType::Ellipse(_))
|
||||||
|
| Some(e @ EntityType::Spline(_))
|
||||||
|
| Some(e @ EntityType::XLine(_)) => {
|
||||||
self.step = Step::PickSide {
|
self.step = Step::PickSide {
|
||||||
handle,
|
handle,
|
||||||
entity: e,
|
entity: e,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue