Code:
import Ilda.*;
/*
* Lissajous Oscillation
* Original sketch by Beautiful Programming
* http://beautifulprogramming.com/lissajous-oscillation/
*
* Modified by CMB so it exports laser art
* Happy lasing!
*/
Ilda ilda;
PFont font;
Part part;
int time = 1;
float r1, r2, r3, r4;
int numAxis = 1;
color bgColor;
boolean showsFPS = true;
boolean doesPaint = true;
boolean autoplay = true;
boolean isPaused = false;
ColorPalette cp;
float spread = 5;
float osc1, osc2;
PGraphics overlay; //Displays the text
PGraphics abstr; //Displays the abstract as found on the web
PGraphics laser; //Display the abstract as rendered to ilda
IldaRenderer r; //Renders ilda points, works in the same way as a PGraphics
boolean renderToLaser = false;
boolean displayLaserError = false;
boolean displayLaser = false;
void setup()
{
size(940, 540, P2D);
ilda = new Ilda(this); //As for now the library needs a reference to this PApplet but it should be possible to remove that in the future
r = new IldaRenderer(ilda, 540, 540); //
r.setOptimise(false); //Assume the software the ilda file ends up has optimisation built in... (also optimisation is still in beta)
overlay = createGraphics(940, 540);
abstr = createGraphics(940, 540);
laser = createGraphics(540, 540, P3D); //To render an ilda frame to a PGraphics it has to be 3D
colorMode(HSB);
background(0);
strokeWeight(0.5);
font = createFont("Helvetica", 18);
overlay.textFont(font, 18);
bgColor = color(0, 10);
randomize();
part = new Part(0, 0);
}
void draw()
{
background(bgColor);
if (!isPaused)
{
//The abstract is now rendered to a separate PGraphics
abstr.beginDraw();
if (!doesPaint)
{
abstr.fill(bgColor);
abstr.noStroke();
abstr.rect(0, 0, width, height);
}
if (autoplay)
{
time++;
if (time > 700) randomize();
}
abstr.translate(width/2, height/2);
r.translate(r.width/2, r.height/2);
abstr.noFill();
osc1 += 0.0011;
osc2 += 0.0013;
r1 = abs(sin(osc1) * 0.009) + 0.001;
r2 = abs(sin(osc2) * 0.009) + 0.001;
if (renderToLaser) r.beginDraw(); //Don't forget this!
part.draw();
abstr.endDraw();
if (renderToLaser) r.endDraw();
}
if (!displayLaser && abstr != null ) image(abstr, 0, 0);
if (displayLaser && renderToLaser)
{
IldaFrame frame = r.getCurrentFrame();
image(frame.renderFrame(laser, true), 200, 0); //This is how you render an Ilda frame to a PGraphics. Easy!
}
if (showsFPS) displayFPS();
}
void randomize()
{
time = 1;
if (doesPaint) background(0);
randomizeColor();
randomizeShape();
randomizeAxis();
}
void randomizeShape()
{
if (doesPaint) abstr.background(0);
float bottom = 0.001;
float top = 0.01;
r1 = random(bottom, top);
r2 = random(bottom, top);
r3 = random(bottom, top);
r4 = random(bottom, top);
spread = random(1, 20);
}
void randomizeAxis()
{
if (doesPaint) abstr.background(0);
numAxis = round(random(2, 30));
}
void randomizeColor()
{
cp = new ColorPalette();
}
void finishRenderingToLaser()
{
if (renderToLaser)
{
renderToLaser = false;
selectOutput("Select where to output ilda file to", "outputSelected");
} else
{
showsFPS = true;
displayLaserError = true;
}
}
void outputSelected(File selection)
{
if (selection == null)
{
println("Wrong file");
return;
}
ilda.writeFile(r.getFrames(), selection.getAbsolutePath()); //This is how you export an ilda file. Can it be easier? (Format 4 by default)
}
void mouseClicked()
{
randomize();
autoplay = false;
}
void keyPressed()
{
if (key == 'o') showsFPS = !showsFPS;
if (key == 'd')
{
float a = alpha(bgColor);
if (a == 10) a = 40;
else a = 10;
bgColor = color(0, a);
}
if (key == 'p') doesPaint = !doesPaint;
if (key == 'a') autoplay = !autoplay;
if (key == ' ') isPaused = !isPaused;
if (key == 'c') randomizeColor();
if (key == 'h') randomizeShape();
if (key == 'x') randomizeAxis();
if (key == 'b') abstr.background(0);
if (key == 'r')
{
renderToLaser = true;
displayLaserError = false;
r.clearAllFrames();
}
if (key == 's') finishRenderingToLaser();
if (key == 't') displayLaser = !displayLaser;
if (key == '=')
{
numAxis++;
if (doesPaint) abstr.background(0);
}
if (key == '-')
{
numAxis--;
if (doesPaint) abstr.background(0);
}
if (numAxis < 1) numAxis = 1;
if (numAxis > 30) numAxis = 30;
}
void displayFPS()
{
overlay.beginDraw();
overlay.background(0, 1);
overlay.fill(255);
String output = "fps=";
output += (int) frameRate;
overlay.textAlign(LEFT);
overlay.text(output, 10, 30);
overlay.text("This overlay: o [ON]", 10, 60);
overlay.text("Toggle background paint: p [" + (doesPaint ? "ON" : "OFF") + "]", 10, 90);
overlay.text("Decay length when background paint toggled off: d", 10, 120);
overlay.text("Auto-randomizing: a [" + (autoplay ? "ON" : "OFF") + "]", 10, 150);
overlay.text("Pause: spacebar [" + (isPaused ? "ON" : "OFF") + "]", 10, 180);
overlay.text("Randomize colour: c", 10, 210);
overlay.text("Randomize shape: h", 10, 240);
overlay.text("Randomize axis: x", 10, 270);
overlay.text("Clear background: b", 10, 300);
overlay.text("Change number of axes: +/- [" + numAxis + "]", 10, 330);
overlay.text("Toggle laser view (only when recording): t [" + (displayLaser ? "LASER" : "PIXELS") + "]", 10, 360);
if (displayLaserError) overlay.text("Error: must record before saving. Hit r before hitting s.", 10, overlay.height-90);
overlay.text("Start recording Ilda file: r [" + (renderToLaser ? "RECORDING: " + r.getFrames().size() + " frames" : "IDLE") + "]", 10, overlay.height-60);
overlay.text("Finish recording Ilda frames and save to file: s", 10, overlay.height-30);
overlay.endDraw();
image(overlay, 0, 0);
}
class ColorPalette
{
PGraphics pg;
int w = 300;
color[] allColors;
ColorPalette()
{
createPalette();
allColors = new color[w];
for (int i=0; i<w; i++)
{
allColors[i] = pg.get(i, 0);
}
}
color getColorAtRatio(float _ratio)
{
int index = min(w-1, floor(w * _ratio));
return allColors[index];
}
void createPalette()
{
pg = createGraphics(w, 1);
pg.colorMode(HSB);
pg.beginDraw();
pg.noStroke();
color baseColor = color(random(255), random(255, 255), random(255, 255));
pg.background(baseColor);
int numColors = 6;
for (int i=0; i<numColors; i++)
{
addColor();
}
pg.endDraw();
}
void addColor()
{
color c = color(random(255), random(0, 255), random(150, 255));
float pos = random(w);
float size = w/4;
for (int i=0; i<w; i++)
{
float ratio = 0;
float d = abs(i-pos);
if (d < size)
{
ratio = 1.0 - (d/size);
}
pg.fill(hue(c), saturation(c), brightness(c), (ratio)*255);
pg.rect(i, 0, 1, 1);
}
}
void draw()
{
image(pg, 0, height/2, width, 10);
}
}
class Part
{
PVector pos[];
int trailLength = 100;
float speed = 0.03;
float o1, o2, o3, o4;
Part(float _x, float _y)
{
pos = new PVector[trailLength+1];
for (int i=0; i<=trailLength; i++)
{
pos[i] = new PVector(0, 0);
}
}
void draw()
{
float step = radians(360/numAxis);
float x, y;
for (int i=0; i<=trailLength; i++)
{
o1 += r1*speed;
o2 += r2*speed;
o3 += r3*speed;
o4 += r4*speed;
x = sin(o1 + i*spread*r1) * 125;
y = cos(o2 + i*spread*r2) * 50;
x += sin(o3 + i*spread*r3) * 125;
y += cos(o4 + i*spread*r4) * 50;
pos[i].x += (x - pos[i].x) * 0.1;
pos[i].y += (y - pos[i].y) * 0.1;
}
for (int i=1; i<=trailLength; i++)
{
abstr.stroke(cp.getColorAtRatio(i/trailLength));
abstr.pushMatrix();
for (int j=0; j<numAxis; j++)
{
abstr.rotate(step);
abstr.line(pos[i].x, pos[i].y, pos[i-1].x, pos[i-1].y);
}
abstr.popMatrix();
}
//I had to reorder the loops otherwise it would be a mess...
if (renderToLaser)
{
for (int j=0; j<numAxis; j++)
{
r.rotate(step);
r.beginShape(LINES); //Using shape and vertex instead of line as this gives better results
for (int i=1; i<=trailLength; i++) //Line works as well but would result in two points per vertex, one of them blanked (not optimal of course)
{
r.stroke(cp.getColorAtRatio(i/trailLength));
r.vertex(pos[i].x, pos[i].y);
}
r.endShape();
}
}
}
}
Download: