performance improvements
This commit is contained in:
parent
8eb0dbf64a
commit
8424171946
12 changed files with 212 additions and 118 deletions
|
|
@ -16,8 +16,8 @@ class CollisionEngine:
|
|||
"""
|
||||
__slots__ = (
|
||||
'clearance', 'max_net_width', 'safety_zone_radius',
|
||||
'static_index', 'static_geometries', 'static_prepared', '_static_id_counter',
|
||||
'dynamic_index', 'dynamic_geometries', '_dynamic_id_counter'
|
||||
'static_index', 'static_geometries', 'static_dilated', 'static_prepared', '_static_id_counter',
|
||||
'dynamic_index', 'dynamic_geometries', 'dynamic_dilated', '_dynamic_id_counter'
|
||||
)
|
||||
|
||||
clearance: float
|
||||
|
|
@ -47,21 +47,24 @@ class CollisionEngine:
|
|||
self.max_net_width = max_net_width
|
||||
self.safety_zone_radius = safety_zone_radius
|
||||
|
||||
# Static obstacles: store raw geometries to avoid double-dilation
|
||||
# Static obstacles
|
||||
self.static_index = rtree.index.Index()
|
||||
self.static_geometries: dict[int, Polygon] = {} # ID -> Polygon
|
||||
self.static_prepared: dict[int, PreparedGeometry] = {} # ID -> PreparedGeometry
|
||||
self.static_geometries: dict[int, Polygon] = {} # ID -> Raw Polygon
|
||||
self.static_dilated: dict[int, Polygon] = {} # ID -> Dilated Polygon (by clearance)
|
||||
self.static_prepared: dict[int, PreparedGeometry] = {} # ID -> Prepared Dilated
|
||||
self._static_id_counter = 0
|
||||
|
||||
# Dynamic paths for multi-net congestion
|
||||
self.dynamic_index = rtree.index.Index()
|
||||
# obj_id -> (net_id, raw_geometry)
|
||||
self.dynamic_geometries: dict[int, tuple[str, Polygon]] = {}
|
||||
# obj_id -> dilated_geometry (by clearance/2)
|
||||
self.dynamic_dilated: dict[int, Polygon] = {}
|
||||
self._dynamic_id_counter = 0
|
||||
|
||||
def add_static_obstacle(self, polygon: Polygon) -> None:
|
||||
"""
|
||||
Add a static obstacle (raw geometry) to the engine.
|
||||
Add a static obstacle to the engine.
|
||||
|
||||
Args:
|
||||
polygon: Raw obstacle geometry.
|
||||
|
|
@ -69,23 +72,31 @@ class CollisionEngine:
|
|||
obj_id = self._static_id_counter
|
||||
self._static_id_counter += 1
|
||||
|
||||
dilated = polygon.buffer(self.clearance)
|
||||
self.static_geometries[obj_id] = polygon
|
||||
self.static_prepared[obj_id] = prep(polygon)
|
||||
self.static_index.insert(obj_id, polygon.bounds)
|
||||
self.static_dilated[obj_id] = dilated
|
||||
self.static_prepared[obj_id] = prep(dilated)
|
||||
self.static_index.insert(obj_id, dilated.bounds)
|
||||
|
||||
def add_path(self, net_id: str, geometry: list[Polygon]) -> None:
|
||||
def add_path(self, net_id: str, geometry: list[Polygon], dilated_geometry: list[Polygon] | None = None) -> None:
|
||||
"""
|
||||
Add a net's routed path (raw geometry) to the dynamic index.
|
||||
Add a net's routed path to the dynamic index.
|
||||
|
||||
Args:
|
||||
net_id: Identifier for the net.
|
||||
geometry: List of raw polygons in the path.
|
||||
dilated_geometry: Optional list of pre-dilated polygons (by clearance/2).
|
||||
"""
|
||||
for poly in geometry:
|
||||
dilation = self.clearance / 2.0
|
||||
for i, poly in enumerate(geometry):
|
||||
obj_id = self._dynamic_id_counter
|
||||
self._dynamic_id_counter += 1
|
||||
|
||||
dil = dilated_geometry[i] if dilated_geometry else poly.buffer(dilation)
|
||||
|
||||
self.dynamic_geometries[obj_id] = (net_id, poly)
|
||||
self.dynamic_index.insert(obj_id, poly.bounds)
|
||||
self.dynamic_dilated[obj_id] = dil
|
||||
self.dynamic_index.insert(obj_id, dil.bounds)
|
||||
|
||||
def remove_path(self, net_id: str) -> None:
|
||||
"""
|
||||
|
|
@ -97,7 +108,8 @@ class CollisionEngine:
|
|||
to_remove = [obj_id for obj_id, (nid, _) in self.dynamic_geometries.items() if nid == net_id]
|
||||
for obj_id in to_remove:
|
||||
nid, poly = self.dynamic_geometries.pop(obj_id)
|
||||
self.dynamic_index.delete(obj_id, poly.bounds)
|
||||
dilated = self.dynamic_dilated.pop(obj_id)
|
||||
self.dynamic_index.delete(obj_id, dilated.bounds)
|
||||
|
||||
def lock_net(self, net_id: str) -> None:
|
||||
"""
|
||||
|
|
@ -109,7 +121,10 @@ class CollisionEngine:
|
|||
to_move = [obj_id for obj_id, (nid, _) in self.dynamic_geometries.items() if nid == net_id]
|
||||
for obj_id in to_move:
|
||||
nid, poly = self.dynamic_geometries.pop(obj_id)
|
||||
self.dynamic_index.delete(obj_id, poly.bounds)
|
||||
dilated = self.dynamic_dilated.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.
|
||||
self.add_static_obstacle(poly)
|
||||
|
||||
def is_collision(
|
||||
|
|
@ -121,15 +136,6 @@ class CollisionEngine:
|
|||
) -> bool:
|
||||
"""
|
||||
Alias for check_collision(buffer_mode='static') for backward compatibility.
|
||||
|
||||
Args:
|
||||
geometry: Move geometry to check.
|
||||
net_width: Width of the net (unused).
|
||||
start_port: Starting port for safety check.
|
||||
end_port: Ending port for safety check.
|
||||
|
||||
Returns:
|
||||
True if collision detected.
|
||||
"""
|
||||
_ = net_width
|
||||
res = self.check_collision(geometry, 'default', buffer_mode='static', start_port=start_port, end_port=end_port)
|
||||
|
|
@ -138,13 +144,6 @@ class CollisionEngine:
|
|||
def count_congestion(self, geometry: Polygon, net_id: str) -> int:
|
||||
"""
|
||||
Alias for check_collision(buffer_mode='congestion') for backward compatibility.
|
||||
|
||||
Args:
|
||||
geometry: Move geometry to check.
|
||||
net_id: Identifier for the net.
|
||||
|
||||
Returns:
|
||||
Number of overlapping nets.
|
||||
"""
|
||||
res = self.check_collision(geometry, net_id, buffer_mode='congestion')
|
||||
return int(res)
|
||||
|
|
@ -156,6 +155,7 @@ class CollisionEngine:
|
|||
buffer_mode: Literal['static', 'congestion'] = 'static',
|
||||
start_port: Port | None = None,
|
||||
end_port: Port | None = None,
|
||||
dilated_geometry: Polygon | None = None,
|
||||
) -> bool | int:
|
||||
"""
|
||||
Check for collisions using unified dilation logic.
|
||||
|
|
@ -166,17 +166,24 @@ class CollisionEngine:
|
|||
buffer_mode: 'static' (full clearance) or 'congestion' (shared).
|
||||
start_port: Optional start port for safety zone.
|
||||
end_port: Optional end port for safety zone.
|
||||
dilated_geometry: Optional pre-buffered geometry (clearance/2).
|
||||
|
||||
Returns:
|
||||
Boolean if static, integer count if congestion.
|
||||
"""
|
||||
if buffer_mode == 'static':
|
||||
test_poly = geometry.buffer(self.clearance)
|
||||
candidates = self.static_index.intersection(test_poly.bounds)
|
||||
# Use raw query against pre-dilated obstacles
|
||||
candidates = self.static_index.intersection(geometry.bounds)
|
||||
|
||||
for obj_id in candidates:
|
||||
if self.static_prepared[obj_id].intersects(test_poly):
|
||||
if self.static_prepared[obj_id].intersects(geometry):
|
||||
if start_port or end_port:
|
||||
# Safety zone check: requires intersection of DILATED query and RAW obstacle.
|
||||
# Always re-buffer here because static check needs full clearance dilation,
|
||||
# whereas the provided dilated_geometry is usually clearance/2.
|
||||
dilation = self.clearance
|
||||
test_poly = geometry.buffer(dilation)
|
||||
|
||||
intersection = test_poly.intersection(self.static_geometries[obj_id])
|
||||
if intersection.is_empty:
|
||||
continue
|
||||
|
|
@ -198,12 +205,12 @@ class CollisionEngine:
|
|||
|
||||
# buffer_mode == 'congestion'
|
||||
dilation = self.clearance / 2.0
|
||||
test_poly = geometry.buffer(dilation)
|
||||
test_poly = dilated_geometry if dilated_geometry else geometry.buffer(dilation)
|
||||
candidates = self.dynamic_index.intersection(test_poly.bounds)
|
||||
|
||||
count = 0
|
||||
for obj_id in candidates:
|
||||
other_net_id, other_poly = self.dynamic_geometries[obj_id]
|
||||
if other_net_id != net_id and test_poly.intersects(other_poly.buffer(dilation)):
|
||||
other_net_id, _ = self.dynamic_geometries[obj_id]
|
||||
if other_net_id != net_id and test_poly.intersects(self.dynamic_dilated[obj_id]):
|
||||
count += 1
|
||||
return count
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue