linter fixes
This commit is contained in:
parent
e2c91076f7
commit
1849075b11
26 changed files with 152 additions and 104 deletions
|
|
@ -37,7 +37,12 @@ def main() -> None:
|
||||||
else:
|
else:
|
||||||
print("The route unexpectedly reached the target. Increase difficulty or reduce the node budget further.")
|
print("The route unexpectedly reached the target. Increase difficulty or reduce the node budget further.")
|
||||||
|
|
||||||
fig, _ax = plot_routing_results(run.results_by_net, list(obstacles), bounds, netlist={"budget_limited_net": (Port(10, 50, 0), Port(85, 60, 180))})
|
fig, _ax = plot_routing_results(
|
||||||
|
run.results_by_net,
|
||||||
|
list(obstacles),
|
||||||
|
bounds,
|
||||||
|
netlist={"budget_limited_net": (Port(10, 50, 0), Port(85, 60, 180))},
|
||||||
|
)
|
||||||
fig.savefig("examples/09_unroutable_best_effort.png")
|
fig.savefig("examples/09_unroutable_best_effort.png")
|
||||||
print("Saved plot to examples/09_unroutable_best_effort.png")
|
print("Saved plot to examples/09_unroutable_best_effort.png")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -105,9 +105,16 @@ class RoutingWorld:
|
||||||
return reach < length - 0.001
|
return reach < length - 0.001
|
||||||
|
|
||||||
def _is_in_safety_zone_fast(self, idx: int, start_port: Port | None, end_port: Port | None) -> bool:
|
def _is_in_safety_zone_fast(self, idx: int, start_port: Port | None, end_port: Port | None) -> bool:
|
||||||
bounds = self._static_obstacles.bounds_array[idx]
|
bounds_array = self._static_obstacles.bounds_array
|
||||||
|
if bounds_array is None:
|
||||||
|
return False
|
||||||
|
bounds = bounds_array[idx]
|
||||||
safety_zone = self.safety_zone_radius
|
safety_zone = self.safety_zone_radius
|
||||||
if start_port and bounds[0] - safety_zone <= start_port.x <= bounds[2] + safety_zone and bounds[1] - safety_zone <= start_port.y <= bounds[3] + safety_zone:
|
if (
|
||||||
|
start_port
|
||||||
|
and bounds[0] - safety_zone <= start_port.x <= bounds[2] + safety_zone
|
||||||
|
and bounds[1] - safety_zone <= start_port.y <= bounds[3] + safety_zone
|
||||||
|
):
|
||||||
return True
|
return True
|
||||||
return bool(
|
return bool(
|
||||||
end_port
|
end_port
|
||||||
|
|
@ -176,15 +183,18 @@ class RoutingWorld:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
self._ensure_static_tree()
|
self._ensure_static_tree()
|
||||||
|
tree = static_obstacles.tree
|
||||||
|
bounds_array = static_obstacles.bounds_array
|
||||||
|
if tree is None or bounds_array is None:
|
||||||
|
return False
|
||||||
|
|
||||||
hits = static_obstacles.tree.query(box(*result.total_dilated_bounds))
|
hits = tree.query(box(*result.total_dilated_bounds))
|
||||||
if hits.size == 0:
|
if hits.size == 0:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
static_bounds = static_obstacles.bounds_array
|
|
||||||
move_poly_bounds = result.dilated_bounds
|
move_poly_bounds = result.dilated_bounds
|
||||||
for hit_idx in hits:
|
for hit_idx in hits:
|
||||||
obstacle_bounds = static_bounds[hit_idx]
|
obstacle_bounds = bounds_array[hit_idx]
|
||||||
poly_hits_obstacle_aabb = False
|
poly_hits_obstacle_aabb = False
|
||||||
for poly_bounds in move_poly_bounds:
|
for poly_bounds in move_poly_bounds:
|
||||||
if (
|
if (
|
||||||
|
|
@ -319,7 +329,7 @@ class RoutingWorld:
|
||||||
raw_geometries = static_obstacles.raw_tree.geometries
|
raw_geometries = static_obstacles.raw_tree.geometries
|
||||||
for component in components:
|
for component in components:
|
||||||
for polygon in component.physical_geometry:
|
for polygon in component.physical_geometry:
|
||||||
buffered = polygon.buffer(self.clearance, join_style=2)
|
buffered = polygon.buffer(self.clearance, join_style="mitre")
|
||||||
hits = static_obstacles.raw_tree.query(buffered, predicate="intersects")
|
hits = static_obstacles.raw_tree.query(buffered, predicate="intersects")
|
||||||
for hit_idx in hits:
|
for hit_idx in hits:
|
||||||
obstacle = raw_geometries[hit_idx]
|
obstacle = raw_geometries[hit_idx]
|
||||||
|
|
@ -373,6 +383,9 @@ class RoutingWorld:
|
||||||
net_width: float | None = None,
|
net_width: float | None = None,
|
||||||
) -> float:
|
) -> float:
|
||||||
static_obstacles = self._static_obstacles
|
static_obstacles = self._static_obstacles
|
||||||
|
tree: STRtree | None
|
||||||
|
is_rect_array: numpy.ndarray | None
|
||||||
|
bounds_array: numpy.ndarray | None
|
||||||
|
|
||||||
radians = numpy.radians(angle_deg)
|
radians = numpy.radians(angle_deg)
|
||||||
cos_v, sin_v = numpy.cos(radians), numpy.sin(radians)
|
cos_v, sin_v = numpy.cos(radians), numpy.sin(radians)
|
||||||
|
|
@ -391,7 +404,7 @@ class RoutingWorld:
|
||||||
is_rect_array = static_obstacles.is_rect_array
|
is_rect_array = static_obstacles.is_rect_array
|
||||||
bounds_array = static_obstacles.bounds_array
|
bounds_array = static_obstacles.bounds_array
|
||||||
|
|
||||||
if tree is None:
|
if tree is None or is_rect_array is None or bounds_array is None:
|
||||||
return max_dist
|
return max_dist
|
||||||
|
|
||||||
candidates = tree.query(box(min_x, min_y, max_x, max_y))
|
candidates = tree.query(box(min_x, min_y, max_x, max_y))
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ from __future__ import annotations
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
from collections.abc import Sequence
|
||||||
from shapely.geometry import Polygon
|
from shapely.geometry import Polygon
|
||||||
|
|
||||||
from inire.geometry.components import ComponentResult
|
from inire.geometry.components import ComponentResult
|
||||||
|
|
@ -13,8 +14,8 @@ def components_overlap(
|
||||||
component_b: ComponentResult,
|
component_b: ComponentResult,
|
||||||
prefer_actual: bool = False,
|
prefer_actual: bool = False,
|
||||||
) -> bool:
|
) -> bool:
|
||||||
polygons_a: tuple[Polygon, ...]
|
polygons_a: Sequence[Polygon]
|
||||||
polygons_b: tuple[Polygon, ...]
|
polygons_b: Sequence[Polygon]
|
||||||
if prefer_actual:
|
if prefer_actual:
|
||||||
polygons_a = component_a.physical_geometry
|
polygons_a = component_a.physical_geometry
|
||||||
polygons_b = component_b.physical_geometry
|
polygons_b = component_b.physical_geometry
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from typing import Literal
|
from typing import TYPE_CHECKING, Literal
|
||||||
|
|
||||||
import numpy
|
import numpy
|
||||||
from shapely.affinity import rotate as shapely_rotate
|
from shapely.affinity import rotate as shapely_rotate
|
||||||
|
|
@ -13,6 +13,9 @@ from inire.constants import TOLERANCE_ANGULAR
|
||||||
from inire.seeds import Bend90Seed, PathSegmentSeed, SBendSeed, StraightSeed
|
from inire.seeds import Bend90Seed, PathSegmentSeed, SBendSeed, StraightSeed
|
||||||
from .primitives import Port, rotation_matrix2
|
from .primitives import Port, rotation_matrix2
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from collections.abc import Sequence
|
||||||
|
|
||||||
|
|
||||||
MoveKind = Literal["straight", "bend90", "sbend"]
|
MoveKind = Literal["straight", "bend90", "sbend"]
|
||||||
BendCollisionModelName = Literal["arc", "bbox", "clipped_bbox"]
|
BendCollisionModelName = Literal["arc", "bbox", "clipped_bbox"]
|
||||||
|
|
@ -27,14 +30,14 @@ def _normalize_length(value: float) -> float:
|
||||||
@dataclass(frozen=True, slots=True)
|
@dataclass(frozen=True, slots=True)
|
||||||
class ComponentResult:
|
class ComponentResult:
|
||||||
start_port: Port
|
start_port: Port
|
||||||
collision_geometry: tuple[Polygon, ...]
|
collision_geometry: Sequence[Polygon]
|
||||||
end_port: Port
|
end_port: Port
|
||||||
length: float
|
length: float
|
||||||
move_type: MoveKind
|
move_type: MoveKind
|
||||||
move_spec: PathSegmentSeed
|
move_spec: PathSegmentSeed
|
||||||
physical_geometry: tuple[Polygon, ...]
|
physical_geometry: Sequence[Polygon]
|
||||||
dilated_collision_geometry: tuple[Polygon, ...]
|
dilated_collision_geometry: Sequence[Polygon]
|
||||||
dilated_physical_geometry: tuple[Polygon, ...]
|
dilated_physical_geometry: Sequence[Polygon]
|
||||||
_bounds: tuple[tuple[float, float, float, float], ...] = field(init=False, repr=False)
|
_bounds: tuple[tuple[float, float, float, float], ...] = field(init=False, repr=False)
|
||||||
_total_bounds: tuple[float, float, float, float] = field(init=False, repr=False)
|
_total_bounds: tuple[float, float, float, float] = field(init=False, repr=False)
|
||||||
_dilated_bounds: tuple[tuple[float, float, float, float], ...] = field(init=False, repr=False)
|
_dilated_bounds: tuple[tuple[float, float, float, float], ...] = field(init=False, repr=False)
|
||||||
|
|
@ -146,7 +149,7 @@ def _clip_bbox_legacy(
|
||||||
minx, miny, maxx, maxy = arc_poly.bounds
|
minx, miny, maxx, maxy = arc_poly.bounds
|
||||||
bbox_poly = box(minx, miny, maxx, maxy)
|
bbox_poly = box(minx, miny, maxx, maxy)
|
||||||
shrink = min(clip_margin, max(radius, width))
|
shrink = min(clip_margin, max(radius, width))
|
||||||
return bbox_poly.buffer(-shrink, join_style=2) if shrink > 0 else bbox_poly
|
return bbox_poly.buffer(-shrink, join_style="mitre") if shrink > 0 else bbox_poly
|
||||||
|
|
||||||
|
|
||||||
def _clip_bbox_polygonal(cxy: tuple[float, float], radius: float, width: float, ts: tuple[float, float]) -> Polygon:
|
def _clip_bbox_polygonal(cxy: tuple[float, float], radius: float, width: float, ts: tuple[float, float]) -> Polygon:
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,18 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import math
|
import math
|
||||||
from collections.abc import Iterator, Mapping
|
from typing import TYPE_CHECKING
|
||||||
from typing import TypeVar
|
|
||||||
|
|
||||||
import numpy
|
import numpy
|
||||||
|
|
||||||
GeometryT = TypeVar("GeometryT")
|
if TYPE_CHECKING:
|
||||||
|
from collections.abc import Iterator, Mapping
|
||||||
|
from shapely.geometry.base import BaseGeometry
|
||||||
|
|
||||||
|
|
||||||
def build_index_payload(
|
def build_index_payload(
|
||||||
geometries: Mapping[int, GeometryT],
|
geometries: Mapping[int, BaseGeometry],
|
||||||
) -> tuple[list[int], list[GeometryT], numpy.ndarray]:
|
) -> tuple[list[int], list[BaseGeometry], numpy.ndarray]:
|
||||||
obj_ids = sorted(geometries)
|
obj_ids = sorted(geometries)
|
||||||
ordered_geometries = [geometries[obj_id] for obj_id in obj_ids]
|
ordered_geometries = [geometries[obj_id] for obj_id in obj_ids]
|
||||||
bounds_array = numpy.array([geometry.bounds for geometry in ordered_geometries], dtype=numpy.float64)
|
bounds_array = numpy.array([geometry.bounds for geometry in ordered_geometries], dtype=numpy.float64)
|
||||||
|
|
@ -42,7 +43,7 @@ def iter_grid_cells(
|
||||||
yield (gx, gy)
|
yield (gx, gy)
|
||||||
|
|
||||||
|
|
||||||
def is_axis_aligned_rect(geometry, *, tolerance: float = 1e-4) -> bool:
|
def is_axis_aligned_rect(geometry: BaseGeometry, *, tolerance: float = 1e-4) -> bool:
|
||||||
bounds = geometry.bounds
|
bounds = geometry.bounds
|
||||||
area = (bounds[2] - bounds[0]) * (bounds[3] - bounds[1])
|
area = (bounds[2] - bounds[0]) * (bounds[3] - bounds[1])
|
||||||
return abs(geometry.area - area) < tolerance
|
return abs(geometry.area - area) < tolerance
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Self
|
from typing import TYPE_CHECKING, Self
|
||||||
|
|
||||||
import numpy
|
import numpy
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
from numpy.typing import NDArray
|
from numpy.typing import NDArray
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -58,6 +60,6 @@ ROT2_180 = numpy.array(((-1, 0), (0, -1)), dtype=numpy.int32)
|
||||||
ROT2_270 = numpy.array(((0, 1), (-1, 0)), dtype=numpy.int32)
|
ROT2_270 = numpy.array(((0, 1), (-1, 0)), dtype=numpy.int32)
|
||||||
|
|
||||||
|
|
||||||
def rotation_matrix2(rotation_deg: int) -> NDArray[numpy.int32]:
|
def rotation_matrix2(rotation_deg: int | float) -> NDArray[numpy.int32]:
|
||||||
quadrant = (_normalize_angle(rotation_deg) // 90) % 4
|
quadrant = (_normalize_angle(rotation_deg) // 90) % 4
|
||||||
return (ROT2_0, ROT2_90, ROT2_180, ROT2_270)[quadrant]
|
return (ROT2_0, ROT2_90, ROT2_180, ROT2_270)[quadrant]
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ class StaticObstacleIndex:
|
||||||
if dilated_geometry is not None:
|
if dilated_geometry is not None:
|
||||||
dilated = dilated_geometry
|
dilated = dilated_geometry
|
||||||
else:
|
else:
|
||||||
dilated = polygon.buffer(self.engine.clearance / 2.0, join_style=2)
|
dilated = polygon.buffer(self.engine.clearance / 2.0, join_style="mitre")
|
||||||
|
|
||||||
self.geometries[obj_id] = polygon
|
self.geometries[obj_id] = polygon
|
||||||
self.dilated[obj_id] = dilated
|
self.dilated[obj_id] = dilated
|
||||||
|
|
@ -109,7 +109,7 @@ class StaticObstacleIndex:
|
||||||
|
|
||||||
for obj_id in sorted(self.geometries.keys()):
|
for obj_id in sorted(self.geometries.keys()):
|
||||||
polygon = self.geometries[obj_id]
|
polygon = self.geometries[obj_id]
|
||||||
dilated = polygon.buffer(total_dilation, join_style=2)
|
dilated = polygon.buffer(total_dilation, join_style="mitre")
|
||||||
geometries.append(dilated)
|
geometries.append(dilated)
|
||||||
bounds_list.append(dilated.bounds)
|
bounds_list.append(dilated.bounds)
|
||||||
is_rect_list.append(is_axis_aligned_rect(dilated))
|
is_rect_list.append(is_axis_aligned_rect(dilated))
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,10 @@ from dataclasses import dataclass, field
|
||||||
from typing import TYPE_CHECKING, Literal
|
from typing import TYPE_CHECKING, Literal
|
||||||
|
|
||||||
from shapely.geometry import Polygon
|
from shapely.geometry import Polygon
|
||||||
|
|
||||||
from inire.geometry.components import BendCollisionModel, BendPhysicalGeometry
|
|
||||||
from inire.seeds import PathSeed
|
from inire.seeds import PathSeed
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
from inire.geometry.components import BendCollisionModel, BendPhysicalGeometry
|
||||||
from inire.geometry.primitives import Port
|
from inire.geometry.primitives import Port
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ class RoutingResult:
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def locked_geometry(self) -> tuple[Polygon, ...]:
|
def locked_geometry(self) -> tuple[Polygon, ...]:
|
||||||
polygons = []
|
polygons: list[Polygon] = []
|
||||||
for component in self.path:
|
for component in self.path:
|
||||||
polygons.extend(component.physical_geometry)
|
polygons.extend(component.physical_geometry)
|
||||||
return tuple(polygons)
|
return tuple(polygons)
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,6 @@ def process_move(
|
||||||
res = res_rel.translate(cp.x, cp.y)
|
res = res_rel.translate(cp.x, cp.y)
|
||||||
context.move_cache_abs[abs_key] = res
|
context.move_cache_abs[abs_key] = res
|
||||||
|
|
||||||
move_radius = params[0] if move_class == "bend90" else (params[1] if move_class == "sbend" else None)
|
|
||||||
add_node(
|
add_node(
|
||||||
parent,
|
parent,
|
||||||
res,
|
res,
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,15 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import math
|
import math
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from inire.constants import TOLERANCE_LINEAR
|
from inire.constants import TOLERANCE_LINEAR
|
||||||
from inire.geometry.components import MoveKind
|
|
||||||
from inire.geometry.primitives import Port
|
|
||||||
|
|
||||||
from ._astar_admission import process_move
|
from ._astar_admission import process_move
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from inire.geometry.components import MoveKind
|
||||||
|
from inire.geometry.primitives import Port
|
||||||
from ._astar_types import AStarContext, AStarMetrics, AStarNode, SearchRunConfig
|
from ._astar_types import AStarContext, AStarMetrics, AStarNode, SearchRunConfig
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -96,7 +99,7 @@ def _visible_straight_candidates(
|
||||||
return []
|
return []
|
||||||
|
|
||||||
collision_engine = context.cost_evaluator.collision_engine
|
collision_engine = context.cost_evaluator.collision_engine
|
||||||
candidates: set[int] = set()
|
tangent_candidates: set[int] = set()
|
||||||
for _, dist, length, dx, dy in sorted(scored)[:4]:
|
for _, dist, length, dx, dy in sorted(scored)[:4]:
|
||||||
angle = math.degrees(math.atan2(dy, dx))
|
angle = math.degrees(math.atan2(dy, dx))
|
||||||
corner_reach = collision_engine.ray_cast(current, angle, max_dist=dist + 0.05, net_width=net_width)
|
corner_reach = collision_engine.ray_cast(current, angle, max_dist=dist + 0.05, net_width=net_width)
|
||||||
|
|
@ -104,9 +107,9 @@ def _visible_straight_candidates(
|
||||||
continue
|
continue
|
||||||
qlen = int(round(length))
|
qlen = int(round(length))
|
||||||
if qlen > 0:
|
if qlen > 0:
|
||||||
candidates.add(qlen)
|
tangent_candidates.add(qlen)
|
||||||
|
|
||||||
return sorted(candidates, reverse=True)
|
return sorted(tangent_candidates, reverse=True)
|
||||||
|
|
||||||
|
|
||||||
def _previous_move_metadata(node: AStarNode) -> tuple[MoveKind | None, float | None]:
|
def _previous_move_metadata(node: AStarNode) -> tuple[MoveKind | None, float | None]:
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,14 @@ from __future__ import annotations
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from inire.geometry.components import BendCollisionModel, BendPhysicalGeometry
|
from inire.model import resolve_bend_geometry
|
||||||
from inire.model import RoutingOptions, RoutingProblem, resolve_bend_geometry
|
|
||||||
from inire.results import RouteMetrics
|
from inire.results import RouteMetrics
|
||||||
from inire.router.visibility import VisibilityManager
|
from inire.router.visibility import VisibilityManager
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from inire.geometry.components import ComponentResult
|
from inire.geometry.components import BendCollisionModel, BendPhysicalGeometry, ComponentResult
|
||||||
|
from inire.geometry.primitives import Port
|
||||||
|
from inire.model import RoutingOptions, RoutingProblem
|
||||||
from inire.router.cost import CostEvaluator
|
from inire.router.cost import CostEvaluator
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -61,7 +62,7 @@ class AStarNode:
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
port,
|
port: Port,
|
||||||
g_cost: float,
|
g_cost: float,
|
||||||
h_cost: float,
|
h_cost: float,
|
||||||
parent: AStarNode | None = None,
|
parent: AStarNode | None = None,
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,8 @@ from inire.router.refiner import PathRefiner
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from collections.abc import Callable, Sequence
|
from collections.abc import Callable, Sequence
|
||||||
|
|
||||||
|
from shapely.geometry import Polygon
|
||||||
|
|
||||||
from inire.geometry.components import ComponentResult
|
from inire.geometry.components import ComponentResult
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -48,8 +50,8 @@ class PathFinder:
|
||||||
self.accumulated_expanded_nodes: list[tuple[int, int, int]] = []
|
self.accumulated_expanded_nodes: list[tuple[int, int, int]] = []
|
||||||
|
|
||||||
def _install_path(self, net_id: str, path: Sequence[ComponentResult]) -> None:
|
def _install_path(self, net_id: str, path: Sequence[ComponentResult]) -> None:
|
||||||
all_geoms = []
|
all_geoms: list[Polygon] = []
|
||||||
all_dilated = []
|
all_dilated: list[Polygon] = []
|
||||||
for result in path:
|
for result in path:
|
||||||
all_geoms.extend(result.collision_geometry)
|
all_geoms.extend(result.collision_geometry)
|
||||||
all_dilated.extend(result.dilated_collision_geometry)
|
all_dilated.extend(result.dilated_collision_geometry)
|
||||||
|
|
@ -215,7 +217,7 @@ class PathFinder:
|
||||||
|
|
||||||
return RoutingResult(
|
return RoutingResult(
|
||||||
net_id=net_id,
|
net_id=net_id,
|
||||||
path=path,
|
path=tuple(path),
|
||||||
reached_target=reached_target,
|
reached_target=reached_target,
|
||||||
report=RoutingReport() if report is None else report,
|
report=RoutingReport() if report is None else report,
|
||||||
)
|
)
|
||||||
|
|
@ -276,7 +278,7 @@ class PathFinder:
|
||||||
report = self.context.cost_evaluator.collision_engine.verify_path_report(net_id, refined_path)
|
report = self.context.cost_evaluator.collision_engine.verify_path_report(net_id, refined_path)
|
||||||
state.results[net_id] = RoutingResult(
|
state.results[net_id] = RoutingResult(
|
||||||
net_id=net_id,
|
net_id=net_id,
|
||||||
path=refined_path,
|
path=tuple(refined_path),
|
||||||
reached_target=result.reached_target,
|
reached_target=result.reached_target,
|
||||||
report=report,
|
report=report,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,13 @@ import heapq
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from inire.constants import TOLERANCE_LINEAR
|
from inire.constants import TOLERANCE_LINEAR
|
||||||
from inire.geometry.primitives import Port
|
|
||||||
|
|
||||||
from ._astar_moves import expand_moves as _expand_moves
|
from ._astar_moves import expand_moves as _expand_moves
|
||||||
from ._astar_types import AStarContext, AStarMetrics, AStarNode as _AStarNode, SearchRunConfig
|
from ._astar_types import AStarContext, AStarMetrics, AStarNode as _AStarNode, SearchRunConfig
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from inire.geometry.components import ComponentResult
|
from inire.geometry.components import ComponentResult
|
||||||
|
from inire.geometry.primitives import Port
|
||||||
|
|
||||||
|
|
||||||
def _reconstruct_path(end_node: _AStarNode) -> list[ComponentResult]:
|
def _reconstruct_path(end_node: _AStarNode) -> list[ComponentResult]:
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,24 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from inire.geometry.collision import RoutingWorld
|
||||||
from inire.model import RoutingOptions, RoutingProblem
|
from inire.model import RoutingOptions, RoutingProblem
|
||||||
|
from inire.router._astar_types import AStarContext
|
||||||
|
from inire.router._router import PathFinder
|
||||||
|
from inire.router.cost import CostEvaluator
|
||||||
|
from inire.router.danger_map import DangerMap
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True, slots=True)
|
@dataclass(frozen=True, slots=True)
|
||||||
class RoutingStack:
|
class RoutingStack:
|
||||||
world: object
|
world: RoutingWorld
|
||||||
danger_map: object
|
danger_map: DangerMap
|
||||||
evaluator: object
|
evaluator: CostEvaluator
|
||||||
context: object
|
context: AStarContext
|
||||||
finder: object
|
finder: PathFinder
|
||||||
|
|
||||||
|
|
||||||
def build_routing_stack(problem: RoutingProblem, options: RoutingOptions) -> RoutingStack:
|
def build_routing_stack(problem: RoutingProblem, options: RoutingOptions) -> RoutingStack:
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ from inire.constants import TOLERANCE_LINEAR
|
||||||
from inire.model import ObjectiveWeights
|
from inire.model import ObjectiveWeights
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
from collections.abc import Sequence
|
||||||
from inire.geometry.collision import RoutingWorld
|
from inire.geometry.collision import RoutingWorld
|
||||||
from inire.geometry.components import ComponentResult, MoveKind
|
from inire.geometry.components import ComponentResult, MoveKind
|
||||||
from inire.geometry.primitives import Port
|
from inire.geometry.primitives import Port
|
||||||
|
|
@ -71,7 +72,7 @@ class CostEvaluator:
|
||||||
def set_target(self, target: Port) -> None:
|
def set_target(self, target: Port) -> None:
|
||||||
self._target_x = target.x
|
self._target_x = target.x
|
||||||
self._target_y = target.y
|
self._target_y = target.y
|
||||||
self._target_r = target.r
|
self._target_r = int(target.r)
|
||||||
rad = np.radians(target.r)
|
rad = np.radians(target.r)
|
||||||
self._target_cos = np.cos(rad)
|
self._target_cos = np.cos(rad)
|
||||||
self._target_sin = np.sin(rad)
|
self._target_sin = np.sin(rad)
|
||||||
|
|
@ -176,7 +177,7 @@ class CostEvaluator:
|
||||||
def path_cost(
|
def path_cost(
|
||||||
self,
|
self,
|
||||||
start_port: Port,
|
start_port: Port,
|
||||||
path: list[ComponentResult],
|
path: Sequence[ComponentResult],
|
||||||
*,
|
*,
|
||||||
weights: ObjectiveWeights | None = None,
|
weights: ObjectiveWeights | None = None,
|
||||||
) -> float:
|
) -> float:
|
||||||
|
|
|
||||||
|
|
@ -51,14 +51,14 @@ class DangerMap:
|
||||||
for poly in obstacles:
|
for poly in obstacles:
|
||||||
# Sample exterior
|
# Sample exterior
|
||||||
exterior = poly.exterior
|
exterior = poly.exterior
|
||||||
dist = 0
|
dist = 0.0
|
||||||
while dist < exterior.length:
|
while dist < exterior.length:
|
||||||
pt = exterior.interpolate(dist)
|
pt = exterior.interpolate(dist)
|
||||||
all_points.append((pt.x, pt.y))
|
all_points.append((pt.x, pt.y))
|
||||||
dist += self.resolution
|
dist += self.resolution
|
||||||
# Sample interiors (holes)
|
# Sample interiors (holes)
|
||||||
for interior in poly.interiors:
|
for interior in poly.interiors:
|
||||||
dist = 0
|
dist = 0.0
|
||||||
while dist < interior.length:
|
while dist < interior.length:
|
||||||
pt = interior.interpolate(dist)
|
pt = interior.interpolate(dist)
|
||||||
all_points.append((pt.x, pt.y))
|
all_points.append((pt.x, pt.y))
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import math
|
import math
|
||||||
from typing import TYPE_CHECKING, Any
|
from typing import TYPE_CHECKING, Any, Literal
|
||||||
|
|
||||||
from inire.geometry.component_overlap import components_overlap
|
from inire.geometry.component_overlap import components_overlap
|
||||||
from inire.geometry.components import Bend90, Straight
|
from inire.geometry.components import Bend90, Straight
|
||||||
|
|
@ -55,7 +55,7 @@ class PathRefiner:
|
||||||
ports.extend(comp.end_port for comp in path)
|
ports.extend(comp.end_port for comp in path)
|
||||||
return ports
|
return ports
|
||||||
|
|
||||||
def _to_local(self, start: Port, point: Port) -> tuple[int, int]:
|
def _to_local(self, start: Port, point: Port) -> tuple[float, float]:
|
||||||
dx = point.x - start.x
|
dx = point.x - start.x
|
||||||
dy = point.y - start.y
|
dy = point.y - start.y
|
||||||
if start.r == 0:
|
if start.r == 0:
|
||||||
|
|
@ -197,8 +197,8 @@ class PathRefiner:
|
||||||
if 0.01 < forward_length < min_straight - 0.01:
|
if 0.01 < forward_length < min_straight - 0.01:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
first_dir = "CCW" if side_extent > 0 else "CW"
|
first_dir: Literal["CW", "CCW"] = "CCW" if side_extent > 0 else "CW"
|
||||||
second_dir = "CW" if side_extent > 0 else "CCW"
|
second_dir: Literal["CW", "CCW"] = "CW" if side_extent > 0 else "CCW"
|
||||||
dilation = self.collision_engine.clearance / 2.0
|
dilation = self.collision_engine.clearance / 2.0
|
||||||
|
|
||||||
path: list[ComponentResult] = []
|
path: list[ComponentResult] = []
|
||||||
|
|
@ -288,10 +288,10 @@ class PathRefiner:
|
||||||
net_id: str,
|
net_id: str,
|
||||||
start: Port,
|
start: Port,
|
||||||
net_width: float,
|
net_width: float,
|
||||||
path: list[ComponentResult],
|
path: Sequence[ComponentResult],
|
||||||
) -> list[ComponentResult]:
|
) -> list[ComponentResult]:
|
||||||
if not path:
|
if not path:
|
||||||
return path
|
return list(path)
|
||||||
|
|
||||||
path = list(path)
|
path = list(path)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ class VisibilityManager:
|
||||||
self.corners: list[tuple[float, float]] = []
|
self.corners: list[tuple[float, float]] = []
|
||||||
self.corner_index = rtree.index.Index()
|
self.corner_index = rtree.index.Index()
|
||||||
self._corner_graph: dict[int, list[tuple[float, float, float]]] = {}
|
self._corner_graph: dict[int, list[tuple[float, float, float]]] = {}
|
||||||
self._point_visibility_cache: dict[tuple[int, int], list[tuple[float, float, float]]] = {}
|
self._point_visibility_cache: dict[tuple[int, int, int], list[tuple[float, float, float]]] = {}
|
||||||
self._built_static_version = -1
|
self._built_static_version = -1
|
||||||
self._build()
|
self._build()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from time import perf_counter
|
from time import perf_counter
|
||||||
from typing import Callable
|
from collections.abc import Callable
|
||||||
|
|
||||||
from shapely.geometry import Polygon, box
|
from shapely.geometry import Polygon, box
|
||||||
|
|
||||||
|
|
@ -154,7 +154,7 @@ def run_example_02() -> ScenarioOutcome:
|
||||||
"vertical_up": (Port(45, 10, 90), Port(45, 90, 90)),
|
"vertical_up": (Port(45, 10, 90), Port(45, 90, 90)),
|
||||||
"vertical_down": (Port(55, 90, 270), Port(55, 10, 270)),
|
"vertical_down": (Port(55, 90, 270), Port(55, 10, 270)),
|
||||||
}
|
}
|
||||||
widths = {net_id: 2.0 for net_id in netlist}
|
widths = dict.fromkeys(netlist, 2.0)
|
||||||
_, _, _, pathfinder = _build_routing_stack(
|
_, _, _, pathfinder = _build_routing_stack(
|
||||||
bounds=(0, 0, 100, 100),
|
bounds=(0, 0, 100, 100),
|
||||||
netlist=netlist,
|
netlist=netlist,
|
||||||
|
|
@ -232,7 +232,7 @@ def run_example_05() -> ScenarioOutcome:
|
||||||
"loop": (Port(100, 100, 90), Port(100, 80, 270)),
|
"loop": (Port(100, 100, 90), Port(100, 80, 270)),
|
||||||
"zig_zag": (Port(20, 150, 0), Port(180, 150, 0)),
|
"zig_zag": (Port(20, 150, 0), Port(180, 150, 0)),
|
||||||
}
|
}
|
||||||
widths = {net_id: 2.0 for net_id in netlist}
|
widths = dict.fromkeys(netlist, 2.0)
|
||||||
_, _, _, pathfinder = _build_routing_stack(
|
_, _, _, pathfinder = _build_routing_stack(
|
||||||
bounds=(0, 0, 200, 200),
|
bounds=(0, 0, 200, 200),
|
||||||
netlist=netlist,
|
netlist=netlist,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,3 @@
|
||||||
import pytest
|
|
||||||
import numpy
|
|
||||||
from shapely.geometry import Polygon
|
|
||||||
from inire import CongestionOptions, RoutingOptions, RoutingProblem, SearchOptions
|
from inire import CongestionOptions, RoutingOptions, RoutingProblem, SearchOptions
|
||||||
from inire.geometry.collision import RoutingWorld
|
from inire.geometry.collision import RoutingWorld
|
||||||
from inire.geometry.primitives import Port
|
from inire.geometry.primitives import Port
|
||||||
|
|
@ -10,7 +7,6 @@ from inire.router._astar_types import AStarContext
|
||||||
from inire.router._router import PathFinder
|
from inire.router._router import PathFinder
|
||||||
from inire.router.cost import CostEvaluator
|
from inire.router.cost import CostEvaluator
|
||||||
from inire.router.danger_map import DangerMap
|
from inire.router.danger_map import DangerMap
|
||||||
from inire import RoutingResult
|
|
||||||
|
|
||||||
|
|
||||||
def _build_pathfinder(
|
def _build_pathfinder(
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,15 @@ from __future__ import annotations
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import statistics
|
import statistics
|
||||||
from collections.abc import Callable
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from inire.tests.example_scenarios import SCENARIOS, ScenarioOutcome
|
from inire.tests.example_scenarios import SCENARIOS, ScenarioOutcome
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from collections.abc import Callable
|
||||||
|
|
||||||
|
|
||||||
RUN_PERFORMANCE = os.environ.get("INIRE_RUN_PERFORMANCE") == "1"
|
RUN_PERFORMANCE = os.environ.get("INIRE_RUN_PERFORMANCE") == "1"
|
||||||
PERFORMANCE_REPEATS = 3
|
PERFORMANCE_REPEATS = 3
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING, cast
|
||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
import numpy
|
import numpy
|
||||||
from shapely.geometry import MultiPolygon, Polygon
|
from shapely.geometry import MultiPolygon, Polygon
|
||||||
|
|
@ -129,7 +129,7 @@ def plot_danger_map(
|
||||||
if ax is None:
|
if ax is None:
|
||||||
fig, ax = plt.subplots(figsize=(10, 10))
|
fig, ax = plt.subplots(figsize=(10, 10))
|
||||||
else:
|
else:
|
||||||
fig = ax.get_figure()
|
fig = cast("Figure", ax.get_figure())
|
||||||
|
|
||||||
# Generate a temporary grid for visualization
|
# Generate a temporary grid for visualization
|
||||||
res = resolution if resolution is not None else max(1.0, (danger_map.maxx - danger_map.minx) / 200.0)
|
res = resolution if resolution is not None else max(1.0, (danger_map.maxx - danger_map.minx) / 200.0)
|
||||||
|
|
@ -155,12 +155,12 @@ def plot_danger_map(
|
||||||
# Need to transpose because grid is [x, y] and imshow expects [row, col] (y, x)
|
# Need to transpose because grid is [x, y] and imshow expects [row, col] (y, x)
|
||||||
im = ax.imshow(
|
im = ax.imshow(
|
||||||
grid.T,
|
grid.T,
|
||||||
origin='lower',
|
origin="lower",
|
||||||
extent=[danger_map.minx, danger_map.maxx, danger_map.miny, danger_map.maxy],
|
extent=(danger_map.minx, danger_map.maxx, danger_map.miny, danger_map.maxy),
|
||||||
cmap='YlOrRd',
|
cmap="YlOrRd",
|
||||||
alpha=0.6
|
alpha=0.6,
|
||||||
)
|
)
|
||||||
plt.colorbar(im, ax=ax, label='Danger Cost')
|
plt.colorbar(im, ax=ax, label="Danger Cost")
|
||||||
ax.set_title("Danger Map (Proximity Costs)")
|
ax.set_title("Danger Map (Proximity Costs)")
|
||||||
return fig, ax
|
return fig, ax
|
||||||
|
|
||||||
|
|
@ -176,7 +176,7 @@ def plot_expanded_nodes(
|
||||||
if ax is None:
|
if ax is None:
|
||||||
fig, ax = plt.subplots(figsize=(10, 10))
|
fig, ax = plt.subplots(figsize=(10, 10))
|
||||||
else:
|
else:
|
||||||
fig = ax.get_figure()
|
fig = cast("Figure", ax.get_figure())
|
||||||
|
|
||||||
if not nodes:
|
if not nodes:
|
||||||
return fig, ax
|
return fig, ax
|
||||||
|
|
@ -206,7 +206,7 @@ def plot_expansion_density(
|
||||||
if ax is None:
|
if ax is None:
|
||||||
fig, ax = plt.subplots(figsize=(12, 12))
|
fig, ax = plt.subplots(figsize=(12, 12))
|
||||||
else:
|
else:
|
||||||
fig = ax.get_figure()
|
fig = cast("Figure", ax.get_figure())
|
||||||
|
|
||||||
if not nodes:
|
if not nodes:
|
||||||
ax.text(0.5, 0.5, "No Expansion Data", ha='center', va='center', transform=ax.transAxes)
|
ax.text(0.5, 0.5, "No Expansion Data", ha='center', va='center', transform=ax.transAxes)
|
||||||
|
|
@ -224,14 +224,14 @@ def plot_expansion_density(
|
||||||
# Plot as image
|
# Plot as image
|
||||||
im = ax.imshow(
|
im = ax.imshow(
|
||||||
h.T,
|
h.T,
|
||||||
origin='lower',
|
origin="lower",
|
||||||
extent=[bounds[0], bounds[2], bounds[1], bounds[3]],
|
extent=(bounds[0], bounds[2], bounds[1], bounds[3]),
|
||||||
cmap='plasma',
|
cmap="plasma",
|
||||||
interpolation='nearest',
|
interpolation="nearest",
|
||||||
alpha=0.7
|
alpha=0.7,
|
||||||
)
|
)
|
||||||
|
|
||||||
plt.colorbar(im, ax=ax, label='Expansion Count')
|
plt.colorbar(im, ax=ax, label="Expansion Count")
|
||||||
ax.set_title("Search Expansion Density")
|
ax.set_title("Search Expansion Density")
|
||||||
ax.set_xlim(bounds[0], bounds[2])
|
ax.set_xlim(bounds[0], bounds[2])
|
||||||
ax.set_ylim(bounds[1], bounds[3])
|
ax.set_ylim(bounds[1], bounds[3])
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,18 @@ lint.ignore = [
|
||||||
"TRY003", # Long exception message
|
"TRY003", # Long exception message
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[tool.ruff.lint.per-file-ignores]
|
||||||
|
"inire/tests/*.py" = ["ANN", "ARG005", "PT009"]
|
||||||
|
|
||||||
|
[tool.mypy]
|
||||||
|
python_version = "3.11"
|
||||||
|
warn_unused_configs = true
|
||||||
|
exclude = ["^examples/", "^inire/tests/"]
|
||||||
|
|
||||||
|
[[tool.mypy.overrides]]
|
||||||
|
module = ["scipy.*"]
|
||||||
|
ignore_missing_imports = true
|
||||||
|
|
||||||
[tool.pytest.ini_options]
|
[tool.pytest.ini_options]
|
||||||
addopts = "-rsXx"
|
addopts = "-rsXx"
|
||||||
testpaths = ["inire"]
|
testpaths = ["inire"]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue