Hi friends! I am trying to draw an array of curves from PVector coordinates.
Basically it is similar to Particle system tutorial, just instead of points, I need to get curves.
I try to use begin shape (in class Particle), storing x,y coordinates in arraylists, but I am getting an error while iterating over the ArrayList (NullPointException).
Heeeeeeeeeeeeeeeeeeeelp :[
ArrayList<ParticleSystem> systems;
void setup() {
size(640, 360);
systems = new ArrayList<ParticleSystem>();
}
void draw() {
background(0);
for (ParticleSystem ps : systems) {
ps.run();
ps.addParticle();
}
if (systems.isEmpty()) {
fill(255,255,0);
}
}
void mousePressed() {
systems.add(new ParticleSystem(1, new PVector(mouseX, mouseY)));
}
// A simple Particle class
class Particle {
PVector position;
//ArrayList<PVector> position;
PVector velocity;
float vector_scale;
float lifespan;
FloatList xx;
FloatList yy;
ArrayList<FloatList> old_x;
ArrayList<FloatList> old_y;
Particle(PVector l) {
float vector_scale = 0.05;//ector scaling factor, we want small steps
FloatList xx = new FloatList();
FloatList yy = new FloatList();
ArrayList<FloatList> old_x = new ArrayList<FloatList>();
ArrayList<FloatList> old_y = new ArrayList<FloatList>();
// v is vector from the field
float n = 3 * map(noise(l.x/155,l.y/155),0,1,-1,1); // 100, 300 or 1000
velocity = new PVector(cos(n),sin(n));
l.x += vector_scale * velocity.x;
l.y += vector_scale * velocity.y;
//velocity = new PVector(random(-1, 1), random(-2, 0)); //moving vector
position = l.copy();
lifespan = 100.0;
}
void run() {
update();
display();
}
// Method to update position
void update() {
position.add(velocity);
lifespan -= 1.0;
}
// Method to display
void display() {
strokeWeight(2);
stroke(255, lifespan);
beginShape();
for (int i = xx.size()-1; i <= 0; i--) {
curveVertex(xx.get(i), yy.get(i));
}
endShape();
// populate the x and y lists
xx.append(position.x);
yy.append(position.y);
}
// Is the particle still useful?
boolean isDead() {
return (lifespan < 0.0);
}
}
// An ArrayList is used to manage the list of Particles
class ParticleSystem {
ArrayList<Particle> particles; // An arraylist for all the particles
PVector origin; // An origin point for where particles are birthed
ParticleSystem(int num, PVector v) {
particles = new ArrayList<Particle>(); // Initialize the arraylist
origin = v.copy();
// Store the origin point
for (int i = 0; i < num; i++) {
particles.add(new Particle(origin)); // Add "num" amount of particles to the arraylist
}
}
void run() {
// Cycle through the ArrayList backwards, because we are deleting while iterating
for (int i = particles.size()-1; i >= 0; i--) {
Particle p = particles.get(i);
p.run();
//println(XXorigin.get(i));
stroke(255,0,0);
strokeWeight(8);
strokeJoin(ROUND);
//point(XXorigin.get(i), YYorigin.get(i));
//line(XXorigin.get(0), YYorigin.get(0), XXorigin.get(i), YYorigin.get(i));
if (p.isDead()) {
particles.remove(i);
}
}
}
void addParticle() {
Particle p;
// Add either a Particle or CrazyParticle to the system
p = new Particle(origin);
particles.add(p);
}
void addParticle(Particle p) {
particles.add(p);
}
// A method to test if the particle system still has particles
boolean dead() {
return particles.isEmpty();
}
}
for (int i = 0; i < xx.size(); i++) {
curveVertex(xx.get(i), yy.get(i));
}
And it started to work!
Thus, adding every new curve slows down the process by a lot!
Also it produces a new curve form the starting point to every new coordinate. What I want is to draw 1 curve from the beginning, following every new coordinate. And repeat that for every new origin I add.
Maybe I need to do that in ParticleSystem Class instead? Or something wrong with the for loop?
Your code is wonderful, but has totally different structure than mine.
Could you please explain it a bit?
For instance, if I want to map a big noise field to draw it on, or an image to follow, how can I apply it with this code?
I tried to do that here, but it results in particles moving in circles.
//add some wander to the particle to make it more interesting
float heading = 2 * map(noise(this.acc.x/500,this.acc.y/500),0,1,-1,1);
this.vel = new PVector(cos(heading),sin(heading));
this.addForce(cos(heading), sin(heading));
you basically have it correct. i was being lazy though and really the movement code should be removed from the particle update and instead the particles should be fed a force using the addForce method from outside. this whole thing follows Daniel’s work closely (for good reason ) so it might be worth checking out this video
and he might have actually made an updated version of that as well.
further you can read more on his book The Nature of Code website here
Thank you again so much
I am shy to ask more, you already helped a lot…
But I guess, if you say
Then my main question is how can I put data from particles.add into run() function, without adding new particles?
I guess the answer is somewhere in using “.this”?
class ParticleSystem {
ArrayList<Particle> particles; // An arraylist for all the particles
PVector origin; // An origin point for where particles are birthed
FloatList xx;
FloatList yy;
ParticleSystem(int num, PVector v) {
particles = new ArrayList<Particle>(); // Initialize the arraylist
origin = v.copy(); // Store the origin point
xx = new FloatList();
yy = new FloatList();
for (int i = 0; i < num; i++) {
particles.add(new Particle(origin)); // Add "num" amount of particles to the arraylist
}
}
void run() {
// Use origins of created particles here, witout creating new particles??
}
the curve is made up of many line segments. each of which is created using pairs of vertices.
here i’ve increased the line segment length by increasing the distance between vertices. i have also highlighted each vertex with an ellipse.
ArrayList<Particle> particles;
void setup() {
size(640, 480);
noFill();
strokeWeight(2);
particles = new ArrayList<Particle>();
}
void draw() {
background(255);
for(int i = particles.size() - 1; i >= 0; i--) {
Particle p = particles.get(i);
p.update();
//if(p.pos.x < -100 || p.pos.x > width+100 || p.pos.y < -100 || p.pos.y > height+100)
//particles.remove(i);
p.present();
}
}
void mousePressed() {
Particle p = new Particle(mouseX, mouseY);
p.addForce(random(-1, 1), random(-1, 1));
particles.add(p);
}
class Particle {
PVector pos;
PVector vel;
PVector acc;
ArrayList<PVector> history;
int maxHistory;
float maxWander;
float maxSpeed;
Particle(float x, float y) {
this.pos = new PVector(x, y);
this.vel = new PVector();
this.acc = new PVector();
this.history = new ArrayList<PVector>();
this.history.add(this.pos.copy());
this.maxHistory = 50;
this.maxWander = 45 * (PI / 180);
this.maxSpeed = 2;
}
void addForce(float fx, float fy) {
this.acc.x += fx;
this.acc.y += fy;
}
void update() {
//add some wander to the particle to make it more interesting
float heading = this.vel.heading();
heading += random(-this.maxWander, this.maxWander);
this.addForce(cos(heading), sin(heading));
this.vel.add(this.acc);
this.vel.limit(this.maxSpeed);
this.acc.mult(0);
this.pos.add(this.vel);
float dist = PVector.dist(this.pos, this.history.get(this.history.size() - 1));
//this controls the length of the line segments or the resolution if you like of the curve which is made up of line segments
if(dist > 25) {
this.history.add(this.pos.copy());
if(this.history.size() > this.maxHistory) {
this.history.remove(0);
}
}
}
void present() {
float k = (float)this.history.size();
for(int i = 1; i < this.history.size(); i++) {
stroke(0, (i / k) * 255);
PVector p1 = this.history.get(i - 1);
PVector p2 = this.history.get(i);
ellipse(p2.x, p2.y, 8, 8);
line(p1.x, p1.y, p2.x, p2.y);
}
}
}
you can see the resolution of the curve is controlled by the distance between each vertex and the smaller the distance the smoother the curve. i feel like there is a misunderstanding on one or both of our ends. i cannot see where you are calculating that there is multiple lines drawn. if you could demonstrate how you arrive at that conclusion i can look further into it but until then i am unsure if there is an issue.
perhaps someone else will weigh in with a solution?