use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
mod convert;
mod only;
mod uniform;
mod zero;
#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, Default)]
#[serde(rename = "Point2d")]
#[serde(rename_all = "snake_case")]
#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
pub struct Point2d<T = f32> {
#[allow(missing_docs)]
pub x: T,
#[allow(missing_docs)]
pub y: T,
}
impl std::fmt::Display for Point2d<f64> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}
impl<T: PartialEq> PartialEq for Point2d<T> {
fn eq(&self, other: &Self) -> bool {
self.x == other.x && self.y == other.y
}
}
impl<T> Point2d<T> {
pub fn with_z(self, z: T) -> Point3d<T> {
let Self { x, y } = self;
Point3d { x, y, z }
}
pub fn map<U, F>(self, mut f: F) -> Point2d<U>
where
F: FnMut(T) -> U,
{
let Self { x, y } = self;
Point2d { x: f(x), y: f(y) }
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq, Default)]
#[serde(rename = "Point3d")]
#[serde(rename_all = "snake_case")]
#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
pub struct Point3d<T = f32> {
#[allow(missing_docs)]
pub x: T,
#[allow(missing_docs)]
pub y: T,
#[allow(missing_docs)]
pub z: T,
}
impl std::fmt::Display for Point3d<f64> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "({}, {}, {})", self.x, self.y, self.z)
}
}
impl From<euler::Vec3> for Point3d<f32> {
fn from(v: euler::Vec3) -> Self {
Self { x: v.x, y: v.y, z: v.z }
}
}
impl<T> Point3d<T> {
pub fn from_2d(Point2d { x, y }: Point2d<T>, z: T) -> Self {
Self { x, y, z }
}
pub fn with_w(self, w: T) -> Point4d<T> {
let Self { x, y, z } = self;
Point4d { x, y, z, w }
}
pub fn map<U, F>(self, mut f: F) -> Point3d<U>
where
F: FnMut(T) -> U,
{
let Self { x, y, z } = self;
Point3d {
x: f(x),
y: f(y),
z: f(z),
}
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema)]
#[serde(rename = "Point4d")]
#[serde(rename_all = "snake_case")]
#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
#[cfg_attr(feature = "ts-rs", ts(export_to = "ModelingCmd.ts"))]
pub struct Point4d<T = f32> {
#[allow(missing_docs)]
pub x: T,
#[allow(missing_docs)]
pub y: T,
#[allow(missing_docs)]
pub z: T,
#[allow(missing_docs)]
pub w: T,
}
impl std::fmt::Display for Point4d<f64> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "({}, {}, {}, {})", self.x, self.y, self.z, self.w)
}
}
impl<T> Point4d<T> {
pub fn map<U, F>(self, mut f: F) -> Point4d<U>
where
F: FnMut(T) -> U,
{
let Self { x, y, z, w } = self;
Point4d {
x: f(x),
y: f(y),
z: f(z),
w: f(w),
}
}
}
impl<T> Point4d<T>
where
T: Copy,
{
pub const fn uniform_3d(xyz: T, w: T) -> Self {
Self {
x: xyz,
y: xyz,
z: xyz,
w,
}
}
}
pub type Quaternion = Point4d;
impl Default for Quaternion {
fn default() -> Self {
Self {
x: 0.0,
y: 0.0,
z: 0.0,
w: 1.0,
}
}
}
impl<T: PartialEq> PartialEq for Point4d<T> {
fn eq(&self, other: &Self) -> bool {
self.x == other.x && self.y == other.y && self.z == other.z && self.w == other.w
}
}
macro_rules! impl_arithmetic {
($typ:ident, $op:ident, $op_assign:ident, $method:ident, $method_assign:ident, $($i:ident),*) => {
impl<T> std::ops::$op<$typ<T>> for $typ<T>
where
T: std::ops::$op<Output = T>,
{
type Output = $typ<T>;
fn $method(self, rhs: $typ<T>) -> Self::Output {
Self {
$(
$i: self.$i.$method(rhs.$i),
)*
}
}
}
impl<T> std::ops::$op_assign for $typ<T>
where
T: std::ops::$op_assign<T>,
{
fn $method_assign(&mut self, other: Self) {
$(
self.$i.$method_assign(other.$i);
)*
}
}
};
}
macro_rules! impl_scalar_arithmetic {
($typ:ident, $op:ident, $op_assign:ident, $method:ident, $method_assign:ident, $($i:ident),*) => {
impl<T> std::ops::$op<T> for $typ<T>
where
T: std::ops::$op<Output = T> + Copy,
{
type Output = $typ<T>;
fn $method(self, rhs: T) -> Self::Output {
Self {
$(
$i: self.$i.$method(rhs),
)*
}
}
}
impl<T> std::ops::$op_assign<T> for $typ<T>
where
T: std::ops::$op_assign<T> + Copy,
{
fn $method_assign(&mut self, other: T) {
$(
self.$i.$method_assign(other);
)*
}
}
};
}
impl_arithmetic!(Point2d, Add, AddAssign, add, add_assign, x, y);
impl_arithmetic!(Point3d, Add, AddAssign, add, add_assign, x, y, z);
impl_arithmetic!(Point2d, Sub, SubAssign, sub, sub_assign, x, y);
impl_arithmetic!(Point3d, Sub, SubAssign, sub, sub_assign, x, y, z);
impl_arithmetic!(Point2d, Mul, MulAssign, mul, mul_assign, x, y);
impl_arithmetic!(Point3d, Mul, MulAssign, mul, mul_assign, x, y, z);
impl_arithmetic!(Point2d, Div, DivAssign, div, div_assign, x, y);
impl_arithmetic!(Point3d, Div, DivAssign, div, div_assign, x, y, z);
impl_scalar_arithmetic!(Point2d, Mul, MulAssign, mul, mul_assign, x, y);
impl_scalar_arithmetic!(Point3d, Mul, MulAssign, mul, mul_assign, x, y, z);
impl_scalar_arithmetic!(Point2d, Div, DivAssign, div, div_assign, x, y);
impl_scalar_arithmetic!(Point3d, Div, DivAssign, div, div_assign, x, y, z);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_math() {
let actual = Point2d { x: 1.0, y: 2.0 } + Point2d { x: 10.0, y: 20.0 };
let expected = Point2d { x: 11.0, y: 22.0 };
assert_eq!(actual, expected);
}
#[test]
fn test_math_assign() {
let mut p = Point2d { x: 1.0, y: 2.0 };
p += Point2d { x: 10.0, y: 20.0 };
let expected = Point2d { x: 11.0, y: 22.0 };
assert_eq!(p, expected);
}
#[test]
fn test_scaling() {
let actual = Point2d { x: 1.0, y: 2.0 } * 3.0;
let expected = Point2d { x: 3.0, y: 6.0 };
assert_eq!(actual, expected);
}
#[test]
fn test_scaling_assign() {
let mut actual = Point2d { x: 1.0, y: 2.0 };
actual *= 3.0;
let expected = Point2d { x: 3.0, y: 6.0 };
assert_eq!(actual, expected);
}
}