viewport_lib/interaction/
sub_object.rs1use 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}
50
51impl SubObjectRef {
52 pub fn is_face(&self) -> bool {
54 matches!(self, Self::Face(_))
55 }
56
57 pub fn is_point(&self) -> bool {
59 matches!(self, Self::Point(_))
60 }
61
62 pub fn is_vertex(&self) -> bool {
64 matches!(self, Self::Vertex(_))
65 }
66
67 pub fn is_edge(&self) -> bool {
69 matches!(self, Self::Edge(_))
70 }
71
72 pub fn index(&self) -> u32 {
74 match *self {
75 Self::Face(i) | Self::Vertex(i) | Self::Edge(i) | Self::Point(i) => i,
76 }
77 }
78
79 pub fn from_feature_id(f: parry3d::shape::FeatureId) -> Option<Self> {
83 match f {
84 parry3d::shape::FeatureId::Face(i) => Some(Self::Face(i)),
85 parry3d::shape::FeatureId::Vertex(i) => Some(Self::Vertex(i)),
86 parry3d::shape::FeatureId::Edge(i) => Some(Self::Edge(i)),
87 _ => None,
88 }
89 }
90}
91
92#[derive(Debug, Clone, Default)]
121pub struct SubSelection {
122 selected: HashSet<(NodeId, SubObjectRef)>,
123 primary: Option<(NodeId, SubObjectRef)>,
124 version: u64,
125}
126
127impl SubSelection {
128 pub fn new() -> Self {
130 Self::default()
131 }
132
133 pub fn version(&self) -> u64 {
138 self.version
139 }
140
141 pub fn select_one(&mut self, object_id: NodeId, sub: SubObjectRef) {
143 self.selected.clear();
144 self.selected.insert((object_id, sub));
145 self.primary = Some((object_id, sub));
146 self.version = self.version.wrapping_add(1);
147 }
148
149 pub fn toggle(&mut self, object_id: NodeId, sub: SubObjectRef) {
154 let key = (object_id, sub);
155 if self.selected.contains(&key) {
156 self.selected.remove(&key);
157 if self.primary == Some(key) {
158 self.primary = self.selected.iter().next().copied();
159 }
160 } else {
161 self.selected.insert(key);
162 self.primary = Some(key);
163 }
164 self.version = self.version.wrapping_add(1);
165 }
166
167 pub fn add(&mut self, object_id: NodeId, sub: SubObjectRef) {
169 self.selected.insert((object_id, sub));
170 self.primary = Some((object_id, sub));
171 self.version = self.version.wrapping_add(1);
172 }
173
174 pub fn remove(&mut self, object_id: NodeId, sub: SubObjectRef) {
176 let key = (object_id, sub);
177 self.selected.remove(&key);
178 if self.primary == Some(key) {
179 self.primary = self.selected.iter().next().copied();
180 }
181 self.version = self.version.wrapping_add(1);
182 }
183
184 pub fn clear(&mut self) {
186 self.selected.clear();
187 self.primary = None;
188 self.version = self.version.wrapping_add(1);
189 }
190
191 pub fn extend(&mut self, items: impl IntoIterator<Item = (NodeId, SubObjectRef)>) {
195 let mut last = None;
196 for item in items {
197 self.selected.insert(item);
198 last = Some(item);
199 }
200 if let Some(item) = last {
201 self.primary = Some(item);
202 }
203 self.version = self.version.wrapping_add(1);
204 }
205
206 pub fn extend_from_rect_pick(&mut self, result: &crate::interaction::picking::RectPickResult) {
211 for (&object_id, subs) in &result.hits {
212 for &sub in subs {
213 self.selected.insert((object_id, sub));
214 self.primary = Some((object_id, sub));
215 }
216 }
217 self.version = self.version.wrapping_add(1);
218 }
219
220 pub fn contains(&self, object_id: NodeId, sub: SubObjectRef) -> bool {
222 self.selected.contains(&(object_id, sub))
223 }
224
225 pub fn primary(&self) -> Option<(NodeId, SubObjectRef)> {
227 self.primary
228 }
229
230 pub fn iter(&self) -> impl Iterator<Item = &(NodeId, SubObjectRef)> {
232 self.selected.iter()
233 }
234
235 pub fn for_object(&self, object_id: NodeId) -> impl Iterator<Item = SubObjectRef> + '_ {
237 self.selected
238 .iter()
239 .filter(move |(id, _)| *id == object_id)
240 .map(|(_, sub)| *sub)
241 }
242
243 pub fn len(&self) -> usize {
245 self.selected.len()
246 }
247
248 pub fn is_empty(&self) -> bool {
250 self.selected.is_empty()
251 }
252
253 pub fn face_count(&self) -> usize {
255 self.selected.iter().filter(|(_, s)| s.is_face()).count()
256 }
257
258 pub fn point_count(&self) -> usize {
260 self.selected.iter().filter(|(_, s)| s.is_point()).count()
261 }
262
263 pub fn vertex_count(&self) -> usize {
265 self.selected.iter().filter(|(_, s)| s.is_vertex()).count()
266 }
267}
268
269#[cfg(test)]
274mod tests {
275 use super::*;
276 use crate::interaction::picking::RectPickResult;
277
278 #[test]
281 fn sub_object_ref_kind_checks() {
282 assert!(SubObjectRef::Face(0).is_face());
283 assert!(!SubObjectRef::Face(0).is_point());
284 assert!(!SubObjectRef::Face(0).is_vertex());
285 assert!(!SubObjectRef::Face(0).is_edge());
286
287 assert!(SubObjectRef::Point(1).is_point());
288 assert!(SubObjectRef::Vertex(2).is_vertex());
289 assert!(SubObjectRef::Edge(3).is_edge());
290 }
291
292 #[test]
293 fn sub_object_ref_index() {
294 assert_eq!(SubObjectRef::Face(7).index(), 7);
295 assert_eq!(SubObjectRef::Vertex(42).index(), 42);
296 assert_eq!(SubObjectRef::Edge(0).index(), 0);
297 assert_eq!(SubObjectRef::Point(99).index(), 99);
298 }
299
300 #[test]
301 fn sub_object_ref_from_feature_id() {
302 use parry3d::shape::FeatureId;
303 assert_eq!(
304 SubObjectRef::from_feature_id(FeatureId::Face(3)),
305 Some(SubObjectRef::Face(3))
306 );
307 assert_eq!(
308 SubObjectRef::from_feature_id(FeatureId::Vertex(1)),
309 Some(SubObjectRef::Vertex(1))
310 );
311 assert_eq!(
312 SubObjectRef::from_feature_id(FeatureId::Edge(2)),
313 Some(SubObjectRef::Edge(2))
314 );
315 assert_eq!(SubObjectRef::from_feature_id(FeatureId::Unknown), None);
316 }
317
318 #[test]
319 fn sub_object_ref_hashable() {
320 let mut set = std::collections::HashSet::new();
321 set.insert(SubObjectRef::Face(0));
322 set.insert(SubObjectRef::Face(0)); set.insert(SubObjectRef::Face(1));
324 set.insert(SubObjectRef::Point(0)); assert_eq!(set.len(), 3);
326 }
327
328 #[test]
331 fn sub_selection_select_one_clears_others() {
332 let mut sel = SubSelection::new();
333 sel.add(1, SubObjectRef::Face(0));
334 sel.add(1, SubObjectRef::Face(1));
335 sel.select_one(1, SubObjectRef::Face(5));
336 assert_eq!(sel.len(), 1);
337 assert!(sel.contains(1, SubObjectRef::Face(5)));
338 assert!(!sel.contains(1, SubObjectRef::Face(0)));
339 }
340
341 #[test]
342 fn sub_selection_toggle() {
343 let mut sel = SubSelection::new();
344 sel.toggle(1, SubObjectRef::Face(0));
345 assert!(sel.contains(1, SubObjectRef::Face(0)));
346 sel.toggle(1, SubObjectRef::Face(0));
347 assert!(!sel.contains(1, SubObjectRef::Face(0)));
348 assert!(sel.is_empty());
349 }
350
351 #[test]
352 fn sub_selection_add_preserves_others() {
353 let mut sel = SubSelection::new();
354 sel.add(1, SubObjectRef::Face(0));
355 sel.add(1, SubObjectRef::Face(1));
356 assert_eq!(sel.len(), 2);
357 assert!(sel.contains(1, SubObjectRef::Face(0)));
358 assert!(sel.contains(1, SubObjectRef::Face(1)));
359 }
360
361 #[test]
362 fn sub_selection_remove() {
363 let mut sel = SubSelection::new();
364 sel.add(1, SubObjectRef::Face(0));
365 sel.add(1, SubObjectRef::Face(1));
366 sel.remove(1, SubObjectRef::Face(0));
367 assert!(!sel.contains(1, SubObjectRef::Face(0)));
368 assert_eq!(sel.len(), 1);
369 }
370
371 #[test]
372 fn sub_selection_clear() {
373 let mut sel = SubSelection::new();
374 sel.add(1, SubObjectRef::Face(0));
375 sel.add(2, SubObjectRef::Point(3));
376 sel.clear();
377 assert!(sel.is_empty());
378 assert_eq!(sel.primary(), None);
379 }
380
381 #[test]
382 fn sub_selection_primary_tracks_last() {
383 let mut sel = SubSelection::new();
384 sel.add(1, SubObjectRef::Face(0));
385 assert_eq!(sel.primary(), Some((1, SubObjectRef::Face(0))));
386 sel.add(2, SubObjectRef::Point(5));
387 assert_eq!(sel.primary(), Some((2, SubObjectRef::Point(5))));
388 }
389
390 #[test]
391 fn sub_selection_contains() {
392 let mut sel = SubSelection::new();
393 sel.add(10, SubObjectRef::Face(3));
394 assert!(sel.contains(10, SubObjectRef::Face(3)));
395 assert!(!sel.contains(10, SubObjectRef::Face(4)));
396 assert!(!sel.contains(99, SubObjectRef::Face(3)));
397 }
398
399 #[test]
400 fn sub_selection_for_object() {
401 let mut sel = SubSelection::new();
402 sel.add(1, SubObjectRef::Face(0));
403 sel.add(1, SubObjectRef::Face(1));
404 sel.add(2, SubObjectRef::Face(0));
405 let obj1: Vec<SubObjectRef> = {
406 let mut v: Vec<_> = sel.for_object(1).collect();
407 v.sort_by_key(|s| s.index());
408 v
409 };
410 assert_eq!(obj1, vec![SubObjectRef::Face(0), SubObjectRef::Face(1)]);
411 let obj2: Vec<SubObjectRef> = sel.for_object(2).collect();
412 assert_eq!(obj2, vec![SubObjectRef::Face(0)]);
413 assert_eq!(sel.for_object(99).count(), 0);
414 }
415
416 #[test]
417 fn sub_selection_version_increments() {
418 let mut sel = SubSelection::new();
419 let v0 = sel.version();
420 sel.add(1, SubObjectRef::Face(0));
421 assert!(sel.version() > v0);
422 let v1 = sel.version();
423 sel.clear();
424 assert!(sel.version() > v1);
425 }
426
427 #[test]
428 fn sub_selection_kind_counts() {
429 let mut sel = SubSelection::new();
430 sel.add(1, SubObjectRef::Face(0));
431 sel.add(1, SubObjectRef::Face(1));
432 sel.add(2, SubObjectRef::Point(0));
433 sel.add(3, SubObjectRef::Vertex(0));
434 assert_eq!(sel.face_count(), 2);
435 assert_eq!(sel.point_count(), 1);
436 assert_eq!(sel.vertex_count(), 1);
437 }
438
439 #[test]
440 fn sub_selection_extend() {
441 let mut sel = SubSelection::new();
442 sel.extend([
443 (1, SubObjectRef::Face(0)),
444 (1, SubObjectRef::Face(1)),
445 (2, SubObjectRef::Point(3)),
446 ]);
447 assert_eq!(sel.len(), 3);
448 assert_eq!(sel.primary(), Some((2, SubObjectRef::Point(3))));
449 }
450
451 #[test]
452 fn sub_selection_extend_from_rect_pick() {
453 let mut result = RectPickResult::default();
454 result
455 .hits
456 .insert(10, vec![SubObjectRef::Face(0), SubObjectRef::Face(1)]);
457 result.hits.insert(20, vec![SubObjectRef::Point(5)]);
458
459 let mut sel = SubSelection::new();
460 sel.extend_from_rect_pick(&result);
461
462 assert_eq!(sel.len(), 3);
463 assert!(sel.contains(10, SubObjectRef::Face(0)));
464 assert!(sel.contains(10, SubObjectRef::Face(1)));
465 assert!(sel.contains(20, SubObjectRef::Point(5)));
466 assert_eq!(sel.face_count(), 2);
467 assert_eq!(sel.point_count(), 1);
468 }
469}