Skip to content

feat: Add support to use convex/concave Path shapes in PolygonComponent, PolygonHitbox and Polygon - #4048

Merged
spydon merged 124 commits into
flame-engine:mainfrom
adario:feat/path-shapes
Sep 21, 2026
Merged

spydon merged 124 commits into
flame-engine:mainfrom
adario:feat/path-shapes

Conversation

@adario

@adario adario commented Sep 15, 2026 •

Copy link
Copy Markdown
Contributor

Description

The purpose of this PR is to add support for using both convex and concave Path shapes when creating PolygonComponent, PolygonHitbox and Polygon objects: the contours of a Path object are sampled and converted to a list of polygon vertices. Support for concave polygons, described below, is available in PolygonRayIntersection.

A complete testbed for all changes is available here; the relevant examples have been ported here too.

The changes add to and introduce a few extensions:

The walkContours, walkContourAt and walkContour methods extract the vertex lists from the contours of a Path:

  • The contour is walked with strides that follow how much it bends, so straight stretches cost a few lookups and tight curves are followed below the step. The granularity parameter is that step, which defaults to 1.0.
  • The corners between straight lines are placed exactly, and the points where the contour reaches its bounds are kept, so that the polygon has the size of the contour.
  • The samples that are not needed to stay within the tolerance are removed, which defaults to half of the granularity. This also works across the point where a closed contour starts and ends.

Direct construction from Path objects is available via:

All three take the index of the contour to use, the first one by default, and the granularity, and only walk that contour of the path.

As long as the Path contours represent valid polygons, the PolygonRayIntersection mixin now supports both convex and concave polygons. Whether the ray starts inside of the polygon is decided by the parity of the edges that it crosses, instead of their absolute quantity. The hits and the crossings come from the same test of which side of the ray each vertex is on, which both edges of a vertex share, so a ray through or next to a vertex is not misjudged and can not slip through between two edges.

isSolid is now an argument of the ShapeComponent constructor, which the polygon, rectangle and circle components and their hitboxes pass on instead of assigning it in their constructor bodies.

The shape components and collision detection pages of the documentation describe the new constructors, and no longer say that polygon hitboxes have to be convex.

In terms of performance, @spydon was kind enough to provide the new path_collision and path_contour benchmarks (thank you very much, Lukas.) There is also a new ray_intersection benchmark for rays against concave and convex contour hitboxes.

Checklist

  • I have followed the Contributor Guide when preparing my PR.
  • I have updated/added tests for ALL new/updated/fixed functionality.
  • I have updated/added relevant documentation in docs and added dartdoc comments with ///.
  • I have updated/added relevant examples in examples or docs.

Breaking Change?

  • Yes, this PR is a breaking change.
  • No, this PR is not a breaking change.

Related Issues

Closes #4040

adario and others added 30 commits September 6, 2026 17:39
…n polygons

Reports vertex counts and sampling error of walkContours, per-ray intersection
cost, agreement between the crossings heuristic, containment, and odd parity
for the inside-hitbox check on concave shapes, the effect of simplifying the
sampled contour, and polygon-polygon intersection cost.
chore: Add benchmark comparing Path contour hitboxes with hand-written polygons
Runs the collision detection system with a hitbox sampled from a Path contour against circles, rectangles, hand-written polygons, and itself, next to the same scenes with a hand-written polygon of the same shape. The polygon scenes also compile on main, so the two can be compared by running the file on both branches.
…cations from segment intersection

ShapeHitbox.intersections now passes the overlap of the two AABBs to the intersection systems, which already knew how to filter edges by a rect but never received one. LineSegment.intersections rejects segment pairs whose bounding boxes do not overlap before doing any math and no longer allocates Line objects or result lists for misses.
chore: Add a collision benchmark for Path contour hitboxes
perf: Only test polygon edges inside the hitbox overlap and drop allocations from segment intersection
Comment thread packages/flame/lib/src/experimental/geometry/shapes/polygon.dart
Comment thread packages/flame/benchmark/path_contour_benchmark.dart Outdated
Comment thread examples/lib/stories/collision_detection/rays_in_shape_example.dart Outdated
Comment thread examples/lib/commons/rounded_rect_component.dart
Comment thread examples/lib/commons/paths.dart Outdated
Comment thread examples/lib/commons/path_component.dart

@luanpotter luanpotter left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, LGTM, nice addition! Left a few comments and questions, but nothing blocking

@adario

adario commented Sep 21, 2026

Copy link
Copy Markdown
Contributor Author

Overall, LGTM, nice addition! Left a few comments and questions, but nothing blocking

Thank you very much, Luan. :-)

@adario

adario commented Sep 21, 2026

Copy link
Copy Markdown
Contributor Author

Apologies for the latest erroneous merge and subsequent revert... blush

@spydon spydon left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this massive undertaking, this will be really useful!

@spydon
spydon enabled auto-merge (squash) September 21, 2026 17:30
@spydon
spydon merged commit 95bc612 into flame-engine:main Sep 21, 2026
8 checks passed
spydon added a commit that referenced this pull request Sep 30, 2026
The `PathComponent` class
[introduced](https://github.com/flame-engine/flame/pull/4048/changes#diff-0c50838cfe31110765e9616e8ae15ab9cbbd3818310d5376acf74f9bce14ea4e)
in PR #4048 is moved from `examples/lib/commons` into `lib/src/geometry`
in the core package, on suggestion from @spydon, and it gets a hitbox
counterpart, `PathHitbox`, so that the pair mirrors `PolygonComponent`
and `PolygonHitbox`.

`PathComponent` renders a `Path` and follows each of its closed contours
with a polygon, in the same way as `PolygonComponent.fromPath` follows a
single contour. The polygons are kept as vertices, not as child
components, and they decide whether a point is inside of the component.
The constructor takes `sampling` and `tolerance` to control how the
contours are followed, and `filter` to leave out the polygons that lie
inside of the largest one, like the eyes of a face.

`PathHitbox` is a `PathComponent` with `ShapeHitbox`, and it is a single
hitbox: it collides, contains points and is hit by rays as a whole,
whichever of its polygons is involved, and it reports one collision to
its parent even when several polygons touch the other hitbox. It
computes its aabb from all the polygons, a ray hits the nearest polygon,
and it takes the `collisionType` like the other hitboxes.
`PathPathIntersections`, `PathPolygonIntersections` and
`CirclePathIntersections` are added to the intersection systems.

The affected examples are updated accordingly, with a
`CollidablePathComponent` in the examples that pairs a `PathComponent`
with a `PathHitbox`. There is documentation for both classes in
`shape_components.md` and `collision_detection.md`, and tests for
`PathComponent` and `PathHitbox`.

---------

Co-authored-by: Lukas Klingsbo <lukas.klingsbo@gmail.com>
Co-authored-by: Lukas Klingsbo <me@lukas.fyi>
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.

PolygonRayIntersection failing on non-Euclidean/concave/hollow polygons

5 participants