From 6819055c21f7e04ab710c6b50141d2f2a9dacaa6 Mon Sep 17 00:00:00 2001 From: wmayer Date: Thu, 30 Mar 2023 00:07:33 +0200 Subject: [PATCH] Gui: implement interactive image scaling --- src/Gui/TaskView/TaskImageScale.cpp | 239 ++++++++++++++++++++++++++- src/Gui/TaskView/TaskImageScale.h | 48 ++++++ src/Gui/TaskView/TaskImageScale.ui | 91 ++++++++-- src/Gui/TaskView/TaskOrientation.cpp | 2 +- 4 files changed, 364 insertions(+), 16 deletions(-) diff --git a/src/Gui/TaskView/TaskImageScale.cpp b/src/Gui/TaskView/TaskImageScale.cpp index 881d1b66b3..a0734912be 100644 --- a/src/Gui/TaskView/TaskImageScale.cpp +++ b/src/Gui/TaskView/TaskImageScale.cpp @@ -24,13 +24,27 @@ #include "PreCompiled.h" #ifndef _PreComp_ # include +# include # include +# include +# include +# include +# include +# include +# include #endif +#include +#include #include #include +#include #include #include +#include +#include +#include +#include #include #include "TaskImageScale.h" @@ -39,6 +53,8 @@ using namespace Gui; +/* TRANSLATOR Gui::TaskImageScale */ + TaskImageScale::TaskImageScale(Image::ImagePlane* obj, QWidget* parent) : QWidget(parent) , ui(new Ui_TaskImageScale) @@ -46,17 +62,30 @@ TaskImageScale::TaskImageScale(Image::ImagePlane* obj, QWidget* parent) , aspectRatio{1.0} { ui->setupUi(this); + ui->pushButtonCancel->hide(); ui->spinBoxWidth->setValue(obj->getXSizeInPixel()); ui->spinBoxHeight->setValue(obj->getYSizeInPixel()); aspectRatio = obj->XSize.getValue() / obj->YSize.getValue(); - connect(ui->spinBoxWidth, qOverload(&QSpinBox::valueChanged), this, &TaskImageScale::changeWidth); - connect(ui->spinBoxHeight, qOverload(&QSpinBox::valueChanged), this, &TaskImageScale::changeHeight); + connect(ui->spinBoxWidth, qOverload(&QSpinBox::valueChanged), + this, &TaskImageScale::changeWidth); + connect(ui->spinBoxHeight, qOverload(&QSpinBox::valueChanged), + this, &TaskImageScale::changeHeight); + connect(ui->pushButtonScale, &QPushButton::clicked, + this, &TaskImageScale::onInteractiveScale); + connect(ui->pushButtonCancel, &QPushButton::clicked, + this, &TaskImageScale::rejectScale); } TaskImageScale::~TaskImageScale() { + if (scale) { + if (scale->isActive()) { + scale->deactivate(); + } + scale->deleteLater(); + } } void TaskImageScale::changeWidth() @@ -85,4 +114,210 @@ void TaskImageScale::changeHeight() } } +View3DInventorViewer* TaskImageScale::getViewer() const +{ + if (!feature.expired()) { + auto vp = Application::Instance->getViewProvider(feature.get()); + auto doc = static_cast(vp)->getDocument(); + auto view = dynamic_cast(doc->getViewOfViewProvider(vp)); + if (view) { + return view->getViewer(); + } + } + + return nullptr; +} + +void TaskImageScale::selectedPoints(size_t num) +{ + if (num == 1) { + ui->labelInstruction->setText(tr("Select second point")); + } + else if (num == 2) { + ui->labelInstruction->setText(tr("Enter desired distance between the points")); + ui->pushButtonScale->setEnabled(true); + ui->pushButtonScale->setText(tr("Accept")); + ui->pushButtonCancel->show(); + ui->quantitySpinBox->setEnabled(true); + ui->quantitySpinBox->setValue(scale->getDistance()); + } +} + +void TaskImageScale::scaleImage(double factor) +{ + if (!feature.expired()) { + feature->XSize.setValue(feature->XSize.getValue() * factor); + feature->YSize.setValue(feature->YSize.getValue() * factor); + + QSignalBlocker blockW(ui->spinBoxWidth); + ui->spinBoxWidth->setValue(feature->getXSizeInPixel()); + QSignalBlocker blockH(ui->spinBoxHeight); + ui->spinBoxHeight->setValue(feature->getYSizeInPixel()); + } +} + +void TaskImageScale::startScale() +{ + scale->activate(); + ui->labelInstruction->setText(tr("Select two points in the 3d view")); + ui->pushButtonScale->setEnabled(false); + ui->pushButtonCancel->hide(); + ui->quantitySpinBox->setEnabled(false); +} + +void TaskImageScale::acceptScale() +{ + scaleImage(ui->quantitySpinBox->value().getValue() / scale->getDistance()); + rejectScale(); +} + +void TaskImageScale::rejectScale() +{ + scale->deactivate(); + ui->labelInstruction->clear(); + ui->pushButtonScale->setText(tr("Interactive")); + ui->pushButtonCancel->hide(); + ui->quantitySpinBox->setEnabled(false); + + scale->clearPoints(); +} + +void TaskImageScale::onInteractiveScale() +{ + if (!feature.expired() && !scale) { + View3DInventorViewer* viewer = getViewer(); + if (viewer) { + scale = new InteractiveScale(viewer); + connect(scale, &InteractiveScale::selectedPoints, + this, &TaskImageScale::selectedPoints); + } + } + + if (scale) { + if (scale->isActive()) { + acceptScale(); + } + else { + startScale(); + } + } +} + +// ---------------------------------------------------------------------------- + +InteractiveScale::InteractiveScale(View3DInventorViewer* view) + : active{false} + , viewer{view} +{ + coords = new SoCoordinate3; + coords->ref(); + root = new SoSeparator; + root->ref(); + + root->addChild(coords); + + SoBaseColor* color = new SoBaseColor; + color->rgb.setValue(1.0F, 0.0F, 0.0F); + root->addChild(color); + root->addChild(new SoLineSet); +} + +InteractiveScale::~InteractiveScale() +{ + coords->unref(); + root->unref(); +} + +void InteractiveScale::activate() +{ + if (viewer) { + static_cast(viewer->getSceneGraph())->addChild(root); + viewer->setEditing(true); + viewer->addEventCallback(SoLocation2Event::getClassTypeId(), InteractiveScale::getMousePosition, this); + viewer->addEventCallback(SoMouseButtonEvent::getClassTypeId(), InteractiveScale::getMouseClick, this); + viewer->setSelectionEnabled(false); + viewer->getWidget()->setCursor(QCursor(Qt::CrossCursor)); + active = true; + } +} + +void InteractiveScale::deactivate() +{ + if (viewer) { + static_cast(viewer->getSceneGraph())->removeChild(root); + viewer->setEditing(false); + viewer->removeEventCallback(SoLocation2Event::getClassTypeId(), InteractiveScale::getMousePosition, this); + viewer->removeEventCallback(SoMouseButtonEvent::getClassTypeId(), InteractiveScale::getMouseClick, this); + viewer->setSelectionEnabled(true); + viewer->getWidget()->setCursor(QCursor(Qt::ArrowCursor)); + active = false; + } +} + +double InteractiveScale::getDistance() const +{ + if (points.size() < 2) + return 0.0; + + return (points[0] - points[1]).length(); +} + +double InteractiveScale::getDistance(const SbVec3f& pt) const +{ + if (points.empty()) + return 0.0; + + return (points[0] - pt).length(); +} + +void InteractiveScale::clearPoints() +{ + points.clear(); + coords->point.setNum(0); +} + +void InteractiveScale::getMouseClick(void * ud, SoEventCallback * ecb) +{ + InteractiveScale* scale = static_cast(ud); + const SoMouseButtonEvent * mbe = static_cast(ecb->getEvent()); + Gui::View3DInventorViewer* view = static_cast(ecb->getUserData()); + + if (mbe->getButton() == SoMouseButtonEvent::BUTTON1 && mbe->getState() == SoButtonEvent::DOWN) { + ecb->setHandled(); + auto pos2d = mbe->getPosition(); + auto pos3d = view->getPointOnFocalPlane(pos2d); + + if (scale->points.empty()) { + scale->points.push_back(pos3d); + scale->coords->point.set1Value(0, pos3d); + } + else if (scale->points.size() == 1) { + double distance = scale->getDistance(pos3d); + if (distance > Base::Precision::Confusion()) { + scale->points.push_back(pos3d); + scale->coords->point.set1Value(1, pos3d); + } + else { + Base::Console().Warning(std::string("Image scale"), "The second point is too close. Retry!\n"); + } + } + + Q_EMIT scale->selectedPoints(scale->points.size()); + } +} + +void InteractiveScale::getMousePosition(void * ud, SoEventCallback * ecb) +{ + InteractiveScale* scale = static_cast(ud); + const SoLocation2Event * l2e = static_cast(ecb->getEvent()); + Gui::View3DInventorViewer* view = static_cast(ecb->getUserData()); + + if (scale->points.size() == 1) { + ecb->setHandled(); + auto pos2d = l2e->getPosition(); + auto pos3d = view->getPointOnFocalPlane(pos2d); + scale->coords->point.set1Value(1, pos3d); + } +} + #include "moc_TaskImageScale.cpp" diff --git a/src/Gui/TaskView/TaskImageScale.h b/src/Gui/TaskView/TaskImageScale.h index 198e7d9ad3..657e83e5cd 100644 --- a/src/Gui/TaskView/TaskImageScale.h +++ b/src/Gui/TaskView/TaskImageScale.h @@ -24,13 +24,53 @@ #ifndef GUI_TASKIMAGESCALE_H #define GUI_TASKIMAGESCALE_H +#include #include #include #include #include +#include + +class SbVec3f; +class SoEventCallback; +class SoCoordinate3; +class SoSeparator; +class SoLineSet; namespace Gui { +class View3DInventorViewer; +class InteractiveScale : public QObject +{ + Q_OBJECT + +public: + explicit InteractiveScale(View3DInventorViewer* view); + ~InteractiveScale(); + void activate(); + void deactivate(); + bool isActive() const { + return active; + } + double getDistance() const; + double getDistance(const SbVec3f&) const; + void clearPoints(); + +private: + static void getMouseClick(void * ud, SoEventCallback * n); + static void getMousePosition(void * ud, SoEventCallback * n); + +Q_SIGNALS: + void selectedPoints(size_t); + +private: + bool active; + SoCoordinate3* coords; + SoSeparator* root; + QPointer viewer; + std::vector points; +}; + class Ui_TaskImageScale; class TaskImageScale : public QWidget { @@ -43,9 +83,17 @@ public: private: void changeWidth(); void changeHeight(); + void onInteractiveScale(); + View3DInventorViewer* getViewer() const; + void selectedPoints(size_t num); + void scaleImage(double); + void startScale(); + void acceptScale(); + void rejectScale(); private: std::unique_ptr ui; + QPointer scale; App::WeakPtrT feature; double aspectRatio; }; diff --git a/src/Gui/TaskView/TaskImageScale.ui b/src/Gui/TaskView/TaskImageScale.ui index 314e558bef..8427bf1bb8 100644 --- a/src/Gui/TaskView/TaskImageScale.ui +++ b/src/Gui/TaskView/TaskImageScale.ui @@ -6,14 +6,14 @@ 0 0 - 277 - 178 + 293 + 266 Scale image - + @@ -78,20 +78,85 @@ - - - Qt::Vertical + + + - - - 20 - 18 - - - + + + + + + + Interactively scale the image + + + Interactive + + + + + + + Cancel + + + + + + + Qt::Horizontal + + + + 136 + 20 + + + + + + + + + + + + Desired distance: + + + + + + + false + + + mm + + + + + + + + + + + + + + + + + Gui::QuantitySpinBox + QWidget +
Gui/QuantitySpinBox.h
+
+
diff --git a/src/Gui/TaskView/TaskOrientation.cpp b/src/Gui/TaskView/TaskOrientation.cpp index 62577899a8..df9be47826 100644 --- a/src/Gui/TaskView/TaskOrientation.cpp +++ b/src/Gui/TaskView/TaskOrientation.cpp @@ -62,7 +62,7 @@ void TaskOrientation::open() { if (!feature.expired()) { App::Document* doc = feature->getDocument(); - doc->openTransaction(QT_TRANSLATE_NOOP("Command", "Change orientation")); + doc->openTransaction(QT_TRANSLATE_NOOP("Command", "Edit image")); restore(feature->Placement.getValue()); } }