diskpack 0.5.0__tar.gz → 0.6.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: diskpack
3
- Version: 0.5.0
3
+ Version: 0.6.0
4
4
  Summary: A high-performance vectorized circle packer with spatial hashing.
5
5
  Author-email: James Kelly <mrkellyjam@gmail.com>
6
6
  Requires-Python: >=3.8
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "diskpack"
7
- version = "0.5.0"
7
+ version = "0.6.0"
8
8
  authors = [{ name="James Kelly", email="mrkellyjam@gmail.com" }]
9
9
  description = "A high-performance vectorized circle packer with spatial hashing."
10
10
  readme = "README.md"
@@ -8,6 +8,9 @@ Point = np.ndarray
8
8
  GridKey = Tuple[int, int]
9
9
  Circle = Tuple[float, float, float]
10
10
 
11
+ # Threshold for switching between vectorized and spatial index approaches
12
+ VECTORIZED_THRESHOLD = 300
13
+
11
14
 
12
15
  @dataclass
13
16
  class PackingConfig:
@@ -192,6 +195,27 @@ class CirclePacker:
192
195
  origin=self.geometry.min_coords,
193
196
  mega_threshold=self.config.mega_circle_threshold
194
197
  )
198
+
199
+ # Cache for numpy arrays (avoid repeated conversion)
200
+ self._centers_arr: Optional[np.ndarray] = None
201
+ self._radii_arr: Optional[np.ndarray] = None
202
+ self._cache_valid = False
203
+
204
+ def _invalidate_cache(self) -> None:
205
+ """Mark the numpy array cache as needing refresh."""
206
+ self._cache_valid = False
207
+
208
+ def _get_arrays(self) -> Tuple[np.ndarray, np.ndarray]:
209
+ """Get cached numpy arrays of centers and radii."""
210
+ if not self._cache_valid or self._centers_arr is None:
211
+ if len(self.centers) > 0:
212
+ self._centers_arr = np.array(self.centers)
213
+ self._radii_arr = np.array(self.radii)
214
+ else:
215
+ self._centers_arr = np.empty((0, 2))
216
+ self._radii_arr = np.empty(0)
217
+ self._cache_valid = True
218
+ return self._centers_arr, self._radii_arr
195
219
 
196
220
  def _sample_candidate_points(self, count: int) -> np.ndarray:
197
221
  points = np.random.uniform(
@@ -209,16 +233,37 @@ class CirclePacker:
209
233
  return max_radius - self.config.padding
210
234
 
211
235
  def _compute_max_radii_batch(self, points: np.ndarray) -> np.ndarray:
212
- """Vectorized max radius computation for multiple points."""
236
+ """
237
+ Vectorized max radius computation for multiple points.
238
+
239
+ Uses hybrid approach:
240
+ - Fully vectorized numpy for small circle counts (faster due to no Python loop)
241
+ - Spatial index for large circle counts (faster due to fewer distance calculations)
242
+ """
213
243
  if len(points) == 0:
214
244
  return np.array([])
215
245
 
246
+ # Boundary distances (fully vectorized)
216
247
  max_radii = self.geometry.distances_to_boundary_batch(points)
217
248
 
218
- if len(self.centers) > 0:
219
- centers_arr = np.array(self.centers)
220
- radii_arr = np.array(self.radii)
221
-
249
+ if len(self.centers) == 0:
250
+ return max_radii - self.config.padding
251
+
252
+ centers_arr, radii_arr = self._get_arrays()
253
+
254
+ if len(self.centers) < VECTORIZED_THRESHOLD:
255
+ # Fully vectorized approach for small circle counts
256
+ # Compute distance from each point to each circle: (n_points, n_circles)
257
+ dists = np.linalg.norm(
258
+ points[:, np.newaxis, :] - centers_arr[np.newaxis, :, :],
259
+ axis=2
260
+ ) - radii_arr
261
+
262
+ # Min distance to any circle for each point
263
+ min_circle_dists = np.min(dists, axis=1)
264
+ max_radii = np.minimum(max_radii, min_circle_dists)
265
+ else:
266
+ # Spatial index approach for large circle counts
222
267
  for i, point in enumerate(points):
223
268
  indices = list(self.spatial_index.get_nearby_indices(point))
224
269
  if indices:
@@ -256,6 +301,7 @@ class CirclePacker:
256
301
  self.centers.append(center)
257
302
  self.radii.append(radius)
258
303
  self.spatial_index.add_circle(idx, center, radius)
304
+ self._invalidate_cache()
259
305
 
260
306
  def _generate_hex_grid(self, radius: float) -> np.ndarray:
261
307
  """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: diskpack
3
- Version: 0.5.0
3
+ Version: 0.6.0
4
4
  Summary: A high-performance vectorized circle packer with spatial hashing.
5
5
  Author-email: James Kelly <mrkellyjam@gmail.com>
6
6
  Requires-Python: >=3.8
File without changes
File without changes
File without changes
File without changes