parry3d/shape/
cuboid.rs

1//! Support mapping based Cuboid shape.
2
3#[cfg(feature = "dim3")]
4use crate::math::Real;
5use crate::math::Vector;
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        let j = (i + 1) % 2;
188        let mut a = Vector::ZERO;
189        a[i] = he[i];
190        a[j] = he[j].copysign(local_dir[j]);
191
192        let mut b = a;
193        b[i] = -he[i];
194
195        let vid1 = Self::vertex_feature_id(a);
196        let vid2 = Self::vertex_feature_id(b);
197        let fid = (vid1.max(vid2) << 2) | vid1.min(vid2) | 0b11_00_00;
198
199        PolygonalFeature {
200            vertices: [a, b],
201            vids: PackedFeatureId::vertices([vid1, vid2]),
202            fid: PackedFeatureId::face(fid),
203            num_vertices: 2,
204        }
205    }
206
207    /// Return the face of this cuboid with a normal that maximizes
208    /// the dot product with `local_dir`.
209    #[cfg(feature = "dim3")]
210    pub fn support_feature(&self, local_dir: Vector) -> PolygonalFeature {
211        // TODO: this should actually return the feature.
212        // And we should change all the callers of this method to use
213        // `.support_face` instead of this method to preserve their old behavior.
214        self.support_face(local_dir)
215        /*
216        const MAX_DOT_THRESHOLD: Real = crate::utils::COS_10_DEGREES;
217        const MIN_DOT_THRESHOLD: Real = 1.0 - MAX_DOT_THRESHOLD;
218
219        let amax = local_dir.amax();
220        let amin = local_dir.amin();
221
222        if amax > MAX_DOT_THRESHOLD {
223            // Support face.
224            CuboidFeature::Face(support_face(self, local_dir))
225        } else if amin < MIN_DOT_THRESHOLD {
226            // Support edge.
227            CuboidFeature::Edge(support_edge(self, local_dir))
228        } else {
229            // Support vertex.
230            CuboidFeature::Vertex(support_vertex(self, local_dir))
231        }
232        */
233    }
234
235    // #[cfg(feature = "dim3")
236    // pub(crate) fn support_vertex(&self, local_dir: Vector) -> CuboidFeatureVertex {
237    //     let vertex = local_support_point(self, local_dir);
238    //     let vid = vertex_feature_id(vertex);
239    //
240    //     CuboidFeatureVertex { vertex, vid }
241    // }
242
243    /// Return the edge segment of this cuboid with a normal cone containing
244    /// a direction that that maximizes the dot product with `local_dir`.
245    #[cfg(feature = "dim3")]
246    pub fn local_support_edge_segment(&self, local_dir: Vector) -> Segment {
247        let he = self.half_extents;
248        let i = local_dir.abs().min_position();
249        let j = (i + 1) % 3;
250        let k = (i + 2) % 3;
251        let mut a = Vector::ZERO;
252        a[i] = he[i];
253        a[j] = he[j].copysign(local_dir[j]);
254        a[k] = he[k].copysign(local_dir[k]);
255
256        let mut b = a;
257        b[i] = -he[i];
258
259        Segment::new(a, b)
260    }
261
262    /// Computes the face with a normal that maximizes the dot-product with `local_dir`.
263    #[cfg(feature = "dim3")]
264    pub fn support_face(&self, local_dir: Vector) -> PolygonalFeature {
265        // NOTE: can we use the orthonormal basis of local_dir
266        // to make this AoSoA friendly?
267        let he = self.half_extents;
268        let imax = local_dir.abs().max_position();
269        #[expect(clippy::unnecessary_cast)]
270        let sign = (1.0 as Real).copysign(local_dir[imax]);
271
272        let vertices = match imax {
273            0 => [
274                Vector::new(he.x * sign, he.y, he.z),
275                Vector::new(he.x * sign, -he.y, he.z),
276                Vector::new(he.x * sign, -he.y, -he.z),
277                Vector::new(he.x * sign, he.y, -he.z),
278            ],
279            1 => [
280                Vector::new(he.x, he.y * sign, he.z),
281                Vector::new(-he.x, he.y * sign, he.z),
282                Vector::new(-he.x, he.y * sign, -he.z),
283                Vector::new(he.x, he.y * sign, -he.z),
284            ],
285            2 => [
286                Vector::new(he.x, he.y, he.z * sign),
287                Vector::new(he.x, -he.y, he.z * sign),
288                Vector::new(-he.x, -he.y, he.z * sign),
289                Vector::new(-he.x, he.y, he.z * sign),
290            ],
291            _ => unreachable!(),
292        };
293
294        pub fn vid(i: u32) -> u32 {
295            // Each vertex has an even feature id.
296            i * 2
297        }
298
299        let sign_index = ((sign as i8 + 1) / 2) as usize;
300        // The vertex id as numbered depending on the sign of the vertex
301        // component. A + sign means the corresponding bit is 0 while a -
302        // sign means the corresponding bit is 1.
303        // For exampl the vertex [2.0, -1.0, -3.0] has the id 0b011
304        let vids = match imax {
305            0 => [
306                [vid(0b000), vid(0b010), vid(0b011), vid(0b001)],
307                [vid(0b100), vid(0b110), vid(0b111), vid(0b101)],
308            ][sign_index],
309            1 => [
310                [vid(0b000), vid(0b100), vid(0b101), vid(0b001)],
311                [vid(0b010), vid(0b110), vid(0b111), vid(0b011)],
312            ][sign_index],
313            2 => [
314                [vid(0b000), vid(0b010), vid(0b110), vid(0b100)],
315                [vid(0b001), vid(0b011), vid(0b111), vid(0b101)],
316            ][sign_index],
317            _ => unreachable!(),
318        };
319
320        // The feature ids of edges is obtained from the vertex ids
321        // of their endpoints.
322        // Assuming vid1 > vid2, we do:   (vid1 << 3) | vid2 | 0b11000000
323        //
324        let eids = match imax {
325            0 => [
326                [0b11_010_000, 0b11_011_010, 0b11_011_001, 0b11_001_000],
327                [0b11_110_100, 0b11_111_110, 0b11_111_101, 0b11_101_100],
328            ][sign_index],
329            1 => [
330                [0b11_100_000, 0b11_101_100, 0b11_101_001, 0b11_001_000],
331                [0b11_110_010, 0b11_111_110, 0b11_111_011, 0b11_011_010],
332            ][sign_index],
333            2 => [
334                [0b11_010_000, 0b11_110_010, 0b11_110_100, 0b11_100_000],
335                [0b11_011_001, 0b11_111_011, 0b11_111_101, 0b11_101_001],
336            ][sign_index],
337            _ => unreachable!(),
338        };
339
340        // The face with normals [x, y, z] are numbered [10, 11, 12].
341        // The face with negated normals are numbered [13, 14, 15].
342        let fid = imax + sign_index * 3 + 10;
343
344        PolygonalFeature {
345            vertices,
346            vids: PackedFeatureId::vertices(vids),
347            eids: PackedFeatureId::edges(eids),
348            fid: PackedFeatureId::face(fid as u32),
349            num_vertices: 4,
350        }
351    }
352
353    /// The normal of the given feature of this shape.
354    #[cfg(feature = "dim2")]
355    pub fn feature_normal(&self, feature: FeatureId) -> Option<Vector> {
356        match feature {
357            FeatureId::Face(id) => {
358                let mut dir: Vector = Vector::ZERO;
359
360                if id < 2 {
361                    dir[id as usize] = 1.0;
362                } else {
363                    dir[id as usize - 2] = -1.0;
364                }
365                Some(dir)
366            }
367            FeatureId::Vertex(id) => {
368                let mut dir: Vector = Vector::ZERO;
369
370                match id {
371                    0b00 => {
372                        dir[0] = 1.0;
373                        dir[1] = 1.0;
374                    }
375                    0b01 => {
376                        dir[1] = 1.0;
377                        dir[0] = -1.0;
378                    }
379                    0b11 => {
380                        dir[0] = -1.0;
381                        dir[1] = -1.0;
382                    }
383                    0b10 => {
384                        dir[1] = -1.0;
385                        dir[0] = 1.0;
386                    }
387                    _ => return None,
388                }
389
390                Some(dir.normalize())
391            }
392            _ => None,
393        }
394    }
395
396    /// The normal of the given feature of this shape.
397    #[cfg(feature = "dim3")]
398    pub fn feature_normal(&self, feature: FeatureId) -> Option<Vector> {
399        match feature {
400            FeatureId::Face(id) => {
401                let mut dir: Vector = Vector::ZERO;
402
403                if id < 3 {
404                    dir[id as usize] = 1.0;
405                } else {
406                    dir[id as usize - 3] = -1.0;
407                }
408                Some(dir)
409            }
410            FeatureId::Edge(id) => {
411                let edge = id & 0b011;
412                let face1 = (edge + 1) % 3;
413                let face2 = (edge + 2) % 3;
414                let signs = id >> 2;
415
416                let mut dir: Vector = Vector::ZERO;
417
418                if signs & (1 << face1) != 0 {
419                    dir[face1 as usize] = -1.0
420                } else {
421                    dir[face1 as usize] = 1.0
422                }
423
424                if signs & (1 << face2) != 0 {
425                    dir[face2 as usize] = -1.0
426                } else {
427                    dir[face2 as usize] = 1.0;
428                }
429
430                Some(dir.normalize())
431            }
432            FeatureId::Vertex(id) => {
433                let mut dir: Vector = Vector::ZERO;
434                for i in 0..3 {
435                    if id & (1 << i) != 0 {
436                        dir[i] = -1.0;
437                    } else {
438                        dir[i] = 1.0
439                    }
440                }
441
442                Some(dir.normalize())
443            }
444            _ => None,
445        }
446    }
447}
448
449impl SupportMap for Cuboid {
450    #[inline]
451    fn local_support_point(&self, dir: Vector) -> Vector {
452        dir.copy_sign_to(self.half_extents)
453    }
454}
455
456/*
457impl ConvexPolyhedron for Cuboid {
458    fn vertex(&self, id: FeatureId) -> Vector {
459        let vid = id.unwrap_vertex();
460        let mut res = self.half_extents;
461
462        for i in 0..DIM {
463            if vid & (1 << i) != 0 {
464                res[i] = -res[i]
465            }
466        }
467
468        Vector::from(res)
469    }
470
471    #[cfg(feature = "dim3")]
472    fn edge(&self, id: FeatureId) -> (Vector, Vector, FeatureId, FeatureId) {
473        let eid = id.unwrap_edge();
474        let mut res = self.half_extents;
475
476        let edge_i = eid & 0b11;
477        let vertex_i = eid >> 2;
478
479        for i in 0..DIM {
480            if i as u32 != edge_i && (vertex_i & (1 << i) != 0) {
481                res[i] = -res[i]
482            }
483        }
484
485        let p1 = Vector::from(res);
486        res[edge_i as usize] = -res[edge_i as usize];
487        let p2 = Vector::from(res);
488        let vid1 = FeatureId::Vertex(vertex_i & !(1 << edge_i));
489        let vid2 = FeatureId::Vertex(vertex_i | (1 << edge_i));
490
491        (p1, p2, vid1, vid2)
492    }
493
494    fn face(&self, id: FeatureId, out: &mut ConvexPolygonalFeature) {
495        out.clear();
496
497        let i = id.unwrap_face() as usize;
498        let i1;
499        let sign;
500
501        if i < DIM {
502            i1 = i;
503            sign = 1.0;
504        } else {
505            i1 = i - DIM;
506            sign = -1.0;
507        }
508
509        #[cfg(feature = "dim2")]
510        {
511            let i2 = (i1 + 1) % 2;
512
513            let mut vertex = self.half_extents;
514            vertex[i1] *= sign;
515            vertex[i2] *= if i1 == 0 { -sign } else { sign };
516
517            let p1 = Vector::from(vertex);
518            vertex[i2] = -vertex[i2];
519            let p2 = Vector::from(vertex);
520
521            let mut vertex_id1 = if sign < 0.0 {
522                1 << i1
523            } else {
524                0
525            };
526            let mut vertex_id2 = vertex_id1;
527            if p1[i2] < 0.0 {
528                vertex_id1 |= 1 << i2;
529            } else {
530                vertex_id2 |= 1 << i2;
531            }
532
533            out.push(p1, FeatureId::Vertex(vertex_id1));
534            out.push(p2, FeatureId::Vertex(vertex_id2));
535
536            let mut normal: Vector = Vector::ZERO;
537            normal[i1] = sign;
538            out.set_normal(normal);
539            out.set_feature_id(FeatureId::Face(i as u32));
540        }
541        #[cfg(feature = "dim3")]
542        {
543            let i2 = (i1 + 1) % 3;
544            let i3 = (i1 + 2) % 3;
545            let (edge_i2, edge_i3) = if sign > 0.0 {
546                (i2, i3)
547            } else {
548                (i3, i2)
549            };
550            let mask_i2 = !(1 << edge_i2); // The masks are for ensuring each edge has a unique ID.
551            let mask_i3 = !(1 << edge_i3);
552            let mut vertex = self.half_extents;
553            vertex[i1] *= sign;
554
555            let (sbit, msbit) = if sign < 0.0 {
556                (1, 0)
557            } else {
558                (0, 1)
559            };
560            let mut vertex_id = sbit << i1;
561            out.push(Vector::from(vertex), FeatureId::Vertex(vertex_id));
562            out.push_edge_feature_id(FeatureId::Edge(
563                edge_i2 as u32 | ((vertex_id & mask_i2) << 2),
564            ));
565
566            vertex[i2] = -sign * self.half_extents[i2];
567            vertex[i3] = sign * self.half_extents[i3];
568            vertex_id |= msbit << i2 | sbit << i3;
569            out.push(Vector::from(vertex), FeatureId::Vertex(vertex_id));
570            out.push_edge_feature_id(FeatureId::Edge(
571                edge_i3 as u32 | ((vertex_id & mask_i3) << 2),
572            ));
573
574            vertex[i2] = -self.half_extents[i2];
575            vertex[i3] = -self.half_extents[i3];
576            vertex_id |= 1 << i2 | 1 << i3;
577            out.push(Vector::from(vertex), FeatureId::Vertex(vertex_id));
578            out.push_edge_feature_id(FeatureId::Edge(
579                edge_i2 as u32 | ((vertex_id & mask_i2) << 2),
580            ));
581
582            vertex[i2] = sign * self.half_extents[i2];
583            vertex[i3] = -sign * self.half_extents[i3];
584            vertex_id = sbit << i1 | sbit << i2 | msbit << i3;
585            out.push(Vector::from(vertex), FeatureId::Vertex(vertex_id));
586            out.push_edge_feature_id(FeatureId::Edge(
587                edge_i3 as u32 | ((vertex_id & mask_i3) << 2),
588            ));
589
590            let mut normal: Vector = Vector::ZERO;
591            normal[i1] = sign;
592            out.set_normal(normal);
593
594            if sign > 0.0 {
595                out.set_feature_id(FeatureId::Face(i1 as u32));
596            } else {
597                out.set_feature_id(FeatureId::Face(i1 as u32 + 3));
598            }
599
600            out.recompute_edge_normals();
601        }
602    }
603
604    fn support_face_toward(
605        &self,
606        m: &Pose,
607        dir: Vector,
608        out: &mut ConvexPolygonalFeature,
609    ) {
610        out.clear();
611        let local_dir = m.inverse_transform_vector(dir);
612        let imax = iamax(local_dir);
613
614        if local_dir[imax] > 0.0 {
615            self.face(FeatureId::Face(imax as u32), out);
616            out.transform_by(m);
617        } else {
618            self.face(FeatureId::Face((imax + DIM) as u32), out);
619            out.transform_by(m);
620        }
621    }
622
623    fn support_feature_toward(
624        &self,
625        m: &Pose,
626        dir: Vector,
627        angle: Real,
628        out: &mut ConvexPolygonalFeature,
629    ) {
630        let local_dir = m.inverse_transform_vector(dir);
631        let cang = <Real as ComplexField>::cos(angle);
632        let mut support_point = self.half_extents;
633
634        out.clear();
635
636        #[cfg(feature = "dim2")]
637        {
638            let mut support_point_id = 0;
639            for i1 in 0..2 {
640                let sign = local_dir[i1].signum();
641                if sign * local_dir[i1] >= cang {
642                    if sign > 0.0 {
643                        self.face(FeatureId::Face(i1 as u32), out);
644                        out.transform_by(m);
645                    } else {
646                        self.face(FeatureId::Face(i1 as u32 + 2), out);
647                        out.transform_by(m);
648                    }
649                    return;
650                } else {
651                    if sign < 0.0 {
652                        support_point_id |= 1 << i1;
653                    }
654                    support_point[i1] *= sign;
655                }
656            }
657
658            // We are not on a face, return the support vertex.
659            out.push(
660                m * Vector::from(support_point),
661                FeatureId::Vertex(support_point_id),
662            );
663            out.set_feature_id(FeatureId::Vertex(support_point_id));
664        }
665
666        #[cfg(feature = "dim3")]
667        {
668            let sang = <Real as ComplexField>::sin(angle);
669            let mut support_point_id = 0;
670
671            // Check faces.
672            for i1 in 0..3 {
673                let sign = local_dir[i1].signum();
674                if sign * local_dir[i1] >= cang {
675                    if sign > 0.0 {
676                        self.face(FeatureId::Face(i1 as u32), out);
677                        out.transform_by(m);
678                    } else {
679                        self.face(FeatureId::Face(i1 as u32 + 3), out);
680                        out.transform_by(m);
681                    }
682                    return;
683                } else {
684                    if sign < 0.0 {
685                        support_point[i1] *= sign;
686                        support_point_id |= 1 << i1;
687                    }
688                }
689            }
690
691            // Check edges.
692            for i in 0..3 {
693                let sign = local_dir[i].signum();
694
695                // sign * local_dir[i] <= cos(pi / 2 - angle)
696                if sign * local_dir[i] <= sang {
697                    support_point[i] = -self.half_extents[i];
698                    let p1 = Vector::from(support_point);
699                    support_point[i] = self.half_extents[i];
700                    let p2 = Vector::from(support_point);
701                    let p2_id = support_point_id & !(1 << i);
702                    out.push(m * p1, FeatureId::Vertex(support_point_id | (1 << i)));
703                    out.push(m * p2, FeatureId::Vertex(p2_id));
704
705                    let edge_id = FeatureId::Edge(i as u32 | (p2_id << 2));
706                    out.push_edge_feature_id(edge_id);
707                    out.set_feature_id(edge_id);
708                    return;
709                }
710            }
711
712            // We are not on a face or edge, return the support vertex.
713            out.push(
714                m * Vector::from(support_point),
715                FeatureId::Vertex(support_point_id),
716            );
717            out.set_feature_id(FeatureId::Vertex(support_point_id));
718        }
719    }
720
721    fn support_feature_id_toward(&self, local_dir: Vector) -> FeatureId {
722        let one_degree: Real = (f64::consts::PI / 180.0) as Real;
723        let cang = <Real as ComplexField>::cos(one_degree);
724
725        #[cfg(feature = "dim2")]
726        {
727            let mut support_point_id = 0;
728            for i1 in 0..2 {
729                let sign = local_dir[i1].signum();
730                if sign * local_dir[i1] >= cang {
731                    if sign > 0.0 {
732                        return FeatureId::Face(i1 as u32);
733                    } else {
734                        return FeatureId::Face(i1 as u32 + 2);
735                    }
736                } else {
737                    if sign < 0.0 {
738                        support_point_id |= 1 << i1;
739                    }
740                }
741            }
742
743            // We are not on a face, return the support vertex.
744            FeatureId::Vertex(support_point_id)
745        }
746
747        #[cfg(feature = "dim3")]
748        {
749            let sang = <Real as ComplexField>::sin(one_degree);
750            let mut support_point_id = 0;
751
752            // Check faces.
753            for i1 in 0..3 {
754                let sign = local_dir[i1].signum();
755                if sign * local_dir[i1] >= cang {
756                    if sign > 0.0 {
757                        return FeatureId::Face(i1 as u32);
758                    } else {
759                        return FeatureId::Face(i1 as u32 + 3);
760                    }
761                } else {
762                    if sign < 0.0 {
763                        support_point_id |= 1 << i1;
764                    }
765                }
766            }
767
768            // Check edges.
769            for i in 0..3 {
770                let sign = local_dir[i].signum();
771
772                // sign * local_dir[i] <= cos(pi / 2 - angle)
773                if sign * local_dir[i] <= sang {
774                    let mask_i = !(1 << i); // To ensure each edge has a unique id.
775                    return FeatureId::Edge(i as u32 | ((support_point_id & mask_i) << 2));
776                }
777            }
778
779            FeatureId::Vertex(support_point_id)
780        }
781    }
782}
783*/