further performance improvements

This commit is contained in:
Jan Petykiewicz 2026-03-09 22:16:34 -07:00
commit c36bce9978
8 changed files with 168 additions and 58 deletions

View file

@ -17,7 +17,7 @@ class CollisionEngine:
__slots__ = (
'clearance', 'max_net_width', 'safety_zone_radius',
'static_index', 'static_geometries', 'static_dilated', 'static_prepared', '_static_id_counter',
'dynamic_index', 'dynamic_geometries', 'dynamic_dilated', '_dynamic_id_counter'
'dynamic_index', 'dynamic_geometries', 'dynamic_dilated', 'dynamic_prepared', '_dynamic_id_counter'
)
clearance: float
@ -60,6 +60,7 @@ class CollisionEngine:
self.dynamic_geometries: dict[int, tuple[str, Polygon]] = {}
# obj_id -> dilated_geometry (by clearance/2)
self.dynamic_dilated: dict[int, Polygon] = {}
self.dynamic_prepared: dict[int, PreparedGeometry] = {}
self._dynamic_id_counter = 0
def add_static_obstacle(self, polygon: Polygon) -> None:
@ -96,6 +97,7 @@ class CollisionEngine:
self.dynamic_geometries[obj_id] = (net_id, poly)
self.dynamic_dilated[obj_id] = dil
self.dynamic_prepared[obj_id] = prep(dil)
self.dynamic_index.insert(obj_id, dil.bounds)
def remove_path(self, net_id: str) -> None:
@ -109,6 +111,7 @@ class CollisionEngine:
for obj_id in to_remove:
nid, poly = self.dynamic_geometries.pop(obj_id)
dilated = self.dynamic_dilated.pop(obj_id)
self.dynamic_prepared.pop(obj_id)
self.dynamic_index.delete(obj_id, dilated.bounds)
def lock_net(self, net_id: str) -> None:
@ -122,6 +125,7 @@ class CollisionEngine:
for obj_id in to_move:
nid, poly = self.dynamic_geometries.pop(obj_id)
dilated = self.dynamic_dilated.pop(obj_id)
self.dynamic_prepared.pop(obj_id)
self.dynamic_index.delete(obj_id, dilated.bounds)
# Re-buffer for static clearance if necessary.
# Note: dynamic is clearance/2, static is clearance.
@ -178,20 +182,28 @@ class CollisionEngine:
for obj_id in candidates:
if self.static_prepared[obj_id].intersects(geometry):
if start_port or end_port:
# Optimization: Instead of expensive buffer + intersection,
# use distance() and check if it's within clearance only near ports.
raw_obstacle = self.static_geometries[obj_id]
# If the intersection is within clearance, distance will be < clearance.
# We already know it intersects the dilated obstacle, so distance < clearance.
is_safe = False
# Optimization: Skip expensive intersection if neither port is near the obstacle's bounds
# (Plus a small margin for safety zone)
sz = self.safety_zone_radius
is_near_port = False
for p in [start_port, end_port]:
if p:
# Quick bounds check
b = self.static_dilated[obj_id].bounds
if (b[0] - sz <= p.x <= b[2] + sz and
b[1] - sz <= p.y <= b[3] + sz):
is_near_port = True
break
# Use intersection bounds to check proximity to ports
# We need the intersection of the geometry and the RAW obstacle
if not is_near_port:
return True # Collision, and not near any port safety zone
# Only if near port, do the expensive check
raw_obstacle = self.static_geometries[obj_id]
intersection = geometry.intersection(raw_obstacle)
if not intersection.is_empty:
ix_minx, ix_miny, ix_maxx, ix_maxy = intersection.bounds
is_safe = False
for p in [start_port, end_port]:
if p and (abs(ix_minx - p.x) < sz and
abs(ix_maxx - p.x) < sz and
@ -199,9 +211,9 @@ class CollisionEngine:
abs(ix_maxy - p.y) < sz):
is_safe = True
break
if is_safe:
continue
if is_safe:
continue
return True
return False
@ -213,6 +225,6 @@ class CollisionEngine:
count = 0
for obj_id in candidates:
other_net_id, _ = self.dynamic_geometries[obj_id]
if other_net_id != net_id and test_poly.intersects(self.dynamic_dilated[obj_id]):
if other_net_id != net_id and self.dynamic_prepared[obj_id].intersects(test_poly):
count += 1
return count