Begin to migrate board logic to seperate file
This commit is contained in:
parent
0cc76f9ccd
commit
53bd4a9d22
116
src/board.rs
Normal file
116
src/board.rs
Normal file
@ -0,0 +1,116 @@
|
||||
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<usize>,
|
||||
pub labels: HashMap<String, String>,
|
||||
}
|
||||
#[derive(Serialize, Deserialize, Debug, Default)]
|
||||
#[serde(default)]
|
||||
pub struct Board {
|
||||
pub labels: HashMap<String, Vec<String>>,
|
||||
pub nodes: HashMap<usize, Node>,
|
||||
}
|
||||
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<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)
|
||||
}
|
||||
|
||||
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(&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 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()
|
||||
}
|
||||
|
130
src/main.rs
130
src/main.rs
@ -1,5 +1,3 @@
|
||||
use serde::{ Serialize, Deserialize };
|
||||
|
||||
use fltk::*;
|
||||
use fltk::prelude::*;
|
||||
use fltk::enums::*;
|
||||
@ -11,102 +9,19 @@ use image::{ DynamicImage };
|
||||
|
||||
use state::Storage;
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use std::sync::Mutex;
|
||||
|
||||
use std::io::{ Write, Read, Cursor };
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
|
||||
mod board;
|
||||
use board::Board;
|
||||
use board::{ encode_png, decode_png, write_board_to_file };
|
||||
|
||||
//////////////////// Global State ////////////////////
|
||||
// Don't @ me...
|
||||
static STATE: Storage<Mutex<AppState>> = Storage::new();
|
||||
|
||||
//////////////////// Data Layout ////////////////////
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Default)]
|
||||
#[serde(default)]
|
||||
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)]
|
||||
struct Board {
|
||||
labels: HashMap<String, Vec<String>>,
|
||||
nodes: HashMap<usize, Node>,
|
||||
}
|
||||
impl Board {
|
||||
pub fn new() -> Self {
|
||||
let nodes = HashMap::new();
|
||||
let labels = HashMap::new();
|
||||
Board { nodes, labels }
|
||||
}
|
||||
|
||||
pub fn new_node(&mut self, x: f32, y: f32) -> usize {
|
||||
let id = self.next_id();
|
||||
self.nodes.insert(id, Node {
|
||||
x,
|
||||
y,
|
||||
name: String::new(),
|
||||
edges: HashSet::new(),
|
||||
labels: HashMap::new(),
|
||||
});
|
||||
id
|
||||
}
|
||||
|
||||
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<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!();
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////// Auxilary Dialogs ////////////////////
|
||||
fn add_remove_labels_dialog() {
|
||||
let mut state = STATE.get().lock().unwrap();
|
||||
@ -226,19 +141,6 @@ mod dispatch {
|
||||
|
||||
//////////////////// Utility Functions ////////////////////
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
@ -247,10 +149,6 @@ fn inv_lerp(v0: f32, v1: f32, a: f32) -> f32 {
|
||||
(a - v0) / (v1 - v0)
|
||||
}
|
||||
|
||||
fn dist_sq((x0, y0): (f32, f32), (x1, y1): (f32, f32)) -> f32 {
|
||||
(x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0)
|
||||
}
|
||||
|
||||
trait CoordTransformer {
|
||||
fn to_coords(&self, x: i32, y: i32) -> (f32, f32);
|
||||
fn from_coords(&self, x: f32, y: f32) -> (i32, i32);
|
||||
@ -356,23 +254,11 @@ fn main() {
|
||||
}
|
||||
});
|
||||
menubar.add("File/Save As ...", Shortcut::None, menu::MenuFlag::Normal, move |_| {
|
||||
let state = STATE.get().lock().unwrap();
|
||||
let mut fc = dialog::NativeFileChooser::new(dialog::NativeFileChooserType::BrowseSaveFile);
|
||||
fc.show();
|
||||
|
||||
let file = File::create(fc.filename()).unwrap();
|
||||
let mut ar = zip::ZipWriter::new(file);
|
||||
let options = zip::write::FileOptions::default();
|
||||
let state = STATE.get().lock().unwrap();
|
||||
|
||||
ar.start_file("graph.json", options).ok();
|
||||
ar.write(&serde_json::to_vec(&state.board).unwrap()).ok();
|
||||
if let Some(image) = state.image_raw.as_ref() {
|
||||
ar.start_file("image.png", options).ok();
|
||||
let data = encode_png(image);
|
||||
ar.write_all(&data).unwrap();
|
||||
ar.flush().unwrap();
|
||||
}
|
||||
ar.finish().ok();
|
||||
let filename = fc.filename();
|
||||
write_board_to_file(&state.board, state.image_raw.as_ref(), &filename).unwrap();
|
||||
});
|
||||
menubar.add("Edit/Edit Nodes", Shortcut::None, menu::MenuFlag::Normal, |_| {
|
||||
let mut state = STATE.get().lock().unwrap();
|
||||
|
Loading…
Reference in New Issue
Block a user