moveit2
The MoveIt Motion Planning Framework for ROS 2.
collision_linear_model.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2016, CITEC, Bielefeld University
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: Robert Haschke */
36 
39 
40 #include <QItemSelection>
41 #include <QPainter>
42 #include <cmath>
43 namespace moveit_setup
44 {
45 namespace srdf_setup
46 {
47 CollisionLinearModel::CollisionLinearModel(CollisionMatrixModel* src, QObject* parent) : QAbstractProxyModel(parent)
48 {
49  setSourceModel(src);
50 }
52 {
53  delete sourceModel();
54 }
55 
56 QModelIndex CollisionLinearModel::mapFromSource(const QModelIndex& sourceIndex) const
57 {
58  // map (row,column) index to linear index k
59  // http://stackoverflow.com/questions/27086195/linear-index-upper-triangular-matrix
60  int r = sourceIndex.row(), c = sourceIndex.column();
61  int n = sourceModel()->columnCount();
62  if (r == c)
63  return QModelIndex(); // main diagonal elements are invalid
64  if (r > c) // only consider upper triagonal matrix
65  std::swap(r, c); // swap r,c if below diagonal
66 
67  int k = (n * (n - 1) / 2) - (n - r) * ((n - r) - 1) / 2 + c - r - 1;
68  return index(k, 2);
69 }
70 
71 QModelIndex CollisionLinearModel::mapToSource(const QModelIndex& proxyIndex) const
72 {
73  // map linear index k to (row, column)
74  // http://stackoverflow.com/questions/27086195/linear-index-upper-triangular-matrix
75  int n = sourceModel()->columnCount();
76  int k = proxyIndex.row(); // linear (row) index
77  int r = n - 2 - static_cast<int>(sqrt(-8 * k + 4 * n * (n - 1) - 7) / 2.0 - 0.5);
78  int c = k + r + 1 - n * (n - 1) / 2 + (n - r) * ((n - r) - 1) / 2;
79  return sourceModel()->index(r, c);
80 }
81 
82 int CollisionLinearModel::rowCount(const QModelIndex& /*parent*/) const
83 {
84  int n = sourceModel()->rowCount();
85  return (n * (n - 1) / 2);
86 }
87 
88 int CollisionLinearModel::columnCount(const QModelIndex& /*parent*/) const
89 {
90  return 4;
91 }
92 
93 QModelIndex CollisionLinearModel::index(int row, int column, const QModelIndex& /*parent*/) const
94 {
95  return createIndex(row, column);
96 }
97 
98 QModelIndex CollisionLinearModel::parent(const QModelIndex& /*child*/) const
99 {
100  return QModelIndex();
101 }
102 
103 QVariant CollisionLinearModel::data(const QModelIndex& index, int role) const
104 {
105  QModelIndex src_index = mapToSource(index);
106  switch (index.column())
107  {
108  case 0: // link name 1
109  if (role != Qt::DisplayRole)
110  {
111  return QVariant();
112  }
113  else
114  {
115  return sourceModel()->headerData(src_index.row(), Qt::Horizontal, Qt::DisplayRole);
116  }
117  case 1: // link name 2
118  if (role != Qt::DisplayRole)
119  return QVariant();
120  return sourceModel()->headerData(src_index.column(), Qt::Vertical, Qt::DisplayRole);
121  case 2: // checkbox
122  if (role != Qt::CheckStateRole)
123  {
124  return QVariant();
125  }
126  else
127  {
128  return sourceModel()->data(src_index, Qt::CheckStateRole);
129  }
130  case 3: // reason
131  if (role != Qt::DisplayRole)
132  {
133  return QVariant();
134  }
135  else
136  {
137  return sourceModel()->data(src_index, Qt::ToolTipRole);
138  }
139  }
140  return QVariant();
141 }
142 
144 {
145  QModelIndex src_index = mapToSource(index(row, 0));
146  return qobject_cast<CollisionMatrixModel*>(sourceModel())->reason(src_index);
147 }
148 
149 bool CollisionLinearModel::setData(const QModelIndex& index, const QVariant& value, int role)
150 {
151  QModelIndex src_index = mapToSource(index);
152 
153  if (role == Qt::CheckStateRole)
154  {
155  sourceModel()->setData(src_index, value, role);
156  int r = index.row();
157  Q_EMIT dataChanged(this->index(r, 2), this->index(r, 3)); // reason changed too
158  return true;
159  }
160  return false; // reject all other changes
161 }
162 
163 void CollisionLinearModel::setEnabled(const QItemSelection& selection, bool value)
164 {
165  for (const auto idx : selection.indexes())
166  {
167  if (idx.column() != 2) // only consider checkbox indexes
168  continue;
169  setData(idx, value ? Qt::Checked : Qt::Unchecked, Qt::CheckStateRole);
170  }
171 }
172 
173 Qt::ItemFlags CollisionLinearModel::flags(const QModelIndex& index) const
174 {
175  if (index.column() == 2)
176  {
177  return Qt::ItemIsUserCheckable | QAbstractItemModel::flags(index);
178  }
179  else
180  {
181  return QAbstractItemModel::flags(index);
182  }
183 }
184 
185 QVariant CollisionLinearModel::headerData(int section, Qt::Orientation orientation, int role) const
186 {
187  if (role != Qt::DisplayRole)
188  return QVariant();
189 
190  if (orientation == Qt::Horizontal)
191  {
192  switch (section)
193  {
194  case 0:
195  return "Link A";
196  case 1:
197  return "Link B";
198  case 2:
199  return "Disabled";
200  case 3:
201  return "Reason to Disable";
202  }
203  }
204  else if (orientation == Qt::Vertical)
205  {
206  return section + 1;
207  }
208  return QVariant();
209 }
210 
211 SortFilterProxyModel::SortFilterProxyModel(QObject* parent) : QSortFilterProxyModel(parent), show_all_(false)
212 {
213 #if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
214  connect(this, SIGNAL(sourceModelChanged()), this, SLOT(initSorting()));
215 #endif
216 
217  // by default: sort by link A (col 0), then link B (col 1)
218  sort_columns_ << 0 << 1;
219  sort_orders_ << Qt::AscendingOrder << Qt::AscendingOrder;
220 }
221 
222 QVariant SortFilterProxyModel::headerData(int section, Qt::Orientation orientation, int role) const
223 {
224  if (role == Qt::DisplayRole && orientation == Qt::Vertical)
225  {
226  return section + 1; // simply enumerate rows
227  }
228  else
229  {
230  return QSortFilterProxyModel::headerData(section, orientation, role);
231  }
232 }
233 
234 void SortFilterProxyModel::setEnabled(const QItemSelection& selection, bool value)
235 {
236  static_cast<CollisionLinearModel*>(sourceModel())->setEnabled(mapSelectionToSource(selection), value);
237 }
238 
239 void SortFilterProxyModel::initSorting()
240 {
241  int cols = sourceModel()->columnCount();
242  int prev_size = sort_columns_.size();
243  sort_columns_.resize(cols);
244  sort_orders_.resize(cols);
245 
246  // initialize new entries to -1
247  for (int i = prev_size, end = sort_columns_.size(); i < end; ++i)
248  sort_columns_[i] = -1;
249 }
250 
252 {
253  if (show_all_ == show_all)
254  return;
255 
256  show_all_ = show_all;
257  invalidateFilter();
258 }
259 
260 bool SortFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const
261 {
262  CollisionLinearModel* m = qobject_cast<CollisionLinearModel*>(sourceModel());
263  if (!show_all_ && m->reason(source_row) > ALWAYS &&
264  m->data(m->index(source_row, 2), Qt::CheckStateRole) != Qt::Checked)
265  return false; // not accepted due to check state
266 
267  const QRegExp regexp = filterRegExp();
268  if (regexp.isEmpty())
269  return true;
270 
271  return m->data(m->index(source_row, 0, source_parent), Qt::DisplayRole).toString().contains(regexp) ||
272  m->data(m->index(source_row, 1, source_parent), Qt::DisplayRole).toString().contains(regexp);
273 }
274 
275 // define a fallback comparison operator for QVariants
276 bool compareVariants(const QVariant& left, const QVariant& right)
277 {
278  if (left.userType() == QVariant::Type::Int)
279  {
280  return left.toInt() < right.toInt();
281  }
282  else
283  {
284  return left.toString() < right.toString();
285  }
286 }
287 
288 bool SortFilterProxyModel::lessThan(const QModelIndex& src_left, const QModelIndex& src_right) const
289 {
290  int row_left = src_left.row();
291  int row_right = src_right.row();
292  QAbstractItemModel* m = sourceModel();
293 
294  for (int i = 0, end = sort_columns_.size(); i < end && sort_columns_[i] >= 0; ++i)
295  {
296  int sc = sort_columns_[i];
297  int role = sc == 2 ? Qt::CheckStateRole : Qt::DisplayRole;
298  QVariant value_left = m->data(m->index(row_left, sc), role);
299  QVariant value_right = m->data(m->index(row_right, sc), role);
300 
301  if (value_left == value_right)
302  continue;
303 
304  bool smaller = compareVariants(value_left, value_right);
305  if (sort_orders_[i] == Qt::DescendingOrder)
306  smaller = !smaller;
307  return smaller;
308  }
309  return false;
310 }
311 
312 void SortFilterProxyModel::sort(int column, Qt::SortOrder order)
313 {
314  beginResetModel();
315  if (column < 0)
316  {
317  initSorting();
318  }
319  else
320  {
321  // remember sorting history
322  int prev_idx = sort_columns_.indexOf(column);
323  if (prev_idx < 0)
324  prev_idx = sort_columns_.size() - 1;
325  // remove old entries
326  sort_columns_.remove(prev_idx);
327  sort_orders_.remove(prev_idx);
328  // add new entries at front
329  sort_columns_.insert(0, column);
330  sort_orders_.insert(0, order);
331  }
332  QSortFilterProxyModel::sort(column, Qt::AscendingOrder);
333  endResetModel();
334 }
335 } // namespace srdf_setup
336 } // namespace moveit_setup
QVariant headerData(int section, Qt::Orientation orientation, int role) const override
bool setData(const QModelIndex &index, const QVariant &value, int role) override
QModelIndex mapFromSource(const QModelIndex &sourceIndex) const override
CollisionLinearModel(CollisionMatrixModel *src, QObject *parent=nullptr)
QModelIndex mapToSource(const QModelIndex &proxyIndex) const override
Qt::ItemFlags flags(const QModelIndex &index) const override
int columnCount(const QModelIndex &parent) const override
int rowCount(const QModelIndex &parent) const override
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
void setEnabled(const QItemSelection &selection, bool value)
QVariant data(const QModelIndex &index, int role) const override
QModelIndex parent(const QModelIndex &child) const override
bool lessThan(const QModelIndex &src_left, const QModelIndex &src_right) const override
void setEnabled(const QItemSelection &selection, bool value)
void sort(int column, Qt::SortOrder order) override
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override
QVariant headerData(int section, Qt::Orientation orientation, int role) const override
DisabledReason
Reasons for disabling link pairs. Append "in collision" for understanding. NOT_DISABLED means the lin...
bool compareVariants(const QVariant &left, const QVariant &right)