use geometric_algebra::{ppga2d, ppga3d, GeometricQuotient, OuterProduct, RegressiveProduct, Transformation, Zero};
use std::convert::TryInto;
#[macro_export]
macro_rules! hash_set(
[ $($key:expr),*$(,)? ] => {
{
#[allow(unused_mut)]
let mut set = ::std::collections::HashSet::new();
$(set.insert($key);)*
set
}
};
);
#[macro_export]
macro_rules! hash_map(
{ $($key:expr => $value:expr),*$(,)? } => {
{
#[allow(unused_mut)]
let mut map = ::std::collections::HashMap::new();
$(map.insert($key, $value);)*
map
}
};
);
#[macro_export]
macro_rules! match_option {
($value:expr, $value_kind:path) => {
match $value {
$value_kind(value) => Some(value),
_ => None,
}
};
}
pub fn transmute_vec<S, T>(mut vec: Vec<S>) -> Vec<T> {
let ptr = vec.as_mut_ptr() as *mut T;
let len = vec.len() * std::mem::size_of::<S>() / std::mem::size_of::<T>();
let capacity = vec.capacity() * std::mem::size_of::<S>() / std::mem::size_of::<T>();
std::mem::forget(vec);
unsafe { Vec::from_raw_parts(ptr, len, capacity) }
}
pub fn transmute_slice<S, T>(slice: &[S]) -> &[T] {
let ptr = slice.as_ptr() as *const T;
let len = std::mem::size_of_val(slice) / std::mem::size_of::<T>();
unsafe { std::slice::from_raw_parts(ptr, len) }
}
pub fn transmute_slice_mut<S, T>(slice: &mut [S]) -> &mut [T] {
let ptr = slice.as_mut_ptr() as *mut T;
let len = std::mem::size_of_val(slice) / std::mem::size_of::<T>();
unsafe { std::slice::from_raw_parts_mut(ptr, len) }
}
pub fn line_line_intersection(a: ppga2d::Plane, b: ppga2d::Plane) -> ppga2d::Point {
let p = a.outer_product(b);
p * (1.0 / p[0])
}
pub fn aabb_to_convex_polygon(bounding_box: &[f32; 4]) -> [ppga2d::Point; 4] {
[
ppga2d::Point::new(1.0, bounding_box[0], bounding_box[1]),
ppga2d::Point::new(1.0, bounding_box[0], bounding_box[3]),
ppga2d::Point::new(1.0, bounding_box[2], bounding_box[3]),
ppga2d::Point::new(1.0, bounding_box[2], bounding_box[1]),
]
}
pub fn do_convex_polygons_overlap(a: &[ppga2d::Point], b: &[ppga2d::Point]) -> bool {
for (a, b) in [(a, b), (b, a)] {
'outer: for index in 0..a.len() {
let plane = a[(index + 1) % a.len()].regressive_product(a[index]);
for point in b {
if point.regressive_product(plane) <= 0.0 {
continue 'outer;
}
}
return false;
}
}
true
}
pub fn rotate_90_degree_clockwise(v: ppga2d::Plane) -> ppga2d::Plane {
ppga2d::Plane::new(0.0, v[2], -v[1])
}
pub fn point_to_vec(p: ppga2d::Point) -> [f32; 2] {
[p[1] / p[0], p[2] / p[0]]
}
pub fn vec_to_point(v: [f32; 2]) -> ppga2d::Point {
ppga2d::Point::new(1.0, v[0], v[1])
}
pub fn weighted_vec_to_point(w: f32, v: [f32; 2]) -> ppga2d::Point {
ppga2d::Point::new(w, v[0] * w, v[1] * w)
}
pub fn rotate2d(mut angle: f32) -> ppga2d::Motor {
angle *= 0.5;
ppga2d::Motor::new(angle.cos(), angle.sin(), 0.0, 0.0)
}
pub fn translate2d(v: [f32; 2]) -> ppga2d::Motor {
ppga2d::Motor::new(1.0, 0.0, -0.5 * v[1], 0.5 * v[0])
}
pub fn rotation2d(motor: ppga2d::Motor) -> f32 {
2.0 * motor[1].atan2(motor[0])
}
pub fn translation2d(mut motor: ppga2d::Motor) -> [f32; 2] {
motor = motor.geometric_quotient(ppga2d::Rotor::new(motor[0], motor[1]));
[2.0 * motor[3], -2.0 * motor[2]]
}
pub fn rotate_around_axis(angle: f32, axis: &[f32; 3]) -> ppga3d::Rotor {
let sinus = (angle * 0.5).sin();
ppga3d::Rotor::new((angle * 0.5).cos(), axis[0] * sinus, axis[1] * sinus, axis[2] * sinus)
}
pub fn motor2d_to_motor3d(motor: &ppga2d::Motor) -> ppga3d::Motor {
ppga3d::Motor::new(motor[0], 0.0, 0.0, motor[1], 0.0, -motor[3], motor[2], 0.0)
}
pub fn motor2d_to_mat3(motor: &ppga2d::Motor) -> [ppga2d::Point; 3] {
let result = [1, 2, 0]
.iter()
.map(|index| {
let mut point = ppga2d::Point::zero();
point[*index] = 1.0;
let row = motor.transformation(point);
ppga2d::Point::new(row[1], row[2], row[0])
})
.collect::<Vec<_>>();
result.try_into().unwrap()
}
pub fn motor3d_to_mat4(motor: &ppga3d::Motor) -> [ppga3d::Point; 4] {
let result = [1, 2, 3, 0]
.iter()
.map(|index| {
let mut point = ppga3d::Point::zero();
point[*index] = 1.0;
let row = motor.transformation(point);
ppga3d::Point::new(row[1], row[2], row[3], row[0])
})
.collect::<Vec<_>>();
result.try_into().unwrap()
}
pub fn perspective_projection(field_of_view_y: f32, aspect_ratio: f32, near: f32, far: f32) -> [ppga3d::Point; 4] {
let height = 1.0 / (field_of_view_y * 0.5).tan();
let denominator = 1.0 / (near - far);
[
ppga3d::Point::new(height / aspect_ratio, 0.0, 0.0, 0.0),
ppga3d::Point::new(0.0, height, 0.0, 0.0),
ppga3d::Point::new(0.0, 0.0, -far * denominator, 1.0),
ppga3d::Point::new(0.0, 0.0, near * far * denominator, 0.0),
]
}
pub fn matrix_multiplication(a: &[ppga3d::Point; 4], b: &[ppga3d::Point; 4]) -> [ppga3d::Point; 4] {
[
a[0] * b[0][0] + a[1] * b[0][1] + a[2] * b[0][2] + a[3] * b[0][3],
a[0] * b[1][0] + a[1] * b[1][1] + a[2] * b[1][2] + a[3] * b[1][3],
a[0] * b[2][0] + a[1] * b[2][1] + a[2] * b[2][2] + a[3] * b[2][3],
a[0] * b[3][0] + a[1] * b[3][1] + a[2] * b[3][2] + a[3] * b[3][3],
]
}
pub fn srgb_to_linear(mut color: [f32; 4]) -> [f32; 4] {
for channel in color.iter_mut().take(3) {
*channel = if *channel > 0.04045 {
((*channel + 0.055) / 1.055).powf(2.4)
} else {
*channel / 12.92
};
}
color
}
pub fn linear_to_srgb(mut color: [f32; 4]) -> [f32; 4] {
for channel in color.iter_mut().take(3) {
*channel = if *channel > 0.0031308 {
1.055 * (*channel).powf(1.0 / 2.4) - 0.055
} else {
12.92 * *channel
};
}
color
}