euler_property.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2016, Bielefeld University, CITEC
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * * Neither the name of the Willow Garage, Inc. nor the names of its
14  * contributors may be used to endorse or promote products derived from
15  * this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  *
29  * Author: Robert Haschke <rhaschke@techfak.uni-bielefeld.de>
30  */
31 
32 #include <QStringList>
35 #include <angles/angles.h>
36 #include <boost/format.hpp>
37 #include <boost/assign/list_of.hpp>
38 #include "euler_property.h"
39 
40 namespace rviz
41 {
42 
44  std::invalid_argument(msg)
45 {
46 }
47 
48 EulerProperty::EulerProperty(Property* parent, const QString& name,
49  const Eigen::Quaterniond& value,
50  const char *changed_slot,
51  QObject* receiver)
52  : Property(name, QVariant(),
53  "Angles specified in degrees.\n"
54  "Choose axes with spec like xyz, zxz, or rpy.\n"
55  "Composition w.r.t. the static or rotating frame\n"
56  "is selected by prefixing with 's' or 'r' (default).",
57  parent, changed_slot, receiver)
58  , quaternion_(value)
59  , ignore_child_updates_(false)
60  , angles_read_only_(false)
61  , update_string_(true)
62 {
63  euler_[0] = new FloatProperty("", 0, "rotation angle about first axis", this);
64  euler_[1] = new FloatProperty("", 0, "rotation angle about second axis", this);
65  euler_[2] = new FloatProperty("", 0, "rotation angle about third axis", this);
66  setEulerAxes("rpy");
67 
68  for (int i=0; i < 3; ++i) {
69  connect(euler_[i], SIGNAL(aboutToChange()), this, SLOT(emitAboutToChange()));
70  connect(euler_[i], SIGNAL(changed()), this, SLOT(updateFromChildren()));
71  }
72 }
73 
74 void EulerProperty::setQuaternion(const Eigen::Quaterniond& q)
75 {
76  if (quaternion_.isApprox(q)) return;
77  updateAngles(q); // this will also emit changed signals (in setEulerAngles)
78 }
79 
80 void EulerProperty::setEulerAngles(double euler[], bool normalize)
81 {
82  Eigen::Quaterniond q;
83  if (fixed_)
84  q = Eigen::AngleAxisd(euler[2], Eigen::Vector3d::Unit(axes_[2])) *
85  Eigen::AngleAxisd(euler[1], Eigen::Vector3d::Unit(axes_[1])) *
86  Eigen::AngleAxisd(euler[0], Eigen::Vector3d::Unit(axes_[0]));
87  else
88  q = Eigen::AngleAxisd(euler[0], Eigen::Vector3d::Unit(axes_[0])) *
89  Eigen::AngleAxisd(euler[1], Eigen::Vector3d::Unit(axes_[1])) *
90  Eigen::AngleAxisd(euler[2], Eigen::Vector3d::Unit(axes_[2]));
91 
92  if (normalize) setQuaternion(q);
93  else {
94  for (int i=0; i < 3; ++i) {
95  float deg = angles::to_degrees(euler[i]);
96  if (!Eigen::internal::isApprox(deg, euler_[i]->getFloat())) {
97  update_string_ = true;
99  euler_[i]->setValue(deg);
100  }
101  }
102 
103  if (!quaternion_.isApprox(q)) {
104  Q_EMIT aboutToChange();
105  quaternion_ = q;
106  Q_EMIT quaternionChanged(q);
107  } else if (update_string_) {
108  Q_EMIT aboutToChange();
109  } else { // there is nothing to update at all
110  return;
111  }
112  updateString();
113  Q_EMIT changed();
114  }
115 }
116 
117 void EulerProperty::setEulerAngles(double e1, double e2, double e3, bool normalize)
118 {
119  double euler[3] = {e1,e2,e3};
120  setEulerAngles(euler, normalize);
121 }
122 
123 void EulerProperty::setEulerAxes(const QString &axes_spec)
124 {
125  static const std::vector<QString> xyzNames = boost::assign::list_of("x")("y")("z");
126  static const std::vector<QString> rpyNames = boost::assign::list_of("roll")("pitch")("yaw");
127  const std::vector<QString> *names = &xyzNames;
128 
129  if (axes_string_ == axes_spec) return;
130  QString sAxes = axes_spec;
131  if (sAxes == "rpy") {
132  sAxes = "sxyz";
133  names = &rpyNames;
134  } else if (sAxes == "ypr") {
135  sAxes = "rzyx";
136  names = &rpyNames;
137  }
138 
139  // static or rotated frame order?
140  QString::iterator pc = sAxes.begin();
141  bool fixed;
142  if (*pc == 's') fixed = true;
143  else if (*pc == 'r') fixed = false;
144  else {fixed = false; --pc;}
145  ++pc; // advance to first axis char
146 
147  // need to have 3 axes specs
148  if (sAxes.end() - pc != 3)
149  throw invalid_axes((boost::format("Invalid axes spec: %s. Expecting 3 chars from [xyz]")
150  % axes_spec.toStdString()).str());
151 
152  // parse axes specs into indexes
153  uint axes[3];
154  for (uint i=0; i < 3; ++i, ++pc) {
155  int idx = pc->toLatin1() - 'x';
156  if (idx < 0 || idx > 2)
157  throw invalid_axes((boost::format("invalid axis char: %c (only xyz allowed)") % pc->unicode()).str());
158  if (i > 0 && axes[i-1] == static_cast<uint>(idx))
159  throw invalid_axes("consecutive axes need to be different");
160  axes[i] = idx;
161  }
162 
163  // everything OK: accept changes
164  axes_string_ = axes_spec;
165  fixed_ = fixed;
166  for (int i=0; i < 3; ++i) {
167  axes_[i] = axes[i];
168  euler_[i]->setName((*names)[axes[i]]);
169  }
170 
171  // finally compute euler angles matching the new axes
172  update_string_ = true;
174 }
175 
176 bool EulerProperty::setValue(const QVariant& value)
177 {
178  static const QString statusAxes ("Euler axes");
179  static const QString statusAngles ("Euler angles");
180 
181  const QRegExp axesSpec("\\s*([a-z]+)\\s*:?");
182  QString s = value.toString();
183 
184  // parse axes spec
185  if (axesSpec.indexIn(s) != -1) {
186  try {
187  setEulerAxes(axesSpec.cap(1));
188  Q_EMIT statusUpdate(StatusProperty::Ok, statusAxes, axes_string_);
189  } catch (const invalid_axes &e) {
190  Q_EMIT statusUpdate(StatusProperty::Warn, statusAxes, e.what());
191  return false;
192  }
193  s = s.mid(axesSpec.matchedLength());
194  }
195 
196  // in read-only mode only allow to change axes, but not angles
197  if (angles_read_only_) {
198  Q_EMIT statusUpdate(StatusProperty::Warn, statusAngles, "read-only");
199  return true;
200  }
201 
202  if (s.trimmed().isEmpty())
203  return true; // allow change of axes only
204 
205  // parse angles
206  QStringList strings = s.split(';');
207  double euler[3];
208  bool ok = true;
209  for (int i=0; i < 3 && ok; ++i)
210  {
211  if (i < strings.size())
212  euler[i] = angles::from_degrees(strings[i].toDouble(&ok));
213  else // providing a single value will set all angles to this (1st) value
214  euler[i] = euler[0];
215  }
216  if (!ok)
217  {
218  Q_EMIT statusUpdate(StatusProperty::Warn, statusAngles,
219  "failed to parse angle value");
220  return false;
221  }
222  if (strings.size() != 3 && strings.size() != 1) {
223  Q_EMIT statusUpdate(StatusProperty::Warn, statusAngles,
224  "expecting 3 semicolon-separated values");
225  return false;
226  }
227 
228  Q_EMIT statusUpdate(StatusProperty::Ok, statusAngles, "");
229  setEulerAngles(euler, false);
230  return true;
231 }
232 
234 {
235  if (ignore_child_updates_) return;
236  double euler[3];
237  for (int i = 0; i < 3; ++i)
238  euler[i] = angles::from_degrees(euler_[i]->getValue().toFloat());
239 
240  ignore_child_updates_ = true;
241  setEulerAngles(euler, false);
242  ignore_child_updates_ = false;
243 }
244 
246 {
247  if (ignore_child_updates_) return;
248  Q_EMIT aboutToChange();
249 }
250 
251 void EulerProperty::updateAngles(const Eigen::Quaterniond &q)
252 {
253  Eigen::Vector3d e;
254  if (fixed_) {
255  e = q.matrix().eulerAngles(axes_[2], axes_[1], axes_[0]);
256  std::swap(e[0], e[2]);
257  } else
258  e = q.matrix().eulerAngles(axes_[0], axes_[1], axes_[2]);
259  setEulerAngles(e.data(), false);
260 }
261 
263 {
264  QString s = QString("%1: %2; %3; %4")
265  .arg(axes_string_)
266  .arg(euler_[0]->getFloat(), 0, 'f', 1)
267  .arg(euler_[1]->getFloat(), 0, 'f', 1)
268  .arg(euler_[2]->getFloat(), 0, 'f', 1);
269  value_ = s.replace(".0", "");
270  update_string_ = false; // reset update flag
271 }
272 
273 void EulerProperty::load(const Config& config)
274 {
275  QString axes;
276  float euler[3];
277  if (config.mapGetString("axes", &axes) &&
278  config.mapGetFloat("e1", euler+0 ) &&
279  config.mapGetFloat("e2", euler+1 ) &&
280  config.mapGetFloat("e3", euler+2 ))
281  {
282  // Setting the value once is better than letting the Property class
283  // load all parameters independently, which would result in 4 update calls
284  setEulerAxes(axes);
285  for (int i=0; i < 3; ++i)
286  euler[i] = angles::from_degrees(euler[i]);
287  setEulerAngles(euler[0], euler[1], euler[2], false);
288  }
289 }
290 
291 void EulerProperty::save(Config config) const
292 {
293  // Saving the child values explicitly avoids having Property::save()
294  // save the summary string version of the property.
295  config.mapSetValue("axes", axes_string_);
296  config.mapSetValue("e1", euler_[0]->getValue());
297  config.mapSetValue("e2", euler_[1]->getValue());
298  config.mapSetValue("e3", euler_[2]->getValue());
299 }
300 
301 void EulerProperty::setReadOnly(bool read_only)
302 {
303  // read-only mode should allow for changing axes, but not angles
304  angles_read_only_ = read_only;
305  // do not pass read-only to base class, but only to children
306  for (int i=0; i < 3; ++i)
307  euler_[i]->setReadOnly(read_only);
308 }
309 
310 } // end namespace rviz
virtual bool setValue(const QVariant &value)
virtual void setReadOnly(bool read_only)
Overridden from Property to propagate read-only-ness to children.
void quaternionChanged(Eigen::Quaterniond q)
Eigen::Quaterniond quaternion_
XmlRpcServer s
bool mapGetFloat(const QString &key, float *value_out) const
virtual void setName(const QString &name)
void setEulerAngles(double euler[3], bool normalize)
QVariant value_
invalid_axes(const std::string &msg)
bool mapGetString(const QString &key, QString *value_out) const
virtual bool setValue(const QVariant &new_value)
void mapSetValue(const QString &key, QVariant value)
void updateAngles(const Eigen::Quaterniond &q)
static double from_degrees(double degrees)
void setQuaternion(const Eigen::Quaterniond &q)
void setEulerAxes(const QString &axes_spec)
static double to_degrees(double radians)
FloatProperty * euler_[3]
TFSIMD_FORCE_INLINE Vector3 & normalize()
virtual void save(Config config) const
void aboutToChange()
virtual void load(const Config &config)
Load the value of this property and/or its children from the given Config node.
virtual QVariant getValue() const
EulerProperty(Property *parent=0, const QString &name=QString(), const Eigen::Quaterniond &value=Eigen::Quaterniond::Identity(), const char *changed_slot=0, QObject *receiver=0)
void statusUpdate(int, const QString &, const QString &)


agni_tf_tools
Author(s): Robert Haschke
autogenerated on Fri Jun 7 2019 22:04:59