sepax2d/
lib.rs

1//! [![Crates.io](https://img.shields.io/crates/v/sepax2d.svg)](https://crates.io/crates/sepax2d)
2//! [![MIT/Apache 2.0](https://img.shields.io/badge/license-MIT%2FApache-blue.svg)](./LICENSE)
3//!
4//! # sepax2d
5//! A safe Rust crate for finding and resolving collisions of convex shapes using the Separating Axis Theorem (SAT) in two dimensions.
6//!
7//! ### Usage
8//!
9//! Add the following to the `[dependencies]` section of your `Cargo.toml`:
10//!
11//! ```toml
12//! sepax2d = "0.3"
13//! ```
14//!
15//! Import the types of shapes you'd like to use and create some new shapes:
16//!
17//! ```rust
18//! use sepax2d::prelude::*;
19//! #
20//! # let top_left = (1.0, 1.0);
21//! # let width = 5.0;
22//! # let height = 5.0;
23//! # let center = (2.0, 0.0);
24//! # let radius = 2.0;
25//! # let arm = (1.0, 0.0);
26//! # let position = (-1.0, -1.0);
27//! # let u_vector = (2.0, 1.0);
28//! # let v_vector = (0.0, -2.0);
29//!
30//! let rectangle = AABB::new(top_left, width, height);
31//! let circle = Circle::new(center, radius);
32//! let capsule = Capsule::new(center, arm, radius);
33//! let parallelogram = Parallelogram::new(position, u_vector, v_vector);
34//!
35//! //The vertices of a polygon are position vectors
36//! //relative to the shape's position, i.e. if position
37//! //is (1, 2), then the absolute location of the
38//! //first vertex is (1, 0).
39//! let triangle = Polygon::from_vertices
40//! (
41//!
42//!     position,
43//!     vec![(0.0, -2.0), (-1.0, 2.0), (1.0, 2.0)]
44//!
45//! );
46//! ```
47//!
48//! Use the `sat_overlap(&left, &right)` method to get a `bool` indicating whether or not the two given shapes overlap.
49//! Any struct implementing the `Shape` trait can be used.
50//!
51//! ```rust
52//! # use sepax2d::prelude::*;
53//! #
54//! # let top_left = (1.0, 1.0);
55//! # let width = 5.0;
56//! # let height = 5.0;
57//! # let center = (2.0, 0.0);
58//! # let radius = 2.0;
59//! # let arm = (1.0, 0.0);
60//! # let position = (-1.0, -1.0);
61//! # let rectangle = AABB::new(top_left, width, height);
62//! # let circle = Circle::new(center, radius);
63//! # let capsule = Capsule::new(center, arm, radius);
64//! #
65//! # let triangle = Polygon::from_vertices
66//! # (
67//! #
68//! #    position,
69//! #    vec![(0.0, -2.0), (-1.0, 2.0), (1.0, 2.0)]
70//! #
71//! # );
72//! #
73//! assert!(sat_overlap(&circle, &capsule));
74//! assert!(!sat_overlap(&triangle, &rectangle));
75//! ```
76//!
77//! Use the `sat_collision(&left, &right)` method to get a `(f32, f32)` which represents the shift needed to add to `right`'s
78//! position in order to resolve the overlap of the two shapes.
79//!
80//! ```rust
81//! # use sepax2d::prelude::*;
82//! #
83//! # let top_left = (1.0, 1.0);
84//! # let width = 5.0;
85//! # let height = 5.0;
86//! # let center = (2.0, 0.0);
87//! # let radius = 2.0;
88//! # let arm = (1.0, 0.0);
89//! # let position = (-1.0, -1.0);
90//! #
91//! # let rectangle = AABB::new(top_left, width, height);
92//! # let circle = Circle::new(center, radius);
93//! # let capsule = Capsule::new(center, arm, radius);
94//! #
95//! # let mut triangle = Polygon::from_vertices
96//! # (
97//! #
98//! #    position,
99//! #    vec![(0.0, -2.0), (-1.0, 2.0), (1.0, 2.0)]
100//! #
101//! # );
102//! #
103//! let resolution = sat_collision(&circle, &triangle);
104//!
105//! let position = triangle.position();
106//! triangle.set_position((position.0 + resolution.0, position.1 + resolution.1));
107//!
108//! assert!(!sat_overlap(&circle, &triangle));
109//! ```
110//!
111//! Use the `contains_point(&shape, point)` method to get a `bool` indicating whether or not the specified point
112//! is inside the given shape.
113//!
114//! ```rust
115//! # use sepax2d::prelude::*;
116//!
117//! let rect = AABB::new((0.0, 0.0), 5.0, 2.0);
118//!
119//! assert!(contains_point(&rect, (1.0, 1.0)));
120//! assert!(!contains_point(&rect, (10.0, -1.0)));
121//! ```
122//!
123//! `Polygon`, `Circle`, `Capsule`, and `Parallelogram` shapes implement the `Rotate` trait, which allows you to rotate them
124//! around their `position`.
125//!
126//! ```rust
127//! # use sepax2d::prelude::*;
128//! # let position = (-1.0, -1.0);
129//!
130//! let mut triangle = Polygon::from_vertices
131//! (
132//!
133//!    position,
134//!    vec![(-1.0, 0.0), (0.0, 2.0), (1.0, 0.0)]
135//!
136//! );
137//!
138//! triangle.rotate(std::f32::consts::FRAC_PI_2)
139//! //New vertices: [(0.0, -1.0), (-2.0, 0.0), (0.0, 1.0)]
140//! ```
141//!
142//! You can use the `intersects_line`, `intersects_ray`, and `intersects_segment` methods to
143//! check whether a shape intersects with the corresponding type of line.
144//!
145//! ```rust
146//! # use sepax2d::prelude::*;
147//!
148//! let triangle = Polygon::from_vertices((0.0, vec![(0.0, 0.0), (1.0, 1.0), (-1.0, 1.0)]));
149//!
150//! assert!(intersects_segment(&triangle, (2.0, 0.5), (-2.0, 0.5)));
151//! ```
152//!
153//! ### Features
154//!
155//! Enable the `serde` feature for (De)Serialization of supported shapes!
156
157#![allow(clippy::needless_return)]
158
159pub mod polygon;
160pub mod circle;
161pub mod aabb;
162pub mod capsule;
163pub mod parallelogram;
164
165pub mod line;
166
167/// A trait describing the behavior needed to implement SAT overlap and collision
168/// for a given shape.
169pub trait Shape
170{
171
172    /// The location of the shape in 2D space.
173    fn position(&self) -> (f32, f32);
174
175    /// Set the location of the shape.
176    fn set_position(&mut self, position: (f32, f32));
177
178    /// The number of axes the shape provides for testing. For polygons, it is
179    /// the same as the number of vertices, but for circles it is simply one.
180    fn num_axes(&self) -> usize;
181
182    /// The method used to access the axes during the SAT calculations. This is
183    /// used to avoid the memory allocation of a new vector or array each time
184    /// we calculate collisions.
185    fn get_axis(&self, index: usize, target: (f32, f32)) -> (f32, f32);
186
187    /// Getting the minimum and maximum projection of the shape onto the given axis
188    /// to look for overlap. Normalize denotes whether or not the axis passed in is
189    /// a unit vector to avoid repeating calculations.
190    fn project(&self, axis: (f32, f32), normalize: bool) -> (f32, f32);
191
192    /// Determine whether or not the shape needs access to the closest vertex of
193    /// another shape to check collisions.
194    fn needs_closest(&self, index: usize) -> bool;
195
196    /// Gets the closest vertex/primary point/position to the given target, NOT the closest point
197    /// on the shape.
198    fn get_closest(&self, target: (f32, f32)) -> (f32, f32);
199
200    /// The point corresponding to the given axis, if applicable. Otherwise, position.
201    fn point(&self, index: usize) -> (f32, f32);
202
203}
204
205/// A trait indicating that a shape can be rotated around its position. Applicable
206/// to all shapes other than AABB.
207pub trait Rotate
208{
209
210    /// Rotate the shape by the given angle, with the rotation counterclockwise when
211    /// the Y-axis points up.
212    fn rotate(&mut self, angle: f32);
213
214    /// Rotate the shape using the given sine and cosine of an angle. Use this when
215    /// you are rotating multiple shapes by the same angle and don't want to re-calculate
216    /// the trig functions.
217    fn rotate_sincos(&mut self, sin: f32, cos: f32);
218
219}
220
221//Helper macro to rotate the given 2D vector v by the rotation matrix with sine s and cosine c
222#[macro_export]
223macro_rules! rotate
224{
225
226    ($s: expr, $c: expr, $v: expr) =>
227    {
228
229        ($c * $v.0 - $s * $v.1, $s * $v.0 + $c * $v.1   )
230
231    };
232
233}
234
235/// Returns true if the given shapes overlap, and false if they do not. Does not work for
236/// degenerate shapes.
237///
238/// This method performs a floating point comparison with Rust's built in epsilon constant, so it may
239/// return the incorrect answer for shapes which are very small or very close together.
240///
241/// Requires both shapes to be convex.
242///
243/// # Examples
244///
245/// ```
246/// use sepax2d::prelude::*;
247///
248/// let square = Polygon::from_vertices((1.0, 1.0), vec![(-1.0, 1.0), (1.0, 1.0), (1.0, -1.0), (-1.0, -1.0)]);
249/// let triangle = Polygon::from_vertices((0.0, 0.0), vec![(2.0, 2.0), (0.0, -2.0), (-1.0, 0.0)]);
250///
251/// assert!(sat_overlap(&square, &triangle));
252/// ```
253pub fn sat_overlap(left: &(impl Shape + ?Sized), right: &(impl Shape + ?Sized)) -> bool
254{
255
256    if !shape_overlap(left, right, false).0
257    {
258
259        return false;
260
261    }
262
263    if !shape_overlap(right, left, false).0
264    {
265
266        return false;
267
268    }
269
270    return true;
271
272}
273
274/// Returns the vector that needs to be added to the second shape's position to resolve a collision with
275/// the first shape. Does not work for degenerate shapes.
276///
277/// If the shapes are not colliding, it returns the zero vector.
278///
279/// This method performs a floating point comparison with Rust's built in epsilon constant, so it may
280/// return the incorrect answer for shapes which are very small or very close together.
281///
282/// Requires both shapes to be convex.
283///
284/// # Examples
285///
286/// ```
287/// use sepax2d::prelude::*;
288///
289/// let square = Polygon::from_vertices((1.0, 1.0), vec![(-1.0, 1.0), (1.0, 1.0), (1.0, -1.0), (-1.0, -1.0)]);
290/// let triangle = Polygon::from_vertices((-3.5, 1.0), vec![(4.0, 0.0), (0.0, 6.0), (-4.0, 0.0)]);
291///
292/// let aabb = AABB::new((0.0, 2.0), 2.0, 2.0);
293///
294/// let resolution = sat_collision(&square, &triangle);
295/// //resolution = (-0.5, 0.0) up to floating point error
296///
297/// assert!(resolution.0 + 0.5 < f32::EPSILON && resolution.0 + 0.5 > -f32::EPSILON);
298/// assert!(resolution.1 < f32::EPSILON && resolution.1 > -f32::EPSILON);
299///
300/// let aabb_resolution = sat_collision(&aabb, &triangle);
301/// //resolution = (-0.5, 0.0) up to floating point error. aabb is a different way to
302/// //represent the same shape as square
303///
304/// assert!(resolution.0 + 0.5 < f32::EPSILON && resolution.0 + 0.5 > -f32::EPSILON);
305/// assert!(resolution.1 < f32::EPSILON && resolution.1 > -f32::EPSILON);
306///
307/// ```
308pub fn sat_collision(left: &(impl Shape + ?Sized), right: &(impl Shape + ?Sized)) -> (f32, f32)
309{
310
311    let l_overlap = shape_overlap(left, right, true);
312    let r_overlap = shape_overlap(right, left, true);
313
314    //Ensure that the vector points from left to right
315    let r_flipped = (true, r_overlap.1, (-r_overlap.2.0, -r_overlap.2.1));
316
317    let overlap = if l_overlap.1 < r_flipped.1 { l_overlap } else { r_flipped };
318
319    return (overlap.1 * overlap.2.0, overlap.1 * overlap.2.1);
320
321}
322
323/// Returns true if the given shape contains the specified point, and false if
324/// it does not. Does not work for degenerate polygons.
325///
326/// This method performs a floating point comparison with Rust's built in epsilon constant, so it may
327/// return the incorrect answer for shapes which are very small or very close to the point.
328///
329/// Requires the shape to be convex.
330///
331/// # Examples
332///
333/// ```
334/// use sepax2d::prelude::*;
335///
336/// let square = Polygon::from_vertices((1.0, 1.0), vec![(-1.0, 1.0), (1.0, 1.0), (1.0, -1.0), (-1.0, -1.0)]);
337/// let triangle = Polygon::from_vertices((0.0, 0.0), vec![(2.0, 2.0), (0.0, -2.0), (-1.0, 0.0)]);
338///
339/// assert!(contains_point(&triangle, (0.5, 0.5)));
340/// assert!(!contains_point(&square, (-2.0, 2.0)));
341/// ```
342pub fn contains_point(shape: &(impl Shape + ?Sized), point: (f32, f32)) -> bool
343{
344
345    let polygon = polygon::Polygon::from_vertices(point, vec![(0.0, 0.0)]);
346
347    return shape_overlap(shape, &polygon, false).0;
348
349}
350
351fn shape_overlap(axes: &(impl Shape + ?Sized), projected: &(impl Shape + ?Sized), normalize: bool) -> (bool, f32, (f32, f32))
352{
353
354    let mut min_overlap = f32::MAX;
355    let mut min_axis = (0.0, 0.0);
356
357    let num_axes = axes.num_axes();
358    for i in 0..num_axes
359    {
360
361        let closest = if axes.needs_closest(i) { projected.get_closest(axes.point(i)) } else { (0.0, 0.0) };
362        let mut axis = axes.get_axis(i, closest);
363
364        //If we are just checking for overlap, we can skip normalizing the axis. However,
365        //we need to normalize to find the minimum penetration vector.
366        if normalize
367        {
368
369            let length = f32::sqrt((axis.0 * axis.0) + (axis.1 * axis.1));
370                    
371            if length > f32::EPSILON
372            {
373
374                axis = (axis.0 / length, axis.1 / length);
375
376            }
377
378        }
379
380        let (min_l, max_l) = axes.project(axis, normalize);
381        let (min_r, max_r) = projected.project(axis, normalize);
382
383        //If there is no overlap, we can return early
384        if min_l > max_r - f32::EPSILON || min_r > max_l - f32::EPSILON
385        {
386
387            return (false, 0.0, (0.0, 0.0));
388
389        }
390
391        let overlap = f32::min(max_l - min_r, max_r - min_l);
392        if overlap < min_overlap
393        {
394
395            min_overlap = overlap;
396            min_axis = axis;
397
398        }
399
400    }
401
402    //Ensure that the chosen axis of penetration points from axes to projected
403    let axes_position = axes.position();
404    let projected_position = projected.position();
405    let difference = (projected_position.0 - axes_position.0, projected_position.1 - axes_position.1);
406    if (difference.0 * min_axis.0 + difference.1 * min_axis.1) < -f32::EPSILON
407    {
408
409        min_axis.0 *= -1.0;
410        min_axis.1 *= -1.0;
411
412    }
413
414    return (true, min_overlap, min_axis);
415
416}
417
418fn project(position: (f32, f32), axis: (f32, f32), points: &[(f32, f32)]) -> (f32, f32)
419{
420
421    let mut min = f32::MAX;
422    let mut max = f32::MIN;
423
424    for (x, y) in points
425    {
426
427        let position = (position.0 + *x, position.1 + *y);
428
429        let projection = (position.0 * axis.0) + (position.1 * axis.1);
430
431        min = f32::min(min, projection);
432        max = f32::max(max, projection);
433
434    }
435
436    return (min, max);
437
438}
439
440fn closest(position: (f32, f32), target: (f32, f32), points: &[(f32, f32)]) -> (f32, f32)
441{
442
443    let mut point = (0.0, 0.0);
444    let mut min = f32::MAX;
445
446    for (x, y) in points
447    {
448
449        let position = (position.0 + *x, position.1 + *y);
450        let dist_square = (position.0 - target.0) * (position.0 - target.0) + (position.1 - target.1) * (position.1 - target.1);
451
452        if dist_square < min
453        {
454
455            point = position;
456            min = dist_square;
457
458        }
459
460    }
461
462    return point;
463
464}
465
466#[allow(dead_code)]
467fn float_equal(left: f32, right: f32) -> bool
468{
469
470    return (left - right).abs() < 0.00001;
471
472}
473
474#[cfg(test)]
475mod sat_tests
476{
477
478    use super::*;
479    use super::prelude::*;
480
481    #[test]
482    fn test_sat_overlap()
483    {
484
485        //Polygons
486        let square = Polygon::from_vertices((0.0, 0.0), vec![(0.0, 0.0), (4.0, 0.0), (4.0, 4.0), (0.0, 4.0)]);
487        let triangle = Polygon::from_vertices((2.0, 2.0), vec![(-1.0, 1.0), (0.0, -1.0), (1.0, 1.0)]);
488        let pentagon = Polygon::from_vertices((-3.0, 0.0), vec![(2.0, 0.0), (4.0, 1.0), (2.0, 2.0), (0.0, 2.0), (0.0, 0.0)]);
489        let triangle2 = Polygon::from_vertices((4.0, 3.0), vec![(-2.0, 1.0), (-1.0, -2.0), (2.0, 0.0)]);
490
491        assert!(sat_overlap(&pentagon, &pentagon));
492        assert!(sat_overlap(&square, &triangle));
493        assert!(sat_overlap(&triangle, &square));
494        assert!(sat_overlap(&square, &pentagon));
495        assert!(sat_overlap(&pentagon, &square));
496        assert!(sat_overlap(&square, &triangle2));
497        assert!(sat_overlap(&triangle2, &square));
498        assert!(sat_overlap(&triangle, &triangle2));
499        assert!(sat_overlap(&triangle, &triangle2));
500
501        //Circles
502        let circle1 = Circle::new((-1.0, -1.0), 2.0);
503        let circle2 = Circle::new((-2.0, -1.0), 1.1);
504
505        assert!(sat_overlap(&circle1, &square));
506        assert!(sat_overlap(&circle2, &pentagon));
507        assert!(sat_overlap(&pentagon, &circle1));
508        assert!(sat_overlap(&circle1, &circle2));
509
510    }
511
512    #[test]
513    fn test_sat_no_overlap()
514    {
515
516        //Polygons
517        let triangle = Polygon::from_vertices((2.0, 2.0), vec![(-1.0, 1.0), (0.0, -1.0), (1.0, 1.0)]);
518        let pentagon = Polygon::from_vertices((-3.0, 0.0), vec![(2.0, 0.0), (4.0, 1.0), (2.0, 2.0), (0.0, 2.0), (0.0, 0.0)]);
519        let triangle2 = Polygon::from_vertices((4.0, 3.0), vec![(-2.0, 1.0), (-1.0, -2.0), (2.0, 0.0)]);
520
521        assert!(!sat_overlap(&pentagon, &triangle));
522        assert!(!sat_overlap(&triangle, &pentagon));
523        assert!(!sat_overlap(&pentagon, &triangle2));
524        assert!(!sat_overlap(&triangle2, &pentagon));
525
526        //Circles
527        let circle1 = Circle::new((-1.0, -1.0), 2.0);
528        let circle2 = Circle::new((-2.0, -1.0), 1.1);
529        let circle3 = Circle::new((2.0, 5.0), 1.0);
530
531        assert!(!sat_overlap(&triangle, &circle1));
532        assert!(!sat_overlap(&circle2, &triangle2));
533        assert!(!sat_overlap(&circle1, &circle3));
534        assert!(!sat_overlap(&circle3, &circle2));
535
536    }
537
538    #[test]
539    fn test_sat_collision()
540    {
541
542        //Polygons
543        let square = Polygon::from_vertices((0.0, 0.0), vec![(0.0, 0.0), (4.0, 0.0), (4.0, 4.0), (0.0, 4.0)]);
544        let rectangle = Polygon::from_vertices((1.0, -2.0), vec![(0.0, 0.0), (1.0, 0.0), (1.0, 2.1), (0.0, 2.1)]);
545        let triangle = Polygon::from_vertices((0.0, 0.0), vec![(0.5, 0.6), (0.0, -1.0), (-0.5, 0.6)]);
546        let triangle2 = Polygon::from_vertices((-0.4, -0.4), vec![(0.0, 0.0), (1.0, 0.0), (0.0, 1.0)]);
547
548        let resolution = sat_collision(&square, &rectangle);
549        assert!(float_equal(resolution.0, 0.0));
550        assert!(float_equal(resolution.1, -0.1));
551
552        let resolution1 = sat_collision(&rectangle, &square);
553        assert!(float_equal(resolution1.0, 0.0));
554        assert!(float_equal(resolution1.1, 0.1));
555
556        let resolution2 = sat_collision(&square, &triangle);
557        assert!(float_equal(resolution2.0, -0.5));
558        assert!(float_equal(resolution2.1, 0.0));
559
560        let resolution3 = sat_collision(&square, &triangle2);
561        assert!(float_equal((resolution3.0 * -1.0) + (resolution3.1), 0.0)); //Assert that the minimum axis of penetration is (1,1)
562
563        //Circles
564        let circle = Circle::new((2.0, -0.5), 1.0);
565        let circle2 = Circle::new((0.0, -0.5), 1.1);
566
567        let resolution4 = sat_collision(&circle, &square);
568        assert!(float_equal(resolution4.0, 0.0));
569        assert!(float_equal(resolution4.1, 0.5));
570
571        let resolution5 = sat_collision(&circle, &triangle2);
572        assert!(float_equal(resolution5.0, 0.0));
573        assert!(float_equal(resolution5.1, 0.0));
574
575        let resolution6 = sat_collision(&circle2, &circle);
576        assert!(float_equal(resolution6.0, 0.1));
577        assert!(float_equal(resolution6.1, 0.0));
578
579    }
580
581    #[test]
582    fn test_capsule_collision()
583    {
584
585        let capsule = Capsule::new((0.0, 0.0), (0.0, 2.0), 2.0);
586
587        let triangle = Polygon::from_vertices((0.0, 5.0), vec![(0.0, -2.0), (-1.0, 2.0), (1.0, 2.0)]);
588        let rectangle = AABB::new((-4.0, 4.0), 2.5, 0.5);
589        let circle1 = Circle::new((2.0, -2.5), 1.0);
590        let circle2 = Circle::new((3.0, 0.0), 1.5);
591
592        assert!(sat_overlap(&capsule, &circle1));
593        assert!(!sat_overlap(&rectangle, &capsule));
594
595        let resolution1 = sat_collision(&circle2, &capsule);
596        let resolution2 = sat_collision(&capsule, &triangle);
597
598        assert!(float_equal(resolution1.0, -0.5));
599        assert!(float_equal(resolution1.1, 0.0));
600        assert!(float_equal(resolution2.0, 0.0));
601        assert!(float_equal(resolution2.1, 1.0));
602
603    }
604
605    #[test]
606    fn test_parallelogram_collision()
607    {
608
609        let gram = Parallelogram::new((0.0, -0.5), (2.0, 1.0), (-1.0, -1.0));
610
611        let triangle = Polygon::from_vertices((0.0, 5.0), vec![(0.0, -2.0), (-1.0, 2.0), (1.0, 2.0)]);
612        let rectangle = AABB::new((-1.0, 0.0), 4.0, 2.0);
613        let circle = Circle::new((2.0, -2.5), 1.0);
614
615        assert!(!sat_overlap(&gram, &triangle));
616        assert!(!sat_overlap(&gram, &circle));
617
618        assert!(sat_overlap(&gram, &rectangle));
619
620        let resolution = sat_collision(&rectangle, &gram);
621        println!("{:?}", resolution);
622        
623        assert!(float_equal(resolution.0, 0.0));
624        assert!(float_equal(resolution.1, -0.5));
625
626    }
627
628    #[test]
629    fn test_contains_point()
630    {
631
632        let capsule = Capsule::new((0.0, 0.0), (0.0, 2.0), 2.0);
633        let triangle = Polygon::from_vertices((0.0, 5.0), vec![(0.0, -2.0), (-1.0, 2.0), (1.0, 2.0)]);
634        let rectangle = AABB::new((-4.0, 4.0), 2.5, 0.5);
635        let circle = Circle::new((2.0, -2.5), 1.0);
636
637        assert!(contains_point(&capsule, (1.0, 1.0)));
638        assert!(contains_point(&triangle, (0.0, 5.0)));
639        assert!(contains_point(&rectangle, (-2.0, 4.1)));
640        assert!(contains_point(&circle, (2.5, -3.0)));
641
642
643        assert!(!contains_point(&capsule, (2.0, 4.0)));
644        assert!(!contains_point(&triangle, (0.0, 0.0)));
645        assert!(!contains_point(&rectangle, (-4.0, 3.9)));
646        assert!(!contains_point(&circle, (1.5, -3.5)));
647
648    }
649
650    #[test]
651    fn test_rotate()
652    {
653
654        let mut triangle = Polygon::from_vertices((0.0, 0.0), vec![(0.0, 0.0), (2.0, 0.0), (0.0, 1.0)]);
655        let mut capsule = Capsule::new((2.0, 1.0), (2.0, 0.0), 4.0);
656        let mut gram = Parallelogram::new((3.0, 4.0), (2.0, 1.0), (-1.0, 1.0));
657
658        capsule.rotate(std::f32::consts::FRAC_PI_4);
659
660        assert!(float_equal(capsule.arm().0, 2.0 / f32::sqrt(2.0)));
661        assert!(float_equal(capsule.arm().1, 2.0 / f32::sqrt(2.0)));
662        assert!(float_equal(capsule.perp().0, -4.0 / f32::sqrt(2.0)));
663        assert!(float_equal(capsule.perp().1, 4.0 / f32::sqrt(2.0)));
664
665        let sin = f32::sin(std::f32::consts::PI);
666        let cos = f32::cos(std::f32::consts::PI);
667
668        triangle.rotate_sincos(sin, cos);
669
670        assert!(float_equal(triangle.vertices[1].0, -2.0));
671        assert!(float_equal(triangle.vertices[1].1, 0.0));
672        assert!(float_equal(triangle.vertices[2].0, 0.0));
673        assert!(float_equal(triangle.vertices[2].1, -1.0));
674
675        gram.rotate_sincos(sin, cos);
676
677        assert!(float_equal(gram.u.0, -2.0));
678        assert!(float_equal(gram.u.1, -1.0));
679        assert!(float_equal(gram.v.0, 1.0));
680        assert!(float_equal(gram.v.1, -1.0));
681
682    }
683
684}
685
686pub mod prelude
687{
688
689    pub use crate::{sat_overlap, sat_collision, contains_point};
690
691    pub use crate::Shape;
692    pub use crate::Rotate;
693
694    pub use crate::polygon::Polygon;
695    pub use crate::circle::Circle;
696    pub use crate::aabb::AABB;
697    pub use crate::capsule::Capsule;
698    pub use crate::parallelogram::Parallelogram;
699
700    pub use crate::line::intersects_line;
701    pub use crate::line::intersects_ray;
702    pub use crate::line::intersects_segment;
703
704}