scirs2_spatial/collision/
shapes.rs

1// Geometric primitives used for collision detection
2//
3// This module provides the geometric primitives that are used for collision
4// detection algorithms, including circles, triangles, boxes in 2D and 3D.
5
6// ---------------------------------------------------------------------------
7// 2D Geometric Primitives
8// ---------------------------------------------------------------------------
9
10/// A 2D circle
11#[derive(Debug, Clone, Copy)]
12pub struct Circle {
13    /// Center of the circle [x, y]
14    pub center: [f64; 2],
15    /// Radius of the circle
16    pub radius: f64,
17}
18
19impl Circle {
20    /// Creates a new circle with the given center and radius
21    pub fn new(center: [f64; 2], radius: f64) -> Self {
22        Circle { center, radius }
23    }
24
25    /// Calculates the area of the circle
26    pub fn area(&self) -> f64 {
27        std::f64::consts::PI * self.radius * self.radius
28    }
29
30    /// Tests if a point is inside the circle
31    pub fn contains_point(&self, point: &[f64; 2]) -> bool {
32        super::narrowphase::point_circle_collision(point, self)
33    }
34}
35
36/// A 2D line segment defined by two endpoints
37#[derive(Debug, Clone, Copy)]
38pub struct LineSegment2D {
39    /// First endpoint [x, y]
40    pub start: [f64; 2],
41    /// Second endpoint [x, y]
42    pub end: [f64; 2],
43}
44
45impl LineSegment2D {
46    /// Creates a new line segment with the given endpoints
47    pub fn new(start: [f64; 2], end: [f64; 2]) -> Self {
48        LineSegment2D { start, end }
49    }
50
51    /// Calculates the length of the line segment
52    pub fn length(&self) -> f64 {
53        let dx = self.end[0] - self.start[0];
54        let dy = self.end[1] - self.start[1];
55        (dx * dx + dy * dy).sqrt()
56    }
57}
58
59/// A 2D triangle defined by three vertices
60#[derive(Debug, Clone, Copy)]
61pub struct Triangle2D {
62    /// First vertex [x, y]
63    pub v1: [f64; 2],
64    /// Second vertex [x, y]
65    pub v2: [f64; 2],
66    /// Third vertex [x, y]
67    pub v3: [f64; 2],
68}
69
70impl Triangle2D {
71    /// Creates a new triangle with the given vertices
72    pub fn new(a: [f64; 2], b: [f64; 2], c: [f64; 2]) -> Self {
73        Triangle2D {
74            v1: a,
75            v2: b,
76            v3: c,
77        }
78    }
79
80    /// Calculates the area of the triangle
81    pub fn area(&self) -> f64 {
82        0.5 * ((self.v2[0] - self.v1[0]) * (self.v3[1] - self.v1[1])
83            - (self.v3[0] - self.v1[0]) * (self.v2[1] - self.v1[1]))
84            .abs()
85    }
86
87    /// Tests if a point is inside the triangle
88    pub fn contains_point(&self, point: &[f64; 2]) -> bool {
89        super::narrowphase::point_triangle2d_collision(point, self)
90    }
91
92    /// Provides access to the first vertex (alias for v1)
93    pub fn a(&self) -> &[f64; 2] {
94        &self.v1
95    }
96
97    /// Provides access to the second vertex (alias for v2)
98    pub fn b(&self) -> &[f64; 2] {
99        &self.v2
100    }
101
102    /// Provides access to the third vertex (alias for v3)
103    pub fn c(&self) -> &[f64; 2] {
104        &self.v3
105    }
106}
107
108/// A 2D axis-aligned bounding box
109#[derive(Debug, Clone, Copy)]
110pub struct Box2D {
111    /// Minimum corner [x, y]
112    pub min: [f64; 2],
113    /// Maximum corner [x, y]
114    pub max: [f64; 2],
115}
116
117impl Box2D {
118    /// Creates a new 2D axis-aligned bounding box with the given minimum and maximum corners
119    pub fn new(min: [f64; 2], max: [f64; 2]) -> Self {
120        Box2D { min, max }
121    }
122
123    /// Gets the width of the box
124    pub fn width(&self) -> f64 {
125        self.max[0] - self.min[0]
126    }
127
128    /// Gets the height of the box
129    pub fn height(&self) -> f64 {
130        self.max[1] - self.min[1]
131    }
132
133    /// Calculates the area of the box
134    pub fn area(&self) -> f64 {
135        self.width() * self.height()
136    }
137
138    /// Gets the center of the box
139    pub fn center(&self) -> [f64; 2] {
140        [
141            (self.min[0] + self.max[0]) * 0.5,
142            (self.min[1] + self.max[1]) * 0.5,
143        ]
144    }
145
146    /// Tests if a point is inside the box
147    pub fn contains_point(&self, point: &[f64; 2]) -> bool {
148        super::narrowphase::point_box2d_collision(point, self)
149    }
150}
151
152// ---------------------------------------------------------------------------
153// 3D Geometric Primitives
154// ---------------------------------------------------------------------------
155
156/// A 3D sphere
157#[derive(Debug, Clone, Copy)]
158pub struct Sphere {
159    /// Center of the sphere [x, y, z]
160    pub center: [f64; 3],
161    /// Radius of the sphere
162    pub radius: f64,
163}
164
165impl Sphere {
166    /// Creates a new sphere with the given center and radius
167    pub fn new(center: [f64; 3], radius: f64) -> Self {
168        Sphere { center, radius }
169    }
170
171    /// Calculates the volume of the sphere
172    pub fn volume(&self) -> f64 {
173        (4.0 / 3.0) * std::f64::consts::PI * self.radius * self.radius * self.radius
174    }
175
176    /// Tests if a point is inside the sphere
177    pub fn contains_point(&self, point: &[f64; 3]) -> bool {
178        super::narrowphase::point_sphere_collision(point, self)
179    }
180}
181
182/// A 3D line segment defined by two endpoints
183#[derive(Debug, Clone, Copy)]
184pub struct LineSegment3D {
185    /// First endpoint [x, y, z]
186    pub start: [f64; 3],
187    /// Second endpoint [x, y, z]
188    pub end: [f64; 3],
189}
190
191impl LineSegment3D {
192    /// Creates a new 3D line segment with the given endpoints
193    pub fn new(start: [f64; 3], end: [f64; 3]) -> Self {
194        LineSegment3D { start, end }
195    }
196
197    /// Calculates the length of the line segment
198    pub fn length(&self) -> f64 {
199        let dx = self.end[0] - self.start[0];
200        let dy = self.end[1] - self.start[1];
201        let dz = self.end[2] - self.start[2];
202        (dx * dx + dy * dy + dz * dz).sqrt()
203    }
204}
205
206/// A 3D triangle defined by three vertices
207#[derive(Debug, Clone, Copy)]
208pub struct Triangle3D {
209    /// First vertex [x, y, z]
210    pub v1: [f64; 3],
211    /// Second vertex [x, y, z]
212    pub v2: [f64; 3],
213    /// Third vertex [x, y, z]
214    pub v3: [f64; 3],
215}
216
217impl Triangle3D {
218    /// Creates a new 3D triangle with the given vertices
219    pub fn new(a: [f64; 3], b: [f64; 3], c: [f64; 3]) -> Self {
220        Triangle3D {
221            v1: a,
222            v2: b,
223            v3: c,
224        }
225    }
226
227    /// Calculates the area of the triangle
228    pub fn area(&self) -> f64 {
229        let edge1 = [
230            self.v2[0] - self.v1[0],
231            self.v2[1] - self.v1[1],
232            self.v2[2] - self.v1[2],
233        ];
234
235        let edge2 = [
236            self.v3[0] - self.v1[0],
237            self.v3[1] - self.v1[1],
238            self.v3[2] - self.v1[2],
239        ];
240
241        // Cross product of edges
242        let cross = [
243            edge1[1] * edge2[2] - edge1[2] * edge2[1],
244            edge1[2] * edge2[0] - edge1[0] * edge2[2],
245            edge1[0] * edge2[1] - edge1[1] * edge2[0],
246        ];
247
248        // Area is half the magnitude of the cross product
249        0.5 * (cross[0] * cross[0] + cross[1] * cross[1] + cross[2] * cross[2]).sqrt()
250    }
251
252    /// Calculates the normal vector of the triangle
253    pub fn normal(&self) -> [f64; 3] {
254        let edge1 = [
255            self.v2[0] - self.v1[0],
256            self.v2[1] - self.v1[1],
257            self.v2[2] - self.v1[2],
258        ];
259
260        let edge2 = [
261            self.v3[0] - self.v1[0],
262            self.v3[1] - self.v1[1],
263            self.v3[2] - self.v1[2],
264        ];
265
266        // Cross product of edges gives the normal
267        let normal = [
268            edge1[1] * edge2[2] - edge1[2] * edge2[1],
269            edge1[2] * edge2[0] - edge1[0] * edge2[2],
270            edge1[0] * edge2[1] - edge1[1] * edge2[0],
271        ];
272
273        // Normalize
274        let normal_length =
275            (normal[0] * normal[0] + normal[1] * normal[1] + normal[2] * normal[2]).sqrt();
276        if normal_length == 0.0 {
277            [0.0, 0.0, 0.0] // Degenerate triangle
278        } else {
279            [
280                normal[0] / normal_length,
281                normal[1] / normal_length,
282                normal[2] / normal_length,
283            ]
284        }
285    }
286
287    /// Provides access to the first vertex (alias for v1)
288    pub fn a(&self) -> &[f64; 3] {
289        &self.v1
290    }
291
292    /// Provides access to the second vertex (alias for v2)
293    pub fn b(&self) -> &[f64; 3] {
294        &self.v2
295    }
296
297    /// Provides access to the third vertex (alias for v3)
298    pub fn c(&self) -> &[f64; 3] {
299        &self.v3
300    }
301}
302
303/// A 3D axis-aligned bounding box
304#[derive(Debug, Clone, Copy)]
305pub struct Box3D {
306    /// Minimum corner [x, y, z]
307    pub min: [f64; 3],
308    /// Maximum corner [x, y, z]
309    pub max: [f64; 3],
310}
311
312impl Box3D {
313    /// Creates a new 3D axis-aligned bounding box with the given minimum and maximum corners
314    pub fn new(min: [f64; 3], max: [f64; 3]) -> Self {
315        Box3D { min, max }
316    }
317
318    /// Gets the width of the box (x-dimension)
319    pub fn width(&self) -> f64 {
320        self.max[0] - self.min[0]
321    }
322
323    /// Gets the height of the box (y-dimension)
324    pub fn height(&self) -> f64 {
325        self.max[1] - self.min[1]
326    }
327
328    /// Gets the depth of the box (z-dimension)
329    pub fn depth(&self) -> f64 {
330        self.max[2] - self.min[2]
331    }
332
333    /// Calculates the volume of the box
334    pub fn volume(&self) -> f64 {
335        self.width() * self.height() * self.depth()
336    }
337
338    /// Gets the center of the box
339    pub fn center(&self) -> [f64; 3] {
340        [
341            (self.min[0] + self.max[0]) * 0.5,
342            (self.min[1] + self.max[1]) * 0.5,
343            (self.min[2] + self.max[2]) * 0.5,
344        ]
345    }
346
347    /// Tests if a point is inside the box
348    pub fn contains_point(&self, point: &[f64; 3]) -> bool {
349        super::narrowphase::point_box3d_collision(point, self)
350    }
351}