oxihuman_morph/
rbf_deformer.rs1#![allow(dead_code)]
4
5#[derive(Debug, Clone, Copy, PartialEq)]
9pub enum RbfKernel {
10 Gaussian,
11 Multiquadric,
12 InverseQuadratic,
13}
14
15#[derive(Debug, Clone)]
17pub struct RbfControlPoint {
18 pub center: Vec<f32>,
19 pub coefficient: f32,
20}
21
22#[derive(Debug, Clone)]
24pub struct RbfDeformer {
25 pub kernel: RbfKernel,
26 pub epsilon: f32,
27 pub control_points: Vec<RbfControlPoint>,
28}
29
30impl RbfDeformer {
31 pub fn new(kernel: RbfKernel) -> Self {
32 RbfDeformer {
33 kernel,
34 epsilon: 1.0,
35 control_points: Vec::new(),
36 }
37 }
38}
39
40pub fn new_rbf_deformer(kernel: RbfKernel) -> RbfDeformer {
42 RbfDeformer::new(kernel)
43}
44
45pub fn rbf_kernel_value(deformer: &RbfDeformer, distance: f32) -> f32 {
47 let r = distance * deformer.epsilon;
48 match deformer.kernel {
49 RbfKernel::Gaussian => (-r * r).exp(),
50 RbfKernel::Multiquadric => (1.0 + r * r).sqrt(),
51 RbfKernel::InverseQuadratic => 1.0 / (1.0 + r * r),
52 }
53}
54
55pub fn rbf_add_control_point(deformer: &mut RbfDeformer, center: Vec<f32>, coefficient: f32) {
57 deformer.control_points.push(RbfControlPoint {
58 center,
59 coefficient,
60 });
61}
62
63pub fn rbf_point_count(deformer: &RbfDeformer) -> usize {
65 deformer.control_points.len()
66}
67
68pub fn rbf_evaluate(deformer: &RbfDeformer, query: &[f32]) -> f32 {
70 deformer
71 .control_points
72 .iter()
73 .map(|cp| {
74 let n = cp.center.len().min(query.len());
75 let dist: f32 = (0..n)
76 .map(|i| (cp.center[i] - query[i]).powi(2))
77 .sum::<f32>()
78 .sqrt();
79 cp.coefficient * rbf_kernel_value(deformer, dist)
80 })
81 .sum()
82}
83
84pub fn rbf_to_json(deformer: &RbfDeformer) -> String {
86 format!(
87 r#"{{"kernel":"{}","epsilon":{:.4},"points":{}}}"#,
88 match deformer.kernel {
89 RbfKernel::Gaussian => "gaussian",
90 RbfKernel::Multiquadric => "multiquadric",
91 RbfKernel::InverseQuadratic => "inverse_quadratic",
92 },
93 deformer.epsilon,
94 deformer.control_points.len()
95 )
96}
97
98#[cfg(test)]
99mod tests {
100 use super::*;
101
102 #[test]
103 fn test_new_rbf_deformer_no_points() {
104 let d = new_rbf_deformer(RbfKernel::Gaussian);
105 assert_eq!(
106 rbf_point_count(&d),
107 0, );
109 }
110
111 #[test]
112 fn test_add_control_point_increases_count() {
113 let mut d = new_rbf_deformer(RbfKernel::Gaussian);
114 rbf_add_control_point(&mut d, vec![0.0, 0.0], 1.0);
115 assert_eq!(rbf_point_count(&d), 1 ,);
116 }
117
118 #[test]
119 fn test_gaussian_at_zero_distance_is_one() {
120 let d = new_rbf_deformer(RbfKernel::Gaussian);
121 let v = rbf_kernel_value(&d, 0.0);
122 assert!((v - 1.0).abs() < 1e-5, );
123 }
124
125 #[test]
126 fn test_inverse_quadratic_at_zero_distance_is_one() {
127 let d = new_rbf_deformer(RbfKernel::InverseQuadratic);
128 let v = rbf_kernel_value(&d, 0.0);
129 assert!((v - 1.0).abs() < 1e-5, );
130 }
131
132 #[test]
133 fn test_multiquadric_at_zero_distance_is_one() {
134 let d = new_rbf_deformer(RbfKernel::Multiquadric);
135 let v = rbf_kernel_value(&d, 0.0);
136 assert!((v - 1.0).abs() < 1e-5, );
137 }
138
139 #[test]
140 fn test_evaluate_empty_is_zero() {
141 let d = new_rbf_deformer(RbfKernel::Gaussian);
142 let v = rbf_evaluate(&d, &[0.0, 0.0]);
143 assert!((v).abs() < 1e-6 ,);
144 }
145
146 #[test]
147 fn test_evaluate_at_control_point() {
148 let mut d = new_rbf_deformer(RbfKernel::Gaussian);
149 rbf_add_control_point(&mut d, vec![0.0, 0.0], 1.0);
150 let v = rbf_evaluate(&d, &[0.0, 0.0]);
151 assert!(
152 (v - 1.0).abs() < 1e-5, );
154 }
155
156 #[test]
157 fn test_to_json_contains_kernel() {
158 let d = new_rbf_deformer(RbfKernel::Gaussian);
159 let j = rbf_to_json(&d);
160 assert!(j.contains("gaussian"), );
161 }
162
163 #[test]
164 fn test_epsilon_default_one() {
165 let d = new_rbf_deformer(RbfKernel::Gaussian);
166 assert!((d.epsilon - 1.0).abs() < 1e-5, );
167 }
168
169 #[test]
170 fn test_gaussian_decays_with_distance() {
171 let d = new_rbf_deformer(RbfKernel::Gaussian);
172 let near = rbf_kernel_value(&d, 0.1);
173 let far = rbf_kernel_value(&d, 2.0);
174 assert!(near > far ,);
175 }
176}