forceatlas2 0.4.0

fast force-directed generic n-dimension graph layout
Documentation
pub mod attraction;
pub mod gravity;
pub mod repulsion;

use crate::{
	layout::{Layout, Settings},
	util::*,
};

#[doc(hidden)]
pub trait Attraction<T: Coord + std::fmt::Debug> {
	fn choose_attraction(settings: &Settings<T>) -> fn(&mut Layout<T>);
}

#[doc(hidden)]
pub trait Repulsion<T: Coord + std::fmt::Debug> {
	fn choose_repulsion(settings: &Settings<T>) -> fn(&mut Layout<T>);
}

default impl<T> Attraction<T> for Layout<T>
where
	T: Coord + std::fmt::Debug,
{
	#[allow(clippy::collapsible_else_if)]
	fn choose_attraction(settings: &Settings<T>) -> fn(&mut Layout<T>) {
		if settings.prevent_overlapping.is_some() {
			if settings.lin_log {
				if settings.dissuade_hubs {
					attraction::apply_attraction_dh_log_po
				} else {
					attraction::apply_attraction_log_po
				}
			} else {
				if settings.dissuade_hubs {
					attraction::apply_attraction_dh_po
				} else {
					attraction::apply_attraction_po
				}
			}
		} else {
			if settings.lin_log {
				if settings.dissuade_hubs {
					attraction::apply_attraction_dh_log
				} else {
					attraction::apply_attraction_log
				}
			} else {
				if settings.dissuade_hubs {
					attraction::apply_attraction_dh
				} else {
					attraction::apply_attraction
				}
			}
		}
	}
}

impl<T> Attraction<T> for Layout<T>
where
	T: Copy + Coord + std::fmt::Debug,
{
	#[allow(clippy::collapsible_else_if)]
	fn choose_attraction(settings: &Settings<T>) -> fn(&mut Layout<T>) {
		if settings.prevent_overlapping.is_some() {
			if settings.lin_log {
				if settings.dissuade_hubs {
					attraction::apply_attraction_dh_log_po
				} else {
					attraction::apply_attraction_log_po
				}
			} else {
				if settings.dissuade_hubs {
					attraction::apply_attraction_dh_po
				} else {
					attraction::apply_attraction_po
				}
			}
		} else {
			if settings.lin_log {
				if settings.dissuade_hubs {
					attraction::apply_attraction_dh_log
				} else {
					attraction::apply_attraction_log
				}
			} else {
				if settings.dissuade_hubs {
					attraction::apply_attraction_dh
				} else {
					match settings.dimensions {
						2 => attraction::apply_attraction_2d,
						3 => attraction::apply_attraction_3d,
						_ => attraction::apply_attraction,
					}
				}
			}
		}
	}
}

pub fn choose_gravity<T: Coord + std::fmt::Debug>(settings: &Settings<T>) -> fn(&mut Layout<T>) {
	if settings.kg.is_zero() {
		return |_| {};
	}
	if settings.strong_gravity {
		gravity::apply_gravity_sg
	} else {
		gravity::apply_gravity
	}
}

default impl<T> Repulsion<T> for Layout<T>
where
	T: Coord + std::fmt::Debug,
{
	fn choose_repulsion(settings: &Settings<T>) -> fn(&mut Layout<T>) {
		#[cfg(feature = "barnes_hut")]
		if settings.barnes_hut.is_some() {
			unimplemented!("Barnes-Hut only implemented for f64")
		}
		if settings.prevent_overlapping.is_some() {
			repulsion::apply_repulsion_po
		} else {
			repulsion::apply_repulsion
		}
	}
}

default impl<T: Send + Sync> Repulsion<T> for Layout<T>
where
	T: Coord + std::fmt::Debug,
{
	fn choose_repulsion(settings: &Settings<T>) -> fn(&mut Layout<T>) {
		#[cfg(feature = "barnes_hut")]
		if settings.barnes_hut.is_some() {
			unimplemented!("Barnes-Hut only implemented for f64")
		}
		if settings.prevent_overlapping.is_some() {
			repulsion::apply_repulsion_po
		} else {
			#[cfg(feature = "parallel")]
			if settings.chunk_size.is_some() {
				return repulsion::apply_repulsion_parallel;
			}
			repulsion::apply_repulsion
		}
	}
}

impl Repulsion<f64> for Layout<f64> {
	fn choose_repulsion(settings: &Settings<f64>) -> fn(&mut Layout<f64>) {
		#[cfg(feature = "barnes_hut")]
		if settings.barnes_hut.is_some() {
			return match settings.dimensions {
				2 => {
					if settings.prevent_overlapping.is_some() {
						repulsion::apply_repulsion_bh_2d_po
					} else {
						repulsion::apply_repulsion_bh_2d
					}
				}
				3 => {
					if settings.prevent_overlapping.is_some() {
						repulsion::apply_repulsion_bh_3d_po
					} else {
						repulsion::apply_repulsion_bh_3d
					}
				}
				_ => unimplemented!("Barnes-Hut only implemented for 2D and 3D"),
			};
		}
		if settings.prevent_overlapping.is_some() {
			repulsion::apply_repulsion_po
		} else {
			match settings.dimensions {
				2 => {
					#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
					{
						if is_x86_feature_detected!("avx2") {
							#[cfg(feature = "parallel")]
							if settings.chunk_size.is_some() {
								return repulsion::apply_repulsion_2d_simd_f64_parallel;
							}
							return repulsion::apply_repulsion_2d_simd_f64;
						}
					}
					#[cfg(feature = "parallel")]
					if settings.chunk_size.is_some() {
						return repulsion::apply_repulsion_2d_parallel;
					}
					repulsion::apply_repulsion_2d
				}
				3 => repulsion::apply_repulsion_3d,
				_ => {
					#[cfg(feature = "parallel")]
					if settings.chunk_size.is_some() {
						return repulsion::apply_repulsion_parallel;
					}
					repulsion::apply_repulsion
				}
			}
		}
	}
}

impl Repulsion<f32> for Layout<f32> {
	fn choose_repulsion(settings: &Settings<f32>) -> fn(&mut Layout<f32>) {
		if settings.prevent_overlapping.is_some() {
			repulsion::apply_repulsion_po
		} else {
			match settings.dimensions {
				2 => {
					#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
					{
						if is_x86_feature_detected!("avx2") {
							#[cfg(feature = "parallel")]
							if settings.chunk_size.is_some() {
								return repulsion::apply_repulsion_2d_simd_f32_parallel;
							}
							return repulsion::apply_repulsion_2d_simd_f32;
						}
					}
					#[cfg(feature = "parallel")]
					if settings.chunk_size.is_some() {
						return repulsion::apply_repulsion_2d_parallel;
					}
					repulsion::apply_repulsion_2d
				}
				3 => repulsion::apply_repulsion_3d,
				_ => {
					#[cfg(feature = "parallel")]
					if settings.chunk_size.is_some() {
						return repulsion::apply_repulsion_parallel;
					}
					repulsion::apply_repulsion
				}
			}
		}
	}
}