Billiards Balls Stuck Inside Each other - java

I am designing a billiards game for my Java class. I am having an issue with the billiard balls collisions. The balls hit into each other and occasionally slide into each other, becoming stuck. I can't seem to identify the cause of this error. I was hoping somebody could assist me in finding the cause of the issue. My code is below. Thank You.
float cueX = 200;
float cueY = 225;
float cueDeltaX;
float cueDeltaY;
float ballWidth = 25;
float score = 0;
Billiards[] billiards = new Billiards[3];
void setup()
{
size (850, 450);
background(0);
fill(#29B748);
rect(0, 0, 599, 599);
billiards[0] = new Billiards(600, 225, 0, 0, false, "", 0);
billiards[1] = new Billiards(625, 211, 0, 0, false, "", 1);
billiards[2] = new Billiards(625, 239, 0, 0, false, "", 2);
//billiards[3] = new Billiards(625, 250, 0, 0, false, "", 2);
}
void draw()
{
background(0);
fill(#FFFFFF);
stroke(#A6A7A6);
text("DeltaX: " + cueDeltaX + " Delta Y: " + cueDeltaY, 20, 20);
text(score, 500, 20);
fill(#29B748);
rect(25, 25, 799, 399);
poolCueLines();
drawCue();
moveCue();
cueBounce();
cueFriction();
drawBilliards();
billiards[0].collision();
billiards[0].moveBall();
billiards[0].billiardBounce();
billiards[0].billiardFriction();
billiards[1].collision();
billiards[1].moveBall();
billiards[1].billiardBounce();
billiards[1].billiardFriction();
billiards[2].collision();
billiards[2].moveBall();
billiards[2].billiardBounce();
billiards[2].billiardFriction();
}
void poolCueLines() {
if (mousePressed)
{
stroke(#FFFFFF);
line(cueX, cueY, mouseX, mouseY);
}
}
void mouseReleased()
{
cueDeltaX = (cueX - mouseX)/50;
cueDeltaY = (cueY - mouseY)/50;
}
void drawCue() {
noStroke();
fill(0);
fill(#FFFFFF);
stroke(#A6A7A6);
ellipse(cueX, cueY, ballWidth, ballWidth);
noFill();
}
void moveCue() {
cueX += cueDeltaX;
cueY += cueDeltaY;
}
void cueBounce() {
if (cueX > width-25-ballWidth/2 || cueX < 25 + ballWidth/ 2) {
cueDeltaX = -cueDeltaX;
cueDeltaX = cueDeltaX * 0.6;
if (cueX < 25+ ballWidth/2) {
cueX = 26 + ballWidth/2;
} else {
cueX = width-26-ballWidth/2;
}
}
if (cueY > height-25-ballWidth/2 || cueY < 25 + ballWidth/ 2) {
cueDeltaY = -cueDeltaY;
cueDeltaY = cueDeltaY * 0.6;
if (cueY < 25+ ballWidth/2) {
cueY = 26 + ballWidth/2;
} else {
cueY = height-26-ballWidth/2;
}
}
}
void drawBilliards() {
//Yellow Ball 1
fill(#ffff00);
stroke(#A6A7A6);
ellipse(billiards[0].ballXpos, billiards[0].ballYpos, ballWidth, ballWidth);
//Blue 2
fill(#000099);
stroke(#A6A7A6);
ellipse(billiards[1].ballXpos, billiards[1].ballYpos, ballWidth, ballWidth);
//Red 3
fill(#ff0000);
stroke(#A6A7A6);
ellipse(billiards[2].ballXpos, billiards[2].ballYpos, ballWidth, ballWidth);
}
void cueFriction() {
cueDeltaX = cueDeltaX * 0.995;
cueDeltaY = cueDeltaY * 0.995;
}
class Billiards
{
float ballXpos;
float ballYpos;
float deltaXball;
float deltaYball;
int billiardsNum;
Billiards(float tempXpos, float tempYpos, float deltaXbill, float deltaYbill, boolean stripe, String stripeColor, int billiardNum) {
ballXpos = tempXpos;
ballYpos = tempYpos;
deltaXball = deltaXbill;
deltaYball = deltaYbill;
billiardsNum = billiardNum;
}
void collision() {
if (cueX > ballXpos-ballWidth && cueX < ballXpos+ballWidth) {
if (cueY < ballYpos+ballWidth && cueY > ballYpos-ballWidth) {
cueDeltaX = -cueDeltaX * 0.8;
deltaXball = -cueDeltaX * 0.6;
cueDeltaY = -cueDeltaY * 0.8;
deltaYball = -cueDeltaY * 0.6;
}
}
int ballNum = 0;
for (int i=0; i < 3; i++) {
if (billiards[ballNum].ballXpos > ballXpos-ballWidth && billiards[ballNum].ballXpos < ballXpos+ballWidth) {
if (billiards[ballNum].ballYpos < ballYpos+ballWidth && billiards[ballNum].ballYpos > ballYpos-ballWidth) {
if (billiardsNum == ballNum) {
} else {
//if (billiards[ballNum].deltaXball < 0.2 || billiards[ballNum].deltaYball < 0.2) {
if (deltaXball > 0){
billiards[ballNum].ballXpos += -3;
}else if (deltaXball < 0){
billiards[ballNum].ballXpos += 3;
}
if (deltaYball > 0){
billiards[ballNum].ballXpos += -3;
}else if (deltaYball < 0){
billiards[ballNum].ballXpos += 3;
}
billiards[ballNum].deltaXball = -billiards[ballNum].deltaXball * 0.8;
deltaXball = -billiards[ballNum].deltaXball * 0.6;
billiards[ballNum].deltaYball = -billiards[ballNum].deltaYball * 0.8;
deltaYball = -billiards[ballNum].deltaYball * 0.6;
//}
//} else {
// billiards[ballNum].deltaXball = -billiards[ballNum].deltaXball * 0.8;
// deltaXball = -billiards[ballNum].deltaXball * 0.6;
// billiards[ballNum].deltaYball = -billiards[ballNum].deltaYball * 0.8;
// deltaYball = -billiards[ballNum].deltaYball * 0.6;
//}
}
}
}
ballNum += 1;
}
}
void moveBall() {
ballXpos += deltaXball;
ballYpos += deltaYball;
}
void billiardBounce() {
if (ballXpos > width-25-ballWidth/2 || ballXpos < 25 + ballWidth/ 2) {
deltaXball = -deltaXball;
deltaXball = deltaXball * 0.6;
if (ballXpos < 25+ ballWidth/2) {
ballXpos = 26 + ballWidth/2;
} else {
ballXpos = width-26-ballWidth/2;
}
}
if (ballYpos > height-25-ballWidth/2 || ballYpos < 25 + ballWidth/ 2) {
deltaYball = -deltaYball;
deltaYball = deltaYball * 0.6;
if (ballYpos < 25+ ballWidth/2) {
ballYpos = 26 + ballWidth/2;
} else {
ballYpos = height-26-ballWidth/2;
}
}
}
void billiardFriction() {
deltaXball = deltaXball * 0.995;
deltaYball = deltaYball * 0.995;
}
}

Well, the problem is in your collision code. You've got a lot of magic hard-coded numbers in there, and zero comments describing what they're for. That's going to make this very hard to debug, which makes it hard to help you.
But one thing that jumps out to me is that you're handling collision separately from movement. This might be okay, but with how you're doing it, you can get yourself in this situation:
Imagine Ball A being between Ball B and Ball C.
Ball A is moving to the right.
You check Ball A's collision, and it collides with Ball B. Now you tell Ball A to start moving left.
You then move Ball A to the left, but you don't check whether that will result in a collision. So now Ball A and Ball C are colliding.
Ball C was moving to the right, but then you check its collision. Sure enough it's colliding with Ball A, so you tell it to start moving to the left.
Now Ball A and Ball C are both moving to the left, even though they're colliding.
You might want to go through and add comments until you understand exactly what your code is doing. But honestly, this isn't exactly an easy problem. You might be better off starting over with a blank sketch and starting with something simpler. Try to narrow your problem down to an MCVE. Instead of posting your entire sketch, just get it narrowed down to a specific case with two circles colliding using hard-coded values instead of user input.
You also might want to take a look at the CircleCollision example that comes with the Processing editor. Just go to File -> Examples -> Topics -> Motion -> CircleCollision, and you'll see a sketch that shows an example of handling circle collisions.
Here is the collision code from that example:
void checkCollision(Ball other) {
// get distances between the balls components
PVector bVect = PVector.sub(other.position, position);
// calculate magnitude of the vector separating the balls
float bVectMag = bVect.mag();
if (bVectMag < r + other.r) {
// get angle of bVect
float theta = bVect.heading();
// precalculate trig values
float sine = sin(theta);
float cosine = cos(theta);
/* bTemp will hold rotated ball positions. You
just need to worry about bTemp[1] position*/
PVector[] bTemp = {
new PVector(), new PVector()
};
/* this ball's position is relative to the other
so you can use the vector between them (bVect) as the
reference point in the rotation expressions.
bTemp[0].position.x and bTemp[0].position.y will initialize
automatically to 0.0, which is what you want
since b[1] will rotate around b[0] */
bTemp[1].x = cosine * bVect.x + sine * bVect.y;
bTemp[1].y = cosine * bVect.y - sine * bVect.x;
// rotate Temporary velocities
PVector[] vTemp = {
new PVector(), new PVector()
};
vTemp[0].x = cosine * velocity.x + sine * velocity.y;
vTemp[0].y = cosine * velocity.y - sine * velocity.x;
vTemp[1].x = cosine * other.velocity.x + sine * other.velocity.y;
vTemp[1].y = cosine * other.velocity.y - sine * other.velocity.x;
/* Now that velocities are rotated, you can use 1D
conservation of momentum equations to calculate
the final velocity along the x-axis. */
PVector[] vFinal = {
new PVector(), new PVector()
};
// final rotated velocity for b[0]
vFinal[0].x = ((m - other.m) * vTemp[0].x + 2 * other.m * vTemp[1].x) / (m + other.m);
vFinal[0].y = vTemp[0].y;
// final rotated velocity for b[0]
vFinal[1].x = ((other.m - m) * vTemp[1].x + 2 * m * vTemp[0].x) / (m + other.m);
vFinal[1].y = vTemp[1].y;
// hack to avoid clumping
bTemp[0].x += vFinal[0].x;
bTemp[1].x += vFinal[1].x;
/* Rotate ball positions and velocities back
Reverse signs in trig expressions to rotate
in the opposite direction */
// rotate balls
PVector[] bFinal = {
new PVector(), new PVector()
};
bFinal[0].x = cosine * bTemp[0].x - sine * bTemp[0].y;
bFinal[0].y = cosine * bTemp[0].y + sine * bTemp[0].x;
bFinal[1].x = cosine * bTemp[1].x - sine * bTemp[1].y;
bFinal[1].y = cosine * bTemp[1].y + sine * bTemp[1].x;
// update balls to screen position
other.position.x = position.x + bFinal[1].x;
other.position.y = position.y + bFinal[1].y;
position.add(bFinal[0]);
// update velocities
velocity.x = cosine * vFinal[0].x - sine * vFinal[0].y;
velocity.y = cosine * vFinal[0].y + sine * vFinal[0].x;
other.velocity.x = cosine * vFinal[1].x - sine * vFinal[1].y;
other.velocity.y = cosine * vFinal[1].y + sine * vFinal[1].x;
}
}
You can also view web-based versions of the above example:
Circle Collision
Bouncy Bubbles

Related

How can I make two objects of the same class bounce when colliding?

I made a bouncing ball code, and so far as the bouncing itself the code works perfectly. I then created a second ball, and it also does what it's supposed to do. However, when I try to use an if condition to make the two balls bounce off one another as well as the edges, it doesn't work. Either they don't move or they just don't collide, and go through each other. This code was made in processing. Can anyone help me make ball1 and ball2 collide?
Moving ball1;
Moving ball2;
void setup(){
size(600,600);
ball1 = new Moving();
ball2 = new Moving();
}
void draw(){
background(255);
ball1.move();
ball1.display();
ball1.bounce();
ball2.move();
ball2.display();
ball2.bounce();
ball1.clash();
ball2.clash();
}
class Moving {
float speed = 7;
float x = random(0, width);
float y= random(0, height);
float xdirection = 1;
float ydirection = 1;
float ball_size = 50;
float radius = ball_size/2;
Moving() {
}
void move() {
x = x + (xdirection * speed);
y = y + (ydirection* speed);
}
void display() {
noStroke();
fill(50, 0, 50);
circle(x, y, ball_size);
}
void bounce() {
if ((x >= width - radius) || (x <= radius)) {
xdirection = xdirection * -1;
}
if ((y >= height - radius)|| (y<=radius)) {
ydirection = ydirection * -1;
}
}
void clash() {
if ((ball1.y+radius == ball2.y+radius) && (ball1.x+radius == ball2.x+radius)) {
ball1.ydirection = ball1.ydirection * -1;
ball2.ydirection = ball2.ydirection * -1;
ball1.xdirection = ball1.xdirection * -1;
ball2.xdirection = ball2.xdirection * -1;
x = x + (xdirection * speed);
y = y + (ydirection* speed);
if (ball1.x+radius == ball2.x+radius) {
xdirection = xdirection * -1;
}
}
}
}

How can I make my shape "point" at my player

I am trying to make a game where enemies spawn from the top (like a vertical scrolling game) and one kind of enemy will basically follow the player's X coordinate while going down. The enemy class is called Follower and right now it does points to the player (see update function on Follower) but it's not as accurate as I need it to be. For example, if the player accelerates, the follower won't be able to see him all the time.
One way to look at it is that I want the position of the player to be a coordinate in a radiant system and make the vertices of my Follower accurately just rotate and create a straight line looking at it every frame
here is the Follower Class:
public class Follower {
Player target; //follow this
//position
private Vector2 position;
private float x;
private float y;
//speed
private Vector2 velocity;
private float speed;
private float radians;
private float faceTarget;
//dimensions
private float[] shapeX;
private float[] shapeY;
private int numPoints; //vertices for the shape
private boolean remove; //to remove from the game
public Follower(float x,float y, Player target){
this.target = target;
this.x = x;
this.y = y;
velocity = new Vector2(0, 0);
numPoints = 4;
speed = 200;
shapeX = new float[numPoints];
shapeY = new float[numPoints];
radians = 3.1415f / 2;
setShape();
}
public void setShape(){
//top vertice
shapeX[0] = x + MathUtils.cos(radians) * 30;
shapeY[0] = y + MathUtils.sin(radians) * 30;
//left vertice
shapeX[1] = x + MathUtils.cos(radians - 4 * 3.1415f / 10) * 30;
shapeY[1] = y + MathUtils.sin(radians - 4 * 3.1415f / 10) * 30;
//bottom vertice
shapeX[2] = x + MathUtils.cos(radians + 3.1415f) * 60;
shapeY[2] = y + MathUtils.sin(radians + 3.1415f) * 60;
//left vertice
shapeX[3] = x + MathUtils.cos(radians + 4 * 3.1415f / 10) * 30;
shapeY[3] = y + MathUtils.sin(radians + 4 * 3.1415f / 10) * 30;
}
public boolean shouldRemove() {
return remove;
}
public void update(float dt) {
float angle = (float) Math.atan2(target.getPosition().y - y, target.getPosition().x - x); //angle between the follower and target
velocity.set((float) Math.cos(angle) * speed , -speed); //setting direction to follow the target
radians += Math.cos(angle) * dt; //THIS HERE IS MAKING IT ROTATE
x += velocity.x * dt;
y += velocity.y * dt;
setShape();
if(y <= 0 - 60)
remove = true;
else
remove = false;
}
public void draw(ShapeRenderer sp){
sp.setColor(1, 1, 1 ,1);
sp.begin(ShapeRenderer.ShapeType.Line);
for(int i = 0, j = shapeX.length - 1;
i < shapeX.length;
j = i++) {
sp.line(shapeX[i], shapeY[i], shapeX[j], shapeY[j]);
}
sp.end();
}
}
I am not adding the GameScreen because I do not see the need of showing how they are rendered, either way, it'll stay the same.
Also, with the line of code, I am using the Follower points to the player with the bottom vertice as the "eyes"
Thanks for the answers!

Balls collision detection - radius calculation - nearly done

Hello :) I'm totally lost. I have two balls on the screen, floating. Also I have a method that checks is there is a collision and a method name 'collide' that collides :)
When both balls goes in a straight line on each other it collides well. The problem is shown on the picture:
So, the methods are:
public final float ball_radius = 2.4f; // ball image has 48 width
public boolean isColliding(Ball ball)
{
distance = Math.sqrt((ball.image_center_x - this.image_center_x)*(ball.image_center_x - this.image_center_x)+(ball.image_center_y - this.image_center_y)*(ball.image_center_y - this.image_center_y));
if(distance <= 2*ball_radius)
return true;
/*
float sumRadius = 9.6f;
float sqrRadius = sumRadius * sumRadius;
float distSqr = (xd * xd) + (yd * yd);
if (distSqr <= sqrRadius)
{
return true;
}*/
return false;
}
void Collide(Ball ball1, Ball ball2)
{
double dx = (ball1.x - ball2.x) + dt * (ball1.vx - ball2.vx);
double dy = (ball1.y - ball2.y) + dt * (ball1.vy - ball2.vy);
// if collision swap velocities
if (Math.sqrt(dx * dx + dy * dy) <= 2*ball_radius) {
double tempx = ball1.vx;
double tempy = ball1.vy;
ball1.vx = ball2.vx;
ball1.vy = ball2.vy;
ball2.vx = tempx;
ball2.vy = tempy;
}
}
private void moveBalls(){
for (int i = 0; i < balls.size(); i++) {
Ball ball1 = balls.get(i);
for (int a = i + 1; a < balls.size(); a++) {
Ball ball2 = balls.get(a);
if(ball1.isColliding(ball2)) {
ball1.Collide(ball1, ball2);
checkHealthAndChangeColor(ball1, ball2);
}
//catchMP.start();
}
}
for (int i = 0; i < balls.size(); i++) {
balls.get(i).step();
}
}

Java - plotting sine waves

I am using the following code to generate a sine wave:
The speed of this animation is slow-fast-slow.
Code:
public static final double SINE_TO_180 = 114.58865012930961, TIMES = 180, SINE_OF_90 = Math.sin(Math.toRadians(90));
public static void main(String[] args) throws Exception{
float velc = 200;
float angle = 45;
float resistance = 0f;
double multiple = (velc * 2.5 / SINE_TO_180);
int offset = 0;
double y = 0;
double x = 0;
double h = 0;
double cos = Math.cos(Math.toRadians(angle));
double sin = Math.sin(Math.toRadians(angle));
for(int i = offset; i < TIMES + 1 + offset; i ++){
y += ((Math.sin(Math.toRadians(i * 2)))) * multiple * sin;
if(y >= h)
h = y;
x += Math.sin(Math.toRadians(i)) * multiple * ((1 - resistance) * 1.5) * Math.abs(cos);
// x += multiple * cos;
// if(i + offset < TIMES / 2){
// x += Math.sin(Math.toRadians(i)) * multiple * ((1 - resistance) * 1.5);
// }else{
// x += Math.sin(Math.toRadians(TIMES / 2)) * multiple * (1 - resistance);
// }
}
y = Math.round(y);
//do round x?
x = Math.round(x);
System.out.println("X: " + x);
JFrame frm = new JFrame("Projectile!");
frm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
BufferedImage b = new BufferedImage((int)x + 1, (int)h + 1, BufferedImage.TYPE_INT_RGB);
Graphics g = b.getGraphics();
y = 0;
x = 0;
JLabel l = new JLabel(new ImageIcon(b));
frm.add(l);
frm.pack();
frm.setVisible(true);
for(int i = offset; i < TIMES + 1 + offset; i ++){
y += ((Math.sin(Math.toRadians(i * 2)))) * multiple * sin;
x += Math.sin(Math.toRadians(i)) * multiple * ((1 - resistance) * 1.5) * Math.abs(cos);
// x += multiple * cos;
// if(i + offset < TIMES / 2){
// x += Math.sin(Math.toRadians(i)) * multiple * ((1 - resistance) * 1.5);
// }else{
// x += Math.sin(Math.toRadians(TIMES / 2)) * multiple * (1 - resistance);
// }
g.setColor(Color.red);
g.drawLine((int)x, (int)(h - y), (int)x, (int)(h - y));
l.setIcon(new ImageIcon(b));
frm.repaint();
Thread.sleep((int)(1000.0 / 24.0));
}
ImageIO.write(b, "png", new File("C:\\proj.png"));
}
Now I would like to change the sine animation to fast-slow-fast where it is slow at its peak so I tried the following result and got this: I would expect it to be the same just the animation speed different. Code:
public static void main(String[] args) throws Exception{
float velc = 200;
float angle = 45;
float resistance = 0f;
double multiple = (velc * 2.5 / SINE_TO_180);
int offset = 0;
double y = 0;
double x = 0;
double h = 0;
double cos = Math.cos(Math.toRadians(angle));
double sin = Math.sin(Math.toRadians(angle));
for(int i = offset; i < TIMES + 1 + offset; i ++){
y += (1 - Math.sin(Math.toRadians(i * 2))) * multiple * sin;
if(y >= h)
h = y;
// x += Math.sin(Math.toRadians(i)) * multiple * ((1 - resistance) * 1.5) * Math.abs(cos);
x += 2;
// x += multiple * cos;
// if(i + offset < TIMES / 2){
// x += Math.sin(Math.toRadians(i)) * multiple * ((1 - resistance) * 1.5);
// }else{
// x += Math.sin(Math.toRadians(TIMES / 2)) * multiple * (1 - resistance);
// }
}
y = Math.round(y);
//do round x?
x = Math.round(x);
System.out.println("X: " + x);
JFrame frm = new JFrame("Projectile!");
frm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
BufferedImage b = new BufferedImage((int)x + 1, (int)h + 1, BufferedImage.TYPE_INT_RGB);
Graphics g = b.getGraphics();
y = 0;
x = 0;
JLabel l = new JLabel(new ImageIcon(b));
frm.add(l);
frm.pack();
frm.setVisible(true);
for(int i = offset; i < TIMES + 1 + offset; i ++){
y += (1 - Math.sin(Math.toRadians(i * 2))) * multiple * sin;
x += 2;
// x += Math.sin(Math.toRadians(i)) * multiple * ((1 - resistance) * 1.5) * Math.abs(cos);
// x += multiple * cos;
// if(i + offset < TIMES / 2){
// x += Math.sin(Math.toRadians(i)) * multiple * ((1 - resistance) * 1.5);
// }else{
// x += Math.sin(Math.toRadians(TIMES / 2)) * multiple * (1 - resistance);
// }
g.setColor(Color.red);
g.drawLine((int)x, (int)(h - y), (int)x, (int)(h - y));
l.setIcon(new ImageIcon(b));
frm.repaint();
Thread.sleep((int)(1000.0 / 24.0));
}
ImageIO.write(b, "png", new File("C:\\proj2.png"));
}
Anyone know what I am doing wrong because I expect the result to be the same as the first just different animation speeds?
if you want to have a smooth animation i would seperate the data and the animation;
first create your data - it is a (mathematical) function [meaning f(x)->y], so you can simply use an array for data
private int endOfX = 100; //adjust as you wish
private int[] data;
public void calculateData(){
data = new int[amendOfX ountPixels];
for(int x = 0; x < endOfX ; x++){
y = x*x; //this is an example, use your mathematical function here
}
}
so - now you can easily use this data to provide a smooth animation
public class AnimationOfFunction(){
public static void main(String[] args){
new AnimationOfFunktion().createAndShowGui(); //as from java tutorials
}
private BufferedImage img;
private Graphics gr;
private void createAndShowGui(){
calculateData(); first of all we create the data!
JFrame frame = new JFrame();//then create your frame here
JPanel panel = createContent(); //create your drawing panel here
frame.add(panel);//adding drawing panel
frame.pack(); //setting the proper size of frame
frame.setVisible(true); //show frame
startAnimation(panel); //this is important - after showing the frame you start your animation here!
}
}
so - this would be the start of you application, what to do now? first create a proper drawing panel:
private JPanel createContent(){
//create a anonym class
#surpress serial
JPanel panel = new JPanel(){
#override
public void paintComponent(Graphics gr){
super.paintComponent(gr);
gr.drawImage(img, 0,0, null);
}
}
panel.setPreferredSize(new Dimension(img.getWidth(), img.getHeight() ));
return panel;
}
and most important - you have to start the animation:
private void startAnimation(final JPanel panel){
//create a anonym class
Runnable r = new Runnable(){
private int py = 0; //previous values
private int py = 0; //previous values
#overrdie
public void run(){
for(int x = 0; x < endOfX ; x++){
int y = data[x];
//now we have x and y, so you can plot your function;
gr.drawLine(px, py, x,y); //you can scale here
int sleeptime = calculateSleepTime(px,py, x,y);
Thread.sleep(sleeptime);
//set the previouse values;
px = x;
py = y;
//important - repaint your panel to create an 'animation'
panel.repaint();
}
}
}
//having that runnable we must start that runnable within an thread
Thread thread = new Thread(r);
thread.setDaemon(true);
thread.start();
}
so whats left to do? we must calculate the sleep time: if the distance between two points is 'big' we sleep longer, if the distance is short, we sleep less...
public int calculateSleeptime(int px, int py, int x, int y){
int distance = (y-py)*(y-py)+(x-px)*(x-px);
distance = (int)(Math.sqrt(distance);
int sleepTime = distance*100; //play with this value!
return sleeptime;
}
I have written that code all out of my head, i didn't have any IDE to check if it contains any spelling errors or compiling errors, please do that for yourself, as well as i didn't initiate the BufferedImage img ang Graphics gr. but obvious you can do that already!

Nesting issue in Processing

I'm having an issue with the animation I'm making. The principal idea is that 6 equilateral triangles revolve around a central point, while also rotating about their own selves.
When I run the code, each instance of a triangle uses the previous instance as a reference point, rather than the centre. This causes a cool spiral effect, but it's not what I'm after.
Code follows:
//Declare
tri myTri1;
tri myTri2;
tri myTri3;
tri myTri4;
tri myTri5;
tri myTri6;
void setup() {
size(600, 600);
smooth();
//Initialise
myTri1 = new tri();
myTri2 = new tri();
myTri3 = new tri();
myTri4 = new tri();
myTri5 = new tri();
myTri6 = new tri();
}
void draw() {
background(0);
//Call Functions
myTri1.run();
translate(width/2,height/2);
rotate(PI/3);
translate(-width/2,-height/2);
myTri2.run();
translate(width/2,height/2);
rotate(PI/3);
translate(-width/2,-height/2);
myTri3.run();
translate(width/2,height/2);
rotate(PI/3);
translate(-width/2,-height/2);
myTri4.run();
translate(width/2,height/2);
rotate(PI/3);
translate(-width/2,-height/2);
myTri5.run();
translate(width/2,height/2);
rotate(PI/3);
translate(-width/2,-height/2);
myTri6.run();
}
Second tab:
class tri {
//Variables
float ax, ay, bx, by, cx, cy; //triangle point coordinates
float theta; //triangle angle
float pi = PI; //pi reference
//Construct
tri() {
theta = PI/6;
ax = 0;
ay = 0;
bx = -50*(sin(theta));
by = +50*(cos(theta));
cx = +50*(sin(theta));
cy = +50*(cos(theta));
}
//Functions
void run() {
translate(width/2, height/2);
revolve(); //revolve triangle about centre
spin(); //spin triangle about itself
pulse(); //move triangle in/out
display(); //show triangle
translate(-width/2, -height/2);
}
void spin() {
translate(0, by/2); //sets rotation axis to centre of triangle
rotate(millis()*-0.0005*pi);
translate(0, -by/2); //resets axis to centre of window
}
void revolve() {
translate(-2*by, 0);
ax = ax + 2*sin(millis()*0.005);
ay = ay + 4*cos(millis()*0.005);
bx = bx + 2*sin(millis()*0.005);
by = by + 4*cos(millis()*0.005);
cx = cx + 2*sin(millis()*0.005);
cy = cy + 4*cos(millis()*0.005);
translate(2*by, 0);
}
void pulse() {
ay = ay + 5*sin(millis()*0.005);
by = by + 5*sin(millis()*0.005);
cy = cy + 5*sin(millis()*0.005);
}
void display() {
fill(255);
strokeWeight(0.8);
triangle(ax, ay, bx, by, cx, cy);
}
}
If anyone can point out where I'm going wrong with this it would be awesome, and if you can suggest any optimisations RE the formation of the hexagon (instead of the mess of translations) I would be incredibly happy.
Franchesca's suggestion good. You should have an idea of where the origin is and how the coordinate space transformations you apply affect that, at least until you get a feel for it and you're in complete control.
I also warmly recommend this Processing tutorial on 2d transformations
Now, back to your code :)
First thing you can improve is getting used to for loops and arrays.
They may look scary at first, but once you get the hang of them they're quite easy.
Wherever you can think of a situation where repetition is needed, you can use a for loop to make your life easier.
In your case, generating the triangles and storing them can be done using loops and arrays.
For loop have the following syntax:
for keyword (3 elements: a start point,an end point(condition) and an increment,(separated by the ; character)
Let's say you want to move from a(0) to b(10) one step at a time:
for(int currentPos = 0 ; currentPos < 10; currentPos++){
println("step: " + currentPos);
}
If you can walk, you can also skip :)
for(int currentPos = 0 ; currentPos < 10; currentPos+=2){
println("step: " + currentPos);
}
even backwards if you want:
for(int currentPos = 10 ; currentPos > 0; currentPos--){
println("step: " + currentPos);
}
This is very useful when traversing all sort of data(triangles in a scene, vertices in a triangle, etc.)
How do you organize your data ? You place it in a list or array.
An array contains elements of the same type and has a set length.
The syntax to declare an array is like so:
ObjectType[] nameOfArray;
and you can initialize an empty array:
int[] fiveNumbers = new int[5];//new keyword then the data type and length in sq.brackets
or you can initialize the array with values:
String[] words = {"ini","mini","miny","moe"};
You access elements in an array using square brackets and the index of the object in the list you want to access. Arrays have a length property so you can easily count objects.
background(255);
String[] words = {"ini","mini","miny","moe"};
for(int i = 0 ; i < words.length; i++){
fill(map(i,0,words.length, 0,255));
text(words[i],10,10*(i+1));
}
Now back to your original question.
Here is your main code simplified using for loops and arrays:
//Declare
int numTri = 6;//number of triangles
tri[] triangles = new tri[numTri];//a list/an array of tri objects (currently empty)
float angleIncrement = TWO_PI/numTri;
float radius = 100;
void setup() {
size(600, 600);
smooth();
//Initialise
for(int i = 0 ; i < numTri; i++){
triangles[i] = new tri();//allocate/initialise each tri object into it's 'slot' in the list/array
}
}
void draw() {
background(0);
translate(width * .5, height * .5);//move everything to the centre
for(int i = 0 ; i < numTri; i++){
pushMatrix();
rotate(angleIncrement * i);//rotate from the last offset(centre)
translate(radius,0);//move on (rotated) X axis away from the centre
triangles[i].run();
popMatrix();
}
}
void drawAxes(int size){
pushStyle();
stroke(255,0,0);
line(0,0,size,0);
stroke(0,255,0);
line(0,0,0,size);
popStyle();
}
Notice I've indented the code within push/pop matrix calls.
It's not necessary but I've added that so you can get a feel for how coordinate spaces nest.
These call are very useful as they deal with the nitty gritty math part behind the scenes for you. Notice how I'm placing the symbols in a circle without using the polar to cartesian conversion formula (cos(angle) * radius, sin(angle) * radius).
You can test that with this code from your other tab:
class tri {
//Variables
float ax, ay, bx, by, cx, cy; //triangle point coordinates
float theta; //triangle angle
float pi = PI; //pi reference
//Construct
tri() {
theta = PI/6;
ax = 0;
ay = 0;
bx = -50*(sin(theta));
by = +50*(cos(theta));
cx = +50*(sin(theta));
cy = +50*(cos(theta));
}
//Functions
void run() {
pushMatrix();
revolve(); //revolve triangle about centre
// pulse(); //move triangle in/out
display(); //show triangle
popMatrix();
}
void revolve() {
translate(-2*by, 0);
float angle = millis()*0.005;
float cos = cos(angle);
float sin = sin(angle);
ax = ax + 2*sin;
ay = ay + 4*cos;
bx = bx + 2*sin;
by = by + 4*cos;
cx = cx + 2*sin;
cy = cy + 4*cos;
translate(2*by, 0);
}
void pulse() {
ay = ay + 5*sin(millis()*0.005);
by = by + 5*sin(millis()*0.005);
cy = cy + 5*sin(millis()*0.005);
}
void display() {
fill(255);
strokeWeight(0.8);
triangle(ax, ay, bx, by, cx, cy);
}
}
Also notice I've added a drawAxes function. That's just a utility to make it easier to understand in what coordinate space your drawing.
Again, going back to arrays and for loops, here's a modified version of your code:
class tri {
//Variables
float ai = TWO_PI/3;//angle increment
float r = 50;
float sr = r * 1.5;//spin radius
float vt = 5;//vertical translation(for pulse)
PVector[] verts = new PVector[3];
boolean rotateAroundCentre = true;
boolean translateAroundCentre = false;
boolean translateVertically = false;
//Construct
tri() {
for(int i = 0 ; i < 3; i++){
verts[i] = new PVector(cos(ai * i) * r,sin(ai * i) * r);
}
}
//Functions
void run() {
pushMatrix();
float angle = millis()*0.0005;
if(rotateAroundCentre) rotate(angle);
if(translateVertically) translate(sin(angle)*vt,0);
if(translateAroundCentre){
// translate(cos(angle) * sr,sin(angle) * r);
// or
rotate(angle);
translate(sr,0);
}
display(); //show triangle
popMatrix();
}
void display() {
fill(255);
strokeWeight(0.8);
triangle(verts[0].x, verts[0].y, verts[1].x, verts[1].y, verts[2].x, verts[2].y);
drawAxes(10);
}
}
Feel free to play with the boolean rotateAroundCentre,translateAroundCentre,translateVertically variables and have fun playing with coordinates and geometry :)
For example here's a version of the sketch that you can toggle the 3 options above using the 1/2/3 keys on your keyboard:
//Declare
int numTri = 6;//number of triangles
tri[] triangles = new tri[numTri];//a list/an array of tri objects (currently empty)
float angleIncrement = TWO_PI/numTri;
float radius = 100;
boolean[] options = {false,false,false};
void setup() {
size(600, 600);
smooth();
//Initialise
for(int i = 0 ; i < numTri; i++){
triangles[i] = new tri();//allocate/initialise each tri object into it's 'slot' in the list/array
}
}
void draw() {
background(0);
translate(width * .5, height * .5);//move everything to the centre
for(int i = 0 ; i < numTri; i++){
pushMatrix();
rotate(angleIncrement * i);//rotate from the last offset(centre)
translate(radius,0);//move on (rotated) X axis away from the centre
drawAxes(20);
triangles[i].run();
popMatrix();
}
}
void drawAxes(int size){
pushStyle();
stroke(255,0,0);
line(0,0,size,0);
stroke(0,255,0);
line(0,0,0,size);
popStyle();
}
void keyReleased(){
for(int i = 0 ; i < 3; i++) if(key == (49+i)) options[i] = !options[i];//quick'n'dirty option toggling
for(int i = 0; i < numTri; i++) {
triangles[i].rotateAroundCentre = options[0];
triangles[i].translateAroundCentre = options[1];
triangles[i].translateVertically = options[2];
}
}
class tri {
//Variables
float ai = TWO_PI/3;//angle increment
float r = 50;
float sr = r * 1.5;//spin radius
float vt = 5;//vertical translation(for pulse)
PVector[] verts = new PVector[3];
boolean rotateAroundCentre = false;
boolean translateAroundCentre = false;
boolean translateVertically = false;
//Construct
tri() {
for(int i = 0 ; i < 3; i++){
verts[i] = new PVector(cos(ai * i) * r,sin(ai * i) * r);
}
}
//Functions
void run() {
pushMatrix();
float angle = millis()*0.0005;
if(rotateAroundCentre) rotate(angle);
drawAxes(30);
if(translateVertically) translate(sin(angle)*vt,0);
drawAxes(40);
if(translateAroundCentre){
// translate(cos(angle) * sr,sin(angle) * r);
// or
rotate(angle);
drawAxes(40);
translate(sr,0);
}
display(); //show triangle
popMatrix();
}
void display() {
fill(255);
strokeWeight(0.8);
triangle(verts[0].x, verts[0].y, verts[1].x, verts[1].y, verts[2].x, verts[2].y);
drawAxes(10);
}
}

Categories

Resources