thimni 0.3.2

efficient SDF collision without discretizatio, neural nets, or interval arithmetic
Documentation
use macroquad::prelude::*;
use thimni::sdf::SDF;
use thimni::utils::{AABB, CollisionParameters};

const BALL_SPEED: f32 = 50.0;

// workaround for inability to derive an external trait for an external struct
struct MRect(Rect);
struct MCircle(Circle);

impl SDF<2, ::glam::Vec2> for MCircle {
    fn dist(&self, p: ::glam::Vec2) -> f32 {
        let k = p - ::glam::Vec2::new(self.0.x, self.0.y);

        k.length() - self.0.r
    }

    fn gradient(&self, p: ::glam::Vec2, _params: &CollisionParameters) -> ::glam::Vec2 {
        let k = p - ::glam::Vec2::new(self.0.x, self.0.y);

        k.normalize_or_zero()
    }

    fn aabb(&self) -> AABB<2, ::glam::Vec2> {
        AABB {
            min: ::glam::Vec2::new(self.0.x - self.0.r, self.0.y - self.0.r),
            max: ::glam::Vec2::new(self.0.x + self.0.r, self.0.y + self.0.r),
        }
    }
}

impl SDF<2, ::glam::Vec2> for MRect {
    fn dist(&self, p: ::glam::Vec2) -> f32 {
        let b = ::glam::Vec2 {
            x: self.0.w * 0.5,
            y: self.0.h * 0.5,
        };
        let c = ::glam::Vec2 {
            x: self.0.x,
            y: self.0.y,
        } + b;

        let w = (p - c).abs() - b;

        w.max_element() //.min(0.0) - 0.0
    }

    fn aabb(&self) -> AABB<2, ::glam::Vec2> {
        let top_left = ::glam::Vec2::new(self.0.x, self.0.y);
        let bottom_rigt = top_left + ::glam::Vec2::new(self.0.w, self.0.h);
        AABB {
            min: top_left.min(bottom_rigt),
            max: top_left.max(bottom_rigt),
        }
    }
}

#[macroquad::main("twod")]
async fn main() {
    let mut collision_params = CollisionParameters::default();

    collision_params.normal_epsilon = 0.01;
    collision_params.area_percentage = 0.95;

    let shape1 = MRect(Rect::new(100.0, 200.0, 70.0, 90.0));

    let shape3 = MCircle(Circle::new(400.0, 200.0, 100.0));

    let mut shape2 = MCircle(Circle::new(300.0, 400.0, 100.0));
    let mut shape2_vel = Vec2::ZERO;

    loop {
        clear_background(BLACK);

        shape2.0.x += shape2_vel.x * BALL_SPEED * get_frame_time();
        shape2.0.y += shape2_vel.y * BALL_SPEED * get_frame_time();

        if is_key_down(KeyCode::W) {
            shape2_vel.y += 1.0;
        }
        if is_key_down(KeyCode::S) {
            shape2_vel.y -= 1.0;
        }
        if is_key_down(KeyCode::D) {
            shape2_vel.x -= 1.0;
        }
        if is_key_down(KeyCode::A) {
            shape2_vel.x += 1.0;
        }

        shape2_vel = shape2_vel.lerp(Vec2::ZERO, get_frame_time() * 100.0);
        shape2_vel = shape2_vel.normalize_or_zero();

        draw_rectangle(shape1.0.x, shape1.0.y, shape1.0.w, shape1.0.h, RED);

        draw_circle(shape2.0.x, shape2.0.y, shape2.0.r, BLUE);

        draw_circle(shape3.0.x, shape3.0.y, shape3.0.r, PINK);

        if let Some(res) = shape2.get_coll_point(&shape1, &mut collision_params) {
            let l = res.gradient.length();
            draw_line(
                res.point.x,
                res.point.y,
                res.point.x + res.gradient.x * l * 40.0,
                res.point.y + res.gradient.y * l * 40.0,
                5.0,
                PINK,
            );

            draw_circle(res.point.x, res.point.y, 5.0, PURPLE);

            shape2_vel.x = res.gradient.x;
            shape2_vel.y = res.gradient.y;

            let depth = shape2.sum_gradient_depth(&shape1, &collision_params, &res);

            shape2_vel.x += res.gradient.x * depth;
            shape2_vel.y += res.gradient.y * depth;
        };

        if let Some(res) = shape2.get_coll_point(&shape3, &mut collision_params) {
            let l = res.gradient.length();
            draw_line(
                res.point.x,
                res.point.y,
                res.point.x + res.gradient.x * l * 40.0,
                res.point.y + res.gradient.y * l * 40.0,
                5.0,
                PINK,
            );

            draw_circle(res.point.x, res.point.y, 5.0, PURPLE);

            let depth = shape2.sum_gradient_depth(&shape3, &collision_params, &res);

            shape2_vel.x += res.gradient.x * depth;
            shape2_vel.y += res.gradient.y * depth;
        };

        draw_text(&format!("{}", get_fps()), 70.0, 70.0, 50.0, YELLOW);

        next_frame().await
    }
}