diff --git a/board.zip b/board.zip index 18dead6..342d503 100644 Binary files a/board.zip and b/board.zip differ diff --git a/src/board.rs b/src/board.rs index 76f1830..74ca986 100644 --- a/src/board.rs +++ b/src/board.rs @@ -1,133 +1,171 @@ -use serde::{ Serialize, Deserialize }; -use std::collections::{ HashMap, HashSet }; - -#[derive(Serialize, Deserialize, Debug, Default)] -#[serde(default)] -pub struct Node { - pub x: f32, - pub y: f32, - - pub name: String, - pub edges: HashSet, - pub labels: HashMap, -} -#[derive(Serialize, Deserialize, Debug, Default)] -#[serde(default)] -pub struct Board { - pub labels: HashMap>, - pub nodes: HashMap, -} -impl Board { - pub fn new() -> Self { - let nodes = HashMap::new(); - let labels = HashMap::new(); - Board { nodes, labels } - } - - 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 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 nearest_node(&self, x: f32, y: f32) -> Option { - 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) -} - -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)> { - 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 { - 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() -} - +use serde::{ Serialize, Deserialize }; +use std::collections::{ HashMap, HashSet }; + +#[derive(Serialize, Deserialize, Debug, Default)] +#[serde(default)] +pub struct Node { + pub x: f32, + pub y: f32, + + pub name: String, + pub edges: HashSet, + pub labels: HashMap, +} +#[derive(Serialize, Deserialize, Debug, Default)] +#[serde(default)] +pub struct Board { + pub labels: HashMap>, + pub nodes: HashMap, +} +impl Board { + pub fn new() -> Self { + let nodes = HashMap::new(); + let labels = HashMap::new(); + Board { nodes, labels } + } + + 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 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 nearest_node(&self, x: f32, y: f32) -> Option { + 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) +} + +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)> { + 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 { + 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 { + fn origin(&self) -> (I, I); + fn extremes(&self) -> (I, I); + fn to_coords(&self, x: I, y: I) -> (f32, f32) { + let (sx, sy) = self.origin(); + let (ex, ey) = self.extremes(); + ( + inv_lerp(sx.to_coordtype(), ex.to_coordtype(), x.to_coordtype()), + inv_lerp(sy.to_coordtype(), ey.to_coordtype(), y.to_coordtype()), + ) + } + fn from_coords(&self, x: f32, y: f32) -> (I, I) { + let (sx, sy) = self.origin(); + let (ex, ey) = self.extremes(); + ( + CoordtypeConvertible::from_coordtype(lerp(sx.to_coordtype(), ex.to_coordtype(), x)), + CoordtypeConvertible::from_coordtype(lerp(sy.to_coordtype(), ey.to_coordtype(), y)), + ) + } +} + +pub trait CoordtypeConvertible { + fn to_coordtype(self) -> f32; + fn from_coordtype(coordtype: f32) -> Self; +} + +impl CoordtypeConvertible for i32 { + fn to_coordtype(self) -> f32 { self as f32 } + fn from_coordtype(coordtype: f32) -> i32 { coordtype as i32 } +} diff --git a/src/main.rs b/src/main.rs index 1de7d61..9b51d45 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,7 +13,7 @@ use std::sync::Mutex; mod board; use board::Board; -use board::{ encode_png, write_board_to_file, read_board_from_file }; +use board::{ encode_png, write_board_to_file, read_board_from_file, CoordTransformer }; //////////////////// Global State //////////////////// // Don't @ me... @@ -133,35 +133,9 @@ mod dispatch { } } -//////////////////// Utility Functions //////////////////// - -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) -} - -trait CoordTransformer { - fn to_coords(&self, x: i32, y: i32) -> (f32, f32); - fn from_coords(&self, x: f32, y: f32) -> (i32, i32); -} - -impl CoordTransformer for frame::Frame { - fn to_coords(&self, x: i32, y: i32) -> (f32, f32) { - ( - inv_lerp(self.x() as f32, (self.x() + self.w()) as f32, x as f32), - inv_lerp(self.y() as f32, (self.y() + self.h()) as f32, y as f32), - ) - } - - fn from_coords(&self, x: f32, y: f32) -> (i32, i32) { - ( - lerp(self.x() as f32, (self.x() + self.w()) as f32, x) as i32, - lerp(self.y() as f32, (self.y() + self.h()) as f32, y) as i32, - ) - } +impl CoordTransformer for frame::Frame { + fn origin(&self) -> (i32, i32) { (self.x(), self.y()) } + fn extremes(&self) -> (i32, i32) { (self.x()+self.w(), self.y()+self.h()) } } //////////////////// App State ////////////////////