add_edges helper for directional navigation (#17389)

# Objective

While `add_looping_edges` is a helpful method for manually defining
directional navigation maps, we don't always want to loop around!

## Solution

Add a non-looping variant.

These commits are cherrypicked from the more complex #17247.

## Testing

I've updated the `directional_navigation` example to use these changes,
and verified that it works.

---------

Co-authored-by: Rob Parrett <robparrett@gmail.com>
Co-authored-by: Benjamin Brienen <benjamin.brienen@outlook.com>
This commit is contained in:
Alice Cecile 2025-01-15 18:43:03 -05:00 committed by GitHub
parent 5cc3f4727e
commit 72f70745c5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 35 additions and 11 deletions

View File

@ -173,7 +173,16 @@ impl DirectionalNavigationMap {
self.add_edge(b, a, direction.opposite());
}
/// Add symmetrical edges between all entities in the provided slice, looping back to the first entity at the end.
/// Add symmetrical edges between each consecutive pair of entities in the provided slice.
///
/// Unlike [`add_looping_edges`](Self::add_looping_edges), this method does not loop back to the first entity.
pub fn add_edges(&mut self, entities: &[Entity], direction: CompassOctant) {
for pair in entities.windows(2) {
self.add_symmetrical_edge(pair[0], pair[1], direction);
}
}
/// Add symmetrical edges between each consecutive pair of entities in the provided slice, looping back to the first entity at the end.
///
/// This is useful for creating a circular navigation path between a set of entities, such as a menu.
pub fn add_looping_edges(&mut self, entities: &[Entity], direction: CompassOctant) {
@ -342,6 +351,25 @@ mod tests {
assert_eq!(map.get_neighbor(c, CompassOctant::West), None);
}
#[test]
fn edges() {
let mut world = World::new();
let a = world.spawn_empty().id();
let b = world.spawn_empty().id();
let c = world.spawn_empty().id();
let mut map = DirectionalNavigationMap::default();
map.add_edges(&[a, b, c], CompassOctant::East);
assert_eq!(map.get_neighbor(a, CompassOctant::East), Some(b));
assert_eq!(map.get_neighbor(b, CompassOctant::East), Some(c));
assert_eq!(map.get_neighbor(c, CompassOctant::East), None);
assert_eq!(map.get_neighbor(a, CompassOctant::West), None);
assert_eq!(map.get_neighbor(b, CompassOctant::West), Some(a));
assert_eq!(map.get_neighbor(c, CompassOctant::West), Some(b));
}
#[test]
fn looping_edges() {
let mut world = World::new();

View File

@ -214,16 +214,12 @@ fn setup_ui(
// but don't loop around when the edge is reached.
// While looping is a very reasonable choice, we're not doing it here to demonstrate the different options.
for col in 0..N_COLS {
// Don't iterate over the last row, as no lower row exists to connect to
for row in 0..N_ROWS - 1 {
let upper_entity = button_entities.get(&(row, col)).unwrap();
let lower_entity = button_entities.get(&(row + 1, col)).unwrap();
directional_nav_map.add_symmetrical_edge(
*upper_entity,
*lower_entity,
CompassOctant::South,
);
}
let entities_in_column: Vec<Entity> = (0..N_ROWS)
.map(|row| button_entities.get(&(row, col)).unwrap())
.copied()
.collect();
directional_nav_map.add_edges(&entities_in_column, CompassOctant::South);
}
// When changing scenes, remember to set an initial focus!