Make a large selection scale with the drawing

Selecting a whole drawing was unusable on a 186 468-entity file. Four costs
scaled with the selection where they did not have to.

Every selected handle triggered a walk of the entire document to find LEADERs
pointing at it. On the file this came from, selecting 471 583 entities in a
585 786-entity drawing did not finish inside eight minutes. A reverse index
built once per geometry epoch answers the same question; a test asserts it
returns exactly what the walk returned, including the case of two leaders on
one annotation.

Group expansion asked the selection about every group: for each selected
handle it scanned each group's own member list. Asking each group once
whether it holds anything selected turns that around.

Selecting and deselecting went one entity at a time, and `deselect_entity`
rebuilt the ordered list with a `retain` per handle, so removing N of M
walked the order list N times. Both now take a slice.

Grips were built for every selected entity. AutoCAD stops at GRIPOBJLIMIT,
and the DWG header does not carry the variable, so its default of 100 stands
in for it.

The remaining two commits are measurement: `select-commit` reports the phases
of a selection separately, which is what showed the bulk paths had been added
to the press-drag branch while a click-move-click window goes through a
different one entirely.
This commit is contained in:
Panos committed 2026-09-09 10:29:24 +03:00
1 parent 56e09cd3f5
commit d02ce08349
5 files changed
+424 -42

No files matched your search

+10
View File
@@ -1877,6 +1877,13 @@ pub struct Scene {
entity_block_map_cache: RefCell<Option<(u64, HashMap<Handle, Handle>)>>,
/// Candidate handles per block, in document order and keyed by geometry epoch.
block_members_cache: RefCell<Option<BlockMembers>>,
/// Annotation handle -> the LEADERs that point at it, resolved once per
/// `geometry_epoch`.
///
/// Selecting an entity has to pull in a LEADER whose annotation it is, and
/// that used to be answered by walking the whole document — per selected
/// handle. Selecting N entities cost N document walks.
leaders_by_annotation_cache: RefCell<Option<(u64, HashMap<Handle, Vec<Handle>>)>>,
/// Insert/Viewport/Block/BlockEnd handles omitted by the spatial index.
unindexable_cache: RefCell<Option<(u64, Vec<Handle>)>>,
/// `(epoch, layout block, present type names, list exposed to the UI)`.
@@ -2128,6 +2135,7 @@ impl Scene {
model_extents_cache: RefCell::new(None),
entity_block_map_cache: RefCell::new(None),
block_members_cache: RefCell::new(None),
leaders_by_annotation_cache: RefCell::new(None),
unindexable_cache: RefCell::new(None),
layout_type_names_cache: RefCell::new(None),
dependency_index_cache: RefCell::new(None),
@@ -10797,6 +10805,8 @@ mod journal_tests {
assert_eq!(folded.len(), before.len() + 1);
}
// Differential oracle: the incrementally-patched entity index must always
// equal a from-scratch rebuild after any add / move / erase.
#[test]
fn block_member_index_matches_the_full_scan() {
use acadrust::entities::Line;