Envelopes: document how a straight segment's handles count in the top object corner test

A top object's corner test treats a straight segment as having its
handles a third of the way along it, so an anchor between two straight
segments is a corner only when it turns out of the outline. docs/mcp.md
and the doc comment now say so, and a new test covers an inward anchor
between straight segments. The bulging-curve corner test checks against
a fixed point instead of the same nearest-point routine the code uses.
This commit is contained in:
dotCooCoo
2026-11-09 00:31:07 +07:00
committed by bflatastic
parent ed6c0d434f
commit 51bab66409
3 changed files with 21 additions and 7 deletions
+14 -5
View File
@@ -262,18 +262,17 @@ mod tests {
fn a_top_object_corner_inside_a_bulging_curve_is_the_nearest_outline_point() {
// The top left anchor's handle bulges out past the corner, so the anchor turns inward
// there and the corner falls inside the curve.
let top = CubicBez::new((0.0, 0.0), (-50.0, -50.0), (150.0, 0.0), (200.0, 0.0));
// The outline's bounding box starts at (-7.957, -22.222); the top curve's point nearest
// that corner, found by sampling the curve 2,000,000 times, is (-5.072093, -18.127254).
let path = outline(&[
((0.0, 0.0), (0.0, 0.0), (-50.0, -50.0)),
((200.0, 0.0), (150.0, 0.0), (200.0, 0.0)),
((200.0, 100.0), (200.0, 100.0), (200.0, 100.0)),
((0.0, 100.0), (0.0, 100.0), (0.0, 100.0)),
]);
use vectorcraft_geom::kurbo::ParamCurveNearest;
let b = path.subpaths[0].segment(3).bounding_box().union(top.bounding_box());
let want = top.eval(top.nearest(Point::new(b.x0, b.y0), 1e-12).t);
let want = Point::new(-5.072093, -18.127254);
let tl = Coons::from_top_object(&path, None).unwrap().corners()[0];
assert!(tl.distance(want) < 1e-6, "{tl:?}, want {want:?}");
assert!(tl.distance(want) < 1e-5, "{tl:?}, want {want:?}");
assert!(tl.distance(Point::ZERO) > 1.0, "{tl:?}");
}
@@ -316,6 +315,16 @@ mod tests {
}
}
#[test]
fn a_top_object_corner_skips_an_inward_anchor_between_straight_segments() {
// A 200 × 100 outline of straight segments with a notch at its top left: (40,40) is the
// anchor nearest the box corner, but it turns into the outline, so (60,0) is the corner.
let c = |p: (f64, f64)| (p, p, p);
let path = outline(&[c((60.0, 0.0)), c((200.0, 0.0)), c((200.0, 100.0)), c((0.0, 100.0)), c((0.0, 60.0)), c((40.0, 40.0))]);
let tl = Coons::from_top_object(&path, None).unwrap().corners()[0];
assert!(tl.distance(Point::new(60.0, 0.0)) < 1e-9, "{tl:?}");
}
#[test]
fn a_top_object_corner_tie_goes_to_the_first_anchor() {
// Both chamfer anchors lie 30 from the box corner: the first one along the outline, in
+4 -1
View File
@@ -354,7 +354,10 @@ const CORNER_END_SNAP: f64 = 0.005;
/// Is the anchor at `a`, reached by handle `h_in` and left by handle `h_out` on an outline that
/// runs clockwise on screen, a corner of a top object? A handle of [`SHORT_HANDLE`] or shorter
/// makes it one. Otherwise its tangent directions must differ by [`SMOOTH_TURN`] or more and turn
/// clockwise, so it points out of the outline.
/// clockwise, so it points out of the outline. A straight segment's handles here are the ones
/// [`Coons::from_top_object`] gives it, a third of the way along, so an anchor between two
/// straight segments longer than three times [`SHORT_HANDLE`] is a corner only when it turns out
/// of the outline.
fn top_object_corner_anchor(h_in: Point, a: Point, h_out: Point) -> bool {
let (vin, vout) = (a - h_in, h_out - a);
let (len_in, len_out) = (vin.hypot(), vout.hypot());
+3 -1
View File
@@ -6619,7 +6619,9 @@ arcLower, arcUpper, arch, bulge, shellLower, shellUpper, flag, wave, fish, rise,
A top object's outline is split into four sides at four corners. Each corner is the anchor nearest a corner of the
outline's bounding box among the anchors within 47.5% of the box's width and height from that box corner that count as
corners: an anchor with a handle 0.01 pt long or shorter, or one whose tangent unit vectors differ by 0.01 or more in
x or y and turn out of the outline. On a tie, the anchor met first along the outline in the order it was drawn wins.
x or y and turn out of the outline. In this test a straight segment counts as having its handles a third of the way
along it, so an anchor between two straight segments longer than 0.03 pt is a corner only when it turns out of the
outline. On a tie, the anchor met first along the outline in the order it was drawn wins.
Without such an anchor, the corner is the outline's point nearest the box corner, moved onto the anchor at the end of
its segment when within 0.5% of the segment's parameter. Each piece of a side between anchors and corners takes a
share of the content's width or height in proportion to the length of its control polygon, spread along the piece by