| 
 | 1 | +use crate::CellId;  | 
 | 2 | +use crate::Instance;  | 
 | 3 | +use aph_disjoint_set::DisjointSet;  | 
 | 4 | +use geometry::bbox::Bbox;  | 
 | 5 | +use geometry::rect::Rect;  | 
 | 6 | +use std::collections::{HashMap, HashSet};  | 
 | 7 | +use std::hash::Hash;  | 
 | 8 | + | 
 | 9 | +use crate::Element;  | 
 | 10 | +use crate::{Cell, Library, Shape, TransformRef, Transformation};  | 
 | 11 | + | 
 | 12 | +/// Returns true if two shapes overlap on a flat plane, and false otherwise.  | 
 | 13 | +fn intersect_shapes<L>(shape1: &Shape<L>, shape2: &Shape<L>) -> bool {  | 
 | 14 | +    let shape1_bbox: Rect = shape1.bbox_rect();  | 
 | 15 | +    let shape2_bbox: Rect = shape2.bbox_rect();  | 
 | 16 | +    shape1_bbox.intersection(shape2_bbox).is_some()  | 
 | 17 | +}  | 
 | 18 | + | 
 | 19 | +/// Returns a vector of all shapes from a given cell instance and its children  | 
 | 20 | +/// with their coordinates transformed into the coordinate system of the instance's parent.  | 
 | 21 | +fn flatten_instance<L>(inst: &Instance, lib: &Library<L>) -> Vec<Shape<L>>  | 
 | 22 | +where  | 
 | 23 | +    L: Connectivity + Clone,  | 
 | 24 | +{  | 
 | 25 | +    let mut ret: Vec<Shape<L>> = vec![];  | 
 | 26 | + | 
 | 27 | +    let cellid: CellId = inst.child();  | 
 | 28 | +    let transform: Transformation = inst.transformation();  | 
 | 29 | + | 
 | 30 | +    // Add all shape elements directly from child cell after applying the instances transformation  | 
 | 31 | +    for elem in lib.cell(cellid).elements() {  | 
 | 32 | +        if let Element::Shape(shape) = elem {  | 
 | 33 | +            let transformed_shape = shape.transform_ref(transform);  | 
 | 34 | +            ret.push(transformed_shape);  | 
 | 35 | +        }  | 
 | 36 | +    }  | 
 | 37 | + | 
 | 38 | +    // Recursively flatten child instances and apply cumulative transformations  | 
 | 39 | +    for instance in lib.cell(cellid).instances() {  | 
 | 40 | +        let mut flattened_shapes = flatten_instance::<L>(instance.1, lib);  | 
 | 41 | +        for flattened_shape in &mut flattened_shapes {  | 
 | 42 | +            *flattened_shape = flattened_shape.transform_ref(transform);  | 
 | 43 | +        }  | 
 | 44 | +        ret.append(&mut flattened_shapes);  | 
 | 45 | +    }  | 
 | 46 | + | 
 | 47 | +    ret  | 
 | 48 | +}  | 
 | 49 | + | 
 | 50 | +/// Returns a vector of all shapes from a single cell's transformed child instances and itself.  | 
 | 51 | +fn flatten_cell<L>(cell: &Cell<L>, lib: &Library<L>) -> Vec<Shape<L>>  | 
 | 52 | +where  | 
 | 53 | +    L: Connectivity + Clone,  | 
 | 54 | +{  | 
 | 55 | +    let mut ret: Vec<Shape<L>> = vec![];  | 
 | 56 | + | 
 | 57 | +    // No transformation needed  | 
 | 58 | +    for elem in cell.elements() {  | 
 | 59 | +        if let Element::Shape(shape) = elem {  | 
 | 60 | +            ret.push(shape.clone());  | 
 | 61 | +        }  | 
 | 62 | +    }  | 
 | 63 | + | 
 | 64 | +    for inst in cell.instances() {  | 
 | 65 | +        for flattened_shape in flatten_instance::<L>(inst.1, lib) {  | 
 | 66 | +            ret.push(flattened_shape);  | 
 | 67 | +        }  | 
 | 68 | +    }  | 
 | 69 | + | 
 | 70 | +    ret  | 
 | 71 | +}  | 
 | 72 | + | 
 | 73 | +pub trait Connectivity: Sized + PartialEq + Eq + Clone + Hash {  | 
 | 74 | +    fn connected_layers(&self) -> Vec<Self>;  | 
 | 75 | + | 
 | 76 | +    /// Returns a vector of layers connected to a given layer.  | 
 | 77 | +    fn connected(&self, other: &Self) -> bool {  | 
 | 78 | +        self.connected_layers().contains(other)  | 
 | 79 | +    }  | 
 | 80 | + | 
 | 81 | +    /// Returns a vector of unique hashsets of all connected groups of connected child shapes in a given cell.  | 
 | 82 | +    fn connected_components(cell: &Cell<Self>, lib: &Library<Self>) -> Vec<HashSet<Shape<Self>>>  | 
 | 83 | +    where  | 
 | 84 | +        Self: Clone,  | 
 | 85 | +    {  | 
 | 86 | +        // All sub-shapes contained in given cell  | 
 | 87 | +        let all_shapes = flatten_cell::<Self>(cell, lib);  | 
 | 88 | +        let mut djs = DisjointSet::new(all_shapes.len());  | 
 | 89 | + | 
 | 90 | +        // Build disjoint sets based on overlap and layer connectivity  | 
 | 91 | +        for (start_index, start_shape) in all_shapes.clone().into_iter().enumerate() {  | 
 | 92 | +            for (other_index, other_shape) in all_shapes.clone().into_iter().enumerate() {  | 
 | 93 | +                if start_index != other_index  | 
 | 94 | +                    && intersect_shapes::<Self>(&start_shape, &other_shape)  | 
 | 95 | +                    && start_shape.layer().connected(other_shape.layer())  | 
 | 96 | +                {  | 
 | 97 | +                    djs.union(start_index, other_index);  | 
 | 98 | +                }  | 
 | 99 | +            }  | 
 | 100 | +        }  | 
 | 101 | + | 
 | 102 | +        let mut component_map: HashMap<usize, HashSet<Shape<Self>>> = HashMap::new();  | 
 | 103 | + | 
 | 104 | +        for (start_index, start_shape) in all_shapes.into_iter().enumerate() {  | 
 | 105 | +            let root: usize = djs.get_root(start_index).into_inner();  | 
 | 106 | +            component_map.entry(root).or_default().insert(start_shape);  | 
 | 107 | +        }  | 
 | 108 | + | 
 | 109 | +        component_map.into_values().collect()  | 
 | 110 | +    }  | 
 | 111 | +}  | 
 | 112 | + | 
 | 113 | +#[cfg(test)]  | 
 | 114 | +mod tests {  | 
 | 115 | +    use super::*;  | 
 | 116 | +    use crate::{Cell, Instance, LibraryBuilder, Shape};  | 
 | 117 | +    use geometry::rect::Rect;  | 
 | 118 | +    use std::collections::{HashMap, HashSet};  | 
 | 119 | + | 
 | 120 | +    // This struct helps check if two shapes are connected after connected_components has been run  | 
 | 121 | +    struct ComponentLookup<L> {  | 
 | 122 | +        shape_to_component_id: HashMap<Shape<L>, usize>,  | 
 | 123 | +    }  | 
 | 124 | + | 
 | 125 | +    impl<L> ComponentLookup<L>  | 
 | 126 | +    where  | 
 | 127 | +        L: Connectivity + Clone,  | 
 | 128 | +    {  | 
 | 129 | +        /// Creates a new ComponentLookup from a vector of connected components.  | 
 | 130 | +        fn new(components: Vec<HashSet<Shape<L>>>) -> Self {  | 
 | 131 | +            let mut shape_to_component_id = HashMap::new();  | 
 | 132 | +            for (component_id, component_set) in components.into_iter().enumerate() {  | 
 | 133 | +                for shape in component_set.into_iter() {  | 
 | 134 | +                    shape_to_component_id.insert(shape, component_id);  | 
 | 135 | +                }  | 
 | 136 | +            }  | 
 | 137 | +            ComponentLookup {  | 
 | 138 | +                shape_to_component_id,  | 
 | 139 | +            }  | 
 | 140 | +        }  | 
 | 141 | + | 
 | 142 | +        /// Returns true if both shapes are found and belong to the same component, and false otherwise.  | 
 | 143 | +        fn are_connected(&self, s1: &Shape<L>, s2: &Shape<L>) -> bool {  | 
 | 144 | +            let comp_id1 = self.shape_to_component_id.get(s1);  | 
 | 145 | +            let comp_id2 = self.shape_to_component_id.get(s2);  | 
 | 146 | + | 
 | 147 | +            match (comp_id1, comp_id2) {  | 
 | 148 | +                // If both shapes are found, check if their component IDs are the same  | 
 | 149 | +                (Some(&id1), Some(&id2)) => id1 == id2,  | 
 | 150 | +                _ => false,  | 
 | 151 | +            }  | 
 | 152 | +        }  | 
 | 153 | +    }  | 
 | 154 | + | 
 | 155 | +    #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]  | 
 | 156 | +    enum Layer {  | 
 | 157 | +        Met1,  | 
 | 158 | +        Via1,  | 
 | 159 | +        Met2,  | 
 | 160 | +        Outside,  | 
 | 161 | +    }  | 
 | 162 | + | 
 | 163 | +    impl Connectivity for Layer {  | 
 | 164 | +        fn connected_layers(&self) -> Vec<Self> {  | 
 | 165 | +            match self {  | 
 | 166 | +                Self::Met1 => vec![Self::Met1, Self::Via1],  | 
 | 167 | +                Self::Via1 => vec![Self::Met1, Self::Via1, Self::Met2],  | 
 | 168 | +                Self::Met2 => vec![Self::Via1, Self::Met2],  | 
 | 169 | +                Self::Outside => vec![],  | 
 | 170 | +            }  | 
 | 171 | +        }  | 
 | 172 | +    }  | 
 | 173 | + | 
 | 174 | +    #[test]  | 
 | 175 | +    fn test_complete() {  | 
 | 176 | +        let mut small_cell: Cell<Layer> = Cell::new("small cell test");  | 
 | 177 | +        let mut big_cell: Cell<Layer> = Cell::new("big cell test");  | 
 | 178 | +        let mut lib_builder: LibraryBuilder<Layer> = LibraryBuilder::new();  | 
 | 179 | + | 
 | 180 | +        // Build small cell first and add to big cell  | 
 | 181 | +        let m2_shape1 = Shape::new(Layer::Met2, Rect::from_sides(0, 0, 30, 30));  | 
 | 182 | +        small_cell.add_element(m2_shape1.clone());  | 
 | 183 | + | 
 | 184 | +        lib_builder.add_cell(small_cell.clone());  | 
 | 185 | +        let small_cell_id = lib_builder.cells().next().unwrap().0;  | 
 | 186 | + | 
 | 187 | +        let small_cell_instance = Instance::new(small_cell_id, "small_cell_test");  | 
 | 188 | +        big_cell.add_instance(small_cell_instance);  | 
 | 189 | + | 
 | 190 | +        // Build big cell  | 
 | 191 | +        let m1_shape = Shape::new(Layer::Met1, Rect::from_sides(0, 0, 100, 100));  | 
 | 192 | +        let v1_shape = Shape::new(Layer::Via1, Rect::from_sides(10, 10, 100, 100));  | 
 | 193 | +        let m2_shape2 = Shape::new(Layer::Met2, Rect::from_sides(10, 10, 50, 50));  | 
 | 194 | +        let m2_shape3 = Shape::new(Layer::Met2, Rect::from_sides(1000, 1000, 5000, 5000));  | 
 | 195 | +        big_cell.add_element(m1_shape.clone());  | 
 | 196 | +        big_cell.add_element(v1_shape.clone());  | 
 | 197 | +        big_cell.add_element(m2_shape2.clone());  | 
 | 198 | +        big_cell.add_element(m2_shape3.clone());  | 
 | 199 | + | 
 | 200 | +        lib_builder.add_cell(big_cell.clone());  | 
 | 201 | + | 
 | 202 | +        // Build fixed library  | 
 | 203 | +        let lib = lib_builder.clone().build().unwrap();  | 
 | 204 | + | 
 | 205 | +        // Add an outside shape for testing  | 
 | 206 | +        let outside_shape = Shape::new(Layer::Outside, Rect::from_sides(0, 0, 100, 100));  | 
 | 207 | + | 
 | 208 | +        // Find all connected components of big_cell's child shapes  | 
 | 209 | +        let components_vec = Layer::connected_components(&big_cell, &lib);  | 
 | 210 | + | 
 | 211 | +        let component_lookup = ComponentLookup::new(components_vec.clone());  | 
 | 212 | + | 
 | 213 | +        // Expected connections  | 
 | 214 | +        assert!(  | 
 | 215 | +            component_lookup.are_connected(&m1_shape, &v1_shape),  | 
 | 216 | +            "m1_shape should be connected to v1_shape"  | 
 | 217 | +        );  | 
 | 218 | +        assert!(  | 
 | 219 | +            component_lookup.are_connected(&m1_shape, &m2_shape2),  | 
 | 220 | +            "m1_shape should be connected to m2_shape2"  | 
 | 221 | +        );  | 
 | 222 | +        assert!(  | 
 | 223 | +            component_lookup.are_connected(&m1_shape, &m2_shape1), // m2_shape1 is from the instance  | 
 | 224 | +            "m1_shape should be connected to m2_shape1"  | 
 | 225 | +        );  | 
 | 226 | + | 
 | 227 | +        // Expected disconnection  | 
 | 228 | +        assert!(  | 
 | 229 | +            !component_lookup.are_connected(&m1_shape, &m2_shape3),  | 
 | 230 | +            "m1_shape should not be connected to m2_shape3 (isolated)"  | 
 | 231 | +        );  | 
 | 232 | +        assert!(  | 
 | 233 | +            !component_lookup.are_connected(&m1_shape, &outside_shape),  | 
 | 234 | +            "m1_shape should not be connected to outside_shape"  | 
 | 235 | +        );  | 
 | 236 | + | 
 | 237 | +        // Double check number of connected components in library  | 
 | 238 | +        assert_eq!(  | 
 | 239 | +            components_vec.len(),  | 
 | 240 | +            2,  | 
 | 241 | +            "Expected 2 total connected components."  | 
 | 242 | +        );  | 
 | 243 | +    }  | 
 | 244 | +}  | 
0 commit comments