oxiphysics_collision/query/types.rs
1// Auto-generated module
2//
3// 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use super::functions::*;
6
7/// A child shape entry: a boxed shape query together with its 4×4 world transform.
8pub type ShapeEntry = (Box<dyn ShapeQuery>, [[f64; 4]; 4]);
9
10/// A view frustum defined by 6 half-space planes.
11///
12/// Each plane is `[nx, ny, nz, d]` where `n·p + d >= 0` is the "inside" half-space.
13/// Planes should be: near, far, left, right, top, bottom.
14pub struct ViewFrustum {
15 /// The 6 frustum planes.
16 pub planes: [[f64; 4]; 6],
17}
18impl ViewFrustum {
19 /// Create a frustum from 6 plane coefficients.
20 pub fn new(planes: [[f64; 4]; 6]) -> Self {
21 Self { planes }
22 }
23 /// Test whether a sphere is potentially visible (intersects the frustum).
24 ///
25 /// Returns `false` only if the sphere is fully outside at least one plane.
26 pub fn test_sphere(&self, center: [f64; 3], radius: f64) -> bool {
27 for plane in &self.planes {
28 let n = [plane[0], plane[1], plane[2]];
29 let d = plane[3];
30 let sd = dot(center, n) + d;
31 if sd < -radius {
32 return false;
33 }
34 }
35 true
36 }
37 /// Test whether an AABB is potentially visible.
38 ///
39 /// Uses the p-vertex / n-vertex method for conservative rejection.
40 pub fn test_aabb(&self, min: [f64; 3], max: [f64; 3]) -> bool {
41 for plane in &self.planes {
42 let n = [plane[0], plane[1], plane[2]];
43 let d = plane[3];
44 let p = [
45 if n[0] >= 0.0 { max[0] } else { min[0] },
46 if n[1] >= 0.0 { max[1] } else { min[1] },
47 if n[2] >= 0.0 { max[2] } else { min[2] },
48 ];
49 if dot(p, n) + d < 0.0 {
50 return false;
51 }
52 }
53 true
54 }
55}
56/// Result of a ray cast in the all-pairs query.
57pub struct AllPairsRayResult {
58 /// Index into the input shapes array of the hit shape.
59 pub shape_index: usize,
60 /// Time of impact.
61 pub toi: f64,
62 /// Surface normal at the hit point.
63 pub normal: [f64; 3],
64 /// Hit point in world space.
65 pub point: [f64; 3],
66}
67/// Result of a sphere cast against the scene.
68pub struct SphereCastResult {
69 /// Index of the first shape hit.
70 pub shape_index: usize,
71 /// Time of impact.
72 pub toi: f64,
73 /// Contact normal at the hit (pointing from the hit shape toward the sphere centre).
74 pub normal: [f64; 3],
75}
76/// Result of a continuous time-of-impact (TOI) query between two moving spheres.
77pub struct ToiResult {
78 /// Time of impact in `[0, max_t]`.
79 pub toi: f64,
80 /// Contact normal at impact (from B toward A).
81 pub normal: [f64; 3],
82 /// Contact point in world space.
83 pub point: [f64; 3],
84}
85/// A shape and its distance from the query point.
86pub struct NearestShape {
87 /// Index of the shape in the input array.
88 pub index: usize,
89 /// Signed distance from the query point to the shape surface.
90 /// Negative if the point is inside the shape.
91 pub distance: f64,
92 /// Closest point on the shape surface to the query point.
93 pub closest_point: [f64; 3],
94}
95/// Compound shape composed of transformed child shapes.
96pub struct CompoundQuery {
97 /// Child shapes together with their world transforms.
98 pub shapes: Vec<ShapeEntry>,
99}
100impl CompoundQuery {
101 /// Create an empty compound shape.
102 pub fn new() -> Self {
103 CompoundQuery { shapes: Vec::new() }
104 }
105 /// Add a child shape with the given 4×4 world transform.
106 pub fn add_shape(&mut self, shape: Box<dyn ShapeQuery>, transform: [[f64; 4]; 4]) {
107 self.shapes.push((shape, transform));
108 }
109 /// Cast a ray against all child shapes and return the first (closest) hit.
110 pub fn ray_cast_any(
111 &self,
112 ray_origin: [f64; 3],
113 ray_dir: [f64; 3],
114 max_toi: f64,
115 ) -> Option<RayCastResult> {
116 let mut best: Option<RayCastResult> = None;
117 for (shape, transform) in &self.shapes {
118 let (local_origin, local_dir) = transform_ray(ray_origin, ray_dir, *transform);
119 let current_max = best.as_ref().map_or(max_toi, |b| b.toi);
120 if let Some(local_res) = shape.ray_cast(local_origin, local_dir, current_max) {
121 let world_point = transform_point(local_res.point, *transform);
122 let n = local_res.normal;
123 let t = transform;
124 let world_normal = normalize([
125 t[0][0] * n[0] + t[0][1] * n[1] + t[0][2] * n[2],
126 t[1][0] * n[0] + t[1][1] * n[1] + t[1][2] * n[2],
127 t[2][0] * n[0] + t[2][1] * n[1] + t[2][2] * n[2],
128 ]);
129 let candidate = RayCastResult {
130 toi: local_res.toi,
131 normal: world_normal,
132 point: world_point,
133 };
134 let replace = match &best {
135 None => true,
136 Some(b) => candidate.toi < b.toi,
137 };
138 if replace {
139 best = Some(candidate);
140 }
141 }
142 }
143 best
144 }
145}
146/// Descriptor for a shape in the all-pairs query.
147pub enum QueryShapeRef<'a> {
148 /// A sphere.
149 Sphere(&'a QuerySphere),
150 /// An axis-aligned box.
151 Box(&'a QueryBox),
152 /// A capsule.
153 Capsule(&'a QueryCapsule),
154}
155/// Result of a successful ray cast against a shape.
156pub struct RayCastResult {
157 /// Time of impact along the ray (parametric distance).
158 pub toi: f64,
159 /// Surface normal at the hit point (world-space unit vector).
160 pub normal: [f64; 3],
161 /// Hit point in world space.
162 pub point: [f64; 3],
163}
164/// Sphere shape for queries.
165pub struct QuerySphere {
166 /// Centre of the sphere.
167 pub center: [f64; 3],
168 /// Radius.
169 pub radius: f64,
170}
171/// Result of a segment-to-segment closest-point query.
172pub struct SegmentDistResult {
173 /// Closest point on segment A.
174 pub point_a: [f64; 3],
175 /// Closest point on segment B.
176 pub point_b: [f64; 3],
177 /// Distance between the two closest points.
178 pub distance: f64,
179}
180/// Result of a closest-point query on a shape.
181pub struct PointQueryResult {
182 /// Closest point on (or inside) the shape surface.
183 pub point: [f64; 3],
184 /// Signed distance: negative when the query point is inside the shape.
185 pub distance: f64,
186 /// `true` when the query point lies inside the shape.
187 pub inside: bool,
188}
189/// Capsule shape for queries (line segment + radius).
190pub struct QueryCapsule {
191 /// First endpoint of the central segment.
192 pub p0: [f64; 3],
193 /// Second endpoint of the central segment.
194 pub p1: [f64; 3],
195 /// Radius of the capsule.
196 pub radius: f64,
197}
198impl QueryCapsule {
199 /// Closest point on the segment p0→p1 to `point`, returning the point and
200 /// the parameter t ∈ \[0,1\].
201 pub(super) fn closest_point_on_segment(&self, point: [f64; 3]) -> ([f64; 3], f64) {
202 let ab = sub(self.p1, self.p0);
203 let ap = sub(point, self.p0);
204 let len_sq = dot(ab, ab);
205 if len_sq < 1e-30 {
206 return (self.p0, 0.0);
207 }
208 let t = clamp(dot(ap, ab) / len_sq, 0.0, 1.0);
209 let closest = add(self.p0, scale(ab, t));
210 (closest, t)
211 }
212}
213/// Axis-aligned box shape for queries.
214pub struct QueryBox {
215 /// Centre of the box.
216 pub center: [f64; 3],
217 /// Half-extents along each axis.
218 pub half_extents: [f64; 3],
219}