diff --git a/Cargo.lock b/Cargo.lock index 57e01ab6..86a9e334 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -878,7 +878,7 @@ checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" [[package]] name = "cadkernel" version = "0.1.0" -source = "git+https://github.com/HakanSeven12/cadkernel.git?rev=eda1263#eda1263da23c1b18e7593151a277a0593bb317ef" +source = "git+https://github.com/HakanSeven12/cadkernel.git?rev=3d5de36#3d5de36cb5b391f17da924c5f80e34a0cdc1d948" dependencies = [ "acadrust", "cavalier_contours", diff --git a/Cargo.toml b/Cargo.toml index c9f65c2d..3aaed7a5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,7 @@ rfd = "0.17" clap = { version = "4", features = ["derive"] } env_logger = "0.11" acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "0975677", features = ["serde"] } -cadkernel = { git = "https://github.com/HakanSeven12/cadkernel.git", rev = "eda1263", features = ["acis", "offset"] } +cadkernel = { git = "https://github.com/HakanSeven12/cadkernel.git", rev = "3d5de36", features = ["acis", "offset"] } dwg-thumbnailer = { path = "crates/dwg-thumbnailer" } flate2 = "1" image = { version = "0.25", default-features = false, features = ["png", "jpeg", "bmp", "tiff"] } diff --git a/src/app/model_ops.rs b/src/app/model_ops.rs index c4ba816f..2c67604a 100644 --- a/src/app/model_ops.rs +++ b/src/app/model_ops.rs @@ -43,9 +43,8 @@ impl super::OpenCADStudio { // Selected entities that have a cached B-rep. let handles: Vec = self.tabs[i] .scene - .selected - .iter() - .copied() + .selected_handles_in_order() + .into_iter() .filter(|h| !self.tabs[i].scene.is_layer_locked(*h)) .filter(|h| self.tabs[i].scene.solid_models.contains_key(h)) .collect(); diff --git a/src/scene/mod.rs b/src/scene/mod.rs index ad546ffb..64e371ae 100644 --- a/src/scene/mod.rs +++ b/src/scene/mod.rs @@ -1422,6 +1422,7 @@ pub struct Scene { lighting_cache: RefCell>>, /// Currently selected entity handles. pub selected: HashSet, + selected_order: Vec, /// Session-only ISOLATEOBJECTS / HIDEOBJECTS state. Never written to DWG/DXF. pub object_isolation: ObjectIsolationState, /// Entity handles temporarily removed from the base render while an @@ -1841,6 +1842,7 @@ impl Scene { object_data_cache: crate::entities::object_data::ObjectDataCache::default(), lighting_cache: RefCell::new(HashMap::default()), selected: HashSet::default(), + selected_order: Vec::new(), object_isolation: ObjectIsolationState::default(), preview_hidden: HashSet::default(), command_preview_hidden: HashSet::default(), @@ -4377,6 +4379,7 @@ impl Scene { .hidden .extend(self.selected.iter().copied()); self.selected.clear(); + self.selected_order.clear(); self.bump_entities(&changes); } diff --git a/src/scene/selection.rs b/src/scene/selection.rs index 8c1af2d3..b58d85e2 100644 --- a/src/scene/selection.rs +++ b/src/scene/selection.rs @@ -40,17 +40,42 @@ impl Scene { if exclusive { self.selected.clear(); + self.selected_order.clear(); } - self.selected.extend(handles); + for handle in handles { + if self.selected.insert(handle) { + self.selected_order.push(handle); + } + } self.bump_selection(); } pub fn deselect_all(&mut self) { self.selected.clear(); + self.selected_order.clear(); self.bump_selection(); } + pub(crate) fn selected_handles_in_order(&self) -> Vec { + let mut seen = HashSet::default(); + let mut ordered: Vec<_> = self + .selected_order + .iter() + .copied() + .filter(|handle| self.selected.contains(handle) && seen.insert(*handle)) + .collect(); + let mut missing: Vec<_> = self + .selected + .iter() + .copied() + .filter(|handle| seen.insert(*handle)) + .collect(); + missing.sort_unstable_by_key(|handle| handle.value()); + ordered.extend(missing); + ordered + } + /// Replace the complete selection and invalidate the GPU highlight overlay /// only when its contents actually changed. History/file/command paths must /// use this instead of assigning `selected` directly. @@ -62,7 +87,22 @@ impl Scene { .collect(); if self.selected != selected { + let mut seen = HashSet::default(); + let mut order: Vec<_> = self + .selected_order + .iter() + .copied() + .filter(|handle| selected.contains(handle) && seen.insert(*handle)) + .collect(); + let mut added: Vec<_> = selected + .iter() + .copied() + .filter(|handle| seen.insert(*handle)) + .collect(); + added.sort_unstable_by_key(|handle| handle.value()); + order.extend(added); self.selected = selected; + self.selected_order = order; self.bump_selection(); } } @@ -74,6 +114,7 @@ impl Scene { for handle in handles { changed |= self.selected.remove(&handle); + self.selected_order.retain(|selected| *selected != handle); } if changed { @@ -82,9 +123,9 @@ impl Scene { } pub fn selected_entities(&self) -> Vec<(Handle, &EntityType)> { - self.selected - .iter() - .filter_map(|&h| self.document.get_entity(h).map(|e| (h, e))) + self.selected_handles_in_order() + .into_iter() + .filter_map(|h| self.document.get_entity(h).map(|e| (h, e))) .collect() } @@ -107,7 +148,7 @@ impl Scene { match scope { crate::app::QSelectScope::CurrentSpace => self.current_layout_entity_handles(), crate::app::QSelectScope::CurrentSelection => { - self.selected.iter().copied().collect() + self.selected_handles_in_order() } } } @@ -152,6 +193,7 @@ impl Scene { let key = (entity_type_name(e), e.as_entity().layer().to_string()); if pairs.contains(&key) { self.selected.insert(h); + self.selected_order.push(h); added += 1; } } @@ -174,9 +216,11 @@ impl Scene { .filter_map(|w| Self::handle_from_wire_name(&w.name)) .collect(); self.selected.clear(); + self.selected_order.clear(); for h in all { if !prev.contains(&h) { self.selected.insert(h); + self.selected_order.push(h); } } self.bump_selection(); @@ -679,6 +723,7 @@ impl Scene { self.remember_removed_cache_categories(h); self.document.remove_entity_arc(h); highlight_changed |= self.selected.remove(&h); + self.selected_order.retain(|selected| *selected != h); if self.hover_highlight == Some(h) { self.hover_highlight = None; highlight_changed = true;