virtuoso_cli/client/
layout_ops.rs1#![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 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}