//! Shared broad phase for interactive modify commands. //! //! The scene already narrows the cursor pick. Commands still need fast access //! to the picked analytic entity and, for intersection-heavy operations such //! as TRIM, to nearby boundary entities. Keeping this compact command-local //! index avoids cloning/scanning the complete drawing on every mouse move. use acadrust::{EntityType, Handle}; use rustc_hash::{FxHashMap, FxHashSet}; use crate::scene::convert::tess::entity_world_aabb_f64; use crate::scene::pick::quadtree::QuadTree; pub(super) struct ModifyEntityIndex { by_handle: FxHashMap, tree: Option, unbounded: Vec, } impl ModifyEntityIndex { pub(super) fn build(entities: &[EntityType]) -> Self { let mut by_handle = FxHashMap::default(); let mut bounded = Vec::new(); let mut unbounded = Vec::new(); let mut world = [ f64::INFINITY, f64::INFINITY, f64::NEG_INFINITY, f64::NEG_INFINITY, ]; for (index, entity) in entities.iter().enumerate() { let handle = entity.common().handle; by_handle.insert(handle, index); if let Some(aabb) = entity_world_aabb_f64(entity) { world[0] = world[0].min(aabb[0]); world[1] = world[1].min(aabb[1]); world[2] = world[2].max(aabb[2]); world[3] = world[3].max(aabb[3]); bounded.push((handle, aabb)); } else { unbounded.push(handle); } } let tree = if bounded.is_empty() { None } else { let span = (world[2] - world[0]).max(world[3] - world[1]).max(1.0); let pad = span * 1.0e-9 + 1.0e-6; let mut tree = QuadTree::new([ world[0] - pad, world[1] - pad, world[2] + pad, world[3] + pad, ]); for (handle, aabb) in bounded { tree.insert(handle, aabb); } Some(tree) }; Self { by_handle, tree, unbounded, } } #[inline] pub(super) fn get<'a>( &self, entities: &'a [EntityType], handle: Handle, ) -> Option<&'a EntityType> { self.by_handle .get(&handle) .and_then(|index| entities.get(*index)) } pub(super) fn nearby_handles( &self, entities: &[EntityType], handle: Handle, ) -> Option> { let entity = self.get(entities, handle)?; let aabb = entity_world_aabb_f64(entity)?; let span = (aabb[2] - aabb[0]).max(aabb[3] - aabb[1]).max(1.0); let pad = span * 1.0e-10 + 1.0e-7; let query = [aabb[0] - pad, aabb[1] - pad, aabb[2] + pad, aabb[3] + pad]; let mut handles: FxHashSet = self .tree .as_ref() .map(|tree| tree.query_rect(query).into_iter().collect()) .unwrap_or_default(); handles.extend(self.unbounded.iter().copied()); Some(handles) } }