fullscreen
NoiseBasedWarping.pde_1_DomainWarper.pde_2_DomainStrategy.pde
/*--------------------------------------------------------------------------------------------------------------------------------
Noise Based Warping Explorer
Ale González · 2012 · Dominio Público | Public Domain
----------------------------------------------------------------------------------------------------------------------------------
A explorer of noise based warping, as explained here: http://www.iquilezles.org/www/articles/warp/warp.htm
And as pointed out here: http://amnonp5.wordpress.com/2012/06/17/playing-with-glsl-in-processing/
----------------------------------------------------------------------------------------------------------------------------------
The process as described basically by Iñigo:
"Say you have some geometry or image defined as a function of space. For geometry, that would be a function of the form f(x,y,z)
and for an image it would be f(x,y). We can just write both cases more compactly as f(p), where p is the position in space for
which we can evaluate the volumetric density that will define our (iso)surface or image color. Warping simply means we distort
the domain with another function g(p) before we evaluate f. Basically, we replace f(p) with f(g(p)). g can be anything, but often
we want to distort the image of f just a little bit with respect its regular behabiour. Then, it makes sense to have g(p) being
just the identiy plus a small arbitary distortion h(p), or in other words,
g(p) = p + h(p),
meaning we will be computing
f( p + h(p) )
This technique is really powerful and allows you to shape apples, buildings, animals or any other thing you might imagine. For the
purpose of this article, we are going to work only with fbm based patterns, both for f ang h. This will produce some abstract but
beautiful images with a pretty organic quality to them."
----------------------------------------------------------------------------------------------------------------------------------
Thanks to both, Íñigo and Amnon, for dedicating so much time to share. ;-)
----------------------------------------------------------------------------------------------------------------------------------
Font: 'sw!ft' by orgdot, as shared on dafont, http://www.dafont.com/orgdot.d518. Thanks again!
--------------------------------------------------------------------------------------------------------------------------------*/
/*
Global variables and objects
*/
//Canvas size
final int W= 500, H= 500;
//A font to output frameRate
PFont theFont;
//Default level of recursion and text properties
int level = 1,
txt_x = 25,
txt_y = 25,
txt_c = 0xddffff00,
txt_s = 10;
//default values to instantiate the noise domain and warper objects
float
perlin_x= .01,
perlin_y= .01,
simplex_x= .0045,
simplex_y= .0045,
min_V = -1f,
max_V = 1f,
vK=4f;
boolean
moving = false,
hud = true;
DomainStrategy domain;
DomainWarper warper;
/*
Main methods
*/
void setup(){
size (W,H,P2D);
cursor (CROSS);
domain = new Perlin (255, perlin_x, perlin_y);
warper = new DomainWarper (level, min_V, max_V, vK);
textFont (loadFont("SWFTv01-16.vlw"), txt_s);
fill (txt_c);
}
void draw(){
loadPixels();
if (moving) {warper.x2--; warper.y1++;}
//Iterate over screen pixels
for (int y=0, yW=0; y<H; y++, yW+=W) {
for(int x=0; x<W; x++) {
//And use domain values (v) to set grayscale values to them, using the domain function provided
int v = (int) warper.getValue (x,y, domain);
pixels[x+yW] = v<<16|v<<8|v;
}
}
updatePixels();
if (hud) text ("Level: " + level + " - K: " + vK + " - Framerate: " + nfc(frameRate,2), txt_x, txt_y);
}
/*
Some interaction here:
· Move mouse to shift the warping, acting onto one of the vectors that define the parent of the tree structure
· Keys:
'1' : use Perlin noise based domain
'2' : use Simplex noise based domain
'Up' : increase level of recursion (up to 5)
'Down' : decrease level of recursion (down to 0).
*/
void mousePressed(){
switch (mouseButton) {
case (LEFT):
moving= !moving;
break;
case (RIGHT):
hud = !hud;
break;
}
}
void keyPressed(){
if (key!=CODED) {
switch (key) {
case (KeyEvent.VK_1) : //key '1' constant value as implemented in KeyEvent Java class
domain = new Perlin (255, perlin_x, perlin_y);
break;
case (KeyEvent.VK_2) : //...
domain = new Simplex (255, simplex_x, simplex_y);
break;
}
} else {
if (keyCode==UP && level<10) warper= new DomainWarper (++level, min_V, max_V, vK);
else if (keyCode==DOWN && level>1) warper= new DomainWarper (--level, min_V, max_V, vK);
else if (keyCode==RIGHT && vK<=10) warper= new DomainWarper (level, min_V, max_V, ++vK);
else if (keyCode==LEFT && vK>=0) warper= new DomainWarper (level, min_V, max_V, --vK);
}
}

