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