// SPDX-License-Identifier: LGPL-2.1-or-later /*************************************************************************** * Copyright (c) 2010 Jürgen Riegel * * * * This file is part of the FreeCAD CAx development system. * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Library General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this library; see the file COPYING.LIB. If not, * * write to the Free Software Foundation, Inc., 59 Temple Place, * * Suite 330, Boston, MA 02111-1307, USA * * * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "CommandConstraints.h" #include "DrawSketchHandler.h" #include "Utils.h" #include "ViewProviderSketch.h" using namespace SketcherGui; using namespace Sketcher; /************************************ Attorney *******************************************/ inline void ViewProviderSketchDrawSketchHandlerAttorney:: setConstraintSelectability(ViewProviderSketch& vp, bool enabled /*= true*/) { vp.setConstraintSelectability(enabled); } inline void ViewProviderSketchDrawSketchHandlerAttorney::setPositionText( ViewProviderSketch& vp, const Base::Vector2d& Pos, const SbString& txt ) { vp.setPositionText(Pos, txt); } inline void ViewProviderSketchDrawSketchHandlerAttorney::setPositionText( ViewProviderSketch& vp, const Base::Vector2d& Pos ) { vp.setPositionText(Pos); } inline void ViewProviderSketchDrawSketchHandlerAttorney::resetPositionText(ViewProviderSketch& vp) { vp.resetPositionText(); } inline void ViewProviderSketchDrawSketchHandlerAttorney::drawEdit( ViewProviderSketch& vp, const std::vector& EditCurve ) { vp.drawEdit(EditCurve); } inline void ViewProviderSketchDrawSketchHandlerAttorney::drawEdit( ViewProviderSketch& vp, const std::list>& list ) { vp.drawEdit(list); } inline void ViewProviderSketchDrawSketchHandlerAttorney::drawEditMarkers( ViewProviderSketch& vp, const std::vector& EditMarkers, unsigned int augmentationlevel ) { vp.drawEditMarkers(EditMarkers, augmentationlevel); } inline void ViewProviderSketchDrawSketchHandlerAttorney::setAxisPickStyle(ViewProviderSketch& vp, bool on) { vp.setAxisPickStyle(on); } inline void ViewProviderSketchDrawSketchHandlerAttorney::moveCursorToSketchPoint( ViewProviderSketch& vp, Base::Vector2d point ) { vp.moveCursorToSketchPoint(point); } inline void ViewProviderSketchDrawSketchHandlerAttorney::ensureFocus(ViewProviderSketch& vp) { vp.ensureFocus(); } inline void ViewProviderSketchDrawSketchHandlerAttorney::preselectAtPoint( ViewProviderSketch& vp, Base::Vector2d point ) { vp.preselectAtPoint(point); } inline int ViewProviderSketchDrawSketchHandlerAttorney::getPreselectPoint(const ViewProviderSketch& vp) { return vp.getPreselectPoint(); } inline int ViewProviderSketchDrawSketchHandlerAttorney::getPreselectCurve(const ViewProviderSketch& vp) { return vp.getPreselectCurve(); } inline int ViewProviderSketchDrawSketchHandlerAttorney::getPreselectCross(const ViewProviderSketch& vp) { return vp.getPreselectCross(); } inline void ViewProviderSketchDrawSketchHandlerAttorney::setAngleSnapping( ViewProviderSketch& vp, bool enable, Base::Vector2d referencePoint ) { vp.setAngleSnapping(enable, referencePoint); } inline void ViewProviderSketchDrawSketchHandlerAttorney::moveConstraint( ViewProviderSketch& vp, int constNum, const Base::Vector2d& toPos, OffsetMode offset ) { vp.moveConstraint(constNum, toPos, offset); } inline void ViewProviderSketchDrawSketchHandlerAttorney::signalToolChanged( const ViewProviderSketch& vp, const std::string& toolname ) { vp.signalToolChanged(toolname); } /**************************** CurveConverter **********************************************/ CurveConverter::CurveConverter() { try { ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath( "User parameter:BaseApp/Preferences/View" ); hGrp->Attach(this); } catch (const Base::ValueError& e) { // ensure that if parameter strings are not well-formed, // the exception is not propagated Base::Console().developerError("CurveConverter", "Malformed parameter string: %s\n", e.what()); } updateCurvedEdgeCountSegmentsParameter(); } CurveConverter::~CurveConverter() { // Do not detach from the parameter group. // So far there is only a single static instance of CurveConverter inside // DrawSketchHandler::drawEdit. This static instance will be destroyed after // the main() function has been exited so that any attempt to access the // parameter managers is undefined behaviour. See issue #13622. } std::vector CurveConverter::toVector2D(const Part::Geometry* geometry) { std::vector vector2d; auto emplaceasvector2d = [&vector2d](const Base::Vector3d& point) { vector2d.emplace_back(point.x, point.y); }; auto isperiodicconic = geometry->is() || geometry->is(); auto isbounded = geometry->isDerivedFrom(); if (geometry->is()) { // add a line auto geo = static_cast(geometry); emplaceasvector2d(geo->getStartPoint()); emplaceasvector2d(geo->getEndPoint()); } else if (isperiodicconic || isbounded) { auto geo = static_cast(geometry); double segment = (geo->getLastParameter() - geo->getFirstParameter()) / curvedEdgeCountSegments; for (int i = 0; i < curvedEdgeCountSegments; i++) { emplaceasvector2d(geo->value(geo->getFirstParameter() + i * segment)); } // either close the curve for untrimmed conic or set the last point for bounded curves emplaceasvector2d(isperiodicconic ? geo->value(0) : geo->value(geo->getLastParameter())); } return vector2d; } std::list> CurveConverter::toVector2DList( const std::vector& geometries ) { std::list> list; for (const auto& geo : geometries) { list.push_back(toVector2D(geo)); } return list; } void CurveConverter::updateCurvedEdgeCountSegmentsParameter() { ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath( "User parameter:BaseApp/Preferences/View" ); int stdcountsegments = hGrp->GetInt("SegmentsPerGeometry", 50); // value cannot be smaller than 6 if (stdcountsegments < 6) { stdcountsegments = 6; } curvedEdgeCountSegments = stdcountsegments; }; /** Observer for parameter group. */ void CurveConverter::OnChange(Base::Subject& rCaller, const char* sReason) { (void)rCaller; if (strcmp(sReason, "SegmentsPerGeometry") == 0) { updateCurvedEdgeCountSegmentsParameter(); } } /**************************** DrawSketchHandler *******************************************/ //************************************************************************** // Construction/Destruction DrawSketchHandler::DrawSketchHandler() : Gui::ToolHandler() , sketchgui(nullptr) {} DrawSketchHandler::~DrawSketchHandler() {} std::string DrawSketchHandler::getToolName() const { return "DSH_None"; } std::unique_ptr DrawSketchHandler::createWidget() const { return nullptr; } bool DrawSketchHandler::isWidgetVisible() const { return false; }; QPixmap DrawSketchHandler::getToolIcon() const { return QPixmap(); } QString DrawSketchHandler::getToolWidgetText() const { return QString(); } void DrawSketchHandler::activate(ViewProviderSketch* vp) { sketchgui = vp; if (!Gui::ToolHandler::activate()) { sketchgui->purgeHandler(); } } void DrawSketchHandler::setSketchGui(ViewProviderSketch* vp) { sketchgui = vp; } void DrawSketchHandler::deactivate() { Gui::ToolHandler::deactivate(); ViewProviderSketchDrawSketchHandlerAttorney::setConstraintSelectability(*sketchgui, true); // clear temporary Curve and Markers from the scenograph clearEdit(); clearEditMarkers(); resetPositionText(); setAngleSnapping(false); ViewProviderSketchDrawSketchHandlerAttorney::signalToolChanged(*sketchgui, "DSH_None"); } void DrawSketchHandler::preActivated() { this->signalToolChanged(); ViewProviderSketchDrawSketchHandlerAttorney::setConstraintSelectability(*sketchgui, false); } void DrawSketchHandler::registerPressedKey(bool pressed, int key) { // the default behaviour is to quit - specific handler categories may // override this behaviour, for example to implement a continuous mode if (key == SoKeyboardEvent::ESCAPE && !pressed) { quit(); } } void DrawSketchHandler::pressRightButton(Base::Vector2d /*onSketchPos*/) { // the default behaviour is to quit - specific handler categories may // override this behaviour, for example to implement a continuous mode quit(); } void DrawSketchHandler::quit() { assert(sketchgui); Gui::Selection().rmvSelectionGate(); Gui::Selection().rmvPreselect(); sketchgui->purgeHandler(); } void DrawSketchHandler::toolWidgetChanged(QWidget* newwidget) { toolwidget = newwidget; onWidgetChanged(); } //************************************************************************** // Helpers int DrawSketchHandler::getHighestVertexIndex() { return sketchgui->getSketchObject()->getHighestVertexIndex(); } int DrawSketchHandler::getHighestCurveIndex() { return sketchgui->getSketchObject()->getHighestCurveIndex(); } std::vector DrawSketchHandler::suggestedConstraintsPixmaps( std::vector& suggestedConstraints ) { std::vector pixmaps; // Iterate through AutoConstraints types and get their pixmaps for (auto& autoCstr : suggestedConstraints) { QString iconType; switch (autoCstr.Type) { case Horizontal: iconType = QStringLiteral("Constraint_Horizontal"); break; case Vertical: iconType = QStringLiteral("Constraint_Vertical"); break; case Coincident: iconType = QStringLiteral("Constraint_PointOnPoint"); break; case PointOnObject: iconType = QStringLiteral("Constraint_PointOnObject"); break; case Symmetric: iconType = QStringLiteral("Constraint_Symmetric"); break; case Tangent: iconType = QStringLiteral("Constraint_Tangent"); break; default: break; } if (!iconType.isEmpty()) { constexpr int iconWidth = 16; QPixmap icon = Gui::BitmapFactory().pixmapFromSvg( iconType.toStdString().c_str(), QSize(iconWidth, iconWidth) ); pixmaps.push_back(icon); } } return pixmaps; } DrawSketchHandler::PreselectionData DrawSketchHandler::getPreselectionData() { SketchObject* obj = sketchgui->getSketchObject(); // Extract preselection information (vertex, curve, cross) PreselectionData preSelData; int preSelPnt = getPreselectPoint(); int preSelCrv = getPreselectCurve(); int preSelCrs = getPreselectCross(); if (preSelPnt != -1) { obj->getGeoVertexIndex(preSelPnt, preSelData.geoId, preSelData.posId); } else if (preSelCrv != -1) { const Part::Geometry* geom = obj->getGeometry(preSelCrv); if (geom) { preSelData.geoId = preSelCrv; if (geom->is()) { auto* line = static_cast(geom); preSelData.hitShapeDir = line->getEndPoint() - line->getStartPoint(); preSelData.isLine = true; } } } else if (preSelCrs == 0) { preSelData.geoId = Sketcher::GeoEnum::RtPnt; preSelData.posId = PointPos::start; } else if (preSelCrs == 1) { preSelData.geoId = Sketcher::GeoEnum::HAxis; preSelData.hitShapeDir = Base::Vector3d(1, 0, 0); preSelData.isLine = true; } else if (preSelCrs == 2) { preSelData.geoId = Sketcher::GeoEnum::VAxis; preSelData.hitShapeDir = Base::Vector3d(0, 1, 0); preSelData.isLine = true; } return preSelData; } bool DrawSketchHandler::isLineCenterAutoConstraint(int GeoId, const Base::Vector2d& Pos) const { SketchObject* obj = sketchgui->getSketchObject(); auto* geo = obj->getGeometry(GeoId); if (geo->isDerivedFrom()) { auto* line = static_cast(geo); Base::Vector2d startPoint = toVector2d(line->getStartPoint()); Base::Vector2d endPoint = toVector2d(line->getEndPoint()); Base::Vector2d midPoint = (startPoint + endPoint) / 2; // Check if we are at middle of the line if ((Pos - midPoint).Length() < (endPoint - startPoint).Length() * 0.05) { return true; } } return false; } void DrawSketchHandler::seekPreselectionAutoConstraint( std::vector& suggestedConstraints, const Base::Vector2d& Pos, const Base::Vector2d& Dir, AutoConstraint::TargetType type ) { PreselectionData preSel = getPreselectionData(); if (preSel.geoId != GeoEnum::GeoUndef) { // Currently only considers objects in current Sketcher AutoConstraint constr; constr.Type = Sketcher::None; constr.GeoId = preSel.geoId; constr.PosId = preSel.posId; if (type == AutoConstraint::VERTEX || type == AutoConstraint::VERTEX_NO_TANGENCY) { if (preSel.posId == PointPos::none) { bool lineCenter = isLineCenterAutoConstraint(preSel.geoId, Pos); constr.Type = lineCenter ? Sketcher::Symmetric : Sketcher::PointOnObject; } else { constr.Type = Sketcher::Coincident; } } else if (type == AutoConstraint::CURVE && preSel.posId != PointPos::none) { constr.Type = Sketcher::PointOnObject; } else if (type == AutoConstraint::CURVE && preSel.posId == PointPos::none) { constr.Type = Sketcher::Tangent; } if (constr.Type == Sketcher::Tangent && preSel.isLine) { if (Dir.Length() < 1e-8 || preSel.hitShapeDir.Length() < 1e-8) { return; // Direction not set so return; } // We are hitting a line and have hitting vector information Base::Vector3d dir3d = Base::Vector3d(Dir.x, Dir.y, 0); double cosangle = dir3d.Normalize() * preSel.hitShapeDir.Normalize(); // the angle between the line and the hitting direction are over around 6 degrees if (fabs(cosangle) > 0.995f) { return; } } if (constr.Type != Sketcher::None) { suggestedConstraints.push_back(constr); } } } void DrawSketchHandler::seekAlignmentAutoConstraint( std::vector& suggestedConstraints, const Base::Vector2d& Dir ) { using std::numbers::pi; constexpr double angleDevRad = Base::toRadians(2); AutoConstraint constr; constr.Type = Sketcher::None; constr.GeoId = GeoEnum::GeoUndef; constr.PosId = PointPos::none; double angle = std::abs(atan2(Dir.y, Dir.x)); if (angle < angleDevRad || (pi - angle) < angleDevRad) { // Suggest horizontal constraint constr.Type = Sketcher::Horizontal; } else if (std::abs(angle - pi / 2) < angleDevRad) { // Suggest vertical constraint constr.Type = Sketcher::Vertical; } if (constr.Type != Sketcher::None) { suggestedConstraints.push_back(constr); } } void DrawSketchHandler::seekTangentAutoConstraint( std::vector& suggestedConstraints, const Base::Vector2d& Pos, const Base::Vector2d& Dir ) { using std::numbers::pi; SketchObject* obj = sketchgui->getSketchObject(); int tangId = GeoEnum::GeoUndef; // Do not consider if distance is more than that. // Decrease this value when a candidate is found. double tangDeviation = 0.1 * sketchgui->getScaleFactor(); // Get geometry list const std::vector geomlist = obj->getCompleteGeometry(); Base::Vector3d tmpPos(Pos.x, Pos.y, 0.f); // Current cursor point Base::Vector3d tmpDir(Dir.x, Dir.y, 0.f); // Direction of line Base::Vector3d tmpStart(Pos.x - Dir.x, Pos.y - Dir.y, 0.f); // Start point int i = -1; for (auto* geo : geomlist) { i++; if (geo->is()) { auto* circle = static_cast(geo); Base::Vector3d center = circle->getCenter(); double radius = circle->getRadius(); // ignore if no touch (use dot product) if (tmpDir * (center - tmpPos) > 0 || tmpDir * (center - tmpStart) < 0) { continue; } Base::Vector3d projPnt(0.f, 0.f, 0.f); projPnt = projPnt.ProjectToLine(center - tmpPos, tmpDir); double projDist = std::abs(projPnt.Length() - radius); // Find if nearest if (projDist < tangDeviation) { tangId = i; tangDeviation = projDist; } } else if (geo->is()) { auto* ellipse = static_cast(geo); Base::Vector3d center = ellipse->getCenter(); double a = ellipse->getMajorRadius(); double b = ellipse->getMinorRadius(); Base::Vector3d majdir = ellipse->getMajorAxisDir(); double cf = sqrt(a * a - b * b); Base::Vector3d focus1P = center + cf * majdir; Base::Vector3d focus2P = center - cf * majdir; Base::Vector3d norm = Base::Vector3d(Dir.y, -Dir.x).Normalize(); double distancetoline = norm * (tmpPos - focus1P); // distance focus1 to line // mirror of focus1 with respect to the line Base::Vector3d focus1PMirrored = focus1P + 2 * distancetoline * norm; double error = fabs((focus1PMirrored - focus2P).Length() - 2 * a); if (error < tangDeviation) { tangId = i; tangDeviation = error; } } else if (geo->is()) { auto* arc = static_cast(geo); Base::Vector3d center = arc->getCenter(); double radius = arc->getRadius(); // ignore if no touch (use dot product) if (tmpDir * (center - tmpPos) > 0 || tmpDir * (center - tmpStart) < 0) { continue; } Base::Vector3d projPnt(0.f, 0.f, 0.f); projPnt = projPnt.ProjectToLine(center - tmpPos, tmpDir); double projDist = std::abs(projPnt.Length() - radius); if (projDist < tangDeviation) { double startAngle, endAngle; arc->getRange(startAngle, endAngle, /*emulateCCW=*/true); double angle = atan2(projPnt.y, projPnt.x); while (angle < startAngle) { angle += 2 * pi; // Bring it to range of arc } // if the point is on correct side of arc if (angle <= endAngle) { // Now need to check only one side tangId = i; tangDeviation = projDist; } } } else if (geo->is()) { auto* aoe = static_cast(geo); Base::Vector3d center = aoe->getCenter(); double a = aoe->getMajorRadius(); double b = aoe->getMinorRadius(); Base::Vector3d majdir = aoe->getMajorAxisDir(); double cf = sqrt(a * a - b * b); Base::Vector3d focus1P = center + cf * majdir; Base::Vector3d focus2P = center - cf * majdir; Base::Vector3d norm = Base::Vector3d(Dir.y, -Dir.x).Normalize(); double distancetoline = norm * (tmpPos - focus1P); // distance focus1 to line // mirror of focus1 with respect to the line Base::Vector3d focus1PMirrored = focus1P + 2 * distancetoline * norm; double error = fabs((focus1PMirrored - focus2P).Length() - 2 * a); if (error < tangDeviation) { double startAngle, endAngle; aoe->getRange(startAngle, endAngle, /*emulateCCW=*/true); double angle = Base::fmod( atan2( -aoe->getMajorRadius() * ((tmpPos.x - center.x) * majdir.y - (tmpPos.y - center.y) * majdir.x), aoe->getMinorRadius() * ((tmpPos.x - center.x) * majdir.x + (tmpPos.y - center.y) * majdir.y) ) - startAngle, 2.f * pi ); while (angle < startAngle) { angle += 2 * pi; // Bring it to range of arc } // if the point is on correct side of arc if (angle <= endAngle) { // Now need to check only one side tangId = i; tangDeviation = error; } } } } if (tangId != GeoEnum::GeoUndef) { if (tangId > getHighestCurveIndex()) { // external Geometry tangId = getHighestCurveIndex() - tangId; } AutoConstraint constr; constr.Type = Tangent; constr.GeoId = tangId; constr.PosId = PointPos::none; suggestedConstraints.push_back(constr); } } int DrawSketchHandler::seekAutoConstraint( std::vector& suggestedConstraints, const Base::Vector2d& Pos, const Base::Vector2d& Dir, AutoConstraint::TargetType type ) { suggestedConstraints.clear(); if (!sketchgui->Autoconstraints.getValue()) { return 0; // If Autoconstraints property is not set quit } seekPreselectionAutoConstraint(suggestedConstraints, Pos, Dir, type); if (Dir.Length() > 1e-8 && type != AutoConstraint::CURVE) { seekAlignmentAutoConstraint(suggestedConstraints, Dir); if (type != AutoConstraint::VERTEX_NO_TANGENCY) { seekTangentAutoConstraint(suggestedConstraints, Pos, Dir); } } return suggestedConstraints.size(); } void DrawSketchHandler::createAutoConstraints( const std::vector& autoConstrs, int geoId1, Sketcher::PointPos posId1, bool createowncommand /*= true*/ ) { if (!sketchgui->Autoconstraints.getValue()) { return; // If Autoconstraints property is not set quit } if (!autoConstrs.empty()) { if (createowncommand) { // Open the Command Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Add Auto-Constraints")); } // Iterate through constraints for (auto& cstr : autoConstrs) { int geoId2 = cstr.GeoId; switch (cstr.Type) { case Sketcher::Coincident: { if (posId1 == Sketcher::PointPos::none) { continue; } // If the auto constraint has a point create a coincident otherwise it is an // edge on a point Gui::cmdAppObjectArgs( sketchgui->getObject(), "addConstraint(Sketcher.Constraint('Coincident',%d,%d,%d,%d)) ", geoId1, static_cast(posId1), cstr.GeoId, static_cast(cstr.PosId) ); } break; case Sketcher::PointOnObject: { Sketcher::PointPos posId2 = cstr.PosId; if (posId1 == Sketcher::PointPos::none) { // Auto constraining an edge so swap parameters std::swap(geoId1, geoId2); std::swap(posId1, posId2); } Gui::cmdAppObjectArgs( sketchgui->getObject(), "addConstraint(Sketcher.Constraint('PointOnObject',%d,%d,%d)) ", geoId1, static_cast(posId1), geoId2 ); } break; case Sketcher::Symmetric: { Gui::cmdAppObjectArgs( sketchgui->getObject(), "addConstraint(Sketcher.Constraint('Symmetric',%d,1,%d,2,%d,%d)) ", geoId2, geoId2, geoId1, static_cast(posId1) ); } break; // In special case of Horizontal/Vertical constraint, geoId2 is normally unused // and should be 'Constraint::GeoUndef' However it can be used as a way to // require the function to apply these constraints on another geometry In this // case the caller as to set geoId2, then it will be used as target instead of // geoId2 case Sketcher::Horizontal: { Gui::cmdAppObjectArgs( sketchgui->getObject(), "addConstraint(Sketcher.Constraint('Horizontal',%d)) ", geoId2 != GeoEnum::GeoUndef ? geoId2 : geoId1 ); } break; case Sketcher::Vertical: { Gui::cmdAppObjectArgs( sketchgui->getObject(), "addConstraint(Sketcher.Constraint('Vertical',%d)) ", geoId2 != GeoEnum::GeoUndef ? geoId2 : geoId1 ); } break; case Sketcher::Tangent: { Sketcher::SketchObject* Obj = sketchgui->getSketchObject(); const Part::Geometry* geom1 = Obj->getGeometry(geoId1); const Part::Geometry* geom2 = Obj->getGeometry(cstr.GeoId); // ellipse tangency support using construction elements (lines) if (geom1 && geom2 && (geom1->is() || geom2->is())) { if (!geom1->is()) { std::swap(geoId1, geoId2); } // geoId1 is the ellipse geom1 = Obj->getGeometry(geoId1); geom2 = Obj->getGeometry(geoId2); if (geom2->is() || geom2->is() || geom2->is() || geom2->is()) { // in all these cases an intermediate element is needed makeTangentToEllipseviaNewPoint( Obj, static_cast(geom1), geom2, geoId1, geoId2 ); return; } } // arc of ellipse tangency support using external elements if (geom1 && geom2 && (geom1->is() || geom2->is())) { if (!geom1->is()) { std::swap(geoId1, geoId2); } // geoId1 is the arc of ellipse geom1 = Obj->getGeometry(geoId1); geom2 = Obj->getGeometry(geoId2); if (geom2->is() || geom2->is() || geom2->is()) { // in all these cases an intermediate element is needed makeTangentToArcOfEllipseviaNewPoint( Obj, static_cast(geom1), geom2, geoId1, geoId2 ); return; } } Gui::cmdAppObjectArgs( sketchgui->getObject(), "addConstraint(Sketcher.Constraint('Tangent',%d, %d)) ", geoId1, cstr.GeoId ); } break; default: break; } if (createowncommand) { Gui::Command::commitCommand(); } // Gui::Command::updateActive(); // There is already an recompute in each command // creation, this is redundant. } } } int DrawSketchHandler::seekAndRenderAutoConstraint( std::vector& suggestedConstraints, const Base::Vector2d& Pos, const Base::Vector2d& Dir, AutoConstraint::TargetType type ) { if (seekAutoConstraint(suggestedConstraints, Pos, Dir, type)) { renderSuggestConstraintsCursor(suggestedConstraints); } else { applyCursor(); } return suggestedConstraints.size(); } void DrawSketchHandler::renderSuggestConstraintsCursor(std::vector& suggestedConstraints) { std::vector pixmaps = suggestedConstraintsPixmaps(suggestedConstraints); addCursorTail(pixmaps); } void DrawSketchHandler::setPositionText(const Base::Vector2d& Pos, const SbString& text) { ViewProviderSketchDrawSketchHandlerAttorney::setPositionText(*sketchgui, Pos, text); } void DrawSketchHandler::setPositionText(const Base::Vector2d& Pos) { ViewProviderSketchDrawSketchHandlerAttorney::setPositionText(*sketchgui, Pos); } void DrawSketchHandler::resetPositionText() { ViewProviderSketchDrawSketchHandlerAttorney::resetPositionText(*sketchgui); } void DrawSketchHandler::drawEdit(const std::vector& EditCurve) const { ViewProviderSketchDrawSketchHandlerAttorney::drawEdit(*sketchgui, EditCurve); } void DrawSketchHandler::drawEdit(const std::list>& list) const { ViewProviderSketchDrawSketchHandlerAttorney::drawEdit(*sketchgui, list); } void DrawSketchHandler::drawEdit(const std::vector& geometries) const { static CurveConverter c; auto list = c.toVector2DList(geometries); drawEdit(list); } void DrawSketchHandler::clearEdit() const { drawEdit(std::vector()); } void DrawSketchHandler::clearEditMarkers() const { drawEditMarkers(std::vector()); } void DrawSketchHandler::drawPositionAtCursor(const Base::Vector2d& position) { setPositionText(position); } void DrawSketchHandler::drawDirectionAtCursor(const Base::Vector2d& position, const Base::Vector2d& origin) { if (!showCursorCoords()) { return; } float length = (position - origin).Length(); float angle = (position - origin).GetAngle(Base::Vector2d(1.f, 0.f)); SbString text; std::string lengthString = lengthToDisplayFormat(length, 1); std::string angleString = angleToDisplayFormat(angle * 180.0 / std::numbers::pi, 1); text.sprintf(" (%s, %s)", lengthString.c_str(), angleString.c_str()); setPositionText(position, text); } void DrawSketchHandler::drawWidthHeightAtCursor( const Base::Vector2d& position, const double val1, const double val2 ) { if (!showCursorCoords()) { return; } SbString text; std::string val1String = lengthToDisplayFormat(val1, 1); std::string val2String = lengthToDisplayFormat(val2, 1); text.sprintf(" (%s x %s)", val1String.c_str(), val2String.c_str()); setPositionText(position, text); } void DrawSketchHandler::drawDoubleAtCursor(const Base::Vector2d& position, const double val, Base::Unit unit) { if (!showCursorCoords()) { return; } SbString text; std::string doubleString = unit == Base::Unit::Length ? lengthToDisplayFormat(val, 1) : angleToDisplayFormat(Base::toDegrees(val), 1); text.sprintf(" (%s)", doubleString.c_str()); setPositionText(position, text); } std::unique_ptr DrawSketchHandler::createToolWidget() const { return createWidget(); // NVI } bool DrawSketchHandler::isToolWidgetVisible() const { return isWidgetVisible(); // NVI } QPixmap DrawSketchHandler::getToolWidgetHeaderIcon() const { return getToolIcon(); } QString DrawSketchHandler::getToolWidgetHeaderText() const { return getToolWidgetText(); } void DrawSketchHandler::drawEditMarkers( const std::vector& EditMarkers, unsigned int augmentationlevel ) const { ViewProviderSketchDrawSketchHandlerAttorney::drawEditMarkers( *sketchgui, EditMarkers, augmentationlevel ); } void DrawSketchHandler::setAxisPickStyle(bool on) { ViewProviderSketchDrawSketchHandlerAttorney::setAxisPickStyle(*sketchgui, on); } void DrawSketchHandler::moveCursorToSketchPoint(Base::Vector2d point) { ViewProviderSketchDrawSketchHandlerAttorney::moveCursorToSketchPoint(*sketchgui, point); } void DrawSketchHandler::ensureFocus() { ViewProviderSketchDrawSketchHandlerAttorney::ensureFocus(*sketchgui); } void DrawSketchHandler::preselectAtPoint(Base::Vector2d point) { ViewProviderSketchDrawSketchHandlerAttorney::preselectAtPoint(*sketchgui, point); } int DrawSketchHandler::getPreselectPoint() const { return ViewProviderSketchDrawSketchHandlerAttorney::getPreselectPoint(*sketchgui); } int DrawSketchHandler::getPreselectCurve() const { return ViewProviderSketchDrawSketchHandlerAttorney::getPreselectCurve(*sketchgui); } int DrawSketchHandler::getPreselectCross() const { return ViewProviderSketchDrawSketchHandlerAttorney::getPreselectCross(*sketchgui); } Sketcher::SketchObject* DrawSketchHandler::getSketchObject() { return sketchgui->getSketchObject(); } void DrawSketchHandler::setAngleSnapping(bool enable, Base::Vector2d referencePoint) { ViewProviderSketchDrawSketchHandlerAttorney::setAngleSnapping(*sketchgui, enable, referencePoint); } void DrawSketchHandler::moveConstraint(int constNum, const Base::Vector2d& toPos, OffsetMode offset) { ViewProviderSketchDrawSketchHandlerAttorney::moveConstraint(*sketchgui, constNum, toPos, offset); } void DrawSketchHandler::signalToolChanged() const { ViewProviderSketchDrawSketchHandlerAttorney::signalToolChanged(*sketchgui, this->getToolName()); }