Skip to content

Return the feasible positions of null area of the general NFP - #151

Merged
fontanf merged 1 commit into
mainfrom
nfp-feasible-elements-and-points
Oct 30, 2026
Merged

fontanf merged 1 commit into
mainfrom
nfp-feasible-elements-and-points

Conversation

@fontanf

@fontanf fontanf commented Oct 30, 2026

Copy link
Copy Markdown
Owner

Problem

The general no_fit_polygon (shapes with holes) is the union of the convex NFPs of the convex parts of the shapes. A position on the boundaries of several convex NFPs, but strictly inside none of them, doesn't make the shapes overlap, even though it is strictly inside their union. A MultiShapeWithHoles can't represent these positions of null area, so they were lost:

Fixed shape, orbiting 1×1 square Expected Before
3×3 with a 1×1 hole feasible point (1, 1) plain 4×4 square
4×3 with a 2×1 hole feasible segment (1, 1)–(2, 1) plain 5×4 rectangle
U shape with a slot of width 1 feasible slit plain 4×4 square

Changes

  • no_fit_polygon(const ShapeWithHoles&, const ShapeWithHoles&) now returns a NoFitPolygonOutput:

    • nfp: the regions, as before;
    • feasible_elements: line segments strictly inside nfp whose points don't make the shapes overlap;
    • feasible_points: isolated points strictly inside nfp which don't make the shapes overlap.

    Callers need to use .nfp to get the previous result.

  • A feasible line segment lies on the parts of the boundaries of two convex NFPs which overlap in opposite directions (always line segments, the convex NFPs being convex). A feasible point lies at an intersection of the boundaries of convex NFPs. Only these candidates are checked: not strictly inside any convex NFP, and strictly inside nfp. The last check matters because the boundaries of the convex NFPs may be slightly outside of the computed union.

  • Before computing the intersections, the elements of the convex NFPs strictly inside the union of the other convex NFPs are removed, by walking along each element from one convex NFP to the next.

  • New IntersectionTree::find_shape(point, strict, condition), which stops at the first shape containing the point, with a condition evaluated before the containment check (used here as a cheap bounding-box filter).

Performance

Simple cases are barely affected. On the sawtooth self-pairing test (Issue558SawtoothSelfPairing, 442 vertices, ~13k convex NFPs), no_fit_polygon goes from about 4.6 s to about 10.5 s, mostly spent computing the ~7M intersections between the remaining elements.

Tests

New NoFitPolygonFeasibleTest suite: square and disk in holes, slots and notches of exactly their size, plus a loose-fit control. Each test checks the expected feasible elements and points, that these positions don't make the shapes overlap, and that the positions strictly inside the NFP around them do. All 714 tests pass.

The NFP of two general shapes is the union of the convex NFPs of their
convex parts. But a position on the boundaries of several convex NFPs,
strictly inside none of them, doesn't make the shapes overlap, even if
it is strictly inside their union. The union, a MultiShapeWithHoles,
can't represent these positions: when the orbiting shape fits exactly a
hole of the fixed shape, the NFP had no hole instead of a single
feasible point, and when it fits exactly the width of a slot, the NFP
had no slit instead of a feasible line segment.

no_fit_polygon now returns a NoFitPolygonOutput, with the union in nfp,
and the feasible line segments and points strictly inside it in
feasible_elements and feasible_points. A feasible line segment is on
the parts of the boundaries of two convex NFPs which overlap in opposite
directions, and a feasible point is at an intersection of the
boundaries of convex NFPs, so only these candidates are checked. The
elements of the convex NFPs strictly inside the union of the other ones
are removed first, and IntersectionTree::find_shape, which stops at the
first shape containing a point, makes the checks fast enough: on the
sawtooth self-pairing test, no_fit_polygon goes from about 4.6 s to
about 10.5 s.
@fontanf
fontanf merged commit e394fe8 into main Oct 30, 2026
3 checks passed
@fontanf
fontanf deleted the nfp-feasible-elements-and-points branch October 30, 2026 22:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant