How can I handle drawing a circle, having that circle break, and begin drawing elsewhere? - geometry

Working in Processing, I am trying to build my first generative patch. What I want to have happen is start drawing a circle somewhere on screen (a point following the path of a circle), but after a random amount of time, the circle breaks, the line goes in a random direction for a random amount of time, and begins drawing a new circle elsewhere.
Right now I have the circle being drawn, and I have a toggle mechanism that turns on and off after a random period of time. I can't figure out how to get it "break" from that original circle, let alone get it to start a new circle elsewhere. Would anybody have some advice on how to accomplish this? I think it might have an interesting visual effect.
Rotor r;
float timer = 0;
boolean freeze = false;
void setup() {
size(1000,600);
smooth();
noFill();
frameRate(60);
background(255);
timeLimit();
r = new Rotor(random(width),random(height),random(40,100));
}
void draw() {
float t = frameCount / 100.0;
timer = timer + frameRate/1000;
r.drawRotor(t);
if(timer > timeLimit()){
timer = 0;
timeLimit();
if(freeze == true){
freeze = false;
}else{
freeze = true;
}
background(255);
}
}
float timeLimit(){
float timeLimit = random(200);
return timeLimit;
}
Rotor Class:
class Rotor {
color c;
int thickness;
float xPoint;
float yPoint;
float radius;
float angle = 0;
float centerX;
float centerY;
Rotor(float cX, float cY, float rad) {
c = color(0);
thickness = 1;
centerX = cX;
centerY = cY;
radius = rad;
}
void drawRotor(float t) {
stroke(c);
strokeWeight(thickness);
angle = angle + frameRate/1000;
xPoint = centerX + cos(angle) * radius;
yPoint = centerY + sin(angle) * radius;
ellipse(xPoint, yPoint,thickness,thickness);
}
}

First to answer your question about "breaking" circle: you need to create new rotor instance or just change its properties like center and radius. If I got your idea right you just need one instance of rotor so just change this values:
r.centerX = newX;
r.centerY = newY
r.radius = random(40,100) //as you have in setup
But how you can calculate new position? It could be random but you want to create path so you need to calculate it. And here comes the tricky part. So how to make connecting line and start new circle?
First you will need two mode. First will draw circle second will draw line. Simplest way to achieve that is by updating rotor draw method [You can pass mode variable as parameter of drawRotor function or as global variable]:
if(mode == 1){
angle += frameRate/1000;
}else{
radius += 2;
}
As you can see I just differ between increasing angle to draw circle and increasing radius to draw line (not in random direction but in way from center). Then we will need to calculate new position of circle's center. To do this we simple calculate how it would continue according to angle and substitute new radiusso whole part will looks like this:
if(mode != 1){
float newR = random(40,100);
float newX = r.centerX + cos(r.angle) * (r.radius - newR);
float newY = r.centerY + sin(r.angle) * (r.radius - newR);
r.newPos(newX, newY);
r.radius = newR; //we cant change it earlier because we need also old value
}
This will happen inside your "time handler" function only when you change mode back to drawing circle. Mode can be simple changed within handler
mode *= -1; //but need to be init to 1 inside setup()
If you want to have path always visible just delete background() function but if you want some cool effect add this at the begging of draw()
noStroke(); //No stroke needed and you turn it on again in drawRotor()
fill( 255,255,255, 10 ); //This will set transparency to 10%
rect(0,0,width,height); //You put layer after each "point" you draw
noFill(); //This will restore fill settings as you have before
Here I paste whole code just for demonstration and you should modify it according your own purpose. Better to code own version.

The call to background()usually comes as first thing in draw. That's because the draw only renders at the end of each loop (frame). So calling bg at the beginning will clear all stuff drawn in last frame. If you need to persist the draws trough frames can either remove the call to background() or draw your stuff every frame. Or yet draw stuff in a PGraphics and display it.
The other thing is each time the 'Rotor' stops you should give it new random coordinates.
If you go for removing the background() call this will do the trick:
Rotor r;
float timer = 0;
boolean freeze = false;
void setup() {
size(1000,600);
smooth();
noFill();
frameRate(60);
background(255);
timeLimit();
r = new Rotor(random(width),random(height),random(40,100));
}
void draw() {
float t = frameCount / 100.0;
timer = timer + frameRate/1000;
r.drawRotor(t);
if(timer > timeLimit()){
timer = 0;
timeLimit();
//***** here new coordinates!!
r = new Rotor(random(width),random(height),random(40,100));
//*****
if(freeze == true){
freeze = false;
}else{
freeze = true;
}
//***** no background()
// background(255);
}
}
float timeLimit(){
float timeLimit = random(200);
return timeLimit;
}
class Rotor {
color c;
int thickness;
float xPoint;
float yPoint;
float radius;
float angle = 0;
float centerX;
float centerY;
Rotor(float cX, float cY, float rad) {
c = color(0);
thickness = 1;
centerX = cX;
centerY = cY;
radius = rad;
}
void drawRotor(float t) {
stroke(c);
strokeWeight(thickness);
angle = angle + frameRate/1000;
xPoint = centerX + cos(angle) * radius;
yPoint = centerY + sin(angle) * radius;
ellipse(xPoint, yPoint,thickness,thickness);
}
}
now, if you need to clear the screen, You can make a List (ArrayList?) and add a new Rotor to it when the previous is done. But you need to manage the Rotor to be able to display it self without animating as well. So new created Rotor would animate, and old ones would just display its arc without animating. Or make a PGraphis with no call to bg and display it in main canvas that can have a bg call...
A side note, be aware that relying in frameRate to times stuff makes it dependable on the system performance. You can do the same thing using millis()to avoid that. Not an issue so far, as this is very light yet, but may become an issue if the project grows further.

