use js_sys::Float32Array;
use wasm_bindgen::prelude::*;
use crate::{
simd::{
addition_simd, exp_bivector_simd, geometric_product_simd, lerp_simd, norm_simd,
normalize_simd, reverse_simd, rotor_slerp_simd, sandwich_simd, scalar_mul_simd,
subtraction_simd, SimdBatch,
},
GpuMultivector,
};
#[wasm_bindgen]
pub struct WasmMultivector {
inner: GpuMultivector,
}
#[wasm_bindgen]
impl WasmMultivector {
#[wasm_bindgen(constructor)]
#[must_use]
pub fn new() -> Self {
Self {
inner: GpuMultivector::zero(),
}
}
#[wasm_bindgen]
#[must_use]
pub fn scalar(s: f32) -> Self {
Self {
inner: GpuMultivector::scalar(s),
}
}
#[wasm_bindgen]
#[must_use]
pub fn vector(x: f32, y: f32, z: f32) -> Self {
Self {
inner: GpuMultivector::vector(x, y, z),
}
}
#[wasm_bindgen(js_name = "fromCoeffs")]
#[must_use]
pub fn from_coeffs(coeffs: &[f32]) -> Self {
let mut inner = GpuMultivector::zero();
for (dst, &c) in inner.coeffs.iter_mut().zip(coeffs.iter()) {
*dst = c;
}
Self { inner }
}
#[wasm_bindgen(js_name = "getScalar")]
#[must_use]
pub fn get_scalar(&self) -> f32 {
self.inner.get_scalar()
}
#[wasm_bindgen(js_name = "getVector")]
#[must_use]
#[allow(clippy::tuple_array_conversions)]
pub fn get_vector(&self) -> Float32Array {
let (x, y, z) = self.inner.get_vector();
Float32Array::from([x, y, z].as_slice())
}
#[wasm_bindgen(js_name = "getCoeffs")]
#[must_use]
pub fn get_coeffs(&self) -> Float32Array {
Float32Array::from(&self.inner.coeffs[..])
}
#[wasm_bindgen]
#[must_use]
pub fn add(&self, other: &Self) -> Self {
Self {
inner: addition_simd(&self.inner, &other.inner),
}
}
#[wasm_bindgen]
#[must_use]
pub fn sub(&self, other: &Self) -> Self {
Self {
inner: subtraction_simd(&self.inner, &other.inner),
}
}
#[wasm_bindgen]
#[must_use]
pub fn scale(&self, s: f32) -> Self {
Self {
inner: scalar_mul_simd(&self.inner, s),
}
}
#[wasm_bindgen(js_name = "geometricProduct")]
#[must_use]
pub fn geometric_product(&self, other: &Self) -> Self {
Self {
inner: geometric_product_simd(&self.inner, &other.inner),
}
}
#[wasm_bindgen]
#[must_use]
pub fn reverse(&self) -> Self {
Self {
inner: reverse_simd(&self.inner),
}
}
#[wasm_bindgen]
#[must_use]
pub fn normalize(&self) -> Self {
Self {
inner: normalize_simd(&self.inner),
}
}
#[wasm_bindgen]
#[must_use]
pub fn norm(&self) -> f32 {
norm_simd(&self.inner)
}
#[wasm_bindgen]
#[must_use]
pub fn sandwich(&self, other: &Self) -> Self {
Self {
inner: sandwich_simd(&other.inner, &self.inner),
}
}
#[wasm_bindgen]
#[must_use]
pub fn exp(&self) -> Self {
Self {
inner: exp_bivector_simd(&self.inner),
}
}
#[wasm_bindgen]
#[must_use]
pub fn lerp(&self, other: &Self, t: f32) -> Self {
Self {
inner: lerp_simd(&self.inner, &other.inner, t),
}
}
#[wasm_bindgen]
#[must_use]
pub fn slerp(&self, other: &Self, t: f32) -> Self {
Self {
inner: rotor_slerp_simd(&self.inner, &other.inner, t),
}
}
#[wasm_bindgen(js_name = "clone")]
#[must_use]
pub fn clone_mv(&self) -> Self {
Self { inner: self.inner }
}
}
impl Default for WasmMultivector {
fn default() -> Self {
Self::new()
}
}
#[wasm_bindgen]
pub struct WasmBatch;
fn float32_to_mvs(data: &Float32Array) -> Vec<GpuMultivector> {
let vec: Vec<f32> = data.to_vec();
vec.as_chunks::<8>()
.0
.iter()
.map(|coeffs| GpuMultivector { coeffs: *coeffs })
.collect()
}
fn mvs_to_float32(mvs: &[GpuMultivector]) -> Float32Array {
let flat: Vec<f32> = mvs.iter().flat_map(|mv| mv.coeffs).collect();
Float32Array::from(&flat[..])
}
#[wasm_bindgen]
impl WasmBatch {
#[wasm_bindgen(js_name = "geometricProduct")]
#[must_use]
pub fn geometric_product(a: &Float32Array, b: &Float32Array) -> Float32Array {
let a_mvs = float32_to_mvs(a);
let b_mvs = float32_to_mvs(b);
let results = SimdBatch::geometric_product(&a_mvs, &b_mvs);
mvs_to_float32(&results)
}
#[wasm_bindgen]
#[must_use]
pub fn addition(a: &Float32Array, b: &Float32Array) -> Float32Array {
let a_mvs = float32_to_mvs(a);
let b_mvs = float32_to_mvs(b);
let results = SimdBatch::addition(&a_mvs, &b_mvs);
mvs_to_float32(&results)
}
#[wasm_bindgen]
#[must_use]
pub fn sandwich(rotors: &Float32Array, vectors: &Float32Array) -> Float32Array {
let rotor_mvs = float32_to_mvs(rotors);
let vector_mvs = float32_to_mvs(vectors);
let results = SimdBatch::sandwich(&rotor_mvs, &vector_mvs);
mvs_to_float32(&results)
}
#[wasm_bindgen]
#[must_use]
pub fn exp(bivectors: &Float32Array) -> Float32Array {
let mvs = float32_to_mvs(bivectors);
let results = SimdBatch::exp(&mvs);
mvs_to_float32(&results)
}
#[wasm_bindgen]
#[must_use]
pub fn normalize(mvs: &Float32Array) -> Float32Array {
let mv_vec = float32_to_mvs(mvs);
let results = SimdBatch::normalize(&mv_vec);
mvs_to_float32(&results)
}
#[wasm_bindgen(js_name = "rotorSlerp")]
#[must_use]
pub fn rotor_slerp(a: &Float32Array, b: &Float32Array, t: f32) -> Float32Array {
let a_mvs = float32_to_mvs(a);
let b_mvs = float32_to_mvs(b);
let results = SimdBatch::rotor_slerp(&a_mvs, &b_mvs, t);
mvs_to_float32(&results)
}
}
#[wasm_bindgen(js_name = "createRotorXY")]
#[must_use]
pub fn create_rotor_xy(angle: f32) -> WasmMultivector {
let half = angle / 2.0;
let mut inner = GpuMultivector::zero();
inner.coeffs[0] = half.cos();
inner.coeffs[3] = half.sin(); WasmMultivector { inner }
}
#[wasm_bindgen(js_name = "createRotorXZ")]
#[must_use]
pub fn create_rotor_xz(angle: f32) -> WasmMultivector {
let half = angle / 2.0;
let mut inner = GpuMultivector::zero();
inner.coeffs[0] = half.cos();
inner.coeffs[5] = half.sin(); WasmMultivector { inner }
}
#[wasm_bindgen(js_name = "createRotorYZ")]
#[must_use]
pub fn create_rotor_yz(angle: f32) -> WasmMultivector {
let half = angle / 2.0;
let mut inner = GpuMultivector::zero();
inner.coeffs[0] = half.cos();
inner.coeffs[6] = half.sin(); WasmMultivector { inner }
}
#[wasm_bindgen(js_name = "createRotorAxisAngle")]
#[must_use]
pub fn create_rotor_axis_angle(ax: f32, ay: f32, az: f32, angle: f32) -> WasmMultivector {
let len = (ax * ax + ay * ay + az * az).sqrt();
if len < 1e-10 {
return WasmMultivector::scalar(1.0);
}
let ax = ax / len;
let ay = ay / len;
let az = az / len;
let half = angle / 2.0;
let s = half.sin();
let mut inner = GpuMultivector::zero();
inner.coeffs[0] = half.cos();
inner.coeffs[3] = az * s; inner.coeffs[5] = -ay * s; inner.coeffs[6] = ax * s;
WasmMultivector { inner }
}
#[wasm_bindgen(js_name = "getDispatchThreshold")]
#[must_use]
pub fn get_dispatch_threshold() -> usize {
crate::GPU_DISPATCH_THRESHOLD
}