Skip to content

Conversation

@tigercosmos
Copy link
Collaborator

The PR is a prototype of trapezoidal decomposition for the further support of polygon operations.

@tigercosmos tigercosmos marked this pull request as draft December 26, 2025 14:12
Copy link
Collaborator Author

@tigercosmos tigercosmos left a comment

Choose a reason for hiding this comment

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

@yungyuc Could you take a look and discuss if the current PR approach is correct? I want to add trapezoidal decomposition with further support of polygon boolean operations. However, after studying a while, I am still confusing what to do next.

Comment on lines +987 to +994
struct Trapezoid
{
value_type top_y; ///< Y-coordinate of the top horizontal edge
value_type bottom_y; ///< Y-coordinate of the bottom horizontal edge
segment_type left_edge; ///< Left boundary segment of the trapezoid
segment_type right_edge; ///< Right boundary segment of the trapezoid
size_t source_polygon; ///< ID of the polygon this trapezoid came from
};
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Struct for trapazoid

Copy link
Member

Choose a reason for hiding this comment

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

Trapezoid should be taken outside to be a normal class (with the name Trapezoid3d, class declaration, private data with prefix, and accessors). It will be so useful that you may just create TrapezoidPad using this PR.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Got it. Thanks.

* @throws std::out_of_range if polygon_id is invalid
* @throws std::invalid_argument if polygon has fewer than 3 nodes
*/
std::vector<Trapezoid> const & decompose_polygon(size_t polygon_id);
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The API for trapazoidal decomposition.

Copy link
Member

Choose a reason for hiding this comment

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

Create a helper class TrapezoidalDecomposition (or a more accurate name you like), and rename PolygonPad::decompose_polygon() to PolygonPad::decompose_to_trapezoid() for accurate semantics. Call the helper class from decompose_to_trapezoid().

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Got it.

* @param p2 Second polygon
* @return Vector of polygons forming the union
*/
std::vector<polygon_type> boolean_union(polygon_type const & p1, polygon_type const & p2);
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The part I am not sure. Do we want to have operation APIs in the PolygonPad? The operation is between polygon and polygon, or polygon pad or polygon pad?

Copy link
Member

Choose a reason for hiding this comment

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

Good point. Area Boolean is set Boolean, so it operates between sets, so PolygonPad and PolygonPad.

Our goal is volumetric Boolean, but we need to make the area Boolean very clear before moving forward.

Copy link
Member

Choose a reason for hiding this comment

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

Create helper classes AreaBooleanUnion, AreaBooleanIntersection, and AreaBooleanDifference for the 3 operations you are prototyping here.

Use naive implementation at this point. Add unit tests to make sure they have Python interface and do not crash. Leave TODO comments in the tests saying results are not confirmed to be correct and the correctness is left for a follow-up PR.

This is good progress.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I see. Thanks!

@yungyuc yungyuc added the geometry Geometry entities and manipulation label Dec 27, 2025
Copy link
Member

@yungyuc yungyuc left a comment

Choose a reason for hiding this comment

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

Did you use any AI tool to help? I am curious about it. If you did please specify.

This is good progress. Points to address:

  • Make a normal (non-nested) Trapezoid3d and TrapezoidPad.
  • Do not use hash table and do not use hash table to vectors.
  • Create a helper class TrapezoidalDecomposition (or a more accurate name you like).
  • Rename PolygonPad::decompose_polygon() to PolygonPad::decompose_to_trapezoid() for accurate semantics.
  • Create helper classes AreaBooleanUnion, AreaBooleanIntersection, and AreaBooleanDifference for the 3 operations.
  • For the area Boolean, add unit tests to make sure they have Python interface and do not crash. Leave TODO comments in the tests saying results are not confirmed to be correct and the correctness is left for a follow-up PR.

Comment on lines +987 to +994
struct Trapezoid
{
value_type top_y; ///< Y-coordinate of the top horizontal edge
value_type bottom_y; ///< Y-coordinate of the bottom horizontal edge
segment_type left_edge; ///< Left boundary segment of the trapezoid
segment_type right_edge; ///< Right boundary segment of the trapezoid
size_t source_polygon; ///< ID of the polygon this trapezoid came from
};
Copy link
Member

Choose a reason for hiding this comment

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

Trapezoid should be taken outside to be a normal class (with the name Trapezoid3d, class declaration, private data with prefix, and accessors). It will be so useful that you may just create TrapezoidPad using this PR.

// - Point location query structure
// - Integration with node lists for synchronization
/// Cache of trapezoidal decompositions, keyed by polygon ID. Computed lazily on first access.
std::unordered_map<size_t, std::vector<Trapezoid>> m_decompositions;
Copy link
Member

Choose a reason for hiding this comment

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

Do not use hash table. Hash table is the last resort when (1) you run out of ideas or (2) you run out of time.

A has table to vector is even worth: tons of dynamic memory allocation. It is bound to be slow.

This is why you need TrapezoidPad.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Got it.

* @throws std::out_of_range if polygon_id is invalid
* @throws std::invalid_argument if polygon has fewer than 3 nodes
*/
std::vector<Trapezoid> const & decompose_polygon(size_t polygon_id);
Copy link
Member

Choose a reason for hiding this comment

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

Create a helper class TrapezoidalDecomposition (or a more accurate name you like), and rename PolygonPad::decompose_polygon() to PolygonPad::decompose_to_trapezoid() for accurate semantics. Call the helper class from decompose_to_trapezoid().

* @param p2 Second polygon
* @return Vector of polygons forming the union
*/
std::vector<polygon_type> boolean_union(polygon_type const & p1, polygon_type const & p2);
Copy link
Member

Choose a reason for hiding this comment

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

Good point. Area Boolean is set Boolean, so it operates between sets, so PolygonPad and PolygonPad.

Our goal is volumetric Boolean, but we need to make the area Boolean very clear before moving forward.

* @param p2 Second polygon
* @return Vector of polygons forming the union
*/
std::vector<polygon_type> boolean_union(polygon_type const & p1, polygon_type const & p2);
Copy link
Member

Choose a reason for hiding this comment

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

Create helper classes AreaBooleanUnion, AreaBooleanIntersection, and AreaBooleanDifference for the 3 operations you are prototyping here.

Use naive implementation at this point. Add unit tests to make sure they have Python interface and do not crash. Leave TODO comments in the tests saying results are not confirmed to be correct and the correctness is left for a follow-up PR.

This is good progress.

@tigercosmos
Copy link
Collaborator Author

@yungyuc Yes, I used AI and spent around 5–6 hours exploring how to implement the trapezoidal decomposition. In the end, I wasn’t happy with the outcome and wasn’t confident about the design direction. I removed most of the implementation, kept what seemed to be the most reasonable interface, and opened this PR for discussion.

@yungyuc
Copy link
Member

yungyuc commented Jan 1, 2026

@yungyuc Yes, I used AI and spent around 5–6 hours exploring how to implement the trapezoidal decomposition. In the end, I wasn’t happy with the outcome and wasn’t confident about the design direction. I removed most of the implementation, kept what seemed to be the most reasonable interface, and opened this PR for discussion.

6 hours on the trapezoidal algorithm are not too many, but spending time to study it and then prototype incrementally will be more productive. Dividing tasks is an art that we cannot count on tools already.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

geometry Geometry entities and manipulation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants