use crate::core::transformation::{build_rotation_matrix, rotate_node};
use crate::error::{Error, Result};
pub fn generate_block_cluster(
length: [f64; 3],
centre: [f64; 3],
theta: [f64; 3],
resolution: [usize; 3],
) -> Result<Vec<[f64; 3]>> {
for &s in &length {
if s <= 0.0 {
return Err(Error::MeshGeneration(
"Invalid length: dimensions must be positive.".to_string(),
));
}
}
for &r in &resolution {
if r == 0 {
return Err(Error::MeshGeneration(
"Invalid resolution: dimensions must be non-zero.".to_string(),
));
}
}
let [rx, ry, rz] = resolution;
let step = [
length[0] / (rx as f64),
length[1] / (ry as f64),
length[2] / (rz as f64),
];
let start = [
centre[0] - length[0] * 0.5,
centre[1] - length[1] * 0.5,
centre[2] - length[2] * 0.5,
];
let rotation_matrix = build_rotation_matrix(&theta);
let mut nodes = vec![[0.0; 3]; (rx + 1) * (ry + 1) * (rz + 1)];
let mut node_count = 0;
for i in 0..=rx {
for j in 0..=ry {
for k in 0..=rz {
nodes[node_count] = [
start[0] + i as f64 * step[0],
start[1] + j as f64 * step[1],
start[2] + k as f64 * step[2],
];
if theta.iter().any(|angle| *angle != 0.0) {
rotate_node(&mut nodes[node_count], &rotation_matrix, ¢re);
}
node_count += 1;
}
}
}
Ok(nodes)
}
pub fn generate_sphere_cluster(
radius: f64,
centre: [f64; 3],
resolution: usize,
) -> Result<Vec<[f64; 3]>> {
if radius <= 0.0 {
return Err(Error::MeshGeneration(
"Invalid radius: must be positive.".to_string(),
));
}
if resolution == 0 {
return Err(Error::MeshGeneration(
"Invalid resolution: must be non-zero.".to_string(),
));
}
let mut nodes = Vec::new();
let length = radius * 2.0;
let step = length / (resolution as f64);
let start = [centre[0] - radius, centre[1] - radius, centre[2] - radius];
for i in 0..=resolution {
for j in 0..=resolution {
for k in 0..=resolution {
let point = [
start[0] + i as f64 * step,
start[1] + j as f64 * step,
start[2] + k as f64 * step,
];
let dist_sq = (point[0] - centre[0]).powi(2)
+ (point[1] - centre[1]).powi(2)
+ (point[2] - centre[2]).powi(2);
if dist_sq <= radius.powi(2) {
nodes.push(point);
}
}
}
}
Ok(nodes)
}
#[cfg(test)]
mod tests {
use super::*;
const EPSILON: f64 = 1e-9;
#[test]
fn test_generate_block_cluster() {
let length = [2.0, 2.0, 2.0];
let centre = [1.0, 1.0, 1.0];
let theta = [0.0, 0.0, 0.0];
let resolution = [2, 2, 2];
let nodes = generate_block_cluster(length, centre, theta, resolution).unwrap();
assert_eq!(nodes.len(), 3 * 3 * 3);
assert!((nodes[0][0] - 0.0).abs() < EPSILON);
assert!((nodes[0][1] - 0.0).abs() < EPSILON);
assert!((nodes[0][2] - 0.0).abs() < EPSILON);
let last_node = nodes.last().unwrap();
assert!((last_node[0] - 2.0).abs() < EPSILON);
assert!((last_node[1] - 2.0).abs() < EPSILON);
assert!((last_node[2] - 2.0).abs() < EPSILON);
let result =
generate_block_cluster([0.0, 1.0, 1.0], [0.0; 3], [0.0; 3], [1, 1, 1]);
assert!(result.is_err());
assert!(matches!(result, Err(Error::MeshGeneration(_))));
let result =
generate_block_cluster([1.0, 1.0, 1.0], [0.0; 3], [0.0; 3], [1, 0, 1]);
assert!(result.is_err());
assert!(matches!(result, Err(Error::MeshGeneration(_))));
}
#[test]
fn test_generate_sphere_cluster() {
let radius = 1.0;
let centre = [0.0, 0.0, 0.0];
let resolution = 10;
let nodes = generate_sphere_cluster(radius, centre, resolution).unwrap();
let bounding_box_nodes = (resolution + 1).pow(3);
assert!(!nodes.is_empty());
assert!(nodes.len() < bounding_box_nodes);
for node in nodes {
let dist_sq = node[0].powi(2) + node[1].powi(2) + node[2].powi(2);
assert!(dist_sq <= radius.powi(2) + EPSILON);
}
let result = generate_sphere_cluster(-1.0, [0.0; 3], 10);
assert!(result.is_err());
assert!(matches!(result, Err(Error::MeshGeneration(_))));
let result = generate_sphere_cluster(1.0, [0.0; 3], 0);
assert!(result.is_err());
assert!(matches!(result, Err(Error::MeshGeneration(_))));
}
}