default_collisions_widget.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2012, Willow Garage, Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above
14  * copyright notice, this list of conditions and the following
15  * disclaimer in the documentation and/or other materials provided
16  * with the distribution.
17  * * Neither the name of Willow Garage nor the names of its
18  * contributors may be used to endorse or promote products derived
19  * from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *********************************************************************/
34 
35 /* Author: Dave Coleman */
36 
37 #include <algorithm>
38 #include <QAction>
39 #include <QApplication>
40 #include <QButtonGroup>
41 #include <QCheckBox>
42 #include <QFont>
43 #include <QGroupBox>
44 #include <QHBoxLayout>
45 #include <QKeyEvent>
46 #include <QLabel>
47 #include <QLineEdit>
48 #include <QMenu>
49 #include <QMessageBox>
50 #include <QProgressBar>
51 #include <QPushButton>
52 #include <QRadioButton>
53 #include <QSlider>
54 #include <QSpinBox>
55 #include <QString>
56 #include <QTableView>
57 #include <QVBoxLayout>
58 
60 #include "header_widget.h"
61 #include "../tools/collision_matrix_model.h"
62 #include "../tools/collision_linear_model.h"
63 #include "../tools/rotated_header_view.h"
64 #include <ros/console.h>
65 
66 namespace
67 {
68 // ******************************************************************************************
69 // Convert LinkPairMap to SRDF
70 // ******************************************************************************************
71 void linkPairsToSRDF(const moveit_setup_assistant::LinkPairMap& pairs, srdf::SRDFWriter& srdf)
72 {
73  // reset the data in the SRDF Writer class
74  srdf.disabled_collision_pairs_.clear();
75 
76  // Create temp disabled collision
78 
79  // copy the data in this class's LinkPairMap datastructure to srdf::Model::CollisionPair format
80  for (const auto& item : pairs)
81  {
82  // Only copy those that are actually disabled
83  if (item.second.disable_check)
84  {
85  dc.link1_ = item.first.first;
86  dc.link2_ = item.first.second;
88  srdf.disabled_collision_pairs_.push_back(dc);
89  }
90  }
91 }
92 } // namespace
93 
94 namespace moveit_setup_assistant
95 {
96 // ******************************************************************************************
97 // User interface for editing the default collision matrix list in an SRDF
98 // ******************************************************************************************
99 DefaultCollisionsWidget::DefaultCollisionsWidget(QWidget* parent, const MoveItConfigDataPtr& config_data)
100  : SetupScreenWidget(parent), model_(nullptr), selection_model_(nullptr), worker_(nullptr), config_data_(config_data)
101 {
102  // Basic widget container
103  layout_ = new QVBoxLayout(this);
104 
105  // Top Label Area ------------------------------------------------
107  "Optimize Self-Collision Checking",
108  "This searches for pairs of robot links that can safely be disabled from collision checking, decreasing motion "
109  "planning time. These pairs are disabled when they are always in collision, never in collision, in collision in "
110  "the robot's default position, or when the links are adjacent to each other on the kinematic chain. Sampling "
111  "density specifies how many random robot positions to check for self collision.",
112  this);
113  layout_->addWidget(header);
114 
115  // Top Button Area -----------------------------------------------
116  controls_box_ = new QGroupBox(this);
117  layout_->addWidget(controls_box_);
118  QVBoxLayout* controls_box_layout = new QVBoxLayout(controls_box_);
119 
120  QHBoxLayout* slider_layout = new QHBoxLayout();
121  slider_layout->setAlignment(Qt::AlignTop | Qt::AlignLeft);
122  controls_box_layout->addLayout(slider_layout);
123 
124  // Slider Label
125  QLabel* density_left_label = new QLabel(this);
126  density_left_label->setText("Sampling Density: Low");
127  slider_layout->addWidget(density_left_label);
128 
129  // Slider
130  sample_slider_ = new QSlider(this);
131  sample_slider_->setTickPosition(QSlider::TicksBelow);
132  sample_slider_->setMinimum(1000);
133  sample_slider_->setMaximum(100000);
134  sample_slider_->setSingleStep(1000);
135  sample_slider_->setPageStep(10000);
136  sample_slider_->setSliderPosition(10000); // 10,000 is default
137  sample_slider_->setTickInterval(10000);
138  sample_slider_->setOrientation(Qt::Horizontal);
139  slider_layout->addWidget(sample_slider_);
140  connect(sample_slider_, SIGNAL(valueChanged(int)), this, SLOT(changeNumSamples(int)));
141 
142  // Slider Right Label
143  QLabel* density_right_label = new QLabel(this);
144  density_right_label->setText("High ");
145  slider_layout->addWidget(density_right_label);
146 
147  // Spinbox Value Label
148  sample_spinbox_ = new QSpinBox(this);
149  sample_spinbox_->setMinimumWidth(70);
150  sample_spinbox_->setMinimum(1000);
151  sample_spinbox_->setMaximum(100000000);
152  sample_spinbox_->setSingleStep(1000);
153  sample_spinbox_->setEnabled(true);
154  slider_layout->addWidget(sample_spinbox_);
155  changeNumSamples(sample_slider_->value()); // initialize label with value
156  connect(sample_spinbox_, SIGNAL(valueChanged(int)), this, SLOT(changeNumSamples(int)));
157 
158  QHBoxLayout* buttons_layout = new QHBoxLayout();
159  buttons_layout->setAlignment(Qt::AlignRight);
160  controls_box_layout->addLayout(buttons_layout);
161 
162  // Fraction spin box
163  fraction_label_ = new QLabel(this);
164  fraction_label_->setText("Min. collisions for \"always\"-colliding pairs:");
165  buttons_layout->addWidget(fraction_label_);
166 
167  fraction_spinbox_ = new QSpinBox(this);
168  fraction_spinbox_->setRange(1, 100);
169  fraction_spinbox_->setValue(95);
170  fraction_spinbox_->setSuffix("%");
171  buttons_layout->addWidget(fraction_spinbox_);
172 
173  // Generate Button
174  btn_generate_ = new QPushButton(this);
175  btn_generate_->setText("&Generate Collision Matrix");
176  btn_generate_->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
177  connect(btn_generate_, SIGNAL(clicked()), this, SLOT(startGeneratingCollisionTable()));
178  buttons_layout->addWidget(btn_generate_);
179 
180  // Progress Bar Area ---------------------------------------------
181 
182  // Progress Label
183  progress_label_ = new QLabel(this);
184  progress_label_->setText("Generating Default Collision Matrix");
185  progress_label_->hide();
186  layout_->addWidget(progress_label_);
187 
188  // Progress Bar
189  progress_bar_ = new QProgressBar(this);
190  progress_bar_->setMaximum(100);
191  progress_bar_->setMinimum(0);
192  progress_bar_->hide(); // only show when computation begins
193  layout_->addWidget(progress_bar_);
194 
195  // Interrupt Button
196  btn_interrupt_ = new QPushButton(this);
197  btn_interrupt_->setText("Interrupt");
198  btn_interrupt_->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
199  connect(btn_interrupt_, SIGNAL(clicked()), this, SLOT(interruptGeneratingCollisionTable()));
200  layout_->addWidget(btn_interrupt_);
201 
202  // Table Area --------------------------------------------
203 
204  // Table
205  collision_table_ = new QTableView(this);
206  layout_->addWidget(collision_table_);
207 
208  QAction* action;
209  action = new QAction(tr("Show"), this);
210  header_actions_ << action;
211  connect(action, SIGNAL(triggered()), this, SLOT(showSections()));
212  action = new QAction(tr("Hide"), this);
213  header_actions_ << action;
214  connect(action, SIGNAL(triggered()), this, SLOT(hideSections()));
215  action = new QAction(tr("Hide others"), this);
216  header_actions_ << action;
217  connect(action, SIGNAL(triggered()), this, SLOT(hideOtherSections()));
218  action = new QAction(tr("Disable by default"), this);
219  header_actions_ << action;
220  connect(action, &QAction::triggered, this, [this] { setDefaults(true); });
221  action = new QAction(tr("Enable by default"), this);
222  header_actions_ << action;
223  connect(action, &QAction::triggered, this, [this] { setDefaults(false); });
224 
225  // Bottom Area ----------------------------------------
226 
227  QHBoxLayout* bottom_layout = new QHBoxLayout();
228  bottom_layout->setAlignment(Qt::AlignRight);
229  layout_->addLayout(bottom_layout);
230 
231  // Link Filter QLineEdit
232  link_name_filter_ = new QLineEdit(this);
233  link_name_filter_->setPlaceholderText("link name filter");
234  bottom_layout->addWidget(link_name_filter_);
235 
236  // Collision Filter Checkbox
237  collision_checkbox_ = new QCheckBox(this);
238  collision_checkbox_->setText("show enabled pairs");
239  connect(collision_checkbox_, SIGNAL(toggled(bool)), this, SLOT(checkedFilterChanged()));
240  bottom_layout->addWidget(collision_checkbox_);
241 
242  // View Mode Buttons
243  view_mode_buttons_ = new QButtonGroup(this);
244  QRadioButton* radio_btn;
245  radio_btn = new QRadioButton("linear view");
246  bottom_layout->addWidget(radio_btn);
247  view_mode_buttons_->addButton(radio_btn, LINEAR_MODE);
248 
249  radio_btn = new QRadioButton("matrix view");
250  bottom_layout->addWidget(radio_btn);
251  view_mode_buttons_->addButton(radio_btn, MATRIX_MODE);
252  radio_btn->setChecked(true);
253  connect(view_mode_buttons_, SIGNAL(buttonClicked(int)), this, SLOT(loadCollisionTable()));
254 
255  // Revert Button
256  btn_revert_ = new QPushButton(this);
257  btn_revert_->setText("&Revert");
258  btn_revert_->setToolTip("Revert current changes to collision matrix");
259  btn_revert_->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
260  btn_revert_->setDisabled(true);
261  connect(btn_revert_, SIGNAL(clicked()), this, SLOT(revertChanges()));
262  bottom_layout->addWidget(btn_revert_);
263 
264  setLayout(layout_);
265  setWindowTitle("Default Collision Matrix");
266 
267  collision_table_->installEventFilter(this);
268 }
269 
271 {
272  delete model_;
273 }
274 
275 // ******************************************************************************************
276 // start thread generating the collision table
277 // ******************************************************************************************
279 {
280  // Disable controls on form
281  disableControls(true);
282  btn_revert_->setEnabled(true); // allow to interrupt and revert
283 
284  // create a MonitorThread running generateCollisionTable() in a worker thread and monitoring the progress
285  worker_ = new MonitorThread([this](unsigned int* progress) { generateCollisionTable(progress); }, progress_bar_);
286  connect(worker_, SIGNAL(finished()), this, SLOT(finishGeneratingCollisionTable()));
287  worker_->start(); // start after having finished() signal connected
288 }
289 
290 // ******************************************************************************************
291 // interrupt generating the collision table
292 // ******************************************************************************************
294 {
295  if (QMessageBox::No == QMessageBox::question(this, "Collision Matrix Generation",
296  "Collision Matrix Generation is still active. Cancel computation?",
297  QMessageBox::Yes | QMessageBox::No, QMessageBox::No))
298  return;
299 
300  if (worker_)
301  {
302  worker_->cancel();
303  worker_->wait();
304  }
305 }
306 
307 // ******************************************************************************************
308 // cleanup after worker_ thread has finished
309 // ******************************************************************************************
311 {
312  if (!worker_->canceled())
313  {
314  // Load the results into the GUI
316 
318  }
319  disableControls(false); // enable controls, hide interrupt button + progress bar
320  progress_bar_->show(); // make progress bar visislbe again
321 
322  worker_->deleteLater();
323  worker_ = nullptr;
324 }
325 
326 // ******************************************************************************************
327 // The worker function to compute the collision matrix
328 // ******************************************************************************************
329 void DefaultCollisionsWidget::generateCollisionTable(unsigned int* collision_progress)
330 {
331  unsigned int num_trials = (sample_spinbox_->value() / 1000.0) * 1000; // round the value in 1000s
332  num_trials = num_trials < 1000 ? 1000 : num_trials; // make sure that num_trials >= 1000
333  double min_frac = (double)fraction_spinbox_->value() / 100.0;
334 
335  const bool verbose = true; // Output benchmarking and statistics
336  const bool include_never_colliding = true;
337 
338  // clear previously loaded collision matrix entries
339  config_data_->getPlanningScene()->getAllowedCollisionMatrixNonConst().clear();
340 
341  // Find the default collision matrix - all links that are allowed to collide
343  config_data_->getPlanningScene(), collision_progress, include_never_colliding, num_trials, min_frac, verbose);
344  linkPairsToSRDF(link_pairs, *wip_srdf_);
345  // Update collision_matrix for robot pose's use
346  config_data_->loadAllowedCollisionMatrix(*wip_srdf_);
347 
348  // Indicate end the progress bar loop (MonitorThread::run())
349  if (worker_ && !worker_->canceled())
350  *collision_progress = 100;
351 
352  ROS_INFO_STREAM("Thread complete " << link_pairs.size());
353 }
354 
355 // ******************************************************************************************
356 // Displays data in the link_pairs_ data structure into a QtTableWidget
357 // ******************************************************************************************
359 {
360  CollisionMatrixModel* matrix_model = new CollisionMatrixModel(
361  wip_srdf_, config_data_->getPlanningScene()->getRobotModel()->getLinkModelNamesWithCollisionGeometry());
362  QAbstractItemModel* model;
363 
364  if (view_mode_buttons_->checkedId() == MATRIX_MODE)
365  {
366  model = matrix_model;
367  }
368  else
369  {
370  CollisionLinearModel* linear_model = new CollisionLinearModel(matrix_model);
371  SortFilterProxyModel* sorted_model = new SortFilterProxyModel();
372  sorted_model->setShowAll(collision_checkbox_->checkState() == Qt::Checked);
373  model = sorted_model;
374  sorted_model->setSourceModel(linear_model);
375  // ensure deletion of underlying models with model
376  linear_model->setParent(sorted_model);
377  matrix_model->setParent(linear_model);
378  }
379  connect(link_name_filter_, SIGNAL(textChanged(QString)), model, SLOT(setFilterRegExp(QString)));
380  QMetaObject::invokeMethod(model, "setFilterRegExp", Q_ARG(QString, link_name_filter_->text()));
381 
382  collision_table_->setModel(model);
383  // delete old and remember new model
384  delete model_;
385  model_ = model;
386 
387  // delete old and fetch new selection model
388  delete selection_model_;
389  selection_model_ = collision_table_->selectionModel();
390 
391  QHeaderView *horizontal_header, *vertical_header;
392 
393  // activate some model-specific settings
394  if (view_mode_buttons_->checkedId() == MATRIX_MODE)
395  {
396  connect(selection_model_, SIGNAL(currentChanged(QModelIndex, QModelIndex)), this,
397  SLOT(previewSelectedMatrix(QModelIndex)));
398 
399  collision_table_->setSelectionBehavior(QAbstractItemView::SelectItems);
400  collision_table_->setSelectionMode(QAbstractItemView::ExtendedSelection);
401 
402  collision_table_->setHorizontalHeader(horizontal_header = new RotatedHeaderView(Qt::Horizontal, this));
403  collision_table_->setVerticalHeader(vertical_header = new RotatedHeaderView(Qt::Vertical, this));
404  collision_table_->setSortingEnabled(false);
405 
406  collision_checkbox_->hide();
407  horizontal_header->setVisible(true);
408  vertical_header->setVisible(true);
409 
410  horizontal_header->setContextMenuPolicy(Qt::CustomContextMenu);
411  connect(horizontal_header, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showHeaderContextMenu(QPoint)));
412  vertical_header->setContextMenuPolicy(Qt::CustomContextMenu);
413  connect(vertical_header, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showHeaderContextMenu(QPoint)));
414  }
415  else
416  {
417  connect(selection_model_, SIGNAL(currentChanged(QModelIndex, QModelIndex)), this,
418  SLOT(previewSelectedLinear(QModelIndex)));
419 
420  collision_table_->setSelectionBehavior(QAbstractItemView::SelectRows);
421  collision_table_->setSelectionMode(QAbstractItemView::ExtendedSelection);
422 
423  collision_table_->setHorizontalHeader(horizontal_header = new QHeaderView(Qt::Horizontal, this));
424  collision_table_->setVerticalHeader(vertical_header = new QHeaderView(Qt::Vertical, this));
425  collision_table_->sortByColumn(0, Qt::AscendingOrder);
426  collision_table_->setSortingEnabled(true);
427 
428  collision_checkbox_->show();
429  horizontal_header->setVisible(true);
430  vertical_header->setVisible(true);
431  horizontal_header->setSectionResizeMode(QHeaderView::Stretch);
432 
433  vertical_header->setContextMenuPolicy(Qt::CustomContextMenu);
434  connect(vertical_header, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showHeaderContextMenu(QPoint)));
435 
436 #if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
437  horizontal_header->setSectionsClickable(true);
438  vertical_header->setSectionsClickable(true);
439 #else
440  horizontal_header->setClickable(true);
441  vertical_header->setClickable(true);
442 #endif
443  }
444 
445 // notice changes to the model
446 #if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
447  connect(model_, SIGNAL(dataChanged(QModelIndex, QModelIndex, QVector<int>)), this,
448  SLOT(collisionsChanged(QModelIndex)));
449 #else
450  connect(model_, SIGNAL(dataChanged(QModelIndex, QModelIndex)), this, SLOT(collisionsChanged(QModelIndex)));
451 #endif
452 }
453 
454 void DefaultCollisionsWidget::collisionsChanged(const QModelIndex& index)
455 {
456  btn_revert_->setEnabled(true); // enable revert button
457 
458  if (!index.isValid())
459  return;
460  // Hm. For some reason, QTableView doesn't change selection if we click a checkbox
461  bool linear_mode = (view_mode_buttons_->checkedId() == LINEAR_MODE);
462  const QItemSelection& selection = selection_model_->selection();
463  if ((linear_mode && !selection.contains(index)) || // in linear mode: index not in selection
464  (!linear_mode && !(selection.contains(index) || // in matrix mode: index or symmetric index not in selection
465  selection.contains(model_->index(index.column(), index.row())))))
466  {
467  QItemSelectionModel::SelectionFlags flags = QItemSelectionModel::Select | QItemSelectionModel::Current;
468  if (linear_mode)
469  flags |= QItemSelectionModel::Rows;
470  selection_model_->setCurrentIndex(index, flags);
471  }
472 }
473 
475 {
476  // This method might be triggered from either of the headers
477  QPoint global;
478  if (sender() == collision_table_->verticalHeader())
479  {
480  clicked_section_ = collision_table_->verticalHeader()->logicalIndexAt(p);
481  clicked_headers_ = Qt::Vertical;
482  global = collision_table_->verticalHeader()->mapToGlobal(p);
483  }
484  else if (sender() == collision_table_->horizontalHeader())
485  {
486  clicked_section_ = collision_table_->horizontalHeader()->logicalIndexAt(p);
487  clicked_headers_ = Qt::Horizontal;
488  global = collision_table_->horizontalHeader()->mapToGlobal(p);
489  }
490  else
491  {
492  clicked_section_ = -1;
493  clicked_headers_ = Qt::Horizontal | Qt::Vertical;
494  }
495 
496  QMenu menu;
497  if (clicked_section_ < 0)
498  menu.addAction(header_actions_.at(0)); // only 'show' action
499  else
500  menu.addActions(header_actions_);
501  menu.exec(global);
502 
503  clicked_headers_ = {};
504  clicked_section_ = -1;
505 }
506 
507 QList<int> DefaultCollisionsWidget::selectedSections(QHeaderView*& header) const
508 {
509  QList<int> list;
510  if (clicked_headers_ == Qt::Horizontal)
511  {
512  header = collision_table_->horizontalHeader();
513  for (const QModelIndex& index : selection_model_->selectedColumns())
514  if (!header->isSectionHidden(index.column()))
515  list << index.column();
516  }
517  else if (clicked_headers_ == Qt::Vertical)
518  {
519  header = collision_table_->verticalHeader();
520  for (const QModelIndex& index : selection_model_->selectedRows())
521  if (!header->isSectionHidden(index.row()))
522  list << index.row();
523  }
524  // if somewhere else than the selection was clicked, only consider this row/column
525  if (!list.contains(clicked_section_))
526  return { clicked_section_ };
527 
528  return list;
529 }
530 
532 {
533  QHeaderView* header;
534  auto list = selectedSections(header);
535 
536  for (auto index : list)
537  header->setSectionHidden(index, true);
538 }
539 
541 {
542  QHeaderView* header;
543  auto selected = selectedSections(header);
544 
545  for (std::size_t index = 0, end = header->count(); index != end; ++index)
546  if (!selected.contains(index))
547  header->setSectionHidden(index, true);
548 }
549 
551 {
552  if (clicked_section_ < 0) // show all
553  {
554  if (clicked_headers_.testFlag(Qt::Horizontal)) // show all columns
555  showSections(collision_table_->horizontalHeader(), { 0, model_->columnCount() - 1 });
556 
557  if (clicked_headers_.testFlag(Qt::Vertical)) // show all rows
558  showSections(collision_table_->verticalHeader(), { 0, model_->rowCount() - 1 });
559 
560  return;
561  }
562 
563  QHeaderView* header;
564  QList<int> list = selectedSections(header);
565  showSections(header, list);
566 }
567 
568 void DefaultCollisionsWidget::showSections(QHeaderView* header, const QList<int>& logicalIndexes)
569 {
570  if (logicalIndexes.size() < 2)
571  return;
572  int prev = 0;
573  for (int next = 1, end = logicalIndexes.size(); next != end; prev = next, ++next)
574  {
575  for (int index = logicalIndexes[prev], index_end = logicalIndexes[next]; index <= index_end; ++index)
576  header->setSectionHidden(index, false);
577  }
578 }
579 
581 {
582  QHeaderView* header;
583  QList<int> list = selectedSections(header);
584  auto m = collision_table_->model();
585 
586  for (auto index : list)
587  {
588  bool changed = false;
589  if (disabled)
590  {
591  const auto& name = m->headerData(index, Qt::Horizontal, Qt::DisplayRole).toString().toStdString();
592  // add name to no_default_collision_links_ (if not yet in there)
593  auto& links = wip_srdf_->no_default_collision_links_;
594  if (std::find(links.begin(), links.end(), name) == links.end())
595  {
596  links.push_back(name);
597  changed = true;
598  }
599  // remove-erase disabled pairs that are redundant now
600  auto& pairs = wip_srdf_->disabled_collision_pairs_;
601  auto last = std::remove_if(pairs.begin(), pairs.end(),
602  [&name](const auto& p) { return p.link1_ == name || p.link2_ == name; });
603  changed |= last != pairs.end();
604  pairs.erase(last, pairs.end());
605  }
606  else
607  {
608  const auto& name = m->headerData(index, Qt::Horizontal, Qt::DisplayRole).toString().toStdString();
609  // remove-erase name from no_default_collision_links_
610  auto& links = wip_srdf_->no_default_collision_links_;
611  {
612  auto last = std::remove(links.begin(), links.end(), name);
613  changed |= last != links.end();
614  links.erase(last, links.end());
615  }
616 
617  // remove explicitly enabled pairs
618  auto& pairs = wip_srdf_->enabled_collision_pairs_;
619  auto last = std::remove_if(pairs.begin(), pairs.end(), [&name, &links](const auto& p) {
620  return (p.link1_ == name && std::find(links.begin(), links.end(), p.link2_) == links.end()) ||
621  (p.link2_ == name && std::find(links.begin(), links.end(), p.link1_) == links.end());
622  });
623  pairs.erase(last, pairs.end());
624  }
625  if (changed)
626  btn_revert_->setEnabled(true);
627  }
628 }
629 
631 {
632  *wip_srdf_ = *config_data_->srdf_;
634  btn_revert_->setEnabled(false); // no changes to revert
635 }
636 
637 bool DefaultCollisionsWidget::eventFilter(QObject* object, QEvent* event)
638 {
639  if (object != collision_table_)
640  return false; // leave event unhandled
641 
642  if (event->type() == QEvent::Enter)
643  {
644  // grab focus as soon as mouse enters to allow for <space> to work in all cases
645  collision_table_->setFocus();
646  return false;
647  }
648  else if (event->type() == QEvent::KeyPress)
649  {
650  QKeyEvent* key_event = static_cast<QKeyEvent*>(event);
651  if (key_event->key() != Qt::Key_Space)
652  return false;
653 
654  toggleSelection(selection_model_->selection());
655  return true; // no need for further processing
656  }
657 
658  return false;
659 }
660 
661 void DefaultCollisionsWidget::toggleSelection(QItemSelection selection)
662 {
663  // remove hidden rows / columns from selection
664  int rows = model_->rowCount();
665  int cols = model_->columnCount();
666  for (int r = 0; r != rows; ++r)
667  {
668  if (collision_table_->isRowHidden(r))
669  selection.merge(QItemSelection(model_->index(r, 0), model_->index(r, cols - 1)), QItemSelectionModel::Deselect);
670  }
671  for (int c = 0; c != cols; ++c)
672  {
673  if (collision_table_->isColumnHidden(c))
674  selection.merge(QItemSelection(model_->index(0, c), model_->index(rows - 1, c)), QItemSelectionModel::Deselect);
675  }
676 
677  // set all selected items to inverse value of current item
678  const QModelIndex& cur_idx = selection_model_->currentIndex();
679  if (view_mode_buttons_->checkedId() == MATRIX_MODE)
680  {
681  QModelIndex input_index;
682  if (cur_idx.flags() & Qt::ItemIsUserCheckable)
683  input_index = cur_idx; // if current index is checkable, this serves as input
684  else
685  { // search for first checkable index in selection that can serve as input
686  for (const auto idx : selection.indexes())
687  {
688  if (idx.flags() & Qt::ItemIsUserCheckable)
689  {
690  input_index = idx;
691  break;
692  }
693  }
694  if (!input_index.isValid())
695  return; // no valid selection
696  }
697 
698  bool current = model_->data(input_index, Qt::CheckStateRole) == Qt::Checked;
700  m->setEnabled(selection, !current);
701  }
702  else
703  {
704  bool current = model_->data(model_->index(cur_idx.row(), 2), Qt::CheckStateRole) == Qt::Checked;
706  m->setEnabled(selection, !current);
707  }
708 }
709 
710 // ******************************************************************************************
711 // GUI func for updating number of samples
712 // ******************************************************************************************
714 {
715  sample_spinbox_->blockSignals(true);
716  sample_slider_->blockSignals(true);
717 
718  int rounded_value = round(value / 1000.0) * 1000;
719  if (!sample_spinbox_->hasFocus())
720  {
721  sample_spinbox_->setValue(rounded_value);
722  }
723  sample_slider_->setValue(rounded_value);
724 
725  sample_spinbox_->blockSignals(false);
726  sample_slider_->blockSignals(false);
727 }
728 
729 // ******************************************************************************************
730 // Helper function to disable parts of GUI during computation
731 // ******************************************************************************************
733 {
734  controls_box_->setDisabled(disable);
735  collision_table_->setDisabled(disable);
736 
737  if (disable)
738  {
739  progress_bar_->show(); // only show when computation begins
740  progress_label_->show();
741  btn_interrupt_->show();
742  }
743  else
744  {
745  progress_bar_->hide();
746  progress_label_->hide();
747  btn_interrupt_->hide();
748  }
749 
750  QApplication::processEvents(); // allow the progress bar to be shown
751 }
752 
753 // ******************************************************************************************
754 // Changes the table to show or hide collisions that are not disabled (that have collision checking enabled)
755 // ******************************************************************************************
757 {
758  SortFilterProxyModel* m = qobject_cast<SortFilterProxyModel*>(model_);
759  m->setShowAll(collision_checkbox_->checkState() == Qt::Checked);
760 }
761 
762 // ******************************************************************************************
763 // Preview whatever element is selected
764 // ******************************************************************************************
766 {
767  // Unhighlight all links
768  Q_EMIT unhighlightAll();
769 
770  if (!index.isValid())
771  return;
772 
773  // normalize index
774  int r = index.row();
775  int c = index.column();
776  if (r == c)
777  return;
778  if (r > c)
779  std::swap(r, c);
780 
781  // Highlight link pair
782  const QString& first_link = model_->headerData(r, Qt::Vertical, Qt::DisplayRole).toString();
783  const QString& second_link = model_->headerData(c, Qt::Horizontal, Qt::DisplayRole).toString();
784  uint check_state = model_->data(index, Qt::CheckStateRole).toUInt();
785 
786  QColor color = (check_state == Qt::Checked) ? QColor(0, 255, 0) : QColor(255, 0, 0);
787  Q_EMIT highlightLink(first_link.toStdString(), color);
788  Q_EMIT highlightLink(second_link.toStdString(), color);
789 }
790 
792 {
793  // Unhighlight all links
794  Q_EMIT unhighlightAll();
795 
796  if (!index.isValid())
797  return;
798 
799  // Highlight link pair
800  const QString& first_link = model_->data(model_->index(index.row(), 0), Qt::DisplayRole).toString();
801  const QString& second_link = model_->data(model_->index(index.row(), 1), Qt::DisplayRole).toString();
802  uint check_state = model_->data(model_->index(index.row(), 2), Qt::CheckStateRole).toUInt();
803 
804  QColor color = (check_state == Qt::Checked) ? QColor(0, 255, 0) : QColor(255, 0, 0);
805  Q_EMIT highlightLink(first_link.toStdString(), color);
806  Q_EMIT highlightLink(second_link.toStdString(), color);
807 }
808 
809 // ******************************************************************************************
810 // Called when setup assistant navigation switches to this screen
811 // ******************************************************************************************
813 {
814  // srdf backup
815  wip_srdf_ = std::make_shared<srdf::SRDFWriter>(*config_data_->srdf_);
816 
817  // Load the data to the table
819 
820  // Enable the table
821  disableControls(false);
822  btn_revert_->setEnabled(false); // no changes to revert
823 }
824 
826 {
827  if (worker_)
828  {
829  if (QMessageBox::No == QMessageBox::question(this, "Collision Matrix Generation",
830  "Collision Matrix Generation is still active. Cancel computation?",
831  QMessageBox::Yes | QMessageBox::No, QMessageBox::No))
832  return false;
833  if (worker_)
834  {
835  worker_->cancel();
836  worker_->wait();
837  }
838  }
839  *config_data_->srdf_ = *wip_srdf_;
840 
841  return true;
842 }
843 
844 moveit_setup_assistant::MonitorThread::MonitorThread(const boost::function<void(unsigned int*)>& f,
845  QProgressBar* progress_bar)
846  : progress_(0), canceled_(false)
847 {
848  // start worker thread
849  worker_ = boost::thread([f, progress_ptr = &progress_] { f(progress_ptr); });
850  // connect progress bar for updates
851  if (progress_bar)
852  connect(this, SIGNAL(progress(int)), progress_bar, SLOT(setValue(int)));
853 }
854 
856 {
857  // loop until worker thread is finished or cancel is requested
858  while (!canceled_ && progress_ < 100)
859  {
860  Q_EMIT progress(progress_);
861  QThread::msleep(100); // sleep 100ms
862  }
863 
864  // cancel worker thread
865  if (canceled_)
866  worker_.interrupt();
867 
868  worker_.join();
869 
870  Q_EMIT progress(progress_);
871 }
872 
873 } // namespace moveit_setup_assistant
default_collisions_widget.h
moveit_setup_assistant::DefaultCollisionsWidget::setDefaults
void setDefaults(bool enabled)
Definition: default_collisions_widget.cpp:580
moveit_setup_assistant::DefaultCollisionsWidget::btn_generate_
QPushButton * btn_generate_
Definition: default_collisions_widget.h:173
moveit_setup_assistant::DefaultCollisionsWidget::~DefaultCollisionsWidget
~DefaultCollisionsWidget() override
Definition: default_collisions_widget.cpp:270
moveit_setup_assistant::DefaultCollisionsWidget::finishGeneratingCollisionTable
void finishGeneratingCollisionTable()
finish generating collision matrix after worker thread has finished
Definition: default_collisions_widget.cpp:310
moveit_setup_assistant::DefaultCollisionsWidget::changeNumSamples
void changeNumSamples(int value)
GUI func for showing number of samples. value will be rounded in 1000s.
Definition: default_collisions_widget.cpp:713
moveit_setup_assistant::DefaultCollisionsWidget::sample_spinbox_
QSpinBox * sample_spinbox_
Definition: default_collisions_widget.h:171
moveit_setup_assistant::DefaultCollisionsWidget::collision_checkbox_
QCheckBox * collision_checkbox_
Definition: default_collisions_widget.h:179
moveit_setup_assistant::DefaultCollisionsWidget::focusGiven
void focusGiven() override
Called when setup assistant navigation switches to this screen.
Definition: default_collisions_widget.cpp:812
moveit_setup_assistant::DefaultCollisionsWidget::selectedSections
QList< int > selectedSections(QHeaderView *&header) const
Definition: default_collisions_widget.cpp:507
moveit_setup_assistant::DefaultCollisionsWidget::LINEAR_MODE
@ LINEAR_MODE
Definition: default_collisions_widget.h:82
CollisionMatrixModel
Definition: collision_matrix_model.h:48
moveit_setup_assistant::DefaultCollisionsWidget::fraction_label_
QLabel * fraction_label_
Definition: default_collisions_widget.h:180
moveit_setup_assistant::DefaultCollisionsWidget::btn_interrupt_
QPushButton * btn_interrupt_
Definition: default_collisions_widget.h:176
moveit_setup_assistant::DefaultCollisionsWidget::view_mode_buttons_
QButtonGroup * view_mode_buttons_
Definition: default_collisions_widget.h:183
moveit_setup_assistant::DefaultCollisionsWidget::disableControls
void disableControls(bool disable)
Helper function to disable parts of GUI during computation.
Definition: default_collisions_widget.cpp:732
moveit_setup_assistant::MonitorThread::progress
void progress(int)
srdf::SRDFWriter
moveit_setup_assistant::DefaultCollisionsWidget::hideOtherSections
void hideOtherSections()
Definition: default_collisions_widget.cpp:540
r
r
moveit_setup_assistant::MonitorThread::progress_
unsigned int progress_
Definition: default_collisions_widget.h:254
moveit_setup_assistant::DefaultCollisionsWidget::toggleSelection
void toggleSelection(QItemSelection selection)
Definition: default_collisions_widget.cpp:661
rows
int rows
moveit_setup_assistant::DefaultCollisionsWidget::generateCollisionTable
void generateCollisionTable(unsigned int *collision_progress)
The thread that is called to allow the GUI to update. Calls an external function to do calcs.
Definition: default_collisions_widget.cpp:329
moveit_setup_assistant::DefaultCollisionsWidget::selection_model_
QItemSelectionModel * selection_model_
Definition: default_collisions_widget.h:169
moveit_setup_assistant::DefaultCollisionsWidget::link_name_filter_
QLineEdit * link_name_filter_
Definition: default_collisions_widget.h:178
moveit_setup_assistant::MoveItConfigData::COLLISIONS
@ COLLISIONS
Definition: moveit_config_data.h:228
CollisionLinearModel
Definition: collision_linear_model.h:45
moveit_setup_assistant::DefaultCollisionsWidget::loadCollisionTable
void loadCollisionTable()
Update view and data model for the link_pairs data structure.
Definition: default_collisions_widget.cpp:358
SortFilterProxyModel
Definition: collision_linear_model.h:74
moveit_setup_assistant::MonitorThread
Definition: default_collisions_widget.h:233
SetupScreenWidget::highlightLink
void highlightLink(const std::string &name, const QColor &)
Event for telling rviz to highlight a link of the robot.
moveit_setup_assistant::DefaultCollisionsWidget::header_actions_
QList< QAction * > header_actions_
Definition: default_collisions_widget.h:185
moveit_setup_assistant::DefaultCollisionsWidget::clicked_headers_
Qt::Orientations clicked_headers_
Definition: default_collisions_widget.h:186
console.h
srdf::Model::CollisionPair::link2_
std::string link2_
moveit_setup_assistant::MonitorThread::run
void run() override
Definition: default_collisions_widget.cpp:855
moveit_setup_assistant::DefaultCollisionsWidget::DefaultCollisionsWidget
DefaultCollisionsWidget(QWidget *parent, const MoveItConfigDataPtr &config_data)
User interface for editing the default collision matrix list in an SRDF.
Definition: default_collisions_widget.cpp:99
moveit_setup_assistant::MonitorThread::MonitorThread
MonitorThread(const boost::function< void(unsigned int *)> &f, QProgressBar *progress_bar=nullptr)
Definition: default_collisions_widget.cpp:844
moveit_setup_assistant::DefaultCollisionsWidget::revertChanges
void revertChanges()
Revert current changes to collision matrix.
Definition: default_collisions_widget.cpp:630
moveit_setup_assistant::DefaultCollisionsWidget::collision_table_
QTableView * collision_table_
Definition: default_collisions_widget.h:167
moveit_setup_assistant::DefaultCollisionsWidget::worker_
MonitorThread * worker_
Definition: default_collisions_widget.h:192
moveit_setup_assistant::MonitorThread::canceled
bool canceled() const
Definition: default_collisions_widget.h:244
c
c
moveit_setup_assistant::DefaultCollisionsWidget::showSections
void showSections()
Definition: default_collisions_widget.cpp:550
moveit_setup_assistant::MonitorThread::worker_
boost::thread worker_
Definition: default_collisions_widget.h:253
moveit_setup_assistant::DefaultCollisionsWidget::MATRIX_MODE
@ MATRIX_MODE
Definition: default_collisions_widget.h:81
moveit_setup_assistant::DefaultCollisionsWidget::model_
QAbstractItemModel * model_
Definition: default_collisions_widget.h:168
moveit_setup_assistant::DefaultCollisionsWidget::previewSelectedMatrix
void previewSelectedMatrix(const QModelIndex &index)
Called when current row has changed.
Definition: default_collisions_widget.cpp:765
value
float value
SetupScreenWidget
Definition: setup_screen_widget.h:44
prev
EndPoint * prev[3]
moveit_setup_assistant::computeDefaultCollisions
LinkPairMap computeDefaultCollisions(const planning_scene::PlanningSceneConstPtr &parent_scene, unsigned int *progress, const bool include_never_colliding, const unsigned int trials, const double min_collision_faction, const bool verbose)
Generate an adjacency list of links that are always and never in collision, to speed up collision det...
Definition: compute_default_collisions.cpp:205
next
EndPoint * next[3]
SetupScreenWidget::unhighlightAll
void unhighlightAll()
Event for telling rviz to unhighlight all links of the robot.
ROS_INFO_STREAM
#define ROS_INFO_STREAM(args)
name
name
moveit_setup_assistant::DefaultCollisionsWidget::interruptGeneratingCollisionTable
void interruptGeneratingCollisionTable()
interrupt generating collision matrix
Definition: default_collisions_widget.cpp:293
moveit_setup_assistant::DefaultCollisionsWidget::clicked_section_
int clicked_section_
Definition: default_collisions_widget.h:187
header_widget.h
moveit_setup_assistant::DefaultCollisionsWidget::config_data_
moveit_setup_assistant::MoveItConfigDataPtr config_data_
Contains all the configuration data for the setup assistant.
Definition: default_collisions_widget.h:195
moveit_setup_assistant
Definition: compute_default_collisions.h:46
moveit_setup_assistant::DefaultCollisionsWidget::showHeaderContextMenu
void showHeaderContextMenu(const QPoint &p)
Definition: default_collisions_widget.cpp:474
moveit_setup_assistant::DefaultCollisionsWidget::sample_slider_
QSlider * sample_slider_
Definition: default_collisions_widget.h:172
moveit_setup_assistant::disabledReasonToString
const std::string disabledReasonToString(DisabledReason reason)
Converts a reason for disabling a link pair into a string.
Definition: compute_default_collisions.cpp:685
moveit_setup_assistant::MonitorThread::cancel
void cancel()
Definition: default_collisions_widget.h:240
moveit_setup_assistant::DefaultCollisionsWidget::layout_
QVBoxLayout * layout_
Definition: default_collisions_widget.h:170
moveit_setup_assistant::DefaultCollisionsWidget::progress_label_
QLabel * progress_label_
Definition: default_collisions_widget.h:177
m
m
moveit_setup_assistant::DefaultCollisionsWidget::wip_srdf_
srdf::SRDFWriterPtr wip_srdf_
Working copy of SRDF config.
Definition: default_collisions_widget.h:197
f
f
moveit_setup_assistant::DefaultCollisionsWidget::hideSections
void hideSections()
Definition: default_collisions_widget.cpp:531
moveit_setup_assistant::DefaultCollisionsWidget::previewSelectedLinear
void previewSelectedLinear(const QModelIndex &index)
Definition: default_collisions_widget.cpp:791
moveit_setup_assistant::DefaultCollisionsWidget::collisionsChanged
void collisionsChanged(const QModelIndex &index)
Collision model changed.
Definition: default_collisions_widget.cpp:454
moveit_setup_assistant::DefaultCollisionsWidget::focusLost
bool focusLost() override
Called when setup assistant navigation switches away from this screen.
Definition: default_collisions_widget.cpp:825
moveit_setup_assistant::DefaultCollisionsWidget::startGeneratingCollisionTable
void startGeneratingCollisionTable()
start generating collision matrix in a worker thread
Definition: default_collisions_widget.cpp:278
moveit_setup_assistant::HeaderWidget
Definition: header_widget.h:51
srdf
index
unsigned int index
cols
int cols
moveit_setup_assistant::DefaultCollisionsWidget::btn_revert_
QPushButton * btn_revert_
Definition: default_collisions_widget.h:182
find
auto find(Container &pairs, const std::string &link1, const std::string &link2)
Definition: collision_matrix_model.cpp:98
header
const std::string header
moveit_setup_assistant::DefaultCollisionsWidget::progress_bar_
QProgressBar * progress_bar_
Definition: default_collisions_widget.h:175
verbose
bool verbose
moveit_setup_assistant::DefaultCollisionsWidget::eventFilter
bool eventFilter(QObject *object, QEvent *event) override
Definition: default_collisions_widget.cpp:637
srdf::Model::CollisionPair
moveit_setup_assistant::RotatedHeaderView
Definition: rotated_header_view.h:75
moveit_setup_assistant::LinkPairMap
std::map< std::pair< std::string, std::string >, LinkPairData > LinkPairMap
LinkPairMap is an adjacency list structure containing links in string-based form. Used for disabled l...
Definition: compute_default_collisions.h:76
srdf::Model::CollisionPair::reason_
std::string reason_
moveit_setup_assistant::DefaultCollisionsWidget::controls_box_
QGroupBox * controls_box_
Definition: default_collisions_widget.h:174
srdf::Model::CollisionPair::link1_
std::string link1_
SortFilterProxyModel::setShowAll
void setShowAll(bool show_all)
Definition: collision_linear_model.cpp:224
moveit_setup_assistant::DefaultCollisionsWidget::fraction_spinbox_
QSpinBox * fraction_spinbox_
Definition: default_collisions_widget.h:181
moveit_setup_assistant::DefaultCollisionsWidget::checkedFilterChanged
void checkedFilterChanged()
Change filter settings to show/hide enabled collisions.
Definition: default_collisions_widget.cpp:756


moveit_setup_assistant
Author(s): Dave Coleman
autogenerated on Sat May 3 2025 02:28:04