GCC Code Coverage Report


Directory: ./
File: include/crocoddyl/multibody/contacts/multiple-contacts.hxx
Date: 2025-06-03 08:14:12
Exec Total Coverage
Lines: 0 294 0.0%
Functions: 0 52 0.0%
Branches: 0 721 0.0%

Line Branch Exec Source
1 ///////////////////////////////////////////////////////////////////////////////
2 // BSD 3-Clause License
3 //
4 // Copyright (C) 2019-2025, LAAS-CNRS, University of Edinburgh,
5 // Heriot-Watt University
6 // Copyright note valid unless otherwise stated in individual files.
7 // All rights reserved.
8 ///////////////////////////////////////////////////////////////////////////////
9
10 namespace crocoddyl {
11
12 template <typename Scalar>
13 ✗ ContactModelMultipleTpl<Scalar>::ContactModelMultipleTpl(
14 std::shared_ptr<StateMultibody> state, const std::size_t nu)
15 ✗ : state_(state),
16 ✗ nc_(0),
17 ✗ nc_total_(0),
18 ✗ nu_(nu),
19 ✗ compute_all_contacts_(false) {}
20
21 template <typename Scalar>
22 ✗ ContactModelMultipleTpl<Scalar>::ContactModelMultipleTpl(
23 std::shared_ptr<StateMultibody> state)
24 ✗ : state_(state),
25 ✗ nc_(0),
26 ✗ nc_total_(0),
27 ✗ nu_(state->get_nv()),
28 ✗ compute_all_contacts_(false) {}
29
30 template <typename Scalar>
31 ✗ ContactModelMultipleTpl<Scalar>::~ContactModelMultipleTpl() {}
32
33 template <typename Scalar>
34 ✗ void ContactModelMultipleTpl<Scalar>::addContact(
35 const std::string& name, std::shared_ptr<ContactModelAbstract> contact,
36 const bool active) {
37 ✗ if (contact->get_nu() != nu_) {
38 ✗ throw_pretty("Invalid argument: "
39 << name
40 << " contact item doesn't have the same control dimension (" +
41 std::to_string(nu_) + ")");
42 }
43 std::pair<typename ContactModelContainer::iterator, bool> ret =
44 ✗ contacts_.insert(std::make_pair(
45 name, std::make_shared<ContactItem>(name, contact, active)));
46 ✗ if (ret.second == false) {
47 std::cerr << "Warning: we couldn't add the " << name
48 ✗ << " contact item, it already existed." << std::endl;
49 ✗ } else if (active) {
50 ✗ nc_ += contact->get_nc();
51 ✗ nc_total_ += contact->get_nc();
52 ✗ active_set_.insert(name);
53 ✗ } else if (!active) {
54 ✗ nc_total_ += contact->get_nc();
55 ✗ inactive_set_.insert(name);
56 }
57 ✗ }
58
59 template <typename Scalar>
60 ✗ void ContactModelMultipleTpl<Scalar>::removeContact(const std::string& name) {
61 ✗ typename ContactModelContainer::iterator it = contacts_.find(name);
62 ✗ if (it != contacts_.end()) {
63 ✗ nc_ -= it->second->contact->get_nc();
64 ✗ nc_total_ -= it->second->contact->get_nc();
65 ✗ contacts_.erase(it);
66 ✗ active_set_.erase(name);
67 ✗ inactive_set_.erase(name);
68 } else {
69 std::cerr << "Warning: we couldn't remove the " << name
70 ✗ << " contact item, it doesn't exist." << std::endl;
71 }
72 ✗ }
73
74 template <typename Scalar>
75 ✗ void ContactModelMultipleTpl<Scalar>::changeContactStatus(
76 const std::string& name, const bool active) {
77 ✗ typename ContactModelContainer::iterator it = contacts_.find(name);
78 ✗ if (it != contacts_.end()) {
79 ✗ if (active && !it->second->active) {
80 ✗ nc_ += it->second->contact->get_nc();
81 ✗ active_set_.insert(name);
82 ✗ inactive_set_.erase(name);
83 ✗ } else if (!active && it->second->active) {
84 ✗ nc_ -= it->second->contact->get_nc();
85 ✗ inactive_set_.insert(name);
86 ✗ active_set_.erase(name);
87 }
88 // "else" case: Contact status unchanged - already in desired state
89 ✗ it->second->active = active;
90 } else {
91 std::cerr << "Warning: we couldn't change the status of the " << name
92 ✗ << " contact item, it doesn't exist." << std::endl;
93 }
94 ✗ }
95
96 template <typename Scalar>
97 ✗ void ContactModelMultipleTpl<Scalar>::calc(
98 const std::shared_ptr<ContactDataMultiple>& data,
99 const Eigen::Ref<const VectorXs>& x) {
100 ✗ if (data->contacts.size() != contacts_.size()) {
101 ✗ throw_pretty("Invalid argument: "
102 << "it doesn't match the number of contact datas and models");
103 }
104
105 ✗ std::size_t nc = 0;
106 ✗ const std::size_t nv = state_->get_nv();
107 ✗ typename ContactModelContainer::iterator it_m, end_m;
108 ✗ typename ContactDataContainer::iterator it_d, end_d;
109 ✗ if (compute_all_contacts_) {
110 ✗ for (it_m = contacts_.begin(), end_m = contacts_.end(),
111 ✗ it_d = data->contacts.begin(), end_d = data->contacts.end();
112 ✗ it_m != end_m || it_d != end_d; ++it_m, ++it_d) {
113 ✗ const std::shared_ptr<ContactItem>& m_i = it_m->second;
114 ✗ const std::size_t nc_i = m_i->contact->get_nc();
115 ✗ if (m_i->active) {
116 ✗ const std::shared_ptr<ContactDataAbstract>& d_i = it_d->second;
117 ✗ assert_pretty(
118 it_m->first == it_d->first,
119 "it doesn't match the contact name between model and data ("
120 << it_m->first << " != " << it_d->first << ")");
121 ✗ m_i->contact->calc(d_i, x);
122 ✗ data->a0.segment(nc, nc_i) = d_i->a0;
123 ✗ data->Jc.block(nc, 0, nc_i, nv) = d_i->Jc;
124 } else {
125 ✗ data->a0.segment(nc, nc_i).setZero();
126 ✗ data->Jc.block(nc, 0, nc_i, nv).setZero();
127 }
128 ✗ nc += nc_i;
129 }
130 } else {
131 ✗ for (it_m = contacts_.begin(), end_m = contacts_.end(),
132 ✗ it_d = data->contacts.begin(), end_d = data->contacts.end();
133 ✗ it_m != end_m || it_d != end_d; ++it_m, ++it_d) {
134 ✗ const std::shared_ptr<ContactItem>& m_i = it_m->second;
135 ✗ if (m_i->active) {
136 ✗ const std::shared_ptr<ContactDataAbstract>& d_i = it_d->second;
137 ✗ assert_pretty(
138 it_m->first == it_d->first,
139 "it doesn't match the contact name between model and data ("
140 << it_m->first << " != " << it_d->first << ")");
141
142 ✗ m_i->contact->calc(d_i, x);
143 ✗ const std::size_t nc_i = m_i->contact->get_nc();
144 ✗ data->a0.segment(nc, nc_i) = d_i->a0;
145 ✗ data->Jc.block(nc, 0, nc_i, nv) = d_i->Jc;
146 ✗ nc += nc_i;
147 }
148 }
149 }
150 ✗ }
151
152 template <typename Scalar>
153 ✗ void ContactModelMultipleTpl<Scalar>::calcDiff(
154 const std::shared_ptr<ContactDataMultiple>& data,
155 const Eigen::Ref<const VectorXs>& x) {
156 ✗ if (data->contacts.size() != contacts_.size()) {
157 ✗ throw_pretty("Invalid argument: "
158 << "it doesn't match the number of contact datas and models");
159 }
160
161 ✗ std::size_t nc = 0;
162 ✗ const std::size_t ndx = state_->get_ndx();
163 ✗ typename ContactModelContainer::iterator it_m, end_m;
164 ✗ typename ContactDataContainer::iterator it_d, end_d;
165 ✗ if (compute_all_contacts_) {
166 ✗ for (it_m = contacts_.begin(), end_m = contacts_.end(),
167 ✗ it_d = data->contacts.begin(), end_d = data->contacts.end();
168 ✗ it_m != end_m || it_d != end_d; ++it_m, ++it_d) {
169 ✗ const std::shared_ptr<ContactItem>& m_i = it_m->second;
170 ✗ const std::size_t nc_i = m_i->contact->get_nc();
171 ✗ if (m_i->active) {
172 ✗ assert_pretty(
173 it_m->first == it_d->first,
174 "it doesn't match the contact name between model and data ("
175 << it_m->first << " != " << it_d->first << ")");
176 ✗ const std::shared_ptr<ContactDataAbstract>& d_i = it_d->second;
177
178 ✗ m_i->contact->calcDiff(d_i, x);
179 ✗ data->da0_dx.block(nc, 0, nc_i, ndx) = d_i->da0_dx;
180 } else {
181 ✗ data->da0_dx.block(nc, 0, nc_i, ndx).setZero();
182 }
183 ✗ nc += nc_i;
184 }
185 } else {
186 ✗ for (it_m = contacts_.begin(), end_m = contacts_.end(),
187 ✗ it_d = data->contacts.begin(), end_d = data->contacts.end();
188 ✗ it_m != end_m || it_d != end_d; ++it_m, ++it_d) {
189 ✗ const std::shared_ptr<ContactItem>& m_i = it_m->second;
190 ✗ if (m_i->active) {
191 ✗ const std::shared_ptr<ContactDataAbstract>& d_i = it_d->second;
192 ✗ assert_pretty(
193 it_m->first == it_d->first,
194 "it doesn't match the contact name between model and data ("
195 << it_m->first << " != " << it_d->first << ")");
196
197 ✗ m_i->contact->calcDiff(d_i, x);
198 ✗ const std::size_t nc_i = m_i->contact->get_nc();
199 ✗ data->da0_dx.block(nc, 0, nc_i, ndx) = d_i->da0_dx;
200 ✗ nc += nc_i;
201 }
202 }
203 }
204 ✗ }
205
206 template <typename Scalar>
207 ✗ void ContactModelMultipleTpl<Scalar>::updateAcceleration(
208 const std::shared_ptr<ContactDataMultiple>& data,
209 const VectorXs& dv) const {
210 ✗ if (static_cast<std::size_t>(dv.size()) != state_->get_nv()) {
211 ✗ throw_pretty(
212 "Invalid argument: " << "dv has wrong dimension (it should be " +
213 std::to_string(state_->get_nv()) + ")");
214 }
215 ✗ data->dv = dv;
216 ✗ }
217
218 template <typename Scalar>
219 ✗ void ContactModelMultipleTpl<Scalar>::updateForce(
220 const std::shared_ptr<ContactDataMultiple>& data, const VectorXs& force) {
221 ✗ if (static_cast<std::size_t>(force.size()) !=
222 ✗ (compute_all_contacts_ ? nc_total_ : nc_)) {
223 ✗ throw_pretty(
224 "Invalid argument: "
225 << "force has wrong dimension (it should be " +
226 std::to_string((compute_all_contacts_ ? nc_total_ : nc_)) + ")");
227 }
228 ✗ if (static_cast<std::size_t>(data->contacts.size()) != contacts_.size()) {
229 ✗ throw_pretty("Invalid argument: "
230 << "it doesn't match the number of contact datas and models");
231 }
232
233 ✗ for (ForceIterator it = data->fext.begin(); it != data->fext.end(); ++it) {
234 ✗ *it = pinocchio::ForceTpl<Scalar>::Zero();
235 }
236
237 ✗ std::size_t nc = 0;
238 ✗ typename ContactModelContainer::iterator it_m, end_m;
239 ✗ typename ContactDataContainer::iterator it_d, end_d;
240 ✗ if (compute_all_contacts_) {
241 ✗ for (it_m = contacts_.begin(), end_m = contacts_.end(),
242 ✗ it_d = data->contacts.begin(), end_d = data->contacts.end();
243 ✗ it_m != end_m || it_d != end_d; ++it_m, ++it_d) {
244 ✗ const std::shared_ptr<ContactItem>& m_i = it_m->second;
245 ✗ const std::shared_ptr<ContactDataAbstract>& d_i = it_d->second;
246 ✗ assert_pretty(it_m->first == it_d->first,
247 "it doesn't match the contact name between data and model");
248 ✗ const std::size_t nc_i = m_i->contact->get_nc();
249 ✗ if (m_i->active) {
250 const Eigen::VectorBlock<const VectorXs, Eigen::Dynamic> force_i =
251 ✗ force.segment(nc, nc_i);
252 ✗ m_i->contact->updateForce(d_i, force_i);
253 ✗ const pinocchio::JointIndex joint =
254 ✗ state_->get_pinocchio()->frames[d_i->frame].parentJoint;
255 ✗ data->fext[joint] = d_i->fext;
256 } else {
257 ✗ m_i->contact->setZeroForce(d_i);
258 }
259 ✗ nc += nc_i;
260 }
261 } else {
262 ✗ for (it_m = contacts_.begin(), end_m = contacts_.end(),
263 ✗ it_d = data->contacts.begin(), end_d = data->contacts.end();
264 ✗ it_m != end_m || it_d != end_d; ++it_m, ++it_d) {
265 ✗ const std::shared_ptr<ContactItem>& m_i = it_m->second;
266 ✗ const std::shared_ptr<ContactDataAbstract>& d_i = it_d->second;
267 ✗ assert_pretty(it_m->first == it_d->first,
268 "it doesn't match the contact name between data and model");
269 ✗ if (m_i->active) {
270 ✗ const std::size_t nc_i = m_i->contact->get_nc();
271 const Eigen::VectorBlock<const VectorXs, Eigen::Dynamic> force_i =
272 ✗ force.segment(nc, nc_i);
273 ✗ m_i->contact->updateForce(d_i, force_i);
274 ✗ const pinocchio::JointIndex joint =
275 ✗ state_->get_pinocchio()->frames[d_i->frame].parentJoint;
276 ✗ data->fext[joint] = d_i->fext;
277 ✗ nc += nc_i;
278 } else {
279 ✗ m_i->contact->setZeroForce(d_i);
280 }
281 }
282 }
283 ✗ }
284
285 template <typename Scalar>
286 ✗ void ContactModelMultipleTpl<Scalar>::updateAccelerationDiff(
287 const std::shared_ptr<ContactDataMultiple>& data,
288 const MatrixXs& ddv_dx) const {
289 ✗ if (static_cast<std::size_t>(ddv_dx.rows()) != state_->get_nv() ||
290 ✗ static_cast<std::size_t>(ddv_dx.cols()) != state_->get_ndx()) {
291 ✗ throw_pretty(
292 "Invalid argument: " << "ddv_dx has wrong dimension (it should be " +
293 std::to_string(state_->get_nv()) + "," +
294 std::to_string(state_->get_ndx()) + ")");
295 }
296 ✗ data->ddv_dx = ddv_dx;
297 ✗ }
298
299 template <typename Scalar>
300 ✗ void ContactModelMultipleTpl<Scalar>::updateForceDiff(
301 const std::shared_ptr<ContactDataMultiple>& data, const MatrixXs& df_dx,
302 const MatrixXs& df_du) const {
303 ✗ const std::size_t ndx = state_->get_ndx();
304 ✗ if (static_cast<std::size_t>(df_dx.rows()) !=
305 ✗ (compute_all_contacts_ ? nc_total_ : nc_) ||
306 ✗ static_cast<std::size_t>(df_dx.cols()) != ndx) {
307 ✗ throw_pretty(
308 "Invalid argument: "
309 << "df_dx has wrong dimension (it should be " +
310 std::to_string((compute_all_contacts_ ? nc_total_ : nc_)) + "," +
311 std::to_string(ndx) + ")");
312 }
313 ✗ if (static_cast<std::size_t>(df_du.rows()) !=
314 ✗ (compute_all_contacts_ ? nc_total_ : nc_) ||
315 ✗ static_cast<std::size_t>(df_du.cols()) != nu_) {
316 ✗ throw_pretty(
317 "Invalid argument: "
318 << "df_du has wrong dimension (it should be " +
319 std::to_string((compute_all_contacts_ ? nc_total_ : nc_)) + "," +
320 std::to_string(nu_) + ")");
321 }
322 ✗ if (static_cast<std::size_t>(data->contacts.size()) != contacts_.size()) {
323 ✗ throw_pretty("Invalid argument: "
324 << "it doesn't match the number of contact datas and models");
325 }
326
327 ✗ std::size_t nc = 0;
328 ✗ typename ContactModelContainer::const_iterator it_m, end_m;
329 ✗ typename ContactDataContainer::const_iterator it_d, end_d;
330 ✗ if (compute_all_contacts_) {
331 ✗ for (it_m = contacts_.begin(), end_m = contacts_.end(),
332 ✗ it_d = data->contacts.begin(), end_d = data->contacts.end();
333 ✗ it_m != end_m || it_d != end_d; ++it_m, ++it_d) {
334 ✗ const std::shared_ptr<ContactItem>& m_i = it_m->second;
335 ✗ const std::shared_ptr<ContactDataAbstract>& d_i = it_d->second;
336 ✗ assert_pretty(it_m->first == it_d->first,
337 "it doesn't match the contact name between data and model");
338 ✗ const std::size_t nc_i = m_i->contact->get_nc();
339 ✗ if (m_i->active) {
340 const Eigen::Block<const MatrixXs> df_dx_i =
341 ✗ df_dx.block(nc, 0, nc_i, ndx);
342 const Eigen::Block<const MatrixXs> df_du_i =
343 ✗ df_du.block(nc, 0, nc_i, nu_);
344 ✗ m_i->contact->updateForceDiff(d_i, df_dx_i, df_du_i);
345 } else {
346 ✗ m_i->contact->setZeroForceDiff(d_i);
347 }
348 ✗ nc += nc_i;
349 }
350 } else {
351 ✗ for (it_m = contacts_.begin(), end_m = contacts_.end(),
352 ✗ it_d = data->contacts.begin(), end_d = data->contacts.end();
353 ✗ it_m != end_m || it_d != end_d; ++it_m, ++it_d) {
354 ✗ const std::shared_ptr<ContactItem>& m_i = it_m->second;
355 ✗ const std::shared_ptr<ContactDataAbstract>& d_i = it_d->second;
356 ✗ assert_pretty(it_m->first == it_d->first,
357 "it doesn't match the contact name between data and model");
358 ✗ if (m_i->active) {
359 ✗ const std::size_t nc_i = m_i->contact->get_nc();
360 const Eigen::Block<const MatrixXs> df_dx_i =
361 ✗ df_dx.block(nc, 0, nc_i, ndx);
362 const Eigen::Block<const MatrixXs> df_du_i =
363 ✗ df_du.block(nc, 0, nc_i, nu_);
364 ✗ m_i->contact->updateForceDiff(d_i, df_dx_i, df_du_i);
365 ✗ nc += nc_i;
366 } else {
367 ✗ m_i->contact->setZeroForceDiff(d_i);
368 }
369 }
370 }
371 ✗ }
372
373 template <typename Scalar>
374 ✗ void ContactModelMultipleTpl<Scalar>::updateRneaDiff(
375 const std::shared_ptr<ContactDataMultiple>& data,
376 pinocchio::DataTpl<Scalar>& pinocchio) const {
377 ✗ if (static_cast<std::size_t>(data->contacts.size()) !=
378 ✗ this->get_contacts().size()) {
379 ✗ throw_pretty("Invalid argument: "
380 << "it doesn't match the number of contact datas and models");
381 }
382 ✗ typename ContactModelContainer::const_iterator it_m, end_m;
383 ✗ typename ContactDataContainer::const_iterator it_d, end_d;
384 ✗ for (it_m = contacts_.begin(), end_m = contacts_.end(),
385 ✗ it_d = data->contacts.begin(), end_d = data->contacts.end();
386 ✗ it_m != end_m || it_d != end_d; ++it_m, ++it_d) {
387 ✗ const std::shared_ptr<ContactItem>& m_i = it_m->second;
388 ✗ const std::shared_ptr<ContactDataAbstract>& d_i = it_d->second;
389 ✗ assert_pretty(it_m->first == it_d->first,
390 "it doesn't match the contact name between data and model");
391 ✗ if (m_i->active) {
392 ✗ switch (m_i->contact->get_type()) {
393 ✗ case pinocchio::ReferenceFrame::LOCAL:
394 ✗ break;
395 ✗ case pinocchio::ReferenceFrame::WORLD:
396 case pinocchio::ReferenceFrame::LOCAL_WORLD_ALIGNED:
397 ✗ pinocchio.dtau_dq += d_i->dtau_dq;
398 ✗ break;
399 }
400 }
401 }
402 ✗ }
403
404 template <typename Scalar>
405 std::shared_ptr<ContactDataMultipleTpl<Scalar> >
406 ✗ ContactModelMultipleTpl<Scalar>::createData(
407 pinocchio::DataTpl<Scalar>* const data) {
408 return std::allocate_shared<ContactDataMultiple>(
409 ✗ Eigen::aligned_allocator<ContactDataMultiple>(), this, data);
410 }
411
412 template <typename Scalar>
413 template <typename NewScalar>
414 ✗ ContactModelMultipleTpl<NewScalar> ContactModelMultipleTpl<Scalar>::cast()
415 const {
416 typedef ContactModelMultipleTpl<NewScalar> ReturnType;
417 typedef StateMultibodyTpl<NewScalar> StateType;
418 typedef ContactItemTpl<NewScalar> ContactType;
419 ✗ ReturnType ret(
420 ✗ std::make_shared<StateType>(state_->template cast<NewScalar>()), nu_);
421 ✗ typename ContactModelContainer::const_iterator it_m, end_m;
422 ✗ for (it_m = contacts_.begin(), end_m = contacts_.end(); it_m != end_m;
423 ✗ ++it_m) {
424 ✗ const std::string name = it_m->first;
425 ✗ const ContactType& m_i = it_m->second->template cast<NewScalar>();
426 ✗ ret.addContact(name, m_i.contact, m_i.active);
427 }
428 ✗ return ret;
429 ✗ }
430
431 template <typename Scalar>
432 const std::shared_ptr<StateMultibodyTpl<Scalar> >&
433 ✗ ContactModelMultipleTpl<Scalar>::get_state() const {
434 ✗ return state_;
435 }
436
437 template <typename Scalar>
438 const typename ContactModelMultipleTpl<Scalar>::ContactModelContainer&
439 ✗ ContactModelMultipleTpl<Scalar>::get_contacts() const {
440 ✗ return contacts_;
441 }
442
443 template <typename Scalar>
444 ✗ std::size_t ContactModelMultipleTpl<Scalar>::get_nc() const {
445 ✗ return nc_;
446 }
447
448 template <typename Scalar>
449 ✗ std::size_t ContactModelMultipleTpl<Scalar>::get_nc_total() const {
450 ✗ return nc_total_;
451 }
452
453 template <typename Scalar>
454 ✗ std::size_t ContactModelMultipleTpl<Scalar>::get_nu() const {
455 ✗ return nu_;
456 }
457
458 template <typename Scalar>
459 ✗ const std::set<std::string>& ContactModelMultipleTpl<Scalar>::get_active_set()
460 const {
461 ✗ return active_set_;
462 }
463
464 template <typename Scalar>
465 ✗ const std::set<std::string>& ContactModelMultipleTpl<Scalar>::get_inactive_set()
466 const {
467 ✗ return inactive_set_;
468 }
469
470 template <typename Scalar>
471 ✗ bool ContactModelMultipleTpl<Scalar>::getContactStatus(
472 const std::string& name) const {
473 ✗ typename ContactModelContainer::const_iterator it = contacts_.find(name);
474 ✗ if (it != contacts_.end()) {
475 ✗ return it->second->active;
476 } else {
477 std::cerr << "Warning: we couldn't get the status of the " << name
478 ✗ << " contact item, it doesn't exist." << std::endl;
479 ✗ return false;
480 }
481 }
482
483 template <typename Scalar>
484 ✗ bool ContactModelMultipleTpl<Scalar>::getComputeAllContacts() const {
485 ✗ return compute_all_contacts_;
486 }
487
488 template <typename Scalar>
489 ✗ void ContactModelMultipleTpl<Scalar>::setComputeAllContacts(const bool status) {
490 ✗ compute_all_contacts_ = status;
491 ✗ }
492
493 template <class Scalar>
494 ✗ std::ostream& operator<<(std::ostream& os,
495 const ContactModelMultipleTpl<Scalar>& model) {
496 ✗ const auto& active = model.get_active_set();
497 ✗ const auto& inactive = model.get_inactive_set();
498 ✗ os << "ContactModelMultiple:" << std::endl;
499 ✗ os << " Active:" << std::endl;
500 ✗ for (std::set<std::string>::const_iterator it = active.begin();
501 ✗ it != active.end(); ++it) {
502 const std::shared_ptr<
503 ✗ typename ContactModelMultipleTpl<Scalar>::ContactItem>& contact_item =
504 ✗ model.get_contacts().find(*it)->second;
505 ✗ if (it != --active.end()) {
506 ✗ os << " " << *it << ": " << *contact_item << std::endl;
507 } else {
508 ✗ os << " " << *it << ": " << *contact_item << std::endl;
509 }
510 }
511 ✗ os << " Inactive:" << std::endl;
512 ✗ for (std::set<std::string>::const_iterator it = inactive.begin();
513 ✗ it != inactive.end(); ++it) {
514 const std::shared_ptr<
515 ✗ typename ContactModelMultipleTpl<Scalar>::ContactItem>& contact_item =
516 ✗ model.get_contacts().find(*it)->second;
517 ✗ if (it != --inactive.end()) {
518 ✗ os << " " << *it << ": " << *contact_item << std::endl;
519 } else {
520 ✗ os << " " << *it << ": " << *contact_item;
521 }
522 }
523 ✗ return os;
524 }
525
526 } // namespace crocoddyl
527