parry2d/shape/cuboid.rs
1//! Support mapping based Cuboid shape.
2
3#[cfg(feature = "dim3")]
4use crate::math::Real;
5use crate::math::{Vector, VectorExt};
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
188 let vertices = match i {
189 0 => [
190 Vector::new(he.x, local_dir.y.copy_sign_to(he.y)),
191 Vector::new(-he.x, local_dir.y.copy_sign_to(he.y)),
192 ],
193 _ => [
194 Vector::new(local_dir.x.copy_sign_to(he.x), he.y),
195 Vector::new(local_dir.x.copy_sign_to(he.x), -he.y),
196 ],
197 };
198
199 let vid1 = Self::vertex_feature_id(vertices[0]);
200 let vid2 = Self::vertex_feature_id(vertices[1]);
201 let fid = (vid1.max(vid2) << 2) | vid1.min(vid2) | 0b11_00_00;
202
203 PolygonalFeature {
204 vertices,
205 vids: PackedFeatureId::vertices([vid1, vid2]),
206 fid: PackedFeatureId::face(fid),
207 num_vertices: 2,
208 }
209 }
210
211 /// Return the face of this cuboid with a normal that maximizes
212 /// the dot product with `local_dir`.
213 #[cfg(feature = "dim3")]
214 pub fn support_feature(&self, local_dir: Vector) -> PolygonalFeature {
215 // TODO: this should actually return the feature.
216 // And we should change all the callers of this method to use
217 // `.support_face` instead of this method to preserve their old behavior.
218 self.support_face(local_dir)
219 /*
220 const MAX_DOT_THRESHOLD: Real = crate::utils::COS_10_DEGREES;
221 const MIN_DOT_THRESHOLD: Real = 1.0 - MAX_DOT_THRESHOLD;
222
223 let amax = local_dir.amax();
224 let amin = local_dir.amin();
225
226 if amax > MAX_DOT_THRESHOLD {
227 // Support face.
228 CuboidFeature::Face(support_face(self, local_dir))
229 } else if amin < MIN_DOT_THRESHOLD {
230 // Support edge.
231 CuboidFeature::Edge(support_edge(self, local_dir))
232 } else {
233 // Support vertex.
234 CuboidFeature::Vertex(support_vertex(self, local_dir))
235 }
236 */
237 }
238
239 // #[cfg(feature = "dim3")
240 // pub(crate) fn support_vertex(&self, local_dir: Vector) -> CuboidFeatureVertex {
241 // let vertex = local_support_point(self, local_dir);
242 // let vid = vertex_feature_id(vertex);
243 //
244 // CuboidFeatureVertex { vertex, vid }
245 // }
246
247 /// Return the edge segment of this cuboid with a normal cone containing
248 /// a direction that that maximizes the dot product with `local_dir`.
249 #[cfg(feature = "dim3")]
250 pub fn local_support_edge_segment(&self, local_dir: Vector) -> Segment {
251 let he = self.half_extents;
252 let i = local_dir.abs().min_position();
253 let j = (i + 1) % 3;
254 let k = (i + 2) % 3;
255 let mut a = Vector::ZERO;
256 a.vset(i, he.vget(i));
257 a.vset(j, local_dir.vget(j).copy_sign_to(he.vget(j)));
258 a.vset(k, local_dir.vget(k).copy_sign_to(he.vget(k)));
259
260 let mut b = a;
261 b.vset(i, -he.vget(i));
262
263 Segment::new(a, b)
264 }
265
266 /// Computes the face with a normal that maximizes the dot-product with `local_dir`.
267 #[cfg(feature = "dim3")]
268 // The identity ors/shifts below are kept so the bit patterns line up with the comments
269 // documenting the vertex/edge numbering.
270 #[allow(clippy::identity_op)]
271 pub fn support_face(&self, local_dir: Vector) -> PolygonalFeature {
272 // NOTE: can we use the orthonormal basis of local_dir
273 // to make this AoSoA friendly?
274 let he = self.half_extents;
275 let imax = local_dir.abs().max_position();
276 #[expect(clippy::unnecessary_cast)]
277 let sign = match imax {
278 0 => local_dir.x.copy_sign_to(1.0 as Real),
279 1 => local_dir.y.copy_sign_to(1.0 as Real),
280 _ => local_dir.z.copy_sign_to(1.0 as Real),
281 };
282
283 let vertices = match imax {
284 0 => [
285 Vector::new(he.x * sign, he.y, he.z),
286 Vector::new(he.x * sign, -he.y, he.z),
287 Vector::new(he.x * sign, -he.y, -he.z),
288 Vector::new(he.x * sign, he.y, -he.z),
289 ],
290 1 => [
291 Vector::new(he.x, he.y * sign, he.z),
292 Vector::new(-he.x, he.y * sign, he.z),
293 Vector::new(-he.x, he.y * sign, -he.z),
294 Vector::new(he.x, he.y * sign, -he.z),
295 ],
296 _ => [
297 Vector::new(he.x, he.y, he.z * sign),
298 Vector::new(he.x, -he.y, he.z * sign),
299 Vector::new(-he.x, -he.y, he.z * sign),
300 Vector::new(-he.x, he.y, he.z * sign),
301 ],
302 };
303
304 pub fn vid(i: u32) -> u32 {
305 // Each vertex has an even feature id.
306 i * 2
307 }
308
309 let sign_index = ((sign as isize + 1) / 2) as u32;
310 // The vertex id as numbered depending on the sign of the vertex
311 // component. A + sign means the corresponding bit is 0 while a -
312 // sign means the corresponding bit is 1.
313 // For example the vertex [2.0, -1.0, -3.0] has the id 0b011
314 let vids = match imax {
315 0 => {
316 let sbit = sign_index << 2;
317 [
318 vid(0b000 | sbit),
319 vid(0b010 | sbit),
320 vid(0b011 | sbit),
321 vid(0b001 | sbit),
322 ]
323 }
324 1 => {
325 let sbit = sign_index << 1;
326 [
327 vid(0b000 | sbit),
328 vid(0b100 | sbit),
329 vid(0b101 | sbit),
330 vid(0b001 | sbit),
331 ]
332 }
333 _ => {
334 let sbit = sign_index;
335 [
336 vid(0b000 | sbit),
337 vid(0b010 | sbit),
338 vid(0b110 | sbit),
339 vid(0b100 | sbit),
340 ]
341 }
342 };
343
344 // The feature ids of edges is obtained from the vertex ids
345 // of their endpoints.
346 // Assuming vid1 > vid2, we do: (vid1 << 3) | vid2 | 0b11000000
347 //
348 let eids = match imax {
349 0 => {
350 let sbits = (sign_index << 2) | (sign_index << 5); // 0b00_100_100
351 [
352 0b11_010_000 | sbits,
353 0b11_011_010 | sbits,
354 0b11_011_001 | sbits,
355 0b11_001_000 | sbits,
356 ]
357 }
358 1 => {
359 let sbits = (sign_index << 1) | (sign_index << 4); // 0b00_010_010
360 [
361 0b11_100_000 | sbits,
362 0b11_101_100 | sbits,
363 0b11_101_001 | sbits,
364 0b11_001_000 | sbits,
365 ]
366 }
367 _ => {
368 let sbits = (sign_index << 0) | (sign_index << 3); // 0b00_001_001
369 [
370 0b11_010_000 | sbits,
371 0b11_110_010 | sbits,
372 0b11_110_100 | sbits,
373 0b11_100_000 | sbits,
374 ]
375 }
376 };
377
378 // The face with normals [x, y, z] are numbered [10, 11, 12].
379 // The face with negated normals are numbered [13, 14, 15].
380 let fid = imax as u32 + sign_index * 3 + 10;
381
382 PolygonalFeature {
383 vertices,
384 vids: PackedFeatureId::vertices(vids),
385 eids: PackedFeatureId::edges(eids),
386 fid: PackedFeatureId::face(fid),
387 num_vertices: 4,
388 }
389 }
390
391 /// The normal of the given feature of this shape.
392 #[cfg(feature = "dim2")]
393 pub fn feature_normal(&self, feature: FeatureId) -> Option<Vector> {
394 match feature {
395 FeatureId::Face(id) => {
396 let mut dir: Vector = Vector::ZERO;
397
398 if id < 2 {
399 dir.vset(id as usize, 1.0);
400 } else {
401 dir.vset(id as usize - 2, -1.0);
402 }
403 Some(dir)
404 }
405 FeatureId::Vertex(id) => {
406 let mut dir: Vector = Vector::ZERO;
407
408 match id {
409 0b00 => {
410 dir.x = 1.0;
411 dir.y = 1.0;
412 }
413 0b01 => {
414 dir.y = 1.0;
415 dir.x = -1.0;
416 }
417 0b11 => {
418 dir.x = -1.0;
419 dir.y = -1.0;
420 }
421 0b10 => {
422 dir.y = -1.0;
423 dir.x = 1.0;
424 }
425 _ => return None,
426 }
427
428 Some(dir.normalize())
429 }
430 _ => None,
431 }
432 }
433
434 /// The normal of the given feature of this shape.
435 #[cfg(feature = "dim3")]
436 pub fn feature_normal(&self, feature: FeatureId) -> Option<Vector> {
437 match feature {
438 FeatureId::Face(id) => {
439 let mut dir: Vector = Vector::ZERO;
440
441 if id < 3 {
442 dir.vset(id as usize, 1.0);
443 } else {
444 dir.vset(id as usize - 3, -1.0);
445 }
446 Some(dir)
447 }
448 FeatureId::Edge(id) => {
449 let edge = id & 0b011;
450 let face1 = (edge + 1) % 3;
451 let face2 = (edge + 2) % 3;
452 let signs = id >> 2;
453
454 let mut dir: Vector = Vector::ZERO;
455
456 if signs & (1 << face1) != 0 {
457 dir.vset(face1 as usize, -1.0)
458 } else {
459 dir.vset(face1 as usize, 1.0)
460 }
461
462 if signs & (1 << face2) != 0 {
463 dir.vset(face2 as usize, -1.0)
464 } else {
465 dir.vset(face2 as usize, 1.0);
466 }
467
468 Some(dir.normalize())
469 }
470 FeatureId::Vertex(id) => {
471 let mut dir: Vector = Vector::ZERO;
472 for i in 0..3 {
473 if id & (1 << i) != 0 {
474 dir.vset(i, -1.0);
475 } else {
476 dir.vset(i, 1.0)
477 }
478 }
479
480 Some(dir.normalize())
481 }
482 _ => None,
483 }
484 }
485}
486
487impl SupportMap for Cuboid {
488 #[inline]
489 fn local_support_point(&self, dir: Vector) -> Vector {
490 dir.copy_sign_to(self.half_extents)
491 }
492}
493
494/*
495impl ConvexPolyhedron for Cuboid {
496 fn vertex(&self, id: FeatureId) -> Vector {
497 let vid = id.unwrap_vertex();
498 let mut res = self.half_extents;
499
500 for i in 0..DIM {
501 if vid & (1 << i) != 0 {
502 res.vset(i, -res.vget(i))
503 }
504 }
505
506 res
507 }
508
509 #[cfg(feature = "dim3")]
510 fn edge(&self, id: FeatureId) -> (Vector, Vector, FeatureId, FeatureId) {
511 let eid = id.unwrap_edge();
512 let mut res = self.half_extents;
513
514 let edge_i = eid & 0b11;
515 let vertex_i = eid >> 2;
516
517 for i in 0..DIM {
518 if i as u32 != edge_i && (vertex_i & (1 << i) != 0) {
519 res.vset(i, -res.vget(i))
520 }
521 }
522
523 let p1 = res;
524 res.vset(edge_i as usize, -res.vget(edge_i as usize));
525 let p2 = res;
526 let vid1 = FeatureId::Vertex(vertex_i & !(1 << edge_i));
527 let vid2 = FeatureId::Vertex(vertex_i | (1 << edge_i));
528
529 (p1, p2, vid1, vid2)
530 }
531
532 fn face(&self, id: FeatureId, out: &mut ConvexPolygonalFeature) {
533 out.clear();
534
535 let i = id.unwrap_face() as usize;
536 let i1;
537 let sign;
538
539 if i < DIM {
540 i1 = i;
541 sign = 1.0;
542 } else {
543 i1 = i - DIM;
544 sign = -1.0;
545 }
546
547 #[cfg(feature = "dim2")]
548 {
549 let i2 = (i1 + 1) % 2;
550
551 let mut vertex = self.half_extents;
552 vertex.vset(i1, vertex.vget(i1) * sign);
553 vertex.vset(i2, vertex.vget(i2) * if i1 == 0 { -sign } else { sign });
554
555 let p1 = vertex;
556 vertex.vset(i2, -vertex.vget(i2));
557 let p2 = vertex;
558
559 let mut vertex_id1 = if sign < 0.0 {
560 1 << i1
561 } else {
562 0
563 };
564 let mut vertex_id2 = vertex_id1;
565 if p1.vget(i2) < 0.0 {
566 vertex_id1 |= 1 << i2;
567 } else {
568 vertex_id2 |= 1 << i2;
569 }
570
571 out.push(p1, FeatureId::Vertex(vertex_id1));
572 out.push(p2, FeatureId::Vertex(vertex_id2));
573
574 let mut normal: Vector = Vector::ZERO;
575 normal.vset(i1, sign);
576 out.set_normal(normal);
577 out.set_feature_id(FeatureId::Face(i as u32));
578 }
579 #[cfg(feature = "dim3")]
580 {
581 let i2 = (i1 + 1) % 3;
582 let i3 = (i1 + 2) % 3;
583 let (edge_i2, edge_i3) = if sign > 0.0 {
584 (i2, i3)
585 } else {
586 (i3, i2)
587 };
588 let mask_i2 = !(1 << edge_i2); // The masks are for ensuring each edge has a unique ID.
589 let mask_i3 = !(1 << edge_i3);
590 let mut vertex = self.half_extents;
591 vertex.vset(i1, vertex.vget(i1) * sign);
592
593 let (sbit, msbit) = if sign < 0.0 {
594 (1, 0)
595 } else {
596 (0, 1)
597 };
598 let mut vertex_id = sbit << i1;
599 out.push(vertex, FeatureId::Vertex(vertex_id));
600 out.push_edge_feature_id(FeatureId::Edge(
601 edge_i2 as u32 | ((vertex_id & mask_i2) << 2),
602 ));
603
604 vertex.vset(i2, -sign * self.half_extents.vget(i2));
605 vertex.vset(i3, sign * self.half_extents.vget(i3));
606 vertex_id |= msbit << i2 | sbit << i3;
607 out.push(vertex, FeatureId::Vertex(vertex_id));
608 out.push_edge_feature_id(FeatureId::Edge(
609 edge_i3 as u32 | ((vertex_id & mask_i3) << 2),
610 ));
611
612 vertex.vset(i2, -self.half_extents.vget(i2));
613 vertex.vset(i3, -self.half_extents.vget(i3));
614 vertex_id |= 1 << i2 | 1 << i3;
615 out.push(vertex, FeatureId::Vertex(vertex_id));
616 out.push_edge_feature_id(FeatureId::Edge(
617 edge_i2 as u32 | ((vertex_id & mask_i2) << 2),
618 ));
619
620 vertex.vset(i2, sign * self.half_extents.vget(i2));
621 vertex.vset(i3, -sign * self.half_extents.vget(i3));
622 vertex_id = sbit << i1 | sbit << i2 | msbit << i3;
623 out.push(vertex, FeatureId::Vertex(vertex_id));
624 out.push_edge_feature_id(FeatureId::Edge(
625 edge_i3 as u32 | ((vertex_id & mask_i3) << 2),
626 ));
627
628 let mut normal: Vector = Vector::ZERO;
629 normal.vset(i1, sign);
630 out.set_normal(normal);
631
632 if sign > 0.0 {
633 out.set_feature_id(FeatureId::Face(i1 as u32));
634 } else {
635 out.set_feature_id(FeatureId::Face(i1 as u32 + 3));
636 }
637
638 out.recompute_edge_normals();
639 }
640 }
641
642 fn support_face_toward(
643 &self,
644 m: &Pose,
645 dir: Vector,
646 out: &mut ConvexPolygonalFeature,
647 ) {
648 out.clear();
649 let local_dir = m.inverse_transform_vector(dir);
650 let imax = iamax(local_dir);
651
652 if local_dir.vget(imax) > 0.0 {
653 self.face(FeatureId::Face(imax as u32), out);
654 out.transform_by(m);
655 } else {
656 self.face(FeatureId::Face((imax + DIM) as u32), out);
657 out.transform_by(m);
658 }
659 }
660
661 fn support_feature_toward(
662 &self,
663 m: &Pose,
664 dir: Vector,
665 angle: Real,
666 out: &mut ConvexPolygonalFeature,
667 ) {
668 let local_dir = m.inverse_transform_vector(dir);
669 let cang = <Real as ComplexField>::cos(angle);
670 let mut support_point = self.half_extents;
671
672 out.clear();
673
674 #[cfg(feature = "dim2")]
675 {
676 let mut support_point_id = 0;
677 for i1 in 0..2 {
678 let sign = local_dir.vget(i1).signum();
679 if sign * local_dir.vget(i1) >= cang {
680 if sign > 0.0 {
681 self.face(FeatureId::Face(i1 as u32), out);
682 out.transform_by(m);
683 } else {
684 self.face(FeatureId::Face(i1 as u32 + 2), out);
685 out.transform_by(m);
686 }
687 return;
688 } else {
689 if sign < 0.0 {
690 support_point_id |= 1 << i1;
691 }
692 support_point.vset(i1, support_point.vget(i1) * sign);
693 }
694 }
695
696 // We are not on a face, return the support vertex.
697 out.push(
698 m * support_point,
699 FeatureId::Vertex(support_point_id),
700 );
701 out.set_feature_id(FeatureId::Vertex(support_point_id));
702 }
703
704 #[cfg(feature = "dim3")]
705 {
706 let sang = <Real as ComplexField>::sin(angle);
707 let mut support_point_id = 0;
708
709 // Check faces.
710 for i1 in 0..3 {
711 let sign = local_dir.vget(i1).signum();
712 if sign * local_dir.vget(i1) >= cang {
713 if sign > 0.0 {
714 self.face(FeatureId::Face(i1 as u32), out);
715 out.transform_by(m);
716 } else {
717 self.face(FeatureId::Face(i1 as u32 + 3), out);
718 out.transform_by(m);
719 }
720 return;
721 } else {
722 if sign < 0.0 {
723 support_point.vset(i1, support_point.vget(i1) * sign);
724 support_point_id |= 1 << i1;
725 }
726 }
727 }
728
729 // Check edges.
730 for i in 0..3 {
731 let sign = local_dir.vget(i).signum();
732
733 // sign * local_dir.vget(i) <= cos(pi / 2 - angle)
734 if sign * local_dir.vget(i) <= sang {
735 support_point.vset(i, -self.half_extents.vget(i));
736 let p1 = support_point;
737 support_point.vset(i, self.half_extents.vget(i));
738 let p2 = support_point;
739 let p2_id = support_point_id & !(1 << i);
740 out.push(m * p1, FeatureId::Vertex(support_point_id | (1 << i)));
741 out.push(m * p2, FeatureId::Vertex(p2_id));
742
743 let edge_id = FeatureId::Edge(i as u32 | (p2_id << 2));
744 out.push_edge_feature_id(edge_id);
745 out.set_feature_id(edge_id);
746 return;
747 }
748 }
749
750 // We are not on a face or edge, return the support vertex.
751 out.push(
752 m * support_point,
753 FeatureId::Vertex(support_point_id),
754 );
755 out.set_feature_id(FeatureId::Vertex(support_point_id));
756 }
757 }
758
759 fn support_feature_id_toward(&self, local_dir: Vector) -> FeatureId {
760 let one_degree: Real = (f64::consts::PI / 180.0) as Real;
761 let cang = <Real as ComplexField>::cos(one_degree);
762
763 #[cfg(feature = "dim2")]
764 {
765 let mut support_point_id = 0;
766 for i1 in 0..2 {
767 let sign = local_dir.vget(i1).signum();
768 if sign * local_dir.vget(i1) >= cang {
769 if sign > 0.0 {
770 return FeatureId::Face(i1 as u32);
771 } else {
772 return FeatureId::Face(i1 as u32 + 2);
773 }
774 } else {
775 if sign < 0.0 {
776 support_point_id |= 1 << i1;
777 }
778 }
779 }
780
781 // We are not on a face, return the support vertex.
782 FeatureId::Vertex(support_point_id)
783 }
784
785 #[cfg(feature = "dim3")]
786 {
787 let sang = <Real as ComplexField>::sin(one_degree);
788 let mut support_point_id = 0;
789
790 // Check faces.
791 for i1 in 0..3 {
792 let sign = local_dir.vget(i1).signum();
793 if sign * local_dir.vget(i1) >= cang {
794 if sign > 0.0 {
795 return FeatureId::Face(i1 as u32);
796 } else {
797 return FeatureId::Face(i1 as u32 + 3);
798 }
799 } else {
800 if sign < 0.0 {
801 support_point_id |= 1 << i1;
802 }
803 }
804 }
805
806 // Check edges.
807 for i in 0..3 {
808 let sign = local_dir.vget(i).signum();
809
810 // sign * local_dir.vget(i) <= cos(pi / 2 - angle)
811 if sign * local_dir.vget(i) <= sang {
812 let mask_i = !(1 << i); // To ensure each edge has a unique id.
813 return FeatureId::Edge(i as u32 | ((support_point_id & mask_i) << 2));
814 }
815 }
816
817 FeatureId::Vertex(support_point_id)
818 }
819 }
820}
821*/