Related

Processing fft crash

Weird thing. I keep getting processing or java to crash with this code which is based on a sample code from the processing website.
On pc it doesn't work at all, on one mac it works for 5 seconds until it crushes and on another mac it just crust and gives me this:
libc++abi.dylib: terminating with uncaught exception of type std::runtime_error: RtApiCore::probeDeviceOpen: the device (2) does not support the requested channel count.
Could not run the sketch (Target VM failed to initialize).
Do you think it's a problem with the library or with the code?
If it's a problem with the library, could you recommend the best sound library to do something like this?
Thank you :)
import processing.sound.*;
FFT fft;
AudioIn in;
int bands = 512;
float[] spectrum = new float[bands];
void setup() {
size(900, 600);
background(255);
// Create an Input stream which is routed into the Amplitude analyzer
fft = new FFT(this, bands);
in = new AudioIn(this, 0);
// start the Audio Input
in.start();
// patch the AudioIn
fft.input(in);
}
void draw() {
background(255);
int midPointW = width/2;
int midPointH = height/2;
float angle = 1;
fft.analyze(spectrum);
//float radius = 200;
for(int i = 0; i < bands; i++){
// create the actions for placing points on a circle
float radius = spectrum[i]*height*10;
//float radius = 10;
float endX = midPointW+sin(angle) * radius*10;
float endY = midPointH+cos(angle) * radius*10;
float startX = midPointW+sin(angle) * radius*5;
float startY = midPointH+cos(angle) * radius*5;
// The result of the FFT is normalized
// draw the line for frequency band i scaling it up by 5 to get more amplitude.
line( startX, startY, endX, endY);
angle = angle + angle;
println(endX, "" ,endY);
// if(angle > 360){
// angle = 0;
// }
}
}
If you print the values you use like angle and start x,y you'll notice that:
start/end x,y values become NaN(not a number - invalid)
angle quickly goes to Infinity (but not beyond)
One of the main issues is this line:
angle = angle + angle;
You're exponentially increasing this value which you probably don't want.
Additionally, bare in mind trigonometric functions such as sin() and cos() use radians not degrees, so values tend to be small. You can constrain the values to 360 degrees or TWO_PI radians using the modulo operator(%) or the constrain() function:
angle = (angle + 0.01) % TWO_PI;
You were very close though as your angle > 360 check shows it. Not sure why you've left that commented out.
Here's your code with the tweak and comments
import processing.sound.*;
FFT fft;
AudioIn in;
int bands = 512;
float[] spectrum = new float[bands];
void setup() {
size(900, 600);
background(255);
// Create an Input stream which is routed into the Amplitude analyzer
fft = new FFT(this, bands);
in = new AudioIn(this, 0);
// start the Audio Input
in.start();
// patch the AudioIn
fft.input(in);
}
void draw() {
background(255);
int midPointW = width/2;
int midPointH = height/2;
float angle = 1;
fft.analyze(spectrum);
//float radius = 200;
for (int i = 0; i < bands; i++) {
// create the actions for placing points on a circle
float radius = spectrum[i] * height * 10;
//float radius = 10;
float endX = midPointW + (sin(angle) * radius * 10);
float endY = midPointH + (cos(angle) * radius * 10);
float startX = midPointW + (sin(angle) * radius * 5);
float startY = midPointH + (cos(angle) * radius * 5);
// The result of the FFT is normalized
// draw the line for frequency band i scaling it up by 5 to get more amplitude.
line( startX, startY, endX, endY);
//angle = angle + angle;
angle = (angle + 0.01) % TWO_PI;//linearly increase the angle and constrain it to a 360 degrees (2 * PI)
}
}
void exit() {
in.stop();//try to cleanly stop the audio input
super.exit();
}
The sketch ran for more than 5 minutes but when closing the sketch I still encountered JVM crashes on OSX.
I haven't used this sound library much and haven't looked into it's internals, but it might be a bug.
If this still is causing problems, for pragmatic reasons I'd recommend installing a different Processing library for FFT sound analysis via Contribution Manager.
Here are a couple of libraries:
Minim - provides some nice linear and logarithmic averaging functions that can help in visualisations
Beads - feature rich but more Java like syntax. There's also a free book on it: Sonifying Processing
Both libraries provide FFT examples.

