From 260bf5c767ca93e5aae5daa1f908602b15b78271 Mon Sep 17 00:00:00 2001 From: jan Date: Thu, 7 Apr 2022 16:14:31 -0700 Subject: [PATCH] use own code for checking if polygons intersect (as opposed to finding the intersection) --- snarled/main.py | 23 ++++++++++--- snarled/poly.py | 90 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 5 deletions(-) diff --git a/snarled/main.py b/snarled/main.py index 2974191..a4b0d31 100644 --- a/snarled/main.py +++ b/snarled/main.py @@ -11,7 +11,7 @@ from numpy.typing import NDArray, ArrayLike from pyclipper import scale_to_clipper, scale_from_clipper, PyPolyNode from .types import connectivity_t, layer_t, contour_t -from .poly import poly_contains_points +from .poly import poly_contains_points, intersects from .clipper import union_nonzero, union_evenodd, intersection_evenodd, difference_evenodd, hier2oriented from .tracker import NetsInfo, NetName from .utils import connectivity2layers @@ -112,6 +112,7 @@ def trace_connectivity( nets_info = NetsInfo() for ii, (top_layer, via_layer, bot_layer) in enumerate(connectivity): + logger.info(f'{ii}, {top_layer}, {via_layer}, {bot_layer}') for metal_layer in (top_layer, bot_layer): if metal_layer in loaded_layers: continue @@ -142,7 +143,7 @@ def trace_connectivity( )) # Figure out which nets are shorted by vias, then merge them - merge_pairs = find_merge_pairs(nets_info.nets, top_layer, bot_layer, via_polys) + merge_pairs = find_merge_pairs(nets_info.nets, top_layer, bot_layer, via_polys, clipper_scale_factor) for net_a, net_b in merge_pairs: nets_info.merge(net_a, net_b) @@ -286,6 +287,7 @@ def find_merge_pairs( top_layer: layer_t, bot_layer: layer_t, via_polys: Optional[Sequence[contour_t]], + clipper_scale_factor: int, ) -> Set[Tuple[NetName, NetName]]: """ Given a collection of (possibly anonymous) nets, figure out which pairs of @@ -325,12 +327,23 @@ def find_merge_pairs( if via_polys is not None: top_bot = intersection_evenodd(top_polys, bot_polys) - overlap = intersection_evenodd(top_bot, via_polys) - via_polys = difference_evenodd(via_polys, overlap) # reduce set of via polys for future nets + overlap = check_any_intersection(scale_from_clipper(top_bot, clipper_scale_factor), + scale_from_clipper(via_polys, clipper_scale_factor)) +# overlap = intersection_evenodd(top_bot, via_polys) +# via_polys = difference_evenodd(via_polys, overlap) # reduce set of via polys for future nets else: - overlap = intersection_evenodd(top_polys, bot_polys) # TODO verify there aren't any suspicious corner cases for this +# overlap = intersection_evenodd(top_polys, bot_polys) # TODO verify there aren't any suspicious corner cases for this + overlap = check_any_intersection(scale_from_clipper(top_polys, clipper_scale_factor), scale_from_clipper(bot_polys, clipper_scale_factor)) if overlap: merge_pairs.add(name_pair) return merge_pairs + + +def check_any_intersection(polys_a, polys_b) -> bool: + for poly_a in polys_a: + for poly_b in polys_b: + if intersects(poly_a, poly_b): + return True + return False diff --git a/snarled/poly.py b/snarled/poly.py index 7d86924..10b0023 100644 --- a/snarled/poly.py +++ b/snarled/poly.py @@ -65,3 +65,93 @@ def poly_contains_points( inside = nontrivial.copy() inside[nontrivial] = nontrivial_inside return inside + + +def intersects(poly_a: ArrayLike, poly_b: ArrayLike) -> bool: + """ + Check if two polygons overlap and/or touch. + + Args: + poly_a: List of vertices, implicitly closed: `[[x0, y0], [x1, y1], ...]` + poly_b: List of vertices, implicitly closed: `[[x0, y0], [x1, y1], ...]` + + Returns: + `True` if the polygons overlap and/or touch. + """ + poly_a = numpy.array(poly_a, copy=False) + poly_b = numpy.array(poly_b, copy=False) + + # Check bounding boxes + min_a = poly_a.min(axis=0) + min_b = poly_b.min(axis=0) + max_a = poly_a.max(axis=0) + max_b = poly_b.max(axis=0) + + if ((min_a > max_b) | (min_b > max_a)).all(): + return False + + #TODO: Check against sorted coords? + + #Check if edges intersect + if poly_edges_intersect(poly_a, poly_b): + return True + + # Check if either polygon contains the other + if poly_contains_points(poly_b, poly_a).any(): + return True + + if poly_contains_points(poly_a, poly_b).any(): + return True + + return False + + +def poly_edges_intersect( + poly_a: NDArray[numpy.float64], + poly_b: NDArray[numpy.float64], + ) -> NDArray[numpy.int_]: + """ + Check if the edges of two polygons intersect. + + Args: + poly_a: NDArray of vertices, implicitly closed: `[[x0, y0], [x1, y1], ...]` + poly_b: NDArray of vertices, implicitly closed: `[[x0, y0], [x1, y1], ...]` + + Returns: + `True` if the polygons' edges intersect. + """ + a_next = numpy.roll(poly_a, -1, axis=0) + b_next = numpy.roll(poly_b, -1, axis=0) + + # Lists of initial/final coordinates for polygon segments + xi1 = poly_a[:, 0, None] + yi1 = poly_a[:, 1, None] + xf1 = a_next[:, 0, None] + yf1 = a_next[:, 1, None] + + xi2 = poly_b[:, 0, None] + yi2 = poly_b[:, 1, None] + xf2 = b_next[:, 0, None] + yf2 = b_next[:, 1, None] + + # Perform calculation + dxi = xi1 - xi2 + dyi = yi1 - yi2 + dx1 = xf1 - xi1 + dx2 = xf2 - xi2 + dy1 = yf1 - yi1 + dy2 = yf2 - yi2 + + numerator_a = dx2 * dyi - dy2 * dxi + numerator_b = dx1 * dyi - dy1 * dxi + denominator = dy2 * dx1 - dx2 * dy1 + + # Avoid warnings since we may multiply eg. NaN*False + with numpy.errstate(invalid='ignore', divide='ignore'): + u_a = numerator_a / denominator + u_b = numerator_b / denominator + + # Find the adjacency matrix + adjacency = numpy.logical_and.reduce((u_a >= 0, u_a <= 1, u_b >= 0, u_b <= 1)) + + return adjacency.any()