Wave movement in processing We want to simulate a wave movement based on the up-and-down of a grid of balls. I have imagined of two kinds of ways that we can interact with our wave grid: -Point based control: The wave is simulated starting from a point, and moves towards all directions from that very point. We will gonna control the position of the point that affects the grid, by mouse position, or Leap Motion gesture position. -Line based control: The wave grid is affected by a line, and moves in the vertical direction of the line. The line is adjusted by our mouse position or Leap Motion gesture position.
- The grid of balls
We will write a class for the grid. They are the basic class that we will simulate the movement of grid. I have used IGeo Vector library here.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
//import the Igeo library which we will use its vector class import igeo.IVec2; import igeo.IVec; //clarify a class named grid class grid { //clarify a vector called v, which represents the position of this grid node IVec v; grid(){ } //the construction function of grid grid(IVec v){ this.v = v; } //another construction function of grid grid(double x,double y, double z){ this.v = new IVec(x,y,z); } //the function to change the z position of the grid node void setZ(double z){ this.v.z = z; } //the getter for position IVec location(){ return this.v; } //the getter for position 2d IVec2 location2d(){ return this.v.to2d(); } //the function to draw 2d void draw(PApplet app){ pushStyle(); stroke(0); strokeWeight(PApplet.map((float)this.v.z, -100, 100, 0, 10)); point((float)v.x, (float)v.y); popStyle(); } //the function to draw 3d void draw3d(PApplet app){ pushStyle(); stroke(0); // strokeWeight(PApplet.map((float)this.v.z, -10, 10, 0, 10)); //float zz = PApplet.map((float)v.z, start1, stop1, start2, stop2) line((float)v.x, (float)v.y,0,(float)v.x, (float)v.y,(float)v.z+100); popStyle(); } } |
- Control the grid by point
Here I will write a class that functions as a point field that can affect the position of the grid.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
//the class that will be used to control the condition of //grid nodes class ptWave { IVec dir=new IVec(0, 1, 0); //direction of wave movement, line is vertival to that double L=50;//how long the length of a wava is double phase=0;//current location double S=100;//how high the wave is boolean cir=false;//is it a one time wave or a circular wave IVec pos = new IVec();//position of the wave point ptWave() { } void setLocation(double x, double y) { this.pos.x = x; this.pos.y = y; } void time(double i) { this.phase+=i; } double zLocation(IVec2 v) { double dis = this.dist(v); return Math.cos(dis/this.L-this.phase)*this.S; } void affect(grid[][] gs) { for (grid[] gg : gs) { for (grid g : gg) { g.setZ(this.zLocation(g.location2d())); } } } double dist(IVec2 v) { return this.location2d().dist(v); } double dist(grid g) { return this.location2d().dist(g.location2d()); } IVec location() { return this.pos; } IVec2 location2d() { return this.pos.to2d(); } void draw() { pushStyle(); stroke(255, 0, 0); fill(255, 0, 0); ellipse((float)this.pos.x, (float)this.pos.y, 10, 10); popStyle(); } } |
For this point field, it is capable of calculating the distance to each ball in the grid, and thus change the position based on the distance, through the calculation of the cos value based on the distance, the wave distance, the phase, and the circulation.
- Control the grid by Line
Here I will write a class that functions as a line field that can affect the position of the grid. It is based on a line2d class that can help me do some geometry calculation.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
import processing.core.PApplet; import igeo.IVec2; class Line2d { double A, B, C; IVec2 a, b; Line2d() { } Line2d(IVec2 a, IVec2 b) { this.a = a; this.b = b; this.A = a.y - b.y; this.B = b.x - a.x; this.C = a.x * b.y - a.y * b.x; } Line2d(double A, double B, double C) { this.A = A; this.B = B; this.C = C; } IVec2 VerticalPoint(IVec2 p) { IVec2 v = new IVec2(); double tmp = A * A + B * B; v.x = (B * B * p.x - A * B * p.y - A * C) / tmp; v.y = (A * A * p.y - A * B * p.x - B * C) / tmp; return v; } boolean contain(IVec2 p) { // judge edge contain p. on edge doesn't count as contain if (Math.abs(A * p.x + B * p.y + C) > 0.0001) return false; else { if (this.a.eq(p) || this.b.eq(p)) return false; else { IVec2 ab = b.dup().sub(a); IVec2 comp = p.dup().sub(this.a); if (comp.x / ab.x > 1 || comp.x / ab.x < 0) return false; else return true; } } } int GetIntersection(IVec2 a, IVec2 b, IVec2 c, IVec2 d) { IVec2 intersection = new IVec2(0, 0); if (Math.abs(b.y - a.y) + Math.abs(b.x - a.x) + Math.abs(d.y - c.y) + Math.abs(d.x - c.x) == 0) { if ((c.x - a.x) + (c.y - a.y) == 0) { // System.out.println("ABCDÊÇͬһ¸öµã£¡"); } else { // System.out.println("ABÊÇÒ»¸öµã£¬CDÊÇÒ»¸öµã£¬ÇÒAC²»Í¬£¡"); } return 0; } if (Math.abs(b.y - a.y) + Math.abs(b.x - a.x) == 0) { if ((a.x - d.x) * (c.y - d.y) - (a.y - d.y) * (c.x - d.x) == 0) { // System.out.println("A¡¢BÊÇÒ»¸öµã£¬ÇÒÔÚCDÏ߶ÎÉÏ£¡"); } else { // System.out.println("A¡¢BÊÇÒ»¸öµã£¬ÇÒ²»ÔÚCDÏ߶ÎÉÏ£¡"); } return 0; } if (Math.abs(d.y - c.y) + Math.abs(d.x - c.x) == 0) { if ((d.x - b.x) * (a.y - b.y) - (d.y - b.y) * (a.x - b.x) == 0) { // System.out.println("C¡¢DÊÇÒ»¸öµã£¬ÇÒÔÚABÏ߶ÎÉÏ£¡"); } else { // System.out.println("C¡¢DÊÇÒ»¸öµã£¬ÇÒ²»ÔÚABÏ߶ÎÉÏ£¡"); } return 0; } if ((b.y - a.y) * (c.x - d.x) - (b.x - a.x) * (c.y - d.y) == 0) { // System.out.println("Ï߶ÎƽÐУ¬ÎÞ½»µã£¡"); return 0; } intersection.x = ((b.x - a.x) * (c.x - d.x) * (c.y - a.y) - c.x * (b.x - a.x) * (c.y - d.y) + a.x * (b.y - a.y) * (c.x - d.x)) / ((b.y - a.y) * (c.x - d.x) - (b.x - a.x) * (c.y - d.y)); intersection.y = ((b.y - a.y) * (c.y - d.y) * (c.x - a.x) - c.y * (b.y - a.y) * (c.x - d.x) + a.y * (b.x - a.x) * (c.y - d.y)) / ((b.x - a.x) * (c.y - d.y) - (b.y - a.y) * (c.x - d.x)); if ((intersection.x - a.x) * (intersection.x - b.x) <= 0 && (intersection.x - c.x) * (intersection.x - d.x) <= 0 && (intersection.y - a.y) * (intersection.y - b.y) <= 0 && (intersection.y - c.y) * (intersection.y - d.y) <= 0) { // System.out.println("Ï߶ÎÏཻÓÚµã(" + intersection.x + "," + // intersection.y + ")£¡"); return 1; // 'Ïཻ } else { // System.out.println("Ï߶ÎÏཻÓÚÐé½»µã(" + intersection.x + "," + // intersection.y + ")£¡"); return -1; // 'Ïཻµ«²»ÔÚÏ߶ÎÉÏ } } void draw(PApplet app){ line((float)a.x, (float)a.y, (float)b.x, (float)b.y); } } |
Based on the line class above, I wrote a line wave class which will utilize the functions to affect grid nodes according to the distance to the certain line described in the way of line2d class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
import igeo.IVec; import igeo.IVec2; class lineWave { IVec2[] DirList;//awaited direction list IVec2 dir; //direction of wave movement, line is vertival to that double L=50;//how long the length of a wava is double phase=0;//current location double S=100;//how high the wave is boolean cir=false;//is it a one time wave or a circular wave IVec2 pos;//one point on the line wave Line2d l; float wid, hei; lineWave(){ //this.l = new Line2d(IVec); } lineWave(IVec2 pos, int dir, double s){ //this.l = new Line2d(IVec); DirList = new IVec2[8]; DirList[0] = new IVec2(0,-1); DirList[1] = new IVec2(0,1); DirList[2] = new IVec2(-1,0); DirList[3] = new IVec2(1,0); DirList[4] = new IVec2(-1,1); DirList[5] = new IVec2(-1,-1); DirList[6] = new IVec2(1,-1); DirList[7] = new IVec2(1,1); this.dir = this.DirList[dir]; this.S = s; this.pos = pos; IVec2 lv = this.dir.dup().rot(Math.PI/2); IVec2 vv = this.pos.dup().add(lv); this.l = new Line2d(this.pos,vv); } double zLocation(IVec2 v){ IVec2 vv = this.l.VerticalPoint(v); double dis = vv.dist(v); return Math.cos(dis/this.L-this.phase)*this.S; } void resetPos(double x, double y){ this.pos.x = x; this.pos.y = y; IVec2 lv = this.dir.dup().rot(Math.PI/2); IVec2 vv = this.pos.dup().add(lv); this.l = new Line2d(this.pos,vv); } void setWH(float wid2,float hei){ this.wid = wid2; this.hei = hei; } void resetDir(IVec2 vv){ IVec2 dd = new IVec2(); double angle = Double.MAX_VALUE; int count = 0; for (int i=0;i<this.DirList.length;i++){ if (DirList[i].angle(vv)<angle){ angle = DirList[i].angle(vv); dd = DirList[i]; count = i; } } if (count==0) this.pos = new IVec2(wid/2,0); else if (count==1) this.pos = new IVec2(wid/2,hei); else if (count==2) this.pos = new IVec2(0,hei/2); else if (count==3) this.pos = new IVec2(wid,hei/2); else if (count==4) this.pos = new IVec2(0,hei); else if (count==5) this.pos = new IVec2(0,0); else if (count==6) this.pos = new IVec2(wid,0); else this.pos = new IVec2(wid,hei); println(this.pos); this.dir = dd; IVec2 lv = this.dir.dup().rot(Math.PI/2); IVec2 vvv = this.pos.dup().add(lv); this.l = new Line2d(this.pos,vvv); } void time(double i){ this.phase+=i; } void affect(grid[][] gs){ for (grid[] gg : gs){ for(grid g : gg){ g.setZ(this.zLocation(g.location2d())); } } } void draw(PApplet app){ pushStyle(); //line(x1, y1, x2, y2); this.l.draw(app); ellipse((float)pos.x,(float)pos.y,10,10); popStyle(); } } |
- The Main function of the point control simulation software
We need this main function that will utilize the class of grid and pointwave that we wrote, thus to finalize the simulate of the behavior of the wave movement based on our control of that point. We will start with the mouse control, which is a function built in processing, and later on we will use the Leap Motion control to replace the mouse Position here. In order to show 3D perspective in a more controled way, I have used a 3D camera library called peasy camera here.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
import needed library for vector and 3d camera import igeo.*; import peasy.*; PeasyCam cam;//initiate the camera float wid = 500;//width of screen float hei = 500;//height of screen int wnum = 5;//width number of grid node int hnum = 5;//height number of grid node grid[][] gs;//to the grid node matrix we are gonna simulate ptWave pt;//the control point void setup() { size((int)wid, (int)hei, P3D); //these are to initiate the camera cam = new PeasyCam(this, 1000); cam.lookAt((int)wid/2, (int)hei/2, 0); cam.setMinimumDistance(50); cam.setMaximumDistance(500000); cam.setRotations(100, 0, 0); //initiate the control point pt = new ptWave(); //initiate the grids gs = new grid[wnum][hnum]; for (int i=0; i<wnum; i++) { for (int j=0; j<hnum; j++) { gs[i][j] = new grid(wid/wnum*i, hei/hnum*j, 0); } } } void draw() { //reset the position of the control point to mouse position pt.setLocation(mouseX, mouseY); //time goes by, wave moves accordingly pt.time(0.1f); //the control point works on the consition of the grid nodes pt.affect(gs); background(255); //draw the grid network for (grid[] gg : gs) { for (grid g : gg) { g.draw3d(this); } } pt.draw(this); } |