// ------------------------------------------------------------------------------------------- class dot { float x,y,u,v,ax,ay; color normalcolor, activecolor, lockedcolor, immobilecolor; float radius; boolean dormant = false; boolean mobile = true; boolean mouselocked = false; boolean active = false; float charge; int nlinks = 0; float transparency = 0.07; float activetransparency = 0.7; boolean imposegrayness = true; dot(float x0, float y0, float ch, float rad, color col) { if (imposegrayness) {col = color(0);} x = x0; y = y0; normalcolor = color(red(col),blue(col),green(col),transparency*255); activecolor = color(red(col),green(col),blue(col),activetransparency*255); radius = rad; charge = ch; u = 0; v = 0; ax = 0; ay = 0; lockedcolor = lighten(lighten(activecolor)); immobilecolor = color(150,0,0,alpha(lockedcolor)); } boolean over() { if ((sq(mouseX-x2scr(x))+sq(mouseY-y2scr(y))) <= sq(radius)) { return true; } else { return false; } } void update(float dt) { if (!dormant) { if ((mousePressed) && (over())) { mouselocked = true; } if (!mousePressed) {mouselocked = false;} move(dt); } } void onmouserelease() { if (over()) { if (((keyCode==CONTROL) || (keyCode==157)) && keyPressed) { mobile = !mobile; } else { active = !active; } } } void move(float dt) { if (mouselocked) { x = scr2x(mouseX); y = scr2y(mouseY); u = 0; v = 0; } else if (mobile) { u = u + ax*dt; v = v + ay*dt; x = x + u*dt; y = y + v*dt; x = constrain(x,0,1); y = constrain(y,0,screenHeight/(float)screenWidth); } } void draw() { if (!dormant) { if (mouselocked) { fill(lockedcolor); } else if (!mobile) { fill(immobilecolor); } else if (active) { fill(activecolor); } else { fill(normalcolor); } noStroke(); ellipse(x2scr(x),y2scr(y),radius,radius); // text(str(nlinks),x2scr(x)+radius,y2scr(y)); if (active) { if (mobile) { stroke(darken(activecolor)); } else { stroke(darken(immobilecolor)); } noFill(); strokeWeight(0.5); ellipse(x2scr(x),y2scr(y),1.7*radius,1.7*radius); } } } } // ------------------------------------------------------------------------------------------- class spring { dot from, to; float eqlength, breaklength; float kpull, kpush; color normalcolor; float width = 1; boolean dormant = false; spring(dot fromdot, dot todot, float k) { from = fromdot; to = todot; fromdot.nlinks++; todot.nlinks++; normalcolor = darken(blend(to.normalcolor,from.normalcolor,ADD)); eqlength = sqrt(sq(to.x-from.x)+sq(to.y-from.y)); // start the spring at equilibrium breaklength = 5*eqlength; kpull = k; kpush = k; } void update() { if (from==to) {dormant = true;} if (!dormant) { force(); } } boolean over() { return false; } void force() { // exert spring forces on endpoint dots if (!dormant) { float len = sqrt(sq(to.x-from.x)+sq(to.y-from.y)); float dl = len - eqlength; float F; if (dl > 0) { // spring stretched F = -dl * kpull; } else { // spring compressed F = -dl * kpush; } if (F != 0) { float theta = atan2(to.y-from.y,to.x-from.x); if (abs(theta)<=TWO_PI) { float dax = F*cos(theta); float day = F*sin(theta); to.ax = to.ax + dax; // note: assuming all dots have a mass of 1 to.ay = to.ay + day; from.ax = from.ax - dax; from.ay = from.ay - day; } } if (len>breaklength) {dormant = true;} // if stretched too far, apply the force this time but then break if (len==0) {dormant = true;} // if reduced to length 0, deactivate } } void draw() { if (!dormant) { stroke(normalcolor); strokeWeight(width); line(x2scr(from.x), y2scr(from.y), x2scr(to.x), y2scr(to.y)); } } } // ------------------------------------------------------------------------------------------- class face { color normalcolor; float transparency; dot[] vertices; int N; boolean dormant = false; face(dot[] v, color col, float tr) { N = v.length; vertices = v; normalcolor = col; transparency = tr; } boolean contains(dot d) { boolean f = false; for (int i=0; i