home games dev
Lewpen.com»Research & Development»Animation»Java Effects»TextureEvolver

TextureEvolver

/ TextureEvolver.java

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);
    }
  }

}

Comments

Article

Sponsored Links

Toys & Games:

Doggie Doo
Nerf Vortex
Monster High
Lagoona Hydration Station
Milky Bunny
Moshling Tree House
Lego Ninja Go Fire Temple
Fireman Sam Pontypandy Rescue
Rock Elmo
Star Wars Ultimate Force Lightsaber

Games

The Dodge Game
Flatspace

2-Player Games:

Quake 2D
Meteora

Puzzle Games:

Mini Tetris
Sudoku Solver

Development

3D Graphics:

3D Graphics Articles
WebGL Examples
Flash 3D Engine
Java 3D Engine

Development:

Programming Articles
Animation Demos
Game Development Examples

Links

iBuddy Social Network
Local Legends Football
PHP Charts & Graphs
CubeLogix Studios