plozone 0.1.0

3D spatial zone engine: geofencing, octree hole-scanning, realtime sync (WebSocket + QUIC + io_uring), voxel pathfinding, and AV sensor fusion.
Documentation
//! Bevy ECS integration (feature `bevy`).
//!
//! Provides [`Spatial3dPlugin`] which inserts a shared [`ZoneStore`] and
//! [`OctreeNode`] into Bevy's ECS as resources, along with a system that
//! tracks entity zone membership and fires enter/exit events.

use std::sync::{Arc, RwLock};

use bevy::prelude::*;

use crate::coord::CoordSystem;
use crate::octree::OctreeNode;
use crate::store::ZoneStore;
use crate::zone::ZoneEntry;

/// ECS resource wrapping a shared zone store.
#[derive(Resource)]
pub struct SharedZoneStore(pub Arc<RwLock<ZoneStore>>);

/// ECS resource wrapping a shared octree.
#[derive(Resource)]
pub struct SharedOctreeRes(pub Arc<RwLock<OctreeNode>>);

/// Component that tracks which zones an entity currently occupies.
#[derive(Component, Default)]
pub struct ZoneTracker {
    pub active: Vec<u32>,
}

/// Fired when an entity enters a zone.
#[derive(Event)]
pub struct ZoneEnterEvent {
    pub entity: Entity,
    pub zone_id: u32,
}

/// Fired when an entity exits a zone.
#[derive(Event)]
pub struct ZoneExitEvent {
    pub entity: Entity,
    pub zone_id: u32,
}

/// Bevy plugin that wires up zone tracking for a given coordinate system.
///
/// ```ignore
/// App::new()
///     .add_plugins(DefaultPlugins)
///     .add_plugins(Spatial3dPlugin::new(
///         DirectCartesian,
///         5000.0,
///     ))
///     .run();
/// ```
pub struct Spatial3dPlugin<C: CoordSystem + Clone + Send + Sync + 'static> {
    pub coord: C,
    pub world_half: f64,
}

impl<C: CoordSystem + Clone + Send + Sync + 'static> Plugin for Spatial3dPlugin<C> {
    fn build(&self, app: &mut App) {
        let store = ZoneStore::from_entries(&[], &self.coord);
        let octree = OctreeNode::new([0.0; 3], self.world_half);

        app.insert_resource(SharedZoneStore(Arc::new(RwLock::new(store))))
            .insert_resource(SharedOctreeRes(Arc::new(RwLock::new(octree))))
            .insert_resource(CoordRes(self.coord.clone()))
            .add_event::<ZoneEnterEvent>()
            .add_event::<ZoneExitEvent>()
            .add_systems(Update, zone_tracking_system::<C>);
    }
}

#[derive(Resource)]
struct CoordRes<C>(C);

/// System that checks each entity with a `GlobalTransform` and `ZoneTracker`
/// against the zone store, firing enter/exit events on transitions.
fn zone_tracking_system<C: CoordSystem + Clone + Send + Sync + 'static>(
    store: Res<SharedZoneStore>,
    coord: Res<CoordRes<C>>,
    mut query: Query<(Entity, &GlobalTransform, &mut ZoneTracker)>,
    mut enter: EventWriter<ZoneEnterEvent>,
    mut exit: EventWriter<ZoneExitEvent>,
) {
    let store = store.0.read().unwrap();
    let conv = &coord.as_ref().0;
    for (entity, tf, mut tracker) in &mut query {
        let t = tf.translation();
        let p = conv.to_internal([t.x as f64, t.z as f64, t.y as f64]);
        let current = store.query_enu(p);

        for &id in &current {
            if !tracker.active.contains(&id) {
                enter.write(ZoneEnterEvent { entity, zone_id: id });
            }
        }
        for &id in &tracker.active {
            if !current.contains(&id) {
                exit.write(ZoneExitEvent { entity, zone_id: id });
            }
        }
        tracker.active = current;
    }
}

/// Helper to add zones from Bevy systems.
pub fn add_zone_to_store(
    store: &SharedZoneStore,
    entry: ZoneEntry,
    conv: &dyn CoordSystem,
) {
    store.0.write().unwrap().add_zone(entry.id, &entry.zone, conv);
}

/// Helper to insert a point into the octree from Bevy systems.
pub fn insert_point(
    octree: &SharedOctreeRes,
    pos: [f64; 3],
    threshold: usize,
) {
    octree.0.write().unwrap().insert(pos, threshold);
}