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