simvx.core.navigation

2D Navigation and Pathfinding for SimVX.

Provides graph-based and grid-based A* pathfinding, plus a NavigationAgent2D node that follows computed paths with steering and emits signals on arrival.

Usage: from simvx.core import PathFinder2D, NavGrid2D, NavigationAgent2D

# Graph-based pathfinding
astar = PathFinder2D()
astar.add_point(0, (0, 0))
astar.add_point(1, (10, 0))
astar.connect_points(0, 1)
path = astar.get_point_path(0, 1)  # [(0,0), (10,0)]

# Grid-based pathfinding
grid = NavGrid2D(20, 20, cell_size=32.0)
grid.set_solid(5, 5)
path = grid.find_path((0, 0), (10, 10))

# Navigation agent node
agent = NavigationAgent2D()
agent.set_navigation(astar)
agent.target_position = (10, 0)

Submodules

Package Contents

Classes

PathFinder2D

Graph-based A* pathfinding. Supports both grid-based and arbitrary graphs.

NavGrid2D

Grid-based A* with automatic cell management.

NavigationAgent2D

2D node that follows A* paths with steering.

Data

log

__all__

CornerPolicy

How a grid treats a diagonal step past the corner of a solid cell.

API

simvx.core.navigation.log

‘getLogger(…)’

simvx.core.navigation.__all__

[‘PathFinder2D’, ‘NavGrid2D’, ‘NavigationAgent2D’, ‘PathFollower2D’, ‘CornerPolicy’]

simvx.core.navigation.CornerPolicy

None

How a grid treats a diagonal step past the corner of a solid cell.

class simvx.core.navigation.PathFinder2D[source]

Graph-based A* pathfinding. Supports both grid-based and arbitrary graphs.

Initialization

__slots__

(‘_points’, ‘_connections’, ‘_weights’, ‘_disabled’)

add_point(id: int, position: tuple[float, float], weight: float = 1.0)[source]

Add a point to the graph. Weight scales traversal cost (default 1.0).

remove_point(id: int)[source]

Remove a point and all its connections.

connect_points(id1: int, id2: int, bidirectional: bool = True)[source]

Connect two points. Both must already exist.

disconnect_points(id1: int, id2: int, bidirectional: bool = True)[source]

Remove connection between two points.

set_point_disabled(id: int, disabled: bool = True)[source]

Disable/enable a point. Disabled points are excluded from pathfinding.

is_point_disabled(id: int) bool[source]
has_point(id: int) bool[source]
get_point_position(id: int) tuple[float, float][source]
get_point_connections(id: int) set[int][source]
property point_count: int[source]

Number of registered points (including disabled).

get_closest_point(position: tuple[float, float]) int[source]

Return id of the closest non-disabled point to the given position.

get_id_path(from_id: int, to_id: int) list[int][source]

A* search returning list of point IDs from start to end. Empty list if no path.

get_point_path(from_id: int, to_id: int) list[tuple[float, float]][source]

A* search returning list of world positions.

class simvx.core.navigation.NavGrid2D(width: int, height: int, cell_size: float = 1.0, corners: simvx.core.navigation.CornerPolicy = 'strict', offset: tuple[float, float] = (0.0, 0.0))[source]

Grid-based A* with automatic cell management.

corners decides when a diagonal step past the corner of a solid cell is allowed. Cutting a corner is only executable by an agent that is small enough to fit through the gap it leaves, so the four policies trade agent size against path length:

"never" No diagonal steps at all; every route is 4-connected. "strict" (default) A diagonal needs both of the two cells it passes between to be free, so no agent is ever asked to squeeze through a gap of zero width. "relaxed" A diagonal needs at least one of the two free, which lets a point-sized agent slip past a single corner, as between diagonally placed fence posts. "always" Diagonals are unconditional, so a route may cross a sealed diagonal wall that no agent of any size can actually pass.

Args: width: Grid width in cells. height: Grid height in cells. cell_size: Size of each cell in world units. corners: Diagonal-corner policy, one of "never", "strict", "relaxed" or "always". offset: World-space offset of the grid origin.

Raises: ValueError: If corners is not one of the four policy names.

Initialization

__slots__

(‘_width’, ‘_height’, ‘_cell_size’, ‘_corners’, ‘_offset’, ‘_solid’, ‘_weights’)

property width: int[source]
property height: int[source]
property cell_size: float[source]
property corners: simvx.core.navigation.CornerPolicy[source]

The diagonal-corner policy this grid searches with.

set_solid(x: int, y: int, solid: bool = True)[source]

Mark a cell as solid (impassable) or clear it.

is_solid(x: int, y: int) bool[source]
set_weight(x: int, y: int, weight: float)[source]

