parry2d/shape/
feature_id.rs

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