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.
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.
The library include Traer physics. (you don't have to install Traer Library separately). The library is located here:
Download and unzip the cocntent inside your sketchFolder/library folder.
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(); }
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.
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(); }
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(); }
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(); }
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(); }
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(); }
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(); }