dart
TinkertoyWorldNode.hpp
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 #ifndef DART_EXAMPLE_OSG_OSGATLASSIMBICON_TINKERTOYWORLDNODE_HPP_
34 #define DART_EXAMPLE_OSG_OSGATLASSIMBICON_TINKERTOYWORLDNODE_HPP_
35 
36 #include <dart/dart.hpp>
37 
38 const double DefaultBlockLength = 0.5;
39 const double DefaultBlockWidth = 0.075;
40 const double DefaultJointRadius = 1.5 * DefaultBlockWidth / 2.0;
41 const double BalsaWoodDensity = 0.16 * 10e3; // kg/m^3
42 const double DefaultBlockMass
43  = BalsaWoodDensity * DefaultBlockLength * pow(DefaultBlockWidth, 2);
44 const double DefaultDamping = 0.4;
45 
46 const Eigen::Vector4d DefaultSimulationColor
47  = Eigen::Vector4d(0.5, 0.5, 1.0, 1.0);
48 const Eigen::Vector4d DefaultPausedColor
49  = Eigen::Vector4d(0xEE, 0xC9, 0x00, 0.0) / 255.0
50  + Eigen::Vector4d(0, 0, 0, 1.0);
51 const Eigen::Vector4d DefaultSelectedColor = dart::Color::Red(1.0);
52 const Eigen::Vector4d DefaultForceBodyColor = dart::Color::Fuchsia(1.0);
53 const Eigen::Vector4d DefaultForceLineColor
54  = Eigen::Vector4d(1.0, 0.63, 0.0, 1.0);
55 
56 const double MaxForce = 200.0;
57 const double DefaultForceCoeff = 100.0;
58 const double MaxForceCoeff = 1000.0;
59 const double MinForceCoeff = 10.0;
60 const double ForceIncrement = 10.0;
61 
62 //==============================================================================
64 {
65 public:
66  TinkertoyWorldNode(const dart::simulation::WorldPtr& world)
68  mForceCoeff(DefaultForceCoeff),
69  mWasSimulating(false)
70  {
71  mTarget = dart::gui::osg::InteractiveFrame::createShared(
72  dart::dynamics::Frame::World());
73  getWorld()->addSimpleFrame(mTarget);
74 
75  createShapes();
76  createInitialToy1();
77  createInitialToy2();
78  createForceLine();
79  }
80 
81  void setAllBodyColors(const Eigen::Vector4d& color)
82  {
83  for (size_t i = 0; i < getWorld()->getNumSkeletons(); ++i)
84  {
85  const dart::dynamics::SkeletonPtr& skel = getWorld()->getSkeleton(i);
86  for (size_t j = 0; j < skel->getNumBodyNodes(); ++j)
87  {
88  dart::dynamics::BodyNode* bn = skel->getBodyNode(j);
89  for (size_t k = 0; k < bn->getNumShapeNodes(); ++k)
90  bn->getShapeNode(k)->getVisualAspect()->setColor(color);
91  }
92  }
93  }
94 
95  void setPickedNodeColor(const Eigen::Vector4d& color)
96  {
97  if (!mPickedNode)
98  return;
99 
100  for (size_t i = 0; i < mPickedNode->getNumShapeNodes(); ++i)
101  mPickedNode->getShapeNode(i)->getVisualAspect()->setColor(color);
102  }
103 
104  void resetForceLine()
105  {
106  if (mPickedNode)
107  {
108  mForceLine->setVertex(0, mPickedNode->getWorldTransform() * mPickedPoint);
109  mForceLine->setVertex(1, mTarget->getWorldTransform().translation());
110  }
111  else
112  {
113  mForceLine->setVertex(0, Eigen::Vector3d::Zero());
114  mForceLine->setVertex(1, Eigen::Vector3d::Zero());
115  }
116  }
117 
118  void customPreRefresh() override
119  {
120  if (isSimulating())
121  {
122  setAllBodyColors(DefaultSimulationColor);
123  setPickedNodeColor(DefaultForceBodyColor);
124  }
125  else
126  {
127  setAllBodyColors(DefaultPausedColor);
128  setPickedNodeColor(DefaultSelectedColor);
129  }
130 
131  resetForceLine();
132  }
133 
134  void customPreStep() override
135  {
136  if (mPickedNode)
137  {
138  Eigen::Vector3d F = mForceCoeff
139  * (mTarget->getWorldTransform().translation()
140  - mPickedNode->getWorldTransform() * mPickedPoint);
141 
142  const double F_norm = F.norm();
143  if (F_norm > MaxForce)
144  F = MaxForce * F / F_norm;
145 
146  mPickedNode->addExtForce(F, mPickedPoint);
147  }
148  }
149 
150  void handlePick(const dart::gui::osg::PickInfo& pick)
151  {
153  = dynamic_cast<dart::dynamics::BodyNode*>(pick.frame->getParentFrame());
154 
155  if (!bn)
156  return;
157 
158  mPickedNode = bn;
159  mPickedPoint = bn->getWorldTransform().inverse() * pick.position;
160 
161  Eigen::Isometry3d tf = bn->getWorldTransform();
162  tf.translation()
163  = pick.position + pick.normal.normalized() * DefaultBlockWidth / 2.0;
164 
165  mTarget->setTransform(tf);
166  }
167 
168  void clearPick()
169  {
170  mPickedNode = nullptr;
171  mTarget->setTransform(Eigen::Isometry3d::Identity());
172  }
173 
174  void deletePick()
175  {
176  if (!mPickedNode)
177  return;
178 
179  if (isSimulating())
180  {
181  std::cout << " -- Please pause simulation [using the Spacebar] before "
182  << "attempting to delete blocks." << std::endl;
183  return;
184  }
185 
186  dart::dynamics::SkeletonPtr temporary = mPickedNode->remove();
187  for (size_t i = 0; i < temporary->getNumBodyNodes(); ++i)
188  {
190  mViewer->enableDragAndDrop(temporary->getBodyNode(i)));
191  }
192 
193  getWorld()->getConstraintSolver()->getCollisionGroup()->removeShapeFramesOf(
194  temporary.get());
195 
196  clearPick();
197  }
198 
199  void createShapes()
200  {
201  createWeldJointShape();
202  createRevoluteJointShape();
203  createBallJointShape();
204  createBlockShape();
205  }
206 
207  void createWeldJointShape()
208  {
209  mWeldJointShape
210  = std::make_shared<dart::dynamics::BoxShape>(Eigen::Vector3d(
211  2.0 * DefaultJointRadius, DefaultBlockWidth, DefaultBlockWidth));
212 
213  mWeldJointShape->addDataVariance(dart::dynamics::Shape::DYNAMIC_COLOR);
214  }
215 
216  void createRevoluteJointShape()
217  {
218  mRevoluteJointShape = std::make_shared<dart::dynamics::CylinderShape>(
219  DefaultJointRadius, 1.5 * DefaultBlockWidth);
220 
221  mRevoluteJointShape->addDataVariance(dart::dynamics::Shape::DYNAMIC_COLOR);
222  }
223 
224  void createBallJointShape()
225  {
226  mBallJointShape
227  = std::make_shared<dart::dynamics::SphereShape>(DefaultJointRadius);
228 
229  mBallJointShape->addDataVariance(dart::dynamics::Shape::DYNAMIC_COLOR);
230  }
231 
232  void createBlockShape()
233  {
234  mBlockShape = std::make_shared<dart::dynamics::BoxShape>(Eigen::Vector3d(
235  DefaultBlockLength, DefaultBlockWidth, DefaultBlockWidth));
236 
237  mBlockShape->addDataVariance(dart::dynamics::Shape::DYNAMIC_COLOR);
238 
239  mBlockOffset = Eigen::Isometry3d::Identity();
240  mBlockOffset.translation()[0] = DefaultBlockLength / 2.0;
241  }
242 
243  template <class JointType>
244  std::pair<JointType*, dart::dynamics::BodyNode*> addBlock(
245  dart::dynamics::BodyNode* parent,
246  const Eigen::Isometry3d& relTf,
247  const dart::dynamics::ShapePtr& jointShape)
248  {
249  if (isSimulating())
250  {
251  std::cout << " -- Please pause simulation [using the Spacebar] before "
252  << "attempting to add new bodies" << std::endl;
253  return std::make_pair(nullptr, nullptr);
254  }
255 
256  dart::dynamics::SkeletonPtr skel;
257  if (parent)
258  {
259  skel = parent->getSkeleton();
260  }
261  else
262  {
264  "toy_#" + std::to_string(getWorld()->getNumSkeletons() + 1));
265  getWorld()->addSkeleton(skel);
266  }
267 
268  auto pair = skel->createJointAndBodyNodePair<JointType>(parent);
269  JointType* joint = pair.first;
270  dart::dynamics::BodyNode* bn = pair.second;
271  bn->setName("block_#" + std::to_string(skel->getNumBodyNodes()));
272  joint->setName("joint_#" + std::to_string(skel->getNumJoints()));
273 
274  joint->setTransformFromParentBodyNode(relTf);
275  for (size_t i = 0; i < joint->getNumDofs(); ++i)
276  joint->getDof(i)->setDampingCoefficient(DefaultDamping);
277 
281  dart::dynamics::DynamicsAspect>(jointShape);
282 
285  dart::dynamics::CollisionAspect,
286  dart::dynamics::DynamicsAspect>(mBlockShape);
287  block->setRelativeTransform(mBlockOffset);
288 
289  dart::dynamics::Inertia inertia = bn->getInertia();
290  inertia.setMass(DefaultBlockMass);
291  inertia.setMoment(mBlockShape->computeInertia(DefaultBlockMass));
292  inertia.setLocalCOM(DefaultBlockLength / 2.0 * Eigen::Vector3d::UnitX());
293  bn->setInertia(inertia);
294 
295  getWorld()->getConstraintSolver()->getCollisionGroup()->addShapeFramesOf(
296  bn);
297 
298  clearPick();
299 
300  return std::make_pair(joint, bn);
301  }
302 
303  Eigen::Isometry3d getRelTf() const
304  {
305  return mPickedNode ? mTarget->getTransform(mPickedNode)
306  : mTarget->getWorldTransform();
307  }
308 
309  void addWeldJointBlock()
310  {
311  addWeldJointBlock(mPickedNode, getRelTf());
312  }
313 
314  dart::dynamics::BodyNode* addWeldJointBlock(
315  dart::dynamics::BodyNode* parent, const Eigen::Isometry3d& relTf)
316  {
317  return addBlock<dart::dynamics::WeldJoint>(parent, relTf, mWeldJointShape)
318  .second;
319  }
320 
321  void addRevoluteJointBlock()
322  {
323  addRevoluteJointBlock(mPickedNode, getRelTf());
324  }
325 
326  dart::dynamics::BodyNode* addRevoluteJointBlock(
327  dart::dynamics::BodyNode* parent, const Eigen::Isometry3d& relTf)
328  {
329  auto pair = addBlock<dart::dynamics::RevoluteJoint>(
330  parent, relTf, mRevoluteJointShape);
331 
332  if (pair.first)
333  pair.first->setAxis(Eigen::Vector3d::UnitZ());
334 
335  return pair.second;
336  }
337 
338  void addBallJointBlock()
339  {
340  addBallJointBlock(mPickedNode, getRelTf());
341  }
342 
343  dart::dynamics::BodyNode* addBallJointBlock(
344  dart::dynamics::BodyNode* parent, const Eigen::Isometry3d& relTf)
345  {
346  return addBlock<dart::dynamics::BallJoint>(parent, relTf, mBallJointShape)
347  .second;
348  }
349 
350  void createInitialToy1()
351  {
352  Eigen::Isometry3d tf(Eigen::Isometry3d::Identity());
353  tf.rotate(Eigen::AngleAxisd(
354  dart::math::toRadian(45.0), Eigen::Vector3d::UnitY()));
355  dart::dynamics::BodyNode* bn = addBallJointBlock(nullptr, tf);
356 
357  tf = Eigen::Isometry3d::Identity();
358  tf.translation()[0] = DefaultBlockLength;
359  tf.linear() = Eigen::Matrix3d::Identity();
360  tf.prerotate(Eigen::AngleAxisd(
361  dart::math::toRadian(90.0), Eigen::Vector3d::UnitX()));
362  bn = addRevoluteJointBlock(bn, tf);
363 
364  tf = Eigen::Isometry3d::Identity();
365  tf.rotate(Eigen::AngleAxisd(
366  dart::math::toRadian(90.0), Eigen::Vector3d::UnitZ()));
367  bn = addWeldJointBlock(bn, tf);
368 
369  tf = Eigen::Isometry3d::Identity();
370  tf.translation()[0] = DefaultBlockLength / 2.0;
371  tf.translation()[2] = DefaultBlockWidth;
372  tf.rotate(Eigen::AngleAxisd(
373  dart::math::toRadian(-30.0), Eigen::Vector3d::UnitZ()));
374  bn = addBallJointBlock(bn, tf);
375  }
376 
377  void createInitialToy2()
378  {
379  Eigen::Isometry3d tf(Eigen::Isometry3d::Identity());
380  tf.rotate(Eigen::AngleAxisd(
381  dart::math::toRadian(90.0), Eigen::Vector3d::UnitY()));
382  tf.pretranslate(-1.0 * Eigen::Vector3d::UnitX());
383  dart::dynamics::BodyNode* bn = addBallJointBlock(nullptr, tf);
384 
385  tf = Eigen::Isometry3d::Identity();
386  tf.translation()[0] = DefaultBlockLength;
387  tf.translation()[2] = DefaultBlockLength / 2.0;
388  tf.rotate(Eigen::AngleAxisd(
389  dart::math::toRadian(90.0), Eigen::Vector3d::UnitY()));
390  bn = addWeldJointBlock(bn, tf);
391 
392  tf = Eigen::Isometry3d::Identity();
393  tf.rotate(Eigen::AngleAxisd(
394  dart::math::toRadian(-90.0), Eigen::Vector3d::UnitX()));
395  tf.rotate(Eigen::AngleAxisd(
396  dart::math::toRadian(-90.0), Eigen::Vector3d::UnitZ()));
397  tf.translation()[2] = DefaultBlockWidth / 2.0;
398  addRevoluteJointBlock(bn, tf);
399 
400  tf.translation()[0] = DefaultBlockLength;
401  bn = addRevoluteJointBlock(bn, tf);
402 
403  tf = Eigen::Isometry3d::Identity();
404  tf.translation()[0] = DefaultBlockLength;
405  addBallJointBlock(bn, tf);
406  }
407 
408  void createForceLine()
409  {
410  dart::dynamics::SimpleFramePtr lineFrame
411  = std::make_shared<dart::dynamics::SimpleFrame>(
412  dart::dynamics::Frame::World());
413 
414  mForceLine = std::make_shared<dart::dynamics::LineSegmentShape>(
415  Eigen::Vector3d::Zero(), Eigen::Vector3d::Zero(), 3.0);
416  mForceLine->addDataVariance(dart::dynamics::Shape::DYNAMIC_VERTICES);
417 
418  lineFrame->setShape(mForceLine);
419  lineFrame->createVisualAspect();
420  lineFrame->getVisualAspect()->setColor(DefaultForceLineColor);
421 
422  getWorld()->addSimpleFrame(lineFrame);
423  }
424 
425  void setForceCoeff(double coeff)
426  {
427  mForceCoeff = coeff;
428 
429  if (mForceCoeff > MaxForceCoeff)
430  mForceCoeff = MaxForceCoeff;
431  else if (mForceCoeff < MinForceCoeff)
432  mForceCoeff = MinForceCoeff;
433  }
434 
435  double getForceCoeff() const
436  {
437  return mForceCoeff;
438  }
439 
440  void incrementForceCoeff()
441  {
442  mForceCoeff += ForceIncrement;
443  if (mForceCoeff > MaxForceCoeff)
444  mForceCoeff = MaxForceCoeff;
445 
446  std::cout << "[Force Coefficient: " << mForceCoeff << "]" << std::endl;
447  }
448 
449  void decrementForceCoeff()
450  {
451  mForceCoeff -= ForceIncrement;
452  if (mForceCoeff < MinForceCoeff)
453  mForceCoeff = MinForceCoeff;
454 
455  std::cout << "[Force Coefficient: " << mForceCoeff << "]" << std::endl;
456  }
457 
458  void reorientTarget()
459  {
460  Eigen::Isometry3d tf = mTarget->getWorldTransform();
461  tf.linear() = Eigen::Matrix3d::Identity();
462  mTarget->setTransform(tf);
463  }
464 
465  float mForceCoeff;
466 
467 protected:
468  void setupViewer() override
469  {
470  mViewer->enableDragAndDrop(mTarget.get());
471  }
472 
473  dart::dynamics::ShapePtr mWeldJointShape;
474  dart::dynamics::ShapePtr mRevoluteJointShape;
475  dart::dynamics::ShapePtr mBallJointShape;
476  dart::dynamics::ShapePtr mBlockShape;
477  Eigen::Isometry3d mBlockOffset;
478 
479  dart::dynamics::BodyNode* mPickedNode;
480  Eigen::Vector3d mPickedPoint;
481  dart::gui::osg::InteractiveFramePtr mTarget;
482 
483  dart::dynamics::LineSegmentShapePtr mForceLine;
484 
485  bool mWasSimulating;
486 };
487 
488 #endif // DART_EXAMPLE_OSG_OSGATLASSIMBICON_TINKERTOYWORLDNODE_HPP_
static SkeletonPtr create(const std::string &_name="Skeleton")
Create a new Skeleton inside of a shared_ptr.
Definition: Skeleton.cpp:384
Definition: ShapeFrame.hpp:131
void customPreStep() override
If update() is not overloaded, this function will be called at the beginning of each simulation step...
Definition: TinkertoyWorldNode.hpp:134
SkeletonPtr remove(const std::string &_name="temporary")
Remove this BodyNode and all of its children (recursively) from their Skeleton.
Definition: BodyNode.cpp:782
ShapeNode * createShapeNodeWith(const ShapePtr &shape)
Create a ShapeNode with the specified Aspects and an automatically assigned name: <BodyNodeName>_Shap...
Definition: BodyNode.hpp:185
Viewer * mViewer
Viewer that this WorldNode is inside of.
Definition: WorldNode.hpp:194
void setLocalCOM(const Eigen::Vector3d &_com)
Set the center of mass with respect to the Body-fixed frame.
Definition: Inertia.cpp:132
std::shared_ptr< dart::simulation::World > getWorld() const
Get the World that this WorldNode is associated with.
Definition: WorldNode.cpp:106
void setMoment(const Eigen::Matrix3d &_moment)
Set the moment of inertia (about the center of mass).
Definition: Inertia.cpp:145
void setMass(double _mass)
Set the mass.
Definition: Inertia.cpp:119
DragAndDrop * enableDragAndDrop(dart::dynamics::Entity *_entity)
Returns a nullptr if _entity is not a type that can support the built-in drag and drop features...
Definition: Viewer.cpp:587
const std::string & setName(const std::string &_name) override
Set name.
Definition: BodyNode.cpp:422
Definition: Inertia.hpp:43
Definition: DefaultEventHandler.hpp:59
Frame * getParentFrame()
Get the parent (reference) frame of this Entity.
Definition: Entity.cpp:81
The primitive properties (such as x/y/z scaling) of the shape might change.
Definition: Shape.hpp:88
bool isSimulating() const
Returns true iff the WorldNode is stepping between render cycles.
Definition: WorldNode.cpp:161
void setInertia(const Inertia &inertia)
Set the inertia data for this BodyNode.
Definition: BodyNode.cpp:566
bool disableDragAndDrop(DragAndDrop *_dnd)
Delete a DragAndDrop object.
Definition: Viewer.cpp:705
Definition: ShapeNode.hpp:48
void setupViewer() override
Called when this world gets added to an dart::gui::osg::Viewer.
Definition: TinkertoyWorldNode.hpp:468
void customPreRefresh() override
If update() is not overloaded, this function will be called at the beginning of each rendering cycle...
Definition: TinkertoyWorldNode.hpp:118
void addExtForce(const Eigen::Vector3d &_force, const Eigen::Vector3d &_offset=Eigen::Vector3d::Zero(), bool _isForceLocal=false, bool _isOffsetLocal=true)
Add applying linear Cartesian forces to this node.
Definition: BodyNode.cpp:1213
BodyNode class represents a single node of the skeleton.
Definition: BodyNode.hpp:74
const Inertia & getInertia() const
Get the inertia data for this BodyNode.
Definition: BodyNode.cpp:584
The coloring or textures of the shape might change.
Definition: Shape.hpp:90
Definition: TinkertoyWorldNode.hpp:63
Definition: ShapeFrame.hpp:113
SkeletonPtr getSkeleton() override
Return the Skeleton that this Node is attached to.
Definition: BodyNode.cpp:861
Definition: ShapeFrame.hpp:50
Definition: RealTimeWorldNode.hpp:44
const Eigen::Isometry3d & getWorldTransform() const
Get the transform of this Frame with respect to the World Frame.
Definition: Frame.cpp:93