allow cutting any shape, always require pyclipper

This commit is contained in:
Jan Petykiewicz 2017-10-15 13:48:55 -07:00
parent 87c2ef5948
commit 9308454ad4
5 changed files with 67 additions and 46 deletions

View File

@ -9,8 +9,9 @@ E-beam doses, and the ability to output to multiple formats.
## Installation ## Installation
Requirements: Requirements:
* python 3 (written and tested with 3.5) * python >= 3.5 (written and tested with 3.6)
* numpy * numpy
* pyclipper
* matplotlib (optional, used for visualization functions and text) * matplotlib (optional, used for visualization functions and text)
* python-gdsii (optional, used for gdsii i/o) * python-gdsii (optional, used for gdsii i/o)
* svgwrite (optional, used for svg output) * svgwrite (optional, used for svg output)
@ -27,5 +28,5 @@ pip install git+https://mpxd.net/gogs/jan/masque.git@release
* Polygon de-embedding * Polygon de-embedding
### Maybe ### Maybe
* Construct from bitmap * Construct from bitmap
* Boolean operations on polygons (eg. using pyclipper) * Boolean operations on polygons (using pyclipper)
* Output to OASIS * Output to OASIS (using fatamorgana)

View File

@ -18,6 +18,7 @@
Dependencies: Dependencies:
- numpy - numpy
- pyclipper
- matplotlib [Pattern.visualize(...)] - matplotlib [Pattern.visualize(...)]
- python-gdsii [masque.file.gdsii] - python-gdsii [masque.file.gdsii]
- svgwrite [masque.file.svgwrite] - svgwrite [masque.file.svgwrite]

View File

@ -2,6 +2,8 @@ from typing import List
import copy import copy
import numpy import numpy
from numpy import pi from numpy import pi
import pyclipper
from pyclipper import scale_to_clipper, scale_from_clipper
from . import Shape, normalized_shape_tuple from . import Shape, normalized_shape_tuple
from .. import PatternError from .. import PatternError
@ -173,49 +175,17 @@ class Polygon(Shape):
(offset, scale/norm_value, rotation, self.dose), \ (offset, scale/norm_value, rotation, self.dose), \
lambda: Polygon(reordered_vertices*norm_value, layer=self.layer) lambda: Polygon(reordered_vertices*norm_value, layer=self.layer)
def cut(self, def clean_vertices(self) -> 'Polygon':
cut_xs: numpy.ndarray = None,
cut_ys: numpy.ndarray = None
) -> List['Polygon']:
""" """
Decomposes the polygon into a list of constituents by cutting along the Removes duplicate, co-linear and otherwise redundant vertices.
specified x and/or y coordinates.
:param cut_xs: list of x-coordinates to cut along (e.g., [1, 1.4, 6]) :returns: self
:param cut_ys: list of y-coordinates to cut along (e.g., [1, 3, 5.4])
:return: List of Polygon objects
""" """
import pyclipper self.vertices = scale_from_clipper(
from pyclipper import scale_to_clipper, scale_from_clipper pyclipper.CleanPolygon(
scale_to_clipper(
self.vertices
)))
return self
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
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,)
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)
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

View File

@ -3,6 +3,9 @@ from abc import ABCMeta, abstractmethod
import copy import copy
import numpy import numpy
import pyclipper
from pyclipper import scale_to_clipper, scale_from_clipper
from .. import PatternError from .. import PatternError
from ..utils import is_scalar, rotation_matrix_2d, vector2 from ..utils import is_scalar, rotation_matrix_2d, vector2
@ -271,3 +274,49 @@ class Shape(metaclass=ABCMeta):
return manhattan_polygons return manhattan_polygons
def cut(self,
cut_xs: numpy.ndarray = None,
cut_ys: numpy.ndarray = None
) -> List['Polygon']:
"""
Decomposes the shape into a list of constituent polygons by polygonizing and
then cutting along the specified x and/or y coordinates.
:param cut_xs: list of x-coordinates to cut along (e.g., [1, 1.4, 6])
:param cut_ys: list of y-coordinates to cut along (e.g., [1, 3, 5.4])
:return: List of Polygon objects
"""
from . import Polygon
clipped_shapes = []
for polygon in self.to_polygons():
min_x, min_y = numpy.min(polygon.vertices, axis=0)
max_x, max_y = numpy.max(polygon.vertices, axis=0)
range_x = max_x - min_x
range_y = max_y - min_y
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,)
for i in range(2):
for j in range(2):
clipper = pyclipper.Pyclipper()
clipper.AddPath(scale_to_clipper(polygon.vertices), pyclipper.PT_SUBJECT, True)
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 = polygon.copy()
poly.vertices = part
clipped_shapes.append(poly)
return clipped_shapes

View File

@ -10,14 +10,14 @@ setup(name='masque',
url='https://mpxd.net/gogs/jan/masque', url='https://mpxd.net/gogs/jan/masque',
packages=find_packages(), packages=find_packages(),
install_requires=[ install_requires=[
'numpy' 'numpy',
'pyclipper',
], ],
extras_require={ extras_require={
'visualization': ['matplotlib'], 'visualization': ['matplotlib'],
'gdsii': ['python-gdsii'], 'gdsii': ['python-gdsii'],
'svg': ['svgwrite'], 'svg': ['svgwrite'],
'text': ['freetype-py', 'matplotlib'], 'text': ['freetype-py', 'matplotlib'],
'clipping': ['pyclipper'],
}, },
) )