use crate::circle::Circle;
pub struct Capsule
{
pub position: (f32, f32),
arm: (f32, f32),
perp: (f32, f32),
pub radius: f32
}
impl Capsule
{
pub fn new(position: (f32, f32), arm: (f32, f32), radius: f32) -> Capsule
{
return Capsule { position, arm, radius, perp: Capsule::set_perp(arm, radius) };
}
fn set_perp(arm: (f32, f32), radius: f32) -> (f32, f32)
{
let length = f32::sqrt((arm.0 * arm.0) + (arm.1 * arm.1));
let mut perp = (-arm.1, arm.0);
if length > f32::EPSILON
{
perp = ((perp.0 * radius) / length, (perp.1 * radius) / length);
}
return perp;
}
pub fn set_radius(&mut self, radius: f32)
{
self.radius = radius;
self.perp = Capsule::set_perp(self.arm, self.radius);
}
pub fn radius(&self) -> f32
{
return self.radius;
}
pub fn set_arm(&mut self, arm: (f32, f32))
{
self.arm = arm;
self.perp = Capsule::set_perp(self.arm, self.radius);
}
pub fn arm(&self) -> (f32, f32)
{
return self.arm;
}
pub fn perp(&self) -> (f32, f32)
{
return self.perp;
}
fn points(&self) -> [(f32, f32); 6]
{
return
[
self.arm,
(-self.arm.0, -self.arm.1),
(self.arm.0 + self.perp.0, self.arm.1 + self.perp.1),
(self.arm.0 - self.perp.0, self.arm.1 - self.perp.1),
(-self.arm.0 + self.perp.0, -self.arm.1 + self.perp.1),
(-self.arm.0 - self.perp.0, -self.arm.1 - self.perp.1)
];
}
}
impl crate::Shape for Capsule
{
fn position(&self) -> (f32, f32)
{
return self.position;
}
fn set_position(&mut self, position: (f32, f32))
{
self.position = position;
}
fn num_axes(&self) -> usize
{
return 4;
}
fn get_axis(&self, index: usize, target: (f32, f32)) -> (f32, f32)
{
return match index
{
0 => self.arm,
1 => self.perp,
2 => (target.0 - self.position.0 - self.arm.0, target.1 - self.position.1 - self.arm.1),
_ => (target.0 - self.position.0 + self.arm.0, target.1 - self.position.1 + self.arm.1)
};
}
fn project(&self, axis: (f32, f32), normalize: bool) -> (f32, f32)
{
let circle1 = Circle::new((self.position.0 + self.arm.0, self.position.1 + self.arm.1), self.radius);
let circle2 = Circle::new((self.position.0 - self.arm.0, self.position.1 - self.arm.1), self.radius);
let projection1 = circle1.project(axis, normalize);
let projection2 = circle2.project(axis, normalize);
return (f32::min(projection1.0, projection2.0), f32::max(projection1.1, projection2.1));
}
fn needs_closest(&self, index: usize) -> bool
{
return match index
{
0 => false,
1 => false,
2 => true,
_ => true
};
}
fn get_closest(&self, target: (f32, f32)) -> (f32, f32)
{
return crate::closest(self.position, target, &self.points());
}
fn point(&self, index: usize) -> (f32, f32)
{
return match index
{
0 => (self.position.0 + self.arm.0 + self.perp.0, self.position.1 + self.arm.1 + self.perp.1),
1 => (self.position.0 + self.arm.0 + self.perp.0, self.position.1 + self.arm.1 + self.perp.1),
2 => (self.position.0 + self.arm.0, self.position.1 + self.arm.1),
_ => (self.position.0 - self.arm.0, self.position.1 - self.arm.1)
};
}
}
#[cfg(test)]
mod capsule_tests
{
use super::*;
use crate::{float_equal, Shape};
#[test]
fn test_num_axes()
{
let capsule = Capsule::new((1.0, 2.0), (2.0, 3.0), 3.0);
assert_eq!(capsule.num_axes(), 4);
}
#[test]
fn test_get_axis()
{
let capsule = Capsule::new((0.0, 0.0), (0.0, 2.0), 1.0);
let axis1 = capsule.get_axis(0, (4.0, 2.0));
let axis2 = capsule.get_axis(1, (4.0, 2.0));
let axis3 = capsule.get_axis(2, (4.0, 2.0));
let axis4 = capsule.get_axis(3, (4.0, 2.0));
assert!(float_equal(axis1.0, 0.0));
assert!(float_equal(axis1.1, 2.0));
assert!(float_equal(axis2.0, -1.0));
assert!(float_equal(axis2.1, 0.0));
assert!(float_equal(axis3.0, 4.0));
assert!(float_equal(axis3.1, 0.0));
assert!(float_equal(axis4.0, 4.0));
assert!(float_equal(axis4.1, 4.0));
}
#[test]
fn test_project()
{
let capsule = Capsule::new((1.0, 2.0), (1.0, 1.0), 2.0);
let axis1 = (0.0, 1.0);
let axis2 = (-1.0, 2.0);
let projection1 = capsule.project(axis1, true);
let projection2 = capsule.project(axis2, false);
assert!(float_equal(projection1.0, -1.0));
assert!(float_equal(projection1.1, 5.0));
assert!(float_equal(projection2.0, 2.0 - 2.0 * f32::sqrt(5.0)));
assert!(float_equal(projection2.1, 4.0 + 2.0 * f32::sqrt(5.0)));
}
#[test]
fn test_needs_closest()
{
let capsule = Capsule::new((8.0, 10.0), (-2.5, 3.0), 3.4);
assert!(!capsule.needs_closest(0));
assert!(!capsule.needs_closest(1));
assert!(capsule.needs_closest(2));
assert!(capsule.needs_closest(3));
}
}