| | |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | import FreeCAD |
| | import Path |
| | import Path.Op.Area as PathAreaOp |
| | import Path.Op.Base as PathOp |
| |
|
| | from PySide.QtCore import QT_TRANSLATE_NOOP |
| |
|
| |
|
| | __title__ = "Base CAM Pocket Operation" |
| | __author__ = "sliptonic (Brad Collette)" |
| | __url__ = "https://www.freecad.org" |
| | __doc__ = "Base class and implementation for pocket operations." |
| |
|
| | if False: |
| | Path.Log.setLevel(Path.Log.Level.DEBUG, Path.Log.thisModule()) |
| | Path.Log.trackModule(Path.Log.thisModule()) |
| | else: |
| | Path.Log.setLevel(Path.Log.Level.INFO, Path.Log.thisModule()) |
| |
|
| | translate = FreeCAD.Qt.translate |
| |
|
| |
|
| | class ObjectPocket(PathAreaOp.ObjectOp): |
| | """Base class for proxy objects of all pocket operations.""" |
| |
|
| | @classmethod |
| | def pocketPropertyEnumerations(cls, dataType="data"): |
| | """pocketPropertyEnumerations(dataType="data")... return property enumeration lists of specified dataType. |
| | Args: |
| | dataType = 'data', 'raw', 'translated' |
| | Notes: |
| | 'data' is list of internal string literals used in code |
| | 'raw' is list of (translated_text, data_string) tuples |
| | 'translated' is list of translated string literals |
| | """ |
| |
|
| | enums = { |
| | "CutMode": [ |
| | (translate("CAM_Pocket", "Climb"), "Climb"), |
| | (translate("CAM_Pocket", "Conventional"), "Conventional"), |
| | ], |
| | "StartAt": [ |
| | (translate("CAM_Pocket", "Center"), "Center"), |
| | (translate("CAM_Pocket", "Edge"), "Edge"), |
| | ], |
| | "ClearingPattern": [ |
| | (translate("CAM_Pocket", "ZigZag"), "ZigZag"), |
| | (translate("CAM_Pocket", "Offset"), "Offset"), |
| | (translate("CAM_Pocket", "ZigZagOffset"), "ZigZagOffset"), |
| | (translate("CAM_Pocket", "Line"), "Line"), |
| | (translate("CAM_Pocket", "Grid"), "Grid"), |
| | ], |
| | } |
| |
|
| | if dataType == "raw": |
| | return enums |
| |
|
| | data = list() |
| | idx = 0 if dataType == "translated" else 1 |
| |
|
| | Path.Log.debug(enums) |
| |
|
| | for k, v in enumerate(enums): |
| | data.append((v, [tup[idx] for tup in enums[v]])) |
| | Path.Log.debug(data) |
| |
|
| | return data |
| |
|
| | def areaOpFeatures(self, obj): |
| | """areaOpFeatures(obj) ... Pockets have a FinishDepth and work on Faces""" |
| | return PathOp.FeatureBaseFaces | PathOp.FeatureFinishDepth | self.pocketOpFeatures(obj) |
| |
|
| | def pocketOpFeatures(self, obj): |
| | return 0 |
| |
|
| | def initPocketOp(self, obj): |
| | """initPocketOp(obj) ... overwrite to initialize subclass. |
| | Can safely be overwritten by subclass.""" |
| | pass |
| |
|
| | def opExecute(self, obj): |
| | if len(obj.Base) == 0: |
| | return |
| | super().opExecute(obj) |
| |
|
| | def areaOpSetDefaultValues(self, obj, job): |
| | obj.PocketLastStepOver = 0 |
| |
|
| | def pocketInvertExtraOffset(self): |
| | """pocketInvertExtraOffset() ... return True if ExtraOffset's direction is inward. |
| | Can safely be overwritten by subclass.""" |
| | return False |
| |
|
| | def initAreaOp(self, obj): |
| | """initAreaOp(obj) ... create pocket specific properties. |
| | Do not overwrite, implement initPocketOp(obj) instead.""" |
| | Path.Log.track() |
| |
|
| | |
| | obj.addProperty( |
| | "App::PropertyEnumeration", |
| | "CutMode", |
| | "Pocket", |
| | QT_TRANSLATE_NOOP( |
| | "App::Property", |
| | "The direction that the toolpath should go around the part ClockWise (CW) or CounterClockWise (CCW)", |
| | ), |
| | ) |
| | obj.addProperty( |
| | "App::PropertyDistance", |
| | "ExtraOffset", |
| | "Pocket", |
| | QT_TRANSLATE_NOOP( |
| | "App::Property", |
| | "Extra offset to apply to the operation. Direction is operation dependent.", |
| | ), |
| | ) |
| | obj.addProperty( |
| | "App::PropertyEnumeration", |
| | "StartAt", |
| | "Pocket", |
| | QT_TRANSLATE_NOOP("App::Property", "Start pocketing at center or boundary"), |
| | ) |
| | obj.addProperty( |
| | "App::PropertyPercent", |
| | "StepOver", |
| | "Pocket", |
| | QT_TRANSLATE_NOOP( |
| | "App::Property", "Percent of cutter diameter to step over on each pass" |
| | ), |
| | ) |
| | obj.addProperty( |
| | "App::PropertyFloat", |
| | "ZigZagAngle", |
| | "Pocket", |
| | QT_TRANSLATE_NOOP("App::Property", "Angle of the zigzag pattern"), |
| | ) |
| | obj.addProperty( |
| | "App::PropertyEnumeration", |
| | "ClearingPattern", |
| | "Pocket", |
| | QT_TRANSLATE_NOOP("App::Property", "Clearing pattern to use"), |
| | ) |
| | obj.addProperty( |
| | "App::PropertyBool", |
| | "MinTravel", |
| | "Pocket", |
| | QT_TRANSLATE_NOOP("App::Property", "Use 3D Sorting of Path"), |
| | ) |
| | obj.addProperty( |
| | "App::PropertyBool", |
| | "KeepToolDown", |
| | "Pocket", |
| | QT_TRANSLATE_NOOP("App::Property", "Attempts to avoid unnecessary retractions."), |
| | ) |
| | obj.addProperty( |
| | "App::PropertyPercent", |
| | "PocketLastStepOver", |
| | "Pocket", |
| | QT_TRANSLATE_NOOP( |
| | "App::Property", |
| | "Last Stepover Radius. If 0, 50% of cutter is used. Tuning this can be used to improve stepover for some shapes", |
| | ), |
| | ) |
| | obj.addProperty( |
| | "App::PropertyBool", |
| | "UseRestMachining", |
| | "Pocket", |
| | QT_TRANSLATE_NOOP( |
| | "App::Property", |
| | "Skips machining regions that have already been cleared by previous operations.", |
| | ), |
| | ) |
| |
|
| | for n in self.pocketPropertyEnumerations(): |
| | setattr(obj, n[0], n[1]) |
| |
|
| | self.initPocketOp(obj) |
| |
|
| | def areaOpRetractTool(self, obj): |
| | Path.Log.debug("retracting tool: %d" % (not obj.KeepToolDown)) |
| | return not obj.KeepToolDown |
| |
|
| | def areaOpUseProjection(self, obj): |
| | """areaOpUseProjection(obj) ... return False""" |
| | return False |
| |
|
| | def areaOpAreaParams(self, obj, isHole): |
| | """areaOpAreaParams(obj, isHole) ... return dictionary with pocket's area parameters""" |
| | Path.Log.track() |
| | params = {} |
| | params["Fill"] = 0 |
| | params["Coplanar"] = 0 |
| | params["PocketMode"] = 1 |
| | params["SectionCount"] = -1 |
| | params["Angle"] = obj.ZigZagAngle |
| | params["FromCenter"] = obj.StartAt == "Center" |
| | params["PocketStepover"] = (self.radius * 2) * (float(obj.StepOver) / 100) |
| | extraOffset = obj.ExtraOffset.Value |
| | if self.pocketInvertExtraOffset(): |
| | extraOffset = 0 - extraOffset |
| | params["PocketExtraOffset"] = extraOffset |
| | params["ToolRadius"] = self.radius |
| | params["PocketLastStepover"] = obj.PocketLastStepOver |
| |
|
| | Pattern = { |
| | "ZigZag": 1, |
| | "Offset": 2, |
| | "ZigZagOffset": 4, |
| | "Line": 5, |
| | "Grid": 6, |
| | } |
| |
|
| | params["PocketMode"] = Pattern.get(obj.ClearingPattern, 1) |
| |
|
| | if obj.SplitArcs: |
| | params["Explode"] = True |
| | params["FitArcs"] = False |
| |
|
| | return params |
| |
|
| | def opOnDocumentRestored(self, obj): |
| | super().opOnDocumentRestored(obj) |
| | if not hasattr(obj, "PocketLastStepOver"): |
| | obj.addProperty( |
| | "App::PropertyPercent", |
| | "PocketLastStepOver", |
| | "Pocket", |
| | QT_TRANSLATE_NOOP( |
| | "App::Property", |
| | "Last Stepover Radius. If 0, 50% of cutter is used. Tuning this can be used to improve stepover for some shapes", |
| | ), |
| | ) |
| | obj.PocketLastStepOver = 0 |
| |
|
| | if not hasattr(obj, "UseRestMachining"): |
| | obj.addProperty( |
| | "App::PropertyBool", |
| | "UseRestMachining", |
| | "Pocket", |
| | QT_TRANSLATE_NOOP( |
| | "App::Property", |
| | "Skips machining regions that have already been cleared by previous operations.", |
| | ), |
| | ) |
| |
|
| | if hasattr(obj, "OffsetPattern"): |
| | obj.setGroupOfProperty("OffsetPattern", "Pocket") |
| | obj.renameProperty("OffsetPattern", "ClearingPattern") |
| | if hasattr(obj, "RestMachiningRegions"): |
| | obj.removeProperty("RestMachiningRegions") |
| | if hasattr(obj, "RestMachiningRegionsNeedRecompute"): |
| | obj.removeProperty("RestMachiningRegionsNeedRecompute") |
| |
|
| | Path.Log.track() |
| |
|
| | def areaOpPathParams(self, obj, isHole): |
| | """areaOpAreaParams(obj, isHole) ... return dictionary with pocket's path parameters""" |
| | params = {} |
| |
|
| | CutMode = ["Conventional", "Climb"] |
| | params["orientation"] = CutMode.index(obj.CutMode) |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | if obj.MinTravel and obj.UseStartPoint and obj.StartPoint is not None: |
| | params["sort_mode"] = 3 |
| | params["threshold"] = self.radius * 2 |
| | return params |
| |
|
| |
|
| | def SetupProperties(): |
| | setup = PathAreaOp.SetupProperties() |
| | setup.append("CutMode") |
| | setup.append("ExtraOffset") |
| | setup.append("StepOver") |
| | setup.append("ZigZagAngle") |
| | setup.append("ClearingPattern") |
| | setup.append("StartAt") |
| | setup.append("MinTravel") |
| | setup.append("KeepToolDown") |
| | return setup |
| |
|