moveit2
The MoveIt Motion Planning Framework for ROS 2.
Loading...
Searching...
No Matches
collision_matrix_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
38#include <boost/assign.hpp>
39#include <QVector>
40#include <QBrush>
41#include <QColor>
42#include <QPalette>
43#include <QApplication>
44#include <QItemSelection>
45#include <QRegularExpression>
46#include <unordered_map>
47
48namespace moveit_setup
49{
50namespace srdf_setup
51{
53static const std::unordered_map<DisabledReason, const char*> LONG_REASONS_TO_STRING =
54 boost::assign::map_list_of // clang-format off
55 ( NEVER, "Never in Collision" )
56 ( DEFAULT, "Collision by Default" )
57 ( ADJACENT, "Adjacent Links" )
58 ( ALWAYS, "Always in Collision" )
59 ( USER, "User Disabled" )
60 ( NOT_DISABLED, ""); // clang-format on
61
63static const std::unordered_map<DisabledReason, QVariant> LONG_REASONS_TO_BRUSH =
64 boost::assign::map_list_of // clang-format off
65 ( NEVER, QBrush(QColor("lightgreen")) )
66 ( DEFAULT, QBrush(QColor("lightpink")) )
67 ( ADJACENT, QBrush(QColor("powderblue")) )
68 ( ALWAYS, QBrush(QColor("tomato")) )
69 ( USER, QBrush(QColor("yellow")) )
70 ( NOT_DISABLED, QBrush()); // clang-format on
71
72CollisionMatrixModel::CollisionMatrixModel(LinkPairMap& pairs, const std::vector<std::string>& names, QObject* parent)
73 : QAbstractTableModel(parent), pairs_(pairs), std_names_(names)
74{
75 int idx = 0;
76 for (std::vector<std::string>::const_iterator it = names.begin(), end = names.end(); it != end; ++it, ++idx)
77 {
78 visual_to_index_ << idx;
79 q_names_ << QString::fromStdString(*it);
80 }
81}
82
83// return item in pairs map given a normalized index, use item(normalized(index))
84LinkPairMap::iterator CollisionMatrixModel::item(const QModelIndex& index)
85{
86 int r = visual_to_index_[index.row()], c = visual_to_index_[index.column()];
87 if (r == c)
88 return pairs_.end();
89
90 // setLinkPair() actually inserts the pair (A,B) where A < B
91 if (std_names_[r] >= std_names_[c])
92 std::swap(r, c);
93
94 return pairs_.find(std::make_pair(std_names_[r], std_names_[c]));
95}
96
97int CollisionMatrixModel::rowCount(const QModelIndex& /*parent*/) const
98{
99 return visual_to_index_.size();
100}
101
102int CollisionMatrixModel::columnCount(const QModelIndex& /*parent*/) const
103{
104 return visual_to_index_.size();
105}
106
107QVariant CollisionMatrixModel::data(const QModelIndex& index, int role) const
108{
109 if (index.isValid() && index.row() == index.column() && role == Qt::BackgroundRole)
110 return QApplication::palette().window();
111
112 LinkPairMap::const_iterator item = this->item(index);
113 if (item == pairs_.end())
114 return QVariant();
115
116 switch (role)
117 {
118 case Qt::CheckStateRole:
119 return item->second.disable_check ? Qt::Checked : Qt::Unchecked;
120 case Qt::ToolTipRole:
121 return LONG_REASONS_TO_STRING.at(item->second.reason);
122 case Qt::BackgroundRole:
123 return LONG_REASONS_TO_BRUSH.at(item->second.reason);
124 }
125 return QVariant();
126}
127
128DisabledReason CollisionMatrixModel::reason(const QModelIndex& index) const
129{
130 LinkPairMap::const_iterator item = this->item(index);
131 if (item == pairs_.end())
132 return NOT_DISABLED;
133 return item->second.reason;
134}
135
136bool CollisionMatrixModel::setData(const QModelIndex& index, const QVariant& value, int role)
137{
138 if (role == Qt::CheckStateRole)
139 {
140 LinkPairMap::iterator item = this->item(index);
141 if (item == pairs_.end())
142 return false;
143
144 bool new_value = (value.toInt() == Qt::Checked);
145 if (item->second.disable_check == new_value)
146 return true;
147
148 item->second.disable_check = new_value;
149
150 // Handle USER Reasons: 1) pair is disabled by user
151 if (item->second.disable_check && item->second.reason == NOT_DISABLED)
152 {
153 item->second.reason = USER;
154
155 // Handle USER Reasons: 2) pair was disabled by user and now is enabled (not checked)
156 }
157 else if (!item->second.disable_check && item->second.reason == USER)
158 {
159 item->second.reason = NOT_DISABLED;
160 }
161
162 QModelIndex mirror = this->index(index.column(), index.row());
163 Q_EMIT dataChanged(index, index);
164 Q_EMIT dataChanged(mirror, mirror);
165 return true;
166 }
167 return false; // reject all other changes
168}
169
170void CollisionMatrixModel::setEnabled(const QItemSelection& selection, bool value)
171{
172 // perform changes without signalling
173 QItemSelection changes;
174 blockSignals(true);
175 for (const auto& range : selection)
176 {
177 setEnabled(range.indexes(), value);
178
179 const QModelIndex& top_left = range.topLeft();
180 const QModelIndex& bottom_right = range.bottomRight();
181 changes.select(top_left, bottom_right);
182 changes.select(createIndex(top_left.column(), top_left.row()),
183 createIndex(bottom_right.column(), bottom_right.row()));
184 }
185 blockSignals(false);
186
187 // emit changes
188 for (const auto& range : changes)
189 Q_EMIT dataChanged(range.topLeft(), range.bottomRight());
190}
191
192void CollisionMatrixModel::setEnabled(const QModelIndexList& indexes, bool value)
193{
194 for (const auto idx : indexes)
195 setData(idx, value ? Qt::Checked : Qt::Unchecked, Qt::CheckStateRole);
196}
197
199{
200 beginResetModel();
201 QRegularExpression regexp(QRegularExpression::escape(filter));
202 visual_to_index_.clear();
203 for (int idx = 0, end = q_names_.size(); idx != end; ++idx)
204 {
205 if (q_names_[idx].contains(regexp))
206 visual_to_index_ << idx;
207 }
208 endResetModel();
209}
210
211QVariant CollisionMatrixModel::headerData(int section, Qt::Orientation /*orientation*/, int role) const
212{
213 if (role == Qt::DisplayRole)
214 return q_names_[visual_to_index_[section]];
215 return QVariant();
216}
217
218Qt::ItemFlags CollisionMatrixModel::flags(const QModelIndex& index) const
219{
220 if (!index.isValid())
221 return Qt::NoItemFlags;
222
223 Qt::ItemFlags f = QAbstractTableModel::flags(index);
224 if (index.row() != index.column())
225 f |= Qt::ItemIsUserCheckable;
226 return f;
227}
228} // namespace srdf_setup
229} // namespace moveit_setup
int columnCount(const QModelIndex &parent=QModelIndex()) const override
bool setData(const QModelIndex &, const QVariant &value, int role) override
CollisionMatrixModel(LinkPairMap &pairs, const std::vector< std::string > &names, QObject *parent=nullptr)
void setEnabled(const QItemSelection &selection, bool value)
DisabledReason reason(const QModelIndex &index) const
int rowCount(const QModelIndex &parent=QModelIndex()) const override
Qt::ItemFlags flags(const QModelIndex &index) const override
QVariant headerData(int section, Qt::Orientation orientation, int role) const override
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
DisabledReason
Reasons for disabling link pairs. Append "in collision" for understanding. NOT_DISABLED means the lin...
std::map< std::pair< std::string, std::string >, LinkPairData > LinkPairMap
LinkPairMap is an adjacency list structure containing links in string-based form. Used for disabled l...