1 /* -------------------------------------------------------------------------- *
2 * OpenSim: testIterators.cpp *
3 * -------------------------------------------------------------------------- *
4 * The OpenSim API is a toolkit for musculoskeletal modeling and simulation. *
5 * See http://opensim.stanford.edu and the NOTICE file for more information. *
6 * OpenSim is developed at Stanford University and supported by the US *
7 * National Institutes of Health (U54 GM072970, R24 HD065690) and by DARPA *
8 * through the Warrior Web program. *
9 * *
10 * Copyright (c) 2005-2017 Stanford University and the Authors *
11 * Author(s): Ayman Habib *
12 * Contributer(s) : *
13 * *
14 * Licensed under the Apache License, Version 2.0 (the "License"); you may *
15 * not use this file except in compliance with the License. You may obtain a *
16 * copy of the License at http://www.apache.org/licenses/LICENSE-2.0. *
17 * *
18 * Unless required by applicable law or agreed to in writing, software *
19 * distributed under the License is distributed on an "AS IS" BASIS, *
20 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
21 * See the License for the specific language governing permissions and *
22 * limitations under the License. *
23 * -------------------------------------------------------------------------- */
24 #include <iostream>
25 #include <OpenSim/Simulation/Model/Model.h>
26 #include "OpenSim/Simulation/SimbodyEngine/PinJoint.h"
27 #include <OpenSim/Common/LoadOpenSimLibrary.h>
28 #include <OpenSim/Auxiliary/auxiliaryTestFunctions.h>
29
30 using namespace OpenSim;
31 using namespace std;
32
33 // Example filter that allows for iterating only through Components that have
34 // state variables, used for demonstration purposes.
35 class ComponentWithStateVariables : public ComponentFilter {
36 public:
ComponentWithStateVariables()37 ComponentWithStateVariables() {
38 //std::cout << "ComponentWithStateVariables constructed" << std::endl;
39 };
isMatch(const Component & comp) const40 bool isMatch(const Component& comp) const override {
41 return (comp.getNumStateVariables()>0);
42 };
~ComponentWithStateVariables()43 ~ComponentWithStateVariables() {
44 //std::cout << "ComponentWithStateVariables destructed" << std::endl;
45 }
clone() const46 ComponentWithStateVariables* clone() const override {
47 return new ComponentWithStateVariables(*this);
48 }
49 };
50
51 const std::string modelFilename = "arm26.osim";
52 // TODO: Hard-coding these numbers is not ideal. As we introduce more components
53 // to recompose existing components, this will need continual updating. For example,
54 // Joint's often add PhysicalOffsetFrames to handle what used to be baked in location
55 // and orientation offsets.
56 // 2018-09-05 updates to accommodate ModelComponentSets: BodySet, JointSet,
57 // ConstraintSet, ForceSet, ProbeSet, WrapObjectSet, ...
58 const int expectedNumComponents = 200;
59 const int expectedNumJointsWithStateVariables = 2;
60 // 2018-08-22 added 2 for JointSet and ForceSet that contain Components with states
61 const int expectedNumModelComponentsWithStateVariables = 12;
62 // Below updated from 1 to 7 to account for offset frame and its geometry and
63 // wrapobjectset that are now part of the Joint
64 const int expectedNumJntComponents = 7;
65 // Test using the iterator to skip over every other Component (Frame in this case)
66 // nf = 1 ground + 2 bodies + 4 joint offsets = 7, skipping - 3 = 4
67 const int expectedNumCountSkipFrames = 4;
68
69 namespace OpenSim {
70
71 class Device : public ModelComponent {
72 OpenSim_DECLARE_CONCRETE_OBJECT(Device, ModelComponent);
73 }; // end of Device
74
75 } // namespace OpenSim
76
77
testNestedComponentListConsistency()78 void testNestedComponentListConsistency() {
79 using SimTK::Vec3;
80 using SimTK::Inertia;
81
82 Model model(modelFilename);
83
84 auto device = new OpenSim::Device();
85 device->setName("device");
86
87 auto humerus = new OpenSim::Body("device_humerus", 1, Vec3(0), Inertia(0));
88 auto radius = new OpenSim::Body("device_radius", 1, Vec3(0), Inertia(0));
89
90 auto shoulder = new OpenSim::PinJoint("device_shoulder",
91 model.getGround(), Vec3(0), Vec3(0),
92 *humerus, Vec3(0, 1, 0), Vec3(0));
93 auto elbow = new OpenSim::PinJoint("device_elbow",
94 *humerus, Vec3(0), Vec3(0),
95 *radius, Vec3(0, 1, 0), Vec3(0));
96
97 device->addComponent(shoulder);
98 device->addComponent(elbow);
99
100 model.addModelComponent(device);
101
102 std::vector<const Joint*> joints1{}, joints2{};
103 std::set<const Coordinate*> coords{};
104
105 std::cout << "Joints in the model: " << std::endl;
106 for(const auto& joint : model.getComponentList<Joint>()) {
107 std::cout << " " << joint.getAbsolutePathString() << std::endl;
108 joints1.push_back(&joint);
109 }
110
111 std::cout << "Joints and Coordinates: " << std::endl;
112 for(const auto& joint : model.getComponentList<Joint>()) {
113 joints2.push_back(&joint);
114 std::cout << " Joint: " << joint.getAbsolutePathString() << std::endl;
115 for(const auto& coord : joint.getComponentList<Coordinate>()) {
116 std::cout << " Coord: "
117 << coord.getAbsolutePathString() << std::endl;
118 coords.insert(&coord);
119 }
120 }
121
122 // Joints list should be a unique set.
123 ASSERT(std::set<const Joint*>{joints1.begin(), joints1.end()}.size() == 4);
124 // Joints1 and Joints2 must be identical.
125 ASSERT(joints1 == joints2);
126 // Expected number of unique coordinates.
127 ASSERT(coords.size() == 4);
128 }
129
testComponentListConst()130 void testComponentListConst() {
131
132 Model model(modelFilename);
133 model.printSubcomponentInfo();
134
135 ComponentList<const Component> componentsList = model.getComponentList();
136 cout << "list begin: " << componentsList.begin()->getName() << endl;
137 int numComponents = 0;
138 for (ComponentList<const Component>::const_iterator
139 it = componentsList.begin(); it != componentsList.end(); ++it) {
140 cout << "Iterator is at: " << it->getAbsolutePathString() <<
141 " <" << it->getConcreteClassName() << ">" << endl;
142 numComponents++;
143 // it->setName("this line should not compile; using const_iterator.");
144 }
145
146 ComponentList<const OpenSim::Body> bodiesList =
147 model.getComponentList<OpenSim::Body>();
148
149 int numBodies = 0;
150 cout << "Bodies list begin: " << bodiesList.begin()->getName() << endl;
151 for (ComponentList<OpenSim::Body>::const_iterator
152 it = bodiesList.begin(); it != bodiesList.end(); ++it) {
153 cout << "Iterator is at Body: " << it->getName() << endl;
154 numBodies++;
155 }
156 // Now we try the post increment variant of the iterator
157 cout << "Bodies list begin, using post increment: "
158 << bodiesList.begin()->getName() << endl;
159 int numBodiesPost = 0;
160 for (ComponentList<OpenSim::Body>::const_iterator
161 itPost = bodiesList.begin(); itPost != bodiesList.end(); itPost++) {
162 cout << "Iterator is at Body: " << itPost->getName() << endl;
163 numBodiesPost++;
164 }
165
166 int numMuscles = 0;
167 cout << "Using range-for loop over Muscles: " << endl;
168 ComponentList<const Muscle> musclesList = model.getComponentList<Muscle>();
169 for (const Muscle& muscle : musclesList) {
170 cout << "Iterator is at muscle: " << muscle.getName() << endl;
171 numMuscles++;
172 }
173
174 int numGeomPaths = 0;
175 ComponentList<const GeometryPath> geomPathList =
176 model.getComponentList<GeometryPath>();
177 for (const GeometryPath& gpath : geomPathList) {
178 (void)gpath; // Suppress unused variable warning.
179 numGeomPaths++;
180 }
181 const OpenSim::Joint& shoulderJnt = model.getJointSet().get(0);
182 // cycle through components under shoulderJnt should return the Joint
183 // and the Coordinate
184 int numJntComponents = 0;
185 ComponentList<const Component> jComponentsList =
186 shoulderJnt.getComponentList();
187 cout << "Components/subComponents under Shoulder Joint:" << endl;
188 for (ComponentList<Component>::const_iterator
189 it = jComponentsList.begin(); it != jComponentsList.end(); ++it) {
190 cout << "Iterator is at: " << it->getConcreteClassName() << " "
191 << it->getAbsolutePathString() << endl;
192 numJntComponents++;
193 }
194 cout << "Num all components = " << numComponents << endl;
195 cout << "Num bodies = " << numBodies << endl;
196 cout << "Num Muscles = " << numMuscles << endl;
197 cout << "Num GeometryPath components = " << numGeomPaths << endl;
198 // Components = Model + 3Body + 3Marker + 2(Joint+Coordinate)
199 // + 6(Muscle+GeometryPath)
200
201 // To test states we must have added the components to the system
202 // which is done when the model creates and initializes the system
203 SimTK::State state = model.initSystem();
204
205 unsigned numJoints{}, numCoords{};
206 for(const auto& joint : model.getComponentList<Joint>()) {
207 cout << "Joint: " << joint.getAbsolutePathString() << endl;
208 ++numJoints;
209 for(const auto& coord : joint.getComponentList<Coordinate>()) {
210 cout << "Coord: " << coord.getAbsolutePathString() << endl;
211 ++numCoords;
212 }
213 }
214 ASSERT(numJoints == 2);
215 ASSERT(numCoords == 2);
216
217 int numJointsWithStateVariables = 0;
218 ComponentList<const Joint> jointsWithStates =
219 model.getComponentList<Joint>();
220 ComponentWithStateVariables myFilter;
221 jointsWithStates.setFilter(myFilter);
222 for (const Joint& comp : jointsWithStates) {
223 cout << comp.getConcreteClassName() << ":"
224 << comp.getAbsolutePathString() << endl;
225 numJointsWithStateVariables++;
226 }
227
228 int numModelComponentsWithStateVariables = 0;
229 ComponentList<const ModelComponent> comps =
230 model.getComponentList<ModelComponent>();
231 comps.setFilter(myFilter);
232 for (const ModelComponent& comp : comps) {
233 cout << comp.getConcreteClassName() << ":"
234 << comp.getAbsolutePathString() << endl;
235 numModelComponentsWithStateVariables++;
236 }
237
238 cout << "numModelComponentsWithStateVariables ="
239 << numModelComponentsWithStateVariables << endl;
240
241 //Now test a std::iterator method
242 ComponentList<const Frame> allFrames = model.getComponentList<Frame>();
243 ComponentList<Frame>::const_iterator skipIter = allFrames.begin();
244 int countSkipFrames = 0;
245 while (skipIter != allFrames.end()){
246 cout << skipIter->getConcreteClassName() << ":"
247 << skipIter->getAbsolutePathString() << endl;
248 std::advance(skipIter, 2);
249 countSkipFrames++;
250 }
251
252 ASSERT(numComponents == expectedNumComponents,
253 "", 0, "Number of Components mismatch");
254 ASSERT(numBodies == model.getNumBodies(), "", 0, "Number of Bodies mismatch");
255 ASSERT(numBodiesPost == numBodies, "", 0, "Number of Bodies post mismatch");
256 ASSERT(numMuscles == model.getMuscles().getSize(), "", 0,
257 "Number of Muscles mismatch");
258 ASSERT(numJointsWithStateVariables == expectedNumJointsWithStateVariables,
259 "", 0, "Number of Joints with StateVariables mismatch");
260 ASSERT(numModelComponentsWithStateVariables ==
261 expectedNumModelComponentsWithStateVariables, "", 0,
262 "Number of Components with StateVariables mismatch");
263 ASSERT(numJntComponents == expectedNumJntComponents, "", 0,
264 "Number of Components within Joints mismatch");
265 ASSERT(countSkipFrames == expectedNumCountSkipFrames, "", 0,
266 "Number of Frames skipping every other one, mismatch");
267 }
268
269 // This test repeats the same tests as testComponentListConst(), but using
270 // updComponentList() instead, and focusing on using const iterators.
271 // That is, one still cannot
272 // modify the components, even though we are using updComponentList(). It's
273 // important that, even if you have a ComponentList() that does not allow
274 // modifying the components, it still can provide const access to the
275 // components.
testComponentListNonConstWithConstIterator()276 void testComponentListNonConstWithConstIterator() {
277 Model model(modelFilename);
278
279 // Making this a const ComponentList causes us to use the const
280 // begin()/end() methods, which return const_iterators, and thus avoids
281 // the need for implicit conversion of non-const iterator to const_iterator.
282 const ComponentList<Component> componentsList = model.updComponentList();
283 std::cout << "list begin: " << componentsList.begin()->getName() << std::endl;
284 int numComponents = 0;
285 for (ComponentList<Component>::const_iterator it = componentsList.begin();
286 it != componentsList.end();
287 ++it) {
288 std::cout << "Iterator is at: " << it->getAbsolutePathString() << " <" << it->getConcreteClassName() << ">" << std::endl;
289 numComponents++;
290 // it->setName("this line should not compile; using const_iterator.");
291 }
292
293 ComponentList<OpenSim::Body> bodiesList = model.updComponentList<OpenSim::Body>();
294 int numBodies = 0;
295 // Test the cbegin()/cend() variants, which avoid the implicit conversion
296 // from iterator to const_iterator.
297 for (ComponentList<OpenSim::Body>::const_iterator it = bodiesList.cbegin();
298 it != bodiesList.cend();
299 ++it) {
300 std::cout << "Iterator is at Body: " << it->getName() << std::endl;
301 numBodies++;
302 }
303 // Now we try the post increment variant of the iterator
304 std::cout << "Bodies list begin, using post increment: "
305 << bodiesList.begin()->getName() << std::endl;
306 int numBodiesPost = 0;
307 for (ComponentList<OpenSim::Body>::const_iterator itPost = bodiesList.begin();
308 itPost != bodiesList.end();
309 itPost++) {
310 std::cout << "Iterator is at Body: " << itPost->getName() << std::endl;
311 numBodiesPost++;
312 }
313
314 int numMuscles = 0;
315 std::cout << "Using range-for loop over Muscles: " << std::endl;
316 // Make the ComponentList const to force the range for loop to use the
317 // const iterator.
318 const ComponentList<Muscle> musclesList = model.updComponentList<Muscle>();
319 for (const Muscle& muscle : musclesList) {
320 std::cout << "Iterator is at muscle: " << muscle.getName() << std::endl;
321 numMuscles++;
322 }
323
324 int numGeomPaths = 0;
325 // Make the ComponentList const to force the range for loop to use the
326 // const iterator.
327 const ComponentList<GeometryPath> geomPathList = model.updComponentList<GeometryPath>();
328 for (const GeometryPath& gpath : geomPathList) {
329 (void)gpath; // Suppress unused variable warning.
330 numGeomPaths++;
331 }
332 OpenSim::Joint& shoulderJnt = model.getJointSet().get(0);
333 // cycle through components under shoulderJnt should return the Joint
334 // itself and the Coordinate
335 int numJntComponents = 0;
336 // This test relies on the implicit conversion from iterator to
337 // const_iterator.
338 ComponentList<Component> jComponentsList = shoulderJnt.updComponentList();
339 std::cout << "Components/subComponents under Shoulder Joint:" << std::endl;
340 for (ComponentList<Component>::const_iterator it = jComponentsList.begin();
341 it != jComponentsList.end();
342 ++it) {
343 std::cout << "Iterator is at: " << it->getConcreteClassName() << " " << it->getAbsolutePathString() << std::endl;
344 numJntComponents++;
345 }
346 cout << "Num all components = " << numComponents << std::endl;
347 cout << "Num bodies = " << numBodies << std::endl;
348 cout << "Num Muscles = " << numMuscles << std::endl;
349 cout << "Num GeometryPath components = " << numGeomPaths << std::endl;
350
351 // To test states we must have added the components to the system
352 // which is done when the model creates and initializes the system
353 SimTK::State state = model.initSystem();
354
355 int numJointsWithStateVariables = 0;
356 ComponentList<Joint> jointsWithStates = model.updComponentList<Joint>();
357 ComponentWithStateVariables myFilter;
358 jointsWithStates.setFilter(myFilter);
359 for (const Joint& comp : jointsWithStates) {
360 cout << comp.getConcreteClassName() << ":" << comp.getAbsolutePathString() << endl;
361 numJointsWithStateVariables++;
362 }
363 int numModelComponentsWithStateVariables = 0;
364 ComponentList<ModelComponent> comps = model.updComponentList<ModelComponent>();
365 comps.setFilter(myFilter);
366 for (const ModelComponent& comp : comps) {
367 cout << comp.getConcreteClassName() << ":" << comp.getAbsolutePathString() << endl;
368 numModelComponentsWithStateVariables++;
369 }
370 //Now test a std::iterator method
371 ComponentList<Frame> allFrames = model.updComponentList<Frame>();
372 // This line uses implicit conversion from iterator to const_iterator.
373 ComponentList<Frame>::const_iterator skipIter = allFrames.begin();
374 int countSkipFrames = 0;
375 while (skipIter != allFrames.end()){
376 cout << skipIter->getConcreteClassName() << ":" << skipIter->getAbsolutePathString() << endl;
377 std::advance(skipIter, 2);
378 countSkipFrames++;
379 }
380
381 ASSERT(numComponents == expectedNumComponents);
382 ASSERT(numBodies == model.getNumBodies());
383 ASSERT(numBodiesPost == numBodies);
384 ASSERT(numMuscles == model.getMuscles().getSize());
385 ASSERT(numJointsWithStateVariables == expectedNumJointsWithStateVariables);
386 ASSERT(numModelComponentsWithStateVariables ==
387 expectedNumModelComponentsWithStateVariables);
388 ASSERT(numJntComponents == expectedNumJntComponents);
389 ASSERT(countSkipFrames == expectedNumCountSkipFrames);
390
391
392 // It is not possible to convert const_iterator to (non-const) iterator.
393 {
394 // Lines are commented out b/c they don't compile. I (Chris) uncommented
395 // them during development of the non-const iterators to check that
396 // these lines do not compile.
397 ComponentList<Body> mutBodyList = model.updComponentList<Body>();
398 // ComponentList<Body>::iterator itBody = mutBodyList.cbegin();
399 // Also does not work with an abstract type.
400 ComponentList<Component> mutCompList = model.updComponentList<Component>();
401 // ComponentList<Component>::iterator itComp = mutCompList.cbegin();
402 }
403
404 }
405
406 // Similar to testComponentListNonConstWithNonConstIterator, except that we
407 // allow modifying the elements of the list.
testComponentListNonConstWithNonConstIterator()408 void testComponentListNonConstWithNonConstIterator() {
409 Model model(modelFilename);
410
411 ComponentList<Component> componentsList = model.updComponentList();
412 int numComponents = 0;
413 for (ComponentList<Component>::iterator it = componentsList.begin();
414 it != componentsList.end();
415 ++it) {
416 numComponents++;
417 it->setName(it->getName());
418 }
419
420 ComponentList<OpenSim::Body> bodiesList = model.updComponentList<OpenSim::Body>();
421 int numBodies = 0;
422 for (ComponentList<OpenSim::Body>::iterator it = bodiesList.begin();
423 it != bodiesList.end();
424 ++it) {
425 numBodies++;
426 it->setName(it->getName());
427 }
428 // Now we try the post increment variant of the iterator
429 int numBodiesPost = 0;
430 for (ComponentList<OpenSim::Body>::iterator itPost = bodiesList.begin();
431 itPost != bodiesList.end();
432 itPost++) {
433 numBodiesPost++;
434 itPost->setName(itPost->getName());
435 }
436
437 int numMuscles = 0;
438 ComponentList<Muscle> musclesList = model.updComponentList<Muscle>();
439 for (Muscle& muscle : musclesList) {
440 numMuscles++;
441 muscle.setName(muscle.getName());
442 }
443
444 int numGeomPaths = 0;
445 ComponentList<GeometryPath> geomPathList = model.updComponentList<GeometryPath>();
446 for (GeometryPath& gpath : geomPathList) {
447 (void)gpath; // Suppress unused variable warning.
448 numGeomPaths++;
449 gpath.setName(gpath.getName());
450 }
451 OpenSim::Joint& shoulderJnt = model.getJointSet().get(0);
452 // cycle through components under shoulderJnt should return the Joint
453 // itself and the Coordinate
454 int numJntComponents = 0;
455 ComponentList<Component> jComponentsList = shoulderJnt.updComponentList();
456 for (ComponentList<Component>::iterator it = jComponentsList.begin();
457 it != jComponentsList.end();
458 ++it) {
459 numJntComponents++;
460 it->setName(it->getName());
461 }
462 cout << "Num all components = " << numComponents << std::endl;
463 cout << "Num bodies = " << numBodies << std::endl;
464 cout << "Num Muscles = " << numMuscles << std::endl;
465 cout << "Num GeometryPath components = " << numGeomPaths << std::endl;
466
467 // To test states we must have added the components to the system
468 // which is done when the model creates and initializes the system
469 SimTK::State state = model.initSystem();
470
471 int numJointsWithStateVariables = 0;
472 ComponentList<Joint> jointsWithStates = model.updComponentList<Joint>();
473 ComponentWithStateVariables myFilter;
474 jointsWithStates.setFilter(myFilter);
475 for (Joint& comp : jointsWithStates) {
476 numJointsWithStateVariables++;
477 comp.setName(comp.getName());
478 }
479 int numModelComponentsWithStateVariables = 0;
480 ComponentList<ModelComponent> comps = model.updComponentList<ModelComponent>();
481 comps.setFilter(myFilter);
482 for (ModelComponent& comp : comps) {
483 numModelComponentsWithStateVariables++;
484 comp.setName(comp.getName());
485 }
486 //Now test a std::iterator method
487 ComponentList<Frame> allFrames = model.updComponentList<Frame>();
488 ComponentList<Frame>::iterator skipIter = allFrames.begin();
489 int countSkipFrames = 0;
490 while (skipIter != allFrames.end()){
491 skipIter->setName(skipIter->getName());
492 std::advance(skipIter, 2);
493 countSkipFrames++;
494 }
495
496 ASSERT(numComponents == expectedNumComponents);
497 ASSERT(numBodies == model.getNumBodies());
498 ASSERT(numBodiesPost == numBodies);
499 ASSERT(numMuscles == model.getMuscles().getSize());
500 ASSERT(numJointsWithStateVariables == expectedNumJointsWithStateVariables);
501
502 cout << "numModelComponentsWithStateVariables ="
503 << numModelComponentsWithStateVariables << endl;
504
505 ASSERT(numModelComponentsWithStateVariables ==
506 expectedNumModelComponentsWithStateVariables);
507 ASSERT(numJntComponents == expectedNumJntComponents);
508 ASSERT(countSkipFrames == expectedNumCountSkipFrames);
509 }
510
511 // Ensure that we can compare const_iterator and (non-const) iterator.
testComponentListComparisonOperators()512 void testComponentListComparisonOperators() {
513 Model model(modelFilename);
514
515 ComponentList<Body> list = model.updComponentList<Body>();
516 ComponentList<Body>::const_iterator constIt = list.cbegin();
517 ComponentList<Body>::iterator mutIt = list.begin();
518 SimTK_TEST(constIt == mutIt);
519 // It's important to switch the operands, since operator==() is invoked
520 // on the first operand, and we want to ensure both iterators can be
521 // used as the first operand.
522 SimTK_TEST(mutIt == constIt);
523 SimTK_TEST(constIt.equals(mutIt));
524 // No support for mutIt.equals(constIt); (no implicit conversion from
525 // const to non-const iterator.
526
527 ++mutIt;
528 SimTK_TEST(constIt != mutIt);
529 SimTK_TEST(mutIt != constIt);
530 }
531
main()532 int main() {
533 LoadOpenSimLibrary("osimActuators");
534 SimTK_START_TEST("testIterators");
535 SimTK_SUBTEST(testComponentListConst);
536 SimTK_SUBTEST(testComponentListNonConstWithConstIterator);
537 SimTK_SUBTEST(testComponentListNonConstWithNonConstIterator);
538 SimTK_SUBTEST(testComponentListComparisonOperators);
539 SimTK_SUBTEST(testNestedComponentListConsistency);
540 SimTK_END_TEST();
541 }
542
543
544
545