diff --git a/src/Mod/Part/App/TopoShape.cpp b/src/Mod/Part/App/TopoShape.cpp
index 7ce4568f34..356f116e00 100644
--- a/src/Mod/Part/App/TopoShape.cpp
+++ b/src/Mod/Part/App/TopoShape.cpp
@@ -2437,7 +2437,7 @@ TopoDS_Shape TopoShape::makeLongHelix(Standard_Real pitch, Standard_Real height,
Handle(Geom_Surface) surf;
Standard_Boolean isCylinder;
- if (angle < Precision::Confusion()) { // Cylindrical helix
+ if (std::fabs(angle) < Precision::Confusion()) { // Cylindrical helix
if (radius < Precision::Confusion())
Standard_Failure::Raise("Radius of helix too small");
surf= new Geom_CylindricalSurface(cylAx2, radius);
@@ -2445,8 +2445,6 @@ TopoDS_Shape TopoShape::makeLongHelix(Standard_Real pitch, Standard_Real height,
}
else { // Conical helix
angle = Base::toRadians(angle);
- if (angle < Precision::Confusion())
- Standard_Failure::Raise("Angle of helix too small");
surf = new Geom_ConicalSurface(gp_Ax3(cylAx2), angle, radius);
isCylinder = false;
}
diff --git a/src/Mod/PartDesign/App/AppPartDesign.cpp b/src/Mod/PartDesign/App/AppPartDesign.cpp
index 3cae7878dd..ce39347034 100644
--- a/src/Mod/PartDesign/App/AppPartDesign.cpp
+++ b/src/Mod/PartDesign/App/AppPartDesign.cpp
@@ -59,6 +59,7 @@
#include "FeatureLoft.h"
#include "ShapeBinder.h"
#include "FeatureBase.h"
+#include "FeatureHelix.h"
namespace PartDesign {
extern PyObject* initModule();
@@ -116,6 +117,9 @@ PyMOD_INIT_FUNC(_PartDesign)
PartDesign::Loft ::init();
PartDesign::AdditiveLoft ::init();
PartDesign::SubtractiveLoft ::init();
+ PartDesign::Helix ::init();
+ PartDesign::AdditiveHelix ::init();
+ PartDesign::SubtractiveHelix ::init();
PartDesign::ShapeBinder ::init();
PartDesign::SubShapeBinder ::init();
PartDesign::Plane ::init();
diff --git a/src/Mod/PartDesign/App/Body.cpp b/src/Mod/PartDesign/App/Body.cpp
index d29e5b90d6..face2936eb 100644
--- a/src/Mod/PartDesign/App/Body.cpp
+++ b/src/Mod/PartDesign/App/Body.cpp
@@ -140,7 +140,7 @@ App::DocumentObject* Body::getPrevSolidFeature(App::DocumentObject *start)
if (rvIt != features.rend()) { // the solid found in model list
return *rvIt;
}
-
+
return nullptr;
}
@@ -197,7 +197,7 @@ bool Body::isMemberOfMultiTransform(const App::DocumentObject* f)
// This can be recognized because the Originals property is empty (it is contained
// in the MultiTransform instead)
// COMMENT ON THE COMMENT:
- // This is wrong because at the creation (addObject) and before assigning the originals, that
+ // This is wrong because at the creation (addObject) and before assigning the originals, that
// is when this code is executed, the originals property is indeed empty.
//
// However, for the purpose of setting the base feature, the transform feature has been modified
@@ -244,7 +244,7 @@ Body* Body::findBodyOf(const App::DocumentObject* feature)
{
if(!feature)
return nullptr;
-
+
return static_cast
(BodyBase::findBodyOf(feature));
}
@@ -253,15 +253,15 @@ std::vector Body::addObject(App::DocumentObject *feature)
{
if(!isAllowed(feature))
throw Base::ValueError("Body: object is not allowed");
-
+
//TODO: features should not add all links
-
+
//only one group per object. If it is in a body the single feature will be removed
auto *group = App::GroupExtension::getGroupOfObject(feature);
if(group && group != getExtendedObject())
group->getExtensionByType()->removeObject(feature);
-
-
+
+
insertObject (feature, getNextSolidFeature (), /*after = */ false);
// Move the Tip if we added a solid
if (isSolidFeature(feature)) {
@@ -272,22 +272,22 @@ std::vector Body::addObject(App::DocumentObject *feature)
&& feature->isDerivedFrom(PartDesign::Feature::getClassTypeId()))
{
for(auto obj : Group.getValues()) {
- if(obj->Visibility.getValue()
- && obj!=feature
+ if(obj->Visibility.getValue()
+ && obj!=feature
&& obj->isDerivedFrom(PartDesign::Feature::getClassTypeId()))
obj->Visibility.setValue(false);
}
}
-
+
std::vector result = {feature};
return result;
}
std::vector< App::DocumentObject* > Body::addObjects(std::vector< App::DocumentObject* > objs) {
-
+
for(auto obj : objs)
addObject(obj);
-
+
return objs;
}
@@ -298,7 +298,7 @@ void Body::insertObject(App::DocumentObject* feature, App::DocumentObject* targe
if (target && !hasObject (target)) {
throw Base::ValueError("Body: the feature we should insert relative to is not part of that body");
}
-
+
//ensure that all origin links are ok
relinkToOrigin(feature);
@@ -445,7 +445,7 @@ void Body::onSettingDocument() {
void Body::onChanged (const App::Property* prop) {
// we neither load a project nor perform undo/redo
- if (!this->isRestoring()
+ if (!this->isRestoring()
&& this->getDocument()
&& !this->getDocument()->isPerformingTransaction()) {
if (prop == &BaseFeature) {
@@ -506,7 +506,7 @@ std::vector Body::getSubObjects(int reason) const {
return {};
}
-App::DocumentObject *Body::getSubObject(const char *subname,
+App::DocumentObject *Body::getSubObject(const char *subname,
PyObject **pyObj, Base::Matrix4D *pmat, bool transform, int depth) const
{
#if 1
@@ -525,8 +525,8 @@ App::DocumentObject *Body::getSubObject(const char *subname,
// We return the shape only if there are feature visible inside
for(auto obj : Group.getValues()) {
- if(obj->Visibility.getValue() &&
- obj->isDerivedFrom(PartDesign::Feature::getClassTypeId()))
+ if(obj->Visibility.getValue() &&
+ obj->isDerivedFrom(PartDesign::Feature::getClassTypeId()))
{
return Part::BodyBase::getSubObject(subname,pyObj,pmat,transform,depth);
}
@@ -546,3 +546,14 @@ void Body::onDocumentRestored()
_GroupTouched.setStatus(App::Property::Output,true);
DocumentObject::onDocumentRestored();
}
+
+// a body is solid if it has features that are solid
+bool Body::isSolid()
+{
+ std::vector features = getFullModel();
+ for (auto it = features.begin(); it!=features.end(); ++it){
+ if (isSolidFeature((*it)))
+ return true;
+ }
+ return false;
+}
diff --git a/src/Mod/PartDesign/App/Body.h b/src/Mod/PartDesign/App/Body.h
index 3ba7bfbbc7..d26852987d 100644
--- a/src/Mod/PartDesign/App/Body.h
+++ b/src/Mod/PartDesign/App/Body.h
@@ -118,7 +118,7 @@ public:
PyObject *getPyObject(void) override;
virtual std::vector getSubObjects(int reason=0) const override;
- virtual App::DocumentObject *getSubObject(const char *subname,
+ virtual App::DocumentObject *getSubObject(const char *subname,
PyObject **pyObj, Base::Matrix4D *pmat, bool transform, int depth) const override;
void setShowTip(bool enable) {
@@ -137,6 +137,9 @@ public:
*/
App::DocumentObject *getNextSolidFeature(App::DocumentObject* start = NULL);
+ // a body is solid if it has features that are solid according to member isSolidFeature.
+ bool isSolid(void);
+
protected:
virtual void onSettingDocument() override;
diff --git a/src/Mod/PartDesign/App/CMakeLists.txt b/src/Mod/PartDesign/App/CMakeLists.txt
index 112a4ab12b..a623563930 100644
--- a/src/Mod/PartDesign/App/CMakeLists.txt
+++ b/src/Mod/PartDesign/App/CMakeLists.txt
@@ -109,6 +109,8 @@ SET(FeaturesSketchBased_SRCS
FeaturePipe.cpp
FeatureLoft.h
FeatureLoft.cpp
+ FeatureHelix.h
+ FeatureHelix.cpp
)
SOURCE_GROUP("SketchBasedFeatures" FILES ${FeaturesSketchBased_SRCS})
diff --git a/src/Mod/PartDesign/App/FeatureHelix.cpp b/src/Mod/PartDesign/App/FeatureHelix.cpp
new file mode 100644
index 0000000000..9a8dbc8e3c
--- /dev/null
+++ b/src/Mod/PartDesign/App/FeatureHelix.cpp
@@ -0,0 +1,553 @@
+/***************************************************************************
+ * Copyright (c) 2010 Juergen Riegel *
+ * 2020 David Österberg *
+ * 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
+# include
+# include
+# include
+# include
+# include
+# include
+# include
+# include
+# include
+# include
+# include
+# include
+# include
+# include
+# include
+# include
+# include
+# include
+# include
+# include
+# include
+# include
+# include
+# include
+# include
+#endif
+
+# include
+# include
+# include
+# include
+# include
+# include
+
+# include
+# include
+
+# include "FeatureHelix.h"
+
+const double PI = 3.14159265359;
+
+using namespace PartDesign;
+
+const char* Helix::ModeEnums[] = {"pitch-height", "pitch-turns", "height-turns", NULL};
+
+PROPERTY_SOURCE(PartDesign::Helix, PartDesign::ProfileBased)
+
+Helix::Helix()
+{
+ addSubType = FeatureAddSub::Additive;
+
+ ADD_PROPERTY_TYPE(Base,(Base::Vector3d(0.0,0.0,0.0)),"Helix", App::Prop_ReadOnly, "Base");
+ ADD_PROPERTY_TYPE(Axis,(Base::Vector3d(0.0,1.0,0.0)),"Helix", App::Prop_ReadOnly, "Axis");
+ ADD_PROPERTY_TYPE(Pitch,(10.),"Helix", App::Prop_None, "Pitch");
+ ADD_PROPERTY_TYPE(Height,(30.0),"Helix", App::Prop_None, "Height");
+ ADD_PROPERTY_TYPE(Turns,(3.0),"Helix", App::Prop_None, "Turns");
+ ADD_PROPERTY_TYPE(LeftHanded,(long(0)),"Helix", App::Prop_None, "LeftHanded");
+ ADD_PROPERTY_TYPE(Reversed,(long(0)),"Helix", App::Prop_None, "Reversed");
+ ADD_PROPERTY_TYPE(Angle,(0.0),"Helix", App::Prop_None, "Angle");
+ ADD_PROPERTY_TYPE(ReferenceAxis,(0),"Helix", App::Prop_None, "Reference axis of revolution");
+ ADD_PROPERTY_TYPE(Mode, (long(0)), "Helix", App::Prop_None, "Helix input mode");
+ ADD_PROPERTY_TYPE(Outside,(long(0)),"Helix", App::Prop_None, "Outside");
+ ADD_PROPERTY_TYPE(HasBeenEdited,(long(0)),"Helix", App::Prop_None, "HasBeenEdited");
+ Mode.setEnums(ModeEnums);
+
+}
+
+short Helix::mustExecute() const
+{
+ if (Placement.isTouched() ||
+ ReferenceAxis.isTouched() ||
+ Axis.isTouched() ||
+ Base.isTouched() ||
+ Angle.isTouched())
+ return 1;
+ return ProfileBased::mustExecute();
+}
+
+App::DocumentObjectExecReturn *Helix::execute(void)
+{
+ // Validate and normalize parameters
+ switch (Mode.getValue()) {
+ case 0: // pitch - height
+ if (Pitch.getValue() < Precision::Confusion())
+ return new App::DocumentObjectExecReturn("Error: Pitch too small");
+ if (Height.getValue() < Precision::Confusion())
+ return new App::DocumentObjectExecReturn("Error: height too small!");
+ break;
+ case 1: // pitch - turns
+ if (Pitch.getValue() < Precision::Confusion())
+ return new App::DocumentObjectExecReturn("Error: pitch too small!");
+ if (Turns.getValue() < Precision::Confusion())
+ return new App::DocumentObjectExecReturn("Error: turns too small!");
+ Height.setValue(Turns.getValue()*Pitch.getValue());
+ break;
+ case 2: // height - turns
+ if (Height.getValue() < Precision::Confusion())
+ return new App::DocumentObjectExecReturn("Error: height too small!");
+ if (Turns.getValue() < Precision::Confusion())
+ return new App::DocumentObjectExecReturn("Error turns too small!");
+ Pitch.setValue(Height.getValue()/Turns.getValue());
+ break;
+ default:
+ return new App::DocumentObjectExecReturn("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("Error: No valid sketch or face");
+ else {
+ //TODO: currently we only allow planar faces. the reason for this is that with other faces in front, we could
+ //not use the current simulate approach and build the start and end face from the wires. As the shell
+ //begins always at the spine and not the profile, the sketchshape cannot be used directly as front face.
+ //We would need a method to translate the front shape to match the shell starting position somehow...
+ TopoDS_Face face = TopoDS::Face(sketchshape);
+ BRepAdaptor_Surface adapt(face);
+ if(adapt.GetType() != GeomAbs_Plane)
+ return new App::DocumentObjectExecReturn("Error: Face must be planar");
+ }
+
+ // if the Base property has a valid shape, fuse the AddShape into it
+ TopoDS_Shape base;
+ try {
+ base = getBaseShape();
+ } catch (const Base::Exception&) {
+ // fall back to support (for legacy features)
+ base = TopoDS_Shape();
+ }
+
+
+ // update Axis from ReferenceAxis
+ try {
+ updateAxis();
+ } 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 {
+ this->positionByPrevious();
+ TopLoc_Location invObjLoc = this->getLocation().Inverted();
+
+ base.Move(invObjLoc);
+
+ // generate the helix path
+ TopoDS_Shape path = generateHelixPath();
+
+ // Below is basically a copy paste (with some simplification) from FeaturePipe.cpp Pipe::execute
+ // TODO: find a way to reduce code repetition. E.g can I rip out this functionality of Pipe:execute to a static helper
+ // function and call from here?
+
+ std::vector wires;
+ try {
+ wires = getProfileWires();
+ } catch (const Base::Exception& e) {
+ return new App::DocumentObjectExecReturn(e.what());
+ }
+
+ std::vector> wiresections;
+ for(TopoDS_Wire& wire : wires)
+ wiresections.emplace_back(1, wire);
+
+ //maybe we need a scaling law
+ Handle(Law_Function) scalinglaw;
+
+ //build all shells
+ std::vector shells;
+ std::vector frontwires, backwires;
+ for(std::vector& wires : wiresections) {
+
+ BRepOffsetAPI_MakePipeShell mkPS(TopoDS::Wire(path));
+
+ mkPS.SetTolerance(Precision::Confusion());
+ mkPS.SetTransitionMode(BRepBuilderAPI_Transformed);
+
+ mkPS.SetMode(true); //This is for frenet
+ //mkPipeShell.SetMode(TopoDS::Wire(auxpath), true); // this is for two rails
+
+
+ if(!scalinglaw) {
+ for(TopoDS_Wire& wire : wires) {
+ wire.Move(invObjLoc);
+ mkPS.Add(wire);
+ }
+ }
+ else {
+ for(TopoDS_Wire& wire : wires) {
+ wire.Move(invObjLoc);
+ mkPS.SetLaw(wire, scalinglaw);
+ }
+ }
+
+ if (!mkPS.IsReady())
+ return new App::DocumentObjectExecReturn("Error: Could not build");
+
+ shells.push_back(mkPS.Shape());
+
+
+ if (!mkPS.Shape().Closed()) {
+ // shell is not closed - use simulate to get the end wires
+ TopTools_ListOfShape sim;
+ mkPS.Simulate(2, sim);
+
+ frontwires.push_back(TopoDS::Wire(sim.First()));
+ backwires.push_back(TopoDS::Wire(sim.Last()));
+ }
+ }
+
+ BRepBuilderAPI_MakeSolid mkSolid;
+
+ if (!frontwires.empty()) {
+ // build the end faces, sew the shell and build the final solid
+ TopoDS_Shape front = Part::FaceMakerCheese::makeFace(frontwires);
+ TopoDS_Shape back = Part::FaceMakerCheese::makeFace(backwires);
+
+ BRepBuilderAPI_Sewing sewer;
+ sewer.SetTolerance(Precision::Confusion());
+ sewer.Add(front);
+ sewer.Add(back);
+
+ for(TopoDS_Shape& s : shells)
+ sewer.Add(s);
+
+ sewer.Perform();
+ mkSolid.Add(TopoDS::Shell(sewer.SewedShape()));
+ } else {
+ // shells are already closed - add them directly
+ for (TopoDS_Shape& s : shells) {
+ mkSolid.Add(TopoDS::Shell(s));
+ }
+ }
+
+ if(!mkSolid.IsDone())
+ return new App::DocumentObjectExecReturn("Error: Result is not a solid");
+
+ TopoDS_Shape result = mkSolid.Shape();
+ BRepClass3d_SolidClassifier SC(result);
+ SC.PerformInfinitePoint(Precision::Confusion());
+ if (SC.State() == TopAbs_IN)
+ result.Reverse();
+
+ AddSubShape.setValue(result);
+
+
+ if(base.IsNull()) {
+
+ if (getAddSubType() == FeatureAddSub::Subtractive)
+ return new App::DocumentObjectExecReturn("Error: There is nothing to subtract\n");
+
+ int solidCount = countSolids(result);
+ if (solidCount > 1) {
+ return new App::DocumentObjectExecReturn("Error: Result has multiple solids");
+ }
+ Shape.setValue(getSolid(result));
+ return App::DocumentObject::StdReturn;
+ }
+
+ if(getAddSubType() == FeatureAddSub::Additive) {
+
+ BRepAlgoAPI_Fuse mkFuse(base, result);
+ if (!mkFuse.IsDone())
+ return new App::DocumentObjectExecReturn("Error: Adding the helix failed");
+ // we have to get the solids (fuse sometimes creates compounds)
+ TopoDS_Shape boolOp = this->getSolid(mkFuse.Shape());
+ // lets check if the result is a solid
+ if (boolOp.IsNull())
+ return new App::DocumentObjectExecReturn("Error: Result is not a solid");
+
+ int solidCount = countSolids(boolOp);
+ if (solidCount > 1) {
+ return new App::DocumentObjectExecReturn("Error: Result has multiple solids");
+ }
+
+ boolOp = refineShapeIfActive(boolOp);
+ Shape.setValue(getSolid(boolOp));
+ }
+ else if(getAddSubType() == FeatureAddSub::Subtractive) {
+
+ TopoDS_Shape boolOp;
+
+ if (Outside.getValue()) { // are we subtracting the inside or the outside of the profile.
+ BRepAlgoAPI_Common mkCom(result, base);
+ if (!mkCom.IsDone())
+ return new App::DocumentObjectExecReturn("Error: Intersecting the helix failed");
+ boolOp = this->getSolid(mkCom.Shape());
+
+ } else {
+ BRepAlgoAPI_Cut mkCut(base, result);
+ if (!mkCut.IsDone())
+ return new App::DocumentObjectExecReturn("Error: Subtracting the helix failed");
+ boolOp = this->getSolid(mkCut.Shape());
+ }
+
+ // lets check if the result is a solid
+ if (boolOp.IsNull())
+ return new App::DocumentObjectExecReturn("Error: Result is not a solid");
+
+ int solidCount = countSolids(boolOp);
+ if (solidCount > 1) {
+ return new App::DocumentObjectExecReturn("Error: Result has multiple solids");
+ }
+
+ boolOp = refineShapeIfActive(boolOp);
+ Shape.setValue(getSolid(boolOp));
+ }
+
+ return App::DocumentObject::StdReturn;
+ }
+ catch (Standard_Failure& e) {
+
+ if (std::string(e.GetMessageString()) == "TopoDS::Face")
+ return new App::DocumentObjectExecReturn("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(void)
+{
+ App::DocumentObject *pcReferenceAxis = ReferenceAxis.getValue();
+ const std::vector &subReferenceAxis = ReferenceAxis.getSubValues();
+ Base::Vector3d base;
+ Base::Vector3d dir;
+ getAxis(pcReferenceAxis, subReferenceAxis, base, dir, false);
+
+ Base.setValue(base.x,base.y,base.z);
+ Axis.setValue(dir.x,dir.y,dir.z);
+}
+
+
+TopoDS_Shape Helix::generateHelixPath(void)
+{
+ double pitch = Pitch.getValue();
+ double height = Height.getValue();
+ bool leftHanded = LeftHanded.getValue();
+ bool reversed = Reversed.getValue();
+ double angle = Angle.getValue();
+ if (angle < Precision::Confusion() && angle > -Precision::Confusion())
+ angle = 0.0;
+
+ // 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);
+
+ Base::Vector3d normal = getProfileNormal();
+ Base::Vector3d start = v.Cross(normal); // pointing towards the desired helix start point.
+ gp_Dir dir_start(start.x, start.y, start.z);
+
+ // Determine radius as the minimum distance between sketchshape and axis.
+ // also find out in what quadrant relative to the axis the profile is located.
+ double radius = 1e99;
+ bool turned = false;
+ double startOffset = 1e99;
+ TopoDS_Shape sketchshape = getVerifiedFace();
+ BRepBuilderAPI_MakeEdge axisEdge(gp_Lin(pnt, dir));
+ BRepBuilderAPI_MakeEdge startEdge(gp_Lin(pnt, dir_start));
+ for (TopExp_Explorer xp(sketchshape, TopAbs_FACE); xp.More(); xp.Next()) {
+ const TopoDS_Face face = TopoDS::Face(xp.Current());
+ TopoDS_Wire wire = ShapeAnalysis::OuterWire(face);
+ BRepExtrema_DistShapeShape distR(wire, axisEdge.Shape(), Precision::Confusion());
+ if (distR.IsDone()) {
+ if (distR.Value() < radius) {
+ radius = distR.Value();
+ const gp_Pnt p1 = distR.PointOnShape1(1);
+ const gp_Pnt p2 = distR.PointOnShape2(1);
+ double offsetProfile = p1.X()*dir_start.X() + p1.Y()*dir_start.Y() + p1.Z()*dir_start.Z();
+ double offsetAxis = p2.X()*dir_start.X() + p2.Y()*dir_start.Y() + p2.Z()*dir_start.Z();
+ turned = (offsetProfile < offsetAxis);
+ }
+ }
+ BRepExtrema_DistShapeShape distStart(wire, startEdge.Shape(), Precision::Confusion());
+ if (distStart.IsDone()) {
+ if (distStart.Value() < abs(startOffset)) {
+ const gp_Pnt p1 = distStart.PointOnShape1(1);
+ const gp_Pnt p2 = distStart.PointOnShape2(1);
+ double offsetProfile = p1.X()*dir.X() + p1.Y()*dir.Y() + p1.Z()*dir.Z();
+ double offsetAxis = p2.X()*dir.X() + p2.Y()*dir.Y() + p2.Z()*dir.Z();
+ startOffset = offsetProfile - offsetAxis;
+ }
+ }
+
+ }
+
+ if (radius < Precision::Confusion()) {
+ // in this case ensure that axis is not in the sketch plane
+ if (v*normal < Precision::Confusion())
+ throw Base::ValueError("Error: Result is self intersecting");
+ radius = 1.0; //fallback to radius 1
+ startOffset = 0.0;
+ }
+
+ //build the helix path
+ TopoShape helix = TopoShape().makeLongHelix(pitch, height, radius, angle, leftHanded);
+ TopoDS_Shape path = helix.getShape();
+
+
+ /*
+ * The helix wire is created with the axis coinciding with z-axis and the start point at (radius, 0, 0)
+ * We want to move it so that the axis becomes aligned with "dir" and "pnt", we also want (radius,0,0) to
+ * map to the sketch plane.
+ */
+
+
+ gp_Pnt origo(0.0, 0.0, 0.0);
+ gp_Dir dir_axis1(0.0, 0.0, 1.0); // pointing along the helix axis, as created.
+ gp_Dir dir_axis2(1.0, 0.0, 0.0); // pointing towards the helix start point, as created.
+
+ gp_Trsf mov;
+
+
+ if (reversed) {
+ mov.SetRotation(gp_Ax1(origo, dir_axis2), PI);
+ TopLoc_Location loc(mov);
+ path.Move(loc);
+ }
+
+ if (abs(startOffset) > 0) { // translate the helix so that the starting point aligns with the profile
+ mov.SetTranslation(startOffset*gp_Vec(dir_axis1));
+ TopLoc_Location loc(mov);
+ path.Move(loc);
+ }
+
+ if (turned) { // turn the helix so that the starting point aligns with the profile
+ mov.SetRotation(gp_Ax1(origo, dir_axis1), 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());
+
+
+# if OCC_VERSION_HEX < 0x70500
+ /* I initially tried using path.Move(invObjLoc) like usual. But it does not give the right result
+ * The starting point of the helix is not correct and I don't know why! With below hack it works.
+ */
+ Base::Vector3d placeAxis;
+ double placeAngle;
+ this->Placement.getValue().getRotation().getValue(placeAxis, placeAngle);
+ gp_Dir placeDir(placeAxis.x, placeAxis.y, placeAxis.z);
+ mov.SetRotation(gp_Ax1(origo, placeDir), placeAngle);
+ TopLoc_Location loc2(mov);
+ path.Move(loc2.Inverted());
+# else
+ TopLoc_Location invObjLoc = this->getLocation().Inverted();
+ path.Move(invObjLoc);
+# endif
+
+ return path;
+}
+
+// this function calculates self intersection safe pitch based on the profile bounding box.
+double Helix::safePitch()
+{
+ // Below is an approximation. It is possible to do the general way by solving for the pitch
+ // where the helix is self intersecting.
+
+ double angle = Angle.getValue()/180.0*PI;
+
+ TopoDS_Shape sketchshape = getVerifiedFace();
+ Bnd_Box bb;
+ BRepBndLib::Add(sketchshape, bb);
+
+ double Xmin, Ymin, Zmin, Xmax, Ymax, Zmax;
+ bb.Get(Xmin, Ymin, Zmin, Xmax, Ymax, Zmax);
+
+ double X = Xmax - Xmin, Y = Ymax - Ymin, Z = Zmax - Zmin;
+
+ Base::Vector3d v = Axis.getValue();
+ gp_Dir dir(v.x,v.y,v.z);
+ gp_Vec bbvec(X, Y, Z);
+
+ double p0 = bbvec*dir; // safe pitch if angle=0
+
+ Base::Vector3d n = getProfileNormal();
+ Base::Vector3d s = v.Cross(n); // pointing towards the desired helix start point.
+ gp_Dir dir_s(s.x, s.y, s.z);
+
+ if (tan(abs(angle))*p0 > abs(bbvec*dir_s))
+ return abs(bbvec*dir_s)/tan(abs(angle));
+ else
+ return p0;
+}
+
+// this function proposes pitch and height
+void Helix::proposeParameters(bool force)
+{
+ if (force || !HasBeenEdited.getValue()) {
+ double pitch = 1.1*safePitch();
+ Pitch.setValue(pitch);
+ Height.setValue(pitch*3.0);
+ HasBeenEdited.setValue(1);
+ }
+}
+
+
+PROPERTY_SOURCE(PartDesign::AdditiveHelix, PartDesign::Helix)
+AdditiveHelix::AdditiveHelix() {
+ addSubType = Additive;
+}
+
+PROPERTY_SOURCE(PartDesign::SubtractiveHelix, PartDesign::Helix)
+SubtractiveHelix::SubtractiveHelix() {
+ addSubType = Subtractive;
+}
diff --git a/src/Mod/PartDesign/App/FeatureHelix.h b/src/Mod/PartDesign/App/FeatureHelix.h
new file mode 100644
index 0000000000..ad5fad0111
--- /dev/null
+++ b/src/Mod/PartDesign/App/FeatureHelix.h
@@ -0,0 +1,103 @@
+/***************************************************************************
+ * Copyright (c) 2010 Juergen Riegel *
+ * *
+ * 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 *
+ * *
+ ***************************************************************************/
+
+
+#ifndef PARTDESIGN_Helix_H
+#define PARTDESIGN_Helix_H
+
+#include
+#include "FeatureSketchBased.h"
+#include
+
+namespace PartDesign
+{
+
+class PartDesignExport Helix : public ProfileBased
+{
+ PROPERTY_HEADER(PartDesign::Helix);
+
+public:
+ Helix();
+
+ App::PropertyVector Base;
+ App::PropertyVector Axis;
+ App::PropertyLength Pitch;
+ App::PropertyLength Height;
+ App::PropertyFloat Turns;
+ App::PropertyBool LeftHanded;
+ App::PropertyAngle Angle;
+ App::PropertyEnumeration Mode;
+ App::PropertyBool Outside;
+ App::PropertyBool HasBeenEdited;
+
+ /** if this property is set to a valid link, both Axis and Base properties
+ * are calculated according to the linked line
+ */
+ App::PropertyLinkSub ReferenceAxis;
+
+ /** @name methods override feature */
+ //@{
+ App::DocumentObjectExecReturn *execute(void);
+ short mustExecute() const;
+ /// returns the type name of the view provider
+ const char* getViewProviderName(void) const {
+ return "PartDesignGui::ViewProviderHelix";
+ }
+ //@}
+
+ void proposeParameters(bool force = false);
+ double safePitch(void);
+
+protected:
+ /// updates Axis from ReferenceAxis
+ void updateAxis(void);
+
+ /// generate helix and move it to the right location.
+ TopoDS_Shape generateHelixPath(void);
+
+ // project shape on plane. Used for detecting self intersection.
+ TopoDS_Shape projectShape(const TopoDS_Shape& input, const gp_Ax2& plane);
+
+private:
+ static const char* ModeEnums[];
+};
+
+
+class PartDesignExport AdditiveHelix : public Helix {
+
+ PROPERTY_HEADER(PartDesign::AdditiveHelix);
+public:
+ AdditiveHelix();
+};
+
+
+class PartDesignExport SubtractiveHelix : public Helix {
+
+ PROPERTY_HEADER(PartDesign::SubtractiveHelix);
+public:
+ SubtractiveHelix();
+};
+
+} //namespace PartDesign
+
+
+#endif // PART_Helix_H
diff --git a/src/Mod/PartDesign/App/FeatureSketchBased.cpp b/src/Mod/PartDesign/App/FeatureSketchBased.cpp
index 8fe9f1dc58..f92b3299fc 100644
--- a/src/Mod/PartDesign/App/FeatureSketchBased.cpp
+++ b/src/Mod/PartDesign/App/FeatureSketchBased.cpp
@@ -208,7 +208,7 @@ TopoDS_Shape ProfileBased::getVerifiedFace(bool silent) const {
if(faces.empty()) {
if(!shape.hasSubShape(TopAbs_WIRE))
shape = shape.makEWires();
- if(shape.hasSubShape(TopAbs_WIRE))
+ if(shape.hasSubShape(TopAbs_WIRE))
shape = shape.makEFace(0,"Part::FaceMakerCheese");
else
err = "Cannot make face from profile";
@@ -1012,7 +1012,7 @@ double ProfileBased::getReversedAngle(const Base::Vector3d &b, const Base::Vecto
}
void ProfileBased::getAxis(const App::DocumentObject *pcReferenceAxis, const std::vector &subReferenceAxis,
- Base::Vector3d& base, Base::Vector3d& dir)
+ Base::Vector3d& base, Base::Vector3d& dir, bool checkPerpendicular)
{
dir = Base::Vector3d(0,0,0); // If unchanged signals that no valid axis was found
if (pcReferenceAxis == NULL)
@@ -1071,7 +1071,7 @@ void ProfileBased::getAxis(const App::DocumentObject *pcReferenceAxis, const std
dir = line->getDirection();
// Check that axis is perpendicular with sketch plane!
- if (sketchplane.Axis().Direction().IsParallel(gp_Dir(dir.x, dir.y, dir.z), Precision::Angular()))
+ if (checkPerpendicular && sketchplane.Axis().Direction().IsParallel(gp_Dir(dir.x, dir.y, dir.z), Precision::Angular()))
throw Base::ValueError("Rotation axis must not be perpendicular with the sketch plane");
return;
}
@@ -1082,7 +1082,7 @@ void ProfileBased::getAxis(const App::DocumentObject *pcReferenceAxis, const std
line->Placement.getValue().multVec(Base::Vector3d (1,0,0), dir);
// Check that axis is perpendicular with sketch plane!
- if (sketchplane.Axis().Direction().IsParallel(gp_Dir(dir.x, dir.y, dir.z), Precision::Angular()))
+ if (checkPerpendicular && sketchplane.Axis().Direction().IsParallel(gp_Dir(dir.x, dir.y, dir.z), Precision::Angular()))
throw Base::ValueError("Rotation axis must not be perpendicular with the sketch plane");
return;
}
diff --git a/src/Mod/PartDesign/App/FeatureSketchBased.h b/src/Mod/PartDesign/App/FeatureSketchBased.h
index 207bde6d85..7e13019ba4 100644
--- a/src/Mod/PartDesign/App/FeatureSketchBased.h
+++ b/src/Mod/PartDesign/App/FeatureSketchBased.h
@@ -78,7 +78,7 @@ public:
* Default is false.
*/
Part::Part2DObject* getVerifiedSketch(bool silent=false) const;
-
+
/**
* Verifies the linked Profile and returns it if it is a valid object
* @param silent if profile property is malformed and the parameter is true
@@ -86,7 +86,7 @@ public:
* Default is false.
*/
Part::Feature* getVerifiedObject(bool silent=false) const;
-
+
/**
* Verifies the linked Object and returns the shape used as profile
* @param silent if profirle property is malformed and the parameter is true
@@ -94,25 +94,25 @@ public:
* Default is false.
*/
TopoDS_Shape getVerifiedFace(bool silent = false) const;
-
+
/// Returns the wires the sketch is composed of
std::vector getProfileWires() const;
-
+
/// Returns the face of the sketch support (if any)
const TopoDS_Face getSupportFace() const;
-
+
Base::Vector3d getProfileNormal() const;
Part::TopoShape getProfileShape() const;
/// retrieves the number of axes in the linked sketch (defined as construction lines)
- int getSketchAxisCount(void) const;
+ int getSketchAxisCount(void) const;
virtual Part::Feature* getBaseObject(bool silent=false) const;
-
+
//backwards compatibility: profile property was renamed and has different type now
virtual void Restore(Base::XMLReader& reader);
-
+
protected:
void remapSupportShape(const TopoDS_Shape&);
@@ -167,8 +167,8 @@ protected:
double getReversedAngle(const Base::Vector3d& b, const Base::Vector3d& v);
/// get Axis from ReferenceAxis
void getAxis(const App::DocumentObject* pcReferenceAxis, const std::vector& subReferenceAxis,
- Base::Vector3d& base, Base::Vector3d& dir);
-
+ Base::Vector3d& base, Base::Vector3d& dir, bool checkPerpendicular=true);
+
void onChanged(const App::Property* prop);
private:
bool isParallelPlane(const TopoDS_Shape&, const TopoDS_Shape&) const;
diff --git a/src/Mod/PartDesign/Gui/AppPartDesignGui.cpp b/src/Mod/PartDesign/Gui/AppPartDesignGui.cpp
index 880ffddefb..32fcab7467 100644
--- a/src/Mod/PartDesign/Gui/AppPartDesignGui.cpp
+++ b/src/Mod/PartDesign/Gui/AppPartDesignGui.cpp
@@ -61,6 +61,7 @@
#include "ViewProviderThickness.h"
#include "ViewProviderPipe.h"
#include "ViewProviderLoft.h"
+#include "ViewProviderHelix.h"
#include "ViewProviderShapeBinder.h"
#include "ViewProviderBase.h"
@@ -156,6 +157,7 @@ PyMOD_INIT_FUNC(PartDesignGui)
PartDesignGui::ViewProviderPrimitive ::init();
PartDesignGui::ViewProviderPipe ::init();
PartDesignGui::ViewProviderLoft ::init();
+ PartDesignGui::ViewProviderHelix ::init();
PartDesignGui::ViewProviderBase ::init();
// add resources and reloads the translators
diff --git a/src/Mod/PartDesign/Gui/CMakeLists.txt b/src/Mod/PartDesign/Gui/CMakeLists.txt
index 292221c8c6..2a2ce18401 100644
--- a/src/Mod/PartDesign/Gui/CMakeLists.txt
+++ b/src/Mod/PartDesign/Gui/CMakeLists.txt
@@ -55,6 +55,7 @@ set(PartDesignGui_MOC_HDRS
TaskPrimitiveParameters.h
TaskPipeParameters.h
TaskLoftParameters.h
+ TaskHelixParameters.h
)
fc_wrap_cpp(PartDesignGui_MOC_SRCS ${PartDesignGui_MOC_HDRS})
SOURCE_GROUP("Moc" FILES ${PartDesignGui_MOC_SRCS})
@@ -89,6 +90,7 @@ set(PartDesignGui_UIC_SRCS
TaskPipeScaling.ui
TaskLoftParameters.ui
DlgReference.ui
+ TaskHelixParameters.ui
)
if(BUILD_QT5)
@@ -158,7 +160,9 @@ SET(PartDesignGuiViewProvider_SRCS
ViewProviderPipe.cpp
ViewProviderLoft.h
ViewProviderLoft.cpp
- ViewProviderBase.h
+ ViewProviderHelix.h
+ ViewProviderHelix.cpp
+ ViewProviderBase.h
ViewProviderBase.cpp
)
SOURCE_GROUP("ViewProvider" FILES ${PartDesignGuiViewProvider_SRCS})
@@ -237,6 +241,9 @@ SET(PartDesignGuiTaskDlgs_SRCS
TaskLoftParameters.ui
TaskLoftParameters.h
TaskLoftParameters.cpp
+ TaskHelixParameters.ui
+ TaskHelixParameters.h
+ TaskHelixParameters.cpp
)
SOURCE_GROUP("TaskDialogs" FILES ${PartDesignGuiTaskDlgs_SRCS})
diff --git a/src/Mod/PartDesign/Gui/Command.cpp b/src/Mod/PartDesign/Gui/Command.cpp
index 22be4e650b..547821c59b 100644
--- a/src/Mod/PartDesign/Gui/Command.cpp
+++ b/src/Mod/PartDesign/Gui/Command.cpp
@@ -55,6 +55,7 @@
#include
#include
#include
+
#include
#include
#include
@@ -384,7 +385,7 @@ void CmdPartDesignSubShapeBinder::activated(int iMsg)
}
values = std::move(links);
}
-
+
PartDesign::SubShapeBinder *binder = 0;
try {
openCommand(QT_TRANSLATE_NOOP("Command", "Create SubShapeBinder"));
@@ -403,7 +404,7 @@ void CmdPartDesignSubShapeBinder::activated(int iMsg)
commitCommand();
} catch (Base::Exception &e) {
e.ReportException();
- QMessageBox::critical(Gui::getMainWindow(),
+ QMessageBox::critical(Gui::getMainWindow(),
QObject::tr("Sub-Shape Binder"), QString::fromUtf8(e.what()));
abortCommand();
}
@@ -994,7 +995,7 @@ void prepareProfileBased(PartDesign::Body *pcActiveBody, Gui::Command* cmd, cons
FCMD_OBJ_CMD(pcActiveBody,"newObject('PartDesign::" << which << "','" << FeatName << "')");
auto Feat = pcActiveBody->getDocument()->getObject(FeatName.c_str());
-
+
auto objCmd = Gui::Command::getObjectCmd(feature);
if (feature->isDerivedFrom(Part::Part2DObject::getClassTypeId()) || subs.empty()) {
FCMD_OBJ_CMD(Feat,"Profile = " << objCmd);
@@ -1003,7 +1004,7 @@ void prepareProfileBased(PartDesign::Body *pcActiveBody, Gui::Command* cmd, cons
std::ostringstream ss;
for (auto &s : subs)
ss << "'" << s << "',";
- FCMD_OBJ_CMD(Feat,"Profile = (" << objCmd << ", [" << ss.str() << "])");
+ FCMD_OBJ_CMD(Feat,"Profile = (" << objCmd << ", [" << ss.str() << "])");
}
//for additive and subtractive lofts allow the user to preselect the sections
@@ -1042,10 +1043,44 @@ void prepareProfileBased(PartDesign::Body *pcActiveBody, Gui::Command* cmd, cons
func(static_cast(feature), Feat);
};
+
+ // in case of subtractive types, check that there is something to subtract from
+ if ((which.find("Subtractive") != std::string::npos) ||
+ (which.compare("Groove") == 0) ||
+ (which.compare("Pocket") == 0)) {
+
+ if (!pcActiveBody->isSolid()) {
+ QMessageBox msgBox;
+ msgBox.setText(QObject::tr("Cannot use this command as there is no solid to subtract from."));
+ msgBox.setInformativeText(QObject::tr("Ensure that the body contains a feature before attempting a subtractive command."));
+ msgBox.setStandardButtons(QMessageBox::Ok);
+ msgBox.setDefaultButton(QMessageBox::Ok);
+ msgBox.exec();
+ return;
+ }
+ }
+
+
//if a profile is selected we can make our life easy and fast
std::vector selection = cmd->getSelection().getSelectionEx();
if (!selection.empty()) {
- base_worker(selection.front().getObject(), selection.front().getSubNames());
+ bool onlyAllowed = true;
+ for (auto it = selection.begin(); it!=selection.end(); ++it){
+ if (PartDesign::Body::findBodyOf((*it).getObject()) != pcActiveBody) { // the selected objects must belong to the body
+ onlyAllowed = false;
+ break;
+ }
+ }
+ if (!onlyAllowed) {
+ QMessageBox msgBox;
+ msgBox.setText(QObject::tr("Cannot use selected object. Selected object must belong to the active body"));
+ msgBox.setInformativeText(QObject::tr("Consider using a ShapeBinder or a BaseFeature to reference external geometry in a body."));
+ msgBox.setStandardButtons(QMessageBox::Ok);
+ msgBox.setDefaultButton(QMessageBox::Ok);
+ msgBox.exec();
+ } else {
+ base_worker(selection.front().getObject(), selection.front().getSubNames());
+ }
return;
}
@@ -1418,7 +1453,7 @@ void CmdPartDesignGroove::activated(int iMsg)
else {
FCMD_OBJ_CMD(Feat,"ReferenceAxis = ("<getOrigin()->getY())<<",[''])");
}
-
+
FCMD_OBJ_CMD(Feat,"Angle = 360.0");
try {
@@ -1643,6 +1678,119 @@ bool CmdPartDesignSubtractiveLoft::isActive(void)
return hasActiveDocument();
}
+//===========================================================================
+// PartDesign_Additive_Helix
+//===========================================================================
+DEF_STD_CMD_A(CmdPartDesignAdditiveHelix)
+
+CmdPartDesignAdditiveHelix::CmdPartDesignAdditiveHelix()
+ : Command("PartDesign_AdditiveHelix")
+{
+ sAppModule = "PartDesign";
+ sGroup = QT_TR_NOOP("PartDesign");
+ sMenuText = QT_TR_NOOP("Additive helix");
+ sToolTipText = QT_TR_NOOP("Sweep a selected sketch along a helix");
+ sWhatsThis = "PartDesign_AdditiveHelix";
+ sStatusTip = sToolTipText;
+ sPixmap = "PartDesign_Additive_Helix";
+}
+
+void CmdPartDesignAdditiveHelix::activated(int iMsg)
+{
+ Q_UNUSED(iMsg);
+ App::Document *doc = getDocument();
+ if (!PartDesignGui::assureModernWorkflow(doc))
+ return;
+
+ PartDesign::Body *pcActiveBody = PartDesignGui::getBody(true);
+
+ if (!pcActiveBody)
+ return;
+
+ Gui::Command* cmd = this;
+ auto worker = [cmd, &pcActiveBody](Part::Feature* sketch, App::DocumentObject *Feat) {
+
+ if (!Feat) return;
+
+ // specific parameters for helix
+ Gui::Command::updateActive();
+
+ if (sketch->isDerivedFrom(Part::Part2DObject::getClassTypeId())) {
+ FCMD_OBJ_CMD(Feat,"ReferenceAxis = (" << getObjectCmd(sketch) << ",['V_Axis'])");
+ }
+ else {
+ FCMD_OBJ_CMD(Feat,"ReferenceAxis = (" << getObjectCmd(pcActiveBody->getOrigin()->getY()) << ",[''])");
+ }
+
+ finishProfileBased(cmd, sketch, Feat);
+ cmd->adjustCameraPosition();
+ };
+
+ prepareProfileBased(pcActiveBody, this, "AdditiveHelix", worker);
+}
+
+bool CmdPartDesignAdditiveHelix::isActive(void)
+{
+ return hasActiveDocument();
+}
+
+
+//===========================================================================
+// PartDesign_Subtractive_Helix
+//===========================================================================
+DEF_STD_CMD_A(CmdPartDesignSubtractiveHelix)
+
+CmdPartDesignSubtractiveHelix::CmdPartDesignSubtractiveHelix()
+ : Command("PartDesign_SubtractiveHelix")
+{
+ sAppModule = "PartDesign";
+ sGroup = QT_TR_NOOP("PartDesign");
+ sMenuText = QT_TR_NOOP("Subtractive helix");
+ sToolTipText = QT_TR_NOOP("Sweep a selected sketch along a helix and remove it from the body");
+ sWhatsThis = "PartDesign_SubtractiveHelix";
+ sStatusTip = sToolTipText;
+ sPixmap = "PartDesign_Subtractive_Helix";
+}
+
+void CmdPartDesignSubtractiveHelix::activated(int iMsg)
+{
+ Q_UNUSED(iMsg);
+ App::Document *doc = getDocument();
+ if (!PartDesignGui::assureModernWorkflow(doc))
+ return;
+
+ PartDesign::Body *pcActiveBody = PartDesignGui::getBody(true);
+
+ if (!pcActiveBody)
+ return;
+
+ Gui::Command* cmd = this;
+ auto worker = [cmd, &pcActiveBody](Part::Feature* sketch, App::DocumentObject *Feat) {
+
+ if (!Feat) return;
+
+ // specific parameters for helix
+ Gui::Command::updateActive();
+
+ if (sketch->isDerivedFrom(Part::Part2DObject::getClassTypeId())) {
+ FCMD_OBJ_CMD(Feat,"ReferenceAxis = (" << getObjectCmd(sketch) << ",['V_Axis'])");
+ }
+ else {
+ FCMD_OBJ_CMD(Feat,"ReferenceAxis = (" << getObjectCmd(pcActiveBody->getOrigin()->getY()) << ",[''])");
+ }
+
+ finishProfileBased(cmd, sketch, Feat);
+ cmd->adjustCameraPosition();
+ };
+
+ prepareProfileBased(pcActiveBody, this, "SubtractiveHelix", worker);
+}
+
+bool CmdPartDesignSubtractiveHelix::isActive(void)
+{
+ return hasActiveDocument();
+}
+
//===========================================================================
// Common utility functions for Dressup features
//===========================================================================
@@ -2176,7 +2324,7 @@ void CmdPartDesignPolarPattern::activated(int iMsg)
}
if (!direction) {
auto body = static_cast(Part::BodyBase::findBodyOf(features.front()));
- if (body) {
+ if (body) {
FCMD_OBJ_CMD(Feat,"Axis = ("<getOrigin()->getZ())<<",[''])");
}
}
@@ -2397,7 +2545,7 @@ void CmdPartDesignBoolean::activated(int iMsg)
std::string FeatName = getUniqueObjectName("Boolean",pcActiveBody);
FCMD_OBJ_CMD(pcActiveBody,"newObject('PartDesign::Boolean','"<getDocument()->getObject(FeatName.c_str());
-
+
// If we don't add an object to the boolean group then don't update the body
// as otherwise this will fail and it will be marked as invalid
bool updateDocument = false;
@@ -2456,6 +2604,8 @@ void CreatePartDesignCommands(void)
rcCmdMgr.addCommand(new CmdPartDesignSubtractivePipe);
rcCmdMgr.addCommand(new CmdPartDesignAdditiveLoft);
rcCmdMgr.addCommand(new CmdPartDesignSubtractiveLoft);
+ rcCmdMgr.addCommand(new CmdPartDesignAdditiveHelix);
+ rcCmdMgr.addCommand(new CmdPartDesignSubtractiveHelix);
rcCmdMgr.addCommand(new CmdPartDesignFillet());
rcCmdMgr.addCommand(new CmdPartDesignDraft());
diff --git a/src/Mod/PartDesign/Gui/Resources/PartDesign.qrc b/src/Mod/PartDesign/Gui/Resources/PartDesign.qrc
index 729a9c3b9a..bb55317411 100644
--- a/src/Mod/PartDesign/Gui/Resources/PartDesign.qrc
+++ b/src/Mod/PartDesign/Gui/Resources/PartDesign.qrc
@@ -6,6 +6,7 @@
icons/PartDesign_Additive_Ellipsoid.svgicons/PartDesign_Additive_Loft.svgicons/PartDesign_Additive_Pipe.svg
+ icons/PartDesign_Additive_Helix.svgicons/PartDesign_Additive_Prism.svgicons/PartDesign_Additive_Sphere.svgicons/PartDesign_Additive_Torus.svg
@@ -50,6 +51,7 @@
icons/PartDesign_Subtractive_Ellipsoid.svgicons/PartDesign_Subtractive_Loft.svgicons/PartDesign_Subtractive_Pipe.svg
+ icons/PartDesign_Subtractive_Helix.svgicons/PartDesign_Subtractive_Prism.svgicons/PartDesign_Subtractive_Sphere.svgicons/PartDesign_Subtractive_Torus.svg
diff --git a/src/Mod/PartDesign/Gui/Resources/icons/PartDesign_Additive_Helix.svg b/src/Mod/PartDesign/Gui/Resources/icons/PartDesign_Additive_Helix.svg
new file mode 100644
index 0000000000..ed20f4cb1c
--- /dev/null
+++ b/src/Mod/PartDesign/Gui/Resources/icons/PartDesign_Additive_Helix.svg
@@ -0,0 +1,1456 @@
+
+
diff --git a/src/Mod/PartDesign/Gui/Resources/icons/PartDesign_Subtractive_Helix.svg b/src/Mod/PartDesign/Gui/Resources/icons/PartDesign_Subtractive_Helix.svg
new file mode 100644
index 0000000000..91e1d56a6d
--- /dev/null
+++ b/src/Mod/PartDesign/Gui/Resources/icons/PartDesign_Subtractive_Helix.svg
@@ -0,0 +1,1456 @@
+
+
diff --git a/src/Mod/PartDesign/Gui/TaskHelixParameters.cpp b/src/Mod/PartDesign/Gui/TaskHelixParameters.cpp
new file mode 100644
index 0000000000..2fa03e580c
--- /dev/null
+++ b/src/Mod/PartDesign/Gui/TaskHelixParameters.cpp
@@ -0,0 +1,511 @@
+/***************************************************************************
+ * Copyright (c) 2011 Juergen Riegel *
+ * 2020 David Österberg *
+ * *
+ * 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_
+#endif
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "ReferenceSelection.h"
+#include "Utils.h"
+
+#include "ui_TaskHelixParameters.h"
+#include "TaskHelixParameters.h"
+
+using namespace PartDesignGui;
+using namespace Gui;
+
+
+/* TRANSLATOR PartDesignGui::TaskHelixParameters */
+
+TaskHelixParameters::TaskHelixParameters(PartDesignGui::ViewProviderHelix *HelixView, QWidget *parent)
+ : TaskSketchBasedParameters(HelixView, parent, "PartDesign_Additive_Helix",tr("Helix parameters")),
+ ui (new Ui_TaskHelixParameters)
+{
+ // we need a separate container widget to add all controls to
+ proxy = new QWidget(this);
+ ui->setupUi(proxy);
+ QMetaObject::connectSlotsByName(this);
+
+ connect(ui->pitch, SIGNAL(valueChanged(double)),
+ this, SLOT(onPitchChanged(double)));
+ connect(ui->height, SIGNAL(valueChanged(double)),
+ this, SLOT(onHeightChanged(double)));
+ connect(ui->turns, SIGNAL(valueChanged(double)),
+ this, SLOT(onTurnsChanged(double)));
+ connect(ui->coneAngle, SIGNAL(valueChanged(double)),
+ this, SLOT(onAngleChanged(double)));
+ connect(ui->axis, SIGNAL(activated(int)),
+ this, SLOT(onAxisChanged(int)));
+ connect(ui->checkBoxLeftHanded, SIGNAL(toggled(bool)),
+ this, SLOT(onLeftHandedChanged(bool)));
+ connect(ui->checkBoxReversed, SIGNAL(toggled(bool)),
+ this, SLOT(onReversedChanged(bool)));
+ connect(ui->checkBoxUpdateView, SIGNAL(toggled(bool)),
+ this, SLOT(onUpdateView(bool)));
+ connect(ui->inputMode, SIGNAL(activated(int)),
+ this, SLOT(onModeChanged(int)));
+ connect(ui->checkBoxOutside, SIGNAL(toggled(bool)),
+ this, SLOT(onOutsideChanged(bool)));
+
+ this->groupLayout()->addWidget(proxy);
+
+ // Temporarily prevent unnecessary feature recomputes
+ ui->axis->blockSignals(true);
+ ui->pitch->blockSignals(true);
+ ui->height->blockSignals(true);
+ ui->turns->blockSignals(true);
+ ui->coneAngle->blockSignals(true);
+ ui->checkBoxLeftHanded->blockSignals(true);
+ ui->checkBoxReversed->blockSignals(true);
+ ui->checkBoxOutside->blockSignals(true);
+
+ //bind property mirrors
+ PartDesign::ProfileBased* pcFeat = static_cast(vp->getObject());
+
+ PartDesign::Helix* rev = static_cast(vp->getObject());
+
+ if (!(rev->HasBeenEdited).getValue()) {
+ rev->proposeParameters();
+ recomputeFeature();
+ }
+
+ this->propAngle = &(rev->Angle);
+ this->propPitch = &(rev->Pitch);
+ this->propHeight = &(rev->Height);
+ this->propTurns = &(rev->Turns);
+ this->propReferenceAxis = &(rev->ReferenceAxis);
+ this->propLeftHanded = &(rev->LeftHanded);
+ this->propReversed = &(rev->Reversed);
+ this->propMode = &(rev->Mode);
+ this->propOutside = &(rev->Outside);
+
+ double pitch = propPitch->getValue();
+ double height = propHeight->getValue();
+ double turns = propTurns->getValue();
+ double angle = propAngle->getValue();
+ bool leftHanded = propLeftHanded->getValue();
+ bool reversed = propReversed->getValue();
+ int index = propMode->getValue();
+ bool outside = propOutside->getValue();
+
+ ui->pitch->setValue(pitch);
+ ui->height->setValue(height);
+ ui->turns->setValue(turns);
+ ui->coneAngle->setValue(angle);
+ ui->checkBoxLeftHanded->setChecked(leftHanded);
+ ui->checkBoxReversed->setChecked(reversed);
+ ui->inputMode->setCurrentIndex(index);
+ ui->checkBoxOutside->setChecked(outside);
+
+ blockUpdate = false;
+ updateUI();
+
+ // enable use of parametric expressions for the numerical fields
+ ui->pitch->bind(static_cast(pcFeat)->Pitch);
+ ui->height->bind(static_cast(pcFeat)->Height);
+ ui->turns->bind(static_cast(pcFeat)->Turns);
+ ui->coneAngle->bind(static_cast(pcFeat)->Angle);
+
+ ui->axis->blockSignals(false);
+ ui->pitch->blockSignals(false);
+ ui->height->blockSignals(false);
+ ui->turns->blockSignals(false);
+ ui->coneAngle->blockSignals(false);
+ ui->checkBoxLeftHanded->blockSignals(false);
+ ui->checkBoxReversed->blockSignals(false);
+ ui->checkBoxOutside->blockSignals(false);
+
+ setFocus ();
+
+ //show the parts coordinate system axis for selection
+ PartDesign::Body * body = PartDesign::Body::findBodyOf ( vp->getObject () );
+ if(body) {
+ try {
+ App::Origin *origin = body->getOrigin();
+ ViewProviderOrigin* vpOrigin;
+ vpOrigin = static_cast(Gui::Application::Instance->getViewProvider(origin));
+ vpOrigin->setTemporaryVisibility(true, false);
+ } catch (const Base::Exception &ex) {
+ ex.ReportException();
+ }
+ }
+}
+
+void TaskHelixParameters::fillAxisCombo(bool forceRefill)
+{
+ bool oldVal_blockUpdate = blockUpdate;
+ blockUpdate = true;
+
+ if (axesInList.empty())
+ forceRefill = true;//not filled yet, full refill
+
+ if (forceRefill){
+ ui->axis->clear();
+
+ this->axesInList.clear();
+
+ //add sketch axes
+ PartDesign::ProfileBased* pcFeat = static_cast(vp->getObject());
+ Part::Part2DObject* pcSketch = dynamic_cast(pcFeat->Profile.getValue());
+ if (pcSketch){
+ addAxisToCombo(pcSketch,"V_Axis",QObject::tr("Vertical sketch axis"));
+ addAxisToCombo(pcSketch,"H_Axis",QObject::tr("Horizontal sketch axis"));
+ for (int i=0; i < pcSketch->getAxisCount(); i++) {
+ QString itemText = QObject::tr("Construction line %1").arg(i+1);
+ std::stringstream sub;
+ sub << "Axis" << i;
+ addAxisToCombo(pcSketch,sub.str(),itemText);
+ }
+ }
+
+ //add part axes
+ PartDesign::Body * body = PartDesign::Body::findBodyOf ( pcFeat );
+ if (body) {
+ try {
+ App::Origin* orig = body->getOrigin();
+ addAxisToCombo(orig->getX(),"",tr("Base X axis"));
+ addAxisToCombo(orig->getY(),"",tr("Base Y axis"));
+ addAxisToCombo(orig->getZ(),"",tr("Base Z axis"));
+ } catch (const Base::Exception &ex) {
+ ex.ReportException();
+ }
+ }
+
+ //add "Select reference"
+ addAxisToCombo(0,std::string(),tr("Select reference..."));
+ }//endif forceRefill
+
+ //add current link, if not in list
+ //first, figure out the item number for current axis
+ int indexOfCurrent = -1;
+ App::DocumentObject* ax = propReferenceAxis->getValue();
+ const std::vector &subList = propReferenceAxis->getSubValues();
+ for (size_t i = 0; i < axesInList.size(); i++) {
+ if (ax == axesInList[i]->getValue() && subList == axesInList[i]->getSubValues())
+ indexOfCurrent = i;
+ }
+ if (indexOfCurrent == -1 && ax) {
+ assert(subList.size() <= 1);
+ std::string sub;
+ if (!subList.empty())
+ sub = subList[0];
+ addAxisToCombo(ax, sub, getRefStr(ax, subList));
+ indexOfCurrent = axesInList.size()-1;
+ }
+
+ //highlight current.
+ if (indexOfCurrent != -1)
+ ui->axis->setCurrentIndex(indexOfCurrent);
+
+ blockUpdate = oldVal_blockUpdate;
+}
+
+void TaskHelixParameters::addAxisToCombo(App::DocumentObject* linkObj,
+ std::string linkSubname,
+ QString itemText)
+{
+ this->ui->axis->addItem(itemText);
+ this->axesInList.emplace_back(new App::PropertyLinkSub);
+ App::PropertyLinkSub &lnk = *(axesInList[axesInList.size()-1]);
+ lnk.setValue(linkObj,std::vector(1,linkSubname));
+}
+
+void TaskHelixParameters::updateUI()
+{
+ fillAxisCombo();
+
+ auto pcHelix = static_cast(vp->getObject());
+ auto status = std::string(pcHelix->getStatusString());
+ if (status.compare("Valid")==0 || status.compare("Touched")==0) {
+ if (pcHelix->safePitch() > propPitch->getValue())
+ status = "Warning: helix might be self intersecting";
+ else
+ status = "";
+ }
+ ui->labelMessage->setText(QString::fromUtf8(status.c_str()));
+
+ bool isPitchVisible = false;
+ bool isHeightVisible = false;
+ bool isTurnsVisible = false;
+ bool isOutsideVisible = false;
+
+ if(pcHelix->getAddSubType() == PartDesign::FeatureAddSub::Subtractive)
+ isOutsideVisible = true;
+
+ switch (propMode->getValue()) {
+ case 0:
+ isPitchVisible = true;
+ isHeightVisible = true;
+ break;
+ case 1:
+ isPitchVisible = true;
+ isTurnsVisible = true;
+ break;
+ default:
+ isHeightVisible = true;
+ isTurnsVisible = true;
+ }
+
+ ui->pitch->setVisible(isPitchVisible);
+ ui->labelPitch->setVisible(isPitchVisible);
+
+ ui->height->setVisible(isHeightVisible);
+ ui->labelHeight->setVisible(isHeightVisible);
+
+ ui->turns->setVisible(isTurnsVisible);
+ ui->labelTurns->setVisible(isTurnsVisible);
+
+ ui->checkBoxOutside->setVisible(isOutsideVisible);
+
+}
+
+void TaskHelixParameters::onSelectionChanged(const Gui::SelectionChanges& msg)
+{
+ if (msg.Type == Gui::SelectionChanges::AddSelection) {
+
+ exitSelectionMode();
+ std::vector axis;
+ App::DocumentObject* selObj;
+ if (getReferencedSelection(vp->getObject(), msg, selObj, axis) && selObj) {
+ propReferenceAxis->setValue(selObj, axis);
+ recomputeFeature();
+ updateUI();
+ }
+ }
+}
+
+
+void TaskHelixParameters::onPitchChanged(double len)
+{
+ propPitch->setValue(len);
+ recomputeFeature();
+ updateUI();
+}
+
+void TaskHelixParameters::onHeightChanged(double len)
+{
+ propHeight->setValue(len);
+ recomputeFeature();
+ updateUI();
+}
+
+void TaskHelixParameters::onTurnsChanged(double len)
+{
+ propTurns->setValue(len);
+ recomputeFeature();
+ updateUI();
+}
+
+void TaskHelixParameters::onAngleChanged(double len)
+{
+ propAngle->setValue(len);
+ recomputeFeature();
+ updateUI();
+}
+
+void TaskHelixParameters::onAxisChanged(int num)
+{
+ PartDesign::ProfileBased* pcHelix = static_cast(vp->getObject());
+
+ if (axesInList.empty())
+ return;
+
+ App::DocumentObject *oldRefAxis = propReferenceAxis->getValue();
+ std::vector oldSubRefAxis = propReferenceAxis->getSubValues();
+ std::string oldRefName;
+ if (!oldSubRefAxis.empty())
+ oldRefName = oldSubRefAxis.front();
+
+ App::PropertyLinkSub &lnk = *(axesInList[num]);
+ if (lnk.getValue() == 0) {
+ // enter reference selection mode
+ TaskSketchBasedParameters::onSelectReference(true, true, false, true);
+ } else {
+ if (!pcHelix->getDocument()->isIn(lnk.getValue())){
+ Base::Console().Error("Object was deleted\n");
+ return;
+ }
+ propReferenceAxis->Paste(lnk);
+ exitSelectionMode();
+ }
+
+ try {
+ App::DocumentObject *newRefAxis = propReferenceAxis->getValue();
+ const std::vector &newSubRefAxis = propReferenceAxis->getSubValues();
+ std::string newRefName;
+ if (!newSubRefAxis.empty())
+ newRefName = newSubRefAxis.front();
+
+ if (oldRefAxis != newRefAxis ||
+ oldSubRefAxis.size() != newSubRefAxis.size() ||
+ oldRefName != newRefName) {
+ bool reversed = propReversed->getValue();
+ if (reversed != propReversed->getValue()) {
+ propReversed->setValue(reversed);
+ ui->checkBoxReversed->blockSignals(true);
+ ui->checkBoxReversed->setChecked(reversed);
+ ui->checkBoxReversed->blockSignals(false);
+ }
+ }
+
+ recomputeFeature();
+ }
+ catch (const Base::Exception& e) {
+ e.ReportException();
+ }
+}
+
+void TaskHelixParameters::onModeChanged(int index)
+{
+
+ propMode->setValue(index);
+
+ ui->pitch->setValue(propPitch->getValue());
+ ui->height->setValue(propHeight->getValue());
+ ui->turns->setValue((propHeight->getValue())/(propPitch->getValue()));
+
+ recomputeFeature();
+ updateUI();
+}
+
+void TaskHelixParameters::onLeftHandedChanged(bool on)
+{
+ propLeftHanded->setValue(on);
+ recomputeFeature();
+}
+
+void TaskHelixParameters::onReversedChanged(bool on)
+{
+ propReversed->setValue(on);
+ recomputeFeature();
+ updateUI();
+}
+
+void TaskHelixParameters::onOutsideChanged(bool on)
+{
+ propOutside->setValue(on);
+ recomputeFeature();
+ updateUI();
+}
+
+
+TaskHelixParameters::~TaskHelixParameters()
+{
+ try {
+ //hide the parts coordinate system axis for selection
+ PartDesign::Body * body = vp ? PartDesign::Body::findBodyOf(vp->getObject()) : 0;
+ if (body) {
+ App::Origin *origin = body->getOrigin();
+ ViewProviderOrigin* vpOrigin;
+ vpOrigin = static_cast(Gui::Application::Instance->getViewProvider(origin));
+ vpOrigin->resetTemporaryVisibility();
+ }
+ } catch (const Base::Exception &ex) {
+ ex.ReportException();
+ }
+
+}
+
+void TaskHelixParameters::changeEvent(QEvent *e)
+{
+ TaskBox::changeEvent(e);
+ if (e->type() == QEvent::LanguageChange) {
+ ui->retranslateUi(proxy);
+ }
+}
+
+void TaskHelixParameters::getReferenceAxis(App::DocumentObject*& obj, std::vector& sub) const
+{
+ if (axesInList.empty())
+ throw Base::RuntimeError("Not initialized!");
+
+ int num = ui->axis->currentIndex();
+ const App::PropertyLinkSub &lnk = *(axesInList[num]);
+ if (lnk.getValue() == 0) {
+ throw Base::RuntimeError("Still in reference selection mode; reference wasn't selected yet");
+ } else {
+ PartDesign::ProfileBased* pcRevolution = static_cast(vp->getObject());
+ if (!pcRevolution->getDocument()->isIn(lnk.getValue())){
+ throw Base::RuntimeError("Object was deleted");
+ }
+
+ obj = lnk.getValue();
+ sub = lnk.getSubValues();
+ }
+}
+
+// this is used for logging the command fully when recording macros
+void TaskHelixParameters::apply()
+{
+ std::vector sub;
+ App::DocumentObject* obj;
+ getReferenceAxis(obj, sub);
+ std::string axis = buildLinkSingleSubPythonStr(obj, sub);
+ auto tobj = vp->getObject();
+ FCMD_OBJ_CMD(tobj,"ReferenceAxis = " << axis);
+ FCMD_OBJ_CMD(tobj,"Mode = " << propMode->getValue());
+ FCMD_OBJ_CMD(tobj,"Pitch = " << propPitch->getValue());
+ FCMD_OBJ_CMD(tobj,"Height = " << propHeight->getValue());
+ FCMD_OBJ_CMD(tobj,"Turns = " << propTurns->getValue());
+ FCMD_OBJ_CMD(tobj,"Angle = " << propAngle->getValue());
+ FCMD_OBJ_CMD(tobj,"LeftHanded = " << (propLeftHanded->getValue() ? 1 : 0));
+ FCMD_OBJ_CMD(tobj,"Reversed = " << (propReversed->getValue() ? 1 : 0));
+}
+
+
+//**************************************************************************
+//**************************************************************************
+// TaskDialog
+//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+TaskDlgHelixParameters::TaskDlgHelixParameters(ViewProviderHelix *HelixView)
+ : TaskDlgSketchBasedParameters(HelixView)
+{
+ assert(HelixView);
+ Content.push_back(new TaskHelixParameters(HelixView));
+}
+
+
+#include "moc_TaskHelixParameters.cpp"
diff --git a/src/Mod/PartDesign/Gui/TaskHelixParameters.h b/src/Mod/PartDesign/Gui/TaskHelixParameters.h
new file mode 100644
index 0000000000..442c8376e6
--- /dev/null
+++ b/src/Mod/PartDesign/Gui/TaskHelixParameters.h
@@ -0,0 +1,131 @@
+/***************************************************************************
+ * Copyright (c) 2011 Juergen Riegel *
+ * *
+ * 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 *
+ * *
+ ***************************************************************************/
+
+
+#ifndef GUI_TASKVIEW_TaskHelixParameters_H
+#define GUI_TASKVIEW_TaskHelixParameters_H
+
+#include
+#include
+#include
+
+#include "TaskSketchBasedParameters.h"
+#include "ViewProviderHelix.h"
+
+class Ui_TaskHelixParameters;
+
+namespace App {
+class Property;
+}
+
+namespace Gui {
+class ViewProvider;
+}
+
+namespace PartDesignGui {
+
+
+
+class TaskHelixParameters : public TaskSketchBasedParameters
+{
+ Q_OBJECT
+
+public:
+ TaskHelixParameters(ViewProviderHelix *HelixView,QWidget *parent = 0);
+ ~TaskHelixParameters();
+
+ virtual void apply() override;
+
+ /**
+ * @brief fillAxisCombo fills the combo and selects the item according to
+ * current value of revolution object's axis reference.
+ * @param forceRefill if true, the combo box will be completely refilled. If
+ * false, the current value of revolution object's axis will be added to the
+ * list (if necessary), and selected. If the list is empty, it will be refilled anyway.
+ */
+ void fillAxisCombo(bool forceRefill = false);
+ void addAxisToCombo(App::DocumentObject *linkObj, std::string linkSubname, QString itemText);
+
+private Q_SLOTS:
+ void onPitchChanged(double);
+ void onHeightChanged(double);
+ void onTurnsChanged(double);
+ void onAngleChanged(double);
+ void onAxisChanged(int);
+ void onLeftHandedChanged(bool);
+ void onReversedChanged(bool);
+ void onModeChanged(int);
+ void onOutsideChanged(bool);
+
+
+protected:
+ void onSelectionChanged(const Gui::SelectionChanges& msg) override;
+ void changeEvent(QEvent *e) override;
+ bool updateView() const;
+ void getReferenceAxis(App::DocumentObject *&obj, std::vector &sub) const;
+
+
+ //mirrors of helixes's properties
+ App::PropertyLength* propPitch;
+ App::PropertyLength* propHeight;
+ App::PropertyFloat* propTurns;
+ App::PropertyBool* propLeftHanded;
+ App::PropertyBool* propReversed;
+ App::PropertyLinkSub* propReferenceAxis;
+ App::PropertyAngle* propAngle;
+ App::PropertyEnumeration* propMode;
+ App::PropertyBool* propOutside;
+
+
+private:
+ void updateUI();
+
+private:
+ QWidget* proxy;
+ Ui_TaskHelixParameters* ui;
+
+ /**
+ * @brief axesInList is the list of links corresponding to axis combo; must
+ * be kept in sync with the combo. A special value of zero-pointer link is
+ * for "Select axis" item.
+ *
+ * It is a list of pointers, because properties prohibit assignment. Use new
+ * when adding stuff, and delete when removing stuff.
+ */
+ std::vector> axesInList;
+};
+
+/// simulation dialog for the TaskView
+class TaskDlgHelixParameters : public TaskDlgSketchBasedParameters
+{
+ Q_OBJECT
+
+public:
+ TaskDlgHelixParameters(ViewProviderHelix *HelixView);
+
+ ViewProviderHelix* getHelixView() const
+ { return static_cast(vp); }
+};
+
+} //namespace PartDesignGui
+
+#endif // GUI_TASKVIEW_TaskHelixParameters_H
diff --git a/src/Mod/PartDesign/Gui/TaskHelixParameters.ui b/src/Mod/PartDesign/Gui/TaskHelixParameters.ui
new file mode 100644
index 0000000000..ac4215ac14
--- /dev/null
+++ b/src/Mod/PartDesign/Gui/TaskHelixParameters.ui
@@ -0,0 +1,303 @@
+
+
+ PartDesignGui::TaskHelixParameters
+
+
+
+ 0
+ 0
+ 278
+ 193
+
+
+
+ Form
+
+
+
+
+
+
+
+
+ Status:
+
+
+
+
+
+
+ Valid
+
+
+
+
+
+
+
+
+
+
+
+ Axis:
+
+
+
+
+
+
+
+ Base X axis
+
+
+
+
+ Base Y axis
+
+
+
+
+ Base Z axis
+
+
+
+
+ Horizontal sketch axis
+
+
+
+
+ Vertical sketch axis
+
+
+
+
+ Select reference...
+
+
+
+
+
+
+
+
+
+
+
+
+ Mode:
+
+
+
+
+
+
+
+ Pitch-Height
+
+
+
+
+ Pitch-Turns
+
+
+
+
+ Height-Turns
+
+
+
+
+
+
+
+
+
+
+
+
+ Pitch:
+
+
+
+
+
+
+ false
+
+
+ mm
+
+
+ 0.000000000000000
+
+
+ 1.000000000000000
+
+
+ 10.000000000000000
+
+
+
+
+
+
+
+
+
+
+
+ Height:
+
+
+
+
+
+
+ false
+
+
+ mm
+
+
+ 0.000000000000000
+
+
+ 1.000000000000000
+
+
+ 30.000000000000000
+
+
+
+
+
+
+
+
+
+
+
+ Turns:
+
+
+
+
+
+
+ false
+
+
+ 0.000000000000000
+
+
+ 1.000000000000000
+
+
+ 3.0000000000000
+
+
+
+
+
+
+
+
+
+
+
+ Cone angle:
+
+
+
+
+
+
+ false
+
+
+ deg
+
+
+ -89.000000000000000
+
+
+ 89.000000000000000
+
+
+ 5.000000000000000
+
+
+ 0.000000000000000
+
+
+
+
+
+
+
+
+
+ true
+
+
+ Left handed
+
+
+
+
+
+
+
+ true
+
+
+ Reversed
+
+
+
+
+
+
+
+ Remove outside of profile
+
+
+ false
+
+
+
+
+
+
+
+ Qt::Horizontal
+
+
+
+
+
+
+
+ Update view
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+ Gui::QuantitySpinBox
+ QWidget
+ Gui/QuantitySpinBox.h
+
+
+
+
+
diff --git a/src/Mod/PartDesign/Gui/ViewProviderHelix.cpp b/src/Mod/PartDesign/Gui/ViewProviderHelix.cpp
new file mode 100644
index 0000000000..d1ec60c4c7
--- /dev/null
+++ b/src/Mod/PartDesign/Gui/ViewProviderHelix.cpp
@@ -0,0 +1,120 @@
+/***************************************************************************
+ * Copyright (c) 2011 Juergen Riegel *
+ * *
+ * 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
+# include
+#endif
+
+#include
+#include
+
+#include
+#include
+#include
+
+#include "TaskHelixParameters.h"
+#include "ViewProviderHelix.h"
+
+using namespace PartDesignGui;
+
+PROPERTY_SOURCE(PartDesignGui::ViewProviderHelix,PartDesignGui::ViewProvider)
+
+
+ViewProviderHelix::ViewProviderHelix()
+{
+}
+
+ViewProviderHelix::~ViewProviderHelix()
+{
+}
+
+void ViewProviderHelix::setupContextMenu(QMenu* menu, QObject* receiver, const char* member)
+{
+ QAction* act;
+ act = menu->addAction(QObject::tr("Edit helix"), receiver, member);
+ act->setData(QVariant((int)ViewProvider::Default));
+ PartDesignGui::ViewProviderAddSub::setupContextMenu(menu, receiver, member);
+}
+
+TaskDlgFeatureParameters *ViewProviderHelix::getEditDialog()
+{
+ return new TaskDlgHelixParameters( this );
+}
+
+QIcon ViewProviderHelix::getIcon(void) const {
+ QString str = QString::fromLatin1("PartDesign_");
+ auto* prim = static_cast(getObject());
+ if(prim->getAddSubType() == PartDesign::FeatureAddSub::Additive)
+ str += QString::fromLatin1("Additive_");
+ else
+ str += QString::fromLatin1("Subtractive_");
+
+ str += QString::fromLatin1("Helix.svg");
+ return PartDesignGui::ViewProvider::mergeGreyableOverlayIcons(Gui::BitmapFactory().pixmap(str.toStdString().c_str()));
+}
+
+bool ViewProviderHelix::setEdit(int ModNum)
+{
+
+ if (ModNum == ViewProvider::Default ) {
+ auto* prim = static_cast(getObject());
+ setPreviewDisplayMode(prim->getAddSubType() == PartDesign::FeatureAddSub::Subtractive);
+ }
+ return ViewProviderAddSub::setEdit(ModNum);
+}
+
+void ViewProviderHelix::unsetEdit(int ModNum)
+{
+ setPreviewDisplayMode(false);
+ // Rely on parent class to:
+ // restitute old workbench (set setEdit above) and close the dialog if exiting editing
+ PartDesignGui::ViewProvider::unsetEdit(ModNum);
+}
+
+std::vector ViewProviderHelix::claimChildren(void) const {
+ std::vector temp;
+ App::DocumentObject* sketch = static_cast(getObject())->Profile.getValue();
+ if (sketch != NULL && sketch->isDerivedFrom(Part::Part2DObject::getClassTypeId()))
+ temp.push_back(sketch);
+
+ return temp;
+}
+
+bool ViewProviderHelix::onDelete(const std::vector &s) {
+ PartDesign::ProfileBased* feature = static_cast(getObject());
+
+ // get the Sketch
+ Sketcher::SketchObject *pcSketch = 0;
+ if (feature->Profile.getValue())
+ pcSketch = static_cast(feature->Profile.getValue());
+
+ // if abort command deleted the object the sketch is visible again
+ if (pcSketch && Gui::Application::Instance->getViewProvider(pcSketch))
+ Gui::Application::Instance->getViewProvider(pcSketch)->show();
+
+ return ViewProvider::onDelete(s);
+}
+
diff --git a/src/Mod/PartDesign/Gui/ViewProviderHelix.h b/src/Mod/PartDesign/Gui/ViewProviderHelix.h
new file mode 100644
index 0000000000..83dd7d6d55
--- /dev/null
+++ b/src/Mod/PartDesign/Gui/ViewProviderHelix.h
@@ -0,0 +1,62 @@
+/***************************************************************************
+ * Copyright (c) 2011 Juergen Riegel *
+ * *
+ * 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 *
+ * *
+ ***************************************************************************/
+
+
+#ifndef PARTGUI_ViewProviderHelix_H
+#define PARTGUI_ViewProviderHelix_H
+
+#include "ViewProviderAddSub.h"
+
+
+namespace PartDesignGui {
+
+class PartDesignGuiExport ViewProviderHelix : public ViewProviderAddSub
+{
+ PROPERTY_HEADER(PartDesignGui::ViewProviderHelix);
+
+public:
+ /// constructor
+ ViewProviderHelix();
+ /// destructor
+ virtual ~ViewProviderHelix();
+
+ void setupContextMenu(QMenu*, QObject*, const char*);
+
+ /// grouping handling
+ std::vector claimChildren(void)const;
+
+ virtual bool onDelete(const std::vector &);
+
+protected:
+ virtual QIcon getIcon(void) const;
+
+ /// Returns a newly created TaskDlgHelixParameters
+ virtual TaskDlgFeatureParameters *getEditDialog();
+ virtual bool setEdit(int ModNum);
+ virtual void unsetEdit(int ModNum);
+};
+
+
+} // namespace PartDesignGui
+
+
+#endif // PARTGUI_ViewProviderHelix_H
diff --git a/src/Mod/PartDesign/Gui/ViewProviderSketchBased.cpp b/src/Mod/PartDesign/Gui/ViewProviderSketchBased.cpp
index fb47925a07..399ad44242 100644
--- a/src/Mod/PartDesign/Gui/ViewProviderSketchBased.cpp
+++ b/src/Mod/PartDesign/Gui/ViewProviderSketchBased.cpp
@@ -72,3 +72,4 @@ bool ViewProviderSketchBased::onDelete(const std::vector &s) {
return ViewProvider::onDelete(s);
}
+
diff --git a/src/Mod/PartDesign/Gui/ViewProviderSketchBased.h b/src/Mod/PartDesign/Gui/ViewProviderSketchBased.h
index 3831c3c923..cb5d9e18d3 100644
--- a/src/Mod/PartDesign/Gui/ViewProviderSketchBased.h
+++ b/src/Mod/PartDesign/Gui/ViewProviderSketchBased.h
@@ -23,7 +23,7 @@
#ifndef VIEWPROVIDERSKETCHBASED_H_QKP3UG9A
#define VIEWPROVIDERSKETCHBASED_H_QKP3UG9A
-#include "ViewProvider.h"
+#include "ViewProviderAddSub.h"
namespace PartDesignGui {
@@ -44,6 +44,7 @@ public:
std::vector claimChildren(void)const;
virtual bool onDelete(const std::vector &);
+
};
} /* PartDesignGui */
diff --git a/src/Mod/PartDesign/Gui/Workbench.cpp b/src/Mod/PartDesign/Gui/Workbench.cpp
index 073473aa8e..73b7576c16 100644
--- a/src/Mod/PartDesign/Gui/Workbench.cpp
+++ b/src/Mod/PartDesign/Gui/Workbench.cpp
@@ -409,6 +409,8 @@ void Workbench::activated()
"PartDesign_SubtractivePipe",
"PartDesign_AdditiveLoft",
"PartDesign_SubtractiveLoft",
+ "PartDesign_AdditiveHelix",
+ "PartDesign_SubtractiveHelix",
0};
Watcher.push_back(new Gui::TaskView::TaskWatcherCommands(
"SELECT Sketcher::SketchObject COUNT 1",
@@ -496,14 +498,14 @@ Gui::MenuItem* Workbench::setupMenuBar() const
Gui::MenuItem* additives = new Gui::MenuItem;
additives->setCommand("Create an additive feature");
*additives << "PartDesign_Pad" << "PartDesign_Revolution"
- << "PartDesign_AdditiveLoft" << "PartDesign_AdditivePipe";
+ << "PartDesign_AdditiveLoft" << "PartDesign_AdditivePipe" << "PartDesign_AdditiveHelix";
// subtractives
Gui::MenuItem* subtractives = new Gui::MenuItem;
subtractives->setCommand("Create a subtractive feature");
*subtractives << "PartDesign_Pocket" << "PartDesign_Hole"
<< "PartDesign_Groove" << "PartDesign_SubtractiveLoft"
- << "PartDesign_SubtractivePipe";
+ << "PartDesign_SubtractivePipe" << "PartDesign_SubtractiveHelix";
// transformations
Gui::MenuItem* transformations = new Gui::MenuItem;
@@ -598,6 +600,7 @@ Gui::ToolBarItem* Workbench::setupToolBars() const
<< "PartDesign_Revolution"
<< "PartDesign_AdditiveLoft"
<< "PartDesign_AdditivePipe"
+ << "PartDesign_AdditiveHelix"
<< "PartDesign_CompPrimitiveAdditive"
<< "Separator"
<< "PartDesign_Pocket"
@@ -605,6 +608,7 @@ Gui::ToolBarItem* Workbench::setupToolBars() const
<< "PartDesign_Groove"
<< "PartDesign_SubtractiveLoft"
<< "PartDesign_SubtractivePipe"
+ << "PartDesign_SubtractiveHelix"
<< "PartDesign_CompPrimitiveSubtractive"
<< "Separator"
<< "PartDesign_Mirrored"