Need help on calculating the reflection of a point hitting a circle from inside.

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:

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:


	
	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.


	
	//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


	
	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.


	
	public void calculate(){
		for (int i = 0; i < 2; i++) {
			speed[i] += acceleration[i];
			position[i] -= speed[i];
			speed[i] *= 0.1f;
		}
	}

Misc. functions


	
	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


	
	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.

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

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

https://docs.google.com/drawings/pub?id=16yWg2RUlPZIeygizuhvKuqUhLMcbwlW5Oc7335_-eMU&w=960&h=720

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 ;D

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.

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’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.

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:

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?

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

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. :smiley:

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


	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;
	}