pathfinder/geometry/src/vector.rs

542 lines
11 KiB
Rust
Raw Normal View History

// pathfinder/geometry/src/basic/point.rs
//
// Copyright © 2019 The Pathfinder Project Developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! A SIMD-optimized point type.
use pathfinder_simd::default::{F32x2, F32x4, I32x2};
use std::ops::{Add, AddAssign, Mul, Neg, Sub};
2019-02-05 13:55:01 -05:00
/// 2D points with 32-bit floating point coordinates.
2019-01-12 20:13:58 -05:00
#[derive(Clone, Copy, Debug, Default)]
pub struct Vector2F(pub F32x2);
impl Vector2F {
#[inline]
pub fn new(x: f32, y: f32) -> Vector2F {
Vector2F(F32x2::new(x, y))
}
#[inline]
pub fn splat(value: f32) -> Vector2F {
Vector2F(F32x2::splat(value))
}
#[inline]
2019-12-07 14:51:47 -05:00
pub fn to_3d(self) -> Vector3F {
Vector3F(self.0.to_f32x4().concat_xy_zw(F32x4::new(0.0, 0.0, 0.0, 0.0)))
}
#[inline]
pub fn to_4d(self) -> Vector4F {
Vector4F(self.0.to_f32x4().concat_xy_zw(F32x4::new(0.0, 0.0, 0.0, 1.0)))
}
#[inline]
pub fn x(self) -> f32 {
self.0[0]
}
#[inline]
pub fn y(self) -> f32 {
self.0[1]
}
2019-01-28 15:57:15 -05:00
#[inline]
pub fn set_x(&mut self, x: f32) {
self.0[0] = x;
}
#[inline]
pub fn set_y(&mut self, y: f32) {
self.0[1] = y;
}
#[inline]
pub fn min(self, other: Vector2F) -> Vector2F {
Vector2F(self.0.min(other.0))
}
#[inline]
pub fn max(self, other: Vector2F) -> Vector2F {
Vector2F(self.0.max(other.0))
}
#[inline]
pub fn clamp(self, min_val: Vector2F, max_val: Vector2F) -> Vector2F {
self.max(min_val).min(max_val)
}
#[inline]
pub fn det(self, other: Vector2F) -> f32 {
self.x() * other.y() - self.y() * other.x()
}
2019-01-28 15:57:15 -05:00
#[inline]
pub fn dot(self, other: Vector2F) -> f32 {
let xy = self.0 * other.0;
xy.x() + xy.y()
}
2019-01-28 15:57:15 -05:00
#[inline]
pub fn scale(self, x: f32) -> Vector2F {
Vector2F(self.0 * F32x2::splat(x))
2019-01-28 15:57:15 -05:00
}
2019-02-05 13:55:01 -05:00
2019-02-05 23:10:20 -05:00
#[inline]
pub fn scale_xy(self, factors: Vector2F) -> Vector2F {
Vector2F(self.0 * factors.0)
2019-02-05 23:10:20 -05:00
}
2019-02-05 13:55:01 -05:00
#[inline]
pub fn floor(self) -> Vector2F {
Vector2F(self.0.floor())
2019-02-05 13:55:01 -05:00
}
#[inline]
pub fn ceil(self) -> Vector2F {
Vector2F(self.0.ceil())
2019-02-05 13:55:01 -05:00
}
2019-02-05 23:10:20 -05:00
/// Rounds both coordinates to the nearest integer.
#[inline]
pub fn round(self) -> Vector2F {
Vector2F(self.0.to_i32x2().to_f32x2())
}
/// Treats this point as a vector and calculates its squared length.
#[inline]
pub fn square_length(self) -> f32 {
let squared = self.0 * self.0;
squared[0] + squared[1]
}
2019-02-05 23:10:20 -05:00
/// Treats this point as a vector and calculates its length.
#[inline]
pub fn length(self) -> f32 {
f32::sqrt(self.square_length())
2019-02-05 23:10:20 -05:00
}
2019-02-06 16:12:53 -05:00
/// Treats this point as a vector and normalizes it.
#[inline]
pub fn normalize(self) -> Vector2F {
2019-02-06 16:12:53 -05:00
self.scale(1.0 / self.length())
}
/// Swaps y and x.
#[inline]
pub fn yx(self) -> Vector2F {
Vector2F(self.0.yx())
2019-02-06 16:12:53 -05:00
}
#[inline]
pub fn is_zero(self) -> bool {
self == Vector2F::default()
}
2019-02-20 19:21:58 -05:00
#[inline]
pub fn lerp(self, other: Vector2F, t: f32) -> Vector2F {
self + (other - self).scale(t)
2019-02-20 19:21:58 -05:00
}
#[inline]
pub fn to_i32(self) -> Vector2I {
Vector2I(self.0.to_i32x2())
}
}
impl PartialEq for Vector2F {
#[inline]
fn eq(&self, other: &Vector2F) -> bool {
self.0.packed_eq(other.0).all_true()
}
}
impl Add<Vector2F> for Vector2F {
type Output = Vector2F;
#[inline]
fn add(self, other: Vector2F) -> Vector2F {
Vector2F(self.0 + other.0)
}
}
impl Sub<Vector2F> for Vector2F {
type Output = Vector2F;
#[inline]
fn sub(self, other: Vector2F) -> Vector2F {
Vector2F(self.0 - other.0)
}
}
impl Mul<Vector2F> for Vector2F {
type Output = Vector2F;
#[inline]
fn mul(self, other: Vector2F) -> Vector2F {
Vector2F(self.0 * other.0)
}
}
impl Neg for Vector2F {
type Output = Vector2F;
#[inline]
fn neg(self) -> Vector2F {
Vector2F(-self.0)
}
}
2019-02-05 13:55:01 -05:00
/// 2D points with 32-bit signed integer coordinates.
#[derive(Clone, Copy, Debug, Default)]
pub struct Vector2I(pub I32x2);
2019-02-05 13:55:01 -05:00
impl Vector2I {
2019-02-05 13:55:01 -05:00
#[inline]
pub fn new(x: i32, y: i32) -> Vector2I {
Vector2I(I32x2::new(x, y))
2019-02-05 13:55:01 -05:00
}
#[inline]
pub fn splat(value: i32) -> Vector2I {
Vector2I(I32x2::splat(value))
2019-02-05 13:55:01 -05:00
}
#[inline]
pub fn x(self) -> i32 {
2019-02-05 13:55:01 -05:00
self.0[0]
}
#[inline]
pub fn y(self) -> i32 {
2019-02-05 13:55:01 -05:00
self.0[1]
}
#[inline]
pub fn set_x(&mut self, x: i32) {
self.0[0] = x;
}
#[inline]
pub fn set_y(&mut self, y: i32) {
self.0[1] = y;
}
2019-02-07 19:38:24 -05:00
#[inline]
pub fn min(self, other: Vector2I) -> Vector2I {
Vector2I(self.0.min(other.0))
}
#[inline]
pub fn max(self, other: Vector2I) -> Vector2I {
Vector2I(self.0.max(other.0))
}
2019-02-07 19:38:24 -05:00
#[inline]
pub fn scale(self, factor: i32) -> Vector2I {
Vector2I(self.0 * I32x2::splat(factor))
2019-02-07 19:38:24 -05:00
}
#[inline]
pub fn scale_xy(self, factors: Vector2I) -> Vector2I {
Vector2I(self.0 * factors.0)
}
#[inline]
pub fn to_f32(self) -> Vector2F {
Vector2F(self.0.to_f32x2())
}
2019-02-05 13:55:01 -05:00
}
impl Add<Vector2I> for Vector2I {
type Output = Vector2I;
#[inline]
fn add(self, other: Vector2I) -> Vector2I {
Vector2I(self.0 + other.0)
}
}
impl AddAssign<Vector2I> for Vector2I {
#[inline]
fn add_assign(&mut self, other: Vector2I) {
self.0 += other.0
}
}
impl Neg for Vector2I {
type Output = Vector2I;
#[inline]
fn neg(self) -> Vector2I {
Vector2I(-self.0)
}
}
impl Sub<Vector2I> for Vector2I {
type Output = Vector2I;
2019-02-07 19:38:24 -05:00
#[inline]
fn sub(self, other: Vector2I) -> Vector2I {
Vector2I(self.0 - other.0)
2019-02-07 19:38:24 -05:00
}
}
impl PartialEq for Vector2I {
#[inline]
fn eq(&self, other: &Vector2I) -> bool {
self.0.packed_eq(other.0).all_true()
}
}
2019-12-07 14:51:47 -05:00
/// 3D points.
///
/// The w value in the SIMD vector is always 0.0.
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct Vector3F(pub F32x4);
impl Vector3F {
#[inline]
pub fn new(x: f32, y: f32, z: f32) -> Vector3F {
Vector3F(F32x4::new(x, y, z, 0.0))
}
#[inline]
pub fn splat(x: f32) -> Vector3F {
let mut vector = F32x4::splat(x);
vector.set_w(0.0);
Vector3F(vector)
}
/// Truncates this vector to 2D.
#[inline]
pub fn to_2d(self) -> Vector2F {
Vector2F(self.0.xy())
}
/// Converts this vector to an equivalent 3D homogeneous one with a w component of 1.0.
#[inline]
pub fn to_4d(self) -> Vector4F {
let mut vector = self.0;
vector.set_w(1.0);
Vector4F(vector)
}
#[inline]
pub fn cross(self, other: Vector3F) -> Vector3F {
Vector3F(self.0.yzxw() * other.0.zxyw() - self.0.zxyw() * other.0.yzxw())
}
#[inline]
pub fn square_length(self) -> f32 {
let squared = self.0 * self.0;
squared[0] + squared[1] + squared[2]
}
#[inline]
pub fn length(self) -> f32 {
f32::sqrt(self.square_length())
}
#[inline]
pub fn normalize(self) -> Vector3F {
Vector3F(self.0 * F32x4::splat(1.0 / self.length()))
}
#[inline]
pub fn x(self) -> f32 {
self.0[0]
}
#[inline]
pub fn y(self) -> f32 {
self.0[1]
}
#[inline]
pub fn z(self) -> f32 {
self.0[2]
}
#[inline]
pub fn scale(self, factor: f32) -> Vector3F {
Vector3F(self.0 * F32x4::splat(factor))
}
2019-12-07 14:51:47 -05:00
}
impl Add<Vector3F> for Vector3F {
type Output = Vector3F;
#[inline]
fn add(self, other: Vector3F) -> Vector3F {
Vector3F(self.0 + other.0)
}
}
impl AddAssign for Vector3F {
#[inline]
fn add_assign(&mut self, other: Vector3F) {
self.0 += other.0
}
}
impl Neg for Vector3F {
type Output = Vector3F;
#[inline]
fn neg(self) -> Vector3F {
Vector3F(self.0 * F32x4::new(-1.0, -1.0, -1.0, 0.0))
}
}
2019-12-07 14:51:47 -05:00
impl Sub<Vector3F> for Vector3F {
type Output = Vector3F;
#[inline]
fn sub(self, other: Vector3F) -> Vector3F {
Vector3F(self.0 - other.0)
}
}
2019-02-05 13:55:01 -05:00
/// 3D homogeneous points.
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Vector4F(pub F32x4);
impl Vector4F {
#[inline]
pub fn new(x: f32, y: f32, z: f32, w: f32) -> Vector4F {
Vector4F(F32x4::new(x, y, z, w))
}
#[inline]
pub fn splat(value: f32) -> Vector4F {
Vector4F(F32x4::splat(value))
}
#[inline]
pub fn to_2d(self) -> Vector2F {
2019-12-07 14:51:47 -05:00
self.to_3d().to_2d()
}
/// Performs perspective division to convert this vector to 3D.
#[inline]
pub fn to_3d(self) -> Vector3F {
let mut vector = self.0 * F32x4::splat(1.0 / self.w());
vector.set_w(0.0);
Vector3F(vector)
}
#[inline]
pub fn x(self) -> f32 {
self.0[0]
}
#[inline]
pub fn y(self) -> f32 {
self.0[1]
}
#[inline]
pub fn z(self) -> f32 {
self.0[2]
}
#[inline]
pub fn w(self) -> f32 {
self.0[3]
}
2019-02-08 18:16:53 -05:00
#[inline]
pub fn scale(self, x: f32) -> Vector4F {
2019-02-08 18:16:53 -05:00
let mut factors = F32x4::splat(x);
factors[3] = 1.0;
Vector4F(self.0 * factors)
2019-02-08 18:16:53 -05:00
}
2019-01-17 11:45:23 -05:00
#[inline]
pub fn set_x(&mut self, x: f32) {
self.0[0] = x
}
#[inline]
pub fn set_y(&mut self, y: f32) {
self.0[1] = y
}
2019-01-16 23:41:05 -05:00
#[inline]
pub fn set_z(&mut self, z: f32) {
self.0[2] = z
}
2019-01-17 11:45:23 -05:00
#[inline]
pub fn set_w(&mut self, w: f32) {
self.0[3] = w
}
2019-01-25 17:28:53 -05:00
#[inline]
pub fn approx_eq(self, other: Vector4F, epsilon: f32) -> bool {
2019-01-25 17:28:53 -05:00
self.0.approx_eq(other.0, epsilon)
}
2019-01-29 18:44:31 -05:00
/// Checks to see whether this *homogeneous* coordinate equals zero.
///
/// Note that since this treats the coordinate as a homogeneous coordinate, the `w` is ignored.
// TODO(pcwalton): Optimize with SIMD.
#[inline]
pub fn is_zero(self) -> bool {
self.x() == 0.0 && self.y() == 0.0 && self.z() == 0.0
}
2019-01-31 18:29:13 -05:00
#[inline]
pub fn lerp(self, other: Vector4F, t: f32) -> Vector4F {
Vector4F(self.0 + (other.0 - self.0) * F32x4::splat(t))
2019-01-31 18:29:13 -05:00
}
}
impl Add<Vector4F> for Vector4F {
type Output = Vector4F;
2019-01-16 23:41:05 -05:00
#[inline]
fn add(self, other: Vector4F) -> Vector4F {
Vector4F(self.0 + other.0)
2019-01-16 23:41:05 -05:00
}
}
impl AddAssign for Vector4F {
2019-01-16 23:41:05 -05:00
#[inline]
fn add_assign(&mut self, other: Vector4F) {
2019-01-16 23:41:05 -05:00
self.0 += other.0
}
}
impl Mul<Vector4F> for Vector4F {
type Output = Vector4F;
#[inline]
fn mul(self, other: Vector4F) -> Vector4F {
Vector4F(self.0 * other.0)
}
}
impl Neg for Vector4F {
type Output = Vector4F;
/// NB: This does not negate w, because that is rarely what you what for homogeneous
/// coordinates.
#[inline]
fn neg(self) -> Vector4F {
Vector4F(self.0 * F32x4::new(-1.0, -1.0, -1.0, 1.0))
}
}
2019-12-07 14:51:47 -05:00
impl Sub<Vector4F> for Vector4F {
type Output = Vector4F;
#[inline]
fn sub(self, other: Vector4F) -> Vector4F {
Vector4F(self.0 - other.0)
}
}
impl Default for Vector4F {
#[inline]
fn default() -> Vector4F {
let mut point = F32x4::default();
point.set_w(1.0);
Vector4F(point)
}
}