1 /*
2 * Copyright (c) 2011-2021, The DART development contributors
3 * All rights reserved.
4 *
5 * The list of contributors can be found at:
6 * https://github.com/dartsim/dart/blob/master/LICENSE
7 *
8 * This file is provided under the following "BSD-style" License:
9 * Redistribution and use in source and binary forms, with or
10 * without modification, are permitted provided that the following
11 * conditions are met:
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * * Redistributions in binary form must reproduce the above
15 * copyright notice, this list of conditions and the following
16 * disclaimer in the documentation and/or other materials provided
17 * with the distribution.
18 * * This code incorporates portions of Open Dynamics Engine
19 * (Copyright (c) 2001-2004, Russell L. Smith. All rights
20 * reserved.) and portions of FCL (Copyright (c) 2011, Willow
21 * Garage, Inc. All rights reserved.), which were released under
22 * the same BSD license as below
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
25 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
26 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
27 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
28 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
29 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
32 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
33 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
35 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 #include "dart/simulation/World.hpp"
40
41 #include <iostream>
42 #include <string>
43 #include <vector>
44
45 #include "dart/collision/CollisionGroup.hpp"
46 #include "dart/common/Console.hpp"
47 #include "dart/constraint/BoxedLcpConstraintSolver.hpp"
48 #include "dart/constraint/ConstrainedGroup.hpp"
49 #include "dart/dynamics/Skeleton.hpp"
50 #include "dart/integration/SemiImplicitEulerIntegrator.hpp"
51
52 namespace dart {
53 namespace simulation {
54
55 //==============================================================================
create(const std::string & name)56 std::shared_ptr<World> World::create(const std::string& name)
57 {
58 return std::make_shared<World>(name);
59 }
60
61 //==============================================================================
World(const std::string & _name)62 World::World(const std::string& _name)
63 : mName(_name),
64 mNameMgrForSkeletons("World::Skeleton | " + _name, "skeleton"),
65 mNameMgrForSimpleFrames("World::SimpleFrame | " + _name, "frame"),
66 mGravity(0.0, 0.0, -9.81),
67 mTimeStep(0.001),
68 mTime(0.0),
69 mFrame(0),
70 mRecording(new Recording(mSkeletons)),
71 onNameChanged(mNameChangedSignal)
72 {
73 mIndices.push_back(0);
74
75 auto solver = std::make_unique<constraint::BoxedLcpConstraintSolver>();
76 setConstraintSolver(std::move(solver));
77 }
78
79 //==============================================================================
~World()80 World::~World()
81 {
82 delete mRecording;
83
84 for (common::Connection& connection : mNameConnectionsForSkeletons)
85 connection.disconnect();
86
87 for (common::Connection& connection : mNameConnectionsForSimpleFrames)
88 connection.disconnect();
89 }
90
91 //==============================================================================
clone() const92 WorldPtr World::clone() const
93 {
94 WorldPtr worldClone = World::create(mName);
95
96 worldClone->setGravity(mGravity);
97 worldClone->setTimeStep(mTimeStep);
98
99 auto cd = getConstraintSolver()->getCollisionDetector();
100 worldClone->getConstraintSolver()->setCollisionDetector(
101 cd->cloneWithoutCollisionObjects());
102
103 // Clone and add each Skeleton
104 for (std::size_t i = 0; i < mSkeletons.size(); ++i)
105 {
106 worldClone->addSkeleton(mSkeletons[i]->cloneSkeleton());
107 }
108
109 // Clone and add each SimpleFrame
110 for (std::size_t i = 0; i < mSimpleFrames.size(); ++i)
111 {
112 worldClone->addSimpleFrame(
113 mSimpleFrames[i]->clone(mSimpleFrames[i]->getParentFrame()));
114 }
115
116 // For each newly cloned SimpleFrame, try to make its parent Frame be one of
117 // the new clones if there is a match. This is meant to minimize any possible
118 // interdependencies between the kinematics of different worlds.
119 for (std::size_t i = 0; i < worldClone->getNumSimpleFrames(); ++i)
120 {
121 dynamics::Frame* current_parent
122 = worldClone->getSimpleFrame(i)->getParentFrame();
123
124 dynamics::SimpleFramePtr parent_candidate
125 = worldClone->getSimpleFrame(current_parent->getName());
126
127 if (parent_candidate)
128 worldClone->getSimpleFrame(i)->setParentFrame(parent_candidate.get());
129 }
130
131 return worldClone;
132 }
133
134 //==============================================================================
setTimeStep(double _timeStep)135 void World::setTimeStep(double _timeStep)
136 {
137 if (_timeStep <= 0.0)
138 {
139 dtwarn << "[World] Attempting to set negative timestep. Ignoring this "
140 << "request because it can lead to undefined behavior.\n";
141 return;
142 }
143
144 mTimeStep = _timeStep;
145 assert(mConstraintSolver);
146 mConstraintSolver->setTimeStep(_timeStep);
147 for (auto& skel : mSkeletons)
148 skel->setTimeStep(_timeStep);
149 }
150
151 //==============================================================================
getTimeStep() const152 double World::getTimeStep() const
153 {
154 return mTimeStep;
155 }
156
157 //==============================================================================
reset()158 void World::reset()
159 {
160 mTime = 0.0;
161 mFrame = 0;
162 mRecording->clear();
163 mConstraintSolver->clearLastCollisionResult();
164 }
165
166 //==============================================================================
step(bool _resetCommand)167 void World::step(bool _resetCommand)
168 {
169 // Integrate velocity for unconstrained skeletons
170 for (auto& skel : mSkeletons)
171 {
172 if (!skel->isMobile())
173 continue;
174
175 skel->computeForwardDynamics();
176 skel->integrateVelocities(mTimeStep);
177 }
178
179 // Detect activated constraints and compute constraint impulses
180 mConstraintSolver->solve();
181
182 // Compute velocity changes given constraint impulses
183 for (auto& skel : mSkeletons)
184 {
185 if (!skel->isMobile())
186 continue;
187
188 if (skel->isImpulseApplied())
189 {
190 skel->computeImpulseForwardDynamics();
191 skel->setImpulseApplied(false);
192 }
193
194 skel->integratePositions(mTimeStep);
195
196 if (_resetCommand)
197 {
198 skel->clearInternalForces();
199 skel->clearExternalForces();
200 skel->resetCommands();
201 }
202 }
203
204 mTime += mTimeStep;
205 mFrame++;
206 }
207
208 //==============================================================================
setTime(double _time)209 void World::setTime(double _time)
210 {
211 mTime = _time;
212 }
213
214 //==============================================================================
getTime() const215 double World::getTime() const
216 {
217 return mTime;
218 }
219
220 //==============================================================================
getSimFrames() const221 int World::getSimFrames() const
222 {
223 return mFrame;
224 }
225
226 //==============================================================================
setName(const std::string & _newName)227 const std::string& World::setName(const std::string& _newName)
228 {
229 if (_newName == mName)
230 return mName;
231
232 const std::string oldName = mName;
233 mName = _newName;
234
235 mNameChangedSignal.raise(oldName, mName);
236
237 mNameMgrForSkeletons.setManagerName("World::Skeleton | " + mName);
238 mNameMgrForSimpleFrames.setManagerName("World::SimpleFrame | " + mName);
239
240 return mName;
241 }
242
243 //==============================================================================
getName() const244 const std::string& World::getName() const
245 {
246 return mName;
247 }
248
249 //==============================================================================
setGravity(const Eigen::Vector3d & _gravity)250 void World::setGravity(const Eigen::Vector3d& _gravity)
251 {
252 mGravity = _gravity;
253 for (std::vector<dynamics::SkeletonPtr>::iterator it = mSkeletons.begin();
254 it != mSkeletons.end();
255 ++it)
256 {
257 (*it)->setGravity(_gravity);
258 }
259 }
260
261 //==============================================================================
getGravity() const262 const Eigen::Vector3d& World::getGravity() const
263 {
264 return mGravity;
265 }
266
267 //==============================================================================
getSkeleton(std::size_t _index) const268 dynamics::SkeletonPtr World::getSkeleton(std::size_t _index) const
269 {
270 if (_index < mSkeletons.size())
271 return mSkeletons[_index];
272
273 return nullptr;
274 }
275
276 //==============================================================================
getSkeleton(const std::string & _name) const277 dynamics::SkeletonPtr World::getSkeleton(const std::string& _name) const
278 {
279 return mNameMgrForSkeletons.getObject(_name);
280 }
281
282 //==============================================================================
getNumSkeletons() const283 std::size_t World::getNumSkeletons() const
284 {
285 return mSkeletons.size();
286 }
287
288 //==============================================================================
addSkeleton(const dynamics::SkeletonPtr & _skeleton)289 std::string World::addSkeleton(const dynamics::SkeletonPtr& _skeleton)
290 {
291 if (nullptr == _skeleton)
292 {
293 dtwarn << "[World::addSkeleton] Attempting to add a nullptr Skeleton to "
294 << "the world!\n";
295 return "";
296 }
297
298 // If mSkeletons already has _skeleton, then we do nothing.
299 if (find(mSkeletons.begin(), mSkeletons.end(), _skeleton) != mSkeletons.end())
300 {
301 dtwarn << "[World::addSkeleton] Skeleton named [" << _skeleton->getName()
302 << "] is already in the world." << std::endl;
303 return _skeleton->getName();
304 }
305
306 mSkeletons.push_back(_skeleton);
307 mMapForSkeletons[_skeleton] = _skeleton;
308
309 mNameConnectionsForSkeletons.push_back(_skeleton->onNameChanged.connect(
310 [=](dynamics::ConstMetaSkeletonPtr skel,
311 const std::string&,
312 const std::string&) { this->handleSkeletonNameChange(skel); }));
313
314 _skeleton->setName(
315 mNameMgrForSkeletons.issueNewNameAndAdd(_skeleton->getName(), _skeleton));
316
317 _skeleton->setTimeStep(mTimeStep);
318 _skeleton->setGravity(mGravity);
319
320 mIndices.push_back(mIndices.back() + _skeleton->getNumDofs());
321 mConstraintSolver->addSkeleton(_skeleton);
322
323 // Update recording
324 mRecording->updateNumGenCoords(mSkeletons);
325
326 return _skeleton->getName();
327 }
328
329 //==============================================================================
removeSkeleton(const dynamics::SkeletonPtr & _skeleton)330 void World::removeSkeleton(const dynamics::SkeletonPtr& _skeleton)
331 {
332 assert(
333 _skeleton != nullptr
334 && "Attempted to remove nullptr Skeleton from world");
335
336 if (nullptr == _skeleton)
337 {
338 dtwarn << "[World::removeSkeleton] Attempting to remove a nullptr Skeleton "
339 << "from the world!\n";
340 return;
341 }
342
343 // Find index of _skeleton in mSkeleton.
344 std::size_t index = 0;
345 for (; index < mSkeletons.size(); ++index)
346 {
347 if (mSkeletons[index] == _skeleton)
348 break;
349 }
350
351 // If i is equal to the number of skeletons, then _skeleton is not in
352 // mSkeleton. We do nothing.
353 if (index == mSkeletons.size())
354 {
355 dtwarn << "[World::removeSkeleton] Skeleton [" << _skeleton->getName()
356 << "] is not in the world.\n";
357 return;
358 }
359
360 // Update mIndices.
361 for (std::size_t i = index + 1; i < mSkeletons.size() - 1; ++i)
362 mIndices[i] = mIndices[i + 1] - _skeleton->getNumDofs();
363 mIndices.pop_back();
364
365 // Remove _skeleton from constraint handler.
366 mConstraintSolver->removeSkeleton(_skeleton);
367
368 // Remove _skeleton from mSkeletons
369 mSkeletons.erase(
370 remove(mSkeletons.begin(), mSkeletons.end(), _skeleton),
371 mSkeletons.end());
372
373 // Disconnect the name change monitor
374 mNameConnectionsForSkeletons[index].disconnect();
375 mNameConnectionsForSkeletons.erase(
376 mNameConnectionsForSkeletons.begin() + index);
377
378 // Update recording
379 mRecording->updateNumGenCoords(mSkeletons);
380
381 // Remove from NameManager
382 mNameMgrForSkeletons.removeName(_skeleton->getName());
383
384 // Remove from the pointer map
385 mMapForSkeletons.erase(_skeleton);
386 }
387
388 //==============================================================================
removeAllSkeletons()389 std::set<dynamics::SkeletonPtr> World::removeAllSkeletons()
390 {
391 std::set<dynamics::SkeletonPtr> ptrs;
392 for (std::vector<dynamics::SkeletonPtr>::iterator it = mSkeletons.begin(),
393 end = mSkeletons.end();
394 it != end;
395 ++it)
396 ptrs.insert(*it);
397
398 while (getNumSkeletons() > 0)
399 removeSkeleton(getSkeleton(0));
400
401 return ptrs;
402 }
403
404 //==============================================================================
hasSkeleton(const dynamics::ConstSkeletonPtr & skeleton) const405 bool World::hasSkeleton(const dynamics::ConstSkeletonPtr& skeleton) const
406 {
407 return std::find(mSkeletons.begin(), mSkeletons.end(), skeleton)
408 != mSkeletons.end();
409 }
410
411 //==============================================================================
hasSkeleton(const std::string & skeletonName) const412 bool World::hasSkeleton(const std::string& skeletonName) const
413 {
414 return mNameMgrForSkeletons.hasName(skeletonName);
415 }
416
417 //==============================================================================
getIndex(int _index) const418 int World::getIndex(int _index) const
419 {
420 return mIndices[_index];
421 }
422
423 //==============================================================================
getSimpleFrame(std::size_t _index) const424 dynamics::SimpleFramePtr World::getSimpleFrame(std::size_t _index) const
425 {
426 if (_index < mSimpleFrames.size())
427 return mSimpleFrames[_index];
428
429 return nullptr;
430 }
431
432 //==============================================================================
getSimpleFrame(const std::string & _name) const433 dynamics::SimpleFramePtr World::getSimpleFrame(const std::string& _name) const
434 {
435 return mNameMgrForSimpleFrames.getObject(_name);
436 }
437
438 //==============================================================================
getNumSimpleFrames() const439 std::size_t World::getNumSimpleFrames() const
440 {
441 return mSimpleFrames.size();
442 }
443
444 //==============================================================================
addSimpleFrame(const dynamics::SimpleFramePtr & _frame)445 std::string World::addSimpleFrame(const dynamics::SimpleFramePtr& _frame)
446 {
447 assert(_frame != nullptr && "Attempted to add nullptr SimpleFrame to world");
448
449 if (nullptr == _frame)
450 {
451 dtwarn << "[World::addFrame] Attempting to add a nullptr SimpleFrame to "
452 "the world!\n";
453 return "";
454 }
455
456 if (find(mSimpleFrames.begin(), mSimpleFrames.end(), _frame)
457 != mSimpleFrames.end())
458 {
459 dtwarn << "[World::addFrame] SimpleFrame named [" << _frame->getName()
460 << "] is already in the world.\n";
461 return _frame->getName();
462 }
463
464 mSimpleFrames.push_back(_frame);
465 mSimpleFrameToShared[_frame.get()] = _frame;
466
467 mNameConnectionsForSimpleFrames.push_back(_frame->onNameChanged.connect(
468 [=](const dynamics::Entity* _entity,
469 const std::string&,
470 const std::string&) { this->handleSimpleFrameNameChange(_entity); }));
471
472 _frame->setName(
473 mNameMgrForSimpleFrames.issueNewNameAndAdd(_frame->getName(), _frame));
474
475 return _frame->getName();
476 }
477
478 //==============================================================================
removeSimpleFrame(const dynamics::SimpleFramePtr & _frame)479 void World::removeSimpleFrame(const dynamics::SimpleFramePtr& _frame)
480 {
481 assert(
482 _frame != nullptr
483 && "Attempted to remove nullptr SimpleFrame from world");
484
485 std::vector<dynamics::SimpleFramePtr>::iterator it
486 = find(mSimpleFrames.begin(), mSimpleFrames.end(), _frame);
487
488 if (it == mSimpleFrames.end())
489 {
490 dtwarn << "[World::removeFrame] Frame named [" << _frame->getName()
491 << "] is not in the world.\n";
492 return;
493 }
494
495 std::size_t index = it - mSimpleFrames.begin();
496
497 // Remove the frame
498 mSimpleFrames.erase(mSimpleFrames.begin() + index);
499
500 // Disconnect the name change monitor
501 mNameConnectionsForSimpleFrames[index].disconnect();
502 mNameConnectionsForSimpleFrames.erase(
503 mNameConnectionsForSimpleFrames.begin() + index);
504
505 // Remove from NameManager
506 mNameMgrForSimpleFrames.removeName(_frame->getName());
507
508 // Remove from the pointer map
509 mSimpleFrameToShared.erase(_frame.get());
510 }
511
512 //==============================================================================
removeAllSimpleFrames()513 std::set<dynamics::SimpleFramePtr> World::removeAllSimpleFrames()
514 {
515 std::set<dynamics::SimpleFramePtr> ptrs;
516 for (std::vector<dynamics::SimpleFramePtr>::iterator it
517 = mSimpleFrames.begin(),
518 end = mSimpleFrames.end();
519 it != end;
520 ++it)
521 ptrs.insert(*it);
522
523 while (getNumSimpleFrames() > 0)
524 removeSimpleFrame(getSimpleFrame(0));
525
526 return ptrs;
527 }
528
529 //==============================================================================
checkCollision(bool checkAllCollisions)530 bool World::checkCollision(bool checkAllCollisions)
531 {
532 collision::CollisionOption option;
533
534 if (checkAllCollisions)
535 option.maxNumContacts = 1e+3;
536 else
537 option.maxNumContacts = 1u;
538
539 return checkCollision(option);
540 }
541
542 //==============================================================================
checkCollision(const collision::CollisionOption & option,collision::CollisionResult * result)543 bool World::checkCollision(
544 const collision::CollisionOption& option,
545 collision::CollisionResult* result)
546 {
547 return mConstraintSolver->getCollisionGroup()->collide(option, result);
548 }
549
550 //==============================================================================
getLastCollisionResult() const551 const collision::CollisionResult& World::getLastCollisionResult() const
552 {
553 return mConstraintSolver->getLastCollisionResult();
554 }
555
556 //==============================================================================
setConstraintSolver(constraint::UniqueConstraintSolverPtr solver)557 void World::setConstraintSolver(constraint::UniqueConstraintSolverPtr solver)
558 {
559 if (!solver)
560 {
561 dtwarn << "[World::setConstraintSolver] nullptr for constraint solver is "
562 << "not allowed. Doing nothing.";
563 return;
564 }
565
566 if (mConstraintSolver)
567 solver->setFromOtherConstraintSolver(*mConstraintSolver);
568
569 mConstraintSolver = std::move(solver);
570 mConstraintSolver->setTimeStep(mTimeStep);
571 }
572
573 //==============================================================================
getConstraintSolver()574 constraint::ConstraintSolver* World::getConstraintSolver()
575 {
576 return mConstraintSolver.get();
577 }
578
579 //==============================================================================
getConstraintSolver() const580 const constraint::ConstraintSolver* World::getConstraintSolver() const
581 {
582 return mConstraintSolver.get();
583 }
584
585 //==============================================================================
bake()586 void World::bake()
587 {
588 const auto collisionResult = getConstraintSolver()->getLastCollisionResult();
589 const auto nContacts = static_cast<int>(collisionResult.getNumContacts());
590 const auto nSkeletons = getNumSkeletons();
591
592 Eigen::VectorXd state(getIndex(nSkeletons) + 6 * nContacts);
593 for (auto i = 0u; i < getNumSkeletons(); ++i)
594 {
595 state.segment(getIndex(i), getSkeleton(i)->getNumDofs())
596 = getSkeleton(i)->getPositions();
597 }
598
599 for (auto i = 0; i < nContacts; ++i)
600 {
601 auto begin = getIndex(nSkeletons) + i * 6;
602 state.segment(begin, 3) = collisionResult.getContact(i).point;
603 state.segment(begin + 3, 3) = collisionResult.getContact(i).force;
604 }
605
606 mRecording->addState(state);
607 }
608
609 //==============================================================================
getRecording()610 Recording* World::getRecording()
611 {
612 return mRecording;
613 }
614
615 //==============================================================================
handleSkeletonNameChange(const dynamics::ConstMetaSkeletonPtr & _skeleton)616 void World::handleSkeletonNameChange(
617 const dynamics::ConstMetaSkeletonPtr& _skeleton)
618 {
619 if (nullptr == _skeleton)
620 {
621 dterr << "[World::handleSkeletonNameChange] Received a name change "
622 << "callback for a nullptr Skeleton. This is most likely a bug. "
623 << "Please report this!\n";
624 assert(false);
625 return;
626 }
627
628 // Get the new name of the Skeleton
629 const std::string& newName = _skeleton->getName();
630
631 // Find the shared version of the Skeleton
632 std::map<dynamics::ConstMetaSkeletonPtr, dynamics::SkeletonPtr>::iterator it
633 = mMapForSkeletons.find(_skeleton);
634 if (it == mMapForSkeletons.end())
635 {
636 dterr << "[World::handleSkeletonNameChange] Could not find Skeleton named ["
637 << _skeleton->getName() << "] in the shared_ptr map of World ["
638 << getName() << "]. This is most likely a bug. Please report this!\n";
639 assert(false);
640 return;
641 }
642 dynamics::SkeletonPtr sharedSkel = it->second;
643
644 // Inform the NameManager of the change
645 std::string issuedName
646 = mNameMgrForSkeletons.changeObjectName(sharedSkel, newName);
647
648 // If the name issued by the NameManger does not match, reset the name of the
649 // Skeleton to match the newly issued name.
650 if ((!issuedName.empty()) && (newName != issuedName))
651 {
652 sharedSkel->setName(issuedName);
653 }
654 else if (issuedName.empty())
655 {
656 dterr << "[World::handleSkeletonNameChange] Skeleton named ["
657 << sharedSkel->getName() << "] (" << sharedSkel << ") does not exist "
658 << "in the NameManager of World [" << getName() << "]. This is most "
659 << "likely a bug. Please report this!\n";
660 assert(false);
661 return;
662 }
663 }
664
665 //==============================================================================
handleSimpleFrameNameChange(const dynamics::Entity * _entity)666 void World::handleSimpleFrameNameChange(const dynamics::Entity* _entity)
667 {
668 // Check that this is actually a SimpleFrame
669 const dynamics::SimpleFrame* frame
670 = dynamic_cast<const dynamics::SimpleFrame*>(_entity);
671
672 if (nullptr == frame)
673 {
674 dterr << "[World::handleFrameNameChange] Received a callback for a nullptr "
675 << "enity. This is most likely a bug. Please report this!\n";
676 assert(false);
677 return;
678 }
679
680 // Get the new name of the Frame
681 const std::string& newName = frame->getName();
682
683 // Find the shared version of the Frame
684 std::map<const dynamics::SimpleFrame*, dynamics::SimpleFramePtr>::iterator it
685 = mSimpleFrameToShared.find(frame);
686 if (it == mSimpleFrameToShared.end())
687 {
688 dterr << "[World::handleFrameNameChange] Could not find SimpleFrame named ["
689 << frame->getName() << "] in the shared_ptr map of World ["
690 << getName() << "]. This is most likely a bug. Please report this!\n";
691 assert(false);
692 return;
693 }
694 dynamics::SimpleFramePtr sharedFrame = it->second;
695
696 std::string issuedName
697 = mNameMgrForSimpleFrames.changeObjectName(sharedFrame, newName);
698
699 if ((!issuedName.empty()) && (newName != issuedName))
700 {
701 sharedFrame->setName(issuedName);
702 }
703 else if (issuedName.empty())
704 {
705 dterr << "[World::handleFrameNameChange] SimpleFrame named ["
706 << frame->getName() << "] (" << frame << ") does not exist in the "
707 << "NameManager of World [" << getName() << "]. This is most likely "
708 << "a bug. Please report this!\n";
709 assert(false);
710 return;
711 }
712 }
713
714 } // namespace simulation
715 } // namespace dart
716