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)
396 menu.addAction(header_actions_.at(0));
398 menu.addActions(header_actions_);
401 clicked_headers_ = {};
402 clicked_section_ = -1;
405 void DefaultCollisionsWidget::hideSections()
408 QHeaderView* header =
nullptr;
409 if (clicked_headers_ == Qt::Horizontal)
411 for (
const QModelIndex& index : selection_model_->selectedColumns())
412 list << index.column();
413 header = collision_table_->horizontalHeader();
415 else if (clicked_headers_ == Qt::Vertical)
417 for (
const QModelIndex& index : selection_model_->selectedRows())
419 header = collision_table_->verticalHeader();
423 if (!list.contains(clicked_section_))
426 list << clicked_section_;
429 for (
auto index : list)
430 header->setSectionHidden(index,
true);
433 void DefaultCollisionsWidget::hideOtherSections()
436 QHeaderView* header =
nullptr;
437 if (clicked_headers_ == Qt::Horizontal)
439 header = collision_table_->horizontalHeader();
440 for (
const QModelIndex& index : selection_model_->selectedColumns())
441 if (!header->isSectionHidden(index.column()))
442 list << index.column();
444 else if (clicked_headers_ == Qt::Vertical)
446 header = collision_table_->verticalHeader();
447 for (
const QModelIndex& index : selection_model_->selectedRows())
448 if (!header->isSectionHidden(index.row()))
453 if (!list.contains(clicked_section_))
456 list << clicked_section_;
460 for (std::size_t index = 0, end = header->count(); index != end; ++index)
461 header->setSectionHidden(index,
true);
464 for (
auto index : list)
465 header->setSectionHidden(index,
false);
468 void DefaultCollisionsWidget::showSections()
471 if (clicked_section_ < 0)
473 if (clicked_headers_.testFlag(Qt::Horizontal))
477 list << 0 << model_->columnCount() - 1;
478 showSections(collision_table_->horizontalHeader(), list);
481 if (clicked_headers_.testFlag(Qt::Vertical))
484 list << 0 << model_->rowCount() - 1;
485 showSections(collision_table_->verticalHeader(), list);
490 QHeaderView* header =
nullptr;
491 if (clicked_headers_ == Qt::Horizontal)
493 for (
const QModelIndex& index : selection_model_->selectedColumns())
494 list << index.column();
495 header = collision_table_->horizontalHeader();
497 else if (clicked_headers_ == Qt::Vertical)
499 for (
const QModelIndex& index : selection_model_->selectedRows())
501 header = collision_table_->verticalHeader();
505 if (!list.contains(clicked_section_))
508 list << clicked_section_;
510 showSections(header, list);
512 void DefaultCollisionsWidget::showSections(QHeaderView* header,
const QList<int>& logicalIndexes)
514 if (logicalIndexes.size() < 2)
517 for (
int next = 1, end = logicalIndexes.size(); next != end; prev = next, ++next)
519 for (
int index = logicalIndexes[prev], index_end = logicalIndexes[next]; index <= index_end; ++index)
520 header->setSectionHidden(index,
false);
524 void DefaultCollisionsWidget::revertChanges()
527 loadCollisionTable();
528 btn_revert_->setEnabled(
false);
531 bool DefaultCollisionsWidget::eventFilter(QObject*
object, QEvent* event)
533 if (
object != collision_table_)
536 if (event->type() == QEvent::Enter)
539 collision_table_->setFocus();
542 else if (event->type() == QEvent::KeyPress)
544 QKeyEvent* key_event =
static_cast<QKeyEvent*
>(event);
545 if (key_event->key() != Qt::Key_Space)
548 toggleSelection(selection_model_->selection());
555 void DefaultCollisionsWidget::toggleSelection(QItemSelection selection)
558 int rows = model_->rowCount();
559 int cols = model_->columnCount();
560 for (
int r = 0;
r != rows; ++
r)
562 if (collision_table_->isRowHidden(
r))
563 selection.merge(QItemSelection(model_->index(
r, 0), model_->index(
r, cols - 1)), QItemSelectionModel::Deselect);
565 for (
int c = 0;
c != cols; ++
c)
567 if (collision_table_->isColumnHidden(
c))
568 selection.merge(QItemSelection(model_->index(0,
c), model_->index(rows - 1,
c)), QItemSelectionModel::Deselect);
572 const QModelIndex& cur_idx = selection_model_->currentIndex();
573 if (view_mode_buttons_->checkedId() ==
MATRIX_MODE)
575 QModelIndex input_index;
576 if (cur_idx.flags() & Qt::ItemIsUserCheckable)
577 input_index = cur_idx;
580 for (
const auto idx : selection.indexes())
582 if (idx.flags() & Qt::ItemIsUserCheckable)
588 if (!input_index.isValid())
592 bool current = model_->data(input_index, Qt::CheckStateRole) == Qt::Checked;
593 CollisionMatrixModel* m =
static_cast<CollisionMatrixModel*
>(model_);
594 m->setEnabled(selection, !current);
598 bool current = model_->data(model_->index(cur_idx.row(), 2), Qt::CheckStateRole) == Qt::Checked;
599 SortFilterProxyModel* m =
static_cast<SortFilterProxyModel*
>(model_);
600 m->setEnabled(selection, !current);
607 void DefaultCollisionsWidget::changeDensityLabel(
int value)
609 density_value_label_->setText(QString::number(value * 1000 + 1000));
615 void DefaultCollisionsWidget::disableControls(
bool disable)
617 controls_box_->setDisabled(disable);
618 collision_table_->setDisabled(disable);
622 progress_bar_->show();
623 progress_label_->show();
627 progress_label_->hide();
628 progress_bar_->hide();
631 QApplication::processEvents();
637 void DefaultCollisionsWidget::checkedFilterChanged()
639 SortFilterProxyModel* m = qobject_cast<SortFilterProxyModel*>(model_);
640 m->setShowAll(collision_checkbox_->checkState() == Qt::Checked);
646 void DefaultCollisionsWidget::previewSelectedMatrix(
const QModelIndex& index)
651 if (!index.isValid())
656 int c = index.column();
663 const QString& first_link = model_->headerData(
r, Qt::Vertical, Qt::DisplayRole).toString();
664 const QString& second_link = model_->headerData(
c, Qt::Horizontal, Qt::DisplayRole).toString();
665 uint check_state = model_->data(index, Qt::CheckStateRole).toUInt();
667 QColor color = (check_state == Qt::Checked) ? QColor(0, 255, 0) : QColor(255, 0, 0);
672 void DefaultCollisionsWidget::previewSelectedLinear(
const QModelIndex& index)
677 if (!index.isValid())
681 const QString& first_link = model_->data(model_->index(index.row(), 0), Qt::DisplayRole).toString();
682 const QString& second_link = model_->data(model_->index(index.row(), 1), Qt::DisplayRole).toString();
683 uint check_state = model_->data(model_->index(index.row(), 2), Qt::CheckStateRole).toUInt();
685 QColor color = (check_state == Qt::Checked) ? QColor(0, 255, 0) : QColor(255, 0, 0);
693 void DefaultCollisionsWidget::focusGiven()
699 loadCollisionTable();
702 disableControls(
false);
703 btn_revert_->setEnabled(
false);
706 bool DefaultCollisionsWidget::focusLost()
710 if (QMessageBox::No == QMessageBox::question(
this,
"Collision Matrix Generation",
711 "Collision Matrix Generation is still active. Cancel computation?",
712 QMessageBox::Yes | QMessageBox::No, QMessageBox::No))
724 : setup_step_(setup_step), canceled_(false)
728 connect(
this, SIGNAL(
progress(
int)), progress_bar, SLOT(setValue(
int)));
738 QThread::msleep(100);
752 #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)