Skip to main content

parry3d/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        #[cfg(not(target_arch = "spirv"))]
488        assert_eq!(code & Self::HEADER_MASK, 0);
489        Self(Self::HEADER_VERTEX | code)
490    }
491
492    /// Creates a packed feature ID for an edge with the given index.
493    ///
494    /// Available only in 3D mode (`dim3` feature).
495    ///
496    /// # Panics
497    ///
498    /// Panics in debug mode if `code` uses any of the upper 2 bits (i.e., if `code >= 2^30`).
499    /// The maximum valid value is 1,073,741,823 (0x3FFFFFFF).
500    ///
501    /// # Examples
502    ///
503    /// ```
504    /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
505    /// use parry3d::shape::{PackedFeatureId, FeatureId};
506    ///
507    /// let packed = PackedFeatureId::edge(10);
508    /// assert!(packed.is_edge());
509    /// assert_eq!(packed.unpack(), FeatureId::Edge(10));
510    /// # }
511    /// ```
512    #[cfg(feature = "dim3")]
513    pub fn edge(code: u32) -> Self {
514        #[cfg(not(target_arch = "spirv"))]
515        assert_eq!(code & Self::HEADER_MASK, 0);
516        Self(Self::HEADER_EDGE | code)
517    }
518
519    /// Creates a packed feature ID for a face with the given index.
520    ///
521    /// # Panics
522    ///
523    /// Panics in debug mode if `code` uses any of the upper 2 bits (i.e., if `code >= 2^30`).
524    /// The maximum valid value is 1,073,741,823 (0x3FFFFFFF).
525    ///
526    /// # Examples
527    ///
528    /// ```
529    /// # #[cfg(all(feature = "dim2", feature = "f32"))] {
530    /// use parry2d::shape::{PackedFeatureId, FeatureId};
531    ///
532    /// let packed = PackedFeatureId::face(15);
533    /// assert!(packed.is_face());
534    /// assert_eq!(packed.unpack(), FeatureId::Face(15));
535    /// # }
536    /// ```
537    pub fn face(code: u32) -> Self {
538        #[cfg(not(target_arch = "spirv"))]
539        assert_eq!(code & Self::HEADER_MASK, 0);
540        Self(Self::HEADER_FACE | code)
541    }
542
543    #[cfg(feature = "dim2")]
544    /// Converts an array of vertex feature ids into an array of packed feature ids.
545    pub(crate) fn vertices(code: [u32; 2]) -> [Self; 2] {
546        [Self::vertex(code[0]), Self::vertex(code[1])]
547    }
548
549    #[cfg(feature = "dim3")]
550    /// Converts an array of vertex feature ids into an array of packed feature ids.
551    pub(crate) fn vertices(code: [u32; 4]) -> [Self; 4] {
552        [
553            Self::vertex(code[0]),
554            Self::vertex(code[1]),
555            Self::vertex(code[2]),
556            Self::vertex(code[3]),
557        ]
558    }
559
560    #[cfg(feature = "dim3")]
561    /// Converts an array of edge feature ids into an array of packed feature ids.
562    pub(crate) fn edges(code: [u32; 4]) -> [Self; 4] {
563        [
564            Self::edge(code[0]),
565            Self::edge(code[1]),
566            Self::edge(code[2]),
567            Self::edge(code[3]),
568        ]
569    }
570
571    /// Unpacks this feature ID into the full `FeatureId` enum.
572    ///
573    /// This converts the compact packed representation back into the explicit enum form,
574    /// allowing you to pattern match on the feature type.
575    ///
576    /// # Examples
577    ///
578    /// ```
579    /// # #[cfg(all(feature = "dim2", feature = "f32"))] {
580    /// use parry2d::shape::{FeatureId, PackedFeatureId};
581    ///
582    /// let packed = PackedFeatureId::vertex(42);
583    /// let unpacked = packed.unpack();
584    ///
585    /// match unpacked {
586    ///     FeatureId::Vertex(id) => assert_eq!(id, 42),
587    ///     _ => panic!("Expected a vertex!"),
588    /// }
589    /// # }
590    /// ```
591    ///
592    /// Round-trip conversion:
593    ///
594    /// ```
595    /// # #[cfg(all(feature = "dim2", feature = "f32"))] {
596    /// use parry2d::shape::{FeatureId, PackedFeatureId};
597    ///
598    /// let original = FeatureId::Face(100);
599    /// let packed: PackedFeatureId = original.into();
600    /// let unpacked = packed.unpack();
601    ///
602    /// assert_eq!(original, unpacked);
603    /// # }
604    /// ```
605    pub fn unpack(self) -> FeatureId {
606        let header = self.0 & Self::HEADER_MASK;
607        let code = self.0 & Self::CODE_MASK;
608        match header {
609            Self::HEADER_VERTEX => FeatureId::Vertex(code),
610            #[cfg(feature = "dim3")]
611            Self::HEADER_EDGE => FeatureId::Edge(code),
612            Self::HEADER_FACE => FeatureId::Face(code),
613            _ => FeatureId::Unknown,
614        }
615    }
616
617    /// Checks if this feature ID identifies a face.
618    ///
619    /// This is a very fast operation (single bitwise AND and comparison).
620    ///
621    /// # Examples
622    ///
623    /// ```
624    /// # #[cfg(all(feature = "dim2", feature = "f32"))] {
625    /// use parry2d::shape::PackedFeatureId;
626    ///
627    /// let face = PackedFeatureId::face(5);
628    /// let vertex = PackedFeatureId::vertex(5);
629    ///
630    /// assert!(face.is_face());
631    /// assert!(!vertex.is_face());
632    /// # }
633    /// ```
634    pub fn is_face(self) -> bool {
635        self.0 & Self::HEADER_MASK == Self::HEADER_FACE
636    }
637
638    /// Checks if this feature ID identifies a vertex.
639    ///
640    /// This is a very fast operation (single bitwise AND and comparison).
641    ///
642    /// # Examples
643    ///
644    /// ```
645    /// # #[cfg(all(feature = "dim2", feature = "f32"))] {
646    /// use parry2d::shape::PackedFeatureId;
647    ///
648    /// let vertex = PackedFeatureId::vertex(10);
649    /// let face = PackedFeatureId::face(10);
650    ///
651    /// assert!(vertex.is_vertex());
652    /// assert!(!face.is_vertex());
653    /// # }
654    /// ```
655    pub fn is_vertex(self) -> bool {
656        self.0 & Self::HEADER_MASK == Self::HEADER_VERTEX
657    }
658
659    /// Checks if this feature ID identifies an edge.
660    ///
661    /// Available only in 3D mode (`dim3` feature).
662    ///
663    /// This is a very fast operation (single bitwise AND and comparison).
664    ///
665    /// # Examples
666    ///
667    /// ```
668    /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
669    /// use parry3d::shape::PackedFeatureId;
670    ///
671    /// let edge = PackedFeatureId::edge(7);
672    /// let vertex = PackedFeatureId::vertex(7);
673    ///
674    /// assert!(edge.is_edge());
675    /// assert!(!vertex.is_edge());
676    /// # }
677    /// ```
678    #[cfg(feature = "dim3")]
679    pub fn is_edge(self) -> bool {
680        self.0 & Self::HEADER_MASK == Self::HEADER_EDGE
681    }
682
683    /// Checks if this feature ID is unknown.
684    ///
685    /// # Examples
686    ///
687    /// ```
688    /// # #[cfg(all(feature = "dim2", feature = "f32"))] {
689    /// use parry2d::shape::PackedFeatureId;
690    ///
691    /// let unknown = PackedFeatureId::UNKNOWN;
692    /// let vertex = PackedFeatureId::vertex(0);
693    ///
694    /// assert!(unknown.is_unknown());
695    /// assert!(!vertex.is_unknown());
696    /// # }
697    /// ```
698    pub fn is_unknown(self) -> bool {
699        self == Self::UNKNOWN
700    }
701}
702
703impl From<FeatureId> for PackedFeatureId {
704    /// Converts a `FeatureId` into its packed representation.
705    ///
706    /// This is a lossless conversion that encodes the feature type and index
707    /// into a single `u32` value.
708    ///
709    /// # Examples
710    ///
711    /// ```
712    /// # #[cfg(all(feature = "dim2", feature = "f32"))] {
713    /// use parry2d::shape::{FeatureId, PackedFeatureId};
714    ///
715    /// // Explicit conversion
716    /// let feature = FeatureId::Vertex(123);
717    /// let packed = PackedFeatureId::from(feature);
718    /// assert!(packed.is_vertex());
719    ///
720    /// // Using Into trait
721    /// let feature = FeatureId::Face(456);
722    /// let packed: PackedFeatureId = feature.into();
723    /// assert!(packed.is_face());
724    ///
725    /// // Round-trip conversion preserves the value
726    /// let original = FeatureId::Vertex(789);
727    /// let packed: PackedFeatureId = original.into();
728    /// assert_eq!(packed.unpack(), original);
729    /// # }
730    /// ```
731    fn from(value: FeatureId) -> Self {
732        match value {
733            FeatureId::Face(fid) => Self::face(fid),
734            #[cfg(feature = "dim3")]
735            FeatureId::Edge(fid) => Self::edge(fid),
736            FeatureId::Vertex(fid) => Self::vertex(fid),
737            FeatureId::Unknown => Self::UNKNOWN,
738        }
739    }
740}