parry2d/shape/feature_id.rs
1/// An identifier of a geometric feature (vertex, edge, or face) of a shape.
2///
3/// Feature IDs are used throughout Parry to identify specific geometric features on shapes
4/// during collision detection, contact generation, and other geometric queries. They allow
5/// algorithms to track which parts of shapes are interacting, which is essential for:
6///
7/// - **Contact manifold generation**: Tracking persistent contact points between frames
8/// - **Collision response**: Determining which features are colliding
9/// - **Debug visualization**: Highlighting specific geometric elements
10/// - **Feature-based queries**: Retrieving geometric data for specific shape features
11///
12/// # Feature Types
13///
14/// - **Vertex**: A corner point of the shape (0-dimensional feature)
15/// - **Edge**: A line segment connecting two vertices (1-dimensional feature, 3D only)
16/// - **Face**: A flat surface bounded by edges (2-dimensional feature)
17/// - **Unknown**: Used when the feature type cannot be determined or is not applicable
18///
19/// # Shape-Specific Identifiers
20///
21/// The numeric ID within each feature type is shape-dependent. For example:
22/// - For a cuboid, vertex IDs might range from 0-7 (8 corners)
23/// - For a triangle, face ID 0 typically refers to the triangle itself
24/// - For composite shapes, IDs might encode both the sub-shape and the feature within it
25///
26/// The exact meaning of these IDs depends on the shape's internal representation, but they
27/// are guaranteed to allow efficient retrieval of the feature's geometric information.
28///
29/// # Examples
30///
31/// Basic usage of feature IDs in 2D:
32///
33/// ```
34/// # #[cfg(all(feature = "dim2", feature = "f32"))] {
35/// use parry2d::shape::FeatureId;
36///
37/// // Create a vertex feature identifier
38/// let vertex_id = FeatureId::Vertex(5);
39/// assert_eq!(vertex_id.unwrap_vertex(), 5);
40///
41/// // Create a face feature identifier (in 2D, faces are edges of the polygon)
42/// let face_id = FeatureId::Face(2);
43/// assert_eq!(face_id.unwrap_face(), 2);
44///
45/// // Unknown feature (used as default)
46/// let unknown = FeatureId::Unknown;
47/// assert_eq!(unknown, FeatureId::default());
48/// # }
49/// ```
50///
51/// Basic usage of feature IDs in 3D:
52///
53/// ```
54/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
55/// use parry3d::shape::FeatureId;
56///
57/// // Create a vertex feature identifier
58/// let vertex_id = FeatureId::Vertex(5);
59/// assert_eq!(vertex_id.unwrap_vertex(), 5);
60///
61/// // Create an edge feature identifier (only available in 3D)
62/// let edge_id = FeatureId::Edge(3);
63/// assert_eq!(edge_id.unwrap_edge(), 3);
64///
65/// // Create a face feature identifier
66/// let face_id = FeatureId::Face(2);
67/// assert_eq!(face_id.unwrap_face(), 2);
68///
69/// // Unknown feature (used as default)
70/// let unknown = FeatureId::Unknown;
71/// assert_eq!(unknown, FeatureId::default());
72/// # }
73/// ```
74///
75/// Pattern matching on feature types in 3D:
76///
77/// ```
78/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
79/// use parry3d::shape::FeatureId;
80///
81/// fn describe_feature(feature: FeatureId) -> String {
82/// match feature {
83/// FeatureId::Vertex(id) => format!("Vertex #{}", id),
84/// FeatureId::Edge(id) => format!("Edge #{}", id),
85/// FeatureId::Face(id) => format!("Face #{}", id),
86/// FeatureId::Unknown => "Unknown feature".to_string(),
87/// }
88/// }
89///
90/// assert_eq!(describe_feature(FeatureId::Vertex(3)), "Vertex #3");
91/// assert_eq!(describe_feature(FeatureId::Edge(5)), "Edge #5");
92/// assert_eq!(describe_feature(FeatureId::Face(1)), "Face #1");
93/// # }
94/// ```
95///
96/// # 2D vs 3D
97///
98/// In 2D mode (`dim2` feature), the `Edge` variant is not available since edges in 2D
99/// are effectively the same as faces (line segments). In 2D:
100/// - Vertices represent corner points
101/// - Faces represent edges of the polygon
102///
103/// In 3D mode (`dim3` feature), all three types are available:
104/// - Vertices are 0D points
105/// - Edges are 1D line segments
106/// - Faces are 2D polygons
107#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
108#[cfg_attr(
109 feature = "rkyv",
110 derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize)
111)]
112#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, Default)]
113pub enum FeatureId {
114 /// Shape-dependent identifier of a vertex (0-dimensional corner point).
115 ///
116 /// The numeric ID is specific to each shape type and allows efficient lookup
117 /// of the vertex's position and other geometric properties.
118 Vertex(u32),
119 #[cfg(feature = "dim3")]
120 /// Shape-dependent identifier of an edge (1-dimensional line segment).
121 ///
122 /// Available only in 3D mode. The numeric ID is specific to each shape type
123 /// and allows efficient lookup of the edge's endpoints and direction.
124 Edge(u32),
125 /// Shape-dependent identifier of a face (2-dimensional flat surface).
126 ///
127 /// In 2D, faces represent the edges of polygons (line segments).
128 /// In 3D, faces represent polygonal surfaces. The numeric ID is specific
129 /// to each shape type and allows efficient lookup of the face's vertices,
130 /// normal vector, and other properties.
131 Face(u32),
132 // XXX: remove this variant.
133 /// Unknown or unidentified feature.
134 ///
135 /// Used as a default value or when the specific feature cannot be determined.
136 /// This variant should generally be avoided in production code.
137 #[default]
138 Unknown,
139}
140
141impl FeatureId {
142 /// Retrieves the numeric ID if this is a vertex feature.
143 ///
144 /// # Panics
145 ///
146 /// Panics if the feature is not a vertex (i.e., if it's an edge, face, or unknown).
147 ///
148 /// # Examples
149 ///
150 /// ```
151 /// # #[cfg(all(feature = "dim2", feature = "f32"))] {
152 /// use parry2d::shape::FeatureId;
153 ///
154 /// let vertex = FeatureId::Vertex(42);
155 /// assert_eq!(vertex.unwrap_vertex(), 42);
156 /// # }
157 /// ```
158 ///
159 /// This will panic:
160 ///
161 /// ```no_run
162 /// # #[cfg(all(feature = "dim2", feature = "f32"))] {
163 /// use parry2d::shape::FeatureId;
164 ///
165 /// let face = FeatureId::Face(5);
166 /// face.unwrap_vertex(); // Panics!
167 /// # }
168 /// # #[cfg(all(feature = "dim2", feature = "f64"))] {
169 /// use parry2d_f64::shape::FeatureId;
170 ///
171 /// let face = FeatureId::Face(5);
172 /// face.unwrap_vertex(); // Panics!
173 /// # }
174 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
175 /// use parry3d::shape::FeatureId;
176 ///
177 /// let face = FeatureId::Face(5);
178 /// face.unwrap_vertex(); // Panics!
179 /// # }
180 /// # #[cfg(all(feature = "dim3", feature = "f64"))] {
181 /// use parry3d_f64::shape::FeatureId;
182 ///
183 /// let face = FeatureId::Face(5);
184 /// face.unwrap_vertex(); // Panics!
185 /// # }
186 /// ```
187 pub fn unwrap_vertex(self) -> u32 {
188 match self {
189 FeatureId::Vertex(id) => id,
190 _ => panic!("The feature id does not identify a vertex."),
191 }
192 }
193
194 /// Retrieves the numeric ID if this is an edge feature.
195 ///
196 /// Available only in 3D mode (`dim3` feature).
197 ///
198 /// # Panics
199 ///
200 /// Panics if the feature is not an edge (i.e., if it's a vertex, face, or unknown).
201 ///
202 /// # Examples
203 ///
204 /// ```
205 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
206 /// use parry3d::shape::FeatureId;
207 ///
208 /// let edge = FeatureId::Edge(7);
209 /// assert_eq!(edge.unwrap_edge(), 7);
210 /// # }
211 /// ```
212 ///
213 /// This will panic:
214 ///
215 /// ```no_run
216 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
217 /// use parry3d::shape::FeatureId;
218 ///
219 /// let vertex = FeatureId::Vertex(3);
220 /// vertex.unwrap_edge(); // Panics!
221 /// # }
222 /// ```
223 #[cfg(feature = "dim3")]
224 pub fn unwrap_edge(self) -> u32 {
225 match self {
226 FeatureId::Edge(id) => id,
227 _ => panic!("The feature id does not identify an edge."),
228 }
229 }
230
231 /// Retrieves the numeric ID if this is a face feature.
232 ///
233 /// # Panics
234 ///
235 /// Panics if the feature is not a face (i.e., if it's a vertex, edge, or unknown).
236 ///
237 /// # Examples
238 ///
239 /// ```
240 /// # #[cfg(all(feature = "dim2", feature = "f32"))] {
241 /// use parry2d::shape::FeatureId;
242 ///
243 /// let face = FeatureId::Face(12);
244 /// assert_eq!(face.unwrap_face(), 12);
245 /// # }
246 /// ```
247 ///
248 /// This will panic:
249 ///
250 /// ```should_panic
251 /// # #[cfg(all(feature = "dim2", feature = "f32"))] {
252 /// use parry2d::shape::FeatureId;
253 ///
254 /// let vertex = FeatureId::Vertex(0);
255 /// vertex.unwrap_face(); // Panics!
256 /// # }
257 /// # #[cfg(all(feature = "dim2", feature = "f64"))] {
258 /// use parry2d_f64::shape::FeatureId;
259 ///
260 /// let vertex = FeatureId::Vertex(0);
261 /// vertex.unwrap_face(); // Panics!
262 /// # }
263 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
264 /// use parry3d::shape::FeatureId;
265 ///
266 /// let vertex = FeatureId::Vertex(0);
267 /// vertex.unwrap_face(); // Panics!
268 /// # }
269 /// # #[cfg(all(feature = "dim3", feature = "f64"))] {
270 /// use parry3d_f64::shape::FeatureId;
271 ///
272 /// let vertex = FeatureId::Vertex(0);
273 /// vertex.unwrap_face(); // Panics!
274 /// # }
275 /// ```
276 pub fn unwrap_face(self) -> u32 {
277 match self {
278 FeatureId::Face(id) => id,
279 _ => panic!("The feature id does not identify a face."),
280 }
281 }
282}
283
284/// A memory-efficient feature ID where the type and index are packed into a single `u32`.
285///
286/// `PackedFeatureId` is a space-optimized version of [`FeatureId`] that encodes both the
287/// feature type (vertex, edge, or face) and its numeric identifier in a single 32-bit value.
288/// This is particularly useful when storing large numbers of feature IDs, as it uses half
289/// the memory of a standard enum representation.
290///
291/// # Memory Layout
292///
293/// The packing scheme uses the upper 2 bits to encode the feature type, leaving 30 bits
294/// (0-1,073,741,823) for the feature index:
295///
296/// ```text
297/// ┌──┬──┬────────────────────────────────┐
298/// │31│30│29 0│
299/// ├──┴──┴────────────────────────────────┤
300/// │Type │ Feature Index │
301/// │(2b) │ (30 bits) │
302/// └─────┴────────────────────────────────┘
303///
304/// Type encoding:
305/// - 00: Unknown
306/// - 01: Vertex
307/// - 10: Edge (3D only)
308/// - 11: Face
309/// ```
310///
311/// # Use Cases
312///
313/// Use `PackedFeatureId` when:
314/// - Storing feature IDs in large data structures (e.g., contact manifolds)
315/// - Passing feature IDs across FFI boundaries where a fixed size is required
316/// - Memory usage is a concern and you have many feature IDs
317///
318/// Use regular [`FeatureId`] when:
319/// - Code clarity is more important than memory usage
320/// - You need to pattern match on feature types frequently
321/// - Working with small numbers of feature IDs
322///
323/// # Examples
324///
325/// Creating and unpacking feature IDs in 2D:
326///
327/// ```
328/// # #[cfg(all(feature = "dim2", feature = "f32"))] {
329/// use parry2d::shape::{FeatureId, PackedFeatureId};
330///
331/// // Create a packed vertex ID
332/// let packed_vertex = PackedFeatureId::vertex(10);
333/// assert!(packed_vertex.is_vertex());
334/// assert!(!packed_vertex.is_face());
335///
336/// // Create a packed face ID
337/// let packed_face = PackedFeatureId::face(5);
338/// assert!(packed_face.is_face());
339///
340/// // Unpack to get the full enum
341/// let unpacked = packed_face.unpack();
342/// assert_eq!(unpacked, FeatureId::Face(5));
343/// # }
344/// ```
345///
346/// Creating and unpacking feature IDs in 3D:
347///
348/// ```
349/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
350/// use parry3d::shape::{FeatureId, PackedFeatureId};
351///
352/// // Create a packed vertex ID
353/// let packed_vertex = PackedFeatureId::vertex(10);
354/// assert!(packed_vertex.is_vertex());
355/// assert!(!packed_vertex.is_face());
356///
357/// // Create a packed edge ID (3D only)
358/// let packed_edge = PackedFeatureId::edge(7);
359/// assert!(packed_edge.is_edge());
360///
361/// // Create a packed face ID
362/// let packed_face = PackedFeatureId::face(5);
363/// assert!(packed_face.is_face());
364///
365/// // Unpack to get the full enum
366/// let unpacked = packed_face.unpack();
367/// assert_eq!(unpacked, FeatureId::Face(5));
368/// # }
369/// ```
370///
371/// Converting between packed and unpacked forms:
372///
373/// ```
374/// # #[cfg(all(feature = "dim2", feature = "f32"))] {
375/// use parry2d::shape::{FeatureId, PackedFeatureId};
376///
377/// // From FeatureId to PackedFeatureId
378/// let feature = FeatureId::Vertex(42);
379/// let packed: PackedFeatureId = feature.into();
380/// assert!(packed.is_vertex());
381///
382/// // From PackedFeatureId back to FeatureId
383/// let unpacked = packed.unpack();
384/// assert_eq!(unpacked, FeatureId::Vertex(42));
385/// # }
386/// ```
387///
388/// Working with the unknown feature:
389///
390/// ```
391/// # #[cfg(all(feature = "dim2", feature = "f32"))] {
392/// use parry2d::shape::PackedFeatureId;
393///
394/// let unknown = PackedFeatureId::UNKNOWN;
395/// assert!(unknown.is_unknown());
396/// assert!(!unknown.is_vertex());
397/// assert!(!unknown.is_face());
398/// # }
399/// ```
400///
401/// Checking feature types efficiently in 3D:
402///
403/// ```
404/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
405/// use parry3d::shape::PackedFeatureId;
406///
407/// let vertex = PackedFeatureId::vertex(100);
408/// let edge = PackedFeatureId::edge(50);
409/// let face = PackedFeatureId::face(25);
410///
411/// // Type checking is very fast (just bit masking)
412/// assert!(vertex.is_vertex());
413/// assert!(edge.is_edge());
414/// assert!(face.is_face());
415///
416/// // Different types are not equal
417/// assert_ne!(vertex, edge);
418/// assert_ne!(edge, face);
419/// # }
420/// ```
421///
422/// # Performance
423///
424/// `PackedFeatureId` provides several performance benefits:
425/// - **Memory**: Uses 4 bytes vs 8 bytes for `FeatureId` (on 64-bit systems)
426/// - **Cache efficiency**: Better cache utilization when storing many IDs
427/// - **Type checking**: Very fast (single bitwise AND operation)
428/// - **Conversion**: Converting to/from `FeatureId` is essentially free
429///
430/// # Limitations
431///
432/// The packing scheme limits feature indices to 30 bits (max value: 1,073,741,823).
433/// Attempting to create a packed feature ID with a larger index will panic in debug
434/// mode due to the assertion checks in the constructor methods.
435#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
436#[cfg_attr(
437 feature = "rkyv",
438 derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize)
439)]
440#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
441pub struct PackedFeatureId(pub u32);
442
443impl PackedFeatureId {
444 /// Constant representing an unknown or unidentified feature.
445 ///
446 /// This is the default value and corresponds to `FeatureId::Unknown`.
447 ///
448 /// # Examples
449 ///
450 /// ```
451 /// # #[cfg(all(feature = "dim2", feature = "f32"))] {
452 /// use parry2d::shape::{PackedFeatureId, FeatureId};
453 ///
454 /// let unknown = PackedFeatureId::UNKNOWN;
455 /// assert!(unknown.is_unknown());
456 /// assert_eq!(unknown.unpack(), FeatureId::Unknown);
457 /// # }
458 /// ```
459 pub const UNKNOWN: Self = Self(0);
460
461 const CODE_MASK: u32 = 0x3fff_ffff;
462 const HEADER_MASK: u32 = !Self::CODE_MASK;
463 const HEADER_VERTEX: u32 = 0b01 << 30;
464 #[cfg(feature = "dim3")]
465 const HEADER_EDGE: u32 = 0b10 << 30;
466 const HEADER_FACE: u32 = 0b11 << 30;
467
468 /// Creates a packed feature ID for a vertex with the given index.
469 ///
470 /// # Panics
471 ///
472 /// Panics in debug mode if `code` uses any of the upper 2 bits (i.e., if `code >= 2^30`).
473 /// The maximum valid value is 1,073,741,823 (0x3FFFFFFF).
474 ///
475 /// # Examples
476 ///
477 /// ```
478 /// # #[cfg(all(feature = "dim2", feature = "f32"))] {
479 /// use parry2d::shape::{PackedFeatureId, FeatureId};
480 ///
481 /// let packed = PackedFeatureId::vertex(5);
482 /// assert!(packed.is_vertex());
483 /// assert_eq!(packed.unpack(), FeatureId::Vertex(5));
484 /// # }
485 /// ```
486 pub fn vertex(code: u32) -> Self {
487 assert_eq!(code & Self::HEADER_MASK, 0);
488 Self(Self::HEADER_VERTEX | code)
489 }
490
491 /// Creates a packed feature ID for an edge with the given index.
492 ///
493 /// Available only in 3D mode (`dim3` feature).
494 ///
495 /// # Panics
496 ///
497 /// Panics in debug mode if `code` uses any of the upper 2 bits (i.e., if `code >= 2^30`).
498 /// The maximum valid value is 1,073,741,823 (0x3FFFFFFF).
499 ///
500 /// # Examples
501 ///
502 /// ```
503 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
504 /// use parry3d::shape::{PackedFeatureId, FeatureId};
505 ///
506 /// let packed = PackedFeatureId::edge(10);
507 /// assert!(packed.is_edge());
508 /// assert_eq!(packed.unpack(), FeatureId::Edge(10));
509 /// # }
510 /// ```
511 #[cfg(feature = "dim3")]
512 pub fn edge(code: u32) -> Self {
513 assert_eq!(code & Self::HEADER_MASK, 0);
514 Self(Self::HEADER_EDGE | code)
515 }
516
517 /// Creates a packed feature ID for a face with the given index.
518 ///
519 /// # Panics
520 ///
521 /// Panics in debug mode if `code` uses any of the upper 2 bits (i.e., if `code >= 2^30`).
522 /// The maximum valid value is 1,073,741,823 (0x3FFFFFFF).
523 ///
524 /// # Examples
525 ///
526 /// ```
527 /// # #[cfg(all(feature = "dim2", feature = "f32"))] {
528 /// use parry2d::shape::{PackedFeatureId, FeatureId};
529 ///
530 /// let packed = PackedFeatureId::face(15);
531 /// assert!(packed.is_face());
532 /// assert_eq!(packed.unpack(), FeatureId::Face(15));
533 /// # }
534 /// ```
535 pub fn face(code: u32) -> Self {
536 assert_eq!(code & Self::HEADER_MASK, 0);
537 Self(Self::HEADER_FACE | code)
538 }
539
540 #[cfg(feature = "dim2")]
541 /// Converts an array of vertex feature ids into an array of packed feature ids.
542 pub(crate) fn vertices(code: [u32; 2]) -> [Self; 2] {
543 [Self::vertex(code[0]), Self::vertex(code[1])]
544 }
545
546 #[cfg(feature = "dim3")]
547 /// Converts an array of vertex feature ids into an array of packed feature ids.
548 pub(crate) fn vertices(code: [u32; 4]) -> [Self; 4] {
549 [
550 Self::vertex(code[0]),
551 Self::vertex(code[1]),
552 Self::vertex(code[2]),
553 Self::vertex(code[3]),
554 ]
555 }
556
557 #[cfg(feature = "dim3")]
558 /// Converts an array of edge feature ids into an array of packed feature ids.
559 pub(crate) fn edges(code: [u32; 4]) -> [Self; 4] {
560 [
561 Self::edge(code[0]),
562 Self::edge(code[1]),
563 Self::edge(code[2]),
564 Self::edge(code[3]),
565 ]
566 }
567
568 /// Unpacks this feature ID into the full `FeatureId` enum.
569 ///
570 /// This converts the compact packed representation back into the explicit enum form,
571 /// allowing you to pattern match on the feature type.
572 ///
573 /// # Examples
574 ///
575 /// ```
576 /// # #[cfg(all(feature = "dim2", feature = "f32"))] {
577 /// use parry2d::shape::{FeatureId, PackedFeatureId};
578 ///
579 /// let packed = PackedFeatureId::vertex(42);
580 /// let unpacked = packed.unpack();
581 ///
582 /// match unpacked {
583 /// FeatureId::Vertex(id) => assert_eq!(id, 42),
584 /// _ => panic!("Expected a vertex!"),
585 /// }
586 /// # }
587 /// ```
588 ///
589 /// Round-trip conversion:
590 ///
591 /// ```
592 /// # #[cfg(all(feature = "dim2", feature = "f32"))] {
593 /// use parry2d::shape::{FeatureId, PackedFeatureId};
594 ///
595 /// let original = FeatureId::Face(100);
596 /// let packed: PackedFeatureId = original.into();
597 /// let unpacked = packed.unpack();
598 ///
599 /// assert_eq!(original, unpacked);
600 /// # }
601 /// ```
602 pub fn unpack(self) -> FeatureId {
603 let header = self.0 & Self::HEADER_MASK;
604 let code = self.0 & Self::CODE_MASK;
605 match header {
606 Self::HEADER_VERTEX => FeatureId::Vertex(code),
607 #[cfg(feature = "dim3")]
608 Self::HEADER_EDGE => FeatureId::Edge(code),
609 Self::HEADER_FACE => FeatureId::Face(code),
610 _ => FeatureId::Unknown,
611 }
612 }
613
614 /// Checks if this feature ID identifies a face.
615 ///
616 /// This is a very fast operation (single bitwise AND and comparison).
617 ///
618 /// # Examples
619 ///
620 /// ```
621 /// # #[cfg(all(feature = "dim2", feature = "f32"))] {
622 /// use parry2d::shape::PackedFeatureId;
623 ///
624 /// let face = PackedFeatureId::face(5);
625 /// let vertex = PackedFeatureId::vertex(5);
626 ///
627 /// assert!(face.is_face());
628 /// assert!(!vertex.is_face());
629 /// # }
630 /// ```
631 pub fn is_face(self) -> bool {
632 self.0 & Self::HEADER_MASK == Self::HEADER_FACE
633 }
634
635 /// Checks if this feature ID identifies a vertex.
636 ///
637 /// This is a very fast operation (single bitwise AND and comparison).
638 ///
639 /// # Examples
640 ///
641 /// ```
642 /// # #[cfg(all(feature = "dim2", feature = "f32"))] {
643 /// use parry2d::shape::PackedFeatureId;
644 ///
645 /// let vertex = PackedFeatureId::vertex(10);
646 /// let face = PackedFeatureId::face(10);
647 ///
648 /// assert!(vertex.is_vertex());
649 /// assert!(!face.is_vertex());
650 /// # }
651 /// ```
652 pub fn is_vertex(self) -> bool {
653 self.0 & Self::HEADER_MASK == Self::HEADER_VERTEX
654 }
655
656 /// Checks if this feature ID identifies an edge.
657 ///
658 /// Available only in 3D mode (`dim3` feature).
659 ///
660 /// This is a very fast operation (single bitwise AND and comparison).
661 ///
662 /// # Examples
663 ///
664 /// ```
665 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
666 /// use parry3d::shape::PackedFeatureId;
667 ///
668 /// let edge = PackedFeatureId::edge(7);
669 /// let vertex = PackedFeatureId::vertex(7);
670 ///
671 /// assert!(edge.is_edge());
672 /// assert!(!vertex.is_edge());
673 /// # }
674 /// ```
675 #[cfg(feature = "dim3")]
676 pub fn is_edge(self) -> bool {
677 self.0 & Self::HEADER_MASK == Self::HEADER_EDGE
678 }
679
680 /// Checks if this feature ID is unknown.
681 ///
682 /// # Examples
683 ///
684 /// ```
685 /// # #[cfg(all(feature = "dim2", feature = "f32"))] {
686 /// use parry2d::shape::PackedFeatureId;
687 ///
688 /// let unknown = PackedFeatureId::UNKNOWN;
689 /// let vertex = PackedFeatureId::vertex(0);
690 ///
691 /// assert!(unknown.is_unknown());
692 /// assert!(!vertex.is_unknown());
693 /// # }
694 /// ```
695 pub fn is_unknown(self) -> bool {
696 self == Self::UNKNOWN
697 }
698}
699
700impl From<FeatureId> for PackedFeatureId {
701 /// Converts a `FeatureId` into its packed representation.
702 ///
703 /// This is a lossless conversion that encodes the feature type and index
704 /// into a single `u32` value.
705 ///
706 /// # Examples
707 ///
708 /// ```
709 /// # #[cfg(all(feature = "dim2", feature = "f32"))] {
710 /// use parry2d::shape::{FeatureId, PackedFeatureId};
711 ///
712 /// // Explicit conversion
713 /// let feature = FeatureId::Vertex(123);
714 /// let packed = PackedFeatureId::from(feature);
715 /// assert!(packed.is_vertex());
716 ///
717 /// // Using Into trait
718 /// let feature = FeatureId::Face(456);
719 /// let packed: PackedFeatureId = feature.into();
720 /// assert!(packed.is_face());
721 ///
722 /// // Round-trip conversion preserves the value
723 /// let original = FeatureId::Vertex(789);
724 /// let packed: PackedFeatureId = original.into();
725 /// assert_eq!(packed.unpack(), original);
726 /// # }
727 /// ```
728 fn from(value: FeatureId) -> Self {
729 match value {
730 FeatureId::Face(fid) => Self::face(fid),
731 #[cfg(feature = "dim3")]
732 FeatureId::Edge(fid) => Self::edge(fid),
733 FeatureId::Vertex(fid) => Self::vertex(fid),
734 FeatureId::Unknown => Self::UNKNOWN,
735 }
736 }
737}