pub struct MassProperties {
pub local_com: Point<f32>,
pub inv_mass: f32,
pub inv_principal_inertia: AngVector<f32>,
}Expand description
The mass properties of a rigid body.
Mass properties define how an object responds to forces and torques in physics simulation. They include the mass, center of mass, and angular inertia (resistance to rotation).
§Fields
- local_com: Center of mass in the shape’s local coordinate system
- inv_mass: Inverse mass (1/mass). Zero = infinite mass (immovable)
- inv_principal_inertia: Inverse angular inertia along principal axes
- principal_inertia_local_frame (3D only): Rotation to principal inertia axes
§Why Inverse Values?
Physics engines store inverse mass and inertia because:
- Infinite mass/inertia (immovable objects) = zero inverse
- Avoids division in the simulation loop (multiply by inverse instead)
- More numerically stable for very heavy objects
§Angular Inertia
Angular inertia (moment of inertia) describes resistance to rotation:
- Higher values: Harder to spin (like a heavy wheel)
- Lower values: Easier to spin (like a light rod)
- Different per axis: Objects resist rotation differently around different axes
§Principal Inertia
The inertia tensor is diagonalized to principal axes:
- Rotation around principal axes is independent
- Simplifies physics calculations
- In 2D: Only one axis (perpendicular to the plane)
- In 3D: Three orthogonal axes
§Example
use parry3d::mass_properties::MassProperties;
use parry3d::shape::{Ball, Shape};
use nalgebra::Point3;
// Compute mass properties for a unit ball with density 1.0
let ball = Ball::new(1.0);
let props = ball.mass_properties(1.0);
// Mass of a unit sphere with density 1.0
let mass = props.mass();
println!("Mass: {}", mass);
// Center of mass (at origin for a ball)
assert_eq!(props.local_com, Point3::origin());
// For simulation, use inverse values
if props.inv_mass > 0.0 {
// Object has finite mass - can be moved
println!("Can apply forces");
} else {
// Object has infinite mass - immovable (like terrain)
println!("Static/kinematic object");
}§Combining Mass Properties
Mass properties can be added to create compound objects:
use parry3d::mass_properties::MassProperties;
use parry3d::shape::{Ball, Cuboid, Shape};
use nalgebra::Vector3;
let ball = Ball::new(1.0);
let cuboid = Cuboid::new(Vector3::new(1.0, 1.0, 1.0));
let ball_props = ball.mass_properties(1.0);
let cuboid_props = cuboid.mass_properties(1.0);
// Combined properties (ball + cuboid)
let combined = ball_props + cuboid_props;
// Total mass is sum of individual masses
let total_mass = combined.mass();
println!("Combined mass: {}", total_mass);Fields§
§local_com: Point<f32>The center of mass in local (shape-relative) coordinates.
This is the balance point of the object. For symmetric shapes, it’s typically at the geometric center. All angular inertia calculations are relative to this point.
inv_mass: f32The inverse of the mass (1 / mass).
- Positive value: Normal object with finite mass
- Zero: Infinite mass (immovable/static object)
To get the actual mass, use mass() method or compute 1.0 / inv_mass.
inv_principal_inertia: AngVector<f32>The inverse of the principal angular inertia values.
These are the angular inertia values along the principal inertia axes:
- 2D: Single scalar value (rotation around perpendicular axis)
- 3D: Vector of three values (rotation around X, Y, Z principal axes)
Angular inertia relative to the center of mass (local_com).
Zero components indicate infinite inertia (no rotation) along that axis.
Implementations§
Source§impl MassProperties
impl MassProperties
Sourcepub fn new(local_com: Point<f32>, mass: f32, principal_inertia: f32) -> Self
pub fn new(local_com: Point<f32>, mass: f32, principal_inertia: f32) -> Self
Creates mass properties from center of mass, mass, and angular inertia (2D).
§Arguments
local_com- Center of mass in local coordinatesmass- The mass (positive value, use 0.0 for infinite mass)principal_inertia- Angular inertia around the perpendicular axis
§Example (2D)
use parry2d::mass_properties::MassProperties;
use nalgebra::Point2;
// Create mass properties for a 10kg object
let props = MassProperties::new(
Point2::origin(), // Centered at origin
10.0, // 10kg mass
5.0 // Angular inertia
);
assert_eq!(props.mass(), 10.0);
assert_eq!(props.inv_mass, 0.1); // 1/10Sourcepub fn principal_inertia(&self) -> AngVector<f32>
pub fn principal_inertia(&self) -> AngVector<f32>
The angular inertia along the principal inertia axes and center of mass of the rigid-body.
Sourcepub fn world_com(&self, pos: &Isometry<f32>) -> Point<f32>
pub fn world_com(&self, pos: &Isometry<f32>) -> Point<f32>
The world-space center of mass of the rigid-body.
Sourcepub fn world_inv_inertia(&self, _rot: &Rotation<f32>) -> AngularInertia<f32>
pub fn world_inv_inertia(&self, _rot: &Rotation<f32>) -> AngularInertia<f32>
The world-space inverse angular inertia tensor of the rigid-body.
Sourcepub fn transform_by(&self, m: &Isometry<f32>) -> Self
pub fn transform_by(&self, m: &Isometry<f32>) -> Self
Transform each element of the mass properties.
Sourcepub fn set_mass(&mut self, new_mass: f32, adjust_angular_inertia: bool)
pub fn set_mass(&mut self, new_mass: f32, adjust_angular_inertia: bool)
Changes the mass on these mass-properties.
The adjust_angular_inertia argument should always be true, unless
there are some specific reasons not to do so. Setting this to true
will automatically adjust the angular inertia of self to account
for the mass change (i.e. it will multiply the angular inertia by
new_mass / prev_mass). Setting it to false will not change the
current angular inertia.
Source§impl MassProperties
impl MassProperties
Sourcepub fn from_ball(density: f32, radius: f32) -> Self
pub fn from_ball(density: f32, radius: f32) -> Self
Computes the mass properties of a ball (sphere in 3D, circle in 2D).
A ball is a perfectly round geometric shape defined by its radius. This function calculates the physical properties needed for physics simulation, including mass, center of mass, and angular inertia (resistance to rotation).
§Arguments
density- The material density (mass per unit volume/area). Higher values make heavier objects.- In 3D: units are typically kg/m³ (e.g., water = 1000, steel = 7850)
- In 2D: units are typically kg/m² (mass per unit area)
radius- The radius of the ball (distance from center to surface)
§Returns
A MassProperties struct containing:
- mass: Total mass calculated from volume and density
- local_com: Center of mass at the origin (balls are perfectly symmetric)
- inv_principal_inertia: Inverse angular inertia (resistance to spinning)
§Physics Background
Balls have uniform density and perfect symmetry, which means:
- The center of mass is at the geometric center (origin)
- All rotational axes have the same angular inertia (isotropic)
- In 3D: moment of inertia = (2/5) * mass * radius²
- In 2D: moment of inertia = (1/2) * mass * radius²
§Example (3D)
use parry3d::mass_properties::MassProperties;
use nalgebra::Point3;
// Create mass properties for a 0.5m radius ball with density 1000 kg/m³ (water density)
let radius = 0.5;
let density = 1000.0;
let ball_props = MassProperties::from_ball(density, radius);
// Volume of sphere: (4/3) * π * r³ = 0.524 m³
// Mass: volume * density = 524 kg
let mass = ball_props.mass();
assert!((mass - 523.6).abs() < 1.0); // Approximately 524 kg
// Center of mass is at the origin for symmetric shapes
assert_eq!(ball_props.local_com, Point3::origin());
// Check if object can be moved (finite mass)
assert!(ball_props.inv_mass > 0.0, "Ball has finite mass and can move");§Example (2D)
use parry2d::mass_properties::MassProperties;
use nalgebra::Point2;
// Create a circular disc with 1.0m radius and density 100 kg/m²
let radius = 1.0;
let density = 100.0;
let circle_props = MassProperties::from_ball(density, radius);
// Area of circle: π * r² = 3.14 m²
// Mass: area * density = 314 kg
let mass = circle_props.mass();
assert!((mass - 314.159).abs() < 0.1); // Approximately 314 kg
println!("Circle mass: {:.2} kg", mass);
println!("Moment of inertia: {:.2}", circle_props.principal_inertia());§Use Cases
- Sports balls: Soccer balls, basketballs, bowling balls
- Planets and celestial bodies: Spherical approximations
- Particles: Point-like objects with rotational inertia
- Wheels and gears: Cylindrical objects in 2D simulations
§Performance Note
This is a very fast computation (constant time) as it only involves basic arithmetic with the radius and density. Balls are the simplest shape for collision detection and physics simulation.
Source§impl MassProperties
impl MassProperties
Sourcepub fn from_capsule(
density: f32,
a: Point<f32>,
b: Point<f32>,
radius: f32,
) -> Self
pub fn from_capsule( density: f32, a: Point<f32>, b: Point<f32>, radius: f32, ) -> Self
Computes the mass properties of a capsule (pill-shaped object).
A capsule is a cylinder with hemispherical caps on both ends. It’s defined by two endpoint centers (a line segment) and a radius. Capsules are commonly used for character controllers and elongated objects because they provide smooth collision handling without sharp edges.
§Arguments
density- The material density (mass per unit volume/area). Higher values make heavier objects.- In 3D: units are typically kg/m³
- In 2D: units are typically kg/m² (mass per unit area)
a- First endpoint of the capsule’s central axis (center of first hemisphere)b- Second endpoint of the capsule’s central axis (center of second hemisphere)radius- The radius of the capsule (distance from the axis to the surface)
§Returns
A MassProperties struct containing:
- mass: Total mass calculated from volume and density
- local_com: Center of mass at the midpoint between
aandb - inv_principal_inertia: Inverse angular inertia (varies by axis)
- principal_inertia_local_frame (3D only): Rotation aligning capsule axis with Y
§Physics Background
A capsule consists of:
- A cylindrical body connecting the two endpoints
- Two hemispherical caps (which together form one complete sphere)
- The total length is:
distance(a, b) + 2 * radius - Mass and inertia are computed by combining cylinder + sphere components
§Example (3D) - Character Controller
use parry3d::mass_properties::MassProperties;
use nalgebra::Point3;
// Create a capsule for a standing character (height ~2m, radius 0.3m)
// Endpoints at (0, 0, 0) and (0, 2, 0) form vertical capsule
let a = Point3::origin();
let b = Point3::new(0.0, 2.0, 0.0);
let radius = 0.3;
let density = 985.0; // Similar to human body density
let character_props = MassProperties::from_capsule(density, a, b, radius);
// Center of mass is at the midpoint
let expected_com = Point3::new(0.0, 1.0, 0.0);
assert!((character_props.local_com - expected_com).norm() < 0.01);
let mass = character_props.mass();
println!("Character mass: {:.2} kg", mass); // Approximately 70-80 kg
// Inertia is higher around horizontal axes (harder to tip over)
let inertia = character_props.principal_inertia();
println!("Inertia X: {:.2}, Y: {:.2}, Z: {:.2}", inertia.x, inertia.y, inertia.z);§Example (3D) - Horizontal Capsule (Lying Down)
use parry3d::mass_properties::MassProperties;
use nalgebra::Point3;
// Create a horizontal capsule along the X-axis
let a = Point3::new(-1.0, 0.0, 0.0);
let b = Point3::new(1.0, 0.0, 0.0);
let radius = 0.5;
let density = 1000.0;
let capsule_props = MassProperties::from_capsule(density, a, b, radius);
// Center of mass at midpoint (origin)
assert_eq!(capsule_props.local_com, Point3::origin());
// Total length = distance + 2*radius = 2.0 + 1.0 = 3.0 meters
println!("Mass: {:.2} kg", capsule_props.mass());§Example (2D) - Stadium Shape
use parry2d::mass_properties::MassProperties;
use nalgebra::Point2;
// Create a horizontal 2D capsule (stadium/discorectangle shape)
let a = Point2::new(-2.0, 0.0);
let b = Point2::new(2.0, 0.0);
let radius = 1.0;
let density = 100.0; // kg/m²
let stadium_props = MassProperties::from_capsule(density, a, b, radius);
println!("Stadium mass: {:.2} kg", stadium_props.mass());
println!("Moment of inertia: {:.2}", stadium_props.principal_inertia());§Use Cases
- Character controllers: Humanoid characters, NPCs
- Vehicles: Simplified car or boat bodies
- Projectiles: Bullets, missiles, arrows
- Limbs: Arms, legs in ragdoll physics
- Cylinders with rounded ends: Pipes, rods, poles
§Common Mistakes
- Total length confusion: The visual length is
distance(a, b) + 2 * radius, not justdistance(a, b). The hemispheres add extra length. - Endpoint placement: Points
aandbare centers of the hemispherical caps, not the extreme ends of the capsule.
§Performance Note
Capsules are very efficient for collision detection (almost as fast as spheres) and provide smooth rolling behavior. They’re preferred over cylinders for dynamic objects that need to move smoothly.
Source§impl MassProperties
impl MassProperties
Sourcepub fn from_compound(
density: f32,
shapes: &[(Isometry<f32>, SharedShape)],
) -> Self
pub fn from_compound( density: f32, shapes: &[(Isometry<f32>, SharedShape)], ) -> Self
Computes the mass properties of a compound shape (combination of multiple shapes).
A compound shape is a collection of sub-shapes, each with its own position and orientation. This function computes the mass properties of each sub-shape, transforms them to their local positions, and combines them using the parallel axis theorem to get the total mass properties.
§Arguments
density- The material density applied to all sub-shapes- In 3D: kg/m³ (mass per unit volume)
- In 2D: kg/m² (mass per unit area)
shapes- Array of (position, shape) pairs- Each shape has an
Isometry(position + rotation) - Shapes can be any type implementing the
Shapetrait
- Each shape has an
§Returns
A MassProperties struct containing:
- mass: Sum of all sub-shape masses
- local_com: Combined center of mass (mass-weighted average)
- inv_principal_inertia: Combined angular inertia
§Physics Background
The parallel axis theorem is used to shift inertia tensors:
- Each shape’s mass properties are computed in its local frame
- Properties are transformed to the compound’s coordinate system
- Center of mass is the mass-weighted average of all sub-shapes
- Angular inertia accounts for both local rotation and offset from COM
§Example (3D) - Dumbbell
use parry3d::mass_properties::MassProperties;
use parry3d::shape::{Ball, SharedShape};
use nalgebra::{Isometry3, Vector3};
// Create a dumbbell: two balls connected by a bar
let ball = SharedShape::new(Ball::new(0.5));
let bar = SharedShape::new(parry3d::shape::Cuboid::new(Vector3::new(0.1, 1.0, 0.1)));
let shapes = vec![
(Isometry3::translation(0.0, -1.0, 0.0), ball.clone()), // Left ball
(Isometry3::identity(), bar), // Center bar
(Isometry3::translation(0.0, 1.0, 0.0), ball), // Right ball
];
let density = 1000.0;
let dumbbell_props = MassProperties::from_compound(density, &shapes);
println!("Dumbbell mass: {:.2} kg", dumbbell_props.mass());
println!("Center of mass: {:?}", dumbbell_props.local_com);
// Dumbbell has high inertia around X and Z (hard to spin end-over-end)
// but low inertia around Y (easy to spin along the bar)
let inertia = dumbbell_props.principal_inertia();
println!("Inertia: X={:.3}, Y={:.3}, Z={:.3}", inertia.x, inertia.y, inertia.z);§Example (2D) - Table
use parry2d::mass_properties::MassProperties;
use parry2d::shape::{Cuboid, SharedShape};
use nalgebra::{Isometry2, Vector2};
// Create a simple table: top surface + legs
let top = SharedShape::new(Cuboid::new(Vector2::new(2.0, 0.1))); // Wide, thin top
let leg = SharedShape::new(Cuboid::new(Vector2::new(0.1, 0.5))); // Narrow, tall leg
let shapes = vec![
(Isometry2::translation(0.0, 0.6), top), // Table top
(Isometry2::translation(-1.5, 0.0), leg.clone()), // Left leg
(Isometry2::translation(1.5, 0.0), leg), // Right leg
];
let density = 500.0; // Wood
let table_props = MassProperties::from_compound(density, &shapes);
println!("Table mass: {:.2} kg", table_props.mass());§Example (3D) - Robot Arm
use parry3d::mass_properties::MassProperties;
use parry3d::shape::{Capsule, Cuboid, SharedShape};
use nalgebra::{Isometry3, Point3, Vector3};
// Simple robot arm with multiple segments
let base = SharedShape::new(Cuboid::new(Vector3::new(0.3, 0.2, 0.3)));
let upper_arm = SharedShape::new(Capsule::new(
Point3::origin(),
Point3::new(0.0, 1.0, 0.0),
0.1
));
let forearm = SharedShape::new(Capsule::new(
Point3::origin(),
Point3::new(0.0, 0.8, 0.0),
0.08
));
let shapes = vec![
(Isometry3::identity(), base),
(Isometry3::translation(0.0, 0.2, 0.0), upper_arm),
(Isometry3::translation(0.0, 1.2, 0.0), forearm),
];
let density = 2700.0; // Aluminum
let arm_props = MassProperties::from_compound(density, &shapes);
println!("Robot arm mass: {:.2} kg", arm_props.mass());
println!("Arm center of mass: {:?}", arm_props.local_com);§Use Cases
- Complex objects: Multi-part objects (tables, chairs, vehicles)
- Articulated bodies: Robot arms, character skeletons
- Assemblies: Combining simple shapes into complex forms
- Non-convex shapes: Convex decomposition results
- Hierarchical structures: Nested compound shapes
§Different Densities
To use different densities for different parts:
// Compute each part separately with its own density
let heavy_part = ball_shape.mass_properties(5000.0).transform_by(&pos1);
let light_part = cuboid_shape.mass_properties(100.0).transform_by(&pos2);
// Combine manually
let total = heavy_part + light_part;§Performance Note
The computation time is O(n) where n is the number of sub-shapes. Each shape’s mass properties are computed once and then combined. This is efficient even for large numbers of shapes.
§See Also
MassProperties::transform_by(): Transform mass properties to a new frameAddtrait: Combine mass properties with+operatorSumtrait: Sum an iterator of mass properties
Source§impl MassProperties
impl MassProperties
Sourcepub fn from_convex_polygon(
density: f32,
vertices: &[Point<f32>],
) -> MassProperties
pub fn from_convex_polygon( density: f32, vertices: &[Point<f32>], ) -> MassProperties
Computes the mass properties of a convex polygon (2D only).
A convex polygon is a 2D shape where all interior angles are less than 180 degrees and all vertices point outward. This function decomposes the polygon into triangles from the center of mass and sums their mass properties.
§Arguments
density- The material density in kg/m² (mass per unit area)vertices- A slice of points defining the polygon vertices- Must form a convex shape
- Vertices should be ordered counter-clockwise
- At least 3 vertices required
§Returns
A MassProperties struct containing:
- mass: Total mass calculated from area and density
- local_com: Center of mass (weighted average of triangle centroids)
- inv_principal_inertia: Inverse angular inertia (scalar in 2D)
§Physics Background
The algorithm:
- Computes the geometric center of all vertices
- Creates triangles from the center to each edge
- Calculates area and inertia for each triangle
- Combines results using weighted averages
§Example - Pentagon
use parry2d::mass_properties::MassProperties;
use nalgebra::Point2;
use std::f32::consts::PI;
// Create a regular pentagon with radius 1.0
let mut vertices = Vec::new();
for i in 0..5 {
let angle = (i as f32) * 2.0 * PI / 5.0;
vertices.push(Point2::new(angle.cos(), angle.sin()));
}
let density = 100.0;
let pentagon_props = MassProperties::from_convex_polygon(density, &vertices);
println!("Pentagon mass: {:.2} kg", pentagon_props.mass());
println!("Center of mass: {:?}", pentagon_props.local_com);
// For a regular polygon centered at origin, COM should be near origin
assert!(pentagon_props.local_com.coords.norm() < 0.01);§Example - Trapezoid
use parry2d::mass_properties::MassProperties;
use nalgebra::Point2;
// Create a trapezoid (4 vertices)
let vertices = vec![
Point2::origin(), // Bottom left
Point2::new(4.0, 0.0), // Bottom right
Point2::new(3.0, 2.0), // Top right
Point2::new(1.0, 2.0), // Top left
];
let density = 50.0;
let trap_props = MassProperties::from_convex_polygon(density, &vertices);
// Area of trapezoid = ((b1 + b2) / 2) × h = ((4 + 2) / 2) × 2 = 6 m²
// Mass = area × density = 300 kg
let mass = trap_props.mass();
println!("Trapezoid mass: {:.2} kg", mass);§Example - Custom Convex Shape
use parry2d::mass_properties::MassProperties;
use nalgebra::Point2;
// Arbitrary convex polygon
let vertices = vec![
Point2::origin(),
Point2::new(2.0, 0.0),
Point2::new(3.0, 1.0),
Point2::new(2.0, 2.5),
Point2::new(0.0, 2.0),
];
let density = 200.0;
let props = MassProperties::from_convex_polygon(density, &vertices);
println!("Custom polygon mass: {:.2} kg", props.mass());
println!("Moment of inertia: {:.2}", props.principal_inertia());§Use Cases
- Custom 2D shapes: Game objects with specific geometry
- Simplified collision: Convex approximations of complex shapes
- Platforms: Angled or irregular platforms in 2D games
- Polygonal wheels: Multi-sided rotating objects
- Terrain segments: Ground pieces with varying slopes
§Important Requirements
- Must be convex: Non-convex (concave) polygons will produce incorrect results
- Vertex ordering: Counter-clockwise order is conventional but either works
- Minimum vertices: At least 3 vertices (forms a triangle)
- No self-intersection: Vertices must not cross each other
§For Non-Convex Shapes
If your polygon is concave (not convex):
- Use convex decomposition algorithms to break it into convex parts
- Compute mass properties for each convex part
- Combine using
from_compound()or by summing MassProperties
Alternatively, use from_trimesh() with a triangulated version of the shape.
§Performance Note
Computation time is O(n) where n is the number of vertices. The polygon is decomposed into n triangles, each processed independently.
Source§impl MassProperties
impl MassProperties
Sourcepub fn from_cuboid(density: f32, half_extents: Vector<f32>) -> Self
pub fn from_cuboid(density: f32, half_extents: Vector<f32>) -> Self
Computes the mass properties of a cuboid (box in 3D, rectangle in 2D).
A cuboid is a box-shaped object with three dimensions (or two in 2D), where each
dimension can have a different size. The cuboid is centered at the origin, and
half_extents define the distance from the center to each face.
§Arguments
density- The material density (mass per unit volume/area). Higher values make heavier objects.- In 3D: units are typically kg/m³ (e.g., wood = 500-900, concrete = 2400)
- In 2D: units are typically kg/m² (mass per unit area)
half_extents- Half the size along each axis (center to face distance).- In 3D:
Vector3::new(hx, hy, hz)creates a box with dimensions 2hx × 2hy × 2hz - In 2D:
Vector2::new(hx, hy)creates a rectangle with dimensions 2hx × 2hy
- In 3D:
§Returns
A MassProperties struct containing:
- mass: Total mass calculated from volume and density
- local_com: Center of mass at the origin (cuboids are symmetric)
- inv_principal_inertia: Inverse angular inertia along each axis
§Physics Background
Cuboids have axis-aligned mass distribution:
- Center of mass is at the geometric center (origin)
- Angular inertia varies per axis based on dimensions
- Longer dimensions increase inertia around perpendicular axes
- In 3D, each axis has different inertia: I_x depends on y and z extents, etc.
§Example (3D) - Wooden Crate
use parry3d::mass_properties::MassProperties;
use nalgebra::{Point3, Vector3};
// Create a wooden crate: 2m × 1m × 1m (half_extents = 1.0, 0.5, 0.5)
// Wood density: approximately 600 kg/m³
let half_extents = Vector3::new(1.0, 0.5, 0.5);
let density = 600.0;
let crate_props = MassProperties::from_cuboid(density, half_extents);
// Volume = (2 * 1.0) × (2 * 0.5) × (2 * 0.5) = 2 m³
// Mass = volume × density = 1200 kg
let mass = crate_props.mass();
assert!((mass - 1200.0).abs() < 0.1);
// Longer dimension (x-axis) means higher inertia around y and z axes
let inertia = crate_props.principal_inertia();
println!("Inertia around x-axis: {:.2}", inertia.x); // Lowest (easier to spin around length)
println!("Inertia around y-axis: {:.2}", inertia.y); // Higher
println!("Inertia around z-axis: {:.2}", inertia.z); // Higher
// Center of mass is at the origin
assert_eq!(crate_props.local_com, Point3::origin());§Example (3D) - Cube
use parry3d::mass_properties::MassProperties;
use nalgebra::Vector3;
// Create a 1m × 1m × 1m cube (half_extents = 0.5 on all axes)
let half_extents = Vector3::new(0.5, 0.5, 0.5);
let density = 1000.0; // Water density
let cube_props = MassProperties::from_cuboid(density, half_extents);
// Volume = 1 m³, Mass = 1000 kg
assert!((cube_props.mass() - 1000.0).abs() < 0.1);
// For a cube, all axes have equal inertia (symmetric)
let inertia = cube_props.principal_inertia();
assert!((inertia.x - inertia.y).abs() < 0.01);
assert!((inertia.y - inertia.z).abs() < 0.01);§Example (2D) - Rectangular Platform
use parry2d::mass_properties::MassProperties;
use nalgebra::Vector2;
// Create a 4m × 2m rectangular platform (half_extents = 2.0, 1.0)
let half_extents = Vector2::new(2.0, 1.0);
let density = 500.0; // kg/m²
let platform_props = MassProperties::from_cuboid(density, half_extents);
// Area = (2 * 2.0) × (2 * 1.0) = 8 m²
// Mass = area × density = 4000 kg
let mass = platform_props.mass();
assert!((mass - 4000.0).abs() < 0.1);
println!("Platform mass: {:.2} kg", mass);
println!("Moment of inertia: {:.2}", platform_props.principal_inertia());§Use Cases
- Boxes and crates: Storage containers, shipping boxes
- Building blocks: Walls, floors, platforms
- Vehicles: Simplified car or truck bodies
- Furniture: Tables, chairs, cabinets
- Terrain: Rectangular ground segments
§Common Mistakes
- Wrong dimensions: Remember that
half_extentsare HALF the total size. For a 2m × 2m × 2m box, useVector3::new(1.0, 1.0, 1.0), not(2.0, 2.0, 2.0) - Unit confusion: Ensure density units match your distance units (kg/m³ with meters, kg/cm³ with centimeters, etc.)
§Performance Note
This is a very fast computation (constant time). Cuboids are the second simplest shape after balls and are highly efficient for collision detection.
Source§impl MassProperties
impl MassProperties
Sourcepub fn from_triangle(
density: f32,
a: &Point<f32>,
b: &Point<f32>,
c: &Point<f32>,
) -> MassProperties
pub fn from_triangle( density: f32, a: &Point<f32>, b: &Point<f32>, c: &Point<f32>, ) -> MassProperties
Computes the mass properties of a triangle.
A triangle is the simplest polygon, defined by three vertices. In 2D, this represents a filled triangular region. In 3D, this represents a flat triangular surface with negligible thickness (useful for thin sheets or as building blocks for meshes).
§Arguments
density- The material density (mass per unit area in both 2D and 3D)- Units are typically kg/m² (surface density)
- For 3D triangles, this represents the density of a thin sheet
a,b,c- The three vertices of the triangle
§Returns
A MassProperties struct containing:
- mass: Total mass calculated from area and density
- local_com: Center of mass at the centroid (average of three vertices)
- inv_principal_inertia: Inverse angular inertia
§Physics Background
Triangles have specific geometric properties:
- Area (2D): Using cross product of edge vectors
- Area (3D): Same formula, treating triangle as flat surface
- Center of mass: Always at centroid = (a + b + c) / 3
- Angular inertia: Depends on vertex positions relative to centroid
- Degenerate cases: Zero-area triangles (collinear points) return zero mass
§Example (2D) - Right Triangle
use parry2d::mass_properties::MassProperties;
use nalgebra::Point2;
// Create a right triangle with legs of 3m and 4m
let a = Point2::origin();
let b = Point2::new(3.0, 0.0);
let c = Point2::new(0.0, 4.0);
let density = 100.0; // kg/m²
let triangle_props = MassProperties::from_triangle(density, &a, &b, &c);
// Area = (1/2) × base × height = (1/2) × 3 × 4 = 6 m²
// Mass = area × density = 600 kg
let mass = triangle_props.mass();
assert!((mass - 600.0).abs() < 0.1);
// Center of mass at centroid: (0+3+0)/3, (0+0+4)/3 = (1, 1.333)
let com = triangle_props.local_com;
assert!((com.x - 1.0).abs() < 0.01);
assert!((com.y - 4.0/3.0).abs() < 0.01);
println!("Triangle mass: {:.2} kg", mass);
println!("Center of mass: ({:.2}, {:.2})", com.x, com.y);§Example (2D) - Equilateral Triangle
use parry2d::mass_properties::MassProperties;
use nalgebra::Point2;
// Equilateral triangle with side length 2m
let side = 2.0;
let height = side * (3.0_f32.sqrt() / 2.0);
let a = Point2::origin();
let b = Point2::new(side, 0.0);
let c = Point2::new(side / 2.0, height);
let density = 50.0;
let tri_props = MassProperties::from_triangle(density, &a, &b, &c);
// For equilateral triangle: Area = (side² × √3) / 4
let expected_area = side * side * 3.0_f32.sqrt() / 4.0;
let mass = tri_props.mass();
assert!((mass - expected_area * density).abs() < 0.1);
println!("Equilateral triangle mass: {:.2} kg", mass);§Example (3D) - Triangle as Thin Sheet
use parry3d::mass_properties::MassProperties;
use nalgebra::Point3;
// Triangle in 3D space (e.g., a metal plate or sail)
let a = Point3::origin();
let b = Point3::new(2.0, 0.0, 0.0);
let c = Point3::new(1.0, 2.0, 0.0);
let density = 200.0; // kg/m² (sheet metal)
let plate_props = MassProperties::from_triangle(density, &a, &b, &c);
// Area = 2 m² (base=2, height=2, area=(1/2)×2×2=2)
// Mass = 400 kg
let mass = plate_props.mass();
assert!((mass - 400.0).abs() < 0.1);
println!("Metal plate mass: {:.2} kg", mass);§Example - Degenerate Triangle (Collinear Points)
use parry2d::mass_properties::MassProperties;
use nalgebra::Point2;
// Three points on a line (no area)
let a = Point2::origin();
let b = Point2::new(1.0, 1.0);
let c = Point2::new(2.0, 2.0);
let density = 100.0;
let degenerate = MassProperties::from_triangle(density, &a, &b, &c);
// Zero area means zero mass
assert_eq!(degenerate.mass(), 0.0);
println!("Degenerate triangle has zero mass");§Use Cases
- Mesh building blocks: Triangles are the basis for triangle meshes
- Thin surfaces: Sails, flags, sheets of material
- Terrain patches: Small triangular ground segments
- Simple shapes: Quick prototyping with basic geometry
- 2D games: Triangular platforms, obstacles, or decorations
§Vertex Order
- The order of vertices (a, b, c) matters for orientation
- Counter-clockwise order is conventional in 2D
- In 3D, vertex order determines the normal direction (right-hand rule)
- However, for mass properties, the orientation doesn’t affect the result
§Common Use with Meshes
For complex shapes, use from_trimesh() instead, which handles multiple triangles:
// For a single triangle, use from_triangle
let props = MassProperties::from_triangle(density, &a, &b, &c);
// For multiple triangles, use from_trimesh
let vertices = vec![a, b, c, d, e, f];
let indices = vec![[0, 1, 2], [3, 4, 5]];
let mesh_props = MassProperties::from_trimesh(density, &vertices, &indices);§Performance Note
Computing triangle mass properties is very fast (constant time) and involves only basic geometric calculations (area, centroid, and moment of inertia).
Source§impl MassProperties
impl MassProperties
Sourcepub fn from_trimesh(
density: f32,
vertices: &[Point<f32>],
indices: &[[u32; 3]],
) -> MassProperties
pub fn from_trimesh( density: f32, vertices: &[Point<f32>], indices: &[[u32; 3]], ) -> MassProperties
Computes the mass properties of a triangle mesh.
A triangle mesh (trimesh) is a collection of triangles that together form a 2D or 3D shape. This function works for both convex and non-convex (concave) meshes. It decomposes the mesh into individual triangles, computes each triangle’s mass properties, and combines them.
§Arguments
density- The material density- In 3D: kg/m³ (mass per unit volume) - treats mesh as a solid volume
- In 2D: kg/m² (mass per unit area) - treats mesh as a flat surface
vertices- Array of vertex positions (points in space)indices- Array of triangle indices, each element is[u32; 3]- Each triplet references three vertices forming a triangle
- Indices must be valid: all values < vertices.len()
§Returns
A MassProperties struct containing:
- mass: Total mass of all triangles combined
- local_com: Center of mass (area/volume weighted)
- inv_principal_inertia: Combined angular inertia
§Physics Background
For each triangle:
- Compute area (2D) or volume contribution (3D)
- Find center of mass (centroid)
- Calculate moment of inertia
- Use parallel axis theorem to shift to common reference frame
- Sum all contributions
§Example (2D) - L-Shape
use parry2d::mass_properties::MassProperties;
use nalgebra::Point2;
// Create an L-shaped mesh from two rectangles (4 triangles)
let vertices = vec![
Point2::origin(),
Point2::new(2.0, 0.0),
Point2::new(2.0, 1.0),
Point2::new(1.0, 1.0),
Point2::new(1.0, 3.0),
Point2::new(0.0, 3.0),
];
let indices = vec![
[0, 1, 2], // Bottom rectangle (triangle 1)
[0, 2, 3], // Bottom rectangle (triangle 2)
[0, 3, 4], // Vertical part (triangle 1)
[0, 4, 5], // Vertical part (triangle 2)
];
let density = 100.0;
let l_shape_props = MassProperties::from_trimesh(density, &vertices, &indices);
println!("L-shape mass: {:.2} kg", l_shape_props.mass());
println!("Center of mass: {:?}", l_shape_props.local_com);§Example (3D) - Pyramid
use parry3d::mass_properties::MassProperties;
use nalgebra::Point3;
// Square pyramid: 4 vertices at base + 1 apex
let vertices = vec![
Point3::new(-1.0, 0.0, -1.0), // Base corner 1
Point3::new(1.0, 0.0, -1.0), // Base corner 2
Point3::new(1.0, 0.0, 1.0), // Base corner 3
Point3::new(-1.0, 0.0, 1.0), // Base corner 4
Point3::new(0.0, 2.0, 0.0), // Apex
];
let indices = vec![
[0, 1, 4], // Side face 1
[1, 2, 4], // Side face 2
[2, 3, 4], // Side face 3
[3, 0, 4], // Side face 4
[0, 2, 1], // Base (triangle 1)
[0, 3, 2], // Base (triangle 2)
];
let density = 1000.0;
let pyramid_props = MassProperties::from_trimesh(density, &vertices, &indices);
println!("Pyramid mass: {:.2} kg", pyramid_props.mass());
println!("Center of mass: {:?}", pyramid_props.local_com);§Example (3D) - Loading from File
use parry3d::mass_properties::MassProperties;
// Assume you've loaded a mesh from an OBJ file
let mesh = load_obj_file("complex_model.obj");
let vertices = mesh.vertices;
let indices = mesh.indices;
let density = 2700.0; // Aluminum
let props = MassProperties::from_trimesh(density, &vertices, &indices);
println!("Model mass: {:.2} kg", props.mass());§Use Cases
- Complex 3D models: Characters, vehicles, buildings
- Terrain: Height-mapped ground, caves, landscapes
- Custom shapes: Anything representable as triangles
- Imported models: Meshes from modeling software (Blender, Maya, etc.)
- Non-convex objects: Concave shapes that can’t use simpler primitives
§Mesh Quality Considerations
- Watertight meshes (3D): For accurate volume/mass, mesh should be closed
- Open meshes may give incorrect results
- Check for holes, gaps, or missing faces
- Triangle orientation: Consistent winding order improves accuracy
- Counter-clockwise from outside (right-hand rule)
- Degenerate triangles: Zero-area triangles are automatically handled (skipped)
- Overlapping triangles: Can cause incorrect results; ensure clean mesh
§Performance
Computation time is O(n) where n is the number of triangles. For large meshes (thousands of triangles), this can take noticeable time. Consider:
- Using simpler primitive approximations when possible
- Pre-computing mass properties and caching results
- Simplifying meshes for physics (use low-poly collision mesh)
§Trimesh vs Simpler Shapes
For better performance and accuracy, use primitive shapes when possible:
- Ball, Cuboid, Cylinder: Much faster and more accurate
- Capsule: Better for elongated objects
- Compound: Combine multiple primitives
Use trimesh only when the shape is truly complex and can’t be approximated.
§See Also
from_convex_polyhedron(): Alias for convex meshesfrom_triangle(): For single trianglesfrom_compound(): Combine multiple simpler shapes
Source§impl MassProperties
impl MassProperties
Sourcepub fn from_voxels(density: f32, voxels: &Voxels) -> Self
pub fn from_voxels(density: f32, voxels: &Voxels) -> Self
Computes the mass properties of a voxel grid.
Voxels (volumetric pixels) represent a 3D shape as a grid of small cubes. This function treats each non-empty voxel as a small cuboid and combines their mass properties. It’s useful for volumetric data, destructible terrain, or shapes that are difficult to represent with traditional geometry.
§Arguments
density- The material density- In 3D: kg/m³ (mass per unit volume)
- In 2D: kg/m² (mass per unit area)
voxels- AVoxelsstructure containing the voxel grid- Each voxel is a small cube/square of uniform size
- Voxels can be empty or filled
- Since v0.25.0, uses sparse storage internally for efficiency
§Returns
A MassProperties struct containing:
- mass: Total mass of all non-empty voxels
- local_com: Center of mass (weighted average of voxel centers)
- inv_principal_inertia: Combined angular inertia
§Physics Background
The algorithm:
- Compute mass properties of a single voxel (small cuboid)
- For each non-empty voxel, shift its mass properties to its position
- Sum all contributions using parallel axis theorem
- Empty voxels contribute nothing (zero mass)
§Example (3D) - Simple Voxel Object
use parry3d::mass_properties::MassProperties;
use parry3d::shape::Voxels;
use nalgebra::{Point3, Vector3};
// Create a 3×3×3 voxel grid with 1m voxels
let voxel_size = Vector3::new(1.0, 1.0, 1.0);
// Fill some voxels to create an L-shape
let voxels = &[
Point3::new(0, 0, 0), // Bottom bar
Point3::new(1, 0, 0),
Point3::new(2, 0, 0),
Point3::new(0, 1, 0), // Vertical part
Point3::new(0, 2, 0),
];
let voxels = Voxels::new(voxel_size, voxels);
let density = 1000.0; // Water density
let voxel_props = MassProperties::from_voxels(density, &voxels);
// 5 voxels × 1m³ each × 1000 kg/m³ = 5000 kg
println!("Voxel object mass: {:.2} kg", voxel_props.mass());
println!("Center of mass: {:?}", voxel_props.local_com);§Example (3D) - Destructible Terrain
use parry3d::mass_properties::MassProperties;
use parry3d::shape::Voxels;
use nalgebra::{Point3, Vector3};
// Create a chunk of destructible terrain
let voxel_size = Vector3::new(0.5, 0.5, 0.5); // 50cm voxels
let mut voxels = vec![];
// Fill a 4×4×4 solid block
for x in 0..4 {
for y in 0..4 {
for z in 0..4 {
voxels.push(Point3::new(x, y, z));
}
}
}
let mut terrain = Voxels::new(voxel_size, &voxels);
let density = 2400.0; // Concrete
let terrain_props = MassProperties::from_voxels(density, &terrain);
println!("Terrain chunk mass: {:.2} kg", terrain_props.mass());§Example - Sparse Voxel Grid (Efficient)
use parry3d::mass_properties::MassProperties;
use parry3d::shape::Voxels;
use nalgebra::{Point3, Vector3};
// Large sparse grid (only stores filled voxels since v0.25.0)
let voxel_size = Vector3::new(0.1, 0.1, 0.1);
// Scatter some voxels in a large space (efficient with sparse storage)
let voxels = &[
Point3::new(0, 0, 0),
Point3::new(100, 50, 75),
Point3::new(-50, 200, -30),
];
let voxels = Voxels::new(voxel_size, voxels);
let density = 1000.0;
let props = MassProperties::from_voxels(density, &voxels);
// Only 3 voxels contribute to mass
println!("Sparse voxel mass: {:.4} kg", props.mass());§Use Cases
- Destructible terrain: Voxel-based environments (Minecraft-style)
- Medical imaging: CT scans, MRI data volumetric analysis
- Procedural generation: Voxel-based world generation
- Simulation: Granular materials, fluids represented as voxels
- Dynamic shapes: Objects that change shape at runtime
- Complex geometry: Shapes difficult to represent with meshes
§Performance Considerations
- Sparse storage (v0.25.0+): Only filled voxels consume memory
- Computation time: O(n) where n = number of filled voxels
- For large grids: Prefer coarser voxel sizes when possible
- Memory usage: Each voxel stores position and state
- Alternative: For static shapes, consider using triangle meshes
§Voxel Size Trade-offs
Smaller voxels:
- More accurate representation of curved surfaces
- More voxels = longer computation time
- Higher memory usage (more voxels to store)
Larger voxels:
- Faster computation
- Less memory
- Blockier appearance (lower resolution)
§Accuracy Notes
- Voxel representation is an approximation of the true shape
- Smooth curves become staircase patterns
- Mass properties accuracy depends on voxel resolution
- For exact results with smooth shapes, use primitive shapes or meshes
§Empty vs Filled Voxels
- Only non-empty voxels contribute to mass
- Empty voxels are ignored (zero mass, no inertia)
- The voxel state is checked using
vox.state.is_empty()
§See Also
Voxels::new(): Create a new voxel gridVoxels::set_voxel(): Add or remove voxelsfrom_trimesh(): Alternative for precise shapesfrom_compound(): Combine multiple shapes efficiently
Trait Implementations§
Source§impl AbsDiffEq for MassProperties
impl AbsDiffEq for MassProperties
Source§fn default_epsilon() -> Self::Epsilon
fn default_epsilon() -> Self::Epsilon
Source§fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool
fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool
Source§fn abs_diff_ne(&self, other: &Rhs, epsilon: Self::Epsilon) -> bool
fn abs_diff_ne(&self, other: &Rhs, epsilon: Self::Epsilon) -> bool
AbsDiffEq::abs_diff_eq.Source§impl Add for MassProperties
impl Add for MassProperties
Source§type Output = MassProperties
type Output = MassProperties
+ operator.Source§fn add(self, other: MassProperties) -> Self
fn add(self, other: MassProperties) -> Self
+ operation. Read moreSource§impl AddAssign for MassProperties
impl AddAssign for MassProperties
Source§fn add_assign(&mut self, rhs: MassProperties)
fn add_assign(&mut self, rhs: MassProperties)
+= operation. Read moreSource§impl Clone for MassProperties
impl Clone for MassProperties
Source§fn clone(&self) -> MassProperties
fn clone(&self) -> MassProperties
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for MassProperties
impl Debug for MassProperties
Source§impl Default for MassProperties
impl Default for MassProperties
Source§fn default() -> MassProperties
fn default() -> MassProperties
Source§impl PartialEq for MassProperties
impl PartialEq for MassProperties
Source§impl RelativeEq for MassProperties
impl RelativeEq for MassProperties
Source§fn default_max_relative() -> Self::Epsilon
fn default_max_relative() -> Self::Epsilon
Source§fn relative_eq(
&self,
other: &Self,
epsilon: Self::Epsilon,
max_relative: Self::Epsilon,
) -> bool
fn relative_eq( &self, other: &Self, epsilon: Self::Epsilon, max_relative: Self::Epsilon, ) -> bool
Source§fn relative_ne(
&self,
other: &Rhs,
epsilon: Self::Epsilon,
max_relative: Self::Epsilon,
) -> bool
fn relative_ne( &self, other: &Rhs, epsilon: Self::Epsilon, max_relative: Self::Epsilon, ) -> bool
RelativeEq::relative_eq.Source§impl Sub for MassProperties
impl Sub for MassProperties
Source§type Output = MassProperties
type Output = MassProperties
- operator.Source§fn sub(self, other: MassProperties) -> Self
fn sub(self, other: MassProperties) -> Self
- operation. Read moreSource§impl SubAssign for MassProperties
impl SubAssign for MassProperties
Source§fn sub_assign(&mut self, rhs: MassProperties)
fn sub_assign(&mut self, rhs: MassProperties)
-= operation. Read moreSource§impl Sum for MassProperties
impl Sum for MassProperties
Source§impl Zero for MassProperties
impl Zero for MassProperties
impl Copy for MassProperties
impl StructuralPartialEq for MassProperties
Auto Trait Implementations§
impl Freeze for MassProperties
impl RefUnwindSafe for MassProperties
impl Send for MassProperties
impl Sync for MassProperties
impl Unpin for MassProperties
impl UnwindSafe for MassProperties
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>, which can then be
downcast into Box<dyn ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>, which can then be further
downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.Source§impl<T> DowncastSend for T
impl<T> DowncastSend for T
Source§impl<T> DowncastSync for T
impl<T> DowncastSync for T
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
Source§fn to_subset(&self) -> Option<SS>
fn to_subset(&self) -> Option<SS>
self from the equivalent element of its
superset. Read moreSource§fn is_in_subset(&self) -> bool
fn is_in_subset(&self) -> bool
self is actually part of its subset T (and can be converted to it).Source§fn to_subset_unchecked(&self) -> SS
fn to_subset_unchecked(&self) -> SS
self.to_subset but without any property checks. Always succeeds.Source§fn from_subset(element: &SS) -> SP
fn from_subset(element: &SS) -> SP
self to the equivalent element of its superset.