Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add 2D polygon boolean operations in Geometry singleton #28987

Merged
merged 1 commit into from
May 23, 2019
Merged

Add 2D polygon boolean operations in Geometry singleton #28987

merged 1 commit into from
May 23, 2019

Conversation

Xrayez
Copy link
Contributor

@Xrayez Xrayez commented May 18, 2019

Important prerequisite: #29003 ✔️

Supercedes #23559.
Closes #22275.

This is a more compact, easy to use implementation for boolean polygon/polyline operations:

geometry-clipper

Boolean polygon vs polygon operations

Geometry.merge_polygons_2d(poly_a, poly_b) # union
Geometry.clip_polygons_2d(poly_a, poly_b) # difference
Geometry.intersect_polygons_2d(poly_a, poly_b) # common area
Geometry.exclude_polygons_2d(poly_a, poly_b) # all but common area

Boolean polyline vs polygon operations

# Note: union and xor don't make sense here
Geometry.clip_polyline_with_polygon_2d(poly_a, poly_b) # cut
Geometry.intersect_polyline_with_polygon_2d(poly_a, poly_b) # chop

Polyline/Polygon grow/shrink operations

Geometry.offset_polygon_2d(polygon, delta) # delta positive or negative to grow/shrink
Geometry.offset_polyline_2d(polyline, delta) # returns polygon(s)!

All the methods return an array of polygons/polylines. The resulting polygons could possibly be holes which could be checked with Geometry.is_polygon_clockwise().

Could be easily used both within engine and via scripting, so that it can be useful for improving other areas like navigation and path finding.

Clipper 6.4.2 is used internally to perform polypaths clipping, as well as inflating/deflating polypaths.
The implementation is mostly forward compatible with Clipper 10.0.0.

Issues

#17319 - currently prevents Clipper from being compiled in release mode, Clipper should be patched to optionally disable exceptions (see #29003), which should help #25262 fixed by #29032.

Test project

geometry-clipper-test.zip

core/math/geometry.h Outdated Show resolved Hide resolved
@Xrayez
Copy link
Contributor Author

Xrayez commented May 19, 2019

Ok, some jobs still fail with tools=no enabled as expected because of #17319, so #29003 needs to be merged first and this PR to be rebased later.

@akien-mga akien-mga added this to the 3.2 milestone May 20, 2019
@akien-mga
Copy link
Member

As discussed in PR meeting today, the added feature seems good. #29003 needs to be solved first, and then this PR should be rebased to squash commits, but otherwise it should be good to merge.

@Xrayez Xrayez requested a review from a team as a code owner May 22, 2019 13:49
@akien-mga
Copy link
Member

akien-mga commented May 22, 2019

#29003 is merged, so you can eventually rebase on top of it/master to fix the build.

Clipper 6.4.2 is used internally to perform polypaths clipping, as well
as inflating/deflating polypaths. The following methods were added:

```
Geometry.merge_polygons_2d(poly_a, poly_b) # union
Geometry.clip_polygons_2d(poly_a, poly_b) # difference
Geometry.intersect_polygons_2d(poly_a, poly_b) # intersection
Geometry.exclude_polygons_2d(poly_a, poly_b) # xor

Geometry.clip_polyline_with_polygon_2d(poly_a, poly_b)
Geometry.intersect_polyline_with_polygon_2d(poly_a, poly_b)

Geometry.offset_polygon_2d(polygon, delta) # inflate/deflate
Geometry.offset_polyline_2d(polyline, delta) # returns polygons

// This one helps to implement CSG-like behaviour:
Geometry.transform_points_2d(points, transform)
```

All the methods return an array of polygons/polylines. The resulting
polygons could possibly be holes which could be checked with
`Geometry.is_polygon_clockwise()` which was exposed to scripting as well.
@Xrayez
Copy link
Contributor Author

Xrayez commented May 23, 2019

I wanted to add some more input validation, but seems like Clipper can handle most input without throwing errors (the API is exposed in such a way that misuse is kept to a minimum, or eliminated completely), so I'm satisfied with it.

@akien-mga akien-mga merged commit 664f462 into godotengine:master May 23, 2019
@akien-mga
Copy link
Member

Thanks for the awesome features!

@Xrayez Xrayez deleted the geometry-clipper-bind branch May 23, 2019 09:34
@TotCac
Copy link

TotCac commented May 23, 2019

Many thanks

@Dr4kzor
Copy link

Dr4kzor commented Jun 14, 2019

Is there a way to merge a list of polygons?
I'm trying to merge a placed tilemap collision box in order to get less total polygons, but if we try and merge it by iterating the placed tiles collision polygons it wont happen as a proper merge is only possible with overlapping or adjacent tiles.

So is this a possible missing feature? Or am Doing things wrong?

@Xrayez
Copy link
Contributor Author

Xrayez commented Jun 14, 2019

@Dr4kzor yeah, there are some solutions to this but please make a new issue here at Godot repository to discuss your use case further and whether it's worth to implement it and I'll post possible solutions, ping me with @Xrayez directly in newly created issue to get my attention. already opened for you.

@ghost
Copy link

ghost commented Jun 14, 2019

Was wondering the same, but haven't had to approach the problem yet myself. I imagine it might be worked around by tacking on a point to one polygon that would create intersection before performing the union.

