Spaces:
Sleeping
Sleeping
Create courtCoordinates.py
Browse files- courtCoordinates.py +438 -0
courtCoordinates.py
ADDED
@@ -0,0 +1,438 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import pandas as pd
|
2 |
+
import numpy as np
|
3 |
+
|
4 |
+
class CourtCoordinates:
|
5 |
+
'''
|
6 |
+
Stores court dimensions and calculates the (x,y,z) coordinates of the outside perimeter,
|
7 |
+
three point line, backboard, hoop, and free throw line.
|
8 |
+
The default dimensions of a men's NCAA court.
|
9 |
+
'''
|
10 |
+
def __init__(self):
|
11 |
+
self.court_length = 94 # the court is 94 feet long
|
12 |
+
self.court_width = 50 # the court is 50 feet wide
|
13 |
+
self.hoop_loc_x = 25 # the hoop is at the center of the court length-wise
|
14 |
+
self.hoop_loc_y = 4.25 # the center of the hoop is 63 inches from the baseline
|
15 |
+
self.hoop_loc_z = 10 # the hoop is 10 feet off the ground
|
16 |
+
self.hoop_radius = .75
|
17 |
+
self.three_arc_distance = 23.75 # the NCAA men's three-point arc is 22ft and 1.75in from the hoop center
|
18 |
+
self.three_straight_distance = 22 # the NCAA men's three-point straight section is 21ft 8in from the hoop center
|
19 |
+
self.three_straight_length = 8.89 # the NCAA men's three-point straight section length is 8ft and 10.75in
|
20 |
+
self.backboard_width = 6 # backboard is 6ft wide
|
21 |
+
self.backboard_height = 4 # backboard is 4ft tall
|
22 |
+
self.backboard_baseline_offset = 3 # backboard is 3ft from the baseline
|
23 |
+
self.backboard_floor_offset = 9 # backboard is 9ft from the floor
|
24 |
+
self.free_throw_line_distance = 15 # distance from the free throw line to the backboard
|
25 |
+
|
26 |
+
@staticmethod
|
27 |
+
def calculate_quadratic_values(a, b, c):
|
28 |
+
'''
|
29 |
+
Given values a, b, and c,
|
30 |
+
the function returns the output of the quadratic formula
|
31 |
+
'''
|
32 |
+
x1 = (-b + (b ** 2 - 4 * a * c) ** 0.5) / (2 * a)
|
33 |
+
x2 = (-b - (b ** 2 - 4 * a * c) ** 0.5) / (2 * a)
|
34 |
+
|
35 |
+
return x1, x2
|
36 |
+
|
37 |
+
def __get_court_perimeter_coordinates(self):
|
38 |
+
'''
|
39 |
+
Returns coordinates of full court perimeter lines. A court that is 50 feet wide and 94 feet long
|
40 |
+
In shot chart data, each foot is represented by 10 units.
|
41 |
+
'''
|
42 |
+
width = self.court_width
|
43 |
+
length = self.court_length
|
44 |
+
court_perimeter_bounds = [
|
45 |
+
[0, 0, 0],
|
46 |
+
[width, 0, 0],
|
47 |
+
[width, length, 0],
|
48 |
+
[0, length, 0],
|
49 |
+
[0, 0, 0]
|
50 |
+
]
|
51 |
+
|
52 |
+
court_df = pd.DataFrame(court_perimeter_bounds, columns=['x','y','z'])
|
53 |
+
court_df['line_group'] = 'outside_perimeter'
|
54 |
+
court_df['color'] = 'court'
|
55 |
+
|
56 |
+
return court_df
|
57 |
+
|
58 |
+
def __get_half_court_coordinates(self):
|
59 |
+
'''
|
60 |
+
Returns coordinates for the half court line.
|
61 |
+
'''
|
62 |
+
width = self.court_width
|
63 |
+
half_length = self.court_length / 2
|
64 |
+
circle_radius = 6
|
65 |
+
circle_radius2 = 2
|
66 |
+
circle_center = [width / 2, half_length, 0]
|
67 |
+
circle_points = []
|
68 |
+
circle_points2 = []
|
69 |
+
num_points = 400 # Number of points to approximate the circle
|
70 |
+
for i in range(num_points):
|
71 |
+
angle = 2 * np.pi * i / num_points
|
72 |
+
x = circle_center[0] + circle_radius * np.cos(angle)
|
73 |
+
y = circle_center[1] + circle_radius * np.sin(angle)
|
74 |
+
circle_points.append([x, y, circle_center[2]])
|
75 |
+
for i in range(num_points):
|
76 |
+
angle = 2 * np.pi * i / num_points
|
77 |
+
x = circle_center[0] + circle_radius2 * np.cos(angle)
|
78 |
+
y = circle_center[1] + circle_radius2 * np.sin(angle)
|
79 |
+
circle_points2.append([x, y, circle_center[2]])
|
80 |
+
|
81 |
+
# Example radius of the free throw circle, adjust as needed
|
82 |
+
|
83 |
+
half_court_bounds = [[0, half_length, 0], [width, half_length, 0]]
|
84 |
+
|
85 |
+
half_df = pd.DataFrame(half_court_bounds, columns=['x','y','z'])
|
86 |
+
circle_df = pd.DataFrame(circle_points, columns=['x', 'y', 'z'])
|
87 |
+
circle_df['line_group'] = f'free_throw_circle'
|
88 |
+
circle_df['color'] = 'court'
|
89 |
+
|
90 |
+
circle_df2 = pd.DataFrame(circle_points2, columns=['x', 'y', 'z'])
|
91 |
+
circle_df2['line_group'] = f'free_throw_circle'
|
92 |
+
circle_df2['color'] = 'court'
|
93 |
+
|
94 |
+
half_df['line_group'] = 'half_court'
|
95 |
+
half_df['color'] = 'court'
|
96 |
+
|
97 |
+
return pd.concat([half_df, circle_df,circle_df2])
|
98 |
+
|
99 |
+
def __get_backboard_coordinates(self, loc):
|
100 |
+
'''
|
101 |
+
Returns coordinates of the backboard on both ends of the court
|
102 |
+
A backboard is 6 feet wide, 4 feet tall
|
103 |
+
Also adds a smaller rectangle inside the backboard starting at hoop height
|
104 |
+
'''
|
105 |
+
|
106 |
+
backboard_start = (self.court_width / 2) - (self.backboard_width / 2)
|
107 |
+
backboard_end = (self.court_width / 2) + (self.backboard_width / 2)
|
108 |
+
height = self.backboard_height
|
109 |
+
floor_offset = self.backboard_floor_offset
|
110 |
+
|
111 |
+
if loc == 'far':
|
112 |
+
offset = self.backboard_baseline_offset
|
113 |
+
elif loc == 'near':
|
114 |
+
offset = self.court_length - self.backboard_baseline_offset
|
115 |
+
|
116 |
+
# Backboard coordinates
|
117 |
+
backboard_bounds = [
|
118 |
+
[backboard_start, offset, floor_offset],
|
119 |
+
[backboard_start, offset, floor_offset + height],
|
120 |
+
[backboard_end, offset, floor_offset + height],
|
121 |
+
[backboard_end, offset, floor_offset],
|
122 |
+
[backboard_start, offset, floor_offset]
|
123 |
+
]
|
124 |
+
|
125 |
+
# Coordinates of the smaller rectangle
|
126 |
+
smaller_rect_width = 1.5 # Width of the smaller rectangle in feet
|
127 |
+
smaller_rect_height = 1 # Height of the smaller rectangle in feet
|
128 |
+
hoop_height = self.hoop_loc_z # Height of the hoop
|
129 |
+
|
130 |
+
# Smaller rectangle coordinates
|
131 |
+
smaller_rect_start_x = backboard_start + (self.backboard_width / 2) - (smaller_rect_width / 2)
|
132 |
+
smaller_rect_end_x = backboard_start + (self.backboard_width / 2) + (smaller_rect_width / 2)
|
133 |
+
smaller_rect_y = offset # Y coordinate same as the backboard
|
134 |
+
|
135 |
+
smaller_rect_bounds = [
|
136 |
+
[smaller_rect_start_x, offset, hoop_height],
|
137 |
+
[smaller_rect_start_x, offset, hoop_height + smaller_rect_height],
|
138 |
+
[smaller_rect_end_x, offset, hoop_height + smaller_rect_height],
|
139 |
+
[smaller_rect_end_x, offset, hoop_height],
|
140 |
+
[smaller_rect_start_x, offset, hoop_height]
|
141 |
+
]
|
142 |
+
|
143 |
+
# Combine coordinates into DataFrames
|
144 |
+
backboard_df = pd.DataFrame(backboard_bounds, columns=['x', 'y', 'z'])
|
145 |
+
backboard_df['line_group'] = f'{loc}_backboard'
|
146 |
+
backboard_df['color'] = 'backboard'
|
147 |
+
|
148 |
+
smaller_rect_df = pd.DataFrame(smaller_rect_bounds, columns=['x', 'y', 'z'])
|
149 |
+
smaller_rect_df['line_group'] = f'{loc}_smaller_rectangle'
|
150 |
+
smaller_rect_df['color'] = 'backboard' # Set color for smaller rectangle
|
151 |
+
|
152 |
+
return pd.concat([backboard_df, smaller_rect_df])
|
153 |
+
|
154 |
+
|
155 |
+
def __get_three_point_coordinates(self, loc):
|
156 |
+
'''
|
157 |
+
Returns coordinates of the three point line on both ends of the court
|
158 |
+
Given that the ncaa men's three point line is 22ft and 1.5in from the center of the hoop
|
159 |
+
'''
|
160 |
+
|
161 |
+
# init values
|
162 |
+
hoop_loc_x, hoop_loc_y = self.hoop_loc_x, self.hoop_loc_y
|
163 |
+
strt_dst_start = (self.court_width/2) - self.three_straight_distance
|
164 |
+
strt_dst_end = (self.court_width/2) + self.three_straight_distance
|
165 |
+
strt_len = self.three_straight_length
|
166 |
+
arc_dst = self.three_arc_distance
|
167 |
+
|
168 |
+
start_straight = [
|
169 |
+
[strt_dst_start,0,0],
|
170 |
+
[strt_dst_start,strt_len,0]
|
171 |
+
]
|
172 |
+
end_straight = [
|
173 |
+
[strt_dst_end,strt_len,0],
|
174 |
+
[strt_dst_end,0,0]
|
175 |
+
]
|
176 |
+
line_coordinates = []
|
177 |
+
|
178 |
+
if loc == 'near':
|
179 |
+
crt_len = self.court_length
|
180 |
+
hoop_loc_y = crt_len - hoop_loc_y
|
181 |
+
start_straight = [[strt_dst_start,crt_len,0],[strt_dst_start,crt_len-strt_len,0]]
|
182 |
+
end_straight = [[strt_dst_end,crt_len-strt_len,0], [strt_dst_end,crt_len,0]]
|
183 |
+
|
184 |
+
# drawing the three point line
|
185 |
+
line_coordinates.extend(start_straight)
|
186 |
+
|
187 |
+
a = 1
|
188 |
+
b = -2 * hoop_loc_y
|
189 |
+
d = arc_dst
|
190 |
+
for x_coord in np.linspace(int(strt_dst_start), int(strt_dst_end), 100):
|
191 |
+
c = hoop_loc_y ** 2 + (x_coord - 25) ** 2 - (d) ** 2
|
192 |
+
|
193 |
+
y1, y2 = self.calculate_quadratic_values(a, b, c)
|
194 |
+
if loc == 'far':
|
195 |
+
y_coord = y1
|
196 |
+
if loc == 'near':
|
197 |
+
y_coord = y2
|
198 |
+
|
199 |
+
line_coordinates.append([x_coord, y_coord, 0])
|
200 |
+
|
201 |
+
line_coordinates.extend(end_straight)
|
202 |
+
|
203 |
+
far_three_df = pd.DataFrame(line_coordinates, columns=['x', 'y', 'z'])
|
204 |
+
far_three_df['line_group'] = f'{loc}_three'
|
205 |
+
far_three_df['color'] = 'court'
|
206 |
+
|
207 |
+
return far_three_df
|
208 |
+
|
209 |
+
def __get_hoop_coordinates(self, loc):
|
210 |
+
'''
|
211 |
+
Returns the hoop coordinates of the far/near hoop
|
212 |
+
'''
|
213 |
+
hoop_coordinates_top_half = []
|
214 |
+
hoop_coordinates_bottom_half = []
|
215 |
+
|
216 |
+
hoop_loc_x, hoop_loc_y, hoop_loc_z = (self.hoop_loc_x, self.hoop_loc_y, self.hoop_loc_z)
|
217 |
+
|
218 |
+
if loc == 'near':
|
219 |
+
hoop_loc_y = self.court_length - hoop_loc_y
|
220 |
+
|
221 |
+
hoop_radius = self.hoop_radius
|
222 |
+
hoop_min_x, hoop_max_x = (hoop_loc_x - hoop_radius, hoop_loc_x + hoop_radius)
|
223 |
+
hoop_step = 0.1
|
224 |
+
|
225 |
+
a = 1
|
226 |
+
b = -2 * hoop_loc_y
|
227 |
+
for hoop_coord_x in np.arange(hoop_min_x, hoop_max_x + hoop_step/2, hoop_step):
|
228 |
+
c = hoop_loc_y ** 2 + (hoop_loc_x - round(hoop_coord_x,2)) ** 2 - hoop_radius ** 2
|
229 |
+
hoop_coord_y1, hoop_coord_y2 = self.calculate_quadratic_values(a, b, c)
|
230 |
+
|
231 |
+
hoop_coordinates_top_half.append([hoop_coord_x, hoop_coord_y1, hoop_loc_z])
|
232 |
+
hoop_coordinates_bottom_half.append([hoop_coord_x, hoop_coord_y2, hoop_loc_z])
|
233 |
+
|
234 |
+
hoop_coordinates_df = pd.DataFrame(hoop_coordinates_top_half + hoop_coordinates_bottom_half[::-1], columns=['x','y','z'])
|
235 |
+
hoop_coordinates_df['line_group'] = f'{loc}_hoop'
|
236 |
+
hoop_coordinates_df['color'] = 'hoop'
|
237 |
+
|
238 |
+
return hoop_coordinates_df
|
239 |
+
def __get_hoop_coordinates2(self, loc):
|
240 |
+
num_net_lines = 10 # Number of vertical lines in the net
|
241 |
+
net_length = 1.75 # Length of the net hanging down from the hoop (in feet)
|
242 |
+
initial_radius = self.hoop_radius # Radius at the top of the net
|
243 |
+
|
244 |
+
hoop_net_coordinates = []
|
245 |
+
hoop_loc_x, hoop_loc_y, hoop_loc_z = self.hoop_loc_x, self.hoop_loc_y, self.hoop_loc_z
|
246 |
+
if loc == 'near':
|
247 |
+
hoop_loc_y = self.court_length - hoop_loc_y
|
248 |
+
|
249 |
+
|
250 |
+
for i in range(num_net_lines):
|
251 |
+
angle = (i * 2 * np.pi) / num_net_lines
|
252 |
+
|
253 |
+
for j in np.linspace(0, net_length, num=10):
|
254 |
+
# Decrease the radius from the initial radius to half of it at the bottom
|
255 |
+
current_radius = initial_radius * (1 - (j / net_length) * 0.5)
|
256 |
+
|
257 |
+
x = hoop_loc_x + current_radius * np.cos(angle)
|
258 |
+
y = hoop_loc_y + current_radius * np.sin(angle)
|
259 |
+
z = hoop_loc_z - j
|
260 |
+
|
261 |
+
hoop_net_coordinates.append([x, y, z])
|
262 |
+
|
263 |
+
# Add lines on the other side (negative angles)
|
264 |
+
for i in range(num_net_lines):
|
265 |
+
angle = (i * 2 * np.pi) / num_net_lines + np.pi # Shift angles to cover the opposite side
|
266 |
+
|
267 |
+
for j in np.linspace(0, net_length, num=10):
|
268 |
+
current_radius = initial_radius * (1 - (j / net_length) * 0.5)
|
269 |
+
|
270 |
+
x = hoop_loc_x + current_radius * np.cos(angle)
|
271 |
+
y = hoop_loc_y + current_radius * np.sin(angle)
|
272 |
+
z = hoop_loc_z - j
|
273 |
+
|
274 |
+
hoop_net_coordinates.append([x, y, z])
|
275 |
+
|
276 |
+
hoop_net_df = pd.DataFrame(hoop_net_coordinates, columns=['x', 'y', 'z'])
|
277 |
+
hoop_net_df['line_group'] = f'{loc}hoop_net'
|
278 |
+
hoop_net_df['color'] = 'net' # Set color to Light Gray or any other color you prefer
|
279 |
+
|
280 |
+
return hoop_net_df
|
281 |
+
|
282 |
+
|
283 |
+
|
284 |
+
|
285 |
+
@staticmethod
|
286 |
+
def calculate_quadratic_values(a, b, c):
|
287 |
+
'''
|
288 |
+
Given values a, b, and c,
|
289 |
+
the function returns the output of the quadratic formula
|
290 |
+
'''
|
291 |
+
x1 = (-b + (b ** 2 - 4 * a * c) ** 0.5) / (2 * a)
|
292 |
+
x2 = (-b - (b ** 2 - 4 * a * c) ** 0.5) / (2 * a)
|
293 |
+
|
294 |
+
return x1, x2
|
295 |
+
|
296 |
+
def __get_free_throw_line_and_circle_coordinates(self, loc):
|
297 |
+
'''
|
298 |
+
Returns coordinates of the free throw line, circle at the center of the free throw line,
|
299 |
+
and lines extending from the free throw line to the baseline.
|
300 |
+
Also adds two parallel lines, each 2 feet away from the existing lines,
|
301 |
+
and a semicircle with a 4 ft radius starting at 3 ft or 91 ft from the baseline.
|
302 |
+
The free throw line is 15 feet from the backboard and spans from sideline to sideline.
|
303 |
+
The circle is centered on the free throw line and cuts the line in half.
|
304 |
+
'''
|
305 |
+
distance = 18
|
306 |
+
width = self.court_width
|
307 |
+
length = self.court_length
|
308 |
+
circle_radius = 6 # Radius of the free throw circle
|
309 |
+
semicircle_radius = 4 # Radius of the semicircle
|
310 |
+
offset = 2 # Offset distance for the additional lines
|
311 |
+
semicircle_start_distance_near = 3 # Starting distance from baseline for 'near'
|
312 |
+
semicircle_start_distance_far = 91 # Starting distance from baseline for 'far'
|
313 |
+
semicircle_start = semicircle_start_distance_near
|
314 |
+
semicircle_start2 = semicircle_start_distance_far
|
315 |
+
# Coordinates for the free throw line and the circle
|
316 |
+
if loc == 'far':
|
317 |
+
line_start = [17, length - distance, 0]
|
318 |
+
line_end = [width - 17, length - distance, 0]
|
319 |
+
circle_center = [width / 2, length - distance, 0]
|
320 |
+
baseline_y = length # Baseline is at the end of the court
|
321 |
+
left_offset = -offset
|
322 |
+
right_offset = offset
|
323 |
+
else:
|
324 |
+
line_start = [17, distance, 0]
|
325 |
+
line_end = [width - 17, distance, 0]
|
326 |
+
circle_center = [width / 2, distance, 0]
|
327 |
+
baseline_y = 0 # Baseline is at the start of the court
|
328 |
+
left_offset = -offset
|
329 |
+
right_offset = offset
|
330 |
+
|
331 |
+
# Generate circle coordinates
|
332 |
+
circle_points = []
|
333 |
+
num_points = 400 # Number of points to approximate the circle
|
334 |
+
for i in range(num_points):
|
335 |
+
angle = 2 * np.pi * i / num_points
|
336 |
+
x = circle_center[0] + circle_radius * np.cos(angle)
|
337 |
+
y = circle_center[1] + circle_radius * np.sin(angle)
|
338 |
+
circle_points.append([x, y, circle_center[2]])
|
339 |
+
|
340 |
+
|
341 |
+
# Generate semicircle coordinates
|
342 |
+
semicircle_points = []
|
343 |
+
num_points = 100 # Number of points to approximate the semicircle
|
344 |
+
for i in range(num_points + 1):
|
345 |
+
angle = np.pi * i / num_points
|
346 |
+
x = circle_center[0] + 4 * np.cos(angle)
|
347 |
+
y = semicircle_start + 5 * np.sin(angle)
|
348 |
+
semicircle_points.append([x, y, circle_center[2]])
|
349 |
+
semicircle_points2 = []
|
350 |
+
for i in range(num_points + 1):
|
351 |
+
angle = np.pi * i / num_points
|
352 |
+
x = circle_center[0] + 4 * np.cos(angle)
|
353 |
+
y = semicircle_start2 - 5 * np.sin(angle)
|
354 |
+
semicircle_points2.append([x, y, circle_center[2]])
|
355 |
+
|
356 |
+
# Coordinates for the straight lines extending to the baseline
|
357 |
+
left_line_start = [19, length - distance, 0] if loc == 'far' else [19, distance, 0]
|
358 |
+
left_line_end = [19, baseline_y, 0]
|
359 |
+
right_line_start = [width - 19, length - distance, 0] if loc == 'far' else [width - 19, distance, 0]
|
360 |
+
right_line_end = [width - 19, baseline_y, 0]
|
361 |
+
|
362 |
+
# Additional lines
|
363 |
+
left_offset_line_start = [19 + left_offset, length - distance, 0] if loc == 'far' else [19 + left_offset, distance, 0]
|
364 |
+
left_offset_line_end = [19 + left_offset, baseline_y, 0]
|
365 |
+
right_offset_line_start = [width - 19 + right_offset, length - distance, 0] if loc == 'far' else [width - 19 + right_offset, distance, 0]
|
366 |
+
right_offset_line_end = [width - 19 + right_offset, baseline_y, 0]
|
367 |
+
|
368 |
+
# Combine coordinates into DataFrames
|
369 |
+
free_throw_line_bounds = [line_start, line_end]
|
370 |
+
free_throw_line_df = pd.DataFrame(free_throw_line_bounds, columns=['x', 'y', 'z'])
|
371 |
+
free_throw_line_df['line_group'] = f'{loc}_free_throw_line'
|
372 |
+
free_throw_line_df['color'] = 'court'
|
373 |
+
|
374 |
+
circle_df = pd.DataFrame(circle_points, columns=['x', 'y', 'z'])
|
375 |
+
circle_df['line_group'] = f'{loc}_free_throw_circle'
|
376 |
+
circle_df['color'] = 'court'
|
377 |
+
|
378 |
+
semicircle_df = pd.DataFrame(semicircle_points, columns=['x', 'y', 'z'])
|
379 |
+
semicircle_df['line_group'] = f'free_throw_semicircle'
|
380 |
+
semicircle_df['color'] = 'court'
|
381 |
+
|
382 |
+
semicircle_df2 = pd.DataFrame(semicircle_points2, columns=['x', 'y', 'z'])
|
383 |
+
semicircle_df2['line_group'] = f'free_throw_semicircle2'
|
384 |
+
semicircle_df2['color'] = 'court'
|
385 |
+
|
386 |
+
left_line_df = pd.DataFrame([left_line_start, left_line_end], columns=['x', 'y', 'z'])
|
387 |
+
left_line_df['line_group'] = f'{loc}_free_throw_left_line'
|
388 |
+
left_line_df['color'] = 'court'
|
389 |
+
|
390 |
+
right_line_df = pd.DataFrame([right_line_start, right_line_end], columns=['x', 'y', 'z'])
|
391 |
+
right_line_df['line_group'] = f'{loc}_free_throw_right_line'
|
392 |
+
right_line_df['color'] = 'court'
|
393 |
+
|
394 |
+
# Additional offset lines
|
395 |
+
left_offset_line_df = pd.DataFrame([left_offset_line_start, left_offset_line_end], columns=['x', 'y', 'z'])
|
396 |
+
left_offset_line_df['line_group'] = f'{loc}_free_throw_left_offset_line'
|
397 |
+
left_offset_line_df['color'] = 'court'
|
398 |
+
|
399 |
+
right_offset_line_df = pd.DataFrame([right_offset_line_start, right_offset_line_end], columns=['x', 'y', 'z'])
|
400 |
+
right_offset_line_df['line_group'] = f'{loc}_free_throw_right_offset_line'
|
401 |
+
right_offset_line_df['color'] = 'court'
|
402 |
+
|
403 |
+
# Return combined DataFrame
|
404 |
+
return pd.concat([
|
405 |
+
free_throw_line_df,
|
406 |
+
circle_df,
|
407 |
+
semicircle_df2,
|
408 |
+
semicircle_df,
|
409 |
+
left_line_df,
|
410 |
+
right_line_df,
|
411 |
+
left_offset_line_df,
|
412 |
+
right_offset_line_df
|
413 |
+
])
|
414 |
+
|
415 |
+
|
416 |
+
|
417 |
+
|
418 |
+
|
419 |
+
def get_court_lines(self):
|
420 |
+
'''
|
421 |
+
Returns a concatenated DataFrame of all the court coordinates including the new free throw line.
|
422 |
+
'''
|
423 |
+
court_df = self.__get_court_perimeter_coordinates()
|
424 |
+
half_df = self.__get_half_court_coordinates()
|
425 |
+
backboard_home = self.__get_backboard_coordinates('near')
|
426 |
+
backboard_away = self.__get_backboard_coordinates('far')
|
427 |
+
hoop_away = self.__get_hoop_coordinates('near')
|
428 |
+
hoop_home = self.__get_hoop_coordinates('far')
|
429 |
+
net_away = self.__get_hoop_coordinates2('near')
|
430 |
+
net_home = self.__get_hoop_coordinates2('far')
|
431 |
+
three_home = self.__get_three_point_coordinates('near')
|
432 |
+
three_away = self.__get_three_point_coordinates('far')
|
433 |
+
free_throw_line = self.__get_free_throw_line_and_circle_coordinates('near')
|
434 |
+
free_throw_line2 = self.__get_free_throw_line_and_circle_coordinates('far') # Get free throw line coordinates
|
435 |
+
|
436 |
+
court_lines_df = pd.concat([court_df, half_df, backboard_home, backboard_away, hoop_away, hoop_home, three_home, three_away, free_throw_line,free_throw_line2,net_away,net_home])
|
437 |
+
|
438 |
+
return court_lines_df
|