use crate::{
core::utilities::{squared_euclidean_distance_3d, euclidean_distance_3d},
core::transformation::{build_rotation_matrix, rotate_node},
};
pub fn select_nodes_closest_to_point(nodes: &[[f64; 3]], point: [f64; 3]) -> Vec<usize> {
let mut min_distance = f64::INFINITY;
let mut closest_indices: Vec<usize> = Vec::new();
for (i, node) in nodes.iter().enumerate() {
let distance = squared_euclidean_distance_3d(*node, point);
if (distance - min_distance).abs() <= f64::EPSILON {
closest_indices.push(i);
} else if distance < min_distance {
closest_indices.clear();
closest_indices.push(i);
min_distance = distance;
}
}
closest_indices
}
pub fn select_nodes_on_line(
nodes: &[[f64; 3]],
point_a: [f64; 3],
point_b: [f64; 3],
) -> Vec<usize> {
let ab = euclidean_distance_3d(point_a, point_b);
(0..nodes.len())
.filter(|&id| {
let node = nodes[id];
let ap = euclidean_distance_3d(point_a, node);
let bp = euclidean_distance_3d(point_b, node);
(ap + bp - ab).abs() <= f64::EPSILON && ap <= ab && bp <= ab
})
.collect()
}
pub fn select_nodes_in_sphere(
nodes: &[[f64; 3]],
radius: f64,
centre: [f64; 3],
) -> Vec<usize> {
let radius_squared = radius * radius;
(0..nodes.len())
.filter(|&id| {
let node = nodes[id];
squared_euclidean_distance_3d(node, centre) <= radius_squared
})
.collect()
}
pub fn select_nodes_in_box(
nodes: &[[f64; 3]],
length: [f64; 3],
centre: [f64; 3],
theta: [f64; 3],
) -> Vec<usize> {
let reversed_theta = [-theta[0], -theta[1], -theta[2]];
let rotation_matrix = build_rotation_matrix(&reversed_theta);
(0..nodes.len())
.filter(|&id| {
let mut node = nodes[id];
rotate_node(&mut node, &rotation_matrix, ¢re);
(node[0] >= centre[0] - length[0] * 0.5
&& node[0] <= centre[0] + length[0] * 0.5)
&& (node[1] >= centre[1] - length[1] * 0.5
&& node[1] <= centre[1] + length[1] * 0.5)
&& (node[2] >= centre[2] - length[2] * 0.5
&& node[2] <= centre[2] + length[2] * 0.5)
})
.collect()
}
pub fn select_nodes_closest_to_plane(
nodes: &[[f64; 3]],
origin: [f64; 3],
normal: [f64; 3],
) -> Vec<usize> {
let d = origin
.iter()
.zip(normal.iter())
.map(|(o, n)| o * n)
.sum::<f64>();
let mut closest_node_ids: Vec<usize> = Vec::new();
let mut min_distance = f64::MAX;
for (id, node) in nodes.iter().enumerate() {
let distance = (normal
.iter()
.zip(node.iter())
.map(|(n, x)| n * x)
.sum::<f64>()
- d)
.abs();
if distance < min_distance {
min_distance = distance;
closest_node_ids.clear();
closest_node_ids.push(id);
} else if (distance - min_distance).abs() < f64::EPSILON {
closest_node_ids.push(id);
}
}
closest_node_ids
}
pub fn select_nodes_in_plane_direction(
nodes: &[[f64; 3]],
origin: [f64; 3],
normal: [f64; 3],
) -> Vec<usize> {
(0..nodes.len())
.filter(|&id| is_node_on_positive_side(nodes[id], origin, normal))
.collect()
}
pub fn is_node_on_positive_side(
node: [f64; 3],
origin: [f64; 3],
normal: [f64; 3],
) -> bool {
let dot_product = normal
.iter()
.zip(node.iter())
.map(|(n, p)| n * p)
.sum::<f64>()
- origin
.iter()
.zip(normal.iter())
.map(|(o, n)| o * n)
.sum::<f64>();
dot_product >= 0.0
}
#[cfg(test)]
mod tests {
use crate::core::{primitives::generate_box, mesh::Mesh};
use super::*;
fn new_block() -> Mesh {
generate_box([1., 1., 1.], [0.5, 0.5, 0.5], [0.0, 0.0, 0.0], [2, 2, 1]).unwrap()
}
#[test]
fn test_select_node_closest_to() {
let block = new_block();
let closest_nodes = select_nodes_closest_to_point(&block.nodes, [0.4, 0.4, 0.4]);
assert_eq!(closest_nodes[0], 8);
}
#[test]
fn test_select_nodes_on_line() {
let block = new_block();
let selected_nodes =
select_nodes_on_line(&block.nodes, [0.0, 0.0, 0.0], [1.0, 0.0, 0.0]);
assert_eq!(selected_nodes.len(), 3);
assert_eq!(selected_nodes[0], 0);
assert_eq!(selected_nodes[1], 6);
assert_eq!(selected_nodes[2], 12);
}
#[test]
fn test_select_nodes_in_sphere() {
let block = new_block();
let selected_nodes = select_nodes_in_sphere(&block.nodes, 0.5, [0.0, 0.0, 0.0]);
assert_eq!(selected_nodes.len(), 3);
assert_eq!(selected_nodes[0], 0);
assert_eq!(selected_nodes[1], 2);
assert_eq!(selected_nodes[2], 6);
}
#[test]
fn test_select_nodes_in_box() {
let block = new_block();
let selected_nodes = select_nodes_in_box(
&block.nodes,
[1.0, 0.5, 0.5],
[0.0, 0.0, 0.0],
[0.0, 0.0, 0.0],
);
assert_eq!(selected_nodes.len(), 2);
assert_eq!(selected_nodes[0], 0);
assert_eq!(selected_nodes[1], 6);
}
}