diff --git a/src/App/GeoFeature.cpp b/src/App/GeoFeature.cpp index 1d9e6d763c..b09323d4d7 100644 --- a/src/App/GeoFeature.cpp +++ b/src/App/GeoFeature.cpp @@ -209,6 +209,13 @@ void GeoFeature::setMaterialAppearance(const App::Material& material) Q_UNUSED(material) } +bool GeoFeature::getCameraAlignmentDirection(Base::Vector3d& direction, const char* subname) const +{ + Q_UNUSED(subname) + Q_UNUSED(direction) + return false; +} + #ifdef FC_USE_TNP_FIX bool GeoFeature::hasMissingElement(const char* subname) { diff --git a/src/App/GeoFeature.h b/src/App/GeoFeature.h index 025d5f969e..d483a45243 100644 --- a/src/App/GeoFeature.h +++ b/src/App/GeoFeature.h @@ -141,6 +141,15 @@ public: * appearance from an App::Material object. */ virtual void setMaterialAppearance(const App::Material& material); + + /** + * @brief Virtual function to get the camera alignment direction + * + * Finds a direction to align the camera with. + * + * @return bool whether or not a direction is found. + */ + virtual bool getCameraAlignmentDirection(Base::Vector3d& direction, const char* subname = nullptr) const; #ifdef FC_USE_TNP_FIX /** Search sub element using internal cached geometry * diff --git a/src/Gui/CommandView.cpp b/src/Gui/CommandView.cpp index 8e39ce0a8a..df9bf58ead 100644 --- a/src/Gui/CommandView.cpp +++ b/src/Gui/CommandView.cpp @@ -4018,6 +4018,33 @@ bool StdRecallWorkingView::isActive() return view && view->getViewer()->hasHomePosition(); } +//=========================================================================== +// Std_AlignToSelection +//=========================================================================== +DEF_STD_CMD_A(StdCmdAlignToSelection) + +StdCmdAlignToSelection::StdCmdAlignToSelection() + : Command("Std_AlignToSelection") +{ + sGroup = "View"; + sMenuText = QT_TR_NOOP("Align to selection"); + sToolTipText = QT_TR_NOOP("Align the view with the selection"); + sWhatsThis = "Std_AlignToSelection"; + sPixmap = "align-to-selection"; + eType = Alter3DView; +} + +void StdCmdAlignToSelection::activated(int iMsg) +{ + Q_UNUSED(iMsg); + doCommand(Command::Gui,"Gui.SendMsgToActiveView(\"AlignToSelection\")"); +} + +bool StdCmdAlignToSelection::isActive() +{ + return getGuiApplication()->sendHasMsgToActiveView("AlignToSelection"); +} + //=========================================================================== // Instantiation //=========================================================================== @@ -4049,6 +4076,7 @@ void CreateViewStdCommands() rcCmdMgr.addCommand(new StdStoreWorkingView()); rcCmdMgr.addCommand(new StdRecallWorkingView()); rcCmdMgr.addCommand(new StdCmdViewGroup()); + rcCmdMgr.addCommand(new StdCmdAlignToSelection()); rcCmdMgr.addCommand(new StdCmdViewExample1()); rcCmdMgr.addCommand(new StdCmdViewExample2()); diff --git a/src/Gui/Icons/align-to-selection.svg b/src/Gui/Icons/align-to-selection.svg new file mode 100644 index 0000000000..0d4e894525 --- /dev/null +++ b/src/Gui/Icons/align-to-selection.svg @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + [maxwxyz] + + + https://www.freecad.org/wiki/index.php?title=Artwork + + + FreeCAD + + + FreeCAD/src/ + + + FreeCAD LGPL2+ + + + 2024 + + + + + + + + + + + + + + + + diff --git a/src/Gui/Icons/resource.qrc b/src/Gui/Icons/resource.qrc index 19ea65f9b3..af3c2e690d 100644 --- a/src/Gui/Icons/resource.qrc +++ b/src/Gui/Icons/resource.qrc @@ -234,6 +234,7 @@ colors.svg px.svg AddonManager.svg + align-to-selection.svg Group.svg Geofeaturegroup.svg Geoassembly.svg diff --git a/src/Gui/View3DInventor.cpp b/src/Gui/View3DInventor.cpp index 937128baf1..7770a71f2e 100644 --- a/src/Gui/View3DInventor.cpp +++ b/src/Gui/View3DInventor.cpp @@ -408,6 +408,10 @@ bool View3DInventor::onMsg(const char* pMsg, const char** ppReturn) getGuiDocument()->saveCopy(); return true; } + else if (strcmp("AlignToSelection", pMsg) == 0) { + _viewer->alignToSelection(); + return true; + } else if (strcmp("ZoomIn", pMsg) == 0) { View3DInventorViewer* viewer = getViewer(); viewer->navigationStyle()->zoomIn(); @@ -511,6 +515,9 @@ bool View3DInventor::onHasMsg(const char* pMsg) const else if(strncmp("Dump",pMsg,4) == 0) { return true; } + else if (strcmp("AlignToSelection", pMsg) == 0) { + return true; + } if (strcmp("ZoomIn", pMsg) == 0) { return true; } diff --git a/src/Gui/View3DInventorViewer.cpp b/src/Gui/View3DInventorViewer.cpp index ee5313b081..c792191e0c 100644 --- a/src/Gui/View3DInventorViewer.cpp +++ b/src/Gui/View3DInventorViewer.cpp @@ -3341,6 +3341,39 @@ void View3DInventorViewer::viewSelection() } } +void View3DInventorViewer::alignToSelection() +{ + if (!getCamera()) { + return; + } + + const auto selection = Selection().getSelection(); + + // Empty selection + if (selection.empty()) { + return; + } + + // Too much selections + if (selection.size() > 1) { + return; + } + + // Get the geo feature + App::GeoFeature* geoFeature = nullptr; + std::pair elementName; + App::GeoFeature::resolveElement(selection[0].pObject, selection[0].SubName, elementName, false, App::GeoFeature::ElementNameType::Normal, nullptr, nullptr, &geoFeature); + if (!geoFeature) { + return; + } + + Base::Vector3d direction; + if (geoFeature->getCameraAlignmentDirection(direction, selection[0].SubName)) { + const auto orientation = SbRotation(SbVec3f(0, 0, 1), Base::convertTo(direction)); + setCameraOrientation(orientation); + } +} + /** * @brief Decide if it should be possible to start any animation * diff --git a/src/Gui/View3DInventorViewer.h b/src/Gui/View3DInventorViewer.h index eca7f186b0..cdcdc5c853 100644 --- a/src/Gui/View3DInventorViewer.h +++ b/src/Gui/View3DInventorViewer.h @@ -414,6 +414,8 @@ public: */ void viewSelection(); + void alignToSelection(); + void setGradientBackground(Background); Background getGradientBackground() const; void setGradientBackgroundColor(const SbColor& fromColor, diff --git a/src/Gui/Workbench.cpp b/src/Gui/Workbench.cpp index f7049aa52f..cac301643d 100644 --- a/src/Gui/Workbench.cpp +++ b/src/Gui/Workbench.cpp @@ -804,7 +804,7 @@ ToolBarItem* StdWorkbench::setupToolBars() const // View auto view = new ToolBarItem( root ); view->setCommand("View"); - *view << "Std_ViewFitAll" << "Std_ViewFitSelection" << "Std_ViewGroup" + *view << "Std_ViewFitAll" << "Std_ViewFitSelection" << "Std_ViewGroup" << "Std_AlignToSelection" << "Separator" << "Std_DrawStyle" << "Std_TreeViewActions" << "Separator" << "Std_MeasureDistance" << "Std_Measure"; diff --git a/src/Mod/Part/App/PartFeature.cpp b/src/Mod/Part/App/PartFeature.cpp index d42be05ca7..50795b6b08 100644 --- a/src/Mod/Part/App/PartFeature.cpp +++ b/src/Mod/Part/App/PartFeature.cpp @@ -35,6 +35,7 @@ # include # include # include +# include # include # include # include @@ -68,6 +69,7 @@ #include #include +#include "Geometry.h" #include "PartFeature.h" #include "PartFeaturePy.h" #include "PartPyCXX.h" @@ -1655,6 +1657,44 @@ bool Feature::isElementMappingDisabled(App::PropertyContainer* container) // return false; } +bool Feature::getCameraAlignmentDirection(Base::Vector3d& direction, const char* subname) const +{ + const auto topoShape = getTopoShape(this, subname, true); + + if (topoShape.isNull()) { + return false; + } + + // Face normal + if (topoShape.isPlanar()) { + try { + const auto face = TopoDS::Face(topoShape.getShape()); + gp_Pnt point; + gp_Vec vector; + BRepGProp_Face(face).Normal(0, 0, point, vector); + direction = Base::Vector3d(vector.X(), vector.Y(), vector.Z()).Normalize(); + return true; + } + catch (Standard_TypeMismatch&) { + // Shape is not a face, do nothing + } + } + + // Edge direction + const size_t edgeCount = topoShape.countSubShapes(TopAbs_EDGE); + if (edgeCount == 1 && topoShape.isLinearEdge()) { + if (const std::unique_ptr geometry = Geometry::fromShape(topoShape.getSubShape(TopAbs_EDGE, 1), true)) { + const std::unique_ptr geomLine(static_cast(geometry.get())->toLine()); + if (geomLine) { + direction = geomLine->getDir().Normalize(); + return true; + } + } + } + + return GeoFeature::getCameraAlignmentDirection(direction, subname); +} + // --------------------------------------------------------- PROPERTY_SOURCE(Part::FilletBase, Part::Feature) diff --git a/src/Mod/Part/App/PartFeature.h b/src/Mod/Part/App/PartFeature.h index 6a1af4d48a..4387ccdd74 100644 --- a/src/Mod/Part/App/PartFeature.h +++ b/src/Mod/Part/App/PartFeature.h @@ -152,6 +152,8 @@ public: create(const TopoShape& shape, const char* name = nullptr, App::Document* document = nullptr); static bool isElementMappingDisabled(App::PropertyContainer *container); + + bool getCameraAlignmentDirection(Base::Vector3d& direction, const char* subname) const override; #ifdef FC_USE_TNP_FIX const std::vector& searchElementCache(const std::string &element,