use glam::Vec2;
use crate::BodyId;
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct ShapeId(sys::b2ShapeId);
impl ShapeId {
pub const MAX_POLYGON_POINTS: usize = 8;
pub fn from_b2(input: sys::b2ShapeId) -> Self {
Self(input)
}
pub fn shape_kind(&self) -> ShapeKind {
let shape = unsafe { sys::b2Shape_GetType(self.0) };
match shape {
sys::b2ShapeType_b2_circleShape => ShapeKind::Circle,
sys::b2ShapeType_b2_polygonShape => ShapeKind::Polygon,
s => unimplemented!("{s:?}"),
}
}
pub fn dimensions(&self) -> Vec2 {
Vec2::new(self.width(), self.height())
}
pub fn width(&self) -> f32 {
unsafe {
match self.shape_kind() {
ShapeKind::Circle => sys::b2Shape_GetCircle(self.0).radius * 2.0,
ShapeKind::Polygon => {
let polygon = sys::b2Shape_GetPolygon(self.0);
let mut min_x = f32::INFINITY;
let mut max_x = f32::NEG_INFINITY;
for i in 0..polygon.count as usize {
let x = polygon.vertices[i].x;
min_x = min_x.min(x);
max_x = max_x.max(x);
}
max_x - min_x
}
}
}
}
pub fn height(&self) -> f32 {
unsafe {
match self.shape_kind() {
ShapeKind::Circle => sys::b2Shape_GetCircle(self.0).radius * 2.0,
ShapeKind::Polygon => {
let polygon = sys::b2Shape_GetPolygon(self.0);
let mut min_y = f32::INFINITY;
let mut max_y = f32::NEG_INFINITY;
for i in 0..polygon.count as usize {
let y = polygon.vertices[i].y;
min_y = min_y.min(y);
max_y = max_y.max(y);
}
max_y - min_y
}
}
}
}
pub fn is_valid(self) -> bool {
unsafe { sys::b2Shape_IsValid(self.0) }
}
pub fn get_body(self) -> BodyId {
let raw = unsafe { sys::b2Shape_GetBody(self.0) };
BodyId::from_b2(raw)
}
#[must_use]
pub fn create_circle(body_id: BodyId, center: Vec2, radius: f32, shape_def: &ShapeDefinition) -> Self {
let shape_id = unsafe {
sys::b2CreateCircleShape(
body_id.into(),
&shape_def.0,
&sys::b2Circle {
center: center.into(),
radius,
},
)
};
Self::from_b2(shape_id)
}
#[must_use]
pub fn create_rectangle(
body_id: BodyId,
half_dims: Vec2,
offset: Vec2,
rotation: f32,
shape_def: &ShapeDefinition,
) -> Self {
let shape_id = unsafe {
sys::b2CreatePolygonShape(
body_id.into(),
&shape_def.0,
&sys::b2MakeOffsetBox(
half_dims.x,
half_dims.y,
offset.into(),
sys::b2Rot {
c: rotation.cos(),
s: rotation.sin(),
},
),
)
};
Self::from_b2(shape_id)
}
#[must_use]
pub fn create_polygon(body_id: BodyId, polygon_points: &[Vec2], shape_def: &ShapeDefinition) -> Option<Self> {
if polygon_points.len() > Self::MAX_POLYGON_POINTS || polygon_points.len() < 3 {
return None;
}
let hull = unsafe {
sys::b2ComputeHull(
polygon_points.as_ptr() as *const sys::b2Vec2,
polygon_points.len() as i32,
)
};
if hull.count == 0 {
return None;
}
let shape_id =
unsafe { sys::b2CreatePolygonShape(body_id.into(), &shape_def.0, &sys::b2MakePolygon(&hull, 0.0)) };
Some(Self::from_b2(shape_id))
}
}
impl From<sys::b2ShapeId> for ShapeId {
fn from(value: sys::b2ShapeId) -> Self {
Self::from_b2(value)
}
}
impl From<ShapeId> for sys::b2ShapeId {
fn from(value: ShapeId) -> Self {
value.0
}
}
impl std::fmt::Debug for ShapeId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.pad(&format!("{}@{}v{}", self.0.world0, self.0.index1, self.0.generation))
}
}
#[derive(Debug, Clone, Copy)]
#[repr(transparent)]
pub struct ShapeDefinition(sys::b2ShapeDef);
impl ShapeDefinition {
pub fn new() -> Self {
Self(unsafe { sys::b2DefaultShapeDef() })
}
pub fn density(mut self, density: f32) -> Self {
self.0.density = density;
self
}
pub fn category(mut self, category: u64) -> Self {
self.0.filter.categoryBits = category;
self
}
pub fn mask(mut self, mask: u64) -> Self {
self.0.filter.maskBits = mask;
self
}
pub fn is_sensor(mut self, is_sensor: bool) -> Self {
self.0.isSensor = is_sensor;
self
}
pub fn enable_contact_events(mut self, enable_contact_events: bool) -> Self {
self.0.enableContactEvents = enable_contact_events;
self
}
pub fn restitution(mut self, restitution: f32) -> Self {
self.0.material.restitution = restitution;
self
}
pub fn friction(mut self, friction: f32) -> Self {
self.0.material.friction = friction;
self
}
}
impl Default for ShapeDefinition {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u32)]
pub enum ShapeKind {
Circle = sys::b2ShapeType_b2_circleShape,
Polygon = sys::b2ShapeType_b2_polygonShape,
}