diff --git a/src/Mod/Fem/App/AppFem.cpp b/src/Mod/Fem/App/AppFem.cpp index a317d61334..8f8d024eb2 100755 --- a/src/Mod/Fem/App/AppFem.cpp +++ b/src/Mod/Fem/App/AppFem.cpp @@ -41,6 +41,11 @@ #include "FemSetNodesObject.h" #include "HypothesisPy.h" +#include "FemConstraintBearing.h" +#include "FemConstraintFixed.h" +#include "FemConstraintForce.h" +#include "FemConstraintGear.h" +#include "FemConstraintPulley.h" extern struct PyMethodDef Fem_methods[]; @@ -115,6 +120,12 @@ void AppFemExport initFem() Fem::FemSetGeometryObject ::init(); Fem::FemSetNodesObject ::init(); + Fem::Constraint ::init(); + Fem::ConstraintBearing ::init(); + Fem::ConstraintFixed ::init(); + Fem::ConstraintForce ::init(); + Fem::ConstraintGear ::init(); + Fem::ConstraintPulley ::init(); } } // extern "C" diff --git a/src/Mod/Fem/App/CMakeLists.txt b/src/Mod/Fem/App/CMakeLists.txt index 5dd69680bd..62dd1bf105 100755 --- a/src/Mod/Fem/App/CMakeLists.txt +++ b/src/Mod/Fem/App/CMakeLists.txt @@ -24,10 +24,10 @@ set(Fem_LIBS Mesh FreeCADApp ) - -if(SMESH_FOUND) - include_directories( ${SMESH_INCLUDE_DIR} ) - list( APPEND Fem_LIBS ${SMESH_LIBRARIES} ) + +if(SMESH_FOUND) + include_directories( ${SMESH_INCLUDE_DIR} ) + list( APPEND Fem_LIBS ${SMESH_LIBRARIES} ) endif(SMESH_FOUND) generate_from_xml(FemMeshPy) @@ -64,6 +64,18 @@ SET(Fem_SRCS FemMesh.h FemMeshProperty.cpp FemMeshProperty.h + FemConstraint.cpp + FemConstraint.h + FemConstraintBearing.h + FemConstraintBearing.cpp + FemConstraintFixed.cpp + FemConstraintFixed.h + FemConstraintForce.cpp + FemConstraintForce.h + FemConstraintGear.cpp + FemConstraintGear.h + FemConstraintPulley.cpp + FemConstraintPulley.h ${Mod_SRCS} ${Python_SRCS} ) diff --git a/src/Mod/Fem/App/FemConstraint.cpp b/src/Mod/Fem/App/FemConstraint.cpp new file mode 100644 index 0000000000..32c8bcd263 --- /dev/null +++ b/src/Mod/Fem/App/FemConstraint.cpp @@ -0,0 +1,329 @@ +/*************************************************************************** + * Copyright (c) 2013 Jan Rheinländer * + * * + * 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 +#endif + +#include "FemConstraint.h" + +#include +#include + +using namespace Fem; + +PROPERTY_SOURCE(Fem::Constraint, App::DocumentObject); + +Constraint::Constraint() +{ + ADD_PROPERTY_TYPE(References,(0,0),"Constraint",(App::PropertyType)(App::Prop_None),"Elements where the constraint is applied"); + ADD_PROPERTY_TYPE(NormalDirection,(Base::Vector3f(0,0,1)),"Constraint",App::PropertyType(App::Prop_ReadOnly|App::Prop_Output),"Normal direction pointing outside of solid"); +} + +Constraint::~Constraint() +{ +} + +App::DocumentObjectExecReturn *Constraint::execute(void) +{ + References.touch(); + return StdReturn; +} + +void Constraint::onChanged(const App::Property* prop) +{ + //Base::Console().Error("Constraint::onChanged() %s\n", prop->getName()); + if (prop == &References) { + // If References are changed, recalculate the normal direction. If no useful reference is found, + // use z axis or previous value. If several faces are selected, only the first one is used + std::vector Objects = References.getValues(); + std::vector SubElements = References.getSubValues(); + + // Extract geometry from References + TopoDS_Shape sh; + + for (int i = 0; i < Objects.size(); i++) { + App::DocumentObject* obj = Objects[i]; + Part::Feature* feat = static_cast(obj); + const Part::TopoShape& toposhape = feat->Shape.getShape(); + if (!toposhape._Shape.IsNull()) { + sh = toposhape.getSubShape(SubElements[i].c_str()); + + if (sh.ShapeType() == TopAbs_FACE) { + // Get face normal in center point + TopoDS_Face face = TopoDS::Face(sh); + BRepGProp_Face props(face); + gp_Vec normal; + gp_Pnt center; + double u1,u2,v1,v2; + props.Bounds(u1,u2,v1,v2); + props.Normal((u1+u2)/2.0,(v1+v2)/2.0,center,normal); + normal.Normalize(); + NormalDirection.setValue(normal.X(), normal.Y(), normal.Z()); + // One face is enough... + App::DocumentObject::onChanged(prop); + return; + } + } + } + } + + App::DocumentObject::onChanged(prop); +} + +void Constraint::onDocumentRestored() +{ + // This seems to be the only way to make the ViewProvider display the constraint + References.touch(); + App::DocumentObject::onDocumentRestored(); +} + +const bool Constraint::getPoints(std::vector &points, std::vector &normals) const +{ + std::vector Objects = References.getValues(); + std::vector SubElements = References.getSubValues(); + + // Extract geometry from References + TopoDS_Shape sh; + + for (int i = 0; i < Objects.size(); i++) { + App::DocumentObject* obj = Objects[i]; + Part::Feature* feat = static_cast(obj); + const Part::TopoShape& toposhape = feat->Shape.getShape(); + if (toposhape.isNull()) + return false; + sh = toposhape.getSubShape(SubElements[i].c_str()); + + if (sh.ShapeType() == TopAbs_VERTEX) { + const TopoDS_Vertex& vertex = TopoDS::Vertex(sh); + gp_Pnt p = BRep_Tool::Pnt(vertex); + points.push_back(Base::Vector3f(p.X(), p.Y(), p.Z())); + normals.push_back(NormalDirection.getValue()); + } else if (sh.ShapeType() == TopAbs_EDGE) { + BRepAdaptor_Curve curve(TopoDS::Edge(sh)); + double fp = curve.FirstParameter(); + double lp = curve.LastParameter(); + GProp_GProps props; + BRepGProp::LinearProperties(TopoDS::Edge(sh), props); + double l = props.Mass(); + // Create points with 10 units distance, but at least one at the beginning and end of the edge + int steps; + if (l >= 20) + steps = round(l / 10); + else + steps = 1; + double step = (lp - fp) / steps; + for (int i = 0; i < steps + 1; i++) { + gp_Pnt p = curve.Value(i * step); + points.push_back(Base::Vector3f(p.X(), p.Y(), p.Z())); + normals.push_back(NormalDirection.getValue()); + } + } else if (sh.ShapeType() == TopAbs_FACE) { + TopoDS_Face face = TopoDS::Face(sh); + // Surface boundaries + BRepAdaptor_Surface surface(face); + double ufp = surface.FirstUParameter(); + double ulp = surface.LastUParameter(); + double vfp = surface.FirstVParameter(); + double vlp = surface.LastVParameter(); + // Surface normals + BRepGProp_Face props(face); + gp_Vec normal; + gp_Pnt center; + // Get an estimate for the number of arrows by finding the average length of curves + Handle(Adaptor3d_HSurface) hsurf; + hsurf = new BRepAdaptor_HSurface(surface); + Adaptor3d_IsoCurve isoc(hsurf, GeomAbs_IsoU, vfp); + double l = GCPnts_AbscissaPoint::Length(isoc, Precision::Confusion()); + isoc.Load(GeomAbs_IsoU, vlp); + double lv = (l + GCPnts_AbscissaPoint::Length(isoc, Precision::Confusion()))/2.0; + isoc.Load(GeomAbs_IsoV, ufp); + l = GCPnts_AbscissaPoint::Length(isoc, Precision::Confusion()); + isoc.Load(GeomAbs_IsoV, ulp); + double lu = (l + GCPnts_AbscissaPoint::Length(isoc, Precision::Confusion()))/2.0; + int stepsv; + if (lv >= 20.0) + stepsv = round(lv / 10); + else + stepsv = 2; // Minimum of three arrows to ensure (as much as possible) that at least one is displayed + int stepsu; + if (lu >= 20.0) + stepsu = round(lu / 10); + else + stepsu = 2; + double stepv = (vlp - vfp) / stepsv; + double stepu = (ulp - ufp) / stepsu; + // Create points and normals + for (int i = 0; i < stepsv + 1; i++) { + for (int j = 0; j < stepsu + 1; j++) { + double v = vfp + i * stepv; + double u = ufp + j * stepu; + gp_Pnt p = surface.Value(u, v); + BRepClass_FaceClassifier classifier(face, p, Precision::Confusion()); + if (classifier.State() != TopAbs_OUT) { + points.push_back(Base::Vector3f(p.X(), p.Y(), p.Z())); + props.Normal(u, v,center,normal); + normal.Normalize(); + normals.push_back(Base::Vector3f(normal.X(), normal.Y(), normal.Z())); + } + } + } + } + } + + return true; +} + +const bool Constraint::getCylinder(float& radius, float& height, Base::Vector3f& base, Base::Vector3f& axis) const +{ + std::vector Objects = References.getValues(); + std::vector SubElements = References.getSubValues(); + if (Objects.empty()) + return false; + App::DocumentObject* obj = Objects[0]; + Part::Feature* feat = static_cast(obj); + Part::TopoShape toposhape = feat->Shape.getShape(); + if (toposhape.isNull()) + return false; + TopoDS_Shape sh = toposhape.getSubShape(SubElements[0].c_str()); + + TopoDS_Face face = TopoDS::Face(sh); + BRepAdaptor_Surface surface(face); + gp_Cylinder cyl = surface.Cylinder(); + gp_Pnt start = surface.Value(surface.FirstUParameter(), surface.FirstVParameter()); + gp_Pnt end = surface.Value(surface.FirstUParameter(), surface.LastVParameter()); + height = start.Distance(end); + radius = cyl.Radius(); + + gp_Pnt b = cyl.Location(); + base = Base::Vector3f(b.X(), b.Y(), b.Z()); + gp_Dir dir = cyl.Axis().Direction(); + axis = Base::Vector3f(dir.X(), dir.Y(), dir.Z()); + + return true; +} + +Base::Vector3f Constraint::getBasePoint(const Base::Vector3f& base, const Base::Vector3f& axis, + const App::PropertyLinkSub& location, const float& dist) +{ + // Get the point specified by Location and Distance + App::DocumentObject* objLoc = location.getValue(); + std::vector names = location.getSubValues(); + if (names.size() == 0) + return Base::Vector3f(0,0,0); + std::string subName = names.front(); + Part::Feature* featLoc = static_cast(objLoc); + TopoDS_Shape shloc = featLoc->Shape.getShape().getSubShape(subName.c_str()); + + // Get a plane from the Location reference + gp_Pln plane; + gp_Dir cylaxis(axis.x, axis.y, axis.z); + if (shloc.ShapeType() == TopAbs_FACE) { + BRepAdaptor_Surface surface(TopoDS::Face(shloc)); + plane = surface.Plane(); + } else { + BRepAdaptor_Curve curve(TopoDS::Edge(shloc)); + gp_Lin line = curve.Line(); + gp_Dir tang = line.Direction().Crossed(cylaxis); + gp_Dir norm = line.Direction().Crossed(tang); + plane = gp_Pln(line.Location(), norm); + } + + // Translate the plane in direction of the cylinder (for positive values of Distance) + Handle_Geom_Plane pln = new Geom_Plane(plane); + gp_Pnt cylbase(base.x, base.y, base.z); + GeomAPI_ProjectPointOnSurf proj(cylbase, pln); + if (!proj.IsDone()) + return Base::Vector3f(0,0,0); + + gp_Pnt projPnt = proj.NearestPoint(); + if ((fabs(dist) > Precision::Confusion()) && (projPnt.IsEqual(cylbase, Precision::Confusion()) == Standard_False)) + plane.Translate(gp_Vec(projPnt, cylbase).Normalized().Multiplied(dist)); + Handle_Geom_Plane plnt = new Geom_Plane(plane); + + // Intersect translated plane with cylinder axis + Handle_Geom_Curve crv = new Geom_Line(cylbase, cylaxis); + GeomAPI_IntCS intersector(crv, plnt); + if (!intersector.IsDone()) + return Base::Vector3f(0,0,0); + gp_Pnt inter = intersector.Point(1); + return Base::Vector3f(inter.X(), inter.Y(), inter.Z()); +} + +const Base::Vector3f Constraint::getDirection(const App::PropertyLinkSub &direction) +{ + App::DocumentObject* obj = direction.getValue(); + std::vector names = direction.getSubValues(); + if (names.size() == 0) + return Base::Vector3f(0,0,0); + std::string subName = names.front(); + Part::Feature* feat = static_cast(obj); + TopoDS_Shape sh = feat->Shape.getShape().getSubShape(subName.c_str()); + gp_Dir dir; + + if (sh.ShapeType() == TopAbs_FACE) { + BRepAdaptor_Surface surface(TopoDS::Face(sh)); + if (surface.GetType() == GeomAbs_Plane) { + dir = surface.Plane().Axis().Direction(); + } else { + return Base::Vector3f(0,0,0); // "Direction must be a planar face or linear edge" + } + } else if (sh.ShapeType() == TopAbs_EDGE) { + BRepAdaptor_Curve line(TopoDS::Edge(sh)); + if (line.GetType() == GeomAbs_Line) { + dir = line.Line().Direction(); + } else { + return Base::Vector3f(0,0,0); // "Direction must be a planar face or linear edge" + } + } + + Base::Vector3f the_direction(dir.X(), dir.Y(), dir.Z()); + the_direction.Normalize(); + return the_direction; +} diff --git a/src/Mod/Fem/App/FemConstraint.h b/src/Mod/Fem/App/FemConstraint.h new file mode 100644 index 0000000000..157d76285b --- /dev/null +++ b/src/Mod/Fem/App/FemConstraint.h @@ -0,0 +1,73 @@ +/*************************************************************************** + * Copyright (c) 2013 Jan Rheinländer * + * * + * 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 FEM_CONSTRAINT_H +#define FEM_CONSTRAINT_H + +#include +#include +#include +#include + +namespace Fem +{ + +class AppFemExport Constraint : public App::DocumentObject +{ + PROPERTY_HEADER(Fem::Constraint); + +public: + /// Constructor + Constraint(void); + virtual ~Constraint(); + + App::PropertyLinkSubList References; + // Read-only (calculated values). These trigger changes in the ViewProvider + App::PropertyVector NormalDirection; + + /// recalculate the object + virtual App::DocumentObjectExecReturn *execute(void); + + /// returns the type name of the ViewProvider + virtual const char* getViewProviderName(void) const { + return "FemGui::ViewProviderFemConstraint"; + } + +protected: + virtual void onChanged(const App::Property* prop); + virtual void onDocumentRestored(); + +protected: + /// Calculate the points where symbols should be drawn + const bool getPoints(std::vector& points, std::vector& normals) const; + const bool getCylinder(float& radius, float& height, Base::Vector3f& base, Base::Vector3f& axis) const; + Base::Vector3f getBasePoint(const Base::Vector3f& base, const Base::Vector3f& axis, + const App::PropertyLinkSub &location, const float& dist); + const Base::Vector3f getDirection(const App::PropertyLinkSub &direction); + +}; + +} //namespace Fem + + +#endif // FEM_CONSTRAINT_H diff --git a/src/Mod/Fem/App/FemConstraintBearing.cpp b/src/Mod/Fem/App/FemConstraintBearing.cpp new file mode 100644 index 0000000000..ed399bd026 --- /dev/null +++ b/src/Mod/Fem/App/FemConstraintBearing.cpp @@ -0,0 +1,117 @@ +/*************************************************************************** + * Copyright (c) 2013 Jan Rheinländer * + * * + * 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 +#endif + +#include "FemConstraintBearing.h" + +#include +#include + +using namespace Fem; + +PROPERTY_SOURCE(Fem::ConstraintBearing, Fem::Constraint); + +ConstraintBearing::ConstraintBearing() +{ + ADD_PROPERTY_TYPE(Location,(0),"ConstraintBearing",(App::PropertyType)(App::Prop_None), + "Element giving axial location of constraint"); + ADD_PROPERTY(Dist,(0.0)); + ADD_PROPERTY(AxialFree,(0)); + ADD_PROPERTY(Radius,(0.0)); + ADD_PROPERTY(Height,(0.0)); + ADD_PROPERTY_TYPE(BasePoint,(Base::Vector3f(0,0,0)),"ConstraintBearing",App::PropertyType(App::Prop_ReadOnly|App::Prop_Output), + "Base point of cylindrical bearing seat"); + ADD_PROPERTY_TYPE(Axis,(Base::Vector3f(0,1,0)),"ConstraintBearing",App::PropertyType(App::Prop_ReadOnly|App::Prop_Output), + "Axis of bearing seat"); +} + +App::DocumentObjectExecReturn *ConstraintBearing::execute(void) +{ + return Constraint::execute(); +} + +void ConstraintBearing::onChanged(const App::Property* prop) +{ + //Base::Console().Error("ConstraintBearing: onChanged %s\n", prop->getName()); + // Note: If we call this at the end, then the symbol ist not oriented correctly initially + // because the NormalDirection has not been calculated yet + Constraint::onChanged(prop); + + if (prop == &References) { + // Find data of cylinder + float radius, height; + Base::Vector3f base, axis; + if (!getCylinder(radius, height, base, axis)) + return; + Radius.setValue(radius); + Axis.setValue(axis); + Height.setValue(height); + // Update base point + base = base + axis * height/2; + if (Location.getValue() != NULL) { + base = getBasePoint(base, axis, Location, Dist.getValue()); + } + BasePoint.setValue(base); + BasePoint.touch(); // This triggers ViewProvider::updateData() + } else if ((prop == &Location) || (prop == &Dist)) { + App::DocumentObject* obj = Location.getValue(); + std::vector names = Location.getSubValues(); + if (names.size() == 0) { + return; + } + std::string subName = names.front(); + Part::Feature* feat = static_cast(obj); + TopoDS_Shape sh = feat->Shape.getShape().getSubShape(subName.c_str()); + + if (sh.ShapeType() == TopAbs_FACE) { + BRepAdaptor_Surface surface(TopoDS::Face(sh)); + if (surface.GetType() != GeomAbs_Plane) { + return; // "Location must be a planar face or linear edge" + } + } else if (sh.ShapeType() == TopAbs_EDGE) { + BRepAdaptor_Curve line(TopoDS::Edge(sh)); + if (line.GetType() != GeomAbs_Line) { + return; // "Location must be a planar face or linear edge" + } + } + + float radius, height; + Base::Vector3f base, axis; + if (!getCylinder(radius, height, base, axis)) + return; + base = getBasePoint(base + axis * height/2, axis, Location, Dist.getValue()); + BasePoint.setValue(base); + BasePoint.touch(); + } +} diff --git a/src/Mod/Fem/App/FemConstraintBearing.h b/src/Mod/Fem/App/FemConstraintBearing.h new file mode 100644 index 0000000000..66fcd0e070 --- /dev/null +++ b/src/Mod/Fem/App/FemConstraintBearing.h @@ -0,0 +1,71 @@ +/*************************************************************************** + * Copyright (c) 2013 Jan Rheinländer * + * * + * 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 FEM_CONSTRAINTBEARING_H +#define FEM_CONSTRAINTBEARING_H + +#include +#include +#include + +#include "FemConstraint.h" + +namespace Fem +{ + +class AppFemExport ConstraintBearing : public Fem::Constraint +{ + PROPERTY_HEADER(Fem::ConstraintBearing); + +public: + /// Constructor + ConstraintBearing(void); + + /// Location reference + App::PropertyLinkSub Location; + /// Distance from location reference + App::PropertyFloat Dist; + /// Is the bearing free to move in axial direction? + App::PropertyBool AxialFree; + // Read-only (calculated values). These trigger changes in the ViewProvider + App::PropertyFloat Radius; + App::PropertyFloat Height; + App::PropertyVector BasePoint; + App::PropertyVector Axis; + + /// recalculate the object + virtual App::DocumentObjectExecReturn *execute(void); + + /// returns the type name of the ViewProvider + const char* getViewProviderName(void) const { + return "FemGui::ViewProviderFemConstraintBearing"; + } + +protected: + virtual void onChanged(const App::Property* prop); +}; + +} //namespace Fem + + +#endif // FEM_CONSTRAINTBEARING_H diff --git a/src/Mod/Fem/App/FemConstraintFixed.cpp b/src/Mod/Fem/App/FemConstraintFixed.cpp new file mode 100644 index 0000000000..762b63d891 --- /dev/null +++ b/src/Mod/Fem/App/FemConstraintFixed.cpp @@ -0,0 +1,84 @@ +/*************************************************************************** + * Copyright (c) 2013 Jan Rheinländer * + * * + * 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 +#endif + +#include "FemConstraintFixed.h" + +#include +#include + +using namespace Fem; + +PROPERTY_SOURCE(Fem::ConstraintFixed, Fem::Constraint); + +ConstraintFixed::ConstraintFixed() +{ + ADD_PROPERTY_TYPE(Points,(Base::Vector3f()),"ConstraintFixed",App::PropertyType(App::Prop_ReadOnly|App::Prop_Output), + "Points where symbols are drawn"); + ADD_PROPERTY_TYPE(Normals,(Base::Vector3f()),"ConstraintFixed",App::PropertyType(App::Prop_ReadOnly|App::Prop_Output), + "Normals where symbols are drawn"); + Points.setValues(std::vector()); + Normals.setValues(std::vector()); +} + +App::DocumentObjectExecReturn *ConstraintFixed::execute(void) +{ + return Constraint::execute(); +} + +void ConstraintFixed::onChanged(const App::Property* prop) +{ + // Note: If we call this at the end, then the symbols are not oriented correctly initially + // because the NormalDirection has not been calculated yet + Constraint::onChanged(prop); + + if (prop == &References) { + std::vector points; + std::vector normals; + if (getPoints(points, normals)) { + Points.setValues(points); + Normals.setValues(normals); + Points.touch(); // This triggers ViewProvider::updateData() + } + } +} diff --git a/src/Mod/Fem/App/FemConstraintFixed.h b/src/Mod/Fem/App/FemConstraintFixed.h new file mode 100644 index 0000000000..0294fbdaa3 --- /dev/null +++ b/src/Mod/Fem/App/FemConstraintFixed.h @@ -0,0 +1,63 @@ +/*************************************************************************** + * Copyright (c) 2013 Jan Rheinländer * + * * + * 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 FEM_CONSTRAINTFIXED_H +#define FEM_CONSTRAINTFIXED_H + +#include +#include +#include + +#include "FemConstraint.h" + +namespace Fem +{ + +class AppFemExport ConstraintFixed : public Fem::Constraint +{ + PROPERTY_HEADER(Fem::ConstraintFixed); + +public: + /// Constructor + ConstraintFixed(void); + + // Read-only (calculated values). These trigger changes in the ViewProvider + App::PropertyVectorList Points; + App::PropertyVectorList Normals; + + /// recalculate the object + virtual App::DocumentObjectExecReturn *execute(void); + + /// returns the type name of the ViewProvider + const char* getViewProviderName(void) const { + return "FemGui::ViewProviderFemConstraintFixed"; + } + +protected: + virtual void onChanged(const App::Property* prop); +}; + +} //namespace Fem + + +#endif // FEM_CONSTRAINTFIXED_H diff --git a/src/Mod/Fem/App/FemConstraintForce.cpp b/src/Mod/Fem/App/FemConstraintForce.cpp new file mode 100644 index 0000000000..771929d23a --- /dev/null +++ b/src/Mod/Fem/App/FemConstraintForce.cpp @@ -0,0 +1,101 @@ +/*************************************************************************** + * Copyright (c) 2013 Jan Rheinländer * + * * + * 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 +#endif + +#include "FemConstraintForce.h" + +#include +#include + +using namespace Fem; + +PROPERTY_SOURCE(Fem::ConstraintForce, Fem::Constraint); + +ConstraintForce::ConstraintForce() +{ + ADD_PROPERTY(Force,(0.0)); + ADD_PROPERTY_TYPE(Direction,(0),"ConstraintForce",(App::PropertyType)(App::Prop_None), + "Element giving direction of constraint"); + ADD_PROPERTY(Reversed,(0)); + ADD_PROPERTY_TYPE(Points,(Base::Vector3f()),"ConstraintForce",App::PropertyType(App::Prop_ReadOnly|App::Prop_Output), + "Points where arrows are drawn"); + ADD_PROPERTY_TYPE(DirectionVector,(Base::Vector3f(0,0,1)),"ConstraintForce",App::PropertyType(App::Prop_ReadOnly|App::Prop_Output), + "Direction of arrows"); + naturalDirectionVector = Base::Vector3f(0,0,1); + Points.setValues(std::vector()); +} + +App::DocumentObjectExecReturn *ConstraintForce::execute(void) +{ + return Constraint::execute(); +} + +void ConstraintForce::onChanged(const App::Property* prop) +{ + // Note: If we call this at the end, then the arrows are not oriented correctly initially + // because the NormalDirection has not been calculated yet + Constraint::onChanged(prop); + + if (prop == &References) { + std::vector points; + std::vector normals; + if (getPoints(points, normals)) { + Points.setValues(points); // We don't use the normals because all arrows should have the same direction + Points.touch(); // This triggers ViewProvider::updateData() + } + } else if (prop == &Direction) { + Base::Vector3f direction = getDirection(Direction); + if (direction.Length() < Precision::Confusion()) + return; + naturalDirectionVector = direction; + if (Reversed.getValue()) + direction = -direction; + DirectionVector.setValue(direction); + DirectionVector.touch(); + } else if (prop == &Reversed) { + if (Reversed.getValue() && (DirectionVector.getValue() == naturalDirectionVector)) { + DirectionVector.setValue(-naturalDirectionVector); + DirectionVector.touch(); + } else if (!Reversed.getValue() && (DirectionVector.getValue() != naturalDirectionVector)) { + DirectionVector.setValue(naturalDirectionVector); + DirectionVector.touch(); + } + } else if (prop == &NormalDirection) { + // Set a default direction if no direction reference has been given + if (Direction.getValue() == NULL) { + DirectionVector.setValue(NormalDirection.getValue()); + naturalDirectionVector = NormalDirection.getValue(); + } + } +} diff --git a/src/Mod/Fem/App/FemConstraintForce.h b/src/Mod/Fem/App/FemConstraintForce.h new file mode 100644 index 0000000000..1386e7d7d0 --- /dev/null +++ b/src/Mod/Fem/App/FemConstraintForce.h @@ -0,0 +1,69 @@ +/*************************************************************************** + * Copyright (c) 2013 Jan Rheinländer * + * * + * 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 FEM_CONSTRAINTFORCE_H +#define FEM_CONSTRAINTFORCE_H + +#include +#include +#include + +#include "FemConstraint.h" + +namespace Fem +{ + +class AppFemExport ConstraintForce : public Fem::Constraint +{ + PROPERTY_HEADER(Fem::ConstraintForce); + +public: + /// Constructor + ConstraintForce(void); + + App::PropertyFloat Force; + App::PropertyLinkSub Direction; + App::PropertyBool Reversed; + // Read-only (calculated values). These trigger changes in the ViewProvider + App::PropertyVectorList Points; + App::PropertyVector DirectionVector; + + /// recalculate the object + virtual App::DocumentObjectExecReturn *execute(void); + + /// returns the type name of the ViewProvider + const char* getViewProviderName(void) const { + return "FemGui::ViewProviderFemConstraintForce"; + } + +protected: + virtual void onChanged(const App::Property* prop); + +private: + Base::Vector3f naturalDirectionVector; +}; + +} //namespace Fem + + +#endif // FEM_CONSTRAINTFORCE_H diff --git a/src/Mod/Fem/App/FemConstraintGear.cpp b/src/Mod/Fem/App/FemConstraintGear.cpp new file mode 100644 index 0000000000..ec62a294cc --- /dev/null +++ b/src/Mod/Fem/App/FemConstraintGear.cpp @@ -0,0 +1,86 @@ +/*************************************************************************** + * Copyright (c) 2013 Jan Rheinländer * + * * + * 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 +#endif + +#include "FemConstraintGear.h" + +#include +#include + +using namespace Fem; + +PROPERTY_SOURCE(Fem::ConstraintGear, Fem::ConstraintBearing); + +ConstraintGear::ConstraintGear() +{ + ADD_PROPERTY(Diameter,(100.0)); + ADD_PROPERTY(Force,(1000.0)); + ADD_PROPERTY(ForceAngle,(0.0)); + ADD_PROPERTY_TYPE(Direction,(0),"ConstraintGear",(App::PropertyType)(App::Prop_None), + "Element giving direction of gear force"); + ADD_PROPERTY(Reversed,(0)); + ADD_PROPERTY_TYPE(DirectionVector,(Base::Vector3f(1,1,1).Normalize()),"ConstraintGear",App::PropertyType(App::Prop_ReadOnly|App::Prop_Output), + "Direction of gear force"); + naturalDirectionVector = Base::Vector3f(1,1,1).Normalize(); +} + +App::DocumentObjectExecReturn *ConstraintGear::execute(void) +{ + return ConstraintBearing::execute(); +} + +void ConstraintGear::onChanged(const App::Property* prop) +{ + ConstraintBearing::onChanged(prop); + + if (prop == &Direction) { + Base::Vector3f direction = getDirection(Direction); + if (direction.Length() < Precision::Confusion()) + return; + naturalDirectionVector = direction; + if (Reversed.getValue()) + direction = -direction; + DirectionVector.setValue(direction); + DirectionVector.touch(); + } else if (prop == &Reversed) { + if (Reversed.getValue() && (DirectionVector.getValue() == naturalDirectionVector)) { + DirectionVector.setValue(-naturalDirectionVector); + DirectionVector.touch(); + } else if (!Reversed.getValue() && (DirectionVector.getValue() != naturalDirectionVector)) { + DirectionVector.setValue(naturalDirectionVector); + DirectionVector.touch(); + } + } + // The computation for the force angle is simpler in the ViewProvider directly +} diff --git a/src/Mod/Fem/App/FemConstraintGear.h b/src/Mod/Fem/App/FemConstraintGear.h new file mode 100644 index 0000000000..11926f3504 --- /dev/null +++ b/src/Mod/Fem/App/FemConstraintGear.h @@ -0,0 +1,70 @@ +/*************************************************************************** + * Copyright (c) 2013 Jan Rheinländer * + * * + * 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 FEM_CONSTRAINTGear_H +#define FEM_CONSTRAINTGear_H + +#include +#include +#include + +#include "FemConstraintBearing.h" + +namespace Fem +{ + +class AppFemExport ConstraintGear : public Fem::ConstraintBearing +{ + PROPERTY_HEADER(Fem::ConstraintGear); + +public: + /// Constructor + ConstraintGear(void); + + App::PropertyFloat Diameter; + App::PropertyFloat Force; + App::PropertyFloat ForceAngle; + App::PropertyLinkSub Direction; + App::PropertyBool Reversed; + // Read-only (calculated values). These trigger changes in the ViewProvider + App::PropertyVector DirectionVector; + + /// recalculate the object + virtual App::DocumentObjectExecReturn *execute(void); + + /// returns the type name of the ViewProvider + const char* getViewProviderName(void) const { + return "FemGui::ViewProviderFemConstraintGear"; + } + +protected: + virtual void onChanged(const App::Property* prop); + +private: + Base::Vector3f naturalDirectionVector; +}; + +} //namespace Fem + + +#endif // FEM_CONSTRAINTGear_H diff --git a/src/Mod/Fem/App/FemConstraintPulley.cpp b/src/Mod/Fem/App/FemConstraintPulley.cpp new file mode 100644 index 0000000000..4c933b4d9f --- /dev/null +++ b/src/Mod/Fem/App/FemConstraintPulley.cpp @@ -0,0 +1,98 @@ +/*************************************************************************** + * Copyright (c) 2013 Jan Rheinländer * + * * + * 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 +#endif + +#include "FemConstraintPulley.h" + +#include +#include + +using namespace Fem; + +PROPERTY_SOURCE(Fem::ConstraintPulley, Fem::ConstraintGear); + +ConstraintPulley::ConstraintPulley() +{ + ADD_PROPERTY(OtherDiameter,(100.0)); + ADD_PROPERTY(CenterDistance,(500.0)); + ADD_PROPERTY(IsDriven,(0)); + ADD_PROPERTY(TensionForce,(0.0)); + + ADD_PROPERTY_TYPE(BeltAngle,(0),"ConstraintPulley",App::PropertyType(App::Prop_ReadOnly|App::Prop_Output), + "Angle of belt forces"); + ADD_PROPERTY_TYPE(BeltForce1,(0.0),"ConstraintPulley",App::PropertyType(App::Prop_ReadOnly|App::Prop_Output), + "First belt force"); + ADD_PROPERTY_TYPE(BeltForce2,(0.0),"ConstraintPulley",App::PropertyType(App::Prop_ReadOnly|App::Prop_Output), + "Second belt force"); + ForceAngle.setValue(00.0); + Diameter.setValue(300.0); + // calculate initial values of read-only properties + onChanged(&Force); +} + +App::DocumentObjectExecReturn *ConstraintPulley::execute(void) +{ + return ConstraintBearing::execute(); +} + +void ConstraintPulley::onChanged(const App::Property* prop) +{ + ConstraintGear::onChanged(prop); + + if ((prop == &Diameter) || (prop == &OtherDiameter) || (prop == &CenterDistance)) { + if (CenterDistance.getValue() > Precision::Confusion()) { + BeltAngle.setValue(asin((Diameter.getValue() - OtherDiameter.getValue())/2/CenterDistance.getValue())); + BeltAngle.touch(); + } + } else if ((prop == &Force) || (prop == &TensionForce) || (prop == &IsDriven)) { + double radius = Diameter.getValue() / 2.0; + if (radius < Precision::Confusion()) + return; + double force = Force.getValue() / (radius/1000); + if (fabs(force) < Precision::Confusion()) + return; + bool neg = (force < 0.0); + if (neg) + force *= -1.0; + + if ((IsDriven.getValue() && neg) || (!IsDriven.getValue() && !neg)) { + BeltForce1.setValue(force + TensionForce.getValue()); + BeltForce2.setValue(TensionForce.getValue()); + } else { + BeltForce2.setValue(force + TensionForce.getValue()); + BeltForce1.setValue(TensionForce.getValue()); + } + BeltForce1.touch(); + } +} diff --git a/src/Mod/Fem/App/FemConstraintPulley.h b/src/Mod/Fem/App/FemConstraintPulley.h new file mode 100644 index 0000000000..611652570a --- /dev/null +++ b/src/Mod/Fem/App/FemConstraintPulley.h @@ -0,0 +1,73 @@ +/*************************************************************************** + * Copyright (c) 2013 Jan Rheinländer * + * * + * 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 FEM_CONSTRAINTPulley_H +#define FEM_CONSTRAINTPulley_H + +#include +#include +#include + +#include "FemConstraintGear.h" + +namespace Fem +{ + +class AppFemExport ConstraintPulley : public Fem::ConstraintGear +{ + PROPERTY_HEADER(Fem::ConstraintPulley); + +public: + /// Constructor + ConstraintPulley(void); + + /// Other pulley diameter + App::PropertyFloat OtherDiameter; + /// Center distance between the pulleys + App::PropertyFloat CenterDistance; + /// Driven pulley or driving pulley? + App::PropertyBool IsDriven; + /// Belt tension force + App::PropertyFloat TensionForce; + // Read-only (calculated values). These trigger changes in the ViewProvider + App::PropertyFloat BeltAngle; + App::PropertyFloat BeltForce1; + App::PropertyFloat BeltForce2; + + /// recalculate the object + virtual App::DocumentObjectExecReturn *execute(void); + + /// returns the type name of the ViewProvider + const char* getViewProviderName(void) const { + return "FemGui::ViewProviderFemConstraintPulley"; + } + +protected: + virtual void onChanged(const App::Property* prop); + +}; + +} //namespace Fem + + +#endif // FEM_CONSTRAINTPulley_H diff --git a/src/Mod/Fem/Gui/AppFemGui.cpp b/src/Mod/Fem/Gui/AppFemGui.cpp index d36865d91f..f591b18a7d 100755 --- a/src/Mod/Fem/Gui/AppFemGui.cpp +++ b/src/Mod/Fem/Gui/AppFemGui.cpp @@ -34,6 +34,12 @@ #include "ViewProviderSetElements.h" #include "ViewProviderSetFaces.h" #include "ViewProviderSetGeometry.h" +#include "ViewProviderFemConstraint.h" +#include "ViewProviderFemConstraintBearing.h" +#include "ViewProviderFemConstraintFixed.h" +#include "ViewProviderFemConstraintForce.h" +#include "ViewProviderFemConstraintGear.h" +#include "ViewProviderFemConstraintPulley.h" #include "Workbench.h" //#include "resources/qrc_Fem.cpp" @@ -63,16 +69,22 @@ void FemGuiExport initFemGui() (void) Py_InitModule("FemGui", FemGui_Import_methods); /* mod name, table ptr */ Base::Console().Log("Loading GUI of Fem module... done\n"); - // instanciating the commands + // instantiating the commands CreateFemCommands(); // addition objects FemGui::Workbench ::init(); - FemGui::ViewProviderFemMesh ::init(); - FemGui::ViewProviderSetNodes ::init(); - FemGui::ViewProviderSetElements ::init(); - FemGui::ViewProviderSetFaces ::init(); - FemGui::ViewProviderSetGeometry ::init(); + FemGui::ViewProviderFemMesh ::init(); + FemGui::ViewProviderSetNodes ::init(); + FemGui::ViewProviderSetElements ::init(); + FemGui::ViewProviderSetFaces ::init(); + FemGui::ViewProviderSetGeometry ::init(); + FemGui::ViewProviderFemConstraint ::init(); + FemGui::ViewProviderFemConstraintBearing ::init(); + FemGui::ViewProviderFemConstraintFixed ::init(); + FemGui::ViewProviderFemConstraintForce ::init(); + FemGui::ViewProviderFemConstraintGear ::init(); + FemGui::ViewProviderFemConstraintPulley ::init(); // add resources and reloads the translators loadFemResource(); diff --git a/src/Mod/Fem/Gui/CMakeLists.txt b/src/Mod/Fem/Gui/CMakeLists.txt index 6a36cfbec2..3c9c080bc2 100755 --- a/src/Mod/Fem/Gui/CMakeLists.txt +++ b/src/Mod/Fem/Gui/CMakeLists.txt @@ -23,10 +23,10 @@ set(FemGui_LIBS Fem FreeCADGui ) - -if(SMESH_FOUND) - include_directories( ${SMESH_INCLUDE_DIR} ) - list( APPEND FemGui_LIBS ${SMESH_LIBRARIES} ) + +if(SMESH_FOUND) + include_directories( ${SMESH_INCLUDE_DIR} ) + list( APPEND FemGui_LIBS ${SMESH_LIBRARIES} ) endif(SMESH_FOUND) set(FemGui_MOC_HDRS @@ -34,6 +34,12 @@ set(FemGui_MOC_HDRS TaskObjectName.h TaskCreateNodeSet.h TaskDlgCreateNodeSet.h + TaskFemConstraint.h + TaskFemConstraintBearing.h + TaskFemConstraintFixed.h + TaskFemConstraintForce.h + TaskFemConstraintGear.h + TaskFemConstraintPulley.h ) fc_wrap_cpp(FemGui_MOC_SRCS ${FemGui_MOC_HDRS}) SOURCE_GROUP("Moc" FILES ${FemGui_MOC_SRCS}) @@ -42,9 +48,37 @@ set(FemGui_UIC_SRCS Hypothesis.ui TaskCreateNodeSet.ui TaskObjectName.ui + TaskFemConstraint.ui + TaskFemConstraintBearing.ui + TaskFemConstraintFixed.ui + TaskFemConstraintForce.ui ) qt4_wrap_ui(FemGui_UIC_HDRS ${FemGui_UIC_SRCS}) +SET(FemGui_DLG_SRCS + ${FemGui_UIC_HDRS} + Hypothesis.ui + Hypothesis.cpp + Hypothesis.h + TaskFemConstraint.ui + TaskFemConstraint.cpp + TaskFemConstraint.h + TaskFemConstraintBearing.ui + TaskFemConstraintBearing.cpp + TaskFemConstraintBearing.h + TaskFemConstraintFixed.ui + TaskFemConstraintFixed.cpp + TaskFemConstraintFixed.h + TaskFemConstraintForce.ui + TaskFemConstraintForce.cpp + TaskFemConstraintForce.h + TaskFemConstraintGear.cpp + TaskFemConstraintGear.h + TaskFemConstraintPulley.cpp + TaskFemConstraintPulley.h +) +SOURCE_GROUP("Dialogs" FILES ${FemGui_DLG_SRCS}) + qt4_add_resources(FemResource_SRCS Resources/Fem.qrc) SOURCE_GROUP("Resources" FILES ${FemResource_SRCS}) @@ -62,14 +96,26 @@ SET(FemGui_SRCS_ViewProvider ViewProviderSetGeometry.h FemSelectionGate.cpp FemSelectionGate.h + ViewProviderFemConstraint.cpp + ViewProviderFemConstraint.h + ViewProviderFemConstraintBearing.cpp + ViewProviderFemConstraintBearing.h + ViewProviderFemConstraintFixed.cpp + ViewProviderFemConstraintFixed.h + ViewProviderFemConstraintForce.cpp + ViewProviderFemConstraintForce.h + ViewProviderFemConstraintGear.cpp + ViewProviderFemConstraintGear.h + ViewProviderFemConstraintPulley.cpp + ViewProviderFemConstraintPulley.h ) SOURCE_GROUP("ViewProvider" FILES ${FemGui_SRCS_ViewProvider}) SET(FemGui_SRCS_TaskBoxes - TaskObjectName.ui + TaskObjectName.ui TaskObjectName.cpp TaskObjectName.h - TaskCreateNodeSet.ui + TaskCreateNodeSet.ui TaskCreateNodeSet.cpp TaskCreateNodeSet.h ) @@ -81,6 +127,9 @@ SET(FemGui_SRCS_TaskDlg Hypothesis.ui Hypothesis.cpp Hypothesis.h + TaskFemConstraint.ui + TaskFemConstraint.cpp + TaskFemConstraint.h ) SOURCE_GROUP("Task_Dialogs" FILES ${FemGui_SRCS_TaskDlg}) @@ -104,6 +153,18 @@ SET(FemGui_SRCS ${FemGui_SRCS_TaskDlg} ${FemGui_SRCS_TaskBoxes} ${FemGui_SRCS_Module} + ViewProviderFemConstraint.cpp + ViewProviderFemConstraint.h + ViewProviderFemConstraintBearing.cpp + ViewProviderFemConstraintBearing.h + ViewProviderFemConstraintFixed.cpp + ViewProviderFemConstraintFixed.h + ViewProviderFemConstraintForce.cpp + ViewProviderFemConstraintForce.h + ViewProviderFemConstraintGear.cpp + ViewProviderFemConstraintGear.h + ViewProviderFemConstraintPulley.cpp + ViewProviderFemConstraintPulley.h ) @@ -111,11 +172,11 @@ SET(FemGui_SRCS add_library(FemGui SHARED ${FemGui_SRCS}) target_link_libraries(FemGui ${FemGui_LIBS}) - -fc_target_copy_resource(FemGui - ${CMAKE_SOURCE_DIR}/src/Mod/Fem - ${CMAKE_BINARY_DIR}/Mod/Fem - InitGui.py) + +fc_target_copy_resource(FemGui + ${CMAKE_SOURCE_DIR}/src/Mod/Fem + ${CMAKE_BINARY_DIR}/Mod/Fem + InitGui.py) if(MSVC) set_target_properties(FemGui PROPERTIES SUFFIX ".pyd") diff --git a/src/Mod/Fem/Gui/Command.cpp b/src/Mod/Fem/Gui/Command.cpp index 655879f22d..490e53f844 100755 --- a/src/Mod/Fem/Gui/Command.cpp +++ b/src/Mod/Fem/Gui/Command.cpp @@ -52,6 +52,7 @@ #include #include #include +#include #include "Hypothesis.h" @@ -85,6 +86,162 @@ bool CmdFemCreateFromShape::isActive(void) return Gui::Selection().countObjectsOfType(type) > 0; } +DEF_STD_CMD_A(CmdFemConstraintBearing); + +CmdFemConstraintBearing::CmdFemConstraintBearing() + : Command("Fem_ConstraintBearing") +{ + sAppModule = "Fem"; + sGroup = QT_TR_NOOP("Fem"); + sMenuText = QT_TR_NOOP("Create FEM bearing constraint"); + sToolTipText = QT_TR_NOOP("Create FEM constraint for a bearing"); + sWhatsThis = sToolTipText; + sStatusTip = sToolTipText; + sPixmap = "Fem_ConstraintBearing"; +} + +void CmdFemConstraintBearing::activated(int iMsg) +{ + std::string FeatName = getUniqueObjectName("FemConstraintBearing"); + + openCommand("Make FEM constraint for bearing"); + doCommand(Doc,"App.activeDocument().addObject(\"Fem::ConstraintBearing\",\"%s\")",FeatName.c_str()); + updateActive(); + + doCommand(Gui,"Gui.activeDocument().setEdit('%s')",FeatName.c_str()); +} + +bool CmdFemConstraintBearing::isActive(void) +{ + return hasActiveDocument(); +} + +DEF_STD_CMD_A(CmdFemConstraintFixed); + +CmdFemConstraintFixed::CmdFemConstraintFixed() + : Command("Fem_ConstraintFixed") +{ + sAppModule = "Fem"; + sGroup = QT_TR_NOOP("Fem"); + sMenuText = QT_TR_NOOP("Create FEM fixed constraint"); + sToolTipText = QT_TR_NOOP("Create FEM constraint for a fixed geometric entity"); + sWhatsThis = sToolTipText; + sStatusTip = sToolTipText; + sPixmap = "Fem_ConstraintFixed"; +} + +void CmdFemConstraintFixed::activated(int iMsg) +{ + std::string FeatName = getUniqueObjectName("FemConstraintFixed"); + + openCommand("Make FEM constraint fixed geometry"); + doCommand(Doc,"App.activeDocument().addObject(\"Fem::ConstraintFixed\",\"%s\")",FeatName.c_str()); + updateActive(); + + doCommand(Gui,"Gui.activeDocument().setEdit('%s')",FeatName.c_str()); +} + +bool CmdFemConstraintFixed::isActive(void) +{ + return hasActiveDocument(); +} + +DEF_STD_CMD_A(CmdFemConstraintForce); + +CmdFemConstraintForce::CmdFemConstraintForce() + : Command("Fem_ConstraintForce") +{ + sAppModule = "Fem"; + sGroup = QT_TR_NOOP("Fem"); + sMenuText = QT_TR_NOOP("Create FEM force constraint"); + sToolTipText = QT_TR_NOOP("Create FEM constraint for a force acting on a geometric entity"); + sWhatsThis = sToolTipText; + sStatusTip = sToolTipText; + sPixmap = "Fem_ConstraintForce"; +} + +void CmdFemConstraintForce::activated(int iMsg) +{ + std::string FeatName = getUniqueObjectName("FemConstraintForce"); + + openCommand("Make FEM constraint force on geometry"); + doCommand(Doc,"App.activeDocument().addObject(\"Fem::ConstraintForce\",\"%s\")",FeatName.c_str()); + doCommand(Doc,"App.activeDocument().%s.Force = 0.0",FeatName.c_str()); + updateActive(); + + doCommand(Gui,"Gui.activeDocument().setEdit('%s')",FeatName.c_str()); +} + +bool CmdFemConstraintForce::isActive(void) +{ + return hasActiveDocument(); +} + +DEF_STD_CMD_A(CmdFemConstraintGear); + +CmdFemConstraintGear::CmdFemConstraintGear() + : Command("Fem_ConstraintGear") +{ + sAppModule = "Fem"; + sGroup = QT_TR_NOOP("Fem"); + sMenuText = QT_TR_NOOP("Create FEM gear constraint"); + sToolTipText = QT_TR_NOOP("Create FEM constraint for a gear"); + sWhatsThis = sToolTipText; + sStatusTip = sToolTipText; + sPixmap = "Fem_ConstraintGear"; +} + +void CmdFemConstraintGear::activated(int iMsg) +{ + std::string FeatName = getUniqueObjectName("FemConstraintGear"); + + openCommand("Make FEM constraint for gear"); + doCommand(Doc,"App.activeDocument().addObject(\"Fem::ConstraintGear\",\"%s\")",FeatName.c_str()); + doCommand(Doc,"App.activeDocument().%s.Diameter = 100.0",FeatName.c_str()); + updateActive(); + + doCommand(Gui,"Gui.activeDocument().setEdit('%s')",FeatName.c_str()); +} + +bool CmdFemConstraintGear::isActive(void) +{ + return hasActiveDocument(); +} + +DEF_STD_CMD_A(CmdFemConstraintPulley); + +CmdFemConstraintPulley::CmdFemConstraintPulley() + : Command("Fem_ConstraintPulley") +{ + sAppModule = "Fem"; + sGroup = QT_TR_NOOP("Fem"); + sMenuText = QT_TR_NOOP("Create FEM pulley constraint"); + sToolTipText = QT_TR_NOOP("Create FEM constraint for a pulley"); + sWhatsThis = sToolTipText; + sStatusTip = sToolTipText; + sPixmap = "Fem_ConstraintPulley"; +} + +void CmdFemConstraintPulley::activated(int iMsg) +{ + std::string FeatName = getUniqueObjectName("FemConstraintPulley"); + + openCommand("Make FEM constraint for pulley"); + doCommand(Doc,"App.activeDocument().addObject(\"Fem::ConstraintPulley\",\"%s\")",FeatName.c_str()); + doCommand(Doc,"App.activeDocument().%s.Diameter = 300.0",FeatName.c_str()); + doCommand(Doc,"App.activeDocument().%s.OtherDiameter = 100.0",FeatName.c_str()); + doCommand(Doc,"App.activeDocument().%s.CenterDistance = 500.0",FeatName.c_str()); + doCommand(Doc,"App.activeDocument().%s.Force = 100.0",FeatName.c_str()); + doCommand(Doc,"App.activeDocument().%s.TensionForce = 100.0",FeatName.c_str()); + updateActive(); + + doCommand(Gui,"Gui.activeDocument().setEdit('%s')",FeatName.c_str()); +} + +bool CmdFemConstraintPulley::isActive(void) +{ + return hasActiveDocument(); +} // ##################################################################################################### @@ -281,4 +438,9 @@ void CreateFemCommands(void) rcCmdMgr.addCommand(new CmdFemCreateFromShape()); rcCmdMgr.addCommand(new CmdFemCreateNodesSet()); rcCmdMgr.addCommand(new CmdFemDefineNodesSet()); + rcCmdMgr.addCommand(new CmdFemConstraintBearing()); + rcCmdMgr.addCommand(new CmdFemConstraintFixed()); + rcCmdMgr.addCommand(new CmdFemConstraintForce()); + rcCmdMgr.addCommand(new CmdFemConstraintGear()); + rcCmdMgr.addCommand(new CmdFemConstraintPulley()); } diff --git a/src/Mod/Fem/Gui/Resources/Fem.qrc b/src/Mod/Fem/Gui/Resources/Fem.qrc index e63839105d..a93dcf0990 100755 --- a/src/Mod/Fem/Gui/Resources/Fem.qrc +++ b/src/Mod/Fem/Gui/Resources/Fem.qrc @@ -2,6 +2,11 @@ icons/Fem_FemMesh.svg icons/Fem_FemMesh_createnodebypoly.svg + icons/Fem_ConstraintForce.svg + icons/Fem_ConstraintFixed.svg + icons/Fem_ConstraintBearing.svg + icons/Fem_ConstraintGear.svg + icons/Fem_ConstraintPulley.svg translations/Fem_af.qm translations/Fem_de.qm translations/Fem_fi.qm diff --git a/src/Mod/Fem/Gui/Resources/icons/Fem_ConstraintBearing.svg b/src/Mod/Fem/Gui/Resources/icons/Fem_ConstraintBearing.svg new file mode 100644 index 0000000000..8b9904b4ae --- /dev/null +++ b/src/Mod/Fem/Gui/Resources/icons/Fem_ConstraintBearing.svg @@ -0,0 +1,435 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Mod/Fem/Gui/Resources/icons/Fem_ConstraintFixed.svg b/src/Mod/Fem/Gui/Resources/icons/Fem_ConstraintFixed.svg new file mode 100644 index 0000000000..9c42928d6d --- /dev/null +++ b/src/Mod/Fem/Gui/Resources/icons/Fem_ConstraintFixed.svg @@ -0,0 +1,192 @@ + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Mod/Fem/Gui/Resources/icons/Fem_ConstraintForce.svg b/src/Mod/Fem/Gui/Resources/icons/Fem_ConstraintForce.svg new file mode 100644 index 0000000000..16968d60b2 --- /dev/null +++ b/src/Mod/Fem/Gui/Resources/icons/Fem_ConstraintForce.svg @@ -0,0 +1,204 @@ + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Mod/Fem/Gui/Resources/icons/Fem_ConstraintGear.svg b/src/Mod/Fem/Gui/Resources/icons/Fem_ConstraintGear.svg new file mode 100644 index 0000000000..186772d3dd --- /dev/null +++ b/src/Mod/Fem/Gui/Resources/icons/Fem_ConstraintGear.svg @@ -0,0 +1,1175 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Mod/Fem/Gui/Resources/icons/Fem_ConstraintPulley.svg b/src/Mod/Fem/Gui/Resources/icons/Fem_ConstraintPulley.svg new file mode 100644 index 0000000000..3515aa933d --- /dev/null +++ b/src/Mod/Fem/Gui/Resources/icons/Fem_ConstraintPulley.svg @@ -0,0 +1,1069 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Mod/Fem/Gui/TaskFemConstraint.cpp b/src/Mod/Fem/Gui/TaskFemConstraint.cpp new file mode 100644 index 0000000000..1169971f0d --- /dev/null +++ b/src/Mod/Fem/Gui/TaskFemConstraint.cpp @@ -0,0 +1,212 @@ +/*************************************************************************** + * Copyright (c) 2013 Jan Rheinländer * + * * + * 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 +#endif + +#include "ui_TaskFemConstraint.h" +#include "TaskFemConstraint.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace FemGui; +using namespace Gui; + +/* TRANSLATOR FemGui::TaskFemConstraint */ + +TaskFemConstraint::TaskFemConstraint(ViewProviderFemConstraint *ConstraintView,QWidget *parent,const char* pixmapname) + : TaskBox(Gui::BitmapFactory().pixmap(pixmapname),tr("FEM constraint parameters"),true, parent),ConstraintView(ConstraintView) +{ + selectionMode = selref; + + // Setup the dialog inside the Shaft Wizard dialog + if ((ConstraintView->wizardWidget != NULL) && (ConstraintView->wizardSubLayout != NULL)) { + // Hide the shaft wizard table widget to make more space + ConstraintView->wizardSubLayout->itemAt(0)->widget()->hide(); + QGridLayout* buttons = ConstraintView->wizardSubLayout->findChild(); + for (int b = 0; b < buttons->count(); b++) + buttons->itemAt(b)->widget()->hide(); + + // Show this dialog for the FEM constraint + ConstraintView->wizardWidget->addWidget(this); + + // Add buttons to finish editing the constraint without closing the shaft wizard dialog + okButton = new QPushButton(QObject::tr("Ok")); + cancelButton = new QPushButton(QObject::tr("Cancel")); + buttonBox = new QDialogButtonBox(); + buttonBox->addButton(okButton, QDialogButtonBox::AcceptRole); + buttonBox->addButton(cancelButton, QDialogButtonBox::RejectRole); + QObject::connect(okButton, SIGNAL(clicked()), this, SLOT(onButtonWizOk())); + QObject::connect(cancelButton, SIGNAL(clicked()), this, SLOT(onButtonWizCancel())); + ConstraintView->wizardWidget->addWidget(buttonBox); + } +} + +void TaskFemConstraint::keyPressEvent(QKeyEvent *ke) +{ + if ((ConstraintView->wizardWidget != NULL) && (ConstraintView->wizardSubLayout != NULL)) + // Prevent from closing this dialog AND the shaft wizard dialog + // TODO: This should trigger an update in the shaft wizard but its difficult to access a python dialog from here... + if (ke->key() == Qt::Key_Return) + return; + + TaskBox::keyPressEvent(ke); +} + +const std::string TaskFemConstraint::getReferences(const std::vector& items) const +{ + std::string result; + for (std::vector::const_iterator i = items.begin(); i != items.end(); i++) { + int pos = i->find_last_of(":"); + std::string objStr = "App.ActiveDocument." + i->substr(0, pos); + std::string refStr = "\"" + i->substr(pos+1) + "\""; + result = result + (i != items.begin() ? ", " : "") + "(" + objStr + "," + refStr + ")"; + } + + return result; +} + +void TaskFemConstraint::onReferenceDeleted(const int row) { + Fem::Constraint* pcConstraint = static_cast(ConstraintView->getObject()); + std::vector Objects = pcConstraint->References.getValues(); + std::vector SubElements = pcConstraint->References.getSubValues(); + + Objects.erase(Objects.begin() + row); + SubElements.erase(SubElements.begin() + row); + pcConstraint->References.setValues(Objects, SubElements); +} + +void TaskFemConstraint::onButtonReference(const bool pressed) { + if (pressed) + selectionMode = selref; + else + selectionMode = selnone; + Gui::Selection().clearSelection(); +} + +void TaskFemConstraint::onButtonWizOk() +{ + // Remove dialog elements + buttonBox->removeButton(okButton); + delete okButton; + buttonBox->removeButton(cancelButton); + delete cancelButton; + ConstraintView->wizardWidget->removeWidget(buttonBox); + delete buttonBox; + ConstraintView->wizardWidget->removeWidget(this); + + // Show the wizard shaft dialog again + ConstraintView->wizardSubLayout->itemAt(0)->widget()->show(); + QGridLayout* buttons = ConstraintView->wizardSubLayout->findChild(); + for (int b = 0; b < buttons->count(); b++) + buttons->itemAt(b)->widget()->show(); + + Gui::Application::Instance->activeDocument()->resetEdit(); // Reaches ViewProviderFemConstraint::unsetEdit() eventually +} + +void TaskFemConstraint::onButtonWizCancel() +{ + Fem::Constraint* pcConstraint = static_cast(ConstraintView->getObject()); + if (pcConstraint != NULL) + pcConstraint->getDocument()->remObject(pcConstraint->getNameInDocument()); + onButtonWizOk(); +} + +const QString TaskFemConstraint::makeRefText(const App::DocumentObject* obj, const std::string& subName) const +{ + return QString::fromUtf8((std::string(obj->getNameInDocument()) + ":" + subName).c_str()); +} + +//************************************************************************** +//************************************************************************** +// TaskDialog +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +//==== calls from the TaskView =============================================================== + + +bool TaskDlgFemConstraint::accept() +{ + std::string name = ConstraintView->getObject()->getNameInDocument(); + + try { + std::string refs = parameter->getReferences(); + + if (!refs.empty()) { + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.References = [%s]", name.c_str(), refs.c_str()); + } else { + QMessageBox::warning(parameter, tr("Input error"), tr("You must specify at least one reference")); + return false; + } + + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.recompute()"); + if (!ConstraintView->getObject()->isValid()) + throw Base::Exception(ConstraintView->getObject()->getStatusString()); + Gui::Command::doCommand(Gui::Command::Gui,"Gui.activeDocument().resetEdit()"); + Gui::Command::commitCommand(); + } + catch (const Base::Exception& e) { + QMessageBox::warning(parameter, tr("Input error"), QString::fromAscii(e.what())); + return false; + } + + return true; +} + +bool TaskDlgFemConstraint::reject() +{ + // roll back the changes + Gui::Command::abortCommand(); + Gui::Command::doCommand(Gui::Command::Gui,"Gui.activeDocument().resetEdit()"); + + return true; +} + + +#include "moc_TaskFemConstraint.cpp" diff --git a/src/Mod/Fem/Gui/TaskFemConstraint.h b/src/Mod/Fem/Gui/TaskFemConstraint.h new file mode 100644 index 0000000000..72390e113a --- /dev/null +++ b/src/Mod/Fem/Gui/TaskFemConstraint.h @@ -0,0 +1,107 @@ +/*************************************************************************** + * Copyright (c) 2013 Jan Rheinländer * + * * + * 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_TaskFemConstraint_H +#define GUI_TASKVIEW_TaskFemConstraint_H + +#include +#include +#include + +#include "ViewProviderFemConstraint.h" + +namespace FemGui { + +class TaskFemConstraint : public Gui::TaskView::TaskBox, public Gui::SelectionObserver +{ + Q_OBJECT + +public: + TaskFemConstraint(ViewProviderFemConstraint *ConstraintView,QWidget *parent = 0,const char* pixmapname = ""); + virtual ~TaskFemConstraint() {} + + virtual const std::string getReferences(void) const {} + const std::string getReferences(const std::vector& items) const; + +protected Q_SLOTS: + void onReferenceDeleted(const int row); + void onButtonReference(const bool pressed = true); + // Shaft Wizard integration + void onButtonWizOk(); + void onButtonWizCancel(); + +protected: + virtual void changeEvent(QEvent *e) { TaskBox::changeEvent(e); } + const QString makeRefText(const App::DocumentObject* obj, const std::string& subName) const; + virtual void keyPressEvent(QKeyEvent * ke); + +private: + virtual void onSelectionChanged(const Gui::SelectionChanges&) {} + +protected: + QWidget* proxy; + ViewProviderFemConstraint *ConstraintView; + enum {seldir, selref, selloc, selnone} selectionMode; + +private: + // This seems to be the only way to access the widgets again in order to remove them from the dialog + QDialogButtonBox* buttonBox; + QPushButton* okButton; + QPushButton* cancelButton; +}; + +/// simulation dialog for the TaskView +class TaskDlgFemConstraint : public Gui::TaskView::TaskDialog +{ + Q_OBJECT + +public: + /* + /// is called the TaskView when the dialog is opened + virtual void open() {} + /// is called by the framework if an button is clicked which has no accept or reject role + virtual void clicked(int) {} + /// is called by the framework if the dialog is accepted (Ok) + */ + virtual bool accept(); + /// is called by the framework if the dialog is rejected (Cancel) + virtual bool reject(); + /// is called by the framework if the user presses the help button + virtual bool isAllowedAlterDocument(void) const + { return false; } + + /// returns for Close and Help button + virtual QDialogButtonBox::StandardButtons getStandardButtons(void) const + { return QDialogButtonBox::Ok|QDialogButtonBox::Cancel; } + + ViewProviderFemConstraint* getConstraintView() const + { return ConstraintView; } + +protected: + ViewProviderFemConstraint *ConstraintView; + TaskFemConstraint *parameter; +}; + +} //namespace FemGui + +#endif // GUI_TASKVIEW_TaskFemConstraint_H diff --git a/src/Mod/Fem/Gui/TaskFemConstraint.ui b/src/Mod/Fem/Gui/TaskFemConstraint.ui new file mode 100644 index 0000000000..2ca053720b --- /dev/null +++ b/src/Mod/Fem/Gui/TaskFemConstraint.ui @@ -0,0 +1,208 @@ + + + TaskFemConstraint + + + + 0 + 0 + 257 + 461 + + + + Form + + + + + + + + + Add reference + + + + + + + + + + + + Load [N] + + + + + + + -99999.000000000000000 + + + 99999.000000000000000 + + + 500.000000000000000 + + + + + + + + + + + Diameter + + + + + + + 3 + + + -99999.000000000000000 + + + 99999.000000000000000 + + + 100.000000000000000 + + + + + + + + + + + Other diameter + + + + + + + 3 + + + -99999.000000000000000 + + + 99999.000000000000000 + + + 200.000000000000000 + + + + + + + + + + + Center distance + + + + + + + -99999.000000000000000 + + + 99999.000000000000000 + + + 500.000000000000000 + + + + + + + + + + + Direction + + + + + + + + + + + + Reverse direction + + + + + + + + + Location + + + + + + + + + + + + + + Distance + + + + + + + -99999.000000000000000 + + + 99999.000000000000000 + + + 10.000000000000000 + + + + + + + + + Qt::Vertical + + + + 17 + 56 + + + + + + + + + diff --git a/src/Mod/Fem/Gui/TaskFemConstraintBearing.cpp b/src/Mod/Fem/Gui/TaskFemConstraintBearing.cpp new file mode 100644 index 0000000000..cf98b3003c --- /dev/null +++ b/src/Mod/Fem/Gui/TaskFemConstraintBearing.cpp @@ -0,0 +1,356 @@ +/*************************************************************************** + * Copyright (c) 2013 Jan Rheinländer * + * * + * 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 +#endif + +#include "ui_TaskFemConstraintBearing.h" +#include "TaskFemConstraintBearing.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace FemGui; +using namespace Gui; + +/* TRANSLATOR FemGui::TaskFemConstraintBearing */ + +TaskFemConstraintBearing::TaskFemConstraintBearing(ViewProviderFemConstraint *ConstraintView,QWidget *parent, + const char *pixmapname) + : TaskFemConstraint(ConstraintView, parent, pixmapname) +{ + // we need a separate container widget to add all controls to + proxy = new QWidget(this); + ui = new Ui_TaskFemConstraintBearing(); + ui->setupUi(proxy); + QMetaObject::connectSlotsByName(this); + + // Create a context menu for the listview of the references + QAction* action = new QAction(tr("Delete"), ui->listReferences); + action->connect(action, SIGNAL(triggered()), + this, SLOT(onReferenceDeleted())); + ui->listReferences->addAction(action); + ui->listReferences->setContextMenuPolicy(Qt::ActionsContextMenu); + + connect(ui->spinDistance, SIGNAL(valueChanged(double)), + this, SLOT(onDistanceChanged(double))); + connect(ui->buttonReference, SIGNAL(pressed()), + this, SLOT(onButtonReference())); + connect(ui->buttonLocation, SIGNAL(pressed()), + this, SLOT(onButtonLocation())); + connect(ui->checkAxial, SIGNAL(toggled(bool)), + this, SLOT(onCheckAxial(bool))); + + this->groupLayout()->addWidget(proxy); + + // Temporarily prevent unnecessary feature recomputes + ui->spinDistance->blockSignals(true); + ui->listReferences->blockSignals(true); + ui->buttonReference->blockSignals(true); + ui->buttonLocation->blockSignals(true); + ui->checkAxial->blockSignals(true); + + // Get the feature data + Fem::ConstraintBearing* pcConstraint = static_cast(ConstraintView->getObject()); + double d = pcConstraint->Dist.getValue(); + std::vector Objects = pcConstraint->References.getValues(); + std::vector SubElements = pcConstraint->References.getSubValues(); + std::vector locStrings = pcConstraint->Location.getSubValues(); + QString loc; + if (!locStrings.empty()) + loc = makeRefText(pcConstraint->Location.getValue(), locStrings.front()); + bool axialfree = pcConstraint->AxialFree.getValue(); + + // Fill data into dialog elements + ui->spinDistance->setMinimum(-FLOAT_MAX); + ui->spinDistance->setMaximum(FLOAT_MAX); + ui->spinDistance->setValue(d); + ui->listReferences->clear(); + for (int i = 0; i < Objects.size(); i++) + ui->listReferences->addItem(makeRefText(Objects[i], SubElements[i])); + if (Objects.size() > 0) + ui->listReferences->setCurrentRow(0, QItemSelectionModel::ClearAndSelect); + ui->lineLocation->setText(loc); + ui->checkAxial->setChecked(axialfree); + + // Hide unwanted ui elements + ui->labelDiameter->setVisible(false); + ui->spinDiameter->setVisible(false); + ui->labelOtherDiameter->setVisible(false); + ui->spinOtherDiameter->setVisible(false); + ui->labelCenterDistance->setVisible(false); + ui->spinCenterDistance->setVisible(false); + ui->checkIsDriven->setVisible(false); + ui->labelForce->setVisible(false); + ui->spinForce->setVisible(false); + ui->labelTensionForce->setVisible(false); + ui->spinTensionForce->setVisible(false); + ui->labelForceAngle->setVisible(false); + ui->spinForceAngle->setVisible(false); + ui->buttonDirection->setVisible(false); + ui->lineDirection->setVisible(false); + ui->checkReversed->setVisible(false); + + ui->spinDistance->blockSignals(false); + ui->listReferences->blockSignals(false); + ui->buttonReference->blockSignals(false); + ui->buttonLocation->blockSignals(false); + ui->checkAxial->blockSignals(false); + + onButtonReference(true); +} + +void TaskFemConstraintBearing::onSelectionChanged(const Gui::SelectionChanges& msg) +{ + if (msg.Type == Gui::SelectionChanges::AddSelection) { + // Don't allow selection in other document + if (strcmp(msg.pDocName, ConstraintView->getObject()->getDocument()->getName()) != 0) + return; + + if (!msg.pSubName || msg.pSubName[0] == '\0') + return; + std::string subName(msg.pSubName); + + if (selectionMode == selnone) + return; + + Fem::ConstraintBearing* pcConstraint = static_cast(ConstraintView->getObject()); + App::DocumentObject* obj = ConstraintView->getObject()->getDocument()->getObject(msg.pObjectName); + Part::Feature* feat = static_cast(obj); + TopoDS_Shape ref = feat->Shape.getShape().getSubShape(subName.c_str()); + + if (selectionMode == selref) { + std::vector Objects = pcConstraint->References.getValues(); + std::vector SubElements = pcConstraint->References.getSubValues(); + + if (Objects.size() > 0) { + QMessageBox::warning(this, tr("Selection error"), tr("Please use only a single reference for bearing constraint")); + return; + } + if (subName.substr(0,4) != "Face") { + QMessageBox::warning(this, tr("Selection error"), tr("Only faces can be picked")); + return; + } + + // Only cylindrical faces allowed + BRepAdaptor_Surface surface(TopoDS::Face(ref)); + if (surface.GetType() != GeomAbs_Cylinder) { + QMessageBox::warning(this, tr("Selection error"), tr("Only cylindrical faces can be picked")); + return; + } + + // add the new reference + Objects.push_back(obj); + SubElements.push_back(subName); + pcConstraint->References.setValues(Objects,SubElements); + ui->listReferences->addItem(makeRefText(obj, subName)); + + // Turn off reference selection mode + onButtonReference(false); + } else if (selectionMode == selloc) { + if (subName.substr(0,4) == "Face") { + BRepAdaptor_Surface surface(TopoDS::Face(ref)); + if (surface.GetType() != GeomAbs_Plane) { + QMessageBox::warning(this, tr("Selection error"), tr("Only planar faces can be picked")); + return; + } + } else if (subName.substr(0,4) == "Edge") { + BRepAdaptor_Curve line(TopoDS::Edge(ref)); + if (line.GetType() != GeomAbs_Line) { + QMessageBox::warning(this, tr("Selection error"), tr("Only linear edges can be picked")); + return; + } + } else { + QMessageBox::warning(this, tr("Selection error"), tr("Only faces and edges can be picked")); + return; + } + std::vector references(1,subName); + pcConstraint->Location.setValue(obj, references); + ui->lineLocation->setText(makeRefText(obj, subName)); + + // Turn off location selection mode + onButtonLocation(false); + } + + Gui::Selection().clearSelection(); + } +} + +void TaskFemConstraintBearing::onDistanceChanged(double l) +{ + Fem::ConstraintBearing* pcConstraint = static_cast(ConstraintView->getObject()); + pcConstraint->Dist.setValue((float)l); +} + +void TaskFemConstraintBearing::onReferenceDeleted() { + int row = ui->listReferences->currentIndex().row(); + TaskFemConstraint::onReferenceDeleted(row); + ui->listReferences->model()->removeRow(row); + ui->listReferences->setCurrentRow(0, QItemSelectionModel::ClearAndSelect); +} + +void TaskFemConstraintBearing::onButtonLocation(const bool pressed) { + if (pressed) { + selectionMode = selloc; + } else { + selectionMode = selnone; + } + ui->buttonLocation->setChecked(pressed); + Gui::Selection().clearSelection(); +} + +void TaskFemConstraintBearing::onCheckAxial(const bool pressed) +{ + Fem::ConstraintBearing* pcConstraint = static_cast(ConstraintView->getObject()); + pcConstraint->AxialFree.setValue(pressed); +} + +double TaskFemConstraintBearing::getDistance(void) const +{ + return ui->spinDistance->value(); +} + +const std::string TaskFemConstraintBearing::getReferences() const +{ + int rows = ui->listReferences->model()->rowCount(); + + std::vector items; + for (int r = 0; r < rows; r++) + items.push_back(ui->listReferences->item(r)->text().toStdString()); + return TaskFemConstraint::getReferences(items); +} + +const std::string TaskFemConstraintBearing::getLocationName(void) const +{ + std::string loc = ui->lineLocation->text().toStdString(); + if (loc.empty()) + return ""; + + int pos = loc.find_last_of(":"); + return loc.substr(0, pos).c_str(); +} + +const std::string TaskFemConstraintBearing::getLocationObject(void) const +{ + std::string loc = ui->lineLocation->text().toStdString(); + if (loc.empty()) + return ""; + + int pos = loc.find_last_of(":"); + return loc.substr(pos+1).c_str(); +} + +bool TaskFemConstraintBearing::getAxial() const +{ + return ui->checkAxial->isChecked(); +} + +TaskFemConstraintBearing::~TaskFemConstraintBearing() +{ + delete ui; +} + +void TaskFemConstraintBearing::changeEvent(QEvent *e) +{ + TaskBox::changeEvent(e); + if (e->type() == QEvent::LanguageChange) { + ui->spinDistance->blockSignals(true); + ui->retranslateUi(proxy); + ui->spinDistance->blockSignals(false); + } +} + +//************************************************************************** +//************************************************************************** +// TaskDialog +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +TaskDlgFemConstraintBearing::TaskDlgFemConstraintBearing(ViewProviderFemConstraintBearing *ConstraintView) +{ + this->ConstraintView = ConstraintView; + assert(ConstraintView); + this->parameter = new TaskFemConstraintBearing(ConstraintView); + + Content.push_back(parameter); +} + +//==== calls from the TaskView =============================================================== + +bool TaskDlgFemConstraintBearing::accept() +{ + std::string name = ConstraintView->getObject()->getNameInDocument(); + const TaskFemConstraintBearing* parameterBearing = static_cast(parameter); + + try { + //Gui::Command::openCommand("FEM force constraint changed"); + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Dist = %f",name.c_str(), parameterBearing->getDistance()); + + std::string locname = parameterBearing->getLocationName().data(); + std::string locobj = parameterBearing->getLocationObject().data(); + + if (!locname.empty()) { + QString buf = QString::fromUtf8("(App.ActiveDocument.%1,[\"%2\"])"); + buf = buf.arg(QString::fromStdString(locname)); + buf = buf.arg(QString::fromStdString(locobj)); + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Location = %s", name.c_str(), buf.toStdString().c_str()); + } else { + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Location = None", name.c_str()); + } + + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.AxialFree = %s", name.c_str(), parameterBearing->getAxial() ? "True" : "False"); + } + catch (const Base::Exception& e) { + QMessageBox::warning(parameter, tr("Input error"), QString::fromAscii(e.what())); + return false; + } + + return TaskDlgFemConstraint::accept(); +} + +#include "moc_TaskFemConstraintBearing.cpp" diff --git a/src/Mod/Fem/Gui/TaskFemConstraintBearing.h b/src/Mod/Fem/Gui/TaskFemConstraintBearing.h new file mode 100644 index 0000000000..e6a566f483 --- /dev/null +++ b/src/Mod/Fem/Gui/TaskFemConstraintBearing.h @@ -0,0 +1,92 @@ +/*************************************************************************** + * Copyright (c) 2013 Jan Rheinländer * + * * + * 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_TaskFemConstraintBearing_H +#define GUI_TASKVIEW_TaskFemConstraintBearing_H + +#include +#include +#include + +#include "TaskFemConstraint.h" +#include "ViewProviderFemConstraintBearing.h" + +class Ui_TaskFemConstraintBearing; + +namespace App { +class Property; +} + +namespace Gui { +class ViewProvider; +} + +namespace FemGui { + +class TaskFemConstraintBearing : public TaskFemConstraint +{ + Q_OBJECT + +public: + TaskFemConstraintBearing(ViewProviderFemConstraint *ConstraintView, QWidget *parent = 0, + const char* pixmapname = "Fem_ConstraintBearing"); + virtual ~TaskFemConstraintBearing(); + + double getDistance(void) const; + virtual const std::string getReferences() const; + const std::string getLocationName(void) const; + const std::string getLocationObject(void) const; + bool getAxial(void) const; + +private Q_SLOTS: + void onReferenceDeleted(void); + void onDistanceChanged(double l); + void onButtonLocation(const bool pressed = true); + void onCheckAxial(bool); + +protected: + virtual void changeEvent(QEvent *e); + virtual void onSelectionChanged(const Gui::SelectionChanges& msg); + +protected: + Ui_TaskFemConstraintBearing* ui; + +}; + +/// simulation dialog for the TaskView +class TaskDlgFemConstraintBearing : public TaskDlgFemConstraint +{ + Q_OBJECT + +public: + TaskDlgFemConstraintBearing() {} + TaskDlgFemConstraintBearing(ViewProviderFemConstraintBearing *ConstraintView); + + /// is called by the framework if the dialog is accepted (Ok) + virtual bool accept(); + +}; + +} //namespace FemGui + +#endif // GUI_TASKVIEW_TaskFemConstraintBearing_H diff --git a/src/Mod/Fem/Gui/TaskFemConstraintBearing.ui b/src/Mod/Fem/Gui/TaskFemConstraintBearing.ui new file mode 100644 index 0000000000..f451990943 --- /dev/null +++ b/src/Mod/Fem/Gui/TaskFemConstraintBearing.ui @@ -0,0 +1,270 @@ + + + TaskFemConstraintBearing + + + + 0 + 0 + 257 + 534 + + + + Form + + + + + + Add reference + + + + + + + + + + + + true + + + Gear diameter + + + + + + + -99999.000000000000000 + + + 99999.000000000000000 + + + 100.000000000000000 + + + + + + + + + + + true + + + Other pulley dia + + + + + + + -99999.000000000000000 + + + 99999.000000000000000 + + + 100.000000000000000 + + + + + + + + + + + Center distance + + + + + + + -99999.000000000000000 + + + 99999.000000000000000 + + + 1000.000000000000000 + + + + + + + + + + + Force + + + + + + + -99999.000000000000000 + + + 99999.000000000000000 + + + 1000.000000000000000 + + + + + + + + + + + Belt tension force + + + + + + + -99999.000000000000000 + + + 99999.000000000000000 + + + 1000.000000000000000 + + + + + + + + + Driven pulley + + + + + + + + + Force location [deg] + + + + + + + 1 + + + -360.000000000000000 + + + 360.000000000000000 + + + 0.000000000000000 + + + + + + + + + + + Force Direction + + + + + + + + + + + + Reversed direction + + + + + + + Axial free + + + + + + + + + Location + + + + + + + + + + + + + + Distance + + + + + + + -99999.000000000000000 + + + 99999.000000000000000 + + + 0.000000000000000 + + + + + + + + + Qt::Vertical + + + + 17 + 56 + + + + + + + + + diff --git a/src/Mod/Fem/Gui/TaskFemConstraintFixed.cpp b/src/Mod/Fem/Gui/TaskFemConstraintFixed.cpp new file mode 100644 index 0000000000..cc9ad5217e --- /dev/null +++ b/src/Mod/Fem/Gui/TaskFemConstraintFixed.cpp @@ -0,0 +1,217 @@ +/*************************************************************************** + * Copyright (c) 2013 Jan Rheinländer * + * * + * 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 +#endif + +#include "ui_TaskFemConstraintFixed.h" +#include "TaskFemConstraintFixed.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace FemGui; +using namespace Gui; + +/* TRANSLATOR FemGui::TaskFemConstraintFixed */ + +TaskFemConstraintFixed::TaskFemConstraintFixed(ViewProviderFemConstraintFixed *ConstraintView,QWidget *parent) + : TaskFemConstraint(ConstraintView, parent, "Fem_ConstraintFixed") +{ + // we need a separate container widget to add all controls to + proxy = new QWidget(this); + ui = new Ui_TaskFemConstraintFixed(); + ui->setupUi(proxy); + QMetaObject::connectSlotsByName(this); + + // Create a context menu for the listview of the references + QAction* action = new QAction(tr("Delete"), ui->listReferences); + action->connect(action, SIGNAL(triggered()), + this, SLOT(onReferenceDeleted())); + ui->listReferences->addAction(action); + ui->listReferences->setContextMenuPolicy(Qt::ActionsContextMenu); + + connect(ui->buttonReference, SIGNAL(pressed()), + this, SLOT(onButtonReference())); + + this->groupLayout()->addWidget(proxy); + + // Temporarily prevent unnecessary feature recomputes + ui->listReferences->blockSignals(true); + ui->buttonReference->blockSignals(true); + + // Get the feature data + Fem::ConstraintFixed* pcConstraint = static_cast(ConstraintView->getObject()); + std::vector Objects = pcConstraint->References.getValues(); + std::vector SubElements = pcConstraint->References.getSubValues(); + + // Fill data into dialog elements + ui->listReferences->clear(); + for (int i = 0; i < Objects.size(); i++) + ui->listReferences->addItem(makeRefText(Objects[i], SubElements[i])); + if (Objects.size() > 0) + ui->listReferences->setCurrentRow(0, QItemSelectionModel::ClearAndSelect); + + ui->listReferences->blockSignals(false); + ui->buttonReference->blockSignals(false); + + // Selection mode can be always on since there is nothing else in the UI + onButtonReference(true); +} + +void TaskFemConstraintFixed::onSelectionChanged(const Gui::SelectionChanges& msg) +{ + if (msg.Type == Gui::SelectionChanges::AddSelection) { + // Don't allow selection in other document + if (strcmp(msg.pDocName, ConstraintView->getObject()->getDocument()->getName()) != 0) + return; + + if (!msg.pSubName || msg.pSubName[0] == '\0') + return; + std::string subName(msg.pSubName); + + if (selectionMode == selnone) + return; + + std::vector references(1,subName); + Fem::ConstraintFixed* pcConstraint = static_cast(ConstraintView->getObject()); + App::DocumentObject* obj = ConstraintView->getObject()->getDocument()->getObject(msg.pObjectName); + Part::Feature* feat = static_cast(obj); + TopoDS_Shape ref = feat->Shape.getShape().getSubShape(subName.c_str()); + + if (selectionMode == selref) { + std::vector Objects = pcConstraint->References.getValues(); + std::vector SubElements = pcConstraint->References.getSubValues(); + + // Ensure we don't have mixed reference types + if (SubElements.size() > 0) { + if (subName.substr(0,4) != SubElements.front().substr(0,4)) { + QMessageBox::warning(this, tr("Selection error"), tr("Mixed shape types are not possible. Use a second constraint instead")); + return; + } + } else { + if ((subName.substr(0,4) != "Face") && (subName.substr(0,4) != "Edge") && (subName.substr(0,6) != "Vertex")) { + QMessageBox::warning(this, tr("Selection error"), tr("Only faces, edges and vertices can be picked")); + return; + } + } + + // Avoid duplicates + int pos = 0; + for (; pos < Objects.size(); pos++) + if (obj == Objects[pos]) + break; + + if (pos != Objects.size()) + if (subName == SubElements[pos]) + return; + + // add the new reference + Objects.push_back(obj); + SubElements.push_back(subName); + pcConstraint->References.setValues(Objects,SubElements); + ui->listReferences->addItem(makeRefText(obj, subName)); + } + + Gui::Selection().clearSelection(); + } +} + +void TaskFemConstraintFixed::onReferenceDeleted() { + int row = ui->listReferences->currentIndex().row(); + TaskFemConstraint::onReferenceDeleted(row); + ui->listReferences->model()->removeRow(row); + ui->listReferences->setCurrentRow(0, QItemSelectionModel::ClearAndSelect); +} + +const std::string TaskFemConstraintFixed::getReferences() const +{ + int rows = ui->listReferences->model()->rowCount(); + + std::vector items; + for (int r = 0; r < rows; r++) + items.push_back(ui->listReferences->item(r)->text().toStdString()); + return TaskFemConstraint::getReferences(items); +} + +TaskFemConstraintFixed::~TaskFemConstraintFixed() +{ + delete ui; +} + +void TaskFemConstraintFixed::changeEvent(QEvent *e) +{ + TaskBox::changeEvent(e); + if (e->type() == QEvent::LanguageChange) { + ui->retranslateUi(proxy); + } +} + +//************************************************************************** +//************************************************************************** +// TaskDialog +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +TaskDlgFemConstraintFixed::TaskDlgFemConstraintFixed(ViewProviderFemConstraintFixed *ConstraintView) +{ + this->ConstraintView = ConstraintView; + assert(ConstraintView); + this->parameter = new TaskFemConstraintFixed(ConstraintView);; + + Content.push_back(parameter); +} + +//==== calls from the TaskView =============================================================== + +bool TaskDlgFemConstraintFixed::accept() +{ + return TaskDlgFemConstraint::accept(); +} + +#include "moc_TaskFemConstraintFixed.cpp" diff --git a/src/Mod/Fem/Gui/TaskFemConstraintFixed.h b/src/Mod/Fem/Gui/TaskFemConstraintFixed.h new file mode 100644 index 0000000000..d6c4042821 --- /dev/null +++ b/src/Mod/Fem/Gui/TaskFemConstraintFixed.h @@ -0,0 +1,84 @@ +/*************************************************************************** + * Copyright (c) 2013 Jan Rheinländer * + * * + * 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_TaskFemConstraintFixed_H +#define GUI_TASKVIEW_TaskFemConstraintFixed_H + +#include +#include +#include + +#include "TaskFemConstraint.h" +#include "ViewProviderFemConstraintFixed.h" + +class Ui_TaskFemConstraintFixed; + +namespace App { +class Property; +} + +namespace Gui { +class ViewProvider; +} + +namespace FemGui { + +class TaskFemConstraintFixed : public TaskFemConstraint +{ + Q_OBJECT + +public: + TaskFemConstraintFixed(ViewProviderFemConstraintFixed *ConstraintView,QWidget *parent = 0); + virtual ~TaskFemConstraintFixed(); + + virtual const std::string getReferences() const; + +private Q_SLOTS: + void onReferenceDeleted(void); + +protected: + virtual void changeEvent(QEvent *e); + +private: + virtual void onSelectionChanged(const Gui::SelectionChanges& msg); + +private: + Ui_TaskFemConstraintFixed* ui; +}; + +/// simulation dialog for the TaskView +class TaskDlgFemConstraintFixed : public TaskDlgFemConstraint +{ + Q_OBJECT + +public: + TaskDlgFemConstraintFixed(ViewProviderFemConstraintFixed *ConstraintView); + + /// is called by the framework if the dialog is accepted (Ok) + virtual bool accept(); + +}; + +} //namespace FemGui + +#endif // GUI_TASKVIEW_TaskFemConstraintFixed_H diff --git a/src/Mod/Fem/Gui/TaskFemConstraintFixed.ui b/src/Mod/Fem/Gui/TaskFemConstraintFixed.ui new file mode 100644 index 0000000000..799fe1bb1b --- /dev/null +++ b/src/Mod/Fem/Gui/TaskFemConstraintFixed.ui @@ -0,0 +1,44 @@ + + + TaskFemConstraintFixed + + + + 0 + 0 + 257 + 233 + + + + Form + + + + + + Add reference + + + + + + + + + + Qt::Vertical + + + + 17 + 56 + + + + + + + + + diff --git a/src/Mod/Fem/Gui/TaskFemConstraintForce.cpp b/src/Mod/Fem/Gui/TaskFemConstraintForce.cpp new file mode 100644 index 0000000000..8a51a99e48 --- /dev/null +++ b/src/Mod/Fem/Gui/TaskFemConstraintForce.cpp @@ -0,0 +1,363 @@ +/*************************************************************************** + * Copyright (c) 2013 Jan Rheinländer * + * * + * 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 +#endif + +#include "ui_TaskFemConstraintForce.h" +#include "TaskFemConstraintForce.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace FemGui; +using namespace Gui; + +/* TRANSLATOR FemGui::TaskFemConstraintForce */ + +TaskFemConstraintForce::TaskFemConstraintForce(ViewProviderFemConstraintForce *ConstraintView,QWidget *parent) + : TaskFemConstraint(ConstraintView, parent, "Fem_ConstraintForce") +{ + // we need a separate container widget to add all controls to + proxy = new QWidget(this); + ui = new Ui_TaskFemConstraintForce(); + ui->setupUi(proxy); + QMetaObject::connectSlotsByName(this); + + // Create a context menu for the listview of the references + QAction* action = new QAction(tr("Delete"), ui->listReferences); + action->connect(action, SIGNAL(triggered()), + this, SLOT(onReferenceDeleted())); + ui->listReferences->addAction(action); + ui->listReferences->setContextMenuPolicy(Qt::ActionsContextMenu); + + connect(ui->spinForce, SIGNAL(valueChanged(double)), + this, SLOT(onForceChanged(double))); + connect(ui->buttonReference, SIGNAL(pressed()), + this, SLOT(onButtonReference())); + connect(ui->buttonDirection, SIGNAL(pressed()), + this, SLOT(onButtonDirection())); + connect(ui->checkReverse, SIGNAL(toggled(bool)), + this, SLOT(onCheckReverse(bool))); + + this->groupLayout()->addWidget(proxy); + + // Temporarily prevent unnecessary feature recomputes + ui->spinForce->blockSignals(true); + ui->listReferences->blockSignals(true); + ui->buttonReference->blockSignals(true); + ui->buttonDirection->blockSignals(true); + ui->checkReverse->blockSignals(true); + + // Get the feature data + Fem::ConstraintForce* pcConstraint = static_cast(ConstraintView->getObject()); + double f = pcConstraint->Force.getValue(); + std::vector Objects = pcConstraint->References.getValues(); + std::vector SubElements = pcConstraint->References.getSubValues(); + std::vector dirStrings = pcConstraint->Direction.getSubValues(); + QString dir; + if (!dirStrings.empty()) + dir = makeRefText(pcConstraint->Direction.getValue(), dirStrings.front()); + bool reversed = pcConstraint->Reversed.getValue(); + + // Fill data into dialog elements + ui->spinForce->setMinimum(0); + ui->spinForce->setMaximum(FLOAT_MAX); + ui->spinForce->setValue(f); + ui->listReferences->clear(); + for (int i = 0; i < Objects.size(); i++) + ui->listReferences->addItem(makeRefText(Objects[i], SubElements[i])); + if (Objects.size() > 0) + ui->listReferences->setCurrentRow(0, QItemSelectionModel::ClearAndSelect); + ui->lineDirection->setText(dir.isEmpty() ? tr("") : dir); + ui->checkReverse->setChecked(reversed); + + ui->spinForce->blockSignals(false); + ui->listReferences->blockSignals(false); + ui->buttonReference->blockSignals(false); + ui->buttonDirection->blockSignals(false); + ui->checkReverse->blockSignals(false); + + updateUI(); +} + +void TaskFemConstraintForce::updateUI() +{ + if (ui->listReferences->model()->rowCount() == 0) { + // Go into reference selection mode if no reference has been selected yet + onButtonReference(true); + return; + } + + std::string ref = ui->listReferences->item(0)->text().toStdString(); + int pos = ref.find_last_of(":"); + if (ref.substr(pos+1, 6) == "Vertex") + ui->labelForce->setText(tr("Force [N]")); + else if (ref.substr(pos+1, 4) == "Edge") + ui->labelForce->setText(tr("Force [N/mm]")); + else if (ref.substr(pos+1, 4) == "Face") + ui->labelForce->setText(tr("Force [N/mm²]")); +} + +void TaskFemConstraintForce::onSelectionChanged(const Gui::SelectionChanges& msg) +{ + if (msg.Type == Gui::SelectionChanges::AddSelection) { + // Don't allow selection in other document + if (strcmp(msg.pDocName, ConstraintView->getObject()->getDocument()->getName()) != 0) + return; + + if (!msg.pSubName || msg.pSubName[0] == '\0') + return; + std::string subName(msg.pSubName); + + if (selectionMode == selnone) + return; + + std::vector references(1,subName); + Fem::ConstraintForce* pcConstraint = static_cast(ConstraintView->getObject()); + App::DocumentObject* obj = ConstraintView->getObject()->getDocument()->getObject(msg.pObjectName); + Part::Feature* feat = static_cast(obj); + TopoDS_Shape ref = feat->Shape.getShape().getSubShape(subName.c_str()); + + if (selectionMode == selref) { + std::vector Objects = pcConstraint->References.getValues(); + std::vector SubElements = pcConstraint->References.getSubValues(); + + // Ensure we don't have mixed reference types + if (SubElements.size() > 0) { + if (subName.substr(0,4) != SubElements.front().substr(0,4)) { + QMessageBox::warning(this, tr("Selection error"), tr("Mixed shape types are not possible. Use a second constraint instead")); + return; + } + } else { + if ((subName.substr(0,4) != "Face") && (subName.substr(0,4) != "Edge") && (subName.substr(0,6) != "Vertex")) { + QMessageBox::warning(this, tr("Selection error"), tr("Only faces, edges and vertices can be picked")); + return; + } + } + + // Avoid duplicates + int pos = 0; + for (; pos < Objects.size(); pos++) + if (obj == Objects[pos]) + break; + + if (pos != Objects.size()) + if (subName == SubElements[pos]) + return; + + // add the new reference + Objects.push_back(obj); + SubElements.push_back(subName); + pcConstraint->References.setValues(Objects,SubElements); + ui->listReferences->addItem(makeRefText(obj, subName)); + + // Turn off reference selection mode + onButtonReference(false); + } else if (selectionMode == seldir) { + if (subName.substr(0,4) == "Face") { + BRepAdaptor_Surface surface(TopoDS::Face(ref)); + if (surface.GetType() != GeomAbs_Plane) { + QMessageBox::warning(this, tr("Selection error"), tr("Only planar faces can be picked")); + return; + } + } else if (subName.substr(0,4) == "Edge") { + BRepAdaptor_Curve line(TopoDS::Edge(ref)); + if (line.GetType() != GeomAbs_Line) { + QMessageBox::warning(this, tr("Selection error"), tr("Only linear edges can be picked")); + return; + } + } else { + QMessageBox::warning(this, tr("Selection error"), tr("Only faces and edges can be picked")); + return; + } + pcConstraint->Direction.setValue(obj, references); + ui->lineDirection->setText(makeRefText(obj, subName)); + + // Turn off direction selection mode + onButtonDirection(false); + } + + Gui::Selection().clearSelection(); + updateUI(); + } +} + +void TaskFemConstraintForce::onForceChanged(double f) +{ + Fem::ConstraintForce* pcConstraint = static_cast(ConstraintView->getObject()); + pcConstraint->Force.setValue((float)f); +} + +void TaskFemConstraintForce::onReferenceDeleted() { + int row = ui->listReferences->currentIndex().row(); + TaskFemConstraint::onReferenceDeleted(row); + ui->listReferences->model()->removeRow(row); + ui->listReferences->setCurrentRow(0, QItemSelectionModel::ClearAndSelect); +} + +void TaskFemConstraintForce::onButtonDirection(const bool pressed) { + if (pressed) { + selectionMode = seldir; + } else { + selectionMode = selnone; + } + ui->buttonDirection->setChecked(pressed); + Gui::Selection().clearSelection(); +} + +void TaskFemConstraintForce::onCheckReverse(const bool pressed) +{ + Fem::ConstraintForce* pcConstraint = static_cast(ConstraintView->getObject()); + pcConstraint->Reversed.setValue(pressed); +} + +double TaskFemConstraintForce::getForce(void) const +{ + return ui->spinForce->value(); +} + +const std::string TaskFemConstraintForce::getReferences() const +{ + int rows = ui->listReferences->model()->rowCount(); + + std::vector items; + for (int r = 0; r < rows; r++) + items.push_back(ui->listReferences->item(r)->text().toStdString()); + return TaskFemConstraint::getReferences(items); +} + +const std::string TaskFemConstraintForce::getDirectionName(void) const +{ + std::string dir = ui->lineDirection->text().toStdString(); + if (dir.empty()) + return ""; + + int pos = dir.find_last_of(":"); + return dir.substr(0, pos).c_str(); +} + +const std::string TaskFemConstraintForce::getDirectionObject(void) const +{ + std::string dir = ui->lineDirection->text().toStdString(); + if (dir.empty()) + return ""; + + int pos = dir.find_last_of(":"); + return dir.substr(pos+1).c_str(); +} + +bool TaskFemConstraintForce::getReverse() const +{ + return ui->checkReverse->isChecked(); +} + +TaskFemConstraintForce::~TaskFemConstraintForce() +{ + delete ui; +} + +void TaskFemConstraintForce::changeEvent(QEvent *e) +{ + TaskBox::changeEvent(e); + if (e->type() == QEvent::LanguageChange) { + ui->spinForce->blockSignals(true); + ui->retranslateUi(proxy); + ui->spinForce->blockSignals(false); + } +} + +//************************************************************************** +//************************************************************************** +// TaskDialog +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +TaskDlgFemConstraintForce::TaskDlgFemConstraintForce(ViewProviderFemConstraintForce *ConstraintView) +{ + this->ConstraintView = ConstraintView; + assert(ConstraintView); + this->parameter = new TaskFemConstraintForce(ConstraintView);; + + Content.push_back(parameter); +} + +//==== calls from the TaskView =============================================================== + +bool TaskDlgFemConstraintForce::accept() +{ + std::string name = ConstraintView->getObject()->getNameInDocument(); + const TaskFemConstraintForce* parameterForce = static_cast(parameter); + + try { + //Gui::Command::openCommand("FEM force constraint changed"); + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Force = %f",name.c_str(), parameterForce->getForce()); + + std::string dirname = parameterForce->getDirectionName().data(); + std::string dirobj = parameterForce->getDirectionObject().data(); + + if (!dirname.empty()) { + QString buf = QString::fromUtf8("(App.ActiveDocument.%1,[\"%2\"])"); + buf = buf.arg(QString::fromStdString(dirname)); + buf = buf.arg(QString::fromStdString(dirobj)); + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Direction = %s", name.c_str(), buf.toStdString().c_str()); + } else { + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Direction = None", name.c_str()); + } + + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Reversed = %s", name.c_str(), parameterForce->getReverse() ? "True" : "False"); + } + catch (const Base::Exception& e) { + QMessageBox::warning(parameter, tr("Input error"), QString::fromAscii(e.what())); + return false; + } + + return TaskDlgFemConstraint::accept(); +} + +#include "moc_TaskFemConstraintForce.cpp" diff --git a/src/Mod/Fem/Gui/TaskFemConstraintForce.h b/src/Mod/Fem/Gui/TaskFemConstraintForce.h new file mode 100644 index 0000000000..5da5923609 --- /dev/null +++ b/src/Mod/Fem/Gui/TaskFemConstraintForce.h @@ -0,0 +1,92 @@ +/*************************************************************************** + * Copyright (c) 2013 Jan Rheinländer * + * * + * 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_TaskFemConstraintForce_H +#define GUI_TASKVIEW_TaskFemConstraintForce_H + +#include +#include +#include + +#include "TaskFemConstraint.h" +#include "ViewProviderFemConstraintForce.h" + +class Ui_TaskFemConstraintForce; + +namespace App { +class Property; +} + +namespace Gui { +class ViewProvider; +} + +namespace FemGui { + +class TaskFemConstraintForce : public TaskFemConstraint +{ + Q_OBJECT + +public: + TaskFemConstraintForce(ViewProviderFemConstraintForce *ConstraintView,QWidget *parent = 0); + virtual ~TaskFemConstraintForce(); + + double getForce(void) const; + virtual const std::string getReferences() const; + const std::string getDirectionName(void) const; + const std::string getDirectionObject(void) const; + bool getReverse(void) const; + +private Q_SLOTS: + void onReferenceDeleted(void); + void onForceChanged(double); + void onButtonDirection(const bool pressed = true); + void onCheckReverse(bool); + +protected: + virtual void changeEvent(QEvent *e); + +private: + virtual void onSelectionChanged(const Gui::SelectionChanges& msg); + void updateUI(); + +private: + Ui_TaskFemConstraintForce* ui; +}; + +/// simulation dialog for the TaskView +class TaskDlgFemConstraintForce : public TaskDlgFemConstraint +{ + Q_OBJECT + +public: + TaskDlgFemConstraintForce(ViewProviderFemConstraintForce *ConstraintView); + + /// is called by the framework if the dialog is accepted (Ok) + virtual bool accept(); + +}; + +} //namespace FemGui + +#endif // GUI_TASKVIEW_TaskFemConstraintForce_H diff --git a/src/Mod/Fem/Gui/TaskFemConstraintForce.ui b/src/Mod/Fem/Gui/TaskFemConstraintForce.ui new file mode 100644 index 0000000000..83536403d2 --- /dev/null +++ b/src/Mod/Fem/Gui/TaskFemConstraintForce.ui @@ -0,0 +1,89 @@ + + + TaskFemConstraintForce + + + + 0 + 0 + 257 + 233 + + + + Form + + + + + + Add reference + + + + + + + + + + + + Load [N] + + + + + + + -99999.000000000000000 + + + 99999.000000000000000 + + + 500.000000000000000 + + + + + + + + + + + Direction + + + + + + + + + + + + Reverse direction + + + + + + + Qt::Vertical + + + + 17 + 56 + + + + + + + + + diff --git a/src/Mod/Fem/Gui/TaskFemConstraintGear.cpp b/src/Mod/Fem/Gui/TaskFemConstraintGear.cpp new file mode 100644 index 0000000000..4f4f3adf9b --- /dev/null +++ b/src/Mod/Fem/Gui/TaskFemConstraintGear.cpp @@ -0,0 +1,315 @@ +/*************************************************************************** + * Copyright (c) 2013 Jan Rheinländer * + * * + * 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 +#endif + +#include "ui_TaskFemConstraintBearing.h" +#include "TaskFemConstraintGear.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace FemGui; +using namespace Gui; + +/* TRANSLATOR FemGui::TaskFemConstraintGear */ + +TaskFemConstraintGear::TaskFemConstraintGear(ViewProviderFemConstraint *ConstraintView,QWidget *parent, const char *pixmapname) + : TaskFemConstraintBearing(ConstraintView, parent, pixmapname) +{ + connect(ui->spinDiameter, SIGNAL(valueChanged(double)), + this, SLOT(onDiameterChanged(double))); + connect(ui->spinForce, SIGNAL(valueChanged(double)), + this, SLOT(onForceChanged(double))); + connect(ui->spinForceAngle, SIGNAL(valueChanged(double)), + this, SLOT(onForceAngleChanged(double))); + connect(ui->buttonDirection, SIGNAL(pressed()), + this, SLOT(onButtonDirection())); + connect(ui->checkReversed, SIGNAL(toggled(bool)), + this, SLOT(onCheckReversed(bool))); + + // Temporarily prevent unnecessary feature recomputes + ui->spinDiameter->blockSignals(true); + ui->spinForce->blockSignals(true); + ui->spinForceAngle->blockSignals(true); + ui->checkReversed->blockSignals(true); + + // Get the feature data + Fem::ConstraintGear* pcConstraint = static_cast(ConstraintView->getObject()); + double dia = pcConstraint->Diameter.getValue(); + double force = pcConstraint->Force.getValue(); + double angle = pcConstraint->ForceAngle.getValue(); + std::vector dirStrings = pcConstraint->Direction.getSubValues(); + QString dir; + if (!dirStrings.empty()) + dir = makeRefText(pcConstraint->Direction.getValue(), dirStrings.front()); + bool reversed = pcConstraint->Reversed.getValue(); + + // Fill data into dialog elements + ui->spinDiameter->setMinimum(0); + ui->spinDiameter->setMaximum(FLOAT_MAX); + ui->spinDiameter->setValue(dia); + ui->spinForce->setMinimum(0); + ui->spinForce->setMaximum(FLOAT_MAX); + ui->spinForce->setValue(force); + ui->spinForceAngle->setMinimum(-360); + ui->spinForceAngle->setMaximum(360); + ui->spinForceAngle->setValue(angle); + ui->lineDirection->setText(dir); + ui->checkReversed->setChecked(reversed); + + // Adjust ui + ui->labelDiameter->setVisible(true); + ui->spinDiameter->setVisible(true); + ui->labelForce->setVisible(true); + ui->spinForce->setVisible(true); + ui->labelForceAngle->setVisible(true); + ui->spinForceAngle->setVisible(true); + ui->buttonDirection->setVisible(true); + ui->lineDirection->setVisible(true); + ui->checkReversed->setVisible(true); + ui->checkAxial->setVisible(false); + + ui->spinDiameter->blockSignals(false); + ui->spinForce->blockSignals(false); + ui->spinForceAngle->blockSignals(false); + ui->checkReversed->blockSignals(false); +} + +void TaskFemConstraintGear::onSelectionChanged(const Gui::SelectionChanges& msg) +{ + TaskFemConstraintBearing::onSelectionChanged(msg); + + if (msg.Type == Gui::SelectionChanges::AddSelection) { + // Don't allow selection in other document + if (strcmp(msg.pDocName, ConstraintView->getObject()->getDocument()->getName()) != 0) + return; + + if (!msg.pSubName || msg.pSubName[0] == '\0') + return; + std::string subName(msg.pSubName); + + if (selectionMode == selnone) + return; + + std::vector references(1,subName); + Fem::ConstraintGear* pcConstraint = static_cast(ConstraintView->getObject()); + App::DocumentObject* obj = ConstraintView->getObject()->getDocument()->getObject(msg.pObjectName); + Part::Feature* feat = static_cast(obj); + TopoDS_Shape ref = feat->Shape.getShape().getSubShape(subName.c_str()); + + if (selectionMode == seldir) { + if (subName.substr(0,4) == "Face") { + BRepAdaptor_Surface surface(TopoDS::Face(ref)); + if (surface.GetType() != GeomAbs_Plane) { + QMessageBox::warning(this, tr("Selection error"), tr("Only planar faces can be picked")); + return; + } + } else if (subName.substr(0,4) == "Edge") { + BRepAdaptor_Curve line(TopoDS::Edge(ref)); + if (line.GetType() != GeomAbs_Line) { + QMessageBox::warning(this, tr("Selection error"), tr("Only linear edges can be picked")); + return; + } + } else { + QMessageBox::warning(this, tr("Selection error"), tr("Only faces and edges can be picked")); + return; + } + pcConstraint->Direction.setValue(obj, references); + ui->lineDirection->setText(makeRefText(obj, subName)); + + // Turn off direction selection mode + onButtonDirection(false); + } + + Gui::Selection().clearSelection(); + } +} + +void TaskFemConstraintGear::onDiameterChanged(double l) +{ + Fem::ConstraintGear* pcConstraint = static_cast(ConstraintView->getObject()); + pcConstraint->Diameter.setValue((float)l); +} + +void TaskFemConstraintGear::onForceChanged(double f) +{ + Fem::ConstraintGear* pcConstraint = static_cast(ConstraintView->getObject()); + pcConstraint->Force.setValue((float)f); +} + +void TaskFemConstraintGear::onForceAngleChanged(double a) +{ + Fem::ConstraintGear* pcConstraint = static_cast(ConstraintView->getObject()); + pcConstraint->ForceAngle.setValue((float)a); +} + +void TaskFemConstraintGear::onButtonDirection(const bool pressed) { + if (pressed) { + selectionMode = seldir; + } else { + selectionMode = selnone; + } + ui->buttonDirection->setChecked(pressed); + Gui::Selection().clearSelection(); +} + +void TaskFemConstraintGear::onCheckReversed(const bool pressed) +{ + Fem::ConstraintGear* pcConstraint = static_cast(ConstraintView->getObject()); + pcConstraint->Reversed.setValue(pressed); +} + +double TaskFemConstraintGear::getForce(void) const +{ + return ui->spinForce->value(); +} + +double TaskFemConstraintGear::getForceAngle(void) const +{ + return ui->spinForceAngle->value(); +} + +const std::string TaskFemConstraintGear::getDirectionName(void) const +{ + std::string dir = ui->lineDirection->text().toStdString(); + if (dir.empty()) + return ""; + + int pos = dir.find_last_of(":"); + return dir.substr(0, pos).c_str(); +} + +const std::string TaskFemConstraintGear::getDirectionObject(void) const +{ + std::string dir = ui->lineDirection->text().toStdString(); + if (dir.empty()) + return ""; + + int pos = dir.find_last_of(":"); + return dir.substr(pos+1).c_str(); +} + +bool TaskFemConstraintGear::getReverse() const +{ + return ui->checkReversed->isChecked(); +} + +double TaskFemConstraintGear::getDiameter(void) const +{ + return ui->spinDiameter->value(); +} + +void TaskFemConstraintGear::changeEvent(QEvent *e) +{ + TaskBox::changeEvent(e); + if (e->type() == QEvent::LanguageChange) { + ui->spinDiameter->blockSignals(true); + ui->spinForce->blockSignals(true); + ui->spinForceAngle->blockSignals(true); + ui->checkReversed->blockSignals(true); + ui->retranslateUi(proxy); + ui->spinDiameter->blockSignals(false); + ui->spinForce->blockSignals(false); + ui->spinForceAngle->blockSignals(true); + ui->checkReversed->blockSignals(false); + } +} + +//************************************************************************** +//************************************************************************** +// TaskDialog +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +TaskDlgFemConstraintGear::TaskDlgFemConstraintGear(ViewProviderFemConstraintGear *ConstraintView) +{ + this->ConstraintView = ConstraintView; + assert(ConstraintView); + this->parameter = new TaskFemConstraintGear(ConstraintView, 0, "Fem_ConstraintGear"); + + Content.push_back(parameter); +} + +//==== calls from the TaskView =============================================================== + +bool TaskDlgFemConstraintGear::accept() +{ + std::string name = ConstraintView->getObject()->getNameInDocument(); + const TaskFemConstraintGear* parameterGear = static_cast(parameter); + + try { + //Gui::Command::openCommand("FEM force constraint changed"); + std::string dirname = parameterGear->getDirectionName().data(); + std::string dirobj = parameterGear->getDirectionObject().data(); + + if (!dirname.empty()) { + QString buf = QString::fromUtf8("(App.ActiveDocument.%1,[\"%2\"])"); + buf = buf.arg(QString::fromStdString(dirname)); + buf = buf.arg(QString::fromStdString(dirobj)); + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Direction = %s", name.c_str(), buf.toStdString().c_str()); + } else { + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Direction = None", name.c_str()); + } + + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Reversed = %s", name.c_str(), parameterGear->getReverse() ? "True" : "False"); + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Diameter = %f",name.c_str(), parameterGear->getDiameter()); + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Force = %f",name.c_str(), parameterGear->getForce()); + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.ForceAngle = %f",name.c_str(), parameterGear->getForceAngle()); + } + catch (const Base::Exception& e) { + QMessageBox::warning(parameter, tr("Input error"), QString::fromAscii(e.what())); + return false; + } + + return TaskDlgFemConstraintBearing::accept(); +} + +#include "moc_TaskFemConstraintGear.cpp" diff --git a/src/Mod/Fem/Gui/TaskFemConstraintGear.h b/src/Mod/Fem/Gui/TaskFemConstraintGear.h new file mode 100644 index 0000000000..048d9d6963 --- /dev/null +++ b/src/Mod/Fem/Gui/TaskFemConstraintGear.h @@ -0,0 +1,79 @@ +/*************************************************************************** + * Copyright (c) 2013 Jan Rheinländer * + * * + * 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_TaskFemConstraintGear_H +#define GUI_TASKVIEW_TaskFemConstraintGear_H + +#include +#include +#include + +#include "TaskFemConstraintBearing.h" +#include "ViewProviderFemConstraintGear.h" + +namespace FemGui { + +class TaskFemConstraintGear : public TaskFemConstraintBearing +{ + Q_OBJECT + +public: + TaskFemConstraintGear(ViewProviderFemConstraint *ConstraintView,QWidget *parent = 0, + const char* pixmapname = "Fem_ConstraintGear"); + + double getDiameter(void) const; + double getForce(void) const; + double getForceAngle(void) const; + const std::string getDirectionName(void) const; + const std::string getDirectionObject(void) const; + bool getReverse(void) const; + +private Q_SLOTS: + void onDiameterChanged(double dia); + void onForceChanged(double force); + void onForceAngleChanged(double angle); + void onButtonDirection(const bool pressed = true); + void onCheckReversed(bool); + +protected: + virtual void changeEvent(QEvent *e); + virtual void onSelectionChanged(const Gui::SelectionChanges& msg); +}; + +/// simulation dialog for the TaskView +class TaskDlgFemConstraintGear : public TaskDlgFemConstraintBearing +{ + Q_OBJECT + +public: + TaskDlgFemConstraintGear() {} + TaskDlgFemConstraintGear(ViewProviderFemConstraintGear *ConstraintView); + + /// is called by the framework if the dialog is accepted (Ok) + virtual bool accept(); + +}; + +} //namespace FemGui + +#endif // GUI_TASKVIEW_TaskFemConstraintGear_H diff --git a/src/Mod/Fem/Gui/TaskFemConstraintPulley.cpp b/src/Mod/Fem/Gui/TaskFemConstraintPulley.cpp new file mode 100644 index 0000000000..e6d7c55ce1 --- /dev/null +++ b/src/Mod/Fem/Gui/TaskFemConstraintPulley.cpp @@ -0,0 +1,210 @@ +/*************************************************************************** + * Copyright (c) 2013 Jan Rheinländer * + * * + * 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 "ui_TaskFemConstraintBearing.h" +#include "TaskFemConstraintPulley.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace FemGui; +using namespace Gui; + +/* TRANSLATOR FemGui::TaskFemConstraintPulley */ + +TaskFemConstraintPulley::TaskFemConstraintPulley(ViewProviderFemConstraintPulley *ConstraintView,QWidget *parent) + : TaskFemConstraintGear(ConstraintView, parent, "Fem_ConstraintPulley") +{ + connect(ui->spinOtherDiameter, SIGNAL(valueChanged(double)), + this, SLOT(onOtherDiameterChanged(double))); + connect(ui->spinCenterDistance, SIGNAL(valueChanged(double)), + this, SLOT(onCenterDistanceChanged(double))); + connect(ui->checkIsDriven, SIGNAL(toggled(bool)), + this, SLOT(onCheckIsDriven(bool))); + connect(ui->spinTensionForce, SIGNAL(valueChanged(double)), + this, SLOT(onTensionForceChanged(double))); + + // Temporarily prevent unnecessary feature recomputes + ui->spinOtherDiameter->blockSignals(true); + ui->spinCenterDistance->blockSignals(true); + ui->checkIsDriven->blockSignals(true); + ui->spinTensionForce->blockSignals(true); + + // Get the feature data + Fem::ConstraintPulley* pcConstraint = static_cast(ConstraintView->getObject()); + double otherdia = pcConstraint->OtherDiameter.getValue(); + double centerdist = pcConstraint->CenterDistance.getValue(); + bool isdriven = pcConstraint->IsDriven.getValue(); + double tensionforce = pcConstraint->TensionForce.getValue(); + + // Fill data into dialog elements + ui->spinOtherDiameter->setMinimum(0); + ui->spinOtherDiameter->setMaximum(FLOAT_MAX); + ui->spinOtherDiameter->setValue(otherdia); + ui->spinCenterDistance->setMinimum(0); + ui->spinCenterDistance->setMaximum(FLOAT_MAX); + ui->spinCenterDistance->setValue(centerdist); + ui->checkIsDriven->setChecked(isdriven); + ui->spinForce->setMinimum(-FLOAT_MAX); + ui->spinTensionForce->setMinimum(0); + ui->spinTensionForce->setMaximum(FLOAT_MAX); + ui->spinTensionForce->setValue(tensionforce); + + // Adjust ui + ui->buttonDirection->setVisible(false); + ui->lineDirection->setVisible(false); + ui->checkReversed->setVisible(false); + ui->labelDiameter->setText(tr("Pulley diameter")); + ui->labelForce->setText(tr("Torque [Nm]")); + ui->labelOtherDiameter->setVisible(true); + ui->spinOtherDiameter->setVisible(true); + ui->labelCenterDistance->setVisible(true); + ui->spinCenterDistance->setVisible(true); + ui->checkIsDriven->setVisible(true); + ui->labelTensionForce->setVisible(true); + ui->spinTensionForce->setVisible(true); + + ui->spinOtherDiameter->blockSignals(false); + ui->spinCenterDistance->blockSignals(false); + ui->checkIsDriven->blockSignals(false); + ui->spinTensionForce->blockSignals(false); +} + +void TaskFemConstraintPulley::onOtherDiameterChanged(double l) +{ + Fem::ConstraintPulley* pcConstraint = static_cast(ConstraintView->getObject()); + pcConstraint->OtherDiameter.setValue((float)l); +} + +void TaskFemConstraintPulley::onCenterDistanceChanged(double l) +{ + Fem::ConstraintPulley* pcConstraint = static_cast(ConstraintView->getObject()); + pcConstraint->CenterDistance.setValue((float)l); +} + +void TaskFemConstraintPulley::onTensionForceChanged(double force) +{ + Fem::ConstraintPulley* pcConstraint = static_cast(ConstraintView->getObject()); + pcConstraint->TensionForce.setValue((float)force); +} + +void TaskFemConstraintPulley::onCheckIsDriven(const bool pressed) +{ + Fem::ConstraintPulley* pcConstraint = static_cast(ConstraintView->getObject()); + pcConstraint->IsDriven.setValue(pressed); +} + +double TaskFemConstraintPulley::getTorque(void) const +{ + return ui->spinForce->value(); +} + +double TaskFemConstraintPulley::getTensionForce(void) const +{ + return ui->spinTensionForce->value(); +} + +bool TaskFemConstraintPulley::getIsDriven() const +{ + return ui->checkIsDriven->isChecked(); +} + +double TaskFemConstraintPulley::getOtherDiameter(void) const +{ + return ui->spinOtherDiameter->value(); +} + +double TaskFemConstraintPulley::getCenterDistance(void) const +{ + return ui->spinCenterDistance->value(); +} + +void TaskFemConstraintPulley::changeEvent(QEvent *e) +{ + TaskBox::changeEvent(e); + if (e->type() == QEvent::LanguageChange) { + ui->spinOtherDiameter->blockSignals(true); + ui->spinCenterDistance->blockSignals(true); + ui->checkIsDriven->blockSignals(true); + ui->spinTensionForce->blockSignals(true); + ui->retranslateUi(proxy); + ui->spinOtherDiameter->blockSignals(false); + ui->spinCenterDistance->blockSignals(false); + ui->checkIsDriven->blockSignals(false); + ui->spinTensionForce->blockSignals(false); + } +} + +//************************************************************************** +//************************************************************************** +// TaskDialog +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +TaskDlgFemConstraintPulley::TaskDlgFemConstraintPulley(ViewProviderFemConstraintPulley *ConstraintView) +{ + this->ConstraintView = ConstraintView; + assert(ConstraintView); + this->parameter = new TaskFemConstraintPulley(ConstraintView);; + + Content.push_back(parameter); +} + +//==== calls from the TaskView =============================================================== + +bool TaskDlgFemConstraintPulley::accept() +{ + std::string name = ConstraintView->getObject()->getNameInDocument(); + const TaskFemConstraintPulley* parameterPulley = static_cast(parameter); + + try { + //Gui::Command::openCommand("FEM pulley constraint changed"); + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.OtherDiameter = %f",name.c_str(), parameterPulley->getOtherDiameter()); + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.CenterDistance = %f",name.c_str(), parameterPulley->getCenterDistance()); + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.IsDriven = %s",name.c_str(), parameterPulley->getIsDriven() ? "True" : "False"); + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.TensionForce = %f",name.c_str(), parameterPulley->getTensionForce()); + } + catch (const Base::Exception& e) { + QMessageBox::warning(parameter, tr("Input error"), QString::fromAscii(e.what())); + return false; + } + + return TaskDlgFemConstraintGear::accept(); +} + +#include "moc_TaskFemConstraintPulley.cpp" diff --git a/src/Mod/Fem/Gui/TaskFemConstraintPulley.h b/src/Mod/Fem/Gui/TaskFemConstraintPulley.h new file mode 100644 index 0000000000..f2b92c5b9e --- /dev/null +++ b/src/Mod/Fem/Gui/TaskFemConstraintPulley.h @@ -0,0 +1,74 @@ +/*************************************************************************** + * Copyright (c) 2013 Jan Rheinländer * + * * + * 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_TaskFemConstraintPulley_H +#define GUI_TASKVIEW_TaskFemConstraintPulley_H + +#include +#include +#include + +#include "TaskFemConstraintGear.h" +#include "ViewProviderFemConstraintPulley.h" + +namespace FemGui { + +class TaskFemConstraintPulley : public TaskFemConstraintGear +{ + Q_OBJECT + +public: + TaskFemConstraintPulley(ViewProviderFemConstraintPulley *ConstraintView,QWidget *parent = 0); + + double getOtherDiameter(void) const; + double getCenterDistance(void) const; + double getTensionForce(void) const; + double getTorque(void) const; + bool getIsDriven(void) const; + +private Q_SLOTS: + void onOtherDiameterChanged(double dia); + void onCenterDistanceChanged(double dia); + void onTensionForceChanged(double force); + void onCheckIsDriven(bool); + +protected: + virtual void changeEvent(QEvent *e); +}; + +/// simulation dialog for the TaskView +class TaskDlgFemConstraintPulley : public TaskDlgFemConstraintGear +{ + Q_OBJECT + +public: + TaskDlgFemConstraintPulley(ViewProviderFemConstraintPulley *ConstraintView); + + /// is called by the framework if the dialog is accepted (Ok) + virtual bool accept(); + +}; + +} //namespace FemGui + +#endif // GUI_TASKVIEW_TaskFemConstraintPulley_H diff --git a/src/Mod/Fem/Gui/ViewProviderFemConstraint.cpp b/src/Mod/Fem/Gui/ViewProviderFemConstraint.cpp new file mode 100644 index 0000000000..0668c05f89 --- /dev/null +++ b/src/Mod/Fem/Gui/ViewProviderFemConstraint.cpp @@ -0,0 +1,439 @@ +/*************************************************************************** + * Copyright (c) 2013 Jan Rheinländer * + * * + * 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 +#endif + +#include "ViewProviderFemConstraint.h" +#include "TaskFemConstraint.h" + +#include "Gui/Control.h" +#include "Gui/MainWindow.h" +#include "Gui/Command.h" +#include "Gui/Application.h" +#include "Gui/Document.h" + +#include + +using namespace FemGui; + +PROPERTY_SOURCE(FemGui::ViewProviderFemConstraint, Gui::ViewProviderDocumentObject) + + +ViewProviderFemConstraint::ViewProviderFemConstraint() +{ + ADD_PROPERTY(TextColor,(0.0f,0.0f,0.0f)); + ADD_PROPERTY(FaceColor,(1.0f,0.0f,0.2f)); + ADD_PROPERTY(ShapeColor,(1.0f,0.0f,0.2f)); + ADD_PROPERTY(FontSize,(18)); + ADD_PROPERTY(DistFactor,(1.0)); + ADD_PROPERTY(Mirror,(false)); + + pFont = new SoFontStyle(); + pFont->ref(); + pLabel = new SoText2(); + pLabel->ref(); + pTextColor = new SoBaseColor(); + pTextColor->ref(); + + pMaterials = new SoMaterial(); + pMaterials->ref(); + pMaterials->diffuseColor.setValue(1.0f, 0.0f, 0.2f); + pMaterials->transparency.setValue(0.1); + //pMaterials->ambientColor.setValue(0.8f, 0.8f, 0.8f); + //pMaterials->shininess.setValue(1.0); + + pShapeSep = new SoSeparator(); + pShapeSep->ref(); + + TextColor.touch(); + FontSize.touch(); + FaceColor.touch(); + + wizardWidget = NULL; + wizardSubLayout = NULL; + constraintDialog = NULL; +} + +ViewProviderFemConstraint::~ViewProviderFemConstraint() +{ + pFont->unref(); + pLabel->unref(); + pTextColor->unref(); + pMaterials->unref(); + pShapeSep->unref(); +} + +void ViewProviderFemConstraint::attach(App::DocumentObject* pcObject) +{ + ViewProviderDocumentObject::attach(pcObject); + + SoPickStyle* ps = new SoPickStyle(); + ps->style = SoPickStyle::UNPICKABLE; + + SoSeparator* sep = new SoSeparator(); + SoShapeHints* hints = new SoShapeHints(); + hints->shapeType.setValue(SoShapeHints::UNKNOWN_SHAPE_TYPE); + hints->vertexOrdering.setValue(SoShapeHints::COUNTERCLOCKWISE); + sep->addChild(ps); + sep->addChild(hints); + sep->addChild(pMaterials); + sep->addChild(pShapeSep); + addDisplayMaskMode(sep, "Base"); +} + +std::vector ViewProviderFemConstraint::getDisplayModes(void) const +{ + // add modes + std::vector StrList; + StrList.push_back("Base"); + return StrList; +} + +void ViewProviderFemConstraint::setDisplayMode(const char* ModeName) +{ + if (strcmp(ModeName, "Base") == 0) + setDisplayMaskMode("Base"); + ViewProviderDocumentObject::setDisplayMode(ModeName); +} + +std::vector ViewProviderFemConstraint::claimChildren(void)const +{ + return std::vector(); +} + +void ViewProviderFemConstraint::setupContextMenu(QMenu* menu, QObject* receiver, const char* member) +{ + QAction* act; + act = menu->addAction(QObject::tr("Edit constraint"), receiver, member); + act->setData(QVariant((int)ViewProvider::Default)); + ViewProviderDocumentObject::setupContextMenu(menu, receiver, member); +} + +void ViewProviderFemConstraint::onChanged(const App::Property* prop) +{ + if (prop == &Mirror || prop == &DistFactor) { + updateData(prop); + } + else if (prop == &TextColor) { + const App::Color& c = TextColor.getValue(); + pTextColor->rgb.setValue(c.r,c.g,c.b); + } + else if (prop == &FaceColor) { + const App::Color& c = FaceColor.getValue(); + pMaterials->diffuseColor.setValue(c.r,c.g,c.b); + } + else if (prop == &FontSize) { + pFont->size = FontSize.getValue(); + } + else { + ViewProviderDocumentObject::onChanged(prop); + } +} + +bool ViewProviderFemConstraint::setEdit(int ModNum) +{ + return Gui::ViewProviderGeometryObject::setEdit(ModNum); +} + +void ViewProviderFemConstraint::unsetEdit(int ModNum) +{ + if ((wizardWidget != NULL) && (wizardSubLayout != NULL) && (constraintDialog != NULL)) { + wizardWidget = NULL; + wizardSubLayout = NULL; + delete constraintDialog; + constraintDialog = NULL; + + // Notify the Shaft Wizard that we have finished editing + // See WizardShaft.py on why we do it this way + Gui::Command::runCommand(Gui::Command::Doc, "Gui.runCommand('PartDesign_WizardShaftCallBack')"); + } else { + if (ModNum == ViewProvider::Default) { + // when pressing ESC make sure to close the dialog + Gui::Control().closeDialog(); + } + else { + ViewProviderDocumentObject::unsetEdit(ModNum); + } + } +} +/* +// Create a local coordinate system with the z-axis given in dir +void getLocalCoordinateSystem(const SbVec3f& z, SbVec3f& y, SbVec3f& x) +{ + // Find the y axis in an arbitrary direction, normal to z + // Conditions: + // y1 * z1 + y2 * z2 + y3 * z3 = |y| |z| cos(90°) = 0 + // |y| = sqrt(y1^2 + y2^2 + y3^2) = 1 + float z1, z2, z3; + z.getValue(z1, z2, z3); + float y1, y2, y3; + if (fabs(z1) > Precision::Confusion()) { + // Choose: y3 = 0 + // Solution: + // y1 * z1 + y2 * z2 = 0 + // y1 = - z2/z1 y2 + // sqrt(z2^2/z1^2 y2^2 + y2^2) = 1 + // y2^2 ( 1 + z2^2/z1^2)) = +-1 -> choose +1 otherwise no solution + // y2 = +- sqrt(1 / (1 + z2^2/z1^2)) + y3 = 0; + y2 = sqrt(1 / (1 + z2*z2 / (z1*z1))); + y1 = -z2/z1 * y2; + // Note: result might be (0, 1, 0) + } else if (fabs(z2) > Precision::Confusion()) { + // Given: z1 = 0 + // Choose: y1 = 0 + // Solution: + // y2 * z2 + y3 * z3 = 0 + // y2 = - z3/z2 y3 + // sqrt(z3^2/z2^2 y3^3 + y3^2) = 1 + // y3^2 (1 + z3^2/z2^2)) = +1 + // y3 = +- sqrt(1 / (1 + z3^2/z2^2)) + y1 = 0; + y3 = sqrt(1 / (1 + z3*z3 / (z2*z2))); + y2 = -z3/z2 * y3; + // Note: result might be (0, 0, 1) + } else if (fabs(z3) > Precision::Confusion()) { + // Given: z1 = z2 = 0 + // Choose the remaining possible axis + y1 = 1; + y2 = 0; + y3 = 0; + } + + y = SbVec3f(y1, y2, y3); + x = y.cross(z); +} +*/ +#define PLACEMENT_CHILDREN 2 + +void ViewProviderFemConstraint::createPlacement(SoSeparator* sep, const SbVec3f &base, const SbRotation &r) +{ + SoTranslation* trans = new SoTranslation(); + trans->ref(); + trans->translation.setValue(base); + sep->addChild(trans); + SoRotation* rot = new SoRotation(); + rot->ref(); + rot->rotation.setValue(r); + sep->addChild(rot); +} + +void ViewProviderFemConstraint::updatePlacement(const SoSeparator* sep, const int idx, const SbVec3f &base, const SbRotation &r) +{ + SoTranslation* trans = static_cast(sep->getChild(idx)); + trans->translation.setValue(base); + SoRotation* rot = static_cast(sep->getChild(idx+1)); + rot->rotation.setValue(r); +} + +#define CONE_CHILDREN 2 + +void ViewProviderFemConstraint::createCone(SoSeparator* sep, const double height, const double radius) +{ + // Adjust cone so that the tip is on base + SoTranslation* trans = new SoTranslation(); + trans->ref(); + trans->translation.setValue(SbVec3f(0,-height/2,0)); + sep->addChild(trans); + SoCone* cone = new SoCone(); + cone->ref(); + cone->height.setValue(height); + cone->bottomRadius.setValue(radius); + sep->addChild(cone); +} + +SoSeparator* ViewProviderFemConstraint::createCone(const double height, const double radius) +{ + // Create a new cone node + SoSeparator* sep = new SoSeparator(); + createCone(sep, height, radius); + return sep; +} + +void ViewProviderFemConstraint::updateCone(const SoNode* node, const int idx, const double height, const double radius) +{ + const SoSeparator* sep = static_cast(node); + SoTranslation* trans = static_cast(sep->getChild(idx)); + trans->translation.setValue(SbVec3f(0,-height/2,0)); + SoCone* cone = static_cast(sep->getChild(idx+1)); + cone->height.setValue(height); + cone->bottomRadius.setValue(radius); +} + +#define CYLINDER_CHILDREN 1 + +void ViewProviderFemConstraint::createCylinder(SoSeparator* sep, const double height, const double radius) +{ + SoCylinder* cyl = new SoCylinder(); + cyl->ref(); + cyl->height.setValue(height); + cyl->radius.setValue(radius); + sep->addChild(cyl); +} + +SoSeparator* ViewProviderFemConstraint::createCylinder(const double height, const double radius) +{ + // Create a new cylinder node + SoSeparator* sep = new SoSeparator(); + createCylinder(sep, height, radius); + return sep; +} + +void ViewProviderFemConstraint::updateCylinder(const SoNode* node, const int idx, const double height, const double radius) +{ + const SoSeparator* sep = static_cast(node); + SoCylinder* cyl = static_cast(sep->getChild(idx)); + cyl->height.setValue(height); + cyl->radius.setValue(radius); +} + +#define CUBE_CHILDREN 1 + +void ViewProviderFemConstraint::createCube(SoSeparator* sep, const double width, const double length, const double height) +{ + SoCube* cube = new SoCube(); + cube->ref(); + cube->width.setValue(width); + cube->depth.setValue(length); + cube->height.setValue(height); + sep->addChild(cube); +} + +SoSeparator* ViewProviderFemConstraint::createCube(const double width, const double length, const double height) +{ + SoSeparator* sep = new SoSeparator(); + createCube(sep, width, length, height); + return sep; +} + +void ViewProviderFemConstraint::updateCube(const SoNode* node, const int idx, const double width, const double length, const double height) +{ + const SoSeparator* sep = static_cast(node); + SoCube* cube = static_cast(sep->getChild(idx)); + cube->width.setValue(width); + cube->depth.setValue(length); + cube->height.setValue(height); +} + +#define ARROW_CHILDREN (CONE_CHILDREN + PLACEMENT_CHILDREN + CYLINDER_CHILDREN) + +void ViewProviderFemConstraint::createArrow(SoSeparator* sep, const double length, const double radius) +{ + createCone(sep, radius, radius/2); + createPlacement(sep, SbVec3f(0, -radius/2-(length-radius)/2, 0), SbRotation()); + createCylinder(sep, length-radius, radius/5); +} + +SoSeparator* ViewProviderFemConstraint::createArrow(const double length, const double radius) +{ + SoSeparator* sep = new SoSeparator(); + createArrow(sep, length, radius); + return sep; +} + +void ViewProviderFemConstraint::updateArrow(const SoNode* node, const int idx, const double length, const double radius) +{ + const SoSeparator* sep = static_cast(node); + updateCone(sep, idx, radius, radius/2); + updatePlacement(sep, idx+CONE_CHILDREN, SbVec3f(0, -radius/2-(length-radius)/2, 0), SbRotation()); + updateCylinder(sep, idx+CONE_CHILDREN+PLACEMENT_CHILDREN, length-radius, radius/5); +} + +#define FIXED_CHILDREN (CONE_CHILDREN + PLACEMENT_CHILDREN + CUBE_CHILDREN) + +void ViewProviderFemConstraint::createFixed(SoSeparator* sep, const double height, const double width, const bool gap) +{ + createCone(sep, height-width/4, height-width/4); + createPlacement(sep, SbVec3f(0, -(height-width/4)/2-width/8 - (gap ? 1.0 : 0.1) * width/8, 0), SbRotation()); + createCube(sep, width, width, width/4); +} + +SoSeparator* ViewProviderFemConstraint::createFixed(const double height, const double width, const bool gap) +{ + SoSeparator* sep = new SoSeparator(); + createFixed(sep, height, width, gap); + return sep; +} + +void ViewProviderFemConstraint::updateFixed(const SoNode* node, const int idx, const double height, const double width, const bool gap) +{ + const SoSeparator* sep = static_cast(node); + updateCone(sep, idx, height-width/4, height-width/4); + updatePlacement(sep, idx+CONE_CHILDREN, SbVec3f(0, -(height-width/4)/2-width/8 - (gap ? 1.0 : 0.0) * width/8, 0), SbRotation()); + updateCube(sep, idx+CONE_CHILDREN+PLACEMENT_CHILDREN, width, width, width/4); +} + +QObject* ViewProviderFemConstraint::findChildByName(const QObject* parent, const QString& name) +{ + for (QObjectList::const_iterator o = parent->children().begin(); o != parent->children().end(); o++) { + if ((*o)->objectName() == name) + return *o; + if (!(*o)->children().empty()) { + QObject* result = findChildByName(*o, name); + if (result != NULL) + return result; + } + } + + return NULL; +} + +void ViewProviderFemConstraint::checkForWizard() +{ + wizardWidget= NULL; + wizardSubLayout = NULL; + Gui::MainWindow* mw = Gui::getMainWindow(); + if (mw == NULL) return; + QDockWidget* dw = mw->findChild(QObject::tr("Combo View")); + if (dw == NULL) return; + QWidget* cw = dw->findChild(QObject::tr("Combo View")); + if (cw == NULL) return; + QTabWidget* tw = cw->findChild(QObject::tr("combiTab")); + if (tw == NULL) return; + QStackedWidget* sw = tw->findChild(QObject::tr("qt_tabwidget_stackedwidget")); + if (sw == NULL) return; + QScrollArea* sa = sw->findChild(); + if (sa== NULL) return; + QWidget* wd = sa->widget(); // This is the reason why we cannot use findChildByName() right away!!! + if (wd == NULL) return; + QObject* wiz = findChildByName(wd, QObject::tr("ShaftWizard")); // FIXME: Actually, we don't want to translate this... + if (wiz != NULL) + wizardWidget = static_cast(wiz); + wizardSubLayout = wiz->findChild(QObject::tr("ShaftWizardLayout")); +} diff --git a/src/Mod/Fem/Gui/ViewProviderFemConstraint.h b/src/Mod/Fem/Gui/ViewProviderFemConstraint.h new file mode 100644 index 0000000000..82e1803d5b --- /dev/null +++ b/src/Mod/Fem/Gui/ViewProviderFemConstraint.h @@ -0,0 +1,124 @@ +/*************************************************************************** + * Copyright (c) 2013 Jan Rheinländer * + * * + * 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_VIEWPROVIDERFEMCONSTRAINT_H +#define GUI_VIEWPROVIDERFEMCONSTRAINT_H + +#include + +#include "Gui/ViewProviderGeometryObject.h" +#include +#include +#include + +class SoFontStyle; +class SoText2; +class SoBaseColor; +class SoTranslation; +class SbRotation; +class SoMaterial; + +namespace Gui { +class View3DInventorViewer; + namespace TaskView { + class TaskDialog; + } +} + +namespace FemGui +{ + +class TaskFemConstraint; + +class FemGuiExport ViewProviderFemConstraint : public Gui::ViewProviderGeometryObject +{ + PROPERTY_HEADER(FemGui::ViewProviderFemConstraint); + +public: + /// Constructor + ViewProviderFemConstraint(void); + virtual ~ViewProviderFemConstraint(); + + // Display properties + App::PropertyColor TextColor; + App::PropertyColor FaceColor; + App::PropertyColor ShapeColor; + App::PropertyInteger FontSize; + App::PropertyFloat DistFactor; + App::PropertyBool Mirror; + + void attach(App::DocumentObject *); + virtual void updateData(const App::Property* prop) { Gui::ViewProviderGeometryObject::updateData(prop); } + std::vector getDisplayModes(void) const; + void setDisplayMode(const char* ModeName); + + std::vector claimChildren(void)const; + void setupContextMenu(QMenu*, QObject*, const char*); + +protected: + void onChanged(const App::Property* prop); + virtual bool setEdit(int ModNum); + virtual void unsetEdit(int ModNum); + + static void createPlacement(SoSeparator* sep, const SbVec3f &base, const SbRotation &r); + static void updatePlacement(const SoSeparator* sep, const int idx, const SbVec3f &base, const SbRotation &r); + static void createCone(SoSeparator* sep, const double height, const double radius); + static SoSeparator* createCone(const double height, const double radius); + static void updateCone(const SoNode* node, const int idx, const double height, const double radius); + static void createCylinder(SoSeparator* sep, const double height, const double radius); + static SoSeparator* createCylinder(const double height, const double radius); + static void updateCylinder(const SoNode* node, const int idx, const double height, const double radius); + static void createCube(SoSeparator* sep, const double width, const double length, const double height); + static SoSeparator* createCube(const double width, const double length, const double height); + static void updateCube(const SoNode* node, const int idx, const double width, const double length, const double height); + static void createArrow(SoSeparator* sep, const double length, const double radius); + static SoSeparator* createArrow(const double length, const double radius); + static void updateArrow(const SoNode* node, const int idx, const double length, const double radius); + static void createFixed(SoSeparator* sep, const double height, const double width, const bool gap = false); + static SoSeparator* createFixed(const double height, const double width, const bool gap = false); + static void updateFixed(const SoNode* node, const int idx, const double height, const double width, const bool gap = false); + +private: + SoFontStyle * pFont; + SoText2 * pLabel; + SoBaseColor * pTextColor; + SoMaterial * pMaterials; + +protected: + SoSeparator * pShapeSep; + + // Shaft design wizard integration +protected: + friend class TaskFemConstraint; + QVBoxLayout* wizardWidget; + QVBoxLayout* wizardSubLayout; + TaskFemConstraint* constraintDialog; + + void checkForWizard(); + static QObject* findChildByName(const QObject* parent, const QString& name); +}; + +} //namespace FemGui + + +#endif // GUI_VIEWPROVIDERFEMCONSTRAINT_H diff --git a/src/Mod/Fem/Gui/ViewProviderFemConstraintBearing.cpp b/src/Mod/Fem/Gui/ViewProviderFemConstraintBearing.cpp new file mode 100644 index 0000000000..a2186df8ff --- /dev/null +++ b/src/Mod/Fem/Gui/ViewProviderFemConstraintBearing.cpp @@ -0,0 +1,153 @@ +/*************************************************************************** + * Copyright (c) 2013 Jan Rheinländer * + * * + * 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 +#endif + +#include "ViewProviderFemConstraintBearing.h" +#include +#include "TaskFemConstraintBearing.h" +#include "Gui/Control.h" +#include "Gui/MainWindow.h" + +#include + +using namespace FemGui; + +PROPERTY_SOURCE(FemGui::ViewProviderFemConstraintBearing, FemGui::ViewProviderFemConstraint) + + +ViewProviderFemConstraintBearing::ViewProviderFemConstraintBearing() +{ + sPixmap = "Fem_ConstraintBearing"; + wizardWidget = NULL; +} + +ViewProviderFemConstraintBearing::~ViewProviderFemConstraintBearing() +{ +} + +bool ViewProviderFemConstraintBearing::setEdit(int ModNum) +{ + Base::Console().Error("ViewProviderFemConstraintBearing::setEdit()\n"); + Base::Console().Error("Active dialog: %s\n", Gui::Control().activeDialog()->objectName().toStdString().c_str()); + + if (ModNum == ViewProvider::Default ) { + // When double-clicking on the item for this constraint the + // object unsets and sets its edit mode without closing + // the task panel + Gui::TaskView::TaskDialog *dlg = Gui::Control().activeDialog(); + TaskDlgFemConstraintBearing *constrDlg = qobject_cast(dlg); + if (constrDlg && constrDlg->getConstraintView() != this) + constrDlg = 0; // another constraint left open its task panel + if (dlg && !constrDlg) { + // This case will occur in the ShaftWizard application + checkForWizard(); + if ((wizardWidget == NULL) || (wizardSubLayout == NULL)) { + // No shaft wizard is running + QMessageBox msgBox; + msgBox.setText(QObject::tr("A dialog is already open in the task panel")); + msgBox.setInformativeText(QObject::tr("Do you want to close this dialog?")); + msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); + msgBox.setDefaultButton(QMessageBox::Yes); + int ret = msgBox.exec(); + if (ret == QMessageBox::Yes) + Gui::Control().closeDialog(); + else + return false; + } else if (constraintDialog != NULL) { + // Another FemConstraint* dialog is already open inside the Shaft Wizard + // Ignore the request to open another dialog + return false; + } else { + constraintDialog = new TaskFemConstraintBearing(this); + return true; + } + } + + // clear the selection (convenience) + Gui::Selection().clearSelection(); + + // start the edit dialog + if (constrDlg) + Gui::Control().showDialog(constrDlg); + else + Gui::Control().showDialog(new TaskDlgFemConstraintBearing(this)); + + return true; + } + else { + return ViewProviderDocumentObject::setEdit(ModNum); + } +} + +void ViewProviderFemConstraintBearing::updateData(const App::Property* prop) +{ + // Gets called whenever a property of the attached object changes + Fem::ConstraintBearing* pcConstraint = static_cast(this->getObject()); + + if (strcmp(prop->getName(),"References") == 0) + Base::Console().Error("\n"); // enable a breakpoint here + + if (strcmp(prop->getName(),"BasePoint") == 0) { + // Remove and recreate the symbol + pShapeSep->removeAllChildren(); + + // This should always point outside of the cylinder + Base::Vector3f normal = pcConstraint->NormalDirection.getValue(); + Base::Vector3f base = pcConstraint->BasePoint.getValue(); + float radius = pcConstraint->Radius.getValue(); + base = base + radius * normal; + + SbVec3f b(base.x, base.y, base.z); + SbVec3f dir(normal.x, normal.y, normal.z); + SbRotation rot(SbVec3f(0,-1,0), dir); + + createPlacement(pShapeSep, b, rot); + pShapeSep->addChild(createFixed(radius/2, radius/2 * 1.5, pcConstraint->AxialFree.getValue())); + } else if (strcmp(prop->getName(),"AxialFree") == 0) { + if (pShapeSep->getNumChildren() > 0) { + // Change the symbol + Base::Vector3f normal = pcConstraint->NormalDirection.getValue(); + Base::Vector3f base = pcConstraint->BasePoint.getValue(); + float radius = pcConstraint->Radius.getValue(); + base = base + radius * normal; + + SbVec3f b(base.x, base.y, base.z); + SbVec3f dir(normal.x, normal.y, normal.z); + SbRotation rot(SbVec3f(0,-1,0), dir); + + updatePlacement(pShapeSep, 0, b, rot); + const SoSeparator* sep = static_cast(pShapeSep->getChild(2)); + updateFixed(sep, 0, radius/2, radius/2 * 1.5, pcConstraint->AxialFree.getValue()); + } + } + + ViewProviderFemConstraint::updateData(prop); +} diff --git a/src/Mod/Fem/Gui/ViewProviderFemConstraintBearing.h b/src/Mod/Fem/Gui/ViewProviderFemConstraintBearing.h new file mode 100644 index 0000000000..603a043137 --- /dev/null +++ b/src/Mod/Fem/Gui/ViewProviderFemConstraintBearing.h @@ -0,0 +1,75 @@ +/*************************************************************************** + * Copyright (c) 2013 Jan Rheinländer * + * * + * 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_VIEWPROVIDERFEMCONSTRAINTBEARING_H +#define GUI_VIEWPROVIDERFEMCONSTRAINTBEARING_H + +#include + +#include "ViewProviderFemConstraint.h" +#include +#include + +class SoFontStyle; +class SoText2; +class SoBaseColor; +class SoTranslation; +class SbRotation; +class SoMaterial; +class SoLightModel; +class SoCoordinate3; +class SoIndexedLineSet; +class SoIndexedFaceSet; +class SoEventCallback; +class SoMarkerSet; + +namespace Gui { +class View3DInventorViewer; + namespace TaskView { + class TaskDialog; + } +} + +namespace FemGui +{ + +class FemGuiExport ViewProviderFemConstraintBearing : public FemGui::ViewProviderFemConstraint +{ + PROPERTY_HEADER(FemGui::ViewProviderFemConstraintBearing); + +public: + /// Constructor + ViewProviderFemConstraintBearing(); + virtual ~ViewProviderFemConstraintBearing(); + + virtual void updateData(const App::Property*); + +protected: + virtual bool setEdit(int ModNum); + +}; + +} //namespace FemGui + + +#endif // GUI_VIEWPROVIDERFEMCONSTRAINTBEARING_H diff --git a/src/Mod/Fem/Gui/ViewProviderFemConstraintFixed.cpp b/src/Mod/Fem/Gui/ViewProviderFemConstraintFixed.cpp new file mode 100644 index 0000000000..d82603353f --- /dev/null +++ b/src/Mod/Fem/Gui/ViewProviderFemConstraintFixed.cpp @@ -0,0 +1,163 @@ +/*************************************************************************** + * Copyright (c) 2013 Jan Rheinländer * + * * + * 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 + +#endif + +#include "ViewProviderFemConstraintFixed.h" +#include +#include "TaskFemConstraintFixed.h" + +#include "Gui/Control.h" + +#include + +using namespace FemGui; + +PROPERTY_SOURCE(FemGui::ViewProviderFemConstraintFixed, FemGui::ViewProviderFemConstraint) + + +ViewProviderFemConstraintFixed::ViewProviderFemConstraintFixed() +{ + sPixmap = "Fem_ConstraintFixed"; +} + +ViewProviderFemConstraintFixed::~ViewProviderFemConstraintFixed() +{ +} + +bool ViewProviderFemConstraintFixed::setEdit(int ModNum) +{ + Base::Console().Error("ViewProviderFemConstraintFixed::setEdit()\n"); + if (ModNum == ViewProvider::Default ) { + // When double-clicking on the item for this constraint the + // object unsets and sets its edit mode without closing + // the task panel + Gui::TaskView::TaskDialog *dlg = Gui::Control().activeDialog(); + TaskDlgFemConstraintFixed *constrDlg = qobject_cast(dlg); + if (constrDlg && constrDlg->getConstraintView() != this) + constrDlg = 0; // another constraint left open its task panel + if (dlg && !constrDlg) { + // This case will occur in the ShaftWizard application + checkForWizard(); + if ((wizardWidget == NULL) || (wizardSubLayout == NULL)) { + // No shaft wizard is running + QMessageBox msgBox; + msgBox.setText(QObject::tr("A dialog is already open in the task panel")); + msgBox.setInformativeText(QObject::tr("Do you want to close this dialog?")); + msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); + msgBox.setDefaultButton(QMessageBox::Yes); + int ret = msgBox.exec(); + if (ret == QMessageBox::Yes) + Gui::Control().closeDialog(); + else + return false; + } else if (constraintDialog != NULL) { + // Another FemConstraint* dialog is already open inside the Shaft Wizard + // Ignore the request to open another dialog + return false; + } else { + constraintDialog = new TaskFemConstraintFixed(this); + return true; + } + } + + // clear the selection (convenience) + Gui::Selection().clearSelection(); + + // start the edit dialog + if (constrDlg) + Gui::Control().showDialog(constrDlg); + else + Gui::Control().showDialog(new TaskDlgFemConstraintFixed(this)); + + return true; + } else { + return ViewProviderDocumentObject::setEdit(ModNum); + } +} + +#define HEIGHT 4 +#define WIDTH (1.5*HEIGHT) + +void ViewProviderFemConstraintFixed::updateData(const App::Property* prop) +{ + // Gets called whenever a property of the attached object changes + Fem::ConstraintFixed* pcConstraint = static_cast(this->getObject()); + + /* + // This has a HUGE performance penalty as opposed to separate nodes for every symbol + // The problem seems to be SoCone + if (pShapeSep->getNumChildren() == 0) { + // Set up the nodes + SoMultipleCopy* cp = new SoMultipleCopy(); + cp->ref(); + cp->matrix.setNum(0); + cp->addChild((SoNode*)createFixed(HEIGHT, WIDTH)); + pShapeSep->addChild(cp); + } + */ + + if (strcmp(prop->getName(),"Points") == 0) { + // Note: Points and Normals are always updated together + pShapeSep->removeAllChildren(); + + const std::vector& points = pcConstraint->Points.getValues(); + const std::vector& normals = pcConstraint->Normals.getValues(); + std::vector::const_iterator n = normals.begin(); + /* + SoMultipleCopy* cp = static_cast(pShapeSep->getChild(0)); + cp->matrix.setNum(points.size()); + int idx = 0; + */ + + for (std::vector::const_iterator p = points.begin(); p != points.end(); p++) { + SbVec3f base(p->x, p->y, p->z); + SbVec3f dir(n->x, n->y, n->z); + SbRotation rot(SbVec3f(0,-1,0), dir); + /* + SbMatrix m; + m.setTransform(base, rot, SbVec3f(1,1,1)); + cp->matrix.set1Value(idx, m); + idx++ + */ + SoSeparator* sep = new SoSeparator(); + createPlacement(sep, base, rot); + createFixed(sep, HEIGHT, WIDTH); + pShapeSep->addChild(sep); + + n++; + } + } + + ViewProviderFemConstraint::updateData(prop); +} diff --git a/src/Mod/Fem/Gui/ViewProviderFemConstraintFixed.h b/src/Mod/Fem/Gui/ViewProviderFemConstraintFixed.h new file mode 100644 index 0000000000..ff9825e8a0 --- /dev/null +++ b/src/Mod/Fem/Gui/ViewProviderFemConstraintFixed.h @@ -0,0 +1,74 @@ +/*************************************************************************** + * Copyright (c) 2013 Jan Rheinländer * + * * + * 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_VIEWPROVIDERFEMCONSTRAINTFIXED_H +#define GUI_VIEWPROVIDERFEMCONSTRAINTFIXED_H + +#include + +#include "ViewProviderFemConstraint.h" +#include + +class SoFontStyle; +class SoText2; +class SoBaseColor; +class SoTranslation; +class SbRotation; +class SoMaterial; +class SoLightModel; +class SoCoordinate3; +class SoIndexedLineSet; +class SoIndexedFaceSet; +class SoEventCallback; +class SoMarkerSet; + +namespace Gui { +class View3DInventorViewer; + namespace TaskView { + class TaskDialog; + } +} + +namespace FemGui +{ + +class FemGuiExport ViewProviderFemConstraintFixed : public FemGui::ViewProviderFemConstraint +{ + PROPERTY_HEADER(FemGui::ViewProviderFemConstraintFixed); + +public: + /// Constructor + ViewProviderFemConstraintFixed(); + virtual ~ViewProviderFemConstraintFixed(); + + virtual void updateData(const App::Property*); + +protected: + virtual bool setEdit(int ModNum); + +}; + +} //namespace FemGui + + +#endif // GUI_VIEWPROVIDERFEMCONSTRAINTFIXED_H diff --git a/src/Mod/Fem/Gui/ViewProviderFemConstraintForce.cpp b/src/Mod/Fem/Gui/ViewProviderFemConstraintForce.cpp new file mode 100644 index 0000000000..56b49b0bd2 --- /dev/null +++ b/src/Mod/Fem/Gui/ViewProviderFemConstraintForce.cpp @@ -0,0 +1,202 @@ +/*************************************************************************** + * Copyright (c) 2013 Jan Rheinländer * + * * + * 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 + +#endif + +#include "ViewProviderFemConstraintForce.h" +#include +#include "TaskFemConstraintForce.h" +#include "Gui/Control.h" + +#include + +using namespace FemGui; + +PROPERTY_SOURCE(FemGui::ViewProviderFemConstraintForce, FemGui::ViewProviderFemConstraint) + + +ViewProviderFemConstraintForce::ViewProviderFemConstraintForce() +{ + sPixmap = "Fem_ConstraintForce"; +} + +ViewProviderFemConstraintForce::~ViewProviderFemConstraintForce() +{ +} + +bool ViewProviderFemConstraintForce::setEdit(int ModNum) +{ + Base::Console().Error("ViewProviderFemConstraintForce::setEdit(%u)\n", ModNum); + + if (ModNum == ViewProvider::Default ) { + // When double-clicking on the item for this constraint the + // object unsets and sets its edit mode without closing + // the task panel + Gui::TaskView::TaskDialog *dlg = Gui::Control().activeDialog(); + TaskDlgFemConstraintForce *constrDlg = qobject_cast(dlg); + if (constrDlg && constrDlg->getConstraintView() != this) + constrDlg = 0; // another constraint left open its task panel + if (dlg && !constrDlg) { + // This case will occur in the ShaftWizard application + checkForWizard(); + if ((wizardWidget == NULL) || (wizardSubLayout == NULL)) { + // No shaft wizard is running + QMessageBox msgBox; + msgBox.setText(QObject::tr("A dialog is already open in the task panel")); + msgBox.setInformativeText(QObject::tr("Do you want to close this dialog?")); + msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); + msgBox.setDefaultButton(QMessageBox::Yes); + int ret = msgBox.exec(); + if (ret == QMessageBox::Yes) + Gui::Control().closeDialog(); + else + return false; + } else if (constraintDialog != NULL) { + // Another FemConstraint* dialog is already open inside the Shaft Wizard + // Ignore the request to open another dialog + return false; + } else { + constraintDialog = new TaskFemConstraintForce(this); + return true; + } + } + + // clear the selection (convenience) + Gui::Selection().clearSelection(); + + // start the edit dialog + if (constrDlg) + Gui::Control().showDialog(constrDlg); + else + Gui::Control().showDialog(new TaskDlgFemConstraintForce(this)); + + return true; + } + else { + return ViewProviderDocumentObject::setEdit(ModNum); + } +} + +#define ARROWLENGTH 9 +#define ARROWHEADRADIUS (ARROWLENGTH/3) + +void ViewProviderFemConstraintForce::updateData(const App::Property* prop) +{ + // Gets called whenever a property of the attached object changes + Fem::ConstraintForce* pcConstraint = static_cast(this->getObject()); + + /* + // This has a HUGE performance penalty as opposed to separate nodes for every symbol + // The problem seems to be SoCone + if (pShapeSep->getNumChildren() == 0) { + // Set up the nodes + SoMultipleCopy* cp = new SoMultipleCopy(); + cp->ref(); + cp->matrix.setNum(0); + cp->addChild((SoNode*)createArrow(ARROWLENGTH, ARROWHEADRADIUS)); + pShapeSep->addChild(cp); + } + */ + + if (strcmp(prop->getName(),"Points") == 0) { + // Redraw all arrows + pShapeSep->removeAllChildren(); + + // This should always point outside of the solid + Base::Vector3f normal = pcConstraint->NormalDirection.getValue(); + + // Get default direction (on first call to method) + Base::Vector3f forceDirection = pcConstraint->DirectionVector.getValue(); + if (forceDirection.Length() < Precision::Confusion()) + forceDirection = normal; + + SbVec3f dir(forceDirection.x, forceDirection.y, forceDirection.z); + SbRotation rot(SbVec3f(0,1,0), dir); + + const std::vector& points = pcConstraint->Points.getValues(); + + /* + SoMultipleCopy* cp = static_cast(pShapeSep->getChild(0)); + cp->matrix.setNum(points.size()); + int idx = 0;*/ + + for (std::vector::const_iterator p = points.begin(); p != points.end(); p++) { + SbVec3f base(p->x, p->y, p->z); + if (forceDirection.GetAngle(normal) < M_PI_2) // Move arrow so it doesn't disappear inside the solid + base = base + dir * ARROWLENGTH; + /* + SbMatrix m; + m.setTransform(base, rot, SbVec3f(1,1,1)); + cp->matrix.set1Value(idx, m); + idx++; + */ + SoSeparator* sep = new SoSeparator(); + createPlacement(sep, base, rot); + createArrow(sep, ARROWLENGTH, ARROWHEADRADIUS); + pShapeSep->addChild(sep); + } + } else if (strcmp(prop->getName(),"DirectionVector") == 0) { // Note: "Reversed" also triggers "DirectionVector" + // Re-orient all arrows + Base::Vector3f normal = pcConstraint->NormalDirection.getValue(); + Base::Vector3f forceDirection = pcConstraint->DirectionVector.getValue(); + if (forceDirection.Length() < Precision::Confusion()) + forceDirection = normal; + + SbVec3f dir(forceDirection.x, forceDirection.y, forceDirection.z); + SbRotation rot(SbVec3f(0,1,0), dir); + + const std::vector& points = pcConstraint->Points.getValues(); + + /* + SoMultipleCopy* cp = static_cast(pShapeSep->getChild(0)); + cp->matrix.setNum(points.size()); + */ + int idx = 0; + + for (std::vector::const_iterator p = points.begin(); p != points.end(); p++) { + SbVec3f base(p->x, p->y, p->z); + if (forceDirection.GetAngle(normal) < M_PI_2) + base = base + dir * ARROWLENGTH; + /* + SbMatrix m; + m.setTransform(base, rot, SbVec3f(1,1,1)); + cp->matrix.set1Value(idx, m);*/ + + SoSeparator* sep = static_cast(pShapeSep->getChild(idx)); + updatePlacement(sep, 0, base, rot); + updateArrow(sep, 2, ARROWLENGTH, ARROWHEADRADIUS); + idx++; + } + } + + ViewProviderFemConstraint::updateData(prop); +} diff --git a/src/Mod/Fem/Gui/ViewProviderFemConstraintForce.h b/src/Mod/Fem/Gui/ViewProviderFemConstraintForce.h new file mode 100644 index 0000000000..ba6027fc61 --- /dev/null +++ b/src/Mod/Fem/Gui/ViewProviderFemConstraintForce.h @@ -0,0 +1,78 @@ +/*************************************************************************** + * Copyright (c) 2013 Jan Rheinländer * + * * + * 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_VIEWPROVIDERFEMCONSTRAINTFORCE_H +#define GUI_VIEWPROVIDERFEMCONSTRAINTFORCE_H + +#include + +#include "ViewProviderFemConstraint.h" +#include + +class SoFontStyle; +class SoText2; +class SoBaseColor; +class SoTranslation; +class SbRotation; +class SoMaterial; +class SoLightModel; +class SoCoordinate3; +class SoIndexedLineSet; +class SoIndexedFaceSet; +class SoEventCallback; +class SoMarkerSet; + +namespace Gui { +class View3DInventorViewer; + namespace TaskView { + class TaskDialog; + } +} + +namespace FemGui +{ + +class FemGuiExport ViewProviderFemConstraintForce : public FemGui::ViewProviderFemConstraint +{ + PROPERTY_HEADER(FemGui::ViewProviderFemConstraintForce); + +public: + /// Constructor + ViewProviderFemConstraintForce(); + virtual ~ViewProviderFemConstraintForce(); + + virtual void updateData(const App::Property*); + +protected: + virtual bool setEdit(int ModNum); + +private: + /// Direction of the force + Base::Vector3f forceDirection; + +}; + +} //namespace FemGui + + +#endif // GUI_VIEWPROVIDERFEMCONSTRAINTFORCE_H diff --git a/src/Mod/Fem/Gui/ViewProviderFemConstraintGear.cpp b/src/Mod/Fem/Gui/ViewProviderFemConstraintGear.cpp new file mode 100644 index 0000000000..f5df203590 --- /dev/null +++ b/src/Mod/Fem/Gui/ViewProviderFemConstraintGear.cpp @@ -0,0 +1,191 @@ +/*************************************************************************** + * Copyright (c) 2013 Jan Rheinländer * + * * + * 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 +#endif + +#include "ViewProviderFemConstraintGear.h" +#include +#include "TaskFemConstraintGear.h" +#include "Gui/Control.h" + +#include + +using namespace FemGui; + +PROPERTY_SOURCE(FemGui::ViewProviderFemConstraintGear, FemGui::ViewProviderFemConstraint) + + +ViewProviderFemConstraintGear::ViewProviderFemConstraintGear() +{ + sPixmap = "Fem_ConstraintGear"; +} + +ViewProviderFemConstraintGear::~ViewProviderFemConstraintGear() +{ +} + +bool ViewProviderFemConstraintGear::setEdit(int ModNum) +{ + Base::Console().Error("ViewProviderFemConstraintGear::setEdit()\n"); + if (ModNum == ViewProvider::Default ) { + // When double-clicking on the item for this constraint the + // object unsets and sets its edit mode without closing + // the task panel + Gui::TaskView::TaskDialog *dlg = Gui::Control().activeDialog(); + TaskDlgFemConstraintGear *constrDlg = qobject_cast(dlg); + if (constrDlg && constrDlg->getConstraintView() != this) + constrDlg = 0; // another constraint left open its task panel + if (dlg && !constrDlg) { + // This case will occur in the ShaftWizard application + checkForWizard(); + if ((wizardWidget == NULL) || (wizardSubLayout == NULL)) { + // No shaft wizard is running + QMessageBox msgBox; + msgBox.setText(QObject::tr("A dialog is already open in the task panel")); + msgBox.setInformativeText(QObject::tr("Do you want to close this dialog?")); + msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); + msgBox.setDefaultButton(QMessageBox::Yes); + int ret = msgBox.exec(); + if (ret == QMessageBox::Yes) + Gui::Control().closeDialog(); + else + return false; + } else if (constraintDialog != NULL) { + // Another FemConstraint* dialog is already open inside the Shaft Wizard + // Ignore the request to open another dialog + return false; + } else { + constraintDialog = new TaskFemConstraintGear(this); + return true; + } + } + + // clear the selection (convenience) + Gui::Selection().clearSelection(); + + // start the edit dialog + if (constrDlg) + Gui::Control().showDialog(constrDlg); + else + Gui::Control().showDialog(new TaskDlgFemConstraintGear(this)); + + return true; + } + else { + return ViewProviderDocumentObject::setEdit(ModNum); + } +} + +void ViewProviderFemConstraintGear::updateData(const App::Property* prop) +{ + Fem::ConstraintGear* pcConstraint = static_cast(this->getObject()); + + // Gets called whenever a property of the attached object changes + if (strcmp(prop->getName(),"BasePoint") == 0) { + if (pcConstraint->Height.getValue() > Precision::Confusion()) { + // Remove and recreate the symbol + pShapeSep->removeAllChildren(); + + Base::Vector3f base = pcConstraint->BasePoint.getValue(); + Base::Vector3f axis = pcConstraint->Axis.getValue(); + Base::Vector3f direction = pcConstraint->DirectionVector.getValue(); + if (direction.Length() < Precision::Confusion()) + direction = Base::Vector3f(0,1,0); + float radius = pcConstraint->Radius.getValue(); + float dia = pcConstraint->Diameter.getValue(); + if (dia < 2 * radius) + dia = 2 * radius; + float angle = pcConstraint->ForceAngle.getValue() / 180 * M_PI; + + SbVec3f b(base.x, base.y, base.z); + SbVec3f ax(axis.x, axis.y, axis.z); + SbVec3f dir(direction.x, direction.y, direction.z); + //Base::Console().Error("DirectionVector: %f, %f, %f\n", direction.x, direction.y, direction.z); + + createPlacement(pShapeSep, b, SbRotation(SbVec3f(0,1,0), ax)); + pShapeSep->addChild(createCylinder(pcConstraint->Height.getValue() * 0.8, dia/2)); + createPlacement(pShapeSep, SbVec3f(dia/2 * sin(angle), 0, dia/2 * cos(angle)), SbRotation(ax, dir)); + pShapeSep->addChild(createArrow(dia/2, dia/8)); + } + } else if (strcmp(prop->getName(),"Diameter") == 0) { + if (pShapeSep->getNumChildren() > 0) { + // Change the symbol + Base::Vector3f axis = pcConstraint->Axis.getValue(); + Base::Vector3f direction = pcConstraint->DirectionVector.getValue(); + if (direction.Length() < Precision::Confusion()) + direction = Base::Vector3f(0,1,0); + float dia = pcConstraint->Diameter.getValue(); + float radius = pcConstraint->Radius.getValue(); + if (dia < 2 * radius) + dia = 2 * radius; + float angle = pcConstraint->ForceAngle.getValue() / 180 * M_PI; + + SbVec3f ax(axis.x, axis.y, axis.z); + SbVec3f dir(direction.x, direction.y, direction.z); + + const SoSeparator* sep = static_cast(pShapeSep->getChild(2)); + updateCylinder(sep, 0, pcConstraint->Height.getValue() * 0.8, dia/2); + updatePlacement(pShapeSep, 3, SbVec3f(dia/2 * sin(angle), 0, dia/2 * cos(angle)), SbRotation(ax, dir)); + sep = static_cast(pShapeSep->getChild(5)); + updateArrow(sep, 0, dia/2, dia/8); + } + } else if ((strcmp(prop->getName(),"DirectionVector") == 0) || (strcmp(prop->getName(),"ForceAngle") == 0)) { + // Note: "Reversed" also triggers "DirectionVector" + if (pShapeSep->getNumChildren() > 0) { + // Re-orient the symbol + Base::Vector3f axis = pcConstraint->Axis.getValue(); + Base::Vector3f direction = pcConstraint->DirectionVector.getValue(); + if (direction.Length() < Precision::Confusion()) + direction = Base::Vector3f(0,1,0); + float dia = pcConstraint->Diameter.getValue(); + float angle = pcConstraint->ForceAngle.getValue() / 180 * M_PI; + + SbVec3f ax(axis.x, axis.y, axis.z); + SbVec3f dir(direction.x, direction.y, direction.z); + /*Base::Console().Error("Axis: %f, %f, %f\n", axis.x, axis.y, axis.z); + Base::Console().Error("Direction: %f, %f, %f\n", direction.x, direction.y, direction.z); + SbRotation rot = SbRotation(ax, dir); + SbMatrix m; + rot.getValue(m); + SbMat m2; + m.getValue(m2); + Base::Console().Error("Matrix: %f, %f, %f, %f\n", m[0][0], m[1][0], m[2][0], m[3][0]); + // Note: In spite of the fact that the rotation matrix takes on 3 different values if 3 + // normal directions are chosen, the resulting arrow will only point in two different + // directions when ax = (1,0,0) (but for ax=(0,1,0) it points in 3 different directions!) + */ + + updatePlacement(pShapeSep, 3, SbVec3f(dia/2 * sin(angle), 0, dia/2 * cos(angle)), SbRotation(ax, dir)); + } + } + + ViewProviderFemConstraint::updateData(prop); +} diff --git a/src/Mod/Fem/Gui/ViewProviderFemConstraintGear.h b/src/Mod/Fem/Gui/ViewProviderFemConstraintGear.h new file mode 100644 index 0000000000..55f08a1adf --- /dev/null +++ b/src/Mod/Fem/Gui/ViewProviderFemConstraintGear.h @@ -0,0 +1,74 @@ +/*************************************************************************** + * Copyright (c) 2013 Jan Rheinländer * + * * + * 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_VIEWPROVIDERFEMCONSTRAINTGear_H +#define GUI_VIEWPROVIDERFEMCONSTRAINTGear_H + +#include + +#include "ViewProviderFemConstraint.h" +#include + +class SoFontStyle; +class SoText2; +class SoBaseColor; +class SoTranslation; +class SbRotation; +class SoMaterial; +class SoLightModel; +class SoCoordinate3; +class SoIndexedLineSet; +class SoIndexedFaceSet; +class SoEventCallback; +class SoMarkerSet; + +namespace Gui { +class View3DInventorViewer; + namespace TaskView { + class TaskDialog; + } +} + +namespace FemGui +{ + +class FemGuiExport ViewProviderFemConstraintGear : public FemGui::ViewProviderFemConstraint +{ + PROPERTY_HEADER(FemGui::ViewProviderFemConstraintGear); + +public: + /// Constructor + ViewProviderFemConstraintGear(); + virtual ~ViewProviderFemConstraintGear(); + + virtual void updateData(const App::Property*); + +protected: + virtual bool setEdit(int ModNum); + +}; + +} //namespace FemGui + + +#endif // GUI_VIEWPROVIDERFEMCONSTRAINTGear_H diff --git a/src/Mod/Fem/Gui/ViewProviderFemConstraintPulley.cpp b/src/Mod/Fem/Gui/ViewProviderFemConstraintPulley.cpp new file mode 100644 index 0000000000..ed10aa5249 --- /dev/null +++ b/src/Mod/Fem/Gui/ViewProviderFemConstraintPulley.cpp @@ -0,0 +1,225 @@ +/*************************************************************************** + * Copyright (c) 2013 Jan Rheinländer * + * * + * 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 +#endif + +#include "ViewProviderFemConstraintPulley.h" +#include +#include "TaskFemConstraintPulley.h" +#include "Gui/Control.h" + +#include + +using namespace FemGui; + +PROPERTY_SOURCE(FemGui::ViewProviderFemConstraintPulley, FemGui::ViewProviderFemConstraint) + + +ViewProviderFemConstraintPulley::ViewProviderFemConstraintPulley() +{ + sPixmap = "Fem_ConstraintPulley"; +} + +ViewProviderFemConstraintPulley::~ViewProviderFemConstraintPulley() +{ +} + +bool ViewProviderFemConstraintPulley::setEdit(int ModNum) +{ + Base::Console().Error("ViewProviderFemConstraintPulley::setEdit()\n"); + if (ModNum == ViewProvider::Default ) { + // When double-clicking on the item for this constraint the + // object unsets and sets its edit mode without closing + // the task panel + Gui::TaskView::TaskDialog *dlg = Gui::Control().activeDialog(); + TaskDlgFemConstraintPulley *constrDlg = qobject_cast(dlg); + if (constrDlg && constrDlg->getConstraintView() != this) + constrDlg = 0; // another constraint left open its task panel + if (dlg && !constrDlg) { + // This case will occur in the ShaftWizard application + checkForWizard(); + if ((wizardWidget == NULL) || (wizardSubLayout == NULL)) { + // No shaft wizard is running + QMessageBox msgBox; + msgBox.setText(QObject::tr("A dialog is already open in the task panel")); + msgBox.setInformativeText(QObject::tr("Do you want to close this dialog?")); + msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); + msgBox.setDefaultButton(QMessageBox::Yes); + int ret = msgBox.exec(); + if (ret == QMessageBox::Yes) + Gui::Control().closeDialog(); + else + return false; + } else if (constraintDialog != NULL) { + // Another FemConstraint* dialog is already open inside the Shaft Wizard + // Ignore the request to open another dialog + return false; + } else { + constraintDialog = new TaskFemConstraintPulley(this); + return true; + } + } + + // clear the selection (convenience) + Gui::Selection().clearSelection(); + + // start the edit dialog + if (constrDlg) + Gui::Control().showDialog(constrDlg); + else + Gui::Control().showDialog(new TaskDlgFemConstraintPulley(this)); + + return true; + } + else { + return ViewProviderDocumentObject::setEdit(ModNum); + } +} + +void ViewProviderFemConstraintPulley::updateData(const App::Property* prop) +{ + // Gets called whenever a property of the attached object changes + Fem::ConstraintPulley* pcConstraint = static_cast(this->getObject()); + + if (strcmp(prop->getName(),"BasePoint") == 0) { + if (pcConstraint->Height.getValue() > Precision::Confusion()) { + // Remove and recreate the symbol + pShapeSep->removeAllChildren(); + + // This should always point outside of the cylinder + Base::Vector3f base = pcConstraint->BasePoint.getValue(); + Base::Vector3f axis = pcConstraint->Axis.getValue(); + float radius = pcConstraint->Radius.getValue(); + float dia = pcConstraint->Diameter.getValue(); + if (dia < 2 * radius) + dia = 2 * radius; + float forceAngle = pcConstraint->ForceAngle.getValue() / 180 * M_PI; + float beltAngle = pcConstraint->BeltAngle.getValue(); + double rat1 = 0.8, rat2 = 0.2; + float f1 = pcConstraint->BeltForce1.getValue(); + float f2 = pcConstraint->BeltForce2.getValue(); + if (f1+f2 > Precision::Confusion()) { + rat1 = f1 / (f1+f2); + rat2 = f2 / (f1+f2); + } + + SbVec3f b(base.x, base.y, base.z); + SbVec3f ax(axis.x, axis.y, axis.z); + + createPlacement(pShapeSep, b, SbRotation(SbVec3f(0,1,0), ax)); // child 0 and 1 + pShapeSep->addChild(createCylinder(pcConstraint->Height.getValue() * 0.8, dia/2)); // child 2 + SoSeparator* sep = new SoSeparator(); + createPlacement(sep, SbVec3f(dia/2 * sin(forceAngle+beltAngle), 0, dia/2 * cos(forceAngle+beltAngle)), + SbRotation(SbVec3f(0,1,0), SbVec3f(sin(forceAngle+beltAngle+M_PI_2),0,cos(forceAngle+beltAngle+M_PI_2)))); + createPlacement(sep, SbVec3f(0, dia/8 + dia/2 * rat1, 0), SbRotation()); + sep->addChild(createArrow(dia/8 + dia/2 * rat1, dia/8)); + pShapeSep->addChild(sep); // child 3 + sep = new SoSeparator(); + createPlacement(sep, SbVec3f(-dia/2 * sin(forceAngle-beltAngle), 0, -dia/2 * cos(forceAngle-beltAngle)), + SbRotation(SbVec3f(0,1,0), SbVec3f(-sin(forceAngle-beltAngle-M_PI_2),0,-cos(forceAngle-beltAngle-M_PI_2)))); + createPlacement(sep, SbVec3f(0, dia/8 + dia/2 * rat2, 0), SbRotation()); + sep->addChild(createArrow(dia/8 + dia/2 * rat2, dia/8)); + pShapeSep->addChild(sep); // child 4 + } + } else if (strcmp(prop->getName(),"Diameter") == 0) { + if (pShapeSep->getNumChildren() > 0) { + // Change the symbol + float radius = pcConstraint->Radius.getValue(); + float dia = pcConstraint->Diameter.getValue(); + if (dia < 2 * radius) + dia = 2 * radius; + float forceAngle = pcConstraint->ForceAngle.getValue() / 180 * M_PI; + float beltAngle = pcConstraint->BeltAngle.getValue(); + double rat1 = 0.8, rat2 = 0.2; + float f1 = pcConstraint->BeltForce1.getValue(); + float f2 = pcConstraint->BeltForce2.getValue(); + if (f1+f2 > Precision::Confusion()) { + rat1 = f1 / (f1+f2); + rat2 = f2 / (f1+f2); + } + + const SoSeparator* sep = static_cast(pShapeSep->getChild(2)); + updateCylinder(sep, 0, pcConstraint->Height.getValue() * 0.8, dia/2); + sep = static_cast(pShapeSep->getChild(3)); + updatePlacement(sep, 0, SbVec3f(dia/2 * sin(forceAngle+beltAngle), 0, dia/2 * cos(forceAngle+beltAngle)), + SbRotation(SbVec3f(0,1,0), SbVec3f(sin(forceAngle+beltAngle+M_PI_2),0,cos(forceAngle+beltAngle+M_PI_2)))); + updatePlacement(sep, 2, SbVec3f(0, dia/8 + dia/2 * rat1, 0), SbRotation()); + const SoSeparator* subsep = static_cast(sep->getChild(4)); + updateArrow(subsep, 0, dia/8 + dia/2 * rat1, dia/8); + sep = static_cast(pShapeSep->getChild(4)); + updatePlacement(sep, 0, SbVec3f(-dia/2 * sin(forceAngle-beltAngle), 0, -dia/2 * cos(forceAngle-beltAngle)), + SbRotation(SbVec3f(0,1,0), SbVec3f(-sin(forceAngle-beltAngle-M_PI_2),0,-cos(forceAngle-beltAngle-M_PI_2)))); + updatePlacement(sep, 2, SbVec3f(0, dia/8 + dia/2 * rat2, 0), SbRotation()); + subsep = static_cast(sep->getChild(4)); + updateArrow(subsep, 0, dia/8 + dia/2 * rat2, dia/8); + } + } else if ((strcmp(prop->getName(), "ForceAngle") == 0) || (strcmp(prop->getName(), "BeltAngle") == 0)) { + if (pShapeSep->getNumChildren() > 0) { + float radius = pcConstraint->Radius.getValue(); + float dia = pcConstraint->Diameter.getValue(); + if (dia < 2 * radius) + dia = 2 * radius; + float forceAngle = pcConstraint->ForceAngle.getValue() / 180 * M_PI; + float beltAngle = pcConstraint->BeltAngle.getValue(); + + const SoSeparator* sep = static_cast(pShapeSep->getChild(3)); + updatePlacement(sep, 0, SbVec3f(dia/2 * sin(forceAngle+beltAngle), 0, dia/2 * cos(forceAngle+beltAngle)), + SbRotation(SbVec3f(0,1,0), SbVec3f(sin(forceAngle+beltAngle+M_PI_2),0,cos(forceAngle+beltAngle+M_PI_2)))); + sep = static_cast(pShapeSep->getChild(4)); + updatePlacement(sep, 0, SbVec3f(-dia/2 * sin(forceAngle-beltAngle), 0, -dia/2 * cos(forceAngle-beltAngle)), + SbRotation(SbVec3f(0,1,0), SbVec3f(-sin(forceAngle-beltAngle-M_PI_2),0,-cos(forceAngle-beltAngle-M_PI_2)))); + } + } else if ((strcmp(prop->getName(), "BeltForce1") == 0) || (strcmp(prop->getName(), "BeltForce2") == 0)) { + if (pShapeSep->getNumChildren() > 0) { + float radius = pcConstraint->Radius.getValue(); + float dia = pcConstraint->Diameter.getValue(); + if (dia < 2 * radius) + dia = 2 * radius; + double rat1 = 0.8, rat2 = 0.2; + float f1 = pcConstraint->BeltForce1.getValue(); + float f2 = pcConstraint->BeltForce2.getValue(); + if (f1+f2 > Precision::Confusion()) { + rat1 = f1 / (f1+f2); + rat2 = f2 / (f1+f2); + } + + const SoSeparator* sep = static_cast(pShapeSep->getChild(3)); + updatePlacement(sep, 2, SbVec3f(0, dia/8 + dia/2 * rat1, 0), SbRotation()); + const SoSeparator* subsep = static_cast(sep->getChild(4)); + updateArrow(subsep, 0, dia/8 + dia/2 * rat1, dia/8); + sep = static_cast(pShapeSep->getChild(4)); + updatePlacement(sep, 2, SbVec3f(0, dia/8 + dia/2 * rat2, 0), SbRotation()); + subsep = static_cast(sep->getChild(4)); + updateArrow(subsep, 0, dia/8 + dia/2 * rat2, dia/8); + } + } + + ViewProviderFemConstraint::updateData(prop); +} diff --git a/src/Mod/Fem/Gui/ViewProviderFemConstraintPulley.h b/src/Mod/Fem/Gui/ViewProviderFemConstraintPulley.h new file mode 100644 index 0000000000..50d14d6507 --- /dev/null +++ b/src/Mod/Fem/Gui/ViewProviderFemConstraintPulley.h @@ -0,0 +1,74 @@ +/*************************************************************************** + * Copyright (c) 2013 Jan Rheinländer * + * * + * 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_VIEWPROVIDERFEMCONSTRAINTPulley_H +#define GUI_VIEWPROVIDERFEMCONSTRAINTPulley_H + +#include + +#include "ViewProviderFemConstraint.h" +#include + +class SoFontStyle; +class SoText2; +class SoBaseColor; +class SoTranslation; +class SbRotation; +class SoMaterial; +class SoLightModel; +class SoCoordinate3; +class SoIndexedLineSet; +class SoIndexedFaceSet; +class SoEventCallback; +class SoMarkerSet; + +namespace Gui { +class View3DInventorViewer; + namespace TaskView { + class TaskDialog; + } +} + +namespace FemGui +{ + +class FemGuiExport ViewProviderFemConstraintPulley : public FemGui::ViewProviderFemConstraint +{ + PROPERTY_HEADER(FemGui::ViewProviderFemConstraintPulley); + +public: + /// Constructor + ViewProviderFemConstraintPulley(); + virtual ~ViewProviderFemConstraintPulley(); + + virtual void updateData(const App::Property*); + +protected: + virtual bool setEdit(int ModNum); + +}; + +} //namespace FemGui + + +#endif // GUI_VIEWPROVIDERFEMCONSTRAINTPulley_H diff --git a/src/Mod/Fem/Gui/Workbench.cpp b/src/Mod/Fem/Gui/Workbench.cpp index e638ad8062..763c204759 100755 --- a/src/Mod/Fem/Gui/Workbench.cpp +++ b/src/Mod/Fem/Gui/Workbench.cpp @@ -29,7 +29,7 @@ #include "Workbench.h" #include -#include +#include using namespace FemGui; @@ -56,7 +56,12 @@ Gui::ToolBarItem* Workbench::setupToolBars() const Gui::ToolBarItem* fem = new Gui::ToolBarItem(root); fem->setCommand("FEM"); *fem << "Fem_CreateFromShape" - << "Fem_CreateNodesSet"; + << "Fem_CreateNodesSet" + << "Fem_ConstraintFixed" + << "Fem_ConstraintForce" + << "Fem_ConstraintBearing" + << "Fem_ConstraintGear" + << "Fem_ConstraintPulley"; return root; } @@ -68,7 +73,12 @@ Gui::MenuItem* Workbench::setupMenuBar() const root->insertItem(item, fem); fem->setCommand("&FEM"); *fem << "Fem_CreateFromShape" - << "Fem_CreateNodesSet"; + << "Fem_CreateNodesSet" + << "Fem_ConstraintFixed" + << "Fem_ConstraintForce" + << "Fem_ConstraintBearing" + << "Fem_ConstraintGear" + << "Fem_ConstraintPulley"; return root; } diff --git a/src/Mod/PartDesign/WizardShaft/InitGui.py b/src/Mod/PartDesign/WizardShaft/InitGui.py deleted file mode 100644 index 73e570ce2a..0000000000 --- a/src/Mod/PartDesign/WizardShaft/InitGui.py +++ /dev/null @@ -1,76 +0,0 @@ -# PartDesign gui init module -# (c) 2003 Juergen Riegel -# -# Gathering all the information to start FreeCAD -# This is the second one of three init scripts, the third one -# runs when the gui is up - -#*************************************************************************** -#* (c) Juergen Riegel (juergen.riegel@web.de) 2002 * -#* * -#* This file is part of the FreeCAD CAx development system. * -#* * -#* This program is free software; you can redistribute it and/or modify * -#* it under the terms of the GNU General Public License (GPL) * -#* as published by the Free Software Foundation; either version 2 of * -#* the License, or (at your option) any later version. * -#* for detail see the LICENCE text file. * -#* * -#* FreeCAD 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 FreeCAD; if not, write to the Free Software * -#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * -#* USA * -#* * -#* Juergen Riegel 2002 * -#***************************************************************************/ - -class PartDesignWorkbench ( Workbench ): - "PartDesign workbench object" - from WizardShaft import WizardShaft - Icon = """ - /* XPM */ - static char * partdesign_xpm[] = { - "16 16 9 1", - " c None", - ". c #040006", - "+ c #070F38", - "@ c #002196", - "# c #0030F3", - "$ c #5A4D20", - "% c #858EB2", - "& c #DEB715", - "* c #BFB99D", - " & ........ ", - "&&&$..@@@@@@+...", - "&&&&$@#####@..@.", - "&&&&&$......@#@.", - "&&&&&&@@@+.###@.", - "$&&&&&&@#@.###@.", - ".$&&&&&%#@.###@.", - ".@*&&&*%#@.###@.", - ".@#*&**%#@.###@.", - ".@#@%%%.@@.###@.", - ".@@@@@@@#@.###@.", - ".@#######@.###@.", - ".@#######@.##+. ", - ".+@@@####@.@.. ", - " ......+++.. ", - " ... "}; - """ - MenuText = "Part Design" - ToolTip = "Part Design workbench" - - def Initialize(self): - # load the module - import PartDesignGui - import PartDesign - def GetClassName(self): - return "PartDesignGui::Workbench" - -Gui.addWorkbench(PartDesignWorkbench()) - diff --git a/src/Mod/PartDesign/WizardShaft/SegmentFunction.py b/src/Mod/PartDesign/WizardShaft/SegmentFunction.py index a9e7c501e2..365aa30a11 100644 --- a/src/Mod/PartDesign/WizardShaft/SegmentFunction.py +++ b/src/Mod/PartDesign/WizardShaft/SegmentFunction.py @@ -21,6 +21,7 @@ # ******************************************************************************/ import FreeCAD # just for debug printing to console... +import numpy as np class SegmentFunctionSegment: "One segment of a segment function" @@ -39,6 +40,10 @@ class SegmentFunctionSegment: "Return true if the start of this segment is xval" #FIXME: 1E-9 is arbitrary here. But since units are in meters, 1E-9 is a nanometer... return abs(self.start - xval) < 1E-9 + + def isZero(self): + #FIXME: 1E-9 is arbitrary here. But since units are in meters, 1E-9 is a nanometer... + return abs(self.coefficient) < 1E-5 def value(self, xval): if xval < self.start: @@ -48,13 +53,21 @@ class SegmentFunctionSegment: def clone(self): return SegmentFunctionSegment(self.start, self.variable, self.coefficient, self.exponent) - + def negate(self): self.coefficient *= -1 + return self + + def negated(self): + return SegmentFunctionSegment(self.start, self.variable, self.coefficient * -1.0, self.exponent) + + def __mul__(self, value): + return SegmentFunctionSegment(self.start, self.variable, self.coefficient * value, self.exponent) def integrate(self): self.exponent = self.exponent + 1 self.coefficient = self.coefficient * 1 / self.exponent + return self def asString(self): return "%f * {%s - %f}^%i" % (self.coefficient, self.variable, self.start, self.exponent) @@ -69,11 +82,38 @@ class SegmentFunction: self.variable = "x" self.segments = [] self.name = name + + def findSegment(self, xval): + "Find segment valid for the given xval" + for s in self.segments: + if s.start <= xval: + return s + return self.segments[len(self.segments)] + + def isZero(self): + for s in self.segments: + if not s.isZero(): + return False + return True def negate(self): for s in self.segments: s.negate() return self + + def negated(self): + result = SegmentFunction() + result.variable = self.variable + for s in self.segments: + result.segments.append(s.negated()) + return result + + def __mul__(self, value): + result = SegmentFunction() + result.variable = self.variable + for s in self.segments: + result.segments.append(s * value) + return result def index(self, xval): "Find insert position for start value xval" @@ -90,11 +130,14 @@ class SegmentFunction: for key in sorted(dict.iterkeys()): #if abs(dict[key]) > 1E-9: self.segments.append(SegmentFunctionSegment(key, var, dict[key], 0)) + + def addSegment(self, st, coeff, exp = 0.0): + if abs(coeff) > 1E-9: + self.segments.insert(self.index(st), SegmentFunctionSegment(st, self.variable, coeff, exp)) def addSegments(self, dict): for key in sorted(dict.iterkeys()): - if abs(dict[key]) > 1E-9: - self.segments.insert(self.index(key), SegmentFunctionSegment(key, self.variable, dict[key], 0)) + self.addSegment(key, dict[key]) def setMaxX(self, mx): self.maxX = mx @@ -124,6 +167,7 @@ class SegmentFunction: "Integrate all segments with respect to the variable" for s in self.segments: s.integrate() + return self def integrated(self): "Return a copy of self integrated with respect to the variable" @@ -156,3 +200,206 @@ class SegmentFunction: FreeCAD.Console.PrintMessage(" + ") FreeCAD.Console.PrintMessage("\n") +class IntervalFunction: + "Function defined in intervals" + intervals = [] # vector of tuples (begin, length) + values = [] # vector of constant values applicable for this interval + + def __init__(self): + self.intervals = [] + self.values = [] + + def addInterval(self, begin, length, value): + self.intervals.append((begin, length)) + self.values.append(value) + + def value(self, xval): + for i in range(len(self.intervals)): + if xval >= self.intervals[i][0] and xval < self.intervals[i][0] + self.intervals[i][1]: + return self.values[i] + return self.values[len(self.values)-1] + + def lowervalue(self, xval): + return self.value(xval - 1E-8) + + def index(self, xval): + lastStart = 0.0 + for i in range(len(self.intervals)): + newStart = self.intervals[i][0] + if (xval >= lastStart) and (xval < newStart): + return i-1 + lastStart = newStart + return len(self.intervals)-1 + + def interval(self, xval): + "Return interval (begin, length) for this xval" + return self.intervals[self.index(xval)] + + def begin(self, xval): + return self.intervals[self.index(xval)][0] + + def length(self, xval): + return self.intervals[self.index(xval)][1] + +class StressFunction: + "Specialization for segment-wise display of stresses" + # The hairy thing about this is that the segments of the segfunc usually do not correspond with the intervals of the intfunc! + segfunc = None # The segment function for the force/moment + intfunc = None # The divisors, an interval function giving a specific value for each interval + name = "sigma" + + def __init__(self, f, i): + self.segfunc = f + self.intfunc = i + + def isZero(self): + return self.segfunc.isZero() + + def evaluate(self, maxX, pointsX): + # Note: This usually creates a few more points than specified in pointsX + offset = (maxX - self.segfunc.segments[0].start) / (pointsX - 1) + xvals = set([self.segfunc.segments[0].start + s * offset for s in range(pointsX)]) + starts = set([self.segfunc.segments[i].start for i in range(len(self.segfunc.segments))]) + xvals = xvals.union(starts) # Make sure we have a point on each segment start + divs = set([self.intfunc.intervals[i][0] for i in range(len(self.intfunc.intervals))]) + xvals = xvals.union(divs) + + xresult = [] + yresult = [] + for xval in sorted(xvals): + if xval in starts: + # create double point at segment border + xresult.append(xval) + yresult.append(self.segfunc.lowervalue(xval) / self.intfunc.value(xval)) + if (xval in divs): + # create double point at divisor border + xresult.append(xval) + yresult.append(self.segfunc.value(xval) / self.intfunc.lowervalue(xval)) + xresult.append(xval) + yresult.append(self.segfunc.value(xval) / self.intfunc.value(xval)) + return (xresult, yresult) + +class TranslationFunction: + "Specialization for segment-wise display of translations" + tangfunc = None # The segment function for the tangent to the bending line + transfunc = None # The segment function for translations of the shaft (the bending line) + intfunc = None # The divisors, a vector of tuples (location, divisor) + boundaries = {} # The boundary conditions, dictionary of location:[left boundary, right boundary] + module = 2.1E12 + name = "w" + + def __init__(self, f, E, d, tangents, translations): + if f.isZero(): + return + # Note: Integration has to be segment-wise because the area moment is not constant in different segments. But this only becomes relevant + # when boundary conditions are being applied + # E I_i w_i'(x) = tangfunc + C_i0 + self.tangfunc = f.integrated() + self.tangfunc.name = "w'" + self.tangfunc.output() + # E I_i w_i(x) = transfunc + C_i0 x + C_i1 + self.transfunc = self.tangfunc.integrated() # + C_i0 * x + C_i1 (integration constants for interval number i) + self.transfunc.name = "w" + self.transfunc.output() + self.module = E + self.intfunc = d + + # Solve boundary conditions. There are two types: + # External boundary conditions, e.g. a given tangent direction or translation value at a given x-value + # Internal boundary conditions, i.e. at the segment borders the tangent direction and translation of the lines must be equal + # Note that the relevant boundaries are those of the intfunc (where the area moment of the shaft cross-section changes) + # Every interval of the transfunc has two integration constants C_i0 and C_i1 that need to be defined + # Matrix of coefficients + A = np.zeros(shape = (2 * len(self.intfunc.intervals), 2 * len(self.intfunc.intervals))) + # Vector of RHS values + b = np.zeros(shape = 2 * len(self.intfunc.intervals)) + # Current row where coefficients of next equation will be added + row = 0 + + # First look at external boundary conditions + for bound in tangents: + xval = bound[0] + tang = bound[1] + i = self.intfunc.index(xval) # index of this segment + I_i = self.intfunc.value(xval) # Area moment of this segment + # w_i'(xval) = tang => (tangfunc(xval) + C_i0) / (E * I_i) = tang => C_i0 = tang * (E * I_i) - tangfunc(xval) + A[row][2 * i] = 1.0 + b[row] = tang * E * I_i - self.tangfunc.value(xval) + row += 1 + for bound in translations: + xval = bound[0] + trans = bound[1] + i = self.intfunc.index(xval) # index of this segment + I_i = self.intfunc.value(xval) # Area moment of this segment + # w_i(xval) = trans => (transfunc(xval) + C_i0 * xval + C_i1) / (E * I_i) = trans => xval / (E * I_i) * C_i0 + 1 / (E * I_i) * C_i1 = trans - transfunc(xval) / (E * I_i) + A[row][2 * i] = xval / (E * I_i) + A[row][2 * i + 1] = 1 / (E * I_i) + b[row] = trans - self.transfunc.value(xval) / (E * I_i) + row += 1 + + # Now look at internal boundary conditions (n intervals have n-1 common segment boundaries) + for i in range(len(self.intfunc.intervals) - 1): + x_start = self.intfunc.intervals[i][0] + x_end = x_start + self.intfunc.intervals[i][1] + I_i = self.intfunc.value(x_start) # Area moment of this segment + I_ip1 = self.intfunc.value(x_end) + # w_i'(x_end) = w_i+1'(xend) => (tangfunc(x_end) + C_i0) / (E * I_i) = (tangfunc(x_end) * C_i+1,0) / (E * I_i+1) + # => 1 / (E * I_i) C_i0 - 1 / (E * I_i+1) * C_i+1,0 = tangfunc(x_end) / (E * I_i+1) - tangfunc(x_end) / (E * I_i) + A[row][2 * i] = 1 / (E * I_i) + A[row][2 * (i+1)] = -1 / (E * I_ip1) + b[row] = self.tangfunc.value(x_end) / (E * I_ip1) - self.tangfunc.value(x_end) / (E * I_i) + row += 1 + # w_i(x_end) = w_i+1(xend) => (transfunc(x_end) + C_i0 * x_end + C_i1) / (E * I_i) = (transfunc(x_end) * C_i+1,0) * x_end + C_i+1,1) / (E * I_i+1) + # => x_end / (E * I_i) C_i0 + 1 / (E * I_i) C_i1 - x_end / (E * I_i+1) * C_i+1,0 - 1 / (E * I_i+1) * C_i+1,1 = transfunc(x_end) / (E * I_i+1) - transfunc(x_end) / (E * I_i) + A[row][2 * i] = x_end / (E * I_i) + A[row][2 * i + 1] = 1 / (E * I_i) + A[row][2 * (i+1)] = -x_end / (E * I_ip1) + A[row][2 * (i+1) + 1] = -1 / (E * I_ip1) + b[row] = self.transfunc.value(x_end) / (E * I_ip1) - self.transfunc.value(x_end) / (E * I_i) + row += 1 + + #FreeCAD.Console.PrintMessage(A) + #FreeCAD.Console.PrintMessage(" * x = ") + #FreeCAD.Console.PrintMessage(b) + #FreeCAD.Console.PrintMessage("\n") + + try: + self.boundaries = np.linalg.solve(A, b) # A * self.boundaries = b + except np.linalg.linalg.LinAlgError, e: + FreeCAD.Console.PrintMessage(e.message) + FreeCAD.Console.PrintMessage(". No solution possible.\n") + return + + def isZero(self): + if self.transfunc is None: + return True + return self.transfunc.isZero() + + def evaluate(self, maxX, pointsX): + # Note: This usually creates a few more points than specified in pointsX + offset = (maxX - self.transfunc.segments[0].start) / (pointsX - 1) + xvals = set([self.transfunc.segments[0].start + s * offset for s in range(pointsX)]) + starts = set([self.transfunc.segments[i].start for i in range(len(self.transfunc.segments))]) + xvals = xvals.union(starts) # Make sure we have a point on each segment start + divs = set([self.intfunc.intervals[i][0] for i in range(len(self.intfunc.intervals))]) + xvals = xvals.union(divs) + E = self.module + + xresult = [] + yresult = [] + for xval in sorted(xvals): + if xval in divs: + i = self.intfunc.index(xval) + (begin, length) = self.intfunc.interval(xval) + I_i = self.intfunc.value(xval) + C_i0 = self.boundaries[2 * i] + C_i1 = self.boundaries[2 * i + 1] + FreeCAD.Console.PrintMessage("Interval %u: %f to %f, I_i: %f, C_i0: %f, C_i1: %f\n" % (i, begin, length, I_i, C_i0, C_i1)) + + xresult.append(xval) + # w(xval) = (transfunc(xval) + C_i0 * xval + C_i1) / (E * I_i) + value = (self.transfunc.value(xval) + C_i0 * xval + C_i1) / (E * I_i) + yresult.append(value) + + return (xresult, yresult) + diff --git a/src/Mod/PartDesign/WizardShaft/Shaft.py b/src/Mod/PartDesign/WizardShaft/Shaft.py index e426812c85..6503e7c314 100644 --- a/src/Mod/PartDesign/WizardShaft/Shaft.py +++ b/src/Mod/PartDesign/WizardShaft/Shaft.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- #/****************************************************************************** # * Copyright (c)2012 Jan Rheinlaender * # * * @@ -20,18 +21,18 @@ # * * # ******************************************************************************/ -import FreeCAD, FreeCADGui # FreeCAD just required for debug printing to the console... -from SegmentFunction import SegmentFunction +import FreeCAD, FreeCADGui +from SegmentFunction import SegmentFunction, IntervalFunction, StressFunction, TranslationFunction from ShaftFeature import ShaftFeature from ShaftDiagram import Diagram +import math class ShaftSegment: length = 0.0 diameter = 0.0 innerdiameter = 0.0 - loadType = "None" - loadSize = 0.0 - loadLocation = 0.0 + constraintType = "None" + constraint = None def __init__(self, l, d, di): self.length = l @@ -40,22 +41,47 @@ class ShaftSegment: class Shaft: "The axis of the shaft is always assumed to correspond to the X-axis" + parent = None + doc = None # List of shaft segments (each segment has a different diameter) segments = [] - # The sketch - sketch = 0 - #featureWindow = None + # The feature + feature = 0 # The diagrams diagrams = {} # map of function name against Diagram object # Calculation of shaft - Qy = 0 # force in direction of y axis - Qz = 0 # force in direction of z axis - Mbz = 0 # bending moment around z axis - Mby = 0 # bending moment around y axis - Mtz = 0 # torsion moment around z axis + F = [None, None, None] # force in direction of [x,y,z]-axis + M = [None, None, None] # bending moment around [x,z,y]-axis + w = [None, None, None] # Shaft translation due to bending + sigmaN = [None, None, None] # normal stress in direction of x-axis, shear stress in direction of [y,z]-axis + sigmaB = [None, None, None] # # torque stress around x-axis, maximum bending stress in direction of [y,z]-axis + # Names (note Qy corresponds with Mz, and Qz with My) + Fstr = ["Nx","Qy","Qz"] # Forces + Mstr = ["Mx","Mz","My"] # Moments + wstr = ["", "wy", "wz"] # Translations + sigmaNstr = ["sigmax","sigmay","sigmaz"] # Normal/shear stresses + sigmaBstr = ["taut","sigmabz", "sigmaby"] # Torsion/bending stresses + # For diagram labelling + Qstrings = (("Normal force [x]", "x", "mm", "N_x", "N"), + ("Shear force [y]", "x", "mm", "Q_y", "N"), + ("Shear force [z]", "x", "mm", "Q_z", "N")) + Mstrings = (("Torque [x]", "x", "mm", "M_t", "Nm"), + ("Bending moment [z]", "x", "mm", "M_{b,z}", "Nm"), + ("Bending moment [y]", "x", "mm", "M_{b,y}", "Nm")) + wstrings = (("", "", "", "", ""), + ("Translation [y]", "x", "mm", "w_y", "mm"), + ("Translation [z]", "x", "mm", "w_z", "mm")) + sigmaNstrings = (("Normal stress [x]", "x", "mm", "\sigma_x", u"N/mm²"), + ("Shear stress [y]", "x", "mm", "\sigma_y", u"N/mm²"), + ("Shear stress [z]", "x", "mm", "\sigma_z", u"N/mm²")) + sigmaBstrings = (("Torque stress [x]", "x", "mm", "\tau_t", u"N/mm²"), + ("Bending stress [z]", "x", "mm", "\sigma_{b,z}", u"N/mm²"), + ("Bending stress [y]", "x", "mm", "\sigma_{b,y}", u"N/mm²")) - def __init__(self, doc): - self.sketch = ShaftFeature(doc) + def __init__(self, parent): + self.parent = parent + self.doc = parent.doc + self.feature = ShaftFeature(self.doc) def getLengthTo(self, index): "Get the total length of all segments up to the given one" @@ -65,40 +91,81 @@ class Shaft: return result def addSegment(self, l, d, di): - #print "Adding segment: ", l, " : ", d self.segments.append(ShaftSegment(l,d,di)) - self.sketch.addSegment(l, d, di) - # We don't call equilibrium() here because the new segment has no loads defined yet + self.feature.addSegment(l, d, di) + # We don't call equilibrium() here because the new segment has no constraints defined yet + # Fix face reference of fixed segment if it is the last one + for i in range(1, len(self.segments)): + if self.segments[i].constraintType is not "Fixed": + continue + if i == len(self.segments) - 1: + self.segments[index].constraint.References = [( self.feature.feature, "Face%u" % (2 * (index+1) + 1) )] + else: + # Remove reference since it is now in the middle of the shaft (which is not allowed) + self.segments[index].constraint.References = [(None, "")] def updateSegment(self, index, length = None, diameter = None, innerdiameter = None): oldLength = self.segments[index].length - #print "Old length of ", index, ": ", oldLength, ", new Length: ", length, " diameter: ", diameter + if length is not None: self.segments[index].length = length if diameter is not None: self.segments[index].diameter = diameter if innerdiameter is not None: self.segments[index].innerdiameter = innerdiameter - self.sketch.updateSegment(index, oldLength, self.segments[index].length, - self.segments[index].diameter, self.segments[index].innerdiameter) + + self.feature.updateSegment(index, oldLength, self.segments[index].length, self.segments[index].diameter, self.segments[index].innerdiameter) self.equilibrium() self.updateDiagrams() - def updateLoad(self, index, loadType = None, loadSize = None, loadLocation = None): - if (loadType is not None): - self.segments[index].loadType = loadType - if (loadSize is not None): - self.segments[index].loadSize = loadSize - if (loadLocation is not None): - if (loadLocation >= 0) and (loadLocation <= self.segments[index].length): - self.segments[index].loadLocation = loadLocation - else: - # TODO: Show warning - FreeCAD.Console.PrintMessage("Load location must be inside segment\n") - - #self.feature.updateForces() graphical representation of the forces + def updateConstraint(self, index, constraintType): + if (constraintType is not None): + # Did the constraint type change? + if (self.segments[index].constraintType != "None") and (self.segments[index].constraintType != constraintType): + self.doc.removeObject(self.segments[index].constraint.Name) + self.segments[index].constraint = None + + self.segments[index].constraintType = constraintType + + # Create constraint if it does not exist yet or has changed + if self.segments[index].constraint is None: + if (constraintType == "Force"): + # TODO: Create a reference point and put the force onto it + constraint = self.doc.addObject("Fem::ConstraintForce","ShaftConstraintForce") + constraint.Force = 1000.0 + self.segments[index].constraint = constraint + elif (constraintType == "Fixed"): + # TODO: Use robust reference as soon as it is available for the face + constraint = self.doc.addObject("Fem::ConstraintFixed","ShaftConstraintFixed") + if index == 0: + constraint.References = [( self.feature.feature, "Face1")] + elif index == len(self.segments) - 1: + constraint.References = [( self.feature.feature, "Face%u" % (2 * (index+1) + 1) )] + self.segments[index].constraint = constraint + elif (constraintType == "Bearing"): + # TODO: Use robust reference as soon as it is available for the cylindrical face reference + constraint = self.doc.addObject("Fem::ConstraintBearing","ShaftConstraintBearing") + constraint.References = [( self.feature.feature, "Face%u" % (2 * (index+1)) )] + constraint.AxialFree = True + self.segments[index].constraint = constraint + elif (constraintType == "Pulley"): + constraint= self.doc.addObject("Fem::ConstraintPulley","ShaftConstraintPulley") + constraint.References = [( self.feature.feature, "Face%u" % (2 * (index+1)) )] + self.segments[index].constraint = constraint + elif (constraintType == "Gear"): + constraint = self.doc.addObject("Fem::ConstraintGear","ShaftConstraintGear") + constraint.References = [( self.feature.feature, "Face%u" % (2 * (index+1)) )] + self.segments[index].constraint = constraint + self.equilibrium() self.updateDiagrams() + + def editConstraint(self, index): + if (self.segments[index].constraint is not None): + FreeCADGui.activeDocument().setEdit(self.segments[index].constraint.Name) + + def getConstraint(self, index): + return self.segments[index].constraint def updateEdge(self, column, start): App.Console.PrintMessage("Not implemented yet - waiting for robust references...") @@ -132,114 +199,375 @@ class Shaft: # FIXME: This is impossible without robust references anchored in the sketch!!! return - def updateDiagrams(self): - if (self.Qy == 0) or (self.Mbz == 0): - return - if self.Qy.name in self.diagrams: - # Update diagram - self.diagrams[self.Qy.name].update(self.Qy, self.getLengthTo(len(self.segments)) / 1000.0) - else: - # Create diagram - self.diagrams[self.Qy.name] = Diagram() - self.diagrams[self.Qy.name].create("Shear force", self.Qy, self.getLengthTo(len(self.segments)) / 1000.0, "x", "mm", 1000.0, "Q_y", "N", 1.0, 10) - if self.Mbz.name in self.diagrams: - # Update diagram - self.diagrams[self.Mbz.name].update(self.Mbz, self.getLengthTo(len(self.segments)) / 1000.0) - else: - # Create diagram - self.diagrams[self.Mbz.name] = Diagram() - self.diagrams[self.Mbz.name].create("Bending moment", self.Mbz, self.getLengthTo(len(self.segments)) / 1000.0, "x", "mm", 1000.0, "M_{b,z}", "Nm", 1.0, 10) + def updateDiagrams(self): + for ax in range(3): + if self.F[ax] is not None: + if self.F[ax].name in self.diagrams: + self.diagrams[self.F[ax].name].update(self.F[ax], self.getLengthTo(len(self.segments)) / 1000.0) + if self.M[ax] is not None: + if self.M[ax].name in self.diagrams: + self.diagrams[self.M[ax].name].update(self.M[ax], self.getLengthTo(len(self.segments)) / 1000.0) + if self.w[ax] is not None: + if self.w[ax].name in self.diagrams: + self.diagrams[self.w[ax].name].update(self.w[ax], self.getLengthTo(len(self.segments)) / 1000.0) + if self.sigmaN[ax] is not None: + if self.sigmaN[ax].name in self.diagrams: + self.diagrams[self.sigmaN[ax].name].update(self.sigmaN[ax], self.getLengthTo(len(self.segments)) / 1000.0) + if self.sigmaB[ax] is not None: + if self.sigmaB[ax].name in self.diagrams: + self.diagrams[self.sigmaB[ax].name].update(self.sigmaB[ax], self.getLengthTo(len(self.segments)) / 1000.0) + def showDiagram(self, which): + if which in self.Fstr: + ax = self.Fstr.index(which) + text = self.Qstrings[ax] + if self.F[ax] == None: + # No data + return + if self.F[ax].name in self.diagrams: + # Diagram is already open, close it again + self.diagrams[self.F[ax].name].close() + del (self.diagrams[self.F[ax].name]) + return + self.diagrams[self.F[ax].name] = Diagram() + self.diagrams[self.F[ax].name].create(text[0], self.F[ax], self.getLengthTo(len(self.segments)) / 1000.0, text[1], text[2], 1000.0, text[3], text[4], 1.0, 10) + elif which in self.Mstr: + ax = self.Mstr.index(which) + text = self.Mstrings[ax] + if self.M[ax] == None: + # No data + return + if self.M[ax].name in self.diagrams: + # Diagram is already open, close it again + self.diagrams[self.M[ax].name].close() + del (self.diagrams[self.M[ax].name]) + return + self.diagrams[self.M[ax].name] = Diagram() + self.diagrams[self.M[ax].name].create(text[0], self.M[ax], self.getLengthTo(len(self.segments)) / 1000.0, text[1], text[2], 1000.0, text[3], text[4], 1.0, 20) + elif which in self.wstr: + ax = self.wstr.index(which) + text = self.wstrings[ax] + if self.w[ax] == None: + # No data + return + if self.w[ax].name in self.diagrams: + # Diagram is already open, close it again + self.diagrams[self.w[ax].name].close() + del (self.diagrams[self.w[ax].name]) + return + self.diagrams[self.w[ax].name] = Diagram() + self.diagrams[self.w[ax].name].create(text[0], self.w[ax], self.getLengthTo(len(self.segments)) / 1000.0, text[1], text[2], 1000.0, text[3], text[4], 1000.0, 30) + elif which in self.sigmaNstr: + ax = self.sigmaNstr.index(which) + text = self.sigmaNstrings[ax] + if self.sigmaN[ax] == None: + # No data + return + if self.sigmaN[ax].name in self.diagrams: + # Diagram is already open, close it again + self.diagrams[self.sigmaN[ax].name].close() + del (self.diagrams[self.sigmaN[ax].name]) + return + self.diagrams[self.sigmaN[ax].name] = Diagram() + self.diagrams[self.sigmaN[ax].name].create(text[0], self.sigmaN[ax], self.getLengthTo(len(self.segments)) / 1000.0, text[1], text[2], 1000.0, text[3], text[4], 1.0E-6, 10) + elif which in self.sigmaBstr: + ax = self.sigmaBstr.index(which) + text = self.sigmaBstrings[ax] + if self.sigmaB[ax] == None: + # No data + return + if self.sigmaB[ax].name in self.diagrams: + # Diagram is already open, close it again + self.diagrams[self.sigmaB[ax].name].close() + del (self.diagrams[self.sigmaB[ax].name]) + return + self.diagrams[self.sigmaB[ax].name] = Diagram() + self.diagrams[self.sigmaB[ax].name].create(text[0], self.sigmaB[ax], self.getLengthTo(len(self.segments)) / 1000.0, text[1], text[2], 1000.0, text[3], text[4], 1.0E-6, 20) + + def addTo(self, dict, location, value): + if location not in dict: + dict[location] = value + else: + dict[location] += value + def equilibrium(self): # Build equilibrium equations - forces = {0.0:0.0} # dictionary of (location : outer force) - moments = {0.0:0.0} # dictionary of (location : outer moment) - variableNames = [""] # names of all variables - locations = {} # dictionary of (variableName : location) - coefficientsFy = [0] # force equilibrium equation - coefficientsMbz = [0] # moment equilibrium equation - - for i in range(len(self.segments)): - lType = self.segments[i].loadType - load = -1 # -1 means unknown (just for debug printing) - location = -1 - - if lType == "Fixed": - # Fixed segment - if i == 0: - location = 0 - variableNames.append("Fy%u" % i) - coefficientsFy.append(1) - coefficientsMbz.append(0) - variableNames.append("Mz%u" % i) - coefficientsFy.append(0) - coefficientsMbz.append(1) # Force does not contribute because location is zero - elif i == len(self.segments) - 1: - location = self.getLengthTo(len(self.segments)) / 1000 - variableNames.append("Fy%u" % i) - coefficientsFy.append(1) - coefficientsMbz.append(location) - variableNames.append("Mz%u" % i) - coefficientsFy.append(0) - coefficientsMbz.append(1) - else: - # TODO: Better error message - FreeCAD.Console.PrintMessage("Fixed constraint must be at beginning or end of shaft\n") - return - - locations["Fy%u" % i] = location - locations["Mz%u" % i] = location - elif lType == "Static": - # Static load (currently force only) - load = self.segments[i].loadSize - location = (self.getLengthTo(i) + self.segments[i].loadLocation) / 1000 # convert to meters - coefficientsFy[0] = coefficientsFy[0] - load - forces[location] = load - coefficientsMbz[0] = coefficientsMbz[0] - load * location - moments[location] = 0 - #elif lType == "None": - # # No loads on segment - - FreeCAD.Console.PrintMessage("Segment: %u, type: %s, load: %f, location: %f\n" % (i, lType, load, location)) - - self.printEquilibrium(variableNames, coefficientsFy) - self.printEquilibrium(variableNames, coefficientsMbz) - - # Build matrix and vector for linear algebra solving algorithm try: import numpy as np except ImportError: FreeCAD.Console.PrintMessage("numpy is not installed on your system\n") raise ImportError("numpy not installed") - if (len(coefficientsFy) < 3) or (len(coefficientsMbz) < 3): - return - A = np.array([coefficientsFy[1:], coefficientsMbz[1:]]) - b = np.array([coefficientsFy[0], coefficientsMbz[0]]) - solution = np.linalg.solve(A, b) + + # Initialization of structures. All three axes are handled separately so everything is 3-fold + # dictionaries of (location : outer force/moment) with reverse sign, which means that the segment functions for the section force and section moment + # created from them will have signs as by the convention in + # http://www.umwelt-campus.de/ucb/fileadmin/users/90_t.preussler/dokumente/Skripte/TEMECH/TMI/Ebene_Balkenstatik.pdf (page 10) + # (see also example on page 19) + forces = [{0.0:0.0}, {0.0:0.0}, {0.0:0.0}] + moments = [{0.0:0.0}, {0.0:0.0}, {0.0:0.0}] + # Boundary conditions for shaft bending line + tangents = [[], [], []] # Tangents to shaft bending line + translations = [[], [], []] # Shaft displacement + # Variable names, e.g. Fx, Mz. Because the system must be exactly determined, not more than two independent variables for each + # force/moment per axis are possible (if there are more no solution is calculated) + variableNames = [[""], [""], [""]] + # # dictionary of (variableName : location) giving the x-coordinate at which the force/moment represented by the variable acts on the shaft + locations = {} + # Coefficients of the equilibrium equations in the form a = b * F1 + c * F2 and d = e * M1 + f * M2 + # LHS (variables a1, a2, a3, d3) initialized to zero + coefficientsF = [[0], [0], [0]] + coefficientsM = [[0], [0], [0]] - # Complete dictionary of forces and moments - if variableNames[1][0] == "F": - forces[locations[variableNames[1]]] = solution[0] - else: - moments[locations[variableNames[1]]] = solution[0] + for i in range(len(self.segments)): + cType = self.segments[i].constraintType + constraint = self.segments[i].constraint + + if cType == "Fixed": + # Fixed segment + if i == 0: + # At beginning of shaft + location = 0 + elif i == len(self.segments) - 1: + # At end of shaft + location = self.getLengthTo(len(self.segments)) / 1000.0 # convert to meters + else: + # TODO: Better error message + FreeCAD.Console.PrintMessage("Fixed constraint must be at beginning or end of shaft\n") + return + + for ax in range(3): + # Create a new reaction force + variableNames[ax].append("%s%u" % (self.Fstr[ax], i)) + coefficientsF[ax].append(1) + # Register location of reaction force + locations["%s%u" % (self.Fstr[ax], i)] = location + # Boundary conditions for the translations + tangents[ax].append((location, 0.0)) + translations[ax].append((location, 0.0)) + coefficientsM[0].append(0) # Reaction force contributes no moment around x axis + coefficientsM[1].append(location) # Reaction force contributes a positive moment around z axis + coefficientsM[2].append(-location) # Reaction force contributes a negative moment around y axis + + for ax in range(3): + # Create a new reaction moment + variableNames[ax].append("%s%u" % (self.Mstr[ax], i)) + coefficientsF[ax].append(0) + coefficientsM[ax].append(1) + locations["%s%u" % (self.Mstr[ax], i)] = location + + elif cType == "Force": + # Static force (currently force on midpoint of segment only) + force = constraint.DirectionVector.multiply(constraint.Force) + # TODO: Extract value of the location from geometry + location = (self.getLengthTo(i) + self.segments[i].length/2.0) / 1000.0 + # The force itself + for ax in range(3): + if abs(force[ax]) > 0.0: + coefficientsF[ax][0] = coefficientsF[ax][0] - force[ax] # neg. because this coefficient is on the LHS of the equilibrium equation + self.addTo(forces[ax], location, -force[ax]) # neg. to fulfill the convention mentioned above + # Moments created by the force (by definition no moment is created by the force in x-direction) + if abs(force[1]) > 0.0: + coefficientsM[1][0] = coefficientsM[1][0] - force[1] * location # moment around z-axis + self.addTo(moments[1], location, 0) + if abs(force[2]) > 0.0: + coefficientsM[2][0] = coefficientsM[2][0] + force[2] * location # moment around y-axis + self.addTo(moments[2], location, 0) # No outer moment acts here! + + elif cType == "Bearing": + location = constraint.BasePoint.x / 1000.0 # TODO: This assumes that the shaft feature starts with the first segment at (0,0,0) and its axis corresponds to the x-axis + # Bearing reaction forces. TODO: the bearing is assumed to not induce any reaction moments + start = (0 if constraint.AxialFree == False else 1) + for ax in range(start, 3): + variableNames[ax].append("%s%u" % (self.Fstr[ax], i)) + coefficientsF[ax].append(1) + locations["%s%u" % (self.Fstr[ax], i)] = location + # Boundary condition + translations[ax].append((location, 0.0)) + if constraint.AxialFree == False: + coefficientsM[0].append(0) # Reaction force contributes no moment around x axis + coefficientsM[1].append(location) # Reaction force contributes a positive moment around z axis + coefficientsM[2].append(-location) # Reaction force contributes a negative moment around y axis + + elif cType == "Gear": + force = constraint.DirectionVector.multiply(constraint.Force) + location = constraint.BasePoint.x / 1000.0 + lever = [0, constraint.Diameter/2.0/1000.0 * math.sin(constraint.ForceAngle / 180.0 * math.pi), + constraint.Diameter/2.0 /1000.0* math.cos(constraint.ForceAngle / 180.0 * math.pi)] + + # Effect of the gear force + for ax in range(3): + if abs(force[ax]) > 0.0: + # Effect of the force + coefficientsF[ax][0] = coefficientsF[ax][0] - force[ax] + self.addTo(forces[ax], location, -force[ax]) + # Moments created by the force (by definition no moment is created by the force in x-direction) + if abs(force[1]) > 0.0: + coefficientsM[1][0] = coefficientsM[1][0] - force[1] * location # moment around z-axis + self.addTo(moments[1], location, 0) + if abs(force[2]) > 0.0: + coefficientsM[2][0] = coefficientsM[2][0] + force[2] * location # moment around y-axis + self.addTo(moments[2], location, 0) # No outer moment acts here! + + # Moments created by the force and lever + if abs(force[0]) > 0.0: + momenty = force[0] * lever[2] + momentz = force[0] * lever[1] + coefficientsM[1][0] = coefficientsM[1][0] + momentz # moment around z-axis + self.addTo(moments[1], location, momentz) + coefficientsM[2][0] = coefficientsM[2][0] - momenty # moment around y-axis + self.addTo(moments[2], location, -momenty) + if abs(force[1]) > 0.0: + moment = force[1] * lever[2] + coefficientsM[0][0] = coefficientsM[0][0] + moment + self.addTo(moments[0], location, moment) + if abs(force[2]) > 0.0: + moment = force[2] * lever[1] + coefficientsM[0][0] = coefficientsM[0][0] - moment + self.addTo(moments[0], location, -moment) + elif cType == "Pulley": + forceAngle1 = (constraint.ForceAngle + constraint.BeltAngle + 90.0) / 180.0 * math.pi + forceAngle2 = (constraint.ForceAngle - constraint.BeltAngle + 90.0) / 180.0 * math.pi + #FreeCAD.Console.PrintMessage("BeltForce1: %f, BeltForce2: %f\n" % (constraint.BeltForce1, constraint.BeltForce2)) + #FreeCAD.Console.PrintMessage("Angle1: %f, Angle2: %f\n" % (forceAngle1, forceAngle2)) + force = [0, -constraint.BeltForce1 * math.sin(forceAngle1) - constraint.BeltForce2 * math.sin(forceAngle2), + constraint.BeltForce1 * math.cos(forceAngle1) + constraint.BeltForce2 * math.cos(forceAngle2)] + location = constraint.BasePoint.x / 1000.0 + + # Effect of the pulley forces + for ax in range(3): + if abs(force[ax]) > 0.0: + # Effect of the force + coefficientsF[ax][0] = coefficientsF[ax][0] - force[ax] + self.addTo(forces[ax], location, -force[ax]) + # Moments created by the force (by definition no moment is created by the force in x-direction) + if abs(force[1] ) > 0.0: + coefficientsM[1][0] = coefficientsM[1][0] - force[1] * location # moment around z-axis + self.addTo(moments[1], location, 0) + if abs(force[2]) > 0.0: + coefficientsM[2][0] = coefficientsM[2][0] + force[2] * location # moment around y-axis + self.addTo(moments[2], location, 0) # No outer moment acts here! + + # Torque + moment = constraint.Force * (1 if constraint.IsDriven is True else -1) + coefficientsM[0][0] = coefficientsM[0][0] + moment + self.addTo(moments[0], location, moment) - if variableNames[2][0] == "F": - forces[locations[variableNames[2]]] = solution[1] - else: - moments[locations[variableNames[2]]] = solution[1] + areas = [None, None, None] + areamoments = [None, None, None] + bendingmoments = [None, None, None] + torquemoments = [None, None, None] + + for ax in range(3): + FreeCAD.Console.PrintMessage("Axis: %u\n" % ax) + self.printEquilibrium(variableNames[ax], coefficientsF[ax]) + self.printEquilibrium(variableNames[ax], coefficientsM[ax]) - FreeCAD.Console.PrintMessage(forces) - FreeCAD.Console.PrintMessage(moments) - self.Qy = SegmentFunction("Qy") - self.Qy.buildFromDict("x", forces) - self.Qy.output() - self.Mbz = self.Qy.integrated().negate() - self.Mbz.addSegments(moments) # takes care of boundary conditions - self.Mbz.name = "Mbz" - self.Mbz.output() + if len(coefficientsF[ax]) <= 1: + # Note: coefficientsF and coefficientsM always have the same length + FreeCAD.Console.PrintMessage("Matrix is singular, no solution possible\n") + self.parent.updateButtons(ax, False) + continue + + # Handle special cases. Note that the code above should ensure that coefficientsF and coefficientsM always have same length + solution = [None, None] + if len(coefficientsF[ax]) == 2: + if coefficientsF[ax][1] != 0.0 and coefficientsF[ax][0] != 0.0: + solution[0] = coefficientsF[ax][0] / coefficientsF[ax][1] + if coefficientsM[ax][1] != 0.0 and coefficientsM[ax][0] != 0.0: + solution[1] = coefficientsM[ax][0] / coefficientsM[ax][1] + if abs(solution[0] - solution[1]) < 1E9: + FreeCAD.Console.PrintMessage("System is statically undetermined. No solution possible.\n") + self.parent.updateButtons(ax, False) + continue + else: + # Build matrix and vector for linear algebra solving algorithm + # TODO: This could easily be done manually... there are only 2 variables and 6 coefficients + A = np.array([coefficientsF[ax][1:], coefficientsM[ax][1:]]) + b = np.array([coefficientsF[ax][0], coefficientsM[ax][0]]) + try: + solution = np.linalg.solve(A, b) # A * solution = b + except np.linalg.linalg.LinAlgError, e: + FreeCAD.Console.PrintMessage(e.message) + FreeCAD.Console.PrintMessage(". No solution possible.\n") + self.parent.updateButtons(ax, False) + continue + + # Complete dictionary of forces and moments with the two reaction forces that were calculated + for i in range(2): + if solution[i] is None: + continue + FreeCAD.Console.PrintMessage("Reaction force/moment: %s = %f\n" % (variableNames[ax][i+1], solution[i])) + if variableNames[ax][i+1][0] == "M": + moments[ax][locations[variableNames[ax][i+1]]] = -solution[i] + else: + forces[ax][locations[variableNames[ax][i+1]]] = -solution[i] + + FreeCAD.Console.PrintMessage(forces[ax]) + FreeCAD.Console.PrintMessage("\n") + FreeCAD.Console.PrintMessage(moments[ax]) + FreeCAD.Console.PrintMessage("\n") + + # Forces + self.F[ax] = SegmentFunction(self.Fstr[ax]) + self.F[ax].buildFromDict("x", forces[ax]) + self.parent.updateButton(1, ax, not self.F[ax].isZero()) + self.F[ax].output() + # Moments + if ax == 0: + self.M[0] = SegmentFunction(self.Mstr[0]) + self.M[0].buildFromDict("x", moments[0]) + elif ax == 1: + self.M[1] = self.F[1].integrated().negate() + self.M[1].name = self.Mstr[1] + self.M[1].addSegments(moments[1]) # takes care of boundary conditions + elif ax == 2: + self.M[2] = self.F[2].integrated() + self.M[2].name = self.Mstr[2] + self.M[2].addSegments(moments[2]) # takes care of boundary conditions + self.parent.updateButton(2, ax, not self.M[ax].isZero()) + self.M[ax].output() + + # Areas and area moments + location = 0.0 + areas[ax] = IntervalFunction() # A [m²] + areamoments[ax] = IntervalFunction() # I [m⁴] + bendingmoments[ax] = IntervalFunction() # W_b [m³] + torquemoments[ax] = IntervalFunction() # W_t [m³] + + for i in range(len(self.segments)): + od = self.segments[i].diameter/1000.0 + id = self.segments[i].innerdiameter/1000.0 + length = self.segments[i].length/1000.0 + areas[ax].addInterval(location, length, math.pi/4.0 * (math.pow(od, 2.0) - math.pow(id, 2.0))) + areamoment = math.pi/64.0 * (math.pow(od, 4.0) - math.pow(id, 4.0)) + areamoments[ax].addInterval(location, length, areamoment) + bendingmoments[ax].addInterval(location, length, areamoment / (od / 2.0)) + torquemoments[ax].addInterval(location, length, 2 * (areamoment / (od / 2.0))) + location += length + + # Bending line + if ax > 0: + if len(tangents[ax])+ len(translations[ax]) == 2: + # TODO: Get Young's module from material type instead of using 210000 N/mm² = 2.1E12 N/m² + self.w[ax] = TranslationFunction(self.M[ax].negated(), 2.1E12, areamoments[ax], tangents[ax], translations[ax]) + self.w[ax].name= self.wstr[ax] + self.parent.updateButton(3, ax, not self.w[ax].isZero()) + else: + self.parent.updateButton(3, ax, False) + + # Normal/shear stresses and torque/bending stresses + self.sigmaN[ax] = StressFunction(self.F[ax], areas[ax]) + self.sigmaN[ax].name = self.sigmaNstr[ax] + self.parent.updateButton(4, ax, not self.sigmaN[ax].isZero()) + if ax == 0: + self.sigmaB[ax] = StressFunction(self.M[ax] , torquemoments[ax]) + else: + self.sigmaB[ax] = StressFunction(self.M[ax], bendingmoments[ax]) + self.sigmaB[ax].name = self.sigmaBstr[ax] + self.parent.updateButton(5, ax, not self.sigmaB[ax].isZero()) def printEquilibrium(self, var, coeff): - # Auxiliary method for debugging purposes + # Auxiliary method for debugging purposes for i in range(len(var)): if i == 0: FreeCAD.Console.PrintMessage("%f = " % coeff[i]) diff --git a/src/Mod/PartDesign/WizardShaft/ShaftDiagram.py b/src/Mod/PartDesign/WizardShaft/ShaftDiagram.py index 097b571c06..3954da8474 100644 --- a/src/Mod/PartDesign/WizardShaft/ShaftDiagram.py +++ b/src/Mod/PartDesign/WizardShaft/ShaftDiagram.py @@ -38,6 +38,7 @@ class Diagram: ypoints = [] # Plot object thePlot = None + win = None def create(self, title, function, xlength, xname, xunit, xscale, yname, yunit, yscale, numxpoints): # Initialize @@ -54,7 +55,7 @@ class Diagram: self.numxpoints = numxpoints # Create a plot window - win = Plot.figure(title) + self.win = Plot.figure(title) # Get the plot object from the window self.thePlot = Plot.getPlot() # Format the plot object @@ -96,3 +97,7 @@ class Diagram: axes.set_xlim(right = max(self.xpoints) * 1.05) axes.set_ylim(min(self.ypoints) * 1.05, max(self.ypoints) * 1.05) self.thePlot.update() + + def close(self): + # Close the associated mdiSubWindow + self.win.parent().close() diff --git a/src/Mod/PartDesign/WizardShaft/WizardShaft.py b/src/Mod/PartDesign/WizardShaft/WizardShaft.py index 799effb7df..15b3f1b721 100644 --- a/src/Mod/PartDesign/WizardShaft/WizardShaft.py +++ b/src/Mod/PartDesign/WizardShaft/WizardShaft.py @@ -21,7 +21,6 @@ # ******************************************************************************/ import FreeCAD, FreeCADGui -#import os from PyQt4 import QtCore, QtGui from WizardShaftTable import WizardShaftTable from Shaft import Shaft @@ -38,6 +37,8 @@ class TaskWizardShaft: shaft = 0 # Feature featureWindow = 0 + # Buttons + buttons = [[None, None, None], [None, None, None], [None, None, None], [None, None, None], [None, None, None], [None, None, None]] def __init__(self, doc): mw = QtGui.qApp.activeWindow() @@ -54,15 +55,97 @@ class TaskWizardShaft: featureWindow = cw.subWindowList()[-1] else: featureWindow = cw.activeSubWindow() - + + # Buttons for diagram display + buttons = QtGui.QGridLayout() + bnames = [["All [x]", "All [y]", "All [z]" ], + ["N [x]", "Q [y]", "Q [z]"], + ["Mt [x]", "Mb [z]", "Mb [y]"], + ["", "w [y]", "w [z]"], + ["sigma [x]", "sigma [y]", "sigma [z]"], + ["tau [x]", "sigmab [z]", "sigmab [y]"]] + slots = [[self.slotAllx, self.slotAlly, self.slotAllz], + [self.slotFx, self.slotQy, self.slotQz], + [self.slotMx, self.slotMz, self.slotMy], + [self.slotNone, self.slotWy, self.slotWz], + [self.slotSigmax, self.slotSigmay, self.slotSigmaz], + [self.slotTaut, self.slotSigmabz, self.slotSigmaby]] + for col in range(3): + for row in range(6): + button = QtGui.QPushButton(bnames[row][col]) + buttons.addWidget(button, row, col) + self.buttons[row][col] = button + button.clicked.connect(slots[row][col]) + # Create Shaft object - self.shaft = Shaft(self.doc) - - # Assign a table widget to the dock window - self.table = WizardShaftTable(self, self.shaft) - self.form = self.table.widget - self.form.setWindowTitle("Shaft wizard") - + self.shaft = Shaft(self) + # Create table widget + self.form = QtGui.QWidget() + self.table = WizardShaftTable(self, self.shaft) + + # The top layout will contain the Shaft Wizard layout plus the elements of the FEM constraints dialog + layout = QtGui.QVBoxLayout() + layout.setObjectName("ShaftWizard") # Do not change or translate: Required to detect whether Shaft Wizard is running in FemGui::ViewProviderFemConstraintXXX + sublayout = QtGui.QVBoxLayout() + sublayout.setObjectName("ShaftWizardLayout") # Do not change or translate + sublayout.addWidget(self.table.widget) + sublayout.addLayout(buttons) + layout.addLayout(sublayout) + self.form.setLayout(layout) + + # Switch to feature window + mdi=QtGui.qApp.activeWindow().findChild(QtGui.QMdiArea) + cw.setActiveSubWindow(featureWindow) + + def slotAllx(self): + self.shaft.showDiagram("Allx") + def slotAlly(self): + self.shaft.showDiagram("Ally") + def slotAllz(self): + self.shaft.showDiagram("Allz") + + def slotFx(self): + self.shaft.showDiagram("Nx") + def slotQy(self): + self.shaft.showDiagram("Qy") + def slotQz(self): + self.shaft.showDiagram("Qz") + + def slotMx(self): + self.shaft.showDiagram("Mx") + def slotMz(self): + self.shaft.showDiagram("Mz") + def slotMy(self): + self.shaft.showDiagram("My") + + def slotNone(self): + pass + def slotWy(self): + self.shaft.showDiagram("wy") + def slotWz(self): + self.shaft.showDiagram("wz") + + def slotSigmax(self): + self.shaft.showDiagram("sigmax") + def slotSigmay(self): + self.shaft.showDiagram("sigmay") + def slotSigmaz(self): + self.shaft.showDiagram("sigmaz") + + def slotTaut(self): + self.shaft.showDiagram("taut") + def slotSigmabz(self): + self.shaft.showDiagram("sigmabz") + def slotSigmaby(self): + self.shaft.showDiagram("sigmaby") + + def updateButton(self, row, col, flag): + self.buttons[row][col].setEnabled(flag) + + def updateButtons(self, col, flag): + for row in range(len(self.buttons)): + self.updateButton(row, col, flag) + def getStandardButtons(self): return int(QtGui.QDialogButtonBox.Ok) @@ -75,10 +158,20 @@ class TaskWizardShaft: del self.form return True -class WizardShaftGui: - def Activated(self): - FreeCADGui.Control.showDialog(TaskWizardShaft(FreeCAD.ActiveDocument)) +# Work-around to allow a callback +# Problem: From the FemConstraint ViewProvider, we need to tell the Shaft instance that the user finished editing the constraint +# We can find the Shaft Wizard dialog object from C++, but there is not way to reach the Shaft instance +# Also it seems to be impossible to access the active dialog from Python, so Gui::Command::runCommand() is not an option either +# Note: Another way would be to create a hidden widget in the Shaft Wizard dialog and write some data to it, triggering a slot +# in the python code +WizardShaftDlg = None +class WizardShaftGui: + def Activated(self): + global WizardShaftDlg + WizardShaftDlg = TaskWizardShaft(FreeCAD.ActiveDocument) + FreeCADGui.Control.showDialog(WizardShaftDlg) + def GetResources(self): IconPath = FreeCAD.ConfigGet("AppHomePath") + "Mod/PartDesign/WizardShaft/WizardShaft.svg" MenuText = 'Shaft design wizard...' @@ -87,8 +180,29 @@ class WizardShaftGui: def IsActive(self): return FreeCAD.ActiveDocument != None + + def __del__(self): + global WizardShaftDlg + WizardShaftDlg = None + +class WizardShaftGuiCallback: + def Activated(self): + global WizardShaftDlg + if WizardShaftDlg != None and WizardShaftDlg.table != None: + WizardShaftDlg.table.finishEditConstraint() + + def isActive(self): + global WizardShaftDlg + return (WizardShaftDlg is not None) + + def GetResources(self): + IconPath = FreeCAD.ConfigGet("AppHomePath") + "Mod/PartDesign/WizardShaft/WizardShaft.svg" + MenuText = 'Shaft design wizard...' + ToolTip = 'Start the shaft design wizard' + return {'Pixmap' : IconPath, 'MenuText': MenuText, 'ToolTip': ToolTip} FreeCADGui.addCommand('PartDesign_WizardShaft', WizardShaftGui()) +FreeCADGui.addCommand('PartDesign_WizardShaftCallBack', WizardShaftGuiCallback()) #Note: Start wizard in Python Console with # Gui.runCommand('PartDesign_WizardShaft') diff --git a/src/Mod/PartDesign/WizardShaft/WizardShaftTable.py b/src/Mod/PartDesign/WizardShaft/WizardShaftTable.py index 4120521751..0d73da049b 100644 --- a/src/Mod/PartDesign/WizardShaft/WizardShaftTable.py +++ b/src/Mod/PartDesign/WizardShaft/WizardShaftTable.py @@ -31,21 +31,18 @@ class WizardShaftTable: "Length" : 0, "Diameter" : 1, "InnerDiameter" : 2, - "LoadType" : 3, - "LoadSize" : 4, - "LoadLocation" : 5, - "StartEdgeType" : 6, - "StartEdgeSize" : 7, - "EndEdgeType" : 8, - "EndEdgeSize" : 9 + "ConstraintType" : 3, + "StartEdgeType" : 4, + "StartEdgeSize" : 5, + "EndEdgeType" : 6, + "EndEdgeSize" : 7 } rowDictReverse = {} - headers = ["Length [mm]", + headers = [ + "Length [mm]", "Diameter [mm]", "Inner diameter [mm]", - "Load type", - "Load [N]", - "Location [mm]", + "Constraint type", "Start edge type", "Start edge size", "End edge type", @@ -54,6 +51,8 @@ class WizardShaftTable: widget = 0 wizard = 0 shaft = 0 + editedRow = None + editedColumn = None def __init__(self, w, s): for key in self.rowDict.iterkeys(): @@ -62,9 +61,10 @@ class WizardShaftTable: self.wizard = w self.shaft = s # Create table widget - self.widget = QtGui.QTableWidget(len(self.rowDict), 0) + self.widget = QtGui.QTableWidget(len(self.rowDict), 0) + self.widget.setObjectName("ShaftWizardTable") # Do not change or translate: Used in ViewProviderFemConstraintXXX + self.widget.setWindowTitle("Shaft wizard") self.widget.resize(QtCore.QSize(300,200)) - #self.widget.setFocusPolicy(QtCore.Qt.StrongFocus) # Label rows and columns self.widget.setVerticalHeaderLabels(self.headers) @@ -82,14 +82,12 @@ class WizardShaftTable: self.addColumn() self.setLength(0, 40.0) self.setDiameter(0, 50.0) - self.setLoadType(0, "Static") - self.setLoadSize(0, 1000.0) - self.setLoadLocation(0, 25.0) + self.setConstraintType(0, "Bearing") # Section 2 self.addColumn() self.setLength(1, 80.0) self.setDiameter(1, 60.0) - self.setLoadType(1, "Fixed") + self.setConstraintType(1, "Force") def slotInsertColumn(self, point): # FIXME: Allow inserting columns, not just adding at the end @@ -144,32 +142,21 @@ class WizardShaftTable: widget.setValue(innerdiameter) widget.valueChanged.connect(self.slotValueChanged) widget.editingFinished.connect(self.slotEditingFinished) - # Load type + # Constraint type widget = QtGui.QComboBox(self.widget) widget.insertItem(0, "None") widget.insertItem(1, "Fixed") - widget.insertItem(2, "Static") + widget.insertItem(2, "Force") widget.insertItem(3, "Bearing") - widget.insertItem(4, "Pulley") - self.widget.setCellWidget(self.rowDict["LoadType"], index, widget) + widget.insertItem(4, "Gear") + widget.insertItem(5, "Pulley") + action = QtGui.QAction("Edit constraint", widget) + action.triggered.connect(self.slotEditConstraint) + widget.addAction(action) + widget.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu) + self.widget.setCellWidget(self.rowDict["ConstraintType"], index, widget) widget.setCurrentIndex(0) - self.widget.connect(widget, QtCore.SIGNAL("currentIndexChanged(const QString&)"), self.slotLoadType) - # Load size - widget = QtGui.QDoubleSpinBox(self.widget) - widget.setMinimum(-1E9) - widget.setMaximum(1E9) - self.widget.setCellWidget(self.rowDict["LoadSize"], index, widget) - widget.setValue(0) - widget.valueChanged.connect(self.slotValueChanged) - widget.editingFinished.connect(self.slotEditingFinished) - # Load location - widget = QtGui.QDoubleSpinBox(self.widget) - widget.setMinimum(0) - widget.setMaximum(1E9) - self.widget.setCellWidget(self.rowDict["LoadLocation"], index, widget) - widget.setValue(0) - widget.valueChanged.connect(self.slotValueChanged) - widget.editingFinished.connect(self.slotEditingFinished) + self.widget.connect(widget, QtCore.SIGNAL("currentIndexChanged(const QString&)"), self.slotConstraintType) # Start edge type widget = QtGui.QComboBox(self.widget) widget.insertItem(0, "None",) @@ -177,7 +164,7 @@ class WizardShaftTable: widget.insertItem(2, "Fillet") self.widget.setCellWidget(self.rowDict["StartEdgeType"],index, widget) widget.setCurrentIndex(0) - self.widget.connect(widget, QtCore.SIGNAL("currentIndexChanged(const QString&)"), self.slotLoadType) + #self.widget.connect(widget, QtCore.SIGNAL("currentIndexChanged(const QString&)"), self.slotLoadType) # Start edge size widget = QtGui.QDoubleSpinBox(self.widget) widget.setMinimum(0) @@ -193,7 +180,7 @@ class WizardShaftTable: widget.insertItem(2, "Fillet") self.widget.setCellWidget(self.rowDict["EndEdgeType"],index, widget) widget.setCurrentIndex(0) - self.widget.connect(widget, QtCore.SIGNAL("currentIndexChanged(const QString&)"), self.slotLoadType) + #self.widget.connect(widget, QtCore.SIGNAL("currentIndexChanged(const QString&)"), self.slotLoadType) # End edge size widget = QtGui.QDoubleSpinBox(self.widget) widget.setMinimum(0) @@ -208,6 +195,8 @@ class WizardShaftTable: self.editedValue = value def slotEditingFinished(self): + if self.editedRow == None: + return rowName = self.rowDictReverse[self.editedRow] if rowName is None: return @@ -217,12 +206,8 @@ class WizardShaftTable: self.shaft.updateSegment(self.editedColumn, diameter = self.getDoubleValue(rowName, self.editedColumn)) elif rowName == "InnerDiameter": self.shaft.updateSegment(self.editedColumn, innerdiameter = self.getDoubleValue(rowName, self.editedColumn)) - elif rowName == "LoadType": - self.shaft.updateLoad(self.editedColumn, loadType = self.getListValue(rowName, self.editedColumn)) - elif rowName == "LoadSize": - self.shaft.updateLoad(self.editedColumn, loadSize = self.getDoubleValue(rowName, self.editedColumn)) - elif rowName == "LoadLocation": - self.shaft.updateLoad(self.editedColumn, loadLocation = self.getDoubleValue(rowName, self.editedColumn)) + elif rowName == "Constraintype": + self.shaft.updateConstraint(self.editedColumn, self.getListValue(rowName, self.editedColumn)) elif rowName == "StartEdgeType": pass elif rowName == "StartEdgeSize": @@ -232,6 +217,13 @@ class WizardShaftTable: elif rowName == "EndEdgeSize": pass + def slotEditConstraint(self): + (self.editedRow, self.editedColumn) = self.getFocusedCell() # Because finishEditConstraint() will trigger slotEditingFinished() which requires this information + self.shaft.editConstraint(self.editedColumn) + + def finishEditConstraint(self): + self.shaft.updateConstraint(self.editedColumn, self.getConstraintType(self.editedColumn)) + def setLength(self, column, l): self.setDoubleValue("Length", column, l) self.shaft.updateSegment(column, length = l) @@ -254,32 +246,15 @@ class WizardShaftTable: return self.getDoubleValue("InnerDiameter", column) @QtCore.pyqtSlot('QString') - def slotLoadType(self, text): - if text != "Fixed": - if (self.getLoadSize is None) or (self.getLoadLocation is None): - return - self.shaft.updateLoad(self.getFocusedColumn(), loadType = text) + def slotConstraintType(self, text): + self.shaft.updateConstraint(self.getFocusedColumn(), text) - def setLoadType(self, column, t): - self.setListValue("LoadType", column, t) - self.shaft.updateLoad(column, loadType = t) + def setConstraintType(self, column, t): + self.setListValue("ConstraintType", column, t) + self.shaft.updateConstraint(column, t) - def getLoadType(self, column): - return self.getListValue("LoadType", column) - - def setLoadSize(self, column, s): - self.setDoubleValue("LoadSize", column, s) - self.shaft.updateLoad(column, loadSize = s) - - def getLoadSize(self, column): - return self.getDoubleValue("LoadSize", column) - - def setLoadLocation(self, column, l): - self.setDoubleValue("LoadLocation", column, l) - self.shaft.updateLoad(column, loadLocation = l) - - def getLoadLocation(self, column): - return self.getDoubleValue("LoadLocation", column) + def getConstraintType(self, column): + return self.getListValue("ConstraintType", column) def slotStartEdgeType(self, old, new): pass @@ -334,7 +309,7 @@ class WizardShaftTable: def getListValue(self, row, column): widget = self.widget.cellWidget(self.rowDict[row], column) if widget is not None: - return widget.currentText().toAscii()[0].upper() + return widget.currentText().toAscii() #[0].upper() else: return None