Java-Gaming.org    
Featured games (79)
games approved by the League of Dukes
Games in Showcase (476)
Games in Android Showcase (106)
games submitted by our members
Games in WIP (533)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  Need help on calculating the reflection of a point hitting a circle from inside.  (Read 1917 times)
0 Members and 1 Guest are viewing this topic.
Offline tom_mai78101
« Posted 2012-07-23 11:06:21 »

Here's a simple diagram of the point and a circle the title is describing about.



As far as I know, when the point hits the circle from the inside, as shown above, the point of collision is registered via a simple collision detection.

Something confuses me is the tangent of the collision point. When I obtain the point of collision's Cartesian coordinates, use the center of the circle to obtain the vector from it, I only know that I am able to calculate the Theta angle (from X axis to the vector) and Phi angle (from Y axis to the vector) of the collision point to the center of the circle by using Math.atan2(), which I think is not able to help me in finding the normal of the point is moving at.

Here's how I calculate the ball's projectory:

1. Via some specific way of obtaining values from an accelerometer, I set the values and load them in a Java float array. These values are the acceleration values for X, Y and Z axes of the point in a 3D world. For now, we will focus on a 2D top-down view, with Z positive is up, Y positive is front, and X positive is to the right.
2. The acceleration values are then feed into the speed values.
3. Then I continuously add the speed values to the position values. In between these, I do not rely on a Time variable.

I was wondering if anyone knows how I should calculate the actual normal vector of the point? Thanks in advance.

Here's the source code for the point.

Initializations:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
public class Cue extends Point {
   protected Bitmap bitmap = null;
   protected final Rect srcRect = new Rect();
   protected RectF dstRect = new RectF();
   
   private boolean jumping = false;
   private double radians = 0;
   private double radianSpeed = 1.0;
   private double multiplier = 0;
   private double oldXOffset = 0;
   private float[] jumpPositionSpeed = new float[3];
   private Paint paint;
   private boolean goingOut = false;
   
   public Cue(int w, int h) {
      srcRect.set(0, 0, 16, 16);
      dstRect.set(0, 0, 16, 16);
      this.diameter = (srcRect.right - srcRect.left);
      for (int i = 0; i < 2; i++)
         jumpPositionSpeed[i] = 0f;
      jumping = false;
      this.setBoundary(w, h);
      paint = new Paint();
      paint.setARGB(255, 255, 255, 255);
      goingOut = false;
   }
Gameticks and rendering:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
   
   public void render(Canvas c) {
      if (bitmap != null) {
         c.drawBitmap(bitmap, srcRect, dstRect, paint);
      }
   }
   
   public void tick(Level l) {
      if (bitmap != null) {
         if (!jumping) {
            if (l.goalReached){
               bind(l.hole);
            }
            calculate();
            move();
         }
         else
            jump();
         if (acceleration[2] < 0.0) {
            multiplier = 10;
            jumping = true;
         }
      }
      else
         bitmap = Art.sprites;
   }
Bind function: Used for calculating how I should keep the point inside the boundaries I set forth. Ignore the //FIXME comment. I placed it there to remind me in third person, so my PC is like a boss.
1  
2  
3  
4  
5  
6  
7  
8  
   
   //FIXME: If you know the current position and velocity of the object, then you can figure out the coordinates where it would intersect with the circle. You can then obtain the tangent to the circle at that point, or more specifically the normal to the circle at that point. Once you know that, you can calculate the angle to the normal that the point is moving at, and use that to calculate the angle that it will reflect at.
  public void bind(Hole h){
      double distance = Math.hypot(this.position[0] - h.x + (16+this.diameter)/2, this.position[1] - h.y + (16+this.diameter)/2);
      if (distance > (16+this.diameter)/2){
         //Clueless from here on out.
     }
   }
Actions
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
   
