use anyhow::{Result, anyhow, bail};
use clap::Args;
use bio_forge::Structure;
use bio_forge::ops::Transform;
use crate::commands::run_with_spinner;
#[derive(Debug, Args)]
pub struct TransformArgs {
#[arg(long)]
pub center: bool,
#[arg(long = "center-mass")]
pub center_mass: bool,
#[arg(long, value_name = "X,Y,Z")]
pub translate: Option<String>,
#[arg(long = "rotate-x", value_name = "DEG")]
pub rotate_x: Option<f64>,
#[arg(long = "rotate-y", value_name = "DEG")]
pub rotate_y: Option<f64>,
#[arg(long = "rotate-z", value_name = "DEG")]
pub rotate_z: Option<f64>,
}
pub fn run(structure: &mut Structure, args: &TransformArgs) -> Result<()> {
run_with_spinner("Applying transforms", || {
if args.center {
Transform::center_geometry(structure, None);
}
if args.center_mass {
Transform::center_mass(structure, None);
}
if let Some(deg) = args.rotate_x {
Transform::rotate_x(structure, deg.to_radians());
}
if let Some(deg) = args.rotate_y {
Transform::rotate_y(structure, deg.to_radians());
}
if let Some(deg) = args.rotate_z {
Transform::rotate_z(structure, deg.to_radians());
}
if let Some(vector) = &args.translate {
let (x, y, z) = parse_vector(vector)?;
Transform::translate(structure, x, y, z);
}
Ok(())
})
}
fn parse_vector(value: &str) -> Result<(f64, f64, f64)> {
let parts: Vec<_> = value
.split(',')
.map(|p| p.trim())
.filter(|p| !p.is_empty())
.collect();
if parts.len() != 3 {
bail!("Invalid translation '{}'. Expected format 'x,y,z'.", value);
}
let x = parts[0]
.parse::<f64>()
.map_err(|_| anyhow!("Invalid X component in translation: '{}'", parts[0]))?;
let y = parts[1]
.parse::<f64>()
.map_err(|_| anyhow!("Invalid Y component in translation: '{}'", parts[1]))?;
let z = parts[2]
.parse::<f64>()
.map_err(|_| anyhow!("Invalid Z component in translation: '{}'", parts[2]))?;
Ok((x, y, z))
}