19 #include <vtkCellData.h>
20 #include <vtkCellPicker.h>
21 #include <vtkDataArray.h>
22 #include <vtkDataSet.h>
23 #include <vtkHardwareSelector.h>
24 #include <vtkIdTypeArray.h>
25 #include <vtkInformation.h>
26 #include <vtkMapper.h>
27 #include <vtkPointData.h>
28 #include <vtkPointPicker.h>
29 #include <vtkPoints.h>
31 #include <vtkProp3D.h>
32 #include <vtkPropPicker.h>
33 #include <vtkRenderWindow.h>
34 #include <vtkRenderWindowInteractor.h>
35 #include <vtkRenderer.h>
36 #include <vtkSelection.h>
37 #include <vtkSelectionNode.h>
40 #include <QApplication>
51 : QObject(parent), m_pickOnPoint(pickOnPoint), m_pickOption(pickOpt) {
54 "[cvPointPickingHelper] Parent widget is null, shortcut may "
64 keySequence,
nullptr, parent);
72 m_shortcut->setContextWidget(parent, Qt::ApplicationShortcut);
76 QString(
"[cvPointPickingHelper] Created shortcut: %1, context: %2, "
78 .arg(keySequence.toString())
79 .arg(m_shortcut->isEnabled() ?
"ApplicationShortcut"
81 .arg(m_shortcut->isEnabled() ?
"yes" :
"no"));
84 &cvPointPickingHelper::pickPoint);
87 parent->setFocusPolicy(Qt::StrongFocus);
99 vtkRenderWindowInteractor* interactor) {
100 m_interactor = interactor;
105 m_renderer = renderer;
110 m_contextWidget = widget;
117 QWidget* parentWidget = qobject_cast<QWidget*>(m_shortcut->parent());
123 m_shortcut->setContextWidget(parentWidget, Qt::ApplicationShortcut);
134 m_shortcut->setEnabled(enabled, setFocus);
140 return m_shortcut ? m_shortcut->isEnabled() :
false;
144 void cvPointPickingHelper::getCellNormal(vtkDataSet* dataset,
149 vtkDataArray* cellNormals = dataset->GetCellData()->GetNormals();
150 if (cellNormals && cellId < cellNormals->GetNumberOfTuples()) {
151 cellNormals->GetTuple(cellId,
normal);
156 int cellType = cell->GetCellType();
157 if (cellType == VTK_TRIANGLE || cellType == VTK_QUAD ||
158 cellType == VTK_POLYGON) {
160 double p0[3], p1[3], p2[3];
161 cell->GetPoints()->GetPoint(0, p0);
162 cell->GetPoints()->GetPoint(1, p1);
163 cell->GetPoints()->GetPoint(2, p2);
166 double v1[3] = {p1[0] - p0[0], p1[1] - p0[1], p1[2] - p0[2]};
167 double v2[3] = {p2[0] - p0[0], p2[1] - p0[1], p2[2] - p0[2]};
170 normal[0] = v1[1] * v2[2] - v1[2] * v2[1];
171 normal[1] = v1[2] * v2[0] - v1[0] * v2[2];
172 normal[2] = v1[0] * v2[1] - v1[1] * v2[0];
186 void cvPointPickingHelper::pickPoint() {
188 QString(
"[cvPointPickingHelper::pickPoint] Helper=%1, shortcut "
189 "enabled=%2, contextWidget=%3")
190 .arg((quintptr)
this, 0, 16)
191 .arg(m_shortcut && m_shortcut->isEnabled() ?
"yes" :
"no")
192 .arg((quintptr)m_contextWidget.data(), 0, 16));
198 if (!m_shortcut || !m_shortcut->isEnabled()) {
201 "[cvPointPickingHelper::pickPoint] Shortcut disabled, "
209 if (!m_contextWidget) {
214 if (!m_contextWidget->isVisible()) {
217 "[cvPointPickingHelper::pickPoint] Context widget not visible, "
236 if (!m_interactor || !m_renderer) {
237 CVLog::Warning(
"[cvPointPickingHelper] Interactor or renderer not set");
242 QWidget* renderWidget =
nullptr;
243 if (m_interactor->GetRenderWindow()) {
244 renderWidget =
static_cast<QWidget*
>(
245 m_interactor->GetRenderWindow()->GetGenericWindowId());
250 renderWidget = qobject_cast<QWidget*>(m_shortcut ? m_shortcut->parent()
255 CVLog::Warning(
"[cvPointPickingHelper] Cannot determine render widget");
262 QPointF pos = renderWidget->mapFromGlobal(QCursor::pos());
263 QSize sz = renderWidget->size();
264 bool outside = pos.x() < 0 || pos.x() > sz.width() || pos.y() < 0 ||
265 pos.y() > sz.height();
268 "[cvPointPickingHelper] Cursor is outside the render widget");
279 const int* eventpos = m_interactor->GetEventPosition();
280 if (eventpos && (eventpos[0] >= 0 && eventpos[1] >= 0)) {
284 displayX = eventpos[0];
285 displayY = eventpos[1];
288 int* renderSize = m_interactor->GetRenderWindow()->GetSize();
289 if (displayX < 0 || displayX >= renderSize[0] || displayY < 0 ||
290 displayY >= renderSize[1]) {
293 QString(
"[cvPointPickingHelper] GetEventPosition() out of "
295 "falling back to QCursor::pos()")
298 displayX =
static_cast<int>(pos.x());
299 displayY = sz.height() -
static_cast<int>(pos.y()) - 1;
304 displayX =
static_cast<int>(pos.x());
305 displayY = sz.height() -
static_cast<int>(pos.y()) - 1;
308 double position[3] = {0.0, 0.0, 0.0};
309 double normal[3] = {0.0, 0.0, 1.0};
310 bool pickSuccess =
false;
318 if (m_selectionCache.valid && m_selectionCache.displayX == displayX &&
319 m_selectionCache.displayY == displayY &&
320 m_selectionCache.pickOnPoint == m_pickOnPoint) {
322 std::copy(m_selectionCache.position, m_selectionCache.position + 3,
324 std::copy(m_selectionCache.normal, m_selectionCache.normal + 3,
normal);
328 m_selectionCache.valid =
false;
336 if (!m_hardwareSelector) {
341 m_hardwareSelector->SetRenderer(m_renderer);
342 m_hardwareSelector->SetArea(displayX, displayY, displayX, displayY);
343 m_hardwareSelector->SetFieldAssociation(
344 vtkDataObject::FIELD_ASSOCIATION_POINTS);
346 vtkSelection* selection = m_hardwareSelector->Select();
348 if (selection && selection->GetNumberOfNodes() > 0) {
349 vtkSelectionNode* node = selection->GetNode(0);
352 vtkProp::SafeDownCast(node->GetProperties()->Get(
353 vtkSelectionNode::PROP()));
354 vtkActor* actor = vtkActor::SafeDownCast(prop);
356 if (actor && actor->GetMapper()) {
357 vtkIdTypeArray* selectionIds =
358 vtkIdTypeArray::SafeDownCast(
359 node->GetSelectionList());
362 selectionIds->GetNumberOfTuples() > 0) {
363 vtkIdType pointId = selectionIds->GetValue(0);
364 vtkDataSet* dataset =
365 actor->GetMapper()->GetInput();
367 if (dataset && pointId >= 0 &&
368 pointId < dataset->GetNumberOfPoints()) {
369 dataset->GetPoint(pointId,
position);
374 dataset->GetPointData()
378 normals->GetNumberOfTuples()) {
400 if (m_propPicker->Pick(displayX, displayY, 0, m_renderer)) {
401 vtkProp3D* prop = m_propPicker->GetProp3D();
402 vtkActor* actor = vtkActor::SafeDownCast(prop);
404 if (actor && actor->GetMapper()) {
405 if (!m_pointPicker) {
408 m_pointPicker->SetTolerance(0.01);
412 m_pointPicker->AddPickList(actor);
413 m_pointPicker->PickFromListOn();
415 if (m_pointPicker->Pick(displayX, displayY, 0,
417 vtkIdType pointId = m_pointPicker->GetPointId();
418 vtkDataSet* dataset =
419 actor->GetMapper()->GetInput();
421 if (dataset && pointId >= 0 &&
422 pointId < dataset->GetNumberOfPoints()) {
423 dataset->GetPoint(pointId,
position);
428 dataset->GetPointData()
432 normals->GetNumberOfTuples()) {
440 m_pointPicker->InitializePickList();
441 m_pointPicker->PickFromListOff();
456 if (m_propPicker->Pick(displayX, displayY, 0, m_renderer)) {
457 vtkProp3D* prop = m_propPicker->GetProp3D();
458 vtkActor* actor = vtkActor::SafeDownCast(prop);
460 if (actor && actor->GetMapper()) {
465 m_cellPicker->SetTolerance(0.005);
469 m_cellPicker->AddPickList(actor);
470 m_cellPicker->PickFromListOn();
472 if (m_cellPicker->Pick(displayX, displayY, 0, m_renderer)) {
473 m_cellPicker->GetPickPosition(
position);
478 double* pickedNormal =
479 m_cellPicker->GetPickNormal();
481 normal[0] = pickedNormal[0];
482 normal[1] = pickedNormal[1];
483 normal[2] = pickedNormal[2];
489 m_cellPicker->InitializePickList();
490 m_cellPicker->PickFromListOff();
497 m_selectionCache.displayX = displayX;
498 m_selectionCache.displayY = displayY;
499 m_selectionCache.pickOnPoint = m_pickOnPoint;
502 m_selectionCache.valid =
true;
512 auto isValidVector = [](
const double x[3]) {
513 return !std::isnan(
x[0]) && !std::isnan(
x[1]) && !std::isnan(
x[2]) &&
514 !std::isinf(
x[0]) && !std::isinf(
x[1]) && !std::isinf(
x[2]);
517 switch (m_pickOption) {
523 "[cvPointPickingHelper] Invalid position picked");
528 if (isValidVector(
normal)) {
532 "[cvPointPickingHelper] Normal was not available");
539 QString(
"[cvPointPickingHelper] Picked point: "
540 "(%1, %2, %3), normal: (%4, %5, %6)")
551 "[cvPointPickingHelper] Position or normal was not "
560 m_selectionCache.valid =
false;
561 m_selectionCache.displayX = -1;
562 m_selectionCache.displayY = -1;
563 m_selectionCache.id = -1;
static bool PrintDebug(const char *format,...)
Same as Print, but works only in Debug mode.
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
static bool PrintVerbose(const char *format,...)
Prints out a verbose formatted message in console.
bool isEnabled() const
Check if shortcut is enabled.
~cvPointPickingHelper() override
@ Coordinates
Pick point coordinates only.
@ Normal
Pick normal only.
@ CoordinatesAndNormal
Pick both coordinates and normal.
void setEnabled(bool enabled, bool setFocus=false)
Enable or disable the shortcut.
void setContextWidget(QWidget *widget)
Set the context widget for the shortcut.
cvPointPickingHelper(const QKeySequence &keySequence, bool pickOnPoint, QWidget *parent=nullptr, PickOption pickOpt=Coordinates)
Constructor.
void setInteractor(vtkRenderWindowInteractor *interactor)
Set the VTK interactor for picking.
void setRenderer(vtkRenderer *renderer)
Set the VTK renderer for picking.
void clearSelectionCache()
Clear the selection cache (ParaView-style optimization) Call this when the scene changes to invalidat...
void pickNormal(double px, double py, double pz, double nx, double ny, double nz)
Emitted when a point and normal are picked.
void pick(double x, double y, double z)
Emitted when a point is picked.
ecvModalShortcut * addModalShortcut(const QKeySequence &keySequence, QAction *action, QWidget *parent)
static ecvKeySequences & instance()