1 #include <pcl/apps/cloud_composer/project_model.h>
2 #include <pcl/apps/cloud_composer/tool_interface/abstract_tool.h>
3 #include <pcl/apps/cloud_composer/commands.h>
4 #include <pcl/apps/cloud_composer/work_queue.h>
5 #include <pcl/apps/cloud_composer/items/cloud_item.h>
6 #include <pcl/apps/cloud_composer/cloud_view.h>
7 #include <pcl/apps/cloud_composer/merge_selection.h>
8 #include <pcl/apps/cloud_composer/transform_clouds.h>
9 #include <pcl/memory.h>
10 
11 #include <QAction>
12 #include <QFileDialog>
13 #include <QItemSelectionModel>
14 #include <QMessageBox>
15 #include <QThread>
16 
17 #include <vtkSmartPointer.h>
18 #include <vtkImageData.h>
19 #include <vtkImageReader2Factory.h>
20 #include <vtkImageReader2.h>
21 
ProjectModel(QObject * parent)22 pcl::cloud_composer::ProjectModel::ProjectModel (QObject* parent)
23   : QStandardItemModel (parent)
24 {
25 
26   last_directory_ = QDir (".");
27 
28   selection_model_ = new QItemSelectionModel (this);
29 
30   undo_stack_ = new QUndoStack (this);
31   undo_stack_->setUndoLimit (10);
32 
33   work_thread_ = new QThread();
34   work_queue_ = new WorkQueue ();
35   work_queue_->moveToThread (work_thread_);
36 
37   connect (this, SIGNAL (enqueueNewAction (AbstractTool*, ConstItemList)),
38            work_queue_, SLOT (enqueueNewAction (AbstractTool*, ConstItemList)));
39   connect (work_queue_, SIGNAL (commandComplete (CloudCommand*)),
40            this, SLOT (commandCompleted (CloudCommand*)));
41   work_thread_->start ();
42 
43   connect (this, SIGNAL (rowsInserted ( const QModelIndex, int, int)),
44            this, SIGNAL (modelChanged ()));
45   connect (this, SIGNAL (rowsRemoved  ( const QModelIndex, int, int)),
46            this, SIGNAL (modelChanged ()));
47 
48   connect (this, SIGNAL (rowsAboutToBeRemoved  ( const QModelIndex, int, int)),
49            this, SLOT (clearSelection()));
50   connect (selection_model_, SIGNAL (selectionChanged(QItemSelection,QItemSelection)),
51            this, SLOT (emitAllStateSignals ()));
52   connect (selection_model_, SIGNAL (selectionChanged(QItemSelection,QItemSelection)),
53            this, SLOT (itemSelectionChanged (QItemSelection,QItemSelection)));
54 
55   selected_style_map_.insert (interactor_styles::PCL_VISUALIZER, true);
56   selected_style_map_.insert (interactor_styles::CLICK_TRACKBALL, false);
57   selected_style_map_.insert (interactor_styles::RECTANGULAR_FRUSTUM, false);
58   selected_style_map_.insert (interactor_styles::SELECTED_TRACKBALL, false);
59 }
60 
ProjectModel(const ProjectModel &)61 pcl::cloud_composer::ProjectModel::ProjectModel (const ProjectModel&)
62 : QStandardItemModel ()
63 {
64 }
65 
~ProjectModel()66 pcl::cloud_composer::ProjectModel::~ProjectModel ()
67 {
68   work_thread_->quit ();
69   work_thread_->deleteLater ();
70   work_queue_->deleteLater ();
71 }
72 
ProjectModel(QString project_name,QObject * parent)73 pcl::cloud_composer::ProjectModel::ProjectModel (QString project_name, QObject* parent)
74 : QStandardItemModel (parent)
75 {
76   selection_model_ = new QItemSelectionModel(this);
77   setName (std::move(project_name));
78 }
79 
80 void
setName(const QString & new_name)81 pcl::cloud_composer::ProjectModel::setName (const QString& new_name)
82 {
83   //If it hasn't been set yet
84   if (!horizontalHeaderItem (0))
85     setHorizontalHeaderItem (0, new QStandardItem (new_name));
86   else
87   {
88     QStandardItem* header = horizontalHeaderItem (0);
89     header->setText(new_name);
90   }
91 }
92 
93 void
setCloudView(CloudView * view)94 pcl::cloud_composer::ProjectModel::setCloudView (CloudView* view)
95 {
96   cloud_view_ = view;
97   // Initialize status variables tied to the view
98   setAxisVisibility (true);
99 
100 }
101 
102 void
setPointSelection(const std::shared_ptr<SelectionEvent> & selected_event)103 pcl::cloud_composer::ProjectModel::setPointSelection (const std::shared_ptr<SelectionEvent>& selected_event)
104 {
105   selection_event_ = selected_event;
106   //Get all the items in this project that are clouds
107   QList <CloudItem*> project_clouds;
108   for (int i = 0; i < this->rowCount (); ++i)
109   {
110     CloudItem* cloud_item = dynamic_cast <CloudItem*> (this->item (i));
111     if ( cloud_item )
112       project_clouds.append ( cloud_item );
113   }
114 
115   selected_item_index_map_.clear ();
116   // Find all indices in the selected points which are present in the clouds
117   foreach (CloudItem* cloud_item, project_clouds)
118   {
119     pcl::PointIndices::Ptr found_indices = pcl::make_shared<pcl::PointIndices>();
120     selected_event->findIndicesInItem (cloud_item, found_indices);
121     if (!found_indices->indices.empty ())
122     {
123       qDebug () << "Found "<<found_indices->indices.size ()<<" points in "<<cloud_item->text ();
124       selected_item_index_map_. insert (cloud_item, found_indices);
125       cloud_item->setForeground (QBrush (Qt::green));
126     }
127   }
128   setSelectedStyle (interactor_styles::PCL_VISUALIZER);
129   emit mouseStyleState (interactor_styles::PCL_VISUALIZER);
130 }
131 
132 void
manipulateClouds(const std::shared_ptr<ManipulationEvent> & manip_event)133 pcl::cloud_composer::ProjectModel::manipulateClouds (const std::shared_ptr<ManipulationEvent>& manip_event)
134 {
135 
136   //Get all the items in this project that are clouds
137   QList <CloudItem*> project_clouds;
138   for (int i = 0; i < this->rowCount (); ++i)
139   {
140     CloudItem* cloud_item = dynamic_cast <CloudItem*> (this->item (i));
141     if ( cloud_item )
142       project_clouds.append ( cloud_item );
143   }
144 
145   QMap <QString, vtkSmartPointer<vtkMatrix4x4> > transform_map = manip_event->getEndMap ();
146   QList <QString> ids = transform_map.keys ();
147   ConstItemList input_data;
148 
149   TransformClouds* transform_tool = new TransformClouds (transform_map);
150   foreach (CloudItem* cloud_item, project_clouds)
151   {
152     if (ids.contains (cloud_item->getId ()))
153     {
154       qDebug () << "Found matching item for actor "<<cloud_item->getId ();
155       input_data.append (cloud_item);
156     }
157   }
158 
159   //Move the tool object to the work queue thread
160   transform_tool->moveToThread (work_thread_);
161   //Emit signal which tells work queue to enqueue this new action
162   emit enqueueNewAction (transform_tool, input_data);
163 
164 
165 }
166 
167 void
insertNewCloudFromFile()168 pcl::cloud_composer::ProjectModel::insertNewCloudFromFile ()
169 {
170   qDebug () << "Inserting cloud from file...";
171   QString filename = QFileDialog::getOpenFileName (nullptr,tr ("Select cloud to open"), last_directory_.absolutePath (), tr ("PointCloud(*.pcd)"));
172   if ( filename.isNull ())
173   {
174     qWarning () << "No file selected, no cloud loaded";
175     return;
176   }
177   QFileInfo file_info (filename);
178   last_directory_ = file_info.absoluteDir ();
179 
180   pcl::PCLPointCloud2::Ptr cloud_blob (new pcl::PCLPointCloud2);
181   Eigen::Vector4f origin;
182   Eigen::Quaternionf orientation;
183   int version;
184 
185   pcl::PCDReader pcd;
186   if (pcd.read (filename.toStdString (), *cloud_blob, origin, orientation, version) < 0)
187   {
188     qDebug () << "Failed to read cloud from file";
189     return;
190   }
191   if (cloud_blob->width * cloud_blob->height == 0)
192   {
193     qDebug () << "Cloud read has zero size!";
194     return;
195   }
196 
197   QString short_filename = file_info.baseName ();
198   //Check if this name already exists in the project - if so, append digit
199   QList <QStandardItem*> items = findItems (short_filename);
200   if (!items.empty ())
201   {
202     int k = 2;
203     items = findItems (short_filename+ tr ("-%1").arg (k));
204     while (!items.empty ())
205     {
206       ++k;
207       items = findItems (short_filename+ tr ("-%1").arg (k));
208     }
209     short_filename += tr ("-%1").arg (k);
210   }
211   CloudItem* new_item = new CloudItem (short_filename, cloud_blob, origin, orientation, true);
212 
213   insertNewCloudComposerItem (new_item, invisibleRootItem());
214 
215 }
216 
217 void
insertNewCloudFromRGBandDepth()218 pcl::cloud_composer::ProjectModel::insertNewCloudFromRGBandDepth ()
219 {
220   qDebug () << "Inserting cloud from RGB and Depth files...";
221   QString rgb_filename = QFileDialog::getOpenFileName (nullptr,tr ("Select rgb image file to open"), last_directory_.absolutePath (), tr ("Images(*.png *.bmp *.tif *.ppm)"));
222   QString depth_filename;
223   if ( rgb_filename.isNull ())
224   {
225     qWarning () << "No file selected, no cloud loaded";
226     return;
227   }
228   QFileInfo file_info (rgb_filename);
229   last_directory_ = file_info.absoluteDir ();
230   QString base_name = file_info.baseName ();
231   QStringList depth_filter;
232   depth_filter << base_name.split("_").at(0) + "_depth.*";
233   last_directory_.setNameFilters (depth_filter);
234   QFileInfoList depth_info_list = last_directory_.entryInfoList ();
235   if (depth_info_list.empty ())
236   {
237     qCritical () << "Could not find depth file in format (rgb file base name)_depth.*";
238     return;
239   }
240   if (depth_info_list.size () > 1)
241   {
242     qWarning () << "Found more than one file which matches depth naming format, using first one!";
243   }
244   depth_filename = depth_info_list.at (0).absoluteFilePath ();
245 
246   //Read the images
247   vtkSmartPointer<vtkImageReader2Factory> reader_factory = vtkSmartPointer<vtkImageReader2Factory>::New ();
248   vtkImageReader2* rgb_reader = reader_factory->CreateImageReader2 (rgb_filename.toStdString ().c_str ());
249   qDebug () << "RGB File="<<rgb_filename;
250   if ( ! rgb_reader->CanReadFile (rgb_filename.toStdString ().c_str ()))
251   {
252     qCritical () << "Cannot read rgb image file!";
253     return;
254   }
255   rgb_reader->SetFileName (rgb_filename.toStdString ().c_str ());
256   rgb_reader->Update ();
257   qDebug () << "Depth File="<<depth_filename;
258   vtkImageReader2* depth_reader = reader_factory->CreateImageReader2 (depth_filename.toStdString ().c_str ());
259   if ( ! depth_reader->CanReadFile (depth_filename.toStdString ().c_str ()))
260   {
261     qCritical () << "Cannot read depth image file!";
262     return;
263   }
264   depth_reader->SetFileName (depth_filename.toStdString ().c_str ());
265   depth_reader->Update ();
266 
267   vtkSmartPointer<vtkImageData> rgb_image = rgb_reader->GetOutput ();
268   int *rgb_dims = rgb_image->GetDimensions ();
269   vtkSmartPointer<vtkImageData> depth_image = depth_reader->GetOutput ();
270   int *depth_dims = depth_image->GetDimensions ();
271 
272   if (rgb_dims[0] != depth_dims[0] || rgb_dims[1] != depth_dims[1])
273   {
274     qCritical () << "Depth and RGB dimensions to not match!";
275     qDebug () << "RGB Image is of size "<<rgb_dims[0] << " by "<<rgb_dims[1];
276     qDebug () << "Depth Image is of size "<<depth_dims[0] << " by "<<depth_dims[1];
277     return;
278   }
279   qDebug () << "Images loaded, making cloud";
280   PointCloud<PointXYZRGB>::Ptr cloud (new PointCloud<PointXYZRGB>);
281   cloud->points.reserve (depth_dims[0] * depth_dims[1]);
282   cloud->width = depth_dims[0];
283   cloud->height = depth_dims[1];
284   cloud->is_dense = false;
285 
286 
287   // Fill in image data
288   int centerX = static_cast<int>(cloud->width / 2.0);
289   int centerY = static_cast<int>(cloud->height / 2.0);
290   unsigned short* depth_pixel;
291   unsigned char* color_pixel;
292   float scale = 1.0f/1000.0f;
293   float focal_length = 525.0f;
294   float fl_const = 1.0f / focal_length;
295   depth_pixel = static_cast<unsigned short*>(depth_image->GetScalarPointer (depth_dims[0]-1,depth_dims[1]-1,0));
296   color_pixel = static_cast<unsigned char*> (rgb_image->GetScalarPointer (depth_dims[0]-1,depth_dims[1]-1,0));
297 
298   for (std::uint32_t y=0; y<cloud->height; ++y)
299   {
300     for (std::uint32_t x=0; x<cloud->width; ++x, --depth_pixel, color_pixel-=3)
301     {
302       PointXYZRGB new_point;
303       //  std::uint8_t* p_i = &(cloud_blob->data[y * cloud_blob->row_step + x * cloud_blob->point_step]);
304       float depth = (float)(*depth_pixel) * scale;
305     //  qDebug () << "Depth = "<<depth;
306       if (depth == 0.0f)
307       {
308         new_point.x = new_point.y = new_point.z = std::numeric_limits<float>::quiet_NaN ();
309       }
310       else
311       {
312         new_point.x = ((float)(x - centerX)) * depth * fl_const;
313         new_point.y = ((float)(centerY - y)) * depth * fl_const; // vtk seems to start at the bottom left image corner
314         new_point.z = depth;
315       }
316 
317       new_point.r = color_pixel[0];
318       new_point.g = color_pixel[1];
319       new_point.b = color_pixel[2];
320       cloud->points.push_back (new_point);
321       //   qDebug () << "depth = "<<depth << "x,y,z="<<data[0]<<","<<data[1]<<","<<data[2];
322       //qDebug() << "r ="<<color_pixel[0]<<" g="<<color_pixel[1]<<" b="<<color_pixel[2];
323 
324     }
325   }
326   qDebug () << "Done making cloud!";
327 
328   QString short_filename = file_info.baseName ();
329   //Check if this name already exists in the project - if so, append digit
330   QList <QStandardItem*> items = findItems (short_filename);
331   if (!items.empty ())
332   {
333     int k = 2;
334     items = findItems (short_filename+ tr ("-%1").arg (k));
335     while (!items.empty ())
336     {
337       ++k;
338       items = findItems (short_filename+ tr ("-%1").arg (k));
339     }
340     short_filename += tr ("-%1").arg (k);
341   }
342 
343   CloudItem* new_item = CloudItem::createCloudItemFromTemplate<PointXYZRGB> (short_filename,cloud);
344 
345   insertNewCloudComposerItem (new_item, invisibleRootItem());
346 
347 }
348 void
saveSelectedCloudToFile()349 pcl::cloud_composer::ProjectModel::saveSelectedCloudToFile ()
350 {
351   qDebug () << "Saving cloud to file...";
352   QModelIndexList selected_indexes = selection_model_->selectedIndexes ();
353   if (selected_indexes.empty ())
354   {
355     QMessageBox::warning (qobject_cast<QWidget *>(this->parent ()), "No Cloud Selected", "Cannot save, no cloud is selected in the browser or cloud view");
356     return;
357   }
358   if (selected_indexes.size () > 1)
359   {
360     QMessageBox::warning (qobject_cast<QWidget *>(this->parent ()), "Too many clouds Selected", "Cannot save, currently only support saving one cloud at a time");
361     return;
362   }
363 
364   QStandardItem* item = this->itemFromIndex (selected_indexes.value (0));
365   CloudItem* cloud_to_save = dynamic_cast <CloudItem*> (item);
366   if (!cloud_to_save )
367   {
368     QMessageBox::warning (qobject_cast<QWidget *>(this->parent ()), "Not a Cloud!", "Selected item is not a cloud, not saving!");
369     return;
370   }
371 
372   QString filename = QFileDialog::getSaveFileName (nullptr,tr ("Save Cloud"), last_directory_.absolutePath (), tr ("PointCloud(*.pcd)"));
373   if ( filename.isNull ())
374   {
375     qWarning () << "No file selected, not saving";
376     return;
377   }
378   QFileInfo file_info (filename);
379   last_directory_ = file_info.absoluteDir ();
380 
381   pcl::PCLPointCloud2::ConstPtr cloud = cloud_to_save->data (ItemDataRole::CLOUD_BLOB).value <pcl::PCLPointCloud2::ConstPtr> ();
382   Eigen::Vector4f origin = cloud_to_save->data (ItemDataRole::ORIGIN).value <Eigen::Vector4f> ();
383   Eigen::Quaternionf orientation = cloud_to_save->data (ItemDataRole::ORIENTATION).value <Eigen::Quaternionf> ();
384   pcl::io::savePCDFile (filename.toStdString (), *cloud, origin, orientation );
385 
386 }
387 
388 void
enqueueToolAction(AbstractTool * tool)389 pcl::cloud_composer::ProjectModel::enqueueToolAction (AbstractTool* tool)
390 {
391   qDebug () << "Enqueuing tool action "<<tool->getToolName ()<<" in project model "<<this->getName ();
392   //Get the currently selected item(s), put them in a list, and create the command
393   ConstItemList input_data;
394   QModelIndexList selected_indexes = selection_model_->selectedIndexes ();
395   if (selected_indexes.empty ())
396   {
397     QMessageBox::warning (qobject_cast<QWidget *>(this->parent ()), "No Items Selected", "Cannot use tool, no item is selected in the browser or cloud view");
398     return;
399   }
400   foreach (QModelIndex index, selected_indexes)
401   {
402     QStandardItem* item = this->itemFromIndex (index);
403     if ( dynamic_cast <CloudComposerItem*> (item))
404       input_data.append (dynamic_cast <CloudComposerItem*> (item));
405   }
406   qDebug () << "Input for tool is "<<input_data.size () << " element(s)";
407 
408   //Move the tool object to the work queue thread
409   tool->moveToThread (work_thread_);
410   //Emit signal which tells work queue to enqueue this new action
411   emit enqueueNewAction (tool, input_data);
412 }
413 
414 
415 void
commandCompleted(CloudCommand * command)416 pcl::cloud_composer::ProjectModel::commandCompleted (CloudCommand* command)
417 {
418   //We set the project model here - this wasn't done earlier so model is never exposed to plugins
419   command->setProjectModel (this);
420   qDebug () << "Applying command changes to model and pushing onto undo stack";
421   //command->redo ();
422   undo_stack_->push (command);
423 
424 }
425 
426 void
insertNewCloudComposerItem(CloudComposerItem * new_item,QStandardItem * parent_item)427 pcl::cloud_composer::ProjectModel::insertNewCloudComposerItem (CloudComposerItem* new_item, QStandardItem* parent_item)
428 {
429   parent_item->appendRow (new_item);
430 }
431 
432 ///////////////////////////////////////////////////////////////
433 //Slots for commands arriving from GUI
434 ///////////////////////////////////////////////////////////////
435 void
clearSelection()436 pcl::cloud_composer::ProjectModel::clearSelection ()
437 {
438   getSelectionModel ()->clearSelection ();
439 
440   //Clear the point selector as well if it has an active selection
441   if (selection_event_)
442     selection_event_.reset ();
443 
444   foreach (CloudItem* selected_item, selected_item_index_map_.keys())
445   {
446     qDebug () << "Setting item color back to black";
447     selected_item->setForeground (QBrush (Qt::black));;
448   }
449 
450   selected_item_index_map_.clear ();
451 }
452 
453 void
deleteSelectedItems()454 pcl::cloud_composer::ProjectModel::deleteSelectedItems ()
455 {
456 
457   QModelIndexList selected_indexes = selection_model_->selectedIndexes ();
458   if (selected_indexes.empty ())
459   {
460     QMessageBox::warning (qobject_cast<QWidget *>(this->parent ()), "No Items Selected", "Cannot execute delete command, no item is selected in the browser or cloud view");
461     return;
462   }
463 
464   ConstItemList input_data;
465   foreach (QModelIndex index, selected_indexes)
466   {
467     QStandardItem* item = this->itemFromIndex (index);
468     //qDebug () << item->text () << " selected!";
469     if ( dynamic_cast <CloudComposerItem*> (item))
470       input_data.append (dynamic_cast <CloudComposerItem*> (item));
471   }
472  // qDebug () << "Input for command is "<<input_data.size () << " element(s)";
473   DeleteItemCommand* delete_command = new DeleteItemCommand (ConstItemList ());
474   delete_command->setInputData (input_data);
475   if (delete_command->runCommand (nullptr))
476     commandCompleted(delete_command);
477   else
478     qCritical () << "Execution of delete command failed!";
479 }
480 
481 void
setAxisVisibility(bool visible)482 pcl::cloud_composer::ProjectModel::setAxisVisibility (bool visible)
483 {
484   //qDebug () << "Setting axis visibility to "<<visible;
485   axis_visible_ = visible;
486   cloud_view_->setAxisVisibility (axis_visible_);
487 }
488 
489 void
mouseStyleChanged(QAction * new_style_action)490 pcl::cloud_composer::ProjectModel::mouseStyleChanged (QAction* new_style_action)
491 {
492   interactor_styles::INTERACTOR_STYLES selected_style = new_style_action->data ().value<interactor_styles::INTERACTOR_STYLES> ();
493   qDebug () << "Selected style ="<<selected_style;
494   setSelectedStyle (selected_style);
495 
496   // Now set the correct interactor
497   if (cloud_view_)
498     cloud_view_->setInteractorStyle (selected_style);
499   else
500     qWarning () << "No Cloud View active, can't change interactor style!";
501 
502 
503 }
504 
505 void
createNewCloudFromSelection()506 pcl::cloud_composer::ProjectModel::createNewCloudFromSelection ()
507 {
508   // We need only clouds to be selected
509   if (!onlyCloudItemsSelected ())
510   {
511     qCritical () << "Only Clouds Selected = False -- Cannot create a new cloud from non-cloud selection";
512     return;
513   }
514   //Add selected items into input data list
515   QModelIndexList selected_indexes = selection_model_->selectedIndexes ();
516   ConstItemList input_data;
517   foreach (QModelIndex index, selected_indexes)
518   {
519     QStandardItem* item = this->itemFromIndex (index);
520     //qDebug () << item->text () << " selected!";
521     if ( dynamic_cast <CloudComposerItem*> (item))
522       input_data.append (dynamic_cast <CloudComposerItem*> (item));
523   }
524 
525   QMap <const CloudItem*, pcl::PointIndices::ConstPtr> selected_const_map;
526   foreach ( CloudItem* item, selected_item_index_map_.keys ())
527     selected_const_map.insert (item, selected_item_index_map_.value (item));
528   MergeSelection* merge_tool = new MergeSelection (selected_const_map);
529 
530   //We don't call the enqueueToolAction function since that would abort if we only have a green selection
531   //Move the tool object to the work queue thread
532   merge_tool->moveToThread (work_thread_);
533   //Emit signal which tells work queue to enqueue this new action
534   emit enqueueNewAction (merge_tool, input_data);
535 
536 
537 }
538 
539 void
selectAllItems(QStandardItem * item)540 pcl::cloud_composer::ProjectModel::selectAllItems (QStandardItem* item)
541 {
542 
543   if (!item)
544     item = this->invisibleRootItem ();
545   else
546    selection_model_->select (item->index (), QItemSelectionModel::Select);
547   //qDebug () << "Select all!"<< item->rowCount();
548   for (int i = 0; i < item->rowCount (); ++i)
549   {
550     if (item->child (i))
551       selectAllItems(item->child (i));
552   }
553 
554 }
555 
556 /////////////////////////////////////////////////////////
557 //Slots for Model State
558 ////////////////////////////////////////////////////////
559 void
emitAllStateSignals()560 pcl::cloud_composer::ProjectModel::emitAllStateSignals ()
561 {
562   emit axisVisible (axis_visible_);
563   emit deleteAvailable (selection_model_->hasSelection ());
564   emit newCloudFromSelectionAvailable (onlyCloudItemsSelected ());
565 
566   //Find out which style is active, emit the signal
567   foreach (interactor_styles::INTERACTOR_STYLES style, selected_style_map_.keys())
568   {
569     if (selected_style_map_.value (style))
570     {
571       emit mouseStyleState (style);
572       break;
573     }
574   }
575 
576 
577 }
578 
579 void
itemSelectionChanged(const QItemSelection &,const QItemSelection &)580 pcl::cloud_composer::ProjectModel::itemSelectionChanged ( const QItemSelection &, const QItemSelection &)
581 {
582   //qDebug () << "Item selection changed!";
583   //Set all point selected cloud items back to green text, since if they are selected they get changed to white
584   foreach (CloudItem* selected_item, selected_item_index_map_.keys())
585   {
586     selected_item->setForeground (QBrush (Qt::green));;
587   }
588 }
589 
590 
591 //////////////////////////////////////////////////////
592 //  Private Support Functions
593 //////////////////////////////////////////////////////
594 
595 
596 bool
onlyCloudItemsSelected()597 pcl::cloud_composer::ProjectModel::onlyCloudItemsSelected ()
598 {
599   QModelIndexList selected_indexes = selection_model_->selectedIndexes();
600   foreach (QModelIndex model_index, selected_indexes)
601   {
602     if (this->itemFromIndex (model_index)->type () != CloudComposerItem::CLOUD_ITEM )
603     {
604       return false;
605     }
606   }
607   return true;
608 }
609 
610 void
setSelectedStyle(interactor_styles::INTERACTOR_STYLES style)611 pcl::cloud_composer::ProjectModel::setSelectedStyle (interactor_styles::INTERACTOR_STYLES style)
612 {
613   QMap<interactor_styles::INTERACTOR_STYLES, bool>::iterator itr = selected_style_map_.begin();
614   while (itr != selected_style_map_.end ())
615   {
616     itr.value() = false;
617     ++itr;
618   }
619   selected_style_map_[style] = true;
620 
621 }
622 
623 
624