extern crate acacia;
extern crate nalgebra;
extern crate rand;
use acacia::partition::Ncube;
use acacia::{AssociatedData, DataQuery, Node, Position, Tree};
use nalgebra::{distance, zero, Point3, Vector3};
use rand::distributions::{Distribution, Uniform};
use rand::thread_rng;
struct PointMass {
mass: f64,
position: Point3<f64>,
}
impl Position for PointMass {
type Point = Point3<f64>;
fn position(&self) -> Point3<f64> {
self.position
}
}
fn newton(m: f64, p1: Point3<f64>, p2: Point3<f64>) -> Vector3<f64> {
let diff: Vector3<f64> = p1 - p2;
let r = diff.norm();
diff * m / r.powi(3)
}
fn main() {
let origin = Point3::origin();
let mut rng = thread_rng();
let coord_range = Uniform::from(-5.0..5.0);
let particles: Vec<_> = (0..1000)
.map(|_| PointMass {
mass: 1.0,
position: Point3::new(
coord_range.sample(&mut rng),
coord_range.sample(&mut rng),
coord_range.sample(&mut rng),
),
})
.collect();
let tree = Tree::new(
particles.iter(),
Ncube::new(origin, 11.0),
(origin, 0.0),
&|obj| (obj.position, obj.mass),
&|&(com1, m1), &(com2, m2)| {
if m1 + m2 > 0.0 {
(com1 + (com2 - com1) * (m2 / (m1 + m2)), m1 + m2)
} else {
(origin, 0.0)
}
},
)
.expect("Couldn't construct tree");
let test_point = Point3::new(2.3, -4.1, 1.6);
let theta = 0.5; let tree_gravity: Vector3<f64> =
tree.query_data(|node| {
let &(ref center_of_mass, _) = node.data();
let d = distance(&test_point, center_of_mass);
let delta = distance(&node.partition().center(), center_of_mass);
d < node.partition().width() / theta + delta
})
.map(|&(center_of_mass, mass)| newton(mass, center_of_mass, test_point))
.fold(zero(), |a, b| a + b);
let exact_gravity: Vector3<f64> = particles
.iter()
.map(|particle| newton(particle.mass, particle.position, test_point))
.fold(zero(), |a, b| a + b);
println!(
"Tree-computed gravity at {:?} is {:?} (exact: {:?})",
test_point, tree_gravity, exact_gravity
);
}