diff --git a/src/Gui/Navigation/NavigationStyle.cpp b/src/Gui/Navigation/NavigationStyle.cpp index 7137011838..e312803d19 100644 --- a/src/Gui/Navigation/NavigationStyle.cpp +++ b/src/Gui/Navigation/NavigationStyle.cpp @@ -606,16 +606,30 @@ void NavigationStyle::boxZoom(const SbBox2s& box) // Set height or height angle of the camera float scaleX = (float)sizeX/(float)size[0]; float scaleY = (float)sizeY/(float)size[1]; - float scale = std::max(scaleX, scaleY); - if (cam->getTypeId() == SoOrthographicCamera::getClassTypeId()) { - float height = static_cast(cam)->height.getValue() * scale; - static_cast(cam)->height = height; - } - else if (cam->getTypeId() == SoPerspectiveCamera::getClassTypeId()) { - float height = static_cast(cam)->heightAngle.getValue() / 2.0f; - height = 2.0f * atan(tan(height) * scale); - static_cast(cam)->heightAngle = height; + float scaleFactor = std::max(scaleX, scaleY); + + doScale(cam, scaleFactor); +} +void NavigationStyle::scale(float factor) +{ + SoCamera* cam = viewer->getSoRenderManager()->getCamera(); + if (!cam) { // no camera + return; } + + // Find the current center of the screen + SbVec3f direction; + cam->orientation.getValue().multVec(SbVec3f(0, 0, -1), direction); + SbVec3f initCenter = cam->position.getValue() + cam->focalDistance.getValue() * direction; + + // Move the camera to the origin for scaling + cam->position = cam->position.getValue() - initCenter; + + // Scale the view + doScale(cam, factor); + + // Move the camera back to it's initial position scaled + cam->position = cam->position.getValue() + initCenter * factor; } void NavigationStyle::viewAll() @@ -954,7 +968,18 @@ void NavigationStyle::doZoom(SoCamera* camera, float logfactor, const SbVec2f& p } } } - +void NavigationStyle::doScale(SoCamera * cam, float factor) +{ + if (cam->getTypeId() == SoOrthographicCamera::getClassTypeId()) { + float height = static_cast(cam)->height.getValue() * factor; + static_cast(cam)->height = height; + } + else if (cam->getTypeId() == SoPerspectiveCamera::getClassTypeId()) { + float height = static_cast(cam)->heightAngle.getValue() / 2.0f; + height = 2.0f * atan(tan(height) * factor); + static_cast(cam)->heightAngle = height; + } +} void NavigationStyle::doRotate(SoCamera * camera, float angle, const SbVec2f& pos) { SbBool zoomAtCur = this->zoomAtCursor; diff --git a/src/Gui/Navigation/NavigationStyle.h b/src/Gui/Navigation/NavigationStyle.h index 152b6f030a..348bbea95c 100644 --- a/src/Gui/Navigation/NavigationStyle.h +++ b/src/Gui/Navigation/NavigationStyle.h @@ -165,6 +165,8 @@ public: void reorientCamera(SoCamera* camera, const SbRotation& rotation, const SbVec3f& rotationCenter); void boxZoom(const SbBox2s& box); + // Scale the camera inplace + void scale(float factor); virtual void viewAll(); void setViewingMode(const ViewerMode newmode); @@ -221,6 +223,7 @@ protected: virtual void zoomByCursor(const SbVec2f & thispos, const SbVec2f & prevpos); void doZoom(SoCamera * camera, int wheeldelta, const SbVec2f& pos); void doZoom(SoCamera * camera, float logzoomfactor, const SbVec2f& pos); + void doScale(SoCamera * camera, float factor); void doRotate(SoCamera * camera, float angle, const SbVec2f& pos); void spin(const SbVec2f & pointerpos); SbBool doSpin(); diff --git a/src/Gui/View3DInventorViewer.cpp b/src/Gui/View3DInventorViewer.cpp index 38ef27286c..1f9638b348 100644 --- a/src/Gui/View3DInventorViewer.cpp +++ b/src/Gui/View3DInventorViewer.cpp @@ -3378,6 +3378,10 @@ void View3DInventorViewer::boxZoom(const SbBox2s& box) { navigation->boxZoom(box); } +void View3DInventorViewer::scale(float factor) +{ + navigation->scale(factor); +} SbBox3f View3DInventorViewer::getBoundingBox() const { diff --git a/src/Gui/View3DInventorViewer.h b/src/Gui/View3DInventorViewer.h index 47fa52d4e3..242d80bef3 100644 --- a/src/Gui/View3DInventorViewer.h +++ b/src/Gui/View3DInventorViewer.h @@ -407,6 +407,10 @@ public: * Zooms the viewport to the size of the bounding box. */ void boxZoom(const SbBox2s&); + /** + * Scale the viewport by a linear amount + */ + void scale(float factor); /** * Reposition the current camera so we can see the complete scene. */ diff --git a/src/Mod/Sketcher/App/SketchObject.cpp b/src/Mod/Sketcher/App/SketchObject.cpp index 464b7e3b12..422ba65fa1 100644 --- a/src/Mod/Sketcher/App/SketchObject.cpp +++ b/src/Mod/Sketcher/App/SketchObject.cpp @@ -886,6 +886,13 @@ int SketchObject::setDatum(int ConstrId, double Datum) return err; } +double SketchObject::getDatum(int ConstrId) const +{ + if (!this->Constraints[ConstrId]->isDimensional()) { + return 0.0; + } + return this->Constraints[ConstrId]->getValue(); +} int SketchObject::setDriving(int ConstrId, bool isdriving) { @@ -7596,6 +7603,22 @@ int SketchObject::getGeoIdFromCompleteGeometryIndex(int completeGeometryIndex) c else return (completeGeometryIndex - completeGeometryCount); } +bool SketchObject::hasSingleScaleDefiningConstraint() const +{ + const std::vector& vals = this->Constraints.getValues(); + + bool foundOne = false; + for (auto val : vals) { + // An angle does not define scale + if (val->isDimensional() && val->Type != Angle) { + if (foundOne) { + return false; + } + foundOne = true; + } + } + return foundOne; +} std::unique_ptr SketchObject::getGeometryFacade(int GeoId) const { diff --git a/src/Mod/Sketcher/App/SketchObject.h b/src/Mod/Sketcher/App/SketchObject.h index 7e70270427..eb8a5433b5 100644 --- a/src/Mod/Sketcher/App/SketchObject.h +++ b/src/Mod/Sketcher/App/SketchObject.h @@ -272,6 +272,8 @@ public: int getGeoIdFromCompleteGeometryIndex(int completeGeometryIndex) const; + bool hasSingleScaleDefiningConstraint() const; + /// returns non zero if the sketch contains conflicting constraints int hasConflicts() const; /** @@ -299,6 +301,8 @@ public: int solve(bool updateGeoAfterSolving = true); /// set the datum of a Distance or Angle constraint and solve int setDatum(int ConstrId, double Datum); + /// get the datum of a Distance or Angle constraint + double getDatum(int ConstrId) const; /// set the driving status of this constraint and solve int setDriving(int ConstrId, bool isdriving); /// get the driving status of this constraint diff --git a/src/Mod/Sketcher/Gui/CMakeLists.txt b/src/Mod/Sketcher/Gui/CMakeLists.txt index 2128149650..2161d4f545 100644 --- a/src/Mod/Sketcher/Gui/CMakeLists.txt +++ b/src/Mod/Sketcher/Gui/CMakeLists.txt @@ -75,6 +75,7 @@ SET(SketcherGui_SRCS CommandCreateGeo.cpp CommandConstraints.h CommandConstraints.cpp + CommandSketcherTools.h CommandSketcherTools.cpp CommandSketcherBSpline.cpp CommandSketcherOverlay.cpp diff --git a/src/Mod/Sketcher/Gui/CommandSketcherTools.cpp b/src/Mod/Sketcher/Gui/CommandSketcherTools.cpp index 4058d4e827..6c007b8761 100644 --- a/src/Mod/Sketcher/Gui/CommandSketcherTools.cpp +++ b/src/Mod/Sketcher/Gui/CommandSketcherTools.cpp @@ -42,16 +42,23 @@ #include #include #include +#include #include #include #include #include #include +#include +#include +#include +#include "CommandSketcherTools.h" #include "DrawSketchHandler.h" #include "SketchRectangularArrayDialog.h" #include "Utils.h" #include "ViewProviderSketch.h" +#include + #include "DrawSketchHandlerTranslate.h" #include "DrawSketchHandlerOffset.h" @@ -2522,3 +2529,24 @@ void CreateSketcherCommandsConstraintAccel() rcCmdMgr.addCommand(new CmdSketcherPaste()); } // clang-format on + +void SketcherGui::centerScale(Sketcher::SketchObject* Obj, double scaleFactor) +{ + std::vector allGeoIds(Obj->Geometry.getValues().size()); + std::iota(allGeoIds.begin(), allGeoIds.end(), 0); + + Gui::Document* doc = Gui::Application::Instance->activeDocument(); + auto* vp = static_cast(doc->getInEdit()); + auto scaler = DrawSketchHandlerScale::make_centerScale(allGeoIds, scaleFactor, false); + scaler->setSketchGui(vp); + scaler->executeCommands(); + + if (auto* view3d = dynamic_cast(doc->getActiveView())) { + auto viewer = view3d->getViewer(); + bool isAnimating = viewer->isAnimationEnabled(); + + viewer->setAnimationEnabled(false); + viewer->scale(scaleFactor); + viewer->setAnimationEnabled(isAnimating); + } +} diff --git a/src/Mod/Sketcher/Gui/CommandSketcherTools.h b/src/Mod/Sketcher/Gui/CommandSketcherTools.h new file mode 100644 index 0000000000..c0d6204da4 --- /dev/null +++ b/src/Mod/Sketcher/Gui/CommandSketcherTools.h @@ -0,0 +1,17 @@ + +#ifndef SKETCHERGUI_CommandSketcherTools_H +#define SKETCHERGUI_CommandSketcherTools_H + +#include + +namespace SketcherGui +{ + +// These functions are declared here to promote code reuse from other modules + +/// Scale the sketch around it's origin by a factor +/// and will not abort the current transaction if it fails +void centerScale(Sketcher::SketchObject* Obj, double scale_factor); + +} // namespace SketcherGui +#endif // SKETCHERGUI_CommandSketcherTools_H diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandler.cpp b/src/Mod/Sketcher/Gui/DrawSketchHandler.cpp index 7c66b4ac9a..640dbf5b26 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandler.cpp +++ b/src/Mod/Sketcher/Gui/DrawSketchHandler.cpp @@ -309,6 +309,10 @@ void DrawSketchHandler::activate(ViewProviderSketch* vp) sketchgui->purgeHandler(); } } +void DrawSketchHandler::setSketchGui(ViewProviderSketch* vp) +{ + sketchgui = vp; +} void DrawSketchHandler::deactivate() { diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandler.h b/src/Mod/Sketcher/Gui/DrawSketchHandler.h index 4954bb5c38..607f4ee50a 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandler.h +++ b/src/Mod/Sketcher/Gui/DrawSketchHandler.h @@ -145,6 +145,7 @@ public: virtual ~DrawSketchHandler(); void activate(ViewProviderSketch*); + void setSketchGui(ViewProviderSketch* vp); void deactivate() override; virtual void mouseMove(Base::Vector2d pos) = 0; diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandlerScale.h b/src/Mod/Sketcher/Gui/DrawSketchHandlerScale.h index 547a5a9c8a..9ef102712d 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandlerScale.h +++ b/src/Mod/Sketcher/Gui/DrawSketchHandlerScale.h @@ -40,6 +40,8 @@ #include "GeometryCreationMode.h" #include "Utils.h" +#include + using namespace Sketcher; namespace SketcherGui @@ -71,6 +73,8 @@ public: explicit DrawSketchHandlerScale(std::vector listOfGeoIds) : listOfGeoIds(listOfGeoIds) , deleteOriginal(true) + , abortOnFail(true) + , allowOriginConstraint(false) , refLength(0.0) , length(0.0) , scaleFactor(0.0) @@ -83,6 +87,51 @@ public: ~DrawSketchHandlerScale() override = default; + static std::unique_ptr + make_centerScale(std::vector listOfGeoIds, double scaleFactor, bool abortOnFail) + { + auto out = std::make_unique(listOfGeoIds); + out->referencePoint = Base::Vector2d(0.0, 0.0); + out->scaleFactor = scaleFactor; + out->abortOnFail = abortOnFail; + out->allowOriginConstraint = true; + return out; + } + +public: + void executeCommands() override + { + try { + Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Scale geometries")); + + createShape(false); + + commandAddShapeGeometryAndConstraints(); + + if (deleteOriginal) { + deleteOriginalGeos(); + } + + Gui::Command::commitCommand(); + } + catch (const Base::Exception& e) { + e.reportException(); + Gui::NotifyError(sketchgui, + QT_TRANSLATE_NOOP("Notifications", "Error"), + QT_TRANSLATE_NOOP("Notifications", "Failed to scale")); + + if (abortOnFail) { + Gui::Command::abortCommand(); + } + THROWM(Base::RuntimeError, + QT_TRANSLATE_NOOP( + "Notifications", + "Tool execution aborted") "\n") // This prevents constraints from being + // applied on non existing geometry + } + } + + private: void updateDataAndDrawToPosition(Base::Vector2d onSketchPos) override { @@ -109,36 +158,6 @@ private: } } - void executeCommands() override - { - try { - Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Scale geometries")); - - createShape(false); - - commandAddShapeGeometryAndConstraints(); - - if (deleteOriginal) { - deleteOriginalGeos(); - } - - Gui::Command::commitCommand(); - } - catch (const Base::Exception& e) { - e.reportException(); - Gui::NotifyError(sketchgui, - QT_TRANSLATE_NOOP("Notifications", "Error"), - QT_TRANSLATE_NOOP("Notifications", "Failed to scale")); - - 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 createAutoConstraints() override { // none @@ -204,8 +223,12 @@ private: std::vector listOfGeoIds; Base::Vector2d referencePoint, startPoint, endPoint; bool deleteOriginal; + bool abortOnFail; // When the scale operation is part of a larger transaction, one might want + // to continue even if the scaling failed + bool allowOriginConstraint; // Conserve constraints with origin double refLength, length, scaleFactor; + void deleteOriginalGeos() { std::stringstream stream; @@ -340,48 +363,75 @@ private: std::vector geoIdsWhoAlreadyHasEqual = {}; for (auto& cstr : vals) { + if (skipConstraint(cstr)) { + continue; + } + int firstIndex = indexOfGeoId(listOfGeoIds, cstr->First); int secondIndex = indexOfGeoId(listOfGeoIds, cstr->Second); int thirdIndex = indexOfGeoId(listOfGeoIds, cstr->Third); auto newConstr = std::unique_ptr(cstr->copy()); - newConstr->First = firstCurveCreated + firstIndex; + newConstr->First = offsetGeoID(firstIndex, firstCurveCreated); if ((cstr->Type == Symmetric || cstr->Type == Tangent || cstr->Type == Perpendicular || cstr->Type == Angle) - && firstIndex >= 0 && secondIndex >= 0 && thirdIndex >= 0) { - newConstr->Second = firstCurveCreated + secondIndex; - newConstr->Third = firstCurveCreated + thirdIndex; + && secondIndex != GeoEnum::GeoUndef && thirdIndex != GeoEnum::GeoUndef) { + newConstr->Second = offsetGeoID(secondIndex, firstCurveCreated); + newConstr->Third = offsetGeoID(thirdIndex, firstCurveCreated); } else if ((cstr->Type == Coincident || cstr->Type == Tangent || cstr->Type == Symmetric || cstr->Type == Perpendicular || cstr->Type == Parallel || cstr->Type == Equal || cstr->Type == Angle || cstr->Type == PointOnObject || cstr->Type == InternalAlignment) - && firstIndex >= 0 && secondIndex >= 0 - && thirdIndex == GeoEnum::GeoUndef) { - newConstr->Second = firstCurveCreated + secondIndex; + && secondIndex != GeoEnum::GeoUndef && thirdIndex == GeoEnum::GeoUndef) { + newConstr->Second = offsetGeoID(secondIndex, firstCurveCreated); } - else if ((cstr->Type == Radius || cstr->Type == Diameter) && firstIndex >= 0) { + else if (cstr->Type == Radius || cstr->Type == Diameter) { newConstr->setValue(newConstr->getValue() * scaleFactor); } else if ((cstr->Type == Distance || cstr->Type == DistanceX || cstr->Type == DistanceY) - && firstIndex >= 0 && secondIndex >= 0) { - newConstr->Second = firstCurveCreated + secondIndex; + && secondIndex != GeoEnum::GeoUndef) { + newConstr->Second = offsetGeoID(secondIndex, firstCurveCreated); newConstr->setValue(newConstr->getValue() * scaleFactor); } - else if ((cstr->Type == Block || cstr->Type == Weight) && firstIndex >= 0) { - newConstr->First = firstCurveCreated + firstIndex; - } - else { - continue; - } + // (cstr->Type == Block || cstr->Type == Weight) ShapeConstraints.push_back(std::move(newConstr)); } } } + bool skipConstraint(const Constraint* constr) const + { + // We might want to skip (remove) a constraint if + return + // 1. it's first geometry is undefined => not a valid constraint, should not happen + (constr->First == GeoEnum::GeoUndef) + // 2. we do not want to have constraints that relate to the origin => it would break if + // the scale center is not the origin + || (!allowOriginConstraint + && (constr->First == GeoEnum::VAxis || constr->First == GeoEnum::HAxis + || constr->Second == GeoEnum::VAxis || constr->Second == GeoEnum::HAxis + || constr->Third == GeoEnum::VAxis || constr->Third == GeoEnum::HAxis)) + + // 3. it is linked to an external projected geometry => would be unstable + || (constr->First != GeoEnum::GeoUndef && constr->First <= GeoEnum::RefExt) + || (constr->Second != GeoEnum::GeoUndef && constr->Second <= GeoEnum::RefExt) + || (constr->Third != GeoEnum::GeoUndef && constr->Third <= GeoEnum::RefExt); + } + + // Offset the geom index to match the newly created one + // except if it is negative in which case it is external + // or origin which remain unchanged + // this assumes that a call to skipConstraint() has been + // performed and that the constraint is valid within the context + // of the scale operation + int offsetGeoID(int index, int firstCurveCreated) + { + return index < 0 ? index : index + firstCurveCreated; + } Base::Vector3d getScaledPoint(Base::Vector3d&& pointToScale, const Base::Vector2d& referencePoint, double scaleFactor) diff --git a/src/Mod/Sketcher/Gui/EditDatumDialog.cpp b/src/Mod/Sketcher/Gui/EditDatumDialog.cpp index fd8337da31..d3f8ac32b7 100644 --- a/src/Mod/Sketcher/Gui/EditDatumDialog.cpp +++ b/src/Mod/Sketcher/Gui/EditDatumDialog.cpp @@ -37,14 +37,20 @@ #include #include #include +#include #include #include +#include #include "EditDatumDialog.h" +#include "CommandSketcherTools.h" #include "Utils.h" #include "ViewProviderSketch.h" +#include "SketcherSettings.h" #include "ui_InsertDatum.h" +#include + using namespace SketcherGui; @@ -222,6 +228,9 @@ void EditDatumDialog::accepted() else { auto unitString = newQuant.getUnit().getString(); unitString = Base::Tools::escapeQuotesFromString(unitString); + + performAutoScale(newDatum); + Gui::cmdAppObjectArgs(sketch, "setDatum(%i,App.Units.Quantity('%f %s'))", ConstrNbr, @@ -318,4 +327,102 @@ void EditDatumDialog::formEditorOpened(bool state) } } + +// This function checks an object's visible flag recursively in a Gui::Document +// assuming that lastParent (if provided) is visible +bool isVisibleUpTo(App::DocumentObject* obj, Gui::Document* doc, App::DocumentObject* lastParent) +{ + while (obj && obj != lastParent) { + auto parentviewprovider = doc->getViewProvider(obj); + + if (!parentviewprovider || !parentviewprovider->isVisible()) { + return false; + } + obj = obj->getFirstParent(); + } + return true; +} +bool hasVisualFeature(App::DocumentObject* obj, App::DocumentObject* rootObj, Gui::Document* doc) +{ + auto docObjects = doc->getDocument()->getObjects(); + for (auto object : docObjects) { + + // Presumably, the sketch that is being edited has visual features, but + // that's not interesting + if (object == obj) { + continue; + } + + // No need to continue analysis if the object's visible flag is down + bool visible = isVisibleUpTo(object, doc, rootObj); + if (!visible) { + continue; + } + + App::DocumentObject* link = object->getLinkedObject(); + if (link->getDocument() != doc->getDocument()) { + Gui::Document* linkDoc = Gui::Application::Instance->getDocument(link->getDocument()); + if (linkDoc && hasVisualFeature(link, link, linkDoc)) { + return true; + } + continue; + } + + // Skip objects that are not of geometric nature + if (!object->isDerivedFrom()) { + continue; + } + + // Skip datum objects + if (object->isDerivedFrom()) { + continue; + } + + // Skip container objects because getting their bounging box might + // return a valid bounding box around annotations or datums + if (object->hasExtension(App::GeoFeatureGroupExtension::getExtensionClassTypeId())) { + continue; + } + + // Get the bounding box of the object + auto viewProvider = doc->getViewProvider(object); + if (viewProvider && viewProvider->getBoundingBox().IsValid()) { + return true; + } + } + return false; +} + +void EditDatumDialog::performAutoScale(double newDatum) +{ + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath( + "User parameter:BaseApp/Preferences/Mod/Sketcher/dimensioning"); + long autoScaleMode = + hGrp->GetInt("AutoScaleMode", static_cast(SketcherGui::AutoScaleMode::Always)); + + // There is a single constraint in the sketch so it can + // be used as a reference to scale the geometries around the origin + // if there are external geometries, it is safe to assume that the sketch + // was drawn with these geometries as scale references (use <= 2 because + // the sketch axis are considered as external geometries) + if ((autoScaleMode == static_cast(SketcherGui::AutoScaleMode::Always) + || (autoScaleMode + == static_cast(SketcherGui::AutoScaleMode::WhenNoScaleFeatureIsVisible) + && !hasVisualFeature(sketch, nullptr, Gui::Application::Instance->activeDocument()))) + && sketch->getExternalGeometryCount() <= 2 && sketch->hasSingleScaleDefiningConstraint()) { + try { + double oldDatum = sketch->getDatum(ConstrNbr); + double scale_factor = newDatum / oldDatum; + float initLabelDistance = sketch->Constraints[ConstrNbr]->LabelDistance; + float initLabelPosition = sketch->Constraints[ConstrNbr]->LabelPosition; + centerScale(sketch, scale_factor); + sketch->setLabelDistance(ConstrNbr, initLabelDistance * scale_factor); + sketch->setLabelPosition(ConstrNbr, initLabelPosition * scale_factor); + } + catch (const Base::Exception& e) { + Base::Console().error("Exception performing autoscale: %s\n", e.what()); + } + } +} + #include "moc_EditDatumDialog.cpp" diff --git a/src/Mod/Sketcher/Gui/EditDatumDialog.h b/src/Mod/Sketcher/Gui/EditDatumDialog.h index 1aec82147e..d0911cf64d 100644 --- a/src/Mod/Sketcher/Gui/EditDatumDialog.h +++ b/src/Mod/Sketcher/Gui/EditDatumDialog.h @@ -65,6 +65,9 @@ private Q_SLOTS: void drivingToggled(bool); void datumChanged(); void formEditorOpened(bool); + +private: + void performAutoScale(double newDatum); }; } // namespace SketcherGui diff --git a/src/Mod/Sketcher/Gui/SketcherSettings.cpp b/src/Mod/Sketcher/Gui/SketcherSettings.cpp index 17c84fbcc8..7a752ba8b9 100644 --- a/src/Mod/Sketcher/Gui/SketcherSettings.cpp +++ b/src/Mod/Sketcher/Gui/SketcherSettings.cpp @@ -165,6 +165,9 @@ void SketcherSettings::saveSettings() hGrp->SetBool("DimensioningDiameter", Diameter); hGrp->SetBool("DimensioningRadius", Radius); + index = ui->autoScaleMode->currentIndex(); + hGrp->SetInt("AutoScaleMode", index); + hGrp = App::GetApplication().GetParameterGroupByPath( "User parameter:BaseApp/Preferences/Mod/Sketcher/Tools"); @@ -221,6 +224,16 @@ void SketcherSettings::loadSettings() index = Diameter ? (Radius ? 0 : 1) : 2; ui->radiusDiameterMode->setCurrentIndex(index); + + // The items have to be added in the same order + // as the AutoScaleMode enum + ui->autoScaleMode->clear(); + ui->autoScaleMode->addItem(tr("Always")); + ui->autoScaleMode->addItem(tr("Never")); + ui->autoScaleMode->addItem(tr("When no scale feature is visible")); + index = hGrp->GetInt("AutoScaleMode", static_cast(AutoScaleMode::Always)); + ui->autoScaleMode->setCurrentIndex(index); + hGrp = App::GetApplication().GetParameterGroupByPath( "User parameter:BaseApp/Preferences/Mod/Sketcher/Tools"); ui->ovpVisibility->clear(); @@ -281,6 +294,8 @@ void SketcherSettings::resetSettingsToDefaults() hGrp->RemoveBool("DimensioningDiameter"); hGrp->RemoveBool("DimensioningRadius"); + hGrp->RemoveInt("AutoScaleMode"); + hGrp = App::GetApplication().GetParameterGroupByPath( "User parameter:BaseApp/Preferences/Mod/Sketcher/Tools"); // reset "OVP visibility" parameter diff --git a/src/Mod/Sketcher/Gui/SketcherSettings.h b/src/Mod/Sketcher/Gui/SketcherSettings.h index d417b119c8..d08e2f57fc 100644 --- a/src/Mod/Sketcher/Gui/SketcherSettings.h +++ b/src/Mod/Sketcher/Gui/SketcherSettings.h @@ -128,6 +128,19 @@ private: std::unique_ptr ui; }; +// Mode for the sketch autoscale feature which scales +// the geometry and zooms the camera when the first +// scale defining constraint is set +enum class AutoScaleMode : int +{ + Always = 0, + Never = 1, + + // Attempts to find scale reference objects int the viewport + // (such as a 3d body) and disable the feature if it finds one + WhenNoScaleFeatureIsVisible = 2 +}; + } // namespace SketcherGui #endif // SKETCHERGUI_SKETCHERSETTINGS_H diff --git a/src/Mod/Sketcher/Gui/SketcherSettings.ui b/src/Mod/Sketcher/Gui/SketcherSettings.ui index 3492cc5ef8..6c3a0dc52d 100644 --- a/src/Mod/Sketcher/Gui/SketcherSettings.ui +++ b/src/Mod/Sketcher/Gui/SketcherSettings.ui @@ -7,7 +7,7 @@ 0 0 500 - 536 + 592 @@ -250,14 +250,17 @@ Requires to re-enter edit mode to take effect. Dimension constraint - - - - Dimensioning constraints: + + + + While using the Dimension tool you may choose 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. - + Select the type of dimensioning constraints for your toolbar: @@ -275,16 +278,39 @@ This setting is only for the toolbar. Whichever you choose, all tools are always - - - - While using the Dimension tool you may choose 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. + + + + Dimensioning constraints: + + + + Scale upon first constraint + + + + + + + + Always + + + + + Never + + + + + Only if there is no visual scale indicator + + + + @@ -307,7 +333,7 @@ This setting is only for the toolbar. Whichever you choose, all tools are always - + Choose a visibility mode for the On-View-Parameters: