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 SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
19 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
20 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
21 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
26 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 #include "dart/dynamics/Frame.hpp"
34
35 #include "dart/common/Console.hpp"
36 #include "dart/dynamics/Shape.hpp"
37
38 namespace dart {
39 namespace dynamics {
40
41 typedef std::set<Entity*> EntityPtrSet;
42 typedef std::set<Frame*> FramePtrSet;
43
44 //==============================================================================
~Frame()45 Frame::~Frame()
46 {
47 if (isWorld())
48 return;
49
50 changeParentFrame(nullptr);
51
52 // Inform all child entities that this Frame is disappearing by setting their
53 // reference frames to the World frame.
54 EntityPtrSet::iterator it = mChildEntities.begin(),
55 end = mChildEntities.end();
56 while (it != end)
57 (*(it++))->changeParentFrame(Frame::World());
58 // Note: When we instruct an Entity to change its parent Frame, it will erase
59 // itself from this Frame's mChildEntities list. This would invalidate the
60 // 'it' and clobber our attempt to iterate through the std::set if we applied
61 // changeParentFrame() directly to the 'it' iterator. So instead we use the
62 // post-increment operator to iterate 'it' forward and we apply
63 // changeParentFrame() to the temporary iterator created by the
64 // post-increment operator. Put simply: we increment 'it' forward once and
65 // then apply changeParentFrame() to the pointer that 'it' held just before
66 // it incremented.
67
68 // The entity destructor takes care of informing the parent Frame that this
69 // one is disappearing
70 }
71
72 //==============================================================================
World()73 Frame* Frame::World()
74 {
75 static WorldFrame world;
76 return &world;
77 }
78
79 //==============================================================================
WorldShared()80 std::shared_ptr<Frame> Frame::WorldShared()
81 {
82 struct EnableMakeShared : WorldFrame
83 {
84 EnableMakeShared() : Entity(nullptr, true), WorldFrame()
85 {
86 }
87 };
88 static auto sharedWorld = std::make_shared<EnableMakeShared>();
89 return sharedWorld;
90 }
91
92 //==============================================================================
getWorldTransform() const93 const Eigen::Isometry3d& Frame::getWorldTransform() const
94 {
95 if (mAmWorld)
96 return mWorldTransform;
97
98 if (mNeedTransformUpdate)
99 {
100 mWorldTransform
101 = mParentFrame->getWorldTransform() * getRelativeTransform();
102 mNeedTransformUpdate = false;
103 }
104
105 return mWorldTransform;
106 }
107
108 //==============================================================================
getTransform(const Frame * _withRespectTo) const109 Eigen::Isometry3d Frame::getTransform(const Frame* _withRespectTo) const
110 {
111 if (_withRespectTo->isWorld())
112 return getWorldTransform();
113 else if (_withRespectTo == mParentFrame)
114 return getRelativeTransform();
115 else if (_withRespectTo == this)
116 return Eigen::Isometry3d::Identity();
117
118 return _withRespectTo->getWorldTransform().inverse() * getWorldTransform();
119 }
120
121 //==============================================================================
getTransform(const Frame * withRespectTo,const Frame * inCoordinatesOf) const122 Eigen::Isometry3d Frame::getTransform(
123 const Frame* withRespectTo, const Frame* inCoordinatesOf) const
124 {
125 assert(nullptr != withRespectTo);
126 assert(nullptr != inCoordinatesOf);
127
128 if (withRespectTo == inCoordinatesOf)
129 return getTransform(withRespectTo);
130
131 // Rotation from "inCoordinatesOf" to "withRespecTo"
132 Eigen::Isometry3d T = Eigen::Isometry3d::Identity();
133 T.linear() = inCoordinatesOf->getWorldTransform().linear().transpose()
134 * withRespectTo->getWorldTransform().linear();
135
136 return T * getTransform(withRespectTo);
137 }
138
139 //==============================================================================
getSpatialVelocity() const140 const Eigen::Vector6d& Frame::getSpatialVelocity() const
141 {
142 if (mAmWorld)
143 return mVelocity;
144
145 if (mNeedVelocityUpdate)
146 {
147 mVelocity
148 = math::AdInvT(
149 getRelativeTransform(), getParentFrame()->getSpatialVelocity())
150 + getRelativeSpatialVelocity();
151
152 mNeedVelocityUpdate = false;
153 }
154
155 return mVelocity;
156 }
157
158 //==============================================================================
getSpatialVelocity(const Frame * _relativeTo,const Frame * _inCoordinatesOf) const159 Eigen::Vector6d Frame::getSpatialVelocity(
160 const Frame* _relativeTo, const Frame* _inCoordinatesOf) const
161 {
162 if (this == _relativeTo)
163 return Eigen::Vector6d::Zero();
164
165 if (_relativeTo->isWorld())
166 {
167 if (this == _inCoordinatesOf)
168 return getSpatialVelocity();
169
170 if (_inCoordinatesOf->isWorld())
171 return math::AdR(getWorldTransform(), getSpatialVelocity());
172
173 return math::AdR(getTransform(_inCoordinatesOf), getSpatialVelocity());
174 }
175
176 const Eigen::Vector6d& result = (getSpatialVelocity()
177 - math::AdT(
178 _relativeTo->getTransform(this),
179 _relativeTo->getSpatialVelocity()))
180 .eval();
181
182 if (this == _inCoordinatesOf)
183 return result;
184
185 return math::AdR(getTransform(_inCoordinatesOf), result);
186 }
187
188 //==============================================================================
getSpatialVelocity(const Eigen::Vector3d & _offset) const189 Eigen::Vector6d Frame::getSpatialVelocity(const Eigen::Vector3d& _offset) const
190 {
191 return getSpatialVelocity(_offset, Frame::World(), this);
192 }
193
194 //==============================================================================
getSpatialVelocity(const Eigen::Vector3d & _offset,const Frame * _relativeTo,const Frame * _inCoordinatesOf) const195 Eigen::Vector6d Frame::getSpatialVelocity(
196 const Eigen::Vector3d& _offset,
197 const Frame* _relativeTo,
198 const Frame* _inCoordinatesOf) const
199 {
200 if (this == _relativeTo)
201 return Eigen::Vector6d::Zero();
202
203 Eigen::Vector6d v = getSpatialVelocity();
204 v.tail<3>().noalias() += v.head<3>().cross(_offset);
205
206 if (_relativeTo->isWorld())
207 {
208 if (this == _inCoordinatesOf)
209 return v;
210
211 return math::AdR(getTransform(_inCoordinatesOf), v);
212 }
213
214 Eigen::Vector6d v_0 = math::AdT(
215 _relativeTo->getTransform(this), _relativeTo->getSpatialVelocity());
216 v_0.tail<3>().noalias() += v_0.head<3>().cross(_offset);
217
218 v = v - v_0;
219
220 if (this == _inCoordinatesOf)
221 return v;
222
223 return math::AdR(getTransform(_inCoordinatesOf), v);
224 }
225
226 //==============================================================================
getLinearVelocity(const Frame * _relativeTo,const Frame * _inCoordinatesOf) const227 Eigen::Vector3d Frame::getLinearVelocity(
228 const Frame* _relativeTo, const Frame* _inCoordinatesOf) const
229 {
230 return getSpatialVelocity(_relativeTo, _inCoordinatesOf).tail<3>();
231 }
232
233 //==============================================================================
getLinearVelocity(const Eigen::Vector3d & _offset,const Frame * _relativeTo,const Frame * _inCoordinatesOf) const234 Eigen::Vector3d Frame::getLinearVelocity(
235 const Eigen::Vector3d& _offset,
236 const Frame* _relativeTo,
237 const Frame* _inCoordinatesOf) const
238 {
239 return getSpatialVelocity(_offset, _relativeTo, _inCoordinatesOf).tail<3>();
240 }
241
242 //==============================================================================
getAngularVelocity(const Frame * _relativeTo,const Frame * _inCoordinatesOf) const243 Eigen::Vector3d Frame::getAngularVelocity(
244 const Frame* _relativeTo, const Frame* _inCoordinatesOf) const
245 {
246 return getSpatialVelocity(_relativeTo, _inCoordinatesOf).head<3>();
247 }
248
249 //==============================================================================
getSpatialAcceleration() const250 const Eigen::Vector6d& Frame::getSpatialAcceleration() const
251 {
252 if (mAmWorld)
253 return mAcceleration;
254
255 if (mNeedAccelerationUpdate)
256 {
257 mAcceleration = math::AdInvT(
258 getRelativeTransform(),
259 getParentFrame()->getSpatialAcceleration())
260 + getPrimaryRelativeAcceleration()
261 + getPartialAcceleration();
262
263 mNeedAccelerationUpdate = false;
264 }
265
266 return mAcceleration;
267 }
268
269 //==============================================================================
getSpatialAcceleration(const Frame * _relativeTo,const Frame * _inCoordinatesOf) const270 Eigen::Vector6d Frame::getSpatialAcceleration(
271 const Frame* _relativeTo, const Frame* _inCoordinatesOf) const
272 {
273 // Frame 2: this, Frame 1: _relativeTo, Frame O: _inCoordinatesOf
274 // Acceleration of Frame 2 relative to Frame 1 in coordinates of O: a_21[O]
275 // Total acceleration of Frame 2 in coordinates of Frame 2: a_2[2]
276 // Velocity of Frame 2 relative to Frame 1 in coordinates of Frame 2: v_21[2]
277
278 // a_21[O] = R_O2*( a_2[2] - X_21*a_1[1] - v_2[2] x v_21[2] )
279
280 if (this == _relativeTo)
281 return Eigen::Vector6d::Zero();
282
283 if (_relativeTo->isWorld())
284 {
285 if (this == _inCoordinatesOf)
286 return getSpatialAcceleration();
287
288 if (_inCoordinatesOf->isWorld())
289 return math::AdR(getWorldTransform(), getSpatialAcceleration());
290
291 return math::AdR(getTransform(_inCoordinatesOf), getSpatialAcceleration());
292 }
293
294 const Eigen::Vector6d& result
295 = (getSpatialAcceleration()
296 - math::AdT(
297 _relativeTo->getTransform(this),
298 _relativeTo->getSpatialAcceleration())
299 + math::ad(
300 getSpatialVelocity(),
301 math::AdT(
302 _relativeTo->getTransform(this),
303 _relativeTo->getSpatialVelocity())))
304 .eval();
305
306 if (this == _inCoordinatesOf)
307 return result;
308
309 return math::AdR(getTransform(_inCoordinatesOf), result);
310 }
311
312 //==============================================================================
getSpatialAcceleration(const Eigen::Vector3d & _offset) const313 Eigen::Vector6d Frame::getSpatialAcceleration(
314 const Eigen::Vector3d& _offset) const
315 {
316 return getSpatialAcceleration(_offset, Frame::World(), this);
317 }
318
319 //==============================================================================
getSpatialAcceleration(const Eigen::Vector3d & _offset,const Frame * _relativeTo,const Frame * _inCoordinatesOf) const320 Eigen::Vector6d Frame::getSpatialAcceleration(
321 const Eigen::Vector3d& _offset,
322 const Frame* _relativeTo,
323 const Frame* _inCoordinatesOf) const
324 {
325 if (this == _relativeTo)
326 return Eigen::Vector6d::Zero();
327
328 // Compute spatial acceleration of the point
329 Eigen::Vector6d a = getSpatialAcceleration();
330 a.tail<3>().noalias() += a.head<3>().cross(_offset);
331
332 if (_relativeTo->isWorld())
333 {
334 if (this == _inCoordinatesOf)
335 return a;
336
337 return math::AdR(getTransform(_inCoordinatesOf), a);
338 }
339
340 // Compute the spatial velocity of the point
341 Eigen::Vector6d v = getSpatialVelocity();
342 v.tail<3>().noalias() += v.head<3>().cross(_offset);
343
344 // Compute the acceleration of the reference Frame
345 Eigen::Vector6d a_ref = math::AdT(
346 _relativeTo->getTransform(this), _relativeTo->getSpatialAcceleration());
347 a_ref.tail<3>().noalias() += a_ref.head<3>().cross(_offset);
348
349 // Compute the relative velocity of the point
350 const Eigen::Vector6d& v_rel = getSpatialVelocity(_offset, _relativeTo, this);
351
352 a = a - a_ref - math::ad(v, v_rel);
353
354 if (this == _inCoordinatesOf)
355 return a;
356
357 return math::AdR(getTransform(_inCoordinatesOf), a);
358 }
359
360 //==============================================================================
getLinearAcceleration(const Frame * _relativeTo,const Frame * _inCoordinatesOf) const361 Eigen::Vector3d Frame::getLinearAcceleration(
362 const Frame* _relativeTo, const Frame* _inCoordinatesOf) const
363 {
364 if (this == _relativeTo)
365 return Eigen::Vector3d::Zero();
366
367 const Eigen::Vector6d& v_rel = getSpatialVelocity(_relativeTo, this);
368
369 // r'' = a + w x v
370 const Eigen::Vector3d& a
371 = (getSpatialAcceleration(_relativeTo, this).tail<3>()
372 + v_rel.head<3>().cross(v_rel.tail<3>()))
373 .eval();
374
375 if (this == _inCoordinatesOf)
376 return a;
377
378 return getTransform(_inCoordinatesOf).linear() * a;
379 }
380
381 //==============================================================================
getLinearAcceleration(const Eigen::Vector3d & _offset,const Frame * _relativeTo,const Frame * _inCoordinatesOf) const382 Eigen::Vector3d Frame::getLinearAcceleration(
383 const Eigen::Vector3d& _offset,
384 const Frame* _relativeTo,
385 const Frame* _inCoordinatesOf) const
386 {
387 if (this == _relativeTo)
388 return Eigen::Vector3d::Zero();
389
390 const Eigen::Vector6d& v_rel = getSpatialVelocity(_offset, _relativeTo, this);
391 const Eigen::Vector3d& a
392 = (getSpatialAcceleration(_offset, _relativeTo, this).tail<3>()
393 + v_rel.head<3>().cross(v_rel.tail<3>()))
394 .eval();
395
396 if (this == _inCoordinatesOf)
397 return a;
398
399 return getTransform(_inCoordinatesOf).linear() * a;
400 }
401
402 //==============================================================================
getAngularAcceleration(const Frame * _relativeTo,const Frame * _inCoordinatesOf) const403 Eigen::Vector3d Frame::getAngularAcceleration(
404 const Frame* _relativeTo, const Frame* _inCoordinatesOf) const
405 {
406 return getSpatialAcceleration(_relativeTo, _inCoordinatesOf).head<3>();
407 }
408
409 //==============================================================================
410 template <typename T>
convertToConstSet(const std::set<T * > & _set)411 static std::set<const T*> convertToConstSet(const std::set<T*>& _set)
412 {
413 std::set<const T*> const_set;
414 for (const auto& element : _set)
415 const_set.insert(element);
416
417 return const_set;
418 }
419
420 //==============================================================================
getChildEntities()421 const std::set<Entity*>& Frame::getChildEntities()
422 {
423 return mChildEntities;
424 }
425
426 //==============================================================================
getChildEntities() const427 const std::set<const Entity*> Frame::getChildEntities() const
428 {
429 return convertToConstSet<Entity>(mChildEntities);
430 }
431
432 //==============================================================================
getNumChildEntities() const433 std::size_t Frame::getNumChildEntities() const
434 {
435 return mChildEntities.size();
436 }
437
438 //==============================================================================
getChildFrames()439 const std::set<Frame*>& Frame::getChildFrames()
440 {
441 return mChildFrames;
442 }
443
444 //==============================================================================
getChildFrames() const445 std::set<const Frame*> Frame::getChildFrames() const
446 {
447 return convertToConstSet<Frame>(mChildFrames);
448 }
449
450 //==============================================================================
getNumChildFrames() const451 std::size_t Frame::getNumChildFrames() const
452 {
453 return mChildFrames.size();
454 }
455
456 //==============================================================================
isShapeFrame() const457 bool Frame::isShapeFrame() const
458 {
459 return mAmShapeFrame;
460 }
461
462 //==============================================================================
asShapeFrame()463 ShapeFrame* Frame::asShapeFrame()
464 {
465 return nullptr;
466 }
467
468 //==============================================================================
asShapeFrame() const469 const ShapeFrame* Frame::asShapeFrame() const
470 {
471 return nullptr;
472 }
473
474 //==============================================================================
isWorld() const475 bool Frame::isWorld() const
476 {
477 return mAmWorld;
478 }
479
480 //==============================================================================
dirtyTransform()481 void Frame::dirtyTransform()
482 {
483 dirtyVelocity(); // Global Velocity depends on the Global Transform
484
485 // Always trigger the signal, in case a new subscriber has registered in the
486 // time since the last signal
487 mTransformUpdatedSignal.raise(this);
488
489 // If we already know we need to update, just quit
490 if (mNeedTransformUpdate)
491 return;
492
493 mNeedTransformUpdate = true;
494
495 for (Entity* entity : mChildEntities)
496 entity->dirtyTransform();
497 }
498
499 //==============================================================================
dirtyVelocity()500 void Frame::dirtyVelocity()
501 {
502 dirtyAcceleration(); // Global Acceleration depends on Global Velocity
503
504 // Always trigger the signal, in case a new subscriber has registered in the
505 // time since the last signal
506 mVelocityChangedSignal.raise(this);
507
508 // If we already know we need to update, just quit
509 if (mNeedVelocityUpdate)
510 return;
511
512 mNeedVelocityUpdate = true;
513
514 for (Entity* entity : mChildEntities)
515 entity->dirtyVelocity();
516 }
517
518 //==============================================================================
dirtyAcceleration()519 void Frame::dirtyAcceleration()
520 {
521 // Always trigger the signal, in case a new subscriber has registered in the
522 // time since the last signal
523 mAccelerationChangedSignal.raise(this);
524
525 // If we already know we need to update, just quit
526 if (mNeedAccelerationUpdate)
527 return;
528
529 mNeedAccelerationUpdate = true;
530
531 for (Entity* entity : mChildEntities)
532 entity->dirtyAcceleration();
533 }
534
535 //==============================================================================
Frame(Frame * _refFrame)536 Frame::Frame(Frame* _refFrame)
537 : Entity(ConstructFrame),
538 mWorldTransform(Eigen::Isometry3d::Identity()),
539 mVelocity(Eigen::Vector6d::Zero()),
540 mAcceleration(Eigen::Vector6d::Zero()),
541 mAmWorld(false),
542 mAmShapeFrame(false)
543 {
544 mAmFrame = true;
545 changeParentFrame(_refFrame);
546 }
547
548 //==============================================================================
Frame()549 Frame::Frame() : Frame(ConstructAbstract)
550 {
551 // Delegated to Frame(ConstructAbstract)
552 }
553
554 //==============================================================================
Frame(ConstructAbstractTag)555 Frame::Frame(ConstructAbstractTag)
556 : Entity(Entity::ConstructAbstract), mAmWorld(false), mAmShapeFrame(false)
557 {
558 dterr << "[Frame::constructor] You are calling a constructor for the Frame "
559 << "class which is only meant to be used by pure abstract classes. If "
560 << "you are seeing this, then there is a bug!\n";
561 assert(false);
562 }
563
564 //==============================================================================
changeParentFrame(Frame * _newParentFrame)565 void Frame::changeParentFrame(Frame* _newParentFrame)
566 {
567 if (mParentFrame == _newParentFrame)
568 return;
569
570 if (_newParentFrame)
571 {
572 if (_newParentFrame->descendsFrom(this))
573 {
574 if (!(this->isWorld() && _newParentFrame->isWorld()))
575 // We make an exception here for the World Frame, because it's
576 // special/unique
577 {
578 dtwarn << "[Frame::changeParentFrame] Attempting to create a circular "
579 << "kinematic dependency by making Frame '" << getName()
580 << "' a child of Frame '" << _newParentFrame->getName() << "'. "
581 << "This will not be allowed.\n";
582 return;
583 }
584 }
585 }
586
587 if (mParentFrame && !mParentFrame->isWorld())
588 {
589 FramePtrSet::iterator it = mParentFrame->mChildFrames.find(this);
590 if (it != mParentFrame->mChildFrames.end())
591 mParentFrame->mChildFrames.erase(it);
592 }
593
594 if (nullptr == _newParentFrame)
595 {
596 Entity::changeParentFrame(_newParentFrame);
597 return;
598 }
599
600 if (!mAmQuiet && !_newParentFrame->isWorld())
601 _newParentFrame->mChildFrames.insert(this);
602
603 Entity::changeParentFrame(_newParentFrame);
604 }
605
606 //==============================================================================
processNewEntity(Entity *)607 void Frame::processNewEntity(Entity*)
608 {
609 // Do nothing
610 }
611
612 //==============================================================================
processRemovedEntity(Entity *)613 void Frame::processRemovedEntity(Entity*)
614 {
615 // Do nothing
616 }
617
618 //==============================================================================
Frame(ConstructWorldTag)619 Frame::Frame(ConstructWorldTag)
620 : Entity(this, true),
621 mWorldTransform(Eigen::Isometry3d::Identity()),
622 mVelocity(Eigen::Vector6d::Zero()),
623 mAcceleration(Eigen::Vector6d::Zero()),
624 mAmWorld(true),
625 mAmShapeFrame(false)
626 {
627 mAmFrame = true;
628 }
629
630 //==============================================================================
631 const Eigen::Vector6d WorldFrame::mZero = Eigen::Vector6d::Zero();
632
633 //==============================================================================
getRelativeTransform() const634 const Eigen::Isometry3d& WorldFrame::getRelativeTransform() const
635 {
636 return mRelativeTf;
637 }
638
639 //==============================================================================
getRelativeSpatialVelocity() const640 const Eigen::Vector6d& WorldFrame::getRelativeSpatialVelocity() const
641 {
642 return mZero;
643 }
644
645 //==============================================================================
getRelativeSpatialAcceleration() const646 const Eigen::Vector6d& WorldFrame::getRelativeSpatialAcceleration() const
647 {
648 return mZero;
649 }
650
651 //==============================================================================
getPrimaryRelativeAcceleration() const652 const Eigen::Vector6d& WorldFrame::getPrimaryRelativeAcceleration() const
653 {
654 return mZero;
655 }
656
657 //==============================================================================
getPartialAcceleration() const658 const Eigen::Vector6d& WorldFrame::getPartialAcceleration() const
659 {
660 return mZero;
661 }
662
663 //==============================================================================
setName(const std::string & name)664 const std::string& WorldFrame::setName(const std::string& name)
665 {
666 dterr << "[WorldFrame::setName] attempting to change name of World frame to ["
667 << name << "], but this is not allowed!\n";
668 static const std::string worldName = "World";
669 return worldName;
670 }
671
672 //==============================================================================
getName() const673 const std::string& WorldFrame::getName() const
674 {
675 static const std::string worldName = "World";
676 return worldName;
677 }
678
679 //==============================================================================
WorldFrame()680 WorldFrame::WorldFrame()
681 : Entity(nullptr, true),
682 Frame(ConstructWorld),
683 mRelativeTf(Eigen::Isometry3d::Identity())
684 {
685 changeParentFrame(this);
686 }
687
688 } // namespace dynamics
689 } // namespace dart
690