Block.cpp
Go to the documentation of this file.
1 /*+-------------------------------------------------------------------------+
2  | MultiVehicle simulator (libmvsim) |
3  | |
4  | Copyright (C) 2014-2020 Jose Luis Blanco Claraco |
5  | Copyright (C) 2017 Borys Tymchenko (Odessa Polytechnic University) |
6  | Distributed under 3-clause BSD License |
7  | See COPYING |
8  +-------------------------------------------------------------------------+ */
9 
10 #include <mrpt/core/bits_math.h>
11 #include <mrpt/core/format.h>
12 #include <mrpt/core/lock_helper.h>
13 #include <mrpt/math/TPose2D.h>
15 #include <mrpt/poses/CPose2D.h>
16 #include <mvsim/Block.h>
17 #include <mvsim/World.h>
18 
19 #include <map>
20 #include <rapidxml.hpp>
21 #include <rapidxml_print.hpp>
22 #include <rapidxml_utils.hpp>
23 #include <sstream> // std::stringstream
24 #include <string>
25 
26 #include "JointXMLnode.h"
27 #include "XMLClassesRegistry.h"
28 #include "xml_utils.h"
29 
30 using namespace mvsim;
31 using namespace std;
32 
33 static XmlClassesRegistry block_classes_registry("block:class");
34 
35 // Protected ctor:
36 Block::Block(World* parent) : VisualObject(parent)
37 {
38  // Default shape:
39  m_block_poly.emplace_back(-0.5, -0.5);
40  m_block_poly.emplace_back(-0.5, 0.5);
41  m_block_poly.emplace_back(0.5, 0.5);
42  m_block_poly.emplace_back(0.5, -0.5);
44 }
45 
49 {
50  // Sanity checks:
51  if (!xml_node)
52  throw runtime_error(
53  "[Block::register_vehicle_class] XML node is nullptr");
54  if (0 != strcmp(xml_node->name(), "block:class"))
55  throw runtime_error(mrpt::format(
56  "[Block::register_block_class] XML element is '%s' "
57  "('block:class' expected)",
58  xml_node->name()));
59 
60  // rapidxml doesn't allow making copied of objects.
61  // So: convert to txt; then re-parse.
62  std::stringstream ss;
63  ss << *xml_node;
64 
65  block_classes_registry.add(ss.str());
66 }
67 
69 {
70  using namespace std;
71  using namespace rapidxml;
72 
73  if (!root) throw runtime_error("[Block::factory] XML node is nullptr");
74  if (0 != strcmp(root->name(), "block"))
75  throw runtime_error(mrpt::format(
76  "[Block::factory] XML root element is '%s' ('block' expected)",
77  root->name()));
78 
79  // "class": When there is a 'class="XXX"' attribute, look for each parameter
80  // in the set of "root" + "class_root" XML nodes:
81  // --------------------------------------------------------------------------------
82  JointXMLnode<> block_root_node;
83  const rapidxml::xml_node<char>* class_root = nullptr;
84  {
85  block_root_node.add(
86  root); // Always search in root. Also in the class root, if any:
87  const xml_attribute<>* block_class = root->first_attribute("class");
88  if (block_class)
89  {
90  const string sClassName = block_class->value();
91  class_root = block_classes_registry.get(sClassName);
92  if (!class_root)
93  throw runtime_error(mrpt::format(
94  "[Block::factory] Block class '%s' undefined",
95  sClassName.c_str()));
96  block_root_node.add(class_root);
97  }
98  }
99 
100  // Build object (we don't use class factory for blocks)
101  // ----------------------------------------------------
102  Block::Ptr block = Block::Ptr(new Block(parent));
103 
104  // Init params
105  // -------------------------------------------------
106  // attrib: name
107  {
108  const xml_attribute<>* attrib_name = root->first_attribute("name");
109  if (attrib_name && attrib_name->value())
110  {
111  block->m_name = attrib_name->value();
112  }
113  else
114  {
115  // Default name:
116  static int cnt = 0;
117  block->m_name = mrpt::format("block%03i", ++cnt);
118  }
119  }
120 
121  // (Mandatory) initial pose:
122  {
123  const xml_node<>* node = block_root_node.first_node("init_pose");
124  if (!node)
125  throw runtime_error(
126  "[Block::factory] Missing XML node <init_pose>");
127 
129  if (3 != ::sscanf(node->value(), "%lf %lf %lf", &q.x, &q.y, &q.yaw))
130  throw runtime_error(
131  "[Block::factory] Error parsing <init_pose>...</init_pose>");
132  q.yaw *= M_PI / 180.0; // deg->rad
133  block->setPose(q);
134  }
135 
136  // (Optional) initial vel:
137  {
138  const xml_node<>* node = block_root_node.first_node("init_vel");
139  if (node)
140  {
142  if (3 !=
143  ::sscanf(
144  node->value(), "%lf %lf %lf", &dq.vx, &dq.vy, &dq.omega))
145  throw runtime_error(
146  "[Block::factory] Error parsing <init_vel>...</init_vel>");
147  dq.omega *= M_PI / 180.0; // deg->rad
148 
149  // Convert twist (velocity) from local -> global coords:
150  dq.rotate(block->getPose().yaw);
151  block->setTwist(dq);
152  }
153  }
154 
155  // Custom visualization 3D model:
156  // -----------------------------------------------------------
157  block->parseVisual(block_root_node.first_node("visual"));
158  block->parseSimulable(block_root_node.first_node("publish"));
159 
160  // Params:
161  // -----------------------------------------------------------
163  *root, block->m_params, {}, "[Block::factory]");
164  if (class_root)
166  *class_root, block->m_params, {}, "[Block::factory]");
167 
168  // Auto shape node from visual?
169  if (const rapidxml::xml_node<char>* xml_shape_viz =
170  block_root_node.first_node("shape_from_visual");
171  xml_shape_viz)
172  {
173  mrpt::math::TPoint3D bbmin, bbmax;
174  block->getVisualModelBoundingBox(bbmin, bbmax);
175  if (bbmin == bbmax)
176  {
178  "Error: Tag <shape_from_visual/> found but bounding box of "
179  "visual object seems incorrect.");
180  }
181 
182  block->m_block_poly.clear();
183  block->m_block_poly.emplace_back(bbmin.x, bbmin.y);
184  block->m_block_poly.emplace_back(bbmin.x, bbmax.y);
185  block->m_block_poly.emplace_back(bbmax.x, bbmax.y);
186  block->m_block_poly.emplace_back(bbmax.x, bbmin.y);
187 
188  block->updateMaxRadiusFromPoly();
189  }
190 
191  // Shape node (optional, fallback to default shape if none found)
192  if (const rapidxml::xml_node<char>* xml_shape =
193  block_root_node.first_node("shape");
194  xml_shape)
195  {
197  *xml_shape, block->m_block_poly, "[Block::factory]");
198  block->updateMaxRadiusFromPoly();
199  }
200 
201  // Register bodies, fixtures, etc. in Box2D simulator:
202  // ----------------------------------------------------
203  block->create_multibody_system(*parent->getBox2DWorld());
204 
205  if (block->m_b2d_body)
206  {
207  // Init pos:
208  const auto q = block->getPose();
209  const auto dq = block->getTwist();
210 
211  block->m_b2d_body->SetTransform(b2Vec2(q.x, q.y), q.yaw);
212  // Init vel:
213  block->m_b2d_body->SetLinearVelocity(b2Vec2(dq.vx, dq.vy));
214  block->m_b2d_body->SetAngularVelocity(dq.omega);
215  }
216 
217  return block;
218 }
219 
220 Block::Ptr Block::factory(World* parent, const std::string& xml_text)
221 {
222  // Parse the string as if it was an XML file:
223  std::stringstream s;
224  s.str(xml_text);
225 
226  char* input_str = const_cast<char*>(xml_text.c_str());
228  try
229  {
230  xml.parse<0>(input_str);
231  }
232  catch (rapidxml::parse_error& e)
233  {
234  unsigned int line =
235  static_cast<long>(std::count(input_str, e.where<char>(), '\n') + 1);
236  throw std::runtime_error(mrpt::format(
237  "[Block::factory] XML parse error (Line %u): %s",
238  static_cast<unsigned>(line), e.what()));
239  }
240  return Block::factory(parent, xml.first_node());
241 }
242 
244 {
246 }
247 
251 {
253 }
254 
256 {
257  return mrpt::poses::CPose3D(getPose());
258 }
259 
261  mrpt::opengl::COpenGLScene& scene, bool childrenOnly)
262 {
263  auto lck = mrpt::lockHelper(m_gui_mtx);
264 
265  // 1st time call?? -> Create objects
266  // ----------------------------------
267  if (!childrenOnly)
268  {
269  if (!m_gl_block)
270  {
271  m_gl_block = mrpt::opengl::CSetOfObjects::Create();
272 
273  // Block shape:
276  gl_poly->setLocation(0, 0, m_block_z_min);
277  gl_poly->setColor_u8(m_block_color);
278  m_gl_block->insert(gl_poly);
279 
280  scene.insert(m_gl_block);
281  }
282 
283  // Update them:
284  m_gl_block->setPose(getPose());
285  }
286 
287  if (!m_gl_forces)
288  {
289  // Visualization of forces:
291  m_gl_forces->setLineWidth(3.0);
292  m_gl_forces->setColor_u8(0xff, 0xff, 0xff);
293 
294  scene.insert(m_gl_forces); // forces are in global coords
295  }
296 
297  // Other common stuff:
299 }
300 
302 {
304  {
305  std::lock_guard<std::mutex> csl(m_force_segments_for_rendering_cs);
306  m_gl_forces->clear();
308  m_gl_forces->setVisibility(true);
309  }
310  else
311  {
312  m_gl_forces->setVisibility(false);
313  }
314 }
315 
317 {
318  using namespace mrpt::math;
319 
320  m_max_radius = 0.001f;
321  for (const auto& segment : m_block_poly)
322  {
323  const float n = segment.norm();
324  mrpt::keep_max(m_max_radius, n);
325  }
326 }
327 
330 {
331  // Define the dynamic body. We set its position and call the body factory.
332  b2BodyDef bodyDef;
333  bodyDef.type = b2_dynamicBody;
334 
335  m_b2d_body = world.CreateBody(&bodyDef);
336 
337  // Define shape of block:
338  // ------------------------------
339  {
340  // Convert shape into Box2D format:
341  const size_t nPts = m_block_poly.size();
342  ASSERT_(nPts >= 3);
343  ASSERT_LE_(nPts, (size_t)b2_maxPolygonVertices);
344  std::vector<b2Vec2> pts(nPts);
345  for (size_t i = 0; i < nPts; i++)
346  pts[i] = b2Vec2(m_block_poly[i].x, m_block_poly[i].y);
347 
348  b2PolygonShape blockPoly;
349  blockPoly.Set(&pts[0], nPts);
350  // blockPoly.m_radius = 1e-3; // The "skin" depth of the body
351 
352  // Define the dynamic body fixture.
353  b2FixtureDef fixtureDef;
354  fixtureDef.shape = &blockPoly;
355  fixtureDef.restitution = m_restitution;
356 
357  // Set the box density to be non-zero, so it will be dynamic.
359  blockPoly.ComputeMass(&mass, 1); // Mass with density=1 => compute area
360  fixtureDef.density = m_mass / mass.mass;
361 
362  // Override the default friction.
363  fixtureDef.friction = m_lateral_friction; // 0.3f;
364 
365  // Add the shape to the body.
366  m_fixture_block = m_b2d_body->CreateFixture(&fixtureDef);
367 
368  // Compute center of mass:
369  b2MassData vehMass;
370  m_fixture_block->GetMassData(&vehMass);
371  m_block_com.x = vehMass.center.x;
372  m_block_com.y = vehMass.center.y;
373  }
374 
375  // Create "archor points" to simulate friction with the ground:
376  // -----------------------------------------------------------------
377  const size_t nContactPoints = 2;
378  const double weight_per_contact_point =
379  m_mass * getWorldObject()->get_gravity() / nContactPoints;
380  const double mu = m_ground_friction;
381  const double max_friction = mu * weight_per_contact_point;
382 
383  // Location (local coords) of each contact-point:
384  const mrpt::math::TPoint2D pt_loc[nContactPoints] = {
387 
388  b2FrictionJointDef fjd;
389 
391  fjd.bodyB = m_b2d_body;
392 
393  for (size_t i = 0; i < nContactPoints; i++)
394  {
395  const b2Vec2 local_pt = b2Vec2(pt_loc[i].x, pt_loc[i].y);
396 
397  fjd.localAnchorA = m_b2d_body->GetWorldPoint(local_pt);
398  fjd.localAnchorB = local_pt;
399  fjd.maxForce = max_friction;
400  fjd.maxTorque = 0;
401 
402  b2FrictionJoint* b2_friction = dynamic_cast<b2FrictionJoint*>(
403  m_world->getBox2DWorld()->CreateJoint(&fjd));
404  m_friction_joints.push_back(b2_friction);
405  }
406 }
407 
409  const mrpt::math::TVector2D& force, const mrpt::math::TPoint2D& applyPoint)
410 {
411  ASSERT_(m_b2d_body);
412  // Application point -> world coords
413  const b2Vec2 wPt =
414  m_b2d_body->GetWorldPoint(b2Vec2(applyPoint.x, applyPoint.y));
415  m_b2d_body->ApplyForce(b2Vec2(force.x, force.y), wPt, true /*wake up*/);
416 }
417 
418 bool Block::isStatic() const
419 {
420  ASSERT_(m_b2d_body);
421  return m_b2d_body->GetType() == b2_staticBody;
422 }
423 
425 {
426  ASSERT_(m_b2d_body);
427  m_b2d_body->SetType(b ? b2_staticBody : b2_dynamicBody);
428 }
const b2Shape * shape
Definition: b2Fixture.h:71
double m_block_z_max
Definition: Block.h:138
This file contains rapidxml parser and DOM implementation.
GLint GLint GLint GLint GLint GLint y
virtual mrpt::poses::CPose3D internalGuiGetVisualPose() override
Definition: Block.cpp:255
Ch * where() const
Definition: rapidxml.hpp:94
b2Vec2 localAnchorA
The local anchor point relative to bodyA&#39;s origin.
Ch * value() const
Definition: rapidxml.hpp:692
mrpt::math::TPoint2D m_block_com
In local coordinates.
Definition: Block.h:140
double m_max_radius
Definition: Block.h:136
void parse_xmlnode_children_as_param(const rapidxml::xml_node< char > &xml_node, const TParameterDefinitions &params, const std::map< std::string, std::string > &variableNamesValues={}, const char *functionNameContext="")
Definition: xml_utils.cpp:179
void updateMaxRadiusFromPoly()
Definition: Block.cpp:316
mrpt::opengl::CSetOfObjects::Ptr m_gl_block
Definition: Block.h:163
#define THROW_EXCEPTION(msg)
b2Vec2 localAnchorB
The local anchor point relative to bodyB&#39;s origin.
virtual void apply_force(const mrpt::math::TVector2D &force, const mrpt::math::TPoint2D &applyPoint=mrpt::math::TPoint2D(0, 0)) override
Definition: Block.cpp:408
float32 maxForce
The maximum friction force in N.
#define M_PI
static Ptr factory(World *parent, const rapidxml::xml_node< char > *xml_node)
Definition: Block.cpp:68
static void register_block_class(const rapidxml::xml_node< char > *xml_node)
Definition: Block.cpp:48
mrpt::math::TPose3D getPose() const
Definition: Simulable.h:55
static CSetOfLinesPtr Create(const std::vector< mrpt::math::TSegment3D > &sgms, const bool antiAliasing=true)
void parse(Ch *text)
Definition: rapidxml.hpp:1381
std::unique_ptr< b2World > & getBox2DWorld()
Definition: World.h:189
void setIsStatic(bool b)
Definition: Block.cpp:424
b2Vec2 center
The position of the shape&#39;s centroid relative to the shape&#39;s origin.
Definition: b2Shape.h:33
xml_node< Ch > * first_node(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const
Definition: rapidxml.hpp:936
A 2D column vector.
Definition: b2Math.h:52
void insert(const CRenderizablePtr &newObject, const std::string &viewportName=std::string("main"))
std::shared_ptr< Block > Ptr
Definition: Block.h:36
GLdouble s
Friction joint definition.
double get_gravity() const
Definition: World.h:82
double m_lateral_friction
Default: 0.5.
Definition: Block.h:142
GLsizei n
b2BodyType type
Definition: b2Body.h:74
void ComputeMass(b2MassData *massData, float32 density) const
float32 mass
The mass of the shape, usually in kilograms.
Definition: b2Shape.h:30
static CPolyhedronPtr CreateCustomPrism(const std::vector< mrpt::math::TPoint2D > &baseVertices, double height)
World * getWorldObject()
Definition: VisualObject.h:34
GLint GLint GLint GLint GLint x
Ch * name() const
Definition: rapidxml.hpp:673
double m_mass
Definition: Block.h:133
std::string BASE_IMPEXP format(const char *fmt,...) MRPT_printf_format_check(1
virtual void create_multibody_system(b2World &world)
Definition: Block.cpp:329
mrpt::opengl::CSetOfLines::Ptr m_gl_forces
Definition: Block.h:164
float32 maxTorque
The maximum friction torque in N-m.
TGUI_Options m_gui_options
Definition: World.h:311
virtual const char * what() const
Definition: rapidxml.hpp:85
xml_attribute< Ch > * first_attribute(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const
Definition: rapidxml.hpp:1025
#define b2_maxPolygonVertices
Definition: b2Settings.h:54
double m_ground_friction
Default: 0.5.
Definition: Block.h:143
void GetMassData(b2MassData *massData) const
Definition: b2Fixture.h:334
void rotate(const double ang)
std::mutex m_force_segments_for_rendering_cs
Definition: Block.h:165
float32 density
The density, usually in kg/m^2.
Definition: b2Fixture.h:83
virtual void simul_post_timestep(const TSimulContext &context)
Definition: Simulable.cpp:38
std::vector< mrpt::math::TSegment3D > m_force_segments_for_rendering
Definition: Block.h:166
const rapidxml::xml_node< Ch > * first_node(const char *name)
Definition: JointXMLnode.h:28
virtual void simul_pre_timestep(const TSimulContext &context) override
Definition: Block.cpp:243
float32 y
Definition: b2Math.h:139
mrpt::math::TPolygon2D m_block_poly
Definition: Block.h:135
virtual void simul_pre_timestep(const TSimulContext &context)
Definition: Simulable.cpp:25
double m_block_z_min
each change via updateMaxRadiusFromPoly()
Definition: Block.h:138
virtual void internalGuiUpdate(mrpt::opengl::COpenGLScene &scene, bool childrenOnly) override
Definition: Block.cpp:260
void internal_internalGuiUpdate_forces(mrpt::opengl::COpenGLScene &scene)
Definition: Block.cpp:301
void Set(const b2Vec2 *points, int32 count)
bool isStatic() const
Definition: Block.cpp:418
double mass() const
Definition: Block.h:92
b2Body * getBox2DGroundBody()
Definition: World.h:194
#define ASSERT_(f)
virtual void simul_post_timestep(const TSimulContext &context) override
Definition: Block.cpp:250
mrpt::img::TColor m_block_color
Definition: Block.h:139
float32 restitution
The restitution (elasticity) usually in the range [0,1].
Definition: b2Fixture.h:80
const rapidxml::xml_node< char > * get(const std::string &xml_node_class) const
string ss
void parse_xmlnode_shape(const rapidxml::xml_node< char > &xml_node, mrpt::math::TPolygon2D &out_poly, const char *functionNameContext="")
Definition: xml_utils.cpp:222
This file contains rapidxml printer implementation.
GLdouble GLdouble GLdouble b
void add(const std::string &input_xml_node_class)
GLdouble GLdouble GLdouble GLdouble q
double m_restitution
Deault: 0.01.
Definition: Block.h:144
float32 x
Definition: b2Math.h:139
Block(World *parent)
Definition: Block.cpp:36
b2Fixture * m_fixture_block
Definition: Block.h:158
b2Body * bodyA
The first attached body.
Definition: b2Joint.h:92
This holds the mass data computed for a shape.
Definition: b2Shape.h:27
std::mutex m_gui_mtx
Definition: Block.h:168
void add(const rapidxml::xml_node< Ch > *node)
Definition: JointXMLnode.h:27
static XmlClassesRegistry block_classes_registry("block:class")
float32 friction
The friction coefficient, usually in the range [0,1].
Definition: b2Fixture.h:77
b2Body * CreateBody(const b2BodyDef *def)
Definition: b2World.cpp:107
std::vector< b2FrictionJoint * > m_friction_joints
Definition: Block.h:130
b2Body * bodyB
The second attached body.
Definition: b2Joint.h:95


mvsim
Author(s):
autogenerated on Fri May 7 2021 03:05:51