@@ -99,6 +99,47 @@ def __lt__(self, other):
9999 return self .max > other .max
100100
101101
102+ def closest_point_on_segment (p , a , b ):
103+ """
104+ Return the closest point on segment ab to point p.
105+ p, a, b: numpy arrays of shape (2,)
106+ """
107+ ab = b - a
108+ ap = p - a
109+
110+ ab_len_sq = np .dot (ab , ab )
111+ if ab_len_sq < np .finfo (float ).eps :
112+ return a .copy ()
113+ else :
114+ t = np .dot (ap , ab ) / ab_len_sq
115+ t = np .clip (t , 0 , 1 ) # constrain t to [0, 1]
116+ return a + t * ab
117+
118+
119+ def closest_point_on_polygon (p , polygon ):
120+ """
121+ Find the closest point on a polygon to point p.
122+
123+ :param p: np.array of shape (2,) - the target point
124+ :param polygon: np.array of shape (N, 2) - the polygon vertices
125+ (assumed closed or will be treated as closed)
126+ :return: np.array of shape (2,) - closest point on polygon
127+ """
128+ min_dist = np .inf
129+ closest_point = None
130+ num_points = polygon .shape [0 ]
131+ for i in range (num_points ):
132+ a = polygon [i ]
133+ b = polygon [(i + 1 ) % num_points ] # wrap around
134+ proj = closest_point_on_segment (p , a , b )
135+ dist = np .linalg .norm (p - proj )
136+ if dist < min_dist :
137+ min_dist = dist
138+ closest_point = proj
139+
140+ return closest_point
141+
142+
102143def _get_centroid_cell (polygon ):
103144 """
104145 Estimate the polygon's centroid as an initial guess.
@@ -121,7 +162,9 @@ def _get_centroid_cell(polygon):
121162 b = a
122163
123164 if area == 0 :
124- return Cell (points [0 ][0 ], points [0 ][1 ], 0.0 , polygon )
165+ midpoint = (np .min (points , axis = 0 ) + np .max (points , axis = 0 )) / 2
166+ closest = closest_point_on_polygon (midpoint , points )
167+ return Cell (closest [0 ], closest [1 ], 0.0 , polygon )
125168
126169 return Cell (cx / area , cy / area , 0.0 , polygon )
127170
0 commit comments