Processing detecct whether mouse is inside a bouncing circle when clicked - processing

Trying to determine whether the mouse has been clicked within a certain ball and when it is clicked I want to display text that relates to that specific ball, this is what I have so far any help would be appreciated.
int rad = 60; // Width of the shape
float xpos1, ypos1; // Starting position of shape
float xspeed = 4; // Speed of the shape
float yspeed = 4; // Speed of the shape
int xdirection = 1; // Left or Right
int ydirection = 1; // Top to Bottom
boolean overBox1 = false;
void setup()
{
size(1200, 800);
noStroke();
frameRate(30);
ellipseMode(RADIUS);
// Set the starting position of the shape
xpos1 = width/2;
ypos1 = height/2;
}
void draw()
{
background(102);
circleone();
}
void circleone()
{
xpos1 = xpos1 + ( xspeed * xdirection );
ypos1 = ypos1 + ( yspeed * ydirection );
if (xpos1 > width-rad || xpos1 < rad) {
xdirection *= -1;
}
if (ypos1 > height-rad || ypos1 < rad) {
ydirection *= -1;
}
ellipse(xpos1, ypos1, rad, rad);
}
void mouseClick()
{
if (overCircle(xpos1,ypos1,rad)==true)
{
println("YO");
}
}
boolean overCircle(float x, float y, int radius) {
float disX = x - mouseX;
float disY = y - mouseY;
if (sqrt(sq(disX) + sq(disY)) <radius) {
return true;
} else {
return false;
}
}

This logic seems overcomplicated:
float disX = x - mouseX;
float disY = y - mouseY;
if (sqrt(sq(disX) + sq(disY)) <radius)
You could just use the dist() function instead:
return dist(x, y, mouseX, mouseY) < radius;
Then if you want to display some text when the ball is clicked, you're going to have to add more than a println() statement. You might set a boolean variable to true, and then in the draw() function you'd check that variable and draw something to the screen when it's true.
If you're trying to get many clickable balls on the screen, then you might think about creating classes that represent a ball and putting your logic in there.
But you haven't really asked a question. You've posted some code, haven't told us what you expect it to do, what it does instead, or how those two things are different. Stack Overflow isn't really designed for general "how do I do this" type questions. It's for more specific "I tried X, expected Y, but got Z instead" type questions. So you'll have much better luck if you post a MCVE instead of your entire project and ask a specific question about a specific line of code. Good luck.

Related

Processing - Multiple Image-Buttons That will Shape a Circle Together

I have several buttons that I created like this:
In the setup()
PImage[] imgs1 = {loadImage("AREA1_1.png"),loadImage("AREA1_2.png"),loadImage("AREA1_3.png")};
cp5.addButton("AREA_1") // The button
.setValue(128)
.setPosition(-60,7) // x and y relative to the group
.setImages(imgs1)
.updateSize()
.moveTo(AreaRingGroup); // add it to the group
;
After draw()
void AREA_1(){
println("AREA_1");
if (port != null){
port.write("a\n");
}
Now, I have to add about 24 small buttons from images that are going to be seen as 1 circle. But instead of adding them using cp5.addButton like above, I thought I should create a class.
I want to extend the existing CP5 button class because I want to be able to reach the cp5 parameters; like using .setImages() for 3 mouseClick conditions in the button images.
My problem is, I couldn't figure out
How to extend the CP5 button class, so that I can use angles (since I want to place buttons in a circular way) instead of giving x,y positions.
How to use serial port writings using this class, like the above void (AREA_1) example.
Here is my silly attempt:
class myButton extends Button
{
myButton(ControlP5 cp5, String theName)
{
super(cp5, cp5.getTab("default"), theName, 0,0,0, autoWidth, autoHeight);
}
PVector pos = new PVector (0,0);
PVector center = new PVector(500,500);
PImage[] img = new PImage[3];
String lable;
int sizeX = 10, sizeY=10;
color imgColor;
Boolean pressed = false;
Boolean clicked = false;
//Constructor
myButton(float a, String theName)
{
//Angle between the center I determined and the position of the
button.
a = degrees (PVector.angleBetween(center, pos));
//Every button will have 3 images for the 3 mouseClick conditions.
for (int i = 0; i < 2; ++i)
(img[i] = loadImage(lable + i + ".png")).resize(sizeX, sizeY);
}
}
Laancelot's idea isn't bad, however, due to controlP5's OOP architecture it might not be possible, or at least trivial, to extend the controlP5's Button class to achieve your goal from a controlP5 sketch.
I'm making the assumption this is connected to other LED ring related questions you posted around this time (such as this one).
It might be wrong (and I'd be interested in seeing a solution), but without forking/modifying the controlP5 library itself, the inheritance chain gets a bit tricky.
Ideally you'd just need to extend Button and be done with it, however the method used to tell states (if a button over/pressed/etc.) is the Controller superclass's inside() method. To work with your image you'd need override this method to and swap the logic so it takes into account the alpha transparency of your png button skin (e.g. if the pixel under the cursor is opaque then it's inside otherwise it's not)
Here's a rough illustration you were constrained to using a Processing sketch:
public abstract class CustomController< T > extends Controller< T> {
public CustomController( ControlP5 theControlP5 , String theName ) {
super( theControlP5 , theControlP5.getDefaultTab( ) , theName , 0 , 0 , autoWidth , autoHeight );
theControlP5.register( theControlP5.papplet , theName , this );
}
protected CustomController( final ControlP5 theControlP5 , final ControllerGroup< ? > theParent , final String theName , final float theX , final float theY , final int theWidth , final int theHeight ) {
super(theControlP5, theParent, theName, theX, theY, theWidth, theHeight);
}
boolean inside( ) {
/* constrain the bounds of the controller to the dimensions of the cp5 area, required since PGraphics as render
* area has been introduced. */
//float x0 = PApplet.max( 0 , x( position ) + x( _myParent.getAbsolutePosition( ) ) );
//float x1 = PApplet.min( cp5.pgw , x( position ) + x( _myParent.getAbsolutePosition( ) ) + getWidth( ) );
//float y0 = PApplet.max( 0 , y( position ) + y( _myParent.getAbsolutePosition( ) ) );
//float y1 = PApplet.min( cp5.pgh , y( position ) + y( _myParent.getAbsolutePosition( ) ) + getHeight( ) );
//return ( _myControlWindow.mouseX > x0 && _myControlWindow.mouseX < x1 && _myControlWindow.mouseY > y0 && _myControlWindow.mouseY < y1 );
// FIXME: add alpha pixel logic here
return true;
}
}
public class CustomButton<Button> extends CustomController< Button > {
protected CustomButton( ControlP5 theControlP5 , ControllerGroup< ? > theParent , String theName , float theDefaultValue , int theX , int theY , int theWidth , int theHeight ) {
super( theControlP5 , theParent , theName , theX , theY , theWidth , theHeight );
_myValue = theDefaultValue;
_myCaptionLabel.align( CENTER , CENTER );
}
// isn't this fun ?
//public CustomButton setImage( PImage theImage ) {
// return super.setImage( theImage , DEFAULT );
//}
}
I would suggest a simpler workaround: simply add smaller buttons in a ring layout (using polar to cartesian coordinate conversion):
import controlP5.*;
ControlP5 cp5;
void setup(){
size(600, 600);
background(0);
cp5 = new ControlP5(this);
int numLEDs = 24;
float radius = 240;
for(int i = 0; i < numLEDs; i++){
float angle = (TWO_PI / numLEDs * i) - HALF_PI;
cp5.addButton(nf(i, 2))
.setPosition(width * 0.5 + cos(angle) * radius, height * 0.5 + sin(angle) * radius)
.setSize(30, 30);
}
}
void draw(){}
Sure, not as pretty, but much much simpler.
Hopefully rendering the ring image drawn behind as a background and changing the button colour might be enough to get the point across.
It might be simpler to skip ControlP5 altogether and make your own button class for such a custom behaviour.
Let's say you want to have a square button in a ring layout that also rotates along the ring. This rotation will make checking the bounds tricker, but not impossible. You could hold track of the button's 2D transformation matrix using PMatrix2D) to render the button, but use the inverse of this transformation matrix to convert between Processing's global coordinate system to the button's local coordinate system. This would make checking if the button hovered trivial again (as the mouse converted to local coordinates would be within the button's bounding box). Here's an example:
TButton btn;
void setup(){
size(600, 600);
btn = new TButton(0, 300, 300, 90, 90, radians(45));
}
void draw(){
background(0);
btn.update(mouseX, mouseY, mousePressed);
btn.draw();
}
class TButton{
PMatrix2D transform = new PMatrix2D();
PMatrix2D inverseTransform;
int index;
int x, y, w, h;
float angle;
boolean isOver;
boolean isPressed;
PVector cursorGlobal = new PVector();
PVector cursorLocal = new PVector();
color fillOver = color(192);
color fillOut = color(255);
color fillOn = color(127);
TButton(int index, int x, int y, int w, int h, float angle){
this.index = index;
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.angle = angle;
transform.translate(x, y);
transform.rotate(angle);
inverseTransform = transform.get();
inverseTransform.invert();
}
void update(int mx, int my, boolean mPressed){
cursorGlobal.set(mx, my);
inverseTransform.mult(cursorGlobal, cursorLocal);
isOver = ((cursorLocal.x >= 0 && cursorLocal.x <= w) &&
(cursorLocal.y >= 0 && cursorLocal.y <= h));
isPressed = mPressed && isOver;
}
void draw(){
pushMatrix();
applyMatrix(transform);
pushStyle();
if(isOver) fill(fillOver);
if(isPressed) fill(fillOn);
rect(0, 0, w, h);
popStyle();
popMatrix();
}
}
If you can draw one button, you can draw many buttons, in a ring layout:
int numLEDs = 24;
TButton[] ring = new TButton[numLEDs];
TButton selectedLED;
void setup(){
size(600, 600);
float radius = 240;
float ledSize= 30;
float offsetX = width * 0.5;
float offsetY = height * 0.5;
for(int i = 0 ; i < numLEDs; i++){
float angle = (TWO_PI / numLEDs * i) - HALF_PI;
float x = offsetX + (cos(angle) * radius);
float y = offsetY + (sin(angle) * radius);
ring[i] = new TButton(i, x, y, ledSize, ledSize, angle);
}
println("click to select");
println("click and drag to change colour");
}
void draw(){
background(0);
for(int i = 0 ; i < numLEDs; i++){
ring[i].update(mouseX, mouseY);
ring[i].draw();
}
}
void onButtonClicked(TButton button){
selectedLED = button;
println("selected", selectedLED);
}
void mouseReleased(){
for(int i = 0 ; i < numLEDs; i++){
ring[i].mouseReleased();
}
}
void mouseDragged(){
if(selectedLED != null){
float r = map(mouseX, 0, width, 0, 255);
float g = map(mouseY, 0, height, 0, 255);
float b = 255 - r;
selectedLED.fillColor = color(r, g, b);
}
}
class TButton{
PMatrix2D transform = new PMatrix2D();
PMatrix2D inverseTransform;
int index;
float x, y, w, h;
float angle;
boolean isOver;
PVector cursorGlobal = new PVector();
PVector cursorLocal = new PVector();
color fillColor = color(255);
TButton(int index, float x, float y, float w, float h, float angle){
this.index = index;
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.angle = angle;
transform.translate(x, y);
transform.rotate(angle);
inverseTransform = transform.get();
inverseTransform.invert();
}
void update(int mx, int my){
cursorGlobal.set(mx, my);
inverseTransform.mult(cursorGlobal, cursorLocal);
isOver = ((cursorLocal.x >= 0 && cursorLocal.x <= w) &&
(cursorLocal.y >= 0 && cursorLocal.y <= h));
}
void mouseReleased(){
if(isOver) onButtonClicked(this);
}
void draw(){
pushMatrix();
applyMatrix(transform);
pushStyle();
if(isOver) {
fill(red(fillColor) * .5, green(fillColor) * .5, blue(fillColor) * .5);
}else{
fill(fillColor);
}
rect(0, 0, w, h);
popStyle();
popMatrix();
}
String toString(){
return "[TButton index=" + index + "]";
}
}
This isn't bad, but it easily gets complicated, right ?
What is you apply a global transformation in draw(): that may need to be handled by the button ? What if the button is nested in a bunch of pushMatrix()/popMatrix() calls when .draw() is called ? etc...
Could this be simpler ? Sure, the LEDs appear as rotated squares on a ring layout, however the important part that lights up is circular in shape. A rotated circle looks the same regardless of the angle. Checking if the mouse is over is trivial, as you can see in the Processing RollOver example : simply check if the distance between a circle's position and the cursor is smaller than the circle's radius.
Here's an example using circular buttons (removing the need for handling coordinate space transformations):
int numLEDs = 24;
CButton[] ring = new CButton[numLEDs];
CButton selectedLED;
void setup(){
size(600, 600);
float radius = 240;
float ledSize= 30;
float offsetX = width * 0.5;
float offsetY = height * 0.5;
for(int i = 0 ; i < numLEDs; i++){
float angle = (TWO_PI / numLEDs * i) - HALF_PI;
float x = offsetX + (cos(angle) * radius);
float y = offsetY + (sin(angle) * radius);
ring[i] = new CButton(i, x, y, ledSize);
}
println("click to select");
println("click and drag to change colour");
}
void draw(){
background(0);
for(int i = 0 ; i < numLEDs; i++){
ring[i].update(mouseX, mouseY);
ring[i].draw();
}
}
void onButtonClicked(CButton button){
selectedLED = button;
println("selected", selectedLED);
}
void mouseReleased(){
for(int i = 0 ; i < numLEDs; i++){
ring[i].mouseReleased();
}
}
void mouseDragged(){
if(selectedLED != null){
float r = map(mouseX, 0, width, 0, 255);
float g = map(mouseY, 0, height, 0, 255);
float b = 255 - r;
selectedLED.fillColor = color(r, g, b);
}
}
class CButton{
int index;
float x, y, radius, diameter;
boolean isOver;
color fillColor = color(255);
CButton(int index, float x, float y, float radius){
this.index = index;
this.x = x;
this.y = y;
this.radius= radius;
diameter = radius * 2;
}
void update(int mx, int my){
isOver = dist(x, y, mx, my) < radius;
}
void mouseReleased(){
if(isOver) onButtonClicked(this);
}
void draw(){
pushMatrix();
pushStyle();
if(isOver) {
fill(red(fillColor) * .5, green(fillColor) * .5, blue(fillColor) * .5);
}else{
fill(fillColor);
}
ellipse(x, y, diameter, diameter);
popStyle();
popMatrix();
}
String toString(){
return "[CButton index=" + index + "]";
}
}
Using these circular buttons on top of your NeoPixel ring image and perhaps with a bit of glow might look pretty good and simpler to achieve in a Processing sketch than taking the ControlP5 route. Don't get me wrong, it's a great library, but for custom behaviours it sometimes makes more sense to use your own custom code.
One other aspect to think is the overall interaction.
If this interface would be used to set the colors of an LED ring once, then it might ok, although it would take numLEDs * 3 interactions to set all the LEDs with a custom colour. If this was for a custom frame by frame LED animation tool then the interaction would get exhausting after a few frames.

Trying to make a ball bounce on the screen in processing using classes

I am working on making a single ball bounce all over the screen but I have an issue with making the ball bounce the bounce() function is responsible for making the ball bounce but I run into an array out of bounds exception despite only going through 1 element in the array. I have provided the code below. This code utilizes 2 classes (Ball_Usage and Ball).
Ball_Usage code:
Ball ball1;
void setup(){
size(500,500);
ball1 = new Ball();
}
void draw(){
background(255);
ball1.bounce();
}
Ball:
class Ball{
float [] xPos = {};
float [] yPos = {};
float [] dia = {};
float [] xSpeed = {};
float [] ySpeed = {};
Ball(){
}
Ball(float x, float y, float argDia){
xPos = append(xPos, x);
yPos = append(yPos, y);
dia = append(dia, argDia);
}
void bounce(){
for(int i=0; i<1; i++){
ellipse(xPos[i], yPos[i], 50, 50);
xPos[i] += xSpeed[i];
yPos[i] += ySpeed[i];
if(xPos[i]<0 || xPos[i]>=width){
xSpeed[i] = -xSpeed[i];
}
if(yPos[i]<0 || yPos[i]>=height){
ySpeed[i] = -ySpeed[i];
}
}
}
}
I believe the confusion comes from the fact that your class has two constructors:
an empty constructor (that takes no arguments): Ball()
a constructor with position and argDia (guessing diameter ?) arguments: Ball(float x, float y, float argDia)
In setup() you call the empty constructor:
ball1 = new Ball();
This means the five float arrays still have a length of 0, hence the out of bounds exception.
Even if you call the position + diameter version of the constructor, the xSpeed, ySpeed arrays will still have length 0.
You could fix this by initialising the two two arrays as well as using this version of the constructor:
Ball ball1;
void setup() {
size(500, 500);
//ball1 = new Ball();
ball1 = new Ball(250, 250, 50);
}
void draw() {
background(255);
ball1.bounce();
}
class Ball {
float [] xPos = {};
float [] yPos = {};
float [] dia = {};
float [] xSpeed = {};
float [] ySpeed = {};
Ball() {
}
Ball(float x, float y, float argDia) {
xPos = append(xPos, x);
yPos = append(yPos, y);
dia = append(dia, argDia);
xSpeed = append(xSpeed, random(-1, 1));
ySpeed = append(ySpeed, random(-1, 1));
}
void bounce() {
for (int i=0; i<1; i++) {
ellipse(xPos[i], yPos[i], 50, 50);
xPos[i] += xSpeed[i];
yPos[i] += ySpeed[i];
if (xPos[i]<0 || xPos[i]>=width) {
xSpeed[i] = -xSpeed[i];
}
if (yPos[i]<0 || yPos[i]>=height) {
ySpeed[i] = -ySpeed[i];
}
}
}
}
This will work, however there is still a bit of confusion: why use the arrays at all if you're only looping once for the first element ? It makes the arrays and for loop mostly redundant.
You might choose to keep if you plan to change the diameter over time (which in your code is hardcoded to 50), maybe positions and velocities and render a changing history of a ball.
If you don't, you could simply use float properties instead of arrays:
Ball ball1;
void setup() {
size(500, 500);
ball1 = new Ball();
}
void draw() {
background(255);
ball1.bounce();
}
class Ball {
float xPos;
float yPos;
float diameter = 50;
float xSpeed;
float ySpeed;
Ball() {
xPos = width / 2;
yPos = height / 2;
xSpeed = random(-1, 1);
ySpeed = random(-1, 1);
}
void bounce() {
ellipse(xPos, yPos, diameter, diameter);
xPos += xSpeed;
yPos += ySpeed;
if (xPos < 0 || xPos >= width) {
xSpeed = -xSpeed;
}
if (yPos < 0 || yPos >= height) {
ySpeed = -ySpeed;
}
}
}
This looks more similar to the Bounce Processing Example.
You can at a later stage make an array of Ball objects.
Additionally it's worth formatting code as it saves you time reading/scrolling through it and visually it's easier to scan the structure of the program (how each part fits in) and therefore makes it easier to debug/run mentally. It takes no effort as you can simply press Ctrl+T on Windows/Linux or CMD+T on OSX. On the long run this will pay off, especially as programs get longer and more complex as you spend more time reading code than writing code. It's a good habit to pickup early while learning to code. Have fun!

Processing weird issue with float

I've been trying to figure this out for a few hours now to no avail. It is quite simple code, a bouncing ball (particle). Initializing the velocity of the particle to (0, 0) will keep it bouncing up and down. Changing the initialized velocity of the particle to (0, 0.01) or any decimal float will cause the ball to decrease
Particle p;
void setup() {
size(500, 600);
background(0);
p = new Particle(width / 2, height / 2);
}
void draw() {
background(0, 10);
p.applyForce(new PVector(0.0, 1.0)); // gravity
p.update();
p.checkBoundaries();
p.display();
}
class Particle {
PVector pos, vel, acc;
int dia;
Particle(float x, float y) {
pos = new PVector(x, y);
vel = new PVector(0.0, 0.0);
acc = new PVector(0.0, 0.0);
dia = 30;
}
void applyForce(PVector force) {
acc.add(force);
}
void update() {
vel.add(acc);
pos.add(vel);
acc.mult(0);
}
void display() {
ellipse(pos.x, pos.y, dia, dia);
}
void checkBoundaries() {
if (pos.x > width) {
pos.x = width;
vel.x *= -1;
} else if (pos.x < 0) {
vel.x *= -1;
pos.x = 0;
}
if (pos.y > height ) {
vel.y *= -1;
pos.y = height;
}
}
}
I'm not an expert in processing vectors, but I believe I've figured out why this is occurring. If you try to recreate this issue with different values of the y part of the velocity vector, you find that it only happens when the values are not multiples of .5. Based on that, this line is probably responsible:
if (pos.y > height ) {
vel.y *= -1;
pos.y = height;
}
This line rounds the height of the ball, and reverses the velocity of it. This works fine when the ball hits 0 exactly and gets extra velocity before it comes back up, but when the ball goes slightly lower than it should, the velocity that comes from an extra iteration does not get added. As it happens, multiples of .5 hit exactly 0, but other values don't. My proof is that when you change the offending code to the following, every value causes the ball to fall to the ground eventually:
if (pos.y >= height ) {
vel.y *= -1;
pos.y = height;
}
In short, rounding and not making the ball bounce when it hits 0 causes this issue. I hope this answered your question.

Processing: How can I make multiple elements in a for() loop move to one location then return?

I have a grid of ellipses generated by two for() loops. What I'd like to do is have all of these ellipses ease into mouseX and mouseY when mousePressed == true, otherwise return to their position in the grid. How can I go about this? I've started with this template, which doesn't work as I can't figure out how affect the position of the ellipses, but the easing is set up.
float x;
float y;
float easeIn = 0.01;
float easeOut = 0.08;
float targetX;
float targetY;
void setup() {
size(700, 700);
pixelDensity(2);
background(255);
noStroke();
}
void draw() {
fill(255, 255, 255, 80);
rect(0, 0, width, height);
for (int i = 50; i < width-50; i += 30) {
for (int j = 50; j < height-50; j += 30) {
fill(0, 0, 0);
ellipse(i, j, 5, 5);
if (mousePressed == true) {
// go to mouseX
targetX = mouseX;
// ease in
float dx = targetX - x;
if (abs(dx) > 1) {
x += dx * easeIn;
}
//go to mouseY
targetY = mouseY;
// ease in
float dy = targetY - y;
if (abs(dy) > 1) {
y += dy * easeIn;
}
} else {
// return to grid
targetX = i;
// ease out
float dx = targetX - x;
if (abs(dx) > 1) {
x += dx * easeOut;
}
// return to grid
targetY = j;
// ease out
float dy = targetY - y;
if (abs(dy) > 1) {
y += dy * easeOut;
}
}
}
}
}
Any help would be greatly appreciated. I'm not sure in which order to do things/which elements should be contained in the loop.
Thanks!
You're going to have to keep track of a few things for each dot: its "home" position, its current position,its speed, etc.
The easiest way to do this would be to create a class that encapsulates all of that information for a single dot. Then you'd just need an ArrayList of instances of the class, and iterate over them to update or draw them.
Here's an example:
ArrayList<Dot> dots = new ArrayList<Dot>();
void setup() {
size(700, 700);
background(255);
noStroke();
//create your Dots
for (int i = 50; i < width-50; i += 30) {
for (int j = 50; j < height-50; j += 30) {
dots.add(new Dot(i, j));
}
}
}
void draw() {
background(255);
//iterate over your Dots and move and draw each
for (Dot dot : dots) {
dot.stepAndRender();
}
}
class Dot {
//the point to return to when the mouse is not pressed
float homeX;
float homeY;
//current position
float currentX;
float currentY;
public Dot(float homeX, float homeY) {
this.homeX = homeX;
this.homeY = homeY;
currentX = homeX;
currentY = homeY;
}
void stepAndRender() {
if (mousePressed) {
//use a weighted average to chase the mouse
//you could use whatever logic you want here
currentX = (mouseX+currentX*99)/100;
currentY = (mouseY+currentY*99)/100;
} else {
//use a weighted average to return home
//you could use whatever logic you want here
currentX = (homeX+currentX*99)/100;
currentY = (homeY+currentY*99)/100;
}
//draw the ellipse
fill(0, 0, 0);
ellipse(currentX, currentY, 5, 5);
}
}
Note that I'm just using a weighted average to determine the position of each ellipse, but you could change that to whatever you want. You could give each ellipse a different speed, or use your easing logic, whatever. But the idea is the same: encapsulate everything you need into a class, and then put the logic for one dot into that class.
I'd recommend getting this working for a single dot first, and then moving up to getting it working with multiple dots. Then if you have another question you can post the code for just a single dot instead of a bunch. Good luck.

How to change speed of ellipses in processing

I am trying to create an array of circles which all move at different speeds and have different colours using classes which i have done however about 5 seconds into running it for some reason the ellipses all go to the edges of the window and the code doesn't work properly. Below I have provided my code including the class and setup:
circle circles = new circle(1, 8);
void setup() {
size(800, 600);
}
void draw() {
background(255);
circles.display();
circles.bounce();
}
class circle {
int[] posX = new int [10];
int[] posY = new int [10];
float[] speedX = new float[10];
float[] speedY = new float[10];
int[] red = new int [10];
int[] green = new int [10];
int[] blue = new int [10];
circle(float start, float end) {
for (int i = 0; i < 10; i++) {
speedX[i] = random(start, end);
speedY[i] = random(start, end);
posX[i] = int(random(500, 800));
posY[i] = int(random(500, 600));
red[i] = int(random(0, 255));
green[i] = int(random(0, 255));
blue[i] = int(random(0, 255));
}
}
void display() {
for (int i = 0; i < 10; i++) {
fill(red[i], green[i], blue[i]);
ellipse(posX[i], posY[i], 50, 50);
}
}
void bounce() {
for (int i = 0; i < 10; i++) {
posX[i] += speedX[i];
posY[i] += speedY[i];
if (posX[i] - 50 < 0 || posX[i] + 50 > width) {
speedX[i] = -speedX[i];
}
if (posY[i] - 50 < 0 || posY[i] + 50 > height) {
speedY[i] = -speedY[i];
}
}
}
}
The main problem is that you were not setting the balls to be inside the screen again! This is a very common problem, and Dan Shiffman used it to show the new Processing 3 Debugger.
But since i've found many stuff about your code i will also comment other stuff that might help you with programming.
But first let me show you the working code:
int n_of_circles = 10;
Circle[] circles = new Circle[n_of_circles];
void setup() {
size(800, 600);
for (int i = 0; i < n_of_circles; i++) {
circles[i] = new Circle(1, 8);
}
}
void draw() {
background(255);
for (int i = 0; i < n_of_circles; i++) {
circles[i].display();
circles[i].move();
}
}
class Circle {
float posX = random(0, width); //no need to set these value in constructor
float posY = random(0, height); //we can set it here
float speedX = 666; //we will change these on constructor
float speedY = 666; //666 is just a personal convention that i use to indicate that :p
//Similarly, no need to set the color variable in the constructor
//also, lets use a color variable instead of 3 separate variables
color circ_color = color(
(int) random(0, 255),
(int) random(0, 255),
(int) random(0, 255)
);
//defining circle diameter instead of using a literal ("50", in our case)
int diameter = 50;
int radius = diameter/2;
Circle(float min_speed, float max_speed) {
// The purpose of "min_speed" and "max_speed" are clearer than "start" and "end"
speedX = random(min_speed, max_speed);
speedY = random(min_speed, max_speed);
}
void display() {
//this push an pop commands ensure that this fill command won't
//interfere with any other stuff, if you ever reuse this code
pushStyle();
fill( circ_color );
ellipse(posX, posY, diameter, diameter);
popStyle();
}
//notice how i separated the move declartions form the bounce. It's good programming practice
//to keep stuff simpler. Functions should always have one single responsibility.
//In this case it won't helpĀ“much, but we're yoru move function more complex, it would!
void move(){
posX += speedX;
posY += speedY;
bounce();
}
void bounce() {
//note that you were using the diameter, but you should really use the radius!
//if it's touching left or side edges
if (posX - radius < 0 || posX + radius > width) {
speedX *= -1; //inverts the sign
//Here's the actual missing bits!
if (posX - radius < 0 ){ posX = radius; }
if (posX + radius > width ){ posX = width-radius; }
}
//if it's touching top or bottom edges
if (posY - radius < 0 || posY + radius > height) {
speedY *= -1; //inverts the sign
//Here's the actual missing bits!
if (posY - radius < 0 ){ posY = radius; }
if (posY + radius > height ){ posY = height-radius; }
}
}
} //end of class Circle
Just i few suggestions:
The convention is that Class names are upper case. This you you have Circle circles = new Circle(1,5); This is clearer.
Avoid using literals! (writing hard-coded numbers) For example, you we're using "ellipse(posX[i], posY[i], 50, 50);". By using a variable for diameter your code becomes more modular. Should you change the circles diameter, you just alter one line of code!
Use arrays of objects instead of classes that contains other stuff. That's good programming practice as it makes your code easier to understand. Now if you need to group lots of circles you use an array, or you can create another class "Group_of_circles" for ex.
I know i've presented some new concepts/syntaxes and changed a lot to your code, but i tried to comment everything to make it clear and instructive
Since i've already changed much thing i didn't use any Vectors, but when dealing with positions , you really should use PVectors! This free book have a great chapter on them!
Try to find good tutorials about software development. They will teach about godo programming practices like keeping your code as modular as possible and etc. But don't worry, it will come to you natrually over time!
Try learncpp.com, tutorialss 1-10a and 1-4b. Those 2 are part of a c++ tutorial, but are a good starting point and refer to programming in general.

Resources