use wasm_bindgen::prelude::*;
use crate::{Dodecet, DodecetArray, DodecetError, Result};
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(typescript_type = "number")]
pub type JsNumber;
}
fn to_js_error(err: DodecetError) -> JsValue {
JsValue::from_str(&err.to_string())
}
#[wasm_bindgen]
pub struct WasmDodecet {
inner: Dodecet,
}
#[wasm_bindgen]
impl WasmDodecet {
#[wasm_bindgen(constructor)]
pub fn new(value: u16) -> Result<WasmDodecet, JsValue> {
Dodecet::new(value)
.map(|inner| WasmDodecet { inner })
.map_err(to_js_error)
}
#[wasm_bindgen(js_name = fromHexUnchecked)]
pub fn from_hex_unchecked(value: u16) -> WasmDodecet {
WasmDodecet {
inner: Dodecet::from_hex(value),
}
}
pub fn value(&self) -> u16 {
self.inner.value()
}
pub fn nibble(&self, index: u8) -> Result<u8, JsValue> {
self.inner.nibble(index).map_err(to_js_error)
}
#[wasm_bindgen(js_name = setNibble)]
pub fn set_nibble(&mut self, index: u8, nibble: u8) -> Result<(), JsValue> {
self.inner.set_nibble(index, nibble).map_err(to_js_error)
}
#[wasm_bindgen(js_name = isZero)]
pub fn is_zero(&self) -> bool {
self.inner.is_zero()
}
#[wasm_bindgen(js_name = isMax)]
pub fn is_max(&self) -> bool {
self.inner.is_max()
}
#[wasm_bindgen(js_name = countOnes)]
pub fn count_ones(&self) -> u32 {
self.inner.count_ones()
}
#[wasm_bindgen(js_name = toHex)]
pub fn to_hex(&self) -> String {
self.inner.to_hex_string()
}
#[wasm_bindgen(js_name = fromHex)]
pub fn from_hex(s: &str) -> Result<WasmDodecet, JsValue> {
Dodecet::from_hex_str(s)
.map(|inner| WasmDodecet { inner })
.map_err(to_js_error)
}
#[wasm_bindgen(js_name = toBinary)]
pub fn to_binary(&self) -> String {
self.inner.to_binary_string()
}
#[wasm_bindgen(js_name = asSigned)]
pub fn as_signed(&self) -> i16 {
self.inner.as_signed()
}
pub fn normalize(&self) -> f64 {
self.inner.normalize()
}
pub fn and(&self, other: &WasmDodecet) -> WasmDodecet {
WasmDodecet {
inner: self.inner.and(other.inner),
}
}
pub fn or(&self, other: &WasmDodecet) -> WasmDodecet {
WasmDodecet {
inner: self.inner.or(other.inner),
}
}
pub fn xor(&self, other: &WasmDodecet) -> WasmDodecet {
WasmDodecet {
inner: self.inner.xor(other.inner),
}
}
pub fn not(&self) -> WasmDodecet {
WasmDodecet {
inner: self.inner.not(),
}
}
pub fn add(&self, other: &WasmDodecet) -> WasmDodecet {
WasmDodecet {
inner: self.inner.wrapping_add(other.inner),
}
}
pub fn sub(&self, other: &WasmDodecet) -> WasmDodecet {
WasmDodecet {
inner: self.inner.wrapping_sub(other.inner),
}
}
pub fn mul(&self, other: &WasmDodecet) -> WasmDodecet {
WasmDodecet {
inner: self.inner.wrapping_mul(other.inner),
}
}
pub fn clone(&self) -> WasmDodecet {
WasmDodecet {
inner: self.inner,
}
}
pub fn toString(&self) -> String {
format!("{:?}", self.inner)
}
}
#[wasm_bindgen]
pub struct WasmPoint3D {
inner: crate::geometric::Point3D,
}
#[wasm_bindgen]
impl WasmPoint3D {
#[wasm_bindgen(constructor)]
pub fn new(x: u16, y: u16, z: u16) -> WasmPoint3D {
WasmPoint3D {
inner: crate::geometric::Point3D::new(x, y, z),
}
}
pub fn x(&self) -> u16 {
self.inner.x()
}
pub fn y(&self) -> u16 {
self.inner.y()
}
pub fn z(&self) -> u16 {
self.inner.z()
}
#[wasm_bindgen(js_name = normalized)]
pub fn normalized(&self) -> JsValue {
let (x, y, z) = self.inner.normalized();
let result = js_sys::Object::new();
js_sys::Reflect::set(&result, &"x".into(), &x.into()).unwrap();
js_sys::Reflect::set(&result, &"y".into(), &y.into()).unwrap();
js_sys::Reflect::set(&result, &"z".into(), &z.into()).unwrap();
result.into()
}
#[wasm_bindgen(js_name = signed)]
pub fn signed(&self) -> JsValue {
let (x, y, z) = self.inner.signed();
let result = js_sys::Object::new();
js_sys::Reflect::set(&result, &"x".into(), &x.into()).unwrap();
js_sys::Reflect::set(&result, &"y".into(), &y.into()).unwrap();
js_sys::Reflect::set(&result, &"z".into(), &z.into()).unwrap();
result.into()
}
#[wasm_bindgen(js_name = distanceTo)]
pub fn distance_to(&self, other: &WasmPoint3D) -> f64 {
self.inner.distance_to(&other.inner)
}
#[wasm_bindgen(js_name = toHex)]
pub fn to_hex(&self) -> String {
self.inner.to_hex_string()
}
#[wasm_bindgen(js_name = fromHex)]
pub fn from_hex(s: &str) -> Result<WasmPoint3D, JsValue> {
crate::geometric::Point3D::from_hex_str(s)
.map(|inner| WasmPoint3D { inner })
.map_err(to_js_error)
}
pub fn clone(&self) -> WasmPoint3D {
WasmPoint3D {
inner: self.inner.clone(),
}
}
}
#[wasm_bindgen]
pub struct WasmVector3D {
inner: crate::geometric::Vector3D,
}
#[wasm_bindgen]
impl WasmVector3D {
#[wasm_bindgen(constructor)]
pub fn new(x: i16, y: i16, z: i16) -> WasmVector3D {
WasmVector3D {
inner: crate::geometric::Vector3D::new(x, y, z),
}
}
pub fn x(&self) -> i16 {
self.inner.x()
}
pub fn y(&self) -> i16 {
self.inner.y()
}
pub fn z(&self) -> i16 {
self.inner.z()
}
pub fn magnitude(&self) -> f64 {
self.inner.magnitude()
}
pub fn normalize(&self) -> JsValue {
let (x, y, z) = self.inner.normalize();
let result = js_sys::Object::new();
js_sys::Reflect::set(&result, &"x".into(), &x.into()).unwrap();
js_sys::Reflect::set(&result, &"y".into(), &y.into()).unwrap();
js_sys::Reflect::set(&result, &"z".into(), &z.into()).unwrap();
result.into()
}
pub fn dot(&self, other: &WasmVector3D) -> i32 {
self.inner.dot(&other.inner)
}
pub fn cross(&self, other: &WasmVector3D) -> WasmVector3D {
WasmVector3D {
inner: self.inner.cross(&other.inner),
}
}
pub fn add(&self, other: &WasmVector3D) -> WasmVector3D {
WasmVector3D {
inner: self.inner.add(&other.inner),
}
}
pub fn sub(&self, other: &WasmVector3D) -> WasmVector3D {
WasmVector3D {
inner: self.inner.sub(&other.inner),
}
}
pub fn scale(&self, scalar: f64) -> WasmVector3D {
WasmVector3D {
inner: self.inner.scale(scalar),
}
}
pub fn clone(&self) -> WasmVector3D {
WasmVector3D {
inner: self.inner.clone(),
}
}
}
#[wasm_bindgen]
pub struct DodecetUtils;
#[wasm_bindgen]
impl DodecetUtils {
#[wasm_bindgen(js_name = MAX_DODECET)]
pub fn max_dodecet() -> u16 {
crate::MAX_DODECET
}
#[wasm_bindgen(js_name = DODECET_BITS)]
pub fn dodecet_bits() -> u8 {
crate::DODECET_BITS
}
#[wasm_bindgen(js_name = NIBBLES)]
pub fn nibbles() -> u8 {
crate::NIBBLES
}
#[wasm_bindgen(js_name = CAPACITY)]
pub fn capacity() -> u16 {
crate::CAPACITY
}
#[wasm_bindgen(js_name = encodeFloat)]
pub fn encode_float(value: f64) -> WasmDodecet {
let clamped = value.clamp(0.0, 1.0);
let encoded = (clamped * 4095.0) as u16;
WasmDodecet {
inner: Dodecet::from_hex(encoded),
}
}
#[wasm_bindgen(js_name = decodeFloat)]
pub fn decode_float(dodecet: &WasmDodecet) -> f64 {
dodecet.inner.normalize()
}
#[wasm_bindgen(js_name = encodeFloatArray)]
pub fn encode_float_array(values: &js_sys::Array) -> JsValue {
let result = js_sys::Array::new();
for value in values.iter() {
if let Some(num) = value.as_f64() {
let encoded = Self::encode_float(num);
result.push(&encoded.into());
}
}
result.into()
}
#[wasm_bindgen(js_name = decodeDodecetArray)]
pub fn decode_dodecet_array(dodecets: &js_sys::Array) -> JsValue {
let result = js_sys::Array::new();
for value in dodecets.iter() {
if let Some(dodecet) = value.dyn_ref::<WasmDodecet>() {
result.push(&Self::decode_float(dodecet).into());
}
}
result.into()
}
}
#[wasm_bindgen]
pub const MAX_DODECET: u16 = 4095;
#[wasm_bindgen]
pub const DODECET_BITS: u8 = 12;
#[wasm_bindgen]
pub const NIBBLES: u8 = 3;
#[wasm_bindgen]
pub const CAPACITY: u16 = 4096;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_wasm_dodecet_creation() {
let d = WasmDodecet::new(0xABC).unwrap();
assert_eq!(d.value(), 0xABC);
}
#[test]
fn test_wasm_point3d() {
let point = WasmPoint3D::new(0x100, 0x200, 0x300);
assert_eq!(point.x(), 0x100);
assert_eq!(point.y(), 0x200);
assert_eq!(point.z(), 0x300);
}
#[test]
fn test_wasm_vector3d() {
let v = WasmVector3D::new(100, 200, 300);
assert_eq!(v.x(), 100);
assert_eq!(v.y(), 200);
assert_eq!(v.z(), 300);
}
#[test]
fn test_utils_encode_decode() {
let original = 0.5;
let encoded = DodecetUtils::encode_float(original);
let decoded = DodecetUtils::decode_float(&encoded);
assert!((decoded - original).abs() < 0.001);
}
}