Sketcher: Sketch autoscale (#21084)
* Working scale prototype * Call viewAll to fit geometries in the viewport post-scaling * Exclude angle dimensions * Scale the viewport rather than calling viewAll * Scale dimension annotation along geometries * Early return when counting more than one dimensional constraint * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Disable sketch autoscale if there are external geometries in the sketch * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add a setting to disable the feature _ and eventually parametrize * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Check for objects in the viewport in the sketch's ancestry to decide wheter or not to autoscale * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * More consistent camera scaling * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Check for visual indicator in the whole document * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Find visible items in nested assemblies * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Check visual elements in assemblies nested in assemblies * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Set the dimension even if the scaling fails * Allow constraints that interact with the origin axis/root * Remove unused variable * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Misc fixes from review * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
@@ -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<float>(scaleX, scaleY);
|
||||
if (cam->getTypeId() == SoOrthographicCamera::getClassTypeId()) {
|
||||
float height = static_cast<SoOrthographicCamera*>(cam)->height.getValue() * scale;
|
||||
static_cast<SoOrthographicCamera*>(cam)->height = height;
|
||||
}
|
||||
else if (cam->getTypeId() == SoPerspectiveCamera::getClassTypeId()) {
|
||||
float height = static_cast<SoPerspectiveCamera*>(cam)->heightAngle.getValue() / 2.0f;
|
||||
height = 2.0f * atan(tan(height) * scale);
|
||||
static_cast<SoPerspectiveCamera*>(cam)->heightAngle = height;
|
||||
float scaleFactor = std::max<float>(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<SoOrthographicCamera*>(cam)->height.getValue() * factor;
|
||||
static_cast<SoOrthographicCamera*>(cam)->height = height;
|
||||
}
|
||||
else if (cam->getTypeId() == SoPerspectiveCamera::getClassTypeId()) {
|
||||
float height = static_cast<SoPerspectiveCamera*>(cam)->heightAngle.getValue() / 2.0f;
|
||||
height = 2.0f * atan(tan(height) * factor);
|
||||
static_cast<SoPerspectiveCamera*>(cam)->heightAngle = height;
|
||||
}
|
||||
}
|
||||
void NavigationStyle::doRotate(SoCamera * camera, float angle, const SbVec2f& pos)
|
||||
{
|
||||
SbBool zoomAtCur = this->zoomAtCursor;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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<Constraint*>& 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<const GeometryFacade> SketchObject::getGeometryFacade(int GeoId) const
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -75,6 +75,7 @@ SET(SketcherGui_SRCS
|
||||
CommandCreateGeo.cpp
|
||||
CommandConstraints.h
|
||||
CommandConstraints.cpp
|
||||
CommandSketcherTools.h
|
||||
CommandSketcherTools.cpp
|
||||
CommandSketcherBSpline.cpp
|
||||
CommandSketcherOverlay.cpp
|
||||
|
||||
@@ -42,16 +42,23 @@
|
||||
#include <Gui/Document.h>
|
||||
#include <Gui/MainWindow.h>
|
||||
#include <Gui/Notifications.h>
|
||||
#include <Gui/View3DInventor.h>
|
||||
#include <Gui/Selection/Selection.h>
|
||||
#include <Gui/Selection/SelectionObject.h>
|
||||
#include <Mod/Sketcher/App/PythonConverter.h>
|
||||
#include <Mod/Sketcher/App/SketchObject.h>
|
||||
#include <Mod/Sketcher/App/SolverGeometryExtension.h>
|
||||
#include <Gui/Application.h>
|
||||
#include <Base/ServiceProvider.h>
|
||||
#include <App/Services.h>
|
||||
|
||||
#include "CommandSketcherTools.h"
|
||||
#include "DrawSketchHandler.h"
|
||||
#include "SketchRectangularArrayDialog.h"
|
||||
#include "Utils.h"
|
||||
#include "ViewProviderSketch.h"
|
||||
#include <Inventor/actions/SoGetBoundingBoxAction.h>
|
||||
|
||||
|
||||
#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<int> allGeoIds(Obj->Geometry.getValues().size());
|
||||
std::iota(allGeoIds.begin(), allGeoIds.end(), 0);
|
||||
|
||||
Gui::Document* doc = Gui::Application::Instance->activeDocument();
|
||||
auto* vp = static_cast<SketcherGui::ViewProviderSketch*>(doc->getInEdit());
|
||||
auto scaler = DrawSketchHandlerScale::make_centerScale(allGeoIds, scaleFactor, false);
|
||||
scaler->setSketchGui(vp);
|
||||
scaler->executeCommands();
|
||||
|
||||
if (auto* view3d = dynamic_cast<Gui::View3DInventor*>(doc->getActiveView())) {
|
||||
auto viewer = view3d->getViewer();
|
||||
bool isAnimating = viewer->isAnimationEnabled();
|
||||
|
||||
viewer->setAnimationEnabled(false);
|
||||
viewer->scale(scaleFactor);
|
||||
viewer->setAnimationEnabled(isAnimating);
|
||||
}
|
||||
}
|
||||
|
||||
17
src/Mod/Sketcher/Gui/CommandSketcherTools.h
Normal file
17
src/Mod/Sketcher/Gui/CommandSketcherTools.h
Normal file
@@ -0,0 +1,17 @@
|
||||
|
||||
#ifndef SKETCHERGUI_CommandSketcherTools_H
|
||||
#define SKETCHERGUI_CommandSketcherTools_H
|
||||
|
||||
#include <Mod/Sketcher/App/SketchObject.h>
|
||||
|
||||
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
|
||||
@@ -309,6 +309,10 @@ void DrawSketchHandler::activate(ViewProviderSketch* vp)
|
||||
sketchgui->purgeHandler();
|
||||
}
|
||||
}
|
||||
void DrawSketchHandler::setSketchGui(ViewProviderSketch* vp)
|
||||
{
|
||||
sketchgui = vp;
|
||||
}
|
||||
|
||||
void DrawSketchHandler::deactivate()
|
||||
{
|
||||
|
||||
@@ -145,6 +145,7 @@ public:
|
||||
virtual ~DrawSketchHandler();
|
||||
|
||||
void activate(ViewProviderSketch*);
|
||||
void setSketchGui(ViewProviderSketch* vp);
|
||||
void deactivate() override;
|
||||
|
||||
virtual void mouseMove(Base::Vector2d pos) = 0;
|
||||
|
||||
@@ -40,6 +40,8 @@
|
||||
#include "GeometryCreationMode.h"
|
||||
#include "Utils.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
using namespace Sketcher;
|
||||
|
||||
namespace SketcherGui
|
||||
@@ -71,6 +73,8 @@ public:
|
||||
explicit DrawSketchHandlerScale(std::vector<int> 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<DrawSketchHandlerScale>
|
||||
make_centerScale(std::vector<int> listOfGeoIds, double scaleFactor, bool abortOnFail)
|
||||
{
|
||||
auto out = std::make_unique<DrawSketchHandlerScale>(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<int> 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<int> 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<Constraint>(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)
|
||||
|
||||
@@ -37,14 +37,20 @@
|
||||
#include <Gui/Notifications.h>
|
||||
#include <Gui/View3DInventor.h>
|
||||
#include <Gui/View3DInventorViewer.h>
|
||||
#include <Gui/Document.h>
|
||||
#include <Mod/Sketcher/App/GeometryFacade.h>
|
||||
#include <Mod/Sketcher/App/SketchObject.h>
|
||||
#include <App/Datums.h>
|
||||
|
||||
#include "EditDatumDialog.h"
|
||||
#include "CommandSketcherTools.h"
|
||||
#include "Utils.h"
|
||||
#include "ViewProviderSketch.h"
|
||||
#include "SketcherSettings.h"
|
||||
#include "ui_InsertDatum.h"
|
||||
|
||||
#include <numeric>
|
||||
|
||||
|
||||
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<App::GeoFeature>()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip datum objects
|
||||
if (object->isDerivedFrom<App::DatumElement>()) {
|
||||
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<int>(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<int>(SketcherGui::AutoScaleMode::Always)
|
||||
|| (autoScaleMode
|
||||
== static_cast<int>(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"
|
||||
|
||||
@@ -65,6 +65,9 @@ private Q_SLOTS:
|
||||
void drivingToggled(bool);
|
||||
void datumChanged();
|
||||
void formEditorOpened(bool);
|
||||
|
||||
private:
|
||||
void performAutoScale(double newDatum);
|
||||
};
|
||||
|
||||
} // namespace SketcherGui
|
||||
|
||||
@@ -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<int>(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
|
||||
|
||||
@@ -128,6 +128,19 @@ private:
|
||||
std::unique_ptr<Ui_SketcherSettingsAppearance> 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
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>500</width>
|
||||
<height>536</height>
|
||||
<height>592</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -250,14 +250,17 @@ Requires to re-enter edit mode to take effect.</string>
|
||||
<string>Dimension constraint</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_general">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="dimensioningLabel">
|
||||
<property name="text">
|
||||
<string>Dimensioning constraints:</string>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="radiusDiameterMode">
|
||||
<property name="toolTip">
|
||||
<string>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.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="dimensioningMode">
|
||||
<property name="toolTip">
|
||||
<string>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
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="radiusDiameterMode">
|
||||
<property name="toolTip">
|
||||
<string>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.</string>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="dimensioningLabel">
|
||||
<property name="text">
|
||||
<string>Dimensioning constraints:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="autoScaleModeLabel">
|
||||
<property name="text">
|
||||
<string>Scale upon first constraint</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="autoScaleMode">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Always</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Never</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Only if there is no visual scale indicator</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -307,7 +333,7 @@ This setting is only for the toolbar. Whichever you choose, all tools are always
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="ovpVisibility">
|
||||
<property name="toolTip">
|
||||
<string>Choose a visibility mode for the On-View-Parameters:
|
||||
|
||||
Reference in New Issue
Block a user