1use std::collections::HashSet;
14
15use crate::interaction::selection::NodeId;
16
17#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
36#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
37#[non_exhaustive]
38pub enum SubObjectRef {
39 Face(u32),
41 Vertex(u32),
43 Edge(u32),
47 Point(u32),
49 Voxel(u32),
55}
56
57impl SubObjectRef {
58 pub fn is_face(&self) -> bool {
60 matches!(self, Self::Face(_))
61 }
62
63 pub fn is_point(&self) -> bool {
65 matches!(self, Self::Point(_))
66 }
67
68 pub fn is_vertex(&self) -> bool {
70 matches!(self, Self::Vertex(_))
71 }
72
73 pub fn is_edge(&self) -> bool {
75 matches!(self, Self::Edge(_))
76 }
77
78 pub fn is_voxel(&self) -> bool {
80 matches!(self, Self::Voxel(_))
81 }
82
83 pub fn index(&self) -> u32 {
85 match *self {
86 Self::Face(i) | Self::Vertex(i) | Self::Edge(i) | Self::Point(i) | Self::Voxel(i) => i,
87 }
88 }
89
90 pub fn from_feature_id(f: parry3d::shape::FeatureId) -> Option<Self> {
94 match f {
95 parry3d::shape::FeatureId::Face(i) => Some(Self::Face(i)),
96 parry3d::shape::FeatureId::Vertex(i) => Some(Self::Vertex(i)),
97 parry3d::shape::FeatureId::Edge(i) => Some(Self::Edge(i)),
98 _ => None,
99 }
100 }
101}
102
103#[derive(Debug, Clone, Default)]
132pub struct SubSelection {
133 selected: HashSet<(NodeId, SubObjectRef)>,
134 primary: Option<(NodeId, SubObjectRef)>,
135 version: u64,
136}
137
138impl SubSelection {
139 pub fn new() -> Self {
141 Self::default()
142 }
143
144 pub fn version(&self) -> u64 {
149 self.version
150 }
151
152 pub fn select_one(&mut self, object_id: NodeId, sub: SubObjectRef) {
154 self.selected.clear();
155 self.selected.insert((object_id, sub));
156 self.primary = Some((object_id, sub));
157 self.version = self.version.wrapping_add(1);
158 }
159
160 pub fn toggle(&mut self, object_id: NodeId, sub: SubObjectRef) {
165 let key = (object_id, sub);
166 if self.selected.contains(&key) {
167 self.selected.remove(&key);
168 if self.primary == Some(key) {
169 self.primary = self.selected.iter().next().copied();
170 }
171 } else {
172 self.selected.insert(key);
173 self.primary = Some(key);
174 }
175 self.version = self.version.wrapping_add(1);
176 }
177
178 pub fn add(&mut self, object_id: NodeId, sub: SubObjectRef) {
180 self.selected.insert((object_id, sub));
181 self.primary = Some((object_id, sub));
182 self.version = self.version.wrapping_add(1);
183 }
184
185 pub fn remove(&mut self, object_id: NodeId, sub: SubObjectRef) {
187 let key = (object_id, sub);
188 self.selected.remove(&key);
189 if self.primary == Some(key) {
190 self.primary = self.selected.iter().next().copied();
191 }
192 self.version = self.version.wrapping_add(1);
193 }
194
195 pub fn clear(&mut self) {
197 self.selected.clear();
198 self.primary = None;
199 self.version = self.version.wrapping_add(1);
200 }
201
202 pub fn extend(&mut self, items: impl IntoIterator<Item = (NodeId, SubObjectRef)>) {
206 let mut last = None;
207 for item in items {
208 self.selected.insert(item);
209 last = Some(item);
210 }
211 if let Some(item) = last {
212 self.primary = Some(item);
213 }
214 self.version = self.version.wrapping_add(1);
215 }
216
217 pub fn extend_from_rect_pick(&mut self, result: &crate::interaction::picking::RectPickResult) {
222 for (&object_id, subs) in &result.hits {
223 for &sub in subs {
224 self.selected.insert((object_id, sub));
225 self.primary = Some((object_id, sub));
226 }
227 }
228 self.version = self.version.wrapping_add(1);
229 }
230
231 pub fn contains(&self, object_id: NodeId, sub: SubObjectRef) -> bool {
233 self.selected.contains(&(object_id, sub))
234 }
235
236 pub fn primary(&self) -> Option<(NodeId, SubObjectRef)> {
238 self.primary
239 }
240
241 pub fn iter(&self) -> impl Iterator<Item = &(NodeId, SubObjectRef)> {
243 self.selected.iter()
244 }
245
246 pub fn for_object(&self, object_id: NodeId) -> impl Iterator<Item = SubObjectRef> + '_ {
248 self.selected
249 .iter()
250 .filter(move |(id, _)| *id == object_id)
251 .map(|(_, sub)| *sub)
252 }
253
254 pub fn len(&self) -> usize {
256 self.selected.len()
257 }
258
259 pub fn is_empty(&self) -> bool {
261 self.selected.is_empty()
262 }
263
264 pub fn face_count(&self) -> usize {
266 self.selected.iter().filter(|(_, s)| s.is_face()).count()
267 }
268
269 pub fn point_count(&self) -> usize {
271 self.selected.iter().filter(|(_, s)| s.is_point()).count()
272 }
273
274 pub fn vertex_count(&self) -> usize {
276 self.selected.iter().filter(|(_, s)| s.is_vertex()).count()
277 }
278
279 pub fn voxel_count(&self) -> usize {
281 self.selected.iter().filter(|(_, s)| s.is_voxel()).count()
282 }
283}
284
285pub struct VolumeSelectionInfo {
294 pub dims: [u32; 3],
296 pub bbox_min: [f32; 3],
298 pub bbox_max: [f32; 3],
300 pub model: [[f32; 4]; 4],
302}
303
304pub struct SubSelectionRef {
321 pub(crate) items: Vec<(NodeId, SubObjectRef)>,
323 pub(crate) mesh_lookup:
330 std::collections::HashMap<u64, (Vec<[f32; 3]>, Vec<u32>)>,
331 pub(crate) model_matrices: std::collections::HashMap<u64, glam::Mat4>,
337 pub(crate) point_positions: std::collections::HashMap<u64, Vec<[f32; 3]>>,
342 pub(crate) voxel_lookup: std::collections::HashMap<u64, VolumeSelectionInfo>,
348 pub version: u64,
353}
354
355impl SubSelectionRef {
356 pub fn new(
364 sub_selection: &SubSelection,
365 mesh_lookup: std::collections::HashMap<u64, (Vec<[f32; 3]>, Vec<u32>)>,
366 model_matrices: std::collections::HashMap<u64, glam::Mat4>,
367 point_positions: std::collections::HashMap<u64, Vec<[f32; 3]>>,
368 ) -> Self {
369 Self {
370 items: sub_selection
371 .iter()
372 .map(|(n, s)| (*n, *s))
373 .collect(),
374 mesh_lookup,
375 model_matrices,
376 point_positions,
377 voxel_lookup: std::collections::HashMap::new(),
378 version: sub_selection.version(),
379 }
380 }
381
382 pub fn with_voxels(
387 mut self,
388 lookup: std::collections::HashMap<u64, VolumeSelectionInfo>,
389 ) -> Self {
390 self.voxel_lookup = lookup;
391 self
392 }
393
394 pub fn is_empty(&self) -> bool {
396 self.items.is_empty()
397 }
398}
399
400#[cfg(test)]
405mod tests {
406 use super::*;
407 use crate::interaction::picking::RectPickResult;
408
409 #[test]
412 fn sub_object_ref_kind_checks() {
413 assert!(SubObjectRef::Face(0).is_face());
414 assert!(!SubObjectRef::Face(0).is_point());
415 assert!(!SubObjectRef::Face(0).is_vertex());
416 assert!(!SubObjectRef::Face(0).is_edge());
417
418 assert!(SubObjectRef::Point(1).is_point());
419 assert!(SubObjectRef::Vertex(2).is_vertex());
420 assert!(SubObjectRef::Edge(3).is_edge());
421 }
422
423 #[test]
424 fn sub_object_ref_index() {
425 assert_eq!(SubObjectRef::Face(7).index(), 7);
426 assert_eq!(SubObjectRef::Vertex(42).index(), 42);
427 assert_eq!(SubObjectRef::Edge(0).index(), 0);
428 assert_eq!(SubObjectRef::Point(99).index(), 99);
429 }
430
431 #[test]
432 fn sub_object_ref_from_feature_id() {
433 use parry3d::shape::FeatureId;
434 assert_eq!(
435 SubObjectRef::from_feature_id(FeatureId::Face(3)),
436 Some(SubObjectRef::Face(3))
437 );
438 assert_eq!(
439 SubObjectRef::from_feature_id(FeatureId::Vertex(1)),
440 Some(SubObjectRef::Vertex(1))
441 );
442 assert_eq!(
443 SubObjectRef::from_feature_id(FeatureId::Edge(2)),
444 Some(SubObjectRef::Edge(2))
445 );
446 assert_eq!(SubObjectRef::from_feature_id(FeatureId::Unknown), None);
447 }
448
449 #[test]
450 fn sub_object_ref_hashable() {
451 let mut set = std::collections::HashSet::new();
452 set.insert(SubObjectRef::Face(0));
453 set.insert(SubObjectRef::Face(0)); set.insert(SubObjectRef::Face(1));
455 set.insert(SubObjectRef::Point(0)); assert_eq!(set.len(), 3);
457 }
458
459 #[test]
462 fn sub_selection_select_one_clears_others() {
463 let mut sel = SubSelection::new();
464 sel.add(1, SubObjectRef::Face(0));
465 sel.add(1, SubObjectRef::Face(1));
466 sel.select_one(1, SubObjectRef::Face(5));
467 assert_eq!(sel.len(), 1);
468 assert!(sel.contains(1, SubObjectRef::Face(5)));
469 assert!(!sel.contains(1, SubObjectRef::Face(0)));
470 }
471
472 #[test]
473 fn sub_selection_toggle() {
474 let mut sel = SubSelection::new();
475 sel.toggle(1, SubObjectRef::Face(0));
476 assert!(sel.contains(1, SubObjectRef::Face(0)));
477 sel.toggle(1, SubObjectRef::Face(0));
478 assert!(!sel.contains(1, SubObjectRef::Face(0)));
479 assert!(sel.is_empty());
480 }
481
482 #[test]
483 fn sub_selection_add_preserves_others() {
484 let mut sel = SubSelection::new();
485 sel.add(1, SubObjectRef::Face(0));
486 sel.add(1, SubObjectRef::Face(1));
487 assert_eq!(sel.len(), 2);
488 assert!(sel.contains(1, SubObjectRef::Face(0)));
489 assert!(sel.contains(1, SubObjectRef::Face(1)));
490 }
491
492 #[test]
493 fn sub_selection_remove() {
494 let mut sel = SubSelection::new();
495 sel.add(1, SubObjectRef::Face(0));
496 sel.add(1, SubObjectRef::Face(1));
497 sel.remove(1, SubObjectRef::Face(0));
498 assert!(!sel.contains(1, SubObjectRef::Face(0)));
499 assert_eq!(sel.len(), 1);
500 }
501
502 #[test]
503 fn sub_selection_clear() {
504 let mut sel = SubSelection::new();
505 sel.add(1, SubObjectRef::Face(0));
506 sel.add(2, SubObjectRef::Point(3));
507 sel.clear();
508 assert!(sel.is_empty());
509 assert_eq!(sel.primary(), None);
510 }
511
512 #[test]
513 fn sub_selection_primary_tracks_last() {
514 let mut sel = SubSelection::new();
515 sel.add(1, SubObjectRef::Face(0));
516 assert_eq!(sel.primary(), Some((1, SubObjectRef::Face(0))));
517 sel.add(2, SubObjectRef::Point(5));
518 assert_eq!(sel.primary(), Some((2, SubObjectRef::Point(5))));
519 }
520
521 #[test]
522 fn sub_selection_contains() {
523 let mut sel = SubSelection::new();
524 sel.add(10, SubObjectRef::Face(3));
525 assert!(sel.contains(10, SubObjectRef::Face(3)));
526 assert!(!sel.contains(10, SubObjectRef::Face(4)));
527 assert!(!sel.contains(99, SubObjectRef::Face(3)));
528 }
529
530 #[test]
531 fn sub_selection_for_object() {
532 let mut sel = SubSelection::new();
533 sel.add(1, SubObjectRef::Face(0));
534 sel.add(1, SubObjectRef::Face(1));
535 sel.add(2, SubObjectRef::Face(0));
536 let obj1: Vec<SubObjectRef> = {
537 let mut v: Vec<_> = sel.for_object(1).collect();
538 v.sort_by_key(|s| s.index());
539 v
540 };
541 assert_eq!(obj1, vec![SubObjectRef::Face(0), SubObjectRef::Face(1)]);
542 let obj2: Vec<SubObjectRef> = sel.for_object(2).collect();
543 assert_eq!(obj2, vec![SubObjectRef::Face(0)]);
544 assert_eq!(sel.for_object(99).count(), 0);
545 }
546
547 #[test]
548 fn sub_selection_version_increments() {
549 let mut sel = SubSelection::new();
550 let v0 = sel.version();
551 sel.add(1, SubObjectRef::Face(0));
552 assert!(sel.version() > v0);
553 let v1 = sel.version();
554 sel.clear();
555 assert!(sel.version() > v1);
556 }
557
558 #[test]
559 fn sub_selection_kind_counts() {
560 let mut sel = SubSelection::new();
561 sel.add(1, SubObjectRef::Face(0));
562 sel.add(1, SubObjectRef::Face(1));
563 sel.add(2, SubObjectRef::Point(0));
564 sel.add(3, SubObjectRef::Vertex(0));
565 assert_eq!(sel.face_count(), 2);
566 assert_eq!(sel.point_count(), 1);
567 assert_eq!(sel.vertex_count(), 1);
568 }
569
570 #[test]
571 fn sub_selection_extend() {
572 let mut sel = SubSelection::new();
573 sel.extend([
574 (1, SubObjectRef::Face(0)),
575 (1, SubObjectRef::Face(1)),
576 (2, SubObjectRef::Point(3)),
577 ]);
578 assert_eq!(sel.len(), 3);
579 assert_eq!(sel.primary(), Some((2, SubObjectRef::Point(3))));
580 }
581
582 #[test]
583 fn sub_selection_extend_from_rect_pick() {
584 let mut result = RectPickResult::default();
585 result
586 .hits
587 .insert(10, vec![SubObjectRef::Face(0), SubObjectRef::Face(1)]);
588 result.hits.insert(20, vec![SubObjectRef::Point(5)]);
589
590 let mut sel = SubSelection::new();
591 sel.extend_from_rect_pick(&result);
592
593 assert_eq!(sel.len(), 3);
594 assert!(sel.contains(10, SubObjectRef::Face(0)));
595 assert!(sel.contains(10, SubObjectRef::Face(1)));
596 assert!(sel.contains(20, SubObjectRef::Point(5)));
597 assert_eq!(sel.face_count(), 2);
598 assert_eq!(sel.point_count(), 1);
599 }
600}