phys-collision 2.0.1-beta.0

Provides collision detection ability
// Copyright (C) 2020-2025 phys-collision authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use glam_det::nums::*;
use glam_det::{Vec2x4, Vec3x4};

use crate::shapes::CylinderWide;

pub(crate) const FRAC_1_SQRT_2: f32x4 = f32x4::const_splat(std::f32::consts::FRAC_1_SQRT_2);

pub(super) const EPS_3: f32x4 = f32x4::const_splat(1e-3);
pub(super) const EPS_5: f32x4 = f32x4::const_splat(1e-5);
pub(super) const EPS_6: f32x4 = f32x4::const_splat(1e-6);
pub(super) const EPS_7: f32x4 = f32x4::const_splat(1e-7);
pub(super) const EPS_8: f32x4 = f32x4::const_splat(1e-8);
pub(super) const EPS_10: f32x4 = f32x4::const_splat(1e-10);
pub(super) const EPS_12: f32x4 = f32x4::const_splat(1e-12);
pub(super) const EPS_14: f32x4 = f32x4::const_splat(1e-14);
pub(super) const EPS_15: f32x4 = f32x4::const_splat(1e-15);

#[must_use]
pub fn generate_interior_points(
    cylinder: &CylinderWide,
    cylinder_local_normal: Vec3x4,
    local_closest_on_cylinder: Vec3x4,
) -> (Vec2x4, Vec2x4, Vec2x4, Vec2x4) {
    // Assume we can just use the 4 local extreme points of the cylinder at first.
    // Then, if there is sufficient tilt, replace the closest extreme point to
    // the deepest point with the deepest point.
    let interpolation_min = f32x4::splat(0.9999);
    let inverse_interpolation_span = f32x4::splat(1.0 / 0.00005);
    let parallel_weight = ((cylinder_local_normal.y.absf() - interpolation_min)
        * inverse_interpolation_span)
        .clamp(f32x4::ZERO, f32x4::ONE);
    let deepest_weight = f32x4::ONE - parallel_weight;
    let replace_x = local_closest_on_cylinder
        .x
        .absf()
        .gt(local_closest_on_cylinder.z.absf());
    let replace_0 = local_closest_on_cylinder.x.gt(f32x4::ZERO) & replace_x;
    let replace_1 = local_closest_on_cylinder.x.le(f32x4::ZERO) & replace_x;
    let replace_2 = local_closest_on_cylinder.z.gt(f32x4::ZERO) & !replace_x;
    let replace_3 = local_closest_on_cylinder.z.le(f32x4::ZERO) & !replace_x;
    let scaled_radius = parallel_weight * cylinder.radius;
    let scaled_closest_x = deepest_weight * local_closest_on_cylinder.x;
    let scaled_closest_z = deepest_weight * local_closest_on_cylinder.z;
    let interior_0 = Vec2x4::new(
        (scaled_closest_x + scaled_radius).select(replace_0, cylinder.radius),
        scaled_closest_z.select(replace_0, f32x4::ZERO),
    );
    let interior_1 = Vec2x4::new(
        (scaled_closest_x - scaled_radius).select(replace_1, -cylinder.radius),
        scaled_closest_z.select(replace_1, f32x4::ZERO),
    );
    let interior_2 = Vec2x4::new(
        scaled_closest_x.select(replace_2, f32x4::ZERO),
        (scaled_closest_z + scaled_radius).select(replace_2, cylinder.radius),
    );
    let interior_3 = Vec2x4::new(
        scaled_closest_x.select(replace_3, f32x4::ZERO),
        (scaled_closest_z - scaled_radius).select(replace_3, -cylinder.radius),
    );

    (interior_0, interior_1, interior_2, interior_3)
}

pub(super) trait NormalizeExt: Sized {
    type Length;

    fn normalize_and_length_or(&self, default: Self, epsilon: Self::Length)
        -> (Self::Length, Self);

    #[inline]
    fn normalize_or(&self, default: Self, epsilon: Self::Length) -> Self {
        self.normalize_and_length_or(default, epsilon).1
    }
}

macro_rules! impl_normalize_ext {
    ($length_type:ty, $($type:ty),*) => {
        $(
            impl NormalizeExt for $type {
                type Length = $length_type;

                #[inline]
                fn normalize_and_length_or(
                    &self,
                    default: Self,
                    epsilon: Self::Length,
                ) -> (Self::Length, Self) {
                    let length = self.length();
                    let length_recip = length.recip();
                    (
                        length,
                        Self::lane_select(length.gt(epsilon), self * length_recip, default),
                    )
                }
            }
        )*
    };
}

impl_normalize_ext!(f32x4, Vec3x4, Vec2x4);