GCC Code Coverage Report


Directory: ./
File: include/pinocchio/parsers/mjcf/mjcf-graph.hpp
Date: 2025-02-12 21:03:38
Exec Total Coverage
Lines: 39 39 100.0%
Branches: 54 102 52.9%

Line Branch Exec Source
1 //
2 // Copyright (c) 2015-2024 CNRS INRIA
3 //
4
5 #ifndef __pinocchio_parsers_mjcf_graph_hpp__
6 #define __pinocchio_parsers_mjcf_graph_hpp__
7
8 #include "pinocchio/parsers/urdf.hpp"
9 #include "pinocchio/multibody/model.hpp"
10 #include "pinocchio/multibody/joint/joints.hpp"
11 #include "pinocchio/algorithm/contact-info.hpp"
12 #include <boost/property_tree/xml_parser.hpp>
13 #include <boost/property_tree/ptree.hpp>
14 #include <boost/foreach.hpp>
15 #include <boost/math/constants/constants.hpp>
16 #include <boost/filesystem.hpp>
17 #include <boost/logic/tribool.hpp>
18 #include <boost/lexical_cast.hpp>
19
20 #include <sstream>
21 #include <limits>
22 #include <iostream>
23 #include <unordered_map>
24 #include <map>
25
26 namespace pinocchio
27 {
28 namespace mjcf
29 {
30 namespace details
31 {
32 struct MjcfGraph;
33 struct MjcfJoint;
34 struct MjcfGeom;
35 struct MjcfSite;
36
37 /// @brief Informations that are stocked in the XML tag compile.
38 ///
39 struct PINOCCHIO_PARSERS_DLLAPI MjcfCompiler
40 {
41 public:
42 // Global attribute to use limit that are in the model or not
43 bool autolimits = true;
44
45 // Attribute to keep or not the full path of files specified in the model
46 bool strippath = false;
47 // Directory where all the meshes are (can be relative or absolute)
48 std::string meshdir;
49 // Directory where all the textures are (can be relative or absolute)
50 std::string texturedir;
51
52 // Value for angle conversion (Mujoco default - degrees)
53 double angle_converter = boost::math::constants::pi<double>() / 180.0;
54 // Euler Axis to use to convert angles representation to quaternion
55 Eigen::Matrix3d mapEulerAngles = Eigen::Matrix3d::Identity();
56
57 // Value to crop the mass (if mass < boundMass, mass = boundMass)
58 double boundMass = 0;
59 // Value to crop the diagonal of the inertia matrix (if mass < boundMass, mass = boundMass)
60 double boundInertia = 0;
61
62 // True, false or auto - auto = indeterminate
63 boost::logic::tribool inertiafromgeom = boost::logic::indeterminate;
64
65 /// @brief Convert the angle in radian if model was declared to use degree
66 /// @param angle_ angle to convert
67 /// @return converted angle
68 double convertAngle(const double & angle_) const;
69
70 /// @brief Convert the euler angles according to the convention declared in the compile tag.
71 /// @param angles Euler angles
72 /// @return Quaternion representation of the euler angles
73 Eigen::Matrix3d convertEuler(const Eigen::Vector3d & angles) const;
74 };
75
76 /// @brief Structure to stock all default classes information
77 struct MjcfClass
78 {
79 public:
80 typedef boost::property_tree::ptree ptree;
81
82 // name of the default class
83 std::string className;
84 // Ptree associated with the class name
85 ptree classElement;
86 };
87
88 /// @brief All Bodies informations extracted from mjcf model
89 struct MjcfBody
90 {
91 public:
92 // Name of the body
93 std::string bodyName;
94 // Name of the parent
95 std::string bodyParent;
96 // Name of the default class used by this body (optional)
97 std::string bodyClassName;
98 // Special default class, that is common to all bodies and children if not specified
99 // otherwise
100 std::string childClass;
101
102 // Position of the body wrt to the previous body
103 SE3 bodyPlacement = SE3::Identity();
104 // Body inertia
105 Inertia bodyInertia = Inertia::Identity();
106
107 // Vector of joints associated with the body
108 std::vector<MjcfJoint> jointChildren;
109 // Vector of geometries associated with the body
110 std::vector<MjcfGeom> geomChildren;
111 // Vector of sites
112 std::vector<MjcfSite> siteChildren;
113 };
114
115 /// @brief All joint limits
116 struct PINOCCHIO_PARSERS_DLLAPI RangeJoint
117 {
118 // Max effort
119 Eigen::VectorXd maxEffort;
120 // Max velocity
121 Eigen::VectorXd maxVel;
122 // Max position
123 Eigen::VectorXd maxConfig;
124 // Min position
125 Eigen::VectorXd minConfig;
126
127 // Join Stiffness
128 Eigen::VectorXd springStiffness;
129 // joint position or angle in which the joint spring (if any) achieves equilibrium
130 Eigen::VectorXd springReference;
131
132 // friction applied in this joint
133 Eigen::VectorXd friction;
134 // Damping applied by this joint.
135 Eigen::VectorXd damping;
136
137 // Armature inertia created by this joint
138 Eigen::VectorXd armature;
139 // Dry friction.
140 double frictionLoss = 0.;
141
142
8/16
✓ Branch 2 taken 349 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 349 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 349 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 349 times.
✗ Branch 12 not taken.
✓ Branch 14 taken 349 times.
✗ Branch 15 not taken.
✓ Branch 17 taken 349 times.
✗ Branch 18 not taken.
✓ Branch 20 taken 349 times.
✗ Branch 21 not taken.
✓ Branch 23 taken 349 times.
✗ Branch 24 not taken.
349 RangeJoint() = default;
143 198 explicit RangeJoint(double v)
144
8/16
✓ Branch 2 taken 198 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 198 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 198 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 198 times.
✗ Branch 12 not taken.
✓ Branch 14 taken 198 times.
✗ Branch 15 not taken.
✓ Branch 17 taken 198 times.
✗ Branch 18 not taken.
✓ Branch 20 taken 198 times.
✗ Branch 21 not taken.
✓ Branch 23 taken 198 times.
✗ Branch 24 not taken.
198 {
145 198 const double infty = std::numeric_limits<double>::infinity();
146
2/4
✓ Branch 1 taken 198 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 198 times.
✗ Branch 5 not taken.
198 maxVel = Eigen::VectorXd::Constant(1, infty);
147
2/4
✓ Branch 1 taken 198 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 198 times.
✗ Branch 5 not taken.
198 maxEffort = Eigen::VectorXd::Constant(1, infty);
148
2/4
✓ Branch 1 taken 198 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 198 times.
✗ Branch 5 not taken.
198 minConfig = Eigen::VectorXd::Constant(1, -infty);
149
2/4
✓ Branch 1 taken 198 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 198 times.
✗ Branch 5 not taken.
198 maxConfig = Eigen::VectorXd::Constant(1, infty);
150
2/4
✓ Branch 1 taken 198 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 198 times.
✗ Branch 5 not taken.
198 springStiffness = Eigen::VectorXd::Constant(1, v);
151
2/4
✓ Branch 1 taken 198 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 198 times.
✗ Branch 5 not taken.
198 springReference = Eigen::VectorXd::Constant(1, v);
152
2/4
✓ Branch 1 taken 198 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 198 times.
✗ Branch 5 not taken.
198 friction = Eigen::VectorXd::Constant(1, 0.);
153
2/4
✓ Branch 1 taken 198 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 198 times.
✗ Branch 5 not taken.
198 damping = Eigen::VectorXd::Constant(1, 0.);
154
2/4
✓ Branch 1 taken 198 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 198 times.
✗ Branch 5 not taken.
198 armature = Eigen::VectorXd::Constant(1, 0.);
155 198 }
156
157 /// @brief Set dimension to the limits to match the joint nq and nv.
158 /// @tparam Nq joint configuration
159 /// @tparam Nv joint velocity
160 /// @return Range with new dimension
161 template<int Nq, int Nv>
162 RangeJoint setDimension() const;
163
164 /// @brief Concatenate 2 rangeJoint
165 /// @tparam Nq old_range, joint configuration
166 /// @tparam Nv old_range, joint velocity
167 /// @param range to concatenate with
168 /// @return Concatenated range.
169 template<int Nq, int Nv>
170 RangeJoint concatenate(const RangeJoint & range) const;
171 };
172
173 /// @brief All joint information parsed from the mjcf model
174 struct PINOCCHIO_PARSERS_DLLAPI MjcfJoint
175 {
176 public:
177 typedef boost::property_tree::ptree ptree;
178
179 // Name of the joint
180 std::string jointName = "free";
181 // Placement of the joint wrt to its body - default Identity
182 SE3 jointPlacement = SE3::Identity();
183
184 // axis of the joint - default "0 0 1"
185 Eigen::Vector3d axis = Eigen::Vector3d::UnitZ();
186 // Limits that applie to this joint
187 RangeJoint range{1};
188
189 // type of the joint (hinge, ball, slide, free) - default "hinge"
190 std::string jointType = "hinge";
191
192 double posRef = 0.; // only possible for hinge and slides
193
194 /// @param el ptree joint node
195 /// @param currentBody body to which the joint belongs to
196 /// @param currentGraph current Mjcf graph (needed to get compiler information)
197 void fill(const ptree & el, const MjcfBody & currentBody, const MjcfGraph & currentGraph);
198
199 /// @brief Go through a joint node (default class or not) and parse info into the structure
200 /// @param el ptree joint node
201 /// @param use_limits whether to parse the limits or not
202 void
203 goThroughElement(const ptree & el, bool use_limits, const MjcfCompiler & currentCompiler);
204 };
205 /// @brief All informations related to a mesh are stored here
206 struct MjcfMesh
207 {
208 // Scale of the mesh
209 Eigen::Vector3d scale = Eigen::Vector3d::Constant(1);
210 // Path to the mesh file
211 std::string filePath;
212 // Vertices of the mesh
213 Eigen::MatrixX3d vertices;
214 };
215
216 /// @brief All informations related to a texture are stored here
217 struct MjcfTexture
218 {
219 // [2d, cube, skybox], “cube”
220 std::string textType = "cube";
221 // Path to the texture file
222 std::string filePath;
223 // Size of the grid if needed
224 Eigen::Vector2d gridsize = Eigen::Vector2d::Constant(1);
225 };
226
227 /// @brief All informations related to material are stored here
228 struct PINOCCHIO_PARSERS_DLLAPI MjcfMaterial
229 {
230 typedef boost::property_tree::ptree ptree;
231 // Color of the material
232 Eigen::Vector4d rgba = Eigen::Vector4d::Constant(1);
233
234 float reflectance = 0;
235
236 float shininess = 0.5;
237
238 float specular = 0.5;
239
240 float emission = 0;
241 // name of the texture to apply on the material
242 std::string texture;
243
244 /// @brief Go through a ptree node to look for material tag related
245 /// @param el ptree material node
246 void goThroughElement(const ptree & el);
247 };
248
249 struct PINOCCHIO_PARSERS_DLLAPI MjcfGeom
250 {
251 public:
252 typedef boost::property_tree::ptree ptree;
253
254 // Kind of possible geometry
255 enum TYPE
256 {
257 VISUAL,
258 COLLISION,
259 BOTH
260 };
261 // name of the geometry object
262 std::string geomName;
263
264 // [plane, hfield, sphere, capsule, ellipsoid, cylinder, box, mesh, sdf], “sphere”
265 std::string geomType = "sphere";
266
267 // Kind of the geometry object
268 TYPE geomKind = BOTH;
269
270 // Contact filtering and dynamic pair (used here to determine geometry kind)
271 int contype = 1;
272 int conaffinity = 1;
273 // Geometry group (used to determine geometry kind)
274 int group = 0;
275
276 // String that hold size parameter
277 std::string sizeS;
278 // Optional in case fromto tag is used
279 boost::optional<std::string> fromtoS;
280 // Size parameter
281 Eigen::VectorXd size;
282
283 // Color of the geometry
284 Eigen::Vector4d rgba = Eigen::Vector4d::Constant(1);
285
286 // Name of the material applied on the geometry
287 std::string materialName;
288 // Name of the mesh (optional)
289 std::string meshName;
290
291 // Density for computing the mass
292 double density = 1000;
293 // If mass is only on the outer layer of the geometry
294 bool shellinertia = false;
295
296 // Geometry Placement in parent body. Center of the frame of geometry is the center of mass.
297 SE3 geomPlacement = SE3::Identity();
298 // Inertia of the geometry obj
299 Inertia geomInertia = Inertia::Identity();
300 // optional mass (if not defined, will use density)
301 boost::optional<double> massGeom;
302
303 /// @brief Find the geometry kind
304 void findKind();
305
306 /// @brief Compute Geometry size based on sizeS and fromtoS
307 void computeSize();
308
309 /// @brief Compute geometry inertia
310 void computeInertia();
311
312 /// @brief Fill Geometry element with info from ptree nodes
313 void fill(const ptree & el, const MjcfBody & currentBody, const MjcfGraph & currentGraph);
314
315 /// @bried Go through a geom ptree node, to gather informations
316 void goThroughElement(const ptree & el, const MjcfGraph & currentGraph);
317 };
318
319 struct PINOCCHIO_PARSERS_DLLAPI MjcfSite
320 {
321 typedef boost::property_tree::ptree ptree;
322
323 SE3 sitePlacement = SE3::Identity();
324
325 std::string siteName;
326
327 void fill(const ptree & el, const MjcfBody & currentBody, const MjcfGraph & currentGraph);
328 void goThroughElement(const ptree & el, const MjcfGraph & currentGraph);
329 };
330
331 /*
332 typedef struct mjsEquality_ { // equality specification
333 mjsElement* element; // element type
334 mjString* name; // name
335 mjtEq type; // constraint type
336 double data[mjNEQDATA]; // type-dependent data
337 mjtByte active; // is equality initially active
338 mjString* name1; // name of object 1
339 mjString* name2; // name of object 2
340 mjtNum solref[mjNREF]; // solver reference
341 mjtNum solimp[mjNIMP]; // solver impedance
342 mjString* info; // message appended to errors
343 } mjsEquality;
344 */
345 struct PINOCCHIO_PARSERS_DLLAPI MjcfEquality
346 {
347 typedef boost::property_tree::ptree ptree;
348
349 // Optional name of the equality constraint
350 std::string name;
351
352 // Type of the constraint: (connect for now)
353 std::string type;
354
355 // // Optional class for setting unspecified attributes
356 // std::string class;
357
358 // active: 'true' or 'false' (default: 'true')
359 // solref and solimp
360
361 // Name of the first body participating in the constraint
362 std::string body1;
363 // Name of the second body participating in the constraint (optional, default: world)
364 std::string body2;
365
366 // Coordinates of the 3D anchor point where the two bodies are connected.
367 // Specified relative to the local coordinate frame of the first body.
368 Eigen::Vector3d anchor = Eigen::Vector3d::Zero();
369
370 // TODO: implement when weld is introduced
371 // This attribute specifies the relative pose (3D position followed by 4D quaternion
372 // orientation) of body2 relative to body1. If the quaternion part (i.e., last 4 components
373 // of the vector) are all zeros, as in the default setting, this attribute is ignored and
374 // the relative pose is the one corresponding to the model reference pose in qpos0. The
375 // unusual default is because all equality constraint types share the same default for their
376 // numeric parameters.
377 // Eigen::VectorXd relativePose = Eigen::VectorXd::Zero(7);
378 };
379
380 /// @brief The graph which contains all information taken from the mjcf file
381 struct PINOCCHIO_PARSERS_DLLAPI MjcfGraph
382 {
383 public:
384 typedef boost::property_tree::ptree ptree;
385 typedef std::vector<std::string> VectorOfStrings;
386 typedef std::unordered_map<std::string, MjcfBody> BodyMap_t;
387 typedef std::unordered_map<std::string, MjcfClass> ClassMap_t;
388 typedef std::unordered_map<std::string, MjcfMaterial> MaterialMap_t;
389 typedef std::unordered_map<std::string, MjcfMesh> MeshMap_t;
390 typedef std::unordered_map<std::string, MjcfTexture> TextureMap_t;
391 typedef std::unordered_map<std::string, Eigen::VectorXd> ConfigMap_t;
392 typedef std::map<std::string, MjcfEquality> EqualityMap_t;
393
394 // Compiler Info needed to properly parse the rest of file
395 MjcfCompiler compilerInfo;
396 // Map of default classes
397 ClassMap_t mapOfClasses;
398 // Map of bodies
399 BodyMap_t mapOfBodies;
400 // Map of Materials
401 MaterialMap_t mapOfMaterials;
402 // Map of Meshes
403 MeshMap_t mapOfMeshes;
404 // Map of textures
405 TextureMap_t mapOfTextures;
406 // Map of model configurations
407 ConfigMap_t mapOfConfigs;
408 // Map of equality constraints
409 EqualityMap_t mapOfEqualities;
410
411 // reference configuration
412 Eigen::VectorXd referenceConfig;
413
414 // property tree where xml file is stored
415 ptree pt;
416
417 // Ordered list of bodies
418 VectorOfStrings bodiesList;
419
420 // Name of the model
421 std::string modelName;
422 std::string modelPath;
423
424 // Urdf Visitor to add joint and body
425 typedef pinocchio::urdf::details::
426 UrdfVisitor<double, 0, ::pinocchio::JointCollectionDefaultTpl>
427 UrdfVisitor;
428 UrdfVisitor & urdfVisitor;
429
430 /// @brief graph constructor
431 /// @param urdfVisitor
432 42 MjcfGraph(UrdfVisitor & urdfVisitor, const std::string & modelPath)
433
1/2
✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
42 : modelPath(modelPath)
434
2/4
✓ Branch 9 taken 42 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 42 times.
✗ Branch 13 not taken.
42 , urdfVisitor(urdfVisitor)
435 {
436 42 }
437
438 /// @brief Convert pose of an mjcf element into SE3
439 /// @param el ptree element with all the pose element
440 /// @return pose in SE3
441 SE3 convertPosition(const ptree & el) const;
442
443 /// @brief Convert Inertia of an mjcf element into Inertia model of pinocchio
444 /// @param el ptree element with all the inertial information
445 /// @return Inertia element in pinocchio
446 Inertia convertInertiaFromMjcf(const ptree & el) const;
447
448 /// @brief Go through the default part of the file and get all the class name. Fill the
449 /// mapOfDefault for later use.
450 /// @param el ptree element. Root of the default
451 void parseDefault(ptree & el, const ptree & parent, const std::string & parentTag);
452
453 /// @brief Go through the main body of the mjcf file "worldbody" to get all the info ready
454 /// to create the model.
455 /// @param el root of the tree
456 /// @param parentName name of the parentBody in the robot tree
457 void parseJointAndBody(
458 const ptree & el,
459 const boost::optional<std::string> & childClass,
460 const std::string & parentName = "");
461
462 /// @brief Parse all the info from the compile node into compilerInfo
463 /// @param el ptree compile node
464 void parseCompiler(const ptree & el);
465
466 /// @brief Parse all the info from a texture node
467 /// @param el ptree texture node
468 void parseTexture(const ptree & el);
469
470 /// @brief Parse all the info from a material node
471 /// @param el ptree material node
472 void parseMaterial(const ptree & el);
473
474 /// @brief Parse all the info from a mesh node
475 /// @param el ptree mesh node
476 void parseMesh(const ptree & el);
477
478 /// @brief Parse all the info from the meta tag asset (mesh, material, texture)
479 /// @param el ptree texture node
480 void parseAsset(const ptree & el);
481
482 /// @brief Parse all the info from the meta tag keyframe
483 /// @param el ptree keyframe node
484 void parseKeyFrame(const ptree & el);
485
486 /// @brief Parse all the info from the equality tag
487 /// @param el ptree equality node
488 void parseEquality(const ptree & el);
489
490 /// @brief parse the mjcf file into a graph
491 void parseGraph();
492
493 /// @brief parse the mjcf file into a graph
494 /// @param xmlStr xml file name
495 void parseGraphFromXML(const std::string & xmlStr);
496
497 /// @brief Create a joint to add to the joint composite if needed
498 /// @tparam TypeX joint with axis X
499 /// @tparam TypeY joint with axis Y
500 /// @tparam TypeZ joint with axis Z
501 /// @tparam TypeUnaligned joint with axis unaligned
502 /// @param axis axis of the joint
503 /// @return one of the joint with the right axis
504 template<typename TypeX, typename TypeY, typename TypeZ, typename TypeUnaligned>
505 JointModel createJoint(const Eigen::Vector3d & axis);
506
507 /// @brief Add a joint to the model. only needed when a body has a solo joint child
508 /// @param jointInfo The joint to add to the tree
509 /// @param currentBody The body associated with the joint
510 /// @param bodyInJoint Position of the body wrt to its joint
511 void
512 addSoloJoint(const MjcfJoint & jointInfo, const MjcfBody & currentBody, SE3 & bodyInJoint);
513
514 /// @brief Use all the infos that were parsed from the xml file to add a body and joint to
515 /// the model
516 /// @param nameOfBody Name of the body to add
517 void fillModel(const std::string & nameOfBody);
518
519 /// @brief Fill the pinocchio model with all the infos from the graph
520 void parseRootTree();
521
522 /// @brief Fill reference configuration for a body and all it's associated dof
523 /// @param currentBody body to check
524 void fillReferenceConfig(const MjcfBody & currentBody);
525
526 /// @brief Add a keyframe to the model (ie reference configuration)
527 /// @param keyframe Keyframe to add
528 /// @param keyName Name of the keyframe
529 void addKeyFrame(const Eigen::VectorXd & keyframe, const std::string & keyName);
530
531 /// @brief Parse the equality constraints and add them to the model
532 /// @param model Model to add the constraints to
533 /// @param contact_models Vector of contact models to add the constraints to
534 void parseContactInformation(
535 const Model & model,
536 PINOCCHIO_STD_VECTOR_WITH_EIGEN_ALLOCATOR(RigidConstraintModel) & contact_models);
537
538 /// @brief Fill geometry model with all the info taken from the mjcf model file
539 /// @param type Type of geometry to parse (COLLISION or VISUAL)
540 /// @param geomModel geometry model to fill
541 /// @param meshLoader mesh loader from hpp::fcl
542 void parseGeomTree(
543 const GeometryType & type,
544 GeometryModel & geomModel,
545 ::hpp::fcl::MeshLoaderPtr & meshLoader);
546 };
547 namespace internal
548 {
549 1518 inline std::istringstream getConfiguredStringStream(const std::string & str)
550 {
551 1518 std::istringstream posStream(str);
552
1/2
✓ Branch 1 taken 1518 times.
✗ Branch 2 not taken.
1518 posStream.exceptions(std::ios::badbit);
553 1518 return posStream;
554 }
555
556 template<int N>
557 2908 inline Eigen::Matrix<double, N, 1> getVectorFromStream(const std::string & str)
558 {
559
1/2
✓ Branch 1 taken 1454 times.
✗ Branch 2 not taken.
2908 std::istringstream stream = getConfiguredStringStream(str);
560
1/2
✓ Branch 1 taken 1454 times.
✗ Branch 2 not taken.
2908 Eigen::Matrix<double, N, 1> vector;
561
2/2
✓ Branch 0 taken 4176 times.
✓ Branch 1 taken 1454 times.
11260 for (int i = 0; i < N; i++)
562
2/4
✓ Branch 1 taken 4176 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4176 times.
✗ Branch 5 not taken.
8352 stream >> vector(i);
563
564 5816 return vector;
565 2908 }
566
567 8 inline Eigen::VectorXd getUnknownSizeVectorFromStream(const std::string & str)
568 {
569
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 std::istringstream stream = getConfiguredStringStream(str);
570 8 std::vector<double> vector;
571 double elem;
572
4/6
✓ Branch 1 taken 69 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 69 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 61 times.
✓ Branch 7 taken 8 times.
69 while (stream >> elem)
573 {
574
1/2
✓ Branch 1 taken 61 times.
✗ Branch 2 not taken.
61 vector.push_back(elem);
575 }
576
577
1/2
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 Eigen::VectorXd returnVector(vector.size());
578
2/2
✓ Branch 1 taken 61 times.
✓ Branch 2 taken 8 times.
69 for (std::size_t i = 0; i < vector.size(); i++)
579
1/2
✓ Branch 2 taken 61 times.
✗ Branch 3 not taken.
61 returnVector(static_cast<Eigen::Index>(i)) = vector[i];
580
581 16 return returnVector;
582 8 }
583 } // namespace internal
584 } // namespace details
585 } // namespace mjcf
586 } // namespace pinocchio
587
588 #endif // __pinocchio_parsers_mjcf_graph_hpp__
589