mod cuboid;
mod cylinder;
mod sphere;
use coord::{Coord, Direction, Periodic};
use describe::Describe;
use system::Residue;
pub use self::cuboid::Cuboid;
pub use self::cylinder::Cylinder;
pub use self::sphere::Sphere;
pub trait Contains: Describe {
fn contains(&self, coord: Coord) -> bool;
}
pub trait Volume: Contains {
fn fill(self, fill_type: FillType) -> Self;
fn volume(&self) -> f64;
}
#[derive(Clone, Copy, Debug)]
pub enum FillType {
Density(f64),
NumCoords(u64),
}
impl FillType {
fn to_num_coords<T: Volume>(&self, volume: &T) -> u64 {
match *self {
FillType::Density(density) => (volume.volume() * density).round() as u64,
FillType::NumCoords(num) => num,
}
}
}
#[allow(dead_code)]
fn cut_to_cylinder(coords: &[Coord], bottom_center: Coord, alignment: Direction,
radius: f64, height: f64) -> Vec<Coord> {
coords.iter()
.filter(|&c| {
let (dr, dh) = bottom_center.distance_cylindrical(*c, alignment);
dr <= radius && dh >= 0.0 && dh <= height
})
.map(|&c| c - bottom_center)
.collect()
}
#[allow(dead_code)]
fn cut_to_sphere(coords: &[Coord], center: Coord, radius: f64) -> Vec<Coord> {
coords.iter()
.filter(|c| c.distance(center) <= radius)
.map(|&c| c - center)
.collect()
}
pub fn pbc_multiply_volume(coords: &[Coord], size: Coord, nx: usize, ny: usize, nz: usize)
-> Vec<Coord> {
let capacity = (nx * ny * nz) as usize * coords.len();
match capacity {
1 => {
let mut new_coords = Vec::new();
new_coords.extend_from_slice(coords);
new_coords
},
_ => {
let mut new_coords = Vec::with_capacity(capacity);
for i in 0..nx {
for j in 0..ny {
for k in 0..nz {
let pbc_add = size.pbc_multiply(i, j, k);
for &coord in coords {
new_coords.push(coord + pbc_add);
}
}
}
}
new_coords
},
}
}
pub fn prune_residues_from_volume<T: ?Sized>(coords: &[Coord],
origin: Coord,
residue: &Residue,
volume: &T)
-> Vec<Coord> where T: Contains {
coords.iter()
.filter(|&c0| {
residue.atoms
.iter()
.map(|ref atom| atom.position + *c0 + origin)
.all(|c1| !volume.contains(c1))
})
.cloned()
.collect::<Vec<_>>()
}
#[cfg(test)]
mod tests {
use super::*;
use system::Atom;
#[test]
fn fill_type_returns_correct_numbers() {
let size = Coord::new(1.0, 2.0, 3.0);
let cuboid = Cuboid {
size,
.. Cuboid::default()
};
let density = 15.6;
let expected_num_coords = (cuboid.volume() * density).round() as u64;
assert_eq!(FillType::Density(density).to_num_coords(&cuboid), expected_num_coords);
let num = 11;
assert_eq!(FillType::NumCoords(num).to_num_coords(&cuboid), num);
}
#[test]
fn coordinates_within_cuboid_are_pruned() {
let residue = resbase!["RES", ("A", 0.0, 0.0, 0.0)];
let cuboid = Cuboid {
size: Coord::new(1.0, 1.0, 1.0),
.. Cuboid::default()
};
let coords_within = vec![
Coord::new(0.1, 0.1, 0.1),
Coord::new(0.5, 0.5, 0.5),
Coord::new(0.9, 0.9, 0.9)
];
let coords_without = vec![
Coord::new(-0.1, 0.1, 0.1),
Coord::new(1.1, 0.9, 0.9)
];
let coords = coords_within
.iter()
.chain(coords_without.iter())
.cloned()
.collect::<Vec<Coord>>();
let pruned = prune_residues_from_volume(&coords, Coord::ORIGO, &residue, &cuboid);
assert_eq!(coords_without, pruned);
}
#[test]
fn coordinates_within_cuboid_prune_with_respect_to_residue_atoms() {
let residue = resbase![
"RES",
("A", 0.0, 0.0, 0.0),
("B", 1.0, 0.0, 0.0) ];
let cuboid = Cuboid {
size: Coord::new(1.0, 1.0, 1.0),
.. Cuboid::default()
};
let coords_within = vec![
Coord::new(-0.9, 0.1, 0.1), Coord::new(-0.5, 0.5, 0.5),
Coord::new(-0.1, 0.9, 0.9),
Coord::new(0.1, 0.9, 0.9), Coord::new(0.5, 0.9, 0.9),
Coord::new(0.9, 0.9, 0.9)
];
let coords_without = vec![
Coord::new(-1.1, 0.1, 0.1),
Coord::new(1.1, 0.9, 0.9)
];
let coords = coords_within
.iter()
.chain(coords_without.iter())
.cloned()
.collect::<Vec<Coord>>();
let pruned = prune_residues_from_volume(&coords, Coord::ORIGO, &residue, &cuboid);
assert_eq!(coords_without, pruned);
}
#[test]
fn pruning_accounts_for_the_relative_translation_of_objects() {
let cuboid = Cuboid {
origin: Coord::new(1.0, 0.0, 0.0),
size: Coord::new(2.0, 1.0, 1.0),
.. Cuboid::default()
};
let residue = resbase!["RES", ("A", 0.0, 0.0, 0.0)];
let origin = Coord::new(1.0, 0.0, 0.0);
let coords = vec![
Coord::new(0.5, 0.5, 0.5), Coord::new(1.5, 0.5, 0.5), Coord::new(2.5, 0.5, 0.5) ];
let pruned = prune_residues_from_volume(&coords, origin, &residue, &cuboid);
assert_eq!(pruned, vec![Coord::new(2.5, 0.5, 0.5)]);
}
}