1515from __future__ import annotations
1616
1717import math
18+ from typing import NamedTuple
1819
19- Point = tuple [float , float ]
20+
21+ class Point (NamedTuple ):
22+ """
23+ A 2D point with real-valued coordinates.
24+
25+ >>> Point(0.0, 0.0)
26+ Point(x=0.0, y=0.0)
27+ >>> Point(1.5, -2.0)
28+ Point(x=1.5, y=-2.0)
29+ """
30+
31+ x : float
32+ y : float
2033
2134
2235def cross_product (origin : Point , point_a : Point , point_b : Point ) -> float :
@@ -29,30 +42,30 @@ def cross_product(origin: Point, point_a: Point, point_b: Point) -> float:
2942 < 0 : Clockwise turn (right turn)
3043 = 0 : Collinear points
3144
32- >>> cross_product((0.0, 0.0), (1.0, 0.0), (1.0, 1.0))
45+ >>> cross_product(Point (0.0, 0.0), Point (1.0, 0.0), Point (1.0, 1.0))
3346 1.0
34- >>> cross_product((0.0, 0.0), (1.0, 1.0), (1.0, 0.0))
47+ >>> cross_product(Point (0.0, 0.0), Point (1.0, 1.0), Point (1.0, 0.0))
3548 -1.0
36- >>> cross_product((0.0, 0.0), (1.0, 1.0), (2.0, 2.0))
49+ >>> cross_product(Point (0.0, 0.0), Point (1.0, 1.0), Point (2.0, 2.0))
3750 0.0
3851 """
39- return (point_a [ 0 ] - origin [ 0 ] ) * (point_b [ 1 ] - origin [ 1 ] ) - (
40- point_a [ 1 ] - origin [ 1 ]
41- ) * ( point_b [ 0 ] - origin [ 0 ])
52+ return (point_a . x - origin . x ) * (point_b . y - origin . y ) - ( point_a . y - origin . y ) * (
53+ point_b . x - origin . x
54+ )
4255
4356
4457def distance_squared (point_a : Point , point_b : Point ) -> float :
4558 """
4659 Compute the squared Euclidean distance between point_a and point_b.
4760
48- >>> distance_squared((0.0, 0.0), (3.0, 4.0))
61+ >>> distance_squared(Point (0.0, 0.0), Point (3.0, 4.0))
4962 25.0
50- >>> distance_squared((1.0, 1.0), (1.0, 1.0))
63+ >>> distance_squared(Point (1.0, 1.0), Point (1.0, 1.0))
5164 0.0
52- >>> distance_squared((-1.0, -1.0), (2.0, 3.0))
65+ >>> distance_squared(Point (-1.0, -1.0), Point (2.0, 3.0))
5366 25.0
5467 """
55- return (point_a [ 0 ] - point_b [ 0 ] ) ** 2 + (point_a [ 1 ] - point_b [ 1 ] ) ** 2
68+ return (point_a . x - point_b . x ) ** 2 + (point_a . y - point_b . y ) ** 2
5669
5770
5871def convex_hull (points : list [Point ]) -> list [Point ]:
@@ -63,14 +76,20 @@ def convex_hull(points: list[Point]) -> list[Point]:
6376 Time Complexity: O(n log n) where n is the number of points.
6477 Space Complexity: O(n)
6578
66- >>> convex_hull([(0.0, 0.0), (1.0, 1.0)])
67- [(0.0, 0.0), (1.0, 1.0)]
68- >>> convex_hull([(0.0, 0.0), (3.0, 0.0), (3.0, 3.0), (0.0, 3.0), (1.0, 1.0)])
69- [(0.0, 0.0), (3.0, 0.0), (3.0, 3.0), (0.0, 3.0)]
70- >>> convex_hull([(0.0, 0.0), (1.0, 1.0), (2.0, 2.0)])
71- [(0.0, 0.0), (2.0, 2.0)]
72- >>> convex_hull([(1.0, 1.0)])
73- [(1.0, 1.0)]
79+ >>> convex_hull([Point(0.0, 0.0), Point(1.0, 1.0)])
80+ [Point(x=0.0, y=0.0), Point(x=1.0, y=1.0)]
81+ >>> convex_hull([
82+ ... Point(0.0, 0.0),
83+ ... Point(3.0, 0.0),
84+ ... Point(3.0, 3.0),
85+ ... Point(0.0, 3.0),
86+ ... Point(1.0, 1.0),
87+ ... ])
88+ [Point(x=0.0, y=0.0), Point(x=3.0, y=0.0), Point(x=3.0, y=3.0), Point(x=0.0, y=3.0)]
89+ >>> convex_hull([Point(0.0, 0.0), Point(1.0, 1.0), Point(2.0, 2.0)])
90+ [Point(x=0.0, y=0.0), Point(x=2.0, y=2.0)]
91+ >>> convex_hull([Point(1.0, 1.0)])
92+ [Point(x=1.0, y=1.0)]
7493 """
7594 unique_points = sorted (set (points ))
7695 if len (unique_points ) <= 1 :
@@ -109,24 +128,34 @@ def rotating_calipers(points: list[Point]) -> tuple[float, tuple[Point, Point]]:
109128 Raises:
110129 ValueError: If fewer than 2 points are provided.
111130
112- >>> points = [(0.0, 0.0), (3.0, 0.0), (3.0, 4.0), (0.0, 4.0)]
131+ >>> points = [
132+ ... Point(0.0, 0.0),
133+ ... Point(3.0, 0.0),
134+ ... Point(3.0, 4.0),
135+ ... Point(0.0, 4.0),
136+ ... ]
113137 >>> max_dist, pair = rotating_calipers(points)
114138 >>> max_dist
115139 5.0
116140 >>> pair in [
117- ... ((0.0, 0.0), (3.0, 4.0)),
118- ... ((3.0, 4.0), (0.0, 0.0)),
119- ... ((3.0, 0.0), (0.0, 4.0)),
120- ... ((0.0, 4.0), (3.0, 0.0)),
141+ ... (Point (0.0, 0.0), Point (3.0, 4.0)),
142+ ... (Point (3.0, 4.0), Point (0.0, 0.0)),
143+ ... (Point (3.0, 0.0), Point (0.0, 4.0)),
144+ ... (Point (0.0, 4.0), Point (3.0, 0.0)),
121145 ... ]
122146 True
123- >>> rotating_calipers([(0.0, 0.0), (0.0, 5.0)])
124- (5.0, ((0.0, 0.0), (0.0, 5.0)))
125- >>> rotating_calipers([(1.0, 1.0), (1.0, 1.0)])
126- (0.0, ((1.0, 1.0), (1.0, 1.0)))
127- >>> rotating_calipers([(0.0, 0.0), (1.0, 1.0), (2.0, 2.0), (3.0, 3.0)])[0]
147+ >>> rotating_calipers([Point(0.0, 0.0), Point(0.0, 5.0)])
148+ (5.0, (Point(x=0.0, y=0.0), Point(x=0.0, y=5.0)))
149+ >>> rotating_calipers([Point(1.0, 1.0), Point(1.0, 1.0)])
150+ (0.0, (Point(x=1.0, y=1.0), Point(x=1.0, y=1.0)))
151+ >>> rotating_calipers([
152+ ... Point(0.0, 0.0),
153+ ... Point(1.0, 1.0),
154+ ... Point(2.0, 2.0),
155+ ... Point(3.0, 3.0),
156+ ... ])[0]
128157 4.242640687119285
129- >>> rotating_calipers([(1.0, 1.0)])
158+ >>> rotating_calipers([Point (1.0, 1.0)])
130159 Traceback (most recent call last):
131160 ...
132161 ValueError: At least 2 points are required to compute polygon diameter.
@@ -140,7 +169,7 @@ def rotating_calipers(points: list[Point]) -> tuple[float, tuple[Point, Point]]:
140169 if hull_size == 1 :
141170 return 0.0 , (hull [0 ], hull [0 ])
142171 if hull_size == 2 :
143- return math .hypot (hull [0 ][ 0 ] - hull [1 ][ 0 ] , hull [0 ][ 1 ] - hull [1 ][ 1 ] ), (
172+ return math .hypot (hull [0 ]. x - hull [1 ]. x , hull [0 ]. y - hull [1 ]. y ), (
144173 hull [0 ],
145174 hull [1 ],
146175 )
0 commit comments