diff --git a/src/Mod/Part/App/TopoShape.h b/src/Mod/Part/App/TopoShape.h index d0e095278f..a6894f9ab1 100644 --- a/src/Mod/Part/App/TopoShape.h +++ b/src/Mod/Part/App/TopoShape.h @@ -261,6 +261,13 @@ enum class OpenResult allowOpenResult }; +// See BRepFeat_MakeRevol +enum class RevolMode { + CutFromBase = 0, + FuseWithBase = 1, + None = 2 +}; + /** The representation for a CAD Shape */ // NOLINTNEXTLINE cppcoreguidelines-special-member-functions @@ -1081,7 +1088,6 @@ public: /** Make revolved shell around a basis shape * - * @param base: the basis shape * @param axis: the revolving axis * @param d: rotation angle in degree * @param face_maker: optional type name of the the maker used to make a @@ -1097,6 +1103,67 @@ public: } + /** Make revolved shell around a basis shape + * + * @param base: the basis shape + * @param axis: the revolving axis + * @param d: rotation angle in degree + * @param face_maker: optional type name of the the maker used to make a + * face from basis shape + * @param supportface: the bottom face for the revolution, or null + * @param uptoface: the upper limit face for the revolution, or null + * @param Mode: the opencascade defined modes + * @param Modify: if opencascade should modify existing shapes + * @param op: optional string to be encoded into topo naming for indicating + * the operation + * + * @return Return the generated new shape. The TopoShape itself is not modified. + */ + TopoShape& makeElementRevolution(const TopoShape& _base, + const gp_Ax1& axis, + double d, + const TopoDS_Face& supportface, + const TopoDS_Face& uptoface, + const char* face_maker = nullptr, + RevolMode Mode = RevolMode::None, + Standard_Boolean Modify = Standard_True, + const char* op = nullptr); + + /** Make revolved shell around a basis shape + * + * @param axis: the revolving axis + * @param d: rotation angle in degree + * @param face_maker: optional type name of the the maker used to make a + * face from basis shape + * @param supportface: the bottom face for the revolution, or null + * @param uptoface: the upper limit face for the revolution, or null + * @param Mode: the opencascade defined modes + * @param Modify: if opencascade should modify existing shapes + * @param op: optional string to be encoded into topo naming for indicating + * the operation + * + * @return Return the generated new shape. The TopoShape itself is not modified. + */ + TopoShape& makeElementRevolution(const gp_Ax1& axis, + double d, + const TopoDS_Face& supportface, + const TopoDS_Face& uptoface, + const char* face_maker = nullptr, + RevolMode Mode = RevolMode::None, + Standard_Boolean Modify = Standard_True, + const char* op = nullptr) const + { + return TopoShape(0, Hasher).makeElementRevolution(*this, + axis, + d, + supportface, + uptoface, + face_maker, + Mode, + Modify, + op); + } + /** Make a prism that is a linear sweep of a basis shape * * @param base: the basis shape diff --git a/src/Mod/Part/App/TopoShapeExpansion.cpp b/src/Mod/Part/App/TopoShapeExpansion.cpp index 24d9e4d6a2..2a01402ff9 100644 --- a/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -106,6 +106,7 @@ #include #include #include +#include FC_LOG_LEVEL_INIT("TopoShape", true, true) // NOLINT @@ -4439,6 +4440,51 @@ TopoShape& TopoShape::makeElementRevolve(const TopoShape& _base, return makeElementShape(mkRevol, base, op); } +TopoShape& TopoShape::makeElementRevolution(const TopoShape& _base, + const gp_Ax1& axis, + double d, + const TopoDS_Face& supportface, + const TopoDS_Face& uptoface, + const char* face_maker, + RevolMode Mode, + Standard_Boolean Modify, + const char* op) +{ + if (!op) { + op = Part::OpCodes::Revolve; + } + + TopoShape base(_base); + if (base.isNull()) { + FC_THROWM(NullShapeException, "Null shape"); + } + if (face_maker && !base.hasSubShape(TopAbs_FACE)) { + if (!base.hasSubShape(TopAbs_WIRE)) { + base = base.makeElementWires(); + } + base = base.makeElementFace(nullptr, face_maker, nullptr); + } + + BRepFeat_MakeRevol mkRevol; + for (TopExp_Explorer xp(base.getShape(), TopAbs_FACE); xp.More(); xp.Next()) { + mkRevol.Init(_base.getShape(), + xp.Current(), + supportface, + axis, + static_cast(Mode), + Modify); + mkRevol.Perform(uptoface); + if (!mkRevol.IsDone()) { + throw Base::RuntimeError("Revolution: Up to face: Could not revolve the sketch!"); + } + base = mkRevol.Shape(); + if (Mode == RevolMode::None) { + Mode = RevolMode::FuseWithBase; + } + } + return makeElementShape(mkRevol, base, op); +} + TopoShape& TopoShape::makeElementDraft(const TopoShape& shape, const std::vector& _faces, const gp_Dir& pullDirection, diff --git a/src/Mod/PartDesign/App/FeatureRevolution.cpp b/src/Mod/PartDesign/App/FeatureRevolution.cpp index b03e48d175..9f884a752d 100644 --- a/src/Mod/PartDesign/App/FeatureRevolution.cpp +++ b/src/Mod/PartDesign/App/FeatureRevolution.cpp @@ -38,6 +38,7 @@ #include #include "FeatureRevolution.h" +#include "Mod/Part/App/TopoShapeOpCode.h" using namespace PartDesign; @@ -78,24 +79,29 @@ short Revolution::mustExecute() const return ProfileBased::mustExecute(); } -App::DocumentObjectExecReturn *Revolution::execute() +App::DocumentObjectExecReturn* Revolution::execute() { // Validate parameters // All angles are in radians unless explicitly stated double angleDeg = Angle.getValue(); - if (angleDeg > 360.0) - return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Angle of revolution too large")); + if (angleDeg > 360.0) { + return new App::DocumentObjectExecReturn( + QT_TRANSLATE_NOOP("Exception", "Angle of revolution too large")); + } double angle = Base::toRadians(angleDeg); - if (angle < Precision::Angular()) - return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Angle of revolution too small")); + if (angle < Precision::Angular()) { + return new App::DocumentObjectExecReturn( + QT_TRANSLATE_NOOP("Exception", "Angle of revolution too small")); + } double angle2 = Base::toRadians(Angle2.getValue()); - TopoDS_Shape sketchshape; + TopoShape sketchshape; try { sketchshape = getVerifiedFace(); - } catch (const Base::Exception& e) { + } + catch (const Base::Exception& e) { return new App::DocumentObjectExecReturn(e.what()); } @@ -103,27 +109,36 @@ App::DocumentObjectExecReturn *Revolution::execute() TopoShape base; try { base = getBaseTopoShape(); - } catch (const Base::Exception&) { + } + catch (const Base::Exception&) { // fall back to support (for legacy features) - base = TopoShape(); } // update Axis from ReferenceAxis try { updateAxis(); - } catch (const Base::Exception& e) { + } + catch (const Base::Exception& e) { return new App::DocumentObjectExecReturn(e.what()); } - // get revolve axis - Base::Vector3d b = Base.getValue(); - gp_Pnt pnt(b.x,b.y,b.z); - Base::Vector3d v = Axis.getValue(); - gp_Dir dir(v.x,v.y,v.z); - try { - if (sketchshape.IsNull()) - return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Creating a face from sketch failed")); + // get revolve axis + Base::Vector3d b = Base.getValue(); + gp_Pnt pnt(b.x, b.y, b.z); + Base::Vector3d v = Axis.getValue(); + + if (v.IsNull()) { + return new App::DocumentObjectExecReturn( + QT_TRANSLATE_NOOP("Exception", "Reference axis is invalid")); + } + + gp_Dir dir(v.x, v.y, v.z); + + if (sketchshape.isNull()) { + return new App::DocumentObjectExecReturn( + QT_TRANSLATE_NOOP("Exception", "Creating a face from sketch failed")); + } RevolMethod method = methodFromString(Type.getValueAsString()); @@ -132,51 +147,99 @@ App::DocumentObjectExecReturn *Revolution::execute() pnt.Transform(invObjLoc.Transformation()); dir.Transform(invObjLoc.Transformation()); base.move(invObjLoc); - sketchshape.Move(invObjLoc); + sketchshape.move(invObjLoc); // Check distance between sketchshape and axis - to avoid failures and crashes TopExp_Explorer xp; - xp.Init(sketchshape, TopAbs_FACE); - for (;xp.More(); xp.Next()) { - if (checkLineCrossesFace(gp_Lin(pnt, dir), TopoDS::Face(xp.Current()))) - return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Revolve axis intersects the sketch")); + xp.Init(sketchshape.getShape(), TopAbs_FACE); + for (; xp.More(); xp.Next()) { + if (checkLineCrossesFace(gp_Lin(pnt, dir), TopoDS::Face(xp.Current()))) { + return new App::DocumentObjectExecReturn( + QT_TRANSLATE_NOOP("Exception", "Revolve axis intersects the sketch")); + } } // Create a fresh support even when base exists so that it can be used for patterns +#ifdef FC_USE_TNP_FIX + TopoShape result; +#else TopoDS_Shape result; +#endif TopoDS_Face supportface = getSupportFace(); supportface.Move(invObjLoc); - if (method == RevolMethod::ToFace || method == RevolMethod::ToFirst || method == RevolMethod::ToLast) { + if (method == RevolMethod::ToFace || method == RevolMethod::ToFirst + || method == RevolMethod::ToLast) { TopoDS_Face upToFace; if (method == RevolMethod::ToFace) { getFaceFromLinkSub(upToFace, UpToFace); upToFace.Move(invObjLoc); } - else - throw Base::RuntimeError("ProfileBased: Revolution up to first/last is not yet supported"); + else { + throw Base::RuntimeError( + "ProfileBased: Revolution up to first/last is not yet supported"); + } // TODO: This method is designed for extrusions. needs to be adapted for revolutions. // getUpToFace(upToFace, base, supportface, sketchshape, method, dir); - TopoDS_Face supportface = getSupportFace(); + // TopoDS_Face supportface = getSupportFace(); supportface.Move(invObjLoc); - if (Reversed.getValue()) + if (Reversed.getValue()) { dir.Reverse(); + } - TopExp_Explorer Ex(supportface,TopAbs_WIRE); - if (!Ex.More()) + TopExp_Explorer Ex(supportface, TopAbs_WIRE); + if (!Ex.More()) { supportface = TopoDS_Face(); + } RevolMode mode = RevolMode::None; - generateRevolution(result, base.getShape(), sketchshape, supportface, upToFace, gp_Ax1(pnt, dir), method, mode, Standard_True); +#ifdef FC_USE_TNP_FIX + // revolve the face to a solid + // TopoShape result(0); + try { + result = base.makeElementRevolution(gp_Ax1(pnt, dir), angle, supportface, upToFace); + } + catch (Standard_Failure&) { + return new App::DocumentObjectExecReturn("Could not revolve the sketch!"); + } +#else + generateRevolution(result, + base.getShape(), + sketchshape.getShape(), + supportface, + upToFace, + gp_Ax1(pnt, dir), + method, + mode, + Standard_True); +#endif } else { bool midplane = Midplane.getValue(); bool reversed = Reversed.getValue(); - generateRevolution(result, sketchshape, gp_Ax1(pnt, dir), angle, angle2, midplane, reversed, method); + generateRevolution(result, + sketchshape.getShape(), + gp_Ax1(pnt, dir), + angle, + angle2, + midplane, + reversed, + method); } +#ifdef FC_USE_TNP_FIX + if (!result.isNull()) { + result = refineShapeIfActive(result); + // set the additive shape property for later usage in e.g. pattern + this->AddSubShape.setValue(result); + + if (!base.isNull()) { + result = result.makeElementFuse(base); + result = refineShapeIfActive(result); + } +#else if (!result.IsNull()) { result = refineShapeIfActive(result); // set the additive shape property for later usage in e.g. pattern @@ -186,16 +249,21 @@ App::DocumentObjectExecReturn *Revolution::execute() // Let's call algorithm computing a fuse operation: BRepAlgoAPI_Fuse mkFuse(base.getShape(), result); // Let's check if the fusion has been successful - if (!mkFuse.IsDone()) - throw Part::BooleanException(QT_TRANSLATE_NOOP("Exception", "Fusion with base feature failed")); + if (!mkFuse.IsDone()) { + throw Part::BooleanException( + QT_TRANSLATE_NOOP("Exception", "Fusion with base feature failed")); + } result = mkFuse.Shape(); result = refineShapeIfActive(result); } +#endif this->Shape.setValue(getSolid(result)); } - else - return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Could not revolve the sketch!")); + else { + return new App::DocumentObjectExecReturn( + QT_TRANSLATE_NOOP("Exception", "Could not revolve the sketch!")); + } // eventually disable some settings that are not valid for the current method updateProperties(method); @@ -204,11 +272,15 @@ App::DocumentObjectExecReturn *Revolution::execute() } catch (Standard_Failure& e) { - if (std::string(e.GetMessageString()) == "TopoDS::Face") - return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Could not create face from sketch.\n" - "Intersecting sketch entities in a sketch are not allowed.")); - else + if (std::string(e.GetMessageString()) == "TopoDS::Face") { + return new App::DocumentObjectExecReturn( + QT_TRANSLATE_NOOP("Exception", + "Could not create face from sketch.\n" + "Intersecting sketch entities in a sketch are not allowed.")); + } + else { return new App::DocumentObjectExecReturn(e.GetMessageString()); + } } catch (Base::Exception& e) { return new App::DocumentObjectExecReturn(e.what()); @@ -257,7 +329,12 @@ Revolution::RevolMethod Revolution::methodFromString(const std::string& methodSt return RevolMethod::Dimension; } +#ifdef FC_USE_TNP_FIX +void Revolution::generateRevolution(TopoShape& revol, +#else void Revolution::generateRevolution(TopoDS_Shape& revol, + +#endif const TopoDS_Shape& sketchshape, const gp_Ax1& axis, const double angle, @@ -267,46 +344,49 @@ void Revolution::generateRevolution(TopoDS_Shape& revol, RevolMethod method) { if (method == RevolMethod::Dimension || method == RevolMethod::TwoDimensions || method == RevolMethod::ThroughAll) { - double angleTotal = angle; - double angleOffset = 0.; + double angleTotal = angle; + double angleOffset = 0.; - if (method == RevolMethod::TwoDimensions) { - // Rotate the face by `angle2`/`angle` to get "second" angle - angleTotal += angle2; - angleOffset = angle2 * -1.0; - } - else if (midplane) { - // Rotate the face by half the angle to get Revolution symmetric to sketch plane - angleOffset = -angle / 2; - } + if (method == RevolMethod::TwoDimensions) { + // Rotate the face by `angle2`/`angle` to get "second" angle + angleTotal += angle2; + angleOffset = angle2 * -1.0; + } + else if (midplane) { + // Rotate the face by half the angle to get Revolution symmetric to sketch plane + angleOffset = -angle / 2; + } - if (fabs(angleTotal) < Precision::Angular()) - throw Base::ValueError("Cannot create a revolution with zero angle."); + if (fabs(angleTotal) < Precision::Angular()) + throw Base::ValueError("Cannot create a revolution with zero angle."); - gp_Ax1 revolAx(axis); - if (reversed) { - revolAx.Reverse(); - } + gp_Ax1 revolAx(axis); + if (reversed) { + revolAx.Reverse(); + } - TopoDS_Shape from = sketchshape; - if (method == RevolMethod::TwoDimensions || midplane) { - gp_Trsf mov; - mov.SetRotation(revolAx, angleOffset); - TopLoc_Location loc(mov); - from.Move(loc); - } + TopoDS_Shape from = sketchshape; + if (method == RevolMethod::TwoDimensions || midplane) { + gp_Trsf mov; + mov.SetRotation(revolAx, angleOffset); + TopLoc_Location loc(mov); + from.Move(loc); + } - // revolve the face to a solid - // BRepPrimAPI is the only option that allows use of this shape for patterns. - // See https://forum.freecadweb.org/viewtopic.php?f=8&t=70185&p=611673#p611673. - BRepPrimAPI_MakeRevol RevolMaker(from, revolAx, angleTotal); +#ifdef FC_USE_TNP_FIX + revol = TopoShape(from).makeElementRevolve(revolAx,angleTotal); +#else + // revolve the face to a solid + // BRepPrimAPI is the only option that allows use of this shape for patterns. + // See https://forum.freecadweb.org/viewtopic.php?f=8&t=70185&p=611673#p611673. + BRepPrimAPI_MakeRevol RevolMaker(from, revolAx, angleTotal); - if (!RevolMaker.IsDone()) - throw Base::RuntimeError("ProfileBased: RevolMaker failed! Could not revolve the sketch!"); - else - revol = RevolMaker.Shape(); - } - else { + if (!RevolMaker.IsDone()) + throw Base::RuntimeError("ProfileBased: RevolMaker failed! Could not revolve the sketch!"); + else + revol = RevolMaker.Shape(); +#endif + } else { std::stringstream str; str << "ProfileBased: Internal error: Unknown method for generateRevolution()"; throw Base::RuntimeError(str.str()); diff --git a/src/Mod/PartDesign/App/FeatureRevolution.h b/src/Mod/PartDesign/App/FeatureRevolution.h index bf80298875..583eb1b1fd 100644 --- a/src/Mod/PartDesign/App/FeatureRevolution.h +++ b/src/Mod/PartDesign/App/FeatureRevolution.h @@ -95,7 +95,11 @@ protected: /** * Generates a revolution of the input sketchshape and stores it in the given \a revol. */ +#ifdef FC_USE_TNP_FIX + void generateRevolution(TopoShape& revol, +#else void generateRevolution(TopoDS_Shape& revol, +#endif const TopoDS_Shape& sketchshape, const gp_Ax1& ax1, const double angle, diff --git a/src/Mod/PartDesign/PartDesignTests/TestTopologicalNamingProblem.py b/src/Mod/PartDesign/PartDesignTests/TestTopologicalNamingProblem.py index a027aca68b..0b67045ec7 100644 --- a/src/Mod/PartDesign/PartDesignTests/TestTopologicalNamingProblem.py +++ b/src/Mod/PartDesign/PartDesignTests/TestTopologicalNamingProblem.py @@ -498,14 +498,7 @@ class TestTopologicalNamingProblem(unittest.TestCase): self.Doc.recompute() # Assert # self.assertEqual(len(body.Shape.childShapes()), 1) - if App.GuiUp: - # Todo: This triggers a 'hasher mismatch' warning in TopoShape::mapSubElement as called by - # flushElementMap. This appears to be the case whenever you have a parent with a hasher - # that has children without hashmaps. The warning seems to be spurious in this case, but - # perhaps there is a solution involving setting hashmaps on all elements. - self.assertEqual(body.Shape.childShapes()[0].ElementMapSize, 30) - else: - self.assertEqual(body.Shape.childShapes()[0].ElementMapSize, 26) + self.assertEqual(body.Shape.childShapes()[0].ElementMapSize, 30) self.assertEqual(body.Shape.ElementMapSize,30) self.assertEqual(sketch.Shape.ElementMapSize,12) self.assertEqual(pad.Shape.ElementMapSize,30) @@ -520,7 +513,7 @@ class TestTopologicalNamingProblem(unittest.TestCase): return # Act revolution = self.Doc.addObject('PartDesign::Revolution', 'Revolution') - revolution.ReferenceAxis = body.Origin.OriginFeatures[1] + revolution.ReferenceAxis = (self.Doc.getObject('Sketch'),['V_Axis']) revolution.Profile = sketch # Causing segfault body.addObject(sketch) body.addObject(revolution) @@ -582,7 +575,7 @@ class TestTopologicalNamingProblem(unittest.TestCase): # Act helix = self.Doc.addObject('PartDesign::AdditiveHelix', 'Helix') helix.Profile = sketch - helix.ReferenceAxis = body.Origin.OriginFeatures[2] + helix.ReferenceAxis = (self.Doc.getObject('Sketch'),['V_Axis']) body.addObject(sketch) body.addObject(helix) self.Doc.recompute() @@ -789,6 +782,166 @@ class TestTopologicalNamingProblem(unittest.TestCase): self.assertEqual(plane.Shape.ElementMapSize, 0) self.assertEqual(pad.Shape.ElementMapSize, 26) + # def testChangeSketch(self): + # # Arrange + # self.Body = self.Doc.addObject('PartDesign::Body', 'Body') + # # Make first offset cube Pad + # self.PadSketch = self.Doc.addObject('Sketcher::SketchObject', 'Sketch') + # self.Body.addObject(self.PadSketch) + # TestSketcherApp.CreateRectangleSketch(self.PadSketch, (0, 0), (31.37, 25.2)) + # self.Doc.recompute() + # self.Pad = self.Doc.addObject("PartDesign::Pad", "Pad") + # self.Body.addObject(self.Pad) + # self.Pad.Profile = self.PadSketch + # self.Pad.Length = 10 + # self.Doc.recompute() + # + # self.Sketch001 = self.Body.newObject('Sketcher::SketchObject','Sketch001') + # self.Sketch001.AttachmentSupport = (self.Doc.getObject('Pad'),['Face6',]) + # self.Sketch001.MapMode = 'FlatFace' + # App.ActiveDocument.recompute() + # + # self.Sketch001.addExternal("Pad","Edge10") + # self.Sketch001.addExternal("Pad","Edge7") + # + # geoList = [] + # geoList.append(Part.Circle(App.Vector(15.093666, 13.036922, 0.000000), + # App.Vector(0.000000, 0.000000, 1.000000), 5.000000)) + # self.Sketch001.addGeometry(geoList,False) + # del geoList + # self.Sketch001.addConstraint(Sketcher.Constraint('Radius',0,5.000000)) + # self.Sketch001.addConstraint(Sketcher.Constraint('Symmetric',-3,2,-4,1,0,3)) + # App.ActiveDocument.recompute() + # self.Doc.recompute() + # + # self.Pad001 = self.Body.newObject('PartDesign::Pad','Pad001') + # self.Pad001.Profile = self.Doc.getObject('Sketch001') + # self.Pad001.Length = 10 + # App.ActiveDocument.recompute() + # self.Pad001.ReferenceAxis = (self.Doc.getObject('Sketch001'),['N_Axis']) + # self.Sketch001.Visibility = False + # App.ActiveDocument.recompute() + # + # self.Pad001.Length = 10.000000 + # self.Pad001.TaperAngle = 0.000000 + # self.Pad001.UseCustomVector = 0 + # self.Pad001.Direction = (0, 0, 1) + # self.Pad001.ReferenceAxis = (self.Doc.getObject('Sketch001'), ['N_Axis']) + # self.Pad001.AlongSketchNormal = 1 + # self.Pad001.Type = 0 + # self.Pad001.UpToFace = None + # self.Pad001.Reversed = 0 + # self.Pad001.Midplane = 0 + # self.Pad001.Offset = 0 + # self.Doc.recompute() + # self.Doc.getObject('Pad').Visibility = False + # + # self.Doc.getObject('Sketch001').Visibility = False + # + # # Modify the original sketch to generate TNP issue + # geoList = [] + # geoList.append(Part.LineSegment(App.Vector(2.510468, 22.837425, 0.000000), + # App.Vector(2.510468, 19.933617, 0.000000))) + # geoList.append(Part.LineSegment(App.Vector(2.510468, 19.933617, 0.000000), + # App.Vector(4.869811, 19.933617, 0.000000))) + # geoList.append(Part.LineSegment(App.Vector(4.869811, 19.933617, 0.000000), + # App.Vector(4.869811, 22.837425, 0.000000))) + # geoList.append(Part.LineSegment(App.Vector(4.869811, 22.837425, 0.000000), + # App.Vector(2.510468, 22.837425, 0.000000))) + # self.PadSketch.addGeometry(geoList,False) + # del geoList + # + # constraintList = [] + # constraintList.append(Sketcher.Constraint('Coincident', 4, 2, 5, 1)) + # constraintList.append(Sketcher.Constraint('Coincident', 5, 2, 6, 1)) + # constraintList.append(Sketcher.Constraint('Coincident', 6, 2, 7, 1)) + # constraintList.append(Sketcher.Constraint('Coincident', 7, 2, 4, 1)) + # constraintList.append(Sketcher.Constraint('Vertical', 4)) + # constraintList.append(Sketcher.Constraint('Vertical', 6)) + # constraintList.append(Sketcher.Constraint('Horizontal', 5)) + # constraintList.append(Sketcher.Constraint('Horizontal', 7)) + # self.PadSketch.addConstraint(constraintList) + # del constraintList + # self.Doc.recompute() + # # Assert + # if self.Body.Shape.ElementMapVersion == "": # Should be '4' as of Mar 2023. + # return + # self.assertEqual(self.Body.Shape.BoundBox.XMin,0) + # self.assertEqual(self.Body.Shape.BoundBox.YMin,0) + # self.assertEqual(self.Body.Shape.BoundBox.ZMin,0) + # self.assertEqual(self.Body.Shape.BoundBox.XMax,31.37) + # self.assertEqual(self.Body.Shape.BoundBox.YMax,25.2) + # self.assertEqual(self.Body.Shape.BoundBox.ZMax,20) + # + # def testApplyFillet(self): + # # Arrange + # self.Body = self.Doc.addObject('PartDesign::Body', 'Body') + # # Make first offset cube Pad + # self.PadSketch = self.Doc.addObject('Sketcher::SketchObject', 'Sketch') + # self.Body.addObject(self.PadSketch) + # TestSketcherApp.CreateRectangleSketch(self.PadSketch, (0, 0), (31.37, 25.2)) + # self.Doc.recompute() + # self.Pad = self.Doc.addObject("PartDesign::Pad", "Pad") + # self.Body.addObject(self.Pad) + # self.Pad.Profile = self.PadSketch + # self.Pad.Length = 10 + # self.Doc.recompute() + # + # self.Sketch001 = self.Body.newObject('Sketcher::SketchObject','Sketch001') + # self.Sketch001.AttachmentSupport = (self.Doc.getObject('Pad'),['Face6',]) + # self.Sketch001.MapMode = 'FlatFace' + # App.ActiveDocument.recompute() + # + # self.Sketch001.addExternal("Pad","Edge10") + # self.Sketch001.addExternal("Pad","Edge7") + # + # geoList = [] + # geoList.append(Part.Circle(App.Vector(15.093666, 13.036922, 0.000000), + # App.Vector(0.000000, 0.000000, 1.000000), 5.000000)) + # self.Sketch001.addGeometry(geoList,False) + # del geoList + # self.Sketch001.addConstraint(Sketcher.Constraint('Radius',0,5.000000)) + # self.Sketch001.addConstraint(Sketcher.Constraint('Symmetric',-3,2,-4,1,0,3)) + # App.ActiveDocument.recompute() + # self.Doc.recompute() + # + # self.Pad001 = self.Body.newObject('PartDesign::Pad','Pad001') + # self.Pad001.Profile = self.Doc.getObject('Sketch001') + # self.Pad001.Length = 10 + # App.ActiveDocument.recompute() + # self.Pad001.ReferenceAxis = (self.Doc.getObject('Sketch001'),['N_Axis']) + # self.Sketch001.Visibility = False + # App.ActiveDocument.recompute() + # + # self.Pad001.Length = 10.000000 + # self.Pad001.TaperAngle = 0.000000 + # self.Pad001.UseCustomVector = 0 + # self.Pad001.Direction = (0, 0, 1) + # self.Pad001.ReferenceAxis = (self.Doc.getObject('Sketch001'), ['N_Axis']) + # self.Pad001.AlongSketchNormal = 1 + # self.Pad001.Type = 0 + # self.Pad001.UpToFace = None + # self.Pad001.Reversed = 0 + # self.Pad001.Midplane = 0 + # self.Pad001.Offset = 0 + # self.Doc.recompute() + # self.Doc.getObject('Pad').Visibility = False + # + # self.Doc.getObject('Sketch001').Visibility = False + # filleted = self.Pad001.Shape.makeFillet(1,self.Pad001.Shape.Edges[0:2]) + # self.filleted = Part.show(filleted,"Filleted") + # # self.Body.addObject(self.filleted) + # self.Doc.recompute() + # # Assert + # if self.Body.Shape.ElementMapVersion == "": # Should be '4' as of Mar 2023. + # return + # self.assertEqual(self.Body.Shape.BoundBox.XMin,0) + # self.assertEqual(self.Body.Shape.BoundBox.YMin,0) + # self.assertEqual(self.Body.Shape.BoundBox.ZMin,0) + # self.assertEqual(self.Body.Shape.BoundBox.XMax,31.37) + # self.assertEqual(self.Body.Shape.BoundBox.YMax,25.2) + # self.assertEqual(self.Body.Shape.BoundBox.ZMax,20) + def create_t_sketch(self): self.Doc.getObject('Body').newObject('Sketcher::SketchObject', 'Sketch') geo_list = [