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