Skip to content

Commit

Permalink
Gui: add method to select objects with a 3D ray (FreeCAD#16789)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
kwahoo2 authored Dec 13, 2024
1 parent d4b7d07 commit a4c8523
Show file tree
Hide file tree
Showing 4 changed files with 165 additions and 0 deletions.
95 changes: 95 additions & 0 deletions src/Gui/View3DInventor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,11 @@
# include <Inventor/nodes/SoOrthographicCamera.h>
# include <Inventor/nodes/SoPerspectiveCamera.h>
# include <Inventor/nodes/SoSeparator.h>
# include <Inventor/SoPickedPoint.h>
#endif

#include <App/Document.h>
#include <App/GeoFeature.h>
#include <Base/Builder3D.h>
#include <Base/Console.h>
#include <Base/Interpreter.h>
Expand All @@ -70,8 +72,10 @@
#include "View3DInventorViewer.h"
#include "View3DPy.h"
#include "ViewProvider.h"
#include "ViewProviderDocumentObject.h"
#include "WaitCursor.h"

#include "Utilities.h"

using namespace Gui;

Expand Down Expand Up @@ -773,6 +777,97 @@ void View3DInventor::setCurrentViewMode(ViewMode newmode)
}
}

RayPickInfo View3DInventor::getObjInfoRay(Base::Vector3d* startvec, Base::Vector3d* dirvec)
{
double vsx, vsy, vsz;
double vdx, vdy, vdz;
vsx = startvec->x;
vsy = startvec->y;
vsz = startvec->z;
vdx = dirvec->x;
vdy = dirvec->y;
vdz = dirvec->z;
// near plane clipping is required to avoid false intersections
float near = 0.1;

RayPickInfo ret = {.isValid = false,
.point = Base::Vector3d(),
.document = "",
.object = "",
.parentObject = std::nullopt,
.component = std::nullopt,
.subName = std::nullopt};
SoRayPickAction action(getViewer()->getSoRenderManager()->getViewportRegion());
action.setRay(SbVec3f(vsx, vsy, vsz), SbVec3f(vdx, vdy, vdz), near);
action.apply(getViewer()->getSoRenderManager()->getSceneGraph());
SoPickedPoint* Point = action.getPickedPoint();

if (!Point) {
return ret;
}

ret.point = Base::convertTo<Base::Vector3d>(Point->getPoint());
ViewProvider* vp = getViewer()->getViewProviderByPath(Point->getPath());
if (vp && vp->isDerivedFrom(ViewProviderDocumentObject::getClassTypeId())) {
if (!vp->isSelectable()) {
return ret;
}
auto vpd = static_cast<ViewProviderDocumentObject*>(vp);
if (vp->useNewSelectionModel()) {
std::string subname;
if (!vp->getElementPicked(Point, subname)) {
return ret;
}
auto obj = vpd->getObject();
if (!obj) {
return ret;
}
if (!subname.empty()) {
App::ElementNamePair elementName;
auto sobj = App::GeoFeature::resolveElement(obj, subname.c_str(), elementName);
if (!sobj) {
return ret;
}
if (sobj != obj) {
ret.parentObject = obj->getExportName();
ret.subName = subname;
obj = sobj;
}
subname = !elementName.oldName.empty() ? elementName.oldName : elementName.newName;
}
ret.document = obj->getDocument()->getName();
ret.object = obj->getNameInDocument();
ret.component = subname;
ret.isValid = true;
}
else {
ret.document = vpd->getObject()->getDocument()->getName();
ret.object = vpd->getObject()->getNameInDocument();
// search for a SoFCSelection node
SoFCDocumentObjectAction objaction;
objaction.apply(Point->getPath());
if (objaction.isHandled()) {
ret.component = objaction.componentName.getString();
}
}
// ok, found the node of interest
ret.isValid = true;
}
else {
// custom nodes not in a VP: search for a SoFCSelection node
SoFCDocumentObjectAction objaction;
objaction.apply(Point->getPath());
if (objaction.isHandled()) {
ret.document = objaction.documentName.getString();
ret.object = objaction.objectName.getString();
ret.component = objaction.componentName.getString();
// ok, found the node of interest
ret.isValid = true;
}
}
return ret;
}

