-
Notifications
You must be signed in to change notification settings - Fork 287
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add blacklist to BodyNodeCollisionFilter #911
Conversation
Codecov Report
@@ Coverage Diff @@
## release-6.3 #911 +/- ##
==============================================
Coverage ? 50.65%
==============================================
Files ? 300
Lines ? 23482
Branches ? 3029
==============================================
Hits ? 11894
Misses ? 10249
Partials ? 1339
|
This is kind of a high-level thought which isn't particularly important, but I was thinking: The function name I wonder if the function name should be flipped on its head: Since we're deprecating the old API and introducing a new one here anyway, this seems like it would be a good time to make this change. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Most of my comments are about minor tweaks that I'd be interested in hearing your thoughts on. Overall, this is a good implementation of a much-needed feature.
dart/collision/CollisionFilter.hpp
Outdated
|
||
protected: | ||
/// Collision filters | ||
std::vector<CollisionFilter*> mFilters; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we're manually enforcing uniqueness on the entries of mFilters
, is there a reason we use std::vector
instead of std::unordered_set
? We don't appear to ever use random access for mFilters
(instead, we run through it using iterators), and I'm skeptical that we'll see a noticeable improvement in performance from std::vector
. I'd rather use the container that implicitly provides the properties that we want.
dart/collision/CollisionFilter.cpp
Outdated
std::swap(bodyNodeLess, bodyNodeGreater); | ||
|
||
// Add the pair only when it doesn't already exist | ||
const auto resultLeft = mBlackList.find(bodyNodeLess); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a super minor nitpick, but we could use the built-in insert
operator of map
and set
instead of all this transactional logic. The insert
function won't actually insert anything into a key that already exists; it'll just return an iterator to the existing entry in the map. That would also avoid doing two lookups (one to check for existence and a second to insert the entry).
I created an example implementation here which you can feel free to merge if you like it.
dart/collision/CollisionFilter.cpp
Outdated
const bool foundLeft = (resultLeft != mBlackList.end()); | ||
if (foundLeft) | ||
{ | ||
auto& key = resultLeft->second; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor naming nitpick, but technically second
is the value of an entry, and first
would be the key of the entry. Maybe we can rename this to something like ... (I'm honestly struggling to think of a good name)... partners
... associates
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The canonical name here would be key -> value, but I'm guessing that's semantically pretty weak as well. Maybe just matches, pairs, or entries?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about associatedRights
?
dart/collision/CollisionFilter.cpp
Outdated
} | ||
|
||
//============================================================================== | ||
bool BodyNodeCollisionFilter::needsCollisionCheck( | ||
const collision::CollisionObject* object1, | ||
const collision::CollisionObject* object2) const | ||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment is really for line 171, but Github won't let me comment there:
Why do we assume that non-ShapeNodes
are always checked for collisions? Is it even possible for a non-ShapeNode
to provide a CollisionObject
? Rather than short-circuiting if one of them is not a ShapeNode
, I would expect to assert that both are, in fact, ShapeNode
s.
dart/collision/CollisionFilter.hpp
Outdated
bool areAdjacentBodies(const dynamics::BodyNode* bodyNode1, | ||
const dynamics::BodyNode* bodyNode2) const; | ||
|
||
/// Returns true if the BodyNode pair is in the blacklist. | ||
bool existsBodyNodePairInBlacklist( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I totally get why the function was given this name, but I feel like it could flow better. Maybe isBodyNodePairInBlackList(~,~)
? Or hasBodyNodePairInBlackList(~,~)
? containsBodyNodePairInBlackList(~,~)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like hasBodyNodePairInBlackList(~,~)
!
dart/collision/CollisionFilter.hpp
Outdated
|
||
/// List of pairs to be excluded in the collision detection. | ||
/// | ||
/// Each pair is stored as which the key of the std::unordered_map is not |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure this is worded correctly. It sounds like it's saying the key of the std::unordered_map
is "not always greater" than the elements of its associated set when it should say that the key "is always less than or equal to" (or "is never greater").
That said, I just noticed that we're allowing BodyNode
s to be paired with themselves, which I think is kind of pointless since a self-collision within a BodyNode is always true and meaningless. I think we should filter out that possibility during the adding process and make this statement "the key is always less than the elements in its associated value".
I've created a recommended rewording here.
dart/collision/CollisionFilter.hpp
Outdated
/// std::set. | ||
std::unordered_map< | ||
const dynamics::BodyNode*, | ||
std::set<const dynamics::BodyNode*>> mBlackList; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason we're using std::set
instead of std::unordered_set
? I don't see anywhere that the order would matter.
dart/collision/CollisionFilter.cpp
Outdated
const bool foundRight = (resultRight != key.end()); | ||
if (foundRight) | ||
{ | ||
key.erase(bodyNodeGreater); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One more nitpick: We don't need to check for existence before calling erase(~)
. If the value doesn't exist in the set, it won't erase anything. We can get rid of the if-statement and just call key.erase(bodyNodeGreater);
and then if(key.empty()) mBlackList.erase(resultLeft);
.
I agree with you regarding the idea of changing the function name to |
I believe all the comments are addressed. @mxgrey Could you do the second round review? |
Of course there's also a third option: |
Oh, regarding non- We might want to consider a |
After thinking about it some more, the original behavior actually made total sense for non- Sorry for going back and forth on the matter. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I mentioned in a higher-level comment, I wonder if we should consider a more generic templated BlackListCollisionFilter
and inherit that in BodyNodeCollisionFilter
.
dart/collision/CollisionFilter.cpp
Outdated
if (!filter) | ||
return; | ||
|
||
const auto result = std::find(mFilters.begin(), mFilters.end(), filter); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have two thoughts on this where the second thought makes the first through irrelevant, but I'll mention it anyway, just in case you're able to correct me:
-
I'm not sure if
std::find
will take advantage ofstd::unordered_set
's useful hashing properties. It's entirely plausible that it could (using either template specialization or function overloading), and I wouldn't be very surprised if it did, but I can't find any indication online that this is the case. So this might be performing an O(N) lookup even though our container could do a O(1) lookup. Please let me know if you're aware of any specialized optimizations thatstd::find
performs, because I'd be very interested in knowing. -
Calling
mFilters.insert(filter)
will insertfilter
if it wasn't already in the set; otherwise it will do nothing. So checking to see iffilter
is already in the set doesn't do anything for us here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-
I believe there are no specialized optimizations or that
std::find
performs, and also believe that there is no advantage of usingstd::find
overstd::unordered_set::find
. -
This is just an oversight: This is a legacy code that was for when the type of
mFilters
wasstd::vector
, which should be removed asstd::unordered_set
is used now.
That is my original intention. I thought you meant not to allow the case that |
I like this idea. Also, I would like to generalize the functionality one step further. A collision filter should implement We could instead create a container that stores a set of pairs and lets us know whether a pair is in the container or not. Then I'm thinking of the name for the container as |
Hi, The function Thanks, |
Answered in #1251 |
This pull request enables us to enable/disable collision checking for specific pairs of body nodes by maintaining a blacklist in
BodyNodeCollisionFilter
. Also,ConstraintSolver::getCollisionOption()
is added to enable to modify the collision filter through it as:Minor changes:
CompositeCollisionFilter
class to enable to use more than two collision filters by composing them in this classCollisionFilter::needCollision(...)
toCollisionFilter::needsCollisionCheck(...)