1######################################################################
2#                                                                    #
3#  Copyright 2009 Lucas Heitzmann Gabrielli.                         #
4#  This file is part of gdspy, distributed under the terms of the    #
5#  Boost Software License - Version 1.0.  See the accompanying       #
6#  LICENSE file or <http://www.boost.org/LICENSE_1_0.txt>            #
7#                                                                    #
8######################################################################
9
10import pytest
11import gdspy
12import numpy
13import uuid
14import os
15
16gdspy.library.use_current_library = False
17
18
19def unique():
20    return str(uuid.uuid4())
21
22
23@pytest.fixture
24def tree():
25    p1 = gdspy.Polygon(((0, 0), (0, 1), (1, 0)), 0, 0)
26    p2 = gdspy.Polygon(((2, 0), (2, 1), (1, 0)), 1, 1)
27    l1 = gdspy.Label("label1", (0, 0), layer=11)
28    l2 = gdspy.Label("label2", (2, 1), layer=12)
29    c1 = gdspy.Cell("tree_" + unique())
30    c1.add(p1)
31    c1.add(l1)
32    c2 = gdspy.Cell("tree_" + unique())
33    c2.add(l2)
34    c2.add(p2)
35    c2.add(gdspy.CellReference(c1))
36    c3 = gdspy.Cell("tree_" + unique())
37    c3.add(gdspy.CellArray(c2, 3, 2, (3, 3)))
38    return c3, c2, c1
39
40
41def test_duplicate():
42    lib = gdspy.GdsLibrary()
43    name = "c_duplicate"
44    c = gdspy.Cell(name)
45    lib.add(c)
46    with pytest.raises(ValueError) as e:
47        lib.add(gdspy.Cell(name), overwrite_duplicate=False)
48    assert name in str(e.value)
49
50
51def test_ignore_duplicate():
52    lib = gdspy.GdsLibrary()
53    c1 = gdspy.Cell("c_ignore_duplicate")
54    lib.add(c1)
55    lib.add(c1, overwrite_duplicate=False)
56    lib.add(gdspy.Cell(c1.name), overwrite_duplicate=True)
57    assert lib.cells[c1.name] is not c1
58
59
60def test_str():
61    c = gdspy.Cell("c_str")
62    assert str(c) == 'Cell ("c_str", 0 polygons, 0 paths, 0 labels, 0 references)'
63
64
65def test_add_element():
66    p = gdspy.Polygon(((0, 0), (1, 0), (0, 1)))
67    c = gdspy.Cell("c_add_element")
68    assert c.add(p) is c
69    assert c.add([p, p]) is c
70    assert c.polygons == [p, p, p]
71
72
73def test_add_label():
74    lbl = gdspy.Label("label", (0, 0))
75    c = gdspy.Cell("c_add_label")
76    assert c.add(lbl) is c
77    assert c.add([lbl, lbl]) is c
78    assert c.labels == [lbl, lbl, lbl]
79
80
81def test_copy():
82    name = "c_copy"
83    p = gdspy.Polygon(((0, 0), (1, 0), (0, 1)))
84    lbl = gdspy.Label("label", (0, 0))
85    c1 = gdspy.Cell(name)
86    c1.add(p)
87    c1.add(lbl)
88    c3 = c1.copy(name, False)
89    assert c3.polygons == c1.polygons and c3.polygons is not c1.polygons
90    assert c3.labels == c1.labels and c3.labels is not c1.labels
91    cref = gdspy.Cell("c_ref").add(gdspy.Rectangle((-1, -1), (-2, -2)))
92    c1.add(gdspy.CellReference(cref))
93    c1.get_bounding_box()
94    c4 = c1.copy("c_copy_1", True)
95    assert c4.polygons != c1.polygons
96    assert c4.labels != c1.labels
97    assert c1._bb_valid
98    assert cref._bb_valid
99    assert not c4._bb_valid
100
101
102def test_remove(tree):
103    c3, c2, c1 = tree
104    c1.remove_polygons(lambda p, layer, d: layer == 1)
105    assert len(c1.polygons) == 1
106    c1.remove_polygons(lambda p, layer, d: layer == 0)
107    assert len(c1.polygons) == 0
108    c1.remove_labels(lambda lbl: lbl.layer == 12)
109    assert len(c1.labels) == 1
110    c1.remove_labels(lambda lbl: lbl.layer == 11)
111    assert len(c1.labels) == 0
112
113
114def test_area():
115    c = gdspy.Cell("c_area")
116    c.add(gdspy.Rectangle((0, 0), (1, 1), layer=0))
117    c.add(gdspy.Rectangle((0, 0), (1, 1), layer=1))
118    c.add(gdspy.Rectangle((1, 1), (2, 2), layer=1))
119    c.add(gdspy.Rectangle((1, 1), (2, 2), datatype=2))
120    assert c.area() == 4.0
121    assert c.area(True) == {(0, 0): 1.0, (1, 0): 2.0, (0, 2): 1}
122
123
124def test_flatten_00(tree):
125    c3, c2, c1 = tree
126    c3.flatten()
127    assert len(c3.polygons) == 12
128    for i in range(12):
129        assert c3.polygons[i].layers == [0] or c3.polygons[i].layers == [1]
130        assert c3.polygons[i].layers == c3.polygons[i].datatypes
131    assert len(c3.labels) == 12
132
133
134def test_flatten_01(tree):
135    c3, c2, c1 = tree
136    c3.flatten(None, 2, 3)
137    assert len(c3.polygons) == 12
138    for i in range(12):
139        assert c3.polygons[i].layers == [0] or c3.polygons[i].layers == [1]
140        assert c3.polygons[i].datatypes == [2]
141    assert len(c3.labels) == 12
142    assert all(lbl.texttype == 3 for lbl in c3.labels)
143
144
145def test_flatten_10(tree):
146    c3, c2, c1 = tree
147    c3.flatten(2)
148    assert len(c3.polygons) == 12
149    for i in range(12):
150        assert c3.polygons[i].datatypes == [0] or c3.polygons[i].datatypes == [1]
151        assert c3.polygons[i].layers == [2]
152    assert len(c3.labels) == 12
153    assert all(lbl.layer == 2 for lbl in c3.labels)
154
155
156def test_flatten_11(tree):
157    c3, c2, c1 = tree
158    c3.flatten(2, 3, 4)
159    assert len(c3.polygons) == 12
160    assert all(p.layers == [2] for p in c3.polygons)
161    assert all(p.datatypes == [3] for p in c3.polygons)
162    assert len(c3.labels) == 12
163    assert all(lbl.layer == 2 for lbl in c3.labels)
164    assert all(lbl.texttype == 4 for lbl in c3.labels)
165
166
167def test_bb(tree):
168    c3, c2, c1 = tree
169    err = numpy.array(((0, 0), (8, 4))) - c3.get_bounding_box()
170    assert numpy.max(numpy.abs(err)) == 0
171
172    p2 = gdspy.Polygon(((-1, 2), (-1, 1), (0, 2)), 2, 2)
173    c2.add(p2)
174    err = numpy.array(((-1, 0), (8, 5))) - c3.get_bounding_box()
175    assert numpy.max(numpy.abs(err)) == 0
176
177    p1 = gdspy.Polygon(((0, 3), (0, 2), (1, 3)), 3, 3)
178    c1.add(p1)
179    err = numpy.array(((-1, 0), (8, 6))) - c3.get_bounding_box()
180    assert numpy.max(numpy.abs(err)) == 0
181
182
183def test_layers(tree):
184    assert tree[0].get_layers() == {0, 1, 11, 12}
185
186
187def test_datatypes(tree):
188    assert tree[0].get_datatypes() == {0, 1}
189
190
191def test_get_polygons1(tree):
192    c3, c2, c1 = tree
193    p1 = gdspy.Polygon(((0, 3), (0, 2), (1, 3)), 3, 3)
194    c1.add(p1)
195    assert len(c3.get_polygons()) == 18
196    assert len(c3.get_polygons(False, 0)) == 6
197    assert len(c3.get_polygons(False, 1)) == 12
198    assert set(c3.get_polygons(True).keys()) == {(0, 0), (1, 1), (3, 3)}
199    assert set(c3.get_polygons(True, 0).keys()) == {c2.name}
200    assert set(c3.get_polygons(True, 1).keys()) == {c1.name, (1, 1)}
201
202
203def test_get_polygons2(tree):
204    c3, c2, c1 = tree
205    c1.add(gdspy.Rectangle((0, 0), (1, 1), 0, 0))
206    assert len(c1.get_polygons()) == 2
207    d = c1.get_polygons(True)
208    assert len(d) == 1
209    assert (0, 0) in d
210    assert len(d[(0, 0)]) == 2
211    c3.add(gdspy.CellReference(c1))
212    d = c3.get_polygons(True)
213    assert len(d) == 2
214    assert (0, 0) in d and (1, 1) in d
215    assert len(d[(0, 0)]) == 14
216    assert len(d[(1, 1)]) == 6
217
218
219def test_get_polygons3():
220    c0 = gdspy.Cell("empty")
221    assert len(c0.get_polygons()) == 0
222    assert len(c0.get_polygons(True)) == 0
223    assert len(c0.get_polygons(False, -1)) == 0
224
225
226def test_get_polygons4(tree):
227    c3, c2, c1 = tree
228    c3.add(gdspy.Rectangle((0, 0), (1, 1), 0, 0))
229    assert len(c3.get_polygons((0, 0))) == 7
230    assert len(c3.get_polygons((0, 0), 0)) == 1
231    assert len(c3.get_polygons((1, 1), 0)) == 0
232    assert len(c3.get_polygons((0, 0), 1)) == 1
233    assert len(c3.get_polygons((1, 1), 1)) == 6
234
235
236def test_write_svg(tree, tmpdir):
237    _, _, c1 = tree
238    fname = str(tmpdir.join("c1.svg"))
239    c1.write_svg(fname)
240    assert os.path.isfile(fname)
241    assert not os.stat(fname).st_size == 0
242
243
244def test_write_svg_with_style(tree, tmpdir):
245    _, _, c1 = tree
246    fname = str(tmpdir.join("c1.svg"))
247    style = {(0, 0): {"fill": "CC00FF"}}
248    c1.write_svg(fname, style=style)
249    assert os.path.isfile(fname)
250    assert not os.stat(fname).st_size == 0
251
252
253def assert_bb(bb1, bb2):
254    for p1, p2 in zip(bb1, bb2):
255        for c1, c2 in zip(p1, p2):
256            assert abs(c1 - c2) < 1e-12
257
258
259def test_bounding_box():
260    cell1 = gdspy.Cell("1")
261    cell1.add(gdspy.Rectangle((0, 0), (1, 1)))
262
263    # Cell1: plain 1x1 um square, with bottom-left corner at 0,0
264    assert_bb(cell1.get_bounding_box(), ((0, 0), (1, 1)))
265
266    # Cell2: place and rotate Cell1 45deg about origin
267    cell2 = gdspy.Cell("2")
268    cell2.add(gdspy.CellReference(cell1, rotation=45))
269
270    assert_bb(cell2.get_bounding_box(), ((-(0.5 ** 0.5), 0), (0.5 ** 0.5, 2 ** 0.5)))
271
272    # Cell3: place and rotate Cell2 an additional 45deg about origin (Cell1 is now rotated total 90deg)
273    cell3 = gdspy.Cell("3")
274    cell3.add(gdspy.CellReference(cell2, rotation=45))
275
276    assert_bb(cell3.get_bounding_box(), ((-1, 0), (0, 1)))
277
278    # Cell4: nest Cell2 one level deeper with no transform (geometric equivalent to Cell2)
279    cell4 = gdspy.Cell("4")
280    cell4.add(gdspy.CellReference(cell2))
281
282    assert_bb(cell4.get_bounding_box(), ((-(0.5 ** 0.5), 0), (0.5 ** 0.5, 2 ** 0.5)))
283
284    # Cell5: rotate Cell4 an addition 45 degrees (geometric equivalent to Cell3)
285    cell5 = gdspy.Cell("5")
286    cell5.add(gdspy.CellReference(cell4, rotation=45))
287
288    assert_bb(cell5.get_bounding_box(), ((-1, 0), (0, 1)))
289
290    # Cell6: translate Cell1 by 2um east
291    cell6 = gdspy.Cell("6")
292    cell6.add(gdspy.CellReference(cell1, origin=(2, 0)))
293
294    assert_bb(cell6.get_bounding_box(), ((2, 0), (3, 1)))
295
296    cell7a = gdspy.Cell("7a")
297
298    assert cell7a.get_bounding_box() is None
299
300    cell7b = gdspy.Cell("7b")
301    cell7b.add(gdspy.CellReference(cell7a, rotation=90))
302
303    assert cell7b.get_bounding_box() is None
304
305    cell7c = gdspy.Cell("7c")
306    cell7c.add(gdspy.CellReference(cell7b))
307
308    assert cell7c.get_bounding_box() is None
309
310    cell7 = gdspy.Cell("7")
311    cell7.add(cell1)
312    cell7.add(cell7c)
313
314    assert_bb(cell7.get_bounding_box(), ((0, 0), (1, 1)))
315
316
317def test_bounding_box2():
318    cell0 = gdspy.Cell("0")
319    cell0.add(gdspy.Rectangle((0, 0), (0.5, 0.25)))
320
321    cell1 = gdspy.Cell("1")
322    cell1.add(gdspy.CellArray(cell0, 2, 4, (0.5, 0.25)))
323
324    # Cell1: plain 1x1 um square, with bottom-left corner at 0,0
325    assert_bb(cell1.get_bounding_box(), ((0, 0), (1, 1)))
326
327    # Cell2: place and rotate Cell1 45deg about origin
328    cell2 = gdspy.Cell("2")
329    cell2.add(gdspy.CellReference(cell1, rotation=45))
330
331    assert_bb(cell2.get_bounding_box(), ((-(0.5 ** 0.5), 0), (0.5 ** 0.5, 2 ** 0.5)))
332
333    # Cell3: place and rotate Cell2 an additional 45deg about origin (Cell1 is now rotated total 90deg)
334    cell3 = gdspy.Cell("3")
335    cell3.add(gdspy.CellReference(cell2, rotation=45))
336
337    assert_bb(cell3.get_bounding_box(), ((-1, 0), (0, 1)))
338
339    # Cell4: nest Cell2 one level deeper with no transform (geometric equivalent to Cell2)
340    cell4 = gdspy.Cell("4")
341    cell4.add(gdspy.CellArray(cell2, 1, 1, (1, 1)))
342
343    assert_bb(cell4.get_bounding_box(), ((-(0.5 ** 0.5), 0), (0.5 ** 0.5, 2 ** 0.5)))
344
345    # Cell5: rotate Cell4 an addition 45 degrees (geometric equivalent to Cell3)
346    cell5 = gdspy.Cell("5")
347    cell5.add(gdspy.CellArray(cell4, 1, 1, (1, 1), rotation=45))
348
349    assert_bb(cell5.get_bounding_box(), ((-1, 0), (0, 1)))
350
351    # Cell6: translate Cell1 by 2um east
352    cell6 = gdspy.Cell("6")
353    cell6.add(gdspy.CellArray(cell1, 1, 1, (1, 1), origin=(2, 0)))
354
355    assert_bb(cell6.get_bounding_box(), ((2, 0), (3, 1)))
356