/*************************************************************************** * 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 * * * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "DrawGuiUtil.h" #include "QGSPage.h" #include "TaskCosmeticCircle.h" #include "TaskSelectLineAttributes.h" #include "ViewProviderBalloon.h" #include "ViewProviderDimension.h" #include "ViewProviderPage.h" using namespace TechDrawGui; using namespace TechDraw; using DU = DrawUtil; namespace TechDrawGui { //TechDraw::LineFormat activeAttributes; // container holding global line attributes //internal helper functions TechDraw::LineFormat& _getActiveLineAttributes(); Base::Vector3d _circleCenter(Base::Vector3d p1, Base::Vector3d p2, Base::Vector3d p3); void _createThreadCircle(const std::string Name, TechDraw::DrawViewPart* objFeat, double factor); void _createThreadLines(const std::vector& SubNames, TechDraw::DrawViewPart* objFeat, double factor); void _setLineAttributes(TechDraw::CosmeticEdge* cosEdge); void _setLineAttributes(TechDraw::CenterLine* cosEdge); void _setLineAttributes(TechDraw::CosmeticEdge* cosEdge, int style, float weight, Base::Color color); void _setLineAttributes(TechDraw::CenterLine* cosEdge, int style, float weight, Base::Color color); double _getAngle(Base::Vector3d center, Base::Vector3d point); std::vector _getVertexPoints(const std::vector& SubNames, TechDraw::DrawViewPart* objFeat); bool _checkSel(Gui::Command* cmd, std::vector& selection, TechDraw::DrawViewPart*& objFeat, const std::string& message); std::string _createBalloon(Gui::Command* cmd, TechDraw::DrawViewPart* objFeat); //=========================================================================== // TechDraw_ExtensionHoleCircle //=========================================================================== void execHoleCircle(Gui::Command* cmd) { //create centerlines of a hole/bolt circle std::vector selection; TechDraw::DrawViewPart* objFeat{nullptr}; if (!_checkSel(cmd, selection, objFeat, QT_TRANSLATE_NOOP("Command","TechDraw hole circle"))) { return; } const std::vector SubNames = selection[0].getSubNames(); std::vector Circles; for (const std::string& Name : SubNames) { int GeoId = TechDraw::DrawUtil::getIndexFromName(Name); std::string GeoType = TechDraw::DrawUtil::getGeomTypeFromName(Name); TechDraw::BaseGeomPtr geom = objFeat->getGeomByIndex(GeoId); if (GeoType == "Edge") { if (geom->getGeomType() == GeomType::CIRCLE || geom->getGeomType() == GeomType::ARCOFCIRCLE) { TechDraw::CirclePtr cgen = std::static_pointer_cast(geom); Circles.push_back(cgen); } } } if (Circles.size() <= 2) { QMessageBox::warning(Gui::getMainWindow(), QObject::tr("TechDraw hole circle"), QObject::tr("Fewer than three circles selected")); return; } Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Bolt circle centerlines")); // make the bolt hole circle from 3 scaled and rotated points Base::Vector3d bigCenter = _circleCenter(Circles[0]->center, Circles[1]->center, Circles[2]->center); double bigRadius = (Circles[0]->center - bigCenter).Length(); // now convert the center & radius to canonical form bigCenter = CosmeticVertex::makeCanonicalPointInverted(objFeat, bigCenter); bigRadius = bigRadius / objFeat->getScale(); TechDraw::BaseGeomPtr bigCircle = std::make_shared(bigCenter, bigRadius); std::string bigCircleTag = objFeat->addCosmeticEdge(bigCircle); TechDraw::CosmeticEdge* ceCircle = objFeat->getCosmeticEdge(bigCircleTag); _setLineAttributes(ceCircle); // make the center lines for the individual bolt holes constexpr double ExtendFactor{1.1}; for (const TechDraw::CirclePtr& oneCircle : Circles) { // convert the center to canonical form Base::Vector3d oneCircleCenter = CosmeticVertex::makeCanonicalPointInverted(objFeat, oneCircle->center); // oneCircle->radius is scaled. double oneRadius = oneCircle->radius / objFeat->getScale(); // what is magic number 2 (now ExtendFactor)? just a fudge factor to extend the line beyond the bolt // hole circle? should it be a function of hole diameter? maybe 110% of oneRadius? Base::Vector3d delta = (oneCircleCenter - bigCenter).Normalize() * (oneRadius * ExtendFactor); Base::Vector3d startPt = oneCircleCenter + delta; Base::Vector3d endPt = oneCircleCenter - delta; std::string oneLineTag = objFeat->addCosmeticEdge(startPt, endPt); TechDraw::CosmeticEdge* ceLine = objFeat->getCosmeticEdge(oneLineTag); _setLineAttributes(ceLine); } cmd->getSelection().clearSelection(); objFeat->refreshCEGeoms(); objFeat->requestPaint(); Gui::Command::commitCommand(); } }// namespace TechDrawGui DEF_STD_CMD_A(CmdTechDrawExtensionHoleCircle) CmdTechDrawExtensionHoleCircle::CmdTechDrawExtensionHoleCircle() : Command("TechDraw_ExtensionHoleCircle") { sAppModule = "TechDraw"; sGroup = QT_TR_NOOP("TechDraw"); sMenuText = QT_TR_NOOP("Bolt Circle Centerlines"); sToolTipText = QT_TR_NOOP("Adds centerlines to a circular pattern of three or more selected circles"); sWhatsThis = "TechDraw_ExtensionHoleCircle"; sStatusTip = sMenuText; sPixmap = "TechDraw_ExtensionHoleCircle"; } void CmdTechDrawExtensionHoleCircle::activated(int iMsg) { Q_UNUSED(iMsg); execHoleCircle(this); //Base::Console().message("HoleCircle started\n"); } bool CmdTechDrawExtensionHoleCircle::isActive() { bool havePage = DrawGuiUtil::needPage(this); bool haveView = DrawGuiUtil::needView(this); return (havePage && haveView); } //=========================================================================== // TechDraw_ExtensionCircleCenterLines //=========================================================================== void execCircleCenterLines(Gui::Command* cmd) { // create circle centerlines std::vector selection; TechDraw::DrawViewPart* objFeat{nullptr}; if (!_checkSel(cmd, selection, objFeat, QT_TRANSLATE_NOOP("Command","TechDraw circle centerlines"))) { return; } Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Circle Centerlines")); const std::vector SubNames = selection[0].getSubNames(); for (const std::string& Name : SubNames) { int GeoId = TechDraw::DrawUtil::getIndexFromName(Name); TechDraw::BaseGeomPtr geom = objFeat->getGeomByIndex(GeoId); std::string GeoType = TechDraw::DrawUtil::getGeomTypeFromName(Name); if (GeoType == "Edge") { if (geom->getGeomType() == GeomType::CIRCLE || geom->getGeomType() == GeomType::ARCOFCIRCLE) { TechDraw::CirclePtr cgen = std::static_pointer_cast(geom); // cgen->center is a scaled, rotated and inverted point Base::Vector3d center = CosmeticVertex::makeCanonicalPointInverted(objFeat, cgen->center); double radius = cgen->radius / objFeat->getScale(); // right, left, top, bottom are formed from a canonical point (center) // so they do not need to be changed to canonical form. Base::Vector3d right(center.x + radius + 2.0, center.y, 0.0); Base::Vector3d top(center.x, center.y + radius + 2.0, 0.0); Base::Vector3d left(center.x - radius - 2.0, center.y, 0.0); Base::Vector3d bottom(center.x, center.y - radius - 2.0, 0.0); std::string line1tag = objFeat->addCosmeticEdge(right, left); std::string line2tag = objFeat->addCosmeticEdge(top, bottom); TechDraw::CosmeticEdge* horiz = objFeat->getCosmeticEdge(line1tag); _setLineAttributes(horiz); TechDraw::CosmeticEdge* vert = objFeat->getCosmeticEdge(line2tag); _setLineAttributes(vert); } } } cmd->getSelection().clearSelection(); objFeat->refreshCEGeoms(); objFeat->requestPaint(); Gui::Command::commitCommand(); } DEF_STD_CMD_A(CmdTechDrawExtensionCircleCenterLines) CmdTechDrawExtensionCircleCenterLines::CmdTechDrawExtensionCircleCenterLines() : Command("TechDraw_ExtensionCircleCenterLines") { sAppModule = "TechDraw"; sGroup = QT_TR_NOOP("TechDraw"); sMenuText = QT_TR_NOOP("Circle Centerlines"); sToolTipText = QT_TR_NOOP("Adds centerlines to the selected circles and arcs"); sWhatsThis = "TechDraw_ExtensionCircleCenterLines"; sStatusTip = sMenuText; sPixmap = "TechDraw_ExtensionCircleCenterLines"; } void CmdTechDrawExtensionCircleCenterLines::activated(int iMsg) { Q_UNUSED(iMsg); execCircleCenterLines(this); } bool CmdTechDrawExtensionCircleCenterLines::isActive() { bool havePage = DrawGuiUtil::needPage(this); bool haveView = DrawGuiUtil::needView(this); return (havePage && haveView); } //=========================================================================== // TechDraw_ExtensionCircleCenterLinesGroup //=========================================================================== DEF_STD_CMD_ACL(CmdTechDrawExtensionCircleCenterLinesGroup) CmdTechDrawExtensionCircleCenterLinesGroup::CmdTechDrawExtensionCircleCenterLinesGroup() : Command("TechDraw_ExtensionCircleCenterLinesGroup") { sAppModule = "TechDraw"; sGroup = QT_TR_NOOP("TechDraw"); sMenuText = QT_TR_NOOP("Circle Centerlines"); sToolTipText = QT_TR_NOOP("Adds centerlines to selected circles and arcs"); sWhatsThis = "TechDraw_ExtensionCircleCenterLinesGroup"; sStatusTip = sMenuText; } void CmdTechDrawExtensionCircleCenterLinesGroup::activated(int 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; } auto pcAction = qobject_cast(_pcAction); pcAction->setIcon(pcAction->actions().at(iMsg)->icon()); switch (iMsg) { case 0://circle centerlines execCircleCenterLines(this); break; case 1://bolt circle centerlines execHoleCircle(this); break; default: Base::Console().message("CMD::CVGrp - invalid iMsg: %d\n", iMsg); }; } Gui::Action* CmdTechDrawExtensionCircleCenterLinesGroup::createAction() { Gui::ActionGroup* pcAction = new Gui::ActionGroup(this, Gui::getMainWindow()); pcAction->setDropDownMenu(true); applyCommandData(this->className(), pcAction); QAction* p1 = pcAction->addAction(QString()); p1->setIcon(Gui::BitmapFactory().iconFromTheme("TechDraw_ExtensionCircleCenterLines")); p1->setObjectName(QStringLiteral("TechDraw_ExtensionCircleCenterLines")); p1->setWhatsThis(QStringLiteral("TechDraw_ExtensionCircleCenterLines")); QAction* p2 = pcAction->addAction(QString()); p2->setIcon(Gui::BitmapFactory().iconFromTheme("TechDraw_ExtensionHoleCircle")); p2->setObjectName(QStringLiteral("TechDraw_ExtensionHoleCircle")); p2->setWhatsThis(QStringLiteral("TechDraw_ExtensionHoleCircle")); _pcAction = pcAction; languageChange(); pcAction->setIcon(p1->icon()); int defaultId = 0; pcAction->setProperty("defaultAction", QVariant(defaultId)); return pcAction; } void CmdTechDrawExtensionCircleCenterLinesGroup::languageChange() { Command::languageChange(); if (!_pcAction) { return; } Gui::ActionGroup* pcAction = qobject_cast(_pcAction); QList action = pcAction->actions(); QAction* arc1 = action[0]; arc1->setText( QApplication::translate("CmdTechDrawExtensionCircleCenterLines", "Circle Centerlines")); arc1->setToolTip(QApplication::translate("CmdTechDrawExtensionCircleCenterLines", "Adds centerlines to selected circles and arcs:")); arc1->setStatusTip(arc1->text()); QAction* arc2 = action[1]; arc2->setText( QApplication::translate("CmdTechDrawExtensionHoleCircle", "Bolt Circle Centerlines")); arc2->setToolTip(QApplication::translate("CmdTechDrawExtensionHoleCircle", "Adds centerlines to a circular pattern of selected circles")); arc2->setStatusTip(arc2->text()); } bool CmdTechDrawExtensionCircleCenterLinesGroup::isActive() { bool havePage = DrawGuiUtil::needPage(this); bool haveView = DrawGuiUtil::needView(this, true); return (havePage && haveView); } //=========================================================================== // TechDraw_ExtensionThreadHoleSide //=========================================================================== void execThreadHoleSide(Gui::Command* cmd) { constexpr double ThreadFactor{1.176}; // add cosmetic thread to side view of hole std::vector selection; TechDraw::DrawViewPart* objFeat{nullptr}; if (!_checkSel(cmd, selection, objFeat, QT_TRANSLATE_NOOP("Command","TechDraw Thread Hole Side"))) { return; } Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Cosmetic Thread Hole Side")); const std::vector SubNames = selection[0].getSubNames(); if (SubNames.size() >= 2) { _createThreadLines(SubNames, objFeat, ThreadFactor); } cmd->getSelection().clearSelection(); objFeat->refreshCEGeoms(); objFeat->requestPaint(); Gui::Command::commitCommand(); } DEF_STD_CMD_A(CmdTechDrawExtensionThreadHoleSide) CmdTechDrawExtensionThreadHoleSide::CmdTechDrawExtensionThreadHoleSide() : Command("TechDraw_ExtensionThreadHoleSide") { sAppModule = "TechDraw"; sGroup = QT_TR_NOOP("TechDraw"); sMenuText = QT_TR_NOOP("Cosmetic Thread Hole Side View"); sToolTipText = QT_TR_NOOP("Adds a cosmetic thread to the side view of a hole or circle"); sWhatsThis = "TechDraw_ExtensionThreadHoleSide"; sStatusTip = sMenuText; sPixmap = "TechDraw_ExtensionThreadHoleSide"; } void CmdTechDrawExtensionThreadHoleSide::activated(int iMsg) { Q_UNUSED(iMsg); execThreadHoleSide(this); } bool CmdTechDrawExtensionThreadHoleSide::isActive() { bool havePage = DrawGuiUtil::needPage(this); bool haveView = DrawGuiUtil::needView(this); return (havePage && haveView); } //=========================================================================== // TechDraw_ExtensionThreadBoltSide //=========================================================================== void execThreadBoltSide(Gui::Command* cmd) { constexpr double ThreadFactor{0.85}; // add cosmetic thread to side view of bolt std::vector selection; TechDraw::DrawViewPart* objFeat{nullptr}; if (!_checkSel(cmd, selection, objFeat, QT_TRANSLATE_NOOP("Command","TechDraw Thread Bolt Side"))) { return; } Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Cosmetic Thread Bolt Side")); const std::vector SubNames = selection[0].getSubNames(); if (SubNames.size() >= 2) { _createThreadLines(SubNames, objFeat, ThreadFactor); } cmd->getSelection().clearSelection(); objFeat->refreshCEGeoms(); objFeat->requestPaint(); Gui::Command::commitCommand(); } DEF_STD_CMD_A(CmdTechDrawExtensionThreadBoltSide) CmdTechDrawExtensionThreadBoltSide::CmdTechDrawExtensionThreadBoltSide() : Command("TechDraw_ExtensionThreadBoltSide") { sAppModule = "TechDraw"; sGroup = QT_TR_NOOP("TechDraw"); sMenuText = QT_TR_NOOP("Cosmetic Thread Bolt Side View"); sToolTipText = QT_TR_NOOP("Adds a cosmetic thread to the side view of a " "bolt/screw/rod between two selected parallel lines"); sWhatsThis = "TechDraw_ExtensionThreadBoltSide"; sStatusTip = sMenuText; sPixmap = "TechDraw_ExtensionThreadBoltSide"; } void CmdTechDrawExtensionThreadBoltSide::activated(int iMsg) { Q_UNUSED(iMsg); execThreadBoltSide(this); } bool CmdTechDrawExtensionThreadBoltSide::isActive() { bool havePage = DrawGuiUtil::needPage(this); bool haveView = DrawGuiUtil::needView(this); return (havePage && haveView); } //=========================================================================== // TechDraw_ExtensionThreadHoleBottom //=========================================================================== void execThreadHoleBottom(Gui::Command* cmd) { constexpr double ThreadFactor{1.177}; // factor above is 1.176. should they be the same? // add cosmetic thread to bottom view of hole std::vector selection; TechDraw::DrawViewPart* objFeat{nullptr}; if (!_checkSel(cmd, selection, objFeat, QT_TRANSLATE_NOOP("Command","TechDraw Thread Hole Bottom"))) { return; } Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Cosmetic thread hole bottom")); const std::vector SubNames = selection[0].getSubNames(); for (const std::string& Name : SubNames) { _createThreadCircle(Name, objFeat, ThreadFactor); } cmd->getSelection().clearSelection(); objFeat->refreshCEGeoms(); objFeat->requestPaint(); Gui::Command::commitCommand(); } DEF_STD_CMD_A(CmdTechDrawExtensionThreadHoleBottom) CmdTechDrawExtensionThreadHoleBottom::CmdTechDrawExtensionThreadHoleBottom() : Command("TechDraw_ExtensionThreadHoleBottom") { sAppModule = "TechDraw"; sGroup = QT_TR_NOOP("TechDraw"); sMenuText = QT_TR_NOOP("Cosmetic Thread Hole Bottom View"); sToolTipText = QT_TR_NOOP("Adds a cosmetic thread to the top or bottom view of selected holes or circles"); sWhatsThis = "TechDraw_ExtensionThreadHoleBottom"; sStatusTip = sMenuText; sPixmap = "TechDraw_ExtensionThreadHoleBottom"; } void CmdTechDrawExtensionThreadHoleBottom::activated(int iMsg) { Q_UNUSED(iMsg); execThreadHoleBottom(this); } bool CmdTechDrawExtensionThreadHoleBottom::isActive() { bool havePage = DrawGuiUtil::needPage(this); bool haveView = DrawGuiUtil::needView(this); return (havePage && haveView); } //=========================================================================== // TechDraw_ExtensionThreadBoltBottom //=========================================================================== void execThreadBoltBottom(Gui::Command* cmd) { constexpr double ThreadFactor{0.85}; // add cosmetic thread to bottom view of bolt std::vector selection; TechDraw::DrawViewPart* objFeat{nullptr}; if (!_checkSel(cmd, selection, objFeat, QT_TRANSLATE_NOOP("Command","TechDraw Thread Bolt Bottom"))) { return; } Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Cosmetic Thread Bolt Bottom")); const std::vector SubNames = selection[0].getSubNames(); for (const std::string& Name : SubNames) { _createThreadCircle(Name, objFeat, ThreadFactor); } cmd->getSelection().clearSelection(); objFeat->refreshCEGeoms(); objFeat->requestPaint(); Gui::Command::commitCommand(); } DEF_STD_CMD_A(CmdTechDrawExtensionThreadBoltBottom) CmdTechDrawExtensionThreadBoltBottom::CmdTechDrawExtensionThreadBoltBottom() : Command("TechDraw_ExtensionThreadBoltBottom") { sAppModule = "TechDraw"; sGroup = QT_TR_NOOP("TechDraw"); sMenuText = QT_TR_NOOP("Cosmetic Thread Bolt Bottom View"); sToolTipText = QT_TR_NOOP("Adds a cosmetic thread to the top or bottom view of the selected bolts/screws/rods"); sWhatsThis = "TechDraw_ExtensionThreadBoltBottom"; sStatusTip = sMenuText; sPixmap = "TechDraw_ExtensionThreadBoltBottom"; } void CmdTechDrawExtensionThreadBoltBottom::activated(int iMsg) { Q_UNUSED(iMsg); execThreadBoltBottom(this); } bool CmdTechDrawExtensionThreadBoltBottom::isActive() { bool havePage = DrawGuiUtil::needPage(this); bool haveView = DrawGuiUtil::needView(this); return (havePage && haveView); } //=========================================================================== // TechDraw_ExtensionThreadsGroup //=========================================================================== DEF_STD_CMD_ACL(CmdTechDrawExtensionThreadsGroup) CmdTechDrawExtensionThreadsGroup::CmdTechDrawExtensionThreadsGroup() : Command("TechDraw_ExtensionThreadsGroup") { sAppModule = "TechDraw"; sGroup = QT_TR_NOOP("TechDraw"); sMenuText = QT_TR_NOOP("Cosmetic Thread Hole Side View"); sToolTipText = QT_TR_NOOP("Add a cosmetic thread to the side view of a selected hole between two selected parallel lines"); sWhatsThis = "TechDraw_ExtensionThreadsGroup"; sStatusTip = sMenuText; } void CmdTechDrawExtensionThreadsGroup::activated(int iMsg) { // Base::Console().message("CMD::TechDrawExtensionThreadsGroup - activated(%d)\n", 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; } auto pcAction = qobject_cast(_pcAction); pcAction->setIcon(pcAction->actions().at(iMsg)->icon()); switch (iMsg) { case 0://thread hole side view execThreadHoleSide(this); break; case 1://thread hole bottom view execThreadHoleBottom(this); break; case 2://thread bolt side view execThreadBoltSide(this); break; case 3://thread bolt bottom view execThreadBoltBottom(this); break; default: Base::Console().message("CMD::CVGrp - invalid iMsg: %d\n", iMsg); }; } Gui::Action* CmdTechDrawExtensionThreadsGroup::createAction() { Gui::ActionGroup* pcAction = new Gui::ActionGroup(this, Gui::getMainWindow()); pcAction->setDropDownMenu(true); applyCommandData(this->className(), pcAction); QAction* p1 = pcAction->addAction(QString()); p1->setIcon(Gui::BitmapFactory().iconFromTheme("TechDraw_ExtensionThreadHoleSide")); p1->setObjectName(QStringLiteral("TechDraw_ExtensionThreadHoleSide")); p1->setWhatsThis(QStringLiteral("TechDraw_ExtensionThreadHoleSide")); QAction* p2 = pcAction->addAction(QString()); p2->setIcon(Gui::BitmapFactory().iconFromTheme("TechDraw_ExtensionThreadHoleBottom")); p2->setObjectName(QStringLiteral("TechDraw_ExtensionThreadHoleBottom")); p2->setWhatsThis(QStringLiteral("TechDraw_ExtensionThreadHoleBottom")); QAction* p3 = pcAction->addAction(QString()); p3->setIcon(Gui::BitmapFactory().iconFromTheme("TechDraw_ExtensionThreadBoltSide")); p3->setObjectName(QStringLiteral("TechDraw_ExtensionThreadBoltSide")); p3->setWhatsThis(QStringLiteral("TechDraw_ExtensionThreadBoltSide")); QAction* p4 = pcAction->addAction(QString()); p4->setIcon(Gui::BitmapFactory().iconFromTheme("TechDraw_ExtensionThreadBoltBottom")); p4->setObjectName(QStringLiteral("TechDraw_ExtensionThreadBoltBottom")); p4->setWhatsThis(QStringLiteral("TechDraw_ExtensionThreadBoltBottom")); _pcAction = pcAction; languageChange(); pcAction->setIcon(p1->icon()); int defaultId = 0; pcAction->setProperty("defaultAction", QVariant(defaultId)); return pcAction; } void CmdTechDrawExtensionThreadsGroup::languageChange() { Command::languageChange(); if (!_pcAction) { return; } auto pcAction = qobject_cast(_pcAction); QList action = pcAction->actions(); QAction* arc1 = action[0]; arc1->setText(QApplication::translate("CmdTechDrawExtensionThreadHoleSide", "Cosmetic Thread Hole Side View")); arc1->setToolTip(QApplication::translate("CmdTechDrawExtensionThreadHoleSide", "Adds a cosmetic thread to the side view of a " "selected hole between two selected parallel lines")); arc1->setStatusTip(arc1->text()); QAction* arc2 = action[1]; arc2->setText(QApplication::translate("CmdTechDrawExtensionThreadHoleBottom", "Cosmetic Thread Hole Bottom View")); arc2->setToolTip( QApplication::translate("CmdTechDrawExtensionThreadHoleBottom", "Adds a cosmetic thread to the top or bottom view of holes or circles")); arc2->setStatusTip(arc2->text()); QAction* arc3 = action[2]; arc3->setText(QApplication::translate("CmdTechDrawExtensionThreadBoltSide", "Cosmetic Thread Bolt Side View")); arc3->setToolTip( QApplication::translate("CmdTechDrawExtensionThreadBoltSide", "Adds a cosmetic thread to the side view of a bolt/screw/rod " "between two selected parallel lines")); arc3->setStatusTip(arc3->text()); QAction* arc4 = action[3]; arc4->setText(QApplication::translate("CmdTechDrawExtensionThreadBoltBottom", "Cosmetic Thread Bolt Bottom View")); arc4->setToolTip(QApplication::translate( "CmdTechDrawExtensionThreadBoltBottom", "Adds a cosmetic thread to the top or bottom view of the selected bolts/screws/rods")); arc4->setStatusTip(arc4->text()); } bool CmdTechDrawExtensionThreadsGroup::isActive() { bool havePage = DrawGuiUtil::needPage(this); bool haveView = DrawGuiUtil::needView(this, true); return (havePage && haveView); } //=========================================================================== // TechDraw_ExtensionSelectLineAttributes //=========================================================================== DEF_STD_CMD_A(CmdTechDrawExtensionSelectLineAttributes) CmdTechDrawExtensionSelectLineAttributes::CmdTechDrawExtensionSelectLineAttributes() : Command("TechDraw_ExtensionSelectLineAttributes") { sAppModule = "TechDraw"; sGroup = QT_TR_NOOP("TechDraw"); sMenuText = QT_TR_NOOP("Select Line Attributes, Cascade Spacing and Delta Distance"); sToolTipText = QT_TR_NOOP( "Configures the default attributes for cosmetic lines and centerlines, including cascade spacing and delta distance"); sWhatsThis = "TechDraw_ExtensionSelectLineAttributes"; sStatusTip = sMenuText; sPixmap = "TechDraw_ExtensionSelectLineAttributes"; } void CmdTechDrawExtensionSelectLineAttributes::activated(int iMsg) { Q_UNUSED(iMsg); Gui::Control().showDialog(new TaskDlgSelectLineAttributes()); } bool CmdTechDrawExtensionSelectLineAttributes::isActive() { bool havePage = DrawGuiUtil::needPage(this); bool haveView = DrawGuiUtil::needView(this); return (havePage && haveView); } //=========================================================================== // TechDraw_ExtensionChangeLineAttributes //=========================================================================== DEF_STD_CMD_A(CmdTechDrawExtensionChangeLineAttributes) CmdTechDrawExtensionChangeLineAttributes::CmdTechDrawExtensionChangeLineAttributes() : Command("TechDraw_ExtensionChangeLineAttributes") { sAppModule = "TechDraw"; sGroup = QT_TR_NOOP("TechDraw"); sMenuText = QT_TR_NOOP("Change Line Attributes"); sToolTipText = QT_TR_NOOP("Changes the selected cosmetic lines and centerlines to the specified attributes"); sWhatsThis = "TechDraw_ExtensionChangeLineAttributes"; sStatusTip = sMenuText; sPixmap = "TechDraw_ExtensionChangeLineAttributes"; } void CmdTechDrawExtensionChangeLineAttributes::activated(int iMsg) { // change attributes (type, width, color) of a cosmetic or centerline Q_UNUSED(iMsg); std::vector selection; TechDraw::DrawViewPart* objFeat{nullptr}; if (!_checkSel(this, selection, objFeat, QT_TRANSLATE_NOOP("Command","TechDraw change line attributes"))) { return; } Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Change line attributes")); const std::vector subNames = selection[0].getSubNames(); for (const std::string& name : subNames) { int num = DrawUtil::getIndexFromName(name); BaseGeomPtr baseGeo = objFeat->getGeomByIndex(num); if (baseGeo) { if (baseGeo->getCosmetic()) { if (baseGeo->source() == SourceType::COSMETICEDGE) { TechDraw::CosmeticEdge* cosEdgeTag = objFeat->getCosmeticEdgeBySelection(name); _setLineAttributes(cosEdgeTag); } else if (baseGeo->source() == SourceType::CENTERLINE) { TechDraw::CenterLine* centerLineTag = objFeat->getCenterLineBySelection(name); _setLineAttributes(centerLineTag); } } } } getSelection().clearSelection(); objFeat->refreshCEGeoms(); objFeat->requestPaint(); Gui::Command::commitCommand(); } bool CmdTechDrawExtensionChangeLineAttributes::isActive() { bool havePage = DrawGuiUtil::needPage(this); bool haveView = DrawGuiUtil::needView(this); return (havePage && haveView); } //=========================================================================== // TechDraw_ExtensionVertexAtIntersection //=========================================================================== DEF_STD_CMD_A(CmdTechDrawExtensionVertexAtIntersection) CmdTechDrawExtensionVertexAtIntersection::CmdTechDrawExtensionVertexAtIntersection() : Command("TechDraw_ExtensionVertexAtIntersection") { sAppModule = "TechDraw"; sGroup = QT_TR_NOOP("TechDraw"); sMenuText = QT_TR_NOOP("Cosmetic Intersection Vertices"); sToolTipText = QT_TR_NOOP("Adds cosmetic vertices at the intersections of selected edges"); sWhatsThis = "TechDraw_ExtensionVertexAtIntersection"; sStatusTip = sMenuText; sPixmap = "TechDraw_ExtensionVertexAtIntersection"; } void CmdTechDrawExtensionVertexAtIntersection::activated(int iMsg) { Q_UNUSED(iMsg); //Base::Console().message("VertexAtIntersection started\n"); std::vector selection; TechDraw::DrawViewPart* objFeat{nullptr}; if (!_checkSel(this, selection, objFeat, QT_TRANSLATE_NOOP("Command","TechDraw cosmetic intersection vertices"))) { return; } Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Cosmetic intersection vertices")); const std::vector SubNames = selection[0].getSubNames(); if (SubNames.size() >= 2) { std::string GeoType1 = TechDraw::DrawUtil::getGeomTypeFromName(SubNames[0]); std::string GeoType2 = TechDraw::DrawUtil::getGeomTypeFromName(SubNames[1]); if (GeoType1 == "Edge" && GeoType2 == "Edge") { int GeoId1 = TechDraw::DrawUtil::getIndexFromName(SubNames[0]); TechDraw::BaseGeomPtr geom1 = objFeat->getGeomByIndex(GeoId1); int GeoId2 = TechDraw::DrawUtil::getIndexFromName(SubNames[1]); TechDraw::BaseGeomPtr geom2 = objFeat->getGeomByIndex(GeoId2); std::vector interPoints = geom1->intersection(geom2); for (auto pt : interPoints) { // geometry points are inverted Base::Vector3d temp = CosmeticVertex::makeCanonicalPointInverted(objFeat, pt); objFeat->addCosmeticVertex(temp, false); } } } getSelection().clearSelection(); objFeat->refreshCVGeoms(); objFeat->requestPaint(); Gui::Command::commitCommand(); } bool CmdTechDrawExtensionVertexAtIntersection::isActive() { bool havePage = DrawGuiUtil::needPage(this); bool haveView = DrawGuiUtil::needView(this); return (havePage && haveView); } //=========================================================================== // TechDraw_CosmeticCircle //=========================================================================== DEF_STD_CMD_A(CmdTechDrawCosmeticCircle) CmdTechDrawCosmeticCircle::CmdTechDrawCosmeticCircle() : Command("TechDraw_CosmeticCircle") { sAppModule = "TechDraw"; sGroup = QT_TR_NOOP("TechDraw"); sMenuText = QT_TR_NOOP("Cosmetic 1 Point Circle"); sToolTipText = QT_TR_NOOP("Adds a cosmetic circle based on a selected centerpoint"); sWhatsThis = "TechDraw_CosmeticCircle"; sStatusTip = sToolTipText; sPixmap = "actions/TechDraw_CosmeticCircle"; } bool CmdTechDrawCosmeticCircle::isActive() { bool havePage = DrawGuiUtil::needPage(this); bool haveView = DrawGuiUtil::needView(this, true); return (havePage && haveView); } void execCosmeticCircleCenter(Gui::Command* cmd) { TechDraw::DrawPage* page = DrawGuiUtil::findPage(cmd); if (!page) { return; } std::vector selection = cmd->getSelection().getSelectionEx(); TechDraw::DrawViewPart* baseFeat = nullptr; std::vector subNames2D; std::vector< std::pair > objs3D; if (selection.empty()) { QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong Selection"), QObject::tr("Selection is empty.")); return; } for (auto& so: selection) { if (so.getObject()->isDerivedFrom()) { baseFeat = static_cast (so.getObject()); subNames2D = so.getSubNames(); } else if (so.getObject()->isDerivedFrom()) { std::vector subNames3D = so.getSubNames(); for (auto& sub3D: subNames3D) { std::pair temp; temp.first = static_cast(so.getObject()); temp.second = sub3D; objs3D.push_back(temp); } } else { //garbage } } if (!baseFeat) { QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong Selection"), QObject::tr("You must select a base View for the circle.")); return; } std::vector edgeNames; std::vector vertexNames; for (auto& s: subNames2D) { std::string geomType = DrawUtil::getGeomTypeFromName(s); if (geomType == "Vertex") { vertexNames.push_back(s); } else if (geomType == "Edge") { edgeNames.push_back(s); } } //check if editing existing edge if (!edgeNames.empty() && (edgeNames.size() == 1)) { TechDraw::CosmeticEdge* ce = baseFeat->getCosmeticEdgeBySelection(edgeNames.front()); if (!ce || !(ce->m_geometry->getGeomType() == GeomType::CIRCLE || ce->m_geometry->getGeomType() == GeomType::ARCOFCIRCLE)) { QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong Selection"), QObject::tr("Selection is not a Cosmetic Circle or a Cosmetic Arc of Circle.")); return; } Gui::Control().showDialog(new TaskDlgCosmeticCircle(baseFeat, edgeNames.front())); return; } std::vector points; std::vector is3d; //get the 2D points if (!vertexNames.empty()) { for (auto& v2d: vertexNames) { int idx = DrawUtil::getIndexFromName(v2d); TechDraw::VertexPtr v = baseFeat->getProjVertexByIndex(idx); if (v) { points.push_back(v->point()); is3d.push_back(false); } } } //get the 3D points if (!objs3D.empty()) { for (auto& o3D: objs3D) { int idx = DrawUtil::getIndexFromName(o3D.second); Part::TopoShape s = o3D.first->Shape.getShape(); TopoDS_Vertex v = TopoDS::Vertex(s.getSubShape(TopAbs_VERTEX, idx)); Base::Vector3d p = DrawUtil::vertex2Vector(v); points.push_back(p); is3d.push_back(true); } } if (points.empty()) { QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong Selection"), QObject::tr("Please select a center for the circle.")); return; } bool centerIs3d = false; if (!is3d.empty()) { centerIs3d = is3d.front(); } Gui::Control().showDialog(new TaskDlgCosmeticCircle(baseFeat, points, centerIs3d)); } void CmdTechDrawCosmeticCircle::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; } execCosmeticCircleCenter(this); updateActive(); Gui::Selection().clearSelection(); } //=========================================================================== // TechDraw_ExtensionDrawCosmArc //=========================================================================== //! adds an anti-clockwise arc based on a center point, a radius/start angle point and an end angle //! point. Selection order is significant - center, start end. void execDrawCosmArc(Gui::Command* cmd) { //draw a cosmetic arc of circle std::vector selection; TechDraw::DrawViewPart* objFeat{nullptr}; if (!_checkSel(cmd, selection, objFeat, QT_TRANSLATE_NOOP("Command","TechDraw cosmetic arc"))) { return; } Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Cosmetic arc")); const std::vector SubNames = selection[0].getSubNames(); std::vector vertexPoints; vertexPoints = _getVertexPoints(SubNames, objFeat); if (vertexPoints.size() >= 3) { // vertexPoints come from stored geometry, so are centered, scaled, rotated and inverted (CSRIz). // because the points are inverted, the start and end angles will be mirrored unless we invert the points // before calculating the angle. Base::Vector3d center = CosmeticVertex::makeCanonicalPoint(objFeat, DU::invertY(vertexPoints[0])); Base::Vector3d end1 = CosmeticVertex::makeCanonicalPoint(objFeat, DU::invertY(vertexPoints[1])); Base::Vector3d end2 = CosmeticVertex::makeCanonicalPoint(objFeat, DU::invertY(vertexPoints[2])); double arcRadius = (end1 - center).Length(); double angle1 = _getAngle(center, end1); double angle2 = _getAngle(center, end2); TechDraw::BaseGeomPtr baseGeo = std::make_shared( center, arcRadius, angle1, angle2); TechDraw::AOCPtr aoc = std::static_pointer_cast(baseGeo); // having done our calculations in sensible coordinates, we convert to inverted coords std::string arcTag = objFeat->addCosmeticEdge(baseGeo->inverted()); TechDraw::CosmeticEdge* arcEdge = objFeat->getCosmeticEdge(arcTag); _setLineAttributes(arcEdge); objFeat->refreshCEGeoms(); objFeat->requestPaint(); cmd->getSelection().clearSelection(); Gui::Command::commitCommand(); } } DEF_STD_CMD_A(CmdTechDrawExtensionDrawCosmArc) CmdTechDrawExtensionDrawCosmArc::CmdTechDrawExtensionDrawCosmArc() : Command("TechDraw_ExtensionDrawCosmArc") { sAppModule = "TechDraw"; sGroup = QT_TR_NOOP("TechDraw"); sMenuText = QT_TR_NOOP("Cosmetic Arc"); sToolTipText = QT_TR_NOOP("Adds a cosmetic counter clockwise arc based on three vertices, " "where the first selection is the center point and the second is the radius and start point"); sWhatsThis = "TechDraw_ExtensionDrawCosmArc"; sStatusTip = sMenuText; sPixmap = "TechDraw_ExtensionDrawCosmArc"; } void CmdTechDrawExtensionDrawCosmArc::activated(int iMsg) { Q_UNUSED(iMsg); //Base::Console().message("Cosmetic Arc started\n"); execDrawCosmArc(this); } bool CmdTechDrawExtensionDrawCosmArc::isActive() { bool havePage = DrawGuiUtil::needPage(this); bool haveView = DrawGuiUtil::needView(this); return (havePage && haveView); } //=========================================================================== // TechDraw_ExtensionDrawCosmCircle //=========================================================================== void execDrawCosmCircle(Gui::Command* cmd) { //draw a cosmetic circle std::vector selection; TechDraw::DrawViewPart* objFeat{nullptr}; if (!_checkSel(cmd, selection, objFeat, QT_TRANSLATE_NOOP("Command","TechDraw cosmetic circle"))) { return; } Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Cosmetic Circle")); const std::vector SubNames = selection[0].getSubNames(); std::vector vertexPoints; vertexPoints = _getVertexPoints(SubNames, objFeat); if (vertexPoints.size() >= 2) { double circleRadius = (vertexPoints[1] - vertexPoints[0]).Length() / objFeat->getScale(); auto center = CosmeticVertex::makeCanonicalPointInverted(objFeat, vertexPoints[0]); TechDraw::BaseGeomPtr baseGeo = std::make_shared(center, circleRadius); std::string circleTag = objFeat->addCosmeticEdge(baseGeo); TechDraw::CosmeticEdge* circleEdge = objFeat->getCosmeticEdge(circleTag); _setLineAttributes(circleEdge); objFeat->refreshCEGeoms(); objFeat->requestPaint(); cmd->getSelection().clearSelection(); Gui::Command::commitCommand(); } } DEF_STD_CMD_A(CmdTechDrawExtensionDrawCosmCircle) CmdTechDrawExtensionDrawCosmCircle::CmdTechDrawExtensionDrawCosmCircle() : Command("TechDraw_ExtensionDrawCosmCircle") { sAppModule = "TechDraw"; sGroup = QT_TR_NOOP("TechDraw"); sMenuText = QT_TR_NOOP("Cosmetic 2 Point Circle"); sToolTipText = QT_TR_NOOP("Adds a cosmetic circle based on two selected vertices, where the first is the center point and the second is the radius"); sWhatsThis = "TechDraw_ExtensionDrawCosmCircle"; sStatusTip = sToolTipText; sPixmap = "TechDraw_ExtensionDrawCosmCircle"; } void CmdTechDrawExtensionDrawCosmCircle::activated(int iMsg) { Q_UNUSED(iMsg); //Base::Console().message("Cosmetic Circle started\n"); execDrawCosmCircle(this); } bool CmdTechDrawExtensionDrawCosmCircle::isActive() { bool havePage = DrawGuiUtil::needPage(this); bool haveView = DrawGuiUtil::needView(this); return (havePage && haveView); } //=========================================================================== // TechDraw_ExtensionDrawCosmCircle3Points //=========================================================================== void execDrawCosmCircle3Points(Gui::Command* cmd) { //draw a cosmetic circle through 3 points std::vector selection; TechDraw::DrawViewPart* objFeat{nullptr}; if (!_checkSel(cmd, selection, objFeat, QT_TRANSLATE_NOOP("Command","TechDraw Cosmetic Circle 3 Points"))) { return; } Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Cosmetic Circle 3 Points")); const std::vector SubNames = selection[0].getSubNames(); std::vector vertexPoints; vertexPoints = _getVertexPoints(SubNames, objFeat); if (vertexPoints.size() >= 3) { Base::Vector3d circleCenter = _circleCenter(vertexPoints[0], vertexPoints[1], vertexPoints[2]); double circleRadius = (vertexPoints[0] - circleCenter).Length() / objFeat->getScale(); circleCenter = CosmeticVertex::makeCanonicalPointInverted(objFeat, circleCenter); TechDraw::BaseGeomPtr theCircle = std::make_shared(circleCenter, circleRadius); std::string circleTag = objFeat->addCosmeticEdge(theCircle); TechDraw::CosmeticEdge* circleEdge = objFeat->getCosmeticEdge(circleTag); _setLineAttributes(circleEdge); objFeat->refreshCEGeoms(); objFeat->requestPaint(); cmd->getSelection().clearSelection(); Gui::Command::commitCommand(); } } DEF_STD_CMD_A(CmdTechDrawExtensionDrawCosmCircle3Points) CmdTechDrawExtensionDrawCosmCircle3Points::CmdTechDrawExtensionDrawCosmCircle3Points() : Command("TechDraw_ExtensionDrawCosmCircle3Points") { sAppModule = "TechDraw"; sGroup = QT_TR_NOOP("TechDraw"); sMenuText = QT_TR_NOOP("Cosmetic 3 Point Circle"); sToolTipText = QT_TR_NOOP("Adds a cosmetic circle that passes through 3 selected perimeter points"); sWhatsThis = "TechDraw_ExtensionDrawCosmCircle3Points"; sStatusTip = sMenuText; sPixmap = "TechDraw_ExtensionDrawCosmCircle3Points"; } void CmdTechDrawExtensionDrawCosmCircle3Points::activated(int iMsg) { Q_UNUSED(iMsg); //Base::Console().message("Cosmetic Circle 3 Points started\n"); execDrawCosmCircle3Points(this); } bool CmdTechDrawExtensionDrawCosmCircle3Points::isActive() { bool havePage = DrawGuiUtil::needPage(this); bool haveView = DrawGuiUtil::needView(this); return (havePage && haveView); } //=========================================================================== // TechDraw_ExtensionDrawCirclesGroup //=========================================================================== DEF_STD_CMD_ACL(CmdTechDrawExtensionDrawCirclesGroup) CmdTechDrawExtensionDrawCirclesGroup::CmdTechDrawExtensionDrawCirclesGroup() : Command("TechDraw_ExtensionDrawCirclesGroup") { sAppModule = "TechDraw"; sGroup = QT_TR_NOOP("TechDraw"); sMenuText = QT_TR_NOOP("Cosmetic 1 Point Circle"); sToolTipText = QT_TR_NOOP("Adds a cosmetic circle based on two vertices, where the first selection is the centerpoint and the second is the radius"); sWhatsThis = "TechDraw_ExtensionDrawCirclesGroup"; sStatusTip = sMenuText; } void CmdTechDrawExtensionDrawCirclesGroup::activated(int iMsg) { // Base::Console().message("CMD::ExtensionDrawCirclesGroup - activated(%d)\n", 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; } auto pcAction = qobject_cast(_pcAction); pcAction->setIcon(pcAction->actions().at(iMsg)->icon()); switch (iMsg) { case 0: // 1 Point Circle execCosmeticCircleCenter(this); break; case 1: // 2 Point Circle execDrawCosmCircle(this); break; case 2: // 3 Point Circle execDrawCosmCircle3Points(this); break; case 3: // Cosmetic Arc execDrawCosmArc(this); break; default: Base::Console().message("CMD::CVGrp - invalid iMsg: %d\n", iMsg); }; } Gui::Action* CmdTechDrawExtensionDrawCirclesGroup::createAction() { Gui::ActionGroup* pcAction = new Gui::ActionGroup(this, Gui::getMainWindow()); pcAction->setDropDownMenu(true); applyCommandData(this->className(), pcAction); QAction* p1 = pcAction->addAction(QString()); p1->setIcon(Gui::BitmapFactory().iconFromTheme("actions/TechDraw_CosmeticCircle")); p1->setObjectName(QStringLiteral("TechDraw_CosmeticCircle")); p1->setWhatsThis(QStringLiteral("TechDraw_CosmeticCircle")); QAction* p2 = pcAction->addAction(QString()); p2->setIcon(Gui::BitmapFactory().iconFromTheme("TechDraw_ExtensionDrawCosmCircle")); p2->setObjectName(QStringLiteral("TechDraw_ExtensionDrawCosmCircle")); p2->setWhatsThis(QStringLiteral("TechDraw_ExtensionDrawCosmCircle")); QAction* p3 = pcAction->addAction(QString()); p3->setIcon(Gui::BitmapFactory().iconFromTheme("TechDraw_ExtensionDrawCosmCircle3Points")); p3->setObjectName(QStringLiteral("TechDraw_ExtensionDrawCosmCircle3Points")); p3->setWhatsThis(QStringLiteral("TechDraw_ExtensionDrawCosmCircle3Points")); QAction* p4 = pcAction->addAction(QString()); p4->setIcon(Gui::BitmapFactory().iconFromTheme("TechDraw_ExtensionDrawCosmArc")); p4->setObjectName(QStringLiteral("TechDraw_ExtensionDrawCosmArc")); p4->setWhatsThis(QStringLiteral("TechDraw_ExtensionDrawCosmArc")); _pcAction = pcAction; languageChange(); pcAction->setIcon(p1->icon()); int defaultId = 0; pcAction->setProperty("defaultAction", QVariant(defaultId)); return pcAction; } void CmdTechDrawExtensionDrawCirclesGroup::languageChange() { Command::languageChange(); if (!_pcAction) { return; } auto pcAction = qobject_cast(_pcAction); QList action = pcAction->actions(); QAction* p1 = action[0]; p1->setText(QApplication::translate("CmdTechDrawCosmeticCircle", "Cosmetic 1 Point Circle")); p1->setToolTip(QApplication::translate("CmdTechDrawCosmeticCircle", "Adds a cosmetic circle based on a selected centerpoint")); p1->setStatusTip(p1->text()); QAction* p2 = action[1]; p2->setText(QApplication::translate("CmdTechDrawExtensionDrawCosmCircle", "Cosmetic 2 Point Circle")); p2->setToolTip(QApplication::translate("CmdTechDrawExtensionDrawCosmCircle", "Adds a cosmetic circle based on two vertices, where " "the first selection is the centerpoint and the second is the radius")); p2->setStatusTip(p2->text()); QAction* p3 = action[2]; p3->setText(QApplication::translate("CmdTechDrawExtensionDrawCosmCircle3Points", "Cosmetic 3 Point Circle")); p3->setToolTip(QApplication::translate("CmdTechDrawExtensionDrawCosmCircle3Points", "Adds a cosmetic circle that passes through 3 selected perimeter points")); p3->setStatusTip(p3->text()); QAction* p4 = action[3]; p4->setText(QApplication::translate("CmdTechDrawExtensionDrawCosmArc", "Cosmetic Arc")); p4->setToolTip(QApplication::translate("CmdTechDrawExtensionDrawCosmArc", "Adds a cosmetic counter clockwise arc based on three vertices, " "where the first selection is the center point and the second is the radius and start point.")); p4->setStatusTip(p4->text()); } bool CmdTechDrawExtensionDrawCirclesGroup::isActive() { bool havePage = DrawGuiUtil::needPage(this); bool haveView = DrawGuiUtil::needView(this, true); return (havePage && haveView); } //=========================================================================== // TechDraw_ExtensionLineParallel //=========================================================================== void execLineParallelPerpendicular(Gui::Command* cmd, bool isParallel) { // create a line parallel or perpendicular to another line std::vector selection; TechDraw::DrawViewPart* objFeat{nullptr}; if (!_checkSel(cmd, selection, objFeat, QT_TRANSLATE_NOOP("Command","TechDraw Cosmetic Line Parallel/Perpendicular"))) { return; } Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Cosmetic Line Parallel/Perpendicular")); const std::vector SubNames = selection[0].getSubNames(); if (SubNames.size() >= 2) { std::string GeoType1 = TechDraw::DrawUtil::getGeomTypeFromName(SubNames[0]); std::string GeoType2 = TechDraw::DrawUtil::getGeomTypeFromName(SubNames[1]); int EdgeId{-1}; int VertId{-1}; if (GeoType1 == "Edge" && GeoType2 == "Vertex") { EdgeId = TechDraw::DrawUtil::getIndexFromName(SubNames[0]); VertId = TechDraw::DrawUtil::getIndexFromName(SubNames[1]); } else if (GeoType2 == "Edge" && GeoType1 == "Vertex") { EdgeId = TechDraw::DrawUtil::getIndexFromName(SubNames[1]); VertId = TechDraw::DrawUtil::getIndexFromName(SubNames[0]); } else { // we don't have an edge + vertex as selection return; } TechDraw::BaseGeomPtr geom1 = objFeat->getGeomByIndex(EdgeId); TechDraw::GenericPtr lineGen = std::static_pointer_cast(geom1); // ends are scaled and rotated Base::Vector3d lineStart = lineGen->points.at(0); lineStart = CosmeticVertex::makeCanonicalPointInverted(objFeat, lineStart); Base::Vector3d lineEnd = lineGen->points.at(1); lineEnd = CosmeticVertex::makeCanonicalPointInverted(objFeat, lineEnd); TechDraw::VertexPtr vert = objFeat->getProjVertexByIndex(VertId); Base::Vector3d vertexPoint(vert->point().x, vert->point().y, 0.0); vertexPoint = CosmeticVertex::makeCanonicalPointInverted(objFeat, vertexPoint); Base::Vector3d halfVector = (lineEnd - lineStart) / 2.0; if (!isParallel) { float dummy = halfVector.x; halfVector.x = -halfVector.y; halfVector.y = dummy; } Base::Vector3d startPoint = vertexPoint + halfVector; Base::Vector3d endPoint = vertexPoint - halfVector; TechDraw::BaseGeomPtr cLine = CosmeticEdge::makeLineFromCanonicalPoints(startPoint, endPoint); std::string lineTag = objFeat->addCosmeticEdge(cLine); TechDraw::CosmeticEdge* lineEdge = objFeat->getCosmeticEdge(lineTag); _setLineAttributes(lineEdge); objFeat->refreshCEGeoms(); objFeat->requestPaint(); cmd->getSelection().clearSelection(); } Gui::Command::commitCommand(); } DEF_STD_CMD_A(CmdTechDrawExtensionLineParallel) CmdTechDrawExtensionLineParallel::CmdTechDrawExtensionLineParallel() : Command("TechDraw_ExtensionLineParallel") { sAppModule = "TechDraw"; sGroup = QT_TR_NOOP("TechDraw"); sMenuText = QT_TR_NOOP("Cosmetic Parallel Line"); sToolTipText = QT_TR_NOOP("Adds a cosmetic circle to 3 selected vertices"); sWhatsThis = "TechDraw_ExtensionLineParallel"; sStatusTip = sMenuText; sPixmap = "TechDraw_ExtensionLineParallel"; } void CmdTechDrawExtensionLineParallel::activated(int iMsg) { Q_UNUSED(iMsg); execLineParallelPerpendicular(this, true); } bool CmdTechDrawExtensionLineParallel::isActive() { bool havePage = DrawGuiUtil::needPage(this); bool haveView = DrawGuiUtil::needView(this); return (havePage && haveView); } //=========================================================================== // TechDraw_ExtensionLinePerpendicular //=========================================================================== DEF_STD_CMD_A(CmdTechDrawExtensionLinePerpendicular) CmdTechDrawExtensionLinePerpendicular::CmdTechDrawExtensionLinePerpendicular() : Command("TechDraw_ExtensionLinePerpendicular") { sAppModule = "TechDraw"; sGroup = QT_TR_NOOP("TechDraw"); sMenuText = QT_TR_NOOP("Cosmetic Perpendicular Line"); sToolTipText = QT_TR_NOOP("Adds a cosmetic line perpendicular to the selected line through the selected vertex"); sWhatsThis = "TechDraw_ExtensionLinePerpendicular"; sStatusTip = sMenuText; sPixmap = "TechDraw_ExtensionLinePerpendicular"; } void CmdTechDrawExtensionLinePerpendicular::activated(int iMsg) { Q_UNUSED(iMsg); execLineParallelPerpendicular(this, false); } bool CmdTechDrawExtensionLinePerpendicular::isActive() { bool havePage = DrawGuiUtil::needPage(this); bool haveView = DrawGuiUtil::needView(this); return (havePage && haveView); } //=========================================================================== // TechDraw_ExtensionLinePPGroup //=========================================================================== DEF_STD_CMD_ACL(CmdTechDrawExtensionLinePPGroup) CmdTechDrawExtensionLinePPGroup::CmdTechDrawExtensionLinePPGroup() : Command("TechDraw_ExtensionLinePPGroup") { sAppModule = "TechDraw"; sGroup = QT_TR_NOOP("TechDraw"); sMenuText = QT_TR_NOOP("Cosmetic Parallel Line"); sToolTipText = QT_TR_NOOP("Adds a cosmetic line parallel to the selected line through the selected vertex"); sWhatsThis = "TechDraw_ExtensionLinePPGroup"; sStatusTip = sMenuText; } void CmdTechDrawExtensionLinePPGroup::activated(int iMsg) { // Base::Console().message("CMD::ExtensionLinePPGroup - activated(%d)\n", 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; } auto pcAction = qobject_cast(_pcAction); pcAction->setIcon(pcAction->actions().at(iMsg)->icon()); switch (iMsg) { case 0://create parallel line execLineParallelPerpendicular(this, true); break; case 1://create perpendicular line execLineParallelPerpendicular(this, false); break; default: Base::Console().message("CMD::CVGrp - invalid iMsg: %d\n", iMsg); }; } Gui::Action* CmdTechDrawExtensionLinePPGroup::createAction() { Gui::ActionGroup* pcAction = new Gui::ActionGroup(this, Gui::getMainWindow()); pcAction->setDropDownMenu(true); applyCommandData(this->className(), pcAction); QAction* p1 = pcAction->addAction(QString()); p1->setIcon(Gui::BitmapFactory().iconFromTheme("TechDraw_ExtensionLineParallel")); p1->setObjectName(QStringLiteral("TechDraw_ExtensionLineParallel")); p1->setWhatsThis(QStringLiteral("TechDraw_ExtensionLineParallel")); QAction* p2 = pcAction->addAction(QString()); p2->setIcon(Gui::BitmapFactory().iconFromTheme("TechDraw_ExtensionLinePerpendicular")); p2->setObjectName(QStringLiteral("TechDraw_ExtensionLinePerpendicular")); p2->setWhatsThis(QStringLiteral("TechDraw_ExtensionLinePerpendicular")); _pcAction = pcAction; languageChange(); pcAction->setIcon(p1->icon()); int defaultId = 0; pcAction->setProperty("defaultAction", QVariant(defaultId)); return pcAction; } void CmdTechDrawExtensionLinePPGroup::languageChange() { Command::languageChange(); if (!_pcAction) { return; } Gui::ActionGroup* pcAction = qobject_cast(_pcAction); QList action = pcAction->actions(); QAction* arc1 = action[0]; arc1->setText( QApplication::translate("CmdTechDrawExtensionLineParallel", "Cosmetic Parallel Line")); arc1->setToolTip( QApplication::translate("CmdTechDrawExtensionLineParallel", "Adds a cosmetic line parallel to the selected line through the selected vertex")); arc1->setStatusTip(arc1->text()); QAction* arc2 = action[1]; arc2->setText(QApplication::translate("CmdTechDrawExtensionLinePerpendicular", "Cosmetic Perpendicular Line")); arc2->setToolTip(QApplication::translate( "CmdTechDrawExtensionLinePerpendicular", "Adds a cosmetic line perpendicular to the selected line through the selected vertex")); arc2->setStatusTip(arc2->text()); } bool CmdTechDrawExtensionLinePPGroup::isActive() { bool havePage = DrawGuiUtil::needPage(this); bool haveView = DrawGuiUtil::needView(this, true); return (havePage && haveView); } //=========================================================================== // TechDraw_ExtensionLockUnlockView //=========================================================================== DEF_STD_CMD_A(CmdTechDrawExtensionLockUnlockView) CmdTechDrawExtensionLockUnlockView::CmdTechDrawExtensionLockUnlockView() : Command("TechDraw_ExtensionLockUnlockView") { sAppModule = "TechDraw"; sGroup = QT_TR_NOOP("TechDraw"); sMenuText = QT_TR_NOOP("Toggle View Lock"); sToolTipText = QT_TR_NOOP("Locks or unlocks the position of the selected views"); sWhatsThis = "TechDraw_ExtensionLockUnlockView"; sStatusTip = sMenuText; sPixmap = "TechDraw_ExtensionLockUnlockView"; } void CmdTechDrawExtensionLockUnlockView::activated(int iMsg) { Q_UNUSED(iMsg); std::vector selection = getSelection().getSelectionEx(); if (selection.empty()) { return; } Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Lock/Unlock View")); for (auto& sel : selection) { auto* obj = static_cast(sel.getObject()); if (obj->isDerivedFrom()) { bool lockPosition = obj->LockPosition.getValue(); lockPosition = !lockPosition; obj->LockPosition.setValue(lockPosition); } } Gui::Command::commitCommand(); } bool CmdTechDrawExtensionLockUnlockView::isActive() { bool havePage = DrawGuiUtil::needPage(this); bool haveView = DrawGuiUtil::needView(this); return (havePage && haveView); } //=========================================================================== // TechDraw_ExtensionExtendLine //=========================================================================== void execExtendShortenLine(Gui::Command* cmd, bool extend) { // extend or shorten a cosmetic line or a centerline std::vector selection; TechDraw::DrawViewPart* objFeat{nullptr}; if (!_checkSel(cmd, selection, objFeat, QT_TRANSLATE_NOOP("Command","TechDraw Extend/Shorten Line"))) { return; } Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Extend/shorten line")); const std::vector subNames = selection[0].getSubNames(); if (!subNames.empty()) { std::string name = subNames[0]; int num = DrawUtil::getIndexFromName(name); std::string geoType = TechDraw::DrawUtil::getGeomTypeFromName(name); if (geoType == "Edge") { TechDraw::BaseGeomPtr baseGeo = objFeat->getGeomByIndex(num); if (baseGeo) { if (baseGeo->getGeomType() == GeomType::GENERIC) { // start and end points are geometry points and are scaled, rotated and inverted // convert start and end to unscaled, unrotated. Base::Vector3d P0 = CosmeticVertex::makeCanonicalPointInverted(objFeat, baseGeo->getStartPoint()); Base::Vector3d P1 = CosmeticVertex::makeCanonicalPointInverted(objFeat, baseGeo->getEndPoint()); bool isCenterLine = false; TechDraw::CenterLine* centerEdge = nullptr; if (baseGeo->getCosmetic()) { std::string uniTag = baseGeo->getCosmeticTag(); int oldStyle = 1; float oldWeight = 1.0; Base::Color oldColor; std::vector toDelete; toDelete.push_back(uniTag); if (baseGeo->source() == SourceType::COSMETICEDGE) { // cosmetic edge auto cosEdge = objFeat->getCosmeticEdge(uniTag); oldStyle = cosEdge->m_format.getLineNumber(); oldWeight = cosEdge->m_format.getWidth(); oldColor = cosEdge->m_format.getColor(); objFeat->removeCosmeticEdge(toDelete); } else if (baseGeo->source() == SourceType::CENTERLINE) { // centerline isCenterLine = true; centerEdge = objFeat->getCenterLine(uniTag); } Base::Vector3d direction = (P1 - P0).Normalize(); Base::Vector3d delta = direction * activeDimAttributes.getLineStretch(); Base::Vector3d startPt, endPt; if (extend) { // make it longer startPt = P0 - delta; endPt = P1 + delta; } else { // make it shorter startPt = P0 + delta; endPt = P1 - delta; } // startPt.y = -startPt.y; // endPt.y = -endPt.y; if (isCenterLine) { if (extend) { centerEdge->m_extendBy += activeDimAttributes.getLineStretch(); } else { centerEdge->m_extendBy -= activeDimAttributes.getLineStretch(); } objFeat->refreshCLGeoms(); } else { std::string lineTag = objFeat->addCosmeticEdge(startPt, endPt); TechDraw::CosmeticEdge* lineEdge = objFeat->getCosmeticEdge(lineTag); _setLineAttributes(lineEdge, oldStyle, oldWeight, oldColor); objFeat->refreshCEGeoms(); } objFeat->requestPaint(); } } } } } Gui::Command::commitCommand(); } DEF_STD_CMD_A(CmdTechDrawExtensionExtendLine) CmdTechDrawExtensionExtendLine::CmdTechDrawExtensionExtendLine() : Command("TechDraw_ExtensionExtendLine") { sAppModule = "TechDraw"; sGroup = QT_TR_NOOP("TechDraw"); sMenuText = QT_TR_NOOP("Extend Line"); sToolTipText = QT_TR_NOOP("Extends a selected cosmetic line or centerline at both ends by the specified delta distance"); sWhatsThis = "TechDraw_ExtensionExtendLine"; sStatusTip = sToolTipText; sPixmap = "TechDraw_ExtensionExtendLine"; } void CmdTechDrawExtensionExtendLine::activated(int iMsg) { Q_UNUSED(iMsg); execExtendShortenLine(this, true); ///Base::Console().message("ExtendLine started\n"); } bool CmdTechDrawExtensionExtendLine::isActive() { bool havePage = DrawGuiUtil::needPage(this); bool haveView = DrawGuiUtil::needView(this); return (havePage && haveView); } //=========================================================================== // TechDraw_ExtensionShortenLine //=========================================================================== DEF_STD_CMD_A(CmdTechDrawExtensionShortenLine) CmdTechDrawExtensionShortenLine::CmdTechDrawExtensionShortenLine() : Command("TechDraw_ExtensionShortenLine") { sAppModule = "TechDraw"; sGroup = QT_TR_NOOP("TechDraw"); sMenuText = QT_TR_NOOP("Shorten Line"); sToolTipText = QT_TR_NOOP("Shortens a selected cosmetic line or centerline at both ends by the specified delta distance"); sWhatsThis = "TechDraw_ExtensionShortenLine"; sStatusTip = sMenuText; sPixmap = "TechDraw_ExtensionShortenLine"; } void CmdTechDrawExtensionShortenLine::activated(int iMsg) { Q_UNUSED(iMsg); execExtendShortenLine(this, false); ///Base::Console().message("ShortenLine started\n"); } bool CmdTechDrawExtensionShortenLine::isActive() { bool havePage = DrawGuiUtil::needPage(this); bool haveView = DrawGuiUtil::needView(this); return (havePage && haveView); } //=========================================================================== // TechDraw_ExtensionExtendShortenLineGroup //=========================================================================== DEF_STD_CMD_ACL(CmdTechDrawExtendShortenLineGroup) CmdTechDrawExtendShortenLineGroup::CmdTechDrawExtendShortenLineGroup() : Command("TechDraw_ExtensionExtendShortenLineGroup") { sAppModule = "TechDraw"; sGroup = QT_TR_NOOP("TechDraw"); sMenuText = QT_TR_NOOP("Extend Line"); sToolTipText = QT_TR_NOOP("Extends a selected cosmetic line or centerline at both ends by the specified delta distance"); sWhatsThis = "TechDraw_ExtensionExtendShortenLineGroup"; sStatusTip = sMenuText; } void CmdTechDrawExtendShortenLineGroup::activated(int iMsg) { // Base::Console().message("CMD::ExtendShortenLineGroup - activated(%d)\n", 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; } auto pcAction = qobject_cast(_pcAction); pcAction->setIcon(pcAction->actions().at(iMsg)->icon()); switch (iMsg) { case 0://extend a line execExtendShortenLine(this, true); break; case 1://shorten line execExtendShortenLine(this, false); break; default: Base::Console().message("CMD::CVGrp - invalid iMsg: %d\n", iMsg); }; } Gui::Action* CmdTechDrawExtendShortenLineGroup::createAction() { Gui::ActionGroup* pcAction = new Gui::ActionGroup(this, Gui::getMainWindow()); pcAction->setDropDownMenu(true); applyCommandData(this->className(), pcAction); QAction* p1 = pcAction->addAction(QString()); p1->setIcon(Gui::BitmapFactory().iconFromTheme("TechDraw_ExtensionExtendLine")); p1->setObjectName(QStringLiteral("TechDraw_ExtensionExtendLine")); p1->setWhatsThis(QStringLiteral("TechDraw_ExtensionExtendLine")); QAction* p2 = pcAction->addAction(QString()); p2->setIcon(Gui::BitmapFactory().iconFromTheme("TechDraw_ExtensionShortenLine")); p2->setObjectName(QStringLiteral("TechDraw_ExtensionShortenLine")); p2->setWhatsThis(QStringLiteral("TechDraw_ExtensionShortenLine")); _pcAction = pcAction; languageChange(); pcAction->setIcon(p1->icon()); int defaultId = 0; pcAction->setProperty("defaultAction", QVariant(defaultId)); return pcAction; } void CmdTechDrawExtendShortenLineGroup::languageChange() { Command::languageChange(); if (!_pcAction) { return; } Gui::ActionGroup* pcAction = qobject_cast(_pcAction); QList action = pcAction->actions(); QAction* arc1 = action[0]; arc1->setText(QApplication::translate("CmdTechDrawExtensionExtendLine", "Extend Line")); arc1->setToolTip(QApplication::translate( "CmdTechDrawExtensionExtendLine", "Extends a selected cosmetic line or centerline at both ends by the specified delta distance")); arc1->setStatusTip(arc1->text()); QAction* arc2 = action[1]; arc2->setText(QApplication::translate("CmdTechDrawExtensionShortenLine", "Shorten Line")); arc2->setToolTip(QApplication::translate( "CmdTechDrawExtensionShortenLine", "Shortens a selected cosmetic line or centerline at both ends by the specified delta distance")); arc2->setStatusTip(arc2->text()); } bool CmdTechDrawExtendShortenLineGroup::isActive() { bool havePage = DrawGuiUtil::needPage(this); bool haveView = DrawGuiUtil::needView(this, true); return (havePage && haveView); } //=========================================================================== // TechDraw_ExtensionAreaAnnotation //=========================================================================== DEF_STD_CMD_A(CmdTechDrawExtensionAreaAnnotation) CmdTechDrawExtensionAreaAnnotation::CmdTechDrawExtensionAreaAnnotation() : Command("TechDraw_ExtensionAreaAnnotation") { sAppModule = "TechDraw"; sGroup = QT_TR_NOOP("TechDraw"); sMenuText = QT_TR_NOOP("Area Annotation"); sToolTipText = QT_TR_NOOP("Calculates the area of multiple selected faces"); sWhatsThis = "TechDraw_ExtensionAreaAnnotation"; sStatusTip = sToolTipText; sPixmap = "TechDraw_ExtensionAreaAnnotation"; } void CmdTechDrawExtensionAreaAnnotation::activated(int iMsg) // calculate the area of selected faces, create output in a balloon { Q_UNUSED(iMsg); std::vector selection; TechDraw::DrawViewPart* objFeat{nullptr}; if (!_checkSel(this, selection, objFeat, QT_TRANSLATE_NOOP("Command","TechDraw Calculate Selected Area"))) { return; } // we must have at least 1 face in the selection const std::vector subNamesAll = selection[0].getSubNames(); std::vector subNames; for (auto& name : subNamesAll) { std::string geomType = DrawUtil::getGeomTypeFromName(name); if (geomType == "Face") { subNames.push_back(name); } } if (subNames.empty()) { QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Incorrect selection"), QObject::tr("No faces in selection")); return; } // we have at least 1 face Base::Vector3d center; double totalArea = 0.0; for (const std::string& name : subNames) { TechDraw::FacePtr face = objFeat->getFace(name); if (!face) { continue; } GProp_GProps faceProps; BRepGProp::SurfaceProperties(face->toOccFace(), faceProps); double faceArea = faceProps.Mass(); totalArea += faceArea; center += faceArea*Base::convertTo(faceProps.CentreOfMass()); } if (totalArea > 0.0) { center /= totalArea; } // if area calculation was successful, start the command Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Calculate Face Area")); // at first we create the balloon std::string balloonName = _createBalloon(this, objFeat); TechDraw::DrawViewBalloon* balloon = nullptr; balloon = dynamic_cast( this->getDocument()->getObject(balloonName.c_str())); if (!balloon) { throw Base::TypeError("CmdTechDrawNewBalloon - balloon not found\n"); } // the balloon has been created successfully // calculate needed variables double scale = objFeat->getScale(); center = DrawUtil::invertY(center/scale); double scale2 = scale * scale; totalArea = totalArea / scale2;//convert from view scale to internal mm2 //make area unit-aware Base::Quantity asQuantity; asQuantity.setValue(totalArea); asQuantity.setUnit(Base::Unit::Area); QString qUserString = QString::fromStdString(asQuantity.getUserString()); if (qUserString.endsWith(QStringLiteral("^2"))) { qUserString.chop(2); qUserString.append(QStringLiteral("²")); } std::string sUserString = qUserString.toStdString(); // set the attributes in the data tab's fields // balloon->SourceView.setValue(objFeat); balloon->BubbleShape.setValue("Rectangle"); balloon->EndType.setValue("None"); balloon->KinkLength.setValue(0.0); balloon->X.setValue(center.x); balloon->Y.setValue(center.y); balloon->OriginX.setValue(center.x); balloon->OriginY.setValue(center.y); balloon->ScaleType.setValue("Page"); balloon->Text.setValue(sUserString); // look for the ballons's view provider TechDraw::DrawPage* page = objFeat->findParentPage(); Gui::Document* guiDoc = Gui::Application::Instance->getDocument(page->getDocument()); auto viewProvider = static_cast(guiDoc->getViewProvider(balloon)); if (viewProvider) { // view provider successfully found, // set the attributes in the view tab's fields viewProvider->Fontsize.setValue(2.0); viewProvider->LineWidth.setValue(TechDraw::LineGroup::getDefaultWidth("Graphic")); viewProvider->LineVisible.setValue(false); viewProvider->Color.setValue(Base::Color(1.0, 0.0, 0.0)); } Gui::Command::commitCommand(); objFeat->touch(true); Gui::Command::updateActive(); } bool CmdTechDrawExtensionAreaAnnotation::isActive() { bool havePage = DrawGuiUtil::needPage(this); bool haveView = DrawGuiUtil::needView(this); return (havePage && haveView); } //=========================================================================== // TechDraw_ExtensionArcLengthAnnotation //=========================================================================== DEF_STD_CMD_A(CmdTechDrawExtensionArcLengthAnnotation) CmdTechDrawExtensionArcLengthAnnotation::CmdTechDrawExtensionArcLengthAnnotation() : Command("TechDraw_ExtensionArcLengthAnnotation") { sAppModule = "TechDraw"; sGroup = QT_TR_NOOP("TechDraw"); sMenuText = QT_TR_NOOP("Arc Length Annotation"); sToolTipText = QT_TR_NOOP("Inserts an annotation with the calculated arc length of the selected edges"); sWhatsThis = "TechDraw_ExtensionArcLengthAnnotation"; sStatusTip = sToolTipText; sPixmap = "TechDraw_ExtensionArcLengthAnnotation"; } void CmdTechDrawExtensionArcLengthAnnotation::activated(int iMsg) // Calculate the arc length of selected edge and create a balloon holding the datum { Q_UNUSED(iMsg); std::vector selection; TechDraw::DrawViewPart *objFeat{nullptr}; if (!_checkSel(this, selection, objFeat, QT_TRANSLATE_NOOP("Command", "TechDraw Calculate Selected Arc Length"))) { return; } // Collect all edges in the selection std::vector subNames; for (auto &name : selection[0].getSubNames()) { if (DrawUtil::getGeomTypeFromName(name) == "Edge") { subNames.push_back(name); } } if (subNames.empty()) { QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Incorrect selection"), QObject::tr("No edges in selection")); return; } // Now we have at least one edge std::vector lengths(subNames.size()); double totalLength = 0.0; for (size_t iName = 0; iName < subNames.size(); ++iName) { lengths[iName] = totalLength; TechDraw::BaseGeomPtr edge = objFeat->getEdge(subNames[iName]); if (!edge) { continue; } GProp_GProps edgeProps; BRepGProp::LinearProperties(edge->getOCCEdge(), edgeProps); totalLength += edgeProps.Mass(); lengths[iName] = totalLength; } // We have calculated the length, let's start the command Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Calculate Edge Length")); // First we need to create the balloon std::string balloonName = _createBalloon(this, objFeat); auto balloon = dynamic_cast(getDocument()->getObject(balloonName.c_str())); if (!balloon) { throw Base::TypeError("CmdTechDrawNewBalloon - balloon not found\n"); } // Find the edge halving the selected path and the offset from its starting point double anchorLength = totalLength*0.5; size_t iLength = 0; while (iLength < lengths.size() && lengths[iLength] < anchorLength) { ++iLength; } if (iLength > 0) { anchorLength -= lengths[iLength - 1]; } // As reasonable anchor base point seems the "halving" edge endpoint BRepAdaptor_Curve curve(objFeat->getEdge(subNames[iLength])->getOCCEdge()); gp_Pnt midPoint; curve.D0(curve.LastParameter(), midPoint); // Now try to get the real path center which lies anchorLength from edge start point GCPnts_AbscissaPoint abscissa(Precision::Confusion(), curve, anchorLength, curve.FirstParameter()); if (abscissa.IsDone()) { curve.D0(abscissa.Parameter(), midPoint); } double scale = objFeat->getScale(); Base::Vector3d anchor = DrawUtil::invertY(Base::convertTo(midPoint)/scale); totalLength /= scale; // Use virtual dimension view helper to format resulting value TechDraw::DrawViewDimension helperDim; using Format = DimensionFormatter::Format; std::string valueStr = helperDim.formatValue(totalLength, QString::fromUtf8(helperDim.FormatSpec.getStrValue().data()), helperDim.isMultiValueSchema() ? Format::UNALTERED : Format::FORMATTED); balloon->Text.setValue("◠ " + valueStr); // Set balloon format to be referencing dimension-like int stdStyle = Preferences::getPreferenceGroup("Dimensions")->GetInt("StandardAndStyle", ViewProviderDimension::STD_STYLE_ISO_ORIENTED); bool asmeStyle = stdStyle == ViewProviderDimension::STD_STYLE_ASME_INLINED || stdStyle == ViewProviderDimension::STD_STYLE_ASME_REFERENCING; balloon->BubbleShape.setValue(asmeStyle ? "None" : "Line"); balloon->EndType.setValue(Preferences::getPreferenceGroup("Dimensions")->GetInt("ArrowStyle", 0)); balloon->OriginX.setValue(anchor.x); balloon->OriginY.setValue(anchor.y); // Set balloon label position a bit upwards and to the right, as QGSPage::createBalloon does double textOffset = 20.0/scale; balloon->X.setValue(anchor.x + textOffset); balloon->Y.setValue(anchor.y + textOffset); // Adjust the kink length accordingly to the standard used auto viewProvider = dynamic_cast(Gui::Application::Instance->getViewProvider(balloon)); if (viewProvider) { balloon->KinkLength.setValue((asmeStyle ? 12.0 : 1.0)*viewProvider->LineWidth.getValue()); } // Close the command and update the view Gui::Command::commitCommand(); objFeat->touch(true); Gui::Command::updateActive(); } bool CmdTechDrawExtensionArcLengthAnnotation::isActive() { bool havePage = DrawGuiUtil::needPage(this); bool haveView = DrawGuiUtil::needView(this); return (havePage && haveView); } //=========================================================================== // internal helper routines //=========================================================================== namespace TechDrawGui { LineFormat& _getActiveLineAttributes() { return LineFormat::getCurrentLineFormat(); } std::string _createBalloon(Gui::Command* cmd, TechDraw::DrawViewPart* objFeat) // create a new balloon, return its name as string { std::string featName; TechDraw::DrawPage* page = objFeat->findParentPage(); Gui::Document* guiDoc = Gui::Application::Instance->getDocument(page->getDocument()); auto pageVP = freecad_cast(guiDoc->getViewProvider(page)); if (pageVP) { QGSPage* scenePage = pageVP->getQGSPage(); featName = scenePage->getDrawPage()->getDocument()->getUniqueObjectName("Balloon"); std::string pageName = scenePage->getDrawPage()->getNameInDocument(); cmd->doCommand(cmd->Doc, "App.activeDocument().addObject('TechDraw::DrawViewBalloon', '%s')", featName.c_str()); cmd->doCommand(cmd->Doc, "App.activeDocument().%s.SourceView = (App.activeDocument().%s)", featName.c_str(), objFeat->getNameInDocument()); cmd->doCommand(cmd->Doc, "App.activeDocument().%s.addView(App.activeDocument().%s)", pageName.c_str(), featName.c_str()); } return featName; } bool _checkSel(Gui::Command* cmd, std::vector& selection, TechDraw::DrawViewPart*& objFeat, const std::string& message) { // check selection of getSelectionEx() and selection[0].getObject() selection = cmd->getSelection().getSelectionEx(); if (selection.empty()) { // message is translated in caller QMessageBox::warning(Gui::getMainWindow(), QString::fromUtf8(message.c_str()), QObject::tr("Selection is empty")); return false; } objFeat = dynamic_cast(selection[0].getObject()); if (!objFeat) { QMessageBox::warning(Gui::getMainWindow(), QString::fromUtf8(message.c_str()), QObject::tr("No object selected")); return false; } return true; } //! return the vertices in the selection as [Base::Vector3d] std::vector _getVertexPoints(const std::vector& SubNames, TechDraw::DrawViewPart* objFeat) { std::vector vertexPoints; for (const std::string& Name : SubNames) { std::string GeoType = TechDraw::DrawUtil::getGeomTypeFromName(Name); if (GeoType == "Vertex") { int GeoId = TechDraw::DrawUtil::getIndexFromName(Name); TechDraw::VertexPtr vert = objFeat->getProjVertexByIndex(GeoId); vertexPoints.push_back(vert->point()); } } return vertexPoints; } //! get angle between x-axis and the vector from center to point. //! result is [0, 360] double _getAngle(Base::Vector3d center, Base::Vector3d point) { constexpr double DegreesHalfCircle{180.0}; Base::Vector3d vecCP = point - center; double angle = DU::angleWithX(vecCP) * DegreesHalfCircle / std::numbers::pi; return angle; } Base::Vector3d _circleCenter(Base::Vector3d p1, Base::Vector3d p2, Base::Vector3d p3) { Base::Vector2d v1(p1.x, p1.y); Base::Vector2d v2(p2.x, p2.y); Base::Vector2d v3(p3.x, p3.y); Base::Vector2d center = Part::Geom2dCircle::getCircleCenter(v1, v2, v3); return Base::Vector3d(center.x, center.y, 0.0); } void _createThreadCircle(const std::string Name, TechDraw::DrawViewPart* objFeat, double factor) { constexpr double ArcStartDegree{255.0}; constexpr double ArcEndDegree{165.0}; // create the 3/4 arc symbolizing a thread from top seen double scale = objFeat->getScale(); int GeoId = TechDraw::DrawUtil::getIndexFromName(Name); TechDraw::BaseGeomPtr geom = objFeat->getGeomByIndex(GeoId); std::string GeoType = TechDraw::DrawUtil::getGeomTypeFromName(Name); if (GeoType == "Edge" && geom->getGeomType() == GeomType::CIRCLE) { TechDraw::CirclePtr cgen = std::static_pointer_cast(geom); // center is rotated and scaled Base::Vector3d center = CosmeticVertex::makeCanonicalPointInverted(objFeat, cgen->center); // radius is scaled float radius = cgen->radius * factor / scale; TechDraw::BaseGeomPtr threadArc = std::make_shared(center, radius, ArcStartDegree, ArcEndDegree); std::string arcTag = objFeat->addCosmeticEdge(threadArc); TechDraw::CosmeticEdge* arc = objFeat->getCosmeticEdge(arcTag); _setLineAttributes(arc); } } void _createThreadLines(const std::vector& SubNames, TechDraw::DrawViewPart* objFeat, double factor) { // create symbolizing lines of a thread from the side seen std::string GeoType0 = TechDraw::DrawUtil::getGeomTypeFromName(SubNames[0]); std::string GeoType1 = TechDraw::DrawUtil::getGeomTypeFromName(SubNames[1]); if ((GeoType0 == "Edge") && (GeoType1 == "Edge")) { int GeoId0 = TechDraw::DrawUtil::getIndexFromName(SubNames[0]); int GeoId1 = TechDraw::DrawUtil::getIndexFromName(SubNames[1]); TechDraw::BaseGeomPtr geom0 = objFeat->getGeomByIndex(GeoId0); TechDraw::BaseGeomPtr geom1 = objFeat->getGeomByIndex(GeoId1); if (geom0->getGeomType() != GeomType::GENERIC || geom1->getGeomType() != GeomType::GENERIC) { QMessageBox::warning(Gui::getMainWindow(), QObject::tr("TechDraw thread hole side"), QObject::tr("Select 2 straight lines")); return; } TechDraw::GenericPtr line0 = std::static_pointer_cast(geom0); TechDraw::GenericPtr line1 = std::static_pointer_cast(geom1); // start and end points are scaled,rotated and inverted (CSRIx). // convert start and end to unscaled, unrotated. Base::Vector3d start0 = CosmeticVertex::makeCanonicalPointInverted(objFeat, line0->getStartPoint()); Base::Vector3d start1 = CosmeticVertex::makeCanonicalPointInverted(objFeat, line1->getStartPoint()); Base::Vector3d end0 = CosmeticVertex::makeCanonicalPointInverted(objFeat, line0->getEndPoint()); Base::Vector3d end1 = CosmeticVertex::makeCanonicalPointInverted(objFeat, line1->getEndPoint()); if (DrawUtil::circulation(start0, end0, start1) != DrawUtil::circulation(end0, end1, start1)) { Base::Vector3d help1 = start1; Base::Vector3d help2 = end1; start1 = help2; end1 = help1; } float kernelDiam = (start1 - start0).Length(); float kernelFactor = (kernelDiam * factor - kernelDiam) / 2; Base::Vector3d delta = (start1 - start0).Normalize() * kernelFactor; std::string line0Tag = objFeat->addCosmeticEdge(start0 - delta, end0 - delta); std::string line1Tag = objFeat->addCosmeticEdge(start1 + delta, end1 + delta); TechDraw::CosmeticEdge* cosTag0 = objFeat->getCosmeticEdge(line0Tag); TechDraw::CosmeticEdge* cosTag1 = objFeat->getCosmeticEdge(line1Tag); _setLineAttributes(cosTag0); _setLineAttributes(cosTag1); } } void _setLineAttributes(TechDraw::CosmeticEdge* cosEdge) { // set line attributes of a cosmetic edge cosEdge->m_format.setStyle(_getActiveLineAttributes().getStyle()); cosEdge->m_format.setWidth(_getActiveLineAttributes().getWidth()); cosEdge->m_format.setColor(_getActiveLineAttributes().getColor()); cosEdge->m_format.setVisible(_getActiveLineAttributes().getVisible()); cosEdge->m_format.setLineNumber(_getActiveLineAttributes().getLineNumber()); } void _setLineAttributes(TechDraw::CenterLine* cosEdge) { // set line attributes of a cosmetic edge cosEdge->m_format.setStyle(_getActiveLineAttributes().getStyle()); cosEdge->m_format.setWidth(_getActiveLineAttributes().getWidth()); cosEdge->m_format.setColor(_getActiveLineAttributes().getColor()); cosEdge->m_format.setVisible(_getActiveLineAttributes().getVisible()); cosEdge->m_format.setLineNumber(_getActiveLineAttributes().getLineNumber()); } void _setLineAttributes(TechDraw::CosmeticEdge* cosEdge, int style, float weight, Base::Color color) { // set line attributes of a cosmetic edge cosEdge->m_format.setStyle(style); cosEdge->m_format.setWidth(weight); cosEdge->m_format.setColor(color); cosEdge->m_format.setVisible(_getActiveLineAttributes().getVisible()); cosEdge->m_format.setLineNumber(LineGenerator::fromQtStyle((Qt::PenStyle)style)); } void _setLineAttributes(TechDraw::CenterLine* cosEdge, int style, float weight, Base::Color color) { // set line attributes of a centerline cosEdge->m_format.setStyle(style); cosEdge->m_format.setWidth(weight); cosEdge->m_format.setColor(color); cosEdge->m_format.setVisible(_getActiveLineAttributes().getVisible()); cosEdge->m_format.setLineNumber(style);} }// namespace TechDrawGui //------------------------------------------------------------------------------ void CreateTechDrawCommandsExtensions() { Gui::CommandManager& rcCmdMgr = Gui::Application::Instance->commandManager(); rcCmdMgr.addCommand(new CmdTechDrawExtensionSelectLineAttributes()); rcCmdMgr.addCommand(new CmdTechDrawExtendShortenLineGroup()); rcCmdMgr.addCommand(new CmdTechDrawExtensionExtendLine()); rcCmdMgr.addCommand(new CmdTechDrawExtensionShortenLine()); rcCmdMgr.addCommand(new CmdTechDrawExtensionLockUnlockView()); rcCmdMgr.addCommand(new CmdTechDrawExtensionChangeLineAttributes()); rcCmdMgr.addCommand(new CmdTechDrawExtensionCircleCenterLinesGroup()); rcCmdMgr.addCommand(new CmdTechDrawExtensionCircleCenterLines()); rcCmdMgr.addCommand(new CmdTechDrawExtensionHoleCircle()); rcCmdMgr.addCommand(new CmdTechDrawExtensionVertexAtIntersection()); rcCmdMgr.addCommand(new CmdTechDrawExtensionDrawCirclesGroup()); rcCmdMgr.addCommand(new CmdTechDrawCosmeticCircle()); rcCmdMgr.addCommand(new CmdTechDrawExtensionDrawCosmCircle()); rcCmdMgr.addCommand(new CmdTechDrawExtensionDrawCosmArc()); rcCmdMgr.addCommand(new CmdTechDrawExtensionDrawCosmCircle3Points()); rcCmdMgr.addCommand(new CmdTechDrawExtensionLinePPGroup()); rcCmdMgr.addCommand(new CmdTechDrawExtensionLineParallel()); rcCmdMgr.addCommand(new CmdTechDrawExtensionLinePerpendicular()); rcCmdMgr.addCommand(new CmdTechDrawExtensionThreadsGroup()); rcCmdMgr.addCommand(new CmdTechDrawExtensionThreadHoleSide()); rcCmdMgr.addCommand(new CmdTechDrawExtensionThreadBoltSide()); rcCmdMgr.addCommand(new CmdTechDrawExtensionThreadHoleBottom()); rcCmdMgr.addCommand(new CmdTechDrawExtensionThreadBoltBottom()); rcCmdMgr.addCommand(new CmdTechDrawExtensionAreaAnnotation()); rcCmdMgr.addCommand(new CmdTechDrawExtensionArcLengthAnnotation()); }