Skip to content
This repository was archived by the owner on Mar 1, 2025. It is now read-only.

Commit a4c8523

Browse files
authored
Gui: add method to select objects with a 3D ray (FreeCAD#16789)
* Implementation of ray picking method for 3d picking * Ray picking logic moved to C++ (cherry picked from commit ed23214) * formatting, do not return unecessary dict keys, near plane clipping
1 parent d4b7d07 commit a4c8523

File tree

4 files changed

+165
-0
lines changed

4 files changed

+165
-0
lines changed

src/Gui/View3DInventor.cpp

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,11 @@
4747
# include <Inventor/nodes/SoOrthographicCamera.h>
4848
# include <Inventor/nodes/SoPerspectiveCamera.h>
4949
# include <Inventor/nodes/SoSeparator.h>
50+
# include <Inventor/SoPickedPoint.h>
5051
#endif
5152

5253
#include <App/Document.h>
54+
#include <App/GeoFeature.h>
5355
#include <Base/Builder3D.h>
5456
#include <Base/Console.h>
5557
#include <Base/Interpreter.h>
@@ -70,8 +72,10 @@
7072
#include "View3DInventorViewer.h"
7173
#include "View3DPy.h"
7274
#include "ViewProvider.h"
75+
#include "ViewProviderDocumentObject.h"
7376
#include "WaitCursor.h"
7477

78+
#include "Utilities.h"
7579

7680
using namespace Gui;
7781

@@ -773,6 +777,97 @@ void View3DInventor::setCurrentViewMode(ViewMode newmode)
773777
}
774778
}
775779

780+
RayPickInfo View3DInventor::getObjInfoRay(Base::Vector3d* startvec, Base::Vector3d* dirvec)
781+
{
782+
double vsx, vsy, vsz;
783+
double vdx, vdy, vdz;
784+
vsx = startvec->x;
785+
vsy = startvec->y;
786+
vsz = startvec->z;
787+
vdx = dirvec->x;
788+
vdy = dirvec->y;
789+
vdz = dirvec->z;
790+
// near plane clipping is required to avoid false intersections
791+
float near = 0.1;
792+
793+
RayPickInfo ret = {.isValid = false,
794+
.point = Base::Vector3d(),
795+
.document = "",
796+
.object = "",
797+
.parentObject = std::nullopt,
798+
.component = std::nullopt,
799+
.subName = std::nullopt};
800+
SoRayPickAction action(getViewer()->getSoRenderManager()->getViewportRegion());
801+
action.setRay(SbVec3f(vsx, vsy, vsz), SbVec3f(vdx, vdy, vdz), near);
802+
action.apply(getViewer()->getSoRenderManager()->getSceneGraph());
803+
SoPickedPoint* Point = action.getPickedPoint();
804+
805+
if (!Point) {
806+
return ret;
807+
}
808+
809+
ret.point = Base::convertTo<Base::Vector3d>(Point->getPoint());
810+
ViewProvider* vp = getViewer()->getViewProviderByPath(Point->getPath());
811+
if (vp && vp->isDerivedFrom(ViewProviderDocumentObject::getClassTypeId())) {
812+
if (!vp->isSelectable()) {
813+
return ret;
814+
}
815+
auto vpd = static_cast<ViewProviderDocumentObject*>(vp);
816+
if (vp->useNewSelectionModel()) {
817+
std::string subname;
818+
if (!vp->getElementPicked(Point, subname)) {
819+
return ret;
820+
}
821+
auto obj = vpd->getObject();
822+
if (!obj) {
823+
return ret;
824+
}
825+
if (!subname.empty()) {
826+
App::ElementNamePair elementName;
827+
auto sobj = App::GeoFeature::resolveElement(obj, subname.c_str(), elementName);
828+
if (!sobj) {
829+
return ret;
830+
}
831+
if (sobj != obj) {
832+
ret.parentObject = obj->getExportName();
833+
ret.subName = subname;
834+
obj = sobj;
835+
}
836+
subname = !elementName.oldName.empty() ? elementName.oldName : elementName.newName;
837+
}
838+
ret.document = obj->getDocument()->getName();
839+
ret.object = obj->getNameInDocument();
840+
ret.component = subname;
841+
ret.isValid = true;
842+
}
843+
else {
844+
ret.document = vpd->getObject()->getDocument()->getName();
845+
ret.object = vpd->getObject()->getNameInDocument();
846+
// search for a SoFCSelection node
847+
SoFCDocumentObjectAction objaction;
848+
objaction.apply(Point->getPath());
849+
if (objaction.isHandled()) {
850+
ret.component = objaction.componentName.getString();
851+
}
852+
}
853+
// ok, found the node of interest
854+
ret.isValid = true;
855+
}
856+
else {
857+
// custom nodes not in a VP: search for a SoFCSelection node
858+
SoFCDocumentObjectAction objaction;
859+
objaction.apply(Point->getPath());
860+
if (objaction.isHandled()) {
861+
ret.document = objaction.documentName.getString();
862+
ret.object = objaction.objectName.getString();
863+
ret.component = objaction.componentName.getString();
864+
// ok, found the node of interest
865+
ret.isValid = true;
866+
}
867+
}
868+
return ret;
869+
}
870+
776871
bool View3DInventor::eventFilter(QObject* watched, QEvent* e)
777872
{
778873
// As long as this widget is a top-level window (either in 'TopLevel' or 'FullScreen' mode) we

src/Gui/View3DInventor.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131

3232
#include "MDIView.h"
3333

34+
#include "Base/Vector3D.h"
3435

3536
class QPrinter;
3637
class QStackedWidget;
@@ -43,6 +44,16 @@ class View3DPy;
4344
class View3DSettings;
4445
class NaviCubeSettings;
4546

47+
struct RayPickInfo
48+
{
49+
bool isValid;
50+
Base::Vector3d point;
51+
std::string document;
52+
std::string object;
53+
std::optional<std::string> parentObject;
54+
std::optional<std::string> component;
55+
std::optional<std::string> subName;
56+
};
4657
class GuiExport GLOverlayWidget : public QWidget
4758
{
4859
Q_OBJECT
@@ -98,6 +109,8 @@ class GuiExport View3DInventor : public MDIView
98109
* GL widget to get all key events in \a TopLevel or \a Fullscreen mode.
99110
*/
100111
void setCurrentViewMode(ViewMode b) override;
112+
RayPickInfo getObjInfoRay(Base::Vector3d* startvec,
113+
Base::Vector3d* dirvec);
101114
bool setCamera(const char* pCamera);
102115
void toggleClippingPlane();
103116
bool hasClippingPlane() const;

src/Gui/View3DPy.cpp

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,14 @@ void View3DInventorPy::init_type()
158158
"\n"
159159
"Does the same as getObjectInfo() but returns a list of dictionaries or None.\n");
160160
add_noargs_method("getSize",&View3DInventorPy::getSize,"getSize()");
161+
add_varargs_method("getObjectInfoRay",&View3DInventorPy::getObjectInfoRay,
162+
"getObjectInfoRay(tuple(3D vector,3D vector) or tuple of 6 floats) -> dictionary or None\n"
163+
"\n"
164+
"Vectors represent start point and direction of intersection ray\n"
165+
"Return a dictionary with the name of document, object and component. The\n"
166+
"dictionary also contains the coordinates of the appropriate 3d point of\n"
167+
"the underlying geometry in the scenegraph.\n"
168+
"If no geometry was found 'None' is returned, instead.\n");
161169
add_varargs_method("getPoint",&View3DInventorPy::getPointOnFocalPlane,
162170
"Same as getPointOnFocalPlane");
163171
add_varargs_method("getPointOnFocalPlane",&View3DInventorPy::getPointOnFocalPlane,
@@ -1501,6 +1509,54 @@ Py::Object View3DInventorPy::getObjectsInfo(const Py::Tuple& args)
15011509
}
15021510
}
15031511

