diff --git a/FlatCAMObj.py b/FlatCAMObj.py index caeb61d7..f339a399 100644 --- a/FlatCAMObj.py +++ b/FlatCAMObj.py @@ -1026,7 +1026,9 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): "painttooldia": 0.0625, "paintoverlap": 0.15, "paintmargin": 0.01, - "paintmethod": "standard" + "paintmethod": "standard", + "multidepth": False, + "depthperpass": -0.002 }) # Attributes to be included in serialization @@ -1055,7 +1057,9 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): "painttooldia": self.ui.painttooldia_entry, "paintoverlap": self.ui.paintoverlap_entry, "paintmargin": self.ui.paintmargin_entry, - "paintmethod": self.ui.paintmethod_combo + "paintmethod": self.ui.paintmethod_combo, + "multidepth": self.ui.mpass_cb, + "depthperpass": self.ui.maxdepth_entry }) self.ui.plot_cb.stateChanged.connect(self.on_plot_cb_click) @@ -1137,11 +1141,19 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): self.read_form() self.generatecncjob() - def generatecncjob(self, z_cut=None, z_move=None, - feedrate=None, tooldia=None, outname=None, - spindlespeed=None): + def generatecncjob(self, + z_cut=None, + z_move=None, + feedrate=None, + tooldia=None, + outname=None, + spindlespeed=None, + multidepth=None, + depthperpass=None): """ - Creates a CNCJob out of this Geometry object. + Creates a CNCJob out of this Geometry object. The actual + work is done by the target FlatCAMCNCjob object's + `generate_from_geometry_2()` method. :param z_cut: Cut depth (negative) :param z_move: Hight of the tool when travelling (not cutting) @@ -1157,6 +1169,8 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): z_move = z_move if z_move is not None else self.options["travelz"] feedrate = feedrate if feedrate is not None else self.options["feedrate"] tooldia = tooldia if tooldia is not None else self.options["cnctooldia"] + multidepth = multidepth if multidepth is not None else self.options["multidepth"] + depthperpass = depthperpass if depthperpass is not None else self.options["depthperpass"] # To allow default value to be "" (optional in gui) and translate to None # if not isinstance(spindlespeed, int): @@ -1186,7 +1200,10 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): job_obj.spindlespeed = spindlespeed app_obj.progress.emit(40) # TODO: The tolerance should not be hard coded. Just for testing. - job_obj.generate_from_geometry_2(self, tolerance=0.0005) + job_obj.generate_from_geometry_2(self, + multidepth=multidepth, + depthpercut=depthperpass, + tolerance=0.0005) app_obj.progress.emit(50) job_obj.gcode_parse() diff --git a/GUIElements.py b/GUIElements.py index 79171a96..831379bc 100644 --- a/GUIElements.py +++ b/GUIElements.py @@ -246,9 +246,16 @@ class VerticalScrollArea(QtGui.QScrollArea): return QtGui.QWidget.eventFilter(self, source, event) -class OptionalInputSection(): +class OptionalInputSection: def __init__(self, cb, optinputs): + """ + Associates the a checkbox with a set of inputs. + + :param cb: Checkbox that enables the optional inputs. + :param optinputs: List of widgets that are optional. + :return: + """ assert isinstance(cb, FCCheckBox), \ "Expected an FCCheckBox, got %s" % type(cb) diff --git a/ObjectUI.py b/ObjectUI.py index ebb4ea17..f7ce3d97 100644 --- a/ObjectUI.py +++ b/ObjectUI.py @@ -222,7 +222,9 @@ class GeometryObjectUI(ObjectUI): ) self.custom_box.addWidget(self.plot_cb) - ## Create CNC Job + #----------------------------------- + # Create CNC Job + #----------------------------------- self.cncjob_label = QtGui.QLabel('Create CNC Job:') self.cncjob_label.setToolTip( "Create a CNC Job object\n" @@ -283,15 +285,38 @@ class GeometryObjectUI(ObjectUI): self.cncspindlespeed_entry = IntEntry(allow_empty=True) grid1.addWidget(self.cncspindlespeed_entry, 4, 1) + # Multi-pass + mpasslabel = QtGui.QLabel('Multi-Depth:') + mpasslabel.setToolTip( + "Use multiple passes to limit\n" + "the cut depth in each pass. Will\n" + "cut multiple times until Cut Z is\n" + "reached." + ) + grid1.addWidget(mpasslabel, 5, 0) + self.mpass_cb = FCCheckBox() + grid1.addWidget(self.mpass_cb, 5, 1) + + maxdepthlabel = QtGui.QLabel('Depth/pass:') + maxdepthlabel.setToolTip( + "Depth of each pass." + ) + grid1.addWidget(maxdepthlabel, 6, 0) + self.maxdepth_entry = LengthEntry() + grid1.addWidget(self.maxdepth_entry, 6, 1) + + self.ois_mpass = OptionalInputSection(self.mpass_cb, [self.maxdepth_entry]) + + # Button self.generate_cnc_button = QtGui.QPushButton('Generate') self.generate_cnc_button.setToolTip( "Generate the CNC Job object." ) self.custom_box.addWidget(self.generate_cnc_button) - ################ - ## Paint area ## - ################ + #------------------------------ + # Paint area + #------------------------------ self.paint_label = QtGui.QLabel('Paint Area:') self.paint_label.setToolTip( "Creates tool paths to cover the\n" diff --git a/camlib.py b/camlib.py index c677227f..5748635b 100644 --- a/camlib.py +++ b/camlib.py @@ -2715,8 +2715,13 @@ class CNCjob(Geometry): self.gcode = gcode - def generate_from_geometry_2(self, geometry, append=True, tooldia=None, tolerance=0, - multipass=False, depthpercut=None): + def generate_from_geometry_2(self, + geometry, + append=True, + tooldia=None, + tolerance=0, + multidepth=False, + depthpercut=None): """ Second algorithm to generate from Geometry. @@ -2732,6 +2737,7 @@ class CNCjob(Geometry): """ assert isinstance(geometry, Geometry), \ "Expected a Geometry, got %s" % type(geometry) + log.debug("generate_from_geometry_2()") ## Flatten the geometry @@ -2768,7 +2774,7 @@ class CNCjob(Geometry): self.gcode += self.feedminutecode + "\n" self.gcode += "F%.2f\n" % self.feedrate self.gcode += "G00 Z%.4f\n" % self.z_move # Move (up) to travel height - if(self.spindlespeed != None): + if self.spindlespeed is not None: self.gcode += "M03 S%d\n" % int(self.spindlespeed) # Spindle start with configured speed else: self.gcode += "M03\n" # Spindle start @@ -2794,7 +2800,7 @@ class CNCjob(Geometry): if pt != geo.coords[0] and pt == geo.coords[-1]: geo.coords = list(geo.coords)[::-1] - if not multipass: + if not multidepth: # G-code # Note: self.linear2gcode() and self.point2gcode() will # lower and raise the tool every time. @@ -2804,24 +2810,52 @@ class CNCjob(Geometry): self.gcode += self.point2gcode(geo) else: log.warning("G-code generation not implemented for %s" % (str(type(geo)))) + + #--------- Multi-pass --------- else: if depthpercut is None: depthpercut = self.z_cut depth = 0 + reverse = False while depth > self.z_cut: depth -= depthpercut + log.debug("DEPTH: %f" % depth) # TODO: Working... # G-code # Note: self.linear2gcode() and self.point2gcode() will # lower and raise the tool every time. - # if type(geo) == LineString or type(geo) == LinearRing: - # self.gcode += self.linear2gcode(geo, tolerance=tolerance) - # elif type(geo) == Point: - # self.gcode += self.point2gcode(geo) - # else: - # log.warning("G-code generation not implemented for %s" % (str(type(geo)))) + # Cut at specific depth and do not leave the tool. + if type(geo) == LineString or type(geo) == LinearRing: + self.gcode += self.linear2gcode(geo, tolerance=tolerance, + zcut=depth, + up=False) + + # Ignore multi-pass for points. + elif type(geo) == Point: + self.gcode += self.point2gcode(geo) + break # Ignoring ... + + else: + log.warning("G-code generation not implemented for %s" % (str(type(geo)))) + + # Reverse coordinates if not a loop so we can continue + # cutting without returning to the beginhing. + if type(geo) == LineString: + geo.coords = list(geo.coords)[::-1] + reverse = True + + # If geometry is reversed, revert. + if reverse: + if type(geo) == LineString: + geo.coords = list(geo.coords)[::-1] + + # Lift the tool + self.gcode += "G00 Z%.4f\n" % self.z_move + # self.gcode += "( End of path. )\n" + + # Did deletion at the beginning. # Delete from index, update current location and continue. #rti.delete(hits[0], geo.coords[0]) #rti.delete(hits[0], geo.coords[-1])