parry3d/shape/
cuboid.rs

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