#![cfg_attr(feature = "cargo-clippy", allow(clippy::derive_hash_xor_eq))]
extern crate gif;
extern crate image;
extern crate pythagoras;
extern crate rand;
#[macro_use]
extern crate log;
#[macro_use]
pub mod macros;
pub mod consts;
pub mod coordinate;
pub mod data;
pub mod group;
pub mod map;
pub mod node;
pub mod tools;
pub mod traits;
pub use traits::*;
#[cfg(test)]
mod tests;
#[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: EdgeStyle,
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],
}
#[derive(Copy, PartialEq, Eq, Clone, Debug)]
pub enum EdgeStyle {
Direct,
Ellipse,
Straight,
}
#[derive(Debug, Clone)]
pub enum Shape {
Circle,
Square,
Triangle,
}
impl Shape {
pub fn area(&self, area: usize) -> Vec<Coordinate> {
match *self {
Shape::Circle => Shape::circle_area(area),
Shape::Square => Shape::square_area(area),
Shape::Triangle => Shape::triangle_area(area),
}
}
fn circle_area(area: usize) -> Vec<Coordinate> {
let mut vec = Vec::new();
let mut pos = coordinate!((area - 1), 0);
let mut err: i16 = 1 - (area << 1) as i16;
let mut d = Coordinate::new(err, 1);
let q_plot = |x1, y1, x2, y2| tools::plot(coordinate!(x1, y1), coordinate!(x2, y2));
while pos.x >= pos.y {
for i in [1, -1].iter() {
vec.append(&mut q_plot(pos.x, i * pos.y, -pos.x, i * pos.y));
vec.append(&mut q_plot(i * pos.y, -pos.x, i * pos.y, pos.x));
}
if err <= 0 {
pos.y += 1;
d.y += 2;
err += d.y;
} else {
pos.x -= 1;
d.x += 2;
err += d.x;
}
}
vec
}
fn square_area(area: usize) -> Vec<Coordinate> {
(0..area).fold(vec![], |mut acc, x| {
for i in 0..area {
acc.push(coordinate!(x, i));
}
acc
})
}
fn triangle_area(area: usize) -> Vec<Coordinate> {
(0..area).fold(vec![], |mut acc, x| {
acc.append(&mut tools::plot(
coordinate!(area / 2, 0),
coordinate!(x, area),
));
acc
})
}
}
impl std::default::Default for EdgeStyle {
fn default() -> Self {
EdgeStyle::Direct
}
}
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>) {
self.img.put_pixel(l.x() as u32, l.y() as u32, color);
}
pub fn dimensions(&self) -> Coordinate {
coordinate!(self.img.width(), self.img.height())
}
}
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()))
}
}
impl MinMax for HL {
fn min_max(&self) -> (Coordinate, Coordinate) {
let zero = coordinate!();
let to = self.to.unwrap_or(zero);
(self.position(), to)
}
}
impl MinMax for Node {
fn min_max(&self) -> (Coordinate, Coordinate) {
let mut max = coordinate!(consts::DEFAULT_SIZE);
let mut min = self.position();
min -= max;
max += self.geo;
(min, max)
}
}
impl MinMax for Group {
fn min_max(&self) -> (Coordinate, Coordinate) {
let mut min = coordinate!(0, 0);
let mut max = coordinate!(0, 0);
for node in &self.nodes {
let (min2, max2) = node.min_max();
max.x = std::cmp::max(max.x, max2.x);
min.x = std::cmp::min(min.x, min2.x);
max.y = std::cmp::max(max.y, max2.y);
min.y = std::cmp::min(min.y, min2.y);
}
(min + self.position(), max + self.position())
}
}
impl Location for HL {
fn position(&self) -> Coordinate {
let zero = coordinate!();
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
}
}
impl Draw for Node {
fn draw(&self, mut image: IW, offset: Coordinate, shape: &Shape) -> IW {
let s = consts::DEFAULT_LINK_SIZE / 2;
let pos = self.geo + offset - coordinate!(s, s);
for link in &self.links {
image = link.draw(image, offset, u32::from(consts::DEFAULT_LINK_SIZE));
}
for o in shape.area(self.size() as usize) {
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
};
image.put(&(pos + o), 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(&self, image: IW, mut offset: Coordinate, shape: &Shape) -> IW {
offset += self.position();
self.nodes
.iter()
.fold(image, |acc, node| node.draw(acc, offset, shape))
}
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!(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 = cluster!(c);
group.set().hash = (c.x + c.y) as u64;
group
}
}
impl From<Node> for Group {
fn from(node: Node) -> Self {
let mut group = cluster!(node.position());
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 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!(self.size() / 2, self.size() / 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<P: Hash + Location>(&self, other: &P) -> 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,
format!(
"index {} too large or not connected. Index: {} expected. Connection status: \
{}",
index,
self.get_link_avail_index(),
self.links[index].is_connected()
),
))
} 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!();
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: EdgeStyle::default(),
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: EdgeStyle::default(),
f,
t,
from: None,
to: None,
}
}
pub fn style(&mut self, style: EdgeStyle) {
self.style = style;
}
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 = coordinate!(size / 2);
offset += s;
from += offset;
to += offset;
for i in 0..size {
for j in 0..size {
let add = coordinate!(j, i) - s - s;
let col = (size - i) as u8 * consts::DEFAULT_SHADE as u8;
let plot = match self.style {
EdgeStyle::Direct => {
tools::plot_type(from + add, to + add, &tools::plot_bresenham)
}
EdgeStyle::Straight => {
tools::plot_type(from + add, to + add, &tools::plot_rectangle)
}
EdgeStyle::Ellipse => {
tools::plot_type(from + add, to + add, &tools::plot_ellipse)
}
};
let _ = plot
.iter()
.map(|c| image.put(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!(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 all(&self, func: &Fn(&Node) -> bool) -> bool {
for node in self.nodes.iter() {
if !func(node) {
return false;
}
}
true
}
pub fn any(&self, func: &Fn(&Node) -> bool) -> bool {
for node in self.nodes.iter() {
if func(node) {
return true;
}
}
false
}
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!(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!(), &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!(),
}
}
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)
}
pub fn map_shape<T: Draw + Location + Hash + MinMax>(
self,
element: &[T],
shape: &Shape,
) -> 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>(
mut self,
element: &[T],
filter: &Fn(&T) -> bool,
shape: &Shape,
) -> 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)
}
}