/*
A domain-based compound warper.
Each possible level of recursion is encapsulated in a warper object inside a unary tree structure
*/
class DomainWarper {
//Each warping level is 'described' by two points, defined by the vectors: v1{x1,y1} and v2{x2,y2}
//k is a constant that scales the effect of the correspondent recursion level
float x1, y1, x2, y2, k;
//Further depth level, only accesible from its direct parent
private DomainWarper child;
/*
Constructors
*/
//Create a custom warper from given parameters
DomainWarper (float x1, float y1, float x2, float y2, float k) {
this.x1=x1; this.y1=y1;
this.x2=x2; this.y2=y2;
this.k=k;
child = null;
}
//Create a warper of given depth, defined at each level by a given k and random vectors,
//using a float range minV<-->maxV.
DomainWarper (int level, float minV, float maxV, float k){
x1= random (minV, maxV); y1= random (minV, maxV);
x2= random (minV, maxV); y2= random (minV, maxV);
this.k=k;
if(--level>0) this.addLevel (new DomainWarper(level, minV, maxV, k));
}
/*
Methods
*/
/*
To add recursively a new level, defined by a Warper child:
If the node is a leaf, add it the new child, else traverse its children until a leaf is found and the warper is finally added to this one
*/
void addLevel (DomainWarper rw) {
if (child==null) child=rw; else child.addLevel (rw);
}
/*
This method calculates recursively the sum of associated vectors of parent node and its children. The associated vector of a warper object is
defined by calculating its coordinates as the result of applying the domain algorithm to p{x,y}+v1 and p+v2, scaled by the k coefficient afterwards.
*/
PVector getLevelVector (int x, int y, DomainStrategy domain) {
PVector levelVector = new PVector (domain.getValue (x+x1, y+y1) *k, domain.getValue (x+x2, y+y2) *k);
if (child!=null) levelVector.add (child.getLevelVector (x, y, domain));
return levelVector;
}
/*
The main output of the object is this value, result of applying the domain algorithm to
the associated vector of the parent of the tree
*/
float getValue (int x, int y, DomainStrategy domain) {
PVector v = getLevelVector (x, y, domain);
return domain.getValue (x+v.x, y+v.y);
}
}

/*
Interface DomainStrategy.
I have preferred to encapsulate different domain math algorithms in objects
implementing this interface in order to be easily extended, keeping clean
the warper class.
*/
interface DomainStrategy {
float getValue (float x, float y);
}
/*
This one uses Perlin noise as ported to P5 by Toxi from the original implementation
by german demo group Farbrausch: http://www.farb-rausch.de/fr010src.zip
Original implementation: Ken Perlin, 1984
*/
class Perlin implements DomainStrategy {
float magnitude, kX, kY;
Perlin (float magnitude, float kX, float kY) {
this.magnitude = magnitude;
this.kX = kX; this.kY = kY;
}
float getValue (float x, float y) {
return magnitude * noise (kX*x, kY*y);
}
}
/*
Simplex noise as implemented by Toxi in his Math library, and as described in
this paper by Stefan Gustavson: http://staffwww.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf.
Original implementation: Ken Perlin, 2001
http://mrl.nyu.edu/~perlin/paper445.pdf Siggraph 2002 paper explaining the new algorithm
http://mrl.nyu.edu/~perlin/noise/ the raw code -- quite hardcoded, compact, beautiful... and absolutely impenetrable :-)
*/
import toxi.math.noise.SimplexNoise;
class Simplex implements DomainStrategy {
float magnitude, kX, kY;
Simplex (float magnitude, float kX, float kY) {
this.magnitude = magnitude;
this.kX = kX; this.kY = kY;
}
float getValue (float x, float y) {
//Simplex yields values from -1d to 1d unlike standard P5 noise implementation (that gives values from 0 to 1f),
//so this object yields the absolute value casted to float:
return magnitude * abs((float) SimplexNoise.noise(kX*x, kY*y));
}
}