Point Cloud Library (PCL)  1.7.1
pcl_visualizer.hpp
1 /*
2  * Software License Agreement (BSD License)
3  *
4  * Point Cloud Library (PCL) - www.pointclouds.org
5  * Copyright (c) 2012, Open Perception, Inc.
6  *
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * * Redistributions of source code must retain the above copyright
14  * notice, this list of conditions and the following disclaimer.
15  * * Redistributions in binary form must reproduce the above
16  * copyright notice, this list of conditions and the following
17  * disclaimer in the documentation and/or other materials provided
18  * with the distribution.
19  * * Neither the name of the copyright holder(s) nor the names of its
20  * contributors may be used to endorse or promote products derived
21  * from this software without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
33  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34  * POSSIBILITY OF SUCH DAMAGE.
35  *
36  */
37 
38 #ifndef PCL_PCL_VISUALIZER_IMPL_H_
39 #define PCL_PCL_VISUALIZER_IMPL_H_
40 
41 #include <vtkSmartPointer.h>
42 #include <vtkCellArray.h>
43 #include <vtkLeaderActor2D.h>
44 #include <vtkVectorText.h>
45 #include <vtkAlgorithmOutput.h>
46 #include <vtkFollower.h>
47 #include <vtkSphereSource.h>
48 #include <vtkProperty2D.h>
49 #include <vtkDataSetSurfaceFilter.h>
50 #include <vtkPointData.h>
51 #include <vtkPolyDataMapper.h>
52 #include <vtkProperty.h>
53 #include <vtkMapper.h>
54 #include <vtkCellData.h>
55 #include <vtkDataSetMapper.h>
56 #include <vtkRenderer.h>
57 #include <vtkRendererCollection.h>
58 #include <vtkAppendPolyData.h>
59 #include <vtkTextProperty.h>
60 #include <vtkLODActor.h>
61 
62 #include <pcl/visualization/common/shapes.h>
63 
64 //////////////////////////////////////////////////////////////////////////////////////////////
65 template <typename PointT> bool
67  const typename pcl::PointCloud<PointT>::ConstPtr &cloud,
68  const std::string &id, int viewport)
69 {
70  // Convert the PointCloud to VTK PolyData
71  PointCloudGeometryHandlerXYZ<PointT> geometry_handler (cloud);
72  return (addPointCloud<PointT> (cloud, geometry_handler, id, viewport));
73 }
74 
75 //////////////////////////////////////////////////////////////////////////////////////////////
76 template <typename PointT> bool
78  const typename pcl::PointCloud<PointT>::ConstPtr &cloud,
79  const PointCloudGeometryHandler<PointT> &geometry_handler,
80  const std::string &id, int viewport)
81 {
82  // Check to see if this ID entry already exists (has it been already added to the visualizer?)
83  CloudActorMap::iterator am_it = cloud_actor_map_->find (id);
84 
85  if (am_it != cloud_actor_map_->end ())
86  {
87  PCL_WARN ("[addPointCloud] A PointCloud with id <%s> already exists! Please choose a different id and retry.\n", id.c_str ());
88  return (false);
89  }
90 
91  //PointCloudColorHandlerRandom<PointT> color_handler (cloud);
92  PointCloudColorHandlerCustom<PointT> color_handler (cloud, 255, 255, 255);
93  return (fromHandlersToScreen (geometry_handler, color_handler, id, viewport, cloud->sensor_origin_, cloud->sensor_orientation_));
94 }
95 
96 //////////////////////////////////////////////////////////////////////////////////////////////
97 template <typename PointT> bool
99  const typename pcl::PointCloud<PointT>::ConstPtr &cloud,
100  const GeometryHandlerConstPtr &geometry_handler,
101  const std::string &id, int viewport)
102 {
103  // Check to see if this ID entry already exists (has it been already added to the visualizer?)
104  CloudActorMap::iterator am_it = cloud_actor_map_->find (id);
105 
106  if (am_it != cloud_actor_map_->end ())
107  {
108  // Here we're just pushing the handlers onto the queue. If needed, something fancier could
109  // be done such as checking if a specific handler already exists, etc.
110  am_it->second.geometry_handlers.push_back (geometry_handler);
111  return (true);
112  }
113 
114  //PointCloudColorHandlerRandom<PointT> color_handler (cloud);
115  PointCloudColorHandlerCustom<PointT> color_handler (cloud, 255, 255, 255);
116  return (fromHandlersToScreen (geometry_handler, color_handler, id, viewport, cloud->sensor_origin_, cloud->sensor_orientation_));
117 }
118 
119 //////////////////////////////////////////////////////////////////////////////////////////////
120 template <typename PointT> bool
122  const typename pcl::PointCloud<PointT>::ConstPtr &cloud,
123  const PointCloudColorHandler<PointT> &color_handler,
124  const std::string &id, int viewport)
125 {
126  // Check to see if this ID entry already exists (has it been already added to the visualizer?)
127  CloudActorMap::iterator am_it = cloud_actor_map_->find (id);
128 
129  if (am_it != cloud_actor_map_->end ())
130  {
131  PCL_WARN ("[addPointCloud] A PointCloud with id <%s> already exists! Please choose a different id and retry.\n", id.c_str ());
132 
133  // Here we're just pushing the handlers onto the queue. If needed, something fancier could
134  // be done such as checking if a specific handler already exists, etc.
135  //cloud_actor_map_[id].color_handlers.push_back (color_handler);
136  //style_->setCloudActorMap (boost::make_shared<CloudActorMap> (cloud_actor_map_));
137  return (false);
138  }
139  // Convert the PointCloud to VTK PolyData
140  PointCloudGeometryHandlerXYZ<PointT> geometry_handler (cloud);
141  return (fromHandlersToScreen (geometry_handler, color_handler, id, viewport, cloud->sensor_origin_, cloud->sensor_orientation_));
142 }
143 
144 //////////////////////////////////////////////////////////////////////////////////////////////
145 template <typename PointT> bool
147  const typename pcl::PointCloud<PointT>::ConstPtr &cloud,
148  const ColorHandlerConstPtr &color_handler,
149  const std::string &id, int viewport)
150 {
151  // Check to see if this entry already exists (has it been already added to the visualizer?)
152  CloudActorMap::iterator am_it = cloud_actor_map_->find (id);
153  if (am_it != cloud_actor_map_->end ())
154  {
155  // Here we're just pushing the handlers onto the queue. If needed, something fancier could
156  // be done such as checking if a specific handler already exists, etc.
157  am_it->second.color_handlers.push_back (color_handler);
158  return (true);
159  }
160 
161  PointCloudGeometryHandlerXYZ<PointT> geometry_handler (cloud);
162  return (fromHandlersToScreen (geometry_handler, color_handler, id, viewport, cloud->sensor_origin_, cloud->sensor_orientation_));
163 }
164 
165 //////////////////////////////////////////////////////////////////////////////////////////////
166 template <typename PointT> bool
168  const typename pcl::PointCloud<PointT>::ConstPtr &cloud,
169  const GeometryHandlerConstPtr &geometry_handler,
170  const ColorHandlerConstPtr &color_handler,
171  const std::string &id, int viewport)
172 {
173  // Check to see if this entry already exists (has it been already added to the visualizer?)
174  CloudActorMap::iterator am_it = cloud_actor_map_->find (id);
175  if (am_it != cloud_actor_map_->end ())
176  {
177  // Here we're just pushing the handlers onto the queue. If needed, something fancier could
178  // be done such as checking if a specific handler already exists, etc.
179  am_it->second.geometry_handlers.push_back (geometry_handler);
180  am_it->second.color_handlers.push_back (color_handler);
181  return (true);
182  }
183  return (fromHandlersToScreen (geometry_handler, color_handler, id, viewport, cloud->sensor_origin_, cloud->sensor_orientation_));
184 }
185 
186 //////////////////////////////////////////////////////////////////////////////////////////////
187 template <typename PointT> bool
189  const typename pcl::PointCloud<PointT>::ConstPtr &cloud,
190  const PointCloudColorHandler<PointT> &color_handler,
191  const PointCloudGeometryHandler<PointT> &geometry_handler,
192  const std::string &id, int viewport)
193 {
194  // Check to see if this ID entry already exists (has it been already added to the visualizer?)
195  CloudActorMap::iterator am_it = cloud_actor_map_->find (id);
196 
197  if (am_it != cloud_actor_map_->end ())
198  {
199  PCL_WARN ("[addPointCloud] A PointCloud with id <%s> already exists! Please choose a different id and retry.\n", id.c_str ());
200  // Here we're just pushing the handlers onto the queue. If needed, something fancier could
201  // be done such as checking if a specific handler already exists, etc.
202  //cloud_actor_map_[id].geometry_handlers.push_back (geometry_handler);
203  //cloud_actor_map_[id].color_handlers.push_back (color_handler);
204  //style_->setCloudActorMap (boost::make_shared<CloudActorMap> (cloud_actor_map_));
205  return (false);
206  }
207  return (fromHandlersToScreen (geometry_handler, color_handler, id, viewport, cloud->sensor_origin_, cloud->sensor_orientation_));
208 }
209 
210 //////////////////////////////////////////////////////////////////////////////////////////////
211 template <typename PointT> void
212 pcl::visualization::PCLVisualizer::convertPointCloudToVTKPolyData (
213  const typename pcl::PointCloud<PointT>::ConstPtr &cloud,
216 {
218  if (!polydata)
219  {
220  allocVtkPolyData (polydata);
222  polydata->SetVerts (vertices);
223  }
224 
225  // Create the supporting structures
226  vertices = polydata->GetVerts ();
227  if (!vertices)
229 
230  vtkIdType nr_points = cloud->points.size ();
231  // Create the point set
232  vtkSmartPointer<vtkPoints> points = polydata->GetPoints ();
233  if (!points)
234  {
236  points->SetDataTypeToFloat ();
237  polydata->SetPoints (points);
238  }
239  points->SetNumberOfPoints (nr_points);
240 
241  // Get a pointer to the beginning of the data array
242  float *data = (static_cast<vtkFloatArray*> (points->GetData ()))->GetPointer (0);
243 
244  // Set the points
245  if (cloud->is_dense)
246  {
247  for (vtkIdType i = 0; i < nr_points; ++i)
248  memcpy (&data[i * 3], &cloud->points[i].x, 12); // sizeof (float) * 3
249  }
250  else
251  {
252  vtkIdType j = 0; // true point index
253  for (vtkIdType i = 0; i < nr_points; ++i)
254  {
255  // Check if the point is invalid
256  if (!pcl_isfinite (cloud->points[i].x) ||
257  !pcl_isfinite (cloud->points[i].y) ||
258  !pcl_isfinite (cloud->points[i].z))
259  continue;
260 
261  memcpy (&data[j * 3], &cloud->points[i].x, 12); // sizeof (float) * 3
262  j++;
263  }
264  nr_points = j;
265  points->SetNumberOfPoints (nr_points);
266  }
267 
268  vtkSmartPointer<vtkIdTypeArray> cells = vertices->GetData ();
269  updateCells (cells, initcells, nr_points);
270 
271  // Set the cells and the vertices
272  vertices->SetCells (nr_points, cells);
273 }
274 
275 //////////////////////////////////////////////////////////////////////////////////////////////
276 template <typename PointT> void
277 pcl::visualization::PCLVisualizer::convertPointCloudToVTKPolyData (
281 {
283  if (!polydata)
284  {
285  allocVtkPolyData (polydata);
287  polydata->SetVerts (vertices);
288  }
289 
290  // Use the handler to obtain the geometry
292  geometry_handler.getGeometry (points);
293  polydata->SetPoints (points);
294 
295  vtkIdType nr_points = points->GetNumberOfPoints ();
296 
297  // Create the supporting structures
298  vertices = polydata->GetVerts ();
299  if (!vertices)
301 
302  vtkSmartPointer<vtkIdTypeArray> cells = vertices->GetData ();
303  updateCells (cells, initcells, nr_points);
304  // Set the cells and the vertices
305  vertices->SetCells (nr_points, cells);
306 }
307 
308 ////////////////////////////////////////////////////////////////////////////////////////////
309 template <typename PointT> bool
311  const typename pcl::PointCloud<PointT>::ConstPtr &cloud,
312  double r, double g, double b, const std::string &id, int viewport)
313 {
314  vtkSmartPointer<vtkDataSet> data = createPolygon<PointT> (cloud);
315  if (!data)
316  return (false);
317 
318  // Check to see if this ID entry already exists (has it been already added to the visualizer?)
319  ShapeActorMap::iterator am_it = shape_actor_map_->find (id);
320  if (am_it != shape_actor_map_->end ())
321  {
323 
324  // Add old data
325  all_data->AddInput (reinterpret_cast<vtkPolyDataMapper*> ((vtkActor::SafeDownCast (am_it->second))->GetMapper ())->GetInput ());
326 
327  // Add new data
329  surface_filter->SetInput (vtkUnstructuredGrid::SafeDownCast (data));
330  vtkSmartPointer<vtkPolyData> poly_data = surface_filter->GetOutput ();
331  all_data->AddInput (poly_data);
332 
333  // Create an Actor
335  createActorFromVTKDataSet (all_data->GetOutput (), actor);
336  actor->GetProperty ()->SetRepresentationToWireframe ();
337  actor->GetProperty ()->SetColor (r, g, b);
338  actor->GetMapper ()->ScalarVisibilityOff ();
339  removeActorFromRenderer (am_it->second, viewport);
340  addActorToRenderer (actor, viewport);
341 
342  // Save the pointer/ID pair to the global actor map
343  (*shape_actor_map_)[id] = actor;
344  }
345  else
346  {
347  // Create an Actor
349  createActorFromVTKDataSet (data, actor);
350  actor->GetProperty ()->SetRepresentationToWireframe ();
351  actor->GetProperty ()->SetColor (r, g, b);
352  actor->GetMapper ()->ScalarVisibilityOff ();
353  addActorToRenderer (actor, viewport);
354 
355  // Save the pointer/ID pair to the global actor map
356  (*shape_actor_map_)[id] = actor;
357  }
358 
359  return (true);
360 }
361 
362 ////////////////////////////////////////////////////////////////////////////////////////////
363 template <typename PointT> bool
365  const pcl::PlanarPolygon<PointT> &polygon,
366  double r, double g, double b, const std::string &id, int viewport)
367 {
368  vtkSmartPointer<vtkDataSet> data = createPolygon<PointT> (polygon);
369  if (!data)
370  return (false);
371 
372  // Check to see if this ID entry already exists (has it been already added to the visualizer?)
373  ShapeActorMap::iterator am_it = shape_actor_map_->find (id);
374  if (am_it != shape_actor_map_->end ())
375  {
377 
378  // Add old data
379  all_data->AddInput (reinterpret_cast<vtkPolyDataMapper*> ((vtkActor::SafeDownCast (am_it->second))->GetMapper ())->GetInput ());
380 
381  // Add new data
383  surface_filter->SetInput (vtkUnstructuredGrid::SafeDownCast (data));
384  vtkSmartPointer<vtkPolyData> poly_data = surface_filter->GetOutput ();
385  all_data->AddInput (poly_data);
386 
387  // Create an Actor
389  createActorFromVTKDataSet (all_data->GetOutput (), actor);
390  actor->GetProperty ()->SetRepresentationToWireframe ();
391  actor->GetProperty ()->SetColor (r, g, b);
392  actor->GetMapper ()->ScalarVisibilityOn ();
393  actor->GetProperty ()->BackfaceCullingOff ();
394  removeActorFromRenderer (am_it->second, viewport);
395  addActorToRenderer (actor, viewport);
396 
397  // Save the pointer/ID pair to the global actor map
398  (*shape_actor_map_)[id] = actor;
399  }
400  else
401  {
402  // Create an Actor
404  createActorFromVTKDataSet (data, actor);
405  actor->GetProperty ()->SetRepresentationToWireframe ();
406  actor->GetProperty ()->SetColor (r, g, b);
407  actor->GetMapper ()->ScalarVisibilityOn ();
408  actor->GetProperty ()->BackfaceCullingOff ();
409  addActorToRenderer (actor, viewport);
410 
411  // Save the pointer/ID pair to the global actor map
412  (*shape_actor_map_)[id] = actor;
413  }
414  return (true);
415 }
416 
417 ////////////////////////////////////////////////////////////////////////////////////////////
418 template <typename PointT> bool
420  const typename pcl::PointCloud<PointT>::ConstPtr &cloud,
421  const std::string &id, int viewport)
422 {
423  return (!addPolygon<PointT> (cloud, 0.5, 0.5, 0.5, id, viewport));
424 }
425 
426 ////////////////////////////////////////////////////////////////////////////////////////////
427 template <typename P1, typename P2> bool
428 pcl::visualization::PCLVisualizer::addLine (const P1 &pt1, const P2 &pt2, double r, double g, double b, const std::string &id, int viewport)
429 {
430  // Check to see if this ID entry already exists (has it been already added to the visualizer?)
431  ShapeActorMap::iterator am_it = shape_actor_map_->find (id);
432  if (am_it != shape_actor_map_->end ())
433  {
434  PCL_WARN ("[addLine] A shape with id <%s> already exists! Please choose a different id and retry.\n", id.c_str ());
435  return (false);
436  }
437 
438  vtkSmartPointer<vtkDataSet> data = createLine (pt1.getVector4fMap (), pt2.getVector4fMap ());
439 
440  // Create an Actor
442  createActorFromVTKDataSet (data, actor);
443  actor->GetProperty ()->SetRepresentationToWireframe ();
444  actor->GetProperty ()->SetColor (r, g, b);
445  actor->GetMapper ()->ScalarVisibilityOff ();
446  addActorToRenderer (actor, viewport);
447 
448  // Save the pointer/ID pair to the global actor map
449  (*shape_actor_map_)[id] = actor;
450  return (true);
451 }
452 
453 ////////////////////////////////////////////////////////////////////////////////////////////
454 template <typename P1, typename P2> bool
455 pcl::visualization::PCLVisualizer::addArrow (const P1 &pt1, const P2 &pt2, double r, double g, double b, const std::string &id, int viewport)
456 {
457  // Check to see if this ID entry already exists (has it been already added to the visualizer?)
458  ShapeActorMap::iterator am_it = shape_actor_map_->find (id);
459  if (am_it != shape_actor_map_->end ())
460  {
461  PCL_WARN ("[addArrow] A shape with id <%s> already exists! Please choose a different id and retry.\n", id.c_str ());
462  return (false);
463  }
464 
465  // Create an Actor
467  leader->GetPositionCoordinate ()->SetCoordinateSystemToWorld ();
468  leader->GetPositionCoordinate ()->SetValue (pt1.x, pt1.y, pt1.z);
469  leader->GetPosition2Coordinate ()->SetCoordinateSystemToWorld ();
470  leader->GetPosition2Coordinate ()->SetValue (pt2.x, pt2.y, pt2.z);
471  leader->SetArrowStyleToFilled ();
472  leader->AutoLabelOn ();
473 
474  leader->GetProperty ()->SetColor (r, g, b);
475  addActorToRenderer (leader, viewport);
476 
477  // Save the pointer/ID pair to the global actor map
478  (*shape_actor_map_)[id] = leader;
479  return (true);
480 }
481 
482 ////////////////////////////////////////////////////////////////////////////////////////////
483 template <typename P1, typename P2> bool
484 pcl::visualization::PCLVisualizer::addArrow (const P1 &pt1, const P2 &pt2, double r, double g, double b, bool display_length, const std::string &id, int viewport)
485 {
486  // Check to see if this ID entry already exists (has it been already added to the visualizer?)
487  ShapeActorMap::iterator am_it = shape_actor_map_->find (id);
488  if (am_it != shape_actor_map_->end ())
489  {
490  PCL_WARN ("[addArrow] A shape with id <%s> already exists! Please choose a different id and retry.\n", id.c_str ());
491  return (false);
492  }
493 
494  // Create an Actor
496  leader->GetPositionCoordinate ()->SetCoordinateSystemToWorld ();
497  leader->GetPositionCoordinate ()->SetValue (pt1.x, pt1.y, pt1.z);
498  leader->GetPosition2Coordinate ()->SetCoordinateSystemToWorld ();
499  leader->GetPosition2Coordinate ()->SetValue (pt2.x, pt2.y, pt2.z);
500  leader->SetArrowStyleToFilled ();
501  leader->SetArrowPlacementToPoint1 ();
502  if (display_length)
503  leader->AutoLabelOn ();
504  else
505  leader->AutoLabelOff ();
506 
507  leader->GetProperty ()->SetColor (r, g, b);
508  addActorToRenderer (leader, viewport);
509 
510  // Save the pointer/ID pair to the global actor map
511  (*shape_actor_map_)[id] = leader;
512  return (true);
513 }
514 ////////////////////////////////////////////////////////////////////////////////////////////
515 template <typename P1, typename P2> bool
516 pcl::visualization::PCLVisualizer::addArrow (const P1 &pt1, const P2 &pt2,
517  double r_line, double g_line, double b_line,
518  double r_text, double g_text, double b_text,
519  const std::string &id, int viewport)
520 {
521  // Check to see if this ID entry already exists (has it been already added to the visualizer?)
522  ShapeActorMap::iterator am_it = shape_actor_map_->find (id);
523  if (am_it != shape_actor_map_->end ())
524  {
525  PCL_WARN ("[addArrow] A shape with id <%s> already exists! Please choose a different id and retry.\n", id.c_str ());
526  return (false);
527  }
528 
529  // Create an Actor
531  leader->GetPositionCoordinate ()->SetCoordinateSystemToWorld ();
532  leader->GetPositionCoordinate ()->SetValue (pt1.x, pt1.y, pt1.z);
533  leader->GetPosition2Coordinate ()->SetCoordinateSystemToWorld ();
534  leader->GetPosition2Coordinate ()->SetValue (pt2.x, pt2.y, pt2.z);
535  leader->SetArrowStyleToFilled ();
536  leader->AutoLabelOn ();
537 
538  leader->GetLabelTextProperty()->SetColor(r_text, g_text, b_text);
539 
540  leader->GetProperty ()->SetColor (r_line, g_line, b_line);
541  addActorToRenderer (leader, viewport);
542 
543  // Save the pointer/ID pair to the global actor map
544  (*shape_actor_map_)[id] = leader;
545  return (true);
546 }
547 
548 ////////////////////////////////////////////////////////////////////////////////////////////
549 template <typename P1, typename P2> bool
550 pcl::visualization::PCLVisualizer::addLine (const P1 &pt1, const P2 &pt2, const std::string &id, int viewport)
551 {
552  return (!addLine (pt1, pt2, 0.5, 0.5, 0.5, id, viewport));
553 }
554 
555 ////////////////////////////////////////////////////////////////////////////////////////////
556 template <typename PointT> bool
557 pcl::visualization::PCLVisualizer::addSphere (const PointT &center, double radius, double r, double g, double b, const std::string &id, int viewport)
558 {
559  // Check to see if this ID entry already exists (has it been already added to the visualizer?)
560  ShapeActorMap::iterator am_it = shape_actor_map_->find (id);
561  if (am_it != shape_actor_map_->end ())
562  {
563  PCL_WARN ("[addSphere] A shape with id <%s> already exists! Please choose a different id and retry.\n", id.c_str ());
564  return (false);
565  }
566 
568  data->SetRadius (radius);
569  data->SetCenter (double (center.x), double (center.y), double (center.z));
570  data->SetPhiResolution (10);
571  data->SetThetaResolution (10);
572  data->LatLongTessellationOff ();
573  data->Update ();
574 
575  // Setup actor and mapper
577  mapper->SetInputConnection (data->GetOutputPort ());
578 
579  // Create an Actor
581  actor->SetMapper (mapper);
582  //createActorFromVTKDataSet (data, actor);
583  actor->GetProperty ()->SetRepresentationToSurface ();
584  actor->GetProperty ()->SetInterpolationToFlat ();
585  actor->GetProperty ()->SetColor (r, g, b);
586  actor->GetMapper ()->ImmediateModeRenderingOn ();
587  actor->GetMapper ()->StaticOn ();
588  actor->GetMapper ()->ScalarVisibilityOff ();
589  actor->GetMapper ()->Update ();
590  addActorToRenderer (actor, viewport);
591 
592  // Save the pointer/ID pair to the global actor map
593  (*shape_actor_map_)[id] = actor;
594  return (true);
595 }
596 
597 ////////////////////////////////////////////////////////////////////////////////////////////
598 template <typename PointT> bool
599 pcl::visualization::PCLVisualizer::addSphere (const PointT &center, double radius, const std::string &id, int viewport)
600 {
601  return (addSphere (center, radius, 0.5, 0.5, 0.5, id, viewport));
602 }
603 
604 ////////////////////////////////////////////////////////////////////////////////////////////
605 template<typename PointT> bool
606 pcl::visualization::PCLVisualizer::updateSphere (const PointT &center, double radius, double r, double g, double b, const std::string &id)
607 {
608  // Check to see if this ID entry already exists (has it been already added to the visualizer?)
609  ShapeActorMap::iterator am_it = shape_actor_map_->find (id);
610  if (am_it == shape_actor_map_->end ())
611  return (false);
612 
613  //////////////////////////////////////////////////////////////////////////
614  // Get the actor pointer
615  vtkLODActor* actor = vtkLODActor::SafeDownCast (am_it->second);
616  vtkAlgorithm *algo = actor->GetMapper ()->GetInput ()->GetProducerPort ()->GetProducer ();
617  vtkSphereSource *src = vtkSphereSource::SafeDownCast (algo);
618 
619  src->SetCenter (double (center.x), double (center.y), double (center.z));
620  src->SetRadius (radius);
621  src->Update ();
622  actor->GetProperty ()->SetColor (r, g, b);
623  actor->Modified ();
624 
625  return (true);
626 }
627 
628 //////////////////////////////////////////////////
629 template <typename PointT> bool
631  const std::string &text,
632  const PointT& position,
633  double textScale,
634  double r,
635  double g,
636  double b,
637  const std::string &id,
638  int viewport)
639 {
640  std::string tid;
641  if (id.empty ())
642  tid = text;
643  else
644  tid = id;
645 
646  // Check to see if this ID entry already exists (has it been already added to the visualizer?)
647  ShapeActorMap::iterator am_it = shape_actor_map_->find (tid);
648  if (am_it != shape_actor_map_->end ())
649  {
650  pcl::console::print_warn (stderr, "[addText3d] A text with id <%s> already exists! Please choose a different id and retry.\n", tid.c_str ());
651  return (false);
652  }
653 
655  textSource->SetText (text.c_str());
656  textSource->Update ();
657 
659  textMapper->SetInputConnection (textSource->GetOutputPort ());
660 
661  // Since each follower may follow a different camera, we need different followers
662  rens_->InitTraversal ();
663  vtkRenderer* renderer = NULL;
664  int i = 1;
665  while ((renderer = rens_->GetNextItem ()) != NULL)
666  {
667  // Should we add the actor to all renderers or just to i-nth renderer?
668  if (viewport == 0 || viewport == i)
669  {
671  textActor->SetMapper (textMapper);
672  textActor->SetPosition (position.x, position.y, position.z);
673  textActor->SetScale (textScale);
674  textActor->GetProperty ()->SetColor (r, g, b);
675  textActor->SetCamera (renderer->GetActiveCamera ());
676 
677  renderer->AddActor (textActor);
678  renderer->Render ();
679 
680  // Save the pointer/ID pair to the global actor map. If we are saving multiple vtkFollowers
681  // for multiple viewport
682  std::string alternate_tid = tid;
683  alternate_tid.append(i, '*');
684 
685  (*shape_actor_map_)[(viewport == 0) ? tid : alternate_tid] = textActor;
686  }
687 
688  ++i;
689  }
690 
691  return (true);
692 }
693 
694 //////////////////////////////////////////////////////////////////////////////////////////////
695 template <typename PointNT> bool
697  const typename pcl::PointCloud<PointNT>::ConstPtr &cloud,
698  int level, float scale, const std::string &id, int viewport)
699 {
700  return (addPointCloudNormals<PointNT, PointNT> (cloud, cloud, level, scale, id, viewport));
701 }
702 
703 //////////////////////////////////////////////////////////////////////////////////////////////
704 template <typename PointT, typename PointNT> bool
706  const typename pcl::PointCloud<PointT>::ConstPtr &cloud,
707  const typename pcl::PointCloud<PointNT>::ConstPtr &normals,
708  int level, float scale,
709  const std::string &id, int viewport)
710 {
711  if (normals->points.size () != cloud->points.size ())
712  {
713  PCL_ERROR ("[addPointCloudNormals] The number of points differs from the number of normals!\n");
714  return (false);
715  }
716  // Check to see if this ID entry already exists (has it been already added to the visualizer?)
717  CloudActorMap::iterator am_it = cloud_actor_map_->find (id);
718 
719  if (am_it != cloud_actor_map_->end ())
720  {
721  PCL_WARN ("[addPointCloudNormals] A PointCloud with id <%s> already exists! Please choose a different id and retry.\n", id.c_str ());
722  return (false);
723  }
724 
727 
728  points->SetDataTypeToFloat ();
730  data->SetNumberOfComponents (3);
731 
732 
733  vtkIdType nr_normals = 0;
734  float* pts = 0;
735 
736  // If the cloud is organized, then distribute the normal step in both directions
737  if (cloud->isOrganized () && normals->isOrganized ())
738  {
739  vtkIdType point_step = static_cast<vtkIdType> (sqrt (double (level)));
740  nr_normals = (static_cast<vtkIdType> ((cloud->width - 1)/ point_step) + 1) *
741  (static_cast<vtkIdType> ((cloud->height - 1) / point_step) + 1);
742  pts = new float[2 * nr_normals * 3];
743 
744  vtkIdType cell_count = 0;
745  for (vtkIdType y = 0; y < normals->height; y += point_step)
746  for (vtkIdType x = 0; x < normals->width; x += point_step)
747  {
748  PointT p = (*cloud)(x, y);
749  p.x += (*normals)(x, y).normal[0] * scale;
750  p.y += (*normals)(x, y).normal[1] * scale;
751  p.z += (*normals)(x, y).normal[2] * scale;
752 
753  pts[2 * cell_count * 3 + 0] = (*cloud)(x, y).x;
754  pts[2 * cell_count * 3 + 1] = (*cloud)(x, y).y;
755  pts[2 * cell_count * 3 + 2] = (*cloud)(x, y).z;
756  pts[2 * cell_count * 3 + 3] = p.x;
757  pts[2 * cell_count * 3 + 4] = p.y;
758  pts[2 * cell_count * 3 + 5] = p.z;
759 
760  lines->InsertNextCell (2);
761  lines->InsertCellPoint (2 * cell_count);
762  lines->InsertCellPoint (2 * cell_count + 1);
763  cell_count ++;
764  }
765  }
766  else
767  {
768  nr_normals = (cloud->points.size () - 1) / level + 1 ;
769  pts = new float[2 * nr_normals * 3];
770 
771  for (vtkIdType i = 0, j = 0; j < nr_normals; j++, i = j * level)
772  {
773  PointT p = cloud->points[i];
774  p.x += normals->points[i].normal[0] * scale;
775  p.y += normals->points[i].normal[1] * scale;
776  p.z += normals->points[i].normal[2] * scale;
777 
778  pts[2 * j * 3 + 0] = cloud->points[i].x;
779  pts[2 * j * 3 + 1] = cloud->points[i].y;
780  pts[2 * j * 3 + 2] = cloud->points[i].z;
781  pts[2 * j * 3 + 3] = p.x;
782  pts[2 * j * 3 + 4] = p.y;
783  pts[2 * j * 3 + 5] = p.z;
784 
785  lines->InsertNextCell (2);
786  lines->InsertCellPoint (2 * j);
787  lines->InsertCellPoint (2 * j + 1);
788  }
789  }
790 
791  data->SetArray (&pts[0], 2 * nr_normals * 3, 0);
792  points->SetData (data);
793 
795  polyData->SetPoints (points);
796  polyData->SetLines (lines);
797 
799  mapper->SetInput (polyData);
800  mapper->SetColorModeToMapScalars();
801  mapper->SetScalarModeToUsePointData();
802 
803  // create actor
805  actor->SetMapper (mapper);
806 
807  // Add it to all renderers
808  addActorToRenderer (actor, viewport);
809 
810  // Save the pointer/ID pair to the global actor map
811  (*cloud_actor_map_)[id].actor = actor;
812  return (true);
813 }
814 
815 //////////////////////////////////////////////////////////////////////////////////////////////
816 template <typename PointT, typename GradientT> bool
818  const typename pcl::PointCloud<PointT>::ConstPtr &cloud,
819  const typename pcl::PointCloud<GradientT>::ConstPtr &gradients,
820  int level, double scale,
821  const std::string &id, int viewport)
822 {
823  if (gradients->points.size () != cloud->points.size ())
824  {
825  PCL_ERROR ("[addPointCloudGradients] The number of points differs from the number of gradients!\n");
826  return (false);
827  }
828  // Check to see if this ID entry already exists (has it been already added to the visualizer?)
829  CloudActorMap::iterator am_it = cloud_actor_map_->find (id);
830 
831  if (am_it != cloud_actor_map_->end ())
832  {
833  PCL_WARN ("[addPointCloudGradients] A PointCloud with id <%s> already exists! Please choose a different id and retry.\n", id.c_str ());
834  return (false);
835  }
836 
839 
840  points->SetDataTypeToFloat ();
842  data->SetNumberOfComponents (3);
843 
844  vtkIdType nr_gradients = (cloud->points.size () - 1) / level + 1 ;
845  float* pts = new float[2 * nr_gradients * 3];
846 
847  for (vtkIdType i = 0, j = 0; j < nr_gradients; j++, i = j * level)
848  {
849  PointT p = cloud->points[i];
850  p.x += gradients->points[i].gradient[0] * scale;
851  p.y += gradients->points[i].gradient[1] * scale;
852  p.z += gradients->points[i].gradient[2] * scale;
853 
854  pts[2 * j * 3 + 0] = cloud->points[i].x;
855  pts[2 * j * 3 + 1] = cloud->points[i].y;
856  pts[2 * j * 3 + 2] = cloud->points[i].z;
857  pts[2 * j * 3 + 3] = p.x;
858  pts[2 * j * 3 + 4] = p.y;
859  pts[2 * j * 3 + 5] = p.z;
860 
861  lines->InsertNextCell(2);
862  lines->InsertCellPoint(2*j);
863  lines->InsertCellPoint(2*j+1);
864  }
865 
866  data->SetArray (&pts[0], 2 * nr_gradients * 3, 0);
867  points->SetData (data);
868 
870  polyData->SetPoints(points);
871  polyData->SetLines(lines);
872 
874  mapper->SetInput (polyData);
875  mapper->SetColorModeToMapScalars();
876  mapper->SetScalarModeToUsePointData();
877 
878  // create actor
880  actor->SetMapper (mapper);
881 
882  // Add it to all renderers
883  addActorToRenderer (actor, viewport);
884 
885  // Save the pointer/ID pair to the global actor map
886  (*cloud_actor_map_)[id].actor = actor;
887  return (true);
888 }
889 
890 //////////////////////////////////////////////////////////////////////////////////////////////
891 template <typename PointT> bool
893  const typename pcl::PointCloud<PointT>::ConstPtr &source_points,
894  const typename pcl::PointCloud<PointT>::ConstPtr &target_points,
895  const std::vector<int> &correspondences,
896  const std::string &id,
897  int viewport)
898 {
899  // Check to see if this ID entry already exists (has it been already added to the visualizer?)
900  ShapeActorMap::iterator am_it = shape_actor_map_->find (id);
901  if (am_it != shape_actor_map_->end ())
902  {
903  PCL_WARN ("[addCorrespondences] A set of correspondences with id <%s> already exists! Please choose a different id and retry.\n", id.c_str ());
904  return (false);
905  }
906 
907  int n_corr = int (correspondences.size ());
909 
910  // Prepare colors
912  line_colors->SetNumberOfComponents (3);
913  line_colors->SetName ("Colors");
914  line_colors->SetNumberOfTuples (n_corr);
915  unsigned char* colors = line_colors->GetPointer (0);
916  memset (colors, 0, line_colors->GetNumberOfTuples () * line_colors->GetNumberOfComponents ());
917  pcl::RGB rgb;
918  // Will use random colors or RED by default
919  rgb.r = 255; rgb.g = rgb.b = 0;
920 
921  // Prepare coordinates
923  line_points->SetNumberOfPoints (2 * n_corr);
924 
926  line_cells_id->SetNumberOfComponents (3);
927  line_cells_id->SetNumberOfTuples (n_corr);
928  vtkIdType *line_cell_id = line_cells_id->GetPointer (0);
930 
932  line_tcoords->SetNumberOfComponents (1);
933  line_tcoords->SetNumberOfTuples (n_corr * 2);
934  line_tcoords->SetName ("Texture Coordinates");
935 
936  double tc[3] = {0.0, 0.0, 0.0};
937 
938  int j = 0, idx = 0;
939  // Draw lines between the best corresponding points
940  for (size_t i = 0; i < n_corr; ++i)
941  {
942  const PointT &p_src = source_points->points[i];
943  const PointT &p_tgt = target_points->points[correspondences[i]];
944 
945  int id1 = j * 2 + 0, id2 = j * 2 + 1;
946  // Set the points
947  line_points->SetPoint (id1, p_src.x, p_src.y, p_src.z);
948  line_points->SetPoint (id2, p_tgt.x, p_tgt.y, p_tgt.z);
949  // Set the cell ID
950  *line_cell_id++ = 2;
951  *line_cell_id++ = id1;
952  *line_cell_id++ = id2;
953  // Set the texture coords
954  tc[0] = 0.; line_tcoords->SetTuple (id1, tc);
955  tc[0] = 1.; line_tcoords->SetTuple (id2, tc);
956 
957  getRandomColors (rgb);
958  colors[idx+0] = rgb.r;
959  colors[idx+1] = rgb.g;
960  colors[idx+2] = rgb.b;
961  }
962  line_colors->SetNumberOfTuples (j);
963  line_cells_id->SetNumberOfTuples (j);
964  line_cells->SetCells (n_corr, line_cells_id);
965  line_points->SetNumberOfPoints (j*2);
966  line_tcoords->SetNumberOfTuples (j*2);
967 
968  // Fill in the lines
969  line_data->SetPoints (line_points);
970  line_data->SetLines (line_cells);
971  line_data->GetPointData ()->SetTCoords (line_tcoords);
972  line_data->GetCellData ()->SetScalars (line_colors);
973  line_data->Update ();
974 
975  // Create an Actor
977  createActorFromVTKDataSet (line_data, actor);
978  actor->GetProperty ()->SetRepresentationToWireframe ();
979  actor->GetProperty ()->SetOpacity (0.5);
980  addActorToRenderer (actor, viewport);
981 
982  // Save the pointer/ID pair to the global actor map
983  (*shape_actor_map_)[id] = actor;
984 
985  return (true);
986 }
987 
988 //////////////////////////////////////////////////////////////////////////////////////////////
989 template <typename PointT> bool
991  const typename pcl::PointCloud<PointT>::ConstPtr &source_points,
992  const typename pcl::PointCloud<PointT>::ConstPtr &target_points,
993  const pcl::Correspondences &correspondences,
994  int nth,
995  const std::string &id,
996  int viewport)
997 {
998  if (correspondences.empty ())
999  {
1000  PCL_DEBUG ("[addCorrespondences] An empty set of correspondences given! Nothing to display.\n");
1001  return (false);
1002  }
1003 
1004  // Check to see if this ID entry already exists (has it been already added to the visualizer?)
1005  ShapeActorMap::iterator am_it = shape_actor_map_->find (id);
1006  if (am_it != shape_actor_map_->end ())
1007  {
1008  PCL_WARN ("[addCorrespondences] A set of correspondences with id <%s> already exists! Please choose a different id and retry.\n", id.c_str ());
1009  return (false);
1010  }
1011 
1012  int n_corr = int (correspondences.size () / nth + 1);
1014 
1015  // Prepare colors
1017  line_colors->SetNumberOfComponents (3);
1018  line_colors->SetName ("Colors");
1019  line_colors->SetNumberOfTuples (n_corr);
1020  unsigned char* colors = line_colors->GetPointer (0);
1021  memset (colors, 0, line_colors->GetNumberOfTuples () * line_colors->GetNumberOfComponents ());
1022  pcl::RGB rgb;
1023  // Will use random colors or RED by default
1024  rgb.r = 255; rgb.g = rgb.b = 0;
1025 
1026  // Prepare coordinates
1028  line_points->SetNumberOfPoints (2 * n_corr);
1029 
1031  line_cells_id->SetNumberOfComponents (3);
1032  line_cells_id->SetNumberOfTuples (n_corr);
1033  vtkIdType *line_cell_id = line_cells_id->GetPointer (0);
1035 
1037  line_tcoords->SetNumberOfComponents (1);
1038  line_tcoords->SetNumberOfTuples (n_corr * 2);
1039  line_tcoords->SetName ("Texture Coordinates");
1040 
1041  double tc[3] = {0.0, 0.0, 0.0};
1042 
1043  int j = 0, idx = 0;
1044  // Draw lines between the best corresponding points
1045  for (size_t i = 0; i < correspondences.size (); i += nth, idx = j * 3, ++j)
1046  {
1047  const PointT &p_src = source_points->points[correspondences[i].index_query];
1048  const PointT &p_tgt = target_points->points[correspondences[i].index_match];
1049 
1050  int id1 = j * 2 + 0, id2 = j * 2 + 1;
1051  // Set the points
1052  line_points->SetPoint (id1, p_src.x, p_src.y, p_src.z);
1053  line_points->SetPoint (id2, p_tgt.x, p_tgt.y, p_tgt.z);
1054  // Set the cell ID
1055  *line_cell_id++ = 2;
1056  *line_cell_id++ = id1;
1057  *line_cell_id++ = id2;
1058  // Set the texture coords
1059  tc[0] = 0.; line_tcoords->SetTuple (id1, tc);
1060  tc[0] = 1.; line_tcoords->SetTuple (id2, tc);
1061 
1062  getRandomColors (rgb);
1063  colors[idx+0] = rgb.r;
1064  colors[idx+1] = rgb.g;
1065  colors[idx+2] = rgb.b;
1066  }
1067  line_colors->SetNumberOfTuples (j);
1068  line_cells_id->SetNumberOfTuples (j);
1069  line_cells->SetCells (n_corr, line_cells_id);
1070  line_points->SetNumberOfPoints (j*2);
1071  line_tcoords->SetNumberOfTuples (j*2);
1072 
1073  // Fill in the lines
1074  line_data->SetPoints (line_points);
1075  line_data->SetLines (line_cells);
1076  line_data->GetPointData ()->SetTCoords (line_tcoords);
1077  line_data->GetCellData ()->SetScalars (line_colors);
1078  line_data->Update ();
1079 
1080  // Create an Actor
1082  createActorFromVTKDataSet (line_data, actor);
1083  actor->GetProperty ()->SetRepresentationToWireframe ();
1084  actor->GetProperty ()->SetOpacity (0.5);
1085  addActorToRenderer (actor, viewport);
1086 
1087  // Save the pointer/ID pair to the global actor map
1088  (*shape_actor_map_)[id] = actor;
1089 
1090  return (true);
1091 }
1092 
1093 //////////////////////////////////////////////////////////////////////////////////////////////
1094 template <typename PointT> bool
1096  const typename pcl::PointCloud<PointT>::ConstPtr &source_points,
1097  const typename pcl::PointCloud<PointT>::ConstPtr &target_points,
1098  const pcl::Correspondences &correspondences,
1099  int nth,
1100  const std::string &id)
1101 {
1102  if (correspondences.empty ())
1103  {
1104  PCL_DEBUG ("[updateCorrespondences] An empty set of correspondences given! Nothing to display.\n");
1105  return (false);
1106  }
1107 
1108  // Check to see if this ID entry already exists (has it been already added to the visualizer?)
1109  ShapeActorMap::iterator am_it = shape_actor_map_->find (id);
1110  if (am_it == shape_actor_map_->end ())
1111  return (false);
1112 
1113  vtkSmartPointer<vtkLODActor> actor = vtkLODActor::SafeDownCast (am_it->second);
1114  vtkSmartPointer<vtkPolyData> line_data = reinterpret_cast<vtkPolyDataMapper*>(actor->GetMapper ())->GetInput ();
1115 
1116  int n_corr = int (correspondences.size () / nth + 1);
1117 
1118  // Prepare colors
1120  line_colors->SetNumberOfComponents (3);
1121  line_colors->SetName ("Colors");
1122  line_colors->SetNumberOfTuples (n_corr);
1123  unsigned char* colors = line_colors->GetPointer (0);
1124  memset (colors, 0, line_colors->GetNumberOfTuples () * line_colors->GetNumberOfComponents ());
1125  pcl::RGB rgb;
1126  // Will use random colors or RED by default
1127  rgb.r = 255.0; rgb.g = rgb.b = 0.0;
1128 
1129  // Prepare coordinates
1131  line_points->SetNumberOfPoints (2 * n_corr);
1132 
1134  line_cells_id->SetNumberOfComponents (3);
1135  line_cells_id->SetNumberOfTuples (n_corr);
1136  vtkIdType *line_cell_id = line_cells_id->GetPointer (0);
1137  vtkSmartPointer<vtkCellArray> line_cells = line_data->GetLines ();
1138 
1140  line_tcoords->SetNumberOfComponents (1);
1141  line_tcoords->SetNumberOfTuples (n_corr * 2);
1142  line_tcoords->SetName ("Texture Coordinates");
1143 
1144  double tc[3] = {0.0, 0.0, 0.0};
1145 
1146  int j = 0, idx = 0;
1147  // Draw lines between the best corresponding points
1148  for (size_t i = 0; i < correspondences.size (); i += nth, idx = j * 3, ++j)
1149  {
1150  const PointT &p_src = source_points->points[correspondences[i].index_query];
1151  const PointT &p_tgt = target_points->points[correspondences[i].index_match];
1152 
1153  int id1 = j * 2 + 0, id2 = j * 2 + 1;
1154  // Set the points
1155  line_points->SetPoint (id1, p_src.x, p_src.y, p_src.z);
1156  line_points->SetPoint (id2, p_tgt.x, p_tgt.y, p_tgt.z);
1157  // Set the cell ID
1158  *line_cell_id++ = 2;
1159  *line_cell_id++ = id1;
1160  *line_cell_id++ = id2;
1161  // Set the texture coords
1162  tc[0] = 0.; line_tcoords->SetTuple (id1, tc);
1163  tc[0] = 1.; line_tcoords->SetTuple (id2, tc);
1164 
1165  getRandomColors (rgb);
1166  colors[idx+0] = rgb.r;
1167  colors[idx+1] = rgb.g;
1168  colors[idx+2] = rgb.b;
1169  }
1170  line_colors->SetNumberOfTuples (j);
1171  line_cells_id->SetNumberOfTuples (j);
1172  line_cells->SetCells (n_corr, line_cells_id);
1173  line_points->SetNumberOfPoints (j*2);
1174  line_tcoords->SetNumberOfTuples (j*2);
1175 
1176  // Fill in the lines
1177  line_data->SetPoints (line_points);
1178  line_data->SetLines (line_cells);
1179  line_data->GetPointData ()->SetTCoords (line_tcoords);
1180  line_data->GetCellData ()->SetScalars (line_colors);
1181  line_data->Update ();
1182 
1183  // Update the mapper
1184  reinterpret_cast<vtkPolyDataMapper*>(actor->GetMapper ())->SetInput (line_data);
1185 
1186  return (true);
1187 }
1188 
1189 //////////////////////////////////////////////////////////////////////////////////////////////
1190 template <typename PointT> bool
1191 pcl::visualization::PCLVisualizer::fromHandlersToScreen (
1192  const PointCloudGeometryHandler<PointT> &geometry_handler,
1193  const PointCloudColorHandler<PointT> &color_handler,
1194  const std::string &id,
1195  int viewport,
1196  const Eigen::Vector4f& sensor_origin,
1197  const Eigen::Quaternion<float>& sensor_orientation)
1198 {
1199  if (!geometry_handler.isCapable ())
1200  {
1201  PCL_WARN ("[fromHandlersToScreen] PointCloud <%s> requested with an invalid geometry handler (%s)!\n", id.c_str (), geometry_handler.getName ().c_str ());
1202  return (false);
1203  }
1204 
1205  if (!color_handler.isCapable ())
1206  {
1207  PCL_WARN ("[fromHandlersToScreen] PointCloud <%s> requested with an invalid color handler (%s)!\n", id.c_str (), color_handler.getName ().c_str ());
1208  return (false);
1209  }
1210 
1213  // Convert the PointCloud to VTK PolyData
1214  convertPointCloudToVTKPolyData<PointT> (geometry_handler, polydata, initcells);
1215  // use the given geometry handler
1216  polydata->Update ();
1217 
1218  // Get the colors from the handler
1219  bool has_colors = false;
1220  double minmax[2];
1222  if (color_handler.getColor (scalars))
1223  {
1224  polydata->GetPointData ()->SetScalars (scalars);
1225  scalars->GetRange (minmax);
1226  has_colors = true;
1227  }
1228 
1229  // Create an Actor
1231  createActorFromVTKDataSet (polydata, actor);
1232  if (has_colors)
1233  actor->GetMapper ()->SetScalarRange (minmax);
1234 
1235  // Add it to all renderers
1236  addActorToRenderer (actor, viewport);
1237 
1238  // Save the pointer/ID pair to the global actor map
1239  CloudActor& cloud_actor = (*cloud_actor_map_)[id];
1240  cloud_actor.actor = actor;
1241  cloud_actor.cells = initcells;
1242 
1243  // Save the viewpoint transformation matrix to the global actor map
1245  convertToVtkMatrix (sensor_origin, sensor_orientation, transformation);
1246  cloud_actor.viewpoint_transformation_ = transformation;
1247  cloud_actor.actor->SetUserMatrix (transformation);
1248  cloud_actor.actor->Modified ();
1249 
1250  return (true);
1251 }
1252 
1253 //////////////////////////////////////////////////////////////////////////////////////////////
1254 template <typename PointT> bool
1255 pcl::visualization::PCLVisualizer::fromHandlersToScreen (
1256  const PointCloudGeometryHandler<PointT> &geometry_handler,
1257  const ColorHandlerConstPtr &color_handler,
1258  const std::string &id,
1259  int viewport,
1260  const Eigen::Vector4f& sensor_origin,
1261  const Eigen::Quaternion<float>& sensor_orientation)
1262 {
1263  if (!geometry_handler.isCapable ())
1264  {
1265  PCL_WARN ("[fromHandlersToScreen] PointCloud <%s> requested with an invalid geometry handler (%s)!\n", id.c_str (), geometry_handler.getName ().c_str ());
1266  return (false);
1267  }
1268 
1269  if (!color_handler->isCapable ())
1270  {
1271  PCL_WARN ("[fromHandlersToScreen] PointCloud <%s> requested with an invalid color handler (%s)!\n", id.c_str (), color_handler->getName ().c_str ());
1272  return (false);
1273  }
1274 
1277  // Convert the PointCloud to VTK PolyData
1278  convertPointCloudToVTKPolyData<PointT> (geometry_handler, polydata, initcells);
1279  // use the given geometry handler
1280  polydata->Update ();
1281 
1282  // Get the colors from the handler
1283  bool has_colors = false;
1284  double minmax[2];
1286  if (color_handler->getColor (scalars))
1287  {
1288  polydata->GetPointData ()->SetScalars (scalars);
1289  scalars->GetRange (minmax);
1290  has_colors = true;
1291  }
1292 
1293  // Create an Actor
1295  createActorFromVTKDataSet (polydata, actor);
1296  if (has_colors)
1297  actor->GetMapper ()->SetScalarRange (minmax);
1298 
1299  // Add it to all renderers
1300  addActorToRenderer (actor, viewport);
1301 
1302  // Save the pointer/ID pair to the global actor map
1303  CloudActor& cloud_actor = (*cloud_actor_map_)[id];
1304  cloud_actor.actor = actor;
1305  cloud_actor.cells = initcells;
1306  cloud_actor.color_handlers.push_back (color_handler);
1307 
1308  // Save the viewpoint transformation matrix to the global actor map
1310  convertToVtkMatrix (sensor_origin, sensor_orientation, transformation);
1311  cloud_actor.viewpoint_transformation_ = transformation;
1312  cloud_actor.actor->SetUserMatrix (transformation);
1313  cloud_actor.actor->Modified ();
1314 
1315  return (true);
1316 }
1317 
1318 //////////////////////////////////////////////////////////////////////////////////////////////
1319 template <typename PointT> bool
1320 pcl::visualization::PCLVisualizer::fromHandlersToScreen (
1321  const GeometryHandlerConstPtr &geometry_handler,
1322  const PointCloudColorHandler<PointT> &color_handler,
1323  const std::string &id,
1324  int viewport,
1325  const Eigen::Vector4f& sensor_origin,
1326  const Eigen::Quaternion<float>& sensor_orientation)
1327 {
1328  if (!geometry_handler->isCapable ())
1329  {
1330  PCL_WARN ("[fromHandlersToScreen] PointCloud <%s> requested with an invalid geometry handler (%s)!\n", id.c_str (), geometry_handler->getName ().c_str ());
1331  return (false);
1332  }
1333 
1334  if (!color_handler.isCapable ())
1335  {
1336  PCL_WARN ("[fromHandlersToScreen] PointCloud <%s> requested with an invalid color handler (%s)!\n", id.c_str (), color_handler.getName ().c_str ());
1337  return (false);
1338  }
1339 
1342  // Convert the PointCloud to VTK PolyData
1343  convertPointCloudToVTKPolyData (geometry_handler, polydata, initcells);
1344  // use the given geometry handler
1345  polydata->Update ();
1346 
1347  // Get the colors from the handler
1348  bool has_colors = false;
1349  double minmax[2];
1351  if (color_handler.getColor (scalars))
1352  {
1353  polydata->GetPointData ()->SetScalars (scalars);
1354  scalars->GetRange (minmax);
1355  has_colors = true;
1356  }
1357 
1358  // Create an Actor
1360  createActorFromVTKDataSet (polydata, actor);
1361  if (has_colors)
1362  actor->GetMapper ()->SetScalarRange (minmax);
1363 
1364  // Add it to all renderers
1365  addActorToRenderer (actor, viewport);
1366 
1367  // Save the pointer/ID pair to the global actor map
1368  CloudActor& cloud_actor = (*cloud_actor_map_)[id];
1369  cloud_actor.actor = actor;
1370  cloud_actor.cells = initcells;
1371  cloud_actor.geometry_handlers.push_back (geometry_handler);
1372 
1373  // Save the viewpoint transformation matrix to the global actor map
1375  convertToVtkMatrix (sensor_origin, sensor_orientation, transformation);
1376  cloud_actor.viewpoint_transformation_ = transformation;
1377  cloud_actor.actor->SetUserMatrix (transformation);
1378  cloud_actor.actor->Modified ();
1379 
1380  return (true);
1381 }
1382 
1383 //////////////////////////////////////////////////////////////////////////////////////////////
1384 template <typename PointT> bool
1386  const std::string &id)
1387 {
1388  // Check to see if this ID entry already exists (has it been already added to the visualizer?)
1389  CloudActorMap::iterator am_it = cloud_actor_map_->find (id);
1390 
1391  if (am_it == cloud_actor_map_->end ())
1392  return (false);
1393 
1394  vtkSmartPointer<vtkPolyData> polydata = reinterpret_cast<vtkPolyDataMapper*>(am_it->second.actor->GetMapper ())->GetInput ();
1395  // Convert the PointCloud to VTK PolyData
1396  convertPointCloudToVTKPolyData<PointT> (cloud, polydata, am_it->second.cells);
1397  polydata->Update ();
1398 
1399  // Set scalars to blank, since there is no way we can update them here.
1401  polydata->GetPointData ()->SetScalars (scalars);
1402  polydata->Update ();
1403  double minmax[2];
1404  minmax[0] = std::numeric_limits<double>::min ();
1405  minmax[1] = std::numeric_limits<double>::max ();
1406  am_it->second.actor->GetMapper ()->ImmediateModeRenderingOff ();
1407  am_it->second.actor->GetMapper ()->SetScalarRange (minmax);
1408 
1409  // Update the mapper
1410  reinterpret_cast<vtkPolyDataMapper*>(am_it->second.actor->GetMapper ())->SetInput (polydata);
1411  return (true);
1412 }
1413 
1414 /////////////////////////////////////////////////////////////////////////////////////////////
1415 template <typename PointT> bool
1417  const PointCloudGeometryHandler<PointT> &geometry_handler,
1418  const std::string &id)
1419 {
1420  // Check to see if this ID entry already exists (has it been already added to the visualizer?)
1421  CloudActorMap::iterator am_it = cloud_actor_map_->find (id);
1422 
1423  if (am_it == cloud_actor_map_->end ())
1424  return (false);
1425 
1426  vtkSmartPointer<vtkPolyData> polydata = reinterpret_cast<vtkPolyDataMapper*>(am_it->second.actor->GetMapper ())->GetInput ();
1427  if (!polydata)
1428  return (false);
1429  // Convert the PointCloud to VTK PolyData
1430  convertPointCloudToVTKPolyData (geometry_handler, polydata, am_it->second.cells);
1431 
1432  // Set scalars to blank, since there is no way we can update them here.
1434  polydata->GetPointData ()->SetScalars (scalars);
1435  polydata->Update ();
1436  double minmax[2];
1437  minmax[0] = std::numeric_limits<double>::min ();
1438  minmax[1] = std::numeric_limits<double>::max ();
1439  am_it->second.actor->GetMapper ()->ImmediateModeRenderingOff ();
1440  am_it->second.actor->GetMapper ()->SetScalarRange (minmax);
1441 
1442  // Update the mapper
1443  reinterpret_cast<vtkPolyDataMapper*>(am_it->second.actor->GetMapper ())->SetInput (polydata);
1444  return (true);
1445 }
1446 
1447 
1448 /////////////////////////////////////////////////////////////////////////////////////////////
1449 template <typename PointT> bool
1451  const PointCloudColorHandler<PointT> &color_handler,
1452  const std::string &id)
1453 {
1454  // Check to see if this ID entry already exists (has it been already added to the visualizer?)
1455  CloudActorMap::iterator am_it = cloud_actor_map_->find (id);
1456 
1457  if (am_it == cloud_actor_map_->end ())
1458  return (false);
1459 
1460  // Get the current poly data
1461  vtkSmartPointer<vtkPolyData> polydata = reinterpret_cast<vtkPolyDataMapper*>(am_it->second.actor->GetMapper ())->GetInput ();
1462  if (!polydata)
1463  return (false);
1464  vtkSmartPointer<vtkCellArray> vertices = polydata->GetVerts ();
1465  vtkSmartPointer<vtkPoints> points = polydata->GetPoints ();
1466  // Copy the new point array in
1467  vtkIdType nr_points = cloud->points.size ();
1468  points->SetNumberOfPoints (nr_points);
1469 
1470  // Get a pointer to the beginning of the data array
1471  float *data = (static_cast<vtkFloatArray*> (points->GetData ()))->GetPointer (0);
1472 
1473  int pts = 0;
1474  // If the dataset is dense (no NaNs)
1475  if (cloud->is_dense)
1476  {
1477  for (vtkIdType i = 0; i < nr_points; ++i, pts += 3)
1478  memcpy (&data[pts], &cloud->points[i].x, 12); // sizeof (float) * 3
1479  }
1480  else
1481  {
1482  vtkIdType j = 0; // true point index
1483  for (vtkIdType i = 0; i < nr_points; ++i)
1484  {
1485  // Check if the point is invalid
1486  if (!isFinite (cloud->points[i]))
1487  continue;
1488 
1489  memcpy (&data[pts], &cloud->points[i].x, 12); // sizeof (float) * 3
1490  pts += 3;
1491  j++;
1492  }
1493  nr_points = j;
1494  points->SetNumberOfPoints (nr_points);
1495  }
1496 
1497  vtkSmartPointer<vtkIdTypeArray> cells = vertices->GetData ();
1498  updateCells (cells, am_it->second.cells, nr_points);
1499 
1500  // Set the cells and the vertices
1501  vertices->SetCells (nr_points, cells);
1502 
1503  // Get the colors from the handler
1505  color_handler.getColor (scalars);
1506  double minmax[2];
1507  scalars->GetRange (minmax);
1508  // Update the data
1509  polydata->GetPointData ()->SetScalars (scalars);
1510  polydata->Update ();
1511 
1512  am_it->second.actor->GetMapper ()->ImmediateModeRenderingOff ();
1513  am_it->second.actor->GetMapper ()->SetScalarRange (minmax);
1514 
1515  // Update the mapper
1516  reinterpret_cast<vtkPolyDataMapper*>(am_it->second.actor->GetMapper ())->SetInput (polydata);
1517  return (true);
1518 }
1519 
1520 /////////////////////////////////////////////////////////////////////////////////////////////
1521 template <typename PointT> bool
1523  const typename pcl::PointCloud<PointT>::ConstPtr &cloud,
1524  const std::vector<pcl::Vertices> &vertices,
1525  const std::string &id,
1526  int viewport)
1527 {
1528  if (vertices.empty () || cloud->points.empty ())
1529  return (false);
1530 
1531  CloudActorMap::iterator am_it = cloud_actor_map_->find (id);
1532  if (am_it != cloud_actor_map_->end ())
1533  {
1534  pcl::console::print_warn (stderr,
1535  "[addPolygonMesh] A shape with id <%s> already exists! Please choose a different id and retry.\n",
1536  id.c_str ());
1537  return (false);
1538  }
1539 
1540  int rgb_idx = -1;
1541  std::vector<pcl::PCLPointField> fields;
1543  rgb_idx = pcl::getFieldIndex (*cloud, "rgb", fields);
1544  if (rgb_idx == -1)
1545  rgb_idx = pcl::getFieldIndex (*cloud, "rgba", fields);
1546  if (rgb_idx != -1)
1547  {
1549  colors->SetNumberOfComponents (3);
1550  colors->SetName ("Colors");
1551 
1552  pcl::RGB rgb_data;
1553  for (size_t i = 0; i < cloud->size (); ++i)
1554  {
1555  if (!isFinite (cloud->points[i]))
1556  continue;
1557  memcpy (&rgb_data, reinterpret_cast<const char*> (&cloud->points[i]) + fields[rgb_idx].offset, sizeof (pcl::RGB));
1558  unsigned char color[3];
1559  color[0] = rgb_data.r;
1560  color[1] = rgb_data.g;
1561  color[2] = rgb_data.b;
1562  colors->InsertNextTupleValue (color);
1563  }
1564  }
1565 
1566  // Create points from polyMesh.cloud
1568  vtkIdType nr_points = cloud->points.size ();
1569  points->SetNumberOfPoints (nr_points);
1571 
1572  // Get a pointer to the beginning of the data array
1573  float *data = static_cast<vtkFloatArray*> (points->GetData ())->GetPointer (0);
1574 
1575  int ptr = 0;
1576  std::vector<int> lookup;
1577  // If the dataset is dense (no NaNs)
1578  if (cloud->is_dense)
1579  {
1580  for (vtkIdType i = 0; i < nr_points; ++i, ptr += 3)
1581  memcpy (&data[ptr], &cloud->points[i].x, sizeof (float) * 3);
1582  }
1583  else
1584  {
1585  lookup.resize (nr_points);
1586  vtkIdType j = 0; // true point index
1587  for (vtkIdType i = 0; i < nr_points; ++i)
1588  {
1589  // Check if the point is invalid
1590  if (!isFinite (cloud->points[i]))
1591  continue;
1592 
1593  lookup[i] = static_cast<int> (j);
1594  memcpy (&data[ptr], &cloud->points[i].x, sizeof (float) * 3);
1595  j++;
1596  ptr += 3;
1597  }
1598  nr_points = j;
1599  points->SetNumberOfPoints (nr_points);
1600  }
1601 
1602  // Get the maximum size of a polygon
1603  int max_size_of_polygon = -1;
1604  for (size_t i = 0; i < vertices.size (); ++i)
1605  if (max_size_of_polygon < static_cast<int> (vertices[i].vertices.size ()))
1606  max_size_of_polygon = static_cast<int> (vertices[i].vertices.size ());
1607 
1608  if (vertices.size () > 1)
1609  {
1610  // Create polys from polyMesh.polygons
1612  vtkIdType *cell = cell_array->WritePointer (vertices.size (), vertices.size () * (max_size_of_polygon + 1));
1613  int idx = 0;
1614  if (lookup.size () > 0)
1615  {
1616  for (size_t i = 0; i < vertices.size (); ++i, ++idx)
1617  {
1618  size_t n_points = vertices[i].vertices.size ();
1619  *cell++ = n_points;
1620  //cell_array->InsertNextCell (n_points);
1621  for (size_t j = 0; j < n_points; j++, ++idx)
1622  *cell++ = lookup[vertices[i].vertices[j]];
1623  //cell_array->InsertCellPoint (lookup[vertices[i].vertices[j]]);
1624  }
1625  }
1626  else
1627  {
1628  for (size_t i = 0; i < vertices.size (); ++i, ++idx)
1629  {
1630  size_t n_points = vertices[i].vertices.size ();
1631  *cell++ = n_points;
1632  //cell_array->InsertNextCell (n_points);
1633  for (size_t j = 0; j < n_points; j++, ++idx)
1634  *cell++ = vertices[i].vertices[j];
1635  //cell_array->InsertCellPoint (vertices[i].vertices[j]);
1636  }
1637  }
1639  allocVtkPolyData (polydata);
1640  cell_array->GetData ()->SetNumberOfValues (idx);
1641  cell_array->Squeeze ();
1642  polydata->SetStrips (cell_array);
1643  polydata->SetPoints (points);
1644 
1645  if (colors)
1646  polydata->GetPointData ()->SetScalars (colors);
1647 
1648  createActorFromVTKDataSet (polydata, actor, false);
1649  }
1650  else
1651  {
1653  size_t n_points = vertices[0].vertices.size ();
1654  polygon->GetPointIds ()->SetNumberOfIds (n_points - 1);
1655 
1656  if (lookup.size () > 0)
1657  {
1658  for (size_t j = 0; j < (n_points - 1); ++j)
1659  polygon->GetPointIds ()->SetId (j, lookup[vertices[0].vertices[j]]);
1660  }
1661  else
1662  {
1663  for (size_t j = 0; j < (n_points - 1); ++j)
1664  polygon->GetPointIds ()->SetId (j, vertices[0].vertices[j]);
1665  }
1667  allocVtkUnstructuredGrid (poly_grid);
1668  poly_grid->Allocate (1, 1);
1669  poly_grid->InsertNextCell (polygon->GetCellType (), polygon->GetPointIds ());
1670  poly_grid->SetPoints (points);
1671  poly_grid->Update ();
1672  if (colors)
1673  poly_grid->GetPointData ()->SetScalars (colors);
1674 
1675  createActorFromVTKDataSet (poly_grid, actor, false);
1676  }
1677  addActorToRenderer (actor, viewport);
1678  actor->GetProperty ()->SetRepresentationToSurface ();
1679  // Backface culling renders the visualization slower, but guarantees that we see all triangles
1680  actor->GetProperty ()->BackfaceCullingOff ();
1681  actor->GetProperty ()->SetInterpolationToFlat ();
1682  actor->GetProperty ()->EdgeVisibilityOff ();
1683  actor->GetProperty ()->ShadingOff ();
1684 
1685  // Save the pointer/ID pair to the global actor map
1686  (*cloud_actor_map_)[id].actor = actor;
1687 
1688  // Save the viewpoint transformation matrix to the global actor map
1690  convertToVtkMatrix (cloud->sensor_origin_, cloud->sensor_orientation_, transformation);
1691  (*cloud_actor_map_)[id].viewpoint_transformation_ = transformation;
1692 
1693  return (true);
1694 }
1695 
1696 /////////////////////////////////////////////////////////////////////////////////////////////
1697 template <typename PointT> bool
1699  const typename pcl::PointCloud<PointT>::ConstPtr &cloud,
1700  const std::vector<pcl::Vertices> &verts,
1701  const std::string &id)
1702 {
1703  if (verts.empty ())
1704  {
1705  pcl::console::print_error ("[addPolygonMesh] No vertices given!\n");
1706  return (false);
1707  }
1708 
1709  // Check to see if this ID entry already exists (has it been already added to the visualizer?)
1710  CloudActorMap::iterator am_it = cloud_actor_map_->find (id);
1711  if (am_it == cloud_actor_map_->end ())
1712  return (false);
1713 
1714  // Get the current poly data
1715  vtkSmartPointer<vtkPolyData> polydata = static_cast<vtkPolyDataMapper*>(am_it->second.actor->GetMapper ())->GetInput ();
1716  if (!polydata)
1717  return (false);
1718  vtkSmartPointer<vtkCellArray> cells = polydata->GetStrips ();
1719  if (!cells)
1720  return (false);
1721  vtkSmartPointer<vtkPoints> points = polydata->GetPoints ();
1722  // Copy the new point array in
1723  vtkIdType nr_points = cloud->points.size ();
1724  points->SetNumberOfPoints (nr_points);
1725 
1726  // Get a pointer to the beginning of the data array
1727  float *data = (static_cast<vtkFloatArray*> (points->GetData ()))->GetPointer (0);
1728 
1729  int ptr = 0;
1730  std::vector<int> lookup;
1731  // If the dataset is dense (no NaNs)
1732  if (cloud->is_dense)
1733  {
1734  for (vtkIdType i = 0; i < nr_points; ++i, ptr += 3)
1735  memcpy (&data[ptr], &cloud->points[i].x, sizeof (float) * 3);
1736  }
1737  else
1738  {
1739  lookup.resize (nr_points);
1740  vtkIdType j = 0; // true point index
1741  for (vtkIdType i = 0; i < nr_points; ++i)
1742  {
1743  // Check if the point is invalid
1744  if (!isFinite (cloud->points[i]))
1745  continue;
1746 
1747  lookup [i] = static_cast<int> (j);
1748  memcpy (&data[ptr], &cloud->points[i].x, sizeof (float) * 3);
1749  j++;
1750  ptr += 3;
1751  }
1752  nr_points = j;
1753  points->SetNumberOfPoints (nr_points);
1754  }
1755 
1756  // Update colors
1757  vtkUnsignedCharArray* colors = vtkUnsignedCharArray::SafeDownCast (polydata->GetPointData ()->GetScalars ());
1758  int rgb_idx = -1;
1759  std::vector<pcl::PCLPointField> fields;
1760  rgb_idx = pcl::getFieldIndex (*cloud, "rgb", fields);
1761  if (rgb_idx == -1)
1762  rgb_idx = pcl::getFieldIndex (*cloud, "rgba", fields);
1763  if (rgb_idx != -1 && colors)
1764  {
1765  pcl::RGB rgb_data;
1766  int j = 0;
1767  for (size_t i = 0; i < cloud->size (); ++i)
1768  {
1769  if (!isFinite (cloud->points[i]))
1770  continue;
1771  memcpy (&rgb_data,
1772  reinterpret_cast<const char*> (&cloud->points[i]) + fields[rgb_idx].offset,
1773  sizeof (pcl::RGB));
1774  unsigned char color[3];
1775  color[0] = rgb_data.r;
1776  color[1] = rgb_data.g;
1777  color[2] = rgb_data.b;
1778  colors->SetTupleValue (j++, color);
1779  }
1780  }
1781 
1782  // Get the maximum size of a polygon
1783  int max_size_of_polygon = -1;
1784  for (size_t i = 0; i < verts.size (); ++i)
1785  if (max_size_of_polygon < static_cast<int> (verts[i].vertices.size ()))
1786  max_size_of_polygon = static_cast<int> (verts[i].vertices.size ());
1787 
1788  // Update the cells
1790  vtkIdType *cell = cells->WritePointer (verts.size (), verts.size () * (max_size_of_polygon + 1));
1791  int idx = 0;
1792  if (lookup.size () > 0)
1793  {
1794  for (size_t i = 0; i < verts.size (); ++i, ++idx)
1795  {
1796  size_t n_points = verts[i].vertices.size ();
1797  *cell++ = n_points;
1798  for (size_t j = 0; j < n_points; j++, cell++, ++idx)
1799  *cell = lookup[verts[i].vertices[j]];
1800  }
1801  }
1802  else
1803  {
1804  for (size_t i = 0; i < verts.size (); ++i, ++idx)
1805  {
1806  size_t n_points = verts[i].vertices.size ();
1807  *cell++ = n_points;
1808  for (size_t j = 0; j < n_points; j++, cell++, ++idx)
1809  *cell = verts[i].vertices[j];
1810  }
1811  }
1812  cells->GetData ()->SetNumberOfValues (idx);
1813  cells->Squeeze ();
1814  // Set the the vertices
1815  polydata->SetStrips (cells);
1816  polydata->Update ();
1817 
1818  return (true);
1819 }
1820 
1821 #endif