/*************************************************************************** * 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 "PreCompiled.h" #ifndef _PreComp_ #include #include #include #include #include #include #include #include #endif//#ifndef _PreComp_ #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 "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; //=========================================================================== // 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) { auto* mdi = dynamic_cast(Gui::getMainWindow()->activeWindow()); if (!mdi) { return; } ViewProviderPage* vp = mdi->getViewProviderPage(); if (!vp) { return; } QGVPage* viewPage = vp->getQGVPage(); if (!viewPage) { return; } viewPage->activateHandler(newHandler); } //=========================================================================== // 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 = dynamic_cast(Gui::getMainWindow()->activeWindow()); if (mdi) { mdi->setDimensionsSelectability(false); } Gui::Selection().setSelectionStyle(Gui::SelectionSingleton::SelectionStyle::GreedySelection); Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Insert Dimension")); handleInitialSelection(); } void deactivated() override { auto* mdi = dynamic_cast(Gui::getMainWindow()->activeWindow()); if (mdi) { mdi->setDimensionsSelectability(true); } Gui::Selection().setSelectionStyle(Gui::SelectionSingleton::SelectionStyle::NormalSelection); Gui::Command::abortCommand(); } void keyPressEvent(QKeyEvent* event) override { if (event->key() == Qt::Key_M && !selectionEmpty()) { if (availableDimension == AvailableDimension::FIRST) { availableDimension = AvailableDimension::SECOND; } else if (availableDimension == AvailableDimension::SECOND) { availableDimension = AvailableDimension::THIRD; } else if (availableDimension == AvailableDimension::THIRD) { availableDimension = AvailableDimension::FOURTH; } else if (availableDimension == AvailableDimension::FOURTH) { availableDimension = AvailableDimension::FIFTH; } else if (availableDimension == AvailableDimension::FIFTH || availableDimension == AvailableDimension::RESET) { availableDimension = AvailableDimension::FIRST; } makeAppropriateDimension(); event->accept(); } else if (event->key() == Qt::Key_Z && (QApplication::keyboardModifiers() & Qt::ControlModifier)) { // User trying to cancel with Ctrl-Z quit(); event->accept(); } } void keyReleaseEvent(QKeyEvent* event) override { if (event->key() == Qt::Key_Z && (QApplication::keyboardModifiers() & Qt::ControlModifier)) { // User trying to cancel with Ctrl-Z quit(); event->accept(); } else { TechDrawHandler::keyReleaseEvent(event); } } void mouseMoveEvent(QMouseEvent* event) override { mousePos = event->pos(); if (dims.empty()){ return; } bool textToMiddle = false; Base::Vector3d dirMaster, delta; //Change distance dimension based on position of mouse. if (specialDimension == SpecialDimension::LineOr2PointsDistance || specialDimension == SpecialDimension::LineOr2PointsChamfer){ updateDistanceType(); } else if (specialDimension == SpecialDimension::ExtendDistance){ updateExtentDistanceType(); } else if (specialDimension == SpecialDimension::ChainDistance || specialDimension == SpecialDimension::CoordDistance){ updateChainDistanceType(); textToMiddle = true; pointPair pp = dims[0]->getLinearPoints(); dirMaster = pp.second() - pp.first(); //dirMaster.y = -dirMaster.y; // not needed because y is reversed between property X/Y and scenePositions QPointF firstPos = getDimLabel(dims[0])->pos(); Base::Vector3d pMaster(firstPos.x(), firstPos.y(), 0.0); Base::Vector3d ipDelta = DrawUtil::getTrianglePoint(pMaster, dirMaster, Base::Vector3d()); delta = ipDelta.Normalize() * Rez::guiX(activeDimAttributes.getCascadeSpacing()); } int i = 0; for (auto* dim : dims) { auto dimType = static_cast(dim->Type.getValue()); moveDimension(mousePos, dim, textToMiddle, dirMaster, delta, dimType, i); if (specialDimension == SpecialDimension::CoordDistance) { i++; } } } QGIDatumLabel* getDimLabel(DrawViewDimension* d) { auto* vp = dynamic_cast(Gui::Application::Instance->getViewProvider(d)); if (!vp) { return nullptr; } auto* qgivDimension(dynamic_cast(vp->getQView())); if (!qgivDimension) { return nullptr; } return qgivDimension->getDatumLabel(); } void moveDimension(QPoint pos, DrawViewDimension* dim, bool textToMiddle = false, Base::Vector3d dir = Base::Vector3d(), Base::Vector3d delta = Base::Vector3d(), DimensionType type = DimensionType::Distance, int i = 0) { if (!dim) { return; } auto label = getDimLabel(dim); if (!label) { return; } label->setPos(getDimPositionToBe(pos, label->pos(), textToMiddle, dir, delta, type, i)); } QPointF getDimPositionToBe(QPoint pos, QPointF curPos = QPointF(), bool textToMiddle = false, Base::Vector3d dir = Base::Vector3d(), Base::Vector3d delta = Base::Vector3d(), DimensionType type = DimensionType::Distance, int i = 0) { auto* vpp = dynamic_cast(Gui::Application::Instance->getViewProvider(partFeat)); if (!vpp) { return QPointF(); } QPointF scenePos = viewPage->mapToScene(pos) - vpp->getQView()->scenePos(); if (textToMiddle) { // delta is for coord distances. i = 0 when it's a chain so delta is ignored. float dimDistance = Rez::guiX(activeDimAttributes.getCascadeSpacing()); if (type == DimensionType::Distance) { Base::Vector3d pos3d(scenePos.x(), scenePos.y(), 0.0); float xDim = curPos.x(); float yDim = curPos.y(); Base::Vector3d pDim(xDim, yDim, 0.0); Base::Vector3d p3 = DrawUtil::getTrianglePoint(pos3d, dir, pDim); p3 = p3 + delta * i; scenePos = QPointF(p3.x, p3.y); } else if(type == DimensionType::DistanceX) { pointPair pp = dims[0]->getLinearPoints(); if (Rez::guiX(pp.first().y) > scenePos.y()) dimDistance = -dimDistance; double y = scenePos.y() + i * dimDistance; scenePos = QPointF(curPos.x(), y); } else if(type == DimensionType::DistanceY) { pointPair pp = dims[0]->getLinearPoints(); if (Rez::guiX(pp.first().x) > scenePos.x()) dimDistance = -dimDistance; double x = scenePos.x() + i * dimDistance; scenePos = QPointF(x, curPos.y()); } } return scenePos; } void finishDimensionMove() { for (auto* dim : dims) { auto label = getDimLabel(dim); double x = Rez::appX(label->X()), y = Rez::appX(label->Y()); Gui::Command::doCommand(Gui::Command::Doc, "App.ActiveDocument.%s.X = %f", dim->getNameInDocument(), x); Gui::Command::doCommand(Gui::Command::Doc, "App.ActiveDocument.%s.Y = %f", dim->getNameInDocument(), -y); } } void setDimsSelectability(bool val) { for (auto dim : dims) { setDimSelectability(dim, val); } } void setDimSelectability(DrawViewDimension* d, bool val) { QGIDatumLabel* label = getDimLabel(d); if (label) { label->setSelectability(val); } } void mouseReleaseEvent(QMouseEvent* event) override { // Base::Console().Warning("mouseReleaseEvent TH\n"); if (event->button() == Qt::RightButton) { if (!dims.empty()) { Gui::Selection().clearSelection(); clearAndRestartCommand(); event->accept(); } else { TechDrawHandler::mouseReleaseEvent(event); } return; } else if (event->button() == Qt::LeftButton) { mousePos = event->pos(); bool finalize = true; if (removedRef.hasGeometry()) { finalize = false; //Base::Console().Warning("RmvSelection \n"); // Remove the reference from the vector ReferenceVector& selVector = getSelectionVector(removedRef); selVector.erase(std::remove(selVector.begin(), selVector.end(), removedRef), selVector.end()); if (!selectionEmpty()) { availableDimension = AvailableDimension::FIRST; makeAppropriateDimension(); } else { clearAndRestartCommand(); } removedRef = ReferenceEntry(); } if (addedRef.hasGeometry()) { finalize = false; //Base::Console().Warning("AddSelection\n"); //add the geometry to its type vector. Temporarily if not selAllowed if (addedRef.getSubName() == "") { // Behavior deactivated for now because I found it annoying. // To reactivate replace addedRef.hasGeometry() by addedRef.getObject() above. // This means user selected the view itself. if (selectionEmpty()) { restartCommand(QT_TRANSLATE_NOOP("Command", "Add Extent dimension")); createExtentDistanceDimension("DistanceX"); } } else { ReferenceVector& selVector = getSelectionVector(addedRef); selVector.push_back(addedRef); availableDimension = AvailableDimension::FIRST; bool selAllowed = makeAppropriateDimension(); if (!selAllowed) { // remove from selection blockRemoveSel = true; Gui::Selection().rmvSelection(addedRef.getObject()->getDocument()->getName(), addedRef.getObject()->getNameInDocument(), addedRef.getSubName().c_str()); blockRemoveSel = false; if (selVector == selFaces) { // if sel face and not allowed, then a dimension is being created // and user clicked on a face to drop it. // Better would be to disable face selectability when needed. finalize = true; } } } addedRef = ReferenceEntry(); } // Finalize if click on empty space. if (finalize && !dims.empty()) { finalizeCommand(); } } } void onSelectionChanged(const Gui::SelectionChanges& msg) override { //Base::Console().Warning("onSelectionChanged %d - --%s--\n", (int)msg.Type, msg.pSubName); if (msg.Type == Gui::SelectionChanges::ClrSelection) { //clearAndRestartCommand(); return; } if (msg.Object.getObjectName().empty() || msg.Object.getDocument() != getPage()->getDocument()) { if (msg.Type == Gui::SelectionChanges::AddSelection) { Gui::Selection().rmvSelection(msg.pDocName, msg.pObjectName, msg.pSubName); } return; } /*if (msg.Type == Gui::SelectionChanges::SetPreselect) { Base::Console().Warning("SetPreselect\n"); std::string geomName = DrawUtil::getGeomTypeFromName(msg.pSubName); edgeOrPointPreselected = geomName == "Edge" || geomName == "Vertex"; return; } else if (msg.Type == Gui::SelectionChanges::RmvPreselect) { Base::Console().Warning("RmvPreselect\n"); edgeOrPointPreselected = false; return; }*/ App::DocumentObject* obj = msg.Object.getObject(); if (!obj) { if (msg.Type == Gui::SelectionChanges::AddSelection) { Gui::Selection().rmvSelection(msg.pDocName, msg.pObjectName, msg.pSubName); } return; } auto* dvp = dynamic_cast(obj); if (!dvp) { if (msg.Type == Gui::SelectionChanges::AddSelection) { Gui::Selection().rmvSelection(msg.pDocName, msg.pObjectName, msg.pSubName); } return; } if (partFeat && partFeat != dvp) { // Dimensions can only be within one view. if (msg.Type == Gui::SelectionChanges::AddSelection) { Gui::Selection().rmvSelection(msg.pDocName, msg.pObjectName, msg.pSubName); } return; } else { partFeat = dvp; } if (msg.Type == Gui::SelectionChanges::AddSelection) { addedRef = ReferenceEntry(dvp, msg.pSubName); } else if (msg.Type == Gui::SelectionChanges::RmvSelection) { if (!blockRemoveSel) { removedRef = ReferenceEntry(dvp, msg.pSubName); } } } private: QString getCrosshairCursorSVGName() const override { return QString::fromLatin1("TechDraw_Dimension_Pointer"); } protected: SpecialDimension specialDimension; AvailableDimension availableDimension; QPoint mousePos; ReferenceVector selPoints; ReferenceVector selLine; ReferenceVector selCircleArc; ReferenceVector selEllipseArc; ReferenceVector selSplineAndCo; ReferenceVector selFaces; ReferenceVector emptyVector; ReferenceEntry addedRef; ReferenceEntry removedRef; ReferenceVector initialSelection; TechDraw::DrawViewPart* partFeat; std::vector dims; bool blockRemoveSel; void handleInitialSelection() { if (initialSelection.size() == 0) { return; } availableDimension = AvailableDimension::FIRST; partFeat = dynamic_cast(initialSelection[0].getObject()); if (!partFeat) { return; } // Add the selected elements to their corresponding selection vectors for (auto& ref : initialSelection) { ReferenceVector& selVector = getSelectionVector(ref); selVector.push_back(ref); } // See if the selection is valid bool selAllowed = makeAppropriateDimension(); if (!selAllowed) { clearRefVectors(); } } void finalizeCommand() { //Base::Console().Warning("finalizeCommand \n"); finishDimensionMove(); // Ask for the value of datum dimensions ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/TechDraw"); Gui::Command::commitCommand(); // Touch the parent feature so the dimension in tree view appears as a child partFeat->touch(true); // This code enables the continuous creation mode. bool continuousMode = hGrp->GetBool("ContinuousCreationMode", true); if (continuousMode) { Gui::Selection().clearSelection(); clearAndRestartCommand(); } else { viewPage->deactivateHandler(); // no code after this line, Handler get deleted in QGVPage } } ReferenceVector& getSelectionVector(ReferenceEntry& ref) { std::string subName = ref.getSubName(); if (subName == "") { return emptyVector; } auto* dvp = static_cast(ref.getObject()); std::string geomName = DrawUtil::getGeomTypeFromName(subName); if (geomName == "Face") { return selFaces; } else if (geomName == "Edge") { int GeoId(TechDraw::DrawUtil::getIndexFromName(subName)); TechDraw::BaseGeomPtr geom = dvp->getGeomByIndex(GeoId); if (!geom) { return emptyVector; } if (geom->getGeomType() == TechDraw::GENERIC) { TechDraw::GenericPtr gen1 = std::static_pointer_cast(geom); if (gen1->points.size() < 2) { return emptyVector; } return selLine; //Base::Vector3d line = gen1->points.at(1) - gen1->points.at(0); } else if (geom->getGeomType() == TechDraw::CIRCLE || geom->getGeomType() == TechDraw::ARCOFCIRCLE) { return selCircleArc; } else if (geom->getGeomType() == TechDraw::ELLIPSE || geom->getGeomType() == TechDraw::ARCOFELLIPSE) { return selEllipseArc; } else if (geom->getGeomType() == TechDraw::BSPLINE) { //TechDraw::BSplinePtr spline = std::static_pointer_cast(geom); //if (spline->isCircle()) { // return isBSplineCircle; //} //else { //} return selSplineAndCo; } } else if (geomName == "Vertex") { return selPoints; } return emptyVector; } /* bool notSelectedYet(const ReferenceEntry& elem) { auto contains = [&](const ReferenceVector& vec, const ReferenceEntry& elem) { for (const auto& x : vec) { if (x == elem){ return true; } } return false; }; return !contains(selPoints, elem) && !contains(selLine, elem) && !contains(selCircleArc, elem) && !contains(selEllipseArc, elem) && !contains(selFaces, elem); }*/ bool selectionEmpty() { return selPoints.empty() && selLine.empty() && selCircleArc.empty() && selEllipseArc.empty() && selSplineAndCo.empty() && selFaces.empty(); } ReferenceVector allRefs() { ReferenceVector result; result.reserve(selPoints.size() + selLine.size() + selCircleArc.size() + selEllipseArc.size() + selSplineAndCo.size() + selFaces.size()); // Append each vector to result result.insert(result.end(), selPoints.begin(), selPoints.end()); result.insert(result.end(), selLine.begin(), selLine.end()); result.insert(result.end(), selCircleArc.begin(), selCircleArc.end()); result.insert(result.end(), selEllipseArc.begin(), selEllipseArc.end()); result.insert(result.end(), selSplineAndCo.begin(), selSplineAndCo.end()); result.insert(result.end(), selFaces.begin(), selFaces.end()); return result; } bool makeAppropriateDimension() { bool selAllowed = false; //Base::Console().Warning("makeAppropriateDimension %d %d %d %d %d %d\n", selPoints.size(), selLine.size(), selCircleArc.size(), selEllipseArc.size(), selSplineAndCo.size(), selFaces.size()); GeomSelectionSizes selection(selPoints.size(), selLine.size(), selCircleArc.size(), selEllipseArc.size(), selSplineAndCo.size(), selFaces.size()); if (selection.hasFaces()) { if (selection.has1Face()) { makeCts_Faces(selAllowed); } else { return false; } // nothing else with face works } else if (selection.hasPoints()) { if (selection.has1Point()) { selAllowed = true; } else if (selection.has2Points()) { makeCts_2Point(selAllowed); } else if (selection.has3Points()) { makeCts_3Point(selAllowed); } else if (selection.has4MorePoints()) { makeCts_4MorePoints(selAllowed); } else if (selection.has1Point1Line()) { makeCts_1Point1Line(selAllowed); } else if (selection.has1Point1Circle()) { makeCts_1Point1Circle(selAllowed); } else if (selection.has1Point1Ellipse()) { makeCts_1Point1Ellipse(selAllowed); } } else if (selection.hasLines()) { if (selection.has1Line()) { makeCts_1Line(selAllowed); } else if (selection.has2Lines()) { makeCts_2Line(selAllowed); } else if (selection.has1Line1Circle()) { makeCts_1Line1Circle(selAllowed); } else if (selection.has1Line1Ellipse()) { makeCts_1Line1Ellipse(selAllowed); } } else if (selection.hasCirclesOrArcs()) { if (selection.has1Circle()) { makeCts_1Circle(selAllowed); } else if (selection.has2Circles()) { makeCts_2Circle(selAllowed); } } else if (selection.hasEllipseAndCo()) { if (selection.has1Ellipse()) { makeCts_1Ellipse(selAllowed); } if (selection.has2Ellipses()) { makeCts_2Ellipses(selAllowed); } } else if (selection.hasSplineAndCo()) { if (selection.has1Spline()) { makeCts_1Spline(selAllowed); } if (selection.has1SplineAndMore()) { makeCts_1SplineAndMore(selAllowed); } } // Make created constraints unselectable. if (selAllowed) { setDimsSelectability(false); } return selAllowed; } void makeCts_Faces(bool& selAllowed) { //area if (availableDimension == AvailableDimension::FIRST) { restartCommand(QT_TRANSLATE_NOOP("Command", "Add Area dimension")); createAreaDimension(selFaces[0]); selAllowed = true; availableDimension = AvailableDimension::RESET; } } void makeCts_2Point(bool& selAllowed) { //distance if (availableDimension == AvailableDimension::FIRST) { restartCommand(QT_TRANSLATE_NOOP("Command", "Add Distance dimension")); createDistanceDimension("Distance", { selPoints[0], selPoints[1] }); specialDimension = SpecialDimension::LineOr2PointsDistance; selAllowed = true; if (!isVerticalDistance({ selPoints[0], selPoints[1] })) { availableDimension = AvailableDimension::RESET; } } if (availableDimension == AvailableDimension::SECOND) { restartCommand(QT_TRANSLATE_NOOP("Command", "Add DistanceX Chamfer dimension")); createDistanceDimension("DistanceX", { selPoints[0], selPoints[1] }, true); specialDimension = SpecialDimension::LineOr2PointsChamfer; availableDimension = AvailableDimension::RESET; } } void makeCts_3Point(bool& selAllowed) { // chain distances, angle if (availableDimension == AvailableDimension::FIRST) { restartCommand(QT_TRANSLATE_NOOP("Command", "Add horizontal chain dimensions")); createChainDimension("DistanceX"); selAllowed = true; } if (availableDimension == AvailableDimension::SECOND) { restartCommand(QT_TRANSLATE_NOOP("Command", "Add horizontal coordinate dimensions")); createCoordDimension("DistanceX"); } if (availableDimension == AvailableDimension::THIRD) { restartCommand(QT_TRANSLATE_NOOP("Command", "Add 3-points angle dimension")); create3pAngleDimension({ selPoints[0], selPoints[1], selPoints[2] }); } else if (availableDimension == AvailableDimension::FOURTH) { restartCommand(QT_TRANSLATE_NOOP("Command", "Add 3-points angle dimension")); create3pAngleDimension({ selPoints[1], selPoints[2], selPoints[0] }); } else if (availableDimension == AvailableDimension::FIFTH) { restartCommand(QT_TRANSLATE_NOOP("Command", "Add 3-points angle dimension")); create3pAngleDimension({ selPoints[2], selPoints[0], selPoints[1] }); availableDimension = AvailableDimension::RESET; } } void makeCts_4MorePoints(bool& selAllowed) { // chain distances if (availableDimension == AvailableDimension::FIRST) { restartCommand(QT_TRANSLATE_NOOP("Command", "Add horizontal chain dimension")); createChainDimension("DistanceX"); selAllowed = true; } if (availableDimension == AvailableDimension::SECOND) { restartCommand(QT_TRANSLATE_NOOP("Command", "Add horizontal coordinate dimensions")); createCoordDimension("DistanceX"); availableDimension = AvailableDimension::RESET; } } void makeCts_1Point1Line(bool& selAllowed) { //distance if (availableDimension == AvailableDimension::FIRST) { restartCommand(QT_TRANSLATE_NOOP("Command", "Add point to line Distance dimension")); createDistanceDimension("Distance", { selPoints[0], selLine[0] }); selAllowed = true; availableDimension = AvailableDimension::RESET; } } void makeCts_1Point1Circle(bool& selAllowed) { //Distance, extent distance if (availableDimension == AvailableDimension::FIRST) { restartCommand(QT_TRANSLATE_NOOP("Command", "Add length dimension")); createDistanceDimension("Distance", { selPoints[0], selCircleArc[0] }); selAllowed = true; } if (availableDimension == AvailableDimension::SECOND) { restartCommand(QT_TRANSLATE_NOOP("Command", "Add Extent dimension")); createExtentDistanceDimension("DistanceX"); availableDimension = AvailableDimension::RESET; } } void makeCts_1Point1Ellipse(bool& selAllowed) { //Distance if (availableDimension == AvailableDimension::FIRST) { restartCommand(QT_TRANSLATE_NOOP("Command", "Add length dimension")); createDistanceDimension("Distance", { selPoints[0], selEllipseArc[0] }); selAllowed = true; } if (availableDimension == AvailableDimension::SECOND) { restartCommand(QT_TRANSLATE_NOOP("Command", "Add Extent dimension")); createExtentDistanceDimension("DistanceX"); availableDimension = AvailableDimension::RESET; } } void makeCts_1Line(bool& selAllowed) { //distance if (availableDimension == AvailableDimension::FIRST) { restartCommand(QT_TRANSLATE_NOOP("Command", "Add length dimension")); createDistanceDimension("Distance", { selLine[0] }); specialDimension = SpecialDimension::LineOr2PointsDistance; selAllowed = true; if (!isVerticalDistance({ selLine[0] })) { availableDimension = AvailableDimension::RESET; } // Potential improvement for the future: we could show available modes in cursor trail. //std::vector pixmaps = { icon("TechDraw_LengthDimension"), icon("TechDraw_ExtensionCreateHorizChamferDimension") }; //addCursorTail(pixmaps); } if (availableDimension == AvailableDimension::SECOND) { restartCommand(QT_TRANSLATE_NOOP("Command", "Add DistanceX Chamfer dimension")); createDistanceDimension("DistanceX", { selLine[0] }, true); specialDimension = SpecialDimension::LineOr2PointsChamfer; availableDimension = AvailableDimension::RESET; } } void makeCts_2Line(bool& selAllowed) { //angle (if parallel: Distance (see in createAngleDimension)). if (availableDimension == AvailableDimension::FIRST) { restartCommand(QT_TRANSLATE_NOOP("Command", "Add Angle dimension")); createAngleDimension(selLine[0], selLine[1]); selAllowed = true; } if (availableDimension == AvailableDimension::SECOND) { restartCommand(QT_TRANSLATE_NOOP("Command", "Add Extent dimension")); createExtentDistanceDimension("DistanceX"); availableDimension = AvailableDimension::RESET; } } void makeCts_1Line1Circle(bool& selAllowed) { //distance, extent distance if (availableDimension == AvailableDimension::FIRST) { restartCommand(QT_TRANSLATE_NOOP("Command", "Add circle to line Distance dimension")); createDistanceDimension("Distance", { selCircleArc[0], selLine[0] }); selAllowed = true; } if (availableDimension == AvailableDimension::SECOND) { restartCommand(QT_TRANSLATE_NOOP("Command", "Add Extent dimension")); createExtentDistanceDimension("DistanceX"); availableDimension = AvailableDimension::RESET; } } void makeCts_1Line1Ellipse(bool& selAllowed) { //distance, extent distance if (availableDimension == AvailableDimension::FIRST) { restartCommand(QT_TRANSLATE_NOOP("Command", "Add ellipse to line Distance dimension")); createDistanceDimension("Distance", { selEllipseArc[0], selLine[0] }); selAllowed = true; } if (availableDimension == AvailableDimension::SECOND) { restartCommand(QT_TRANSLATE_NOOP("Command", "Add Extent dimension")); createExtentDistanceDimension("DistanceX"); availableDimension = AvailableDimension::RESET; } } void makeCts_1Circle(bool& selAllowed) { if (availableDimension == AvailableDimension::FIRST) { createRadiusDiameterDimension(selCircleArc[0], true); selAllowed = true; } if (availableDimension == AvailableDimension::SECOND) { createRadiusDiameterDimension(selCircleArc[0], false); if (selCircleArc[0].geomEdgeType() != TechDraw::ARCOFCIRCLE) { availableDimension = AvailableDimension::RESET; } } if (availableDimension == AvailableDimension::THIRD) { restartCommand(QT_TRANSLATE_NOOP("Command", "Add Arc Length dimension")); createArcLengthDimension(selCircleArc[0]); availableDimension = AvailableDimension::RESET; } } void makeCts_2Circle(bool& selAllowed) { //Distance if (availableDimension == AvailableDimension::FIRST) { restartCommand(QT_TRANSLATE_NOOP("Command", "Add circle to circle Distance dimension")); createDistanceDimension("Distance", { selCircleArc[0], selCircleArc[1] }); selAllowed = true; } if (availableDimension == AvailableDimension::SECOND) { restartCommand(QT_TRANSLATE_NOOP("Command", "Add Extent dimension")); createExtentDistanceDimension("DistanceX"); availableDimension = AvailableDimension::RESET; } } void makeCts_1Ellipse(bool& selAllowed) { if (availableDimension == AvailableDimension::FIRST) { createRadiusDiameterDimension(selEllipseArc[0], true); selAllowed = true; } if (availableDimension == AvailableDimension::SECOND) { createRadiusDiameterDimension(selEllipseArc[0], false); if (selEllipseArc[0].geomEdgeType() != TechDraw::ARCOFELLIPSE) { availableDimension = AvailableDimension::RESET; } } if (availableDimension == AvailableDimension::THIRD) { restartCommand(QT_TRANSLATE_NOOP("Command", "Add Arc Length dimension")); createArcLengthDimension(selEllipseArc[0]); availableDimension = AvailableDimension::RESET; } } void makeCts_2Ellipses(bool& selAllowed) { //Distance if (availableDimension == AvailableDimension::FIRST) { restartCommand(QT_TRANSLATE_NOOP("Command", "Add ellipse to ellipse Distance dimension")); createDistanceDimension("Distance", { selEllipseArc[0], selEllipseArc[1] }); selAllowed = true; } if (availableDimension == AvailableDimension::SECOND) { restartCommand(QT_TRANSLATE_NOOP("Command", "Add Extent dimension")); createExtentDistanceDimension("DistanceX"); availableDimension = AvailableDimension::RESET; } } void makeCts_1Spline(bool& selAllowed) { //Edge length if (availableDimension == AvailableDimension::FIRST) { restartCommand(QT_TRANSLATE_NOOP("Command", "Add edge length dimension")); createArcLengthDimension(selSplineAndCo[0]); selAllowed = true; availableDimension = AvailableDimension::RESET; } } void makeCts_1SplineAndMore(bool& selAllowed) { //Extend if (availableDimension == AvailableDimension::FIRST) { restartCommand(QT_TRANSLATE_NOOP("Command", "Add Extent dimension")); createExtentDistanceDimension("DistanceX"); selAllowed = true; availableDimension = AvailableDimension::RESET; } } void createAreaDimension(ReferenceEntry ref) { DrawViewDimension* dim = dimMaker(partFeat, "Area", { ref }, {}); dims.push_back(dim); moveDimension(mousePos, dim); } void createRadiusDiameterDimension(ReferenceEntry ref, bool firstCstr) { int GeoId(TechDraw::DrawUtil::getIndexFromName(ref.getSubName())); TechDraw::BaseGeomPtr geom = partFeat->getGeomByIndex(GeoId); bool isCircleGeom = (geom->getGeomType() == TechDraw::CIRCLE) || (geom->getGeomType() == TechDraw::ELLIPSE); // Use same preference as in sketcher? ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/TechDraw/dimensioning"); bool dimensioningDiameter = hGrp->GetBool("DimensioningDiameter", true); bool dimensioningRadius = hGrp->GetBool("DimensioningRadius", true); DrawViewDimension* dim; if ((firstCstr && dimensioningRadius && !dimensioningDiameter) || (!firstCstr && !dimensioningRadius && dimensioningDiameter) || (firstCstr && dimensioningRadius && dimensioningDiameter && !isCircleGeom) || (!firstCstr && dimensioningRadius && dimensioningDiameter && isCircleGeom)) { 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 }) != isAngle) { //isValidMultiEdge check if lines are parallel. restartCommand(QT_TRANSLATE_NOOP("Command", "Add Distance dimension")); createDistanceDimension("Distance", { ref1, ref2 }); return; } DrawViewDimension* dim = dimMaker(partFeat, "Angle", {ref1, ref2}, {}); dims.push_back(dim); moveDimension(mousePos, dim); } void create3pAngleDimension(ReferenceVector refs) { DrawViewDimension* dim = dimMaker(partFeat, "Angle3Pt", refs, {}); dims.push_back(dim); moveDimension(mousePos, dim); } void createArcLengthDimension(ReferenceEntry ref) { DrawViewDimension* dim = makeArcLengthDimension(ref); dims.push_back(dim); moveDimension(mousePos, dim); } void createDistanceDimension(std::string type, ReferenceVector refs, bool chamfer = false) { DrawViewDimension* dim = dimMaker(partFeat, type, refs, {}); if (chamfer) { // Add the angle to the label TechDraw::pointPair pp = dim->getLinearPoints(); float dx = pp.first().x - pp.second().x; float dy = pp.first().y - pp.second().y; int alpha = round(Base::toDegrees(abs(atan(type == "DistanceY" ? (dx / dy) : (dy / dx))))); std::string sAlpha = std::to_string(alpha); std::string formatSpec = dim->FormatSpec.getStrValue(); formatSpec = formatSpec + " x" + sAlpha + "°"; dim->FormatSpec.setValue(formatSpec); } dims.push_back(dim); moveDimension(mousePos, dim); } void createExtentDistanceDimension(std::string type) { specialDimension = SpecialDimension::ExtendDistance; DrawViewDimension* dim = DrawDimHelper::makeExtentDim(partFeat, type, allRefs()); dims.push_back(dim); moveDimension(mousePos, dim); } void updateDistanceType() { if (dims.empty()) { return; } auto type = static_cast(dims[0]->Type.getValue()); SpecialDimension backup = specialDimension; bool chamfer = specialDimension == SpecialDimension::LineOr2PointsChamfer; TechDraw::pointPair pp = dims[0]->getLinearPoints(); Base::Vector3d pnt1 = Rez::guiX(pp.first()); Base::Vector3d pnt2 = Rez::guiX(pp.second()); QPointF fpos = getDimPositionToBe(mousePos); double minX, minY, maxX, maxY; minX = min(pnt1.x, pnt2.x); maxX = max(pnt1.x, pnt2.x); minY = min(pnt1.y, pnt2.y); maxY = max(pnt1.y, pnt2.y); std::string newType = "Distance"; if (fpos.x() > minX && fpos.x() < maxX && (fpos.y() < minY || fpos.y() > maxY) && type != DimensionType::DistanceX) { if (chamfer) { restartCommand(QT_TRANSLATE_NOOP("Command", "Add DistanceX Chamfer dimension")); } else { restartCommand(QT_TRANSLATE_NOOP("Command", "Add DistanceX dimension")); } newType = "DistanceX"; } else if (fpos.y() > minY && fpos.y() < maxY && (fpos.x() < minX || fpos.x() > maxX) && type != DimensionType::DistanceY) { if (chamfer) { restartCommand(QT_TRANSLATE_NOOP("Command", "Add DistanceY Chamfer dimension")); } else { restartCommand(QT_TRANSLATE_NOOP("Command", "Add DistanceY dimension")); } newType = "DistanceY"; } else if ((((fpos.y() < minY || fpos.y() > maxY) && (fpos.x() < minX || fpos.x() > maxX)) || (fpos.y() > minY && fpos.y() < maxY && fpos.x() > minX && fpos.x() < maxX)) && type != DimensionType::Distance && !chamfer) { restartCommand(QT_TRANSLATE_NOOP("Command", "Add Distance dimension")); } else { return; } specialDimension = backup; if (selLine.size() == 1) { createDistanceDimension(newType, { selLine[0] }, chamfer); } else { createDistanceDimension(newType, { selPoints[0], selPoints[1] }, chamfer); } setDimsSelectability(false); } void updateExtentDistanceType() { if (dims.empty()) { return; } auto type = static_cast(dims[0]->Type.getValue()); TechDraw::pointPair pp = dims[0]->getLinearPoints(); Base::Vector3d pnt1 = Rez::guiX(pp.first()); Base::Vector3d pnt2 = Rez::guiX(pp.second()); QPointF fpos = getDimPositionToBe(mousePos); double minX, minY, maxX, maxY; minX = min(pnt1.x, pnt2.x); maxX = max(pnt1.x, pnt2.x); minY = min(pnt1.y, pnt2.y); maxY = max(pnt1.y, pnt2.y); if (fpos.x() > minX && fpos.x() < maxX && (fpos.y() < minY || fpos.y() > maxY) && type != DimensionType::DistanceX) { restartCommand(QT_TRANSLATE_NOOP("Command", "Add DistanceX extent dimension")); createExtentDistanceDimension("DistanceX"); } else if (fpos.y() > minY && fpos.y() < maxY && (fpos.x() < minX || fpos.x() > maxX) && type != DimensionType::DistanceY) { restartCommand(QT_TRANSLATE_NOOP("Command", "Add DistanceY extent dimension")); createExtentDistanceDimension("DistanceY"); } else { return; } setDimsSelectability(false); } void updateChainDistanceType() { if (dims.empty()) { return; } double minX = DBL_MAX; double minY = DBL_MAX; double maxX = -DBL_MAX; double maxY = -DBL_MAX; for (auto dim : dims) { TechDraw::pointPair pp = dim->getLinearPoints(); Base::Vector3d pnt1 = Rez::guiX(pp.first()); Base::Vector3d pnt2 = Rez::guiX(pp.second()); minX = min(minX, min(pnt1.x, pnt2.x)); maxX = max(maxX, max(pnt1.x, pnt2.x)); minY = min(minY, min(pnt1.y, pnt2.y)); maxY = max(maxY, max(pnt1.y, pnt2.y)); } QPointF fpos = getDimPositionToBe(mousePos); auto type = static_cast(dims[0]->Type.getValue()); if (fpos.x() > minX && fpos.x() < maxX && (fpos.y() < minY || fpos.y() > maxY) && type != DimensionType::DistanceX) { if (specialDimension == SpecialDimension::ChainDistance) { restartCommand(QT_TRANSLATE_NOOP("Command", "Add horizontal chain dimensions")); createChainDimension("DistanceX"); } else { restartCommand(QT_TRANSLATE_NOOP("Command", "Add horizontal coord dimensions")); createCoordDimension("DistanceX"); } } else if (fpos.y() > minY && fpos.y() < maxY && (fpos.x() < minX || fpos.x() > maxX) && type != DimensionType::DistanceY) { if (specialDimension == SpecialDimension::ChainDistance) { restartCommand(QT_TRANSLATE_NOOP("Command", "Add vertical chain dimensions")); createChainDimension("DistanceY"); } else { restartCommand(QT_TRANSLATE_NOOP("Command", "Add vertical coord dimensions")); createCoordDimension("DistanceY"); } } else if (((fpos.y() < minY || fpos.y() > maxY) && (fpos.x() < minX || fpos.x() > maxX)) && type != DimensionType::Distance) { if (specialDimension == SpecialDimension::ChainDistance) { restartCommand(QT_TRANSLATE_NOOP("Command", "Add oblique chain dimensions")); createChainDimension("Distance"); } else { restartCommand(QT_TRANSLATE_NOOP("Command", "Add oblique coord dimensions")); createCoordDimension("Distance"); } } else { return; } setDimsSelectability(false); } void createChainDimension(std::string type) { specialDimension = SpecialDimension::ChainDistance; if (type == "Distance") { dims = makeObliqueChainDimension(selPoints); } else { for (size_t i = 0; i < selPoints.size() - 1; ++i) { DrawViewDimension* dim = dimMaker(partFeat, type, { selPoints[i], selPoints[i + 1] }, {}); dims.push_back(dim); positionDimText(dim); } } } void createCoordDimension(std::string type) { specialDimension = SpecialDimension::CoordDistance; if (type == "Distance") { dims = makeObliqueCoordDimension(selPoints); } else { for (size_t i = 0; i < selPoints.size() - 1; ++i) { DrawViewDimension* dim = dimMaker(partFeat, type, { selPoints[0], selPoints[i + 1] }, {}); dims.push_back(dim); positionDimText(dim, i); } } } bool isVerticalDistance(ReferenceVector refs) { DimensionGeometryType geometryRefs2d = validateDimSelection( refs, { "Edge", "Vertex" }, { 1, 2 }, { isDiagonal }); return geometryRefs2d == TechDraw::isDiagonal; } QPixmap icon(std::string name) { qreal pixelRatio = 1; Gui::View3DInventorViewer* viewer = getViewer(); if (viewer) { pixelRatio = viewer->devicePixelRatio(); } int width = 16 * pixelRatio; return Gui::BitmapFactory().pixmapFromSvg(name.c_str(), QSize(width, width)); } void restartCommand(const char* cstrName) { specialDimension = SpecialDimension::None; Gui::Command::abortCommand(); Gui::Command::openCommand(cstrName); dims.clear(); } void clearAndRestartCommand() { Gui::Command::abortCommand(); Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Dimension")); specialDimension = SpecialDimension::None; mousePos = QPoint(0,0); clearRefVectors(); partFeat = nullptr; dims.clear(); } void clearRefVectors() { selPoints.clear(); selLine.clear(); selCircleArc.clear(); selEllipseArc.clear(); selSplineAndCo.clear(); selFaces.clear(); } }; DEF_STD_CMD_A(CmdTechDrawDimension) CmdTechDrawDimension::CmdTechDrawDimension() : Command("TechDraw_Dimension") { sAppModule = "TechDraw"; sGroup = QT_TR_NOOP("TechDraw"); sMenuText = QT_TR_NOOP("Insert Dimension"); sToolTipText = QT_TR_NOOP("Dimension contextually based on your selection.\n" "Depending on your selection you might have several dimensions available. You can cycle through them using the M key.\n" "Left clicking on empty space will validate the current 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("Insert Radius Dimension"); sToolTipText = sMenuText; 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 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( {isCircle, isEllipse, isBSplineCircle, 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("Insert Diameter Dimension"); sToolTipText = sMenuText; 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 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( {isCircle, isEllipse, isBSplineCircle, 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("Insert Length Dimension"); sToolTipText = sMenuText; 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 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( {isVertical, isHorizontal, isDiagonal, 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("Insert Horizontal Dimension"); sToolTipText = sMenuText; 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 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({isHorizontal, isDiagonal, 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("Insert Vertical Dimension"); sToolTipText = sMenuText; 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 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({isVertical, isDiagonal, 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("Insert Angle Dimension"); sToolTipText = sMenuText; 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 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({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("Insert 3-Point Angle Dimension"); sToolTipText = sMenuText; 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 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({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("Insert Area Annotation"); sToolTipText = sMenuText; sWhatsThis = "TechDraw_AreaDimension"; sStatusTip = sToolTipText; sPixmap = "TechDraw_AreaDimension"; } void CmdTechDrawAreaDimension::activated(int iMsg) { Q_UNUSED(iMsg); Gui::TaskView::TaskDialog* dlg = Gui::Control().activeDialog(); if (dlg) { QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Task In Progress"), QObject::tr("Close active task dialog and try again.")); return; } execArea(this); } bool CmdTechDrawAreaDimension::isActive() { 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({isFace}); execDim(cmd, "Area", acceptableGeometry, minimumCounts, acceptableDimensionGeometrys); } // TechDraw_LinkDimension is DEPRECATED. Use TechDraw_DimensionRepair instead. //! link 3D geometry to Dimension(s) on a Page //TODO: should we present all potential Dimensions from all Pages? //=========================================================================== // TechDraw_LinkDimension //=========================================================================== DEF_STD_CMD_A(CmdTechDrawLinkDimension) CmdTechDrawLinkDimension::CmdTechDrawLinkDimension() : Command("TechDraw_LinkDimension") { sAppModule = "TechDraw"; sGroup = QT_TR_NOOP("TechDraw"); sMenuText = QT_TR_NOOP("Link Dimension to 3D Geometry"); sToolTipText = sMenuText; sWhatsThis = "TechDraw_LinkDimension"; sStatusTip = sToolTipText; sPixmap = "TechDraw_LinkDimension"; } void CmdTechDrawLinkDimension::activated(int iMsg) { Q_UNUSED(iMsg); TechDraw::DrawPage* page = DrawGuiUtil::findPage(this); if (!page) { return; } bool result = _checkSelection(this, 2); if (!result) { return; } std::vector selection = getSelection().getSelectionEx( nullptr, App::DocumentObject::getClassTypeId(), Gui::ResolveMode::NoResolve); App::DocumentObject* obj3D = nullptr; std::vector parts; std::vector subs; std::vector::iterator itSel = selection.begin(); for (; itSel != selection.end(); itSel++) { obj3D = ((*itSel).getObject()); std::vector subList = (*itSel).getSubNames(); for (auto& s : subList) { parts.push_back(obj3D); subs.push_back(s); } } if (!obj3D) { QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Incorrect Selection"), QObject::tr("There is no 3D object in your selection")); return; } if (subs.empty()) { QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Incorrect Selection"), QObject::tr("There are no 3D Edges or Vertices in your selection")); return; } // dialog to select the Dimension to link Gui::Control().showDialog(new TaskDlgLinkDim(parts, subs, page)); page->getDocument()->recompute();//still need to recompute in Gui. why? } bool CmdTechDrawLinkDimension::isActive() { bool havePage = DrawGuiUtil::needPage(this); bool haveView = DrawGuiUtil::needView(this); bool taskInProgress = false; if (havePage) { taskInProgress = Gui::Control().activeDialog(); } return (havePage && haveView && !taskInProgress); } //=========================================================================== // TechDraw_ExtentGroup //=========================================================================== DEF_STD_CMD_ACL(CmdTechDrawExtentGroup) CmdTechDrawExtentGroup::CmdTechDrawExtentGroup() : Command("TechDraw_ExtentGroup") { sAppModule = "TechDraw"; sGroup = QT_TR_NOOP("TechDraw"); sMenuText = QT_TR_NOOP("Insert Extent Dimension"); sToolTipText = sMenuText; sWhatsThis = "TechDraw_ExtentGroup"; sStatusTip = sToolTipText; // eType = ForEdit; } void CmdTechDrawExtentGroup::activated(int iMsg) { // Base::Console().Message("CMD::ExtentGrp - 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; } 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(QString::fromLatin1("TechDraw_HorizontalExtentDimension")); p1->setWhatsThis(QString::fromLatin1("TechDraw_HorizontalExtentDimension")); QAction* p2 = pcAction->addAction(QString()); p2->setIcon(Gui::BitmapFactory().iconFromTheme("TechDraw_VerticalExtentDimension")); p2->setObjectName(QString::fromLatin1("TechDraw_VerticalExtentDimension")); p2->setWhatsThis(QString::fromLatin1("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("Insert Horizontal Extent Dimension"); sToolTipText = sMenuText; 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 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) { 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); // 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")); return; } } } //Define the geometric configuration required for a extent dimension StringVector acceptableGeometry({"Edge"}); std::vector minimumCounts({1}); std::vector acceptableDimensionGeometrys({isMultiEdge, isHorizontal, isVertical, isDiagonal, isCircle, isEllipse, isBSplineCircle, isBSpline, isZLimited}); //what 2d geometry configuration did we receive? DimensionGeometryType geometryRefs2d = validateDimSelection( references2d, acceptableGeometry, minimumCounts, acceptableDimensionGeometrys); if (geometryRefs2d == TechDraw::isInvalid) { QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Incorrect Selection"), QObject::tr("Can not make 2D extent dimension from selection")); return; } //what 3d geometry configuration did we receive? DimensionGeometryType geometryRefs3d; if (geometryRefs2d == TechDraw::isViewReference && !references3d.empty()) { geometryRefs3d = validateDimSelection3d(partFeat, references3d, acceptableGeometry, minimumCounts, acceptableDimensionGeometrys); if (geometryRefs3d == isInvalid) { QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Incorrect Selection"), QObject::tr("Can not make 3D extent dimension from selection")); return; } } if (references3d.empty()) { DrawDimHelper::makeExtentDim(partFeat, dimType, references2d); } else { DrawDimHelper::makeExtentDim3d(partFeat, dimType, references3d); } } //=========================================================================== // TechDraw_VerticalExtentDimension //=========================================================================== DEF_STD_CMD_A(CmdTechDrawVerticalExtentDimension) CmdTechDrawVerticalExtentDimension::CmdTechDrawVerticalExtentDimension() : Command("TechDraw_VerticalExtentDimension") { sAppModule = "TechDraw"; sGroup = QT_TR_NOOP("TechDraw"); sMenuText = QT_TR_NOOP("Insert Vertical Extent Dimension"); sToolTipText = sMenuText; 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 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 = sMenuText; 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); } //NOTE: to be deprecated. revisions to the basic dimension allows it to handle //everything that the Landmark Dimension was created to handle. //=========================================================================== // TechDraw_LandmarkDimension //=========================================================================== DEF_STD_CMD_A(CmdTechDrawLandmarkDimension) CmdTechDrawLandmarkDimension::CmdTechDrawLandmarkDimension() : Command("TechDraw_LandmarkDimension") { sAppModule = "TechDraw"; sGroup = QT_TR_NOOP("TechDraw"); sMenuText = QT_TR_NOOP("Insert Landmark Dimension - EXPERIMENTAL"); sToolTipText = sMenuText; sWhatsThis = "TechDraw_LandmarkDimension"; sStatusTip = sToolTipText; sPixmap = "TechDraw_LandmarkDimension"; } void CmdTechDrawLandmarkDimension::activated(int iMsg) { Q_UNUSED(iMsg); bool result = _checkSelection(this, 3); if (!result) { return; } const std::vector objects = getSelection().getObjectsOfType(Part::Feature::getClassTypeId());//?? if (objects.size() != 2) { QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"), QObject::tr("Select 2 point objects and 1 View. (1)")); return; } const std::vector views = getSelection().getObjectsOfType(TechDraw::DrawViewPart::getClassTypeId()); if (views.size() != 1) { QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"), QObject::tr("Select 2 point objects and 1 View. (2)")); return; } TechDraw::DrawViewPart* dvp = static_cast(views.front()); std::vector refs2d; std::vector subs; subs.emplace_back("Vertex1"); subs.emplace_back("Vertex1"); TechDraw::DrawPage* page = dvp->findParentPage(); std::string parentName = dvp->getNameInDocument(); std::string PageName = page->getNameInDocument(); TechDraw::LandmarkDimension* dim = nullptr; std::string FeatName = getUniqueObjectName("LandmarkDim"); openCommand(QT_TRANSLATE_NOOP("Command", "Create Dimension")); doCommand(Doc, "App.activeDocument().addObject('TechDraw::LandmarkDimension', '%s')", FeatName.c_str()); doCommand(Doc, "App.activeDocument().%s.translateLabel('LandmarkDimension', 'LandmarkDim', '%s')", FeatName.c_str(), FeatName.c_str()); if (objects.size() == 2) { //what about distanceX and distanceY?? doCommand(Doc, "App.activeDocument().%s.Type = '%s'", FeatName.c_str(), "Distance"); refs2d.push_back(dvp); refs2d.push_back(dvp); } // } else if (objects.size() == 3) { //not implemented yet // doCommand(Doc, "App.activeDocument().%s.Type = '%s'", FeatName.c_str(), "Angle3Pt"); // refs2d.push_back(dvp); // refs2d.push_back(dvp); // refs2d.push_back(dvp); // //subs.push_back("Vertex1"); // //subs.push_back("Vertex1"); // //subs.push_back("Vertex1"); // } dim = dynamic_cast(getDocument()->getObject(FeatName.c_str())); if (!dim) { throw Base::TypeError("CmdTechDrawLandmarkDimension - dim not found\n"); } dim->References2D.setValues(refs2d, subs); dim->References3D.setValues(objects, subs); doCommand(Doc, "App.activeDocument().%s.addView(App.activeDocument().%s)", PageName.c_str(), FeatName.c_str()); commitCommand(); // Touch the parent feature so the dimension in tree view appears as a child dvp->touch(true); dim->recomputeFeature(); } bool CmdTechDrawLandmarkDimension::isActive() { return isDimCmdActive(this); } //------------------------------------------------------------------------------ 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 CmdTechDrawLinkDimension()); rcCmdMgr.addCommand(new CmdTechDrawLandmarkDimension()); 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? DimensionGeometryType geometryRefs2d = validateDimSelection( references2d, acceptableGeometry, minimumCounts, acceptableDimensionGeometrys); if (geometryRefs2d == TechDraw::isInvalid) { QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Incorrect Selection"), QObject::tr("Can not make 2D dimension from selection")); return; } //what 3d geometry configuration did we receive? DimensionGeometryType geometryRefs3d{TechDraw::isInvalid}; if (geometryRefs2d == TechDraw::isViewReference && !references3d.empty()) { geometryRefs3d = validateDimSelection3d(partFeat, references3d, acceptableGeometry, minimumCounts, acceptableDimensionGeometrys); if (geometryRefs3d == TechDraw::isInvalid) { QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Incorrect Selection"), QObject::tr("Can not make 3D dimension from selection")); return; } } else { references3d.clear(); } //errors and warnings if (type == "Radius" || type == "Diameter") { if (geometryRefs2d == isEllipse || geometryRefs3d == 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 == isBSplineCircle || geometryRefs3d == 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 == isBSpline || geometryRefs3d == isBSpline) { QMessageBox::critical( Gui::getMainWindow(), QObject::tr("B-spline Curve Error"), QObject::tr("Selected edge is a B-spline and a radius/diameter can not be calculated.")); return; } } //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; }