Merge pull request #524 from mdsteele/rectops

Add some convenience methods/operators for Rect and Point
This commit is contained in:
Tony Aldridge
2016-07-28 08:28:55 +01:00
committed by GitHub
+163 -1
View File
@@ -4,7 +4,7 @@
use sys::rect as ll;
use std::mem;
use std::ptr;
use std::ops::{BitAnd, BitOr};
use std::ops::{Add, BitAnd, BitOr, Div, Mul, Neg, Sub};
/// The maximal integer value that can be used for rectangles.
///
@@ -43,6 +43,19 @@ fn clamp_position(val: i32) -> i32 {
}
}
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
}
}
}
}
/// A rectangle.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Rect {
@@ -109,6 +122,11 @@ impl Rect {
self.raw.h as u32
}
/// Returns the width and height of this rectangle.
pub fn size(&self) -> (u32, u32) {
(self.width(), self.height())
}
/// Sets the horizontal position of this rectangle to the given value,
/// clamped to be less than or equal to i32::max_value() / 2.
pub fn set_x(&mut self, x: i32) {
@@ -171,6 +189,58 @@ impl Rect {
Point::new(x, y)
}
/// Returns the top-left corner of this rectangle.
///
/// # Example
///
/// ```
/// use sdl2::rect::{Rect, Point};
/// let rect = Rect::new(1, 0, 2, 3);
/// assert_eq!(Point::new(1, 0), rect.top_left());
/// ```
pub fn top_left(&self) -> Point {
Point::new(self.left(), self.top())
}
/// Returns the top-right corner of this rectangle.
///
/// # Example
///
/// ```
/// use sdl2::rect::{Rect, Point};
/// let rect = Rect::new(1, 0, 2, 3);
/// assert_eq!(Point::new(3, 0), rect.top_right());
/// ```
pub fn top_right(&self) -> Point {
Point::new(self.right(), self.top())
}
/// Returns the bottom-left corner of this rectangle.
///
/// # Example
///
/// ```
/// use sdl2::rect::{Rect, Point};
/// let rect = Rect::new(1, 0, 2, 3);
/// assert_eq!(Point::new(1, 3), rect.bottom_left());
/// ```
pub fn bottom_left(&self) -> Point {
Point::new(self.left(), self.bottom())
}
/// Returns the bottom-right corner of this rectangle.
///
/// # Example
///
/// ```
/// use sdl2::rect::{Rect, Point};
/// let rect = Rect::new(1, 0, 2, 3);
/// assert_eq!(Point::new(3, 3), rect.bottom_right());
/// ```
pub fn bottom_right(&self) -> Point {
Point::new(self.right(), self.bottom())
}
/// Sets the position of the right side of this rectangle to the given
/// value, clamped to be less than or equal to i32::max_value() / 2.
pub fn set_right(&mut self, right: i32) {
@@ -396,6 +466,7 @@ impl Into<(i32, i32)> for Point {
}
impl Point {
/// Creates a new point from the given coordinates.
pub fn new(x: i32, y: i32) -> Point {
Point {
raw: ll::SDL_Point {
@@ -419,6 +490,8 @@ impl Point {
&self.raw
}
/// Returns a new point by shifting this point's coordinates by the given
/// x and y values.
pub fn offset(&self, x: i32, y: i32) -> Point {
let x = match self.raw.x.checked_add(x) {
Some(val) => val,
@@ -443,15 +516,64 @@ impl Point {
Point::new(x, y)
}
/// Returns a new point by multiplying this point's coordinates by the
/// given scale factor.
pub fn scale(&self, f: i32) -> Point {
Point::new(clamped_mul(self.raw.x, f),
clamped_mul(self.raw.y, f))
}
/// Returns the x-coordinate of this point.
pub fn x(&self) -> i32 {
self.raw.x
}
/// Returns the y-coordinate of this point.
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 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 Mul<i32> for Point {
type Output = Point;
fn mul(self, rhs: i32) -> Point {
self.scale(rhs)
}
}
impl Div<i32> for Point {
type Output = Point;
fn div(self, rhs: i32) -> Point {
Point::new(self.x() / rhs, self.y() / rhs)
}
}
#[cfg(test)]
mod test {
use super::{Rect, Point, max_int_value, min_int_value};
@@ -616,4 +738,44 @@ mod 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_sub() {
assert_eq!(
Point::new(-17, 3),
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_clamp() {
assert_eq!(
Point::new(0x7fffffff, -0x7fffffff),
Point::new(-1000000, 5000000) * -3000000
);
}
#[test]
fn point_div() {
assert_eq!(
Point::new(-3, 1),
Point::new(-11, 5) / 3
);
}
}