Transformible coordinates are a board trait
This commit is contained in:
parent
69cf7baa45
commit
5165708a03
304
src/board.rs
304
src/board.rs
@ -1,133 +1,171 @@
|
|||||||
use serde::{ Serialize, Deserialize };
|
use serde::{ Serialize, Deserialize };
|
||||||
use std::collections::{ HashMap, HashSet };
|
use std::collections::{ HashMap, HashSet };
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Default)]
|
#[derive(Serialize, Deserialize, Debug, Default)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct Node {
|
pub struct Node {
|
||||||
pub x: f32,
|
pub x: f32,
|
||||||
pub y: f32,
|
pub y: f32,
|
||||||
|
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub edges: HashSet<usize>,
|
pub edges: HashSet<usize>,
|
||||||
pub labels: HashMap<String, String>,
|
pub labels: HashMap<String, String>,
|
||||||
}
|
}
|
||||||
#[derive(Serialize, Deserialize, Debug, Default)]
|
#[derive(Serialize, Deserialize, Debug, Default)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct Board {
|
pub struct Board {
|
||||||
pub labels: HashMap<String, Vec<String>>,
|
pub labels: HashMap<String, Vec<String>>,
|
||||||
pub nodes: HashMap<usize, Node>,
|
pub nodes: HashMap<usize, Node>,
|
||||||
}
|
}
|
||||||
impl Board {
|
impl Board {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let nodes = HashMap::new();
|
let nodes = HashMap::new();
|
||||||
let labels = HashMap::new();
|
let labels = HashMap::new();
|
||||||
Board { nodes, labels }
|
Board { nodes, labels }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_node(&mut self, x: f32, y: f32, name: String) {
|
pub fn add_node(&mut self, x: f32, y: f32, name: String) {
|
||||||
self.nodes.insert(self.next_id(), Node {
|
self.nodes.insert(self.next_id(), Node {
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
name,
|
name,
|
||||||
edges: HashSet::new(),
|
edges: HashSet::new(),
|
||||||
labels: HashMap::new(),
|
labels: HashMap::new(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_node(&mut self, id: usize) {
|
pub fn remove_node(&mut self, id: usize) {
|
||||||
// We remove this node from the graph, then drop it from each
|
// We remove this node from the graph, then drop it from each
|
||||||
// other nodes edge.
|
// other nodes edge.
|
||||||
self.nodes.remove(&id);
|
self.nodes.remove(&id);
|
||||||
for node in self.nodes.values_mut() {
|
for node in self.nodes.values_mut() {
|
||||||
node.edges.remove(&id);
|
node.edges.remove(&id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn nearest_node(&self, x: f32, y: f32) -> Option<usize> {
|
pub fn nearest_node(&self, x: f32, y: f32) -> Option<usize> {
|
||||||
let f = |n: &Node| dist_sq((n.x, n.y), (x, y));
|
let f = |n: &Node| dist_sq((n.x, n.y), (x, y));
|
||||||
let mut iter = self.nodes.iter();
|
let mut iter = self.nodes.iter();
|
||||||
if let Some((id, node)) = iter.next() {
|
if let Some((id, node)) = iter.next() {
|
||||||
let mut min = *id;
|
let mut min = *id;
|
||||||
let mut min_val = f(node);
|
let mut min_val = f(node);
|
||||||
for (id, node) in iter {
|
for (id, node) in iter {
|
||||||
let val = f(node);
|
let val = f(node);
|
||||||
if val < min_val {
|
if val < min_val {
|
||||||
min = *id;
|
min = *id;
|
||||||
min_val = val;
|
min_val = val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(min)
|
Some(min)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next_id(&self) -> usize {
|
fn next_id(&self) -> usize {
|
||||||
for i in 0 .. {
|
for i in 0 .. {
|
||||||
if !self.nodes.contains_key(&i) {
|
if !self.nodes.contains_key(&i) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
unreachable!();
|
unreachable!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dist_sq((x0, y0): (f32, f32), (x1, y1): (f32, f32)) -> f32 {
|
fn dist_sq((x0, y0): (f32, f32), (x1, y1): (f32, f32)) -> f32 {
|
||||||
(x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0)
|
(x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0)
|
||||||
}
|
}
|
||||||
|
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::io::{ Write, Read, Cursor };
|
use std::io::{ Write, Read, Cursor };
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use image::io::Reader as ImageReader;
|
use image::io::Reader as ImageReader;
|
||||||
use image::{ DynamicImage };
|
use image::{ DynamicImage };
|
||||||
|
|
||||||
pub fn write_board_to_file(board: &Board, image: Option<&DynamicImage>, path: &Path) -> io::Result<()> {
|
pub fn write_board_to_file(board: &Board, image: Option<&DynamicImage>, path: &Path) -> io::Result<()> {
|
||||||
let file = File::create(path)?;
|
let file = File::create(path)?;
|
||||||
let mut ar = zip::ZipWriter::new(file);
|
let mut ar = zip::ZipWriter::new(file);
|
||||||
let options = zip::write::FileOptions::default();
|
let options = zip::write::FileOptions::default();
|
||||||
|
|
||||||
ar.start_file("graph.json", options)?;
|
ar.start_file("graph.json", options)?;
|
||||||
ar.write_all(&serde_json::to_vec(board)?)?;
|
ar.write_all(&serde_json::to_vec(board)?)?;
|
||||||
if let Some(image) = image {
|
if let Some(image) = image {
|
||||||
ar.start_file("image.png", options)?;
|
ar.start_file("image.png", options)?;
|
||||||
let data = encode_png(image);
|
let data = encode_png(image);
|
||||||
ar.write_all(&data)?;
|
ar.write_all(&data)?;
|
||||||
ar.flush()?;
|
ar.flush()?;
|
||||||
}
|
}
|
||||||
ar.finish()?;
|
ar.finish()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_board_from_file(path: &Path) -> io::Result<(Board, Option<DynamicImage>)> {
|
pub fn read_board_from_file(path: &Path) -> io::Result<(Board, Option<DynamicImage>)> {
|
||||||
let file = File::open(path)?;
|
let file = File::open(path)?;
|
||||||
let mut ar = zip::ZipArchive::new(file)?;
|
let mut ar = zip::ZipArchive::new(file)?;
|
||||||
|
|
||||||
let json_file = ar.by_name("graph.json")?;
|
let json_file = ar.by_name("graph.json")?;
|
||||||
let board = serde_json::from_reader(json_file)?;
|
let board = serde_json::from_reader(json_file)?;
|
||||||
let image = ar.by_name("image.png").ok();
|
let image = ar.by_name("image.png").ok();
|
||||||
if image.is_none() {
|
if image.is_none() {
|
||||||
return Ok((board, None))
|
return Ok((board, None))
|
||||||
}
|
}
|
||||||
let mut image_file = image.unwrap();
|
let mut image_file = image.unwrap();
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
image_file.read_to_end(&mut buf)?;
|
image_file.read_to_end(&mut buf)?;
|
||||||
let image = decode_png(&buf);
|
let image = decode_png(&buf);
|
||||||
Ok((board, Some(image)))
|
Ok((board, Some(image)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn encode_png(image: &DynamicImage) -> Vec<u8> {
|
pub fn encode_png(image: &DynamicImage) -> Vec<u8> {
|
||||||
let mut cursor = Cursor::new(Vec::new());
|
let mut cursor = Cursor::new(Vec::new());
|
||||||
image.write_to(&mut cursor, image::ImageOutputFormat::Png).unwrap();
|
image.write_to(&mut cursor, image::ImageOutputFormat::Png).unwrap();
|
||||||
cursor.into_inner()
|
cursor.into_inner()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn decode_png(buf: &[u8]) -> DynamicImage {
|
pub fn decode_png(buf: &[u8]) -> DynamicImage {
|
||||||
let cursor = Cursor::new(buf);
|
let cursor = Cursor::new(buf);
|
||||||
let mut reader = ImageReader::new(cursor);
|
let mut reader = ImageReader::new(cursor);
|
||||||
reader.set_format(image::ImageFormat::Png);
|
reader.set_format(image::ImageFormat::Png);
|
||||||
reader.decode().unwrap()
|
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: CoordtypeConvertible> {
|
||||||
|
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 }
|
||||||
|
}
|
||||||
|
34
src/main.rs
34
src/main.rs
@ -13,7 +13,7 @@ use std::sync::Mutex;
|
|||||||
|
|
||||||
mod board;
|
mod board;
|
||||||
use board::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 ////////////////////
|
//////////////////// Global State ////////////////////
|
||||||
// Don't @ me...
|
// Don't @ me...
|
||||||
@ -133,35 +133,9 @@ mod dispatch {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////// Utility Functions ////////////////////
|
impl CoordTransformer<i32> for frame::Frame {
|
||||||
|
fn origin(&self) -> (i32, i32) { (self.x(), self.y()) }
|
||||||
fn lerp(v0: f32, v1: f32, t: f32) -> f32 {
|
fn extremes(&self) -> (i32, i32) { (self.x()+self.w(), self.y()+self.h()) }
|
||||||
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,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////// App State ////////////////////
|
//////////////////// App State ////////////////////
|
||||||
|
Loading…
Reference in New Issue
Block a user