use std::f32;
use std::ops::{Add, Div, Mul, MulAssign, Neg, Sub};
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct Pt(pub f32, pub f32);
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct WidePt(pub Pt, pub f32);
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Transform {
e: [f32; 6],
}
impl Add for Pt {
type Output = Self;
fn add(self, rhs: Self) -> Self {
Pt(self.x() + rhs.x(), self.y() + rhs.y())
}
}
impl Sub for Pt {
type Output = Self;
fn sub(self, rhs: Self) -> Self {
Pt(self.x() - rhs.x(), self.y() - rhs.y())
}
}
impl Mul<f32> for Pt {
type Output = Self;
fn mul(self, s: f32) -> Self {
Pt(self.x() * s, self.y() * s)
}
}
impl Mul for Pt {
type Output = f32;
fn mul(self, rhs: Self) -> f32 {
self.x() * rhs.y() - self.y() * rhs.x()
}
}
impl Div<f32> for Pt {
type Output = Self;
fn div(self, s: f32) -> Self {
Pt(self.x() / s, self.y() / s)
}
}
impl Neg for Pt {
type Output = Self;
fn neg(self) -> Self {
Pt(-self.x(), -self.y())
}
}
impl Pt {
pub fn x(self) -> f32 {
self.0
}
pub fn y(self) -> f32 {
self.1
}
pub fn mag(self) -> f32 {
self.x().hypot(self.y())
}
pub fn normalize(self) -> Self {
let m = self.mag();
if m > 0.0 {
self / m
} else {
Pt::default()
}
}
pub fn dist_sq(self, rhs: Self) -> f32 {
let dx = self.x() - rhs.x();
let dy = self.y() - rhs.y();
dx * dx + dy * dy
}
#[allow(dead_code)]
pub fn dist(self, rhs: Self) -> f32 {
self.dist_sq(rhs).sqrt()
}
pub fn midpoint(self, rhs: Self) -> Self {
let x = (self.x() + rhs.x()) / 2.0;
let y = (self.y() + rhs.y()) / 2.0;
Pt(x, y)
}
pub fn left(self) -> Self {
Pt(-self.y(), self.x())
}
#[allow(dead_code)]
pub fn right(self) -> Self {
Pt(self.y(), -self.x())
}
#[allow(dead_code)]
pub fn lerp(self, rhs: Self, t: f32) -> Self {
let x = float_lerp(self.x(), rhs.x(), t);
let y = float_lerp(self.y(), rhs.y(), t);
Pt(x, y)
}
pub fn angle_rel(self, rhs: Self) -> f32 {
const PI: f32 = f32::consts::PI;
let th = self.y().atan2(self.x()) - rhs.y().atan2(rhs.x());
if th < -PI {
th + 2.0 * PI
} else if th > PI {
th - 2.0 * PI
} else {
th
}
}
}
pub fn float_lerp(a: f32, b: f32, t: f32) -> f32 {
b + (a - b) * t
}
pub fn intersection(a0: Pt, a1: Pt, b0: Pt, b1: Pt) -> Option<Pt> {
let av = a0 - a1;
let bv = b0 - b1;
let den = av * bv;
if den != 0.0 {
let ca = a0 * a1;
let cb = b0 * b1;
let xn = bv.x() * ca - av.x() * cb;
let yn = bv.y() * ca - av.y() * cb;
Some(Pt(xn / den, yn / den))
} else {
None
}
}
impl Default for WidePt {
fn default() -> Self {
WidePt(Pt::default(), 1.0)
}
}
impl WidePt {
pub fn w(self) -> f32 {
self.1
}
pub fn midpoint(self, rhs: Self) -> Self {
let v = self.0.midpoint(rhs.0);
let w = (self.w() + rhs.w()) / 2.0;
WidePt(v, w)
}
}
impl MulAssign for Transform {
fn mul_assign(&mut self, rhs: Self) {
self.e = self.mul_e(&rhs);
}
}
impl Mul for Transform {
type Output = Self;
fn mul(self, rhs: Self) -> Self {
let e = self.mul_e(&rhs);
Transform { e }
}
}
impl Mul<Pt> for Transform {
type Output = Pt;
fn mul(self, s: Pt) -> Pt {
let x = self.e[0] * s.x() + self.e[1] * s.y() + self.e[2];
let y = self.e[3] * s.x() + self.e[4] * s.y() + self.e[5];
Pt(x, y)
}
}
impl Default for Transform {
fn default() -> Self {
Transform {
e: [1.0, 0.0, 0.0, 0.0, 1.0, 0.0],
}
}
}
impl Transform {
fn mul_e(&self, rhs: &Self) -> [f32; 6] {
let mut e = [0.0; 6];
e[0] = self.e[0] * rhs.e[0] + self.e[3] * rhs.e[1];
e[1] = self.e[1] * rhs.e[0] + self.e[4] * rhs.e[1];
e[2] = self.e[2] * rhs.e[0] + self.e[5] * rhs.e[1] + rhs.e[2];
e[3] = self.e[0] * rhs.e[3] + self.e[3] * rhs.e[4];
e[4] = self.e[1] * rhs.e[3] + self.e[4] * rhs.e[4];
e[5] = self.e[2] * rhs.e[3] + self.e[5] * rhs.e[4] + rhs.e[5];
e
}
pub fn with_translate(tx: f32, ty: f32) -> Self {
Transform {
e: [1.0, 0.0, tx, 0.0, 1.0, ty],
}
}
pub fn with_scale(sx: f32, sy: f32) -> Self {
Transform {
e: [sx, 0.0, 0.0, 0.0, sy, 0.0],
}
}
pub fn with_rotate(th: f32) -> Self {
let sn = th.sin();
let cs = th.cos();
Transform {
e: [cs, -sn, 0.0, sn, cs, 0.0],
}
}
pub fn with_skew(ax: f32, ay: f32) -> Self {
let tnx = ax.tan();
let tny = ay.tan();
Transform {
e: [1.0, tnx, 0.0, tny, 1.0, 0.0],
}
}
pub fn translate(mut self, tx: f32, ty: f32) -> Self {
self *= Transform::with_translate(tx, ty);
self
}
pub fn scale(mut self, sx: f32, sy: f32) -> Self {
self *= Transform::with_scale(sx, sy);
self
}
pub fn rotate(mut self, th: f32) -> Self {
self *= Transform::with_rotate(th);
self
}
pub fn skew(mut self, ax: f32, ay: f32) -> Self {
self *= Transform::with_skew(ax, ay);
self
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_pt() {
let a = Pt(2.0, 1.0);
let b = Pt(3.0, 4.0);
let c = Pt(-1.0, 1.0);
assert_eq!(a + b, Pt(5.0, 5.0));
assert_eq!(b - a, Pt(1.0, 3.0));
assert_eq!(a * 2.0, Pt(4.0, 2.0));
assert_eq!(a / 2.0, Pt(1.0, 0.5));
assert_eq!(-a, Pt(-2.0, -1.0));
assert_eq!(b.mag(), 5.0);
assert_eq!(a.normalize(), Pt(0.8944272, 0.4472136));
assert_eq!(a.dist_sq(b), 10.0);
assert_eq!(b.dist(Pt(0.0, 0.0)), 5.0);
assert_eq!(a.midpoint(b), Pt(2.5, 2.5));
assert_eq!(a.left(), Pt(-1.0, 2.0));
assert_eq!(a.right(), Pt(1.0, -2.0));
assert_eq!(a.angle_rel(b), -0.4636476);
assert_eq!(c.angle_rel(Pt(1.0, 1.0)), 1.5707963);
assert_eq!(Pt(-1.0, -1.0).angle_rel(c), 1.5707965);
}
#[test]
fn test_identity() {
assert_eq!(Transform::default().e, [1.0, 0.0, 0.0, 0.0, 1.0, 0.0]);
assert_eq!(
(Transform::default() * Transform::default()).e,
[1.0, 0.0, 0.0, 0.0, 1.0, 0.0]
);
assert_eq!(Transform::default() * Pt(1.0, 2.0), Pt(1.0, 2.0));
}
#[test]
fn test_translate() {
assert_eq!(
Transform::with_translate(1.5, -1.5).e,
[1.0, 0.0, 1.5, 0.0, 1.0, -1.5]
);
assert_eq!(
Transform::default().translate(2.5, -3.5).e,
[1.0, 0.0, 2.5, 0.0, 1.0, -3.5]
);
assert_eq!(
Transform::default().translate(5.0, 7.0) * Pt(1.0, -2.0),
Pt(6.0, 5.0)
);
}
#[test]
fn test_scale() {
assert_eq!(
Transform::with_scale(2.0, 4.0).e,
[2.0, 0.0, 0.0, 0.0, 4.0, 0.0]
);
assert_eq!(
Transform::default().scale(3.0, 5.0).e,
[3.0, 0.0, 0.0, 0.0, 5.0, 0.0]
);
assert_eq!(
Transform::default().scale(2.0, 3.0) * Pt(1.5, -2.0),
Pt(3.0, -6.0)
);
}
#[test]
fn test_rotate() {
const PI: f32 = f32::consts::PI;
const V: f32 = 0.00000008742278;
assert_eq!(Transform::with_rotate(PI).e, [-1.0, V, 0.0, -V, -1.0, 0.0]);
assert_eq!(
Transform::default().rotate(PI).e,
[-1.0, V, 0.0, -V, -1.0, 0.0]
);
assert_eq!(
Transform::default().rotate(PI / 2.0) * Pt(15.0, 7.0),
Pt(-7.0000005, 15.0)
);
}
#[test]
fn test_skew() {
const PI: f32 = f32::consts::PI;
assert_eq!(
Transform::with_skew(PI / 2.0, 0.0).e,
[1.0, -22877334.0, 0.0, 0.0, 1.0, 0.0]
);
assert_eq!(
Transform::default().skew(PI / 2.0, 0.0).e,
[1.0, -22877334.0, 0.0, 0.0, 1.0, 0.0]
);
assert_eq!(
Transform::with_skew(0.0, PI / 4.0).e,
[1.0, 0.0, 0.0, 1.0, 1.0, 0.0]
);
assert_eq!(
Transform::default().skew(0.0, PI / 4.0).e,
[1.0, 0.0, 0.0, 1.0, 1.0, 0.0]
);
assert_eq!(
Transform::default().skew(0.0, PI / 4.0) * Pt(5.0, 3.0),
Pt(5.0, 8.0)
);
assert_eq!(
Transform::default().skew(0.0, PI / 4.0) * Pt(15.0, 7.0),
Pt(15.0, 22.0)
);
}
#[test]
fn test_transform() {
assert_eq!(
(Transform::with_translate(1.0, 2.0)
* Transform::with_scale(2.0, 2.0))
.e,
[2.0, 0.0, 2.0, 0.0, 2.0, 4.0]
);
assert_eq!(
Transform::with_translate(3.0, 5.0)
* Transform::with_scale(7.0, 11.0)
* Transform::with_rotate(f32::consts::PI / 2.0)
* Transform::with_skew(1.0, -2.0),
Transform::default()
.translate(3.0, 5.0)
.scale(7.0, 11.0)
.rotate(f32::consts::PI / 2.0)
.skew(1.0, -2.0)
);
}
}