Making a 2D Drawing 3D?
in
Contributed Library Questions
•
1 year ago
Hello there,
Right now I have a 2D function involving balls that roam around the screen and then stick together in a certain order when the mouse is pressed. What I want to do is make these balls spheres that perform the same thing in 3D space. Can anybody help me with this? Here's my code:
Right now I have a 2D function involving balls that roam around the screen and then stick together in a certain order when the mouse is pressed. What I want to do is make these balls spheres that perform the same thing in 3D space. Can anybody help me with this? Here's my code:
- import processing.opengl.*;
//import libraries
import toxi.geom.*;
import peasy.*;
//library for PDF export
import processing.pdf.*;
//declase camera
PeasyCam pCam;
//declare arraylist
ArrayList AgentCollection;
// A boolean variable that when set to true triggers the PDF recording process
boolean record = false; - class Ball
{
color colour;
float xpos, ypos;
float xvel, yvel;
float radius;
float mass;
float easing = 0.05;
float closeEnough = 2;
float repelFactor = 100;
Ball(float xpos, float ypos, float radius, color colour)
{
this.xpos = xpos;
this.ypos = ypos;
this.radius = radius;
this.colour = colour;
this.mass = radius / 10;
}
void update()
{
xpos += xvel;
ypos += yvel;
}
void easeTo(float targetx, float targety)
{
float dx = targetx - xpos;
float dy = targety - ypos;
float distance = sqrt(dx*dx + dy*dy);
if(distance < closeEnough)
{
xpos = targetx - repelFactor * dx;
ypos = targety - repelFactor * dy;
}
else
{
xvel = dx * easing;
yvel = dy * easing;
}
}
void render()
{
noStroke();
fill(colour);
ellipse(xpos, ypos, radius * 2, radius * 2);
}
boolean areBallsIntersecting(Ball otherBall)
{
float dx = xpos - otherBall.xpos;
float dy = ypos - otherBall.ypos;
float distance = sqrt(dx*dx + dy*dy);
if(distance < radius + otherBall.radius)
{
return true;
}
else
{
return false;
}
}
void checkBoundaryCollision(float leftEdge, float rightEdge, float topEdge, float bottomEdge)
{
if(xpos > rightEdge - radius)
{
xpos = rightEdge - radius;
xvel *= -1;
}
if(xpos < leftEdge + radius)
{
xpos = leftEdge + radius;
xvel *= -1;
}
if(ypos > bottomEdge - radius)
{
ypos = bottomEdge - radius;
yvel *= -1;
}
if(ypos < topEdge + radius)
{
ypos = topEdge + radius;
yvel *= -1;
}
}
} - //note that the tab doesn't have to have the same name as the function
void ballArraySetup()
{
smallestBalls = new Ball[numSmallestBalls];
for (int r = 0; r < numSmallestBalls; r++)
{
smallestBalls[r] = new Ball(random(width), random(height), 10, color(25));
smallestBalls[r].xvel = random(-2, 2);
smallestBalls[r].yvel = random(-2, 2);
}
smallBalls = new Ball[numSmallBalls];
for (int b = 0; b < numSmallBalls; b++)
{
smallBalls[b] = new Ball(random(width), random(height), 15, color(50));
smallBalls[b].xvel = random(-5, 5);
smallBalls[b].yvel = random(-5, 5);
}
largeBalls = new Ball[numLargeBalls];
for (int l = 0; l < numLargeBalls; l++)
{
largeBalls[l] = new Ball(random(width), random(height), 20, color(75));
largeBalls[l].xvel = random(-5, 5);
largeBalls[l].yvel = random(-5, 5);
}
largestBalls = new Ball[numLargestBalls];
for (int g = 0; g < numLargestBalls; g++)
{
largestBalls[g] = new Ball(random(width), random(height), 25, color(100));
largestBalls[g].xvel = random(-5, 5);
largestBalls[g].yvel = random(-5, 5);
}
moreBalls = new Ball[numMoreBalls];
for (int m = 0; m < numMoreBalls; m++)
{
moreBalls[m] = new Ball(random(width), random(height), 30, color(125));
moreBalls[m].xvel = random(-5, 5);
moreBalls[m].yvel = random(-5, 5);
}
evenMoreBalls = new Ball[numEvenMoreBalls];
for (int e = 0; e < numEvenMoreBalls; e++)
{
evenMoreBalls[e] = new Ball(random(width), random(height), 35, color(150));
evenMoreBalls[e].xvel = random(-5, 5);
evenMoreBalls[e].yvel = random(-5, 5);
}
stillMoreBalls = new Ball[numStillMoreBalls];
for (int s = 0; s < numStillMoreBalls; s++)
{
stillMoreBalls[s] = new Ball(random(width), random(height), 40, color(175));
stillMoreBalls[s].xvel = random(-5, 5);
stillMoreBalls[s].yvel = random(-5, 5);
}
theRealLargestBalls = new Ball[numTheRealLargestBalls];
for (int t = 0; t < numTheRealLargestBalls; t++)
{
theRealLargestBalls[t] = new Ball(random(width), random(height), 45, color(200));
theRealLargestBalls[t].xvel = random(-5, 5);
theRealLargestBalls[t].yvel = random(-5, 5);
}
}
void doGeneralBallStuff()
{
for (int r = 0; r < numSmallestBalls; r++)
{
smallestBalls[r].render();
smallestBalls[r].update();
smallestBalls[r].checkBoundaryCollision(0, width, 0, height);
}
for (int b = 0; b < numSmallBalls; b++)
{
smallBalls[b].render();
smallBalls[b].update();
smallBalls[b].checkBoundaryCollision(0, width, 0, height);
}
for (int l = 0; l < numLargeBalls; l++)
{
largeBalls[l].render();
largeBalls[l].update();
largeBalls[l].checkBoundaryCollision(0, width, 0, height);
}
for (int g = 0; g < numLargestBalls; g++)
{
largestBalls[g].render();
largestBalls[g].update();
largestBalls[g].checkBoundaryCollision(0, width, 0, height);
}
for (int m = 0; m < numMoreBalls; m++)
{
moreBalls[m].render();
moreBalls[m].update();
moreBalls[m].checkBoundaryCollision(0, width, 0, height);
}
for (int e = 0; e < numEvenMoreBalls; e++)
{
evenMoreBalls[e].render();
evenMoreBalls[e].update();
evenMoreBalls[e].checkBoundaryCollision(0, width, 0, height);
}
for (int s = 0; s < numStillMoreBalls; s++)
{
stillMoreBalls[s].render();
stillMoreBalls[s].update();
stillMoreBalls[s].checkBoundaryCollision(0, width, 0, height);
}
for (int t = 0; t < numTheRealLargestBalls; t++)
{
theRealLargestBalls[t].render();
theRealLargestBalls[t].update();
theRealLargestBalls[t].checkBoundaryCollision(0, width, 0, height);
}
}
void checkCollision(Ball ball0, Ball ball1)
{
float dx = ball1.xpos - ball0.xpos;
float dy = ball1.ypos - ball0.ypos;
float dist = sqrt(dx*dx + dy*dy);
if(dist < ball0.radius + ball1.radius)
{
//calculate angle, sine and cosine
float angle = atan2(dy, dx);
float sine = sin(angle);
float cosine = cos(angle);
//rotate ball0's position
PVector pos0 = new PVector(0, 0);
//rotate ball1's position
PVector pos1 = rotateC(dx, dy, sine, cosine, true);
//rotate ball0's velocity
PVector vel0 = rotateC(ball0.xvel, ball0.yvel, sine, cosine, true);
//rotate ball1's velocity
PVector vel1 = rotateC(ball1.xvel, ball1.yvel, sine, cosine, true);
//collision reaction
float vxTotal = vel0.x - vel1.x;
vel0.x = ((ball0.mass - ball1.mass) * vel0.x + 2 * ball1.mass * vel1.x) / (ball0.mass + ball1.mass);
vel1.x = vxTotal + vel0.x;
//update positions
float absV = abs(vel0.x) + abs(vel1.x);
float overlap = (ball0.radius + ball1.radius) - abs(pos0.x - pos1.x);
pos0.x += vel0.x / absV * overlap;
pos1.x += vel1.x / absV * overlap;
//rotate positions back
PVector pos0F = rotateC(pos0.x, pos0.y, sine, cosine, false);
PVector pos1F = rotateC(pos1.x, pos1.y, sine, cosine, false);
//adjust positions to actual screen positions
ball1.xpos = ball0.xpos + pos1F.x;
ball1.ypos = ball0.ypos + pos1F.y;
ball0.xpos = ball0.xpos + pos0F.x;
ball0.ypos = ball0.ypos + pos0F.y;
//rotate velocties back
PVector vel0F = rotateC(vel0.x, vel0.y, sine, cosine, false);
PVector vel1F = rotateC(vel1.x, vel1.y, sine, cosine, false);
ball0.xvel = vel0F.x;
ball0.yvel = vel0F.y;
ball1.xvel = vel1F.x;
ball1.yvel = vel1F.y;
}
}
PVector rotateC(float x, float y, float sine, float cosine, boolean anticlock)
{
PVector result = new PVector(0, 0);
if(anticlock)
{
result.x = x * cosine + y * sine;
result.y = y * cosine - x * sine;
}
else
{
result.x = x * cosine - y * sine;
result.y = y * cosine + x * sine;
}
return result;
}
- //I've created a new tab ballFunctions where I've hidden all the stuff we're happy with now:
//creating and populating the arrays, and ball render(), update() and checkWalls() or whatever we called it
// I create tabs for logical groups of functions - in a recent project I had colour functions together, pdf
// functions together, xml functions together and so on
// it's also useful to move functions out of the main tab once you're happy with them
// and don't need to keep looking at them
int numSmallestBalls = 7;
Ball[] smallestBalls;
int numSmallBalls = 7;
Ball[] smallBalls;
int numLargeBalls = 7;
Ball[] largeBalls;
int numLargestBalls = 7;
Ball[] largestBalls;
int numMoreBalls = 7;
Ball[] moreBalls;
int numEvenMoreBalls = 7;
Ball[] evenMoreBalls;
int numStillMoreBalls = 7;
Ball[] stillMoreBalls;
int numTheRealLargestBalls = 7;
Ball[] theRealLargestBalls;
float easeThreshold = 100;
void setup()
{
size(900, 600, OPENGL);
smooth();
//array creation and population are now hidden away
//in a ballArraySetup() function in the new ballFunctions tab
ballArraySetup();
}
void draw()
{
//will create a PDF from current frame if p-key is pressed
if (record) {
//#### will add the actal frame number to the file name
//pdf will be saved in same folder as the processing file
beginRaw(PDF, "3D-####.pdf");
}
background(255);
//all render(), update() and checkBoundaryCollision() functions are now hidden away
//in a new function in the new ballFunctions tab
doGeneralBallStuff();
checkIntersections();
if (mousePressed) easeToNearest();
checkBallCollisions();
}
void checkBallCollisions()
{
//note the way I've done the nest here
//to check each ball against itself and against each other twice
//would be a waste of time
//if you can't "see" this, draw a grid with columns 0 - 5 say
// and rows 0 - 5, and check off which collsions this method makes
for (int t = 0; t < numSmallestBalls - 1; t++)
{
for (int s = t + 1; s < numSmallestBalls; s++)
{
checkCollision(smallestBalls[t], smallestBalls[s]);
}
}
for (int t = 0; t < numSmallBalls - 1; t++)
{
for (int s = t + 1; s < numSmallBalls; s++)
{
checkCollision(smallBalls[t], smallBalls[s]);
}
}
for (int t = 0; t < numLargeBalls - 1; t++)
{
for (int s = t + 1; s < numLargeBalls; s++)
{
checkCollision(largeBalls[t], largeBalls[s]);
}
}
for (int t = 0; t < numLargestBalls - 1; t++)
{
for (int s = t + 1; s < numLargestBalls; s++)
{
checkCollision(largestBalls[t], largestBalls[s]);
}
}
for (int t = 0; t < numMoreBalls - 1; t++)
{
for (int s = t + 1; s < numMoreBalls; s++)
{
checkCollision(moreBalls[t], moreBalls[s]);
}
}
for (int t = 0; t < numEvenMoreBalls - 1; t++)
{
for (int s = t + 1; s < numEvenMoreBalls; s++)
{
checkCollision(evenMoreBalls[t], evenMoreBalls[s]);
}
}
for (int t = 0; t < numStillMoreBalls - 1; t++)
{
for (int s = t + 1; s < numStillMoreBalls; s++)
{
checkCollision(stillMoreBalls[t], stillMoreBalls[s]);
}
}
for (int t = 0; t < numTheRealLargestBalls - 1; t++)
{
for (int s = t + 1; s < numTheRealLargestBalls; s++)
{
checkCollision(theRealLargestBalls[t], theRealLargestBalls[s]);
}
}
//to end PDF recording
if (record) {
endRaw();
record = false;
}
}
void keyPressed() {
if (key == 'p' || key == 'P') {
record = true;
}
}
void easeToNearest()
{
// I've created a threshold for easing to try to stop the balls clumping together
for (int b = 0; b < numSmallBalls; b++)
{
int closestsmallestBall = -1;
float closestDistance = (width * height) + 10;
for (int r = 0; r < numSmallestBalls; r++)
{
float dx = smallestBalls[r].xpos - smallBalls[b].xpos;
float dy = smallestBalls[r].ypos - smallBalls[b].ypos;
float distance = sqrt(dx*dx + dy*dy);
if (distance < closestDistance)
{
closestDistance = distance;
closestsmallestBall = r;
}
}
if (closestDistance < easeThreshold)
{
smallBalls[b].easeTo(smallestBalls[closestsmallestBall].xpos, smallestBalls[closestsmallestBall].ypos);
}
}
for (int b = 0; b < numLargeBalls; b++)
{
int closestsmallBall = -1;
float closestDistance = (width * height) + 10;
for (int r = 0; r < numSmallBalls; r++)
{
float dx = smallBalls[r].xpos - largeBalls[b].xpos;
float dy = smallBalls[r].ypos - largeBalls[b].ypos;
float distance = sqrt(dx*dx + dy*dy);
if (distance < closestDistance)
{
closestDistance = distance;
closestsmallBall = r;
}
}
if (closestDistance < easeThreshold)
{
largeBalls[b].easeTo(smallBalls[closestsmallBall].xpos, smallBalls[closestsmallBall].ypos);
}
}
for (int b = 0; b < numLargestBalls; b++)
{
int closestlargeBall = -1;
float closestDistance = (width * height) + 10;
for (int r = 0; r < numLargeBalls; r++)
{
float dx = largeBalls[r].xpos - largestBalls[b].xpos;
float dy = largeBalls[r].ypos - largestBalls[b].ypos;
float distance = sqrt(dx*dx + dy*dy);
if (distance < closestDistance)
{
closestDistance = distance;
closestlargeBall = r;
}
}
if (closestDistance < easeThreshold)
{
largestBalls[b].easeTo(largeBalls[closestlargeBall].xpos, largeBalls[closestlargeBall].ypos);
}
}
for (int m = 0; m < numMoreBalls; m++)
{
int closestlargestBall = -1;
float closestDistance = (width * height) + 10;
for (int r = 0; r < numLargestBalls; r++)
{
float dx = largestBalls[r].xpos - moreBalls[m].xpos;
float dy = largestBalls[r].ypos - moreBalls[m].ypos;
float distance = sqrt(dx*dx + dy*dy);
if (distance < closestDistance)
{
closestDistance = distance;
closestlargestBall = r;
}
}
if (closestDistance < easeThreshold)
{
moreBalls[m].easeTo(largestBalls[closestlargestBall].xpos, largestBalls[closestlargestBall].ypos);
}
}
for (int e = 0; e < numEvenMoreBalls; e++)
{
int closestmoreBall = -1;
float closestDistance = (width * height) + 10;
for (int m = 0; m < numMoreBalls; m++)
{
float dx = moreBalls[m].xpos - evenMoreBalls[e].xpos;
float dy = moreBalls[m].ypos - evenMoreBalls[e].ypos;
float distance = sqrt(dx*dx + dy*dy);
if (distance < closestDistance)
{
closestDistance = distance;
closestmoreBall = m;
}
}
if (closestDistance < easeThreshold)
{
evenMoreBalls[e].easeTo(moreBalls[closestmoreBall].xpos, moreBalls[closestmoreBall].ypos);
}
}
for (int s = 0; s < numStillMoreBalls; s++)
{
int closestevenMoreBall = -1;
float closestDistance = (width * height) + 10;
for (int e = 0; e < numEvenMoreBalls; e++)
{
float dx = evenMoreBalls[e].xpos - stillMoreBalls[s].xpos;
float dy = evenMoreBalls[e].ypos - stillMoreBalls[s].ypos;
float distance = sqrt(dx*dx + dy*dy);
if (distance < closestDistance)
{
closestDistance = distance;
closestevenMoreBall = e;
}
}
if (closestDistance < easeThreshold)
{
stillMoreBalls[s].easeTo(evenMoreBalls[closestevenMoreBall].xpos, evenMoreBalls[closestevenMoreBall].ypos);
}
}
for (int t = 0; t < numTheRealLargestBalls; t++)
{
int closeststillMoreBall = -1;
float closestDistance = (width * height) + 10;
for (int s = 0; s < numStillMoreBalls; s++)
{
float dx = stillMoreBalls[s].xpos - theRealLargestBalls[t].xpos;
float dy = stillMoreBalls[s].ypos - theRealLargestBalls[t].ypos;
float distance = sqrt(dx*dx + dy*dy);
if (distance < closestDistance)
{
closestDistance = distance;
closeststillMoreBall = s;
}
}
if (closestDistance < easeThreshold)
{
theRealLargestBalls[t].easeTo(stillMoreBalls[closeststillMoreBall].xpos, stillMoreBalls[closeststillMoreBall].ypos);
}
}
// for (int b = 0; b < numSmallestBalls; b++)
// {
// int closestlargestBall = -1;
// float closestDistance = (width * height) + 10;
//
// for (int r = 0; r < numLargestBalls; r++)
// {
// float dx = largestBalls[r].xpos - smallestBalls[b].xpos;
// float dy = largestBalls[r].ypos - smallestBalls[b].ypos;
// float distance = sqrt(dx*dx + dy*dy);
// if (distance < closestDistance)
// {
// closestDistance = distance;
// closestlargestBall = r;
// }
// }
// if(closestDistance < easeThreshold)
// {
// smallestBalls[b].easeTo(largestBalls[closestlargestBall].xpos, largestBalls[closestlargestBall].ypos);
// }
// }
}
void checkIntersections()
{
// for (int r = 0; r < numLargestBalls; r++)
// {
// for (int b = 0; b < numSmallestBalls; b++)
// {
// largestBalls[r].areBallsIntersecting(smallestBalls[b]);
// }
// }
for (int r = 0; r < numSmallestBalls; r++)
{
for (int b = 0; b < numSmallBalls; b++)
{
smallestBalls[r].areBallsIntersecting(smallBalls[b]);
}
}
for (int b = 0; b < numSmallBalls; b++)
{
for (int l = 0; l < numLargeBalls; l++)
{
smallBalls[b].areBallsIntersecting(largeBalls[l]);
}
}
for (int l = 0; l < numLargeBalls; l++)
{
for (int g = 0; g < numLargestBalls; g++)
{
largeBalls[l].areBallsIntersecting(largestBalls[g]);
}
}
for (int g = 0; g < numLargestBalls; g++)
{
for (int m = 0; m < numMoreBalls; m++)
{
largestBalls[g].areBallsIntersecting(moreBalls[m]);
}
}
for (int m = 0; m < numMoreBalls; m++)
{
for (int e = 0; e < numEvenMoreBalls; e++)
{
moreBalls[m].areBallsIntersecting(evenMoreBalls[e]);
}
}
for (int e = 0; e < numEvenMoreBalls; e++)
{
for (int s = 0; s < numStillMoreBalls; s++)
{
evenMoreBalls[e].areBallsIntersecting(stillMoreBalls[s]);
}
}
for (int s = 0; s < numStillMoreBalls; s++)
{
for (int t = 0; t < numTheRealLargestBalls; t++)
{
stillMoreBalls[s].areBallsIntersecting(theRealLargestBalls[t]);
}
}
}
1