#![warn(missing_docs)]
#![feature(vec_remove_item)]
extern crate float_cmp;
extern crate itertools;
extern crate nalgebra;
extern crate rand;
#[cfg(feature = "serde-1")]
extern crate serde;
pub mod errors;
#[cfg(feature = "serde-1")]
mod serialization;
pub mod shapes;
pub mod util;
use errors::SphericalCowError as Error;
use float_cmp::ApproxEqRatio;
use itertools::Itertools;
use nalgebra::core::{Matrix, Matrix3};
use nalgebra::Point3;
use rand::distributions::Distribution;
use rand::Rng;
use shapes::Sphere;
pub trait Container {
fn contains(&self, sphere: &Sphere) -> bool;
fn volume(&self) -> f32;
}
#[derive(Debug)]
pub struct PackedVolume<C> {
pub spheres: Vec<Sphere>,
pub container: C,
}
impl<C: Container> PackedVolume<C> {
pub fn new<D: Distribution<f64>>(
container: C,
mut size_distribution: &mut D,
) -> Result<PackedVolume<C>, Error> {
let spheres = pack_spheres::<C, D>(&container, &mut size_distribution)?;
Ok(PackedVolume::<C> { spheres, container })
}
pub fn from_vec(spheres: Vec<Sphere>, container: C) -> PackedVolume<C> {
PackedVolume::<C> { spheres, container }
}
pub fn volume_fraction(&self) -> f32 {
let vol_spheres: f32 = self.spheres.iter().map(|sphere| sphere.volume()).sum();
vol_spheres / self.container.volume()
}
pub fn void_ratio(&self) -> f32 {
let vol_spheres: f32 = self.spheres.iter().map(|sphere| sphere.volume()).sum();
let vol_total = self.container.volume();
(vol_total - vol_spheres) / vol_spheres
}
pub fn coordination_number(&self) -> f32 {
let num_particles = self.spheres.len() as f32;
let mut coordinations = 0;
for idx in 0..self.spheres.len() {
coordinations += self.sphere_contacts_count(idx);
}
coordinations as f32 / num_particles
}
pub fn fabric_tensor(&self) -> Matrix3<f32> {
let phi = |i: usize, j: usize| {
let mut sum_all = 0.;
for idx in 0..self.spheres.len() {
let center = self.spheres[idx].center.coords;
let p_c = self.sphere_contacts(idx);
let m_p = p_c.len() as f32;
let mut sum_vec = 0.;
for c in &p_c {
let vec_n_pc = Matrix::cross(¢er, &c.center.coords);
let n_pc = vec_n_pc / nalgebra::norm(&vec_n_pc);
sum_vec += n_pc[i] * n_pc[j];
}
sum_all += sum_vec / m_p;
}
1. / self.spheres.len() as f32 * sum_all
};
Matrix3::from_fn(phi)
}
fn sphere_contacts(&self, sphere_idx: usize) -> Vec<Sphere> {
let center = self.spheres[sphere_idx].center;
let radius = self.spheres[sphere_idx].radius;
self.spheres
.iter()
.cloned()
.filter(|sphere| {
nalgebra::distance(¢er, &sphere.center)
.approx_eq_ratio(&(radius + sphere.radius), 0.0001)
})
.collect()
}
fn sphere_contacts_count(&self, sphere_idx: usize) -> usize {
let center = self.spheres[sphere_idx].center;
let radius = self.spheres[sphere_idx].radius;
self.spheres
.iter()
.filter(|sphere| {
nalgebra::distance(¢er, &sphere.center)
.approx_eq_ratio(&(radius + sphere.radius), 0.0001)
})
.count()
}
}
pub fn pack_spheres<C: Container, D: Distribution<f64>>(
container: &C,
size_distribution: &mut D,
) -> Result<Vec<Sphere>, Error> {
let mut rng = rand::thread_rng();
let init_radii: [f32; 3] = [
size_distribution.sample(&mut rng) as f32,
size_distribution.sample(&mut rng) as f32,
size_distribution.sample(&mut rng) as f32,
];
let mut spheres = init_spheres(&init_radii, container)?;
let mut front = spheres.clone();
let mut new_radius = size_distribution.sample(&mut rng) as f32;
let mut set_v = Vec::new();
let mut set_f = Vec::new();
'outer: while !front.is_empty() {
let curr_sphere = rng.choose(&front).ok_or(Error::NoneFront)?.clone();
set_v.clear();
set_v = spheres
.iter()
.cloned()
.filter(|s_dash| {
s_dash != &curr_sphere
&& nalgebra::distance(&curr_sphere.center, &s_dash.center)
<= curr_sphere.radius + s_dash.radius + 2. * new_radius
})
.collect::<Vec<_>>();
for (s_i, s_j) in set_v.iter().tuple_combinations::<(&Sphere, &Sphere)>() {
set_f.clear();
identify_f(
&mut set_f,
&curr_sphere,
s_i,
s_j,
container,
&set_v,
new_radius,
)?;
if !set_f.is_empty() {
let s_new = rng.choose(&set_f).ok_or(Error::NoneSetF)?;
front.push(s_new.clone());
spheres.push(s_new.clone());
new_radius = size_distribution.sample(&mut rng) as f32;
continue 'outer;
}
}
front.remove_item(&curr_sphere);
}
Ok(spheres)
}
fn init_spheres<C: Container>(radii: &[f32; 3], container: &C) -> Result<Vec<Sphere>, Error> {
let mut init = Vec::new();
let radius_a = radii[0];
let radius_b = radii[1];
let radius_c = radii[2];
let distance_c = radius_a + radius_b;
let distance_b = radius_a + radius_c;
let distance_a = radius_c + radius_b;
let b_2 = distance_b.powi(2);
let x = (b_2 + distance_c.powi(2) - distance_a.powi(2)) / (2. * distance_c);
let y = (b_2 - x.powi(2)).sqrt();
let perimeter = distance_a + distance_b + distance_c;
let incenter_x = (distance_b * distance_c + distance_c * x) / perimeter;
let incenter_y = (distance_c * y) / perimeter;
let s_1 = Sphere::new(Point3::new(-incenter_x, -incenter_y, 0.), radius_a)?;
if container.contains(&s_1) {
init.push(s_1);
} else {
return Err(Error::Uncontained);
}
let s_2 = Sphere::new(
Point3::new(distance_c - incenter_x, -incenter_y, 0.),
radius_b,
)?;
if container.contains(&s_2) {
init.push(s_2);
} else {
return Err(Error::Uncontained);
}
let s_3 = Sphere::new(Point3::new(x - incenter_x, y - incenter_y, 0.), radius_c)?;
if container.contains(&s_3) {
init.push(s_3);
} else {
return Err(Error::Uncontained);
}
Ok(init)
}
fn identify_f<C: Container>(
set_f: &mut Vec<Sphere>,
s_1: &Sphere,
s_2: &Sphere,
s_3: &Sphere,
container: &C,
set_v: &[Sphere],
radius: f32,
) -> Result<(), Error> {
let distance_14 = s_1.radius + radius;
let distance_24 = s_2.radius + radius;
let distance_34 = s_3.radius + radius;
let vector_u = s_1.center - s_2.center;
let unitvector_u = vector_u / nalgebra::norm(&vector_u);
let vector_v = s_1.center - s_3.center;
let unitvector_v = vector_v / nalgebra::norm(&vector_v);
let cross_uv = Matrix::cross(&vector_u, &vector_v);
let unitvector_t = cross_uv / nalgebra::norm(&cross_uv);
let vector_w = -2. * s_1.center.coords;
let distance_c =
distance_14.powi(2) - s_1.center.x.powi(2) - s_1.center.y.powi(2) - s_1.center.z.powi(2);
let distance_a = (distance_24.powi(2)
- distance_c
- s_2.center.x.powi(2)
- s_2.center.y.powi(2)
- s_2.center.z.powi(2)) / (2. * nalgebra::norm(&vector_u));
let distance_b = (distance_34.powi(2)
- distance_c
- s_3.center.x.powi(2)
- s_3.center.y.powi(2)
- s_3.center.z.powi(2)) / (2. * nalgebra::norm(&vector_v));
let dot_uv = nalgebra::dot(&unitvector_u, &unitvector_v);
let dot_wt = nalgebra::dot(&vector_w, &unitvector_t);
let dot_uw = nalgebra::dot(&unitvector_u, &vector_w);
let dot_vw = nalgebra::dot(&unitvector_v, &vector_w);
let denominator = 1. - dot_uv.powi(2);
let alpha = (distance_a - distance_b * dot_uv) / denominator;
let beta = (distance_b - distance_a * dot_uv) / denominator;
let value_d =
alpha.powi(2) + beta.powi(2) + 2. * alpha * beta * dot_uv + alpha * dot_uw + beta * dot_vw
- distance_c;
let dot_wt_2 = dot_wt.powi(2);
let value_4d = 4. * value_d;
if dot_wt_2 > value_4d {
let gamma_pos = 0.5 * (-dot_wt + (dot_wt_2 - value_4d).sqrt());
let gamma_neg = 0.5 * (-dot_wt - (dot_wt_2 - value_4d).sqrt());
let s_4_positive = Sphere::new(
Point3::from(
alpha * unitvector_u + beta * unitvector_v + gamma_pos * unitvector_t,
),
radius,
)?;
let s_4_negative = Sphere::new(
Point3::from(
alpha * unitvector_u + beta * unitvector_v + gamma_neg * unitvector_t,
),
radius,
)?;
if container.contains(&s_4_positive) && !set_v.iter().any(|v| v.overlaps(&s_4_positive)) {
set_f.push(s_4_positive);
}
if container.contains(&s_4_negative) && !set_v.iter().any(|v| v.overlaps(&s_4_negative)) {
set_f.push(s_4_negative);
}
}
Ok(())
}
#[test]
fn init_spheres_err() {
let container = Sphere::new(Point3::origin(), 0.1).unwrap();
assert!(init_spheres(&[10., 15., 20.], &container).is_err());
}
#[test]
fn identify_f_known() {
let one = Sphere::new(Point3::new(0.5, -0.28112677, 0.0), 0.5).unwrap();
let two = Sphere::new(Point3::new(0.058333218, 0.44511732, 0.0), 0.35).unwrap();
let three = Sphere::new(Point3::new(-0.70000005, -0.28112677, 0.0), 0.7).unwrap();
let container = Sphere::new(Point3::origin(), 20.0).unwrap();
let four_p = Sphere::new(Point3::new(0.06666666, 0.12316025, 0.6773287), 0.4).unwrap();
let four_n = Sphere::new(Point3::new(0.06666666, 0.12316025, -0.6773287), 0.4).unwrap();
let mut found = Vec::new();
identify_f::<Sphere>(&mut found, &one, &two, &three, &container, &Vec::new(), 0.4).unwrap();
println!("{:?}", found);
assert!(found.contains(&four_p));
assert!(found.contains(&four_n));
}