From 6d831627839daff3e856682848dcfd6e6c4ff96c Mon Sep 17 00:00:00 2001 From: donovaly Date: Sun, 25 Oct 2020 22:52:31 +0100 Subject: [PATCH] [Part] allow to edit helices via a dialog fixes https://tracker.freecadweb.org/view.php?id=3857 Currently primitives of the Part WB can only be created suing a dialog. Editing is not possible that way. This PR makes the first step in achieving this: - Helices can now be edited using the same dialog ans when created - New location dialog. The Gui::LocationWidget used there is not aware of the rotation angle and it treats the rotation axis as direction. To fix this, I set changed the dialog so that one can define every parameter as in the PartDesign attacher dialog (Placement, rotation axis and rotation angle separately). Another reason for this change was that one could not use the location dialog, fill it with the placement properties of the object, and close the dialog without any change. Because of the missing angle info the objects was then always rotated despite nothing was changed. If this PR goes in, I will extend it for the other primitives. Todo for the future: allow a preview of the changes made in the dialog. (I could not find out how this is done.) --- src/Mod/Part/Gui/DlgPrimitives.cpp | 249 ++++++++++++++---- src/Mod/Part/Gui/DlgPrimitives.h | 31 ++- src/Mod/Part/Gui/Location.ui | 246 ++++++++++++++--- .../Part/Gui/ViewProviderHelixParametric.cpp | 43 +++ .../Part/Gui/ViewProviderHelixParametric.h | 7 + 5 files changed, 485 insertions(+), 91 deletions(-) diff --git a/src/Mod/Part/Gui/DlgPrimitives.cpp b/src/Mod/Part/Gui/DlgPrimitives.cpp index 9a42a4ae9e..f8d7109002 100644 --- a/src/Mod/Part/Gui/DlgPrimitives.cpp +++ b/src/Mod/Part/Gui/DlgPrimitives.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -48,6 +49,7 @@ #include #include #include +#include #include #include "DlgPrimitives.h" @@ -56,6 +58,8 @@ using namespace PartGui; namespace PartGui { +const char* ObjectNameSave; + const char* gce_ErrorStatusText(gce_ErrorType et) { switch (et) @@ -180,13 +184,49 @@ private: /* TRANSLATOR PartGui::DlgPrimitives */ -DlgPrimitives::DlgPrimitives(QWidget* parent) +DlgPrimitives::DlgPrimitives(QWidget* parent, PrimitiveType type, bool edit, const char* ObjectName) : QWidget(parent) { ui.setupUi(this); Gui::Command::doCommand(Gui::Command::Doc, "from FreeCAD import Base"); Gui::Command::doCommand(Gui::Command::Doc, "import Part,PartGui"); + ui.comboBox1->setCurrentIndex(type); + ui.widgetStack2->setCurrentIndex(type); + + // fill the dialog with data if the primitives already exists + if (edit) { + // if existing, the primitive type can not be changed by the user + ui.comboBox1->setDisabled(edit); + // get the primitives object + Gui::Document* doc = Gui::Application::Instance->activeDocument(); + if (!doc) + return; + auto docObj = doc->getDocument()->getObject(ObjectName); + + // read values from the properties + if (type == PrimitiveType::Helix) + { + auto Property = docObj->getPropertyByName("Pitch"); + auto value = static_cast(Property); + ui.helixPitch->setValue(value->getQuantityValue()); + Property = docObj->getPropertyByName("Height"); + value = static_cast(Property); + ui.helixHeight->setValue(value->getQuantityValue()); + Property = docObj->getPropertyByName("Radius"); + value = static_cast(Property); + ui.helixRadius->setValue(value->getQuantityValue()); + Property = docObj->getPropertyByName("Angle"); + value = static_cast(Property); + ui.helixAngle->setValue(value->getQuantityValue()); + Property = docObj->getPropertyByName("LocalCoord"); + auto HandedIndex = static_cast(Property); + ui.helixLocalCS->setCurrentIndex(HandedIndex->getValue()); + + // ToDo: connect signal if there is a preview of primitives available + } + } + // set limits // // plane @@ -682,15 +722,87 @@ void DlgPrimitives::createPrimitive(const QString& placement) } } +void DlgPrimitives::accept(const QString& placement) +{ + QString command; + + // get the current object + Gui::Document* doc = Gui::Application::Instance->activeDocument(); + if (!doc) + return; + auto docObj = doc->getDocument()->getObject(ObjectNameSave); + auto ObjectName = docObj->getNameInDocument(); + + // the combox with the primitive type is fixed + // therefore by reading its state we know what we need to change + if (ui.comboBox1->currentIndex() == 9) { // helix + + command = QString::fromLatin1( + "App.ActiveDocument.%1.Pitch=%2\n" + "App.ActiveDocument.%1.Height=%3\n" + "App.ActiveDocument.%1.Radius=%4\n" + "App.ActiveDocument.%1.Angle=%5\n" + "App.ActiveDocument.%1.LocalCoord=%6\n" + "App.ActiveDocument.%1.Placement=%7\n" + "App.ActiveDocument.recompute()\n") + .arg(QString::fromLatin1(ObjectName)) + .arg(ui.helixPitch->value().getValue(), 0, 'f', Base::UnitsApi::getDecimals()) + .arg(ui.helixHeight->value().getValue(), 0, 'f', Base::UnitsApi::getDecimals()) + .arg(ui.helixRadius->value().getValue(), 0, 'f', Base::UnitsApi::getDecimals()) + .arg(ui.helixAngle->value().getValue(), 0, 'f', Base::UnitsApi::getDecimals()) + .arg(ui.helixLocalCS->currentIndex()) + .arg(placement); + } + + // store command for undo + doc->openCommand(command.toUtf8()); + // execute command + Gui::Command::runCommand(Gui::Command::App, command.toLatin1()); + // commit undo command + doc->commitCommand(); +} + // ---------------------------------------------- /* TRANSLATOR PartGui::Location */ -Location::Location(QWidget* parent) +Location::Location(QWidget* parent, bool edit, const char* ObjectName) { Q_UNUSED(parent); mode = 0; ui.setupUi(this); + + ui.XPositionQSB->setUnit(Base::Unit::Length); + ui.YPositionQSB->setUnit(Base::Unit::Length); + ui.ZPositionQSB->setUnit(Base::Unit::Length); + ui.AngleQSB->setUnit(Base::Unit::Angle); + + // fill location widget if object already exists + if (edit) { + // get the primitives object + Gui::Document* doc = Gui::Application::Instance->activeDocument(); + if (!doc) + return; + auto docObj = doc->getDocument()->getObject(ObjectName); + // get the placement values + auto Property = docObj->getPropertyByName("Placement"); + auto placement = static_cast(Property); + + auto position = placement->getValue().getPosition(); + ui.XPositionQSB->setValue(position.x); + ui.YPositionQSB->setValue(position.y); + ui.ZPositionQSB->setValue(position.z); + + double rotationAngle; + Base::Vector3d rotationAxes; + auto rotation = placement->getValue().getRotation(); + rotation.getRawValue(rotationAxes, rotationAngle); + ui.XDirectionEdit->setValue(rotationAxes.x); + ui.YDirectionEdit->setValue(rotationAxes.y); + ui.ZDirectionEdit->setValue(rotationAxes.z); + // the angle is in this format: 180° = PI, thus transform it to deg + ui.AngleQSB->setValue(rotationAngle*180/M_PI); + } } Location::~Location() @@ -746,8 +858,12 @@ void Location::pickCallback(void * ud, SoEventCallback * n) SbVec3f pnt = point->getPoint(); SbVec3f nor = point->getNormal(); Location* dlg = reinterpret_cast(ud); - dlg->ui.loc->setPosition(Base::Vector3d(pnt[0],pnt[1],pnt[2])); - dlg->ui.loc->setDirection(Base::Vector3d(nor[0],nor[1],nor[2])); + dlg->ui.XPositionQSB->setValue(pnt[0]); + dlg->ui.YPositionQSB->setValue(pnt[1]); + dlg->ui.ZPositionQSB->setValue(pnt[2]); + dlg->ui.XDirectionEdit->setValue(nor[0]); + dlg->ui.YDirectionEdit->setValue(nor[1]); + dlg->ui.ZDirectionEdit->setValue(nor[2]); n->setHandled(); } } @@ -769,59 +885,28 @@ void Location::pickCallback(void * ud, SoEventCallback * n) QString Location::toPlacement() const { - Base::Vector3d d = ui.loc->getDirection(); - gp_Dir dir = gp_Dir(d.x,d.y,d.z); - gp_Pnt pnt = gp_Pnt(0.0,0.0,0.0); - gp_Ax3 ax3; + // create a command to set the position and angle of the primitive object - double cosNX = dir.Dot(gp::DX()); - double cosNY = dir.Dot(gp::DY()); - double cosNZ = dir.Dot(gp::DZ()); - std::vector cosXYZ; - cosXYZ.push_back(fabs(cosNX)); - cosXYZ.push_back(fabs(cosNY)); - cosXYZ.push_back(fabs(cosNZ)); + Base::Vector3d rot; + rot.x = ui.XDirectionEdit->value(); + rot.y = ui.YDirectionEdit->value(); + rot.z = ui.ZDirectionEdit->value(); - int pos = std::max_element(cosXYZ.begin(), cosXYZ.end()) - cosXYZ.begin(); + double angle = ui.AngleQSB->rawValue(); - // +X/-X - if (pos == 0) { - if (cosNX > 0) - ax3 = gp_Ax3(pnt, dir, gp_Dir(0,1,0)); - else - ax3 = gp_Ax3(pnt, dir, gp_Dir(0,-1,0)); - } - // +Y/-Y - else if (pos == 1) { - if (cosNY > 0) - ax3 = gp_Ax3(pnt, dir, gp_Dir(0,0,1)); - else - ax3 = gp_Ax3(pnt, dir, gp_Dir(0,0,-1)); - } - // +Z/-Z - else { - ax3 = gp_Ax3(pnt, dir, gp_Dir(1,0,0)); - } + Base::Vector3d loc; + loc.x = ui.XPositionQSB->rawValue(); + loc.y = ui.YPositionQSB->rawValue(); + loc.z = ui.ZPositionQSB->rawValue(); - gp_Trsf Trf; - Trf.SetTransformation(ax3); - Trf.Invert(); - - gp_XYZ theAxis(0,0,1); - Standard_Real theAngle = 0.0; - Trf.GetRotation(theAxis,theAngle); - - Base::Rotation rot(Base::convertTo(theAxis), theAngle); - Base::Vector3d loc = ui.loc->getPosition(); - - return QString::fromLatin1("Base.Placement(Base.Vector(%1,%2,%3),Base.Rotation(%4,%5,%6,%7))") - .arg(loc.x,0,'f',Base::UnitsApi::getDecimals()) - .arg(loc.y,0,'f',Base::UnitsApi::getDecimals()) - .arg(loc.z,0,'f',Base::UnitsApi::getDecimals()) - .arg(rot[0],0,'f',Base::UnitsApi::getDecimals()) - .arg(rot[1],0,'f',Base::UnitsApi::getDecimals()) - .arg(rot[2],0,'f',Base::UnitsApi::getDecimals()) - .arg(rot[3],0,'f',Base::UnitsApi::getDecimals()); + return QString::fromLatin1("App.Placement(App.Vector(%1,%2,%3),App.Rotation(App.Vector(%4,%5,%6),%7))") + .arg(loc.x, 0, 'f', Base::UnitsApi::getDecimals()) + .arg(loc.y, 0, 'f', Base::UnitsApi::getDecimals()) + .arg(loc.z, 0, 'f', Base::UnitsApi::getDecimals()) + .arg(rot.x, 0, 'f', Base::UnitsApi::getDecimals()) + .arg(rot.y, 0, 'f', Base::UnitsApi::getDecimals()) + .arg(rot.z, 0, 'f', Base::UnitsApi::getDecimals()) + .arg(angle, 0, 'f', Base::UnitsApi::getDecimals()); } // ---------------------------------------------- @@ -832,14 +917,13 @@ TaskPrimitives::TaskPrimitives() { Gui::TaskView::TaskBox* taskbox; widget = new DlgPrimitives(); - taskbox = new Gui::TaskView::TaskBox(QPixmap(), widget->windowTitle(),true, 0); + taskbox = new Gui::TaskView::TaskBox(QPixmap(), widget->windowTitle(), true, 0); taskbox->groupLayout()->addWidget(widget); Content.push_back(taskbox); location = new Location(); - taskbox = new Gui::TaskView::TaskBox(QPixmap(), location->windowTitle(),true, 0); + taskbox = new Gui::TaskView::TaskBox(QPixmap(), location->windowTitle() ,true, 0); taskbox->groupLayout()->addWidget(location); - taskbox->hideGroupBox(); Content.push_back(taskbox); } @@ -871,4 +955,57 @@ bool TaskPrimitives::reject() return true; } +// ---------------------------------------------- + +/* TRANSLATOR PartGui::TaskPrimitivesEdit */ + +TaskPrimitivesEdit::TaskPrimitivesEdit(DlgPrimitives::PrimitiveType type, const char* ObjectName) +{ + // save object name to be able to access it in accept() since if there are e.g. 3 helices + // the last one would be the active object, no matter that one is editing the first one + ObjectNameSave = ObjectName; + // create and show dialog for the primitives + Gui::TaskView::TaskBox* taskbox; + widget = new DlgPrimitives(0, type, true, ObjectName); + taskbox = new Gui::TaskView::TaskBox(QPixmap(), widget->windowTitle(), true, 0); + taskbox->groupLayout()->addWidget(widget); + Content.push_back(taskbox); + + // create and show dialog for the location + location = new Location(0, true, ObjectName); + taskbox = new Gui::TaskView::TaskBox(QPixmap(), location->windowTitle(), true, 0); + taskbox->groupLayout()->addWidget(location); + Content.push_back(taskbox); +} + +TaskPrimitivesEdit::~TaskPrimitivesEdit() +{ + // automatically deleted in the sub-class +} + +QDialogButtonBox::StandardButtons TaskPrimitivesEdit::getStandardButtons() const +{ + return QDialogButtonBox::Close | + QDialogButtonBox::Ok; +} + +void TaskPrimitivesEdit::modifyStandardButtons(QDialogButtonBox* box) +{ + QPushButton* btn = box->button(QDialogButtonBox::Ok); + btn->setText(QApplication::translate("PartGui::DlgPrimitives", "&OK")); +} + +bool TaskPrimitivesEdit::accept() +{ + widget->accept(location->toPlacement()); + Gui::Command::doCommand(Gui::Command::Gui, "Gui.activeDocument().resetEdit()"); + return true; +} + +bool TaskPrimitivesEdit::reject() +{ + Gui::Command::doCommand(Gui::Command::Gui, "Gui.activeDocument().resetEdit()"); + return true; +} + #include "moc_DlgPrimitives.cpp" diff --git a/src/Mod/Part/Gui/DlgPrimitives.h b/src/Mod/Part/Gui/DlgPrimitives.h index 14a1fd0af5..4bda9c260f 100644 --- a/src/Mod/Part/Gui/DlgPrimitives.h +++ b/src/Mod/Part/Gui/DlgPrimitives.h @@ -60,9 +60,16 @@ class DlgPrimitives : public QWidget Q_OBJECT public: - DlgPrimitives(QWidget* parent = 0); + enum PrimitiveType { + Plane, Box, Cylinder, Cone, Sphere, Ellipsoid, Torus, Prism, Wedge, + Helix, Spiral, Circle, Ellipse, Point, Line, RegPolygon + }; + + DlgPrimitives(QWidget* parent = 0, PrimitiveType type = PrimitiveType::Plane, + bool edit = false, const char* ObjectName = ""); ~DlgPrimitives(); void createPrimitive(const QString&); + void accept(const QString&); private Q_SLOTS: void on_buttonCircleFromThreePoints_clicked(); @@ -80,7 +87,7 @@ class Location : public QWidget Q_OBJECT public: - Location(QWidget* parent = 0); + Location(QWidget* parent = 0, bool edit = false, const char* ObjectName = ""); ~Location(); QString toPlacement() const; @@ -105,7 +112,6 @@ public: public: bool accept(); bool reject(); - QDialogButtonBox::StandardButtons getStandardButtons() const; void modifyStandardButtons(QDialogButtonBox*); @@ -114,6 +120,25 @@ private: Location* location; }; +class TaskPrimitivesEdit : public Gui::TaskView::TaskDialog +{ + Q_OBJECT + +public: + TaskPrimitivesEdit(DlgPrimitives::PrimitiveType type, const char* ObjectName); + ~TaskPrimitivesEdit(); + +public: + bool accept(); + bool reject(); + QDialogButtonBox::StandardButtons getStandardButtons() const; + void modifyStandardButtons(QDialogButtonBox*); + +private: + DlgPrimitives* widget; + Location* location; +}; + } // namespace PartGui #endif // PARTGUI_DLGPRIMITIVES_H diff --git a/src/Mod/Part/Gui/Location.ui b/src/Mod/Part/Gui/Location.ui index 69f22d4dd1..ce1b089399 100644 --- a/src/Mod/Part/Gui/Location.ui +++ b/src/Mod/Part/Gui/Location.ui @@ -6,8 +6,8 @@ 0 0 - 209 - 205 + 280 + 307 @@ -16,52 +16,229 @@ true - - - + + + + + + 0 + 0 + + Position - - - - + - - - Qt::Horizontal + + + X - - - 40 - 20 - - - + - + + + + + + + + + + + + - 3D View + Y + + + + + + + - - - Qt::Vertical + + + + + Z + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + Rotation + + + + + + true - - - 20 - 40 - + + Use custom vector for pad direction otherwise +the sketch plane's normal vector will be used - + + Rotation axis direction + + + false + + + + + + + + x + + + + + + + x-component of direction vector + + + false + + + -100.000000000000000 + + + 100.000000000000000 + + + 0.100000000000000 + + + + + + + + + + + + + + y + + + + + + + y-component of direction vector + + + false + + + -100.000000000000000 + + + 100.000000000000000 + + + 0.100000000000000 + + + + + + + + + + + + + + z + + + + + + + z-component of direction vector + + + false + + + -100.000000000000000 + + + 100.000000000000000 + + + 0.100000000000000 + + + 1.000000000000000 + + + + + + + + + + + + + + + + + Angle + + + + + + + + + + 5.000000000000000 + + + + @@ -71,9 +248,14 @@ - Gui::LocationWidget + Gui::QuantitySpinBox QWidget -
Gui/InputVector.h
+
Gui/QuantitySpinBox.h
+
+ + Gui::DoubleSpinBox + QDoubleSpinBox +
Gui/SpinBox.h
diff --git a/src/Mod/Part/Gui/ViewProviderHelixParametric.cpp b/src/Mod/Part/Gui/ViewProviderHelixParametric.cpp index ff39b7e10e..14678a1400 100644 --- a/src/Mod/Part/Gui/ViewProviderHelixParametric.cpp +++ b/src/Mod/Part/Gui/ViewProviderHelixParametric.cpp @@ -27,6 +27,9 @@ # include #endif +#include +#include +#include "DlgPrimitives.h" #include "ViewProviderHelixParametric.h" using namespace PartGui; @@ -55,6 +58,46 @@ std::vector ViewProviderHelixParametric::getDisplayModes(void) cons return StrList; } +void ViewProviderHelixParametric::setupContextMenu(QMenu* menu, QObject* receiver, const char* member) +{ + QAction* act; + act = menu->addAction(QObject::tr("Edit helix"), receiver, member); + act->setData(QVariant((int)ViewProvider::Default)); + ViewProviderSpline::setupContextMenu(menu, receiver, member); +} + +void ViewProviderHelixParametric::updateData(const App::Property* prop) +{ + PartGui::ViewProviderSpline::updateData(prop); +} + +bool ViewProviderHelixParametric::setEdit(int ModNum) +{ + if (ModNum == ViewProvider::Default) { + if (Gui::Control().activeDialog()) + return false; + auto ObjectName = getObject()->getNameInDocument(); + PartGui::TaskPrimitivesEdit* dlg + = new PartGui::TaskPrimitivesEdit(PartGui::DlgPrimitives::PrimitiveType::Helix, ObjectName); + Gui::Control().showDialog(dlg); + return true; + } + else { + ViewProviderSpline::setEdit(ModNum); + return true; + } +} + +void ViewProviderHelixParametric::unsetEdit(int ModNum) +{ + if (ModNum == ViewProvider::Default) { + Gui::Control().closeDialog(); + } + else { + ViewProviderSpline::unsetEdit(ModNum); + } +} + // ------------------------------------------------------------------ PROPERTY_SOURCE(PartGui::ViewProviderSpiralParametric, PartGui::ViewProviderSpline) diff --git a/src/Mod/Part/Gui/ViewProviderHelixParametric.h b/src/Mod/Part/Gui/ViewProviderHelixParametric.h index bb868c7330..bed55bce3b 100644 --- a/src/Mod/Part/Gui/ViewProviderHelixParametric.h +++ b/src/Mod/Part/Gui/ViewProviderHelixParametric.h @@ -39,6 +39,13 @@ public: /// destructor virtual ~ViewProviderHelixParametric(); std::vector getDisplayModes(void) const; + void setupContextMenu(QMenu*, QObject*, const char*); + +protected: + void updateData(const App::Property*); + bool setEdit(int ModNum); + void unsetEdit(int ModNum); + }; class PartGuiExport ViewProviderSpiralParametric : public ViewProviderSpline