/**
* 13/9/65 on 23/1/12
*
* Interactive Art and Computational Design
* Billy Keyes, 2012
*
* A generator of images similar to "13/9/65 Nr. 2" by Frieder Nake
*/
final int NUM_LINES = 9;
final int SEGMENTS = 6;
ArrayList lines;
void setup() {
size(650, 650);
smooth();
noLoop();
stroke(0);
strokeWeight(1.25);
lines = new ArrayList(NUM_LINES + 2);
}
void draw() {
background(240);
// Draw horizontal lines
lines.add(drawSegmentedLine(SEGMENTS, 50, 0, 0));
for (int i = 1; i < NUM_LINES; i++) {
lines.add(drawSegmentedLine(SEGMENTS, 50, i * height / NUM_LINES, 40));
}
lines.add(drawSegmentedLine(SEGMENTS, 50, height, 0));
// Draw vertical "connecting" lines
for (int i = 0; i < lines.size() - 1; i++) {
float[][] t = lines.get(i);
float[][] b = lines.get(i+1);
// Prefer higher numbers of connections between two given rows
int connections = int(sqrt(random(1, 49)));
for (int j = 0; j < connections; j++) {
// Choose a segment to connect
int seg = int(random(SEGMENTS));
if (random(1) < 0.5) {
drawStraightConnectingLines(new float[][]{t[seg], t[seg+1]},
new float[][]{b[seg], b[seg+1]},
int(random(3, 9)), random(0.2, 0.6), random(0.25, 1.0));
} else {
drawDiagConnectingLines(new float[][]{t[seg], t[seg+1]},
new float[][]{b[seg], b[seg+1]},
int(random(3, 9)), random(0.2, 0.6));
}
}
}
// Draw circles
ellipseMode(CENTER);
noFill();
for (int i = 0; i < NUM_LINES; i++) {
for (int j = 0; j < SEGMENTS; j++) {
if (random(1) < 0.1) {
float hf = (float) height;
float wf = (float) width;
float side = sqrt(random(16, sq(hf / (NUM_LINES - 3))));
ellipse(wobble(wf / (SEGMENTS * 2) + j * wf / SEGMENTS, wf / SEGMENTS),
wobble(hf / (NUM_LINES * 2) + i * hf / NUM_LINES, hf / NUM_LINES),
side, side);
}
}
}
}
/**
* @param segs The number of segments to draw
* @param dx The amount of x variation in segment endpoints
* @param y The base y coordinate of segment endpoints
* @param dy The amount of y variation in segment endpoints
*/
float[][] drawSegmentedLine(int segs, float dx, float y, float dy) {
float[][] points = new float[segs + 1][2];
points[0][0] = 0;
points[0][1] = wobble(y, dy);
for (int i = 1; i <= segs; i++) {
points[i][0] = (i == segs) ? width : wobble(i * width / segs, dx);
points[i][1] = wobble(y, dy);
line(points[i - 1][0], points[i - 1][1], points[i][0], points[i][1]);
}
return points;
}
/**
* @param top The top set of segments
* @param bottom The bottom set of segments
* @param density Approximately the number of clusters to draw
* @param cluster Influences how many lines are in each cluster (greater than 0.25)
* @param spread How much of the segment is filled with lines (between 0.0 and 1.0)
*/
void drawStraightConnectingLines(float[][] top, float[][] bottom, int density, float cluster, float spread) {
for (int i = 0; i < top.length; i += 2) {
float xl = max(top[i][0], bottom[i][0]);
float xr = min(top[i+1][0], bottom[i+1][0]);
float diff = spread * (xr - xl);
float base = random(xl, xr - diff);
density = density + int(random(0, 2));
for (int j = 1; j < density; j++) {
for (int k = 0; k < cluster * 4; k++) {
float x = base + wobble(j * diff / density, cluster * diff / density);
float yt = ylerp(top, i, x);
float yb = ylerp(bottom, i, x);
line(x, yt, x, yb);
}
}
}
}
/**
* @param top The top set of segments
* @param bottom The bottom set of segments
* @param density Approximately the number of clusters to draw
* @param cluster Influences how many lines are in each cluster (greater than 0.25)
*/
void drawDiagConnectingLines(float[][] top, float[][] bottom, int density, float cluster) {
for (int i = 0; i < top.length; i += 2) {
float[][] src, dest;
if (random(1) < 0.5) {
src = top;
dest = bottom;
} else {
src = bottom;
dest = top;
}
float diff = src[i+1][0] - src[i][0];
density = density + int(random(0, 2));
for (int j = 1; j < density; j++) {
for (int k = 0; k < cluster * 4; k++) {
float xs = src[i][0] + wobble(j * diff / density, cluster * diff / (density + 1));
float xd = random(dest[i][0], dest[i+1][0]);
float ys = ylerp(src, i, xs);
float yd = ylerp(dest, i, xd);
line(xs, ys, xd, yd);
}
}
}
}
/**
* Produces a random value at most dv/2 away from the given value.
*/
float wobble(float v, float dv) {
return v + random(-dv / 2, dv / 2);
}
/**
* Linear interpolation to find the y coordinate on the degment for the
* given x coordinate.
*/
float ylerp(float[][] points, int i, float x) {
return lerp(points[i][1], points[i+1][1], (x - points[i][0]) / (points[i+1][0] - points[i][0]));
}
void keyPressed() {
if (key == ' ') {
lines.clear();
redraw();
}
} |