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 <cmath>
43namespace moveit_setup
44{
45namespace srdf_setup
46{
47CollisionLinearModel::CollisionLinearModel(CollisionMatrixModel* src, QObject* parent) : QAbstractProxyModel(parent)
48{
49 setSourceModel(src);
50}
52{
53 delete sourceModel();
54}
55
56QModelIndex 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
71QModelIndex 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
82int CollisionLinearModel::rowCount(const QModelIndex& /*parent*/) const
83{
84 int n = sourceModel()->rowCount();
85 return (n * (n - 1) / 2);
86}
87
88int CollisionLinearModel::columnCount(const QModelIndex& /*parent*/) const
89{
90 return 4;
91}
92
93QModelIndex CollisionLinearModel::index(int row, int column, const QModelIndex& /*parent*/) const
94{
95 return createIndex(row, column);
96}
97
98QModelIndex CollisionLinearModel::parent(const QModelIndex& /*child*/) const
99{
100 return QModelIndex();
101}
102
103QVariant 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
149bool 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
163void 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
173Qt::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
185QVariant 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
211SortFilterProxyModel::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
222QVariant 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
234void SortFilterProxyModel::setEnabled(const QItemSelection& selection, bool value)
235{
236 static_cast<CollisionLinearModel*>(sourceModel())->setEnabled(mapSelectionToSource(selection), value);
237}
238
239void 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
260bool 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
276bool 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
288bool 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
312void 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)