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}