1use std::cmp::Ordering;
4use std::collections::{BinaryHeap, HashMap, HashSet};
5
6use crate::records::sch::SchRecord;
7use crate::types::{Coord, CoordPoint, CoordRect};
8
9use super::layout::LayoutEngine;
10use super::types::{Direction, Grid, RoutingPath, WireSegment};
11
12const STRAIGHT_COST: i64 = 1;
14const TURN_COST: i64 = 2;
15const CROSSING_COST: i64 = 5;
16
17#[derive(Clone, Eq, PartialEq)]
19struct PathNode {
20 position: CoordPoint,
21 g_cost: i64, f_cost: i64, direction: Option<Direction>,
24}
25
26impl Ord for PathNode {
27 fn cmp(&self, other: &Self) -> Ordering {
28 other.f_cost.cmp(&self.f_cost)
30 }
31}
32
33impl PartialOrd for PathNode {
34 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
35 Some(self.cmp(other))
36 }
37}
38
39pub struct RoutingEngine {
41 grid: Grid,
43 obstacles: Vec<CoordRect>,
45 existing_wires: Vec<WireSegment>,
47 max_iterations: usize,
49}
50
51impl Default for RoutingEngine {
52 fn default() -> Self {
53 Self::new()
54 }
55}
56
57impl RoutingEngine {
58 pub fn new() -> Self {
60 Self {
61 grid: Grid::default(),
62 obstacles: Vec::new(),
63 existing_wires: Vec::new(),
64 max_iterations: 10000,
65 }
66 }
67
68 pub fn set_grid(&mut self, grid: Grid) {
70 self.grid = grid;
71 }
72
73 pub fn set_max_iterations(&mut self, max: usize) {
75 self.max_iterations = max;
76 }
77
78 pub fn update_obstacles(&mut self, primitives: &[SchRecord], layout: &LayoutEngine) {
80 self.obstacles.clear();
81 self.existing_wires.clear();
82
83 for component in layout.get_placed_components(primitives) {
85 let margin = Coord::from_mils(5.0);
87 self.obstacles.push(CoordRect::from_points(
88 component.bounds.location1.x + margin,
89 component.bounds.location1.y + margin,
90 component.bounds.location2.x - margin,
91 component.bounds.location2.y - margin,
92 ));
93 }
94
95 for record in primitives {
97 if let SchRecord::Wire(wire) = record {
98 for i in 0..wire.vertices.len().saturating_sub(1) {
99 let start = CoordPoint::from_raw(wire.vertices[i].0, wire.vertices[i].1);
100 let end = CoordPoint::from_raw(wire.vertices[i + 1].0, wire.vertices[i + 1].1);
101 self.existing_wires.push(WireSegment::new(start, end));
102 }
103 }
104 }
105 }
106
107 pub fn route(&self, start: CoordPoint, end: CoordPoint) -> Option<RoutingPath> {
109 let start = self.grid.snap(start);
110 let end = self.grid.snap(end);
111
112 if let Some(path) = self.try_direct_path(start, end) {
114 return Some(path);
115 }
116
117 if let Some(path) = self.try_l_path(start, end) {
119 return Some(path);
120 }
121
122 self.route_astar(start, end)
124 }
125
126 fn try_direct_path(&self, start: CoordPoint, end: CoordPoint) -> Option<RoutingPath> {
128 let segment = WireSegment::new(start, end);
129
130 if !segment.is_horizontal() && !segment.is_vertical() {
132 return None;
133 }
134
135 if self.segment_blocked(&segment) {
137 return None;
138 }
139
140 let mut path = RoutingPath::new();
141 path.add_segment(segment);
142 Some(path)
143 }
144
145 fn try_l_path(&self, start: CoordPoint, end: CoordPoint) -> Option<RoutingPath> {
147 let corner1 = CoordPoint::new(end.x, start.y);
149 let seg1_h = WireSegment::new(start, corner1);
150 let seg2_v = WireSegment::new(corner1, end);
151
152 if !self.segment_blocked(&seg1_h) && !self.segment_blocked(&seg2_v) {
153 let mut path = RoutingPath::new();
154 path.add_segment(seg1_h);
155 path.add_segment(seg2_v);
156 return Some(path);
157 }
158
159 let corner2 = CoordPoint::new(start.x, end.y);
161 let seg1_v = WireSegment::new(start, corner2);
162 let seg2_h = WireSegment::new(corner2, end);
163
164 if !self.segment_blocked(&seg1_v) && !self.segment_blocked(&seg2_h) {
165 let mut path = RoutingPath::new();
166 path.add_segment(seg1_v);
167 path.add_segment(seg2_h);
168 return Some(path);
169 }
170
171 None
172 }
173
174 fn segment_blocked(&self, segment: &WireSegment) -> bool {
176 let bounds = segment.bounds();
177
178 for obstacle in &self.obstacles {
179 if bounds.intersects(*obstacle) {
180 if self.line_intersects_rect(segment, obstacle) {
182 return true;
183 }
184 }
185 }
186
187 false
188 }
189
190 fn line_intersects_rect(&self, segment: &WireSegment, rect: &CoordRect) -> bool {
192 let (x1, y1) = (segment.start.x.to_raw(), segment.start.y.to_raw());
193 let (x2, y2) = (segment.end.x.to_raw(), segment.end.y.to_raw());
194 let (rx1, ry1) = (rect.location1.x.to_raw(), rect.location1.y.to_raw());
195 let (rx2, ry2) = (rect.location2.x.to_raw(), rect.location2.y.to_raw());
196
197 if segment.is_horizontal() {
199 let y = y1;
200 if y < ry1 || y > ry2 {
201 return false;
202 }
203 let (min_x, max_x) = if x1 < x2 { (x1, x2) } else { (x2, x1) };
204 return max_x > rx1 && min_x < rx2;
205 }
206
207 if segment.is_vertical() {
208 let x = x1;
209 if x < rx1 || x > rx2 {
210 return false;
211 }
212 let (min_y, max_y) = if y1 < y2 { (y1, y2) } else { (y2, y1) };
213 return max_y > ry1 && min_y < ry2;
214 }
215
216 false
219 }
220
221 fn route_astar(&self, start: CoordPoint, end: CoordPoint) -> Option<RoutingPath> {
223 let mut open_set = BinaryHeap::new();
224 let mut came_from: HashMap<(i32, i32), ((i32, i32), Direction)> = HashMap::new();
225 let mut g_score: HashMap<(i32, i32), i64> = HashMap::new();
226 let mut closed_set: HashSet<(i32, i32)> = HashSet::new();
227
228 let start_key = (start.x.to_raw(), start.y.to_raw());
229 let end_key = (end.x.to_raw(), end.y.to_raw());
230
231 g_score.insert(start_key, 0);
232
233 open_set.push(PathNode {
234 position: start,
235 g_cost: 0,
236 f_cost: self.heuristic(start, end),
237 direction: None,
238 });
239
240 let grid_step = self.grid.spacing.to_raw();
241 let directions = [
242 (Direction::Right, (grid_step, 0)),
243 (Direction::Left, (-grid_step, 0)),
244 (Direction::Up, (0, grid_step)),
245 (Direction::Down, (0, -grid_step)),
246 ];
247
248 let mut iterations = 0;
249
250 while let Some(current) = open_set.pop() {
251 iterations += 1;
252 if iterations > self.max_iterations {
253 return None; }
255
256 let current_key = (current.position.x.to_raw(), current.position.y.to_raw());
257
258 if current_key == end_key {
259 return Some(self.reconstruct_path(came_from, start_key, end_key));
260 }
261
262 if closed_set.contains(¤t_key) {
263 continue;
264 }
265 closed_set.insert(current_key);
266
267 for (dir, (dx, dy)) in &directions {
268 let next_pos = CoordPoint::from_raw(
269 current.position.x.to_raw() + dx,
270 current.position.y.to_raw() + dy,
271 );
272 let next_key = (next_pos.x.to_raw(), next_pos.y.to_raw());
273
274 if closed_set.contains(&next_key) {
275 continue;
276 }
277
278 if self.point_blocked(next_pos) && next_key != end_key {
280 continue;
281 }
282
283 let mut move_cost = STRAIGHT_COST;
285 if let Some(prev_dir) = current.direction {
286 if prev_dir != *dir {
287 move_cost += TURN_COST;
288 }
289 }
290
291 let test_segment = WireSegment::new(current.position, next_pos);
293 if self.crosses_wire(&test_segment) {
294 move_cost += CROSSING_COST;
295 }
296
297 let tentative_g = current.g_cost + move_cost;
298
299 if tentative_g < *g_score.get(&next_key).unwrap_or(&i64::MAX) {
300 came_from.insert(next_key, (current_key, *dir));
301 g_score.insert(next_key, tentative_g);
302
303 let f_cost = tentative_g + self.heuristic(next_pos, end);
304 open_set.push(PathNode {
305 position: next_pos,
306 g_cost: tentative_g,
307 f_cost,
308 direction: Some(*dir),
309 });
310 }
311 }
312 }
313
314 None }
316
317 fn heuristic(&self, from: CoordPoint, to: CoordPoint) -> i64 {
319 let dx = (to.x.to_raw() - from.x.to_raw()).abs() as i64;
320 let dy = (to.y.to_raw() - from.y.to_raw()).abs() as i64;
321 dx + dy
322 }
323
324 fn point_blocked(&self, point: CoordPoint) -> bool {
326 for obstacle in &self.obstacles {
327 if obstacle.contains(point) {
328 return true;
329 }
330 }
331 false
332 }
333
334 fn crosses_wire(&self, segment: &WireSegment) -> bool {
336 for wire in &self.existing_wires {
337 if self.segments_intersect(segment, wire) {
338 return true;
339 }
340 }
341 false
342 }
343
344 fn segments_intersect(&self, s1: &WireSegment, s2: &WireSegment) -> bool {
346 if (s1.is_horizontal() && s2.is_horizontal()) || (s1.is_vertical() && s2.is_vertical()) {
348 return false;
349 }
350
351 let (h_seg, v_seg) = if s1.is_horizontal() {
352 (s1, s2)
353 } else if s1.is_vertical() && s2.is_horizontal() {
354 (s2, s1)
355 } else {
356 return false;
357 };
358
359 let h_y = h_seg.start.y.to_raw();
360 let h_x1 = h_seg.start.x.to_raw().min(h_seg.end.x.to_raw());
361 let h_x2 = h_seg.start.x.to_raw().max(h_seg.end.x.to_raw());
362
363 let v_x = v_seg.start.x.to_raw();
364 let v_y1 = v_seg.start.y.to_raw().min(v_seg.end.y.to_raw());
365 let v_y2 = v_seg.start.y.to_raw().max(v_seg.end.y.to_raw());
366
367 v_x > h_x1 && v_x < h_x2 && h_y > v_y1 && h_y < v_y2
369 }
370
371 fn reconstruct_path(
373 &self,
374 came_from: HashMap<(i32, i32), ((i32, i32), Direction)>,
375 start: (i32, i32),
376 end: (i32, i32),
377 ) -> RoutingPath {
378 let mut path = RoutingPath::new();
379 let mut vertices = vec![CoordPoint::from_raw(end.0, end.1)];
380 let mut current = end;
381
382 while current != start {
383 if let Some((prev, _dir)) = came_from.get(¤t) {
384 vertices.push(CoordPoint::from_raw(prev.0, prev.1));
385 current = *prev;
386 } else {
387 break;
388 }
389 }
390
391 vertices.reverse();
392
393 let simplified = self.simplify_vertices(&vertices);
395
396 for i in 0..simplified.len().saturating_sub(1) {
397 path.add_segment(WireSegment::new(simplified[i], simplified[i + 1]));
398 }
399
400 path
401 }
402
403 fn simplify_vertices(&self, vertices: &[CoordPoint]) -> Vec<CoordPoint> {
405 if vertices.len() <= 2 {
406 return vertices.to_vec();
407 }
408
409 let mut simplified = vec![vertices[0]];
410
411 for i in 1..vertices.len() - 1 {
412 let prev = simplified.last().unwrap();
413 let curr = &vertices[i];
414 let next = &vertices[i + 1];
415
416 let dx1 = curr.x.to_raw() - prev.x.to_raw();
418 let dy1 = curr.y.to_raw() - prev.y.to_raw();
419 let dx2 = next.x.to_raw() - curr.x.to_raw();
420 let dy2 = next.y.to_raw() - curr.y.to_raw();
421
422 let collinear = (dx1 == 0 && dx2 == 0) || (dy1 == 0 && dy2 == 0);
424 if !collinear {
425 simplified.push(*curr);
426 }
427 }
428
429 simplified.push(*vertices.last().unwrap());
430 simplified
431 }
432
433 pub fn find_junctions(&self, path: &RoutingPath) -> Vec<CoordPoint> {
435 let mut junctions = Vec::new();
436
437 for segment in &path.segments {
438 for wire in &self.existing_wires {
439 if let Some(intersection) = self.find_intersection(segment, wire) {
440 if intersection != segment.start && intersection != segment.end {
442 junctions.push(intersection);
443 }
444 }
445 }
446 }
447
448 junctions.sort_by_key(|p| (p.x.to_raw(), p.y.to_raw()));
450 junctions.dedup_by(|a, b| a.x == b.x && a.y == b.y);
451
452 junctions
453 }
454
455 fn find_intersection(&self, s1: &WireSegment, s2: &WireSegment) -> Option<CoordPoint> {
457 if (s1.is_horizontal() && s2.is_horizontal()) || (s1.is_vertical() && s2.is_vertical()) {
459 return None;
460 }
461
462 let (h_seg, v_seg) = if s1.is_horizontal() {
463 (s1, s2)
464 } else if s1.is_vertical() && s2.is_horizontal() {
465 (s2, s1)
466 } else {
467 return None;
468 };
469
470 let h_y = h_seg.start.y;
471 let h_x1 = h_seg.start.x.min(h_seg.end.x);
472 let h_x2 = h_seg.start.x.max(h_seg.end.x);
473
474 let v_x = v_seg.start.x;
475 let v_y1 = v_seg.start.y.min(v_seg.end.y);
476 let v_y2 = v_seg.start.y.max(v_seg.end.y);
477
478 if v_x >= h_x1 && v_x <= h_x2 && h_y >= v_y1 && h_y <= v_y2 {
480 Some(CoordPoint::new(v_x, h_y))
481 } else {
482 None
483 }
484 }
485
486 pub fn auto_route_all(
488 &mut self,
489 connections: &[(CoordPoint, CoordPoint)],
490 ) -> Vec<Option<RoutingPath>> {
491 let mut results = Vec::new();
492
493 let mut sorted_connections: Vec<_> = connections.iter().enumerate().collect();
495 sorted_connections.sort_by_key(|(_, (start, end))| {
496 let dx = (end.x - start.x).abs().to_raw();
497 let dy = (end.y - start.y).abs().to_raw();
498 dx + dy
499 });
500
501 for (_, (start, end)) in sorted_connections {
502 let path = self.route(*start, *end);
503
504 if let Some(ref p) = path {
506 for segment in &p.segments {
507 self.existing_wires.push(*segment);
508 }
509 }
510
511 results.push(path);
512 }
513
514 results
515 }
516}