Skip to content

Commit 067ac25

Browse files
committed
fix: directional navigation uses edge to edge distance not center to center
1 parent ffc66f1 commit 067ac25

File tree

1 file changed

+53
-4
lines changed

1 file changed

+53
-4
lines changed

crates/bevy_input_focus/src/directional_navigation.rs

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ use bevy_ecs::{
6666
prelude::*,
6767
system::SystemParam,
6868
};
69-
use bevy_math::{CompassOctant, Dir2, Vec2};
69+
use bevy_math::{CompassOctant, Dir2, Rect, Vec2};
7070
use bevy_ui::{ComputedNode, UiGlobalTransform, UiSystems};
7171
use thiserror::Error;
7272

@@ -576,7 +576,6 @@ fn score_candidate(
576576
// Get direction in mathematical coordinates, then flip Y for UI coordinates
577577
let dir = Dir2::from(octant).as_vec2() * Vec2::new(1.0, -1.0);
578578
let to_candidate = candidate_pos - origin_pos;
579-
let distance = to_candidate.length();
580579

581580
// Check direction first
582581
// Convert UI coordinates (Y+ = down) to mathematical coordinates (Y+ = up) by flipping Y
@@ -599,15 +598,27 @@ fn score_candidate(
599598
return f32::INFINITY;
600599
}
601600

601+
// Calculate distance between rectangle edges, not centers
602+
let origin_rect = Rect::from_center_size(origin_pos, origin_size);
603+
let candidate_rect = Rect::from_center_size(candidate_pos, candidate_size);
604+
let dx = (candidate_rect.min.x - origin_rect.max.x)
605+
.max(origin_rect.min.x - candidate_rect.max.x)
606+
.max(0.0);
607+
let dy = (candidate_rect.min.y - origin_rect.max.y)
608+
.max(origin_rect.min.y - candidate_rect.max.y)
609+
.max(0.0);
610+
let distance = (dx * dx + dy * dy).sqrt();
611+
602612
// Check max distance
603613
if let Some(max_dist) = config.max_search_distance {
604614
if distance > max_dist {
605615
return f32::INFINITY;
606616
}
607617
}
608618

609-
// Calculate alignment score
610-
let alignment = if distance > 0.0 {
619+
// Calculate alignment score using center-to-center direction
620+
let center_distance = to_candidate.length();
621+
let alignment = if center_distance > 0.0 {
611622
to_candidate.normalize().dot(dir).max(0.0)
612623
} else {
613624
1.0
@@ -1171,4 +1182,42 @@ mod tests {
11711182
Some(node_c)
11721183
);
11731184
}
1185+
1186+
#[test]
1187+
fn test_edge_distance_vs_center_distance() {
1188+
let mut nav_map = DirectionalNavigationMap::default();
1189+
let config = AutoNavigationConfig::default();
1190+
1191+
let left = Entity::from_bits(1);
1192+
let wide_top = Entity::from_bits(2);
1193+
let bottom = Entity::from_bits(3);
1194+
1195+
let left_node = FocusableArea {
1196+
entity: left,
1197+
position: Vec2::new(100.0, 200.0),
1198+
size: Vec2::new(100.0, 100.0),
1199+
};
1200+
1201+
let wide_top_node = FocusableArea {
1202+
entity: wide_top,
1203+
position: Vec2::new(350.0, 150.0),
1204+
size: Vec2::new(300.0, 80.0),
1205+
};
1206+
1207+
let bottom_node = FocusableArea {
1208+
entity: bottom,
1209+
position: Vec2::new(270.0, 300.0),
1210+
size: Vec2::new(100.0, 80.0),
1211+
};
1212+
1213+
let nodes = vec![left_node, wide_top_node, bottom_node];
1214+
1215+
auto_generate_navigation_edges(&mut nav_map, &nodes, &config);
1216+
1217+
assert_eq!(
1218+
nav_map.get_neighbor(left, CompassOctant::East),
1219+
Some(wide_top),
1220+
"Should navigate to wide_top not bottom, even though bottom's center is closer."
1221+
);
1222+
}
11741223
}

0 commit comments

Comments
 (0)