Skip to main content

virtuoso_cli/client/
layout_ops.rs

1use crate::client::bridge::escape_skill_string;
2
3#[cfg(test)]
4mod tests {
5    use super::*;
6
7    fn ops() -> LayoutOps {
8        LayoutOps::new()
9    }
10
11    #[test]
12    fn create_rect_skill_format() {
13        let s = ops().create_rect("M1", "drawing", &[(0, 0), (100, 200)]);
14        assert_eq!(
15            s,
16            r#"rodCreateRect(?layer "M1" ?purpose "drawing" ?bBox ((0 0) (100 200)))"#
17        );
18    }
19
20    #[test]
21    fn create_rect_escapes_layer() {
22        let s = ops().create_rect(r#"M"1"#, "drawing", &[(0, 0), (1, 1)]);
23        assert!(s.contains(r#""M\"1""#), "layer must be escaped: {s}");
24    }
25
26    #[test]
27    fn create_polygon_skill_format() {
28        let pts = vec![(0, 0), (10, 0), (10, 10)];
29        let s = ops().create_polygon("poly", "drawing", &pts);
30        assert!(s.contains("rodCreatePolygon"), "{s}");
31        assert!(s.contains("0 0"), "{s}");
32        assert!(s.contains("10 10"), "{s}");
33    }
34
35    #[test]
36    fn create_path_includes_width() {
37        let pts = vec![(0, 0), (50, 0)];
38        let s = ops().create_path("M2", "drawing", 5, &pts);
39        assert!(s.contains("?width 5"), "{s}");
40    }
41
42    #[test]
43    fn create_instance_orientation() {
44        let s = ops().create_instance("tsmc", "nmos", "layout", (10, 20), "MY");
45        assert!(s.contains("\"MY\""), "orient must appear: {s}");
46        assert!(
47            s.contains("10:20") || s.contains("10 20"),
48            "origin must appear: {s}"
49        );
50    }
51}
52
53#[derive(Default)]
54pub struct LayoutOps;
55
56impl LayoutOps {
57    pub fn new() -> Self {
58        Self
59    }
60
61    /// `bbox`: `[(ll_x, ll_y), (ur_x, ur_y)]` — lower-left and upper-right corners.
62    /// Formats to SKILL `?bBox ((ll_x ll_y) (ur_x ur_y))`.
63    pub fn create_rect(&self, layer: &str, purpose: &str, bbox: &[(i64, i64); 2]) -> String {
64        let layer = escape_skill_string(layer);
65        let purpose = escape_skill_string(purpose);
66        let ((x1, y1), (x2, y2)) = (bbox[0], bbox[1]);
67        format!(
68            r#"rodCreateRect(?layer "{layer}" ?purpose "{purpose}" ?bBox (({x1} {y1}) ({x2} {y2})))"#
69        )
70    }
71
72    pub fn create_polygon(&self, layer: &str, purpose: &str, points: &[(i64, i64)]) -> String {
73        let layer = escape_skill_string(layer);
74        let purpose = escape_skill_string(purpose);
75        let pts: String = points
76            .iter()
77            .map(|(x, y)| format!("{x} {y}"))
78            .collect::<Vec<_>>()
79            .join(" ");
80        format!(r#"rodCreatePolygon(?layer "{layer}" ?purpose "{purpose}" ?points list({pts}))"#)
81    }
82
83    pub fn create_path(
84        &self,
85        layer: &str,
86        purpose: &str,
87        width: i64,
88        points: &[(i64, i64)],
89    ) -> String {
90        let layer = escape_skill_string(layer);
91        let purpose = escape_skill_string(purpose);
92        let pts: String = points
93            .iter()
94            .map(|(x, y)| format!("{x} {y}"))
95            .collect::<Vec<_>>()
96            .join(" ");
97        format!(
98            r#"rodCreatePath(?layer "{layer}" ?purpose "{purpose}" ?width {width} ?points list({pts}))"#
99        )
100    }
101
102    pub fn create_via(&self, via_def: &str, origin: (i64, i64)) -> String {
103        let via_def = escape_skill_string(via_def);
104        let (x, y) = origin;
105        format!(r#"rodCreateVia(?viaHeader "{via_def}" ?origin {x}:{y})"#)
106    }
107
108    pub fn create_label(
109        &self,
110        layer: &str,
111        _purpose: &str,
112        text: &str,
113        origin: (i64, i64),
114    ) -> String {
115        let layer = escape_skill_string(layer);
116        let text = escape_skill_string(text);
117        let (x, y) = origin;
118        format!(
119            r#"dbCreateLabel(dbGetCurCellView() dbGetLayerByName(dbGetCurCellView() "{layer}") {x}:{y} "{text}" "centerCenter" "R0" "stick" 0.0625)"#
120        )
121    }
122
123    pub fn create_instance(
124        &self,
125        lib: &str,
126        cell: &str,
127        view: &str,
128        origin: (i64, i64),
129        orient: &str,
130    ) -> String {
131        let lib = escape_skill_string(lib);
132        let cell = escape_skill_string(cell);
133        let view = escape_skill_string(view);
134        let orient = escape_skill_string(orient);
135        let (x, y) = origin;
136        format!(
137            r#"dbCreateInst(dbOpenCellViewByType("{lib}" "{cell}" "{view}" nil "r") nil nil {x}:{y} "{orient}" 1)"#
138        )
139    }
140
141    pub fn set_active_lpp(&self, layer: &str, purpose: &str) -> String {
142        let layer = escape_skill_string(layer);
143        let purpose = escape_skill_string(purpose);
144        format!(r#"leSetEntryLayer(list("{layer}" "{purpose}"))"#)
145    }
146
147    pub fn fit_view(&self) -> String {
148        r#"hiRedraw() hiZoomBox(hiGetCurrentWindow() geGetWindowBox(hiGetCurrentWindow()) geGetEditCellView()~>bBox)"#.into()
149    }
150
151    pub fn read_summary(&self) -> String {
152        r#"let((cv) cv = geGetEditCellView() list(cv~>libName cv~>cellName cv~>viewName cv~>bBox length(cv~>instances) length(cv~>nets)))"#.into()
153    }
154
155    pub fn read_geometry(&self, layer: &str, purpose: &str) -> String {
156        let layer = escape_skill_string(layer);
157        let purpose = escape_skill_string(purpose);
158        format!(
159            r#"let((cv shapes) cv = geGetEditCellView() shapes = nil foreach(shape cv~>shapes when(shape~>lpp == list("{layer}" "{purpose}") shapes = cons(shape~>bBox shapes))) shapes)"#
160        )
161    }
162
163    pub fn delete_shapes_on_layer(&self, layer: &str, purpose: &str) -> String {
164        let layer = escape_skill_string(layer);
165        let purpose = escape_skill_string(purpose);
166        format!(
167            r#"let((cv) cv = geGetEditCellView() foreach(shape cv~>shapes when(shape~>lpp == list("{layer}" "{purpose}") dbDeleteObject(shape))))"#
168        )
169    }
170
171    pub fn highlight_net(&self, net_name: &str) -> String {
172        let net_name = escape_skill_string(net_name);
173        format!(
174            r#"let((cv net) cv = geGetEditCellView() net = dbFindNetByName(cv "{net_name}") when(net hiHighlight(net)))"#
175        )
176    }
177}