import java.awt.image.*;
import java.awt.Graphics;
import java.awt.Color;
import java.awt.Event;
import java.awt.Image;
import java.awt.Font;
//---- Node ------------------------------------------------------------------//
class Node
{
//- Constants representing each type of operation
static final int NUM = 0;
static final int ADD = 1;
static final int SUB = 2;
static final int MUL = 3;
static final int DIV = 4;
static final int LOG = 5;
static final int SIN = 6;
static final int COS = 7;
static final int MOD = 8;
static final int ROUND = 9;
static final int MIN = 10;
static final int MAX = 11;
static final int LUT0 = 12;
static final int LUT1 = 13;
static final int LUT2 = 14;
static final int VARX = 15;
static final int VARY = 16;
static final int XSQYSQ = 17;
static final int NSQNSQ = 18;
static final int XELSEY = 19; // = if X > Y then X else Y
static final int SINCOS = 20; // = sin(n0(x,x)) + cos(n1(y,y))
static final int ROTXY = 21; // = n0(X',Y') where (X',Y') is (X,Y) rotated by value radians
static final int ABS = 22; // = Abs(n0)
static final int EVEN = 23; // = n1(y,y) if n0(x,y) is even and n2(x,x) if it is odd
static final int BLUR = 24; // average of pixel and four around it at a distance of 'value'
static final int EMBOSS = 25; // emboss pixel with four around it at a distance of 'value'
static final int NUMTYPES = 26;
static int[] tp = {0, 2, 2, 2, 2, -1, 1, 1, -2, 1, 2, 2, 3, 3, 3, 0, 0, 1, 2, 2, 2, 1, 1, 3, 1, 1};
static int[] tq = {8, 12, 12, 10, 7, 0, 12, 12, 0, 15, 12, 12, 10, 10, 10, 8, 8, 10, 10, 12, 10, 15, 4, 2, 0, 0};
// static int[] tq = {1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0};
// static int[] tq = {1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
private int type = 0;
private float value = 0;
static int lasttype = -1;
private Node[] n = null;
//---- evaluate
public float evaluate(float x, float y)
{
switch(type)
{
case NUM : return value;
case ADD : return value * (n[0].evaluate(x,y) + n[1].evaluate(x,y));
case SUB : return value * (n[0].evaluate(x,y) - n[1].evaluate(x,y));
case MUL : return value * (n[0].evaluate(x,y) * n[1].evaluate(x,y));
case DIV : return value * (div(n[0].evaluate(x,y), n[1].evaluate(x,y)));
case LOG : return log(n[0].evaluate(x,y));
case SIN : return value * (float)Math.sin(n[0].evaluate(x,y));
case COS : return value * (float)Math.cos(n[0].evaluate(x,y));
case MOD : return mod(n[0].evaluate(x,y), n[1].evaluate(x,y));
case ROUND : return value * (float)(int)(n[0].evaluate(x,y)+0.5);
case MIN : return value * min(n[0].evaluate(x,y), n[1].evaluate(x,y));
case MAX : return value * max(n[0].evaluate(x,y), n[1].evaluate(x,y));
case LUT0 : return value * n[0].evaluate(n[1].evaluate(x,y), n[2].evaluate(x,y));
case LUT1 : return value * n[1].evaluate(n[2].evaluate(x,y), n[0].evaluate(x,y));
case LUT2 : return value * n[2].evaluate(n[0].evaluate(x,y), n[1].evaluate(x,y));
case VARX : return value + x;
case VARY : return value + y;
case XSQYSQ: return (x*x+y*y)*n[0].evaluate(x,y);
case NSQNSQ: { float n0 = n[0].evaluate(x,y), n1 = n[1].evaluate(x,y); return n0*n0+n1*n1; }
case XELSEY: return n[0].evaluate(x,y) > n[1].evaluate(x,y) ? value - x : value - y;
case SINCOS: return value * (float)(Math.sin(n[0].evaluate(x,x)) + Math.cos(n[1].evaluate(y,y)));
case ROTXY : { float n1 = value, s = (float)Math.sin(n1), c = (float)Math.cos(n1); return n[0].evaluate(x*c-y*s,x*s+y*c); }
case ABS : { float n0 = n[0].evaluate(x,y); return n0 > 0 ? n0 : -n0; }
case EVEN : return (((int)n[0].evaluate(x,y)) & 2) == 0 ? n[1].evaluate(y,y) : n[2].evaluate(x,x);
case BLUR : return value + 0.2f*(n[0].evaluate(x,y) + n[0].evaluate(x-value*0.2f,y) + n[0].evaluate(x+value*0.2f,y) + n[0].evaluate(x,y-value*0.2f) + n[0].evaluate(x,y+value*0.2f));
case EMBOSS: return value + 5.0f*n[0].evaluate(x,y) - n[0].evaluate(x-value*0.2f,y) - n[0].evaluate(x+value*0.2f,y) - n[0].evaluate(x,y-value*0.2f) - n[0].evaluate(x,y+value*0.2f);
}
return (float)value;
}
//---- toString
public String toString()
{
switch(type)
{
case NUM : return ""+value;
case ADD : return "("+n[0]+" + "+n[1]+")";
case SUB : return "("+n[0]+" - "+n[1]+")";
case MUL : return "("+n[0]+" * "+n[1]+")";
case DIV : return "("+n[0]+" / "+n[1]+")";
case LOG : return "log("+n[0]+")";
case SIN : return "sin("+n[0]+")";
case COS : return "cos("+n[0]+")";
case MOD : return "("+n[0]+" % "+n[1]+")";
case ROUND : return "round("+n[0]+")";
case MIN : return "min("+n[0]+", "+n[1]+")";
case MAX : return "max("+n[0]+", "+n[1]+")";
case LUT0 : return "lut0("+n[0]+", "+n[1]+", "+n[2]+")";
case LUT1 : return "lut1("+n[0]+", "+n[1]+", "+n[2]+")";
case LUT2 : return "lut2("+n[0]+", "+n[1]+", "+n[2]+")";
case VARX : return "X";
case VARY : return "Y";
case XSQYSQ: return "((x*x+y*y) * "+n[0]+")";
case XELSEY: return "("+n[0]+" > "+n[1]+" ? X : Y)";
case SINCOS: return "(sin(Y=X, "+n[0]+") + cos(X=Y, "+n[1]+"))";
case ROTXY : return "ROTXY("+n[0]+")";
case ABS : return "abs("+n[0]+")";
case EVEN : return "EVEN("+n[0]+","+n[1]+","+n[2]+")";
case BLUR : return "BLUR("+n[0]+")";
case EMBOSS: return "EMBOSS("+n[0]+")";
}
return "()";
}
//---- Node - creates a blank node
private Node()
{
}
//---- Node - creates a tree with the given depth
public Node(int depth)
{
// Stop the same node appearing twice in a row..
int tempp = -1;
if(lasttype > -1) { tempp = tp[lasttype]; tp[lasttype] = -1; }
// Pick a random node type - if we are at the bottom depth only allow types with no parameters
if(depth > 2)
{
int q=0;
for(type=0; type<NUMTYPES; type++) if(tp[type] > 0) { q += tq[type]; }
q = (int)(Math.random()*q);
for(type=0; q>tq[type] || tp[type] == 0; type++) if(tp[type] > 0) { q -= tq[type]; }
}
else if(depth > 0)
{
int q=0;
for(int type=0; type<NUMTYPES; type++) if(tp[type] >= 0) { q += tq[type]; }
q = (int)(Math.random()*q);
for(type=0; q>tq[type] || tp[type] < 0; type++) if(tp[type] >= 0) { q -= tq[type]; }
}
else
{
int q=0;
for(type=0; type<NUMTYPES; type++) if(tp[type] == 0) { q += tq[type]; }
q = (int)(Math.random()*q);
for(type=0; q>tq[type] || tp[type] != 0; type++) if(tp[type] == 0) { q -= tq[type]; }
}
if(tempp > -1) tp[lasttype] = tempp;
lasttype = type;
// Pick a random value
value = (float)(Math.random()*2.0-1.0);
// Make the correct number of subnodes
if(tp[type] > 0)
{
n = new Node[tp[type]];
for(int i=0; i<tp[type]; i++) n[i] = new Node(depth-1);
}
else
{
n = null;
}
}
//---- numNodes - counts the nodes in this node's tree
public int numNodes()
{
int t = 1;
for(int i=0; i<tp[type]; i++) t += n[i].numNodes();
return t;
}
//---- getNode - gets a node from the tree
int getNode_index;
private Node getNode(Node nn)
{
if(getNode_index <= 0) return nn;
getNode_index--;
for(int i=0; i<nn.tp[nn.type]; i++)
{
Node getn = getNode(nn.n[i]);
if(getn != null) return getn;
}
return null;
}
public Node getNode(int index)
{
getNode_index = index;
Node nn = getNode(this);
return nn == null ? this : nn;
}
//---- setNode - modifies a node in the tree
public int setNode(int index, Node nn)
{
if(index <= 0) return -1;
int t = 1;
for(int i=0; i<tp[type]; i++)
{
if(index == t) { n[i] = nn; return -1; }
int k = n[i].setNode(index-t, nn);
if(k == -1) return -1;
t += k;
}
return t;
}
//---- makeCopy - returns a copy of this node tree
public Node makeCopy()
{
Node nn = new Node();
nn.type = type;
nn.value = value;
if(tp[type] <= 0)
{
nn.n = null;
}
else
{
nn.n = new Node[tp[type]];
for(int i=0; i<tp[type]; i++) nn.n[i] = n[i].makeCopy();
}
return nn;
}
//---- randomValues
public void randomValues()
{
value = (float)(Math.random()*2.0-1.0);
for(int i=0; i<tp[type]; i++) n[i].randomValues();
}
//---- min, max, div, log, mod
private float max(float x, float y) { return x > y ? x : y; }
private float min(float x, float y) { return x < y ? x : y; }
private float div(float x, float y) { return y > -0.000001 && y < 0.000001 ? 0 : x/y; }
private float log(float x) { return x == 0 ? 0 : x < 0 ? (float)Math.log(-x) : (float)Math.log(x); }
private float mod(float x, float y) { return y > -0.000001 && y < 0.000001 ? 0 : x-((float)((int)(x/y)))*y; };
}
//---- Entity ----------------------------------------------------------------//
class Entity
{
private Node n;
//- Entity - creates a new simple random entity
public Entity()
{
n = new Node(3);
}
//- Entity - creates an entity from the given node tree
public Entity(Node n)
{
if(n == null) { int i = 0, j= 5; j=5/i; }
this.n = n.makeCopy();
}
//- getPixel
public float getValue(float x, float y)
{
return n.evaluate(x, y);
}
//- mutate - mutates this entity
public void mutate()
{
float action = (float)Math.random();
if(action < 0.25)
{
// 25% chance of swapping two nodes in the tree around
int r1 = (int)(Math.random()*(n.numNodes()-1)+1);
int r2 = (int)(Math.random()*(n.numNodes()-1)+1);
Node n1 = n.getNode(r1).makeCopy();
Node n2 = n.getNode(r2).makeCopy();
n.setNode(r1, n2);
n.setNode(r2, n1);
}
else if(action < 0.45)
{
// 20% chance of overwriting part of the tree with a new tree
int r = (int)(Math.random()*(n.numNodes()-1)+1);
Node nn = new Node((int)(Math.random()*4+1));
n.setNode(r, nn);
}
else if(action < 0.70)
{
// 25% chance that a bit of tree will be inserted somewhere
int r = (int)(Math.random()*(n.numNodes()-1)+1);
Node nn = new Node((int)(Math.random()*2+1));
int rr = (int)(Math.random()*(nn.numNodes()-1)+1);
nn.setNode(rr, n.getNode(r).makeCopy());
n.setNode(r, nn);
}
else if(action < 0.85)
{
// 15% chance that the insertion will happen at the top of the tree so root node is moved inside a new tree
Node nn = new Node((int)(Math.random()*2+1));
int rr = (int)(Math.random()*(nn.numNodes()-1)+1);
nn.setNode(rr, n.makeCopy());
n = nn;
}
else if(action < 1.00)
{
// 15% chance that a subtree will randomise it's value variables
int r = (int)(Math.random()*n.numNodes());
if(r== 0) { n.randomValues(); } else { n.getNode(r).randomValues(); }
}
else
{
// 0% chance that the tree will be replaced by a totally new tree
Node nn = new Node((int)(Math.random()*4+2));
n = nn;
}
}
//- getMutation - returns a mutation of this entity
public Entity getMutation()
{
Entity e = new Entity(n);
int r = (int)(Math.random()*4)+1;
for(int i=0; i<r; i++) e.mutate();
return e;
}
//- toString
public String toString()
{
return ""+n;
}
}
//---- TextureEvolver --------------------------------------------------------//
public class TextureEvolver extends java.applet.Applet implements Runnable
{
//- Data
// Mouse variables
int mouse_oldx = 0, mouse_oldy = 0;
int mouse_x = 0, mouse_y = 0;
boolean mouse_drag = false;
// Thread variables
Thread runThread;
int drawx, drawy;
int drawx2, drawy2;
int doing = 0;
// Graphics variables
int im_w = 600, im_h = 320;
Graphics img;
Image im;
// Palette
Color[] drawcol;
// Entities
Entity[] ent;
Entity entParent, entParent2;
//- Init
public void init()
{
System.out.println("LKS Texture Evolver v0.1");
drawcol = new Color[256];
int s = 1;
double rr=0, gg=0, bb=0;
for(int i=0; i<256; i++)
{
s--;
if(s <= 0) {
rr = 0.5 + 2.0*(((i*i*i-5)&15)/15.0);
gg = 0.5 + 2.0*(((3*i*i+9)&15)/15.0);
bb = 0.5 + 2.0*(((i*i-i+1)&15)/15.0);
s = 4 + ((3*i*i-8*i*i*i+17*i)&31);
}
float r = (float)(0.5 + 0.49*Math.sin(rr*i*3.14159/32.0));
float g = (float)(0.5 + 0.49*Math.sin(gg*i*3.14159/32.0));
float b = (float)(0.5 + 0.49*Math.sin(bb*i*3.14159/32.0));
if(r < 0f) r = 0f; if(g < 0f) g = 0f; if(b < 0f) b = 0f;
if(r > 1f) r = 1f; if(g > 1f) g = 1f; if(b > 1f) b = 1f;
// drawcol[i] = new Color(r, g, b);
drawcol[i] = new Color((float)i/256f, (float)i/256f, (float)i/256f);
}
im = createImage(im_w, im_h);
img = im.getGraphics();
img.setColor(Color.blue);
img.fillRect(0, 0, im_w, im_h);
for(int i=0; i<256; i++)
{
img.setColor(drawcol[i]);
img.fillRect(i*2, 320-8, 2, 8);
}
//- Create entities
entParent = new Entity();
entParent2 = new Entity();
ent = new Entity[12];
for(int i=0; i<12; i++) ent[i] = new Entity();
doing = 3;
}
public void start()
{
if(runThread == null)
{
runThread = new Thread(this);
runThread.start();
}
}
public void stop()
{
if(runThread != null)
{
runThread.stop();
runThread = null;
}
}
private void startDrawChildren()
{
doing &= ~1;
img.setColor(Color.black);
img.fillRect(256+16, 0, 80*4, 80*3);
repaint();
for(int i=0; i<12; i++) ent[i] = entParent.getMutation();
drawx = 0; drawy = 0;
doing |= 1;
}
private void startDrawParent(int ix, int iy)
{
doing &= ~2;
int i = ix+iy*4;
System.out.print("\n\nEntity "+i+":\n\n"+ent[i]+"\n\n");
img.setColor(Color.black);
img.fillRect(0, 0, 256, 256);
repaint();
entParent = ent[i];
drawx2 = 0; drawy2 = 0;
doing |= 2;
}
private void drawChildren()
{
if(drawy < 64)
{
int cc = -1, c;
for(int i=0; i<12; i++)
{
int ox = (i&3)*80+256+80-64;
int oy = (i>>2)*80;
for(drawx = 0; drawx < 64; drawx++)
{
c = ((int)(256f*ent[i].getValue(drawx*(1f/64f), drawy*(1f/64f))))&255;
if(cc != c)
{
img.setColor(drawcol[c]);
cc = c;
}
img.fillRect(drawx + ox, drawy + oy, 1, 1);
}
}
drawy ++;
} else { doing &= ~1; }
repaint();
}
private void drawParent()
{
if(drawy2 < 256)
{
int cc = -1, c;
for(drawx2 = 0; drawx2 < 256; drawx2++)
{
c = ((int)(256f*entParent.getValue(drawx2*(1f/256f), drawy2*(1f/256f))))&255;
if(cc != c)
{
img.setColor(drawcol[c]);
cc = c;
}
img.fillRect(drawx2, drawy2, 1, 1);
}
drawy2 ++;
} else { doing &= ~2; }
repaint();
}
public void run()
{
while(true)
{
while(doing == 0) try { runThread.sleep(200); } catch (InterruptedException e) { return; }
if((doing & 1) != 0) drawChildren();
if((doing & 2) != 0) drawParent();
}
}
public boolean mouseDown(Event evt, int x, int y)
{
mouse_oldx = x;
mouse_oldy = y;
mouse_x = x;
mouse_y = y;
mouse_drag = true;
return true;
}
public boolean mouseUp(Event evt, int x, int y)
{
int ix = (x-256-16)/80;
int iy = y/80;
if(x > 0 && x< 256 && y > 0 && y < 256)
{
startDrawChildren();
}
else if(ix>= 0 && ix < 4 && iy >= 0 && iy < 3)
{
startDrawParent(ix, iy);
}
mouse_drag = false;
return true;
}
public boolean mouseDrag(Event e, int x, int y)
{
mouse_x = x;
mouse_y = y;
repaint();
return true;
}
public void update(Graphics g)
{
paint(g);
}
public void paint(Graphics g)
{
g.drawImage(im, 0, 0, this);
if(mouse_drag) {
g.setColor(Color.white);
g.fillRect(mouse_oldx, mouse_oldy, mouse_x-mouse_oldx, mouse_y-mouse_oldy);
}
}
}