forked from 1b-agent/opencadstudio-upstream
fix(snap): arc segments of a polyline offer their centre and quadrants
Center and Quadrant found nothing on a polyline's bulge segments: the lightweight and heavy polylines carried no snap points for them, and a polyline split into several wires kept its points on one piece whose box the others' never reached. Each arc segment now carries its centre and quadrants, hovering the segment offers its centre, and a wire's box covers its snap points. Fixes #1640 Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
29392de267
commit
fae64886ee
6 files changed
+150
-28
No files matched your search
+38
-3
@@ -484,9 +484,9 @@ pub fn snap_from(curve: &PlanarCurve) -> CurveSnap {
|
||||
// there and let their midpoints be derived; everything else names its own
|
||||
// ends and middle explicitly.
|
||||
//
|
||||
// The same distinction is why a polyline's arc-segment centres are not
|
||||
// emitted: a wire carrying a centre is treated as round elsewhere, which
|
||||
// a polyline with one bulge in it is not.
|
||||
// The same distinction is why a polyline's arc-segment centres carry
|
||||
// their own hint: a wire carrying a `Center` is treated as round
|
||||
// elsewhere, which a polyline with one bulge in it is not.
|
||||
let chain = matches!(curve.curve, Curve::Line(_) | Curve::Polyline(_));
|
||||
let mut out = CurveSnap::default();
|
||||
let mut push = |world: [f64; 3], hint: SnapHint| {
|
||||
@@ -499,6 +499,7 @@ pub fn snap_from(curve: &PlanarCurve) -> CurveSnap {
|
||||
SnapKind::Endpoint => push(world, SnapHint::Endpoint),
|
||||
SnapKind::Midpoint if !chain => push(world, SnapHint::Midpoint),
|
||||
SnapKind::Centre if !chain => push(world, SnapHint::Center),
|
||||
SnapKind::Centre => push(world, SnapHint::ArcCenter),
|
||||
SnapKind::Quadrant => push(world, SnapHint::Quadrant),
|
||||
_ => {}
|
||||
}
|
||||
@@ -506,6 +507,20 @@ pub fn snap_from(curve: &PlanarCurve) -> CurveSnap {
|
||||
out
|
||||
}
|
||||
|
||||
/// The centres and quadrants of a polyline's arc segments, for a polyline
|
||||
/// render that names its vertices itself.
|
||||
pub fn arc_segment_snaps(entity: &EntityType) -> Vec<(glam::DVec3, SnapHint)> {
|
||||
entity_curve(entity)
|
||||
.map(|curve| {
|
||||
snap_from(&curve)
|
||||
.snap_pts
|
||||
.into_iter()
|
||||
.filter(|(_, hint)| matches!(hint, SnapHint::ArcCenter | SnapHint::Quadrant))
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
/// The magnitude the geometry's own coordinates sit at, for scaling a
|
||||
/// relative tolerance. Floored at one so geometry near the origin does not
|
||||
/// end up with a tolerance of nothing.
|
||||
@@ -806,6 +821,26 @@ mod tests {
|
||||
assert!(snap.snap_pts.is_empty(), "{:?}", snap.snap_pts);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_polyline_arc_segment_offers_its_centre_and_quadrants() {
|
||||
let mut polyline = LwPolylineEnt::default();
|
||||
polyline.normal = v3(0.0, 0.0, 1.0);
|
||||
let mut bend = LwVertex::from_coords(0.0, 0.0);
|
||||
bend.bulge = 0.9999999999999999;
|
||||
polyline.vertices = vec![LwVertex::from_coords(-20.0, 0.0), bend, LwVertex::from_coords(20.0, 0.0)];
|
||||
let snaps = arc_segment_snaps(&EntityType::LwPolyline(polyline));
|
||||
let at = |hint: SnapHint, x: f64, y: f64| {
|
||||
snaps.iter().any(|(p, h)| {
|
||||
std::mem::discriminant(h) == std::mem::discriminant(&hint)
|
||||
&& (p.x - x).abs() < 1e-9
|
||||
&& (p.y - y).abs() < 1e-9
|
||||
})
|
||||
};
|
||||
assert!(at(SnapHint::ArcCenter, 10.0, 0.0), "{snaps:?}");
|
||||
assert!(at(SnapHint::Quadrant, 10.0, -10.0), "{snaps:?}");
|
||||
assert!(!at(SnapHint::Quadrant, 10.0, 10.0), "{snaps:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_closed_curve_has_no_ends_to_offer() {
|
||||
let mut circle = CircleEnt::default();
|
||||
|
||||
@@ -1226,7 +1226,13 @@ fn apply_transform(pline: &mut LwPolyline, t: &EntityTransform) {
|
||||
|
||||
impl RenderConvertible for LwPolyline {
|
||||
fn to_render(&self, document: &codec::CadDocument) -> Option<RenderEntity> {
|
||||
Some(to_render(self, document.header.fill_mode))
|
||||
let mut render = to_render(self, document.header.fill_mode);
|
||||
// Arc segments offer their centres and quadrants.
|
||||
if self.vertices.iter().any(|v| v.bulge != 0.0) {
|
||||
let entity = codec::EntityType::LwPolyline(self.clone());
|
||||
render.snap_pts.extend(crate::entities::curve::arc_segment_snaps(&entity));
|
||||
}
|
||||
Some(render)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -493,7 +493,13 @@ fn centerline_metadata_2d(
|
||||
|
||||
impl RenderConvertible for Polyline2D {
|
||||
fn to_render(&self, document: &codec::CadDocument) -> Option<RenderEntity> {
|
||||
Some(tessellate_polyline2d(self, document.header.fill_mode))
|
||||
let mut render = tessellate_polyline2d(self, document.header.fill_mode);
|
||||
// Arc segments offer their centres and quadrants.
|
||||
if self.vertices.iter().any(|v| v.bulge != 0.0) {
|
||||
let entity = codec::EntityType::Polyline2D(self.clone());
|
||||
render.snap_pts.extend(crate::entities::curve::arc_segment_snaps(&entity));
|
||||
}
|
||||
Some(render)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user