1use crate::{
2 coord::{CoordX, CoordY},
3 interval_overlap,
4 point::{Point, point},
5};
6
7pub const ROUTE_GUTTER: i32 = 1;
11
12pub const MOAT_LANES: i32 = 5;
14
15pub const MOAT_REACH: i32 = (MOAT_LANES - 1) + 2;
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
25pub struct Block {
26 pub top_left: Point,
27 pub bottom_right: Point,
28}
29
30impl Block {
31 #[must_use]
32 pub fn expand_x(&self, delta_x: impl Into<CoordX>) -> Self {
33 let delta_x: CoordX = delta_x.into();
34 Block {
35 top_left: point(self.top_left.x - delta_x, self.top_left.y),
36 bottom_right: point(self.bottom_right.x + delta_x, self.bottom_right.y),
37 }
38 }
39 #[must_use]
40 pub fn expand_y(&self, delta_y: impl Into<CoordY>) -> Self {
41 let delta_y: CoordY = delta_y.into();
42 Block {
43 top_left: point(self.top_left.x, self.top_left.y - delta_y),
44 bottom_right: point(self.bottom_right.x, self.bottom_right.y + delta_y),
45 }
46 }
47 pub fn spans_y(&self, y: CoordY) -> bool {
48 self.top_left.y <= y && self.bottom_right.y >= y
49 }
50 pub fn spans_x(&self, x: CoordX) -> bool {
51 self.top_left.x <= x && self.bottom_right.x >= x
52 }
53 pub fn is_left_of(&self, x: CoordX) -> bool {
54 self.bottom_right.x < x
55 }
56 pub fn is_right_of(&self, x: CoordX) -> bool {
57 self.top_left.x > x
58 }
59 pub fn is_above(&self, y: CoordY) -> bool {
60 self.bottom_right.y < y
61 }
62 pub fn is_below(&self, y: CoordY) -> bool {
63 self.top_left.y > y
64 }
65 pub fn contains(&self, point: Point) -> bool {
66 self.spans_x(point.x) && self.spans_y(point.y)
67 }
68 pub fn intersects_edge(
69 &self,
70 start_point: impl Into<Point>,
71 end_point: impl Into<Point>,
72 ) -> bool {
73 self.edge_crossing(start_point, end_point).is_some()
74 }
75
76 pub fn edge_crossing(
81 &self,
82 a: impl Into<Point>,
83 b: impl Into<Point>,
84 ) -> Option<(Point, Point)> {
85 let (a, b): (Point, Point) = (a.into(), b.into());
86 if a.y == b.y {
87 let (lo, hi) = (a.x.min(b.x), a.x.max(b.x));
88 (self.spans_y(a.y) && interval_overlap(lo, hi, self.top_left.x, self.bottom_right.x))
89 .then(|| {
90 (
91 Point {
92 x: lo.max(self.top_left.x),
93 y: a.y,
94 },
95 Point {
96 x: hi.min(self.bottom_right.x),
97 y: a.y,
98 },
99 )
100 })
101 } else {
102 let (lo, hi) = (a.y.min(b.y), a.y.max(b.y));
103 (self.spans_x(a.x) && interval_overlap(lo, hi, self.top_left.y, self.bottom_right.y))
104 .then(|| {
105 (
106 Point {
107 x: a.x,
108 y: lo.max(self.top_left.y),
109 },
110 Point {
111 x: a.x,
112 y: hi.min(self.bottom_right.y),
113 },
114 )
115 })
116 }
117 }
118
119 pub fn hugs_wire(&self, a: impl Into<Point>, b: impl Into<Point>, gutter: i32) -> bool {
124 let (a, b) = (a.into(), b.into());
125 if a.x == b.x {
126 let near_edge = (a.x - self.top_left.x).abs() <= gutter
127 || (a.x - self.bottom_right.x).abs() <= gutter;
128 let (lo, hi) = (a.y.min(b.y), a.y.max(b.y));
129 near_edge && lo < self.bottom_right.y && self.top_left.y < hi
130 } else if a.y == b.y {
131 let near_edge = (a.y - self.top_left.y).abs() <= gutter
132 || (a.y - self.bottom_right.y).abs() <= gutter;
133 let (lo, hi) = (a.x.min(b.x), a.x.max(b.x));
134 near_edge && lo < self.bottom_right.x && self.top_left.x < hi
135 } else {
136 false
137 }
138 }
139}
140
141#[cfg(test)]
142mod tests {
143 use super::*;
144
145 fn block_0_0_to_10_10() -> Block {
146 Block {
147 top_left: point(0, 0),
148 bottom_right: point(10, 10),
149 }
150 }
151
152 #[test]
153 fn vertical_same_side_hug() {
154 let block = block_0_0_to_10_10();
155 assert!(block.hugs_wire(point(11, 2), point(11, 8), ROUTE_GUTTER));
156 }
157
158 #[test]
159 fn wide_offset_vertical_does_not_hug() {
160 let block = block_0_0_to_10_10();
161 assert!(!block.hugs_wire(point(12, 2), point(12, 8), ROUTE_GUTTER));
162 }
163
164 #[test]
165 fn horizontal_east_pin_leave_from_middle_slot_does_not_hug() {
166 let block = block_0_0_to_10_10();
167 assert!(!block.hugs_wire(point(11, 5), point(20, 5), ROUTE_GUTTER));
168 }
169
170 #[test]
171 fn horizontal_east_pin_leave_from_top_slot_does_not_hug() {
172 let block = block_0_0_to_10_10();
173 assert!(!block.hugs_wire(point(11, 1), point(20, 1), ROUTE_GUTTER));
174 }
175
176 #[test]
177 fn horizontal_hug_above_top_edge() {
178 let block = block_0_0_to_10_10();
179 assert!(block.hugs_wire(point(2, -1), point(8, -1), ROUTE_GUTTER));
180 }
181
182 #[test]
183 fn diagonal_never_hugs() {
184 let block = block_0_0_to_10_10();
185 assert!(!block.hugs_wire(point(11, 2), point(20, 8), ROUTE_GUTTER));
186 }
187}