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)