use super::*;
#[derive(Default, Clone, Copy, Debug, PartialEq)]
pub struct RoundedBox2d {
pub halfsize: Vec2,
pub radius: f32,
}
impl RoundedBox2d {
pub const fn new(halfsize: Vec2, radius: f32) -> Self {
Self { halfsize, radius }
}
pub fn box_part(&self) -> Box2d {
Box2d::new(self.halfsize)
}
}
impl SymmetricBoundingBox2d for RoundedBox2d {
fn symmetric_bounding_box(&self) -> Box2d {
Box2d::with_halfdims(self.halfsize.x + self.radius, self.halfsize.y + self.radius)
}
}
impl ExtremePoint2d for RoundedBox2d {
fn extreme_point(&self, direction: Vec2) -> Vec2 {
let a = Ball::new(self.radius);
let b = Box2d::new(self.halfsize);
a.extreme_point(direction) + b.extreme_point(direction)
}
}
impl MinkowskiNegationIsIdentity for RoundedBox2d {}
#[cfg(disable)]
impl CollidesRel2d<Point> for RoundedBox2d {
fn collides(&self, t: &Point, delta: &Vec2) -> bool {
let p = self.extreme_point(delta);
delta.length_squared() <= delta.dot(p)
}
}
impl CollidesRel2d<Point> for RoundedBox2d {
fn collides_rel(&self, t: &Point, rel: &impl Transformation2d) -> bool {
let bbox = self.symmetric_bounding_box();
if bbox.collides_rel(t, rel) {
let delta = rel.apply_origin();
let x0 = -self.halfsize.x < delta.x;
let x1 = delta.x < self.halfsize.x;
if x0 && x1 {
return true;
}
let y0 = -self.halfsize.y < delta.y;
let y1 = delta.y < self.halfsize.y;
if y0 && y1 {
return true;
}
let c = Ball::with_radius(self.radius);
if x0 {
if y0 {
let cdelta = delta - self.halfsize;
c.collides_rel(t, &cdelta)
} else {
let cdelta = delta + Vec2::new(-self.halfsize.x, self.halfsize.y);
c.collides_rel(t, &cdelta)
}
} else {
if y0 {
let cdelta = delta + Vec2::new(self.halfsize.x, -self.halfsize.y);
c.collides_rel(t, &cdelta)
} else {
let cdelta = delta + self.halfsize;
c.collides_rel(t, &cdelta)
}
}
} else {
false
}
}
}
impl PenetratesRel2d<Point> for RoundedBox2d {
fn penetrates_rel(&self, t: &Point, rel: &impl Transformation2d) -> Option<Vec2> {
let bbox = self.symmetric_bounding_box();
if bbox.collides_rel(&Point, rel) {
let delta = rel.apply_origin();
let x0 = -self.halfsize.x < delta.x;
let x1 = delta.x < self.halfsize.x;
let middle_x = x0 && x1;
let y0 = -self.halfsize.y < delta.y;
let y1 = delta.y < self.halfsize.y;
let middle_y = y0 && y1;
if middle_x || middle_y {
return bbox.penetrates_rel(&Point, &delta);
}
let c = Ball::with_radius(self.radius);
if x0 {
if y0 {
let cdelta = delta - self.halfsize;
c.penetrates_rel(t, &cdelta)
} else {
let cdelta = delta + Vec2::new(-self.halfsize.x, self.halfsize.y);
c.penetrates_rel(t, &cdelta)
}
} else {
if y0 {
let cdelta = delta + Vec2::new(self.halfsize.x, -self.halfsize.y);
c.penetrates_rel(t, &cdelta)
} else {
let cdelta = delta + self.halfsize;
c.penetrates_rel(t, &cdelta)
}
}
} else {
None
}
}
}
#[cfg(disable)]
impl CollidesRel2d<RoundedBox2d> for RoundedBox2d {
fn collides(&self, t: &RoundedBox2d, delta: &Vec2) -> bool {
self.minkowski_difference(t).collides(&Point, &delta)
}
}
#[cfg(disable)]
impl PenetratesRel2d<RoundedBox2d> for RoundedBox2d {
fn penetrates(&self, t: &RoundedBox2d, delta: &Vec2) -> Option<Vec2> {
Point.penetrates(&self.minkowski_difference(t), &delta)
}
}
impl SdfRel2d<Point> for RoundedBox2d {
fn sdf_rel(&self, t: &Point, rel: &impl Transformation2d) -> f32 {
SdfRel2d::sdf_rel(&self.box_part(), t, rel) - self.radius
}
}
impl SdfRel2dVector<Point> for RoundedBox2d {
fn sdfv_rel(&self, t: &Point, rel: &impl Transformation2d) -> Vec2 {
let delta = rel.apply_origin();
let delta_x = delta.x.abs() - self.halfsize.x;
let delta_y = delta.y.abs() - self.halfsize.y;
if self.box_part().collides_rel(t, rel) {
if delta_x > delta_y {
return Vec2::new((delta_x - self.radius) * delta.x.signum(), 0.0);
} else {
return Vec2::new(0.0, (delta_y - self.radius) * delta.y.signum());
}
} else {
if delta_x <= 0.0 {
return Vec2::new(0.0, (delta_y - self.radius) * delta.y.signum());
} else if delta_y <= 0.0 {
return Vec2::new((delta_x - self.radius) * delta.x.signum(), 0.0);
} else {
let corner = Vec2::new(delta_x * delta.x.signum(), delta_y * delta.y.signum());
let corner_length = corner.length();
let corner = corner * (corner_length - self.radius) / corner_length;
return corner;
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
}