Processing: How do I make an object move in a circular path?

I have created a class where I define Shape objects for my program. Each of these Shapes has a transparent ellipse drawn around it (I defined that in my constructor) and if any other Shape moves into that circular ellipse area, I want that Shape to change it's direction so that it moves in a circular path.
Each Shape object has a defined radius attribute (because of the ellipse I draw around each object) and I want to use that value to determine how big of a circular pattern the Shape has to move in when it collides.
Please help! Anything is greatly appreciated!
EDIT:
As I said above, I want the shape to move into a circular path. HOWEVER, I want it only to move in a circular path once (meaning it moves around a circle once) and then I want it to continue on the original path it was programmed with.
The short answer is that you'll have to use basic trig to figure out the angle between the points, and then more basic trig to determine subsequent points on the circular path.
Check out the trigonometry section of the Processing reference for more info.
But basically, if you have two points, you can use the atan2() function to calculate the angle between them. You'd use this to find the starting angle from the center of your circle to the shape.
Once you have that angle, you can simply increment it, and then use cos() and sin() to figure out the x and y coordinates at that new angle.
Here is a basic sketch that does all of the above:
PVector center;
float angle;
float radius;
void setup() {
size(500, 500);
center = new PVector(width/2, height/2);
//get the initial point
//for you, this would be the initial location of the object
PVector point = new PVector(random(width), random(height));
//find the angle between the points
float deltaX = center.x - point.x;
float deltaY = center.y - point.y;
angle = atan2(deltaX, deltaY);
//find the radius of the circle
radius = dist(center.x, center.y, point.x, point.y);
ellipseMode(RADIUS);
}
void draw() {
background(0);
//draw the center point
ellipse(center.x, center.y, 10, 10);
//find the point based on the angle
float x = center.x + cos(angle)*radius;
float y = center.y + sin(angle)*radius;
//draw the traveling point
ellipse(x, y, 10, 10);
//increment the angle to move the point
angle += PI/120;
}
Well, before I saw Kevin's post, I did one also. Not using objects, just a simple procedural example. Posting anyway :)
PVector pos, speed, stored;
float diam = 40;
boolean wonder = false;
float angle = 0;
void setup() {
size(300, 300);
// arbitrary positioning and speeding
pos = new PVector(-20, height/2);
speed = new PVector(1, 0);
noStroke();
}
void draw() {
background(5);
// normally increment speed
if (!wonder) {
pos.add(speed);
} else {
// if is to wonder...
if (angle <= 360) {
//get circle path by trig
pos.x = stored.x + cos(radians(angle))*diam;
pos.y = stored.y + sin(radians(angle))*diam;
} else {
// if the circle is complete
// reset angle and stop wondering
wonder = false;
angle = 0;
}
// increment angle
angle++;
}
// draw
ellipse(pos.x, pos.y, diam, diam);
}
void mouseClicked() {
if (isOverCircle() ) {
// store position where it has being clicked
stored = pos.get();
// off set the diam
stored.x -= diam;
// trig wondering
wonder = true;
angle = 0;
}
}
boolean isOverCircle() {
float disX = pos.x - mouseX;
float disY = pos.y - mouseY;
return sqrt(sq(disX) + sq(disY)) < diam/2;
}

Converting client coordinates to Pixel coordinates for simulating a mouse click in MFC

I am trying to simulate a mouse click on the CView window in a legacy code which I must say I don't fully understand. The idea is to search for a particular item in the CView, get its co-ordinates and then simulate a right mouse click on it using SendInput. I want to understand if the basic steps I am following are correct before I proceed digging further into the legacy code which has a bunch of transformations happening across co-ordinate systems :( Here are the steps I follow:
Get the position co-ordinates of the item displayed in CView. at this point the co-ordinates is in the internal co-ordinate system (lets call it CDPoint).
CDPoint gPosn = viewObj->m_point_a ;
Covert the co-ordinates to the client co-ordinate system i.e CDPoint to CPoint using the existing transformations in the code.
CPoint newPosn = GetTransform().Scale(gPosn);
//Note: The basis of arriving that this is the correct transformation to use is the below code with the exact reverse transform happening in the mouse click handler code to convert CPoint to CDPoint:
`CDesignView::OnLButtonDown(UINT nFlags, CPoint p) {
CDPoint np = GetTransform().DeScale(p);
}`
Is this thinking right that CPoint received in the OnLButtonDown() handler will always be in the client co-ordinates and hence the reverse transform should convert CDPoint (internal co-ordinates) to client coordinates (CPoint) ?
Convert client co-ordinates to screen co-ordinates:
ClientToScreen(&newPosn);
Pass these values to SendInput function after converting to pixel co-ordinates:
INPUT buffer[1];
MouseSetup(buffer);
MouseMoveAbsolute(buffer, newPos.x, newPos.y);
MouseClick(buffer);
The Mousexxx() functions are defined as below similar to the sample code in this post:
How to simulate a mouse movement
.
#define SCREEN_WIDTH (::GetSystemMetrics( SM_CXSCREEN )-1)
#define SCREEN_HEIGHT (::GetSystemMetrics( SM_CYSCREEN )-1)
static void inline makeAbsXY(double &x, double &y) {
x = (x * 0xFFFF) / SCREEN_WIDTH ;
y = (y * 0xFFFF) / SCREEN_HEIGHT ;
}
static void inline MouseSetup(INPUT *buffer)
{
buffer->type = INPUT_MOUSE;
buffer->mi.dx = (0 * (0xFFFF / SCREEN_WIDTH));
buffer->mi.dy = (0 * (0xFFFF / SCREEN_HEIGHT));
buffer->mi.mouseData = 0;
buffer->mi.dwFlags = MOUSEEVENTF_ABSOLUTE;
buffer->mi.time = 0;
buffer->mi.dwExtraInfo = 0;
}
static void inline MouseMoveAbsolute(INPUT *buffer, double x, double y)
{
makeAbsXY(x,y) ;
buffer->mi.dx = x ;
buffer->mi.dy = y ;
buffer->mi.dwFlags = (MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE);
SendInput(1, buffer, sizeof(INPUT));
}
static void inline MouseClick(INPUT *buffer)
{
buffer->mi.dwFlags = (MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_RIGHTDOWN);
SendInput(1, buffer, sizeof(INPUT));
Sleep(10);
buffer->mi.dwFlags = (MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_RIGHTUP);
SendInput(1, buffer, sizeof(INPUT));
}
Could anyone pls provide pointers on what might be going wrong in these steps since the simulated mosue click always seem to be shifted left by some factor which keeps increasing as x becoems larger. I have verified that is gPosn is pointing to (0,0) it always simulates a mouse click on the top right corner of the client screen.
Thanks for your time.
If you have x and y in client coordinates, you have to convert them to screen coordinates:
POINT point;
point.x = x;
point.y = y;
::ClientToScreen(m_hWnd, point);
Where m_hWnd is the window which owns the objects. x and y are relative to top-left of the client area of this window.
Assuming point.x and point.y are in screen coordinates, the rest of the conversion for SendInput is correct. You can also create INPUT array for SendInput, this will send the mouse messages without interruption.
INPUT input[3];
for (int i = 0; i < 3; i++)
{
memset(&input[i], 0, sizeof(INPUT));
input[i].type = INPUT_MOUSE;
}
input[0].mi.dx = (point.x * 0xFFFF) / (GetSystemMetrics(SM_CXSCREEN) - 1);
input[0].mi.dy = (point.y * 0xFFFF) / (GetSystemMetrics(SM_CYSCREEN) - 1);
input[0].mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
input[1].mi.dwFlags = MOUSEEVENTF_RIGHTDOWN;
input[2].mi.dwFlags = MOUSEEVENTF_RIGHTUP;
SendInput(3, input, sizeof(INPUT));

How to handle ball to ball collision with trigonometry instead of vectors?

