@@ -824,13 +824,12 @@ def create(
824824 elif polygon_hole :
825825 if isinstance (polygon_hole , list ):
826826 polygon_hole = GrpcPolygonData (points = polygon_hole )
827-
828827 padstack_data .set_hole_parameters (
829828 offset_x = value0 ,
830829 offset_y = value0 ,
831830 rotation = value0 ,
832831 type_geom = GrpcPadGeometryType .PADGEOMTYPE_POLYGON ,
833- sizes = polygon_hole ,
832+ fp = polygon_hole ,
834833 )
835834 padstack_data .plating_percentage = GrpcValue (20.0 )
836835 else :
@@ -1332,6 +1331,36 @@ def get_padstack_instances_rtree_index(self, nets=None):
13321331 padstack_instances_index .insert (inst .id , inst .position )
13331332 return padstack_instances_index
13341333
1334+ def get_padstack_instances_id_intersecting_polygon (self , points , nets = None , padstack_instances_index = None ):
1335+ """Returns the list of padstack instances ID intersecting a given bounding box and nets.
1336+
1337+ Parameters
1338+ ----------
1339+ bounding_box : tuple or list.
1340+ bounding box, [x1, y1, x2, y2]
1341+ nets : str or list, optional
1342+ net name of list of nets name applying filtering on padstack instances selection. If ``None`` is provided
1343+ all instances are included in the index. Default value is ``None``.
1344+ padstack_instances_index : optional, Rtree object.
1345+ Can be provided optionally to prevent computing padstack instances Rtree index again.
1346+
1347+ Returns
1348+ -------
1349+ List of padstack instances ID intersecting the bounding box.
1350+ """
1351+ if not points :
1352+ raise Exception ("No points defining polygon was provided" )
1353+ if not padstack_instances_index :
1354+ padstack_instances_index = {}
1355+ for inst in self .instances :
1356+ padstack_instances_index [inst .edb_uid ] = inst .position
1357+ _x = [pt [0 ] for pt in points ]
1358+ _y = [pt [1 ] for pt in points ]
1359+ points = [_x , _y ]
1360+ return [
1361+ ind for ind , pt in padstack_instances_index .items () if GeometryOperators .is_point_in_polygon (pt , points )
1362+ ]
1363+
13351364 def get_padstack_instances_intersecting_bounding_box (self , bounding_box , nets = None ):
13361365 """Returns the list of padstack instances ID intersecting a given bounding box and nets.
13371366
@@ -1357,18 +1386,23 @@ def get_padstack_instances_intersecting_bounding_box(self, bounding_box, nets=No
13571386 return list (index .intersection (bounding_box ))
13581387
13591388 def merge_via_along_lines (
1360- self , net_name = "GND" , distance_threshold = 5e-3 , minimum_via_number = 6 , selected_angles = None
1389+ self ,
1390+ net_name = "GND" ,
1391+ distance_threshold = 5e-3 ,
1392+ minimum_via_number = 6 ,
1393+ selected_angles = None ,
1394+ padstack_instances_id = None ,
13611395 ):
13621396 """Replace padstack instances along lines into a single polygon.
13631397
1364- Detect all padstack instances that are placed along lines and replace them by a single polygon based one
1398+ Detect all pad-stack instances that are placed along lines and replace them by a single polygon based one
13651399 forming a wall shape. This method is designed to simplify meshing on via fence usually added to shield RF traces
13661400 on PCB.
13671401
13681402 Parameters
13691403 ----------
13701404 net_name : str
1371- Net name used for detected padstack instances. Default value is ``"GND"``.
1405+ Net name used for detected pad-stack instances. Default value is ``"GND"``.
13721406
13731407 distance_threshold : float, None, optional
13741408 If two points in a line are separated by a distance larger than `distance_threshold`,
@@ -1382,22 +1416,29 @@ def merge_via_along_lines(
13821416 Other values can be assigned like 45 degrees. When `None` is provided all lines are detected. Default value
13831417 is `None`.
13841418
1419+ padstack_instances_id : List[int]
1420+ List of pad-stack instances ID's to include. If `None`, the algorithm will scan all pad-stack
1421+ instances belonging to the specified net. Default value is `None`.
1422+
13851423 Returns
13861424 -------
1387- bool
1388- ``True`` when succeeded ``False`` when failed. <
1425+ List[int], list of created pad-stack instances id.
13891426
13901427 """
13911428 _def = list (set ([inst .padstack_def for inst in list (self .instances .values ()) if inst .net_name == net_name ]))
13921429 if not _def :
13931430 self ._logger .error (f"No padstack definition found for net { net_name } " )
13941431 return False
1432+ instances_created = []
13951433 _instances_to_delete = []
13961434 padstack_instances = []
1397- for pdstk_def in _def :
1398- padstack_instances .append (
1399- [inst for inst in self .definitions [pdstk_def .name ].instances if inst .net_name == net_name ]
1400- )
1435+ if padstack_instances_id :
1436+ padstack_instances = [[self .instances [id ] for id in padstack_instances_id ]]
1437+ else :
1438+ for pdstk_def in _def :
1439+ padstack_instances .append (
1440+ [inst for inst in self .definitions [pdstk_def .name ].instances if inst .net_name == net_name ]
1441+ )
14011442 for pdstk_series in padstack_instances :
14021443 instances_location = [inst .position for inst in pdstk_series ]
14031444 lines , line_indexes = GeometryOperators .find_points_along_lines (
@@ -1431,70 +1472,112 @@ def merge_via_along_lines(
14311472 polygon_hole = polygon_data ,
14321473 ):
14331474 self ._logger .error (f"Failed to create padstack definition { new_padstack_def .name } " )
1434- if not self .place (position = [0 , 0 ], definition_name = new_padstack_def , net_name = net_name ):
1435- self ._logger .error (f"Failed to place padstack instance { new_padstack_def .name } " )
1475+ new_instance = self .place (position = [0 , 0 ], definition_name = new_padstack_def , net_name = net_name )
1476+ if not new_instance :
1477+ self ._logger .error (f"Failed to place padstack instance { new_padstack_def } " )
1478+ else :
1479+ instances_created .append (new_instance .id )
14361480 for inst in _instances_to_delete :
14371481 inst .delete ()
1438- return True
1482+ return instances_created
14391483
14401484 def merge_via (self , contour_boxes , net_filter = None , start_layer = None , stop_layer = None ):
1441- """Evaluate padstack instances included on the provided point list and replace all by single instance.
1485+ """Evaluate pad-stack instances included on the provided point list and replace all by single instance.
14421486
14431487 Parameters
14441488 ----------
14451489 contour_boxes : List[List[List[float, float]]]
14461490 Nested list of polygon with points [x,y].
14471491 net_filter : optional
1448- List[str: net_name] apply a net filter,
1449- nets included in the filter are excluded from the via merge.
1492+ List[str: net_name] apply a net filter, nets included in the filter are excluded from the via merge.
14501493 start_layer : optional, str
1451- Padstack instance start layer, if `None` the top layer is selected.
1494+ Pad-stack instance start layer, if `None` the top layer is selected.
14521495 stop_layer : optional, str
1453- Padstack instance stop layer, if `None` the bottom layer is selected.
1496+ Pad-stack instance stop layer, if `None` the bottom layer is selected.
14541497
14551498 Return
14561499 ------
1457- List[str], list of created padstack instances ID.
1500+ List[str], list of created pad-stack instances ID.
14581501
14591502 """
1503+
1504+ import numpy as np
1505+ from scipy .spatial import ConvexHull
1506+
14601507 merged_via_ids = []
14611508 if not contour_boxes :
1462- self ._pedb .logger .error ("No contour box provided, you need to pass a nested list as argument." )
1463- return False
1464- if not start_layer :
1465- start_layer = list (self ._pedb .stackup .layers .values ())[0 ].name
1466- if not stop_layer :
1467- stop_layer = list (self ._pedb .stackup .layers .values ())[- 1 ].name
1509+ raise Exception ("No contour box provided, you need to pass a nested list as argument." )
14681510 instances_index = {}
14691511 for id , inst in self .instances .items ():
14701512 instances_index [id ] = inst .position
14711513 for contour_box in contour_boxes :
1514+ all_instances = self .instances
14721515 instances = self .get_padstack_instances_id_intersecting_polygon (
14731516 points = contour_box , padstack_instances_index = instances_index
14741517 )
1475- if net_filter :
1476- instances = [self .instances [id ] for id in instances if not self .instances [id ].net .name in net_filter ]
1477- net = self .instances [instances [0 ]].net .name
1478- instances_pts = np .array ([self .instances [id ].position for id in instances ])
1479- convex_hull_contour = ConvexHull (instances_pts )
1480- contour_points = list (instances_pts [convex_hull_contour .vertices ])
1481- layer = list (self ._pedb .stackup .layers .values ())[0 ].name
1482- polygon = self ._pedb .modeler .create_polygon (main_shape = contour_points , layer_name = layer )
1483- polygon_data = polygon .polygon_data
1484- polygon .delete ()
1485- new_padstack_def = generate_unique_name ("test" )
1486- if not self .create (
1487- padstackname = new_padstack_def ,
1488- pad_shape = "Polygon" ,
1489- antipad_shape = "Polygon" ,
1490- pad_polygon = polygon_data ,
1491- antipad_polygon = polygon_data ,
1492- polygon_hole = polygon_data ,
1493- start_layer = start_layer ,
1494- stop_layer = stop_layer ,
1495- ):
1496- self ._logger .error (f"Failed to create padstack definition { new_padstack_def } " )
1497- merged_instance = self .place (position = [0 , 0 ], definition_name = new_padstack_def , net_name = net )
1498- merged_via_ids .append (merged_instance .id )
1499- [self .instances [id ].delete () for id in instances ]
1518+ if not instances :
1519+ raise Exception (f"No padstack instances found inside { contour_box } " )
1520+ else :
1521+ if net_filter :
1522+ instances = [id for id in instances if not self .instances [id ].net_name in net_filter ]
1523+ if start_layer :
1524+ if start_layer not in self ._pedb .stackup .layers .keys ():
1525+ raise Exception (f"{ start_layer } not exist" )
1526+ else :
1527+ instances = [id for id in instances if all_instances [id ].start_layer == start_layer ]
1528+ if stop_layer :
1529+ if stop_layer not in self ._pedb .stackup .layers .keys ():
1530+ raise Exception (f"{ stop_layer } not exist" )
1531+ else :
1532+ instances = [id for id in instances if all_instances [id ].stop_layer == stop_layer ]
1533+ if not instances :
1534+ raise Exception (
1535+ f"No padstack instances found inside { contour_box } between { start_layer } and { stop_layer } "
1536+ )
1537+
1538+ if not start_layer :
1539+ start_layer = list (self ._pedb .stackup .layers .values ())[0 ].name
1540+ if not stop_layer :
1541+ stop_layer = list (self ._pedb .stackup .layers .values ())[- 1 ].name
1542+
1543+ net = self .instances [instances [0 ]].net_name
1544+ x_values = []
1545+ y_values = []
1546+ for inst in instances :
1547+ pos = instances_index [inst ]
1548+ x_values .append (pos [0 ])
1549+ y_values .append (pos [1 ])
1550+ x_values = list (set (x_values ))
1551+ y_values = list (set (y_values ))
1552+ if len (x_values ) == 1 or len (y_values ) == 1 :
1553+ create_instances = self .merge_via_along_lines (
1554+ net_name = net , padstack_instances_id = instances , minimum_via_number = 2
1555+ )
1556+ merged_via_ids .extend (create_instances )
1557+ else :
1558+ instances_pts = np .array ([instances_index [id ] for id in instances ])
1559+ convex_hull_contour = ConvexHull (instances_pts )
1560+ contour_points = list (instances_pts [convex_hull_contour .vertices ])
1561+ layer = list (self ._pedb .stackup .layers .values ())[0 ].name
1562+ polygon = self ._pedb .modeler .create_polygon (points = contour_points , layer_name = layer )
1563+ polygon_data = polygon .polygon_data
1564+ polygon .delete ()
1565+ new_padstack_def = generate_unique_name (self .instances [instances [0 ]].definition .name )
1566+ if not self .create (
1567+ padstackname = new_padstack_def ,
1568+ pad_shape = "Polygon" ,
1569+ antipad_shape = "Polygon" ,
1570+ pad_polygon = polygon_data ,
1571+ antipad_polygon = polygon_data ,
1572+ polygon_hole = polygon_data ,
1573+ start_layer = start_layer ,
1574+ stop_layer = stop_layer ,
1575+ ):
1576+ raise Exception (f"Failed to create padstack definition { new_padstack_def } " )
1577+ merged_instance = self .place (position = [0 , 0 ], definition_name = new_padstack_def , net_name = net )
1578+ merged_instance .start_layer = start_layer
1579+ merged_instance .stop_layer = stop_layer
1580+
1581+ merged_via_ids .append (merged_instance .edb_uid )
1582+ _ = [all_instances [id ].delete () for id in instances ]
15001583 return merged_via_ids
0 commit comments