Skip to main content

topstitch/mod_def/
dtypes.rs

1// SPDX-License-Identifier: Apache-2.0
2
3use crate::lefdef::{DefDieArea, DefPoint};
4use crate::mod_def::tracks::{TrackDefinition, TrackOrientation};
5#[derive(Clone)]
6pub(crate) struct VerilogImport {
7    pub(crate) sources: Vec<String>,
8    pub(crate) incdirs: Vec<String>,
9    pub(crate) defines: Vec<(String, String)>,
10    pub(crate) skip_unsupported: bool,
11    pub(crate) ignore_unknown_modules: bool,
12}
13
14// Floorplanning-related types
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub struct Coordinate {
18    pub x: i64,
19    pub y: i64,
20}
21
22impl std::ops::Add for Coordinate {
23    type Output = Coordinate;
24
25    fn add(self, rhs: Coordinate) -> Coordinate {
26        Coordinate {
27            x: self.x + rhs.x,
28            y: self.y + rhs.y,
29        }
30    }
31}
32
33impl std::ops::Add<&Coordinate> for Coordinate {
34    type Output = Coordinate;
35
36    fn add(self, rhs: &Coordinate) -> Coordinate {
37        Coordinate {
38            x: self.x + rhs.x,
39            y: self.y + rhs.y,
40        }
41    }
42}
43
44impl std::ops::Add<Coordinate> for &Coordinate {
45    type Output = Coordinate;
46
47    fn add(self, rhs: Coordinate) -> Coordinate {
48        Coordinate {
49            x: self.x + rhs.x,
50            y: self.y + rhs.y,
51        }
52    }
53}
54
55impl std::ops::Add for &Coordinate {
56    type Output = Coordinate;
57
58    fn add(self, rhs: &Coordinate) -> Coordinate {
59        Coordinate {
60            x: self.x + rhs.x,
61            y: self.y + rhs.y,
62        }
63    }
64}
65
66impl std::ops::Sub for Coordinate {
67    type Output = Coordinate;
68
69    fn sub(self, rhs: Coordinate) -> Coordinate {
70        Coordinate {
71            x: self.x - rhs.x,
72            y: self.y - rhs.y,
73        }
74    }
75}
76
77impl std::ops::Sub<&Coordinate> for Coordinate {
78    type Output = Coordinate;
79
80    fn sub(self, rhs: &Coordinate) -> Coordinate {
81        Coordinate {
82            x: self.x - rhs.x,
83            y: self.y - rhs.y,
84        }
85    }
86}
87
88impl std::ops::Sub<Coordinate> for &Coordinate {
89    type Output = Coordinate;
90
91    fn sub(self, rhs: Coordinate) -> Coordinate {
92        Coordinate {
93            x: self.x - rhs.x,
94            y: self.y - rhs.y,
95        }
96    }
97}
98
99impl std::ops::Sub for &Coordinate {
100    type Output = Coordinate;
101
102    fn sub(self, rhs: &Coordinate) -> Coordinate {
103        Coordinate {
104            x: self.x - rhs.x,
105            y: self.y - rhs.y,
106        }
107    }
108}
109
110impl From<(i64, i64)> for Coordinate {
111    fn from(value: (i64, i64)) -> Self {
112        Coordinate {
113            x: value.0,
114            y: value.1,
115        }
116    }
117}
118
119impl Coordinate {
120    pub fn apply_transform(&self, transform: &Mat3) -> Coordinate {
121        let vector = nalgebra::Vector3::new(self.x, self.y, 1);
122        let result = transform.0 * vector;
123        Coordinate {
124            x: result[0],
125            y: result[1],
126        }
127    }
128
129    pub fn with_x(&self, x: i64) -> Coordinate {
130        Coordinate { x, y: self.y }
131    }
132
133    pub fn with_y(&self, y: i64) -> Coordinate {
134        Coordinate { x: self.x, y }
135    }
136
137    pub fn to_transform(&self) -> Mat3 {
138        Mat3::translate(self.x, self.y)
139    }
140}
141
142/// Represents an optionally bounded inclusive interval along an edge or track.
143#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
144pub struct Range {
145    pub min: Option<i64>,
146    pub max: Option<i64>,
147}
148
149impl Range {
150    /// Creates a range spanning the inclusive bounds of `a` and `b`,
151    /// automatically ordering them if necessary.
152    pub fn new(a: i64, b: i64) -> Self {
153        if a <= b {
154            Range {
155                min: Some(a),
156                max: Some(b),
157            }
158        } else {
159            Range {
160                min: Some(b),
161                max: Some(a),
162            }
163        }
164    }
165
166    /// Creates a semi-infinite range beginning at `min` and extending upward.
167    pub fn from_min(min: i64) -> Self {
168        Range {
169            min: Some(min),
170            max: None,
171        }
172    }
173
174    /// Creates a semi-infinite range ending at `max` and extending downward.
175    pub fn from_max(max: i64) -> Self {
176        Range {
177            min: None,
178            max: Some(max),
179        }
180    }
181
182    /// Creates a fully unbounded range.
183    pub fn any() -> Self {
184        Range {
185            min: None,
186            max: None,
187        }
188    }
189
190    /// Returns `true` if `value` lies inside the inclusive bounds of this
191    /// range.
192    pub fn contains(&self, value: i64) -> bool {
193        self.min.is_none_or(|min| value >= min) && self.max.is_none_or(|max| value <= max)
194    }
195
196    /// Returns `true` if every value in `self` is also contained within
197    /// `other`.
198    pub fn is_subset_of(&self, other: &Range) -> bool {
199        self.min
200            .is_none_or(|self_min| other.min.is_none_or(|other_min| self_min >= other_min))
201            && self
202                .max
203                .is_none_or(|self_max| other.max.is_none_or(|other_max| self_max <= other_max))
204    }
205
206    /// Computes the overlapping portion of two ranges, if any.
207    pub fn intersection(&self, other: &Range) -> Option<Range> {
208        let min = match (self.min, other.min) {
209            (Some(a), Some(b)) => Some(a.max(b)),
210            (Some(a), None) => Some(a),
211            (None, Some(b)) => Some(b),
212            (None, None) => None,
213        };
214        let max = match (self.max, other.max) {
215            (Some(a), Some(b)) => Some(a.min(b)),
216            (Some(a), None) => Some(a),
217            (None, Some(b)) => Some(b),
218            (None, None) => None,
219        };
220        match (min, max) {
221            (Some(min_val), Some(max_val)) if min_val > max_val => None,
222            _ => Some(Range { min, max }),
223        }
224    }
225}
226
227impl std::fmt::Display for Range {
228    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
229        match (self.min, self.max) {
230            (Some(min), Some(max)) => write!(f, "[{min}, {max}]"),
231            (Some(min), None) => write!(f, "[{min}, ...]"),
232            (None, Some(max)) => write!(f, "[..., {max}]"),
233            (None, None) => write!(f, "[...]"),
234        }
235    }
236}
237
238/// A half-open directed segment of a polygon boundary.
239pub struct Edge {
240    pub a: Coordinate,
241    pub b: Coordinate,
242}
243
244/// Enumerates the axis-aligned orientation of an edge.
245#[derive(Clone, Copy, Debug, PartialEq, Eq)]
246pub enum EdgeOrientation {
247    North,
248    South,
249    East,
250    West,
251}
252
253impl Edge {
254    /// Returns the orientation of the edge, or `None` if it is not
255    /// axis-aligned.
256    pub fn orientation(&self) -> Option<EdgeOrientation> {
257        if self.a.y == self.b.y {
258            if self.a.x < self.b.x {
259                Some(EdgeOrientation::East)
260            } else {
261                Some(EdgeOrientation::West)
262            }
263        } else if self.a.x == self.b.x {
264            if self.a.y < self.b.y {
265                Some(EdgeOrientation::North)
266            } else {
267                Some(EdgeOrientation::South)
268            }
269        } else {
270            None
271        }
272    }
273
274    /// Returns the inclusive range spanned by the edge along the X axis.
275    pub fn get_x_range(&self) -> Range {
276        Range::new(self.a.x, self.b.x)
277    }
278
279    /// Returns the inclusive range spanned by the edge along the Y axis.
280    pub fn get_y_range(&self) -> Range {
281        Range::new(self.a.y, self.b.y)
282    }
283
284    /// Returns the edge-parallel coordinate range for axis-aligned edges.
285    pub fn get_coord_range(&self) -> Option<Range> {
286        let edge_orientation = self.orientation()?;
287        match edge_orientation {
288            EdgeOrientation::North | EdgeOrientation::South => Some(self.get_y_range()),
289            EdgeOrientation::East | EdgeOrientation::West => Some(self.get_x_range()),
290        }
291    }
292
293    /// Returns the perpendicular distance from `point` to this edge when the
294    /// projection of the point lies within the edge's inclusive span.
295    pub fn distance_to_coordinate(&self, point: &Coordinate) -> Option<i64> {
296        match self.orientation()? {
297            EdgeOrientation::North | EdgeOrientation::South => {
298                if self.get_y_range().contains(point.y) {
299                    Some(i64::abs_diff(point.x, self.a.x) as i64)
300                } else {
301                    None
302                }
303            }
304            EdgeOrientation::East | EdgeOrientation::West => {
305                if self.get_x_range().contains(point.x) {
306                    Some(i64::abs_diff(point.y, self.a.y) as i64)
307                } else {
308                    None
309                }
310            }
311        }
312    }
313
314    /// Returns the usable track index range for the given track definition, if
315    /// the track orientation is compatible with this edge.
316    pub fn get_index_range(&self, track: &TrackDefinition) -> Option<Range> {
317        let edge_orientation = self.orientation()?;
318
319        let coord_range = match (&track.orientation, edge_orientation) {
320            (TrackOrientation::Horizontal, EdgeOrientation::North | EdgeOrientation::South) => {
321                self.get_y_range()
322            }
323            (TrackOrientation::Vertical, EdgeOrientation::East | EdgeOrientation::West) => {
324                self.get_x_range()
325            }
326            _ => return None,
327        };
328
329        let track_range = track.convert_coord_range_to_index_range(&coord_range);
330
331        // sanity check - after converting the track indices back to coordinates, the
332        // resulting range should be a subset of the original coordinate range
333        let round_trip_coord_range: Range = track.convert_index_range_to_coord_range(&track_range);
334        assert!(round_trip_coord_range.is_subset_of(&coord_range));
335
336        Some(track_range)
337    }
338
339    /// Returns the absolute coordinate value (in the edge's axis) of
340    /// `track_index_on_edge` measured from the edge start.
341    pub fn get_position_on_edge(&self, track: &TrackDefinition, track_index_on_edge: usize) -> i64 {
342        let start_stop = self.get_index_range(track).unwrap();
343        let start_index = start_stop.min.unwrap();
344        track.index_to_position(start_index + (track_index_on_edge as i64))
345    }
346
347    /// Returns the [`Coordinate`] where `track_index_on_edge` intersects this
348    /// edge when using the supplied track definition.
349    pub fn get_coordinate_on_edge(
350        &self,
351        track: &TrackDefinition,
352        track_index_on_edge: usize,
353    ) -> Coordinate {
354        let position = self.get_position_on_edge(track, track_index_on_edge);
355        match track.orientation {
356            TrackOrientation::Horizontal => Coordinate {
357                x: self.a.x,
358                y: position,
359            },
360            TrackOrientation::Vertical => Coordinate {
361                x: position,
362                y: self.a.y,
363            },
364        }
365    }
366
367    /// Returns a matrix corresponding to the necessary rotation of a pin
368    /// polygon from the "standard" orientation (llx=-w/2, lly=0, urx=+w/2,
369    /// ury=h) to be placed on this edge.
370    pub fn to_pin_transform(&self) -> Mat3 {
371        match self.orientation() {
372            Some(EdgeOrientation::North) => Mat3::from_orientation(Orientation::R270),
373            Some(EdgeOrientation::South) => Mat3::from_orientation(Orientation::R90),
374            Some(EdgeOrientation::East) => Mat3::from_orientation(Orientation::R180),
375            Some(EdgeOrientation::West) => Mat3::from_orientation(Orientation::R0),
376            None => panic!("Edge is not axis-aligned; only rectilinear edges are supported"),
377        }
378    }
379}
380
381#[derive(Debug, Clone, Copy, PartialEq, Eq)]
382pub enum Orientation {
383    R0,
384    R90,
385    R180,
386    R270,
387    MX,
388    MY,
389    MX90,
390    MY90,
391}
392
393impl Orientation {
394    pub fn prepend_transform(&self, transform: &Orientation) -> Self {
395        (&Mat3::from_orientation(*transform) * &Mat3::from_orientation(*self)).as_orientation()
396    }
397
398    pub fn append_transform(&self, transform: &Orientation) -> Self {
399        (&Mat3::from_orientation(*self) * &Mat3::from_orientation(*transform)).as_orientation()
400    }
401}
402
403#[derive(Debug, Clone)]
404pub struct Polygon(pub Vec<Coordinate>);
405pub struct BoundingBox {
406    pub min_x: i64,
407    pub max_x: i64,
408    pub min_y: i64,
409    pub max_y: i64,
410}
411
412impl BoundingBox {
413    pub fn get_width(&self) -> i64 {
414        self.max_x - self.min_x
415    }
416
417    pub fn get_height(&self) -> i64 {
418        self.max_y - self.min_y
419    }
420
421    pub fn get_width_height(&self) -> (i64, i64) {
422        (self.get_width(), self.get_height())
423    }
424
425    pub fn center(&self) -> Coordinate {
426        Coordinate {
427            x: (self.min_x + self.max_x) / 2,
428            y: (self.min_y + self.max_y) / 2,
429        }
430    }
431
432    pub fn union(&self, other: &BoundingBox) -> BoundingBox {
433        BoundingBox {
434            min_x: self.min_x.min(other.min_x),
435            max_x: self.max_x.max(other.max_x),
436            min_y: self.min_y.min(other.min_y),
437            max_y: self.max_y.max(other.max_y),
438        }
439    }
440
441    pub fn intersects(&self, other: &BoundingBox) -> bool {
442        // strict equality is used because abutted boxes are not considered to intersect
443        (self.min_x < other.max_x)
444            && (self.max_x > other.min_x)
445            && (self.min_y < other.max_y)
446            && (self.max_y > other.min_y)
447    }
448
449    pub fn covers(&self, other: &BoundingBox) -> bool {
450        // note how equality is used on boundaries here: a pin whose out edge is exactly
451        // on the boundary of this shape is considered to be covered.
452        (self.min_x <= other.min_x)
453            && (self.max_x >= other.max_x)
454            && (self.min_y <= other.min_y)
455            && (self.max_y >= other.max_y)
456    }
457
458    pub fn apply_transform(&self, m: &Mat3) -> BoundingBox {
459        Polygon::from_bbox(self).apply_transform(m).bbox()
460    }
461
462    pub fn x_distance(&self, other: &BoundingBox) -> i64 {
463        if self.max_x < other.min_x {
464            other.min_x - self.max_x
465        } else if other.max_x < self.min_x {
466            self.min_x - other.max_x
467        } else {
468            0
469        }
470    }
471
472    pub fn y_distance(&self, other: &BoundingBox) -> i64 {
473        if self.max_y < other.min_y {
474            other.min_y - self.max_y
475        } else if other.max_y < self.min_y {
476            self.min_y - other.max_y
477        } else {
478            0
479        }
480    }
481
482    pub fn gap(&self, other: &BoundingBox) -> i64 {
483        self.x_distance(other) + self.y_distance(other)
484    }
485}
486
487impl Polygon {
488    pub fn new(points: Vec<Coordinate>) -> Self {
489        Polygon(points)
490    }
491    /// Returns the `i`th edge, wrapping around for the closing segment.
492    pub fn get_edge(&self, i: usize) -> Edge {
493        assert!(i < self.0.len(), "Edge index out of bounds");
494        let a = self.0[i];
495        let b = self.0[(i + 1) % self.0.len()];
496        Edge { a, b }
497    }
498
499    pub fn closest_edge_index(&self, point: &Coordinate) -> Option<usize> {
500        self.closest_edge_index_where(point, |_| true)
501    }
502
503    pub fn closest_edge_index_where<F>(&self, point: &Coordinate, mut predicate: F) -> Option<usize>
504    where
505        F: FnMut(&Edge) -> bool,
506    {
507        let mut best: Option<(usize, i64)> = None;
508        for idx in 0..self.num_edges() {
509            let edge = self.get_edge(idx);
510            if !predicate(&edge) {
511                continue;
512            }
513            if let Some(distance) = edge.distance_to_coordinate(point) {
514                match &mut best {
515                    Some((_, best_distance)) if distance >= *best_distance => {}
516                    _ => best = Some((idx, distance)),
517                }
518            }
519        }
520        best.map(|(idx, _)| idx)
521    }
522
523    pub fn from_width_height(width: i64, height: i64) -> Self {
524        Self::from_bbox(&BoundingBox {
525            min_x: 0,
526            min_y: 0,
527            max_x: width,
528            max_y: height,
529        })
530    }
531
532    pub fn from_bbox(bbox: &BoundingBox) -> Self {
533        Self::new(vec![
534            Coordinate {
535                x: bbox.min_x,
536                y: bbox.min_y,
537            },
538            Coordinate {
539                x: bbox.min_x,
540                y: bbox.max_y,
541            },
542            Coordinate {
543                x: bbox.max_x,
544                y: bbox.max_y,
545            },
546            Coordinate {
547                x: bbox.max_x,
548                y: bbox.min_y,
549            },
550        ])
551    }
552
553    pub fn apply_transform(&self, m: &Mat3) -> Polygon {
554        let pts = self
555            .0
556            .iter()
557            .map(|p| {
558                let v = nalgebra::Vector3::new(p.x, p.y, 1);
559                let result = m.0 * v;
560                Coordinate {
561                    x: result[0],
562                    y: result[1],
563                }
564            })
565            .collect();
566        Polygon(pts)
567    }
568
569    pub fn bbox(&self) -> BoundingBox {
570        let mut min_x = i64::MAX;
571        let mut max_x = i64::MIN;
572        let mut min_y = i64::MAX;
573        let mut max_y = i64::MIN;
574        for p in &self.0 {
575            if p.x < min_x {
576                min_x = p.x;
577            }
578            if p.x > max_x {
579                max_x = p.x;
580            }
581            if p.y < min_y {
582                min_y = p.y;
583            }
584            if p.y > max_y {
585                max_y = p.y;
586            }
587        }
588        BoundingBox {
589            min_x,
590            max_x,
591            min_y,
592            max_y,
593        }
594    }
595
596    /// Returns true if the polygon is a rectangle.
597    pub fn is_rectangular(&self) -> bool {
598        let points = &self.0;
599        points.len() == 4 && self.is_rectilinear()
600    }
601
602    /// Returns true if all edges are axis-aligned and non-degenerate.
603    pub fn is_rectilinear(&self) -> bool {
604        let points = &self.0;
605        for i in 0..points.len() {
606            let a = points[i];
607            let b = points[(i + 1) % points.len()];
608            if !(a.x == b.x || a.y == b.y) {
609                return false;
610            }
611            if a.x == b.x && a.y == b.y {
612                return false;
613            }
614        }
615        true
616    }
617
618    /// Returns true if the polygon is defined clockwise, using the shoelace
619    /// formula. ref: <https://en.wikipedia.org/wiki/Shoelace_formula>
620    pub fn is_clockwise(&self) -> bool {
621        let points = &self.0;
622
623        assert!(points.len() >= 3, "need at least 3 vertices");
624
625        let mut twice_area: i128 = 0; // use i128 to avoid overflow
626        for (idx, point) in points.iter().enumerate() {
627            let point_next = points[(idx + 1) % points.len()];
628            let (x, y) = (point.x as i128, point.y as i128);
629            let (x_next, y_next) = (point_next.x as i128, point_next.y as i128);
630            twice_area += (x * y_next) - (x_next * y);
631        }
632
633        twice_area < 0
634    }
635
636    /// Returns true if the polygon starts with the leftmost vertical edge. In
637    /// the case of a tie, make sure the lowest leftmost vertical edge is
638    /// chosen.
639    pub fn starts_with_leftmost_vertical_edge(&self) -> bool {
640        let points = &self.0;
641
642        assert!(
643            points.len() >= 2,
644            "need at least 2 vertices to determine if a polygon starts with the leftmost vertical edge"
645        );
646
647        if points[0].x != points[1].x {
648            // does not start with a vertical edge
649            return false;
650        }
651
652        let first_x = points[0].x;
653        let first_y = points[0].y.min(points[1].y);
654
655        for idx in 1..points.len() {
656            let point = points[idx];
657            let point_next = points[(idx + 1) % points.len()];
658            if point.x != point_next.x {
659                // skip if not a vertical edge
660                continue;
661            } else if point.x > first_x {
662                // skip if to the right of the first edge
663                continue;
664            } else if point.x < first_x {
665                // return false if this edge is to the left of the first edge
666                return false;
667            } else if point.y.min(point_next.y) < first_y {
668                // edges are both leftmost; make sure the first one is lower
669                return false;
670            }
671        }
672
673        true
674    }
675
676    pub fn num_vertices(&self) -> usize {
677        self.0.len()
678    }
679
680    pub fn num_edges(&self) -> usize {
681        self.0.len()
682    }
683
684    pub fn edge_index_for_coordinate(&self, coordinate: &Coordinate) -> Option<usize> {
685        for idx in 0..self.num_edges() {
686            let edge = self.get_edge(idx);
687            match edge.orientation() {
688                Some(EdgeOrientation::North | EdgeOrientation::South) => {
689                    if edge.get_y_range().contains(coordinate.y) && coordinate.x == edge.a.x {
690                        return Some(idx);
691                    }
692                }
693                Some(EdgeOrientation::East | EdgeOrientation::West) => {
694                    if edge.get_x_range().contains(coordinate.x) && coordinate.y == edge.a.y {
695                        return Some(idx);
696                    }
697                }
698                None => continue,
699            }
700        }
701        None
702    }
703
704    pub fn find_opposite_edge(&self, coordinate: &Coordinate) -> Result<usize, String> {
705        let src_edge_idx = self.edge_index_for_coordinate(coordinate).unwrap();
706        let src_edge = self.get_edge(src_edge_idx);
707
708        let src_is_horiz = match src_edge.orientation() {
709            Some(EdgeOrientation::North | EdgeOrientation::South) => false,
710            Some(EdgeOrientation::East | EdgeOrientation::West) => true,
711            None => {
712                return Err(
713                    "Edge is not axis-aligned; only rectilinear edges are supported".to_string(),
714                );
715            }
716        };
717
718        for dst_edge_idx in 0..self.num_edges() {
719            if dst_edge_idx == src_edge_idx {
720                continue;
721            }
722
723            let dst_edge = self.get_edge(dst_edge_idx);
724            let dst_is_horiz = match dst_edge.orientation() {
725                Some(EdgeOrientation::North | EdgeOrientation::South) => false,
726                Some(EdgeOrientation::East | EdgeOrientation::West) => true,
727                None => continue,
728            };
729
730            if (src_is_horiz && dst_is_horiz && dst_edge.get_x_range().contains(coordinate.x))
731                || (!src_is_horiz)
732                    && (!dst_is_horiz)
733                    && dst_edge.get_y_range().contains(coordinate.y)
734            {
735                return Ok(dst_edge_idx);
736            } else {
737                continue;
738            }
739        }
740        Err("Unable to locate a unique opposite edge".to_string())
741    }
742
743    /// Returns the centroid of the polygon.
744    pub fn centroid(&self) -> Coordinate {
745        let mut centroid = Coordinate { x: 0, y: 0 };
746        for p in &self.0 {
747            centroid.x += p.x;
748            centroid.y += p.y;
749        }
750        centroid.x /= self.0.len() as i64;
751        centroid.y /= self.0.len() as i64;
752        centroid
753    }
754
755    pub fn to_def_die_area(&self) -> DefDieArea {
756        DefDieArea {
757            points: self.0.iter().map(|p| DefPoint { x: p.x, y: p.y }).collect(),
758        }
759    }
760
761    pub fn to_geo_polygon(&self) -> geo::Polygon<i64> {
762        geo::Polygon::new(
763            geo::LineString::from(
764                self.0
765                    .iter()
766                    .map(|p| geo::Point::new(p.x, p.y))
767                    .collect::<Vec<_>>(),
768            ),
769            vec![],
770        )
771    }
772
773    pub fn to_geo_polygon_f64(&self) -> geo::Polygon<f64> {
774        geo::Polygon::new(
775            geo::LineString::from(
776                self.0
777                    .iter()
778                    .map(|p| {
779                        // Error if f64 cannot represent exactly
780                        if (p.x as f64) as i64 != p.x || (p.y as f64) as i64 != p.y {
781                            panic!(
782                                "Coordinate values cannot be represented exactly as f64: ({}, {})",
783                                p.x, p.y
784                            );
785                        }
786                        geo::Point::new(p.x as f64, p.y as f64)
787                    })
788                    .collect::<Vec<_>>(),
789            ),
790            vec![],
791        )
792    }
793}
794
795impl std::ops::Add<Coordinate> for Polygon {
796    type Output = Polygon;
797
798    fn add(self, rhs: Coordinate) -> Polygon {
799        Polygon(
800            self.0
801                .into_iter()
802                .map(|point| point + rhs)
803                .collect::<Vec<_>>(),
804        )
805    }
806}
807
808impl std::ops::Add<Coordinate> for &Polygon {
809    type Output = Polygon;
810
811    fn add(self, rhs: Coordinate) -> Polygon {
812        Polygon(self.0.iter().map(|point| point + rhs).collect::<Vec<_>>())
813    }
814}
815
816impl std::ops::Sub<Coordinate> for Polygon {
817    type Output = Polygon;
818
819    fn sub(self, rhs: Coordinate) -> Polygon {
820        Polygon(
821            self.0
822                .into_iter()
823                .map(|point| point - rhs)
824                .collect::<Vec<_>>(),
825        )
826    }
827}
828
829impl std::ops::Sub<Coordinate> for &Polygon {
830    type Output = Polygon;
831
832    fn sub(self, rhs: Coordinate) -> Polygon {
833        Polygon(self.0.iter().map(|point| point - rhs).collect::<Vec<_>>())
834    }
835}
836
837impl PartialEq for Polygon {
838    fn eq(&self, other: &Self) -> bool {
839        let a = &self.0;
840        let b = &other.0;
841        if a.len() != b.len() {
842            return false;
843        }
844        if a.is_empty() {
845            return true;
846        }
847        // rotation-invariant equality: try aligning every index
848        for start in 0..b.len() {
849            let mut all_match = true;
850            for (i, a_i) in a.iter().enumerate() {
851                let j = (start + i) % b.len();
852                if a_i != &b[j] {
853                    all_match = false;
854                    break;
855                }
856            }
857            if all_match {
858                return true;
859            }
860        }
861        false
862    }
863}
864
865impl Eq for Polygon {}
866
867#[derive(Debug, Clone, Copy)]
868pub struct Placement {
869    pub coordinate: Coordinate,
870    pub orientation: Orientation,
871}
872
873impl Default for Placement {
874    fn default() -> Self {
875        Placement {
876            coordinate: Coordinate { x: 0, y: 0 },
877            orientation: Orientation::R0,
878        }
879    }
880}
881
882impl Placement {
883    pub fn transform(&self) -> Mat3 {
884        Mat3::from_orientation_then_translation(&self.orientation, &self.coordinate)
885    }
886}
887
888#[derive(Debug, Clone, Copy, PartialEq, Eq)]
889#[repr(transparent)]
890pub struct Mat3(pub nalgebra::Matrix3<i64>);
891
892impl Mat3 {
893    pub fn identity() -> Mat3 {
894        Mat3(nalgebra::Matrix3::identity())
895    }
896
897    pub fn translate(dx: i64, dy: i64) -> Mat3 {
898        Mat3(nalgebra::Matrix3::new(1, 0, dx, 0, 1, dy, 0, 0, 1))
899    }
900
901    pub fn from_orientation(o: Orientation) -> Mat3 {
902        const ROTATE_90: Mat3 = Mat3(nalgebra::Matrix3::new(0, -1, 0, 1, 0, 0, 0, 0, 1));
903        const MIRROR_X: Mat3 = Mat3(nalgebra::Matrix3::new(1, 0, 0, 0, -1, 0, 0, 0, 1));
904        const MIRROR_Y: Mat3 = Mat3(nalgebra::Matrix3::new(-1, 0, 0, 0, 1, 0, 0, 0, 1));
905
906        match o {
907            Orientation::R0 => Self::identity(),
908            Orientation::R90 => ROTATE_90,
909            Orientation::R180 => &ROTATE_90 * &ROTATE_90,
910            Orientation::R270 => &(&ROTATE_90 * &ROTATE_90) * &ROTATE_90,
911            Orientation::MX => MIRROR_X,
912            Orientation::MY => MIRROR_Y,
913            Orientation::MX90 => &ROTATE_90 * &MIRROR_X,
914            Orientation::MY90 => &ROTATE_90 * &MIRROR_Y,
915        }
916    }
917
918    pub fn as_orientation(&self) -> Orientation {
919        for orientation in [
920            Orientation::R0,
921            Orientation::R90,
922            Orientation::R180,
923            Orientation::R270,
924            Orientation::MX,
925            Orientation::MY,
926            Orientation::MX90,
927            Orientation::MY90,
928        ] {
929            let ref_mat = Self::from_orientation(orientation);
930            if self.0.fixed_view::<2, 2>(0, 0) == ref_mat.0.fixed_view::<2, 2>(0, 0) {
931                return orientation;
932            }
933        }
934        panic!("Unsupported orientation: {self:?}");
935    }
936
937    pub fn as_coordinate(&self) -> Coordinate {
938        Coordinate {
939            x: self.0[(0, 2)],
940            y: self.0[(1, 2)],
941        }
942    }
943
944    pub fn from_orientation_then_translation(
945        orientation: &Orientation,
946        translation: &Coordinate,
947    ) -> Mat3 {
948        let orientation_transform = Mat3::from_orientation(*orientation);
949        let translation_transform = Mat3::translate(translation.x, translation.y);
950
951        &translation_transform * &orientation_transform
952    }
953
954    pub fn inverse(&self) -> Mat3 {
955        let m = &self.0;
956        assert!(
957            m[(2, 0)] == 0 && m[(2, 1)] == 0 && m[(2, 2)] == 1,
958            "Matrix is not a valid 2D homogeneous transformation (bottom row must be [0, 0, 1]), got [{}, {}, {}]",
959            m[(2, 0)],
960            m[(2, 1)],
961            m[(2, 2)]
962        );
963
964        let r = m.fixed_view::<2, 2>(0, 0).into_owned();
965        let det = r[(0, 0)] * r[(1, 1)] - r[(1, 0)] * r[(0, 1)];
966        assert!(
967            det == 1 || det == -1,
968            "Rotation part of the matrix must have determinant +1 or -1, got {}",
969            det
970        );
971
972        let t = m.fixed_view::<2, 1>(0, 2).into_owned();
973
974        let rt = r.transpose();
975
976        let minus_rt_t = -(rt * t);
977
978        let mut inv = nalgebra::Matrix3::<i64>::zeros();
979        inv.fixed_view_mut::<2, 2>(0, 0).copy_from(&rt);
980        inv.fixed_view_mut::<2, 1>(0, 2).copy_from(&minus_rt_t);
981        inv[(2, 2)] = 1;
982
983        Mat3(inv)
984    }
985}
986
987impl std::ops::Mul<&Mat3> for &Mat3 {
988    type Output = Mat3;
989    fn mul(self, rhs: &Mat3) -> Mat3 {
990        Mat3(self.0 * rhs.0)
991    }
992}
993
994// LEF pin description from ModDef
995#[derive(Debug, Clone)]
996pub struct PhysicalPin {
997    pub layer: String,
998    pub polygon: Polygon,
999    pub transform: Mat3,
1000}
1001
1002impl PhysicalPin {
1003    pub fn new(layer: impl AsRef<str>, polygon: Polygon) -> Self {
1004        Self::from_transform(layer, polygon, Mat3::identity())
1005    }
1006
1007    pub fn from_transform(layer: impl AsRef<str>, polygon: Polygon, transform: Mat3) -> Self {
1008        Self {
1009            layer: layer.as_ref().to_string(),
1010            polygon,
1011            transform,
1012        }
1013    }
1014
1015    pub fn with_transform(&self, transform: Mat3) -> Self {
1016        Self::from_transform(&self.layer, self.polygon.clone(), transform)
1017    }
1018
1019    pub fn from_orientation_then_translation(
1020        layer: impl AsRef<str>,
1021        polygon: Polygon,
1022        orientation: Orientation,
1023        translation: Coordinate,
1024    ) -> Self {
1025        let transform = Mat3::from_orientation_then_translation(&orientation, &translation);
1026        Self::from_transform(layer, polygon, transform)
1027    }
1028
1029    pub fn with_orientation_then_translation(
1030        &self,
1031        orientation: Orientation,
1032        translation: Coordinate,
1033    ) -> Self {
1034        Self::from_orientation_then_translation(
1035            &self.layer,
1036            self.polygon.clone(),
1037            orientation,
1038            translation,
1039        )
1040    }
1041
1042    pub fn from_translation(
1043        layer: impl AsRef<str>,
1044        polygon: Polygon,
1045        translation: Coordinate,
1046    ) -> Self {
1047        let transform = Mat3::translate(translation.x, translation.y);
1048        Self::from_transform(layer, polygon, transform)
1049    }
1050
1051    pub fn with_translation(&self, translation: Coordinate) -> Self {
1052        Self::from_translation(&self.layer, self.polygon.clone(), translation)
1053    }
1054
1055    pub fn transformed_polygon(&self) -> Polygon {
1056        self.polygon.apply_transform(&self.transform)
1057    }
1058
1059    pub fn translation(&self) -> Coordinate {
1060        self.transform.as_coordinate()
1061    }
1062}
1063
1064impl std::ops::Add<Coordinate> for PhysicalPin {
1065    type Output = PhysicalPin;
1066
1067    fn add(self, rhs: Coordinate) -> PhysicalPin {
1068        PhysicalPin {
1069            layer: self.layer,
1070            transform: {
1071                let mut transform = self.transform;
1072                transform.0[(0, 2)] += rhs.x;
1073                transform.0[(1, 2)] += rhs.y;
1074                transform
1075            },
1076            polygon: self.polygon,
1077        }
1078    }
1079}
1080
1081impl std::ops::Add<Coordinate> for &PhysicalPin {
1082    type Output = PhysicalPin;
1083
1084    fn add(self, rhs: Coordinate) -> PhysicalPin {
1085        PhysicalPin {
1086            layer: self.layer.clone(),
1087            transform: {
1088                let mut transform = self.transform;
1089                transform.0[(0, 2)] += rhs.x;
1090                transform.0[(1, 2)] += rhs.y;
1091                transform
1092            },
1093            polygon: self.polygon.clone(),
1094        }
1095    }
1096}