| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | #include <limits> |
| | #include <Mod/Part/App/FCBRepAlgoAPI_Fuse.h> |
| | #include <BRep_Builder.hxx> |
| | #include <BRepFeat_MakePrism.hxx> |
| | #include <BRepPrimAPI_MakePrism.hxx> |
| | #include <gp_Dir.hxx> |
| | #include <gp_Ax2.hxx> |
| | #include <Precision.hxx> |
| | #include <TopExp_Explorer.hxx> |
| | #include <TopoDS_Compound.hxx> |
| | #include <TopoDS_Face.hxx> |
| | #include <TopoDS_Shape.hxx> |
| |
|
| | #include <App/Document.h> |
| | #include <Base/Tools.h> |
| | #include <Mod/Part/App/ExtrusionHelper.h> |
| | #include "Mod/Part/App/TopoShapeOpCode.h" |
| | #include <Mod/Part/App/PartFeature.h> |
| |
|
| | #include "FeatureExtrude.h" |
| |
|
| | FC_LOG_LEVEL_INIT("PartDesign", true, true) |
| |
|
| | using namespace PartDesign; |
| |
|
| | const char* FeatureExtrude::SideTypesEnums[] = {"One side", "Two sides", "Symmetric", nullptr}; |
| |
|
| | PROPERTY_SOURCE(PartDesign::FeatureExtrude, PartDesign::ProfileBased) |
| |
|
| | App::PropertyQuantityConstraint::Constraints FeatureExtrude::signedLengthConstraint |
| | = {-std::numeric_limits<double>::max(), std::numeric_limits<double>::max(), 1.0}; |
| | double FeatureExtrude::maxAngle = 90 - Base::toDegrees<double>(Precision::Angular()); |
| | App::PropertyAngle::Constraints FeatureExtrude::floatAngle = {-maxAngle, maxAngle, 1.0}; |
| |
|
| | FeatureExtrude::FeatureExtrude() = default; |
| |
|
| | short FeatureExtrude::mustExecute() const |
| | { |
| | if (Placement.isTouched() || SideType.isTouched() || Type.isTouched() || Type2.isTouched() |
| | || Length.isTouched() || Length2.isTouched() || TaperAngle.isTouched() |
| | || TaperAngle2.isTouched() || UseCustomVector.isTouched() || Direction.isTouched() |
| | || ReferenceAxis.isTouched() || AlongSketchNormal.isTouched() || Offset.isTouched() |
| | || Offset2.isTouched() || UpToFace.isTouched() || UpToFace2.isTouched() |
| | || UpToShape.isTouched() || UpToShape2.isTouched()) { |
| | return 1; |
| | } |
| | return ProfileBased::mustExecute(); |
| | } |
| |
|
| | Base::Vector3d FeatureExtrude::computeDirection(const Base::Vector3d& sketchVector, bool inverse) |
| | { |
| | (void)inverse; |
| | Base::Vector3d extrudeDirection; |
| |
|
| | if (!UseCustomVector.getValue()) { |
| | if (!ReferenceAxis.getValue()) { |
| | |
| | extrudeDirection = sketchVector; |
| | AlongSketchNormal.setReadOnly(true); |
| | } |
| | else { |
| | |
| | App::DocumentObject* pcReferenceAxis = ReferenceAxis.getValue(); |
| | const std::vector<std::string>& subReferenceAxis = ReferenceAxis.getSubValues(); |
| | Base::Vector3d base; |
| | Base::Vector3d dir; |
| | getAxis(pcReferenceAxis, subReferenceAxis, base, dir, ForbiddenAxis::NotPerpendicularWithNormal); |
| | switch (addSubType) { |
| | case Type::Additive: |
| | extrudeDirection = dir; |
| | break; |
| | case Type::Subtractive: |
| | extrudeDirection = -dir; |
| | break; |
| | } |
| | } |
| | } |
| | else { |
| | |
| | |
| | if ((fabs(Direction.getValue().x) < Precision::Confusion()) |
| | && (fabs(Direction.getValue().y) < Precision::Confusion()) |
| | && (fabs(Direction.getValue().z) < Precision::Confusion())) { |
| | Direction.setValue(sketchVector); |
| | } |
| | extrudeDirection = Direction.getValue(); |
| | } |
| |
|
| | |
| | Direction.setReadOnly(!UseCustomVector.getValue()); |
| | ReferenceAxis.setReadOnly(UseCustomVector.getValue()); |
| | |
| | if (UseCustomVector.getValue()) { |
| | AlongSketchNormal.setReadOnly(false); |
| | } |
| |
|
| | |
| | |
| | Direction.setValue(extrudeDirection); |
| | return extrudeDirection; |
| | } |
| |
|
| | bool FeatureExtrude::hasTaperedAngle() const |
| | { |
| | return fabs(TaperAngle.getValue()) > Base::toRadians(Precision::Angular()) |
| | || fabs(TaperAngle2.getValue()) > Base::toRadians(Precision::Angular()); |
| | } |
| |
|
| | void FeatureExtrude::onChanged(const App::Property* prop) |
| | { |
| | if (!isRestoring() && prop == &Midplane) { |
| | |
| | |
| | App::DocumentObject* obj = Profile.getValue(); |
| | auto baseName = obj ? obj->getNameInDocument() : ""; |
| | Base::Console().warning( |
| | "The 'Midplane' property being set for the extrusion of %s is deprecated and has " |
| | "been replaced by the 'SideType' property in FeatureExtrude. Please update your script," |
| | " this property will be removed in a future version.\n", |
| | baseName |
| | ); |
| | if (Midplane.getValue()) { |
| | SideType.setValue("Symmetric"); |
| | } |
| | else { |
| | Base::Console() |
| | .warning("Deprecated Midplane property was explicitly set to False: assuming SideType='One side'\n"); |
| | SideType.setValue("One side"); |
| | } |
| | } |
| | ProfileBased::onChanged(prop); |
| | } |
| |
|
| | TopoShape FeatureExtrude::makeShellFromUpToShape(TopoShape shape, TopoShape sketchshape, gp_Dir dir) |
| | { |
| |
|
| | |
| | std::vector<Part::cutTopoShapeFaces> cfaces = Part::findAllFacesCutBy(shape, sketchshape, dir); |
| | if (cfaces.empty()) { |
| | dir = -dir; |
| | cfaces = Part::findAllFacesCutBy(shape, sketchshape, dir); |
| | } |
| |
|
| | if (cfaces.empty()) { |
| | return shape; |
| | } |
| |
|
| | struct Part::cutTopoShapeFaces* nearFace {}; |
| | struct Part::cutTopoShapeFaces* farFace {}; |
| | nearFace = farFace = &cfaces.front(); |
| | for (auto& face : cfaces) { |
| | if (face.distsq > farFace->distsq) { |
| | farFace = &face; |
| | } |
| | else if (face.distsq < nearFace->distsq) { |
| | nearFace = &face; |
| | } |
| | } |
| |
|
| | if (nearFace != farFace) { |
| | std::vector<TopoShape> faceList; |
| | for (auto& face : shape.getSubTopoShapes(TopAbs_FACE)) { |
| | if (!(face == farFace->face)) { |
| | |
| | |
| | faceList.push_back(face); |
| | } |
| | } |
| | return shape.makeElementCompound(faceList); |
| | } |
| | return shape; |
| | } |
| |
|
| | void FeatureExtrude::updateProperties() |
| | { |
| | std::string sideTypeVal = SideType.getValueAsString(); |
| | std::string methodSide1 = Type.getValueAsString(); |
| | std::string methodSide2 = Type2.getValueAsString(); |
| |
|
| | bool isLength1Enabled = false; |
| | bool isTaper1Visible = false; |
| | bool isUpToFace1Enabled = false; |
| | bool isUpToShape1Enabled = false; |
| | bool isOffset1Enabled = false; |
| |
|
| | bool isType2Enabled = false; |
| | bool isLength2Enabled = false; |
| | bool isTaper2Visible = false; |
| | bool isUpToFace2Enabled = false; |
| | bool isUpToShape2Enabled = false; |
| | bool isOffset2Enabled = false; |
| |
|
| | bool currentAlongSketchNormalEnabled = false; |
| |
|
| | auto configureSideProperties = [&](const std::string& method, |
| | bool& lengthEnabled, |
| | bool& taperVisible, |
| | bool& upToFaceEnabled, |
| | bool& upToShapeEnabled, |
| | bool& localAlongSketchNormal, |
| | bool& localOffset) { |
| | if (method == "Length") { |
| | lengthEnabled = true; |
| | taperVisible = true; |
| | localAlongSketchNormal = true; |
| | } |
| | else if (method == "UpToFace") { |
| | upToFaceEnabled = true; |
| | localOffset = true; |
| | } |
| | else if (method == "UpToShape") { |
| | upToShapeEnabled = true; |
| | localOffset = true; |
| | } |
| | else if (method == "UpToLast" || method == "UpToFirst") { |
| | localOffset = true; |
| | } |
| | else if (method == "ThroughAll") { |
| | taperVisible = true; |
| | } |
| | }; |
| |
|
| | if (sideTypeVal == "One side") { |
| | bool side1ASN = false; |
| | configureSideProperties( |
| | methodSide1, |
| | isLength1Enabled, |
| | isTaper1Visible, |
| | isUpToFace1Enabled, |
| | isUpToShape1Enabled, |
| | side1ASN, |
| | isOffset1Enabled |
| | ); |
| | currentAlongSketchNormalEnabled = side1ASN; |
| | } |
| | else if (sideTypeVal == "Two sides") { |
| | isType2Enabled = true; |
| |
|
| | bool side1ASN = false; |
| | configureSideProperties( |
| | methodSide1, |
| | isLength1Enabled, |
| | isTaper1Visible, |
| | isUpToFace1Enabled, |
| | isUpToShape1Enabled, |
| | side1ASN, |
| | isOffset1Enabled |
| | ); |
| |
|
| | bool side2ASN = false; |
| | configureSideProperties( |
| | methodSide2, |
| | isLength2Enabled, |
| | isTaper2Visible, |
| | isUpToFace2Enabled, |
| | isUpToShape2Enabled, |
| | side2ASN, |
| | isOffset2Enabled |
| | ); |
| |
|
| | currentAlongSketchNormalEnabled = side1ASN || side2ASN; |
| | } |
| | else if (sideTypeVal == "Symmetric") { |
| | bool symASN = false; |
| | configureSideProperties( |
| | methodSide1, |
| | isLength1Enabled, |
| | isTaper1Visible, |
| | isUpToFace1Enabled, |
| | isUpToShape1Enabled, |
| | symASN, |
| | isOffset1Enabled |
| | ); |
| | currentAlongSketchNormalEnabled = symASN; |
| | } |
| |
|
| | Length.setReadOnly(!isLength1Enabled); |
| | TaperAngle.setReadOnly(!isTaper1Visible); |
| | UpToFace.setReadOnly(!isUpToFace1Enabled); |
| | UpToShape.setReadOnly(!isUpToShape1Enabled); |
| | Offset.setReadOnly(!isOffset1Enabled); |
| |
|
| | Type2.setReadOnly(!isType2Enabled); |
| | Length2.setReadOnly(!isLength2Enabled); |
| | TaperAngle2.setReadOnly(!isTaper2Visible); |
| | UpToFace2.setReadOnly(!isUpToFace2Enabled); |
| | UpToShape2.setReadOnly(!isUpToShape2Enabled); |
| | Offset2.setReadOnly(!isOffset2Enabled); |
| |
|
| | AlongSketchNormal.setReadOnly(!currentAlongSketchNormalEnabled); |
| | } |
| |
|
| | void FeatureExtrude::setupObject() |
| | { |
| | ProfileBased::setupObject(); |
| | } |
| |
|
| | App::DocumentObjectExecReturn* FeatureExtrude::buildExtrusion(ExtrudeOptions options) |
| | { |
| | if (onlyHaveRefined()) { |
| | return App::DocumentObject::StdReturn; |
| | } |
| |
|
| |
|
| | bool makeface = options.testFlag(ExtrudeOption::MakeFace); |
| | bool fuse = options.testFlag(ExtrudeOption::MakeFuse); |
| | bool inverseDirection = options.testFlag(ExtrudeOption::InverseDirection); |
| |
|
| | std::string Sidemethod(SideType.getValueAsString()); |
| | std::string method(Type.getValueAsString()); |
| | std::string method2(Type2.getValueAsString()); |
| |
|
| | |
| | double L = method == "ThroughAll" ? getThroughAllLength() |
| | : method == "Length" ? Length.getValue() |
| | : 0.0; |
| | double L2 = Sidemethod == "Two sides" ? method2 == "ThroughAll" ? getThroughAllLength() |
| | : method2 == "Length" ? Length2.getValue() |
| | : 0.0 |
| | : 0.0; |
| |
|
| | if ((Sidemethod == "One side" && method == "Length") |
| | || (Sidemethod == "Two sides" && method == "Length" && method2 == "Length")) { |
| |
|
| | if (std::abs(L + L2) < Precision::Confusion()) { |
| | if (addSubType == Type::Additive) { |
| | return new App::DocumentObjectExecReturn( |
| | QT_TRANSLATE_NOOP("Exception", "Cannot create a pad with a total length of zero.") |
| | ); |
| | } |
| | else { |
| | return new App::DocumentObjectExecReturn( |
| | QT_TRANSLATE_NOOP("Exception", "Cannot create a pocket with a total length of zero.") |
| | ); |
| | } |
| | } |
| | } |
| |
|
| | Part::Feature* obj = nullptr; |
| | TopoShape sketchshape; |
| | try { |
| | obj = getVerifiedObject(); |
| | if (makeface) { |
| | sketchshape = getTopoShapeVerifiedFace(); |
| | } |
| | else { |
| | std::vector<TopoShape> shapes; |
| | bool hasEdges = false; |
| | auto subs = Profile.getSubValues(false); |
| | if (subs.empty()) { |
| | subs.emplace_back(""); |
| | } |
| | bool failed = false; |
| | for (auto& sub : subs) { |
| | if (sub.empty() && subs.size() > 1) { |
| | continue; |
| | } |
| | TopoShape shape = Part::Feature::getTopoShape( |
| | obj, |
| | Part::ShapeOption::NeedSubElement | Part::ShapeOption::ResolveLink |
| | | Part::ShapeOption::Transform, |
| | sub.c_str() |
| | ); |
| |
|
| | if (shape.isNull()) { |
| | FC_ERR( |
| | getFullName() |
| | << ": failed to get profile shape " << obj->getFullName() << "." << sub |
| | ); |
| | failed = true; |
| | } |
| | hasEdges = hasEdges || shape.hasSubShape(TopAbs_EDGE); |
| | shapes.push_back(shape); |
| | } |
| | if (failed) { |
| | return new App::DocumentObjectExecReturn( |
| | QT_TRANSLATE_NOOP("Exception", "Failed to obtain profile shape") |
| | ); |
| | } |
| | if (hasEdges) { |
| | sketchshape.makeElementWires(shapes); |
| | } |
| | else { |
| | sketchshape.makeElementCompound( |
| | shapes, |
| | nullptr, |
| | TopoShape::SingleShapeCompoundCreationPolicy::returnShape |
| | ); |
| | } |
| | } |
| | } |
| | catch (const Base::Exception& e) { |
| | return new App::DocumentObjectExecReturn(e.what()); |
| | } |
| | catch (const Standard_Failure& e) { |
| | return new App::DocumentObjectExecReturn(e.GetMessageString()); |
| | } |
| |
|
| | |
| | TopoShape base = getBaseTopoShape(true); |
| |
|
| | |
| | Base::Vector3d SketchVector = getProfileNormal(); |
| |
|
| | try { |
| | this->positionByPrevious(); |
| | auto invObjLoc = getLocation().Inverted(); |
| |
|
| | auto invTrsf = invObjLoc.Transformation(); |
| |
|
| | base.move(invObjLoc); |
| |
|
| | Base::Vector3d paddingDirection = computeDirection(SketchVector, inverseDirection); |
| |
|
| | |
| | gp_Dir dir(paddingDirection.x, paddingDirection.y, paddingDirection.z); |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | double factor = fabs(dir * gp_Dir(SketchVector.x, SketchVector.y, SketchVector.z)); |
| |
|
| | |
| | if (factor < Precision::Confusion()) { |
| | return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP( |
| | "Exception", |
| | "Creation failed because direction is orthogonal to sketch's normal vector" |
| | )); |
| | } |
| |
|
| | |
| | if (AlongSketchNormal.getValue()) { |
| | L = L / factor; |
| | L2 = L2 / factor; |
| | } |
| |
|
| | |
| | |
| | Direction.setValue(paddingDirection); |
| |
|
| | dir.Transform(invTrsf); |
| | if (Reversed.getValue()) { |
| | dir.Reverse(); |
| | } |
| |
|
| | if (sketchshape.isNull()) { |
| | return new App::DocumentObjectExecReturn( |
| | QT_TRANSLATE_NOOP("Exception", "Creating a face from sketch failed") |
| | ); |
| | } |
| | sketchshape.move(invObjLoc); |
| |
|
| | std::vector<TopoShape> prisms; |
| | double taper1 = TaperAngle.getValue(); |
| | double offset1 = Offset.getValue(); |
| |
|
| | if (Sidemethod == "One side") { |
| | TopoShape prism1 = generateSingleExtrusionSide( |
| | sketchshape, |
| | method, |
| | L, |
| | taper1, |
| | UpToFace, |
| | UpToShape, |
| | dir, |
| | offset1, |
| | makeface, |
| | base, |
| | invObjLoc |
| | ); |
| | prisms.push_back(prism1); |
| | } |
| | else if (Sidemethod == "Symmetric") { |
| | |
| | |
| | if (method == "Length") { |
| | if (std::fabs(taper1) > Precision::Angular()) { |
| | |
| | |
| | |
| | L /= 2.0; |
| | TopoShape prism1 = generateSingleExtrusionSide( |
| | sketchshape.makeElementCopy(), |
| | method, |
| | L, |
| | taper1, |
| | UpToFace, |
| | UpToShape, |
| | dir, |
| | offset1, |
| | makeface, |
| | base, |
| | invObjLoc |
| | ); |
| | if (!prism1.isNull() && !prism1.getShape().IsNull()) { |
| | prisms.push_back(prism1); |
| | } |
| |
|
| | gp_Dir dir2 = dir; |
| | dir2.Reverse(); |
| | TopoShape prism2 = generateSingleExtrusionSide( |
| | sketchshape.makeElementCopy(), |
| | method, |
| | L, |
| | taper1, |
| | UpToFace, |
| | UpToShape, |
| | dir2, |
| | offset1, |
| | makeface, |
| | base, |
| | invObjLoc |
| | ); |
| | if (!prism2.isNull() && !prism2.getShape().IsNull()) { |
| | prisms.push_back(prism2); |
| | } |
| | } |
| | else { |
| | |
| | |
| | |
| | gp_Trsf start_transform; |
| | start_transform.SetTranslation(gp_Vec(dir).Reversed() * (L / 2.0)); |
| |
|
| | TopoShape moved_sketch = sketchshape.makeElementCopy(); |
| | moved_sketch.move(start_transform); |
| |
|
| | TopoShape prism1 = generateSingleExtrusionSide( |
| | moved_sketch, |
| | method, |
| | L, |
| | taper1, |
| | UpToFace, |
| | UpToShape, |
| | dir, |
| | offset1, |
| | makeface, |
| | base, |
| | invObjLoc |
| | ); |
| | if (!prism1.isNull() && !prism1.getShape().IsNull()) { |
| | prisms.push_back(prism1); |
| | } |
| | } |
| | } |
| | else { |
| | |
| | TopoShape prism1 = generateSingleExtrusionSide( |
| | sketchshape, |
| | method, |
| | L, |
| | taper1, |
| | UpToFace, |
| | UpToShape, |
| | dir, |
| | offset1, |
| | makeface, |
| | base, |
| | invObjLoc |
| | ); |
| | prisms.push_back(prism1); |
| |
|
| | |
| | |
| | gp_Dir sketchNormalDir(SketchVector.x, SketchVector.y, SketchVector.z); |
| | sketchNormalDir.Transform(invTrsf); |
| |
|
| | Base::Vector3d sketchCenter = sketchshape.getBoundBox().GetCenter(); |
| | gp_Ax2 mirrorPlane( |
| | gp_Pnt(sketchCenter.x, sketchCenter.y, sketchCenter.z), |
| | sketchNormalDir |
| | ); |
| | TopoShape prism2 = prism1.makeElementMirror(mirrorPlane); |
| | prisms.push_back(prism2); |
| | } |
| | } |
| | else if (Sidemethod == "Two sides") { |
| | double taper2 = TaperAngle2.getValue(); |
| | double offset2 = Offset2.getValue(); |
| | gp_Dir dir2 = dir; |
| | dir2.Reverse(); |
| | bool noTaper = std::fabs(taper1) < Precision::Angular() |
| | && std::fabs(taper2) < Precision::Angular(); |
| | bool method1LengthBased = method == "Length" || method == "ThroughAll"; |
| | bool method2LengthBased = method2 == "Length" || method2 == "ThroughAll"; |
| |
|
| | if (method1LengthBased && method2 != "UpToFirst" && noTaper) { |
| | gp_Trsf start_transform; |
| | start_transform.SetTranslation(gp_Vec(dir) * L); |
| |
|
| | TopoShape moved_sketch = sketchshape.makeElementCopy(); |
| | moved_sketch.move(start_transform); |
| | TopoShape prism = generateSingleExtrusionSide( |
| | moved_sketch, |
| | method2, |
| | L + L2, |
| | 0.0, |
| | UpToFace2, |
| | UpToShape2, |
| | dir2, |
| | offset2, |
| | makeface, |
| | base, |
| | invObjLoc |
| | ); |
| | if (!prism.isNull() && !prism.getShape().IsNull()) { |
| | prisms.push_back(prism); |
| | } |
| | } |
| | else if (method2LengthBased && method != "UpToFirst" && noTaper) { |
| | gp_Trsf start_transform; |
| | start_transform.SetTranslation(gp_Vec(dir).Reversed() * L2); |
| |
|
| | TopoShape moved_sketch = sketchshape.makeElementCopy(); |
| | moved_sketch.move(start_transform); |
| | TopoShape prism = generateSingleExtrusionSide( |
| | moved_sketch, |
| | method, |
| | L + L2, |
| | 0.0, |
| | UpToFace, |
| | UpToShape, |
| | dir, |
| | offset1, |
| | makeface, |
| | base, |
| | invObjLoc |
| | ); |
| | if (!prism.isNull() && !prism.getShape().IsNull()) { |
| | prisms.push_back(prism); |
| | } |
| | } |
| | else { |
| | TopoShape prism1 = generateSingleExtrusionSide( |
| | sketchshape.makeElementCopy(), |
| | method, |
| | L, |
| | taper1, |
| | UpToFace, |
| | UpToShape, |
| | dir, |
| | offset1, |
| | makeface, |
| | base, |
| | invObjLoc |
| | ); |
| | if (!prism1.isNull() && !prism1.getShape().IsNull()) { |
| | prisms.push_back(prism1); |
| | } |
| |
|
| | |
| | TopoShape prism2 = generateSingleExtrusionSide( |
| | sketchshape.makeElementCopy(), |
| | method2, |
| | L2, |
| | taper2, |
| | UpToFace2, |
| | UpToShape2, |
| | dir2, |
| | offset2, |
| | makeface, |
| | base, |
| | invObjLoc |
| | ); |
| | if (!prism2.isNull() && !prism2.getShape().IsNull()) { |
| | prisms.push_back(prism2); |
| | } |
| | } |
| | } |
| |
|
| | |
| | TopoShape prism(0, getDocument()->getStringHasher()); |
| | if (prisms.empty()) { |
| | return new App::DocumentObjectExecReturn( |
| | QT_TRANSLATE_NOOP("Exception", "No extrusion geometry was generated.") |
| | ); |
| | } |
| | else if (prisms.size() == 1) { |
| | prism = prisms[0]; |
| | } |
| | else { |
| | try { |
| | prism.makeElementXor(prisms, Part::OpCodes::Extrude); |
| | } |
| | catch (const Standard_Failure& e) { |
| | return new App::DocumentObjectExecReturn( |
| | std::string("Failed to xor extrusion sides (OCC): ") + e.GetMessageString() |
| | ); |
| | } |
| | catch (const Base::Exception& e) { |
| | return new App::DocumentObjectExecReturn( |
| | std::string("Failed to xor extrusion sides: ") + e.what() |
| | ); |
| | } |
| | } |
| |
|
| | if (prism.isNull()) { |
| | return new App::DocumentObjectExecReturn( |
| | QT_TRANSLATE_NOOP("Exception", "Resulting fused extrusion is null.") |
| | ); |
| | } |
| |
|
| | |
| | this->rawShape = prism; |
| | prism = refineShapeIfActive(prism); |
| | |
| | this->AddSubShape.setValue(prism); |
| |
|
| | if (base.shapeType(true) <= TopAbs_SOLID && fuse) { |
| | prism.Tag = -this->getID(); |
| |
|
| | |
| | TopoShape result(0, getDocument()->getStringHasher()); |
| | try { |
| | const char* maker; |
| | switch (getAddSubType()) { |
| | case Subtractive: |
| | maker = Part::OpCodes::Cut; |
| | break; |
| | default: |
| | maker = Part::OpCodes::Fuse; |
| | } |
| | result.makeElementBoolean(maker, {base, prism}); |
| | } |
| | catch (Standard_Failure&) { |
| | return new App::DocumentObjectExecReturn( |
| | QT_TRANSLATE_NOOP("Exception", "Fusion with base feature failed") |
| | ); |
| | } |
| | |
| | auto solRes = this->getSolid(result); |
| | |
| | if (solRes.isNull()) { |
| | return new App::DocumentObjectExecReturn( |
| | QT_TRANSLATE_NOOP("Exception", "Resulting shape is not a solid") |
| | ); |
| | } |
| |
|
| | |
| | this->rawShape = result; |
| | solRes = refineShapeIfActive(result); |
| |
|
| | if (!isSingleSolidRuleSatisfied(solRes.getShape())) { |
| | return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP( |
| | "Exception", |
| | "Result has multiple solids: enable 'Allow Compound' in the active body." |
| | )); |
| | } |
| | this->Shape.setValue(getSolid(solRes)); |
| | } |
| | else if (prism.hasSubShape(TopAbs_SOLID)) { |
| | if (prism.countSubShapes(TopAbs_SOLID) > 1) { |
| | prism.makeElementFuse(prism.getSubTopoShapes(TopAbs_SOLID)); |
| | } |
| |
|
| | |
| | this->rawShape = prism; |
| | prism = refineShapeIfActive(prism); |
| | if (!isSingleSolidRuleSatisfied(prism.getShape())) { |
| | return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP( |
| | "Exception", |
| | "Result has multiple solids: enable 'Allow Compound' in the active body." |
| | )); |
| | } |
| | prism = getSolid(prism); |
| | this->Shape.setValue(prism); |
| | } |
| | else { |
| | |
| | this->rawShape = prism; |
| | prism = refineShapeIfActive(prism); |
| | if (!isSingleSolidRuleSatisfied(prism.getShape())) { |
| | return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP( |
| | "Exception", |
| | "Result has multiple solids: enable 'Allow Compound' in the active body." |
| | )); |
| | } |
| | this->Shape.setValue(prism); |
| | } |
| |
|
| | |
| | updateProperties(); |
| |
|
| | return App::DocumentObject::StdReturn; |
| | } |
| | 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 or multiple faces in a sketch are not allowed." |
| | )); |
| | } |
| | else { |
| | return new App::DocumentObjectExecReturn(e.GetMessageString()); |
| | } |
| | } |
| | catch (Base::Exception& e) { |
| | return new App::DocumentObjectExecReturn(e.what()); |
| | } |
| | } |
| |
|
| | TopoShape FeatureExtrude::generateSingleExtrusionSide( |
| | const TopoShape& sketchshape, |
| | const std::string& method, |
| | double length, |
| | double taperAngleDeg, |
| | App::PropertyLinkSub& upToFacePropHandle, |
| | App::PropertyLinkSubList& upToShapePropHandle, |
| | gp_Dir dir, |
| | double offsetVal, |
| | bool makeFace, |
| | const TopoShape& base, |
| | TopLoc_Location& invObjLoc |
| | ) |
| | { |
| | TopoShape prism(0, getDocument()->getStringHasher()); |
| |
|
| | if (method == "UpToFirst" || method == "UpToLast" || method == "UpToFace" |
| | || method == "UpToShape") { |
| | |
| | TopoShape supportface = getTopoShapeSupportFace(); |
| | auto invObjLoc = getLocation().Inverted(); |
| | supportface.move(invObjLoc); |
| |
|
| | if (!supportface.hasSubShape(TopAbs_WIRE)) { |
| | supportface = TopoShape(); |
| | } |
| |
|
| | TopoShape upToShape; |
| | int faceCount = 1; |
| | |
| | if (method == "UpToFace") { |
| | getUpToFaceFromLinkSub(upToShape, upToFacePropHandle); |
| | upToShape.move(invObjLoc); |
| | } |
| | else if (method == "UpToShape") { |
| | faceCount = getUpToShapeFromLinkSubList(upToShape, upToShapePropHandle); |
| | upToShape.move(invObjLoc); |
| | if (faceCount == 0) { |
| | |
| | upToShape = base; |
| | } |
| | } |
| |
|
| | if (faceCount == 1) { |
| | getUpToFace(upToShape, base, sketchshape, method, dir); |
| | addOffsetToFace(upToShape, dir, offsetVal); |
| | } |
| | else { |
| | if (fabs(offsetVal) > Precision::Confusion()) { |
| | throw Base::RuntimeError("Extrude: Can only offset one face"); |
| | } |
| | |
| | upToShape = makeShellFromUpToShape(upToShape, sketchshape, dir); |
| | } |
| |
|
| | try { |
| | TopoShape _base; |
| | if (addSubType != FeatureAddSub::Subtractive) { |
| | _base = base; |
| | } |
| | prism.makeElementPrismUntil( |
| | _base, |
| | sketchshape, |
| | supportface, |
| | upToShape, |
| | dir, |
| | TopoShape::PrismMode::None, |
| | true |
| | ); |
| | } |
| | catch (Base::Exception&) { |
| | if (method == "UpToShape" && faceCount > 1) { |
| | throw Base::RuntimeError( |
| | "Extrude: Unable to reach the selected shape, please select faces" |
| | ); |
| | } |
| | } |
| | } |
| | else if (method == "Length" || method == "ThroughAll") { |
| | using std::numbers::pi; |
| |
|
| | Part::ExtrusionParameters params; |
| | params.taperAngleFwd = Base::toRadians(taperAngleDeg); |
| | params.innerWireTaper = Part::InnerWireTaper::SameAsOuter; |
| |
|
| | if (std::fabs(params.taperAngleFwd) >= Precision::Angular() |
| | || std::fabs(params.taperAngleRev) >= Precision::Angular()) { |
| | if (fabs(params.taperAngleFwd) > pi * 0.5 - Precision::Angular() |
| | || fabs(params.taperAngleRev) > pi * 0.5 - Precision::Angular()) { |
| | return prism; |
| | } |
| | params.dir = dir; |
| | params.solid = makeFace; |
| | params.lengthFwd = length; |
| |
|
| | std::vector<TopoShape> drafts; |
| | Part::ExtrusionHelper::makeElementDraft( |
| | params, |
| | sketchshape, |
| | drafts, |
| | getDocument()->getStringHasher() |
| | ); |
| | if (drafts.empty()) { |
| | return prism; |
| | } |
| | prism.makeElementCompound( |
| | drafts, |
| | nullptr, |
| | TopoShape::SingleShapeCompoundCreationPolicy::returnShape |
| | ); |
| | } |
| | else { |
| | |
| | |
| | |
| | |
| | |
| | |
| | try { |
| | prism.makeElementPrism(sketchshape, length * gp_Vec(dir)); |
| | } |
| | catch (Standard_Failure&) { |
| | throw Base::RuntimeError("FeatureExtrusion: Length: Could not extrude the sketch!"); |
| | } |
| | } |
| | } |
| |
|
| | return prism; |
| | } |
| |
|
| | void FeatureExtrude::onDocumentRestored() |
| | { |
| | |
| | if (strcmp(Type.getValueAsString(), "?TwoLengths") == 0) { |
| | Type.setValue("Length"); |
| | Type2.setValue("Length"); |
| | SideType.setValue("Two sides"); |
| | } |
| | else if (Midplane.getValue()) { |
| | Midplane.setValue(false); |
| | SideType.setValue("Symmetric"); |
| | } |
| |
|
| | ProfileBased::onDocumentRestored(); |
| | } |
| |
|