My 2D graphics course went live today! It was hot here last night so I slept on the couch downstairs, and by coincidence my dog woke me up around 12:30. Since the course materials were supposed to appear on the site at midnight, I checked – and there they were! So everything seems smooth so far, and I’ve had no complaints from students.
To celebrate, I decided to make something pretty. I wrote this program for making little splindly-yarn things. Here are a few images I just made. It’s fun to play with this – I could sit here making little things like this all day!
Here’s the code. If you’re in my class, you’ll find all of this easy to understand by the time we’re done.
// Circle Nomads // Andrew Glassner, 5 May 2013 // We start with a single circle of Nomads. Click the // mouse to start a new one, centered at the mouse. Nomad[] NomadList; int StepCount; int MaxSteps; boolean Frozen = false; float NoiseScale; void setup() { size(800, 600); smooth(); // Start with one burst in a random place initNomads(random(0,width), random(0,height)); background(0); } // Create a circle of Nomads. Each one is randomly on the // circle, and has some variation of the shared color. void initNomads(float diskX, float diskY) { NomadList = new Nomad[300]; float diskR = random(25, 100); colorMode(HSB); color baseClr = color(random(0,255), random(100,255), random(170,255)); colorMode(RGB); for (int i=0; i<300; i++) { float angle = random(0, TWO_PI); color thisClr = wiggleColorHSB(baseClr, .1, .1, .1); NomadList[i] = new Nomad(diskX+(diskR*cos(angle)), diskY+(diskR*sin(angle)), thisClr, 1); } StepCount = 0; MaxSteps = int(random(200, 800)); // how many times to draw // low values of noise give long, thin tendrils. High values are twistier. NoiseScale = random(.0001, .03); } // given an HSB color, wiggle each of the values around a bit color wiggleColorHSB(color c, float wh, float ws, float wb) { colorMode(HSB); float newHue = (hue(c)+(wh*255*random(-1, 1))) % 255; float newSat = constrain(saturation(c)+(ws*255*random(-1,1)), 0, 255); float newBrt = constrain(brightness(c)+(wb*255*random(-1,1)), 0, 255); color newClr = color(newHue, newSat, newBrt); colorMode(RGB); return(newClr); } void draw() { if (Frozen) return; if (++StepCount > MaxSteps) { return; } for (int i=0; i<NomadList.length; i++) { NomadList[i].render(); NomadList[i].update(); } } void keyTyped() { if (key == ' ') Frozen = !Frozen; } void mousePressed() { initNomads(mouseX, mouseY); } // A class for a little wanderer class Nomad { PVector velocity, pos, oldPos; color clr; float r, opacity; Nomad(float startX, float startY, color startClr, float startR) { pos = new PVector(startX, startY); oldPos = pos.get(); clr = startClr; r = startR; opacity = random(.25, .5); velocity = new PVector(random(.5, 1), random(.5, 1)); if (random(100)>50) velocity.x *= -1; if (random(100)>50) velocity.y *= -1; } /* Each Nomad has a velocity and a position. On each frame, the position is updated by the velocity. The velocity changes on every frame, inheriting some change from the underlying noise. */ void update() { // Get a new value for the velocity from the noise float vx = noise(pos.x*NoiseScale, pos.y*NoiseScale); float vy = noise((2*width)+(pos.x*NoiseScale), (2*height)+(pos.y*NoiseScale)); // Noise values are from (0,1) but we want them from (-1,1) vx = lerp(-1, 1, vx); vy = lerp(-1, 1, vy); // Get some "blending factors" to control how the velocity updates float bx = lerp(.25, .75, noise(pos.y * NoiseScale)); float by = lerp(.25, .75, noise(pos.x * NoiseScale)); // Add some of the new values to the velocity velocity.x += bx*vx; velocity.y += by*vy; // Normalize the velocity, save the old point, update the position velocity.normalize(); oldPos = pos.get(); pos.x += velocity.x; pos.y += velocity.y; // Finally, make the particle just ever so slightly less opaque opacity *= .995; } void render() { stroke(clr, opacity*255); strokeWeight(r); noFill(); line(oldPos.x, oldPos.y, pos.x, pos.y); } }