2
\$\begingroup\$

I have a rectangular object and a ball ( sphere ) in 3D and would like to orient the rectangular object in such way that when a collision with the ball and the object happens, to redirect the ball towards a certain point

Here's an example of what I want to achieve

Example

In short: How to rotate the black rectangle in order to redirect the blue ball towards the yellow.

Thanks in advance

\$\endgroup\$
2
  • \$\begingroup\$ Since you're working in 3D, is the 'rectangle' a plate or a box? Thus, can we ignore its thickness or not? \$\endgroup\$
    – liggiorgio
    Aug 30 '21 at 11:19
  • \$\begingroup\$ It's a box, the same as in this image: IMG. \$\endgroup\$
    – owmygawd23
    Aug 30 '21 at 13:38
2
\$\begingroup\$

This gets more complicated than it might appear on the surface, because as we rotate the box, we also change the position where the sphere comes into contact with it, and that then changes the outgoing velocity we'd need to achieve to hit our target, which then changes the angle we need to reach to rebound with that velocity, which then changes the position...

There is a 1:1 relationship between these, but trying to work out a closed-form equation I could solve led me into an absolute mess of trig. Someone else may have an insight that I'm missing, but I have a sneaking suspicion that iterative approximation might be our best bet here.

First though, we can reduce the dimensionality of the problem. Even though we're in 3D, the sphere's initial position, target position, and initial velocity give us a 2D plane. The normal vector pointing perpendicular from the "bouncing face" of our box has to lie in this plane too.

Now we need to figure out how much to rotate the box around that movement plane's normal to achieve our goal. So from here on in we can think of the problem almost entirely in that 2D plane.

We can think of our box's intersection with this plane as a rectangle, whose width is the thickness of the box from the bouncing face to the opposite face, and whose height is \$2 \sqrt{ \left(\frac {\text{diagonal}} 2\right)^2 - \text{distance}^2}\$, where \$\text{diagonal}\$ is the length of the diagonal of the bouncing face, and \$\text{distance}\$ is the distance of the sphere's motion plane from the origin of the box. Anything inside this rectangle, we can twist our box to hit, anything outside, we can't.

Let's impose a coordinate system on this plane, where the origin is the center of our rectangle, and the x axis points opposite the sphere's initial velocity. You can cross this direction with the plane normal to get your y axis. (There are two choices, but either one works)

We'll start with the rectangle rotated "0 degrees" relative to this coordinate frame, so that the bouncing face points directly against the sphere's incoming velocity. That gives us the best possible chance to catch the sphere. Compute where the sphere hits the line segment of the bouncing face in this orientation in the usual way.

If it misses even in this most favourable angle, then we give up - there's no way we can rotate the box to affect the sphere at all - it will always fly past us unless we translate the box in space, which was not part of the question.

If we do detect an intersection, then we reflect the ball's velocity in the usual way, and we check whether our target lies "above" or "below" this reflected trajectory (counter-clockwise or clockwise in our imposed 2D coordinate system).

Next, we turn the rectangle as far as we can in that direction while still intersecting the sphere's path, and we check the trajectory again.

If we get the same result both times ("above and above" or "below and below"), then again we give up. No matter how we rotate the box we'll always miss - though you can take the smallest miss of the two if you like.

If you get opposite results (your first bounce went "above" and your second bounce went "below" or vice versa), then we have a solution in sight, and we can find it by binary search between these two extremes.

You now have an upper and lower bound for the solution angle. Choose an angle halfway between those bounds, and check again. If the result is "above", replace your previous "above" bound. If the result is "below", replace the previous "below" bound. Rinse and repeat, halving your uncertainty with each iteration, until you close in to your desired precision around the solution.

Once you have a solution in this imposed coordinate space, you can transform that face normal back to your original world coordinates, and aim the box to face that vector.

\$\endgroup\$
1
\$\begingroup\$

Since the angle coming in is the same as going out - let us call it β - and you know the angle between your blue and yellow line - let us call it α - you can calculate β by 2*β + α = 180°. The angle α can be determined if you know the coordinates of the blue circle and the yellow circle and the point of contact with your rectangle: α = arccos ( a.b / |a| . |b| )

You need to read DMGregory's answer for the big picture; this is just a proof of concept for the narrow constraints known to me by your question.

#include <stdio.h> // printf
#include <cmath> // acos, sqrtf

#define PI 3.14159265

struct vec2
{
    union
    {
        struct { float x, y; };
    };
    vec2() {}
    vec2(float x, float y) : x(x), y(y) {}
    float dot(const vec2 &o) const  { return x*o.x + y*o.y; }
    float squaredlen() const { return dot(*this); }
    float magnitude() const  { return sqrtf(squaredlen()); }
    vec2 &sub(const vec2 &o) { x -= o.x; y -= o.y; return *this; }
};

float rad2deg(float r){ return r*180.0/PI; }

void example(vec2 A, vec2 B, vec2 C) // pertains to https://gamedev.stackexchange.com/q/195488/93991
{
    vec2 v_a = vec2(A).sub(C); // a⃗: 0A - 0C (The vector a⃗ is Origin-to-PointA minus Origin-to-PointC)
    vec2 v_b = vec2(B).sub(C); // b⃗: 0B - 0C (see above)
    printf("a⃗ = (%+3.2f, %+3.2f) has |a⃗| = %3.2f\n", v_a.x, v_a.y, v_a.magnitude());
    printf("b⃗ = (%+3.2f, %+3.2f) has |b⃗| = %3.2f\n", v_b.x, v_b.y, v_b.magnitude());
    printf("a⃗∙b⃗ = a⃗.x*b⃗.x + a⃗.y*b⃗.y = %5.3f\n", v_a.dot(v_b)); // https://en.wikipedia.org/wiki/Dot_product
    float rad_alpha = acos(v_a.dot(v_b) / (v_a.magnitude() * v_b.magnitude())); // alpha in radians 
    float deg_beta = (180.0f - rad2deg(rad_alpha)) / 2.0f; // beta in degrees
    printf("At C[%3.2f,%3.2f] the angle β must be %5.3f° (α = %5.3f^=%5.3f°) to reflect object from A[%3.2f, %3.2f] to reach B[%3.2f, %3.2f]\n", C.x, C.y, deg_beta, rad_alpha, rad2deg(rad_alpha), A.x, A.y, B.x, B.y); 
}

int main()
{
    example( vec2( 7.5f,  4.5f), vec2( 7.5f, -4.5f), vec2( 3.0f,  0.0f) ); // α == 90°  
    example( vec2( 3.2f,  1.0f), vec2( 9.9f, -4.2f), vec2( 1.1f,  0.3f) ); // α ~= 45°
    example( vec2( 1.0f,  1.0f), vec2( 5.0f, -1.0f), vec2( 0.0f,  0.0f) ); // α ~= 56°
    return 0;
}
\$\endgroup\$
1
  • 1
    \$\begingroup\$ Would it be possible to implement some code example in order to understand your answer better ? Thank you \$\endgroup\$
    – owmygawd23
    Aug 30 '21 at 15:50

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .