pyedb 0.23.0__py3-none-any.whl → 0.25.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pyedb might be problematic. Click here for more details.
- pyedb/__init__.py +1 -1
- pyedb/component_libraries/ansys_components.py +48 -2
- pyedb/configuration/cfg_operations.py +38 -4
- pyedb/configuration/cfg_ports_sources.py +16 -2
- pyedb/configuration/configuration.py +8 -0
- pyedb/dotnet/edb.py +39 -37
- pyedb/dotnet/edb_core/cell/hierarchy/hierarchy_obj.py +11 -0
- pyedb/dotnet/edb_core/cell/layout.py +47 -23
- pyedb/dotnet/edb_core/cell/layout_obj.py +0 -9
- pyedb/dotnet/edb_core/cell/primitive/__init__.py +3 -0
- pyedb/dotnet/edb_core/cell/{primitive.py → primitive/bondwire.py} +1 -146
- pyedb/dotnet/edb_core/cell/primitive/path.py +351 -0
- pyedb/dotnet/edb_core/cell/primitive/primitive.py +895 -0
- pyedb/dotnet/edb_core/cell/terminal/bundle_terminal.py +0 -4
- pyedb/dotnet/edb_core/cell/terminal/edge_terminal.py +1 -1
- pyedb/dotnet/edb_core/cell/terminal/terminal.py +2 -2
- pyedb/dotnet/edb_core/components.py +48 -25
- pyedb/dotnet/edb_core/dotnet/database.py +1 -23
- pyedb/dotnet/edb_core/dotnet/primitive.py +3 -139
- pyedb/dotnet/edb_core/edb_data/nets_data.py +1 -11
- pyedb/dotnet/edb_core/edb_data/padstacks_data.py +10 -24
- pyedb/dotnet/edb_core/edb_data/primitives_data.py +56 -868
- pyedb/dotnet/edb_core/geometry/polygon_data.py +43 -0
- pyedb/dotnet/edb_core/hfss.py +26 -22
- pyedb/dotnet/edb_core/layout_validation.py +3 -3
- pyedb/dotnet/edb_core/modeler.py +64 -81
- pyedb/dotnet/edb_core/nets.py +5 -4
- pyedb/dotnet/edb_core/padstack.py +12 -13
- pyedb/dotnet/edb_core/siwave.py +1 -1
- pyedb/dotnet/edb_core/stackup.py +3 -3
- pyedb/ipc2581/ecad/cad_data/layer_feature.py +1 -1
- pyedb/ipc2581/ecad/cad_data/polygon.py +2 -2
- pyedb/ipc2581/ecad/cad_data/step.py +2 -2
- pyedb/siwave.py +99 -0
- {pyedb-0.23.0.dist-info → pyedb-0.25.0.dist-info}/METADATA +4 -4
- {pyedb-0.23.0.dist-info → pyedb-0.25.0.dist-info}/RECORD +38 -35
- {pyedb-0.23.0.dist-info → pyedb-0.25.0.dist-info}/LICENSE +0 -0
- {pyedb-0.23.0.dist-info → pyedb-0.25.0.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,895 @@
|
|
|
1
|
+
# Copyright (C) 2023 - 2024 ANSYS, Inc. and/or its affiliates.
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
#
|
|
4
|
+
#
|
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
# furnished to do so, subject to the following conditions:
|
|
11
|
+
#
|
|
12
|
+
# The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
# copies or substantial portions of the Software.
|
|
14
|
+
#
|
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
# SOFTWARE.
|
|
22
|
+
import math
|
|
23
|
+
|
|
24
|
+
from pyedb.dotnet.edb_core.cell.connectable import Connectable
|
|
25
|
+
from pyedb.dotnet.edb_core.general import convert_py_list_to_net_list
|
|
26
|
+
from pyedb.dotnet.edb_core.geometry.polygon_data import PolygonData
|
|
27
|
+
from pyedb.modeler.geometry_operators import GeometryOperators
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class Primitive(Connectable):
|
|
31
|
+
"""Manages EDB functionalities for a primitives.
|
|
32
|
+
It inherits EDB Object properties.
|
|
33
|
+
|
|
34
|
+
Examples
|
|
35
|
+
--------
|
|
36
|
+
>>> from pyedb import Edb
|
|
37
|
+
>>> edb = Edb(myedb, edbversion="2021.2")
|
|
38
|
+
>>> edb_prim = edb.modeler.primitives[0]
|
|
39
|
+
>>> edb_prim.is_void # Class Property
|
|
40
|
+
>>> edb_prim.IsVoid() # EDB Object Property
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
def __init__(self, pedb, edb_object):
|
|
44
|
+
super().__init__(pedb, edb_object)
|
|
45
|
+
self._app = self._pedb
|
|
46
|
+
self._core_stackup = pedb.stackup
|
|
47
|
+
self._core_net = pedb.nets
|
|
48
|
+
self.primitive_object = self._edb_object
|
|
49
|
+
|
|
50
|
+
bondwire_type = self._pedb._edb.Cell.Primitive.BondwireType
|
|
51
|
+
self._bondwire_type = {
|
|
52
|
+
"invalid": bondwire_type.Invalid,
|
|
53
|
+
"apd": bondwire_type.ApdBondwire,
|
|
54
|
+
"jedec_4": bondwire_type.Jedec4Bondwire,
|
|
55
|
+
"jedec_5": bondwire_type.Jedec5Bondwire,
|
|
56
|
+
"num_of_bondwire_type": bondwire_type.NumOfBondwireType,
|
|
57
|
+
}
|
|
58
|
+
bondwire_cross_section_type = self._pedb._edb.Cell.Primitive.BondwireCrossSectionType
|
|
59
|
+
self._bondwire_cross_section_type = {
|
|
60
|
+
"invalid": bondwire_cross_section_type.Invalid,
|
|
61
|
+
"round": bondwire_cross_section_type.BondwireRound,
|
|
62
|
+
"rectangle": bondwire_cross_section_type.BondwireRectangle,
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
def type(self):
|
|
67
|
+
"""Return the type of the primitive.
|
|
68
|
+
|
|
69
|
+
Expected output is among ``"Circle"``, ``"Rectangle"``,``"Polygon"``,``"Path"`` or ``"Bondwire"``.
|
|
70
|
+
|
|
71
|
+
Returns
|
|
72
|
+
-------
|
|
73
|
+
str
|
|
74
|
+
"""
|
|
75
|
+
try:
|
|
76
|
+
return self._edb_object.GetPrimitiveType().ToString()
|
|
77
|
+
except AttributeError: # pragma: no cover
|
|
78
|
+
return ""
|
|
79
|
+
|
|
80
|
+
@property
|
|
81
|
+
def primitive_type(self):
|
|
82
|
+
"""Return the type of the primitive.
|
|
83
|
+
|
|
84
|
+
Expected output is among ``"circle"``, ``"rectangle"``,``"polygon"``,``"path"`` or ``"bondwire"``.
|
|
85
|
+
|
|
86
|
+
Returns
|
|
87
|
+
-------
|
|
88
|
+
str
|
|
89
|
+
"""
|
|
90
|
+
return self._edb_object.GetPrimitiveType().ToString().lower()
|
|
91
|
+
|
|
92
|
+
@property
|
|
93
|
+
def net_name(self):
|
|
94
|
+
"""Get the primitive net name.
|
|
95
|
+
|
|
96
|
+
Returns
|
|
97
|
+
-------
|
|
98
|
+
str
|
|
99
|
+
"""
|
|
100
|
+
return self.net.name
|
|
101
|
+
|
|
102
|
+
@net_name.setter
|
|
103
|
+
def net_name(self, name):
|
|
104
|
+
if isinstance(name, str):
|
|
105
|
+
net = self._app.nets.nets[name].net_object
|
|
106
|
+
self.primitive_object.SetNet(net)
|
|
107
|
+
else:
|
|
108
|
+
try:
|
|
109
|
+
self.net = name.name
|
|
110
|
+
except: # pragma: no cover
|
|
111
|
+
self._app.logger.error("Failed to set net name.")
|
|
112
|
+
|
|
113
|
+
@property
|
|
114
|
+
def layer(self):
|
|
115
|
+
"""Get the primitive edb layer object."""
|
|
116
|
+
layer_name = self._edb_object.GetLayer().GetName()
|
|
117
|
+
return self._pedb.stackup.all_layers[layer_name]
|
|
118
|
+
|
|
119
|
+
@property
|
|
120
|
+
def layer_name(self):
|
|
121
|
+
"""Get the primitive layer name.
|
|
122
|
+
|
|
123
|
+
Returns
|
|
124
|
+
-------
|
|
125
|
+
str
|
|
126
|
+
"""
|
|
127
|
+
try:
|
|
128
|
+
return self.layer.name
|
|
129
|
+
except (KeyError, AttributeError): # pragma: no cover
|
|
130
|
+
return None
|
|
131
|
+
|
|
132
|
+
@layer_name.setter
|
|
133
|
+
def layer_name(self, val):
|
|
134
|
+
layer_list = list(self._core_stackup.layers.keys())
|
|
135
|
+
if isinstance(val, str) and val in layer_list:
|
|
136
|
+
layer = self._core_stackup.layers[val]._edb_layer
|
|
137
|
+
if layer:
|
|
138
|
+
self.primitive_object.SetLayer(layer)
|
|
139
|
+
else:
|
|
140
|
+
raise AttributeError("Layer {} not found.".format(val))
|
|
141
|
+
elif isinstance(val, type(self._core_stackup.layers[layer_list[0]])):
|
|
142
|
+
try:
|
|
143
|
+
self.primitive_object.SetLayer(val._edb_layer)
|
|
144
|
+
except:
|
|
145
|
+
raise AttributeError("Failed to assign new layer on primitive.")
|
|
146
|
+
else:
|
|
147
|
+
raise AttributeError("Invalid input value")
|
|
148
|
+
|
|
149
|
+
@property
|
|
150
|
+
def is_void(self):
|
|
151
|
+
"""Either if the primitive is a void or not.
|
|
152
|
+
|
|
153
|
+
Returns
|
|
154
|
+
-------
|
|
155
|
+
bool
|
|
156
|
+
"""
|
|
157
|
+
return self._edb_object.IsVoid()
|
|
158
|
+
|
|
159
|
+
def get_connected_objects(self):
|
|
160
|
+
"""Get connected objects.
|
|
161
|
+
|
|
162
|
+
Returns
|
|
163
|
+
-------
|
|
164
|
+
list
|
|
165
|
+
"""
|
|
166
|
+
return self._pedb.get_connected_objects(self._layout_obj_instance)
|
|
167
|
+
|
|
168
|
+
def area(self, include_voids=True):
|
|
169
|
+
"""Return the total area.
|
|
170
|
+
|
|
171
|
+
Parameters
|
|
172
|
+
----------
|
|
173
|
+
include_voids : bool, optional
|
|
174
|
+
Either if the voids have to be included in computation.
|
|
175
|
+
The default value is ``True``.
|
|
176
|
+
|
|
177
|
+
Returns
|
|
178
|
+
-------
|
|
179
|
+
float
|
|
180
|
+
"""
|
|
181
|
+
area = self._edb_object.GetPolygonData().Area()
|
|
182
|
+
if include_voids:
|
|
183
|
+
for el in self._edb_object.Voids:
|
|
184
|
+
area -= el.GetPolygonData().Area()
|
|
185
|
+
return area
|
|
186
|
+
|
|
187
|
+
@property
|
|
188
|
+
def is_negative(self):
|
|
189
|
+
"""Determine whether this primitive is negative.
|
|
190
|
+
|
|
191
|
+
Returns
|
|
192
|
+
-------
|
|
193
|
+
bool
|
|
194
|
+
True if it is negative, False otherwise.
|
|
195
|
+
"""
|
|
196
|
+
return self._edb_object.GetIsNegative()
|
|
197
|
+
|
|
198
|
+
@is_negative.setter
|
|
199
|
+
def is_negative(self, value):
|
|
200
|
+
self._edb_object.SetIsNegative(value)
|
|
201
|
+
|
|
202
|
+
@staticmethod
|
|
203
|
+
def _eval_arc_points(p1, p2, h, n=6, tol=1e-12):
|
|
204
|
+
"""Get the points of the arc
|
|
205
|
+
|
|
206
|
+
Parameters
|
|
207
|
+
----------
|
|
208
|
+
p1 : list
|
|
209
|
+
Arc starting point.
|
|
210
|
+
p2 : list
|
|
211
|
+
Arc ending point.
|
|
212
|
+
h : float
|
|
213
|
+
Arc height.
|
|
214
|
+
n : int
|
|
215
|
+
Number of points to generate along the arc.
|
|
216
|
+
tol : float
|
|
217
|
+
Geometric tolerance.
|
|
218
|
+
|
|
219
|
+
Returns
|
|
220
|
+
-------
|
|
221
|
+
list, list
|
|
222
|
+
Points generated along the arc.
|
|
223
|
+
"""
|
|
224
|
+
# fmt: off
|
|
225
|
+
if abs(h) < tol:
|
|
226
|
+
return [], []
|
|
227
|
+
elif h > 0:
|
|
228
|
+
reverse = False
|
|
229
|
+
x1 = p1[0]
|
|
230
|
+
y1 = p1[1]
|
|
231
|
+
x2 = p2[0]
|
|
232
|
+
y2 = p2[1]
|
|
233
|
+
else:
|
|
234
|
+
reverse = True
|
|
235
|
+
x1 = p2[0]
|
|
236
|
+
y1 = p2[1]
|
|
237
|
+
x2 = p1[0]
|
|
238
|
+
y2 = p1[1]
|
|
239
|
+
h *= -1
|
|
240
|
+
xa = (x2 - x1) / 2
|
|
241
|
+
ya = (y2 - y1) / 2
|
|
242
|
+
xo = x1 + xa
|
|
243
|
+
yo = y1 + ya
|
|
244
|
+
a = math.sqrt(xa ** 2 + ya ** 2)
|
|
245
|
+
if a < tol:
|
|
246
|
+
return [], []
|
|
247
|
+
r = (a ** 2) / (2 * h) + h / 2
|
|
248
|
+
if abs(r - a) < tol:
|
|
249
|
+
b = 0
|
|
250
|
+
th = 2 * math.asin(1) # chord angle
|
|
251
|
+
else:
|
|
252
|
+
b = math.sqrt(r ** 2 - a ** 2)
|
|
253
|
+
th = 2 * math.asin(a / r) # chord angle
|
|
254
|
+
|
|
255
|
+
# center of the circle
|
|
256
|
+
xc = xo + b * ya / a
|
|
257
|
+
yc = yo - b * xa / a
|
|
258
|
+
|
|
259
|
+
alpha = math.atan2((y1 - yc), (x1 - xc))
|
|
260
|
+
xr = []
|
|
261
|
+
yr = []
|
|
262
|
+
for i in range(n):
|
|
263
|
+
i += 1
|
|
264
|
+
dth = (float(i) / (n + 1)) * th
|
|
265
|
+
xi = xc + r * math.cos(alpha - dth)
|
|
266
|
+
yi = yc + r * math.sin(alpha - dth)
|
|
267
|
+
xr.append(xi)
|
|
268
|
+
yr.append(yi)
|
|
269
|
+
|
|
270
|
+
if reverse:
|
|
271
|
+
xr.reverse()
|
|
272
|
+
yr.reverse()
|
|
273
|
+
# fmt: on
|
|
274
|
+
return xr, yr
|
|
275
|
+
|
|
276
|
+
def _get_points_for_plot(self, my_net_points, num):
|
|
277
|
+
"""
|
|
278
|
+
Get the points to be plotted.
|
|
279
|
+
"""
|
|
280
|
+
# fmt: off
|
|
281
|
+
x = []
|
|
282
|
+
y = []
|
|
283
|
+
for i, point in enumerate(my_net_points):
|
|
284
|
+
if not self.is_arc(point):
|
|
285
|
+
x.append(point.X.ToDouble())
|
|
286
|
+
y.append(point.Y.ToDouble())
|
|
287
|
+
# i += 1
|
|
288
|
+
else:
|
|
289
|
+
arc_h = point.GetArcHeight().ToDouble()
|
|
290
|
+
p1 = [my_net_points[i - 1].X.ToDouble(), my_net_points[i - 1].Y.ToDouble()]
|
|
291
|
+
if i + 1 < len(my_net_points):
|
|
292
|
+
p2 = [my_net_points[i + 1].X.ToDouble(), my_net_points[i + 1].Y.ToDouble()]
|
|
293
|
+
else:
|
|
294
|
+
p2 = [my_net_points[0].X.ToDouble(), my_net_points[0].Y.ToDouble()]
|
|
295
|
+
x_arc, y_arc = self._eval_arc_points(p1, p2, arc_h, num)
|
|
296
|
+
x.extend(x_arc)
|
|
297
|
+
y.extend(y_arc)
|
|
298
|
+
# i += 1
|
|
299
|
+
# fmt: on
|
|
300
|
+
return x, y
|
|
301
|
+
|
|
302
|
+
@property
|
|
303
|
+
def center(self):
|
|
304
|
+
"""Return the primitive bounding box center coordinate.
|
|
305
|
+
|
|
306
|
+
Returns
|
|
307
|
+
-------
|
|
308
|
+
list
|
|
309
|
+
[x, y]
|
|
310
|
+
|
|
311
|
+
"""
|
|
312
|
+
bbox = self.bbox
|
|
313
|
+
return [(bbox[0] + bbox[2]) / 2, (bbox[1] + bbox[3]) / 2]
|
|
314
|
+
|
|
315
|
+
def is_arc(self, point):
|
|
316
|
+
"""Either if a point is an arc or not.
|
|
317
|
+
|
|
318
|
+
Returns
|
|
319
|
+
-------
|
|
320
|
+
bool
|
|
321
|
+
"""
|
|
322
|
+
return point.IsArc()
|
|
323
|
+
|
|
324
|
+
def get_connected_object_id_set(self):
|
|
325
|
+
"""Produce a list of all geometries physically connected to a given layout object.
|
|
326
|
+
|
|
327
|
+
Returns
|
|
328
|
+
-------
|
|
329
|
+
list
|
|
330
|
+
Found connected objects IDs with Layout object.
|
|
331
|
+
"""
|
|
332
|
+
layoutInst = self._edb_object.GetLayout().GetLayoutInstance()
|
|
333
|
+
layoutObjInst = layoutInst.GetLayoutObjInstance(self._edb_object, None) # 2nd arg was []
|
|
334
|
+
return [loi.GetLayoutObj().GetId() for loi in layoutInst.GetConnectedObjects(layoutObjInst).Items]
|
|
335
|
+
|
|
336
|
+
@property
|
|
337
|
+
def bbox(self):
|
|
338
|
+
"""Return the primitive bounding box points. Lower left corner, upper right corner.
|
|
339
|
+
|
|
340
|
+
Returns
|
|
341
|
+
-------
|
|
342
|
+
list
|
|
343
|
+
[lower_left x, lower_left y, upper right x, upper right y]
|
|
344
|
+
|
|
345
|
+
"""
|
|
346
|
+
bbox = self.polygon_data._edb_object.GetBBox()
|
|
347
|
+
return [bbox.Item1.X.ToDouble(), bbox.Item1.Y.ToDouble(), bbox.Item2.X.ToDouble(), bbox.Item2.Y.ToDouble()]
|
|
348
|
+
|
|
349
|
+
def convert_to_polygon(self):
|
|
350
|
+
"""Convert path to polygon.
|
|
351
|
+
|
|
352
|
+
Returns
|
|
353
|
+
-------
|
|
354
|
+
bool, :class:`dotnet.edb_core.edb_data.primitives.EDBPrimitives`
|
|
355
|
+
Polygon when successful, ``False`` when failed.
|
|
356
|
+
|
|
357
|
+
"""
|
|
358
|
+
if self.type == "Path":
|
|
359
|
+
polygon_data = self._edb_object.GetPolygonData()
|
|
360
|
+
polygon = self._app.modeler.create_polygon(polygon_data, self.layer_name, [], self.net_name)
|
|
361
|
+
self._edb_object.Delete()
|
|
362
|
+
return polygon
|
|
363
|
+
else:
|
|
364
|
+
return False
|
|
365
|
+
|
|
366
|
+
def intersection_type(self, primitive):
|
|
367
|
+
"""Get intersection type between actual primitive and another primitive or polygon data.
|
|
368
|
+
|
|
369
|
+
Parameters
|
|
370
|
+
----------
|
|
371
|
+
primitive : :class:`pyaeedt.edb_core.edb_data.primitives_data.EDBPrimitives` or `PolygonData`
|
|
372
|
+
|
|
373
|
+
Returns
|
|
374
|
+
-------
|
|
375
|
+
int
|
|
376
|
+
Intersection type:
|
|
377
|
+
0 - objects do not intersect,
|
|
378
|
+
1 - this object fully inside other (no common contour points),
|
|
379
|
+
2 - other object fully inside this,
|
|
380
|
+
3 - common contour points,
|
|
381
|
+
4 - undefined intersection.
|
|
382
|
+
"""
|
|
383
|
+
poly = primitive
|
|
384
|
+
try:
|
|
385
|
+
poly = primitive.polygon_data
|
|
386
|
+
except AttributeError:
|
|
387
|
+
pass
|
|
388
|
+
return int(self.polygon_data._edb_object.GetIntersectionType(poly._edb_object))
|
|
389
|
+
|
|
390
|
+
def is_intersecting(self, primitive):
|
|
391
|
+
"""Check if actual primitive and another primitive or polygon data intesects.
|
|
392
|
+
|
|
393
|
+
Parameters
|
|
394
|
+
----------
|
|
395
|
+
primitive : :class:`pyaeedt.edb_core.edb_data.primitives_data.EDBPrimitives` or `PolygonData`
|
|
396
|
+
|
|
397
|
+
Returns
|
|
398
|
+
-------
|
|
399
|
+
bool
|
|
400
|
+
"""
|
|
401
|
+
return True if self.intersection_type(primitive) >= 1 else False
|
|
402
|
+
|
|
403
|
+
def get_closest_point(self, point):
|
|
404
|
+
"""Get the closest point of the primitive to the input data.
|
|
405
|
+
|
|
406
|
+
Parameters
|
|
407
|
+
----------
|
|
408
|
+
point : list of float or PointData
|
|
409
|
+
|
|
410
|
+
Returns
|
|
411
|
+
-------
|
|
412
|
+
list of float
|
|
413
|
+
"""
|
|
414
|
+
if isinstance(point, (list, tuple)):
|
|
415
|
+
point = self._app.edb_api.geometry.point_data(self._app.edb_value(point[0]), self._app.edb_value(point[1]))
|
|
416
|
+
|
|
417
|
+
p0 = self.polygon_data._edb_object.GetClosestPoint(point)
|
|
418
|
+
return [p0.X.ToDouble(), p0.Y.ToDouble()]
|
|
419
|
+
|
|
420
|
+
@property
|
|
421
|
+
def arcs(self):
|
|
422
|
+
"""Get the Primitive Arc Data."""
|
|
423
|
+
return self.polygon_data.arcs
|
|
424
|
+
|
|
425
|
+
@property
|
|
426
|
+
def longest_arc(self):
|
|
427
|
+
"""Get the longest arc."""
|
|
428
|
+
len = 0
|
|
429
|
+
arc = None
|
|
430
|
+
for i in self.arcs:
|
|
431
|
+
if i.is_segment and i.length > len:
|
|
432
|
+
arc = i
|
|
433
|
+
len = i.length
|
|
434
|
+
return arc
|
|
435
|
+
|
|
436
|
+
def subtract(self, primitives):
|
|
437
|
+
"""Subtract active primitive with one or more primitives.
|
|
438
|
+
|
|
439
|
+
Parameters
|
|
440
|
+
----------
|
|
441
|
+
primitives : :class:`dotnet.edb_core.edb_data.EDBPrimitives` or EDB PolygonData or EDB Primitive or list
|
|
442
|
+
|
|
443
|
+
Returns
|
|
444
|
+
-------
|
|
445
|
+
List of :class:`dotnet.edb_core.edb_data.EDBPrimitives`
|
|
446
|
+
"""
|
|
447
|
+
poly = self.primitive_object.GetPolygonData()
|
|
448
|
+
if not isinstance(primitives, list):
|
|
449
|
+
primitives = [primitives]
|
|
450
|
+
primi_polys = []
|
|
451
|
+
voids_of_prims = []
|
|
452
|
+
for prim in primitives:
|
|
453
|
+
if isinstance(prim, Primitive):
|
|
454
|
+
primi_polys.append(prim.primitive_object.GetPolygonData())
|
|
455
|
+
for void in prim.voids:
|
|
456
|
+
voids_of_prims.append(void.polygon_data._edb_object)
|
|
457
|
+
else:
|
|
458
|
+
try:
|
|
459
|
+
primi_polys.append(prim.GetPolygonData())
|
|
460
|
+
except:
|
|
461
|
+
primi_polys.append(prim)
|
|
462
|
+
for v in self.voids[:]:
|
|
463
|
+
primi_polys.append(v.polygon_data._edb_object)
|
|
464
|
+
primi_polys = poly.Unite(convert_py_list_to_net_list(primi_polys))
|
|
465
|
+
p_to_sub = poly.Unite(convert_py_list_to_net_list([poly] + voids_of_prims))
|
|
466
|
+
list_poly = poly.Subtract(p_to_sub, primi_polys)
|
|
467
|
+
new_polys = []
|
|
468
|
+
if list_poly:
|
|
469
|
+
for p in list_poly:
|
|
470
|
+
if p.IsNull():
|
|
471
|
+
continue
|
|
472
|
+
new_polys.append(
|
|
473
|
+
self._app.modeler.create_polygon(p, self.layer_name, net_name=self.net_name, voids=[]),
|
|
474
|
+
)
|
|
475
|
+
self.delete()
|
|
476
|
+
for prim in primitives:
|
|
477
|
+
if isinstance(prim, Primitive):
|
|
478
|
+
prim.delete()
|
|
479
|
+
else:
|
|
480
|
+
try:
|
|
481
|
+
prim.Delete()
|
|
482
|
+
except AttributeError:
|
|
483
|
+
continue
|
|
484
|
+
return new_polys
|
|
485
|
+
|
|
486
|
+
def intersect(self, primitives):
|
|
487
|
+
"""Intersect active primitive with one or more primitives.
|
|
488
|
+
|
|
489
|
+
Parameters
|
|
490
|
+
----------
|
|
491
|
+
primitives : :class:`dotnet.edb_core.edb_data.EDBPrimitives` or EDB PolygonData or EDB Primitive or list
|
|
492
|
+
|
|
493
|
+
Returns
|
|
494
|
+
-------
|
|
495
|
+
List of :class:`dotnet.edb_core.edb_data.EDBPrimitives`
|
|
496
|
+
"""
|
|
497
|
+
poly = self._edb_object.GetPolygonData()
|
|
498
|
+
if not isinstance(primitives, list):
|
|
499
|
+
primitives = [primitives]
|
|
500
|
+
primi_polys = []
|
|
501
|
+
for prim in primitives:
|
|
502
|
+
if isinstance(prim, Primitive):
|
|
503
|
+
primi_polys.append(prim.primitive_object.GetPolygonData())
|
|
504
|
+
else:
|
|
505
|
+
primi_polys.append(prim._edb_object.GetPolygonData())
|
|
506
|
+
# primi_polys.append(prim)
|
|
507
|
+
list_poly = poly.Intersect(convert_py_list_to_net_list([poly]), convert_py_list_to_net_list(primi_polys))
|
|
508
|
+
new_polys = []
|
|
509
|
+
if list_poly:
|
|
510
|
+
voids = self.voids
|
|
511
|
+
for p in list_poly:
|
|
512
|
+
if p.IsNull():
|
|
513
|
+
continue
|
|
514
|
+
list_void = []
|
|
515
|
+
void_to_subtract = []
|
|
516
|
+
if voids:
|
|
517
|
+
for void in voids:
|
|
518
|
+
void_pdata = void._edb_object.GetPolygonData()
|
|
519
|
+
int_data2 = p.GetIntersectionType(void_pdata)
|
|
520
|
+
if int_data2 > 2 or int_data2 == 1:
|
|
521
|
+
void_to_subtract.append(void_pdata)
|
|
522
|
+
elif int_data2 == 2:
|
|
523
|
+
list_void.append(void_pdata)
|
|
524
|
+
if void_to_subtract:
|
|
525
|
+
polys_cleans = p.Subtract(
|
|
526
|
+
convert_py_list_to_net_list(p), convert_py_list_to_net_list(void_to_subtract)
|
|
527
|
+
)
|
|
528
|
+
for polys_clean in polys_cleans:
|
|
529
|
+
if not polys_clean.IsNull():
|
|
530
|
+
void_to_append = [v for v in list_void if polys_clean.GetIntersectionType(v) == 2]
|
|
531
|
+
new_polys.append(
|
|
532
|
+
self._app.modeler.create_polygon(
|
|
533
|
+
polys_clean, self.layer_name, net_name=self.net_name, voids=void_to_append
|
|
534
|
+
)
|
|
535
|
+
)
|
|
536
|
+
else:
|
|
537
|
+
new_polys.append(
|
|
538
|
+
self._app.modeler.create_polygon(
|
|
539
|
+
p, self.layer_name, net_name=self.net_name, voids=list_void
|
|
540
|
+
)
|
|
541
|
+
)
|
|
542
|
+
else:
|
|
543
|
+
new_polys.append(
|
|
544
|
+
self._app.modeler.create_polygon(p, self.layer_name, net_name=self.net_name, voids=list_void)
|
|
545
|
+
)
|
|
546
|
+
self.delete()
|
|
547
|
+
for prim in primitives:
|
|
548
|
+
if isinstance(prim, Primitive):
|
|
549
|
+
prim.delete()
|
|
550
|
+
else:
|
|
551
|
+
try:
|
|
552
|
+
prim.Delete()
|
|
553
|
+
except AttributeError:
|
|
554
|
+
continue
|
|
555
|
+
return new_polys
|
|
556
|
+
|
|
557
|
+
def unite(self, primitives):
|
|
558
|
+
"""Unite active primitive with one or more primitives.
|
|
559
|
+
|
|
560
|
+
Parameters
|
|
561
|
+
----------
|
|
562
|
+
primitives : :class:`dotnet.edb_core.edb_data.EDBPrimitives` or EDB PolygonData or EDB Primitive or list
|
|
563
|
+
|
|
564
|
+
Returns
|
|
565
|
+
-------
|
|
566
|
+
List of :class:`dotnet.edb_core.edb_data.EDBPrimitives`
|
|
567
|
+
"""
|
|
568
|
+
poly = self._edb_object.GetPolygonData()
|
|
569
|
+
if not isinstance(primitives, list):
|
|
570
|
+
primitives = [primitives]
|
|
571
|
+
primi_polys = []
|
|
572
|
+
for prim in primitives:
|
|
573
|
+
if isinstance(prim, Primitive):
|
|
574
|
+
primi_polys.append(prim.primitive_object.GetPolygonData())
|
|
575
|
+
else:
|
|
576
|
+
try:
|
|
577
|
+
primi_polys.append(prim.GetPolygonData())
|
|
578
|
+
except:
|
|
579
|
+
primi_polys.append(prim)
|
|
580
|
+
list_poly = poly.Unite(convert_py_list_to_net_list([poly] + primi_polys))
|
|
581
|
+
new_polys = []
|
|
582
|
+
if list_poly:
|
|
583
|
+
voids = self.voids
|
|
584
|
+
for p in list_poly:
|
|
585
|
+
if p.IsNull():
|
|
586
|
+
continue
|
|
587
|
+
list_void = []
|
|
588
|
+
if voids:
|
|
589
|
+
for void in voids:
|
|
590
|
+
void_pdata = void.primitive_object.GetPolygonData()
|
|
591
|
+
int_data2 = p.GetIntersectionType(void_pdata)
|
|
592
|
+
if int_data2 > 1:
|
|
593
|
+
list_void.append(void_pdata)
|
|
594
|
+
new_polys.append(
|
|
595
|
+
self._app.modeler.create_polygon(p, self.layer_name, net_name=self.net_name, voids=list_void),
|
|
596
|
+
)
|
|
597
|
+
self.delete()
|
|
598
|
+
for prim in primitives:
|
|
599
|
+
if isinstance(prim, Primitive):
|
|
600
|
+
prim.delete()
|
|
601
|
+
else:
|
|
602
|
+
try:
|
|
603
|
+
prim.Delete()
|
|
604
|
+
except AttributeError:
|
|
605
|
+
continue
|
|
606
|
+
return new_polys
|
|
607
|
+
|
|
608
|
+
def get_closest_arc_midpoint(self, point):
|
|
609
|
+
"""Get the closest arc midpoint of the primitive to the input data.
|
|
610
|
+
|
|
611
|
+
Parameters
|
|
612
|
+
----------
|
|
613
|
+
point : list of float or PointData
|
|
614
|
+
|
|
615
|
+
Returns
|
|
616
|
+
-------
|
|
617
|
+
list of float
|
|
618
|
+
"""
|
|
619
|
+
if isinstance(point, self._app.edb_api.geometry.geometry.PointData):
|
|
620
|
+
point = [point.X.ToDouble(), point.Y.ToDouble()]
|
|
621
|
+
dist = 1e12
|
|
622
|
+
out = None
|
|
623
|
+
for arc in self.arcs:
|
|
624
|
+
mid_point = arc.mid_point
|
|
625
|
+
mid_point = [mid_point.X.ToDouble(), mid_point.Y.ToDouble()]
|
|
626
|
+
if GeometryOperators.points_distance(mid_point, point) < dist:
|
|
627
|
+
out = arc.mid_point
|
|
628
|
+
dist = GeometryOperators.points_distance(mid_point, point)
|
|
629
|
+
return [out.X.ToDouble(), out.Y.ToDouble()]
|
|
630
|
+
|
|
631
|
+
@property
|
|
632
|
+
def voids(self):
|
|
633
|
+
""":obj:`list` of :class:`Primitive <ansys.edb.primitive.Primitive>`: List of void\
|
|
634
|
+
primitive objects inside the primitive.
|
|
635
|
+
|
|
636
|
+
Read-Only.
|
|
637
|
+
"""
|
|
638
|
+
return [self._pedb.layout.find_object_by_id(void.GetId()) for void in self._edb_object.Voids]
|
|
639
|
+
|
|
640
|
+
@property
|
|
641
|
+
def shortest_arc(self):
|
|
642
|
+
"""Get the longest arc."""
|
|
643
|
+
len = 1e12
|
|
644
|
+
arc = None
|
|
645
|
+
for i in self.arcs:
|
|
646
|
+
if i.is_segment and i.length < len:
|
|
647
|
+
arc = i
|
|
648
|
+
len = i.length
|
|
649
|
+
return arc
|
|
650
|
+
|
|
651
|
+
@property
|
|
652
|
+
def aedt_name(self):
|
|
653
|
+
"""Name to be visualized in AEDT.
|
|
654
|
+
|
|
655
|
+
Returns
|
|
656
|
+
-------
|
|
657
|
+
str
|
|
658
|
+
Name.
|
|
659
|
+
"""
|
|
660
|
+
from System import String
|
|
661
|
+
|
|
662
|
+
val = String("")
|
|
663
|
+
|
|
664
|
+
_, name = self._edb_object.GetProductProperty(self._pedb._edb.ProductId.Designer, 1, val)
|
|
665
|
+
name = str(name).strip("'")
|
|
666
|
+
if name == "":
|
|
667
|
+
if str(self.primitive_type) == "Path":
|
|
668
|
+
ptype = "line"
|
|
669
|
+
elif str(self.primitive_type) == "Rectangle":
|
|
670
|
+
ptype = "rect"
|
|
671
|
+
elif str(self.primitive_type) == "Polygon":
|
|
672
|
+
ptype = "poly"
|
|
673
|
+
elif str(self.primitive_type) == "Bondwire":
|
|
674
|
+
ptype = "bwr"
|
|
675
|
+
else:
|
|
676
|
+
ptype = str(self.primitive_type).lower()
|
|
677
|
+
name = "{}_{}".format(ptype, self.id)
|
|
678
|
+
self._edb_object.SetProductProperty(self._pedb._edb.ProductId.Designer, 1, name)
|
|
679
|
+
return name
|
|
680
|
+
|
|
681
|
+
@aedt_name.setter
|
|
682
|
+
def aedt_name(self, value):
|
|
683
|
+
self._edb_object.SetProductProperty(self._pedb._edb.ProductId.Designer, 1, value)
|
|
684
|
+
|
|
685
|
+
@property
|
|
686
|
+
def polygon_data(self):
|
|
687
|
+
""":class:`pyedb.dotnet.edb_core.dotnet.database.PolygonDataDotNet`: Outer contour of the Polygon object."""
|
|
688
|
+
return PolygonData(self._pedb, self._edb_object.GetPolygonData())
|
|
689
|
+
|
|
690
|
+
@polygon_data.setter
|
|
691
|
+
def polygon_data(self, poly):
|
|
692
|
+
self._edb_object.SetPolygonData(poly._edb_object)
|
|
693
|
+
|
|
694
|
+
def add_void(self, point_list):
|
|
695
|
+
"""Add a void to current primitive.
|
|
696
|
+
|
|
697
|
+
Parameters
|
|
698
|
+
----------
|
|
699
|
+
point_list : list or :class:`pyedb.dotnet.edb_core.edb_data.primitives_data.EDBPrimitives` \
|
|
700
|
+
or EDB Primitive Object. Point list in the format of `[[x1,y1], [x2,y2],..,[xn,yn]]`.
|
|
701
|
+
|
|
702
|
+
Returns
|
|
703
|
+
-------
|
|
704
|
+
bool
|
|
705
|
+
``True`` if successful, either ``False``.
|
|
706
|
+
"""
|
|
707
|
+
if isinstance(point_list, list):
|
|
708
|
+
plane = self._pedb.modeler.Shape("polygon", points=point_list)
|
|
709
|
+
_poly = self._pedb.modeler.shape_to_polygon_data(plane)
|
|
710
|
+
if _poly is None or _poly.IsNull() or _poly is False:
|
|
711
|
+
self._logger.error("Failed to create void polygon data")
|
|
712
|
+
return False
|
|
713
|
+
point_list = self._pedb.modeler.create_polygon(
|
|
714
|
+
_poly, layer_name=self.layer_name, net_name=self.net.name
|
|
715
|
+
)._edb_object
|
|
716
|
+
elif "_edb_object" in dir(point_list):
|
|
717
|
+
point_list = point_list._edb_object
|
|
718
|
+
elif "primitive_obj" in dir(point_list):
|
|
719
|
+
point_list = point_list.primitive_obj
|
|
720
|
+
return self._edb_object.AddVoid(point_list)
|
|
721
|
+
|
|
722
|
+
@property
|
|
723
|
+
def api_class(self):
|
|
724
|
+
return self._pedb._edb.Cell.Primitive
|
|
725
|
+
|
|
726
|
+
def set_hfss_prop(self, material, solve_inside):
|
|
727
|
+
"""Set HFSS properties.
|
|
728
|
+
|
|
729
|
+
Parameters
|
|
730
|
+
----------
|
|
731
|
+
material : str
|
|
732
|
+
Material property name to be set.
|
|
733
|
+
solve_inside : bool
|
|
734
|
+
Whether to do solve inside.
|
|
735
|
+
"""
|
|
736
|
+
self._edb_object.SetHfssProp(material, solve_inside)
|
|
737
|
+
|
|
738
|
+
@property
|
|
739
|
+
def has_voids(self):
|
|
740
|
+
""":obj:`bool`: If a primitive has voids inside.
|
|
741
|
+
|
|
742
|
+
Read-Only.
|
|
743
|
+
"""
|
|
744
|
+
return self._edb_object.HasVoids()
|
|
745
|
+
|
|
746
|
+
@property
|
|
747
|
+
def owner(self):
|
|
748
|
+
""":class:`Primitive <ansys.edb.primitive.Primitive>`: Owner of the primitive object.
|
|
749
|
+
|
|
750
|
+
Read-Only.
|
|
751
|
+
"""
|
|
752
|
+
pid = self._edb_object.GetOwner().GetId()
|
|
753
|
+
return self._pedb.layout.self.find_object_by_id(pid)
|
|
754
|
+
|
|
755
|
+
@property
|
|
756
|
+
def is_parameterized(self):
|
|
757
|
+
""":obj:`bool`: Primitive's parametrization.
|
|
758
|
+
|
|
759
|
+
Read-Only.
|
|
760
|
+
"""
|
|
761
|
+
return self._edb_object.IsParameterized()
|
|
762
|
+
|
|
763
|
+
def get_hfss_prop(self):
|
|
764
|
+
"""
|
|
765
|
+
Get HFSS properties.
|
|
766
|
+
|
|
767
|
+
Returns
|
|
768
|
+
-------
|
|
769
|
+
material : str
|
|
770
|
+
Material property name.
|
|
771
|
+
solve_inside : bool
|
|
772
|
+
If solve inside.
|
|
773
|
+
"""
|
|
774
|
+
material = ""
|
|
775
|
+
solve_inside = True
|
|
776
|
+
self._edb_object.GetHfssProp(material, solve_inside)
|
|
777
|
+
return material, solve_inside
|
|
778
|
+
|
|
779
|
+
def remove_hfss_prop(self):
|
|
780
|
+
"""Remove HFSS properties."""
|
|
781
|
+
self._edb_object.RemoveHfssProp()
|
|
782
|
+
|
|
783
|
+
@property
|
|
784
|
+
def is_zone_primitive(self):
|
|
785
|
+
""":obj:`bool`: If primitive object is a zone.
|
|
786
|
+
|
|
787
|
+
Read-Only.
|
|
788
|
+
"""
|
|
789
|
+
return self._edb_object.IsZonePrimitive()
|
|
790
|
+
|
|
791
|
+
@property
|
|
792
|
+
def can_be_zone_primitive(self):
|
|
793
|
+
""":obj:`bool`: If a primitive can be a zone.
|
|
794
|
+
|
|
795
|
+
Read-Only.
|
|
796
|
+
"""
|
|
797
|
+
return True
|
|
798
|
+
|
|
799
|
+
def make_zone_primitive(self, zone_id):
|
|
800
|
+
"""Make primitive a zone primitive with a zone specified by the provided id.
|
|
801
|
+
|
|
802
|
+
Parameters
|
|
803
|
+
----------
|
|
804
|
+
zone_id : int
|
|
805
|
+
Id of zone primitive will use.
|
|
806
|
+
|
|
807
|
+
"""
|
|
808
|
+
self._edb_object.MakeZonePrimitive(zone_id)
|
|
809
|
+
|
|
810
|
+
def points(self, arc_segments=6):
|
|
811
|
+
"""Return the list of points with arcs converted to segments.
|
|
812
|
+
|
|
813
|
+
Parameters
|
|
814
|
+
----------
|
|
815
|
+
arc_segments : int
|
|
816
|
+
Number of facets to convert an arc. Default is `6`.
|
|
817
|
+
|
|
818
|
+
Returns
|
|
819
|
+
-------
|
|
820
|
+
tuple
|
|
821
|
+
The tuple contains 2 lists made of X and Y points coordinates.
|
|
822
|
+
"""
|
|
823
|
+
my_net_points = list(self._edb_object.GetPolygonData().Points)
|
|
824
|
+
xt, yt = self._get_points_for_plot(my_net_points, arc_segments)
|
|
825
|
+
if not xt:
|
|
826
|
+
return []
|
|
827
|
+
x, y = GeometryOperators.orient_polygon(xt, yt, clockwise=True)
|
|
828
|
+
return x, y
|
|
829
|
+
|
|
830
|
+
def points_raw(self):
|
|
831
|
+
"""Return a list of Edb points.
|
|
832
|
+
|
|
833
|
+
Returns
|
|
834
|
+
-------
|
|
835
|
+
list
|
|
836
|
+
Edb Points.
|
|
837
|
+
"""
|
|
838
|
+
points = []
|
|
839
|
+
my_net_points = list(self._edb_object.GetPolygonData().Points)
|
|
840
|
+
for point in my_net_points:
|
|
841
|
+
points.append(point)
|
|
842
|
+
return points
|
|
843
|
+
|
|
844
|
+
def expand(self, offset=0.001, tolerance=1e-12, round_corners=True, maximum_corner_extension=0.001):
|
|
845
|
+
"""Expand the polygon shape by an absolute value in all direction.
|
|
846
|
+
Offset can be negative for negative expansion.
|
|
847
|
+
|
|
848
|
+
Parameters
|
|
849
|
+
----------
|
|
850
|
+
offset : float, optional
|
|
851
|
+
Offset value in meters.
|
|
852
|
+
tolerance : float, optional
|
|
853
|
+
Tolerance in meters.
|
|
854
|
+
round_corners : bool, optional
|
|
855
|
+
Whether to round corners or not.
|
|
856
|
+
If True, use rounded corners in the expansion otherwise use straight edges (can be degenerate).
|
|
857
|
+
maximum_corner_extension : float, optional
|
|
858
|
+
The maximum corner extension (when round corners are not used) at which point the corner is clipped.
|
|
859
|
+
"""
|
|
860
|
+
return self.polygon_data.expand(offset, tolerance, round_corners, maximum_corner_extension)
|
|
861
|
+
|
|
862
|
+
def scale(self, factor, center=None):
|
|
863
|
+
"""Scales the polygon relative to a center point by a factor.
|
|
864
|
+
|
|
865
|
+
Parameters
|
|
866
|
+
----------
|
|
867
|
+
factor : float
|
|
868
|
+
Scaling factor.
|
|
869
|
+
center : List of float or str [x,y], optional
|
|
870
|
+
If None scaling is done from polygon center.
|
|
871
|
+
|
|
872
|
+
Returns
|
|
873
|
+
-------
|
|
874
|
+
bool
|
|
875
|
+
``True`` when successful, ``False`` when failed.
|
|
876
|
+
"""
|
|
877
|
+
if not isinstance(factor, str):
|
|
878
|
+
factor = float(factor)
|
|
879
|
+
polygon_data = self.polygon_data.create_from_arcs(self.polygon_data._edb_object.GetArcData(), True)
|
|
880
|
+
if not center:
|
|
881
|
+
center = self.polygon_data._edb_object.GetBoundingCircleCenter()
|
|
882
|
+
if center:
|
|
883
|
+
polygon_data._edb_object.Scale(factor, center)
|
|
884
|
+
self.polygon_data = polygon_data
|
|
885
|
+
return True
|
|
886
|
+
else:
|
|
887
|
+
self._pedb.logger.error(f"Failed to evaluate center on primitive {self.id}")
|
|
888
|
+
elif isinstance(center, list) and len(center) == 2:
|
|
889
|
+
center = self._edb.Geometry.PointData(
|
|
890
|
+
self._edb.Utility.Value(center[0]), self._edb.Utility.Value(center[1])
|
|
891
|
+
)
|
|
892
|
+
polygon_data._edb_object.Scale(factor, center)
|
|
893
|
+
self.polygon_data = polygon_data
|
|
894
|
+
return True
|
|
895
|
+
return False
|