My near term use cases would mainly be on the tool end. Merging polygon geometries for colliders and such, as part of an offline optimization. Not there yet though, but my intuition is that it would be useful sometimes to have a direct method that would merge/union polygons that don't intersect.

I have designs for a much further out project (12-14 months) that would do many things with dynamic 2D geometry with polygons. So these 2D geometric additions so far are quite nice. 👍

When or if a more concrete need emerges I'll be sure to give you ping on it, or append it to any existing issue.

@Jiaming00
Copy link

To be honest, these features are much more powerful than I thought, ♥thank you very much♥

@gururise
Copy link
Contributor

gururise commented Nov 29, 2019

Thank you for the new features!!

Wondering did something happen to Geometry.transform_points_2d()? Wanted to use it to convert from local -> global coordinates on v3.2 Beta2:

image

EDIT: Just found PR #31865 where you removed this function and suggested the use of xform() from Transform2D. Thanks!

@Jiaming00
Copy link

@Xrayez Hi, sorry for disturbing you, I am using these APIs, I would like to ask if the arrays returned by these APIs are distributed in a tree structure (like this: http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Classes/PolyTree/_Body.htm)

@Xrayez
Copy link
Contributor Author

Xrayez commented Dec 25, 2019

@Jiaming00 the internal implementation does use PolyTree structure, but only to allow polygon/polyline operations when required. The output doesn't preserve the hierarchy of inner and outer polygons due to performance and for the sake of API simplicity. You can tell whether the output is a boundary or a hole by using is_polygon_clockwise method though.

You'll have better luck by checking out godot-clipper module if you need more control over the many parameters which Clipper provides.

@RichardEllicott
Copy link

is there any documentation of how i can get back the data you would have from a boolean as in a list of polygons that now represent the original shape with a hole in it?

I can't seem to find documentation on how to use this

i'm trying to get triangles back to draw csv data... so if i could define a 2D shape, then cut a hole in it resulting in triangles i can use as a mesh

@Xrayez
Copy link
Contributor Author

Xrayez commented Feb 26, 2021

@RichardEllicott Godot only provides core support for geometry operations. I think you're looking for a method for decomposing polygons with holes into triangles/convex. Unfortunately, Godot does not have robust methods to deal with such input (yet?).

If you're comfortable compiling the engine from source, you can look at the Goost project which provides more functionality revolving around geometry operations (and more). You may also consider opening a thread in Goost for discussion purposes, regardless of whether you're planning to use Goost.

i'm trying to get triangles back to draw csv data... so if i could define a 2D shape, then cut a hole in it resulting in triangles i can use as a mesh

I've recently implemented boolean nodes in 2D which are equivalent to CSG in Godot if you'd like to take a look: goostengine/goost#39. That includes PolyCollisionShape2D class (which is based on PolyShape2D base class responsible for building either triangle or convex meshes from boolean operation nodes PolyNode2D).

In any case, consider documenting your use case at godotengine/godot-proposals#913 as well if you'd like to see those features to be implemented in Godot.

EDIT: you can also download precompiled Godot+Goost binaries if you'd like to give those classes a try.

@RichardEllicott
Copy link

@RichardEllicott Godot only provides core support for geometry operations. I think you're looking for a method for decomposing polygons with holes into triangles/convex. Unfortunately, Godot does not have robust methods to deal with such input (yet?).

okay thanks for your reply and advice, i will take a look, i will post my reason why in the suggested proposal

@akien-mga akien-mga modified the milestones: 3.2, 3.3 Apr 20, 2021
@RichardEllicott
Copy link

RichardEllicott commented Oct 8, 2021

@RichardEllicott Godot only provides core support for geometry operations. I think you're looking for a method for decomposing polygons with holes into triangles/convex. Unfortunately, Godot does not have robust methods to deal with such input (yet?).

Hi, sorry to bother you. I was looking back at this old project that was stuck needing a boolean.

Nothing in the goost libarary seems to fit the bill. What you say "I think you're looking for a method for decomposing polygons with holes into triangles/convex" ...... that sounds logical i get that, i mean even decompose a polygon and a hole into concave shapes is also fine

i can't seem to find this, not aware of any other libraries? If we found it, it really should go in the engine as this is a task like triangulation, people don't want to do this manually

i'm reading this document, page 9, https://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf
and heard you talking about ear clipping. This sounds like the most logical approach.... it's called the same ear clipping you have mentioned in your proposals?

so i just wanted to run that by you in case you had anything to add, cheers

@Xrayez
Copy link
Contributor Author

Xrayez commented Oct 8, 2021

@RichardEllicott hi, I'm glad that you're still interested in this!

As I said, feel free to ask a question at Goost discussions, I'd appreciate this, because Godot is not currently capable of doing this kind of triangulation. And this PR is not really the place for providing complex solutions.

But you're probably looking for PolyDecomp2D singleton, you can pass both outer (boundary) and inner (holes) into polygons parameter.

If you need a more elaborate response, again feel free to ask a question.

i can't seem to find this, not aware of any other libraries? If we found it, it really should go in the engine as this is a task like triangulation, people don't want to do this manually

I share your vision and desire, but unlike in Goost, I cannot decide what goes into Godot core.

@Xrayez Xrayez changed the title Expose 2D polygon boolean operations in Geometry singleton Add 2D polygon boolean operations in Geometry singleton Feb 2, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Implement Clipper external library wrapper to GDScript
9 participants