use euclidean::orthogonal_vector;
use linear_isomorphic::*;
use num_traits::float::TotalOrder;
use crate::lagrange_polynomials::partial_derivatives;
pub fn numerical_derivative<T, F, S>(x: S, f: &F, h: S) -> T
where
T: InnerSpace<S>,
S: linear_isomorphic::RealField,
F: Fn(S) -> T,
{
let denom = h + h;
let num = f(x + h) - f(x - h);
num * (S::from(1.0).unwrap() / denom)
}
pub fn tangent<T, F, S>(x: S, f: &F, h: S) -> T
where
T: InnerSpace<S>,
S: linear_isomorphic::RealField,
F: Fn(S) -> T,
{
numerical_derivative(x, f, h)
}
pub fn numerical_gradient<T, F, S>(point: &T, f: F, dim: usize, epsilon: S) -> T
where
T: InnerSpace<S>,
S: linear_isomorphic::RealField + core::ops::Mul<T, Output = T>,
F: Fn(&T) -> S,
{
let mut res = point.clone();
let mut sample = point.clone();
for i in 0..dim
{
sample[i] += epsilon;
let v1 = f(&sample);
sample[i] -= S::from(2.0).unwrap() * epsilon;
let v2 = f(&sample);
sample[i] = point[i];
res[i] = (v1 - v2) / S::from(2.0).unwrap() * epsilon;
}
res
}
pub fn implicit_curvature_features<V, S, F>(
point: &V,
fun: F,
epsilon: S,
) -> (S, S, [V; 3])
where
V: InnerSpace<S>,
S: RealField
+ std::iter::Sum
+ std::iter::Product
+ TotalOrder
+ core::ops::Mul<V, Output = V>,
F: Fn(&V) -> S + Clone,
{
let n: V = numerical_gradient::<V, _, S>(point, &fun, 3, epsilon).normalized();
let u: V = orthogonal_vector::<S, V>(&n);
let v = n.cross(&u);
let d1 = u.clone();
let d2 = v.clone();
let d3 = n.clone();
let fun_as_tangent = move |x, y, z| {
let mut transformed_point = V::default();
transformed_point += u.clone() * x;
transformed_point += v.clone() * y;
transformed_point += n.clone() * z;
transformed_point += point.clone();
fun(&transformed_point)
};
let phi_uu = partial_derivatives(
[S::from(0.).unwrap(); 3],
&fun_as_tangent,
[2, 0, 0],
epsilon,
3,
);
let phi_vv = partial_derivatives(
[S::from(0.).unwrap(); 3],
&fun_as_tangent,
[0, 2, 0],
epsilon,
3,
);
let phi_uv = partial_derivatives(
[S::from(0.).unwrap(); 3],
&fun_as_tangent,
[1, 1, 0],
epsilon,
3,
);
let phi_n = partial_derivatives(
[S::from(0.).unwrap(); 3],
&fun_as_tangent,
[0, 0, 1],
epsilon,
3,
);
let gaussian_curvature = (phi_uu * phi_vv - (phi_uv * phi_uv)) / (phi_n * phi_n);
let mean_curvature = (phi_uu + phi_vv) / (S::from(2.).unwrap() * phi_n.abs());
let coeff = (mean_curvature * mean_curvature - gaussian_curvature)
.abs()
.sqrt();
let kmin = mean_curvature - coeff;
let kmax = mean_curvature + coeff;
let t1 = d1.clone() * phi_uv + d2.clone() * (kmin * phi_n - phi_uu);
let t2 = d1.clone() * (kmax * phi_n - phi_vv) + d2.clone() * phi_uv;
(kmin, kmax, [t1, t2, d3])
}
pub fn implicit_gaussian_curvature<V, S, F>(point: &V, fun: F, epsilon: S) -> S
where
V: InnerSpace<S>,
S: RealField
+ std::iter::Sum
+ std::iter::Product
+ TotalOrder
+ core::ops::Mul<V, Output = V>,
F: Fn(&V) -> S + Clone,
{
let n = numerical_gradient(point, &fun, 3, epsilon).normalized();
let u = orthogonal_vector(&n);
let v = n.cross(&u);
let fun_as_tangent = move |x, y, z| {
let mut transformed_point = V::default();
transformed_point += u.clone() * x;
transformed_point += v.clone() * y;
transformed_point += n.clone() * z;
transformed_point += point.clone();
fun(&transformed_point)
};
let phi_uu = partial_derivatives(
[S::from(0.).unwrap(); 3],
&fun_as_tangent,
[2, 0, 0],
epsilon,
3,
);
let phi_vv = partial_derivatives(
[S::from(0.).unwrap(); 3],
&fun_as_tangent,
[0, 2, 0],
epsilon,
3,
);
let phi_uv = partial_derivatives(
[S::from(0.).unwrap(); 3],
&fun_as_tangent,
[1, 1, 0],
epsilon,
3,
);
let phi_n = partial_derivatives(
[S::from(0.).unwrap(); 3],
&fun_as_tangent,
[0, 0, 1],
epsilon,
3,
);
(phi_uu * phi_vv - (phi_uv * phi_uv)) / (phi_n * phi_n)
}
pub fn implicit_mean_curvature<V, S, F>(point: &V, fun: F, epsilon: S) -> S
where
V: InnerSpace<S>,
S: RealField
+ std::iter::Sum
+ std::iter::Product
+ TotalOrder
+ core::ops::Mul<V, Output = V>,
F: Fn(&V) -> S + Clone,
{
let n = numerical_gradient(point, &fun, 3, epsilon).normalized();
let u = orthogonal_vector(&n);
let v = n.cross(&u);
let fun_as_tangent = move |x, y, z| {
let mut transformed_point = V::default();
transformed_point += u.clone() * x;
transformed_point += v.clone() * y;
transformed_point += n.clone() * z;
transformed_point += point.clone();
fun(&transformed_point)
};
let phi_uu = partial_derivatives(
[S::from(0.).unwrap(); 3],
&fun_as_tangent,
[2, 0, 0],
epsilon,
3,
);
let phi_vv = partial_derivatives(
[S::from(0.).unwrap(); 3],
&fun_as_tangent,
[0, 2, 0],
epsilon,
3,
);
let phi_n = partial_derivatives(
[S::from(0.).unwrap(); 3],
&fun_as_tangent,
[0, 0, 1],
epsilon,
3,
);
(phi_uu + phi_vv) / (S::from(2.).unwrap() * phi_n.abs())
}
pub fn sample_discrete_curve<V, S>(t: S, curve: &[V]) -> V
where
V: linear_isomorphic::InnerSpace<S>,
S: linear_isomorphic::RealField,
{
debug_assert!(t.is_finite());
t.clamp(S::from(0.).unwrap(), S::from(1.).unwrap());
let t = t.clamp(S::from(0.).unwrap(), S::from(1.).unwrap());
let arclength = (0..curve.len() - 1)
.map(|i| (curve[i + 1].clone() - curve[i].clone()).norm())
.fold(S::from(0.).unwrap(), |acc, x| acc + x);
let target = t * arclength;
let mut current = S::from(0.).unwrap();
let mut i = 0;
loop
{
let l = (curve[i].clone() - curve[i + 1].clone()).norm();
if current + l >= target || i >= curve.len() - 1
{
break;
}
current += l;
i += 1;
}
let v = target - current;
let extent = (curve[i].clone() - curve[i + 1].clone()).norm();
let t = v / extent;
curve[i].clone() * (S::from(1.).unwrap() - t) + curve[i + 1].clone() * t
}
pub fn sample_discrete_bitangents<V, S>(t: S, curve: &[V], bitangents: &[V]) -> V
where
V: linear_isomorphic::InnerSpace<S>,
S: linear_isomorphic::RealField,
{
debug_assert!(t.is_finite());
t.clamp(S::from(0.).unwrap(), S::from(1.).unwrap());
let t = t.clamp(S::from(0.).unwrap(), S::from(1.).unwrap());
let arclength = (0..curve.len() - 1)
.map(|i| (curve[i + 1].clone() - curve[i].clone()).norm())
.fold(S::from(0.).unwrap(), |acc, x| acc + x);
let target = t * arclength;
let mut current = S::from(0.).unwrap();
let mut i = 0;
loop
{
let l = (curve[i].clone() - curve[i + 1].clone()).norm();
if current + l >= target || i >= curve.len() - 1
{
break;
}
current += l;
i += 1;
}
let v = target - current;
let extent = (curve[i].clone() - curve[i + 1].clone()).norm();
let t = v / extent;
bitangents[i].clone() * (S::from(1.).unwrap() - t) + bitangents[i + 1].clone() * t
}
#[cfg(test)]
mod tests
{
use super::*;
type Vec3 = nalgebra::Vector3<f64>;
#[test]
fn test_implicit_curvature()
{
let sphere_sdf = |p: &Vec3| p.dot(&p) - 4.;
let curvature =
implicit_mean_curvature(&Vec3::new(4.0, 0., 0.), sphere_sdf, 0.01);
assert!((curvature - 0.25).abs() < 0.01, "{curvature}");
let sphere_sdf = |p: &Vec3| p.dot(&p) - 4.;
let curvature =
implicit_gaussian_curvature(&Vec3::new(4.0, 0., 0.), sphere_sdf, 0.1);
assert!((curvature - 1. / 16.).abs() < 0.01, "{}", curvature);
}
}