Skip to main content

physdes/
vlsi_ops.rs

1//! VLSI-specific geometric operations
2//!
3//! This module provides operations commonly used in VLSI physical design and layout.
4
5use crate::{Point, Polygon};
6
7/// Represents a rectangle in 2D space
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct Rectangle<T> {
10    pub min: Point<T, T>,
11    pub max: Point<T, T>,
12}
13
14impl<T: Clone + Ord + Copy + std::ops::Add<Output = T>> Rectangle<T> {
15    /// Creates a new rectangle from min and max points
16    ///
17    /// # Arguments
18    ///
19    /// * `min` - The minimum (bottom-left) corner
20    /// * `max` - The maximum (top-right) corner
21    ///
22    /// # Examples
23    ///
24    /// ```
25    /// use physdes::{Point, vlsi_ops::Rectangle};
26    ///
27    /// let rect = Rectangle::new(
28    ///     Point::new(0, 0),
29    ///     Point::new(10, 20)
30    /// );
31    /// ```
32    pub fn new(min: Point<T, T>, max: Point<T, T>) -> Self {
33        assert!(min.xcoord <= max.xcoord && min.ycoord <= max.ycoord);
34        Rectangle { min, max }
35    }
36
37    /// Creates a rectangle from origin, width, and height
38    pub fn from_dimensions(origin: Point<T, T>, width: T, height: T) -> Self {
39        Rectangle::new(
40            origin,
41            Point::new(origin.xcoord + width, origin.ycoord + height),
42        )
43    }
44
45    /// Checks if this rectangle overlaps with another
46    ///
47    /// # Examples
48    ///
49    /// ```
50    /// use physdes::{Point, vlsi_ops::Rectangle};
51    ///
52    /// let rect1 = Rectangle::new(Point::new(0, 0), Point::new(10, 10));
53    /// let rect2 = Rectangle::new(Point::new(5, 5), Point::new(15, 15));
54    /// assert!(rect1.overlaps(&rect2));
55    ///
56    /// let rect3 = Rectangle::new(Point::new(20, 20), Point::new(30, 30));
57    /// assert!(!rect1.overlaps(&rect3));
58    /// ```
59    pub fn overlaps(&self, other: &Self) -> bool {
60        self.min.xcoord <= other.max.xcoord
61            && self.max.xcoord >= other.min.xcoord
62            && self.min.ycoord <= other.max.ycoord
63            && self.max.ycoord >= other.min.ycoord
64    }
65
66    /// Checks if this rectangle contains another rectangle
67    pub fn contains(&self, other: &Self) -> bool {
68        self.min.xcoord <= other.min.xcoord
69            && self.max.xcoord >= other.max.xcoord
70            && self.min.ycoord <= other.min.ycoord
71            && self.max.ycoord >= other.max.ycoord
72    }
73
74    /// Checks if this rectangle contains a point
75    pub fn contains_point(&self, point: &Point<T, T>) -> bool {
76        point.xcoord >= self.min.xcoord
77            && point.xcoord <= self.max.xcoord
78            && point.ycoord >= self.min.ycoord
79            && point.ycoord <= self.max.ycoord
80    }
81
82    /// Computes the intersection with another rectangle
83    ///
84    /// Returns `None` if the rectangles don't overlap
85    pub fn intersect(&self, other: &Self) -> Option<Self>
86    where
87        T: std::ops::Add<Output = T>,
88    {
89        if !self.overlaps(other) {
90            return None;
91        }
92
93        Some(Rectangle::new(
94            Point::new(
95                self.min.xcoord.max(other.min.xcoord),
96                self.min.ycoord.max(other.min.ycoord),
97            ),
98            Point::new(
99                self.max.xcoord.min(other.max.xcoord),
100                self.max.ycoord.min(other.max.ycoord),
101            ),
102        ))
103    }
104
105    /// Computes the area of the rectangle
106    pub fn area(&self) -> T
107    where
108        T: std::ops::Sub<Output = T> + std::ops::Mul<Output = T>,
109    {
110        let width = self.max.xcoord - self.min.xcoord;
111        let height = self.max.ycoord - self.min.ycoord;
112        width * height
113    }
114
115    /// Computes the bounding rectangle of two rectangles
116    pub fn bounding_rect(&self, other: &Self) -> Self {
117        Rectangle::new(
118            Point::new(
119                self.min.xcoord.min(other.min.xcoord),
120                self.min.ycoord.min(other.min.ycoord),
121            ),
122            Point::new(
123                self.max.xcoord.max(other.max.xcoord),
124                self.max.ycoord.max(other.max.ycoord),
125            ),
126        )
127    }
128
129    /// Converts to a Polygon
130    pub fn to_polygon(&self) -> Polygon<T>
131    where
132        T: Clone + std::ops::Add<Output = T> + num_traits::Num + std::ops::AddAssign + Ord,
133    {
134        Polygon::new(&[
135            self.min,
136            Point::new(self.max.xcoord, self.min.ycoord),
137            self.max,
138            Point::new(self.min.xcoord, self.max.ycoord),
139        ])
140    }
141}
142
143/// Detects if any pair of rectangles overlap using the line sweep algorithm.
144///
145/// The algorithm uses a sweep line approach:
146/// 1. Create events for left and right edges of each rectangle
147/// 2. Sort events by x-coordinate
148/// 3. Sweep from left to right, maintaining active rectangles
149/// 4. Check y-overlap when a new rectangle becomes active
150///
151/// # Arguments
152///
153/// * `rectangles` - A slice of rectangles to check for overlaps
154///
155/// # Returns
156///
157/// The indices of two overlapping rectangles if found, otherwise `None`
158///
159/// # Examples
160///
161/// ```
162/// use physdes::{Point, vlsi_ops::{Rectangle, detect_overlap}};
163///
164/// let rects = vec![
165///     Rectangle::new(Point::new(0, 0), Point::new(10, 10)),
166///     Rectangle::new(Point::new(5, 5), Point::new(15, 15)),
167///     Rectangle::new(Point::new(20, 20), Point::new(30, 30)),
168/// ];
169/// let result = detect_overlap(&rects);
170/// assert!(result.is_some());
171/// assert_eq!(result.unwrap(), (0, 1));
172/// ```
173pub fn detect_overlap<T>(rectangles: &[Rectangle<T>]) -> Option<(usize, usize)>
174where
175    T: Copy + Ord,
176{
177    if rectangles.len() < 2 {
178        return None;
179    }
180
181    // Create events: (x_coord, is_start, rect_index)
182    let mut events: Vec<(T, bool, usize)> = Vec::with_capacity(rectangles.len() * 2);
183    for (idx, rect) in rectangles.iter().enumerate() {
184        events.push((rect.min.xcoord, true, idx));
185        events.push((rect.max.xcoord, false, idx));
186    }
187
188    events.sort_by(|a, b| a.0.cmp(&b.0));
189
190    // Active rectangles: (rect_index, y_min, y_max)
191    let mut active: Vec<(usize, T, T)> = Vec::new();
192
193    for &(_x, is_start, idx) in &events {
194        let rect = &rectangles[idx];
195
196        if is_start {
197            // Check y-overlap with all active rectangles
198            for &(other_idx, other_y_min, other_y_max) in &active {
199                if rect.min.ycoord <= other_y_max && other_y_min <= rect.max.ycoord {
200                    return Some((other_idx, idx));
201                }
202            }
203            active.push((idx, rect.min.ycoord, rect.max.ycoord));
204        } else {
205            // O(1) removal: swap with back and pop
206            for i in 0..active.len() {
207                if active[i].0 == idx {
208                    active.swap_remove(i);
209                    break;
210                }
211            }
212        }
213    }
214
215    None
216}
217
218/// Calculates Manhattan distance between two points
219///
220/// The Manhattan distance is the sum of the absolute differences of coordinates.
221/// This is commonly used in VLSI routing where wires can only run horizontally or vertically.
222///
223/// # Arguments
224///
225/// * `p1` - First point
226/// * `p2` - Second point
227///
228/// # Examples
229///
230/// ```
231/// use physdes::{Point, vlsi_ops::manhattan_distance};
232///
233/// let p1 = Point::new(0, 0);
234/// let p2 = Point::new(3, 4);
235/// let dist = manhattan_distance(&p1, &p2);
236/// assert_eq!(dist, 7); // |3-0| + |4-0| = 7
237/// ```
238pub fn manhattan_distance<T>(p1: &Point<T, T>, p2: &Point<T, T>) -> T
239where
240    T: Ord + Copy + std::ops::Sub<Output = T> + std::ops::Add<Output = T>,
241{
242    let dx = if p1.xcoord > p2.xcoord {
243        p1.xcoord - p2.xcoord
244    } else {
245        p2.xcoord - p1.xcoord
246    };
247    let dy = if p1.ycoord > p2.ycoord {
248        p1.ycoord - p2.ycoord
249    } else {
250        p2.ycoord - p1.ycoord
251    };
252    dx + dy
253}
254
255/// Checks if two rectangles satisfy minimum spacing requirements
256///
257/// # Arguments
258///
259/// * `rect1` - First rectangle
260/// * `rect2` - Second rectangle
261/// * `min_spacing` - Minimum required spacing
262///
263/// # Examples
264///
265/// ```
266/// use physdes::{Point, vlsi_ops::{Rectangle, check_spacing}};
267///
268/// let rect1 = Rectangle::new(Point::new(0, 0), Point::new(10, 10));
269/// let rect2 = Rectangle::new(Point::new(10, 0), Point::new(20, 10));
270///
271/// // These rectangles touch (spacing = 0)
272/// assert!(!check_spacing(&rect1, &rect2, 1));
273///
274/// let rect3 = Rectangle::new(Point::new(12, 0), Point::new(22, 10));
275/// // These have spacing of 2
276/// assert!(check_spacing(&rect1, &rect3, 1));
277/// ```
278pub fn check_spacing<T>(rect1: &Rectangle<T>, rect2: &Rectangle<T>, min_spacing: T) -> bool
279where
280    T: Ord + Copy + std::ops::Sub<Output = T> + std::ops::Add<Output = T>,
281{
282    if rect1.overlaps(rect2) {
283        return false;
284    }
285
286    // Calculate horizontal spacing
287    let horiz_spacing = if rect1.max.xcoord <= rect2.min.xcoord {
288        rect2.min.xcoord - rect1.max.xcoord
289    } else if rect2.max.xcoord <= rect1.min.xcoord {
290        rect1.min.xcoord - rect2.max.xcoord
291    } else {
292        T::clone(&min_spacing) // Overlapping in x, check y
293    };
294
295    // Calculate vertical spacing
296    let vert_spacing = if rect1.max.ycoord <= rect2.min.ycoord {
297        rect2.min.ycoord - rect1.max.ycoord
298    } else if rect2.max.ycoord <= rect1.min.ycoord {
299        rect1.min.ycoord - rect2.max.ycoord
300    } else {
301        T::clone(&min_spacing) // Overlapping in y, check x
302    };
303
304    horiz_spacing >= min_spacing && vert_spacing >= min_spacing
305}
306
307/// Computes the minimum bounding rectangle of a set of rectangles
308///
309/// # Arguments
310///
311/// * `rects` - Slice of rectangles
312///
313/// # Examples
314///
315/// ```
316/// use physdes::{Point, vlsi_ops::{Rectangle, bounding_rect}};
317///
318/// let rects = vec![
319///     Rectangle::new(Point::new(0, 0), Point::new(10, 10)),
320///     Rectangle::new(Point::new(5, 5), Point::new(15, 15)),
321/// ];
322///
323/// let bbox = bounding_rect(&rects).unwrap();
324/// assert_eq!(bbox.min, Point::new(0, 0));
325/// assert_eq!(bbox.max, Point::new(15, 15));
326/// ```
327pub fn bounding_rect<T>(rects: &[Rectangle<T>]) -> Option<Rectangle<T>>
328where
329    T: Ord + Copy + std::ops::Add<Output = T>,
330{
331    if rects.is_empty() {
332        return None;
333    }
334
335    let mut min_x = rects[0].min.xcoord;
336    let mut min_y = rects[0].min.ycoord;
337    let mut max_x = rects[0].max.xcoord;
338    let mut max_y = rects[0].max.ycoord;
339
340    for rect in &rects[1..] {
341        min_x = min_x.min(rect.min.xcoord);
342        min_y = min_y.min(rect.min.ycoord);
343        max_x = max_x.max(rect.max.xcoord);
344        max_y = max_y.max(rect.max.ycoord);
345    }
346
347    Some(Rectangle::new(
348        Point::new(min_x, min_y),
349        Point::new(max_x, max_y),
350    ))
351}
352
353/// Computes total area covered by rectangles, accounting for overlaps
354///
355/// # Examples
356///
357/// ```
358/// use physdes::{Point, vlsi_ops::{Rectangle, total_area}};
359///
360/// let rects = vec![
361///     Rectangle::new(Point::new(0, 0), Point::new(10, 10)),
362///     Rectangle::new(Point::new(5, 5), Point::new(15, 15)),
363/// ];
364///
365/// let area = total_area(&rects);
366/// // First rect: 100, second rect: 100, overlap: 25, total: 175
367/// assert_eq!(area, 175);
368/// ```
369pub fn total_area(rects: &[Rectangle<i32>]) -> i32 {
370    if rects.is_empty() {
371        return 0;
372    }
373
374    let mut total = 0;
375    for (i, rect) in rects.iter().enumerate() {
376        total += rect.area();
377        // Subtract overlaps with previously counted rectangles
378        for other in &rects[..i] {
379            if let Some(intersection) = rect.intersect(other) {
380                total -= intersection.area();
381            }
382        }
383    }
384
385    total
386}
387
388#[cfg(test)]
389mod tests {
390    use super::*;
391
392    #[test]
393    fn test_rectangle_creation() {
394        let rect = Rectangle::new(Point::new(0, 0), Point::new(10, 20));
395        assert_eq!(rect.min, Point::new(0, 0));
396        assert_eq!(rect.max, Point::new(10, 20));
397    }
398
399    #[test]
400    fn test_rectangle_overlap() {
401        let rect1 = Rectangle::new(Point::new(0, 0), Point::new(10, 10));
402        let rect2 = Rectangle::new(Point::new(5, 5), Point::new(15, 15));
403        assert!(rect1.overlaps(&rect2));
404
405        let rect3 = Rectangle::new(Point::new(20, 20), Point::new(30, 30));
406        assert!(!rect1.overlaps(&rect3));
407    }
408
409    #[test]
410    fn test_rectangle_area() {
411        let rect = Rectangle::new(Point::new(0, 0), Point::new(10, 20));
412        assert_eq!(rect.area(), 200);
413    }
414
415    #[test]
416    fn test_manhattan_distance() {
417        let p1 = Point::new(0, 0);
418        let p2 = Point::new(3, 4);
419        assert_eq!(manhattan_distance(&p1, &p2), 7);
420
421        let p3 = Point::new(5, 0);
422        assert_eq!(manhattan_distance(&p1, &p3), 5);
423    }
424
425    #[test]
426    fn test_check_spacing() {
427        let rect1 = Rectangle::new(Point::new(0, 0), Point::new(10, 10));
428        let rect2 = Rectangle::new(Point::new(10, 0), Point::new(20, 10));
429        assert!(!check_spacing(&rect1, &rect2, 1));
430
431        let rect3 = Rectangle::new(Point::new(12, 0), Point::new(22, 10));
432        assert!(check_spacing(&rect1, &rect3, 1));
433    }
434
435    #[test]
436    fn test_bounding_rect() {
437        let rects = vec![
438            Rectangle::new(Point::new(0, 0), Point::new(10, 10)),
439            Rectangle::new(Point::new(5, 5), Point::new(15, 15)),
440        ];
441
442        let bbox = bounding_rect(&rects).unwrap();
443        assert_eq!(bbox.min, Point::new(0, 0));
444        assert_eq!(bbox.max, Point::new(15, 15));
445    }
446
447    #[test]
448    fn test_total_area() {
449        let rects = vec![
450            Rectangle::new(Point::new(0, 0), Point::new(10, 10)),
451            Rectangle::new(Point::new(5, 5), Point::new(15, 15)),
452        ];
453
454        let area = total_area(&rects);
455        assert_eq!(area, 175);
456    }
457
458    #[test]
459    fn test_from_dimensions() {
460        let origin = Point::new(5, 10);
461        let rect = Rectangle::from_dimensions(origin, 20, 30);
462        assert_eq!(rect.min, origin);
463        assert_eq!(rect.max, Point::new(25, 40));
464    }
465
466    #[test]
467    fn test_rectangle_contains() {
468        let rect = Rectangle::new(Point::new(0, 0), Point::new(10, 10));
469        let inner = Rectangle::new(Point::new(2, 2), Point::new(8, 8));
470        let outer = Rectangle::new(Point::new(-5, -5), Point::new(15, 15));
471
472        assert!(rect.contains(&inner));
473        assert!(!rect.contains(&outer));
474    }
475
476    #[test]
477    fn test_rectangle_contains_point() {
478        let rect = Rectangle::new(Point::new(0, 0), Point::new(10, 10));
479        let inside = Point::new(5, 5);
480        let outside = Point::new(15, 15);
481        let on_edge = Point::new(10, 5);
482
483        assert!(rect.contains_point(&inside));
484        assert!(!rect.contains_point(&outside));
485        assert!(rect.contains_point(&on_edge));
486    }
487
488    #[test]
489    fn test_bounding_rect_method() {
490        let rect1 = Rectangle::new(Point::new(0, 0), Point::new(10, 10));
491        let rect2 = Rectangle::new(Point::new(5, 5), Point::new(15, 15));
492        let bbox = rect1.bounding_rect(&rect2);
493
494        assert_eq!(bbox.min, Point::new(0, 0));
495        assert_eq!(bbox.max, Point::new(15, 15));
496    }
497
498    #[test]
499    fn test_to_polygon() {
500        let rect = Rectangle::new(Point::new(0, 0), Point::new(10, 20));
501        let polygon = rect.to_polygon();
502
503        assert_eq!(polygon.vertices().len(), 4);
504        assert!(polygon.is_rectilinear());
505    }
506
507    #[test]
508    fn test_bounding_rect_empty() {
509        let rects: Vec<Rectangle<i32>> = vec![];
510        assert!(bounding_rect(&rects).is_none());
511    }
512
513    #[test]
514    fn test_total_area_empty() {
515        let rects: Vec<Rectangle<i32>> = vec![];
516        assert_eq!(total_area(&rects), 0);
517    }
518
519    #[test]
520    fn test_manhattan_distance_reverse() {
521        // Test case where p2 > p1 (different branch)
522        let p1 = Point::new(5, 3);
523        let p2 = Point::new(1, 0);
524        assert_eq!(manhattan_distance(&p1, &p2), 7); // |5-1| + |3-0| = 7
525    }
526
527    #[test]
528    fn test_manhattan_distance_same() {
529        // Test case where coordinates are equal
530        let p1 = Point::new(5, 5);
531        let p2 = Point::new(5, 5);
532        assert_eq!(manhattan_distance(&p1, &p2), 0);
533    }
534
535    #[test]
536    fn test_check_spacing_vertical() {
537        // Test vertical spacing
538        let rect1 = Rectangle::new(Point::new(0, 0), Point::new(10, 10));
539        let rect2 = Rectangle::new(Point::new(0, 10), Point::new(10, 20));
540        assert!(!check_spacing(&rect1, &rect2, 1)); // touching
541
542        let rect3 = Rectangle::new(Point::new(0, 12), Point::new(10, 22));
543        assert!(check_spacing(&rect1, &rect3, 1)); // spacing of 2
544    }
545
546    #[test]
547    fn test_check_spacing_overlapping() {
548        // Test case where rectangles overlap
549        let rect1 = Rectangle::new(Point::new(0, 0), Point::new(10, 10));
550        let rect2 = Rectangle::new(Point::new(5, 5), Point::new(15, 15));
551        assert!(!check_spacing(&rect1, &rect2, 1)); // overlapping
552    }
553}