Fix polyline offset arc bulge after joining moved endpoints (#817) (#825)

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
MarekSpodBiedry 2026-10-13 11:17:25 +02:00 committed by GitHub
parent 64b7825d08
commit 4257674dfa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 38 additions and 5 deletions

View File

@ -682,20 +682,40 @@ pub(crate) fn offset_polyline(pl: &Polyline, d: f64) -> Option<Vec<PolyVertex>>
return None;
}
let n = segs.len();
let mut verts: Vec<PolyVertex> = Vec::new();
// Where each offset segment starts after joining it to the previous one.
let mut starts: Vec<Vec2> = Vec::with_capacity(n);
for i in 0..n {
let s = segs.get(i)?;
let start = if i == 0 && !pl.closed {
starts.push(if i == 0 && !pl.closed {
s.start()
} else {
let prev = segs.get((i + n - 1) % n)?;
join_point(prev, s).unwrap_or(s.start())
});
}
let mut verts: Vec<PolyVertex> = Vec::new();
for i in 0..n {
let s = segs.get(i)?;
let start = *starts.get(i)?;
let end = if i + 1 < n {
*starts.get(i + 1)?
} else if pl.closed {
*starts.first()?
} else {
s.end()
};
let bulge = match s {
Segment::Line(_) => 0.0,
Segment::Arc { arc, ccw } => {
let sw = arc.sweep();
cadcraft_geom::arc_to_bulge(if *ccw { sw } else { -sw })
// The joined endpoints may have moved along the circle: take the sweep between them.
let sweep = if start.near(end, 1e-9) {
arc.sweep()
} else if *ccw {
ccw_sweep(arc.center.angle_to(start), arc.center.angle_to(end))
} else {
ccw_sweep(arc.center.angle_to(end), arc.center.angle_to(start))
};
cadcraft_geom::arc_to_bulge(if *ccw { sweep } else { -sweep })
}
};
verts.push(PolyVertex { p: start, bulge, start_width: 0.0, end_width: 0.0 });
@ -703,7 +723,6 @@ pub(crate) fn offset_polyline(pl: &Polyline, d: f64) -> Option<Vec<PolyVertex>>
if !pl.closed {
verts.push(PolyVertex::new(segs.last()?.end()));
}
// Recompute arc bulges where endpoints moved (approximation: keep sweep sign).
Some(verts)
}

View File

@ -707,3 +707,17 @@ fn pedit_preselected_line_asks_to_convert() {
assert!(s.running.is_none());
assert!(matches!(kind(&s, &m), EntityKind::Line(_)));
}
#[test]
fn offset_polyline_arc_bulge_follows_joined_endpoints() {
let mut s = Session::new();
// A diagonal line into a CCW semicircle centred on (10, 5); offsetting inward moves the arc start.
let pl = pline(&mut s, json!([[0, -5], {"p": [10, 0], "bulge": 1}, [10, 10]]), false);
s.execute("offset", &json!({"handle": pl, "distance": 1, "side": [12, 5]})).unwrap();
let p = poly(&last(&s));
let (a, b) = (p.vertices[1], p.vertices[2]);
let (arc, ccw) = cadcraft_geom::bulge_to_arc(a.p, b.p, a.bulge).unwrap();
assert!(ccw);
assert!(near(arc.center, 10.0, 5.0), "arc centre drifted to {:?}", arc.center);
assert!((arc.radius - 4.0).abs() < 1e-6, "radius {}", arc.radius);
}