From f87c657ad51bd7bb7d14dbd3503c779894e20dba Mon Sep 17 00:00:00 2001 From: Calli Date: Fri, 27 Dec 2024 21:46:53 +0100 Subject: [PATCH] fix: bugs in drawing edges, file modal error state and line section handling --- src/algorithm/drawing/draw_edge.rs | 14 ++ src/algorithm/local_search.rs | 3 +- src/algorithm/station_contraction.rs | 134 +++++++++++++------- src/components/canvas/dbl_click.rs | 11 +- src/components/canvas/keydown.rs | 6 - src/components/molecules/file_modal.rs | 10 +- src/components/organisms/canvas_controls.rs | 36 +++--- src/components/state/mod.rs | 4 +- src/models/edge.rs | 6 +- src/models/grid_node.rs | 5 + src/models/line.rs | 12 +- src/models/map.rs | 2 +- src/models/station.rs | 7 - src/utils/error.rs | 9 +- src/utils/line_sections.rs | 69 +++++----- 15 files changed, 179 insertions(+), 149 deletions(-) diff --git a/src/algorithm/drawing/draw_edge.rs b/src/algorithm/drawing/draw_edge.rs index be3c6d7..418ca84 100644 --- a/src/algorithm/drawing/draw_edge.rs +++ b/src/algorithm/drawing/draw_edge.rs @@ -39,10 +39,24 @@ pub fn draw_edge( state: CanvasState, height_offset: f64, ) { + let mut steps = steps; let from_pos = from.to_canvas_pos(state); let to_pos = to.to_canvas_pos(state); let has_offset = height_offset.abs() > f64::EPSILON; + #[allow(unused_assignments)] // It is used to keep the borrow going + let mut steps_vec = Vec::new(); + if let Some(start) = steps.first() { + if !from.is_neighbor_of(start) { + steps_vec = steps + .iter() + .rev() + .copied() + .collect::>(); + steps = steps_vec.as_ref(); + } + } + // The position of the start node on the canvas, based on the direction it is // leaving the station from let (from_x, from_y) = calc_closest_corner( diff --git a/src/algorithm/local_search.rs b/src/algorithm/local_search.rs index ca9ab0a..34f6c94 100644 --- a/src/algorithm/local_search.rs +++ b/src/algorithm/local_search.rs @@ -208,8 +208,7 @@ pub async fn local_search( if station.get_pos() != station.get_original_pos() && !station .get_pos() - .get_neighbors() - .contains(&station.get_original_pos()) + .is_neighbor_of(&station.get_original_pos()) { neighborhood.insert(0, station.get_original_pos()); } diff --git a/src/algorithm/station_contraction.rs b/src/algorithm/station_contraction.rs index b61004c..a78ddf6 100644 --- a/src/algorithm/station_contraction.rs +++ b/src/algorithm/station_contraction.rs @@ -18,8 +18,8 @@ use crate::{ }, utils::{ line_sections::{ - get_line_section_parts, trace_line_section, + LineSection, }, Result, }, @@ -28,42 +28,39 @@ use crate::{ /// Resolves a cycle of two stations in a line section by taking out the /// starting edge from the line section. -fn resolve_two_station_cycle( - mut line_section: Vec, - mut start: StationID, - mut middles: Vec, -) -> (Vec, StationID, Vec) { - for edge in &line_section.clone() { - if edge.get_from() == start { - line_section.retain(|e| e != edge); - start = edge.get_to(); +fn resolve_two_station_cycle(mut line_section: LineSection) -> LineSection { + for edge in &line_section + .edges + .clone() + { + if edge.get_from() == line_section.ends[0] { + line_section + .edges + .retain(|e| e != edge); + line_section.ends[0] = edge.get_to(); break; - } else if edge.get_to() == start { - line_section.retain(|e| e != edge); - start = edge.get_from(); + } else if edge.get_to() == line_section.ends[0] { + line_section + .edges + .retain(|e| e != edge); + line_section.ends[0] = edge.get_from(); break; } } - middles.retain(|id| *id != start); - (line_section, start, middles) + line_section + .middles + .retain(|id| *id != line_section.ends[0]); + line_section } /// Resolves a line section that is a cycle by taking the station with the most /// edges connected out of the cycle together with the edges it is connected /// to. -fn resolve_cycle( - map: &Map, - mut line_section: Vec, - mut middles: Vec, -) -> ( - Vec, - Vec, - Vec, -) { +fn resolve_cycle(map: &Map, mut line_section: LineSection) -> LineSection { // Find the station with the most edges connected to it. - let mut biggest_station = (middles[0], 0); - for edge in &line_section { + let mut biggest_station = (line_section.middles[0], 0); + for edge in &line_section.edges { let start = edge.get_from(); let end = edge.get_to(); let start_station = map @@ -103,20 +100,34 @@ fn resolve_cycle( // Take out the biggest station and the edges it is connected to, this will // ensure the cycle will now have at least 3 stations in it after contraction. let mut ends = Vec::new(); - middles.retain(|id| *id != biggest_station.0); - for edge in &line_section.clone() { + line_section + .middles + .retain(|id| *id != biggest_station.0); + for edge in &line_section + .edges + .clone() + { if edge.get_from() == biggest_station.0 { - line_section.retain(|e| e != edge); + line_section + .edges + .retain(|e| e != edge); ends.push(edge.get_to()); - middles.retain(|id| *id != edge.get_to()); + line_section + .middles + .retain(|id| *id != edge.get_to()); } else if edge.get_to() == biggest_station.0 { - line_section.retain(|e| e != edge); + line_section + .edges + .retain(|e| e != edge); ends.insert(0, edge.get_from()); - middles.retain(|id| *id != edge.get_from()); + line_section + .middles + .retain(|id| *id != edge.get_from()); } } + line_section.ends = ends; - (line_section, ends, middles) + line_section } /// Check if the station can be contracted into an edge between its neighboring @@ -164,35 +175,59 @@ pub fn contract_stations( while let Some(edge) = unchecked_edges.next() { let mut line_section = trace_line_section(map, edge.get_id(), true); - let (mut ends, mut middles) = get_line_section_parts(&line_section); - if ends.is_empty() { - if middles.len() <= 3 { + + if line_section + .ends + .first() + == line_section + .ends + .last() + { + if line_section + .middles + .len() + <= 3 + { // Just skip, we need at least 4 stations to contract part of a cycle. continue; } logging::log!("Line section has no ends, resolving cycle"); - (line_section, ends, middles) = resolve_cycle(map, line_section, middles); - } else if ends.len() != 2 { + line_section = resolve_cycle(map, line_section); + } else if line_section + .ends + .len() + != 2 + { panic!( "Line section does not have two ends, but instead {}", - ends.len() + line_section + .ends + .len() ); } - let mut start = ends[0]; - let end = ends[1]; + let mut start = line_section.ends[0]; + let end = line_section.ends[1]; if map .get_edge_id_between_if_exists(start, end) .is_some() { // Edge already exists, so we have to resolve the two station cycle this would // form. - (line_section, start, middles) = - resolve_two_station_cycle(line_section, start, middles); + line_section = resolve_two_station_cycle(line_section); + start = line_section.ends[0]; } // Check for other edge cases preventing contraction. - if !can_contract_into(settings, map, start, end, middles.len()) { + if !can_contract_into( + settings, + map, + start, + end, + line_section + .middles + .len(), + ) { continue; } @@ -203,9 +238,10 @@ pub fn contract_stations( .get_mut_edge(new_edge_id) .unwrap(); - new_edge.extend_contracted_stations(&middles); + new_edge.extend_contracted_stations(&line_section.middles); - let middle_stations = middles + let middle_stations = line_section + .middles .iter() .map(|id| { map.get_station(*id) @@ -222,7 +258,11 @@ pub fn contract_stations( // Remove the edges that we contracted from our list of unchecked edges, as we // checked them by contracting them. unchecked_edges = unchecked_edges - .filter(|e| !line_section.contains(e)) + .filter(|e| { + !line_section + .edges + .contains(e) + }) .collect::>() .into_iter(); } diff --git a/src/components/canvas/dbl_click.rs b/src/components/canvas/dbl_click.rs index 6fe779c..187058a 100644 --- a/src/components/canvas/dbl_click.rs +++ b/src/components/canvas/dbl_click.rs @@ -9,10 +9,7 @@ use crate::{ GridNode, SelectedStation, }, - utils::line_sections::{ - get_line_section_parts, - trace_line_section, - }, + utils::line_sections::trace_line_section, MapState, }; @@ -33,10 +30,9 @@ pub fn on_dbl_click(map_state: &mut MapState, ev: &UiEvent, shift_key: bool) { if let Some(edge_id) = map.edge_at_node(mouse_pos) { let line_section = trace_line_section(map, edge_id, false); - let (_, middles) = get_line_section_parts(&line_section); - map_state.set_selected_stations( - middles + line_section + .middles .into_iter() .map(|s| { let mut selected = SelectedStation::new( @@ -76,6 +72,7 @@ pub fn on_dbl_click(map_state: &mut MapState, ev: &UiEvent, shift_key: bool) { map_state.set_selected_edges( line_section + .edges .into_iter() .map(|e| e.get_id()) .chain( diff --git a/src/components/canvas/keydown.rs b/src/components/canvas/keydown.rs index ae1c762..a8e4da1 100644 --- a/src/components/canvas/keydown.rs +++ b/src/components/canvas/keydown.rs @@ -18,12 +18,6 @@ pub fn on_keydown(map_state_signal: &RwSignal, ev: &KeyboardEvent) { }); } - leptos::logging::log!( - "Key pressed: {}, ctrl: {}", - ev.key(), - ev.ctrl_key() - ); - if ev.key() == "z" && ev.ctrl_key() { map_state_signal.update(|map_state| { if let Some(map) = HistoryState::undo( diff --git a/src/components/molecules/file_modal.rs b/src/components/molecules/file_modal.rs index 4240362..94c8d8c 100644 --- a/src/components/molecules/file_modal.rs +++ b/src/components/molecules/file_modal.rs @@ -45,13 +45,10 @@ impl FileType { /// Gets the file uploaded to the input element by the user and passes its /// contents to the provided `on_submit` callback function. -fn get_file(input: &HtmlInputElement, on_submit: S) +fn get_file(input: &HtmlInputElement, on_submit: S, error_state: RwSignal) where S: Fn(FileType, String) + 'static, { - let error_state = - use_context::>().expect("to have found the global error state"); - let Some(file) = input .files() .and_then(|l| l.item(0)) @@ -105,6 +102,9 @@ where S: Fn(FileType, String) + Send + 'static + Copy, C: Fn() + 'static, { + let error_state = + use_context::>().expect("to have found the global error state"); + let input_ref: NodeRef = NodeRef::new(); view! { @@ -125,7 +125,7 @@ where // footer
-
} diff --git a/src/components/organisms/canvas_controls.rs b/src/components/organisms/canvas_controls.rs index 7d79df2..4469fd7 100644 --- a/src/components/organisms/canvas_controls.rs +++ b/src/components/organisms/canvas_controls.rs @@ -158,7 +158,9 @@ pub fn CanvasControls() -> impl IntoView { IDManager::from_data(resp.id_manager_data); } else { map_state.update(|state| { - if let Some(map) = state + if let Some((_, before_map)) = abort_handle.get_untracked() { + state.set_map(before_map); + } else if let Some(map) = state .get_last_loaded() .cloned() { @@ -187,7 +189,9 @@ pub fn CanvasControls() -> impl IntoView { .expect("failed to start algorithm worker"); set_abort_handle(Some(( abort_handle, - req.map + map_state + .get_untracked() + .get_map() .clone(), ))); @@ -213,6 +217,8 @@ pub fn CanvasControls() -> impl IntoView { handle_algorithm_response(resp, req.partial); } } + + set_abort_handle(None); } }); @@ -298,17 +304,13 @@ pub fn CanvasControls() -> impl IntoView { // Abort the algorithm. let abort_algorithm = move |_| { - if algorithm_req - .pending() - .get() - { - if let Some((handle, original_map)) = abort_handle.get() { - handle.abort(); - algorithm_req.clear(); - map_state.update(|state| { - state.set_map(original_map); - }); - } + if let Some((handle, original_map)) = abort_handle.get_untracked() { + handle.abort(); + algorithm_req.clear(); + map_state.update(|state| { + state.set_map(original_map); + }); + set_abort_handle(None); } }; @@ -316,9 +318,9 @@ pub fn CanvasControls() -> impl IntoView { let algorithm_button_class = move || { let mut class = "absolute right-5 top-5 group".to_owned(); - if algorithm_req - .pending() + if abort_handle .get() + .is_some() { class += " is-calculating"; } @@ -363,12 +365,12 @@ pub fn CanvasControls() -> impl IntoView { - +
- +