use std::fmt::Debug;
use log::error;
use num_traits::Float;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::types::{
computation::{
model::{Data, DataType, Parameter},
traits::{ImplicitComponent, ImplicitFunction, ModelFloat},
},
geometry::{BoundingBox, Vec3},
};
use super::traits::SignedDistance;
static SPHERE_PARAMS: &[Parameter; 2] = &[
Parameter {
name: "Centre",
data_type: DataType::Vec3,
},
Parameter {
name: "Radius",
data_type: DataType::Value,
},
];
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, Copy)]
pub struct Sphere<T> {
pub centre: Vec3<T>,
pub radius: T,
}
impl<T> Sphere<T> {
pub fn new(centre: Vec3<T>, radius: T) -> Self {
Self { centre, radius }
}
pub fn at_coord(x: T, y: T, z: T, radius: T) -> Self {
Self {
centre: Vec3::new(x, y, z),
radius,
}
}
}
impl<T: Float> Sphere<T> {
pub fn bounds(&self) -> BoundingBox<T> {
let min = self.centre - Vec3::new(self.radius, self.radius, self.radius);
let max = self.centre + Vec3::new(self.radius, self.radius, self.radius);
BoundingBox::new(min, max)
}
}
impl<T: ModelFloat> SignedDistance<T> for Sphere<T> {
fn signed_distance(&self, x: T, y: T, z: T) -> T {
self.centre.distance_to_coord(x, y, z) - self.radius
}
}
impl<T: ModelFloat> ImplicitFunction<T> for Sphere<T> {
fn eval(&self, x: T, y: T, z: T) -> T {
self.centre.distance_to_coord(x, y, z) - self.radius
}
}
impl<T: ModelFloat> ImplicitComponent<T> for Sphere<T> {
fn parameters(&self) -> &[Parameter] {
SPHERE_PARAMS
}
fn set_parameter(&mut self, parameter_name: &str, data: Data<T>) {
if !(Parameter::set_vec3_from_param(parameter_name, &data, "Centre", &mut self.centre)
|| Parameter::set_value_from_param(parameter_name, &data, "Radius", &mut self.radius))
{
error!("Unknown parameter name: {}", parameter_name);
}
}
fn read_parameter(&self, parameter_name: &str) -> Option<Data<T>> {
match parameter_name {
"Centre" => Some(Data::Vec3(self.centre)),
"Radius" => Some(Data::Value(self.radius)),
_ => None,
}
}
fn name(&self) -> &'static str {
"Sphere"
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get_assigns_params() {
let mut sphere = Sphere::new(Vec3::new(1., 1., 1.), 10.);
let params = sphere.parameters().to_vec();
for param in params {
match param.data_type {
DataType::Value => sphere.set_parameter(param.name, Data::Value(1.)),
DataType::Vec3 => {
sphere.set_parameter(param.name, Data::Vec3(Vec3::new(1., 1., 1.)))
}
_ => panic!("Error in the param"),
}
}
assert!((sphere.radius - 1.).abs() < f64::epsilon());
assert!(sphere.centre.distance_to_coord(1., 1., 1.).abs() < f64::epsilon());
}
}