Skip to content

Commit 4997bae

Browse files
committed
Reduce the complexity of other/graham_scan.py
1 parent db5215f commit 4997bae

File tree

1 file changed

+76
-74
lines changed

1 file changed

+76
-74
lines changed

other/graham_scan.py

Lines changed: 76 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,82 @@
1313
from math import atan2, degrees
1414
from sys import maxsize
1515

16+
# traversal from the lowest and the most left point in anti-clockwise direction
17+
# if direction gets right, the previous point is not the convex hull.
18+
class Direction(Enum):
19+
left = 1
20+
straight = 2
21+
right = 3
22+
23+
24+
def angle_comparer(point: tuple[int, int], minx: int, miny: int) -> float:
25+
"""Return the angle toward to point from (minx, miny)
26+
27+
:param point: The target point
28+
minx: The starting point's x
29+
miny: The starting point's y
30+
:return: the angle
31+
32+
Examples:
33+
>>> angle_comparer((1,1), 0, 0)
34+
45.0
35+
36+
>>> angle_comparer((100,1), 10, 10)
37+
-5.710593137499642
38+
39+
>>> angle_comparer((5,5), 2, 3)
40+
33.690067525979785
41+
"""
42+
# sort the points accorgind to the angle from the lowest and the most left point
43+
x = point[0]
44+
y = point[1]
45+
angle = degrees(atan2(y - miny, x - minx))
46+
return angle
47+
48+
49+
def check_direction(
50+
starting: tuple[int, int], via: tuple[int, int], target: tuple[int, int]
51+
) -> Direction:
52+
"""Return the direction toward to the line from via to target from starting
53+
54+
:param starting: The starting point
55+
via: The via point
56+
target: The target point
57+
:return: the Direction
58+
59+
Examples:
60+
>>> check_direction((1,1), (2,2), (3,3))
61+
Direction.straight
62+
63+
>>> check_direction((60,1), (-50,199), (30,2))
64+
Direction.left
65+
66+
>>> check_direction((0,0), (5,5), (10,0))
67+
Direction.right
68+
"""
69+
x0, y0 = starting
70+
x1, y1 = via
71+
x2, y2 = target
72+
via_angle = degrees(atan2(y1 - y0, x1 - x0))
73+
if via_angle < 0:
74+
via_angle += 360
75+
target_angle = degrees(atan2(y2 - y0, x2 - x0))
76+
if target_angle < 0:
77+
target_angle += 360
78+
# t-
79+
# \ \
80+
# \ v
81+
# \|
82+
# s
83+
# via_angle is always lower than target_angle, if direction is left.
84+
# If they are same, it means they are on a same line of convex hull.
85+
if target_angle > via_angle:
86+
return Direction.left
87+
elif target_angle == via_angle:
88+
return Direction.straight
89+
else:
90+
return Direction.right
91+
1692

1793
def graham_scan(points: list[tuple[int, int]]) -> list[tuple[int, int]]:
1894
"""Pure implementation of graham scan algorithm in Python
@@ -57,86 +133,12 @@ def graham_scan(points: list[tuple[int, int]]) -> list[tuple[int, int]]:
57133
# remove the lowest and the most left point from points for preparing for sort
58134
points.pop(minidx)
59135

60-
def angle_comparer(point: tuple[int, int], minx: int, miny: int) -> float:
61-
"""Return the angle toward to point from (minx, miny)
62-
63-
:param point: The target point
64-
minx: The starting point's x
65-
miny: The starting point's y
66-
:return: the angle
67-
68-
Examples:
69-
>>> angle_comparer((1,1), 0, 0)
70-
45.0
71-
72-
>>> angle_comparer((100,1), 10, 10)
73-
-5.710593137499642
74-
75-
>>> angle_comparer((5,5), 2, 3)
76-
33.690067525979785
77-
"""
78-
# sort the points accorgind to the angle from the lowest and the most left point
79-
x = point[0]
80-
y = point[1]
81-
angle = degrees(atan2(y - miny, x - minx))
82-
return angle
83-
84136
sorted_points = sorted(points, key=lambda point: angle_comparer(point, minx, miny))
85137
# This insert actually costs complexity,
86138
# and you should instead add (minx, miny) into stack later.
87139
# I'm using insert just for easy understanding.
88140
sorted_points.insert(0, (minx, miny))
89141

90-
# traversal from the lowest and the most left point in anti-clockwise direction
91-
# if direction gets right, the previous point is not the convex hull.
92-
class Direction(Enum):
93-
left = 1
94-
straight = 2
95-
right = 3
96-
97-
def check_direction(
98-
starting: tuple[int, int], via: tuple[int, int], target: tuple[int, int]
99-
) -> Direction:
100-
"""Return the direction toward to the line from via to target from starting
101-
102-
:param starting: The starting point
103-
via: The via point
104-
target: The target point
105-
:return: the Direction
106-
107-
Examples:
108-
>>> check_direction((1,1), (2,2), (3,3))
109-
Direction.straight
110-
111-
>>> check_direction((60,1), (-50,199), (30,2))
112-
Direction.left
113-
114-
>>> check_direction((0,0), (5,5), (10,0))
115-
Direction.right
116-
"""
117-
x0, y0 = starting
118-
x1, y1 = via
119-
x2, y2 = target
120-
via_angle = degrees(atan2(y1 - y0, x1 - x0))
121-
if via_angle < 0:
122-
via_angle += 360
123-
target_angle = degrees(atan2(y2 - y0, x2 - x0))
124-
if target_angle < 0:
125-
target_angle += 360
126-
# t-
127-
# \ \
128-
# \ v
129-
# \|
130-
# s
131-
# via_angle is always lower than target_angle, if direction is left.
132-
# If they are same, it means they are on a same line of convex hull.
133-
if target_angle > via_angle:
134-
return Direction.left
135-
elif target_angle == via_angle:
136-
return Direction.straight
137-
else:
138-
return Direction.right
139-
140142
stack: deque[tuple[int, int]] = deque()
141143
stack.append(sorted_points[0])
142144
stack.append(sorted_points[1])

0 commit comments

Comments
 (0)