Set traversal weight for a cell. Default is 1.0; higher = costlier.

find_path(from_cell: tuple[int, int], to_cell: tuple[int, int]) list[tuple[int, int]][source]

Find shortest path between two grid cells. Returns list of (x,y) cells.

Diagonal steps are admitted according to the grid’s corners policy. Returns an empty list when either end is off the grid, either end is solid, or no route exists.

find_path_world(from_pos: tuple[float, float], to_pos: tuple[float, float]) list[tuple[float, float]][source]

Find path using world positions (auto-converts to/from grid coords).

class simvx.core.navigation.NavigationAgent2D(max_speed: float = 100.0, path_desired_distance: float = 4.0, **kwargs)[source]

Bases: simvx.core.nodes_2d.node2d.Node2D

2D node that follows A* paths with steering.

Assign a pathfinder (PathFinder2D or NavGrid2D) via set_navigation(), set target_position, and the agent moves toward it each physics frame.

Emits navigation_finished when the target is reached.

Usage: agent = NavigationAgent2D(max_speed=200.0) agent.set_navigation(my_astar) agent.target_position = Vec2(500, 300)

Initialization

max_speed

‘Property(…)’

path_desired_distance

‘Property(…)’

property target_position: simvx.core.math.types.Vec2[source]
property velocity: simvx.core.math.types.Vec2[source]

Current velocity (read-only, computed each physics frame).

property is_navigation_finished: bool[source]
set_navigation(nav: simvx.core.navigation.PathFinder2D | simvx.core.navigation.NavGrid2D)[source]

Assign a pathfinder instance.

on_fixed_update(dt: float)[source]
position

‘_SpatialVecProperty(…)’

rotation

‘Property(…)’

scale

‘_SpatialVecProperty(…)’

z_index

‘Property(…)’

z_as_relative

‘Property(…)’

render_layer

‘Property(…)’

set_render_layer(index: int, enabled: bool = True) None
is_on_render_layer(index: int) bool
property absolute_z_index: int
property rotation_degrees: float
property world_position: simvx.core.math.types.Vec2
property world_rotation: float
property world_scale: simvx.core.math.types.Vec2
property world_transform: tuple[simvx.core.math.types.Vec2, simvx.core.math.types.Vec2, float]
property forward: simvx.core.math.types.Vec2
property right: simvx.core.math.types.Vec2
translate(offset: tuple[float, float] | numpy.ndarray)
rotate(radians: float)
rotate_deg(degrees: float)
look_at(target: tuple[float, float] | numpy.ndarray)
transform_points(points: list[simvx.core.math.types.Vec2]) list[simvx.core.math.types.Vec2]
draw_polygon(renderer, points: list[simvx.core.math.types.Vec2], closed=True, colour=None)
wrap_screen(margin: float = 20)
hdr

‘Property(…)’

property transform_render_dirty: bool
strict_errors: ClassVar[bool]

True

dev_checks: ClassVar[bool]

None

script_error_raised

‘Signal(…)’

dynamic: bool

False

visible

‘Property(…)’

update_mode

‘Property(…)’

__properties__: ClassVar[dict[str, simvx.core.descriptors.Property]]

None

classmethod __init_subclass__(**kwargs)
property name: str
property visible_in_tree: bool
reset_error() None
add_child(node: simvx.core.node.T) simvx.core.node.T
remove_child(node: simvx.core.node.Node) None
reparent(new_parent: simvx.core.node.Node)
node_at(path, default=_NO_DEFAULT)
find(target, *, direct: bool = False)
find_all(target, *, direct: bool = False)
expect(target, *, direct: bool = False)
ancestor(target)
walk(*, include_self: bool = True) collections.abc.Iterator[simvx.core.node.Node]
property path: str
property is_scene_root: bool
add_to_group(group: str)
remove_from_group(group: str)
is_in_group(group: str) bool
on_ready() None
on_enter_tree() None
on_exit_tree() None
on_update(dt: float) None
on_draw(renderer) None
on_picked(event: simvx.core.events.InputEvent) None
on_unhandled_input(event: simvx.core.events.TreeInputEvent) None
start_coroutine(gen: simvx.core.descriptors.Coroutine) simvx.core.descriptors.CoroutineHandle
stop_coroutine(gen_or_handle)
queue_redraw() None
property render_dirty: bool
clear_children()
destroy()
property destroying: bool
call_deferred(method: collections.abc.Callable[..., Any], *args: Any) None
property app
property tree: simvx.core.scene_tree.SceneTree
property physics
property physics_2d
__getitem__(key: str)
classmethod get_properties() dict[str, simvx.core.descriptors.Property]
__repr__()