diff --git a/FlatCAMApp.py b/FlatCAMApp.py index af1516b4..56f00760 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -271,6 +271,8 @@ class App(QtCore.QObject): "geometry_paintoverlap": self.defaults_form.geometry_group.paintoverlap_entry, "geometry_paintmargin": self.defaults_form.geometry_group.paintmargin_entry, "geometry_selectmethod": self.defaults_form.geometry_group.selectmethod_combo, + "geometry_pathconnect": self.defaults_form.geometry_group.pathconnect_cb, + "geometry_paintcontour": self.defaults_form.geometry_group.contour_cb, "cncjob_plot": self.defaults_form.cncjob_group.plot_cb, "cncjob_tooldia": self.defaults_form.cncjob_group.tooldia_entry, "cncjob_prepend": self.defaults_form.cncjob_group.prepend_text, @@ -317,6 +319,8 @@ class App(QtCore.QObject): "geometry_paintoverlap": 0.15, "geometry_paintmargin": 0.0, "geometry_selectmethod": "single", + "geometry_pathconnect": True, + "geometry_paintcontour": True, "cncjob_plot": True, "cncjob_tooldia": 0.016, "cncjob_prepend": "", diff --git a/FlatCAMGUI.py b/FlatCAMGUI.py index d06b09ed..0f5c7458 100644 --- a/FlatCAMGUI.py +++ b/FlatCAMGUI.py @@ -785,19 +785,29 @@ class GeometryOptionsGroupUI(OptionsGroupUI): self.pathconnect_cb = FCCheckBox() grid2.addWidget(self.pathconnect_cb, 4, 1) + # Paint contour + contourlabel = QtGui.QLabel("Contour:") + contourlabel.setToolTip( + "Cut around the perimeter of the polygon\n" + "to trim rough edges." + ) + grid2.addWidget(contourlabel, 5, 0) + self.contour_cb = FCCheckBox() + grid2.addWidget(self.contour_cb, 5, 1) + # Polygon selection selectlabel = QtGui.QLabel('Selection:') selectlabel.setToolTip( "How to select the polygons to paint." ) - grid2.addWidget(selectlabel, 5, 0) + grid2.addWidget(selectlabel, 6, 0) # grid3 = QtGui.QGridLayout() self.selectmethod_combo = RadioSet([ {"label": "Single", "value": "single"}, {"label": "All", "value": "all"}, # {"label": "Rectangle", "value": "rectangle"} ]) - grid2.addWidget(self.selectmethod_combo, 5, 1) + grid2.addWidget(self.selectmethod_combo, 6, 1) class CNCJobOptionsGroupUI(OptionsGroupUI): diff --git a/FlatCAMObj.py b/FlatCAMObj.py index 674b529d..028b65ca 100644 --- a/FlatCAMObj.py +++ b/FlatCAMObj.py @@ -1211,6 +1211,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): "paintmargin": 0.01, "paintmethod": "standard", "pathconnect": True, + "paintcontour": True, "multidepth": False, "depthperpass": 0.002, "selectmethod": "single" @@ -1244,6 +1245,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): "paintmargin": self.ui.paintmargin_entry, "paintmethod": self.ui.paintmethod_combo, "pathconnect": self.ui.pathconnect_cb, + "paintcontour": self.ui.paintcontour_cb, "multidepth": self.ui.mpass_cb, "depthperpass": self.ui.maxdepth_entry, "selectmethod": self.ui.selectmethod_combo @@ -1262,7 +1264,8 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): if self.options["selectmethod"] == "all": self.paint_poly_all(tooldia, overlap, - connect=self.option["pathconnect"]) + connect=self.options["pathconnect"], + contour=self.options["paintcontour"]) return if self.options["selectmethod"] == "single": @@ -1274,12 +1277,13 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): self.app.plotcanvas.mpl_disconnect(subscription) point = [event.xdata, event.ydata] self.paint_poly_single_click(point, tooldia, overlap, - connect=self.options["pathconnect"]) + connect=self.options["pathconnect"], + contour=self.options["paintcontour"]) subscription = self.app.plotcanvas.mpl_connect('button_press_event', doit) def paint_poly_single_click(self, inside_pt, tooldia, overlap, - outname=None, connect=True): + outname=None, connect=True, contour=True): """ Paints a polygon selected by clicking on its interior. @@ -1290,6 +1294,8 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): :param tooldia: Diameter of the painting tool :param overlap: Overlap of the tool between passes. :param outname: Name of the resulting Geometry Object. + :param connect: Connect lines to avoid tool lifts. + :param contour: Paint around the edges. :return: None """ @@ -1315,16 +1321,19 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): if self.options["paintmethod"] == "seed": # Type(cp) == FlatCAMRTreeStorage | None cp = self.clear_polygon2(poly.buffer(-self.options["paintmargin"]), - tooldia, overlap=overlap, connect=connect) + tooldia, overlap=overlap, connect=connect, + contour=contour) elif self.options["paintmethod"] == "lines": # Type(cp) == FlatCAMRTreeStorage | None cp = self.clear_polygon3(poly.buffer(-self.options["paintmargin"]), - tooldia, overlap=overlap, connect=connect) + tooldia, overlap=overlap, connect=connect, + contour=contour) else: # Type(cp) == FlatCAMRTreeStorage | None cp = self.clear_polygon(poly.buffer(-self.options["paintmargin"]), - tooldia, overlap=overlap, connect=connect) + tooldia, overlap=overlap, connect=connect, + contour=contour) if cp is not None: geo_obj.solid_geometry = list(cp.get_objects()) @@ -1354,13 +1363,16 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): # Background self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]}) - def paint_poly_all(self, tooldia, overlap, outname=None): + def paint_poly_all(self, tooldia, overlap, outname=None, + connect=True, contour=True): """ Paints all polygons in this object. :param tooldia: :param overlap: :param outname: + :param connect: Connect lines to avoid tool lifts. + :param contour: Paint around the edges. :return: """ @@ -1394,17 +1406,20 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): if self.options["paintmethod"] == "seed": # Type(cp) == FlatCAMRTreeStorage | None cp = self.clear_polygon2(poly.buffer(-self.options["paintmargin"]), - tooldia, overlap=overlap) + tooldia, overlap=overlap, contour=contour, + connect=connect) elif self.options["paintmethod"] == "lines": # Type(cp) == FlatCAMRTreeStorage | None cp = self.clear_polygon3(poly.buffer(-self.options["paintmargin"]), - tooldia, overlap=overlap) + tooldia, overlap=overlap, contour=contour, + connect=connect) else: # Type(cp) == FlatCAMRTreeStorage | None cp = self.clear_polygon(poly.buffer(-self.options["paintmargin"]), - tooldia, overlap=overlap) + tooldia, overlap=overlap, contour=contour, + connect=connect) if cp is not None: geo_obj.solid_geometry += list(cp.get_objects()) diff --git a/ObjectUI.py b/ObjectUI.py index 5d1e765c..2211126b 100644 --- a/ObjectUI.py +++ b/ObjectUI.py @@ -409,19 +409,28 @@ class GeometryObjectUI(ObjectUI): self.pathconnect_cb = FCCheckBox() grid2.addWidget(self.pathconnect_cb, 4, 1) + contourlabel = QtGui.QLabel("Contour:") + contourlabel.setToolTip( + "Cut around the perimeter of the polygon\n" + "to trim rough edges." + ) + grid2.addWidget(contourlabel, 5, 0) + self.paintcontour_cb = FCCheckBox() + grid2.addWidget(self.paintcontour_cb, 5, 1) + # Polygon selection selectlabel = QtGui.QLabel('Selection:') selectlabel.setToolTip( "How to select the polygons to paint." ) - grid2.addWidget(selectlabel, 5, 0) + grid2.addWidget(selectlabel, 6, 0) #grid3 = QtGui.QGridLayout() self.selectmethod_combo = RadioSet([ {"label": "Single", "value": "single"}, {"label": "All", "value": "all"}, #{"label": "Rectangle", "value": "rectangle"} ]) - grid2.addWidget(self.selectmethod_combo, 5, 1) + grid2.addWidget(self.selectmethod_combo, 6, 1) # GO Button self.generate_paint_button = QtGui.QPushButton('Generate') diff --git a/camlib.py b/camlib.py index 7720c536..c00e69ab 100644 --- a/camlib.py +++ b/camlib.py @@ -475,7 +475,8 @@ class Geometry(object): return boundary.difference(self.solid_geometry) @staticmethod - def clear_polygon(polygon, tooldia, overlap=0.15, connect=True): + def clear_polygon(polygon, tooldia, overlap=0.15, connect=True, + contour=True): """ Creates geometry inside a polygon for a tool to cover the whole area. @@ -486,6 +487,10 @@ class Geometry(object): :param polygon: Polygon to clear. :param tooldia: Diameter of the tool. :param overlap: Overlap of toolpasses. + :param connect: Draw lines between disjoint segments to + minimize tool lifts. + :param contour: Paint around the edges. Inconsequential in + this painting method. :return: """ @@ -550,7 +555,8 @@ class Geometry(object): return geoms @staticmethod - def clear_polygon2(polygon, tooldia, seedpoint=None, overlap=0.15, connect=True): + def clear_polygon2(polygon, tooldia, seedpoint=None, overlap=0.15, + connect=True, contour=True): """ Creates geometry inside a polygon for a tool to cover the whole area. @@ -564,6 +570,8 @@ class Geometry(object): :param tooldia: Diameter of the tool :param seedpoint: Shapely.geometry.Point or None :param overlap: Tool fraction overlap bewteen passes + :param connect: Connect disjoint segment to minumize tool lifts + :param contour: Cut countour inside the polygon. :return: List of toolpaths covering polygon. :rtype: FlatCAMRTreeStorage | None """ @@ -608,15 +616,16 @@ class Geometry(object): radius += tooldia * (1 - overlap) - # Clean inside edges of the original polygon - outer_edges = [x.exterior for x in autolist(polygon.buffer(-tooldia / 2))] - inner_edges = [] - for x in autolist(polygon.buffer(-tooldia / 2)): # Over resulting polygons - for y in x.interiors: # Over interiors of each polygon - inner_edges.append(y) - #geoms += outer_edges + inner_edges - for g in outer_edges + inner_edges: - geoms.insert(g) + # Clean inside edges (contours) of the original polygon + if contour: + outer_edges = [x.exterior for x in autolist(polygon.buffer(-tooldia / 2))] + inner_edges = [] + for x in autolist(polygon.buffer(-tooldia / 2)): # Over resulting polygons + for y in x.interiors: # Over interiors of each polygon + inner_edges.append(y) + #geoms += outer_edges + inner_edges + for g in outer_edges + inner_edges: + geoms.insert(g) # Optimization connect touching paths # log.debug("Connecting paths...") @@ -630,7 +639,8 @@ class Geometry(object): return geoms @staticmethod - def clear_polygon3(polygon, tooldia, overlap=0.15, connect=True): + def clear_polygon3(polygon, tooldia, overlap=0.15, connect=True, + contour=True): """ Creates geometry inside a polygon for a tool to cover the whole area. @@ -642,6 +652,7 @@ class Geometry(object): :param tooldia: Tool diameter. :param overlap: Tool path overlap percentage. :param connect: Connect lines to avoid tool lifts. + :param contour: Paint around the edges. :return: """ @@ -683,10 +694,11 @@ class Geometry(object): for line in lines_trimmed: geoms.insert(line) - # Add margin to storage - # geoms.insert(margin_poly.exterior) - # for ints in margin_poly.interiors: - # geoms.insert(ints) + # Add margin (contour) to storage + if contour: + geoms.insert(margin_poly.exterior) + for ints in margin_poly.interiors: + geoms.insert(ints) # Optimization: Reduce lifts if connect: