Skip to content

Instantly share code, notes, and snippets.

Created April 20, 2014 01:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save anonymous/11ec707e5706620ac95c to your computer and use it in GitHub Desktop.
Save anonymous/11ec707e5706620ac95c to your computer and use it in GitHub Desktop.
sawtooth
//Processing source for sawtooth
//@author amiagoodperson.tumblr.com
//technically two separate files but it doesn't really matter
//specifics.pde
//--Parameters--
color bg = color(0, 0, 0); //rgb
int tIncr = 9; //how much tmpCount increments by
String filename = "sawtooth"; //leave blank to make no images
int saveFreq = 4;
int backgroundFreq = 0; // every n frames, 0 for always, -1 for never
//possibly: layer things?
//todo:
/* done:
* "radial": drawn at radius r from centre
* "radialr": "radial reverse", starts from outside in
* "radialf": "full radial", a full line instead of a ray
* "grid": drawn in a grid, specified further by grid
* "recursive": grid, but done recursively (yields different results with tmpCount)
*/
String scheme = "radial";
//done: "xy" (regular 2-d), "hex" (hex grid)
String grid = "hex";
int radialnum = 16; // # of times to rotate on radial scheme
float spacing = 5;
//planned: transformations such as: zooming perspective
//--Phase--
//"sine": sine wave, "sawtooth": sawtooth wave / mod, "ramptooth": line + sawtooth
/* done:
* scale is constant multiplier
* if mod is non-zero, c %= mod
* "linear": scale*count = c
* "triangle": c, bounded to between 0 and p[0] in a triangle wave
* "parabolic": between -p[0] and p[0], but scales quadratically
* "parabroke": parabolic, but shifted outside [0, p[0]] (uses p[0]^2 / 2 - (thing) instead of -p[0] + (thing) )
* "trapezoid": same as triangle, but spends p[1] time at max / min values
* "noise": built-in noise function at c * p[0]
*/
//TODO: only save one full cycle of images
String pFunc = "triangle";
float scale = PI/30;
float mod = 0;
float[] p = {2*PI, PI/4};
float phase(int count)
{
float c = scale*count;
if(mod > 0 && !pFunc.equals("trapezoid"))
c %= mod;
if(pFunc.equals("linear"))
return c;
else if(pFunc.equals("triangle"))
return abs(p[0] - (c % (2*p[0])));
else if(pFunc.equals("trapezoid"))
return constrain(abs(p[0] + p[1] - (c % (2*(p[0]+p[1])))) - p[1]/2, 0, p[0]);
else if(pFunc.equals("noise"))
return p[0] * noise(c/5);
else if(pFunc.equals("parabroke"))
return (sq(p[0])/2 - sq(p[0] - (c % (2*p[0])))) / p[0];
else if(pFunc.equals("parabolic"))
return (sq(p[0]) - sq(p[0] - (c % (2*p[0])))) / p[0];
return c;
}
//--Drawing--
String mode = "circlewaves";
int lastbg = 0;
float rand = random(1);
boolean toggle = false;
//phase has the current phase corresponding to count
//tphase has the phase corrsponding to tmpCount
void drawSomething(float x, float y)
{
toggle = !toggle;
float tphase = phase(tmpCount);
if(mode.equals("googly"))
drawGooglyEyes(x, y, tphase);
else if(mode.equals("twirlers"))
drawTwirlers(x, y, tphase);
else if(mode.equals("circlewaves"))
drawCircleWaves(x, y, tphase);
else if(mode.equals("noise"))
drawNoise(x, y, tphase);
else if(mode.equals("ocircles"))
drawOffsetCircles(x, y, tphase);
else if(mode.equals("bcurves"))
drawBezierCurves(x, y, tphase);
else if(mode.equals("signalavg"))
drawSignalAverage(x, y, tphase);
else if(mode.equals("deriv"))
drawDerivatives(x, y, tphase);
}
//function and derivative helpers
float f(float x)
{
//to define a periodic function with a specified period
float period = 2*PI;
if(period != 0)
{
x %= period;
if(x < 0)
x -= period*floor(x/period);
}
return sin(x*1.8);
}
float dnf(float x, int n)
{
float incr = 0.1;
if(n == 1)
return (f(x+incr) - f(x-incr)) / (2*incr);
return (dnf(x+incr, n-1) - dnf(x-incr, n-1)) / (2*incr);
}
void drawDerivatives(float x, float y, float tphase)
{
float amp1 = 10;
float amp2 = 10;
float radius = max(5, spacing*0.75);
float input = x*0.01 + tphase-p[0]/2;
fill((rand*255 + 80*tphase/p[0]) % 255, 230, 230);
ellipse(x, y - amp1*f(input), radius, radius);
for(int i = 1; i < 5; i++)
{
//amp2 = 50;
fill((40*i + rand*255 + 80*tphase/p[0]) % 255, 230, 230);
ellipse(x, y - amp2*dnf(input, i), radius, radius);
}
}
void drawSignalAverage(float x, float y, float tphase)
{
//wave velocity + amplitudes
float v1 = 4;// * 1/(1 + tphase/p[0]);
float v2 = 10;// * (1 + tphase/p[0]);
float a1 = 120;
float a2 = 70;//*(x/(w/2));//*sin(PI/w*(x-w/2));
float radius = max(5, spacing*0.75);
//"angular velocity", phase dependence on time
float w1 = -1*w/2*tphase/p[0];
float w2 = 1*w/2*tphase/p[0];
//these phases are only dependent on one coord to achieve the desired effect.
float tp1 = (x - w1) * 0.01 * v1;
float tp2 = (x - w2) * 0.01 * v2;
fill((rand*255 + 80*tphase/p[0]) % 255, 230, 230);
ellipse(x, y + sin(tp1)*a1*(tphase/p[0]) + sin(tp2)*a2*(1 - tphase/p[0]), radius, radius);
//ellipse(x, y + 0.5*sin(tp1)*a1*(tphase/p[0]) + noise(1, tp2*0.01)*sin(tp2)*a2*(1 - tphase/p[0]), radius, radius);
}
void drawBezierCurves(float x, float y, float tphase)
{
boolean stroke = true;
int fillAlpha = 30;
boolean bnw = false;
int mode = 3; //anchor systems
if(fillAlpha > 0)
fill((rand*255 + sin(tphase)*40) % 255, 200, 200, fillAlpha);
else
noFill();
if(stroke)
{
strokeWeight(1);
if(bnw)
stroke(255);
else
stroke((rand*255 + sin(tphase)*40) % 255, 200, 200);
}
else
noStroke();
//radial / radialr
if(y == 0)
{
PVector anchor1, anchor2, anchor3, anchor4;
anchor1 = new PVector(x, -h/2);
anchor2 = new PVector(x, h/2);
switch(mode)
{
default:
case 1:
anchor3 = new PVector(x + 0*w*sin(tphase), y + 0*h/4);
anchor4 = new PVector(x - 0.2*w*sin(tphase), y - h/4);
break;
case 2:
anchor3 = new PVector(x + 0.5*w, y + h/2*sin(tphase));
anchor4 = new PVector(x - 0.5*w, y - h/2*sin(tphase));
break;
case 3:
anchor3 = new PVector(x + w/2*cos(tphase), y + h/2*sin(tphase));
anchor4 = new PVector(x - w/2*cos(tphase), y - h/2*sin(tphase));
break;
}
bezier(anchor1.x, anchor1.y, anchor3.x, anchor3.y, anchor4.x, anchor4.y, anchor2.x, anchor2.y);
}
}
//pretty much only looks good on radialr / radialnum=1
void drawOffsetCircles(float x, float y, float tphase)
{
boolean bnw = false;
boolean phaseColor = true;
boolean fixedOffDir = false;
PVector v = new PVector(x, y);
PVector v2 = new PVector(x, y);
v2.setMag(spacing);
if(fixedOffDir)
{
v2.rotate(2*PI*rand);
v2.mult(tphase);
}
else
{
v2.rotate(tphase);
v.mult(sqrt(0.5));
}
if(bnw)
{
if(toggle)
fill(0, 0, 0);
else
fill(0, 0, 255);
}
else
{
if(phaseColor)
fill((255*rand + map(sin(tphase), -1, 1, 0, 60) + 255) % 255, 200, 200);
else
{
if(toggle)
fill(rand*255, 200, 240);
else
fill((rand*255 + rand*991) % 255, 200, 200);
}
}
ellipse(v2.x, v2.y, v.mag(), v.mag());
}
//just a working name, doesn't actually use noise all that much
void drawNoise(float x, float y, float tphase)
{
boolean rectangle = false;
boolean phaseColor = true;
float threshold = 0.35; //returns if noise < threshold
PVector v = new PVector(x, y);
//v.rotate(radians(45));
//v.rotate(tphase);
if(phaseColor)
fill((255*rand + map(tphase, 0, p[0], 0, 40) + 255) % 255, 240, 240);
else
fill((255*(rand + noise(x*0.005, y*0.005, tphase))) % 255, 240, 240);
if(noise(v.x, v.y, tphase*0.05) < threshold)
return;
if(rectangle)
rect(v.x-spacing/2, v.y-spacing/2, spacing, spacing);
else
ellipse(v.x, v.y, spacing/2, spacing/2);
}
void drawCircleWaves(float x, float y, float tphase)
{
//either multiply v or add another sin/cos thing on to the coords
boolean multiplyv = true; //true: rotate / no rotate
boolean rotate = true;
//in order of priority
boolean lowColor = false;
boolean bnw = false;
boolean distfill = true;
PVector v = new PVector(x, y);
float r = spacing/2;
if(bnw)
fill(0);
else if(lowColor)
{
if(tphase <= p[0]/2)
fill(255*rand, 200, 255);
else
fill((255*rand + 40) % 255, 200, 255);
}
else if(distfill)
fill((255*rand + map(v.mag(), 0, w/sqrt(2), 0, 80)) % 255, 200, 255);
else
fill((255*rand + map(tphase, 0, p[0], 0, 40)) % 255, 200, 255);
if(rotate)
v.rotate(tphase/p[0]*50.0/v.mag());
if(multiplyv)
{
v.mult(0.9+0.2*tphase/p[0]);
ellipse(v.x, v.y, r, r);
}
else
ellipse(v.x+spacing*cos(tphase), v.y+spacing*sin(tphase), r, r);
}
//draws a circle made out of 'slices' of equal size, each of a different color (in c)
//rotates based on phase
void drawTwirlers(float x, float y, float tphase)
{
//params
boolean useFocus = false;
float focusRadius = w/4;
PVector focus = new PVector(w/4*cos(tphase) - x, h/2*sin(tphase) - y);
boolean coloredBG = false;
float r = spacing*0.425;
int cseed = 140;//(int)(255*noise(-0.2, 0.4)); //constant at run time
int cspacing = 30;
int ncolors = 3;
color[] c = new color[ncolors];
for(int i = 0; i < ncolors; i++)
c[i] = color((cseed+i*cspacing) % 255, 200, 250);
float ang;
fill(c[0]);
if(coloredBG && count != lastbg)
{
background(cseed, 200, 250);
lastbg = count;
}
else if(!coloredBG)
ellipse(x, y, r, r);
for(int i = 1; i < ncolors; i++)
{
fill(c[i]);
if(useFocus)
ang = focus.heading() - PI/(ncolors*2) + TAU*i/ncolors;
else
ang = (tphase+i*TAU/ncolors) % TAU;
arc(x, y, r, r, ang, ang+TAU/ncolors, PIE);
}
}
void drawGooglyEyes(float x, float y, float tphase)
{
//looking at same point
boolean useFocus = true;
float focusRadius = w/4;
PVector focus = new PVector(w/4*cos(tphase) - x, h/2*sin(tphase) - y);
boolean bnw = true; //black and white
boolean eyes = true; //white circles
//eyeballs
float r = spacing/3;
if(eyes)
{
fill(255);
//fill(255.0/(2*PI)*tphase, 200, 200);
ellipse(x, y, r, r);
}
//googly eye interior:
//smaller circles, offset colour
float r2 = r/2;
float dcolor = 40;
PVector v2 = new PVector(r2, 0);
if(useFocus)
v2.rotate(focus.heading());
else
v2.rotate(tphase);
if(bnw)
fill(0);
else
fill((dcolor + 255.0/(2*PI)*tphase) % 255, 200, 200);
ellipse(x+v2.x, y+v2.y, r2, r2);
}
//framework.pde
int w, h;
int fps = 60;
int count = 0;
int tmpCount;
float phase;
void setup()
{
size(500, 500);
w = width;
h = height;
frameRate(fps);
background(0);
initialize();
}
void initialize()
{
colorMode(HSB);
ellipseMode(RADIUS);
fill(255);
noStroke();
}
void draw()
{
if(backgroundFreq == 0 || (backgroundFreq > 0 && count % backgroundFreq == 0))
background(bg);
count++;
drawMain();
if(filename.length() > 0 && count % saveFreq == 0)
save(filename + filename(count/saveFreq) + ".png");
}
void drawMain()
{
int dist;
translate(w/2, h/2);
phase = phase(count);
tmpCount = count;
//print(phase + " " + count + "\t");
//e.g. concentric circles
//increments once per circle
if(scheme.equals("radial"))
drawRadial();
//reverse order
else if(scheme.equals("radialr"))
drawRadialReverse();
else if(scheme.equals("radialf"))
drawRadialFull();
//flood-fill style grid
else if(scheme.equals("recursive"))
drawRecursive();
//regular grid
else if(scheme.equals("grid"))
{
if(grid.equals("hex"))
drawHexGrid();
else if(grid.equals("xy"))
drawGrid();
}
}
void drawRadial()
{
//drawSomething(0, 0);
for(int i = 0; i < radialnum; i++)
{
tmpCount = count;
for(float r = 0; r*r < (w*w + h*h); r += spacing)
{
tmpCount += tIncr;
drawSomething(r, 0);
}
rotate(TAU/radialnum);
}
}
void drawRadialFull()
{
//drawSomething(0, 0);
for(int i = 0; i < radialnum; i++)
{
tmpCount = count;
for(float r = 0; r*r < (w*w + h*h); r += spacing)
{
drawSomething(r, 0);
tmpCount += tIncr;
}
tmpCount = count;
for(float r = spacing; r*r < (w*w + h*h); r += spacing)
{
tmpCount -= tIncr;
drawSomething(-r, 0);
}
rotate(TAU/radialnum);
}
}
void drawRadialReverse()
{
int mult = 0;
for(int i = 0; i < radialnum; i++)
{
tmpCount = count;
mult = (int)floor(sqrt(w*w + h*h) / spacing);
for(float r = spacing*mult; r > 0; r -= spacing)
{
drawSomething(r, 0);
tmpCount += tIncr;
}
rotate(TAU/radialnum);
}
tmpCount += tIncr;
drawSomething(0, 0);
}
//does grid, but recursively so tmpcount increments differently
void drawRecursive()
{
boolean[][] drawn;
int dim;
if(grid.equals("xy"))
{
dim = 1 + 2*(int)floor(h/2/spacing);
drawn = new boolean[dim][dim];
drawGridR(false, drawn, dim/2, dim/2);
}
else if(grid.equals("hex"))
{
//because of 30 degree angle, we need 2x as many things
dim = 1 + 2*(int)floor(h/spacing);
drawn = new boolean[dim][dim];
drawGridR(true, drawn, dim/2, dim/2);
}
}
void drawGridR(boolean hex, boolean[][] drawn, int x, int y)
{
if(x < 0 || x >= drawn.length || y < 0 || y >= drawn[x].length || drawn[x][y])
return;
tmpCount += tIncr;
drawn[x][y] = true;
if(hex)
{
PVector p = hexPos(x-drawn.length/2, y-drawn[x].length/2, 0);
drawSomething(p.x, p.y);
}
else
drawSomething(spacing*(x-drawn.length/2), spacing*(y-drawn[x].length/2));
drawGridR(hex, drawn, x+1, y);
drawGridR(hex, drawn, x, y+1);
drawGridR(hex, drawn, x-1, y);
drawGridR(hex, drawn, x, y-1);
}
void drawHexGrid()
{
PVector p = new PVector(0, 0);
drawSomething(p.x, p.y); //centred one
tmpCount += tIncr;
//# of segments is h/2 / (spacing*sin30),
for(int i = 1; i < h / spacing; i++)
{
for(int j = 0; j < i; j++)
{
p = hexPos(i-j, -i, j);
drawSomething(p.x, p.y);
p = hexPos(-i+j, i, -j);
drawSomething(p.x, p.y);
p = hexPos(-i, j, i-j);
drawSomething(p.x, p.y);
p = hexPos(i, -j, -i+j);
drawSomething(p.x, p.y);
p = hexPos(j, i-j, -i);
drawSomething(p.x, p.y);
p = hexPos(-j, -i+j, i);
drawSomething(p.x, p.y);
}
tmpCount += tIncr;
}
}
void drawGrid()
{
for(float i = -w/2; i <= w/2; i += spacing)
{
for(float j = -h/2; j <= h/2; j += spacing)
{
//add option to change from taxi-cab distance to cartesian distance?
tmpCount = count + tIncr * (int)round(abs(i/spacing) + abs(j/spacing));
drawSomething(i, j);
}
}
}
PVector hexPos(int x, int y, int z)
{
PVector vdist = new PVector(spacing*x, 0);
PVector vdist2 = new PVector(spacing, 0);
vdist2.rotate(2*PI/3);
y += x;
vdist2.mult(y);
vdist.add(vdist2);
return vdist;
}
String filename(int i)
{
if(i > 26)
return 'z' + filename(i - 26);
return ""+(char)('a'+i-1);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment