import processing.opengl.*; final static int numPenguins = 30; final static int numLights = 4; final static float diameter = 40; // diameter of the penguins final static float spring = 0.95; // how much give penguins have against each other (1 is none, 0 is no resistance) final static float wallBounce = -0.2; // how much do they bounce off the wall? 0 not at all, -1 w/ complete energy final static float baseLightAversion = 5000; // factor adjusting how much they run away from the light final static float exposureFactor = 100; // how much does the light recieved increase the exposure? final static float lightAmountFactor = 10; // How much to bump up the light amount final static boolean showExposure = true; final static float gravity = 0; // how much are they attracted to the bottom? final static float friction = 0.000000000001; // how much do they slow down when uninterefered with? final static float maxSpeed = 1.0; // top speed for penguins final static float maxExposure = 400.0; // top exposure factor final static float exposureDecrement = 0.1; // how much to diminish exposure final static float lightInfluenceLine = 0;//5; // How big to make the light influence line? (0 to disable) final static int lightRecess = 50; // how far offstage are the lights? final static float lightIntensity = 1; Penguin[] penguins = new Penguin[numPenguins]; Light[] lights = new Light[numLights]; int draggingPenguin = -1; void setup() { size(500, 500,OPENGL); frameRate(50); // smooth(); for (int i = 0; i < numPenguins; i++) { penguins[i] = new Penguin(random(width), random(height), diameter, i, penguins); } lights[0] = new Light(0 - lightRecess, 0 - lightRecess, lightIntensity); lights[1] = new Light(width + lightRecess, 0 - lightRecess, lightIntensity); lights[2] = new Light(0 - lightRecess, height + lightRecess, lightIntensity); lights[3] = new Light(width + lightRecess, height + lightRecess, lightIntensity); // lights[4] = new Light(width / 2, height / 2, lightIntensity / 100); } void draw() { background(0); for (int i = 0; i < numLights; i++) { lights[i].display(); } for (int i = 0; i < numPenguins; i++) { penguins[i].collide(); penguins[i].interpretLight(); penguins[i].move(); penguins[i].display(); } } void mousePressed(){ for(int i = numPenguins - 1; i >= 0; i--){ float dx = mouseX - penguins[i].x, dy = mouseY - penguins[i].y; if (sqrt(dx*dx + dy*dy) <= (diameter/2)){ draggingPenguin = i; break; } } if (draggingPenguin == -1){ for(int i = 0; i < numLights; i++){ lights[i].turnOff(); } } } void mouseReleased(){ if (draggingPenguin != -1){ draggingPenguin = -1; } else{ for(int i = 0; i < numLights; i++){ lights[i].turnOn(); } } } float lightIntensity(float brightness, float distance){ return lightAmountFactor * brightness / (distance * distance * distance); } class Penguin { float x, y; float diameter; float vx = 0; float vy = 0; int id; Penguin[] others; float exposure = 1.0; float lightInfluence[][] = new float[numLights + 1][2]; int dispCounter = 100; float lastLightX = 0, lastLightY = 0; Penguin(float xin, float yin, float din, int idin, Penguin[] oin) { x = xin; y = yin; diameter = din; id = idin; others = oin; } /** * interpretLight calculates how much light the Penguin is exposed to and adjusts * the velocities accordingly. */ void interpretLight(){ if (exposure >= exposureDecrement) exposure -=ÊexposureDecrement; if (exposure > maxExposure) exposure = maxExposure; // Check each of the lights for (int i = 0; i < numLights; i++){ float dx = lights[i].x - x; float dy = lights[i].y - y; float distance = sqrt(dx*dx + dy*dy); float lightAmount = lightIntensity(lights[i].getBrightness(), distance); // TODO check to see if another penguin is blocking this light for (int j = 0; j < numPenguins; j++){ // uhh..... how do I do this? if (j != i){ if (isBetween(penguins[j], lights[i])){ // If this were really smart, it would clalculate // how much shadow would be cast on the penguin lightAmount = 0; } } } exposure += (lightAmount * baseLightAversion * exposureFactor); float angle = atan2(dy, dx); float targetX = x + cos(angle); float targetY = y + sin(angle); float ax = (targetX - lights[i].x) * (lightAmount) * baseLightAversion; float ay = (targetY - lights[i].y) * (lightAmount) * baseLightAversion; // Gather all of the light influences into an array lightInfluence[i][0] = ax; lightInfluence[i][1] = ay; } // Now analyze the light influences and apply the overall influence lastLightX = 0; lastLightY = 0; if (draggingPenguin != id){ for (int i=0; i curMaxSpeed){ float speedAdjust = speed / curMaxSpeed; vy /= speedAdjust; vx /= speedAdjust; } vy += gravity; x += vx; y += vy; float halfDiameter = diameter/2; if (x + halfDiameter > width) { x = width - halfDiameter; vx *= wallBounce; } else if (x - halfDiameter < 0) { x = halfDiameter; vx *= wallBounce; } if (y + halfDiameter > height) { y = height - halfDiameter; vy *= wallBounce; } else if (y - halfDiameter < 0) { y = halfDiameter; vy *= wallBounce; } } else{ vy = y - mouseY - y; vx = mouseX - x; y = mouseY; x = mouseX; } } void display() { stroke(50); // noStroke(); float pColor = 0; if (showExposure){ pColor = exposure / maxExposure * 255; } fill(pColor, 0, 0, 204); ellipse(x, y, diameter, diameter); if (draggingPenguin != id){ stroke(255); if (lightInfluenceLine > 0){ for(int i = 0; i < numLights; i++){ line(x, y, x + (lightInfluence[i][0] * lightInfluenceLine), y + (lightInfluence[i][1] * lightInfluenceLine)); } } stroke(255, 0, 255); line(x, y, x + (lightInfluence[numLights][0] * lightInfluenceLine), y + (lightInfluence[numLights][1] * lightInfluenceLine)); float dx = mouseX - x, dy = mouseY - y; if (dispCounter > 40){ if (sqrt(dx*dx + dy*dy) <= (diameter/2)){ System.out.println("Penguin # " + id + " (vx, vy):(" + vx + "," + vy + ") exposure: " + exposure + " lastLight:(" + lastLightX + "," + lastLightY + ")"); dispCounter = 0; } } else{ dispCounter++; } } } boolean isBetween(Penguin p, Light l){ float porpX = proportion(x, l.x, p.x); if (!isPropOk(porpX)) return false; float porpY = proportion(y, l.y, p.y); if (!isPropOk(porpY)) return false; float acceptableDeviation = p.diameter * porpX + p.diameter; float betweenY = y + ((l.y - y) * porpY); if (abs(betweenY - p.y) < acceptableDeviation) return true; return false; } float proportion(float me, float light, float thingBetween){ return (thingBetween - me) / (light - me); } boolean isPropOk(float porp){ if (porp <= 0) return false; if (porp > 1) return false; return true; } } class Light{ float x, y, brightness; int innerCol; int outerCol = color(0, 1); float radius; boolean isOff = false; Light(float x, float y, float brightness){ this.x = x; this.y = y; this.brightness = brightness; radius = (width + height) / 2 * brightness; innerCol = color((int) (255 * brightness)); } void display(){ float curBrightness = isOff ? 0 : brightness; radius = (width + height) / 2 * curBrightness; innerCol = color((int) (255 * curBrightness)); noStroke(); beginShape(TRIANGLE_STRIP); for(float theta=0; theta