Skip to main content

oxiphysics_geometry/compound/
compound_traits.rs

1//! # Compound - Trait Implementations
2//!
3//! This module contains trait implementations for `Compound`.
4//!
5//! ## Implemented Traits
6//!
7//! - `Shape`
8//!
9//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
10
11use crate::shape::{RayHit, Shape};
12use oxiphysics_core::Aabb;
13use oxiphysics_core::math::{Mat3, Real, Vec3};
14
15use super::types::Compound;
16
17impl Shape for Compound {
18    fn bounding_box(&self) -> Aabb {
19        if self.children.is_empty() {
20            return Aabb::new(Vec3::zeros(), Vec3::zeros());
21        }
22        let mut result: Option<Aabb> = None;
23        for (transform, shape) in &self.children {
24            let local_aabb = shape.bounding_box();
25            let corners = [
26                Vec3::new(local_aabb.min.x, local_aabb.min.y, local_aabb.min.z),
27                Vec3::new(local_aabb.max.x, local_aabb.min.y, local_aabb.min.z),
28                Vec3::new(local_aabb.min.x, local_aabb.max.y, local_aabb.min.z),
29                Vec3::new(local_aabb.max.x, local_aabb.max.y, local_aabb.min.z),
30                Vec3::new(local_aabb.min.x, local_aabb.min.y, local_aabb.max.z),
31                Vec3::new(local_aabb.max.x, local_aabb.min.y, local_aabb.max.z),
32                Vec3::new(local_aabb.min.x, local_aabb.max.y, local_aabb.max.z),
33                Vec3::new(local_aabb.max.x, local_aabb.max.y, local_aabb.max.z),
34            ];
35            let mut min = transform.transform_point(&corners[0]);
36            let mut max = min;
37            for corner in &corners[1..] {
38                let p = transform.transform_point(corner);
39                min = min.inf(&p);
40                max = max.sup(&p);
41            }
42            let child_aabb = Aabb::new(min, max);
43            result = Some(match result {
44                Some(r) => r.merge(&child_aabb),
45                None => child_aabb,
46            });
47        }
48        result.unwrap_or_else(|| Aabb::new(Vec3::zeros(), Vec3::zeros()))
49    }
50    fn support_point(&self, direction: &Vec3) -> Vec3 {
51        let mut best_dot = Real::NEG_INFINITY;
52        let mut best_point = Vec3::zeros();
53        for (transform, shape) in &self.children {
54            let local_dir = transform.inverse().transform_vector(direction);
55            let local_support = shape.support_point(&local_dir);
56            let world_support = transform.transform_point(&local_support);
57            let d = world_support.dot(direction);
58            if d > best_dot {
59                best_dot = d;
60                best_point = world_support;
61            }
62        }
63        best_point
64    }
65    fn volume(&self) -> Real {
66        self.children.iter().map(|(_, s)| s.volume()).sum()
67    }
68    fn center_of_mass(&self) -> Vec3 {
69        let total_vol: Real = self.children.iter().map(|(_, s)| s.volume()).sum();
70        if total_vol < 1e-12 {
71            return Vec3::zeros();
72        }
73        let weighted: Vec3 = self
74            .children
75            .iter()
76            .map(|(t, s)| {
77                let local_com = s.center_of_mass();
78                let world_com = t.transform_point(&local_com);
79                world_com * s.volume()
80            })
81            .sum();
82        weighted / total_vol
83    }
84    fn inertia_tensor(&self, mass: Real) -> Mat3 {
85        let total_vol: Real = self.children.iter().map(|(_, s)| s.volume()).sum();
86        if total_vol < 1e-12 {
87            return Mat3::zeros();
88        }
89        let com = self.center_of_mass();
90        let mut total = Mat3::zeros();
91        for (transform, shape) in &self.children {
92            let child_mass = mass * shape.volume() / total_vol;
93            let child_inertia = shape.inertia_tensor(child_mass);
94            let child_com = transform.transform_point(&shape.center_of_mass());
95            let r = child_com - com;
96            let r2 = r.dot(&r);
97            let parallel = Mat3::identity() * r2 - r * r.transpose();
98            total += child_inertia + parallel * child_mass;
99        }
100        total
101    }
102    fn ray_cast(&self, ray_origin: &Vec3, ray_direction: &Vec3, max_toi: Real) -> Option<RayHit> {
103        let mut best: Option<RayHit> = None;
104        for (transform, shape) in &self.children {
105            let inv = transform.inverse();
106            let local_origin = inv.transform_point(ray_origin);
107            let local_dir = inv.transform_vector(ray_direction);
108            if let Some(hit) = shape.ray_cast(&local_origin, &local_dir, max_toi)
109                && best.as_ref().is_none_or(|b| hit.toi < b.toi)
110            {
111                let world_point = transform.transform_point(&hit.point);
112                let world_normal = transform.transform_vector(&hit.normal).normalize();
113                best = Some(RayHit {
114                    point: world_point,
115                    normal: world_normal,
116                    toi: hit.toi,
117                });
118            }
119        }
120        best
121    }
122}