diff --git a/src/Mod/Fem/App/AppFem.cpp b/src/Mod/Fem/App/AppFem.cpp index a317d6133..7a29fcada 100755 --- a/src/Mod/Fem/App/AppFem.cpp +++ b/src/Mod/Fem/App/AppFem.cpp @@ -41,6 +41,7 @@ #include "FemSetNodesObject.h" #include "HypothesisPy.h" +#include "FemConstraint.h" extern struct PyMethodDef Fem_methods[]; @@ -115,6 +116,7 @@ void AppFemExport initFem() Fem::FemSetGeometryObject ::init(); Fem::FemSetNodesObject ::init(); + Fem::Constraint ::init(); } } // extern "C" diff --git a/src/Mod/Fem/App/CMakeLists.txt b/src/Mod/Fem/App/CMakeLists.txt index 5dd69680b..ba1688999 100755 --- a/src/Mod/Fem/App/CMakeLists.txt +++ b/src/Mod/Fem/App/CMakeLists.txt @@ -64,6 +64,8 @@ SET(Fem_SRCS FemMesh.h FemMeshProperty.cpp FemMeshProperty.h + FemConstraint.cpp + FemConstraint.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 000000000..ae9ef45ec --- /dev/null +++ b/src/Mod/Fem/App/FemConstraint.cpp @@ -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 * + * * + ***************************************************************************/ + + +#include "PreCompiled.h" + +#ifndef _PreComp_ +#endif + +#include "FemConstraint.h" + +#include + +using namespace Fem; + +const char* Constraint::TypeEnums[]= {"Force on geometry","Fixed", + "Bearing (radial free)", "Bearing (radial fixed)", + "Pulley", "Gear (straight toothed)", NULL}; + +PROPERTY_SOURCE(Fem::Constraint, App::DocumentObject); + +Constraint::Constraint() +{ + ADD_PROPERTY(Type,((long)0)); + Type.setEnums(TypeEnums); + ADD_PROPERTY(Force,(0.0)); + ADD_PROPERTY_TYPE(References,(0,0),"Constraint",(App::PropertyType)(App::Prop_None),"Elements where the constraint is applied"); + ADD_PROPERTY_TYPE(Direction,(0),"Constraint",(App::PropertyType)(App::Prop_None),"Element giving direction of constraint"); + ADD_PROPERTY(Reversed,(0)); + ADD_PROPERTY(Distance,(0.0)); + ADD_PROPERTY_TYPE(Location,(0),"Constraint",(App::PropertyType)(App::Prop_None),"Element giving location where constraint is applied"); + ADD_PROPERTY(Diameter,(0.0)); + ADD_PROPERTY(OtherDiameter,(0.0)); + ADD_PROPERTY(CenterDistance,(0.0)); +} + +Constraint::~Constraint() +{ +} + +App::DocumentObjectExecReturn *Constraint::execute(void) +{ + // Ensure that the constraint symbols follow the changed geometry + References.touch(); + return DocumentObject::StdReturn; +} + +void Constraint::onChanged(const App::Property* prop) +{ + DocumentObject::onChanged(prop); +} diff --git a/src/Mod/Fem/App/FemConstraint.h b/src/Mod/Fem/App/FemConstraint.h new file mode 100644 index 000000000..625ba95ae --- /dev/null +++ b/src/Mod/Fem/App/FemConstraint.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_CONSTRAINT_H +#define FEM_CONSTRAINT_H + +#include +#include + +namespace Fem +{ + +class AppFemExport Constraint : public App::DocumentObject +{ + PROPERTY_HEADER(Fem::Constraint); + +public: + /// Constructor + Constraint(void); + virtual ~Constraint(); + + App::PropertyEnumeration Type; + App::PropertyLinkSubList References; + App::PropertyFloat Force; + App::PropertyLinkSub Direction; + App::PropertyBool Reversed; + App::PropertyLinkSub Location; + App::PropertyFloat Distance; + App::PropertyFloat Diameter; + App::PropertyFloat OtherDiameter; + App::PropertyFloat CenterDistance; + + /// recalculate the object + virtual App::DocumentObjectExecReturn *execute(void); + + /// returns the type name of the ViewProvider + const char* getViewProviderName(void) const { + return "FemGui::ViewProviderFemConstraint"; + } + +protected: + virtual void onChanged(const App::Property* prop); + +private: + static const char* TypeEnums[]; +}; + +} //namespace Fem + + +#endif // FEM_CONSTRAINT_H diff --git a/src/Mod/Fem/Gui/AppFemGui.cpp b/src/Mod/Fem/Gui/AppFemGui.cpp index d36865d91..46e20afe8 100755 --- a/src/Mod/Fem/Gui/AppFemGui.cpp +++ b/src/Mod/Fem/Gui/AppFemGui.cpp @@ -34,6 +34,7 @@ #include "ViewProviderSetElements.h" #include "ViewProviderSetFaces.h" #include "ViewProviderSetGeometry.h" +#include "ViewProviderFemConstraint.h" #include "Workbench.h" //#include "resources/qrc_Fem.cpp" @@ -63,16 +64,17 @@ 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(); // add resources and reloads the translators loadFemResource(); diff --git a/src/Mod/Fem/Gui/CMakeLists.txt b/src/Mod/Fem/Gui/CMakeLists.txt index 6a36cfbec..52c1c0dd7 100755 --- a/src/Mod/Fem/Gui/CMakeLists.txt +++ b/src/Mod/Fem/Gui/CMakeLists.txt @@ -34,6 +34,7 @@ set(FemGui_MOC_HDRS TaskObjectName.h TaskCreateNodeSet.h TaskDlgCreateNodeSet.h + TaskFemConstraint.h ) fc_wrap_cpp(FemGui_MOC_SRCS ${FemGui_MOC_HDRS}) SOURCE_GROUP("Moc" FILES ${FemGui_MOC_SRCS}) @@ -42,9 +43,21 @@ set(FemGui_UIC_SRCS Hypothesis.ui TaskCreateNodeSet.ui TaskObjectName.ui + TaskFemConstraint.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 +) +SOURCE_GROUP("Dialogs" FILES ${FemGui_DLG_SRCS}) + qt4_add_resources(FemResource_SRCS Resources/Fem.qrc) SOURCE_GROUP("Resources" FILES ${FemResource_SRCS}) @@ -62,14 +75,16 @@ SET(FemGui_SRCS_ViewProvider ViewProviderSetGeometry.h FemSelectionGate.cpp FemSelectionGate.h + ViewProviderFemConstraint.cpp + ViewProviderFemConstraint.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 +96,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}) diff --git a/src/Mod/Fem/Gui/Command.cpp b/src/Mod/Fem/Gui/Command.cpp index 655879f22..cd58e9714 100755 --- a/src/Mod/Fem/Gui/Command.cpp +++ b/src/Mod/Fem/Gui/Command.cpp @@ -52,8 +52,10 @@ #include #include #include +#include #include "Hypothesis.h" +#include "TaskFemConstraint.h" using namespace std; @@ -85,6 +87,38 @@ bool CmdFemCreateFromShape::isActive(void) return Gui::Selection().countObjectsOfType(type) > 0; } +DEF_STD_CMD_A(CmdFemConstraint); + +CmdFemConstraint::CmdFemConstraint() + : Command("Fem_Constraint") +{ + sAppModule = "Fem"; + sGroup = QT_TR_NOOP("Fem"); + sMenuText = QT_TR_NOOP("Create FEM constraint"); + sToolTipText = QT_TR_NOOP("Create FEM constraint"); + sWhatsThis = sToolTipText; + sStatusTip = sToolTipText; + sPixmap = "Fem_Constraint"; +} + +void CmdFemConstraint::activated(int iMsg) +{ + std::string FeatName = getUniqueObjectName("FemConstraint"); + + openCommand("Make FEM constraint"); + doCommand(Doc,"App.activeDocument().addObject(\"Fem::Constraint\",\"%s\")",FeatName.c_str()); + doCommand(Doc,"App.activeDocument().%s.Force = 0.0",FeatName.c_str()); + updateActive(); + + Gui::ViewProvider* vp = Gui::Application::Instance->getViewProvider(App::GetApplication().getActiveDocument()->getActiveObject()); + + doCommand(Gui,"Gui.activeDocument().setEdit('%s')",FeatName.c_str()); +} + +bool CmdFemConstraint::isActive(void) +{ + return hasActiveDocument(); +} // ##################################################################################################### @@ -281,4 +315,5 @@ void CreateFemCommands(void) rcCmdMgr.addCommand(new CmdFemCreateFromShape()); rcCmdMgr.addCommand(new CmdFemCreateNodesSet()); rcCmdMgr.addCommand(new CmdFemDefineNodesSet()); + rcCmdMgr.addCommand(new CmdFemConstraint()); } diff --git a/src/Mod/Fem/Gui/TaskFemConstraint.cpp b/src/Mod/Fem/Gui/TaskFemConstraint.cpp new file mode 100644 index 000000000..b415eedeb --- /dev/null +++ b/src/Mod/Fem/Gui/TaskFemConstraint.cpp @@ -0,0 +1,752 @@ +/*************************************************************************** + * 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 + +#include + +using namespace FemGui; +using namespace Gui; + +/* TRANSLATOR FemGui::TaskFemConstraint */ + +const QString makeRefText(const App::DocumentObject* obj, const std::string& subName) +{ + return QString::fromUtf8((std::string(obj->getNameInDocument()) + ":" + subName).c_str()); +} + +TaskFemConstraint::TaskFemConstraint(ViewProviderFemConstraint *ConstraintView,QWidget *parent) + : TaskBox(Gui::BitmapFactory().pixmap("Fem_Constraint"),tr("FEM constraint parameters"),true, parent),ConstraintView(ConstraintView) +{ + // we need a separate container widget to add all controls to + proxy = new QWidget(this); + ui = new Ui_TaskFemConstraint(); + 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->comboType, SIGNAL(currentIndexChanged(int)), + this, SLOT(onTypeChanged(int))); + 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))); + connect(ui->buttonLocation, SIGNAL(pressed()), + this, SLOT(onButtonLocation())); + connect(ui->spinDistance, SIGNAL(valueChanged(double)), + this, SLOT(onDistanceChanged(double))); + connect(ui->spinDiameter, SIGNAL(valueChanged(double)), + this, SLOT(onDiameterChanged(double))); + connect(ui->spinOtherDia, SIGNAL(valueChanged(double)), + this, SLOT(onOtherDiameterChanged(double))); + connect(ui->spinCenterDistance, SIGNAL(valueChanged(double)), + this, SLOT(onCenterDistanceChanged(double))); + + this->groupLayout()->addWidget(proxy); + + // Temporarily prevent unnecessary feature recomputes + ui->comboType->blockSignals(true); + ui->spinForce->blockSignals(true); + ui->listReferences->blockSignals(true); + ui->buttonReference->blockSignals(true); + ui->buttonDirection->blockSignals(true); + ui->checkReverse->blockSignals(true); + ui->buttonLocation->blockSignals(true); + ui->spinDistance->blockSignals(true); + ui->spinDiameter->blockSignals(true); + ui->spinOtherDia->blockSignals(true); + ui->spinCenterDistance->blockSignals(true); + + // Get the feature data + Fem::Constraint* pcConstraint = static_cast(ConstraintView->getObject()); + int index = pcConstraint->Type.getValue(); + 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(); + std::vector locStrings = pcConstraint->Location.getSubValues(); + QString loc; + if (!locStrings.empty()) + loc = makeRefText(pcConstraint->Location.getValue(), locStrings.front()); + double d = pcConstraint->Distance.getValue(); + double dia = pcConstraint->Diameter.getValue(); + double otherdia = pcConstraint->OtherDiameter.getValue(); + double centerdist = pcConstraint->CenterDistance.getValue(); + + // Fill data into dialog elements + ui->comboType->clear(); + ui->comboType->insertItem(0, tr("Force on geometry")); + ui->comboType->insertItem(1, tr("Fixed")); + ui->comboType->insertItem(2, tr("Bearing (axial free)")); + ui->comboType->insertItem(3, tr("Bearing (axial fixed)")); + ui->comboType->insertItem(4, tr("Pulley")); + ui->comboType->insertItem(5, tr("Gear (straight toothed)")); + ui->comboType->setCurrentIndex(index); + ui->spinForce->setMinimum(0); + ui->spinForce->setMaximum(INT_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->lineDirection->setText(loc.isEmpty() ? tr("") : loc); + ui->spinDistance->setMinimum(INT_MIN); + ui->spinDistance->setMaximum(INT_MAX); + ui->spinDistance->setValue(d); + ui->spinDiameter->setMinimum(0); + ui->spinDiameter->setMaximum(INT_MAX); + ui->spinDiameter->setValue(dia); + ui->spinOtherDia->setMinimum(0); + ui->spinOtherDia->setMaximum(INT_MAX); + ui->spinOtherDia->setValue(otherdia); + ui->spinCenterDistance->setMinimum(0); + ui->spinCenterDistance->setMaximum(INT_MAX); + ui->spinCenterDistance->setValue(centerdist); + + // activate and de-activate dialog elements as appropriate + ui->comboType->blockSignals(false); + ui->spinForce->blockSignals(false); + ui->listReferences->blockSignals(false); + ui->buttonReference->blockSignals(false); + ui->buttonDirection->blockSignals(false); + ui->checkReverse->blockSignals(false); + ui->buttonLocation->blockSignals(false); + ui->spinDistance->blockSignals(false); + ui->spinDiameter->blockSignals(false); + ui->spinOtherDia->blockSignals(false); + ui->spinCenterDistance->blockSignals(false); + + selectionMode = selref; + updateUI(); +} + +void TaskFemConstraint::updateUI() +{ + if (ui->comboType->currentIndex() == 0) { + ui->labelForce->setVisible(true); + ui->spinForce->setVisible(true); + ui->buttonDirection->setVisible(true); + ui->lineDirection->setVisible(true); + ui->checkReverse->setVisible(true); + ui->buttonLocation->setVisible(false); + ui->lineLocation->setVisible(false); + ui->labelDistance->setVisible(false); + ui->spinDistance->setVisible(false); + ui->labelDiameter->setVisible(false); + ui->spinDiameter->setVisible(false); + ui->labelOtherDia->setVisible(false); + ui->spinOtherDia->setVisible(false); + ui->labelCenterDistance->setVisible(false); + ui->spinCenterDistance->setVisible(false); + } else if (ui->comboType->currentIndex() == 1) { + ui->labelForce->setVisible(false); + ui->spinForce->setVisible(false); + ui->buttonDirection->setVisible(false); + ui->lineDirection->setVisible(false); + ui->checkReverse->setVisible(false); + ui->buttonLocation->setVisible(false); + ui->lineLocation->setVisible(false); + ui->labelDistance->setVisible(false); + ui->spinDistance->setVisible(false); + ui->labelDiameter->setVisible(false); + ui->spinDiameter->setVisible(false); + ui->labelOtherDia->setVisible(false); + ui->spinOtherDia->setVisible(false); + ui->labelCenterDistance->setVisible(false); + ui->spinCenterDistance->setVisible(false); + } else if ((ui->comboType->currentIndex() == 2) || (ui->comboType->currentIndex() == 3)) { + ui->labelForce->setVisible(false); + ui->spinForce->setVisible(false); + ui->buttonDirection->setVisible(false); + ui->lineDirection->setVisible(false); + ui->checkReverse->setVisible(false); + ui->buttonLocation->setVisible(true); + ui->lineLocation->setVisible(true); + ui->labelDistance->setVisible(true); + ui->spinDistance->setVisible(true); + ui->labelDiameter->setVisible(false); + ui->spinDiameter->setVisible(false); + ui->labelOtherDia->setVisible(false); + ui->spinOtherDia->setVisible(false); + ui->labelCenterDistance->setVisible(false); + ui->spinCenterDistance->setVisible(false); + } else if (ui->comboType->currentIndex() == 4) { + ui->labelForce->setVisible(false); + ui->spinForce->setVisible(false); + ui->buttonDirection->setVisible(false); + ui->lineDirection->setVisible(false); + ui->checkReverse->setVisible(false); + ui->buttonLocation->setVisible(true); + ui->lineLocation->setVisible(true); + ui->labelDistance->setVisible(true); + ui->spinDistance->setVisible(true); + ui->labelDiameter->setVisible(true); + ui->spinDiameter->setVisible(true); + ui->labelOtherDia->setVisible(true); + ui->spinOtherDia->setVisible(true); + ui->labelCenterDistance->setVisible(true); + ui->spinCenterDistance->setVisible(true); + } else if (ui->comboType->currentIndex() == 5) { + ui->labelForce->setVisible(false); + ui->spinForce->setVisible(false); + ui->buttonDirection->setVisible(false); + ui->lineDirection->setVisible(false); + ui->checkReverse->setVisible(false); + ui->buttonLocation->setVisible(true); + ui->lineLocation->setVisible(true); + ui->labelDistance->setVisible(true); + ui->spinDistance->setVisible(true); + ui->labelDiameter->setVisible(true); + ui->spinDiameter->setVisible(true); + ui->labelOtherDia->setVisible(true); + ui->spinOtherDia->setVisible(true); + ui->labelCenterDistance->setVisible(false); + ui->spinCenterDistance->setVisible(false); + } + + if (ui->listReferences->model()->rowCount() == 0) { + // Go into reference selection mode if no reference has been selected yet + onButtonReference(true); + return; + } + + if (ui->comboType->currentIndex() == 0) { + 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 TaskFemConstraint::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::Constraint* pcConstraint = static_cast(ConstraintView->getObject()); + App::DocumentObject* obj = ConstraintView->getObject()->getDocument()->getObject(msg.pObjectName); + //if (!obj->getClassTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) + // return; + 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 (pcConstraint->Type.getValue() == 0) { + // Force on geometry elements: + // 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; + } else if (pcConstraint->Type.getValue() == 1) { + // Fixed + if ((subName.substr(0,4) != "Face") && (subName.substr(0,4) != "Edge") && (subName.substr(0,6) != "Vertex")) { + QMessageBox::warning(this, tr("Selection error"), tr("Mixed shape types are not possible. Use a second constraint instead")); + 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; + } else if ((pcConstraint->Type.getValue() >= 2) && (pcConstraint->Type.getValue() <= 5)) { + // Bearing, pulley, gear + if (Objects.size() > 0) { + QMessageBox::warning(this, tr("Selection error"), tr("Please use only a single reference for bearing constraint")); + return; + } + // Only cylindrical faces allowed + if (subName.substr(0,4) != "Face") { + QMessageBox::warning(this, tr("Selection error"), tr("Only faces can be picked")); + return; + } + + 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; + } + } else { + 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) || (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; + } + if (selectionMode == seldir) { + pcConstraint->Direction.setValue(obj, references); + ui->lineDirection->setText(makeRefText(obj, subName)); + + // Turn off direction selection mode + onButtonDirection(false); + } else { + pcConstraint->Location.setValue(obj, references); + ui->lineLocation->setText(makeRefText(obj, subName)); + + // Turn off direction selection mode + onButtonLocation(false); + } + } + updateUI(); + } +} + +void TaskFemConstraint::onTypeChanged(int index) +{ + Fem::Constraint* pcConstraint = static_cast(ConstraintView->getObject()); + int oldType = pcConstraint->Type.getValue(); + pcConstraint->Type.setValue(index); + + if (((oldType == 2) && (index == 3)) || ((oldType == 3) && (index == 2))) { + pcConstraint->References.touch(); // Update visual + updateUI(); + } else { + // Clear all references if the old and new type mismatch + std::vector Objects = pcConstraint->References.getValues(); + std::vector SubElements = pcConstraint->References.getSubValues(); + + Objects.clear(); + SubElements.clear(); + pcConstraint->References.setValues(Objects, SubElements); + + ui->listReferences->clear(); //model()->removeRows(0, ui->listReferences->model()->rowCount()); + updateUI(); + } +} + +void TaskFemConstraint::onForceChanged(double f) +{ + Fem::Constraint* pcConstraint = static_cast(ConstraintView->getObject()); + pcConstraint->Force.setValue((float)f); +} + +void TaskFemConstraint::onDistanceChanged(double f) +{ + Fem::Constraint* pcConstraint = static_cast(ConstraintView->getObject()); + pcConstraint->Distance.setValue((float)f); +} + +void TaskFemConstraint::onDiameterChanged(double f) +{ + Fem::Constraint* pcConstraint = static_cast(ConstraintView->getObject()); + pcConstraint->Diameter.setValue((float)f); +} + +void TaskFemConstraint::onOtherDiameterChanged(double f) +{ + Fem::Constraint* pcConstraint = static_cast(ConstraintView->getObject()); + pcConstraint->OtherDiameter.setValue((float)f); +} + +void TaskFemConstraint::onCenterDistanceChanged(double d) +{ + Fem::Constraint* pcConstraint = static_cast(ConstraintView->getObject()); + pcConstraint->CenterDistance.setValue((float)d); +} + +void TaskFemConstraint::onButtonReference(const bool pressed) { + if (pressed) + selectionMode = selref; + else + selectionMode = selnone; + ui->buttonReference->setChecked(pressed); + Gui::Selection().clearSelection(); +} + +void TaskFemConstraint::onReferenceDeleted() { + int row = ui->listReferences->currentIndex().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); + + ui->listReferences->model()->removeRow(row); + ui->listReferences->setCurrentRow(0, QItemSelectionModel::ClearAndSelect); +} + +void TaskFemConstraint::onButtonDirection(const bool pressed) { + if (pressed) { + selectionMode = seldir; + } else { + selectionMode = selnone; + } + ui->buttonDirection->setChecked(pressed); + Gui::Selection().clearSelection(); +} + +void TaskFemConstraint::onButtonLocation(const bool pressed) { + if (pressed) { + selectionMode = selloc; + } else { + selectionMode = selnone; + } + ui->buttonLocation->setChecked(pressed); + Gui::Selection().clearSelection(); +} + +void TaskFemConstraint::onCheckReverse(const bool pressed) +{ + Fem::Constraint* pcConstraint = static_cast(ConstraintView->getObject()); + pcConstraint->Reversed.setValue(pressed); +} + +int TaskFemConstraint::getType(void) const +{ + return ui->comboType->currentIndex(); +} + +double TaskFemConstraint::getForce(void) const +{ + return ui->spinForce->value(); +} + +double TaskFemConstraint::getDistance(void) const +{ + return ui->spinDistance->value(); +} + +double TaskFemConstraint::getDiameter(void) const +{ + return ui->spinDiameter->value(); +} + +double TaskFemConstraint::getOtherDiameter(void) const +{ + return ui->spinOtherDia->value(); +} + +double TaskFemConstraint::getCenterDistance(void) const +{ + return ui->spinCenterDistance->value(); +} + +const std::string TaskFemConstraint::getReferences(void) const +{ + int rows = ui->listReferences->model()->rowCount(); + if (rows == 0) + return ""; + + std::string result; + for (int r = 0; r < rows; r++) { + std::string item = ui->listReferences->item(r)->text().toStdString(); + int pos = item.find_last_of(":"); + std::string objStr = "App.ActiveDocument." + item.substr(0, pos); + std::string refStr = "\"" + item.substr(pos+1) + "\""; + result = result + (r > 0 ? ", " : "") + "(" + objStr + "," + refStr + ")"; + } + + return result; +} + +const std::string TaskFemConstraint::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 TaskFemConstraint::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(); +} + +const std::string TaskFemConstraint::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 TaskFemConstraint::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 TaskFemConstraint::getReverse() const +{ + return ui->checkReverse->isChecked(); +} + +TaskFemConstraint::~TaskFemConstraint() +{ + delete ui; +} + +void TaskFemConstraint::changeEvent(QEvent *e) +{ + TaskBox::changeEvent(e); + if (e->type() == QEvent::LanguageChange) { + ui->comboType->blockSignals(true); + ui->spinForce->blockSignals(true); + ui->spinDistance->blockSignals(true); + int index = ui->comboType->currentIndex(); + ui->comboType->clear(); + ui->comboType->insertItem(0, tr("Force on geometry")); + ui->comboType->insertItem(1, tr("Fixed")); + ui->comboType->insertItem(2, tr("Bearing (axial free)")); + ui->comboType->insertItem(3, tr("Bearing (axial fixed)")); + ui->comboType->insertItem(4, tr("Pulley")); + ui->comboType->insertItem(5, tr("Gear (straight toothed)")); + ui->comboType->setCurrentIndex(index); + ui->retranslateUi(proxy); + ui->comboType->blockSignals(false); + ui->spinForce->blockSignals(false); + ui->spinDistance->blockSignals(false); + } +} + +//************************************************************************** +//************************************************************************** +// TaskDialog +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +TaskDlgFemConstraint::TaskDlgFemConstraint(ViewProviderFemConstraint *ConstraintView) + : TaskDialog(),ConstraintView(ConstraintView) +{ + assert(ConstraintView); + parameter = new TaskFemConstraint(ConstraintView); + + Content.push_back(parameter); +} + +TaskDlgFemConstraint::~TaskDlgFemConstraint() +{ + +} + +//==== calls from the TaskView =============================================================== + + +void TaskDlgFemConstraint::open() +{ + +} + +void TaskDlgFemConstraint::clicked(int) +{ + +} + +bool TaskDlgFemConstraint::accept() +{ + std::string name = ConstraintView->getObject()->getNameInDocument(); + + try { + //Gui::Command::openCommand("FEM constraint changed"); + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Type = %u",name.c_str(),parameter->getType()); + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Force = %f",name.c_str(),parameter->getForce()); + 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; + } + + std::string dirname = parameter->getDirectionName().data(); + std::string dirobj = parameter->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(), parameter->getReverse() ? "True" : "False"); + + std::string locname = parameter->getLocationName().data(); + std::string locobj = parameter->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.Direction = None", name.c_str()); + } + + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Distance = %f",name.c_str(),parameter->getDistance()); + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.Diameter = %f",name.c_str(),parameter->getDiameter()); + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.OtherDiameter = %f",name.c_str(),parameter->getOtherDiameter()); + Gui::Command::doCommand(Gui::Command::Doc,"App.ActiveDocument.%s.CenterDistance = %f",name.c_str(),parameter->getCenterDistance()); + + 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 done things + 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 000000000..d27da1983 --- /dev/null +++ b/src/Mod/Fem/Gui/TaskFemConstraint.h @@ -0,0 +1,131 @@ +/*************************************************************************** + * 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" + +class Ui_TaskFemConstraint; + +namespace App { +class Property; +} + +namespace Gui { +class ViewProvider; +} + +namespace FemGui { + +class TaskFemConstraint : public Gui::TaskView::TaskBox, public Gui::SelectionObserver +{ + Q_OBJECT + +public: + TaskFemConstraint(ViewProviderFemConstraint *ConstraintView,QWidget *parent = 0); + ~TaskFemConstraint(); + + int getType(void) const; + double getForce(void) const; + const std::string getReferences(void) const; + const std::string getDirectionName(void) const; + const std::string getDirectionObject(void) const; + const std::string getLocationName(void) const; + const std::string getLocationObject(void) const; + double getDistance(void) const; + bool getReverse(void) const; + double getDiameter(void) const; + double getOtherDiameter(void) const; + double getCenterDistance(void) const; + +private Q_SLOTS: + void onTypeChanged(int); + void onReferenceDeleted(); + void onForceChanged(double); + void onButtonReference(const bool pressed = true); + void onButtonDirection(const bool pressed = true); + void onButtonLocation(const bool pressed = true); + void onDistanceChanged(double); + void onCheckReverse(bool); + void onDiameterChanged(double); + void onOtherDiameterChanged(double); + void onCenterDistanceChanged(double); + +protected: + void changeEvent(QEvent *e); + +private: + void onSelectionChanged(const Gui::SelectionChanges& msg); + void updateUI(); + +private: + QWidget* proxy; + Ui_TaskFemConstraint* ui; + ViewProviderFemConstraint *ConstraintView; + enum {seldir, selref, selloc, selnone} selectionMode; +}; + +/// simulation dialog for the TaskView +class TaskDlgFemConstraint : public Gui::TaskView::TaskDialog +{ + Q_OBJECT + +public: + TaskDlgFemConstraint(ViewProviderFemConstraint *ConstraintView); + ~TaskDlgFemConstraint(); + + ViewProviderFemConstraint* getConstraintView() const + { return ConstraintView; } + + +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; } + +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 000000000..2ca053720 --- /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/ViewProviderFemConstraint.cpp b/src/Mod/Fem/Gui/ViewProviderFemConstraint.cpp new file mode 100644 index 000000000..2a58cc4bc --- /dev/null +++ b/src/Mod/Fem/Gui/ViewProviderFemConstraint.cpp @@ -0,0 +1,819 @@ +/*************************************************************************** + * 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 +# include +# include +# include +# include +# include +# include +# include +# include +#endif + +#include "ViewProviderFemConstraint.h" +#include "TaskFemConstraint.h" +#include "Gui/SoFCSelection.h" +#include "Gui/Application.h" +#include "Gui/Control.h" +#include "Gui/Command.h" +#include "Gui/Document.h" +#include "Gui/View3DInventorViewer.h" +#include "App/Document.h" + + +#include +#include +#include +#include +#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(FontSize,(18)); + ADD_PROPERTY(DistFactor,(1.0)); + ADD_PROPERTY(Mirror,(false)); + + pFont = new SoFontStyle(); + pFont->ref(); + pLabel = new SoText2(); + pLabel->ref(); + pColor = new SoBaseColor(); + pColor->ref(); + pTextColor = new SoBaseColor(); + pTextColor->ref(); + pTranslation = new SoTranslation(); + pTranslation->ref(); + + TextColor.touch(); + FontSize.touch(); + FaceColor.touch(); + + pCoords = new SoCoordinate3(); + pCoords->ref(); + pCoords->point.setNum(0); + + pFaces = new SoIndexedFaceSet(); + pFaces->ref(); + pFaces->coordIndex.setNum(0); + + sPixmap = "view-femconstraint"; + + normalDirection = new SbVec3f(0, 0, 1); + arrowDirection = NULL; +} + +ViewProviderFemConstraint::~ViewProviderFemConstraint() +{ + pFont->unref(); + pLabel->unref(); + pColor->unref(); + pTextColor->unref(); + pTranslation->unref(); + pCoords->unref(); + pFaces->unref(); + delete arrowDirection; + delete normalDirection; +} + +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 (this->getObject() != NULL) + Base::Console().Error("%s: onChanged: %s\n", this->getObject()->getNameInDocument(), prop->getName()); + else + Base::Console().Error("Anonymous: onChanged: %s\n", prop->getName()); + + 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(); + pColor->rgb.setValue(c.r,c.g,c.b); + } + else if (prop == &FontSize) { + pFont->size = FontSize.getValue(); + } + else { + ViewProviderDocumentObject::onChanged(prop); + } +} + +bool ViewProviderFemConstraint::setEdit(int 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(); + TaskDlgFemConstraint *constrDlg = qobject_cast(dlg); + if (constrDlg && constrDlg->getConstraintView() != this) + constrDlg = 0; // another constraint left open its task panel + if (dlg && !constrDlg) { + 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; + } + + // clear the selection (convenience) + Gui::Selection().clearSelection(); + + // start the edit dialog + if (constrDlg) + Gui::Control().showDialog(constrDlg); + else + Gui::Control().showDialog(new TaskDlgFemConstraint(this)); + + return true; + } + else { + return ViewProviderDocumentObject::setEdit(ModNum); + } +} + +void ViewProviderFemConstraint::unsetEdit(int ModNum) +{ + if (ModNum == ViewProvider::Default) { + // when pressing ESC make sure to close the dialog + Gui::Control().closeDialog(); + } + else { + ViewProviderDocumentObject::unsetEdit(ModNum); + } +} + +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); +} + +void ViewProviderFemConstraint::attach(App::DocumentObject* pcObject) +{ + ViewProviderDocumentObject::attach(pcObject); + + SoPickStyle* ps = new SoPickStyle(); + ps->style = SoPickStyle::UNPICKABLE; + + SoSeparator *faceSep = new SoSeparator(); + faceSep->addChild(ps); + faceSep->addChild(pColor); + faceSep->addChild(pCoords); + faceSep->addChild(pFaces); + + SoSeparator* sep = new SoSeparator(); + sep->addChild(faceSep); + addDisplayMaskMode(sep, "Base"); +} + +// 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 FACETS 12 +#define CONEPOINTS (FACETS + 1) +#define CONEFACETPOINTS (FACETS * 4 + FACETS + 1) + +void createCone(SoMFVec3f& point, SoMFInt32& refs, const int ipoints, const int ifaces, const SbVec3f& base, const SbVec3f& dir, + const double height, const double radius, const bool update = false) +{ + SbVec3f x, y; + getLocalCoordinateSystem(dir, y, x); + + point.set1Value(ipoints, base); // tip + + SbVec3f midpoint(base + dir * height); // centre of the circle + for (int i = 0; i < FACETS; i++) { + float angle = 2 * M_PI / FACETS * i; + point.set1Value(ipoints + i + 1, midpoint + cos(angle) * x * radius + sin(angle) * y * radius); + } + + if (update) + return; + + int32_t faces[CONEFACETPOINTS]; + int start_index = 1; + for (int f = 0; f < FACETS; f++) { + faces[f * 4] = ipoints; // tip of arrow + int idx = start_index; + faces[f * 4 + 1] = ipoints + idx; + idx++; + if (idx > FACETS) idx = 1; // Happens in the last iteration + faces[f * 4 + 2] = ipoints + idx; + faces[f * 4 + 3] = -1; + start_index++; + } + for (int f = 0; f < FACETS; f++) + faces[FACETS * 4 + f] = ipoints + f + 1; + faces[CONEFACETPOINTS - 1] = -1; + refs.setValues(ifaces, CONEFACETPOINTS, faces); +} + +#define CYLPOINTS (FACETS * 2) +#define CYLFACETPOINTS (FACETS * 5 + 2 * FACETS + 2) + +void createCylinder(SoMFVec3f& point, SoMFInt32& refs, const int ipoints, const int ifaces, const SbVec3f& base, const SbVec3f& dir, + const double height, const double radius, const bool update = false) +{ + SbVec3f x, y; + getLocalCoordinateSystem(dir, y, x); + + for (int i = 0; i < CYLPOINTS; i+=2) { + float angle = 2 * M_PI / FACETS * i/2; + point.set1Value(ipoints + i, base + cos(angle) * x * radius + sin(angle) * y * radius); + point.set1Value(ipoints + i + 1, base + dir * height + cos(angle) * x * radius + sin(angle) * y * radius); + } + + if (update) + return; + + int32_t faces[CYLFACETPOINTS]; + int start_index = 0; + for (int f = 0; f < FACETS; f++) { + int idx = start_index; + faces[f * 5] = ipoints + idx; + idx++; + faces[f * 5 + 1] = ipoints + idx; + idx++; + if (idx >= CYLPOINTS) idx = 0; // Happens in the last iteration + faces[f * 5 + 3] = ipoints + idx; + idx++; + faces[f * 5 + 2] = ipoints + idx; + faces[f * 5 + 4] = -1; + start_index += 2; + } + for (int f = 0; f < FACETS; f++) { + faces[FACETS * 5 + f] = ipoints + 2 * f; + faces[FACETS * 5 + FACETS + f + 1] = ipoints + 1 + 2 * f; + } + faces[FACETS * 5 + FACETS] = -1; + faces[CYLFACETPOINTS - 1] = -1; + refs.setValues(ifaces, CYLFACETPOINTS, faces); +} + +#define ARROWPOINTS (CONEPOINTS + CYLPOINTS) +#define ARROWFACETPOINTS (CONEFACETPOINTS + CYLFACETPOINTS) + +void createArrow(SoMFVec3f& point, SoMFInt32& refs, const int ipoints, const int ifaces, const SbVec3f& base, const SbVec3f& dir, + const double length, const double radius, const bool update = false) +{ + createCone(point, refs, ipoints, ifaces, base, dir, radius, radius, update); + createCylinder(point, refs, ipoints + CONEPOINTS, ifaces + CONEFACETPOINTS, base + dir * radius, dir, length-radius, radius/3, update); +} + +#define BOXPOINTS 8 +#define BOXFACEPOINTS 30 + +void createBox(SoMFVec3f& point, SoMFInt32& refs, const int ipoints, const int ifaces, const SbVec3f& base, const SbVec3f& dir, + const double width, const double length, const double height, const bool update = false) +{ + SbVec3f x, y; + getLocalCoordinateSystem(dir, y, x); + + point.set1Value(ipoints, base + width/2 * y + length/2 * x); + point.set1Value(ipoints+1, base + width/2 * y - length/2 * x); + point.set1Value(ipoints+2, base - width/2 * y - length/2 * x); + point.set1Value(ipoints+3, base - width/2 * y + length/2 * x); + point.set1Value(ipoints+4, base + dir * height + width/2 * y + length/2 * x); + point.set1Value(ipoints+5, base + dir * height + width/2 * y - length/2 * x); + point.set1Value(ipoints+6, base + dir * height - width/2 * y - length/2 * x); + point.set1Value(ipoints+7, base + dir * height - width/2 * y + length/2 * x); + + if (update) + return; + + int32_t faces[BOXFACEPOINTS] = { + ipoints, ipoints+1, ipoints+2, ipoints+3, -1, + ipoints, ipoints+1, ipoints+5, ipoints+4, -1, + ipoints+1, ipoints+2, ipoints+6, ipoints+5, -1, + ipoints+2, ipoints+3, ipoints+7, ipoints+6, -1, + ipoints+3, ipoints, ipoints+4, ipoints+7, -1, + ipoints+4, ipoints+5, ipoints+6, ipoints+7, -1}; + refs.setValues(ifaces, BOXFACEPOINTS, faces); +} + +void ViewProviderFemConstraint::findCylinderData(SbVec3f& z, SbVec3f& y, SbVec3f& x, SbVec3f& p, double& radius, double& height) { + Fem::Constraint* pcConstraint = static_cast(this->getObject()); + std::vector Objects = pcConstraint->References.getValues(); + std::vector SubElements = pcConstraint->References.getSubValues(); + if (Objects.empty()) + return; + App::DocumentObject* obj = Objects[0]; + Part::Feature* feat = static_cast(obj); + TopoDS_Shape sh = feat->Shape.getShape().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_Dir dirz = cyl.Axis().Direction(); + z = SbVec3f(dirz.X(), dirz.Y(), dirz.Z()); + gp_Dir diry = cyl.YAxis().Direction(); + y = SbVec3f(diry.X(), diry.Y(), diry.Z()); + gp_Dir dirx = cyl.XAxis().Direction(); + x = SbVec3f(dirx.X(), dirx.Y(), dirx.Z()); + + if (pcConstraint->Location.getValue() == NULL) { + // Get a point in the middle of the cylindrical face. + gp_Pnt centre = cyl.Location(); + SbVec3f base(centre.X(), centre.Y(), centre.Z()); + p = base + z * height/2; + } else { + // Get the point specified by Location and Distance + App::DocumentObject* objLoc = pcConstraint->Location.getValue(); + std::string subName = pcConstraint->Location.getSubValues().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; + 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(dirz); + 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); + GeomAPI_ProjectPointOnSurf proj(cyl.Location(), pln); + if (!proj.IsDone()) + return; + gp_Pnt projPnt = proj.NearestPoint(); + plane.Translate(gp_Vec(projPnt, cyl.Location()).Normalized().Multiplied(pcConstraint->Distance.getValue())); + Handle_Geom_Plane plnt = new Geom_Plane(plane); + // Intersect translated plane with cylinder axis + Handle_Geom_Curve crv = new Geom_Line(cyl.Axis()); + GeomAPI_IntCS intersector(crv, plnt); + if (!intersector.IsDone()) + return; + gp_Pnt inter = intersector.Point(1); + p.setValue(inter.X(), inter.Y(), inter.Z()); + } +} + +void ViewProviderFemConstraint::updateData(const App::Property* prop) +{ + // Gets called whenever a property of the attached object changes + if (this->getObject() != NULL) + Base::Console().Error("%s: updateData: %s\n", this->getObject()->getNameInDocument(), prop->getName()); + else + Base::Console().Error("Anonymous: updateData: %s\n", prop->getName()); + Fem::Constraint* pcConstraint = static_cast(this->getObject()); + + if (strcmp(prop->getName(),"References") == 0) { + const App::PropertyLinkSubList* pr = static_cast(prop); + std::vector Objects = pr->getValues(); + std::vector SubElements = pr->getSubValues(); + + // Remove all arrows + pCoords->point.deleteValues(0, pCoords->point.getNum()); + pFaces->coordIndex.deleteValues(0, pFaces->coordIndex.getNum()); + if (Objects.empty()) { + Base::Console().Error(" updateData: No references\n"); + Objects = pcConstraint->References.getValues(); + SubElements = pcConstraint->References.getSubValues(); + if (Objects.empty()) + return; + } + + Base::Console().Error(" updateData: Found %u references\n", Objects.size()); + + // Re-create all arrows + int type = pcConstraint->Type.getValue(); + + if ((type == 0) || (type == 1)) { + // Force on geometry + std::vector points; + 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()) { + Base::Console().Error(" updateData: Empty toposhape\n"); + return; + } + 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(p); + } 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(sh, props); + double l = props.Mass(); + int steps = round(l / 3); // TODO: Make number of steps depend on actual screen size of element! + double step = (lp - fp) / steps; + if (steps < 1) { + points.push_back(curve.Value(fp)); + points.push_back(curve.Value(lp)); + } else { + for (int i = 0; i < steps + 1; i++) + points.push_back(curve.Value(i * step)); + } + } else if (sh.ShapeType() == TopAbs_FACE) { + TopoDS_Face face = TopoDS::Face(sh); + BRepAdaptor_Surface surface(face); + double ufp = surface.FirstUParameter(); + double ulp = surface.LastUParameter(); + double vfp = surface.FirstVParameter(); + double vlp = surface.LastVParameter(); + double ustep = (ulp - ufp) / 6.0; + double vstep = (vlp - vfp) / 6.0; + // TODO: How to find the distance between ufp and ulp to get the number of steps? + for (int i = 0; i < 7; i++) { + for (int j = 0; j < 7; j++) { + gp_Pnt p = surface.Value(ufp + i * ustep, vfp + j * vstep); + BRepClass_FaceClassifier classifier(face, p, Precision::Confusion()); + if (classifier.State() != TopAbs_OUT) + points.push_back(p); + } + } + } + } + + // Get default direction (on first call to method) + if (arrowDirection == NULL) { + if (sh.ShapeType() == TopAbs_FACE) { + // Get face normal in center point + TopoDS_Face face = TopoDS::Face(sh); + BRepGProp_Face prop(face); + gp_Vec normal; + gp_Pnt center; + double u1,u2,v1,v2; + prop.Bounds(u1,u2,v1,v2); + prop.Normal((u1+u2)/2.0,(v1+v2)/2.0,center,normal); + normal.Normalize(); + normalDirection->setValue(normal.X(), normal.Y(), normal.Z()); + } // else use z axis + + arrowDirection = new SbVec3f(*normalDirection); + } + + if (type == 0) { + // Force on geometry + pCoords->point.setNum(ARROWPOINTS * points.size()); + pFaces->coordIndex.setNum(ARROWFACETPOINTS * points.size()); + int index = 0; + + for (std::vector::const_iterator p = points.begin(); p != points.end(); p++) { + SbVec3f v(p->X(), p->Y(), p->Z()); + if (*arrowDirection != *normalDirection) // Turn arrow around + v = v + *normalDirection * 5.0; + createArrow(pCoords->point, pFaces->coordIndex, + index * ARROWPOINTS, index * ARROWFACETPOINTS, + v, *arrowDirection, 5.0, 1.0); + index++; + } + } else if (type == 1) { + // Fixed + pCoords->point.setNum((CONEPOINTS + BOXPOINTS) * points.size()); + pFaces->coordIndex.setNum((CONEFACETPOINTS + BOXFACEPOINTS) * points.size()); + int index = 0; + + for (std::vector::const_iterator p = points.begin(); p != points.end(); p++) { + SbVec3f v(p->X(), p->Y(), p->Z()); + createCone(pCoords->point, pFaces->coordIndex, + index * (CONEPOINTS + BOXPOINTS), index * (CONEFACETPOINTS + BOXFACEPOINTS), + v, *normalDirection, 2.0, 1.0); + createBox(pCoords->point, pFaces->coordIndex, + index * (CONEPOINTS + BOXPOINTS) + CONEPOINTS, index * (CONEFACETPOINTS + BOXFACEPOINTS) + CONEFACETPOINTS, + v + *normalDirection * 2.0, *normalDirection, 2.0, 2.0, 0.5); + index++; + } + } + } else if ((type == 2) || (type == 3)) { + // Bearing. Note that only one face is allowed for this constraint + SbVec3f z, y, x, p; + double radius, height; + findCylinderData(z, y, x, p, radius, height); + p = p + y * radius; + + pCoords->point.setNum(CONEPOINTS + BOXPOINTS); + pFaces->coordIndex.setNum(CONEFACETPOINTS + BOXFACEPOINTS); + if (type == 2) + // axial free + createCone(pCoords->point, pFaces->coordIndex, 0, 0, p, y, radius/2.5, radius/4); + else + // axial fixed + createCone(pCoords->point, pFaces->coordIndex, 0, 0, p, y, radius/2, radius/4); + + createBox(pCoords->point, pFaces->coordIndex, CONEPOINTS, CONEFACETPOINTS, p + y * radius/2, y, radius, radius, radius/10); + } else if ((type == 4) || (type == 5)) { + // Pulley, Gear + SbVec3f z, y, x, p; + double radius, height; + findCylinderData(z, y, x, p, radius, height); + + double dia = pcConstraint->Diameter.getValue(); + if (dia < Precision::Confusion()) + dia = radius * 4; + double otherdia = pcConstraint->OtherDiameter.getValue(); + if (otherdia < Precision::Confusion()) + otherdia = radius * 2; + double centerdist = pcConstraint->CenterDistance.getValue(); + if (fabs(centerdist) < Precision::Confusion()) + centerdist = 500; + + if (type == 4) { + // Pulley + pCoords->point.setNum(CYLPOINTS + 2 * ARROWPOINTS); + pFaces->coordIndex.setNum(CYLFACETPOINTS + 2 * ARROWFACETPOINTS); + createCylinder(pCoords->point, pFaces->coordIndex, 0, 0, p - z * height * 0.4, z, height * 0.8, dia); + + double angle = asin((dia - otherdia)/2/centerdist); + SbVec3f p1 = p + y * dia * cos(angle) + x * dia * sin(angle); + SbVec3f dir1 = x - y * sin(angle); + dir1.normalize(); + p1 = p1 + dir1 * 2 * radius; + dir1.negate(); + SbVec3f p2 = p - y * dia * cos(angle) + x * dia * sin(angle); + SbVec3f dir2 = x + y * sin(angle); + dir2.normalize(); + p2 = p2 + dir2 * 2 * radius; + dir2.negate(); + createArrow(pCoords->point, pFaces->coordIndex, CYLPOINTS, CYLFACETPOINTS, p1, dir1, 2 * radius, radius/5); + createArrow(pCoords->point, pFaces->coordIndex, CYLPOINTS+ARROWPOINTS, CYLFACETPOINTS+ARROWFACETPOINTS, p2, dir2, 2 * radius, radius/5); + } else if (type == 5) { + // Gear + pCoords->point.setNum(CYLPOINTS + ARROWPOINTS); + pFaces->coordIndex.setNum(CYLFACETPOINTS + ARROWFACETPOINTS); + createCylinder(pCoords->point, pFaces->coordIndex, 0, 0, p - z * height * 0.4, z, height * 0.8, dia); + SbVec3f p1 = p + y * dia; + createArrow(pCoords->point, pFaces->coordIndex, CYLPOINTS, CYLFACETPOINTS, p1, x, radius, radius/5); + } + } + } else if (strcmp(prop->getName(),"Direction") == 0) { + if (arrowDirection == NULL) + return; + const App::PropertyLinkSub* pr = static_cast(prop); + App::DocumentObject* obj = pr->getValue(); + std::vector names = pr->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) { + gp_Dir dir = surface.Plane().Axis().Direction(); + arrowDirection->setValue(dir.X(), dir.Y(), dir.Z()); + } else { + return; // Not a planar face + } + } else if (sh.ShapeType() == TopAbs_EDGE) { + BRepAdaptor_Curve line(TopoDS::Edge(sh)); + if (line.GetType() == GeomAbs_Line) { + gp_Dir dir = line.Line().Direction(); + arrowDirection->setValue(dir.X(), dir.Y(), dir.Z()); + } else { + return; // Not a linear edge + } + } + + // TODO: Check whether direction points inside or outside of solid? But for which reference? + + arrowDirection->normalize(); + + *normalDirection = *arrowDirection; + bool reversed = pcConstraint->Reversed.getValue(); + if (reversed) + arrowDirection->negate(); + + // Re-orient all arrows + int numArrows = pCoords->point.getNum()/ARROWPOINTS; + + for (int i = 0; i < numArrows; i++) { + // Note: for update=true the pFaces->coordIndex is not touched + SbVec3f p = pCoords->point[i * ARROWPOINTS]; + if (reversed) + p = p + *normalDirection * 5.0; + createArrow(pCoords->point, pFaces->coordIndex, + i * ARROWPOINTS, 0, + p, *arrowDirection, 5.0, 1.0, true); + } + } else if (strcmp(prop->getName(),"Reversed") == 0) { + if (arrowDirection == NULL) + return; + bool reversed = static_cast(prop)->getValue(); + bool isReversed = (*arrowDirection != *normalDirection); + if (reversed == isReversed) + return; + + *arrowDirection = *normalDirection; + if (reversed) + arrowDirection->negate(); + + // Reverse all arrows + int numArrows = pCoords->point.getNum()/ARROWPOINTS; + + for (int i = 0; i < numArrows; i++) { + createArrow(pCoords->point, pFaces->coordIndex, + i * ARROWPOINTS, 0, + pCoords->point[i * ARROWPOINTS], *arrowDirection, 5.0, 1.0, true); + } + } else if ((strcmp(prop->getName(),"Location") == 0) || (strcmp(prop->getName(),"Distance") == 0)) { + // Move bearing constraint + SbVec3f z, y, x, p; + double radius, height; + findCylinderData(z, y, x, p, radius, height); + + int type = pcConstraint->Type.getValue(); + if (type == 2) { + // axial free + createCone(pCoords->point, pFaces->coordIndex, 0, 0, p, y, radius/2.5, radius/4, true); + createBox(pCoords->point, pFaces->coordIndex, CONEPOINTS, CONEFACETPOINTS, p + y * radius/2, y, radius, radius, radius/10, true); + } else if (type == 3) { + // axial fixed + createCone(pCoords->point, pFaces->coordIndex, 0, 0, p, y, radius/2, radius/4, true); + createBox(pCoords->point, pFaces->coordIndex, CONEPOINTS, CONEFACETPOINTS, p + y * radius/2, y, radius, radius, radius/10, true); + } else if ((type == 4) || (type == 5)) { + createCylinder(pCoords->point, pFaces->coordIndex, 0, 0, p - z * height * 0.4, z, height * 0.8, pcConstraint->Diameter.getValue(), true); + } + } else if ((strcmp(prop->getName(),"Diameter") == 0) || (strcmp(prop->getName(),"OtherDiameter") == 0) || + (strcmp(prop->getName(),"CenterDistance") == 0)) { + // Update pulley/gear constraint + SbVec3f z, y, x, p; + double radius, height; + findCylinderData(z, y, x, p, radius, height); + + double dia = pcConstraint->Diameter.getValue(); + if (dia < Precision::Confusion()) + dia = radius * 4; + double otherdia = pcConstraint->OtherDiameter.getValue(); + if (otherdia < Precision::Confusion()) + otherdia = radius * 2; + double centerdist = pcConstraint->CenterDistance.getValue(); + if (fabs(centerdist) < Precision::Confusion()) + centerdist = 500; + int type = pcConstraint->Type.getValue(); + + if (type == 4) { + // Pulley + createCylinder(pCoords->point, pFaces->coordIndex, 0, 0, p - z * height * 0.4, z, height * 0.8, dia, true); + + double angle = asin((dia - otherdia)/2/centerdist); + SbVec3f p1 = p + y * dia * cos(angle) + x * dia * sin(angle); + SbVec3f dir1 = x - y * sin(angle); + dir1.normalize(); + p1 = p1 + dir1 * 2 * radius; + dir1.negate(); + SbVec3f p2 = p - y * dia * cos(angle) + x * dia * sin(angle); + SbVec3f dir2 = x + y * sin(angle); + dir2.normalize(); + p2 = p2 + dir2 * 2 * radius; + dir2.negate(); + createArrow(pCoords->point, pFaces->coordIndex, CYLPOINTS, CYLFACETPOINTS, p1, dir1, 2 * radius, radius/5, true); + createArrow(pCoords->point, pFaces->coordIndex, CYLPOINTS+ARROWPOINTS, CYLFACETPOINTS+ARROWFACETPOINTS, p2, dir2, 2 * radius, radius/5, true); + } else if (type == 5) { + // Gear + createCylinder(pCoords->point, pFaces->coordIndex, 0, 0, p - z * height * 0.4, z, height * 0.8, dia, true); + SbVec3f p1 = p + y * dia; + createArrow(pCoords->point, pFaces->coordIndex, CYLPOINTS, CYLFACETPOINTS, p1, x, 2 * radius, radius/5, true); + } + } + ViewProviderDocumentObject::updateData(prop); +} diff --git a/src/Mod/Fem/Gui/ViewProviderFemConstraint.h b/src/Mod/Fem/Gui/ViewProviderFemConstraint.h new file mode 100644 index 000000000..94b434d2a --- /dev/null +++ b/src/Mod/Fem/Gui/ViewProviderFemConstraint.h @@ -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 * + * * + ***************************************************************************/ + + +#ifndef GUI_VIEWPROVIDERFEMCONSTRAINT_H +#define GUI_VIEWPROVIDERFEMCONSTRAINT_H + +#include + +#include "Gui/ViewProviderGeometryObject.h" +#include + +class SoFontStyle; +class SoText2; +class SoBaseColor; +class SoTranslation; +class SoCoordinate3; +class SoIndexedLineSet; +class SoIndexedFaceSet; +class SoEventCallback; +class SoMarkerSet; + +namespace Gui { +class View3DInventorViewer; +} + +namespace FemGui +{ + +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::PropertyInteger FontSize; + App::PropertyFloat DistFactor; + App::PropertyBool Mirror; + + void attach(App::DocumentObject *); + void updateData(const App::Property*); + 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); + +private: + SoFontStyle * pFont; + SoText2 * pLabel; + SoBaseColor * pColor; + SoBaseColor * pTextColor; + SoTranslation * pTranslation; + SoCoordinate3 * pCoords; + SoIndexedFaceSet * pFaces; + + /// Direction pointing outside of the solid + SbVec3f * normalDirection; + /// Direction of the force + SbVec3f * arrowDirection; + + void findCylinderData(SbVec3f& z, SbVec3f& y, SbVec3f& x, SbVec3f& p, double& radius, double& height); +}; + +} //namespace FemGui + + +#endif // GUI_VIEWPROVIDERFEMCONSTRAINT_H diff --git a/src/Mod/Fem/Gui/Workbench.cpp b/src/Mod/Fem/Gui/Workbench.cpp index e638ad806..c4d3a7367 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,8 @@ Gui::ToolBarItem* Workbench::setupToolBars() const Gui::ToolBarItem* fem = new Gui::ToolBarItem(root); fem->setCommand("FEM"); *fem << "Fem_CreateFromShape" - << "Fem_CreateNodesSet"; + << "Fem_CreateNodesSet" + << "Fem_Constraint"; return root; } @@ -68,7 +69,8 @@ Gui::MenuItem* Workbench::setupMenuBar() const root->insertItem(item, fem); fem->setCommand("&FEM"); *fem << "Fem_CreateFromShape" - << "Fem_CreateNodesSet"; + << "Fem_CreateNodesSet" + << "Fem_Constraint"; return root; }