1512+
Py::Object View3DInventorPy::getObjectInfoRay(const Py::Tuple& args)
1513+
{
1514+
PyObject* vs;
1515+
PyObject* vd;
1516+
double vsx, vsy, vsz;
1517+
double vdx, vdy, vdz;
1518+
Py::Object ret = Py::None();
1519+
if (PyArg_ParseTuple(args.ptr(),
1520+
"O!O!",
1521+
&Base::VectorPy::Type,
1522+
&vs,
1523+
&Base::VectorPy::Type,
1524+
&vd)) {
1525+
Base::Vector3d* startvec = static_cast<Base::VectorPy*>(vs)->getVectorPtr();
1526+
Base::Vector3d* dirvec = static_cast<Base::VectorPy*>(vd)->getVectorPtr();
1527+
try {
1528+
RayPickInfo pinfo = getView3DIventorPtr()->getObjInfoRay(startvec, dirvec);
1529+
if (!pinfo.isValid) {
1530+
return ret;
1531+
}
1532+
Py::Dict dict;
1533+
dict.setItem("PickedPoint", Py::asObject(new Base::VectorPy(pinfo.point)));
1534+
dict.setItem("Document", Py::String(pinfo.document));
1535+
dict.setItem("Object", Py::String(pinfo.object));
1536+
if (pinfo.parentObject) {
1537+
dict.setItem("ParentObject", Py::String(pinfo.parentObject.value()));
1538+
}
1539+
if (pinfo.component) {
1540+
dict.setItem("Component", Py::String(pinfo.component.value()));
1541+
}
1542+
if (pinfo.subName) {
1543+
dict.setItem("SubName", Py::String(pinfo.subName.value()));
1544+
}
1545+
ret = dict;
1546+
}
1547+
catch (const Py::Exception&) {
1548+
throw;
1549+
}
1550+
}
1551+
else {
1552+
PyErr_Clear();
1553+
if (!PyArg_ParseTuple(args.ptr(), "dddddd", &vsx, &vsy, &vsz, &vdx, &vdy, &vdz)) {
1554+
throw Py::TypeError("Wrong arguments, two Vectors or six floats expected");
1555+
}
1556+
}
1557+
return ret;
1558+
}
1559+
15041560
Py::Object View3DInventorPy::getSize()
15051561
{
15061562
try {

src/Gui/View3DPy.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ class View3DInventorPy : public Py::PythonExtension<View3DInventorPy>
9595
Py::Object getObjectInfo(const Py::Tuple&);
9696
Py::Object getObjectsInfo(const Py::Tuple&);
9797
Py::Object getSize();
98+
Py::Object getObjectInfoRay(const Py::Tuple&);
9899
Py::Object getPointOnFocalPlane(const Py::Tuple&);
99100
Py::Object projectPointToLine(const Py::Tuple&);
100101
Py::Object getPointOnViewport(const Py::Tuple&);

0 commit comments

Comments
 (0)