From 878dd55ed03268df150a33ca0ddb9a8b679a0dff Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Sat, 4 Jan 2025 13:08:40 +0100 Subject: [PATCH 01/19] ServiceProvider: Better naming of methods This commit renames methods of ServiceProvider to be easier to understand. It also replaces the misleading singleton with more correct here global instance of class. --- src/Base/ServiceProvider.cpp | 5 +- src/Base/ServiceProvider.h | 79 ++++++++++++++++++++++++++---- tests/src/Base/ServiceProvider.cpp | 10 ++-- 3 files changed, 77 insertions(+), 17 deletions(-) diff --git a/src/Base/ServiceProvider.cpp b/src/Base/ServiceProvider.cpp index 1f99a53100..7401c3e9cf 100644 --- a/src/Base/ServiceProvider.cpp +++ b/src/Base/ServiceProvider.cpp @@ -26,8 +26,7 @@ #include "ServiceProvider.h" -Base::ServiceProvider& Base::ServiceProvider::get() +namespace Base { - static Base::ServiceProvider instance; - return instance; +Base::ServiceProvider globalServiceProvider; } diff --git a/src/Base/ServiceProvider.h b/src/Base/ServiceProvider.h index 738c56d172..10aad1032f 100644 --- a/src/Base/ServiceProvider.h +++ b/src/Base/ServiceProvider.h @@ -36,6 +36,49 @@ namespace Base { +/** + * Class that implements basic service container that can be used to obtain different implementation + * of various services. + * + * Primary use of such container is to provide ability to define global services that can be + * implemented within non-core modules. This for example allows to use code that is available only + * in Part module from Base with the only requirement being that Part implements specific interface + * and registers the service within service provider. + * + * For ease of use global service provider instance is provided with convenience functions: + * - Base::provideService + * - Base::provideServiceImplementations + * - Base::registerServiceImplementation + * + * As the example, we can define service that provides placement of sub objects in App: + * @code + * class SubObjectPlacementProvider + * { + * public: + * virtual Base::Placement calculate(SubObjectT object, Base::Placement basePlacement) const = + * 0; + * }; + * @endcode + * + * App does not know how to implement this service, but it can be implemented within Part module: + * @code + * class AttacherSubObjectPlacement final: public App::SubObjectPlacementProvider { ... } + * + * // later in module initialization method + * + * Base::registerServiceImplementation(new + * AttacherSubObjectPlacement); + * @endcode + * + * This service can then be obtained inside other modules, without them being aware of the + * implementation - only the interface: + * + * @code + * auto subObjectPlacementProvider = Base::provideService(); + * @endcode + * + * This function can (and should) be used as default for constructor injection of services. + */ class BaseExport ServiceProvider { struct ServiceDescriptor @@ -106,35 +149,53 @@ public: * @tparam T Service interface */ template - void implement(T* contract) + void registerImplementation(T* contract) { ServiceDescriptor descriptor {typeid(T).name(), contract}; _implementations[typeid(T).name()].push_front(descriptor); } - static ServiceProvider& get(); - private: std::map> _implementations; }; +BaseExport extern ServiceProvider globalServiceProvider; + +/** + * Obtains primary implementation of requested service from the global service provider. + * + * @tparam T Service kind to obtain. + * @return Primary implementation of the service or nullptr if there is no implementation available. + */ template -T* provideImplementation() +T* provideService() { - return ServiceProvider::get().provide(); + return globalServiceProvider.provide(); } +/** + * Obtains all available implementations of requested service in the global service provider. + * + * @tparam T Service kind to obtain. + * @return List of available service implementation. + */ template -std::list provideAllImplementations() +std::list provideServiceImplementations() { - return ServiceProvider::get().all(); + return globalServiceProvider.all(); } +/** + * Registers implementation of service in the global service provider. + * + * @tparam T Service kind to obtain. + * @return List of available service implementation. + */ template -void implementContract(T* implementation) +void registerServiceImplementation(T* implementation) { - ServiceProvider::get().implement(implementation); + globalServiceProvider.registerImplementation(implementation); } } // namespace Base diff --git a/tests/src/Base/ServiceProvider.cpp b/tests/src/Base/ServiceProvider.cpp index 405c8cf134..0d6b41a773 100644 --- a/tests/src/Base/ServiceProvider.cpp +++ b/tests/src/Base/ServiceProvider.cpp @@ -62,7 +62,7 @@ TEST(ServiceProvider, provideImplementation) // Arrange Base::ServiceProvider serviceProvider; - serviceProvider.implement(new FirstServiceImplementation); + serviceProvider.registerImplementation(new FirstServiceImplementation); // Act auto implementation = serviceProvider.provide(); @@ -77,8 +77,8 @@ TEST(ServiceProvider, provideLatestImplementation) // Arrange Base::ServiceProvider serviceProvider; - serviceProvider.implement(new FirstServiceImplementation); - serviceProvider.implement(new SecondServiceImplementation); + serviceProvider.registerImplementation(new FirstServiceImplementation); + serviceProvider.registerImplementation(new SecondServiceImplementation); // Act auto implementation = serviceProvider.provide(); @@ -93,8 +93,8 @@ TEST(ServiceProvider, provideAllImplementations) // Arrange Base::ServiceProvider serviceProvider; - serviceProvider.implement(new FirstServiceImplementation); - serviceProvider.implement(new SecondServiceImplementation); + serviceProvider.registerImplementation(new FirstServiceImplementation); + serviceProvider.registerImplementation(new SecondServiceImplementation); // Act auto implementations = serviceProvider.all(); From 23bdfeb6720272614119b57c52cdc817b3527d60 Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Fri, 27 Dec 2024 22:23:14 +0100 Subject: [PATCH 02/19] ServiceProvider: Use std::string as key for services map This fixes potantial crash that can happen on certain platforms like macOS due to `typeid(T).name()` returning different pointers to string which does not happen on all platforms. --- src/Base/ServiceProvider.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Base/ServiceProvider.h b/src/Base/ServiceProvider.h index 10aad1032f..8746de7baa 100644 --- a/src/Base/ServiceProvider.h +++ b/src/Base/ServiceProvider.h @@ -157,7 +157,7 @@ public: } private: - std::map> _implementations; + std::map> _implementations; }; BaseExport extern ServiceProvider globalServiceProvider; From 49ff7b8bf0e98bee47f53654700ad200fc55a019 Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Mon, 11 Nov 2024 17:57:19 +0100 Subject: [PATCH 03/19] Part: Use TopoShape instead of TopoDS_Shape This changes Attacher to use more our own TopoShape class which is more information rich. --- src/Mod/Part/App/Attacher.cpp | 119 ++++++++++++++-------------- src/Mod/Part/App/Attacher.h | 6 +- tests/src/Mod/Part/App/Attacher.cpp | 4 +- 3 files changed, 65 insertions(+), 64 deletions(-) diff --git a/src/Mod/Part/App/Attacher.cpp b/src/Mod/Part/App/Attacher.cpp index f3800133e6..77f389bc7c 100644 --- a/src/Mod/Part/App/Attacher.cpp +++ b/src/Mod/Part/App/Attacher.cpp @@ -388,8 +388,8 @@ void AttachEngine::suggestMapModes(SuggestResult &result) const result.message = SuggestResult::srLinkBroken; result.bestFitMode = mmDeactivated; - std::vector shapes; - std::vector shapeStorage; + std::vector shapes; + std::vector shapeStorage; std::vector typeStr; try{ readLinks(getRefObjects(),subnames, shapes, shapeStorage, typeStr); @@ -578,8 +578,8 @@ eRefType AttachEngine::getShapeType(const App::DocumentObject *obj, const std::s //const_cast is worth here, to keep obj argument const. We are not going to write anything to obj through this temporary link. tmpLink.setValue(const_cast(obj), subshape.c_str()); - std::vector shapes; - std::vector copiedShapeStorage; + std::vector shapes; + std::vector copiedShapeStorage; std::vector types; readLinks(tmpLink.getValues(), tmpLink.getSubValues(), shapes, copiedShapeStorage, types); @@ -740,13 +740,14 @@ eRefType AttachEngine::getRefTypeByName(const std::string& typeName) throw AttachEngineException(errmsg.str()); } -GProp_GProps AttachEngine::getInertialPropsOfShape(const std::vector &shapes) +GProp_GProps AttachEngine::getInertialPropsOfShape(const std::vector &shapes) { //explode compounds TopTools_HSequenceOfShape totalSeq; - for (const TopoDS_Shape* pSh : shapes) { + for (auto tSh : shapes) { + auto pSh = tSh->getShape(); ShapeExtend_Explorer xp; - totalSeq.Append( xp.SeqFromCompound(*pSh, /*recursive=*/true)); + totalSeq.Append( xp.SeqFromCompound(pSh, /*recursive=*/true)); } if (totalSeq.Length() == 0) throw AttachEngineException("AttachEngine::getInertialPropsOfShape: no geometry provided"); @@ -820,8 +821,8 @@ GProp_GProps AttachEngine::getInertialPropsOfShape(const std::vector& objs, const std::vector &subs, - std::vector &shapes, - std::vector &storage, + std::vector &shapes, + std::vector &storage, std::vector &types) { storage.reserve(objs.size()); @@ -837,12 +838,11 @@ void AttachEngine::readLinks(const std::vector& objs, "AttachEngine3D: attached to a non App::GeoFeature '" << objs[i]->getNameInDocument() << "'"); } } - TopoDS_Shape myShape; - + Part::TopoShape shape; try { // getTopoShape support fully qualified subnames and should return shape with correct // global placement. - Part::TopoShape shape = Part::Feature::getTopoShape(objs[i], subs[i].c_str(), true); + shape = Part::Feature::getTopoShape(objs[i], subs[i].c_str(), true); for (;;) { if (shape.isNull()) { FC_THROWM(AttachEngineException, @@ -856,8 +856,6 @@ void AttachEngine::readLinks(const std::vector& objs, // auto extract the single sub-shape from a compound shape = shape.getSubTopoShape(TopAbs_SHAPE, 1); } - - myShape = shape.getShape(); } catch (Standard_Failure& e) { FC_THROWM(AttachEngineException, @@ -871,18 +869,19 @@ void AttachEngine::readLinks(const std::vector& objs, << '.' << subs[i] << std::endl << e.what()); } - if (myShape.IsNull()) { + + if (shape.isNull()) { FC_THROWM(AttachEngineException, "AttachEngine3D: null subshape " << objs[i]->getNameInDocument() << '.' << subs[i]); } - storage.emplace_back(myShape); + storage.emplace_back(shape); shapes[i] = &(storage.back()); // FIXME: unpack single-child compounds here? Compounds are not used so far, so it should be // considered later, when the need arises. - types[i] = getShapeType(*(shapes[i])); + types[i] = getShapeType(shapes[i]->getShape()); if (subs[i].length() == 0) { types[i] = eRefType(types[i] | rtFlagHasPlacement); } @@ -1140,8 +1139,8 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vector shapes; - std::vector copiedShapeStorage; + std::vector shapes; + std::vector copiedShapeStorage; std::vector types; readLinks(objs, subs, shapes, copiedShapeStorage, types); @@ -1172,7 +1171,7 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vectorgetShape(); if (sh.IsNull()) { throw Base::ValueError( "Null shape in AttachEngine3D::calculateAttachedPlacement()!"); @@ -1207,7 +1206,7 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vector 0) { - const TopoDS_Edge& e = TopoDS::Edge(*shapes[0]); + const TopoDS_Edge& e = TopoDS::Edge(shapes[0]->getShape()); BRepAdaptor_Curve adapt(e); gp_Ax3 pos; switch (adapt.GetType()) { @@ -1259,7 +1258,7 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vectorgetShape()); } catch (...) { } @@ -1330,7 +1329,7 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vectorgetShape()); } catch (...) { } @@ -1387,14 +1386,14 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vectorShapeType() == TopAbs_VERTEX) { + if (shapes[0]->shapeType() == TopAbs_VERTEX) { std::swap(shapes[0], shapes[1]); bThruVertex = true; } TopoDS_Face face; try { - face = TopoDS::Face(*(shapes[0])); + face = TopoDS::Face(shapes[0]->getShape()); } catch (...) { } @@ -1405,7 +1404,7 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vectorgetShape()); } catch (...) { } @@ -1472,14 +1471,14 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vectorShapeType() == TopAbs_VERTEX && shapes.size() >= 2) { + if (shapes[0]->shapeType() == TopAbs_VERTEX && shapes.size() >= 2) { std::swap(shapes[0], shapes[1]); bThruVertex = true; } TopoDS_Edge path; try { - path = TopoDS::Edge(*(shapes[0])); + path = TopoDS::Edge(shapes[0]->getShape()); } catch (...) { } @@ -1506,7 +1505,7 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vector= 2) { TopoDS_Vertex vertex; try { - vertex = TopoDS::Vertex(*(shapes[1])); + vertex = TopoDS::Vertex(shapes[1]->getShape()); } catch (...) { } @@ -1633,7 +1632,7 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vector points; for (const auto & shape : shapes) { - const TopoDS_Shape &sh = *shape; + const TopoDS_Shape &sh = shape->getShape(); if (sh.IsNull()) { throw Base::ValueError( "Null shape in AttachEngine3D::calculateAttachedPlacement()!"); @@ -1725,7 +1724,7 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vectorgetShape()); } catch (...) { } @@ -1845,28 +1844,28 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vectorIsNull()) { + if (shapes[0]->isNull()) { THROWM(Base::TypeError, "AttachEngine3D::calculateAttachedPlacement: null shape!") } - if (shapes[0]->ShapeType() != TopAbs_VERTEX) { + if (shapes[0]->shapeType() != TopAbs_VERTEX) { THROWM(Base::TypeError, "AttachEngine3D::calculateAttachedPlacement: first reference must be a " "vertex, it's not") } - SketchBasePoint = BRep_Tool::Pnt(TopoDS::Vertex(*(shapes[0]))); + SketchBasePoint = BRep_Tool::Pnt(TopoDS::Vertex(shapes[0]->getShape())); // read out axes directions for (size_t i = 1; i < 3 && i < shapes.size(); ++i) { - if (shapes[i]->IsNull()) { + if (shapes[i]->isNull()) { THROWM(Base::TypeError, "AttachEngine3D::calculateAttachedPlacement: null shape!") } - if (shapes[i]->ShapeType() == TopAbs_VERTEX) { - gp_Pnt p = BRep_Tool::Pnt(TopoDS::Vertex(*(shapes[i]))); + if (shapes[i]->shapeType() == TopAbs_VERTEX) { + gp_Pnt p = BRep_Tool::Pnt(TopoDS::Vertex(shapes[i]->getShape())); dirs[order[i - 1]] = gp_Vec(SketchBasePoint, p); } - else if (shapes[i]->ShapeType() == TopAbs_EDGE) { - const TopoDS_Edge& e = TopoDS::Edge(*(shapes[i])); + else if (shapes[i]->shapeType() == TopAbs_EDGE) { + const TopoDS_Edge& e = TopoDS::Edge(shapes[i]->getShape()); BRepAdaptor_Curve crv(e); double u1 = crv.FirstParameter(); double u2 = crv.LastParameter(); @@ -2083,8 +2082,8 @@ AttachEngineLine::_calculateAttachedPlacement(const std::vector shapes; - std::vector copiedShapeStorage; + std::vector shapes; + std::vector copiedShapeStorage; std::vector types; readLinks(objs, subs, shapes, copiedShapeStorage, types); @@ -2157,7 +2156,7 @@ AttachEngineLine::_calculateAttachedPlacement(const std::vector points; for (const auto & shape : shapes) { - const TopoDS_Shape &sh = *shape; + const TopoDS_Shape &sh = shape->getShape(); if (sh.IsNull()) { throw Base::ValueError( "Null shape in AttachEngineLine::calculateAttachedPlacement()!"); @@ -2197,13 +2196,13 @@ AttachEngineLine::_calculateAttachedPlacement(const std::vectorIsNull()) { + if (shapes[0]->isNull()) { throw Base::ValueError( "Null shape in AttachEngineLine::calculateAttachedPlacement()!"); } TopoDS_Edge e; try { - e = TopoDS::Edge(*(shapes[0])); + e = TopoDS::Edge(shapes[0]->getShape()); } catch (...) { } @@ -2228,13 +2227,13 @@ AttachEngineLine::_calculateAttachedPlacement(const std::vectorIsNull()) { + if (shapes[0]->isNull()) { throw Base::ValueError( "Null shape in AttachEngineLine::calculateAttachedPlacement()!"); } TopoDS_Edge e; try { - e = TopoDS::Edge(*(shapes[0])); + e = TopoDS::Edge(shapes[0]->getShape()); } catch (...) { } @@ -2283,13 +2282,13 @@ AttachEngineLine::_calculateAttachedPlacement(const std::vectorIsNull() || shapes[1]->IsNull()) { + if (shapes[0]->isNull() || shapes[1]->isNull()) { throw Base::ValueError( "Null shape in AttachEngineLine::calculateAttachedPlacement()!"); } - const TopoDS_Face& face1 = TopoDS::Face(*(shapes[0])); - const TopoDS_Face& face2 = TopoDS::Face(*(shapes[1])); + const TopoDS_Face& face1 = TopoDS::Face(shapes[0]->getShape()); + const TopoDS_Face& face2 = TopoDS::Face(shapes[1]->getShape()); Handle(Geom_Surface) hSurf1 = BRep_Tool::Surface(face1); Handle(Geom_Surface) hSurf2 = BRep_Tool::Surface(face2); @@ -2324,15 +2323,15 @@ AttachEngineLine::_calculateAttachedPlacement(const std::vectorIsNull()) { + if (shapes[0]->isNull()) { throw Base::ValueError( "Null shape in AttachEngineLine::calculateAttachedPlacement()!"); } - if (shapes[1]->IsNull()) { + if (shapes[1]->isNull()) { throw Base::ValueError( "Null shape in AttachEngineLine::calculateAttachedPlacement()!"); } - BRepExtrema_DistShapeShape distancer(*(shapes[0]), *(shapes[1])); + BRepExtrema_DistShapeShape distancer(shapes[0]->getShape(), shapes[1]->getShape()); if (!distancer.IsDone()) { throw Base::ValueError("AttachEngineLine::calculateAttachedPlacement: " "proximity calculation failed."); @@ -2450,8 +2449,8 @@ AttachEnginePoint::_calculateAttachedPlacement(const std::vector shapes; - std::vector copiedShapeStorage; + std::vector shapes; + std::vector copiedShapeStorage; std::vector types; readLinks(objs, subs, shapes, copiedShapeStorage, types); @@ -2470,7 +2469,7 @@ AttachEnginePoint::_calculateAttachedPlacement(const std::vector points; assert(!shapes.empty()); - const TopoDS_Shape& sh = *shapes[0]; + const TopoDS_Shape& sh = shapes[0]->getShape(); if (sh.IsNull()) { throw Base::ValueError( "Null shape in AttachEnginePoint::calculateAttachedPlacement()!"); @@ -2492,13 +2491,13 @@ AttachEnginePoint::_calculateAttachedPlacement(const std::vectorIsNull()) { + if (shapes[0]->isNull()) { throw Base::ValueError( "Null shape in AttachEnginePoint::calculateAttachedPlacement()!"); } TopoDS_Edge e; try { - e = TopoDS::Edge(*(shapes[0])); + e = TopoDS::Edge(shapes[0]->getShape()); } catch (...) { } @@ -2546,16 +2545,16 @@ AttachEnginePoint::_calculateAttachedPlacement(const std::vectorIsNull()) { + if (shapes[0]->isNull()) { throw Base::ValueError( "Null shape in AttachEnginePoint::calculateAttachedPlacement()!"); } - if (shapes[1]->IsNull()) { + if (shapes[1]->isNull()) { throw Base::ValueError( "Null shape in AttachEnginePoint::calculateAttachedPlacement()!"); } - BasePoint = getProximityPoint(mmode, *(shapes[0]), *(shapes[1])); + BasePoint = getProximityPoint(mmode, shapes[0]->getShape(), shapes[1]->getShape()); } break; case mm0CenterOfMass: { GProp_GProps gpr = AttachEngine::getInertialPropsOfShape(shapes); diff --git a/src/Mod/Part/App/Attacher.h b/src/Mod/Part/App/Attacher.h index dd0c8ba569..1a11bc220f 100644 --- a/src/Mod/Part/App/Attacher.h +++ b/src/Mod/Part/App/Attacher.h @@ -363,7 +363,7 @@ public://helper functions that may be useful outside of the class static eRefType getRefTypeByName(const std::string &typeName); - static GProp_GProps getInertialPropsOfShape(const std::vector &shapes); + static GProp_GProps getInertialPropsOfShape(const std::vector &shapes); std::vector getRefObjects() const; const std::vector &getSubValues() const {return subnames;} @@ -430,7 +430,9 @@ protected: } static void readLinks(const std::vector &objs, const std::vector &subs, - std::vector& shapes, std::vector &storage, + + std::vector& shapes, + std::vector &storage, std::vector &types); static void throwWrongMode(eMapMode mmode); diff --git a/tests/src/Mod/Part/App/Attacher.cpp b/tests/src/Mod/Part/App/Attacher.cpp index 67b25e466b..facb6d1a0d 100644 --- a/tests/src/Mod/Part/App/Attacher.cpp +++ b/tests/src/Mod/Part/App/Attacher.cpp @@ -79,8 +79,8 @@ TEST_F(AttacherTest, TestGetShapeType) TEST_F(AttacherTest, TestGetInertialPropsOfShape) { auto& attacher = _boxes[1]->attacher(); - std::vector result; - auto faces = _boxes[1]->Shape.getShape().getSubShapes(TopAbs_FACE); + std::vector result; + auto faces = _boxes[1]->Shape.getShape().getSubTopoShapes(TopAbs_FACE); result.emplace_back(&faces[0]); auto shapeType = attacher.getInertialPropsOfShape(result); EXPECT_EQ(result.size(), 1); From 88141d6dce04ef5c2ca3f496dd30dfae2cdb3fb6 Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Mon, 11 Nov 2024 17:59:07 +0100 Subject: [PATCH 04/19] Part: Add midpoint attachement mode This adds midpoint attachement mode to Attacher. It works just like method for finding placement of JCS in Assembly. --- src/Base/Rotation.cpp | 6 +++ src/Base/Rotation.h | 3 ++ src/Mod/Part/App/Attacher.cpp | 80 +++++++++++++++++++++++++++++++++++ src/Mod/Part/App/Attacher.h | 1 + src/Mod/Part/App/Geometry.cpp | 67 ++++++++++++++++++++++++----- src/Mod/Part/App/Geometry.h | 9 ++++ 6 files changed, 155 insertions(+), 11 deletions(-) diff --git a/src/Base/Rotation.cpp b/src/Base/Rotation.cpp index e1c871e0f7..9947d0cd0b 100644 --- a/src/Base/Rotation.cpp +++ b/src/Base/Rotation.cpp @@ -81,6 +81,12 @@ Rotation::Rotation(const Vector3d& rotateFrom, const Vector3d& rotateTo) this->setValue(rotateFrom, rotateTo); } +Rotation Rotation::fromNormalVector(const Vector3d& normal) +{ + // We rotate Z axis to be aligned with the supplied normal vector + return Rotation(Vector3d(0, 0, 1), normal); +} + const double* Rotation::getValue() const { return &this->quat[0]; diff --git a/src/Base/Rotation.h b/src/Base/Rotation.h index f663c81510..03b82d62ad 100644 --- a/src/Base/Rotation.h +++ b/src/Base/Rotation.h @@ -49,6 +49,9 @@ public: Rotation(const Rotation& rot) = default; Rotation(Rotation&& rot) = default; ~Rotation() = default; + + /// Utility function to create Rotation based on direction / normal vector + static Rotation fromNormalVector(const Vector3d& normal); //@} /** Methods to get or set rotations. */ diff --git a/src/Mod/Part/App/Attacher.cpp b/src/Mod/Part/App/Attacher.cpp index 77f389bc7c..8b9e594f57 100644 --- a/src/Mod/Part/App/Attacher.cpp +++ b/src/Mod/Part/App/Attacher.cpp @@ -69,6 +69,8 @@ #include "AttachExtension.h" #include "Tools.h" +#include + using namespace Part; using namespace Attacher; @@ -136,6 +138,7 @@ const char* AttachEngine::eMapModeStrings[]= { "OYX", "ParallelPlane", + "MidPoint", nullptr}; @@ -1895,6 +1898,83 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vectorattachmentOffset; return plm; } break; + case mmMidpoint: { + Base::Placement placement; + + auto shape = shapes.front(); + auto geom = Geometry::fromShape(shape->getShape()); + + switch (shape->shapeType()) { + case TopAbs_VERTEX: { + if (auto point = dynamic_cast(geom.get())) { + placement.setPosition(point->getPoint()); + } + } + break; + + case TopAbs_EDGE: { + if (auto conic = dynamic_cast(geom.get())) { + placement.setPosition(conic->getLocation()); + placement.setRotation(conic->getRotation().value_or(Base::Rotation {})); + } else if (auto line = dynamic_cast(geom.get())) { + auto u1 = line->getFirstParameter(); + auto u2 = line->getLastParameter(); + + auto middle = (u1 + u2) / 2; + + placement.setPosition(line->pointAtParameter(middle)); + + Base::Vector3d direction; + if (!line->normalAt(middle, direction)) { + line->tangent(middle, direction); + } + + placement.setRotation(Base::Rotation::fromNormalVector(-direction)); + } + } + break; + + case TopAbs_FACE: { + auto surface = dynamic_cast(geom.get()); + + if (auto sphere = dynamic_cast(geom.get())) { + placement.setPosition(sphere->getLocation()); + } else if (auto cone = dynamic_cast(geom.get())) { + placement.setPosition(cone->getApex()); + } else { + placement.setPosition(shape->centerOfGravity().value()); + } + + if (auto rotation = surface->getRotation()) { + placement.setRotation(*rotation); + } else { + auto adaptorSurface = BRepAdaptor_Surface(TopoDS::Face(shape->getShape()), true); + + auto u1 = adaptorSurface.FirstUParameter(); + auto u2 = adaptorSurface.LastUParameter(); + auto v1 = adaptorSurface.FirstVParameter(); + auto v2 = adaptorSurface.LastVParameter(); + + // calculate the normal at midpoint of the surface and use it as Z axis + gp_Dir dir; + surface->normal((u1 + u2) / 2, (v1 + v2) / 2, dir); + + placement.setRotation(Base::Rotation::fromNormalVector(Base::convertTo(-dir))); + } + } + break; + + default: + THROWM(Base::TypeError, + "AttachEngine3D::calculateAttachedPlacement: Unsupported shape type, " + "must be one of: Vertex, Edge, Face"); + break; + } + + return placement * attachmentOffset; + + break; + } default: throwWrongMode(mmode); } // switch (MapMode) diff --git a/src/Mod/Part/App/Attacher.h b/src/Mod/Part/App/Attacher.h index 1a11bc220f..9b617d7547 100644 --- a/src/Mod/Part/App/Attacher.h +++ b/src/Mod/Part/App/Attacher.h @@ -108,6 +108,7 @@ enum eMapMode { mmOYX, mmParallelPlane, + mmMidpoint, mmDummy_NumberOfModes//a value useful to check the validity of mode value };//see also eMapModeStrings[] definition in .cpp diff --git a/src/Mod/Part/App/Geometry.cpp b/src/Mod/Part/App/Geometry.cpp index 9a47464e2b..f786c75c68 100644 --- a/src/Mod/Part/App/Geometry.cpp +++ b/src/Mod/Part/App/Geometry.cpp @@ -149,6 +149,8 @@ #include "ToroidPy.h" #include "TopoShape.h" +#include + #if OCC_VERSION_HEX >= 0x070600 using GeomAdaptor_HCurve = GeomAdaptor_Curve; @@ -2127,10 +2129,25 @@ void GeomConic::setLocation(const Base::Vector3d& Center) Base::Vector3d GeomConic::getCenter() const { - Handle(Geom_Conic) conic = Handle(Geom_Conic)::DownCast(handle()); + Handle(Geom_Conic) conic = Handle(Geom_Conic)::DownCast(handle()); gp_Ax1 axis = conic->Axis(); const gp_Pnt& loc = axis.Location(); - return Base::Vector3d(loc.X(),loc.Y(),loc.Z()); + return Base::Vector3d(loc.X(), loc.Y(), loc.Z()); +} + +std::optional GeomConic::getRotation() const +{ + Handle(Geom_Conic) conic = Handle(Geom_Conic)::DownCast(handle()); + + if (!conic) { + return {}; + } + + gp_Trsf trsf; + trsf.SetTransformation(conic->Position(), gp_Ax3()); + + auto q = trsf.GetRotation(); + return Base::Rotation(q.X(), q.Y(), q.Z(), q.W()); } void GeomConic::setCenter(const Base::Vector3d& Center) @@ -2142,7 +2159,6 @@ void GeomConic::setCenter(const Base::Vector3d& Center) conic->SetLocation(p1); } catch (Standard_Failure& e) { - THROWM(Base::CADKernelError,e.GetMessageString()) } } @@ -4803,28 +4819,36 @@ GeomPlane* GeomSurface::toPlane(bool clone, double tol) const if (isDerivedFrom(GeomPlane::getClassTypeId())) { if (clone) { return dynamic_cast(this->clone()); - } else { + } + else { return dynamic_cast(this->copy()); } } gp_Pln pln; - if (!isPlanar(&pln, tol)) + if (!isPlanar(&pln, tol)) { return nullptr; + } auto res = new GeomPlane(pln); res->copyNonTag(this); - if (clone) + if (clone) { res->tag = this->tag; + } return res; } +std::optional GeomSurface::getRotation() const +{ + return {}; +} + TopoDS_Shape GeomSurface::toShape() const { Handle(Geom_Surface) s = Handle(Geom_Surface)::DownCast(handle()); - Standard_Real u1,u2,v1,v2; - s->Bounds(u1,u2,v1,v2); - BRepBuilderAPI_MakeFace mkBuilder(s, u1, u2, v1, v2, Precision::Confusion() ); + Standard_Real u1, u2, v1, v2; + s->Bounds(u1, u2, v1, v2); + BRepBuilderAPI_MakeFace mkBuilder(s, u1, u2, v1, v2, Precision::Confusion()); return mkBuilder.Shape(); } @@ -5180,9 +5204,24 @@ GeomElementarySurface::~GeomElementarySurface() Base::Vector3d GeomElementarySurface::getLocation(void) const { - Handle(Geom_ElementarySurface) surf = Handle(Geom_ElementarySurface)::DownCast(handle()); + Handle(Geom_ElementarySurface) surf = Handle(Geom_ElementarySurface)::DownCast(handle()); gp_Pnt loc = surf->Location(); - return Base::Vector3d(loc.X(),loc.Y(),loc.Z()); + return Base::Vector3d(loc.X(), loc.Y(), loc.Z()); +} + +std::optional GeomPlane::getRotation() const +{ + Handle(Geom_ElementarySurface) s = Handle(Geom_ElementarySurface)::DownCast(handle()); + + if (!s) { + return {}; + } + + gp_Trsf trsf; + trsf.SetTransformation(s->Position().Ax2(),gp_Ax3()); + auto q = trsf.GetRotation(); + + return Base::Rotation(q.X(),q.Y(),q.Z(),q.W()); } Base::Vector3d GeomElementarySurface::getDir(void) const @@ -5411,6 +5450,12 @@ double GeomCone::getSemiAngle() const return mySurface->SemiAngle(); } +Base::Vector3d GeomCone::getApex() const +{ + Handle(Geom_ConicalSurface) s = Handle(Geom_ConicalSurface)::DownCast(handle()); + return Base::convertTo(s->Apex()); +} + bool GeomCone::isSame(const Geometry &_other, double tol, double atol) const { if(_other.getTypeId() != getTypeId()) diff --git a/src/Mod/Part/App/Geometry.h b/src/Mod/Part/App/Geometry.h index 4af1809128..376b16b99a 100644 --- a/src/Mod/Part/App/Geometry.h +++ b/src/Mod/Part/App/Geometry.h @@ -58,6 +58,7 @@ #include #include #include +#include #include @@ -409,6 +410,7 @@ public: */ Base::Vector3d getCenter() const; Base::Vector3d getLocation() const; + std::optional getRotation() const; void setLocation(const Base::Vector3d& Center); /*! * \deprecated use setLocation @@ -882,6 +884,8 @@ public: GeomPlane *toPlane(bool clone=true, double tol=1e-7) const; + virtual std::optional getRotation() const; + bool tangentU(double u, double v, gp_Dir& dirU) const; bool tangentV(double u, double v, gp_Dir& dirV) const; bool normal(double u, double v, gp_Dir& dir) const; @@ -961,6 +965,7 @@ public: ~GeomElementarySurface() override; Base::Vector3d getLocation() const; + Base::Vector3d getDir() const; Base::Vector3d getXDir() const; Base::Vector3d getYDir() const; @@ -1014,6 +1019,8 @@ public: double getRadius() const; double getSemiAngle() const; + Base::Vector3d getApex() const; + bool isSame(const Geometry &other, double tol, double atol) const override; void setHandle(const Handle(Geom_ConicalSurface)&); @@ -1091,6 +1098,8 @@ public: ~GeomPlane() override; Geometry *copy() const override; + std::optional getRotation() const override; + // Persistence implementer --------------------- unsigned int getMemSize() const override; void Save(Base::Writer &/*writer*/) const override; From 2a69e3d8788061cb1f5eb81eabcfd316d1ca515f Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Mon, 11 Nov 2024 17:54:29 +0100 Subject: [PATCH 05/19] Base: Add ComplexGeoData::centerOfGravity helper This adds a simple quality of life helper returning center of gravity as std::optional instead of C style method accepting reference and returning bool to indicate method success. --- src/App/ComplexGeoData.cpp | 11 +++++++++++ src/App/ComplexGeoData.h | 3 +++ 2 files changed, 14 insertions(+) diff --git a/src/App/ComplexGeoData.cpp b/src/App/ComplexGeoData.cpp index e8d50030a4..b8a1c67c6a 100644 --- a/src/App/ComplexGeoData.cpp +++ b/src/App/ComplexGeoData.cpp @@ -184,6 +184,17 @@ bool ComplexGeoData::getCenterOfGravity(Base::Vector3d& unused) const return false; } +std::optional ComplexGeoData::centerOfGravity() const +{ + Base::Vector3d centerOfGravity; + + if (getCenterOfGravity(centerOfGravity)) { + return centerOfGravity; + } + + return {}; +} + const std::string& ComplexGeoData::elementMapPrefix() { static std::string prefix(ELEMENT_MAP_PREFIX); diff --git a/src/App/ComplexGeoData.h b/src/App/ComplexGeoData.h index 995b797944..8c0f8dc59b 100644 --- a/src/App/ComplexGeoData.h +++ b/src/App/ComplexGeoData.h @@ -28,6 +28,8 @@ #define APP_COMPLEX_GEO_DATA_H #include +#include + #include #include #include @@ -200,6 +202,7 @@ public: * The default implementation only returns false. */ virtual bool getCenterOfGravity(Base::Vector3d& center) const; + virtual std::optional centerOfGravity() const; //@} static const std::string& elementMapPrefix(); From 8813a0c1153ad22186da43bdf226f96922945a0a Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Tue, 3 Dec 2024 18:35:27 +0100 Subject: [PATCH 06/19] Gui: Refactor TDragger --- src/Gui/SoFCCSysDragger.cpp | 115 ++++++++++++++++++++++-------------- src/Gui/SoFCCSysDragger.h | 23 +++++++- src/Gui/SoTextLabel.cpp | 7 ++- 3 files changed, 98 insertions(+), 47 deletions(-) diff --git a/src/Gui/SoFCCSysDragger.cpp b/src/Gui/SoFCCSysDragger.cpp index c7e049e5c7..ed569c44de 100644 --- a/src/Gui/SoFCCSysDragger.cpp +++ b/src/Gui/SoFCCSysDragger.cpp @@ -27,10 +27,8 @@ #include #include #include -#include #include #include -#include #include #include #include @@ -46,6 +44,9 @@ #include #include #include +#include +#include +#include #endif #include @@ -56,6 +57,8 @@ #include "MainWindow.h" #include "SoFCDB.h" +#include + /* GENERAL NOTE ON COIN3D CUSTOM DRAGGERS @@ -100,14 +103,20 @@ TDragger::TDragger() this->ref(); #endif - SO_KIT_ADD_CATALOG_ENTRY(translatorSwitch, SoSwitch, TRUE, geomSeparator, "", TRUE); - SO_KIT_ADD_CATALOG_ENTRY(translator, SoSeparator, TRUE, translatorSwitch, "", TRUE); - SO_KIT_ADD_CATALOG_ENTRY(translatorActive, SoSeparator, TRUE, translatorSwitch, "", TRUE); + SO_KIT_ADD_CATALOG_ENTRY(translator, SoSeparator, TRUE, geomSeparator, "", TRUE); + SO_KIT_ADD_CATALOG_ENTRY(activeSwitch, SoSwitch, TRUE, translator, "", TRUE); + SO_KIT_ADD_CATALOG_ENTRY(activeColor, SoBaseColor, TRUE, activeSwitch, "", TRUE); + SO_KIT_ADD_CATALOG_ENTRY(coneSeparator, SoSeparator, TRUE, translator, "", TRUE); + SO_KIT_ADD_CATALOG_ENTRY(cylinderSeparator, SoSeparator, TRUE, translator, "", TRUE); + SO_KIT_ADD_CATALOG_ENTRY(labelSeparator, SoSeparator, TRUE, translator, "", TRUE); if (SO_KIT_IS_FIRST_INSTANCE()) { buildFirstInstance(); } + SO_KIT_ADD_CATALOG_ENTRY(translator, SoSeparator, TRUE, geomSeparator, "", TRUE); + + SO_KIT_ADD_FIELD(label, ("")); SO_KIT_ADD_FIELD(translation, (0.0, 0.0, 0.0)); SO_KIT_ADD_FIELD(translationIncrement, (1.0)); SO_KIT_ADD_FIELD(translationIncrementCount, (0)); @@ -118,11 +127,15 @@ TDragger::TDragger() // initialize default parts. // first is from 'SO_KIT_CATALOG_ENTRY_HEADER' macro // second is unique name from buildFirstInstance(). - this->setPartAsDefault("translator", "CSysDynamics_TDragger_Translator"); - this->setPartAsDefault("translatorActive", "CSysDynamics_TDragger_TranslatorActive"); + SoInteractionKit::setPartAsDefault("coneSeparator", "CSysDynamics_TDragger_Cone"); + SoInteractionKit::setPartAsDefault("cylinderSeparator", "CSysDynamics_TDragger_Cylinder"); + SoInteractionKit::setPartAsDefault("activeColor", "CSysDynamics_TDragger_ActiveColor"); + + SoInteractionKit::setPart("labelSeparator", buildLabelGeometry()); + + auto sw = SO_GET_ANY_PART(this, "activeSwitch", SoSwitch); + SoInteractionKit::setSwitchValue(sw, SO_SWITCH_NONE); - SoSwitch* sw = SO_GET_ANY_PART(this, "translatorSwitch", SoSwitch); - SoInteractionKit::setSwitchValue(sw, 0); this->addStartCallback(&TDragger::startCB); this->addMotionCallback(&TDragger::motionCB); @@ -150,34 +163,22 @@ TDragger::~TDragger() void TDragger::buildFirstInstance() { - SoGroup* geometryGroup = buildGeometry(); + auto cylinderSeparator = buildCylinderGeometry(); + auto coneSeparator = buildConeGeometry(); + auto activeColor = buildActiveColor(); - auto localTranslator = new SoSeparator(); - localTranslator->setName("CSysDynamics_TDragger_Translator"); - localTranslator->addChild(geometryGroup); - SoFCDB::getStorage()->addChild(localTranslator); + cylinderSeparator->setName("CSysDynamics_TDragger_Cylinder"); + coneSeparator->setName("CSysDynamics_TDragger_Cone"); + activeColor->setName("CSysDynamics_TDragger_ActiveColor"); - auto localTranslatorActive = new SoSeparator(); - localTranslatorActive->setName("CSysDynamics_TDragger_TranslatorActive"); - auto colorActive = new SoBaseColor(); - colorActive->rgb.setValue(1.0, 1.0, 0.0); - localTranslatorActive->addChild(colorActive); - localTranslatorActive->addChild(geometryGroup); - SoFCDB::getStorage()->addChild(localTranslatorActive); + SoFCDB::getStorage()->addChild(cylinderSeparator); + SoFCDB::getStorage()->addChild(coneSeparator); + SoFCDB::getStorage()->addChild(activeColor); } -SoGroup* TDragger::buildGeometry() +SoSeparator* TDragger::buildCylinderGeometry() const { - // this builds one leg in the Y+ direction because of default done direction. - // the location anchor for shapes is the center of shape. - - auto root = new SoGroup(); - - // cylinder - float cylinderHeight = 10.0; - float cylinderRadius = 0.1f; auto cylinderSeparator = new SoSeparator(); - root->addChild(cylinderSeparator); auto cylinderLightModel = new SoLightModel(); cylinderLightModel->model = SoLightModel::BASE_COLOR; @@ -192,14 +193,15 @@ SoGroup* TDragger::buildGeometry() cylinder->height.setValue(cylinderHeight); cylinderSeparator->addChild(cylinder); - // cone - float coneBottomRadius = 0.8F; - float coneHeight = 2.5; - auto coneSeparator = new SoSeparator(); - root->addChild(coneSeparator); + return cylinderSeparator; +} +SoSeparator* TDragger::buildConeGeometry() const +{ auto coneLightModel = new SoLightModel(); coneLightModel->model = SoLightModel::BASE_COLOR; + + auto coneSeparator = new SoSeparator(); coneSeparator->addChild(coneLightModel); auto pickStyle = new SoPickStyle(); @@ -216,7 +218,33 @@ SoGroup* TDragger::buildGeometry() cone->height.setValue(coneHeight); coneSeparator->addChild(cone); - return root; + return coneSeparator; +} + +SoSeparator* TDragger::buildLabelGeometry() +{ + auto labelSeparator = new SoSeparator(); + + auto labelTranslation = new SoTranslation(); + labelTranslation->translation.setValue(0.0, cylinderHeight + coneHeight * 1.5, 0.0); + labelSeparator->addChild(labelTranslation); + + auto label = new SoFrameLabel(); + label->justification = SoFrameLabel::CENTER; + label->string.connectFrom(&this->label); + label->textColor.setValue(1.0, 1.0, 1.0); + label->horAlignment.setValue(SoImage::CENTER); + label->vertAlignment.setValue(SoImage::HALF); + labelSeparator->addChild(label); + + return labelSeparator; +} +SoBaseColor* TDragger::buildActiveColor() +{ + auto colorActive = new SoBaseColor(); + colorActive->rgb.setValue(1.0, 1.0, 0.0); + + return colorActive; } void TDragger::startCB(void*, SoDragger* d) @@ -274,8 +302,8 @@ void TDragger::valueChangedCB(void*, SoDragger* d) void TDragger::dragStart() { SoSwitch* sw; - sw = SO_GET_ANY_PART(this, "translatorSwitch", SoSwitch); - SoInteractionKit::setSwitchValue(sw, 1); + sw = SO_GET_ANY_PART(this, "activeSwitch", SoSwitch); + SoInteractionKit::setSwitchValue(sw, SO_SWITCH_ALL); // do an initial projection to eliminate discrepancies // in arrow head pick. we define the arrow in the y+ direction @@ -334,8 +362,8 @@ void TDragger::drag() void TDragger::dragFinish() { SoSwitch* sw; - sw = SO_GET_ANY_PART(this, "translatorSwitch", SoSwitch); - SoInteractionKit::setSwitchValue(sw, 0); + sw = SO_GET_ANY_PART(this, "activeSwitch", SoSwitch); + SoInteractionKit::setSwitchValue(sw, SO_SWITCH_NONE); } SbBool TDragger::setUpConnections(SbBool onoff, SbBool doitalways) @@ -1165,21 +1193,22 @@ SoFCCSysDragger::SoFCCSysDragger() SbColor(0, 1.0, 0).getPackedValue(0.0f), SbColor(0, 0, 1.0).getPackedValue(0.0f)); - // Increments - // Translator TDragger* tDragger; tDragger = SO_GET_ANY_PART(this, "xTranslatorDragger", TDragger); tDragger->translationIncrement.connectFrom(&this->translationIncrement); tDragger->autoScaleResult.connectFrom(&this->autoScaleResult); + tDragger->label.setValue("U"); translationIncrementCountX.connectFrom(&tDragger->translationIncrementCount); tDragger = SO_GET_ANY_PART(this, "yTranslatorDragger", TDragger); tDragger->translationIncrement.connectFrom(&this->translationIncrement); tDragger->autoScaleResult.connectFrom(&this->autoScaleResult); + tDragger->label.setValue("V"); translationIncrementCountY.connectFrom(&tDragger->translationIncrementCount); tDragger = SO_GET_ANY_PART(this, "zTranslatorDragger", TDragger); tDragger->translationIncrement.connectFrom(&this->translationIncrement); tDragger->autoScaleResult.connectFrom(&this->autoScaleResult); + tDragger->label.setValue("W"); translationIncrementCountZ.connectFrom(&tDragger->translationIncrementCount); // Planar Translator TPlanarDragger* tPlanarDragger; diff --git a/src/Gui/SoFCCSysDragger.h b/src/Gui/SoFCCSysDragger.h index 20364506b1..bf33ec9a92 100644 --- a/src/Gui/SoFCCSysDragger.h +++ b/src/Gui/SoFCCSysDragger.h @@ -29,10 +29,12 @@ #include #include #include +#include #include #include #include #include +#include #include class SoCamera; @@ -50,12 +52,23 @@ namespace Gui class TDragger : public SoDragger { SO_KIT_HEADER(TDragger); - SO_KIT_CATALOG_ENTRY_HEADER(translatorSwitch); + SO_KIT_CATALOG_ENTRY_HEADER(activeSwitch); + SO_KIT_CATALOG_ENTRY_HEADER(activeColor); SO_KIT_CATALOG_ENTRY_HEADER(translator); - SO_KIT_CATALOG_ENTRY_HEADER(translatorActive); + SO_KIT_CATALOG_ENTRY_HEADER(cylinderSeparator); + SO_KIT_CATALOG_ENTRY_HEADER(coneSeparator); + SO_KIT_CATALOG_ENTRY_HEADER(labelSeparator); + + static constexpr float coneBottomRadius { 0.8f }; + static constexpr float coneHeight { 2.5f }; + + static constexpr float cylinderHeight { 10.0f }; + static constexpr float cylinderRadius { 0.1f }; public: static void initClass(); TDragger(); + + SoSFString label; //!< set from outside and used to label SoSFVec3f translation; //!< set from outside and used from outside for single precision. SoSFDouble translationIncrement; //!< set from outside and used for rounding. SoSFInt32 translationIncrementCount; //!< number of steps. used from outside. @@ -82,6 +95,12 @@ private: void buildFirstInstance(); SbVec3f roundTranslation(const SbVec3f &vecIn, float incrementIn); SoGroup* buildGeometry(); + + SoSeparator* buildCylinderGeometry() const; + SoSeparator* buildConeGeometry() const; + SoSeparator* buildLabelGeometry(); + SoBaseColor* buildActiveColor(); + using inherited = SoDragger; }; diff --git a/src/Gui/SoTextLabel.cpp b/src/Gui/SoTextLabel.cpp index 6b2f55d53c..feed088b84 100644 --- a/src/Gui/SoTextLabel.cpp +++ b/src/Gui/SoTextLabel.cpp @@ -388,7 +388,6 @@ void SoFrameLabel::setIcon(const QPixmap &pixMap) drawImage(); } - void SoFrameLabel::notify(SoNotList * list) { SoField *f = list->getLastField(); @@ -468,7 +467,6 @@ void SoFrameLabel::drawImage() if (drawIcon) { painter.drawImage(QPoint(padding, paddingIconV), iconImg); - } painter.setPen(front); @@ -495,6 +493,11 @@ void SoFrameLabel::drawImage() */ void SoFrameLabel::GLRender(SoGLRenderAction *action) { + SoState * state = action->getState(); + + const SbColor& diffuse = SoLazyElement::getDiffuse(state, 0); + this->backgroundColor.setValue(diffuse); + inherited::GLRender(action); } From b470f74398945b1549617929f6275cdd13420de5 Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Wed, 4 Dec 2024 18:17:36 +0100 Subject: [PATCH 07/19] Gui: Add QuantitySpinBox normalization --- src/Gui/QuantitySpinBox.cpp | 31 +++++++++++++++++++++++++++++-- src/Gui/QuantitySpinBox.h | 3 +++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/Gui/QuantitySpinBox.cpp b/src/Gui/QuantitySpinBox.cpp index 1ca7d684e2..9e8acddaca 100644 --- a/src/Gui/QuantitySpinBox.cpp +++ b/src/Gui/QuantitySpinBox.cpp @@ -405,10 +405,18 @@ void QuantitySpinBox::resizeEvent(QResizeEvent * event) resizeWidget(); } -void Gui::QuantitySpinBox::keyPressEvent(QKeyEvent *event) +void Gui::QuantitySpinBox::keyPressEvent(QKeyEvent* event) { - if (!handleKeyEvent(event->text())) + const auto isEnter = event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return; + + if (isEnter && !isNormalized()) { + normalize(); + return; + } + + if (!handleKeyEvent(event->text())) { QAbstractSpinBox::keyPressEvent(event); + } } void Gui::QuantitySpinBox::paintEvent(QPaintEvent*) @@ -477,6 +485,24 @@ double QuantitySpinBox::rawValue() const return d->quantity.getValue(); } +void QuantitySpinBox::normalize() +{ + // this does not really change the value, only the representation + QSignalBlocker blocker(this); + + Q_D(const QuantitySpinBox); + return setValue(d->quantity); +} + +bool QuantitySpinBox::isNormalized() +{ + static const QRegularExpression operators(QStringLiteral("[+\\-/*]"), + QRegularExpression::CaseInsensitiveOption); + + Q_D(const QuantitySpinBox); + return !d->validStr.contains(operators); +} + void QuantitySpinBox::setValue(const Base::Quantity& value) { Q_D(QuantitySpinBox); @@ -884,6 +910,7 @@ void QuantitySpinBox::focusInEvent(QFocusEvent * event) void QuantitySpinBox::focusOutEvent(QFocusEvent * event) { validateInput(); + normalize(); QToolTip::hideText(); QAbstractSpinBox::focusOutEvent(event); diff --git a/src/Gui/QuantitySpinBox.h b/src/Gui/QuantitySpinBox.h index dadc28d906..780553718c 100644 --- a/src/Gui/QuantitySpinBox.h +++ b/src/Gui/QuantitySpinBox.h @@ -57,6 +57,9 @@ public: /// Get the current quantity without unit double rawValue() const; + void normalize(); + bool isNormalized(); + /// Gives the current state of the user input, gives true if it is a valid input with correct quantity /// or returns false if the input is a unparsable string or has a wrong unit. bool hasValidInput() const; From 1df3b5be6c6343d0fd2cb1ee3e8410c5e029d0de Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Sun, 27 Oct 2024 18:39:51 +0100 Subject: [PATCH 08/19] Gui: Rework TaskCSysDragger into new Transform Dialog This commit refactor ViewProviderDragger and TaskCSysDragger to be more modern and to support selecting TransformOrigin. --- src/App/Application.cpp | 4 + src/App/CMakeLists.txt | 2 + src/App/GeoFeature.cpp | 24 +- src/App/GeoFeature.h | 1 + src/App/Services.cpp | 30 ++ src/App/Services.h | 74 ++++ src/Base/Rotation.cpp | 7 + src/Base/Rotation.h | 51 +-- src/Gui/CMakeLists.txt | 1 + src/Gui/SoFCCSysDragger.cpp | 19 +- src/Gui/SoFCCSysDragger.h | 4 + src/Gui/SoTextLabel.cpp | 34 +- src/Gui/SoTextLabel.h | 2 + src/Gui/TaskCSysDragger.cpp | 648 +++++++++++++++++++++++++++----- src/Gui/TaskCSysDragger.h | 159 ++++++-- src/Gui/TaskCSysDragger.ui | 544 +++++++++++++++++++++++++++ src/Gui/Utilities.h | 22 ++ src/Gui/ViewProvider.cpp | 26 +- src/Gui/ViewProviderDragger.cpp | 367 +++++++++--------- src/Gui/ViewProviderDragger.h | 42 ++- src/Gui/ViewProviderLink.cpp | 2 +- src/Mod/Part/App/AppPart.cpp | 10 +- src/Mod/Part/App/CMakeLists.txt | 2 + src/Mod/Part/App/Services.cpp | 52 +++ src/Mod/Part/App/Services.h | 47 +++ 25 files changed, 1785 insertions(+), 389 deletions(-) create mode 100644 src/App/Services.cpp create mode 100644 src/App/Services.h create mode 100644 src/Gui/TaskCSysDragger.ui create mode 100644 src/Mod/Part/App/Services.cpp create mode 100644 src/Mod/Part/App/Services.h diff --git a/src/App/Application.cpp b/src/App/Application.cpp index 56ef0a38b2..9bd89679b8 100644 --- a/src/App/Application.cpp +++ b/src/App/Application.cpp @@ -63,6 +63,7 @@ #include #include #include +#include #include #include #include @@ -89,6 +90,7 @@ #include "Application.h" #include "CleanupProcess.h" #include "ComplexGeoData.h" +#include "Services.h" #include "DocumentObjectFileIncluded.h" #include "DocumentObjectGroup.h" #include "DocumentObjectGroupPy.h" @@ -2217,6 +2219,8 @@ void Application::initTypes() new Base::ExceptionProducer; new Base::ExceptionProducer; new Base::ExceptionProducer; + + Base::registerServiceImplementation(new NullCenterOfMass); } namespace { diff --git a/src/App/CMakeLists.txt b/src/App/CMakeLists.txt index 8512c365ea..a4ab592db6 100644 --- a/src/App/CMakeLists.txt +++ b/src/App/CMakeLists.txt @@ -289,6 +289,7 @@ SET(FreeCADApp_CPP_SRCS MetadataPyImp.cpp ElementNamingUtils.cpp SafeMode.cpp + Services.cpp StringHasher.cpp StringHasherPyImp.cpp StringIDPyImp.cpp @@ -313,6 +314,7 @@ SET(FreeCADApp_HPP_SRCS MeasureManager.h Metadata.h ElementNamingUtils.h + Services.h StringHasher.h ) diff --git a/src/App/GeoFeature.cpp b/src/App/GeoFeature.cpp index d87f405ceb..8d34144c8c 100644 --- a/src/App/GeoFeature.cpp +++ b/src/App/GeoFeature.cpp @@ -61,12 +61,7 @@ void GeoFeature::transformPlacement(const Base::Placement& transform) Base::Placement GeoFeature::globalPlacement() const { - auto* group = GeoFeatureGroupExtension::getGroupOfObject(this); - if (group) { - auto ext = group->getExtensionByType(); - return ext->globalGroupPlacement() * Placement.getValue(); - } - return Placement.getValue(); + return GeoFeature::getGlobalPlacement(this); } const PropertyComplexGeoData* GeoFeature::getPropertyOfGeometry() const @@ -351,3 +346,20 @@ Base::Placement GeoFeature::getGlobalPlacement(App::DocumentObject* targetObj, return getGlobalPlacement(targetObj, prop->getValue(), subs[0]); } + +Base::Placement GeoFeature::getGlobalPlacement(const DocumentObject* obj) +{ + auto placementProperty = obj->getPropertyByName("Placement"); + + if (!placementProperty) { + return {}; + } + + auto* group = GeoFeatureGroupExtension::getGroupOfObject(obj); + if (group) { + auto ext = group->getExtensionByType(); + return ext->globalGroupPlacement() * placementProperty->getValue(); + } + + return placementProperty->getValue(); +} diff --git a/src/App/GeoFeature.h b/src/App/GeoFeature.h index cb4dcfbd66..abc36912c5 100644 --- a/src/App/GeoFeature.h +++ b/src/App/GeoFeature.h @@ -195,6 +195,7 @@ public: static Base::Placement getGlobalPlacement(DocumentObject* targetObj, DocumentObject* rootObj, const std::string& sub); static Base::Placement getGlobalPlacement(DocumentObject* targetObj, PropertyXLinkSub* prop); + static Base::Placement getGlobalPlacement(const DocumentObject* obj); protected: void onChanged(const Property* prop) override; diff --git a/src/App/Services.cpp b/src/App/Services.cpp new file mode 100644 index 0000000000..ae70735441 --- /dev/null +++ b/src/App/Services.cpp @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/**************************************************************************** + * * + * Copyright (c) 2024 Kacper Donat * + * * + * 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 "Services.h" + +std::optional +App::NullCenterOfMass::ofDocumentObject([[maybe_unused]] DocumentObject* object) const +{ + return std::nullopt; +} \ No newline at end of file diff --git a/src/App/Services.h b/src/App/Services.h new file mode 100644 index 0000000000..297819e0c6 --- /dev/null +++ b/src/App/Services.h @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/**************************************************************************** + * * + * Copyright (c) 2024 Kacper Donat * + * * + * 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 APP_SERVICES_H +#define APP_SERVICES_H + +#include "DocumentObject.h" + +#include +#include + +namespace App +{ + +/** +* This service should provide placement of given sub object (like for example face). +* This feature is not implemented in the core and so it must be provided by module. +*/ +class SubObjectPlacementProvider +{ +public: + virtual ~SubObjectPlacementProvider() = default; + + /** + * Returns placement of sub object relative to the base placement. + */ + virtual Base::Placement calculate(SubObjectT object, Base::Placement basePlacement) const = 0; +}; + +/** +* This service should provide center of mass calculation; +*/ +class CenterOfMassProvider +{ +public: + virtual ~CenterOfMassProvider() = default; + + virtual std::optional ofDocumentObject(DocumentObject* object) const = 0; +}; + +/** +* Default implementation for the center of mass contract +* It always returns empty optional +*/ +class NullCenterOfMass final : public CenterOfMassProvider +{ +public: + std::optional ofDocumentObject(DocumentObject* object) const override; +}; + +} + + +#endif // APP_SERVICES_H diff --git a/src/Base/Rotation.cpp b/src/Base/Rotation.cpp index 9947d0cd0b..623b41dd2d 100644 --- a/src/Base/Rotation.cpp +++ b/src/Base/Rotation.cpp @@ -87,6 +87,13 @@ Rotation Rotation::fromNormalVector(const Vector3d& normal) return Rotation(Vector3d(0, 0, 1), normal); } +Rotation Rotation::fromEulerAngles(EulerSequence theOrder, double alpha, double beta, double gamma) +{ + Rotation rotation; + rotation.setEulerAngles(theOrder, alpha, beta, gamma); + return rotation; +} + const double* Rotation::getValue() const { return &this->quat[0]; diff --git a/src/Base/Rotation.h b/src/Base/Rotation.h index 03b82d62ad..9f8e6b5c16 100644 --- a/src/Base/Rotation.h +++ b/src/Base/Rotation.h @@ -50,29 +50,6 @@ public: Rotation(Rotation&& rot) = default; ~Rotation() = default; - /// Utility function to create Rotation based on direction / normal vector - static Rotation fromNormalVector(const Vector3d& normal); - //@} - - /** Methods to get or set rotations. */ - //@{ - const double* getValue() const; - void getValue(double& q0, double& q1, double& q2, double& q3) const; - void setValue(double q0, double q1, double q2, double q3); - /// If not a null quaternion then \a axis will be normalized - void getValue(Vector3d& axis, double& rfAngle) const; - /// Does the same as the method above unless normalizing the axis. - void getRawValue(Vector3d& axis, double& rfAngle) const; - void getValue(Matrix4D& matrix) const; - void setValue(const double q[4]); - void setValue(const Matrix4D& matrix); - void setValue(const Vector3d& axis, double fAngle); - void setValue(const Vector3d& rotateFrom, const Vector3d& rotateTo); - /// Euler angles in yaw,pitch,roll notation - void setYawPitchRoll(double y, double p, double r); - /// Euler angles in yaw,pitch,roll notation - void getYawPitchRoll(double& y, double& p, double& r) const; - enum EulerSequence { Invalid, @@ -115,6 +92,34 @@ public: EulerSequenceLast, }; + + /// Utility function to create Rotation based on direction / normal vector + /// Z base vector is assumed to represent the normal vector + static Rotation fromNormalVector(const Vector3d& normal); + /// Utility function to create Rotation based on euler angles + static Rotation + fromEulerAngles(EulerSequence theOrder, double alpha, double beta, double gamma); + //@} + + /** Methods to get or set rotations. */ + //@{ + const double* getValue() const; + void getValue(double& q0, double& q1, double& q2, double& q3) const; + void setValue(double q0, double q1, double q2, double q3); + /// If not a null quaternion then \a axis will be normalized + void getValue(Vector3d& axis, double& rfAngle) const; + /// Does the same as the method above unless normalizing the axis. + void getRawValue(Vector3d& axis, double& rfAngle) const; + void getValue(Matrix4D& matrix) const; + void setValue(const double q[4]); + void setValue(const Matrix4D& matrix); + void setValue(const Vector3d& axis, double fAngle); + void setValue(const Vector3d& rotateFrom, const Vector3d& rotateTo); + /// Euler angles in yaw,pitch,roll notation + void setYawPitchRoll(double y, double p, double r); + /// Euler angles in yaw,pitch,roll notation + void getYawPitchRoll(double& y, double& p, double& r) const; + static const char* eulerSequenceName(EulerSequence seq); static EulerSequence eulerSequenceFromName(const char* name); void getEulerAngles(EulerSequence theOrder, double& alpha, double& beta, double& gamma) const; diff --git a/src/Gui/CMakeLists.txt b/src/Gui/CMakeLists.txt index 9e71c9a4f7..ede57f85ba 100644 --- a/src/Gui/CMakeLists.txt +++ b/src/Gui/CMakeLists.txt @@ -402,6 +402,7 @@ SET(Gui_UIC_SRCS SceneInspector.ui InputVector.ui Placement.ui + TaskCSysDragger.ui TextureMapping.ui TaskView/TaskAppearance.ui TaskView/TaskOrientation.ui diff --git a/src/Gui/SoFCCSysDragger.cpp b/src/Gui/SoFCCSysDragger.cpp index ed569c44de..a395b968b1 100644 --- a/src/Gui/SoFCCSysDragger.cpp +++ b/src/Gui/SoFCCSysDragger.cpp @@ -230,15 +230,17 @@ SoSeparator* TDragger::buildLabelGeometry() labelSeparator->addChild(labelTranslation); auto label = new SoFrameLabel(); - label->justification = SoFrameLabel::CENTER; label->string.connectFrom(&this->label); label->textColor.setValue(1.0, 1.0, 1.0); - label->horAlignment.setValue(SoImage::CENTER); - label->vertAlignment.setValue(SoImage::HALF); + label->horAlignment = SoImage::CENTER; + label->vertAlignment = SoImage::HALF; + label->border = false; + label->backgroundUseBaseColor = true; labelSeparator->addChild(label); return labelSeparator; } + SoBaseColor* TDragger::buildActiveColor() { auto colorActive = new SoBaseColor(); @@ -1170,7 +1172,6 @@ SoFCCSysDragger::SoFCCSysDragger() SO_KIT_ADD_CATALOG_ENTRY(zRotatorDragger, RDragger, TRUE, zRotatorSeparator, "", TRUE); // Other - SO_KIT_ADD_FIELD(translation, (0.0, 0.0, 0.0)); SO_KIT_ADD_FIELD(translationIncrement, (1.0)); SO_KIT_ADD_FIELD(translationIncrementCountX, (0)); @@ -1186,6 +1187,10 @@ SoFCCSysDragger::SoFCCSysDragger() SO_KIT_ADD_FIELD(draggerSize, (1.0)); SO_KIT_ADD_FIELD(autoScaleResult, (1.0)); + SO_KIT_ADD_FIELD(xAxisLabel, ("X")); + SO_KIT_ADD_FIELD(yAxisLabel, ("Y")); + SO_KIT_ADD_FIELD(zAxisLabel, ("Z")); + SO_KIT_INIT_INSTANCE(); // Colors @@ -1198,17 +1203,17 @@ SoFCCSysDragger::SoFCCSysDragger() tDragger = SO_GET_ANY_PART(this, "xTranslatorDragger", TDragger); tDragger->translationIncrement.connectFrom(&this->translationIncrement); tDragger->autoScaleResult.connectFrom(&this->autoScaleResult); - tDragger->label.setValue("U"); + tDragger->label.connectFrom(&xAxisLabel); translationIncrementCountX.connectFrom(&tDragger->translationIncrementCount); tDragger = SO_GET_ANY_PART(this, "yTranslatorDragger", TDragger); tDragger->translationIncrement.connectFrom(&this->translationIncrement); tDragger->autoScaleResult.connectFrom(&this->autoScaleResult); - tDragger->label.setValue("V"); + tDragger->label.connectFrom(&yAxisLabel); translationIncrementCountY.connectFrom(&tDragger->translationIncrementCount); tDragger = SO_GET_ANY_PART(this, "zTranslatorDragger", TDragger); tDragger->translationIncrement.connectFrom(&this->translationIncrement); tDragger->autoScaleResult.connectFrom(&this->autoScaleResult); - tDragger->label.setValue("W"); + tDragger->label.connectFrom(&zAxisLabel); translationIncrementCountZ.connectFrom(&tDragger->translationIncrementCount); // Planar Translator TPlanarDragger* tPlanarDragger; diff --git a/src/Gui/SoFCCSysDragger.h b/src/Gui/SoFCCSysDragger.h index bf33ec9a92..b759bcf70a 100644 --- a/src/Gui/SoFCCSysDragger.h +++ b/src/Gui/SoFCCSysDragger.h @@ -282,6 +282,10 @@ public: SoSFInt32 rotationIncrementCountY; //!< used from outside for rotation y steps. SoSFInt32 rotationIncrementCountZ; //!< used from outside for rotation z steps. + SoSFString xAxisLabel; //!< label for X axis + SoSFString yAxisLabel; //!< label for Y axis + SoSFString zAxisLabel; //!< label for Z axis + void clearIncrementCounts(); //!< used to reset after drag update. /*! @brief Overall scale of dragger node. diff --git a/src/Gui/SoTextLabel.cpp b/src/Gui/SoTextLabel.cpp index feed088b84..1a4a29c0aa 100644 --- a/src/Gui/SoTextLabel.cpp +++ b/src/Gui/SoTextLabel.cpp @@ -379,6 +379,8 @@ SoFrameLabel::SoFrameLabel() SO_NODE_ADD_FIELD(name, ("Helvetica")); SO_NODE_ADD_FIELD(size, (12)); SO_NODE_ADD_FIELD(frame, (true)); + SO_NODE_ADD_FIELD(border, (true)); + SO_NODE_ADD_FIELD(backgroundUseBaseColor, (false)); //SO_NODE_ADD_FIELD(image, (SbVec2s(0,0), 0, NULL)); } @@ -397,9 +399,11 @@ void SoFrameLabel::notify(SoNotList * list) f == &this->justification || f == &this->name || f == &this->size || - f == &this->frame) { + f == &this->frame || + f == &this->border) { drawImage(); } + inherited::notify(list); } @@ -417,11 +421,12 @@ void SoFrameLabel::drawImage() int w = 0; int h = fm.height() * num; const SbColor& b = backgroundColor.getValue(); - QColor brush; - brush.setRgbF(b[0],b[1],b[2]); + QColor backgroundBrush; + backgroundBrush.setRgbF(b[0],b[1],b[2]); const SbColor& t = textColor.getValue(); QColor front; front.setRgbF(t[0],t[1],t[2]); + const QPen borderPen(QColor(0,0,127), 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); QStringList lines; for (int i=0; igetState(); - const SbColor& diffuse = SoLazyElement::getDiffuse(state, 0); - this->backgroundColor.setValue(diffuse); + if (backgroundUseBaseColor.getValue()) { + SoState* state = action->getState(); + const SbColor& diffuse = SoLazyElement::getDiffuse(state, 0); + + if (diffuse != this->backgroundColor.getValue()) { + this->backgroundColor.setValue(diffuse); + } + } inherited::GLRender(action); } diff --git a/src/Gui/SoTextLabel.h b/src/Gui/SoTextLabel.h index b91feb1980..56b9726711 100644 --- a/src/Gui/SoTextLabel.h +++ b/src/Gui/SoTextLabel.h @@ -118,6 +118,8 @@ public: SoSFName name; SoSFInt32 size; SoSFBool frame; + SoSFBool border; + SoSFBool backgroundUseBaseColor; //SoSFImage image; QPixmap iconPixmap; diff --git a/src/Gui/TaskCSysDragger.cpp b/src/Gui/TaskCSysDragger.cpp index ab23bc533a..a4e38bcce3 100644 --- a/src/Gui/TaskCSysDragger.cpp +++ b/src/Gui/TaskCSysDragger.cpp @@ -26,11 +26,20 @@ #include #include #include +#include #endif +#include +#include + #include -#include "Document.h" // must be before TaskCSysDragger.h -#include "TaskCSysDragger.h" +#include +#include +#include +#include +#include + +#include "Document.h" // must be before TaskCSysDragger.h #include "Application.h" #include "BitmapFactory.h" #include "Command.h" @@ -39,139 +48,582 @@ #include "ViewProviderDragger.h" #include "TaskView/TaskView.h" +#include "TaskCSysDragger.h" +#include "ui_TaskCSysDragger.h" + +#include using namespace Gui; - -static double degreesToRadians(const double °reesIn) +namespace { - return degreesIn * (M_PI / 180.0); -} - -TaskCSysDragger::TaskCSysDragger(Gui::ViewProviderDocumentObject* vpObjectIn, Gui::SoFCCSysDragger* draggerIn) : - dragger(draggerIn) +void alignGridLayoutColumns(const std::list& layouts, unsigned column = 0) { - assert(vpObjectIn); - assert(draggerIn); - vpObject = vpObjectIn->getObject(); - dragger->ref(); + std::vector widths; - setupGui(); -} + auto getActualWidth = [&](const QGridLayout* layout) -> int { + if (auto const item = layout->itemAtPosition(0, column)) { + return item->geometry().width(); + } -TaskCSysDragger::~TaskCSysDragger() -{ - dragger->unref(); - Gui::Application::Instance->commandManager().getCommandByName("Std_OrthographicCamera")->setEnabled(true); - Gui::Application::Instance->commandManager().getCommandByName("Std_PerspectiveCamera")->setEnabled(true); -} + return 0; + }; -void TaskCSysDragger::dragStartCallback(void *, SoDragger *) -{ - // This is called when a manipulator is about to manipulating - if(firstDrag) - { - Gui::Application::Instance->activeDocument()->openCommand(QT_TRANSLATE_NOOP("Command", "Transform")); - firstDrag=false; + for (const auto layout : layouts) { + widths.push_back(getActualWidth(layout)); + } + + const auto maxWidth = *std::max_element(widths.begin(), widths.end()); + for (const auto layout : layouts) { + layout->setColumnMinimumWidth(column, maxWidth); } } -void TaskCSysDragger::setupGui() +} // namespace + +TaskTransform::TaskTransform(Gui::ViewProviderDragger* vp, + Gui::SoFCCSysDragger* dragger, + QWidget* parent, + App::SubObjectPlacementProvider* subObjectPlacemenProvider, + App::CenterOfMassProvider* centerOfMassProvider) + : TaskBox(Gui::BitmapFactory().pixmap("Std_TransformManip.svg"), tr("Transform"), false, parent) + , vp(vp) + , subObjectPlacementProvider(subObjectPlacemenProvider) + , centerOfMassProvider(centerOfMassProvider) + , dragger(dragger) + , ui(new Ui_TaskCSysDragger) { - auto incrementsBox = new Gui::TaskView::TaskBox( - Gui::BitmapFactory().pixmap("Std_TransformManip"), - tr("Transform"), true, nullptr); + blockSelection(true); - auto gridLayout = new QGridLayout(); - gridLayout->setColumnStretch(1, 1); + dragger->addStartCallback(dragStartCallback, this); + dragger->addMotionCallback(dragMotionCallback, this); - auto tLabel = new QLabel(tr("Translation Increment:"), incrementsBox); - gridLayout->addWidget(tLabel, 0, 0, Qt::AlignRight); + vp->resetTransformOrigin(); - QFontMetrics metrics(QApplication::font()); - int spinBoxWidth = metrics.averageCharWidth() * 20; - tSpinBox = new QuantitySpinBox(incrementsBox); - tSpinBox->setMinimum(0.0); - tSpinBox->setMaximum(std::numeric_limits::max()); - tSpinBox->setUnit(Base::Unit::Length); - tSpinBox->setMinimumWidth(spinBoxWidth); - gridLayout->addWidget(tSpinBox, 0, 1, Qt::AlignLeft); + if (auto geoFeature = vp->getObject()) { + originalPlacement = geoFeature->Placement.getValue(); + } - auto rLabel = new QLabel(tr("Rotation Increment:"), incrementsBox); - gridLayout->addWidget(rLabel, 1, 0, Qt::AlignRight); - - rSpinBox = new QuantitySpinBox(incrementsBox); - rSpinBox->setMinimum(0.0); - rSpinBox->setMaximum(180.0); - rSpinBox->setUnit(Base::Unit::Angle); - rSpinBox->setMinimumWidth(spinBoxWidth); - gridLayout->addWidget(rSpinBox, 1, 1, Qt::AlignLeft); - - incrementsBox->groupLayout()->addLayout(gridLayout); - Content.push_back(incrementsBox); - - connect(tSpinBox, qOverload(&QuantitySpinBox::valueChanged), this, &TaskCSysDragger::onTIncrementSlot); - connect(rSpinBox, qOverload(&QuantitySpinBox::valueChanged), this, &TaskCSysDragger::onRIncrementSlot); + setupGui(); } -void TaskCSysDragger::onTIncrementSlot(double freshValue) +TaskTransform::~TaskTransform() { - dragger->translationIncrement.setValue(freshValue); + Gui::Application::Instance->commandManager() + .getCommandByName("Std_OrthographicCamera") + ->setEnabled(true); + + Gui::Application::Instance->commandManager() + .getCommandByName("Std_PerspectiveCamera") + ->setEnabled(true); } -void TaskCSysDragger::onRIncrementSlot(double freshValue) +void TaskTransform::dragStartCallback(void*, SoDragger*) { - dragger->rotationIncrement.setValue(degreesToRadians(freshValue)); + // This is called when a manipulator is about to manipulating + if (firstDrag) { + Gui::Application::Instance->activeDocument()->openCommand( + QT_TRANSLATE_NOOP("Command", "Transform")); + firstDrag = false; + } +} + +void TaskTransform::dragMotionCallback(void* data, SoDragger* dragger) +{ + auto task = static_cast(data); + + task->updatePositionAndRotationUi(); +} + +void TaskTransform::loadPlacementModeItems() const +{ + ui->placementComboBox->clear(); + + ui->placementComboBox->addItem(tr("Object origin"), + QVariant::fromValue(PlacementMode::ObjectOrigin)); + + if (centerOfMassProvider->ofDocumentObject(vp->getObject()).has_value()) { + ui->placementComboBox->addItem(tr("Center of mass / Centroid"), + QVariant::fromValue(PlacementMode::Centroid)); + } + + if (subObjectPlacementProvider) { + ui->placementComboBox->addItem(tr("Custom"), QVariant::fromValue(PlacementMode::Custom)); + } +} + +void TaskTransform::loadPositionModeItems() const +{ + ui->positionModeComboBox->clear(); + ui->positionModeComboBox->addItem(tr("Local"), QVariant::fromValue(PositionMode::Local)); + ui->positionModeComboBox->addItem(tr("Absolute"), QVariant::fromValue(PositionMode::Absolute)); +} + +void TaskTransform::setupGui() +{ + auto proxy = new QWidget(this); + ui->setupUi(proxy); + this->groupLayout()->addWidget(proxy); + + loadPlacementModeItems(); + loadPositionModeItems(); + + ui->referencePickerWidget->hide(); + ui->alignRotationCheckBox->hide(); + + for (auto positionSpinBox : {ui->translationIncrementSpinBox, + ui->xPositionSpinBox, + ui->yPositionSpinBox, + ui->zPositionSpinBox}) { + positionSpinBox->setUnit(Base::Unit::Length); + } + + for (auto rotationSpinBox : {ui->rotationIncrementSpinBox, + ui->xRotationSpinBox, + ui->yRotationSpinBox, + ui->zRotationSpinBox}) { + rotationSpinBox->setUnit(Base::Unit::Angle); + } + + connect(ui->translationIncrementSpinBox, + qOverload(&QuantitySpinBox::valueChanged), + this, + [this](double) { + updateIncrements(); + }); + connect(ui->rotationIncrementSpinBox, + qOverload(&QuantitySpinBox::valueChanged), + this, + [this](double) { + updateIncrements(); + }); + connect(ui->positionModeComboBox, + qOverload(&QComboBox::currentIndexChanged), + this, + &TaskTransform::onCoordinateSystemChange); + connect(ui->placementComboBox, + qOverload(&QComboBox::currentIndexChanged), + this, + &TaskTransform::onPlacementModeChange); + connect(ui->pickTransformOriginButton, + &QPushButton::clicked, + this, + &TaskTransform::onPickTransformOrigin); + connect(ui->alignToOtherObjectButton, + &QPushButton::clicked, + this, + &TaskTransform::onAlignToOtherObject); + connect(ui->flipPartButton, &QPushButton::clicked, this, &TaskTransform::onFlip); + + connect(ui->alignRotationCheckBox, + &QCheckBox::clicked, + this, + &TaskTransform::onAlignRotationChanged); + + for (auto positionSpinBox : + {ui->xPositionSpinBox, ui->yPositionSpinBox, ui->zPositionSpinBox}) { + connect(positionSpinBox, + qOverload(&QuantitySpinBox::valueChanged), + this, + [this](double) { + onPositionChange(); + }); + } + + for (auto rotationSpinBox : + {ui->xRotationSpinBox, ui->yRotationSpinBox, ui->zRotationSpinBox}) { + connect(rotationSpinBox, + qOverload(&QuantitySpinBox::valueChanged), + this, + [this](double) { + onRotationChange(); + }); + } + + alignGridLayoutColumns({ui->absolutePositionLayout, + ui->absoluteRotationLayout, + ui->transformOriginLayout, + ui->referencePickerLayout}); + + updateInputLabels(); + updateDraggerLabels(); + updateIncrements(); + updatePositionAndRotationUi(); +} + +void TaskTransform::updatePositionAndRotationUi() const +{ + + const auto xyzPlacement = vp->getDraggerPlacement(); + const auto uvwPlacement = currentCoordinateSystem().origin.inverse() * xyzPlacement; + + auto fixNegativeZero = [](const double value) { + return std::fabs(value) < Base::Precision::Confusion() ? 0.0 : value; + }; + + auto setPositionValues = [&](const Base::Vector3d& vec, auto* x, auto* y, auto* z) { + [[maybe_unused]] + auto blockers = {QSignalBlocker(x), QSignalBlocker(y), QSignalBlocker(z)}; + + x->setValue(fixNegativeZero(vec.x)); + y->setValue(fixNegativeZero(vec.y)); + z->setValue(fixNegativeZero(vec.z)); + }; + + auto setRotationValues = [&](const Base::Rotation& rot, auto* x, auto* y, auto* z) { + [[maybe_unused]] + auto blockers = {QSignalBlocker(x), QSignalBlocker(y), QSignalBlocker(z)}; + + double alpha, beta, gamma; + rot.getEulerAngles(Base::Rotation::Intrinsic_XYZ, alpha, beta, gamma); + + x->setValue(fixNegativeZero(alpha)); + y->setValue(fixNegativeZero(beta)); + z->setValue(fixNegativeZero(gamma)); + }; + + auto setValues = [&](const Base::Placement& placement, + auto* px, + auto* py, + auto* pz, + auto* rx, + auto* ry, + auto* rz) { + setPositionValues(placement.getPosition(), px, py, pz); + setRotationValues(placement.getRotation(), rx, ry, rz); + }; + + setValues(uvwPlacement, + ui->xPositionSpinBox, + ui->yPositionSpinBox, + ui->zPositionSpinBox, + ui->xRotationSpinBox, + ui->yRotationSpinBox, + ui->zRotationSpinBox); +} + +void TaskTransform::updateInputLabels() const +{ + auto [xLabel, yLabel, zLabel] = currentCoordinateSystem().labels; + + ui->xPositionLabel->setText(QString::fromStdString(xLabel)); + ui->yPositionLabel->setText(QString::fromStdString(yLabel)); + ui->zPositionLabel->setText(QString::fromStdString(zLabel)); + + ui->xRotationLabel->setText(QString::fromStdString(xLabel)); + ui->yRotationLabel->setText(QString::fromStdString(yLabel)); + ui->zRotationLabel->setText(QString::fromStdString(zLabel)); +} + +void TaskTransform::updateDraggerLabels() const +{ + auto coordinateSystem = + isDraggerAlignedToCoordinateSystem() ? absoluteCoordinateSystem() : localCoordinateSystem(); + + auto [xLabel, yLabel, zLabel] = coordinateSystem.labels; + + dragger->xAxisLabel.setValue(xLabel.c_str()); + dragger->yAxisLabel.setValue(yLabel.c_str()); + dragger->zAxisLabel.setValue(zLabel.c_str()); +} + +void TaskTransform::updateIncrements() const +{ + dragger->translationIncrement.setValue( + std::max(ui->translationIncrementSpinBox->rawValue(), 0.001)); + dragger->rotationIncrement.setValue( + Base::toRadians(std::max(ui->rotationIncrementSpinBox->rawValue(), 0.01))); +} + +void TaskTransform::setSelectionMode(SelectionMode mode) +{ + Gui::Selection().clearSelection(); + + ui->pickTransformOriginButton->setText(tr("Pick reference")); + ui->alignToOtherObjectButton->setText(tr("Move to other object")); + + switch (mode) { + case SelectionMode::SelectTransformOrigin: + blockSelection(false); + ui->referenceLineEdit->setText(tr("Select face, edge or vertex...")); + ui->pickTransformOriginButton->setText(tr("Cancel")); + break; + + case SelectionMode::SelectAlignTarget: + ui->alignToOtherObjectButton->setText(tr("Cancel")); + blockSelection(false); + break; + + case SelectionMode::None: + blockSelection(true); + break; + } + + selectionMode = mode; +} + +TaskTransform::SelectionMode TaskTransform::getSelectionMode() const +{ + return selectionMode; +} + +TaskTransform::CoordinateSystem TaskTransform::localCoordinateSystem() const +{ + auto origin = originalPlacement * vp->getTransformOrigin(); + origin.setRotation(vp->getDraggerPlacement().getRotation()); + + return {{"U", "V", "W"}, origin}; +} + +TaskTransform::CoordinateSystem TaskTransform::absoluteCoordinateSystem() const +{ + return { + {"X", "Y", "Z"}, + Base::Placement {}, + }; +} + +TaskTransform::CoordinateSystem TaskTransform::currentCoordinateSystem() const +{ + return ui->positionModeComboBox->currentIndex() == 0 ? localCoordinateSystem() + : absoluteCoordinateSystem(); +} + +void TaskTransform::onSelectionChanged(const SelectionChanges& msg) +{ + if (msg.Type != SelectionChanges::AddSelection) { + return; + } + + if (!subObjectPlacementProvider) { + return; + } + + auto doc = Application::Instance->getDocument(msg.pDocName); + auto obj = doc->getDocument()->getObject(msg.pObjectName); + + auto globalPlacement = App::GeoFeature::getGlobalPlacement(obj); + auto localPlacement = App::GeoFeature::getPlacementFromProp(obj, "Placement"); + auto rootPlacement = App::GeoFeature::getGlobalPlacement(vp->getObject()); + + auto selectedObjectPlacement = rootPlacement.inverse() * globalPlacement + * subObjectPlacementProvider->calculate(msg.Object, localPlacement); + + switch (selectionMode) { + case SelectionMode::SelectTransformOrigin: {auto label = msg.pOriginalMsg + ? QStringLiteral("%1#%2.%3") + .arg(QLatin1String(msg.pOriginalMsg->pObjectName), + QLatin1String(msg.pObjectName), + QLatin1String(msg.pSubName)) + : QStringLiteral("%1.%2").arg(QLatin1String(msg.pObjectName), QLatin1String(msg.pSubName)); + + + ui->referenceLineEdit->setText(label); + + customTransformOrigin = selectedObjectPlacement; + + updateTransformOrigin(); + + break; + } + + case SelectionMode::SelectAlignTarget: { + vp->setDraggerPlacement(vp->getObjectPlacement() * selectedObjectPlacement); + + vp->updateTransformFromDragger(); + vp->updatePlacementFromDragger(); + + break; + } + + default: + // no-op + break; + } + + + setSelectionMode(SelectionMode::None); +} + +void TaskTransform::onAlignRotationChanged() +{ + updateDraggerLabels(); + updateTransformOrigin(); +} + +void TaskTransform::onAlignToOtherObject() +{ + setSelectionMode(SelectionMode::SelectAlignTarget); +} + +void TaskTransform::onFlip() +{ + auto placement = vp->getDraggerPlacement(); + + placement.setRotation(placement.getRotation() + * Base::Rotation::fromNormalVector(Base::Vector3d(0, 0, -1))); + + vp->setDraggerPlacement(placement); + + vp->updateTransformFromDragger(); + vp->updatePlacementFromDragger(); +} + +void TaskTransform::onPickTransformOrigin() +{ + setSelectionMode(selectionMode == SelectionMode::None ? SelectionMode::SelectTransformOrigin + : SelectionMode::None); +} + +void TaskTransform::onPlacementModeChange(int index) +{ + placementMode = ui->placementComboBox->currentData().value(); + + updateTransformOrigin(); +} + +void TaskTransform::updateTransformOrigin() +{ + auto getTransformOrigin = [this](const PlacementMode& mode) -> Base::Placement { + switch (mode) { + case PlacementMode::ObjectOrigin: + return {}; + case PlacementMode::Centroid: + if (const auto com = centerOfMassProvider->ofDocumentObject(vp->getObject())) { + return Base::Placement {*com, {}}; + } + return {}; + case PlacementMode::Custom: + return customTransformOrigin.value_or(Base::Placement {}); + default: + return {}; + } + }; + + ui->referencePickerWidget->setVisible(placementMode == PlacementMode::Custom); + + if (placementMode == PlacementMode::Custom && !customTransformOrigin.has_value()) { + onPickTransformOrigin(); + } + + auto transformOrigin = getTransformOrigin(placementMode); + if (isDraggerAlignedToCoordinateSystem()) { + transformOrigin.setRotation( + (vp->getObjectPlacement().inverse() * absoluteCoordinateSystem().origin).getRotation()); + } + + vp->setTransformOrigin(transformOrigin); + + updatePositionAndRotationUi(); + updateDraggerLabels(); +} + +bool TaskTransform::isDraggerAlignedToCoordinateSystem() const +{ + return positionMode == PositionMode::Absolute && ui->alignRotationCheckBox->isChecked(); +} + +void TaskTransform::onTransformOriginReset() +{ + vp->resetTransformOrigin(); +} + +void TaskTransform::onCoordinateSystemChange([[maybe_unused]] int mode) +{ + positionMode = ui->positionModeComboBox->currentData().value(); + + ui->alignRotationCheckBox->setVisible(positionMode != PositionMode::Local); + + updateInputLabels(); + updatePositionAndRotationUi(); + updateTransformOrigin(); +} + +void TaskTransform::onPositionChange() +{ + const auto uvwPosition = Base::Vector3d(ui->xPositionSpinBox->rawValue(), + ui->yPositionSpinBox->rawValue(), + ui->zPositionSpinBox->rawValue()); + + const auto xyzPosition = currentCoordinateSystem().origin.getPosition() + + currentCoordinateSystem().origin.getRotation().multVec(uvwPosition); + + const auto placement = vp->getDraggerPlacement(); + + vp->setDraggerPlacement({xyzPosition, placement.getRotation()}); + + vp->updateTransformFromDragger(); + vp->updatePlacementFromDragger(); +} + +void TaskTransform::onRotationChange() +{ + const auto uvwRotation = Base::Rotation::fromEulerAngles(Base::Rotation::Intrinsic_XYZ, + ui->xRotationSpinBox->rawValue(), + ui->yRotationSpinBox->rawValue(), + ui->zRotationSpinBox->rawValue()); + + const auto xyzRotation = currentCoordinateSystem().origin.getRotation() * uvwRotation; + + const auto placement = vp->getDraggerPlacement(); + + vp->setDraggerPlacement({placement.getPosition(), xyzRotation}); + + vp->updateTransformFromDragger(); + vp->updatePlacementFromDragger(); +} + +TaskCSysDragger::TaskCSysDragger(ViewProviderDragger* vp, SoFCCSysDragger* dragger) + : vp(vp) +{ + transform = new TaskTransform(vp, dragger); + Content.push_back(transform); } void TaskCSysDragger::open() { - dragger->addStartCallback(dragStartCallback, this); - //we can't have user switching camera types while dragger is shown. - Gui::Application::Instance->commandManager().getCommandByName("Std_OrthographicCamera")->setEnabled(false); - Gui::Application::Instance->commandManager().getCommandByName("Std_PerspectiveCamera")->setEnabled(false); -// dragger->translationIncrement.setValue(lastTranslationIncrement); -// dragger->rotationIncrement.setValue(lastRotationIncrement); - ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/History/Dragger"); - double lastTranslationIncrement = hGrp->GetFloat("LastTranslationIncrement", 1.0); - double lastRotationIncrement = hGrp->GetFloat("LastRotationIncrement", 15.0); - tSpinBox->setValue(lastTranslationIncrement); - rSpinBox->setValue(lastRotationIncrement); + // we can't have user switching camera types while dragger is shown. + Gui::Application::Instance->commandManager() + .getCommandByName("Std_OrthographicCamera") + ->setEnabled(false); - Gui::TaskView::TaskDialog::open(); + Gui::Application::Instance->commandManager() + .getCommandByName("Std_PerspectiveCamera") + ->setEnabled(false); + + Gui::TaskView::TaskDialog::open(); + + Gui::Application::Instance->activeDocument()->openCommand( + QT_TRANSLATE_NOOP("Command", "Transform")); } bool TaskCSysDragger::accept() { - ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/History/Dragger"); - hGrp->SetFloat("LastTranslationIncrement", tSpinBox->rawValue()); - hGrp->SetFloat("LastRotationIncrement", rSpinBox->rawValue()); + if (auto documentObject = vp->getObject()) { + Gui::Document* document = + Gui::Application::Instance->getDocument(documentObject->getDocument()); + assert(document); + document->commitCommand(); + document->resetEdit(); + document->getDocument()->recompute(); + } - App::DocumentObject* dObject = vpObject.getObject(); - if (dObject) { - Gui::Document* document = Gui::Application::Instance->getDocument(dObject->getDocument()); - assert(document); - firstDrag = true; - document->commitCommand(); - document->resetEdit(); - document->getDocument()->recompute(); - } - return Gui::TaskView::TaskDialog::accept(); + return Gui::TaskView::TaskDialog::accept(); } bool TaskCSysDragger::reject() { - App::DocumentObject* dObject = vpObject.getObject(); - if (dObject) { - Gui::Document* document = Gui::Application::Instance->getDocument(dObject->getDocument()); - assert(document); - firstDrag = true; - document->abortCommand(); - document->resetEdit(); - document->getDocument()->recompute(); - } - return Gui::TaskView::TaskDialog::reject(); + if (auto documentObject = vp->getObject()) { + Gui::Document* document = + Gui::Application::Instance->getDocument(documentObject->getDocument()); + assert(document); + document->abortCommand(); + document->resetEdit(); + document->getDocument()->recompute(); + } + + return Gui::TaskView::TaskDialog::reject(); } #include "moc_TaskCSysDragger.cpp" diff --git a/src/Gui/TaskCSysDragger.h b/src/Gui/TaskCSysDragger.h index ba223e5d2c..aac80bf0b9 100644 --- a/src/Gui/TaskCSysDragger.h +++ b/src/Gui/TaskCSysDragger.h @@ -25,39 +25,138 @@ #define TASKCSYSDRAGGER_H #include "TaskView/TaskDialog.h" -#include +#include "TaskView/TaskView.h" +#include "ViewProviderDragger.h" + +#include +#include class SoDragger; -namespace Gui +namespace Attacher { - class QuantitySpinBox; - class SoFCCSysDragger; - class ViewProviderDragger; - - class TaskCSysDragger : public Gui::TaskView::TaskDialog - { - Q_OBJECT - public: - TaskCSysDragger(ViewProviderDocumentObject *vpObjectIn, SoFCCSysDragger *draggerIn); - ~TaskCSysDragger() override; - QDialogButtonBox::StandardButtons getStandardButtons() const override - { return QDialogButtonBox::Ok | QDialogButtonBox::Cancel;} - void open() override; - bool accept() override; - bool reject() override; - private Q_SLOTS: - void onTIncrementSlot(double freshValue); - void onRIncrementSlot(double freshValue); - private: - static inline bool firstDrag = true; - static void dragStartCallback(void * data, SoDragger * d); - void setupGui(); - App::DocumentObjectT vpObject; - SoFCCSysDragger *dragger; - QuantitySpinBox *tSpinBox; - QuantitySpinBox *rSpinBox; - }; + class AttachEngine; } -#endif // TASKCSYSDRAGGER_H +namespace Gui +{ +class QuantitySpinBox; +class SoFCCSysDragger; +class ViewProviderDragger; +class Ui_TaskCSysDragger; + +class TaskTransform : public Gui::TaskView::TaskBox, public Gui::SelectionObserver +{ + Q_OBJECT + +public: + enum class SelectionMode { None, SelectTransformOrigin, SelectAlignTarget }; + enum class PlacementMode { ObjectOrigin, Centroid, Custom }; + enum class PositionMode { Local, Absolute }; + + struct CoordinateSystem + { + std::array labels; + Base::Placement origin; + }; + + Q_ENUM(SelectionMode) + Q_ENUM(PlacementMode) + Q_ENUM(PositionMode) + + TaskTransform(Gui::ViewProviderDragger* vp, + Gui::SoFCCSysDragger* dragger, + QWidget* parent = nullptr, + App::SubObjectPlacementProvider* subObjectPlacementProvider = + Base::provideService(), + App::CenterOfMassProvider* centerOfMassProvider = + Base::provideService()); + ~TaskTransform() override; + + void setSelectionMode(SelectionMode mode); + SelectionMode getSelectionMode() const; + + CoordinateSystem absoluteCoordinateSystem() const; + CoordinateSystem localCoordinateSystem() const; + CoordinateSystem currentCoordinateSystem() const; + +private: + void onSelectionChanged(const SelectionChanges& msg) override; + +private Q_SLOTS: + void onPlacementModeChange(int index); + + void onPickTransformOrigin(); + void onTransformOriginReset(); + void onAlignRotationChanged(); + + void onAlignToOtherObject(); + void onFlip(); + + void onCoordinateSystemChange(int mode); + + void onPositionChange(); + void onRotationChange(); + +private: + static inline bool firstDrag = true; + static void dragStartCallback(void* data, SoDragger* d); + static void dragMotionCallback(void* data, SoDragger* d); + + void setupGui(); + + void loadPreferences(); + void savePreferences(); + + void loadPositionModeItems() const; + void loadPlacementModeItems() const; + + void updatePositionAndRotationUi() const; + void updateDraggerLabels() const; + void updateInputLabels() const; + void updateIncrements() const; + void updateTransformOrigin(); + + bool isDraggerAlignedToCoordinateSystem() const; + + ViewProviderDragger* vp; + + const App::SubObjectPlacementProvider* subObjectPlacementProvider; + const App::CenterOfMassProvider *centerOfMassProvider; + + CoinPtr dragger; + + Ui_TaskCSysDragger *ui; + + SelectionMode selectionMode { SelectionMode::None }; + PlacementMode placementMode { PlacementMode::ObjectOrigin }; + PositionMode positionMode { PositionMode::Local }; + + std::optional customTransformOrigin {}; + Base::Placement originalPlacement {}; +}; + +class TaskCSysDragger: public Gui::TaskView::TaskDialog +{ + Q_OBJECT + +public: + TaskCSysDragger(ViewProviderDragger* vp, SoFCCSysDragger* dragger); + ~TaskCSysDragger() override = default; + + QDialogButtonBox::StandardButtons getStandardButtons() const override + { + return QDialogButtonBox::Ok | QDialogButtonBox::Cancel; + } + + void open() override; + bool accept() override; + bool reject() override; + +private: + ViewProviderDragger* vp; + TaskTransform* transform; +}; +} // namespace Gui + +#endif // TASKCSYSDRAGGER_H diff --git a/src/Gui/TaskCSysDragger.ui b/src/Gui/TaskCSysDragger.ui new file mode 100644 index 0000000000..a86539f01d --- /dev/null +++ b/src/Gui/TaskCSysDragger.ui @@ -0,0 +1,544 @@ + + + Gui::TaskCSysDragger + + + + 0 + 0 + 450 + 1012 + + + + Placement + + + + + + 0 + + + + + Coordinate System + + + positionModeComboBox + + + + + + + + Local Coordinate System + + + + + Global Coordinate System + + + + + + + + + + 0 + + + + + align dragger rotation with selected coordinate system + + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 41 + + + + + + + + Qt::Orientation::Vertical + + + QSizePolicy::Policy::Fixed + + + + 20 + 16 + + + + + + + + Translation + + + + 9 + + + 9 + + + 9 + + + 9 + + + 6 + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + mm + + + + + + + mm + + + + + + + + 0 + 0 + + + + X + + + xPositionSpinBox + + + + + + + + 0 + 0 + + + + Y + + + yPositionSpinBox + + + + + + + + 0 + 0 + + + + Z + + + zPositionSpinBox + + + + + + + mm + + + + + + + + + + + + + Utilities + + + + + + Move to other object + + + + + + + QFrame::Shape::StyledPanel + + + QFrame::Shadow::Raised + + + + + + + Flip + + + + + + + + + + Dragger + + + + 9 + + + 9 + + + 9 + + + 9 + + + + + + + + + 0 + 0 + + + + QFrame::Shape::NoFrame + + + <b>Snapping</b> + + + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter + + + + + + + + 0 + 0 + + + + Translation + + + translationIncrementSpinBox + + + + + + + 0.000000000000000 + + + 360.000000000000000 + + + 5.000000000000000 + + + + + + + Qt::Orientation::Vertical + + + QSizePolicy::Policy::Fixed + + + + 20 + 10 + + + + + + + + + QLayout::SizeConstraint::SetDefaultConstraint + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 68 + 0 + + + + Reference + + + referenceLineEdit + + + + + + + pick reference + + + + + + + true + + + + + + + + + + mm + + + 0.000000000000000 + + + 2147483647.000000000000000 + + + 1.000000000000000 + + + + + + + Mode + + + placementComboBox + + + + + + + + 0 + 0 + + + + Rotation + + + rotationIncrementSpinBox + + + + + + + + + + Rotation + + + + 9 + + + 9 + + + 9 + + + 9 + + + 6 + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + 0 + 0 + + + + Y + + + yRotationSpinBox + + + + + + + + 0 + 0 + + + + Z + + + zRotationSpinBox + + + + + + + + + + + + + + 0 + 0 + + + + X + + + xRotationSpinBox + + + + + + + + + + + + + + + Gui::QuantitySpinBox + QAbstractSpinBox +
Gui/QuantitySpinBox.h
+
+
+ + placementComboBox + referenceLineEdit + pickTransformOriginButton + translationIncrementSpinBox + rotationIncrementSpinBox + positionModeComboBox + alignRotationCheckBox + xPositionSpinBox + yPositionSpinBox + zPositionSpinBox + xRotationSpinBox + yRotationSpinBox + zRotationSpinBox + alignToOtherObjectButton + flipPartButton + + + +
diff --git a/src/Gui/Utilities.h b/src/Gui/Utilities.h index b9e8019464..42079bf1e1 100644 --- a/src/Gui/Utilities.h +++ b/src/Gui/Utilities.h @@ -104,6 +104,28 @@ struct vec_traits { private: const vec_type& v; }; + +template <> +inline SbMatrix convertTo(const Base::Matrix4D& vec2) +{ + double dMtrx[16]; + vec2.getGLMatrix(dMtrx); + return SbMatrix(dMtrx[0], dMtrx[1], dMtrx[2], dMtrx[3], // clazy:exclude=rule-of-two-soft + dMtrx[4], dMtrx[5], dMtrx[6], dMtrx[7], + dMtrx[8], dMtrx[9], dMtrx[10], dMtrx[11], + dMtrx[12],dMtrx[13],dMtrx[14], dMtrx[15]); +} + +template <> +inline Base::Matrix4D convertTo(const SbMatrix& vec2) +{ + Base::Matrix4D mat; + for(int i=0;i<4;++i) { + for(int j=0;j<4;++j) + mat[i][j] = vec2[j][i]; + } + return mat; +} } namespace App{ class DocumentObject; } diff --git a/src/Gui/ViewProvider.cpp b/src/Gui/ViewProvider.cpp index 95085b677d..5fbaeb758e 100644 --- a/src/Gui/ViewProvider.cpp +++ b/src/Gui/ViewProvider.cpp @@ -56,6 +56,8 @@ #include "ViewProviderLink.h" #include "ViewProviderPy.h" +#include + FC_LOG_LEVEL_INIT("ViewProvider", true, true) @@ -345,13 +347,7 @@ QIcon ViewProvider::mergeColorfulOverlayIcons (const QIcon & orig) const void ViewProvider::setTransformation(const Base::Matrix4D &rcMatrix) { - double dMtrx[16]; - rcMatrix.getGLMatrix(dMtrx); - - pcTransform->setMatrix(SbMatrix(dMtrx[0], dMtrx[1], dMtrx[2], dMtrx[3], - dMtrx[4], dMtrx[5], dMtrx[6], dMtrx[7], - dMtrx[8], dMtrx[9], dMtrx[10], dMtrx[11], - dMtrx[12],dMtrx[13],dMtrx[14], dMtrx[15])); + pcTransform->setMatrix(convert(rcMatrix)); } void ViewProvider::setTransformation(const SbMatrix &rcMatrix) @@ -361,24 +357,12 @@ void ViewProvider::setTransformation(const SbMatrix &rcMatrix) SbMatrix ViewProvider::convert(const Base::Matrix4D &rcMatrix) { - //NOLINTBEGIN - double dMtrx[16]; - rcMatrix.getGLMatrix(dMtrx); - return SbMatrix(dMtrx[0], dMtrx[1], dMtrx[2], dMtrx[3], // clazy:exclude=rule-of-two-soft - dMtrx[4], dMtrx[5], dMtrx[6], dMtrx[7], - dMtrx[8], dMtrx[9], dMtrx[10], dMtrx[11], - dMtrx[12],dMtrx[13],dMtrx[14], dMtrx[15]); - //NOLINTEND + return Base::convertTo(rcMatrix); } Base::Matrix4D ViewProvider::convert(const SbMatrix &smat) { - Base::Matrix4D mat; - for(int i=0;i<4;++i) { - for(int j=0;j<4;++j) - mat[i][j] = smat[j][i]; - } - return mat; + return Base::convertTo(smat); } void ViewProvider::addDisplayMaskMode(SoNode *node, const char* type) diff --git a/src/Gui/ViewProviderDragger.cpp b/src/Gui/ViewProviderDragger.cpp index 2e20b3f52c..4e60eab9c1 100644 --- a/src/Gui/ViewProviderDragger.cpp +++ b/src/Gui/ViewProviderDragger.cpp @@ -23,16 +23,19 @@ #include "PreCompiled.h" #ifndef _PreComp_ -# include -# include -# include -# include -# include -# include +#include +#include +#include +#include +#include +#include +#include #endif #include #include +#include +#include #include "Gui/ViewParams.h" #include "Application.h" @@ -44,20 +47,25 @@ #include "TaskCSysDragger.h" #include "View3DInventorViewer.h" #include "ViewProviderDragger.h" +#include "Utilities.h" +#include using namespace Gui; PROPERTY_SOURCE(Gui::ViewProviderDragger, Gui::ViewProviderDocumentObject) -ViewProviderDragger::ViewProviderDragger() = default; +ViewProviderDragger::ViewProviderDragger() +{ + ADD_PROPERTY_TYPE(TransformOrigin, ({}), nullptr, App::Prop_Hidden, nullptr); +}; ViewProviderDragger::~ViewProviderDragger() = default; void ViewProviderDragger::updateData(const App::Property* prop) { - if (prop->isDerivedFrom(App::PropertyPlacement::getClassTypeId()) && - strcmp(prop->getName(), "Placement") == 0) { + if (prop->isDerivedFrom(App::PropertyPlacement::getClassTypeId()) + && strcmp(prop->getName(), "Placement") == 0) { // Note: If R is the rotation, c the rotation center and t the translation // vector then Inventor applies the following transformation: R*(x-c)+c+t // In FreeCAD a placement only has a rotation and a translation part but @@ -73,6 +81,30 @@ void ViewProviderDragger::updateData(const App::Property* prop) ViewProviderDocumentObject::updateData(prop); } +void ViewProviderDragger::setTransformOrigin(const Base::Placement& placement) +{ + TransformOrigin.setValue(placement); +} + +void ViewProviderDragger::resetTransformOrigin() +{ + setTransformOrigin({}); +} + +void ViewProviderDragger::onChanged(const App::Property* property) +{ + if (property == &TransformOrigin) { + updateDraggerPosition(); + } + + ViewProviderDocumentObject::onChanged(property); +} + +TaskView::TaskDialog* ViewProviderDragger::getTransformDialog() +{ + return new TaskCSysDragger(this, csysDragger); +} + bool ViewProviderDragger::doubleClicked() { Gui::Application::Instance->activeDocument()->setEdit(this, (int)ViewProvider::Default); @@ -81,249 +113,216 @@ bool ViewProviderDragger::doubleClicked() void ViewProviderDragger::setupContextMenu(QMenu* menu, QObject* receiver, const char* member) { - QIcon iconObject = mergeGreyableOverlayIcons(Gui::BitmapFactory().pixmap("Std_TransformManip.svg")); + QIcon iconObject = + mergeGreyableOverlayIcons(Gui::BitmapFactory().pixmap("Std_TransformManip.svg")); QAction* act = menu->addAction(iconObject, QObject::tr("Transform"), receiver, member); act->setData(QVariant((int)ViewProvider::Transform)); ViewProviderDocumentObject::setupContextMenu(menu, receiver, member); } -ViewProvider *ViewProviderDragger::startEditing(int mode) { +ViewProvider* ViewProviderDragger::startEditing(int mode) +{ _linkDragger = nullptr; auto ret = ViewProviderDocumentObject::startEditing(mode); - if(!ret) + if (!ret) { return ret; - return _linkDragger?_linkDragger:ret; + } + return _linkDragger ? _linkDragger : ret; } -bool ViewProviderDragger::checkLink() { +bool ViewProviderDragger::checkLink() +{ // Trying to detect if the editing request is forwarded by a link object, // usually by doubleClicked(). If so, we route the request back. There shall // be no risk of infinite recursion, as ViewProviderLink handles // ViewProvider::Transform request by itself. - ViewProviderDocumentObject *vpParent = nullptr; + ViewProviderDocumentObject* vpParent = nullptr; std::string subname; + auto doc = Application::Instance->editDocument(); - if(!doc) + if (!doc) { return false; - doc->getInEdit(&vpParent,&subname); - if(!vpParent) + } + + doc->getInEdit(&vpParent, &subname); + if (!vpParent) { return false; + } + auto sobj = vpParent->getObject()->getSubObject(subname.c_str()); - if(!sobj || sobj==getObject() || sobj->getLinkedObject(true)!=getObject()) + if (!sobj || sobj == getObject() || sobj->getLinkedObject(true) != getObject()) { return false; + } + auto vp = Application::Instance->getViewProvider(sobj); - if(!vp) + if (!vp) { return false; + } + _linkDragger = vp->startEditing(ViewProvider::Transform); - if(_linkDragger) - return true; - return false; + + return _linkDragger != nullptr; } bool ViewProviderDragger::setEdit(int ModNum) { - Q_UNUSED(ModNum); + Q_UNUSED(ModNum); - if (checkLink()) { - return true; - } - - App::DocumentObject *genericObject = this->getObject(); - - if (genericObject->isDerivedFrom(App::GeoFeature::getClassTypeId())) { - auto geoFeature = static_cast(genericObject); - const Base::Placement &placement = geoFeature->Placement.getValue(); - auto tempTransform = new SoTransform(); - tempTransform->ref(); - updateTransform(placement, tempTransform); + if (checkLink()) { + return true; + } assert(!csysDragger); + csysDragger = new SoFCCSysDragger(); - csysDragger->setAxisColors( - Gui::ViewParams::instance()->getAxisXColor(), - Gui::ViewParams::instance()->getAxisYColor(), - Gui::ViewParams::instance()->getAxisZColor() - ); + csysDragger->setAxisColors(Gui::ViewParams::instance()->getAxisXColor(), + Gui::ViewParams::instance()->getAxisYColor(), + Gui::ViewParams::instance()->getAxisZColor()); csysDragger->draggerSize.setValue(ViewParams::instance()->getDraggerScale()); - csysDragger->translation.setValue(tempTransform->translation.getValue()); - csysDragger->rotation.setValue(tempTransform->rotation.getValue()); - - tempTransform->unref(); - - pcTransform->translation.connectFrom(&csysDragger->translation); - pcTransform->rotation.connectFrom(&csysDragger->rotation); + csysDragger->addStartCallback(dragStartCallback, this); csysDragger->addFinishCallback(dragFinishCallback, this); + csysDragger->addMotionCallback(dragMotionCallback, this); - // dragger node is added to viewer's editing root in setEditViewer - // pcRoot->insertChild(csysDragger, 0); - csysDragger->ref(); + Gui::Control().showDialog(getTransformDialog()); - auto task = new TaskCSysDragger(this, csysDragger); - Gui::Control().showDialog(task); - } + updateDraggerPosition(); - return true; + return true; } void ViewProviderDragger::unsetEdit(int ModNum) { - Q_UNUSED(ModNum); + Q_UNUSED(ModNum); - if(csysDragger) - { - pcTransform->translation.disconnect(&csysDragger->translation); - pcTransform->rotation.disconnect(&csysDragger->rotation); + csysDragger.reset(); - // dragger node is added to viewer's editing root in setEditViewer - // pcRoot->removeChild(csysDragger); //should delete csysDragger - csysDragger->unref(); - csysDragger = nullptr; - } - Gui::Control().closeDialog(); + Gui::Control().closeDialog(); } void ViewProviderDragger::setEditViewer(Gui::View3DInventorViewer* viewer, int ModNum) { Q_UNUSED(ModNum); - if (csysDragger && viewer) - { - auto rootPickStyle = new SoPickStyle(); - rootPickStyle->style = SoPickStyle::UNPICKABLE; - auto selection = static_cast(viewer->getSceneGraph()); - selection->insertChild(rootPickStyle, 0); - viewer->setSelectionEnabled(false); - csysDragger->setUpAutoScale(viewer->getSoRenderManager()->getCamera()); + if (csysDragger && viewer) { + csysDragger->setUpAutoScale(viewer->getSoRenderManager()->getCamera()); - auto mat = viewer->getDocument()->getEditingTransform(); - viewer->getDocument()->setEditingTransform(mat); - auto feat = dynamic_cast(getObject()); - if(feat) { - auto matInverse = feat->Placement.getValue().toMatrix(); - matInverse.inverse(); - mat *= matInverse; - } - viewer->setupEditingRoot(csysDragger,&mat); + auto mat = viewer->getDocument()->getEditingTransform(); + if (auto geoFeature = getObject()) { + mat *= geoFeature->Placement.getValue().inverse().toMatrix(); + } + + viewer->getDocument()->setEditingTransform(mat); + viewer->setupEditingRoot(csysDragger, &mat); } } void ViewProviderDragger::unsetEditViewer(Gui::View3DInventorViewer* viewer) -{ - auto selection = static_cast(viewer->getSceneGraph()); - SoNode *child = selection->getChild(0); - if (child && child->isOfType(SoPickStyle::getClassTypeId())) { - selection->removeChild(child); - viewer->setSelectionEnabled(true); - } -} +{} -void ViewProviderDragger::dragFinishCallback(void *data, SoDragger *d) +void ViewProviderDragger::dragStartCallback(void* data, [[maybe_unused]] SoDragger* d) { // This is called when a manipulator has done manipulating + auto vp = static_cast(data); - auto sudoThis = static_cast(data); - auto dragger = static_cast(d); - updatePlacementFromDragger(sudoThis, dragger); - - //Gui::Application::Instance->activeDocument()->commitCommand(); + vp->draggerPlacement = vp->getDraggerPlacement(); + vp->csysDragger->clearIncrementCounts(); } -void ViewProviderDragger::updatePlacementFromDragger(ViewProviderDragger* sudoThis, SoFCCSysDragger* draggerIn) +void ViewProviderDragger::dragFinishCallback(void* data, SoDragger* d) { - App::DocumentObject *genericObject = sudoThis->getObject(); - if (!genericObject->isDerivedFrom(App::GeoFeature::getClassTypeId())) - return; - auto geoFeature = static_cast(genericObject); - Base::Placement originalPlacement = geoFeature->Placement.getValue(); - double pMatrix[16]; - originalPlacement.toMatrix().getMatrix(pMatrix); - Base::Placement freshPlacement = originalPlacement; + // This is called when a manipulator has done manipulating + auto vp = static_cast(data); - //local cache for brevity. - double translationIncrement = draggerIn->translationIncrement.getValue(); - double rotationIncrement = draggerIn->rotationIncrement.getValue(); - int tCountX = draggerIn->translationIncrementCountX.getValue(); - int tCountY = draggerIn->translationIncrementCountY.getValue(); - int tCountZ = draggerIn->translationIncrementCountZ.getValue(); - int rCountX = draggerIn->rotationIncrementCountX.getValue(); - int rCountY = draggerIn->rotationIncrementCountY.getValue(); - int rCountZ = draggerIn->rotationIncrementCountZ.getValue(); + vp->draggerPlacement = vp->getDraggerPlacement(); + vp->csysDragger->clearIncrementCounts(); - //just as a little sanity check make sure only 1 or 2 fields has changed. - int numberOfFieldChanged = 0; - if (tCountX) numberOfFieldChanged++; - if (tCountY) numberOfFieldChanged++; - if (tCountZ) numberOfFieldChanged++; - if (rCountX) numberOfFieldChanged++; - if (rCountY) numberOfFieldChanged++; - if (rCountZ) numberOfFieldChanged++; - if (numberOfFieldChanged == 0) - return; - assert(numberOfFieldChanged == 1 || numberOfFieldChanged == 2); + vp->updatePlacementFromDragger(); +} - //helper lambdas. - auto getVectorX = [&pMatrix]() {return Base::Vector3d(pMatrix[0], pMatrix[4], pMatrix[8]);}; - auto getVectorY = [&pMatrix]() {return Base::Vector3d(pMatrix[1], pMatrix[5], pMatrix[9]);}; - auto getVectorZ = [&pMatrix]() {return Base::Vector3d(pMatrix[2], pMatrix[6], pMatrix[10]);}; +void ViewProviderDragger::dragMotionCallback(void* data, SoDragger* d) +{ + auto vp = static_cast(data); - if (tCountX) - { - Base::Vector3d movementVector(getVectorX()); - movementVector *= (tCountX * translationIncrement); - freshPlacement.move(movementVector); - geoFeature->Placement.setValue(freshPlacement); - } - if (tCountY) - { - Base::Vector3d movementVector(getVectorY()); - movementVector *= (tCountY * translationIncrement); - freshPlacement.move(movementVector); - geoFeature->Placement.setValue(freshPlacement); - } - if (tCountZ) - { - Base::Vector3d movementVector(getVectorZ()); - movementVector *= (tCountZ * translationIncrement); - freshPlacement.move(movementVector); - geoFeature->Placement.setValue(freshPlacement); - } - if (rCountX) - { - Base::Vector3d rotationVector(getVectorX()); - Base::Rotation rotation(rotationVector, rCountX * rotationIncrement); - freshPlacement.setRotation(rotation * freshPlacement.getRotation()); - geoFeature->Placement.setValue(freshPlacement); - } - if (rCountY) - { - Base::Vector3d rotationVector(getVectorY()); - Base::Rotation rotation(rotationVector, rCountY * rotationIncrement); - freshPlacement.setRotation(rotation * freshPlacement.getRotation()); - geoFeature->Placement.setValue(freshPlacement); - } - if (rCountZ) - { - Base::Vector3d rotationVector(getVectorZ()); - Base::Rotation rotation(rotationVector, rCountZ * rotationIncrement); - freshPlacement.setRotation(rotation * freshPlacement.getRotation()); - geoFeature->Placement.setValue(freshPlacement); - } + vp->updateTransformFromDragger(); +} - draggerIn->clearIncrementCounts(); +void ViewProviderDragger::updatePlacementFromDragger() +{ + const auto geoFeature = getObject(); + + if (!geoFeature) { + return; + } + + geoFeature->Placement.setValue(getDraggerPlacement() * getTransformOrigin().inverse()); +} + +void ViewProviderDragger::updateTransformFromDragger() +{ + const auto placement = getDraggerPlacement() * getTransformOrigin().inverse(); + + pcTransform->translation.setValue(Base::convertTo(placement.getPosition())); + pcTransform->rotation.setValue(Base::convertTo(placement.getRotation())); +} + +Base::Placement ViewProviderDragger::getDraggerPlacement() const +{ + const double translationStep = csysDragger->translationIncrement.getValue(); + const int xSteps = csysDragger->translationIncrementCountX.getValue(); + const int ySteps = csysDragger->translationIncrementCountY.getValue(); + const int zSteps = csysDragger->translationIncrementCountZ.getValue(); + + const auto rotation = draggerPlacement.getRotation(); + const auto xBase = rotation.multVec(Base::Vector3d(1, 0, 0)); + const auto yBase = rotation.multVec(Base::Vector3d(0, 1, 0)); + const auto zBase = rotation.multVec(Base::Vector3d(0, 0, 1)); + + const auto positionIncrement = + xBase * (translationStep * xSteps) + + yBase * (translationStep * ySteps) + + zBase * (translationStep * zSteps); + + const double rotationStep = csysDragger->rotationIncrement.getValue(); + const int xRotationSteps = csysDragger->rotationIncrementCountX.getValue(); + const int yRotationSteps = csysDragger->rotationIncrementCountY.getValue(); + const int zRotationSteps = csysDragger->rotationIncrementCountZ.getValue(); + + auto newRotation = rotation; + newRotation = newRotation * Base::Rotation(Base::Vector3d(1, 0, 0), xRotationSteps * rotationStep); + newRotation = newRotation * Base::Rotation(Base::Vector3d(0, 1, 0), yRotationSteps * rotationStep); + newRotation = newRotation * Base::Rotation(Base::Vector3d(0, 0, 1), zRotationSteps * rotationStep); + + return Base::Placement( + draggerPlacement.getPosition() + positionIncrement, + newRotation + ); +} + +void ViewProviderDragger::setDraggerPlacement(const Base::Placement& placement) +{ + csysDragger->translation.setValue(Base::convertTo(placement.getPosition())); + csysDragger->rotation.setValue(Base::convertTo(placement.getRotation())); + + draggerPlacement = placement; + csysDragger->clearIncrementCounts(); +} + +void ViewProviderDragger::updateDraggerPosition() +{ + if (!csysDragger) { + return; + } + + auto placement = getObject()->Placement.getValue() * getTransformOrigin(); + + setDraggerPlacement(placement); } void ViewProviderDragger::updateTransform(const Base::Placement& from, SoTransform* to) { - auto q0 = (float)from.getRotation().getValue()[0]; - auto q1 = (float)from.getRotation().getValue()[1]; - auto q2 = (float)from.getRotation().getValue()[2]; - auto q3 = (float)from.getRotation().getValue()[3]; - auto px = (float)from.getPosition().x; - auto py = (float)from.getPosition().y; - auto pz = (float)from.getPosition().z; - to->rotation.setValue(q0,q1,q2,q3); - to->translation.setValue(px,py,pz); - to->center.setValue(0.0f,0.0f,0.0f); - to->scaleFactor.setValue(1.0f,1.0f,1.0f); + to->rotation.setValue(Base::convertTo(from.getRotation())); + to->translation.setValue(Base::convertTo(from.getPosition())); + to->center.setValue(0.0f, 0.0f, 0.0f); + to->scaleFactor.setValue(1.0f, 1.0f, 1.0f); } diff --git a/src/Gui/ViewProviderDragger.h b/src/Gui/ViewProviderDragger.h index 40502f016a..ce5a6da50c 100644 --- a/src/Gui/ViewProviderDragger.h +++ b/src/Gui/ViewProviderDragger.h @@ -25,16 +25,20 @@ #define GUI_VIEWPROVIDER_DRAGGER_H #include "ViewProviderDocumentObject.h" +#include "SoFCCSysDragger.h" +#include +#include class SoDragger; class SoTransform; -namespace Base { class Placement;} - namespace Gui { +namespace TaskView { + class TaskDialog; +} + class View3DInventorViewer; -class SoFCCSysDragger; /** * The base class for all view providers modifying the placement @@ -52,6 +56,13 @@ public: /// destructor. ~ViewProviderDragger() override; + App::PropertyPlacement TransformOrigin; + + Base::Placement getTransformOrigin() const { return TransformOrigin.getValue(); } + void setTransformOrigin(const Base::Placement& placement); + void resetTransformOrigin(); + +public: /** @name Edit methods */ //@{ bool doubleClicked() override; @@ -63,21 +74,40 @@ public: /*! synchronize From FC placement to Coin placement*/ static void updateTransform(const Base::Placement &from, SoTransform *to); + void updatePlacementFromDragger(); + void updateTransformFromDragger(); + + Base::Placement getDraggerPlacement() const; + void setDraggerPlacement(const Base::Placement& placement); + protected: bool setEdit(int ModNum) override; void unsetEdit(int ModNum) override; void setEditViewer(View3DInventorViewer*, int ModNum) override; void unsetEditViewer(View3DInventorViewer*) override; //@} - SoFCCSysDragger *csysDragger = nullptr; + + void onChanged(const App::Property* prop) override; + + /** + * Returns a newly create dialog for the part to be placed in the task view + * Must be reimplemented in subclasses. + */ + virtual TaskView::TaskDialog* getTransformDialog(); + + CoinPtr csysDragger = nullptr; private: - static void dragFinishCallback(void * data, SoDragger * d); - static void updatePlacementFromDragger(ViewProviderDragger *sudoThis, SoFCCSysDragger *draggerIn); + static void dragStartCallback(void *data, SoDragger *d); + static void dragFinishCallback(void *data, SoDragger *d); + static void dragMotionCallback(void *data, SoDragger *d); + + void updateDraggerPosition(); bool checkLink(); ViewProvider *_linkDragger = nullptr; + Base::Placement draggerPlacement { }; }; } // namespace Gui diff --git a/src/Gui/ViewProviderLink.cpp b/src/Gui/ViewProviderLink.cpp index a2cad7a67f..6e2fe24d0b 100644 --- a/src/Gui/ViewProviderLink.cpp +++ b/src/Gui/ViewProviderLink.cpp @@ -2893,7 +2893,7 @@ void ViewProviderLink::setEditViewer(Gui::View3DInventorViewer* viewer, int ModN dragger->setUpAutoScale(viewer->getSoRenderManager()->getCamera()); viewer->setupEditingRoot(pcDragger,&dragCtx->preTransform); - auto task = new TaskCSysDragger(this, dragger); + auto task = new TaskCSysDragger(nullptr, dragger); Gui::Control().showDialog(task); } } diff --git a/src/Mod/Part/App/AppPart.cpp b/src/Mod/Part/App/AppPart.cpp index c244d033a5..7668f6d9dd 100644 --- a/src/Mod/Part/App/AppPart.cpp +++ b/src/Mod/Part/App/AppPart.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include "ArcOfCirclePy.h" @@ -187,8 +188,12 @@ #include #include "MeasureClient.h" + #include +#include +#include + namespace Part { extern PyObject* initModule(); } @@ -572,7 +577,10 @@ PyMOD_INIT_FUNC(Part) .GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod/Part/Boolean"); Part::FuzzyHelper::setBooleanFuzzy(hGrp->GetFloat("BooleanFuzzy",10.0)); - + + Base::registerServiceImplementation(new AttacherSubObjectPlacement); + Base::registerServiceImplementation(new PartCenterOfMass); + PyMOD_Return(partModule); } // clang-format on diff --git a/src/Mod/Part/App/CMakeLists.txt b/src/Mod/Part/App/CMakeLists.txt index 71bedcac65..871d58c2df 100644 --- a/src/Mod/Part/App/CMakeLists.txt +++ b/src/Mod/Part/App/CMakeLists.txt @@ -553,6 +553,8 @@ SET(Part_SRCS PreCompiled.h ProgressIndicator.cpp ProgressIndicator.h + Services.cpp + Services.h TopoShape.cpp TopoShape.h TopoShapeCache.cpp diff --git a/src/Mod/Part/App/Services.cpp b/src/Mod/Part/App/Services.cpp new file mode 100644 index 0000000000..53eb46b947 --- /dev/null +++ b/src/Mod/Part/App/Services.cpp @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/**************************************************************************** + * * + * Copyright (c) 2024 Kacper Donat * + * * + * 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 "Services.h" + +AttacherSubObjectPlacement::AttacherSubObjectPlacement() + : attacher(std::make_unique()) +{ + attacher->setUp({}, Attacher::mmMidpoint); +} + +Base::Placement AttacherSubObjectPlacement::calculate(App::SubObjectT object, + Base::Placement basePlacement) const +{ + attacher->setReferences({object}); + return basePlacement.inverse() * attacher->calculateAttachedPlacement(basePlacement); +} + +std::optional PartCenterOfMass::ofDocumentObject(App::DocumentObject* object) const +{ + if (const auto feature = dynamic_cast(object)) { + const auto shape = feature->Shape.getShape(); + + if (const auto cog = shape.centerOfGravity()) { + const Base::Placement comPlacement { *cog, Base::Rotation { } }; + + return (feature->Placement.getValue().inverse() * comPlacement).getPosition(); + } + } + + return {}; +} \ No newline at end of file diff --git a/src/Mod/Part/App/Services.h b/src/Mod/Part/App/Services.h new file mode 100644 index 0000000000..217ff47e8d --- /dev/null +++ b/src/Mod/Part/App/Services.h @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/**************************************************************************** + * * + * Copyright (c) 2024 Kacper Donat * + * * + * 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 PART_SERVICES_H +#define PART_SERVICES_H + +#include +#include + +class AttacherSubObjectPlacement final: public App::SubObjectPlacementProvider +{ +public: + AttacherSubObjectPlacement(); + + Base::Placement calculate(App::SubObjectT object, Base::Placement basePlacement) const override; + +private: + std::unique_ptr attacher; +}; + +class PartCenterOfMass final: public App::CenterOfMassProvider +{ +public: + std::optional ofDocumentObject(App::DocumentObject* object) const override; +}; + +#endif // PART_SERVICES_H From fab235682db031e99bf7d08ca7eaee09aeb1fb04 Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Thu, 5 Dec 2024 14:42:13 +0100 Subject: [PATCH 09/19] Gui: Make ViewProviderLink based on ViewProviderDragger --- src/Gui/TaskCSysDragger.cpp | 30 +++-- src/Gui/ViewProviderDragger.cpp | 22 ++-- src/Gui/ViewProviderDragger.h | 1 + src/Gui/ViewProviderLink.cpp | 190 ++++++------------------------ src/Gui/ViewProviderLink.h | 13 +- src/Gui/ViewProviderLinkPy.xml | 6 - src/Gui/ViewProviderLinkPyImp.cpp | 17 +-- src/Mod/Part/App/Attacher.cpp | 11 +- src/Mod/Part/App/Services.cpp | 5 +- 9 files changed, 88 insertions(+), 207 deletions(-) diff --git a/src/Gui/TaskCSysDragger.cpp b/src/Gui/TaskCSysDragger.cpp index a4e38bcce3..ad9d8e61cf 100644 --- a/src/Gui/TaskCSysDragger.cpp +++ b/src/Gui/TaskCSysDragger.cpp @@ -101,9 +101,7 @@ TaskTransform::TaskTransform(Gui::ViewProviderDragger* vp, vp->resetTransformOrigin(); - if (auto geoFeature = vp->getObject()) { - originalPlacement = geoFeature->Placement.getValue(); - } + originalPlacement = vp->getObjectPlacement(); setupGui(); } @@ -400,23 +398,29 @@ void TaskTransform::onSelectionChanged(const SelectionChanges& msg) return; } + if (!msg.pOriginalMsg) { + // this should not happen! Original should contain unresolved message. + return; + } + auto doc = Application::Instance->getDocument(msg.pDocName); auto obj = doc->getDocument()->getObject(msg.pObjectName); - auto globalPlacement = App::GeoFeature::getGlobalPlacement(obj); + auto orgDoc = Application::Instance->getDocument(msg.pOriginalMsg->pDocName); + auto orgObj = orgDoc->getDocument()->getObject(msg.pOriginalMsg->pObjectName); + + auto globalPlacement = App::GeoFeature::getGlobalPlacement(obj, orgObj, msg.pOriginalMsg->pSubName); auto localPlacement = App::GeoFeature::getPlacementFromProp(obj, "Placement"); auto rootPlacement = App::GeoFeature::getGlobalPlacement(vp->getObject()); + auto attachedPlacement = subObjectPlacementProvider->calculate(msg.Object, localPlacement); - auto selectedObjectPlacement = rootPlacement.inverse() * globalPlacement - * subObjectPlacementProvider->calculate(msg.Object, localPlacement); + auto selectedObjectPlacement = rootPlacement.inverse() * globalPlacement * attachedPlacement; switch (selectionMode) { - case SelectionMode::SelectTransformOrigin: {auto label = msg.pOriginalMsg - ? QStringLiteral("%1#%2.%3") - .arg(QLatin1String(msg.pOriginalMsg->pObjectName), - QLatin1String(msg.pObjectName), - QLatin1String(msg.pSubName)) - : QStringLiteral("%1.%2").arg(QLatin1String(msg.pObjectName), QLatin1String(msg.pSubName)); + case SelectionMode::SelectTransformOrigin: {auto label = QStringLiteral("%1#%2.%3") + .arg(QLatin1String(msg.pOriginalMsg->pObjectName), + QLatin1String(msg.pObjectName), + QLatin1String(msg.pSubName)); ui->referenceLineEdit->setText(label); @@ -491,7 +495,7 @@ void TaskTransform::updateTransformOrigin() return {}; case PlacementMode::Centroid: if (const auto com = centerOfMassProvider->ofDocumentObject(vp->getObject())) { - return Base::Placement {*com, {}}; + return {*com, {}}; } return {}; case PlacementMode::Custom: diff --git a/src/Gui/ViewProviderDragger.cpp b/src/Gui/ViewProviderDragger.cpp index 4e60eab9c1..719c22e6a3 100644 --- a/src/Gui/ViewProviderDragger.cpp +++ b/src/Gui/ViewProviderDragger.cpp @@ -49,6 +49,7 @@ #include "ViewProviderDragger.h" #include "Utilities.h" +#include #include using namespace Gui; @@ -208,9 +209,7 @@ void ViewProviderDragger::setEditViewer(Gui::View3DInventorViewer* viewer, int M csysDragger->setUpAutoScale(viewer->getSoRenderManager()->getCamera()); auto mat = viewer->getDocument()->getEditingTransform(); - if (auto geoFeature = getObject()) { - mat *= geoFeature->Placement.getValue().inverse().toMatrix(); - } + mat *= getObjectPlacement().inverse().toMatrix(); viewer->getDocument()->setEditingTransform(mat); viewer->setupEditingRoot(csysDragger, &mat); @@ -249,13 +248,13 @@ void ViewProviderDragger::dragMotionCallback(void* data, SoDragger* d) void ViewProviderDragger::updatePlacementFromDragger() { - const auto geoFeature = getObject(); + const auto placement = getObject()->getPropertyByName("Placement"); - if (!geoFeature) { + if (!placement) { return; } - geoFeature->Placement.setValue(getDraggerPlacement() * getTransformOrigin().inverse()); + placement->setValue(getDraggerPlacement() * getTransformOrigin().inverse()); } void ViewProviderDragger::updateTransformFromDragger() @@ -266,6 +265,15 @@ void ViewProviderDragger::updateTransformFromDragger() pcTransform->rotation.setValue(Base::convertTo(placement.getRotation())); } +Base::Placement ViewProviderDragger::getObjectPlacement() const +{ + if (auto placement = getObject()->getPropertyByName("Placement")) { + return placement->getValue(); + } + + return {}; +} + Base::Placement ViewProviderDragger::getDraggerPlacement() const { const double translationStep = csysDragger->translationIncrement.getValue(); @@ -314,7 +322,7 @@ void ViewProviderDragger::updateDraggerPosition() return; } - auto placement = getObject()->Placement.getValue() * getTransformOrigin(); + auto placement = getObjectPlacement() * getTransformOrigin(); setDraggerPlacement(placement); } diff --git a/src/Gui/ViewProviderDragger.h b/src/Gui/ViewProviderDragger.h index ce5a6da50c..13d37efb23 100644 --- a/src/Gui/ViewProviderDragger.h +++ b/src/Gui/ViewProviderDragger.h @@ -77,6 +77,7 @@ public: void updatePlacementFromDragger(); void updateTransformFromDragger(); + Base::Placement getObjectPlacement() const; Base::Placement getDraggerPlacement() const; void setDraggerPlacement(const Base::Placement& placement); diff --git a/src/Gui/ViewProviderLink.cpp b/src/Gui/ViewProviderLink.cpp index 6e2fe24d0b..3e0d0b5244 100644 --- a/src/Gui/ViewProviderLink.cpp +++ b/src/Gui/ViewProviderLink.cpp @@ -1624,7 +1624,7 @@ static const char *_LinkElementIcon = "LinkElement"; ViewProviderLink::ViewProviderLink() :linkType(LinkTypeNone),hasSubName(false),hasSubElement(false) - ,useCenterballDragger(false),childVp(nullptr),overlayCacheKey(0) + ,childVp(nullptr),overlayCacheKey(0) { sPixmap = _LinkIcon; @@ -1670,7 +1670,7 @@ ViewProviderLink::~ViewProviderLink() } bool ViewProviderLink::isSelectable() const { - return !pcDragger && Selectable.getValue(); + return Selectable.getValue(); } void ViewProviderLink::attach(App::DocumentObject *pcObj) { @@ -1851,7 +1851,7 @@ void ViewProviderLink::updateDataPrivate(App::LinkBaseExtension *ext, const App: auto propLinkPlacement = ext->getLinkPlacementProperty(); if(!propLinkPlacement || propLinkPlacement == prop) { const auto &pla = static_cast(prop)->getValue(); - ViewProviderGeometryObject::updateTransform(pla, pcTransform); + // ViewProviderGeometryObject::updateTransform(pla, pcTransform); const auto &v = ext->getScaleVector(); if(canScale(v)) pcTransform->scaleFactor.setValue(v.x,v.y,v.z); @@ -2764,25 +2764,28 @@ ViewProvider *ViewProviderLink::startEditing(int mode) { } static thread_local bool _pendingTransform; - static thread_local Base::Matrix4D _editingTransform; + static thread_local Matrix4D _editingTransform; auto doc = Application::Instance->editDocument(); - if(mode==ViewProvider::Transform) { - if(_pendingTransform && doc) + if (mode == ViewProvider::Transform) { + if (_pendingTransform && doc) { doc->setEditingTransform(_editingTransform); + } - if(!initDraggingPlacement()) + if (!initDraggingPlacement()) { return nullptr; - if(useCenterballDragger) - pcDragger = CoinPtr(new SoCenterballDragger); - else - pcDragger = CoinPtr(new SoFCCSysDragger); - updateDraggingPlacement(dragCtx->initialPlacement,true); - pcDragger->addStartCallback(dragStartCallback, this); - pcDragger->addFinishCallback(dragFinishCallback, this); - pcDragger->addMotionCallback(dragMotionCallback, this); - return inherited::startEditing(mode); + } + + if (auto result = inherited::startEditing(mode)) { + csysDragger->addStartCallback(dragStartCallback, this); + csysDragger->addFinishCallback(dragFinishCallback, this); + csysDragger->addMotionCallback(dragMotionCallback, this); + + setDraggerPlacement(dragCtx->initialPlacement); + + return result; + } } if(!linkEdit()) { @@ -2839,6 +2842,7 @@ bool ViewProviderLink::setEdit(int ModNum) Selection().clearSelection(); return true; } + return inherited::setEdit(ModNum); } @@ -2849,125 +2853,21 @@ void ViewProviderLink::setEditViewer(Gui::View3DInventorViewer* viewer, int ModN return; } - if (pcDragger && viewer) - { - auto rootPickStyle = new SoPickStyle(); - rootPickStyle->style = SoPickStyle::UNPICKABLE; - static_cast( - viewer->getSceneGraph())->insertChild(rootPickStyle, 0); - - if(useCenterballDragger) { - auto dragger = static_cast(pcDragger.get()); - auto group = new SoAnnotation; - auto pickStyle = new SoPickStyle; - pickStyle->setOverride(true); - group->addChild(pickStyle); - group->addChild(pcDragger); - - // Because the dragger is not grouped with the actual geometry, - // we use an invisible cube sized by the bounding box obtained from - // initDraggingPlacement() to scale the centerball dragger properly - - auto * ss = static_cast(dragger->getPart("surroundScale", TRUE)); - ss->numNodesUpToContainer = 3; - ss->numNodesUpToReset = 2; - - auto *geoGroup = new SoGroup; - group->addChild(geoGroup); - auto *style = new SoDrawStyle; - style->style.setValue(SoDrawStyle::INVISIBLE); - style->setOverride(TRUE); - geoGroup->addChild(style); - auto *cube = new SoCube; - geoGroup->addChild(cube); - auto length = std::max(std::max(dragCtx->bbox.LengthX(), - dragCtx->bbox.LengthY()), dragCtx->bbox.LengthZ()); - cube->width = length; - cube->height = length; - cube->depth = length; - - viewer->setupEditingRoot(group,&dragCtx->preTransform); - } else { - auto dragger = static_cast(pcDragger.get()); - dragger->draggerSize.setValue(ViewParams::instance()->getDraggerScale()); - dragger->setUpAutoScale(viewer->getSoRenderManager()->getCamera()); - viewer->setupEditingRoot(pcDragger,&dragCtx->preTransform); - - auto task = new TaskCSysDragger(nullptr, dragger); - Gui::Control().showDialog(task); - } - } + ViewProviderDragger::setEditViewer(viewer, ModNum); } void ViewProviderLink::unsetEditViewer(Gui::View3DInventorViewer* viewer) { - SoNode *child = static_cast(viewer->getSceneGraph())->getChild(0); - if (child && child->isOfType(SoPickStyle::getClassTypeId())) - static_cast(viewer->getSceneGraph())->removeChild(child); - pcDragger.reset(); dragCtx.reset(); - Gui::Control().closeDialog(); + + inherited::unsetEditViewer(viewer); } -Base::Placement ViewProviderLink::currentDraggingPlacement() const -{ - // if there isn't an active dragger return a default placement - if (!pcDragger) - return Base::Placement(); - - SbVec3f v; - SbRotation r; - if (useCenterballDragger) { - auto dragger = static_cast(pcDragger.get()); - v = dragger->center.getValue(); - r = dragger->rotation.getValue(); - } - else { - auto dragger = static_cast(pcDragger.get()); - v = dragger->translation.getValue(); - r = dragger->rotation.getValue(); - } - - float q1,q2,q3,q4; - r.getValue(q1,q2,q3,q4); - return Base::Placement(Base::Vector3d(v[0],v[1],v[2]),Base::Rotation(q1,q2,q3,q4)); -} - -void ViewProviderLink::enableCenterballDragger(bool enable) { - if(enable == useCenterballDragger) - return; - if(pcDragger) - LINK_THROW(Base::RuntimeError,"Cannot change dragger during dragging"); - useCenterballDragger = enable; -} - -void ViewProviderLink::updateDraggingPlacement(const Base::Placement &pla, bool force) { - if(pcDragger && (force || currentDraggingPlacement()!=pla)) { - const auto &pos = pla.getPosition(); - const auto &rot = pla.getRotation(); - FC_LOG("updating dragger placement (" << pos.x << ", " << pos.y << ", " << pos.z << ')'); - if(useCenterballDragger) { - auto dragger = static_cast(pcDragger.get()); - SbBool wasenabled = dragger->enableValueChangedCallbacks(FALSE); - SbMatrix matrix; - matrix = convert(pla.toMatrix()); - dragger->center.setValue(SbVec3f(0,0,0)); - dragger->setMotionMatrix(matrix); - if (wasenabled) { - dragger->enableValueChangedCallbacks(TRUE); - dragger->valueChanged(); - } - }else{ - auto dragger = static_cast(pcDragger.get()); - dragger->translation.setValue(SbVec3f(pos.x,pos.y,pos.z)); - dragger->rotation.setValue(rot[0],rot[1],rot[2],rot[3]); - } - } -} - -bool ViewProviderLink::callDraggerProxy(const char *fname, bool update) { - if(!pcDragger) +bool ViewProviderLink::callDraggerProxy(const char* fname) { + if (!csysDragger) { return false; + } + Base::PyGILStateLocker lock; try { auto* proxy = getPropertyByName("Proxy"); @@ -2986,48 +2886,32 @@ bool ViewProviderLink::callDraggerProxy(const char *fname, bool update) { return true; } - if(update) { - auto ext = getLinkExtension(); - if(ext) { - const auto &pla = currentDraggingPlacement(); - auto prop = ext->getLinkPlacementProperty(); - if(!prop) - prop = ext->getPlacementProperty(); - if(prop) { - auto plaNew = pla * Base::Placement(dragCtx->mat); - if(prop->getValue()!=plaNew) - prop->setValue(plaNew); - } - updateDraggingPlacement(pla); - } - } return false; } void ViewProviderLink::dragStartCallback(void *data, SoDragger *) { auto me = static_cast(data); - me->dragCtx->initialPlacement = me->currentDraggingPlacement(); - if(!me->callDraggerProxy("onDragStart",false)) { - me->dragCtx->cmdPending = true; - me->getDocument()->openCommand(QT_TRANSLATE_NOOP("Command", "Link Transform")); - }else - me->dragCtx->cmdPending = false; + + me->dragCtx->initialPlacement = me->getDraggerPlacement(); + me->callDraggerProxy("onDragStart"); } void ViewProviderLink::dragFinishCallback(void *data, SoDragger *) { auto me = static_cast(data); - me->callDraggerProxy("onDragEnd",true); - if(me->dragCtx->cmdPending) { - if(me->currentDraggingPlacement() == me->dragCtx->initialPlacement) + me->callDraggerProxy("onDragEnd"); + + if (me->dragCtx->cmdPending) { + if (me->getDraggerPlacement() == me->dragCtx->initialPlacement) { me->getDocument()->abortCommand(); - else + } else { me->getDocument()->commitCommand(); + } } } void ViewProviderLink::dragMotionCallback(void *data, SoDragger *) { auto me = static_cast(data); - me->callDraggerProxy("onDragMotion",true); + me->callDraggerProxy("onDragMotion"); } void ViewProviderLink::updateLinks(ViewProvider *vp) { diff --git a/src/Gui/ViewProviderLink.h b/src/Gui/ViewProviderLink.h index fc296f2561..79ead6d8cf 100644 --- a/src/Gui/ViewProviderLink.h +++ b/src/Gui/ViewProviderLink.h @@ -184,10 +184,10 @@ protected: Py::Object PythonObject; }; -class GuiExport ViewProviderLink : public ViewProviderDocumentObject +class GuiExport ViewProviderLink : public ViewProviderDragger { PROPERTY_HEADER_WITH_OVERRIDE(Gui::ViewProviderLink); - using inherited = ViewProviderDocumentObject; + using inherited = ViewProviderDragger; public: App::PropertyBool OverrideMaterial; @@ -248,11 +248,6 @@ public: static void updateLinks(ViewProvider *vp); - void updateDraggingPlacement(const Base::Placement &pla, bool force=false); - Base::Placement currentDraggingPlacement() const; - void enableCenterballDragger(bool enable); - bool isUsingCenterballDragger() const { return useCenterballDragger; } - std::map getElementColors(const char *subname=nullptr) const override; void setElementColors(const std::map &colors) override; @@ -311,7 +306,7 @@ protected: ViewProvider *getLinkedView(bool real,const App::LinkBaseExtension *ext=nullptr) const; bool initDraggingPlacement(); - bool callDraggerProxy(const char *fname, bool update); + bool callDraggerProxy(const char* fname); private: static void dragStartCallback(void * data, SoDragger * d); @@ -323,7 +318,6 @@ protected: LinkType linkType; bool hasSubName; bool hasSubElement; - bool useCenterballDragger; struct DraggerContext{ Base::Matrix4D preTransform; @@ -333,7 +327,6 @@ protected: bool cmdPending; }; std::unique_ptr dragCtx; - CoinPtr pcDragger; ViewProviderDocumentObject *childVp; LinkInfoPtr childVpLink; mutable qint64 overlayCacheKey; diff --git a/src/Gui/ViewProviderLinkPy.xml b/src/Gui/ViewProviderLinkPy.xml index 88aaa0747f..09641c2cf8 100644 --- a/src/Gui/ViewProviderLinkPy.xml +++ b/src/Gui/ViewProviderLinkPy.xml @@ -19,12 +19,6 @@ - - - Get/set dragger type - - - Get the associated LinkView object diff --git a/src/Gui/ViewProviderLinkPyImp.cpp b/src/Gui/ViewProviderLinkPyImp.cpp index c1f3588e71..066c87f2e9 100644 --- a/src/Gui/ViewProviderLinkPyImp.cpp +++ b/src/Gui/ViewProviderLinkPyImp.cpp @@ -46,29 +46,16 @@ std::string ViewProviderLinkPy::representation() const Py::Object ViewProviderLinkPy::getDraggingPlacement() const { return Py::asObject(new Base::PlacementPy(new Base::Placement( - getViewProviderLinkPtr()->currentDraggingPlacement()))); + getViewProviderLinkPtr()->getDraggerPlacement()))); } void ViewProviderLinkPy::setDraggingPlacement(Py::Object arg) { if(!PyObject_TypeCheck(arg.ptr(),&Base::PlacementPy::Type)) throw Py::TypeError("expects a placement"); - getViewProviderLinkPtr()->updateDraggingPlacement( + getViewProviderLinkPtr()->setDraggerPlacement( *static_cast(arg.ptr())->getPlacementPtr()); } -Py::Boolean ViewProviderLinkPy::getUseCenterballDragger() const { - return {getViewProviderLinkPtr()->isUsingCenterballDragger()}; -} - -void ViewProviderLinkPy::setUseCenterballDragger(Py::Boolean arg) { - try { - getViewProviderLinkPtr()->enableCenterballDragger(arg); - }catch(const Base::Exception &e){ - e.setPyException(); - throw Py::Exception(); - } -} - Py::Object ViewProviderLinkPy::getLinkView() const { return Py::Object(getViewProviderLinkPtr()->getPyLinkView(),true); } diff --git a/src/Mod/Part/App/Attacher.cpp b/src/Mod/Part/App/Attacher.cpp index 8b9e594f57..84c1c08d2c 100644 --- a/src/Mod/Part/App/Attacher.cpp +++ b/src/Mod/Part/App/Attacher.cpp @@ -1901,6 +1901,11 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vector(objs[0])) { + return plane->Placement.getValue() * attachmentOffset; + } + auto shape = shapes.front(); auto geom = Geometry::fromShape(shape->getShape()); @@ -1929,7 +1934,7 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vectortangent(middle, direction); } - placement.setRotation(Base::Rotation::fromNormalVector(-direction)); + placement.setRotation(Base::Rotation::fromNormalVector(direction)); } } break; @@ -1941,8 +1946,10 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vectorgetLocation()); } else if (auto cone = dynamic_cast(geom.get())) { placement.setPosition(cone->getApex()); + } else if (auto com = shape->centerOfGravity()) { + placement.setPosition(*com); } else { - placement.setPosition(shape->centerOfGravity().value()); + placement.setPosition(shape->getBoundBox().GetCenter()); } if (auto rotation = surface->getRotation()) { diff --git a/src/Mod/Part/App/Services.cpp b/src/Mod/Part/App/Services.cpp index 53eb46b947..65eac11cdb 100644 --- a/src/Mod/Part/App/Services.cpp +++ b/src/Mod/Part/App/Services.cpp @@ -33,7 +33,10 @@ Base::Placement AttacherSubObjectPlacement::calculate(App::SubObjectT object, Base::Placement basePlacement) const { attacher->setReferences({object}); - return basePlacement.inverse() * attacher->calculateAttachedPlacement(basePlacement); + + auto calculatedAttachment = attacher->calculateAttachedPlacement(basePlacement); + + return basePlacement.inverse() * calculatedAttachment; } std::optional PartCenterOfMass::ofDocumentObject(App::DocumentObject* object) const From 25cf119c60db7398391dc8fad96513d8d4acbf22 Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Sun, 8 Dec 2024 17:44:22 +0100 Subject: [PATCH 10/19] Gui: Add Dragger placement preview in Transform dialog --- src/Gui/SoFCCSysDragger.cpp | 5 ++- src/Gui/SoFCCSysDragger.h | 1 + src/Gui/TaskCSysDragger.cpp | 65 ++++++++++++++++++++++++++++--------- src/Gui/TaskCSysDragger.h | 2 ++ 4 files changed, 57 insertions(+), 16 deletions(-) diff --git a/src/Gui/SoFCCSysDragger.cpp b/src/Gui/SoFCCSysDragger.cpp index a395b968b1..3d4ad6b650 100644 --- a/src/Gui/SoFCCSysDragger.cpp +++ b/src/Gui/SoFCCSysDragger.cpp @@ -1044,7 +1044,7 @@ SoFCCSysDragger::SoFCCSysDragger() SO_KIT_ADD_CATALOG_ENTRY(annotation, So3DAnnotation, TRUE, geomSeparator, "", TRUE); SO_KIT_ADD_CATALOG_ENTRY(scaleNode, SoScale, TRUE, annotation, "", TRUE); - + SO_KIT_ADD_CATALOG_ENTRY(pickStyle, SoPickStyle, TRUE, annotation, "", TRUE); // Translator SO_KIT_ADD_CATALOG_ENTRY(xTranslatorSwitch, SoSwitch, TRUE, annotation, "", TRUE); @@ -1309,6 +1309,9 @@ SoFCCSysDragger::SoFCCSysDragger() localScaleNode->scaleFactor.connectFrom(&scaleEngine->vector); autoScaleResult.connectFrom(&draggerSize); + SoPickStyle* localPickStyle = SO_GET_ANY_PART(this, "pickStyle", SoPickStyle); + localPickStyle->style = SoPickStyle::SHAPE_ON_TOP; + addValueChangedCallback(&SoFCCSysDragger::valueChangedCB); translationSensor.setFunction(&SoFCCSysDragger::translationSensorCB); diff --git a/src/Gui/SoFCCSysDragger.h b/src/Gui/SoFCCSysDragger.h index b759bcf70a..99c7a77ac6 100644 --- a/src/Gui/SoFCCSysDragger.h +++ b/src/Gui/SoFCCSysDragger.h @@ -217,6 +217,7 @@ class GuiExport SoFCCSysDragger : public SoDragger SO_KIT_HEADER(SoFCCSysDragger); SO_KIT_CATALOG_ENTRY_HEADER(annotation); SO_KIT_CATALOG_ENTRY_HEADER(scaleNode); + SO_KIT_CATALOG_ENTRY_HEADER(pickStyle); // Translator SO_KIT_CATALOG_ENTRY_HEADER(xTranslatorSwitch); SO_KIT_CATALOG_ENTRY_HEADER(yTranslatorSwitch); diff --git a/src/Gui/TaskCSysDragger.cpp b/src/Gui/TaskCSysDragger.cpp index ad9d8e61cf..6c9a90d734 100644 --- a/src/Gui/TaskCSysDragger.cpp +++ b/src/Gui/TaskCSysDragger.cpp @@ -115,6 +115,8 @@ TaskTransform::~TaskTransform() Gui::Application::Instance->commandManager() .getCommandByName("Std_PerspectiveCamera") ->setEnabled(true); + + savePreferences(); } void TaskTransform::dragStartCallback(void*, SoDragger*) @@ -244,12 +246,29 @@ void TaskTransform::setupGui() ui->transformOriginLayout, ui->referencePickerLayout}); + loadPreferences(); + updateInputLabels(); updateDraggerLabels(); updateIncrements(); updatePositionAndRotationUi(); } +void TaskTransform::loadPreferences() +{ + double lastTranslationIncrement = hGrp->GetFloat("LastTranslationIncrement", 1.0); + double lastRotationIncrement = hGrp->GetFloat("LastRotationIncrement", 5.0); + + ui->translationIncrementSpinBox->setValue(lastTranslationIncrement); + ui->rotationIncrementSpinBox->setValue(lastRotationIncrement); +} + +void TaskTransform::savePreferences() +{ + hGrp->SetFloat("LastTranslationIncrement", ui->translationIncrementSpinBox->rawValue()); + hGrp->SetFloat("LastRotationIncrement", ui->rotationIncrementSpinBox->rawValue()); +} + void TaskTransform::updatePositionAndRotationUi() const { @@ -338,22 +357,30 @@ void TaskTransform::setSelectionMode(SelectionMode mode) { Gui::Selection().clearSelection(); + SoPickStyle* draggerPickStyle = SO_GET_PART(dragger, "pickStyle", SoPickStyle); + ui->pickTransformOriginButton->setText(tr("Pick reference")); ui->alignToOtherObjectButton->setText(tr("Move to other object")); switch (mode) { case SelectionMode::SelectTransformOrigin: + draggerPickStyle->style = SoPickStyle::UNPICKABLE; + draggerPickStyle->setOverride(true); blockSelection(false); ui->referenceLineEdit->setText(tr("Select face, edge or vertex...")); ui->pickTransformOriginButton->setText(tr("Cancel")); break; case SelectionMode::SelectAlignTarget: + draggerPickStyle->style = SoPickStyle::UNPICKABLE; + draggerPickStyle->setOverride(true); ui->alignToOtherObjectButton->setText(tr("Cancel")); blockSelection(false); break; case SelectionMode::None: + draggerPickStyle->style = SoPickStyle::SHAPE_ON_TOP; + draggerPickStyle->setOverride(false); blockSelection(true); break; } @@ -390,7 +417,10 @@ TaskTransform::CoordinateSystem TaskTransform::currentCoordinateSystem() const void TaskTransform::onSelectionChanged(const SelectionChanges& msg) { - if (msg.Type != SelectionChanges::AddSelection) { + const auto isSupportedMessage = + msg.Type == SelectionChanges::AddSelection || msg.Type == SelectionChanges::SetPreselect; + + if (!isSupportedMessage) { return; } @@ -416,18 +446,21 @@ void TaskTransform::onSelectionChanged(const SelectionChanges& msg) auto selectedObjectPlacement = rootPlacement.inverse() * globalPlacement * attachedPlacement; - switch (selectionMode) { - case SelectionMode::SelectTransformOrigin: {auto label = QStringLiteral("%1#%2.%3") + auto label = QStringLiteral("%1#%2.%3") .arg(QLatin1String(msg.pOriginalMsg->pObjectName), QLatin1String(msg.pObjectName), QLatin1String(msg.pSubName)); - - ui->referenceLineEdit->setText(label); - - customTransformOrigin = selectedObjectPlacement; - - updateTransformOrigin(); + switch (selectionMode) { + case SelectionMode::SelectTransformOrigin: { + if (msg.Type == SelectionChanges::AddSelection) { + ui->referenceLineEdit->setText(label); + customTransformOrigin = selectedObjectPlacement; + updateTransformOrigin(); + setSelectionMode(SelectionMode::None); + } else { + vp->setTransformOrigin(selectedObjectPlacement); + } break; } @@ -435,8 +468,12 @@ void TaskTransform::onSelectionChanged(const SelectionChanges& msg) case SelectionMode::SelectAlignTarget: { vp->setDraggerPlacement(vp->getObjectPlacement() * selectedObjectPlacement); - vp->updateTransformFromDragger(); - vp->updatePlacementFromDragger(); + if (msg.Type == SelectionChanges::AddSelection) { + vp->updateTransformFromDragger(); + vp->updatePlacementFromDragger(); + + setSelectionMode(SelectionMode::None); + } break; } @@ -445,9 +482,6 @@ void TaskTransform::onSelectionChanged(const SelectionChanges& msg) // no-op break; } - - - setSelectionMode(SelectionMode::None); } void TaskTransform::onAlignRotationChanged() @@ -508,7 +542,8 @@ void TaskTransform::updateTransformOrigin() ui->referencePickerWidget->setVisible(placementMode == PlacementMode::Custom); if (placementMode == PlacementMode::Custom && !customTransformOrigin.has_value()) { - onPickTransformOrigin(); + setSelectionMode(SelectionMode::SelectTransformOrigin); + return; } auto transformOrigin = getTransformOrigin(placementMode); diff --git a/src/Gui/TaskCSysDragger.h b/src/Gui/TaskCSysDragger.h index aac80bf0b9..c7f7ed1d35 100644 --- a/src/Gui/TaskCSysDragger.h +++ b/src/Gui/TaskCSysDragger.h @@ -134,6 +134,8 @@ private: std::optional customTransformOrigin {}; Base::Placement originalPlacement {}; + + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/History/Dragger"); }; class TaskCSysDragger: public Gui::TaskView::TaskDialog From 736fbee539ceb16f2232a52cf0bd23205164d41b Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Wed, 11 Dec 2024 20:15:03 +0100 Subject: [PATCH 11/19] App: Fix GeoFeature::getGlobalPlacement when rootObj is Link This fixes a case when in case of rootObj being Link placement was not calculated correctly due to not changing of the document. While for original use it was fine as this was mostly used in case where there was defined non-link root for general case Links can be root objects of the document. --- src/App/GeoFeature.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/App/GeoFeature.cpp b/src/App/GeoFeature.cpp index 8d34144c8c..344aea1a58 100644 --- a/src/App/GeoFeature.cpp +++ b/src/App/GeoFeature.cpp @@ -311,6 +311,11 @@ Base::Placement GeoFeature::getGlobalPlacement(App::DocumentObject* targetObj, return plc; } + if (rootObj->isLink()) { + // Update doc in case its an external link. + doc = rootObj->getLinkedObject()->getDocument(); + } + for (auto& name : names) { App::DocumentObject* obj = doc->getObject(name.c_str()); if (!obj) { From a3bf63bae2f41474fd69bd8a7d7b6be16afab351 Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Wed, 11 Dec 2024 23:39:04 +0100 Subject: [PATCH 12/19] PD: Forward transform requests to Body --- src/Gui/TaskCSysDragger.cpp | 8 ++++++++ src/Gui/ViewProviderDragger.cpp | 25 ++++++++++++----------- src/Gui/ViewProviderDragger.h | 6 +++--- src/Mod/PartDesign/Gui/ViewProvider.cpp | 27 ++++++++++++++++++++++++- src/Mod/PartDesign/Gui/ViewProvider.h | 2 ++ 5 files changed, 52 insertions(+), 16 deletions(-) diff --git a/src/Gui/TaskCSysDragger.cpp b/src/Gui/TaskCSysDragger.cpp index 6c9a90d734..2b15f080dd 100644 --- a/src/Gui/TaskCSysDragger.cpp +++ b/src/Gui/TaskCSysDragger.cpp @@ -382,6 +382,9 @@ void TaskTransform::setSelectionMode(SelectionMode mode) draggerPickStyle->style = SoPickStyle::SHAPE_ON_TOP; draggerPickStyle->setOverride(false); blockSelection(true); + + vp->setTransformOrigin(vp->getTransformOrigin()); + break; } @@ -492,6 +495,11 @@ void TaskTransform::onAlignRotationChanged() void TaskTransform::onAlignToOtherObject() { + if (selectionMode == SelectionMode::SelectAlignTarget) { + setSelectionMode(SelectionMode::None); + return; + } + setSelectionMode(SelectionMode::SelectAlignTarget); } diff --git a/src/Gui/ViewProviderDragger.cpp b/src/Gui/ViewProviderDragger.cpp index 719c22e6a3..cf37d474a8 100644 --- a/src/Gui/ViewProviderDragger.cpp +++ b/src/Gui/ViewProviderDragger.cpp @@ -49,6 +49,7 @@ #include "ViewProviderDragger.h" #include "Utilities.h" +#include #include #include @@ -123,15 +124,17 @@ void ViewProviderDragger::setupContextMenu(QMenu* menu, QObject* receiver, const ViewProvider* ViewProviderDragger::startEditing(int mode) { - _linkDragger = nullptr; + forwardedViewProvider = nullptr; + auto ret = ViewProviderDocumentObject::startEditing(mode); if (!ret) { return ret; } - return _linkDragger ? _linkDragger : ret; + + return forwardedViewProvider ? forwardedViewProvider : ret; } -bool ViewProviderDragger::checkLink() +bool ViewProviderDragger::forwardToLink() { // Trying to detect if the editing request is forwarded by a link object, // usually by doubleClicked(). If so, we route the request back. There shall @@ -150,26 +153,24 @@ bool ViewProviderDragger::checkLink() return false; } - auto sobj = vpParent->getObject()->getSubObject(subname.c_str()); - if (!sobj || sobj == getObject() || sobj->getLinkedObject(true) != getObject()) { + if (vpParent == this) { return false; } - auto vp = Application::Instance->getViewProvider(sobj); - if (!vp) { + if (!vpParent->isDerivedFrom()) { return false; } - _linkDragger = vp->startEditing(ViewProvider::Transform); + forwardedViewProvider = vpParent->startEditing(ViewProvider::Transform); - return _linkDragger != nullptr; + return forwardedViewProvider != nullptr; } bool ViewProviderDragger::setEdit(int ModNum) { Q_UNUSED(ModNum); - if (checkLink()) { + if (forwardToLink()) { return true; } @@ -208,8 +209,8 @@ void ViewProviderDragger::setEditViewer(Gui::View3DInventorViewer* viewer, int M if (csysDragger && viewer) { csysDragger->setUpAutoScale(viewer->getSoRenderManager()->getCamera()); - auto mat = viewer->getDocument()->getEditingTransform(); - mat *= getObjectPlacement().inverse().toMatrix(); + auto originPlacement = App::GeoFeature::getGlobalPlacement(getObject()) * getObjectPlacement().inverse(); + auto mat = originPlacement.toMatrix(); viewer->getDocument()->setEditingTransform(mat); viewer->setupEditingRoot(csysDragger, &mat); diff --git a/src/Gui/ViewProviderDragger.h b/src/Gui/ViewProviderDragger.h index 13d37efb23..a2d9686bbb 100644 --- a/src/Gui/ViewProviderDragger.h +++ b/src/Gui/ViewProviderDragger.h @@ -90,6 +90,8 @@ protected: void onChanged(const App::Property* prop) override; + bool forwardToLink(); + /** * Returns a newly create dialog for the part to be placed in the task view * Must be reimplemented in subclasses. @@ -97,6 +99,7 @@ protected: virtual TaskView::TaskDialog* getTransformDialog(); CoinPtr csysDragger = nullptr; + ViewProvider *forwardedViewProvider = nullptr; private: static void dragStartCallback(void *data, SoDragger *d); @@ -105,9 +108,6 @@ private: void updateDraggerPosition(); - bool checkLink(); - - ViewProvider *_linkDragger = nullptr; Base::Placement draggerPlacement { }; }; diff --git a/src/Mod/PartDesign/Gui/ViewProvider.cpp b/src/Mod/PartDesign/Gui/ViewProvider.cpp index 2b1d364aec..9da111e119 100644 --- a/src/Mod/PartDesign/Gui/ViewProvider.cpp +++ b/src/Mod/PartDesign/Gui/ViewProvider.cpp @@ -81,7 +81,16 @@ void ViewProvider::setupContextMenu(QMenu* menu, QObject* receiver, const char* bool ViewProvider::setEdit(int ModNum) { - if (ModNum == ViewProvider::Default ) { + if (ModNum == ViewProvider::Transform) { + if (forwardToLink()) { + return true; + } + + // this is feature so we need to forward the transform to the body + forwardedViewProvider = getBodyViewProvider(); + return forwardedViewProvider->startEditing(ModNum); + } + else if (ModNum == ViewProvider::Default) { // When double-clicking on the item for this feature the // object unsets and sets its edit mode without closing // the task panel @@ -194,6 +203,22 @@ void ViewProvider::onChanged(const App::Property* prop) { PartGui::ViewProviderPartExt::onChanged(prop); } +Gui::ViewProvider* ViewProvider::startEditing(int ModNum) +{ + // in case of transform we forward the request to body + if (ModNum == Transform) { + forwardedViewProvider = nullptr; + + if (!ViewProviderPart::startEditing(ModNum)) { + return nullptr; + } + + return forwardedViewProvider; + } + + return ViewProviderPart::startEditing(ModNum); +} + void ViewProvider::setTipIcon(bool onoff) { isSetTipIcon = onoff; diff --git a/src/Mod/PartDesign/Gui/ViewProvider.h b/src/Mod/PartDesign/Gui/ViewProvider.h index 9b8e16c38d..6e303a0e6e 100644 --- a/src/Mod/PartDesign/Gui/ViewProvider.h +++ b/src/Mod/PartDesign/Gui/ViewProvider.h @@ -55,6 +55,8 @@ public: void updateData(const App::Property*) override; void onChanged(const App::Property* prop) override; + Gui::ViewProvider* startEditing(int ModNum) override; + void setTipIcon(bool onoff); //body mode means that the object is part of a body and that the body is used to set the From d08810c6495fea2cf79c604387cadc850039b5b8 Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Sun, 15 Dec 2024 16:58:52 +0100 Subject: [PATCH 13/19] Part: Extract methods from AttachEngine::readLinks --- src/Mod/Part/App/Attacher.cpp | 101 +++++++++++++++++++++------------- src/Mod/Part/App/Attacher.h | 23 ++++++++ 2 files changed, 85 insertions(+), 39 deletions(-) diff --git a/src/Mod/Part/App/Attacher.cpp b/src/Mod/Part/App/Attacher.cpp index 84c1c08d2c..5009ce260b 100644 --- a/src/Mod/Part/App/Attacher.cpp +++ b/src/Mod/Part/App/Attacher.cpp @@ -831,52 +831,20 @@ void AttachEngine::readLinks(const std::vector& objs, storage.reserve(objs.size()); shapes.resize(objs.size()); types.resize(objs.size()); + for (std::size_t i = 0; i < objs.size(); i++) { - auto* geof = dynamic_cast(objs[i]); + auto geof = extractGeoFeature(objs[i]); if (!geof) { - // Accept App::Links to GeoFeatures - geof = dynamic_cast(objs[i]->getLinkedObject()); - if (!geof) { - FC_THROWM(AttachEngineException, - "AttachEngine3D: attached to a non App::GeoFeature '" << objs[i]->getNameInDocument() << "'"); - } - } - Part::TopoShape shape; - try { - // getTopoShape support fully qualified subnames and should return shape with correct - // global placement. - shape = Part::Feature::getTopoShape(objs[i], subs[i].c_str(), true); - for (;;) { - if (shape.isNull()) { - FC_THROWM(AttachEngineException, - "AttachEngine3D: subshape not found " - << objs[i]->getNameInDocument() << '.' << subs[i]); - } - if (shape.shapeType() != TopAbs_COMPOUND - || shape.countSubShapes(TopAbs_SHAPE) != 1) { - break; - } - // auto extract the single sub-shape from a compound - shape = shape.getSubTopoShape(TopAbs_SHAPE, 1); - } - } - catch (Standard_Failure& e) { FC_THROWM(AttachEngineException, - "AttachEngine3D: subshape not found " << objs[i]->getNameInDocument() - << '.' << subs[i] << std::endl - << e.GetMessageString()); - } - catch (Base::CADKernelError& e) { - FC_THROWM(AttachEngineException, - "AttachEngine3D: subshape not found " << objs[i]->getNameInDocument() - << '.' << subs[i] << std::endl - << e.what()); + "AttachEngine3D: attached to a non App::GeoFeature '" + << objs[i]->getNameInDocument() << "'"); } + auto shape = extractSubShape(objs[i], subs[i]); if (shape.isNull()) { FC_THROWM(AttachEngineException, - "AttachEngine3D: null subshape " << objs[i]->getNameInDocument() << '.' - << subs[i]); + "AttachEngine3D: null subshape " << objs[i]->getNameInDocument() << '.' + << subs[i]); } storage.emplace_back(shape); @@ -885,12 +853,67 @@ void AttachEngine::readLinks(const std::vector& objs, // FIXME: unpack single-child compounds here? Compounds are not used so far, so it should be // considered later, when the need arises. types[i] = getShapeType(shapes[i]->getShape()); + if (subs[i].length() == 0) { types[i] = eRefType(types[i] | rtFlagHasPlacement); } } } +App::GeoFeature* AttachEngine::extractGeoFeature(App::DocumentObject *obj) +{ + if (auto geof = dynamic_cast(obj)) { + return geof; + } + + auto linkedObject = obj->getLinkedObject(); + if (auto linkedGeof = dynamic_cast(linkedObject)) { + return linkedGeof; + } + + return nullptr; +} + +TopoShape AttachEngine::extractSubShape(App::DocumentObject* obj, const std::string& subname) +{ + TopoShape shape; + + try { + // getTopoShape support fully qualified subnames and should return shape with correct + // global placement. + shape = Feature::getTopoShape(obj, subname.c_str(), true); + + for (;;) { + if (shape.isNull()) { + FC_THROWM(AttachEngineException, + "AttachEngine3D: subshape not found " << obj->getNameInDocument() << '.' + << subname); + } + + if (shape.shapeType() != TopAbs_COMPOUND || shape.countSubShapes(TopAbs_SHAPE) != 1) { + break; + } + + // auto extract the single sub-shape from a compound + shape = shape.getSubTopoShape(TopAbs_SHAPE, 1); + } + } + catch (Standard_Failure& e) { + FC_THROWM(AttachEngineException, + "AttachEngine3D: subshape not found " << obj->getNameInDocument() << '.' + << subname << std::endl + << e.GetMessageString()); + } + catch (Base::CADKernelError& e) { + FC_THROWM(AttachEngineException, + "AttachEngine3D: subshape not found " << obj->getNameInDocument() << '.' + << subname << std::endl + << e.what()); + } + + return shape; +} + void AttachEngine::throwWrongMode(eMapMode mmode) { std::stringstream errmsg; diff --git a/src/Mod/Part/App/Attacher.h b/src/Mod/Part/App/Attacher.h index 9b617d7547..a990069f0b 100644 --- a/src/Mod/Part/App/Attacher.h +++ b/src/Mod/Part/App/Attacher.h @@ -438,6 +438,29 @@ protected: static void throwWrongMode(eMapMode mmode); + /** + * Extracts GeoFeature instance from given DocumentObject. + * + * In case of object itself being GeoFeature it returns itself, in other cases (like links) + * the method should return pointer to associated GeoFeature or nullptr if none is available. + * + * @param obj The document object to extract the GeoFeature. + * + * @return App::GeoFeature pointer associated with this document object + */ + static App::GeoFeature* extractGeoFeature(App::DocumentObject* obj); + + /** + * Tries to extract sub shape from document object with given subname. + * + * @param obj DocumentObject containing the sub shape + * @param subname Name of the sub shape to extract + * + * @return Extracted sub shape. Can be null. + * + * @throws AttachEngineException If given sub shape does not exist or is impossible to obtain. + */ + static Part::TopoShape extractSubShape(App::DocumentObject* obj, const std::string& subname); }; From fe96e7e19483dc82b374ce77ff3cb87e09731ea9 Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Sun, 15 Dec 2024 18:15:19 +0100 Subject: [PATCH 14/19] Gui: Reset UVW coordinate system in transform when rotation changes --- src/Gui/TaskCSysDragger.cpp | 102 ++++++++++++++++++++++++++++-------- src/Gui/TaskCSysDragger.h | 17 ++++-- 2 files changed, 92 insertions(+), 27 deletions(-) diff --git a/src/Gui/TaskCSysDragger.cpp b/src/Gui/TaskCSysDragger.cpp index 2b15f080dd..df4efe35f5 100644 --- a/src/Gui/TaskCSysDragger.cpp +++ b/src/Gui/TaskCSysDragger.cpp @@ -101,7 +101,8 @@ TaskTransform::TaskTransform(Gui::ViewProviderDragger* vp, vp->resetTransformOrigin(); - originalPlacement = vp->getObjectPlacement(); + referencePlacement = vp->getObjectPlacement(); + globalOrigin = vp->getObjectPlacement() * App::GeoFeature::getGlobalPlacement(vp->getObject()).inverse(); setupGui(); } @@ -119,7 +120,7 @@ TaskTransform::~TaskTransform() savePreferences(); } -void TaskTransform::dragStartCallback(void*, SoDragger*) +void TaskTransform::dragStartCallback(void* data, SoDragger*) { // This is called when a manipulator is about to manipulating if (firstDrag) { @@ -127,13 +128,24 @@ void TaskTransform::dragStartCallback(void*, SoDragger*) QT_TRANSLATE_NOOP("Command", "Transform")); firstDrag = false; } + + auto task = static_cast(data); + + task->referenceRotation = task->vp->getDraggerPlacement().getRotation(); } void TaskTransform::dragMotionCallback(void* data, SoDragger* dragger) { auto task = static_cast(data); - task->updatePositionAndRotationUi(); + const auto currentRotation = task->referencePlacement.getRotation(); + const auto updatedRotation = task->vp->getDraggerPlacement().getRotation(); + + if (!updatedRotation.isSame(currentRotation, tolerance)) { + task->resetReferencePlacement(); + } else { + task->updatePositionAndRotationUi(); + } } void TaskTransform::loadPlacementModeItems() const @@ -157,7 +169,7 @@ void TaskTransform::loadPositionModeItems() const { ui->positionModeComboBox->clear(); ui->positionModeComboBox->addItem(tr("Local"), QVariant::fromValue(PositionMode::Local)); - ui->positionModeComboBox->addItem(tr("Absolute"), QVariant::fromValue(PositionMode::Absolute)); + ui->positionModeComboBox->addItem(tr("Global"), QVariant::fromValue(PositionMode::Global)); } void TaskTransform::setupGui() @@ -236,8 +248,8 @@ void TaskTransform::setupGui() connect(rotationSpinBox, qOverload(&QuantitySpinBox::valueChanged), this, - [this](double) { - onRotationChange(); + [=](double) { + onRotationChange(rotationSpinBox); }); } @@ -271,9 +283,14 @@ void TaskTransform::savePreferences() void TaskTransform::updatePositionAndRotationUi() const { + auto referencePlacement = currentCoordinateSystem().origin; + + if (positionMode == PositionMode::Local) { + referencePlacement.setRotation(referenceRotation); + } const auto xyzPlacement = vp->getDraggerPlacement(); - const auto uvwPlacement = currentCoordinateSystem().origin.inverse() * xyzPlacement; + const auto uvwPlacement = referencePlacement.inverse() * xyzPlacement; auto fixNegativeZero = [](const double value) { return std::fabs(value) < Base::Precision::Confusion() ? 0.0 : value; @@ -336,7 +353,7 @@ void TaskTransform::updateInputLabels() const void TaskTransform::updateDraggerLabels() const { auto coordinateSystem = - isDraggerAlignedToCoordinateSystem() ? absoluteCoordinateSystem() : localCoordinateSystem(); + isDraggerAlignedToCoordinateSystem() ? globalCoordinateSystem() : localCoordinateSystem(); auto [xLabel, yLabel, zLabel] = coordinateSystem.labels; @@ -398,24 +415,21 @@ TaskTransform::SelectionMode TaskTransform::getSelectionMode() const TaskTransform::CoordinateSystem TaskTransform::localCoordinateSystem() const { - auto origin = originalPlacement * vp->getTransformOrigin(); + auto origin = referencePlacement; origin.setRotation(vp->getDraggerPlacement().getRotation()); return {{"U", "V", "W"}, origin}; } -TaskTransform::CoordinateSystem TaskTransform::absoluteCoordinateSystem() const +TaskTransform::CoordinateSystem TaskTransform::globalCoordinateSystem() const { - return { - {"X", "Y", "Z"}, - Base::Placement {}, - }; + return {{"X", "Y", "Z"}, globalOrigin}; } TaskTransform::CoordinateSystem TaskTransform::currentCoordinateSystem() const { return ui->positionModeComboBox->currentIndex() == 0 ? localCoordinateSystem() - : absoluteCoordinateSystem(); + : globalCoordinateSystem(); } void TaskTransform::onSelectionChanged(const SelectionChanges& msg) @@ -472,8 +486,7 @@ void TaskTransform::onSelectionChanged(const SelectionChanges& msg) vp->setDraggerPlacement(vp->getObjectPlacement() * selectedObjectPlacement); if (msg.Type == SelectionChanges::AddSelection) { - vp->updateTransformFromDragger(); - vp->updatePlacementFromDragger(); + moveObjectToDragger(); setSelectionMode(SelectionMode::None); } @@ -503,6 +516,17 @@ void TaskTransform::onAlignToOtherObject() setSelectionMode(SelectionMode::SelectAlignTarget); } +void TaskTransform::moveObjectToDragger() +{ + vp->updateTransformFromDragger(); + vp->updatePlacementFromDragger(); + + resetReferenceRotation(); + resetReferencePlacement(); + + updatePositionAndRotationUi(); +} + void TaskTransform::onFlip() { auto placement = vp->getDraggerPlacement(); @@ -512,8 +536,7 @@ void TaskTransform::onFlip() vp->setDraggerPlacement(placement); - vp->updateTransformFromDragger(); - vp->updatePlacementFromDragger(); + moveObjectToDragger(); } void TaskTransform::onPickTransformOrigin() @@ -557,18 +580,31 @@ void TaskTransform::updateTransformOrigin() auto transformOrigin = getTransformOrigin(placementMode); if (isDraggerAlignedToCoordinateSystem()) { transformOrigin.setRotation( - (vp->getObjectPlacement().inverse() * absoluteCoordinateSystem().origin).getRotation()); + (vp->getObjectPlacement().inverse() * globalCoordinateSystem().origin).getRotation()); } vp->setTransformOrigin(transformOrigin); + resetReferencePlacement(); + resetReferenceRotation(); + updatePositionAndRotationUi(); updateDraggerLabels(); } +void TaskTransform::resetReferencePlacement() +{ + referencePlacement = vp->getDraggerPlacement(); +} + +void TaskTransform::resetReferenceRotation() +{ + referenceRotation = vp->getDraggerPlacement().getRotation(); +} + bool TaskTransform::isDraggerAlignedToCoordinateSystem() const { - return positionMode == PositionMode::Absolute && ui->alignRotationCheckBox->isChecked(); + return positionMode == PositionMode::Global && ui->alignRotationCheckBox->isChecked(); } void TaskTransform::onTransformOriginReset() @@ -604,14 +640,32 @@ void TaskTransform::onPositionChange() vp->updatePlacementFromDragger(); } -void TaskTransform::onRotationChange() +void TaskTransform::onRotationChange(QuantitySpinBox* changed) { + if (positionMode == PositionMode::Local) { + for (auto rotationSpinBox : {ui->xRotationSpinBox, + ui->yRotationSpinBox, + ui->zRotationSpinBox}) { + QSignalBlocker blocker(rotationSpinBox); + + // if any other spinbox contains non-zero value we need to reset rotation reference first + if (std::fabs(rotationSpinBox->rawValue()) > tolerance && rotationSpinBox != changed) { + resetReferenceRotation(); + rotationSpinBox->setValue(0.0); + } + } + } + const auto uvwRotation = Base::Rotation::fromEulerAngles(Base::Rotation::Intrinsic_XYZ, ui->xRotationSpinBox->rawValue(), ui->yRotationSpinBox->rawValue(), ui->zRotationSpinBox->rawValue()); - const auto xyzRotation = currentCoordinateSystem().origin.getRotation() * uvwRotation; + auto referenceRotation = positionMode == PositionMode::Local + ? this->referenceRotation + : currentCoordinateSystem().origin.getRotation(); + + const auto xyzRotation = referenceRotation * uvwRotation; const auto placement = vp->getDraggerPlacement(); @@ -619,6 +673,8 @@ void TaskTransform::onRotationChange() vp->updateTransformFromDragger(); vp->updatePlacementFromDragger(); + + resetReferencePlacement(); } TaskCSysDragger::TaskCSysDragger(ViewProviderDragger* vp, SoFCCSysDragger* dragger) diff --git a/src/Gui/TaskCSysDragger.h b/src/Gui/TaskCSysDragger.h index c7f7ed1d35..dc037200e8 100644 --- a/src/Gui/TaskCSysDragger.h +++ b/src/Gui/TaskCSysDragger.h @@ -49,10 +49,12 @@ class TaskTransform : public Gui::TaskView::TaskBox, public Gui::SelectionObserv { Q_OBJECT + static constexpr double tolerance = 1e-7; + public: enum class SelectionMode { None, SelectTransformOrigin, SelectAlignTarget }; enum class PlacementMode { ObjectOrigin, Centroid, Custom }; - enum class PositionMode { Local, Absolute }; + enum class PositionMode { Local, Global }; struct CoordinateSystem { @@ -76,7 +78,7 @@ public: void setSelectionMode(SelectionMode mode); SelectionMode getSelectionMode() const; - CoordinateSystem absoluteCoordinateSystem() const; + CoordinateSystem globalCoordinateSystem() const; CoordinateSystem localCoordinateSystem() const; CoordinateSystem currentCoordinateSystem() const; @@ -96,7 +98,7 @@ private Q_SLOTS: void onCoordinateSystemChange(int mode); void onPositionChange(); - void onRotationChange(); + void onRotationChange(QuantitySpinBox* changed); private: static inline bool firstDrag = true; @@ -117,6 +119,11 @@ private: void updateIncrements() const; void updateTransformOrigin(); + void resetReferencePlacement(); + void resetReferenceRotation(); + + void moveObjectToDragger(); + bool isDraggerAlignedToCoordinateSystem() const; ViewProviderDragger* vp; @@ -133,7 +140,9 @@ private: PositionMode positionMode { PositionMode::Local }; std::optional customTransformOrigin {}; - Base::Placement originalPlacement {}; + Base::Placement referencePlacement {}; + Base::Placement globalOrigin {}; + Base::Rotation referenceRotation {}; ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/History/Dragger"); }; From df2e0ba8959610cfb9dbcac28b743591f2982aed Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Wed, 18 Dec 2024 21:00:00 +0100 Subject: [PATCH 15/19] Gui: Fix crash on qBounds for imperial units --- src/Gui/QuantitySpinBox.cpp | 2 +- src/Gui/TaskCSysDragger.ui | 12 ------------ 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/src/Gui/QuantitySpinBox.cpp b/src/Gui/QuantitySpinBox.cpp index 9e8acddaca..1ab6a81e84 100644 --- a/src/Gui/QuantitySpinBox.cpp +++ b/src/Gui/QuantitySpinBox.cpp @@ -449,7 +449,7 @@ void QuantitySpinBox::updateEdit(const QString& text) edit->setText(text); - cursor = qBound(0, cursor, edit->displayText().size() - d->unitStr.size()); + cursor = qBound(0, cursor, qMax(0, edit->displayText().size() - d->unitStr.size())); if (selsize > 0) { edit->setSelection(0, cursor); } diff --git a/src/Gui/TaskCSysDragger.ui b/src/Gui/TaskCSysDragger.ui index a86539f01d..0371c734a7 100644 --- a/src/Gui/TaskCSysDragger.ui +++ b/src/Gui/TaskCSysDragger.ui @@ -126,16 +126,10 @@ - - mm - - - mm - @@ -188,9 +182,6 @@ - - mm - @@ -370,9 +361,6 @@ - - mm - 0.000000000000000 From 27c1eaa39c832fcf266d3da185d74c8afed304d2 Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Sun, 22 Dec 2024 18:44:28 +0100 Subject: [PATCH 16/19] Gui: Disable controls when selecting origin in transform dialog --- src/Gui/TaskCSysDragger.cpp | 20 ++++++++++++++++++++ src/Gui/TaskCSysDragger.h | 1 + 2 files changed, 21 insertions(+) diff --git a/src/Gui/TaskCSysDragger.cpp b/src/Gui/TaskCSysDragger.cpp index df4efe35f5..99161881c4 100644 --- a/src/Gui/TaskCSysDragger.cpp +++ b/src/Gui/TaskCSysDragger.cpp @@ -406,6 +406,8 @@ void TaskTransform::setSelectionMode(SelectionMode mode) } selectionMode = mode; + + updateSpinBoxesReadOnlyStatus(); } TaskTransform::SelectionMode TaskTransform::getSelectionMode() const @@ -592,6 +594,24 @@ void TaskTransform::updateTransformOrigin() updateDraggerLabels(); } +void TaskTransform::updateSpinBoxesReadOnlyStatus() const +{ + const bool isReadOnly = selectionMode != SelectionMode::None; + + const auto controls = { + ui->xPositionSpinBox, + ui->yPositionSpinBox, + ui->zPositionSpinBox, + ui->xRotationSpinBox, + ui->yRotationSpinBox, + ui->zRotationSpinBox, + }; + + for (const auto& control : controls) { + control->setReadOnly(isReadOnly); + } +} + void TaskTransform::resetReferencePlacement() { referencePlacement = vp->getDraggerPlacement(); diff --git a/src/Gui/TaskCSysDragger.h b/src/Gui/TaskCSysDragger.h index dc037200e8..35b5af60b4 100644 --- a/src/Gui/TaskCSysDragger.h +++ b/src/Gui/TaskCSysDragger.h @@ -118,6 +118,7 @@ private: void updateInputLabels() const; void updateIncrements() const; void updateTransformOrigin(); + void updateSpinBoxesReadOnlyStatus() const; void resetReferencePlacement(); void resetReferenceRotation(); From 178535980ec07bea7c9871a27ab0433d6775624a Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Fri, 27 Dec 2024 22:53:09 +0100 Subject: [PATCH 17/19] Gui: Use extrinsic angles for absolute position mode in transform dialog --- src/Gui/TaskCSysDragger.cpp | 10 ++++++++-- src/Gui/TaskCSysDragger.h | 2 ++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Gui/TaskCSysDragger.cpp b/src/Gui/TaskCSysDragger.cpp index 99161881c4..15d3a0bff4 100644 --- a/src/Gui/TaskCSysDragger.cpp +++ b/src/Gui/TaskCSysDragger.cpp @@ -310,7 +310,7 @@ void TaskTransform::updatePositionAndRotationUi() const auto blockers = {QSignalBlocker(x), QSignalBlocker(y), QSignalBlocker(z)}; double alpha, beta, gamma; - rot.getEulerAngles(Base::Rotation::Intrinsic_XYZ, alpha, beta, gamma); + rot.getEulerAngles(eulerSequence(), alpha, beta, gamma); x->setValue(fixNegativeZero(alpha)); y->setValue(fixNegativeZero(beta)); @@ -434,6 +434,12 @@ TaskTransform::CoordinateSystem TaskTransform::currentCoordinateSystem() const : globalCoordinateSystem(); } +Base::Rotation::EulerSequence TaskTransform::eulerSequence() const +{ + return positionMode == PositionMode::Local ? Base::Rotation::Intrinsic_XYZ + : Base::Rotation::Extrinsic_XYZ; +} + void TaskTransform::onSelectionChanged(const SelectionChanges& msg) { const auto isSupportedMessage = @@ -676,7 +682,7 @@ void TaskTransform::onRotationChange(QuantitySpinBox* changed) } } - const auto uvwRotation = Base::Rotation::fromEulerAngles(Base::Rotation::Intrinsic_XYZ, + const auto uvwRotation = Base::Rotation::fromEulerAngles(eulerSequence(), ui->xRotationSpinBox->rawValue(), ui->yRotationSpinBox->rawValue(), ui->zRotationSpinBox->rawValue()); diff --git a/src/Gui/TaskCSysDragger.h b/src/Gui/TaskCSysDragger.h index 35b5af60b4..ade395b2d8 100644 --- a/src/Gui/TaskCSysDragger.h +++ b/src/Gui/TaskCSysDragger.h @@ -82,6 +82,8 @@ public: CoordinateSystem localCoordinateSystem() const; CoordinateSystem currentCoordinateSystem() const; + Base::Rotation::EulerSequence eulerSequence() const; + private: void onSelectionChanged(const SelectionChanges& msg) override; From 62cdaaff56efc797dc8044782b977ef5b3aa5d33 Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Sat, 28 Dec 2024 00:01:38 +0100 Subject: [PATCH 18/19] Gui: Zero out rotation only if more than 1 axis changed --- src/Gui/TaskCSysDragger.cpp | 62 ++++++++++++++++++--------------- src/Gui/TaskCSysDragger.h | 18 +++++----- src/Gui/ViewProviderDragger.cpp | 5 +++ src/Gui/ViewProviderDragger.h | 13 +++++++ 4 files changed, 60 insertions(+), 38 deletions(-) diff --git a/src/Gui/TaskCSysDragger.cpp b/src/Gui/TaskCSysDragger.cpp index 15d3a0bff4..baf6217739 100644 --- a/src/Gui/TaskCSysDragger.cpp +++ b/src/Gui/TaskCSysDragger.cpp @@ -128,24 +128,39 @@ void TaskTransform::dragStartCallback(void* data, SoDragger*) QT_TRANSLATE_NOOP("Command", "Transform")); firstDrag = false; } - - auto task = static_cast(data); - - task->referenceRotation = task->vp->getDraggerPlacement().getRotation(); } void TaskTransform::dragMotionCallback(void* data, SoDragger* dragger) { auto task = static_cast(data); - const auto currentRotation = task->referencePlacement.getRotation(); + const auto currentRotation = task->vp->getOriginalDraggerPlacement().getRotation(); const auto updatedRotation = task->vp->getDraggerPlacement().getRotation(); + const auto rotationAxisHasChanged = [task](auto first, auto second) { + double alpha, beta, gamma; + + (first.inverse() * second).getEulerAngles(task->eulerSequence(), alpha, beta, gamma); + + auto angles = {alpha, beta, gamma}; + const int changed = std::count_if(angles.begin(), angles.end(), [](double angle) { + return std::fabs(angle) > tolerance; + }); + + // if representation of both differs by more than one axis the axis of rotation must be + // different + return changed > 1; + }; + if (!updatedRotation.isSame(currentRotation, tolerance)) { task->resetReferencePlacement(); - } else { - task->updatePositionAndRotationUi(); + + if (rotationAxisHasChanged(task->referenceRotation, updatedRotation)) { + task->referenceRotation = currentRotation; + } } + + task->updatePositionAndRotationUi(); } void TaskTransform::loadPlacementModeItems() const @@ -283,11 +298,7 @@ void TaskTransform::savePreferences() void TaskTransform::updatePositionAndRotationUi() const { - auto referencePlacement = currentCoordinateSystem().origin; - - if (positionMode == PositionMode::Local) { - referencePlacement.setRotation(referenceRotation); - } + const auto referencePlacement = currentCoordinateSystem().origin; const auto xyzPlacement = vp->getDraggerPlacement(); const auto uvwPlacement = referencePlacement.inverse() * xyzPlacement; @@ -317,24 +328,17 @@ void TaskTransform::updatePositionAndRotationUi() const z->setValue(fixNegativeZero(gamma)); }; - auto setValues = [&](const Base::Placement& placement, - auto* px, - auto* py, - auto* pz, - auto* rx, - auto* ry, - auto* rz) { - setPositionValues(placement.getPosition(), px, py, pz); - setRotationValues(placement.getRotation(), rx, ry, rz); - }; + setPositionValues(uvwPlacement.getPosition(), + ui->xPositionSpinBox, + ui->yPositionSpinBox, + ui->zPositionSpinBox); - setValues(uvwPlacement, - ui->xPositionSpinBox, - ui->yPositionSpinBox, - ui->zPositionSpinBox, - ui->xRotationSpinBox, - ui->yRotationSpinBox, - ui->zRotationSpinBox); + setRotationValues(positionMode == PositionMode::Local + ? referenceRotation.inverse() * xyzPlacement.getRotation() + : uvwPlacement.getRotation(), + ui->xRotationSpinBox, + ui->yRotationSpinBox, + ui->zRotationSpinBox); } void TaskTransform::updateInputLabels() const diff --git a/src/Gui/TaskCSysDragger.h b/src/Gui/TaskCSysDragger.h index ade395b2d8..0f7fed2cca 100644 --- a/src/Gui/TaskCSysDragger.h +++ b/src/Gui/TaskCSysDragger.h @@ -75,15 +75,6 @@ public: Base::provideService()); ~TaskTransform() override; - void setSelectionMode(SelectionMode mode); - SelectionMode getSelectionMode() const; - - CoordinateSystem globalCoordinateSystem() const; - CoordinateSystem localCoordinateSystem() const; - CoordinateSystem currentCoordinateSystem() const; - - Base::Rotation::EulerSequence eulerSequence() const; - private: void onSelectionChanged(const SelectionChanges& msg) override; @@ -107,6 +98,15 @@ private: static void dragStartCallback(void* data, SoDragger* d); static void dragMotionCallback(void* data, SoDragger* d); + void setSelectionMode(SelectionMode mode); + SelectionMode getSelectionMode() const; + + CoordinateSystem globalCoordinateSystem() const; + CoordinateSystem localCoordinateSystem() const; + CoordinateSystem currentCoordinateSystem() const; + + Base::Rotation::EulerSequence eulerSequence() const; + void setupGui(); void loadPreferences(); diff --git a/src/Gui/ViewProviderDragger.cpp b/src/Gui/ViewProviderDragger.cpp index cf37d474a8..2484ee197e 100644 --- a/src/Gui/ViewProviderDragger.cpp +++ b/src/Gui/ViewProviderDragger.cpp @@ -308,6 +308,11 @@ Base::Placement ViewProviderDragger::getDraggerPlacement() const ); } +Base::Placement ViewProviderDragger::getOriginalDraggerPlacement() const +{ + return draggerPlacement; +} + void ViewProviderDragger::setDraggerPlacement(const Base::Placement& placement) { csysDragger->translation.setValue(Base::convertTo(placement.getPosition())); diff --git a/src/Gui/ViewProviderDragger.h b/src/Gui/ViewProviderDragger.h index a2d9686bbb..21808413a7 100644 --- a/src/Gui/ViewProviderDragger.h +++ b/src/Gui/ViewProviderDragger.h @@ -56,10 +56,16 @@ public: /// destructor. ~ViewProviderDragger() override; + /// Origin used when object is transformed. It temporarily changes the origin of object. + /// Dragger is normally placed at the transform origin, unless explicitly overridden via + /// ViewProviderDragger#setDraggerPlacement() method. App::PropertyPlacement TransformOrigin; + /// Convenience method to obtain the transform origin Base::Placement getTransformOrigin() const { return TransformOrigin.getValue(); } + /// Convenience method to set the transform origin void setTransformOrigin(const Base::Placement& placement); + /// Resets transform origin to the object origin void resetTransformOrigin(); public: @@ -74,11 +80,18 @@ public: /*! synchronize From FC placement to Coin placement*/ static void updateTransform(const Base::Placement &from, SoTransform *to); + /// updates placement of object based on dragger position void updatePlacementFromDragger(); + /// updates transform of object based on dragger position, can be used to preview movement void updateTransformFromDragger(); + /// Gets object placement relative to its coordinate system Base::Placement getObjectPlacement() const; + /// Gets current dragger placement, including current dragger movement Base::Placement getDraggerPlacement() const; + /// Gets original dragger placement, without current dragger movement + Base::Placement getOriginalDraggerPlacement() const; + /// Sets placement of dragger relative to objects origin void setDraggerPlacement(const Base::Placement& placement); protected: From 8062779ffea193f69b7750b79bdf58cc38ef5aae Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Sat, 28 Dec 2024 00:04:09 +0100 Subject: [PATCH 19/19] Gui: Initialize reference rotation properly --- src/Gui/TaskCSysDragger.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Gui/TaskCSysDragger.cpp b/src/Gui/TaskCSysDragger.cpp index baf6217739..c59a9f3f89 100644 --- a/src/Gui/TaskCSysDragger.cpp +++ b/src/Gui/TaskCSysDragger.cpp @@ -25,8 +25,6 @@ #include #include #include -#include -#include #endif #include @@ -102,6 +100,8 @@ TaskTransform::TaskTransform(Gui::ViewProviderDragger* vp, vp->resetTransformOrigin(); referencePlacement = vp->getObjectPlacement(); + referenceRotation = referencePlacement.getRotation(); + globalOrigin = vp->getObjectPlacement() * App::GeoFeature::getGlobalPlacement(vp->getObject()).inverse(); setupGui();