Skip to content

Commit

Permalink
fix: collection of minor bugfixes and fix for wrong cost summation in…
Browse files Browse the repository at this point in the history
… dijkstra
  • Loading branch information
CalliEve committed Nov 4, 2024
1 parent 126b87d commit 136f5a0
Show file tree
Hide file tree
Showing 8 changed files with 137 additions and 47 deletions.
63 changes: 52 additions & 11 deletions src/algorithm/cost_calculation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -294,8 +294,7 @@ fn calc_station_exit_cost(
{
return match_angle_cost(
(calculate_angle(station_node, node, target_node) / 45.0).round() * 45.0,
)
.map(|c| c / 2.0);
);
}

if !station_approach_available(
Expand Down Expand Up @@ -350,7 +349,8 @@ fn calc_station_exit_cost(
}

// If the ends of the opposite edge are in the neighbors of the station, we
// calculate the angle with that node.
// calculate the angle with that node. We want this to be a preference, but not
// mandatory, so halve the angle penalty.
for edge_node in opposite_edge.get_edge_ends() {
if neighbor_nodes.contains(&edge_node) {
return calc_angle_cost(edge_node, station.get_pos(), node).map(|c| c / 2.0);
Expand All @@ -366,8 +366,7 @@ fn calc_station_exit_cost(
opp_station.get_pos(),
station.get_pos(),
node,
)
.map(|c| c / 2.0);
);
}
}
}
Expand Down Expand Up @@ -638,29 +637,71 @@ mod tests {

#[test]
fn test_calc_angle_cost() {
let first_45 = GridNode::from((1, 0));
let second_45 = GridNode::from((1, 1));
let third_45 = GridNode::from((2, 0));
let result_45 = calc_angle_cost(first_45, second_45, third_45);
assert_eq!(result_45, Ok(5.0));

let first_90 = GridNode::from((0, 0));
let second_90 = GridNode::from((1, 1));
let third_90 = GridNode::from((2, 0));
let result_90 = calc_angle_cost(first_90, second_90, third_90);
assert_eq!(result_90, Ok(2.5));

let first_135 = GridNode::from((0, 1));
let second_135 = GridNode::from((1, 1));
let third_135 = GridNode::from((2, 0));
let result_135 = calc_angle_cost(first_135, second_135, third_135);
assert_eq!(result_135, Ok(0.5));

let first_180 = GridNode::from((0, 2));
let second_180 = GridNode::from((1, 1));
let third_180 = GridNode::from((2, 0));
let result_180 = calc_angle_cost(first_180, second_180, third_180);
assert_eq!(result_180, Ok(0.0));

let first_135 = GridNode::from((2, 2));
let first_135 = GridNode::from((1, 2));
let second_135 = GridNode::from((1, 1));
let third_135 = GridNode::from((1, 0));
let third_135 = GridNode::from((2, 0));
let result_135 = calc_angle_cost(first_135, second_135, third_135);
assert_eq!(result_135, Ok(0.5));

let first_90 = GridNode::from((0, 0));
let first_90 = GridNode::from((2, 2));
let second_90 = GridNode::from((1, 1));
let third_90 = GridNode::from((2, 0));
let result_90 = calc_angle_cost(first_90, second_90, third_90);
assert_eq!(result_90, Ok(2.5));

let first_45 = GridNode::from((1, 0));
let first_45 = GridNode::from((2, 1));
let second_45 = GridNode::from((1, 1));
let third_45 = GridNode::from((2, 0));
let result_45 = calc_angle_cost(first_45, second_45, third_45);
assert_eq!(result_45, Ok(5.0));

let first_180 = GridNode::from((2, 0));
let second_180 = GridNode::from((1, 1));
let third_180 = GridNode::from((0, 2));
let result_180 = calc_angle_cost(first_180, second_180, third_180);
assert_eq!(result_180, Ok(0.0));

let first_135 = GridNode::from((2, 0));
let second_135 = GridNode::from((1, 1));
let third_135 = GridNode::from((1, 2));
let result_135 = calc_angle_cost(first_135, second_135, third_135);
assert_eq!(result_135, Ok(0.5));

let first_90 = GridNode::from((2, 0));
let second_90 = GridNode::from((1, 1));
let third_90 = GridNode::from((2, 2));
let result_90 = calc_angle_cost(first_90, second_90, third_90);
assert_eq!(result_90, Ok(2.5));

let first_45 = GridNode::from((2, 0));
let second_45 = GridNode::from((1, 1));
let third_45 = GridNode::from((2, 1));
let result_45 = calc_angle_cost(first_45, second_45, third_45);
assert_eq!(result_45, Ok(5.0));
}

#[test]
Expand Down Expand Up @@ -858,7 +899,7 @@ mod tests {
);
// unsettled and at 90 degree angle
assert_eq!(
1.25,
2.5,
calc_station_exit_cost(
settings,
&map,
Expand Down Expand Up @@ -889,7 +930,7 @@ mod tests {

// settled with other edge at 90 degree angle
assert_eq!(
1.25,
2.5,
calc_station_exit_cost(
settings,
&map,
Expand Down
54 changes: 31 additions & 23 deletions src/algorithm/edge_dijkstra.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@ use std::{

use ordered_float::NotNan;
use priority_queue::PriorityQueue;
use serde_json::json;

use super::{
cost_calculation::calc_node_cost,
log_print,
occupation::OccupiedNodes,
AlgorithmSettings,
};
Expand Down Expand Up @@ -117,8 +119,19 @@ pub fn edge_dijkstra(
);
}

let mut visited_cost = Vec::new();

while let Some((current, current_cost)) = queue.pop() {
if visited.contains(&current.node) {
panic!("Node visited twice in Dijkstra algorithm.");
}

visited.insert(current.node);
visited_cost.push((
current.node,
*current_cost.0,
*current.cost,
));

if current_cost
.0
Expand Down Expand Up @@ -165,30 +178,25 @@ pub fn edge_dijkstra(
occupied,
)?)?;

// Don't even look at nodes that have infinite cost.
// CHECKME: this is a bit of a hack, we should probably add them to the queue
// and handle the break when we encounter the first, but this causes
// a bug?
// Don't even look at nodes that have infinite cost. saves us from handling them
// later and thus is more efficient.
if cost.is_infinite() {
continue;
}

// Add the heuristic cost to the cost for the queue.
let cost_with_heuristic = cost + neighbor.diagonal_distance_to(from_station.get_pos());

let neighbor_item = QueueItem::from_parent(&current, neighbor, cost);
if let Some((_, old_cost)) = queue.get(&neighbor_item) {
if old_cost.0 > cost_with_heuristic {
queue.push(
neighbor_item,
Reverse(cost_with_heuristic),
);

let neighbor_priority = Reverse(neighbor_item.cost);
if let Some(old_priority) = queue.get_priority(&neighbor_item) {
if old_priority < &neighbor_priority {
*queue
.get_mut(&neighbor_item)
.unwrap()
.0 = neighbor_item.clone();
queue.change_priority(&neighbor_item, neighbor_priority);
}
} else {
queue.push(
neighbor_item,
Reverse(cost_with_heuristic),
);
queue.push(neighbor_item, neighbor_priority);
}
}
}
Expand Down Expand Up @@ -274,12 +282,12 @@ mod tests {
(
GridNode::from((0, 0)),
vec![
GridNode::from((1, 0)),
GridNode::from((2, 0)),
GridNode::from((3, 0)),
GridNode::from((4, 1)),
GridNode::from((5, 2)),
GridNode::from((6, 3))
GridNode::from((1, 1)),
GridNode::from((2, 2)),
GridNode::from((3, 3)),
GridNode::from((4, 4)),
GridNode::from((5, 4)),
GridNode::from((6, 4))
],
GridNode::from((7, 4))
)
Expand Down
3 changes: 2 additions & 1 deletion src/algorithm/recalculate_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,8 @@ mod tests {

let mut state = MapState::new(map.clone());
state.calculate_algorithm_settings();
let settings = state.get_algorithm_settings();
let mut settings = state.get_algorithm_settings();
settings.edge_routing_attempts = 1;

println!(
"testing on map {map_file} with {} stations and {} edges",
Expand Down
2 changes: 1 addition & 1 deletion src/algorithm/route_edges.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ pub fn route_edges(
log_print(
settings,
&format!(
"routed edge {} from {start} to {end}\nOriginally from {} to {}",
"routed edge {} from {start} to {end} at cost {cost}\nOriginally from {} to {}",
edge.get_id(),
from_station.get_pos(),
to_station.get_pos(),
Expand Down
6 changes: 6 additions & 0 deletions src/components/molecules/canvas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,12 @@ fn on_mouse_up(map_state: &mut MapState, ev: &UiEvent, shift_key: bool) {
return;
}

for selected_id in map_state.get_selected_edges() {
map.get_mut_edge(*selected_id)
.expect("selected edge should exist")
.deselect();
}

map_state.set_selected_edges(vec![edge_id]);
map.get_mut_edge(edge_id)
.expect("edge should exist")
Expand Down
26 changes: 24 additions & 2 deletions src/components/state/canvas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,24 @@ impl CanvasState {
self.size
}

/// Get the size of the canvas that is currently visible.
pub fn get_visible_size(&self) -> (u32, u32) {
let width = (self
.x_limit
.1
- self
.x_limit
.0) as u32;
let height = (self
.y_limit
.1
- self
.y_limit
.0) as u32;

(width, height)
}

/// A setter method for the canvas size.
pub fn set_size(&mut self, size: (u32, u32)) {
self.size = size;
Expand Down Expand Up @@ -218,12 +236,12 @@ impl CanvasState {
self.size
.1,
) / self.drawn_square_size())
.ceil() as i32;
.round() as i32;
let height_node_count = (f64::from(
self.size
.0,
) / self.drawn_square_size())
.ceil() as i32;
.round() as i32;

self.x_limit = (
self.offset
Expand All @@ -248,19 +266,23 @@ impl CanvasState {
pub fn is_on_canvas(&self, node: GridNode) -> bool {
self.x_limit
.0
- 1
< node.0
&& node.0
< self
.x_limit
.1
+ 1
&& self
.y_limit
.0
- 1
< node.1
&& node.1
< self
.y_limit
.1
+ 1
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/models/grid_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ impl GridNode {
/// Translate the [`GridNode`] to a canvas coordinate, given the state of
/// the canvas.
pub fn to_canvas_pos(self, state: CanvasState) -> (f64, f64) {
if !state.is_on_canvas(self) {
if self.0 == i32::MIN || self.1 == i32::MIN {
return (f64::MIN, f64::MIN);
}

Expand Down
28 changes: 20 additions & 8 deletions src/utils/parsing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,16 @@ pub(super) fn normalize_coords(mut items: Vec<(f64, f64)>, state: CanvasState) -

let size_x = f64::from(
state
.get_size()
.1,
) - 4.0 * square_size;
.get_visible_size()
.0,
) * square_size
- 4.0 * square_size;
let size_y = f64::from(
state
.get_size()
.0,
) - 4.0 * square_size;
.get_visible_size()
.1,
) * square_size
- 4.0 * square_size;

let mut min_x = f64::MAX;
let mut max_x = f64::MIN;
Expand All @@ -50,8 +52,18 @@ pub(super) fn normalize_coords(mut items: Vec<(f64, f64)>, state: CanvasState) -
}

for (x, y) in &mut items {
*x = (*x - min_x) / (max_x - min_x) * size_x + 2.0 * square_size;
*y = (*y - min_y) / (max_y - min_y) * size_y + 2.0 * square_size;
*x = (*x - min_x) / (max_x - min_x) * size_x
+ (state
.get_offset()
.0 as f64
/ square_size)
+ 2.0 * square_size;
*y = (*y - min_y) / (max_y - min_y) * size_y
+ (state
.get_offset()
.1 as f64
/ square_size)
+ 2.0 * square_size;
}

items
Expand Down

0 comments on commit 136f5a0

Please sign in to comment.