diff --git a/src/Gui/CMakeLists.txt b/src/Gui/CMakeLists.txt index cb75c5cc66..57dc5d60d8 100644 --- a/src/Gui/CMakeLists.txt +++ b/src/Gui/CMakeLists.txt @@ -1217,6 +1217,7 @@ SET(FreeCADGui_CPP_SRCS ManualAlignment.cpp StartupProcess.cpp TransactionObject.cpp + ToolHandler.cpp ) SET(FreeCADGui_SRCS Application.h @@ -1254,6 +1255,7 @@ SET(FreeCADGui_SRCS ManualAlignment.h StartupProcess.h TransactionObject.h + ToolHandler.h ${FreeCADGui_SDK_MOC_HDRS} ) diff --git a/src/Gui/ToolHandler.cpp b/src/Gui/ToolHandler.cpp new file mode 100644 index 0000000000..722aa197fd --- /dev/null +++ b/src/Gui/ToolHandler.cpp @@ -0,0 +1,288 @@ +/*************************************************************************** + * Copyright (c) 2024 Pierre-Louis Boyer * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#include "PreCompiled.h" +#ifndef _PreComp_ +#include + +#include +#include + +#include +#endif // #ifndef _PreComp_ + +#include +#include + +#include "Application.h" +#include "BitmapFactory.h" +#include "CommandT.h" +#include "MainWindow.h" +#include "View3DInventor.h" +#include "View3DInventorViewer.h" + +#include "ToolHandler.h" + +using namespace Gui; + +/**************************** ToolHandler *******************************************/ + +QString ToolHandler::getCrosshairCursorSVGName() const +{ + return QString::fromLatin1("None"); +} + +bool ToolHandler::activate() +{ + // save the cursor at the time the DSH is activated + QWidget* cw = getCursorWidget(); + if (cw) { + oldCursor = cw->cursor(); + + updateCursor(); + + this->preActivated(); + this->activated(); + return true; + } + + return false; +} + +void ToolHandler::deactivate() +{ + this->deactivated(); + this->postDeactivated(); + + unsetCursor(); +} + +//************************************************************************** +// Helpers + +unsigned long ToolHandler::getCrosshairColor() +{ + unsigned long color = 0xFFFFFFFF; // white + ParameterGrp::handle hGrp = + App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/View"); + color = hGrp->GetUnsigned("CursorCrosshairColor", color); + // from rgba to rgb + color = (color >> 8) & 0xFFFFFF; + return color; +} + +void ToolHandler::setCrosshairCursor(const QString& svgName) +{ + const unsigned long defaultCrosshairColor = 0xFFFFFF; + unsigned long color = getCrosshairColor(); + auto colorMapping = std::map(); + colorMapping[defaultCrosshairColor] = color; + // hot spot of all SVG icons should be 8,8 for 32x32 size (16x16 for 64x64) + int hotX = 8; + int hotY = 8; + setSvgCursor(svgName, hotX, hotY, colorMapping); +} + +void ToolHandler::setCrosshairCursor(const char* svgName) +{ + QString cursorName = QString::fromLatin1(svgName); + setCrosshairCursor(cursorName); +} + +void ToolHandler::setSvgCursor(const QString& cursorName, + int x, + int y, + const std::map& colorMapping) +{ + // The TechDraw_Pointer_*.svg icons have a default size of 64x64. When directly creating + // them with a size of 32x32 they look very bad. + // As a workaround the icons are created with 64x64 and afterwards the pixmap is scaled to + // 32x32. This workaround is only needed if pRatio is equal to 1.0 + // + qreal pRatio = devicePixelRatio(); + bool isRatioOne = (pRatio == 1.0); + qreal defaultCursorSize = isRatioOne ? 64 : 32; + qreal hotX = x; + qreal hotY = y; +#if !defined(Q_OS_WIN32) && !defined(Q_OS_MAC) + if (qGuiApp->platformName() == QLatin1String("xcb")) { + hotX *= pRatio; + hotY *= pRatio; + } +#endif + qreal cursorSize = defaultCursorSize * pRatio; + + QPixmap pointer = Gui::BitmapFactory().pixmapFromSvg(cursorName.toStdString().c_str(), + QSizeF(cursorSize, cursorSize), + colorMapping); + if (isRatioOne) { + pointer = pointer.scaled(32, 32); + } + pointer.setDevicePixelRatio(pRatio); + setCursor(pointer, hotX, hotY, false); +} + +void ToolHandler::setCursor(const QPixmap& p, int x, int y, bool autoScale) +{ + + QWidget* cw = getCursorWidget(); + if (cw) { + QCursor cursor; + QPixmap p1(p); + // TODO remove autoScale after all cursors are SVG-based + if (autoScale) { + qreal pRatio = devicePixelRatio(); + int newWidth = p.width() * pRatio; + int newHeight = p.height() * pRatio; + p1 = p1.scaled(newWidth, newHeight, Qt::KeepAspectRatio, Qt::SmoothTransformation); + p1.setDevicePixelRatio(pRatio); + qreal hotX = x; + qreal hotY = y; +#if !defined(Q_OS_WIN32) && !defined(Q_OS_MAC) + if (qGuiApp->platformName() == QLatin1String("xcb")) { + hotX *= pRatio; + hotY *= pRatio; + } +#endif + cursor = QCursor(p1, hotX, hotY); + } + else { + // already scaled + cursor = QCursor(p1, x, y); + } + + actCursor = cursor; + actCursorPixmap = p1; + + setWidgetCursor(cursor); + } +} + +void ToolHandler::addCursorTail(std::vector& pixmaps) +{ + // Create a pixmap that will contain icon and each autoconstraint icon + QPixmap baseIcon = QPixmap(actCursorPixmap); + baseIcon.setDevicePixelRatio(actCursorPixmap.devicePixelRatio()); + qreal pixelRatio = baseIcon.devicePixelRatio(); + // cursor size in device independent pixels + qreal baseCursorWidth = baseIcon.width(); + qreal baseCursorHeight = baseIcon.height(); + + int tailWidth = 0; + for (auto const& p : pixmaps) { + tailWidth += p.width(); + } + + int newIconWidth = baseCursorWidth + tailWidth; + int newIconHeight = baseCursorHeight; + + QPixmap newIcon(newIconWidth, newIconHeight); + newIcon.fill(Qt::transparent); + + QPainter qp; + qp.begin(&newIcon); + + qp.drawPixmap(QPointF(0, 0), + baseIcon.scaled(baseCursorWidth * pixelRatio, + baseCursorHeight * pixelRatio, + Qt::KeepAspectRatio, + Qt::SmoothTransformation)); + + // Iterate through pixmaps and them to the cursor pixmap + qreal currentIconX = baseCursorWidth; + qreal currentIconY; + + for (auto& icon : pixmaps) { + currentIconY = baseCursorHeight - icon.height(); + qp.drawPixmap(QPointF(currentIconX, currentIconY), icon); + currentIconX += icon.width(); + } + + qp.end(); // Finish painting + + // Create the new cursor with the icon. + QPoint p = actCursor.hotSpot(); + newIcon.setDevicePixelRatio(pixelRatio); + QCursor newCursor(newIcon, p.x(), p.y()); + applyCursor(newCursor); +} + +void ToolHandler::updateCursor() +{ + auto cursorstring = getCrosshairCursorSVGName(); + + if (cursorstring != QString::fromLatin1("None")) { + setCrosshairCursor(cursorstring); + } +} + +void ToolHandler::applyCursor() +{ + applyCursor(actCursor); +} + +void ToolHandler::applyCursor(QCursor& newCursor) +{ + setWidgetCursor(newCursor); +} + +void ToolHandler::unsetCursor() +{ + setWidgetCursor(oldCursor); +} + +qreal ToolHandler::devicePixelRatio() +{ + qreal pixelRatio = 1; + + QWidget* cw = getCursorWidget(); + if (cw) { + pixelRatio = cw->devicePixelRatio(); + } + return pixelRatio; +} + +QWidget* ToolHandler::getCursorWidget() +{ + Gui::View3DInventorViewer* viewer = getViewer(); + if (viewer) { + return viewer->getWidget(); + } + return nullptr; +} + +void ToolHandler::setWidgetCursor(QCursor cursor) +{ + QWidget* cw = getCursorWidget(); + if (cw) { + cw->setCursor(cursor); + } +} + +Gui::View3DInventorViewer* ToolHandler::getViewer() +{ + Gui::MDIView* view = Gui::getMainWindow()->activeWindow(); + if (view && view->isDerivedFrom(Gui::View3DInventor::getClassTypeId())) { + return static_cast(view)->getViewer(); + } + return nullptr; +} diff --git a/src/Gui/ToolHandler.h b/src/Gui/ToolHandler.h new file mode 100644 index 0000000000..100fa5bd5f --- /dev/null +++ b/src/Gui/ToolHandler.h @@ -0,0 +1,127 @@ +/*************************************************************************** + * Copyright (c) 2024 Pierre-Louis Boyer * + * * + * 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_ToolHandler_H +#define GUI_ToolHandler_H + +#include +#include + +#include +#include + +#include "Selection.h" + + +namespace Gui +{ +class View3DInventorViewer; + + +class GuiExport ToolHandler +{ +public: + ToolHandler() = default; + virtual ~ToolHandler() = default; + + bool activate(); + virtual void deactivate(); + + virtual void quit() + {} + + /// updates the actCursor with the icon by calling getCrosshairCursorSVGName(), + /// enabling to set data member dependent icons (i.e. for different construction methods) + void updateCursor(); + +private: // NVI + virtual void preActivated() + {} + virtual void activated() + {} + virtual void deactivated() + {} + virtual void postDeactivated() + {} + +protected: // NVI requiring base implementation + virtual QString getCrosshairCursorSVGName() const; + + +protected: + // helpers + /** + * Sets a cursor for 3D inventor view. + * pixmap as a cursor image in device independent pixels. + * + * \param autoScale - set this to false if pixmap already scaled for HiDPI + **/ + + /** @name Icon helpers */ + //@{ + void setCursor(const QPixmap& pixmap, int x, int y, bool autoScale = true); + + + void unsetCursor(); + + /// restitutes the DSH cached cursor (without any tail due to autoconstraints, ...) + void applyCursor(); + + void addCursorTail(std::vector& pixmaps); + + /// returns the color to be used for the crosshair (configurable as a parameter) + unsigned long getCrosshairColor(); + + /// functions to set the cursor to a given svgName (to be migrated to NVI style) + + qreal devicePixelRatio(); + //@} + + View3DInventorViewer* getViewer(); + + virtual QWidget* getCursorWidget(); + + virtual void setWidgetCursor(QCursor cursor); + +private: + void setSvgCursor(const QString& svgName, int x, int y, + const std::map& colorMapping = + std::map()); + + + void applyCursor(QCursor& newCursor); + + void setCrosshairCursor(const QString& svgName); + void setCrosshairCursor(const char* svgName); + +protected: + + QCursor oldCursor; + QCursor actCursor; + QPixmap actCursorPixmap; +}; + + +} // namespace Gui + + +#endif // GUI_ToolHandler_H diff --git a/src/Mod/Sketcher/Gui/CommandConstraints.cpp b/src/Mod/Sketcher/Gui/CommandConstraints.cpp index 409c43b89b..c29345db24 100644 --- a/src/Mod/Sketcher/Gui/CommandConstraints.cpp +++ b/src/Mod/Sketcher/Gui/CommandConstraints.cpp @@ -1376,6 +1376,7 @@ public: , selLine({}) , selCircleArc({}) , selEllipseAndCo({}) + , selSplineAndCo({}) , initialSelection(std::move(SubNames)) , numberOfConstraintsCreated(0) { @@ -1616,6 +1617,15 @@ protected: Sketcher::SketchObject* Obj; + void clearRefVectors() + { + selPoints.clear(); + selLine.clear(); + selCircleArc.clear(); + selEllipseAndCo.clear(); + selSplineAndCo.clear(); + } + void handleInitialSelection() { if (initialSelection.size() == 0) { @@ -1648,11 +1658,7 @@ protected: bool selAllowed = makeAppropriateConstraint(Base::Vector2d(0.,0.)); if (!selAllowed) { - selPoints.clear(); - selLine.clear(); - selCircleArc.clear(); - selEllipseAndCo.clear(); - selSplineAndCo.clear(); + clearRefVectors(); } } @@ -1686,10 +1692,7 @@ protected: numberOfConstraintsCreated = 0; specialConstraint = SpecialConstraint::None; previousOnSketchPos = Base::Vector2d(0.f, 0.f); - selPoints.clear(); - selLine.clear(); - selCircleArc.clear(); - selEllipseAndCo.clear(); + clearRefVectors(); } else { sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandler.cpp b/src/Mod/Sketcher/Gui/DrawSketchHandler.cpp index 9bf503e569..f9799f4bcd 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandler.cpp +++ b/src/Mod/Sketcher/Gui/DrawSketchHandler.cpp @@ -270,7 +270,8 @@ void CurveConverter::OnChange(Base::Subject& rCaller, const char* s // Construction/Destruction DrawSketchHandler::DrawSketchHandler() - : sketchgui(nullptr) + : Gui::ToolHandler() + , sketchgui(nullptr) {} DrawSketchHandler::~DrawSketchHandler() @@ -281,11 +282,6 @@ std::string DrawSketchHandler::getToolName() const return "DSH_None"; } -QString DrawSketchHandler::getCrosshairCursorSVGName() const -{ - return QString::fromLatin1("None"); -} - std::unique_ptr DrawSketchHandler::createWidget() const { return nullptr; @@ -311,36 +307,20 @@ void DrawSketchHandler::activate(ViewProviderSketch* vp) { sketchgui = vp; - // save the cursor at the time the DSH is activated - auto* view = dynamic_cast(Gui::getMainWindow()->activeWindow()); - - if (view) { - Gui::View3DInventorViewer* viewer = dynamic_cast(view)->getViewer(); - oldCursor = viewer->getWidget()->cursor(); - - updateCursor(); - - this->signalToolChanged(); - - this->preActivated(); - this->activated(); - } - else { + if (!Gui::ToolHandler::activate()) { sketchgui->purgeHandler(); } } void DrawSketchHandler::deactivate() { - this->deactivated(); - this->postDeactivated(); + Gui::ToolHandler::deactivate(); ViewProviderSketchDrawSketchHandlerAttorney::setConstraintSelectability(*sketchgui, true); // clear temporary Curve and Markers from the scenograph clearEdit(); clearEditMarkers(); resetPositionText(); - unsetCursor(); setAngleSnapping(false); ViewProviderSketchDrawSketchHandlerAttorney::signalToolChanged(*sketchgui, "DSH_None"); @@ -348,25 +328,10 @@ void DrawSketchHandler::deactivate() void DrawSketchHandler::preActivated() { + this->signalToolChanged(); ViewProviderSketchDrawSketchHandlerAttorney::setConstraintSelectability(*sketchgui, false); } -void DrawSketchHandler::quit() -{ - assert(sketchgui); - - Gui::Selection().rmvSelectionGate(); - Gui::Selection().rmvPreselect(); - - sketchgui->purgeHandler(); -} - -void DrawSketchHandler::toolWidgetChanged(QWidget* newwidget) -{ - toolwidget = newwidget; - onWidgetChanged(); -} - void DrawSketchHandler::registerPressedKey(bool pressed, int key) { // the default behaviour is to quit - specific handler categories may @@ -383,6 +348,23 @@ void DrawSketchHandler::pressRightButton(Base::Vector2d /*onSketchPos*/) quit(); } + +void DrawSketchHandler::quit() +{ + assert(sketchgui); + + Gui::Selection().rmvSelectionGate(); + Gui::Selection().rmvPreselect(); + + sketchgui->purgeHandler(); +} + +void DrawSketchHandler::toolWidgetChanged(QWidget* newwidget) +{ + toolwidget = newwidget; + onWidgetChanged(); +} + //************************************************************************** // Helpers @@ -396,198 +378,6 @@ int DrawSketchHandler::getHighestCurveIndex() return sketchgui->getSketchObject()->getHighestCurveIndex(); } -unsigned long DrawSketchHandler::getCrosshairColor() -{ - unsigned long color = 0xFFFFFFFF; // white - ParameterGrp::handle hGrp = - App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/View"); - color = hGrp->GetUnsigned("CursorCrosshairColor", color); - // from rgba to rgb - color = (color >> 8) & 0xFFFFFF; - return color; -} - -void DrawSketchHandler::setCrosshairCursor(const QString& svgName) -{ - const unsigned long defaultCrosshairColor = 0xFFFFFF; - unsigned long color = getCrosshairColor(); - auto colorMapping = std::map(); - colorMapping[defaultCrosshairColor] = color; - // hot spot of all SVG icons should be 8,8 for 32x32 size (16x16 for 64x64) - int hotX = 8; - int hotY = 8; - setSvgCursor(svgName, hotX, hotY, colorMapping); -} - -void DrawSketchHandler::setCrosshairCursor(const char* svgName) -{ - QString cursorName = QString::fromLatin1(svgName); - setCrosshairCursor(cursorName); -} - -void DrawSketchHandler::setSvgCursor(const QString& cursorName, - int x, - int y, - const std::map& colorMapping) -{ - // The Sketcher_Pointer_*.svg icons have a default size of 64x64. When directly creating - // them with a size of 32x32 they look very bad. - // As a workaround the icons are created with 64x64 and afterwards the pixmap is scaled to - // 32x32. This workaround is only needed if pRatio is equal to 1.0 - // - qreal pRatio = devicePixelRatio(); - bool isRatioOne = (pRatio == 1.0); - qreal defaultCursorSize = isRatioOne ? 64 : 32; - qreal hotX = x; - qreal hotY = y; -#if !defined(Q_OS_WIN32) && !defined(Q_OS_MAC) - if (qGuiApp->platformName() == QLatin1String("xcb")) { - hotX *= pRatio; - hotY *= pRatio; - } -#endif - qreal cursorSize = defaultCursorSize * pRatio; - - QPixmap pointer = Gui::BitmapFactory().pixmapFromSvg(cursorName.toStdString().c_str(), - QSizeF(cursorSize, cursorSize), - colorMapping); - if (isRatioOne) { - pointer = pointer.scaled(32, 32); - } - pointer.setDevicePixelRatio(pRatio); - setCursor(pointer, hotX, hotY, false); -} - -void DrawSketchHandler::setCursor(const QPixmap& p, int x, int y, bool autoScale) -{ - Gui::View3DInventorViewer* viewer = getViewer(); - if (viewer) { - QCursor cursor; - QPixmap p1(p); - // TODO remove autoScale after all cursors are SVG-based - if (autoScale) { - qreal pRatio = viewer->devicePixelRatio(); - int newWidth = p.width() * pRatio; - int newHeight = p.height() * pRatio; - p1 = p1.scaled(newWidth, newHeight, Qt::KeepAspectRatio, Qt::SmoothTransformation); - p1.setDevicePixelRatio(pRatio); - qreal hotX = x; - qreal hotY = y; -#if !defined(Q_OS_WIN32) && !defined(Q_OS_MAC) - if (qGuiApp->platformName() == QLatin1String("xcb")) { - hotX *= pRatio; - hotY *= pRatio; - } -#endif - cursor = QCursor(p1, hotX, hotY); - } - else { - // already scaled - cursor = QCursor(p1, x, y); - } - - actCursor = cursor; - actCursorPixmap = p1; - - viewer->getWidget()->setCursor(cursor); - } -} - -void DrawSketchHandler::addCursorTail(std::vector& pixmaps) -{ - // Create a pixmap that will contain icon and each autoconstraint icon - Gui::MDIView* view = Gui::getMainWindow()->activeWindow(); - if (view && view->isDerivedFrom(Gui::View3DInventor::getClassTypeId())) { - QPixmap baseIcon = QPixmap(actCursorPixmap); - baseIcon.setDevicePixelRatio(actCursorPixmap.devicePixelRatio()); - qreal pixelRatio = baseIcon.devicePixelRatio(); - // cursor size in device independent pixels - qreal baseCursorWidth = baseIcon.width(); - qreal baseCursorHeight = baseIcon.height(); - - int tailWidth = 0; - for (auto const& p : pixmaps) { - tailWidth += p.width(); - } - - int newIconWidth = baseCursorWidth + tailWidth; - int newIconHeight = baseCursorHeight; - - QPixmap newIcon(newIconWidth, newIconHeight); - newIcon.fill(Qt::transparent); - - QPainter qp; - qp.begin(&newIcon); - - qp.drawPixmap(QPointF(0, 0), - baseIcon.scaled(baseCursorWidth * pixelRatio, - baseCursorHeight * pixelRatio, - Qt::KeepAspectRatio, - Qt::SmoothTransformation)); - - // Iterate through pixmaps and them to the cursor pixmap - std::vector::iterator pit = pixmaps.begin(); - int i = 0; - qreal currentIconX = baseCursorWidth; - qreal currentIconY; - - for (; pit != pixmaps.end(); ++pit, i++) { - QPixmap icon = *pit; - currentIconY = baseCursorHeight - icon.height(); - qp.drawPixmap(QPointF(currentIconX, currentIconY), icon); - currentIconX += icon.width(); - } - - qp.end(); // Finish painting - - // Create the new cursor with the icon. - QPoint p = actCursor.hotSpot(); - newIcon.setDevicePixelRatio(pixelRatio); - QCursor newCursor(newIcon, p.x(), p.y()); - applyCursor(newCursor); - } -} - -void DrawSketchHandler::updateCursor() -{ - auto cursorstring = getCrosshairCursorSVGName(); - - if (cursorstring != QString::fromLatin1("None")) { - setCrosshairCursor(cursorstring); - } -} - -void DrawSketchHandler::applyCursor() -{ - applyCursor(actCursor); -} - -void DrawSketchHandler::applyCursor(QCursor& newCursor) -{ - Gui::View3DInventorViewer* viewer = getViewer(); - if (viewer) { - viewer->getWidget()->setCursor(newCursor); - } -} - -void DrawSketchHandler::unsetCursor() -{ - Gui::View3DInventorViewer* viewer = getViewer(); - if (viewer) { - viewer->getWidget()->setCursor(oldCursor); - } -} - -qreal DrawSketchHandler::devicePixelRatio() -{ - qreal pixelRatio = 1; - Gui::View3DInventorViewer* viewer = getViewer(); - if (viewer) { - pixelRatio = viewer->devicePixelRatio(); - } - return pixelRatio; -} - std::vector DrawSketchHandler::suggestedConstraintsPixmaps(std::vector& suggestedConstraints) { @@ -1284,12 +1074,3 @@ void DrawSketchHandler::signalToolChanged() const { ViewProviderSketchDrawSketchHandlerAttorney::signalToolChanged(*sketchgui, this->getToolName()); } - -Gui::View3DInventorViewer* DrawSketchHandler::getViewer() -{ - Gui::MDIView* view = Gui::getMainWindow()->activeWindow(); - if (view && view->isDerivedFrom(Gui::View3DInventor::getClassTypeId())) { - return static_cast(view)->getViewer(); - } - return nullptr; -} diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandler.h b/src/Mod/Sketcher/Gui/DrawSketchHandler.h index c12a39e3c2..ab66917f07 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandler.h +++ b/src/Mod/Sketcher/Gui/DrawSketchHandler.h @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -137,26 +138,28 @@ private: * implemented in DrawSketchHandler and used from its derived classes by virtue of the inheritance. * This promotes a concentrating the coupling in a single point (and code reuse). */ -class SketcherGuiExport DrawSketchHandler +class SketcherGuiExport DrawSketchHandler: public Gui::ToolHandler { public: DrawSketchHandler(); virtual ~DrawSketchHandler(); void activate(ViewProviderSketch*); - void deactivate(); + void deactivate() override; + + virtual void mouseMove(Base::Vector2d pos) = 0; + virtual bool pressButton(Base::Vector2d pos) = 0; + virtual bool releaseButton(Base::Vector2d pos) = 0; + + virtual void registerPressedKey(bool pressed, int key); + virtual void pressRightButton(Base::Vector2d pos); - virtual void mouseMove(Base::Vector2d onSketchPos) = 0; - virtual bool pressButton(Base::Vector2d onSketchPos) = 0; - virtual bool releaseButton(Base::Vector2d onSketchPos) = 0; virtual bool onSelectionChanged(const Gui::SelectionChanges&) { return false; } - virtual void registerPressedKey(bool pressed, int key); - virtual void pressRightButton(Base::Vector2d onSketchPos); - virtual void quit(); + void quit() override; friend class ViewProviderSketch; @@ -215,19 +218,12 @@ public: //@} private: // NVI - virtual void preActivated(); - virtual void activated() - {} - virtual void deactivated() - {} - virtual void postDeactivated() - {} + void preActivated() override; virtual void onWidgetChanged() {} protected: // NVI requiring base implementation virtual std::string getToolName() const; - virtual QString getCrosshairCursorSVGName() const; virtual std::unique_ptr createWidget() const; virtual bool isWidgetVisible() const; @@ -235,37 +231,6 @@ protected: // NVI requiring base implementation virtual QString getToolWidgetText() const; protected: - // helpers - /** - * Sets a cursor for 3D inventor view. - * pixmap as a cursor image in device independent pixels. - * - * \param autoScale - set this to false if pixmap already scaled for HiDPI - **/ - - /** @name Icon helpers */ - //@{ - void setCursor(const QPixmap& pixmap, int x, int y, bool autoScale = true); - - /// updates the actCursor with the icon by calling getCrosshairCursorSVGName(), - /// enabling to set data member dependent icons (i.e. for different construction methods) - void updateCursor(); - - /// restitutes the cursor that was in use at the moment of starting the DrawSketchHandler (i.e. - /// oldCursor) - void unsetCursor(); - - /// restitutes the DSH cached cursor (e.g. without any tail due to autoconstraints, ...) - void applyCursor(); - - /// returns the color to be used for the crosshair (configurable as a parameter) - unsigned long getCrosshairColor(); - - /// functions to set the cursor to a given svgName (to be migrated to NVI style) - - qreal devicePixelRatio(); - //@} - void drawEdit(const std::vector& EditCurve) const; void drawEdit(const std::list>& list) const; void drawEdit(const std::vector& geometries) const; @@ -300,22 +265,6 @@ protected: void signalToolChanged() const; - Gui::View3DInventorViewer* getViewer(); - -private: - void setSvgCursor(const QString& svgName, - int x, - int y, - const std::map& colorMapping = - std::map()); - - void addCursorTail(std::vector& pixmaps); - - void applyCursor(QCursor& newCursor); - - void setCrosshairCursor(const QString& svgName); - void setCrosshairCursor(const char* svgName); - protected: /** @@ -325,9 +274,6 @@ protected: suggestedConstraintsPixmaps(std::vector& suggestedConstraints); ViewProviderSketch* sketchgui; - QCursor oldCursor; - QCursor actCursor; - QPixmap actCursorPixmap; QWidget* toolwidget; }; diff --git a/src/Mod/TechDraw/App/DimensionFormatter.cpp b/src/Mod/TechDraw/App/DimensionFormatter.cpp index 1c78d2357e..1bfa57eb61 100644 --- a/src/Mod/TechDraw/App/DimensionFormatter.cpp +++ b/src/Mod/TechDraw/App/DimensionFormatter.cpp @@ -63,16 +63,19 @@ std::string DimensionFormatter::formatValue(const qreal value, { // Base::Console().Message("DF::formatValue() - %s isRestoring: %d\n", // m_dimension->getNameInDocument(), m_dimension->isRestoring()); - bool angularMeasure = false; + bool angularMeasure = m_dimension->Type.isValue("Angle") || m_dimension->Type.isValue("Angle3Pt"); + bool areaMeasure = m_dimension->Type.isValue("Area"); QLocale loc; Base::Quantity asQuantity; asQuantity.setValue(value); - if ( (m_dimension->Type.isValue("Angle")) || - (m_dimension->Type.isValue("Angle3Pt")) ) { - angularMeasure = true; + if (angularMeasure) { asQuantity.setUnit(Base::Unit::Angle); - } else { + } + else if (areaMeasure) { + asQuantity.setUnit(Base::Unit::Area); + } + else { asQuantity.setUnit(Base::Unit::Length); } @@ -128,9 +131,14 @@ std::string DimensionFormatter::formatValue(const qreal value, if (angularMeasure) { userVal = asQuantity.getValue(); qBasicUnit = QString::fromUtf8("°"); - } else { + } + else { double convertValue = Base::Quantity::parse(QString::fromLatin1("1") + qBasicUnit).getValue(); userVal = asQuantity.getValue() / convertValue; + if (areaMeasure) { + userVal = userVal / convertValue; // divide again as area is length² + qBasicUnit = qBasicUnit + QString::fromUtf8("²"); + } } if (isTooSmall(userVal, formatSpecifier)) { @@ -153,37 +161,44 @@ std::string DimensionFormatter::formatValue(const qreal value, return Base::Tools::toStdString(formatPrefix) + Base::Tools::toStdString(qUserString) + Base::Tools::toStdString(formatSuffix); - } else if (partial == 1) { // prefix number[unit] suffix + } + else if (partial == 1) { // prefix number[unit] suffix if (angularMeasure) { //always insert unit after value return Base::Tools::toStdString(formatPrefix) + formattedValueString + "°" + Base::Tools::toStdString(formatSuffix); - } else if (m_dimension->showUnits()){ + } + else if (m_dimension->showUnits() || areaMeasure){ if (isDim && m_dimension->haveTolerance()) { //unit will be included in tolerance so don't repeat it here return Base::Tools::toStdString(formatPrefix) + formattedValueString + Base::Tools::toStdString(formatSuffix); - } else { + } + else { //no tolerance, so we need to include unit return Base::Tools::toStdString(formatPrefix) + formattedValueString + " " + Base::Tools::toStdString(qBasicUnit) + Base::Tools::toStdString(formatSuffix); } - } else { + } + else { //showUnits is false return Base::Tools::toStdString(formatPrefix) + formattedValueString + Base::Tools::toStdString(formatSuffix); } - } else if (partial == 2) { // just the unit + } + else if (partial == 2) { // just the unit if (angularMeasure) { return Base::Tools::toStdString(qBasicUnit); - } else if (m_dimension->showUnits()) { + } + else if (m_dimension->showUnits() || areaMeasure) { return Base::Tools::toStdString(qBasicUnit); - } else { + } + else { return ""; } } diff --git a/src/Mod/TechDraw/App/DimensionGeometry.cpp b/src/Mod/TechDraw/App/DimensionGeometry.cpp index 60186d6b96..fad87f1308 100644 --- a/src/Mod/TechDraw/App/DimensionGeometry.cpp +++ b/src/Mod/TechDraw/App/DimensionGeometry.cpp @@ -361,3 +361,35 @@ void arcPoints::dump(const std::string& text) const DrawUtil::formatVector(arcEnds.second()).c_str()); Base::Console().Message("arcPoints - midArc: %s\n", DrawUtil::formatVector(midArc).c_str()); } + + +areaPoint::areaPoint() : + area(0.0), + center(Base::Vector3d()) +{ +} + +areaPoint& areaPoint::operator=(const areaPoint& ap) +{ + area = ap.area; + center = ap.center; + return *this; +} + +void areaPoint::move(const Base::Vector3d& offset) +{ + center = center - offset; +} + +void areaPoint::project(const DrawViewPart* dvp) +{ + area = area * dvp->getScale(); + center = dvp->projectPoint(center) * dvp->getScale(); +} + +void areaPoint::dump(const std::string& text) const +{ + Base::Console().Message("areaPoint - %s\n", text.c_str()); + Base::Console().Message("areaPoint - area: %.3f center: %s\n", area, + DrawUtil::formatVector(center).c_str()); +} diff --git a/src/Mod/TechDraw/App/DimensionGeometry.h b/src/Mod/TechDraw/App/DimensionGeometry.h index 3a651dbe48..0a74b2e2eb 100644 --- a/src/Mod/TechDraw/App/DimensionGeometry.h +++ b/src/Mod/TechDraw/App/DimensionGeometry.h @@ -156,6 +156,24 @@ public: bool arcCW; }; +//a convenient container for area dimension +class TechDrawExport areaPoint +{ +public: + areaPoint(); + areaPoint(const areaPoint& ap) = default; + + areaPoint& operator= (const areaPoint& ap); + + void move(const Base::Vector3d& offset); + void project(const DrawViewPart* dvp); + void dump(const std::string& text) const; + +//TODO: setters and getters + double area; + Base::Vector3d center; +}; + } //end namespace TechDraw #endif diff --git a/src/Mod/TechDraw/App/DimensionReferences.cpp b/src/Mod/TechDraw/App/DimensionReferences.cpp index 3cfed7f2f8..656af6ee88 100644 --- a/src/Mod/TechDraw/App/DimensionReferences.cpp +++ b/src/Mod/TechDraw/App/DimensionReferences.cpp @@ -86,6 +86,12 @@ ReferenceEntry& ReferenceEntry::operator=(const ReferenceEntry& otherRef) } +bool ReferenceEntry::operator==(const ReferenceEntry& otherRef) const +{ + return getObjectName() == otherRef.getObjectName() && getSubName() == otherRef.getSubName(); +} + + TopoDS_Shape ReferenceEntry::getGeometry() const { // Base::Console().Message("RE::getGeometry() - objectName: %s sub: **%s**\n", @@ -206,6 +212,10 @@ Part::TopoShape ReferenceEntry::asTopoShape() const TopoDS_Edge edge = TopoDS::Edge(geom); return asTopoShapeEdge(edge); } + if (geom.ShapeType() == TopAbs_FACE) { + TopoDS_Face face = TopoDS::Face(geom); + return asTopoShapeFace(face); + } throw Base::RuntimeError("Dimension Reference has unsupported geometry"); } @@ -253,12 +263,30 @@ Part::TopoShape ReferenceEntry::asTopoShapeEdge(const TopoDS_Edge &edge) return { edge }; } +Part::TopoShape ReferenceEntry::asTopoShapeFace(const TopoDS_Face &face) +{ + return { face }; +} + std::string ReferenceEntry::geomType() const { // Base::Console().Message("RE::geomType() - subName: **%s**\n", getSubName().c_str()); return DrawUtil::getGeomTypeFromName(getSubName()); } +GeomType ReferenceEntry::geomEdgeType() const +{ + int geoId = TechDraw::DrawUtil::getIndexFromName(getSubName()); + auto dvp = static_cast(getObject()); + BaseGeomPtr geom = dvp->getGeomByIndex(geoId); + + if (geomType() == "Edge" && geom) { + return geom->getGeomType(); + } + + return GeomType::NOTDEF; +} + bool ReferenceEntry::isWholeObject() const { return getSubName().empty(); @@ -314,12 +342,19 @@ bool ReferenceEntry::hasGeometry2d() const if (vert) { return true; } - } else if (gType == "Edge") { + } + else if (gType == "Edge") { auto edge = dvp->getGeomByIndex(geomNumber); if (edge) { return true; } } + else if (gType == "Face") { + auto face = dvp->getFace(getSubName()); + if (face) { + return true; + } + } return false; } diff --git a/src/Mod/TechDraw/App/DimensionReferences.h b/src/Mod/TechDraw/App/DimensionReferences.h index 19b1a11681..faa5fd769c 100644 --- a/src/Mod/TechDraw/App/DimensionReferences.h +++ b/src/Mod/TechDraw/App/DimensionReferences.h @@ -33,6 +33,7 @@ #include #include +#include namespace App { @@ -59,6 +60,7 @@ public: ~ReferenceEntry() = default; ReferenceEntry& operator= (const ReferenceEntry& otherRef); + bool operator== (const ReferenceEntry& otherRef) const; App::DocumentObject* getObject() const; void setObject(App::DocumentObject* docObj) { m_object = docObj; } @@ -71,6 +73,8 @@ public: TopoDS_Shape getGeometry() const; std::string geomType() const; + GeomType geomEdgeType() const; + bool isWholeObject() const; Part::TopoShape asTopoShape() const; @@ -86,6 +90,7 @@ private: static Part::TopoShape asTopoShapeVertex(const TopoDS_Vertex &vert); static Part::TopoShape asTopoShapeEdge(const TopoDS_Edge& edge); + static Part::TopoShape asTopoShapeFace(const TopoDS_Face& edge); App::DocumentObject* m_object{nullptr}; std::string m_subName; diff --git a/src/Mod/TechDraw/App/DrawDimHelper.cpp b/src/Mod/TechDraw/App/DrawDimHelper.cpp index f6b9ec0da1..e07968d1a7 100644 --- a/src/Mod/TechDraw/App/DrawDimHelper.cpp +++ b/src/Mod/TechDraw/App/DrawDimHelper.cpp @@ -59,13 +59,31 @@ using namespace TechDraw; -void DrawDimHelper::makeExtentDim(DrawViewPart* dvp, std::vector edgeNames, +DrawViewDimension* DrawDimHelper::makeExtentDim(DrawViewPart* dvp, + const std::string& dimType, ReferenceVector references2d) +{ + std::vector edgeNames; + for (auto& ref : references2d) { + if (ref.getSubName().empty()) { + continue; + } + std::string geomType = DrawUtil::getGeomTypeFromName(ref.getSubName()); + if (geomType == "Edge") { + edgeNames.push_back(ref.getSubName()); + } + } + int direction = dimType == "DistanceX" ? 0 : dimType == "DistanceY" ? 1 : 2; + + return makeExtentDim(dvp, edgeNames, direction); +} + +DrawViewDimension* DrawDimHelper::makeExtentDim(DrawViewPart* dvp, std::vector edgeNames, int direction) { // Base::Console().Message("DDH::makeExtentDim() - dvp: %s edgeNames: %d\n", // dvp->Label.getValue(), edgeNames.size()); if (!dvp) { - return; + return nullptr; } std::string dimType = "DistanceX"; @@ -74,8 +92,12 @@ void DrawDimHelper::makeExtentDim(DrawViewPart* dvp, std::vector ed dimType = "DistanceY"; dimNum = 1; } + else if (direction == LENGTH) { + dimType = "Distance"; + dimNum = 2; + } - TechDraw::DrawPage* page = dvp->findParentPage(); + DrawPage* page = dvp->findParentPage(); std::string pageName = page->getNameInDocument(); App::Document* doc = dvp->getDocument(); @@ -89,8 +111,7 @@ void DrawDimHelper::makeExtentDim(DrawViewPart* dvp, std::vector ed Base::Interpreter().runStringArg( "App.activeDocument().%s.DirExtent = %d", dimName.c_str(), dimNum); - TechDraw::DrawViewDimExtent* dimExt = - dynamic_cast(doc->getObject(dimName.c_str())); + auto* dimExt = dynamic_cast(doc->getObject(dimName.c_str())); if (!dimExt) { throw Base::TypeError("Dim extent not found"); } @@ -113,6 +134,14 @@ void DrawDimHelper::makeExtentDim(DrawViewPart* dvp, std::vector ed dimName.c_str()); dimExt->recomputeFeature(); + + return dimExt; +} + +void DrawDimHelper::makeExtentDim3d(DrawViewPart* dvp, const std::string& dimType, ReferenceVector references3d) +{ + int direction = dimType == "DistanceX" ? 0 : dimType == "DistanceY" ? 1 : 2; + return makeExtentDim3d(dvp, references3d, direction); } void DrawDimHelper::makeExtentDim3d(DrawViewPart* dvp, ReferenceVector references, int direction) @@ -130,7 +159,7 @@ void DrawDimHelper::makeExtentDim3d(DrawViewPart* dvp, ReferenceVector reference dimNum = 1; } - TechDraw::DrawPage* page = dvp->findParentPage(); + DrawPage* page = dvp->findParentPage(); std::string pageName = page->getNameInDocument(); App::Document* doc = dvp->getDocument(); @@ -144,8 +173,7 @@ void DrawDimHelper::makeExtentDim3d(DrawViewPart* dvp, ReferenceVector reference Base::Interpreter().runStringArg( "App.activeDocument().%s.DirExtent = %d", dimName.c_str(), dimNum); - TechDraw::DrawViewDimExtent* dimExt = - dynamic_cast(doc->getObject(dimName.c_str())); + auto* dimExt = dynamic_cast(doc->getObject(dimName.c_str())); if (!dimExt) { throw Base::TypeError("Dim extent not found"); } diff --git a/src/Mod/TechDraw/App/DrawDimHelper.h b/src/Mod/TechDraw/App/DrawDimHelper.h index c8d315750f..6490ecfbc4 100644 --- a/src/Mod/TechDraw/App/DrawDimHelper.h +++ b/src/Mod/TechDraw/App/DrawDimHelper.h @@ -44,9 +44,18 @@ class DrawViewDimension; /// Additional functions for working with Dimensions class TechDrawExport DrawDimHelper { public: - static void makeExtentDim(DrawViewPart* dvp, + + static DrawViewDimension* makeExtentDim(DrawViewPart* dvp, + const std::string& dimType, + ReferenceVector references2d); + + static DrawViewDimension* makeExtentDim(DrawViewPart* dvp, std::vector edgeNames, int direction); + + static void makeExtentDim3d(DrawViewPart* dvp, + const std::string& dimType, + ReferenceVector references2d); static void makeExtentDim3d(DrawViewPart* dvp, ReferenceVector references, int direction); @@ -55,7 +64,7 @@ class TechDrawExport DrawDimHelper { TopoDS_Edge& boundary); - static TechDraw::DrawViewDimension* makeDistDim(DrawViewPart* dvp, + static DrawViewDimension* makeDistDim(DrawViewPart* dvp, std::string dimType, Base::Vector3d refMin, Base::Vector3d refMax, diff --git a/src/Mod/TechDraw/App/DrawViewDimension.cpp b/src/Mod/TechDraw/App/DrawViewDimension.cpp index 45c158a043..4f37c84be3 100644 --- a/src/Mod/TechDraw/App/DrawViewDimension.cpp +++ b/src/Mod/TechDraw/App/DrawViewDimension.cpp @@ -31,6 +31,8 @@ #include #include +#include +#include #include #include #include @@ -93,6 +95,7 @@ const char* DrawViewDimension::TypeEnums[] = {"Distance", "Diameter", "Angle", "Angle3Pt", + "Area", nullptr}; const char* DrawViewDimension::MeasureTypeEnums[] = {"True", "Projected", nullptr}; @@ -267,6 +270,12 @@ void DrawViewDimension::resetArc() m_arcPoints.arcCW = false; } +void DrawViewDimension::resetArea() +{ + m_areaPoint.center = Base::Vector3d(0, 0, 0); + m_areaPoint.area = 0.0; +} + void DrawViewDimension::onChanged(const App::Property* prop) { if (prop == &References3D) { @@ -461,6 +470,7 @@ App::DocumentObjectExecReturn* DrawViewDimension::execute() resetLinear(); resetAngular(); resetArc(); + resetArea(); // we have either or both valid References3D and References2D ReferenceVector references = getEffectiveReferences(); @@ -499,6 +509,13 @@ App::DocumentObjectExecReturn* DrawViewDimension::execute() m_anglePoints = getAnglePointsThreeVerts(references); m_hasGeometry = true; } + else if (Type.isValue("Area")) { + if (getRefType() != oneFace) { + throw Base::RuntimeError("area dimension has non-face references"); + } + m_areaPoint = getAreaParameters(references); + m_hasGeometry = true; + } overrideKeepUpdated(false); return DrawView::execute(); @@ -679,6 +696,9 @@ double DrawViewDimension::getTrueDimValue() const else if (Type.isValue("Angle") || Type.isValue("Angle3Pt")) { result = measurement->angle(); } + else if (Type.isValue("Area")) { + result = measurement->area(); + } else { // tarfu throw Base::ValueError("getDimValue() - Unknown Dimension Type (3)"); } @@ -721,9 +741,8 @@ double DrawViewDimension::getProjectedDimValue() const } } else if (Type.isValue("Radius")) { - arcPoints pts = m_arcPoints; - result = - pts.radius / getViewPart()->getScale(); // Projected BaseGeom is scaled for drawing + // Projected BaseGeom is scaled for drawing + result = m_arcPoints.radius / getViewPart()->getScale(); } else if (Type.isValue("Diameter")) { arcPoints pts = m_arcPoints; @@ -738,6 +757,9 @@ double DrawViewDimension::getProjectedDimValue() const double legAngle = Base::toDegrees(leg0.GetAngle(leg1)); result = legAngle; } + else if (Type.isValue("Area")) { + result = m_areaPoint.area / getViewPart()->getScale(); + } return result; } @@ -927,7 +949,7 @@ arcPoints DrawViewDimension::getArcParameters(ReferenceVector references) ssMessage << getNameInDocument() << " can not find geometry for 2d reference (4)"; throw Base::RuntimeError(ssMessage.str()); } - return arcPointsFromBaseGeom(getViewPart()->getGeomByIndex(iSubelement)); + return arcPointsFromBaseGeom(geom); } // this is a 3d reference @@ -1324,6 +1346,40 @@ anglePoints DrawViewDimension::getAnglePointsThreeVerts(ReferenceVector referenc return pts; } +areaPoint DrawViewDimension::getAreaParameters(ReferenceVector references) +{ + areaPoint pts; + + App::DocumentObject* refObject = references.front().getObject(); + if (refObject->isDerivedFrom() && !references[0].getSubName().empty()) { + // this is a 2d object (a DVP + subelements) + TechDraw::FacePtr face = getViewPart()->getFace(references[0].getSubName()); + if (!face) { + std::stringstream ssMessage; + ssMessage << getNameInDocument() << " can not find geometry for 2d reference (4)"; + throw Base::RuntimeError(ssMessage.str()); + } + + pts.area = face->getArea(); + pts.center = face->getCenter(); + } + else { + // this is a 3d reference + TopoDS_Shape geometry = references[0].getGeometry(); + if (geometry.IsNull() || geometry.ShapeType() != TopAbs_FACE) { + throw Base::RuntimeError("Geometry for dimension reference is null."); + } + const TopoDS_Face& face = TopoDS::Face(geometry); + + GProp_GProps props; + BRepGProp::SurfaceProperties(face, props); + pts.area = props.Mass(); + pts.center = DrawUtil::toVector3d(props.CentreOfMass()); + } + + return pts; +} + DrawViewPart* DrawViewDimension::getViewPart() const { if (References2D.getValues().empty()) { @@ -1411,6 +1467,7 @@ int DrawViewDimension::getRefTypeSubElements(const std::vector& sub int refType = invalidRef; int refEdges{0}; int refVertices{0}; + int refFaces{0}; for (const auto& se : subElements) { if (DrawUtil::getGeomTypeFromName(se) == "Vertex") { @@ -1419,23 +1476,29 @@ int DrawViewDimension::getRefTypeSubElements(const std::vector& sub if (DrawUtil::getGeomTypeFromName(se) == "Edge") { refEdges++; } + if (DrawUtil::getGeomTypeFromName(se) == "Face") { + refFaces++; + } } - if (refEdges == 0 && refVertices == 2) { + if (refEdges == 0 && refVertices == 2 && refFaces == 0) { refType = twoVertex; } - if (refEdges == 0 && refVertices == 3) { + if (refEdges == 0 && refVertices == 3 && refFaces == 0) { refType = threeVertex; } - if (refEdges == 1 && refVertices == 0) { + if (refEdges == 1 && refVertices == 0 && refFaces == 0) { refType = oneEdge; } - if (refEdges == 1 && refVertices == 1) { + if (refEdges == 1 && refVertices == 1 && refFaces == 0) { refType = vertexEdge; } - if (refEdges == 2 && refVertices == 0) { + if (refEdges == 2 && refVertices == 0 && refFaces == 0) { refType = twoEdge; } + if (refEdges == 0 && refVertices == 0 && refFaces == 1) { + refType = oneFace; + } return refType; } @@ -1749,6 +1812,17 @@ bool DrawViewDimension::validateReferenceForm() const return (subGeom0 == "Vertex" && subGeom1 == "Vertex" && subGeom2 == "Vertex"); } + if (Type.isValue("Area")) { + if (references.size() != 1) { + return false; + } + std::string subGeom = DrawUtil::getGeomTypeFromName(references.front().getSubName()); + if (subGeom != "Face") { + return false; + } + return true; + } + return false; } diff --git a/src/Mod/TechDraw/App/DrawViewDimension.h b/src/Mod/TechDraw/App/DrawViewDimension.h index c7d687ab2b..56836c7ae9 100644 --- a/src/Mod/TechDraw/App/DrawViewDimension.h +++ b/src/Mod/TechDraw/App/DrawViewDimension.h @@ -101,6 +101,7 @@ public: twoVertex, vertexEdge, threeVertex, + oneFace, extent }; @@ -178,6 +179,10 @@ public: { return m_anglePoints; } + areaPoint getAreaPoint() + { + return m_areaPoint; + } bool leaderIntersectsArc(Base::Vector3d s, Base::Vector3d pointOnCircle); @@ -228,6 +233,8 @@ protected: virtual anglePoints getAnglePointsTwoEdges(ReferenceVector references); virtual anglePoints getAnglePointsThreeVerts(ReferenceVector references); + virtual areaPoint getAreaParameters(ReferenceVector references); + Measure::Measurement* measurement; double dist2Segs(Base::Vector3d s1, Base::Vector3d e1, Base::Vector3d s2, Base::Vector3d e2) const; @@ -236,6 +243,7 @@ protected: void resetLinear(); void resetAngular(); void resetArc(); + void resetArea(); bool okToProceed(); void updateSavedGeometry(); @@ -252,6 +260,7 @@ private: pointPair m_arrowPositions; arcPoints m_arcPoints; anglePoints m_anglePoints; + areaPoint m_areaPoint; bool m_hasGeometry; friend class DimensionFormatter; diff --git a/src/Mod/TechDraw/App/Geometry.cpp b/src/Mod/TechDraw/App/Geometry.cpp index e73a2faf1a..5c768e7541 100644 --- a/src/Mod/TechDraw/App/Geometry.cpp +++ b/src/Mod/TechDraw/App/Geometry.cpp @@ -168,6 +168,13 @@ Base::Vector3d Face::getCenter() const { return DrawUtil::toVector3d(faceProps.CentreOfMass()); } +double Face::getArea() const { + GProp_GProps faceProps; + BRepGProp::SurfaceProperties(toOccFace(), faceProps); + + return faceProps.Mass(); +} + Face::~Face() { for(auto it : wires) { diff --git a/src/Mod/TechDraw/App/Geometry.h b/src/Mod/TechDraw/App/Geometry.h index 759e9d18ed..86be92638d 100644 --- a/src/Mod/TechDraw/App/Geometry.h +++ b/src/Mod/TechDraw/App/Geometry.h @@ -348,6 +348,7 @@ class TechDrawExport Face TopoDS_Face toOccFace() const; std::vector wires; + double getArea() const; Base::Vector3d getCenter() const; }; using FacePtr = std::shared_ptr; diff --git a/src/Mod/TechDraw/App/GeometryMatcher.cpp b/src/Mod/TechDraw/App/GeometryMatcher.cpp index 004e195860..e7ba33dffa 100644 --- a/src/Mod/TechDraw/App/GeometryMatcher.cpp +++ b/src/Mod/TechDraw/App/GeometryMatcher.cpp @@ -26,6 +26,8 @@ #include "PreCompiled.h" #ifndef _PreComp_ +#include +#include #endif #include @@ -75,9 +77,13 @@ bool GeometryMatcher::compareGeometry(const Part::TopoShape &shape1, const Part: if (geom1.ShapeType() == TopAbs_EDGE) { return compareEdges(geom1, geom2); } + if (geom1.ShapeType() == TopAbs_FACE) { + return compareFaces(geom1, geom2); + } return false; } + bool GeometryMatcher::comparePoints(const TopoDS_Shape& shape1, const TopoDS_Shape& shape2) { // Base::Console().Message("GM::comparePoints()\n"); @@ -92,6 +98,27 @@ bool GeometryMatcher::comparePoints(const TopoDS_Shape& shape1, const TopoDS_Sha return point1.IsEqual(point2, EWTOLERANCE); } +bool GeometryMatcher::compareFaces(const TopoDS_Shape& shape1, const TopoDS_Shape& shape2) +{ + // Base::Console().Message("GM::compareFaces()\n"); + + if (shape1.ShapeType() != TopAbs_FACE || shape2.ShapeType() != TopAbs_FACE) { + // can not compare these shapes + return false; + } + TopoDS_Face face1 = TopoDS::Face(shape1); + TopoDS_Face face2 = TopoDS::Face(shape2); + + //Note: face1.IsSame(face2) and face1.IsEqual(face2) do not work. + + GProp_GProps props1, props2; + BRepGProp::SurfaceProperties(face1, props1); + BRepGProp::SurfaceProperties(face2, props2); + + // Check if areas are approximately equal + return fabs(props1.Mass() - props2.Mass()) < 1e-5; +} + bool GeometryMatcher::compareEdges(const TopoDS_Shape& shape1, const TopoDS_Shape& shape2) { // Base::Console().Message("GM::compareEdges()\n"); diff --git a/src/Mod/TechDraw/App/GeometryMatcher.h b/src/Mod/TechDraw/App/GeometryMatcher.h index 00dd3028dc..9b2d16f929 100644 --- a/src/Mod/TechDraw/App/GeometryMatcher.h +++ b/src/Mod/TechDraw/App/GeometryMatcher.h @@ -58,6 +58,7 @@ public: private: static bool comparePoints(const TopoDS_Shape& shape1, const TopoDS_Shape& shape2); static bool compareEdges(const TopoDS_Shape& shape1, const TopoDS_Shape& shape2); + static bool compareFaces(const TopoDS_Shape& shape1, const TopoDS_Shape& shape2); static bool compareLines(const TopoDS_Edge& edge1, const TopoDS_Edge& edge2); static bool compareCircles(const TopoDS_Edge& edge1, const TopoDS_Edge& edge2); diff --git a/src/Mod/TechDraw/Gui/AppTechDrawGui.cpp b/src/Mod/TechDraw/Gui/AppTechDrawGui.cpp index 0b4feec49e..89263a47fa 100644 --- a/src/Mod/TechDraw/Gui/AppTechDrawGui.cpp +++ b/src/Mod/TechDraw/Gui/AppTechDrawGui.cpp @@ -125,11 +125,11 @@ PyMOD_INIT_FUNC(TechDrawGui) // instantiating the commands CreateTechDrawCommands(); - CreateTechDrawCommandsDims(); CreateTechDrawCommandsDecorate(); CreateTechDrawCommandsAnnotate(); CreateTechDrawCommandsExtensionDims(); CreateTechDrawCommandsExtensions(); + CreateTechDrawCommandsDims(); CreateTechDrawCommandsStack(); TechDrawGui::Workbench::init(); diff --git a/src/Mod/TechDraw/Gui/CMakeLists.txt b/src/Mod/TechDraw/Gui/CMakeLists.txt index fbd3c490c6..2951db9ad5 100644 --- a/src/Mod/TechDraw/Gui/CMakeLists.txt +++ b/src/Mod/TechDraw/Gui/CMakeLists.txt @@ -102,6 +102,7 @@ SET(TechDrawGui_SRCS CommandDecorate.cpp CommandAnnotate.cpp CommandExtensionDims.cpp + CommandExtensionDims.h CommandExtensionPack.cpp CommandStack.cpp DimensionValidators.cpp @@ -223,6 +224,8 @@ SET(TechDrawGui_SRCS TaskCosmeticCircle.cpp TaskCosmeticCircle.h TaskCosmeticCircle.ui + TechDrawHandler.cpp + TechDrawHandler.h Widgets/CompassDialWidget.cpp Widgets/CompassDialWidget.h Widgets/CompassWidget.cpp diff --git a/src/Mod/TechDraw/Gui/CommandCreateDims.cpp b/src/Mod/TechDraw/Gui/CommandCreateDims.cpp index 5b32915763..db5b2f332d 100644 --- a/src/Mod/TechDraw/Gui/CommandCreateDims.cpp +++ b/src/Mod/TechDraw/Gui/CommandCreateDims.cpp @@ -28,11 +28,16 @@ #include #include +#include +#include +#include +#include #endif//#ifndef _PreComp_ #include #include #include +#include #include #include #include @@ -42,25 +47,37 @@ #include #include #include +#include +#include #include #include #include #include +#include #include #include #include #include +#include "CommandExtensionDims.h" #include "DimensionValidators.h" #include "DrawGuiUtil.h" +#include "QGIViewDimension.h" +#include "QGVPage.h" +#include "MDIViewPage.h" #include "TaskDimRepair.h" #include "TaskLinkDim.h" +#include "TaskSelectLineAttributes.h" +#include "TechDrawHandler.h" +#include "ViewProviderDimension.h" +#include "ViewProviderDrawingView.h" +#include "ViewProviderDrawingView.h" using namespace TechDrawGui; using namespace TechDraw; using namespace std; - +using DimensionType = TechDraw::DrawViewDimension::DimensionType; //=========================================================================== // utility routines @@ -70,6 +87,7 @@ using namespace std; bool _checkSelection(Gui::Command* cmd, unsigned maxObjs = 2); bool _checkDrawViewPart(Gui::Command* cmd); + void execDistance(Gui::Command* cmd); void execDistanceX(Gui::Command* cmd); void execDistanceY(Gui::Command* cmd); @@ -77,21 +95,1305 @@ void execAngle(Gui::Command* cmd); void execAngle3Pt(Gui::Command* cmd); void execRadius(Gui::Command* cmd); void execDiameter(Gui::Command* cmd); +void execArea(Gui::Command* cmd); -void execExtent(Gui::Command* cmd, int direction); +void execExtent(Gui::Command* cmd, const std::string& dimType); DrawViewDimension* dimensionMaker(TechDraw::DrawViewPart* dvp, std::string dimType, ReferenceVector references2d, ReferenceVector references3d); +DrawViewDimension* dimMaker(TechDraw::DrawViewPart* dvp, std::string dimType, + ReferenceVector references2d, ReferenceVector references3d); -void positionDimText(DrawViewDimension* dim); +void positionDimText(DrawViewDimension* dim, int indexOffset = 0); + +void activateHandler(TechDrawHandler* newHandler) +{ + auto* mdi = dynamic_cast(Gui::getMainWindow()->activeWindow()); + if (!mdi) { + return; + } + + ViewProviderPage* vp = mdi->getViewProviderPage(); + if (!vp) { + return; + } + + QGVPage* viewPage = vp->getQGVPage(); + if (!viewPage) { + return; + } + viewPage->activateHandler(newHandler); +} -//NOTE: this is not shown in toolbar and doesn't always work right in the menu. -// should be removed. //=========================================================================== -// TechDraw_NewDimension +// TechDraw_Dimension //=========================================================================== -// this is deprecated. use individual add dimension commands. +class GeomSelectionSizes +{ +public: + GeomSelectionSizes(size_t s_pts, size_t s_lns, size_t s_cir, size_t s_ell, size_t s_spl, size_t s_fcs) : + s_pts(s_pts), s_lns(s_lns), s_cir(s_cir), s_ell(s_ell), s_spl(s_spl), s_fcs(s_fcs) {} + ~GeomSelectionSizes() {} + + bool hasPoints() const { return s_pts > 0; } + bool hasLines() const { return s_lns > 0; } + bool hasCirclesOrArcs() const { return s_cir > 0; } + bool hasEllipseAndCo() const { return s_ell > 0; } + bool hasSplineAndCo() const { return s_spl > 0; } + bool hasFaces() const { return s_fcs > 0; } + + bool has1Face() const { return s_pts == 0 && s_lns == 0 && s_cir == 0 && s_ell == 0 && s_spl == 0 && s_fcs == 1; } + + bool has1Point() const { return s_pts == 1 && s_lns == 0 && s_cir == 0 && s_ell == 0 && s_spl == 0 && s_fcs == 0; } + bool has2Points() const { return s_pts == 2 && s_lns == 0 && s_cir == 0 && s_ell == 0 && s_spl == 0 && s_fcs == 0; } + bool has1Point1Line() const { return s_pts == 1 && s_lns == 1 && s_cir == 0 && s_ell == 0 && s_spl == 0 && s_fcs == 0; } + bool has3Points() const { return s_pts == 3 && s_lns == 0 && s_cir == 0 && s_ell == 0 && s_spl == 0 && s_fcs == 0; } + bool has4MorePoints() const { return s_pts >= 4 && s_lns == 0 && s_cir == 0 && s_ell == 0 && s_spl == 0 && s_fcs == 0; } + bool has2Points1Line() const { return s_pts == 2 && s_lns == 1 && s_cir == 0 && s_ell == 0 && s_spl == 0 && s_fcs == 0; } + bool has3MorePoints1Line() const { return s_pts >= 3 && s_lns == 1 && s_cir == 0 && s_ell == 0 && s_spl == 0 && s_fcs == 0; } + bool has1Point1Circle() const { return s_pts == 1 && s_lns == 0 && s_cir == 1 && s_ell == 0 && s_spl == 0 && s_fcs == 0; } + bool has1Point1Ellipse() const { return s_pts == 1 && s_lns == 0 && s_cir == 0 && s_ell == 1 && s_spl == 0 && s_fcs == 0; } + + bool has1Line() const { return s_pts == 0 && s_lns == 1 && s_cir == 0 && s_ell == 0 && s_spl == 0 && s_fcs == 0; } + bool has2Lines() const { return s_pts == 0 && s_lns == 2 && s_cir == 0 && s_ell == 0 && s_spl == 0 && s_fcs == 0; } + bool has3MoreLines() const { return s_pts == 0 && s_lns >= 3 && s_cir == 0 && s_ell == 0 && s_spl == 0 && s_fcs == 0; } + bool has1Line1Circle() const { return s_pts == 0 && s_lns == 1 && s_cir == 1 && s_ell == 0 && s_spl == 0 && s_fcs == 0; } + bool has1Line2Circles() const { return s_pts == 0 && s_lns == 1 && s_cir == 2 && s_ell == 0 && s_spl == 0 && s_fcs == 0; } + bool has1Line1Ellipse() const { return s_pts == 0 && s_lns == 1 && s_cir == 0 && s_ell == 1 && s_spl == 0 && s_fcs == 0; } + + bool has1Circle() const { return s_pts == 0 && s_lns == 0 && s_cir == 1 && s_ell == 0 && s_spl == 0 && s_fcs == 0; } + bool has2Circles() const { return s_pts == 0 && s_lns == 0 && s_cir == 2 && s_ell == 0 && s_spl == 0 && s_fcs == 0; } + bool has3MoreCircles() const { return s_pts == 0 && s_lns == 0 && s_cir >= 3 && s_ell == 0 && s_spl == 0 && s_fcs == 0; } + bool has1Circle1Ellipse() const { return s_pts == 0 && s_lns == 0 && s_cir == 1 && s_ell == 1 && s_spl == 0 && s_fcs == 0; } + + bool has1Ellipse() const { return s_pts == 0 && s_lns == 0 && s_cir == 0 && s_ell == 1 && s_spl == 0 && s_fcs == 0; } + bool has2Ellipses() const { return s_pts == 0 && s_lns == 0 && s_cir == 0 && s_ell == 2 && s_spl == 0 && s_fcs == 0; } + + bool has1Spline() const { return s_pts == 0 && s_lns == 0 && s_cir == 0 && s_ell == 0 && s_spl == 1 && s_fcs == 0; } + bool has1SplineAndMore() const { return s_spl >= 1 && s_fcs == 0; } + + size_t s_pts, s_lns, s_cir, s_ell, s_spl, s_fcs; +}; + +class TDHandlerDimension : public TechDrawHandler, + public Gui::SelectionObserver +{ +public: + explicit TDHandlerDimension(ReferenceVector refs, TechDraw::DrawViewPart* pFeat) + : SelectionObserver(true) + , specialDimension(SpecialDimension::None) + , availableDimension(AvailableDimension::FIRST) + , mousePos(QPoint(0, 0)) + , selPoints({}) + , selLine({}) + , selCircleArc({}) + , selEllipseArc({}) + , selSplineAndCo({}) + , selFaces({}) + , emptyVector({}) + , addedRef(ReferenceEntry()) + , removedRef(ReferenceEntry()) + , initialSelection(std::move(refs)) + , partFeat(pFeat) + , dims({}) + , blockRemoveSel(false) + { + } + ~TDHandlerDimension() + { + } + + enum class AvailableDimension { + FIRST, + SECOND, + THIRD, + FOURTH, + FIFTH, + RESET + }; + + enum class SpecialDimension { + LineOr2PointsDistance, + LineOr2PointsChamfer, + ExtendDistance, + ChainDistance, + CoordDistance, + None + }; + + + void activated() override + { + auto* mdi = dynamic_cast(Gui::getMainWindow()->activeWindow()); + if (mdi) { + mdi->setDimensionsSelectability(false); + } + Gui::Selection().setSelectionStyle(Gui::SelectionSingleton::SelectionStyle::GreedySelection); + Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Insert Dimension")); + handleInitialSelection(); + } + + void deactivated() override + { + auto* mdi = dynamic_cast(Gui::getMainWindow()->activeWindow()); + if (mdi) { + mdi->setDimensionsSelectability(true); + } + Gui::Selection().setSelectionStyle(Gui::SelectionSingleton::SelectionStyle::NormalSelection); + Gui::Command::abortCommand(); + } + + void keyPressEvent(QKeyEvent* event) override + { + if (event->key() == Qt::Key_M && !selectionEmpty()) { + if (availableDimension == AvailableDimension::FIRST) { + availableDimension = AvailableDimension::SECOND; + } + else if (availableDimension == AvailableDimension::SECOND) { + availableDimension = AvailableDimension::THIRD; + } + else if (availableDimension == AvailableDimension::THIRD) { + availableDimension = AvailableDimension::FOURTH; + } + else if (availableDimension == AvailableDimension::FOURTH) { + availableDimension = AvailableDimension::FIFTH; + } + else if (availableDimension == AvailableDimension::FIFTH || availableDimension == AvailableDimension::RESET) { + availableDimension = AvailableDimension::FIRST; + } + makeAppropriateDimension(); + event->accept(); + } + else if (event->key() == Qt::Key_Z && (QApplication::keyboardModifiers() & Qt::ControlModifier)) { + // User trying to cancel with Ctrl-Z + quit(); + event->accept(); + } + } + void keyReleaseEvent(QKeyEvent* event) override + { + if (event->key() == Qt::Key_Z && (QApplication::keyboardModifiers() & Qt::ControlModifier)) { + // User trying to cancel with Ctrl-Z + quit(); + event->accept(); + } + else { + TechDrawHandler::keyReleaseEvent(event); + } + } + + void mouseMoveEvent(QMouseEvent* event) override + { + mousePos = event->pos(); + + if (dims.empty()){ + return; + } + + bool textToMiddle = false; + Base::Vector3d dirMaster, delta; + //Change distance dimension based on position of mouse. + if (specialDimension == SpecialDimension::LineOr2PointsDistance + || specialDimension == SpecialDimension::LineOr2PointsChamfer){ + updateDistanceType(); + } + else if (specialDimension == SpecialDimension::ExtendDistance){ + updateExtentDistanceType(); + } + else if (specialDimension == SpecialDimension::ChainDistance || specialDimension == SpecialDimension::CoordDistance){ + updateChainDistanceType(); + textToMiddle = true; + pointPair pp = dims[0]->getLinearPoints(); + dirMaster = pp.second() - pp.first(); + //dirMaster.y = -dirMaster.y; // not needed because y is reversed between property X/Y and scenePositions + + QPointF firstPos = getDimLabel(dims[0])->pos(); + Base::Vector3d pMaster(firstPos.x(), firstPos.y(), 0.0); + Base::Vector3d ipDelta = DrawUtil::getTrianglePoint(pMaster, dirMaster, Base::Vector3d()); + delta = ipDelta.Normalize() * Rez::guiX(activeDimAttributes.getCascadeSpacing()); + } + + int i = 0; + for (auto* dim : dims) { + auto dimType = static_cast(dim->Type.getValue()); + moveDimension(mousePos, dim, textToMiddle, dirMaster, delta, dimType, i); + + if (specialDimension == SpecialDimension::CoordDistance) { + i++; + } + } + } + + QGIDatumLabel* getDimLabel(DrawViewDimension* d) + { + auto* vp = dynamic_cast(Gui::Application::Instance->getViewProvider(d)); + if (!vp) { + return nullptr; + } + auto* qgivDimension(dynamic_cast(vp->getQView())); + if (!qgivDimension) { + return nullptr; + } + return qgivDimension->getDatumLabel(); + } + void moveDimension(QPoint pos, DrawViewDimension* dim, bool textToMiddle = false, Base::Vector3d dir = Base::Vector3d(), + Base::Vector3d delta = Base::Vector3d(), DimensionType type = DimensionType::Distance, int i = 0) + { + if (!dim) { return; } + auto label = getDimLabel(dim); + if (!label) { return; } + + label->setPos(getDimPositionToBe(pos, label->pos(), textToMiddle, dir, delta, type, i)); + } + QPointF getDimPositionToBe(QPoint pos, QPointF curPos = QPointF(), bool textToMiddle = false, Base::Vector3d dir = Base::Vector3d(), + Base::Vector3d delta = Base::Vector3d(), DimensionType type = DimensionType::Distance, int i = 0) + { + auto* vpp = dynamic_cast(Gui::Application::Instance->getViewProvider(partFeat)); + if (!vpp) { return QPointF(); } + + + QPointF scenePos = viewPage->mapToScene(pos) - vpp->getQView()->scenePos(); + + if (textToMiddle) { + // delta is for coord distances. i = 0 when it's a chain so delta is ignored. + float dimDistance = Rez::guiX(activeDimAttributes.getCascadeSpacing()); + if (type == DimensionType::Distance) { + Base::Vector3d pos3d(scenePos.x(), scenePos.y(), 0.0); + float xDim = curPos.x(); + float yDim = curPos.y(); + Base::Vector3d pDim(xDim, yDim, 0.0); + Base::Vector3d p3 = DrawUtil::getTrianglePoint(pos3d, dir, pDim); + p3 = p3 + delta * i; + scenePos = QPointF(p3.x, p3.y); + } + else if(type == DimensionType::DistanceX) { + pointPair pp = dims[0]->getLinearPoints(); + if (Rez::guiX(pp.first().y) > scenePos.y()) + dimDistance = -dimDistance; + + double y = scenePos.y() + i * dimDistance; + scenePos = QPointF(curPos.x(), y); + } + else if(type == DimensionType::DistanceY) { + pointPair pp = dims[0]->getLinearPoints(); + if (Rez::guiX(pp.first().x) > scenePos.x()) + dimDistance = -dimDistance; + + double x = scenePos.x() + i * dimDistance; + scenePos = QPointF(x, curPos.y()); + } + } + + return scenePos; + } + void finishDimensionMove() + { + for (auto* dim : dims) { + auto label = getDimLabel(dim); + double x = Rez::appX(label->X()), y = Rez::appX(label->Y()); + Gui::Command::doCommand(Gui::Command::Doc, "App.ActiveDocument.%s.X = %f", + dim->getNameInDocument(), x); + Gui::Command::doCommand(Gui::Command::Doc, "App.ActiveDocument.%s.Y = %f", + dim->getNameInDocument(), -y); + } + } + + void setDimsSelectability(bool val) + { + for (auto dim : dims) { + setDimSelectability(dim, val); + } + } + void setDimSelectability(DrawViewDimension* d, bool val) + { + QGIDatumLabel* label = getDimLabel(d); + if (label) { + label->setSelectability(val); + } + } + + void mouseReleaseEvent(QMouseEvent* event) override + { + // Base::Console().Warning("mouseReleaseEvent TH\n"); + if (event->button() == Qt::RightButton) { + if (!dims.empty()) { + Gui::Selection().clearSelection(); + clearAndRestartCommand(); + event->accept(); + } + else { + TechDrawHandler::mouseReleaseEvent(event); + } + return; + } + else if (event->button() == Qt::LeftButton) { + mousePos = event->pos(); + + bool finalize = true; + + if (removedRef.hasGeometry()) { + finalize = false; + //Base::Console().Warning("RmvSelection \n"); + // Remove the reference from the vector + ReferenceVector& selVector = getSelectionVector(removedRef); + selVector.erase(std::remove(selVector.begin(), selVector.end(), removedRef), selVector.end()); + + if (!selectionEmpty()) { + availableDimension = AvailableDimension::FIRST; + makeAppropriateDimension(); + } + else { + clearAndRestartCommand(); + } + removedRef = ReferenceEntry(); + } + + if (addedRef.hasGeometry()) { + finalize = false; + //Base::Console().Warning("AddSelection\n"); + //add the geometry to its type vector. Temporarily if not selAllowed + if (addedRef.getSubName() == "") { + // Behavior deactivated for now because I found it annoying. + // To reactivate replace addedRef.hasGeometry() by addedRef.getObject() above. + // This means user selected the view itself. + if (selectionEmpty()) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add Extent dimension")); + createExtentDistanceDimension("DistanceX"); + } + } + else { + ReferenceVector& selVector = getSelectionVector(addedRef); + selVector.push_back(addedRef); + + availableDimension = AvailableDimension::FIRST; + bool selAllowed = makeAppropriateDimension(); + + if (!selAllowed) { + // remove from selection + blockRemoveSel = true; + + Gui::Selection().rmvSelection(addedRef.getObject()->getDocument()->getName(), + addedRef.getObject()->getNameInDocument(), addedRef.getSubName().c_str()); + blockRemoveSel = false; + + if (selVector == selFaces) { + // if sel face and not allowed, then a dimension is being created + // and user clicked on a face to drop it. + // Better would be to disable face selectability when needed. + finalize = true; + } + } + } + addedRef = ReferenceEntry(); + } + + + // Finalize if click on empty space. + if (finalize && !dims.empty()) { + finalizeCommand(); + } + } + } + + void onSelectionChanged(const Gui::SelectionChanges& msg) override + { + //Base::Console().Warning("onSelectionChanged %d - --%s--\n", (int)msg.Type, msg.pSubName); + + if (msg.Type == Gui::SelectionChanges::ClrSelection) { + //clearAndRestartCommand(); + return; + } + + if (msg.Object.getObjectName().empty() + || msg.Object.getDocument() != getPage()->getDocument()) { + if (msg.Type == Gui::SelectionChanges::AddSelection) { + Gui::Selection().rmvSelection(msg.pDocName, msg.pObjectName, msg.pSubName); + } + return; + } + + /*if (msg.Type == Gui::SelectionChanges::SetPreselect) { + Base::Console().Warning("SetPreselect\n"); + std::string geomName = DrawUtil::getGeomTypeFromName(msg.pSubName); + edgeOrPointPreselected = geomName == "Edge" || geomName == "Vertex"; + return; + } + else if (msg.Type == Gui::SelectionChanges::RmvPreselect) { + Base::Console().Warning("RmvPreselect\n"); + edgeOrPointPreselected = false; + return; + }*/ + + App::DocumentObject* obj = msg.Object.getObject(); + if (!obj) { + if (msg.Type == Gui::SelectionChanges::AddSelection) { + Gui::Selection().rmvSelection(msg.pDocName, msg.pObjectName, msg.pSubName); + } + return; + } + + auto* dvp = dynamic_cast(obj); + if (!dvp) { + if (msg.Type == Gui::SelectionChanges::AddSelection) { + Gui::Selection().rmvSelection(msg.pDocName, msg.pObjectName, msg.pSubName); + } + return; + } + + if (partFeat && partFeat != dvp) { + // Dimensions can only be within one view. + + if (msg.Type == Gui::SelectionChanges::AddSelection) { + Gui::Selection().rmvSelection(msg.pDocName, msg.pObjectName, msg.pSubName); + } + return; + } + else { + partFeat = dvp; + } + + if (msg.Type == Gui::SelectionChanges::AddSelection) { + addedRef = ReferenceEntry(dvp, msg.pSubName); + } + else if (msg.Type == Gui::SelectionChanges::RmvSelection) { + if (!blockRemoveSel) { + removedRef = ReferenceEntry(dvp, msg.pSubName); + } + } + } + +private: + QString getCrosshairCursorSVGName() const override + { + return QString::fromLatin1("TechDraw_Dimension_Pointer"); + } + +protected: + SpecialDimension specialDimension; + AvailableDimension availableDimension; + + QPoint mousePos; + + ReferenceVector selPoints; + ReferenceVector selLine; + ReferenceVector selCircleArc; + ReferenceVector selEllipseArc; + ReferenceVector selSplineAndCo; + ReferenceVector selFaces; + ReferenceVector emptyVector; + + ReferenceEntry addedRef; + ReferenceEntry removedRef; + + ReferenceVector initialSelection; + + TechDraw::DrawViewPart* partFeat; + + std::vector dims; + + bool blockRemoveSel; + + void handleInitialSelection() + { + if (initialSelection.size() == 0) { + return; + } + + availableDimension = AvailableDimension::FIRST; + + partFeat = dynamic_cast(initialSelection[0].getObject()); + if (!partFeat) { return; } + + // Add the selected elements to their corresponding selection vectors + for (auto& ref : initialSelection) { + ReferenceVector& selVector = getSelectionVector(ref); + selVector.push_back(ref); + } + + // See if the selection is valid + bool selAllowed = makeAppropriateDimension(); + + if (!selAllowed) { + clearRefVectors(); + } + } + + void finalizeCommand() + { + //Base::Console().Warning("finalizeCommand \n"); + + finishDimensionMove(); + + // Ask for the value of datum dimensions + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/TechDraw"); + + Gui::Command::commitCommand(); + + // Touch the parent feature so the dimension in tree view appears as a child + partFeat->touch(true); + + // This code enables the continuous creation mode. + bool continuousMode = hGrp->GetBool("ContinuousCreationMode", true); + if (continuousMode) { + Gui::Selection().clearSelection(); + clearAndRestartCommand(); + } + else { + viewPage->deactivateHandler(); // no code after this line, Handler get deleted in QGVPage + } + } + + ReferenceVector& getSelectionVector(ReferenceEntry& ref) + { + std::string subName = ref.getSubName(); + if (subName == "") { + return emptyVector; + } + + auto* dvp = static_cast(ref.getObject()); + + std::string geomName = DrawUtil::getGeomTypeFromName(subName); + if (geomName == "Face") { + return selFaces; + } + else if (geomName == "Edge") { + + int GeoId(TechDraw::DrawUtil::getIndexFromName(subName)); + TechDraw::BaseGeomPtr geom = dvp->getGeomByIndex(GeoId); + if (!geom) { + return emptyVector; + } + + if (geom->getGeomType() == TechDraw::GENERIC) { + TechDraw::GenericPtr gen1 = std::static_pointer_cast(geom); + if (gen1->points.size() < 2) { + return emptyVector; + } + return selLine; + //Base::Vector3d line = gen1->points.at(1) - gen1->points.at(0); + } + else if (geom->getGeomType() == TechDraw::CIRCLE || geom->getGeomType() == TechDraw::ARCOFCIRCLE) { + return selCircleArc; + } + else if (geom->getGeomType() == TechDraw::ELLIPSE || geom->getGeomType() == TechDraw::ARCOFELLIPSE) { + return selEllipseArc; + } + else if (geom->getGeomType() == TechDraw::BSPLINE) { + //TechDraw::BSplinePtr spline = std::static_pointer_cast(geom); + //if (spline->isCircle()) { + // return isBSplineCircle; + //} + //else { + //} + return selSplineAndCo; + } + } + else if (geomName == "Vertex") { + return selPoints; + } + + return emptyVector; + } + + /* + bool notSelectedYet(const ReferenceEntry& elem) + { + auto contains = [&](const ReferenceVector& vec, const ReferenceEntry& elem) { + for (const auto& x : vec) + { + if (x == elem){ + return true; + } + } + return false; + }; + + return !contains(selPoints, elem) + && !contains(selLine, elem) + && !contains(selCircleArc, elem) + && !contains(selEllipseArc, elem) + && !contains(selFaces, elem); + }*/ + + bool selectionEmpty() + { + return selPoints.empty() && selLine.empty() && selCircleArc.empty() && selEllipseArc.empty() && selSplineAndCo.empty() && selFaces.empty(); + } + + ReferenceVector allRefs() + { + ReferenceVector result; + + result.reserve(selPoints.size() + selLine.size() + selCircleArc.size() + selEllipseArc.size() + selSplineAndCo.size() + selFaces.size()); + + // Append each vector to result + result.insert(result.end(), selPoints.begin(), selPoints.end()); + result.insert(result.end(), selLine.begin(), selLine.end()); + result.insert(result.end(), selCircleArc.begin(), selCircleArc.end()); + result.insert(result.end(), selEllipseArc.begin(), selEllipseArc.end()); + result.insert(result.end(), selSplineAndCo.begin(), selSplineAndCo.end()); + result.insert(result.end(), selFaces.begin(), selFaces.end()); + + return result; + } + + bool makeAppropriateDimension() { + bool selAllowed = false; + //Base::Console().Warning("makeAppropriateDimension %d %d %d %d %d %d\n", selPoints.size(), selLine.size(), selCircleArc.size(), selEllipseArc.size(), selSplineAndCo.size(), selFaces.size()); + + GeomSelectionSizes selection(selPoints.size(), selLine.size(), selCircleArc.size(), selEllipseArc.size(), selSplineAndCo.size(), selFaces.size()); + if (selection.hasFaces()) { + if (selection.has1Face()) { makeCts_Faces(selAllowed); } + else { return false; } // nothing else with face works + } + else if (selection.hasPoints()) { + if (selection.has1Point()) { selAllowed = true; } + else if (selection.has2Points()) { makeCts_2Point(selAllowed); } + else if (selection.has3Points()) { makeCts_3Point(selAllowed); } + else if (selection.has4MorePoints()) { makeCts_4MorePoints(selAllowed); } + else if (selection.has1Point1Line()) { makeCts_1Point1Line(selAllowed); } + else if (selection.has1Point1Circle()) { makeCts_1Point1Circle(selAllowed); } + else if (selection.has1Point1Ellipse()) { makeCts_1Point1Ellipse(selAllowed); } + } + else if (selection.hasLines()) { + if (selection.has1Line()) { makeCts_1Line(selAllowed); } + else if (selection.has2Lines()) { makeCts_2Line(selAllowed); } + else if (selection.has1Line1Circle()) { makeCts_1Line1Circle(selAllowed); } + else if (selection.has1Line1Ellipse()) { makeCts_1Line1Ellipse(selAllowed); } + } + else if (selection.hasCirclesOrArcs()) { + if (selection.has1Circle()) { makeCts_1Circle(selAllowed); } + else if (selection.has2Circles()) { makeCts_2Circle(selAllowed); } + } + else if (selection.hasEllipseAndCo()) { + if (selection.has1Ellipse()) { makeCts_1Ellipse(selAllowed); } + if (selection.has2Ellipses()) { makeCts_2Ellipses(selAllowed); } + } + else if (selection.hasSplineAndCo()) { + if (selection.has1Spline()) { makeCts_1Spline(selAllowed); } + if (selection.has1SplineAndMore()) { makeCts_1SplineAndMore(selAllowed); } + } + + // Make created constraints unselectable. + if (selAllowed) { + setDimsSelectability(false); + } + + return selAllowed; + } + + void makeCts_Faces(bool& selAllowed) + { + //area + if (availableDimension == AvailableDimension::FIRST) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add Area dimension")); + createAreaDimension(selFaces[0]); + selAllowed = true; + availableDimension = AvailableDimension::RESET; + } + } + + void makeCts_2Point(bool& selAllowed) + { + //distance + if (availableDimension == AvailableDimension::FIRST) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add Distance dimension")); + createDistanceDimension("Distance", { selPoints[0], selPoints[1] }); + specialDimension = SpecialDimension::LineOr2PointsDistance; + selAllowed = true; + if (!isVerticalDistance({ selPoints[0], selPoints[1] })) { + availableDimension = AvailableDimension::RESET; + } + } + if (availableDimension == AvailableDimension::SECOND) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add DistanceX Chamfer dimension")); + createDistanceDimension("DistanceX", { selPoints[0], selPoints[1] }, true); + specialDimension = SpecialDimension::LineOr2PointsChamfer; + availableDimension = AvailableDimension::RESET; + } + } + + void makeCts_3Point(bool& selAllowed) + { + // chain distances, angle + if (availableDimension == AvailableDimension::FIRST) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add horizontal chain dimensions")); + createChainDimension("DistanceX"); + selAllowed = true; + } + if (availableDimension == AvailableDimension::SECOND) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add horizontal coordinate dimensions")); + createCoordDimension("DistanceX"); + } + if (availableDimension == AvailableDimension::THIRD) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add 3-points angle dimension")); + create3pAngleDimension({ selPoints[0], selPoints[1], selPoints[2] }); + } + else if (availableDimension == AvailableDimension::FOURTH) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add 3-points angle dimension")); + create3pAngleDimension({ selPoints[1], selPoints[2], selPoints[0] }); + } + else if (availableDimension == AvailableDimension::FIFTH) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add 3-points angle dimension")); + create3pAngleDimension({ selPoints[2], selPoints[0], selPoints[1] }); + availableDimension = AvailableDimension::RESET; + } + } + + void makeCts_4MorePoints(bool& selAllowed) + { + // chain distances + if (availableDimension == AvailableDimension::FIRST) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add horizontal chain dimension")); + createChainDimension("DistanceX"); + selAllowed = true; + } + if (availableDimension == AvailableDimension::SECOND) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add horizontal coordinate dimensions")); + createCoordDimension("DistanceX"); + availableDimension = AvailableDimension::RESET; + } + } + + void makeCts_1Point1Line(bool& selAllowed) + { + //distance + if (availableDimension == AvailableDimension::FIRST) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add point to line Distance dimension")); + createDistanceDimension("Distance", { selPoints[0], selLine[0] }); + selAllowed = true; + availableDimension = AvailableDimension::RESET; + } + } + + void makeCts_1Point1Circle(bool& selAllowed) + { + //Distance, extent distance + if (availableDimension == AvailableDimension::FIRST) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add length dimension")); + createDistanceDimension("Distance", { selPoints[0], selCircleArc[0] }); + selAllowed = true; + } + if (availableDimension == AvailableDimension::SECOND) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add Extent dimension")); + createExtentDistanceDimension("DistanceX"); + availableDimension = AvailableDimension::RESET; + } + } + + void makeCts_1Point1Ellipse(bool& selAllowed) + { + //Distance + if (availableDimension == AvailableDimension::FIRST) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add length dimension")); + createDistanceDimension("Distance", { selPoints[0], selEllipseArc[0] }); + selAllowed = true; + } + if (availableDimension == AvailableDimension::SECOND) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add Extent dimension")); + createExtentDistanceDimension("DistanceX"); + availableDimension = AvailableDimension::RESET; + } + } + + void makeCts_1Line(bool& selAllowed) + { + //distance + if (availableDimension == AvailableDimension::FIRST) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add length dimension")); + createDistanceDimension("Distance", { selLine[0] }); + specialDimension = SpecialDimension::LineOr2PointsDistance; + selAllowed = true; + if (!isVerticalDistance({ selLine[0] })) { + availableDimension = AvailableDimension::RESET; + } + // Potential improvement for the future: we could show available modes in cursor trail. + //std::vector pixmaps = { icon("TechDraw_LengthDimension"), icon("TechDraw_ExtensionCreateHorizChamferDimension") }; + //addCursorTail(pixmaps); + } + if (availableDimension == AvailableDimension::SECOND) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add DistanceX Chamfer dimension")); + createDistanceDimension("DistanceX", { selLine[0] }, true); + specialDimension = SpecialDimension::LineOr2PointsChamfer; + availableDimension = AvailableDimension::RESET; + } + } + + void makeCts_2Line(bool& selAllowed) + { + //angle (if parallel: Distance (see in createAngleDimension)). + if (availableDimension == AvailableDimension::FIRST) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add Angle dimension")); + createAngleDimension(selLine[0], selLine[1]); + selAllowed = true; + } + if (availableDimension == AvailableDimension::SECOND) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add Extent dimension")); + createExtentDistanceDimension("DistanceX"); + availableDimension = AvailableDimension::RESET; + } + } + + void makeCts_1Line1Circle(bool& selAllowed) + { + //distance, extent distance + if (availableDimension == AvailableDimension::FIRST) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add circle to line Distance dimension")); + createDistanceDimension("Distance", { selCircleArc[0], selLine[0] }); + selAllowed = true; + } + if (availableDimension == AvailableDimension::SECOND) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add Extent dimension")); + createExtentDistanceDimension("DistanceX"); + availableDimension = AvailableDimension::RESET; + } + } + + void makeCts_1Line1Ellipse(bool& selAllowed) + { + //distance, extent distance + if (availableDimension == AvailableDimension::FIRST) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add ellipse to line Distance dimension")); + createDistanceDimension("Distance", { selEllipseArc[0], selLine[0] }); + selAllowed = true; + } + if (availableDimension == AvailableDimension::SECOND) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add Extent dimension")); + createExtentDistanceDimension("DistanceX"); + availableDimension = AvailableDimension::RESET; + } + } + + void makeCts_1Circle(bool& selAllowed) + { + if (availableDimension == AvailableDimension::FIRST) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add Radius dimension")); + createRadiusDiameterDimension(selCircleArc[0], true); + selAllowed = true; + } + if (availableDimension == AvailableDimension::SECOND) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add Radius dimension")); + createRadiusDiameterDimension(selCircleArc[0], false); + if (selCircleArc[0].geomEdgeType() != TechDraw::ARCOFCIRCLE) { + availableDimension = AvailableDimension::RESET; + } + } + if (availableDimension == AvailableDimension::THIRD) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add Arc Length dimension")); + createArcLengthDimension(selCircleArc[0]); + availableDimension = AvailableDimension::RESET; + } + } + + void makeCts_2Circle(bool& selAllowed) + { + //Distance + if (availableDimension == AvailableDimension::FIRST) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add circle to circle Distance dimension")); + createDistanceDimension("Distance", { selCircleArc[0], selCircleArc[1] }); + selAllowed = true; + } + if (availableDimension == AvailableDimension::SECOND) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add Extent dimension")); + createExtentDistanceDimension("DistanceX"); + availableDimension = AvailableDimension::RESET; + } + } + + void makeCts_1Ellipse(bool& selAllowed) + { + if (availableDimension == AvailableDimension::FIRST) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add Radius dimension")); + createRadiusDiameterDimension(selEllipseArc[0], true); + selAllowed = true; + } + if (availableDimension == AvailableDimension::SECOND) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add Radius dimension")); + createRadiusDiameterDimension(selEllipseArc[0], false); + if (selEllipseArc[0].geomEdgeType() != TechDraw::ARCOFELLIPSE) { + availableDimension = AvailableDimension::RESET; + } + } + if (availableDimension == AvailableDimension::THIRD) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add Arc Length dimension")); + createArcLengthDimension(selEllipseArc[0]); + availableDimension = AvailableDimension::RESET; + } + } + + void makeCts_2Ellipses(bool& selAllowed) + { + //Distance + if (availableDimension == AvailableDimension::FIRST) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add ellipse to ellipse Distance dimension")); + createDistanceDimension("Distance", { selEllipseArc[0], selEllipseArc[1] }); + selAllowed = true; + } + if (availableDimension == AvailableDimension::SECOND) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add Extent dimension")); + createExtentDistanceDimension("DistanceX"); + availableDimension = AvailableDimension::RESET; + } + } + + void makeCts_1Spline(bool& selAllowed) + { + //Edge length + if (availableDimension == AvailableDimension::FIRST) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add edge length dimension")); + createArcLengthDimension(selSplineAndCo[0]); + selAllowed = true; + availableDimension = AvailableDimension::RESET; + } + } + + void makeCts_1SplineAndMore(bool& selAllowed) + { + //Extend + if (availableDimension == AvailableDimension::FIRST) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add Extent dimension")); + createExtentDistanceDimension("DistanceX"); + selAllowed = true; + availableDimension = AvailableDimension::RESET; + } + } + + void createAreaDimension(ReferenceEntry ref) + { + DrawViewDimension* dim = dimMaker(partFeat, "Area", { ref }, {}); + + dims.push_back(dim); + moveDimension(mousePos, dim); + } + + void createRadiusDiameterDimension(ReferenceEntry ref, bool firstCstr) { + bool isCircleGeom = true; + + int GeoId(TechDraw::DrawUtil::getIndexFromName(ref.getSubName())); + TechDraw::BaseGeomPtr geom = partFeat->getGeomByIndex(GeoId); + isCircleGeom = (geom->getGeomType() == TechDraw::CIRCLE) || (geom->getGeomType() == TechDraw::ELLIPSE); + + // Use same preference as in sketcher? + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/TechDraw/dimensioning"); + bool dimensioningDiameter = hGrp->GetBool("DimensioningDiameter", true); + bool dimensioningRadius = hGrp->GetBool("DimensioningRadius", true); + + DrawViewDimension* dim; + if ((firstCstr && dimensioningRadius && !dimensioningDiameter) || + (!firstCstr && !dimensioningRadius && dimensioningDiameter) || + (firstCstr && dimensioningRadius && dimensioningDiameter && !isCircleGeom) || + (!firstCstr && dimensioningRadius && dimensioningDiameter && isCircleGeom)) { + dim = dimMaker(partFeat, "Radius", { ref }, {}); + } + else { + dim = dimMaker(partFeat, "Diameter", { ref }, {}); + } + + dims.push_back(dim); + moveDimension(mousePos, dim); + } + + void createAngleDimension(ReferenceEntry ref1, ReferenceEntry ref2) { + if (TechDraw::isValidMultiEdge({ ref1, ref2 }) != isAngle) { + //isValidMultiEdge check if lines are parallel. + restartCommand(QT_TRANSLATE_NOOP("Command", "Add Distance dimension")); + createDistanceDimension("Distance", { ref1, ref2 }); + return; + } + + DrawViewDimension* dim = dimMaker(partFeat, "Angle", {ref1, ref2}, {}); + + dims.push_back(dim); + moveDimension(mousePos, dim); + } + + void create3pAngleDimension(ReferenceVector refs) + { + DrawViewDimension* dim = dimMaker(partFeat, "Angle3Pt", refs, {}); + + dims.push_back(dim); + moveDimension(mousePos, dim); + } + + void createArcLengthDimension(ReferenceEntry ref) + { + DrawViewDimension* dim = makeArcLengthDimension(ref); + + dims.push_back(dim); + moveDimension(mousePos, dim); + } + + void createDistanceDimension(std::string type, ReferenceVector refs, bool chamfer = false) + { + DrawViewDimension* dim = dimMaker(partFeat, type, refs, {}); + + if (chamfer) { + // Add the angle to the label + TechDraw::pointPair pp = dim->getLinearPoints(); + float dx = pp.first().x - pp.second().x; + float dy = pp.first().y - pp.second().y; + int alpha = round(Base::toDegrees(abs(atan(type == "DistanceY" ? (dx / dy) : (dy / dx))))); + std::string sAlpha = std::to_string(alpha); + std::string formatSpec = dim->FormatSpec.getStrValue(); + formatSpec = formatSpec + " x" + sAlpha + "°"; + dim->FormatSpec.setValue(formatSpec); + } + + dims.push_back(dim); + moveDimension(mousePos, dim); + } + + void createExtentDistanceDimension(std::string type) { + specialDimension = SpecialDimension::ExtendDistance; + + DrawViewDimension* dim = DrawDimHelper::makeExtentDim(partFeat, type, allRefs()); + + dims.push_back(dim); + moveDimension(mousePos, dim); + } + + void updateDistanceType() + { + if (dims.empty()) { + return; + } + + auto type = static_cast(dims[0]->Type.getValue()); + SpecialDimension backup = specialDimension; + bool chamfer = specialDimension == SpecialDimension::LineOr2PointsChamfer; + + TechDraw::pointPair pp = dims[0]->getLinearPoints(); + Base::Vector3d pnt1 = Rez::guiX(pp.first()); + Base::Vector3d pnt2 = Rez::guiX(pp.second()); + + QPointF fpos = getDimPositionToBe(mousePos); + double minX, minY, maxX, maxY; + minX = min(pnt1.x, pnt2.x); + maxX = max(pnt1.x, pnt2.x); + minY = min(pnt1.y, pnt2.y); + maxY = max(pnt1.y, pnt2.y); + + std::string newType = "Distance"; + if (fpos.x() > minX && fpos.x() < maxX + && (fpos.y() < minY || fpos.y() > maxY) && type != DimensionType::DistanceX) { + if (chamfer) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add DistanceX Chamfer dimension")); + } + else { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add DistanceX dimension")); + } + newType = "DistanceX"; + } + else if (fpos.y() > minY && fpos.y() < maxY + && (fpos.x() < minX || fpos.x() > maxX) && type != DimensionType::DistanceY) { + if (chamfer) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add DistanceY Chamfer dimension")); + } + else { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add DistanceY dimension")); + } + newType = "DistanceY"; + } + else if ((((fpos.y() < minY || fpos.y() > maxY) && (fpos.x() < minX || fpos.x() > maxX)) + || (fpos.y() > minY && fpos.y() < maxY && fpos.x() > minX && fpos.x() < maxX)) && type != DimensionType::Distance + && !chamfer) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add Distance dimension")); + } + else { + return; + } + specialDimension = backup; + + if (selLine.size() == 1) { + createDistanceDimension(newType, { selLine[0] }, chamfer); + } + else { + createDistanceDimension(newType, { selPoints[0], selPoints[1] }, chamfer); + } + setDimsSelectability(false); + } + + void updateExtentDistanceType() + { + if (dims.empty()) { + return; + } + + auto type = static_cast(dims[0]->Type.getValue()); + + TechDraw::pointPair pp = dims[0]->getLinearPoints(); + Base::Vector3d pnt1 = Rez::guiX(pp.first()); + Base::Vector3d pnt2 = Rez::guiX(pp.second()); + + QPointF fpos = getDimPositionToBe(mousePos); + + double minX, minY, maxX, maxY; + minX = min(pnt1.x, pnt2.x); + maxX = max(pnt1.x, pnt2.x); + minY = min(pnt1.y, pnt2.y); + maxY = max(pnt1.y, pnt2.y); + + if (fpos.x() > minX && fpos.x() < maxX + && (fpos.y() < minY || fpos.y() > maxY) && type != DimensionType::DistanceX) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add DistanceX extent dimension")); + createExtentDistanceDimension("DistanceX"); + } + else if (fpos.y() > minY && fpos.y() < maxY + && (fpos.x() < minX || fpos.x() > maxX) && type != DimensionType::DistanceY) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add DistanceY extent dimension")); + createExtentDistanceDimension("DistanceY"); + } + else { + return; + } + + setDimsSelectability(false); + } + + void updateChainDistanceType() + { + if (dims.empty()) { + return; + } + + double minX = DBL_MAX; + double minY = DBL_MAX; + double maxX = -DBL_MAX; + double maxY = -DBL_MAX; + for (auto dim : dims) { + TechDraw::pointPair pp = dim->getLinearPoints(); + Base::Vector3d pnt1 = Rez::guiX(pp.first()); + Base::Vector3d pnt2 = Rez::guiX(pp.second()); + + minX = min(minX, min(pnt1.x, pnt2.x)); + maxX = max(maxX, max(pnt1.x, pnt2.x)); + minY = min(minY, min(pnt1.y, pnt2.y)); + maxY = max(maxY, max(pnt1.y, pnt2.y)); + } + + QPointF fpos = getDimPositionToBe(mousePos); + + auto type = static_cast(dims[0]->Type.getValue()); + + if (fpos.x() > minX && fpos.x() < maxX + && (fpos.y() < minY || fpos.y() > maxY) && type != DimensionType::DistanceX) { + if (specialDimension == SpecialDimension::ChainDistance) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add horizontal chain dimensions")); + createChainDimension("DistanceX"); + } + else { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add horizontal coord dimensions")); + createCoordDimension("DistanceX"); + } + } + else if (fpos.y() > minY && fpos.y() < maxY + && (fpos.x() < minX || fpos.x() > maxX) && type != DimensionType::DistanceY) { + if (specialDimension == SpecialDimension::ChainDistance) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add vertical chain dimensions")); + createChainDimension("DistanceY"); + } + else { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add vertical coord dimensions")); + createCoordDimension("DistanceY"); + } + } + else if (((fpos.y() < minY || fpos.y() > maxY) && (fpos.x() < minX || fpos.x() > maxX)) && type != DimensionType::Distance) { + if (specialDimension == SpecialDimension::ChainDistance) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add oblique chain dimensions")); + createChainDimension("Distance"); + } + else { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add oblique coord dimensions")); + createCoordDimension("Distance"); + } + } + else { + return; + } + + setDimsSelectability(false); + } + + void createChainDimension(std::string type) + { + specialDimension = SpecialDimension::ChainDistance; + if (type == "Distance") { + dims = makeObliqueChainDimension(selPoints); + } + else { + for (size_t i = 0; i < selPoints.size() - 1; ++i) { + DrawViewDimension* dim = dimMaker(partFeat, type, { selPoints[i], selPoints[i + 1] }, {}); + + dims.push_back(dim); + positionDimText(dim); + } + } + } + + void createCoordDimension(std::string type) + { + specialDimension = SpecialDimension::CoordDistance; + if (type == "Distance") { + dims = makeObliqueCoordDimension(selPoints); + } + else { + for (size_t i = 0; i < selPoints.size() - 1; ++i) { + DrawViewDimension* dim = dimMaker(partFeat, type, { selPoints[0], selPoints[i + 1] }, {}); + + dims.push_back(dim); + positionDimText(dim, i); + } + } + } + + bool isVerticalDistance(ReferenceVector refs) + { + DimensionGeometryType geometryRefs2d = validateDimSelection( + refs, { "Edge", "Vertex" }, { 1, 2 }, { isDiagonal }); + + return geometryRefs2d == TechDraw::isDiagonal; + } + + QPixmap icon(std::string name) + { + qreal pixelRatio = 1; + Gui::View3DInventorViewer* viewer = getViewer(); + if (viewer) { + pixelRatio = viewer->devicePixelRatio(); + } + int width = 16 * pixelRatio; + return Gui::BitmapFactory().pixmapFromSvg(name.c_str(), QSize(width, width)); + } + + void restartCommand(const char* cstrName) { + specialDimension = SpecialDimension::None; + Gui::Command::abortCommand(); + Gui::Command::openCommand(cstrName); + + dims.clear(); + } + + void clearAndRestartCommand() { + Gui::Command::abortCommand(); + Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Dimension")); + specialDimension = SpecialDimension::None; + mousePos = QPoint(0,0); + clearRefVectors(); + partFeat = nullptr; + dims.clear(); + } + + void clearRefVectors() + { + selPoints.clear(); + selLine.clear(); + selCircleArc.clear(); + selEllipseArc.clear(); + selSplineAndCo.clear(); + selFaces.clear(); + } +}; DEF_STD_CMD_A(CmdTechDrawDimension) @@ -101,7 +1403,9 @@ CmdTechDrawDimension::CmdTechDrawDimension() sAppModule = "TechDraw"; sGroup = QT_TR_NOOP("TechDraw"); sMenuText = QT_TR_NOOP("Insert Dimension"); - sToolTipText = sMenuText; + sToolTipText = QT_TR_NOOP("Dimension contextually based on your selection.\n" + "Depending on your selection you might have several dimensions available. You can cycle through them using the M key.\n" + "Left clicking on empty space will validate the current Dimensiont. Right clicking or pressing Esc will cancel."); sWhatsThis = "TechDraw_Dimension"; sStatusTip = sToolTipText; sPixmap = "TechDraw_Dimension"; @@ -110,6 +1414,13 @@ CmdTechDrawDimension::CmdTechDrawDimension() void CmdTechDrawDimension::activated(int iMsg) { Q_UNUSED(iMsg); + + ReferenceVector references2d; + ReferenceVector references3d; + TechDraw::DrawViewPart* partFeat = + TechDraw::getReferencesFromSelection(references2d, references3d); + + activateHandler(new TDHandlerDimension(references2d, partFeat)); } bool CmdTechDrawDimension::isActive() @@ -119,6 +1430,56 @@ bool CmdTechDrawDimension::isActive() return (havePage && haveView); } + +// Comp for dimension tools ============================================= + +class CmdTechDrawCompDimensionTools : public Gui::GroupCommand +{ +public: + CmdTechDrawCompDimensionTools() + : GroupCommand("TechDraw_CompDimensionTools") + { + sAppModule = "TechDraw"; + sGroup = "TechDraw"; + sMenuText = QT_TR_NOOP("Dimension"); + sToolTipText = QT_TR_NOOP("Dimension tools."); + sWhatsThis = "TechDraw_CompDimensionTools"; + sStatusTip = sToolTipText; + eType = ForEdit; + + setCheckable(false); + setRememberLast(false); + + addCommand("TechDraw_Dimension"); + addCommand(); //separator + addCommand("TechDraw_LengthDimension"); + addCommand("TechDraw_HorizontalDimension"); + addCommand("TechDraw_VerticalDimension"); + addCommand("TechDraw_RadiusDimension"); + addCommand("TechDraw_DiameterDimension"); + addCommand("TechDraw_AngleDimension"); + addCommand("TechDraw_3PtAngleDimension"); + addCommand("TechDraw_AreaDimension"); + addCommand("TechDraw_ExtensionCreateLengthArc"); + addCommand(); //separator + addCommand("TechDraw_HorizontalExtentDimension"); + addCommand("TechDraw_VerticalExtentDimension"); + addCommand(); //separator + addCommand("TechDraw_ExtensionCreateHorizChainDimension"); + addCommand("TechDraw_ExtensionCreateVertChainDimension"); + addCommand("TechDraw_ExtensionCreateObliqueChainDimension"); + addCommand(); //separator + addCommand("TechDraw_ExtensionCreateHorizCoordDimension"); + addCommand("TechDraw_ExtensionCreateVertCoordDimension"); + addCommand("TechDraw_ExtensionCreateObliqueCoordDimension"); + addCommand(); //separator + addCommand("TechDraw_ExtensionCreateHorizChamferDimension"); + addCommand("TechDraw_ExtensionCreateVertChamferDimension"); + } + + const char* className() const override { return "CmdTechDrawCompDimensionTools"; } +}; + //=========================================================================== // TechDraw_RadiusDimension //=========================================================================== @@ -135,6 +1496,8 @@ CmdTechDrawRadiusDimension::CmdTechDrawRadiusDimension() sWhatsThis = "TechDraw_RadiusDimension"; sStatusTip = sToolTipText; sPixmap = "TechDraw_RadiusDimension"; + sAccel = "D"; + eType = ForEdit; } void CmdTechDrawRadiusDimension::activated(int iMsg) @@ -829,6 +2192,95 @@ void execAngle3Pt(Gui::Command* cmd) positionDimText(dim); } +//=========================================================================== +// TechDraw_AreaDimension +//=========================================================================== + +DEF_STD_CMD_A(CmdTechDrawAreaDimension) + +CmdTechDrawAreaDimension::CmdTechDrawAreaDimension() + : Command("TechDraw_AreaDimension") +{ + sAppModule = "TechDraw"; + sGroup = QT_TR_NOOP("TechDraw"); + sMenuText = QT_TR_NOOP("Insert Area Dimension"); + sToolTipText = sMenuText; + sWhatsThis = "TechDraw_AreaDimension"; + sStatusTip = sToolTipText; + sPixmap = "TechDraw_AreaDimension"; +} + +void CmdTechDrawAreaDimension::activated(int iMsg) +{ + Q_UNUSED(iMsg); + Gui::TaskView::TaskDialog* dlg = Gui::Control().activeDialog(); + if (dlg) { + QMessageBox::warning(Gui::getMainWindow(), + QObject::tr("Task In Progress"), + QObject::tr("Close active task dialog and try again.")); + return; + } + + execArea(this); +} + +bool CmdTechDrawAreaDimension::isActive() +{ + bool havePage = DrawGuiUtil::needPage(this); + bool haveView = DrawGuiUtil::needView(this); + return (havePage && haveView); +} + +void execArea(Gui::Command* cmd) +{ + bool result = _checkDrawViewPart(cmd); + if (!result) { + QMessageBox::warning(Gui::getMainWindow(), + QObject::tr("Incorrect selection"), + QObject::tr("No View of a Part in selection.")); + return; + } + + ReferenceVector references2d; + ReferenceVector references3d; + TechDraw::DrawViewPart* partFeat = + TechDraw::getReferencesFromSelection(references2d, references3d); + + //Define the geometric configuration required for a length dimension + StringVector acceptableGeometry({"Face"}); + std::vector minimumCounts({1}); + std::vector acceptableDimensionGeometrys({isFace}); + + //what 2d geometry configuration did we receive? + DimensionGeometryType geometryRefs2d = validateDimSelection( + references2d, acceptableGeometry, minimumCounts, acceptableDimensionGeometrys); + if (geometryRefs2d == TechDraw::isInvalid) { + QMessageBox::warning(Gui::getMainWindow(), + QObject::tr("Incorrect Selection"), + QObject::tr("Can not make 2d angle dimension from selection")); + return; + } + + //what 3d geometry configuration did we receive? + DimensionGeometryType geometryRefs3d; + if (geometryRefs2d == TechDraw::isViewReference && !references3d.empty()) { + geometryRefs3d = validateDimSelection3d(partFeat, + references3d, + acceptableGeometry, + minimumCounts, + acceptableDimensionGeometrys); + if (geometryRefs3d == TechDraw::isInvalid) { + QMessageBox::warning(Gui::getMainWindow(), + QObject::tr("Incorrect Selection"), + QObject::tr("Can not make 3d angle dimension from selection")); + return; + } + } + + //build the dimension + dimensionMaker(partFeat, "Area", references2d, references3d); +} + // TechDraw_LinkDimension is DEPRECATED. Use TechDraw_DimensionRepair instead. //! link 3D geometry to Dimension(s) on a Page @@ -946,10 +2398,10 @@ void CmdTechDrawExtentGroup::activated(int iMsg) pcAction->setIcon(pcAction->actions().at(iMsg)->icon()); switch (iMsg) { case 0: - execExtent(this, 0); + execExtent(this, "DistanceX"); break; case 1: - execExtent(this, 1); + execExtent(this, "DistanceY"); break; default: Base::Console().Message("CMD::ExtGrp - invalid iMsg: %d\n", iMsg); @@ -1040,7 +2492,7 @@ void CmdTechDrawHorizontalExtentDimension::activated(int iMsg) return; } - execExtent(this, 0); + execExtent(this, "DistanceX"); } bool CmdTechDrawHorizontalExtentDimension::isActive() @@ -1050,7 +2502,7 @@ bool CmdTechDrawHorizontalExtentDimension::isActive() return (havePage && haveView); } -void execExtent(Gui::Command* cmd, int direction) +void execExtent(Gui::Command* cmd, const std::string& dimType) { bool result = _checkDrawViewPart(cmd); if (!result) { @@ -1059,7 +2511,6 @@ void execExtent(Gui::Command* cmd, int direction) QObject::tr("No View of a Part in selection.")); return; } - ReferenceVector references2d; ReferenceVector references3d; TechDraw::DrawViewPart* partFeat = @@ -1087,6 +2538,7 @@ void execExtent(Gui::Command* cmd, int direction) QObject::tr("Can not make 2d extent dimension from selection")); return; } + //what 3d geometry configuration did we receive? DimensionGeometryType geometryRefs3d; if (geometryRefs2d == TechDraw::isViewReference && !references3d.empty()) { @@ -1104,22 +2556,10 @@ void execExtent(Gui::Command* cmd, int direction) } if (references3d.empty()) { - std::vector edgeNames; - for (auto& ref : references2d) { - if (ref.getSubName().empty()) { - continue; - } - std::string geomType = DrawUtil::getGeomTypeFromName(ref.getSubName()); - if (geomType == "Edge") { - edgeNames.push_back(ref.getSubName()); - } - } - DrawDimHelper::makeExtentDim(partFeat, edgeNames, - direction);//0 - horizontal, 1 - vertical - } else { - DrawDimHelper::makeExtentDim3d(partFeat, - references3d, - direction);//0 - horizontal, 1 - vertical + DrawDimHelper::makeExtentDim(partFeat, dimType, references2d); + } + else { + DrawDimHelper::makeExtentDim3d(partFeat, dimType, references3d); } } @@ -1153,7 +2593,7 @@ void CmdTechDrawVerticalExtentDimension::activated(int iMsg) return; } - execExtent(this, 1); + execExtent(this, "DistanceY"); } bool CmdTechDrawVerticalExtentDimension::isActive() @@ -1339,12 +2779,14 @@ void CreateTechDrawCommandsDims() rcCmdMgr.addCommand(new CmdTechDrawVerticalDimension()); rcCmdMgr.addCommand(new CmdTechDrawAngleDimension()); rcCmdMgr.addCommand(new CmdTechDraw3PtAngleDimension()); + rcCmdMgr.addCommand(new CmdTechDrawAreaDimension()); rcCmdMgr.addCommand(new CmdTechDrawExtentGroup()); rcCmdMgr.addCommand(new CmdTechDrawVerticalExtentDimension()); rcCmdMgr.addCommand(new CmdTechDrawHorizontalExtentDimension()); rcCmdMgr.addCommand(new CmdTechDrawLinkDimension()); rcCmdMgr.addCommand(new CmdTechDrawLandmarkDimension()); rcCmdMgr.addCommand(new CmdTechDrawDimensionRepair()); + rcCmdMgr.addCommand(new CmdTechDrawCompDimensionTools()); } //------------------------------------------------------------------------------ @@ -1352,14 +2794,31 @@ void CreateTechDrawCommandsDims() //Common code to build a dimension feature DrawViewDimension* dimensionMaker(TechDraw::DrawViewPart* dvp, std::string dimType, ReferenceVector references2d, ReferenceVector references3d) +{ + Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Create Dimension")); + + TechDraw::DrawViewDimension* dim = dimMaker(dvp, dimType, references2d, references3d); + + Gui::Command::commitCommand(); + + // Touch the parent feature so the dimension in tree view appears as a child + dvp->touch(true); + + // Select only the newly created dimension + Gui::Selection().clearSelection(); + Gui::Selection().addSelection(dvp->getDocument()->getName(), dim->getNameInDocument()); + + return dim; +} + +DrawViewDimension* dimMaker(TechDraw::DrawViewPart* dvp, std::string dimType, + ReferenceVector references2d, ReferenceVector references3d) { TechDraw::DrawPage* page = dvp->findParentPage(); std::string PageName = page->getNameInDocument(); - TechDraw::DrawViewDimension* dim = nullptr; std::string dimName = dvp->getDocument()->getUniqueObjectName("Dimension"); - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Create Dimension")); Gui::Command::doCommand(Gui::Command::Doc, "App.activeDocument().addObject('TechDraw::DrawViewDimension', '%s')", dimName.c_str()); @@ -1374,8 +2833,7 @@ DrawViewDimension* dimensionMaker(TechDraw::DrawViewPart* dvp, std::string dimTy dimName.c_str(), "Projected"); - dim = - dynamic_cast(dvp->getDocument()->getObject(dimName.c_str())); + auto* dim = dynamic_cast(dvp->getDocument()->getObject(dimName.c_str())); if (!dim) { throw Base::TypeError("CmdTechDrawNewDiameterDimension - dim not found\n"); } @@ -1389,27 +2847,20 @@ DrawViewDimension* dimensionMaker(TechDraw::DrawViewPart* dvp, std::string dimTy PageName.c_str(), dimName.c_str()); - Gui::Command::commitCommand(); - - // Touch the parent feature so the dimension in tree view appears as a child - dvp->touch(true); - - // Select only the newly created dimension - Gui::Selection().clearSelection(); - Gui::Selection().addSelection(dvp->getDocument()->getName(), dim->getNameInDocument()); dim->recomputeFeature(); + return dim; } //position the Dimension text on the view -void positionDimText(DrawViewDimension* dim) +void positionDimText(DrawViewDimension* dim, int offsetIndex) { TechDraw::pointPair pp = dim->getLinearPoints(); Base::Vector3d mid = (pp.first() + pp.second()) / 2.0; dim->X.setValue(mid.x); double fontSize = Preferences::dimFontSizeMM(); - dim->Y.setValue(-mid.y + 0.5 * fontSize); + dim->Y.setValue(-mid.y + (offsetIndex * 1.5 + 0.5) * fontSize); } //=========================================================================== // Selection Validation Helpers diff --git a/src/Mod/TechDraw/Gui/CommandExtensionDims.cpp b/src/Mod/TechDraw/Gui/CommandExtensionDims.cpp index 60f07baa9c..61c126598f 100644 --- a/src/Mod/TechDraw/Gui/CommandExtensionDims.cpp +++ b/src/Mod/TechDraw/Gui/CommandExtensionDims.cpp @@ -25,6 +25,8 @@ # include # include # include +# include +# include #endif # include @@ -52,6 +54,7 @@ #include "DrawGuiUtil.h" #include "TaskCustomizeFormat.h" #include "TaskSelectLineAttributes.h" +#include "CommandExtensionDims.h" using namespace TechDrawGui; @@ -78,7 +81,7 @@ namespace TechDrawGui { std::vector_getDimensions(std::vector selection, std::string needDimType); std::vector _getVertexInfo(TechDraw::DrawViewPart* objFeat, std::vector subNames); - TechDraw::DrawViewDimension* _createLinDimension(Gui::Command* cmd, + TechDraw::DrawViewDimension* _createLinDimension( TechDraw::DrawViewPart* objFeat, std::string startVertex, std::string endVertex, @@ -1280,7 +1283,7 @@ void execCreateHorizChainDimension(Gui::Command* cmd) { float yMaster = 0.0; for (long unsigned int n = 0; n < allVertexes.size() - 1; n++) { TechDraw::DrawViewDimension* dim; - dim = _createLinDimension(cmd, objFeat, allVertexes[n].name, allVertexes[n + 1].name, "DistanceX"); + dim = _createLinDimension(objFeat, allVertexes[n].name, allVertexes[n + 1].name, "DistanceX"); TechDraw::pointPair pp = dim->getLinearPoints(); Base::Vector3d mid = (pp.first() + pp.second()) / 2.0; dim->X.setValue(mid.x); @@ -1347,7 +1350,7 @@ void execCreateVertChainDimension(Gui::Command* cmd) { double fontSize = Preferences::dimFontSizeMM(); for (long unsigned int n = 0; n < allVertexes.size() - 1; n++) { TechDraw::DrawViewDimension* dim; - dim = _createLinDimension(cmd, objFeat, allVertexes[n].name, allVertexes[n + 1].name, "DistanceY"); + dim = _createLinDimension(objFeat, allVertexes[n].name, allVertexes[n + 1].name, "DistanceY"); TechDraw::pointPair pp = dim->getLinearPoints(); Base::Vector3d mid = (pp.first() + pp.second()) / 2.0; if (n == 0) @@ -1404,7 +1407,37 @@ void execCreateObliqueChainDimension(Gui::Command* cmd) { } Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Create Oblique Chain Dim")); - const std::vector subNames = selection[0].getSubNames(); + + std::vector refs; + for (auto& subName : selection[0].getSubNames()) { + refs.push_back(ReferenceEntry(objFeat, subName)); + } + + auto dims = makeObliqueChainDimension(refs); + if(dims.empty()){ + Gui::Command::abortCommand(); + } + else { + objFeat->refreshCEGeoms(); + objFeat->requestPaint(); + cmd->getSelection().clearSelection(); + Gui::Command::commitCommand(); + } +} + +std::vector TechDrawGui::makeObliqueChainDimension(std::vector refs) +{ + if (refs.empty()) { + return {}; + } + + std::vector subNames; + auto* objFeat = static_cast(refs[0].getObject()); + for (auto& ref : refs) { + subNames.push_back(ref.getSubName()); + } + std::vector dims; + std::vector allVertexes, carrierVertexes; allVertexes = _getVertexInfo(objFeat, subNames); if (!allVertexes.empty() && allVertexes.size() > 1) { @@ -1443,17 +1476,16 @@ void execCreateObliqueChainDimension(Gui::Command* cmd) { double fontSize = Preferences::dimFontSizeMM(); for (long unsigned int n = 0; n < allVertexes.size() - 1; n++) { TechDraw::DrawViewDimension* dim; - dim = _createLinDimension(cmd, objFeat, carrierVertexes[n].name, carrierVertexes[n + 1].name, "Distance"); + dim = _createLinDimension(objFeat, carrierVertexes[n].name, carrierVertexes[n + 1].name, "Distance"); TechDraw::pointPair pp = dim->getLinearPoints(); Base::Vector3d mid = (pp.first() + pp.second()) / 2.0 + delta; dim->X.setValue(mid.x); dim->Y.setValue(-mid.y + 0.5 * fontSize); + dims.push_back(dim); } - objFeat->refreshCEGeoms(); - objFeat->requestPaint(); - cmd->getSelection().clearSelection(); - Gui::Command::commitCommand(); } + + return dims; } DEF_STD_CMD_A(CmdTechDrawExtensionCreateObliqueChainDimension) @@ -1630,7 +1662,7 @@ void execCreateHorizCoordDimension(Gui::Command* cmd) { dimDistance = -dimDistance; for (long unsigned int n = 0; n < allVertexes.size() - 1; n++) { TechDraw::DrawViewDimension* dim; - dim = _createLinDimension(cmd, objFeat, allVertexes[0].name, allVertexes[n + 1].name, "DistanceX"); + dim = _createLinDimension(objFeat, allVertexes[0].name, allVertexes[n + 1].name, "DistanceX"); TechDraw::pointPair pp = dim->getLinearPoints(); Base::Vector3d mid = (pp.first() + pp.second()) / 2.0; dim->X.setValue(mid.x); @@ -1703,7 +1735,7 @@ void execCreateVertCoordDimension(Gui::Command* cmd) { double fontSize = Preferences::dimFontSizeMM(); for (long unsigned int n = 0; n < allVertexes.size() - 1; n++) { TechDraw::DrawViewDimension* dim; - dim = _createLinDimension(cmd, objFeat, allVertexes[0].name, allVertexes[n + 1].name, "DistanceY"); + dim = _createLinDimension(objFeat, allVertexes[0].name, allVertexes[n + 1].name, "DistanceY"); TechDraw::pointPair pp = dim->getLinearPoints(); Base::Vector3d mid = (pp.first() + pp.second()) / 2.0; dim->X.setValue(xMaster + dimDistance * n); @@ -1760,7 +1792,37 @@ void execCreateObliqueCoordDimension(Gui::Command* cmd) { } Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Create Oblique Coord Dim")); - const std::vector subNames = selection[0].getSubNames(); + + std::vector refs; + for (auto& subName : selection[0].getSubNames()) { + refs.push_back(ReferenceEntry(objFeat, subName)); + } + + auto dims = makeObliqueCoordDimension(refs); + if (dims.empty()) { + Gui::Command::abortCommand(); + } + else { + objFeat->refreshCEGeoms(); + objFeat->requestPaint(); + cmd->getSelection().clearSelection(); + Gui::Command::commitCommand(); + } +} + +std::vector TechDrawGui::makeObliqueCoordDimension(std::vector refs) +{ + if (refs.empty()) { + return {}; + } + + std::vector subNames; + auto* objFeat = static_cast(refs[0].getObject()); + for (auto& ref : refs) { + subNames.push_back(ref.getSubName()); + } + std::vector dims; + std::vector allVertexes, carrierVertexes; allVertexes = _getVertexInfo(objFeat, subNames); if (!allVertexes.empty() && allVertexes.size() > 1) { @@ -1804,17 +1866,16 @@ void execCreateObliqueCoordDimension(Gui::Command* cmd) { double fontSize = Preferences::dimFontSizeMM(); for (long unsigned int n = 0; n < allVertexes.size() - 1; n++) { TechDraw::DrawViewDimension* dim; - dim = _createLinDimension(cmd, objFeat, carrierVertexes[0].name, carrierVertexes[n + 1].name, "Distance"); + dim = _createLinDimension(objFeat, carrierVertexes[0].name, carrierVertexes[n + 1].name, "Distance"); TechDraw::pointPair pp = dim->getLinearPoints(); Base::Vector3d mid = (pp.first() + pp.second()) / 2.0 + delta * (n + 1); dim->X.setValue(mid.x); dim->Y.setValue(-mid.y + 0.5 * fontSize); + dims.push_back(dim); } } - objFeat->refreshCEGeoms(); - objFeat->requestPaint(); - cmd->getSelection().clearSelection(); - Gui::Command::commitCommand(); + + return dims; } DEF_STD_CMD_A(CmdTechDrawExtensionCreateObliqueCoordDimension) @@ -1991,7 +2052,7 @@ void execCreateHorizChamferDimension(Gui::Command* cmd) { if (!allVertexes.empty() && allVertexes.size() > 1) { const auto Pi180 = 180.0 / M_PI; TechDraw::DrawViewDimension* dim; - dim = _createLinDimension(cmd, objFeat, allVertexes[0].name, allVertexes[1].name, "DistanceX"); + dim = _createLinDimension(objFeat, allVertexes[0].name, allVertexes[1].name, "DistanceX"); float yMax = std::max(abs(allVertexes[0].point.y), abs(allVertexes[1].point.y)) + 7.0; if (std::signbit(allVertexes[0].point.y)) yMax = -yMax; @@ -2060,7 +2121,7 @@ void execCreateVertChamferDimension(Gui::Command* cmd) { if (!allVertexes.empty() && allVertexes.size() > 1) { const auto Pi180 = 180.0 / M_PI; TechDraw::DrawViewDimension* dim; - dim = _createLinDimension(cmd, objFeat, allVertexes[0].name, allVertexes[1].name, "DistanceY"); + dim = _createLinDimension(objFeat, allVertexes[0].name, allVertexes[1].name, "DistanceY"); float xMax = std::max(abs(allVertexes[0].point.x), abs(allVertexes[1].point.x)) + 7.0; if (std::signbit(allVertexes[0].point.x)) xMax = -xMax; @@ -2240,47 +2301,19 @@ void CmdTechDrawExtensionCreateLengthArc::activated(int iMsg) { } Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Create Arc Length Dim")); - const std::vector subNames = selection[0].getSubNames(); - int geoId = TechDraw::DrawUtil::getIndexFromName(subNames[0]); - TechDraw::BaseGeomPtr geom = objFeat->getGeomByIndex(geoId); - std::string geoType = TechDraw::DrawUtil::getGeomTypeFromName(subNames[0]); - if (geoType == "Edge" && geom->getGeomType() == TechDraw::ARCOFCIRCLE) { - TechDraw::AOCPtr arcTag = std::static_pointer_cast(geom); - float radius = arcTag->radius; - Base::Vector3d centerPt = arcTag->center; - centerPt.y = -centerPt.y; - Base::Vector3d startPt = arcTag->startPnt; - startPt.y = -startPt.y; - Base::Vector3d endPt = arcTag->endPnt; - endPt.y = -endPt.y; - std::stringstream startName, endName, formatSpec; - double scale = objFeat->getScale(); - Base::Vector3d cvPoint = CosmeticVertex::makeCanonicalPoint(objFeat, startPt); - std::string startVertTag = objFeat->addCosmeticVertex(cvPoint); - int startVertNumber = objFeat->add1CVToGV(startVertTag); - startName << "Vertex" << startVertNumber; - cvPoint = CosmeticVertex::makeCanonicalPoint(objFeat, endPt); - std::string endVertTag = objFeat->addCosmeticVertex(cvPoint); - int endVertNumber = objFeat->add1CVToGV(endVertTag); - endName << "Vertex" << endVertNumber; - TechDraw::DrawViewDimension* dim; - dim = _createLinDimension(this, objFeat, startName.str(), endName.str(), "Distance"); - TechDraw::pointPair pp = dim->getLinearPoints(); - Base::Vector3d mid = (pp.first() + pp.second()) / 2.0; - dim->X.setValue(mid.x); - dim->Y.setValue(-mid.y); - Base::Vector3d radVec1 = startPt - centerPt; - Base::Vector3d radVec2 = endPt - centerPt; - float alpha = acos((radVec1 * radVec2) / (radVec1.Length() * radVec2.Length())); - float arcLength = alpha * radius / scale; - dim->Arbitrary.setValue(true); - formatSpec << "◠ " << arcLength; - dim->FormatSpec.setValue(formatSpec.str()); + + ReferenceEntry ref(objFeat, selection[0].getSubNames()[0]); + + TechDraw::DrawViewDimension* dim = makeArcLengthDimension(ref); + + if (dim) { objFeat->refreshCEGeoms(); objFeat->requestPaint(); - getSelection().clearSelection(); + Gui::Command::commitCommand(); + } + else { + Gui::Command::abortCommand(); } - Gui::Command::commitCommand(); } bool CmdTechDrawExtensionCreateLengthArc::isActive() @@ -2329,6 +2362,51 @@ bool CmdTechDrawExtensionCustomizeFormat::isActive() return (havePage && haveView); } +DrawViewDimension* TechDrawGui::makeArcLengthDimension(const ReferenceEntry& ref) +{ + DrawViewDimension* dim = nullptr; + auto* dvp = static_cast(ref.getObject()); + + int geoId = DrawUtil::getIndexFromName(ref.getSubName()); + BaseGeomPtr geom = dvp->getGeomByIndex(geoId); + + // Find the edge length. + TechDraw::BaseGeomPtr edge = dvp->getEdge(ref.getSubName()); + if (!edge) { + return nullptr; + } + GProp_GProps edgeProps; + BRepGProp::LinearProperties(edge->getOCCEdge(), edgeProps); + double length = edgeProps.Mass() / dvp->getScale(); + + Base::Vector3d startPt = edge->getStartPoint(); + Base::Vector3d endPt = edge->getEndPoint(); + startPt.y = -startPt.y; + endPt.y = -endPt.y; + + std::stringstream startName, endName, formatSpec; + Base::Vector3d cvPoint = CosmeticVertex::makeCanonicalPoint(dvp, startPt); + std::string startVertTag = dvp->addCosmeticVertex(cvPoint); + int startVertNumber = dvp->add1CVToGV(startVertTag); + startName << "Vertex" << startVertNumber; + cvPoint = CosmeticVertex::makeCanonicalPoint(dvp, endPt); + std::string endVertTag = dvp->addCosmeticVertex(cvPoint); + int endVertNumber = dvp->add1CVToGV(endVertTag); + endName << "Vertex" << endVertNumber; + + dim = _createLinDimension(dvp, startName.str(), endName.str(), "Distance"); + TechDraw::pointPair pp = dim->getLinearPoints(); + Base::Vector3d mid = (pp.first() + pp.second()) / 2.0; + dim->X.setValue(mid.x); + dim->Y.setValue(-mid.y); + + dim->Arbitrary.setValue(true); + formatSpec << "◠ " << length; + dim->FormatSpec.setValue(formatSpec.str()); + + return dim; +} + namespace TechDrawGui { //=========================================================================== // internal helper routines @@ -2387,7 +2465,8 @@ namespace TechDrawGui { } return true; } - TechDraw::DrawViewDimension* _createLinDimension(Gui::Command* cmd, + + TechDraw::DrawViewDimension* _createLinDimension( TechDraw::DrawViewPart* objFeat, std::string startVertex, std::string endVertex, @@ -2396,21 +2475,21 @@ namespace TechDrawGui { { TechDraw::DrawPage* page = objFeat->findParentPage(); std::string PageName = page->getNameInDocument(); - TechDraw::DrawViewDimension* dim = nullptr; - std::string FeatName = cmd->getUniqueObjectName("Dimension"); + std::string FeatName = objFeat->getDocument()->getUniqueObjectName("Dimension"); std::vector objs; std::vector subs; objs.push_back(objFeat); objs.push_back(objFeat); subs.push_back(startVertex); subs.push_back(endVertex); - cmd->doCommand(cmd->Doc, "App.activeDocument().addObject('TechDraw::DrawViewDimension', '%s')", FeatName.c_str()); - cmd->doCommand(cmd->Doc, "App.activeDocument().%s.Type = '%s'", FeatName.c_str(), dimType.c_str()); - dim = dynamic_cast(cmd->getDocument()->getObject(FeatName.c_str())); - if (!dim) + Gui::Command::doCommand(Gui::Command::Doc, "App.activeDocument().addObject('TechDraw::DrawViewDimension', '%s')", FeatName.c_str()); + Gui::Command::doCommand(Gui::Command::Doc, "App.activeDocument().%s.Type = '%s'", FeatName.c_str(), dimType.c_str()); + auto dim = dynamic_cast(objFeat->getDocument()->getObject(FeatName.c_str())); + if (!dim){ throw Base::TypeError("CmdTechDrawExtensionCreateLinDimension - dim not found\n"); + } dim->References2D.setValues(objs, subs); - cmd->doCommand(cmd->Doc, "App.activeDocument().%s.addView(App.activeDocument().%s)", PageName.c_str(), FeatName.c_str()); + Gui::Command::doCommand(Gui::Command::Doc, "App.activeDocument().%s.addView(App.activeDocument().%s)", PageName.c_str(), FeatName.c_str()); // Touch the parent feature so the dimension in tree view appears as a child objFeat->touch(); diff --git a/src/Mod/TechDraw/Gui/CommandExtensionDims.h b/src/Mod/TechDraw/Gui/CommandExtensionDims.h new file mode 100644 index 0000000000..aff7159c71 --- /dev/null +++ b/src/Mod/TechDraw/Gui/CommandExtensionDims.h @@ -0,0 +1,41 @@ +/*************************************************************************** + * Copyright (c) 2021 edi * + * * + * 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 TECHDRAWGUI_CommandExtensionDims_H +#define TECHDRAWGUI_CommandExtensionDims_H + +#include + +namespace TechDraw +{ + class ReferenceEntry; + class DrawViewDimension; +} + +namespace TechDrawGui { + TechDraw::DrawViewDimension* makeArcLengthDimension(const TechDraw::ReferenceEntry& ref); + + std::vector makeObliqueChainDimension(std::vector refs); + std::vector makeObliqueCoordDimension(std::vector refs); +} + +#endif // TECHDRAWGUI_CommandExtensionDims_H diff --git a/src/Mod/TechDraw/Gui/DimensionValidators.cpp b/src/Mod/TechDraw/Gui/DimensionValidators.cpp index e991a938a6..0e2daadc29 100644 --- a/src/Mod/TechDraw/Gui/DimensionValidators.cpp +++ b/src/Mod/TechDraw/Gui/DimensionValidators.cpp @@ -116,7 +116,7 @@ DimensionGeometryType TechDraw::validateDimSelection( StringVector subNames; TechDraw::DrawViewPart* dvpSave(nullptr); for (auto& ref : references) { - TechDraw::DrawViewPart* dvp = dynamic_cast(ref.getObject()); + auto* dvp = dynamic_cast(ref.getObject()); if (dvp) { dvpSave = dvp; if (!ref.getSubName().empty()) { @@ -295,6 +295,10 @@ DimensionGeometryType TechDraw::getGeometryConfiguration(ReferenceVector valid2d if (config > isInvalid) { return config; } + config = isValidSingleFace(valid2dReferences.front()); + if (config > isInvalid) { + return config; + } // no valid configuration found return isInvalid; @@ -336,6 +340,10 @@ DimensionGeometryType TechDraw::getGeometryConfiguration3d(DrawViewPart* dvp, if (config > isInvalid) { return config; } + config = isValidSingleFace3d(dvp, valid3dReferences.front()); + if (config > isInvalid) { + return config; + } config = isValidHybrid3d(dvp, valid3dReferences); if (config > isInvalid) { return config; @@ -461,6 +469,47 @@ DimensionGeometryType TechDraw::isValidSingleEdge3d(DrawViewPart* dvp, Reference return isInvalid; } +//! verify that Selection contains a valid Geometry for a single Edge Dimension +DimensionGeometryType TechDraw::isValidSingleFace(ReferenceEntry ref) +{ + auto objFeat(dynamic_cast(ref.getObject())); + if (!objFeat) { + return isInvalid; + } + + //the Name starts with "Edge" + std::string geomName = DrawUtil::getGeomTypeFromName(ref.getSubName()); + if (geomName != "Face") { + return isInvalid; + } + + auto geom = objFeat->getFace(ref.getSubName()); + if (!geom) { + return isInvalid; + } + + return isFace; +} + +//! verify that Selection contains a valid Geometry for a single Edge Dimension +DimensionGeometryType TechDraw::isValidSingleFace3d(DrawViewPart* dvp, ReferenceEntry ref) +{ + (void)dvp; + //the Name starts with "Edge" + std::string geomName = DrawUtil::getGeomTypeFromName(ref.getSubName()); + if (geomName != "Face") { + return isInvalid; + } + + TopoDS_Shape refShape = ref.getGeometry(); + if (refShape.IsNull() || refShape.ShapeType() != TopAbs_FACE) { + Base::Console().Warning("Geometry for reference is not a face.\n"); + return isInvalid; + } + + return isFace; +} + //! verify that the edge references can make a dimension. Currently only extent //! dimensions support more than 2 edges DimensionGeometryType TechDraw::isValidMultiEdge(ReferenceVector refs) diff --git a/src/Mod/TechDraw/Gui/DimensionValidators.h b/src/Mod/TechDraw/Gui/DimensionValidators.h index 7f371670bd..339f734556 100644 --- a/src/Mod/TechDraw/Gui/DimensionValidators.h +++ b/src/Mod/TechDraw/Gui/DimensionValidators.h @@ -60,6 +60,7 @@ enum DimensionGeometryEnum { isMultiEdge, isZLimited, isHybrid, + isFace, isViewReference //never needs to be specified in the acceptable list }; @@ -89,11 +90,13 @@ bool checkGeometryOccurrences(StringVector subNames, GeomCountMap keyedMinimumCo DimensionGeometryType isValidVertexes(ReferenceVector refs); DimensionGeometryType isValidMultiEdge(ReferenceVector refs); DimensionGeometryType isValidSingleEdge(ReferenceEntry ref); +DimensionGeometryType isValidSingleFace(ReferenceEntry ref); DimensionGeometryType isValidHybrid(ReferenceVector refs); DimensionGeometryType isValidVertexes3d(DrawViewPart* dvp, ReferenceVector refs); DimensionGeometryType isValidMultiEdge3d(DrawViewPart* dvp, ReferenceVector refs); DimensionGeometryType isValidSingleEdge3d(DrawViewPart* dvp, ReferenceEntry ref); +DimensionGeometryType isValidSingleFace3d(DrawViewPart* dvp, ReferenceEntry ref); DimensionGeometryType isValidHybrid3d(DrawViewPart* dvp, ReferenceVector refs); long int mapGeometryTypeToDimType(long int dimType, DimensionGeometryType geometry2d, diff --git a/src/Mod/TechDraw/Gui/DlgPrefsTechDrawDimensions.ui b/src/Mod/TechDraw/Gui/DlgPrefsTechDrawDimensions.ui index eb4870f3f4..a1819701b6 100644 --- a/src/Mod/TechDraw/Gui/DlgPrefsTechDrawDimensions.ui +++ b/src/Mod/TechDraw/Gui/DlgPrefsTechDrawDimensions.ui @@ -524,9 +524,9 @@ Multiplier of 'Font Size' - Controls the size of gap between dimension point and start of extension line for ISO dimensions. -Value * linewidth is the gap. -Normally, no gap is used. If using a gap, the recommended value 8. + Controls the size of gap between dimension point and start of extension line for ISO dimensions. + Value * linewidth is the gap. + Normally, no gap is used. If using a gap, the recommended value 8. Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -545,8 +545,8 @@ Normally, no gap is used. If using a gap, the recommended value 8. - Controls the size of gap between dimension point and start of extension line for ASME dimensions. Value * linewidth is the gap. -Normally, no gap is used. If a gap is used, the recommended value is 6. + Controls the size of gap between dimension point and start of extension line for ASME dimensions. Value * linewidth is the gap. + Normally, no gap is used. If a gap is used, the recommended value is 6. Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -618,6 +618,56 @@ Normally, no gap is used. If a gap is used, the recommended value is 6. + + + + + 0 + 0 + + + + Tools + + + + + + Dimensioning tools: + + + + + + + Select the type of dimensioning tools for your toolbar: +'Single tool': A single tool for all dimensioning in the toolbar: Distance, Distance X / Y, Angle, Radius. (Others in dropdown) +'Separated tools': Individual tools for each dimensioning tool. +'Both': You will have both the 'Dimension' tool and the separated tools. +This setting is only for the toolbar. Whichever you choose, all tools are always available in the menu and through shortcuts. + + + + + + + Dimension tool diameter/radius mode: + + + + + + + While using the Dimension tool you may choose how to handle circles and arcs: +'Auto': The tool will apply radius to arcs and diameter to circles. +'Diameter': The tool will apply diameter to all. +'Radius': The tool will apply radius to all. + + + + + + diff --git a/src/Mod/TechDraw/Gui/DlgPrefsTechDrawDimensionsImp.cpp b/src/Mod/TechDraw/Gui/DlgPrefsTechDrawDimensionsImp.cpp index 0b4b276e3e..c7ef7746ce 100644 --- a/src/Mod/TechDraw/Gui/DlgPrefsTechDrawDimensionsImp.cpp +++ b/src/Mod/TechDraw/Gui/DlgPrefsTechDrawDimensionsImp.cpp @@ -25,6 +25,7 @@ #include "PreCompiled.h" #include +#include #include "DlgPrefsTechDrawDimensionsImp.h" #include "ui_DlgPrefsTechDrawDimensions.h" @@ -67,6 +68,61 @@ void DlgPrefsTechDrawDimensionsImp::saveSettings() ui->pdsbGapISO->onSave(); ui->pdsbGapASME->onSave(); ui->pdsbLineSpacingFactorISO->onSave(); + + enum + { + DimensionSingleTool, + DimensionSeparateTools, + DimensionBoth + }; + + // Dimensioning constraints mode + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath( + "User parameter:BaseApp/Preferences/Mod/TechDraw/dimensioning"); + bool singleTool = true; + bool SeparatedTools = false; + int index = ui->dimensioningMode->currentIndex(); + switch (index) { + case DimensionSeparateTools: + singleTool = false; + SeparatedTools = true; + break; + case DimensionBoth: + singleTool = true; + SeparatedTools = true; + break; + } + hGrp->SetBool("SingleDimensioningTool", singleTool); + hGrp->SetBool("SeparatedDimensioningTools", SeparatedTools); + + ui->radiusDiameterMode->setEnabled(index != 1); + + enum + { + DimensionAutoRadiusDiam, + DimensionDiameter, + DimensionRadius + }; + + bool Diameter = true; + bool Radius = true; + index = ui->radiusDiameterMode->currentIndex(); + switch (index) { + case DimensionDiameter: + Diameter = true; + Radius = false; + break; + case DimensionRadius: + Diameter = false; + Radius = true; + break; + } + hGrp->SetBool("DimensioningDiameter", Diameter); + hGrp->SetBool("DimensioningRadius", Radius); + + if (property("dimensioningMode").toInt() != ui->dimensioningMode->currentIndex()) { + requireRestart(); + } } void DlgPrefsTechDrawDimensionsImp::loadSettings() @@ -101,6 +157,42 @@ void DlgPrefsTechDrawDimensionsImp::loadSettings() ui->pdsbGapASME->onRestore(); ui->pdsbLineSpacingFactorISO->onRestore(); + + // Dimensioning constraints mode + ui->dimensioningMode->clear(); + ui->dimensioningMode->addItem(tr("Single tool")); + ui->dimensioningMode->addItem(tr("Separated tools")); + ui->dimensioningMode->addItem(tr("Both")); + + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath( + "User parameter:BaseApp/Preferences/Mod/TechDraw/dimensioning"); + bool singleTool = hGrp->GetBool("SingleDimensioningTool", true); + bool SeparatedTools = hGrp->GetBool("SeparatedDimensioningTools", true); + int index = SeparatedTools ? (singleTool ? 2 : 1) : 0; + ui->dimensioningMode->setCurrentIndex(index); + setProperty("dimensioningMode", index); + connect(ui->dimensioningMode, + QOverload::of(&QComboBox::currentIndexChanged), + this, + &DlgPrefsTechDrawDimensionsImp::dimensioningModeChanged); + + ui->radiusDiameterMode->setEnabled(index != 1); + + // Dimensioning constraints mode + ui->radiusDiameterMode->clear(); + ui->radiusDiameterMode->addItem(tr("Auto")); + ui->radiusDiameterMode->addItem(tr("Diameter")); + ui->radiusDiameterMode->addItem(tr("Radius")); + + bool Diameter = hGrp->GetBool("DimensioningDiameter", true); + bool Radius = hGrp->GetBool("DimensioningRadius", true); + index = Diameter ? (Radius ? 0 : 1) : 2; + ui->radiusDiameterMode->setCurrentIndex(index); +} + +void DlgPrefsTechDrawDimensionsImp::dimensioningModeChanged(int index) +{ + ui->radiusDiameterMode->setEnabled(index != 1); } /** diff --git a/src/Mod/TechDraw/Gui/DlgPrefsTechDrawDimensionsImp.h b/src/Mod/TechDraw/Gui/DlgPrefsTechDrawDimensionsImp.h index 44b66bf16e..c06dcb0d6c 100644 --- a/src/Mod/TechDraw/Gui/DlgPrefsTechDrawDimensionsImp.h +++ b/src/Mod/TechDraw/Gui/DlgPrefsTechDrawDimensionsImp.h @@ -45,6 +45,7 @@ protected: void saveSettings() override; void loadSettings() override; void changeEvent(QEvent *e) override; + void dimensioningModeChanged(int index); int prefArrowStyle() const; diff --git a/src/Mod/TechDraw/Gui/MDIViewPage.cpp b/src/Mod/TechDraw/Gui/MDIViewPage.cpp index 3af07c9e06..f31241c2f7 100644 --- a/src/Mod/TechDraw/Gui/MDIViewPage.cpp +++ b/src/Mod/TechDraw/Gui/MDIViewPage.cpp @@ -110,6 +110,7 @@ MDIViewPage::MDIViewPage(ViewProviderPage* pageVp, Gui::Document* doc, QWidget* connect(m_printAllAction, &QAction::triggered, this, qOverload<>(&MDIViewPage::printAllPages)); isSelectionBlocked = false; + isContextualMenuEnabled = true; QString tabText = QString::fromUtf8(pageVp->getDrawPage()->getNameInDocument()); tabText += QString::fromUtf8("[*]"); @@ -461,14 +462,16 @@ PyObject* MDIViewPage::getPyObject() void MDIViewPage::contextMenuEvent(QContextMenuEvent* event) { // Base::Console().Message("MDIVP::contextMenuEvent() - reason: %d\n", event->reason()); - QMenu menu; - menu.addAction(m_toggleFrameAction); - menu.addAction(m_toggleKeepUpdatedAction); - menu.addAction(m_exportSVGAction); - menu.addAction(m_exportDXFAction); - menu.addAction(m_exportPDFAction); - menu.addAction(m_printAllAction); - menu.exec(event->globalPos()); + if (isContextualMenuEnabled) { + QMenu menu; + menu.addAction(m_toggleFrameAction); + menu.addAction(m_toggleKeepUpdatedAction); + menu.addAction(m_exportSVGAction); + menu.addAction(m_exportDXFAction); + menu.addAction(m_exportPDFAction); + menu.addAction(m_printAllAction); + menu.exec(event->globalPos()); + } } void MDIViewPage::toggleFrame() { m_vpPage->toggleFrameState(); } @@ -755,13 +758,27 @@ void MDIViewPage::sceneSelectionChanged() std::vector treeSel = Gui::Selection().getSelectionEx(); QList sceneSel = m_qgSceneSelected; - //check if really need to change selection - bool sameSel = compareSelections(treeSel, sceneSel); - if (sameSel) { - return; + + bool saveBlock = blockSelection(true);// block selectionChanged signal from Tree/Observer + blockSceneSelection(true); + + if (sceneSel.empty()) { + if (!treeSel.empty()) { + Gui::Selection().clearSelection(); + } + } + else { + for (auto& sel : treeSel) { + removeSelFromTreeSel(sceneSel, sel); + } + + for (auto* scene : sceneSel) { + addSceneToTreeSel(scene, treeSel); + } } - setTreeToSceneSelect(); + blockSceneSelection(false); + blockSelection(saveBlock); } //Note: Qt says: "no guarantee of selection order"!!! @@ -771,149 +788,48 @@ void MDIViewPage::setTreeToSceneSelect() bool saveBlock = blockSelection(true);// block selectionChanged signal from Tree/Observer blockSceneSelection(true); Gui::Selection().clearSelection(); - QList sceneSel = m_qgSceneSelected; - for (QList::iterator it = sceneSel.begin(); it != sceneSel.end(); ++it) { - QGIView* itemView = dynamic_cast(*it); + + for (auto* scene : m_qgSceneSelected) { + auto* itemView = dynamic_cast(scene); if (!itemView) { - QGIEdge* edge = dynamic_cast(*it); - if (edge) { - QGraphicsItem* parent = edge->parentItem(); - if (!parent) { - continue; - } - - QGIView* viewItem = dynamic_cast(parent); - if (!viewItem) { - continue; - } - - TechDraw::DrawView* viewObj = viewItem->getViewObject(); - - std::stringstream ss; - ss << "Edge" << edge->getProjIndex(); - //bool accepted = - static_cast(Gui::Selection().addSelection(viewObj->getDocument()->getName(), - viewObj->getNameInDocument(), - ss.str().c_str())); - showStatusMsg(viewObj->getDocument()->getName(), viewObj->getNameInDocument(), - ss.str().c_str()); + auto* parent = dynamic_cast(scene->parentItem()); + if (!parent) { + return; + } + TechDraw::DrawView* viewObj = parent->getViewObject(); + if (!viewObj) { continue; } + const char* doc_name = viewObj->getDocument()->getName(); + const char* obj_name = viewObj->getNameInDocument(); - QGIVertex* vert = dynamic_cast(*it); - if (vert) { - QGraphicsItem* parent = vert->parentItem(); - if (!parent) { - continue; - } + auto* edge = dynamic_cast(scene); + auto* vert = dynamic_cast(scene); + auto* face = dynamic_cast(scene); + if (edge || vert || face) { + const char* ssn = getSceneSubName(scene).c_str(); - QGIView* viewItem = dynamic_cast(parent); - if (!viewItem) { - continue; - } - - TechDraw::DrawView* viewObj = viewItem->getViewObject(); - - std::stringstream ss; - ss << "Vertex" << vert->getProjIndex(); - //bool accepted = - static_cast(Gui::Selection().addSelection(viewObj->getDocument()->getName(), - viewObj->getNameInDocument(), - ss.str().c_str())); - showStatusMsg(viewObj->getDocument()->getName(), viewObj->getNameInDocument(), - ss.str().c_str()); - continue; + Gui::Selection().addSelection(doc_name, obj_name, ssn); + showStatusMsg(doc_name, obj_name, ssn); + return; } - - QGIFace* face = dynamic_cast(*it); - if (face) { - QGraphicsItem* parent = face->parentItem(); - if (!parent) { - continue; - } - - QGIView* viewItem = dynamic_cast(parent); - if (!viewItem) { - continue; - } - - TechDraw::DrawView* viewObj = viewItem->getViewObject(); - - std::stringstream ss; - ss << "Face" << face->getProjIndex(); - //bool accepted = - static_cast(Gui::Selection().addSelection(viewObj->getDocument()->getName(), - viewObj->getNameInDocument(), - ss.str().c_str())); - showStatusMsg(viewObj->getDocument()->getName(), viewObj->getNameInDocument(), - ss.str().c_str()); - continue; - } - - QGIDatumLabel* dimLabel = dynamic_cast(*it); - if (dimLabel) { - QGraphicsItem* dimParent = dimLabel->QGraphicsItem::parentItem(); - if (!dimParent) { - continue; - } - - QGIView* dimItem = dynamic_cast(dimParent); - - if (!dimItem) { - continue; - } - - TechDraw::DrawView* dimObj = dimItem->getViewObject(); - if (!dimObj) { - continue; - } - const char* name = dimObj->getNameInDocument(); - if (!name) {//can happen during undo/redo if Dim is selected??? + else if (dynamic_cast(scene) || dynamic_cast(scene)) { + if (!obj_name) {//can happen during undo/redo if Dim is selected??? //Base::Console().Log("INFO - MDIVP::sceneSelectionChanged - dimObj name is null!\n"); continue; } - //bool accepted = - static_cast(Gui::Selection().addSelection(dimObj->getDocument()->getName(), - dimObj->getNameInDocument())); - } - - QGMText* mText = dynamic_cast(*it); - if (mText) { - QGraphicsItem* textParent = mText->QGraphicsItem::parentItem(); - if (!textParent) { - continue; - } - - QGIView* parent = dynamic_cast(textParent); - - if (!parent) { - continue; - } - - TechDraw::DrawView* parentFeat = parent->getViewObject(); - if (!parentFeat) { - continue; - } - const char* name = parentFeat->getNameInDocument(); - if (!name) {//can happen during undo/redo if Dim is selected??? - continue; - } - - //bool accepted = - static_cast(Gui::Selection().addSelection( - parentFeat->getDocument()->getName(), parentFeat->getNameInDocument())); + Gui::Selection().addSelection(doc_name, obj_name); } } else { - TechDraw::DrawView* viewObj = itemView->getViewObject(); if (viewObj && !viewObj->isRemoving()) { - std::string doc_name = viewObj->getDocument()->getName(); - std::string obj_name = viewObj->getNameInDocument(); + const char* doc_name = viewObj->getDocument()->getName(); + const char* obj_name = viewObj->getNameInDocument(); - Gui::Selection().addSelection(doc_name.c_str(), obj_name.c_str()); - showStatusMsg(doc_name.c_str(), obj_name.c_str(), ""); + Gui::Selection().addSelection(doc_name, obj_name); + showStatusMsg(doc_name, obj_name, ""); } } } @@ -922,6 +838,138 @@ void MDIViewPage::setTreeToSceneSelect() blockSelection(saveBlock); } +std::string MDIViewPage::getSceneSubName(QGraphicsItem* scene) +{ + auto* edge = dynamic_cast(scene); + auto* vert = dynamic_cast(scene); + auto* face = dynamic_cast(scene); + if (edge || vert || face) { + auto* viewItem = dynamic_cast(scene->parentItem()); + if (viewItem) { + TechDraw::DrawView* viewObj = viewItem->getViewObject(); + + std::stringstream ss; + if (edge) { ss << "Edge" << edge->getProjIndex(); } + else if (vert) { ss << "Vertex" << vert->getProjIndex(); } + else { ss << "Face" << face->getProjIndex(); } + + return ss.str(); + } + } + return ""; +} + +// adds scene to core selection if it's not in already. +void MDIViewPage::addSceneToTreeSel(QGraphicsItem* sn, std::vector treeSel) +{ + auto* itemView = dynamic_cast(sn); + if (!itemView) { + auto* parent = dynamic_cast(sn->parentItem()); + if (!parent) { + return; + } + TechDraw::DrawView* viewObj = parent->getViewObject(); + if (!viewObj) { + return; + } + + const char* doc_name = viewObj->getDocument()->getName(); + const char* obj_name = viewObj->getNameInDocument(); + std::string sub_name; + + if (dynamic_cast(sn) || dynamic_cast(sn) || dynamic_cast(sn)) { + sub_name = getSceneSubName(sn); + } + + else if (dynamic_cast(sn) || dynamic_cast(sn)) { + if (!obj_name) {//can happen during undo/redo if Dim is selected??? + return; + } + } + else { // are there other cases? + return; + } + + if (!Gui::Selection().isSelected(viewObj, sub_name.c_str())) { + Gui::Selection().addSelection(doc_name, obj_name, sub_name.c_str()); + showStatusMsg(doc_name, obj_name, sub_name.c_str()); + } + } + else { + TechDraw::DrawView* viewObj = itemView->getViewObject(); + if (viewObj && !viewObj->isRemoving()) { + const char* doc_name = viewObj->getDocument()->getName(); + const char* obj_name = viewObj->getNameInDocument(); + + if (!Gui::Selection().isSelected(viewObj)) { + Gui::Selection().addSelection(doc_name, obj_name); + showStatusMsg(doc_name, obj_name, ""); + } + } + } +} + +// remove core selection if scene is not selected anymore +void MDIViewPage::removeSelFromTreeSel(QList sceneSel, Gui::SelectionObject& sel) +{ + std::string selDocName = sel.getDocName(); + App::DocumentObject* selObj = sel.getObject(); + + for (auto& sub : sel.getSubNames()) { + bool found = false; + for (auto& sn : sceneSel) { + auto* itemView = dynamic_cast(sn); + if (!itemView) { + auto* parent = dynamic_cast(sn->parentItem()); + if (!parent) { + continue; + } + TechDraw::DrawView* viewObj = parent->getViewObject(); + if (!viewObj) { + continue; + } + + const char* doc_name = viewObj->getDocument()->getName(); + const char* obj_name = viewObj->getNameInDocument(); + std::string sub_name; + + if (dynamic_cast(sn) || dynamic_cast(sn) || dynamic_cast(sn)) { + sub_name = getSceneSubName(sn); + } + + else if (dynamic_cast(sn) || dynamic_cast(sn)) { + if (!obj_name) {//can happen during undo/redo if Dim is selected??? + continue; + } + } + else { // are there other cases? + continue; + } + + if (selDocName == doc_name && selObj == viewObj && sub == sub_name) { + found = true; + break; + } + } + else { + TechDraw::DrawView* viewObj = itemView->getViewObject(); + if (viewObj && !viewObj->isRemoving()) { + const char* doc_name = viewObj->getDocument()->getName(); + const char* obj_name = viewObj->getNameInDocument(); + + if (selDocName == doc_name && selObj == viewObj) { + found = true; + break; + } + } + } + } + if (!found) { + Gui::Selection().rmvSelection(sel.getDocName(), sel.getObject()->getNameInDocument(), sub.c_str()); + } + } +} + bool MDIViewPage::compareSelections(std::vector treeSel, QList sceneSel) { @@ -1020,6 +1068,17 @@ void MDIViewPage::showStatusMsg(const char* string1, const char* string2, const } } +void MDIViewPage::setDimensionsSelectability(bool val) +{ + for (auto scene : m_scene->items()) { + auto* dl = dynamic_cast(scene); + if (dl) { + dl->setSelectability(val); + } + } +} + + // ---------------------------------------------------------------------------- void MDIViewPagePy::init_type() diff --git a/src/Mod/TechDraw/Gui/MDIViewPage.h b/src/Mod/TechDraw/Gui/MDIViewPage.h index 474247305f..175dcf98b6 100644 --- a/src/Mod/TechDraw/Gui/MDIViewPage.h +++ b/src/Mod/TechDraw/Gui/MDIViewPage.h @@ -109,6 +109,9 @@ public: void setScene(QGSPage* scene, QGVPage* view); void fixSceneDependencies(); + void setDimensionsSelectability(bool val); + void enableContextualMenu(bool val) { isContextualMenuEnabled = val; } + public Q_SLOTS: void viewAll() override; void saveSVG(); @@ -127,6 +130,9 @@ protected: void onDeleteObject(const App::DocumentObject& obj); bool compareSelections(std::vector treeSel, QList sceneSel); + void addSceneToTreeSel(QGraphicsItem* scene, std::vector treeSel); + void removeSelFromTreeSel(QList sceneSel, Gui::SelectionObject& sel); + std::string getSceneSubName(QGraphicsItem* scene); void setTreeToSceneSelect(); void sceneSelectionManager(); @@ -144,6 +150,7 @@ private: std::string m_objectName; std::string m_documentName; bool isSelectionBlocked; + bool isContextualMenuEnabled; QPointer m_scene; QString m_currentPath; diff --git a/src/Mod/TechDraw/Gui/PreferencesGui.cpp b/src/Mod/TechDraw/Gui/PreferencesGui.cpp index c4cfbcd26a..24059b51d0 100644 --- a/src/Mod/TechDraw/Gui/PreferencesGui.cpp +++ b/src/Mod/TechDraw/Gui/PreferencesGui.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -214,7 +215,8 @@ bool PreferencesGui::showGrid() bool PreferencesGui::multiSelection() { - return Preferences::getPreferenceGroup("General")->GetBool("multiSelection", false); + bool greedy = Gui::Selection().getSelectionStyle() == Gui::SelectionSingleton::SelectionStyle::GreedySelection; + return greedy || Preferences::getPreferenceGroup("General")->GetBool("multiSelection", false); } App::Color PreferencesGui::pageColor() diff --git a/src/Mod/TechDraw/Gui/QGIViewDimension.cpp b/src/Mod/TechDraw/Gui/QGIViewDimension.cpp index 6a3e7d139d..d39918d11e 100644 --- a/src/Mod/TechDraw/Gui/QGIViewDimension.cpp +++ b/src/Mod/TechDraw/Gui/QGIViewDimension.cpp @@ -93,8 +93,7 @@ QGIDatumLabel::QGIDatumLabel() : m_dragState(NoDrag) setCacheMode(QGraphicsItem::NoCache); setFlag(ItemSendsGeometryChanges, true); setFlag(ItemIsMovable, true); - setFlag(ItemIsSelectable, true); - setAcceptHoverEvents(true); + setSelectability(true); setFiltersChildEvents(true); m_dimText = new QGCustomText(); @@ -445,6 +444,13 @@ void QGIDatumLabel::setColor(QColor color) m_unitText->setColor(m_colNormal); } +void QGIDatumLabel::setSelectability(bool val) +{ + setFlag(ItemIsSelectable, val); + setAcceptHoverEvents(val); + setAcceptedMouseButtons(val ? Qt::AllButtons : Qt::NoButton); +} + //************************************************************** QGIViewDimension::QGIViewDimension() : dvDimension(nullptr), hasHover(false), m_lineWidth(0.0) { @@ -717,9 +723,9 @@ void QGIViewDimension::draw() return; } - TechDraw::DrawViewDimension* dim = dynamic_cast(getViewObject()); + auto* dim = dynamic_cast(getViewObject()); if (!dim ||//nothing to draw, don't try - !dim->isDerivedFrom(TechDraw::DrawViewDimension::getClassTypeId()) + !dim->isDerivedFrom() || !dim->has2DReferences()) { datumLabel->hide(); hide(); @@ -767,6 +773,9 @@ void QGIViewDimension::draw() else if (strcmp(dimType, "Angle") == 0 || strcmp(dimType, "Angle3Pt") == 0) { drawAngle(dim, vp); } + else if (strcmp(dimType, "Area") == 0) { + drawArea(dim, vp); + } else { Base::Console().Error("QGIVD::draw - this DimensionType is unknown: %s\n", dimType); } @@ -1255,7 +1264,7 @@ void QGIViewDimension::resetArrows() const } void QGIViewDimension::drawArrows(int count, const Base::Vector2d positions[], double angles[], - bool flipped) const + bool flipped, bool forcePoint) const { const int arrowCount = 2; QGIArrow* arrows[arrowCount] = {aHead1, aHead2}; @@ -1275,7 +1284,8 @@ void QGIViewDimension::drawArrows(int count, const Base::Vector2d positions[], d continue; } - arrow->setStyle(QGIArrow::getPrefArrowStyle()); + // some dimensions must use point ends (area). The point style is 3. + arrow->setStyle(forcePoint ? 3 : QGIArrow::getPrefArrowStyle()); auto vp = static_cast(getViewProvider(getViewObject())); auto arrowSize = vp->Arrowsize.getValue(); arrow->setSize(arrowSize); @@ -1403,7 +1413,7 @@ void QGIViewDimension::drawDimensionLine(QPainterPath& painterPath, const Base::Vector2d& targetPoint, double lineAngle, double startPosition, double jointPosition, const Base::BoundBox2d& labelRectangle, int arrowCount, - int standardStyle, bool flipArrows) const + int standardStyle, bool flipArrows, bool forcePointStyle) const { // Keep the convention start position <= 0 jointPosition *= normalizeStartPosition(startPosition, lineAngle); @@ -1423,7 +1433,7 @@ void QGIViewDimension::drawDimensionLine(QPainterPath& painterPath, arrowAngles[0] = lineAngle; arrowAngles[1] = lineAngle + M_PI; - drawArrows(arrowCount, arrowPositions, arrowAngles, flipArrows); + drawArrows(arrowCount, arrowPositions, arrowAngles, flipArrows, forcePointStyle); } void QGIViewDimension::drawDimensionArc(QPainterPath& painterPath, const Base::Vector2d& arcCenter, @@ -2085,6 +2095,75 @@ void QGIViewDimension::drawRadiusExecutive(const Base::Vector2d& centerPoint, dimLines->setPath(radiusPath); } +void QGIViewDimension::drawAreaExecutive(const Base::Vector2d& centerPoint, double area, + const Base::BoundBox2d& labelRectangle, + double centerOverhang, int standardStyle, + int renderExtent, bool flipArrow) const +{ + QPainterPath areaPath; + + Base::Vector2d labelCenter(labelRectangle.GetCenter()); + double labelAngle = 0.0; + bool forcePointStyle = true; + + if (standardStyle == ViewProviderDimension::STD_STYLE_ISO_REFERENCING + || standardStyle == ViewProviderDimension::STD_STYLE_ASME_REFERENCING) { + // The dimensional value text must stay horizontal + + bool left = labelCenter.x < centerPoint.x; + + Base::Vector2d jointDirection; + if (standardStyle == ViewProviderDimension::STD_STYLE_ISO_REFERENCING) { + jointDirection = getIsoRefJointPoint(labelRectangle, left) - centerPoint; + } + else { + jointDirection = getAsmeRefJointPoint(labelRectangle, left) - centerPoint; + } + + double lineAngle = jointDirection.Angle(); + double jointPositions = jointDirection.Length(); + + drawDimensionLine(areaPath, centerPoint, lineAngle, 0.0, jointPositions, labelRectangle, 1, standardStyle, flipArrow, forcePointStyle); + + Base::Vector2d outsetPoint(standardStyle == ViewProviderDimension::STD_STYLE_ISO_REFERENCING + ? getIsoRefOutsetPoint(labelRectangle, left) + : getAsmeRefOutsetPoint(labelRectangle, left)); + + areaPath.moveTo(toQtGui(outsetPoint)); + areaPath.lineTo(toQtGui(centerPoint + jointDirection)); + } + else if (standardStyle == ViewProviderDimension::STD_STYLE_ISO_ORIENTED) { + // We may rotate the label so no reference line is needed + double lineAngle; + double devAngle = computeLineAndLabelAngles(centerPoint, labelCenter, + labelRectangle.Height() * 0.5 + getIsoDimensionLineSpacing(), + lineAngle, labelAngle); + + lineAngle = lineAngle - M_PI; + double labelPosition = -cos(devAngle) * ((labelCenter - centerPoint).Length()); + + drawDimensionLine(areaPath, centerPoint, lineAngle, 0.0, labelPosition, labelRectangle, 1, standardStyle, flipArrow, forcePointStyle); + } + else if (standardStyle == ViewProviderDimension::STD_STYLE_ASME_INLINED) { + // Text must remain horizontal, but it may split the leader line + Base::Vector2d labelDirection(labelCenter - centerPoint); + double lineAngle = labelDirection.Angle(); + double labelPosition = labelDirection.Length(); + + drawDimensionLine(areaPath, centerPoint, lineAngle, 0.0, labelPosition, labelRectangle, 1, standardStyle, flipArrow, forcePointStyle); + } + else { + Base::Console().Error( + "QGIVD::drawRadiusExecutive - this Standard&Style is not supported: %d\n", + standardStyle); + } + + datumLabel->setTransformOriginPoint(datumLabel->boundingRect().center()); + datumLabel->setRotation(toQtDeg(labelAngle)); + + dimLines->setPath(areaPath); +} + void QGIViewDimension::drawDistance(TechDraw::DrawViewDimension* dimension, ViewProviderDimension* viewProvider) const { @@ -2510,6 +2589,21 @@ void QGIViewDimension::drawAngle(TechDraw::DrawViewDimension* dimension, dimLines->setPath(anglePath); } +void QGIViewDimension::drawArea(TechDraw::DrawViewDimension* dimension, + ViewProviderDimension* viewProvider) const +{ + Base::BoundBox2d labelRectangle( + fromQtGui(mapRectFromItem(datumLabel, datumLabel->boundingRect()))); + areaPoint areaPoint = dimension->getAreaPoint(); + + double endAngle; + double startRotation; + + drawAreaExecutive( + fromQtApp(areaPoint.center), areaPoint.area, labelRectangle, 0.0, viewProvider->StandardAndStyle.getValue(), + viewProvider->RenderingExtent.getValue(), viewProvider->FlipArrowheads.getValue()); +} + QColor QGIViewDimension::prefNormalColor() { setNormalColor(PreferencesGui::getAccessibleQColor(PreferencesGui::dimQColor())); diff --git a/src/Mod/TechDraw/Gui/QGIViewDimension.h b/src/Mod/TechDraw/Gui/QGIViewDimension.h index 891260abb8..f74a0cdd57 100644 --- a/src/Mod/TechDraw/Gui/QGIViewDimension.h +++ b/src/Mod/TechDraw/Gui/QGIViewDimension.h @@ -87,6 +87,7 @@ public: void setPrettyPre(); void setPrettyNormal(); void setColor(QColor color); + void setSelectability(bool val); QGCustomText* getDimText() { return m_dimText; } void setDimText(QGCustomText* newText) { m_dimText = newText; } @@ -232,7 +233,7 @@ protected: void draw() override; void resetArrows() const; - void drawArrows(int count, const Base::Vector2d positions[], double angles[], bool flipped) const; + void drawArrows(int count, const Base::Vector2d positions[], double angles[], bool flipped, bool forcePoint = false) const; void drawSingleLine(QPainterPath &painterPath, const Base::Vector2d &lineOrigin, double lineAngle, double startPosition, double endPosition) const; @@ -245,7 +246,7 @@ protected: void drawDimensionLine(QPainterPath &painterPath, const Base::Vector2d &targetPoint, double lineAngle, double startPosition, double jointPosition, const Base::BoundBox2d &labelRectangle, - int arrowCount, int standardStyle, bool flipArrows) const; + int arrowCount, int standardStyle, bool flipArrows, bool forcePointStyle = false) const; void drawDimensionArc(QPainterPath &painterPath, const Base::Vector2d &arcCenter, double arcRadius, double endAngle, double startRotation, double jointAngle, const Base::BoundBox2d &labelRectangle, int arrowCount, @@ -261,10 +262,14 @@ protected: double endAngle, double startRotation, const Base::BoundBox2d &labelRectangle, double centerOverhang, int standardStyle, int renderExtent, bool flipArrow) const; + void drawAreaExecutive(const Base::Vector2d ¢erPoint, double area, const Base::BoundBox2d &labelRectangle, + double centerOverhang, int standardStyle, int renderExtent, bool flipArrow) const; + void drawDistance(TechDraw::DrawViewDimension *dimension, ViewProviderDimension *viewProvider) const; void drawRadius(TechDraw::DrawViewDimension *dimension, ViewProviderDimension *viewProvider) const; void drawDiameter(TechDraw::DrawViewDimension *dimension, ViewProviderDimension *viewProvider) const; void drawAngle(TechDraw::DrawViewDimension *dimension, ViewProviderDimension *viewProvider) const; + void drawArea(TechDraw::DrawViewDimension *dimension, ViewProviderDimension *viewProvider) const; QVariant itemChange( GraphicsItemChange change, const QVariant &value ) override; diff --git a/src/Mod/TechDraw/Gui/QGVPage.cpp b/src/Mod/TechDraw/Gui/QGVPage.cpp index 3869bea238..de73224363 100644 --- a/src/Mod/TechDraw/Gui/QGVPage.cpp +++ b/src/Mod/TechDraw/Gui/QGVPage.cpp @@ -63,6 +63,7 @@ #include "QGVNavStyleTouchpad.h" #include "QGVPage.h" #include "Rez.h" +#include "TechDrawHandler.h" #include "ViewProviderPage.h" @@ -167,7 +168,7 @@ public: QGVPage::QGVPage(ViewProviderPage* vpPage, QGSPage* scenePage, QWidget* parent) : QGraphicsView(parent), m_renderer(Native), drawBkg(true), m_vpPage(nullptr), m_scene(scenePage), balloonPlacing(false), m_showGrid(false), - m_navStyle(nullptr), d(new Private(this)) + m_navStyle(nullptr), d(new Private(this)), toolHandler(nullptr) { assert(vpPage); m_vpPage = vpPage; @@ -281,6 +282,31 @@ void QGVPage::setNavigationStyle(std::string navParm) } } + +void QGVPage::activateHandler(TechDrawHandler* newHandler) +{ + if (toolHandler) { + toolHandler->deactivate(); + } + + toolHandler = std::unique_ptr(newHandler); + toolHandler->activate(this); + + // make sure receiver has focus so immediately pressing Escape will be handled by + // ViewProviderSketch::keyPressed() and dismiss the active handler, and not the entire + // sketcher editor + //Gui::MDIView* mdi = Gui::Application::Instance->activeDocument()->getActiveView(); + //mdi->setFocus(); +} + +void QGVPage::deactivateHandler() +{ + if (toolHandler) { + toolHandler->deactivate(); + toolHandler = nullptr; + } +} + void QGVPage::startBalloonPlacing(DrawView* parent) { // Base::Console().Message("QGVP::startBalloonPlacing(%s)\n", parent->getNameInDocument()); @@ -421,7 +447,12 @@ void QGVPage::wheelEvent(QWheelEvent* event) void QGVPage::keyPressEvent(QKeyEvent* event) { - m_navStyle->handleKeyPressEvent(event); + if (toolHandler) { + toolHandler->keyPressEvent(event); + } + else { + m_navStyle->handleKeyPressEvent(event); + } if (!event->isAccepted()) { QGraphicsView::keyPressEvent(event); } @@ -429,7 +460,12 @@ void QGVPage::keyPressEvent(QKeyEvent* event) void QGVPage::keyReleaseEvent(QKeyEvent* event) { - m_navStyle->handleKeyReleaseEvent(event); + if (toolHandler) { + toolHandler->keyReleaseEvent(event); + } + else { + m_navStyle->handleKeyReleaseEvent(event); + } if (!event->isAccepted()) { QGraphicsView::keyReleaseEvent(event); } @@ -465,6 +501,11 @@ void QGVPage::enterEvent(QEvent* event) void QGVPage::enterEvent(QEnterEvent* event) #endif { + if (toolHandler) { + // if the user interacted with another widget than the mdi, the cursor got unset. + // So we reapply it. + toolHandler->updateCursor(); + } QGraphicsView::enterEvent(event); m_navStyle->handleEnterEvent(event); QGraphicsView::enterEvent(event); @@ -478,21 +519,37 @@ void QGVPage::leaveEvent(QEvent* event) void QGVPage::mousePressEvent(QMouseEvent* event) { - m_navStyle->handleMousePressEvent(event); + if (toolHandler && (event->button() != Qt::MiddleButton)) { + toolHandler->mousePressEvent(event); + } + else { + m_navStyle->handleMousePressEvent(event); + } QGraphicsView::mousePressEvent(event); } void QGVPage::mouseMoveEvent(QMouseEvent* event) { - m_navStyle->handleMouseMoveEvent(event); + if (toolHandler) { + toolHandler->mouseMoveEvent(event); + } + else { + m_navStyle->handleMouseMoveEvent(event); + } QGraphicsView::mouseMoveEvent(event); } void QGVPage::mouseReleaseEvent(QMouseEvent* event) { - m_navStyle->handleMouseReleaseEvent(event); - QGraphicsView::mouseReleaseEvent(event); - resetCursor(); + if (toolHandler && (event->button() != Qt::MiddleButton)) { + QGraphicsView::mouseReleaseEvent(event); + toolHandler->mouseReleaseEvent(event); + } + else { + m_navStyle->handleMouseReleaseEvent(event); + QGraphicsView::mouseReleaseEvent(event); + resetCursor(); + } } TechDraw::DrawPage* QGVPage::getDrawPage() { return m_vpPage->getDrawPage(); } @@ -553,8 +610,7 @@ void QGVPage::activateCursor(QCursor cursor) void QGVPage::resetCursor() { - this->setCursor(Qt::ArrowCursor); - viewport()->setCursor(Qt::ArrowCursor); + activateCursor(Qt::ArrowCursor); } void QGVPage::setPanCursor() { activateCursor(panCursor); } diff --git a/src/Mod/TechDraw/Gui/QGVPage.h b/src/Mod/TechDraw/Gui/QGVPage.h index 3b40888650..c39a9116e9 100644 --- a/src/Mod/TechDraw/Gui/QGVPage.h +++ b/src/Mod/TechDraw/Gui/QGVPage.h @@ -71,6 +71,7 @@ class QGILeaderLine; class QGIRichAnno; class QGITile; class QGVNavStyle; +class TechDrawHandler; class TechDrawGuiExport QGVPage: public QGraphicsView { @@ -101,6 +102,10 @@ public: void showGrid(bool state) { m_showGrid = state; } void updateViewport() { viewport()->repaint(); } + void activateHandler(TechDrawHandler* newHandler); + void deactivateHandler(); + bool isHandlerActive() { return toolHandler != nullptr; } + bool isBalloonPlacing() const { return balloonPlacing; } void setBalloonPlacing(bool isPlacing) { balloonPlacing = isPlacing; } @@ -196,6 +201,8 @@ private: MDIViewPage* m_parentMDI; QContextMenuEvent* m_saveContextEvent; + + std::unique_ptr toolHandler; }; }// namespace TechDrawGui diff --git a/src/Mod/TechDraw/Gui/Resources/TechDraw.qrc b/src/Mod/TechDraw/Gui/Resources/TechDraw.qrc index 82812f4187..132fcfc226 100644 --- a/src/Mod/TechDraw/Gui/Resources/TechDraw.qrc +++ b/src/Mod/TechDraw/Gui/Resources/TechDraw.qrc @@ -86,10 +86,12 @@ icons/square.svg icons/TechDraw_3PtAngleDimension.svg icons/TechDraw_AngleDimension.svg + icons/TechDraw_AreaDimension.svg icons/TechDraw_Balloon.svg icons/TechDraw_CameraOrientation.svg icons/TechDraw_DiameterDimension.svg icons/TechDraw_Dimension.svg + icons/TechDraw_Dimension_Pointer.svg icons/TechDraw_DimensionRepair.svg icons/TechDraw_ExtensionAreaAnnotation.svg icons/TechDraw_ExtensionArcLengthAnnotation.svg diff --git a/src/Mod/TechDraw/Gui/Resources/icons/TechDraw_AreaDimension.svg b/src/Mod/TechDraw/Gui/Resources/icons/TechDraw_AreaDimension.svg new file mode 100644 index 0000000000..534f53298b --- /dev/null +++ b/src/Mod/TechDraw/Gui/Resources/icons/TechDraw_AreaDimension.svg @@ -0,0 +1,88 @@ + + + diff --git a/src/Mod/TechDraw/Gui/Resources/icons/TechDraw_Dimension.svg b/src/Mod/TechDraw/Gui/Resources/icons/TechDraw_Dimension.svg index c3b098638a..4a59128659 100644 --- a/src/Mod/TechDraw/Gui/Resources/icons/TechDraw_Dimension.svg +++ b/src/Mod/TechDraw/Gui/Resources/icons/TechDraw_Dimension.svg @@ -1,26 +1,24 @@ - - + inkscape:export-ydpi="45" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:dc="http://purl.org/dc/elements/1.1/"> - - - - - - - - @@ -131,26 +105,6 @@ offset="1" id="stop3897" /> - - + inkscape:collect="always" + xlink:href="#linearGradient3026" + id="linearGradient927" + x1="16.969255" + y1="39.85923" + x2="58.005249" + y2="39.85923" + gradientUnits="userSpaceOnUse" /> + inkscape:snap-nodes="false" + objecttolerance="10.0" + gridtolerance="10.0" + guidetolerance="10.0" + inkscape:pagecheckerboard="0"> image/svg+xml - [WandererFan] @@ -266,70 +222,81 @@ id="layer1" inkscape:label="Layer 1" inkscape:groupmode="layer"> - - - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/src/Mod/TechDraw/Gui/Resources/icons/TechDraw_Dimension_Pointer.svg b/src/Mod/TechDraw/Gui/Resources/icons/TechDraw_Dimension_Pointer.svg new file mode 100644 index 0000000000..a489693e73 --- /dev/null +++ b/src/Mod/TechDraw/Gui/Resources/icons/TechDraw_Dimension_Pointer.svg @@ -0,0 +1,146 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Mod/TechDraw/Gui/TechDrawHandler.cpp b/src/Mod/TechDraw/Gui/TechDrawHandler.cpp new file mode 100644 index 0000000000..182be5879b --- /dev/null +++ b/src/Mod/TechDraw/Gui/TechDrawHandler.cpp @@ -0,0 +1,131 @@ +/*************************************************************************** + * Copyright (c) 2024 Pierre-Louis Boyer * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#include "PreCompiled.h" +#ifndef _PreComp_ +#include + +#include +#include +#include +#include +#include + +#include +#endif // #ifndef _PreComp_ + +#include +#include +#include +#include +#include +#include +#include + +#include "MDIViewPage.h" +#include "QGVPage.h" +#include "TechDrawHandler.h" + + +using namespace TechDrawGui; + +/**************************** TechDrawHandler *******************************************/ + +TechDrawHandler::TechDrawHandler() : Gui::ToolHandler(), viewPage(nullptr) +{} + +TechDrawHandler::~TechDrawHandler() +{} + +void TechDrawHandler::activate(QGVPage* vp) +{ + auto* mdi = dynamic_cast(Gui::getMainWindow()->activeWindow()); + if (!mdi) { + return; + } + mdi->enableContextualMenu(false); + + viewPage = vp; + + if (!Gui::ToolHandler::activate()) { + viewPage->deactivateHandler(); + } +} + +void TechDrawHandler::deactivate() +{ + Gui::ToolHandler::deactivate(); + + // The context menu event of MDIViewPage comes after the tool is deactivated. + // So to prevent the menu from appearing when the tool is cleared by right mouse click + // we set a small timer. + QTimer::singleShot(100, [this]() { // 100 milliseconds delay + auto* mdi = dynamic_cast(Gui::getMainWindow()->activeWindow()); + if (!mdi) { + return; + } + mdi->enableContextualMenu(true); + }); +} + +void TechDrawHandler::keyReleaseEvent(QKeyEvent* event) +{ + // the default behaviour is to quit - specific handler categories may + // override this behaviour, for example to implement a continuous mode + if (event->key() == Qt::Key_Escape) { + quit(); + event->accept(); + } +} + +void TechDrawHandler::mouseReleaseEvent(QMouseEvent* event) +{ + // the default behaviour is to quit - specific handler categories may + // override this behaviour, for example to implement a continuous mode + if (event->button() == Qt::RightButton) { + quit(); + event->accept(); + } +} + +void TechDrawHandler::quit() +{ + viewPage->deactivateHandler(); +} + +QWidget* TechDrawHandler::getCursorWidget() +{ + return viewPage; +} + +void TechDrawHandler::setWidgetCursor(QCursor cursor) +{ + if (viewPage) { + viewPage->setCursor(cursor); + viewPage->viewport()->setCursor(cursor); + } +} + +TechDraw::DrawPage* TechDrawHandler::getPage() +{ + return viewPage->getDrawPage(); +} \ No newline at end of file diff --git a/src/Mod/TechDraw/Gui/TechDrawHandler.h b/src/Mod/TechDraw/Gui/TechDrawHandler.h new file mode 100644 index 0000000000..3e3c8a81ea --- /dev/null +++ b/src/Mod/TechDraw/Gui/TechDrawHandler.h @@ -0,0 +1,67 @@ +/*************************************************************************** + * Copyright (c) 2024 Pierre-Louis Boyer * + * * + * 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 TechDrawGUI_TechDrawHandler_H +#define TechDrawGUI_TechDrawHandler_H + +#include + +#include + +namespace TechDrawGui +{ +class QGVPage; + +class TechDrawGuiExport TechDrawHandler : public Gui::ToolHandler +{ +public: + + TechDrawHandler(); + virtual ~TechDrawHandler(); + + void activate(QGVPage* vPage); + void deactivate() override; + + void quit() override; + + virtual void mouseMoveEvent(QMouseEvent* event) = 0; + virtual void mousePressEvent(QMouseEvent* event) { Q_UNUSED(event) }; + virtual void mouseReleaseEvent(QMouseEvent* event); + + virtual void keyPressEvent(QKeyEvent* event) = 0; + virtual void keyReleaseEvent(QKeyEvent* event); + + TechDraw::DrawPage* getPage(); + + +protected: + QWidget* getCursorWidget() override; + void setWidgetCursor(QCursor cursor) override; + + QGVPage* viewPage; +}; + + +} // namespace TechDrawGui + + +#endif // TechDrawGUI_TechDrawHandler_H diff --git a/src/Mod/TechDraw/Gui/ViewProviderDimension.cpp b/src/Mod/TechDraw/Gui/ViewProviderDimension.cpp index a08b4fccf3..d40b75a455 100644 --- a/src/Mod/TechDraw/Gui/ViewProviderDimension.cpp +++ b/src/Mod/TechDraw/Gui/ViewProviderDimension.cpp @@ -196,6 +196,8 @@ void ViewProviderDimension::setPixmapForType() sPixmap = "TechDraw_AngleDimension"; } else if (getViewObject()->Type.isValue("Angle3Pt")) { sPixmap = "TechDraw_3PtAngleDimension"; + } else if (getViewObject()->Type.isValue("Area")) { + sPixmap = "TechDraw_ExtensionAreaAnnotation"; } } diff --git a/src/Mod/TechDraw/Gui/Workbench.cpp b/src/Mod/TechDraw/Gui/Workbench.cpp index b0651ffaba..6e06e4f1e2 100644 --- a/src/Mod/TechDraw/Gui/Workbench.cpp +++ b/src/Mod/TechDraw/Gui/Workbench.cpp @@ -28,6 +28,7 @@ #endif #include "Workbench.h" +#include #include #include @@ -82,6 +83,7 @@ Gui::MenuItem* Workbench::setupMenuBar() const // dimensions Gui::MenuItem* dimensions = new Gui::MenuItem; dimensions->setCommand("Dimensions"); + *dimensions << "TechDraw_Dimension"; *dimensions << "TechDraw_LengthDimension"; *dimensions << "TechDraw_HorizontalDimension"; *dimensions << "TechDraw_VerticalDimension"; @@ -89,6 +91,7 @@ Gui::MenuItem* Workbench::setupMenuBar() const *dimensions << "TechDraw_DiameterDimension"; *dimensions << "TechDraw_AngleDimension"; *dimensions << "TechDraw_3PtAngleDimension"; + *dimensions << "TechDraw_AreaDimension"; *dimensions << "TechDraw_HorizontalExtentDimension"; *dimensions << "TechDraw_VerticalExtentDimension"; // TechDraw_LinkDimension is DEPRECATED. Use TechDraw_DimensionRepair instead. @@ -305,14 +308,30 @@ Gui::ToolBarItem* Workbench::setupToolBars() const Gui::ToolBarItem* dims = new Gui::ToolBarItem(root); dims->setCommand("TechDraw Dimensions"); - *dims << "TechDraw_LengthDimension"; - *dims << "TechDraw_HorizontalDimension"; - *dims << "TechDraw_VerticalDimension"; - *dims << "TechDraw_RadiusDimension"; - *dims << "TechDraw_DiameterDimension"; - *dims << "TechDraw_AngleDimension"; - *dims << "TechDraw_3PtAngleDimension"; - *dims << "TechDraw_ExtentGroup"; + + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath( + "User parameter:BaseApp/Preferences/Mod/TechDraw/dimensioning"); + bool separatedTools = hGrp->GetBool("SeparatedDimensioningTools", true); + if (hGrp->GetBool("SingleDimensioningTool", true)) { + if (separatedTools) { + *dims << "TechDraw_Dimension"; + } + else { + *dims << "TechDraw_CompDimensionTools"; + } + } + if (separatedTools) { + *dims << "TechDraw_LengthDimension"; + *dims << "TechDraw_HorizontalDimension"; + *dims << "TechDraw_VerticalDimension"; + *dims << "TechDraw_RadiusDimension"; + *dims << "TechDraw_DiameterDimension"; + *dims << "TechDraw_AngleDimension"; + *dims << "TechDraw_3PtAngleDimension"; + *dims << "TechDraw_AreaDimension"; + *dims << "TechDraw_ExtentGroup"; + } + // TechDraw_LinkDimension is DEPRECATED. Use TechDraw_DimensionRepair instead. // *dims << "TechDraw_LinkDimension"; *dims << "TechDraw_Balloon"; @@ -329,8 +348,10 @@ Gui::ToolBarItem* Workbench::setupToolBars() const *extattribs << "TechDraw_ExtensionPositionSectionView"; *extattribs << "TechDraw_ExtensionPosChainDimensionGroup"; *extattribs << "TechDraw_ExtensionCascadeDimensionGroup"; - *extattribs << "TechDraw_ExtensionAreaAnnotation"; - *extattribs << "TechDraw_ExtensionArcLengthAnnotation"; + if (separatedTools) { + *extattribs << "TechDraw_ExtensionAreaAnnotation"; + *extattribs << "TechDraw_ExtensionArcLengthAnnotation"; + } *extattribs << "TechDraw_ExtensionCustomizeFormat"; Gui::ToolBarItem* extcenter = new Gui::ToolBarItem(root); @@ -344,10 +365,12 @@ Gui::ToolBarItem* Workbench::setupToolBars() const Gui::ToolBarItem* extdimensions = new Gui::ToolBarItem(root); extdimensions->setCommand("TechDraw Extend Dimensions"); - *extdimensions << "TechDraw_ExtensionCreateChainDimensionGroup"; - *extdimensions << "TechDraw_ExtensionCreateCoordDimensionGroup"; - *extdimensions << "TechDraw_ExtensionChamferDimensionGroup"; - *extdimensions << "TechDraw_ExtensionCreateLengthArc"; + if (separatedTools) { + *extdimensions << "TechDraw_ExtensionCreateChainDimensionGroup"; + *extdimensions << "TechDraw_ExtensionCreateCoordDimensionGroup"; + *extdimensions << "TechDraw_ExtensionChamferDimensionGroup"; + *extdimensions << "TechDraw_ExtensionCreateLengthArc"; + } *extdimensions << "TechDraw_ExtensionInsertPrefixGroup"; *extdimensions << "TechDraw_ExtensionIncreaseDecreaseGroup"; @@ -406,6 +429,7 @@ Gui::ToolBarItem* Workbench::setupCommandBars() const Gui::ToolBarItem* dims = new Gui::ToolBarItem(root); dims->setCommand("TechDraw Dimensions"); + *dims << "TechDraw_Dimension"; *dims << "TechDraw_LengthDimension"; *dims << "TechDraw_HorizontalDimension"; *dims << "TechDraw_VerticalDimension";