From 4d9e4efc87182fca19825106111aef41f23745bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Br=C3=A6strup=20Sayoc?= Date: Sat, 14 Sep 2024 00:20:53 +0200 Subject: [PATCH] [TechDraw] Add ability to align points by rotating view Fixes #7061 --- src/Mod/TechDraw/App/DrawUtil.cpp | 24 ++++ src/Mod/TechDraw/App/DrawUtil.h | 2 + src/Mod/TechDraw/Gui/AppTechDrawGui.cpp | 2 + src/Mod/TechDraw/Gui/CMakeLists.txt | 1 + src/Mod/TechDraw/Gui/CommandAlign.cpp | 183 ++++++++++++++++++++++++ src/Mod/TechDraw/Gui/DrawGuiUtil.cpp | 55 +++++++ src/Mod/TechDraw/Gui/DrawGuiUtil.h | 11 ++ src/Mod/TechDraw/Gui/QGIVertex.cpp | 16 +++ src/Mod/TechDraw/Gui/QGIVertex.h | 7 + src/Mod/TechDraw/Gui/QGIView.cpp | 29 ++++ src/Mod/TechDraw/Gui/QGIView.h | 3 + src/Mod/TechDraw/Gui/Workbench.cpp | 7 + 12 files changed, 340 insertions(+) create mode 100644 src/Mod/TechDraw/Gui/CommandAlign.cpp diff --git a/src/Mod/TechDraw/App/DrawUtil.cpp b/src/Mod/TechDraw/App/DrawUtil.cpp index 3c3c84cec1..1ada065b33 100644 --- a/src/Mod/TechDraw/App/DrawUtil.cpp +++ b/src/Mod/TechDraw/App/DrawUtil.cpp @@ -101,6 +101,16 @@ using namespace TechDraw; } } +/*static*/ std::vector DrawUtil::getIndexFromName(const std::vector& geomNames) +{ + std::vector result; + result.reserve(200); + for (const std::string& geomName : geomNames) { + result.push_back(getIndexFromName(geomName)); + } + return result; +} + std::string DrawUtil::getGeomTypeFromName(const std::string& geomName) { if (geomName.empty()) { @@ -126,6 +136,20 @@ std::string DrawUtil::getGeomTypeFromName(const std::string& geomName) } } +//! Check if all geomNames are of same geomType +//! Edge1, Edge2, Edge3 -> true +//! Edge1, Edge2, Vertex7 -> false +bool DrawUtil::isGeomTypeConsistent(const std::vector& geomNames) +{ + std::string reference = getGeomTypeFromName(geomNames.at(0)); + for (std::string geomName : geomNames) { + if (reference != getGeomTypeFromName(geomName)) { + return false; + } + } + return true; +} + std::string DrawUtil::makeGeomName(const std::string& geomType, int index) { std::stringstream newName; diff --git a/src/Mod/TechDraw/App/DrawUtil.h b/src/Mod/TechDraw/App/DrawUtil.h index 1209f0bd3a..adc4960e41 100644 --- a/src/Mod/TechDraw/App/DrawUtil.h +++ b/src/Mod/TechDraw/App/DrawUtil.h @@ -91,7 +91,9 @@ class TechDrawExport DrawUtil { public: static int getIndexFromName(const std::string& geomName); + static std::vector getIndexFromName(const std::vector& geomNames); static std::string getGeomTypeFromName(const std::string& geomName); + static bool isGeomTypeConsistent(const std::vector& geomNames); static std::string makeGeomName(const std::string& geomType, int index); static bool isSamePoint(TopoDS_Vertex v1, TopoDS_Vertex v2, double tolerance = VERTEXTOLERANCE); static bool isZeroEdge(TopoDS_Edge e, double tolerance = VERTEXTOLERANCE); diff --git a/src/Mod/TechDraw/Gui/AppTechDrawGui.cpp b/src/Mod/TechDraw/Gui/AppTechDrawGui.cpp index 27d64fd6c2..8353303525 100644 --- a/src/Mod/TechDraw/Gui/AppTechDrawGui.cpp +++ b/src/Mod/TechDraw/Gui/AppTechDrawGui.cpp @@ -74,6 +74,7 @@ void CreateTechDrawCommandsAnnotate(); void CreateTechDrawCommandsExtensionDims(); void CreateTechDrawCommandsExtensions(); void CreateTechDrawCommandsStack(); +void CreateTechDrawCommandsAlign(); void loadTechDrawResource() { @@ -130,6 +131,7 @@ PyMOD_INIT_FUNC(TechDrawGui) CreateTechDrawCommandsExtensions(); CreateTechDrawCommandsDims(); CreateTechDrawCommandsStack(); + CreateTechDrawCommandsAlign(); TechDrawGui::Workbench::init(); TechDrawGui::MDIViewPage::init(); diff --git a/src/Mod/TechDraw/Gui/CMakeLists.txt b/src/Mod/TechDraw/Gui/CMakeLists.txt index 9339c5828c..445575c3d8 100644 --- a/src/Mod/TechDraw/Gui/CMakeLists.txt +++ b/src/Mod/TechDraw/Gui/CMakeLists.txt @@ -101,6 +101,7 @@ SET(TechDrawGui_SRCS CommandCreateDims.cpp CommandDecorate.cpp CommandAnnotate.cpp + CommandAlign.cpp CommandExtensionDims.cpp CommandExtensionDims.h CommandExtensionPack.cpp diff --git a/src/Mod/TechDraw/Gui/CommandAlign.cpp b/src/Mod/TechDraw/Gui/CommandAlign.cpp new file mode 100644 index 0000000000..5fc81500da --- /dev/null +++ b/src/Mod/TechDraw/Gui/CommandAlign.cpp @@ -0,0 +1,183 @@ +/*************************************************************************** + * Copyright (c) 2024 Benjamin Bræstrup Sayoc * + * * + * 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 + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "QGIView.h" +#include "DrawGuiUtil.h" +#include "ViewProviderViewPart.h" + + +const auto& getSelection = Gui::Command::getSelection; // alias +using namespace TechDrawGui; +using namespace TechDraw; + +namespace TechDrawGui { +class QGIEdge; +class QGIVertex; +} + +namespace { +void incorrectSelection() +{ + QMessageBox::warning( + Gui::getMainWindow(), + QObject::tr("Incorrect Selection"), + QObject::tr("You must select 2 vertexes or 1 edge\n") + ); +} + +void CmdTechDrawAlignByRotation(const Base::Vector2d& direction) +{ + std::vector dvps = getSelection().getObjectsOfType(); + if (dvps.size() != 1) { + incorrectSelection(); + return; + } + TechDraw::DrawViewPart* dvp = dvps[0]; + + Gui::Document* guiDoc = Gui::Application::Instance->getDocument(dvp->getDocument()); + if (!guiDoc) { + return; + } + + Gui::ViewProvider* gvp = guiDoc->getViewProvider(dvp); + auto vpdvp = static_cast(gvp); + if (!vpdvp) { + return; + } + QGIView* view = vpdvp->getQView(); + + std::vector subNames = getSelection().getSelectionEx()[0].getSubNames(); + if(!DrawUtil::isGeomTypeConsistent(subNames)) { + incorrectSelection(); + return; + } + + std::vector subIndexes = DrawUtil::getIndexFromName(subNames); + std::string subType = DrawUtil::getGeomTypeFromName(subNames.at(0)); + if (subType == "Vertex") { + std::vector vertexes = view->getObjects(subIndexes); + if (vertexes.size() == 2) { + QGIVertex* v1 = vertexes.front(); + QGIVertex* v2 = vertexes.back(); + DrawGuiUtil::rotateToAlign(v1, v2, direction); + dvp->recomputeFeature(); + return; + } + } + else if (subType == "Edge") { + std::vector edges = view->getObjects(subIndexes); + if (edges.size() == 1) { + DrawGuiUtil::rotateToAlign(edges.at(0), direction); + dvp->recomputeFeature(); + return; + } + } + + incorrectSelection(); +} +} // anonymous namespace + + +//=========================================================================== +// TechDraw_AlignVertexesVertically +//=========================================================================== + +DEF_STD_CMD_A(CmdTechDrawAlignVertexesVertically) + +CmdTechDrawAlignVertexesVertically::CmdTechDrawAlignVertexesVertically() + : Command("TechDraw_AlignVertexesVertically") +{ + sAppModule = "TechDraw"; + sGroup = QT_TR_NOOP("TechDraw"); + sMenuText = QT_TR_NOOP("Align vertexes/edge vertically by view rotation"); + sToolTipText = sMenuText; + sWhatsThis = "TechDraw_AlignGroup"; + sStatusTip = sToolTipText; +} + +void CmdTechDrawAlignVertexesVertically::activated(int iMsg) +{ + Q_UNUSED(iMsg) + + Base::Vector2d Vertical(0.0, 1.0); + CmdTechDrawAlignByRotation(Vertical); +} + +bool CmdTechDrawAlignVertexesVertically::isActive(void) +{ + bool havePage = DrawGuiUtil::needPage(this); + bool haveView = DrawGuiUtil::needView(this, false); + return (havePage && haveView); +} + + +//=========================================================================== +// TechDraw_AlignVertexesHorizontally +//=========================================================================== + +DEF_STD_CMD_A(CmdTechDrawAlignVertexesHorizontally) + +CmdTechDrawAlignVertexesHorizontally::CmdTechDrawAlignVertexesHorizontally() + : Command("TechDraw_AlignVertexesHorizontally") +{ + sAppModule = "TechDraw"; + sGroup = QT_TR_NOOP("TechDraw"); + sMenuText = QT_TR_NOOP("Align vertexes/edge horizontally by view rotation"); + sToolTipText = sMenuText; + sWhatsThis = "TechDraw_AlignGroup"; + sStatusTip = sToolTipText; +} + +void CmdTechDrawAlignVertexesHorizontally::activated(int iMsg) +{ + Q_UNUSED(iMsg) + + Base::Vector2d Horizontal(1.0, 0.0); + CmdTechDrawAlignByRotation(Horizontal); +} + +bool CmdTechDrawAlignVertexesHorizontally::isActive(void) +{ + bool havePage = DrawGuiUtil::needPage(this); + bool haveView = DrawGuiUtil::needView(this, false); + return (havePage && haveView); +} + + +void CreateTechDrawCommandsAlign(void) +{ + Gui::CommandManager& rcCmdMgr = Gui::Application::Instance->commandManager(); + + rcCmdMgr.addCommand(new CmdTechDrawAlignVertexesVertically()); + rcCmdMgr.addCommand(new CmdTechDrawAlignVertexesHorizontally()); +} diff --git a/src/Mod/TechDraw/Gui/DrawGuiUtil.cpp b/src/Mod/TechDraw/Gui/DrawGuiUtil.cpp index a36782442f..b0208366f1 100644 --- a/src/Mod/TechDraw/Gui/DrawGuiUtil.cpp +++ b/src/Mod/TechDraw/Gui/DrawGuiUtil.cpp @@ -1,5 +1,6 @@ /*************************************************************************** * Copyright (c) 2016 WandererFan * + * Copyright (c) 2024 Benjamin Bræstrup Sayoc * * * * This file is part of the FreeCAD CAx development system. * * * @@ -48,6 +49,7 @@ #include #include #include +#include #include #include #include @@ -74,6 +76,9 @@ #include "DlgPageChooser.h" #include "DrawGuiUtil.h" #include "MDIViewPage.h" +#include "QGIEdge.h" +#include "QGIVertex.h" +#include "QGIViewPart.h" #include "QGSPage.h" #include "ViewProviderPage.h" #include "Rez.h" @@ -776,3 +781,53 @@ QIcon DrawGuiUtil::maskBlackPixels(QIcon itemIcon, QSize iconSize, QColor textCo return filler; } +void DrawGuiUtil::rotateToAlign(const QGIEdge* edge, const Base::Vector2d& direction) +{ + QGIViewPart* view = static_cast(edge->parentItem()); + DrawViewPart* dvp = static_cast(view->getViewObject()); + BaseGeomPtr bg = dvp->getEdgeGeometry().at(edge->getProjIndex()); + std::vector endPoints = bg->findEndPoints(); + Base::Vector3d oldDirection3d = endPoints.at(0) - endPoints.at(1); + Base::Vector2d oldDirection2d(oldDirection3d.x, oldDirection3d.y); + rotateToAlign(dvp, oldDirection2d, direction); +} + +//! The view of p1 and p2 will be rotated to make p1 and p2 aligned with direction (for instance horizontalle aligned) +void DrawGuiUtil::rotateToAlign(const QGIVertex* p1, const QGIVertex* p2, const Base::Vector2d& direction) +{ + QGIViewPart* view = static_cast(p1->parentItem()); + if(view != static_cast(p2->parentItem())) { + Base::Console().Error("Vertexes have to be from the same view!"); + } + + Base::Vector2d oldDirection = p2->vector2dBetweenPoints(p1); + DrawViewPart* dvp = static_cast(view->getViewObject()); + rotateToAlign(dvp, oldDirection, direction); +} + +void DrawGuiUtil::rotateToAlign(DrawViewPart* view, const Base::Vector2d& oldDirection, const Base::Vector2d& newDirection) +{ + // If pointing counterclockwise, we need to rotate clockwise + // If pointing clockwise, we need to rotate counter clockwise + int cw = 1; + if(newDirection.Angle() > oldDirection.Angle()) { + cw = -1; + } + + double toRotate = newDirection.GetAngle(oldDirection); + // Radians to degrees + toRotate = toRotate * 180 / M_PI; + + // Rotate least amount possible + if(toRotate > 90) { + // Instead of rotating 145 degrees to match direction + // we only rotate -35 degrees + toRotate = toRotate - 180; + } + else if(toRotate < -90) { + toRotate = toRotate + 180; + } + + double oldRotation = view->Rotation.getValue(); + view->Rotation.setValue(oldRotation + toRotate * cw); +} diff --git a/src/Mod/TechDraw/Gui/DrawGuiUtil.h b/src/Mod/TechDraw/Gui/DrawGuiUtil.h index 28ab308143..a0dcfa1ca7 100644 --- a/src/Mod/TechDraw/Gui/DrawGuiUtil.h +++ b/src/Mod/TechDraw/Gui/DrawGuiUtil.h @@ -44,14 +44,21 @@ class Feature; namespace TechDraw { class DrawPage; class DrawView; +class DrawViewPart; class LineGenerator; } namespace Gui { class Command; } +namespace Base { +class Vector2d; +} + namespace TechDrawGui { +class QGIEdge; +class QGIVertex; /// Convenient utility functions for TechDraw Gui Module class TechDrawGuiExport DrawGuiUtil { @@ -94,6 +101,10 @@ class TechDrawGuiExport DrawGuiUtil { const App::DocumentObject& targetObject); static std::vector getSubsForSelectedObject(const std::vector& selection, App::DocumentObject* selectedObj); + + static void rotateToAlign(const QGIEdge* edge, const Base::Vector2d& direction); + static void rotateToAlign(const QGIVertex* p1, const QGIVertex* p2, const Base::Vector2d& direction); + static void rotateToAlign(TechDraw::DrawViewPart* view, const Base::Vector2d& oldDirection, const Base::Vector2d& newDirection); }; } //end namespace TechDrawGui diff --git a/src/Mod/TechDraw/Gui/QGIVertex.cpp b/src/Mod/TechDraw/Gui/QGIVertex.cpp index 817067103b..085f052a38 100644 --- a/src/Mod/TechDraw/Gui/QGIVertex.cpp +++ b/src/Mod/TechDraw/Gui/QGIVertex.cpp @@ -1,5 +1,6 @@ /*************************************************************************** * Copyright (c) 2013 Luke Parry * + * Copyright (c) 2024 Benjamin Bræstrup Sayoc * * * * This file is part of the FreeCAD CAx development system. * * * @@ -29,6 +30,8 @@ # include #endif +#include + #include "QGIVertex.h" #include "PreferencesGui.h" #include "QGIPrimPath.h" @@ -67,3 +70,16 @@ void QGIVertex::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, // setBrush(m_brush); QGIPrimPath::paint (painter, &myOption, widget); } + +Base::Vector2d QGIVertex::toVector2d() const +{ + QPointF center = boundingRect().center(); + center = mapToScene(center); + return Base::Vector2d(center.x(), center.y()); +} + +//! Returns a vector drawn from own position to p2 +Base::Vector2d QGIVertex::vector2dBetweenPoints(const QGIVertex* p2) const +{ + return p2->toVector2d() - toVector2d(); +} diff --git a/src/Mod/TechDraw/Gui/QGIVertex.h b/src/Mod/TechDraw/Gui/QGIVertex.h index 65fdaa69c6..f030410bab 100644 --- a/src/Mod/TechDraw/Gui/QGIVertex.h +++ b/src/Mod/TechDraw/Gui/QGIVertex.h @@ -27,6 +27,10 @@ # include "QGIPrimPath.h" +namespace Base { +class Vector2d; +} + namespace TechDrawGui { @@ -45,6 +49,9 @@ public: float getRadius() { return m_radius; } virtual void setRadius(float r); + Base::Vector2d toVector2d() const; + Base::Vector2d vector2dBetweenPoints(const QGIVertex* p2) const; + protected: bool multiselectEligible() override { return true; } diff --git a/src/Mod/TechDraw/Gui/QGIView.cpp b/src/Mod/TechDraw/Gui/QGIView.cpp index 89bdd5e34c..321a373a7a 100644 --- a/src/Mod/TechDraw/Gui/QGIView.cpp +++ b/src/Mod/TechDraw/Gui/QGIView.cpp @@ -1,5 +1,6 @@ /*************************************************************************** * Copyright (c) 2012-2013 Luke Parry * + * Copyright (c) 2024 Benjamin Bræstrup Sayoc * * * * This file is part of the FreeCAD CAx development system. * * * @@ -56,6 +57,7 @@ #include "QGCustomImage.h" #include "QGCustomLabel.h" #include "QGICaption.h" +#include "QGIEdge.h" #include "QGIVertex.h" #include "QGIViewClip.h" #include "QGSPage.h" @@ -972,4 +974,31 @@ void QGIView::makeMark(QPointF pos, QColor color) makeMark(pos.x(), pos.y(), color); } +//! Retrieves objects of type T with given indexes +template +std::vector QGIView::getObjects(std::vector indexes) +{ + QList children = childItems(); + std::vector result; + for (QGraphicsItem*& child : children) { + // Convert QGIVertex* (as T) to QGIVertex + if (child->type() != std::remove_pointer::type::Type) { + continue; + } + + // Get index of child item + T object = static_cast(child); + int target = object->getProjIndex(); + // If child item's index in indexes, then add to results + if (std::find(indexes.begin(), indexes.end(), target) != indexes.end()) { + result.push_back(object); + } + } + return result; +} + +template std::vector QGIView::getObjects(std::vector); +template std::vector QGIView::getObjects(std::vector); + + #include diff --git a/src/Mod/TechDraw/Gui/QGIView.h b/src/Mod/TechDraw/Gui/QGIView.h index 7580b0fd5a..34f1935933 100644 --- a/src/Mod/TechDraw/Gui/QGIView.h +++ b/src/Mod/TechDraw/Gui/QGIView.h @@ -168,6 +168,9 @@ public: void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override; void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override; + template + std::vector getObjects(std::vector indexes); + protected: QGIView* getQGIVByName(std::string name); diff --git a/src/Mod/TechDraw/Gui/Workbench.cpp b/src/Mod/TechDraw/Gui/Workbench.cpp index b9b716edc6..c5bb4a6570 100644 --- a/src/Mod/TechDraw/Gui/Workbench.cpp +++ b/src/Mod/TechDraw/Gui/Workbench.cpp @@ -252,6 +252,12 @@ Gui::MenuItem* Workbench::setupMenuBar() const *symbols << "TechDraw_SurfaceFinishSymbols"; *symbols << "TechDraw_HoleShaftFit"; + + Gui::MenuItem* aligning = new Gui::MenuItem; + aligning->setCommand("Aligning"); + *aligning << "TechDraw_AlignVertexesVertically"; + *aligning << "TechDraw_AlignVertexesHorizontally"; + // main menu draw->setCommand("TechDraw"); *draw << pages; @@ -267,6 +273,7 @@ Gui::MenuItem* Workbench::setupMenuBar() const *draw << symbols; *draw << "Separator"; *draw << stacking; + *draw << aligning; *draw << "Separator"; *draw << toolattrib; *draw << toolcenter;