ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
PCLVis.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
8 #include "PCLVis.h"
9 
10 #include <Utils/PCLConv.h>
11 #include <Utils/cc2sm.h>
12 #include <Utils/sm2cc.h>
13 
14 // ECV_DB
15 #include <ecvMesh.h>
16 #include <ecvPointCloud.h>
17 #include <ecvScalarField.h>
18 
19 #include "Tools/Common/PclTools.h"
20 #include "Tools/Common/ecvTools.h"
21 #include "VtkUtils/vtkutils.h"
24 
25 // SYSTEM
26 #include <cmath>
27 #include <functional>
28 
29 // CV_CORE_LIB
30 #include <CVTools.h>
31 #include <FileSystem.h>
32 #include <Helper.h>
33 #include <ecvGLMatrix.h>
34 
35 // CV_DB_LIB
36 #include <LineSet.h>
37 #include <ecv2DLabel.h>
38 #include <ecvBBox.h>
39 #include <ecvCameraSensor.h>
40 #include <ecvColorScale.h>
41 #include <ecvDisplayTools.h>
42 #include <ecvGBLSensor.h>
43 #include <ecvGenericMesh.h>
44 #include <ecvHObjectCaster.h>
45 #include <ecvMaterial.h>
46 #include <ecvMaterialSet.h>
47 #include <ecvOrientedBBox.h>
48 #include <ecvScalarField.h>
49 
50 // VTK Extension
53 
54 // VTK Light
57 #include <vtkLight.h>
58 #include <vtkLightCollection.h>
59 
60 // VTK for View Properties
61 #include <vtkCameraOrientationRepresentation.h>
62 #include <vtkCameraOrientationWidget.h>
63 #include <vtkCubeAxesActor.h>
64 #include <vtkLightKit.h>
65 
73 
74 // VTK
75 #include <vtkAreaPicker.h>
76 #include <vtkAxes.h>
77 #include <vtkAxesActor.h>
78 #include <vtkBMPReader.h>
79 #include <vtkCamera.h>
80 #include <vtkCaptionActor2D.h>
81 #include <vtkCaptionRepresentation.h>
82 #include <vtkFieldData.h>
83 #include <vtkJPEGReader.h>
84 #include <vtkLookupTable.h>
85 #include <vtkOpenGLRenderWindow.h>
86 #include <vtkOrientationMarkerWidget.h>
87 #include <vtkPNGReader.h>
88 #include <vtkPNMReader.h>
89 #include <vtkPointPicker.h>
90 #include <vtkPolyDataNormals.h>
91 #include <vtkPropAssembly.h>
92 #include <vtkPropPicker.h>
93 #include <vtkProperty.h>
94 #include <vtkQImageToImageSource.h>
95 #include <vtkRenderWindow.h>
96 #include <vtkRenderWindowInteractor.h>
97 #include <vtkRenderer.h>
98 #include <vtkRendererCollection.h>
99 #include <vtkStringArray.h>
100 #include <vtkTIFFReader.h>
101 #include <vtkTextActor.h>
102 #include <vtkTextProperty.h>
103 #include <vtkTexture.h>
104 #include <vtkTransform.h>
105 #include <vtkTubeFilter.h>
106 #include <vtkUnsignedCharArray.h>
107 #include <vtkWidgetEvent.h>
108 #include <vtkWidgetEventTranslator.h>
109 #include <vtkWindowToImageFilter.h>
110 
111 // PCL
112 #include <pcl/common/transforms.h>
113 #include <pcl/visualization/common/actor_map.h>
114 #include <pcl/visualization/point_cloud_color_handlers.h>
115 #include <pcl/visualization/point_cloud_geometry_handlers.h>
116 
117 // Support for VTK 7.1 upwards
118 #ifdef vtkGenericDataArray_h
119 #define SetTupleValue SetTypedTuple
120 #define InsertNextTupleValue InsertNextTypedTuple
121 #define GetTupleValue GetTypedTuple
122 #endif
123 
124 #define ORIENT_MODE 0
125 #define SELECT_MODE 1
126 
127 namespace PclUtils {
128 
129 // ============================================================================
130 // PCLVis Constructor and Destructor
131 // ============================================================================
133  interactor_style,
134  const std::string& viewerName /* = ""*/,
135  bool initIterator /* = false*/,
136  int argc /* = 0*/,
137  char** argv /* = nullptr*/)
138  : pcl::visualization::PCLVisualizer(
139  argc, argv, viewerName, interactor_style, initIterator),
140  m_widget_map(new WidgetActorMap),
141  m_prop_map(new PropActorMap),
142  m_currentMode(ORIENT_MODE),
143  m_pointPickingEnabled(true),
144  m_areaPickingEnabled(false),
145  m_actorPickingEnabled(false),
146  m_autoUpdateCameraPos(false),
147  texture_render_manager_(
148  std::make_unique<renders::TextureRenderManager>()),
149  m_lightIntensity(1.0) {
150  // disable warnings!
151  getRenderWindow()->GlobalWarningDisplayOff();
152 
153  // config center axes!
154  this->configCenterAxes();
155 
156  // config interactor style!
157  this->configInteractorStyle(interactor_style);
158 }
159 
163  interactor_style,
164  const std::string& viewerName,
165  bool initIterator /* = false*/,
166  int argc /* = 0*/, // unused
167  char** argv /* = nullptr*/) // unused
168  : pcl::visualization::PCLVisualizer(argc,
169  argv,
170  ren,
171  wind,
172  viewerName,
173  interactor_style,
174  initIterator),
175  m_widget_map(new WidgetActorMap),
176  m_prop_map(new PropActorMap),
177  m_currentMode(ORIENT_MODE),
178  m_pointPickingEnabled(true),
179  m_areaPickingEnabled(false),
180  m_actorPickingEnabled(false),
181  m_autoUpdateCameraPos(false),
182  texture_render_manager_(
183  std::make_unique<renders::TextureRenderManager>()),
184  m_lightIntensity(1.0) {
185  // disable warnings!
186  getRenderWindow()->GlobalWarningDisplayOff();
187 
188  // config center axes!
189  this->configCenterAxes();
190 
191  // config interactor style!
192  this->configInteractorStyle(interactor_style);
193 }
194 
196  if (isPointPickingEnabled()) {
197  setPointPickingEnabled(false);
198  }
199 
200  if (isAreaPickingEnabled()) {
201  setAreaPickingEnabled(false);
202  }
203 
204  if (isActorPickingEnabled()) {
205  setActorPickingEnabled(false);
206  }
207 
208  if (m_centerAxes) {
210  // this->m_centerAxes->Delete();
211  }
212 
213  // Clean up View Properties actors
214  vtkRenderer* renderer = getCurrentRenderer();
215  if (renderer) {
216  // Remove all Data Axes Grids (one per object)
217  for (auto& pair : m_dataAxesGridMap) {
218  if (pair.second) {
219  renderer->RemoveActor(pair.second);
220  }
221  }
222  m_dataAxesGridMap.clear();
223  }
224 
225  // Disable camera orientation widget
227  m_cameraOrientationWidget->SetEnabled(0);
228  }
229 
230  // vtkSmartPointer will automatically clean up the actors and widgets
231 }
232 
235  this->m_centerAxes->SetComputeNormals(0);
236  this->m_centerAxes->SetPickable(0);
237  this->m_centerAxes->SetScale(0.25, 0.25, 0.25);
239 }
240 
243  interactor_style) {
244  this->TwoDInteractorStyle =
246  this->m_interactorStyle = this->ThreeDInteractorStyle = interactor_style;
247 
248  // add some default manipulators. Applications can override them without
249  // much ado.
250  registerInteractorStyle(false);
251  // getInteractorStyle()->setKeyboardModifier(pcl::visualization::INTERACTOR_KB_MOD_SHIFT);
252 }
253 
255  registerMouse();
256  registerKeyboard();
257  registerPointPicking();
258  registerAreaPicking();
259 }
260 
262  const CCVector3d& axis,
263  double angle,
264  int viewport /* = 0*/) {
265  vtkRenderer* ren = getCurrentRenderer(viewport);
266  vtkRenderWindowInteractor* rwi = getRenderWindowInteractor();
267  if (ren == nullptr || rwi == nullptr) {
268  return;
269  }
270 
271  vtkTransform* transform = vtkTransform::New();
272  vtkCamera* camera = ren->GetActiveCamera();
273 
274  double scale = vtkMath::Norm(camera->GetPosition());
275  if (scale <= 0.0) {
276  scale = vtkMath::Norm(camera->GetFocalPoint());
277  if (scale <= 0.0) {
278  scale = 1.0;
279  }
280  }
281  double* temp = camera->GetFocalPoint();
282  camera->SetFocalPoint(temp[0] / scale, temp[1] / scale, temp[2] / scale);
283  temp = camera->GetPosition();
284  camera->SetPosition(temp[0] / scale, temp[1] / scale, temp[2] / scale);
285 
286  // translate to center
287  transform->Identity();
288 
289  vtkCameraManipulator* currentRotateManipulator =
290  this->get3DInteractorStyle()->FindManipulator(1, 0, 0);
291  if (!currentRotateManipulator) {
292  return;
293  }
294 
295  double center[3];
296  currentRotateManipulator->GetCenter(center);
297  double rotationFactor = currentRotateManipulator->GetRotationFactor();
298  transform->Translate(center[0] / scale, center[1] / scale,
299  center[2] / scale);
300 
301  camera->OrthogonalizeViewUp();
302 
303  transform->RotateWXYZ(angle, axis[0], axis[1], axis[2]);
304 
305  // translate back
306  transform->Translate(-center[0] / scale, -center[1] / scale,
307  -center[2] / scale);
308 
309  camera->ApplyTransform(transform);
310  camera->OrthogonalizeViewUp();
311 
312  // For rescale back.
313  temp = camera->GetFocalPoint();
314  camera->SetFocalPoint(temp[0] * scale, temp[1] * scale, temp[2] * scale);
315  temp = camera->GetPosition();
316  camera->SetPosition(temp[0] * scale, temp[1] * scale, temp[2] * scale);
317 
318  rwi->SetLastEventPosition(pos.x, pos.y);
319  rwi->Render();
320  transform->Delete();
321 }
322 
323 void PCLVis::getCenterOfRotation(double center[3]) {
324  if (this->ThreeDInteractorStyle) {
325  this->ThreeDInteractorStyle->GetCenterOfRotation(center);
326  }
327 }
328 
329 void PCLVis::ExpandBounds(double bounds[6], vtkMatrix4x4* matrix) {
330  if (!bounds) {
331  CVLog::Warning("ERROR: Invalid bounds");
332  return;
333  }
334 
335  if (!matrix) {
336  CVLog::Warning("ERROR: Invalid matrix");
337  return;
338  }
339 
340  // Expand the bounding box by model view transform matrix.
341  double pt[8][4] = {{bounds[0], bounds[2], bounds[5], 1.0},
342  {bounds[1], bounds[2], bounds[5], 1.0},
343  {bounds[1], bounds[2], bounds[4], 1.0},
344  {bounds[0], bounds[2], bounds[4], 1.0},
345  {bounds[0], bounds[3], bounds[5], 1.0},
346  {bounds[1], bounds[3], bounds[5], 1.0},
347  {bounds[1], bounds[3], bounds[4], 1.0},
348  {bounds[0], bounds[3], bounds[4], 1.0}};
349 
350  // \note: Assuming that matrix doesn not have projective component. Hence
351  // not dividing by the homogeneous coordinate after multiplication
352  for (int i = 0; i < 8; ++i) {
353  matrix->MultiplyPoint(pt[i], pt[i]);
354  }
355 
356  // min = mpx = pt[0]
357  double min[4], max[4];
358  for (int i = 0; i < 4; ++i) {
359  min[i] = pt[0][i];
360  max[i] = pt[0][i];
361  }
362 
363  for (int i = 1; i < 8; ++i) {
364  for (int j = 0; j < 3; ++j) {
365  if (min[j] > pt[i][j]) min[j] = pt[i][j];
366  if (max[j] < pt[i][j]) max[j] = pt[i][j];
367  }
368  }
369 
370  // Copy values back to bounds.
371  bounds[0] = min[0];
372  bounds[2] = min[1];
373  bounds[4] = min[2];
374 
375  bounds[1] = max[0];
376  bounds[3] = max[1];
377  bounds[5] = max[2];
378 }
379 
380 void PCLVis::resetCenterOfRotation(int viewport) {
381  this->synchronizeGeometryBounds(viewport);
382  vtkBoundingBox bbox(this->GeometryBounds);
383  double center[3];
384  bbox.GetCenter(center);
385 
386  setCenterOfRotation(center[0], center[1], center[2]);
387 }
388 
389 void PCLVis::setCenterOfRotation(double x, double y, double z) {
390  this->m_centerAxes->SetPosition(x, y, z);
391  if (this->TwoDInteractorStyle) {
392  this->TwoDInteractorStyle->SetCenterOfRotation(x, y, z);
393  }
394  if (this->ThreeDInteractorStyle) {
395  this->ThreeDInteractorStyle->SetCenterOfRotation(x, y, z);
396  }
397 }
398 
399 //----------------------------------------------------------------------------
400 void PCLVis::setRotationFactor(double factor) {
401  if (this->TwoDInteractorStyle) {
402  this->TwoDInteractorStyle->SetRotationFactor(factor);
403  }
404  if (this->ThreeDInteractorStyle) {
405  this->ThreeDInteractorStyle->SetRotationFactor(factor);
406  }
407 }
408 
410  if (this->ThreeDInteractorStyle) {
411  return this->ThreeDInteractorStyle->GetRotationFactor();
412  } else {
413  return 1.0;
414  }
415 }
416 
417 //----------------------------------------------------------------------------
418 void PCLVis::setCamera2DManipulators(const int manipulators[9]) {
419  this->setCameraManipulators(this->TwoDInteractorStyle, manipulators);
420 }
421 
422 //----------------------------------------------------------------------------
423 void PCLVis::setCamera3DManipulators(const int manipulators[9]) {
424  this->setCameraManipulators(this->ThreeDInteractorStyle, manipulators);
425 }
426 
427 //----------------------------------------------------------------------------
430  const int manipulators[9]) {
431  if (!style) {
432  return;
433  }
434 
435  style->RemoveAllManipulators();
436  enum { NONE = 0, SHIFT = 1, CTRL = 2 };
437 
438  enum {
439  PAN = 1,
440  ZOOM = 2,
441  ROLL = 3,
442  ROTATE = 4,
443  MULTI_ROTATE = 5,
444  ZOOM_TO_MOUSE = 6
445  };
446 
447  for (int manip = NONE; manip <= CTRL; manip++) {
448  for (int button = 0; button < 3; button++) {
449  int manipType = manipulators[3 * manip + button];
450  vtkSmartPointer<vtkCameraManipulator> cameraManipulator;
451  switch (manipType) {
452  case PAN:
453  cameraManipulator = vtkSmartPointer<vtkTrackballPan>::New();
454  break;
455  case ZOOM:
456  cameraManipulator =
458  break;
459  case ROLL:
460  cameraManipulator =
462  break;
463  case ROTATE:
464  cameraManipulator =
466  break;
467  case MULTI_ROTATE:
468  cameraManipulator =
470  break;
471  case ZOOM_TO_MOUSE:
472  cameraManipulator =
474  break;
475  }
476  if (cameraManipulator) {
477  cameraManipulator->SetButton(button +
478  1); // since button starts with 1.
479  cameraManipulator->SetControl(manip == CTRL ? 1 : 0);
480  cameraManipulator->SetShift(manip == SHIFT ? 1 : 0);
481  style->AddManipulator(cameraManipulator);
482  }
483  }
484  }
485 }
486 
487 //----------------------------------------------------------------------------
489  if (this->TwoDInteractorStyle) {
490  this->TwoDInteractorStyle->SetMouseWheelMotionFactor(factor);
491  }
492 }
493 
494 //----------------------------------------------------------------------------
496  if (this->ThreeDInteractorStyle) {
497  this->ThreeDInteractorStyle->SetMouseWheelMotionFactor(factor);
498  }
499 }
500 
501 //----------------------------------------------------------------------------
503  vtkBoundingBox bbox(this->GeometryBounds);
504 
505  // include the center of rotation in the axes size determination.
506  bbox.AddPoint(this->m_centerAxes->GetPosition());
507 
508  double widths[3];
509  bbox.GetLengths(widths);
510 
511  // lets make some thickness in all directions
512  double diameterOverTen =
513  bbox.GetMaxLength() > 0 ? bbox.GetMaxLength() / 10.0 : 1.0;
514  widths[0] = widths[0] < diameterOverTen ? diameterOverTen : widths[0];
515  widths[1] = widths[1] < diameterOverTen ? diameterOverTen : widths[1];
516  widths[2] = widths[2] < diameterOverTen ? diameterOverTen : widths[2];
517 
518  widths[0] *= 0.25;
519  widths[1] *= 0.25;
520  widths[2] *= 0.25;
521  this->m_centerAxes->SetScale(widths);
522 }
523 
524 //----------------------------------------------------------------------------
526  // get local bounds to consider 3D widgets correctly.
527  // if ComputeVisiblePropBounds is called when there's no real window on the
528  // local process, all vtkWidgetRepresentations return wacky Z bounds which
529  // screws up the renderer and we don't see any images. Hence we skip this on
530  // non-rendering nodes.
531  if (!this->getCurrentRenderer(viewport)) {
532  return;
533  }
534 
535  this->m_centerAxes->SetUseBounds(0);
536  this->GeometryBounds.Reset();
537 
538  getRendererCollection()->InitTraversal();
539  vtkRenderer* renderer = nullptr;
540  int i = 0;
541  while ((renderer = getRendererCollection()->GetNextItem())) {
542  // Modify all renderer's cameras
543  if (viewport == 0 || viewport == i) {
544  double prop_bounds[6];
545  this->getCurrentRenderer(viewport)->ComputeVisiblePropBounds(
546  prop_bounds);
547  this->GeometryBounds.AddBounds(prop_bounds);
548  }
549  ++i;
550  }
551 
552  this->m_centerAxes->SetUseBounds(1);
553 
554  // sync up bounds across all processes when doing distributed rendering.
555  if (!this->GeometryBounds.IsValid()) {
556  this->GeometryBounds.SetBounds(-1, 1, -1, 1, -1, 1);
557  }
558 
559  this->updateCenterAxes();
560 }
561 
562 //*****************************************************************
563 // Forwarded to center axes.
564 //----------------------------------------------------------------------------
566  if (this->m_centerAxes) {
567  this->m_centerAxes->SetVisibility(v);
568  }
569 }
570 
571 double PCLVis::getGLDepth(int x, int y) {
572  // Get camera focal point and position. Convert to display (screen)
573  // coordinates. We need a depth value for z-buffer.
574  //
575  double cameraFP[4];
576  getVtkCamera()->GetFocalPoint(cameraFP);
577  cameraFP[3] = 1.0;
578  getCurrentRenderer()->SetWorldPoint(cameraFP);
579  getCurrentRenderer()->WorldToDisplay();
580  double* displayCoord = getCurrentRenderer()->GetDisplayPoint();
581  double z_buffer = displayCoord[2];
582 
583  return z_buffer;
584 }
585 
586 double PCLVis::getCameraFocalDistance(int viewport) {
587  return getVtkCamera(viewport)->GetDistance();
588 }
589 
590 void PCLVis::setCameraFocalDistance(double focal_distance, int viewport) {
591  assert(focal_distance >= 0);
593  if (cam) {
594  cam->SetDistance(focal_distance);
595  cam->Modified();
596  }
597 }
598 
599 void PCLVis::zoomCamera(double zoomFactor, int viewport) {
600  assert(zoomFactor >= 0);
602  if (cam) {
603  cam->Zoom(zoomFactor);
604  cam->Modified();
605  }
606 }
607 
608 void PCLVis::getProjectionTransformMatrix(Eigen::Matrix4d& proj) {
609  vtkMatrix4x4* pMat =
610  getVtkCamera()->GetProjectionTransformMatrix(getCurrentRenderer());
611  proj = Eigen::Matrix4d(pMat->GetData());
612  proj.transposeInPlace();
613 }
614 
615 void PCLVis::getModelViewTransformMatrix(Eigen::Matrix4d& view) {
616  vtkMatrix4x4* vMat = getVtkCamera()->GetModelViewTransformMatrix();
617  view = Eigen::Matrix4d(vMat->GetData());
618  view.transposeInPlace();
619 }
620 
621 void PCLVis::resetCamera(const ccBBox* bbox) {
622  if (!bbox) return;
623  double bounds[6];
624  bbox->getBounds(bounds);
625  resetCamera(bounds);
626 }
627 
629  // set all renderer to this viewpoint
630  this->synchronizeGeometryBounds(viewport);
631  if (this->GeometryBounds.IsValid()) {
632  double originBounds[6];
633  this->GeometryBounds.GetBounds(originBounds);
634  this->GeometryBounds.ScaleAboutCenter(2, 2, 2);
635  double bounds[6];
636  this->GeometryBounds.GetBounds(bounds);
637  getCurrentRenderer(viewport)->ResetCameraClippingRange(bounds);
638  this->GeometryBounds.SetBounds(originBounds);
639  }
640  // if (getVtkCamera()->GetParallelProjection())
641  // {
642  // double nearFar[2];
643  // this->getReasonableClippingRange(nearFar, viewport);
644  // this->setCameraClipDistances(nearFar[0] / 3, nearFar[1] * 3,
645  // viewport);
646  // }
647 }
648 
649 void PCLVis::resetCamera(double xMin,
650  double xMax,
651  double yMin,
652  double yMax,
653  double zMin,
654  double zMax) {
655  // Update the camera parameters
656  getRendererCollection()->InitTraversal();
657  vtkRenderer* renderer = nullptr;
658  while ((renderer = getRendererCollection()->GetNextItem()) != nullptr) {
659  renderer->ResetCamera(xMin, xMax, yMin, yMax, zMin, zMax);
660  }
661 }
662 
663 // reset the camera clipping range to include this entire bounding box
664 void PCLVis::getReasonableClippingRange(double range[2], int viewport) {
665  double vn[3], position[3], a, b, c, d;
666  double dist;
667  int i, j, k;
668 
669  this->synchronizeGeometryBounds(viewport);
670 
671  double bounds[6];
672  this->GeometryBounds.GetBounds(bounds);
673 
674  // Don't reset the clipping range when we don't have any 3D visible props
675  if (!vtkMath::AreBoundsInitialized(bounds)) {
676  return;
677  }
678 
679  if (getVtkCamera() == nullptr) {
680  CVLog::Warning("Trying to reset clipping range of non-existent camera");
681  return;
682  }
683 
684  double expandedBounds[6] = {bounds[0], bounds[1], bounds[2],
685  bounds[3], bounds[4], bounds[5]};
686  if (!getVtkCamera()->GetUseOffAxisProjection()) {
687  getVtkCamera()->GetViewPlaneNormal(vn);
688  getVtkCamera()->GetPosition(position);
689  this->expandBounds(expandedBounds,
690  getVtkCamera()->GetModelTransformMatrix());
691  } else {
692  getVtkCamera()->GetEyePosition(position);
693  getVtkCamera()->GetEyePlaneNormal(vn);
694  this->expandBounds(expandedBounds,
695  getVtkCamera()->GetModelViewTransformMatrix());
696  }
697 
698  a = -vn[0];
699  b = -vn[1];
700  c = -vn[2];
701  d = -(a * position[0] + b * position[1] + c * position[2]);
702 
703  // Set the max near clipping plane and the min far clipping plane
704  range[0] = a * expandedBounds[0] + b * expandedBounds[2] +
705  c * expandedBounds[4] + d;
706  range[1] = 1e-18;
707 
708  // Find the closest / farthest bounding box vertex
709  for (k = 0; k < 2; k++) {
710  for (j = 0; j < 2; j++) {
711  for (i = 0; i < 2; i++) {
712  dist = a * expandedBounds[i] + b * expandedBounds[2 + j] +
713  c * expandedBounds[4 + k] + d;
714  range[0] = (dist < range[0]) ? (dist) : (range[0]);
715  range[1] = (dist > range[1]) ? (dist) : (range[1]);
716  }
717  }
718  }
719 
720  // do not let far - near be less than 0.1 of the window height
721  // this is for cases such as 2D images which may have zero range
722  double minGap = 0.0;
723  if (getVtkCamera()->GetParallelProjection()) {
724  minGap = 0.1 * this->getParallelScale();
725  } else {
726  double angle =
727  vtkMath::RadiansFromDegrees(getVtkCamera()->GetViewAngle());
728  minGap = 0.2 * tan(angle / 2.0) * range[1];
729  }
730  if (range[1] - range[0] < minGap) {
731  minGap = minGap - range[1] + range[0];
732  range[1] += minGap / 2.0;
733  range[0] -= minGap / 2.0;
734  }
735 
736  // Do not let the range behind the camera throw off the calculation.
737  if (range[0] < 0.0) {
738  range[0] = 0.0;
739  }
740 
741  // Give ourselves a little breathing room
742  range[0] = 0.99 * range[0] -
743  (range[1] - range[0]) *
744  getCurrentRenderer()->GetClippingRangeExpansion();
745  range[1] = 1.01 * range[1] +
746  (range[1] - range[0]) *
747  getCurrentRenderer()->GetClippingRangeExpansion();
748 
749  // Make sure near is not bigger than far
750  range[0] = (range[0] >= range[1]) ? (0.01 * range[1]) : (range[0]);
751 
752  // Make sure near is at least some fraction of far - this prevents near
753  // from being behind the camera or too close in front. How close is too
754  // close depends on the resolution of the depth buffer
755  if (!getCurrentRenderer()->GetNearClippingPlaneTolerance()) {
756  getCurrentRenderer()->SetNearClippingPlaneTolerance(0.01);
757  if (this->getRenderWindow()) {
758  int ZBufferDepth = this->getRenderWindow()->GetDepthBufferSize();
759  if (ZBufferDepth > 16) {
760  getCurrentRenderer()->SetNearClippingPlaneTolerance(0.001);
761  }
762  }
763  }
764 
765  // make sure the front clipping range is not too far from the far clippnig
766  // range, this is to make sure that the zbuffer resolution is effectively
767  // used
768  if (range[0] <
769  getCurrentRenderer()->GetNearClippingPlaneTolerance() * range[1]) {
770  range[0] = getCurrentRenderer()->GetNearClippingPlaneTolerance() *
771  range[1];
772  }
773 }
774 
775 //----------------------------------------------------------------------------
776 void PCLVis::expandBounds(double bounds[6], vtkMatrix4x4* matrix) {
777  if (!bounds) {
778  CVLog::Error("ERROR: Invalid bounds");
779  return;
780  }
781 
782  if (!matrix) {
783  CVLog::Error("<<ERROR: Invalid matrix");
784  return;
785  }
786 
787  // Expand the bounding box by model view transform matrix.
788  double pt[8][4] = {{bounds[0], bounds[2], bounds[5], 1.0},
789  {bounds[1], bounds[2], bounds[5], 1.0},
790  {bounds[1], bounds[2], bounds[4], 1.0},
791  {bounds[0], bounds[2], bounds[4], 1.0},
792  {bounds[0], bounds[3], bounds[5], 1.0},
793  {bounds[1], bounds[3], bounds[5], 1.0},
794  {bounds[1], bounds[3], bounds[4], 1.0},
795  {bounds[0], bounds[3], bounds[4], 1.0}};
796 
797  // \note: Assuming that matrix doesn not have projective component. Hence
798  // not dividing by the homogeneous coordinate after multiplication
799  for (int i = 0; i < 8; ++i) {
800  matrix->MultiplyPoint(pt[i], pt[i]);
801  }
802 
803  // min = mpx = pt[0]
804  double min[4], max[4];
805  for (int i = 0; i < 4; ++i) {
806  min[i] = pt[0][i];
807  max[i] = pt[0][i];
808  }
809 
810  for (int i = 1; i < 8; ++i) {
811  for (int j = 0; j < 3; ++j) {
812  if (min[j] > pt[i][j]) min[j] = pt[i][j];
813  if (max[j] < pt[i][j]) max[j] = pt[i][j];
814  }
815  }
816 
817  // Copy values back to bounds.
818  bounds[0] = min[0];
819  bounds[2] = min[1];
820  bounds[4] = min[2];
821 
822  bounds[1] = max[0];
823  bounds[3] = max[1];
824  bounds[5] = max[2];
825 }
826 
827 void PCLVis::setCameraViewAngle(double viewAngle, int viewport) {
828  getRendererCollection()->InitTraversal();
829  vtkRenderer* renderer = nullptr;
830  int i = 0;
831  while ((renderer = getRendererCollection()->GetNextItem())) {
832  // Modify all renderer's cameras
833  if (viewport == 0 || viewport == i) {
834  vtkSmartPointer<vtkCamera> cam = renderer->GetActiveCamera();
835  cam->SetViewAngle(viewAngle);
836  renderer->ResetCameraClippingRange();
837  }
838  ++i;
839  }
840 
841  this->resetCameraClippingRange(viewport);
842  UpdateScreen();
843 }
844 
845 /********************************Draw Entities*********************************/
847  const PCLCloud::Ptr& smCloud) {
848  if (!smCloud || smCloud->fields.empty()) {
849  return;
850  }
851 
852  const std::string viewID = CVTools::FromQString(context.viewID);
853  int viewport = context.defaultViewPort;
854 
855  if (context.drawParam.showColors || context.drawParam.showSF) {
856  PointCloudRGB::Ptr cloud_rgb(new PointCloudRGB);
857  FROM_PCL_CLOUD(*smCloud, *cloud_rgb);
858 
859  if (cloud_rgb) {
860  if (!updatePointCloud(cloud_rgb, viewID)) {
861  addPointCloud(cloud_rgb, viewID, viewport);
862  }
863  }
864  } else {
865  PointCloudT::Ptr cloud_xyz(new PointCloudT);
866  FROM_PCL_CLOUD(*smCloud, *cloud_xyz);
867  if (cloud_xyz) {
868  ecvColor::Rgbub col = context.pointsDefaultCol;
869  pcl::visualization::PointCloudColorHandlerCustom<PointT>
870  single_color(cloud_xyz, col.r, col.g, col.b);
871  if (!updatePointCloud<PointT>(cloud_xyz, single_color, viewID)) {
872  addPointCloud<PointT>(cloud_xyz, single_color, viewID,
873  viewport);
874  }
875  }
876  }
877 
878  // Sync normals and RGB colors to VTK polydata for Find Data / selection
879  // extraction PCL's visualization handles colors/normals internally through
880  // handlers, but we need them accessible via GetScalars()/GetNormals() for
881  // extraction
882  updateShadingMode(context, *smCloud);
883 }
884 
885 void PCLVis::draw(const CC_DRAW_CONTEXT& context, const PCLMesh::Ptr& pclMesh) {
886  if (!pclMesh) {
887  return;
888  }
889 
890  std::string viewID = CVTools::FromQString(context.viewID);
891  int viewport = context.defaultViewPort;
892  if (context.visFiltering) {
893  if (contains(viewID)) {
894  removeMesh(viewID, viewport);
895  }
896  addPolygonMesh(*pclMesh, viewID, viewport);
897  } else {
898  if (!updatePolygonMesh(*pclMesh, viewID)) {
899  addPolygonMesh(*pclMesh, viewID, viewport);
900  }
901  }
902 
903  // normal shading
904  updateShadingMode(context, pclMesh->cloud);
905 }
906 
908  const PCLTextureMesh::Ptr& textureMesh) {
909  std::string viewID = CVTools::FromQString(context.viewID);
910  int viewport = context.defaultViewPort;
911  if (contains(viewID)) {
912  removeMesh(viewID, viewport);
913  }
914  addTextureMesh(*textureMesh, viewID, viewport);
915 }
916 
918  const PCLPolygon::Ptr& pclPolygon,
919  bool closed) {
920  std::string viewID = CVTools::FromQString(context.viewID);
921  int viewport = context.defaultViewPort;
922  Eigen::Vector3d polygonColor =
923  ecvColor::Rgb::ToEigen(context.defaultPolylineColor);
924 
925  removeShape(viewID);
926 
927  if (closed) {
928  addPolygon<PointT>(*pclPolygon, polygonColor.x(), polygonColor.y(),
929  polygonColor.z(), viewID, viewport);
930  } else {
931  addPolyline(pclPolygon, polygonColor.x(), polygonColor.y(),
932  polygonColor.z(), context.defaultLineWidth, viewID,
933  viewport);
934  }
935 }
936 
937 void PCLVis::draw(const CC_DRAW_CONTEXT& context, const ccSensor* sensor) {
938  if (!sensor) return;
939 
940  std::string viewID = CVTools::FromQString(context.viewID);
941  int viewport = context.defaultViewPort;
942  float lineWidth = static_cast<float>(context.currentLineWidth);
943  if (contains(viewID)) {
944  removeShapes(viewID, viewport);
945  }
946 
947  vtkSmartPointer<vtkPolyData> linesData = nullptr;
948  if (sensor->isA(CV_TYPES::CAMERA_SENSOR)) {
949  // the sensor to draw
950  const ccCameraSensor* camera =
951  reinterpret_cast<const ccCameraSensor*>(sensor);
952  if (!camera) return;
953  linesData = PclTools::CreateCameraSensor(
954  camera, context.defaultPolylineColor, context.defaultMeshColor);
955  } else if (sensor->isA(CV_TYPES::GBL_SENSOR)) {
956  // the sensor to draw
957  const ccGBLSensor* camera =
958  reinterpret_cast<const ccGBLSensor*>(sensor);
959  ;
960  if (!camera) return;
961  linesData = PclTools::CreateGBLSensor(camera);
962  } else {
963  CVLog::Error("[PCLVis::draw] unsupported sensor type!");
964  }
965 
966  if (!linesData) {
967  if (sensor->isA(CV_TYPES::CAMERA_SENSOR)) {
968  CVLog::Error("[PCLVis::draw] CreateCameraSensor failed!");
969  } else if (sensor->isA(CV_TYPES::GBL_SENSOR)) {
970  CVLog::Error("[PCLVis::draw] CreateGBLSensor failed!");
971  }
972  return;
973  }
974 
975  // Create lines Actor
976  vtkSmartPointer<vtkLODActor> linesActor;
977  PclTools::CreateActorFromVTKDataSet(linesData, linesActor);
978  linesActor->GetProperty()->SetRepresentationToSurface();
979  linesActor->GetProperty()->SetLineWidth(lineWidth);
980  addActorToRenderer(linesActor, viewport);
981 
982  // Save the pointer/ID pair to the global actor map
983  (*getShapeActorMap())[viewID] = linesActor;
984 }
985 
987  const cloudViewer::geometry::LineSet* lineset) {
988  if (!lineset) return;
989 
990  std::string viewID = CVTools::FromQString(context.viewID);
991  int viewport = context.defaultViewPort;
992  float lineWidth = static_cast<float>(context.currentLineWidth);
993  if (contains(viewID)) {
994  removeShapes(viewID, viewport);
995  }
996 
997  vtkSmartPointer<vtkPolyData> linesData =
998  PclTools::CreatePolyDataFromLineSet(*lineset, false);
999 
1000  // Create lines Actor
1001  vtkSmartPointer<vtkLODActor> linesActor;
1002  PclTools::CreateActorFromVTKDataSet(linesData, linesActor);
1003  linesActor->GetProperty()->SetRepresentationToSurface();
1004  linesActor->GetProperty()->SetLineWidth(lineWidth);
1005  addActorToRenderer(linesActor, viewport);
1006 
1007  // Save the pointer/ID pair to the global actor map
1008  (*getShapeActorMap())[viewID] = linesActor;
1009 }
1010 
1012  if (context.transformInfo.isApplyTransform()) {
1013  std::string viewID = CVTools::FromQString(context.viewID);
1014  vtkActor* actor = getActorById(viewID);
1015  if (actor) {
1016  if (context.transformInfo.isPositionChanged) {
1018  CCVector3d::fromArray(context.transformInfo.position.u);
1019  actor->AddPosition(position.u);
1020  }
1021 
1022  double* origin = actor->GetOrigin();
1025  actor->SetUserTransform(trans);
1026  actor->Modified();
1027  }
1028  }
1029 }
1030 
1032  const CC_DRAW_CONTEXT& context, const CCVector3d& origin) {
1033  // remove invalid bbox
1034  std::string bboxId =
1035  CVTools::FromQString(QString("BBox-") + context.viewID);
1036  removeShapes(bboxId, context.defaultViewPort);
1037 
1038  TransformInfo transInfo = context.transformInfo;
1039 
1041  trans->Identity();
1042  // trans->PostMultiply();
1043  trans->Translate(-origin[0], -origin[1], -origin[2]);
1044  if (transInfo.isTranslate) {
1045  trans->Translate(transInfo.transVecStart.u);
1046  trans->Translate(transInfo.transVecEnd.u);
1047  }
1048 
1049  if (transInfo.isRotate) {
1050  if (transInfo.applyEuler) {
1051  trans->RotateZ(transInfo.eulerZYX[0]);
1052  trans->RotateY(transInfo.eulerZYX[1]);
1053  trans->RotateX(transInfo.eulerZYX[2]);
1054  } else {
1055  // Eigen::AngleAxisd
1056  // a(Eigen::Quaterniond(transInfo.quaternion));
1057  // trans->RotateWXYZ
1058  // (cloudViewer::RadiansToDegrees(a.angle ()), a.axis
1059  // ()[0], a.axis ()[1], a.axis ()[2]);
1060  trans->RotateWXYZ(transInfo.rotateParam.angle,
1061  transInfo.rotateParam.rotAxis.u);
1062  }
1063  }
1064 
1065  if (transInfo.isScale) {
1066  trans->Scale(transInfo.scaleXYZ.u);
1067  }
1068 
1069  trans->Translate(origin.data());
1070 
1071  return trans;
1072 }
1073 
1075  const PCLCloud::Ptr& smCloud) {
1076  const std::string viewID = CVTools::FromQString(context.viewID);
1077  int viewport = context.defaultViewPort;
1078 
1079  if (context.drawParam.showNorms) {
1080  const std::string normalID = viewID + "-normal";
1081  pcl::PointCloud<pcl::PointNormal>::Ptr cloud_normals(
1082  new pcl::PointCloud<pcl::PointNormal>);
1083  FROM_PCL_CLOUD(*smCloud, *cloud_normals);
1084  int normalDensity =
1085  context.normalDensity > 20 ? context.normalDensity : 20;
1086  float normalScale =
1087  context.normalScale > 0.02f ? context.normalScale : 0.02f;
1088  if (contains(normalID)) {
1089  removePointClouds(normalID, viewport);
1090  }
1091  addPointCloudNormals<pcl::PointNormal>(cloud_normals, normalDensity,
1092  normalScale, normalID, viewport);
1093  setPointCloudUniqueColor(0.0, 0.0, 1.0, normalID, viewport);
1094  } else {
1095  const std::string normalID = viewID + "-normal";
1096  if (contains(normalID)) {
1097  removePointClouds(normalID, viewport);
1098  }
1099  }
1100 
1101  // normal shading
1102  if (!smCloud || smCloud->fields.empty()) {
1103  PCLCloud cloud;
1104  updateShadingMode(context, cloud);
1105  } else {
1106  updateShadingMode(context, *smCloud);
1107  }
1108 }
1109 
1111  PCLCloud& smCloud) {
1112  // normal shading
1113  const std::string viewID = CVTools::FromQString(context.viewID);
1114  int viewport = context.defaultViewPort;
1115  auto actor = getActorById(viewID);
1116  if (!actor) return;
1117  auto polydata = vtkPolyData::SafeDownCast(actor->GetMapper()->GetInput());
1118  if (!polydata) return;
1119 
1120  // ALWAYS sync normals to VTK polydata if available (for Find Data /
1121  // selection) Normals data is set once and kept - shading mode controls
1122  // visual effect This ensures Find Data can always detect normals regardless
1123  // of display setting Use context.forceRedraw to force update when upstream
1124  // data changes (e.g., normals recalculated)
1125  bool has_normal = false;
1126  if (!smCloud.fields.empty()) {
1127  has_normal = (pcl::getFieldIndex(smCloud, "normal_x") != -1) &&
1128  (pcl::getFieldIndex(smCloud, "normal_y") != -1) &&
1129  (pcl::getFieldIndex(smCloud, "normal_z") != -1);
1130  if (has_normal) {
1131  // Check if normals need to be updated:
1132  // 1. No existing normals
1133  // 2. Tuple count mismatch
1134  // 3. Force redraw requested (upstream data changed)
1135  vtkDataArray* existingNormals =
1136  polydata->GetPointData()->GetNormals();
1137  bool needUpdate = context.forceRedraw || !existingNormals ||
1138  existingNormals->GetNumberOfTuples() !=
1139  static_cast<vtkIdType>(
1140  polydata->GetNumberOfPoints());
1141  if (needUpdate) {
1144  CloudNormal cloud;
1145  FROM_PCL_CLOUD(smCloud, cloud);
1146  if (cloud.points.empty()) {
1147  CVLog::Error("[PCLVis::updateShadingMode] Cloud is empty!");
1148  return;
1149  }
1150  normals->SetNumberOfComponents(3);
1151  normals->SetName("Normals");
1152  for (std::size_t i = 0; i < cloud.points.size(); ++i) {
1153  const NormalT& N = cloud.points[i];
1154  const float normal[3] = {N.normal_x, N.normal_y,
1155  N.normal_z};
1156  normals->InsertNextTupleValue(normal);
1157  }
1158  polydata->GetPointData()->SetNormals(normals);
1159  }
1160  }
1161  }
1162 
1163  // ALWAYS sync RGB colors to VTK polydata if available (for Find Data /
1164  // selection extraction) PCL's visualization uses color handlers internally
1165  // but may not set them as active scalars This ensures the correct color
1166  // array is available for extraction Use context.forceRedraw to force update
1167  // when upstream colors change
1168  //
1169  // IMPORTANT: We add colors as a NAMED array "RGB" (not "Colors") and do NOT
1170  // set as active scalars This prevents scalar field data (like curvature)
1171  // from being confused with RGB during extraction The "HasSourceRGB" flag in
1172  // FieldData indicates this cloud has actual RGB data from source
1173  bool has_color = false;
1174  if (!smCloud.fields.empty()) {
1175  has_color = (pcl::getFieldIndex(smCloud, "rgba") != -1) ||
1176  (pcl::getFieldIndex(smCloud, "rgb") != -1);
1177  if (has_color) {
1178  // Check if colors need to be updated
1179  vtkDataArray* existingColors =
1180  polydata->GetPointData()->GetArray("RGB");
1181  bool needSync = context.forceRedraw || !existingColors ||
1182  existingColors->GetNumberOfTuples() !=
1183  static_cast<vtkIdType>(
1184  polydata->GetNumberOfPoints());
1185  if (needSync) {
1188  colors->SetName(
1189  "RGB"); // Use "RGB" not "Colors" to be explicit
1190  colors->SetNumberOfComponents(3);
1191 
1192  PointCloudRGB cloud;
1193  FROM_PCL_CLOUD(smCloud, cloud);
1194  if (!cloud.points.empty()) {
1195  colors->SetNumberOfTuples(
1196  static_cast<vtkIdType>(cloud.points.size()));
1197  for (std::size_t i = 0; i < cloud.points.size(); ++i) {
1198  const pcl::PointXYZRGB& p = cloud.points[i];
1199  unsigned char color[3] = {p.r, p.g, p.b};
1200  colors->SetTypedTuple(static_cast<vtkIdType>(i), color);
1201  }
1202  // Add as named array, NOT as active scalars
1203  // This prevents confusion with scalar fields during
1204  // extraction
1205  polydata->GetPointData()->AddArray(colors);
1206 
1207  // Set flag indicating this polydata has source RGB data
1208  vtkFieldData* fieldData = polydata->GetFieldData();
1209  if (fieldData) {
1212  hasRGB->SetName("HasSourceRGB");
1213  hasRGB->SetNumberOfTuples(1);
1214  hasRGB->SetValue(0, 1);
1215  fieldData->AddArray(hasRGB);
1216  }
1217 
1219  "[PCLVis::updateShadingMode] Synced %zu RGB colors "
1220  "to VTK polydata (as 'RGB' array)",
1221  cloud.points.size());
1222  }
1223  }
1224  }
1225  }
1226 
1227  // Control shading mode based on showNorms preference
1228  // PHONG shading uses normals for smooth lighting effects
1229  // FLAT shading ignores normals - shows faceted appearance
1230  // NOTE: Don't call SetNormals(nullptr) here! Keep normals data for Find
1231  // Data The shading mode alone controls whether normals affect rendering
1232  if (context.drawParam.showNorms && has_normal) {
1234  } else {
1236  }
1237  actor->GetMapper()->Update();
1238  actor->Modified();
1239 }
1240 
1242  std::string viewID = CVTools::FromQString(context.viewID);
1243  int viewport = context.defaultViewPort;
1244  vtkAbstractWidget* widget = getWidgetById(viewID);
1245  if (!widget || !PclTools::UpdateScalarBar(widget, context)) {
1246  this->removeWidgets(viewID, viewport);
1247  return false;
1248  }
1249 
1250  return true;
1251 }
1252 
1254  std::string viewID = CVTools::FromQString(context.viewID);
1255  if (containWidget(viewID)) {
1257  "[PCLVis::addScalarBar] The id <%s> already exists! Please "
1258  "choose a different id and retry.",
1259  viewID.c_str());
1260  return false;
1261  }
1262 
1265  scalarBarWidget->SetInteractor(getRenderWindowInteractor());
1266  scalarBarWidget->CreateDefaultRepresentation();
1267  scalarBarWidget->CreateDefaultScalarBarActor();
1268  scalarBarWidget->On();
1269 
1270  // Save the pointer/ID pair to the global actor map
1271  (*m_widget_map)[viewID].widget = scalarBarWidget;
1272 
1273  return updateScalarBar(context);
1274 }
1275 
1276 bool PCLVis::updateCaption(const std::string& text,
1277  const CCVector2& pos2D,
1278  const CCVector3& anchorPos,
1279  double r,
1280  double g,
1281  double b,
1282  double a,
1283  int fontSize,
1284  const std::string& viewID,
1285  int viewport) {
1286  Q_UNUSED(pos2D);
1287  Q_UNUSED(a);
1288  Q_UNUSED(viewport);
1289  vtkAbstractWidget* widget = getWidgetById(viewID);
1290  if (!widget) return false;
1291 
1292  CustomVtkCaptionWidget* captionWidget =
1293  CustomVtkCaptionWidget::SafeDownCast(widget);
1294  if (!captionWidget) return false;
1295 
1296  vtkCaptionRepresentation* rep = vtkCaptionRepresentation::SafeDownCast(
1297  captionWidget->GetRepresentation());
1298  if (rep) {
1299  // rep->SetPosition(1.0 * pos2D.x / getRenderWindow()->GetSize()[0], 1.0
1300  // * pos2D.y / getRenderWindow()->GetSize()[1]);
1301  rep->SetAnchorPosition(CCVector3d::fromArray(anchorPos.u).u);
1302  vtkCaptionActor2D* actor2D = rep->GetCaptionActor2D();
1303  actor2D->SetCaption(text.c_str());
1304 
1305  vtkTextProperty* textProperty =
1306  actor2D->GetTextActor()->GetTextProperty();
1307  textProperty->SetColor(r, g, b);
1308  textProperty->SetFontSize(fontSize);
1309  // Set line spacing to prevent text overlap between lines
1310  // 1.2 means 20% extra spacing between lines (1.0 = no extra spacing)
1311  textProperty->SetLineSpacing(1.2);
1312  }
1313 
1314  return true;
1315 }
1316 
1317 bool PCLVis::getCaptionPosition(const std::string& viewID,
1318  float& posX,
1319  float& posY) {
1320  vtkAbstractWidget* widget = getWidgetById(viewID);
1321  if (!widget) {
1322  return false;
1323  }
1324 
1325  CustomVtkCaptionWidget* captionWidget =
1326  CustomVtkCaptionWidget::SafeDownCast(widget);
1327  if (!captionWidget) {
1328  return false;
1329  }
1330 
1331  vtkCaptionRepresentation* rep = vtkCaptionRepresentation::SafeDownCast(
1332  captionWidget->GetRepresentation());
1333  if (!rep) {
1334  return false;
1335  }
1336 
1337  // GetPosition() returns a pointer to double[2] array (normalized
1338  // coordinates)
1339  const double* pos = rep->GetPosition();
1340  if (!pos) {
1341  return false;
1342  }
1343 
1344  // VTK uses normalized coordinates (0.0 to 1.0)
1345  // pos[0] is X (0.0 = left, 1.0 = right)
1346  // pos[1] is Y (0.0 = bottom, 1.0 = top in VTK coordinate system)
1347  posX = static_cast<float>(pos[0]);
1348  posY = static_cast<float>(pos[1]);
1349 
1350  return true;
1351 }
1352 
1353 bool PCLVis::addCaption(const std::string& text,
1354  const CCVector2& pos2D,
1355  const CCVector3& anchorPos,
1356  double r,
1357  double g,
1358  double b,
1359  double a,
1360  int fontSize,
1361  const std::string& viewID,
1362  bool anchorDragable,
1363  int viewport) {
1364  Q_UNUSED(viewport);
1365 
1366  if (containWidget(viewID)) {
1368  "[PCLVis::addCaption] The id <%s> already exists! Please "
1369  "choose a different id and retry.",
1370  viewID.c_str());
1371  return false;
1372  }
1373 
1374  vtkSmartPointer<vtkCaptionRepresentation> captionRepresentation =
1376  {
1377  captionRepresentation->SetAnchorPosition(
1378  CCVector3d::fromArray(anchorPos.u).u);
1379  captionRepresentation->SetPosition(
1380  1.0 * pos2D.x / getRenderWindow()->GetSize()[0],
1381  1.0 * pos2D.y / getRenderWindow()->GetSize()[1]);
1382  }
1383 
1384  vtkCaptionActor2D* actor2D = captionRepresentation->GetCaptionActor2D();
1385  actor2D->SetCaption(text.c_str());
1386  actor2D->ThreeDimensionalLeaderOff();
1387  actor2D->BorderOff();
1388  actor2D->LeaderOn();
1389  actor2D->SetLeaderGlyphSize(10.0);
1390  actor2D->SetMaximumLeaderGlyphSize(10.0);
1391 
1392  const ecvColor::Rgbf& actorColor = ecvColor::FromRgb(ecvColor::red);
1393  actor2D->GetProperty()->SetColor(actorColor.r, actorColor.g, actorColor.b);
1394 
1395  vtkTextProperty* textProperty = actor2D->GetTextActor()->GetTextProperty();
1396  textProperty->SetColor(r, g, b);
1397  textProperty->SetFontSize(fontSize);
1399  textProperty->SetBackgroundColor(col.r, col.g, col.b);
1400  textProperty->SetBackgroundOpacity(a);
1401  textProperty->FrameOn();
1402  textProperty->SetFrameColor(actorColor.r, actorColor.g, actorColor.b);
1403  textProperty->SetFrameWidth(2);
1404  textProperty->BoldOff();
1405  textProperty->ItalicOff();
1406  textProperty->SetFontFamilyToArial();
1407  textProperty->SetJustificationToLeft();
1408  textProperty->SetVerticalJustificationToCentered();
1409  // Set line spacing to prevent text overlap between lines
1410  // 1.2 means 20% extra spacing between lines (1.0 = no extra spacing)
1411  textProperty->SetLineSpacing(1.2);
1412 
1415  captionWidget->SetHandleEnabled(anchorDragable);
1416 
1417  vtkRenderWindowInteractor* interactor = getRenderWindowInteractor();
1418  if (!interactor) {
1419  return false;
1420  }
1421 
1422  captionWidget->SetInteractor(interactor);
1423  captionWidget->SetRepresentation(captionRepresentation);
1424  captionWidget->On();
1425 
1426  // Associate the widget with the corresponding cc2DLabel for selection
1427  // Find the cc2DLabel by viewID in the scene database
1428  cc2DLabel* associatedLabel = nullptr;
1429  ccHObject* sceneRoot = ecvDisplayTools::GetSceneDB();
1430  if (sceneRoot) {
1431  QString viewIDStr = QString::fromStdString(viewID);
1432 
1433  // Recursively search for cc2DLabel with matching viewID
1434  std::function<cc2DLabel*(ccHObject*)> findByViewID =
1435  [&findByViewID, &viewIDStr](ccHObject* node) -> cc2DLabel* {
1436  if (!node) return nullptr;
1437  if (node->getViewId() == viewIDStr &&
1438  node->isA(CV_TYPES::LABEL_2D)) {
1439  return ccHObjectCaster::To2DLabel(node);
1440  }
1441  for (unsigned i = 0; i < node->getChildrenNumber(); ++i) {
1442  cc2DLabel* found = findByViewID(node->getChild(i));
1443  if (found) return found;
1444  }
1445  return nullptr;
1446  };
1447  associatedLabel = findByViewID(sceneRoot);
1448  }
1449 
1450  // Set the associated label for selection notification
1451  if (associatedLabel) {
1452  captionWidget->SetAssociatedLabel(associatedLabel);
1453  }
1454 
1455  // Save the pointer/ID pair to the global actor map
1456  (*m_widget_map)[viewID].widget = captionWidget;
1457 
1458  return true;
1459 }
1460 
1461 bool PCLVis::addPolyline(const PCLPolygon::ConstPtr pclPolygon,
1462  double r,
1463  double g,
1464  double b,
1465  float width,
1466  const std::string& id,
1467  int viewport) {
1469  poly_points->SetNumberOfPoints(pclPolygon->getContour().size());
1470  size_t i;
1471  for (i = 0; i < pclPolygon->getContour().size(); ++i) {
1472  poly_points->SetPoint(i, pclPolygon->getContour()[i].x,
1473  pclPolygon->getContour()[i].y,
1474  pclPolygon->getContour()[i].z);
1475  }
1476 
1477  // Check to see if this ID entry already exists (has it been already added
1478  // to the visualizer?)
1479  pcl::visualization::ShapeActorMap::iterator am_it =
1480  getShapeActorMap()->find(id);
1481  if (am_it != getShapeActorMap()->end()) {
1483  "[addPolyline] A shape with id <%s> already exists! Please "
1484  "choose a different id and retry.",
1485  id.c_str());
1486  return (false);
1487  }
1488 
1489  if (poly_points->GetNumberOfPoints() < 2) {
1490  CVLog::Warning("[addPolyline] point size less 2.");
1491  return (false);
1492  }
1493 
1495 
1496  // Create an Actor
1499  actor->GetProperty()->SetRepresentationToSurface();
1500  actor->GetProperty()->SetLineWidth(width);
1501  actor->GetProperty()->SetColor(r, g, b);
1502  addActorToRenderer(actor, viewport);
1503 
1504  // Save the pointer/ID pair to the global actor map
1505  (*getShapeActorMap())[id] = actor;
1506  return (true);
1507 }
1508 
1510  ecvTextParam textParam = context.textParam;
1511  if (textParam.text.isEmpty()) {
1512  CVLog::Warning("empty text detected!");
1513  return;
1514  }
1515  std::string text = CVTools::FromQString(textParam.text);
1516  // std::string viewID = CVTools::FromQString(context.viewID);
1517  const std::string& viewID = text;
1518 
1519  int viewport = context.defaultViewPort;
1520  int xPos = static_cast<int>(textParam.textPos.x);
1521  int yPos = static_cast<int>(textParam.textPos.y);
1522  ecvColor::Rgbf textColor = ecvTools::TransFormRGB(context.textDefaultCol);
1523  if (textParam.display3D) {
1524  pcl::PointXYZ position;
1525  position.x = textParam.textPos.x;
1526  position.y = textParam.textPos.y;
1527  position.z = textParam.textPos.z;
1528  if (contains(viewID)) {
1529  removeShapes(viewID, viewport);
1530  }
1531 
1532  addText3D(text, position, textParam.textScale, textColor.r, textColor.g,
1533  textColor.b, viewID, viewport);
1534  } else {
1535  if (!updateText(text, xPos, yPos, viewID)) {
1536  addText(text, xPos, yPos, textParam.font.pointSize(), textColor.r,
1537  textColor.g, textColor.b, viewID, viewport);
1538  }
1539  }
1540 }
1541 
1543  const ccMaterialSet* materials) {
1544  CVLog::PrintDebug("[PCLVis::updateTexture] ENTRY: viewID=%s, materials=%zu",
1545  CVTools::FromQString(context.viewID).c_str(),
1546  materials ? materials->size() : 0);
1547 
1548  std::string viewID = CVTools::FromQString(context.viewID);
1549  if (!contains(viewID)) return false;
1550  auto actor = getActorById(viewID);
1551  if (!actor) return false;
1552 
1553  if (!materials || materials->empty()) {
1554  CVLog::Warning("[PCLVis::updateTexture] No materials provided");
1555  return false;
1556  }
1557 
1558  // Get polydata for texture coordinates
1559  vtkPolyData* polydata = nullptr;
1560  vtkPolyDataMapper* mapper =
1561  vtkPolyDataMapper::SafeDownCast(actor->GetMapper());
1562  if (mapper) {
1563  polydata = vtkPolyData::SafeDownCast(mapper->GetInput());
1564  }
1565 
1566  // Get renderer
1567  vtkRenderer* renderer = getCurrentRenderer(context.defaultViewPort);
1568 
1569  // Use TextureRenderManager to update
1570  bool success = texture_render_manager_->Update(actor, materials, polydata,
1571  renderer);
1572  if (success) {
1573  actor->Modified();
1574  }
1575  return success;
1576 }
1577 
1579  const std::string& id,
1580  int viewport) {
1581  CVLog::PrintVerbose("[PCLVis::addTextureMesh] ENTRY: id=%s, materials=%zu",
1582  id.c_str(), mesh.tex_materials.size());
1583 
1584  pcl::visualization::CloudActorMap::iterator am_it =
1585  getCloudActorMap()->find(id);
1586  if (am_it != getCloudActorMap()->end()) {
1587  CVLog::Error(
1588  "[PCLVis::addTextureMesh] A shape with id <%s> already exists!"
1589  " Please choose a different id and retry.",
1590  id.c_str());
1591  return (false);
1592  }
1593  // no texture materials --> exit
1594  if (mesh.tex_materials.empty()) {
1595  CVLog::Error("[PCLVis::addTextureMesh] No textures found!");
1596  return (false);
1597  }
1598  // polygons are mapped to texture materials
1599  if (mesh.tex_materials.size() != mesh.tex_polygons.size()) {
1600  CVLog::Error(
1601  "[PCLVis::addTextureMesh] Materials number %lu differs from "
1602  "polygons number %lu!",
1603  mesh.tex_materials.size(), mesh.tex_polygons.size());
1604  return (false);
1605  }
1606  // each texture material should have its coordinates set
1607  if (mesh.tex_materials.size() != mesh.tex_coordinates.size()) {
1608  CVLog::Error(
1609  "[PCLVis::addTextureMesh] Coordinates number %lu differs from "
1610  "materials number %lu!",
1611  mesh.tex_coordinates.size(), mesh.tex_materials.size());
1612  return (false);
1613  }
1614  // total number of vertices
1615  std::size_t nb_vertices = 0;
1616  for (const auto& tex_polygon : mesh.tex_polygons)
1617  nb_vertices += tex_polygon.size();
1618  // no vertices --> exit
1619  if (nb_vertices == 0) {
1620  CVLog::Error("[PCLVis::addTextureMesh] No vertices found!");
1621  return (false);
1622  }
1623 
1624  // Create points from mesh.cloud
1628  bool has_color = false;
1631  bool has_normal = false;
1632  vtkSmartPointer<vtkMatrix4x4> transformation =
1634 
1635  has_color = (pcl::getFieldIndex(mesh.cloud, "rgba") != -1) ||
1636  (pcl::getFieldIndex(mesh.cloud, "rgb") != -1);
1637  has_normal = (pcl::getFieldIndex(mesh.cloud, "normal_x") != -1) &&
1638  (pcl::getFieldIndex(mesh.cloud, "normal_y") != -1) &&
1639  (pcl::getFieldIndex(mesh.cloud, "normal_z") != -1);
1640 
1641  if (has_color && !has_normal) {
1642  PointCloudRGB cloud;
1643  FROM_PCL_CLOUD(mesh.cloud, cloud);
1644  if (cloud.points.empty()) {
1645  CVLog::Error("[PCLVis::addTextureMesh] Cloud is empty!");
1646  return (false);
1647  }
1648  convertToVtkMatrix(cloud.sensor_origin_, cloud.sensor_orientation_,
1649  transformation);
1650  colors->SetNumberOfComponents(3);
1651  colors->SetName("RGB"); // Use "RGB" not "Colors" for consistency
1652  poly_points->SetNumberOfPoints(cloud.size());
1653  for (std::size_t i = 0; i < cloud.points.size(); ++i) {
1654  const pcl::PointXYZRGB& p = cloud.points[i];
1655  poly_points->InsertPoint((vtkIdType)i, p.x, p.y, p.z);
1656  const unsigned char color[3] = {p.r, p.g, p.b};
1657  colors->InsertNextTupleValue(color);
1658  }
1659  } else if (!has_color && has_normal) {
1660  PointCloudNormal cloud;
1661  FROM_PCL_CLOUD(mesh.cloud, cloud);
1662  if (cloud.points.empty()) {
1663  CVLog::Error("[PCLVis::addTextureMesh] Cloud is empty!");
1664  return (false);
1665  }
1666  convertToVtkMatrix(cloud.sensor_origin_, cloud.sensor_orientation_,
1667  transformation);
1668  normals->SetNumberOfComponents(3);
1669  normals->SetName("Normals");
1670  poly_points->SetNumberOfPoints(cloud.size());
1671  for (std::size_t i = 0; i < cloud.points.size(); ++i) {
1672  const PointNT& p = cloud.points[i];
1673  poly_points->InsertPoint(i, p.x, p.y, p.z);
1674  const float normal[3] = {p.normal_x, p.normal_y, p.normal_z};
1675  normals->InsertNextTupleValue(normal);
1676  }
1677  } else if (has_color && has_normal) {
1678  PointCloudRGBNormal cloud;
1679  FROM_PCL_CLOUD(mesh.cloud, cloud);
1680  if (cloud.points.empty()) {
1681  CVLog::Error("[PCLVis::addTextureMesh] Cloud is empty!");
1682  return (false);
1683  }
1684  convertToVtkMatrix(cloud.sensor_origin_, cloud.sensor_orientation_,
1685  transformation);
1686  normals->SetNumberOfComponents(3);
1687  normals->SetName("Normals");
1688  colors->SetNumberOfComponents(3);
1689  colors->SetName("RGB"); // Use "RGB" not "Colors" for consistency
1690  poly_points->SetNumberOfPoints(cloud.size());
1691  for (std::size_t i = 0; i < cloud.points.size(); ++i) {
1692  const PointRGBNormal& p = cloud.points[i];
1693  poly_points->InsertPoint(i, p.x, p.y, p.z);
1694  const unsigned char color[3] = {p.r, p.g, p.b};
1695  colors->InsertNextTupleValue(color);
1696  const float normal[3] = {p.normal_x, p.normal_y, p.normal_z};
1697  normals->InsertNextTupleValue(normal);
1698  }
1699  } else if (!has_color && !has_normal) {
1700  PointCloudT::Ptr cloud(new PointCloudT());
1701  FROM_PCL_CLOUD(mesh.cloud, *cloud);
1702  // no points --> exit
1703  if (cloud->points.empty()) {
1704  CVLog::Error("[PCLVis::addTextureMesh] Cloud is empty!");
1705  return (false);
1706  }
1707  convertToVtkMatrix(cloud->sensor_origin_, cloud->sensor_orientation_,
1708  transformation);
1709  poly_points->SetNumberOfPoints(cloud->size());
1710  for (std::size_t i = 0; i < cloud->size(); ++i) {
1711  const pcl::PointXYZ& p = (*cloud)[i];
1712  poly_points->InsertPoint(i, p.x, p.y, p.z);
1713  }
1714  }
1715 
1716  // create polys from polyMesh.tex_polygons
1718  for (const auto& tex_polygon : mesh.tex_polygons) {
1719  for (const auto& vertex : tex_polygon) {
1720  polys->InsertNextCell(static_cast<int>(vertex.vertices.size()));
1721  for (const auto& point : vertex.vertices)
1722  polys->InsertCellPoint(point);
1723  }
1724  }
1725 
1727  polydata->SetPolys(polys);
1728  polydata->SetPoints(poly_points);
1729  if (has_color) polydata->GetPointData()->SetScalars(colors);
1730  if (has_normal) polydata->GetPointData()->SetNormals(normals);
1731 
1734  mapper->SetInputData(polydata);
1735 
1737 
1738  // total number of coordinates
1739  std::size_t nb_coordinates = 0;
1740  for (const auto& tex_coordinate : mesh.tex_coordinates)
1741  nb_coordinates += tex_coordinate.size();
1742  // no texture coordinates --> exit
1743  // Detect PBR material encoding (for pcl::TextureMesh compatibility)
1744  // pcl::TextureMesh uses pcl::TexMaterial which encodes multiple textures
1745  // into tex_name due to structure limitations.
1746  // NOTE: For direct ccMaterial usage, use addTextureMeshFromCCMesh() which
1747  // doesn't need encoding and uses ccMaterial API directly.
1748  bool has_pbr = false;
1749  if (!mesh.tex_materials.empty()) {
1750  std::string tex_name = mesh.tex_materials[0].tex_name;
1751  has_pbr = (tex_name.find("_PBR_MULTITEX") != std::string::npos);
1752  if (has_pbr) {
1754  "[PCLVis::addTextureMesh] Detected PBR material encoding "
1755  "(pcl::TextureMesh): %s",
1756  tex_name.c_str());
1757  }
1758  }
1759 
1760  if (nb_coordinates == 0) {
1762  "[PCLVisualizer::addTextureMesh] No textures coordinates "
1763  "found!");
1764  } else {
1765  // ========================================================================
1766  // Unified texture coordinate setup for all rendering paths
1767  // TextureRenderManager will handle texture and material application
1768  // ========================================================================
1770  "[PCLVis::addTextureMesh] Setting up texture coordinates for "
1771  "unified rendering");
1772 
1773  // Add texture coordinate arrays for all textures
1774  // MultiTextureRenderer expects arrays named "TCoords0", "TCoords1",
1775  // etc. PBRRenderer uses "TCoords0" as the active TCoords
1776  for (std::size_t tex_id = 0; tex_id < mesh.tex_coordinates.size();
1777  ++tex_id) {
1778  vtkSmartPointer<vtkFloatArray> coordinates =
1780  coordinates->SetNumberOfComponents(2);
1781  std::stringstream ss;
1782  if (mesh.tex_coordinates.size() == 1) {
1783  ss << "TCoords";
1784  } else {
1785  ss << "TCoords" << tex_id;
1786  }
1787  std::string coords_name = ss.str();
1788  coordinates->SetName(coords_name.c_str());
1789 
1790  // Fill coordinates for this texture
1791  for (const auto& tc : mesh.tex_coordinates[tex_id]) {
1792  coordinates->InsertNextTuple2(tc[0], tc[1]);
1793  }
1794 
1795  polydata->GetPointData()->AddArray(coordinates);
1796 
1797  // Set first texture coordinates as active TCoords (for PBR)
1798  if (tex_id == 0) {
1799  polydata->GetPointData()->SetTCoords(coordinates);
1800  }
1801  }
1802 
1803  // All texture and material application will be handled by
1804  // TextureRenderManager (see below after actor is added to renderer)
1805  }
1806 
1807  // set mapper
1808  actor->SetMapper(mapper);
1809  addActorToRenderer(actor, viewport);
1810 
1811  // Apply materials using TextureRenderManager (must be done after actor is
1812  // added to renderer)
1813  if (!mesh.tex_materials.empty()) {
1815  "[PCLVis::addTextureMesh] Applying materials using "
1816  "TextureRenderManager");
1817  // Get renderer
1818  vtkRenderer* renderer = getCurrentRenderer(viewport);
1819 
1820  // Use MeshTextureApplier to apply materials (unified approach)
1822  actor, mesh, polydata, texture_render_manager_.get(), renderer);
1823  }
1824 
1825  // Save the pointer/ID pair to the global actor map
1826  (*getCloudActorMap())[id].actor = actor;
1827 
1828  // Save the viewpoint transformation matrix to the global actor map
1829  // Store smart pointer in member map to ensure lifetime extends beyond
1830  // function scope
1831  transformation_map_[id] = transformation;
1832  (*getCloudActorMap())[id].viewpoint_transformation_ = transformation.Get();
1833 
1834  return (true);
1835 }
1836 
1838  const std::string& id,
1839  int viewport) {
1841  "[PCLVis::addTextureMeshFromCCMesh] ENTRY: id=%s, viewport=%d",
1842  id.c_str(), viewport);
1843 
1844  if (!mesh) {
1845  CVLog::Error("[PCLVis::addTextureMeshFromCCMesh] Mesh is null!");
1846  return false;
1847  }
1848 
1849  // Check if actor with this ID already exists, and remove it if so
1850  // This allows updating/reloading meshes with the same ID (e.g., when
1851  // materials/textures checkbox is toggled)
1852  pcl::visualization::CloudActorMap::iterator am_it =
1853  getCloudActorMap()->find(id);
1854  if (am_it != getCloudActorMap()->end()) {
1856  "[PCLVis::addTextureMeshFromCCMesh] Actor with id <%s> already "
1857  "exists, removing old actor before adding new one",
1858  id.c_str());
1859  vtkActor* oldActor = am_it->second.actor;
1860  if (oldActor) {
1861  removeActorFromRenderer(oldActor, viewport);
1862  }
1863  // Clean up transformation matrix from member map
1864  transformation_map_.erase(id);
1865  getCloudActorMap()->erase(am_it);
1866  }
1867 
1868  const ccMaterialSet* materials = mesh->getMaterialSet();
1869  if (!materials || materials->empty()) {
1870  CVLog::Error(
1871  "[PCLVis::addTextureMeshFromCCMesh] No materials found in "
1872  "mesh!");
1873  return false;
1874  }
1875 
1876  // Get associated point cloud for cc2smReader
1877  ccPointCloud* cloud =
1879  if (!cloud) {
1880  CVLog::Error(
1881  "[PCLVis::addTextureMeshFromCCMesh] Failed to get point cloud "
1882  "from mesh!");
1883  return false;
1884  }
1885 
1886  // Use cc2smReader::getVtkPolyDataWithTextures to convert mesh to
1887  // vtkPolyData This reuses the proven logic from getPclTextureMesh and
1888  // addTextureMesh which ensures texture coordinates match point order
1889  // perfectly
1890  // Note: getVtkPolyDataWithTextures already adds DatasetName to FieldData
1891  cc2smReader reader(cloud, true);
1893  vtkSmartPointer<vtkMatrix4x4> transformation;
1894  std::vector<std::vector<Eigen::Vector2f>> tex_coordinates;
1895  if (!reader.getVtkPolyDataWithTextures(mesh, polydata, transformation,
1896  tex_coordinates)) {
1897  CVLog::Error(
1898  "[PCLVis::addTextureMeshFromCCMesh] Failed to convert mesh to "
1899  "vtkPolyData with textures!");
1900  return false;
1901  }
1902 
1903  // Create mapper and actor
1906  mapper->SetInputData(polydata);
1907 
1909  actor->SetMapper(mapper);
1910  addActorToRenderer(actor, viewport);
1911 
1912  // Apply textures using MeshTextureApplier (directly from ccMesh)
1913  // Now polydata already has correct texture coordinates from
1914  // getVtkPolyDataWithTextures
1915  vtkRenderer* renderer = getCurrentRenderer(viewport);
1917  actor, materials, tex_coordinates, polydata,
1918  texture_render_manager_.get(), renderer);
1919 
1920  if (!success) {
1921  CVLog::Error(
1922  "[PCLVis::addTextureMeshFromCCMesh] Failed to apply textures!");
1923  return false;
1924  }
1925 
1926  // Save the pointer/ID pair to the global actor map
1927  (*getCloudActorMap())[id].actor = actor;
1928  // Store smart pointer in member map to ensure lifetime extends beyond
1929  // function scope
1930  transformation_map_[id] = transformation;
1931  (*getCloudActorMap())[id].viewpoint_transformation_ = transformation.Get();
1932 
1934  "[PCLVis::addTextureMeshFromCCMesh] Successfully added mesh "
1935  "with %zu materials",
1936  materials->size());
1937  return true;
1938 }
1939 
1941  double width,
1942  double height,
1943  double depth,
1944  double r,
1945  double g,
1946  double b,
1947  const std::string& id,
1948  int viewport) {
1949  // Check to see if this ID entry already exists (has it been already added
1950  // to the visualizer?)
1951  pcl::visualization::ShapeActorMap::iterator am_it =
1952  getShapeActorMap()->find(id);
1953  if (am_it != getShapeActorMap()->end()) {
1954  CVLog::Error(
1955  "[PCLVis::addCube] A shape with id <%s> already exists!"
1956  " Please choose a different id and retry.",
1957  id.c_str());
1958  return (false);
1959  }
1960 
1962  PclTools::CreateCube(width, height, depth, trans);
1963  if (!data) {
1964  return (false);
1965  }
1966 
1967  // Create an Actor
1970  actor->GetProperty()->SetRepresentationToSurface();
1971  actor->GetProperty()->SetColor(r, g, b);
1972  addActorToRenderer(actor, viewport);
1973 
1974  // Save the pointer/ID pair to the global actor map
1975  (*getShapeActorMap())[id] = actor;
1976 
1977  return (true);
1978 }
1979 
1980 bool PCLVis::addOrientedCube(const Eigen::Vector3f& translation,
1981  const Eigen::Quaternionf& rotation,
1982  double width,
1983  double height,
1984  double depth,
1985  double r,
1986  double g,
1987  double b,
1988  const std::string& id,
1989  int viewport) {
1990  // Check to see if this ID entry already exists (has it been already added
1991  // to the visualizer?)
1992  pcl::visualization::ShapeActorMap::iterator am_it =
1993  getShapeActorMap()->find(id);
1994  if (am_it != getShapeActorMap()->end()) {
1995  CVLog::Error(
1996  "[PCLVis::addCube] A shape with id <%s> already exists!"
1997  " Please choose a different id and retry.",
1998  id.c_str());
1999  return (false);
2000  }
2001 
2002  vtkSmartPointer<vtkDataSet> data = pcl::visualization::createCube(
2003  translation, rotation, width, height, depth);
2004  if (!data) {
2005  return (false);
2006  }
2007 
2008  // Create an Actor
2011  actor->GetProperty()->SetRepresentationToSurface();
2012  actor->GetProperty()->SetColor(r, g, b);
2013  addActorToRenderer(actor, viewport);
2014 
2015  // Save the pointer/ID pair to the global actor map
2016  (*getShapeActorMap())[id] = actor;
2017 
2018  return (true);
2019 }
2020 
2022  const std::string& id,
2023  int viewport) {
2024  // Check to see if this ID entry already exists (has it been already added
2025  // to the visualizer?)
2026  pcl::visualization::ShapeActorMap::iterator am_it =
2027  getShapeActorMap()->find(id);
2028  if (am_it != getShapeActorMap()->end()) {
2029  CVLog::Error(
2030  "[PCLVis::addCube] A shape with id <%s> already exists!"
2031  " Please choose a different id and retry.",
2032  id.c_str());
2033  return (false);
2034  }
2035 
2036  std::shared_ptr<cloudViewer::geometry::LineSet> linePoints =
2039  PclTools::CreatePolyDataFromLineSet(*linePoints, false);
2040  if (!data) {
2041  return (false);
2042  }
2043 
2044  // Create an Actor
2047  actor->GetProperty()->SetRepresentationToSurface();
2048  Eigen::Vector3d color = obb.GetColor();
2049  actor->GetProperty()->SetColor(color(0), color(1), color(2));
2050  addActorToRenderer(actor, viewport);
2051 
2052  // Save the pointer/ID pair to the global actor map
2053  (*getShapeActorMap())[id] = actor;
2054 
2055  return (true);
2056 }
2057 /********************************Draw Entities*********************************/
2058 
2059 /******************************** Entities Removement
2060  * *********************************/
2061 void PCLVis::hideShowActors(bool visibility,
2062  const std::string& viewID,
2063  int viewport) {
2064  int opacity = visibility ? 1 : 0;
2065 
2066  vtkActor* actor = getActorById(viewID);
2067  if (actor) {
2068  actor->SetVisibility(opacity);
2069  actor->Modified();
2070  }
2071 }
2072 
2073 void PCLVis::hideShowWidgets(bool visibility,
2074  const std::string& viewID,
2075  int viewport) {
2076  // Extra step: check if there is a widget with the same ID
2077  WidgetActorMap::iterator wa_it = getWidgetActorMap()->find(viewID);
2078  if (wa_it == getWidgetActorMap()->end()) return;
2079 
2080  assert(wa_it->second.widget);
2081 
2082  visibility ? wa_it->second.widget->On() : wa_it->second.widget->Off();
2083 }
2084 
2086  std::string removeViewID = CVTools::FromQString(context.removeViewID);
2087 
2088  bool removeFlag = false;
2089 
2090  int viewport = context.defaultViewPort;
2091  switch (context.removeEntityType) {
2093  removePointClouds(removeViewID, viewport);
2094  removeFlag = true;
2095  break;
2096  }
2097  case ENTITY_TYPE::ECV_MESH: {
2098  removeMesh(removeViewID, viewport);
2099  removePointClouds(removeViewID, viewport);
2100  removeFlag = true;
2101  } break;
2105  case ENTITY_TYPE::ECV_SENSOR: {
2106  removeShapes(removeViewID, viewport);
2107  removePointClouds(removeViewID, viewport);
2108  removeFlag = true;
2109  } break;
2110  case ENTITY_TYPE::ECV_TEXT3D: {
2111  removeText3D(removeViewID, viewport);
2112  removeFlag = true;
2113  break;
2114  }
2115  case ENTITY_TYPE::ECV_TEXT2D: {
2116  removeText2D(removeViewID, viewport);
2117  break;
2118  }
2121  removeWidgets(removeViewID, viewport);
2122  } break;
2123  case ENTITY_TYPE::ECV_ALL: {
2124  removeALL(viewport);
2125  removeFlag = true;
2126  break;
2127  }
2128  default:
2129  break;
2130  }
2131 
2132  return removeFlag;
2133 }
2134 
2135 bool PCLVis::removeWidgets(const std::string& viewId, int viewport) {
2136  // Check to see if the given ID entry exists
2137  PclUtils::WidgetActorMap::iterator wa_it = m_widget_map->find(viewId);
2138 
2139  if (wa_it == m_widget_map->end()) return (false);
2140 
2141  // Remove it from all renderers
2142  if (wa_it->second.widget) {
2143  // Remove the pointer/ID pair to the global actor map
2144  wa_it->second.widget->Off();
2145  m_widget_map->erase(wa_it);
2146  return (true);
2147  }
2148  return (false);
2149 }
2150 
2151 void PCLVis::removePointClouds(const std::string& viewId, int viewport) {
2152  if (contains(viewId)) {
2153  removePointCloud(viewId, viewport);
2154  }
2155 
2156  // for normals case
2157  std::string normalViewId = viewId + "-normal";
2158  if (contains(normalViewId)) {
2159  removePointCloud(normalViewId, viewport);
2160  }
2161 
2162  // Remove associated Data Axes Grid
2163  RemoveDataAxesGrid(viewId);
2164 }
2165 
2166 void PCLVis::removeShapes(const std::string& viewId, int viewport) {
2167  if (contains(viewId)) {
2168  removeShape(viewId, viewport);
2169  }
2170 
2171  // Remove associated Data Axes Grid
2172  RemoveDataAxesGrid(viewId);
2173 }
2174 
2175 void PCLVis::removeMesh(const std::string& viewId, int viewport) {
2176  if (contains(viewId)) {
2177  removePolygonMesh(viewId, viewport);
2178  }
2179 
2180  // Remove associated Data Axes Grid
2181  RemoveDataAxesGrid(viewId);
2182 }
2183 
2184 void PCLVis::removeText3D(const std::string& viewId, int viewport) {
2185  if (contains(viewId)) {
2186  removeText3D(viewId, viewport);
2187  }
2188 }
2189 
2190 void PCLVis::removeText2D(const std::string& viewId, int viewport) {
2191  if (contains(viewId)) {
2192  removeShapes(viewId, viewport);
2193  }
2194 }
2195 
2196 void PCLVis::removeALL(int viewport) {
2197  removeAllPointClouds(viewport);
2198  removeAllShapes(viewport);
2199 }
2200 /******************************** Entities Removement
2201  * *********************************/
2202 
2203 /******************************** Entities Setting
2204  * *********************************/
2206  double r, double g, double b, const std::string& viewID, int viewport) {
2207  if (contains(viewID)) {
2208  setPointCloudRenderingProperties(
2209  pcl::visualization::PCL_VISUALIZER_COLOR, r, g, b, viewID,
2210  viewport);
2211  }
2212 }
2213 
2214 void PCLVis::resetScalarColor(const std::string& viewID,
2215  bool flag /* = true*/,
2216  int viewport /* = 0*/) {
2217  vtkLODActor* actor = vtkLODActor::SafeDownCast(getActorById(viewID));
2218 
2219  if (actor) {
2220  if (flag) {
2221  actor->GetMapper()->ScalarVisibilityOn();
2222  } else {
2223  actor->GetMapper()->ScalarVisibilityOff();
2224  }
2225  actor->Modified();
2226  }
2227 }
2228 
2230  float r, float g, float b, const std::string& viewID, int viewport) {
2231  if (contains(viewID)) {
2232  setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, r,
2233  g, b, viewID, viewport);
2234  }
2235 }
2236 
2237 void PCLVis::setPointSize(const unsigned char pointSize,
2238  const std::string& viewID,
2239  int viewport) {
2240  unsigned char size = pointSize > 16 ? 16 : pointSize;
2241  size = pointSize < 1 ? 1 : pointSize;
2242  if (contains(viewID)) {
2243  setPointCloudRenderingProperties(
2244  pcl::visualization::PCL_VISUALIZER_POINT_SIZE, size, viewID,
2245  viewport);
2246  }
2247 }
2248 
2249 void PCLVis::addScalarFieldToVTK(const std::string& viewID,
2250  ccPointCloud* cloud,
2251  int scalarFieldIndex,
2252  int viewport) {
2253  if (!contains(viewID) || !cloud) {
2254  return;
2255  }
2256 
2257  // Get scalar field
2258  cloudViewer::ScalarField* scalarField =
2259  cloud->getScalarField(scalarFieldIndex);
2260  if (!scalarField) {
2262  "[PCLVis::addScalarFieldToVTK] Invalid scalar field index");
2263  return;
2264  }
2265 
2266  QString sfName = cloud->getScalarFieldName(scalarFieldIndex);
2267  std::string scalarFieldName = sfName.toStdString();
2268 
2269  // Get actor from cloud actor map
2270  pcl::visualization::CloudActorMap::iterator am_it =
2271  getCloudActorMap()->find(viewID);
2272  if (am_it == getCloudActorMap()->end()) {
2273  return;
2274  }
2275 
2276  vtkActor* actor = am_it->second.actor;
2277  if (!actor) {
2278  return;
2279  }
2280 
2281  // Get mapper and polydata
2282  vtkMapper* mapper = actor->GetMapper();
2283  if (!mapper) {
2284  return;
2285  }
2286 
2287  vtkPolyData* polyData = vtkPolyData::SafeDownCast(mapper->GetInput());
2288  if (!polyData) {
2289  return;
2290  }
2291 
2292  vtkPointData* pointData = polyData->GetPointData();
2293  vtkDataArray* activeScalars = pointData->GetScalars();
2294 
2295  // Check if the correct array already exists
2296  vtkDataArray* existingArray = pointData->GetArray(scalarFieldName.c_str());
2297  if (existingArray && existingArray->GetNumberOfComponents() == 1) {
2298  // Correct 1-component array already exists, no need to update
2299  CVLog::PrintDebug(QString("[PCLVis::addScalarFieldToVTK] Scalar array "
2300  "'%1' already exists, skipping")
2301  .arg(sfName));
2302  return;
2303  }
2304 
2305  // NOTE: We no longer remove old scalar field arrays here.
2306  // This ensures all scalar fields remain available for:
2307  // 1. Selection/extraction operations (Find Data feature)
2308  // 2. Tooltip display when switching between different SFs
2309  // 3. Export operations that need to preserve all field data
2310  // The small memory overhead of keeping these arrays is acceptable.
2311 
2312  // Extract scalar values directly from ccPointCloud
2313  vtkIdType numPoints = polyData->GetNumberOfPoints();
2314  vtkSmartPointer<vtkFloatArray> scalarArray =
2316  scalarArray->SetName(scalarFieldName.c_str());
2317  scalarArray->SetNumberOfComponents(1);
2318  scalarArray->SetNumberOfTuples(numPoints);
2319 
2320  // Copy scalar values from ccPointCloud
2321  unsigned cloudSize = cloud->size();
2322  if (static_cast<vtkIdType>(cloudSize) != numPoints) {
2323  CVLog::Warning(QString("[PCLVis::addScalarFieldToVTK] Size mismatch: "
2324  "ccCloud=%1, VTK=%2")
2325  .arg(cloudSize)
2326  .arg(numPoints));
2327  return;
2328  }
2329 
2330  for (vtkIdType i = 0; i < numPoints; ++i) {
2331  float scalarValue = static_cast<float>(
2332  scalarField->getValue(static_cast<unsigned>(i)));
2333  scalarArray->SetValue(i, scalarValue);
2334  }
2335 
2336  // Add array to polydata (but do NOT set as active scalars to avoid
2337  // overriding rendering) The tooltip can access this array by name without
2338  // it being active
2339  pointData->AddArray(scalarArray);
2340  // NOTE: Do NOT call SetActiveScalars here - it would override PCL's RGB
2341  // rendering!
2342 
2343  // Add DatasetName to field data for tooltip display (ParaView style)
2344  vtkFieldData* fieldData = polyData->GetFieldData();
2345  if (fieldData) {
2346  vtkStringArray* datasetNameArray = vtkStringArray::SafeDownCast(
2347  fieldData->GetAbstractArray("DatasetName"));
2348 
2349  if (!datasetNameArray) {
2350  // DatasetName not yet added, create it
2351  datasetNameArray = vtkStringArray::New();
2352  datasetNameArray->SetName("DatasetName");
2353  datasetNameArray->SetNumberOfTuples(1);
2354 
2355  // Use cloud name as dataset name
2356  QString cloudName = cloud->getName();
2357  if (cloudName.isEmpty()) {
2358  cloudName = QString::fromStdString(viewID);
2359  }
2360  datasetNameArray->SetValue(0, cloudName.toStdString());
2361  fieldData->AddArray(datasetNameArray);
2362  datasetNameArray->Delete();
2363 
2364  CVLog::PrintDebug(QString("[PCLVis::addScalarFieldToVTK] Added "
2365  "DatasetName: %1")
2366  .arg(cloudName));
2367  }
2368 
2370  QString("[PCLVis::addScalarFieldToVTK] Added scalar array "
2371  "'%1' with %2 values to VTK polydata")
2372  .arg(sfName)
2373  .arg(numPoints));
2374  }
2375 }
2376 
2377 void PCLVis::syncAllScalarFieldsToVTK(const std::string& viewID,
2378  ccPointCloud* cloud,
2379  int viewport) {
2380  if (!contains(viewID) || !cloud) {
2381  return;
2382  }
2383 
2384  unsigned sfCount = cloud->getNumberOfScalarFields();
2385  if (sfCount == 0) {
2386  return;
2387  }
2388 
2390  QString("[PCLVis::syncAllScalarFieldsToVTK] Syncing %1 scalar "
2391  "fields from ccPointCloud to VTK")
2392  .arg(sfCount));
2393 
2394  // Sync each scalar field
2395  for (unsigned i = 0; i < sfCount; ++i) {
2396  addScalarFieldToVTK(viewID, cloud, static_cast<int>(i), viewport);
2397  }
2398 }
2399 
2400 void PCLVis::setCurrentSourceObject(ccHObject* obj, const std::string& viewID) {
2401  if (obj) {
2402  m_sourceObjectMap[viewID] = obj;
2403 
2404  CVLog::PrintDebug(QString("[PCLVis::setCurrentSourceObject] Set source "
2405  "object: '%1' (type=%2, viewID='%3')")
2406  .arg(obj->getName())
2407  .arg(obj->getClassID())
2408  .arg(QString::fromStdString(viewID)));
2409 
2410  // If it's a point cloud, automatically sync all scalar fields to VTK
2411  ccPointCloud* cloud = getSourceCloud(viewID);
2412  if (cloud) {
2413  syncAllScalarFieldsToVTK(viewID, cloud);
2414  }
2415  } else {
2416  // Remove from map if obj is null
2417  m_sourceObjectMap.erase(viewID);
2418  }
2419 }
2420 
2421 void PCLVis::removeSourceObject(const std::string& viewID) {
2422  m_sourceObjectMap.erase(viewID);
2423 }
2424 
2425 ccHObject* PCLVis::getSourceObject(const std::string& viewID) const {
2426  auto it = m_sourceObjectMap.find(viewID);
2427  if (it != m_sourceObjectMap.end()) {
2428  return it->second;
2429  }
2430  return nullptr;
2431 }
2432 
2433 ccPointCloud* PCLVis::getSourceCloud(const std::string& viewID) const {
2434  ccHObject* obj = getSourceObject(viewID);
2435  if (!obj) return nullptr;
2436 
2437  if (obj->isA(CV_TYPES::POINT_CLOUD)) {
2438  return static_cast<ccPointCloud*>(obj);
2439  }
2440  return nullptr;
2441 }
2442 
2443 ccMesh* PCLVis::getSourceMesh(const std::string& viewID) const {
2444  ccHObject* obj = getSourceObject(viewID);
2445  if (!obj) return nullptr;
2446 
2447  if (obj->isKindOf(CV_TYPES::MESH)) {
2448  return static_cast<ccMesh*>(obj);
2449  }
2450  return nullptr;
2451 }
2452 
2453 bool PCLVis::hasSourceObject(const std::string& viewID) const {
2454  return m_sourceObjectMap.find(viewID) != m_sourceObjectMap.end();
2455 }
2456 
2457 void PCLVis::setScalarFieldName(const std::string& viewID,
2458  const std::string& scalarName,
2459  int viewport) {
2460  if (!contains(viewID)) {
2461  return;
2462  }
2463 
2464  // Get actor from cloud actor map
2465  pcl::visualization::CloudActorMap::iterator am_it =
2466  getCloudActorMap()->find(viewID);
2467  if (am_it == getCloudActorMap()->end()) {
2468  return;
2469  }
2470 
2471  vtkActor* actor = am_it->second.actor;
2472  if (!actor) {
2473  return;
2474  }
2475 
2476  // Get mapper and polydata
2477  vtkMapper* mapper = actor->GetMapper();
2478  if (!mapper) {
2479  return;
2480  }
2481 
2482  vtkPolyData* polyData = vtkPolyData::SafeDownCast(mapper->GetInput());
2483  if (!polyData) {
2484  return;
2485  }
2486 
2487  vtkPointData* pointData = polyData->GetPointData();
2488 
2489  // Try to find an array with the scalar field name (actual scalar values)
2490  // This should be separate from the RGB array used for rendering
2491  vtkDataArray* scalarArray = pointData->GetArray(scalarName.c_str());
2492 
2493  if (scalarArray) {
2494  // Found the scalar array, make it the active scalars for tooltip
2495  pointData->SetActiveScalars(scalarName.c_str());
2496  CVLog::PrintDebug(QString("[PCLVis::setScalarFieldName] Set active "
2497  "scalars to '%1' (%2 components, %3 tuples)")
2498  .arg(QString::fromStdString(scalarName))
2499  .arg(scalarArray->GetNumberOfComponents())
2500  .arg(scalarArray->GetNumberOfTuples()));
2501  } else {
2502  // Scalar array not found, try to set name on default scalars as
2503  // fallback
2504  vtkDataArray* defaultScalars = pointData->GetScalars();
2505  if (defaultScalars) {
2507  QString("[PCLVis::setScalarFieldName] Scalar array '%1' "
2508  "not found, using default scalars")
2509  .arg(QString::fromStdString(scalarName)));
2510  }
2511  }
2512 }
2513 
2514 void PCLVis::setPointCloudOpacity(double opacity,
2515  const std::string& viewID,
2516  int viewport) {
2517  if (contains(viewID)) {
2518  double lastOpacity;
2519  getPointCloudRenderingProperties(
2520  pcl::visualization::RenderingProperties::PCL_VISUALIZER_OPACITY,
2521  lastOpacity, viewID);
2522  if (opacity != lastOpacity) {
2523  setPointCloudRenderingProperties(
2524  pcl::visualization::RenderingProperties::
2525  PCL_VISUALIZER_OPACITY,
2526  opacity, viewID, viewport);
2527  }
2528  }
2529 }
2530 
2531 void PCLVis::setShapeOpacity(double opacity,
2532  const std::string& viewID,
2533  int viewport) {
2534  if (contains(viewID)) {
2535  setShapeRenderingProperties(
2536  pcl::visualization::RenderingProperties::PCL_VISUALIZER_OPACITY,
2537  opacity, viewID, viewport);
2538  }
2539 }
2540 
2541 void PCLVis::setMeshOpacity(double opacity,
2542  const std::string& viewID,
2543  int viewport) {
2544  // Get the actor for this mesh - try vtkLODActor first, then vtkActor
2545  vtkLODActor* lodActor = vtkLODActor::SafeDownCast(getActorById(viewID));
2546  vtkActor* actor = lodActor;
2547  if (!actor) {
2548  // Fallback to vtkActor if not a LODActor
2549  actor = vtkActor::SafeDownCast(getActorById(viewID));
2550  }
2551  if (!actor) {
2552  CVLog::Warning("[PCLVis::setMeshOpacity] Mesh with id <%s> not found",
2553  viewID.c_str());
2554  return;
2555  }
2556 
2557  // Check current opacity to avoid unnecessary updates
2558  double currentOpacity = actor->GetProperty()->GetOpacity();
2559  if (std::abs(currentOpacity - opacity) < 0.001) {
2560  return; // No change needed
2561  }
2562 
2563  // Set the opacity on the actor's property
2564  // VTK automatically detects opacity < 1.0 in
2565  // HasTranslucentPolygonalGeometry()
2566  actor->GetProperty()->SetOpacity(opacity);
2567 
2568  // Configure transparency rendering based on opacity value
2569  // Following ParaView's vtkPVLODActor pattern (line 107-108)
2570  if (opacity < 1.0) {
2571  // Force actor to be treated as translucent for proper depth sorting
2572  // This ensures VTK renders this actor in the translucent pass
2573  actor->ForceTranslucentOn();
2574  actor->ForceOpaqueOff();
2575 
2576  // Configure renderer for transparency support (only once)
2577  vtkRenderer* renderer = getCurrentRenderer();
2578  if (renderer && !renderer->GetUseDepthPeeling()) {
2579  vtkRenderWindow* renderWindow = renderer->GetRenderWindow();
2580  if (renderWindow) {
2581  // Enable alpha bit planes for RGBA transparency
2582  renderWindow->SetAlphaBitPlanes(1);
2583  }
2584 
2585  // Enable depth peeling for correct transparent object ordering
2586  // This is the standard VTK technique for order-independent
2587  // transparency
2588  renderer->SetUseDepthPeeling(1);
2589  // Quality/performance balance
2590  renderer->SetMaximumNumberOfPeels(4);
2591  // Full transparency support
2592  renderer->SetOcclusionRatio(0.0);
2593  }
2594  } else {
2595  // Opacity is 1.0 (fully opaque), render in opaque pass
2596  actor->ForceTranslucentOff();
2597  actor->ForceOpaqueOn();
2598  }
2599 
2600  // Mark the actor as modified to trigger re-render
2601  actor->Modified();
2602 
2603  CVLog::PrintVerbose("[PCLVis::setMeshOpacity] Set opacity to %.3f for <%s>",
2604  opacity, viewID.c_str());
2605 }
2606 
2608  const std::string& viewID,
2609  int viewport) {
2610  if (contains(viewID)) {
2611  setShapeRenderingProperties(
2612  pcl::visualization::RenderingProperties::PCL_VISUALIZER_SHADING,
2613  mode, viewID, viewport);
2614  }
2615 }
2616 
2618  const std::string& viewID,
2619  int viewport) {
2620  vtkActor* actor = getActorById(viewID);
2621  if (!actor) {
2623  "[PCLVis::SetMeshRenderingMode] Requested viewID not found, "
2624  "please check again...");
2625  return;
2626  }
2627 
2628  switch (mode) {
2630  actor->GetProperty()->SetInterpolationToFlat();
2631  break;
2632  }
2634  if (!actor->GetMapper()->GetInput()->GetPointData()->GetNormals()) {
2636  "[PCLVis::setMeshShadingMode] Normals do not exist in "
2637  "the dataset, but Gouraud shading was requested. "
2638  "Estimating normals...");
2641  normals->SetInputConnection(actor->GetMapper()
2642  ->GetInputAlgorithm()
2643  ->GetOutputPort());
2644  vtkDataSetMapper::SafeDownCast(actor->GetMapper())
2645  ->SetInputConnection(normals->GetOutputPort());
2646  }
2647  actor->GetProperty()->SetInterpolationToGouraud();
2648  break;
2649  }
2651  if (!actor->GetMapper()->GetInput()->GetPointData()->GetNormals()) {
2652  PCL_INFO(
2653  "[pcl::visualization::PCLVisualizer::"
2654  "setShapeRenderingProperties] Normals do not exist in "
2655  "the dataset, but Phong shading was requested. "
2656  "Estimating normals...");
2659  normals->SetInputConnection(actor->GetMapper()
2660  ->GetInputAlgorithm()
2661  ->GetOutputPort());
2662  vtkDataSetMapper::SafeDownCast(actor->GetMapper())
2663  ->SetInputConnection(normals->GetOutputPort());
2664  }
2665  actor->GetProperty()->SetInterpolationToPhong();
2666  break;
2667  }
2668  }
2669  actor->Modified();
2670 }
2671 
2673  const std::string& viewID,
2674  int viewport) {
2675  vtkActor* actor = getActorById(viewID);
2676  if (!actor) {
2678  "[PCLVis::SetMeshRenderingMode] Requested viewID not found, "
2679  "please check again...");
2680  return;
2681  }
2682  switch (mode) {
2684  actor->GetProperty()->SetRepresentationToPoints();
2685  break;
2686  }
2688  actor->GetProperty()->SetRepresentationToWireframe();
2689  break;
2690  }
2692  actor->GetProperty()->SetRepresentationToSurface();
2693  break;
2694  }
2695  }
2696  actor->Modified();
2697 }
2698 
2699 void PCLVis::setLightMode(const std::string& viewID, int viewport) {
2700  vtkActor* actor = getActorById(viewID);
2701  if (actor) {
2702  // actor->GetProperty()->SetAmbient(1.0);
2703  actor->GetProperty()->SetLighting(false);
2704  actor->Modified();
2705  }
2706 }
2707 
2708 void PCLVis::setLineWidth(const unsigned char lineWidth,
2709  const std::string& viewID,
2710  int viewport) {
2711  vtkActor* actor = getActorById(viewID);
2712  if (actor) {
2713  actor->GetProperty()->SetLineWidth(float(lineWidth));
2714  actor->Modified();
2715  }
2716 }
2717 /******************************** Entities Setting
2718  * *********************************/
2719 
2720 /******************************** Camera Tools
2721  * *********************************/
2722 pcl::visualization::Camera PCLVis::getCamera(int viewport) {
2723  // get camera param in all viewport
2724  pcl::visualization::Camera camera;
2725 
2726  // Check if interactor style is PCLVisualizerInteractorStyle
2727  // If not (e.g., vtkInteractorStyleImage for 2D image viewer), get camera
2728  // params directly from vtkCamera This prevents crashes when ImageVis sets
2729  // vtkInteractorStyleImage on shared interactor
2730  vtkInteractorObserver* currentStyle =
2732  ? getRenderWindowInteractor()->GetInteractorStyle()
2733  : nullptr;
2734  pcl::visualization::PCLVisualizerInteractorStyle* pclStyle =
2735  dynamic_cast<pcl::visualization::PCLVisualizerInteractorStyle*>(
2736  currentStyle);
2737 
2738  if (pclStyle) {
2739  // Use PCL's getCameraParameters method when interactor style is correct
2740  this->getCameraParameters(camera);
2741  } else {
2742  // Fallback: Get camera parameters directly from vtkCamera
2743  // This is needed when interactor style is vtkInteractorStyleImage (for
2744  // 2D image viewer)
2745  vtkSmartPointer<vtkCamera> vtkCam = getVtkCamera(viewport);
2746  if (vtkCam) {
2747  double pos[3], focal[3], viewUp[3], clipRange[2];
2748  vtkCam->GetPosition(pos);
2749  vtkCam->GetFocalPoint(focal);
2750  vtkCam->GetViewUp(viewUp);
2751  vtkCam->GetClippingRange(clipRange);
2752 
2753  camera.pos[0] = pos[0];
2754  camera.pos[1] = pos[1];
2755  camera.pos[2] = pos[2];
2756  camera.focal[0] = focal[0];
2757  camera.focal[1] = focal[1];
2758  camera.focal[2] = focal[2];
2759  camera.view[0] = viewUp[0];
2760  camera.view[1] = viewUp[1];
2761  camera.view[2] = viewUp[2];
2762  camera.clip[0] = clipRange[0];
2763  camera.clip[1] = clipRange[1];
2764  camera.fovy = vtkCam->GetViewAngle() * M_PI /
2765  180.0; // Convert degrees to radians
2766  }
2767  }
2768 
2769  return camera;
2770 }
2771 
2773  return getCurrentRenderer()->GetActiveCamera();
2774 }
2775 
2777  int viewport /* = 0*/) {
2778  getVtkCamera(viewport)->SetModelTransformMatrix(viewMat.data());
2779 }
2780 
2781 void PCLVis::setParallelScale(double scale, int viewport) {
2782  vtkSmartPointer<vtkCamera> cam = getVtkCamera(viewport);
2783  int flag = cam->GetParallelProjection();
2784  if (flag) {
2785  cam->SetParallelScale(scale);
2786  cam->Modified();
2787  }
2788 }
2789 
2790 double PCLVis::getParallelScale(int viewport) {
2791  return this->getVtkCamera(viewport)->GetParallelScale();
2792 }
2793 
2794 void PCLVis::setOrthoProjection(int viewport) {
2795  vtkSmartPointer<vtkCamera> cam = getVtkCamera(viewport);
2796  int flag = cam->GetParallelProjection();
2797  if (!flag) {
2798  cam->SetParallelProjection(true);
2799  getCurrentRenderer()->SetActiveCamera(cam);
2800  UpdateScreen();
2801  }
2802 }
2803 
2805  vtkSmartPointer<vtkCamera> cam = getVtkCamera(viewport);
2806  int flag = cam->GetParallelProjection();
2807  if (flag) {
2808  cam->SetParallelProjection(false);
2809  getCurrentRenderer()->SetActiveCamera(cam);
2810  UpdateScreen();
2811  }
2812 }
2813 
2814 bool PCLVis::getPerspectiveState(int viewport) {
2815  return !getVtkCamera(viewport)->GetParallelProjection();
2816 }
2817 
2818 /******************************** Camera Tools
2819  * *********************************/
2820 
2821 /******************************** Util Tools *********************************/
2822 void PCLVis::setAreaPickingMode(bool state) {
2823  if (state) {
2824  if (m_currentMode == ORIENT_MODE) {
2826  // Save the point picker
2827  m_point_picker = static_cast<vtkPointPicker*>(
2828  getInteractorStyle()->GetInteractor()->GetPicker());
2829  // Switch for an area picker
2831  getInteractorStyle()->GetInteractor()->SetPicker(m_area_picker);
2832  }
2833  } else {
2834  if (m_currentMode == SELECT_MODE) {
2836  // Restore point picker
2837  getInteractorStyle()->GetInteractor()->SetPicker(m_point_picker);
2838  }
2839  }
2840 
2841  getRendererCollection()->Render();
2842  getInteractorStyle()->GetInteractor()->Render();
2843 }
2844 
2847  if (this->ThreeDInteractorStyle) {
2848  this->ThreeDInteractorStyle->toggleAreaPicking();
2849  }
2850 }
2851 
2853  getPCLInteractorStyle()->GetInteractor()->ExitCallback();
2854 }
2855 /******************************** Util Tools *********************************/
2856 
2857 /********************************MarkerAxes*********************************/
2858 void PCLVis::showPclMarkerAxes(vtkRenderWindowInteractor* interactor) {
2859  if (!interactor) return;
2860  showOrientationMarkerWidgetAxes(interactor);
2861  CVLog::PrintVerbose("Show Orientation Marker Widget Axes!");
2862 }
2863 
2865  // removeOrientationMarkerWidgetAxes();
2867  CVLog::PrintVerbose("Hide Orientation Marker Widget Axes!");
2868 }
2869 
2871  return m_axes_widget != nullptr && m_axes_widget->GetEnabled();
2872 }
2873 
2875  if (m_axes_widget) {
2876  if (m_axes_widget->GetEnabled())
2877  m_axes_widget->SetEnabled(false);
2878  else
2880  "Orientation Widget Axes was already disabled, doing "
2881  "nothing.");
2882  } else {
2884  "Attempted to delete Orientation Widget Axes which does not "
2885  "exist!");
2886  }
2887 }
2888 
2890  vtkRenderWindowInteractor* interactor) {
2891  if (!m_axes_widget) {
2893  1.5, "x", "y", "z", "R", "L", "A", "P", "I", "S");
2895  m_axes_widget->SetOutlineColor(0.9300, 0.5700, 0.1300);
2896  m_axes_widget->SetOrientationMarker(assembly);
2897  m_axes_widget->SetInteractor(interactor);
2898  m_axes_widget->SetViewport(0.8, 0.0, 1.0, 0.2);
2899  m_axes_widget->SetEnabled(true);
2900  m_axes_widget->InteractiveOff();
2901  } else {
2902  m_axes_widget->SetEnabled(true);
2903  CVLog::PrintVerbose("Show Orientation Marker Widget Axes!");
2904  }
2905 }
2906 
2908  if (m_axes_widget) {
2909  if (m_axes_widget->GetEnabled()) {
2910  m_axes_widget->SetEnabled(false);
2911  } else {
2912  m_axes_widget->SetEnabled(true);
2913  }
2914  } else {
2916  "Attempted to delete Orientation Widget Axes which does not "
2917  "exist!");
2918  }
2919 }
2920 /********************************MarkerAxes*********************************/
2921 
2922 /********************************Actor
2923  * Function*********************************/
2924 vtkRenderer* PCLVis::getCurrentRenderer(int viewport) {
2925  auto collection = getRendererCollection();
2926  if (!collection) {
2927  return nullptr;
2928  }
2929 
2930  int itemsNumber = collection->GetNumberOfItems();
2931  if (itemsNumber == 0) {
2932  return nullptr;
2933  }
2934 
2935  if (viewport == 0 || viewport + 1 > itemsNumber) {
2936  return collection->GetFirstRenderer();
2937  }
2938 
2939  collection->InitTraversal();
2940  vtkRenderer* renderer = nullptr;
2941  int i = 0;
2942  while ((renderer = collection->GetNextItem())) {
2943  if (viewport == i) {
2944  return renderer;
2945  }
2946  ++i;
2947  }
2948 
2949  return collection->GetFirstRenderer();
2950 }
2951 
2954  return getInteractorStyle();
2955 }
2956 
2957 vtkProp* PCLVis::getPropById(const std::string& viewId) {
2958  // Check to see if this ID entry already exists (has it been already added
2959  // to the visualizer?) Check to see if the given ID entry exists
2960  pcl::visualization::CloudActorMap::iterator ca_it =
2961  getCloudActorMap()->find(viewId);
2962  // Extra step: check if there is a cloud with the same ID
2963  pcl::visualization::ShapeActorMap::iterator am_it =
2964  getShapeActorMap()->find(viewId);
2965 
2966  bool shape = true;
2967  // Try to find a shape first
2968  if (am_it == getShapeActorMap()->end()) {
2969  // There is no cloud or shape with this ID, so just exit
2970  if (ca_it == getCloudActorMap()->end()) {
2971 #ifdef QT_DEBUG
2972  CVLog::Error(
2973  "[getActorById] Could not find any PointCloud or Shape "
2974  "datasets with id <%s>!",
2975  viewId.c_str());
2976 #endif
2977  return nullptr;
2978  }
2979 
2980  // Cloud found, set shape to false
2981  shape = false;
2982  }
2983 
2984  vtkProp* prop;
2985 
2986  // Remove the pointer/ID pair to the global actor map
2987  if (shape) {
2988  prop = vtkLODActor::SafeDownCast(am_it->second);
2989  if (!prop) {
2990  prop = vtkActor::SafeDownCast(am_it->second);
2991  }
2992  if (!prop) {
2993  prop = vtkPropAssembly::SafeDownCast(am_it->second);
2994  }
2995  } else {
2996  prop = vtkLODActor::SafeDownCast(ca_it->second.actor);
2997  if (!prop) {
2998  prop = vtkActor::SafeDownCast(ca_it->second.actor);
2999  }
3000  }
3001 
3002  // Get the actor pointer
3003 
3004  if (!prop) return nullptr;
3005 
3006  return prop;
3007 }
3008 
3010  const std::string& viewId) {
3013  collections->InitTraversal();
3014  vtkProp* prop = getPropById(viewId);
3015  if (prop) {
3016  prop->InitPathTraversal();
3017  prop->GetActors(collections);
3018  }
3019  return collections;
3020 }
3021 
3022 vtkActor* PCLVis::getActorById(const std::string& viewId) {
3023  // Check to see if this ID entry already exists (has it been already added
3024  // to the visualizer?)
3025  // Check to see if the given ID entry exists
3026  pcl::visualization::CloudActorMap::iterator ca_it =
3027  getCloudActorMap()->find(viewId);
3028  // Extra step: check if there is a cloud with the same ID
3029  pcl::visualization::ShapeActorMap::iterator am_it =
3030  getShapeActorMap()->find(viewId);
3031 
3032  bool shape = true;
3033  // Try to find a shape first
3034  if (am_it == getShapeActorMap()->end()) {
3035  // There is no cloud or shape with this ID, so just exit
3036  if (ca_it == getCloudActorMap()->end()) {
3037 #ifdef QT_DEBUG
3038  CVLog::Error(
3039  "[getActorById] Could not find any PointCloud or Shape "
3040  "datasets with id <%s>!",
3041  viewId.c_str());
3042 #endif
3043  return nullptr;
3044  }
3045 
3046  // Cloud found, set shape to false
3047  shape = false;
3048  }
3049 
3050  vtkActor* actor;
3051 
3052  // Remove the pointer/ID pair to the global actor map
3053  if (shape) {
3054  actor = vtkLODActor::SafeDownCast(am_it->second);
3055  if (!actor) {
3056  actor = vtkActor::SafeDownCast(am_it->second);
3057  }
3058  } else {
3059  actor = vtkLODActor::SafeDownCast(ca_it->second.actor);
3060  if (!actor) {
3061  actor = vtkActor::SafeDownCast(ca_it->second.actor);
3062  }
3063  }
3064 
3065  // Get the actor pointer
3066 
3067  if (!actor) return nullptr;
3068 
3069  return actor;
3070 }
3071 
3072 vtkAbstractWidget* PCLVis::getWidgetById(const std::string& viewId) {
3073  // Extra step: check if there is a widget with the same ID
3074  WidgetActorMap::iterator wa_it = getWidgetActorMap()->find(viewId);
3075  if (wa_it == getWidgetActorMap()->end()) return nullptr;
3076  return wa_it->second.widget;
3077 }
3078 
3079 std::string PCLVis::getIdByActor(vtkProp* actor) {
3080  // Check to see if this ID entry already exists (has it been already added
3081  // to the visualizer?) Check to see if the given ID entry exists
3082  pcl::visualization::CloudActorMap::iterator cloudIt =
3083  getCloudActorMap()->begin();
3084  // Extra step: check if there is a cloud with the same ID
3085  pcl::visualization::ShapeActorMap::iterator shapeIt =
3086  getShapeActorMap()->begin();
3087 
3088  for (; cloudIt != getCloudActorMap()->end(); cloudIt++) {
3089  vtkActor* tempActor = vtkLODActor::SafeDownCast(cloudIt->second.actor);
3090  if (tempActor && tempActor == actor) {
3091  return cloudIt->first;
3092  }
3093  }
3094 
3095  for (; shapeIt != getShapeActorMap()->end(); shapeIt++) {
3096  vtkActor* tempActor = vtkLODActor::SafeDownCast(shapeIt->second);
3097  if (tempActor && tempActor == actor) {
3098  return shapeIt->first;
3099  }
3100  }
3101 
3102  return std::string("");
3103 }
3104 
3106  int viewport) {
3107  vtkProp* actor_to_remove = vtkProp::SafeDownCast(actor);
3108 
3109  // Initialize traversal
3110  getRendererCollection()->InitTraversal();
3111  vtkRenderer* renderer = nullptr;
3112  int i = 0;
3113  while ((renderer = getRendererCollection()->GetNextItem()) != nullptr) {
3114  // Should we remove the actor from all renderers?
3115  if (viewport == 0) {
3116  renderer->RemoveActor(actor);
3117  } else if (viewport ==
3118  i) // add the actor only to the specified viewport
3119  {
3120  // Iterate over all actors in this renderer
3121  vtkPropCollection* actors = renderer->GetViewProps();
3122  actors->InitTraversal();
3123  vtkProp* current_actor = nullptr;
3124  while ((current_actor = actors->GetNextProp()) != nullptr) {
3125  if (current_actor != actor_to_remove) continue;
3126  renderer->RemoveActor(actor);
3127  // Found the correct viewport and removed the actor
3128  return (true);
3129  }
3130  }
3131  ++i;
3132  }
3133  if (viewport == 0) return (true);
3134  return (false);
3135 }
3136 
3138  int viewport) {
3139  // Add it to all renderers
3140  getRendererCollection()->InitTraversal();
3141  vtkRenderer* renderer = nullptr;
3142  int i = 0;
3143  while ((renderer = getRendererCollection()->GetNextItem()) != nullptr) {
3144  // Should we add the actor to all renderers?
3145  if (viewport == 0) {
3146  renderer->AddActor(actor);
3147  } else if (viewport ==
3148  i) // add the actor only to the specified viewport
3149  {
3150  renderer->AddActor(actor);
3151  }
3152  ++i;
3153  }
3154 }
3155 
3157  // Force render window to update after actor changes
3158  // Similar to QVTKWidgetCustom::updateScene()
3159  if (getRenderWindow()) {
3160  getRenderWindow()->Render();
3161  }
3162 }
3163 
3164 void PCLVis::setupInteractor(vtkRenderWindowInteractor* iren,
3165  vtkRenderWindow* win) {
3166  this->interactor_ = iren;
3167  pcl::visualization::PCLVisualizer::setupInteractor(iren, win);
3168 }
3169 /********************************Actor
3170  * Function*********************************/
3171 
3172 /********************************Interactor
3173  * Function*********************************/
3174 
3175 void PCLVis::registerKeyboard() {
3176  m_cloud_mutex.lock(); // for not overwriting the point m_baseCloud
3177  registerKeyboardCallback(&PCLVis::keyboardEventProcess, *this);
3178  CVLog::Print(
3179  "[annotation keyboard Event] press Delete to remove annotations");
3180  m_cloud_mutex.unlock();
3181 }
3182 
3183 void PCLVis::registerMouse() {
3184  m_cloud_mutex.lock(); // for not overwriting the point m_baseCloud
3185  registerMouseCallback(&PCLVis::mouseEventProcess, *this);
3186  CVLog::Print(
3187  "[annotation mouse Event] click left button to pick annotation");
3188  m_cloud_mutex.unlock();
3189 }
3190 
3191 void PCLVis::registerPointPicking() {
3192  m_cloud_mutex.lock(); // for not overwriting the point m_baseCloud
3193  registerPointPickingCallback(&PCLVis::pointPickingProcess, *this);
3194  CVLog::Print("[global pointPicking] SHIFT + left click to select a point!");
3195  m_cloud_mutex.unlock();
3196 }
3197 
3198 void PCLVis::registerInteractorStyle(bool useDefault) {
3199  if (useDefault) {
3200  if (this->ThreeDInteractorStyle) {
3202  manip->SetButton(1);
3203  this->ThreeDInteractorStyle->AddManipulator(manip);
3204  manip->Delete();
3205 
3207  manip2->SetButton(3);
3208  this->ThreeDInteractorStyle->AddManipulator(manip2);
3209  manip2->Delete();
3210 
3212  manip3->SetButton(2);
3213  this->ThreeDInteractorStyle->AddManipulator(manip3);
3214  manip3->Delete();
3215  } else {
3216  CVLog::Warning("register default 3D interactor styles failed!");
3217  }
3218 
3219  if (this->TwoDInteractorStyle) {
3221  manip4->SetButton(1);
3222  this->TwoDInteractorStyle->AddManipulator(manip4);
3223  manip4->Delete();
3224 
3226  manip5->SetButton(3);
3227  this->TwoDInteractorStyle->AddManipulator(manip5);
3228  manip5->Delete();
3229  } else {
3230  CVLog::Warning("register default 2D interactor styles failed!");
3231  }
3232  } else {
3233  int manipulators[9];
3234  // left button
3235  manipulators[0] = 4; // no special key -> Rotate
3236  manipulators[3] = 1; // shift key -> Pan
3237  manipulators[6] = 3; // ctrl key -> Roll(Spin)
3238  // middle button
3239  manipulators[1] = 1; // no special key -> Pan
3240  manipulators[4] = 4; // shift key -> Rotate
3241  manipulators[7] = 3; // ctrl key -> Roll(Spin)
3242  // right button
3243  manipulators[2] = 2; // no special key -> Zoom
3244  manipulators[5] = 1; // shift key -> Pan
3245  manipulators[8] = 6; // ctrl key -> Zoom to Mouse
3246  setCamera3DManipulators(manipulators);
3247 
3248  // left button
3249  manipulators[0] = 1; // no special key -> Pan
3250  manipulators[3] = 2; // shift key -> Zoom
3251  manipulators[6] = 3; // ctrl key -> Roll(Spin)
3252  // middle button
3253  manipulators[1] = 3; // no special key -> Roll(Spin)
3254  manipulators[4] = 2; // shift key -> Zoom
3255  manipulators[7] = 1; // ctrl key -> Pan
3256  // right button
3257  manipulators[2] = 2; // no special key -> Zoom
3258  manipulators[5] = 6; // shift key -> Zoom to Mouse
3259  manipulators[8] = 4; // ctrl key -> Rotate
3260  setCamera2DManipulators(manipulators);
3261  }
3262 }
3263 
3264 void PCLVis::registerAreaPicking() {
3265  m_cloud_mutex.lock(); // for not overwriting the point m_baseCloud
3266  registerAreaPickingCallback(&PCLVis::areaPickingEventProcess, *this);
3267  CVLog::Print("[global areaPicking] press A to start or ending picking!");
3268  m_cloud_mutex.unlock();
3269 }
3270 
3271 void PCLVis::pointPickingProcess(
3272  const pcl::visualization::PointPickingEvent& event, void* args) {
3273  if (!m_pointPickingEnabled) return;
3274 
3275  int idx = event.getPointIndex();
3276  if (idx == -1) return;
3277 
3278  // Because VTK/OpenGL stores data without NaN, we lose the 1-1
3279  // correspondence, so we must search for the real point
3280  CCVector3 picked_pt;
3281  event.getPoint(picked_pt.x, picked_pt.y, picked_pt.z);
3282  std::string id = pickItem(-1, -1, 3.0, 3.0);
3283  emit interactorPointPickedEvent(picked_pt, idx, id);
3284 }
3285 
3286 void PCLVis::areaPickingEventProcess(
3287  const pcl::visualization::AreaPickingEvent& event, void* args) {
3288  if (!m_areaPickingEnabled) return;
3289 
3290  m_selected_slice.clear();
3291  m_selected_slice.resize(0);
3292  event.getPointsIndices(m_selected_slice);
3293 
3294  if (m_selected_slice.empty()) return;
3295 
3297 }
3298 
3299 void PCLVis::keyboardEventProcess(
3300  const pcl::visualization::KeyboardEvent& event, void* args) {
3301  // delete annotation
3302  if (event.keyDown()) // avoid double emitting
3303  {
3304  emit interactorKeyboardEvent(event.getKeySym());
3305  }
3306 }
3307 
3308 void PCLVis::mouseEventProcess(const pcl::visualization::MouseEvent& event,
3309  void* args) {
3310  // fix some unknown black screen issues when using LeftButton
3311  // using RightButton instead of LeftButton to solve it
3312  if (event.getButton() == pcl::visualization::MouseEvent::RightButton &&
3313  event.getType() == pcl::visualization::MouseEvent::MouseButtonPress) {
3314  if (m_actorPickingEnabled) {
3315  vtkActor* pickedActor = pickActor(event.getX(), event.getY());
3316  if (pickedActor) {
3317  emit interactorPickedEvent(pickedActor);
3318  }
3319  }
3320  }
3321 }
3322 
3323 vtkActor* PCLVis::pickActor(double x, double y) {
3324  if (!m_propPicker) {
3326  }
3327 
3328  m_propPicker->Pick(x, y, 0, getRendererCollection()->GetFirstRenderer());
3329  return m_propPicker->GetActor();
3330 }
3331 
3332 std::string PCLVis::pickItem(double x0 /* = -1*/,
3333  double y0 /* = -1*/,
3334  double x1 /*= 5.0*/,
3335  double y1 /*= 5.0*/) {
3336  if (!m_area_picker) {
3338  }
3339  int* pos = getRenderWindowInteractor()->GetEventPosition();
3340 
3341  m_area_picker->AreaPick(pos[0], pos[1], pos[0] + x1, pos[1] + y1,
3342  getRendererCollection()->GetFirstRenderer());
3343  vtkActor* pickedActor = m_area_picker->GetActor();
3344  if (pickedActor) {
3345  return getIdByActor(pickedActor);
3346  } else {
3347  return std::string("-1");
3348  }
3349 }
3350 
3351 QImage PCLVis::renderToImage(int zoomFactor,
3352  bool renderOverlayItems,
3353  bool silent,
3354  int viewport) {
3355  bool coords_changed = false;
3356  bool lengend_changed = false;
3357  bool coords_shown = ecvDisplayTools::OrientationMarkerShown();
3358  bool lengend_shown = ecvDisplayTools::OverlayEntitiesAreDisplayed();
3359  if (lengend_shown) {
3361  lengend_changed = true;
3362  }
3363  if (renderOverlayItems) {
3364  if (!coords_shown) {
3366  coords_changed = true;
3367  }
3368  } else {
3369  if (coords_shown) {
3371  coords_changed = true;
3372  }
3373  }
3374 
3375  vtkSmartPointer<vtkWindowToImageFilter> windowToImageFilter =
3377  windowToImageFilter->SetInput(getRenderWindow());
3378 #if VTK_MAJOR_VERSION > 8 || VTK_MAJOR_VERSION == 8 && VTK_MINOR_VERSION >= 1
3379  windowToImageFilter->SetScale(zoomFactor); // image quality
3380 #else
3381  windowToImageFilter->SetMagnification(zoomFactor); // image quality
3382 #endif
3383  windowToImageFilter->SetInputBufferTypeToRGBA(); // also record the alpha
3384  // (transparency) channel
3385  windowToImageFilter->ReadFrontBufferOff(); // read from the back buffer
3386  windowToImageFilter->Update();
3387 
3388  vtkImageData* imageData = windowToImageFilter->GetOutput();
3389  if (!imageData) {
3390  if (!silent)
3391  CVLog::Error("[PCLVis::renderToImage] invalid vtkImageData!");
3392  return QImage();
3393  }
3394  int width = imageData->GetDimensions()[0];
3395  int height = imageData->GetDimensions()[1];
3396 
3397  QImage outputImage(width, height, QImage::Format_RGB32);
3398  QRgb* rgbPtr =
3399  reinterpret_cast<QRgb*>(outputImage.bits()) + width * (height - 1);
3400  unsigned char* colorsPtr =
3401  reinterpret_cast<unsigned char*>(imageData->GetScalarPointer());
3402  if (!colorsPtr) {
3403  if (!silent)
3404  CVLog::Error(
3405  "[PCLVis::renderToImage] invalid scalar pointer of "
3406  "vtkImageData!");
3407  return QImage();
3408  }
3409 
3410  // Loop over the vtkImageData contents.
3411  for (int row = 0; row < height; row++) {
3412  for (int col = 0; col < width; col++) {
3413  // Swap the vtkImageData RGB values with an equivalent QColor
3414  *(rgbPtr++) =
3415  QColor(colorsPtr[0], colorsPtr[1], colorsPtr[2]).rgb();
3416  colorsPtr += imageData->GetNumberOfScalarComponents();
3417  }
3418 
3419  rgbPtr -= width * 2;
3420  }
3421 
3422  if (outputImage.isNull()) {
3423  if (!silent)
3424  CVLog::Error(
3425  "[PCLVis::renderToImage] Direct screen capture failed! "
3426  "(not enough memory?)");
3427  }
3428 
3429  if (lengend_changed) {
3431  }
3432 
3433  if (coords_changed) {
3435  }
3436 
3437  return outputImage;
3438 }
3439 
3440 // ============================================================================
3441 // PBR Material Conversion Functions
3442 // ============================================================================
3443 
3444 // ============================================================================
3445 // View Properties Implementation (ParaView-compatible)
3446 // ============================================================================
3447 
3448 void PCLVis::setLightIntensity(double intensity) {
3449  // Clamp intensity to valid range (0.0-1.0), matching ParaView
3450  m_lightIntensity = std::max(0.0, std::min(1.0, intensity));
3451 
3452  // Get the renderer
3453  vtkRenderer* renderer = getCurrentRenderer();
3454  if (!renderer) {
3455  CVLog::Warning("[PCLVis] No renderer available for light control");
3456  return;
3457  }
3458 
3459  // Remove all existing lights to start fresh
3460  renderer->RemoveAllLights();
3461 
3462  // Create a new headlight with the desired intensity
3464  light->SetLightTypeToHeadlight(); // Headlight follows camera
3465  light->SetIntensity(m_lightIntensity);
3466  light->SetColor(1.0, 1.0, 1.0); // White light
3467  light->SwitchOn(); // Explicitly turn on the light
3468  renderer->AddLight(light);
3469 
3470  // Force lighting to be enabled on the renderer
3471  renderer->LightFollowCameraOn(); // Ensure light follows camera
3472 
3473  // Update all actors to use lighting (important for proper light response)
3474  // This includes both mesh actors and point cloud actors
3475  vtkActorCollection* actors = renderer->GetActors();
3476  if (actors) {
3477  actors->InitTraversal();
3478  vtkActor* actor = actors->GetNextActor();
3479  while (actor) {
3480  vtkProperty* prop = actor->GetProperty();
3481  if (prop) {
3482  // Enable lighting for this actor
3483  prop->SetLighting(true);
3484 
3485  // Distinguish between point clouds and meshes
3486  // Point clouds typically have only vertices (no cells or only
3487  // vertex cells) Meshes have polygons/triangles (cells)
3488  bool isPointCloud = false;
3489  vtkMapper* mapper = actor->GetMapper();
3490  if (mapper) {
3491  vtkPolyData* polydata =
3492  vtkPolyData::SafeDownCast(mapper->GetInput());
3493  if (polydata) {
3494  // Check if it's a point cloud: has points but no
3495  // meaningful cells (or only vertex cells, which are
3496  // used for point rendering)
3497  vtkIdType numPoints = polydata->GetNumberOfPoints();
3498  vtkIdType numCells = polydata->GetNumberOfCells();
3499  // Point clouds typically have cells only for vertex
3500  // rendering (one cell per point) Meshes have far fewer
3501  // cells (triangles/polygons) than points
3502  isPointCloud = (numCells == 0) ||
3503  (numCells >= numPoints * 0.9);
3504  }
3505  }
3506 
3507  if (isPointCloud) {
3508  // Point clouds with vertex colors: adjust ambient/diffuse
3509  // to match light intensity VTK rendering: final_color =
3510  // vertex_color * (ambient + diffuse * light_intensity +
3511  // specular) To make point clouds darker when light is low
3512  // and brighter when light is high:
3513  // - Low intensity: low ambient + low diffuse = darker
3514  // - High intensity: higher ambient + higher diffuse =
3515  // brighter We scale both ambient and diffuse proportionally
3516  // to light intensity
3517  double baseAmbient =
3518  0.1; // Minimum ambient (for very low light)
3519  double baseDiffuse = 0.3; // Minimum diffuse
3520  double ambient = baseAmbient +
3521  m_lightIntensity * 0.5; // Range: 0.1-0.6
3522  double diffuse = baseDiffuse +
3523  m_lightIntensity * 0.5; // Range: 0.3-0.8
3524  prop->SetAmbient(ambient);
3525  prop->SetDiffuse(diffuse);
3526  prop->SetSpecular(0.1); // Low specular for point clouds
3527  } else {
3528  // Meshes use standard lighting properties
3529  // These values work well for meshes with materials/textures
3530  prop->SetAmbient(0.3); // Standard ambient for meshes
3531  prop->SetDiffuse(0.7); // Standard diffuse for meshes
3532  prop->SetSpecular(
3533  0.2); // Some specular for mesh highlights
3534  }
3535  }
3536  actor = actors->GetNextActor();
3537  }
3538  }
3539 
3540  // Trigger render update
3541  vtkRenderWindow* win = getRenderWindow();
3542  if (win) {
3543  win->Render();
3544  }
3545 }
3546 
3548 
3549 // ============================================================================
3550 // Axes Grid and Camera Orientation Widget (ParaView-compatible)
3551 // ============================================================================
3552 
3553 void PCLVis::SetDataAxesGridProperties(const std::string& viewID,
3554  const AxesGridProperties& props) {
3555  vtkRenderer* renderer = getCurrentRenderer();
3556  if (!renderer) {
3557  CVLog::Warning("[PCLVis] No renderer available for Data Axes Grid");
3558  return;
3559  }
3560 
3561  // Get or create Data Axes Grid for this viewID
3562  vtkSmartPointer<vtkCubeAxesActor>& dataAxesGrid = m_dataAxesGridMap[viewID];
3563 
3564  if (!dataAxesGrid) {
3565  dataAxesGrid = vtkSmartPointer<vtkCubeAxesActor>::New();
3566 
3567  // Set camera reference
3568  dataAxesGrid->SetCamera(renderer->GetActiveCamera());
3569 
3570  // ============ ParaView Default Configuration ============
3571 
3572  // Axes visibility
3573  dataAxesGrid->SetXAxisVisibility(1);
3574  dataAxesGrid->SetYAxisVisibility(1);
3575  dataAxesGrid->SetZAxisVisibility(1);
3576 
3577  // Tick marks visibility (ParaView default: true)
3578  dataAxesGrid->SetXAxisTickVisibility(1);
3579  dataAxesGrid->SetYAxisTickVisibility(1);
3580  dataAxesGrid->SetZAxisTickVisibility(1);
3581 
3582  // Minor tick marks visibility (ParaView default: true)
3583  dataAxesGrid->SetXAxisMinorTickVisibility(1);
3584  dataAxesGrid->SetYAxisMinorTickVisibility(1);
3585  dataAxesGrid->SetZAxisMinorTickVisibility(1);
3586 
3587  // Fly mode (ParaView uses outer edges for data axes)
3588  dataAxesGrid->SetFlyModeToOuterEdges();
3589 
3590  // Text properties (ParaView defaults: Arial, white color, Title: 18pt,
3591  // Label: 14pt)
3592  for (int i = 0; i < 3; i++) {
3593  vtkTextProperty* titleProp = dataAxesGrid->GetTitleTextProperty(i);
3594  if (titleProp) {
3595  titleProp->SetColor(1.0, 1.0, 1.0); // White
3596  titleProp->SetFontFamilyToArial();
3597  titleProp->SetFontSize(18);
3598  titleProp->SetBold(0);
3599  titleProp->SetItalic(0);
3600  }
3601 
3602  vtkTextProperty* labelProp = dataAxesGrid->GetLabelTextProperty(i);
3603  if (labelProp) {
3604  labelProp->SetColor(1.0, 1.0, 1.0); // White
3605  labelProp->SetFontFamilyToArial();
3606  labelProp->SetFontSize(14);
3607  titleProp->SetBold(0);
3608  titleProp->SetItalic(0);
3609  }
3610  }
3611 
3612  // Label format (ParaView default: "{:<#6.3g}")
3613  dataAxesGrid->SetXLabelFormat("{:<#6.3g}");
3614  dataAxesGrid->SetYLabelFormat("{:<#6.3g}");
3615  dataAxesGrid->SetZLabelFormat("{:<#6.3g}");
3616 
3617  // Add to renderer
3618  renderer->AddActor(dataAxesGrid);
3619  }
3620 
3621  // ============ Apply All Properties from struct ============
3622 
3623  // 1. Visibility
3624  dataAxesGrid->SetVisibility(props.visible ? 1 : 0);
3625 
3626  // 2. Axis Titles (Qt QString → std::string)
3627  dataAxesGrid->SetXTitle(props.xTitle.toStdString().c_str());
3628  dataAxesGrid->SetYTitle(props.yTitle.toStdString().c_str());
3629  dataAxesGrid->SetZTitle(props.zTitle.toStdString().c_str());
3630 
3631  // 3. Colors for all axes (CCVector3 [0-255] → double [0.0-1.0])
3632  double colorR = props.color.x / 255.0;
3633  double colorG = props.color.y / 255.0;
3634  double colorB = props.color.z / 255.0;
3635  dataAxesGrid->GetXAxesLinesProperty()->SetColor(colorR, colorG, colorB);
3636  dataAxesGrid->GetYAxesLinesProperty()->SetColor(colorR, colorG, colorB);
3637  dataAxesGrid->GetZAxesLinesProperty()->SetColor(colorR, colorG, colorB);
3638 
3639  // 4. Line width
3640  dataAxesGrid->GetXAxesLinesProperty()->SetLineWidth(props.lineWidth);
3641  dataAxesGrid->GetYAxesLinesProperty()->SetLineWidth(props.lineWidth);
3642  dataAxesGrid->GetZAxesLinesProperty()->SetLineWidth(props.lineWidth);
3643 
3644  // 5. Opacity
3645  dataAxesGrid->GetXAxesLinesProperty()->SetOpacity(props.opacity);
3646  dataAxesGrid->GetYAxesLinesProperty()->SetOpacity(props.opacity);
3647  dataAxesGrid->GetZAxesLinesProperty()->SetOpacity(props.opacity);
3648 
3649  // 6. Labels visibility
3650  if (props.showLabels) {
3651  dataAxesGrid->XAxisLabelVisibilityOn();
3652  dataAxesGrid->YAxisLabelVisibilityOn();
3653  dataAxesGrid->ZAxisLabelVisibilityOn();
3654  } else {
3655  dataAxesGrid->XAxisLabelVisibilityOff();
3656  dataAxesGrid->YAxisLabelVisibilityOff();
3657  dataAxesGrid->ZAxisLabelVisibilityOff();
3658  }
3659 
3660  // 7. Grid lines visibility (ParaView-style)
3661  if (props.showGrid) {
3662  dataAxesGrid->DrawXGridlinesOn();
3663  dataAxesGrid->DrawYGridlinesOn();
3664  dataAxesGrid->DrawZGridlinesOn();
3665  } else {
3666  dataAxesGrid->DrawXGridlinesOff();
3667  dataAxesGrid->DrawYGridlinesOff();
3668  dataAxesGrid->DrawZGridlinesOff();
3669  dataAxesGrid->DrawXInnerGridlinesOff();
3670  dataAxesGrid->DrawYInnerGridlinesOff();
3671  dataAxesGrid->DrawZInnerGridlinesOff();
3672  }
3673 
3674  // 8. Custom axis labels (ParaView-style, QList<QPair<double, QString>> →
3675  // vtkStringArray)
3676  if (props.xUseCustomLabels && !props.xCustomLabels.isEmpty()) {
3677  vtkSmartPointer<vtkStringArray> xLabelsArray =
3679  for (const auto& label : props.xCustomLabels) {
3680  xLabelsArray->InsertNextValue(label.second.toStdString().c_str());
3681  }
3682  dataAxesGrid->SetAxisLabels(0, xLabelsArray); // 0 = X axis
3683  } else {
3684  dataAxesGrid->SetAxisLabels(
3685  0, nullptr); // Use default auto-generated labels
3686  }
3687 
3688  if (props.yUseCustomLabels && !props.yCustomLabels.isEmpty()) {
3689  vtkSmartPointer<vtkStringArray> yLabelsArray =
3691  for (const auto& label : props.yCustomLabels) {
3692  yLabelsArray->InsertNextValue(label.second.toStdString().c_str());
3693  }
3694  dataAxesGrid->SetAxisLabels(1, yLabelsArray); // 1 = Y axis
3695  } else {
3696  dataAxesGrid->SetAxisLabels(1, nullptr);
3697  }
3698 
3699  if (props.zUseCustomLabels && !props.zCustomLabels.isEmpty()) {
3700  vtkSmartPointer<vtkStringArray> zLabelsArray =
3702  for (const auto& label : props.zCustomLabels) {
3703  zLabelsArray->InsertNextValue(label.second.toStdString().c_str());
3704  }
3705  dataAxesGrid->SetAxisLabels(2, zLabelsArray); // 2 = Z axis
3706  } else {
3707  dataAxesGrid->SetAxisLabels(2, nullptr);
3708  }
3709 
3710  // 9. Bounds (custom or from actor or from ccHObject)
3711  if (props.useCustomBounds) {
3712  dataAxesGrid->SetBounds(props.xMin, props.xMax, props.yMin, props.yMax,
3713  props.zMin, props.zMax);
3714  } else {
3715  // Get bounds from the specific actor associated with this viewID
3716  vtkActor* actor = getActorById(viewID);
3717  if (actor) {
3718  double bounds[6];
3719  actor->GetBounds(bounds);
3720  if (bounds[1] > bounds[0] && bounds[3] > bounds[2] &&
3721  bounds[5] > bounds[4]) {
3722  dataAxesGrid->SetBounds(bounds);
3723  } else {
3725  "[PCLVis] Invalid bounds for Data Axes Grid, axes may "
3726  "not display correctly");
3727  }
3728  } else {
3729  // If no actor found, try to get bounds from ccHObject
3730  // This is especially useful for parent nodes/folders that contain
3731  // multiple children
3732  ccHObject* obj = getSourceObject(viewID);
3733 
3734  // If not in source object map, try to find it in the scene DB
3735  // This handles parent nodes/folders that aren't registered in
3736  // m_sourceObjectMap
3737  if (!obj) {
3738  ccHObject* sceneRoot = ecvDisplayTools::GetSceneDB();
3739  if (sceneRoot) {
3740  QString viewIDStr = QString::fromStdString(viewID);
3741  // Recursively search for object with matching viewID
3742  std::function<ccHObject*(ccHObject*)> findByViewID =
3743  [&findByViewID,
3744  &viewIDStr](ccHObject* node) -> ccHObject* {
3745  if (!node) return nullptr;
3746  if (node->getViewId() == viewIDStr) {
3747  return node;
3748  }
3749  for (unsigned i = 0; i < node->getChildrenNumber();
3750  ++i) {
3751  ccHObject* found = findByViewID(node->getChild(i));
3752  if (found) return found;
3753  }
3754  return nullptr;
3755  };
3756  obj = findByViewID(sceneRoot);
3757  }
3758  }
3759 
3760  if (obj) {
3761  // Calculate overall bbox including all children recursively
3762  ccBBox overallBBox = obj->getDisplayBB_recursive(false);
3763  if (overallBBox.isValid()) {
3764  CCVector3 minCorner = overallBBox.minCorner();
3765  CCVector3 maxCorner = overallBBox.maxCorner();
3766  double bounds[6] = {minCorner.x, maxCorner.x, minCorner.y,
3767  maxCorner.y, minCorner.z, maxCorner.z};
3768  if (bounds[1] > bounds[0] && bounds[3] > bounds[2] &&
3769  bounds[5] > bounds[4]) {
3770  dataAxesGrid->SetBounds(bounds);
3772  QString("[PCLVis] Set axes grid bounds from "
3773  "ccHObject '%1' (viewID: %2): "
3774  "[%.2f, %.2f] x [%.2f, %.2f] x [%.2f, "
3775  "%.2f]")
3776  .arg(obj->getName())
3777  .arg(QString::fromStdString(viewID))
3778  .arg(bounds[0])
3779  .arg(bounds[1])
3780  .arg(bounds[2])
3781  .arg(bounds[3])
3782  .arg(bounds[4])
3783  .arg(bounds[5]));
3784  } else {
3786  "[PCLVis] Invalid bounds calculated from "
3787  "ccHObject for Data Axes Grid");
3788  }
3789  } else {
3791  QString("[PCLVis] Invalid bbox for viewID: %1, "
3792  "axes grid bounds not set")
3793  .arg(QString::fromStdString(viewID)));
3794  }
3795  } else {
3796  CVLog::Warning(QString("[PCLVis] No actor or source object "
3797  "found for viewID: %1, axes grid bounds "
3798  "not set")
3799  .arg(QString::fromStdString(viewID)));
3800  }
3801  }
3802  }
3803 
3804  // Trigger update
3805  vtkRenderWindow* win = getRenderWindow();
3806  if (win) {
3807  win->Render();
3808  }
3809 }
3810 
3811 void PCLVis::GetDataAxesGridProperties(const std::string& viewID,
3812  AxesGridProperties& props) const {
3813  auto it = m_dataAxesGridMap.find(viewID);
3814  if (it == m_dataAxesGridMap.end() || !it->second) {
3815  // Return default values
3816  props = AxesGridProperties();
3817  return;
3818  }
3819 
3820  const vtkSmartPointer<vtkCubeAxesActor>& dataAxesGrid = it->second;
3821 
3822  // Get all properties from VTK actor and convert to Qt types
3823  props.visible = (dataAxesGrid->GetVisibility() != 0);
3824 
3825  // Color: double [0.0-1.0] → CCVector3 [0-255]
3826  double vtkColor[3];
3827  dataAxesGrid->GetXAxesLinesProperty()->GetColor(vtkColor);
3828  props.color = CCVector3(static_cast<float>(vtkColor[0] * 255.0),
3829  static_cast<float>(vtkColor[1] * 255.0),
3830  static_cast<float>(vtkColor[2] * 255.0));
3831 
3832  props.lineWidth = dataAxesGrid->GetXAxesLinesProperty()->GetLineWidth();
3833  props.spacing =
3834  1.0; // Conceptual - not directly supported by vtkCubeAxesActor
3835  props.subdivisions = 10; // Not directly supported by vtkCubeAxesActor
3836  props.showLabels = (dataAxesGrid->GetXAxisLabelVisibility() != 0);
3837  props.opacity = dataAxesGrid->GetXAxesLinesProperty()->GetOpacity();
3838  props.showGrid = (dataAxesGrid->GetDrawXGridlines() != 0);
3839 
3840  // Titles: const char* → QString
3841  props.xTitle = QString::fromUtf8(dataAxesGrid->GetXTitle());
3842  props.yTitle = QString::fromUtf8(dataAxesGrid->GetYTitle());
3843  props.zTitle = QString::fromUtf8(dataAxesGrid->GetZTitle());
3844 
3845  // Custom labels: We can't easily retrieve them from vtkCubeAxesActor,
3846  // so we just check if custom labels are being used
3847  props.xUseCustomLabels = (dataAxesGrid->GetAxisLabels(0) != nullptr);
3848  props.yUseCustomLabels = (dataAxesGrid->GetAxisLabels(1) != nullptr);
3849  props.zUseCustomLabels = (dataAxesGrid->GetAxisLabels(2) != nullptr);
3850 
3851  // Custom bounds: We can get the bounds but can't determine if they were
3852  // custom or from actor
3853  double bounds[6];
3854  dataAxesGrid->GetBounds(bounds);
3855  props.xMin = bounds[0];
3856  props.xMax = bounds[1];
3857  props.yMin = bounds[2];
3858  props.yMax = bounds[3];
3859  props.zMin = bounds[4];
3860  props.zMax = bounds[5];
3861  props.useCustomBounds = false; // Can't determine from VTK, assume false
3862 
3863  // Note: Custom label values (xCustomLabels, yCustomLabels, zCustomLabels)
3864  // cannot be retrieved from vtkCubeAxesActor API, they remain empty in the
3865  // returned QList
3866 }
3867 
3868 // ============================================================================
3869 // Camera Orientation Widget (ParaView-compatible)
3870 // ============================================================================
3871 
3873  vtkRenderer* renderer = getCurrentRenderer();
3874  vtkRenderWindowInteractor* interactor = getRenderWindowInteractor();
3875 
3876  if (!renderer || !interactor) {
3878  "[PCLVis] No renderer or interactor available for Camera "
3879  "Orientation Widget");
3880  return;
3881  }
3882 
3883  // Create Camera Orientation Widget if it doesn't exist (ParaView-style)
3887 
3888  // Set parent renderer (the main 3D view renderer)
3889  m_cameraOrientationWidget->SetParentRenderer(renderer);
3890 
3891  // Set interactor for event handling
3892  m_cameraOrientationWidget->SetInteractor(interactor);
3893 
3894  // ParaView disables animation when using QVTKOpenGLWidget
3895  m_cameraOrientationWidget->SetAnimate(false);
3896 
3897  // Create default representation if not already created
3898  m_cameraOrientationWidget->CreateDefaultRepresentation();
3899 
3900  // Configure the default renderer (the widget's own renderer)
3901  vtkRenderer* widgetRenderer =
3902  m_cameraOrientationWidget->GetDefaultRenderer();
3903  if (widgetRenderer) {
3904  // ParaView settings: right upper corner, 20% size
3905  widgetRenderer->SetViewport(0.8, 0.8, 1.0, 1.0);
3906  widgetRenderer->SetLayer(1); // Render on top
3907  widgetRenderer->InteractiveOff();
3908  }
3909 
3910  // Configure representation
3911  auto* rep = vtkCameraOrientationRepresentation::SafeDownCast(
3912  m_cameraOrientationWidget->GetRepresentation());
3913  if (rep) {
3914  // ParaView default size
3915  rep->SetSize(80, 80);
3916 
3917  // ParaView default position: upper right
3918  rep->AnchorToUpperRight();
3919 
3920  // Set padding
3921  int padding[2] = {10, 10};
3922  rep->SetPadding(padding);
3923 
3924  // Square resize to maintain aspect ratio
3925  m_cameraOrientationWidget->SquareResize();
3926  }
3927 
3928  CVLog::PrintVerbose("[PCLVis] Camera Orientation Widget created");
3929  }
3930 
3931  // Update visibility and enabled state (ParaView behavior)
3932  auto* rep = m_cameraOrientationWidget->GetRepresentation();
3933  if (rep) {
3934  rep->SetVisibility(show);
3935 
3936  // ParaView: if we have interactor, also update enabled state
3937  if (interactor) {
3938  m_cameraOrientationWidget->SetEnabled(show ? 1 : 0);
3939  }
3940  }
3941 
3942  // Trigger update
3943  vtkRenderWindow* win = getRenderWindow();
3944  if (win) {
3945  win->Render();
3946  }
3947 
3948  CVLog::PrintDebug(QString("[PCLVis] Camera Orientation Widget: %1")
3949  .arg(show ? "ON" : "OFF"));
3950 }
3951 
3954  return false;
3955  }
3956 
3957  auto* rep = m_cameraOrientationWidget->GetRepresentation();
3958  return rep ? (rep->GetVisibility() != 0) : false;
3959 }
3960 
3961 void PCLVis::RemoveDataAxesGrid(const std::string& viewID) {
3962  auto it = m_dataAxesGridMap.find(viewID);
3963  if (it == m_dataAxesGridMap.end()) {
3964  return; // No Data Axes Grid for this viewID
3965  }
3966 
3967  vtkRenderer* renderer = getCurrentRenderer();
3968  if (renderer && it->second) {
3969  renderer->RemoveActor(it->second);
3970  }
3971 
3972  m_dataAxesGridMap.erase(it);
3973 }
3974 
3975 } // namespace PclUtils
MouseEvent event
constexpr double M_PI
Pi.
Definition: CVConst.h:19
Vector3Tpl< PointCoordinateType > CCVector3
Default 3D Vector.
Definition: CVGeom.h:798
double normal[3]
int width
int size
int height
#define ROTATE(a, i, j, k, l)
Definition: Jacobi.h:13
math::float4 color
math::float3 position
pcl::PointCloud< PointRGBNormal > PointCloudRGBNormal
Definition: PCLCloud.h:29
pcl::PointXYZRGBNormal PointRGBNormal
Definition: PCLCloud.h:28
pcl::PointCloud< PointNT > PointCloudNormal
Definition: PCLCloud.h:25
pcl::PointCloud< PointT > PointCloudT
Definition: PCLCloud.h:17
pcl::PCLPointCloud2 PCLCloud
Definition: PCLCloud.h:34
pcl::Normal NormalT
Definition: PCLCloud.h:26
pcl::PointCloud< PointRGB > PointCloudRGB
Definition: PCLCloud.h:21
pcl::TextureMesh PCLTextureMesh
Definition: PCLCloud.h:33
pcl::PointNormal PointNT
Definition: PCLCloud.h:24
pcl::PointCloud< NormalT > CloudNormal
Definition: PCLCloud.h:27
#define FROM_PCL_CLOUD
Definition: PCLConv.h:11
#define SELECT_MODE
Definition: PCLVis.cpp:125
#define ORIENT_MODE
Definition: PCLVis.cpp:124
Eigen::Matrix3d rotation
Definition: VoxelGridIO.cpp:27
Eigen::Vector3d origin
Definition: VoxelGridIO.cpp:26
static bool PrintDebug(const char *format,...)
Same as Print, but works only in Debug mode.
Definition: CVLog.cpp:153
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
Definition: CVLog.cpp:133
static bool Print(const char *format,...)
Prints out a formatted message in console.
Definition: CVLog.cpp:113
static bool PrintVerbose(const char *format,...)
Prints out a verbose formatted message in console.
Definition: CVLog.cpp:103
static bool Error(const char *format,...)
Display an error dialog with formatted message.
Definition: CVLog.cpp:143
static std::string FromQString(const QString &qs)
Definition: CVTools.cpp:100
bool isActorPickingEnabled()
Definition: PCLVis.h:680
void setCenterOfRotation(double x, double y, double z)
Definition: PCLVis.cpp:389
void resetCenterOfRotation(int viewport=0)
Definition: PCLVis.cpp:380
vtkRenderer * getCurrentRenderer(int viewport=0)
Definition: PCLVis.cpp:2924
vtkBoundingBox GeometryBounds
Definition: PCLVis.h:630
std::map< std::string, ccHObject * > m_sourceObjectMap
Definition: PCLVis.h:794
void SetDataAxesGridProperties(const std::string &viewID, const AxesGridProperties &props)
Set Data Axes Grid properties (Unified Interface)
Definition: PCLVis.cpp:3553
bool isAreaPickingEnabled()
Definition: PCLVis.h:675
virtual void setCamera3DManipulators(const int manipulators[9])
Definition: PCLVis.cpp:423
void hideShowActors(bool visibility, const std::string &viewID, int viewport=0)
Definition: PCLVis.cpp:2061
void hideShowWidgets(bool visibility, const std::string &viewID, int viewport=0)
Definition: PCLVis.cpp:2073
vtkSmartPointer< vtkCameraOrientationWidget > m_cameraOrientationWidget
Definition: PCLVis.h:805
bool m_pointPickingEnabled
Definition: PCLVis.h:705
WidgetActorMapPtr m_widget_map
Internal list with actor pointers and name IDs for widgets.
Definition: PCLVis.h:625
void configInteractorStyle(vtkSmartPointer< VTKExtensions::vtkCustomInteractorStyle > interactor_style)
Definition: PCLVis.cpp:241
void setParallelScale(double scale, int viewport=0)
Definition: PCLVis.cpp:2781
ccMesh * getSourceMesh(const std::string &viewID) const
Get the source object as ccMesh.
Definition: PCLVis.cpp:2443
void setPointCloudUniqueColor(double r, double g, double b, const std::string &viewID, int viewport=0)
Definition: PCLVis.cpp:2205
bool getCaptionPosition(const std::string &viewID, float &posX, float &posY)
Get caption widget 2D position (normalized coordinates 0.0-1.0)
Definition: PCLVis.cpp:1317
vtkSmartPointer< vtkPropCollection > getPropCollectionById(const std::string &viewId)
Definition: PCLVis.cpp:3009
bool addTextureMesh(const PCLTextureMesh &mesh, const std::string &id, int viewport)
Add texture mesh from PCLTextureMesh (deprecated)
Definition: PCLVis.cpp:1578
void setupInteractor(vtkRenderWindowInteractor *iren, vtkRenderWindow *win)
setupInteractor override to init interactor_
Definition: PCLVis.cpp:3164
vtkSmartPointer< vtkTransform > getTransformation(const CC_DRAW_CONTEXT &context, const CCVector3d &origin)
Definition: PCLVis.cpp:1031
void setPerspectiveProjection(int viewport=0)
Definition: PCLVis.cpp:2804
bool isPointPickingEnabled()
Definition: PCLVis.h:667
void initialize()
Definition: PCLVis.cpp:254
void addScalarFieldToVTK(const std::string &viewID, ccPointCloud *cloud, int scalarFieldIndex, int viewport=0)
Add scalar field data from ccPointCloud to VTK polydata.
Definition: PCLVis.cpp:2249
vtkSmartPointer< vtkCamera > getVtkCamera(int viewport=0)
Definition: PCLVis.cpp:2772
void RemoveDataAxesGrid(const std::string &viewID)
Remove Data Axes Grid for a specific object.
Definition: PCLVis.cpp:3961
virtual void setCamera2DManipulators(const int manipulators[9])
Definition: PCLVis.cpp:418
vtkAbstractWidget * getWidgetById(const std::string &viewId)
Definition: PCLVis.cpp:3072
bool getPerspectiveState(int viewport=0)
Definition: PCLVis.cpp:2814
void synchronizeGeometryBounds(int viewport=0)
Definition: PCLVis.cpp:525
vtkSmartPointer< VTKExtensions::vtkCustomInteractorStyle > ThreeDInteractorStyle
Definition: PCLVis.h:635
std::map< std::string, vtkSmartPointer< vtkCubeAxesActor > > m_dataAxesGridMap
Definition: PCLVis.h:802
std::string getIdByActor(vtkProp *actor)
Definition: PCLVis.cpp:3079
void setLightMode(const std::string &viewID, int viewport=0)
Definition: PCLVis.cpp:2699
void setPointSize(const unsigned char pointSize, const std::string &viewID, int viewport=0)
Definition: PCLVis.cpp:2237
void setCurrentSourceObject(ccHObject *obj, const std::string &viewID)
Set the source object for selection operations.
Definition: PCLVis.cpp:2400
void showOrientationMarkerWidgetAxes(vtkRenderWindowInteractor *interactor)
Definition: PCLVis.cpp:2889
void setShapeUniqueColor(float r, float g, float b, const std::string &viewID, int viewport=0)
Definition: PCLVis.cpp:2229
void ToggleCameraOrientationWidget(bool show)
Toggle Camera Orientation Widget visibility (ParaView-style)
Definition: PCLVis.cpp:3872
bool addTextureMeshFromCCMesh(ccGenericMesh *mesh, const std::string &id, int viewport=0)
Add texture mesh directly from ccGenericMesh (preferred)
Definition: PCLVis.cpp:1837
bool updateTexture(const CC_DRAW_CONTEXT &context, const ccMaterialSet *materials)
Definition: PCLVis.cpp:1542
void setRotationFactor(double factor)
Definition: PCLVis.cpp:400
vtkSmartPointer< VTKExtensions::vtkPVCenterAxesActor > m_centerAxes
Definition: PCLVis.h:631
virtual void setCamera2DMouseWheelMotionFactor(double factor)
Definition: PCLVis.cpp:488
void hideOrientationMarkerWidgetAxes()
Definition: PCLVis.cpp:2874
pcl::visualization::Camera getCamera(int viewport=0)
Definition: PCLVis.cpp:2722
static void ExpandBounds(double bounds[6], vtkMatrix4x4 *matrix)
Definition: PCLVis.cpp:329
virtual ~PCLVis()
Definition: PCLVis.cpp:195
bool IsCameraOrientationWidgetShown() const
Check if Camera Orientation Widget is shown.
Definition: PCLVis.cpp:3952
void setLightIntensity(double intensity)
Set global light intensity (ParaView-style)
Definition: PCLVis.cpp:3448
bool addScalarBar(const CC_DRAW_CONTEXT &context)
Definition: PCLVis.cpp:1253
void configCenterAxes()
Definition: PCLVis.cpp:233
void setCameraFocalDistance(double focal_distance, int viewport=0)
Definition: PCLVis.cpp:590
vtkSmartPointer< vtkOrientationMarkerWidget > m_axes_widget
Definition: PCLVis.h:784
vtkSmartPointer< pcl::visualization::PCLVisualizerInteractorStyle > m_interactorStyle
Definition: PCLVis.h:637
bool m_areaPickingEnabled
Definition: PCLVis.h:706
void syncAllScalarFieldsToVTK(const std::string &viewID, ccPointCloud *cloud, int viewport=0)
Sync ALL scalar fields from ccPointCloud to VTK polydata.
Definition: PCLVis.cpp:2377
void setCameraViewAngle(double viewAngle, int viewport=0)
Definition: PCLVis.cpp:827
double getCameraFocalDistance(int viewport=0)
Definition: PCLVis.cpp:586
void UpdateScreen()
UpdateScreen - Updates/refreshes the render window This method forces a render update after actor cha...
Definition: PCLVis.cpp:3156
void toggleOrientationMarkerWidgetAxes()
Definition: PCLVis.cpp:2907
vtkSmartPointer< VTKExtensions::vtkCustomInteractorStyle > get3DInteractorStyle()
Definition: PCLVis.h:619
PCLVis(vtkSmartPointer< VTKExtensions::vtkCustomInteractorStyle > interactor_style, const std::string &viewerName="", bool initIterator=false, int argc=0, char **argv=nullptr)
Default constructor.
Definition: PCLVis.cpp:132
void addActorToRenderer(const vtkSmartPointer< vtkProp > &actor, int viewport=0)
Definition: PCLVis.cpp:3137
double getGLDepth(int x, int y)
Definition: PCLVis.cpp:571
void hidePclMarkerAxes()
Marker Axes.
Definition: PCLVis.cpp:2864
void expandBounds(double bounds[6], vtkMatrix4x4 *matrix)
Definition: PCLVis.cpp:776
ccHObject * getSourceObject(const std::string &viewID) const
Get the source object for a given viewID.
Definition: PCLVis.cpp:2425
vtkSmartPointer< VTKExtensions::vtkCustomInteractorStyle > TwoDInteractorStyle
Definition: PCLVis.h:633
virtual void updateCenterAxes()
Definition: PCLVis.cpp:502
void resetCamera()
Definition: PCLVis.h:263
void setAreaPickingEnabled(bool state)
Definition: PCLVis.h:676
double getParallelScale(int viewport=0)
Definition: PCLVis.cpp:2790
void toggleAreaPicking()
Definition: PCLVis.cpp:2845
void rotateWithAxis(const CCVector2i &pos, const CCVector3d &axis, double angle, int viewport=0)
Definition: PCLVis.cpp:261
bool addCaption(const std::string &text, const CCVector2 &pos2D, const CCVector3 &anchorPos, double r, double g, double b, double a, int fontSize=10, const std::string &viewID="caption", bool anchorDragable=false, int viewport=0)
Definition: PCLVis.cpp:1353
vtkSmartPointer< vtkRenderWindowInteractor > getRenderWindowInteractor()
Get a pointer to the current interactor style used.
Definition: PCLVis.h:148
void zoomCamera(double zoomFactor, int viewport=0)
Definition: PCLVis.cpp:599
void setLineWidth(const unsigned char lineWidth, const std::string &viewID, int viewport=0)
Definition: PCLVis.cpp:2708
void setPointPickingEnabled(bool state)
Definition: PCLVis.h:668
virtual void setCamera3DMouseWheelMotionFactor(double factor)
Definition: PCLVis.cpp:495
void setShapeOpacity(double opacity, const std::string &viewID, int viewport=0)
Definition: PCLVis.cpp:2531
void setScalarFieldName(const std::string &viewID, const std::string &scalarName, int viewport=0)
Definition: PCLVis.cpp:2457
void setAreaPickingMode(bool state)
Definition: PCLVis.cpp:2822
WidgetActorMapPtr getWidgetActorMap()
Return a pointer to the WidgetActorMap this visualizer uses.
Definition: PCLVis.h:606
ccPointCloud * getSourceCloud(const std::string &viewID) const
Get the source object as ccPointCloud.
Definition: PCLVis.cpp:2433
void updateNormals(const CC_DRAW_CONTEXT &context, const PCLCloud::Ptr &smCloud)
Definition: PCLVis.cpp:1074
QImage renderToImage(int zoomFactor=1, bool renderOverlayItems=false, bool silent=false, int viewport=0)
Definition: PCLVis.cpp:3351
void setShapeShadingMode(SHADING_MODE mode, const std::string &viewID, int viewport=0)
Definition: PCLVis.cpp:2607
std::vector< int > m_selected_slice
Definition: PCLVis.h:789
void setCameraManipulators(VTKExtensions::vtkCustomInteractorStyle *style, const int manipulators[9])
Definition: PCLVis.cpp:428
void transformEntities(const CC_DRAW_CONTEXT &context)
Definition: PCLVis.cpp:1011
bool updateCaption(const std::string &text, const CCVector2 &pos2D, const CCVector3 &anchorPos, double r, double g, double b, double a, int fontSize=10, const std::string &viewID="caption", int viewport=0)
Definition: PCLVis.cpp:1276
bool addPolyline(const PCLPolygon::ConstPtr pclPolygon, double r, double g, double b, float width=1.0f, const std::string &id="multiline", int viewport=0)
Definition: PCLVis.cpp:1461
double m_lightIntensity
Definition: PCLVis.h:797
vtkSmartPointer< vtkPropPicker > m_propPicker
Definition: PCLVis.h:787
vtkSmartPointer< pcl::visualization::PCLVisualizerInteractorStyle > getPCLInteractorStyle()
Definition: PCLVis.cpp:2953
void setModelViewMatrix(const ccGLMatrixd &viewMat, int viewport=0)
Definition: PCLVis.cpp:2776
void getReasonableClippingRange(double range[2], int viewport=0)
Definition: PCLVis.cpp:664
bool addOrientedCube(const ccGLMatrixd &trans, double width, double height, double depth, double r=1.0, double g=1.0, double b=1.0, const std::string &id="cube", int viewport=0)
Definition: PCLVis.cpp:1940
void removeSourceObject(const std::string &viewID)
Remove a source object from the map.
Definition: PCLVis.cpp:2421
bool updateScalarBar(const CC_DRAW_CONTEXT &context)
Definition: PCLVis.cpp:1241
void showPclMarkerAxes(vtkRenderWindowInteractor *interactor=nullptr)
Definition: PCLVis.cpp:2858
bool removeEntities(const CC_DRAW_CONTEXT &context)
Definition: PCLVis.cpp:2085
bool removeActorFromRenderer(const vtkSmartPointer< vtkProp > &actor, int viewport=0)
Internal method. Adds a vtk actor to screen.
Definition: PCLVis.cpp:3105
void setPointCloudOpacity(double opacity, const std::string &viewID, int viewport=0)
Definition: PCLVis.cpp:2514
std::mutex m_cloud_mutex
Definition: PCLVis.h:711
vtkSmartPointer< vtkPointPicker > m_point_picker
Definition: PCLVis.h:785
bool containWidget(const std::string &id) const
Check if the widgets or props with the given id was already added to this visualizer.
Definition: PCLVis.h:600
void getModelViewTransformMatrix(Eigen::Matrix4d &view)
Definition: PCLVis.cpp:615
void setActorPickingEnabled(bool state)
Definition: PCLVis.h:681
bool hasSourceObject(const std::string &viewID) const
Check if a source object exists for the given viewID.
Definition: PCLVis.cpp:2453
double getRotationFactor()
Definition: PCLVis.cpp:409
vtkActor * getActorById(const std::string &viewId)
Definition: PCLVis.cpp:3022
void resetScalarColor(const std::string &viewID, bool flag=true, int viewport=0)
Definition: PCLVis.cpp:2214
void setOrthoProjection(int viewport=0)
Definition: PCLVis.cpp:2794
std::string pickItem(double x0=-1, double y0=-1, double x1=5.0, double y1=5.0)
Definition: PCLVis.cpp:3332
void setMeshShadingMode(SHADING_MODE mode, const std::string &viewID, int viewport=0)
Definition: PCLVis.cpp:2617
double getLightIntensity() const
Get current global light intensity.
Definition: PCLVis.cpp:3547
vtkProp * getPropById(const std::string &viewId)
Definition: PCLVis.cpp:2957
void resetCameraClippingRange(int viewport=0)
Definition: PCLVis.cpp:628
void displayText(const CC_DRAW_CONTEXT &context)
Definition: PCLVis.cpp:1509
void exitCallbackProcess()
Definition: PCLVis.cpp:2852
bool pclMarkerAxesShown()
Definition: PCLVis.cpp:2870
void interactorPickedEvent(vtkActor *actor)
int m_currentMode
Definition: PCLVis.h:704
void setMeshRenderingMode(MESH_RENDERING_MODE mode, const std::string &viewID, int viewport=0)
Definition: PCLVis.cpp:2672
bool m_actorPickingEnabled
Definition: PCLVis.h:707
vtkActor * pickActor(double x, double y)
Definition: PCLVis.cpp:3323
void setCenterAxesVisibility(bool)
Definition: PCLVis.cpp:565
vtkSmartPointer< vtkAreaPicker > m_area_picker
Definition: PCLVis.h:786
void GetDataAxesGridProperties(const std::string &viewID, AxesGridProperties &props) const
Get Data Axes Grid properties (Unified Interface)
Definition: PCLVis.cpp:3811
void draw(const CC_DRAW_CONTEXT &context, const PCLCloud::Ptr &smCloud)
Definition: PCLVis.cpp:846
void updateShadingMode(const CC_DRAW_CONTEXT &context, PCLCloud &smCloud)
Definition: PCLVis.cpp:1110
void setMeshOpacity(double opacity, const std::string &viewID, int viewport=0)
Set opacity for mesh (textured or non-textured)
Definition: PCLVis.cpp:2541
void getCenterOfRotation(double center[3])
Definition: PCLVis.cpp:323
void getProjectionTransformMatrix(Eigen::Matrix4d &proj)
Definition: PCLVis.cpp:608
static bool ApplyPBRTextures(vtkLODActor *actor, const pcl::TextureMesh &mesh, vtkPolyData *polydata, TextureRenderManager *render_manager, vtkRenderer *renderer)
Apply PBR textures from PCLTextureMesh (deprecated)
static bool ApplyTexturesFromMaterialSet(vtkLODActor *actor, const ccMaterialSet *materials, const std::vector< std::vector< Eigen::Vector2f >> &tex_coordinates, vtkPolyData *polydata, TextureRenderManager *render_manager, vtkRenderer *renderer)
Apply textures from ccMaterialSet with texture coordinates.
Type y
Definition: CVGeom.h:137
Type u[3]
Definition: CVGeom.h:139
Type x
Definition: CVGeom.h:137
Type z
Definition: CVGeom.h:137
vtkCustomInteractorStyle defines an unique, custom VTK based interactory style for PCL Visualizer app...
static vtkPVCenterAxesActor * New()
2D Vector
Definition: CVGeom.h:32
Type x
Definition: CVGeom.h:36
Type y
Definition: CVGeom.h:36
static Vector3Tpl fromArray(const int a[3])
Constructor from an int array.
Definition: CVGeom.h:268
2D label (typically attached to points)
Definition: ecv2DLabel.h:22
CC to PCL cloud converter.
Definition: cc2sm.h:43
bool getVtkPolyDataWithTextures(ccGenericMesh *mesh, vtkSmartPointer< vtkPolyData > &polydata, vtkSmartPointer< vtkMatrix4x4 > &transformation, std::vector< std::vector< Eigen::Vector2f >> &tex_coordinates)
Convert ccGenericMesh to vtkPolyData with texture coordinates Reuses getPclTextureMesh logic to ensur...
Definition: cc2sm.cpp:1566
Bounding box structure.
Definition: ecvBBox.h:25
Camera (projective) sensor.
Ground-based Laser sensor.
Definition: ecvGBLSensor.h:26
T * data()
Returns a pointer to internal data.
Double version of ccGLMatrixTpl.
Definition: ecvGLMatrix.h:56
Generic mesh interface.
virtual const ccMaterialSet * getMaterialSet() const =0
virtual ccGenericPointCloud * getAssociatedCloud() const =0
Returns the vertices cloud.
static cc2DLabel * To2DLabel(ccHObject *obj)
Converts current object to cc2DLabel (if possible)
static ccPointCloud * ToPointCloud(ccHObject *obj, bool *isLockedVertices=nullptr)
Converts current object to 'equivalent' ccPointCloud.
Hierarchical CLOUDVIEWER Object.
Definition: ecvHObject.h:25
QString getViewId() const
Definition: ecvHObject.h:225
unsigned getChildrenNumber() const
Returns the number of children.
Definition: ecvHObject.h:312
virtual ccBBox getDisplayBB_recursive(bool relative)
Returns the bounding-box of this entity and it's children WHEN DISPLAYED.
Definition: ecvHObject.cpp:817
ccHObject * getChild(unsigned childPos) const
Returns the ith child.
Definition: ecvHObject.h:325
CV_CLASS_ENUM getClassID() const override
Returns class ID.
Definition: ecvHObject.h:232
Mesh (triangle) material.
Triangular mesh.
Definition: ecvMesh.h:35
virtual QString getName() const
Returns object name.
Definition: ecvObject.h:72
bool isA(CV_CLASS_ENUM type) const
Definition: ecvObject.h:131
bool isKindOf(CV_CLASS_ENUM type) const
Definition: ecvObject.h:128
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
Generic sensor interface.
Definition: ecvSensor.h:27
const Vector3Tpl< T > & maxCorner() const
Returns max corner (const)
Definition: BoundingBox.h:156
void getBounds(double bounds[6]) const
Definition: BoundingBox.h:283
const Vector3Tpl< T > & minCorner() const
Returns min corner (const)
Definition: BoundingBox.h:154
bool isValid() const
Returns whether bounding box is valid or not.
Definition: BoundingBox.h:203
const Eigen::Vector3d & GetColor() const
Gets the bounding box color.
ScalarField * getScalarField(int index) const
Returns a pointer to a specific scalar field.
unsigned getNumberOfScalarFields() const
Returns the number of associated (and active) scalar fields.
unsigned size() const override
Definition: PointCloudTpl.h:38
const char * getScalarFieldName(int index) const
Returns the name of a specific scalar field.
A simple scalar field (to be associated to a point cloud)
Definition: ScalarField.h:25
ScalarType & getValue(std::size_t index)
Definition: ScalarField.h:92
LineSet define a sets of lines in 3D. A typical application is to display the point cloud corresponde...
Definition: LineSet.h:29
static std::shared_ptr< LineSet > CreateFromOrientedBoundingBox(const ecvOrientedBBox &box)
Factory function to create a LineSet from an OrientedBoundingBox.
RGB color structure.
Definition: ecvColorTypes.h:49
static Eigen::Vector3d ToEigen(const Type col[3])
Definition: ecvColorTypes.h:72
static void DisplayOverlayEntities(bool state)
static void ToggleOrientationMarker(bool state=true)
static ccHObject * GetSceneDB()
static bool OrientationMarkerShown()
static bool OverlayEntitiesAreDisplayed()
void interactorPointPickedEvent(const CCVector3 &p, int index, const std::string &id)
void interactorAreaPickedEvent(const std::vector< int > &selected_slice)
void interactorKeyboardEvent(const std::string &symKey)
static vtkPVTrackballRotate * New()
static vtkPVTrackballZoom * New()
static vtkTrackballPan * New()
double colors[3]
double normals[3]
MESH_RENDERING_MODE
@ ECV_POINTS_MODE
@ ECV_SURFACE_MODE
@ ECV_WIREFRAME_MODE
SHADING_MODE
@ ECV_SHADING_PHONG
@ ECV_SHADING_FLAT
@ ECV_SHADING_GOURAUD
@ ECV_SENSOR
@ ECV_TEXT3D
@ ECV_POLYLINE_2D
@ ECV_CAPTION
@ ECV_LINES_3D
@ ECV_TEXT2D
@ ECV_MESH
@ ECV_ALL
@ ECV_SHAPE
@ ECV_SCALAR_BAR
@ ECV_POINT_CLOUD
a[190]
const double * e
GraphType data
Definition: graph_cut.cc:138
ImGuiContext * context
Definition: Window.cpp:76
normal_z y
normal_z x
normal_z z
@ MESH
Definition: CVTypes.h:105
@ GBL_SENSOR
Definition: CVTypes.h:117
@ POINT_CLOUD
Definition: CVTypes.h:104
@ LABEL_2D
Definition: CVTypes.h:140
@ CAMERA_SENSOR
Definition: CVTypes.h:118
vtkSmartPointer< vtkPolyData > CreatePolyDataFromLineSet(const cloudViewer::geometry::LineSet &lineset, bool useLineSource=true)
Definition: PclTools.cpp:1124
vtkSmartPointer< vtkPolyData > CreateLine(vtkSmartPointer< vtkPoints > points)
Definition: PclTools.cpp:316
vtkSmartPointer< vtkPropAssembly > CreateCoordinate(double axesLength=1.5, const std::string &xLabel="x", const std::string &yLabel="y", const std::string &zLabel="z", const std::string &xPlus="R", const std::string &xMinus="L", const std::string &yPlus="A", const std::string &yMinus="P", const std::string &zPlus="I", const std::string &zMinus="S")
Definition: PclTools.cpp:581
vtkSmartPointer< vtkPolyData > CreateCameraSensor(const ccCameraSensor *cameraSensor, const ecvColor::Rgb &lineColor, const ecvColor::Rgb &planeColor)
Definition: PclTools.cpp:464
bool UpdateScalarBar(vtkAbstractWidget *widget, const CC_DRAW_CONTEXT &CONTEXT)
Definition: PclTools.cpp:719
vtkSmartPointer< vtkPolyData > CreateGBLSensor(const ccGBLSensor *gBLSensor)
Definition: PclTools.cpp:436
vtkSmartPointer< vtkPolyData > CreateCube(double width, double height, double depth, const ccGLMatrixd &trans)
Definition: PclTools.cpp:1101
void CreateActorFromVTKDataSet(const vtkSmartPointer< vtkDataSet > &data, vtkSmartPointer< vtkActor > &actor, bool use_scalars=true, bool use_vbos=false)
Internal method. Creates a vtk actor from a vtk polydata object.
Definition: PclTools.cpp:166
std::unordered_map< std::string, WidgetMap > WidgetActorMap
Definition: WidgetMap.h:36
std::unordered_map< std::string, vtkSmartPointer< vtkProp > > PropActorMap
Definition: WidgetMap.h:39
void vtkColor(const QColor &clr, double *vtkClr)
Definition: utils.cpp:60
constexpr Rgb white(MAX, MAX, MAX)
constexpr Rgbaf light(0.66f, 0.66f, 0.66f, 1.00f)
constexpr Rgb red(MAX, 0, 0)
Rgbf FromRgb(const Rgb &color)
static ecvColor::Rgbf TransFormRGB(const ecvColor::Rgb &col)
Definition: ecvTools.h:53
Definition: Eigen.h:85
ccScalarField * normalScale
Definition: qM3C2Tools.cpp:44
Data Axes Grid properties structure Encapsulates all properties for vtkCubeAxesActor configuration.
QList< QPair< double, QString > > xCustomLabels
QList< QPair< double, QString > > yCustomLabels
QList< QPair< double, QString > > zCustomLabels
CCVector3 transVecStart
RotateParam rotateParam
CCVector3d eulerZYX
CCVector3 transVecEnd
CCVector3 scaleXYZ
the x y z scale
Display context.
CCVector3d textPos