I am making a simplified version of the ball physics app found at this SO question. I gained a lot from reading through the code and links provided at that question, but in my program I do not use vectors, I just update the coordinates of my balls (each of which has a random angle and speed) with trigonometry functions when they hit the walls.
There is info all over the place for how to handle ball to ball collisions without trigonometry, but I have found none that explain how it can be done with trigonometry.
--EDIT 9/13/2010--
Success... kind of... I got done what I wanted to do, but I was unable to do it how I wanted. If there is a way to calculate ball to ball collisions without the use of vectors it has eluded me. Even so, vectors do seem to be an easier way to handle collisions of all types... I just wish I would have known that when I started my program... would have saved me two or three days of work :) All the code for my (complete?) program is below. I added some neat features like shadows and a decreasing ball radius which really lets you see the difference in the mass of two balls when a big ball hits a small ball. In total there are five class files, AddLogic.java, Ball.java, BallBuilder.java, MouseEventHandler.java, and Vector2D.java.
AddLogic.java
import java.awt.*;
import java.awt.Graphics2D;
import java.awt.image.BufferStrategy;
import java.util.ArrayList;
public class AddLogic implements Runnable {//Make AddLogic a runnable task.
private BallBuilder ballBuilder;
private BufferStrategy strategy;
private static ArrayList objectsToDraw = new ArrayList();
private int floorHeight = 33;
public AddLogic(BallBuilder ballBuilder, BufferStrategy strategy) {
this.ballBuilder = ballBuilder;
this.strategy = strategy;
}
private void logic(BallBuilder ballBuilder, BufferStrategy strategy) {
this.ballBuilder = ballBuilder;
this.strategy = strategy;
while (true) {//Main loop. Draws all objects on screen and calls update methods.
Graphics2D g = (Graphics2D) strategy.getDrawGraphics();//Creates the Graphics2D object g and uses it with the double buffer.
g.setColor(Color.gray);
g.fillRect(0, 0, ballBuilder.getWidth(), ballBuilder.getHeight());//Draw the wall.
g.setColor(Color.lightGray);
g.fillRect(0, ballBuilder.getHeight() - floorHeight, ballBuilder.getWidth(), floorHeight);//Draw the floor.
g.setColor(Color.black);
g.drawLine(0, ballBuilder.getHeight() - floorHeight, ballBuilder.getWidth(), ballBuilder.getHeight() - floorHeight);//Draw the line between the wall and floor.
if (objectsToDrawIsEmpty() == true) {//If no balls have been made display message telling users how to make new ball.
g.setColor(Color.red);
g.drawString("Click Mouse For New Ball", (ballBuilder.getWidth() / 2) - 70, ballBuilder.getHeight() / 2);
}
for (int i = 0; i < objectsToDraw.size(); i++) {//Draw shadows for all balls.
Ball ball = (Ball) objectsToDraw.get(i);
g.setColor(Color.darkGray);
g.fillOval(
(int) ball.ballPosition.getX() - (int) ((ball.ballPosition.getY() / (350 / ball.getBallRadius())) / 2),
ballBuilder.getHeight() - (floorHeight / 2) - (int) ((ball.ballPosition.getY() / (1250 / ball.getBallRadius())) / 2),
(int) ball.ballPosition.getY() / (350 / ball.getBallRadius()),
(int) ball.ballPosition.getY() / (1250 / ball.getBallRadius()));
}
for (int i = 0; i < objectsToDraw.size(); i++) {//Draw all balls by looping through them and checking for any vector or collision updates that need to be made.
Ball ball = (Ball) objectsToDraw.get(i);
g.setColor(ball.getBallColor());
g.fillOval(
(int) ball.ballPosition.getX() - ball.getBallRadius(),
(int) ball.ballPosition.getY() - ball.getBallRadius(),
ball.getBallRadius() * 2,
ball.getBallRadius() * 2);
vectorUpdate(ball);//Update ball vector coordinates.
collisionCheck(ball);//Check ball to ball and ball to wall collisions.
}
if (MouseEventHandler.mouseEventCheck() == true) {// Creates a new ball when mouse is clicked.
Ball ball = new Ball(ballBuilder);
objectsToDraw.add(ball); //Adds the new ball to the array list.
MouseEventHandler.mouseEventUpdate(); //Resets the mouse click event to false.
}
g.dispose();//To aid Java in garbage collection.
strategy.show();//Show all graphics drawn on the buffer.
try {//Try to make thread sleep for 5ms. Results in a frame rate of 200FPS.
Thread.sleep(5);
}
catch (Exception e) {//Catch any exceptions if try fails.
}
}
}
private void vectorUpdate(Ball ball) {//Update the ball vector based upon the ball's current position and its velocity.
ball.ballPosition.setX(ball.ballPosition.getX() + ball.ballVelocity.getX());
ball.ballPosition.setY(ball.ballPosition.getY() + ball.ballVelocity.getY());
}
private void collisionCheck(Ball ball) {//Check for ball to wall collisions. Call check for ball to ball collisions at end of method.
if (ball.ballPosition.getX() - ball.getBallRadius() < 0) {//Check for ball to left wall collision.
ball.ballPosition.setX(ball.getBallRadius());
ball.ballVelocity.setX(-(ball.ballVelocity.getX()));
ball.decreaseBallRadius(ball);//Decrease ball radius by one pixel. Called on left, top, and right walls, but not bottom because it looks weird watching shadow get smaller during bottom bounce.
}
else if (ball.ballPosition.getX() + ball.getBallRadius() > ballBuilder.getWidth()) {//Check for ball to right wall collision.
ball.ballPosition.setX(ballBuilder.getWidth() - ball.getBallRadius());
ball.ballVelocity.setX(-(ball.ballVelocity.getX()));
ball.decreaseBallRadius(ball);//Decrease ball radius by one pixel. Called on left, top, and right walls, but not bottom because it looks weird watching shadow get smaller during bottom bounce.
}
if (ball.ballPosition.getY() - ball.getBallRadius() < 0) {//Check for ball to top wall collision.
ball.ballPosition.setY(ball.getBallRadius());
ball.ballVelocity.setY(-(ball.ballVelocity.getY()));
ball.decreaseBallRadius(ball);//Decrease ball radius by one pixel. Called on left, top, and right walls, but not bottom because it looks weird watching shadow get smaller during bottom bounce.
}
else if (ball.ballPosition.getY() + ball.getBallRadius() + (floorHeight / 2) > ballBuilder.getHeight()) {//Check for ball to bottom wall collision. Floor height is accounted for to give the appearance that ball is bouncing in the center of the floor strip.
ball.ballPosition.setY(ballBuilder.getHeight() - ball.getBallRadius() - (floorHeight / 2));
ball.ballVelocity.setY(-(ball.ballVelocity.getY()));
}
for (int i = 0; i < objectsToDraw.size(); i++) {//Check to see if a ball is touching any other balls by looping through all balls and checking their positions.
Ball otherBall = (Ball) objectsToDraw.get(i);
if (ball != otherBall && Math.sqrt(Math.pow(ball.ballPosition.getX() - otherBall.ballPosition.getX(), 2.0) + Math.pow(ball.ballPosition.getY() - otherBall.ballPosition.getY(), 2.0)) < ball.getBallRadius() + otherBall.getBallRadius()) {
resolveBallToBallCollision(ball, otherBall);//If the ball is hitting another ball calculate the new vectors based on the variables of the balls involved.
}
}
}
private void resolveBallToBallCollision(Ball ball, Ball otherBall) {//Calculate the new vectors after ball to ball collisions.
Vector2D delta = (ball.ballPosition.subtract(otherBall.ballPosition));//Difference between the position of the two balls involved in the collision.
float deltaLength = delta.getLength();//The (x, y) of the delta squared.
Vector2D minimumTranslationDistance = delta.multiply(((ball.getBallRadius() + otherBall.getBallRadius()) - deltaLength) / deltaLength);//The minimum distance the balls should move apart once they.
float ballInverseMass = 1 / ball.getBallMass();//half the ball mass.
float otherBallInverseMass = 1 / otherBall.getBallMass();//half the other ball mass.
ball.ballPosition = ball.ballPosition.add(minimumTranslationDistance.multiply(ballInverseMass / (ballInverseMass + otherBallInverseMass)));//Calculate the new position of the ball.
otherBall.ballPosition = otherBall.ballPosition.subtract(minimumTranslationDistance.multiply(otherBallInverseMass / (ballInverseMass + otherBallInverseMass)));//Calculate the new position of the other ball.
Vector2D impactVelocity = (ball.ballVelocity.subtract(otherBall.ballVelocity));//Find the veloicity of the impact based upon the velocities of the two balls involved.
float normalizedImpactVelocity = impactVelocity.dot(minimumTranslationDistance.normalize());//
if (normalizedImpactVelocity > 0.0f) {//Returns control to calling object if ball and other ball are intersecting, but moving away from each other.
return;
}
float restitution = 2.0f;//The constraint representing friction. A value of 2.0 is 0 friction, a value smaller than 2.0 is more friction, and a value over 2.0 is negative friction.
float i = (-(restitution) * normalizedImpactVelocity) / (ballInverseMass + otherBallInverseMass);
Vector2D impulse = minimumTranslationDistance.multiply(i);
ball.ballVelocity = ball.ballVelocity.add(impulse.multiply(ballInverseMass));//Change the velocity of the ball based upon its mass.
otherBall.ballVelocity = otherBall.ballVelocity.subtract(impulse.multiply(otherBallInverseMass));//Change the velocity of the other ball based upon its mass.
}
public static boolean objectsToDrawIsEmpty() {//Checks to see if there are any balls to draw.
boolean empty = false;
if (objectsToDraw.isEmpty()) {
empty = true;
}
return empty;
}
public void run() {//Runs the AddLogic instance logic in a new thread.
logic(ballBuilder, strategy);
}
}
Ball.java
import java.awt.*;
public class Ball {
private int ballRadius;
private float ballMass;
public Vector2D ballPosition = new Vector2D();
public Vector2D ballVelocity = new Vector2D();
private Color ballColor;
public Ball(BallBuilder ballBuilder) {//Construct a new ball.
this.ballRadius = 75;//When ball is created make its radius 75 pixels.
this.ballMass = ((float)(4 / 3 * Math.PI * Math.pow(ballRadius, 3.0)));//When ball is created make its mass that the volume of a sphere the same size.
this.ballPosition.set(ballRadius, ballBuilder.getHeight() - ballRadius);//When ball is created make its starting coordinates the bottom left hand corner of the screen.
this.ballVelocity.set(randomVelocity(), randomVelocity());//When ball is created make its (x, y) velocity a random value between 0 and 2.
if (AddLogic.objectsToDrawIsEmpty() == true) {//If the ball being created is the first ball, make it blue, otherwise pick a random color.
this.ballColor = Color.blue;
} else {
this.ballColor = randomColor();
}
}
public void decreaseBallRadius(Ball ball){//Decrease the ball radius.
if(ball.getBallRadius() <= 15){//If the ball radius is less than or equal to 15 return control to calling object, else continue.
return;
}
ball.setBallRadius(ball.getBallRadius() - 1);//Decrease the ball radius by 1 pixel.
ball.setBallMass((float)(4 / 3 * Math.PI * Math.pow(ballRadius, 3.0)));//Recalcualte the mass based on the new radius.
}
public int getBallRadius() {
return ballRadius;
}
public float getBallMass(){
return ballMass;
}
public Color getBallColor() {
return ballColor;
}
private void setBallRadius(int newBallRadius) {
this.ballRadius = newBallRadius;
}
private void setBallMass(float newBallMass){
this.ballMass = newBallMass;
}
private float randomVelocity() {//Generate a random number between 0 and 2 for the (x, y) velocity of a ball.
float speed = (float)(Math.random() * 2);
return speed;
}
private Color randomColor() {//Generate a random color for a new ball based on the generation of a random red, green, and blue value.
int red = (int) (Math.random() * 255);
int green = (int) (Math.random() * 255);
int blue = (int) (Math.random() * 255);
ballColor = new Color(red, green, blue);
return ballColor;
}
}
BallBuilder.java
import java.awt.*;
import java.awt.image.*;
import java.util.concurrent.*;
import javax.swing.*;
public class BallBuilder extends Canvas{
private int frameHeight = 600;
private int frameWidth = 800;
private static BufferStrategy strategy;//Create a buffer strategy named strategy.
public BallBuilder(){
setIgnoreRepaint(true);//Tell OS that we will handle any repainting manually.
setBounds(0,0,frameWidth,frameHeight);
JFrame frame = new JFrame("Bouncing Balls");
JPanel panel = new JPanel();
panel.setPreferredSize(new Dimension(frameWidth, frameHeight));
panel.add(this);
frame.setContentPane(panel);
frame.pack();
frame.setResizable(false);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
addMouseListener(new MouseEventHandler());
createBufferStrategy(2);//Create a double buffer for smooth graphics.
strategy = getBufferStrategy();//Apply the double buffer to the buffer strategy named strategy.
}
public static void main(String[] args) {
BallBuilder ballBuilder = new BallBuilder(); // Creates a new ball builder.
ExecutorService executor = Executors.newSingleThreadExecutor();//Creates a thread executor that uses a single thread.
executor.execute(new AddLogic(ballBuilder, strategy));//Executes the runnable task AddLogic on the previously created thread.
}
}
MouseEventHandler.java
import java.awt.event.*;
public class MouseEventHandler extends MouseAdapter{
private static boolean mouseClicked = false;
public void mousePressed(MouseEvent e){//If either of the mouse buttons is pressed the mouse clicked variable is set to true.
mouseClicked = true;
}
public static void mouseEventUpdate(){//When called, sets the mouse clicked variable back to false.
mouseClicked = false;
}
public static boolean mouseEventCheck(){//Returns the state of the mouse clicked variable.
if(mouseClicked){
return true;
}
else{
return false;
}
}
}
Vector2D
public class Vector2D {//A class that takes care of ball position and speed vectors.
private float x;
private float y;
public Vector2D() {
this.setX(0);
this.setY(0);
}
public Vector2D(float x, float y) {
this.setX(x);
this.setY(y);
}
public void set(float x, float y) {
this.setX(x);
this.setY(y);
}
public void setX(float x) {
this.x = x;
}
public void setY(float y) {
this.y = y;
}
public float getX() {
return x;
}
public float getY() {
return y;
}
public float dot(Vector2D v2) {//Speciality method used during calculations of ball to ball collisions.
float result = 0.0f;
result = this.getX() * v2.getX() + this.getY() * v2.getY();
return result;
}
public float getLength() {
return (float) Math.sqrt(getX() * getX() + getY() * getY());
}
public Vector2D add(Vector2D v2) {
Vector2D result = new Vector2D();
result.setX(getX() + v2.getX());
result.setY(getY() + v2.getY());
return result;
}
public Vector2D subtract(Vector2D v2) {
Vector2D result = new Vector2D();
result.setX(this.getX() - v2.getX());
result.setY(this.getY() - v2.getY());
return result;
}
public Vector2D multiply(float scaleFactor) {
Vector2D result = new Vector2D();
result.setX(this.getX() * scaleFactor);
result.setY(this.getY() * scaleFactor);
return result;
}
public Vector2D normalize() {//Speciality method used during calculations of ball to ball collisions.
float length = getLength();
if (length != 0.0f) {
this.setX(this.getX() / length);
this.setY(this.getY() / length);
} else {
this.setX(0.0f);
this.setY(0.0f);
}
return this;
}
}
The speed angle changes when the ball bounces, you should change it after each bounce.
Also, as you'll see later, the speed VALUE (called "modulus") also changes when two balls collide.
EDIT:::
It seems to me that you are accelerating the balls
The
int x = (int) (ball.getBallTempX() +
(ball.getBallPathLength() * Math.cos(ball.getBallAngle())));
Seems to correspond to
int x = (int) (ball.getBallTempX() +
(ball.getBallSpeed() * Math.cos(ball.getBallAngle())));
And the same for "y"
Am I right?
Edit 2::
In fact, you don't need the TempX,TempY,PreviousCenterX and PreviousCenterY either.
Try these methods
private int xCoordinateUpdate(Ball ball) {
int x = (int) (ball.getBallCenterX()+ (ball.Speed()
* Math.cos(ball.getBallAngle())));
return x;
(the same for Y)
Edit 3 ::
One more ... :)
What you need is to code is this formula (any other will fail when you attempt to collide balls):
Pseudocode >
BallCenterX = BallCenterX + BallSpeed * Math.cos(angle)
BallCenterY = BallCenterY + BallSpeed * Math.sin(angle)
Forget any "temp" and "old" values. They will impair your ball colliding feature.
I'll start this answer and keep editing it step by step until we are done. I'll try to guide you to a "semi-vectorized" version of the program, trying to minimize the effort.
Please keep updating the code as you progress, and commenting on my suggestions.
First, a few things:
In
private double randomBallAngle(){
ballAngle = Math.toRadians((Math.random()*90));
return ballAngle;
}
You are working in radians, but in yCoordinateUpdate and yCoordinateUpdate it seems that you are using the same angle in grads (because you are comparing with 90).
Using radians is easier for all the math involved.
Also, the vars GoingUp, etc are not needed since the speed angle will take care of that.
You may set the initial random angle in the interval (0 ... 2 Pi).
You should change (in fact reflect) the speed angle after each border collission (and afterwards, when we are done, after each inter-ball caollission too).
For this reflection, the laws are:
Upper or lower wall: New Angle = - OldAngle
For Right or left wall: New Angle = Pi - OldAngle
We'll keep reflections on the vertex for later ...

