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