some perf improvements
This commit is contained in:
parent
c989ab6b9f
commit
7e6be50a86
22 changed files with 301 additions and 135 deletions
|
|
@ -28,7 +28,7 @@ class CollisionEngine:
|
|||
'dynamic_tree', 'dynamic_obj_ids', 'dynamic_grid', '_dynamic_id_counter',
|
||||
'metrics', '_dynamic_tree_dirty', '_dynamic_net_ids_array', '_inv_grid_cell_size',
|
||||
'_static_bounds_array', '_static_is_rect_array', '_locked_nets',
|
||||
'_static_raw_tree', '_static_raw_obj_ids'
|
||||
'_static_raw_tree', '_static_raw_obj_ids', '_dynamic_bounds_array'
|
||||
)
|
||||
|
||||
def __init__(
|
||||
|
|
@ -72,6 +72,7 @@ class CollisionEngine:
|
|||
self._dynamic_id_counter = 0
|
||||
self._dynamic_tree_dirty = True
|
||||
self._dynamic_net_ids_array = numpy.array([], dtype='<U32')
|
||||
self._dynamic_bounds_array = numpy.array([], dtype=numpy.float64).reshape(0, 4)
|
||||
self._locked_nets: set[str] = set()
|
||||
|
||||
self.metrics = {
|
||||
|
|
@ -135,6 +136,7 @@ class CollisionEngine:
|
|||
geoms = [self.dynamic_dilated[i] for i in ids]
|
||||
self.dynamic_tree = STRtree(geoms)
|
||||
self.dynamic_obj_ids = numpy.array(ids, dtype=numpy.int32)
|
||||
self._dynamic_bounds_array = numpy.array([g.bounds for g in geoms])
|
||||
nids = [self.dynamic_geometries[obj_id][0] for obj_id in self.dynamic_obj_ids]
|
||||
self._dynamic_net_ids_array = numpy.array(nids, dtype='<U32')
|
||||
self._dynamic_tree_dirty = False
|
||||
|
|
@ -191,20 +193,29 @@ class CollisionEngine:
|
|||
self._ensure_static_tree()
|
||||
if self.static_tree is None: return False
|
||||
|
||||
# In sparse A*, result.dilated_geometry is buffered by C/2.
|
||||
# static_dilated is also buffered by C/2.
|
||||
# Total separation = C. Correct for waveguide-waveguide and waveguide-obstacle?
|
||||
# Actually, if result.geometry is width Wi, then dilated is Wi + C.
|
||||
# Wait, result.dilated_geometry is buffered by self._self_dilation = C/2.
|
||||
# So dilated poly is Wi + C.
|
||||
# Obstacle dilated by C/2 is Wo + C.
|
||||
# Intersection means dist < (Wi+C)/2 + (Wo+C)/2? No.
|
||||
# Let's keep it simple:
|
||||
# result.geometry is the REAL waveguide polygon (width Wi).
|
||||
# dilated_geometry is buffered by C/2.
|
||||
# static_dilated is buffered by C/2.
|
||||
# Intersecting them means dist < C. This is correct!
|
||||
# 1. Fast total bounds check
|
||||
tb = result.total_bounds
|
||||
s_bounds = self._static_bounds_array
|
||||
possible_total = (tb[0] < s_bounds[:, 2]) & (tb[2] > s_bounds[:, 0]) & \
|
||||
(tb[1] < s_bounds[:, 3]) & (tb[3] > s_bounds[:, 1])
|
||||
|
||||
if not numpy.any(possible_total):
|
||||
return False
|
||||
|
||||
# 2. Per-polygon AABB check
|
||||
bounds_list = result.bounds
|
||||
any_possible = False
|
||||
for b in bounds_list:
|
||||
possible = (b[0] < s_bounds[:, 2]) & (b[2] > s_bounds[:, 0]) & \
|
||||
(b[1] < s_bounds[:, 3]) & (b[3] > s_bounds[:, 1])
|
||||
if numpy.any(possible):
|
||||
any_possible = True
|
||||
break
|
||||
|
||||
if not any_possible:
|
||||
return False
|
||||
|
||||
# 3. Real geometry check (Triggers Lazy Evaluation)
|
||||
test_geoms = result.dilated_geometry if result.dilated_geometry else result.geometry
|
||||
for i, poly in enumerate(result.geometry):
|
||||
hits = self.static_tree.query(test_geoms[i], predicate='intersects')
|
||||
|
|
@ -215,15 +226,20 @@ class CollisionEngine:
|
|||
return False
|
||||
|
||||
def check_move_congestion(self, result: ComponentResult, net_id: str) -> int:
|
||||
if result.total_dilated_bounds is None: return 0
|
||||
tb = result.total_dilated_bounds
|
||||
if tb is None: return 0
|
||||
self._ensure_dynamic_grid()
|
||||
if not self.dynamic_grid: return 0
|
||||
b = result.total_dilated_bounds; cs = self.grid_cell_size
|
||||
|
||||
cs_inv = self._inv_grid_cell_size
|
||||
gx_min, gy_min = int(tb[0] * cs_inv), int(tb[1] * cs_inv)
|
||||
gx_max, gy_max = int(tb[2] * cs_inv), int(tb[3] * cs_inv)
|
||||
|
||||
any_possible = False
|
||||
dynamic_grid = self.dynamic_grid
|
||||
dynamic_geometries = self.dynamic_geometries
|
||||
for gx in range(int(b[0]/cs), int(b[2]/cs)+1):
|
||||
for gy in range(int(b[1]/cs), int(b[3]/cs)+1):
|
||||
for gx in range(gx_min, gx_max + 1):
|
||||
for gy in range(gy_min, gy_max + 1):
|
||||
cell = (gx, gy)
|
||||
if cell in dynamic_grid:
|
||||
for obj_id in dynamic_grid[cell]:
|
||||
|
|
@ -232,14 +248,40 @@ class CollisionEngine:
|
|||
if any_possible: break
|
||||
if any_possible: break
|
||||
if not any_possible: return 0
|
||||
|
||||
self.metrics['congestion_tree_queries'] += 1
|
||||
self._ensure_dynamic_tree()
|
||||
if self.dynamic_tree is None: return 0
|
||||
|
||||
# 1. Fast total bounds check (LAZY SAFE)
|
||||
tb = result.total_dilated_bounds
|
||||
d_bounds = self._dynamic_bounds_array
|
||||
possible_total = (tb[0] < d_bounds[:, 2]) & (tb[2] > d_bounds[:, 0]) & \
|
||||
(tb[1] < d_bounds[:, 3]) & (tb[3] > d_bounds[:, 1])
|
||||
|
||||
# Filter by net_id (important for negotiated congestion)
|
||||
valid_hits = (self._dynamic_net_ids_array != net_id)
|
||||
if not numpy.any(possible_total & valid_hits):
|
||||
return 0
|
||||
|
||||
# 2. Per-polygon AABB check using query on geometries (LAZY triggering)
|
||||
# We only trigger evaluation if total bounds intersect with other nets.
|
||||
geoms_to_test = result.dilated_geometry if result.dilated_geometry else result.geometry
|
||||
res_indices, tree_indices = self.dynamic_tree.query(geoms_to_test, predicate='intersects')
|
||||
if tree_indices.size == 0: return 0
|
||||
|
||||
if tree_indices.size == 0:
|
||||
return 0
|
||||
|
||||
# Filter out self-overlaps (from same net)
|
||||
hit_net_ids = numpy.take(self._dynamic_net_ids_array, tree_indices)
|
||||
return int(numpy.sum(hit_net_ids != net_id))
|
||||
valid_geoms_hits = (hit_net_ids != net_id)
|
||||
if not numpy.any(valid_geoms_hits):
|
||||
return 0
|
||||
|
||||
# 3. Real geometry check (Only if AABBs intersect with other nets)
|
||||
# We already have hits from STRtree which are accurate for polygons too.
|
||||
# But wait, query(..., predicate='intersects') ALREADY does real check!
|
||||
return int(numpy.sum(valid_geoms_hits))
|
||||
|
||||
def _is_in_safety_zone(self, geometry: Polygon, obj_id: int, start_port: Port | None, end_port: Port | None) -> bool:
|
||||
"""
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue