| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | #include <limits> |
| | #include <BRepAdaptor_Surface.hxx> |
| | #include <Mod/Part/App/FCBRepAlgoAPI_Common.h> |
| | #include <Mod/Part/App/FCBRepAlgoAPI_Cut.h> |
| | #include <Mod/Part/App/FCBRepAlgoAPI_Fuse.h> |
| | #include <BRepBndLib.hxx> |
| | #include <BRepBuilderAPI_MakeSolid.hxx> |
| | #include <BRepBuilderAPI_Sewing.hxx> |
| | #include <BRepClass3d_SolidClassifier.hxx> |
| | #include <BRepOffsetAPI_MakePipe.hxx> |
| | #include <BRepOffsetAPI_MakePipeShell.hxx> |
| | #include <BRepPrimAPI_MakeRevol.hxx> |
| | #include <ShapeFix_ShapeTolerance.hxx> |
| | #include <ShapeFix_Solid.hxx> |
| | #include <Precision.hxx> |
| | #include <TopoDS.hxx> |
| | #include <TopoDS_Face.hxx> |
| | #include <TopoDS_Wire.hxx> |
| | #include <gp_Ax1.hxx> |
| | #include <gp_Ax3.hxx> |
| |
|
| | #include <Standard_Version.hxx> |
| | #include <Base/Axis.h> |
| | #include <Base/Exception.h> |
| | #include <Base/Placement.h> |
| | #include <Base/Tools.h> |
| |
|
| | #include <Mod/Part/App/TopoShape.h> |
| | #include <Mod/Part/App/FaceMakerCheese.h> |
| |
|
| | #include "FeatureHelix.h" |
| |
|
| | using namespace PartDesign; |
| |
|
| | const char* Helix::ModeEnums[] |
| | = {"pitch-height-angle", "pitch-turns-angle", "height-turns-angle", "height-turns-growth", nullptr}; |
| |
|
| | PROPERTY_SOURCE(PartDesign::Helix, PartDesign::ProfileBased) |
| |
|
| | |
| | const App::PropertyFloatConstraint::Constraints Helix::floatTurns |
| | = {Precision::Confusion(), std::numeric_limits<int>::max(), 1.0}; |
| | const App::PropertyFloatConstraint::Constraints Helix::floatTolerance |
| | = {0.1, std::numeric_limits<int>::max(), 1.0}; |
| | const App::PropertyAngle::Constraints Helix::floatAngle = {-89.0, 89.0, 1.0}; |
| |
|
| | Helix::Helix() |
| | { |
| | addSubType = FeatureAddSub::Additive; |
| | auto initialMode = HelixMode::pitch_height_angle; |
| |
|
| | const char* group = "Helix"; |
| | ADD_PROPERTY_TYPE( |
| | Base, |
| | (Base::Vector3d(0.0, 0.0, 0.0)), |
| | group, |
| | App::Prop_ReadOnly, |
| | QT_TRANSLATE_NOOP( |
| | "App::Property", |
| | "The center point of the helix' start; derived from the reference axis." |
| | ) |
| | ); |
| | ADD_PROPERTY_TYPE( |
| | Axis, |
| | (Base::Vector3d(0.0, 1.0, 0.0)), |
| | group, |
| | App::Prop_ReadOnly, |
| | QT_TRANSLATE_NOOP("App::Property", "The helix' direction; derived from the reference axis.") |
| | ); |
| | ADD_PROPERTY_TYPE( |
| | ReferenceAxis, |
| | (nullptr), |
| | group, |
| | App::Prop_None, |
| | QT_TRANSLATE_NOOP("App::Property", "The reference axis of the helix.") |
| | ); |
| | ADD_PROPERTY_TYPE( |
| | Mode, |
| | (long(initialMode)), |
| | group, |
| | App::Prop_None, |
| | QT_TRANSLATE_NOOP( |
| | "App::Property", |
| | "The helix input mode specifies which properties are set by the user.\n" |
| | "Dependent properties are then calculated." |
| | ) |
| | ); |
| | Mode.setEnums(ModeEnums); |
| | ADD_PROPERTY_TYPE( |
| | Pitch, |
| | (10.0), |
| | group, |
| | App::Prop_None, |
| | QT_TRANSLATE_NOOP("App::Property", "The axial distance between two turns.") |
| | ); |
| | ADD_PROPERTY_TYPE( |
| | Height, |
| | (30.0), |
| | group, |
| | App::Prop_None, |
| | QT_TRANSLATE_NOOP( |
| | "App::Property", |
| | "The height of the helix' path, not accounting for the extent of the profile." |
| | ) |
| | ); |
| | ADD_PROPERTY_TYPE( |
| | Turns, |
| | (3.0), |
| | group, |
| | App::Prop_None, |
| | QT_TRANSLATE_NOOP("App::Property", "The number of turns in the helix.") |
| | ); |
| | Turns.setConstraints(&floatTurns); |
| | ADD_PROPERTY_TYPE( |
| | Angle, |
| | (0.0), |
| | group, |
| | App::Prop_None, |
| | QT_TRANSLATE_NOOP( |
| | "App::Property", |
| | "The angle of the cone that forms a hull around the helix.\n" |
| | "Non-zero values turn the helix into a conical spiral.\n" |
| | "Positive values make the radius grow, negative shrinks." |
| | ) |
| | ); |
| | Angle.setConstraints(&floatAngle); |
| | ADD_PROPERTY_TYPE( |
| | Growth, |
| | (0.0), |
| | group, |
| | App::Prop_None, |
| | QT_TRANSLATE_NOOP( |
| | "App::Property", |
| | "The growth of the helix' radius per turn.\n" |
| | "Non-zero values turn the helix into a conical spiral." |
| | ) |
| | ); |
| | ADD_PROPERTY_TYPE( |
| | LeftHanded, |
| | (false), |
| | group, |
| | App::Prop_None, |
| | QT_TRANSLATE_NOOP( |
| | "App::Property", |
| | "Sets the turning direction to left handed,\n" |
| | "i.e. counter-clockwise when moving along its axis." |
| | ) |
| | ); |
| | ADD_PROPERTY_TYPE( |
| | Reversed, |
| | (false), |
| | group, |
| | App::Prop_None, |
| | QT_TRANSLATE_NOOP( |
| | "App::Property", |
| | "Determines whether the helix points in the opposite direction of the axis." |
| | ) |
| | ); |
| | ADD_PROPERTY_TYPE( |
| | Outside, |
| | (false), |
| | group, |
| | App::Prop_None, |
| | QT_TRANSLATE_NOOP( |
| | "App::Property", |
| | "If set, the result will be the intersection of the profile and the preexisting body." |
| | ) |
| | ); |
| | ADD_PROPERTY_TYPE( |
| | HasBeenEdited, |
| | (false), |
| | group, |
| | App::Prop_Hidden, |
| | QT_TRANSLATE_NOOP( |
| | "App::Property", |
| | "If false, the tool will propose an initial value for the pitch based on the profile " |
| | "bounding box,\n" |
| | "so that self intersection is avoided." |
| | ) |
| | ); |
| | ADD_PROPERTY_TYPE( |
| | Tolerance, |
| | (0.1), |
| | group, |
| | App::Prop_None, |
| | QT_TRANSLATE_NOOP("App::Property", "Fusion Tolerance for the Helix, increase if helical shape does not merge nicely with part.") |
| | ); |
| | Tolerance.setConstraints(&floatTolerance); |
| |
|
| | setReadWriteStatusForMode(initialMode); |
| | } |
| |
|
| | short Helix::mustExecute() const |
| | { |
| | if (Placement.isTouched() || ReferenceAxis.isTouched() || Axis.isTouched() || Base.isTouched() |
| | || Angle.isTouched()) { |
| | return 1; |
| | } |
| | return ProfileBased::mustExecute(); |
| | } |
| |
|
| | App::DocumentObjectExecReturn* Helix::execute() |
| | { |
| | if (onlyHaveRefined()) { |
| | return App::DocumentObject::StdReturn; |
| | } |
| |
|
| | |
| | HelixMode mode = static_cast<HelixMode>(Mode.getValue()); |
| | if (mode == HelixMode::pitch_height_angle) { |
| | if (Pitch.getValue() < Precision::Confusion()) { |
| | return new App::DocumentObjectExecReturn( |
| | QT_TRANSLATE_NOOP("Exception", "Error: Pitch too small!") |
| | ); |
| | } |
| | if (Height.getValue() < Precision::Confusion()) { |
| | return new App::DocumentObjectExecReturn( |
| | QT_TRANSLATE_NOOP("Exception", "Error: height too small!") |
| | ); |
| | } |
| | Turns.setValue(Height.getValue() / Pitch.getValue()); |
| | Growth.setValue(Pitch.getValue() * tan(Base::toRadians(Angle.getValue()))); |
| | } |
| | else if (mode == HelixMode::pitch_turns_angle) { |
| | if (Pitch.getValue() < Precision::Confusion()) { |
| | return new App::DocumentObjectExecReturn( |
| | QT_TRANSLATE_NOOP("Exception", "Error: pitch too small!") |
| | ); |
| | } |
| | if (Turns.getValue() < Precision::Confusion()) { |
| | return new App::DocumentObjectExecReturn( |
| | QT_TRANSLATE_NOOP("Exception", "Error: turns too small!") |
| | ); |
| | } |
| | Height.setValue(Turns.getValue() * Pitch.getValue()); |
| | Growth.setValue(Pitch.getValue() * tan(Base::toRadians(Angle.getValue()))); |
| | } |
| | else if (mode == HelixMode::height_turns_angle) { |
| | if (Height.getValue() < Precision::Confusion()) { |
| | return new App::DocumentObjectExecReturn( |
| | QT_TRANSLATE_NOOP("Exception", "Error: height too small!") |
| | ); |
| | } |
| | if (Turns.getValue() < Precision::Confusion()) { |
| | return new App::DocumentObjectExecReturn( |
| | QT_TRANSLATE_NOOP("Exception", "Error: turns too small!") |
| | ); |
| | } |
| | Pitch.setValue(Height.getValue() / Turns.getValue()); |
| | Growth.setValue(Pitch.getValue() * tan(Base::toRadians(Angle.getValue()))); |
| | } |
| | else if (mode == HelixMode::height_turns_growth) { |
| | if (Turns.getValue() < Precision::Confusion()) { |
| | return new App::DocumentObjectExecReturn( |
| | QT_TRANSLATE_NOOP("Exception", "Error: turns too small!") |
| | ); |
| | } |
| | if ((Height.getValue() < Precision::Confusion()) |
| | && (abs(Growth.getValue()) < Precision::Confusion()) && Turns.getValue() > 1.0) { |
| | return new App::DocumentObjectExecReturn( |
| | QT_TRANSLATE_NOOP("Exception", "Error: either height or growth must not be zero!") |
| | ); |
| | } |
| | Pitch.setValue(Height.getValue() / Turns.getValue()); |
| | if (Height.getValue() > 0) { |
| | Angle.setValue( |
| | Base::toDegrees(atan(Turns.getValue() * Growth.getValue() / Height.getValue())) |
| | ); |
| | } |
| | else { |
| | |
| | |
| | |
| | |
| | } |
| | } |
| | else { |
| | return new App::DocumentObjectExecReturn( |
| | QT_TRANSLATE_NOOP("Exception", "Error: unsupported mode") |
| | ); |
| | } |
| |
|
| | TopoDS_Shape sketchshape; |
| | try { |
| | sketchshape = getVerifiedFace(); |
| | } |
| | catch (const Base::Exception& e) { |
| | return new App::DocumentObjectExecReturn(e.what()); |
| | } |
| |
|
| | if (sketchshape.IsNull()) { |
| | return new App::DocumentObjectExecReturn( |
| | QT_TRANSLATE_NOOP("Exception", "Error: No valid sketch or face") |
| | ); |
| | } |
| | else { |
| | |
| | |
| | |
| | |
| | |
| | TopoDS_Face face = TopoDS::Face(sketchshape); |
| | BRepAdaptor_Surface adapt(face); |
| | if (adapt.GetType() != GeomAbs_Plane) { |
| | return new App::DocumentObjectExecReturn( |
| | QT_TRANSLATE_NOOP("Exception", "Error: Face must be planar") |
| | ); |
| | } |
| | } |
| |
|
| | |
| | TopoShape base; |
| | try { |
| | base = getBaseTopoShape(); |
| | } |
| | catch (const Base::Exception&) { |
| | |
| | base = TopoShape(); |
| | } |
| |
|
| | |
| | try { |
| | updateAxis(); |
| | } |
| | catch (const Base::Exception& e) { |
| | return new App::DocumentObjectExecReturn(e.what()); |
| | } |
| |
|
| | try { |
| | this->positionByPrevious(); |
| | TopLoc_Location invObjLoc = this->getLocation().Inverted(); |
| |
|
| | base.move(invObjLoc); |
| |
|
| | TopoDS_Shape result; |
| |
|
| | |
| | TopoDS_Shape path; |
| | if (Angle.getValue() == 0.) { |
| | |
| | path = generateHelixPath(); |
| | } |
| | else { |
| | |
| | path = generateHelixPath(1000.); |
| | } |
| |
|
| | TopoDS_Shape face = sketchshape; |
| | face.Move(invObjLoc); |
| |
|
| | Bnd_Box bounds; |
| | BRepBndLib::Add(path, bounds); |
| | double size = sqrt(bounds.SquareExtent()); |
| | ShapeFix_ShapeTolerance fix; |
| | fix.LimitTolerance(path, Precision::Confusion() * 1e-6 * size); |
| | |
| | |
| | |
| | |
| | |
| |
|
| | BRepOffsetAPI_MakePipe |
| | mkPS(TopoDS::Wire(path), face, GeomFill_Trihedron::GeomFill_IsFrenet, Standard_False); |
| | result = mkPS.Shape(); |
| |
|
| | BRepClass3d_SolidClassifier SC(result); |
| | SC.PerformInfinitePoint(Precision::Confusion()); |
| | if (SC.State() == TopAbs_IN) { |
| | result.Reverse(); |
| | } |
| |
|
| | fix.LimitTolerance( |
| | result, |
| | Precision::Confusion() * size * Tolerance.getValue() |
| | ); |
| | |
| |
|
| | |
| | ShapeFix_Solid fixer; |
| | fixer.Init(TopoDS::Solid(result)); |
| | if (fixer.Perform()) { |
| | result = fixer.Solid(); |
| | } |
| |
|
| | AddSubShape.setValue(result); |
| |
|
| | if (base.isNull()) { |
| |
|
| | if (getAddSubType() == FeatureAddSub::Subtractive) { |
| | return new App::DocumentObjectExecReturn( |
| | QT_TRANSLATE_NOOP("Exception", "Error: There is nothing to subtract") |
| | ); |
| | } |
| |
|
| | if (!isSingleSolidRuleSatisfied(result)) { |
| | return new App::DocumentObjectExecReturn( |
| | QT_TRANSLATE_NOOP("Exception", "Error: Result has multiple solids") |
| | ); |
| | } |
| |
|
| | |
| | this->rawShape = result; |
| | Shape.setValue(getSolid(result)); |
| | return App::DocumentObject::StdReturn; |
| | } |
| |
|
| | if (getAddSubType() == FeatureAddSub::Additive) { |
| |
|
| | FCBRepAlgoAPI_Fuse mkFuse(base.getShape(), result); |
| | if (!mkFuse.IsDone()) { |
| | return new App::DocumentObjectExecReturn( |
| | QT_TRANSLATE_NOOP("Exception", "Error: Adding the helix failed") |
| | ); |
| | } |
| | |
| | TopoShape boolOp = this->getSolid(mkFuse.Shape()); |
| |
|
| | |
| | if (boolOp.isNull()) { |
| | return new App::DocumentObjectExecReturn( |
| | QT_TRANSLATE_NOOP("Exception", "Error: Result is not a solid") |
| | ); |
| | } |
| |
|
| | if (!isSingleSolidRuleSatisfied(boolOp.getShape())) { |
| | return new App::DocumentObjectExecReturn( |
| | QT_TRANSLATE_NOOP("Exception", "Error: Result has multiple solids") |
| | ); |
| | } |
| |
|
| | |
| | this->rawShape = boolOp; |
| | boolOp = refineShapeIfActive(boolOp, RefineErrorPolicy::Warn); |
| | Shape.setValue(getSolid(boolOp)); |
| | } |
| | else if (getAddSubType() == FeatureAddSub::Subtractive) { |
| |
|
| | TopoShape boolOp; |
| |
|
| | if (Outside.getValue()) { |
| | FCBRepAlgoAPI_Common mkCom(result, base.getShape()); |
| | if (!mkCom.IsDone()) { |
| | return new App::DocumentObjectExecReturn( |
| | QT_TRANSLATE_NOOP("Exception", "Error: Intersecting the helix failed") |
| | ); |
| | } |
| | boolOp = this->getSolid(mkCom.Shape()); |
| | } |
| | else { |
| | FCBRepAlgoAPI_Cut mkCut(base.getShape(), result); |
| | if (!mkCut.IsDone()) { |
| | return new App::DocumentObjectExecReturn( |
| | QT_TRANSLATE_NOOP("Exception", "Error: Subtracting the helix failed") |
| | ); |
| | } |
| | boolOp = this->getSolid(mkCut.Shape()); |
| | } |
| |
|
| | |
| | if (boolOp.isNull()) { |
| | return new App::DocumentObjectExecReturn( |
| | QT_TRANSLATE_NOOP("Exception", "Error: Result is not a solid") |
| | ); |
| | } |
| |
|
| | if (!isSingleSolidRuleSatisfied(boolOp.getShape())) { |
| | return new App::DocumentObjectExecReturn( |
| | QT_TRANSLATE_NOOP("Exception", "Error: Result has multiple solids") |
| | ); |
| | } |
| |
|
| | |
| | this->rawShape = boolOp; |
| | boolOp = refineShapeIfActive(boolOp, RefineErrorPolicy::Warn); |
| | Shape.setValue(getSolid(boolOp)); |
| | } |
| |
|
| | return App::DocumentObject::StdReturn; |
| | } |
| | catch (Standard_Failure& e) { |
| |
|
| | if (std::string(e.GetMessageString()) == "TopoDS::Face") { |
| | return new App::DocumentObjectExecReturn( |
| | QT_TRANSLATE_NOOP("Exception", "Error: Could not create face from sketch") |
| | ); |
| | } |
| | else { |
| | return new App::DocumentObjectExecReturn(e.GetMessageString()); |
| | } |
| | } |
| | catch (Base::Exception& e) { |
| | return new App::DocumentObjectExecReturn(e.what()); |
| | } |
| | } |
| |
|
| | void Helix::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::NoCheck); |
| |
|
| | Base.setValue(base.x, base.y, base.z); |
| | Axis.setValue(dir.x, dir.y, dir.z); |
| | } |
| |
|
| | TopoDS_Shape Helix::generateHelixPath(double breakAtTurn) |
| | { |
| | double turns = Turns.getValue(); |
| | double height = Height.getValue(); |
| | bool leftHanded = LeftHanded.getValue(); |
| | bool reversed = Reversed.getValue(); |
| | double angle = Angle.getValue(); |
| | double growth = Growth.getValue(); |
| |
|
| | if (fabs(angle) < Precision::Confusion()) { |
| | angle = 0.0; |
| | } |
| |
|
| | |
| | Base::Vector3d baseVector = Base.getValue(); |
| | gp_Pnt pnt(baseVector.x, baseVector.y, baseVector.z); |
| | Base::Vector3d axisVector = Axis.getValue(); |
| | gp_Dir dir(axisVector.x, axisVector.y, axisVector.z); |
| |
|
| | Base::Vector3d normal = getProfileNormal(); |
| | Base::Vector3d start = axisVector.Cross(normal); |
| |
|
| | |
| | |
| | |
| | if (start.IsNull()) { |
| | auto hopefullyNotParallel = Base::Vector3d(1.0, 2.0, 3.0); |
| | start = normal.Cross(hopefullyNotParallel); |
| | if (start.IsNull()) { |
| | |
| | hopefullyNotParallel = Base::Vector3d(3.0, 2.0, 1.0); |
| | start = normal.Cross(hopefullyNotParallel); |
| | } |
| | } |
| |
|
| | gp_Dir dir_start(start.x, start.y, start.z); |
| |
|
| | |
| | Base::Vector3d profileCenter = getProfileCenterPoint(); |
| |
|
| | |
| | |
| | |
| | double axisOffset = 100.0 * (profileCenter * start - baseVector * start); |
| | double radius = std::fabs(axisOffset); |
| | bool turned = axisOffset < 0; |
| | |
| | |
| | double startOffset = 10000.0 |
| | * std::fabs((angle <= 0. ? 1. : 0.) * (profileCenter * axisVector) - baseVector * axisVector); |
| |
|
| | if (radius < Precision::Confusion()) { |
| | |
| | if (fabs(axisVector * normal) < Precision::Confusion()) { |
| | throw Base::ValueError("Error: Result is self intersecting"); |
| | } |
| | radius = 1000.0; |
| | } |
| |
|
| | bool growthMode = std::string(Mode.getValueAsString()).find("growth") != std::string::npos; |
| | double radiusTop; |
| | if (growthMode) { |
| | radiusTop = radius + turns * growth; |
| | } |
| | else { |
| | radiusTop = radius + height * tan(Base::toRadians(angle)); |
| | } |
| |
|
| | |
| | TopoDS_Shape path |
| | = TopoShape().makeSpiralHelix(radius, radiusTop, height, turns, breakAtTurn, leftHanded); |
| |
|
| | |
| | |
| | |
| | |
| | |
| |
|
| | gp_Pnt origo(0.0, 0.0, 0.0); |
| | gp_Dir dir_axis1(0.0, 0.0, 1.0); |
| | gp_Dir dir_axis2(1.0, 0.0, 0.0); |
| | gp_Trsf mov; |
| |
|
| | if (abs(startOffset) > 0) { |
| | |
| | mov.SetTranslation(startOffset * gp_Vec(dir_axis1)); |
| | TopLoc_Location loc(mov); |
| | path.Move(loc); |
| | } |
| |
|
| | |
| | |
| | if (reversed) { |
| | mov.SetRotation(gp_Ax1(origo, dir_axis2), std::numbers::pi); |
| | TopLoc_Location loc(mov); |
| | path.Move(loc); |
| | } |
| |
|
| | if (turned) { |
| | mov.SetRotation(gp_Ax1(origo, dir_axis1), std::numbers::pi); |
| | TopLoc_Location loc(mov); |
| | path.Move(loc); |
| | } |
| |
|
| | gp_Ax3 sourceCS(origo, dir_axis1, dir_axis2); |
| | gp_Ax3 targetCS(pnt, dir, dir_start); |
| |
|
| | mov.SetTransformation(sourceCS, targetCS); |
| | TopLoc_Location loc(mov); |
| | path.Move(loc.Inverted()); |
| |
|
| | TopLoc_Location invObjLoc = this->getLocation().Inverted(); |
| | path.Move(invObjLoc); |
| |
|
| | return path; |
| | } |
| |
|
| | |
| | double Helix::safePitch() |
| | { |
| | Base::Vector3d axisVec = Axis.getValue(); |
| | Base::Vector3d startVec = axisVec.Cross(getProfileNormal()); |
| | |
| | HelixMode mode = static_cast<HelixMode>(Mode.getValue()); |
| | double growthValue = Growth.getValue(); |
| | double turnsValue = Turns.getValue(); |
| |
|
| | |
| | |
| | |
| | if (startVec.Length() < Precision::Confusion()) { |
| | |
| | if (mode != HelixMode::height_turns_growth) { |
| | return Precision::Confusion(); |
| | } |
| | |
| | |
| | |
| | |
| | |
| | |
| | else { |
| | if (abs(turnsValue) >= 1.0 && abs(growthValue) > 0.0) { |
| | return Precision::Infinite(); |
| | } |
| | } |
| | } |
| |
|
| | double angle = Base::toRadians(Angle.getValue()); |
| | gp_Dir direction(axisVec.x, axisVec.y, axisVec.z); |
| | gp_Dir directionStart(startVec.x, startVec.y, startVec.z); |
| | TopoDS_Shape sketchshape = getVerifiedFace(); |
| | Bnd_Box boundingBox; |
| | BRepBndLib::Add(sketchshape, boundingBox); |
| |
|
| | |
| | double Xmin, Ymin, Zmin, Xmax, Ymax, Zmax; |
| | boundingBox.Get(Xmin, Ymin, Zmin, Xmax, Ymax, Zmax); |
| | double X = Xmax - Xmin, Y = Ymax - Ymin, Z = Zmax - Zmin; |
| | gp_Vec boundingBoxVec(X, Y, Z); |
| |
|
| | |
| | |
| | |
| |
|
| | |
| | double pitch0 = boundingBoxVec * direction; |
| |
|
| | if (mode == HelixMode::height_turns_growth) { |
| | |
| | |
| | |
| | if (abs(growthValue) > abs(boundingBoxVec * directionStart)) { |
| | return 0.0; |
| | } |
| | else { |
| | |
| | |
| | |
| | |
| | if (turnsValue < 1.0) { |
| | return 0.0; |
| | } |
| | else { |
| | return pitch0; |
| | } |
| | } |
| | } |
| | else { |
| | |
| | |
| | |
| | if (tan(abs(angle)) * pitch0 > abs(boundingBoxVec * directionStart)) { |
| | return abs(boundingBoxVec * directionStart) / tan(abs(angle)); |
| | } |
| | else { |
| | return pitch0; |
| | } |
| | } |
| | } |
| |
|
| | |
| | void Helix::proposeParameters(bool force) |
| | { |
| | if (force || !HasBeenEdited.getValue()) { |
| | TopoDS_Shape sketchshape = getVerifiedFace(); |
| | Bnd_Box bb; |
| | BRepBndLib::Add(sketchshape, bb); |
| | bb.SetGap(0.0); |
| | double pitch = 1.1 * sqrt(bb.SquareExtent()); |
| |
|
| | Pitch.setValue(pitch); |
| | Height.setValue(pitch * 3.0); |
| | HasBeenEdited.setValue(true); |
| | } |
| | } |
| |
|
| | Base::Vector3d Helix::getProfileCenterPoint() |
| | { |
| | TopoDS_Shape profileshape; |
| | profileshape = getVerifiedFace(); |
| | Bnd_Box box; |
| | BRepBndLib::Add(profileshape, box); |
| | box.SetGap(0.0); |
| | double xmin, ymin, zmin, xmax, ymax, zmax; |
| | box.Get(xmin, ymin, zmin, xmax, ymax, zmax); |
| | return Base::Vector3d(0.5 * (xmin + xmax), 0.5 * (ymin + ymax), 0.5 * (zmin + zmax)); |
| | } |
| |
|
| | void Helix::handleChangedPropertyType(Base::XMLReader& reader, const char* TypeName, App::Property* prop) |
| | { |
| | |
| | if (prop == &Turns && strcmp(TypeName, "App::PropertyFloat") == 0) { |
| | App::PropertyFloat TurnsProperty; |
| | |
| | TurnsProperty.Restore(reader); |
| | Turns.setValue(TurnsProperty.getValue()); |
| | } |
| | |
| | else if (prop == &Growth && strcmp(TypeName, "App::PropertyLength") == 0) { |
| | App::PropertyLength GrowthProperty; |
| | |
| | GrowthProperty.Restore(reader); |
| | Growth.setValue(GrowthProperty.getValue()); |
| | } |
| | else { |
| | ProfileBased::handleChangedPropertyType(reader, TypeName, prop); |
| | } |
| | } |
| |
|
| | void Helix::onChanged(const App::Property* prop) |
| | { |
| | if (prop == &Mode) { |
| | |
| | auto inputMode = static_cast<HelixMode>(Mode.getValue()); |
| | setReadWriteStatusForMode(inputMode); |
| | } |
| |
|
| | ProfileBased::onChanged(prop); |
| | } |
| |
|
| | void Helix::setReadWriteStatusForMode(HelixMode inputMode) |
| | { |
| | switch (inputMode) { |
| | case HelixMode::pitch_height_angle: |
| | |
| | Pitch.setStatus(App::Property::ReadOnly, false); |
| | Height.setStatus(App::Property::ReadOnly, false); |
| | Angle.setStatus(App::Property::ReadOnly, false); |
| | |
| | Turns.setStatus(App::Property::ReadOnly, true); |
| | Growth.setStatus(App::Property::ReadOnly, true); |
| | break; |
| |
|
| | case HelixMode::pitch_turns_angle: |
| | |
| | Pitch.setStatus(App::Property::ReadOnly, false); |
| | Turns.setStatus(App::Property::ReadOnly, false); |
| | Angle.setStatus(App::Property::ReadOnly, false); |
| | |
| | Height.setStatus(App::Property::ReadOnly, true); |
| | Growth.setStatus(App::Property::ReadOnly, true); |
| | break; |
| |
|
| | case HelixMode::height_turns_angle: |
| | |
| | Height.setStatus(App::Property::ReadOnly, false); |
| | Turns.setStatus(App::Property::ReadOnly, false); |
| | Angle.setStatus(App::Property::ReadOnly, false); |
| | |
| | Pitch.setStatus(App::Property::ReadOnly, true); |
| | Growth.setStatus(App::Property::ReadOnly, true); |
| | break; |
| |
|
| | case HelixMode::height_turns_growth: |
| | |
| | Height.setStatus(App::Property::ReadOnly, false); |
| | Turns.setStatus(App::Property::ReadOnly, false); |
| | Growth.setStatus(App::Property::ReadOnly, false); |
| | |
| | Pitch.setStatus(App::Property::ReadOnly, true); |
| | Angle.setStatus(App::Property::ReadOnly, true); |
| | break; |
| |
|
| | default: |
| | Pitch.setStatus(App::Property::ReadOnly, false); |
| | Height.setStatus(App::Property::ReadOnly, false); |
| | Turns.setStatus(App::Property::ReadOnly, false); |
| | Angle.setStatus(App::Property::ReadOnly, false); |
| | Growth.setStatus(App::Property::ReadOnly, false); |
| | break; |
| | } |
| | } |
| |
|
| | PROPERTY_SOURCE(PartDesign::AdditiveHelix, PartDesign::Helix) |
| | AdditiveHelix::AdditiveHelix() |
| | { |
| | addSubType = Additive; |
| | Outside.setStatus(App::Property::Hidden, true); |
| | } |
| |
|
| | PROPERTY_SOURCE(PartDesign::SubtractiveHelix, PartDesign::Helix) |
| | SubtractiveHelix::SubtractiveHelix() |
| | { |
| | addSubType = Subtractive; |
| | Outside.setStatus(App::Property::Hidden, false); |
| | } |
| |
|