[TechDraw] Add ability to align points by rotating view

Fixes #7061
This commit is contained in:
Benjamin Bræstrup Sayoc
2024-09-14 00:20:53 +02:00
committed by WandererFan
parent 516595fbce
commit 4d9e4efc87
12 changed files with 340 additions and 0 deletions

View File

@@ -101,6 +101,16 @@ using namespace TechDraw;
}
}
/*static*/ std::vector<int> DrawUtil::getIndexFromName(const std::vector<std::string>& geomNames)
{
std::vector<int> 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<std::string>& 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;

View File

@@ -91,7 +91,9 @@ class TechDrawExport DrawUtil
{
public:
static int getIndexFromName(const std::string& geomName);
static std::vector<int> getIndexFromName(const std::vector<std::string>& geomNames);
static std::string getGeomTypeFromName(const std::string& geomName);
static bool isGeomTypeConsistent(const std::vector<std::string>& 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);

View File

@@ -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();

View File

@@ -101,6 +101,7 @@ SET(TechDrawGui_SRCS
CommandCreateDims.cpp
CommandDecorate.cpp
CommandAnnotate.cpp
CommandAlign.cpp
CommandExtensionDims.cpp
CommandExtensionDims.h
CommandExtensionPack.cpp

View File

@@ -0,0 +1,183 @@
/***************************************************************************
* Copyright (c) 2024 Benjamin Bræstrup Sayoc <benj5378@outlook.com> *
* *
* 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 <QMessageBox>
#include <App/DocumentObject.h>
#include <Base/Console.h>
#include <Gui/Action.h>
#include <Gui/Command.h>
#include <Gui/Document.h>
#include <Gui/MainWindow.h>
#include <Gui/Selection/Selection.h>
#include <Mod/TechDraw/App/DrawViewPart.h>
#include <Mod/TechDraw/App/DrawUtil.h>
#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<TechDraw::DrawViewPart*> dvps = getSelection().getObjectsOfType<TechDraw::DrawViewPart>();
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<TechDrawGui::ViewProviderViewPart*>(gvp);
if (!vpdvp) {
return;
}
QGIView* view = vpdvp->getQView();
std::vector<std::string> subNames = getSelection().getSelectionEx()[0].getSubNames();
if(!DrawUtil::isGeomTypeConsistent(subNames)) {
incorrectSelection();
return;
}
std::vector<int> subIndexes = DrawUtil::getIndexFromName(subNames);
std::string subType = DrawUtil::getGeomTypeFromName(subNames.at(0));
if (subType == "Vertex") {
std::vector<QGIVertex*> vertexes = view->getObjects<QGIVertex*>(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<QGIEdge*> edges = view->getObjects<QGIEdge*>(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());
}

View File

@@ -1,5 +1,6 @@
/***************************************************************************
* Copyright (c) 2016 WandererFan <wandererfan@gmail.com> *
* Copyright (c) 2024 Benjamin Bræstrup Sayoc <benj5378@outlook.com> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
@@ -48,6 +49,7 @@
#include <Base/Exception.h>
#include <Base/Parameter.h>
#include <Base/Tools.h>
#include <Base/Tools2D.h>
#include <Base/Type.h>
#include <Gui/Application.h>
#include <Gui/Command.h>
@@ -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<QGIViewPart*>(edge->parentItem());
DrawViewPart* dvp = static_cast<DrawViewPart*>(view->getViewObject());
BaseGeomPtr bg = dvp->getEdgeGeometry().at(edge->getProjIndex());
std::vector<Base::Vector3d> 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<QGIViewPart*>(p1->parentItem());
if(view != static_cast<QGIViewPart*>(p2->parentItem())) {
Base::Console().Error("Vertexes have to be from the same view!");
}
Base::Vector2d oldDirection = p2->vector2dBetweenPoints(p1);
DrawViewPart* dvp = static_cast<DrawViewPart*>(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);
}

View File

@@ -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<std::string> getSubsForSelectedObject(const std::vector<Gui::SelectionObject>& 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

View File

@@ -1,5 +1,6 @@
/***************************************************************************
* Copyright (c) 2013 Luke Parry <l.parry@warwick.ac.uk> *
* Copyright (c) 2024 Benjamin Bræstrup Sayoc <benj5378@outlook.com> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
@@ -29,6 +30,8 @@
# include <QStyleOptionGraphicsItem>
#endif
#include <Base/Tools2D.h>
#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();
}

View File

@@ -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; }

View File

@@ -1,5 +1,6 @@
/***************************************************************************
* Copyright (c) 2012-2013 Luke Parry <l.parry@warwick.ac.uk> *
* Copyright (c) 2024 Benjamin Bræstrup Sayoc <benj5378@outlook.com> *
* *
* 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 <typename T>
std::vector<T> QGIView::getObjects(std::vector<int> indexes)
{
QList<QGraphicsItem*> children = childItems();
std::vector<T> result;
for (QGraphicsItem*& child : children) {
// Convert QGIVertex* (as T) to QGIVertex
if (child->type() != std::remove_pointer<T>::type::Type) {
continue;
}
// Get index of child item
T object = static_cast<T>(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<QGIVertex*> QGIView::getObjects<QGIVertex*>(std::vector<int>);
template std::vector<QGIEdge*> QGIView::getObjects<QGIEdge*>(std::vector<int>);
#include <Mod/TechDraw/Gui/moc_QGIView.cpp>

View File

@@ -168,6 +168,9 @@ public:
void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
template <typename T>
std::vector<T> getObjects(std::vector<int> indexes);
protected:
QGIView* getQGIVByName(std::string name);

View File

@@ -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;