From 1ce3b242f2d605d7cfaf2ff5ffbec5ae2a7f735d Mon Sep 17 00:00:00 2001 From: Markus Lampert Date: Sat, 10 Jun 2017 16:30:52 -0700 Subject: [PATCH] Added solid detection and consolidated PathJob Base selection option (removed empty option). --- src/Mod/Path/CMakeLists.txt | 6 +- src/Mod/Path/PathScripts/PathJob.py | 35 ++++++---- src/Mod/Path/PathScripts/PathUtil.py | 51 ++++++++++++++ src/Mod/Path/PathTests/TestPathPost.py | 3 +- src/Mod/Path/PathTests/TestPathUtil.py | 92 ++++++++++++++++++++++++++ src/Mod/Path/TestPathApp.py | 13 ++-- 6 files changed, 176 insertions(+), 24 deletions(-) create mode 100644 src/Mod/Path/PathScripts/PathUtil.py create mode 100644 src/Mod/Path/PathTests/TestPathUtil.py diff --git a/src/Mod/Path/CMakeLists.txt b/src/Mod/Path/CMakeLists.txt index 8472af12ff..5b73a1a3be 100644 --- a/src/Mod/Path/CMakeLists.txt +++ b/src/Mod/Path/CMakeLists.txt @@ -63,6 +63,7 @@ SET(PathScripts_SRCS PathScripts/PathToolController.py PathScripts/PathToolLenOffset.py PathScripts/PathToolLibraryManager.py + PathScripts/PathUtil.py PathScripts/PathUtils.py PathScripts/PostUtils.py PathScripts/__init__.py @@ -83,15 +84,16 @@ SET(PathScripts_SRCS SET(PathTests_SRCS + PathTests/__init__.py PathTests/PathTestUtils.py + PathTests/test_linuxcnc_00.ngc PathTests/TestPathCore.py PathTests/TestPathDepthParams.py PathTests/TestPathDressupHoldingTags.py PathTests/TestPathGeom.py PathTests/TestPathLog.py PathTests/TestPathPost.py - PathTests/__init__.py - PathTests/test_linuxcnc_00.ngc + PathTests/TestPathUtil.py ) SET(all_files diff --git a/src/Mod/Path/PathScripts/PathJob.py b/src/Mod/Path/PathScripts/PathJob.py index 640c3f379d..2446948f82 100644 --- a/src/Mod/Path/PathScripts/PathJob.py +++ b/src/Mod/Path/PathScripts/PathJob.py @@ -28,6 +28,7 @@ import FreeCAD import Path import PathScripts.PathLog as PathLog import PathScripts.PathToolController as PathToolController +import PathScripts.PathUtil as PathUtil import glob import lxml.etree as xml import os @@ -66,7 +67,7 @@ class JobTemplate: class ObjectPathJob: - def __init__(self, obj, base, template = ""): + def __init__(self, obj, base, template = None): self.obj = obj obj.addProperty("App::PropertyFile", "PostProcessorOutputFile", "Output", QtCore.QT_TRANSLATE_NOOP("App::Property","The NC output file for this project")) obj.PostProcessorOutputFile = PathPreferences.defaultOutputFile() @@ -117,7 +118,7 @@ class ObjectPathJob: obj.Description = job.get(JobTemplate.Description) for tc in tree.getroot().iter(JobTemplate.ToolController): PathToolController.CommandPathToolController.FromTemplate(obj, tc) - else: + elif template is not None: PathToolController.CommandPathToolController.Create(obj.Name) def templateAttrs(self, obj): @@ -160,6 +161,16 @@ class ObjectPathJob: path = Path.Path(cmds) obj.Path = path + @classmethod + def baseCandidates(cls): + '''Answer all objects in the current document which could serve as a Base for a job.''' + return sorted(filter(lambda obj: cls.isBaseCandidate(obj) , FreeCAD.ActiveDocument.Objects), key=lambda o: o.Label) + + @classmethod + def isBaseCandidate(cls, obj): + '''Answer true if the given object can be used as a Base for a job.''' + return PathUtil.isSolid(obj) or (hasattr(obj, 'Proxy') and isinstance(obj.Proxy, ArchPanel.PanelSheet)) + class ViewProviderJob: @@ -218,10 +229,8 @@ class TaskPanel: self.obj.PostProcessor = postProcessors self.obj.PostProcessor = currentPostProcessor - self.form.cboBaseObject.addItem("") - for o in FreeCAD.ActiveDocument.Objects: - if hasattr(o, "Shape"): - self.form.cboBaseObject.addItem(o.Name) + for o in ObjectPathJob.baseCandidates(): + self.form.cboBaseObject.addItem(o.Label) self.postProcessorDefaultTooltip = self.form.cboPostProcessor.toolTip() @@ -271,7 +280,7 @@ class TaskPanel: for index in xrange(self.form.PathsList.count()): item = self.form.PathsList.item(index) for olditem in oldlist: - if olditem.Name == item.text(): + if olditem.Label == item.text(): newlist.append(olditem) self.obj.Group = newlist @@ -305,14 +314,14 @@ class TaskPanel: self.form.PathsList.clear() for child in self.obj.Group: - self.form.PathsList.addItem(child.Name) + self.form.PathsList.addItem(child.Label) baseindex = -1 if self.obj.Base: - baseindex = self.form.cboBaseObject.findText(self.obj.Base.Name, QtCore.Qt.MatchFixedString) + baseindex = self.form.cboBaseObject.findText(self.obj.Base.Label, QtCore.Qt.MatchFixedString) else: for o in FreeCADGui.Selection.getCompleteSelection(): - baseindex = self.form.cboBaseObject.findText(o.Name, QtCore.Qt.MatchFixedString) + baseindex = self.form.cboBaseObject.findText(o.Label, QtCore.Qt.MatchFixedString) if baseindex >= 0: self.form.cboBaseObject.setCurrentIndex(baseindex) @@ -348,10 +357,10 @@ class DlgJobCreate: else: selected = None index = 0 - for solid in sorted(filter(lambda obj: (hasattr(obj, 'Shape') and obj.Shape.isClosed()) or (hasattr(obj, 'Proxy') and isinstance(obj.Proxy, ArchPanel.PanelSheet)), FreeCAD.ActiveDocument.Objects), key=lambda o: o.Label): - if solid.Label == selected: + for base in ObjectPathJob.baseCandidates(): + if base.Label == selected: index = self.dialog.cbModel.count() - self.dialog.cbModel.addItem(solid.Label) + self.dialog.cbModel.addItem(base.Label) self.dialog.cbModel.setCurrentIndex(index) templateFiles = [] diff --git a/src/Mod/Path/PathScripts/PathUtil.py b/src/Mod/Path/PathScripts/PathUtil.py new file mode 100644 index 0000000000..10586d723d --- /dev/null +++ b/src/Mod/Path/PathScripts/PathUtil.py @@ -0,0 +1,51 @@ +# -*- 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 * +# * * +# *************************************************************************** + +''' +The purpose of this file is to collect some handy functions. The reason they +are not in PathUtils (and there is this confusing naming going on) is that +PathUtils depends on PathJob. Which makes it impossible to use the functions +and classes defined there in PathJob. + +So if you add to this file and think about importing anything from PathScripts +other than PathLog, then it probably doesn't belong here. +''' + +import PathScripts.PathLog as PathLog + +PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule()) + +def isSolid(obj): + '''isSolid(obj) ... returns true if an object represents a solid.''' + + if hasattr(obj, 'Tip'): + return isSolid(obj.Tip) + if hasattr(obj, 'Shape'): + if obj.Shape.ShapeType == 'Solid' and obj.Shape.isClosed(): + return True + if obj.Shape.ShapeType == 'Compound': + if hasattr(obj, 'Base') and hasattr(obj, 'Tool'): + return isSolid(obj.Base) and isSolid(obj.Tool) + return False + diff --git a/src/Mod/Path/PathTests/TestPathPost.py b/src/Mod/Path/PathTests/TestPathPost.py index 8fef7e67ae..7327ce0449 100644 --- a/src/Mod/Path/PathTests/TestPathPost.py +++ b/src/Mod/Path/PathTests/TestPathPost.py @@ -46,8 +46,7 @@ class PathPostTestCases(unittest.TestCase): # Create job and setup tool library + default tool job = self.doc.addObject("Path::FeatureCompoundPython", "Job") - PathScripts.PathJob.ObjectPathJob(job) - job.Base = self.doc.Box + PathScripts.PathJob.ObjectPathJob(job, box, None) PathScripts.PathToolController.CommandPathToolController.Create(job.Name, False) tool1 = Path.Tool() tool1.Diameter = 5.0 diff --git a/src/Mod/Path/PathTests/TestPathUtil.py b/src/Mod/Path/PathTests/TestPathUtil.py new file mode 100644 index 0000000000..ba4d77f4e3 --- /dev/null +++ b/src/Mod/Path/PathTests/TestPathUtil.py @@ -0,0 +1,92 @@ +# -*- 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.PathUtil as PathUtil +import Sketcher +import TestSketcherApp + +from PathTests.PathTestUtils import PathTestBase + +class TestPathUtil(PathTestBase): + + def setUp(self): + self.doc = FreeCAD.newDocument("TestPathUtils") + + def tearDown(self): + FreeCAD.closeDocument("TestPathUtils") + + def test00(self): + '''Check that isSolid detects solids.''' + box = self.doc.addObject('Part::Box', 'Box') + cylinder = self.doc.addObject('Part::Cylinder', 'Cylinder') + self.doc.recompute() + self.assertTrue(PathUtil.isSolid(box)) + self.assertTrue(PathUtil.isSolid(cylinder)) + + def test01(self): + '''Check that isSolid detects PDs.''' + body = self.doc.addObject('PartDesign::Body', 'Body') + box = self.doc.addObject('PartDesign::AdditiveBox', 'Box') + body.addObject(box) + self.doc.recompute() + self.assertTrue(PathUtil.isSolid(body)) + + def test02(self): + '''Check that isSolid detects compounds.''' + box = self.doc.addObject('Part::Box', 'Box') + box.Length = 10 + box.Width = 10 + box.Height = 1 + box.Placement = FreeCAD.Placement(FreeCAD.Vector(-5,-5,0), FreeCAD.Rotation(FreeCAD.Vector(0,0,1), 0)) + cyl = self.doc.addObject('Part::Cylinder', 'Cylinder') + cyl.Radius = 1 + cyl.Height = 10 + box.Placement = FreeCAD.Placement(FreeCAD.Vector(0,0,-5), FreeCAD.Rotation(FreeCAD.Vector(0,0,1), 0)) + cut = self.doc.addObject('Part::Cut', 'Cut') + cut.Base = box + cut.Tool = cyl + self.doc.recompute() + self.assertTrue(PathUtil.isSolid(cut)) + + + def test03(self): + '''Check that isSolid ignores sketches.''' + body = self.doc.addObject('PartDesign::Body', 'Body') + sketch = self.doc.addObject('Sketcher::SketchObject', 'Sketch') + body.addObject(sketch) + TestSketcherApp.CreateSlotPlateSet(sketch) + self.doc.recompute() + pad = self.doc.addObject('PartDesign::Pad', 'Pad') + body.addObject(pad) + pad.Profile = sketch + self.doc.recompute() + # the body and the pad are solids + self.assertTrue(PathUtil.isSolid(pad)) + self.assertTrue(PathUtil.isSolid(body)) + # however, the sketch is no + self.assertFalse(PathUtil.isSolid(sketch)) + diff --git a/src/Mod/Path/TestPathApp.py b/src/Mod/Path/TestPathApp.py index f2e9611a00..f32af9a985 100644 --- a/src/Mod/Path/TestPathApp.py +++ b/src/Mod/Path/TestPathApp.py @@ -24,11 +24,10 @@ import TestApp -from PathTests.TestPathLog import TestPathLog -from PathTests.TestPathCore import TestPathCore -from PathTests.TestPathPost import PathPostTestCases - -from PathTests.TestPathGeom import TestPathGeom -from PathTests.TestPathDepthParams import depthTestCases - +from PathTests.TestPathLog import TestPathLog +from PathTests.TestPathCore import TestPathCore +from PathTests.TestPathPost import PathPostTestCases +from PathTests.TestPathGeom import TestPathGeom +from PathTests.TestPathUtil import TestPathUtil +from PathTests.TestPathDepthParams import depthTestCases from PathTests.TestPathDressupHoldingTags import TestHoldingTags