use rapier2d::prelude::*;
use super::ascii_art::get_ascii_art;
use super::types::Shape;
const CHAR_TO_PHYSICS_SCALE: f32 = 1.0;
pub fn create_shape_collider(
shape: &Shape,
rigid_body_set: &mut RigidBodySet,
collider_set: &mut ColliderSet,
) -> (RigidBodyHandle, ColliderHandle) {
let (x, y) = shape.position();
let rotation_rad = shape.rotation_radians();
let rigid_body = RigidBodyBuilder::fixed()
.translation(vector![x, y])
.rotation(rotation_rad)
.build();
let body_handle = rigid_body_set.insert(rigid_body);
let ascii_art = get_ascii_art(shape.shape_type());
let vertices = ascii_art.collision_vertices(shape.shape_type());
let points: Vec<Point<Real>> = vertices
.iter()
.map(|(vx, vy)| point![vx * CHAR_TO_PHYSICS_SCALE, vy * CHAR_TO_PHYSICS_SCALE])
.collect();
let collider = if let Some(hull) = ColliderBuilder::convex_hull(&points) {
hull.friction(0.3).restitution(0.7).build()
} else {
let radius =
(ascii_art.width().max(ascii_art.height()) as f32 / 2.0) * CHAR_TO_PHYSICS_SCALE;
ColliderBuilder::ball(radius)
.friction(0.3)
.restitution(0.7)
.build()
};
let collider_handle = collider_set.insert_with_parent(collider, body_handle, rigid_body_set);
(body_handle, collider_handle)
}
pub fn update_shape_transform(shape: &Shape, rigid_body_set: &mut RigidBodySet) {
if let Some(handle) = shape.rigid_body_handle() {
if let Some(body) = rigid_body_set.get_mut(handle) {
let (x, y) = shape.position();
let rotation = shape.rotation_radians();
let isometry = Isometry::new(vector![x, y], rotation);
body.set_position(isometry, true);
}
}
}
pub fn remove_shape_collider(
shape: &Shape,
rigid_body_set: &mut RigidBodySet,
collider_set: &mut ColliderSet,
island_manager: &mut IslandManager,
impulse_joint_set: &mut ImpulseJointSet,
multibody_joint_set: &mut MultibodyJointSet,
) {
if let Some(collider_handle) = shape.collider_handle() {
collider_set.remove(collider_handle, island_manager, rigid_body_set, true);
}
if let Some(body_handle) = shape.rigid_body_handle() {
rigid_body_set.remove(
body_handle,
island_manager,
collider_set,
impulse_joint_set,
multibody_joint_set,
true,
);
}
}
pub fn point_in_shape(
shape: &Shape,
point_x: f32,
point_y: f32,
collider_set: &ColliderSet,
) -> bool {
if let Some(collider_handle) = shape.collider_handle() {
if let Some(collider) = collider_set.get(collider_handle) {
let point = point![point_x, point_y];
let margin = 0.5;
return collider
.shape()
.contains_local_point(&collider.position().inverse_transform_point(&point))
|| {
let proj = collider.shape().project_local_point(
&collider.position().inverse_transform_point(&point),
true,
);
proj.point.coords.norm() < margin
};
}
}
false
}
pub fn shape_bounds(shape: &Shape) -> (f32, f32, f32, f32) {
let (x, y) = shape.position();
let ascii_art = get_ascii_art(shape.shape_type());
let half_width = (ascii_art.width() as f32 / 2.0) * CHAR_TO_PHYSICS_SCALE;
let half_height = (ascii_art.height() as f32 / 2.0) * CHAR_TO_PHYSICS_SCALE;
let max_dim = half_width.max(half_height);
(x - max_dim, y - max_dim, x + max_dim, y + max_dim)
}
pub fn shapes_overlap(shape1: &Shape, shape2: &Shape, min_separation: f32) -> bool {
let (min_x1, min_y1, max_x1, max_y1) = shape_bounds(shape1);
let (min_x2, min_y2, max_x2, max_y2) = shape_bounds(shape2);
let expanded_min_x1 = min_x1 - min_separation;
let expanded_min_y1 = min_y1 - min_separation;
let expanded_max_x1 = max_x1 + min_separation;
let expanded_max_y1 = max_y1 + min_separation;
!(expanded_max_x1 < min_x2
|| expanded_min_x1 > max_x2
|| expanded_max_y1 < min_y2
|| expanded_min_y1 > max_y2)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::shapes::types::ShapeType as MyShapeType;
#[test]
fn test_shape_bounds() {
let shape = Shape::new(1, MyShapeType::Square, 10.0, 10.0);
let (min_x, min_y, max_x, max_y) = shape_bounds(&shape);
assert!(min_x < 10.0);
assert!(max_x > 10.0);
assert!(min_y < 10.0);
assert!(max_y > 10.0);
}
#[test]
fn test_shapes_overlap_distant() {
let shape1 = Shape::new(1, MyShapeType::Square, 0.0, 0.0);
let shape2 = Shape::new(2, MyShapeType::Square, 100.0, 100.0);
assert!(!shapes_overlap(&shape1, &shape2, 4.0));
}
#[test]
fn test_shapes_overlap_close() {
let shape1 = Shape::new(1, MyShapeType::Square, 10.0, 10.0);
let shape2 = Shape::new(2, MyShapeType::Square, 12.0, 10.0);
assert!(shapes_overlap(&shape1, &shape2, 4.0));
}
}