Visual Servoing Platform  version 3.6.1 under development (2024-07-27)
vpXmlParserHomogeneousMatrix.cpp
1 /*
2  * ViSP, open source Visual Servoing Platform software.
3  * Copyright (C) 2005 - 2024 by Inria. All rights reserved.
4  *
5  * This software is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  * See the file LICENSE.txt at the root directory of this source
10  * distribution for additional information about the GNU GPL.
11  *
12  * For using ViSP with software that can not be combined with the GNU
13  * GPL, please contact Inria about acquiring a ViSP Professional
14  * Edition License.
15  *
16  * See https://visp.inria.fr for more information.
17  *
18  * This software was developed at:
19  * Inria Rennes - Bretagne Atlantique
20  * Campus Universitaire de Beaulieu
21  * 35042 Rennes Cedex
22  * France
23  *
24  * If you have questions regarding the use of this file, please contact
25  * Inria at visp@inria.fr
26  *
27  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
28  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
29  *
30  * Description:
31  * XML parser to load and save Homogeneous Matrix in a XML file
32  */
33 
40 #include <visp3/core/vpXmlParserHomogeneousMatrix.h>
41 
42 #if defined(VISP_HAVE_PUGIXML)
43 #include <pugixml.hpp>
44 
45 /* ----------------------------- LABEL XML ----------------------------- */
46 /* --------------------------------------------------------------------- */
47 #define LABEL_XML_ROOT "root"
48 #define LABEL_XML_M "homogeneous_transformation"
49 #define LABEL_XML_M_NAME "name"
50 #define LABEL_XML_VALUE "values"
51 #define LABEL_XML_TRANSLATION "translation"
52 #define LABEL_XML_TX "tx"
53 #define LABEL_XML_TY "ty"
54 #define LABEL_XML_TZ "tz"
55 #define LABEL_XML_ROTATION "rotation"
56 #define LABEL_XML_TUX "theta_ux"
57 #define LABEL_XML_TUY "theta_uy"
58 #define LABEL_XML_TUZ "theta_uz"
59 
61 #ifndef DOXYGEN_SHOULD_SKIP_THIS
62 class vpXmlParserHomogeneousMatrix::Impl
63 {
64 private:
65  /* --- XML Code------------------------------------------------------------
66  */
67  enum vpXmlCodeType
68  {
69  CODE_XML_BAD = -1,
70  CODE_XML_OTHER,
71  CODE_XML_M,
72  CODE_XML_M_NAME,
73  CODE_XML_VALUE,
74  CODE_XML_TX,
75  CODE_XML_TY,
76  CODE_XML_TZ,
77  CODE_XML_TUX,
78  CODE_XML_TUY,
79  CODE_XML_TUZ
80  };
81 
82 public:
83  Impl() : m_M(), m_name() { }
84 
85  int parse(vpHomogeneousMatrix &M, const std::string &filename, const std::string &name)
86  {
87  pugi::xml_document doc;
88  if (!doc.load_file(filename.c_str())) {
89  std::cerr << std::endl << "ERROR:" << std::endl;
90  std::cerr << " I cannot open the file " << filename << std::endl;
91 
92  return SEQUENCE_ERROR;
93  }
94 
95  pugi::xml_node node = doc.document_element();
96  if (!node) {
97  return SEQUENCE_ERROR;
98  }
99 
100  int ret = read(node, name);
101 
102  M = m_M;
103 
104  return ret;
105  }
106 
114  int read(const pugi::xml_node &node_, const std::string &name)
115  {
116  vpXmlCodeType prop;
117 
119  unsigned int nbM = 0;
120 
121  for (pugi::xml_node node = node_.first_child(); node; node = node.next_sibling()) {
122  if (node.type() == pugi::node_element) {
123  if (SEQUENCE_OK != str2xmlcode(node.name(), prop)) {
124  prop = CODE_XML_OTHER;
125  back = SEQUENCE_ERROR;
126  }
127 
128  if (prop == CODE_XML_M) {
129  if (SEQUENCE_OK == read_matrix(node, name)) {
130  nbM++;
131  }
132  }
133  else {
134  back = SEQUENCE_ERROR;
135  }
136  }
137  }
138 
139  if (nbM == 0) {
140  back = SEQUENCE_ERROR;
141  std::cerr << "No Homogeneous matrix is available" << std::endl << "with name: " << name << std::endl;
142  }
143  else if (nbM > 1) {
144  back = SEQUENCE_ERROR;
145  std::cerr << nbM << " There are more Homogeneous matrix" << std::endl
146  << "with the same name : " << std::endl
147  << "precise your choice..." << std::endl;
148  }
149 
150  return back;
151  }
152 
161  int read_matrix(const pugi::xml_node &node_, const std::string &name)
162  {
163  vpXmlCodeType prop;
164  /* read value in the XML file. */
165  std::string M_name_tmp = "";
166  vpHomogeneousMatrix M_tmp;
167 
169 
170  for (pugi::xml_node node = node_.first_child(); node; node = node.next_sibling()) {
171  if (node.type() == pugi::node_element) {
172  if (SEQUENCE_OK != str2xmlcode(node.name(), prop)) {
173  prop = CODE_XML_OTHER;
174  back = SEQUENCE_ERROR;
175  }
176 
177  switch (prop) {
178  case CODE_XML_M_NAME: {
179  M_name_tmp = node.text().as_string();
180  break;
181  }
182 
183  case CODE_XML_VALUE: // VALUE
184  if (name == M_name_tmp) {
185  std::cout << "Found Homogeneous Matrix with name: \"" << M_name_tmp << "\"" << std::endl;
186  back = read_values(node, M_tmp);
187  }
188  break;
189 
190  case CODE_XML_BAD:
191  case CODE_XML_OTHER:
192  case CODE_XML_M:
193  case CODE_XML_TX:
194  case CODE_XML_TY:
195  case CODE_XML_TZ:
196  case CODE_XML_TUX:
197  case CODE_XML_TUY:
198  case CODE_XML_TUZ:
199 
200  default:
201  back = SEQUENCE_ERROR;
202  break;
203  }
204  }
205  }
206 
207  if (!(name == M_name_tmp)) {
208  back = SEQUENCE_ERROR;
209  }
210  else {
211  this->m_M = M_tmp;
212  // --comment: std::cout << "Convert in Homogeneous Matrix:"<< std::endl;
213  // --comment: std::cout << this-> M << std::endl;
214  this->m_name = M_name_tmp;
215  }
216  return back;
217  }
218 
227  vpXmlCodeSequenceType read_values(const pugi::xml_node &node_, vpHomogeneousMatrix &M)
228  {
229  // counter of the number of read parameters
230  int nb = 0;
231  vpXmlCodeType prop;
232  /* read value in the XML file. */
233 
234  double tx_ = 0.;
235  double ty_ = 0.;
236  double tz_ = 0.;
237  double tux_ = 0.;
238  double tuy_ = 0.;
239  double tuz_ = 0.;
240 
242 
243  for (pugi::xml_node node = node_.first_child(); node; node = node.next_sibling()) {
244  if (node.type() == pugi::node_element) {
245  if (SEQUENCE_OK != str2xmlcode(node.name(), prop)) {
246  prop = CODE_XML_OTHER;
247  back = SEQUENCE_ERROR;
248  }
249 
250  switch (prop) {
251  case CODE_XML_TX:
252  tx_ = node.text().as_double();
253  nb++;
254  break;
255  case CODE_XML_TY:
256  ty_ = node.text().as_double();
257  nb++;
258  break;
259  case CODE_XML_TZ:
260  tz_ = node.text().as_double();
261  nb++;
262  break;
263  case CODE_XML_TUX:
264  tux_ = node.text().as_double();
265  nb++;
266  break;
267  case CODE_XML_TUY:
268  tuy_ = node.text().as_double();
269  nb++;
270  break;
271  case CODE_XML_TUZ:
272  tuz_ = node.text().as_double();
273  nb++;
274  break;
275 
276  case CODE_XML_BAD:
277  case CODE_XML_OTHER:
278  case CODE_XML_M:
279  case CODE_XML_M_NAME:
280  case CODE_XML_VALUE:
281 
282  default:
283  back = SEQUENCE_ERROR;
284  break;
285  }
286  }
287  }
288 
289  if (nb != 6) {
290  std::cerr << "ERROR in 'model' field:\n";
291  std::cerr << "it must contain 6 parameters\n";
292 
293  return SEQUENCE_ERROR;
294  }
295 
296  // Create the Homogeneous matrix
297  M.build(tx_, ty_, tz_, tux_, tuy_, tuz_);
298 
299  // --comment: std::cout << "Read values from file:" << std::endl;
300  // --comment: std::cout << "tx:" << tx_<< std::endl;
301  // --comment: std::cout << "ty:" << ty_<< std::endl;
302  // --comment: std::cout << "tz:" << tz_<< std::endl;
303  // --comment: std::cout << "tux:" << tux_<< std::endl;
304  // --comment: std::cout << "tuy:" << tuy_<< std::endl;
305  // --comment: std::cout << "tuz:" << tuz_<< std::endl;
306 
307  return back;
308  }
309 
310  int save(const vpHomogeneousMatrix &M, const std::string &filename, const std::string &name)
311  {
312  pugi::xml_document doc;
313  pugi::xml_node node;
314 
315  if (!doc.load_file(filename.c_str(), pugi::parse_default | pugi::parse_comments)) {
316  node = doc.append_child(pugi::node_declaration);
317  node.append_attribute("version") = "1.0";
318  node = doc.append_child(LABEL_XML_ROOT);
319  pugi::xml_node nodeComment = node.append_child(pugi::node_comment);
320  nodeComment.set_value("This file stores homogeneous matrix used\n"
321  " in the vpHomogeneousMatrix Class of ViSP available\n"
322  " at https://visp.inria.fr/download/ .\n"
323  " It can be read with the parse method of\n"
324  " the vpXmlParserHomogeneousMatrix class.");
325  }
326 
327  node = doc.document_element();
328  if (!node) {
329  return SEQUENCE_ERROR;
330  }
331 
332  m_M = M;
333 
334  int M_isFound = count(node, name);
335 
336  if (M_isFound) {
337  std::cout << "There is already an homogeneous matrix " << std::endl
338  << "available in the file with the input name: " << name << "." << std::endl
339  << "Please delete it manually from the xml file." << std::endl;
340  return SEQUENCE_ERROR;
341  }
342 
343  write(node, name);
344 
345  doc.save_file(filename.c_str(), PUGIXML_TEXT(" "));
346 
347  return SEQUENCE_OK;
348  }
349 
360  int count(const pugi::xml_node &node_, const std::string &name)
361  {
362  vpXmlCodeType prop;
363  int nbM = 0;
364 
365  for (pugi::xml_node node = node_.first_child(); node; node = node.next_sibling()) {
366  if (node.type() == pugi::node_element) {
367  if (SEQUENCE_OK != str2xmlcode(node.name(), prop)) {
368  prop = CODE_XML_OTHER;
369  }
370  if (prop == CODE_XML_M) {
371  if (SEQUENCE_OK == read_matrix(node, name)) {
372  nbM++;
373  }
374  }
375  }
376  }
377 
378  return nbM;
379  }
380 
389  int write(pugi::xml_node &node, const std::string &name)
390  {
391  int back = SEQUENCE_OK;
392 
393  pugi::xml_node node_tmp;
394  pugi::xml_node node_matrix;
395  pugi::xml_node node_values;
396 
397  // Convert from Rotational matrix to Theta-U vector
399  m_M.extract(R);
400 
401  vpThetaUVector tu(R);
402 
403  // <homogeneous_transformation>
404  node_tmp = node.append_child(pugi::node_comment);
405  node_tmp.set_value("Homogeneous Matrix");
406  node_matrix = node.append_child(LABEL_XML_M);
407  {
408  //<name>
409  if (!name.empty()) {
410  node_tmp = node_matrix.append_child(pugi::node_comment);
411  node_tmp.set_value("Name of the homogeneous matrix");
412  node_matrix.append_child(LABEL_XML_M_NAME).append_child(pugi::node_pcdata).set_value(name.c_str());
413  }
414 
415  //<values>
416  node_values = node_matrix.append_child(LABEL_XML_VALUE);
417  {
418  node_tmp = node_values.append_child(pugi::node_comment);
419  node_tmp.set_value("Translation vector with values in meters");
420 
421  //<tx>
422  node_values.append_child(LABEL_XML_TX).append_child(pugi::node_pcdata).text() = m_M[0][3];
423 
424  //<ty>
425  node_values.append_child(LABEL_XML_TY).append_child(pugi::node_pcdata).text() = m_M[1][3];
426 
427  //<tz>
428  node_values.append_child(LABEL_XML_TZ).append_child(pugi::node_pcdata).text() = m_M[2][3];
429 
430  node_tmp = node_values.append_child(pugi::node_comment);
431  node_tmp.set_value("Rotational vector expressed in angle axis "
432  "representation with values in radians");
433 
434  //<tux>
435  node_values.append_child(LABEL_XML_TUX).append_child(pugi::node_pcdata).text() = tu[0];
436 
437  //<tuy>
438  node_values.append_child(LABEL_XML_TUY).append_child(pugi::node_pcdata).text() = tu[1];
439 
440  //<tuz>
441  node_values.append_child(LABEL_XML_TUZ).append_child(pugi::node_pcdata).text() = tu[2];
442  }
443  }
444 
445  return back;
446  }
447 
455  vpXmlCodeSequenceType str2xmlcode(const char *str, vpXmlCodeType &res)
456  {
457  vpXmlCodeType val_int = CODE_XML_BAD;
459 
460  if (!strcmp(str, LABEL_XML_M)) {
461  val_int = CODE_XML_M;
462  }
463  else if (!strcmp(str, LABEL_XML_M_NAME)) {
464  val_int = CODE_XML_M_NAME;
465  }
466  else if (!strcmp(str, LABEL_XML_VALUE)) {
467  val_int = CODE_XML_VALUE;
468  }
469  else if (!strcmp(str, LABEL_XML_TX)) {
470  val_int = CODE_XML_TX;
471  }
472  else if (!strcmp(str, LABEL_XML_TY)) {
473  val_int = CODE_XML_TY;
474  }
475  else if (!strcmp(str, LABEL_XML_TZ)) {
476  val_int = CODE_XML_TZ;
477  }
478  else if (!strcmp(str, LABEL_XML_TUX)) {
479  val_int = CODE_XML_TUX;
480  }
481  else if (!strcmp(str, LABEL_XML_TUY)) {
482  val_int = CODE_XML_TUY;
483  }
484  else if (!strcmp(str, LABEL_XML_TUZ)) {
485  val_int = CODE_XML_TUZ;
486  }
487  else {
488  val_int = CODE_XML_OTHER;
489  }
490  res = val_int;
491 
492  return back;
493  }
494 
495  vpHomogeneousMatrix getHomogeneousMatrix() const { return m_M; }
496  std::string getHomogeneousMatrixName() const { return m_name; }
497 
498  void setHomogeneousMatrixName(const std::string &name) { m_name = name; }
499 
500 private:
502  std::string m_name;
503 };
504 #endif // DOXYGEN_SHOULD_SKIP_THIS
505 
507 
509 
518 int vpXmlParserHomogeneousMatrix::parse(vpHomogeneousMatrix &M, const std::string &filename, const std::string &name)
519 {
520  return m_impl->parse(M, filename, name);
521 }
522 
531 int vpXmlParserHomogeneousMatrix::save(const vpHomogeneousMatrix &M, const std::string &filename,
532  const std::string &name)
533 {
534  return m_impl->save(M, filename, name);
535 }
536 
538 {
539  return m_impl->getHomogeneousMatrix();
540 }
541 
543 {
544  return m_impl->getHomogeneousMatrixName();
545 }
546 
548 {
549  m_impl->setHomogeneousMatrixName(name);
550 }
551 END_VISP_NAMESPACE
552 #elif !defined(VISP_BUILD_SHARED_LIBS)
553 // Work around to avoid warning: libvisp_core.a(vpXmlParserHomogeneousMatrix.cpp.o) has no symbols
554 void dummy_vpXmlParserHomogeneousMatrix() { };
555 
556 #endif
Implementation of an homogeneous matrix and operations on such kind of matrices.
vpHomogeneousMatrix & build(const vpTranslationVector &t, const vpRotationMatrix &R)
Implementation of a rotation matrix and operations on such kind of matrices.
Implementation of a rotation vector as axis-angle minimal representation.
vpHomogeneousMatrix getHomogeneousMatrix() const
int parse(vpHomogeneousMatrix &M, const std::string &filename, const std::string &name)
int save(const vpHomogeneousMatrix &M, const std::string &filename, const std::string &name)
void setHomogeneousMatrixName(const std::string &name)