From dd029d6dedd93f6fea2bba62c5a68a6bb4c8de13 Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Tue, 21 Sep 2021 15:55:35 +0300 Subject: [PATCH] - fixed an error due of missing attribute of PlotCanvasLegacy when using Legacy2D graphic engine - solving deprecation warnings issued by Shapely - made sure that the Gerber Object geometry is always flattened --- CHANGELOG.md | 6 +++ appEditors/AppGeoEditor.py | 7 +-- appEditors/AppGerberEditor.py | 10 ++-- appGUI/PlotCanvasLegacy.py | 3 ++ appObjects/FlatCAMGeometry.py | 8 +-- appObjects/FlatCAMGerber.py | 84 ++++++++++++++++++------------ appParsers/ParseGerber.py | 36 ++++--------- appPlugins/ToolCopperThieving.py | 14 ++--- appPlugins/ToolCutOut.py | 43 +++------------ appPlugins/ToolEtchCompensation.py | 7 +-- appPlugins/ToolFollow.py | 13 ++--- appPlugins/ToolInvertGerber.py | 18 ++----- appPlugins/ToolIsolation.py | 12 ++--- appPlugins/ToolNCC.py | 40 ++++---------- appPlugins/ToolOptimal.py | 7 ++- camlib.py | 30 ++++++++--- descartes/patch.py | 7 ++- 17 files changed, 143 insertions(+), 202 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d792ccb3..00dbf0ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ CHANGELOG for FlatCAM beta ================================================= +21.09.2021 + +- fixed an error due of missing attribute of PlotCanvasLegacy when using Legacy2D graphic engine +- solving deprecation warnings issued by Shapely +- made sure that the Gerber Object geometry is always flattened + 19.09.2021 - fixed Extract Plugin not extracting soldermask for aperture macros diff --git a/appEditors/AppGeoEditor.py b/appEditors/AppGeoEditor.py index 036dc4d5..6124f35e 100644 --- a/appEditors/AppGeoEditor.py +++ b/appEditors/AppGeoEditor.py @@ -16,7 +16,7 @@ from PyQt6.QtCore import Qt # import inspect -from camlib import distance, arc, three_point_circle, Geometry, FlatCAMRTreeStorage +from camlib import distance, arc, three_point_circle, Geometry, FlatCAMRTreeStorage, flatten_shapely_geometry from appTool import AppTool from appGUI.GUIElements import OptionalInputSection, FCCheckBox, FCLabel, FCComboBox, FCTextAreaRich, \ FCDoubleSpinner, FCButton, FCInputDoubleSpinner, FCTree, NumericalEvalTupleEntry, FCEntry, FCTextEdit, \ @@ -5349,10 +5349,7 @@ class AppGeoEditor(QtCore.QObject): # #################################################################################################### # remove the invalid geometry and also the Points as those are not relevant for the Editor # #################################################################################################### - try: - __ = iter(geo_to_edit) - except TypeError: - geo_to_edit = [geo_to_edit] + geo_to_edit = flatten_shapely_geometry(geo_to_edit) cleaned_geo = [g for g in geo_to_edit if g and not g.is_empty and g.is_valid and g.geom_type != 'Point'] for shape in cleaned_geo: diff --git a/appEditors/AppGerberEditor.py b/appEditors/AppGerberEditor.py index 377b2d1d..802f2ba4 100644 --- a/appEditors/AppGerberEditor.py +++ b/appEditors/AppGerberEditor.py @@ -17,7 +17,7 @@ from vispy.geometry import Rect from copy import copy, deepcopy import logging -from camlib import distance, arc, three_point_circle +from camlib import distance, arc, three_point_circle, flatten_shapely_geometry from appGUI.GUIElements import FCEntry, FCComboBox, FCTable, FCDoubleSpinner, FCSpinner, RadioSet, EvalEntry2, \ FCInputDoubleSpinner, FCButton, OptionalInputSection, FCCheckBox, NumericalEvalTupleEntry, FCLabel, FCTextEdit, \ VerticalScrollArea, FCGridLayout @@ -4886,13 +4886,9 @@ class AppGerberEditor(QtCore.QObject): new_poly = MultiPolygon(poly_buffer) new_poly = new_poly.buffer(0.00000001) new_poly = new_poly.buffer(-0.00000001) - - try: - __ = iter(new_poly) - except TypeError: - new_poly = [new_poly] - + new_poly = flatten_shapely_geometry(new_poly) grb_obj.solid_geometry = deepcopy(new_poly) + grb_obj.follow_geometry = deepcopy(follow_buffer) for k, v in self.gerber_obj_options.items(): diff --git a/appGUI/PlotCanvasLegacy.py b/appGUI/PlotCanvasLegacy.py index e0cf32f7..0a584a51 100644 --- a/appGUI/PlotCanvasLegacy.py +++ b/appGUI/PlotCanvasLegacy.py @@ -434,6 +434,9 @@ class PlotCanvasLegacy(QtCore.QObject): self.canvas.draw() + def on_update_text_hud(self, dx=None, dy=None, x=None, y=None): + self.text_hud.on_update_text_hud(dx=dx, dy=dy, x=x, y=y) + class Thud(QtCore.QObject): text_changed = QtCore.pyqtSignal(str) diff --git a/appObjects/FlatCAMGeometry.py b/appObjects/FlatCAMGeometry.py index 04ff3732..413fe32f 100644 --- a/appObjects/FlatCAMGeometry.py +++ b/appObjects/FlatCAMGeometry.py @@ -13,7 +13,7 @@ from shapely.geometry import MultiLineString, LineString, LinearRing, box import shapely.affinity as affinity -from camlib import Geometry, grace +from camlib import Geometry, flatten_shapely_geometry from appObjects.FlatCAMObj import * @@ -1529,11 +1529,7 @@ class GeometryObject(FlatCAMObj, Geometry): if geo_final.solid_geometry is None: geo_final.solid_geometry = [] - try: - __ = iter(geo_final.solid_geometry) - except TypeError: - geo_final.solid_geometry = [geo_final.solid_geometry] - + geo_final.solid_geometry = flatten_shapely_geometry(geo_final.solid_geometry) new_solid_geometry = [] new_options = {} new_tools = {} diff --git a/appObjects/FlatCAMGerber.py b/appObjects/FlatCAMGerber.py index 058fdaf5..df2ac8c8 100644 --- a/appObjects/FlatCAMGerber.py +++ b/appObjects/FlatCAMGerber.py @@ -11,7 +11,8 @@ # ########################################################## -from shapely.geometry import Point, MultiLineString, LineString, LinearRing +from shapely.geometry import MultiLineString, LinearRing +from camlib import flatten_shapely_geometry from appParsers.ParseGerber import Gerber from appObjects.FlatCAMObj import * @@ -911,12 +912,6 @@ class GerberObject(FlatCAMObj, Gerber): else: geometry = self.solid_geometry - # Make sure geometry is iterable. - try: - __ = iter(geometry) - except TypeError: - geometry = [geometry] - if self.app.is_legacy is False: def random_color(): r_color = np.random.rand(4) @@ -940,34 +935,16 @@ class GerberObject(FlatCAMObj, Gerber): try: if self.options["solid"]: - for g in geometry: - if type(g) == Polygon or type(g) == LineString: - self.add_shape(shape=g, color=color, - face_color=random_color() if self.options['multicolored'] - else face_color, visible=visible) - elif type(g) == Point: - pass - else: - try: - for el in g: - self.add_shape(shape=el, color=color, - face_color=random_color() if self.options['multicolored'] - else face_color, visible=visible) - except TypeError: - self.add_shape(shape=g, color=color, - face_color=random_color() if self.options['multicolored'] - else face_color, visible=visible) + used_color = color + used_face_color = random_color() if self.options['multicolored'] else face_color else: - for g in geometry: - if type(g) == Polygon or type(g) == LineString: - self.add_shape(shape=g, color=random_color() if self.options['multicolored'] else 'black', - visible=visible) - elif type(g) == Point: - pass - else: - for el in g: - self.add_shape(shape=el, color=random_color() if self.options['multicolored'] else 'black', - visible=visible) + used_color = random_color() if self.options['multicolored'] else 'black' + used_face_color = None + + for g in geometry: + if isinstance(g, (Polygon, LineString)): + self.add_shape(shape=g, color=used_color, face_color=used_face_color, visible=visible) + self.shapes.redraw( # update_colors=(self.fill_color, self.outline_color), # indexes=self.app.plotcanvas.shape_collection.data.keys() @@ -977,6 +954,45 @@ class GerberObject(FlatCAMObj, Gerber): except Exception as e: self.app.log.error("GerberObject.plot() --> %s" % str(e)) + # try: + # if self.options["solid"]: + # for g in geometry: + # if type(g) == Polygon or type(g) == LineString: + # self.add_shape(shape=g, color=color, + # face_color=random_color() if self.options['multicolored'] + # else face_color, visible=visible) + # elif type(g) == Point: + # pass + # else: + # try: + # for el in g: + # self.add_shape(shape=el, color=color, + # face_color=random_color() if self.options['multicolored'] + # else face_color, visible=visible) + # except TypeError: + # self.add_shape(shape=g, color=color, + # face_color=random_color() if self.options['multicolored'] + # else face_color, visible=visible) + # else: + # for g in geometry: + # if type(g) == Polygon or type(g) == LineString: + # self.add_shape(shape=g, color=random_color() if self.options['multicolored'] else 'black', + # visible=visible) + # elif type(g) == Point: + # pass + # else: + # for el in g: + # self.add_shape(shape=el, color=random_color() if self.options['multicolored'] else 'black', + # visible=visible) + # self.shapes.redraw( + # # update_colors=(self.fill_color, self.outline_color), + # # indexes=self.app.plotcanvas.shape_collection.data.keys() + # ) + # except (ObjectDeleted, AttributeError): + # self.shapes.clear(update=True) + # except Exception as e: + # self.app.log.error("GerberObject.plot() --> %s" % str(e)) + def plot_aperture(self, only_flashes=False, run_thread=False, **kwargs): """ diff --git a/appParsers/ParseGerber.py b/appParsers/ParseGerber.py index 39707869..feb22853 100644 --- a/appParsers/ParseGerber.py +++ b/appParsers/ParseGerber.py @@ -1,5 +1,5 @@ from PyQt6 import QtWidgets -from camlib import Geometry, arc, arc_angle, ApertureMacro, grace +from camlib import Geometry, arc, arc_angle, ApertureMacro, grace, flatten_shapely_geometry import numpy as np import traceback @@ -417,8 +417,6 @@ class Gerber(Geometry): # Current coordinates current_x = 0 current_y = 0 - previous_x = 0 - previous_y = 0 current_d = None @@ -1630,9 +1628,12 @@ class Gerber(Geometry): self.tools[last_path_aperture]['geometry'] = [] self.tools[last_path_aperture]['geometry'].append(deepcopy(geo_dict)) + # ########################################################################################################## + # Creating the FINAL GEOMETRY + # ########################################################################################################## # --- Apply buffer --- # this treats the case when we are storing geometry as paths - self.follow_geometry = follow_buffer + self.follow_geometry = flatten_shapely_geometry(follow_buffer) # this treats the case when we are storing geometry as solids try: @@ -1705,18 +1706,12 @@ class Gerber(Geometry): candidate_geo.append(self.solid_geometry.buffer(-0.0000001)) self.solid_geometry = candidate_geo - # try: - # self.solid_geometry = self.solid_geometry.union(new_poly) - # except Exception as e: - # # in case in the new_poly are some self intersections try to avoid making union with them - # for poly in new_poly: - # try: - # self.solid_geometry = self.solid_geometry.union(poly) - # except Exception: - # pass else: self.solid_geometry = self.solid_geometry.difference(new_poly) + # flatten the solid geometry + self.solid_geometry = flatten_shapely_geometry(self.solid_geometry) + if self.app.defaults['gerber_clean_apertures']: # clean the Gerber file of apertures with no geometry for apid, apvalue in list(self.tools.items()): @@ -1995,13 +1990,7 @@ class Gerber(Geometry): self.solid_geometry = [self.solid_geometry, geos] # flatten the self.solid_geometry list for import_svg() to import SVG as Gerber - self.solid_geometry = list(self.flatten_list(self.solid_geometry)) - - try: - __ = iter(self.solid_geometry) - except TypeError: - self.solid_geometry = [self.solid_geometry] - + self.solid_geometry = flatten_shapely_geometry(self.solid_geometry) if 0 not in self.tools: self.tools[0] = { 'type': 'REG', @@ -2530,12 +2519,7 @@ class Gerber(Geometry): except AttributeError: return obj - res = buffer_geom(self.solid_geometry) - try: - __ = iter(res) - self.solid_geometry = res - except TypeError: - self.solid_geometry = [res] + self.solid_geometry = flatten_shapely_geometry(buffer_geom(self.solid_geometry)) # we need to buffer the geometry stored in the Gerber apertures, too try: diff --git a/appPlugins/ToolCopperThieving.py b/appPlugins/ToolCopperThieving.py index 01aabbb0..681eb546 100644 --- a/appPlugins/ToolCopperThieving.py +++ b/appPlugins/ToolCopperThieving.py @@ -7,7 +7,7 @@ from PyQt6 import QtWidgets, QtCore, QtGui -from camlib import grace +from camlib import grace, flatten_shapely_geometry from appTool import AppTool from appGUI.GUIElements import FCDoubleSpinner, RadioSet, FCEntry, FCComboBox, FCLabel, FCCheckBox, \ VerticalScrollArea, FCGridLayout, FCFrame, FCComboBox2 @@ -694,11 +694,7 @@ class ToolCopperThieving(AppTool): geo_n = working_obj.solid_geometry if working_obj.kind == 'geometry': - try: - __ = iter(geo_n) - except Exception as e: - log.error("ToolCopperFIll.copper_thieving() 'box' --> %s" % str(e)) - geo_n = [geo_n] + geo_n = flatten_shapely_geometry(geo_n) geo_buff_list = [] for poly in geo_n: @@ -789,11 +785,7 @@ class ToolCopperThieving(AppTool): dy = bounding_box.centroid.y - thieving_box_geo.centroid.y thieving_box_geo = affinity.translate(thieving_box_geo, xoff=dx, yoff=dy) - - try: - _it = iter(thieving_box_geo) - except TypeError: - thieving_box_geo = [thieving_box_geo] + thieving_box_geo = flatten_shapely_geometry(thieving_box_geo) thieving_geo = [] for dot_geo in thieving_box_geo: diff --git a/appPlugins/ToolCutOut.py b/appPlugins/ToolCutOut.py index 6434848b..a27c3c40 100644 --- a/appPlugins/ToolCutOut.py +++ b/appPlugins/ToolCutOut.py @@ -13,6 +13,7 @@ from appGUI.GUIElements import FCDoubleSpinner, FCCheckBox, RadioSet, FCComboBox from shapely.geometry import box, MultiPolygon, Polygon, LineString, LinearRing, MultiLineString, Point from shapely.ops import unary_union, linemerge import shapely.affinity as affinity +from camlib import flatten_shapely_geometry from matplotlib.backend_bases import KeyEvent as mpl_key_event @@ -937,11 +938,7 @@ class CutOut(AppTool): if gap_type == 'bt' and thin_entry != 0: gaps_solid_geo = rest_geo else: - try: - __ = iter(object_geo) - except TypeError: - object_geo = [object_geo] - + object_geo = flatten_shapely_geometry(object_geo) for geom_struct in object_geo: if cutout_obj.kind == 'gerber': if margin >= 0: @@ -991,11 +988,7 @@ class CutOut(AppTool): __, rest_geo = cutout_handler(geom=mb_geo, gapsize=gapsize) mouse_bites_geo = rest_geo else: - try: - __ = iter(mb_object_geo) - except TypeError: - mb_object_geo = [mb_object_geo] - + mb_object_geo = flatten_shapely_geometry(mb_object_geo) for mb_geom_struct in mb_object_geo: if cutout_obj.kind == 'gerber': if margin >= 0: @@ -1269,11 +1262,7 @@ class CutOut(AppTool): gaps_solid_geo = self.subtract_geo(geo, deepcopy(solid_geo)) else: if cutout_obj.kind == 'geometry': - try: - __ = iter(object_geo) - except TypeError: - object_geo = [object_geo] - + object_geo = flatten_shapely_geometry(object_geo) for geom_struct in object_geo: geom_struct = unary_union(geom_struct) xmin, ymin, xmax, ymax = geom_struct.bounds @@ -1287,11 +1276,7 @@ class CutOut(AppTool): except TypeError: gaps_solid_geo.append(self.subtract_geo(geom_struct, c_geo)) elif cutout_obj.kind == 'gerber' and margin >= 0: - try: - __ = iter(object_geo) - except TypeError: - object_geo = [object_geo] - + object_geo = flatten_shapely_geometry(object_geo) for geom_struct in object_geo: geom_struct = unary_union(geom_struct) xmin, ymin, xmax, ymax = geom_struct.bounds @@ -1347,11 +1332,7 @@ class CutOut(AppTool): mouse_bites_geo = self.subtract_geo(mb_geo, mb_solid_geo) else: if cutout_obj.kind == 'geometry': - try: - __ = iter(mb_object_geo) - except TypeError: - mb_object_geo = [mb_object_geo] - + mb_object_geo = flatten_shapely_geometry(mb_object_geo) for mb_geom_struct in mb_object_geo: mb_geom_struct = unary_union(mb_geom_struct) xmin, ymin, xmax, ymax = mb_geom_struct.bounds @@ -1365,11 +1346,7 @@ class CutOut(AppTool): except TypeError: mouse_bites_geo.append(self.subtract_geo(mb_geom_struct, c_geo)) elif cutout_obj.kind == 'gerber' and margin >= 0: - try: - __ = iter(mb_object_geo) - except TypeError: - mb_object_geo = [mb_object_geo] - + mb_object_geo = flatten_shapely_geometry(mb_object_geo) for mb_geom_struct in mb_object_geo: mb_geom_struct = unary_union(mb_geom_struct) xmin, ymin, xmax, ymax = mb_geom_struct.bounds @@ -2259,11 +2236,7 @@ class CutOut(AppTool): """ results = [] - try: - __ = iter(target_geo) - except TypeError: - target_geo = [target_geo] - + target_geo = flatten_shapely_geometry(target_geo) for geo in target_geo: if second_geo.intersects(geo): results.append(second_geo.intersection(geo)) diff --git a/appPlugins/ToolEtchCompensation.py b/appPlugins/ToolEtchCompensation.py index 2a271a38..fdae675b 100644 --- a/appPlugins/ToolEtchCompensation.py +++ b/appPlugins/ToolEtchCompensation.py @@ -10,6 +10,7 @@ from PyQt6 import QtWidgets, QtCore, QtGui from appTool import AppTool from appGUI.GUIElements import FCButton, FCDoubleSpinner, RadioSet, FCComboBox, NumericalEvalEntry, FCEntry, \ VerticalScrollArea, FCGridLayout, FCLabel, FCFrame +from camlib import flatten_shapely_geometry from shapely.ops import unary_union @@ -225,11 +226,7 @@ class ToolEtchCompensation(AppTool): # no need to do anything for zero value offset isn't it? compensating with zero is the same as the original return - try: - __ = iter(grb_obj.solid_geometry) - except TypeError: - grb_obj.solid_geometry = [grb_obj.solid_geometry] - + grb_obj.solid_geometry = flatten_shapely_geometry(grb_obj.solid_geometry) new_solid_geometry = [] for poly in grb_obj.solid_geometry: diff --git a/appPlugins/ToolFollow.py b/appPlugins/ToolFollow.py index 2e950d19..2893ded2 100644 --- a/appPlugins/ToolFollow.py +++ b/appPlugins/ToolFollow.py @@ -10,6 +10,7 @@ from PyQt6 import QtWidgets, QtCore, QtGui from appTool import AppTool from appGUI.GUIElements import RadioSet, FCButton, FCComboBox, FCLabel, VerticalScrollArea, FCGridLayout, FCFrame from appParsers.ParseGerber import Gerber +from camlib import flatten_shapely_geometry from copy import deepcopy @@ -304,11 +305,7 @@ class ToolFollow(AppTool, Gerber): if opt_key.find('tools_') == 0: new_data[opt_key] = app_obj.options[opt_key] - try: - __ = iter(followed_obj.follow_geometry) - except TypeError: - followed_obj.follow_geometry = [followed_obj.follow_geometry] - + followed_obj.follow_geometry = flatten_shapely_geometry(followed_obj.follow_geometry) follow_geo = [ g for g in followed_obj.follow_geometry if g and not g.is_empty and g.is_valid and g.geom_type != 'Point' @@ -384,11 +381,7 @@ class ToolFollow(AppTool, Gerber): self.sel_rect[:] = [] self.points = [] - try: - __ = iter(area_follow) - except TypeError: - area_follow = [area_follow] - + area_follow = flatten_shapely_geometry(area_follow) cleaned_area_follow = [g for g in area_follow if not g.is_empty and g.is_valid and g.geom_type != 'Point'] new_obj.solid_geometry = deepcopy(cleaned_area_follow) diff --git a/appPlugins/ToolInvertGerber.py b/appPlugins/ToolInvertGerber.py index 70507236..c6f4db2b 100644 --- a/appPlugins/ToolInvertGerber.py +++ b/appPlugins/ToolInvertGerber.py @@ -10,6 +10,7 @@ from PyQt6 import QtWidgets, QtCore, QtGui from appTool import AppTool from appGUI.GUIElements import FCButton, FCDoubleSpinner, RadioSet, FCComboBox, FCLabel, \ VerticalScrollArea, FCGridLayout, FCFrame +from camlib import flatten_shapely_geometry from shapely.geometry import box @@ -151,21 +152,12 @@ class ToolInvertGerber(AppTool): xmin, ymin, xmax, ymax = grb_obj.bounds() grb_box = box(xmin, ymin, xmax, ymax).buffer(margin, resolution=grb_circle_steps, join_style=join_style) - - try: - __ = iter(grb_obj.solid_geometry) - except TypeError: - grb_obj.solid_geometry = list(grb_obj.solid_geometry) - new_solid_geometry = deepcopy(grb_box) + grb_obj.solid_geometry = flatten_shapely_geometry(grb_obj.solid_geometry) for poly in grb_obj.solid_geometry: new_solid_geometry = new_solid_geometry.difference(poly) - - try: - __ = iter(new_solid_geometry) - except TypeError: - new_solid_geometry = [new_solid_geometry] + new_solid_geometry = flatten_shapely_geometry(new_solid_geometry) new_options = {} for opt in grb_obj.options: @@ -175,8 +167,8 @@ class ToolInvertGerber(AppTool): if 0 not in new_apertures: new_apertures[0] = { - 'type': 'REG', - 'size': 0.0, + 'type': 'REG', + 'size': 0.0, 'geometry': [] } diff --git a/appPlugins/ToolIsolation.py b/appPlugins/ToolIsolation.py index 4df21fec..17fafb59 100644 --- a/appPlugins/ToolIsolation.py +++ b/appPlugins/ToolIsolation.py @@ -1026,10 +1026,7 @@ class ToolIsolation(AppTool, Gerber): total_geo = MultiPolygon(total_geo) total_geo = total_geo.buffer(0) - try: - __ = iter(total_geo) - geo_len = len(total_geo) - except TypeError: + if isinstance(total_geo, Polygon): msg = ('[ERROR_NOTCL] %s' % _("The Gerber object has one Polygon as geometry.\n" "There are no distances between geometry elements to be found.")) @@ -1165,11 +1162,10 @@ class ToolIsolation(AppTool, Gerber): total_geo = MultiPolygon(total_geo) total_geo = total_geo.buffer(0) - try: - __ = iter(total_geo) - geo_len = len(total_geo) + if isinstance(total_geo, MultiPolygon): + geo_len = len(total_geo.geoms) geo_len = (geo_len * (geo_len - 1)) / 2 - except TypeError: + elif isinstance(total_geo, Polygon): msg = _("The Gerber object has one Polygon as geometry.\n" "There are no distances between geometry elements to be found.") app_obj.inform.emit('[ERROR_NOTCL] %s' % msg) diff --git a/appPlugins/ToolNCC.py b/appPlugins/ToolNCC.py index d11512d9..6dca2c30 100644 --- a/appPlugins/ToolNCC.py +++ b/appPlugins/ToolNCC.py @@ -13,7 +13,7 @@ from appGUI.GUIElements import FCCheckBox, FCDoubleSpinner, RadioSet, FCTable, F VerticalScrollArea, FCGridLayout, FCFrame from appParsers.ParseGerber import Gerber -from camlib import grace +from camlib import grace, flatten_shapely_geometry from copy import deepcopy @@ -1010,10 +1010,7 @@ class NonCopperClear(AppTool, Gerber): total_geo = MultiPolygon(total_geo) total_geo = total_geo.buffer(0) - try: - __ = iter(total_geo) - geo_len = len(total_geo) - except TypeError: + if isinstance(total_geo, Polygon): msg = ('[ERROR_NOTCL] %s' % _("The Gerber object has one Polygon as geometry.\n" "There are no distances between geometry elements to be found.")) @@ -1153,11 +1150,10 @@ class NonCopperClear(AppTool, Gerber): total_geo = MultiPolygon(total_geo) total_geo = total_geo.buffer(0) - try: - __ = iter(total_geo) - geo_len = len(total_geo) + if isinstance(total_geo, MultiPolygon): + geo_len = len(total_geo.geoms) geo_len = (geo_len * (geo_len - 1)) / 2 - except TypeError: + elif isinstance(total_geo, Polygon): app_obj.inform.emit('[ERROR_NOTCL] %s' % _("The Gerber object has one Polygon as geometry.\n" "There are no distances between geometry elements to be found.")) @@ -1949,26 +1945,19 @@ class NonCopperClear(AppTool, Gerber): return None elif ncc_select == 1: # _("Area Selection") env_obj = unary_union(self.sel_rect) - try: - __ = iter(env_obj) - except Exception: - env_obj = [env_obj] + env_obj = flatten_shapely_geometry(env_obj) elif ncc_select == 2: # _("Reference Object") if box_obj is None: return None, None box_geo = box_obj.solid_geometry if box_kind == 'geometry': - try: - __ = iter(box_geo) - env_obj = box_geo - except Exception: - env_obj = [box_geo] - + box_geo = flatten_shapely_geometry(box_geo) elif box_kind == 'gerber': box_geo = unary_union(box_obj.solid_geometry).convex_hull ncc_geo = unary_union(ncc_obj.solid_geometry).convex_hull env_obj = ncc_geo.intersection(box_geo) + env_obj = flatten_shapely_geometry(env_obj) else: self.app.inform.emit('[ERROR_NOTCL] %s' % _("The reference object type is not supported.")) return 'fail' @@ -3001,11 +2990,7 @@ class NonCopperClear(AppTool, Gerber): elif ncc_select == 1: # area geo_n = unary_union(self.sel_rect) - try: - __ = iter(geo_n) - except Exception as e: - self.app.log.error("NonCopperClear.clear_copper() 'area' --> %s" % str(e)) - geo_n = [geo_n] + geo_n = flatten_shapely_geometry(geo_n) geo_buff_list = [] for poly in geo_n: @@ -3019,13 +3004,8 @@ class NonCopperClear(AppTool, Gerber): elif ncc_select == 2: # Reference Object geo_n = ncc_sel_obj.solid_geometry if ncc_sel_obj.kind == 'geometry': - try: - __ = iter(geo_n) - except Exception as e: - self.app.log.error("NonCopperClear.clear_copper() 'Reference Object' --> %s" % str(e)) - geo_n = [geo_n] - geo_buff_list = [] + geo_n = flatten_shapely_geometry(geo_n) for poly in geo_n: if self.app.abort_flag: # graceful abort requested by the user diff --git a/appPlugins/ToolOptimal.py b/appPlugins/ToolOptimal.py index 39425a97..4e8864de 100644 --- a/appPlugins/ToolOptimal.py +++ b/appPlugins/ToolOptimal.py @@ -208,11 +208,10 @@ class ToolOptimal(AppTool): total_geo = MultiPolygon(total_geo) total_geo = total_geo.buffer(0) - try: - __ = iter(total_geo) - geo_len = len(total_geo) + if isinstance(total_geo, MultiPolygon): + geo_len = len(total_geo.geoms) geo_len = (geo_len * (geo_len - 1)) / 2 - except TypeError: + else: app_obj.inform.emit('[ERROR_NOTCL] %s' % _("The Gerber object has one Polygon as geometry.\n" "There are no distances between geometry elements to be found.")) diff --git a/camlib.py b/camlib.py index 021eb92b..ec303e19 100644 --- a/camlib.py +++ b/camlib.py @@ -22,7 +22,7 @@ from rtree import index as rtindex from lxml import etree as ET # See: http://toblerity.org/shapely/manual.html -from shapely.geometry import Polygon, Point, LinearRing +from shapely.geometry import Polygon, Point, LinearRing, MultiPoint from shapely.geometry import box as shply_box from shapely.ops import unary_union, substring, linemerge @@ -2642,11 +2642,7 @@ class Geometry(object): self.el_count = 0 res = buffer_geom(self.tools[tool]['solid_geometry']) - try: - __ = iter(res) - self.tools[tool]['solid_geometry'] = res - except TypeError: - self.tools[tool]['solid_geometry'] = [res] + self.tools[tool]['solid_geometry'] = flatten_shapely_geometry(res) # variables to display the percentage of work done self.geo_len = 0 @@ -8095,6 +8091,28 @@ class CNCjob(Geometry): self.app.proc_container.new_text = '' +def flatten_shapely_geometry(geometry): + """ + + :param geometry: + :type geometry: + :return: + :rtype: + """ + flat_list = [] + try: + if isinstance(geometry, (MultiLineString, MultiPolygon, MultiPoint)): + for geo in geometry.geoms: + flat_list.append(geo) + elif isinstance(geometry, list): + for geo_el in geometry: + flat_list += flatten_shapely_geometry(geo_el) + except TypeError: + flat_list.append(geometry) + + return flat_list + + def get_bounds(geometry_list): """ Will return limit values for a list of geometries diff --git a/descartes/patch.py b/descartes/patch.py index 556798a2..3762079e 100644 --- a/descartes/patch.py +++ b/descartes/patch.py @@ -1,5 +1,5 @@ """Paths and patches""" - +import numpy as np from matplotlib.patches import PathPatch from matplotlib.path import Path from numpy import asarray, concatenate, ones @@ -45,7 +45,10 @@ def PolygonPath(polygon): vals[0] = Path.MOVETO return vals - vertices = concatenate([asarray(this.exterior)] + [asarray(r) for r in this.interiors]) + # vertices = concatenate([asarray(this.exterior)] + [asarray(r) for r in this.interiors]) + # codes = concatenate([coding(this.exterior)] + [coding(r) for r in this.interiors]) + + vertices = concatenate([np.array(this.exterior.coords)] + [np.array(r.coords) for r in this.interiors]) codes = concatenate([coding(this.exterior)] + [coding(r) for r in this.interiors]) return Path(vertices, codes)