diff --git a/masque/shapes/polygon.py b/masque/shapes/polygon.py index 12760a7..60eba42 100644 --- a/masque/shapes/polygon.py +++ b/masque/shapes/polygon.py @@ -185,63 +185,37 @@ class Polygon(Shape): :param cut_ys: list of y-coordinates to cut along (e.g., [1, 3, 5.4]) :return: List of Polygon objects """ - import float_raster - xy_complex = self.vertices[:, 0] + 1j * self.vertices[:, 1] - xy_cleaned = _clean_complex_vertices(xy_complex) - xy = numpy.vstack((numpy.real(xy_cleaned)[None, :], - numpy.imag(xy_cleaned)[None, :])) + import pyclipper + from pyclipper import scale_to_clipper, scale_from_clipper - if cut_xs is None: - cut_xs = tuple() - if cut_ys is None: - cut_ys = tuple() + min_x, min_y = numpy.min(self.vertices, axis=0) + max_x, max_y = numpy.max(self.vertices, axis=0) + range_x = max_x - min_x + range_y = max_y - min_y - mins, maxs = self.get_bounds() - dx, dy = maxs - mins + edge_xs = (min_x - range_x - 1,) + tuple(cut_xs) + (max_x + range_x + 1,) + edge_ys = (min_y - range_y - 1,) + tuple(cut_ys) + (max_y + range_y + 1,) - cx = numpy.hstack((min(tuple(cut_xs) + (mins[0],)) - dx, cut_xs, max((maxs[0],) + tuple(cut_xs)) + dx)) - cy = numpy.hstack((min(tuple(cut_ys) + (mins[1],)) - dy, cut_ys, max((maxs[1],) + tuple(cut_ys)) + dy)) + clipped_shapes = [] + for i in range(2): + for j in range(2): + clipper = pyclipper.Pyclipper() + clipper.AddPath(scale_to_clipper(self.vertices), pyclipper.PT_SUBJECT, True) - all_verts = float_raster.create_vertices(xy, cx, cy) - - polygons = [] - for cx_min, cx_max in zip(cx, cx[1:]): - for cy_min, cy_max in zip(cy, cy[1:]): - clipped_verts = (numpy.real(all_verts).clip(cx_min, cx_max) + 1j * - numpy.imag(all_verts).clip(cy_min, cy_max)) - - cleaned_verts = _clean_complex_vertices(clipped_verts) - if len(cleaned_verts) == 0: - continue - - final_verts = numpy.hstack((numpy.real(cleaned_verts)[:, None], - numpy.imag(cleaned_verts)[:, None])) - polygons.append(Polygon( - vertices=final_verts, - layer=self.layer, - dose=self.dose)) - return polygons - - -def _clean_complex_vertices(vertices: numpy.ndarray) -> numpy.ndarray: - eps = numpy.finfo(vertices.dtype).eps - - def cleanup(v): - # Remove duplicate points - dv = v - numpy.roll(v, 1) - v = v[numpy.abs(dv) > eps] - - # Remove colinear points - dv = v - numpy.roll(v, 1) - m = numpy.angle(dv) % pi - diff_m = m - numpy.roll(m, -1) - return v[numpy.abs(diff_m) > eps] - - n = len(vertices) - cleaned = cleanup(vertices) - while n != len(cleaned): - n = len(cleaned) - cleaned = cleanup(cleaned) - - return cleaned + for start_x, stop_x in zip(edge_xs[i::2], edge_xs[(i+1)::2]): + for start_y, stop_y in zip(edge_ys[j::2], edge_ys[(j+1)::2]): + clipper.AddPath(scale_to_clipper(( + (start_x, start_y), + (start_x, stop_y), + (stop_x, stop_y), + (stop_x, start_y), + )), pyclipper.PT_CLIP, True) + clipped_parts = scale_from_clipper(clipper.Execute(pyclipper.CT_INTERSECTION, + pyclipper.PFT_EVENODD, + pyclipper.PFT_EVENODD)) + for part in clipped_parts: + poly = self.copy() + poly.vertices = part + clipped_shapes.append(poly) + return clipped_shapes diff --git a/masque/subpattern.py b/masque/subpattern.py index c45a4d9..6242468 100644 --- a/masque/subpattern.py +++ b/masque/subpattern.py @@ -156,4 +156,3 @@ class SubPattern: """ self.scale *= c return self - diff --git a/setup.py b/setup.py index c24baf3..3d68302 100644 --- a/setup.py +++ b/setup.py @@ -16,7 +16,8 @@ setup(name='masque', 'visualization': ['matplotlib'], 'gdsii': ['python-gdsii'], 'svg': ['svgwrite'], - 'text': ['freetype-py', 'matplotlib'] + 'text': ['freetype-py', 'matplotlib'], + 'clipping': ['pyclipper'], }, )