diff --git a/src/Mod/Sketcher/Gui/CommandConstraints.cpp b/src/Mod/Sketcher/Gui/CommandConstraints.cpp index 37b1701d4b..fe7d5c0111 100644 --- a/src/Mod/Sketcher/Gui/CommandConstraints.cpp +++ b/src/Mod/Sketcher/Gui/CommandConstraints.cpp @@ -50,7 +50,7 @@ #include "Utils.h" #include "ViewProviderSketch.h" #include "ui_InsertDatum.h" - +#include using namespace std; using namespace SketcherGui; @@ -990,6 +990,1255 @@ void CmdSketcherConstraint::activated(int /*iMsg*/) getSelection().clearSelection(); } + +// Dimension tool ======================================================= + +class GeomSelectionSizes +{ +public: + GeomSelectionSizes(size_t s_pts, size_t s_lns, size_t s_cir, size_t s_ell) : + s_pts(s_pts), s_lns(s_lns), s_cir(s_cir), s_ell(s_ell) {} + ~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 has1Point() const { return s_pts == 1 && s_lns == 0 && s_cir == 0 && s_ell == 0; } + bool has2Points() const { return s_pts == 2 && s_lns == 0 && s_cir == 0 && s_ell == 0; } + bool has1Point1Line() const { return s_pts == 1 && s_lns == 1 && s_cir == 0 && s_ell == 0; } + bool has3Points() const { return s_pts == 3 && s_lns == 0 && s_cir == 0 && s_ell == 0; } + bool has4MorePoints() const { return s_pts >= 4 && s_lns == 0 && s_cir == 0 && s_ell == 0; } + bool has2Points1Line() const { return s_pts == 2 && s_lns == 1 && s_cir == 0 && s_ell == 0; } + bool has3MorePoints1Line() const { return s_pts >= 3 && s_lns == 1 && s_cir == 0 && s_ell == 0; } + bool has1MorePoint1Circle() const { return s_pts >= 1 && s_lns == 0 && s_cir == 1 && s_ell == 0; } + bool has1MorePoint1Ellipse() const { return s_pts >= 1 && s_lns == 0 && s_cir == 0 && s_ell == 1; } + + bool has1Line() const { return s_pts == 0 && s_lns == 1 && s_cir == 0 && s_ell == 0; } + bool has2Lines() const { return s_pts == 0 && s_lns == 2 && s_cir == 0 && s_ell == 0; } + bool has3MoreLines() const { return s_pts == 0 && s_lns >= 3 && s_cir == 0 && s_ell == 0; } + bool has1Line1Circle() const { return s_pts == 0 && s_lns == 1 && s_cir == 1 && s_ell == 0; } + bool has1Line2Circles() const { return s_pts == 0 && s_lns == 1 && s_cir == 2 && s_ell == 0; } + bool has1Line1Ellipse() const { return s_pts == 0 && s_lns == 1 && s_cir == 0 && s_ell == 1; } + + bool has1Circle() const { return s_pts == 0 && s_lns == 0 && s_cir == 1 && s_ell == 0; } + bool has2Circles() const { return s_pts == 0 && s_lns == 0 && s_cir == 2 && s_ell == 0; } + bool has3MoreCircles() const { return s_pts == 0 && s_lns == 0 && s_cir >= 3 && s_ell == 0; } + bool has1Circle1Ellipse() const { return s_pts == 0 && s_lns == 0 && s_cir == 1 && s_ell == 1; } + + bool has1Ellipse() const { return s_pts == 0 && s_lns == 0 && s_cir == 0 && s_ell == 1; } + bool has2MoreEllipses() const { return s_pts == 0 && s_lns == 0 && s_cir == 0 && s_ell >= 2; } + + size_t s_pts, s_lns, s_cir, s_ell; +}; + +class DrawSketchHandlerDimension : public DrawSketchHandler +{ +public: + DrawSketchHandlerDimension() + : specialConstraint(SpecialConstraint::None) + , availableConstraint(AvailableConstraint::FIRST) + , previousOnSketchPos(Base::Vector2d(0.f, 0.f)) + , selPoints({}) + , selLine({}) + , selCircleArc({}) + , selEllipseAndCo({}) + , numberOfConstraintsCreated(0) + { + } + ~DrawSketchHandlerDimension() override + { + } + + enum class AvailableConstraint { + FIRST, + SECOND, + THIRD, + FOURTH, + FIFTH, + RESET + }; + + enum class SpecialConstraint { + LineOr2PointsDistance, + Block, + None + }; + + void activated() override + { + Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Dimension")); + + Obj = sketchgui->getSketchObject(); + + // Constrain icon size in px + qreal pixelRatio = devicePixelRatio(); + const unsigned long defaultCrosshairColor = 0xFFFFFF; + unsigned long color = getCrosshairColor(); + auto colorMapping = std::map(); + colorMapping[defaultCrosshairColor] = color; + + qreal fullIconWidth = 32 * pixelRatio; + qreal iconWidth = 16 * pixelRatio; + QPixmap cursorPixmap = Gui::BitmapFactory().pixmapFromSvg("Sketcher_Crosshair", QSizeF(fullIconWidth, fullIconWidth), colorMapping), + icon = Gui::BitmapFactory().pixmapFromSvg("Constraint_Dimension", QSizeF(iconWidth, iconWidth)); + QPainter cursorPainter; + cursorPainter.begin(&cursorPixmap); + cursorPainter.drawPixmap(16 * pixelRatio, 16 * pixelRatio, icon); + cursorPainter.end(); + int hotX = 8; + int hotY = 8; + cursorPixmap.setDevicePixelRatio(pixelRatio); + // only X11 needs hot point coordinates to be scaled + if (qGuiApp->platformName() == QLatin1String("xcb")) { + hotX *= pixelRatio; + hotY *= pixelRatio; + } + setCursor(cursorPixmap, hotX, hotY, false); + } + + void deactivated() override + { + Gui::Command::abortCommand(); + Obj->solve(); + sketchgui->draw(false, false); // Redraw + } + + void registerPressedKey(bool pressed, int key) override + { + if ((key == SoKeyboardEvent::RIGHT_SHIFT || key == SoKeyboardEvent::LEFT_SHIFT) && pressed) { + if (availableConstraint == AvailableConstraint::FIRST) { + availableConstraint = AvailableConstraint::SECOND; + } + else if (availableConstraint == AvailableConstraint::SECOND) { + availableConstraint = AvailableConstraint::THIRD; + } + else if (availableConstraint == AvailableConstraint::THIRD) { + availableConstraint = AvailableConstraint::FOURTH; + } + else if (availableConstraint == AvailableConstraint::FOURTH) { + availableConstraint = AvailableConstraint::FIFTH; + } + else if (availableConstraint == AvailableConstraint::FIFTH || availableConstraint == AvailableConstraint::RESET) { + availableConstraint = AvailableConstraint::FIRST; + } + makeAppropriateConstraint(previousOnSketchPos); + } + } + + void mouseMove(Base::Vector2d onSketchPos) override + { + const std::vector& ConStr = Obj->Constraints.getValues(); + previousOnSketchPos = onSketchPos; + + //Change distance constraint based on position of mouse. + if (specialConstraint == SpecialConstraint::LineOr2PointsDistance) + updateDistanceType(onSketchPos); + + //Move constraints + if (numberOfConstraintsCreated > 0) { + bool oneMoved = false; + for (int i = 0; i < numberOfConstraintsCreated; i++) { + if (ConStr[ConStr.size() - 1 - i]->isDimensional()) { + Base::Vector2d pointWhereToMove = onSketchPos; + + if (specialConstraint == SpecialConstraint::Block) { + if (i == 0) + pointWhereToMove.x = Obj->getPoint(selPoints[0].GeoId, selPoints[0].PosId).x; + else + pointWhereToMove.y = Obj->getPoint(selPoints[0].GeoId, selPoints[0].PosId).y; + } + moveConstraint(ConStr.size() - 1 - i, pointWhereToMove); + oneMoved = true; + } + } + if (oneMoved) + sketchgui->draw(false, false); // Redraw + } + } + + bool pressButton(Base::Vector2d onSketchPos) override + { + return true; + } + + bool releaseButton(Base::Vector2d onSketchPos) override + { + Q_UNUSED(onSketchPos); + + availableConstraint = AvailableConstraint::FIRST; + SelIdPair selIdPair; + selIdPair.GeoId = GeoEnum::GeoUndef; + selIdPair.PosId = Sketcher::PointPos::none; + std::stringstream ss; + Base::Type newselGeoType = Base::Type::badType(); + + int VtId = getPreselectPoint(); + int CrvId = getPreselectCurve(); + int CrsId = getPreselectCross(); + + if (VtId >= 0) { //Vertex + Obj->getGeoVertexIndex(VtId, + selIdPair.GeoId, selIdPair.PosId); + newselGeoType = Part::GeomPoint::getClassTypeId(); + ss << "Vertex" << VtId + 1; + } + else if (CrsId == 0) { //RootPoint + selIdPair.GeoId = Sketcher::GeoEnum::RtPnt; + selIdPair.PosId = Sketcher::PointPos::start; + newselGeoType = Part::GeomPoint::getClassTypeId(); + ss << "RootPoint"; + } + else if (CrsId == 1) { //H_Axis + selIdPair.GeoId = Sketcher::GeoEnum::HAxis; + newselGeoType = Part::GeomLineSegment::getClassTypeId(); + ss << "H_Axis"; + } + else if (CrsId == 2) { //V_Axis + selIdPair.GeoId = Sketcher::GeoEnum::VAxis; + newselGeoType = Part::GeomLineSegment::getClassTypeId(); + ss << "V_Axis"; + } + else if (CrvId >= 0 || CrvId <= Sketcher::GeoEnum::RefExt) { //Curves + selIdPair.GeoId = CrvId; + const Part::Geometry* geo = Obj->getGeometry(CrvId); + + newselGeoType = geo->getTypeId(); + + if (CrvId >= 0) { + ss << "Edge" << CrvId + 1; + } + else { + ss << "ExternalEdge" << Sketcher::GeoEnum::RefExt + 1 - CrvId; + } + } + + + if (selIdPair.GeoId == GeoEnum::GeoUndef) { + // If mouse is released on "blank" space, finalize and start over + finalizeCommand(); + } + + else if (notSelectedYet(selIdPair)) { + std::vector& selVector = getSelectionVector(newselGeoType); + + //add the geometry to its type vector. Temporarily if not selAllowed + selVector.push_back(selIdPair); + + bool selAllowed = makeAppropriateConstraint(onSketchPos); + + if (selAllowed) { + // If mouse is released on something allowed, select it + Gui::Selection().addSelection(Obj->getDocument()->getName(), + Obj->getNameInDocument(), + ss.str().c_str(), onSketchPos.x, onSketchPos.y, 0.f); + sketchgui->draw(false, false); // Redraw + } + else { + selVector.pop_back(); + } + } + return true; + } +protected: + SpecialConstraint specialConstraint; + AvailableConstraint availableConstraint; + + Base::Vector2d previousOnSketchPos; + + std::vector selPoints; + std::vector selLine; + std::vector selCircleArc; + std::vector selEllipseAndCo; + + int numberOfConstraintsCreated; + + Sketcher::SketchObject* Obj; + + void finalizeCommand() + { + // Ask for the value of datum constraints + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher"); + bool show = hGrp->GetBool("ShowDialogOnDistanceConstraint", true); + const std::vector& ConStr = Obj->Constraints.getValues(); + + bool commandHandledInEditDatum = false; + for (int i = numberOfConstraintsCreated - 1; i >= 0; i--) { + if (show && ConStr[ConStr.size() - 1 - i]->isDimensional() && ConStr[ConStr.size() - 1 - i]->isDriving) { + commandHandledInEditDatum = true; + EditDatumDialog editDatumDialog(sketchgui, ConStr.size() - 1 - i); + editDatumDialog.exec(); + if (!editDatumDialog.isSuccess()) { + break; + } + } + } + + if (!commandHandledInEditDatum) + Gui::Command::commitCommand(); + + // This code enables the continuous creation mode. + bool continuousMode = hGrp->GetBool("ContinuousCreationMode", true); + if (continuousMode) { + Gui::Selection().clearSelection(); + Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Dimension")); + numberOfConstraintsCreated = 0; + specialConstraint = SpecialConstraint::None; + previousOnSketchPos = Base::Vector2d(0.f, 0.f); + selPoints.clear(); + selLine.clear(); + selCircleArc.clear(); + selEllipseAndCo.clear(); + } + else { + sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider + } + } + + std::vector& getSelectionVector(Base::Type selGeoType) { + if (selGeoType == Part::GeomPoint::getClassTypeId()) { + return selPoints; + } + else if (selGeoType == Part::GeomLineSegment::getClassTypeId()) { + return selLine; + } + else if (selGeoType == Part::GeomArcOfCircle::getClassTypeId() || + selGeoType == Part::GeomCircle::getClassTypeId()) { + return selCircleArc; + } + else if (selGeoType == Part::GeomEllipse::getClassTypeId() || + selGeoType == Part::GeomArcOfEllipse::getClassTypeId() || + selGeoType == Part::GeomArcOfHyperbola::getClassTypeId() || + selGeoType == Part::GeomArcOfParabola::getClassTypeId()) { + return selEllipseAndCo; + } + + static std::vector emptyVector; + return emptyVector; + } + + bool notSelectedYet(const SelIdPair& elem) + { + auto contains = [&](const std::vector& vec, const SelIdPair& elem) { + for (const auto& x : vec) + { + if (x.GeoId == elem.GeoId && x.PosId == elem.PosId) + return true; + } + return false; + }; + + return !contains(selPoints, elem) + && !contains(selLine, elem) + && !contains(selCircleArc, elem) + && !contains(selEllipseAndCo, elem); + } + + bool makeAppropriateConstraint(Base::Vector2d onSketchPos) { + bool selAllowed = false; + + GeomSelectionSizes selection(selPoints.size(), selLine.size(), selCircleArc.size(), selEllipseAndCo.size()); + + if (selection.hasPoints()) { + if (selection.has1Point()) { makeCts_1Point(selAllowed, onSketchPos); } + else if (selection.has2Points()) { makeCts_2Point(selAllowed, onSketchPos); } + else if (selection.has1Point1Line()) { makeCts_1Point1Line(selAllowed, onSketchPos); } + else if (selection.has3Points()) { makeCts_3Point(selAllowed, selection.s_pts); } + else if (selection.has4MorePoints()) { makeCts_4MorePoint(selAllowed, selection.s_pts); } + else if (selection.has2Points1Line()) { makeCts_2Point1Line(selAllowed, onSketchPos, selection.s_pts); } + else if (selection.has3MorePoints1Line()) { makeCts_3MorePoint1Line(selAllowed, onSketchPos, selection.s_pts); } + else if (selection.has1MorePoint1Circle()) { makeCts_1MorePoint1Circle(selAllowed); } + else if (selection.has1MorePoint1Ellipse()) { makeCts_1MorePoint1Ellipse(selAllowed); } + } + else if (selection.hasLines()) { + if (selection.has1Line()) { makeCts_1Line(selAllowed, onSketchPos); } + else if (selection.has2Lines()) { makeCts_2Line(selAllowed, onSketchPos); } + else if (selection.has3MoreLines()) { makeCts_3MoreLine(selAllowed, selection.s_lns); } + else if (selection.has1Line1Circle()) { makeCts_1Line1Circle(selAllowed, onSketchPos); } + else if (selection.has1Line2Circles()) { makeCts_1Line2Circle(selAllowed); } + else if (selection.has1Line1Ellipse()) { makeCts_1Line1Ellipse(selAllowed); } + } + else if (selection.hasCirclesOrArcs()) { + if (selection.has1Circle()) { makeCts_1Circle(selAllowed, onSketchPos); } + else if (selection.has2Circles()) { makeCts_2Circle(selAllowed, onSketchPos); } + else if (selection.has3MoreCircles()) { makeCts_3MoreCircle(selAllowed, selection.s_cir); } + else if (selection.has1Circle1Ellipse()) { makeCts_1Circle1Ellipse(selAllowed); } + } + else if (selection.hasEllipseAndCo()) { + if (selection.has1Ellipse()) { makeCts_1Ellipse(selAllowed); } + else if (selection.has2MoreEllipses()) { makeCts_2MoreEllipse(selAllowed, selection.s_ell); } + } + return selAllowed; + } + + void makeCts_1Point(bool& selAllowed, Base::Vector2d onSketchPos) + { + //Lock, autodistance + if (availableConstraint == AvailableConstraint::FIRST) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add lock constraint")); + specialConstraint = SpecialConstraint::Block; + createDistanceXYConstrain(Sketcher::DistanceX, selPoints[0].GeoId, selPoints[0].PosId, Sketcher::GeoEnum::RtPnt, Sketcher::PointPos::start, onSketchPos); + createDistanceXYConstrain(Sketcher::DistanceY, selPoints[0].GeoId, selPoints[0].PosId, Sketcher::GeoEnum::RtPnt, Sketcher::PointPos::start, onSketchPos); + selAllowed = true; + } + if (availableConstraint == AvailableConstraint::SECOND) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add 'Distance to origin' constraint")); + createDistanceConstrain(selPoints[0].GeoId, selPoints[0].PosId, Sketcher::GeoEnum::RtPnt, Sketcher::PointPos::start, onSketchPos); + availableConstraint = AvailableConstraint::RESET; + } + } + void makeCts_2Point(bool& selAllowed, Base::Vector2d onSketchPos) + { + //distance, horizontal, vertical + if (availableConstraint == AvailableConstraint::FIRST) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add Distance constraint")); + createDistanceConstrain(selPoints[0].GeoId, selPoints[0].PosId, selPoints[1].GeoId, selPoints[1].PosId, onSketchPos); + selAllowed = true; + } + if (availableConstraint == AvailableConstraint::SECOND) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add 'Horizontal' constraints")); + createHorizontalConstrain(selPoints[0].GeoId, selPoints[0].PosId, selPoints[1].GeoId, selPoints[1].PosId); + } + if (availableConstraint == AvailableConstraint::THIRD) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add 'Vertical' constraints")); + createVerticalConstrain(selPoints[0].GeoId, selPoints[0].PosId, selPoints[1].GeoId, selPoints[1].PosId); + availableConstraint = AvailableConstraint::RESET; + } + } + void makeCts_1Point1Line(bool& selAllowed, Base::Vector2d onSketchPos) + { + //distance, Symmetry + if (availableConstraint == AvailableConstraint::FIRST) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add point to line Distance constraint")); + createDistanceConstrain(selPoints[0].GeoId, selPoints[0].PosId, selLine[0].GeoId, selLine[0].PosId, onSketchPos); // line to be on second parameter + selAllowed = true; + } + if (availableConstraint == AvailableConstraint::SECOND) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add Symmetry constraint")); + createSymmetryConstrain(selLine[0].GeoId, Sketcher::PointPos::start, selLine[0].GeoId, Sketcher::PointPos::end, selPoints[0].GeoId, selPoints[0].PosId); + availableConstraint = AvailableConstraint::RESET; + } + } + void makeCts_3Point(bool& selAllowed, size_t s_pts) + { + //Horizontal, vertical, symmetry + if (availableConstraint == AvailableConstraint::FIRST) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add 'Horizontal' constraints")); + for (int i = 0; i < s_pts - 1; i++) { + createHorizontalConstrain(selPoints[i].GeoId, selPoints[i].PosId, selPoints[i + 1].GeoId, selPoints[i + 1].PosId); + } + selAllowed = true; + } + if (availableConstraint == AvailableConstraint::SECOND) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add 'Vertical' constraints")); + for (int i = 0; i < s_pts - 1; i++) { + createVerticalConstrain(selPoints[i].GeoId, selPoints[i].PosId, selPoints[i + 1].GeoId, selPoints[i + 1].PosId); + } + } + if (availableConstraint == AvailableConstraint::THIRD) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add Symmetry constraints")); + createSymmetryConstrain(selPoints[0].GeoId, selPoints[0].PosId, selPoints[1].GeoId, selPoints[1].PosId, selPoints[2].GeoId, selPoints[2].PosId); + availableConstraint = AvailableConstraint::RESET; + } + } + void makeCts_4MorePoint(bool& selAllowed, size_t s_pts) + { + //Horizontal, vertical + if (availableConstraint == AvailableConstraint::FIRST) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add 'Horizontal' constraints")); + for (int i = 0; i < s_pts - 1; i++) { + createHorizontalConstrain(selPoints[i].GeoId, selPoints[i].PosId, selPoints[i + 1].GeoId, selPoints[i + 1].PosId); + } + selAllowed = true; + } + if (availableConstraint == AvailableConstraint::SECOND) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add 'Vertical' constraints")); + for (int i = 0; i < s_pts - 1; i++) { + createVerticalConstrain(selPoints[i].GeoId, selPoints[i].PosId, selPoints[i + 1].GeoId, selPoints[i + 1].PosId); + } + availableConstraint = AvailableConstraint::RESET; + } + } + void makeCts_2Point1Line(bool& selAllowed, Base::Vector2d onSketchPos, size_t s_pts) + { + //symmetry, distances + if (availableConstraint == AvailableConstraint::FIRST) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add Symmetry constraint")); + createSymmetryConstrain(selPoints[0].GeoId, selPoints[0].PosId, selPoints[1].GeoId, selPoints[1].PosId, selLine[0].GeoId, selLine[0].PosId); + selAllowed = true; + } + if (availableConstraint == AvailableConstraint::SECOND) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add Distance constraints")); + for (int i = 0; i < s_pts; i++) { + createDistanceConstrain(selPoints[i].GeoId, selPoints[i].PosId, selLine[0].GeoId, selLine[0].PosId, onSketchPos); + } + availableConstraint = AvailableConstraint::RESET; + } + } + void makeCts_3MorePoint1Line(bool& selAllowed, Base::Vector2d onSketchPos, size_t s_pts) + { + //distances + if (availableConstraint == AvailableConstraint::FIRST) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add Distance constraints")); + for (int i = 0; i < s_pts; i++) { + createDistanceConstrain(selPoints[i].GeoId, selPoints[i].PosId, selLine[0].GeoId, selLine[0].PosId, onSketchPos); + } + selAllowed = true; + availableConstraint = AvailableConstraint::RESET; + } + } + void makeCts_1MorePoint1Circle(bool& selAllowed) + { + //distance between 1 point and circle/arc not supported yet. + if (availableConstraint == AvailableConstraint::FIRST) { + //nothing yet + //availableConstraint = AvailableConstraint::RESET; + } + } + void makeCts_1MorePoint1Ellipse(bool& selAllowed) + { + //distance between 1 point and elipse/arc of... not supported yet. + if (availableConstraint == AvailableConstraint::FIRST) { + //nothing yet + //availableConstraint = AvailableConstraint::RESET; + } + } + + void makeCts_1Line(bool& selAllowed, Base::Vector2d onSketchPos) + { + //axis can be selected but we don't want distance on axis! + if ((selLine[0].GeoId != Sketcher::GeoEnum::VAxis && selLine[0].GeoId != Sketcher::GeoEnum::HAxis)) { + //distance, horizontal, vertical, block + if (availableConstraint == AvailableConstraint::FIRST) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add length constraint")); + createDistanceConstrain(selLine[0].GeoId, Sketcher::PointPos::start, selLine[0].GeoId, Sketcher::PointPos::end, onSketchPos); + selAllowed = true; + } + if (availableConstraint == AvailableConstraint::SECOND) { + if (isHorizontalVerticalBlock(selLine[0].GeoId)) { + //if the line has a vertical horizontal or block constraint then we don't switch to other modes as they are horizontal, vertical and block. + availableConstraint = AvailableConstraint::RESET; + } + else { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add Horizontal constraint")); + createHorizontalConstrain(selLine[0].GeoId, Sketcher::PointPos::none, GeoEnum::GeoUndef, Sketcher::PointPos::none); + } + } + if (availableConstraint == AvailableConstraint::THIRD) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add Vertical constraint")); + createVerticalConstrain(selLine[0].GeoId, Sketcher::PointPos::none, GeoEnum::GeoUndef, Sketcher::PointPos::none); + } + if (availableConstraint == AvailableConstraint::FOURTH) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add Block constraint")); + createBlockConstrain(selLine[0].GeoId); + availableConstraint = AvailableConstraint::RESET; + } + } + else { + //But axis can still be selected + selAllowed = true; + } + } + void makeCts_2Line(bool& selAllowed, Base::Vector2d onSketchPos) + { + //angle (if parallel: Distance (see in createAngleConstrain)), equal. + if (availableConstraint == AvailableConstraint::FIRST) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add Angle constraint")); + createAngleConstrain(selLine[0].GeoId, selLine[1].GeoId, onSketchPos); + selAllowed = true; + } + if (availableConstraint == AvailableConstraint::SECOND) { + if (selLine[0].GeoId == Sketcher::GeoEnum::VAxis || selLine[1].GeoId == Sketcher::GeoEnum::VAxis + || selLine[0].GeoId == Sketcher::GeoEnum::HAxis || selLine[1].GeoId == Sketcher::GeoEnum::HAxis) { + //if one line is axis, then can't equal.. + } + else { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add Equality constraint")); + createEqualityConstrain(selLine[0].GeoId, selLine[1].GeoId); + } + availableConstraint = AvailableConstraint::RESET; + } + + } + void makeCts_3MoreLine(bool& selAllowed, size_t s_lns) + { + //equality. + if (availableConstraint == AvailableConstraint::FIRST) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add Equality constraints")); + for (int i = 0; i < s_lns - 1; i++) { + createEqualityConstrain(selLine[i].GeoId, selLine[i + 1].GeoId); + } + selAllowed = true; + availableConstraint = AvailableConstraint::RESET; + } + } + void makeCts_1Line1Circle(bool& selAllowed, Base::Vector2d onSketchPos) + { + //Distance. + if (availableConstraint == AvailableConstraint::FIRST) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add length constraint")); + createDistanceConstrain(selCircleArc[0].GeoId, selCircleArc[0].PosId, selLine[0].GeoId, selLine[0].PosId, onSketchPos); // line to be on second parameter + selAllowed = true; + availableConstraint = AvailableConstraint::RESET; + } + } + void makeCts_1Line2Circle(bool& selAllowed) + { + //symmetry. + if (availableConstraint == AvailableConstraint::FIRST) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add Symmetry constraints")); + createSymmetryConstrain(selCircleArc[0].GeoId, Sketcher::PointPos::mid, selCircleArc[1].GeoId, Sketcher::PointPos::mid, selLine[0].GeoId, selLine[0].PosId); + selAllowed = true; + availableConstraint = AvailableConstraint::RESET; + } + } + void makeCts_1Line1Ellipse(bool& selAllowed) + { + //TODO distance between line and ellipse/arc of... not supported yet. + if (availableConstraint == AvailableConstraint::FIRST) { + //selAllowed = true; + //availableConstraint = AvailableConstraint::RESET; + } + } + + void makeCts_1Circle(bool& selAllowed, Base::Vector2d onSketchPos) + { + //Radius/diameter. Mode changes in createRadiusDiameterConstrain. + restartCommand(QT_TRANSLATE_NOOP("Command", "Add Radius constraint")); + createRadiusDiameterConstrain(selCircleArc[0].GeoId, onSketchPos); + selAllowed = true; + } + void makeCts_2Circle(bool& selAllowed, Base::Vector2d onSketchPos) + { + //Distance, radial distance, equality + if (availableConstraint == AvailableConstraint::FIRST) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add length constraint")); + createDistanceConstrain(selCircleArc[0].GeoId, selCircleArc[0].PosId, selCircleArc[1].GeoId, selCircleArc[1].PosId, onSketchPos); + selAllowed = true; + } + if (availableConstraint == AvailableConstraint::SECOND) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add concentric and length constraint")); + bool created = createCoincidenceConstrain(selCircleArc[0].GeoId, Sketcher::PointPos::mid, selCircleArc[1].GeoId, Sketcher::PointPos::mid); + if (!created) { //Already concentric, so skip to next + availableConstraint = AvailableConstraint::THIRD; + } + else { + createDistanceConstrain(selCircleArc[0].GeoId, selCircleArc[0].PosId, selCircleArc[1].GeoId, selCircleArc[1].PosId, onSketchPos); + } + } + if (availableConstraint == AvailableConstraint::THIRD) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add Equality constraint")); + createEqualityConstrain(selCircleArc[0].GeoId, selCircleArc[1].GeoId); + availableConstraint = AvailableConstraint::RESET; + } + + } + void makeCts_3MoreCircle(bool& selAllowed, size_t s_cir) + { + //equality. + if (availableConstraint == AvailableConstraint::FIRST) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add Equality constraint")); + for (int i = 0; i < s_cir - 1; i++) { + createEqualityConstrain(selCircleArc[i].GeoId, selCircleArc[i + 1].GeoId); + } + selAllowed = true; + availableConstraint = AvailableConstraint::RESET; + } + } + void makeCts_1Circle1Ellipse(bool& selAllowed) + { + //TODO distance between circle and ellipse/arc of... not supported yet. + if (availableConstraint == AvailableConstraint::FIRST) { + //selAllowed = true; + //availableConstraint = AvailableConstraint::RESET; + } + } + + void makeCts_1Ellipse(bool& selAllowed) + { + //One ellipse or arc of ellipse/hyperbola/parabola - no constrain to attribute + selAllowed = true; + } + void makeCts_2MoreEllipse(bool& selAllowed, size_t s_ell) + { + //only ellipse or arc of of same kind, then equality of all radius. + bool allTheSame = 1; + const Part::Geometry* geom = Obj->getGeometry(selEllipseAndCo[0].GeoId); + Base::Type typeOf = geom->getTypeId(); + for (int i = 1; i < s_ell; i++) { + const Part::Geometry* geomi = Obj->getGeometry(selEllipseAndCo[i].GeoId); + if (typeOf != geomi->getTypeId()) { + allTheSame = 0; + } + } + if (allTheSame) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add Equality constraint")); + for (int i = 1; i < s_ell; i++) { + createEqualityConstrain(selEllipseAndCo[0].GeoId, selEllipseAndCo[i].GeoId); + } + selAllowed = true; + } + } + + void createDistanceConstrain(int GeoId1, Sketcher::PointPos PosId1, int GeoId2, Sketcher::PointPos PosId2, Base::Vector2d onSketchPos) { + //We make sure that if there's a line, it is GeoId2. + if (GeoId1 == GeoId2 || (PosId1 != Sketcher::PointPos::none && PosId2 != Sketcher::PointPos::none)) { + specialConstraint = SpecialConstraint::LineOr2PointsDistance; + } + + bool arebothpointsorsegmentsfixed = isPointOrSegmentFixed(Obj, GeoId1) && isPointOrSegmentFixed(Obj, GeoId2); + + if (PosId1 != Sketcher::PointPos::none && PosId2 == Sketcher::PointPos::none) { // Point-line case (and point-circle in the future) + Base::Vector3d pnt = Obj->getPoint(GeoId1, PosId1); + const Part::Geometry* geom = Obj->getGeometry(GeoId2); + if (geom->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { + const Part::GeomLineSegment* lineSeg = static_cast(geom); + Base::Vector3d pnt1 = lineSeg->getStartPoint(); + Base::Vector3d pnt2 = lineSeg->getEndPoint(); + Base::Vector3d d = pnt2 - pnt1; + double ActDist = std::abs(-pnt.x * d.y + pnt.y * d.x + pnt1.x * pnt2.y - pnt2.x * pnt1.y) / d.Length(); + + Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('Distance',%d,%d,%d,%f)) ", + GeoId1, static_cast(PosId1), GeoId2, ActDist); + } + //else if (geom->getTypeId() == Part::GeomCircle::getClassTypeId()) { + // const Part::GeomCircle* circle = static_cast(geom); + //} + } + else if (PosId1 == Sketcher::PointPos::none && PosId2 == Sketcher::PointPos::none) { // Circle - line, circle - circle cases + const Part::Geometry* geo1 = Obj->getGeometry(GeoId1); + const Part::Geometry* geo2 = Obj->getGeometry(GeoId2); + + if (geo1->getTypeId() == Part::GeomCircle::getClassTypeId() + && geo2->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { // Circle - line case + auto circleSeg = static_cast(geo1); + double radius = circleSeg->getRadius(); + Base::Vector3d center = circleSeg->getCenter(); + + auto lineSeg = static_cast(geo2); + Base::Vector3d pnt1 = lineSeg->getStartPoint(); + Base::Vector3d pnt2 = lineSeg->getEndPoint(); + Base::Vector3d d = pnt2 - pnt1; + double ActDist = + std::abs(-center.x * d.y + center.y * d.x + pnt1.x * pnt2.y - pnt2.x * pnt1.y) + / d.Length() + - radius; + + Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('Distance',%d,%d,%f))", + GeoId1, GeoId2, ActDist); + } + else if (geo1->getTypeId() == Part::GeomCircle::getClassTypeId() + && geo2->getTypeId() == Part::GeomCircle::getClassTypeId()) { // Circle - circle case + auto circleSeg1 = static_cast(geo1); + double radius1 = circleSeg1->getRadius(); + Base::Vector3d center1 = circleSeg1->getCenter(); + + auto circleSeg2 = static_cast(geo2); + double radius2 = circleSeg2->getRadius(); + Base::Vector3d center2 = circleSeg2->getCenter(); + + double ActDist = 0.; + + Base::Vector3d intercenter = center1 - center2; + double intercenterdistance = intercenter.Length(); + + if (intercenterdistance >= radius1 && intercenterdistance >= radius2) { + + ActDist = intercenterdistance - radius1 - radius2; + } + else { + double bigradius = std::max(radius1, radius2); + double smallradius = std::min(radius1, radius2); + + ActDist = bigradius - smallradius - intercenterdistance; + } + + Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('Distance',%d,%d,%f))", + GeoId1, GeoId2, ActDist); + } + } + else { //both points + Base::Vector3d pnt1 = Obj->getPoint(GeoId1, PosId1); + Base::Vector3d pnt2 = Obj->getPoint(GeoId2, PosId2); + + Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('Distance',%d,%d,%d,%d,%f)) ", + GeoId1, static_cast(PosId1), GeoId2, static_cast(PosId2), (pnt2 - pnt1).Length()); + } + + const std::vector& ConStr = Obj->Constraints.getValues(); + if (arebothpointsorsegmentsfixed + || GeoId1 <= Sketcher::GeoEnum::RefExt + || constraintCreationMode == Reference) { + // it is a constraint on a external line, make it non-driving + Gui::cmdAppObjectArgs(Obj, "setDriving(%i,%s)", ConStr.size() - 1, "False"); + } + + numberOfConstraintsCreated++; + moveConstraint(ConStr.size() - 1, onSketchPos); + } + + void createDistanceXYConstrain(Sketcher::ConstraintType type, int GeoId1, Sketcher::PointPos PosId1, int GeoId2, Sketcher::PointPos PosId2, Base::Vector2d onSketchPos) { + Base::Vector3d pnt1 = Obj->getPoint(GeoId1, PosId1); + Base::Vector3d pnt2 = Obj->getPoint(GeoId2, PosId2); + double ActLength = pnt2.x - pnt1.x; + + if (type == Sketcher::DistanceY) { + ActLength = pnt2.y - pnt1.y; + } + + //negative sign avoidance: swap the points to make value positive + if (ActLength < -Precision::Confusion()) { + std::swap(GeoId1, GeoId2); + std::swap(PosId1, PosId2); + std::swap(pnt1, pnt2); + ActLength = -ActLength; + } + + if (type == Sketcher::DistanceY) { + Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('DistanceY',%d,%d,%d,%d,%f)) ", + GeoId1, static_cast(PosId1), GeoId2, static_cast(PosId2), ActLength); + } + else { + Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('DistanceX',%d,%d,%d,%d,%f)) ", + GeoId1, static_cast(PosId1), GeoId2, static_cast(PosId2), ActLength); + } + + const std::vector& ConStr = Obj->Constraints.getValues(); + if (areBothPointsOrSegmentsFixed(Obj, GeoId1, GeoId2) || constraintCreationMode == Reference) { + // it is a constraint on a external line, make it non-driving + Gui::cmdAppObjectArgs(Obj, "setDriving(%i,%s)", + ConStr.size() - 1, "False"); + } + + numberOfConstraintsCreated++; + moveConstraint(ConStr.size() - 1, onSketchPos); + } + + void createRadiusDiameterConstrain(int GeoId, Base::Vector2d onSketchPos) { + double radius = 0.0; + bool isCircle = true; + + const Part::Geometry* geom = Obj->getGeometry(GeoId); + if (geom && geom->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) { + const Part::GeomArcOfCircle* arc = static_cast(geom); + radius = arc->getRadius(); + isCircle = false; + } + else if (geom && geom->getTypeId() == Part::GeomCircle::getClassTypeId()) { + const Part::GeomCircle* circle = static_cast(geom); + radius = circle->getRadius(); + } + + if (isBsplinePole(geom)) + Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('Weight',%d,%f)) ", + GeoId, radius); + else { + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher/dimensioning"); + bool dimensioningDiameter = hGrp->GetBool("DimensioningDiameter", true); + bool dimensioningRadius = hGrp->GetBool("DimensioningRadius", true); + + bool firstCstr = true; + if (availableConstraint != AvailableConstraint::FIRST) { + firstCstr = false; + //This way if key is pressed again it goes back to FIRST + availableConstraint = AvailableConstraint::RESET; + } + + if ((firstCstr && dimensioningRadius && !dimensioningDiameter) || + (!firstCstr && !dimensioningRadius && dimensioningDiameter) || + (firstCstr && dimensioningRadius && dimensioningDiameter && !isCircle) || + (!firstCstr && dimensioningRadius && dimensioningDiameter && isCircle) ) { + Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('Radius',%d,%f)) ", + GeoId, radius); + } + else { + Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('Diameter',%d,%f)) ", + GeoId, radius * 2); + } + } + + const std::vector& ConStr = Obj->Constraints.getValues(); + bool fixed = isPointOrSegmentFixed(Obj, GeoId); + if (fixed || constraintCreationMode == Reference || GeoId <= Sketcher::GeoEnum::RefExt) { + Gui::cmdAppObjectArgs(Obj, "setDriving(%i,%s)", ConStr.size() - 1, "False"); + } + + moveConstraint(ConStr.size() - 1, onSketchPos); + + numberOfConstraintsCreated ++; + } + + bool createCoincidenceConstrain(int GeoId1, Sketcher::PointPos PosId1, int GeoId2, Sketcher::PointPos PosId2) { + // check if the edge already has a Block constraint + if (areBothPointsOrSegmentsFixed(Obj, GeoId1, GeoId2)) { + return false; + } + + // check if this coincidence is already enforced (even indirectly) + bool constraintExists = Obj->arePointsCoincident(GeoId1, PosId1, GeoId2, PosId2); + if (!constraintExists && (GeoId1 != GeoId2)) { + Gui::cmdAppObjectArgs(sketchgui->getObject(), "addConstraint(Sketcher.Constraint('Coincident', %d, %d, %d, %d)) ", + GeoId1, static_cast(PosId1), GeoId2, static_cast(PosId2)); + + numberOfConstraintsCreated++; + return true; + } + return false; + } + + void createEqualityConstrain(int GeoId1, int GeoId2) { + // check if the edge already has a Block constraint + if (areBothPointsOrSegmentsFixed(Obj, GeoId1, GeoId2)) { + return; + } + + const Part::Geometry* geo1 = Obj->getGeometry(GeoId1); + const Part::Geometry* geo2 = Obj->getGeometry(GeoId2); + + if ((geo1->getTypeId() == Part::GeomLineSegment::getClassTypeId() && geo2->getTypeId() != Part::GeomLineSegment::getClassTypeId()) || + (geo1->getTypeId() == Part::GeomArcOfHyperbola::getClassTypeId() && geo2->getTypeId() != Part::GeomArcOfHyperbola::getClassTypeId()) || + (geo1->getTypeId() == Part::GeomArcOfParabola::getClassTypeId() && geo2->getTypeId() != Part::GeomArcOfParabola::getClassTypeId()) || + (isBsplinePole(geo1) && !isBsplinePole(geo2)) || + ((geo1->getTypeId() == Part::GeomCircle::getClassTypeId() || geo1->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) && + !(geo2->getTypeId() == Part::GeomCircle::getClassTypeId() || geo2->getTypeId() == Part::GeomArcOfCircle::getClassTypeId())) || + ((geo1->getTypeId() == Part::GeomEllipse::getClassTypeId() || geo1->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId()) && + !(geo2->getTypeId() == Part::GeomEllipse::getClassTypeId() || geo2->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId()))) { + + Gui::TranslatedUserWarning(Obj, + QObject::tr("Wrong selection"), + QObject::tr("Select two or more edges of similar type.")); + return; + } + + Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('Equal',%d,%d)) ", + GeoId1, GeoId2); + numberOfConstraintsCreated++; + } + + void createAngleConstrain(int GeoId1, int GeoId2, Base::Vector2d onSketchPos) { + const Part::Geometry* geom1 = Obj->getGeometry(GeoId1); + const Part::Geometry* geom2 = Obj->getGeometry(GeoId2); + if (geom1->getTypeId() == Part::GeomLineSegment::getClassTypeId() && + geom2->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { + const Part::GeomLineSegment* lineSeg1 = static_cast(geom1); + const Part::GeomLineSegment* lineSeg2 = static_cast(geom2); + + // find the two closest line ends + Sketcher::PointPos PosId1 = Sketcher::PointPos::none; + Sketcher::PointPos PosId2 = Sketcher::PointPos::none; + Base::Vector3d p1[2], p2[2]; + p1[0] = lineSeg1->getStartPoint(); + p1[1] = lineSeg1->getEndPoint(); + p2[0] = lineSeg2->getStartPoint(); + p2[1] = lineSeg2->getEndPoint(); + + // Get the intersection point in 2d of the two lines if possible + Base::Line2d line1(Base::Vector2d(p1[0].x, p1[0].y), Base::Vector2d(p1[1].x, p1[1].y)); + Base::Line2d line2(Base::Vector2d(p2[0].x, p2[0].y), Base::Vector2d(p2[1].x, p2[1].y)); + Base::Vector2d s; + if (line1.Intersect(line2, s)) { + // get the end points of the line segments that are closest to the intersection point + Base::Vector3d s3d(s.x, s.y, p1[0].z); + if (Base::DistanceP2(s3d, p1[0]) < Base::DistanceP2(s3d, p1[1])) + PosId1 = Sketcher::PointPos::start; + else + PosId1 = Sketcher::PointPos::end; + if (Base::DistanceP2(s3d, p2[0]) < Base::DistanceP2(s3d, p2[1])) + PosId2 = Sketcher::PointPos::start; + else + PosId2 = Sketcher::PointPos::end; + } + else { + // if all points are collinear + double length = DBL_MAX; + for (int i = 0; i <= 1; i++) { + for (int j = 0; j <= 1; j++) { + double tmp = Base::DistanceP2(p2[j], p1[i]); + if (tmp < length) { + length = tmp; + PosId1 = i ? Sketcher::PointPos::end : Sketcher::PointPos::start; + PosId2 = j ? Sketcher::PointPos::end : Sketcher::PointPos::start; + } + } + } + } + + Base::Vector3d dir1 = ((PosId1 == Sketcher::PointPos::start) ? 1. : -1.) * + (lineSeg1->getEndPoint() - lineSeg1->getStartPoint()); + Base::Vector3d dir2 = ((PosId2 == Sketcher::PointPos::start) ? 1. : -1.) * + (lineSeg2->getEndPoint() - lineSeg2->getStartPoint()); + + // check if the two lines are parallel, in this case an angle is not possible + Base::Vector3d dir3 = dir1 % dir2; + if (dir3.Length() < Precision::Intersection()) { + Base::Vector3d dist = (p1[0] - p2[0]) % dir1; + if (dist.Sqr() > Precision::Intersection()) { + //distance between 2 points + restartCommand(QT_TRANSLATE_NOOP("Command", "Add Distance constraint")); + if ((selLine[0].GeoId == Sketcher::GeoEnum::VAxis || selLine[0].GeoId == Sketcher::GeoEnum::HAxis)) { + createDistanceConstrain(selLine[1].GeoId, Sketcher::PointPos::start, selLine[0].GeoId, selLine[0].PosId, onSketchPos); + } + else { + createDistanceConstrain(selLine[0].GeoId, Sketcher::PointPos::start, selLine[1].GeoId, selLine[1].PosId, onSketchPos); + } + return; + } + } + + double ActAngle = atan2(dir1.x * dir2.y - dir1.y * dir2.x, + dir1.y * dir2.y + dir1.x * dir2.x); + if (ActAngle < 0) { + ActAngle *= -1; + std::swap(GeoId1, GeoId2); + std::swap(PosId1, PosId2); + } + + Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('Angle',%d,%d,%d,%d,%f)) ", + GeoId1, static_cast(PosId1), GeoId2, static_cast(PosId2), ActAngle); + + const std::vector& ConStr = Obj->Constraints.getValues(); + if (areBothPointsOrSegmentsFixed(Obj, GeoId1, GeoId2) || constraintCreationMode == Reference) { + // it is a constraint on a external line, make it non-driving + + Gui::cmdAppObjectArgs(Obj, "setDriving(%i,%s)", ConStr.size() - 1, "False"); + } + numberOfConstraintsCreated++; + moveConstraint(ConStr.size() - 1, onSketchPos); + } + } + + void createVerticalConstrain(int GeoId1, Sketcher::PointPos PosId1, int GeoId2, Sketcher::PointPos PosId2) { + if (selLine.size() == 1) { + Gui::cmdAppObjectArgs(sketchgui->getObject(), "addConstraint(Sketcher.Constraint('Vertical',%d)) ", GeoId1); + + } + else { //2points + if (areBothPointsOrSegmentsFixed(Obj, GeoId1, GeoId2)) { + return; + } + Gui::cmdAppObjectArgs(sketchgui->getObject(), "addConstraint(Sketcher.Constraint('Vertical',%d,%d,%d,%d)) " + , GeoId1, static_cast(PosId1), GeoId2, static_cast(PosId2)); + } + numberOfConstraintsCreated++; + tryAutoRecompute(Obj); + } + void createHorizontalConstrain(int GeoId1, Sketcher::PointPos PosId1, int GeoId2, Sketcher::PointPos PosId2) { + if (selLine.size() == 1) { + Gui::cmdAppObjectArgs(sketchgui->getObject(), "addConstraint(Sketcher.Constraint('Horizontal',%d)) ", GeoId1); + } + else { //2points + if (areBothPointsOrSegmentsFixed(Obj, GeoId1, GeoId2)) { + return; + } + Gui::cmdAppObjectArgs(sketchgui->getObject(), "addConstraint(Sketcher.Constraint('Horizontal',%d,%d,%d,%d)) " + , GeoId1, static_cast(PosId1), GeoId2, static_cast(PosId2)); + } + numberOfConstraintsCreated++; + tryAutoRecompute(Obj); + } + void createBlockConstrain(int GeoId) { + Gui::cmdAppObjectArgs(sketchgui->getObject(), "addConstraint(Sketcher.Constraint('Block',%d)) ", GeoId); + + numberOfConstraintsCreated++; + tryAutoRecompute(Obj); + } + bool isHorizontalVerticalBlock(int GeoId) { + const std::vector< Sketcher::Constraint* >& vals = Obj->Constraints.getValues(); + + // check if the edge already has a Horizontal/Vertical/Block constraint + for (const auto& constraint : vals) { + if ((constraint->Type == Sketcher::Horizontal || constraint->Type == Sketcher::Vertical || constraint->Type == Sketcher::Block) + && constraint->First == GeoId) { + return true; + } + } + return false; + } + + void createSymmetryConstrain(int GeoId1, Sketcher::PointPos PosId1, int GeoId2, Sketcher::PointPos PosId2, int GeoId3, Sketcher::PointPos PosId3) { + if (selPoints.size() == 2 && selLine.size() == 1) { + if (isEdge(GeoId1, PosId1) && isVertex(GeoId3, PosId3)) { + std::swap(GeoId1, GeoId3); + std::swap(PosId1, PosId3); + } + else if (isEdge(GeoId2, PosId2) && isVertex(GeoId3, PosId3)) { + std::swap(GeoId2, GeoId3); + std::swap(PosId2, PosId3); + } + + if (areAllPointsOrSegmentsFixed(Obj, GeoId1, GeoId2, GeoId3)) { + return; + } + + const Part::Geometry* geom = Obj->getGeometry(GeoId3); + if (geom->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { + if (GeoId1 == GeoId2 && GeoId2 == GeoId3) { + Gui::TranslatedUserWarning(Obj, + QObject::tr("Wrong selection"), + QObject::tr("Cannot add a symmetry constraint between a line and its end points!")); + return; + } + + Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('Symmetric',%d,%d,%d,%d,%d)) ", + GeoId1, static_cast(PosId1), GeoId2, static_cast(PosId2), GeoId3); + + numberOfConstraintsCreated++; + tryAutoRecompute(Obj); + } + } + else { + if (selPoints.size() == 1 && selLine.size() == 1) { //1line 1 point + if (GeoId1 == GeoId3) { + Gui::TranslatedUserWarning(Obj, + QObject::tr("Wrong selection"), + QObject::tr("Cannot add a symmetry constraint between a line and its end points!")); + return; + } + if (areBothPointsOrSegmentsFixed(Obj, GeoId1, GeoId2)) { + return; + } + } + else { + if (areAllPointsOrSegmentsFixed(Obj, GeoId1, GeoId2, GeoId3)) { + return; + } + } + Gui::cmdAppObjectArgs(Obj, "addConstraint(Sketcher.Constraint('Symmetric',%d,%d,%d,%d,%d,%d)) ", + GeoId1, static_cast(PosId1), GeoId2, static_cast(PosId2), GeoId3, static_cast(PosId3)); + + numberOfConstraintsCreated++; + tryAutoRecompute(Obj); + } + } + + void updateDistanceType(Base::Vector2d onSketchPos) + { + const std::vector< Sketcher::Constraint* >& vals = Obj->Constraints.getValues(); + Sketcher::ConstraintType type = vals[vals.size() - 1]->Type; + + Base::Vector3d pnt1, pnt2; + bool addedOrigin = false; + if (selPoints.size() == 1) { + //Case of single point selected, for distance constraint. We add temporarily the origin in the vector. + addedOrigin = true; + SelIdPair selIdPair; + selIdPair.GeoId = Sketcher::GeoEnum::RtPnt; + selIdPair.PosId = Sketcher::PointPos::start; + selPoints.push_back(selIdPair); + } + + if (selLine.size() == 1) { + pnt1 = Obj->getPoint(selLine[0].GeoId, Sketcher::PointPos::start); + pnt2 = Obj->getPoint(selLine[0].GeoId, Sketcher::PointPos::end); + } + else { + pnt1 = Obj->getPoint(selPoints[0].GeoId, selPoints[0].PosId); + pnt2 = Obj->getPoint(selPoints[1].GeoId, selPoints[1].PosId); + } + + 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 (onSketchPos.x > minX && onSketchPos.x < maxX + && (onSketchPos.y < minY || onSketchPos.y > maxY) && type != Sketcher::DistanceX) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add DistanceX constraint")); + specialConstraint = SpecialConstraint::LineOr2PointsDistance; + if (selLine.size() == 1) { + createDistanceXYConstrain(Sketcher::DistanceX, selLine[0].GeoId, Sketcher::PointPos::start, selLine[0].GeoId, Sketcher::PointPos::end, onSketchPos); + } + else { + createDistanceXYConstrain(Sketcher::DistanceX, selPoints[0].GeoId, selPoints[0].PosId, selPoints[1].GeoId, selPoints[1].PosId, onSketchPos); + } + } + else if (onSketchPos.y > minY && onSketchPos.y < maxY + && (onSketchPos.x < minX || onSketchPos.x > maxX) && type != Sketcher::DistanceY) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add DistanceY constraint")); + specialConstraint = SpecialConstraint::LineOr2PointsDistance; + if (selLine.size() == 1) { + createDistanceXYConstrain(Sketcher::DistanceY, selLine[0].GeoId, Sketcher::PointPos::start, selLine[0].GeoId, Sketcher::PointPos::end, onSketchPos); + } + else { + createDistanceXYConstrain(Sketcher::DistanceY, selPoints[0].GeoId, selPoints[0].PosId, selPoints[1].GeoId, selPoints[1].PosId, onSketchPos); + } + } + else if ((((onSketchPos.y < minY || onSketchPos.y > maxY) && (onSketchPos.x < minX || onSketchPos.x > maxX)) + || (onSketchPos.y > minY && onSketchPos.y < maxY && onSketchPos.x > minX && onSketchPos.x < maxX)) && type != Sketcher::Distance) { + restartCommand(QT_TRANSLATE_NOOP("Command", "Add Distance constraint")); + if (selLine.size() == 1) { + createDistanceConstrain(selLine[0].GeoId, Sketcher::PointPos::start, selLine[0].GeoId, Sketcher::PointPos::end, onSketchPos); + } + else { + createDistanceConstrain(selPoints[0].GeoId, selPoints[0].PosId, selPoints[1].GeoId, selPoints[1].PosId, onSketchPos); + } + + } + + if (addedOrigin) { + //remove origin + selPoints.pop_back(); + } + } + + void restartCommand(const char* cstrName) { + specialConstraint = SpecialConstraint::None; + Gui::Command::abortCommand(); + Obj->solve(); + sketchgui->draw(false, false); // Redraw + Gui::Command::openCommand(cstrName); + + numberOfConstraintsCreated = 0; + } +}; + +DEF_STD_CMD_AU(CmdSketcherDimension) + +CmdSketcherDimension::CmdSketcherDimension() + : Command("Sketcher_Dimension") +{ + sAppModule = "Sketcher"; + sGroup = "Sketcher"; + sMenuText = QT_TR_NOOP("Dimension"); + sToolTipText = QT_TR_NOOP("Constrain contextually based on your selection.\n" + "Depending on your selection you might have several constraints available. You can cycle through them using SHIFT key.\n" + "Left clicking on empty space will validate the current constraint. Right clicking or pressing Esc will cancel."); + sWhatsThis = "Sketcher_Dimension"; + sStatusTip = sToolTipText; + sPixmap = "Constraint_Dimension"; + sAccel = "D"; + eType = ForEdit; +} + +void CmdSketcherDimension::activated(int iMsg) +{ + Q_UNUSED(iMsg); + ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerDimension()); + getSelection().clearSelection(); +} + +void CmdSketcherDimension::updateAction(int mode) +{ + switch (mode) { + case Reference: + if (getAction()) + getAction()->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_Dimension_Driven")); + break; + case Driving: + if (getAction()) + getAction()->setIcon(Gui::BitmapFactory().iconFromTheme("Constraint_Dimension")); + break; + } +} + +bool CmdSketcherDimension::isActive(void) +{ + return isCommandActive(getActiveGuiDocument()); +} + + // ============================================================================ class CmdSketcherConstrainHorizontal: public CmdSketcherConstraint @@ -8371,6 +9620,7 @@ CmdSketcherToggleDrivingConstraint::CmdSketcherToggleDrivingConstraint() rcCmdMgr.addCommandMode("ToggleDrivingConstraint", "Sketcher_ConstrainRadiam"); rcCmdMgr.addCommandMode("ToggleDrivingConstraint", "Sketcher_ConstrainAngle"); rcCmdMgr.addCommandMode("ToggleDrivingConstraint", "Sketcher_CompConstrainRadDia"); + rcCmdMgr.addCommandMode("ToggleDrivingConstraint", "Sketcher_Dimension"); // rcCmdMgr.addCommandMode("ToggleDrivingConstraint", "Sketcher_ConstrainSnellsLaw"); } @@ -8581,6 +9831,7 @@ void CreateSketcherCommandsConstraints() rcCmdMgr.addCommand(new CmdSketcherConstrainLock()); rcCmdMgr.addCommand(new CmdSketcherConstrainBlock()); rcCmdMgr.addCommand(new CmdSketcherConstrainCoincident()); + rcCmdMgr.addCommand(new CmdSketcherDimension()); rcCmdMgr.addCommand(new CmdSketcherConstrainParallel()); rcCmdMgr.addCommand(new CmdSketcherConstrainPerpendicular()); rcCmdMgr.addCommand(new CmdSketcherConstrainTangent()); diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandler.cpp b/src/Mod/Sketcher/Gui/DrawSketchHandler.cpp index bcd2fd8c62..f3eccc92fc 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandler.cpp +++ b/src/Mod/Sketcher/Gui/DrawSketchHandler.cpp @@ -138,6 +138,13 @@ ViewProviderSketchDrawSketchHandlerAttorney::setAngleSnapping(ViewProviderSketch vp.setAngleSnapping(enable, referencePoint); } +inline void +ViewProviderSketchDrawSketchHandlerAttorney::moveConstraint(ViewProviderSketch& vp, int constNum, + const Base::Vector2d& toPos) +{ + vp.moveConstraint(constNum, toPos); +} + /**************************** CurveConverter **********************************************/ @@ -1086,3 +1093,9 @@ void DrawSketchHandler::setAngleSnapping(bool enable, Base::Vector2d referencePo ViewProviderSketchDrawSketchHandlerAttorney::setAngleSnapping( *sketchgui, enable, referencePoint); } + +void DrawSketchHandler::moveConstraint(int constNum, const Base::Vector2d& toPos) +{ + ViewProviderSketchDrawSketchHandlerAttorney::moveConstraint( + *sketchgui, constNum, toPos); +} \ No newline at end of file diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandler.h b/src/Mod/Sketcher/Gui/DrawSketchHandler.h index d5350ebcfb..a5679b5e46 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandler.h +++ b/src/Mod/Sketcher/Gui/DrawSketchHandler.h @@ -104,6 +104,7 @@ private: static inline int getPreselectCurve(const ViewProviderSketch& vp); static inline int getPreselectCross(const ViewProviderSketch& vp); + static inline void moveConstraint(ViewProviderSketch& vp, int constNum, const Base::Vector2d& toPos); friend class DrawSketchHandler; }; @@ -230,6 +231,8 @@ protected: void setAngleSnapping(bool enable, Base::Vector2d referencePoint = Base::Vector2d(0., 0.)); + void moveConstraint(int constNum, const Base::Vector2d& toPos); + private: void setSvgCursor(const QString& svgName, int x, int y, const std::map& colorMapping = diff --git a/src/Mod/Sketcher/Gui/EditDatumDialog.cpp b/src/Mod/Sketcher/Gui/EditDatumDialog.cpp index dbfd423b99..2bced92a6d 100644 --- a/src/Mod/Sketcher/Gui/EditDatumDialog.cpp +++ b/src/Mod/Sketcher/Gui/EditDatumDialog.cpp @@ -53,6 +53,7 @@ using namespace SketcherGui; EditDatumDialog::EditDatumDialog(ViewProviderSketch* vp, int ConstrNbr) : ConstrNbr(ConstrNbr) + , success(false) { sketch = vp->getSketchObject(); const std::vector& Constraints = sketch->Constraints.getValues(); @@ -70,7 +71,7 @@ EditDatumDialog::EditDatumDialog(Sketcher::SketchObject* pcSketch, int ConstrNbr EditDatumDialog::~EditDatumDialog() {} -void EditDatumDialog::exec(bool atCursor) +int EditDatumDialog::exec(bool atCursor) { // Return if constraint doesn't have editable value if (Constr->isDimensional()) { @@ -80,7 +81,7 @@ void EditDatumDialog::exec(bool atCursor) QObject::tr("Dimensional constraint"), QObject::tr("Not allowed to edit the datum because the " "sketch contains conflicting constraints")); - return; + return QDialog::Rejected; } Base::Quantity init_val; @@ -173,8 +174,10 @@ void EditDatumDialog::exec(bool atCursor) dlg.setGeometry(x, y, dlg.geometry().width(), dlg.geometry().height()); } - dlg.exec(); + return dlg.exec(); } + + return QDialog::Rejected; } void EditDatumDialog::accepted() @@ -233,6 +236,7 @@ void EditDatumDialog::accepted() //} tryAutoRecompute(sketch); + success = true; } catch (const Base::Exception& e) { Gui::NotifyUserError( @@ -253,6 +257,11 @@ void EditDatumDialog::rejected() sketch->recomputeFeature(); } +bool EditDatumDialog::isSuccess() +{ + return success; +} + void EditDatumDialog::drivingToggled(bool state) { if (state) { diff --git a/src/Mod/Sketcher/Gui/EditDatumDialog.h b/src/Mod/Sketcher/Gui/EditDatumDialog.h index 85d4695f60..2252a28472 100644 --- a/src/Mod/Sketcher/Gui/EditDatumDialog.h +++ b/src/Mod/Sketcher/Gui/EditDatumDialog.h @@ -47,12 +47,14 @@ public: EditDatumDialog(Sketcher::SketchObject* pcSketch, int ConstrNbr); ~EditDatumDialog() override; - void exec(bool atCursor = true); + int exec(bool atCursor = true); + bool isSuccess(); private: Sketcher::SketchObject* sketch; Sketcher::Constraint* Constr; int ConstrNbr; + bool success; std::unique_ptr ui_ins_datum; private Q_SLOTS: diff --git a/src/Mod/Sketcher/Gui/Resources/Sketcher.qrc b/src/Mod/Sketcher/Gui/Resources/Sketcher.qrc index 52f4638e45..6ee58b00c1 100644 --- a/src/Mod/Sketcher/Gui/Resources/Sketcher.qrc +++ b/src/Mod/Sketcher/Gui/Resources/Sketcher.qrc @@ -7,6 +7,8 @@ icons/constraints/Constraint_Block.svg icons/constraints/Constraint_Concentric.svg + icons/constraints/Constraint_Dimension.svg + icons/constraints/Constraint_Dimension_Driven.svg icons/constraints/Constraint_Diameter.svg icons/constraints/Constraint_Diameter_Driven.svg icons/constraints/Constraint_Ellipse_Axis_Angle.svg diff --git a/src/Mod/Sketcher/Gui/Resources/icons/constraints/Constraint_Dimension.svg b/src/Mod/Sketcher/Gui/Resources/icons/constraints/Constraint_Dimension.svg new file mode 100644 index 0000000000..a469fb0200 --- /dev/null +++ b/src/Mod/Sketcher/Gui/Resources/icons/constraints/Constraint_Dimension.svg @@ -0,0 +1,253 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + [wmayer] + + + 2011-10-10 + http://www.freecadweb.org/wiki/index.php?title=Artwork + + + FreeCAD + + + FreeCAD/src/Mod/Sketcher/Gui/Resources/icons/Constraint_Radius.svg + + + FreeCAD LGPL2+ + + + https://www.gnu.org/copyleft/lesser.html + + + [agryson] Alexander Gryson + + + + + + + + + + + + + + + + + + + + diff --git a/src/Mod/Sketcher/Gui/Resources/icons/constraints/Constraint_Dimension_Driven.svg b/src/Mod/Sketcher/Gui/Resources/icons/constraints/Constraint_Dimension_Driven.svg new file mode 100644 index 0000000000..64fb54dbec --- /dev/null +++ b/src/Mod/Sketcher/Gui/Resources/icons/constraints/Constraint_Dimension_Driven.svg @@ -0,0 +1,276 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + [wmayer] + + + 2011-10-10 + http://www.freecadweb.org/wiki/index.php?title=Artwork + + + FreeCAD + + + FreeCAD/src/Mod/Sketcher/Gui/Resources/icons/Constraint_Radius.svg + + + FreeCAD LGPL2+ + + + https://www.gnu.org/copyleft/lesser.html + + + [agryson] Alexander Gryson + + + + + + + + + + + + + + + + + + diff --git a/src/Mod/Sketcher/Gui/SketcherSettings.cpp b/src/Mod/Sketcher/Gui/SketcherSettings.cpp index 0381280338..17b4deb15f 100644 --- a/src/Mod/Sketcher/Gui/SketcherSettings.cpp +++ b/src/Mod/Sketcher/Gui/SketcherSettings.cpp @@ -66,6 +66,54 @@ void SketcherSettings::saveSettings() ui->checkBoxEnableEscape->onSave(); ui->checkBoxNotifyConstraintSubstitutions->onSave(); ui->checkBoxAutoRemoveRedundants->onSave(); + + enum { + DimensionSingleTool, + DimensionSeparateTools, + DimensionBoth + }; + + // Dimensioning constraints mode + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher/dimensioning"); + bool singleTool = true; + bool SeparatedTools = false; + int index = ui->dimensioningMode->currentIndex(); + switch (index) { + case DimensionSeparateTools: + singleTool = false; + SeparatedTools = true; + break; + case DimensionBoth: + singleTool = true; + SeparatedTools = true; + break; + } + hGrp->SetBool("SingleDimensioningTool", singleTool); + hGrp->SetBool("SeparatedDimensioningTools", SeparatedTools); + + ui->radiusDiameterMode->setEnabled(index != 1); + + enum { + DimensionAutoRadiusDiam, + DimensionDiameter, + DimensionRadius + }; + + bool Diameter = true; + bool Radius = true; + index = ui->radiusDiameterMode->currentIndex(); + switch (index) { + case DimensionDiameter: + Diameter = true; + Radius = false; + break; + case DimensionRadius: + Diameter = false; + Radius = true; + break; + } + hGrp->SetBool("DimensioningDiameter", Diameter); + hGrp->SetBool("DimensioningRadius", Radius); } void SketcherSettings::loadSettings() @@ -76,6 +124,38 @@ void SketcherSettings::loadSettings() ui->checkBoxEnableEscape->onRestore(); ui->checkBoxNotifyConstraintSubstitutions->onRestore(); ui->checkBoxAutoRemoveRedundants->onRestore(); + + // Dimensioning constraints mode + ui->dimensioningMode->clear(); + ui->dimensioningMode->addItem(tr("Single tool")); + ui->dimensioningMode->addItem(tr("Separated tools")); + ui->dimensioningMode->addItem(tr("Both")); + + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher/dimensioning"); + bool singleTool = hGrp->GetBool("SingleDimensioningTool", true); + bool SeparatedTools = hGrp->GetBool("SeparatedDimensioningTools", false); + int index = SeparatedTools ? (singleTool ? 2 : 1) : 0; + ui->dimensioningMode->setCurrentIndex(index); + connect(ui->dimensioningMode, QOverload::of(&QComboBox::currentIndexChanged), this, &SketcherSettings::dimensioningModeChanged); + + ui->radiusDiameterMode->setEnabled(index != 1); + + // Dimensioning constraints mode + ui->radiusDiameterMode->clear(); + ui->radiusDiameterMode->addItem(tr("Auto")); + ui->radiusDiameterMode->addItem(tr("Diameter")); + ui->radiusDiameterMode->addItem(tr("Radius")); + + bool Diameter = hGrp->GetBool("DimensioningDiameter", true); + bool Radius = hGrp->GetBool("DimensioningRadius", true); + index = Diameter ? (Radius ? 0 : 1) : 2; + ui->radiusDiameterMode->setCurrentIndex(index); +} + +void SketcherSettings::dimensioningModeChanged(int index) +{ + ui->radiusDiameterMode->setEnabled(index != 1); + SketcherSettings::requireRestart(); } /** diff --git a/src/Mod/Sketcher/Gui/SketcherSettings.h b/src/Mod/Sketcher/Gui/SketcherSettings.h index 3a65ca1243..01a6018f10 100644 --- a/src/Mod/Sketcher/Gui/SketcherSettings.h +++ b/src/Mod/Sketcher/Gui/SketcherSettings.h @@ -51,6 +51,7 @@ public: protected: void changeEvent(QEvent* e) override; + void dimensioningModeChanged(int index); private: std::unique_ptr ui; diff --git a/src/Mod/Sketcher/Gui/SketcherSettings.ui b/src/Mod/Sketcher/Gui/SketcherSettings.ui index 47a71f3c80..a189572028 100644 --- a/src/Mod/Sketcher/Gui/SketcherSettings.ui +++ b/src/Mod/Sketcher/Gui/SketcherSettings.ui @@ -163,6 +163,56 @@ Requires to re-enter edit mode to take effect. + + + + 0 + 0 + + + + Dimension Constraint + + + + + + Dimensioning constraints: + + + + + + + Select the type of dimensioning constraints for your toolbar: +'Single tool': A single tool for all dimensioning constraints : Distance, Distance X / Y, Angle, Radius. +'Separated tools': Separated tools for each dimensioning constraint. +'Both': You will have both the 'Dimension' tool and the separated tools. +This setting is only for the toolbar. Whichever you chose, all tools are always available in the menu and through shortcuts. + + + + + + + Dimension tool diameter/radius mode: + + + + + + + While using the Dimension tool you may chose how to handle circles and arcs: +'Auto': The tool will apply radius to arcs and diameter to circles. +'Diameter': The tool will apply diameter to both arcs and circles. +'Radius': The tool will apply radius to both arcs and circles. + + + + + + + Qt::Vertical diff --git a/src/Mod/Sketcher/Gui/TaskDlgEditSketch.cpp b/src/Mod/Sketcher/Gui/TaskDlgEditSketch.cpp index 0912c4b54b..02a530d51f 100644 --- a/src/Mod/Sketcher/Gui/TaskDlgEditSketch.cpp +++ b/src/Mod/Sketcher/Gui/TaskDlgEditSketch.cpp @@ -100,6 +100,10 @@ bool TaskDlgEditSketch::reject() hGrp->SetBool("ExpandedConstraintsWidget", Constraints->isGroupVisible()); hGrp->SetBool("ExpandedElementsWidget", Elements->isGroupVisible()); + if (sketchView && sketchView->getSketchMode() != ViewProviderSketch::STATUS_NONE) { + sketchView->purgeHandler(); + } + std::string document = getDocumentName();// needed because resetEdit() deletes this instance Gui::Command::doCommand( Gui::Command::Gui, "Gui.getDocument('%s').resetEdit()", document.c_str()); diff --git a/src/Mod/Sketcher/Gui/Workbench.cpp b/src/Mod/Sketcher/Gui/Workbench.cpp index f5c79ef95b..8ad91a07dd 100644 --- a/src/Mod/Sketcher/Gui/Workbench.cpp +++ b/src/Mod/Sketcher/Gui/Workbench.cpp @@ -408,6 +408,7 @@ inline void SketcherAddWorkbenchConstraints(Gui::MenuItem& cons) << "Sketcher_ConstrainSymmetric" << "Sketcher_ConstrainBlock" << "Separator" + << "Sketcher_Dimension" << "Sketcher_ConstrainLock" << "Sketcher_ConstrainDistanceX" << "Sketcher_ConstrainDistanceY" @@ -425,6 +426,9 @@ inline void SketcherAddWorkbenchConstraints(Gui::MenuItem& cons) template<> inline void SketcherAddWorkbenchConstraints(Gui::ToolBarItem& cons) { + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath( + "User parameter:BaseApp/Preferences/Mod/Sketcher/dimensioning"); + cons << "Sketcher_ConstrainCoincident" << "Sketcher_ConstrainPointOnObject" << "Sketcher_ConstrainVertical" @@ -435,15 +439,20 @@ inline void SketcherAddWorkbenchConstraints(Gui::ToolBarItem& << "Sketcher_ConstrainEqual" << "Sketcher_ConstrainSymmetric" << "Sketcher_ConstrainBlock" - << "Separator" - << "Sketcher_ConstrainLock" - << "Sketcher_ConstrainDistanceX" - << "Sketcher_ConstrainDistanceY" - << "Sketcher_ConstrainDistance" - << "Sketcher_CompConstrainRadDia" - << "Sketcher_ConstrainAngle" - // << "Sketcher_ConstrainSnellsLaw" // Rarely used, show only in menu - << "Separator" + << "Separator"; + if (hGrp->GetBool("SingleDimensioningTool", true)) { + cons << "Sketcher_Dimension"; + } + if (hGrp->GetBool("SeparatedDimensioningTools", false)) { + cons << "Sketcher_ConstrainLock" + << "Sketcher_ConstrainDistanceX" + << "Sketcher_ConstrainDistanceY" + << "Sketcher_ConstrainDistance" + << "Sketcher_CompConstrainRadDia" + << "Sketcher_ConstrainAngle"; + // << "Sketcher_ConstrainSnellsLaw" // Rarely used, show only in menu + } + cons << "Separator" << "Sketcher_ToggleDrivingConstraint" << "Sketcher_ToggleActiveConstraint"; }