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*/