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.svg icons/PartDesign_Additive_Loft.svg icons/PartDesign_Additive_Pipe.svg + icons/PartDesign_Additive_Helix.svg icons/PartDesign_Additive_Prism.svg icons/PartDesign_Additive_Sphere.svg icons/PartDesign_Additive_Torus.svg @@ -50,6 +51,7 @@ icons/PartDesign_Subtractive_Ellipsoid.svg icons/PartDesign_Subtractive_Loft.svg icons/PartDesign_Subtractive_Pipe.svg + icons/PartDesign_Subtractive_Helix.svg icons/PartDesign_Subtractive_Prism.svg icons/PartDesign_Subtractive_Sphere.svg icons/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 @@ + + + PartDesign_Additive_Helix + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + PartDesign_Additive_Helix + + + bitacovir, davidosterberg + + + PartDesign_Revolution + 2020/12/30 + http://www.freecadweb.org/wiki/index.php?title=Artwork + + + FreeCAD + + + + + + FreeCAD LGPL2+ + + + https://www.gnu.org/copyleft/lesser.html + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + PartDesign_Subtractive_Helix + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + PartDesign_Subtractive_Helix + + + bitacovir, davidosterberg + + + PartDesign_Revolution + 2020/12/30 + http://www.freecadweb.org/wiki/index.php?title=Artwork + + + FreeCAD + + + + + + FreeCAD LGPL2+ + + + https://www.gnu.org/copyleft/lesser.html + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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"