| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| |
|
| |
|
| | #include <Mod/Part/App/FCBRepAlgoAPI_Cut.h>
|
| | #include <BRepPrimAPI_MakeRevol.hxx>
|
| | #include <BRepFeat_MakeRevol.hxx>
|
| | #include <gp_Lin.hxx>
|
| | #include <Precision.hxx>
|
| | #include <TopExp_Explorer.hxx>
|
| | #include <TopoDS.hxx>
|
| |
|
| | #include <Base/Exception.h>
|
| | #include <Base/Tools.h>
|
| |
|
| | #include "FeatureGroove.h"
|
| | #include "Mod/Part/App/TopoShapeOpCode.h"
|
| |
|
| | using namespace PartDesign;
|
| |
|
| | namespace PartDesign
|
| | {
|
| |
|
| |
|
| |
|
| | const char* Groove::TypeEnums[]
|
| | = {"Angle", "ThroughAll", "UpToFirst", "UpToFace", "TwoAngles", nullptr};
|
| |
|
| | PROPERTY_SOURCE(PartDesign::Groove, PartDesign::ProfileBased)
|
| |
|
| | const App::PropertyAngle::Constraints Groove::floatAngle = {0.0, 360.0, 1.0};
|
| |
|
| | Groove::Groove()
|
| | {
|
| | addSubType = FeatureAddSub::Subtractive;
|
| |
|
| | ADD_PROPERTY_TYPE(Type, (0L), "Groove", App::Prop_None, "Groove type");
|
| | Type.setEnums(TypeEnums);
|
| | ADD_PROPERTY_TYPE(
|
| | Base,
|
| | (Base::Vector3d(0.0f, 0.0f, 0.0f)),
|
| | "Groove",
|
| | App::PropertyType(App::Prop_ReadOnly | App::Prop_Hidden),
|
| | "Base"
|
| | );
|
| | ADD_PROPERTY_TYPE(
|
| | Axis,
|
| | (Base::Vector3d(0.0f, 1.0f, 0.0f)),
|
| | "Groove",
|
| | App::PropertyType(App::Prop_ReadOnly | App::Prop_Hidden),
|
| | "Axis"
|
| | );
|
| | ADD_PROPERTY_TYPE(Angle, (360.0), "Groove", App::Prop_None, "Angle");
|
| | ADD_PROPERTY_TYPE(Angle2, (0.0), "Groove", App::Prop_None, "Groove length in 2nd direction");
|
| | ADD_PROPERTY_TYPE(UpToFace, (nullptr), "Groove", App::Prop_None, "Face where groove will end");
|
| | Angle.setConstraints(&floatAngle);
|
| | ADD_PROPERTY_TYPE(ReferenceAxis, (nullptr), "Groove", (App::Prop_None), "Reference axis of groove");
|
| | }
|
| |
|
| | short Groove::mustExecute() const
|
| | {
|
| | if (Placement.isTouched() || ReferenceAxis.isTouched() || Axis.isTouched() || Base.isTouched()
|
| | || UpToFace.isTouched() || Angle.isTouched() || Angle2.isTouched()) {
|
| | return 1;
|
| | }
|
| | return ProfileBased::mustExecute();
|
| | }
|
| |
|
| | App::DocumentObjectExecReturn* Groove::execute()
|
| | {
|
| | if (onlyHaveRefined()) {
|
| | return App::DocumentObject::StdReturn;
|
| | }
|
| |
|
| | constexpr double maxDegree = 360.0;
|
| | auto method = methodFromString(Type.getValueAsString());
|
| |
|
| |
|
| | double angleDeg = Angle.getValue();
|
| | if (angleDeg > maxDegree) {
|
| | return new App::DocumentObjectExecReturn(
|
| | QT_TRANSLATE_NOOP("Exception", "Angle of groove too large")
|
| | );
|
| | }
|
| |
|
| | double angle = Base::toRadians<double>(angleDeg);
|
| | if (angle < Precision::Angular() && method == RevolMethod::Angle) {
|
| | return new App::DocumentObjectExecReturn(
|
| | QT_TRANSLATE_NOOP("Exception", "Angle of groove too small")
|
| | );
|
| | }
|
| |
|
| | double angle2 = Base::toRadians(Angle2.getValue());
|
| | if (std::fabs(angle + angle2) < Precision::Angular() && method == RevolMethod::TwoAngles) {
|
| | return new App::DocumentObjectExecReturn(
|
| | QT_TRANSLATE_NOOP("Exception", "Angles of groove nullify each other")
|
| | );
|
| | }
|
| |
|
| | TopoShape sketchshape = getTopoShapeVerifiedFace();
|
| |
|
| |
|
| | TopoShape base;
|
| | try {
|
| | base = getBaseTopoShape();
|
| | }
|
| | catch (const Base::Exception&) {
|
| |
|
| | }
|
| |
|
| |
|
| | try {
|
| | updateAxis();
|
| | }
|
| | catch (const Base::Exception& e) {
|
| | return new App::DocumentObjectExecReturn(e.what());
|
| | }
|
| |
|
| | try {
|
| |
|
| | 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")
|
| | );
|
| | }
|
| |
|
| | this->positionByPrevious();
|
| | auto invObjLoc = getLocation().Inverted();
|
| | pnt.Transform(invObjLoc.Transformation());
|
| | dir.Transform(invObjLoc.Transformation());
|
| | base.move(invObjLoc);
|
| | sketchshape.move(invObjLoc);
|
| |
|
| |
|
| | TopExp_Explorer xp;
|
| | 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")
|
| | );
|
| | }
|
| | }
|
| |
|
| |
|
| | TopoShape result(0);
|
| | TopoShape supportface(0);
|
| | try {
|
| | supportface = getSupportFace();
|
| | }
|
| | catch (...) {
|
| |
|
| | }
|
| |
|
| | supportface.move(invObjLoc);
|
| |
|
| | if (method == RevolMethod::ToFace || method == RevolMethod::ToFirst) {
|
| | TopoShape upToFace;
|
| | if (method == RevolMethod::ToFace) {
|
| | getUpToFaceFromLinkSub(upToFace, UpToFace);
|
| | upToFace.move(invObjLoc);
|
| | }
|
| | else {
|
| | throw Base::RuntimeError(
|
| | "ProfileBased: Revolution up to first/last is not yet supported"
|
| | );
|
| | }
|
| |
|
| | if (Reversed.getValue()) {
|
| | dir.Reverse();
|
| | }
|
| |
|
| | TopExp_Explorer Ex(supportface.getShape(), TopAbs_WIRE);
|
| | if (!Ex.More()) {
|
| | supportface = TopoDS_Face();
|
| | }
|
| |
|
| | try {
|
| | result = base.makeElementRevolution(
|
| | base,
|
| | TopoDS::Face(sketchshape.getShape()),
|
| | gp_Ax1(pnt, dir),
|
| | TopoDS::Face(supportface.getShape()),
|
| | TopoDS::Face(upToFace.getShape()),
|
| | nullptr,
|
| | Part::RevolMode::CutFromBase,
|
| | Standard_True
|
| | );
|
| | }
|
| | catch (Standard_Failure&) {
|
| | return new App::DocumentObjectExecReturn("Could not revolve the sketch!");
|
| | }
|
| | }
|
| | else {
|
| | bool midplane = Midplane.getValue();
|
| | bool reversed = Reversed.getValue();
|
| | generateRevolution(
|
| | result,
|
| | sketchshape.getShape(),
|
| | gp_Ax1(pnt, dir),
|
| | angle,
|
| | angle2,
|
| | midplane,
|
| | reversed,
|
| | method
|
| | );
|
| | }
|
| |
|
| | if (!result.isNull()) {
|
| |
|
| | this->rawShape = result;
|
| | result = refineShapeIfActive(result);
|
| |
|
| | this->AddSubShape.setValue(result);
|
| |
|
| | if (!base.isNull()) {
|
| | result = base.makeElementCut(result);
|
| |
|
| | this->rawShape = result;
|
| | result = refineShapeIfActive(result);
|
| | }
|
| | if (!isSingleSolidRuleSatisfied(result.getShape())) {
|
| | return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP(
|
| | "Exception",
|
| | "Result has multiple solids: enable 'Allow Compound' in the active body."
|
| | ));
|
| | }
|
| | result = getSolid(result);
|
| | this->Shape.setValue(result);
|
| | }
|
| | else {
|
| | return new App::DocumentObjectExecReturn(
|
| | QT_TRANSLATE_NOOP("Exception", "Could not revolve the sketch!")
|
| | );
|
| | }
|
| |
|
| |
|
| | updateProperties(method);
|
| |
|
| | 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 in a sketch are not allowed."
|
| | ));
|
| | }
|
| | else {
|
| | return new App::DocumentObjectExecReturn(e.GetMessageString());
|
| | }
|
| | }
|
| | catch (Base::Exception& e) {
|
| | return new App::DocumentObjectExecReturn(e.what());
|
| | }
|
| | }
|
| |
|
| | bool Groove::suggestReversed()
|
| | {
|
| | try {
|
| | updateAxis();
|
| | }
|
| | catch (const Base::Exception&) {
|
| | return false;
|
| | }
|
| |
|
| | return ProfileBased::getReversedAngle(Base.getValue(), Axis.getValue()) > 0.0;
|
| | }
|
| |
|
| | void Groove::updateAxis()
|
| | {
|
| | 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::NotParallelWithNormal);
|
| |
|
| | Base.setValue(base.x, base.y, base.z);
|
| | Axis.setValue(dir.x, dir.y, dir.z);
|
| | }
|
| |
|
| | Groove::RevolMethod Groove::methodFromString(const std::string& methodStr)
|
| | {
|
| | if (methodStr == "Angle") {
|
| | return RevolMethod::Angle;
|
| | }
|
| | if (methodStr == "UpToLast") {
|
| | return RevolMethod::ToLast;
|
| | }
|
| | if (methodStr == "ThroughAll") {
|
| | return RevolMethod::ThroughAll;
|
| | }
|
| | if (methodStr == "UpToFirst") {
|
| | return RevolMethod::ToFirst;
|
| | }
|
| | if (methodStr == "UpToFace") {
|
| | return RevolMethod::ToFace;
|
| | }
|
| | if (methodStr == "TwoAngles") {
|
| | return RevolMethod::TwoAngles;
|
| | }
|
| |
|
| | throw Base::ValueError("Groove:: No such method");
|
| | }
|
| |
|
| | void Groove::generateRevolution(
|
| | TopoShape& revol,
|
| | const TopoShape& sketchshape,
|
| | const gp_Ax1& axis,
|
| | const double angle,
|
| | const double angle2,
|
| | const bool midplane,
|
| | const bool reversed,
|
| | RevolMethod method
|
| | )
|
| | {
|
| | if (method == RevolMethod::Angle || method == RevolMethod::TwoAngles
|
| | || method == RevolMethod::ThroughAll) {
|
| | double angleTotal = angle;
|
| | double angleOffset = 0.;
|
| |
|
| | if (method == RevolMethod::TwoAngles) {
|
| |
|
| | angleTotal += angle2;
|
| | angleOffset = angle2 * -1.0;
|
| | }
|
| | else if (method == RevolMethod::ThroughAll) {
|
| | angleTotal = 2 * std::numbers::pi;
|
| | }
|
| | else if (midplane) {
|
| |
|
| | angleOffset = -angle / 2;
|
| | }
|
| |
|
| | if (fabs(angleTotal) < Precision::Angular()) {
|
| | throw Base::ValueError("Cannot create a revolution with zero angle.");
|
| | }
|
| |
|
| | gp_Ax1 revolAx(axis);
|
| | if (reversed) {
|
| | revolAx.Reverse();
|
| | }
|
| |
|
| | TopoShape from = sketchshape;
|
| | if (method == RevolMethod::TwoAngles || midplane) {
|
| | gp_Trsf mov;
|
| | mov.SetRotation(revolAx, angleOffset);
|
| | TopLoc_Location loc(mov);
|
| | from.move(loc);
|
| | }
|
| |
|
| |
|
| |
|
| |
|
| | revol = from;
|
| | revol = revol.makeElementRevolve(revolAx, angleTotal);
|
| | revol.Tag = -getID();
|
| | }
|
| | else {
|
| | std::stringstream str;
|
| | str << "ProfileBased: Internal error: Unknown method for generateRevolution()";
|
| | throw Base::RuntimeError(str.str());
|
| | }
|
| | }
|
| |
|
| | void Groove::generateRevolution(
|
| | TopoShape& revol,
|
| | const TopoShape& baseshape,
|
| | const TopoDS_Shape& profileshape,
|
| | const TopoDS_Face& supportface,
|
| | const TopoDS_Face& uptoface,
|
| | const gp_Ax1& axis,
|
| | RevolMethod method,
|
| | RevolMode Mode,
|
| | Standard_Boolean Modify
|
| | )
|
| | {
|
| | if (method == RevolMethod::ToFirst || method == RevolMethod::ToFace
|
| | || method == RevolMethod::ToLast) {
|
| | revol = revol.makeElementRevolution(
|
| | baseshape,
|
| | profileshape,
|
| | axis,
|
| | supportface,
|
| | uptoface,
|
| | nullptr,
|
| | static_cast<Part::RevolMode>(Mode),
|
| | Modify,
|
| | nullptr
|
| | );
|
| | }
|
| | else {
|
| | std::stringstream str;
|
| | str << "ProfileBased: Internal error: Unknown method for generateRevolution()";
|
| | throw Base::RuntimeError(str.str());
|
| | }
|
| | }
|
| |
|
| | void Groove::updateProperties(RevolMethod method)
|
| | {
|
| |
|
| |
|
| | bool isAngleEnabled = false;
|
| | bool isAngle2Enabled = false;
|
| | bool isMidplaneEnabled = false;
|
| | bool isReversedEnabled = false;
|
| | bool isUpToFaceEnabled = false;
|
| | if (method == RevolMethod::Angle) {
|
| | isAngleEnabled = true;
|
| | isMidplaneEnabled = true;
|
| | isReversedEnabled = !Midplane.getValue();
|
| | }
|
| | else if (method == RevolMethod::ToLast) {
|
| | isReversedEnabled = true;
|
| | }
|
| | else if (method == RevolMethod::ThroughAll) {
|
| | isMidplaneEnabled = true;
|
| | isReversedEnabled = !Midplane.getValue();
|
| | }
|
| | else if (method == RevolMethod::ToFirst) {
|
| | isReversedEnabled = true;
|
| | }
|
| | else if (method == RevolMethod::ToFace) {
|
| | isReversedEnabled = true;
|
| | isUpToFaceEnabled = true;
|
| | }
|
| | else if (method == RevolMethod::TwoAngles) {
|
| | isAngleEnabled = true;
|
| | isAngle2Enabled = true;
|
| | isReversedEnabled = true;
|
| | }
|
| |
|
| | Angle.setReadOnly(!isAngleEnabled);
|
| | Angle2.setReadOnly(!isAngle2Enabled);
|
| | Midplane.setReadOnly(!isMidplaneEnabled);
|
| | Reversed.setReadOnly(!isReversedEnabled);
|
| | UpToFace.setReadOnly(!isUpToFaceEnabled);
|
| | }
|
| |
|
| | }
|
| |
|