Skip to main content

blockworx/router/
block.rs

1use crate::router::{
2    coord::{CoordX, CoordY},
3    interval_overlap,
4    point::{Point, point},
5};
6
7/// The 1-cell clearance that pins occupy just outside a block edge; wires route
8/// beyond it (the moat/channels start a further cell out). A straight wire may
9/// run alongside an edge no closer than this without being judged to hug.
10pub const ROUTE_GUTTER: i32 = 1;
11
12// A blocked rectangle - inclusive of the edges.
13#[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        // Check for intersection between the edge and the block.  The edge is
61        // either horizontal or vertical, so we can check for intersection by comparing the coordinates.
62        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            // The edge goes from [min_x,max_x], and we have the interval
68            // [self.top_left.x, self.bottom_right.x] - the edge intersects the block if the intervals overlap.
69            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    /// The straight wire `a → b` runs alongside one of this block's edges within
80    /// `gutter` cells — parallel to the edge and overlapping its span — rather than
81    /// merely leaving a pin perpendicular to it (a stub touches the gutter only at
82    /// its anchored endpoint). Such a wire hugs the block and should route around.
83    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}