forked from 1b-agent/opencadstudio-upstream
100 lines
3.1 KiB
Rust
100 lines
3.1 KiB
Rust
//! 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<Handle, usize>,
|
|
tree: Option<QuadTree>,
|
|
unbounded: Vec<Handle>,
|
|
}
|
|
|
|
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<FxHashSet<Handle>> {
|
|
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<Handle> = self
|
|
.tree
|
|
.as_ref()
|
|
.map(|tree| tree.query_rect(query).into_iter().collect())
|
|
.unwrap_or_default();
|
|
handles.extend(self.unbounded.iter().copied());
|
|
Some(handles)
|
|
}
|
|
}
|