gamenite/src/lib.rs

194 lines
5.2 KiB
Rust

use serde::{ Serialize, Deserialize };
use std::collections::{ HashMap, HashSet };
#[derive(Serialize, Deserialize, Debug)]
#[serde(default)]
pub struct Config {
pub horizontal_wrapping: bool,
}
impl Default for Config {
fn default() -> Self {
Config {
horizontal_wrapping: false,
}
}
}
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
#[serde(default)]
pub struct Node {
pub x: f32,
pub y: f32,
pub name: String,
pub edges: HashSet<usize>,
pub labels: HashMap<String, String>,
}
#[derive(Serialize, Deserialize, Debug, Default)]
#[serde(default)]
pub struct Board {
pub labels: HashMap<String, HashSet<String>>,
pub nodes: HashMap<usize, Node>,
pub config: Config,
}
impl Board {
pub fn add_node(&mut self, x: f32, y: f32, name: String) {
self.nodes.insert(self.next_id(), Node {
x,
y,
name,
edges: HashSet::new(),
labels: HashMap::new(),
});
}
pub fn insert_node(&mut self, node: Node) -> usize {
let id = self.next_id();
self.nodes.insert(id, node);
id
}
pub fn remove_node(&mut self, id: usize) {
// We remove this node from the graph, then drop it from each
// other nodes edge.
self.nodes.remove(&id);
for node in self.nodes.values_mut() {
node.edges.remove(&id);
}
}
pub fn remove_label_key(&mut self, key: &str) {
self.labels.remove(key);
}
pub fn remove_label_value(&mut self, key: &str, value: &str) {
if let Some(l) = self.labels.get_mut(key) {
l.remove(value);
}
}
pub fn add_edge(&mut self, from: usize, to: usize) {
let node = self.nodes.get_mut(&from).expect("Could not find node");
node.edges.insert(to);
}
pub fn nearest_node(&self, x: f32, y: f32) -> Option<usize> {
let f = |n: &Node| dist_sq((n.x, n.y), (x, y));
let mut iter = self.nodes.iter();
if let Some((id, node)) = iter.next() {
let mut min = *id;
let mut min_val = f(node);
for (id, node) in iter {
let val = f(node);
if val < min_val {
min = *id;
min_val = val;
}
}
Some(min)
} else {
None
}
}
fn next_id(&self) -> usize {
for i in 0 .. {
if !self.nodes.contains_key(&i) {
return i;
}
}
unreachable!();
}
}
fn dist_sq((x0, y0): (f32, f32), (x1, y1): (f32, f32)) -> f32 {
(x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0)
}
pub fn should_wrap_horizontal(node1: &Node, node2: &Node) -> bool {
let mut xs = [node1.x, node2.x];
xs.sort_by(|a, b| a.partial_cmp(b).unwrap());
xs[0] + (1.0 - xs[1]) < xs[1] - xs[0]
}
use std::io;
use std::io::{ Write, Read, Cursor };
use std::fs::File;
use std::path::Path;
use image::io::Reader as ImageReader;
use image::{ DynamicImage };
pub fn write_board_to_file(board: &Board, image: Option<&DynamicImage>, path: &Path) -> io::Result<()> {
let file = File::create(path)?;
let mut ar = zip::ZipWriter::new(file);
let options = zip::write::FileOptions::default();
ar.start_file("graph.json", options)?;
ar.write_all(&serde_json::to_vec(board)?)?;
if let Some(image) = image {
ar.start_file("image.png", options)?;
let data = encode_png(image);
ar.write_all(&data)?;
ar.flush()?;
}
ar.finish()?;
Ok(())
}
pub fn read_board_from_file(path: &Path) -> io::Result<(Board, Option<DynamicImage>)> {
let file = File::open(path)?;
let mut ar = zip::ZipArchive::new(file)?;
let json_file = ar.by_name("graph.json")?;
let board = serde_json::from_reader(json_file)?;
let image = ar.by_name("image.png").ok();
if image.is_none() {
return Ok((board, None))
}
let mut image_file = image.unwrap();
let mut buf = Vec::new();
image_file.read_to_end(&mut buf)?;
let image = decode_png(&buf);
Ok((board, Some(image)))
}
pub fn encode_png(image: &DynamicImage) -> Vec<u8> {
let mut cursor = Cursor::new(Vec::new());
image.write_to(&mut cursor, image::ImageOutputFormat::Png).unwrap();
cursor.into_inner()
}
pub fn decode_png(buf: &[u8]) -> DynamicImage {
let cursor = Cursor::new(buf);
let mut reader = ImageReader::new(cursor);
reader.set_format(image::ImageFormat::Png);
reader.decode().unwrap()
}
fn lerp(v0: f32, v1: f32, t: f32) -> f32 {
v0 + t * (v1 - v0)
}
fn inv_lerp(v0: f32, v1: f32, a: f32) -> f32 {
(a - v0) / (v1 - v0)
}
pub trait CoordTransformer<I: Into<(f32, f32)> + From<(f32, f32)>> {
fn origin(&self) -> I;
fn extremes(&self) -> I;
fn xform(&self, pos: I) -> (f32, f32) {
let (sx, sy) = self.origin().into();
let (ex, ey) = self.extremes().into();
let (x, y) = pos.into();
(
inv_lerp(sx, ex, x),
inv_lerp(sy, ey, y),
)
}
fn inv_xform(&self, x: f32, y: f32) -> I {
let (sx, sy) = self.origin().into();
let (ex, ey) = self.extremes().into();
(lerp(sx, ex, x), lerp(sy, ey, y)).into()
}
}