#![cfg_attr(feature = "cargo-clippy", allow(clippy::derive_hash_xor_eq))]
extern crate gif;
extern crate image;
extern crate pythagoras;
extern crate rand;
pub mod data;
pub mod group;
pub mod node;
pub mod coordinate;
pub mod map;
pub mod shape;
pub mod tools;
#[cfg(test)]
mod tests;
mod consts {
pub const MAX_LINKS: usize = 5;
pub const NETWORK_REM: usize = 666;
pub const DEFAULT_SIZE: u16 = 4;
pub const DEFAULT_SHADE: u16 = 20;
pub const DEFAULT_LINK_SIZE: u16 = 2;
pub const DEFAULT_RGBA: image::Rgba<u8> = image::Rgba {
data: [0, 0, 0, 255],
};
}
#[derive(Debug, Eq, Copy, Clone, Default)]
pub struct Coordinate {
pub x: i16,
pub y: i16,
}
#[derive(Copy, PartialEq, Eq, Clone, Debug, Default)]
pub struct HL {
pub style: u8,
pub f: u64,
pub t: u64,
pub from: Option<Coordinate>,
pub to: Option<Coordinate>,
}
#[derive(Copy, Clone, Debug)]
pub struct Node {
pub hash: u64,
pub geo: Coordinate,
pub color: image::Rgba<u8>,
pub radius: Option<u32>,
links: [HL; consts::MAX_LINKS],
}
#[derive(Clone, Debug)]
pub struct Group {
settings: Node,
pub nodes: Vec<Node>,
}
#[derive(Clone, Debug, Default)]
pub struct Map {
image: Option<IW>,
add: Coordinate,
}
#[derive(Clone, Copy)]
pub struct Network<T: Draw + Hash + std::marker::Copy> {
pub hash_map: [Option<T>; consts::NETWORK_REM],
}
pub trait Shape {
fn new() -> Self;
fn area(&self, size: u32) -> Vec<Coordinate>;
}
pub trait Hash {
fn hash(&self) -> u64;
}
impl Hash for HL {
fn hash(&self) -> u64 { self.t }
}
impl Hash for Node {
fn hash(&self) -> u64 { self.hash }
}
impl Hash for Group {
fn hash(&self) -> u64 { self.settings.hash() }
}
impl std::fmt::Display for Coordinate {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}, {}", self.x, self.y)
}
}
impl std::fmt::Display for HL {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{} -> {}", self.f, self.t)
}
}
#[derive(Clone, Debug)]
pub struct IW {
img: image::ImageBuffer<image::Rgba<u8>, Vec<u8>>,
}
impl IW {
pub fn image(&self) -> &image::ImageBuffer<image::Rgba<u8>, Vec<u8>> { &self.img }
pub fn put<L: Location>(&mut self, l: &L, color: image::Rgba<u8>) {
if cfg!(debug_assertions)
&& (l.x() as u32 > self.img.width() || l.y() as u32 >= self.img.height())
{
panic!(
"({}) out of bound of image ({}, {})",
l.position(),
self.img.width(),
self.img.height()
);
}
self.put_unsafe(l, color);
}
pub fn put_unsafe<L: Location>(&mut self, l: &L, color: image::Rgba<u8>) {
self.img.put_pixel(l.x() as u32, l.y() as u32, color);
}
pub fn dimensions(&self) -> Coordinate {
Coordinate::new(self.img.width() as i16, self.img.height() as i16)
}
}
pub trait Find: Hash + Location {
fn find<H: Hash>(&self, hash: H) -> Option<Coordinate> {
if self.hash() == hash.hash() {
return Some(self.position());
}
None
}
}
impl Find for HL {}
impl Find for Node {}
impl Find for Group {
fn find<H: Hash>(&self, hash: H) -> Option<Coordinate> {
let f = tools::find(hash.hash(), &self.nodes);
f.and_then(|x| Some(x.position()))
}
}
pub trait MinMax {
fn min_max(&self) -> (Coordinate, Coordinate);
}
impl MinMax for HL {
fn min_max(&self) -> (Coordinate, Coordinate) {
let zero = Coordinate::new(0, 0);
let to = self.to.unwrap_or(zero);
(self.position(), to)
}
}
impl MinMax for Node {
fn min_max(&self) -> (Coordinate, Coordinate) {
let mut max = Coordinate::new(
consts::DEFAULT_SIZE as i16 / 2,
consts::DEFAULT_SIZE as i16 / 2,
);
let mut min = self.position();
min -= max;
max += self.geo;
(min, max)
}
}
impl MinMax for Group {
fn min_max(&self) -> (Coordinate, Coordinate) { group::parameters(self) }
}
pub trait Location {
fn position(&self) -> Coordinate;
fn eq<L: Location>(&self, other: &L) -> bool { self.position() == other.position() }
fn x(&self) -> i16 { self.position().x }
fn y(&self) -> i16 { self.position().y }
fn sum(&self) -> i16 { self.x() + self.y() }
}
impl Location for HL {
fn position(&self) -> Coordinate {
let zero = Coordinate::new(0, 0);
self.from.unwrap_or(zero)
}
}
impl Location for Node {
fn position(&self) -> Coordinate { self.geo.position() }
}
impl Location for Group {
fn position(&self) -> Coordinate { self.settings.position() }
}
impl Location for Coordinate {
fn position(&self) -> Coordinate { *self }
}
pub trait Draw {
fn draw<S: Shape>(&self, image: IW, offset: Coordinate, shape: &S) -> IW;
fn size(&self) -> u32;
fn links(&self) -> &[HL];
}
impl Draw for Node {
fn draw<S: Shape>(&self, mut image: IW, offset: Coordinate, shape: &S) -> IW {
let s = consts::DEFAULT_LINK_SIZE / 2;
let pos = self.geo + offset - Coordinate::new(s as i16, s as i16);
for link in &self.links {
image = link.draw(image, offset, u32::from(consts::DEFAULT_LINK_SIZE));
}
for o in shape.area(self.size()) {
let color = if o.x == 0 || o.y == 0 {
let c = self
.color
.data
.iter()
.map(|x| x.saturating_add(consts::DEFAULT_SHADE as u8))
.collect::<Vec<_>>();
image::Rgba([c[0], c[1], c[2], c[3]])
} else {
self.color
};
let c = pos + o;
image.put_unsafe(&c, color);
}
image
}
fn size(&self) -> u32 {
self.radius
.unwrap_or_else(|| u32::from(consts::DEFAULT_SIZE))
}
fn links(&self) -> &[HL] { &self.links }
}
impl Draw for Group {
fn draw<S: Shape>(&self, mut image: IW, mut offset: Coordinate, shape: &S) -> IW {
offset += self.position();
for node in &self.nodes {
image = node.draw(image, offset, shape);
}
image
}
fn size(&self) -> u32 {
let mut max = 0;
for node in &self.nodes {
max = std::cmp::max(max, node.size());
}
max + self
.settings
.radius
.unwrap_or_else(|| u32::from(consts::DEFAULT_SIZE))
}
fn links(&self) -> &[HL] { &self.settings.links() }
}
impl From<Coordinate> for Node {
fn from(c: Coordinate) -> Self {
let mut node = Node::new("", c);
node.hash = (c.x + c.y) as u64;
node
}
}
impl From<Group> for Node {
fn from(group: Group) -> Self { group.settings }
}
impl From<Coordinate> for Group {
fn from(c: Coordinate) -> Self {
let mut group = Group::new_simple(c.x, c.y);
group.set().hash = (c.x + c.y) as u64;
group
}
}
impl From<Node> for Group {
fn from(node: Node) -> Self {
let mut group = Group::new_simple(node.x(), node.y());
group.settings = node;
group
}
}
impl From<Node> for Coordinate {
fn from(node: Node) -> Self { node.position() }
}
impl From<Group> for Coordinate {
fn from(group: Group) -> Self { group.position() }
}
impl std::ops::Add for Coordinate {
type Output = Coordinate;
fn add(self, other: Coordinate) -> Coordinate {
Coordinate::new(self.x + other.x, self.y + other.y)
}
}
impl std::ops::Sub for Coordinate {
type Output = Coordinate;
fn sub(self, other: Coordinate) -> Coordinate {
Coordinate::new(self.x - other.x, self.y - other.y)
}
}
impl std::ops::AddAssign for Coordinate {
fn add_assign(&mut self, other: Coordinate) {
self.x += other.x;
self.y += other.y;
}
}
impl std::ops::SubAssign for Coordinate {
fn sub_assign(&mut self, other: Coordinate) {
self.x -= other.x;
self.y -= other.y;
}
}
impl Coordinate {
pub fn new(x: i16, y: i16) -> Self { Coordinate { x, y } }
pub fn lt(self, lt: i16) -> bool { self.x < lt || self.y < lt }
pub fn abs(self) -> Coordinate { Coordinate::new(self.x.abs(), self.y.abs()) }
pub fn from_list(list: &[(i16, i16)]) -> Vec<Coordinate> {
coordinate::from_list(&list, &|c, _i| c)
}
}
impl Node {
pub fn new(name: &str, geo: Coordinate) -> Self {
Node {
hash: data::calculate_hash(&name),
geo,
color: consts::DEFAULT_RGBA,
radius: None,
links: [HL::new(0, 0); consts::MAX_LINKS],
}
}
pub fn from_file(path: &str) -> Result<Vec<Self>, std::io::Error> { node::from_file(path) }
pub fn center(&self) -> Coordinate {
let half = Coordinate::new(self.size() as i16 / 2, self.size() as i16 / 2);
self.position() + half
}
pub fn from_list(list: &[(i16, i16)]) -> Vec<Self> {
coordinate::from_list(&list, &|c, i| {
Node::new(&std::char::from_u32(65 + i as u32).unwrap().to_string(), c)
})
}
pub fn is_directly_connected(&self, other: &Node) -> bool {
tools::find(other.hash, self.links()).is_some()
}
pub fn linked_list(list: Vec<Node>) -> Vec<Self> {
Node::linked_list_predicate(list, &|_, _| true)
}
pub fn hl(&self, index: usize) -> std::io::Result<&HL> {
if index > self.get_link_avail_index() || !self.links[index].is_connected() {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
"index too large",
))
} else {
Ok(&self.links[index])
}
}
pub fn hl_mut(&mut self, index: usize) -> std::io::Result<&mut HL> {
if index > self.get_link_avail_index() || !self.links[index].is_connected() {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
"index too large",
))
} else {
Ok(&mut self.links[index])
}
}
pub fn linked_list_predicate(
mut list: Vec<Node>,
f: &Fn(Coordinate, Coordinate) -> bool,
) -> Vec<Self> {
let mut prev = Coordinate::new(0, 0);
let mut prev_h = 0;
for node in &mut list {
if prev_h != 0 && f(prev, node.geo) {
let mut link = HL::new(node.hash, prev_h);
link.to = Some(prev);
link.from = Some(node.geo);
let i = node.get_link_avail_index();
node.links[i] = link;
}
prev_h = node.hash();
prev = node.geo;
}
list
}
pub fn get_link_avail_index(&self) -> usize {
self.links()
.iter()
.position(|x| !x.is_connected())
.or(Some(consts::MAX_LINKS - 1))
.unwrap()
}
pub fn disconnect(&mut self) { self.links = [HL::new(0, 0); consts::MAX_LINKS]; }
pub fn link<P: Hash + Location>(&mut self, other: &P) {
let i = self.get_link_avail_index();
self.links[i] = HL {
style: 0,
f: self.hash,
t: other.hash(),
from: Some(self.geo),
to: Some(other.position()),
};
}
}
impl HL {
pub fn new(f: u64, t: u64) -> Self {
HL {
style: 0,
f,
t,
from: None,
to: None,
}
}
pub fn style(&mut self, s: u8) { self.style = s; }
pub fn is_connected(&self) -> bool { self.f != 0 && self.t != 0 }
fn draw(&self, mut image: IW, mut offset: Coordinate, size: u32) -> IW {
let (mut from, mut to) = self.min_max();
if !self.is_connected() || from == to {
return image;
}
let s = (size / 2) as i16;
offset += Coordinate::new(s, s);
from += offset;
to += offset;
for i in 0..size {
for j in 0..size {
let add = Coordinate::new(j as i16 - s, i as i16 - s);
let col = (size - i) as u8 * consts::DEFAULT_SHADE as u8;
let plot = match self.style {
0 => tools::plot_type(from + add, to + add, &tools::plot_bresenham),
1 => tools::plot_type(from + add, to + add, &tools::plot_rectangle),
2 => tools::plot_type(from + add, to + add, &tools::plot_ellipse),
_ => tools::plot(from + add, to + add),
};
let _ = plot
.iter()
.map(|c| image.put_unsafe(c, image::Rgba([col, col, col, u8::max_value()])))
.collect::<Vec<_>>();
}
}
image
}
}
impl Group {
pub fn new(name: &str, coordinates: Coordinate) -> Self {
Group {
settings: Node::new(name, coordinates),
nodes: Vec::new(),
}
}
pub fn new_node(&mut self) { group::add_node(self, None, None, None); }
pub fn radius(&mut self, radius: u32) { self.settings.radius = Some(radius); }
pub fn nodes(&self) -> &Vec<Node> { &self.nodes }
pub fn set(&mut self) -> &mut Node { &mut self.settings }
pub fn add(&mut self, nr: u32) {
for _ in 0..nr {
let co = coordinate::gen_within_radius(self.position(), self.size());
let mut node = Node::new("", co);
node.color = self.gen_color(co);
self.push(node);
}
}
pub fn each(&mut self, func: &Fn(&mut Node)) {
for node in self.nodes.iter_mut() {
func(node);
}
}
pub fn color(&mut self, rgba: image::Rgba<u8>) { self.settings.color = rgba; }
pub fn node_plot(&mut self, calc: &Fn(usize) -> Coordinate) {
let c = coordinate::calc(self.position(), self.nodes.len(), calc);
let color = self.gen_color(c);
let mut node = Node::new("", c);
node.color = color;
self.push(node);
}
pub fn new_node_min_max(&mut self, min: u32, max: u32) {
group::add_node(self, None, Some(min), Some(max));
}
pub fn new_simple(x: i16, y: i16) -> Self {
Group::new(&(x + y).to_string(), Coordinate::new(x, y))
}
pub fn push(&mut self, mut node: Node) {
node.geo -= self.position();
self.nodes.push(node);
}
pub fn dynamic_radius(&self) -> u32 {
match self.settings.radius {
Some(x) => x,
None => u32::from(consts::DEFAULT_SIZE) + self.nodes.len() as u32 / 2,
}
}
pub fn rotate(&mut self, rad: f64) {
coordinate::rotate_around_axis(Coordinate::new(0, 0), &mut self.nodes, rad);
}
pub fn gen_color(&self, coordinates: Coordinate) -> image::Rgba<u8> {
tools::range_color(
self.dynamic_radius() as i16,
self.settings.color,
self.settings.geo,
coordinates,
)
}
pub fn from_list(list: &[(i16, i16)]) -> Vec<Self> {
coordinate::from_list(&list, &|c, i| {
Group::new(&std::char::from_u32(65 + i as u32).unwrap().to_string(), c)
})
}
pub fn link(&mut self, other: &Group) { self.settings.link(&other.settings); }
}
impl<T: Draw + Hash + std::marker::Copy> Network<T> {
pub fn new(mut elements: Vec<T>) -> Self {
let mut hash_map: [Option<T>; consts::NETWORK_REM] = [None; consts::NETWORK_REM];
while !elements.is_empty() {
let e = elements.remove(0);
hash_map[(e.hash() as usize % consts::NETWORK_REM)] = Some(e);
}
Network { hash_map }
}
}
impl Map {
pub fn new() -> Self {
Map {
image: None,
add: Coordinate::new(0, 0),
}
}
pub fn save(self, path: &std::path::Path) -> Result<(), std::io::Error> {
self.image.unwrap().image().save(path)
}
pub fn consume(self) -> IW { self.image.unwrap() }
pub fn map<T: Draw + Location + Hash + MinMax>(self, element: &[T]) -> Self {
self.map_filter(&element, &|_| true)
}
pub fn map_filter<T: Draw + Location + Hash + MinMax>(
self,
element: &[T],
filter: &Fn(&T) -> bool,
) -> Self {
self.map_params(&element, &filter, &shape::Square::new())
}
pub fn map_shape<T: Draw + Location + Hash + MinMax, S: Shape>(
self,
element: &[T],
shape: &S,
) -> Self {
self.map_params(&element, &|_| true, shape)
}
pub fn map_absolute<T: Draw + Location + Hash + MinMax>(mut self, element: &[T]) -> Self {
if self.image.is_none() {
let (image, _) = map::gen_map(&element);
self.image = Some(IW { img: image });
}
self.map(element)
}
pub fn map_params<T: Draw + Location + Hash + MinMax, S: Shape>(
mut self,
element: &[T],
filter: &Fn(&T) -> bool,
shape: &S,
) -> Self {
if self.image.is_none() {
let (image, add) = map::gen_map(&element);
self.image = Some(IW { img: image });
self.add = add;
}
let add = self.add;
self.image = Some(
element
.iter()
.filter(|x| filter(x))
.fold(self.image.unwrap(), |img, x| x.draw(img, add, shape)),
);
self
}
}
impl Network<Node> {
pub fn path<'a>(&'a self, a: &str, b: &str) -> std::io::Result<Vec<Node>> {
let mut path = map::network::path(self, b, a, &map::network::path_shortest_leg)?;
path.reverse();
Ok(path)
}
pub fn path_rev<'a>(&'a self, a: &str, b: &str) -> std::io::Result<Vec<Node>> {
map::network::path(self, a, b, &map::network::path_shortest_leg)
}
pub fn get(&self, element: &str) -> Option<Node> { map::network::get(self, element) }
}