diff --git a/src/Mod/Part/App/AppPart.cpp b/src/Mod/Part/App/AppPart.cpp index 7814792d6e..289708b483 100644 --- a/src/Mod/Part/App/AppPart.cpp +++ b/src/Mod/Part/App/AppPart.cpp @@ -101,6 +101,7 @@ #include #include #include +#include #include #include #include "Mod/Part/App/SpherePy.h" @@ -342,6 +343,7 @@ PyMOD_INIT_FUNC(Part) // ShapeFix sub-module PyObject* shapeFix(module.getAttr("ShapeFix").ptr()); Base::Interpreter().addType(&Part::ShapeFix_RootPy::Type, shapeFix, "Root"); + Base::Interpreter().addType(&Part::ShapeFix_FacePy::Type, shapeFix, "Face"); Base::Interpreter().addType(&Part::ShapeFix_ShellPy::Type, shapeFix, "Shell"); // ShapeUpgrade sub-module diff --git a/src/Mod/Part/App/AppPartPy.cpp b/src/Mod/Part/App/AppPartPy.cpp index 5b97f5d9d0..1606738ebd 100644 --- a/src/Mod/Part/App/AppPartPy.cpp +++ b/src/Mod/Part/App/AppPartPy.cpp @@ -58,6 +58,8 @@ # include # include # include +# include +# include # include # include # include @@ -308,10 +310,87 @@ class ShapeFixModule : public Py::ExtensionModule public: ShapeFixModule() : Py::ExtensionModule("ShapeFix") { + add_varargs_method("sameParameter",&ShapeFixModule::sameParameter, + "sameParameter(shape, enforce, prec=0.0)" + ); + add_varargs_method("encodeRegularity",&ShapeFixModule::encodeRegularity, + "encodeRegularity(shape, tolerance = 1e-10)\n" + ); + add_varargs_method("removeSmallEdges",&ShapeFixModule::removeSmallEdges, + "removeSmallEdges(shape, tolerance, ReShapeContext)\n" + "Removes edges which are less than given tolerance from shape" + ); + add_varargs_method("fixVertexPosition",&ShapeFixModule::fixVertexPosition, + "fixVertexPosition(shape, tolerance, ReShapeContext)\n" + "Fix position of the vertices having tolerance more tnan specified one" + ); + add_varargs_method("leastEdgeSize",&ShapeFixModule::leastEdgeSize, + "leastEdgeSize(shape)\n" + "Calculate size of least edge" + ); initialize("This is a module working with the ShapeFix framework."); // register with Python } virtual ~ShapeFixModule() {} + +private: + Py::Object sameParameter(const Py::Tuple& args) + { + PyObject* shape; + PyObject* enforce; + double prec = 0.0; + if (!PyArg_ParseTuple(args.ptr(), "O!O!|d", &TopoShapePy::Type, &shape, &PyBool_Type, &enforce, &prec)) + throw Py::Exception(); + + TopoDS_Shape sh = static_cast(shape)->getTopoShapePtr()->getShape(); + bool ok = ShapeFix::SameParameter(sh, PyObject_IsTrue(enforce) ? Standard_True : Standard_False, prec); + return Py::Boolean(ok); + } + Py::Object encodeRegularity(const Py::Tuple& args) + { + PyObject* shape; + double tolang = 1.0e-10; + if (!PyArg_ParseTuple(args.ptr(), "O!|d", &TopoShapePy::Type, &shape, &tolang)) + throw Py::Exception(); + + TopoDS_Shape sh = static_cast(shape)->getTopoShapePtr()->getShape(); + ShapeFix::EncodeRegularity(sh, tolang); + return Py::None(); + } + Py::Object removeSmallEdges(const Py::Tuple& args) + { + PyObject* shape; + double tol; + if (!PyArg_ParseTuple(args.ptr(), "O!d", &TopoShapePy::Type, &shape, &tol)) + throw Py::Exception(); + + TopoDS_Shape sh = static_cast(shape)->getTopoShapePtr()->getShape(); + Handle(ShapeBuild_ReShape) reshape = new ShapeBuild_ReShape(); + TopoShape res = ShapeFix::RemoveSmallEdges(sh, tol, reshape); + return Py::asObject(res.getPyObject()); + } + Py::Object fixVertexPosition(const Py::Tuple& args) + { + PyObject* shape; + double tol; + if (!PyArg_ParseTuple(args.ptr(), "O!d", &TopoShapePy::Type, &shape, &tol)) + throw Py::Exception(); + + TopoDS_Shape sh = static_cast(shape)->getTopoShapePtr()->getShape(); + Handle(ShapeBuild_ReShape) reshape = new ShapeBuild_ReShape(); + bool ok = ShapeFix::FixVertexPosition(sh, tol, reshape); + return Py::Boolean(ok); + } + Py::Object leastEdgeSize(const Py::Tuple& args) + { + PyObject* shape; + if (!PyArg_ParseTuple(args.ptr(), "O!", &TopoShapePy::Type, &shape)) + throw Py::Exception(); + + TopoDS_Shape sh = static_cast(shape)->getTopoShapePtr()->getShape(); + double len = ShapeFix::LeastEdgeSize(sh); + return Py::Float(len); + } }; class ShapeUpgradeModule : public Py::ExtensionModule diff --git a/src/Mod/Part/App/CMakeLists.txt b/src/Mod/Part/App/CMakeLists.txt index 390cb39bf6..4474fee3e9 100644 --- a/src/Mod/Part/App/CMakeLists.txt +++ b/src/Mod/Part/App/CMakeLists.txt @@ -137,6 +137,7 @@ generate_from_xml(HLRBRep/HLRBRep_PolyAlgoPy) generate_from_xml(HLRBRep/PolyHLRToShapePy) generate_from_xml(ShapeFix/ShapeFix_RootPy) +generate_from_xml(ShapeFix/ShapeFix_FacePy) generate_from_xml(ShapeFix/ShapeFix_ShellPy) generate_from_xml(ShapeUpgrade/UnifySameDomainPy) @@ -425,6 +426,8 @@ SOURCE_GROUP("HLRBRep" FILES ${HLRBRepPy_SRCS}) SET(ShapeFixPy_SRCS ShapeFix/ShapeFix_RootPy.xml ShapeFix/ShapeFix_RootPyImp.cpp + ShapeFix/ShapeFix_FacePy.xml + ShapeFix/ShapeFix_FacePyImp.cpp ShapeFix/ShapeFix_ShellPy.xml ShapeFix/ShapeFix_ShellPyImp.cpp ) diff --git a/src/Mod/Part/App/ShapeFix/ShapeFix_FacePy.xml b/src/Mod/Part/App/ShapeFix/ShapeFix_FacePy.xml new file mode 100644 index 0000000000..f24fe631d2 --- /dev/null +++ b/src/Mod/Part/App/ShapeFix/ShapeFix_FacePy.xml @@ -0,0 +1,203 @@ + + + + + + Class for fixing operations on faces + + + + Initializes by face + + + + + Iterates on subshapes and performs fixes + + + + + Add a wire to current face using BRep_Builder. +Wire is added without taking into account orientation of face +(as if face were FORWARD) + + + + + + +Fixes orientation of wires on the face +It tries to make all wires lie outside all others (according +to orientation) by reversing orientation of some of them. +If face lying on sphere or torus has single wire and +AddNaturalBoundMode is True, that wire is not reversed in +any case (supposing that natural bound will be added). +Returns True if wires were reversed + + + + + + +Adds natural boundary on face if it is missing. +Two cases are supported: + - face has no wires + - face lies on geometrically double-closed surface +(sphere or torus) and none of wires is left-oriented +Returns True if natural boundary was added + + + + + + +Detects and fixes the special case when face on a closed +surface is given by two wires closed in 3d but with gap in 2d. +In that case it creates a new wire from the two, and adds a +missing seam edge +Returns True if missing seam was added + + + + + + +Detects wires with small area (that is less than +100*Precision.PConfusion(). Removes these wires if they are internal. +Returns True if at least one small wire removed, False nothing is done. + + + + + + +Detects if wire has a loop and fixes this situation by splitting on the few parts. + + + + + + +Detects and fixes the special case when face has more than one wire +and this wires have intersection point + + + + + + +If wire contains two coincidence edges it must be removed + + + + + + +Fixes topology for a specific case when face is composed +by a single wire belting a periodic surface. In that case +a degenerated edge is reconstructed in the degenerated pole +of the surface. Initial wire gets consistent orientation. +Must be used in couple and before FixMissingSeam routine + + + + + + Iterates on subshapes and performs fixes + + + + + Returns a face which corresponds to the current state + + + + + Returns resulting shape (Face or Shell if splitted) +To be used instead of face() if FixMissingSeam involved + + + + + + Mode for applying fixes of ShapeFix_Wire + + + + + + Mode for applying fixes of orientation +If True, wires oriented to border limited square + + + + + + + If true, natural boundary is added on faces that miss them. +Default is False for faces with single wire (they are +handled by FixOrientation in that case) and True for others. + + + + + + + If True, tries to insert seam if missing + + + + + + If True, drops small wires + + + + + + If True, drops small wires + + + + + + Mode for applying fixes of intersecting wires + + + + + + Mode for applying fixes of loop wires + + + + + + Mode for applying fixes of split face + + + + + + Mode for applying auto-corrected precision + + + + + + Mode for applying periodic degeneration + + + + + diff --git a/src/Mod/Part/App/ShapeFix/ShapeFix_FacePyImp.cpp b/src/Mod/Part/App/ShapeFix/ShapeFix_FacePyImp.cpp new file mode 100644 index 0000000000..7e751f362a --- /dev/null +++ b/src/Mod/Part/App/ShapeFix/ShapeFix_FacePyImp.cpp @@ -0,0 +1,354 @@ +/*************************************************************************** + * Copyright (c) 2022 Werner Mayer * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#include "PreCompiled.h" +#ifndef _PreComp_ +# include +#endif + +#include "ShapeFix/ShapeFix_FacePy.h" +#include "ShapeFix/ShapeFix_FacePy.cpp" +#include +#include +#include + +using namespace Part; + +// returns a string which represents the object e.g. when printed in python +std::string ShapeFix_FacePy::representation() const +{ + return ""; +} + +PyObject *ShapeFix_FacePy::PyMake(struct _typeobject *, PyObject *, PyObject *) // Python wrapper +{ + // create a new instance of ShapeFix_FacePy + return new ShapeFix_FacePy(new ShapeFix_Face); +} + +// constructor method +int ShapeFix_FacePy::PyInit(PyObject* args, PyObject* /*kwds*/) +{ + PyObject* face = nullptr; + if (PyArg_ParseTuple(args, "|O!", &TopoShapeFacePy::Type, &face)) { + if (face) { + getShapeFix_FacePtr()->Init(TopoDS::Face(static_cast(face)->getTopoShapePtr()->getShape())); + } + + return 0; + } + + PyErr_Clear(); + double prec; + PyObject* fwd = Py_True; + if (PyArg_ParseTuple(args, "O!d|O!", &GeometrySurfacePy::Type, &face, &prec, &PyBool_Type, &fwd)) { + if (face) { + Handle(Geom_Surface) surf = Handle(Geom_Surface)::DownCast(static_cast(face)->getGeomSurfacePtr()->handle()); + getShapeFix_FacePtr()->Init(surf, prec, PyObject_IsTrue(fwd) ? Standard_True : Standard_False); + } + + return 0; + } + + PyErr_SetString(PyExc_TypeError, "Supported arguments are:\n" + "-- Face\n" + "-- Surface, Precision, [Forward=True}\n" + " Precision is a Float\n" + " If Forward is the orientation will be FORWARD or REVERSED otherwise" + ); + return -1; +} + +PyObject* ShapeFix_FacePy::init(PyObject *args) +{ + PyObject* face; + if (PyArg_ParseTuple(args, "O!", &TopoShapeFacePy::Type, &face)) { + getShapeFix_FacePtr()->Init(TopoDS::Face(static_cast(face)->getTopoShapePtr()->getShape())); + Py_Return; + } + + PyErr_Clear(); + double prec; + PyObject* fwd = Py_True; + if (PyArg_ParseTuple(args, "O!d|O!", &GeometrySurfacePy::Type, &face, &prec, &PyBool_Type, &fwd)) { + if (face) { + Handle(Geom_Surface) surf = Handle(Geom_Surface)::DownCast(static_cast(face)->getGeomSurfacePtr()->handle()); + getShapeFix_FacePtr()->Init(surf, prec, PyObject_IsTrue(fwd) ? Standard_True : Standard_False); + } + + Py_Return; + } + + PyErr_SetString(PyExc_TypeError, "Supported arguments are:\n" + "-- Face\n" + "-- Surface, Precision, [Forward=True}\n" + " Precision is a Float\n" + " If Forward is the orientation will be FORWARD or REVERSED otherwise" + ); + return nullptr; +} + +PyObject* ShapeFix_FacePy::clearModes(PyObject *args) +{ + if (!PyArg_ParseTuple(args, "")) + return nullptr; + + getShapeFix_FacePtr()->ClearModes(); + Py_Return; +} + +PyObject* ShapeFix_FacePy::add(PyObject *args) +{ + PyObject* wire; + if (!PyArg_ParseTuple(args, "O!", &TopoShapeWirePy::Type, &wire)) + return nullptr; + + getShapeFix_FacePtr()->Add(TopoDS::Wire(static_cast(wire)->getTopoShapePtr()->getShape())); + Py_Return; +} + +PyObject* ShapeFix_FacePy::fixOrientation(PyObject *args) +{ + if (!PyArg_ParseTuple(args, "")) + return nullptr; + + Standard_Boolean ok = getShapeFix_FacePtr()->FixOrientation(); + return Py::new_reference_to(Py::Boolean(ok)); +} + +PyObject* ShapeFix_FacePy::fixAddNaturalBound(PyObject *args) +{ + if (!PyArg_ParseTuple(args, "")) + return nullptr; + + Standard_Boolean ok = getShapeFix_FacePtr()->FixAddNaturalBound(); + return Py::new_reference_to(Py::Boolean(ok)); +} + +PyObject* ShapeFix_FacePy::fixMissingSeam(PyObject *args) +{ + if (!PyArg_ParseTuple(args, "")) + return nullptr; + + Standard_Boolean ok = getShapeFix_FacePtr()->FixMissingSeam(); + return Py::new_reference_to(Py::Boolean(ok)); +} + +PyObject* ShapeFix_FacePy::fixSmallAreaWire(PyObject *args) +{ + PyObject* removeSmall; + if (!PyArg_ParseTuple(args, "O!", &PyBool_Type, &removeSmall)) + return nullptr; + + Standard_Boolean ok = getShapeFix_FacePtr()->FixSmallAreaWire(PyObject_IsTrue(removeSmall) ? Standard_True : Standard_False); + return Py::new_reference_to(Py::Boolean(ok)); +} + +PyObject* ShapeFix_FacePy::fixLoopWire(PyObject *args) +{ + if (!PyArg_ParseTuple(args, "")) + return nullptr; + + TopTools_SequenceOfShape aResWires; + Standard_Boolean ok = getShapeFix_FacePtr()->FixLoopWire(aResWires); + Py::List list; + for (int index = aResWires.Lower(); index <= aResWires.Upper(); index++) { + TopoShape sh = aResWires(index); + list.append(Py::asObject(sh.getPyObject())); + } + return Py::new_reference_to(Py::TupleN(Py::Boolean(ok), list)); +} + +PyObject* ShapeFix_FacePy::fixIntersectingWires(PyObject *args) +{ + if (!PyArg_ParseTuple(args, "")) + return nullptr; + + Standard_Boolean ok = getShapeFix_FacePtr()->FixIntersectingWires(); + return Py::new_reference_to(Py::Boolean(ok)); +} + +PyObject* ShapeFix_FacePy::fixWiresTwoCoincidentEdges(PyObject *args) +{ + if (!PyArg_ParseTuple(args, "")) + return nullptr; + + Standard_Boolean ok = getShapeFix_FacePtr()->FixWiresTwoCoincEdges(); + return Py::new_reference_to(Py::Boolean(ok)); +} + +PyObject* ShapeFix_FacePy::fixPeriodicDegenerated(PyObject *args) +{ + if (!PyArg_ParseTuple(args, "")) + return nullptr; + + Standard_Boolean ok = getShapeFix_FacePtr()->FixPeriodicDegenerated(); + return Py::new_reference_to(Py::Boolean(ok)); +} + +PyObject* ShapeFix_FacePy::perform(PyObject *args) +{ + if (!PyArg_ParseTuple(args, "")) + return nullptr; + + Standard_Boolean ok = getShapeFix_FacePtr()->Perform(); + return Py::new_reference_to(Py::Boolean(ok)); +} + +PyObject* ShapeFix_FacePy::face(PyObject *args) +{ + if (!PyArg_ParseTuple(args, "")) + return nullptr; + + TopoShape shape = getShapeFix_FacePtr()->Face(); + return shape.getPyObject(); +} + +PyObject* ShapeFix_FacePy::result(PyObject *args) +{ + if (!PyArg_ParseTuple(args, "")) + return nullptr; + + TopoShape shape = getShapeFix_FacePtr()->Result(); + return shape.getPyObject(); +} + +Py::Boolean ShapeFix_FacePy::getFixWireMode() const +{ + return Py::Boolean(getShapeFix_FacePtr()->FixWireMode()); +} + +void ShapeFix_FacePy::setFixWireMode(Py::Boolean arg) +{ + getShapeFix_FacePtr()->FixWireMode() = arg; +} + +Py::Boolean ShapeFix_FacePy::getFixOrientationMode() const +{ + return Py::Boolean(getShapeFix_FacePtr()->FixOrientationMode()); +} + +void ShapeFix_FacePy::setFixOrientationMode(Py::Boolean arg) +{ + getShapeFix_FacePtr()->FixOrientationMode() = arg; +} + +Py::Boolean ShapeFix_FacePy::getFixAddNaturalBoundMode() const +{ + return Py::Boolean(getShapeFix_FacePtr()->FixAddNaturalBoundMode()); +} + +void ShapeFix_FacePy::setFixAddNaturalBoundMode(Py::Boolean arg) +{ + getShapeFix_FacePtr()->FixAddNaturalBoundMode() = arg; +} + +Py::Boolean ShapeFix_FacePy::getFixMissingSeamMode() const +{ + return Py::Boolean(getShapeFix_FacePtr()->FixMissingSeamMode()); +} + +void ShapeFix_FacePy::setFixMissingSeamMode(Py::Boolean arg) +{ + getShapeFix_FacePtr()->FixMissingSeamMode() = arg; +} + +Py::Boolean ShapeFix_FacePy::getFixSmallAreaWireMode() const +{ + return Py::Boolean(getShapeFix_FacePtr()->FixSmallAreaWireMode()); +} + +void ShapeFix_FacePy::setFixSmallAreaWireMode(Py::Boolean arg) +{ + getShapeFix_FacePtr()->FixSmallAreaWireMode() = arg; +} + +Py::Boolean ShapeFix_FacePy::getRemoveSmallAreaFaceMode() const +{ + return Py::Boolean(getShapeFix_FacePtr()->RemoveSmallAreaFaceMode()); +} + +void ShapeFix_FacePy::setRemoveSmallAreaFaceMode(Py::Boolean arg) +{ + getShapeFix_FacePtr()->RemoveSmallAreaFaceMode() = arg; +} + +Py::Boolean ShapeFix_FacePy::getFixIntersectingWiresMode() const +{ + return Py::Boolean(getShapeFix_FacePtr()->FixIntersectingWiresMode()); +} + +void ShapeFix_FacePy::setFixIntersectingWiresMode(Py::Boolean arg) +{ + getShapeFix_FacePtr()->FixIntersectingWiresMode() = arg; +} + +Py::Boolean ShapeFix_FacePy::getFixLoopWiresMode() const +{ + return Py::Boolean(getShapeFix_FacePtr()->FixLoopWiresMode()); +} + +void ShapeFix_FacePy::setFixLoopWiresMode(Py::Boolean arg) +{ + getShapeFix_FacePtr()->FixLoopWiresMode() = arg; +} + +Py::Boolean ShapeFix_FacePy::getFixSplitFaceMode() const +{ + return Py::Boolean(getShapeFix_FacePtr()->FixSplitFaceMode()); +} + +void ShapeFix_FacePy::setFixSplitFaceMode(Py::Boolean arg) +{ + getShapeFix_FacePtr()->FixSplitFaceMode() = arg; +} + +Py::Boolean ShapeFix_FacePy::getAutoCorrectPrecisionMode() const +{ + return Py::Boolean(getShapeFix_FacePtr()->AutoCorrectPrecisionMode()); +} + +void ShapeFix_FacePy::setAutoCorrectPrecisionMode(Py::Boolean arg) +{ + getShapeFix_FacePtr()->AutoCorrectPrecisionMode() = arg; +} + +Py::Boolean ShapeFix_FacePy::getFixPeriodicDegeneratedMode() const +{ + return Py::Boolean(getShapeFix_FacePtr()->FixPeriodicDegeneratedMode()); +} + +void ShapeFix_FacePy::setFixPeriodicDegeneratedMode(Py::Boolean arg) +{ + getShapeFix_FacePtr()->FixPeriodicDegeneratedMode() = arg; +} + +PyObject *ShapeFix_FacePy::getCustomAttributes(const char* /*attr*/) const +{ + return nullptr; +} + +int ShapeFix_FacePy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/) +{ + return 0; +} diff --git a/src/Mod/Part/App/ShapeFix/ShapeFix_RootPyImp.cpp b/src/Mod/Part/App/ShapeFix/ShapeFix_RootPyImp.cpp index 7f582a779f..3106bdf2f7 100644 --- a/src/Mod/Part/App/ShapeFix/ShapeFix_RootPyImp.cpp +++ b/src/Mod/Part/App/ShapeFix/ShapeFix_RootPyImp.cpp @@ -45,8 +45,11 @@ PyObject *ShapeFix_RootPy::PyMake(struct _typeobject *, PyObject *, PyObject *) } // constructor method -int ShapeFix_RootPy::PyInit(PyObject* /*args*/, PyObject* /*kwds*/) +int ShapeFix_RootPy::PyInit(PyObject* args, PyObject* /*kwds*/) { + if (!PyArg_ParseTuple(args, "")) + return -1; + return 0; } diff --git a/src/Mod/Part/TestPartApp.py b/src/Mod/Part/TestPartApp.py index 9beed759a6..f471738a8d 100644 --- a/src/Mod/Part/TestPartApp.py +++ b/src/Mod/Part/TestPartApp.py @@ -394,3 +394,99 @@ class PartTestRuledSurface(unittest.TestCase): def tearDown(self): FreeCAD.closeDocument(self.Doc.Name) + +class PartTestShapeFix(unittest.TestCase): + def testShapeFix_Root(self): + with self.assertRaises(TypeError): + Part.ShapeFix.Root([]) + + fix = Part.ShapeFix.Root() + print (fix) + + fix.Precision = 0.0 + self.assertEqual(fix.Precision, 0.0) + + fix.MinTolerance = 0.0 + self.assertEqual(fix.MinTolerance, 0.0) + + fix.MaxTolerance = 0.5 + self.assertEqual(fix.MaxTolerance, 0.5) + + self.assertEqual(fix.limitTolerance(0.25), 0.25) + + def testShapeFix_Face(self): + surface = Part.Plane() + face = surface.toShape(-1, 1, -1, 1) + + Part.ShapeFix.Face() + Part.ShapeFix.Face(surface, 0.00001, True) + with self.assertRaises(TypeError): + Part.ShapeFix.Face([]) + + fix = Part.ShapeFix.Face(face) + print (fix) + + fix.fixOrientation() + fix.fixAddNaturalBound() + fix.fixMissingSeam() + fix.fixSmallAreaWire(True) + fix.fixLoopWire() + fix.fixIntersectingWires() + fix.fixWiresTwoCoincidentEdges() + fix.fixPeriodicDegenerated() + fix.perform() + + fix.add(face.OuterWire) + current = fix.face() + result = fix.result() + + fix.FixWireMode = True + self.assertEqual(fix.FixWireMode, True) + + fix.FixOrientationMode = True + self.assertEqual(fix.FixOrientationMode, True) + + fix.FixAddNaturalBoundMode = True + self.assertEqual(fix.FixAddNaturalBoundMode, True) + + fix.FixMissingSeamMode = True + self.assertEqual(fix.FixMissingSeamMode, True) + + fix.FixSmallAreaWireMode = True + self.assertEqual(fix.FixSmallAreaWireMode, True) + + fix.RemoveSmallAreaFaceMode = True + self.assertEqual(fix.RemoveSmallAreaFaceMode, True) + + fix.FixIntersectingWiresMode = True + self.assertEqual(fix.FixIntersectingWiresMode, True) + + fix.FixLoopWiresMode = True + self.assertEqual(fix.FixLoopWiresMode, True) + + fix.FixSplitFaceMode = True + self.assertEqual(fix.FixSplitFaceMode, True) + + fix.AutoCorrectPrecisionMode = True + self.assertEqual(fix.AutoCorrectPrecisionMode, True) + + fix.FixPeriodicDegeneratedMode = True + self.assertEqual(fix.FixPeriodicDegeneratedMode, True) + + fix.clearModes() + + def testShapeFix_Shell(self): + surface = Part.Plane() + face = surface.toShape(-1, 1, -1, 1) + shell = Part.Shell([face]) + + Part.ShapeFix.Shell() + with self.assertRaises(TypeError): + Part.ShapeFix.Face([]) + + fix = Part.ShapeFix.Shell(shell) + fix.init(shell) + print (fix) + fix.perform() + fix.shell() + fix.shape()