Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,18 @@
## 0.31.0
## 0.31.1

### Modified

- The `CompositeShapeRef` queries (`cast_local_ray_and_get_normal`, `project_local_point`,
`project_local_point_and_get_feature`, `project_local_point_and_get_location`, `cast_shape`,
`cast_shape_nonlinear`, `intersects_shape`, `contact_with_shape`, `distance_to_shape`) no longer
overwrite the result's sub-shape with the index of the part of the composite that answered:
they return that index alongside the result, which stays as the part reported it. The caller
stores it where it wants, so a composite of composites (a scene of trimeshes) can keep both
levels. `intersects_shape` returns `None` instead of a non-intersecting result. The `Shape`
impls of `TriMesh`, `Polyline` and `Compound`, and the `*_composite_shape_shape` free functions,
still report the part in the result's `subshape`/`subshape1`.

## 0.31.0 (yanked)

### Modified

Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ members = [
resolver = "2"

[workspace.package]
version = "0.31.0"
version = "0.31.1"
authors = ["Sébastien Crozet <developer@crozet.re>"]
documentation = "https://parry.rs/docs"
homepage = "https://parry.rs"
Expand Down
26 changes: 16 additions & 10 deletions src/query/contact/contact_composite_shape_shape.rs
Original file line number Diff line number Diff line change
@@ -1,40 +1,39 @@
use crate::bounding_volume::BoundingVolume;
use crate::math::{Pose, Real};
use crate::query::{Contact, QueryDispatcher};
use crate::shape::{CompositeShape, CompositeShapeRef, Shape};
use crate::shape::{CompositeShape, CompositeShapeRef, Shape, SubShapeId};
use crate::utils::PoseOpt;

impl<S: ?Sized + CompositeShape> CompositeShapeRef<'_, S> {
/// Returns the closest/deepest contact between `self` and the given `shape2` positioned at
/// `pose12` relative to `self`.
///
/// Returns `None` if `self` and `shape2` are separated by a distance larger than
/// `prediction`. Otherwise the contact's `subshape1` says which sub-shape of `self` it is on.
/// `prediction`. Otherwise returns the index of the sub-shape of `self` the contact is on
/// alongside the contact, which is left as that sub-shape reported it (its `subshape1` is the
/// sub-shape's own when it is a composite too, and `subshape2` is `shape2`'s).
pub fn contact_with_shape<D: ?Sized + QueryDispatcher>(
&self,
dispatcher: &D,
pose12: &Pose,
shape2: &dyn Shape,
prediction: Real,
) -> Option<Contact> {
) -> Option<(SubShapeId, Contact)> {
let ls_aabb2 = shape2.compute_aabb(pose12).loosened(prediction);
let mut result = None::<Contact>;
let mut result = None::<(SubShapeId, Contact)>;

for part_id in self.0.bvh().intersect_aabb(&ls_aabb2) {
self.0.map_part_at(part_id, &mut |part_pos1, part1, _| {
if let Ok(Some(mut c)) =
dispatcher.contact(&part_pos1.inv_mul(pose12), part1, shape2, prediction)
{
let replace = result.is_none_or(|cbest| c.dist < cbest.dist);
let replace = result.is_none_or(|(_, cbest)| c.dist < cbest.dist);

if replace {
if let Some(part_pos1) = part_pos1 {
c.transform1_by_mut(part_pos1);
}
// `subshape2` is left as the dispatch set it: `shape2` may be a composite
// too, and only it knows which of its parts answered.
c.subshape1 = part_id;
result = Some(c)
result = Some((part_id, c))
}
}
});
Expand All @@ -56,7 +55,14 @@ where
D: ?Sized + QueryDispatcher,
G1: ?Sized + CompositeShape,
{
CompositeShapeRef(g1).contact_with_shape(dispatcher, pose12, g2, prediction)
// `subshape2` is left as the dispatch set it: `g2` may be a composite too, and only it
// knows which of its parts answered.
CompositeShapeRef(g1)
.contact_with_shape(dispatcher, pose12, g2, prediction)
.map(|(part_id, mut c)| {
c.subshape1 = part_id;
c
})
}

/// Best contact between a shape and a composite (`Mesh`, `Compound`) shape.
Expand Down
58 changes: 29 additions & 29 deletions src/query/distance/distance_composite_shape_shape.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,50 +2,44 @@ use crate::bounding_volume::Aabb;
use crate::math::{Pose, Real};
use crate::partitioning::BvhNode;
use crate::query::{QueryDispatcher, ShapeDistance};
use crate::shape::{CompositeShapeRef, Shape, TypedCompositeShape};
use crate::shape::{CompositeShapeRef, Shape, SubShapeId, TypedCompositeShape};
use crate::utils::PoseOpt;

impl<S: ?Sized + TypedCompositeShape> CompositeShapeRef<'_, S> {
/// Calculates the closest distance between `self` and the given `shape2` positioned at
/// `pose12` relative to `self`.
///
/// The result's `subshape1` says which sub-shape of `self` is closest to `shape2`.
/// Returns the index of the sub-shape of `self` closest to `shape2` alongside the distance,
/// which is left as that sub-shape reported it (its `subshape1` is the sub-shape's own when
/// it is a composite too, and `subshape2` is `shape2`'s).
pub fn distance_to_shape<D: ?Sized + QueryDispatcher>(
&self,
dispatcher: &D,
pose12: &Pose,
shape2: &dyn Shape,
) -> Option<ShapeDistance> {
) -> Option<(SubShapeId, ShapeDistance)> {
let ls_aabb2 = shape2.compute_aabb(pose12);
let msum_shift = -ls_aabb2.center();
let msum_margin = ls_aabb2.half_extents();

self.0
.bvh()
.find_best(
Real::MAX,
|node: &BvhNode, _| {
// Compute the minkowski sum of the two Aabbs.
let msum = Aabb {
mins: node.mins() + msum_shift - msum_margin,
maxs: node.maxs() + msum_shift + msum_margin,
};
msum.distance_to_origin()
},
|part_id, _| {
self.0
.map_untyped_part_at(part_id, |part_pos1, part_g1, _| {
dispatcher.distance(&part_pos1.inv_mul(pose12), part_g1, shape2)
})?
.ok()
},
)
// `subshape2` is left as the dispatch set it: `shape2` may be a composite too, and only it
// knows which of its parts answered.
.map(|(part_id, mut result)| {
result.subshape1 = part_id;
result
})
self.0.bvh().find_best(
Real::MAX,
|node: &BvhNode, _| {
// Compute the minkowski sum of the two Aabbs.
let msum = Aabb {
mins: node.mins() + msum_shift - msum_margin,
maxs: node.maxs() + msum_shift + msum_margin,
};
msum.distance_to_origin()
},
|part_id, _| {
self.0
.map_untyped_part_at(part_id, |part_pos1, part_g1, _| {
dispatcher.distance(&part_pos1.inv_mul(pose12), part_g1, shape2)
})?
.ok()
},
)
}
}

Expand All @@ -60,8 +54,14 @@ where
D: ?Sized + QueryDispatcher,
G1: ?Sized + TypedCompositeShape,
{
// `subshape2` is left as the dispatch set it: `g2` may be a composite too, and only it
// knows which of its parts answered.
CompositeShapeRef(g1)
.distance_to_shape(dispatcher, pos12, g2)
.map(|(part_id, mut result)| {
result.subshape1 = part_id;
result
})
.unwrap_or(ShapeDistance::new(Real::MAX))
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,44 +2,37 @@ use crate::bounding_volume::BoundingVolume;
use crate::math::Pose;
use crate::partitioning::BvhNode;
use crate::query::{QueryDispatcher, ShapeIntersection};
use crate::shape::{CompositeShapeRef, Shape, TypedCompositeShape};
use crate::shape::{CompositeShapeRef, Shape, SubShapeId, TypedCompositeShape};
use crate::utils::PoseOpt;

impl<S: ?Sized + TypedCompositeShape> CompositeShapeRef<'_, S> {
/// Tests whether the given other `shape`, positioned at `pose12` relative to `self`,
/// intersects `self`.
///
/// The result's `subshape1` says which sub-shape of `self` it intersects.
/// Returns `None` when they do not intersect. Otherwise returns the index of a sub-shape of
/// `self` that `shape` intersects alongside that sub-shape's own result (its `subshape1` is
/// the sub-shape's own when it is a composite too, and `subshape2` is `shape`'s).
pub fn intersects_shape<D: ?Sized + QueryDispatcher>(
&self,
dispatcher: &D,
pose12: &Pose,
shape: &dyn Shape,
) -> ShapeIntersection {
) -> Option<(SubShapeId, ShapeIntersection)> {
let ls_aabb2 = shape.compute_aabb(pose12);
let found = self
.0
self.0
.bvh()
.leaves(|node: &BvhNode| node.aabb().intersects(&ls_aabb2))
.find_map(|leaf_id| {
self.0
.map_untyped_part_at(leaf_id, |part_pose1, sub1, _| {
// `shape` may be a composite too; keep the sub-shape it reported.
dispatcher
.intersection_test(&part_pose1.inv_mul(pose12), sub1, shape)
.ok()
.filter(|result| result.intersecting)
.map(|result| (leaf_id, result.subshape2))
.map(|result| (leaf_id, result))
})
.flatten()
});

match found {
Some((subshape1, subshape2)) => {
ShapeIntersection::new(true).with_subshapes(subshape1, subshape2)
}
None => ShapeIntersection::new(false),
}
})
}
}

Expand All @@ -54,7 +47,14 @@ where
D: ?Sized + QueryDispatcher,
G1: ?Sized + TypedCompositeShape,
{
CompositeShapeRef(g1).intersects_shape(dispatcher, pos12, g2)
// `subshape2` is left as the dispatch set it: `g2` may be a composite too, and only it
// knows which of its parts answered.
match CompositeShapeRef(g1).intersects_shape(dispatcher, pos12, g2) {
Some((part_id, result)) => {
ShapeIntersection::new(true).with_subshapes(part_id, result.subshape2)
}
None => ShapeIntersection::new(false),
}
}

/// Proximity between a shape and a composite (`Mesh`, `Compound`) shape.
Expand Down
Loading
Loading