Add Andrew's Monotone Chain convex hull algorithm - #15165
Conversation
28807c6 to
c64c1d4
Compare
There was a problem hiding this comment.
🟢 Approval recommended
The new module is self-contained, correctly implements the monotone chain approach, and includes adequate doctest coverage plus references consistent with existing geometry modules.
Pull request overview
Adds a new computational-geometry algorithm module implementing Andrew’s Monotone Chain method for computing the 2D convex hull, consistent with the repository’s educational, doctest-driven style.
Changes:
- Introduces
geometry/monotone_chain.pyimplementingmonotone_chain()with aPointrepresentation and a cross-product orientation helper. - Adds thorough doctest examples covering empty input, duplicates, interior points, and collinear inputs.
- Documents time/space complexity and provides a Wikipedia + paper reference.
File summaries
| File | Description |
|---|---|
| geometry/monotone_chain.py | New convex hull implementation (Andrew’s Monotone Chain) with supporting Point type, orientation helper, and doctests. |
Review details
- Files reviewed: 1/1 changed files
- Comments generated: 0
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| >>> monotone_chain(collinear_points) | ||
| [Point(x=0, y=0), Point(x=3, y=3)] | ||
| """ | ||
| unique_sorted_points = sorted(set(points)) |
There was a problem hiding this comment.
Smart use of set() to deduplicate points before sorting — this
correctly handles the duplicate points test case shown in the
docstring (triangle_with_duplicates).
One thing worth noting: set() on a NamedTuple works because
NamedTuples are hashable by default, but this behaviour should
be mentioned in the docstring for clarity since it's a subtle
dependency on Point being hashable.
| upper_hull.append(candidate_point) | ||
|
|
||
| # Omit the last point of each half because it is repeated at the ends | ||
| return lower_hull[:-1] + upper_hull[:-1] |
There was a problem hiding this comment.
The slicing to omit the last point of each half is correct —
without it the first and last points of the full hull would be
duplicated where lower and upper hulls meet.
Worth adding a brief inline comment explaining why both hulls
drop their last element, since this is the most non-obvious
part of the algorithm for readers unfamiliar with it:
Drop last point of each hull to avoid duplicating
the leftmost and rightmost points where hulls meet
return lower_hull[:-1] + upper_hull[:-1]
| >>> cross_product_direction(Point(0, 0), Point(1, 1), Point(2, 2)) | ||
| 0 | ||
| """ | ||
| return (point_a.x - origin.x) * (point_b.y - origin.y) - (point_a.y - origin.y) * ( |
There was a problem hiding this comment.
The cross product formula is correct and well-documented above.
Consider adding a one-line summary of what the return value
means directly in the docstring Examples section for quick
reference:
Positive = counter-clockwise, Negative = clockwise, Zero = collinear
This would help readers verify the doctests on lines 51-56
without having to re-read the full orientation explanation.
Describe your change:
Add Andrew's Monotone Chain algorithm for computing the 2D convex hull of a set of points in O(n log n) time.
Checklist: