00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032 #include <QStringList>
00033 #include <rviz/properties/float_property.h>
00034 #include <rviz/properties/status_property.h>
00035 #include <angles/angles.h>
00036 #include <boost/format.hpp>
00037 #include <boost/assign/list_of.hpp>
00038 #include "euler_property.h"
00039
00040 namespace rviz
00041 {
00042
00043 EulerProperty::invalid_axes::invalid_axes(const std::string &msg) :
00044 std::invalid_argument(msg)
00045 {
00046 }
00047
00048 EulerProperty::EulerProperty(Property* parent, const QString& name,
00049 const Eigen::Quaterniond& value,
00050 const char *changed_slot,
00051 QObject* receiver)
00052 : Property(name, QVariant(),
00053 "Angles specified in degrees.\n"
00054 "Choose axes with spec like xyz, zxz, or rpy.\n"
00055 "Composition w.r.t. the static or rotating frame\n"
00056 "is selected by prefixing with 's' or 'r' (default).",
00057 parent, changed_slot, receiver)
00058 , quaternion_(value)
00059 , ignore_child_updates_(false)
00060 , angles_read_only_(false)
00061 , update_string_(true)
00062 {
00063 euler_[0] = new FloatProperty("", 0, "rotation angle about first axis", this);
00064 euler_[1] = new FloatProperty("", 0, "rotation angle about second axis", this);
00065 euler_[2] = new FloatProperty("", 0, "rotation angle about third axis", this);
00066 setEulerAxes("rpy");
00067
00068 for (int i=0; i < 3; ++i) {
00069 connect(euler_[i], SIGNAL(aboutToChange()), this, SLOT(emitAboutToChange()));
00070 connect(euler_[i], SIGNAL(changed()), this, SLOT(updateFromChildren()));
00071 }
00072 }
00073
00074 void EulerProperty::setQuaternion(const Eigen::Quaterniond& q)
00075 {
00076 if (quaternion_.isApprox(q)) return;
00077 updateAngles(q);
00078 }
00079
00080 void EulerProperty::setEulerAngles(double euler[], bool normalize)
00081 {
00082 Eigen::Quaterniond q;
00083 if (fixed_)
00084 q = Eigen::AngleAxisd(euler[2], Eigen::Vector3d::Unit(axes_[2])) *
00085 Eigen::AngleAxisd(euler[1], Eigen::Vector3d::Unit(axes_[1])) *
00086 Eigen::AngleAxisd(euler[0], Eigen::Vector3d::Unit(axes_[0]));
00087 else
00088 q = Eigen::AngleAxisd(euler[0], Eigen::Vector3d::Unit(axes_[0])) *
00089 Eigen::AngleAxisd(euler[1], Eigen::Vector3d::Unit(axes_[1])) *
00090 Eigen::AngleAxisd(euler[2], Eigen::Vector3d::Unit(axes_[2]));
00091
00092 if (normalize) setQuaternion(q);
00093 else {
00094 for (int i=0; i < 3; ++i) {
00095 float deg = angles::to_degrees(euler[i]);
00096 if (!Eigen::internal::isApprox(deg, euler_[i]->getFloat())) {
00097 update_string_ = true;
00098 if (!ignore_child_updates_)
00099 euler_[i]->setValue(deg);
00100 }
00101 }
00102
00103 if (!quaternion_.isApprox(q)) {
00104 Q_EMIT aboutToChange();
00105 quaternion_ = q;
00106 Q_EMIT quaternionChanged(q);
00107 } else if (update_string_) {
00108 Q_EMIT aboutToChange();
00109 } else {
00110 return;
00111 }
00112 updateString();
00113 Q_EMIT changed();
00114 }
00115 }
00116
00117 void EulerProperty::setEulerAngles(double e1, double e2, double e3, bool normalize)
00118 {
00119 double euler[3] = {e1,e2,e3};
00120 setEulerAngles(euler, normalize);
00121 }
00122
00123 void EulerProperty::setEulerAxes(const QString &axes_spec)
00124 {
00125 static const std::vector<QString> xyzNames = boost::assign::list_of("x")("y")("z");
00126 static const std::vector<QString> rpyNames = boost::assign::list_of("roll")("pitch")("yaw");
00127 const std::vector<QString> *names = &xyzNames;
00128
00129 if (axes_string_ == axes_spec) return;
00130 QString sAxes = axes_spec;
00131 if (sAxes == "rpy") {
00132 sAxes = "sxyz";
00133 names = &rpyNames;
00134 } else if (sAxes == "ypr") {
00135 sAxes = "rzyx";
00136 names = &rpyNames;
00137 }
00138
00139
00140 QString::iterator pc = sAxes.begin();
00141 bool fixed;
00142 if (*pc == 's') fixed = true;
00143 else if (*pc == 'r') fixed = false;
00144 else {fixed = false; --pc;}
00145 ++pc;
00146
00147
00148 if (sAxes.end() - pc != 3)
00149 throw invalid_axes((boost::format("Invalid axes spec: %s. Expecting 3 chars from [xyz]")
00150 % axes_spec.toStdString()).str());
00151
00152
00153 uint axes[3];
00154 for (uint i=0; i < 3; ++i, ++pc) {
00155 int idx = pc->toLatin1() - 'x';
00156 if (idx < 0 || idx > 2)
00157 throw invalid_axes((boost::format("invalid axis char: %c (only xyz allowed)") % pc->unicode()).str());
00158 if (i > 0 && axes[i-1] == static_cast<uint>(idx))
00159 throw invalid_axes("consecutive axes need to be different");
00160 axes[i] = idx;
00161 }
00162
00163
00164 axes_string_ = axes_spec;
00165 fixed_ = fixed;
00166 for (int i=0; i < 3; ++i) {
00167 axes_[i] = axes[i];
00168 euler_[i]->setName((*names)[axes[i]]);
00169 }
00170
00171
00172 update_string_ = true;
00173 updateAngles(quaternion_);
00174 }
00175
00176 bool EulerProperty::setValue(const QVariant& value)
00177 {
00178 static const QString statusAxes ("Euler axes");
00179 static const QString statusAngles ("Euler angles");
00180
00181 const QRegExp axesSpec("\\s*([a-z]+)\\s*:?");
00182 QString s = value.toString();
00183
00184
00185 if (axesSpec.indexIn(s) != -1) {
00186 try {
00187 setEulerAxes(axesSpec.cap(1));
00188 Q_EMIT statusUpdate(StatusProperty::Ok, statusAxes, axes_string_);
00189 } catch (const invalid_axes &e) {
00190 Q_EMIT statusUpdate(StatusProperty::Warn, statusAxes, e.what());
00191 return false;
00192 }
00193 s = s.mid(axesSpec.matchedLength());
00194 }
00195
00196
00197 if (angles_read_only_) {
00198 Q_EMIT statusUpdate(StatusProperty::Warn, statusAngles, "read-only");
00199 return true;
00200 }
00201
00202 if (s.trimmed().isEmpty())
00203 return true;
00204
00205
00206 QStringList strings = s.split(';');
00207 double euler[3];
00208 bool ok = true;
00209 for (int i=0; i < 3 && ok; ++i)
00210 {
00211 if (i < strings.size())
00212 euler[i] = angles::from_degrees(strings[i].toDouble(&ok));
00213 else
00214 euler[i] = euler[0];
00215 }
00216 if (!ok)
00217 {
00218 Q_EMIT statusUpdate(StatusProperty::Warn, statusAngles,
00219 "failed to parse angle value");
00220 return false;
00221 }
00222 if (strings.size() != 3 && strings.size() != 1) {
00223 Q_EMIT statusUpdate(StatusProperty::Warn, statusAngles,
00224 "expecting 3 semicolon-separated values");
00225 return false;
00226 }
00227
00228 Q_EMIT statusUpdate(StatusProperty::Ok, statusAngles, "");
00229 setEulerAngles(euler, false);
00230 return true;
00231 }
00232
00233 void EulerProperty::updateFromChildren()
00234 {
00235 if (ignore_child_updates_) return;
00236 double euler[3];
00237 for (int i = 0; i < 3; ++i)
00238 euler[i] = angles::from_degrees(euler_[i]->getValue().toFloat());
00239
00240 ignore_child_updates_ = true;
00241 setEulerAngles(euler, false);
00242 ignore_child_updates_ = false;
00243 }
00244
00245 void EulerProperty::emitAboutToChange()
00246 {
00247 if (ignore_child_updates_) return;
00248 Q_EMIT aboutToChange();
00249 }
00250
00251 void EulerProperty::updateAngles(const Eigen::Quaterniond &q)
00252 {
00253 Eigen::Vector3d e;
00254 if (fixed_) {
00255 e = q.matrix().eulerAngles(axes_[2], axes_[1], axes_[0]);
00256 std::swap(e[0], e[2]);
00257 } else
00258 e = q.matrix().eulerAngles(axes_[0], axes_[1], axes_[2]);
00259 setEulerAngles(e.data(), false);
00260 }
00261
00262 void EulerProperty::updateString()
00263 {
00264 QString s = QString("%1: %2; %3; %4")
00265 .arg(axes_string_)
00266 .arg(euler_[0]->getFloat(), 0, 'f', 1)
00267 .arg(euler_[1]->getFloat(), 0, 'f', 1)
00268 .arg(euler_[2]->getFloat(), 0, 'f', 1);
00269 value_ = s.replace(".0", "");
00270 update_string_ = false;
00271 }
00272
00273 void EulerProperty::load(const Config& config)
00274 {
00275 QString axes;
00276 float euler[3];
00277 if (config.mapGetString("axes", &axes) &&
00278 config.mapGetFloat("e1", euler+0 ) &&
00279 config.mapGetFloat("e2", euler+1 ) &&
00280 config.mapGetFloat("e3", euler+2 ))
00281 {
00282
00283
00284 setEulerAxes(axes);
00285 for (int i=0; i < 3; ++i)
00286 euler[i] = angles::from_degrees(euler[i]);
00287 setEulerAngles(euler[0], euler[1], euler[2], false);
00288 }
00289 }
00290
00291 void EulerProperty::save(Config config) const
00292 {
00293
00294
00295 config.mapSetValue("axes", axes_string_);
00296 config.mapSetValue("e1", euler_[0]->getValue());
00297 config.mapSetValue("e2", euler_[1]->getValue());
00298 config.mapSetValue("e3", euler_[2]->getValue());
00299 }
00300
00301 void EulerProperty::setReadOnly(bool read_only)
00302 {
00303
00304 angles_read_only_ = read_only;
00305
00306 for (int i=0; i < 3; ++i)
00307 euler_[i]->setReadOnly(read_only);
00308 }
00309
00310 }