bool View3DInventor::eventFilter(QObject* watched, QEvent* e)
{
// As long as this widget is a top-level window (either in 'TopLevel' or 'FullScreen' mode) we
Expand Down
13 changes: 13 additions & 0 deletions src/Gui/View3DInventor.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@

#include "MDIView.h"

#include "Base/Vector3D.h"

class QPrinter;
class QStackedWidget;
Expand All @@ -43,6 +44,16 @@ class View3DPy;
class View3DSettings;
class NaviCubeSettings;

struct RayPickInfo
{
bool isValid;
Base::Vector3d point;
std::string document;
std::string object;
std::optional<std::string> parentObject;
std::optional<std::string> component;
std::optional<std::string> subName;
};
class GuiExport GLOverlayWidget : public QWidget
{
Q_OBJECT
Expand Down Expand Up @@ -98,6 +109,8 @@ class GuiExport View3DInventor : public MDIView
* GL widget to get all key events in \a TopLevel or \a Fullscreen mode.
*/
void setCurrentViewMode(ViewMode b) override;
RayPickInfo getObjInfoRay(Base::Vector3d* startvec,
Base::Vector3d* dirvec);
bool setCamera(const char* pCamera);
void toggleClippingPlane();
bool hasClippingPlane() const;
Expand Down
56 changes: 56 additions & 0 deletions src/Gui/View3DPy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,14 @@ void View3DInventorPy::init_type()
"\n"
"Does the same as getObjectInfo() but returns a list of dictionaries or None.\n");
add_noargs_method("getSize",&View3DInventorPy::getSize,"getSize()");
add_varargs_method("getObjectInfoRay",&View3DInventorPy::getObjectInfoRay,
"getObjectInfoRay(tuple(3D vector,3D vector) or tuple of 6 floats) -> dictionary or None\n"
"\n"
"Vectors represent start point and direction of intersection ray\n"
"Return a dictionary with the name of document, object and component. The\n"
"dictionary also contains the coordinates of the appropriate 3d point of\n"
"the underlying geometry in the scenegraph.\n"
"If no geometry was found 'None' is returned, instead.\n");
add_varargs_method("getPoint",&View3DInventorPy::getPointOnFocalPlane,
"Same as getPointOnFocalPlane");
add_varargs_method("getPointOnFocalPlane",&View3DInventorPy::getPointOnFocalPlane,
Expand Down Expand Up @@ -1501,6 +1509,54 @@ Py::Object View3DInventorPy::getObjectsInfo(const Py::Tuple& args)
}
}

Py::Object View3DInventorPy::getObjectInfoRay(const Py::Tuple& args)
{
PyObject* vs;
PyObject* vd;
double vsx, vsy, vsz;
double vdx, vdy, vdz;
Py::Object ret = Py::None();
if (PyArg_ParseTuple(args.ptr(),
"O!O!",
&Base::VectorPy::Type,
&vs,
&Base::VectorPy::Type,
&vd)) {
Base::Vector3d* startvec = static_cast<Base::VectorPy*>(vs)->getVectorPtr();
Base::Vector3d* dirvec = static_cast<Base::VectorPy*>(vd)->getVectorPtr();
try {
RayPickInfo pinfo = getView3DIventorPtr()->getObjInfoRay(startvec, dirvec);
if (!pinfo.isValid) {
return ret;
}
Py::Dict dict;
dict.setItem("PickedPoint", Py::asObject(new Base::VectorPy(pinfo.point)));
dict.setItem("Document", Py::String(pinfo.document));
dict.setItem("Object", Py::String(pinfo.object));
if (pinfo.parentObject) {
dict.setItem("ParentObject", Py::String(pinfo.parentObject.value()));
}
if (pinfo.component) {
dict.setItem("Component", Py::String(pinfo.component.value()));
}
if (pinfo.subName) {
dict.setItem("SubName", Py::String(pinfo.subName.value()));
}
ret = dict;
}
catch (const Py::Exception&) {
throw;
}
}
else {
PyErr_Clear();
if (!PyArg_ParseTuple(args.ptr(), "dddddd", &vsx, &vsy, &vsz, &vdx, &vdy, &vdz)) {
throw Py::TypeError("Wrong arguments, two Vectors or six floats expected");
}
}
return ret;
}

Py::Object View3DInventorPy::getSize()
{
try {
Expand Down
1 change: 1 addition & 0 deletions src/Gui/View3DPy.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ class View3DInventorPy : public Py::PythonExtension<View3DInventorPy>
Py::Object getObjectInfo(const Py::Tuple&);
Py::Object getObjectsInfo(const Py::Tuple&);
Py::Object getSize();
Py::Object getObjectInfoRay(const Py::Tuple&);
Py::Object getPointOnFocalPlane(const Py::Tuple&);
Py::Object projectPointToLine(const Py::Tuple&);
Py::Object getPointOnViewport(const Py::Tuple&);
Expand Down

0 comments on commit a4c8523

Please sign in to comment.