How to draw circle with specific color in XNA?

XNA doesn't have any methods which support circle drawing.
Normally when I had to draw circle, always with the same color, I just made image with that circle and then I could display it as a sprite.
But now the color of the circle is specified during runtime, any ideas how to deal with that?
You can simply make an image of a circle with a Transparent background and the coloured part of the circle as White. Then, when it comes to drawing the circles in the Draw() method, select the tint as what you want it to be:
Texture2D circle = CreateCircle(100);
// Change Color.Red to the colour you want
spriteBatch.Draw(circle, new Vector2(30, 30), Color.Red);
Just for fun, here is the CreateCircle method:
public Texture2D CreateCircle(int radius)
{
int outerRadius = radius*2 + 2; // So circle doesn't go out of bounds
Texture2D texture = new Texture2D(GraphicsDevice, outerRadius, outerRadius);
Color[] data = new Color[outerRadius * outerRadius];
// Colour the entire texture transparent first.
for (int i = 0; i < data.Length; i++)
data[i] = Color.TransparentWhite;
// Work out the minimum step necessary using trigonometry + sine approximation.
double angleStep = 1f/radius;
for (double angle = 0; angle < Math.PI*2; angle += angleStep)
{
// Use the parametric definition of a circle: http://en.wikipedia.org/wiki/Circle#Cartesian_coordinates
int x = (int)Math.Round(radius + radius * Math.Cos(angle));
int y = (int)Math.Round(radius + radius * Math.Sin(angle));
data[y * outerRadius + x + 1] = Color.White;
}
texture.SetData(data);
return texture;
}

Resources