Skip to content

filterFlexContacts can keep the wrong final contact after FPS compaction #3297

@fallintoplace

Description

@fallintoplace

I think there may be a bug in filterFlexContacts introduced in 8ba68cee (Limit the number of flex contacts per collision pair).

The short version is that the FPS selection loop can end up keeping a different final contact than the one it actually selected.

The relevant code is here:

  • src/engine/engine_collision_driver.c:401
  • current main at 5d782a2b

What I think is happening:

  • filterFlexContacts selects contacts with farthest-point sampling.
  • During the loop it swaps contacts[best] into the kept prefix at contacts[nselected].
  • But that swap only happens when nselected < mjMAXCONPAIR - 1.
  • On the final selected contact, the function increments nselected and then truncates d->ncon, without compacting contacts[best] into the kept prefix first.

So the last selected contact can be dropped, and the truncated prefix can contain a different contact instead.

A small toy translation of the loop reproduces that behavior: the selected sequence is {A, B, C} but the kept prefix after compaction/truncation becomes {A, B, D}.

I think there is also a second invariant problem here: because contacts[] is swapped in place during FPS, selected[] / min_dist[] no longer stay attached to the same logical candidate after the first swap. But even without leaning on that, the missing final compaction step already looks sufficient to make the retained set wrong.

Expected behavior:

  • the first nselected contacts after filtering should be exactly the contacts selected by FPS.

Actual behavior:

  • the final kept prefix can differ from the selected set.

If this diagnosis looks right, I'm happy to send a small PR with a regression test.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions