Skip to content

Commit f0a63b3

Browse files
committed
refactor: reduces code duplication for getting best candidate
1 parent e4c012a commit f0a63b3

File tree

1 file changed

+49
-64
lines changed

1 file changed

+49
-64
lines changed

crates/bevy_input_focus/src/directional_navigation.rs

Lines changed: 49 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
//! - **Precise control**: Define exact navigation flow, including non-obvious connections like looping edges
5858
//! - **Cross-layer navigation**: Connect elements across different UI layers or z-index levels
5959
//! - **Custom behavior**: Implement domain-specific navigation patterns (e.g., spreadsheet-style wrapping)
60+
6061
use alloc::vec::Vec;
6162
use bevy_app::prelude::*;
6263
use bevy_camera::visibility::InheritedVisibility;
@@ -407,7 +408,7 @@ impl DirectionalNavigationMap {
407408
pub struct DirectionalNavigation<'w, 's> {
408409
/// The currently focused entity.
409410
pub focus: ResMut<'w, InputFocus>,
410-
/// The manual override navigation map containing pre-specified connections between entities.
411+
/// The directional navigation map containing manually defined connections between entities.
411412
pub map: Res<'w, DirectionalNavigationMap>,
412413
/// Configuration for the automated portion of the navigation algorithm.
413414
pub config: Res<'w, AutoNavigationConfig>,
@@ -449,7 +450,12 @@ impl<'w, 's> DirectionalNavigation<'w, 's> {
449450
self.focus.set(new_focus);
450451
Ok(new_focus)
451452
} else if let Some(origin) = self.entity_to_focusable_area(current_focus)
452-
&& let Some(new_focus) = self.find_best_candidate(origin, direction)
453+
&& let Some(new_focus) = find_best_candidate(
454+
&origin,
455+
direction,
456+
&self.get_navigable_nodes(),
457+
&self.config,
458+
)
453459
{
454460
self.focus.set(new_focus);
455461
Ok(new_focus)
@@ -464,44 +470,7 @@ impl<'w, 's> DirectionalNavigation<'w, 's> {
464470
}
465471
}
466472

467-
/// Finds the best entity to navigate to from the origin in the given direction.
468-
/// For details on what "best" means here, refer to [`AutoNavigationConfig`].
469-
fn find_best_candidate(
470-
&self,
471-
origin: FocusableArea,
472-
direction: CompassOctant,
473-
) -> Option<Entity> {
474-
// Find best candidate in this direction
475-
let mut best_candidate = None;
476-
let mut best_score = f32::INFINITY;
477-
478-
for candidate in self.get_navigable_nodes() {
479-
// Skip self
480-
if candidate.entity == origin.entity {
481-
continue;
482-
}
483-
484-
// Score the candidate
485-
let score = score_candidate(
486-
origin.position,
487-
origin.size,
488-
candidate.position,
489-
candidate.size,
490-
direction,
491-
&self.config,
492-
);
493-
494-
if score < best_score {
495-
best_score = score;
496-
best_candidate = Some(candidate.entity);
497-
}
498-
}
499-
500-
best_candidate
501-
}
502-
503-
/// Queries for all visible [`FocusableArea`] that are eligible to be automatically navigated to.
504-
/// These are nodes with
473+
/// Returns a vec of [`FocusableArea`] representing nodes that are eligible to be automatically navigated to.
505474
fn get_navigable_nodes(&self) -> Vec<FocusableArea> {
506475
self.navigable_entities_query
507476
.iter()
@@ -522,6 +491,7 @@ impl<'w, 's> DirectionalNavigation<'w, 's> {
522491
}
523492

524493
/// Gets the [`FocusableArea`] of the provided entity, if it exists.
494+
///
525495
/// Returns None if there was a [`QueryEntityError`](bevy_ecs::query::QueryEntityError).
526496
fn entity_to_focusable_area(&self, entity: Entity) -> Option<FocusableArea> {
527497
self.focusable_area_query
@@ -706,6 +676,44 @@ fn score_candidate(
706676
distance + alignment_penalty
707677
}
708678

679+
/// Finds the best entity to navigate to from the origin towards the given direction.
680+
///
681+
/// For details on what "best" means here, refer to [`AutoNavigationConfig`].
682+
fn find_best_candidate(
683+
origin: &FocusableArea,
684+
direction: CompassOctant,
685+
candidates: &[FocusableArea],
686+
config: &AutoNavigationConfig,
687+
) -> Option<Entity> {
688+
// Find best candidate in this direction
689+
let mut best_candidate = None;
690+
let mut best_score = f32::INFINITY;
691+
692+
for candidate in candidates {
693+
// Skip self
694+
if candidate.entity == origin.entity {
695+
continue;
696+
}
697+
698+
// Score the candidate
699+
let score = score_candidate(
700+
origin.position,
701+
origin.size,
702+
candidate.position,
703+
candidate.size,
704+
direction,
705+
config,
706+
);
707+
708+
if score < best_score {
709+
best_score = score;
710+
best_candidate = Some(candidate.entity);
711+
}
712+
}
713+
714+
best_candidate
715+
}
716+
709717
/// Automatically generates directional navigation edges for a collection of nodes.
710718
///
711719
/// This function takes a slice of navigation nodes with their positions and sizes, and populates
@@ -761,30 +769,7 @@ pub fn auto_generate_navigation_edges(
761769
}
762770

763771
// Find best candidate in this direction
764-
let mut best_candidate = None;
765-
let mut best_score = f32::INFINITY;
766-
767-
for candidate in nodes {
768-
// Skip self
769-
if candidate.entity == origin.entity {
770-
continue;
771-
}
772-
773-
// Score the candidate
774-
let score = score_candidate(
775-
origin.position,
776-
origin.size,
777-
candidate.position,
778-
candidate.size,
779-
octant,
780-
config,
781-
);
782-
783-
if score < best_score {
784-
best_score = score;
785-
best_candidate = Some(candidate.entity);
786-
}
787-
}
772+
let best_candidate = find_best_candidate(origin, octant, nodes, config);
788773

789774
// Add edge if we found a valid candidate
790775
if let Some(neighbor) = best_candidate {

0 commit comments

Comments
 (0)