From fc56730648a1fa406aa74b5c740aa9edc9dbdcd2 Mon Sep 17 00:00:00 2001 From: PaddleStroke Date: Tue, 26 Nov 2024 09:27:40 +0100 Subject: [PATCH 01/15] Core: Datums: Fix axis placement and add migration script. --- src/App/Datums.cpp | 68 ++++++++++++++++++- src/App/Datums.h | 4 ++ src/Gui/ViewProviderLine.cpp | 8 +-- src/Mod/Part/App/PartFeature.cpp | 2 +- src/Mod/PartDesign/App/FeatureSketchBased.cpp | 7 +- src/Mod/Test/Document.py | 6 +- 6 files changed, 81 insertions(+), 14 deletions(-) diff --git a/src/App/Datums.cpp b/src/App/Datums.cpp index 75a6b06e2b..c58c34b72f 100644 --- a/src/App/Datums.cpp +++ b/src/App/Datums.cpp @@ -207,9 +207,9 @@ const std::vector& LocalCoordinateSystem::getS { static const std::vector setupData = { // clang-format off - {App::Line::getClassTypeId(), AxisRoles[0], tr("X-axis"), Base::Rotation()}, - {App::Line::getClassTypeId(), AxisRoles[1], tr("Y-axis"), Base::Rotation(Base::Vector3d(1, 1, 1), M_PI * 2 / 3)}, - {App::Line::getClassTypeId(), AxisRoles[2], tr("Z-axis"), Base::Rotation(Base::Vector3d(1,-1, 1), M_PI * 2 / 3)}, + {App::Line::getClassTypeId(), AxisRoles[0], tr("X-axis"), Base::Rotation(Base::Vector3d(1, 1, 1), M_PI * 2 / 3)}, + {App::Line::getClassTypeId(), AxisRoles[1], tr("Y-axis"), Base::Rotation(Base::Vector3d(-1, 1, 1), M_PI * 2 / 3)}, + {App::Line::getClassTypeId(), AxisRoles[2], tr("Z-axis"), Base::Rotation()}, {App::Plane::getClassTypeId(), PlaneRoles[0], tr("XY-plane"), Base::Rotation()}, {App::Plane::getClassTypeId(), PlaneRoles[1], tr("XZ-plane"), Base::Rotation(1.0, 0.0, 0.0, 1.0)}, {App::Plane::getClassTypeId(), PlaneRoles[2], tr("YZ-plane"), Base::Rotation(Base::Vector3d(1, 1, 1), M_PI * 2 / 3)}, @@ -277,6 +277,68 @@ void LocalCoordinateSystem::unsetupObject() } } +void LocalCoordinateSystem::onDocumentRestored() +{ + GeoFeature::onDocumentRestored(); + + // In 0.22 origins did not have point. + migrateOriginPoint(); + + // In 0.22 the axis placement were wrong. The X axis had identity placement instead of the Z. + // This was fixed but we need to migrate old files. + migrateXAxisPlacement(); +} + +void LocalCoordinateSystem::migrateOriginPoint() +{ + auto features = OriginFeatures.getValues(); + + auto featIt = std::find_if(features.begin(), features.end(), + [](App::DocumentObject* obj) { + return obj->isDerivedFrom(App::DatumElement::getClassTypeId()) && + strcmp(static_cast(obj)->Role.getValue(), PointRoles[0]) == 0; + }); + if (featIt == features.end()) { + // origin point not found let's add it + auto data = getData(PointRoles[0]); + auto* origin = createDatum(data); + features.push_back(origin); + OriginFeatures.setValues(features); + } +} + +void LocalCoordinateSystem::migrateXAxisPlacement() +{ + auto features = OriginFeatures.getValues(); + + bool migrated = false; + + const auto& setupData = getSetupData(); + for (auto* obj : features) { + auto* feature = dynamic_cast (obj); + if (!feature) { continue; } + for (auto data : setupData) { + // ensure the rotation is correct for the role + if (std::strcmp(feature->Role.getValue(), data.role) == 0) { + if (!feature->Placement.getValue().getRotation().isSame(data.rot)) { + feature->Placement.setValue(Base::Placement(Base::Vector3d(), data.rot)); + migrated = true; + } + } + } + } + + static bool warnedUser = false; + if (!warnedUser && migrated) { + Base::Console().Warning("This file was created with an older version of FreeCAD." + "It had some origin's X axis with incorrect placement, which is being fixed now.\n" + "But if you save the file here and open this file back in an " + "older version of FreeCAD, you will find the origin objects axis looking incorrect." + "And if your file is using the origin axis as references it will likely be broken.\n"); + warnedUser = true; + } +} + // ---------------------------------------------------------------------------- LocalCoordinateSystem::LCSExtension::LCSExtension(LocalCoordinateSystem* obj) diff --git a/src/App/Datums.h b/src/App/Datums.h index 8e3d567ea1..002790e78b 100644 --- a/src/App/Datums.h +++ b/src/App/Datums.h @@ -213,6 +213,7 @@ protected: void setupObject() override; /// Removes all planes and axis if they are still linked to the document void unsetupObject() override; + void onDocumentRestored() override; private: struct SetupData; @@ -245,6 +246,9 @@ private: DatumElement* createDatum(SetupData& data); SetupData getData(const char* role); + + void migrateOriginPoint(); + void migrateXAxisPlacement(); }; } // namespace App diff --git a/src/Gui/ViewProviderLine.cpp b/src/Gui/ViewProviderLine.cpp index edf31b38cc..3cf3fdb85b 100644 --- a/src/Gui/ViewProviderLine.cpp +++ b/src/Gui/ViewProviderLine.cpp @@ -83,12 +83,12 @@ void ViewProviderLine::attach(App::DocumentObject *obj) { SbVec3f verts[2]; if (noRole) { - verts[0] = SbVec3f(2 * size, 0, 0); + verts[0] = SbVec3f(0, 0, 2 * size); verts[1] = SbVec3f(0, 0, 0); } else { - verts[0] = SbVec3f(size, 0, 0); - verts[1] = SbVec3f(0.2 * size, 0, 0); + verts[0] = SbVec3f(0, 0, size); + verts[1] = SbVec3f(0, 0, 0.2 * size); } // indexes used to create the edges @@ -107,7 +107,7 @@ void ViewProviderLine::attach(App::DocumentObject *obj) { sep->addChild ( pLines ); auto textTranslation = new SoTranslation (); - textTranslation->translation.setValue ( SbVec3f ( size * 1.1, 0, 0 ) ); + textTranslation->translation.setValue(SbVec3f(0, 0, size * 1.1)); sep->addChild ( textTranslation ); auto ps = new SoPickStyle(); diff --git a/src/Mod/Part/App/PartFeature.cpp b/src/Mod/Part/App/PartFeature.cpp index 48997300d4..ee3ab6948d 100644 --- a/src/Mod/Part/App/PartFeature.cpp +++ b/src/Mod/Part/App/PartFeature.cpp @@ -1018,7 +1018,7 @@ static TopoShape _getTopoShape(const App::DocumentObject* obj, if (linked->isDerivedFrom(App::Line::getClassTypeId())) { static TopoDS_Shape _shape; if (_shape.IsNull()) { - BRepBuilderAPI_MakeEdge builder(gp_Lin(gp_Pnt(0, 0, 0), gp_Dir(1, 0, 0))); + BRepBuilderAPI_MakeEdge builder(gp_Lin(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1))); _shape = builder.Shape(); _shape.Infinite(Standard_True); } diff --git a/src/Mod/PartDesign/App/FeatureSketchBased.cpp b/src/Mod/PartDesign/App/FeatureSketchBased.cpp index e8a467eff9..92f22da99a 100644 --- a/src/Mod/PartDesign/App/FeatureSketchBased.cpp +++ b/src/Mod/PartDesign/App/FeatureSketchBased.cpp @@ -1357,9 +1357,10 @@ void ProfileBased::getAxis(const App::DocumentObject * pcReferenceAxis, const st } if (pcReferenceAxis->isDerivedFrom()) { - const App::Line* line = static_cast(pcReferenceAxis); - base = Base::Vector3d(0, 0, 0); - line->Placement.getValue().multVec(Base::Vector3d(1, 0, 0), dir); + auto* line = static_cast(pcReferenceAxis); + Base::Placement plc = line->Placement.getValue(); + base = plc.getPosition(); + plc.getRotation().multVec(Base::Vector3d(0, 0, 1), dir); verifyAxisFunc(checkAxis, sketchplane, gp_Dir(dir.x, dir.y, dir.z)); return; diff --git a/src/Mod/Test/Document.py b/src/Mod/Test/Document.py index 18ba7b1976..e59ab99e78 100644 --- a/src/Mod/Test/Document.py +++ b/src/Mod/Test/Document.py @@ -338,17 +338,17 @@ class DocumentBasicCases(unittest.TestCase): res = obj.getSubObject("X_Axis", retType=2) self.assertEqual( - res[1].multVec(FreeCAD.Vector(1, 0, 0)).getAngle(FreeCAD.Vector(1, 0, 0)), 0.0 + res[1].multVec(FreeCAD.Vector(0, 0, 1)).getAngle(FreeCAD.Vector(1, 0, 0)), 0.0 ) res = obj.getSubObject("Y_Axis", retType=2) self.assertEqual( - res[1].multVec(FreeCAD.Vector(1, 0, 0)).getAngle(FreeCAD.Vector(0, 1, 0)), 0.0 + res[1].multVec(FreeCAD.Vector(0, 0, 1)).getAngle(FreeCAD.Vector(0, 1, 0)), 0.0 ) res = obj.getSubObject("Z_Axis", retType=2) self.assertEqual( - res[1].multVec(FreeCAD.Vector(1, 0, 0)).getAngle(FreeCAD.Vector(0, 0, 1)), 0.0 + res[1].multVec(FreeCAD.Vector(0, 0, 1)).getAngle(FreeCAD.Vector(0, 0, 1)), 0.0 ) res = obj.getSubObject("XY_Plane", retType=2) From 48dbdacdbd64118b0b553feaa54c64c03893904f Mon Sep 17 00:00:00 2001 From: PaddleStroke Date: Fri, 20 Sep 2024 10:46:01 +0200 Subject: [PATCH 02/15] Part: add datum objects and commands to create them. --- src/App/Datums.cpp | 25 ++- src/App/Datums.h | 25 ++- src/Gui/CommandStructure.cpp | 4 +- src/Mod/Part/App/AppPart.cpp | 5 + src/Mod/Part/App/AttachExtension.h | 1 - src/Mod/Part/App/CMakeLists.txt | 2 + src/Mod/Part/App/Datums.cpp | 63 ++++++ src/Mod/Part/App/Datums.h | 84 ++++++++ src/Mod/Part/Gui/AppPartGui.cpp | 5 + src/Mod/Part/Gui/CMakeLists.txt | 2 + src/Mod/Part/Gui/Command.cpp | 196 ++++++++++++++++++ .../Part/Gui/ViewProviderAttachExtension.cpp | 4 + .../Part/Gui/ViewProviderAttachExtension.h | 1 - src/Mod/Part/Gui/ViewProviderDatum.cpp | 94 +++++++++ src/Mod/Part/Gui/ViewProviderDatum.h | 84 ++++++++ src/Mod/Part/Gui/WorkbenchManipulator.cpp | 16 ++ src/Mod/Part/Gui/WorkbenchManipulator.h | 1 + src/Mod/PartDesign/App/Body.cpp | 4 +- src/Mod/PartDesign/Gui/ViewProviderBody.cpp | 24 ++- src/Mod/PartDesign/Gui/ViewProviderDatum.cpp | 25 ++- src/Mod/PartDesign/Gui/ViewProviderDatum.h | 25 ++- 21 files changed, 626 insertions(+), 64 deletions(-) create mode 100644 src/Mod/Part/App/Datums.cpp create mode 100644 src/Mod/Part/App/Datums.h create mode 100644 src/Mod/Part/Gui/ViewProviderDatum.cpp create mode 100644 src/Mod/Part/Gui/ViewProviderDatum.h diff --git a/src/App/Datums.cpp b/src/App/Datums.cpp index c58c34b72f..3157ea4cd3 100644 --- a/src/App/Datums.cpp +++ b/src/App/Datums.cpp @@ -3,22 +3,21 @@ * Copyright (c) 2015 Alexander Golubev (Fat-Zer) * * Copyright (c) 2024 Ondsel (PL Boyer) * * * - * This file is part of the FreeCAD CAx development system. * + * This file is part of FreeCAD. * * * - * This library is free software; you can redistribute it and/or * - * modify it under the terms of the GNU Library General Public * - * License as published by the Free Software Foundation; either * - * version 2 of the License, or (at your option) any later version. * + * 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. * * * - * This library 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 Library General Public License for more details. * + * 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 Library General Public * - * License along with this library; see the file COPYING.LIB. If not, * - * write to the Free Software Foundation, Inc., 59 Temple Place, * - * Suite 330, Boston, MA 02111-1307, USA * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * * * ***************************************************************************/ diff --git a/src/App/Datums.h b/src/App/Datums.h index 002790e78b..286e7353f3 100644 --- a/src/App/Datums.h +++ b/src/App/Datums.h @@ -3,22 +3,21 @@ * Copyright (c) 2015 Alexander Golubev (Fat-Zer) * * Copyright (c) 2024 Ondsel (PL Boyer) * * * - * This file is part of the FreeCAD CAx development system. * + * This file is part of FreeCAD. * * * - * This library is free software; you can redistribute it and/or * - * modify it under the terms of the GNU Library General Public * - * License as published by the Free Software Foundation; either * - * version 2 of the License, or (at your option) any later version. * + * 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. * * * - * This library 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 Library General Public License for more details. * + * 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 Library General Public * - * License along with this library; see the file COPYING.LIB. If not, * - * write to the Free Software Foundation, Inc., 59 Temple Place, * - * Suite 330, Boston, MA 02111-1307, USA * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * * * ***************************************************************************/ diff --git a/src/Gui/CommandStructure.cpp b/src/Gui/CommandStructure.cpp index 1b14ce5069..3e5f38ae44 100644 --- a/src/Gui/CommandStructure.cpp +++ b/src/Gui/CommandStructure.cpp @@ -156,9 +156,9 @@ void StdCmdVarSet::activated(int iMsg) // add the varset to a group if it is selected auto sels = Selection().getSelectionEx(nullptr, App::DocumentObject::getClassTypeId(), - ResolveMode::OldStyleElement, true); + ResolveMode::OldStyleElement, true); if (sels.size() == 1) { - App::DocumentObject *obj = sels[0].getObject(); + App::DocumentObject* obj = sels[0].getObject(); auto group = obj->getExtension(); if (group) { Gui::Document* docGui = Application::Instance->activeDocument(); diff --git a/src/Mod/Part/App/AppPart.cpp b/src/Mod/Part/App/AppPart.cpp index 105076054a..c244d033a5 100644 --- a/src/Mod/Part/App/AppPart.cpp +++ b/src/Mod/Part/App/AppPart.cpp @@ -58,6 +58,7 @@ #include "ConicPy.h" #include "CustomFeature.h" #include "CylinderPy.h" +#include "Datums.h" #include "DatumFeature.h" #include "EllipsePy.h" #include "FaceMaker.h" @@ -534,6 +535,10 @@ PyMOD_INIT_FUNC(Part) Part::GeomSurfaceOfRevolution ::init(); Part::GeomSurfaceOfExtrusion ::init(); Part::Datum ::init(); + Part::DatumPlane ::init(); + Part::DatumLine ::init(); + Part::DatumPoint ::init(); + Part::LocalCoordinateSystem ::init(); // Geometry2d types Part::Geometry2d ::init(); diff --git a/src/Mod/Part/App/AttachExtension.h b/src/Mod/Part/App/AttachExtension.h index e2c3f480d1..a86abb5947 100644 --- a/src/Mod/Part/App/AttachExtension.h +++ b/src/Mod/Part/App/AttachExtension.h @@ -35,7 +35,6 @@ #include #include "Attacher.h" -#include "PartFeature.h" namespace Part diff --git a/src/Mod/Part/App/CMakeLists.txt b/src/Mod/Part/App/CMakeLists.txt index ecd2760d8d..71bedcac65 100644 --- a/src/Mod/Part/App/CMakeLists.txt +++ b/src/Mod/Part/App/CMakeLists.txt @@ -213,6 +213,8 @@ SET(Features_SRCS CustomFeature.h BodyBase.h BodyBase.cpp + Datums.cpp + Datums.h DatumFeature.cpp DatumFeature.h AttachExtension.h diff --git a/src/Mod/Part/App/Datums.cpp b/src/Mod/Part/App/Datums.cpp new file mode 100644 index 0000000000..1fd53ce669 --- /dev/null +++ b/src/Mod/Part/App/Datums.cpp @@ -0,0 +1,63 @@ +/*************************************************************************** + * Copyright (c) 2024 Ondsel (PL Boyer) * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#include "PreCompiled.h" + +#include "Datums.h" + + +using namespace Part; +using namespace Attacher; + +PROPERTY_SOURCE_WITH_EXTENSIONS(Part::DatumPlane, App::Plane) + +Part::DatumPlane::DatumPlane() +{ + AttachExtension::initExtension(this); + this->setAttacher(new AttachEnginePlane); +} + + +PROPERTY_SOURCE_WITH_EXTENSIONS(Part::DatumLine, App::Line) + +Part::DatumLine::DatumLine() +{ + AttachExtension::initExtension(this); + this->setAttacher(new AttachEngineLine); +} + + +PROPERTY_SOURCE_WITH_EXTENSIONS(Part::DatumPoint, App::Point) + +Part::DatumPoint::DatumPoint() +{ + AttachExtension::initExtension(this); + this->setAttacher(new AttachEnginePoint); +} + + +PROPERTY_SOURCE_WITH_EXTENSIONS(Part::LocalCoordinateSystem, App::LocalCoordinateSystem) + +Part::LocalCoordinateSystem::LocalCoordinateSystem() +{ + AttachExtension::initExtension(this); +} diff --git a/src/Mod/Part/App/Datums.h b/src/Mod/Part/App/Datums.h new file mode 100644 index 0000000000..06f35c865f --- /dev/null +++ b/src/Mod/Part/App/Datums.h @@ -0,0 +1,84 @@ +/*************************************************************************** + * Copyright (c) 2024 Ondsel (PL Boyer) * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#ifndef PART_DATUMS_H +#define PART_DATUMS_H + +#include + +#include "AttachExtension.h" + +namespace Part +{ + +class PartExport DatumPlane : public App::Plane, public AttachExtension +{ + PROPERTY_HEADER_WITH_EXTENSIONS(Part::DatumPlane); + +public: + DatumPlane(); + ~DatumPlane() override = default; + const char* getViewProviderName() const override { + return "PartGui::ViewProviderPlane"; + } +}; + +class PartExport DatumLine : public App::Line, public AttachExtension +{ + PROPERTY_HEADER_WITH_EXTENSIONS(Part::DatumLine); + +public: + DatumLine(); + ~DatumLine() override = default; + const char* getViewProviderName() const override { + return "PartGui::ViewProviderLine"; + } +}; + +class PartExport DatumPoint : public App::Point, public AttachExtension +{ + PROPERTY_HEADER_WITH_EXTENSIONS(Part::DatumPoint); + +public: + DatumPoint(); + ~DatumPoint() override = default; + const char* getViewProviderName() const override { + return "PartGui::ViewProviderPoint"; + } +}; + +class PartExport LocalCoordinateSystem : public App::LocalCoordinateSystem, public AttachExtension +{ + PROPERTY_HEADER_WITH_EXTENSIONS(Part::LocalCoordinateSystem); + +public: + LocalCoordinateSystem(); + ~LocalCoordinateSystem() override = default; + const char* getViewProviderName() const override { + return "PartGui::ViewProviderLCS"; + } +}; + +} //namespace Part + + +#endif // PART_DATUMS_H diff --git a/src/Mod/Part/Gui/AppPartGui.cpp b/src/Mod/Part/Gui/AppPartGui.cpp index 998436403e..3c6cc8845f 100644 --- a/src/Mod/Part/Gui/AppPartGui.cpp +++ b/src/Mod/Part/Gui/AppPartGui.cpp @@ -47,6 +47,7 @@ #include "ViewProvider.h" #include "ViewProvider2DObject.h" #include "ViewProviderAttachExtension.h" +#include "ViewProviderDatum.h" #include "ViewProviderGridExtension.h" #include "ViewProviderBoolean.h" #include "ViewProviderBox.h" @@ -167,6 +168,10 @@ PyMOD_INIT_FUNC(PartGui) PartGui::ViewProviderGridExtensionPython ::init(); PartGui::ViewProviderSplineExtension ::init(); PartGui::ViewProviderSplineExtensionPython ::init(); + PartGui::ViewProviderLine ::init(); + PartGui::ViewProviderPlane ::init(); + PartGui::ViewProviderPoint ::init(); + PartGui::ViewProviderLCS ::init(); PartGui::ViewProviderPartExt ::init(); PartGui::ViewProviderPart ::init(); PartGui::ViewProviderPrimitive ::init(); diff --git a/src/Mod/Part/Gui/CMakeLists.txt b/src/Mod/Part/Gui/CMakeLists.txt index 8df4aa8cd2..cc409ada8b 100644 --- a/src/Mod/Part/Gui/CMakeLists.txt +++ b/src/Mod/Part/Gui/CMakeLists.txt @@ -164,6 +164,8 @@ SET(PartGui_SRCS ViewProvider.h ViewProviderAttachExtension.h ViewProviderAttachExtension.cpp + ViewProviderDatum.cpp + ViewProviderDatum.h ViewProviderExt.cpp ViewProviderExt.h ViewProviderReference.cpp diff --git a/src/Mod/Part/Gui/Command.cpp b/src/Mod/Part/Gui/Command.cpp index 3dee082a43..eed34ee9de 100644 --- a/src/Mod/Part/Gui/Command.cpp +++ b/src/Mod/Part/Gui/Command.cpp @@ -32,6 +32,7 @@ #endif #include +#include #include #include #include @@ -51,6 +52,8 @@ #include #include +#include + #include "BoxSelection.h" #include "CrossSections.h" #include "DlgBooleanOperation.h" @@ -2211,6 +2214,193 @@ bool CmdPartSectionCut::isActive() return hasActiveDocument(); } + +//=========================================================================== +// Part_CoordinateSystem +//=========================================================================== + +namespace { + QString getAutoGroupCommandStr() + // Helper function to get the python code to add the newly created object to the active Part/Body object if present + { + App::GeoFeature* activeObj = Gui::Application::Instance->activeView()->getActiveObject(PDBODYKEY); + if (!activeObj) { + activeObj = Gui::Application::Instance->activeView()->getActiveObject(PARTKEY); + } + + if (activeObj) { + QString activeName = QString::fromLatin1(activeObj->getNameInDocument()); + return QString::fromLatin1("App.ActiveDocument.getObject('%1\').addObject(obj)\n").arg(activeName); + } + + return QString::fromLatin1("# Object created at document root."); + } +} + +DEF_STD_CMD_A(CmdPartCoordinateSystem) + +CmdPartCoordinateSystem::CmdPartCoordinateSystem() + : Command("Part_CoordinateSystem") +{ + sGroup = QT_TR_NOOP("Part"); + sMenuText = QT_TR_NOOP("Create a coordinate system"); + sToolTipText = QT_TR_NOOP("A coordinate system object that can be attached to other objects."); + sWhatsThis = "Part_CoordinateSystem"; + sStatusTip = sToolTipText; + sPixmap = "Std_CoordinateSystem"; +} + +void CmdPartCoordinateSystem::activated(int iMsg) +{ + Q_UNUSED(iMsg); + + openCommand(QT_TRANSLATE_NOOP("Command", "Add a coordinate system")); + + std::string name = getUniqueObjectName("LCS"); + doCommand(Doc, "obj = App.activeDocument().addObject('Part::LocalCoordinateSystem','%s')", name.c_str()); + doCommand(Doc, getAutoGroupCommandStr().toUtf8()); + doCommand(Doc, "obj.Visibility = True"); + doCommand(Doc, "obj.ViewObject.doubleClicked()"); +} + +bool CmdPartCoordinateSystem::isActive() +{ + return hasActiveDocument(); +} + +//=========================================================================== +// Part_Plane +//=========================================================================== +DEF_STD_CMD_A(CmdPartPlane) + +CmdPartPlane::CmdPartPlane() + : Command("Part_Plane") +{ + sGroup = QT_TR_NOOP("Part"); + sMenuText = QT_TR_NOOP("Create a datum plane"); + sToolTipText = QT_TR_NOOP("A plane object that can be attached to other objects."); + sWhatsThis = "Part_Plane"; + sStatusTip = sToolTipText; + sPixmap = "Std_Plane"; +} + +void CmdPartPlane::activated(int iMsg) +{ + Q_UNUSED(iMsg); + + openCommand(QT_TRANSLATE_NOOP("Command", "Add a datum plane")); + + std::string name = getUniqueObjectName("Plane"); + doCommand(Doc, "obj = App.activeDocument().addObject('Part::DatumPlane','%s')", name.c_str()); + doCommand(Doc, getAutoGroupCommandStr().toUtf8()); + doCommand(Doc, "obj.ViewObject.doubleClicked()"); +} + +bool CmdPartPlane::isActive() +{ + return hasActiveDocument(); +} + +//=========================================================================== +// Part_Line +//=========================================================================== +DEF_STD_CMD_A(CmdPartLine) + +CmdPartLine::CmdPartLine() + : Command("Part_Line") +{ + sGroup = QT_TR_NOOP("Part"); + sMenuText = QT_TR_NOOP("Create a datum line"); + sToolTipText = QT_TR_NOOP("A line object that can be attached to other objects."); + sWhatsThis = "Part_Line"; + sStatusTip = sToolTipText; + sPixmap = "Std_Axis"; +} + +void CmdPartLine::activated(int iMsg) +{ + Q_UNUSED(iMsg); + + openCommand(QT_TRANSLATE_NOOP("Command", "Add a datum line")); + + std::string name = getUniqueObjectName("Line"); + doCommand(Doc, "obj = App.activeDocument().addObject('Part::DatumLine','%s')", name.c_str()); + doCommand(Doc, getAutoGroupCommandStr().toUtf8()); + doCommand(Doc, "obj.ViewObject.doubleClicked()"); +} + +bool CmdPartLine::isActive() +{ + return hasActiveDocument(); +} + +//=========================================================================== +// Part_Point +//=========================================================================== +DEF_STD_CMD_A(CmdPartPoint) + +CmdPartPoint::CmdPartPoint() + : Command("Part_Point") +{ + sGroup = QT_TR_NOOP("Part"); + sMenuText = QT_TR_NOOP("Create a datum point"); + sToolTipText = QT_TR_NOOP("A point object that can be attached to other objects."); + sWhatsThis = "Part_Point"; + sStatusTip = sToolTipText; + sPixmap = "Std_Point"; +} + +void CmdPartPoint::activated(int iMsg) +{ + Q_UNUSED(iMsg); + + openCommand(QT_TRANSLATE_NOOP("Command", "Add a datum point")); + + std::string name = getUniqueObjectName("Point"); + doCommand(Doc, "obj = App.activeDocument().addObject('Part::DatumPoint','%s')", name.c_str()); + doCommand(Doc, getAutoGroupCommandStr().toUtf8()); + doCommand(Doc, "obj.ViewObject.doubleClicked()"); +} + +bool CmdPartPoint::isActive() +{ + return hasActiveDocument(); +} + + +//=========================================================================== +// Part_Datums +//=========================================================================== +class CmdPartDatums : public Gui::GroupCommand +{ +public: + CmdPartDatums() + : GroupCommand("Part_Datums") + { + sGroup = QT_TR_NOOP("Part"); + sMenuText = QT_TR_NOOP("Create a datum"); + sToolTipText = QT_TR_NOOP("Create a datum object (LCS, Plane, Line, Point) that can be attached to other objects."); + sWhatsThis = "Part_Datums"; + sStatusTip = sToolTipText; + + setCheckable(false); + + addCommand("Part_CoordinateSystem"); + addCommand("Part_Plane"); + addCommand("Part_Line"); + addCommand("Part_Point"); + } + + const char* className() const override + { + return "CmdPartDatums"; + } + + bool isActive() override + { + return hasActiveDocument(); + } +}; //--------------------------------------------------------------- void CreatePartCommands() @@ -2256,4 +2446,10 @@ void CreatePartCommands() rcCmdMgr.addCommand(new CmdBoxSelection()); rcCmdMgr.addCommand(new CmdPartProjectionOnSurface()); rcCmdMgr.addCommand(new CmdPartSectionCut()); + + rcCmdMgr.addCommand(new CmdPartCoordinateSystem()); + rcCmdMgr.addCommand(new CmdPartPlane()); + rcCmdMgr.addCommand(new CmdPartLine()); + rcCmdMgr.addCommand(new CmdPartPoint()); + rcCmdMgr.addCommand(new CmdPartDatums()); } diff --git a/src/Mod/Part/Gui/ViewProviderAttachExtension.cpp b/src/Mod/Part/Gui/ViewProviderAttachExtension.cpp index 06a2dd377a..af287a1ec5 100644 --- a/src/Mod/Part/Gui/ViewProviderAttachExtension.cpp +++ b/src/Mod/Part/Gui/ViewProviderAttachExtension.cpp @@ -113,6 +113,10 @@ void ViewProviderAttachExtension::extensionSetupContextMenu(QMenu* menu, QObject void ViewProviderAttachExtension::showAttachmentEditor() { + if (Gui::Control().activeDialog()) { + Gui::Control().closeDialog(); + } + // See PropertyEnumAttacherItem::openTask() Gui::TaskView::TaskDialog* dlg = Gui::Control().activeDialog(); TaskDlgAttacher* task; diff --git a/src/Mod/Part/Gui/ViewProviderAttachExtension.h b/src/Mod/Part/Gui/ViewProviderAttachExtension.h index f3a1376c2d..4aaff4ec90 100644 --- a/src/Mod/Part/Gui/ViewProviderAttachExtension.h +++ b/src/Mod/Part/Gui/ViewProviderAttachExtension.h @@ -44,7 +44,6 @@ public: void extensionUpdateData(const App::Property*) override; void extensionSetupContextMenu(QMenu*, QObject*, const char*) override; -private: void showAttachmentEditor(); }; diff --git a/src/Mod/Part/Gui/ViewProviderDatum.cpp b/src/Mod/Part/Gui/ViewProviderDatum.cpp new file mode 100644 index 0000000000..6b7373d1f3 --- /dev/null +++ b/src/Mod/Part/Gui/ViewProviderDatum.cpp @@ -0,0 +1,94 @@ +/*************************************************************************** + * Copyright (c) 2024 Ondsel (PL Boyer) * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#include "PreCompiled.h" + +#ifndef _PreComp_ +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "ViewProviderDatum.h" + + +using namespace PartGui; + +PROPERTY_SOURCE_WITH_EXTENSIONS(PartGui::ViewProviderLine, Gui::ViewProviderLine) + +ViewProviderLine::ViewProviderLine() +{ + PartGui::ViewProviderAttachExtension::initExtension(this); +} + +bool ViewProviderLine::doubleClicked() +{ + showAttachmentEditor(); + return true; +} + +PROPERTY_SOURCE_WITH_EXTENSIONS(PartGui::ViewProviderPlane, Gui::ViewProviderPlane) + +ViewProviderPlane::ViewProviderPlane() +{ + PartGui::ViewProviderAttachExtension::initExtension(this); +} + +bool ViewProviderPlane::doubleClicked() +{ + showAttachmentEditor(); + return true; +} + + +PROPERTY_SOURCE_WITH_EXTENSIONS(PartGui::ViewProviderPoint, Gui::ViewProviderPoint) + +ViewProviderPoint::ViewProviderPoint() +{ + PartGui::ViewProviderAttachExtension::initExtension(this); +} + +bool ViewProviderPoint::doubleClicked() +{ + showAttachmentEditor(); + return true; +} + + +PROPERTY_SOURCE_WITH_EXTENSIONS(PartGui::ViewProviderLCS, Gui::ViewProviderCoordinateSystem) + +ViewProviderLCS::ViewProviderLCS() +{ + PartGui::ViewProviderAttachExtension::initExtension(this); +} + +bool ViewProviderLCS::doubleClicked() +{ + showAttachmentEditor(); + return true; +} diff --git a/src/Mod/Part/Gui/ViewProviderDatum.h b/src/Mod/Part/Gui/ViewProviderDatum.h new file mode 100644 index 0000000000..46d232e80c --- /dev/null +++ b/src/Mod/Part/Gui/ViewProviderDatum.h @@ -0,0 +1,84 @@ +/*************************************************************************** + * Copyright (c) 2024 Ondsel (PL Boyer) * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#ifndef PARTGUI_ViewProviderDatum_H +#define PARTGUI_ViewProviderDatum_H + +#include +#include +#include +#include +#include + +#include + +namespace PartGui { + +class PartGuiExport ViewProviderLine : public Gui::ViewProviderLine, PartGui::ViewProviderAttachExtension +{ + PROPERTY_HEADER_WITH_EXTENSIONS(PartGui::ViewProviderLine); + +public: + ViewProviderLine(); + ~ViewProviderLine() override = default; + + bool doubleClicked() override; +}; + +class PartGuiExport ViewProviderPlane : public Gui::ViewProviderPlane, PartGui::ViewProviderAttachExtension +{ + PROPERTY_HEADER_WITH_EXTENSIONS(PartGui::ViewProviderPlane); + +public: + ViewProviderPlane(); + ~ViewProviderPlane() override = default; + + bool doubleClicked() override; +}; + +class PartGuiExport ViewProviderPoint : public Gui::ViewProviderPoint, PartGui::ViewProviderAttachExtension +{ + PROPERTY_HEADER_WITH_EXTENSIONS(PartGui::ViewProviderPoint); + +public: + ViewProviderPoint(); + ~ViewProviderPoint() override = default; + + bool doubleClicked() override; +}; + +class PartGuiExport ViewProviderLCS : public Gui::ViewProviderCoordinateSystem, PartGui::ViewProviderAttachExtension +{ + PROPERTY_HEADER_WITH_EXTENSIONS(PartGui::ViewProviderLCS); + +public: + ViewProviderLCS(); + ~ViewProviderLCS() override = default; + + bool doubleClicked() override; +}; + +} // namespace PartGui + + +#endif // PARTGUI_ViewProviderDatum_H diff --git a/src/Mod/Part/Gui/WorkbenchManipulator.cpp b/src/Mod/Part/Gui/WorkbenchManipulator.cpp index b6a14976dc..eee8c4aece 100644 --- a/src/Mod/Part/Gui/WorkbenchManipulator.cpp +++ b/src/Mod/Part/Gui/WorkbenchManipulator.cpp @@ -37,6 +37,7 @@ void WorkbenchManipulator::modifyMenuBar([[maybe_unused]] Gui::MenuItem* menuBar void WorkbenchManipulator::modifyToolBars(Gui::ToolBarItem* toolBar) { addSelectionFilter(toolBar); + addDatums(toolBar); } void WorkbenchManipulator::modifyDockWindows([[maybe_unused]] Gui::DockWindowItems* dockWindow) @@ -71,3 +72,18 @@ void WorkbenchManipulator::addSelectionFilter(Gui::ToolBarItem* toolBar) } } } + +void WorkbenchManipulator::addDatums(Gui::ToolBarItem* toolBar) +{ + if (auto view = toolBar->findItem("Structure")) { + auto add = new Gui::ToolBarItem(); // NOLINT + add->setCommand("Part_Datums"); + auto item = view->findItem("Std_Group"); + if (item) { + view->insertItem(item, add); + } + else { + view->appendItem(add); + } + } +} diff --git a/src/Mod/Part/Gui/WorkbenchManipulator.h b/src/Mod/Part/Gui/WorkbenchManipulator.h index 338dd3243d..623f2cc502 100644 --- a/src/Mod/Part/Gui/WorkbenchManipulator.h +++ b/src/Mod/Part/Gui/WorkbenchManipulator.h @@ -54,6 +54,7 @@ protected: private: static void addSectionCut(Gui::MenuItem* menuBar); static void addSelectionFilter(Gui::ToolBarItem* toolBar); + static void addDatums(Gui::ToolBarItem* toolBar); }; } // namespace PartGui diff --git a/src/Mod/PartDesign/App/Body.cpp b/src/Mod/PartDesign/App/Body.cpp index 82ce60f2af..dcc6a666cb 100644 --- a/src/Mod/PartDesign/App/Body.cpp +++ b/src/Mod/PartDesign/App/Body.cpp @@ -219,7 +219,9 @@ bool Body::isAllowed(const App::DocumentObject *obj) //obj->isDerivedFrom() // trouble with this line on Windows!? Linker fails to find getClassTypeId() of the Part::FeaturePython... //obj->isDerivedFrom() // allow VarSets for parameterization - obj->isDerivedFrom() + obj->isDerivedFrom() || + obj->isDerivedFrom() || + obj->isDerivedFrom() ); } diff --git a/src/Mod/PartDesign/Gui/ViewProviderBody.cpp b/src/Mod/PartDesign/Gui/ViewProviderBody.cpp index fc49c5f0f0..604095e3a3 100644 --- a/src/Mod/PartDesign/Gui/ViewProviderBody.cpp +++ b/src/Mod/PartDesign/Gui/ViewProviderBody.cpp @@ -342,11 +342,11 @@ bool ViewProviderBody::canDropObjects() const { // if the BaseFeature property is marked as hidden or read-only then // it's not allowed to modify it. - PartDesign::Body* body = getObject(); - if (body->BaseFeature.testStatus(App::Property::Status::Hidden)) - return false; - if (body->BaseFeature.testStatus(App::Property::Status::ReadOnly)) + auto* body = getObject(); + if (body->BaseFeature.testStatus(App::Property::Status::Hidden) + || body->BaseFeature.testStatus(App::Property::Status::ReadOnly)) { return false; + } return true; } @@ -355,7 +355,15 @@ bool ViewProviderBody::canDropObject(App::DocumentObject* obj) const if (obj->isDerivedFrom()) { return true; } - if (!obj->isDerivedFrom(Part::Feature::getClassTypeId())) { + else if (obj->isDerivedFrom(App::DatumElement::getClassTypeId())) { + // accept only datums that are not part of a LCS. + auto* lcs = static_cast(obj)->getLCS(); + return !lcs; + } + else if (obj->isDerivedFrom(App::LocalCoordinateSystem::getClassTypeId())) { + return !obj->isDerivedFrom(App::Origin::getClassTypeId()); + } + else if (!obj->isDerivedFrom(Part::Feature::getClassTypeId())) { return false; } else if (PartDesign::Body::findBodyOf(obj)) { @@ -375,8 +383,10 @@ bool ViewProviderBody::canDropObject(App::DocumentObject* obj) const void ViewProviderBody::dropObject(App::DocumentObject* obj) { - PartDesign::Body* body = getObject(); - if (obj->isDerivedFrom()) { + auto* body = getObject(); + if (obj->isDerivedFrom() + || obj->isDerivedFrom() + || obj->isDerivedFrom()) { body->addObject(obj); } else if (PartDesign::Body::isAllowed(obj) && PartDesignGui::isFeatureMovable(obj)) { diff --git a/src/Mod/PartDesign/Gui/ViewProviderDatum.cpp b/src/Mod/PartDesign/Gui/ViewProviderDatum.cpp index 813257e0b4..e234245bd9 100644 --- a/src/Mod/PartDesign/Gui/ViewProviderDatum.cpp +++ b/src/Mod/PartDesign/Gui/ViewProviderDatum.cpp @@ -2,22 +2,21 @@ * Copyright (c) 2013 Jan Rheinlaender * * * * * - * This file is part of the FreeCAD CAx development system. * + * This file is part of FreeCAD. * * * - * This library is free software; you can redistribute it and/or * - * modify it under the terms of the GNU Library General Public * - * License as published by the Free Software Foundation; either * - * version 2 of the License, or (at your option) any later version. * + * 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. * * * - * This library 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 Library General Public License for more details. * + * 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 Library General Public * - * License along with this library; see the file COPYING.LIB. If not, * - * write to the Free Software Foundation, Inc., 59 Temple Place, * - * Suite 330, Boston, MA 02111-1307, USA * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * * * ***************************************************************************/ diff --git a/src/Mod/PartDesign/Gui/ViewProviderDatum.h b/src/Mod/PartDesign/Gui/ViewProviderDatum.h index 8187d00ab2..07f5a86765 100644 --- a/src/Mod/PartDesign/Gui/ViewProviderDatum.h +++ b/src/Mod/PartDesign/Gui/ViewProviderDatum.h @@ -2,22 +2,21 @@ * Copyright (c) 2013 Jan Rheinlaender * * * * * - * This file is part of the FreeCAD CAx development system. * + * This file is part of FreeCAD. * * * - * This library is free software; you can redistribute it and/or * - * modify it under the terms of the GNU Library General Public * - * License as published by the Free Software Foundation; either * - * version 2 of the License, or (at your option) any later version. * + * 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. * * * - * This library 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 Library General Public License for more details. * + * 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 Library General Public * - * License along with this library; see the file COPYING.LIB. If not, * - * write to the Free Software Foundation, Inc., 59 Temple Place, * - * Suite 330, Boston, MA 02111-1307, USA * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * * * ***************************************************************************/ From 32bf49cb8a8d2c7ce8df74b98b8a1f3aaa46d719 Mon Sep 17 00:00:00 2001 From: PaddleStroke Date: Thu, 3 Oct 2024 14:34:52 +0200 Subject: [PATCH 03/15] Part: AttachExtension: Refactor to remove code duplicates. --- src/Mod/Part/App/AttachExtension.cpp | 71 ++++++++-------------------- src/Mod/Part/App/AttachExtension.h | 3 ++ 2 files changed, 24 insertions(+), 50 deletions(-) diff --git a/src/Mod/Part/App/AttachExtension.cpp b/src/Mod/Part/App/AttachExtension.cpp index be5cef8b01..0aaf11602d 100644 --- a/src/Mod/Part/App/AttachExtension.cpp +++ b/src/Mod/Part/App/AttachExtension.cpp @@ -411,33 +411,18 @@ void AttachExtension::extensionOnChanged(const App::Property* prop) bool bAttached = false; try{ bAttached = positionBySupport(); - } catch (Base::Exception &e) { + } + catch (Base::Exception &e) { getExtendedObject()->setStatus(App::Error, true); Base::Console().Error("PositionBySupport: %s\n",e.what()); //set error message - how? - } catch (Standard_Failure &e){ + } + catch (Standard_Failure &e){ getExtendedObject()->setStatus(App::Error, true); Base::Console().Error("PositionBySupport: %s\n",e.GetMessageString()); } - // Hide properties when not applicable to reduce user confusion - - eMapMode mmode = eMapMode(this->MapMode.getValue()); - - bool modeIsPointOnCurve = mmode == mmNormalToPath || - mmode == mmFrenetNB || mmode == mmFrenetTN || mmode == mmFrenetTB || - mmode == mmRevolutionSection || mmode == mmConcentric; - - // MapPathParameter is only used if there is a reference to one edge and not edge + vertex - bool hasOneRef = false; - if (_props.attacher && _props.attacher->subnames.size() == 1) { - hasOneRef = true; - } - - this->MapPathParameter.setStatus(App::Property::Status::Hidden, !bAttached || !(modeIsPointOnCurve && hasOneRef)); - this->MapReversed.setStatus(App::Property::Status::Hidden, !bAttached); - this->AttachmentOffset.setStatus(App::Property::Status::Hidden, !bAttached); - getPlacement().setReadOnly(bAttached && mmode != mmTranslate); //for mmTranslate, orientation should remain editable even when attached. + updateSinglePropertyStatus(bAttached); } if (prop == &AttacherEngine) { AttacherType.setValue(enumToClass(AttacherEngine.getValueAsString())); @@ -508,27 +493,9 @@ void AttachExtension::onExtendedDocumentRestored() restoreAttacherEngine(this); - // Hide properties when not applicable to reduce user confusion bool bAttached = positionBySupport(); - eMapMode mmode = eMapMode(this->MapMode.getValue()); - bool modeIsPointOnCurve = - (mmode == mmNormalToPath || - mmode == mmFrenetNB || - mmode == mmFrenetTN || - mmode == mmFrenetTB || - mmode == mmRevolutionSection || - mmode == mmConcentric); - // MapPathParameter is only used if there is a reference to one edge and not edge + vertex - bool hasOneRef = false; - if (_props.attacher && _props.attacher->subnames.size() == 1) { - hasOneRef = true; - } - - this->MapPathParameter.setStatus(App::Property::Status::Hidden, !bAttached || !(modeIsPointOnCurve && hasOneRef)); - this->MapReversed.setStatus(App::Property::Status::Hidden, !bAttached); - this->AttachmentOffset.setStatus(App::Property::Status::Hidden, !bAttached); - getPlacement().setReadOnly(bAttached && mmode != mmTranslate); //for mmTranslate, orientation should remain editable even when attached. + updateSinglePropertyStatus(bAttached); } catch (Base::Exception&) { } @@ -536,7 +503,7 @@ void AttachExtension::onExtendedDocumentRestored() } } -void AttachExtension::updatePropertyStatus(bool bAttached, bool base) +void AttachExtension::updateSinglePropertyStatus(bool bAttached, bool base) { auto& props = base ? this->_baseProps : this->_props; if (!props.mapMode) { @@ -550,12 +517,9 @@ void AttachExtension::updatePropertyStatus(bool bAttached, bool base) || mmode == mmFrenetTB || mmode == mmRevolutionSection || mmode == mmConcentric); // MapPathParameter is only used if there is a reference to one edge and not edge + vertex - bool hasOneRef = false; - if (props.attacher && props.attacher->subnames.size() == 1) { - hasOneRef = true; - } - props.mapPathParameter->setStatus(App::Property::Status::Hidden, - !bAttached || !(modeIsPointOnCurve && hasOneRef)); + bool hasOneRef = props.attacher && props.attacher->subnames.size() == 1; + + props.mapPathParameter->setStatus(App::Property::Status::Hidden, !bAttached || !(modeIsPointOnCurve && hasOneRef)); props.mapReversed->setStatus(App::Property::Status::Hidden, !bAttached); if (base) { @@ -564,11 +528,18 @@ void AttachExtension::updatePropertyStatus(bool bAttached, bool base) else { this->AttachmentOffset.setStatus(App::Property::Status::Hidden, !bAttached); if (getExtendedContainer()) { - getPlacement().setReadOnly( - bAttached && mmode != mmTranslate); // for mmTranslate, orientation should remain - // editable even when attached. + // for mmTranslate, orientation should remain editable even when attached. + getPlacement().setReadOnly(bAttached && mmode != mmTranslate); } - updatePropertyStatus(bAttached, true); + } +} + +void AttachExtension::updatePropertyStatus(bool bAttached, bool base) +{ + updateSinglePropertyStatus(bAttached, base); + + if (!base) { + updateSinglePropertyStatus(bAttached, true); } } diff --git a/src/Mod/Part/App/AttachExtension.h b/src/Mod/Part/App/AttachExtension.h index a86abb5947..3418de74ae 100644 --- a/src/Mod/Part/App/AttachExtension.h +++ b/src/Mod/Part/App/AttachExtension.h @@ -145,7 +145,10 @@ protected: public: void updateAttacherVals(bool base = false) const; + // This update both _props and _baseProps if base = false void updatePropertyStatus(bool attached, bool base = false); + // This update only _props if base = false + void updateSinglePropertyStatus(bool attached, bool base = false); private: struct _Properties: Properties From 0a9a97fc63ee5d452f7e3cb744a70c807f6e6651 Mon Sep 17 00:00:00 2001 From: PaddleStroke Date: Tue, 8 Oct 2024 09:48:42 +0200 Subject: [PATCH 04/15] Run precommit on TaskAttacher.cpp --- src/Mod/Part/Gui/TaskAttacher.cpp | 312 ++++++++++++++++-------------- 1 file changed, 171 insertions(+), 141 deletions(-) diff --git a/src/Mod/Part/Gui/TaskAttacher.cpp b/src/Mod/Part/Gui/TaskAttacher.cpp index d46ad0a4a8..1b57ea6daf 100644 --- a/src/Mod/Part/Gui/TaskAttacher.cpp +++ b/src/Mod/Part/Gui/TaskAttacher.cpp @@ -70,20 +70,23 @@ const QString makeRefString(const App::DocumentObject* obj, const std::string& s // App::Plane, Line or Datum feature return QString::fromLatin1(obj->getNameInDocument()); - if ((sub.size() > 4) && (sub.substr(0,4) == "Face")) { + if ((sub.size() > 4) && (sub.substr(0, 4) == "Face")) { int subId = std::atoi(&sub[4]); return QString::fromLatin1(obj->getNameInDocument()) + QString::fromLatin1(":") + QObject::tr("Face") + QString::number(subId); - } else if ((sub.size() > 4) && (sub.substr(0,4) == "Edge")) { + } + else if ((sub.size() > 4) && (sub.substr(0, 4) == "Edge")) { int subId = std::atoi(&sub[4]); return QString::fromLatin1(obj->getNameInDocument()) + QString::fromLatin1(":") + QObject::tr("Edge") + QString::number(subId); - } else if ((sub.size() > 6) && (sub.substr(0,6) == "Vertex")) { + } + else if ((sub.size() > 6) && (sub.substr(0, 6) == "Vertex")) { int subId = std::atoi(&sub[6]); return QString::fromLatin1(obj->getNameInDocument()) + QString::fromLatin1(":") + QObject::tr("Vertex") + QString::number(subId); - } else { + } + else { //something else that face/edge/vertex. Can be empty string. return QString::fromLatin1(obj->getNameInDocument()) - + (sub.length()>0 ? QString::fromLatin1(":") : QString()) - + QString::fromLatin1(sub.c_str()); + + (sub.length() > 0 ? QString::fromLatin1(":") : QString()) + + QString::fromLatin1(sub.c_str()); } } @@ -98,15 +101,16 @@ void TaskAttacher::makeRefStrings(std::vector& refstrings, std::vector< // for Origin or Datum features refnames is empty but we need a non-empty return value if (refnames[r].empty()) refnames[r] = refs[r]->getNameInDocument(); - } else { + } + else { refstrings.push_back(QObject::tr("No reference selected")); refnames.emplace_back(""); } } } -TaskAttacher::TaskAttacher(Gui::ViewProviderDocumentObject *ViewProvider, QWidget *parent, - QString picture, QString text, TaskAttacher::VisibilityFunction visFunc) +TaskAttacher::TaskAttacher(Gui::ViewProviderDocumentObject* ViewProvider, QWidget* parent, + QString picture, QString text, TaskAttacher::VisibilityFunction visFunc) : TaskBox(Gui::BitmapFactory().pixmap(picture.toLatin1()), text, true, parent) , SelectionObserver(ViewProvider) , ViewProvider(ViewProvider) @@ -125,37 +129,37 @@ TaskAttacher::TaskAttacher(Gui::ViewProviderDocumentObject *ViewProvider, QWidge // clang-format off connect(ui->attachmentOffsetX, qOverload(&Gui::QuantitySpinBox::valueChanged), - this, &TaskAttacher::onAttachmentOffsetXChanged); + this, &TaskAttacher::onAttachmentOffsetXChanged); connect(ui->attachmentOffsetY, qOverload(&Gui::QuantitySpinBox::valueChanged), - this, &TaskAttacher::onAttachmentOffsetYChanged); + this, &TaskAttacher::onAttachmentOffsetYChanged); connect(ui->attachmentOffsetZ, qOverload(&Gui::QuantitySpinBox::valueChanged), - this, &TaskAttacher::onAttachmentOffsetZChanged); + this, &TaskAttacher::onAttachmentOffsetZChanged); connect(ui->attachmentOffsetYaw, qOverload(&Gui::QuantitySpinBox::valueChanged), - this, &TaskAttacher::onAttachmentOffsetYawChanged); + this, &TaskAttacher::onAttachmentOffsetYawChanged); connect(ui->attachmentOffsetPitch, qOverload(&Gui::QuantitySpinBox::valueChanged), - this, &TaskAttacher::onAttachmentOffsetPitchChanged); + this, &TaskAttacher::onAttachmentOffsetPitchChanged); connect(ui->attachmentOffsetRoll, qOverload(&Gui::QuantitySpinBox::valueChanged), - this, &TaskAttacher::onAttachmentOffsetRollChanged); + this, &TaskAttacher::onAttachmentOffsetRollChanged); connect(ui->checkBoxFlip, &QCheckBox::toggled, - this, &TaskAttacher::onCheckFlip); + this, &TaskAttacher::onCheckFlip); connect(ui->buttonRef1, &QPushButton::clicked, - this, &TaskAttacher::onButtonRef1); + this, &TaskAttacher::onButtonRef1); connect(ui->lineRef1, &QLineEdit::textEdited, - this, &TaskAttacher::onRefName1); + this, &TaskAttacher::onRefName1); connect(ui->buttonRef2, &QPushButton::clicked, - this, &TaskAttacher::onButtonRef2); + this, &TaskAttacher::onButtonRef2); connect(ui->lineRef2, &QLineEdit::textEdited, - this, &TaskAttacher::onRefName2); + this, &TaskAttacher::onRefName2); connect(ui->buttonRef3, &QPushButton::clicked, - this, &TaskAttacher::onButtonRef3); + this, &TaskAttacher::onButtonRef3); connect(ui->lineRef3, &QLineEdit::textEdited, - this, &TaskAttacher::onRefName3); + this, &TaskAttacher::onRefName3); connect(ui->buttonRef4, &QPushButton::clicked, - this, &TaskAttacher::onButtonRef4); + this, &TaskAttacher::onButtonRef4); connect(ui->lineRef4, &QLineEdit::textEdited, - this, &TaskAttacher::onRefName4); + this, &TaskAttacher::onRefName4); connect(ui->listOfModes, &QListWidget::itemSelectionChanged, - this, &TaskAttacher::onModeSelect); + this, &TaskAttacher::onModeSelect); // clang-format on this->groupLayout()->addWidget(proxy); @@ -205,19 +209,20 @@ TaskAttacher::TaskAttacher(Gui::ViewProviderDocumentObject *ViewProvider, QWidge this->iActiveRef = 0; else this->iActiveRef = -1; - if (pcAttach->AttachmentSupport.getSize() == 0){ + if (pcAttach->AttachmentSupport.getSize() == 0) { autoNext = true; - } else { + } + else { autoNext = false; } - ui->attachmentOffsetX->bind(App::ObjectIdentifier::parse(ViewProvider->getObject(),std::string("AttachmentOffset.Base.x"))); - ui->attachmentOffsetY->bind(App::ObjectIdentifier::parse(ViewProvider->getObject(),std::string("AttachmentOffset.Base.y"))); - ui->attachmentOffsetZ->bind(App::ObjectIdentifier::parse(ViewProvider->getObject(),std::string("AttachmentOffset.Base.z"))); + ui->attachmentOffsetX->bind(App::ObjectIdentifier::parse(ViewProvider->getObject(), std::string("AttachmentOffset.Base.x"))); + ui->attachmentOffsetY->bind(App::ObjectIdentifier::parse(ViewProvider->getObject(), std::string("AttachmentOffset.Base.y"))); + ui->attachmentOffsetZ->bind(App::ObjectIdentifier::parse(ViewProvider->getObject(), std::string("AttachmentOffset.Base.z"))); - ui->attachmentOffsetYaw->bind(App::ObjectIdentifier::parse(ViewProvider->getObject(),std::string("AttachmentOffset.Rotation.Yaw"))); - ui->attachmentOffsetPitch->bind(App::ObjectIdentifier::parse(ViewProvider->getObject(),std::string("AttachmentOffset.Rotation.Pitch"))); - ui->attachmentOffsetRoll->bind(App::ObjectIdentifier::parse(ViewProvider->getObject(),std::string("AttachmentOffset.Rotation.Roll"))); + ui->attachmentOffsetYaw->bind(App::ObjectIdentifier::parse(ViewProvider->getObject(), std::string("AttachmentOffset.Rotation.Yaw"))); + ui->attachmentOffsetPitch->bind(App::ObjectIdentifier::parse(ViewProvider->getObject(), std::string("AttachmentOffset.Rotation.Pitch"))); + ui->attachmentOffsetRoll->bind(App::ObjectIdentifier::parse(ViewProvider->getObject(), std::string("AttachmentOffset.Rotation.Roll"))); visibilityAutomation(true); updateAttachmentOffsetUI(); @@ -293,10 +298,11 @@ void TaskAttacher::updateReferencesUI() pcAttach->attacher().suggestMapModes(this->lastSuggestResult); if (this->lastSuggestResult.message != SuggestResult::srOK) { - if(!this->lastSuggestResult.nextRefTypeHint.empty()){ + if (!this->lastSuggestResult.nextRefTypeHint.empty()) { //message = "Need more references"; } - } else { + } + else { completed = true; } @@ -314,24 +320,29 @@ bool TaskAttacher::updatePreview() Part::AttachExtension* pcAttach = ViewProvider->getObject()->getExtensionByType(); QString errMessage; bool attached = false; - try{ + try { attached = pcAttach->positionBySupport(); - } catch (Base::Exception &err){ + } + catch (Base::Exception& err) { errMessage = QCoreApplication::translate("Exception", err.what()); - } catch (Standard_Failure &err){ + } + catch (Standard_Failure& err) { errMessage = tr("OCC error: %1").arg(QString::fromLatin1(err.GetMessageString())); - } catch (...) { + } + catch (...) { errMessage = tr("unknown error"); } - if (errMessage.length()>0){ + if (errMessage.length() > 0) { ui->message->setText(tr("Attachment mode failed: %1").arg(errMessage)); ui->message->setStyleSheet(QString::fromLatin1("QLabel{color: red;}")); - } else { - if (!attached){ + } + else { + if (!attached) { ui->message->setText(tr("Not attached")); ui->message->setStyleSheet(QString()); - } else { - std::vector strs = AttacherGui::getUIStrings(pcAttach->attacher().getTypeId(),eMapMode(pcAttach->MapMode.getValue())); + } + else { + std::vector strs = AttacherGui::getUIStrings(pcAttach->attacher().getTypeId(), eMapMode(pcAttach->MapMode.getValue())); ui->message->setText(tr("Attached with mode %1").arg(strs[0])); ui->message->setStyleSheet(QString::fromLatin1("QLabel{color: green;}")); } @@ -345,23 +356,25 @@ bool TaskAttacher::updatePreview() QLineEdit* TaskAttacher::getLine(unsigned idx) { - switch(idx) { - case 0: return ui->lineRef1; - case 1: return ui->lineRef2; - case 2: return ui->lineRef3; - case 3: return ui->lineRef4; - default: return nullptr; + switch (idx) { + case 0: return ui->lineRef1; + case 1: return ui->lineRef2; + case 2: return ui->lineRef3; + case 3: return ui->lineRef4; + default: return nullptr; } } void TaskAttacher::onSelectionChanged(const Gui::SelectionChanges& msg) { - if (!ViewProvider) + if (!ViewProvider) { return; + } if (msg.Type == Gui::SelectionChanges::AddSelection) { - if (iActiveRef < 0) + if (iActiveRef < 0) { return; + } // Note: The validity checking has already been done in ReferenceSelection.cpp Part::AttachExtension* pcAttach = ViewProvider->getObject()->getExtensionByType(); @@ -374,18 +387,20 @@ void TaskAttacher::onSelectionChanged(const Gui::SelectionChanges& msg) std::string subname = msg.pSubName; // Remove subname for planes and datum features - if (selObj->isDerivedFrom() || - selObj->isDerivedFrom()) + if (selObj->isDerivedFrom() || selObj->isDerivedFrom()) { subname = ""; + } // eliminate duplicate selections - for (size_t r = 0; r < refs.size(); r++) - if ((refs[r] == selObj) && (refnames[r] == subname)) + for (size_t r = 0; r < refs.size(); r++) { + if ((refs[r] == selObj) && (refnames[r] == subname)) { return; + } + } - if (autoNext && iActiveRef > 0 && iActiveRef == static_cast(refnames.size())){ - if (refs[iActiveRef-1] == selObj - && refnames[iActiveRef-1].length() != 0 && subname.length() == 0){ + if (autoNext && iActiveRef > 0 && iActiveRef == static_cast(refnames.size())) { + if (refs[iActiveRef - 1] == selObj + && refnames[iActiveRef - 1].length() != 0 && subname.length() == 0) { //A whole object was selected by clicking it twice. Fill it //into previous reference, where a sub-named reference filled by //the first click is already stored. @@ -396,7 +411,8 @@ void TaskAttacher::onSelectionChanged(const Gui::SelectionChanges& msg) if (iActiveRef < static_cast(refs.size())) { refs[iActiveRef] = selObj; refnames[iActiveRef] = subname; - } else { + } + else { refs.push_back(selObj); refnames.push_back(subname); } @@ -406,17 +422,18 @@ void TaskAttacher::onSelectionChanged(const Gui::SelectionChanges& msg) pcAttach->AttachmentSupport.setValues(refs, refnames); updateListOfModes(); eMapMode mmode = getActiveMapMode();//will be mmDeactivated, if selected or if no modes are available - if(mmode == mmDeactivated){ + if (mmode == mmDeactivated) { //error = true; this->completed = false; - } else { + } + else { this->completed = true; } pcAttach->MapMode.setValue(mmode); selectMapMode(mmode); updatePreview(); } - catch(Base::Exception& e) { + catch (Base::Exception& e) { //error = true; ui->message->setText(QCoreApplication::translate("Exception", e.what())); ui->message->setStyleSheet(QString::fromLatin1("QLabel{color: red;}")); @@ -431,11 +448,13 @@ void TaskAttacher::onSelectionChanged(const Gui::SelectionChanges& msg) } if (autoNext) { - if (iActiveRef == -1){ + if (iActiveRef == -1) { //nothing to do - } else if (iActiveRef == 4 || this->lastSuggestResult.nextRefTypeHint.empty()){ + } + else if (iActiveRef == 4 || this->lastSuggestResult.nextRefTypeHint.empty()) { iActiveRef = -1; - } else { + } + else { iActiveRef++; } } @@ -462,17 +481,17 @@ void TaskAttacher::onAttachmentOffsetChanged(double /*val*/, int idx) if (idx == 2) { pos.z = ui->attachmentOffsetZ->value().getValueAs(Base::Quantity::MilliMetre); } - if (idx >= 0 && idx <= 2){ + if (idx >= 0 && idx <= 2) { pl.setPosition(pos); } - if (idx >= 3 && idx <= 5){ + if (idx >= 3 && idx <= 5) { double yaw, pitch, roll; yaw = ui->attachmentOffsetYaw->value().getValueAs(Base::Quantity::Degree); pitch = ui->attachmentOffsetPitch->value().getValueAs(Base::Quantity::Degree); roll = ui->attachmentOffsetRoll->value().getValueAs(Base::Quantity::Degree); Base::Rotation rot; - rot.setYawPitchRoll(yaw,pitch,roll); + rot.setYawPitchRoll(yaw, pitch, roll); pl.setRotation(rot); } @@ -526,7 +545,8 @@ void TaskAttacher::onButtonRef(const bool checked, unsigned idx) if (checked) { Gui::Selection().clearSelection(); iActiveRef = idx; - } else { + } + else { iActiveRef = -1; } updateRefButton(0); @@ -619,12 +639,15 @@ void TaskAttacher::onRefName(const QString& text, unsigned idx) if (obj->isDerivedFrom()) { // everything is OK (we assume a Part can only have exactly 3 App::Plane objects located at the base of the feature tree) subElement.clear(); - } else if (obj->isDerivedFrom()) { + } + else if (obj->isDerivedFrom()) { // everything is OK (we assume a Part can only have exactly 3 App::Line objects located at the base of the feature tree) subElement.clear(); - } else if (obj->isDerivedFrom()) { + } + else if (obj->isDerivedFrom()) { subElement.clear(); - } else { + } + else { // TODO: check validity of the text that was entered: Does subElement actually reference to an element on the obj? auto getSubshapeName = [](const QString& part) -> std::string { @@ -672,7 +695,8 @@ void TaskAttacher::onRefName(const QString& text, unsigned idx) if (idx < refs.size()) { refs[idx] = obj; refnames[idx] = subElement; - } else { + } + else { refs.push_back(obj); refnames.emplace_back(subElement); } @@ -690,12 +714,12 @@ void TaskAttacher::updateRefButton(int idx) return; QAbstractButton* b; - switch(idx){ - case 0: b = ui->buttonRef1; break; - case 1: b = ui->buttonRef2; break; - case 2: b = ui->buttonRef3; break; - case 3: b = ui->buttonRef4; break; - default: throw Base::IndexError("button index out of range"); + switch (idx) { + case 0: b = ui->buttonRef1; break; + case 1: b = ui->buttonRef2; break; + case 2: b = ui->buttonRef3; break; + case 3: b = ui->buttonRef4; break; + default: throw Base::IndexError("button index out of range"); } Part::AttachExtension* pcAttach = ViewProvider->getObject()->getExtensionByType(); @@ -713,10 +737,12 @@ void TaskAttacher::updateRefButton(int idx) if (iActiveRef == idx) { b->setText(tr("Selecting...")); - } else if (idx < static_cast(this->lastSuggestResult.references_Types.size())){ + } + else if (idx < static_cast(this->lastSuggestResult.references_Types.size())) { b->setText(AttacherGui::getShapeTypeText(this->lastSuggestResult.references_Types[idx])); - } else { - b->setText(tr("Reference%1").arg(idx+1)); + } + else { + b->setText(tr("Reference%1").arg(idx + 1)); } } @@ -740,9 +766,9 @@ void TaskAttacher::updateAttachmentOffsetUI() ui->attachmentOffsetPitch->blockSignals(bBlock); ui->attachmentOffsetRoll->blockSignals(bBlock); - ui->attachmentOffsetX->setValue(Base::Quantity(pos.x,Base::Unit::Length)); - ui->attachmentOffsetY->setValue(Base::Quantity(pos.y,Base::Unit::Length)); - ui->attachmentOffsetZ->setValue(Base::Quantity(pos.z,Base::Unit::Length)); + ui->attachmentOffsetX->setValue(Base::Quantity(pos.x, Base::Unit::Length)); + ui->attachmentOffsetY->setValue(Base::Quantity(pos.y, Base::Unit::Length)); + ui->attachmentOffsetZ->setValue(Base::Quantity(pos.z, Base::Unit::Length)); ui->attachmentOffsetYaw->setValue(yaw); ui->attachmentOffsetPitch->setValue(pitch); ui->attachmentOffsetRoll->setValue(roll); @@ -750,13 +776,13 @@ void TaskAttacher::updateAttachmentOffsetUI() auto expressions = ViewProvider->getObject()->ExpressionEngine.getExpressions(); bool bRotationBound = false; bRotationBound = bRotationBound || - expressions.find(App::ObjectIdentifier::parse(ViewProvider->getObject(),std::string("AttachmentOffset.Rotation.Angle"))) != expressions.end(); + expressions.find(App::ObjectIdentifier::parse(ViewProvider->getObject(), std::string("AttachmentOffset.Rotation.Angle"))) != expressions.end(); bRotationBound = bRotationBound || - expressions.find(App::ObjectIdentifier::parse(ViewProvider->getObject(),std::string("AttachmentOffset.Rotation.Axis.x"))) != expressions.end(); + expressions.find(App::ObjectIdentifier::parse(ViewProvider->getObject(), std::string("AttachmentOffset.Rotation.Axis.x"))) != expressions.end(); bRotationBound = bRotationBound || - expressions.find(App::ObjectIdentifier::parse(ViewProvider->getObject(),std::string("AttachmentOffset.Rotation.Axis.y"))) != expressions.end(); + expressions.find(App::ObjectIdentifier::parse(ViewProvider->getObject(), std::string("AttachmentOffset.Rotation.Axis.y"))) != expressions.end(); bRotationBound = bRotationBound || - expressions.find(App::ObjectIdentifier::parse(ViewProvider->getObject(),std::string("AttachmentOffset.Rotation.Axis.z"))) != expressions.end(); + expressions.find(App::ObjectIdentifier::parse(ViewProvider->getObject(), std::string("AttachmentOffset.Rotation.Axis.z"))) != expressions.end(); ui->attachmentOffsetYaw->setEnabled(!bRotationBound); ui->attachmentOffsetPitch->setEnabled(!bRotationBound); @@ -794,22 +820,23 @@ void TaskAttacher::updateListOfModes() this->lastSuggestResult.bestFitMode = mmDeactivated; size_t lastValidModeItemIndex = mmDummy_NumberOfModes; - if (pcAttach->AttachmentSupport.getSize() > 0){ + if (pcAttach->AttachmentSupport.getSize() > 0) { pcAttach->attacher().suggestMapModes(this->lastSuggestResult); modesInList = this->lastSuggestResult.allApplicableModes; modesInList.insert(modesInList.begin(), mmDeactivated); // always have the option to choose Deactivated mode //add reachable modes to the list, too, but gray them out (using lastValidModeItemIndex, later) - lastValidModeItemIndex = modesInList.size()-1; - for(std::pair &rm: this->lastSuggestResult.reachableModes){ + lastValidModeItemIndex = modesInList.size() - 1; + for (std::pair& rm : this->lastSuggestResult.reachableModes) { modesInList.push_back(rm.first); } - } else { + } + else { //no references - display all modes modesInList.clear(); modesInList.push_back(mmDeactivated); - for( int mmode = 0 ; mmode < mmDummy_NumberOfModes ; mmode++){ + for (int mmode = 0; mmode < mmDummy_NumberOfModes; mmode++) { if (pcAttach->attacher().modeEnabled[mmode]) modesInList.push_back(eMapMode(mmode)); } @@ -820,40 +847,42 @@ void TaskAttacher::updateListOfModes() ui->listOfModes->clear(); QListWidgetItem* iSelect = nullptr; if (!modesInList.empty()) { - for (size_t i = 0 ; i < modesInList.size() ; ++i){ + for (size_t i = 0; i < modesInList.size(); ++i) { eMapMode mmode = modesInList[i]; - std::vector mstr = AttacherGui::getUIStrings(pcAttach->attacher().getTypeId(),mmode); + std::vector mstr = AttacherGui::getUIStrings(pcAttach->attacher().getTypeId(), mmode); ui->listOfModes->addItem(mstr[0]); QListWidgetItem* item = ui->listOfModes->item(i); QString tooltip = mstr[1]; if (mmode != mmDeactivated) { tooltip += QString::fromLatin1("\n\n%1\n%2") - .arg(tr("Reference combinations:"), - AttacherGui::getRefListForMode(pcAttach->attacher(),mmode).join(QString::fromLatin1("\n"))); + .arg(tr("Reference combinations:"), + AttacherGui::getRefListForMode(pcAttach->attacher(), mmode).join(QString::fromLatin1("\n"))); } item->setToolTip(tooltip); if (mmode == curMode && curMode != mmDeactivated) iSelect = ui->listOfModes->item(i); - if (i > lastValidModeItemIndex){ + if (i > lastValidModeItemIndex) { //potential mode - can be reached by selecting more stuff item->setFlags(item->flags() & ~(Qt::ItemFlag::ItemIsEnabled | Qt::ItemFlag::ItemIsSelectable)); - refTypeStringList &extraRefs = this->lastSuggestResult.reachableModes[mmode]; - if (extraRefs.size() == 1){ + refTypeStringList& extraRefs = this->lastSuggestResult.reachableModes[mmode]; + if (extraRefs.size() == 1) { QStringList buf; - for(eRefType rt : extraRefs[0]){ + for (eRefType rt : extraRefs[0]) { buf.append(AttacherGui::getShapeTypeText(rt)); } item->setText(tr("%1 (add %2)").arg( - item->text(), - buf.join(QString::fromLatin1("+")) - )); - } else { + item->text(), + buf.join(QString::fromLatin1("+")) + )); + } + else { item->setText(tr("%1 (add more references)").arg(item->text())); } - } else if (mmode == this->lastSuggestResult.bestFitMode){ + } + else if (mmode == this->lastSuggestResult.bestFitMode) { //suggested mode - make bold QFont fnt = item->font(); fnt.setBold(true); @@ -873,7 +902,7 @@ void TaskAttacher::updateListOfModes() void TaskAttacher::selectMapMode(eMapMode mmode) { ui->listOfModes->blockSignals(true); - for (size_t i = 0; i < modesInList.size(); ++i) { + for (size_t i = 0; i < modesInList.size(); ++i) { if (modesInList[i] == mmode) { ui->listOfModes->item(i)->setSelected(true); } @@ -910,7 +939,7 @@ void TaskAttacher::onRefName3(const QString& text) onRefName(text, 2); } -void TaskAttacher::onRefName4(const QString &text) +void TaskAttacher::onRefName4(const QString& text) { onRefName(text, 3); } @@ -920,7 +949,7 @@ bool TaskAttacher::getFlip() const return ui->checkBoxFlip->isChecked(); } -void TaskAttacher::changeEvent(QEvent *e) +void TaskAttacher::changeEvent(QEvent* e) { TaskBox::changeEvent(e); if (e->type() == QEvent::LanguageChange) { @@ -958,11 +987,11 @@ void TaskAttacher::changeEvent(QEvent *e) void TaskAttacher::visibilityAutomation(bool opening_not_closing) { - auto defvisfunc = [] (bool opening_not_closing, - const std::string &postfix, - Gui::ViewProviderDocumentObject* vp, - App::DocumentObject *editObj, - const std::string& editSubName) { + auto defvisfunc = [](bool opening_not_closing, + const std::string& postfix, + Gui::ViewProviderDocumentObject* vp, + App::DocumentObject* editObj, + const std::string& editSubName) { if (opening_not_closing) { QString code = QString::fromLatin1( "import Show\n" @@ -981,19 +1010,19 @@ void TaskAttacher::visibilityAutomation(bool opening_not_closing) "\t\tif len(tvObj.AttachmentSupport) > 0:\n" "\t\t\t_tv_%4.show([lnk[0] for lnk in tvObj.AttachmentSupport])\n" "del(tvObj)" - ).arg( - QString::fromLatin1(Gui::Command::getObjectCmd(vp->getObject()).c_str()), - QString::fromLatin1(Gui::Command::getObjectCmd(editObj).c_str()), - QString::fromLatin1(editSubName.c_str()), - QString::fromLatin1(postfix.c_str())); - Gui::Command::runCommand(Gui::Command::Gui,code.toLatin1().constData()); + ).arg( + QString::fromLatin1(Gui::Command::getObjectCmd(vp->getObject()).c_str()), + QString::fromLatin1(Gui::Command::getObjectCmd(editObj).c_str()), + QString::fromLatin1(editSubName.c_str()), + QString::fromLatin1(postfix.c_str())); + Gui::Command::runCommand(Gui::Command::Gui, code.toLatin1().constData()); } else if (!postfix.empty()) { QString code = QString::fromLatin1( "_tv_%1.restore()\n" "del(_tv_%1)" - ).arg(QString::fromLatin1(postfix.c_str())); - Gui::Command::runCommand(Gui::Command::Gui,code.toLatin1().constData()); + ).arg(QString::fromLatin1(postfix.c_str())); + Gui::Command::runCommand(Gui::Command::Gui, code.toLatin1().constData()); } }; @@ -1009,18 +1038,19 @@ void TaskAttacher::visibilityAutomation(bool opening_not_closing) return; auto editDoc = Gui::Application::Instance->editDocument(); - App::DocumentObject *editObj = ViewProvider->getObject(); + App::DocumentObject* editObj = ViewProvider->getObject(); std::string editSubName; auto sels = Gui::Selection().getSelection(nullptr, Gui::ResolveMode::NoResolve, true); - if(!sels.empty() && sels[0].pResolvedObject - && sels[0].pResolvedObject->getLinkedObject()==editObj) + if (!sels.empty() && sels[0].pResolvedObject + && sels[0].pResolvedObject->getLinkedObject() == editObj) { editObj = sels[0].pObject; editSubName = sels[0].SubName; - } else { - ViewProviderDocumentObject *editVp = nullptr; + } + else { + ViewProviderDocumentObject* editVp = nullptr; if (editDoc) { - editDoc->getInEdit(&editVp,&editSubName); + editDoc->getInEdit(&editVp, &editSubName); if (editVp) editObj = editVp->getObject(); } @@ -1029,7 +1059,7 @@ void TaskAttacher::visibilityAutomation(bool opening_not_closing) try { visAutoFunc(opening_not_closing, ObjectName, ViewProvider, editObj, editSubName); } - catch (const Base::Exception &e){ + catch (const Base::Exception& e) { e.ReportException(); } catch (const Py::Exception&) { @@ -1043,7 +1073,7 @@ void TaskAttacher::visibilityAutomation(bool opening_not_closing) objName.swap(ObjectName); visAutoFunc(opening_not_closing, objName, nullptr, nullptr, std::string()); } - catch (Base::Exception &e) { + catch (Base::Exception& e) { e.ReportException(); } } @@ -1054,14 +1084,14 @@ void TaskAttacher::visibilityAutomation(bool opening_not_closing) // TaskDialog //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -TaskDlgAttacher::TaskDlgAttacher(Gui::ViewProviderDocumentObject *ViewProvider, bool createBox) - : TaskDialog(),ViewProvider(ViewProvider), parameter(nullptr) +TaskDlgAttacher::TaskDlgAttacher(Gui::ViewProviderDocumentObject* ViewProvider, bool createBox) + : TaskDialog(), ViewProvider(ViewProvider), parameter(nullptr) { assert(ViewProvider); setDocumentName(ViewProvider->getDocument()->getDocument()->getName()); - if(createBox) { - parameter = new TaskAttacher(ViewProvider, nullptr, QString(), tr("Attachment")); + if (createBox) { + parameter = new TaskAttacher(ViewProvider, nullptr, QString(), tr("Attachment")); Content.push_back(parameter); } } @@ -1096,11 +1126,11 @@ bool TaskDlgAttacher::accept() //DeepSOIC: changed this to heavily rely on dialog constantly updating feature properties //if (pcAttach->AttachmentOffset.isTouched()){ - Base::Placement plm = pcAttach->AttachmentOffset.getValue(); - double yaw, pitch, roll; - plm.getRotation().getYawPitchRoll(yaw,pitch,roll); - Gui::cmdAppObjectArgs(obj, "AttachmentOffset = App.Placement(App.Vector(%.10f, %.10f, %.10f), App.Rotation(%.10f, %.10f, %.10f))", - plm.getPosition().x, plm.getPosition().y, plm.getPosition().z, yaw, pitch, roll); + Base::Placement plm = pcAttach->AttachmentOffset.getValue(); + double yaw, pitch, roll; + plm.getRotation().getYawPitchRoll(yaw, pitch, roll); + Gui::cmdAppObjectArgs(obj, "AttachmentOffset = App.Placement(App.Vector(%.10f, %.10f, %.10f), App.Rotation(%.10f, %.10f, %.10f))", + plm.getPosition().x, plm.getPosition().y, plm.getPosition().z, yaw, pitch, roll); //} Gui::cmdAppObjectArgs(obj, "MapReversed = %s", pcAttach->MapReversed.getValue() ? "True" : "False"); From a6efacdf24a69b48795fc1e0ca112a8405beeb02 Mon Sep 17 00:00:00 2001 From: PaddleStroke Date: Wed, 9 Oct 2024 16:30:17 +0200 Subject: [PATCH 05/15] TaskAttacher: Make sure hierarchy is respected when adding references. --- src/Mod/Part/App/Attacher.cpp | 78 +++++++++++-------- src/Mod/Part/Gui/TaskAttacher.cpp | 120 ++++++++++++++++++++++++++++-- src/Mod/Part/Gui/TaskAttacher.h | 2 + 3 files changed, 163 insertions(+), 37 deletions(-) diff --git a/src/Mod/Part/App/Attacher.cpp b/src/Mod/Part/App/Attacher.cpp index b452fd90f4..2dd69ed34f 100644 --- a/src/Mod/Part/App/Attacher.cpp +++ b/src/Mod/Part/App/Attacher.cpp @@ -27,6 +27,7 @@ # include # include # include +# include # include # include # include @@ -820,7 +821,7 @@ GProp_GProps AttachEngine::getInertialPropsOfShape(const std::vector &objs, - const std::vector &sub, + const std::vector &subs, std::vector &geofs, std::vector &shapes, std::vector &storage, @@ -831,50 +832,56 @@ void AttachEngine::readLinks(const std::vector &objs, shapes.resize(objs.size()); types.resize(objs.size()); for (std::size_t i = 0; i < objs.size(); i++) { - if (!objs[i]->getTypeId().isDerivedFrom(App::GeoFeature::getClassTypeId())) { + std::string fullSub = subs[i]; + const char* element = Data::findElementName(fullSub.c_str()); + App::DocumentObject* obj = objs[i]->getSubObject(subs[i].c_str()); + + auto* geof = dynamic_cast(obj); + if (!geof) { FC_THROWM(AttachEngineException, "AttachEngine3D: attached to a non App::GeoFeature '" - << objs[i]->getNameInDocument() << "'"); + << obj->getNameInDocument() << "'"); } - auto* geof = dynamic_cast(objs[i]); geofs[i] = geof; - Part::TopoShape shape; + TopoDS_Shape myShape; + Base::Placement plc = App::GeoFeature::getGlobalPlacement(obj, objs[i], fullSub); if (geof->isDerivedFrom(App::Plane::getClassTypeId())) { // obtain Z axis and origin of placement Base::Vector3d norm; - geof->Placement.getValue().getRotation().multVec(Base::Vector3d(0.0, 0.0, 1.0), norm); + plc.getRotation().multVec(Base::Vector3d(0.0, 0.0, 1.0), norm); Base::Vector3d org; - geof->Placement.getValue().multVec(Base::Vector3d(), org); + plc.multVec(Base::Vector3d(), org); // make shape - an local-XY plane infinite face gp_Pln plane = gp_Pln(gp_Pnt(org.x, org.y, org.z), gp_Dir(norm.x, norm.y, norm.z)); - TopoDS_Shape myShape = BRepBuilderAPI_MakeFace(plane).Shape(); + myShape = BRepBuilderAPI_MakeFace(plane).Shape(); myShape.Infinite(true); - storage.emplace_back(myShape); - shapes[i] = &(storage[storage.size() - 1]); } else if (geof->isDerivedFrom(App::Line::getClassTypeId())) { // obtain X axis and origin of placement - // note an inconsistency: App::Line is along local X, PartDesign::DatumLine is along - // local Z. Base::Vector3d dir; - geof->Placement.getValue().getRotation().multVec(Base::Vector3d(1.0, 0.0, 0.0), dir); + plc.getRotation().multVec(Base::Vector3d(0.0, 0.0, 1.0), dir); Base::Vector3d org; - geof->Placement.getValue().multVec(Base::Vector3d(), org); + plc.multVec(Base::Vector3d(), org); // make shape - an infinite line along local X axis gp_Lin line = gp_Lin(gp_Pnt(org.x, org.y, org.z), gp_Dir(dir.x, dir.y, dir.z)); - TopoDS_Shape myShape = BRepBuilderAPI_MakeEdge(line).Shape(); + myShape = BRepBuilderAPI_MakeEdge(line).Shape(); myShape.Infinite(true); - storage.emplace_back(myShape); - shapes[i] = &(storage[storage.size() - 1]); + } + else if (geof->isDerivedFrom(App::Point::getClassTypeId())) { + Base::Vector3d org; + plc.multVec(Base::Vector3d(), org); + + gp_Pnt pnt = gp_Pnt(org.x, org.y, org.z); + myShape = BRepBuilderAPI_MakeVertex(pnt).Shape(); } else { try { - shape = Part::Feature::getTopoShape(geof, sub[i].c_str(), true); + Part::TopoShape shape = Part::Feature::getTopoShape(geof, element, true); for (;;) { if (shape.isNull()) { FC_THROWM(AttachEngineException, "AttachEngine3D: subshape not found " - << objs[i]->getNameInDocument() << '.' << sub[i]); + << objs[i]->getNameInDocument() << '.' << subs[i]); } if (shape.shapeType() != TopAbs_COMPOUND || shape.countSubShapes(TopAbs_SHAPE) != 1) { @@ -883,32 +890,34 @@ void AttachEngine::readLinks(const std::vector &objs, // auto extract the single sub-shape from a compound shape = shape.getSubTopoShape(TopAbs_SHAPE, 1); } - storage.emplace_back(shape.getShape()); + shape.setPlacement(plc); + myShape = shape.getShape(); } catch (Standard_Failure& e) { FC_THROWM(AttachEngineException, "AttachEngine3D: subshape not found " << objs[i]->getNameInDocument() - << '.' << sub[i] << std::endl + << '.' << subs[i] << std::endl << e.GetMessageString()); } catch (Base::CADKernelError& e) { FC_THROWM(AttachEngineException, "AttachEngine3D: subshape not found " << objs[i]->getNameInDocument() - << '.' << sub[i] << std::endl + << '.' << subs[i] << std::endl << e.what()); } - if (storage.back().IsNull()) { + if (myShape.IsNull()) { FC_THROWM(AttachEngineException, "AttachEngine3D: null subshape " << objs[i]->getNameInDocument() << '.' - << sub[i]); + << subs[i]); } - shapes[i] = &(storage.back()); } + storage.emplace_back(myShape); + 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])); - if (sub[i].length() == 0) { + if (subs[i].length() == 0) { types[i] = eRefType(types[i] | rtFlagHasPlacement); } } @@ -977,6 +986,7 @@ Base::Placement AttachEngine::calculateAttachedPlacement(const Base::Placement& for (auto obj : objs) { ++i; auto& sub = subnames[i]; + obj = obj->getSubObject(sub.c_str()); auto& shadow = shadowSubs[i]; if (shadow.empty() || !Data::hasMissingElement(sub.c_str())) { continue; @@ -1176,9 +1186,13 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vectorPlacement.getValue(); - refOrg = gp_Pnt(Place.getPosition().x, Place.getPosition().y, Place.getPosition().z); + Base::Placement Place = App::GeoFeature::getGlobalPlacement(parts[0], objs[0], subs[0]); + Base::Console().Warning("parts[0] = %s\n", parts[0]->getNameInDocument()); + Base::Console().Warning("objs[0] = %s\n", objs[0]->getNameInDocument()); + Base::Console().Warning("subs[0] = %s\n", subs[0]); + Base::Console().Warning("Place = (%f, %f, %f)\n", Place.getPosition().x, Place.getPosition().y, Place.getPosition().z); + Base::Vector3d vec = Place.getPosition(); + gp_Pnt refOrg = gp_Pnt(vec.x, vec.y, vec.z); // origin of linked object // variables to derive the actual placement. // They are to be set, depending on the mode: @@ -2120,9 +2134,9 @@ AttachEngineLine::_calculateAttachedPlacement(const std::vectorPlacement.getValue(); - refOrg = gp_Pnt(Place.getPosition().x, Place.getPosition().y, Place.getPosition().z); + Base::Placement Place = App::GeoFeature::getGlobalPlacement(parts[0], objs[0], subs[0]); + Base::Vector3d vec = Place.getPosition(); + gp_Pnt refOrg = gp_Pnt(vec.x, vec.y, vec.z); // origin of linked object // variables to derive the actual placement. // They are to be set, depending on the mode: diff --git a/src/Mod/Part/Gui/TaskAttacher.cpp b/src/Mod/Part/Gui/TaskAttacher.cpp index 1b57ea6daf..547ff84d25 100644 --- a/src/Mod/Part/Gui/TaskAttacher.cpp +++ b/src/Mod/Part/Gui/TaskAttacher.cpp @@ -43,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -112,7 +113,7 @@ void TaskAttacher::makeRefStrings(std::vector& refstrings, std::vector< TaskAttacher::TaskAttacher(Gui::ViewProviderDocumentObject* ViewProvider, QWidget* parent, QString picture, QString text, TaskAttacher::VisibilityFunction visFunc) : TaskBox(Gui::BitmapFactory().pixmap(picture.toLatin1()), text, true, parent) - , SelectionObserver(ViewProvider) + , SelectionObserver(ViewProvider, true, Gui::ResolveMode::NoResolve) , ViewProvider(ViewProvider) , ui(new Ui_TaskAttacher) , visibilityFunc(visFunc) @@ -365,6 +366,112 @@ QLineEdit* TaskAttacher::getLine(unsigned idx) } } +void TaskAttacher::processSelection(App::DocumentObject*& rootObj, std::string& sub) +{ + // The reference that we store must take into account the hierarchy of geoFeatures. For example: + // - Part + // - - Cube + // - Sketch + // if sketch is attached to Cube.Face1 then it must store Part:Cube.Face3 as Sketch is outside of Part. + // - Part + // - - Cube + // - - Sketch + // In this example if must store Cube:Face3 because Sketch is inside Part, sibling of Cube. + // So placement of Part is already taken into account. + // - Part1 + // - - Part2 + // - - - Cube + // - - Sketch + // In this example it must store Part2:Cube.Face3 since Part1 is already taken into account. + // - Part1 + // - - Part2 + // - - - Cube + // - - Part3 + // - - - Sketch + // In this example it's not possible because Sketch has Part3 placement. So it should be rejected + // So we need to take the selection object and subname, and process them to get the correct obj/sub based + // on attached and attaching objects positions. + + std::vector names = Base::Tools::splitSubName(sub); + if (!rootObj || names.size() < 2) { + return; + } + names.insert(names.begin(), rootObj->getNameInDocument()); + + App::Document* doc = rootObj->getDocument(); + App::DocumentObject* attachingObj = ViewProvider->getObject(); // Attaching object + App::DocumentObject* subObj = rootObj->getSubObject(sub.c_str()); // Object being attached. + if (!subObj || subObj == rootObj) { + // Case of root object. We don't need to modify it. + return; + } + if (subObj == attachingObj) { + //prevent self-referencing + rootObj = nullptr; + return; + } + + // Check if attachingObj is a root object. if so we keep the full path. + auto* group = App::GeoFeatureGroupExtension::getGroupOfObject(attachingObj); + if (!group) { + if (attachingObj->getDocument() != rootObj->getDocument()) { + // If it's not in same document then it's not a good selection + rootObj = nullptr; + } + // if it's same document we keep the rootObj and sub unchanged. + return; + } + + for (size_t i = 0; i < names.size(); ++i) { + App::DocumentObject* obj = doc->getObject(names[i].c_str()); + if (!obj) { + Base::Console().TranslatedUserError("TaskAttacher", + "Unsuitable selection: '%s' cannot be attached to '%s' from within it's group '%s'.\n", + attachingObj->getFullLabel(), subObj->getFullLabel(), group->getFullLabel()); + rootObj = nullptr; + return; + } + + // In case the attaching object is in a link to a part. + // For instance : + // - Part1 + // - - LinkToPart2 + // - - - Cube + // - - - Sketch + obj = obj->getLinkedObject(); + + if (obj == group) { + ++i; + obj = doc->getObject(names[i].c_str()); + if (!obj) { + return; + } + + rootObj = obj; + + // Rebuild 'sub' starting from the next element after the current 'name' + sub = ""; + for (size_t j = i + 1; j < names.size(); ++j) { + sub += names[j]; + if (j != names.size() - 1) { + sub += "."; // Add a period between elements + } + } + return; + } + } + + // if we reach this point it means that attaching object's group is outside of + // the scope of the attached object. For instance: + // - Part1 + // - - Part2 + // - - - Cube + // - - Part3 + // - - - Sketch + // In this case the selection is not acceptable. + rootObj = nullptr; +} + void TaskAttacher::onSelectionChanged(const Gui::SelectionChanges& msg) { if (!ViewProvider) { @@ -377,14 +484,17 @@ void TaskAttacher::onSelectionChanged(const Gui::SelectionChanges& msg) } // Note: The validity checking has already been done in ReferenceSelection.cpp - Part::AttachExtension* pcAttach = ViewProvider->getObject()->getExtensionByType(); + App::DocumentObject* obj = ViewProvider->getObject(); + Part::AttachExtension* pcAttach = obj->getExtensionByType(); std::vector refs = pcAttach->AttachmentSupport.getValues(); std::vector refnames = pcAttach->AttachmentSupport.getSubValues(); - App::DocumentObject* selObj = ViewProvider->getObject()->getDocument()->getObject(msg.pObjectName); - if (!selObj || selObj == ViewProvider->getObject())//prevent self-referencing - return; + App::DocumentObject* selObj = obj->getDocument()->getObject(msg.pObjectName); std::string subname = msg.pSubName; + processSelection(selObj, subname); + if (!selObj) { + return; + } // Remove subname for planes and datum features if (selObj->isDerivedFrom() || selObj->isDerivedFrom()) { diff --git a/src/Mod/Part/Gui/TaskAttacher.h b/src/Mod/Part/Gui/TaskAttacher.h index 8cb5192c00..c8cb3fce9f 100644 --- a/src/Mod/Part/Gui/TaskAttacher.h +++ b/src/Mod/Part/Gui/TaskAttacher.h @@ -113,6 +113,8 @@ private: void updateRefButton(int idx); void updateAttachmentOffsetUI(); + void processSelection(App::DocumentObject*& obj, std::string& sub); + /** * @brief updateListOfModes Fills the mode list with modes that apply to * current set of references. Maintains selection when possible. From deb6ebd276c2e9681a3ad24f7c001c3b1236f73d Mon Sep 17 00:00:00 2001 From: PaddleStroke Date: Tue, 15 Oct 2024 12:23:35 +0200 Subject: [PATCH 06/15] TaskAttacher: Fix the string maker such that it does not show TNP string. --- src/App/GeoFeature.cpp | 2 +- src/App/GeoFeature.h | 2 +- src/Mod/Part/Gui/TaskAttacher.cpp | 35 ++++++++++++------------------- 3 files changed, 15 insertions(+), 24 deletions(-) diff --git a/src/App/GeoFeature.cpp b/src/App/GeoFeature.cpp index 98e627f7c0..d87f405ceb 100644 --- a/src/App/GeoFeature.cpp +++ b/src/App/GeoFeature.cpp @@ -134,7 +134,7 @@ ElementNamePair GeoFeature::_getElementName(const char* name, } } -DocumentObject* GeoFeature::resolveElement(DocumentObject* obj, +DocumentObject* GeoFeature::resolveElement(const DocumentObject* obj, const char* subname, ElementNamePair& elementName, bool append, diff --git a/src/App/GeoFeature.h b/src/App/GeoFeature.h index 7288a5f3bd..cb4dcfbd66 100644 --- a/src/App/GeoFeature.h +++ b/src/App/GeoFeature.h @@ -104,7 +104,7 @@ public: * * @return Return the owner object of the element */ - static DocumentObject* resolveElement(App::DocumentObject* obj, + static DocumentObject* resolveElement(const App::DocumentObject* obj, const char* subname, ElementNamePair& elementName, bool append = false, diff --git a/src/Mod/Part/Gui/TaskAttacher.cpp b/src/Mod/Part/Gui/TaskAttacher.cpp index 547ff84d25..eb0ae41413 100644 --- a/src/Mod/Part/Gui/TaskAttacher.cpp +++ b/src/Mod/Part/Gui/TaskAttacher.cpp @@ -33,6 +33,7 @@ #include #include +#include #include #include #include @@ -63,32 +64,22 @@ namespace sp = std::placeholders; // Create reference name from PropertyLinkSub values in a translatable fashion const QString makeRefString(const App::DocumentObject* obj, const std::string& sub) { - if (!obj) + if (!obj) { return QObject::tr("No reference selected"); + } - if (obj->isDerivedFrom() || - obj->isDerivedFrom()) - // App::Plane, Line or Datum feature + if (obj->isDerivedFrom() || obj->isDerivedFrom()) { return QString::fromLatin1(obj->getNameInDocument()); + } - if ((sub.size() > 4) && (sub.substr(0, 4) == "Face")) { - int subId = std::atoi(&sub[4]); - return QString::fromLatin1(obj->getNameInDocument()) + QString::fromLatin1(":") + QObject::tr("Face") + QString::number(subId); - } - else if ((sub.size() > 4) && (sub.substr(0, 4) == "Edge")) { - int subId = std::atoi(&sub[4]); - return QString::fromLatin1(obj->getNameInDocument()) + QString::fromLatin1(":") + QObject::tr("Edge") + QString::number(subId); - } - else if ((sub.size() > 6) && (sub.substr(0, 6) == "Vertex")) { - int subId = std::atoi(&sub[6]); - return QString::fromLatin1(obj->getNameInDocument()) + QString::fromLatin1(":") + QObject::tr("Vertex") + QString::number(subId); - } - else { - //something else that face/edge/vertex. Can be empty string. - return QString::fromLatin1(obj->getNameInDocument()) - + (sub.length() > 0 ? QString::fromLatin1(":") : QString()) - + QString::fromLatin1(sub.c_str()); - } + // Hide the TNP string from the user. ie show "Body.Pad.Face6" and not : + // "Body.Pad.;#a:1;:G0;XTR;:Hc94:8,F.Face6" + App::ElementNamePair el; + App::GeoFeature::resolveElement(obj, sub.c_str(), el, true); + + return QString::fromLatin1(obj->getNameInDocument()) + + (sub.length() > 0 ? QString::fromLatin1(":") : QString()) + + QString::fromLatin1(el.oldName.c_str()); } void TaskAttacher::makeRefStrings(std::vector& refstrings, std::vector& refnames) { From e76b638298cfcad8aa77e6dbac053c15bc019f6c Mon Sep 17 00:00:00 2001 From: PaddleStroke Date: Thu, 17 Oct 2024 14:17:53 +0200 Subject: [PATCH 07/15] Task attacher: Handle initial selection when no refs. --- src/Mod/Part/Gui/TaskAttacher.cpp | 102 ++++++++++++++++++++++-------- src/Mod/Part/Gui/TaskAttacher.h | 3 + 2 files changed, 77 insertions(+), 28 deletions(-) diff --git a/src/Mod/Part/Gui/TaskAttacher.cpp b/src/Mod/Part/Gui/TaskAttacher.cpp index eb0ae41413..3dac3f92d9 100644 --- a/src/Mod/Part/Gui/TaskAttacher.cpp +++ b/src/Mod/Part/Gui/TaskAttacher.cpp @@ -231,6 +231,8 @@ TaskAttacher::TaskAttacher(Gui::ViewProviderDocumentObject* ViewProvider, QWidge Gui::Document* document = Gui::Application::Instance->getDocument(ViewProvider->getObject()->getDocument()); connectDelObject = document->signalDeletedObject.connect(bnd1); connectDelDocument = document->signalDeleteDocument.connect(bnd2); + + handleInitialSelection(); } TaskAttacher::~TaskAttacher() @@ -463,6 +465,32 @@ void TaskAttacher::processSelection(App::DocumentObject*& rootObj, std::string& rootObj = nullptr; } +void TaskAttacher::handleInitialSelection() +{ + // We handle initial selection only if it is not attached yet. + App::DocumentObject* obj = ViewProvider->getObject(); + Part::AttachExtension* pcAttach = obj->getExtensionByType(); + std::vector refs = pcAttach->AttachmentSupport.getValues(); + + if (!refs.empty()) { + return; + } + std::vector objNames; + std::vector subNames; + + auto sel = Gui::Selection().getSelectionEx("", + App::DocumentObject::getClassTypeId(), Gui::ResolveMode::NoResolve); + for (auto& selObj : sel) { + std::vector subs = selObj.getSubNames(); + const char* objName = selObj.getFeatName(); + for (auto& sub : subs) { + objNames.push_back(objName); + subNames.push_back(sub); + } + } + addToReference(objNames, subNames); +} + void TaskAttacher::onSelectionChanged(const Gui::SelectionChanges& msg) { if (!ViewProvider) { @@ -470,18 +498,26 @@ void TaskAttacher::onSelectionChanged(const Gui::SelectionChanges& msg) } if (msg.Type == Gui::SelectionChanges::AddSelection) { - if (iActiveRef < 0) { - return; - } + addToReference(msg.pObjectName, msg.pSubName); + } +} - // Note: The validity checking has already been done in ReferenceSelection.cpp - App::DocumentObject* obj = ViewProvider->getObject(); - Part::AttachExtension* pcAttach = obj->getExtensionByType(); +void TaskAttacher::addToReference(std::vector objNames, std::vector subNames) +{ + if (iActiveRef < 0 || objNames.size() != subNames.size()) { + return; + } + + // Note: The validity checking has already been done in ReferenceSelection.cpp + App::DocumentObject* obj = ViewProvider->getObject(); + Part::AttachExtension* pcAttach = obj->getExtensionByType(); + + for (size_t i = 0; i < objNames.size(); ++i) { std::vector refs = pcAttach->AttachmentSupport.getValues(); std::vector refnames = pcAttach->AttachmentSupport.getSubValues(); - App::DocumentObject* selObj = obj->getDocument()->getObject(msg.pObjectName); - std::string subname = msg.pSubName; + App::DocumentObject* selObj = obj->getDocument()->getObject(objNames[i].c_str()); + std::string subname = subNames[i]; processSelection(selObj, subname); if (!selObj) { return; @@ -518,26 +554,29 @@ void TaskAttacher::onSelectionChanged(const Gui::SelectionChanges& msg) refnames.push_back(subname); } - //bool error = false; - try { - pcAttach->AttachmentSupport.setValues(refs, refnames); - updateListOfModes(); - eMapMode mmode = getActiveMapMode();//will be mmDeactivated, if selected or if no modes are available - if (mmode == mmDeactivated) { - //error = true; - this->completed = false; + pcAttach->AttachmentSupport.setValues(refs, refnames); + + if (i == objNames.size() - 1) { + // We check for the moed only for the last ref added. This is to avoid unnecessary warnings + // when we handle initial selection. + try { + updateListOfModes(); + eMapMode mmode = getActiveMapMode();//will be mmDeactivated, if selected or if no modes are available + if (mmode == mmDeactivated) { + //error = true; + this->completed = false; + } + else { + this->completed = true; + } + pcAttach->MapMode.setValue(mmode); + selectMapMode(mmode); + updatePreview(); } - else { - this->completed = true; + catch (Base::Exception& e) { + ui->message->setText(QCoreApplication::translate("Exception", e.what())); + ui->message->setStyleSheet(QString::fromLatin1("QLabel{color: red;}")); } - pcAttach->MapMode.setValue(mmode); - selectMapMode(mmode); - updatePreview(); - } - catch (Base::Exception& e) { - //error = true; - ui->message->setText(QCoreApplication::translate("Exception", e.what())); - ui->message->setStyleSheet(QString::fromLatin1("QLabel{color: red;}")); } QLineEdit* line = getLine(iActiveRef); @@ -559,9 +598,16 @@ void TaskAttacher::onSelectionChanged(const Gui::SelectionChanges& msg) iActiveRef++; } } - - updateReferencesUI(); } + + updateReferencesUI(); +} + +void TaskAttacher::addToReference(const char* objName, const char* subName) +{ + std::string objname = objName; + std::string subname = subName; + addToReference({ objname }, { subname }); } void TaskAttacher::onAttachmentOffsetChanged(double /*val*/, int idx) diff --git a/src/Mod/Part/Gui/TaskAttacher.h b/src/Mod/Part/Gui/TaskAttacher.h index c8cb3fce9f..2833bf3ab8 100644 --- a/src/Mod/Part/Gui/TaskAttacher.h +++ b/src/Mod/Part/Gui/TaskAttacher.h @@ -114,6 +114,9 @@ private: void updateAttachmentOffsetUI(); void processSelection(App::DocumentObject*& obj, std::string& sub); + void handleInitialSelection(); + void addToReference(const char* objName, const char* subName); + void addToReference(std::vector objNames, std::vector subNames); /** * @brief updateListOfModes Fills the mode list with modes that apply to From 9504b7e569edc3a95e129153ad72bad06381a0b5 Mon Sep 17 00:00:00 2001 From: PaddleStroke Date: Thu, 10 Oct 2024 15:42:38 +0200 Subject: [PATCH 08/15] PartDesign: Enable the use of the core datums as references. --- src/Mod/PartDesign/Gui/ReferenceSelection.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Mod/PartDesign/Gui/ReferenceSelection.cpp b/src/Mod/PartDesign/Gui/ReferenceSelection.cpp index 70f6817951..78cd679cdc 100644 --- a/src/Mod/PartDesign/Gui/ReferenceSelection.cpp +++ b/src/Mod/PartDesign/Gui/ReferenceSelection.cpp @@ -148,12 +148,12 @@ bool ReferenceSelection::allowOrigin(PartDesign::Body *body, App::OriginGroupExt if (fits) { // check that it actually belongs to the chosen body or part try { // here are some throwers if (body) { - if (body->getOrigin ()->hasObject (pObj) ) { + if (body->hasObject(pObj, true) ) { return true; } } else if (originGroup ) { - if (originGroup->getOrigin()->hasObject(pObj)) { + if (originGroup->hasObject(pObj, true)) { return true; } } From c5fbbb38306aa405bdaf3bf3d98b56838e475c1e Mon Sep 17 00:00:00 2001 From: PaddleStroke Date: Fri, 6 Dec 2024 18:29:50 +0100 Subject: [PATCH 09/15] SubShapeBinder: Add support for point. --- src/Mod/Part/App/PartFeature.cpp | 1 + src/Mod/PartDesign/App/ShapeBinder.cpp | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/src/Mod/Part/App/PartFeature.cpp b/src/Mod/Part/App/PartFeature.cpp index ee3ab6948d..65ae266de8 100644 --- a/src/Mod/Part/App/PartFeature.cpp +++ b/src/Mod/Part/App/PartFeature.cpp @@ -1076,6 +1076,7 @@ static TopoShape _getTopoShape(const App::DocumentObject* obj, shape = TopoShape(tag, hasher, _shape); } } + if (!shape.isNull()) { shape.transformShape(mat * linkMat, false, true); return shape; diff --git a/src/Mod/PartDesign/App/ShapeBinder.cpp b/src/Mod/PartDesign/App/ShapeBinder.cpp index 31040e5001..8402df16c9 100644 --- a/src/Mod/PartDesign/App/ShapeBinder.cpp +++ b/src/Mod/PartDesign/App/ShapeBinder.cpp @@ -28,6 +28,7 @@ # include # include # include +# include #endif #include @@ -194,6 +195,10 @@ void ShapeBinder::getFilteredReferences(const App::PropertyLinkSubList* prop, obj = plane; break; } + if (auto point = dynamic_cast(it)) { + obj = point; + break; + } } } } @@ -244,6 +249,13 @@ Part::TopoShape ShapeBinder::buildShapeFromReferences(App::GeoFeature* obj, std: shape.setPlacement(obj->Placement.getValue()); return shape; } + else if (obj->isDerivedFrom()) { + gp_Pnt point; + BRepBuilderAPI_MakeVertex mkPoint(point); + Part::TopoShape shape(mkPoint.Shape()); + shape.setPlacement(obj->Placement.getValue()); + return shape; + } return TopoDS_Shape(); } From 4066100a79c3e6e82682f004d69133c794cb87aa Mon Sep 17 00:00:00 2001 From: PaddleStroke Date: Fri, 11 Oct 2024 12:41:50 +0200 Subject: [PATCH 10/15] Attacher.cpp: Remove special handling of App::Datums since it is already done in Part::Feature::getTopoShape --- src/Mod/Part/App/Attacher.cpp | 158 +++++++++++++--------------------- src/Mod/Part/App/Attacher.h | 2 +- 2 files changed, 60 insertions(+), 100 deletions(-) diff --git a/src/Mod/Part/App/Attacher.cpp b/src/Mod/Part/App/Attacher.cpp index 2dd69ed34f..f3800133e6 100644 --- a/src/Mod/Part/App/Attacher.cpp +++ b/src/Mod/Part/App/Attacher.cpp @@ -301,7 +301,8 @@ Base::Placement AttachEngine::placementFactory(const gp_Dir &ZAxis, gp_Ax3 ax3;//OCC representation of the final placement if (!makeYVertical) { ax3 = gp_Ax3(Origin, ZAxis, XAxis); - } else if (!makeLegacyFlatFaceOrientation) { + } + else if (!makeLegacyFlatFaceOrientation) { //align Y along Z, if possible gp_Vec YAxis(0.0,0.0,1.0); XAxis = YAxis.Crossed(gp_Vec(ZAxis)); @@ -310,7 +311,8 @@ Base::Placement AttachEngine::placementFactory(const gp_Dir &ZAxis, XAxis = (gp_Vec(1,0,0)*ZAxis.Z()).Normalized(); } ax3 = gp_Ax3(Origin, ZAxis, XAxis); - } else if (makeLegacyFlatFaceOrientation) { + } + else if (makeLegacyFlatFaceOrientation) { //find out, to which axis of support Normal is closest to. //The result will be written into pos variable (0..2 = X..Z) if (!placeOfRef) @@ -386,13 +388,11 @@ void AttachEngine::suggestMapModes(SuggestResult &result) const result.message = SuggestResult::srLinkBroken; result.bestFitMode = mmDeactivated; - - std::vector parts; std::vector shapes; std::vector shapeStorage; std::vector typeStr; try{ - readLinks(getRefObjects(),subnames, parts, shapes, shapeStorage, typeStr); + readLinks(getRefObjects(),subnames, shapes, shapeStorage, typeStr); } catch (Base::Exception &err) { result.references_Types = typeStr; result.message = SuggestResult::srLinkBroken; @@ -578,11 +578,10 @@ 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 parts; std::vector shapes; std::vector copiedShapeStorage; std::vector types; - readLinks(tmpLink.getValues(),tmpLink.getSubValues(), parts, shapes, copiedShapeStorage, types); + readLinks(tmpLink.getValues(), tmpLink.getSubValues(), shapes, copiedShapeStorage, types); assert(types.size() == 1); return types[0]; @@ -815,102 +814,69 @@ GProp_GProps AttachEngine::getInertialPropsOfShape(const std::vector &objs, +void AttachEngine::readLinks(const std::vector& objs, const std::vector &subs, - std::vector &geofs, std::vector &shapes, std::vector &storage, std::vector &types) { - geofs.resize(objs.size()); storage.reserve(objs.size()); shapes.resize(objs.size()); types.resize(objs.size()); for (std::size_t i = 0; i < objs.size(); i++) { - std::string fullSub = subs[i]; - const char* element = Data::findElementName(fullSub.c_str()); - App::DocumentObject* obj = objs[i]->getSubObject(subs[i].c_str()); - - auto* geof = dynamic_cast(obj); + auto* geof = dynamic_cast(objs[i]); if (!geof) { - FC_THROWM(AttachEngineException, - "AttachEngine3D: attached to a non App::GeoFeature '" - << obj->getNameInDocument() << "'"); + // 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() << "'"); + } } - geofs[i] = geof; TopoDS_Shape myShape; - Base::Placement plc = App::GeoFeature::getGlobalPlacement(obj, objs[i], fullSub); - if (geof->isDerivedFrom(App::Plane::getClassTypeId())) { - // obtain Z axis and origin of placement - Base::Vector3d norm; - plc.getRotation().multVec(Base::Vector3d(0.0, 0.0, 1.0), norm); - Base::Vector3d org; - plc.multVec(Base::Vector3d(), org); - // make shape - an local-XY plane infinite face - gp_Pln plane = gp_Pln(gp_Pnt(org.x, org.y, org.z), gp_Dir(norm.x, norm.y, norm.z)); - myShape = BRepBuilderAPI_MakeFace(plane).Shape(); - myShape.Infinite(true); - } - else if (geof->isDerivedFrom(App::Line::getClassTypeId())) { - // obtain X axis and origin of placement - Base::Vector3d dir; - plc.getRotation().multVec(Base::Vector3d(0.0, 0.0, 1.0), dir); - Base::Vector3d org; - plc.multVec(Base::Vector3d(), org); - // make shape - an infinite line along local X axis - gp_Lin line = gp_Lin(gp_Pnt(org.x, org.y, org.z), gp_Dir(dir.x, dir.y, dir.z)); - myShape = BRepBuilderAPI_MakeEdge(line).Shape(); - myShape.Infinite(true); - } - else if (geof->isDerivedFrom(App::Point::getClassTypeId())) { - Base::Vector3d org; - plc.multVec(Base::Vector3d(), org); - gp_Pnt pnt = gp_Pnt(org.x, org.y, org.z); - myShape = BRepBuilderAPI_MakeVertex(pnt).Shape(); - } - else { - try { - Part::TopoShape shape = Part::Feature::getTopoShape(geof, element, 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); + 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); + for (;;) { + if (shape.isNull()) { + FC_THROWM(AttachEngineException, + "AttachEngine3D: subshape not found " + << objs[i]->getNameInDocument() << '.' << subs[i]); } - shape.setPlacement(plc); - myShape = shape.getShape(); - } - 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()); - } - if (myShape.IsNull()) { - FC_THROWM(AttachEngineException, - "AttachEngine3D: null subshape " << 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); } + + myShape = shape.getShape(); } + 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()); + } + if (myShape.IsNull()) { + FC_THROWM(AttachEngineException, + "AttachEngine3D: null subshape " << objs[i]->getNameInDocument() << '.' + << subs[i]); + } + storage.emplace_back(myShape); shapes[i] = &(storage.back()); @@ -921,7 +887,6 @@ void AttachEngine::readLinks(const std::vector &objs, types[i] = eRefType(types[i] | rtFlagHasPlacement); } } - } void AttachEngine::throwWrongMode(eMapMode mmode) @@ -1175,22 +1140,18 @@ AttachEngine3D::_calculateAttachedPlacement(const std::vector parts; std::vector shapes; std::vector copiedShapeStorage; std::vector types; - readLinks(objs, subs, parts, shapes, copiedShapeStorage, types); + readLinks(objs, subs, shapes, copiedShapeStorage, types); - if (parts.empty()) { + if (shapes.empty()) { throw ExceptionCancel(); } // common stuff for all map modes - Base::Placement Place = App::GeoFeature::getGlobalPlacement(parts[0], objs[0], subs[0]); - Base::Console().Warning("parts[0] = %s\n", parts[0]->getNameInDocument()); - Base::Console().Warning("objs[0] = %s\n", objs[0]->getNameInDocument()); - Base::Console().Warning("subs[0] = %s\n", subs[0]); - Base::Console().Warning("Place = (%f, %f, %f)\n", Place.getPosition().x, Place.getPosition().y, Place.getPosition().z); + App::DocumentObject* subObj = objs[0]->getSubObject(subs[0].c_str()); + Base::Placement Place = App::GeoFeature::getGlobalPlacement(subObj, objs[0], subs[0]); Base::Vector3d vec = Place.getPosition(); gp_Pnt refOrg = gp_Pnt(vec.x, vec.y, vec.z); // origin of linked object @@ -2122,19 +2083,19 @@ AttachEngineLine::_calculateAttachedPlacement(const std::vector parts; std::vector shapes; std::vector copiedShapeStorage; std::vector types; - readLinks(objs, subs, parts, shapes, copiedShapeStorage, types); + readLinks(objs, subs, shapes, copiedShapeStorage, types); - if (parts.empty()) { + if (shapes.empty()) { throw ExceptionCancel(); } // common stuff for all map modes - Base::Placement Place = App::GeoFeature::getGlobalPlacement(parts[0], objs[0], subs[0]); + App::DocumentObject* subObj = objs[0]->getSubObject(subs[0].c_str()); + Base::Placement Place = App::GeoFeature::getGlobalPlacement(subObj, objs[0], subs[0]); Base::Vector3d vec = Place.getPosition(); gp_Pnt refOrg = gp_Pnt(vec.x, vec.y, vec.z); // origin of linked object @@ -2489,13 +2450,12 @@ AttachEnginePoint::_calculateAttachedPlacement(const std::vector parts; std::vector shapes; std::vector copiedShapeStorage; std::vector types; - readLinks(objs, subs, parts, shapes, copiedShapeStorage, types); + readLinks(objs, subs, shapes, copiedShapeStorage, types); - if (parts.empty()) { + if (shapes.empty()) { throw ExceptionCancel(); } diff --git a/src/Mod/Part/App/Attacher.h b/src/Mod/Part/App/Attacher.h index 4a16f318b0..dd0c8ba569 100644 --- a/src/Mod/Part/App/Attacher.h +++ b/src/Mod/Part/App/Attacher.h @@ -429,7 +429,7 @@ protected: return ret; } static void readLinks(const std::vector &objs, - const std::vector &subs, std::vector &geofs, + const std::vector &subs, std::vector& shapes, std::vector &storage, std::vector &types); From 5d020082275c2923ba24be75d50387bb0833fd02 Mon Sep 17 00:00:00 2001 From: PaddleStroke Date: Fri, 18 Oct 2024 11:42:00 +0200 Subject: [PATCH 11/15] Remove PartDesign Datums commands from the UI. --- src/Mod/PartDesign/Gui/Workbench.cpp | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/src/Mod/PartDesign/Gui/Workbench.cpp b/src/Mod/PartDesign/Gui/Workbench.cpp index 5f49add309..bfacf1758d 100644 --- a/src/Mod/PartDesign/Gui/Workbench.cpp +++ b/src/Mod/PartDesign/Gui/Workbench.cpp @@ -380,14 +380,6 @@ Gui::MenuItem* Workbench::setupMenuBar() const root->insertItem(item, part); part->setCommand("&Part Design"); - // datums - Gui::MenuItem* datums = new Gui::MenuItem; - datums->setCommand("Create a datum"); - - *datums << "PartDesign_Point" - << "PartDesign_Line" - << "PartDesign_Plane"; - // additives Gui::MenuItem* additives = new Gui::MenuItem; additives->setCommand("Create an additive feature"); @@ -429,8 +421,6 @@ Gui::MenuItem* Workbench::setupMenuBar() const *part << "PartDesign_Body" << "Separator" - << datums - << "PartDesign_CoordinateSystem" << "PartDesign_ShapeBinder" << "PartDesign_SubShapeBinder" << "PartDesign_Clone" @@ -490,8 +480,7 @@ Gui::ToolBarItem* Workbench::setupToolBars() const << "Sketcher_ValidateSketch" << "Part_CheckGeometry" << "PartDesign_SubShapeBinder" - << "PartDesign_Clone" - << "PartDesign_CompDatums"; + << "PartDesign_Clone"; part = new Gui::ToolBarItem(root); part->setCommand("Part Design Modeling"); From 024fee7f9774c0a327371875f7055e1c00afc9d5 Mon Sep 17 00:00:00 2001 From: PaddleStroke Date: Tue, 15 Oct 2024 18:37:34 +0200 Subject: [PATCH 12/15] Assembly: Enable the use of App::Datums --- src/Mod/Assembly/App/AssemblyObject.cpp | 117 +++++++----------- src/Mod/Assembly/App/AssemblyObject.h | 1 - src/Mod/Assembly/App/AssemblyUtils.cpp | 5 +- src/Mod/Assembly/CommandCreateJoint.py | 6 +- src/Mod/Assembly/Gui/ViewProviderAssembly.cpp | 6 - src/Mod/Assembly/JointObject.py | 49 ++++---- src/Mod/Assembly/UtilsAssembly.py | 55 ++++---- 7 files changed, 95 insertions(+), 144 deletions(-) diff --git a/src/Mod/Assembly/App/AssemblyObject.cpp b/src/Mod/Assembly/App/AssemblyObject.cpp index ee82a21e4e..b98b56e42c 100644 --- a/src/Mod/Assembly/App/AssemblyObject.cpp +++ b/src/Mod/Assembly/App/AssemblyObject.cpp @@ -33,6 +33,7 @@ #include #include +#include #include #include #include @@ -45,6 +46,7 @@ #include #include +#include #include #include @@ -92,6 +94,9 @@ FC_LOG_LEVEL_INIT("Assembly", true, true, true) using namespace Assembly; using namespace MbD; + +namespace PartApp = Part; + /* static void printPlacement(Base::Placement plc, const char* name) { @@ -370,7 +375,8 @@ Base::Placement AssemblyObject::getMbdPlacement(std::shared_ptr mbdPar bool AssemblyObject::validateNewPlacements() { // First we check if a grounded object has moved. It can happen that they flip. - for (auto* obj : getGroundedParts()) { + std::vector groundedParts = getGroundedParts(); + for (auto* obj : groundedParts) { auto* propPlacement = dynamic_cast(obj->getPropertyByName("Placement")); if (propPlacement) { @@ -386,7 +392,8 @@ bool AssemblyObject::validateNewPlacements() if (!oldPlc.isSame(newPlacement)) { Base::Console().Warning( - "Assembly : Ignoring bad solve, a grounded object moved.\n"); + "Assembly : Ignoring bad solve, a grounded object (%s) moved.\n", + obj->getFullLabel()); return false; } } @@ -783,34 +790,54 @@ std::vector AssemblyObject::getGroundedParts() if (propObj) { App::DocumentObject* objToGround = propObj->getValue(); - groundedObjs.push_back(objToGround); + if (objToGround) { + if (std::find(groundedObjs.begin(), groundedObjs.end(), objToGround) + == groundedObjs.end()) { + groundedObjs.push_back(objToGround); + } + } } } + + // We also need to add all the root-level datums objects that are not attached. + std::vector objs = Group.getValues(); + for (auto* obj : objs) { + if (obj->isDerivedFrom() + || obj->isDerivedFrom()) { + auto* pcAttach = obj->getExtensionByType(); + if (pcAttach) { + // If it's a Part datums, we check if it's attached. If yes then we ignore it. + std::string mode = pcAttach->MapMode.getValueAsString(); + if (mode != "Deactivated") { + continue; + } + } + if (std::find(groundedObjs.begin(), groundedObjs.end(), obj) == groundedObjs.end()) { + groundedObjs.push_back(obj); + } + } + } + + // Origin is not in Group so we add it separately + groundedObjs.push_back(Origin.getValue()); + return groundedObjs; } std::vector AssemblyObject::fixGroundedParts() { - std::vector groundedJoints = getGroundedJoints(); + std::vector groundedParts = getGroundedParts(); - std::vector groundedObjs; - for (auto obj : groundedJoints) { + for (auto obj : groundedParts) { if (!obj) { continue; } - auto* propObj = dynamic_cast(obj->getPropertyByName("ObjectToGround")); - - if (propObj) { - App::DocumentObject* objToGround = propObj->getValue(); - - Base::Placement plc = getPlacementFromProp(obj, "Placement"); - std::string str = obj->getFullName(); - fixGroundedPart(objToGround, plc, str); - groundedObjs.push_back(objToGround); - } + Base::Placement plc = getPlacementFromProp(obj, "Placement"); + std::string str = obj->getFullName(); + fixGroundedPart(obj, plc, str); } - return groundedObjs; + return groundedParts; } void AssemblyObject::fixGroundedPart(App::DocumentObject* obj, @@ -1984,29 +2011,6 @@ std::vector AssemblyObject::getSubAssemblies() return subAssemblies; } -void AssemblyObject::updateGroundedJointsPlacements() -{ - std::vector groundedJoints = getGroundedJoints(); - - for (auto gJoint : groundedJoints) { - if (!gJoint) { - continue; - } - - auto* propObj = - dynamic_cast(gJoint->getPropertyByName("ObjectToGround")); - auto* propPlc = - dynamic_cast(gJoint->getPropertyByName("Placement")); - - if (propObj && propPlc) { - App::DocumentObject* obj = propObj->getValue(); - auto* propObjPlc = - dynamic_cast(obj->getPropertyByName("Placement")); - propPlc->setValue(propObjPlc->getValue()); - } - } -} - void AssemblyObject::ensureIdentityPlacements() { std::vector group = Group.getValues(); @@ -2037,36 +2041,3 @@ void AssemblyObject::ensureIdentityPlacements() } } } - -/*void Part::handleChangedPropertyType(Base::XMLReader& reader, const char* TypeName, App::Property* -prop) -{ - App::Part::handleChangedPropertyType(reader, TypeName, prop); -}*/ - -/* Apparently not necessary as App::Part doesn't have this. -// Python Assembly feature --------------------------------------------------------- - -namespace App -{ - /// @cond DOXERR - PROPERTY_SOURCE_TEMPLATE(Assembly::AssemblyObjectPython, Assembly::AssemblyObject) - template<> - const char* Assembly::AssemblyObjectPython::getViewProviderName() const - { - return "AssemblyGui::ViewProviderAssembly"; - } - template<> - PyObject* Assembly::AssemblyObjectPython::getPyObject() - { - if (PythonObject.is(Py::_None())) { - // ref counter is set to 1 - PythonObject = Py::Object(new FeaturePythonPyT(this), true); - } - return Py::new_reference_to(PythonObject); - } - /// @endcond - - // explicit template instantiation - template class AssemblyExport FeaturePythonT; -}// namespace App*/ diff --git a/src/Mod/Assembly/App/AssemblyObject.h b/src/Mod/Assembly/App/AssemblyObject.h index a5f7df556c..f4c1c2589d 100644 --- a/src/Mod/Assembly/App/AssemblyObject.h +++ b/src/Mod/Assembly/App/AssemblyObject.h @@ -186,7 +186,6 @@ public: void setObjMasses(std::vector> objectMasses); std::vector getSubAssemblies(); - void updateGroundedJointsPlacements(); std::vector getMotionsFromSimulation(App::DocumentObject* sim); diff --git a/src/Mod/Assembly/App/AssemblyUtils.cpp b/src/Mod/Assembly/App/AssemblyUtils.cpp index d406ef5bfd..f48eb4dbd7 100644 --- a/src/Mod/Assembly/App/AssemblyUtils.cpp +++ b/src/Mod/Assembly/App/AssemblyUtils.cpp @@ -33,6 +33,7 @@ #endif #include +#include #include #include #include @@ -536,7 +537,9 @@ App::DocumentObject* getObjFromRef(const App::DocumentObject* obj, const std::st // getViewProviderName instead of isDerivedFrom to avoid dependency on sketcher const auto isDerivedFromVpSketch = strcmp(obj->getViewProviderName(), "SketcherGui::ViewProviderSketch") == 0; - return isDerivedFromVpSketch || obj->isDerivedFrom(); + return isDerivedFromVpSketch || obj->isDerivedFrom() + || obj->isDerivedFrom() + || obj->isDerivedFrom(); }; // Helper function to handle PartDesign::Body objects diff --git a/src/Mod/Assembly/CommandCreateJoint.py b/src/Mod/Assembly/CommandCreateJoint.py index 77e6b3b7f7..3f3dae5512 100644 --- a/src/Mod/Assembly/CommandCreateJoint.py +++ b/src/Mod/Assembly/CommandCreateJoint.py @@ -47,11 +47,7 @@ def noOtherTaskActive(): def isCreateJointActive(): - return ( - UtilsAssembly.isAssemblyGrounded() - and UtilsAssembly.assembly_has_at_least_n_parts(2) - and noOtherTaskActive() - ) + return UtilsAssembly.assembly_has_at_least_n_parts(1) and noOtherTaskActive() def activateJoint(index): diff --git a/src/Mod/Assembly/Gui/ViewProviderAssembly.cpp b/src/Mod/Assembly/Gui/ViewProviderAssembly.cpp index 1f3016927d..de098764b6 100644 --- a/src/Mod/Assembly/Gui/ViewProviderAssembly.cpp +++ b/src/Mod/Assembly/Gui/ViewProviderAssembly.cpp @@ -233,12 +233,6 @@ bool ViewProviderAssembly::setEdit(int mode) PARTKEY, this->getObject()->getNameInDocument()); - // When we set edit, we update the grounded joints placements to support : - // - If user transformed the grounded object - // - For nested assemblies where the grounded object moves around. - auto* assembly = getObject(); - assembly->updateGroundedJointsPlacements(); - setDragger(); attachSelection(); diff --git a/src/Mod/Assembly/JointObject.py b/src/Mod/Assembly/JointObject.py index 5c6f8d6559..fdac6ed824 100644 --- a/src/Mod/Assembly/JointObject.py +++ b/src/Mod/Assembly/JointObject.py @@ -956,18 +956,6 @@ class GroundedJoint: joint.ObjectToGround = obj_to_ground - joint.addProperty( - "App::PropertyPlacement", - "Placement", - "Ground", - QT_TRANSLATE_NOOP( - "App::Property", - "This is where the part is grounded.", - ), - ) - - joint.Placement = obj_to_ground.Placement - def dumps(self): return None @@ -1160,24 +1148,33 @@ class MakeJointSelGate: return False ref = [obj, [sub]] - selected_object = UtilsAssembly.getObject(ref) + sel_obj = UtilsAssembly.getObject(ref) - if not ( - selected_object.isDerivedFrom("Part::Feature") - or selected_object.isDerivedFrom("App::Part") + if UtilsAssembly.isLink(sel_obj): + linked = sel_obj.getLinkedObject() + if linked == sel_obj: + return True # We accept empty links + sel_obj = linked + + if sel_obj.isDerivedFrom("Part::Feature") or sel_obj.isDerivedFrom("App::Part"): + return True + + if sel_obj.isDerivedFrom("App::LocalCoordinateSystem") or sel_obj.isDerivedFrom( + "App::DatumElement" ): - if UtilsAssembly.isLink(selected_object): - linked = selected_object.getLinkedObject() - if linked == selected_object: - # We accept empty links - return True + datum = sel_obj + if datum.isDerivedFrom("App::DatumElement"): + parent = datum.getParent() + if parent.isDerivedFrom("App::LocalCoordinateSystem"): + datum = parent - if not (linked.isDerivedFrom("Part::Feature") or linked.isDerivedFrom("App::Part")): - return False - else: - return False + if self.assembly.hasObject(datum) and hasattr(datum, "MapMode"): + # accept only datum that are not attached + return datum.MapMode == "Deactivated" - return True + return True + + return False activeTask = None diff --git a/src/Mod/Assembly/UtilsAssembly.py b/src/Mod/Assembly/UtilsAssembly.py index 9474568aab..5995a21ad1 100644 --- a/src/Mod/Assembly/UtilsAssembly.py +++ b/src/Mod/Assembly/UtilsAssembly.py @@ -161,6 +161,21 @@ def getObject(ref): if obj.TypeId in {"App::Part", "Assembly::AssemblyObject"} or isLinkGroup(obj): continue + elif obj.isDerivedFrom("App::LocalCoordinateSystem"): + # 2 cases possible, either we have the LCS itself: "part.LCS." + # or we have a datum: "part.LCS.X_Axis" + if i + 1 < len(names): + obj2 = None + for obji in obj.OutList: + if obji.Name == names[i + 1]: + obj2 = obji + break + if obj2 and obj2.isDerivedFrom("App::DatumElement"): + return obj2 + + elif obj.isDerivedFrom("App::DatumElement"): + return obj + elif obj.TypeId == "PartDesign::Body": if i + 1 < len(names): obj2 = None @@ -168,7 +183,7 @@ def getObject(ref): if obji.Name == names[i + 1]: obj2 = obji break - if obj2 and isBodySubObject(obj2.TypeId): + if obj2 and isBodySubObject(obj2): return obj2 return obj @@ -185,7 +200,7 @@ def getObject(ref): if obji.Name == names[i + 1]: obj2 = obji break - if obj2 and isBodySubObject(obj2.TypeId): + if obj2 and isBodySubObject(obj2): return obj2 return obj elif linked_obj.isDerivedFrom("Part::Feature"): @@ -197,13 +212,12 @@ def getObject(ref): return None -def isBodySubObject(typeId): +def isBodySubObject(obj): return ( - typeId == "Sketcher::SketchObject" - or typeId == "PartDesign::Point" - or typeId == "PartDesign::Line" - or typeId == "PartDesign::Plane" - or typeId == "PartDesign::CoordinateSystem" + obj.isDerivedFrom("Sketcher::SketchObject") + or obj.isDerivedFrom("PartDesign::Datum") + or obj.isDerivedFrom("App::DatumElement") + or obj.isDerivedFrom("App::LocalCoordinateSystem") ) @@ -348,21 +362,6 @@ def getElementName(full_name): if parts[-1] in {"X", "Y", "Z", "Point", "Line", "Plane"}: return "" - # Case of origin objects - if parts[-1] == "": - if "X_Axis" in parts[-2]: - return "X_Axis" - if "Y_Axis" in parts[-2]: - return "Y_Axis" - if "Z_Axis" in parts[-2]: - return "Z_Axis" - if "XY_Plane" in parts[-2]: - return "XY_Plane" - if "XZ_Plane" in parts[-2]: - return "XZ_Plane" - if "YZ_Plane" in parts[-2]: - return "YZ_Plane" - return parts[-1] @@ -946,16 +945,8 @@ def findPlacement(ref, ignoreVertex=False): elt = getElementName(ref[1][0]) vtx = getElementName(ref[1][1]) - # case of origin objects. - if elt == "X_Axis" or elt == "YZ_Plane": - return App.Placement(App.Vector(), App.Rotation(App.Vector(0, 1, 0), -90)) - if elt == "Y_Axis" or elt == "XZ_Plane": - return App.Placement(App.Vector(), App.Rotation(App.Vector(1, 0, 0), 90)) - if elt == "Z_Axis" or elt == "XY_Plane": - return App.Placement() - if not elt or not vtx: - # case of whole parts such as PartDesign::Body or PartDesign::CordinateSystem/Point/Line/Plane. + # case of whole parts such as PartDesign::Body or App/PartDesign::CordinateSystem/Point/Line/Plane. return App.Placement() plc = App.Placement() From 1ce8f2c859ed741928222a1b4b0abadcf7da2bf5 Mon Sep 17 00:00:00 2001 From: PaddleStroke Date: Wed, 11 Dec 2024 09:57:04 +0100 Subject: [PATCH 13/15] AssemblyObject: Use std::unordered_set instead of vector. --- src/Mod/Assembly/App/AssemblyObject.cpp | 33 +++++++++++-------------- src/Mod/Assembly/App/AssemblyObject.h | 6 ++--- 2 files changed, 17 insertions(+), 22 deletions(-) diff --git a/src/Mod/Assembly/App/AssemblyObject.cpp b/src/Mod/Assembly/App/AssemblyObject.cpp index b98b56e42c..45905162c7 100644 --- a/src/Mod/Assembly/App/AssemblyObject.cpp +++ b/src/Mod/Assembly/App/AssemblyObject.cpp @@ -159,7 +159,7 @@ int AssemblyObject::solve(bool enableRedo, bool updateJCS) objectPartMap.clear(); motions.clear(); - std::vector groundedObjs = fixGroundedParts(); + auto groundedObjs = fixGroundedParts(); if (groundedObjs.empty()) { // If no part fixed we can't solve. return -6; @@ -202,7 +202,7 @@ int AssemblyObject::generateSimulation(App::DocumentObject* sim) motions = getMotionsFromSimulation(sim); - std::vector groundedObjs = fixGroundedParts(); + auto groundedObjs = fixGroundedParts(); if (groundedObjs.empty()) { // If no part fixed we can't solve. return -6; @@ -375,7 +375,7 @@ Base::Placement AssemblyObject::getMbdPlacement(std::shared_ptr mbdPar bool AssemblyObject::validateNewPlacements() { // First we check if a grounded object has moved. It can happen that they flip. - std::vector groundedParts = getGroundedParts(); + auto groundedParts = getGroundedParts(); for (auto* obj : groundedParts) { auto* propPlacement = dynamic_cast(obj->getPropertyByName("Placement")); @@ -775,11 +775,11 @@ std::vector AssemblyObject::getJointsOfPart(App::DocumentO return jointsOf; } -std::vector AssemblyObject::getGroundedParts() +std::unordered_set AssemblyObject::getGroundedParts() { std::vector groundedJoints = getGroundedJoints(); - std::vector groundedObjs; + std::unordered_set groundedSet; for (auto gJoint : groundedJoints) { if (!gJoint) { continue; @@ -791,10 +791,7 @@ std::vector AssemblyObject::getGroundedParts() if (propObj) { App::DocumentObject* objToGround = propObj->getValue(); if (objToGround) { - if (std::find(groundedObjs.begin(), groundedObjs.end(), objToGround) - == groundedObjs.end()) { - groundedObjs.push_back(objToGround); - } + groundedSet.insert(objToGround); } } } @@ -812,21 +809,19 @@ std::vector AssemblyObject::getGroundedParts() continue; } } - if (std::find(groundedObjs.begin(), groundedObjs.end(), obj) == groundedObjs.end()) { - groundedObjs.push_back(obj); - } + groundedSet.insert(obj); } } // Origin is not in Group so we add it separately - groundedObjs.push_back(Origin.getValue()); + groundedSet.insert(Origin.getValue()); - return groundedObjs; + return groundedSet; } -std::vector AssemblyObject::fixGroundedParts() +std::unordered_set AssemblyObject::fixGroundedParts() { - std::vector groundedParts = getGroundedParts(); + auto groundedParts = getGroundedParts(); for (auto obj : groundedParts) { if (!obj) { @@ -948,7 +943,7 @@ bool AssemblyObject::isObjInSetOfObjRefs(App::DocumentObject* obj, const std::ve } void AssemblyObject::removeUnconnectedJoints(std::vector& joints, - std::vector groundedObjs) + std::unordered_set groundedObjs) { std::vector connectedParts; @@ -1040,7 +1035,7 @@ bool AssemblyObject::isPartGrounded(App::DocumentObject* obj) return false; } - std::vector groundedObjs = getGroundedParts(); + auto groundedObjs = getGroundedParts(); for (auto* groundedObj : groundedObjs) { if (groundedObj->getFullName() == obj->getFullName()) { @@ -1057,7 +1052,7 @@ bool AssemblyObject::isPartConnected(App::DocumentObject* obj) return false; } - std::vector groundedObjs = getGroundedParts(); + auto groundedObjs = getGroundedParts(); std::vector joints = getJoints(false); std::vector connectedParts; diff --git a/src/Mod/Assembly/App/AssemblyObject.h b/src/Mod/Assembly/App/AssemblyObject.h index f4c1c2589d..b8ea59049c 100644 --- a/src/Mod/Assembly/App/AssemblyObject.h +++ b/src/Mod/Assembly/App/AssemblyObject.h @@ -157,8 +157,8 @@ public: std::vector getJointsOfPart(App::DocumentObject* part); App::DocumentObject* getJointOfPartConnectingToGround(App::DocumentObject* part, std::string& name); - std::vector getGroundedParts(); - std::vector fixGroundedParts(); + std::unordered_set getGroundedParts(); + std::unordered_set fixGroundedParts(); void fixGroundedPart(App::DocumentObject* obj, Base::Placement& plc, std::string& jointName); bool isJointConnectingPartToGround(App::DocumentObject* joint, const char* partPropName); @@ -166,7 +166,7 @@ public: bool isObjInSetOfObjRefs(App::DocumentObject* obj, const std::vector& pairs); void removeUnconnectedJoints(std::vector& joints, - std::vector groundedObjs); + std::unordered_set groundedObjs); void traverseAndMarkConnectedParts(App::DocumentObject* currentPart, std::vector& connectedParts, const std::vector& joints); From e517400ed9ea35d3a7e4523b22e4301b37ce3a4c Mon Sep 17 00:00:00 2001 From: PaddleStroke Date: Wed, 11 Dec 2024 11:22:47 +0100 Subject: [PATCH 14/15] LCS migration : replace warning by a QMessageBox. --- src/App/Datums.cpp | 22 +++--------- src/App/Datums.h | 2 ++ src/Gui/ViewProviderCoordinateSystem.cpp | 46 ++++++++++++++++++++++++ src/Gui/ViewProviderCoordinateSystem.h | 4 +++ 4 files changed, 57 insertions(+), 17 deletions(-) diff --git a/src/App/Datums.cpp b/src/App/Datums.cpp index 3157ea4cd3..7afcf4b754 100644 --- a/src/App/Datums.cpp +++ b/src/App/Datums.cpp @@ -292,13 +292,11 @@ void LocalCoordinateSystem::migrateOriginPoint() { auto features = OriginFeatures.getValues(); - auto featIt = std::find_if(features.begin(), features.end(), - [](App::DocumentObject* obj) { - return obj->isDerivedFrom(App::DatumElement::getClassTypeId()) && + auto isOrigin = [](App::DocumentObject* obj) { + return obj->isDerivedFrom() && strcmp(static_cast(obj)->Role.getValue(), PointRoles[0]) == 0; - }); - if (featIt == features.end()) { - // origin point not found let's add it + }; + if (std::none_of(features.begin(), features.end(), isOrigin)) { auto data = getData(PointRoles[0]); auto* origin = createDatum(data); features.push_back(origin); @@ -310,7 +308,7 @@ void LocalCoordinateSystem::migrateXAxisPlacement() { auto features = OriginFeatures.getValues(); - bool migrated = false; + migrated = false; const auto& setupData = getSetupData(); for (auto* obj : features) { @@ -326,16 +324,6 @@ void LocalCoordinateSystem::migrateXAxisPlacement() } } } - - static bool warnedUser = false; - if (!warnedUser && migrated) { - Base::Console().Warning("This file was created with an older version of FreeCAD." - "It had some origin's X axis with incorrect placement, which is being fixed now.\n" - "But if you save the file here and open this file back in an " - "older version of FreeCAD, you will find the origin objects axis looking incorrect." - "And if your file is using the origin axis as references it will likely be broken.\n"); - warnedUser = true; - } } // ---------------------------------------------------------------------------- diff --git a/src/App/Datums.h b/src/App/Datums.h index 286e7353f3..43c08a285e 100644 --- a/src/App/Datums.h +++ b/src/App/Datums.h @@ -205,6 +205,8 @@ public: // Axis links PropertyLinkList OriginFeatures; + bool migrated; + protected: /// Checks integrity of the LCS App::DocumentObjectExecReturn* execute() override; diff --git a/src/Gui/ViewProviderCoordinateSystem.cpp b/src/Gui/ViewProviderCoordinateSystem.cpp index 15c686a023..45d8707524 100644 --- a/src/Gui/ViewProviderCoordinateSystem.cpp +++ b/src/Gui/ViewProviderCoordinateSystem.cpp @@ -26,6 +26,8 @@ #ifndef _PreComp_ # include # include +# include +# include #endif #include @@ -83,6 +85,50 @@ void ViewProviderCoordinateSystem::attach(App::DocumentObject* pcObject) addDisplayMaskMode(pcGroupChildren, "Base"); } +void ViewProviderCoordinateSystem::finishRestoring() +{ + showMigrationDialog(); +} + +void ViewProviderCoordinateSystem::showMigrationDialog() +{ + auto lcs = dynamic_cast(getObject()); + if (!lcs || !lcs->migrated) { + return; + } + + static bool userWarned = false; + + if (userWarned || !App::GetApplication().GetParameterGroupByPath( + "User parameter:BaseApp/Preferences/View")->GetBool("ShowLCSMigrationWarning", true)) { + return; + } + + // Display the warning message + QMessageBox msgBox(QMessageBox::Warning, + QObject::tr("File Migration Warning"), + QObject::tr("This file was created with an older version of FreeCAD. " + "Origin axes had incorrect placements, which have now been corrected.\n\n" + "However, if you save this file in the current version and reopen it in an" + " older version of FreeCAD, the origin axes will be misaligned. Additionally, " + "if your file references these origin axes, your file will likely be broken."), + QMessageBox::Ok); + + QCheckBox* checkBox = new QCheckBox(QObject::tr("Don't show this warning again")); + msgBox.setCheckBox(checkBox); + + msgBox.exec(); + + // Update static flag if the user has seen the warning + userWarned = true; + + // Save preference if the user selects "Don't show again" + if (checkBox->isChecked()) { + App::GetApplication().GetParameterGroupByPath( + "User parameter:BaseApp/Preferences/View")->SetBool("ShowLCSMigrationWarning", false); + } +} + std::vector ViewProviderCoordinateSystem::getDisplayModes() const { return { "Base" }; diff --git a/src/Gui/ViewProviderCoordinateSystem.h b/src/Gui/ViewProviderCoordinateSystem.h index 02a392d4e1..2448bc6acf 100644 --- a/src/Gui/ViewProviderCoordinateSystem.h +++ b/src/Gui/ViewProviderCoordinateSystem.h @@ -83,7 +83,11 @@ protected: void updateData(const App::Property*) override; bool onDelete(const std::vector &) override; + void finishRestoring() override; + private: + void showMigrationDialog(); + SoGroup *pcGroupChildren; std::map tempVisMap; From 7620e0a9589e5d83b78d4fc854ebd7abb9a1f823 Mon Sep 17 00:00:00 2001 From: PaddleStroke Date: Wed, 11 Dec 2024 13:47:58 +0100 Subject: [PATCH 15/15] TaskAttacher: Refactoring --- src/Mod/Part/Gui/TaskAttacher.cpp | 111 ++++++++++++++---------------- src/Mod/Part/Gui/TaskAttacher.h | 10 ++- 2 files changed, 59 insertions(+), 62 deletions(-) diff --git a/src/Mod/Part/Gui/TaskAttacher.cpp b/src/Mod/Part/Gui/TaskAttacher.cpp index 3dac3f92d9..053403e756 100644 --- a/src/Mod/Part/Gui/TaskAttacher.cpp +++ b/src/Mod/Part/Gui/TaskAttacher.cpp @@ -359,7 +359,7 @@ QLineEdit* TaskAttacher::getLine(unsigned idx) } } -void TaskAttacher::processSelection(App::DocumentObject*& rootObj, std::string& sub) +void TaskAttacher::findCorrectObjAndSubInThisContext(App::DocumentObject*& rootObj, std::string& sub) { // The reference that we store must take into account the hierarchy of geoFeatures. For example: // - Part @@ -369,7 +369,7 @@ void TaskAttacher::processSelection(App::DocumentObject*& rootObj, std::string& // - Part // - - Cube // - - Sketch - // In this example if must store Cube:Face3 because Sketch is inside Part, sibling of Cube. + // In this example it must store Cube:Face3 because Sketch is inside Part, sibling of Cube. // So placement of Part is already taken into account. // - Part1 // - - Part2 @@ -415,6 +415,7 @@ void TaskAttacher::processSelection(App::DocumentObject*& rootObj, std::string& return; } + bool groupPassed = false; for (size_t i = 0; i < names.size(); ++i) { App::DocumentObject* obj = doc->getObject(names[i].c_str()); if (!obj) { @@ -425,6 +426,20 @@ void TaskAttacher::processSelection(App::DocumentObject*& rootObj, std::string& return; } + if (groupPassed) { + rootObj = obj; + + // Rebuild 'sub' starting from the next element after the current 'name' + sub = ""; + for (size_t j = i + 1; j < names.size(); ++j) { + sub += names[j]; + if (j != names.size() - 1) { + sub += "."; // Add a period between elements + } + } + return; + } + // In case the attaching object is in a link to a part. // For instance : // - Part1 @@ -434,23 +449,7 @@ void TaskAttacher::processSelection(App::DocumentObject*& rootObj, std::string& obj = obj->getLinkedObject(); if (obj == group) { - ++i; - obj = doc->getObject(names[i].c_str()); - if (!obj) { - return; - } - - rootObj = obj; - - // Rebuild 'sub' starting from the next element after the current 'name' - sub = ""; - for (size_t j = i + 1; j < names.size(); ++j) { - sub += names[j]; - if (j != names.size() - 1) { - sub += "."; // Add a period between elements - } - } - return; + groupPassed = true; } } @@ -475,20 +474,19 @@ void TaskAttacher::handleInitialSelection() if (!refs.empty()) { return; } - std::vector objNames; - std::vector subNames; + std::vector subAndObjNamePairs; auto sel = Gui::Selection().getSelectionEx("", App::DocumentObject::getClassTypeId(), Gui::ResolveMode::NoResolve); for (auto& selObj : sel) { std::vector subs = selObj.getSubNames(); - const char* objName = selObj.getFeatName(); + std::string objName = selObj.getFeatName(); for (auto& sub : subs) { - objNames.push_back(objName); - subNames.push_back(sub); + SubAndObjName objSubName = { objName, sub }; + subAndObjNamePairs.push_back(objSubName); } } - addToReference(objNames, subNames); + addToReference(subAndObjNamePairs); } void TaskAttacher::onSelectionChanged(const Gui::SelectionChanges& msg) @@ -498,13 +496,14 @@ void TaskAttacher::onSelectionChanged(const Gui::SelectionChanges& msg) } if (msg.Type == Gui::SelectionChanges::AddSelection) { - addToReference(msg.pObjectName, msg.pSubName); + SubAndObjName pair = { msg.pObjectName, msg.pSubName }; + addToReference(pair); } } -void TaskAttacher::addToReference(std::vector objNames, std::vector subNames) +void TaskAttacher::addToReference(const std::vector& pairs) { - if (iActiveRef < 0 || objNames.size() != subNames.size()) { + if (iActiveRef < 0) { return; } @@ -512,13 +511,13 @@ void TaskAttacher::addToReference(std::vector objNames, std::vector App::DocumentObject* obj = ViewProvider->getObject(); Part::AttachExtension* pcAttach = obj->getExtensionByType(); - for (size_t i = 0; i < objNames.size(); ++i) { + for (auto& pair : pairs) { std::vector refs = pcAttach->AttachmentSupport.getValues(); std::vector refnames = pcAttach->AttachmentSupport.getSubValues(); - App::DocumentObject* selObj = obj->getDocument()->getObject(objNames[i].c_str()); - std::string subname = subNames[i]; - processSelection(selObj, subname); + App::DocumentObject* selObj = obj->getDocument()->getObject(pair.objName.c_str()); + std::string subname = pair.subName; + findCorrectObjAndSubInThisContext(selObj, subname); if (!selObj) { return; } @@ -556,29 +555,6 @@ void TaskAttacher::addToReference(std::vector objNames, std::vector pcAttach->AttachmentSupport.setValues(refs, refnames); - if (i == objNames.size() - 1) { - // We check for the moed only for the last ref added. This is to avoid unnecessary warnings - // when we handle initial selection. - try { - updateListOfModes(); - eMapMode mmode = getActiveMapMode();//will be mmDeactivated, if selected or if no modes are available - if (mmode == mmDeactivated) { - //error = true; - this->completed = false; - } - else { - this->completed = true; - } - pcAttach->MapMode.setValue(mmode); - selectMapMode(mmode); - updatePreview(); - } - catch (Base::Exception& e) { - ui->message->setText(QCoreApplication::translate("Exception", e.what())); - ui->message->setStyleSheet(QString::fromLatin1("QLabel{color: red;}")); - } - } - QLineEdit* line = getLine(iActiveRef); if (line) { line->blockSignals(true); @@ -600,14 +576,31 @@ void TaskAttacher::addToReference(std::vector objNames, std::vector } } + try { + updateListOfModes(); + eMapMode mmode = getActiveMapMode(); //will be mmDeactivated, if selected or if no modes are available + if (mmode == mmDeactivated) { + //error = true; + this->completed = false; + } + else { + this->completed = true; + } + pcAttach->MapMode.setValue(mmode); + selectMapMode(mmode); + updatePreview(); + } + catch (Base::Exception& e) { + ui->message->setText(QCoreApplication::translate("Exception", e.what())); + ui->message->setStyleSheet(QString::fromLatin1("QLabel{color: red;}")); + } + updateReferencesUI(); } -void TaskAttacher::addToReference(const char* objName, const char* subName) +void TaskAttacher::addToReference(SubAndObjName pair) { - std::string objname = objName; - std::string subname = subName; - addToReference({ objname }, { subname }); + addToReference({ pair }); } void TaskAttacher::onAttachmentOffsetChanged(double /*val*/, int idx) diff --git a/src/Mod/Part/Gui/TaskAttacher.h b/src/Mod/Part/Gui/TaskAttacher.h index 2833bf3ab8..ce63d6cc21 100644 --- a/src/Mod/Part/Gui/TaskAttacher.h +++ b/src/Mod/Part/Gui/TaskAttacher.h @@ -113,10 +113,14 @@ private: void updateRefButton(int idx); void updateAttachmentOffsetUI(); - void processSelection(App::DocumentObject*& obj, std::string& sub); + void findCorrectObjAndSubInThisContext(App::DocumentObject*& obj, std::string& sub); void handleInitialSelection(); - void addToReference(const char* objName, const char* subName); - void addToReference(std::vector objNames, std::vector subNames); + struct SubAndObjName { + std::string objName; + std::string subName; + }; + void addToReference(SubAndObjName pair); + void addToReference(const std::vector& pairs); /** * @brief updateListOfModes Fills the mode list with modes that apply to