JSim 2026.06.01-p(1)
Loading...
Searching...
No Matches
rigid_body.hpp
Go to the documentation of this file.
1// Copyright (c) JSim contributors.
2// Open Source Software; you can modify and/or share it under the terms of
3// the LGPLv3 license file in the root directory of this project.
4
5#pragma once
6
7#include <algorithm>
8#include <cmath>
9#include <cstdint>
10#include <optional>
11
18
19namespace frcsim {
20
30
44class RigidBody {
45 public:
47 enum class CylinderAxis {
54 };
55
75
78
84 double reference_area_m2{0.0};
85
87 double radius_m{0.0};
88
90 Vector3 box_dimensions_m{0.0, 0.0, 0.0};
91
93 double cylinder_length_m{0.0};
94
97 };
98
103 explicit RigidBody(double mass_kg = 1.0) { setMassKg(mass_kg); }
104
106 double massKg() const { return mass_kg_; }
108 double inverseMass() const { return inv_mass_; }
113 void setMassKg(double mass_kg) {
114 mass_kg_ = (mass_kg > 0.0) ? mass_kg : 1.0;
115 inv_mass_ = 1.0 / mass_kg_;
116 }
117
119 const Vector3& position() const { return position_m_; }
124 void setPosition(const Vector3& position_m) { position_m_ = position_m; }
131 void setPosition(double x_m, double y_m, double z_m) {
132 setPosition(Vector3{x_m, y_m, z_m});
133 }
134
140 void position(double* x_m, double* y_m, double* z_m) const {
141 if (x_m) {
142 *x_m = position_m_.x;
143 }
144 if (y_m) {
145 *y_m = position_m_.y;
146 }
147 if (z_m) {
148 *z_m = position_m_.z;
149 }
150 }
151
153 const Quaternion& orientation() const { return orientation_; }
159 orientation_ = orientation;
160 }
161
163 const Vector3& linearVelocity() const { return linear_velocity_mps_; }
168 void setLinearVelocity(const Vector3& velocity_mps) {
169 linear_velocity_mps_ = velocity_mps;
170 }
171
177 void setLinearVelocity(double vx_mps, double vy_mps, double vz_mps) {
178 setLinearVelocity(Vector3{vx_mps, vy_mps, vz_mps});
179 }
180
186 void linearVelocity(double* vx_mps, double* vy_mps, double* vz_mps) const {
187 if (vx_mps) {
188 *vx_mps = linear_velocity_mps_.x;
189 }
190 if (vy_mps) {
191 *vy_mps = linear_velocity_mps_.y;
192 }
193 if (vz_mps) {
194 *vz_mps = linear_velocity_mps_.z;
195 }
196 }
197
199 const Vector3& angularVelocity() const { return angular_velocity_radps_; }
205 void setAngularVelocity(const Vector3& angular_velocity_radps) {
206 angular_velocity_radps_ = angular_velocity_radps;
207 }
208
214 void setAngularVelocity(double wx_radps, double wy_radps, double wz_radps) {
215 setAngularVelocity(Vector3{wx_radps, wy_radps, wz_radps});
216 }
217
223 void angularVelocity(double* wx_radps, double* wy_radps, double* wz_radps) const {
224 if (wx_radps) {
225 *wx_radps = angular_velocity_radps_.x;
226 }
227 if (wy_radps) {
228 *wy_radps = angular_velocity_radps_.y;
229 }
230 if (wz_radps) {
231 *wz_radps = angular_velocity_radps_.z;
232 }
233 }
234
236 const Matrix3& bodyInertiaTensor() const { return body_inertia_tensor_; }
238 void setBodyInertiaTensor(const Matrix3& inertia) {
239 body_inertia_tensor_ = inertia;
240 inv_body_inertia_tensor_ = inertia.inverse();
241 }
242
244 BodyFlags& flags() { return flags_; }
246 const BodyFlags& flags() const { return flags_; }
247
249 void setMaterial(const Material& material) { material_ = material; }
251 const Material* material() const {
252 return material_ ? &(*material_) : nullptr;
253 }
254
259 void setMaterialId(std::int32_t material_id) { material_id_ = material_id; }
260
265 std::int32_t materialId() const { return material_id_; }
266
271 void setCollisionLayer(std::uint32_t layer_bits) { collision_layer_bits_ = layer_bits; }
272
277 std::uint32_t collisionLayer() const { return collision_layer_bits_; }
278
283 void setCollisionMask(std::uint32_t mask_bits) { collision_mask_bits_ = mask_bits; }
284
289 std::uint32_t collisionMask() const { return collision_mask_bits_; }
290
293 aerodynamic_geometry_ = geometry;
294 }
295
297 return aerodynamic_geometry_ ? &(*aerodynamic_geometry_) : nullptr;
298 }
299
304 if (!aerodynamic_geometry_) {
305 aerodynamic_geometry_ = AerodynamicGeometry{};
306 }
307
308 switch (axis) {
309 case CylinderAxis::kX:
310 aerodynamic_geometry_->cylinder_axis_local = Vector3::unitX();
311 break;
312 case CylinderAxis::kY:
313 aerodynamic_geometry_->cylinder_axis_local = Vector3::unitY();
314 break;
315 case CylinderAxis::kZ:
316 default:
317 aerodynamic_geometry_->cylinder_axis_local = Vector3::unitZ();
318 break;
319 }
320 }
321
329 void setCylinderAxisWorld(const Vector3& axis_world) {
330 if (!aerodynamic_geometry_) {
331 aerodynamic_geometry_ = AerodynamicGeometry{};
332 }
333
334 Vector3 axis_local = orientation_.inverse().rotate(axis_world).normalized();
335 if (axis_local.isZero()) {
336 axis_local = Vector3::unitZ();
337 }
338
339 aerodynamic_geometry_->cylinder_axis_local = axis_local;
340 }
341
348 void setCylinderAxisWorld(double x, double y, double z) {
350 }
351
367 double dragReferenceAreaM2(const Vector3& velocity_world) const {
368 if (!aerodynamic_geometry_)
369 return 0.0;
370
371 const AerodynamicGeometry& geometry = *aerodynamic_geometry_;
372 if (geometry.reference_area_m2 > 0.0)
373 return geometry.reference_area_m2;
374
375 switch (geometry.shape) {
377 const double radius_m = std::max(0.0, geometry.radius_m);
378 return 3.14159265358979323846 * radius_m * radius_m;
379 }
381 if (geometry.box_dimensions_m.x <= 0.0 ||
382 geometry.box_dimensions_m.y <= 0.0 ||
383 geometry.box_dimensions_m.z <= 0.0) {
384 return 0.0;
385 }
386
387 Vector3 velocity_direction = velocity_world.isZero()
389 : velocity_world.normalized();
390 velocity_direction = orientation_.inverse().rotate(velocity_direction);
391
392 const Vector3 dims = geometry.box_dimensions_m;
393 return std::abs(velocity_direction.x) * dims.y * dims.z +
394 std::abs(velocity_direction.y) * dims.x * dims.z +
395 std::abs(velocity_direction.z) * dims.x * dims.y;
396 }
398 const double radius_m = std::max(0.0, geometry.radius_m);
399 const double length_m = std::max(0.0, geometry.cylinder_length_m);
400 if (radius_m <= 0.0 || length_m <= 0.0) {
401 return 0.0;
402 }
403
404 const Vector3 velocity_direction_world =
405 velocity_world.isZero() ? Vector3::unitX()
406 : velocity_world.normalized();
407 Vector3 cylinder_axis_local = geometry.cylinder_axis_local.normalized();
408 if (cylinder_axis_local.isZero()) {
409 cylinder_axis_local = Vector3::unitZ();
410 }
411
412 const Vector3 velocity_direction_local =
413 orientation_.inverse().rotate(velocity_direction_world);
414 const double axis_alignment =
415 std::abs(velocity_direction_local.dot(cylinder_axis_local));
416 const double side_alignment =
417 std::sqrt(std::max(0.0, 1.0 - axis_alignment * axis_alignment));
418
419 const double endcap_area_m2 =
420 3.14159265358979323846 * radius_m * radius_m * axis_alignment;
421 const double side_area_m2 = 2.0 * radius_m * length_m * side_alignment;
422 return endcap_area_m2 + side_area_m2;
423 }
425 default:
426 return 0.0;
427 }
428 }
429
431 void applyForce(const Vector3& force_n) { accumulated_force_n_ += force_n; }
438 void applyForce(double fx_n, double fy_n, double fz_n) {
439 applyForce(Vector3{fx_n, fy_n, fz_n});
440 }
441
447 void applyForceAtPoint(const Vector3& force_n, const Vector3& world_point_m) {
448 applyForce(force_n);
449 const Vector3 r = world_point_m - position_m_;
450 accumulated_torque_nm_ += r.cross(force_n);
451 }
452
462 void applyForceAtPoint(double fx_n, double fy_n, double fz_n, double px_m,
463 double py_m, double pz_m) {
464 applyForceAtPoint(Vector3{fx_n, fy_n, fz_n}, Vector3{px_m, py_m, pz_m});
465 }
466
469 accumulated_force_n_ = Vector3::zero();
470 accumulated_torque_nm_ = Vector3::zero();
471 }
472
490 void integrate(double dt_s, IntegrationMethod method,
491 const Vector3& gravity_mps2, double linear_damping_per_s,
492 double angular_damping_per_s) {
493 if (flags_.is_kinematic) {
495 return;
496 }
497
498 Vector3 total_force = accumulated_force_n_;
499 if (flags_.enable_gravity) {
500 total_force += gravity_mps2 * mass_kg_;
501 }
502
503 const Vector3 linear_accel = total_force * inv_mass_;
504 switch (method) {
506 Integrator::integrateLinearExplicit(position_m_, linear_velocity_mps_,
507 linear_accel, dt_s);
508 break;
510 Integrator::integrateLinearRK2(position_m_, linear_velocity_mps_,
511 linear_accel, dt_s);
512 break;
514 default:
515 Integrator::integrateLinear(position_m_, linear_velocity_mps_,
516 linear_accel, dt_s);
517 break;
518 }
519
520 const double linear_damp = std::max(0.0, 1.0 - linear_damping_per_s * dt_s);
521 linear_velocity_mps_ *= linear_damp;
522
523 const Vector3 angular_accel =
524 inv_body_inertia_tensor_ * accumulated_torque_nm_;
525 Integrator::integrateAngularVelocity(angular_velocity_radps_, angular_accel,
526 dt_s);
527 Integrator::integrateAngular(orientation_, angular_velocity_radps_, dt_s);
528
529 const double angular_damp =
530 std::max(0.0, 1.0 - angular_damping_per_s * dt_s);
531 angular_velocity_radps_ *= angular_damp;
532
534 }
535
536 private:
537 double mass_kg_{1.0};
538 double inv_mass_{1.0};
539
540 Vector3 position_m_{};
541 Quaternion orientation_{};
542 Vector3 linear_velocity_mps_{};
543 Vector3 angular_velocity_radps_{};
544
545 Vector3 accumulated_force_n_{};
546 Vector3 accumulated_torque_nm_{};
547
548 Matrix3 body_inertia_tensor_{Matrix3::identity()};
549 Matrix3 inv_body_inertia_tensor_{Matrix3::identity()};
550
551 BodyFlags flags_{};
552 std::optional<Material> material_{};
553 std::int32_t material_id_{0};
554 std::uint32_t collision_layer_bits_{0xFFFFFFFFu};
555 std::uint32_t collision_mask_bits_{0xFFFFFFFFu};
556 std::optional<AerodynamicGeometry> aerodynamic_geometry_{};
557};
558
559} // namespace frcsim
void applyForce(const Vector3 &force_n)
Adds force at center of mass in newtons.
Definition rigid_body.hpp:431
void setPosition(double x_m, double y_m, double z_m)
Sets world-space position in meters from scalar components.
Definition rigid_body.hpp:131
void applyForce(double fx_n, double fy_n, double fz_n)
Adds force at center of mass in newtons from scalar components.
Definition rigid_body.hpp:438
void clearAccumulators()
Clears accumulated force and torque buffers.
Definition rigid_body.hpp:468
void setAerodynamicGeometry(const AerodynamicGeometry &geometry)
Sets optional aerodynamic geometry metadata.
Definition rigid_body.hpp:292
void setAngularVelocity(const Vector3 &angular_velocity_radps)
Sets world angular velocity in radians per second.
Definition rigid_body.hpp:205
void setBodyInertiaTensor(const Matrix3 &inertia)
Sets body-frame inertia tensor and cached inverse tensor.
Definition rigid_body.hpp:238
void linearVelocity(double *vx_mps, double *vy_mps, double *vz_mps) const
Gets world linear velocity in meters per second via pointers.
Definition rigid_body.hpp:186
void applyForceAtPoint(const Vector3 &force_n, const Vector3 &world_point_m)
Adds force at world-space point, accumulating both force and torque.
Definition rigid_body.hpp:447
const Vector3 & angularVelocity() const
Returns world angular velocity in radians per second.
Definition rigid_body.hpp:199
std::uint32_t collisionMask() const
Gets broad-phase collision mask bits.
Definition rigid_body.hpp:289
void angularVelocity(double *wx_radps, double *wy_radps, double *wz_radps) const
Gets world angular velocity in radians per second via pointers.
Definition rigid_body.hpp:223
const Material * material() const
Gets material if configured, otherwise returns null.
Definition rigid_body.hpp:251
void position(double *x_m, double *y_m, double *z_m) const
Gets world-space position in meters via scalar output pointers.
Definition rigid_body.hpp:140
const AerodynamicGeometry * aerodynamicGeometry() const
Gets aerodynamic geometry if configured, otherwise returns null.
Definition rigid_body.hpp:296
CylinderAxis
Principal axis selection for cylindrical aerodynamic geometry.
Definition rigid_body.hpp:47
@ kX
Local +X axis.
Definition rigid_body.hpp:49
@ kY
Local +Y axis.
Definition rigid_body.hpp:51
@ kZ
Local +Z axis.
Definition rigid_body.hpp:53
const BodyFlags & flags() const
Const access to runtime body flags.
Definition rigid_body.hpp:246
double dragReferenceAreaM2(const Vector3 &velocity_world) const
Computes effective drag reference area for current geometry and motion direction.
Definition rigid_body.hpp:367
void setMassKg(double mass_kg)
Sets mass in kilograms.
Definition rigid_body.hpp:113
BodyFlags & flags()
Mutable access to runtime body flags.
Definition rigid_body.hpp:244
void integrate(double dt_s, IntegrationMethod method, const Vector3 &gravity_mps2, double linear_damping_per_s, double angular_damping_per_s)
Advances translational and rotational state by one fixed timestep.
Definition rigid_body.hpp:490
const Matrix3 & bodyInertiaTensor() const
Returns inertia tensor in body frame.
Definition rigid_body.hpp:236
const Vector3 & linearVelocity() const
Returns world linear velocity in meters per second.
Definition rigid_body.hpp:163
void setMaterial(const Material &material)
Sets optional physical material properties.
Definition rigid_body.hpp:249
void setCollisionLayer(std::uint32_t layer_bits)
Sets broad-phase collision layer bits for this body.
Definition rigid_body.hpp:271
double massKg() const
Returns mass in kilograms.
Definition rigid_body.hpp:106
void setLinearVelocity(double vx_mps, double vy_mps, double vz_mps)
Sets world linear velocity in meters per second from scalars.
Definition rigid_body.hpp:177
void setCylinderAxisWorld(const Vector3 &axis_world)
Sets cylinder axis from world-space direction.
Definition rigid_body.hpp:329
void setLinearVelocity(const Vector3 &velocity_mps)
Sets world linear velocity in meters per second.
Definition rigid_body.hpp:168
void setOrientation(const Quaternion &orientation)
Sets body orientation quaternion.
Definition rigid_body.hpp:158
void setCollisionMask(std::uint32_t mask_bits)
Sets broad-phase collision mask bits for this body.
Definition rigid_body.hpp:283
std::int32_t materialId() const
Gets numeric material identifier used by world interaction tables.
Definition rigid_body.hpp:265
std::uint32_t collisionLayer() const
Gets broad-phase collision layer bits.
Definition rigid_body.hpp:277
void applyForceAtPoint(double fx_n, double fy_n, double fz_n, double px_m, double py_m, double pz_m)
Adds force at world-space point from scalar components.
Definition rigid_body.hpp:462
void setCylinderAxisLocal(CylinderAxis axis)
Sets cylinder axis in local coordinates via canonical enum axis.
Definition rigid_body.hpp:303
void setCylinderAxisWorld(double x, double y, double z)
Sets cylinder axis from world-space scalar direction components.
Definition rigid_body.hpp:348
void setMaterialId(std::int32_t material_id)
Sets numeric material identifier used by world interaction tables.
Definition rigid_body.hpp:259
void setAngularVelocity(double wx_radps, double wy_radps, double wz_radps)
Sets world angular velocity in radians per second from scalars.
Definition rigid_body.hpp:214
const Quaternion & orientation() const
Returns body orientation as a unit quaternion.
Definition rigid_body.hpp:153
double inverseMass() const
Returns inverse mass in 1/kg.
Definition rigid_body.hpp:108
RigidBody(double mass_kg=1.0)
Constructs a rigid body with a sanitized positive mass.
Definition rigid_body.hpp:103
void setPosition(const Vector3 &position_m)
Sets world-space position in meters.
Definition rigid_body.hpp:124
const Vector3 & position() const
Returns world position in meters.
Definition rigid_body.hpp:119
Definition vector.hpp:13
IntegrationMethod
Integration strategy used for rigid body state updates.
Definition rigid_body.hpp:22
@ kExplicitEuler
Position-first explicit Euler integration.
Definition rigid_body.hpp:26
@ kSemiImplicitEuler
Velocity-first (symplectic) Euler integration.
Definition rigid_body.hpp:24
@ kRK2
Second-order midpoint (RK2) integration.
Definition rigid_body.hpp:28
Definition body_flags.hpp:9
static void integrateAngular(Quaternion &orientation, const Vector3 &angularVelocity, double dt) noexcept
Definition integrators.hpp:53
static void integrateLinearExplicit(Vector3 &position, Vector3 &velocity, const Vector3 &acceleration, double dt) noexcept
Definition integrators.hpp:41
static void integrateLinear(Vector3 &position, Vector3 &velocity, const Vector3 &acceleration, double dt) noexcept
Definition integrators.hpp:31
static void integrateLinearRK2(Vector3 &position, Vector3 &velocity, const Vector3 &acceleration, double dt) noexcept
Definition integrators.hpp:74
static void integrateAngularVelocity(Vector3 &angularVelocity, const Vector3 &angularAcceleration, double dt) noexcept
Definition integrators.hpp:65
Contact material parameters for collision response.
Definition material.hpp:20
3x3 matrix type used for rotations and inertia-tensor operations.
Definition matrix.hpp:27
static constexpr Matrix3 identity() noexcept
Definition matrix.hpp:38
Matrix3 inverse() const noexcept
Definition matrix.hpp:154
Unit-quaternion rotation representation and rotation algebra helpers.
Definition quaternion.hpp:25
Geometric metadata used to estimate drag reference area.
Definition rigid_body.hpp:63
Shape
Primitive shape model used for reference-area estimation.
Definition rigid_body.hpp:65
@ kBox
Box model parameterized by box_dimensions_m.
Definition rigid_body.hpp:71
@ kCustom
Use only explicit caller-provided area (no shape inference).
Definition rigid_body.hpp:67
@ kCylinder
Cylinder model parameterized by radius, length, and axis.
Definition rigid_body.hpp:73
@ kSphere
Sphere model parameterized by radius_m.
Definition rigid_body.hpp:69
double reference_area_m2
Explicit area override in square meters.
Definition rigid_body.hpp:84
double radius_m
Sphere/cylinder radius in meters.
Definition rigid_body.hpp:87
Vector3 box_dimensions_m
Box dimensions (x, y, z) in meters.
Definition rigid_body.hpp:90
Shape shape
Selected shape model.
Definition rigid_body.hpp:77
Vector3 cylinder_axis_local
Cylinder principal axis expressed in local body coordinates.
Definition rigid_body.hpp:96
double cylinder_length_m
Cylinder length in meters along cylinder_axis_local.
Definition rigid_body.hpp:93
3D vector utility used throughout JSim physics.
Definition vector.hpp:22
static constexpr Vector3 unitY() noexcept
Returns the +Y basis vector.
Definition vector.hpp:394
Vector3 normalized() const noexcept
Returns the normalized direction vector.
Definition vector.hpp:119
Vector3 cross(const Vector3 &o) const noexcept
Returns cross product with o.
Definition vector.hpp:172
double z
Z component.
Definition vector.hpp:28
static constexpr Vector3 unitX() noexcept
Returns the +X basis vector.
Definition vector.hpp:392
static constexpr Vector3 unitZ() noexcept
Returns the +Z basis vector.
Definition vector.hpp:396
constexpr double dot(const Vector3 &o) const noexcept
Returns dot product with o.
Definition vector.hpp:168
static constexpr Vector3 zero() noexcept
Returns the zero vector.
Definition vector.hpp:360
double x
X component.
Definition vector.hpp:24
constexpr bool isZero(double eps=1e-12) const noexcept
Checks whether the vector is near zero magnitude.
Definition vector.hpp:128
double y
Y component.
Definition vector.hpp:26