How to not overwrite existing list of objects? - processing

I want to draw lines as I click on the canvas, so if I clicked once then save the point, and if I clicked the second time then draw a line behind these two points. But, I want to make this multiple times, so, If I clicked the third time then this point will be the starting point of a new line.
I created like this:
This is in the main:
ArrayList<Shape> shapes = new ArrayList<Shape>();
Shape selected_shape = null;
Boolean drawmode = true;
void setup() {
size(1000, 600);
}
void draw() {
//update();
background(224, 224, 224);
//draw the existing
for(Shape s: shapes){
pushMatrix();
//list all
s.Draw();
s.Log();
popMatrix();
}
println("shape size: "+shapes.size());
}
//menu
int value = 0;
void keyPressed() {
if(key == '0'){
System.out.println("Draw mode OFF"); // exit from draw mode
value = 0;
}
if(key == '1'){
println("Draw a line: select the start point of the line and the end point!"); // line
value = 1;
}
//System.out.println("key: " + key);
}
Line l = new Line();
void mousePressed() {
if(value == 1){
if(l.centerIsSet){
if (mouseButton == LEFT) {
l.x2 = mouseX;
l.y2 = mouseY;
println("end point added");
l.centerIsSet = false;
}
shapes.add(l);
l.Log();
} else {
if (mouseButton == LEFT) {
l.x1 = mouseX;
l.y1 = mouseY;
l.centerIsSet = true;
println("start point added");
}
}
}
}
And I use a shape class and this class is extended with line:
abstract class Shape {
PVector position = new PVector();
PVector fill_color = new PVector(0, 0, 0);
PVector stroke_color = new PVector(0, 0, 0);
PVector select_fill_color = new PVector(255, 0, 0);
PVector select_stroke_color = new PVector(255, 0, 0);
Boolean selected = false;
int shape_color_r;
int shape_color_g;
int shape_color_b;
int shape_rotation_angle = 0;
int detailness = 10;
abstract void Draw();
abstract void Log();
}
and:
class Line extends Shape {
int x1, x2, y1, y2;
Boolean centerIsSet = false;
Line(){}
Line(int x1, int y1){
this.x1 = x1;
this.y1 = y1;
}
Line(int x1, int y1, int x2, int y2){
this.x1 = x1;
this.x2 = x2;
this.y1 = y1;
this.y2 = y2;
}
void Draw(){
line(x1, y1, x2, y2);
}
void Log(){
System.out.println("x1: "+x1+" x2: "+x2+" y1: "+y1+" y2: "+y2);
}
}
But always the last created line overwrite the old ones, how can I resolve this? I think I need a new instance for each line, but I don't know how can I do this.

The variable l refers to the Line object which holds the coordinates of the line which is currently drawn.
If you have finished a line, then the reference to line object l is add to the container shapes. Now you have to create a new line object, for the next line and assign it to l:
shapes.add(l);
l = new Line();

Related

Processing Fix & Optimization of https://github.com/jonlit/spacestarprocessing3d

