diff --git a/FlatCAMApp.py b/FlatCAMApp.py index 37e2c233..2542c50d 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -363,6 +363,8 @@ class App(QtCore.QObject): "excellon_optimization_type": self.excellon_defaults_form.excellon_group.excellon_optimization_radio, "excellon_gcode_type": self.excellon_defaults_form.excellon_group.excellon_gcode_type_radio, "geometry_plot": self.geometry_defaults_form.geometry_group.plot_cb, + "geometry_segx": self.geometry_defaults_form.geometry_group.segx_entry, + "geometry_segy": self.geometry_defaults_form.geometry_group.segy_entry, "geometry_cutz": self.geometry_defaults_form.geometry_group.cutz_entry, "geometry_travelz": self.geometry_defaults_form.geometry_group.travelz_entry, "geometry_feedrate": self.geometry_defaults_form.geometry_group.cncfeedrate_entry, @@ -486,6 +488,8 @@ class App(QtCore.QObject): "excellon_gcode_type": "drills", "geometry_plot": True, + "geometry_segx": 0.0, + "geometry_segy": 0.0, "geometry_cutz": -0.002, "geometry_travelz": 0.1, "geometry_toolchange": False, @@ -628,6 +632,8 @@ class App(QtCore.QObject): "excellon_units": self.excellon_options_form.excellon_group.excellon_units_radio, "excellon_optimization_type": self.excellon_options_form.excellon_group.excellon_optimization_radio, "geometry_plot": self.geometry_options_form.geometry_group.plot_cb, + "geometry_segx": self.geometry_options_form.geometry_group.segx_entry, + "geometry_segy": self.geometry_options_form.geometry_group.segy_entry, "geometry_cutz": self.geometry_options_form.geometry_group.cutz_entry, "geometry_travelz": self.geometry_options_form.geometry_group.travelz_entry, "geometry_feedrate": self.geometry_options_form.geometry_group.cncfeedrate_entry, @@ -709,6 +715,8 @@ class App(QtCore.QObject): "excellon_endz": 2.0, "excellon_zeros": "L", "geometry_plot": True, + "geometry_segx": 0.0, + "geometry_segy": 0.0, "geometry_cutz": -0.002, "geometry_travelz": 0.1, "geometry_feedrate": 3.0, diff --git a/FlatCAMGUI.py b/FlatCAMGUI.py index d0b19797..40263d8e 100644 --- a/FlatCAMGUI.py +++ b/FlatCAMGUI.py @@ -2260,6 +2260,28 @@ class GeometryPrefGroupUI(OptionsGroupUI): self.pp_geometry_name_cb.setFocusPolicy(Qt.StrongFocus) grid2.addWidget(self.pp_geometry_name_cb, 16, 1) + # Size of trace segment on X axis + segx_label = QtWidgets.QLabel("Seg. X size:") + segx_label.setToolTip( + "The size of the trace segment on the X axis.\n" + "Useful for auto-leveling.\n" + "A value of 0 means no segmentation on the X axis." + ) + grid2.addWidget(segx_label, 17, 0) + self.segx_entry = FCEntry() + grid2.addWidget(self.segx_entry, 17, 1) + + # Size of trace segment on Y axis + segy_label = QtWidgets.QLabel("Seg. Y size:") + segy_label.setToolTip( + "The size of the trace segment on the Y axis.\n" + "Useful for auto-leveling.\n" + "A value of 0 means no segmentation on the Y axis." + ) + grid2.addWidget(segy_label, 18, 0) + self.segy_entry = FCEntry() + grid2.addWidget(self.segy_entry, 18, 1) + # ------------------------------ ## Paint area # ------------------------------ diff --git a/FlatCAMObj.py b/FlatCAMObj.py index 62bcd6c5..761f03a8 100644 --- a/FlatCAMObj.py +++ b/FlatCAMObj.py @@ -2962,7 +2962,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): else: self.app.inform.emit("[error_notcl] Failed. No tool selected in the tool table ...") - def mtool_gen_cncjob(self, use_thread=True): + def mtool_gen_cncjob(self, segx=None, segy=None, use_thread=True): """ Creates a multi-tool CNCJob out of this Geometry object. The actual work is done by the target FlatCAMCNCjob object's @@ -2986,6 +2986,9 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): # use the name of the first tool selected in self.geo_tools_table which has the diameter passed as tool_dia outname = "%s_%s" % (self.options["name"], 'cnc') + segx = segx if segx is not None else float(self.app.defaults['geometry_segx']) + segy = segy if segy is not None else float(self.app.defaults['geometry_segy']) + # Object initialization function for app.new_object() # RUNNING ON SEPARATE THREAD! def job_init_single_geometry(job_obj, app_obj): @@ -3004,6 +3007,8 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): # job_obj.create_geometry() job_obj.options['Tools_in_use'] = self.get_selected_tools_table_items() + job_obj.segx = segx + job_obj.segy = segy for tooluid_key in self.sel_tools: tool_cnt += 1 @@ -3345,6 +3350,8 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): toolchange=None, toolchangez=None, toolchangexy=None, extracut=None, startz=None, endz=None, ppname_g=None, + segx=None, + segy=None, use_thread=True): """ Only used for TCL Command. @@ -3376,6 +3383,9 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): multidepth = multidepth if multidepth is not None else self.options["multidepth"] depthperpass = depthperpass if depthperpass is not None else self.options["depthperpass"] + segx = segx if segx is not None else float(self.app.defaults['geometry_segx']) + segy = segy if segy is not None else float(self.app.defaults['geometry_segy']) + extracut = extracut if extracut is not None else self.options["extracut"] startz = startz if startz is not None else self.options["startz"] endz = endz if endz is not None else self.options["endz"] @@ -3410,6 +3420,9 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): job_obj.options['type'] = 'Geometry' job_obj.options['tool_dia'] = tooldia + job_obj.segx = segx + job_obj.segy = segy + # TODO: The tolerance should not be hard coded. Just for testing. job_obj.generate_from_geometry_2(self, tooldia=tooldia, offset=offset, tolerance=0.0005, z_cut=z_cut, z_move=z_move, diff --git a/README.md b/README.md index 9b2e2bb4..176bedfc 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ CAD program, and create G-Code for Isolation routing. - added more key shortcuts into the application; they are now displayed in the GUI menu's - reorganized the Edit -> Preferences -> Global - redesigned the messagebox that is showed when quiting ot creating a New Project: now it has an option ('Cancel') to abort the process returning to the app - +- added options for trace segmentation that can be useful for auto-levelling (code snippet from Lei Zheng from a rejected pull request on FlatCAM https://bitbucket.org/realthunder/ ) 26.01.2019 diff --git a/camlib.py b/camlib.py index 6756371f..9d65a705 100644 --- a/camlib.py +++ b/camlib.py @@ -4332,6 +4332,8 @@ class CNCjob(Geometry): spindlespeed=None, dwell=True, dwelltime=1000, toolchangez=0.787402, endz=2.0, + segx=None, + segy=None, steps_per_circle=None): # Used when parsing G-code arcs @@ -4376,6 +4378,9 @@ class CNCjob(Geometry): self.dwell = dwell self.dwelltime = dwelltime + self.segx = float(segx) if segx is not None else 0.0 + self.segy = float(segy) if segy is not None else 0.0 + self.input_geometry_bounds = None # Attributes to be included in serialization @@ -5456,6 +5461,61 @@ class CNCjob(Geometry): self.solid_geometry = cascaded_union([geo['geom'] for geo in self.gcode_parsed]) return self.solid_geometry + # code snippet added by Lei Zheng in a rejected pull request on FlatCAM https://bitbucket.org/realthunder/ + def segment(self, coords): + """ + break long linear lines to make it more auto level friendly + """ + + if len(coords) < 2 or self.segx <= 0 and self.segy <= 0: + return list(coords) + + path = [coords[0]] + + # break the line in either x or y dimension only + def linebreak_single(line, dim, dmax): + if dmax <= 0: + return None + + if line[1][dim] > line[0][dim]: + sign = 1.0 + d = line[1][dim] - line[0][dim] + else: + sign = -1.0 + d = line[0][dim] - line[1][dim] + if d > dmax: + # make sure we don't make any new lines too short + if d > dmax * 2: + dd = dmax + else: + dd = d / 2 + other = dim ^ 1 + return (line[0][dim] + dd * sign, line[0][other] + \ + dd * (line[1][other] - line[0][other]) / d) + return None + + # recursively breaks down a given line until it is within the + # required step size + def linebreak(line): + pt_new = linebreak_single(line, 0, self.segx) + if pt_new is None: + pt_new2 = linebreak_single(line, 1, self.segy) + else: + pt_new2 = linebreak_single((line[0], pt_new), 1, self.segy) + if pt_new2 is not None: + pt_new = pt_new2[::-1] + + if pt_new is None: + path.append(line[1]) + else: + path.append(pt_new) + linebreak((pt_new, line[1])) + + for pt in coords[1:]: + linebreak((path[-1], pt)) + + return path + def linear2gcode(self, linear, tolerance=0, down=True, up=True, z_cut=None, z_move=None, zdownrate=None, feedrate=None, feedrate_z=None, feedrate_rapid=None, cont=False): @@ -5500,7 +5560,9 @@ class CNCjob(Geometry): gcode = "" - path = list(target_linear.coords) + # path = list(target_linear.coords) + path = self.segment(target_linear.coords) + p = self.pp_geometry # Move fast to 1st point