perf: reuse interaction index after edits
Remap immutable base candidates and cache only changed-wire indexes so block-heavy drawings avoid rebuilding millions of primitives on pointer movement. Build 3D BVHs lazily for plan views.
This commit is contained in:
parent
da34c6817a
commit
1c7e4bb850
2 changed files with 353 additions and 53 deletions
174
src/scene/mod.rs
174
src/scene/mod.rs
|
|
@ -1042,6 +1042,18 @@ pub struct Scene {
|
||||||
Arc<crate::scene::pick::interaction_index::InteractionIndex>,
|
Arc<crate::scene::pick::interaction_index::InteractionIndex>,
|
||||||
)>,
|
)>,
|
||||||
>,
|
>,
|
||||||
|
/// Index of only the live handles changed since the immutable interaction
|
||||||
|
/// base. Reused across pointer events so a large unchanged block is never
|
||||||
|
/// re-indexed merely because one top-level entity was added or removed.
|
||||||
|
interaction_overlay_index_cache: RefCell<
|
||||||
|
Option<(
|
||||||
|
u64,
|
||||||
|
u64,
|
||||||
|
u64,
|
||||||
|
Arc<Vec<WireModel>>,
|
||||||
|
Arc<crate::scene::pick::interaction_index::InteractionIndex>,
|
||||||
|
)>,
|
||||||
|
>,
|
||||||
/// Auxiliary broad phase for objects that have no wire representation:
|
/// Auxiliary broad phase for objects that have no wire representation:
|
||||||
/// hatch-only and solid-only block instances.
|
/// hatch-only and solid-only block instances.
|
||||||
interaction_handle_index_cache: RefCell<
|
interaction_handle_index_cache: RefCell<
|
||||||
|
|
@ -1346,6 +1358,7 @@ impl Scene {
|
||||||
wire_cache: RefCell::new(None),
|
wire_cache: RefCell::new(None),
|
||||||
interaction_index_cache: RefCell::new(Vec::new()),
|
interaction_index_cache: RefCell::new(Vec::new()),
|
||||||
interaction_base_index_cache: RefCell::new(None),
|
interaction_base_index_cache: RefCell::new(None),
|
||||||
|
interaction_overlay_index_cache: RefCell::new(None),
|
||||||
interaction_handle_index_cache: RefCell::new(None),
|
interaction_handle_index_cache: RefCell::new(None),
|
||||||
interaction_aux_work_cache: std::cell::Cell::new(None),
|
interaction_aux_work_cache: std::cell::Cell::new(None),
|
||||||
sort_cache: RefCell::new(None),
|
sort_cache: RefCell::new(None),
|
||||||
|
|
@ -4851,7 +4864,16 @@ impl Scene {
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let started = iced::time::Instant::now();
|
||||||
let index = Arc::new(crate::scene::pick::interaction_index::InteractionIndex::build(wires));
|
let index = Arc::new(crate::scene::pick::interaction_index::InteractionIndex::build(wires));
|
||||||
|
let build_ms = started.elapsed().as_secs_f64() * 1000.0;
|
||||||
|
if std::env::var_os("OCS_PERF").is_some() && build_ms >= 50.0 {
|
||||||
|
eprintln!(
|
||||||
|
"[perf] interaction-index {:>7.1}ms wires={}",
|
||||||
|
build_ms,
|
||||||
|
wires.len(),
|
||||||
|
);
|
||||||
|
}
|
||||||
let mut cache = self.interaction_index_cache.borrow_mut();
|
let mut cache = self.interaction_index_cache.borrow_mut();
|
||||||
cache.push((
|
cache.push((
|
||||||
self.geometry_epoch,
|
self.geometry_epoch,
|
||||||
|
|
@ -4877,6 +4899,7 @@ impl Scene {
|
||||||
fn interaction_overlay_base(
|
fn interaction_overlay_base(
|
||||||
&self,
|
&self,
|
||||||
) -> Option<(
|
) -> Option<(
|
||||||
|
u64,
|
||||||
Arc<crate::scene::pick::interaction_index::InteractionIndex>,
|
Arc<crate::scene::pick::interaction_index::InteractionIndex>,
|
||||||
Vec<(Handle, ChangeKind)>,
|
Vec<(Handle, ChangeKind)>,
|
||||||
)> {
|
)> {
|
||||||
|
|
@ -4890,20 +4913,67 @@ impl Scene {
|
||||||
(*epoch, Arc::clone(index))
|
(*epoch, Arc::clone(index))
|
||||||
};
|
};
|
||||||
let changes = self.replay_since(epoch)?;
|
let changes = self.replay_since(epoch)?;
|
||||||
(changes.len() <= Self::INTERACTION_OVERLAY_MAX_HANDLES).then_some((index, changes))
|
(changes.len() <= Self::INTERACTION_OVERLAY_MAX_HANDLES)
|
||||||
|
.then_some((epoch, index, changes))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn interaction_overlay_changed_index(
|
||||||
|
&self,
|
||||||
|
base_epoch: u64,
|
||||||
|
changes: &[(Handle, ChangeKind)],
|
||||||
|
) -> (
|
||||||
|
Arc<Vec<WireModel>>,
|
||||||
|
Arc<crate::scene::pick::interaction_index::InteractionIndex>,
|
||||||
|
) {
|
||||||
|
let space_key = self.interaction_space_key();
|
||||||
|
{
|
||||||
|
let cache = self.interaction_overlay_index_cache.borrow();
|
||||||
|
if let Some((epoch, cached_base, cached_space, wires, index)) = cache.as_ref() {
|
||||||
|
if *epoch == self.geometry_epoch
|
||||||
|
&& *cached_base == base_epoch
|
||||||
|
&& *cached_space == space_key
|
||||||
|
{
|
||||||
|
return (Arc::clone(wires), Arc::clone(index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let changed_live: Vec<Handle> = changes
|
||||||
|
.iter()
|
||||||
|
.filter_map(|(handle, kind)| {
|
||||||
|
(!matches!(kind, ChangeKind::Removed)).then_some(*handle)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let wires = Arc::new(self.wire_models_for(&changed_live));
|
||||||
|
let index = Arc::new(
|
||||||
|
crate::scene::pick::interaction_index::InteractionIndex::build(&wires),
|
||||||
|
);
|
||||||
|
*self.interaction_overlay_index_cache.borrow_mut() = Some((
|
||||||
|
self.geometry_epoch,
|
||||||
|
base_epoch,
|
||||||
|
space_key,
|
||||||
|
Arc::clone(&wires),
|
||||||
|
Arc::clone(&index),
|
||||||
|
));
|
||||||
|
(wires, index)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn interaction_overlay_wires(
|
fn interaction_overlay_wires(
|
||||||
&self,
|
&self,
|
||||||
base_keys: impl IntoIterator<Item = (u64, u32)>,
|
base_keys: impl IntoIterator<Item = (u64, u32)>,
|
||||||
changes: &[(Handle, ChangeKind)],
|
changes: &[(Handle, ChangeKind)],
|
||||||
) -> Arc<Vec<WireModel>> {
|
changed_wires: &[WireModel],
|
||||||
|
) -> (
|
||||||
|
Arc<Vec<WireModel>>,
|
||||||
|
HashMap<(u64, u32), u32>,
|
||||||
|
HashMap<(u64, u32), u32>,
|
||||||
|
) {
|
||||||
let changed: HashSet<Handle> = changes.iter().map(|(handle, _)| *handle).collect();
|
let changed: HashSet<Handle> = changes.iter().map(|(handle, _)| *handle).collect();
|
||||||
let memo = self.resident_tess_memo.borrow();
|
let memo = self.resident_tess_memo.borrow();
|
||||||
let mut wires = Vec::new();
|
let mut wires = Vec::new();
|
||||||
|
let mut base_slots = HashMap::default();
|
||||||
let mut misses: HashMap<Handle, Vec<u32>> = HashMap::default();
|
let mut misses: HashMap<Handle, Vec<u32>> = HashMap::default();
|
||||||
for (handle, ordinal) in base_keys {
|
for (handle_value, ordinal) in base_keys {
|
||||||
let handle = Handle::new(handle);
|
let handle = Handle::new(handle_value);
|
||||||
if changed.contains(&handle) || self.document.get_entity(handle).is_none() {
|
if changed.contains(&handle) || self.document.get_entity(handle).is_none() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -4911,6 +4981,7 @@ impl Scene {
|
||||||
.get(&handle)
|
.get(&handle)
|
||||||
.and_then(|entity_wires| entity_wires.get(ordinal as usize))
|
.and_then(|entity_wires| entity_wires.get(ordinal as usize))
|
||||||
{
|
{
|
||||||
|
base_slots.insert((handle_value, ordinal), wires.len() as u32);
|
||||||
wires.push(wire.clone());
|
wires.push(wire.clone());
|
||||||
} else {
|
} else {
|
||||||
misses.entry(handle).or_default().push(ordinal);
|
misses.entry(handle).or_default().push(ordinal);
|
||||||
|
|
@ -4925,22 +4996,22 @@ impl Scene {
|
||||||
let entity_wires = self.wire_models_for(&[handle]);
|
let entity_wires = self.wire_models_for(&[handle]);
|
||||||
for ordinal in ordinals {
|
for ordinal in ordinals {
|
||||||
if let Some(wire) = entity_wires.get(ordinal as usize) {
|
if let Some(wire) = entity_wires.get(ordinal as usize) {
|
||||||
|
base_slots.insert((handle.value(), ordinal), wires.len() as u32);
|
||||||
wires.push(wire.clone());
|
wires.push(wire.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Changed handles are absent or stale in the immutable base index. The
|
let mut changed_slots = HashMap::default();
|
||||||
// journal is deliberately small, so append their current runs in full.
|
let mut next_ordinal: HashMap<u64, u32> = HashMap::default();
|
||||||
let changed_live: Vec<Handle> = changes
|
for wire in changed_wires {
|
||||||
.iter()
|
if let Ok(handle) = wire.name.parse::<u64>() {
|
||||||
.filter_map(|(handle, kind)| {
|
let ordinal = next_ordinal.entry(handle).or_default();
|
||||||
(!matches!(kind, ChangeKind::Removed)).then_some(*handle)
|
changed_slots.insert((handle, *ordinal), wires.len() as u32);
|
||||||
})
|
*ordinal += 1;
|
||||||
.collect();
|
|
||||||
if !changed_live.is_empty() {
|
|
||||||
wires.extend(self.wire_models_for(&changed_live));
|
|
||||||
}
|
}
|
||||||
Arc::new(wires)
|
wires.push(wire.clone());
|
||||||
|
}
|
||||||
|
(Arc::new(wires), base_slots, changed_slots)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn indexed_interaction_candidates_xy(
|
fn indexed_interaction_candidates_xy(
|
||||||
|
|
@ -4948,29 +5019,40 @@ impl Scene {
|
||||||
wires: Arc<Vec<WireModel>>,
|
wires: Arc<Vec<WireModel>>,
|
||||||
aabb: [f64; 4],
|
aabb: [f64; 4],
|
||||||
) -> crate::scene::pick::interaction_index::InteractionCandidates {
|
) -> crate::scene::pick::interaction_index::InteractionCandidates {
|
||||||
if let Some((base, changes)) = self.interaction_overlay_base() {
|
if let Some((base_epoch, base, changes)) = self.interaction_overlay_base() {
|
||||||
let perf = std::env::var_os("OCS_PERF").is_some();
|
let perf = std::env::var_os("OCS_PERF").is_some();
|
||||||
let t0 = iced::time::Instant::now();
|
let t0 = iced::time::Instant::now();
|
||||||
let keys = base.query_wire_keys_xy(aabb);
|
let keys = base.query_wire_keys_xy(aabb);
|
||||||
let key_ms = t0.elapsed().as_secs_f64() * 1000.0;
|
let key_ms = t0.elapsed().as_secs_f64() * 1000.0;
|
||||||
|
let t_changed = iced::time::Instant::now();
|
||||||
|
let (changed_wires, changed_index) =
|
||||||
|
self.interaction_overlay_changed_index(base_epoch, &changes);
|
||||||
|
let changed_ms = t_changed.elapsed().as_secs_f64() * 1000.0;
|
||||||
let t_local = iced::time::Instant::now();
|
let t_local = iced::time::Instant::now();
|
||||||
let local = self.interaction_overlay_wires(keys.iter().copied(), &changes);
|
let (local, base_slots, changed_slots) = self.interaction_overlay_wires(
|
||||||
|
keys.iter().copied(),
|
||||||
|
&changes,
|
||||||
|
&changed_wires,
|
||||||
|
);
|
||||||
let local_ms = t_local.elapsed().as_secs_f64() * 1000.0;
|
let local_ms = t_local.elapsed().as_secs_f64() * 1000.0;
|
||||||
let t_index = iced::time::Instant::now();
|
let t_remap = iced::time::Instant::now();
|
||||||
let local_index =
|
let mut result =
|
||||||
crate::scene::pick::interaction_index::InteractionIndex::build(&local);
|
base.query_remapped_xy(Arc::clone(&local), &base_slots, aabb);
|
||||||
let index_ms = t_index.elapsed().as_secs_f64() * 1000.0;
|
result.extend_indexed(
|
||||||
let result = local_index.query_xy(Arc::clone(&local), aabb);
|
changed_index.query_remapped_xy(local, &changed_slots, aabb),
|
||||||
|
);
|
||||||
|
let remap_ms = t_remap.elapsed().as_secs_f64() * 1000.0;
|
||||||
let total_ms = t0.elapsed().as_secs_f64() * 1000.0;
|
let total_ms = t0.elapsed().as_secs_f64() * 1000.0;
|
||||||
if perf && total_ms >= 50.0 {
|
if perf && total_ms >= 50.0 {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"[perf] interaction-overlay {:>7.1}ms keys={} wires={} query={:.1} gather={:.1} index={:.1}",
|
"[perf] interaction-overlay {:>7.1}ms keys={} wires={} query={:.1} changed={:.1} gather={:.1} remap={:.1}",
|
||||||
total_ms,
|
total_ms,
|
||||||
keys.len(),
|
keys.len(),
|
||||||
local.len(),
|
result.len(),
|
||||||
key_ms,
|
key_ms,
|
||||||
|
changed_ms,
|
||||||
local_ms,
|
local_ms,
|
||||||
index_ms,
|
remap_ms,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -4986,35 +5068,51 @@ impl Scene {
|
||||||
eye: glam::DVec3,
|
eye: glam::DVec3,
|
||||||
bounds: iced::Rectangle,
|
bounds: iced::Rectangle,
|
||||||
) -> crate::scene::pick::interaction_index::InteractionCandidates {
|
) -> crate::scene::pick::interaction_index::InteractionCandidates {
|
||||||
if let Some((base, changes)) = self.interaction_overlay_base() {
|
if let Some((base_epoch, base, changes)) = self.interaction_overlay_base() {
|
||||||
let perf = std::env::var_os("OCS_PERF").is_some();
|
let perf = std::env::var_os("OCS_PERF").is_some();
|
||||||
let t0 = iced::time::Instant::now();
|
let t0 = iced::time::Instant::now();
|
||||||
let keys = base.query_wire_keys_screen(screen_rect, view_rot, eye, bounds);
|
let keys = base.query_wire_keys_screen(screen_rect, view_rot, eye, bounds);
|
||||||
let key_ms = t0.elapsed().as_secs_f64() * 1000.0;
|
let key_ms = t0.elapsed().as_secs_f64() * 1000.0;
|
||||||
|
let t_changed = iced::time::Instant::now();
|
||||||
|
let (changed_wires, changed_index) =
|
||||||
|
self.interaction_overlay_changed_index(base_epoch, &changes);
|
||||||
|
let changed_ms = t_changed.elapsed().as_secs_f64() * 1000.0;
|
||||||
let t_local = iced::time::Instant::now();
|
let t_local = iced::time::Instant::now();
|
||||||
let local = self.interaction_overlay_wires(keys.iter().copied(), &changes);
|
let (local, base_slots, changed_slots) = self.interaction_overlay_wires(
|
||||||
|
keys.iter().copied(),
|
||||||
|
&changes,
|
||||||
|
&changed_wires,
|
||||||
|
);
|
||||||
let local_ms = t_local.elapsed().as_secs_f64() * 1000.0;
|
let local_ms = t_local.elapsed().as_secs_f64() * 1000.0;
|
||||||
let t_index = iced::time::Instant::now();
|
let t_remap = iced::time::Instant::now();
|
||||||
let local_index =
|
let mut result = base.query_remapped_screen(
|
||||||
crate::scene::pick::interaction_index::InteractionIndex::build(&local);
|
|
||||||
let index_ms = t_index.elapsed().as_secs_f64() * 1000.0;
|
|
||||||
let result = local_index.query_screen(
|
|
||||||
Arc::clone(&local),
|
Arc::clone(&local),
|
||||||
|
&base_slots,
|
||||||
screen_rect,
|
screen_rect,
|
||||||
view_rot,
|
view_rot,
|
||||||
eye,
|
eye,
|
||||||
bounds,
|
bounds,
|
||||||
);
|
);
|
||||||
|
result.extend_indexed(changed_index.query_remapped_screen(
|
||||||
|
local,
|
||||||
|
&changed_slots,
|
||||||
|
screen_rect,
|
||||||
|
view_rot,
|
||||||
|
eye,
|
||||||
|
bounds,
|
||||||
|
));
|
||||||
|
let remap_ms = t_remap.elapsed().as_secs_f64() * 1000.0;
|
||||||
let total_ms = t0.elapsed().as_secs_f64() * 1000.0;
|
let total_ms = t0.elapsed().as_secs_f64() * 1000.0;
|
||||||
if perf && total_ms >= 50.0 {
|
if perf && total_ms >= 50.0 {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"[perf] interaction-overlay {:>7.1}ms keys={} wires={} query={:.1} gather={:.1} index={:.1}",
|
"[perf] interaction-overlay {:>7.1}ms keys={} wires={} query={:.1} changed={:.1} gather={:.1} remap={:.1}",
|
||||||
total_ms,
|
total_ms,
|
||||||
keys.len(),
|
keys.len(),
|
||||||
local.len(),
|
result.len(),
|
||||||
key_ms,
|
key_ms,
|
||||||
|
changed_ms,
|
||||||
local_ms,
|
local_ms,
|
||||||
index_ms,
|
remap_ms,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -5028,8 +5126,10 @@ impl Scene {
|
||||||
wires: &Arc<Vec<WireModel>>,
|
wires: &Arc<Vec<WireModel>>,
|
||||||
base_radius_px: f32,
|
base_radius_px: f32,
|
||||||
) -> f32 {
|
) -> f32 {
|
||||||
if let Some((base, _)) = self.interaction_overlay_base() {
|
if let Some((base_epoch, base, changes)) = self.interaction_overlay_base() {
|
||||||
base.pick_radius_px(base_radius_px)
|
let (_, changed) =
|
||||||
|
self.interaction_overlay_changed_index(base_epoch, &changes);
|
||||||
|
base.pick_radius_px(changed.pick_radius_px(base_radius_px))
|
||||||
} else {
|
} else {
|
||||||
self.interaction_index(wires).pick_radius_px(base_radius_px)
|
self.interaction_index(wires).pick_radius_px(base_radius_px)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -163,28 +163,24 @@ struct BvhNode3 {
|
||||||
len: u32,
|
len: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SpatialBvh3<T> {
|
struct SpatialBvh3 {
|
||||||
entries: Vec<Entry3<T>>,
|
|
||||||
nodes: Vec<BvhNode3>,
|
nodes: Vec<BvhNode3>,
|
||||||
order: Vec<u32>,
|
order: Vec<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Copy + Ord> SpatialBvh3<T> {
|
impl SpatialBvh3 {
|
||||||
fn build(entries: Vec<Entry3<T>>) -> Self {
|
fn build<T>(entries: &[Entry3<T>]) -> Self {
|
||||||
let mut order: Vec<u32> = (0..entries.len() as u32).collect();
|
let mut order: Vec<u32> = (0..entries.len() as u32).collect();
|
||||||
let mut nodes = Vec::new();
|
let mut nodes = Vec::new();
|
||||||
if !order.is_empty() {
|
if !order.is_empty() {
|
||||||
build_bvh3_node(&entries, &mut order, 0, &mut nodes);
|
build_bvh3_node(entries, &mut order, 0, &mut nodes);
|
||||||
}
|
|
||||||
Self {
|
|
||||||
entries,
|
|
||||||
nodes,
|
|
||||||
order,
|
|
||||||
}
|
}
|
||||||
|
Self { nodes, order }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn query_screen(
|
fn query_screen<T: Copy + Ord>(
|
||||||
&self,
|
&self,
|
||||||
|
entries: &[Entry3<T>],
|
||||||
screen_rect: [f32; 4],
|
screen_rect: [f32; 4],
|
||||||
view_rot: Mat4,
|
view_rot: Mat4,
|
||||||
eye: DVec3,
|
eye: DVec3,
|
||||||
|
|
@ -203,7 +199,7 @@ impl<T: Copy + Ord> SpatialBvh3<T> {
|
||||||
if node.left == u32::MAX {
|
if node.left == u32::MAX {
|
||||||
for &entry_idx in &self.order[node.start as usize..(node.start + node.len) as usize]
|
for &entry_idx in &self.order[node.start as usize..(node.start + node.len) as usize]
|
||||||
{
|
{
|
||||||
let entry = self.entries[entry_idx as usize];
|
let entry = entries[entry_idx as usize];
|
||||||
if aabb3_projects_into(entry.aabb, screen_rect, view_rot, eye, bounds) {
|
if aabb3_projects_into(entry.aabb, screen_rect, view_rot, eye, bounds) {
|
||||||
entry_indices.push(entry.value);
|
entry_indices.push(entry.value);
|
||||||
}
|
}
|
||||||
|
|
@ -425,21 +421,23 @@ impl SpatialGrid {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SpatialSet<T> {
|
struct SpatialSet<T> {
|
||||||
|
entries: Vec<Entry3<T>>,
|
||||||
xy: SpatialGrid,
|
xy: SpatialGrid,
|
||||||
xyz: SpatialBvh3<T>,
|
xyz: std::sync::OnceLock<SpatialBvh3>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Copy + Ord> SpatialSet<T> {
|
impl<T: Copy + Ord> SpatialSet<T> {
|
||||||
fn build(entries: Vec<Entry3<T>>) -> Self {
|
fn build(entries: Vec<Entry3<T>>) -> Self {
|
||||||
let xy = SpatialGrid::build(&entries);
|
let xy = SpatialGrid::build(&entries);
|
||||||
Self {
|
Self {
|
||||||
|
entries,
|
||||||
xy,
|
xy,
|
||||||
xyz: SpatialBvh3::build(entries),
|
xyz: std::sync::OnceLock::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn query_xy(&self, aabb: [f64; 4]) -> Vec<T> {
|
fn query_xy(&self, aabb: [f64; 4]) -> Vec<T> {
|
||||||
self.xy.query(&self.xyz.entries, aabb)
|
self.xy.query(&self.entries, aabb)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn query_screen(
|
fn query_screen(
|
||||||
|
|
@ -449,7 +447,9 @@ impl<T: Copy + Ord> SpatialSet<T> {
|
||||||
eye: DVec3,
|
eye: DVec3,
|
||||||
bounds: Rectangle,
|
bounds: Rectangle,
|
||||||
) -> Vec<T> {
|
) -> Vec<T> {
|
||||||
self.xyz.query_screen(screen_rect, view_rot, eye, bounds)
|
self.xyz
|
||||||
|
.get_or_init(|| SpatialBvh3::build(&self.entries))
|
||||||
|
.query_screen(&self.entries, screen_rect, view_rot, eye, bounds)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -771,6 +771,184 @@ impl InteractionIndex {
|
||||||
self.queried_wire_keys(self.wires.query_screen(screen_rect, view_rot, eye, bounds))
|
self.queried_wire_keys(self.wires.query_screen(screen_rect, view_rot, eye, bounds))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn remap_wire_index(
|
||||||
|
&self,
|
||||||
|
index: u32,
|
||||||
|
slots: &rustc_hash::FxHashMap<(u64, u32), u32>,
|
||||||
|
) -> Option<u32> {
|
||||||
|
let handle = self
|
||||||
|
.wire_handles
|
||||||
|
.get(index as usize)
|
||||||
|
.copied()
|
||||||
|
.flatten()?;
|
||||||
|
let ordinal = self
|
||||||
|
.wire_ordinals
|
||||||
|
.get(index as usize)
|
||||||
|
.copied()
|
||||||
|
.flatten()?;
|
||||||
|
slots.get(&(handle, ordinal)).copied()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remap_wire_indices(
|
||||||
|
&self,
|
||||||
|
mut indices: Vec<u32>,
|
||||||
|
slots: &rustc_hash::FxHashMap<(u64, u32), u32>,
|
||||||
|
) -> Vec<u32> {
|
||||||
|
indices.extend_from_slice(&self.unbounded_wires);
|
||||||
|
let mut local: Vec<u32> = indices
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|index| self.remap_wire_index(index, slots))
|
||||||
|
.collect();
|
||||||
|
local.sort_unstable();
|
||||||
|
local.dedup();
|
||||||
|
local
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remap_refs<T>(
|
||||||
|
&self,
|
||||||
|
values: Vec<T>,
|
||||||
|
slots: &rustc_hash::FxHashMap<(u64, u32), u32>,
|
||||||
|
wire_of: impl Fn(&T) -> u32,
|
||||||
|
set_wire: impl Fn(&mut T, u32),
|
||||||
|
) -> Vec<T> {
|
||||||
|
values
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|mut value| {
|
||||||
|
let wire = self.remap_wire_index(wire_of(&value), slots)?;
|
||||||
|
set_wire(&mut value, wire);
|
||||||
|
Some(value)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn query_remapped_xy(
|
||||||
|
&self,
|
||||||
|
wires: Arc<Vec<WireModel>>,
|
||||||
|
slots: &rustc_hash::FxHashMap<(u64, u32), u32>,
|
||||||
|
aabb: [f64; 4],
|
||||||
|
) -> InteractionCandidates {
|
||||||
|
InteractionCandidates {
|
||||||
|
wires,
|
||||||
|
wire_indices: Some(self.remap_wire_indices(self.wires.query_xy(aabb), slots)),
|
||||||
|
segments: Some(self.remap_refs(
|
||||||
|
self.segments.query_xy(aabb),
|
||||||
|
slots,
|
||||||
|
|value| value.wire,
|
||||||
|
|value, wire| value.wire = wire,
|
||||||
|
)),
|
||||||
|
snap_points: Some(self.remap_refs(
|
||||||
|
self.snap_points.query_xy(aabb),
|
||||||
|
slots,
|
||||||
|
|value| value.wire,
|
||||||
|
|value, wire| value.wire = wire,
|
||||||
|
)),
|
||||||
|
key_vertices: Some(self.remap_refs(
|
||||||
|
self.key_vertices.query_xy(aabb),
|
||||||
|
slots,
|
||||||
|
|value| value.wire,
|
||||||
|
|value, wire| value.wire = wire,
|
||||||
|
)),
|
||||||
|
key_segments: Some(self.remap_refs(
|
||||||
|
self.key_segments.query_xy(aabb),
|
||||||
|
slots,
|
||||||
|
|value| value.wire,
|
||||||
|
|value, wire| value.wire = wire,
|
||||||
|
)),
|
||||||
|
fill_triangles: Some(self.remap_refs(
|
||||||
|
self.fill_triangles.query_xy(aabb),
|
||||||
|
slots,
|
||||||
|
|value| value.wire,
|
||||||
|
|value, wire| value.wire = wire,
|
||||||
|
)),
|
||||||
|
pick_triangles: Some(self.remap_refs(
|
||||||
|
self.pick_triangles.query_xy(aabb),
|
||||||
|
slots,
|
||||||
|
|value| value.wire,
|
||||||
|
|value, wire| value.wire = wire,
|
||||||
|
)),
|
||||||
|
glyphs: Some(self.remap_refs(
|
||||||
|
self.glyphs.query_xy(aabb),
|
||||||
|
slots,
|
||||||
|
|value| value.wire,
|
||||||
|
|value, wire| value.wire = wire,
|
||||||
|
)),
|
||||||
|
query_aabb: Some(aabb),
|
||||||
|
screen_rect: None,
|
||||||
|
screen_view: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn query_remapped_screen(
|
||||||
|
&self,
|
||||||
|
wires: Arc<Vec<WireModel>>,
|
||||||
|
slots: &rustc_hash::FxHashMap<(u64, u32), u32>,
|
||||||
|
screen_rect: [f32; 4],
|
||||||
|
view_rot: Mat4,
|
||||||
|
eye: DVec3,
|
||||||
|
bounds: Rectangle,
|
||||||
|
) -> InteractionCandidates {
|
||||||
|
InteractionCandidates {
|
||||||
|
wires,
|
||||||
|
wire_indices: Some(self.remap_wire_indices(
|
||||||
|
self.wires
|
||||||
|
.query_screen(screen_rect, view_rot, eye, bounds),
|
||||||
|
slots,
|
||||||
|
)),
|
||||||
|
segments: Some(self.remap_refs(
|
||||||
|
self.segments
|
||||||
|
.query_screen(screen_rect, view_rot, eye, bounds),
|
||||||
|
slots,
|
||||||
|
|value| value.wire,
|
||||||
|
|value, wire| value.wire = wire,
|
||||||
|
)),
|
||||||
|
snap_points: Some(self.remap_refs(
|
||||||
|
self.snap_points
|
||||||
|
.query_screen(screen_rect, view_rot, eye, bounds),
|
||||||
|
slots,
|
||||||
|
|value| value.wire,
|
||||||
|
|value, wire| value.wire = wire,
|
||||||
|
)),
|
||||||
|
key_vertices: Some(self.remap_refs(
|
||||||
|
self.key_vertices
|
||||||
|
.query_screen(screen_rect, view_rot, eye, bounds),
|
||||||
|
slots,
|
||||||
|
|value| value.wire,
|
||||||
|
|value, wire| value.wire = wire,
|
||||||
|
)),
|
||||||
|
key_segments: Some(self.remap_refs(
|
||||||
|
self.key_segments
|
||||||
|
.query_screen(screen_rect, view_rot, eye, bounds),
|
||||||
|
slots,
|
||||||
|
|value| value.wire,
|
||||||
|
|value, wire| value.wire = wire,
|
||||||
|
)),
|
||||||
|
fill_triangles: Some(self.remap_refs(
|
||||||
|
self.fill_triangles
|
||||||
|
.query_screen(screen_rect, view_rot, eye, bounds),
|
||||||
|
slots,
|
||||||
|
|value| value.wire,
|
||||||
|
|value, wire| value.wire = wire,
|
||||||
|
)),
|
||||||
|
pick_triangles: Some(self.remap_refs(
|
||||||
|
self.pick_triangles
|
||||||
|
.query_screen(screen_rect, view_rot, eye, bounds),
|
||||||
|
slots,
|
||||||
|
|value| value.wire,
|
||||||
|
|value, wire| value.wire = wire,
|
||||||
|
)),
|
||||||
|
glyphs: Some(self.remap_refs(
|
||||||
|
self.glyphs
|
||||||
|
.query_screen(screen_rect, view_rot, eye, bounds),
|
||||||
|
slots,
|
||||||
|
|value| value.wire,
|
||||||
|
|value, wire| value.wire = wire,
|
||||||
|
)),
|
||||||
|
query_aabb: None,
|
||||||
|
screen_rect: Some(screen_rect),
|
||||||
|
screen_view: Some((view_rot, eye, bounds)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn query_xy(&self, wires: Arc<Vec<WireModel>>, aabb: [f64; 4]) -> InteractionCandidates {
|
pub fn query_xy(&self, wires: Arc<Vec<WireModel>>, aabb: [f64; 4]) -> InteractionCandidates {
|
||||||
let mut wire_indices = self.wires.query_xy(aabb);
|
let mut wire_indices = self.wires.query_xy(aabb);
|
||||||
wire_indices.extend_from_slice(&self.unbounded_wires);
|
wire_indices.extend_from_slice(&self.unbounded_wires);
|
||||||
|
|
@ -970,6 +1148,28 @@ impl InteractionCandidates {
|
||||||
.zip(self.screen_view)
|
.zip(self.screen_view)
|
||||||
.map(|(rect, (view, eye, bounds))| (rect, view, eye, bounds))
|
.map(|(rect, (view, eye, bounds))| (rect, view, eye, bounds))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn extend_indexed(&mut self, other: Self) {
|
||||||
|
debug_assert!(Arc::ptr_eq(&self.wires, &other.wires));
|
||||||
|
|
||||||
|
fn extend<T: Ord>(target: &mut Option<Vec<T>>, incoming: Option<Vec<T>>) {
|
||||||
|
let (Some(target), Some(incoming)) = (target.as_mut(), incoming) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
target.extend(incoming);
|
||||||
|
target.sort_unstable();
|
||||||
|
target.dedup();
|
||||||
|
}
|
||||||
|
|
||||||
|
extend(&mut self.wire_indices, other.wire_indices);
|
||||||
|
extend(&mut self.segments, other.segments);
|
||||||
|
extend(&mut self.snap_points, other.snap_points);
|
||||||
|
extend(&mut self.key_vertices, other.key_vertices);
|
||||||
|
extend(&mut self.key_segments, other.key_segments);
|
||||||
|
extend(&mut self.fill_triangles, other.fill_triangles);
|
||||||
|
extend(&mut self.pick_triangles, other.pick_triangles);
|
||||||
|
extend(&mut self.glyphs, other.glyphs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WireSource for InteractionCandidates {
|
impl WireSource for InteractionCandidates {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue