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