Particles

Particles are a simple way to get curves. Simulation with Springs|Particles|Attractions might be useful to give a sense of physicality. Particles Systems won't give you accurate result, but might be useful to resolve and minimize geometrical problems. They might gives as well, an idea of the structural behavior of your design, but you need to be aware that the result are absolutely not accurate and should not be taken as is. To get more accurate results, you may need to use fancy Finite Element Methods (FEM) to solve your problem. This is intense calculation and are usually not done interactively.

Traer

A simple particle system physics engine for processing. I've designed this to be application / domain agnostic. All this is supposed to do is let you make particles, apply forces and calculate the positions of particles over time in real-time. Anything else you need to handle yourself.

Jeffrey TRAER did a library for particle systems which is simple and used commonly with processing. The library wasn't working with ANAR+ and we did a binding with to be able to use TRAER physics library with ANAR+. The only change is that ANAR.Pt are associated to a Traer particle. If a geometrical construction is based on a point (using this lib), all geometric associativity is preserved.

Download

The library include Traer physics. (you don't have to install Traer Library separately). The library is located here:

  1. TraerAnarV2.zip for ANAR V20+

Installation

Download and unzip the cocntent inside your sketchFolder/library folder.

Basic

import anar.*;
import processing.opengl.*;
import traer.physics.*;
import traer.anar.*;
 
Sim sim = new Sim(0,0.1f);
Obj world = new Obj();
 
public void setup(){
  size(1000,500,OPENGL);
  Anar.init(this);
  createSimulation();
  Pt.globalRender = new RenderPtShapeOriented(new FatCross(15),new AColor(100),new AColor(255,155),Anar.scene);
}
 
void createSimulation(){
  world.add(new PowerPt(Anar.PtRnd(100)).fixed());
  PowerAttractor.globalForce(world,2,0.1f);
}
 
int nParticle = 30;
 
public void draw(){
  background(155);
  sim.updateSim();
  sim.param.draw();
  if ((frameCount%10==0)&&(frameCount<nParticle*10)) {
    PowerPt p = new PowerPt(Anar.PtRnd(100));
    world.add(p);
    PowerAttractor.globalForce(world,10,0.1f);
  }
  world.draw();
}
 
public void keyPressed(){
  if(key=='s')
    sim.simulate = sim.simulate ? false:true;
  if(key=='p')
    save("screenshot50.jpg");
  if(key==' ')
    createSimulation();   
}

anar.ch_img_particles_screenshot50.jpg

First Example

For our first example, we will create a superStructure. The points are generated randomly, but the general form will reach a stable form after few steps. This example shows the basic of the spring particles.

  1. PowerPt(Pt) is a particle.
  2. PowerSpring(Pt,Pt,restLength) is a spring
  3. PowerAttractor(Pt,Pt,strenght) is an attractor.
import anar.*;
import processing.opengl.*;
import traer.physics.*;
import traer.anar.*;
 
Sim sim;
Obj architectureDuFutur;
 
void setup(){
  size(1000,500,OPENGL);
  Anar.init(this);
  Anar.drawAxis(true);
  Pts.globalRender = new RenderPtsAll();  //Initialize a render for all lines
  Pt.globalRender = new RenderPtShapeConstant(new FatCross(15),new AColor(100),new AColor(255,155),Anar.scene);
  createSimulation();
}
 
void createSimulation(){
  //Initialization
  sim = new Sim(0,0.1f);
  architectureDuFutur = new Obj();
  Pts pts = new Pts();
 
  //Create Random Points
  int r = 10;
  for(int i=0; i<100; i++)
    pts.add(new PowerPt(random(-r,r),random(-r,r),random(-r,r)));
 
  //Create Random Connections
  for(int i=0; i<150; i++)
  {
    PowerPt a = (PowerPt)pts.pt(Anar.rndi(pts.numOfPts()));
    PowerPt b = (PowerPt)pts.pt(Anar.rndi(pts.numOfPts()));
    if(a!=b)
      architectureDuFutur.add(new PowerSpring(a,b));
  }
 
  //Apply a Repulsor force on each pair of nodes
  for(int i=0; i<pts.size(); i++)
    for(int j=0; j<pts.size(); j++)
    {
      Pt a = pts.get(i);
      Pt b = pts.get(j);
      if(a!=b)
          new PowerAttractor(((PowerPt)a),((PowerPt)b),-100,0.1);
    }
}
 
void draw(){
  background(150);
  sim.updateSim(); // Update the simulation
  sim.param.draw(); // Draw the sliders
  architectureDuFutur.draw(); // Draw our objects
  Anar.camTarget(architectureDuFutur); // Center the object to the scene
}
 
void keyPressed(){
  if(key=='s')
    sim.simulate = sim.simulate ? false:true;
  if(key=='p')
    save("screenshot01.jpg");
  if(key==' ')
    createSimulation();    
}

anar.ch_img_particles_screenshot00.jpg anar.ch_img_particles_screenshot01.jpg

Second Example

For the second example, we will use a design more of the fifties.

import anar.*;
import processing.opengl.*;
import traer.physics.*;
import traer.anar.*;
 
Sim sim = new Sim(1,0.1f);
Obj mesh;
 
void setup(){
  size(1000,500,OPENGL);
  Anar.init(this);
  Anar.drawAxis(true);
  Pts.globalRender = new RenderPtsAll();
  createSimulation();
  sim.useGlobalAttractorValue = true;
}
 
void createSimulation(){
  PowerPt[][] particles = new PowerPt[10][10];
 
  Obj grid = new Obj();
 
  //Create Particles (in Grid)
  for(int i=0; i<particles.length; i++)
    for(int j=0; j<particles[i].length; j++)
      particles[i][j] = new PowerPt(i*10,j*10,0);
 
  //Create Horizontal Lines
  for(int i=0; i<particles.length; i++)
  {
    Pts p = new Pts();
    for(int j=0; j<particles[i].length; j++)   
      if(i%4==0&&j%3==0)
        p.add(particles[i][j].fixed());
      else
        p.add(particles[i][j]);
 
    grid.add(p);
  }
 
  // Create Vertical Lines
  for (int j = 0; j<particles[0].length; j++){
    Pts p = new Pts();
    for (int i = 0; i<particles.length; i++)
      p.add(particles[i][j]);
    grid.add(p);
  }
 
  mesh=PowerSpring.makeFrom(grid);
  PowerAttractor.globalForce(grid,-200,0.1f);
}
 
void draw(){
  background(155);
  sim.updateSim();
  sim.param.draw();
  mesh.draw();
  Anar.camTarget(mesh);
}
 
void keyPressed(){
  if(key=='s')
    sim.simulate = sim.simulate ? false:true;
  if(key=='p')
    save("screenshot02.jpg");
  if(key==' ')
    createSimulation();    
}

anar.ch_img_particles_screenshot02.jpg anar.ch_img_particles_screenshot03.jpg anar.ch_img_particles_screenshot04.jpg

With Constrains

import anar.*;
import processing.opengl.*;
import traer.physics.*;
import traer.anar.*;
 
Sim sim = new Sim(1,0.1f);
Obj mesh;
 
void setup(){
  size(1000,500,OPENGL);
  Anar.init(this);
  Anar.drawAxis(true);
  createSimulation();
 
  sim.traer.setIntegrator(ParticleSystem.VERLET);
}
 
void createSimulation(){
  PowerPt[][] particles = new PowerPt[10][10];
 
 
  Obj grid = new Obj();
 
  //Create Particles (in Grid)
  for(int i=0; i<particles.length; i++)
    for(int j=0; j<particles[i].length; j++)
      particles[i][j] = new PowerPt(i*10,j*10,0);
 
  //Create Horizontal Lines
  for(int i=0; i<particles.length; i++)
  {
    Pts p = new Pts();
    for(int j=0; j<particles[i].length; j++)   
      if(i%4==0&&j%3==0)
        p.add(particles[i][j].fixed());
      else
        p.add(particles[i][j]);
 
    grid.add(p);
  }
 
  // Create Vertical Lines
  for (int j = 0; j<particles[0].length; j++){
    Pts p = new Pts();
    for (int i = 0; i<particles.length; i++)
      p.add(particles[i][j]);
    grid.add(p);
  }
 
  mesh=PowerSpring.makeFrom(grid);
 
  // add constraints
  for (int i=0; i<6; i++) {
    int l1 = Anar.rndi(mesh.numOfLines());
    int l2 = Anar.rndi(mesh.numOfLines());
    PowerPt a = (PowerPt) mesh.line(l1).pt(Anar.rndi(mesh.line(l1).numOfPts())); 
    PowerPt b = (PowerPt) mesh.line(l2).pt(Anar.rndi(mesh.line(l2).numOfPts()));
    mesh.add(new PowerSpring( a, b, 0f, 0f, .8f*a.length(b)).stroke(255,0,0));
  }
 
  PowerAttractor.globalForce(grid,-200,0.1f);
}
 
void draw(){
  background(155);
  sim.updateSim();
  sim.param.draw();
  mesh.draw();
  Anar.camTarget(mesh);
}
 
void keyPressed(){
  if(key=='s')
    sim.simulate = sim.simulate ? false:true;
  if(key=='p')
    save("screenshot20.jpg");
  if(key==' ')
    createSimulation();   
}

anar.ch_img_particles_screenshot20.jpg

Contraints with a Dome

import anar.*;
import processing.opengl.*;
import traer.physics.*;
import traer.anar.*;
 
Sim sim;
Obj constrainedDome;
 
void setup(){
  size(1000,500,OPENGL);
  Anar.init(this);
  Anar.drawAxis(true);
  Pts.globalRender = new RenderPtsAll();  //Initialize a render for all lines
  createSimulation();
}
 
public void createSimulation(){
  constrainedDome = new Obj();
  sim = new Sim(1,0.1f);
 
  sim.traer.setIntegrator(ParticleSystem.VERLET);
  Face f = new Rect(100,66);
  f.translateZ(1);
 
  int nDiv = 11;
 
  //Divide the distance on two sides of the rectangle
  Pts segment1 = new PtsMid(f.pt(0),f.pt(1),nDiv);
  Pts segment2 = new PtsMid(f.pt(3),f.pt(2),nDiv);
  //We then have two sets with the same number of points
 
  // we draw lines of points between the two sets
  for (int i=0; i<segment1.numOfPts(); i++) {
    constrainedDome.add(PowerSpring.makeFrom(new PtsMid(segment1.pt(i),segment2.pt(i),10)));
    ((PowerPt) constrainedDome.line(i).pt(0)).fixed();
    ((PowerPt) constrainedDome.line(i).ptEnd()).fixed();
 
  }
 
 
  Obj constrains = new Obj();
  // we draw constrains between even lines
  for (int i=0; i<constrainedDome.numOfLines()-1; i++) {
    for (int j=1; j<constrainedDome.line(0).numOfPts()-1; j++) { 
      PowerPt a = (PowerPt) constrainedDome.line(i).pt(j); 
      PowerPt b = (PowerPt) constrainedDome.line(i+1).pt(j);
      if (i==0) a.fixed();
      if (i+1==constrainedDome.numOfLines()-1) b.fixed();
      if (i%2==1) constrains.add(new PowerSpring( a, b, 0f, 0f, 2f*a.length(b)).stroke(255,0,0));
      else constrains.add(new PowerSpring( a, b, PowerSpring.defaultStrength, 20*PowerSpring.defaultDamping, 0f).stroke(255,255,0));
 
    }
  }    
  constrainedDome.add(constrains);
 
}
 
void draw(){
  background(155);
  sim.updateSim(); // Update the simulation
  sim.param.draw(); // Draw the sliders
  constrainedDome.draw(); // Draw our objects
  Anar.camTarget(constrainedDome); // Center the object to the scene
}
 
void keyPressed(){
  if(key=='s')
    sim.simulate = sim.simulate ? false:true;
  if(key=='p')
    save("screenshot40.jpg");
  if(key==' ')
    createSimulation();     
}

anar.ch_img_particles_screenshot40.jpg

Minimal Surfaces

There is different way to use particle systems. You may think to a problem such as minimal surfaces. In this case all the springs are initial distance of 0, according to each elements, they will tends to findd the minimal surface for something that might difficult to predict.

We begin with a distorted shape (non-plannar surface). We divide this face into a set of broken segments. Those points will be fixed and won't move during the simulation.

import anar.*;
import processing.opengl.*;
import traer.physics.*;
import traer.anar.*;
 
Sim sim;
Obj minimalSurface;
 
void setup(){
  size(1000,500,OPENGL);
  Anar.init(this);
  Anar.drawAxis(true);
  Pts.globalRender = new RenderPtsAll();  //Initialize a render for all lines
  createSimulation();
}
 
void createSimulation(){
  minimalSurface = new Obj();
  sim = new Sim(0,0.1f);
  Face f = new Rect(100,66);
  f.pt(0).translateZ(33).translateX(33);
 
  //Divide the distance on two sides of the rectangle
  Pts segment1 = new PtsMid(f.pt(0),f.pt(1),10);
  Pts segment2 = new PtsMid(f.pt(3),f.pt(2),10);
  //We then have two sets with the same number of points
 
  //We create an array of PowerPt
  PowerPt[][] nodes = new PowerPt[segment1.numOfPts()][10];
 
  // Init the points somewhere in space
  for (int i = 0; i<nodes.length; i++)
    for (int j = 0; j<nodes[i].length; j++)
      if(j==0)
        nodes[i][j] = new PowerPt(segment1.pt(i)).fixed();
      else
        if(j==nodes[i].length-1)
          nodes[i][j] = new PowerPt(segment2.pt(i)).fixed();
        else
          nodes[i][j] = new PowerPt(Anar.PtRnd(100));
 
  //Create Springs between lines in one direction
  for (int i = 1; i<nodes.length; i++)
    for (int j = 0; j<nodes[i].length; j++)
      minimalSurface.add(new PowerSpring(nodes[i-1][j],nodes[i][j],0));
 
  //Create Springs between lines in the other direction
  for (int i = 0; i<nodes.length; i++)
    for (int j = 1; j<nodes[i].length; j++)
      minimalSurface.add(new PowerSpring(nodes[i][j-1],nodes[i][j],0));    
}
 
void draw(){
  background(155);
  sim.updateSim(); // Update the simulation
  sim.param.draw(); // Draw the sliders
  minimalSurface.draw(); // Draw our objects
  Anar.camTarget(minimalSurface); // Center the object to the scene
}
 
void keyPressed(){
  if(key=='s')
    sim.simulate = sim.simulate ? false:true;
  if(key=='p')
    save("screenshot12.jpg");
  if(key==' ')
    createSimulation();    
}

anar.ch_img_particles_screenshot11.jpg anar.ch_img_particles_screenshot12.jpg anar.ch_img_particles_screenshot13.jpg anar.ch_img_particles_screenshot14.jpg

Minimal Cylinder

import anar.*;
import processing.opengl.*;
import traer.physics.*;
import traer.anar.*;
 
Sim sim;
Obj minimalSurface;
 
void setup(){
  size(1000,500,OPENGL);
  Anar.init(this);
  Anar.drawAxis(true);
  Pts.globalRender = new RenderPtsAll();  //Initialize a render for all lines
  createSimulation();
}
 
void createSimulation(){
  minimalSurface = new Obj();
  sim = new Sim(0,0.1f);
 
  Face f = new Rect(100,66);
  Face ff = f.copy().rotateZ(1).translateZ(100);
 
  Pts borderF = new Pts();
  Pts borderFF = new Pts();
  //Divide the distance on all sides of both rectangles
  for (int i=0; i<4; i++) {
    borderF.add(new PtsMid(f.ptMod(i),f.ptMod(i+1),10));
    borderFF.add(new PtsMid(ff.ptMod(i),ff.ptMod(i+1),10));
  }
  //close the loop
  borderF.remove(borderF.ptEnd());
  borderFF.remove(borderFF.ptEnd());
  borderF.add(borderF.pt(0));
  borderFF.add(borderFF.pt(0));
 
  //We then have two sets with the same number of points
 
  //We create an array of PowerPt
  PowerPt[][] nodes = new PowerPt[borderF.numOfPts()][10];
 
  // Init the points somewhere in space
  for (int i = 0; i<nodes.length; i++)
    for (int j = 0; j<nodes[i].length; j++)
      if (i==nodes.length-1)
        nodes[i][j] = nodes[0][j];
      else 
        if(j==0)
        nodes[i][j] = new PowerPt(borderF.pt(i)).fixed();
      else
        if(j==nodes[i].length-1)
          nodes[i][j] = new PowerPt(borderFF.pt(i)).fixed();
        else
          nodes[i][j] = new PowerPt(Anar.PtRnd(100));
 
  //Create Springs between lines in one direction
  for (int i = 1; i<nodes.length; i++)
    for (int j = 0; j<nodes[i].length; j++)
      minimalSurface.add(new PowerSpring(nodes[i-1][j],nodes[i][j],0));
 
  //Create Springs between lines in the other direction
  for (int i = 0; i<nodes.length; i++)
    for (int j = 1; j<nodes[i].length; j++)
      minimalSurface.add(new PowerSpring(nodes[i][j-1],nodes[i][j],0));    
}
 
void draw(){
  background(155);
  sim.updateSim(); // Update the simulation
  sim.param.draw(); // Draw the sliders
  minimalSurface.draw(); // Draw our objects
  Anar.camTarget(minimalSurface); // Center the object to the scene
}
 
public void keyPressed(){
  if(key=='s')
    sim.simulate = sim.simulate ? false:true;
  if(key=='p')
    save("screenshot30.jpg");
  if(key==' ')
    createSimulation();   
}

anar.ch_img_particles_screenshot30.jpg