/*************************************************************************** * Copyright (c) 2014 Luke Parry * * Copyright (c) 2022 WandererFan * * * * 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 "CommandExtensionDims.h" #include "DimensionValidators.h" #include "DrawGuiUtil.h" #include "QGIDatumLabel.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" using namespace TechDrawGui; using namespace TechDraw; using namespace std; using DimensionType = TechDraw::DrawViewDimension::DimensionType; using DimensionGeometry = TechDraw::DimensionGeometry; //=========================================================================== // utility routines //=========================================================================== //internal functions bool _checkSelection(Gui::Command* cmd, unsigned maxObjs = 2); bool _checkDrawViewPart(Gui::Command* cmd); bool isDimCmdActive(Gui::Command* cmd) { bool havePage = DrawGuiUtil::needPage(cmd); bool haveView = DrawGuiUtil::needView(cmd); return (havePage && haveView); } void execDistance(Gui::Command* cmd); void execDistanceX(Gui::Command* cmd); void execDistanceY(Gui::Command* cmd); 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 execDim(Gui::Command* cmd, std::string type, StringVector acceptableGeometry, std::vector minimumCounts, std::vector acceptableDimensionGeometrys); 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, int indexOffset = 0); void activateHandler(TechDrawHandler* newHandler) { std::unique_ptr ptr(newHandler); auto* mdi = qobject_cast(Gui::getMainWindow()->activeWindow()); if (!mdi) { return; } ViewProviderPage* vp = mdi->getViewProviderPage(); if (!vp) { return; } QGVPage* viewPage = vp->getQGVPage(); if (!viewPage) { return; } viewPage->activateHandler(ptr.release()); } //=========================================================================== // TechDraw_Dimension //=========================================================================== 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 = qobject_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 = qobject_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 = freecad_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 = freecad_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 = static_cast(scenePos.y()) + i * static_cast(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 = static_cast(scenePos.x()) + i * static_cast(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 { 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; 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; //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 { if (msg.Type == Gui::SelectionChanges::ClrSelection) { return; } App::Document* pageDoc = nullptr; if (auto page = getPage()) { pageDoc = page->getDocument(); } if (msg.Object.getObjectName().empty() || (msg.Object.getDocument() != pageDoc)) { if (msg.Type == Gui::SelectionChanges::AddSelection) { Gui::Selection().rmvSelection(msg.pDocName, msg.pObjectName, msg.pSubName); } 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 QStringLiteral("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() { 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() == GeomType::GENERIC) { TechDraw::GenericPtr gen1 = std::static_pointer_cast(geom); if (gen1->points.size() < 2) { return emptyVector; } return selLine; } else if (geom->getGeomType() == GeomType::CIRCLE || geom->getGeomType() == GeomType::ARCOFCIRCLE) { return selCircleArc; } else if (geom->getGeomType() == GeomType::ELLIPSE || geom->getGeomType() == GeomType::ARCOFELLIPSE) { return selEllipseArc; } else if (geom->getGeomType() == GeomType::BSPLINE) { return selSplineAndCo; } } else if (geomName == "Vertex") { return selPoints; } return emptyVector; } 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; GeomSelectionSizes selection(selPoints.size(), selLine.size(), selCircleArc.size(), selEllipseArc.size(), selSplineAndCo.size(), selFaces.size()); if (selection.hasFaces()) { makeCts_Faces(selAllowed); if (!selection.has1Face()) { Base::Console().warning("Multiple faces are selected. Using first.\n"); } } 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) { createRadiusDiameterDimension(selCircleArc[0], true); selAllowed = true; } if (availableDimension == AvailableDimension::SECOND) { createRadiusDiameterDimension(selCircleArc[0], false); if (selCircleArc[0].geomEdgeType() != GeomType::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) { createRadiusDiameterDimension(selEllipseArc[0], true); selAllowed = true; } if (availableDimension == AvailableDimension::SECOND) { createRadiusDiameterDimension(selEllipseArc[0], false); if (selEllipseArc[0].geomEdgeType() != GeomType::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) { int GeoId(TechDraw::DrawUtil::getIndexFromName(ref.getSubName())); TechDraw::BaseGeomPtr geom = partFeat->getGeomByIndex(GeoId); bool isCircleGeom = (geom->getGeomType() == GeomType::CIRCLE) || (geom->getGeomType() == GeomType::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)) { restartCommand(QT_TRANSLATE_NOOP("Command", "Add radius dimension")); dim = dimMaker(partFeat, "Radius", { ref }, {}); } else { restartCommand(QT_TRANSLATE_NOOP("Command", "Add diameter dimension")); dim = dimMaker(partFeat, "Diameter", { ref }, {}); } dims.push_back(dim); moveDimension(mousePos, dim); } void createAngleDimension(ReferenceEntry ref1, ReferenceEntry ref2) { if (TechDraw::isValidMultiEdge({ ref1, ref2 }) != DimensionGeometry::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 = std::round(Base::toDegrees(std::abs(std::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 = std::numeric_limits::max(); double minY = std::numeric_limits::max(); double maxX = -std::numeric_limits::max(); double maxY = -std::numeric_limits::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) { DimensionGeometry geometryRefs2d = validateDimSelection( refs, { "Edge", "Vertex" }, { 1, 2 }, { DimensionGeometry::isDiagonal }); return geometryRefs2d == DimensionGeometry::isDiagonal; } QPixmap icon(std::string name) { constexpr int width = 16; 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) CmdTechDrawDimension::CmdTechDrawDimension() : Command("TechDraw_Dimension") { sAppModule = "TechDraw"; sGroup = QT_TR_NOOP("TechDraw"); sMenuText = QT_TR_NOOP("Dimension"); sToolTipText = QT_TR_NOOP("Inserts new contextual dimensions to the 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 dimension. Right clicking or pressing Esc will cancel."); sWhatsThis = "TechDraw_Dimension"; sStatusTip = sToolTipText; sPixmap = "TechDraw_Dimension"; sAccel = "D"; } void CmdTechDrawDimension::activated(int iMsg) { Q_UNUSED(iMsg); App::AutoTransaction::setEnable(false); ReferenceVector references2d; ReferenceVector references3d; TechDraw::DrawViewPart* partFeat = TechDraw::getReferencesFromSelection(references2d, references3d); activateHandler(new TDHandlerDimension(references2d, partFeat)); } bool CmdTechDrawDimension::isActive() { return isDimCmdActive(this); } // 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; 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"; } bool isActive() override { return isDimCmdActive(this); } }; //=========================================================================== // TechDraw_RadiusDimension //=========================================================================== DEF_STD_CMD_A(CmdTechDrawRadiusDimension) CmdTechDrawRadiusDimension::CmdTechDrawRadiusDimension() : Command("TechDraw_RadiusDimension") { sAppModule = "TechDraw"; sGroup = QT_TR_NOOP("TechDraw"); sMenuText = QT_TR_NOOP("Radius Dimension"); sToolTipText = QT_TR_NOOP("Inserts a radius dimension of a circular edge or arc"); sWhatsThis = "TechDraw_RadiusDimension"; sStatusTip = sToolTipText; sPixmap = "TechDraw_RadiusDimension"; } void CmdTechDrawRadiusDimension::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 the active task dialog and try again")); return; } execRadius(this); } bool CmdTechDrawRadiusDimension::isActive() { return isDimCmdActive(this); } void execRadius(Gui::Command* cmd) { //Define the geometric configuration required for a radius dimension StringVector acceptableGeometry({"Edge"}); std::vector minimumCounts({1}); std::vector acceptableDimensionGeometrys( {DimensionGeometry::isCircle, DimensionGeometry::isEllipse, DimensionGeometry::isBSplineCircle, DimensionGeometry::isBSpline}); execDim(cmd, "Radius", acceptableGeometry, minimumCounts, acceptableDimensionGeometrys); } //=========================================================================== // TechDraw_DiameterDimension //=========================================================================== DEF_STD_CMD_A(CmdTechDrawDiameterDimension) CmdTechDrawDiameterDimension::CmdTechDrawDiameterDimension() : Command("TechDraw_DiameterDimension") { sAppModule = "TechDraw"; sGroup = QT_TR_NOOP("TechDraw"); sMenuText = QT_TR_NOOP("Diameter Dimension"); sToolTipText = QT_TR_NOOP("Inserts a diameter dimension of a circular edge or arc"); sWhatsThis = "TechDraw_DiameterDimension"; sStatusTip = sToolTipText; sPixmap = "TechDraw_DiameterDimension"; } void CmdTechDrawDiameterDimension::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 the active task dialog and try again")); return; } execDiameter(this); } bool CmdTechDrawDiameterDimension::isActive() { return isDimCmdActive(this); } void execDiameter(Gui::Command* cmd) { //Define the geometric configuration required for a diameter dimension StringVector acceptableGeometry({"Edge"}); std::vector minimumCounts({1}); std::vector acceptableDimensionGeometrys( {DimensionGeometry::isCircle, DimensionGeometry::isEllipse, DimensionGeometry::isBSplineCircle, DimensionGeometry::isBSpline}); execDim(cmd, "Diameter", acceptableGeometry, minimumCounts, acceptableDimensionGeometrys); } //=========================================================================== // TechDraw_LengthDimension //=========================================================================== DEF_STD_CMD_A(CmdTechDrawLengthDimension) CmdTechDrawLengthDimension::CmdTechDrawLengthDimension() : Command("TechDraw_LengthDimension") { sAppModule = "TechDraw"; sGroup = QT_TR_NOOP("TechDraw"); sMenuText = QT_TR_NOOP("Length Dimension"); sToolTipText = QT_TR_NOOP("Inserts a length dimension of an edge or distance between two points"); sWhatsThis = "TechDraw_LengthDimension"; sStatusTip = sToolTipText; sPixmap = "TechDraw_LengthDimension"; } void CmdTechDrawLengthDimension::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 the active task dialog and try again")); return; } execDistance(this); } bool CmdTechDrawLengthDimension::isActive() { return isDimCmdActive(this); } void execDistance(Gui::Command* cmd) { StringVector acceptableGeometry({"Edge", "Vertex"}); std::vector minimumCounts({1, 2}); std::vector acceptableDimensionGeometrys( {DimensionGeometry::isVertical, DimensionGeometry::isHorizontal, DimensionGeometry::isDiagonal, DimensionGeometry::isHybrid}); execDim(cmd, "Distance", acceptableGeometry, minimumCounts, acceptableDimensionGeometrys); } //=========================================================================== // TechDraw_HorizontalDimension //=========================================================================== DEF_STD_CMD_A(CmdTechDrawHorizontalDimension) CmdTechDrawHorizontalDimension::CmdTechDrawHorizontalDimension() : Command("TechDraw_HorizontalDimension") { sAppModule = "TechDraw"; sGroup = QT_TR_NOOP("TechDraw"); sMenuText = QT_TR_NOOP("Horizontal Length Dimension"); sToolTipText = QT_TR_NOOP("Inserts a horizontal length dimension of an edge or distance between two points"); sWhatsThis = "TechDraw_HorizontalDimension"; sStatusTip = sToolTipText; sPixmap = "TechDraw_HorizontalDimension"; sAccel = "SHIFT+H"; } void CmdTechDrawHorizontalDimension::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 the active task dialog and try again")); return; } execDistanceX(this); } bool CmdTechDrawHorizontalDimension::isActive() { return isDimCmdActive(this); } void execDistanceX(Gui::Command* cmd) { //Define the geometric configuration required for a length dimension StringVector acceptableGeometry({"Edge", "Vertex"}); std::vector minimumCounts({1, 2}); std::vector acceptableDimensionGeometrys({DimensionGeometry::isHorizontal, DimensionGeometry::isDiagonal, DimensionGeometry::isHybrid}); execDim(cmd, "DistanceX", acceptableGeometry, minimumCounts, acceptableDimensionGeometrys); } //=========================================================================== // TechDraw_VerticalDimension //=========================================================================== DEF_STD_CMD_A(CmdTechDrawVerticalDimension) CmdTechDrawVerticalDimension::CmdTechDrawVerticalDimension() : Command("TechDraw_VerticalDimension") { sAppModule = "TechDraw"; sGroup = QT_TR_NOOP("TechDraw"); sMenuText = QT_TR_NOOP("Vertical Length Dimension"); sToolTipText = QT_TR_NOOP("Inserts a vertical length dimension of an edge or distance between two points"); sWhatsThis = "TechDraw_VerticalDimension"; sStatusTip = sToolTipText; sPixmap = "TechDraw_VerticalDimension"; sAccel = "SHIFT+V"; } void CmdTechDrawVerticalDimension::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 the active task dialog and try again")); return; } execDistanceY(this); } bool CmdTechDrawVerticalDimension::isActive() { return isDimCmdActive(this); } void execDistanceY(Gui::Command* cmd) { //Define the geometric configuration required for a length dimension StringVector acceptableGeometry({"Edge", "Vertex"}); std::vector minimumCounts({1, 2}); std::vector acceptableDimensionGeometrys({DimensionGeometry::isVertical, DimensionGeometry::isDiagonal, DimensionGeometry::isHybrid}); execDim(cmd, "DistanceY", acceptableGeometry, minimumCounts, acceptableDimensionGeometrys); } //=========================================================================== // TechDraw_AngleDimension //=========================================================================== DEF_STD_CMD_A(CmdTechDrawAngleDimension) CmdTechDrawAngleDimension::CmdTechDrawAngleDimension() : Command("TechDraw_AngleDimension") { sAppModule = "TechDraw"; sGroup = QT_TR_NOOP("TechDraw"); sMenuText = QT_TR_NOOP("Angle Dimension"); sToolTipText = QT_TR_NOOP("Inserts an angle dimension between two edges"); sWhatsThis = "TechDraw_AngleDimension"; sStatusTip = sToolTipText; sPixmap = "TechDraw_AngleDimension"; } void CmdTechDrawAngleDimension::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 the active task dialog and try again")); return; } execAngle(this); } bool CmdTechDrawAngleDimension::isActive() { return isDimCmdActive(this); } void execAngle(Gui::Command* cmd) { //Define the geometric configuration required for a length dimension StringVector acceptableGeometry({"Edge"}); std::vector minimumCounts({2}); std::vector acceptableDimensionGeometrys({DimensionGeometry::isAngle}); execDim(cmd, "Angle", acceptableGeometry, minimumCounts, acceptableDimensionGeometrys); } //=========================================================================== // TechDraw_3PtAngleDimension //=========================================================================== DEF_STD_CMD_A(CmdTechDraw3PtAngleDimension) CmdTechDraw3PtAngleDimension::CmdTechDraw3PtAngleDimension() : Command("TechDraw_3PtAngleDimension") { sAppModule = "TechDraw"; sGroup = QT_TR_NOOP("TechDraw"); sMenuText = QT_TR_NOOP("Angle Dimension From 3 Points"); sToolTipText = QT_TR_NOOP("Inserts an angle dimension between 3 selected points"); sWhatsThis = "TechDraw_3PtAngleDimension"; sStatusTip = sToolTipText; sPixmap = "TechDraw_3PtAngleDimension"; } void CmdTechDraw3PtAngleDimension::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 the active task dialog and try again")); return; } execAngle3Pt(this); } bool CmdTechDraw3PtAngleDimension::isActive() { return isDimCmdActive(this); } void execAngle3Pt(Gui::Command* cmd) { //Define the geometric configuration required for a length dimension StringVector acceptableGeometry({"Vertex"}); std::vector minimumCounts({3}); std::vector acceptableDimensionGeometrys({DimensionGeometry::isAngle3Pt}); execDim(cmd, "Angle3Pt", acceptableGeometry, minimumCounts, acceptableDimensionGeometrys); } //=========================================================================== // TechDraw_AreaDimension //=========================================================================== DEF_STD_CMD_A(CmdTechDrawAreaDimension) CmdTechDrawAreaDimension::CmdTechDrawAreaDimension() : Command("TechDraw_AreaDimension") { sAppModule = "TechDraw"; sGroup = QT_TR_NOOP("TechDraw"); sMenuText = QT_TR_NOOP("Area Annotation"); sToolTipText = QT_TR_NOOP("Inserts an annotation showing the area of a selected face"); 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 the active task dialog and try again")); return; } execArea(this); } bool CmdTechDrawAreaDimension::isActive() { return isDimCmdActive(this); } void execArea(Gui::Command* cmd) { //Define the geometric configuration required for a area dimension StringVector acceptableGeometry({"Face"}); std::vector minimumCounts({1}); std::vector acceptableDimensionGeometrys({DimensionGeometry::isFace}); execDim(cmd, "Area", acceptableGeometry, minimumCounts, acceptableDimensionGeometrys); } //=========================================================================== // TechDraw_ExtentGroup //=========================================================================== DEF_STD_CMD_ACL(CmdTechDrawExtentGroup) CmdTechDrawExtentGroup::CmdTechDrawExtentGroup() : Command("TechDraw_ExtentGroup") { sAppModule = "TechDraw"; sGroup = QT_TR_NOOP("TechDraw"); sMenuText = QT_TR_NOOP("Extent Dimension"); sToolTipText = QT_TR_NOOP("Inserts a dimension showing the extent (overall length) of an object or feature"); sWhatsThis = "TechDraw_ExtentGroup"; sStatusTip = sToolTipText; // eType = ForEdit; } void CmdTechDrawExtentGroup::activated(int iMsg) { Gui::TaskView::TaskDialog* dlg = Gui::Control().activeDialog(); if (dlg) { QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Task in progress"), QObject::tr("Close the active task dialog and try again")); return; } Gui::ActionGroup* pcAction = qobject_cast(_pcAction); pcAction->setIcon(pcAction->actions().at(iMsg)->icon()); switch (iMsg) { case 0: execExtent(this, "DistanceX"); break; case 1: execExtent(this, "DistanceY"); break; default: Base::Console().message("CMD::ExtGrp - invalid iMsg: %d\n", iMsg); }; } Gui::Action* CmdTechDrawExtentGroup::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_HorizontalExtentDimension")); p1->setObjectName(QStringLiteral("TechDraw_HorizontalExtentDimension")); p1->setWhatsThis(QStringLiteral("TechDraw_HorizontalExtentDimension")); QAction* p2 = pcAction->addAction(QString()); p2->setIcon(Gui::BitmapFactory().iconFromTheme("TechDraw_VerticalExtentDimension")); p2->setObjectName(QStringLiteral("TechDraw_VerticalExtentDimension")); p2->setWhatsThis(QStringLiteral("TechDraw_VerticalExtentDimension")); _pcAction = pcAction; languageChange(); pcAction->setIcon(p1->icon()); int defaultId = 0; pcAction->setProperty("defaultAction", QVariant(defaultId)); return pcAction; } void CmdTechDrawExtentGroup::languageChange() { Command::languageChange(); if (!_pcAction) { return; } Gui::ActionGroup* pcAction = qobject_cast(_pcAction); QList a = pcAction->actions(); QAction* arc1 = a[0]; arc1->setText(QApplication::translate("CmdTechDrawExtentGroup", "Horizontal extent")); arc1->setToolTip( QApplication::translate("TechDraw_HorizontalExtent", "Insert horizontal extent dimension")); arc1->setStatusTip(arc1->toolTip()); QAction* arc2 = a[1]; arc2->setText(QApplication::translate("CmdTechDrawExtentGroup", "Vertical extent")); arc2->setToolTip(QApplication::translate("TechDraw_VerticalExtentDimension", "Insert vertical extent dimension")); arc2->setStatusTip(arc2->toolTip()); } bool CmdTechDrawExtentGroup::isActive() { return isDimCmdActive(this); } //=========================================================================== // TechDraw_HorizontalExtentDimension //=========================================================================== DEF_STD_CMD_A(CmdTechDrawHorizontalExtentDimension) CmdTechDrawHorizontalExtentDimension::CmdTechDrawHorizontalExtentDimension() : Command("TechDraw_HorizontalExtentDimension") { sAppModule = "TechDraw"; sGroup = QT_TR_NOOP("TechDraw"); sMenuText = QT_TR_NOOP("Horizontal Extent Dimension"); sToolTipText = QT_TR_NOOP("Inserts a dimension showing the horizontal extent (overall length) of an object or feature."); sWhatsThis = "TechDraw_HorizontalExtentDimension"; sStatusTip = sToolTipText; sPixmap = "TechDraw_HorizontalExtentDimension"; } void CmdTechDrawHorizontalExtentDimension::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 the active task dialog and try again")); return; } execExtent(this, "DistanceX"); } bool CmdTechDrawHorizontalExtentDimension::isActive() { return isDimCmdActive(this); } void execExtent(Gui::Command* cmd, const std::string& dimType) { const char* commandString = nullptr; if (dimType == "DistanceX") { commandString = QT_TRANSLATE_NOOP("Command", "Create Dimension DistanceX"); } else if (dimType == "DistanceY") { commandString = QT_TRANSLATE_NOOP("Command", "Create Dimension DistanceY"); } Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", commandString)); bool result = _checkDrawViewPart(cmd); if (!result) { QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Incorrect selection"), QObject::tr("No view of a part in selection.")); Gui::Command::abortCommand(); return; } ReferenceVector references2d; ReferenceVector references3d; TechDraw::DrawViewPart* partFeat = TechDraw::getReferencesFromSelection(references2d, references3d); // if sticky selection is in use we may get confusing selections that appear to // include both 2d and 3d geometry for the extent dim. if (!references3d.empty()) { for (auto& ref : references2d) { if (!ref.getSubName().empty()) { QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Incorrect selection"), QObject::tr("Selection contains both 2D and 3D geometry")); Gui::Command::abortCommand(); return; } } } //Define the geometric configuration required for a extent dimension StringVector acceptableGeometry({"Edge"}); std::vector minimumCounts({1}); std::vector acceptableDimensionGeometrys({DimensionGeometry::isMultiEdge, DimensionGeometry::isHorizontal, DimensionGeometry::isVertical, DimensionGeometry::isDiagonal, DimensionGeometry::isCircle, DimensionGeometry::isEllipse, DimensionGeometry::isBSplineCircle, DimensionGeometry::isBSpline, DimensionGeometry::isZLimited}); //what 2d geometry configuration did we receive? DimensionGeometry geometryRefs2d = validateDimSelection( references2d, acceptableGeometry, minimumCounts, acceptableDimensionGeometrys); if (geometryRefs2d == DimensionGeometry::isInvalid) { QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Incorrect Selection"), QObject::tr("Cannot make 2D extent dimension from selection")); Gui::Command::abortCommand(); return; } //what 3d geometry configuration did we receive? DimensionGeometry geometryRefs3d; if (geometryRefs2d == DimensionGeometry::isViewReference && !references3d.empty()) { geometryRefs3d = validateDimSelection3d(partFeat, references3d, acceptableGeometry, minimumCounts, acceptableDimensionGeometrys); if (geometryRefs3d == DimensionGeometry::isInvalid) { QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Incorrect Selection"), QObject::tr("Cannot make 3D extent dimension from selection")); Gui::Command::abortCommand(); return; } } if (references3d.empty()) { DrawDimHelper::makeExtentDim(partFeat, dimType, references2d); } else { DrawDimHelper::makeExtentDim3d(partFeat, dimType, references3d); } Gui::Command::commitCommand(); } //=========================================================================== // TechDraw_VerticalExtentDimension //=========================================================================== DEF_STD_CMD_A(CmdTechDrawVerticalExtentDimension) CmdTechDrawVerticalExtentDimension::CmdTechDrawVerticalExtentDimension() : Command("TechDraw_VerticalExtentDimension") { sAppModule = "TechDraw"; sGroup = QT_TR_NOOP("TechDraw"); sMenuText = QT_TR_NOOP("Vertical Extent Dimension"); sToolTipText = QT_TR_NOOP("Inserts a dimension showing the vertical extent (overall length) of an object or feature."); sWhatsThis = "TechDraw_VerticalExtentDimension"; sStatusTip = sToolTipText; sPixmap = "TechDraw_VerticalExtentDimension"; } void CmdTechDrawVerticalExtentDimension::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 the active task dialog and try again")); return; } execExtent(this, "DistanceY"); } bool CmdTechDrawVerticalExtentDimension::isActive() { return isDimCmdActive(this); } //=========================================================================== // TechDraw_DimensionRepair //=========================================================================== DEF_STD_CMD_A(CmdTechDrawDimensionRepair) CmdTechDrawDimensionRepair::CmdTechDrawDimensionRepair() : Command("TechDraw_DimensionRepair") { sAppModule = "TechDraw"; sGroup = QT_TR_NOOP("TechDraw"); sMenuText = QT_TR_NOOP("Repair Dimension References"); sToolTipText = QT_TR_NOOP("Repairs broken or incorrect dimension references"); sWhatsThis = "TechDraw_DimensionRepair"; sStatusTip = sToolTipText; sPixmap = "TechDraw_DimensionRepair"; } void CmdTechDrawDimensionRepair::activated(int iMsg) { Q_UNUSED(iMsg); std::vector dimObjs = getSelection().getObjectsOfType(TechDraw::DrawViewDimension::getClassTypeId()); TechDraw::DrawViewDimension* dim = nullptr; if (dimObjs.empty()) { QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Incorrect selection"), QObject::tr("There is no dimension in your selection")); return; } else { dim = static_cast(dimObjs.at(0)); } Gui::Control().showDialog(new TaskDlgDimReference(dim)); } bool CmdTechDrawDimensionRepair::isActive(void) { bool havePage = DrawGuiUtil::needPage(this); bool haveView = DrawGuiUtil::needView(this); bool taskInProgress = false; if (havePage) { taskInProgress = Gui::Control().activeDialog(); } return (havePage && haveView && !taskInProgress); } //------------------------------------------------------------------------------ void CreateTechDrawCommandsDims() { Gui::CommandManager& rcCmdMgr = Gui::Application::Instance->commandManager(); rcCmdMgr.addCommand(new CmdTechDrawDimension()); rcCmdMgr.addCommand(new CmdTechDrawRadiusDimension()); rcCmdMgr.addCommand(new CmdTechDrawDiameterDimension()); rcCmdMgr.addCommand(new CmdTechDrawLengthDimension()); rcCmdMgr.addCommand(new CmdTechDrawHorizontalDimension()); 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 CmdTechDrawDimensionRepair()); rcCmdMgr.addCommand(new CmdTechDrawCompDimensionTools()); } //------------------------------------------------------------------------------ //Common code to build a dimension feature void execDim(Gui::Command* cmd, std::string type, StringVector acceptableGeometry, std::vector minimumCounts, std::vector acceptableDimensionGeometrys) { 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); //what 2d geometry configuration did we receive? DimensionGeometry geometryRefs2d = validateDimSelection( references2d, acceptableGeometry, minimumCounts, acceptableDimensionGeometrys); if (geometryRefs2d == DimensionGeometry::isInvalid) { QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Incorrect selection"), QObject::tr("Cannot make 2D dimension from selection")); return; } //what 3d geometry configuration did we receive? DimensionGeometry geometryRefs3d{DimensionGeometry::isInvalid}; if (geometryRefs2d == DimensionGeometry::isViewReference && !references3d.empty()) { geometryRefs3d = validateDimSelection3d(partFeat, references3d, acceptableGeometry, minimumCounts, acceptableDimensionGeometrys); if (geometryRefs3d == DimensionGeometry::isInvalid) { QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Incorrect Selection"), QObject::tr("Cannot make 3D dimension from selection")); return; } } else { references3d.clear(); } //errors and warnings if (type == "Radius" || type == "Diameter") { if (geometryRefs2d == DimensionGeometry::isEllipse || geometryRefs3d == DimensionGeometry::isEllipse) { QMessageBox::StandardButton result = QMessageBox::warning( Gui::getMainWindow(), QObject::tr("Ellipse curve warning"), QObject::tr("Selected edge is an Ellipse. Value will be approximate. Continue?"), QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Cancel); if (result != QMessageBox::Ok) { return; } } if (geometryRefs2d == DimensionGeometry::isBSplineCircle || geometryRefs3d == DimensionGeometry::isBSplineCircle) { QMessageBox::StandardButton result = QMessageBox::warning( Gui::getMainWindow(), QObject::tr("B-spline curve warning"), QObject::tr("Selected edge is a B-spline. Value will be approximate. Continue?"), QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Cancel); if (result != QMessageBox::Ok) { return; } } if (geometryRefs2d == DimensionGeometry::isBSpline || geometryRefs3d == DimensionGeometry::isBSpline) { QMessageBox::critical( Gui::getMainWindow(), QObject::tr("B-spline curve error"), QObject::tr("Selected edge is a B-spline and a radius/diameter cannot be calculated.")); return; } } if (geometryRefs2d == DimensionGeometry::isFace && references2d.size() > 1) { Base::Console().warning("Multiple faces are selected. Using first.\n"); references2d.resize(1); } //build the dimension DrawViewDimension* dim = dimensionMaker(partFeat, type, references2d, references3d); if (type == "Distance" || type == "DistanceX" || type == "DistanceY" || type == "Angle" || type == "Angle3Pt") { //position the Dimension text on the view positionDimText(dim); } } 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(); std::string dimName = dvp->getDocument()->getUniqueObjectName("Dimension"); Gui::Command::doCommand(Gui::Command::Doc, "App.activeDocument().addObject('TechDraw::DrawViewDimension', '%s')", dimName.c_str()); Gui::Command::doCommand(Gui::Command::Doc, "App.activeDocument().%s.translateLabel('DrawViewDimension', 'Dimension', '%s')", dimName.c_str(), dimName.c_str()); Gui::Command::doCommand( Gui::Command::Doc, "App.activeDocument().%s.Type = '%s'", dimName.c_str(), dimType.c_str()); Gui::Command::doCommand(Gui::Command::Doc, "App.activeDocument().%s.MeasureType = '%s'", dimName.c_str(), "Projected"); auto* dim = dynamic_cast(dvp->getDocument()->getObject(dimName.c_str())); if (!dim) { throw Base::TypeError("CmdTechDrawNewDiameterDimension - dim not found\n"); } //always have References2D, even if only for the parent DVP dim->setReferences2d(references2d); dim->setReferences3d(references3d); Gui::Command::doCommand(Gui::Command::Doc, "App.activeDocument().%s.addView(App.activeDocument().%s)", PageName.c_str(), dimName.c_str()); dim->recomputeFeature(); return dim; } //position the Dimension text on the view 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 + (offsetIndex * 1.5 + 0.5) * fontSize); } //=========================================================================== // Selection Validation Helpers //=========================================================================== //! common checks of Selection for Dimension commands //non-empty selection, no more than maxObjs selected and at least 1 DrawingPage exists bool _checkSelection(Gui::Command* cmd, unsigned maxObjs) { std::vector selection = cmd->getSelection().getSelectionEx(); if (selection.empty()) { QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Incorrect selection"), QObject::tr("Select an object first")); return false; } const std::vector SubNames = selection[0].getSubNames(); if (SubNames.size() > maxObjs) { QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Incorrect selection"), QObject::tr("Too many objects selected")); return false; } std::vector pages = cmd->getDocument()->getObjectsOfType(TechDraw::DrawPage::getClassTypeId()); if (pages.empty()) { QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Incorrect selection"), QObject::tr("Create a page first.")); return false; } return true; } bool _checkDrawViewPart(Gui::Command* cmd) { std::vector selection = cmd->getSelection().getSelectionEx(); for (auto& sel : selection) { auto dvp = dynamic_cast(sel.getObject()); if (dvp) { return true; } } return false; }