diff --git a/src/Mod/Sketcher/Gui/CommandCreateGeo.cpp b/src/Mod/Sketcher/Gui/CommandCreateGeo.cpp index 13451ded84..f41e61c767 100644 --- a/src/Mod/Sketcher/Gui/CommandCreateGeo.cpp +++ b/src/Mod/Sketcher/Gui/CommandCreateGeo.cpp @@ -1781,7 +1781,7 @@ CONSTRUCTION_UPDATE_ACTION(CmdSketcherCreateTriangle, "Sketcher_CreateTriangle") void CmdSketcherCreateTriangle::activated(int iMsg) { Q_UNUSED(iMsg); - ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerRegularPolygon(3)); + ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerPolygon(3)); } bool CmdSketcherCreateTriangle::isActive() @@ -1810,7 +1810,7 @@ CONSTRUCTION_UPDATE_ACTION(CmdSketcherCreateSquare, "Sketcher_CreateSquare") void CmdSketcherCreateSquare::activated(int iMsg) { Q_UNUSED(iMsg); - ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerRegularPolygon(4)); + ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerPolygon(4)); } bool CmdSketcherCreateSquare::isActive() @@ -1839,7 +1839,7 @@ CONSTRUCTION_UPDATE_ACTION(CmdSketcherCreatePentagon, "Sketcher_CreatePentagon") void CmdSketcherCreatePentagon::activated(int iMsg) { Q_UNUSED(iMsg); - ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerRegularPolygon(5)); + ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerPolygon(5)); } bool CmdSketcherCreatePentagon::isActive() @@ -1869,7 +1869,7 @@ CONSTRUCTION_UPDATE_ACTION(CmdSketcherCreateHexagon, "Sketcher_CreateHexagon") void CmdSketcherCreateHexagon::activated(int iMsg) { Q_UNUSED(iMsg); - ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerRegularPolygon(6)); + ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerPolygon(6)); } bool CmdSketcherCreateHexagon::isActive() @@ -1898,7 +1898,7 @@ CONSTRUCTION_UPDATE_ACTION(CmdSketcherCreateHeptagon, "Sketcher_CreateHeptagon") void CmdSketcherCreateHeptagon::activated(int iMsg) { Q_UNUSED(iMsg); - ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerRegularPolygon(7)); + ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerPolygon(7)); } bool CmdSketcherCreateHeptagon::isActive() @@ -1927,7 +1927,7 @@ CONSTRUCTION_UPDATE_ACTION(CmdSketcherCreateOctagon, "Sketcher_CreateOctagon") void CmdSketcherCreateOctagon::activated(int iMsg) { Q_UNUSED(iMsg); - ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerRegularPolygon(8)); + ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerPolygon(8)); } bool CmdSketcherCreateOctagon::isActive() @@ -1960,7 +1960,7 @@ void CmdSketcherCreateRegularPolygon::activated(int iMsg) // Pop-up asking for values SketcherRegularPolygonDialog srpd; if (srpd.exec() == QDialog::Accepted) { - ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerRegularPolygon(srpd.sides)); + ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerPolygon(srpd.sides)); } } @@ -1988,29 +1988,28 @@ void CmdSketcherCompCreateRegularPolygon::activated(int iMsg) { switch (iMsg) { case 0: - ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerRegularPolygon(3)); + ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerPolygon(3)); break; case 1: - ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerRegularPolygon(4)); + ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerPolygon(4)); break; case 2: - ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerRegularPolygon(5)); + ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerPolygon(5)); break; case 3: - ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerRegularPolygon(6)); + ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerPolygon(6)); break; case 4: - ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerRegularPolygon(7)); + ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerPolygon(7)); break; case 5: - ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerRegularPolygon(8)); + ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerPolygon(8)); break; case 6: { // Pop-up asking for values SketcherRegularPolygonDialog srpd; if (srpd.exec() == QDialog::Accepted) { - ActivateHandler(getActiveGuiDocument(), - new DrawSketchHandlerRegularPolygon(srpd.sides)); + ActivateHandler(getActiveGuiDocument(), new DrawSketchHandlerPolygon(srpd.sides)); } } break; default: diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandlerPolygon.h b/src/Mod/Sketcher/Gui/DrawSketchHandlerPolygon.h index bf30778f0b..178dd13157 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandlerPolygon.h +++ b/src/Mod/Sketcher/Gui/DrawSketchHandlerPolygon.h @@ -20,190 +20,501 @@ * * ***************************************************************************/ + #ifndef SKETCHERGUI_DrawSketchHandlerPolygon_H #define SKETCHERGUI_DrawSketchHandlerPolygon_H -#include - #include "GeometryCreationMode.h" -#include "SketcherRegularPolygonDialog.h" +#include "Utils.h" +#include "DrawSketchDefaultWidgetController.h" +#include "DrawSketchControllableHandler.h" + +#include "SketcherRegularPolygonDialog.h" namespace SketcherGui { extern GeometryCreationMode geometryCreationMode; // defined in CommandCreateGeo.cpp -class DrawSketchHandlerRegularPolygon: public DrawSketchHandler +class DrawSketchHandlerPolygon; + +using DSHPolygonController = + DrawSketchDefaultWidgetController, + /*WidgetParametersT =*/WidgetParameters<1>, + /*WidgetCheckboxesT =*/WidgetCheckboxes<0>, + /*WidgetComboboxesT =*/WidgetComboboxes<0>>; + +using DSHPolygonControllerBase = DSHPolygonController::ControllerBase; + +using DrawSketchHandlerPolygonBase = DrawSketchControllableHandler; + + +class DrawSketchHandlerPolygon: public DrawSketchHandlerPolygonBase { + friend DSHPolygonController; + friend DSHPolygonControllerBase; + public: - explicit DrawSketchHandlerRegularPolygon(size_t nof_corners) - : Corners(nof_corners) - , AngleOfSeparation(2.0 * M_PI / static_cast(Corners)) - , cos_v(cos(AngleOfSeparation)) - , sin_v(sin(AngleOfSeparation)) - , Mode(STATUS_SEEK_First) - , EditCurve(Corners + 1) + DrawSketchHandlerPolygon(int corners = 6) + : numberOfCorners(corners) {} - ~DrawSketchHandlerRegularPolygon() override - {} - /// mode table - enum SelectMode - { - STATUS_SEEK_First, /**< enum value ----. */ - STATUS_SEEK_Second, /**< enum value ----. */ - STATUS_End - }; - - void mouseMove(Base::Vector2d onSketchPos) override - { - - if (Mode == STATUS_SEEK_First) { - setPositionText(onSketchPos); - if (seekAutoConstraint(sugConstr1, onSketchPos, Base::Vector2d(0.f, 0.f))) { - renderSuggestConstraintsCursor(sugConstr1); - return; - } - } - else if (Mode == STATUS_SEEK_Second) { - EditCurve[0] = Base::Vector2d(onSketchPos.x, onSketchPos.y); - EditCurve[Corners] = Base::Vector2d(onSketchPos.x, onSketchPos.y); - - Base::Vector2d dV = onSketchPos - StartPos; - double rx = dV.x; - double ry = dV.y; - for (int i = 1; i < static_cast(Corners); i++) { - const double old_rx = rx; - rx = cos_v * rx - sin_v * ry; - ry = cos_v * ry + sin_v * old_rx; - EditCurve[i] = Base::Vector2d(StartPos.x + rx, StartPos.y + ry); - } - - // Display radius for user - const float radius = dV.Length(); - const float angle = (180.0 / M_PI) * atan2(dV.y, dV.x); - - if (showCursorCoords()) { - SbString text; - std::string radiusString = lengthToDisplayFormat(radius, 1); - std::string angleString = angleToDisplayFormat(angle, 1); - text.sprintf(" (R%s, %s)", radiusString.c_str(), angleString.c_str()); - setPositionText(onSketchPos, text); - } - - drawEdit(EditCurve); - if (seekAutoConstraint(sugConstr2, onSketchPos, Base::Vector2d(0.f, 0.f))) { - renderSuggestConstraintsCursor(sugConstr2); - return; - } - } - applyCursor(); - } - - bool pressButton(Base::Vector2d onSketchPos) override - { - if (Mode == STATUS_SEEK_First) { - StartPos = onSketchPos; - Mode = STATUS_SEEK_Second; - } - else { - Mode = STATUS_End; - } - return true; - } - - bool releaseButton(Base::Vector2d onSketchPos) override - { - Q_UNUSED(onSketchPos); - if (Mode == STATUS_End) { - unsetCursor(); - resetPositionText(); - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add hexagon")); - - try { - Gui::Command::doCommand(Gui::Command::Doc, - "import ProfileLib.RegularPolygon\n" - "ProfileLib.RegularPolygon.makeRegularPolygon(%s,%i,App." - "Vector(%f,%f,0),App.Vector(%f,%f,0),%s)", - Gui::Command::getObjectCmd(sketchgui->getObject()).c_str(), - Corners, - StartPos.x, - StartPos.y, - EditCurve[0].x, - EditCurve[0].y, - constructionModeAsBooleanText()); - - Gui::Command::commitCommand(); - - // add auto constraints at the center of the polygon - if (!sugConstr1.empty()) { - createAutoConstraints(sugConstr1, - getHighestCurveIndex(), - Sketcher::PointPos::mid); - sugConstr1.clear(); - } - - // add auto constraints to the last side of the polygon - if (!sugConstr2.empty()) { - createAutoConstraints(sugConstr2, - getHighestCurveIndex() - 1, - Sketcher::PointPos::end); - sugConstr2.clear(); - } - - tryAutoRecomputeIfNotSolve( - static_cast(sketchgui->getObject())); - } - catch (const Base::Exception&) { - Gui::NotifyError(sketchgui, - QT_TRANSLATE_NOOP("Notifications", "Error"), - QT_TRANSLATE_NOOP("Notifications", "Failed to add polygon")); - - Gui::Command::abortCommand(); - - tryAutoRecompute(static_cast(sketchgui->getObject())); - } - - ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath( - "User parameter:BaseApp/Preferences/Mod/Sketcher"); - bool continuousMode = hGrp->GetBool("ContinuousCreationMode", true); - - if (continuousMode) { - // This code enables the continuous creation mode. - Mode = STATUS_SEEK_First; - EditCurve.clear(); - drawEdit(EditCurve); - EditCurve.resize(Corners + 1); - applyCursor(); - /* this is ok not to call to purgeHandler - * in continuous creation mode because the - * handler is destroyed by the quit() method on pressing the - * right button of the mouse */ - } - else { - sketchgui->purgeHandler(); // no code after this line, Handler get deleted in - // ViewProvider - } - } - return true; - } + ~DrawSketchHandlerPolygon() override = default; private: + void updateDataAndDrawToPosition(Base::Vector2d onSketchPos) override + { + switch (state()) { + case SelectMode::SeekFirst: { + drawPositionAtCursor(onSketchPos); + centerPoint = onSketchPos; + + if (seekAutoConstraint(sugConstraints[0], onSketchPos, Base::Vector2d(0.f, 0.f))) { + renderSuggestConstraintsCursor(sugConstraints[0]); + return; + } + } break; + case SelectMode::SeekSecond: { + firstCorner = onSketchPos; + + try { + CreateAndDrawShapeGeometry(); + } + catch (const Base::ValueError&) { + // equal points while hovering raise an objection that can be safely ignored + } + + if (seekAutoConstraint(sugConstraints[1], onSketchPos, Base::Vector2d(0.f, 0.f))) { + renderSuggestConstraintsCursor(sugConstraints[1]); + return; + } + } break; + default: + break; + } + } + + void executeCommands() override + { + unsetCursor(); + resetPositionText(); + Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add polygon")); + + try { + Gui::Command::doCommand(Gui::Command::Doc, + "import ProfileLib.RegularPolygon\n" + "ProfileLib.RegularPolygon.makeRegularPolygon(%s,%i,App.Vector(" + "%f,%f,0),App.Vector(%f,%f,0),%s)", + Gui::Command::getObjectCmd(sketchgui->getObject()).c_str(), + numberOfCorners, + centerPoint.x, + centerPoint.y, + firstCorner.x, + firstCorner.y, + constructionModeAsBooleanText()); + + Gui::Command::commitCommand(); + + tryAutoRecomputeIfNotSolve( + static_cast(sketchgui->getObject())); + } + catch (const Base::Exception& e) { + Gui::NotifyError(sketchgui, + QT_TRANSLATE_NOOP("Notifications", "Error"), + QT_TRANSLATE_NOOP("Notifications", "Failed to add polygon")); + + Gui::Command::abortCommand(); + THROWM(Base::RuntimeError, + QT_TRANSLATE_NOOP( + "Notifications", + "Tool execution aborted") "\n") // This prevents constraints from being + // applied on non existing geometry + } + } + + void generateAutoConstraints() override + { + // add auto constraints at the center of the polygon + int circlegeoid = getHighestCurveIndex(); + int lastsidegeoid = getHighestCurveIndex() - 1; + if (sugConstraints[0].size() > 0) { + generateAutoConstraintsOnElement(sugConstraints[0], + circlegeoid, + Sketcher::PointPos::mid); + } + + // add auto constraints to the last side of the polygon + if (sugConstraints[1].size() > 0) { + generateAutoConstraintsOnElement(sugConstraints[1], + lastsidegeoid, + Sketcher::PointPos::end); + } + + // Ensure temporary autoconstraints do not generate a redundancy and that the geometry + // parameters are accurate This is particularly important for adding widget mandated + // constraints. + removeRedundantAutoConstraints(); + } + + void createAutoConstraints() override + { + createGeneratedAutoConstraints(true); + + sugConstraints[0].clear(); + sugConstraints[1].clear(); + } + + std::string getToolName() const override + { + return "DSH_Polygon"; + } + QString getCrosshairCursorSVGName() const override { return QString::fromLatin1("Sketcher_Pointer_Regular_Polygon"); } -protected: - const size_t Corners; - const double AngleOfSeparation; - const double cos_v, sin_v; - SelectMode Mode; - Base::Vector2d StartPos; - std::vector EditCurve; - std::vector sugConstr1, sugConstr2; + std::unique_ptr createWidget() const override + { + return std::make_unique(); + } + + bool isWidgetVisible() const override + { + return true; + }; + + QPixmap getToolIcon() const override + { + return Gui::BitmapFactory().pixmap("Sketcher_CreateRegularPolygon"); + } + + QString getToolWidgetText() const override + { + return QString(QObject::tr("Polygon parameters")); + } + + bool canGoToNextMode() override + { + if (state() == SelectMode::SeekSecond && radius < Precision::Confusion()) { + // Prevent validation of null shape. + return false; + } + + return true; + } + + void angleSnappingControl() override + { + if (state() == SelectMode::SeekSecond) { + setAngleSnapping(true, centerPoint); + } + + else { + setAngleSnapping(false); + } + } + +private: + unsigned int numberOfCorners; + Base::Vector2d centerPoint, firstCorner; + double radius; + + void createShape(bool onlyeditoutline) override + { + Q_UNUSED(onlyeditoutline) + + ShapeGeometry.clear(); + + Base::Vector2d prevCorner = firstCorner; + + Base::Vector2d dV = firstCorner - centerPoint; + radius = dV.Length(); + if (radius < Precision::Confusion()) { + return; + } + + double angleOfSeparation = 2.0 * M_PI / static_cast(numberOfCorners); + double cos_v = cos(angleOfSeparation); + double sin_v = sin(angleOfSeparation); + + double rx = dV.x; + double ry = dV.y; + + for (int i = 1; i <= static_cast(numberOfCorners); i++) { + const double old_rx = rx; + rx = cos_v * rx - sin_v * ry; + ry = cos_v * ry + sin_v * old_rx; + Base::Vector2d newCorner = Base::Vector2d(centerPoint.x + rx, centerPoint.y + ry); + addLineToShapeGeometry(toVector3d(prevCorner), + toVector3d(newCorner), + isConstructionMode()); + prevCorner = newCorner; + } + } }; +template<> +auto DSHPolygonControllerBase::getState(int labelindex) const +{ + switch (labelindex) { + case OnViewParameter::First: + case OnViewParameter::Second: + return SelectMode::SeekFirst; + break; + case OnViewParameter::Third: + case OnViewParameter::Fourth: + return SelectMode::SeekSecond; + break; + default: + THROWM(Base::ValueError, "Label index without an associated machine state") + } +} + +template<> +void DSHPolygonController::firstKeyShortcut() +{ + auto value = toolWidget->getParameter(WParameter::First); + toolWidget->setParameter(OnViewParameter::First, value + 1); +} + +template<> +void DSHPolygonController::secondKeyShortcut() +{ + auto value = toolWidget->getParameter(WParameter::First); + if (value > 3.0) { + toolWidget->setParameter(OnViewParameter::First, value - 1); + } +} + +template<> +void DSHPolygonController::configureToolWidget() +{ + + toolWidget->setParameterLabel(OnViewParameter::First, + QApplication::translate("ToolWidgetManager_p4", "Sides 'U'/'J'")); + toolWidget->setParameter(OnViewParameter::First, + handler->numberOfCorners); // unconditionally set + toolWidget->configureParameterUnit(OnViewParameter::First, Base::Unit()); + toolWidget->configureParameterMin(OnViewParameter::First, 3.0); + toolWidget->configureParameterDecimals(OnViewParameter::First, 0); + + onViewParameters[OnViewParameter::First]->setLabelType(Gui::SoDatumLabel::DISTANCEX); + onViewParameters[OnViewParameter::Second]->setLabelType(Gui::SoDatumLabel::DISTANCEY); + onViewParameters[OnViewParameter::Fourth]->setLabelType(Gui::SoDatumLabel::ANGLE); +} + +template<> +void DSHPolygonController::adaptDrawingToParameterChange(int parameterindex, double value) +{ + switch (parameterindex) { + case WParameter::First: + handler->numberOfCorners = std::max(3, static_cast(value)); + break; + } +} + +template<> +void DSHPolygonControllerBase::doEnforceControlParameters(Base::Vector2d& onSketchPos) +{ + + switch (handler->state()) { + case SelectMode::SeekFirst: { + if (onViewParameters[OnViewParameter::First]->isSet) { + onSketchPos.x = onViewParameters[OnViewParameter::First]->getValue(); + } + + if (onViewParameters[OnViewParameter::Second]->isSet) { + onSketchPos.y = onViewParameters[OnViewParameter::Second]->getValue(); + } + } break; + case SelectMode::SeekSecond: { + Base::Vector2d dir = onSketchPos - handler->centerPoint; + double length = dir.Length(); + + if (onViewParameters[OnViewParameter::Third]->isSet) { + length = onViewParameters[OnViewParameter::Third]->getValue(); + if (length < Precision::Confusion()) { + unsetOnViewParameter(onViewParameters[OnViewParameter::Third].get()); + } + else { + if (dir.Length() < Precision::Confusion()) { + dir.x = 1; // if direction cannot be determined, default to (1,0) + } + + onSketchPos = handler->centerPoint + length * dir.Normalize(); + } + } + + if (onViewParameters[OnViewParameter::Fourth]->isSet) { + double angle = onViewParameters[OnViewParameter::Fourth]->getValue() * M_PI / 180; + onSketchPos.x = handler->centerPoint.x + cos(angle) * length; + onSketchPos.y = handler->centerPoint.y + sin(angle) * length; + } + } break; + default: + break; + } +} + +template<> +void DSHPolygonController::adaptParameters(Base::Vector2d onSketchPos) +{ + switch (handler->state()) { + case SelectMode::SeekFirst: { + if (!onViewParameters[OnViewParameter::First]->isSet) { + onViewParameters[OnViewParameter::First]->setSpinboxValue(onSketchPos.x); + } + + if (!onViewParameters[OnViewParameter::Second]->isSet) { + onViewParameters[OnViewParameter::Second]->setSpinboxValue(onSketchPos.y); + } + + bool sameSign = onSketchPos.x * onSketchPos.y > 0.; + onViewParameters[OnViewParameter::First]->setLabelAutoDistanceReverse(!sameSign); + onViewParameters[OnViewParameter::Second]->setLabelAutoDistanceReverse(sameSign); + onViewParameters[OnViewParameter::First]->setPoints(Base::Vector3d(), + toVector3d(onSketchPos)); + onViewParameters[OnViewParameter::Second]->setPoints(Base::Vector3d(), + toVector3d(onSketchPos)); + } break; + case SelectMode::SeekSecond: { + Base::Vector3d start = toVector3d(handler->centerPoint); + Base::Vector3d end = toVector3d(handler->firstCorner); + Base::Vector3d vec = end - start; + + if (!onViewParameters[OnViewParameter::Third]->isSet) { + onViewParameters[OnViewParameter::Third]->setSpinboxValue(vec.Length()); + } + + double range = (handler->firstCorner - handler->centerPoint).Angle(); + if (!onViewParameters[OnViewParameter::Fourth]->isSet) { + onViewParameters[OnViewParameter::Fourth]->setSpinboxValue(range * 180 / M_PI, + Base::Unit::Angle); + } + + onViewParameters[OnViewParameter::Third]->setPoints(start, end); + onViewParameters[OnViewParameter::Fourth]->setPoints(start, Base::Vector3d()); + onViewParameters[OnViewParameter::Fourth]->setLabelRange(range); + + } break; + default: + break; + } +} + +template<> +void DSHPolygonController::doChangeDrawSketchHandlerMode() +{ + switch (handler->state()) { + case SelectMode::SeekFirst: { + if (onViewParameters[OnViewParameter::First]->isSet + && onViewParameters[OnViewParameter::Second]->isSet) { + + handler->setState(SelectMode::SeekSecond); + } + } break; + case SelectMode::SeekSecond: { + if (onViewParameters[OnViewParameter::Third]->isSet + && onViewParameters[OnViewParameter::Fourth]->isSet) { + + handler->setState(SelectMode::End); + } + } break; + default: + break; + } +} + +template<> +void DSHPolygonController::addConstraints() +{ + int lastCurve = handler->getHighestCurveIndex(); + + auto x0 = onViewParameters[OnViewParameter::First]->getValue(); + auto y0 = onViewParameters[OnViewParameter::Second]->getValue(); + auto radius = onViewParameters[OnViewParameter::Third]->getValue(); + + auto x0set = onViewParameters[OnViewParameter::First]->isSet; + auto y0set = onViewParameters[OnViewParameter::Second]->isSet; + auto radiusSet = onViewParameters[OnViewParameter::Third]->isSet; + + using namespace Sketcher; + + auto constraintx0 = [&]() { + ConstraintToAttachment(GeoElementId(lastCurve, PointPos::mid), + GeoElementId::VAxis, + x0, + handler->sketchgui->getObject()); + }; + + auto constrainty0 = [&]() { + ConstraintToAttachment(GeoElementId(lastCurve, PointPos::mid), + GeoElementId::HAxis, + y0, + handler->sketchgui->getObject()); + }; + + auto constraintradius = [&]() { + Gui::cmdAppObjectArgs(handler->sketchgui->getObject(), + "addConstraint(Sketcher.Constraint('Radius',%d,%f)) ", + lastCurve, + radius); + }; + + // NOTE: if AutoConstraints is empty, we can add constraints directly without any diagnose. No + // diagnose was run. + if (handler->AutoConstraints.empty()) { + if (x0set) { + constraintx0(); + } + + if (y0set) { + constrainty0(); + } + + if (radiusSet) { + constraintradius(); + } + } + else { // There is a valid diagnose. + auto startpointinfo = handler->getPointInfo(GeoElementId(lastCurve, PointPos::mid)); + + // if Autoconstraints is empty we do not have a diagnosed system and the parameter will + // always be set + if (x0set && startpointinfo.isXDoF()) { + constraintx0(); + + handler->diagnoseWithAutoConstraints(); // ensure we have recalculated parameters after + // each constraint addition + + startpointinfo = handler->getPointInfo( + GeoElementId(lastCurve, PointPos::mid)); // get updated point position + } + + // if Autoconstraints is empty we do not have a diagnosed system and the parameter will + // always be set + if (y0set && startpointinfo.isYDoF()) { + constrainty0(); + + handler->diagnoseWithAutoConstraints(); // ensure we have recalculated parameters after + // each constraint addition + } + + auto edgeinfo = handler->getEdgeInfo(lastCurve); + auto circle = static_cast(edgeinfo); + + // if Autoconstraints is empty we do not have a diagnosed system and the parameter will + // always be set + if (radiusSet && circle.isRadiusDoF()) { + constraintradius(); + } + } +} + } // namespace SketcherGui