Skip to main content

parry2d/shape/
feature_id.rs

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