diff --git a/README.md b/README.md index 9755d214..1c36314f 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,8 @@ CAD program, and create G-Code for Isolation routing. - working in adding to the Optimal Tool the rest of the distances found in the Gerber and the locations associated; added GUI - added display of the results for the Rules Check Tool in a formatted way - made the Rules Check Tool document window Read Only -- made FlatCAMExcellon and FlatCAMGerber into their own files in the flatcamParser folder +- made Excellon and Gerber classes from camlib into their own files in the flatcamParser folder +- moved the ApertureMacro class from camlib to ParseGerber file 5.10.2019 diff --git a/camlib.py b/camlib.py index 16cffdc5..e4c7083d 100644 --- a/camlib.py +++ b/camlib.py @@ -49,7 +49,7 @@ import ezdxf # TODO: Commented for FlatCAM packaging with cx_freeze # from scipy.spatial import KDTree, Delaunay # from scipy.spatial import Delaunay - +from flatcamParsers.ParseGerber import ApertureMacro from flatcamParsers.ParseSVG import * from flatcamParsers.ParseDXF import * @@ -1702,379 +1702,6 @@ class Geometry(object): # origin=(px, py)) -class ApertureMacro: - """ - Syntax of aperture macros. - - : AM* - : {{*}{*}} - : $K= - : ,{,}| - : $M|< Arithmetic expression> - : 0 - """ - - # ## Regular expressions - am1_re = re.compile(r'^%AM([^\*]+)\*(.+)?(%)?$') - am2_re = re.compile(r'(.*)%$') - amcomm_re = re.compile(r'^0(.*)') - amprim_re = re.compile(r'^[1-9].*') - amvar_re = re.compile(r'^\$([0-9a-zA-z]+)=(.*)') - - def __init__(self, name=None): - self.name = name - self.raw = "" - - # ## These below are recomputed for every aperture - # ## definition, in other words, are temporary variables. - self.primitives = [] - self.locvars = {} - self.geometry = None - - def to_dict(self): - """ - Returns the object in a serializable form. Only the name and - raw are required. - - :return: Dictionary representing the object. JSON ready. - :rtype: dict - """ - - return { - 'name': self.name, - 'raw': self.raw - } - - def from_dict(self, d): - """ - Populates the object from a serial representation created - with ``self.to_dict()``. - - :param d: Serial representation of an ApertureMacro object. - :return: None - """ - for attr in ['name', 'raw']: - setattr(self, attr, d[attr]) - - def parse_content(self): - """ - Creates numerical lists for all primitives in the aperture - macro (in ``self.raw``) by replacing all variables by their - values iteratively and evaluating expressions. Results - are stored in ``self.primitives``. - - :return: None - """ - # Cleanup - self.raw = self.raw.replace('\n', '').replace('\r', '').strip(" *") - self.primitives = [] - - # Separate parts - parts = self.raw.split('*') - - # ### Every part in the macro #### - for part in parts: - # ## Comments. Ignored. - match = ApertureMacro.amcomm_re.search(part) - if match: - continue - - # ## Variables - # These are variables defined locally inside the macro. They can be - # numerical constant or defind in terms of previously define - # variables, which can be defined locally or in an aperture - # definition. All replacements ocurr here. - match = ApertureMacro.amvar_re.search(part) - if match: - var = match.group(1) - val = match.group(2) - - # Replace variables in value - for v in self.locvars: - # replaced the following line with the next to fix Mentor custom apertures not parsed OK - # val = re.sub((r'\$'+str(v)+r'(?![0-9a-zA-Z])'), str(self.locvars[v]), val) - val = val.replace('$' + str(v), str(self.locvars[v])) - - # Make all others 0 - val = re.sub(r'\$[0-9a-zA-Z](?![0-9a-zA-Z])', "0", val) - # Change x with * - val = re.sub(r'[xX]', "*", val) - - # Eval() and store. - self.locvars[var] = eval(val) - continue - - # ## Primitives - # Each is an array. The first identifies the primitive, while the - # rest depend on the primitive. All are strings representing a - # number and may contain variable definition. The values of these - # variables are defined in an aperture definition. - match = ApertureMacro.amprim_re.search(part) - if match: - # ## Replace all variables - for v in self.locvars: - # replaced the following line with the next to fix Mentor custom apertures not parsed OK - # part = re.sub(r'\$' + str(v) + r'(?![0-9a-zA-Z])', str(self.locvars[v]), part) - part = part.replace('$' + str(v), str(self.locvars[v])) - - # Make all others 0 - part = re.sub(r'\$[0-9a-zA-Z](?![0-9a-zA-Z])', "0", part) - - # Change x with * - part = re.sub(r'[xX]', "*", part) - - # ## Store - elements = part.split(",") - self.primitives.append([eval(x) for x in elements]) - continue - - log.warning("Unknown syntax of aperture macro part: %s" % str(part)) - - def append(self, data): - """ - Appends a string to the raw macro. - - :param data: Part of the macro. - :type data: str - :return: None - """ - self.raw += data - - @staticmethod - def default2zero(n, mods): - """ - Pads the ``mods`` list with zeros resulting in an - list of length n. - - :param n: Length of the resulting list. - :type n: int - :param mods: List to be padded. - :type mods: list - :return: Zero-padded list. - :rtype: list - """ - x = [0.0] * n - na = len(mods) - x[0:na] = mods - return x - - @staticmethod - def make_circle(mods): - """ - - :param mods: (Exposure 0/1, Diameter >=0, X-coord, Y-coord) - :return: - """ - - pol, dia, x, y = ApertureMacro.default2zero(4, mods) - - return {"pol": int(pol), "geometry": Point(x, y).buffer(dia/2)} - - @staticmethod - def make_vectorline(mods): - """ - - :param mods: (Exposure 0/1, Line width >= 0, X-start, Y-start, X-end, Y-end, - rotation angle around origin in degrees) - :return: - """ - pol, width, xs, ys, xe, ye, angle = ApertureMacro.default2zero(7, mods) - - line = LineString([(xs, ys), (xe, ye)]) - box = line.buffer(width/2, cap_style=2) - box_rotated = affinity.rotate(box, angle, origin=(0, 0)) - - return {"pol": int(pol), "geometry": box_rotated} - - @staticmethod - def make_centerline(mods): - """ - - :param mods: (Exposure 0/1, width >=0, height >=0, x-center, y-center, - rotation angle around origin in degrees) - :return: - """ - - pol, width, height, x, y, angle = ApertureMacro.default2zero(6, mods) - - box = shply_box(x-width/2, y-height/2, x+width/2, y+height/2) - box_rotated = affinity.rotate(box, angle, origin=(0, 0)) - - return {"pol": int(pol), "geometry": box_rotated} - - @staticmethod - def make_lowerleftline(mods): - """ - - :param mods: (exposure 0/1, width >=0, height >=0, x-lowerleft, y-lowerleft, - rotation angle around origin in degrees) - :return: - """ - - pol, width, height, x, y, angle = ApertureMacro.default2zero(6, mods) - - box = shply_box(x, y, x+width, y+height) - box_rotated = affinity.rotate(box, angle, origin=(0, 0)) - - return {"pol": int(pol), "geometry": box_rotated} - - @staticmethod - def make_outline(mods): - """ - - :param mods: - :return: - """ - - pol = mods[0] - n = mods[1] - points = [(0, 0)]*(n+1) - - for i in range(n+1): - points[i] = mods[2*i + 2:2*i + 4] - - angle = mods[2*n + 4] - - poly = Polygon(points) - poly_rotated = affinity.rotate(poly, angle, origin=(0, 0)) - - return {"pol": int(pol), "geometry": poly_rotated} - - @staticmethod - def make_polygon(mods): - """ - Note: Specs indicate that rotation is only allowed if the center - (x, y) == (0, 0). I will tolerate breaking this rule. - - :param mods: (exposure 0/1, n_verts 3<=n<=12, x-center, y-center, - diameter of circumscribed circle >=0, rotation angle around origin) - :return: - """ - - pol, nverts, x, y, dia, angle = ApertureMacro.default2zero(6, mods) - points = [(0, 0)]*nverts - - for i in range(nverts): - points[i] = (x + 0.5 * dia * cos(2*pi * i/nverts), - y + 0.5 * dia * sin(2*pi * i/nverts)) - - poly = Polygon(points) - poly_rotated = affinity.rotate(poly, angle, origin=(0, 0)) - - return {"pol": int(pol), "geometry": poly_rotated} - - @staticmethod - def make_moire(mods): - """ - Note: Specs indicate that rotation is only allowed if the center - (x, y) == (0, 0). I will tolerate breaking this rule. - - :param mods: (x-center, y-center, outer_dia_outer_ring, ring thickness, - gap, max_rings, crosshair_thickness, crosshair_len, rotation - angle around origin in degrees) - :return: - """ - - x, y, dia, thickness, gap, nrings, cross_th, cross_len, angle = ApertureMacro.default2zero(9, mods) - - r = dia/2 - thickness/2 - result = Point((x, y)).buffer(r).exterior.buffer(thickness/2.0) - ring = Point((x, y)).buffer(r).exterior.buffer(thickness/2.0) # Need a copy! - - i = 1 # Number of rings created so far - - # ## If the ring does not have an interior it means that it is - # ## a disk. Then stop. - while len(ring.interiors) > 0 and i < nrings: - r -= thickness + gap - if r <= 0: - break - ring = Point((x, y)).buffer(r).exterior.buffer(thickness/2.0) - result = cascaded_union([result, ring]) - i += 1 - - # ## Crosshair - hor = LineString([(x - cross_len, y), (x + cross_len, y)]).buffer(cross_th/2.0, cap_style=2) - ver = LineString([(x, y-cross_len), (x, y + cross_len)]).buffer(cross_th/2.0, cap_style=2) - result = cascaded_union([result, hor, ver]) - - return {"pol": 1, "geometry": result} - - @staticmethod - def make_thermal(mods): - """ - Note: Specs indicate that rotation is only allowed if the center - (x, y) == (0, 0). I will tolerate breaking this rule. - - :param mods: [x-center, y-center, diameter-outside, diameter-inside, - gap-thickness, rotation angle around origin] - :return: - """ - - x, y, dout, din, t, angle = ApertureMacro.default2zero(6, mods) - - ring = Point((x, y)).buffer(dout/2.0).difference(Point((x, y)).buffer(din/2.0)) - hline = LineString([(x - dout/2.0, y), (x + dout/2.0, y)]).buffer(t/2.0, cap_style=3) - vline = LineString([(x, y - dout/2.0), (x, y + dout/2.0)]).buffer(t/2.0, cap_style=3) - thermal = ring.difference(hline.union(vline)) - - return {"pol": 1, "geometry": thermal} - - def make_geometry(self, modifiers): - """ - Runs the macro for the given modifiers and generates - the corresponding geometry. - - :param modifiers: Modifiers (parameters) for this macro - :type modifiers: list - :return: Shapely geometry - :rtype: shapely.geometry.polygon - """ - - # ## Primitive makers - makers = { - "1": ApertureMacro.make_circle, - "2": ApertureMacro.make_vectorline, - "20": ApertureMacro.make_vectorline, - "21": ApertureMacro.make_centerline, - "22": ApertureMacro.make_lowerleftline, - "4": ApertureMacro.make_outline, - "5": ApertureMacro.make_polygon, - "6": ApertureMacro.make_moire, - "7": ApertureMacro.make_thermal - } - - # ## Store modifiers as local variables - modifiers = modifiers or [] - modifiers = [float(m) for m in modifiers] - self.locvars = {} - for i in range(0, len(modifiers)): - self.locvars[str(i + 1)] = modifiers[i] - - # ## Parse - self.primitives = [] # Cleanup - self.geometry = Polygon() - self.parse_content() - - # ## Make the geometry - for primitive in self.primitives: - # Make the primitive - prim_geo = makers[str(int(primitive[0]))](primitive[1:]) - - # Add it (according to polarity) - # if self.geometry is None and prim_geo['pol'] == 1: - # self.geometry = prim_geo['geometry'] - # continue - if prim_geo['pol'] == 1: - self.geometry = self.geometry.union(prim_geo['geometry']) - continue - if prim_geo['pol'] == 0: - self.geometry = self.geometry.difference(prim_geo['geometry']) - continue - - return self.geometry - - class AttrDict(dict): def __init__(self, *args, **kwargs): super(AttrDict, self).__init__(*args, **kwargs) @@ -5181,7 +4808,7 @@ def arc(center, radius, start, stop, direction, steps_per_circ): angle = abs(stop - start) - #angle = stop-start + # angle = stop-start steps = max([int(ceil(angle / (2 * pi) * steps_per_circ)), 2]) delta_angle = da_sign[direction] * angle * 1.0 / steps for i in range(steps + 1): @@ -5502,8 +5129,10 @@ def parse_gerber_number(strnumber, int_digits, frac_digits, zeros): # dangling_lines = [] # for i1, i2 in lineIndices_: # p = (i1, i2) -# connections = filter(lambda k: p != k and (p[0] == k[0] or p[0] == k[1] or p[1] == k[0] or p[1] == k[1]), lineIndices_) -# # connections = filter(lambda (i1_, i2_): (i1, i2) != (i1_, i2_) and (i1 == i1_ or i1 == i2_ or i2 == i1_ or i2 == i2_), lineIndices_) +# connections = filter(lambda k: p != k and +# (p[0] == k[0] or p[0] == k[1] or p[1] == k[0] or p[1] == k[1]), lineIndices_) +# # connections = filter(lambda (i1_, i2_): (i1, i2) != (i1_, i2_) and +# (i1 == i1_ or i1 == i2_ or i2 == i1_ or i2 == i2_), lineIndices_) # assert 1 <= len(connections) <= 2 # if len(connections) == 1: # dangling_lines.append((i1, i2)) diff --git a/flatcamParsers/ParseGerber.py b/flatcamParsers/ParseGerber.py index 3402d1f1..bfa611ac 100644 --- a/flatcamParsers/ParseGerber.py +++ b/flatcamParsers/ParseGerber.py @@ -926,7 +926,6 @@ class Gerber(Geometry): elif current_operation_code == 2: if len(path) > 1: geo_s = None - geo_f = None geo_dict = dict() # --- BUFFERED --- @@ -1910,8 +1909,7 @@ class Gerber(Geometry): log.debug('camlib.Gerber.skew() Exception --> %s' % str(e)) return 'fail' - self.app.inform.emit('[success] %s' % - _("Gerber Skew done.")) + self.app.inform.emit('[success] %s' % _("Gerber Skew done.")) self.app.proc_container.new_text = '' def rotate(self, angle, point): @@ -1974,3 +1972,376 @@ class Gerber(Geometry): self.app.inform.emit('[success] %s' % _("Gerber Rotate done.")) self.app.proc_container.new_text = '' + + +class ApertureMacro: + """ + Syntax of aperture macros. + + : AM* + : {{*}{*}} + : $K= + : ,{,}| + : $M|< Arithmetic expression> + : 0 + """ + + # ## Regular expressions + am1_re = re.compile(r'^%AM([^\*]+)\*(.+)?(%)?$') + am2_re = re.compile(r'(.*)%$') + amcomm_re = re.compile(r'^0(.*)') + amprim_re = re.compile(r'^[1-9].*') + amvar_re = re.compile(r'^\$([0-9a-zA-z]+)=(.*)') + + def __init__(self, name=None): + self.name = name + self.raw = "" + + # ## These below are recomputed for every aperture + # ## definition, in other words, are temporary variables. + self.primitives = [] + self.locvars = {} + self.geometry = None + + def to_dict(self): + """ + Returns the object in a serializable form. Only the name and + raw are required. + + :return: Dictionary representing the object. JSON ready. + :rtype: dict + """ + + return { + 'name': self.name, + 'raw': self.raw + } + + def from_dict(self, d): + """ + Populates the object from a serial representation created + with ``self.to_dict()``. + + :param d: Serial representation of an ApertureMacro object. + :return: None + """ + for attr in ['name', 'raw']: + setattr(self, attr, d[attr]) + + def parse_content(self): + """ + Creates numerical lists for all primitives in the aperture + macro (in ``self.raw``) by replacing all variables by their + values iteratively and evaluating expressions. Results + are stored in ``self.primitives``. + + :return: None + """ + # Cleanup + self.raw = self.raw.replace('\n', '').replace('\r', '').strip(" *") + self.primitives = [] + + # Separate parts + parts = self.raw.split('*') + + # ### Every part in the macro #### + for part in parts: + # ## Comments. Ignored. + match = ApertureMacro.amcomm_re.search(part) + if match: + continue + + # ## Variables + # These are variables defined locally inside the macro. They can be + # numerical constant or defind in terms of previously define + # variables, which can be defined locally or in an aperture + # definition. All replacements ocurr here. + match = ApertureMacro.amvar_re.search(part) + if match: + var = match.group(1) + val = match.group(2) + + # Replace variables in value + for v in self.locvars: + # replaced the following line with the next to fix Mentor custom apertures not parsed OK + # val = re.sub((r'\$'+str(v)+r'(?![0-9a-zA-Z])'), str(self.locvars[v]), val) + val = val.replace('$' + str(v), str(self.locvars[v])) + + # Make all others 0 + val = re.sub(r'\$[0-9a-zA-Z](?![0-9a-zA-Z])', "0", val) + # Change x with * + val = re.sub(r'[xX]', "*", val) + + # Eval() and store. + self.locvars[var] = eval(val) + continue + + # ## Primitives + # Each is an array. The first identifies the primitive, while the + # rest depend on the primitive. All are strings representing a + # number and may contain variable definition. The values of these + # variables are defined in an aperture definition. + match = ApertureMacro.amprim_re.search(part) + if match: + # ## Replace all variables + for v in self.locvars: + # replaced the following line with the next to fix Mentor custom apertures not parsed OK + # part = re.sub(r'\$' + str(v) + r'(?![0-9a-zA-Z])', str(self.locvars[v]), part) + part = part.replace('$' + str(v), str(self.locvars[v])) + + # Make all others 0 + part = re.sub(r'\$[0-9a-zA-Z](?![0-9a-zA-Z])', "0", part) + + # Change x with * + part = re.sub(r'[xX]', "*", part) + + # ## Store + elements = part.split(",") + self.primitives.append([eval(x) for x in elements]) + continue + + log.warning("Unknown syntax of aperture macro part: %s" % str(part)) + + def append(self, data): + """ + Appends a string to the raw macro. + + :param data: Part of the macro. + :type data: str + :return: None + """ + self.raw += data + + @staticmethod + def default2zero(n, mods): + """ + Pads the ``mods`` list with zeros resulting in an + list of length n. + + :param n: Length of the resulting list. + :type n: int + :param mods: List to be padded. + :type mods: list + :return: Zero-padded list. + :rtype: list + """ + x = [0.0] * n + na = len(mods) + x[0:na] = mods + return x + + @staticmethod + def make_circle(mods): + """ + + :param mods: (Exposure 0/1, Diameter >=0, X-coord, Y-coord) + :return: + """ + + pol, dia, x, y = ApertureMacro.default2zero(4, mods) + + return {"pol": int(pol), "geometry": Point(x, y).buffer(dia/2)} + + @staticmethod + def make_vectorline(mods): + """ + + :param mods: (Exposure 0/1, Line width >= 0, X-start, Y-start, X-end, Y-end, + rotation angle around origin in degrees) + :return: + """ + pol, width, xs, ys, xe, ye, angle = ApertureMacro.default2zero(7, mods) + + line = LineString([(xs, ys), (xe, ye)]) + box = line.buffer(width/2, cap_style=2) + box_rotated = affinity.rotate(box, angle, origin=(0, 0)) + + return {"pol": int(pol), "geometry": box_rotated} + + @staticmethod + def make_centerline(mods): + """ + + :param mods: (Exposure 0/1, width >=0, height >=0, x-center, y-center, + rotation angle around origin in degrees) + :return: + """ + + pol, width, height, x, y, angle = ApertureMacro.default2zero(6, mods) + + box = shply_box(x-width/2, y-height/2, x+width/2, y+height/2) + box_rotated = affinity.rotate(box, angle, origin=(0, 0)) + + return {"pol": int(pol), "geometry": box_rotated} + + @staticmethod + def make_lowerleftline(mods): + """ + + :param mods: (exposure 0/1, width >=0, height >=0, x-lowerleft, y-lowerleft, + rotation angle around origin in degrees) + :return: + """ + + pol, width, height, x, y, angle = ApertureMacro.default2zero(6, mods) + + box = shply_box(x, y, x+width, y+height) + box_rotated = affinity.rotate(box, angle, origin=(0, 0)) + + return {"pol": int(pol), "geometry": box_rotated} + + @staticmethod + def make_outline(mods): + """ + + :param mods: + :return: + """ + + pol = mods[0] + n = mods[1] + points = [(0, 0)]*(n+1) + + for i in range(n+1): + points[i] = mods[2*i + 2:2*i + 4] + + angle = mods[2*n + 4] + + poly = Polygon(points) + poly_rotated = affinity.rotate(poly, angle, origin=(0, 0)) + + return {"pol": int(pol), "geometry": poly_rotated} + + @staticmethod + def make_polygon(mods): + """ + Note: Specs indicate that rotation is only allowed if the center + (x, y) == (0, 0). I will tolerate breaking this rule. + + :param mods: (exposure 0/1, n_verts 3<=n<=12, x-center, y-center, + diameter of circumscribed circle >=0, rotation angle around origin) + :return: + """ + + pol, nverts, x, y, dia, angle = ApertureMacro.default2zero(6, mods) + points = [(0, 0)]*nverts + + for i in range(nverts): + points[i] = (x + 0.5 * dia * cos(2*pi * i/nverts), + y + 0.5 * dia * sin(2*pi * i/nverts)) + + poly = Polygon(points) + poly_rotated = affinity.rotate(poly, angle, origin=(0, 0)) + + return {"pol": int(pol), "geometry": poly_rotated} + + @staticmethod + def make_moire(mods): + """ + Note: Specs indicate that rotation is only allowed if the center + (x, y) == (0, 0). I will tolerate breaking this rule. + + :param mods: (x-center, y-center, outer_dia_outer_ring, ring thickness, + gap, max_rings, crosshair_thickness, crosshair_len, rotation + angle around origin in degrees) + :return: + """ + + x, y, dia, thickness, gap, nrings, cross_th, cross_len, angle = ApertureMacro.default2zero(9, mods) + + r = dia/2 - thickness/2 + result = Point((x, y)).buffer(r).exterior.buffer(thickness/2.0) + ring = Point((x, y)).buffer(r).exterior.buffer(thickness/2.0) # Need a copy! + + i = 1 # Number of rings created so far + + # ## If the ring does not have an interior it means that it is + # ## a disk. Then stop. + while len(ring.interiors) > 0 and i < nrings: + r -= thickness + gap + if r <= 0: + break + ring = Point((x, y)).buffer(r).exterior.buffer(thickness/2.0) + result = cascaded_union([result, ring]) + i += 1 + + # ## Crosshair + hor = LineString([(x - cross_len, y), (x + cross_len, y)]).buffer(cross_th/2.0, cap_style=2) + ver = LineString([(x, y-cross_len), (x, y + cross_len)]).buffer(cross_th/2.0, cap_style=2) + result = cascaded_union([result, hor, ver]) + + return {"pol": 1, "geometry": result} + + @staticmethod + def make_thermal(mods): + """ + Note: Specs indicate that rotation is only allowed if the center + (x, y) == (0, 0). I will tolerate breaking this rule. + + :param mods: [x-center, y-center, diameter-outside, diameter-inside, + gap-thickness, rotation angle around origin] + :return: + """ + + x, y, dout, din, t, angle = ApertureMacro.default2zero(6, mods) + + ring = Point((x, y)).buffer(dout/2.0).difference(Point((x, y)).buffer(din/2.0)) + hline = LineString([(x - dout/2.0, y), (x + dout/2.0, y)]).buffer(t/2.0, cap_style=3) + vline = LineString([(x, y - dout/2.0), (x, y + dout/2.0)]).buffer(t/2.0, cap_style=3) + thermal = ring.difference(hline.union(vline)) + + return {"pol": 1, "geometry": thermal} + + def make_geometry(self, modifiers): + """ + Runs the macro for the given modifiers and generates + the corresponding geometry. + + :param modifiers: Modifiers (parameters) for this macro + :type modifiers: list + :return: Shapely geometry + :rtype: shapely.geometry.polygon + """ + + # ## Primitive makers + makers = { + "1": ApertureMacro.make_circle, + "2": ApertureMacro.make_vectorline, + "20": ApertureMacro.make_vectorline, + "21": ApertureMacro.make_centerline, + "22": ApertureMacro.make_lowerleftline, + "4": ApertureMacro.make_outline, + "5": ApertureMacro.make_polygon, + "6": ApertureMacro.make_moire, + "7": ApertureMacro.make_thermal + } + + # ## Store modifiers as local variables + modifiers = modifiers or [] + modifiers = [float(m) for m in modifiers] + self.locvars = {} + for i in range(0, len(modifiers)): + self.locvars[str(i + 1)] = modifiers[i] + + # ## Parse + self.primitives = [] # Cleanup + self.geometry = Polygon() + self.parse_content() + + # ## Make the geometry + for primitive in self.primitives: + # Make the primitive + prim_geo = makers[str(int(primitive[0]))](primitive[1:]) + + # Add it (according to polarity) + # if self.geometry is None and prim_geo['pol'] == 1: + # self.geometry = prim_geo['geometry'] + # continue + if prim_geo['pol'] == 1: + self.geometry = self.geometry.union(prim_geo['geometry']) + continue + if prim_geo['pol'] == 0: + self.geometry = self.geometry.difference(prim_geo['geometry']) + continue + + return self.geometry