I previously had an issue with a model not loading correctly (see Processing - loading obj File)
https://stackoverflow.com/users/89766/george-profenza helped me solve the problem in chat, and he wanted to post his optimizations to my code publically.
This also solved the original problem described in the question mentioned above.
You can check out the game at https://github.com/jonlit/spacestarprocessing3d
As mentioned in chat there were a few things slightly off with the existing approach and for visiblity, this are the steps taken to address the issues.
Hope this helps other to debug Processing P3D / OBJ issues:
The first step was to identify the slowest pieces of code. This was done using VisualVM.
This highlighted shape() calls were slow (not not why):
Step 2 was to isolate the problem. Why is loading/displaying a couple of obj files slow.
For reference these are the assets:
rock.obj using rockTexture.png (but currently missing .mtl)
cirno_low.obj using cirno_low_u1_v1.jpeg
This is a test sketch loading/display the .obj files as they are:
PShape rock;
PShape cirno;
void setup(){
size(900, 900, P3D);
cirno = loadShape("cirno_low.obj");
rock = loadShape("rock.obj");
int faces = 0;
int vertices = 0;
for(int i = 0 ; i < rock.getChildCount(); i++){
PShape c = rock.getChild(i);
vertices += c.getVertexCount();
faces++;
}
println("rock faces", faces, "vertices", vertices);
}
void draw(){
background(0);
lights();
translate(width * 0.5, height * 0.5, 0);
rotateY(map(mouseX, 0, width, -PI, PI));
rotateX(map(mouseY, 0, height, PI, -PI));
for(int i = 0 ; i < 81; i++){
pushMatrix();
translate(i % 9 * 100 - width * 0.5,
i / 9 * 100 - height * 0.5, -100);
rotate(map(i, 0, 80, -PI, PI), 0.5, 0.5, 0);
scale(0.5);
shape(rock);
popMatrix();
}
pushMatrix();
scale(10);
shape(cirno);
popMatrix();
surface.setTitle((int)frameRate + "fps");
}
It renders pretty fast, without textures though:
The game uses setTexture() and interestingly enough this drops the frame rate:
PShape rock;
PShape cirno;
void setup(){
size(900, 900, P3D);
cirno = loadShape("cirno_low.obj");
cirno.setTexture(loadImage("cirno_low_u1_v1.jpeg"));
rock = loadShape("rock.obj");
rock.setTexture(loadImage("rockTexture.png"));
int faces = 0;
int vertices = 0;
for(int i = 0 ; i < rock.getChildCount(); i++){
PShape c = rock.getChild(i);
vertices += c.getVertexCount();
faces++;
}
println("rock faces", faces, "vertices", vertices);
}
void draw(){
background(0);
lights();
translate(width * 0.5, height * 0.5, 0);
rotateY(map(mouseX, 0, width, -PI, PI));
rotateX(map(mouseY, 0, height, PI, -PI));
for(int i = 0 ; i < 81; i++){
pushMatrix();
translate(i % 9 * 100 - width * 0.5,
i / 9 * 100 - height * 0.5, -100);
rotate(map(i, 0, 80, -PI, PI), 0.5, 0.5, 0);
scale(0.5);
shape(rock);
popMatrix();
}
pushMatrix();
scale(10);
shape(cirno);
popMatrix();
surface.setTitle((int)frameRate + "fps");
}
Without checking the PShape source code, the assumption is behind the scenes the PShape has to do more work behind the scenes, because loading an .obj file with an .mtl (which helps load the texture as well) render just fine.
Here's the Processing > Examples > Basics > Shape > LoadDisplayOBJ example tweaked: it renders 1250 instances at 60fps:
/**
* Load and Display an OBJ Shape.
*
* The loadShape() command is used to read simple SVG (Scalable Vector Graphics)
* files and OBJ (Object) files into a Processing sketch. This example loads an
* OBJ file of a rocket and displays it to the screen.
*/
PShape rocket;
float ry;
public void setup() {
size(900, 900, P3D);
rocket = loadShape("rocket.obj");
}
public void draw() {
background(0);
lights();
translate(width/2, height/2 + 100, -200);
rotateY(map(mouseX, 0, width, -PI, PI));
rotateX(map(mouseY, 0, height, PI, -PI));
int nc = 1250;
float nr = sqrt(nc);
float sp = 150;
for(int i = 0 ; i < nc; i++){
pushMatrix();
translate(i % nr * sp - width * 0.5,
i / nr * sp - height * 0.5, -sp);
//rotate(map(i, 0, 80, -PI, PI), 0.5, 0.5, 0);
rotateZ(PI + radians(i));
rotateY(ry);
scale(0.5);
shape(rocket);
popMatrix();
}
//rotateZ(PI);
//rotateY(ry);
//shape(rocket);
ry += 0.02;
surface.setTitle((int)frameRate + "fps");
}
This pointed out another issue with how the obj files were used in the game:
each new Star() for example would load the .obj again.
public class Star extends UFO {
public Star (int x, int y, int spd) {
posX = x;
posY = y;
rot = int(random(0, 360));
speed = spd;
symbol = loadShape("rock.obj");
symbol.setTexture(rockTexture); //<>//
...
Ideally these meshes would be loaded once in setup(), with .mtl files and references passed to each instance needing to render them via shape().
This would allow instancing to work as it's the same geometry rendered multiple times. Reloading the same obj file into new memory addresses for each instance would result in many duplicated resources.
One quick fix for the .mtl issue is to simply import the obj in Blender, select it, apply the texture and export it:
(This would also be a good opportunity to rotate/scale models so when they're loaded in Processing, no additional transforms are required and they all can live an in easy to understand coordinate system)
The contents of the exported files I've used are:
cirno_lowWithMTL.mtl
cirno_lowWithMTL.obj
cirno_lowWithMTLDecimated.mtl
cirno_lowWithMTLDecimated.obj
They load/display (with textures) at 60fps (due to the .mtl files)
The recommended optimisation steps (other than using .obj with .mtl files and loading once and re-using mulitple times) are:
avoid extending fixed length arrays (e.g. kryptonit = (Kryptonit[]) append(kryptonit, new Kryptonit(int(random(50, width-150)), int(random(-300, 0))));). ArrayLists are better suited for resizing. In this case in particular a fixed length array is great, as long as it's objects are pre-allocated once, then the positions / states of the objects are updated (e.g. outside of screen objects are marked for re-use and hidden and instead of new objects, existing objects have positions visiblity/reset): in other words Object Pooling)
if meshes are displayed from a single point of view with only rotation on Z axis and position affecting them, they could be images (sprites) instead. (e.g. exporting a static image with alpha channel from Blender at the right scale (or using PGraphics to do this at runtime))
once meshes are loaded, instead of using transformations on them in draw() (e.g. symbol.rotateX(value), which will affect every single vertex in the PShape, use pushMatrix()/popMatrix() call with shape() so simply render the same geometry with different tranformations.
For reference this is the full program with minimal tweaks around loading/using .obj files efficiently (with the old approach commented out and few notes around those regions):
import com.dhchoi.CountdownTimer;
import com.dhchoi.CountdownTimerService;
import controlP5.*;
int zeit;
int punkte;
int leben;
int schwierigkeit = 20;
int zustand = 1;
int boost = 0;
int highscore = 0;
int minuten = 0;
int changeLevel = 0;
boolean paused = true;
boolean gameOver;
JSONArray saves = new JSONArray();
PFont gameOverFont;
PFont gameOverFontSmall;
CountdownTimer timer1 = CountdownTimerService.getNewCountdownTimer(this).configure(1000, 60000);
CountdownTimer kryptonitAnimationTimer1 = CountdownTimerService.getNewCountdownTimer(this).configure(10, 250);
boolean[] keysPressed = new boolean[65536];
ControlP5 cp5;
Star[] stars;
Kryptonit[] kryptonit;
Raumschiff raumschiff;
Player[] players;
String textValue = "";
PImage cirnoTexture;
PImage rockTexture;
PShape cirno;
PShape rock;
void loadMeshes(){
rock = loadShape("rockWithMTL.obj");
rock.scale(0.2);
cirno = loadShape("cirno_lowWithMTL.obj");
cirno.rotateY(HALF_PI);
cirno.rotateZ(HALF_PI * -1);
cirno.scale(5);
}
void settings()
{
//size(800, 400, P3D);
fullScreen(P3D);
smooth(8);
System.setProperty("jogl.disable.openglcore", "true");
}
void setup() {
surface.setResizable(true);
//println("loading textures");
//cirnoTexture = loadImage("cirno_low_u1_v1.jpeg");
//rockTexture = loadImage("rockTexture.png");
//println("finished loading textures: " + cirnoTexture);
loadMeshes();
stars = new Star[0];
kryptonit = new Kryptonit[0];
raumschiff = new Raumschiff(width/2, height/4*3, cirno);
leben = 5;
gameOverFont = createFont("Arial", 36, true);
gameOverFontSmall = createFont("Arial", 16, true);
for (int i = 0; i < schwierigkeit; i++) {
stars = (Star[]) append(stars, new Star(int(random(50, width-150)), int(random(50, height-100)), int(random(5, 15)), rock));
}
players = new Player[0];
cp5 = new ControlP5(this);
cp5.addTextfield("Name")
.setPosition(width/2-100, height/3*2-20)
.setSize(200, 40)
.setFont(gameOverFontSmall)
.setFocus(false)
.setColor(color(255))
.setAutoClear(false)
.setText("Name")
.setLabel("")
.hide()
.lock()
;
}
void draw() {
background(0);
lights();
switch (zustand) {
case 0:
break;
case 1:
fill(255);
text("Zeit:\t" + minuten + ":" + zeit, width-100, 50);
text("Punkte:\t" + punkte, width-100 , 100);
text("Leben:\t" + leben, width-100, 150);
text("Highscore:\t" + highscore, width-100, 200);
text("schwierigkeit:\t" + schwierigkeit, width-100, 250);
try {
for (int i = 0; i < players.length; i++) {
text(players[i].getName() + " " + players[i].getScore(), width-100, 300+15*i);
}
for (int i = 0; i < stars.length; i++) {
stars[i].zeichnen();
stars[i].drehen(random(0, 0.05), random(0, 0.05), random(0, 0.05));
}
for (int i = 0; i < kryptonit.length; i++) {
kryptonit[i].zeichnen();
}
if (kryptonitAnimationTimer1.getTimeLeftUntilFinish() != .0f) {
raumschiff.zeichnen(color(350-kryptonitAnimationTimer1.getTimeLeftUntilFinish(), 100, 100));
} else {
raumschiff.zeichnen(color(100, 100, 100));
}
} catch (Exception e) { e.printStackTrace(); }
noFill();
stroke(100);
rect(50, 50, width-200, height-150);
fill(0);
noStroke();
rect(0, 0, width-150, 48);
if (gameOver) {
pushMatrix();
fill(255);
textAlign(CENTER, CENTER);
textFont(gameOverFont, 36);
textSize(34);
text("GAME OVER!", width/2, height/2);
textFont(gameOverFontSmall, 16);
textSize(16);
text("Press ENTER to resume", width/2, height/2+30);
cp5.get(Textfield.class, "Name").unlock();
cp5.get(Textfield.class, "Name").show();
popMatrix();
}
else if (paused) {
pushMatrix();
fill(255);
textAlign(CENTER, CENTER);
textFont(gameOverFont, 36);
textSize(34);
text("PAUSED!", width/2, height/2);
textFont(gameOverFontSmall, 16);
textSize(16);
text("PRESS ANY KEY TO RESUME", width/2, height/2+30);
popMatrix();
}
break;
default :
background(0);
break;
}
for (int i = 0; i < stars.length; i++) {
if (stars[i].isVisible && sqrt((stars[i].posX - raumschiff.posX) * (stars[i].posX - raumschiff.posX) + (stars[i].posY - raumschiff.posY) * (stars[i].posY - raumschiff.posY) ) < 25){
stars[i].isVisible = false;
punkte+=stars[i].speed;
if (changeLevel > 0) {
changeLevel--;
}
}
}
if (punkte > highscore) {
highscore = punkte;
}
if (kryptonit.length < schwierigkeit / 5) {
//kryptonit = (Kryptonit[]) append(kryptonit, new Kryptonit(int(random(50, width-150)), int(random(-300, 0))));
}
if (stars.length < schwierigkeit) {
stars = (Star[]) append(stars, new Star(int(random(50, width-150)), int(random(-300, 0)), int(random(5, 15)), rock));
}
for (int i = 0; i < kryptonit.length; i++) {
if (kryptonit[i].isVisible && sqrt((kryptonit[i].posX - raumschiff.posX) * (kryptonit[i].posX - raumschiff.posX) + (kryptonit[i].posY - raumschiff.posY) * (kryptonit[i].posY - raumschiff.posY) ) < 25){
kryptonit[i].isVisible = false;
leben-=1;
kryptonitAnimationTimer1.start();
}
}
if (leben < 1){
gameOver = true;
}
if (punkte % 500 <= 20 && punkte % 500 >= 0 && changeLevel == 0 && zustand == 1) {
schwierigkeit+=5;
changeLevel = 5;
}
if (punkte % 500 > 20) {
changeLevel = 0;
}
if (!paused) {
try {
if (!gameOver) {
for (int i = 0; i < stars.length; i++) {
stars[i].bewegen(schwierigkeit/stars[i].speed+boost);
if (stars[i].posY > height-100){
stars[i] = null;
stars[i] = new Star(int(random(58, width-202)), int(random(-300, 0)), int(random(5, 15)), rock);
}
}
for (int i = 0; i < kryptonit.length; i++){
kryptonit[i].bewegen(schwierigkeit/10+boost);
if (kryptonit[i].posY > height-100){
kryptonit[i] = null;
kryptonit[i] = new Kryptonit(int(random(58, width-202)), int(random(-300, 0)));
}
}
}
} catch (Exception e) { e.printStackTrace(); }
}
if (keysPressed[56]){
boost = 5;
}
else {
boost = 0;
}
if (keysPressed[52] && !gameOver && !paused){
try {
raumschiff.bewegen(-7);
if (keysPressed[32]) {
raumschiff.bewegen(-10);
}
} catch (Exception e) { e.printStackTrace(); }
}
if (keysPressed[54] && !gameOver && !paused){
try {
raumschiff.bewegen(7);
if (keysPressed[32]) {
raumschiff.bewegen(10);
}
} catch (Exception e) { e.printStackTrace(); }
}
surface.setTitle((int)frameRate+"fps");
}
void keyPressed() {
if (gameOver && key == ENTER) {
players = (Player[]) append(players, new Player(cp5.get(Textfield.class, "Name").getText(), punkte));
cp5.get(Textfield.class, "Name").lock();
cp5.get(Textfield.class, "Name").hide();
for (int i = 0; i < saves.size(); i++) {
JSONObject playerJSONObject = new JSONObject();
playerJSONObject.setInt("id", i);
playerJSONObject.setString(cp5.get("Name", cp5.get(Textfield.class, "Name").getText()).toString(), "");
playerJSONObject.setInt("score", punkte);
}
saveJSONArray(saves, "data/highscores.json");
schwierigkeit = 20;
paused = true;
gameOver = false;
leben = 5;
punkte = 0;
timer1.reset(CountdownTimer.StopBehavior.STOP_IMMEDIATELY);
timer1.start();
zeit = 0;
stars = null;
stars = new Star[0];
for (int i = 0; i < schwierigkeit; i++) {
stars = (Star[]) append(stars, new Star(int(random(50, width-150)), int(random(50, height-100)), int(random(5, 15)), rock));
}
kryptonit = null;
kryptonit = new Kryptonit[0];
}
keysPressed[key] = true;
}
void keyReleased() {
keysPressed[key] = false;
}
void keyTyped() {
if (key == 'p' || key == 'P') {
if (!gameOver) {
paused = !paused;
if (paused) {
timer1.stop(CountdownTimer.StopBehavior.STOP_IMMEDIATELY);
}
else {
timer1.start();
}
}
}
if (paused && !gameOver && key != 'p' && key != 'P') {
paused = false;
timer1.start();
}
}
void onTickEvent(CountdownTimer t, long timeLeftUntilFinish) {
if (t == timer1) {
zeit++;
}
}
void onFinishEvent(CountdownTimer t) {
if (t == timer1) {
timer1.reset(CountdownTimer.StopBehavior.STOP_AFTER_INTERVAL);
timer1.start();
zeit = 0;
minuten++;
}
}
abstract class Flugobjekt {
public int posX;
public int posY;
public int rot;
public int speed;
boolean isVisible = true;
PShape symbol;
abstract void bewegen (int amount);
}
abstract class UFO extends Flugobjekt {
}
public class Star extends UFO {
float rotationX, rotationY, rotationZ;
public Star (int x, int y, int spd, PShape symbol) {
posX = x;
posY = y;
rot = int(random(0, 360));
speed = spd;
// use a reference to the preloaded PShape (instead of loading a the .obj again for each instance)
this.symbol = symbol;
//symbol = loadShape("rockWithMTL.obj");
//symbol.setTexture(rockTexture);
//symbol.scale(0.2);
/*
fill(255);
stroke(255);
strokeWeight(2);
symbol = createShape();
symbol.beginShape();
symbol.vertex(0, -5);
symbol.vertex(1.4, -2);
symbol.vertex(4.7, -1.5);
symbol.vertex(2.3, 0.7);
symbol.vertex(2.9, 4.0);
symbol.vertex(0, 2.5);
symbol.vertex(-2.9, 4);
symbol.vertex(-2.3, 0.7);
symbol.vertex(-4.7, -1.5);
symbol.vertex(-1.4, -2);
symbol.endShape(CLOSE);
/*/
}
public void zeichnen (){
// skip if PShape (or it's texture) isn't loaded yet)
if(symbol == null){
return;
}
if (isVisible) {
pushMatrix();
translate(posX, posY);
//rotate(rot);
rotateX(rotationX);
rotateY(rotationY);
rotateZ(rotationZ);
//scale(0.2);
shape(symbol);
popMatrix();
}
}
public void bewegen (int amount) {
posY = posY + amount;
}
public void drehen (float xAmount, float yAmount, float zAmount) {
rotationX += xAmount;
rotationY += yAmount;
rotationZ += zAmount;
// symbol.rotateX means all vertices inside the shape will be updated
// use rotateX() then shape() to simply render the same underlying PShape vertex data without updating it all the time
//symbol.rotateX(xAmount);
//symbol.rotateY(yAmount);
//symbol.rotateZ(zAmount);
}
}
public class Kryptonit extends UFO {
public Kryptonit (int x, int y) {
posX = x;
posY = y;
rot = int(random(0, 360));
fill(0);
stroke(255, 0, 0);
strokeWeight(2);
symbol = createShape();
symbol.beginShape();
symbol.vertex(0, -5);
symbol.vertex(1.4, -2);
symbol.vertex(4.7, -1.5);
symbol.vertex(2.3, 0.7);
symbol.vertex(2.9, 4.0);
symbol.vertex(0, 2.5);
symbol.vertex(-2.9, 4);
symbol.vertex(-2.3, 0.7);
symbol.vertex(-4.7, -1.5);
symbol.vertex(-1.4, -2);
symbol.endShape(CLOSE);
}
public void zeichnen (){
if (isVisible) {
pushMatrix();
translate(posX, posY);
rotate(rot);
shape(symbol);
popMatrix();
}
}
public void bewegen (int amount) {
posY = posY + amount;
}
}
public class Raumschiff extends Flugobjekt {
public Raumschiff (int x, int y, PShape symbol) {
posX = x;
posY = y;
fill(100);
noStroke();
this.symbol = symbol;
//symbol = loadShape("cirno_lowWithMTL.obj");//createShape(ELLIPSE, 0, 0, 50, 50);
//symbol.setTexture(cirnoTexture);
//symbol.rotateY(HALF_PI);
//symbol.rotateZ(HALF_PI * -1);
////symbol.rotateX(0.5);
//symbol.scale(5);
/* (Raumschiff)
symbol = createShape();
symbol.beginShape();
symbol.vertex(25, 0);
symbol.vertex(30, 5);
symbol.vertex(30, 5);
symbol.vertex(32, 12);
symbol.vertex(28, 20);
symbol.vertex(31, 28);
symbol.vertex(27, 25);
symbol.vertex(25, 29);
symbol.vertex(23, 25);
symbol.vertex(19, 28);
symbol.vertex(22, 20);
symbol.vertex(18, 12);
symbol.vertex(20, 5);
symbol.endShape(CLOSE);
//*/
}
public void zeichnen (color farbe){
if (isVisible) {
pushMatrix();
symbol.setFill(farbe);
translate(posX, posY);
rotate(rot);
shape(symbol);
popMatrix();
}
}
public void bewegen (int amount) {
posX+=amount;
if (posX < 50) posX = 50;
if (posX > width-150) posX = width-150;
}
}
public class Player {
private String name;
private int score;
public Player (String n, int s) {
name = n;
score = s;
}
public String getName() {
return name;
}
public int getScore() {
return score;
}
}
Here's an example of pre-allocating a number of objects to be reused (a-la object pooling), instead of constant reinstantiation (which has it's costs):
PShape rock;
int numRocks = 25;
Rock[] rocks = new Rock[numRocks];
float halfWidth;
float halfHeight;
void setup(){
size(900, 900, P3D);
rock = loadShape("rockWithMTL.obj");
// ideally the mesh would already been scaled down to avoid this
rock.scale(0.2);
halfWidth = width * 0.5;
halfHeight = height * 0.5;
for(int i = 0 ; i < numRocks; i++){
rocks[i] = new Rock(rock, random(-halfWidth, halfWidth), random(-halfHeight, halfHeight));
}
}
void draw(){
background(0);
lights();
translate(width * 0.5, height * 0.5, 0);
for(int i = 0 ; i < numRocks; i++){
rocks[i].draw();
}
surface.setTitle((int)frameRate + "fps");
}
class Rock{
PShape mesh;
PVector position = new PVector();
PVector velocity = new PVector();
PVector rotationAxis = new PVector();
float rotationAngle = 0;
Rock(PShape mesh, float x, float y){
this.mesh = mesh;
position.x = x;
position.y = y;
velocity.y = random(1, 10);
// pick a random rotation axis
rotationAxis.set(random(1), random(1), random(1));
}
void draw(){
// update
// increment position
position.add(velocity);
// increment rotation
rotationAngle += 0.1;
// object pool behaviour: reset if off screen (no need to re-allocate a new instance)
if(position.y > halfHeight + 100){
position.x = random(-halfWidth, halfWidth);
position.y = -halfHeight - 100;
}
// draw
pushMatrix();
translate(position.x, position.y, position.z);
rotate(rotationAngle, rotationAxis.x, rotationAxis.y, rotationAxis.z);
shape(mesh);
popMatrix();
}
}
Also, here's a super basic demo on encapsulating states. It's a bit hacky because each state know of the other, but shows each could behave as it's own "sketch" that can live in it's own tab and only override it's specific behaviour:
StartScreen start;
GameScreen game;
HighScoreScreen highScore;
StateScreen currentScreen;
void setup(){
size(300, 300);
textAlign(CENTER, CENTER);
textSize(18);
start = new StartScreen();
game = new GameScreen();
highScore = new HighScoreScreen();
currentScreen = start;
}
void draw(){
background(0);
currentScreen.draw();
}
void keyPressed(){
currentScreen.keyPressed();
}
class StateScreen {
StateScreen(){
setup();
}
void setup(){ println(this,"setup()"); }
void draw(){}
void keyPressed(){}
}
class StartScreen extends StateScreen{
void draw(){
fill(sin(frameCount * 0.1) * 127);
text("push any key to\nstart", width * 0.5, height * 0.5);
}
void keyPressed(){
currentScreen = game;
}
}
class GameScreen extends StateScreen{
void draw(){
fill(0, sin(frameCount * 0.1) * 127, 0);
text("push SPACE key to go to\nhigh score screen", width * 0.5, height * 0.5);
}
void keyPressed(){
currentScreen = highScore;
}
}
class HighScoreScreen extends StateScreen{
void draw(){
fill(random(255), random(255), random(255));
text("push SPACE key to go to\nstart screen", width * 0.5, height * 0.5);
}
void keyPressed(){
currentScreen = start;
}
}

Two "Differential Growths" simultaneously - sort of

I'm still new to Stackoverflow and new to the topic of Processing and Java, so apologies if I can't make my question as clear directly.
My goal is to create a "growing shape" with each mouse click. Below you will find my current code, which currently allows one of these shapes to be created with a mouse click. But as soon as I click again, my first shape stops growing and the second one starts (a bit hard to explain - best copy the code once and try it, then you know what I mean).
I've been trying for hours to somehow generate multiple shapes - one for each mouse click - which each continue to grow on their own, unfortunately without success. The goal is to have a lot of growing shapes at the same time.
Do any of you have an idea how to implement this?
If you need more info, just contact me!
Thank you
Lara
Code:
// PARAMETERS
float _maxForce = 0.9;
float _maxSpeed = 1;
float _desiredSeparation = 30;
float _separationCohesionRation = 1.1;
float _maxEdgeLen = 8;
boolean seed = false;
DifferentialLine _diff_line;
void setup() {
frameRate(60);
background(0);
size(1280, 720);
}
void draw() {
if(seed == true){
//println(frameCount);
background(0);
_diff_line.run();
_diff_line.render();
}
}
void mousePressed(){
seed = true;
malewas();
}
void malewas(){
_diff_line = new DifferentialLine(_maxForce, _maxSpeed, _desiredSeparation, _separationCohesionRation, _maxEdgeLen);
//fill(0);
float nodesStart = 3;
float angInc = TWO_PI/nodesStart;
float rayStart = 5;
for (float a=0; a<TWO_PI; a+=angInc) {
float x = mouseX + cos(a) * rayStart;
float y = mouseY + sin(a) * rayStart;
_diff_line.addNode(new Node(x, y, _diff_line.maxForce, _diff_line.maxSpeed, _diff_line.desiredSeparation, _diff_line.separationCohesionRation));
//println(mouseX);
//println(mouseY);
}
}
class DifferentialLine {
ArrayList<Node> nodes;
float maxForce;
float maxSpeed;
float desiredSeparation;
float separationCohesionRation;
float maxEdgeLen;
DifferentialLine(float mF, float mS, float dS, float sCr, float eL) {
nodes = new ArrayList<Node>();
maxSpeed = mF;
maxForce = mS;
desiredSeparation = dS;
separationCohesionRation = sCr;
maxEdgeLen = eL;
}
void run() {
for (Node n : nodes) {
n.run(nodes);
}
growth();
}
void addNode(Node n) {
nodes.add(n);
}
void addNodeAt(Node n, int index) {
nodes.add(index, n);
}
void growth() {
for (int i=0; i<nodes.size()-1; i++) {
Node n1 = nodes.get(i);
Node n2 = nodes.get(i+1);
float d = PVector.dist(n1.position, n2.position);
if (d>maxEdgeLen) { // Can add more rules for inserting nodes
int index = nodes.indexOf(n2);
PVector middleNode = PVector.add(n1.position, n2.position).div(2);
addNodeAt(new Node(middleNode.x, middleNode.y, maxForce, maxSpeed, desiredSeparation, separationCohesionRation), index);
}
}
}
void render() {
for (int i=0; i<nodes.size()-1; i++) {
PVector p1 = nodes.get(i).position;
PVector p2 = nodes.get(i+1).position;
line(p1.x, p1.y, p2.x, p2.y);
colorMode(HSB);
stroke(255);
//stroke(10,frameCount*0.3,100);
//stroke(255);//Farbe
strokeWeight(1); //Strichstärke
if (i==nodes.size()-2) {
line(p2.x, p2.y, nodes.get(0).position.x, nodes.get(0).position.y);
}
}
}
}
class Node {
PVector position;
PVector velocity;
PVector acceleration;
float maxForce;
float maxSpeed;
float desiredSeparation;
float separationCohesionRation;
Node(float x, float y) {
acceleration = new PVector(0, 0);
velocity =PVector.random2D();
position = new PVector(x, y);
}
Node(float x, float y, float mF, float mS, float dS, float sCr) {
acceleration = new PVector(0, 0);
velocity =PVector.random2D();
position = new PVector(x, y);
maxSpeed = mF;
maxForce = mS;
desiredSeparation = dS;
separationCohesionRation = sCr;
}
void run(ArrayList<Node> nodes) {
differentiate(nodes);
update();
}
void applyForce(PVector force) {
acceleration.add(force);
}
void differentiate(ArrayList<Node> nodes) {
PVector separation = separate(nodes);
PVector cohesion = edgeCohesion(nodes);
separation.mult(separationCohesionRation);
//cohesion.mult(1.0);
applyForce(separation);
applyForce(cohesion);
}
void update() {
velocity.add(acceleration);
velocity.limit(maxSpeed);
position.add(velocity);
acceleration.mult(0);
}
PVector seek(PVector target) {
PVector desired = PVector.sub(target, position);
desired.setMag(maxSpeed);
PVector steer = PVector.sub(desired, velocity);
steer.limit(maxForce);
return steer;
}
PVector separate(ArrayList<Node> nodes) {
PVector steer = new PVector(0, 0);
int count = 0;
for (Node other : nodes) {
float d = PVector.dist(position, other.position);
if (d>0 && d < desiredSeparation) {
PVector diff = PVector.sub(position, other.position);
diff.normalize();
diff.div(d); // Weight by distance
steer.add(diff);
count++;
}
}
if (count>0) {
steer.div((float)count);
}
if (steer.mag() > 0) {
steer.setMag(maxSpeed);
steer.sub(velocity);
steer.limit(maxForce);
}
return steer;
}
PVector edgeCohesion (ArrayList<Node> nodes) {
PVector sum = new PVector(0, 0);
int this_index = nodes.indexOf(this);
if (this_index!=0 && this_index!=nodes.size()-1) {
sum.add(nodes.get(this_index-1).position).add(nodes.get(this_index+1).position);
} else if (this_index == 0) {
sum.add(nodes.get(nodes.size()-1).position).add(nodes.get(this_index+1).position);
} else if (this_index == nodes.size()-1) {
sum.add(nodes.get(this_index-1).position).add(nodes.get(0).position);
}
sum.div(2);
return seek(sum);
}
}
Code after first Answer:
// PARAMETERS
float _maxForce = 0.9;
float _maxSpeed = 1;
float _desiredSeparation = 30;
float _separationCohesionRation = 1.1;
float _maxEdgeLen = 8;
boolean seed = false;
DifferentialLine _diff_line;
ArrayList<DifferentialLine> lines = new ArrayList<DifferentialLine>();
void setup() {
frameRate(60);
background(0);
size(1280, 720);
}
void draw() {
if(seed == true){
//println(frameCount);
background(0);
//_diff_line.run();
//_diff_line.render();
for (DifferentialLine line : lines) {
line.run();
line.render();
}
}
}
void mousePressed(){
seed = true;
DifferentialLine newLine = new DifferentialLine(_maxForce, _maxSpeed, _desiredSeparation, _separationCohesionRation, _maxEdgeLen);
malewas();
lines.add(newLine);
}
void malewas(){
_diff_line = new DifferentialLine(_maxForce, _maxSpeed, _desiredSeparation, _separationCohesionRation, _maxEdgeLen);
//fill(0);
float nodesStart = 3;
float angInc = TWO_PI/nodesStart;
float rayStart = 5;
for (float a=0; a<TWO_PI; a+=angInc) {
float x = mouseX + cos(a) * rayStart;
float y = mouseY + sin(a) * rayStart;
_diff_line.addNode(new Node(x, y, _diff_line.maxForce, _diff_line.maxSpeed, _diff_line.desiredSeparation, _diff_line.separationCohesionRation));
//println(mouseX);
//println(mouseY);
}
}
class DifferentialLine {
ArrayList<Node> nodes;
float maxForce;
float maxSpeed;
float desiredSeparation;
float separationCohesionRation;
float maxEdgeLen;
DifferentialLine(float mF, float mS, float dS, float sCr, float eL) {
nodes = new ArrayList<Node>();
maxSpeed = mF;
maxForce = mS;
desiredSeparation = dS;
separationCohesionRation = sCr;
maxEdgeLen = eL;
}
void run() {
for (Node n : nodes) {
n.run(nodes);
}
growth();
}
void addNode(Node n) {
nodes.add(n);
}
void addNodeAt(Node n, int index) {
nodes.add(index, n);
}
void growth() {
for (int i=0; i<nodes.size()-1; i++) {
Node n1 = nodes.get(i);
Node n2 = nodes.get(i+1);
float d = PVector.dist(n1.position, n2.position);
if (d>maxEdgeLen) { // Can add more rules for inserting nodes
int index = nodes.indexOf(n2);
PVector middleNode = PVector.add(n1.position, n2.position).div(2);
addNodeAt(new Node(middleNode.x, middleNode.y, maxForce, maxSpeed, desiredSeparation, separationCohesionRation), index);
}
}
}
void render() {
for (int i=0; i<nodes.size()-1; i++) {
PVector p1 = nodes.get(i).position;
PVector p2 = nodes.get(i+1).position;
line(p1.x, p1.y, p2.x, p2.y);
colorMode(HSB);
stroke(255);
//stroke(10,frameCount*0.3,100);
//stroke(255);//Farbe
strokeWeight(1); //Strichstärke
if (i==nodes.size()-2) {
line(p2.x, p2.y, nodes.get(0).position.x, nodes.get(0).position.y);
}
}
}
}
class Node {
PVector position;
PVector velocity;
PVector acceleration;
float maxForce;
float maxSpeed;
float desiredSeparation;
float separationCohesionRation;
Node(float x, float y) {
acceleration = new PVector(0, 0);
velocity =PVector.random2D();
position = new PVector(x, y);
}
Node(float x, float y, float mF, float mS, float dS, float sCr) {
acceleration = new PVector(0, 0);
velocity =PVector.random2D();
position = new PVector(x, y);
maxSpeed = mF;
maxForce = mS;
desiredSeparation = dS;
separationCohesionRation = sCr;
}
void run(ArrayList<Node> nodes) {
differentiate(nodes);
update();
}
void applyForce(PVector force) {
acceleration.add(force);
}
void differentiate(ArrayList<Node> nodes) {
PVector separation = separate(nodes);
PVector cohesion = edgeCohesion(nodes);
separation.mult(separationCohesionRation);
//cohesion.mult(1.0);
applyForce(separation);
applyForce(cohesion);
}
void update() {
velocity.add(acceleration);
velocity.limit(maxSpeed);
position.add(velocity);
acceleration.mult(0);
}
PVector seek(PVector target) {
PVector desired = PVector.sub(target, position);
desired.setMag(maxSpeed);
PVector steer = PVector.sub(desired, velocity);
steer.limit(maxForce);
return steer;
}
PVector separate(ArrayList<Node> nodes) {
PVector steer = new PVector(0, 0);
int count = 0;
for (Node other : nodes) {
float d = PVector.dist(position, other.position);
if (d>0 && d < desiredSeparation) {
PVector diff = PVector.sub(position, other.position);
diff.normalize();
diff.div(d); // Weight by distance
steer.add(diff);
count++;
}
}
if (count>0) {
steer.div((float)count);
}
if (steer.mag() > 0) {
steer.setMag(maxSpeed);
steer.sub(velocity);
steer.limit(maxForce);
}
return steer;
}
PVector edgeCohesion (ArrayList<Node> nodes) {
PVector sum = new PVector(0, 0);
int this_index = nodes.indexOf(this);
if (this_index!=0 && this_index!=nodes.size()-1) {
sum.add(nodes.get(this_index-1).position).add(nodes.get(this_index+1).position);
} else if (this_index == 0) {
sum.add(nodes.get(nodes.size()-1).position).add(nodes.get(this_index+1).position);
} else if (this_index == nodes.size()-1) {
sum.add(nodes.get(this_index-1).position).add(nodes.get(0).position);
}
sum.div(2);
return seek(sum);
}
}
Problem
While you do in fact create a new shape with each mouse click, you're always assigning it to the same variable (_diff_line):
_diff_line = new DifferentialLine(_maxForce, _maxSpeed, _desiredSeparation, _separationCohesionRation, _maxEdgeLen);
So when you call _diff_line.run() and _diff_line.render() it is only ever operating on the one most recently created. The old ones no longer exist.
Solution
To fix this, you'd need to assign each new line to a different variable. Most likely you'd do this by creating an Array or ArrayList and adding a new DifferentialLine to the array on each click:
// create an ArrayList to hold all the lines
ArrayList<DifferentialLine> lines = new ArrayList<DifferentialLine>();
void mousePressed(){
// create the new line
DifferentialLine newLine = new DifferentialLine(_maxForce, _maxSpeed, _desiredSeparation, _separationCohesionRation, _maxEdgeLen);
// ... do your node setup stuff
// add the new line to the ArrayList
lines.add(newLine);
}
Then, when you want to render them, you can loop through the ArrayList (or Array) and draw each one:
for (DifferentialLine line : lines) {
line.run();
line.render();
}
Solution
// PARAMETERS
float _maxForce = 0.9;
float _maxSpeed = 1;
float _desiredSeparation = 30;
float _separationCohesionRation = 1.1;
float _maxEdgeLen = 8;
boolean seed = false;
//DifferentialLine _diff_line;
ArrayList<DifferentialLine> lines = new ArrayList<DifferentialLine>();
void setup() {
frameRate(60);
background(0);
size(1280, 720);
}
void draw() {
if (seed == true) {
//println(frameCount);
background(0);
//_diff_line.run();
//_diff_line.render();
for (DifferentialLine line : lines) {
line.run();
line.render();
}
}
}
void mousePressed() {
seed = true;
DifferentialLine newLine = new DifferentialLine(_maxForce, _maxSpeed, _desiredSeparation, _separationCohesionRation, _maxEdgeLen);
//drawseed();
float nodesStart = 3;
float angInc = TWO_PI/nodesStart;
float rayStart = 5;
for (float a=0; a<TWO_PI; a+=angInc) {
float x = mouseX + cos(a) * rayStart;
float y = mouseY + sin(a) * rayStart;
newLine.addNode(new Node(x, y, newLine.maxForce, newLine.maxSpeed, newLine.desiredSeparation, newLine.separationCohesionRation));
//println(mouseX);
//println(mouseY);
}
lines.add(newLine);
}
/*void drawseed() {
_diff_line = new DifferentialLine(_maxForce, _maxSpeed, _desiredSeparation, _separationCohesionRation, _maxEdgeLen);
//fill(0);
float nodesStart = 3;
float angInc = TWO_PI/nodesStart;
float rayStart = 5;
for (float a=0; a<TWO_PI; a+=angInc) {
float x = mouseX + cos(a) * rayStart;
float y = mouseY + sin(a) * rayStart;
_diff_line.addNode(new Node(x, y, _diff_line.maxForce, _diff_line.maxSpeed, _diff_line.desiredSeparation, _diff_line.separationCohesionRation));
//println(mouseX);
//println(mouseY);
}
}*/
class DifferentialLine {
ArrayList<Node> nodes;
float maxForce;
float maxSpeed;
float desiredSeparation;
float separationCohesionRation;
float maxEdgeLen;
DifferentialLine(float mF, float mS, float dS, float sCr, float eL) {
nodes = new ArrayList<Node>();
maxSpeed = mF;
maxForce = mS;
desiredSeparation = dS;
separationCohesionRation = sCr;
maxEdgeLen = eL;
}
void run() {
for (Node n : nodes) {
n.run(nodes);
}
growth();
}
void addNode(Node n) {
nodes.add(n);
}
void addNodeAt(Node n, int index) {
nodes.add(index, n);
}
void growth() {
for (int i=0; i<nodes.size()-1; i++) {
Node n1 = nodes.get(i);
Node n2 = nodes.get(i+1);
float d = PVector.dist(n1.position, n2.position);
if (d>maxEdgeLen) { // Can add more rules for inserting nodes
int index = nodes.indexOf(n2);
PVector middleNode = PVector.add(n1.position, n2.position).div(2);
addNodeAt(new Node(middleNode.x, middleNode.y, maxForce, maxSpeed, desiredSeparation, separationCohesionRation), index);
}
}
}
void render() {
for (int i=0; i<nodes.size()-1; i++) {
PVector p1 = nodes.get(i).position;
PVector p2 = nodes.get(i+1).position;
line(p1.x, p1.y, p2.x, p2.y);
colorMode(HSB);
stroke(255);
//stroke(10,frameCount*0.3,100);
//stroke(255);//Farbe
strokeWeight(1); //Strichstärke
if (i==nodes.size()-2) {
line(p2.x, p2.y, nodes.get(0).position.x, nodes.get(0).position.y);
}
}
}
}
class Node {
PVector position;
PVector velocity;
PVector acceleration;
float maxForce;
float maxSpeed;
float desiredSeparation;
float separationCohesionRation;
Node(float x, float y) {
acceleration = new PVector(0, 0);
velocity =PVector.random2D();
position = new PVector(x, y);
}
Node(float x, float y, float mF, float mS, float dS, float sCr) {
acceleration = new PVector(0, 0);
velocity =PVector.random2D();
position = new PVector(x, y);
maxSpeed = mF;
maxForce = mS;
desiredSeparation = dS;
separationCohesionRation = sCr;
}
void run(ArrayList<Node> nodes) {
differentiate(nodes);
update();
}
void applyForce(PVector force) {
acceleration.add(force);
}
void differentiate(ArrayList<Node> nodes) {
PVector separation = separate(nodes);
PVector cohesion = edgeCohesion(nodes);
separation.mult(separationCohesionRation);
//cohesion.mult(1.0);
applyForce(separation);
applyForce(cohesion);
}
void update() {
velocity.add(acceleration);
velocity.limit(maxSpeed);
position.add(velocity);
acceleration.mult(0);
}
PVector seek(PVector target) {
PVector desired = PVector.sub(target, position);
desired.setMag(maxSpeed);
PVector steer = PVector.sub(desired, velocity);
steer.limit(maxForce);
return steer;
}
PVector separate(ArrayList<Node> nodes) {
PVector steer = new PVector(0, 0);
int count = 0;
for (Node other : nodes) {
float d = PVector.dist(position, other.position);
if (d>0 && d < desiredSeparation) {
PVector diff = PVector.sub(position, other.position);
diff.normalize();
diff.div(d); // Weight by distance
steer.add(diff);
count++;
}
}
if (count>0) {
steer.div((float)count);
}
if (steer.mag() > 0) {
steer.setMag(maxSpeed);
steer.sub(velocity);
steer.limit(maxForce);
}
return steer;
}
PVector edgeCohesion (ArrayList<Node> nodes) {
PVector sum = new PVector(0, 0);
int this_index = nodes.indexOf(this);
if (this_index!=0 && this_index!=nodes.size()-1) {
sum.add(nodes.get(this_index-1).position).add(nodes.get(this_index+1).position);
} else if (this_index == 0) {
sum.add(nodes.get(nodes.size()-1).position).add(nodes.get(this_index+1).position);
} else if (this_index == nodes.size()-1) {
sum.add(nodes.get(this_index-1).position).add(nodes.get(0).position);
}
sum.div(2);
return seek(sum);
}
}

How to use Hashmap or Array to create a "list" of countries with attributes

I'm trying to visualize migration data using particle systems. Each row of the dataset contains: source country, destination country, year, and various amounts.
Each row should be represented as a particle system representing that data.
QUESTION:
What will be the best way to create a list of countries or country objects, with x,y location attributes and maybe some other attributes that could be added later?
Entire code for reference:
// Based on Example Written by Casey Reas and Ben Fry
// Edited by Tom Bar-Gal
//particlesystem()
//addparticle()
//particle()
// ========== Table Data Stuff
Table table;
int k = 0;
String[] destCountryArray = new String[0];
String[] sourceCountryArray = new String[0];
String destCountry = "";
String prevDestCountry = "";
String sourceCountry;
// ========
int maxParticles = 12000;
ParticleSystem ps;
ParticleSystem ps2;
int n = 0, n2=0;
int emmitMultiplyer = 1;
int emmitFreq = 1;
float particleSpeed = 0.002;
float locationX = 250;
float locationY = 450;
int[] sourceX = {10, 40, 200, 400, 700};
int[] destX = {300, 600, 300, 600, 600};
int[] amount = {10, 100, 500, 800, 1000};
int highestAmount = max(amount);
// a,b,c... max*a/{a+b+c...}
ParticleSystem[] PSystems;
void setup() {
size(1200, 800);
//=============== load table and create an array of countries
table = loadTable("asylum_seekers.csv", "header");
destCountryArray = (String[]) append(destCountryArray, "Israel");
for (TableRow row : table.rows()) {
//println("going over row" + row.getString("Country / territory of asylum/residence"));
String tempCountryHolder = row.getString("Country / territory of asylum/residence");
//println("Got a temp country holder" + tempCountryHolder);
boolean exists = countryExists(tempCountryHolder);
if (exists==true) {
//println("exists, skipping");
continue;
}
println("Appending "+tempCountryHolder+" to list of length " +destCountryArray.length);
destCountryArray = (String[]) append(destCountryArray, tempCountryHolder);
println("destCountryArray length = "+ destCountryArray.length);
}
//============================
PSystems = new ParticleSystem[destCountryArray.length];
//frameRate(30);
//colorMode(RGB,255,255,255,255);
for (int i = 0; i<destCountryArray.length; i++) {
// Particle Systems syntax = multiplyer, source, destination, amount);
PSystems[i] = new ParticleSystem(1, new Vector3D(i*40+40, 100, 0), new Vector3D(i*40+40, 500, 0), 1/(i+1));
//println("PSystems " + i + " is " +PSystems[i]);
}
//ps = new ParticleSystem(1, new Vector3D(width/2, height/2, 0));
//ps2 = new ParticleSystem(1, new Vector3D(100, 200, 0));
smooth();
}
void draw() {
background(250);
//ellipse(locationX, locationY, 5, 5);
//ellipse(width/2, height/2, 5, 5);
//ellipse(100, 200, 5, 5);
//println(PSystems.length);
for (int i = 0; i<destCountryArray.length; i++) {
//println(PSystems[i]);
PSystems[i].run();
}
for (int i = 0; i<emmitMultiplyer; i++) {
for (int k = 0; k<destCountryArray.length; k++) {
if (frameCount % (k+1) == 0) {
PSystems[k].addParticle();
n++;
}
}
}
n2+=emmitMultiplyer;
fill(0);
text("Frame rate: "
+ int(frameRate), 10, 20);
println(n);
println(n);
}
// ==============================// A simple Particle class // ===============================================//
class Particle {
Vector3D loc;
Vector3D des;
Vector3D vel;
Vector3D acc;
Vector3D locHome, b, c;
float relativeSpeed;
float r;
float timer;
float t=0.0;
// Another constructor (the one we are using here)
Particle(Vector3D l, Vector3D m) {
//acc = new Vector3D(0,0.0005,0); // particle acceleration
acc = new Vector3D(0, 0, 0); // new Vector3D(random(-0.1, 0.1), random(-0.02, 0), 0);
loc = l.copy();
des = m.copy();
locHome = l.copy();
locHome.x = locHome.x+random(-2, 2);
locHome.y = locHome.y+random(-2, 2);
des.x = des.x+random(-2, 2);
des.y=des.y+random(-2, 2);
relativeSpeed = random(0.5, 1.2);
r = random(0.9, 2.3); // particle radius
timer = 10000.0; // particles lifespan
// * emmitMultiplyer = number of living
b=new Vector3D(locHome.x+random(-20, 20), locHome.y+random(120, 180), 0);
c=new Vector3D(des.x+random(-20, 30), des.y-random(120, 180), 0);
}
void run() {
update();
render();
}
// Method to update location
void update() {
if (t>=1)
return;
// https : // www.processing.org/reference/bezierPoint_.html
loc.x = bezierPoint(locHome.x, b.x, c.x, des.x, t);
loc.y = bezierPoint(locHome.y, b.y, c.y, des.y, t);
t = lerp(t, 1, particleSpeed*relativeSpeed);
//t+=particleSpeed*relativeSpeed;
// curvePoint(a, b, c, d, t)
// vel.add(acc);
// loc.add(vel);
//timer -= 1.0;
}
// Method to display
void render() {
ellipseMode(CENTER);
noStroke();
fill(70, 255);
ellipse(loc.x, loc.y, r, r);
}
// Is the particle still useful?
boolean dead() {
// if (timer <= 0.0||t>=1.0) {
if (t>=0.95) {
return true;
} else {
return false;
}
}
}
// ==============================// A ParticleSystem // ===============================================//
// A class to describe a group of Particles
// An ArrayList is used to manage the list of Particles
class ParticleSystem {
ArrayList particles; // An arraylist for all the particles
Vector3D origin; // An origin point for where particles are birthed
Vector3D dest;
int freq;
//ParticleSystem( number of particles / frame, source, destination, frequency);
ParticleSystem(int num, Vector3D v, Vector3D d, float f) {
particles = new ArrayList(); // Initialize the arraylist
origin = v.copy(); // Store the origin point
dest = d.copy();
//if (frameCount % (1/f) == 0){
for (int i = 0; i < num; i++) {
particles.add(new Particle(origin, dest)); // Add "num" amount of particles to the arraylist
}
//}
}
void run() {
// Cycle through the ArrayList backwards b/c we are deleting
for (int i = particles.size()-1; i >= 0; i--) {
Particle p = (Particle) particles.get(i);
p.run();
if (p.dead()) {
particles.remove(i);
n--;
}
}
}
void addParticle() {
particles.add(new Particle(origin, dest));
}
//void addParticle(Particle p) {
// particles.add(p);
//}
// A method to test if the particle system still has particles
boolean dead() {
if (particles.isEmpty()) {
return true;
} else {
return false;
}
}
}
//=================================================== Class Country
public class Country {
public float countryIndex;
public float countryLocationX;
public float countryLocationY;
public float countryName;
}
// ================================================ Simple Vector3D Class
public class Vector3D {
public float x;
public float y;
public float z;
Vector3D(float x_, float y_, float z_) {
x = x_;
y = y_;
z = z_;
}
Vector3D(float x_, float y_) {
x = x_;
y = y_;
z = 0f;
}
Vector3D() {
x = 0f;
y = 0f;
z = 0f;
}
void setX(float x_) {
x = x_;
}
void setY(float y_) {
y = y_;
}
void setZ(float z_) {
z = z_;
}
void setXY(float x_, float y_) {
x = x_;
y = y_;
}
void setXYZ(float x_, float y_, float z_) {
x = x_;
y = y_;
z = z_;
}
void setXYZ(Vector3D v) {
x = v.x;
y = v.y;
z = v.z;
}
public float magnitude() {
return (float) Math.sqrt(x*x + y*y + z*z);
}
public Vector3D copy() {
return new Vector3D(x, y, z);
}
public Vector3D copy(Vector3D v) {
return new Vector3D(v.x, v.y, v.z);
}
public void add(Vector3D v) {
x += v.x;
y += v.y;
z += v.z;
}
public void sub(Vector3D v) {
x -= v.x;
y -= v.y;
z -= v.z;
}
public void mult(float n) {
x *= n;
y *= n;
z *= n;
}
public void div(float n) {
x /= n;
y /= n;
z /= n;
}
public void normalize() {
float m = magnitude();
if (m > 0) {
div(m);
}
}
public void limit(float max) {
if (magnitude() > max) {
normalize();
mult(max);
}
}
public float heading2D() {
float angle = (float) Math.atan2(-y, x);
return -1*angle;
}
public Vector3D add(Vector3D v1, Vector3D v2) {
Vector3D v = new Vector3D(v1.x + v2.x, v1.y + v2.y, v1.z + v2.z);
return v;
}
public Vector3D sub(Vector3D v1, Vector3D v2) {
Vector3D v = new Vector3D(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z);
return v;
}
public Vector3D div(Vector3D v1, float n) {
Vector3D v = new Vector3D(v1.x/n, v1.y/n, v1.z/n);
return v;
}
public Vector3D mult(Vector3D v1, float n) {
Vector3D v = new Vector3D(v1.x*n, v1.y*n, v1.z*n);
return v;
}
public float distance (Vector3D v1, Vector3D v2) {
float dx = v1.x - v2.x;
float dy = v1.y - v2.y;
float dz = v1.z - v2.z;
return (float) Math.sqrt(dx*dx + dy*dy + dz*dz);
}
}
boolean countryExists(String tempCountryHolder) {
for (int i = 0; i < destCountryArray.length; i++) {
//println("comparing '" +tempCountryHolder +"' with '"+destCountryArray[i] + "'");
if (tempCountryHolder.equals(destCountryArray[i]) == true) {
//println("found : "+ tempCountryHolder);
return true; // it exists
} //if
} //for
return false ; // not found
}//func
answer was: hashmap of hashmaps
HashMap<String, HashMap> countries = new HashMap<String, HashMap>();
for (int i=0; i<destCountryArray.length; i++) {
HashMap<String, Float> country = new HashMap<String, Float>();
countries.put(destCountryArray[i], country);
country.put("x", i*20.0);
country.put("y", i*40.0);
}
for (Map.Entry me : countries.entrySet()) {
print(me.getKey() + " is ");
println(me.getValue());
}
You probably don't want to use a nested HashMap like your answer suggests. You probably want to use your own class instead. Something like this:
HashMap<String, Point> countries = new HashMap<String, Point>();
void setup(){
countries.put("one", new Point(2, 3));
countries.put("four", new Point(5, 6));
}
void draw(){
Point pOne = countries.get("one");
point(pOne.x, pOne.y);
}
class Point{
float x;
float y;
public Point(float x, float y){
this.x = x;
this.y = y;
}
}
Or better yet, you could just use Processing's PVector class, which already represents points:
HashMap<String, PVector> countries = new HashMap<String, PVector>();
void setup(){
countries.put("one", new PVector(2, 3));
countries.put("four", new PVector(5, 6));
}
void draw(){
PVector pOne = countries.get("one");
point(pOne.x, pOne.y);
}
More info can be found in the reference. Shameless self-promotion: here is a tutorial on using classes, and here is a tutorial on creating your own classes.

how can I achieve the the effect that growing a set of curves and when any curve occurs others' trail, stop it's growth

i want to achieve like this:
enter image description here
here is my code:
class Particle {
PVector position;
PVector velocity;
PVector acceleration;
PVector initialVelocity;
ArrayList<PVector> path;
boolean life = true;
float q;
public Particle(PVector position, PVector initialVelocity) {
this.position = position;
this.acceleration = new PVector(0, 0);
this.initialVelocity = initialVelocity;
this.velocity = new PVector(0, 0);
path = new ArrayList<PVector>();
}
public void run() {
update();
display();
}
private void update() {
velocity.add(acceleration);
velocity.limit(maxspeed);
trail.add(position.copy());
position.add(velocity);
occurOtherCurves();
arriveBorders();
path.add(position.copy());
acceleration.mult(0);
}
private void display() {
beginShape();
stroke(0);
strokeWeight(1);
noFill();
for(PVector v : path) {
vertex(v.x, v.y);
}
}
public void followField(Field field) {
PVector desired = field.lookup(initialVelocity);
desired.mult(maxspeed);
PVector steer = PVector.sub(desired, velocity);
steer.limit(maxforce);
applyForce(steer);
}
private void applyForce(PVector force) {
acceleration.add(force);
}
public boolean isEndMove(){
return !life;
}
private void arriveBorders(){
if(position.x <= 0 || position.x >= width || position.y <= 0 || position.y >= height)
life = false;
}
private void occurOtherCurves() {
for(PVector p : trail) {
if(p.x == position.x && p.y == position.y) life = false;
}
}
public ArrayList<PVector> getPath() {
return path;
}
}
in occurOtherCurves() function ,ArrayList<PVector> trail is a global variable used to record the positions that all particles passed through,but it didn't work.
thanks for you help.
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.
But generally, it sounds like what you'd want to do is check whether a new path segment intersects with any existing path segments. If it does, then truncate it at the intersection point (or just don't add it) and stop adding path segments to that path.
You'll have much better luck if you break your problem down into smaller pieces, and take on one piece at a time. Then if you get stuck, you can post a MCVE along with a specific technical question. Good luck.

Moving a shape and having the other one follow it

I am trying to draw a number of shapes in processing which react to each others movements. So when I move one of them, it is connected to say 1 or 2 of the other shapes in the sketch. However I don't want it to be a static movement, I want it to look quite natural.
The only code I have at the moment is basic drawing of a shape which moves when you drag it, but have no idea how to link shapes :( Help!!
float bx;
float by;
int boxSize = 75;
boolean overBox = false;
boolean locked = false;
float xOffset = 0.0;
float yOffset = 0.0;
void setup()
{
size(640, 360);
bx = width/2.0;
by = height/2.0;
rectMode(RADIUS);
}
void draw()
{
background(0);
// Test if the cursor is over the box
if (mouseX > bx-boxSize && mouseX < bx+boxSize &&
mouseY > by-boxSize && mouseY < by+boxSize) {
overBox = true;
}
// Draw the box
rect(bx, by, boxSize, boxSize);
}
void mousePressed() {
if(overBox) {
locked = true;
} else {
locked = false;
}
xOffset = mouseX-bx;
yOffset = mouseY-by;
}
void mouseDragged() {
if(locked) {
bx = mouseX-xOffset;
by = mouseY-yOffset;
}
}
void mouseReleased() {
locked = false;
}
I am thinking I need to use PShape, but am unsure.
First of all, I recommend you to use a class for the shapes, that way you can link them easily.
I make some changes in your code in order to get some kind of following behaviour.
class Box{
float bx;
float by;
int size = 75;
Box(float bx, float by){
this.bx = bx;
this.by = by;
}
void drawBox(){
rect(bx, by, size, size);
}
void moveBox(float x, float y){
bx = bx + x;
by = by + y;
}
}
boolean overBox = false;
boolean locked = false;
float xOffset = 0.0;
float yOffset = 0.0;
Box box, secondBox, thirdBox;
void setup()
{
size(640, 360);
box = new Box(width/2.0, height/2.0);
secondBox = new Box(100, 100);
thirdBox = new Box(500, 300);
rectMode(RADIUS);
}
void draw()
{
background(0);
// Test if the cursor is over the box
if (mouseX > box.bx-box.size && mouseX < box.bx+box.size &&
mouseY > box.by-box.size && mouseY < box.by+box.size) {
overBox = true;
box.drawBox();
secondBox.drawBox();
thirdBox.drawBox();
}
}
void mousePressed() {
if(overBox) {
locked = true;
} else {
locked = false;
}
xOffset = mouseX-box.bx;
yOffset = mouseY-box.by;
}
void mouseDragged() {
if(locked) {
box.bx = mouseX-xOffset;
box.by = mouseY-yOffset;
deltaMovemente(box, secondBox);
deltaMovemente(box, thirdBox);
}
}
void mouseReleased() {
locked = false;
}
void deltaMovemente(Box follow, Box box){
float dx = (follow.bx - box.bx)/50;
float dy = (follow.by - box.by)/50;
box.moveBox(dx, dy);
}
Hope this can be useful.
Regards.

Resources