   private void jump() {
      //position[2] is never used.
     int xOffset = (int) (Math.cos(radians + Math.PI) * multiplier) + 8;
      for (int i = 0; i < 2; i++) {
         jumpPositionSpeed[i] += acceleration[i];
         position[i] -= jumpPositionSpeed[i];
         jumpPositionSpeed[i] *= 0.1f;
      }
      if (position[0] > screenWidth - this.diameter)
         position[0] = screenWidth - this.diameter;
      if (position[1] > screenHeight - this.diameter)
         position[1] = screenHeight - this.diameter;
      if (position[0] < 0)
         position[0] = 0;
      if (position[1] < 0)
         position[1] = 0;
      dstRect.set(position[0], position[1], position[0] + this.diameter, position[1] + this.diameter);
      dstRect.left -= xOffset + oldXOffset;
      dstRect.right += xOffset + oldXOffset;
      dstRect.top -= xOffset + oldXOffset;
      dstRect.bottom += xOffset + oldXOffset;
      radians += 0.1 * radianSpeed;
      if (radians > 2 * Math.PI) {
         multiplier -= 4;
         oldXOffset += xOffset - (int) ((Math.cos(radians + Math.PI) * multiplier) + 8);
         radians = 0;
         radianSpeed += 2;
         if (multiplier < 2.0) {
            jumping = false;
            multiplier = 10;
            oldXOffset = 0;
            radianSpeed = 1.0;
            for (int i = 0; i < 2; i++)
               jumpPositionSpeed[i] = 0;
         }
      }
   }
   
   private void move() {
      if (position[0] > screenWidth - this.diameter)
         position[0] = screenWidth - this.diameter;
      if (position[1] > screenHeight - this.diameter)
         position[1] = screenHeight - this.diameter;
      for (int i = 0; i<= 1; i++)
         if (position[i] < 0)
            position[i] = 0;
      dstRect.set(position[0], position[1], position[0] + this.diameter, position[1] + this.diameter);
   }
This function is made to calculate the values from the accelerometer to the screen.
1  
2  
3  
4  
5  
6  
7  
8  
   
   public void calculate(){
      for (int i = 0; i < 2; i++) {
         speed[i] += acceleration[i];
         position[i] -= speed[i];
         speed[i] *= 0.1f;
      }
   }
Misc. functions
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
   
   public boolean isAlive() {
      // There's currently no conditions for the ball to die yet.
     return true;
   }
   
   public boolean isJumping(){
      return jumping;
   }
   
   public void stopJumping() {
      jumping = false;
      multiplier = 10;
      oldXOffset = 0;
      radians = 0;
      radianSpeed = 1.0;
      for (int i = 0; i < 2; i++)
         jumpPositionSpeed[i] = 0;
   }
   
   public void reset(int x, int y){
      position[0] = x - 8;
      position[1] = y - 8;
      speed[0] = 0f;
      speed[1] = 0f;
      //setGoalFlag(false);
     stopJumping();
   }
Collision helper functions for other collision detections/responses
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
   
   public void resolveCollision(Hole h){
      float distance = (16 + this.diameter) / 2;
      float dx = this.position[0] - h.x + distance;
      float dy = this.position[1] - h.y + distance;
      double dist = Math.hypot(dx, dy);
      double penetration = Math.max(0, (distance - dist));
      if (distance > dist) {
         this.position[0] += ((float) (penetration * dx)) / (float) (dist * 2);
         this.position[1] += ((float) (penetration * dy)) / (float) (dist * 2);
      }
   }
   
   public void collisionResponse(Hole h){
      //Assumes that the cue ball is in the hole.
     /*
      double angle = Math.atan2(this.position[1] - h.y+(16+this.diameter/2), this.position[0] - h.x+(16+this.diameter/2));
      angle = Math.toDegrees(angle);
      angle += 90;
      angle = Math.toRadians(angle);
      double temp = this.speed[0];
      this.speed[0] =(float)(this.speed[0] * Math.cos(angle) - this.speed[1] * Math.sin(angle));
      this.speed[1] =(float)(this.speed[1] * Math.cos(angle) + temp * Math.sin(angle));
      for (int i = 0; i<=1; i++){
         position[i] -= speed[i];
      }*/

      double xVelocity = this.speed[0];
      double yVelocity = this.speed[1];
      double xDist = this.position[0] - h.x + (16 + this.diameter)/2;
      double yDist = this.position[1] - h.y + (16+this.diameter)/2;
      double distSquared = xDist * xDist + yDist * yDist;
      double dotProduct = xDist * xVelocity + yDist * yVelocity;
      if (dotProduct > 0) {
         double collisionScale = dotProduct / distSquared;
         double xCollision = xDist * collisionScale;
         double yCollision = yDist * collisionScale;
         //b.speed[0] += xCollision;
        //b.speed[1] += yCollision;
        this.speed[0] -= xCollision;
         this.speed[1] -= yCollision;
      }
   }
   
   public void calculateResponse(){
      for (int i = 0; i<=1; i++){
         position[i] -= speed[i];
         //speed[i] *= 0.1f;
     }
   }
}


The code is probably one of the most ugliest ones you seen here, so I apologize. Sorry.


Offline _Al3x

Senior Member


Medals: 7


Indie Games FTW!


