// // // Generated Landscape // Nick Seaver // for Nick Montfort's Workshop (CMS.950) // 10/06/08 // // // Almost all parameters are randomized // // Press l, o, or s to toggle control lines, output, or sound // //-ONLINE VERSION: //---sound controls are commented out //---window size is fixed //import libraries for audio synthesis //import ddf.minim.*; //import ddf.minim.signals.*; //declare size of landscape float sizeX = random(5000,10000); float sizeY = random(5000,10000); //decide number of peaks to draw int peakCount = int(random(500,700)); //generate arrays to hold the data for each peak float[] xcenter = new float[peakCount]; //make an array to hold the x-coordinate for every peak float[] ycenter = new float[peakCount]; //make an array to hold the y-coordinate for every peak float[] elevation = new float[peakCount]; //make an array to hold the elevation of every peak float[] yUp = new float[peakCount]; //make an array to hold the shaping data for every peak float[] yDown = new float[peakCount]; //make an array to hold the shaping data for every peak float[] xLeftTop = new float[peakCount]; //make an array to hold the shaping data for every peak float[] xRightTop = new float[peakCount]; //make an array to hold the shaping data for every peak float[] xLeftBottom = new float[peakCount]; //make an array to hold the shaping data for every peak float[] xRightBottom = new float[peakCount]; //make an array to hold the shaping data for every peak float[] yRightTop = new float[peakCount]; //make an array to hold the shaping data for every peak float[] yRightBottom = new float[peakCount]; //make an array to hold the shaping data for every peak //generate arrays to hold calculation results based on arrays above float[][] top = new float[peakCount][peakCount]; float[][] ctrlytopright = new float[peakCount][peakCount]; float[][] ctrlxpostop = new float[peakCount][peakCount]; float[][] slopetop = new float[peakCount][peakCount]; float[][] ctrlxnegtop = new float[peakCount][peakCount]; float[][] ctrlytopleft = new float[peakCount][peakCount]; float[][] bottom = new float[peakCount][peakCount]; float[][] ctrlybottomright = new float[peakCount][peakCount]; float[][] ctrlxposbottom = new float[peakCount][peakCount]; float[][] slopebottom = new float[peakCount][peakCount]; float[][] ctrlxnegbottom = new float[peakCount][peakCount]; float[][] ctrlybottomleft = new float[peakCount][peakCount]; //initialize audio component //AudioOutput out; //TriangleWave[] sine = new TriangleWave[peakCount]; //define window start location, relative to overall landscape size float xnew=-sizeX/2; float ynew=-sizeY/2; //declare boolean values to enable toggling outlines, control lines, and sound (control lines and sound off by default) boolean outlines = true; boolean sound = false; boolean control = false; boolean colors = false; //randomly define the window size, but always landscape oriented //int wide = int(random(600,1000)); //int tall = int(wide*random(.5,.75)); //randomize stroke weight (for completeness' sake) int sWeight = int(random(4,6)); //randomize color values int[] hueStart = new int[peakCount]; int[] peakSat = new int[peakCount]; int[] peakBright = new int[peakCount]; //get everything ready void setup(){ size(800,600); //set color mode to HSB to facilitate color shifting by hue value colorMode(HSB, 100); noStroke(); smooth(); //frameRate tweaked to make dragging less laggy. ideal value varies by computer, # of open processes frameRate(100); //start audio and get a line out //Minim.start(this); //out = Minim.getLineOut(Minim.STEREO, 1024); //fill the arrays with randomized values, calibrated to avoid misshaping and unpleasing shapes for (int peak = 0; peak < peakCount; peak++) { xcenter[peak] = random(0,sizeX); ycenter[peak] = random(0,sizeY); elevation[peak] = random(1, 10); //these values control the shape of the bezier curves that make up the peaks yUp[peak] = random(10, 30); yDown[peak] = random(10, 30); xLeftTop[peak] = random(10, 30); xRightTop[peak] = random(10, 30); xLeftBottom[peak] = random(10, 30); xRightBottom[peak] = random(10, 30); yRightTop[peak] = random(15, 30); yRightBottom[peak] = random(15, 30); //make a TriangleWave object for each peak, with peak height mapped to a range of frequencies //.05 is amp, 44100 is the sample rate, and portamento smooths out some clicks //sine[peak] = new TriangleWave(map(elevation[peak],1,10,151,261),.05,44100); //sine[peak].portamento(500); //calculate all control line values for bezier curves at the start to reduce lag for (int L = 11; L > 0; L--) { //define top center and top right point of bezier control lines top[peak][L] = (ycenter[peak]+L*yUp[peak]); ctrlytopright[peak][L] = (top[peak][L] + yRightTop[peak]); ctrlxpostop[peak][L] = (xcenter[peak]+L*xRightTop[peak]); //use generated top center and top right point to determine slope slopetop[peak][L] = (ctrlytopright[peak][L]-top[peak][L])/(ctrlxpostop[peak][L]-xcenter[peak]); //using slope to determine top left point ctrlxnegtop[peak][L] = (xcenter[peak]-L*xLeftTop[peak]); ctrlytopleft[peak][L] = slopetop[peak][L]*(ctrlxnegtop[peak][L]-ctrlxpostop[peak][L]) + ctrlytopright[peak][L]; // that's based on y-y1 = m(x-x1) //define bottom center and bottom right point of bezier control lines bottom[peak][L] = (ycenter[peak]-L*yDown[peak]); ctrlybottomright[peak][L] = (bottom[peak][L] + yRightBottom[peak]); ctrlxposbottom[peak][L] = (xcenter[peak]+L*xRightBottom[peak]); //use bottom center and bottom right point to determine slope slopebottom[peak][L] = (ctrlybottomright[peak][L]-bottom[peak][L])/(ctrlxposbottom[peak][L]-xcenter[peak]); //use slope to determine bottom left point ctrlxnegbottom[peak][L] = (xcenter[peak]-L*xLeftBottom[peak]); ctrlybottomleft[peak][L] = slopebottom[peak][L]*(ctrlxnegbottom[peak][L]-ctrlxposbottom[peak][L]) + ctrlybottomright[peak][L]; //that's based on y-y1 = m(x+x1) }} } //used in testing to report current offset value on click void mousePressed() { println("---------------------"); print("offset x: "); println(xnew); print("offset y: "); println(ynew); } //key toggles to enable/disable sound, outlines, and control lines void keyPressed(){ if (key == 'o') { if (outlines == true) { outlines = false;} else {outlines = true;} } //if (key == 's') { //if (sound == true) { // sound = false; //out.clearSignals();} //else {sound = true;} // } if (key == 'l') { if (control == true) { control = false;} else {control = true;} } if (key == 'c') { if (colors == true) { colors = false;} else {colors = true; for (int peak = 0; peak < peakCount; peak++) { //color values randomized hueStart[peak] = int(random(0,100)); peakSat[peak] = int(random(30,100)); peakBright[peak] = int(random(50,100));} } } } //now we can draw void draw() { //background and translate start each frame to move window through landscape background(66,100,50); //toggle alternate randomized background color if (colors == true) {background(hueStart[1], peakSat[1], peakBright[1]);} translate(xnew,ynew); //start drawing with bottom level (11) and then draw up to top for (int L = 11; L > 0; L--) { //at each level, draw all peaks (from peak[0] to peak[peakCount]) for (int Q = 0; Q < peakCount; Q++){ //but when you draw, only draw peaks that will show up on-screen (by selecting peaks with centerpoints within or near the window range) if (xcenter[Q] > -xnew-width/2 && xcenter[Q] < -xnew+width*1.5 && ycenter[Q] > -ynew-height/2 && ycenter[Q] < -ynew+height*1.5) { if (elevation[Q] <= L){ //hue determined according to peak elevation fill((L*7-10)%70,100,100); //toggle to fully randomize colors if (colors == true) { fill((L*7+hueStart[L])%70,peakSat[L],peakBright[L]); } /*now the program draws the curves using the values from above*/ //toggle outlines on or off if (outlines == true) {stroke(0,0,sWeight); strokeWeight(sWeight);} //draw two curves that join at the top and bottom, with control lines that join smoothly bezier(xcenter[Q]-1, top[Q][L], ctrlxpostop[Q][L], ctrlytopright[Q][L], ctrlxposbottom[Q][L], ctrlybottomright[Q][L], xcenter[Q]-1, bottom[Q][L]); bezier(xcenter[Q], top[Q][L], ctrlxnegtop[Q][L], ctrlytopleft[Q][L], ctrlxnegbottom[Q][L], ctrlybottomleft[Q][L], xcenter[Q], bottom[Q][L]); } } } //this for loop duplicates the one above, but without the stroke, in order to hide extra lines due to overlapping peaks for (int Q = 0; Q < peakCount; Q++){ if (elevation[Q] <= L){ if (xcenter[Q] > -xnew-width/2 && xcenter[Q] < -xnew+width*1.5 && ycenter[Q] > -ynew-height/2 && ycenter[Q] < -ynew+height*1.5) { fill((L*7-10)%70,100,100); //toggle to fully randomize colors if (colors == true) { fill((L*7+hueStart[L])%70,peakSat[L],peakBright[L]);} //noStroke covers up any overlapping outlines from the previous drawing noStroke(); bezier(xcenter[Q]-1, top[Q][L], ctrlxpostop[Q][L], ctrlytopright[Q][L], ctrlxposbottom[Q][L], ctrlybottomright[Q][L], xcenter[Q]-1, bottom[Q][L]); bezier(xcenter[Q], top[Q][L], ctrlxnegtop[Q][L], ctrlytopleft[Q][L], ctrlxnegbottom[Q][L], ctrlybottomleft[Q][L], xcenter[Q], bottom[Q][L]); //draw control lines if toggled on if (control == true) { stroke(0,100,100); strokeWeight(sWeight/4); line(ctrlxpostop[Q][L], ctrlytopright[Q][L], ctrlxnegtop[Q][L], ctrlytopleft[Q][L]); line(ctrlxposbottom[Q][L], ctrlybottomright[Q][L], ctrlxnegbottom[Q][L], ctrlybottomleft[Q][L]); } } } } } //draw outline at the edge of the the landscape area stroke(0, 0, 40); strokeWeight(sWeight*5); noFill(); rect(0,0,sizeX,sizeY); } //whew //sound is changed on mouseRelease, because constant changing during drag was laggy and clicky void mouseReleased(){ //if (sound==true){ //lower gain to inaudible level to mask clicks //out.shiftGain(0,-40,100); //out.clearSignals(); //activate only on-screen TriangleWaves //for (int Q = 0; Q < peakCount; Q++){ //if (xcenter[Q] > -xnew && //xcenter[Q] < -xnew+width && //ycenter[Q] > -ynew && //ycenter[Q] < -ynew+height) { //out.addSignal(sine[Q]); //} //} //split up amplitude across the number of on-screen TriangleWaves (helps normalize the output volume, but not entirely) //for (int Q = 0; Q < peakCount; Q++){ //if (xcenter[Q] > -xnew && //xcenter[Q] < -xnew+width && //ycenter[Q] > -ynew && //ycenter[Q] < -ynew+height) { //sine[Q].setAmp(.6/out.signalCount()); //} //} //bring gain back up after signals established //out.shiftGain(-40,0,100); //} //print offset on mouseRelease println("---------------------"); print("offset x: "); println(xnew); print("offset y: "); println(ynew); } //scrolling occurs when the mouse is dragged void mouseDragged(){ //new translate values are calculated from drag direction xnew = xnew - (pmouseX-mouseX); ynew = ynew - (pmouseY-mouseY); //when the edge of the generated landscape is reached, scrolling stops if(xnew <= -sizeX+width) { xnew = -sizeX+width; } if(xnew >= 0) { xnew = 0; } if(ynew >= 0) { ynew = 0; } if(ynew <= -sizeY+height) { ynew = -sizeY+height; } } //the end