37 #include <QApplication>
38 #include <QHBoxLayout>
41 #include <QMessageBox>
42 #include <QRadioButton>
61 selection_model_ =
nullptr;
65 layout_ =
new QVBoxLayout(
this);
69 "Optimize Self-Collision Checking",
70 "This searches for pairs of robot links that can safely be disabled from collision checking, decreasing motion "
71 "planning time. These pairs are disabled when they are always in collision, never in collision, in collision in "
72 "the robot's default position, or when the links are adjacent to each other on the kinematic chain. Sampling "
73 "density specifies how many random robot positions to check for self collision.",
75 layout_->addWidget(header);
78 controls_box_ =
new QGroupBox(
this);
79 layout_->addWidget(controls_box_);
80 QVBoxLayout* controls_box_layout =
new QVBoxLayout(controls_box_);
82 QHBoxLayout* slider_layout =
new QHBoxLayout();
83 slider_layout->setAlignment(Qt::AlignTop | Qt::AlignLeft);
84 controls_box_layout->addLayout(slider_layout);
87 QLabel* density_left_label =
new QLabel(
this);
88 density_left_label->setText(
"Sampling Density: Low");
89 slider_layout->addWidget(density_left_label);
92 density_slider_ =
new QSlider(
this);
93 density_slider_->setTickPosition(QSlider::TicksBelow);
94 density_slider_->setMinimum(0);
95 density_slider_->setMaximum(99);
96 density_slider_->setSingleStep(10);
97 density_slider_->setPageStep(50);
98 density_slider_->setSliderPosition(9);
99 density_slider_->setTickInterval(10);
100 density_slider_->setOrientation(Qt::Horizontal);
101 slider_layout->addWidget(density_slider_);
102 connect(density_slider_, SIGNAL(valueChanged(
int)),
this, SLOT(changeDensityLabel(
int)));
105 QLabel* density_right_label =
new QLabel(
this);
106 density_right_label->setText(
"High ");
107 slider_layout->addWidget(density_right_label);
110 density_value_label_ =
new QLabel(
this);
111 density_value_label_->setMinimumWidth(50);
112 slider_layout->addWidget(density_value_label_);
113 changeDensityLabel(density_slider_->value());
115 QHBoxLayout* buttons_layout =
new QHBoxLayout();
116 buttons_layout->setAlignment(Qt::AlignRight);
117 controls_box_layout->addLayout(buttons_layout);
120 fraction_label_ =
new QLabel(
this);
121 fraction_label_->setText(
"Min. collisions for \"always\"-colliding pairs:");
122 buttons_layout->addWidget(fraction_label_);
124 fraction_spinbox_ =
new QSpinBox(
this);
125 fraction_spinbox_->setRange(1, 100);
126 fraction_spinbox_->setValue(95);
127 fraction_spinbox_->setSuffix(
"%");
128 buttons_layout->addWidget(fraction_spinbox_);
131 btn_generate_ =
new QPushButton(
this);
132 btn_generate_->setText(
"&Generate Collision Matrix");
133 btn_generate_->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
134 connect(btn_generate_, SIGNAL(clicked()),
this, SLOT(startGeneratingCollisionTable()));
135 buttons_layout->addWidget(btn_generate_);
140 progress_label_ =
new QLabel(
this);
141 progress_label_->setText(
"Generating Default Collision Matrix");
142 progress_label_->hide();
143 layout_->addWidget(progress_label_);
146 progress_bar_ =
new QProgressBar(
this);
147 progress_bar_->setMaximum(100);
148 progress_bar_->setMinimum(0);
149 progress_bar_->hide();
150 layout_->addWidget(progress_bar_);
155 collision_table_ =
new QTableView(
this);
156 layout_->addWidget(collision_table_);
159 action =
new QAction(tr(
"Show"),
this);
160 header_actions_ <<
action;
161 connect(
action, SIGNAL(triggered()),
this, SLOT(showSections()));
162 action =
new QAction(tr(
"Hide"),
this);
163 header_actions_ <<
action;
164 connect(
action, SIGNAL(triggered()),
this, SLOT(hideSections()));
165 action =
new QAction(tr(
"Hide others"),
this);
166 header_actions_ <<
action;
167 connect(
action, SIGNAL(triggered()),
this, SLOT(hideOtherSections()));
171 QHBoxLayout* bottom_layout =
new QHBoxLayout();
172 bottom_layout->setAlignment(Qt::AlignRight);
173 layout_->addLayout(bottom_layout);
176 link_name_filter_ =
new QLineEdit(
this);
177 link_name_filter_->setPlaceholderText(
"link name filter");
178 bottom_layout->addWidget(link_name_filter_);
181 collision_checkbox_ =
new QCheckBox(
this);
182 collision_checkbox_->setText(
"show enabled pairs");
183 connect(collision_checkbox_, SIGNAL(toggled(
bool)),
this, SLOT(checkedFilterChanged()));
184 bottom_layout->addWidget(collision_checkbox_);
187 view_mode_buttons_ =
new QButtonGroup(
this);
188 QRadioButton* radio_btn;
189 radio_btn =
new QRadioButton(
"linear view");
190 bottom_layout->addWidget(radio_btn);
191 view_mode_buttons_->addButton(radio_btn,
LINEAR_MODE);
192 radio_btn->setChecked(
true);
194 radio_btn =
new QRadioButton(
"matrix view");
195 bottom_layout->addWidget(radio_btn);
196 view_mode_buttons_->addButton(radio_btn,
MATRIX_MODE);
197 connect(view_mode_buttons_, SIGNAL(buttonClicked(
int)),
this, SLOT(loadCollisionTable()));
200 btn_revert_ =
new QPushButton(
this);
201 btn_revert_->setText(
"&Revert");
202 btn_revert_->setToolTip(
"Revert current changes to collision matrix");
203 btn_revert_->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
204 btn_revert_->setDisabled(
true);
205 connect(btn_revert_, SIGNAL(clicked()),
this, SLOT(revertChanges()));
206 bottom_layout->addWidget(btn_revert_);
209 setWindowTitle(
"Default Collision Matrix");
211 collision_table_->installEventFilter(
this);
222 void DefaultCollisionsWidget::startGeneratingCollisionTable()
225 disableControls(
true);
226 btn_revert_->setEnabled(
true);
229 setup_step_.
startGenerationThread(density_slider_->value() * 1000 + 1000, fraction_spinbox_->value() / 100.0);
233 connect(worker_, SIGNAL(finished()),
this, SLOT(finishGeneratingCollisionTable()));
240 void DefaultCollisionsWidget::finishGeneratingCollisionTable()
246 loadCollisionTable();
249 disableControls(
false);
251 worker_->deleteLater();
258 void DefaultCollisionsWidget::loadCollisionTable()
260 CollisionMatrixModel* matrix_model =
262 QAbstractItemModel* model;
264 if (view_mode_buttons_->checkedId() ==
MATRIX_MODE)
266 model = matrix_model;
270 CollisionLinearModel* linear_model =
new CollisionLinearModel(matrix_model);
271 SortFilterProxyModel* sorted_model =
new SortFilterProxyModel();
272 model = sorted_model;
273 sorted_model->setSourceModel(linear_model);
275 linear_model->setParent(sorted_model);
276 matrix_model->setParent(linear_model);
278 connect(link_name_filter_, SIGNAL(textChanged(QString)), model, SLOT(setFilterRegExp(QString)));
279 QMetaObject::invokeMethod(model,
"setFilterRegExp", Q_ARG(QString, link_name_filter_->text()));
281 collision_table_->setModel(model);
287 delete selection_model_;
288 selection_model_ = collision_table_->selectionModel();
290 QHeaderView *horizontal_header, *vertical_header;
293 if (view_mode_buttons_->checkedId() ==
MATRIX_MODE)
295 connect(selection_model_, SIGNAL(currentChanged(QModelIndex, QModelIndex)),
this,
296 SLOT(previewSelectedMatrix(QModelIndex)));
298 collision_table_->setSelectionBehavior(QAbstractItemView::SelectItems);
299 collision_table_->setSelectionMode(QAbstractItemView::ExtendedSelection);
301 collision_table_->setHorizontalHeader(horizontal_header =
new RotatedHeaderView(Qt::Horizontal,
this));
302 collision_table_->setVerticalHeader(vertical_header =
new RotatedHeaderView(Qt::Vertical,
this));
303 collision_table_->setSortingEnabled(
false);
305 collision_checkbox_->hide();
306 horizontal_header->setVisible(
true);
307 vertical_header->setVisible(
true);
309 horizontal_header->setContextMenuPolicy(Qt::CustomContextMenu);
310 connect(horizontal_header, SIGNAL(customContextMenuRequested(QPoint)),
this, SLOT(showHeaderContextMenu(QPoint)));
311 vertical_header->setContextMenuPolicy(Qt::CustomContextMenu);
312 connect(vertical_header, SIGNAL(customContextMenuRequested(QPoint)),
this, SLOT(showHeaderContextMenu(QPoint)));
316 connect(selection_model_, SIGNAL(currentChanged(QModelIndex, QModelIndex)),
this,
317 SLOT(previewSelectedLinear(QModelIndex)));
319 collision_table_->setSelectionBehavior(QAbstractItemView::SelectRows);
320 collision_table_->setSelectionMode(QAbstractItemView::ExtendedSelection);
322 collision_table_->setHorizontalHeader(horizontal_header =
new QHeaderView(Qt::Horizontal,
this));
323 collision_table_->setVerticalHeader(vertical_header =
new QHeaderView(Qt::Vertical,
this));
324 collision_table_->sortByColumn(0, Qt::AscendingOrder);
325 collision_table_->setSortingEnabled(
true);
327 collision_checkbox_->show();
328 horizontal_header->setVisible(
true);
329 vertical_header->setVisible(
true);
331 vertical_header->setContextMenuPolicy(Qt::CustomContextMenu);
332 connect(vertical_header, SIGNAL(customContextMenuRequested(QPoint)),
this, SLOT(showHeaderContextMenu(QPoint)));
334 #if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
335 horizontal_header->setSectionsClickable(
true);
336 vertical_header->setSectionsClickable(
true);
338 horizontal_header->setClickable(
true);
339 vertical_header->setClickable(
true);
344 #if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
345 connect(model_, SIGNAL(dataChanged(QModelIndex, QModelIndex, QVector<int>)),
this,
346 SLOT(collisionsChanged(QModelIndex)));
348 connect(model_, SIGNAL(dataChanged(QModelIndex, QModelIndex)),
this, SLOT(collisionsChanged(QModelIndex)));
352 void DefaultCollisionsWidget::collisionsChanged(
const QModelIndex& index)
354 btn_revert_->setEnabled(
true);
356 if (!index.isValid())
359 bool linear_mode = (view_mode_buttons_->checkedId() ==
LINEAR_MODE);
360 const QItemSelection& selection = selection_model_->selection();
361 if ((linear_mode && !selection.contains(index)) ||
362 (!linear_mode && !(selection.contains(index) ||
363 selection.contains(model_->index(index.column(), index.row())))))
365 QItemSelectionModel::SelectionFlags flags = QItemSelectionModel::Select | QItemSelectionModel::Current;
367 flags |= QItemSelectionModel::Rows;
368 selection_model_->setCurrentIndex(index, flags);
372 void DefaultCollisionsWidget::showHeaderContextMenu(
const QPoint& p)
376 if (sender() == collision_table_->verticalHeader())
378 clicked_section_ = collision_table_->verticalHeader()->logicalIndexAt(p);
379 clicked_headers_ = Qt::Vertical;
380 global = collision_table_->verticalHeader()->mapToGlobal(p);
382 else if (sender() == collision_table_->horizontalHeader())
384 clicked_section_ = collision_table_->horizontalHeader()->logicalIndexAt(p);
385 clicked_headers_ = Qt::Horizontal;
386 global = collision_table_->horizontalHeader()->mapToGlobal(p);
390 clicked_section_ = -1;
391 clicked_headers_ = Qt::Horizontal | Qt::Vertical;
395 if (clicked_section_ < 0)
397 menu.addAction(header_actions_.at(0));
401 menu.addActions(header_actions_);
405 clicked_headers_ = {};
406 clicked_section_ = -1;
409 void DefaultCollisionsWidget::hideSections()
412 QHeaderView* header =
nullptr;
413 if (clicked_headers_ == Qt::Horizontal)
415 for (
const QModelIndex& index : selection_model_->selectedColumns())
416 list << index.column();
417 header = collision_table_->horizontalHeader();
419 else if (clicked_headers_ == Qt::Vertical)
421 for (
const QModelIndex& index : selection_model_->selectedRows())
423 header = collision_table_->verticalHeader();
427 if (!list.contains(clicked_section_))
430 list << clicked_section_;
433 for (
auto index : list)
434 header->setSectionHidden(index,
true);
437 void DefaultCollisionsWidget::hideOtherSections()
440 QHeaderView* header =
nullptr;
441 if (clicked_headers_ == Qt::Horizontal)
443 header = collision_table_->horizontalHeader();
444 for (
const QModelIndex& index : selection_model_->selectedColumns())
446 if (!header->isSectionHidden(index.column()))
447 list << index.column();
450 else if (clicked_headers_ == Qt::Vertical)
452 header = collision_table_->verticalHeader();
453 for (
const QModelIndex& index : selection_model_->selectedRows())
455 if (!header->isSectionHidden(index.row()))
461 if (!list.contains(clicked_section_))
464 list << clicked_section_;
468 for (std::size_t index = 0, end = header->count(); index != end; ++index)
469 header->setSectionHidden(index,
true);
472 for (
auto index : list)
473 header->setSectionHidden(index,
false);
476 void DefaultCollisionsWidget::showSections()
479 if (clicked_section_ < 0)
481 if (clicked_headers_.testFlag(Qt::Horizontal))
485 list << 0 << model_->columnCount() - 1;
486 showSections(collision_table_->horizontalHeader(), list);
489 if (clicked_headers_.testFlag(Qt::Vertical))
492 list << 0 << model_->rowCount() - 1;
493 showSections(collision_table_->verticalHeader(), list);
498 QHeaderView* header =
nullptr;
499 if (clicked_headers_ == Qt::Horizontal)
501 for (
const QModelIndex& index : selection_model_->selectedColumns())
502 list << index.column();
503 header = collision_table_->horizontalHeader();
505 else if (clicked_headers_ == Qt::Vertical)
507 for (
const QModelIndex& index : selection_model_->selectedRows())
509 header = collision_table_->verticalHeader();
513 if (!list.contains(clicked_section_))
516 list << clicked_section_;
518 showSections(header, list);
520 void DefaultCollisionsWidget::showSections(QHeaderView* header,
const QList<int>& logicalIndexes)
522 if (logicalIndexes.size() < 2)
525 for (
int next = 1, end = logicalIndexes.size(); next != end; prev = next, ++next)
527 for (
int index = logicalIndexes[prev], index_end = logicalIndexes[next]; index <= index_end; ++index)
528 header->setSectionHidden(index,
false);
532 void DefaultCollisionsWidget::revertChanges()
535 loadCollisionTable();
536 btn_revert_->setEnabled(
false);
539 bool DefaultCollisionsWidget::eventFilter(QObject*
object, QEvent* event)
541 if (
object != collision_table_)
544 if (event->type() == QEvent::Enter)
547 collision_table_->setFocus();
550 else if (event->type() == QEvent::KeyPress)
552 QKeyEvent* key_event =
static_cast<QKeyEvent*
>(event);
553 if (key_event->key() != Qt::Key_Space)
556 toggleSelection(selection_model_->selection());
563 void DefaultCollisionsWidget::toggleSelection(QItemSelection selection)
566 int rows = model_->rowCount();
567 int cols = model_->columnCount();
568 for (
int r = 0; r != rows; ++r)
570 if (collision_table_->isRowHidden(r))
571 selection.merge(QItemSelection(model_->index(r, 0), model_->index(r, cols - 1)), QItemSelectionModel::Deselect);
573 for (
int c = 0; c != cols; ++c)
575 if (collision_table_->isColumnHidden(c))
576 selection.merge(QItemSelection(model_->index(0, c), model_->index(rows - 1, c)), QItemSelectionModel::Deselect);
580 const QModelIndex& cur_idx = selection_model_->currentIndex();
581 if (view_mode_buttons_->checkedId() ==
MATRIX_MODE)
583 QModelIndex input_index;
584 if (cur_idx.flags() & Qt::ItemIsUserCheckable)
586 input_index = cur_idx;
590 for (
const auto idx : selection.indexes())
592 if (idx.flags() & Qt::ItemIsUserCheckable)
598 if (!input_index.isValid())
602 bool current = model_->data(input_index, Qt::CheckStateRole) == Qt::Checked;
603 CollisionMatrixModel* m =
static_cast<CollisionMatrixModel*
>(model_);
604 m->setEnabled(selection, !current);
608 bool current = model_->data(model_->index(cur_idx.row(), 2), Qt::CheckStateRole) == Qt::Checked;
609 SortFilterProxyModel* m =
static_cast<SortFilterProxyModel*
>(model_);
610 m->setEnabled(selection, !current);
617 void DefaultCollisionsWidget::changeDensityLabel(
int value)
619 density_value_label_->setText(QString::number(value * 1000 + 1000));
625 void DefaultCollisionsWidget::disableControls(
bool disable)
627 controls_box_->setDisabled(disable);
628 collision_table_->setDisabled(disable);
632 progress_bar_->show();
633 progress_label_->show();
637 progress_label_->hide();
638 progress_bar_->hide();
641 QApplication::processEvents();
647 void DefaultCollisionsWidget::checkedFilterChanged()
649 SortFilterProxyModel* m = qobject_cast<SortFilterProxyModel*>(model_);
650 m->setShowAll(collision_checkbox_->checkState() == Qt::Checked);
656 void DefaultCollisionsWidget::previewSelectedMatrix(
const QModelIndex& index)
661 if (!index.isValid())
666 int c = index.column();
673 const QString& first_link = model_->headerData(r, Qt::Vertical, Qt::DisplayRole).toString();
674 const QString& second_link = model_->headerData(c, Qt::Horizontal, Qt::DisplayRole).toString();
675 uint check_state = model_->data(index, Qt::CheckStateRole).toUInt();
677 QColor color = (check_state == Qt::Checked) ? QColor(0, 255, 0) : QColor(255, 0, 0);
682 void DefaultCollisionsWidget::previewSelectedLinear(
const QModelIndex& index)
687 if (!index.isValid())
691 const QString& first_link = model_->data(model_->index(index.row(), 0), Qt::DisplayRole).toString();
692 const QString& second_link = model_->data(model_->index(index.row(), 1), Qt::DisplayRole).toString();
693 uint check_state = model_->data(model_->index(index.row(), 2), Qt::CheckStateRole).toUInt();
695 QColor color = (check_state == Qt::Checked) ? QColor(0, 255, 0) : QColor(255, 0, 0);
703 void DefaultCollisionsWidget::focusGiven()
709 loadCollisionTable();
712 disableControls(
false);
713 btn_revert_->setEnabled(
false);
716 bool DefaultCollisionsWidget::focusLost()
720 if (QMessageBox::No == QMessageBox::question(
this,
"Collision Matrix Generation",
721 "Collision Matrix Generation is still active. Cancel computation?",
722 QMessageBox::Yes | QMessageBox::No, QMessageBox::No))
734 : setup_step_(setup_step), canceled_(false)
738 connect(
this, SIGNAL(
progress(
int)), progress_bar, SLOT(setValue(
int)));
748 QThread::msleep(100);
762 #include <pluginlib/class_list_macros.hpp>
PLUGINLIB_EXPORT_CLASS(cached_ik_kinematics_plugin::CachedIKKinematicsPlugin< kdl_kinematics_plugin::KDLKinematicsPlugin >, kinematics::KinematicsBase)
void highlightLink(const std::string &link_name, const QColor &color)
std::vector< std::string > getCollidingLinks()
void startGenerationThread(unsigned int num_trials, double min_frac, bool verbose=true)
int getThreadProgress() const
void linkPairsFromSRDF()
Load Link Pairs from SRDF Format.
void joinGenerationThread()
void cancelGenerationThread()
LinkPairMap & getLinkPairs()
void linkPairsToSRDF()
Output Link Pairs to SRDF Format.
QThread to monitor progress of the setup step.
MonitorThread(DefaultCollisions &setup_step, QProgressBar *progress_bar=nullptr)