|
| 1 | +import Graphs: a_star, grid, weights, add_edge! |
| 2 | +import DeviceLayout: Rectangle, Polygon |
| 3 | + |
| 4 | +abstract type PathfindingRule{T <: RouteRule} <: RouteRule end |
| 5 | + |
| 6 | +struct AStarRouting{T, U <: Coordinate} <: PathfindingRule{T} |
| 7 | + leg_rule::T |
| 8 | + domain::Rectangle{U} |
| 9 | + grid_step::U |
| 10 | + exclusion_fn::Function |
| 11 | + exclusion::Vector{Polygon{U}} |
| 12 | + excluded |
| 13 | + |
| 14 | + function AStarRouting(l, d::Rectangle{U}, s, f::Function) where {U} |
| 15 | + return new{typeof(l), U}(l, d, s, f, Polygon{U}[], []) |
| 16 | + end |
| 17 | +end |
| 18 | + |
| 19 | +leg_rule(pr::PathfindingRule) = pr.leg_rule |
| 20 | + |
| 21 | +function _route_leg!( |
| 22 | + p::Path, |
| 23 | + next::Point, |
| 24 | + nextdir, |
| 25 | + rule::PathfindingRule, |
| 26 | + sty::Paths.Style=Paths.contstyle1(p) |
| 27 | +) |
| 28 | + # Just use underlying rule |
| 29 | + return _route_leg!(p, next, nextdir, leg_rule(rule), sty) |
| 30 | +end |
| 31 | + |
| 32 | +function reconcile!( |
| 33 | + path::Path, |
| 34 | + endpoint::Point, |
| 35 | + end_direction, |
| 36 | + rule::AStarRouting, |
| 37 | + waypoints, |
| 38 | + waydirs; |
| 39 | + initialize_waydirs=false |
| 40 | +) |
| 41 | + # Calculate and insert waypoints |
| 42 | + # Construct grid graph |
| 43 | + bbox = bounds(rule.domain) |
| 44 | + grid_x = range(bbox.ll.x, step=rule.grid_step, stop=bbox.ur.x) |
| 45 | + grid_y = range(bbox.ll.y, step=rule.grid_step, stop=bbox.ur.y) |
| 46 | + in_poly = DeviceLayout.gridpoints_in_polygon(rule.exclusion, grid_x, grid_y) |
| 47 | + nx = length(grid_x) |
| 48 | + ny = length(grid_y) |
| 49 | + grid_graph = grid((nx, ny)) |
| 50 | + |
| 51 | + # Functions to convert from real space/cartesian indices to graph indices |
| 52 | + v_to_ix_iy(v::Int) = Tuple(CartesianIndices((1:nx, 1:ny))[v]) |
| 53 | + ix_iy_to_v(ix::Int, iy::Int) = LinearIndices((1:nx, 1:ny))[ix, iy] |
| 54 | + v_to_xy(v::Int) = Point(grid_x[v_to_ix_iy(v)[1]], grid_y[v_to_ix_iy(v)[2]]) |
| 55 | + xy_to_v(p::Point) = ix_iy_to_v( |
| 56 | + findfirst(xi -> xi + rule.grid_step / 2 > p.x, grid_x), |
| 57 | + findfirst(yi -> yi + rule.grid_step / 2 > p.y, grid_y) |
| 58 | + ) |
| 59 | + |
| 60 | + # Set up pathfinding |
| 61 | + start_v = xy_to_v(p0(path)) |
| 62 | + end_v = xy_to_v(endpoint) |
| 63 | + source = xy_to_v(p0(path) + # pathfinding starts with grid box index in front of start |
| 64 | + rule.grid_step * Point(cos(α0(path)), sin(α0(path)))) |
| 65 | + target = xy_to_v(endpoint - # pathfinding ends with grid box index in front of end |
| 66 | + rule.grid_step * Point(cos(end_direction), sin(end_direction))) |
| 67 | + heuristic(v) = uconvert(NoUnits, norm(endpoint - v_to_xy(v)) / rule.grid_step) |
| 68 | + |
| 69 | + w = Matrix{Float64}(weights(grid_graph)) |
| 70 | + ## Add diagonals |
| 71 | + # for v1 = 1:(nx*ny) |
| 72 | + # for v2 = (v1+1):(nx*ny) |
| 73 | + # ix1, iy1 = v_to_ix_iy(v1) |
| 74 | + # ix2, iy2 = v_to_ix_iy(v2) |
| 75 | + # if abs(ix1 - ix2) == 1 && abs(iy1 - iy2) == 1 |
| 76 | + # add_edge!(grid_graph, v1, v2) |
| 77 | + # w[v1, v2] = w[v2, v1] = sqrt(2) |
| 78 | + # end |
| 79 | + # end |
| 80 | + # end |
| 81 | + |
| 82 | + ## Set edges to excluded grid cells to infinite weight |
| 83 | + ## (deleting vertices would renumber them) |
| 84 | + in_poly[start_v] = true |
| 85 | + in_poly[end_v] = true |
| 86 | + for (v, excluded) in enumerate(in_poly) |
| 87 | + !excluded && continue |
| 88 | + w[v, :] .= Inf |
| 89 | + w[:, v] .= Inf |
| 90 | + end |
| 91 | + |
| 92 | + # Find shortest path |
| 93 | + ## Not deterministic! |
| 94 | + edges = a_star(grid_graph, source, target, w, heuristic) |
| 95 | + # Create waypoints |
| 96 | + ## Whenever we change direction, add a waypoint |
| 97 | + edge_direction(v1, v2) = (v_to_ix_iy(v2) .- v_to_ix_iy(v1)) |
| 98 | + current_direction = edge_direction(start_v, source) |
| 99 | + for e in edges |
| 100 | + direction = edge_direction(e.src, e.dst) |
| 101 | + if direction != current_direction |
| 102 | + push!(waypoints, (v_to_xy(e.src) + v_to_xy(e.dst)) / 2) |
| 103 | + push!(waydirs, atan(direction[2], direction[1])) |
| 104 | + current_direction = direction |
| 105 | + end |
| 106 | + end |
| 107 | + final_direction = edge_direction(target, end_v) |
| 108 | + if current_direction == final_direction |
| 109 | + pop!(waypoints) # Don't need the last one |
| 110 | + pop!(waydirs) |
| 111 | + end |
| 112 | +end |
| 113 | + |
| 114 | +function _update_rule!(sch, node, rule::RouteRule) end |
| 115 | + |
| 116 | +function _update_rule!(sch, node, rule::PathfindingRule) |
| 117 | + for planned_node in keys(sch.ref_dict) # Only components already planned |
| 118 | + (planned_node in rule.excluded) && continue |
| 119 | + append!( |
| 120 | + rule.exclusion, |
| 121 | + reduce( |
| 122 | + vcat, |
| 123 | + DeviceLayout.to_polygons.( |
| 124 | + transformation( |
| 125 | + sch, |
| 126 | + planned_node |
| 127 | + ).(flatten(rule.exclusion_fn(planned_node.component)).elements) |
| 128 | + ), |
| 129 | + init=Polygon{coordinatetype(sch)}[] |
| 130 | + ) |
| 131 | + ) |
| 132 | + push!(rule.excluded, planned_node) |
| 133 | + end |
| 134 | +end |
0 commit comments