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 @@
+
+
+
+
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,