diff --git a/src/Gui/CMakeLists.txt b/src/Gui/CMakeLists.txt index 91ebaa3c9d..9a5342297a 100644 --- a/src/Gui/CMakeLists.txt +++ b/src/Gui/CMakeLists.txt @@ -345,6 +345,7 @@ SET(Gui_UIC_SRCS Placement.ui TextureMapping.ui TaskView/TaskAppearance.ui + TaskView/TaskOrientation.ui TaskView/TaskSelectLinkProperty.ui TaskElementColors.ui DlgObjectSelection.ui @@ -726,6 +727,9 @@ SET(Task_View_SRCS TaskView/TaskAppearance.cpp TaskView/TaskAppearance.h TaskView/TaskAppearance.ui + TaskView/TaskOrientation.cpp + TaskView/TaskOrientation.h + TaskView/TaskOrientation.ui TaskView/TaskSelectLinkProperty.cpp TaskView/TaskSelectLinkProperty.h TaskView/TaskSelectLinkProperty.ui @@ -794,6 +798,7 @@ SOURCE_GROUP("Widget\\QSintActionPanel\\Mocs" FILES ${qsint_MOC_SRCS}) # The 3d view SET(View3D_CPP_SRCS + Camera.cpp Flag.cpp GLBuffer.cpp GLPainter.cpp @@ -824,6 +829,7 @@ SET(View3D_CPP_SRCS ) SET(View3D_SRCS ${View3D_CPP_SRCS} + Camera.h Flag.h GLBuffer.h GLPainter.h @@ -1233,6 +1239,7 @@ if(MSVC) propertyeditor/PropertyItemDelegate.cpp propertyeditor/PropertyModel.cpp TaskView/TaskAppearance.cpp + TaskView/TaskOrientation.cpp TaskView/TaskSelectLinkProperty.cpp TaskView/TaskEditControl.cpp TaskView/TaskView.cpp diff --git a/src/Gui/Camera.cpp b/src/Gui/Camera.cpp index b8df02bc26..1084da3e4f 100644 --- a/src/Gui/Camera.cpp +++ b/src/Gui/Camera.cpp @@ -24,6 +24,7 @@ #include "PreCompiled.h" #include "Camera.h" +#include "Utilities.h" using namespace Gui; @@ -92,51 +93,109 @@ vz.z=0 https://de.wikipedia.org/wiki/Arkussinus_und_Arkuskosinus */ +SbRotation Camera::top() +{ + return SbRotation(0, 0, 0, 1); +} + +SbRotation Camera::bottom() +{ + return SbRotation(1, 0, 0, 0); +} + +SbRotation Camera::front() +{ + auto root = (float)(sqrt(2.0)/2.0); + return SbRotation(root, 0, 0, root); +} + +SbRotation Camera::rear() +{ + auto root = (float)(sqrt(2.0)/2.0); + return SbRotation(0, root, root, 0); +} + +SbRotation Camera::right() +{ + return SbRotation(0.5, 0.5, 0.5, 0.5); +} + +SbRotation Camera::left() +{ + return SbRotation(-0.5, 0.5, 0.5, -0.5); +} + +SbRotation Camera::isometric() +{ + //from math import sqrt, degrees, asin + //p1=App.Rotation(App.Vector(1,0,0),45) + //p2=App.Rotation(App.Vector(0,0,1),-45) + //p3=p2.multiply(p1) + //return SbRotation(0.353553f, -0.146447f, -0.353553f, 0.853553f); + + //from math import sqrt, degrees, asin + //p1=App.Rotation(App.Vector(1,0,0),90) + //p2=App.Rotation(App.Vector(0,0,1),135) + //p3=App.Rotation(App.Vector(-1,1,0),degrees(asin(-sqrt(1.0/3.0)))) + //p4=p3.multiply(p2).multiply(p1) + //return SbRotation(0.17592, 0.424708, 0.820473, 0.339851); + + //from math import sqrt, degrees, asin + //p1=App.Rotation(App.Vector(1,0,0),90) + //p2=App.Rotation(App.Vector(0,0,1),45) + //#p3=App.Rotation(App.Vector(1,1,0),45) + //p3=App.Rotation(App.Vector(1,1,0),degrees(asin(-sqrt(1.0/3.0)))) + //p4=p3.multiply(p2).multiply(p1) + return SbRotation(0.424708f, 0.17592f, 0.339851f, 0.820473f); +} + +SbRotation Camera::dimetric() +{ + return SbRotation(0.567952f, 0.103751f, 0.146726f, 0.803205f); +} + +SbRotation Camera::trimetric() +{ + return SbRotation(0.446015f, 0.119509f, 0.229575f, 0.856787f); +} + SbRotation Camera::rotation(Camera::Orientation view) { switch (view) { case Top: - return SbRotation(0, 0, 0, 1); + return top(); case Bottom: - return SbRotation(1, 0, 0, 0); - case Front: { - auto root = (float)(sqrt(2.0)/2.0); - return SbRotation(root, 0, 0, root); - } - case Rear: { - auto root = (float)(sqrt(2.0)/2.0); - return SbRotation(0, root, root, 0); - } + return bottom(); + case Front: + return front(); + case Rear: + return rear(); case Right: - return SbRotation(0.5, 0.5, 0.5, 0.5); + return right(); case Left: - return SbRotation(-0.5, 0.5, 0.5, -0.5); + return left(); case Isometric: - //from math import sqrt, degrees, asin - //p1=App.Rotation(App.Vector(1,0,0),45) - //p2=App.Rotation(App.Vector(0,0,1),-45) - //p3=p2.multiply(p1) - //return SbRotation(0.353553f, -0.146447f, -0.353553f, 0.853553f); - - //from math import sqrt, degrees, asin - //p1=App.Rotation(App.Vector(1,0,0),90) - //p2=App.Rotation(App.Vector(0,0,1),135) - //p3=App.Rotation(App.Vector(-1,1,0),degrees(asin(-sqrt(1.0/3.0)))) - //p4=p3.multiply(p2).multiply(p1) - //return SbRotation(0.17592, 0.424708, 0.820473, 0.339851); - - //from math import sqrt, degrees, asin - //p1=App.Rotation(App.Vector(1,0,0),90) - //p2=App.Rotation(App.Vector(0,0,1),45) - //#p3=App.Rotation(App.Vector(1,1,0),45) - //p3=App.Rotation(App.Vector(1,1,0),degrees(asin(-sqrt(1.0/3.0)))) - //p4=p3.multiply(p2).multiply(p1) - return SbRotation(0.424708f, 0.17592f, 0.339851f, 0.820473f); + return isometric(); case Dimetric: - return SbRotation(0.567952f, 0.103751f, 0.146726f, 0.803205f); + return dimetric(); case Trimetric: - return SbRotation(0.446015f, 0.119509f, 0.229575f, 0.856787f); + return trimetric(); default: - return SbRotation(0, 0, 0, 1); + return top(); } } + +Base::Rotation Camera::convert(Camera::Orientation view) +{ + return convert(Camera::rotation(view)); +} + +Base::Rotation Camera::convert(const SbRotation& rot) +{ + return Base::convertTo(rot); +} + +SbRotation Camera::convert(const Base::Rotation& rot) +{ + return Base::convertTo(rot); +} diff --git a/src/Gui/Camera.h b/src/Gui/Camera.h index 027a5791ef..9e7a733d56 100644 --- a/src/Gui/Camera.h +++ b/src/Gui/Camera.h @@ -45,7 +45,20 @@ public: Trimetric, }; + static SbRotation top(); + static SbRotation bottom(); + static SbRotation front(); + static SbRotation rear(); + static SbRotation right(); + static SbRotation left(); + static SbRotation isometric(); + static SbRotation dimetric(); + static SbRotation trimetric(); + static SbRotation rotation(Orientation view); + static Base::Rotation convert(Orientation view); + static Base::Rotation convert(const SbRotation&); + static SbRotation convert(const Base::Rotation&); }; } diff --git a/src/Gui/TaskView/TaskOrientation.cpp b/src/Gui/TaskView/TaskOrientation.cpp new file mode 100644 index 0000000000..fe80f48935 --- /dev/null +++ b/src/Gui/TaskView/TaskOrientation.cpp @@ -0,0 +1,227 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later + +/*************************************************************************** + * Copyright (c) 2023 Werner Mayer * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + +#include "PreCompiled.h" +#ifndef _PreComp_ +# include +# include +#endif + +#include +#include +#include +#include +#include + +#include "TaskOrientation.h" +#include "ui_TaskOrientation.h" + + +using namespace Gui; + +TaskOrientation::TaskOrientation(App::GeoFeature* obj, QWidget* parent) + : QWidget(parent) + , ui(new Ui_TaskOrientation) + , feature(obj) +{ + ui->setupUi(this); + + connect(ui->Reverse_checkBox, &QCheckBox::clicked, this, &TaskOrientation::onPreview); + connect(ui->XY_radioButton , &QRadioButton::clicked, this, &TaskOrientation::onPreview); + connect(ui->XZ_radioButton , &QRadioButton::clicked, this, &TaskOrientation::onPreview); + connect(ui->YZ_radioButton , &QRadioButton::clicked, this, &TaskOrientation::onPreview); + connect(ui->Offset_doubleSpinBox, qOverload(&QuantitySpinBox::valueChanged), + this, &TaskOrientation::onPreview); +} + +TaskOrientation::~TaskOrientation() +{ +} + +void TaskOrientation::open() +{ + if (!feature.expired()) { + App::Document* doc = feature->getDocument(); + doc->openTransaction(QT_TRANSLATE_NOOP("Command", "Change orientation")); + restore(feature->Placement.getValue()); + } +} + +void TaskOrientation::accept() +{ + if (!feature.expired()) { + App::Document* doc = feature->getDocument(); + doc->commitTransaction(); + } +} + +void TaskOrientation::reject() +{ + if (!feature.expired()) { + App::Document* doc = feature->getDocument(); + doc->abortTransaction(); + } +} + +void TaskOrientation::onPreview() +{ + updateIcon(); + updatePlacement(); +} + +void TaskOrientation::restore(const Base::Placement& plm) +{ + auto isReversed = [](Camera::Orientation type) { + return (type == Camera::Bottom) || + (type == Camera::Rear) || + (type == Camera::Left); + }; + std::map rotations { + {Camera::Top, Camera::convert(Camera::Top)}, + {Camera::Bottom, Camera::convert(Camera::Bottom)}, + {Camera::Front, Camera::convert(Camera::Front)}, + {Camera::Rear, Camera::convert(Camera::Rear)}, + {Camera::Right, Camera::convert(Camera::Right)}, + {Camera::Left, Camera::convert(Camera::Left)} + }; + + Base::Rotation rot = plm.getRotation(); + Base::Vector3d pos = plm.getPosition(); + + double prec = 1.0e-5; + for (const auto& it : rotations) { + if (rot.isSame(it.second, prec)) { + if (it.first == Camera::Top || + it.first == Camera::Bottom) { + ui->XY_radioButton->setChecked(true); + ui->Offset_doubleSpinBox->setValue(pos.z); + } + else if (it.first == Camera::Front || + it.first == Camera::Rear) { + ui->XZ_radioButton->setChecked(true); + ui->Offset_doubleSpinBox->setValue(pos.y); + } + else if (it.first == Camera::Right || + it.first == Camera::Left) { + ui->YZ_radioButton->setChecked(true); + ui->Offset_doubleSpinBox->setValue(pos.x); + } + + if (isReversed(it.first)) { + ui->Reverse_checkBox->setChecked(true); + } + } + } + + onPreview(); +} + +void TaskOrientation::updatePlacement() +{ + Base::Placement Pos; + double offset = ui->Offset_doubleSpinBox->value().getValue(); + bool reverse = ui->Reverse_checkBox->isChecked(); + if (ui->XY_radioButton->isChecked()) { + if (!reverse) { + Pos = Base::Placement(Base::Vector3d(0, 0, offset), + Camera::convert(Camera::Top)); + } + else { + Pos = Base::Placement(Base::Vector3d(0, 0, offset), + Camera::convert(Camera::Bottom)); + } + } + else if (ui->XZ_radioButton->isChecked()) { + if (!reverse) { + Pos = Base::Placement(Base::Vector3d(0, offset, 0), + Camera::convert(Camera::Front)); + } + else { + Pos = Base::Placement(Base::Vector3d(0, offset, 0), + Camera::convert(Camera::Rear)); + } + } + else if (ui->YZ_radioButton->isChecked()) { + if (!reverse) { + Pos = Base::Placement(Base::Vector3d(offset, 0, 0), + Camera::convert(Camera::Right)); + } + else { + Pos = Base::Placement(Base::Vector3d(offset, 0, 0), + Camera::convert(Camera::Left)); + } + } + + if (!feature.expired()) { + feature->Placement.setValue(Pos); + } +} + +void TaskOrientation::updateIcon() +{ + std::string icon; + bool reverse = ui->Reverse_checkBox->isChecked(); + if (ui->XY_radioButton->isChecked()) { + icon = reverse ? "view-bottom" : "view-top"; + } + else if (ui->XZ_radioButton->isChecked()) { + icon = reverse ? "view-rear" : "view-front"; + } + else if (ui->YZ_radioButton->isChecked()) { + icon = reverse ? "view-left" : "view-right"; + } + + ui->previewLabel->setPixmap( + Gui::BitmapFactory().pixmapFromSvg(icon.c_str(), + ui->previewLabel->size())); +} + +// ---------------------------------------------------------------------------- + +TaskOrientationDialog::TaskOrientationDialog(App::GeoFeature* obj) +{ + widget = new TaskOrientation(obj); + Gui::TaskView::TaskBox* taskbox = new Gui::TaskView::TaskBox( + QPixmap(), widget->windowTitle(), true, nullptr); + taskbox->groupLayout()->addWidget(widget); + Content.push_back(taskbox); +} + +void TaskOrientationDialog::open() +{ + widget->open(); +} + +bool TaskOrientationDialog::accept() +{ + widget->accept(); + return true; +} + +bool TaskOrientationDialog::reject() +{ + widget->reject(); + return true; +} + +#include "moc_TaskOrientation.cpp" diff --git a/src/Gui/TaskView/TaskOrientation.h b/src/Gui/TaskView/TaskOrientation.h new file mode 100644 index 0000000000..ac25492372 --- /dev/null +++ b/src/Gui/TaskView/TaskOrientation.h @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later + +/*************************************************************************** + * Copyright (c) 2023 Werner Mayer * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + +#ifndef GUI_TASKORIENTATION_H +#define GUI_TASKORIENTATION_H + +#include +#include +#include +#include + +namespace Gui { + +class Ui_TaskOrientation; +class TaskOrientation : public QWidget +{ + Q_OBJECT + +public: + explicit TaskOrientation(App::GeoFeature* obj, QWidget* parent = nullptr); + ~TaskOrientation() override; + + void open(); + void accept(); + void reject(); + +private: + void restore(const Base::Placement&); + void onPreview(); + void updateIcon(); + void updatePlacement(); + +private: + std::unique_ptr ui; + App::WeakPtrT feature; +}; + +class TaskOrientationDialog : public Gui::TaskView::TaskDialog +{ + Q_OBJECT + +public: + explicit TaskOrientationDialog(App::GeoFeature* obj); + +public: + void open() override; + bool accept() override; + bool reject() override; + + QDialogButtonBox::StandardButtons getStandardButtons() const override { + return QDialogButtonBox::Ok | QDialogButtonBox::Cancel; + } + +private: + TaskOrientation* widget; +}; + +} + +#endif // GUI_TASKORIENTATION_H diff --git a/src/Gui/TaskView/TaskOrientation.ui b/src/Gui/TaskView/TaskOrientation.ui new file mode 100644 index 0000000000..f09dc30e07 --- /dev/null +++ b/src/Gui/TaskView/TaskOrientation.ui @@ -0,0 +1,114 @@ + + + Gui::TaskOrientation + + + + 0 + 0 + 194 + 200 + + + + Choose orientation + + + + + + Planes + + + + + + XY-Plane + + + true + + + + + + + XZ-Plane + + + + + + + YZ-Plane + + + + + + + + + + Reverse direction + + + + + + + + 48 + 48 + + + + + 48 + 48 + + + + Preview + + + + + + + + + Offset: + + + + + + + mm + + + -999999999.000000000000000 + + + 999999999.000000000000000 + + + 1.000000000000000 + + + + + + + + + + Gui::QuantitySpinBox + QWidget +
Gui/QuantitySpinBox.h
+
+
+ + +
diff --git a/src/Gui/ViewProviderImagePlane.cpp b/src/Gui/ViewProviderImagePlane.cpp index 34aebcca7c..f6f427a396 100644 --- a/src/Gui/ViewProviderImagePlane.cpp +++ b/src/Gui/ViewProviderImagePlane.cpp @@ -23,20 +23,25 @@ #include "PreCompiled.h" #ifndef _PreComp_ # include +# include # include # include +# include # include - # include # include # include # include +# include # include # include #endif #include +#include #include +#include +#include #include #include "ViewProviderImagePlane.h" @@ -46,15 +51,24 @@ using namespace Gui; PROPERTY_SOURCE(Gui::ViewProviderImagePlane, Gui::ViewProviderGeometryObject) +const char* ViewProviderImagePlane::LightingEnums[]= {"One side", "Two side", nullptr}; ViewProviderImagePlane::ViewProviderImagePlane() { + ADD_PROPERTY_TYPE(Lighting,(1L), "Object Style", App::Prop_None, "Set object lighting."); + Lighting.setEnums(LightingEnums); + texture = new SoTexture2; texture->ref(); pcCoords = new SoCoordinate3(); pcCoords->ref(); + shapeHints = new SoShapeHints; + shapeHints->shapeType = SoShapeHints::UNKNOWN_SHAPE_TYPE; + shapeHints->vertexOrdering = SoShapeHints::COUNTERCLOCKWISE; + shapeHints->ref(); + sPixmap = "image-plane"; } @@ -62,6 +76,7 @@ ViewProviderImagePlane::~ViewProviderImagePlane() { pcCoords->unref(); texture->unref(); + shapeHints->unref(); } void ViewProviderImagePlane::attach(App::DocumentObject *pcObj) @@ -85,6 +100,7 @@ void ViewProviderImagePlane::attach(App::DocumentObject *pcObj) texture->model = SoTexture2::MODULATE; planesep->addChild(texture); + planesep->addChild(shapeHints); planesep->addChild(pcShapeMaterial); // plane @@ -113,6 +129,42 @@ std::vector ViewProviderImagePlane::getDisplayModes() const return StrList; } +void ViewProviderImagePlane::onChanged(const App::Property* prop) +{ + if (prop == &Lighting) { + if (Lighting.getValue() == 0) + shapeHints->vertexOrdering = SoShapeHints::UNKNOWN_ORDERING; + else + shapeHints->vertexOrdering = SoShapeHints::COUNTERCLOCKWISE; + } + ViewProviderGeometryObject::onChanged(prop); +} + +void ViewProviderImagePlane::setupContextMenu(QMenu* menu, QObject* receiver, const char* member) +{ + ViewProviderGeometryObject::setupContextMenu(menu, receiver, member); + + Gui::ActionFunction* func = new Gui::ActionFunction(menu); + QAction* orient = menu->addAction(QObject::tr("Change orientation...")); + func->trigger(orient, std::bind(&ViewProviderImagePlane::changeOrientation, this)); + + QAction* scale = menu->addAction(QObject::tr("Scale image...")); + scale->setIcon(QIcon(QLatin1String("images:image-scaling.svg"))); + func->trigger(scale, std::bind(&ViewProviderImagePlane::scaleImage, this)); +} + +void ViewProviderImagePlane::changeOrientation() +{ + Gui::Control().showDialog(new TaskOrientationDialog( + dynamic_cast(getObject()) + )); +} + +void ViewProviderImagePlane::scaleImage() +{ + +} + bool ViewProviderImagePlane::loadSvg(const char* filename, float x, float y, QImage& img) { QFileInfo fi(QString::fromUtf8(filename)); diff --git a/src/Gui/ViewProviderImagePlane.h b/src/Gui/ViewProviderImagePlane.h index 12b5119bba..7228431386 100644 --- a/src/Gui/ViewProviderImagePlane.h +++ b/src/Gui/ViewProviderImagePlane.h @@ -28,6 +28,7 @@ class SoCoordinate3; class SoDrawStyle; +class SoShapeHints; class SoTexture2; class QImage; @@ -39,24 +40,29 @@ class GuiExport ViewProviderImagePlane : public Gui::ViewProviderGeometryObject PROPERTY_HEADER_WITH_OVERRIDE(Gui::ViewProviderImagePlane); public: - /// constructor. ViewProviderImagePlane(); - - /// destructor. ~ViewProviderImagePlane() override; + App::PropertyEnumeration Lighting; + void attach(App::DocumentObject *pcObject) override; void setDisplayMode(const char* ModeName) override; std::vector getDisplayModes() const override; void updateData(const App::Property*) override; + void setupContextMenu(QMenu*, QObject*, const char*) override; + void onChanged(const App::Property* prop) override; private: bool loadSvg(const char*, float x, float y, QImage& img); + void changeOrientation(); + void scaleImage(); -protected: +private: SoCoordinate3 * pcCoords; SoTexture2 * texture; - }; + SoShapeHints * shapeHints; + static const char * LightingEnums[]; +}; } //namespace Gui