1use crate::{
2 core::transformation::rotate_nodes,
3 morph::{
4 linear_algebra::least_squares_solver,
5 transforms::{apply_affine_transform, apply_bernstein_transform},
6 design_block::DesignBlock,
7 },
8 error::{Error, Result},
9};
10
11const REFERENCE_COORDINATE_SYSTEM: [[f64; 3]; 4] = [
12 [0.0, 0.0, 0.0],
13 [1.0, 0.0, 0.0],
14 [0.0, 1.0, 0.0],
15 [0.0, 0.0, 1.0],
16];
17
18pub struct FreeFormDeformer {
20 pub original_design_block: DesignBlock,
22 pub affine_weights_pre: [[f64; 4]; 4],
24 pub affine_weights_post: [[f64; 4]; 4],
26}
27
28impl FreeFormDeformer {
29 pub fn new(original_design_block: DesignBlock) -> Result<Self> {
37 let affine_weights_pre = evaluate_affine_weights(
39 &original_design_block.local_coordinate_system,
40 &REFERENCE_COORDINATE_SYSTEM,
41 )?;
42
43 let affine_weights_post = evaluate_affine_weights(
44 &REFERENCE_COORDINATE_SYSTEM,
45 &original_design_block.local_coordinate_system,
46 )?;
47
48 Ok(FreeFormDeformer {
49 original_design_block,
50 affine_weights_pre,
51 affine_weights_post,
52 })
53 }
54
55 pub fn deform(
64 &self,
65 points: &[[f64; 3]],
66 deformed_design_nodes: &[[f64; 3]],
67 ) -> Result<Vec<[f64; 3]>> {
68 if self.original_design_block.nodes.len() != deformed_design_nodes.len() {
69 return Err(Error::Deformation(
70 "The number of original and deformed design points must match!"
71 .to_string(),
72 ));
73 }
74
75 let corner_point = self.original_design_block.corner_nodes[0];
77 let scaled_points = apply_affine_transform(
78 &points
79 .iter()
80 .map(|pt| {
81 [
82 pt[0] - corner_point[0],
83 pt[1] - corner_point[1],
84 pt[2] - corner_point[2],
85 ]
86 })
87 .collect::<Vec<[f64; 3]>>(),
88 &self.affine_weights_pre,
89 )?;
90
91 let point_ids_in_unit_cube = indices_inside_unit_cube(&scaled_points);
92
93 let scaled_points_in_cube = point_ids_in_unit_cube
94 .iter()
95 .map(|&index| scaled_points[index])
96 .collect::<Vec<[f64; 3]>>();
97
98 let deltas = evaluate_deltas(
100 &self.original_design_block.nodes,
101 deformed_design_nodes,
102 self.original_design_block.theta,
103 self.original_design_block.centre,
104 self.original_design_block.scaling_factors,
105 );
106
107 let shifted_points = apply_bernstein_transform(
109 &scaled_points_in_cube,
110 &deltas,
111 &self.original_design_block.resolution,
112 )?;
113
114 let reverse_scaled_points =
116 apply_affine_transform(&shifted_points, &self.affine_weights_post)?;
117
118 let mut new_points = points.to_vec();
119
120 point_ids_in_unit_cube
121 .iter()
122 .zip(reverse_scaled_points)
123 .for_each(|(&id, pt)| {
124 new_points[id] = [
125 pt[0] + corner_point[0],
126 pt[1] + corner_point[1],
127 pt[2] + corner_point[2],
128 ]
129 });
130
131 Ok(new_points)
132 }
133}
134
135fn evaluate_affine_weights(
155 source_coordinate_system: &[[f64; 3]; 4],
156 target_coordinate_system: &[[f64; 3]; 4],
157) -> Result<[[f64; 4]; 4]> {
158 if source_coordinate_system.len() != target_coordinate_system.len() {
159 return Err(Error::Deformation(
160 "source_coordinate_system and target_coordinate_system must be of the same size!".to_string(),
161 ));
162 }
163
164 let padded_source_coordinate_system = source_coordinate_system
165 .iter()
166 .map(|&[x1, x2, x3]| vec![x1, x2, x3, 1.0])
167 .collect::<Vec<Vec<f64>>>();
168
169 let padded_target_coordinate_system = target_coordinate_system
170 .iter()
171 .map(|&[x1, x2, x3]| vec![x1, x2, x3, 1.0])
172 .collect::<Vec<Vec<f64>>>();
173
174 let a = least_squares_solver(
175 &padded_source_coordinate_system,
176 &padded_target_coordinate_system,
177 )?;
178
179 let mut arr = [[0.0; 4]; 4];
180 for (i, row) in a.iter().enumerate() {
181 for (j, &value) in row.iter().enumerate() {
182 arr[i][j] = value;
183 }
184 }
185
186 Ok(arr)
187}
188
189fn indices_inside_unit_cube(points: &[[f64; 3]]) -> Vec<usize> {
197 let mut indx_points_inside = Vec::new();
198
199 for (index, &point) in points.iter().enumerate() {
200 if point.iter().all(|&coord| (0.0..=1.0).contains(&coord)) {
201 indx_points_inside.push(index);
202 }
203 }
204
205 indx_points_inside
206}
207
208pub fn evaluate_deltas(
220 original_nodes: &[[f64; 3]],
221 deformed_nodes: &[[f64; 3]],
222 theta: [f64; 3],
223 centre: [f64; 3],
224 scaling_factors: [f64; 3],
225) -> Vec<[f64; 3]> {
226 let mut original = original_nodes.to_vec();
227 let mut deformed = deformed_nodes.to_vec();
228 let neg_theta = [-theta[0], -theta[1], -theta[2]];
229
230 rotate_nodes(&mut original, &neg_theta, ¢re);
231 rotate_nodes(&mut deformed, &neg_theta, ¢re);
232
233 original
234 .iter()
235 .zip(deformed.iter())
236 .map(|(&ori, &def)| {
237 [
238 scaling_factors[0] * (def[0] - ori[0]),
239 scaling_factors[1] * (def[1] - ori[1]),
240 scaling_factors[2] * (def[2] - ori[2]),
241 ]
242 })
243 .collect()
244}
245
246#[cfg(test)]
247mod tests {
248 use super::*;
249
250 #[test]
251 fn test_evaluate_deltas() {
252 let original_nodes = &[[1.0, 1.0, 1.0], [2.0, 2.0, 2.0]];
253
254 let deformed_nodes = &[[2.0, 2.0, 2.0], [3.0, 3.0, 3.0]];
255
256 let theta = [0.0, 0.0, 0.0];
257 let centre = [0.0, 0.0, 0.0];
258 let scaling_factors = [1.0, 1.0, 1.0];
259
260 let deltas = evaluate_deltas(
261 original_nodes,
262 deformed_nodes,
263 theta,
264 centre,
265 scaling_factors,
266 );
267
268 assert_eq!(deltas, vec![[1.0, 1.0, 1.0], [1.0, 1.0, 1.0]]);
269 }
270}