#![allow(const_err)]
use crate::sys;
use std::mem;
use std::ptr;
use std::ops::{Deref, DerefMut, Add, AddAssign, BitAnd, BitOr, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
use std::convert::{AsRef, AsMut};
use std::hash::{Hash, Hasher};
pub fn max_int_value() -> u32 {
i32::max_value() as u32 / 2
}
pub fn min_int_value() -> i32 {
i32::min_value() / 2
}
fn clamp_size(val: u32) -> u32 {
if val == 0 {
1
} else if val > max_int_value() {
max_int_value()
} else {
val
}
}
fn clamp_position(val: i32) -> i32 {
if val > max_int_value() as i32 {
max_int_value() as i32
} else if val < min_int_value() {
min_int_value()
} else {
val
}
}
fn clamped_mul(a: i32, b: i32) -> i32 {
match a.checked_mul(b) {
Some(val) => val,
None => {
if (a < 0) ^ (b < 0) {
min_int_value()
} else {
max_int_value() as i32
}
}
}
}
#[derive(Clone, Copy)]
pub struct Rect {
raw: sys::SDL_Rect,
}
impl ::std::fmt::Debug for Rect {
fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
return write!(fmt, "Rect {{ x: {}, y: {}, w: {}, h: {} }}",
self.raw.x, self.raw.y, self.raw.w, self.raw.h);
}
}
impl PartialEq for Rect {
fn eq(&self, other: &Rect) -> bool {
self.raw.x == other.raw.x &&
self.raw.y == other.raw.y &&
self.raw.w == other.raw.w &&
self.raw.h == other.raw.h
}
}
impl Eq for Rect {}
impl Hash for Rect {
fn hash<H: Hasher>(&self, state: &mut H) {
self.raw.x.hash(state);
self.raw.y.hash(state);
self.raw.w.hash(state);
self.raw.h.hash(state);
}
}
impl Rect {
pub fn new(x: i32, y: i32, width: u32, height: u32) -> Rect {
let raw = sys::SDL_Rect {
x: clamp_position(x),
y: clamp_position(y),
w: clamp_size(width) as i32,
h: clamp_size(height) as i32,
};
Rect { raw }
}
pub fn from_center<P>(center: P, width: u32, height: u32)
-> Rect where P: Into<Point> {
let raw = sys::SDL_Rect {
x: 0,
y: 0,
w: clamp_size(width) as i32,
h: clamp_size(height) as i32,
};
let mut rect = Rect { raw };
rect.center_on(center.into());
rect
}
pub fn x(&self) -> i32 {
self.raw.x
}
pub fn y(&self) -> i32 {
self.raw.y
}
pub fn width(&self) -> u32 {
self.raw.w as u32
}
pub fn height(&self) -> u32 {
self.raw.h as u32
}
pub fn size(&self) -> (u32, u32) {
(self.width(), self.height())
}
pub fn set_x(&mut self, x: i32) {
self.raw.x = clamp_position(x);
}
pub fn set_y(&mut self, y: i32) {
self.raw.y = clamp_position(y);
}
pub fn set_width(&mut self, width: u32) {
self.raw.w = clamp_size(width) as i32;
}
pub fn set_height(&mut self, height: u32) {
self.raw.h = clamp_size(height) as i32;
}
pub fn left(&self) -> i32 {
self.raw.x
}
pub fn right(&self) -> i32 {
self.raw.x + self.raw.w
}
pub fn top(&self) -> i32 {
self.raw.y
}
pub fn bottom(&self) -> i32 {
self.raw.y + self.raw.h
}
pub fn center(&self) -> Point {
let x = self.raw.x + (self.raw.w / 2);
let y = self.raw.y + (self.raw.h / 2);
Point::new(x, y)
}
pub fn top_left(&self) -> Point {
Point::new(self.left(), self.top())
}
pub fn top_right(&self) -> Point {
Point::new(self.right(), self.top())
}
pub fn bottom_left(&self) -> Point {
Point::new(self.left(), self.bottom())
}
pub fn bottom_right(&self) -> Point {
Point::new(self.right(), self.bottom())
}
pub fn set_right(&mut self, right: i32) {
self.raw.x = clamp_position(clamp_position(right) - self.raw.w);
}
pub fn set_bottom(&mut self, bottom: i32) {
self.raw.y = clamp_position(clamp_position(bottom) - self.raw.h);
}
pub fn center_on<P>(&mut self, point: P) where P: Into<(i32, i32)> {
let (x, y) = point.into();
self.raw.x = clamp_position(clamp_position(x) - self.raw.w / 2);
self.raw.y = clamp_position(clamp_position(y) - self.raw. h / 2);
}
pub fn offset(&mut self, x: i32, y: i32) {
match self.raw.x.checked_add(x) {
Some(val) => self.raw.x = clamp_position(val),
None => {
if x >= 0 {
self.raw.x = max_int_value() as i32;
} else {
self.raw.x = i32::min_value();
}
},
}
match self.raw.y.checked_add(y) {
Some(val) => self.raw.y = clamp_position(val),
None => {
if y >= 0 {
self.raw.y = max_int_value() as i32;
} else {
self.raw.y = i32::min_value();
}
},
}
}
pub fn reposition<P>(&mut self, point: P) where P: Into<(i32, i32)> {
let (x, y) = point.into();
self.raw.x = clamp_position(x);
self.raw.y = clamp_position(y);
}
pub fn resize(&mut self, width: u32, height: u32) {
self.raw.w = clamp_size(width) as i32;
self.raw.h = clamp_size(height) as i32;
}
pub fn contains_point<P>(&self, point: P) -> bool
where P: Into<(i32, i32)> {
let (x, y) = point.into();
let inside_x = x >= self.left() && x < self.right();
inside_x && (y >= self.top() && y < self.bottom())
}
pub fn contains_rect(&self, other: Rect) -> bool {
other.left() >= self.left() && other.right() <= self.right() &&
other.top() >= self.top() && other.bottom() <= self.bottom()
}
pub fn raw(&self) -> *const sys::SDL_Rect {
&self.raw
}
pub fn raw_mut(&mut self) -> *mut sys::SDL_Rect {
self.raw() as *mut _
}
pub fn raw_slice(slice: &[Rect]) -> *const sys::SDL_Rect {
slice.as_ptr() as *const sys::SDL_Rect
}
pub fn from_ll(raw: sys::SDL_Rect) -> Rect {
Rect::new(raw.x, raw.y, raw.w as u32, raw.h as u32)
}
pub fn from_enclose_points<R: Into<Option<Rect>>>(points: &[Point], clipping_rect: R)
-> Option<Rect>
where R: Into<Option<Rect>>
{
let clipping_rect = clipping_rect.into();
if points.is_empty() {
return None;
}
let mut out = mem::MaybeUninit::uninit();
let clip_ptr = match clipping_rect.as_ref() {
Some(r) => r.raw(),
None => ptr::null()
};
let result = unsafe {
sys::SDL_EnclosePoints(
Point::raw_slice(points),
points.len() as i32,
clip_ptr,
out.as_mut_ptr()
) != sys::SDL_bool::SDL_FALSE
};
if result {
let out = unsafe { out.assume_init() };
Some(Rect::from_ll(out))
} else {
None
}
}
pub fn has_intersection(&self, other: Rect) -> bool {
unsafe {
sys::SDL_HasIntersection(self.raw(), other.raw()) != sys::SDL_bool::SDL_FALSE
}
}
pub fn intersection(&self, other: Rect) -> Option<Rect> {
let mut out = mem::MaybeUninit::uninit();
let success = unsafe {
sys::SDL_IntersectRect(self.raw(), other.raw(), out.as_mut_ptr()) != sys::SDL_bool::SDL_FALSE
};
if success {
let out = unsafe { out.assume_init() };
Some(Rect::from_ll(out))
} else {
None
}
}
pub fn union(&self, other: Rect) -> Rect {
let mut out = mem::MaybeUninit::uninit();
unsafe {
sys::SDL_UnionRect(self.raw(), other.raw(), out.as_mut_ptr())
};
let out = unsafe { out.assume_init() };
Rect::from_ll(out)
}
pub fn intersect_line(&self, start: Point, end: Point)
-> Option<(Point, Point)> {
let (mut start_x, mut start_y) = (start.x(), start.y());
let (mut end_x, mut end_y) = (end.x(), end.y());
let intersected = unsafe {
sys::SDL_IntersectRectAndLine(
self.raw(),
&mut start_x, &mut start_y,
&mut end_x, &mut end_y
) != sys::SDL_bool::SDL_FALSE
};
if intersected {
Some((Point::new(start_x, start_y), Point::new(end_x, end_y)))
} else {
None
}
}
}
impl Deref for Rect {
type Target = sys::SDL_Rect;
fn deref(&self) -> &sys::SDL_Rect {
&self.raw
}
}
impl DerefMut for Rect {
fn deref_mut(&mut self) -> &mut sys::SDL_Rect {
&mut self.raw
}
}
impl Into<sys::SDL_Rect> for Rect {
fn into(self) -> sys::SDL_Rect {
self.raw
}
}
impl Into<(i32, i32, u32, u32)> for Rect {
fn into(self) -> (i32, i32, u32, u32) {
(self.raw.x, self.raw.y, self.raw.w as u32, self.raw.h as u32)
}
}
impl From<sys::SDL_Rect> for Rect {
fn from(raw: sys::SDL_Rect) -> Rect {
Rect { raw }
}
}
impl From<(i32, i32, u32, u32)> for Rect {
fn from((x, y, width, height): (i32, i32, u32, u32)) -> Rect {
Rect::new(x, y, width, height)
}
}
impl AsRef<sys::SDL_Rect> for Rect {
fn as_ref(&self) -> &sys::SDL_Rect {
&self.raw
}
}
impl AsMut<sys::SDL_Rect> for Rect {
fn as_mut(&mut self) -> &mut sys::SDL_Rect {
&mut self.raw
}
}
impl BitAnd<Rect> for Rect {
type Output = Option<Rect>;
fn bitand(self, rhs: Rect) -> Option<Rect> { self.intersection(rhs) }
}
impl BitOr<Rect> for Rect {
type Output = Rect;
fn bitor(self, rhs: Rect) -> Rect { self.union(rhs) }
}
#[derive(Copy, Clone)]
pub struct Point {
raw: sys::SDL_Point
}
impl ::std::fmt::Debug for Point {
fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
return write!(fmt, "Point {{ x: {}, y: {} }}", self.raw.x, self.raw.y);
}
}
impl PartialEq for Point {
fn eq(&self, other: &Point) -> bool {
self.raw.x == other.raw.x && self.raw.y == other.raw.y
}
}
impl Eq for Point {}
impl Hash for Point {
fn hash<H: Hasher>(&self, state: &mut H) {
self.raw.x.hash(state);
self.raw.y.hash(state);
}
}
impl Deref for Point {
type Target = sys::SDL_Point;
fn deref(&self) -> &sys::SDL_Point {
&self.raw
}
}
impl DerefMut for Point {
fn deref_mut(&mut self) -> &mut sys::SDL_Point {
&mut self.raw
}
}
impl AsRef<sys::SDL_Point> for Point {
fn as_ref(&self) -> &sys::SDL_Point {
&self.raw
}
}
impl AsMut<sys::SDL_Point> for Point {
fn as_mut(&mut self) -> &mut sys::SDL_Point {
&mut self.raw
}
}
impl From<sys::SDL_Point> for Point {
fn from(prim: sys::SDL_Point) -> Point {
Point { raw: prim }
}
}
impl From<(i32, i32)> for Point {
fn from((x, y): (i32, i32)) -> Point {
Point::new(x, y)
}
}
impl Into<sys::SDL_Point> for Point {
fn into(self) -> sys::SDL_Point {
self.raw
}
}
impl Into<(i32, i32)> for Point {
fn into(self) -> (i32, i32) {
(self.x(), self.y())
}
}
impl Point {
pub fn new(x: i32, y: i32) -> Point {
Point {
raw: sys::SDL_Point {
x: clamp_position(x),
y: clamp_position(y),
}
}
}
pub fn from_ll(raw: sys::SDL_Point) -> Point {
Point::new(raw.x, raw.y)
}
pub fn raw_slice(slice: &[Point]) -> *const sys::SDL_Point {
slice.as_ptr() as *const sys::SDL_Point
}
pub fn raw(self) -> *const sys::SDL_Point {
&self.raw
}
pub fn offset(self, x: i32, y: i32) -> Point {
let x = match self.raw.x.checked_add(x) {
Some(val) => val,
None => {
if x < 0 {
min_int_value()
} else {
max_int_value() as i32
}
}
};
let y = match self.raw.y.checked_add(y) {
Some(val) => val,
None => {
if y < 0 {
min_int_value()
} else {
max_int_value() as i32
}
}
};
Point::new(x, y)
}
pub fn scale(self, f: i32) -> Point {
Point::new(clamped_mul(self.raw.x, f),
clamped_mul(self.raw.y, f))
}
pub fn x(self) -> i32 {
self.raw.x
}
pub fn y(self) -> i32 {
self.raw.y
}
}
impl Add for Point {
type Output = Point;
fn add(self, rhs: Point) -> Point {
self.offset(rhs.x(), rhs.y())
}
}
impl AddAssign for Point {
fn add_assign(&mut self, rhs: Point) {
self.raw.x = clamp_position(self.x() + rhs.x());
self.raw.y = clamp_position(self.y() + rhs.y());
}
}
impl Neg for Point {
type Output = Point;
fn neg(self) -> Point {
Point::new(-self.x(), -self.y())
}
}
impl Sub for Point {
type Output = Point;
fn sub(self, rhs: Point) -> Point {
self.offset(-rhs.x(), -rhs.y())
}
}
impl SubAssign for Point {
fn sub_assign(&mut self, rhs: Point) {
self.raw.x = clamp_position(self.x() - rhs.x());
self.raw.y = clamp_position(self.y() - rhs.y());
}
}
impl Mul<i32> for Point {
type Output = Point;
fn mul(self, rhs: i32) -> Point {
self.scale(rhs)
}
}
impl MulAssign<i32> for Point {
fn mul_assign(&mut self, rhs: i32) {
self.raw.x = clamped_mul(self.x(), rhs);
self.raw.y = clamped_mul(self.y(), rhs);
}
}
impl Div<i32> for Point {
type Output = Point;
fn div(self, rhs: i32) -> Point {
Point::new(self.x() / rhs, self.y() / rhs)
}
}
impl DivAssign<i32> for Point {
fn div_assign(&mut self, rhs: i32) {
self.raw.x /= rhs;
self.raw.y /= rhs;
}
}
#[cfg(test)]
mod test {
use super::{Rect, Point, max_int_value, min_int_value};
fn tuple(x: i32, y: i32, w: u32, h: u32) -> (i32, i32, u32, u32) {
(x, y, w, h)
}
#[test]
fn enclose_points_valid() {
assert_eq!(
Some(tuple(2, 4, 4, 6)),
Rect::from_enclose_points(
&[Point::new(2, 4), Point::new(5,9)],
None
).map(|r| r.into())
);
}
#[test]
fn enclose_points_outside_clip_rect() {
assert_eq!(
Rect::from_enclose_points(
&[Point::new(0, 0), Point::new(10,10)],
Some(Rect::new(3, 3, 1, 1))),
None
);
}
#[test]
fn enclose_points_max_values() {
assert_eq!(
Some(tuple(
min_int_value(), min_int_value(),
max_int_value(), max_int_value()
)),
Rect::from_enclose_points(
&[Point::new(i32::min_value(), i32::min_value()),
Point::new(i32::max_value(), i32::max_value())], None
).map(|r| r.into())
);
}
#[test]
fn has_intersection() {
let rect = Rect::new(0, 0, 10, 10);
assert!(rect.has_intersection(Rect::new(9, 9, 10, 10)));
assert!(! rect.has_intersection(Rect::new(10, 10, 10, 10)));
assert!(! rect.has_intersection(Rect::new(11, 11, 10, 10)));
}
#[test]
fn intersection() {
let rect = Rect::new(0, 0, 10, 10);
assert_eq!(
rect & Rect::new(9, 9, 10, 10),
Some(Rect::new(9, 9, 1, 1))
);
assert_eq!(
rect & Rect::new(11, 11, 10, 10),
None
);
}
#[test]
fn union() {
assert_eq!(
Rect::new(0, 0, 1, 1) | Rect::new(9, 9, 1, 1),
Rect::new(0, 0, 10, 10)
);
}
#[test]
fn intersect_line() {
assert_eq!(
Rect::new(1, 1, 5, 5).intersect_line(
Point::new(0, 0), Point::new(10, 10)
),
Some((Point::new(1, 1), Point::new(5, 5)))
);
}
#[test]
fn clamp_size_zero() {
assert_eq!(
tuple(0, 0, 1, 1),
Rect::new(0, 0, 0, 0).into()
);
}
#[test]
fn clamp_position_min() {
assert_eq!(
tuple(min_int_value(), min_int_value(), 1, 1),
Rect::new(i32::min_value(), i32::min_value(), 1, 1).into()
);
}
#[test]
fn clamp_size_max() {
assert_eq!(
tuple(0, 0, max_int_value(), max_int_value()),
Rect::new(0, 0, max_int_value() + 1, max_int_value() + 1).into()
);
}
#[test]
fn clamp_i32_max() {
assert_eq!(
tuple(0, 0, max_int_value(), max_int_value()),
Rect::new(
0, 0, i32::max_value() as u32, i32::max_value() as u32
).into()
)
}
#[test]
fn clamp_position_max() {
assert_eq!(
tuple(max_int_value() as i32, max_int_value() as i32, 1, 1),
Rect::new(
max_int_value() as i32 + 1, max_int_value() as i32 + 1, 1, 1
).into()
);
}
#[test]
fn rect_into() {
let test: (i32, i32, u32, u32) = (-11, 5, 50, 20);
assert_eq!(
test,
Rect::new(-11, 5, 50, 20).into()
);
}
#[test]
fn rect_from() {
assert_eq!(
Rect::from((-11, 5, 50, 20)),
Rect::new(-11, 5, 50, 20)
);
}
#[test]
fn point_into() {
let test: (i32, i32) = (-11, 5);
assert_eq!(
test,
Point::new(-11, 5).into()
);
}
#[test]
fn point_from() {
let test: (i32, i32) = (-11, 5);
assert_eq!(
test,
Point::new(-11, 5).into()
);
}
#[test]
fn point_add() {
assert_eq!(
Point::new(-5, 7),
Point::new(-11, 5) + Point::new(6, 2)
);
}
#[test]
fn point_add_assign() {
let mut point = Point::new(-11, 5);
point += Point::new(6, 2);
assert_eq!(
point,
Point::new(-11, 5) + Point::new(6, 2)
);
}
#[test]
fn point_sub() {
assert_eq!(
Point::new(-17, 3),
Point::new(-11, 5) - Point::new(6, 2)
);
}
#[test]
fn point_sub_assign() {
let mut point = Point::new(-11, 5);
point -= Point::new(6, 2);
assert_eq!(
point,
Point::new(-11, 5) - Point::new(6, 2)
);
}
#[test]
fn point_mul() {
assert_eq!(
Point::new(-33, 15),
Point::new(-11, 5) * 3
);
}
#[test]
fn point_mul_assign() {
let mut point = Point::new(-11, 5);
point *= 3;
assert_eq!(
point,
Point::new(-11, 5) * 3
);
}
#[test]
fn point_mul_clamp() {
assert_eq!(
Point::new(0x7fffffff, -0x7fffffff),
Point::new(-1000000, 5000000) * -3000000
);
}
#[test]
fn point_mul_assign_clamp() {
let mut point = Point::new(-1000000, 5000000);
point *= -3000000;
assert_eq!(
point,
Point::new(-1000000, 5000000) * -3000000
);
}
#[test]
fn point_div() {
assert_eq!(
Point::new(-3, 1),
Point::new(-11, 5) / 3
);
}
#[test]
fn point_div_assign () {
let mut point = Point::new(-11, 5);
point /= 3;
assert_eq!(
point,
Point::new(-11, 5) / 3
);
}
}