6

Here is an example (see image) :-

  • The 2 reddish rectangles are static objects (i.e. it can't move).
  • The bluish ball is dynamic object.

So far, I manage to get all penetrating information. Let's consider it as our input:-

  • To resolve the penetration between A and the ball, I may move the ball by either Vec3(1,0,0) OR Vec3(0,2,0).
  • To resolve the penetration between B and the ball, I may move the ball by Vec3(0,1,0).

^ I store it as a 2D Vec3 array problem = {{Vec3{1,0,0},Vec3{0,2,0}},{Vec3{0,1,0}}}.

How to find the best movement (minimum size) of Physics object (e.g. ball in the example) to minimize as much penetration as possible?

The best solution in this example is "move ball by Vec3(1,1,0) : size=1.414".

enter image description here

In real case, the situation can be more complex and ugly,
e.g. it is possible that both A&B (and other Physics object) try to push the ball in a near-opposite direction. (below images)

enter image description here

^ In some scenes, the 2D array problem may lack some detail, but for simplicity, let's pretend that it explains all information accurately.

C++ Code

Here is my MCVE (coliru) :-

#include<iostream>
#include <utility>
#include <vector>
#include <array>
#include <math.h>

using Vec3=std::array<float, 3>;
float dotProduct(Vec3 vec1,Vec3 vec2){
    return vec1[0]*vec2[0]+vec1[1]*vec2[1]+vec1[2]*vec2[2];
}
float size2(Vec3 vec1){
    return vec1[0]*vec1[0]+vec1[1]*vec1[1]+vec1[2]*vec1[2];
}
Vec3 mulFloat(Vec3 vec1,float m){
    return Vec3{vec1[0]*m,vec1[1]*m,vec1[2]*m};
}
Vec3 normalize(Vec3 vec1){
    return mulFloat(vec1,1/sqrt(size2(vec1)));
}

Here is the main() :-

int main() {
    std::vector<std::vector<Vec3>> problem;
    std::vector<Vec3> location1;
    location1.push_back(Vec3{0,2,0});
    location1.push_back(Vec3{1,0,0});
    problem.push_back(location1);
    std::vector<Vec3> location2;
    location2.push_back(Vec3{0,1,0});
    problem.push_back(location2);
    //^ INPUT
    //----- insert YOUR ALGORITHM here  ------
    Vec3 solution=Vec3{0,2,0};

    float totalResidual=0;
    for(auto& location : problem){
        float residualRemainMin=1000000;//numerical limit
        for(auto& orgResidual : location){
            Vec3 orgResidualNormalize=normalize(orgResidual);
            float orgResidualSize=sqrt(size2(orgResidual));
            float residualModifyBy=-dotProduct(orgResidualNormalize,solution);//#1
            //"residualModifyBy" is usually negative
            float remainResidual=std::max(0.0f,orgResidualSize+residualModifyBy);
            //^ "max" because it has no advantage to reduce residual to < 0
            residualRemainMin=std::min(residualRemainMin,remainResidual);
            //^ "min" because the "OR" word
        }
        totalResidual+=residualRemainMin;
    }
    std::cout<<"totalResidual="<<totalResidual;
    return 0;
}

Note (#1) in code: residual is reduced by dotProduct(solution,normalize(orgResidual) ).
My derivation of this formula comes from this image :-
enter image description here

Side Note

I feel that it would be too slow to permute every combination (which I am not exactly sure how to).
I also believe general iterative approach is too slow to converge.

I just want some guideline ; I don't mind a description-only without-code answer.

Edit (29 June 2019)

More test cases :-

{{(0,3,0)},{(2,2,0)}}            ===> (1,3,0)       
{{(0,1,1),(10,0,0)},{(0,2,3)}}   ===> (0,2,3)  
{{(99,1,0),(99,-1,0),(100,0,0)}} ===> (99,1,0) OR (99,-1,0)    (equally good)
cppBeginner
  • 1,114
  • 9
  • 27

1 Answers1

-1

I think an implementation which gives you the correct/best answer in just on step will be very complicated.

My approach would be:

  1. if there are no intersections with any of the objects, move the ball in the direction of the objects (not in the direction of objects already touching) goto 0;
  2. detect all illegal intersections with objects
  3. for each intersection calculate the direction in which you need the ball to move to eliminate the intersection (simplest case would be movement vertical to the object-surface by the amount of the intersection-depth)
  4. move the ball in the directions calculated in 2.
  5. goto 0

Terminate the loop either if the position of the ball does not change any more (or after a limited number of iterations)

In the first Example this would find the best solution in one iteration.
In the second example the ball would slowly walk upwards in every iteration until the no intersection exist anymore)

MrSmith42
  • 9,961
  • 6
  • 38
  • 49
  • It is possible that your solution will return Vec3(0,2,0). ? – cppBeginner May 21 '19 at 08:33
  • In some circumstances, yes, but not in the starting position mentioned in the question (example 1). My approach would in the first iteration move the ball 1m to the right and 1m to the top. In the next loop it would detect that no improvement is possible. – MrSmith42 May 21 '19 at 08:37
  • Where did you mention it? In "3."? By the way, I am so sad that the best result is not guaranteed. – cppBeginner May 21 '19 at 08:40
  • @cppBeginner: the key is *for each intersection*. Therefore the ball will be moved to the right and to the top. – MrSmith42 May 21 '19 at 08:42
  • @cppBeginner: Define best result! The algorithm will always attach the ball to at least 2 surfaces – MrSmith42 May 21 '19 at 08:45
  • 2
    Best result is (1st priority) min remained penetration, (2nd priority) min distance move. I agree about "The algorithm will always attach the ball to at least 2 surfaces". – cppBeginner May 21 '19 at 08:46
  • In A-vs-ball, do I pick "push-right" or "push-up" or both? How do I know? Then, do I need pick "up" from B-vs-ball, and add both result together , move sphere by (Vec3(1,0,0)+Vec3(0,1,0)), and it will conclude step 3? – cppBeginner May 21 '19 at 08:50
  • @cppBeginner: depends. The image is quite small. You can do both if the intersection is with the upper-border and the right border (or take only the one which needs the smallest move) – MrSmith42 May 21 '19 at 10:28