use std::ops;
#[derive(Default, Debug, Copy, Clone, Ord, PartialOrd, PartialEq, Eq, Hash)]
pub struct X(u16);
#[derive(Default, Debug, Copy, Clone, Ord, PartialOrd, PartialEq, Eq, Hash)]
pub struct Y(u16);
#[derive(Default, Debug, Copy, Clone, Ord, PartialOrd, PartialEq, Eq, Hash)]
pub struct Window {
pub position: Point,
pub size: Point,
}
#[derive(Default, Debug, Copy, Clone, Ord, PartialOrd, PartialEq, Eq, Hash)]
pub struct Point {
pub x: X,
pub y: Y,
}
pub fn x(x: u16) -> X {
X(x)
}
pub fn y(y: u16) -> Y {
Y(y)
}
pub fn point(x: X, y: Y) -> Point {
Point { x, y }
}
pub fn window(position: Point, size: Point) -> Window {
Window { position, size }
}
impl Point {
pub fn area(&self) -> u32 {
self.x.0 as u32 * self.y.0 as u32
}
pub fn contains(&self, o: &Point) -> bool {
self.x > o.x && self.y > o.y
}
pub fn point_at_idx(&self, idx: u32) -> Option<Point> {
if idx >= self.area() {
None
} else {
self.x.point_at_idx(idx)
}
}
pub fn idx_at_point(&self, p: Point) -> Option<u32> {
if p.y >= self.y || p.x >= self.x {
None
} else {
self.x.idx_at_point(p)
}
}
}
impl Window {
pub fn crop(&self, w: &Window) -> Window {
let pos = self.position + w.position;
let win = window(pos, w.size);
&win & self
}
}
impl X {
#[inline]
pub fn max(o1: X, o2: X) -> X {
if o1 > o2 {
o1
} else {
o2
}
}
#[inline]
pub fn min(o1: X, o2: X) -> X {
if o1 > o2 {
o2
} else {
o1
}
}
#[inline]
pub fn to_primitive(&self) -> u16 {
self.0
}
pub fn point_at_idx(&self, idx: u32) -> Option<Point> {
if self.0 == 0 {
None
} else {
let px = x((idx % self.0 as u32) as u16);
let py = y((idx / self.0 as u32) as u16);
Some(point(px, py))
}
}
pub fn idx_at_point(&self, p: Point) -> Option<u32> {
if p.x >= *self {
None
} else {
let w = self.0 as u32;
let x = p.x.0 as u32;
let y = p.y.0 as u32;
Some(y * w + x)
}
}
}
impl Y {
#[inline]
pub fn max(o1: Y, o2: Y) -> Y {
if o1 > o2 {
o1
} else {
o2
}
}
#[inline]
pub fn min(o1: Y, o2: Y) -> Y {
if o1 > o2 {
o2
} else {
o1
}
}
#[inline]
pub fn to_primitive(&self) -> u16 {
self.0
}
}
impl ops::Div<u16> for X {
type Output = X;
fn div(self, o: u16) -> Self::Output {
X(self.0 / o)
}
}
impl ops::Div<u16> for Y {
type Output = Y;
fn div(self, o: u16) -> Self::Output {
Y(self.0 / o)
}
}
impl ops::Mul<u16> for X {
type Output = X;
fn mul(self, o: u16) -> Self::Output {
X(self.0.saturating_mul(o))
}
}
impl ops::Mul<u16> for Y {
type Output = Y;
fn mul(self, o: u16) -> Self::Output {
Y(self.0.saturating_mul(o))
}
}
impl ops::BitAnd<&Window> for &Window {
type Output = Window;
fn bitand(self, o: &Window) -> Self::Output {
let pos = point(
X::max(self.position.x, o.position.x),
Y::max(self.position.y, o.position.y),
);
let w = window(
pos,
point(
X::min(
self.position.x + self.size.x - pos.x,
o.position.x + o.size.x - pos.x,
),
Y::min(
self.position.y + self.size.y - pos.y,
o.position.y + o.size.y - pos.y,
),
),
);
trace!("intersect windows: {:?} and {:?} => {:?}", self, o, w);
w
}
}
impl ops::BitAnd<Point> for Window {
type Output = Option<Point>;
fn bitand(self, o: Point) -> Self::Output {
if self.position.x <= o.x
&& self.position.y <= o.y
&& self.position.x + self.size.x >= o.x
&& self.position.y + self.size.y >= o.y
{
Some(o)
} else {
None
}
}
}
impl ops::Mul<Y> for X {
type Output = Point;
fn mul(self, o: Y) -> Self::Output {
point(self, o)
}
}
impl ops::Mul<X> for Y {
type Output = Point;
fn mul(self, o: X) -> Self::Output {
point(o, self)
}
}
impl ops::Div<X> for Point {
type Output = Y;
fn div(self, o: X) -> Self::Output {
Y((self.x.0 as u32 * self.y.0 as u32 / o.0 as u32) as u16)
}
}
impl ops::Div<Y> for Point {
type Output = X;
fn div(self, o: Y) -> Self::Output {
X((self.x.0 as u32 * self.y.0 as u32 / o.0 as u32) as u16)
}
}
impl ops::SubAssign for X {
fn sub_assign(&mut self, o: Self) {
self.0 = self.0.saturating_sub(o.0)
}
}
impl ops::AddAssign for X {
fn add_assign(&mut self, o: Self) {
self.0 = self.0.saturating_add(o.0)
}
}
impl ops::SubAssign for Y {
fn sub_assign(&mut self, o: Self) {
self.0 = self.0.saturating_sub(o.0)
}
}
impl ops::AddAssign for Y {
fn add_assign(&mut self, o: Self) {
self.0 = self.0.saturating_add(o.0)
}
}
impl ops::Sub<X> for X {
type Output = X;
fn sub(self, o: X) -> Self::Output {
X(self.0.saturating_sub(o.0))
}
}
impl ops::Add<X> for X {
type Output = X;
fn add(self, o: X) -> Self::Output {
X(self.0.saturating_add(o.0))
}
}
impl ops::Sub<Y> for Y {
type Output = Y;
fn sub(self, o: Y) -> Self::Output {
Y(self.0.saturating_sub(o.0))
}
}
impl ops::Add<Y> for Y {
type Output = Y;
fn add(self, o: Y) -> Self::Output {
Y(self.0.saturating_add(o.0))
}
}
impl ops::SubAssign for Point {
fn sub_assign(&mut self, o: Self) {
*self = *self - o
}
}
impl ops::AddAssign for Point {
fn add_assign(&mut self, o: Self) {
*self = *self + o
}
}
impl ops::Sub<Point> for Point {
type Output = Point;
fn sub(self, o: Point) -> Self::Output {
point(self.x - o.x, self.y - o.y)
}
}
impl ops::Add<Point> for Point {
type Output = Point;
fn add(self, pos: Point) -> Self::Output {
point(self.x + pos.x, self.y + pos.y)
}
}
impl ops::Sub<X> for Point {
type Output = Point;
fn sub(self, o: X) -> Self::Output {
point(self.x - o, self.y)
}
}
impl ops::Add<X> for Point {
type Output = Point;
fn add(self, o: X) -> Self::Output {
point(self.x + o, self.y)
}
}
impl ops::Sub<Y> for Point {
type Output = Point;
fn sub(self, o: Y) -> Self::Output {
point(self.x, self.y - o)
}
}
impl ops::Add<Y> for Point {
type Output = Point;
fn add(self, o: Y) -> Self::Output {
point(self.x, self.y + o)
}
}
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct Position {
pub left: u16,
pub top: u16,
}
impl Position {
pub fn add(&self, operand: Self) -> Self {
Self {
left: self.left.saturating_add(operand.left),
top: self.top.saturating_add(operand.top),
}
}
}
#[test]
fn test_x_times_y() {
assert_eq!(X(1) * Y(2), point(X(1), Y(2)));
}
#[test]
fn test_y_times_x() {
assert_eq!(Y(2) * X(1), point(X(1), Y(2)));
}
#[test]
fn test_to_primitive() {
assert_eq!(X(3).to_primitive(), 3);
assert_eq!(Y(2).to_primitive(), 2);
}
#[test]
fn test_window_crop() {
let op = window(point(x(0), y(0)), point(x(10000), y(30000)));
let crop = window(point(x(10), y(3)), point(x(100), y(300))).crop(&op);
assert_eq!(crop.position.x, x(10));
assert_eq!(crop.position.y, y(3));
assert_eq!(crop.size.x, x(100));
assert_eq!(crop.size.y, y(300));
let op = window(point(x(10), y(3)), point(x(100), y(300)));
let crop = window(point(x(0), y(0)), point(x(10000), y(30000))).crop(&op);
assert_eq!(crop.position.x, x(10));
assert_eq!(crop.position.y, y(3));
assert_eq!(crop.size.x, x(100));
assert_eq!(crop.size.y, y(300));
}
#[test]
fn test_size_contains() {
let p = point(x(3), y(2));
assert_eq!(p.contains(&point(x(0), y(0))), true);
assert_eq!(p.contains(&point(x(2), y(1))), true);
assert_eq!(p.contains(&point(x(3), y(1))), false);
assert_eq!(p.contains(&point(x(2), y(2))), false);
}
#[test]
fn test_idx_at_point() {
let p = point(x(3), y(2));
assert_eq!(p.idx_at_point(point(x(0), y(0))), Some(0));
assert_eq!(p.idx_at_point(point(x(1), y(0))), Some(1));
assert_eq!(p.idx_at_point(point(x(1), y(1))), Some(4));
assert_eq!(p.idx_at_point(point(x(2), y(1))), Some(5));
assert_eq!(p.idx_at_point(point(x(3), y(1))), None);
assert_eq!(p.idx_at_point(point(x(1), y(2))), None);
assert_eq!(p.point_at_idx(0), Some(point(x(0), y(0))));
assert_eq!(p.point_at_idx(5), Some(point(x(2), y(1))));
assert_eq!(p.point_at_idx(6), None);
}