« Reply #1 - Posted 2012-07-23 11:36:09 »

At least it's indexed Smiley
Can't read much code now (6:30am) maybe later or maybe someone else will do Smiley

Offline Danny02
« Reply #2 - Posted 2012-07-23 13:26:23 »

for reflection you only need the normal. And the normal for every point which lies on the circle is the direction vector to the center of the circle

Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Jimmt
« League of Dukes »

JGO Kernel


Medals: 128
Projects: 4
Exp: 3 years



« Reply #3 - Posted 2012-07-23 16:52:15 »

I am far too lazy to read all that code, but http://www.ntu.edu.sg/home/ehchua/programming/java/J8a_GameIntro-BouncingBalls.html might be what you're looking for. All about bouncing balls  Grin
Offline tom_mai78101
« Reply #4 - Posted 2012-07-24 09:21:53 »

Just realized there's no Multi-Quote feature in this forum. So, copy/pasting...

And another thing. I have this habit of pasting my codes in my post when unnecessary. So, please take your time. I don't think you may read the codes. Just that, everywhere I've been to, I've been asked for the codes.

for reflection you only need the normal. And the normal for every point which lies on the circle is the direction vector to the center of the circle.

Apparently, I got stuck at this point even more than I expected. Once we have two points, and find the normal, how do I find out the direction of the normal it is facing?

There are two normals to a vector; they have two directions, pointing oppositely (A real word, by the way). If I wish to take only one of the two normals, do I need to rely on the angle calculations based off of the Cartesian coordinates of the point of collision? Or I need to do lots of if...else... conditions?

I am far too lazy to read all that code, but http://www.ntu.edu.sg/home/ehchua/programming/java/J8a_GameIntro-BouncingBalls.html might be what you're looking for. All about bouncing balls  Grin

I've been trying so hard to find that link of yours. Sometimes, Google never work out as much as I wanted it to be. Thanks for the link.

EDIT:

Noticed that in the "Circular Container" section, it uses the (time and speed) to acquire the distance displacement, and then add the displacement to the current ball position.

Here's my hunch. I can substitute the time for a game tick, and the X amount of ticks passed will give me the a constant time slice T/X (where T is in the boundaries of the nanoseconds), which I can use to acquire the distance displacement, and calculate() it to the ball position.

So, I may rewrite the algorithm so that T=1 in the tick(), and that could help me shorten the amount of vector calculations. Is this plausible? I see the current algorithm in his section, probably requiring some more passes before the correct position is obtained.
Offline tom_mai78101
« Reply #5 - Posted 2012-07-24 17:01:32 »

I'm happy to say that, after some work, I got a weird response.

When the point is inside the circle, it will jitter quickly while moving along the edge of the circle, before moving out of the circle. It's not completely bounded and I don't know why.

To be honest, I couldn't see how the code is having problems. Angles really do make calculations more difficult to maintain.

Here's the code:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
public void bind(Hole h) {
      double x1 = 0, y1 = 0, x = 0, y = 0, cx = 0, cy = 0, angle = 0, newX= 0, newY= 0;
      double distance1 = Math.hypot((this.position[0]+(this.diameter)/2) - (h.x+16), (this.position[1]+(this.diameter)/2) - (h.y+16));
      double distance2 = Math.hypot((this.position[0]-this.speed[0]+(this.diameter)/2) - (h.x+16), (this.position[1]-this.speed[1]+(this.diameter)/2) - (h.y+16));
      DecimalFormat f = new DecimalFormat("'+'000.;'-'000.");
      DecimalFormat g = new DecimalFormat("'+'000.000;'-'000.000");
      if (distance2 >= 16 || distance1 <= 12){
         // Clueless from here on out. All variables are labeled as such for System.out.println().
        x = this.position[0];
         y = this.position[1];
         cx = h.x;
         cy = h.y;
         
         angle = Math.atan2(y - cy, x - cx);
         newX = x * Math.cos(angle) + y * Math.sin(angle);
         newY = y * Math.cos(angle) - x * Math.sin(angle);
         
         newX += this.speed[0];
         newY -= this.speed[1];
         
         x1 = newX * Math.cos(-angle) + newY * Math.sin(-angle);
         y1 = newY * Math.cos(-angle) - newX * Math.sin(-angle);
         this.position[0] = (float) x1;
         this.position[1] = (float) y1;
      }      
   }

   public void calculateResponse() {
      for (int i = 0; i <= 1; i++) {
         speed[i] += acceleration[i];
         speed[i] *= 0.1f;
      }
   }

   private void move() {
      if (position[0] > screenWidth - this.diameter)
         position[0] = screenWidth - this.diameter;
      if (position[1] > screenHeight - this.diameter)
         position[1] = screenHeight - this.diameter;
      for (int i = 0; i <= 1; i++)
         if (position[i] < 0)
            position[i] = 0;
      dstRect.set(position[0], position[1], position[0] + this.diameter, position[1] + this.diameter);
   }


