Skip to content

Commit

Permalink
feat: zoom and panning added + improved loading of graphml files
Browse files Browse the repository at this point in the history
  • Loading branch information
CalliEve committed Jul 1, 2024
1 parent 0553f5b commit 71d06be
Show file tree
Hide file tree
Showing 27 changed files with 695 additions and 221 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ edition = "2021"

[dependencies]
leptos = { version = "0.6.11", features = ["csr", "nightly"] }
web-sys = { version = "0.3", features = ["HtmlCanvasElement", "HtmlInputElement", "FileList", "File", "Blob", "CanvasRenderingContext2d", "CssStyleDeclaration", "Element", "Window"] }
web-sys = { version = "0.3", features = ["KeyboardEvent", "HtmlCanvasElement", "HtmlInputElement", "FileList", "File", "Blob", "CanvasRenderingContext2d", "CssStyleDeclaration", "Element", "Window"] }
wasm-bindgen = { version = "0.2" }
console_log = "1"
log = "0.4"
Expand Down
16 changes: 11 additions & 5 deletions src/algorithm/closest_corner.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
use std::f64::consts::PI;

use crate::utils::equal_pixel;
use crate::{
components::CanvasState,
utils::equal_pixel,
};

/// Calculates the coordinate of the corner (on an octilinear grid) of a station
/// closest to the given neigbor. An offset is provided for, if the corner is
/// further from the middle of the station coordinate.
pub fn calc_closest_corner(from: (f64, f64), to: (f64, f64), square_size: u32) -> (f64, f64) {
let cardinal_offset = f64::from(square_size) / PI;
let corner_offset = f64::from(square_size) / PI * 0.8;
pub fn calc_closest_corner(from: (f64, f64), to: (f64, f64), state: CanvasState) -> (f64, f64) {
let cardinal_offset = state.drawn_square_size() / PI;
let corner_offset = state.drawn_square_size() / PI * 0.8;

let (from_x, from_y) = from;
let (to_x, to_y) = to;
Expand Down Expand Up @@ -52,7 +55,10 @@ mod tests {
use super::*;

fn run_closest_corner_test(from: (f64, f64), to: (f64, f64), expected: (f64, f64)) {
let result = calc_closest_corner(from, to, 3);
let mut state = CanvasState::new();
state.set_square_size(3);

let result = calc_closest_corner(from, to, state);
let (result_x, result_y) = result;
let (expected_x, expected_y) = expected;

Expand Down
32 changes: 21 additions & 11 deletions src/algorithm/draw_edge.rs
Original file line number Diff line number Diff line change
@@ -1,40 +1,50 @@
use web_sys::CanvasRenderingContext2d;

use super::closest_corner::calc_closest_corner;
use crate::models::GridNode;
use crate::{
components::CanvasState,
models::GridNode,
};

pub fn draw_edge(
from: GridNode,
to: GridNode,
steps: &[GridNode],
canvas: &CanvasRenderingContext2d,
square_size: u32,
state: CanvasState,
) {
let from_pos = from.to_canvas_pos(square_size);
let to_pos = to.to_canvas_pos(square_size);
let from_pos = from.to_canvas_pos(state);
let to_pos = to.to_canvas_pos(state);

let (from_x, from_y) = calc_closest_corner(
from_pos,
steps
.first()
.map_or(to_pos, |s| s.to_canvas_pos(square_size)),
square_size,
.map_or(to_pos, |s| s.to_canvas_pos(state)),
state,
);
canvas.move_to(from_x, from_y);

let mut last_is = state.is_on_canvas(from);
for step in steps {
let (step_x, step_y) = step.to_canvas_pos(square_size);
let (step_x, step_y) = step.to_canvas_pos(state);

let step_is = state.is_on_canvas(*step);
if !last_is && !step_is {
canvas.move_to(step_x, step_y);
continue;
}
last_is = step_is;

canvas.line_to(step_x, step_y);
}

let (to_x, to_y) = calc_closest_corner(
to_pos,
steps
.last()
.map_or(from_pos, |s| {
s.to_canvas_pos(square_size)
}),
square_size,
.map_or(from_pos, |s| s.to_canvas_pos(state)),
state,
);
canvas.line_to(to_x, to_y);
}
41 changes: 26 additions & 15 deletions src/algorithm/grid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,30 @@
use wasm_bindgen::JsValue;
use web_sys::CanvasRenderingContext2d;

use crate::components::CanvasState;

/// Draws the grid onto the canvas based on the given screen size and grid
/// square size. This should be called before anything else is drawn, so the
/// grid is in the background.
pub fn draw_grid(canvas: &CanvasRenderingContext2d, size: (u32, u32), square_size: u32) {
pub fn draw_grid(canvas: &CanvasRenderingContext2d, state: CanvasState) {
canvas.begin_path();
canvas.set_line_width(0.3);
canvas.set_stroke_style(&JsValue::from_str("grey"));

let (height, width) = state.get_size();
let drawn_square_size = state.drawn_square_size();

draw_vertical_lines(
canvas,
size.0,
square_size,
size.1 / square_size,
height,
drawn_square_size,
f64::from(width) / drawn_square_size,
);
draw_horizontal_lines(
canvas,
size.1,
square_size,
size.0 / square_size,
width,
drawn_square_size,
f64::from(height) / drawn_square_size,
);

canvas.stroke();
Expand All @@ -31,11 +36,14 @@ pub fn draw_grid(canvas: &CanvasRenderingContext2d, size: (u32, u32), square_siz
fn draw_vertical_lines(
canvas: &CanvasRenderingContext2d,
length: u32,
square_size: u32,
count: u32,
square_size: f64,
count: f64,
) {
for i in 0..count {
let x = f64::from(i * square_size + square_size);
for i in 0..(count
.round()
.abs() as u32)
{
let x = f64::from(i) * square_size + square_size;
canvas.move_to(x, 0.0);
canvas.line_to(x, f64::from(length));
}
Expand All @@ -45,11 +53,14 @@ fn draw_vertical_lines(
fn draw_horizontal_lines(
canvas: &CanvasRenderingContext2d,
length: u32,
square_size: u32,
count: u32,
square_size: f64,
count: f64,
) {
for i in 0..count {
let y = f64::from(i * square_size + square_size);
for i in 0..(count
.round()
.abs() as u32)
{
let y = f64::from(i) * square_size + square_size;
canvas.move_to(0.0, y);
canvas.line_to(f64::from(length), y);
}
Expand Down
8 changes: 2 additions & 6 deletions src/algorithm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,9 @@ pub fn redraw_canvas(canvas: &HtmlCanvasElement, state: &MapState) {
.dyn_into::<CanvasRenderingContext2d>()
.unwrap();

draw_grid(
&context,
state.get_size(),
state.get_square_size(),
);
draw_grid(&context, state.get_canvas_state());

let draw_drawable = |d: &dyn Drawable| d.draw(&context, state.get_square_size());
let draw_drawable = |d: &dyn Drawable| d.draw(&context, state.get_canvas_state());

state
.get_map()
Expand Down
21 changes: 18 additions & 3 deletions src/components/atoms/button.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,17 @@ pub fn Button(
/// If the button should be colored red (is blue otherwise).
#[prop(optional)]
danger: bool,
/// If the button is an overlay button.
#[prop(optional)]
overlay: bool,
) -> impl IntoView {
let color = if danger { "red" } else { "blue" };
let color = if danger {
"red"
} else if overlay {
"gray"
} else {
"blue"
};

let base = if danger { 600 } else { 400 };
let base_hover = base + 100;
Expand All @@ -31,13 +40,19 @@ pub fn Button(
let dark_hover = dark + 100;
let dark_active = dark + 200;

let mut class = "inline-block rounded px-4 \
py-1.5 text-sm font-semibold uppercase \
let mut class = "inline-block px-4 \
py-1.5 text-center uppercase \
leading-snug shadow-neutral-800 \
dark:shadow-neutral-950 hover:shadow-blue-900 \
dark:hover:shadow-neutral-900"
.to_owned();

if overlay {
class += " rounded-full text-xl font-bold h-11 w-11";
} else {
class += " rounded text-sm font-semibold";
}

if outlined {
class += &format!(
" border-solid border-4 text-{color}-{base} \
Expand Down
8 changes: 7 additions & 1 deletion src/components/atoms/number_input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,19 @@ where
.to_lowercase()
.replace(' ', "_");

let parse_input = move |ev| {
let val = event_target_value(&ev);

on_input(if let Ok(val) = val.parse() { val } else { 1.0 });
};

view! {
<div class="relative mb-3" data-twe-input-wrapper-init>
<input
type="number"
class="peer block min-h-[auto] w-full rounded border-b-2 rounded-md border-solid border-blue-400 bg-transparent px-3 py-[0.32rem] leading-[1.6] outline-none transition-all duration-200 ease-linear peer-focus:text-primary motion-reduce:transition-none dark:text-white dark:placeholder:text-neutral-300 dark:autofill:shadow-autofill dark:peer-focus:text-primary dark:border-blue-600 focus:border-blue-600 dark:focus:border-blue-800"
id={id.clone()}
on:input=move |ev| {on_input(event_target_value(&ev).parse().expect("number input does not give number"))}
on:input=parse_input
max=max
min=min
prop:value=move || value.map(|v| v().max(min)) />
Expand Down
1 change: 1 addition & 0 deletions src/components/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ mod state;

pub use pages::Home;
pub use state::{
CanvasState,
MapState,
StateProvider,
};
41 changes: 19 additions & 22 deletions src/components/molecules/canvas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ use wasm_bindgen::{
};

use crate::{
components::MapState,
components::{
CanvasState,
MapState,
},
models::{
GridNode,
SelectedLine,
Expand Down Expand Up @@ -85,7 +88,7 @@ fn update_canvas_size(map_state: &RwSignal<MapState>) {
.round()) as u32;

// update the state with the new size.
map_state.update(|state| state.set_size((height, width)));
map_state.update(|state| state.update_canvas_state(|canvas| canvas.set_size((height, width))));
}

/// Gets the position on the canvas that was clicked.
Expand Down Expand Up @@ -119,6 +122,7 @@ fn on_mouse_down(map_state: &mut MapState, ev: &UiEvent) {
else {
return;
};
let canvas_state = map_state.get_canvas_state();

// Handle a click while having a new station selected.
if let Some(selected) = map_state
Expand All @@ -131,8 +135,8 @@ fn on_mouse_down(map_state: &mut MapState, ev: &UiEvent) {
return;
}

let canvas_pos = canvas_click_pos(map_state.get_size(), ev);
let mouse_pos = GridNode::from_canvas_pos(canvas_pos, map_state.get_square_size());
let canvas_pos = canvas_click_pos(canvas_state.get_size(), ev);
let mouse_pos = GridNode::from_canvas_pos(canvas_pos, canvas_state);

// Handle a click while having a new line selected
if let Some(selected_line) = map_state
Expand Down Expand Up @@ -203,8 +207,9 @@ fn on_mouse_up(map_state: &mut MapState, ev: &UiEvent) {
.get_selected_line()
.cloned()
{
let canvas_pos = canvas_click_pos(map_state.get_size(), ev);
let mouse_pos = GridNode::from_canvas_pos(canvas_pos, map_state.get_square_size());
let canvas_state = map_state.get_canvas_state();
let canvas_pos = canvas_click_pos(canvas_state.get_size(), ev);
let mouse_pos = GridNode::from_canvas_pos(canvas_pos, canvas_state);

if let Some(station_at_pos) = map
.station_at_node(mouse_pos)
Expand Down Expand Up @@ -250,8 +255,9 @@ fn on_mouse_up(map_state: &mut MapState, ev: &UiEvent) {
/// [mousemove]: https://developer.mozilla.org/en-US/docs/Web/API/Element/mousemove_event
fn on_mouse_move(map_state_signal: &RwSignal<MapState>, ev: &UiEvent) {
let mut map_state = map_state_signal.get();
let canvas_pos = canvas_click_pos(map_state.get_size(), ev);
let mouse_pos = GridNode::from_canvas_pos(canvas_pos, map_state.get_square_size());
let canvas_state = map_state.get_canvas_state();
let canvas_pos = canvas_click_pos(canvas_state.get_size(), ev);
let mouse_pos = GridNode::from_canvas_pos(canvas_pos, canvas_state);

// Handle move of selected line
if let Some(selected) = map_state.get_mut_selected_line() {
Expand Down Expand Up @@ -289,21 +295,11 @@ fn on_mouse_out(map_state: &mut MapState) {

/// Listener for when the user scrolls on the canvas.
fn on_scroll(map_state: &mut MapState, amount: f64) {
let current = map_state.get_square_size();

let size = if amount > 0.0 {
if current >= 100 {
return;
}
current + 5
if amount > 0.0 {
map_state.update_canvas_state(CanvasState::zoom_in);
} else {
if current <= 5 {
return;
}
current - 5
map_state.update_canvas_state(CanvasState::zoom_out);
};

map_state.set_square_size(size);
}

/// The canvas itself.
Expand Down Expand Up @@ -337,6 +333,7 @@ pub fn Canvas() -> impl IntoView {
.expect("should be loaded now");
let s = map_state
.get()
.get_canvas_state()
.get_size();
canvas_node.set_height(s.0);
canvas_node.set_width(s.1);
Expand All @@ -349,7 +346,7 @@ pub fn Canvas() -> impl IntoView {
});

view! {
<div class="grow overflow-hidden bg-zinc-50 dark:bg-neutral-700 text-black dark:text-white">
<div class="absolute grow overflow-hidden bg-zinc-50 dark:bg-neutral-700 text-black dark:text-white">
<canvas
_ref=canvas_ref

Expand Down
Loading

0 comments on commit 71d06be

Please sign in to comment.