#![allow(dead_code)]
#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SymmetryAxis {
X,
Y,
Z,
}
#[allow(dead_code)]
#[derive(Debug, Clone, PartialEq)]
pub struct BodySymmetryV2Params {
pub symmetry_weight: f32,
pub axis: SymmetryAxis,
pub position_tolerance: f32,
pub positive_side_master: bool,
}
impl Default for BodySymmetryV2Params {
fn default() -> Self {
Self {
symmetry_weight: 1.0,
axis: SymmetryAxis::X,
position_tolerance: 0.001,
positive_side_master: true,
}
}
}
#[allow(dead_code)]
pub fn default_body_symmetry_v2_params() -> BodySymmetryV2Params {
BodySymmetryV2Params::default()
}
#[allow(dead_code)]
pub fn mirror_position(pos: [f32; 3], axis: SymmetryAxis) -> [f32; 3] {
match axis {
SymmetryAxis::X => [-pos[0], pos[1], pos[2]],
SymmetryAxis::Y => [pos[0], -pos[1], pos[2]],
SymmetryAxis::Z => [pos[0], pos[1], -pos[2]],
}
}
#[allow(dead_code)]
pub fn are_mirror_pair(a: [f32; 3], b: [f32; 3], axis: SymmetryAxis, tol: f32) -> bool {
let mirrored = mirror_position(a, axis);
let dx = (mirrored[0] - b[0]).abs();
let dy = (mirrored[1] - b[1]).abs();
let dz = (mirrored[2] - b[2]).abs();
dx < tol && dy < tol && dz < tol
}
#[allow(dead_code)]
pub fn symmetrize_position(
master: [f32; 3],
slave: [f32; 3],
axis: SymmetryAxis,
weight: f32,
) -> [f32; 3] {
let w = weight.clamp(0.0, 1.0);
let target = mirror_position(master, axis);
[
slave[0] + (target[0] - slave[0]) * w,
slave[1] + (target[1] - slave[1]) * w,
slave[2] + (target[2] - slave[2]) * w,
]
}
#[allow(dead_code)]
pub fn apply_symmetry(
positions: &mut [[f32; 3]],
pairs: &[(usize, usize)],
params: &BodySymmetryV2Params,
) {
for &(master, slave) in pairs {
if master < positions.len() && slave < positions.len() {
let m_pos = positions[master];
let s_pos = positions[slave];
let axis = params.axis;
let w = params.symmetry_weight;
if params.positive_side_master {
positions[slave] = symmetrize_position(m_pos, s_pos, axis, w);
} else {
positions[master] = symmetrize_position(s_pos, m_pos, axis, w);
}
}
}
}
#[allow(dead_code)]
pub fn set_symmetry_weight(params: &mut BodySymmetryV2Params, value: f32) {
params.symmetry_weight = value.clamp(0.0, 1.0);
}
#[allow(dead_code)]
pub fn reset_body_symmetry_v2(params: &mut BodySymmetryV2Params) {
*params = BodySymmetryV2Params::default();
}
#[allow(dead_code)]
pub fn body_symmetry_v2_to_json(params: &BodySymmetryV2Params) -> String {
let axis = match params.axis {
SymmetryAxis::X => "X",
SymmetryAxis::Y => "Y",
SymmetryAxis::Z => "Z",
};
format!(
r#"{{"symmetry_weight":{:.4},"axis":"{}","tolerance":{:.6},"positive_master":{}}}"#,
params.symmetry_weight, axis, params.position_tolerance, params.positive_side_master
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default() {
let p = BodySymmetryV2Params::default();
assert!((p.symmetry_weight - 1.0).abs() < 1e-6);
}
#[test]
fn test_mirror_x() {
let m = mirror_position([1.0, 2.0, 3.0], SymmetryAxis::X);
assert!((m[0] + 1.0).abs() < 1e-6);
assert!((m[1] - 2.0).abs() < 1e-6);
}
#[test]
fn test_mirror_y() {
let m = mirror_position([1.0, 2.0, 3.0], SymmetryAxis::Y);
assert!((m[1] + 2.0).abs() < 1e-6);
}
#[test]
fn test_mirror_z() {
let m = mirror_position([1.0, 2.0, 3.0], SymmetryAxis::Z);
assert!((m[2] + 3.0).abs() < 1e-6);
}
#[test]
fn test_are_mirror_pair_true() {
let a = [1.0f32, 0.0, 0.0];
let b = [-1.0f32, 0.0, 0.0];
assert!(are_mirror_pair(a, b, SymmetryAxis::X, 0.001));
}
#[test]
fn test_are_mirror_pair_false() {
let a = [1.0f32, 0.0, 0.0];
let b = [1.0f32, 0.0, 0.0];
assert!(!are_mirror_pair(a, b, SymmetryAxis::X, 0.001));
}
#[test]
fn test_symmetrize_full() {
let master = [1.0f32, 0.5, 0.5];
let slave = [0.0f32, 0.5, 0.5];
let result = symmetrize_position(master, slave, SymmetryAxis::X, 1.0);
assert!((result[0] + 1.0).abs() < 1e-6);
}
#[test]
fn test_apply_symmetry() {
let mut positions = [[1.0f32, 0.0, 0.0], [0.5f32, 0.0, 0.0]];
let params = BodySymmetryV2Params::default();
let pairs = vec![(0usize, 1usize)];
apply_symmetry(&mut positions, &pairs, ¶ms);
assert!((positions[1][0] + 1.0).abs() < 1e-6);
}
#[test]
fn test_set_weight_clamp() {
let mut p = BodySymmetryV2Params::default();
set_symmetry_weight(&mut p, 5.0);
assert!((p.symmetry_weight - 1.0).abs() < 1e-6);
}
#[test]
fn test_to_json() {
let j = body_symmetry_v2_to_json(&BodySymmetryV2Params::default());
assert!(j.contains("symmetry_weight"));
assert!(j.contains("axis"));
}
}