diff --git a/src/Mod/Path/CMakeLists.txt b/src/Mod/Path/CMakeLists.txt index 05023311fd..7534672003 100644 --- a/src/Mod/Path/CMakeLists.txt +++ b/src/Mod/Path/CMakeLists.txt @@ -56,6 +56,7 @@ SET(PathScripts_SRCS PathScripts/PathPreferences.py PathScripts/PathPreferencesPathDressup.py PathScripts/PathPreferencesPathJob.py + PathScripts/PathProfileBase.py PathScripts/PathProfileFaces.py PathScripts/PathProfileFacesGui.py PathScripts/PathProfileEdges.py diff --git a/src/Mod/Path/PathScripts/PathContour.py b/src/Mod/Path/PathScripts/PathContour.py index de8f2a9fbe..fb831ae1c0 100644 --- a/src/Mod/Path/PathScripts/PathContour.py +++ b/src/Mod/Path/PathScripts/PathContour.py @@ -29,6 +29,7 @@ import FreeCAD import Part import Path import PathScripts.PathAreaOp as PathAreaOp +import PathScripts.PathProfileBase as PathProfileBase import PathScripts.PathLog as PathLog from PathScripts import PathUtils @@ -53,46 +54,18 @@ __url__ = "http://www.freecadweb.org" """Path Contour object and FreeCAD command""" -class ObjectContour(PathAreaOp.ObjectOp): +class ObjectContour(PathProfileBase.ObjectProfile): - def opFeatures(self, obj): - return PathAreaOp.FeatureTool | PathAreaOp.FeatureDepths | PathAreaOp.FeatureHeights | PathAreaOp.FeatureStartPoint - - def opUseProjection(self, obj): - return True + def baseObject(self): + return super(self.__class__, self) def initOperation(self, obj): - PathLog.track() - - # Contour Properties - obj.addProperty("App::PropertyEnumeration", "Direction", "Contour", QtCore.QT_TRANSLATE_NOOP("App::Property", "The direction that the toolpath should go around the part ClockWise CW or CounterClockWise CCW")) - obj.Direction = ['CW', 'CCW'] # this is the direction that the Contour runs - obj.addProperty("App::PropertyBool", "UseComp", "Contour", QtCore.QT_TRANSLATE_NOOP("App::Property", "make True, if using Cutter Radius Compensation")) - - obj.addProperty("App::PropertyDistance", "OffsetExtra", "Contour", QtCore.QT_TRANSLATE_NOOP("App::Property", "Extra value to stay away from final Contour- good for roughing toolpath")) - - obj.addProperty("App::PropertyEnumeration", "JoinType", "Contour", QtCore.QT_TRANSLATE_NOOP("App::Property", "Controls how tool moves around corners. Default=Round")) - obj.JoinType = ['Round', 'Square', 'Miter'] # this is the direction that the Contour runs - obj.addProperty("App::PropertyFloat", "MiterLimit", "Contour", QtCore.QT_TRANSLATE_NOOP("App::Property", "Maximum distance before a miter join is truncated")) - obj.setEditorMode('MiterLimit', 2) - - self.endVector = None - - def opOnChanged(self, obj, prop): - PathLog.track('prop: {} state: {}'.format(prop, obj.State)) - if prop == 'JoinType': - if obj.JoinType == 'Miter': - obj.setEditorMode('MiterLimit', 0) - else: - obj.setEditorMode('MiterLimit', 2) + self.baseObject().initOperation(obj) + obj.setEditorMode('Side', 2) # it's always outside def opSetDefaultValues(self, obj): - obj.Direction = "CW" - obj.UseComp = True - obj.OffsetExtra = 0.0 - obj.JoinType = "Round" - obj.MiterLimit = 0.1 - + self.baseObject().opSetDefaultValues(obj) + obj.Side = 'Outside' def opShapes(self, obj, commandlist): if obj.UseComp: @@ -123,26 +96,8 @@ class ObjectContour(PathAreaOp.ObjectOp): return [(PathUtils.getEnvelope(partshape=baseobject.Shape, subshape=None, depthparams=self.depthparams), False)] def opAreaParams(self, obj, isHole): - params = {'Fill': 0, 'Coplanar': 2} - - if obj.UseComp is False: - params['Offset'] = 0.0 - else: - params['Offset'] = self.radius + obj.OffsetExtra.Value - - jointype = ['Round', 'Square', 'Miter'] - params['JoinType'] = jointype.index(obj.JoinType) - - if obj.JoinType == 'Miter': - params['MiterLimit'] = obj.MiterLimit - return params - - def opPathParams(self, obj, isHole): - params = {} - if obj.Direction == 'CCW': - params['orientation'] = 0 - else: - params['orientation'] = 1 + params = baseObject().opAreaParams(obj, isHole) + params['Coplanar'] = 2 return params def Create(name): diff --git a/src/Mod/Path/PathScripts/PathProfileBase.py b/src/Mod/Path/PathScripts/PathProfileBase.py new file mode 100644 index 0000000000..e7c84ff14a --- /dev/null +++ b/src/Mod/Path/PathScripts/PathProfileBase.py @@ -0,0 +1,140 @@ +# -*- coding: utf-8 -*- + +# *************************************************************************** +# * * +# * Copyright (c) 2017 sliptonic * +# * * +# * This program is free software; you can redistribute it and/or modify * +# * it under the terms of the GNU Lesser General Public License (LGPL) * +# * as published by the Free Software Foundation; either version 2 of * +# * the License, or (at your option) any later version. * +# * for detail see the LICENCE text file. * +# * * +# * This program is distributed in the hope that it will be useful, * +# * but WITHOUT ANY WARRANTY; without even the implied warranty of * +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +# * GNU Library General Public License for more details. * +# * * +# * You should have received a copy of the GNU Library General Public * +# * License along with this program; if not, write to the Free Software * +# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +# * USA * +# * * +# *************************************************************************** + +import FreeCAD +import Part +import Path +import PathScripts.PathAreaOp as PathAreaOp +import PathScripts.PathUtils as PathUtils +import PathScripts.PathLog as PathLog + +from PySide import QtCore + +"""Path Profile from base features""" + +if False: + PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule()) + PathLog.trackModule(PathLog.thisModule()) +else: + PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) + +if FreeCAD.GuiUp: + import FreeCADGui + from PySide import QtGui + + +# Qt tanslation handling +def translate(context, text, disambig=None): + return QtCore.QCoreApplication.translate(context, text, disambig) + +__title__ = "Path Profile Operation" +__author__ = "sliptonic (Brad Collette)" +__url__ = "http://www.freecadweb.org" + +"""Path Profile object and FreeCAD command for operating on sets of features""" + + +class ObjectProfile(PathAreaOp.ObjectOp): + + def opFeatures(self, obj): + return PathAreaOp.FeatureTool | PathAreaOp.FeatureDepths | PathAreaOp.FeatureHeights | PathAreaOp.FeatureStartPoint + + def initOperation(self, obj): + # Profile Properties + obj.addProperty("App::PropertyEnumeration", "Side", "Profile", QtCore.QT_TRANSLATE_NOOP("App::Property", "Side of edge that tool should cut")) + obj.Side = ['Outside', 'Inside'] # side of profile that cutter is on in relation to direction of profile + obj.addProperty("App::PropertyEnumeration", "Direction", "Profile", QtCore.QT_TRANSLATE_NOOP("App::Property", "The direction that the toolpath should go around the part ClockWise CW or CounterClockWise CCW")) + obj.Direction = ['CW', 'CCW'] # this is the direction that the profile runs + obj.addProperty("App::PropertyBool", "UseComp", "Profile", QtCore.QT_TRANSLATE_NOOP("App::Property", "make True, if using Cutter Radius Compensation")) + + obj.addProperty("App::PropertyDistance", "OffsetExtra", "Profile", QtCore.QT_TRANSLATE_NOOP("App::Property", "Extra value to stay away from final profile- good for roughing toolpath")) + obj.addProperty("App::PropertyEnumeration", "JoinType", "Profile", QtCore.QT_TRANSLATE_NOOP("App::Property", "Controls how tool moves around corners. Default=Round")) + obj.JoinType = ['Round', 'Square', 'Miter'] # this is the direction that the Profile runs + obj.addProperty("App::PropertyFloat", "MiterLimit", "Profile", QtCore.QT_TRANSLATE_NOOP("App::Property", "Maximum distance before a miter join is truncated")) + obj.setEditorMode('MiterLimit', 2) + + obj.Proxy = self + + def onOpChanged(self, obj, prop): + if prop == "UseComp": + if not obj.UseComp: + obj.setEditorMode('Side', 2) + else: + obj.setEditorMode('Side', 0) + + if prop == 'JoinType': + if obj.JoinType == 'Miter': + obj.setEditorMode('MiterLimit', 0) + else: + obj.setEditorMode('MiterLimit', 2) + + def opAreaParams(self, obj, isHole): + params = {} + params['Fill'] = 0 + params['Coplanar'] = 0 + params['SectionCount'] = -1 + + if obj.UseComp: + if obj.Side == 'Inside': + params['Offset'] = 0 - self.radius+obj.OffsetExtra.Value + else: + params['Offset'] = self.radius+obj.OffsetExtra.Value + else: + params['Offset'] = 0.0 + + jointype = ['Round', 'Square', 'Miter'] + params['JoinType'] = jointype.index(obj.JoinType) + + if obj.JoinType == 'Miter': + params['MiterLimit'] = obj.MiterLimit + + return params + + def opPathParams(self, obj, isHole): + params = {} + + # Reverse the direction for holes + if isHole: + direction = "CW" if obj.Direction == "CCW" else "CCW" + else: + direction = obj.Direction + + if direction == 'CCW': + params['orientation'] = 0 + else: + params['orientation'] = 1 + + return params + + def opUseProjection(self, obj): + return True + + def opSetDefaultValues(self, obj): + obj.Side = "Outside" + obj.OffsetExtra = 0.0 + obj.Direction = "CW" + obj.UseComp = True + obj.JoinType = "Round" + obj.MiterLimit = 0.1 + diff --git a/src/Mod/Path/PathScripts/PathProfileFaces.py b/src/Mod/Path/PathScripts/PathProfileFaces.py index befee9fd57..e34666ede9 100644 --- a/src/Mod/Path/PathScripts/PathProfileFaces.py +++ b/src/Mod/Path/PathScripts/PathProfileFaces.py @@ -27,6 +27,7 @@ import FreeCAD import Part import Path import PathScripts.PathAreaOp as PathAreaOp +import PathScripts.PathProfileBase as PathProfileBase import PathScripts.PathLog as PathLog import PathScripts.PathUtils as PathUtils import numpy @@ -48,20 +49,13 @@ __url__ = "http://www.freecadweb.org" """Path Profile object and FreeCAD command""" -class ObjectProfile(PathAreaOp.ObjectOp): +class ObjectProfile(PathProfileBase.ObjectProfile): + + def baseObject(self): + return super(self.__class__, self) def initOperation(self, obj): - - # Profile Properties - obj.addProperty("App::PropertyEnumeration", "Side", "Profile", QtCore.QT_TRANSLATE_NOOP("App::Property", "Side of edge that tool should cut")) - obj.Side = ['Inside', 'Outside'] # side of profile that cutter is on in relation to direction of profile - obj.addProperty("App::PropertyEnumeration", "Direction", "Profile", QtCore.QT_TRANSLATE_NOOP("App::Property", "The direction that the toolpath should go around the part ClockWise CW or CounterClockWise CCW")) - obj.Direction = ['CW', 'CCW'] # this is the direction that the profile runs - obj.addProperty("App::PropertyBool", "UseComp", "Profile", QtCore.QT_TRANSLATE_NOOP("App::Property", "make True, if using Cutter Radius Compensation")) - obj.addProperty("App::PropertyDistance", "OffsetExtra", "Profile", QtCore.QT_TRANSLATE_NOOP("App::Property", "Extra value to stay away from final profile- good for roughing toolpath")) - obj.addProperty("App::PropertyEnumeration", "JoinType", "Profile", QtCore.QT_TRANSLATE_NOOP("App::Property", "Controls how tool moves around corners. Default=Round")) - obj.JoinType = ['Round', 'Square', 'Miter'] # this is the direction that the Contour runs - obj.addProperty("App::PropertyFloat", "MiterLimit", "Profile", QtCore.QT_TRANSLATE_NOOP("App::Property", "Maximum distance before a miter join is truncated")) + self.baseObject().initOperation(obj) # Face specific Properties obj.addProperty("App::PropertyBool", "processHoles", "Profile", QtCore.QT_TRANSLATE_NOOP("App::Property", "Profile holes as well as the outline")) @@ -71,77 +65,14 @@ class ObjectProfile(PathAreaOp.ObjectOp): obj.Proxy = self def opSetDefaultValues(self, obj): - obj.Side = "Outside" - obj.OffsetExtra = 0.0 - obj.Direction = "CW" - obj.UseComp = True - obj.JoinType = "Round" - obj.MiterLimit = 0.1 + self.baseObject().opSetDefaultValues(obj) obj.processHoles = False obj.processCircles = False obj.processPerimeter = True - def onOpChanged(self, obj, prop): - if prop == "UseComp": - if not obj.UseComp: - obj.setEditorMode('Side', 2) - else: - obj.setEditorMode('Side', 0) - - if prop == "JoinType": - obj.setEditorMode('MiterLimit', 2) - if obj.JoinType == 'Miter': - obj.setEditorMode('MiterLimit', 0) - def opFeatures(self, obj): - return PathAreaOp.FeatureTool | PathAreaOp.FeatureDepths | PathAreaOp.FeatureHeights | PathAreaOp.FeatureStartPoint | PathAreaOp.FeatureBaseFaces - - def opUseProjection(self, obj): - return True - - def opAreaParams(self, obj, isHole): - params = {} - params['Fill'] = 0 - params['Coplanar'] = 2 - params['Offset'] = 0.0 - params['SectionCount'] = -1 - - offsetval = 0 - - if obj.UseComp: - offsetval = self.radius + obj.OffsetExtra.Value - - if obj.Side == 'Inside': - offsetval = 0 - offsetval - - if isHole: - offsetval = 0 - offsetval - - params['Offset'] = offsetval - - jointype = ['Round', 'Square', 'Miter'] - params['JoinType'] = jointype.index(obj.JoinType) - - if obj.JoinType == 'Miter': - params['MiterLimit'] = obj.MiterLimit - return params - - def opPathParams(self, obj, isHole): - params = {} - - # Reverse the direction for holes - if isHole: - direction = "CW" if obj.Direction == "CCW" else "CCW" - else: - direction = obj.Direction - - if direction == 'CCW': - params['orientation'] = 0 - else: - params['orientation'] = 1 - return params - + return self.baseObject().opFeatures(obj) | PathAreaOp.FeatureBaseFaces def opShapes(self, obj, commandlist): commandlist.append(Path.Command("(" + obj.Label + ")"))