blockworx/router/
block.rs1use crate::router::{
2 coord::{CoordX, CoordY},
3 interval_overlap,
4 point::{Point, point},
5};
6
7pub const ROUTE_GUTTER: i32 = 1;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
14pub struct Block {
15 pub top_left: Point,
16 pub bottom_right: Point,
17}
18
19impl Block {
20 pub fn expand_x(&self, delta_x: impl Into<CoordX>) -> Self {
21 let delta_x: CoordX = delta_x.into();
22 Block {
23 top_left: point(self.top_left.x - delta_x, self.top_left.y),
24 bottom_right: point(self.bottom_right.x + delta_x, self.bottom_right.y),
25 }
26 }
27 pub fn expand_y(&self, delta_y: impl Into<CoordY>) -> Self {
28 let delta_y: CoordY = delta_y.into();
29 Block {
30 top_left: point(self.top_left.x, self.top_left.y - delta_y),
31 bottom_right: point(self.bottom_right.x, self.bottom_right.y + delta_y),
32 }
33 }
34 pub fn spans_y(&self, y: CoordY) -> bool {
35 self.top_left.y <= y && self.bottom_right.y >= y
36 }
37 pub fn spans_x(&self, x: CoordX) -> bool {
38 self.top_left.x <= x && self.bottom_right.x >= x
39 }
40 pub fn is_left_of(&self, x: CoordX) -> bool {
41 self.bottom_right.x < x
42 }
43 pub fn is_right_of(&self, x: CoordX) -> bool {
44 self.top_left.x > x
45 }
46 pub fn is_above(&self, y: CoordY) -> bool {
47 self.bottom_right.y < y
48 }
49 pub fn is_below(&self, y: CoordY) -> bool {
50 self.top_left.y > y
51 }
52 pub fn contains(&self, point: Point) -> bool {
53 self.spans_x(point.x) && self.spans_y(point.y)
54 }
55 pub fn intersects_edge(
56 &self,
57 start_point: impl Into<Point>,
58 end_point: impl Into<Point>,
59 ) -> bool {
60 let start_point: Point = start_point.into();
63 let end_point: Point = end_point.into();
64 if start_point.y == end_point.y {
65 let min_x = start_point.x.min(end_point.x);
66 let max_x = start_point.x.max(end_point.x);
67 self.spans_y(start_point.y)
70 && interval_overlap(min_x, max_x, self.top_left.x, self.bottom_right.x)
71 } else {
72 let min_y = start_point.y.min(end_point.y);
73 let max_y = start_point.y.max(end_point.y);
74 self.spans_x(start_point.x)
75 && interval_overlap(min_y, max_y, self.top_left.y, self.bottom_right.y)
76 }
77 }
78
79 pub fn hugs_wire(&self, a: impl Into<Point>, b: impl Into<Point>, gutter: i32) -> bool {
84 let (a, b) = (a.into(), b.into());
85 if a.x == b.x {
86 let near_edge = (a.x - self.top_left.x).abs() <= gutter
87 || (a.x - self.bottom_right.x).abs() <= gutter;
88 let (lo, hi) = (a.y.min(b.y), a.y.max(b.y));
89 near_edge && lo < self.bottom_right.y && self.top_left.y < hi
90 } else if a.y == b.y {
91 let near_edge = (a.y - self.top_left.y).abs() <= gutter
92 || (a.y - self.bottom_right.y).abs() <= gutter;
93 let (lo, hi) = (a.x.min(b.x), a.x.max(b.x));
94 near_edge && lo < self.bottom_right.x && self.top_left.x < hi
95 } else {
96 false
97 }
98 }
99}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104
105 fn block_0_0_to_10_10() -> Block {
106 Block {
107 top_left: point(0, 0),
108 bottom_right: point(10, 10),
109 }
110 }
111
112 #[test]
113 fn vertical_same_side_hug() {
114 let block = block_0_0_to_10_10();
115 assert!(block.hugs_wire(point(11, 2), point(11, 8), ROUTE_GUTTER));
116 }
117
118 #[test]
119 fn wide_offset_vertical_does_not_hug() {
120 let block = block_0_0_to_10_10();
121 assert!(!block.hugs_wire(point(12, 2), point(12, 8), ROUTE_GUTTER));
122 }
123
124 #[test]
125 fn horizontal_east_pin_leave_from_middle_slot_does_not_hug() {
126 let block = block_0_0_to_10_10();
127 assert!(!block.hugs_wire(point(11, 5), point(20, 5), ROUTE_GUTTER));
128 }
129
130 #[test]
131 fn horizontal_east_pin_leave_from_top_slot_does_not_hug() {
132 let block = block_0_0_to_10_10();
133 assert!(!block.hugs_wire(point(11, 1), point(20, 1), ROUTE_GUTTER));
134 }
135
136 #[test]
137 fn horizontal_hug_above_top_edge() {
138 let block = block_0_0_to_10_10();
139 assert!(block.hugs_wire(point(2, -1), point(8, -1), ROUTE_GUTTER));
140 }
141
142 #[test]
143 fn diagonal_never_hugs() {
144 let block = block_0_0_to_10_10();
145 assert!(!block.hugs_wire(point(11, 2), point(20, 8), ROUTE_GUTTER));
146 }
147}