Skip to main content

virtuoso_cli/client/
layout_ops.rs

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