38 #ifndef PCL_PCL_VISUALIZER_IMPL_H_
39 #define PCL_PCL_VISUALIZER_IMPL_H_
41 #include <vtkVersion.h>
42 #include <vtkSmartPointer.h>
43 #include <vtkCellArray.h>
44 #include <vtkLeaderActor2D.h>
45 #include <vtkVectorText.h>
46 #include <vtkAlgorithmOutput.h>
47 #include <vtkFollower.h>
49 #include <vtkSphereSource.h>
50 #include <vtkProperty2D.h>
51 #include <vtkDataSetSurfaceFilter.h>
52 #include <vtkPointData.h>
53 #include <vtkPolyDataMapper.h>
54 #include <vtkProperty.h>
55 #include <vtkMapper.h>
56 #include <vtkCellData.h>
57 #include <vtkDataSetMapper.h>
58 #include <vtkRenderer.h>
59 #include <vtkRendererCollection.h>
60 #include <vtkAppendPolyData.h>
61 #include <vtkTextProperty.h>
62 #include <vtkLODActor.h>
63 #include <vtkLineSource.h>
65 #include <pcl/common/utils.h>
69 #ifdef vtkGenericDataArray_h
70 #define SetTupleValue SetTypedTuple
71 #define InsertNextTupleValue InsertNextTypedTuple
72 #define GetTupleValue GetTypedTuple
76 template <
typename Po
intT>
bool
79 const std::string &
id,
int viewport)
83 return (addPointCloud<PointT> (cloud, geometry_handler,
id, viewport));
87 template <
typename Po
intT>
bool
91 const std::string &
id,
int viewport)
95 PCL_WARN (
"[addPointCloud] The id <%s> already exists! Please choose a different id and retry.\n",
id.c_str ());
99 if (pcl::traits::has_color<PointT>())
109 template <
typename Po
intT>
bool
113 const std::string &
id,
int viewport)
119 auto am_it = cloud_actor_map_->find (
id);
120 am_it->second.geometry_handlers.push_back (geometry_handler);
130 template <
typename Po
intT>
bool
134 const std::string &
id,
int viewport)
138 PCL_WARN (
"[addPointCloud] The id <%s> already exists! Please choose a different id and retry.\n",
id.c_str ());
152 template <
typename Po
intT>
bool
156 const std::string &
id,
int viewport)
159 auto am_it = cloud_actor_map_->find (
id);
160 if (am_it != cloud_actor_map_->end ())
164 am_it->second.color_handlers.push_back (color_handler);
173 template <
typename Po
intT>
bool
178 const std::string &
id,
int viewport)
181 auto am_it = cloud_actor_map_->find (
id);
182 if (am_it != cloud_actor_map_->end ())
186 am_it->second.geometry_handlers.push_back (geometry_handler);
187 am_it->second.color_handlers.push_back (color_handler);
194 template <
typename Po
intT>
bool
199 const std::string &
id,
int viewport)
203 PCL_WARN (
"[addPointCloud] The id <%s> already exists! Please choose a different id and retry.\n",
id.c_str ());
215 template <
typename Po
intT>
void
216 pcl::visualization::PCLVisualizer::convertPointCloudToVTKPolyData (
224 allocVtkPolyData (polydata);
226 polydata->SetVerts (vertices);
230 vertices = polydata->GetVerts ();
234 vtkIdType nr_points = cloud->
size ();
240 points->SetDataTypeToFloat ();
241 polydata->SetPoints (points);
243 points->SetNumberOfPoints (nr_points);
246 float *data = (
dynamic_cast<vtkFloatArray*
> (points->GetData ()))->GetPointer (0);
252 for (vtkIdType i = 0; i < nr_points; ++i, ptr += 3) {
253 std::copy(&(*cloud)[i].x, &(*cloud)[i].x + 3, &data[ptr]);
259 for (vtkIdType i = 0; i < nr_points; ++i)
262 if (!std::isfinite ((*cloud)[i].x) ||
263 !std::isfinite ((*cloud)[i].y) ||
264 !std::isfinite ((*cloud)[i].z))
267 std::copy (&(*cloud)[i].x, &(*cloud)[i].x + 3, &data[ptr]);
272 points->SetNumberOfPoints (nr_points);
275 #ifdef VTK_CELL_ARRAY_V2
279 auto numOfCells = vertices->GetNumberOfCells();
282 if (numOfCells < nr_points)
284 for (
int i = numOfCells; i < nr_points; i++)
286 vertices->InsertNextCell(1);
287 vertices->InsertCellPoint(i);
291 else if (numOfCells > nr_points)
293 vertices->ResizeExact(nr_points, nr_points);
296 polydata->SetPoints(points);
297 polydata->SetVerts(vertices);
301 updateCells (cells, initcells, nr_points);
304 vertices->SetCells (nr_points, cells);
307 vertices->SetNumberOfCells(nr_points);
312 template <
typename Po
intT>
void
313 pcl::visualization::PCLVisualizer::convertPointCloudToVTKPolyData (
321 allocVtkPolyData (polydata);
323 polydata->SetVerts (vertices);
329 polydata->SetPoints (points);
331 vtkIdType nr_points = points->GetNumberOfPoints ();
334 vertices = polydata->GetVerts ();
338 #ifdef VTK_CELL_ARRAY_V2
342 auto numOfCells = vertices->GetNumberOfCells();
345 if (numOfCells < nr_points)
347 for (
int i = numOfCells; i < nr_points; i++)
349 vertices->InsertNextCell(1);
350 vertices->InsertCellPoint(i);
354 else if (numOfCells > nr_points)
356 vertices->ResizeExact(nr_points, nr_points);
359 polydata->SetPoints(points);
360 polydata->SetVerts(vertices);
364 updateCells (cells, initcells, nr_points);
366 vertices->SetCells (nr_points, cells);
371 template <
typename Po
intT>
bool
374 double r,
double g,
double b,
const std::string &
id,
int viewport)
381 auto am_it = shape_actor_map_->find (
id);
382 if (am_it != shape_actor_map_->end ())
387 all_data->AddInputData (
reinterpret_cast<vtkPolyDataMapper*
> ((vtkActor::SafeDownCast (am_it->second))->GetMapper ())->GetInput ());
391 surface_filter->AddInputData (vtkUnstructuredGrid::SafeDownCast (data));
393 all_data->AddInputData (poly_data);
397 createActorFromVTKDataSet (all_data->GetOutput (), actor);
398 actor->GetProperty ()->SetRepresentationToWireframe ();
399 actor->GetProperty ()->SetColor (r, g, b);
400 actor->GetMapper ()->ScalarVisibilityOff ();
401 removeActorFromRenderer (am_it->second, viewport);
402 addActorToRenderer (actor, viewport);
405 (*shape_actor_map_)[id] = actor;
411 createActorFromVTKDataSet (data, actor);
412 actor->GetProperty ()->SetRepresentationToWireframe ();
413 actor->GetProperty ()->SetColor (r, g, b);
414 actor->GetMapper ()->ScalarVisibilityOff ();
415 addActorToRenderer (actor, viewport);
418 (*shape_actor_map_)[id] = actor;
425 template <
typename Po
intT>
bool
428 double r,
double g,
double b,
const std::string &
id,
int viewport)
435 auto am_it = shape_actor_map_->find (
id);
436 if (am_it != shape_actor_map_->end ())
441 all_data->AddInputData (
reinterpret_cast<vtkPolyDataMapper*
> ((vtkActor::SafeDownCast (am_it->second))->GetMapper ())->GetInput ());
445 surface_filter->SetInputData (vtkUnstructuredGrid::SafeDownCast (data));
447 all_data->AddInputData (poly_data);
451 createActorFromVTKDataSet (all_data->GetOutput (), actor);
452 actor->GetProperty ()->SetRepresentationToWireframe ();
453 actor->GetProperty ()->SetColor (r, g, b);
454 actor->GetMapper ()->ScalarVisibilityOn ();
455 actor->GetProperty ()->BackfaceCullingOff ();
456 removeActorFromRenderer (am_it->second, viewport);
457 addActorToRenderer (actor, viewport);
460 (*shape_actor_map_)[id] = actor;
466 createActorFromVTKDataSet (data, actor);
467 actor->GetProperty ()->SetRepresentationToWireframe ();
468 actor->GetProperty ()->SetColor (r, g, b);
469 actor->GetMapper ()->ScalarVisibilityOn ();
470 actor->GetProperty ()->BackfaceCullingOff ();
471 addActorToRenderer (actor, viewport);
474 (*shape_actor_map_)[id] = actor;
480 template <
typename Po
intT>
bool
483 const std::string &
id,
int viewport)
485 return (addPolygon<PointT> (cloud, 0.5, 0.5, 0.5,
id, viewport));
489 template <
typename P1,
typename P2>
bool
494 PCL_WARN (
"[addLine] The id <%s> already exists! Please choose a different id and retry.\n",
id.c_str ());
502 createActorFromVTKDataSet (data, actor);
503 actor->GetProperty ()->SetRepresentationToWireframe ();
504 actor->GetProperty ()->SetColor (r, g, b);
505 actor->GetMapper ()->ScalarVisibilityOff ();
506 addActorToRenderer (actor, viewport);
509 (*shape_actor_map_)[id] = actor;
514 template <
typename P1,
typename P2>
bool
519 PCL_WARN (
"[addArrow] The id <%s> already exists! Please choose a different id and retry.\n",
id.c_str ());
525 leader->GetPositionCoordinate ()->SetCoordinateSystemToWorld ();
526 leader->GetPositionCoordinate ()->SetValue (pt1.x, pt1.y, pt1.z);
527 leader->GetPosition2Coordinate ()->SetCoordinateSystemToWorld ();
528 leader->GetPosition2Coordinate ()->SetValue (pt2.x, pt2.y, pt2.z);
529 leader->SetArrowStyleToFilled ();
530 leader->AutoLabelOn ();
532 leader->GetProperty ()->SetColor (r, g, b);
533 addActorToRenderer (leader, viewport);
536 (*shape_actor_map_)[id] = leader;
541 template <
typename P1,
typename P2>
bool
546 PCL_WARN (
"[addArrow] The id <%s> already exists! Please choose a different id and retry.\n",
id.c_str ());
552 leader->GetPositionCoordinate ()->SetCoordinateSystemToWorld ();
553 leader->GetPositionCoordinate ()->SetValue (pt1.x, pt1.y, pt1.z);
554 leader->GetPosition2Coordinate ()->SetCoordinateSystemToWorld ();
555 leader->GetPosition2Coordinate ()->SetValue (pt2.x, pt2.y, pt2.z);
556 leader->SetArrowStyleToFilled ();
557 leader->SetArrowPlacementToPoint1 ();
559 leader->AutoLabelOn ();
561 leader->AutoLabelOff ();
563 leader->GetProperty ()->SetColor (r, g, b);
564 addActorToRenderer (leader, viewport);
567 (*shape_actor_map_)[id] = leader;
571 template <
typename P1,
typename P2>
bool
573 double r_line,
double g_line,
double b_line,
574 double r_text,
double g_text,
double b_text,
575 const std::string &
id,
int viewport)
579 PCL_WARN (
"[addArrow] The id <%s> already exists! Please choose a different id and retry.\n",
id.c_str ());
585 leader->GetPositionCoordinate ()->SetCoordinateSystemToWorld ();
586 leader->GetPositionCoordinate ()->SetValue (pt1.x, pt1.y, pt1.z);
587 leader->GetPosition2Coordinate ()->SetCoordinateSystemToWorld ();
588 leader->GetPosition2Coordinate ()->SetValue (pt2.x, pt2.y, pt2.z);
589 leader->SetArrowStyleToFilled ();
590 leader->AutoLabelOn ();
592 leader->GetLabelTextProperty()->SetColor(r_text, g_text, b_text);
594 leader->GetProperty ()->SetColor (r_line, g_line, b_line);
595 addActorToRenderer (leader, viewport);
598 (*shape_actor_map_)[id] = leader;
603 template <
typename P1,
typename P2>
bool
606 return (addLine (pt1, pt2, 0.5, 0.5, 0.5,
id, viewport));
610 template <
typename Po
intT>
bool
615 PCL_WARN (
"[addSphere] The id <%s> already exists! Please choose a different id and retry.\n",
id.c_str ());
620 data->SetRadius (radius);
621 data->SetCenter (
static_cast<double>(center.x),
static_cast<double>(center.y),
static_cast<double>(center.z));
622 data->SetPhiResolution (10);
623 data->SetThetaResolution (10);
624 data->LatLongTessellationOff ();
629 mapper->SetInputConnection (data->GetOutputPort ());
633 actor->SetMapper (mapper);
635 actor->GetProperty ()->SetRepresentationToSurface ();
636 actor->GetProperty ()->SetInterpolationToFlat ();
637 actor->GetProperty ()->SetColor (r, g, b);
638 actor->GetMapper ()->StaticOn ();
639 actor->GetMapper ()->ScalarVisibilityOff ();
640 actor->GetMapper ()->Update ();
641 addActorToRenderer (actor, viewport);
644 (*shape_actor_map_)[id] = actor;
649 template <
typename Po
intT>
bool
652 return (addSphere (center, radius, 0.5, 0.5, 0.5,
id, viewport));
656 template<
typename Po
intT>
bool
666 auto am_it = shape_actor_map_->find (
id);
667 vtkLODActor* actor = vtkLODActor::SafeDownCast (am_it->second);
670 vtkAlgorithm *algo = actor->GetMapper ()->GetInputAlgorithm ();
671 vtkSphereSource *src = vtkSphereSource::SafeDownCast (algo);
675 src->SetCenter (
double (center.x), double (center.y), double (center.z));
676 src->SetRadius (radius);
678 actor->GetProperty ()->SetColor (r, g, b);
685 template <
typename Po
intT>
bool
687 const std::string &text,
693 const std::string &
id,
706 if (rens_->GetNumberOfItems () <= viewport)
708 PCL_ERROR (
"[addText3D] The viewport [%d] doesn't exist (id <%s>)! \n",
715 rens_->InitTraversal ();
716 for (std::size_t i = viewport; rens_->GetNextItem (); ++i)
718 const std::string uid = tid + std::string (i,
'*');
721 PCL_ERROR (
"[addText3D] The id <%s> already exists in viewport [%d]! \n"
722 "Please choose a different id and retry.\n",
733 textSource->SetText (text.c_str());
734 textSource->Update ();
737 textMapper->SetInputConnection (textSource->GetOutputPort ());
740 rens_->InitTraversal ();
741 vtkRenderer* renderer;
743 while ((renderer = rens_->GetNextItem ()))
746 if (viewport == 0 || viewport == i)
749 textActor->SetMapper (textMapper);
750 textActor->SetPosition (position.x, position.y, position.z);
751 textActor->SetScale (textScale);
752 textActor->GetProperty ()->SetColor (r, g, b);
753 textActor->SetCamera (renderer->GetActiveCamera ());
755 renderer->AddActor (textActor);
759 const std::string uid = tid + std::string (i,
'*');
760 (*shape_actor_map_)[uid] = textActor;
770 template <
typename Po
intT>
bool
772 const std::string &text,
774 double orientation[3],
779 const std::string &
id,
792 if (rens_->GetNumberOfItems () <= viewport)
794 PCL_ERROR (
"[addText3D] The viewport [%d] doesn't exist (id <%s>)!\n",
801 rens_->InitTraversal ();
802 for (std::size_t i = viewport; rens_->GetNextItem (); ++i)
804 const std::string uid = tid + std::string (i,
'*');
807 PCL_ERROR (
"[addText3D] The id <%s> already exists in viewport [%d]! "
808 "Please choose a different id and retry.\n",
819 textSource->SetText (text.c_str());
820 textSource->Update ();
823 textMapper->SetInputConnection (textSource->GetOutputPort ());
826 textActor->SetMapper (textMapper);
827 textActor->SetPosition (position.x, position.y, position.z);
828 textActor->SetScale (textScale);
829 textActor->GetProperty ()->SetColor (r, g, b);
830 textActor->SetOrientation (orientation);
833 rens_->InitTraversal ();
835 for ( vtkRenderer* renderer = rens_->GetNextItem ();
837 renderer = rens_->GetNextItem (), ++i)
839 if (viewport == 0 || viewport == i)
841 renderer->AddActor (textActor);
842 const std::string uid = tid + std::string (i,
'*');
843 (*shape_actor_map_)[uid] = textActor;
851 template <
typename Po
intNT>
bool
854 int level,
float scale,
const std::string &
id,
int viewport)
856 return (addPointCloudNormals<PointNT, PointNT> (cloud, cloud, level, scale,
id, viewport));
860 template <
typename Po
intT,
typename Po
intNT>
bool
864 int level,
float scale,
865 const std::string &
id,
int viewport)
867 if (normals->
size () != cloud->
size ())
869 PCL_ERROR (
"[addPointCloudNormals] The number of points differs from the number of normals!\n");
873 if (normals->
empty ())
875 PCL_WARN (
"[addPointCloudNormals] An empty normal cloud is given! Nothing to display.\n");
881 PCL_WARN (
"[addPointCloudNormals] The id <%s> already exists! Please choose a different id and retry.\n",
id.c_str ());
888 points->SetDataTypeToFloat ();
890 data->SetNumberOfComponents (3);
893 vtkIdType nr_normals = 0;
894 float* pts =
nullptr;
899 auto point_step =
static_cast<vtkIdType
> (sqrt (
static_cast<double>(level)));
900 nr_normals = (
static_cast<vtkIdType
> ((cloud->
width - 1)/ point_step) + 1) *
901 (
static_cast<vtkIdType
> ((cloud->
height - 1) / point_step) + 1);
902 pts =
new float[2 * nr_normals * 3];
904 vtkIdType cell_count = 0;
905 for (vtkIdType y = 0; y < normals->
height; y += point_step)
906 for (vtkIdType x = 0; x < normals->
width; x += point_step)
908 PointT p = (*cloud)(x, y);
911 p.x += (*normals)(x, y).normal[0] * scale;
912 p.y += (*normals)(x, y).normal[1] * scale;
913 p.z += (*normals)(x, y).normal[2] * scale;
915 pts[2 * cell_count * 3 + 0] = (*cloud)(x, y).x;
916 pts[2 * cell_count * 3 + 1] = (*cloud)(x, y).y;
917 pts[2 * cell_count * 3 + 2] = (*cloud)(x, y).z;
918 pts[2 * cell_count * 3 + 3] = p.x;
919 pts[2 * cell_count * 3 + 4] = p.y;
920 pts[2 * cell_count * 3 + 5] = p.z;
922 lines->InsertNextCell (2);
923 lines->InsertCellPoint (2 * cell_count);
924 lines->InsertCellPoint (2 * cell_count + 1);
927 nr_normals = cell_count;
931 nr_normals = (cloud->
size () - 1) / level + 1 ;
932 pts =
new float[2 * nr_normals * 3];
935 for (vtkIdType i = 0; (j < nr_normals) && (i < static_cast<vtkIdType>(cloud->
size())); i += level)
940 p.x += (*normals)[i].normal[0] * scale;
941 p.y += (*normals)[i].normal[1] * scale;
942 p.z += (*normals)[i].normal[2] * scale;
944 pts[2 * j * 3 + 0] = (*cloud)[i].x;
945 pts[2 * j * 3 + 1] = (*cloud)[i].y;
946 pts[2 * j * 3 + 2] = (*cloud)[i].z;
947 pts[2 * j * 3 + 3] = p.x;
948 pts[2 * j * 3 + 4] = p.y;
949 pts[2 * j * 3 + 5] = p.z;
951 lines->InsertNextCell (2);
952 lines->InsertCellPoint (2 * j);
953 lines->InsertCellPoint (2 * j + 1);
959 data->SetArray (&pts[0], 2 * nr_normals * 3, 0, vtkFloatArray::VTK_DATA_ARRAY_DELETE);
960 points->SetData (data);
963 polyData->SetPoints (points);
964 polyData->SetLines (lines);
967 mapper->SetInputData (polyData);
968 mapper->SetColorModeToMapScalars();
969 mapper->SetScalarModeToUsePointData();
973 actor->SetMapper (mapper);
978 actor->SetUserMatrix (transformation);
981 addActorToRenderer (actor, viewport);
984 (*cloud_actor_map_)[id].actor = actor;
989 template <
typename Po
intNT>
bool
993 int level,
float scale,
994 const std::string &
id,
int viewport)
996 return (addPointCloudPrincipalCurvatures<PointNT, PointNT> (cloud, cloud, pcs, level, scale,
id, viewport));
1000 template <
typename Po
intT,
typename Po
intNT>
bool
1005 int level,
float scale,
1006 const std::string &
id,
int viewport)
1010 pcl::console::print_error (
"[addPointCloudPrincipalCurvatures] The number of points differs from the number of principal curvatures/normals!\n");
1016 PCL_WARN (
"[addPointCloudPrincipalCurvatures] The id <%s> already exists! Please choose a different id and retry.\n",
id.c_str ());
1024 unsigned char green[3] = {0, 255, 0};
1025 unsigned char blue[3] = {0, 0, 255};
1029 line_1_colors->SetNumberOfComponents (3);
1030 line_1_colors->SetName (
"Colors");
1032 line_2_colors->SetNumberOfComponents (3);
1033 line_2_colors->SetName (
"Colors");
1036 for (std::size_t i = 0; i < cloud->
size (); i+=level)
1039 p.x += ((*pcs)[i].pc1 * (*pcs)[i].principal_curvature[0]) * scale;
1040 p.y += ((*pcs)[i].pc1 * (*pcs)[i].principal_curvature[1]) * scale;
1041 p.z += ((*pcs)[i].pc1 * (*pcs)[i].principal_curvature[2]) * scale;
1044 line_1->SetPoint1 ((*cloud)[i].x, (*cloud)[i].y, (*cloud)[i].z);
1045 line_1->SetPoint2 (p.x, p.y, p.z);
1047 polydata_1->AddInputData (line_1->GetOutput ());
1048 line_1_colors->InsertNextTupleValue (green);
1050 polydata_1->Update ();
1052 line_1_data->GetCellData ()->SetScalars (line_1_colors);
1055 for (std::size_t i = 0; i < cloud->
size (); i += level)
1057 Eigen::Vector3f pc ((*pcs)[i].principal_curvature[0],
1058 (*pcs)[i].principal_curvature[1],
1059 (*pcs)[i].principal_curvature[2]);
1060 Eigen::Vector3f normal ((*normals)[i].normal[0],
1061 (*normals)[i].normal[1],
1062 (*normals)[i].normal[2]);
1063 Eigen::Vector3f pc_c = pc.cross (normal);
1066 p.x += ((*pcs)[i].pc2 * pc_c[0]) * scale;
1067 p.y += ((*pcs)[i].pc2 * pc_c[1]) * scale;
1068 p.z += ((*pcs)[i].pc2 * pc_c[2]) * scale;
1071 line_2->SetPoint1 ((*cloud)[i].x, (*cloud)[i].y, (*cloud)[i].z);
1072 line_2->SetPoint2 (p.x, p.y, p.z);
1074 polydata_2->AddInputData (line_2->GetOutput ());
1076 line_2_colors->InsertNextTupleValue (blue);
1078 polydata_2->Update ();
1080 line_2_data->GetCellData ()->SetScalars (line_2_colors);
1084 alldata->AddInputData (line_1_data);
1085 alldata->AddInputData (line_2_data);
1090 createActorFromVTKDataSet (alldata->GetOutput (), actor);
1091 actor->GetMapper ()->SetScalarModeToUseCellData ();
1094 addActorToRenderer (actor, viewport);
1099 (*cloud_actor_map_)[id] = act;
1104 template <
typename Po
intT,
typename GradientT>
bool
1108 int level,
double scale,
1109 const std::string &
id,
int viewport)
1111 if (gradients->
size () != cloud->
size ())
1113 PCL_ERROR (
"[addPointCloudGradients] The number of points differs from the number of gradients!\n");
1118 PCL_WARN (
"[addPointCloudGradients] The id <%s> already exists! Please choose a different id and retry.\n",
id.c_str ());
1125 points->SetDataTypeToFloat ();
1127 data->SetNumberOfComponents (3);
1129 vtkIdType nr_gradients = (cloud->
size () - 1) / level + 1 ;
1130 float* pts =
new float[2 * nr_gradients * 3];
1132 for (vtkIdType i = 0, j = 0; j < nr_gradients; j++, i = j * level)
1135 p.x += (*gradients)[i].gradient[0] * scale;
1136 p.y += (*gradients)[i].gradient[1] * scale;
1137 p.z += (*gradients)[i].gradient[2] * scale;
1139 pts[2 * j * 3 + 0] = (*cloud)[i].x;
1140 pts[2 * j * 3 + 1] = (*cloud)[i].y;
1141 pts[2 * j * 3 + 2] = (*cloud)[i].z;
1142 pts[2 * j * 3 + 3] = p.x;
1143 pts[2 * j * 3 + 4] = p.y;
1144 pts[2 * j * 3 + 5] = p.z;
1146 lines->InsertNextCell(2);
1147 lines->InsertCellPoint(2*j);
1148 lines->InsertCellPoint(2*j+1);
1151 data->SetArray (&pts[0], 2 * nr_gradients * 3, 0, vtkFloatArray::VTK_DATA_ARRAY_DELETE);
1152 points->SetData (data);
1155 polyData->SetPoints(points);
1156 polyData->SetLines(lines);
1159 mapper->SetInputData (polyData);
1160 mapper->SetColorModeToMapScalars();
1161 mapper->SetScalarModeToUsePointData();
1165 actor->SetMapper (mapper);
1168 addActorToRenderer (actor, viewport);
1171 (*cloud_actor_map_)[id].actor = actor;
1176 template <
typename Po
intT>
bool
1180 const std::vector<int> &correspondences,
1181 const std::string &
id,
1185 corrs.resize (correspondences.size ());
1187 std::size_t index = 0;
1188 for (
auto &corr : corrs)
1190 corr.index_query = index;
1191 corr.index_match = correspondences[index];
1195 return (addCorrespondences<PointT> (source_points, target_points, corrs,
id, viewport));
1199 template <
typename Po
intT>
bool
1205 const std::string &
id,
1209 if (correspondences.empty ())
1211 PCL_DEBUG (
"[addCorrespondences] An empty set of correspondences given! Nothing to display.\n");
1216 auto am_it = shape_actor_map_->find (
id);
1217 if (am_it != shape_actor_map_->end () && !overwrite)
1219 PCL_WARN (
"[addCorrespondences] A set of correspondences with id <%s> already exists! Please choose a different id and retry.\n",
id.c_str ());
1222 if (am_it == shape_actor_map_->end () && overwrite)
1227 int n_corr =
static_cast<int>(correspondences.size () / nth);
1232 line_colors->SetNumberOfComponents (3);
1233 line_colors->SetName (
"Colors");
1234 line_colors->SetNumberOfTuples (n_corr);
1238 line_points->SetNumberOfPoints (2 * n_corr);
1241 line_cells_id->SetNumberOfComponents (3);
1242 line_cells_id->SetNumberOfTuples (n_corr);
1243 vtkIdType *line_cell_id = line_cells_id->GetPointer (0);
1247 line_tcoords->SetNumberOfComponents (1);
1248 line_tcoords->SetNumberOfTuples (n_corr * 2);
1249 line_tcoords->SetName (
"Texture Coordinates");
1251 double tc[3] = {0.0, 0.0, 0.0};
1253 Eigen::Affine3f source_transformation;
1255 source_transformation.translation () = source_points->
sensor_origin_.template head<3> ();
1256 Eigen::Affine3f target_transformation;
1258 target_transformation.translation () = target_points->
sensor_origin_.template head<3> ();
1262 for (std::size_t i = 0; i < correspondences.size (); i += nth, ++j)
1264 if (correspondences[i].index_match ==
UNAVAILABLE)
1266 PCL_WARN (
"[addCorrespondences] No valid index_match for correspondence %d\n", i);
1270 PointT p_src ((*source_points)[correspondences[i].index_query]);
1271 PointT p_tgt ((*target_points)[correspondences[i].index_match]);
1273 p_src.getVector3fMap () = source_transformation * p_src.getVector3fMap ();
1274 p_tgt.getVector3fMap () = target_transformation * p_tgt.getVector3fMap ();
1276 int id1 = j * 2 + 0, id2 = j * 2 + 1;
1278 line_points->SetPoint (id1, p_src.x, p_src.y, p_src.z);
1279 line_points->SetPoint (id2, p_tgt.x, p_tgt.y, p_tgt.z);
1281 *line_cell_id++ = 2;
1282 *line_cell_id++ = id1;
1283 *line_cell_id++ = id2;
1285 tc[0] = 0.; line_tcoords->SetTuple (id1, tc);
1286 tc[0] = 1.; line_tcoords->SetTuple (id2, tc);
1289 rgb[0] = vtkMath::Random (32, 255);
1290 rgb[1] = vtkMath::Random (32, 255);
1291 rgb[2] = vtkMath::Random (32, 255);
1292 line_colors->InsertTuple (i, rgb);
1294 line_colors->SetNumberOfTuples (j);
1295 line_cells_id->SetNumberOfTuples (j);
1296 line_cells->SetCells (n_corr, line_cells_id);
1297 line_points->SetNumberOfPoints (j*2);
1298 line_tcoords->SetNumberOfTuples (j*2);
1301 line_data->SetPoints (line_points);
1302 line_data->SetLines (line_cells);
1303 line_data->GetPointData ()->SetTCoords (line_tcoords);
1304 line_data->GetCellData ()->SetScalars (line_colors);
1310 createActorFromVTKDataSet (line_data, actor);
1311 actor->GetProperty ()->SetRepresentationToWireframe ();
1312 actor->GetProperty ()->SetOpacity (0.5);
1313 addActorToRenderer (actor, viewport);
1316 (*shape_actor_map_)[id] = actor;
1324 reinterpret_cast<vtkPolyDataMapper*
> (actor->GetMapper ())->SetInputData (line_data);
1331 template <
typename Po
intT>
bool
1337 const std::string &
id,
1340 return (addCorrespondences<PointT> (source_points, target_points, correspondences, nth,
id, viewport,
true));
1344 template <
typename Po
intT>
bool
1345 pcl::visualization::PCLVisualizer::fromHandlersToScreen (
1348 const std::string &
id,
1350 const Eigen::Vector4f& sensor_origin,
1351 const Eigen::Quaternion<float>& sensor_orientation)
1355 PCL_WARN (
"[fromHandlersToScreen] PointCloud <%s> requested with an invalid geometry handler (%s)!\n",
id.c_str (), geometry_handler.
getName ().c_str ());
1361 PCL_WARN (
"[fromHandlersToScreen] PointCloud <%s> requested with an invalid color handler (%s)!\n",
id.c_str (), color_handler.
getName ().c_str ());
1368 convertPointCloudToVTKPolyData<PointT> (geometry_handler, polydata, initcells);
1371 bool has_colors =
false;
1373 if (
auto scalars = color_handler.
getColor ())
1375 polydata->GetPointData ()->SetScalars (scalars);
1376 scalars->GetRange (minmax);
1382 createActorFromVTKDataSet (polydata, actor);
1384 actor->GetMapper ()->SetScalarRange (minmax);
1387 addActorToRenderer (actor, viewport);
1390 CloudActor& cloud_actor = (*cloud_actor_map_)[id];
1391 cloud_actor.actor = actor;
1392 cloud_actor.cells = initcells;
1396 convertToVtkMatrix (sensor_origin, sensor_orientation, transformation);
1397 cloud_actor.viewpoint_transformation_ = transformation;
1398 cloud_actor.actor->SetUserMatrix (transformation);
1399 cloud_actor.actor->Modified ();
1405 template <
typename Po
intT>
bool
1406 pcl::visualization::PCLVisualizer::fromHandlersToScreen (
1407 const PointCloudGeometryHandler<PointT> &geometry_handler,
1408 const ColorHandlerConstPtr &color_handler,
1409 const std::string &
id,
1411 const Eigen::Vector4f& sensor_origin,
1412 const Eigen::Quaternion<float>& sensor_orientation)
1414 if (!geometry_handler.isCapable ())
1416 PCL_WARN (
"[fromHandlersToScreen] PointCloud <%s> requested with an invalid geometry handler (%s)!\n",
id.c_str (), geometry_handler.getName ().c_str ());
1420 if (!color_handler->isCapable ())
1422 PCL_WARN (
"[fromHandlersToScreen] PointCloud <%s> requested with an invalid color handler (%s)!\n",
id.c_str (), color_handler->getName ().c_str ());
1429 convertPointCloudToVTKPolyData<PointT> (geometry_handler, polydata, initcells);
1433 bool has_colors =
false;
1435 if (
auto scalars = color_handler->getColor ())
1437 polydata->GetPointData ()->SetScalars (scalars);
1438 scalars->GetRange (minmax);
1444 createActorFromVTKDataSet (polydata, actor);
1446 actor->GetMapper ()->SetScalarRange (minmax);
1449 addActorToRenderer (actor, viewport);
1452 CloudActor& cloud_actor = (*cloud_actor_map_)[id];
1453 cloud_actor.actor = actor;
1454 cloud_actor.cells = initcells;
1455 cloud_actor.color_handlers.push_back (color_handler);
1459 convertToVtkMatrix (sensor_origin, sensor_orientation, transformation);
1460 cloud_actor.viewpoint_transformation_ = transformation;
1461 cloud_actor.actor->SetUserMatrix (transformation);
1462 cloud_actor.actor->Modified ();
1468 template <
typename Po
intT>
bool
1469 pcl::visualization::PCLVisualizer::fromHandlersToScreen (
1470 const GeometryHandlerConstPtr &geometry_handler,
1471 const PointCloudColorHandler<PointT> &color_handler,
1472 const std::string &
id,
1474 const Eigen::Vector4f& sensor_origin,
1475 const Eigen::Quaternion<float>& sensor_orientation)
1477 if (!geometry_handler->isCapable ())
1479 PCL_WARN (
"[fromHandlersToScreen] PointCloud <%s> requested with an invalid geometry handler (%s)!\n",
id.c_str (), geometry_handler->getName ().c_str ());
1483 if (!color_handler.isCapable ())
1485 PCL_WARN (
"[fromHandlersToScreen] PointCloud <%s> requested with an invalid color handler (%s)!\n",
id.c_str (), color_handler.getName ().c_str ());
1492 convertPointCloudToVTKPolyData (geometry_handler, polydata, initcells);
1496 bool has_colors =
false;
1498 if (
auto scalars = color_handler.getColor ())
1500 polydata->GetPointData ()->SetScalars (scalars);
1501 scalars->GetRange (minmax);
1507 createActorFromVTKDataSet (polydata, actor);
1509 actor->GetMapper ()->SetScalarRange (minmax);
1512 addActorToRenderer (actor, viewport);
1515 CloudActor& cloud_actor = (*cloud_actor_map_)[id];
1516 cloud_actor.actor = actor;
1517 cloud_actor.cells = initcells;
1518 cloud_actor.geometry_handlers.push_back (geometry_handler);
1522 convertToVtkMatrix (sensor_origin, sensor_orientation, transformation);
1523 cloud_actor.viewpoint_transformation_ = transformation;
1524 cloud_actor.actor->SetUserMatrix (transformation);
1525 cloud_actor.actor->Modified ();
1531 template <
typename Po
intT>
bool
1533 const std::string &
id)
1536 auto am_it = cloud_actor_map_->find (
id);
1538 if (am_it == cloud_actor_map_->end ())
1545 convertPointCloudToVTKPolyData<PointT> (cloud, polydata, am_it->second.cells);
1549 polydata->GetPointData ()->SetScalars (scalars);
1551 minmax[0] = std::numeric_limits<double>::min ();
1552 minmax[1] = std::numeric_limits<double>::max ();
1553 am_it->second.actor->GetMapper ()->SetScalarRange (minmax);
1556 reinterpret_cast<vtkPolyDataMapper*
> (am_it->second.actor->GetMapper ())->SetInputData (polydata);
1561 template <
typename Po
intT>
bool
1564 const std::string &
id)
1567 auto am_it = cloud_actor_map_->find (
id);
1569 if (am_it == cloud_actor_map_->end ())
1576 convertPointCloudToVTKPolyData (geometry_handler, polydata, am_it->second.cells);
1580 polydata->GetPointData ()->SetScalars (scalars);
1582 minmax[0] = std::numeric_limits<double>::min ();
1583 minmax[1] = std::numeric_limits<double>::max ();
1584 am_it->second.actor->GetMapper ()->SetScalarRange (minmax);
1587 reinterpret_cast<vtkPolyDataMapper*
> (am_it->second.actor->GetMapper ())->SetInputData (polydata);
1593 template <
typename Po
intT>
bool
1596 const std::string &
id)
1599 auto am_it = cloud_actor_map_->find (
id);
1601 if (am_it == cloud_actor_map_->end ())
1609 convertPointCloudToVTKPolyData<PointT>(cloud, polydata, am_it->second.cells);
1612 bool has_colors =
false;
1614 if (
auto scalars = color_handler.
getColor ())
1617 polydata->GetPointData ()->SetScalars (scalars);
1618 scalars->GetRange (minmax);
1623 am_it->second.actor->GetMapper ()->SetScalarRange (minmax);
1626 reinterpret_cast<vtkPolyDataMapper*
> (am_it->second.actor->GetMapper ())->SetInputData (polydata);
1631 template <
typename Po
intT>
bool
1634 const std::vector<pcl::Vertices> &vertices,
1635 const std::string &
id,
1638 if (vertices.empty () || cloud->
points.empty ())
1643 PCL_WARN (
"[addPolygonMesh] The id <%s> already exists! Please choose a different id and retry.\n",
id.c_str ());
1648 std::vector<pcl::PCLPointField> fields;
1650 rgb_idx = pcl::getFieldIndex<PointT> (
"rgb", fields);
1652 rgb_idx = pcl::getFieldIndex<PointT> (
"rgba", fields);
1656 colors->SetNumberOfComponents (3);
1657 colors->SetName (
"Colors");
1658 std::uint32_t offset = fields[rgb_idx].offset;
1659 for (std::size_t i = 0; i < cloud->
size (); ++i)
1663 const auto*
const rgb_data =
reinterpret_cast<const pcl::RGB*
>(
reinterpret_cast<const char*
> (&(*cloud)[i]) + offset);
1664 unsigned char color[3];
1665 color[0] = rgb_data->r;
1666 color[1] = rgb_data->g;
1667 color[2] = rgb_data->b;
1668 colors->InsertNextTupleValue (color);
1674 vtkIdType nr_points = cloud->
size ();
1675 points->SetNumberOfPoints (nr_points);
1679 float *data =
dynamic_cast<vtkFloatArray*
> (points->GetData ())->GetPointer (0);
1682 std::vector<int> lookup;
1686 for (vtkIdType i = 0; i < nr_points; ++i, ptr += 3) {
1687 std::copy(&(*cloud)[i].x, &(*cloud)[i].x + 3, &data[ptr]);
1692 lookup.resize (nr_points);
1694 for (vtkIdType i = 0; i < nr_points; ++i)
1700 lookup[i] =
static_cast<int> (j);
1701 std::copy (&(*cloud)[i].x, &(*cloud)[i].x + 3, &data[ptr]);
1706 points->SetNumberOfPoints (nr_points);
1710 int max_size_of_polygon = -1;
1711 for (
const auto &vertex : vertices)
1712 if (max_size_of_polygon <
static_cast<int> (vertex.vertices.size ()))
1713 max_size_of_polygon =
static_cast<int> (vertex.vertices.size ());
1715 if (vertices.size () > 1)
1720 const auto idx =
details::fillCells(lookup,vertices,cell_array, max_size_of_polygon);
1723 allocVtkPolyData (polydata);
1724 cell_array->GetData ()->SetNumberOfValues (idx);
1725 cell_array->Squeeze ();
1726 polydata->SetPolys (cell_array);
1727 polydata->SetPoints (points);
1730 polydata->GetPointData ()->SetScalars (colors);
1732 createActorFromVTKDataSet (polydata, actor,
false);
1737 std::size_t n_points = vertices[0].vertices.size ();
1738 polygon->GetPointIds ()->SetNumberOfIds (n_points - 1);
1740 if (!lookup.empty ())
1742 for (std::size_t j = 0; j < (n_points - 1); ++j)
1743 polygon->GetPointIds ()->SetId (j, lookup[vertices[0].vertices[j]]);
1747 for (std::size_t j = 0; j < (n_points - 1); ++j)
1748 polygon->GetPointIds ()->SetId (j, vertices[0].vertices[j]);
1752 poly_grid->Allocate (1, 1);
1753 poly_grid->InsertNextCell (polygon->GetCellType (), polygon->GetPointIds ());
1754 poly_grid->SetPoints (points);
1756 poly_grid->GetPointData ()->SetScalars (colors);
1758 createActorFromVTKDataSet (poly_grid, actor,
false);
1760 addActorToRenderer (actor, viewport);
1761 actor->GetProperty ()->SetRepresentationToSurface ();
1763 actor->GetProperty ()->BackfaceCullingOff ();
1764 actor->GetProperty ()->SetInterpolationToFlat ();
1765 actor->GetProperty ()->EdgeVisibilityOff ();
1766 actor->GetProperty ()->ShadingOff ();
1769 (*cloud_actor_map_)[id].actor = actor;
1774 (*cloud_actor_map_)[id].viewpoint_transformation_ = transformation;
1780 template <
typename Po
intT>
bool
1783 const std::vector<pcl::Vertices> &verts,
1784 const std::string &
id)
1793 auto am_it = cloud_actor_map_->find (
id);
1794 if (am_it == cloud_actor_map_->end ())
1806 vtkIdType nr_points = cloud->
size ();
1807 points->SetNumberOfPoints (nr_points);
1810 float *data = (
dynamic_cast<vtkFloatArray*
> (points->GetData ()))->GetPointer (0);
1813 std::vector<int> lookup;
1817 for (vtkIdType i = 0; i < nr_points; ++i, ptr += 3)
1818 std::copy (&(*cloud)[i].x, &(*cloud)[i].x + 3, &data[ptr]);
1822 lookup.resize (nr_points);
1824 for (vtkIdType i = 0; i < nr_points; ++i)
1830 lookup [i] =
static_cast<int> (j);
1831 std::copy (&(*cloud)[i].x, &(*cloud)[i].x + 3, &data[ptr]);
1836 points->SetNumberOfPoints (nr_points);
1840 vtkUnsignedCharArray* colors = vtkUnsignedCharArray::SafeDownCast (polydata->GetPointData ()->GetScalars ());
1844 std::vector<pcl::PCLPointField> fields;
1845 rgb_idx = pcl::getFieldIndex<PointT> (
"rgb", fields);
1847 rgb_idx = pcl::getFieldIndex<PointT> (
"rgba", fields);
1848 if (rgb_idx != -1 && colors)
1851 std::uint32_t offset = fields[rgb_idx].offset;
1852 for (std::size_t i = 0; i < cloud->
size (); ++i)
1856 const auto*
const rgb_data =
reinterpret_cast<const pcl::RGB*
>(
reinterpret_cast<const char*
> (&(*cloud)[i]) + offset);
1857 unsigned char color[3];
1858 color[0] = rgb_data->r;
1859 color[1] = rgb_data->g;
1860 color[2] = rgb_data->b;
1861 colors->SetTupleValue (j++, color);
1866 int max_size_of_polygon = -1;
1867 for (
const auto &vertex : verts)
1868 if (max_size_of_polygon <
static_cast<int> (vertex.vertices.size ()))
1869 max_size_of_polygon =
static_cast<int> (vertex.vertices.size ());
1876 cells->GetData ()->SetNumberOfValues (idx);
1879 polydata->SetPolys (cells);
1884 #ifdef vtkGenericDataArray_h
1885 #undef SetTupleValue
1886 #undef InsertNextTupleValue
1887 #undef GetTupleValue
PlanarPolygon represents a planar (2D) polygon, potentially in a 3D space.
bool is_dense
True if no points are invalid (e.g., have NaN or Inf values in any of their floating point fields).
Eigen::Quaternionf sensor_orientation_
Sensor acquisition pose (rotation).
std::uint32_t width
The point cloud width (if organized as an image-structure).
bool isOrganized() const
Return whether a dataset is organized (e.g., arranged in a structured grid).
std::uint32_t height
The point cloud height (if organized as an image-structure).
Eigen::Vector4f sensor_origin_
Sensor acquisition pose (origin/translation).
std::vector< PointT, Eigen::aligned_allocator< PointT > > points
The point data.
shared_ptr< const PointCloud< PointT > > ConstPtr
vtkSmartPointer< vtkLODActor > actor
The actor holding the data to render.
bool addPointCloudIntensityGradients(const typename pcl::PointCloud< PointT >::ConstPtr &cloud, const typename pcl::PointCloud< GradientT >::ConstPtr &gradients, int level=100, double scale=1e-6, const std::string &id="cloud", int viewport=0)
Add the estimated surface intensity gradients of a Point Cloud to screen.
bool addPolygonMesh(const pcl::PolygonMesh &polymesh, const std::string &id="polygon", int viewport=0)
Add a PolygonMesh object to screen.
bool addSphere(const PointT ¢er, double radius, const std::string &id="sphere", int viewport=0)
Add a sphere shape from a point and a radius.
bool addPointCloud(const typename pcl::PointCloud< PointT >::ConstPtr &cloud, const std::string &id="cloud", int viewport=0)
Add a Point Cloud (templated) to screen.
bool addCorrespondences(const typename pcl::PointCloud< PointT >::ConstPtr &source_points, const typename pcl::PointCloud< PointT >::ConstPtr &target_points, const std::vector< int > &correspondences, const std::string &id="correspondences", int viewport=0)
Add the specified correspondences to the display.
bool addPolygon(const typename pcl::PointCloud< PointT >::ConstPtr &cloud, double r, double g, double b, const std::string &id="polygon", int viewport=0)
Add a polygon (polyline) that represents the input point cloud (connects all points in order)
bool updatePointCloud(const typename pcl::PointCloud< PointT >::ConstPtr &cloud, const std::string &id="cloud")
Updates the XYZ data for an existing cloud object id on screen.
GeometryHandler::ConstPtr GeometryHandlerConstPtr
bool addPointCloudNormals(const typename pcl::PointCloud< PointNT >::ConstPtr &cloud, int level=100, float scale=0.02f, const std::string &id="cloud", int viewport=0)
Add the estimated surface normals of a Point Cloud to screen.
bool addText3D(const std::string &text, const PointT &position, double textScale=1.0, double r=1.0, double g=1.0, double b=1.0, const std::string &id="", int viewport=0)
Add a 3d text to the scene.
bool updateSphere(const PointT ¢er, double radius, double r, double g, double b, const std::string &id="sphere")
Update an existing sphere shape from a point and a radius.
ColorHandler::ConstPtr ColorHandlerConstPtr
bool addArrow(const P1 &pt1, const P2 &pt2, double r, double g, double b, const std::string &id="arrow", int viewport=0)
Add a line arrow segment between two points, and display the distance between them.
bool addLine(const P1 &pt1, const P2 &pt2, const std::string &id="line", int viewport=0)
Add a line segment from two points.
bool addPointCloudPrincipalCurvatures(const typename pcl::PointCloud< PointNT >::ConstPtr &cloud, const typename pcl::PointCloud< pcl::PrincipalCurvatures >::ConstPtr &pcs, int level=100, float scale=1.0f, const std::string &id="cloud", int viewport=0)
Add the estimated principal curvatures of a Point Cloud to screen.
bool updatePolygonMesh(const typename pcl::PointCloud< PointT >::ConstPtr &cloud, const std::vector< pcl::Vertices > &vertices, const std::string &id="polygon")
Update a PolygonMesh object on screen.
bool updateCorrespondences(const typename pcl::PointCloud< PointT >::ConstPtr &source_points, const typename pcl::PointCloud< PointT >::ConstPtr &target_points, const pcl::Correspondences &correspondences, int nth, const std::string &id="correspondences", int viewport=0)
Update the specified correspondences to the display.
Handler for predefined user colors.
Base Handler class for PointCloud colors.
bool isCapable() const
Check if this handler is capable of handling the input data or not.
virtual std::string getName() const =0
Abstract getName method.
virtual vtkSmartPointer< vtkDataArray > getColor() const =0
Obtain the actual color for the input dataset as a VTK data array.
RGB handler class for colors.
Base handler class for PointCloud geometry.
virtual std::string getName() const =0
Abstract getName method.
bool isCapable() const
Check if this handler is capable of handling the input data or not.
virtual void getGeometry(vtkSmartPointer< vtkPoints > &points) const =0
Obtain the actual point geometry for the input dataset in VTK format.
XYZ handler class for PointCloud geometry.
PCL_EXPORTS vtkSmartPointer< vtkDataSet > createLine(const Eigen::Vector4f &pt1, const Eigen::Vector4f &pt2)
Create a line shape from two points.
PCL_EXPORTS void print_error(const char *format,...)
Print an error message on stream with colors.
void ignore(const T &...)
Utility function to eliminate unused variable warnings.
PCL_EXPORTS vtkIdType fillCells(std::vector< int > &lookup, const std::vector< pcl::Vertices > &vertices, vtkSmartPointer< vtkCellArray > cell_array, int max_size_of_polygon)
PCL_EXPORTS void allocVtkUnstructuredGrid(vtkSmartPointer< vtkUnstructuredGrid > &polydata)
Allocate a new unstructured grid smartpointer.
bool isFinite(const PointT &pt)
Tests if the 3D components of a point are all finite param[in] pt point to be tested return true if f...
static constexpr index_t UNAVAILABLE
std::vector< pcl::Correspondence, Eigen::aligned_allocator< pcl::Correspondence > > Correspondences
constexpr bool isNormalFinite(const PointT &) noexcept
Define methods or creating 3D shapes from parametric models.
A point structure representing Euclidean xyz coordinates, and the RGB color.
A structure representing RGB color information.