From 82ab0d83d65f149dea2ab2f863487ea5241e8595 Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Mon, 10 Feb 2020 12:27:49 +0200 Subject: [PATCH] - started a new way to clear the Gerber polygons based on the 'follow' lines --- README.md | 1 + camlib.py | 141 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 141 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1466cc4b..bc8f6675 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ CAD program, and create G-Code for Isolation routing. - optimized the Paint and NCC Tools. When the Lines type of painting/clearing is used, the lines will try to arrange themselves on the direction that the lines length clearing the polygon are bigger - solved bug that made drilling with Marlin preprocessor very slow - applied the fix for above bug to the TclCommand Drillcncjob too +- started a new way to clear the Gerber polygons based on the 'follow' lines 8.02.2020 diff --git a/camlib.py b/camlib.py index 165635b8..2fb8180e 100644 --- a/camlib.py +++ b/camlib.py @@ -1446,7 +1446,7 @@ class Geometry(object): return None # decide the direction of the lines - if abs(left - right) >= abs(top -bot): + if abs(left - right) >= abs(top - bot): # First line try: y = top - tooldia / 1.99999999 @@ -1562,6 +1562,145 @@ class Geometry(object): return geoms + def fill_with_lines(self, line, aperture_size, tooldia, steps_per_circle, overlap=0.15, connect=True, contour=True, + prog_plot=False): + """ + Creates geometry of lines inside a polygon for a tool to cover + the whole area. + + This algorithm draws parallel lines inside the polygon. + + :param line: The target line that create painted polygon. + :type line: shapely.geometry.LineString or shapely.geometry.MultiLineString + :param tooldia: Tool diameter. + :param steps_per_circle: how many linear segments to use to approximate a circle + :param overlap: Tool path overlap percentage. + :param connect: Connect lines to avoid tool lifts. + :param contour: Paint around the edges. + :param prog_plot: boolean; if to use the progressive plotting + :return: + """ + + # log.debug("camlib.fill_with_lines()") + if not isinstance(line, LineString) or not isinstance(line, MultiLineString): + log.debug("camlib.Geometry.fill_with_lines() --> Not a LineString/MultiLineString but %s" % str(type(line))) + return None + + # ## The toolpaths + # Index first and last points in paths + def get_pts(o): + return [o.coords[0], o.coords[-1]] + + geoms = FlatCAMRTreeStorage() + geoms.get_points = get_pts + + lines_trimmed = [] + + polygon = line.buffer(aperture_size / 1.99999999999999999, int(steps_per_circle)) + + try: + margin_poly = polygon.buffer(-tooldia / 1.99999999, int(steps_per_circle)) + except Exception: + log.debug("camlib.Geometry.fill_with_lines() --> Could not buffer the Polygon, tool diameter too high") + return None + + # First line + try: + delta = 0 + while delta < aperture_size / 2: + if self.app.abort_flag: + # graceful abort requested by the user + raise FlatCAMApp.GracefulException + + # provide the app with a way to process the GUI events when in a blocking loop + QtWidgets.QApplication.processEvents() + + line = line.parallel_offset(distance=delta, side='left', resolution=int(steps_per_circle)) + line = line.intersection(margin_poly) + lines_trimmed.append(line) + + line = line.parallel_offset(distance=delta, side='right', resolution=int(steps_per_circle)) + line = line.intersection(margin_poly) + lines_trimmed.append(line) + + delta += tooldia * (1 - overlap) + if prog_plot: + self.plot_temp_shapes(line) + self.temp_shapes.redraw() + + # Last line + delta = aperture_size / 2 + + line = line.parallel_offset(distance=delta, side='left', resolution=int(steps_per_circle)) + line = line.intersection(margin_poly) + + for ll in line: + lines_trimmed.append(ll) + if prog_plot: + self.plot_temp_shapes(line) + + line = line.parallel_offset(distance=delta, side='left', resolution=int(steps_per_circle)) + line = line.intersection(margin_poly) + + for ll in line: + lines_trimmed.append(ll) + if prog_plot: + self.plot_temp_shapes(line) + except Exception as e: + log.debug('camlib.Geometry.fill_with_lines() Processing poly --> %s' % str(e)) + return None + + if prog_plot: + self.temp_shapes.redraw() + + lines_trimmed = unary_union(lines_trimmed) + + # Add lines to storage + try: + for line in lines_trimmed: + if isinstance(line, LineString) or isinstance(line, LinearRing): + geoms.insert(line) + else: + log.debug("camlib.Geometry.fill_with_lines(). Not a line: %s" % str(type(line))) + except TypeError: + # in case lines_trimmed are not iterable (Linestring, LinearRing) + geoms.insert(lines_trimmed) + + # Add margin (contour) to storage + if contour: + try: + for poly in margin_poly: + if isinstance(poly, Polygon) and not poly.is_empty: + geoms.insert(poly.exterior) + if prog_plot: + self.plot_temp_shapes(poly.exterior) + for ints in poly.interiors: + geoms.insert(ints) + if prog_plot: + self.plot_temp_shapes(ints) + except TypeError: + if isinstance(margin_poly, Polygon) and not margin_poly.is_empty: + marg_ext = margin_poly.exterior + geoms.insert(marg_ext) + if prog_plot: + self.plot_temp_shapes(margin_poly.exterior) + for ints in margin_poly.interiors: + geoms.insert(ints) + if prog_plot: + self.plot_temp_shapes(ints) + + if prog_plot: + self.temp_shapes.redraw() + + # Optimization: Reduce lifts + if connect: + # log.debug("Reducing tool lifts...") + geoms_conn = Geometry.paint_connect(geoms, polygon, tooldia, steps_per_circle) + if geoms_conn: + return geoms_conn + + return geoms + def scale(self, xfactor, yfactor, point=None): """ Scales all of the object's geometry by a given factor. Override