Skip to main content

parry2d/shape/
cuboid.rs

1//! Support mapping based Cuboid shape.
2
3#[cfg(feature = "dim3")]
4use crate::math::Real;
5use crate::math::{Vector, VectorExt};
6#[cfg(feature = "dim3")]
7use crate::shape::Segment;
8use crate::shape::{FeatureId, PackedFeatureId, PolygonalFeature, SupportMap};
9use crate::utils::WSign;
10
11/// A cuboid shape, also known as a box or rectangle.
12///
13/// A cuboid is defined by its **half-extents**, which are half the width, height
14/// (and depth in 3D) along each axis. The cuboid is always axis-aligned in its
15/// local coordinate system and centered at the origin.
16///
17/// # Properties
18///
19/// - **In 2D**: Represents a rectangle with dimensions `2 * half_extents.x` by `2 * half_extents.y`
20/// - **In 3D**: Represents a box with dimensions `2 * half_extents.x/y/z`
21/// - **Convex**: Yes, cuboids are always convex shapes
22/// - **Axis-aligned**: In local space, yes (but can be rotated via transformation)
23///
24/// # Why Half-Extents?
25///
26/// Using half-extents instead of full dimensions makes many calculations simpler
27/// and more efficient. For example, checking if a point is inside a cuboid becomes:
28/// `abs(point.x) <= half_extents.x && abs(point.y) <= half_extents.y`
29///
30/// # Use Cases
31///
32/// Cuboids are ideal for:
33/// - Boxes, crates, and containers
34/// - Walls, floors, and platforms
35/// - Simple collision bounds for complex objects
36/// - AABB (Axis-Aligned Bounding Box) representations
37///
38/// # Example
39///
40/// ```rust
41/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
42/// use parry3d::shape::Cuboid;
43/// use parry3d::math::Vector;
44///
45/// // Create a box that is 4 units wide, 2 units tall, and 6 units deep
46/// // (half-extents are half of each dimension)
47/// let cuboid = Cuboid::new(Vector::new(2.0, 1.0, 3.0));
48///
49/// assert_eq!(cuboid.half_extents.x, 2.0);
50/// assert_eq!(cuboid.half_extents.y, 1.0);
51/// assert_eq!(cuboid.half_extents.z, 3.0);
52///
53/// // Full dimensions would be:
54/// // width = 4.0, height = 2.0, depth = 6.0
55/// # }
56/// ```
57#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
58#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
59#[cfg_attr(feature = "encase", derive(encase::ShaderType))]
60#[cfg_attr(
61    feature = "rkyv",
62    derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize)
63)]
64#[derive(PartialEq, Debug, Copy, Clone)]
65#[repr(C)]
66pub struct Cuboid {
67    /// The half-extents of the cuboid along each axis.
68    ///
69    /// Each component represents half the dimension along that axis:
70    /// - `half_extents.x`: Half the width
71    /// - `half_extents.y`: Half the height
72    /// - `half_extents.z`: Half the depth (3D only)
73    ///
74    /// All components should be positive.
75    pub half_extents: Vector,
76}
77
78impl Cuboid {
79    /// Creates a new cuboid from its half-extents.
80    ///
81    /// Half-extents represent half the width along each axis. To create a cuboid
82    /// with full dimensions (width, height, depth), divide each by 2.
83    ///
84    /// # Arguments
85    ///
86    /// * `half_extents` - Half the dimensions along each axis. All components should be positive.
87    ///
88    /// # Example
89    ///
90    /// ```
91    /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
92    /// use parry3d::shape::Cuboid;
93    /// use parry3d::math::Vector;
94    ///
95    /// // Create a 10x6x4 box (full dimensions)
96    /// let cuboid = Cuboid::new(Vector::new(5.0, 3.0, 2.0));
97    ///
98    /// // Verify the half-extents
99    /// assert_eq!(cuboid.half_extents.x, 5.0);
100    /// assert_eq!(cuboid.half_extents.y, 3.0);
101    /// assert_eq!(cuboid.half_extents.z, 2.0);
102    /// # }
103    /// ```
104    ///
105    /// ```
106    /// # #[cfg(all(feature = "dim2", feature = "f32"))] {
107    /// // In 2D:
108    /// use parry2d::shape::Cuboid;
109    /// use parry2d::math::Vector;
110    ///
111    /// // Create a 20x10 rectangle
112    /// let rect = Cuboid::new(Vector::new(10.0, 5.0));
113    /// assert_eq!(rect.half_extents.x, 10.0);
114    /// assert_eq!(rect.half_extents.y, 5.0);
115    /// # }
116    /// ```
117    #[inline]
118    pub fn new(half_extents: Vector) -> Cuboid {
119        Cuboid { half_extents }
120    }
121
122    /// Computes a scaled version of this cuboid.
123    ///
124    /// Each dimension is multiplied by the corresponding component of the `scale` vector.
125    /// Unlike balls, cuboids can be scaled non-uniformly (different scale factors per axis)
126    /// and still remain valid cuboids.
127    ///
128    /// # Arguments
129    ///
130    /// * `scale` - The scaling factors for each axis
131    ///
132    /// # Returns
133    ///
134    /// A new cuboid with scaled dimensions
135    ///
136    /// # Example
137    ///
138    /// ```
139    /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
140    /// use parry3d::shape::Cuboid;
141    /// use parry3d::math::Vector;
142    ///
143    /// let cuboid = Cuboid::new(Vector::new(1.0, 2.0, 3.0));
144    ///
145    /// // Uniform scaling: double all dimensions
146    /// let scaled_uniform = cuboid.scaled(Vector::new(2.0, 2.0, 2.0));
147    /// assert_eq!(scaled_uniform.half_extents, Vector::new(2.0, 4.0, 6.0));
148    ///
149    /// // Non-uniform scaling: different scale per axis
150    /// let scaled_non_uniform = cuboid.scaled(Vector::new(2.0, 1.0, 0.5));
151    /// assert_eq!(scaled_non_uniform.half_extents, Vector::new(2.0, 2.0, 1.5));
152    /// # }
153    /// ```
154    pub fn scaled(self, scale: Vector) -> Self {
155        let new_hext = self.half_extents * scale;
156        Self {
157            half_extents: new_hext,
158        }
159    }
160
161    /// Return the id of the vertex of this cuboid with a normal that maximizes
162    /// the dot product with `dir`.
163    #[cfg(feature = "dim2")]
164    pub fn vertex_feature_id(vertex: Vector) -> u32 {
165        // TODO: is this still correct with the f64 version?
166        #[allow(clippy::unnecessary_cast)] // Unnecessary for f32 but necessary for f64.
167        {
168            ((vertex.x.to_bits() >> 31) & 0b001 | (vertex.y.to_bits() >> 30) & 0b010) as u32
169        }
170    }
171
172    /// Return the feature of this cuboid with a normal that maximizes
173    /// the dot product with `dir`.
174    #[cfg(feature = "dim2")]
175    pub fn support_feature(&self, local_dir: Vector) -> PolygonalFeature {
176        // In 2D, it is best for stability to always return a face.
177        // It won't have any notable impact on performances anyway.
178        self.support_face(local_dir)
179    }
180
181    /// Return the face of this cuboid with a normal that maximizes
182    /// the dot product with `local_dir`.
183    #[cfg(feature = "dim2")]
184    pub fn support_face(&self, local_dir: Vector) -> PolygonalFeature {
185        let he = self.half_extents;
186        let i = local_dir.abs().min_position();
187
188        let vertices = match i {
189            0 => [
190                Vector::new(he.x, local_dir.y.copy_sign_to(he.y)),
191                Vector::new(-he.x, local_dir.y.copy_sign_to(he.y)),
192            ],
193            _ => [
194                Vector::new(local_dir.x.copy_sign_to(he.x), he.y),
195                Vector::new(local_dir.x.copy_sign_to(he.x), -he.y),
196            ],
197        };
198
199        let vid1 = Self::vertex_feature_id(vertices[0]);
200        let vid2 = Self::vertex_feature_id(vertices[1]);
201        let fid = (vid1.max(vid2) << 2) | vid1.min(vid2) | 0b11_00_00;
202
203        PolygonalFeature {
204            vertices,
205            vids: PackedFeatureId::vertices([vid1, vid2]),
206            fid: PackedFeatureId::face(fid),
207            num_vertices: 2,
208        }
209    }
210
211    /// Return the face of this cuboid with a normal that maximizes
212    /// the dot product with `local_dir`.
213    #[cfg(feature = "dim3")]
214    pub fn support_feature(&self, local_dir: Vector) -> PolygonalFeature {
215        // TODO: this should actually return the feature.
216        // And we should change all the callers of this method to use
217        // `.support_face` instead of this method to preserve their old behavior.
218        self.support_face(local_dir)
219        /*
220        const MAX_DOT_THRESHOLD: Real = crate::utils::COS_10_DEGREES;
221        const MIN_DOT_THRESHOLD: Real = 1.0 - MAX_DOT_THRESHOLD;
222
223        let amax = local_dir.amax();
224        let amin = local_dir.amin();
225
226        if amax > MAX_DOT_THRESHOLD {
227            // Support face.
228            CuboidFeature::Face(support_face(self, local_dir))
229        } else if amin < MIN_DOT_THRESHOLD {
230            // Support edge.
231            CuboidFeature::Edge(support_edge(self, local_dir))
232        } else {
233            // Support vertex.
234            CuboidFeature::Vertex(support_vertex(self, local_dir))
235        }
236        */
237    }
238
239    // #[cfg(feature = "dim3")
240    // pub(crate) fn support_vertex(&self, local_dir: Vector) -> CuboidFeatureVertex {
241    //     let vertex = local_support_point(self, local_dir);
242    //     let vid = vertex_feature_id(vertex);
243    //
244    //     CuboidFeatureVertex { vertex, vid }
245    // }
246
247    /// Return the edge segment of this cuboid with a normal cone containing
248    /// a direction that that maximizes the dot product with `local_dir`.
249    #[cfg(feature = "dim3")]
250    pub fn local_support_edge_segment(&self, local_dir: Vector) -> Segment {
251        let he = self.half_extents;
252        let i = local_dir.abs().min_position();
253        let j = (i + 1) % 3;
254        let k = (i + 2) % 3;
255        let mut a = Vector::ZERO;
256        a.vset(i, he.vget(i));
257        a.vset(j, local_dir.vget(j).copy_sign_to(he.vget(j)));
258        a.vset(k, local_dir.vget(k).copy_sign_to(he.vget(k)));
259
260        let mut b = a;
261        b.vset(i, -he.vget(i));
262
263        Segment::new(a, b)
264    }
265
266    /// Computes the face with a normal that maximizes the dot-product with `local_dir`.
267    #[cfg(feature = "dim3")]
268    // The identity ors/shifts below are kept so the bit patterns line up with the comments
269    // documenting the vertex/edge numbering.
270    #[allow(clippy::identity_op)]
271    pub fn support_face(&self, local_dir: Vector) -> PolygonalFeature {
272        // NOTE: can we use the orthonormal basis of local_dir
273        // to make this AoSoA friendly?
274        let he = self.half_extents;
275        let imax = local_dir.abs().max_position();
276        #[expect(clippy::unnecessary_cast)]
277        let sign = match imax {
278            0 => local_dir.x.copy_sign_to(1.0 as Real),
279            1 => local_dir.y.copy_sign_to(1.0 as Real),
280            _ => local_dir.z.copy_sign_to(1.0 as Real),
281        };
282
283        let vertices = match imax {
284            0 => [
285                Vector::new(he.x * sign, he.y, he.z),
286                Vector::new(he.x * sign, -he.y, he.z),
287                Vector::new(he.x * sign, -he.y, -he.z),
288                Vector::new(he.x * sign, he.y, -he.z),
289            ],
290            1 => [
291                Vector::new(he.x, he.y * sign, he.z),
292                Vector::new(-he.x, he.y * sign, he.z),
293                Vector::new(-he.x, he.y * sign, -he.z),
294                Vector::new(he.x, he.y * sign, -he.z),
295            ],
296            _ => [
297                Vector::new(he.x, he.y, he.z * sign),
298                Vector::new(he.x, -he.y, he.z * sign),
299                Vector::new(-he.x, -he.y, he.z * sign),
300                Vector::new(-he.x, he.y, he.z * sign),
301            ],
302        };
303
304        pub fn vid(i: u32) -> u32 {
305            // Each vertex has an even feature id.
306            i * 2
307        }
308
309        let sign_index = ((sign as isize + 1) / 2) as u32;
310        // The vertex id as numbered depending on the sign of the vertex
311        // component. A + sign means the corresponding bit is 0 while a -
312        // sign means the corresponding bit is 1.
313        // For example the vertex [2.0, -1.0, -3.0] has the id 0b011
314        let vids = match imax {
315            0 => {
316                let sbit = sign_index << 2;
317                [
318                    vid(0b000 | sbit),
319                    vid(0b010 | sbit),
320                    vid(0b011 | sbit),
321                    vid(0b001 | sbit),
322                ]
323            }
324            1 => {
325                let sbit = sign_index << 1;
326                [
327                    vid(0b000 | sbit),
328                    vid(0b100 | sbit),
329                    vid(0b101 | sbit),
330                    vid(0b001 | sbit),
331                ]
332            }
333            _ => {
334                let sbit = sign_index;
335                [
336                    vid(0b000 | sbit),
337                    vid(0b010 | sbit),
338                    vid(0b110 | sbit),
339                    vid(0b100 | sbit),
340                ]
341            }
342        };
343
344        // The feature ids of edges is obtained from the vertex ids
345        // of their endpoints.
346        // Assuming vid1 > vid2, we do:   (vid1 << 3) | vid2 | 0b11000000
347        //
348        let eids = match imax {
349            0 => {
350                let sbits = (sign_index << 2) | (sign_index << 5); // 0b00_100_100
351                [
352                    0b11_010_000 | sbits,
353                    0b11_011_010 | sbits,
354                    0b11_011_001 | sbits,
355                    0b11_001_000 | sbits,
356                ]
357            }
358            1 => {
359                let sbits = (sign_index << 1) | (sign_index << 4); // 0b00_010_010
360                [
361                    0b11_100_000 | sbits,
362                    0b11_101_100 | sbits,
363                    0b11_101_001 | sbits,
364                    0b11_001_000 | sbits,
365                ]
366            }
367            _ => {
368                let sbits = (sign_index << 0) | (sign_index << 3); // 0b00_001_001
369                [
370                    0b11_010_000 | sbits,
371                    0b11_110_010 | sbits,
372                    0b11_110_100 | sbits,
373                    0b11_100_000 | sbits,
374                ]
375            }
376        };
377
378        // The face with normals [x, y, z] are numbered [10, 11, 12].
379        // The face with negated normals are numbered [13, 14, 15].
380        let fid = imax as u32 + sign_index * 3 + 10;
381
382        PolygonalFeature {
383            vertices,
384            vids: PackedFeatureId::vertices(vids),
385            eids: PackedFeatureId::edges(eids),
386            fid: PackedFeatureId::face(fid),
387            num_vertices: 4,
388        }
389    }
390
391    /// The normal of the given feature of this shape.
392    #[cfg(feature = "dim2")]
393    pub fn feature_normal(&self, feature: FeatureId) -> Option<Vector> {
394        match feature {
395            FeatureId::Face(id) => {
396                let mut dir: Vector = Vector::ZERO;
397
398                if id < 2 {
399                    dir.vset(id as usize, 1.0);
400                } else {
401                    dir.vset(id as usize - 2, -1.0);
402                }
403                Some(dir)
404            }
405            FeatureId::Vertex(id) => {
406                let mut dir: Vector = Vector::ZERO;
407
408                match id {
409                    0b00 => {
410                        dir.x = 1.0;
411                        dir.y = 1.0;
412                    }
413                    0b01 => {
414                        dir.y = 1.0;
415                        dir.x = -1.0;
416                    }
417                    0b11 => {
418                        dir.x = -1.0;
419                        dir.y = -1.0;
420                    }
421                    0b10 => {
422                        dir.y = -1.0;
423                        dir.x = 1.0;
424                    }
425                    _ => return None,
426                }
427
428                Some(dir.normalize())
429            }
430            _ => None,
431        }
432    }
433
434    /// The normal of the given feature of this shape.
435    #[cfg(feature = "dim3")]
436    pub fn feature_normal(&self, feature: FeatureId) -> Option<Vector> {
437        match feature {
438            FeatureId::Face(id) => {
439                let mut dir: Vector = Vector::ZERO;
440
441                if id < 3 {
442                    dir.vset(id as usize, 1.0);
443                } else {
444                    dir.vset(id as usize - 3, -1.0);
445                }
446                Some(dir)
447            }
448            FeatureId::Edge(id) => {
449                let edge = id & 0b011;
450                let face1 = (edge + 1) % 3;
451                let face2 = (edge + 2) % 3;
452                let signs = id >> 2;
453
454                let mut dir: Vector = Vector::ZERO;
455
456                if signs & (1 << face1) != 0 {
457                    dir.vset(face1 as usize, -1.0)
458                } else {
459                    dir.vset(face1 as usize, 1.0)
460                }
461
462                if signs & (1 << face2) != 0 {
463                    dir.vset(face2 as usize, -1.0)
464                } else {
465                    dir.vset(face2 as usize, 1.0);
466                }
467
468                Some(dir.normalize())
469            }
470            FeatureId::Vertex(id) => {
471                let mut dir: Vector = Vector::ZERO;
472                for i in 0..3 {
473                    if id & (1 << i) != 0 {
474                        dir.vset(i, -1.0);
475                    } else {
476                        dir.vset(i, 1.0)
477                    }
478                }
479
480                Some(dir.normalize())
481            }
482            _ => None,
483        }
484    }
485}
486
487impl SupportMap for Cuboid {
488    #[inline]
489    fn local_support_point(&self, dir: Vector) -> Vector {
490        dir.copy_sign_to(self.half_extents)
491    }
492}
493
494/*
495impl ConvexPolyhedron for Cuboid {
496    fn vertex(&self, id: FeatureId) -> Vector {
497        let vid = id.unwrap_vertex();
498        let mut res = self.half_extents;
499
500        for i in 0..DIM {
501            if vid & (1 << i) != 0 {
502                res.vset(i, -res.vget(i))
503            }
504        }
505
506        res
507    }
508
509    #[cfg(feature = "dim3")]
510    fn edge(&self, id: FeatureId) -> (Vector, Vector, FeatureId, FeatureId) {
511        let eid = id.unwrap_edge();
512        let mut res = self.half_extents;
513
514        let edge_i = eid & 0b11;
515        let vertex_i = eid >> 2;
516
517        for i in 0..DIM {
518            if i as u32 != edge_i && (vertex_i & (1 << i) != 0) {
519                res.vset(i, -res.vget(i))
520            }
521        }
522
523        let p1 = res;
524        res.vset(edge_i as usize, -res.vget(edge_i as usize));
525        let p2 = res;
526        let vid1 = FeatureId::Vertex(vertex_i & !(1 << edge_i));
527        let vid2 = FeatureId::Vertex(vertex_i | (1 << edge_i));
528
529        (p1, p2, vid1, vid2)
530    }
531
532    fn face(&self, id: FeatureId, out: &mut ConvexPolygonalFeature) {
533        out.clear();
534
535        let i = id.unwrap_face() as usize;
536        let i1;
537        let sign;
538
539        if i < DIM {
540            i1 = i;
541            sign = 1.0;
542        } else {
543            i1 = i - DIM;
544            sign = -1.0;
545        }
546
547        #[cfg(feature = "dim2")]
548        {
549            let i2 = (i1 + 1) % 2;
550
551            let mut vertex = self.half_extents;
552            vertex.vset(i1, vertex.vget(i1) * sign);
553            vertex.vset(i2, vertex.vget(i2) * if i1 == 0 { -sign } else { sign });
554
555            let p1 = vertex;
556            vertex.vset(i2, -vertex.vget(i2));
557            let p2 = vertex;
558
559            let mut vertex_id1 = if sign < 0.0 {
560                1 << i1
561            } else {
562                0
563            };
564            let mut vertex_id2 = vertex_id1;
565            if p1.vget(i2) < 0.0 {
566                vertex_id1 |= 1 << i2;
567            } else {
568                vertex_id2 |= 1 << i2;
569            }
570
571            out.push(p1, FeatureId::Vertex(vertex_id1));
572            out.push(p2, FeatureId::Vertex(vertex_id2));
573
574            let mut normal: Vector = Vector::ZERO;
575            normal.vset(i1, sign);
576            out.set_normal(normal);
577            out.set_feature_id(FeatureId::Face(i as u32));
578        }
579        #[cfg(feature = "dim3")]
580        {
581            let i2 = (i1 + 1) % 3;
582            let i3 = (i1 + 2) % 3;
583            let (edge_i2, edge_i3) = if sign > 0.0 {
584                (i2, i3)
585            } else {
586                (i3, i2)
587            };
588            let mask_i2 = !(1 << edge_i2); // The masks are for ensuring each edge has a unique ID.
589            let mask_i3 = !(1 << edge_i3);
590            let mut vertex = self.half_extents;
591            vertex.vset(i1, vertex.vget(i1) * sign);
592
593            let (sbit, msbit) = if sign < 0.0 {
594                (1, 0)
595            } else {
596                (0, 1)
597            };
598            let mut vertex_id = sbit << i1;
599            out.push(vertex, FeatureId::Vertex(vertex_id));
600            out.push_edge_feature_id(FeatureId::Edge(
601                edge_i2 as u32 | ((vertex_id & mask_i2) << 2),
602            ));
603
604            vertex.vset(i2, -sign * self.half_extents.vget(i2));
605            vertex.vset(i3, sign * self.half_extents.vget(i3));
606            vertex_id |= msbit << i2 | sbit << i3;
607            out.push(vertex, FeatureId::Vertex(vertex_id));
608            out.push_edge_feature_id(FeatureId::Edge(
609                edge_i3 as u32 | ((vertex_id & mask_i3) << 2),
610            ));
611
612            vertex.vset(i2, -self.half_extents.vget(i2));
613            vertex.vset(i3, -self.half_extents.vget(i3));
614            vertex_id |= 1 << i2 | 1 << i3;
615            out.push(vertex, FeatureId::Vertex(vertex_id));
616            out.push_edge_feature_id(FeatureId::Edge(
617                edge_i2 as u32 | ((vertex_id & mask_i2) << 2),
618            ));
619
620            vertex.vset(i2, sign * self.half_extents.vget(i2));
621            vertex.vset(i3, -sign * self.half_extents.vget(i3));
622            vertex_id = sbit << i1 | sbit << i2 | msbit << i3;
623            out.push(vertex, FeatureId::Vertex(vertex_id));
624            out.push_edge_feature_id(FeatureId::Edge(
625                edge_i3 as u32 | ((vertex_id & mask_i3) << 2),
626            ));
627
628            let mut normal: Vector = Vector::ZERO;
629            normal.vset(i1, sign);
630            out.set_normal(normal);
631
632            if sign > 0.0 {
633                out.set_feature_id(FeatureId::Face(i1 as u32));
634            } else {
635                out.set_feature_id(FeatureId::Face(i1 as u32 + 3));
636            }
637
638            out.recompute_edge_normals();
639        }
640    }
641
642    fn support_face_toward(
643        &self,
644        m: &Pose,
645        dir: Vector,
646        out: &mut ConvexPolygonalFeature,
647    ) {
648        out.clear();
649        let local_dir = m.inverse_transform_vector(dir);
650        let imax = iamax(local_dir);
651
652        if local_dir.vget(imax) > 0.0 {
653            self.face(FeatureId::Face(imax as u32), out);
654            out.transform_by(m);
655        } else {
656            self.face(FeatureId::Face((imax + DIM) as u32), out);
657            out.transform_by(m);
658        }
659    }
660
661    fn support_feature_toward(
662        &self,
663        m: &Pose,
664        dir: Vector,
665        angle: Real,
666        out: &mut ConvexPolygonalFeature,
667    ) {
668        let local_dir = m.inverse_transform_vector(dir);
669        let cang = <Real as ComplexField>::cos(angle);
670        let mut support_point = self.half_extents;
671
672        out.clear();
673
674        #[cfg(feature = "dim2")]
675        {
676            let mut support_point_id = 0;
677            for i1 in 0..2 {
678                let sign = local_dir.vget(i1).signum();
679                if sign * local_dir.vget(i1) >= cang {
680                    if sign > 0.0 {
681                        self.face(FeatureId::Face(i1 as u32), out);
682                        out.transform_by(m);
683                    } else {
684                        self.face(FeatureId::Face(i1 as u32 + 2), out);
685                        out.transform_by(m);
686                    }
687                    return;
688                } else {
689                    if sign < 0.0 {
690                        support_point_id |= 1 << i1;
691                    }
692                    support_point.vset(i1, support_point.vget(i1) * sign);
693                }
694            }
695
696            // We are not on a face, return the support vertex.
697            out.push(
698                m * support_point,
699                FeatureId::Vertex(support_point_id),
700            );
701            out.set_feature_id(FeatureId::Vertex(support_point_id));
702        }
703
704        #[cfg(feature = "dim3")]
705        {
706            let sang = <Real as ComplexField>::sin(angle);
707            let mut support_point_id = 0;
708
709            // Check faces.
710            for i1 in 0..3 {
711                let sign = local_dir.vget(i1).signum();
712                if sign * local_dir.vget(i1) >= cang {
713                    if sign > 0.0 {
714                        self.face(FeatureId::Face(i1 as u32), out);
715                        out.transform_by(m);
716                    } else {
717                        self.face(FeatureId::Face(i1 as u32 + 3), out);
718                        out.transform_by(m);
719                    }
720                    return;
721                } else {
722                    if sign < 0.0 {
723                        support_point.vset(i1, support_point.vget(i1) * sign);
724                        support_point_id |= 1 << i1;
725                    }
726                }
727            }
728
729            // Check edges.
730            for i in 0..3 {
731                let sign = local_dir.vget(i).signum();
732
733                // sign * local_dir.vget(i) <= cos(pi / 2 - angle)
734                if sign * local_dir.vget(i) <= sang {
735                    support_point.vset(i, -self.half_extents.vget(i));
736                    let p1 = support_point;
737                    support_point.vset(i, self.half_extents.vget(i));
738                    let p2 = support_point;
739                    let p2_id = support_point_id & !(1 << i);
740                    out.push(m * p1, FeatureId::Vertex(support_point_id | (1 << i)));
741                    out.push(m * p2, FeatureId::Vertex(p2_id));
742
743                    let edge_id = FeatureId::Edge(i as u32 | (p2_id << 2));
744                    out.push_edge_feature_id(edge_id);
745                    out.set_feature_id(edge_id);
746                    return;
747                }
748            }
749
750            // We are not on a face or edge, return the support vertex.
751            out.push(
752                m * support_point,
753                FeatureId::Vertex(support_point_id),
754            );
755            out.set_feature_id(FeatureId::Vertex(support_point_id));
756        }
757    }
758
759    fn support_feature_id_toward(&self, local_dir: Vector) -> FeatureId {
760        let one_degree: Real = (f64::consts::PI / 180.0) as Real;
761        let cang = <Real as ComplexField>::cos(one_degree);
762
763        #[cfg(feature = "dim2")]
764        {
765            let mut support_point_id = 0;
766            for i1 in 0..2 {
767                let sign = local_dir.vget(i1).signum();
768                if sign * local_dir.vget(i1) >= cang {
769                    if sign > 0.0 {
770                        return FeatureId::Face(i1 as u32);
771                    } else {
772                        return FeatureId::Face(i1 as u32 + 2);
773                    }
774                } else {
775                    if sign < 0.0 {
776                        support_point_id |= 1 << i1;
777                    }
778                }
779            }
780
781            // We are not on a face, return the support vertex.
782            FeatureId::Vertex(support_point_id)
783        }
784
785        #[cfg(feature = "dim3")]
786        {
787            let sang = <Real as ComplexField>::sin(one_degree);
788            let mut support_point_id = 0;
789
790            // Check faces.
791            for i1 in 0..3 {
792                let sign = local_dir.vget(i1).signum();
793                if sign * local_dir.vget(i1) >= cang {
794                    if sign > 0.0 {
795                        return FeatureId::Face(i1 as u32);
796                    } else {
797                        return FeatureId::Face(i1 as u32 + 3);
798                    }
799                } else {
800                    if sign < 0.0 {
801                        support_point_id |= 1 << i1;
802                    }
803                }
804            }
805
806            // Check edges.
807            for i in 0..3 {
808                let sign = local_dir.vget(i).signum();
809
810                // sign * local_dir.vget(i) <= cos(pi / 2 - angle)
811                if sign * local_dir.vget(i) <= sang {
812                    let mask_i = !(1 << i); // To ensure each edge has a unique id.
813                    return FeatureId::Edge(i as u32 | ((support_point_id & mask_i) << 2));
814                }
815            }
816
817            FeatureId::Vertex(support_point_id)
818        }
819    }
820}
821*/