Here's a picture, showing how the ball reacts accordingly from left to right:



So, the jittering-ness and the random shootoffs are getting quite unusual for me. Anyone know an explanation for this?
Offline Danny02
« Reply #6 - Posted 2012-07-24 19:11:27 »

seems as I did not inlcude my picture corretly so here it is


The normal vector you are searching for, points from the point of collision (circle border) to the center of the circle. So you have just one normal vector. Now to get your new movment vector, you just have to reflect the old movement vector on the normal vector. After this you revert the direction and you are finished


To reflect a vector on another + reverting the direction:

N: normal vector you want to use the mirroring line, make sure it is normalized(length = 1)
V: vector you want to reflect with N
R: reflected vector

R = -2*(V dot N)*N + V

Offline tom_mai78101
« Reply #7 - Posted 2012-07-26 16:06:05 »

Sorry I didn't reply as soon as possible.

It worked! I felt stupid the moment I see that it worked on first try. I had worked 2 long weeks trying out with angles, rotating, etc. (The codes you see before hand.)

But, it felt good. Cheesy

Thank you! Here's the resulting code, for future references:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
   public void calculateResponse() {
      for (int i = 0; i <= 1; i++) {
         position[i] -= speed[i];
         speed[i] *= 0.9992f;
      }
   }
   
   public void reflect(Hole h){
      //R = -2*(V dot N)*N + V
     //N is normalized.
     double vx = this.speed[0];
      double vy = this.speed[1];
      double nx = (this.position[0]+this.diameter/2) - (h.x+16);
      double ny = (this.position[1]+this.diameter/2) - (h.y+16);
      double nd = Math.hypot(nx, ny);
      if (nd == 0)
         nd = 1;
      nx /= nd;
      ny /= nd;
      double dotProduct = vx*nx+vy*ny;
      double rx = -2*dotProduct*nx+vx;
      double ry = -2*dotProduct*ny+vy;
      this.speed[0] = (float) rx;
      this.speed[1] = (float) ry;
   }

   private void move() {
      if (position[0] > screenWidth - this.diameter)
         position[0] = screenWidth - this.diameter;
      if (position[1] > screenHeight - this.diameter)
         position[1] = screenHeight - this.diameter;
      for (int i = 0; i <= 1; i++)
         if (position[i] < 0)
            position[i] = 0;
      dstRect.set(position[0], position[1], position[0] + this.diameter, position[1] + this.diameter);
   }

   public void tick(Level l) {
      if (bitmap != null) {
         if (!jumping) {
            if (l.goalReached) {
               //THESE HERE ARE PLACED IN THE ORDER OF EXECUTION.
              reflect(l.hole);
               calculateResponse();
               move();
            }
            else {
               calculate();
               move();
            }
         }
         else
            jump();
         if (acceleration[2] < 0.0) {
            multiplier = 10;
            jumping = true;
         }
      }
      else
         bitmap = Art.sprites;
   }
Pages: [1]
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

pw (18 views)
2014-07-24 01:59:36

Riven (17 views)
2014-07-23 21:16:32

Riven (14 views)
2014-07-23 21:07:15

Riven (17 views)
2014-07-23 20:56:16

ctomni231 (45 views)
2014-07-18 06:55:21

Zero Volt (40 views)
2014-07-17 23:47:54

danieldean (32 views)
2014-07-17 23:41:23

MustardPeter (36 views)
2014-07-16 23:30:00

Cero (51 views)
2014-07-16 00:42:17

Riven (50 views)
2014-07-14 18:02:53
HotSpot Options
by dleskov
2014-07-08 03:59:08

Java and Game Development Tutorials
by SwordsMiner
2014-06-14 00:58:24

Java and Game Development Tutorials
by SwordsMiner
2014-06-14 00:47:22

How do I start Java Game Development?
by ra4king
2014-05-17 11:13:37

HotSpot Options
by Roquen
2014-05-15 09:59:54

HotSpot Options
by Roquen
2014-05-06 15:03:10

Escape Analysis
by Roquen
2014-04-29 22:16:43

Experimental Toys
by Roquen
2014-04-28 13:24:22
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!