DATASCAPES code examples from gshowman


This browser does not have a Java Plug-in.
Get the latest Java Plug-in here.

ORIGINAL CODE: Cellular Automata 2 by Mike Davis. A short program for alife experiments. Click in the window to restart. Each cell is represented by a pixel on the display as well as an entry in the array 'cells'. Each cell has a run() method, which performs actions based on the cell's surroundings. Cells run one at a time (to avoid conflicts like wanting to move to the same space) and in random order. Created 9 January 2003 NEW CODE: by gshowman and fbitonti February, 2007 Adding the following features: 1. Vector analysis of pixel intensities -- creates Node objects 2. Providing a UI (through ControlP5 library) to control this analysis 2. Export of .dxf files through the dxf exporter CONTENTS OF THIS FILE: - various global declarations - usual setup() and draw() functions - original Cellular Automata code (may have some additions by gs/fb) - new vector analysis code, including Node class, DXF export - new UI code, covering mouse and keyboard events

Source code: datascapes_clusters_02

Built with Processing



Cluster Finding Code -- Oct. 16, 2007


This script puts a layer of 'analysis' over Mike Davis' basic particle code found on the Processing website (here: http://processing.org/learning/topics/spore1.html). My script adds three main features:
1. A search algorithm that looks for certain densities of particles and flags them with vector points and lines
2. An interface (using Andreas Schlegel's controlP5 library, which you will need!) to experiment with the parameters of that search
3. The ability to export to .dxf (using the exportDXF library, which is built into Processing so no worries)

There is also the option to draw 3D lines instead of 2D, the idea being that when you upload this file to Rhino or something, you will get a 3D geometry. Currently it's just a single winding path -- this is just a proof of concept, after all.
/**
 * ORIGINAL CODE: Cellular Automata 2
 * by Mike Davis.
 *
 * A short program for alife experiments. Click in the window to restart.
 * Each cell is represented by a pixel on the display as well as an entry in
 * the array 'cells'. Each cell has a run() method, which performs actions
 * based on the cell's surroundings. Cells run one at a time (to avoid conflicts
 * like wanting to move to the same space) and in random order.
 *
 * Created 9 January 2003
 *
 * NEW CODE: by gshowman and fbitonti
 * February, 2007
 * Adding the following features:
 * 1. Vector analysis of pixel intensities -- creates Node objects
 * 2. Providing a UI (through ControlP5 library) to control this analysis
 * 2. Export of .dxf files through the dxf exporter
 *
 *
 * CONTENTS OF THIS FILE:
 * - various global declarations
 * - usual setup() and draw() functions
 * - original Cellular Automata code (may have some additions by gs/fb)
 * - new vector analysis code, including Node class, DXF export
 * - new UI code, covering mouse and keyboard events
 *
 */
 
 
// gshowman: Control library
import controlP5.*;
ControlP5 controlP5;
public int sliderValue = 3;
ControlWindow controlWindow;
Controller AnalyzeButton;
 
//make DXF
import processing.dxf.*;
boolean g_exportDXF;
 
 
 
// gshowman: control variables
float match_factor = 0.75;
float node_scalefactor = 0.3;
int g_search_radius = 15;
int g_button_pressed = 0; // duplicate of screen state
color[] g_pixbuffer;
int g_switch = -1;
int g_button_count = 4;
boolean g_analyze = false;
boolean g_resume = false;
boolean g_show_nodes = false;
boolean g_z_mode = false;
 
int screenWidth = 0; // to hold the width of the 'action' part of the screen
// (as opposed to the controlP5 part)
 
// gshowman: a list of all analytic Nodes we are creating
Vector node_list = new Vector();
 
// original CA stuff:
World w;
int numcells = 0;
int maxcells = 4700;
boolean paused = false;
Cell[] cells = new Cell[maxcells];
color spore_color;
// set lower for smoother animation, higher for faster simulation
int runs_per_loop = 10000;
color black = color(0, 0, 0);
 
 
void setup()
{
 size(600, 200,P3D);
 screenWidth = 200;
 frameRate(24);
 clearscr();
 w = new World();
 spore_color = color(172, 255, 128);
 seed();
 g_button_count = 3;
 g_analyze = false;
 g_resume = false;
 g_exportDXF = false;
 node_list = new Vector();
 
 // gshowman
 controlP5 = new ControlP5(this);
 controlP5.addSlider("g_search_radius",0,20,screenWidth+40,40,100,10);
 controlP5.addSlider("match_factor",0.0,1.0,screenWidth+144,40,100,10);
 controlP5.addButton("Analyze",0,screenWidth+40,100,80,20);
 controlP5.addButton("Clear_Analysis",1,screenWidth+124,100,80,20);
 controlP5.addButton("Export_DXF",2,screenWidth+208,100,80,20);
 controlP5.addButton("Show_Nodes",2,screenWidth+40,124,80,20);
 controlP5.addButton("Z_Mode",2,screenWidth+124,124,80,20);
 g_pixbuffer = new color[screenWidth*height];
}
 
 
void seed()
{
  int mynumcells = 0;
 
  // Add cells at random places
  for (int i = 0; i < maxcells; i++)
  {
    int cX = (int)random(screenWidth);
    int cY = (int)random(height);
    if (w.getpix(cX, cY) == black)
    {
      w.setpix(cX, cY, spore_color);
      cells[mynumcells] = new Cell(cX, cY);
      mynumcells++;
    }
  }
 
  numcells = mynumcells;
}
 
void draw()
{
  controlP5.draw();
  if (g_analyze) {
    // run the analysis.
    println ("Running the vector analysis...");
    vectorize(spore_color);
 
    paused = true;
    g_analyze = false;
  }
 
  if (g_resume) {
    // we're going to re-start -- let's re-load the state prior
    // to vectorization
    println("re-instating conditions prior to analysis");
    clearscr();
    for (int i=0; i < screenWidth*height; i++) {
      w.setpix((i)%screenWidth, int ((i)/height), g_pixbuffer[i]);
    }
    paused = false;
    g_resume = false;
  }
 
  if (paused) {
    // draw the analysis (just once)
    draw_connections();
  }
  else {
    // (this is the default state of the system)
    // Run cells in random order
    for (int i = 0; i < runs_per_loop; i++) {
      int selected = min((int)random(numcells), numcells - 1);
      cells[selected].run();
    }
 
  }
 
  // note: so when paused == true, nothing happens
}
 
 
 
// ----------------------------------------------------------------------------
// CELLULAR AUTOMATA CODE (MOSTLY, IF NOT TOTALLY, ORIGINAL... I.E. 2003)
// ----------------------------------------------------------------------------
 
void clearscr()
{
  for (int y = 0; y < height; y++) {
    for (int x = 0; x < screenWidth; x++) {
 
      set(x, y, color(0));
    }
  }
}
 
class Cell
{
  int x, y;
  Cell(int xin, int yin)
  {
    x = xin;
    y = yin;
  }
 
  // Perform action based on surroundings
  void run()
  {
    // Fix cell coordinates
    while(x < 0) {
      x+=screenWidth;
    }
    while(x > screenWidth - 1) {
      x-=screenWidth;
    }
    while(y < 0) {
      y+=height;
    }
    while(y > height - 1) {
      y-=height;
    }
 
    // Cell instructions
    if (w.getpix(x + 1, y) == black) {
      move(0, 1);
    }
    else if (w.getpix(x, y - 1) != black && w.getpix(x, y + 1) != black) {
      move((int)random(9) - 4, (int)random(9) - 4);
    }
  }
  // Will move the cell (dx, dy) units if that space is empty
  void move(int dx, int dy) {
    if (w.getpix(x + dx, y + dy) == black) {
      w.setpix(x + dx, y + dy, w.getpix(x, y));
      w.setpix(x, y, color(0));
      x += dx;
      y += dy;
    }
  }
}
 
//  The World class simply provides two functions, get and set, which access the
//  display in the same way as getPixel and setPixel.  The only difference is that
//  the World class's get and set do screen wraparound ("toroidal coordinates").
class World
{
  void setpix(int x, int y, int c) {
    while(x < 0) x+=screenWidth;
    while(x > screenWidth - 1) x-=screenWidth;
    while(y < 0) y+=height;
    while(y > height - 1) y-=height;
    set(x, y, c);
  }
 
  color getpix(int x, int y) {
    while(x < 0) x+=screenWidth;
    while(x > screenWidth - 1) x-=screenWidth;
    while(y < 0) y+=height;
    while(y > height - 1) y-=height;
    return get(x, y);
  }
}
 
 
 
 
 
// ----------------------------------------------------------------------------
// ANALYSIS CODE (new stuff by gshowman and fbitonti)
// ----------------------------------------------------------------------------
 
 
//NODE
class Node
{
  int x, y, z, radius;
 
  Node (int xin, int yin, int rad) {
    x = xin;
    y = yin;
    radius = rad;
    z = rad;        // yes... the 'rad' is really an abstractd 'intensity', which can
    // be interpreted as a z-coordinate
  }
 
 
  void draw_radius() {
    stroke(255,50,0,150);
    noFill();
    ellipse(x, y, radius*node_scalefactor, radius*node_scalefactor);
  }
}
 
class NodeComparer implements Comparator {
  public int compare(Object n1, Object n2)
  {
    return ((Node) n1).radius - ((Node) n2).radius;
  }
}
 
int find_matches_within(int xx, int yy, int radius, color c)
{
  int matches = 0;
  int x = 0;
  int y = 0;
 
  int xlim = min(screenWidth, xx+radius);
  int ylim = min(height, yy+radius);
 
  for (x = max(0,xx-radius); x < xlim; x++) {
    for (y = max(0,yy-radius); y < ylim; y++) {
      if (get(x,y) == c) {
        matches++;
      }
    }
  }
 
  return matches;
}
 
 
//nodes are created
void create_node(int x, int y, int intensity)
{
  Node A = new Node(x,y,intensity);
  node_list.add(A);
}
 
 
void vectorize(color c)
{
  int x = 0;
  int y = 0;
 
  // 1. take a copy of the current state.
  for (int i = 0; i < screenWidth*height; i++) {
    g_pixbuffer[i] = w.getpix(i%screenWidth, int (i/height));
  }
 
  int radius = g_search_radius;
  int match_limit = int(match_factor * (PI) * radius*radius);
 
  for (x=0; x < screenWidth; x++) {
    for (y=0; y < height; y++) {
 
      if (get(x,y) == c) {
        int matches = 0;
        //match -- check your neighbors
        matches += find_matches_within(x, y, radius, c);
        if (matches >= match_limit) {
          create_node(x, y, matches);
          println ("Created Node at " + x + ", " + y + " with intensity " + matches);
        }
      }
 
    }
  }
}
 
void draw_connections()
{
  //DXF export... begin recording geometry
  if (g_exportDXF) {
    beginRaw("processing.dxf.RawDXF", "C:\\output.dxf");
  }
 
  int nodeListSize = node_list.size ();
  int[] connectionArray = new int[nodeListSize];
  int[] connectionArrayX = new int[nodeListSize];
  int[] connectionArrayY = new int[nodeListSize];
  int connectCount = 0;
 
  // gshowman: sort the vector by intensity
  Collections.sort(node_list, new NodeComparer());
 
  // Connect all nodes in descending order of intensity/radius
  stroke(255,50,0,150);
 
  int last_x = 0;
  int last_y = 0;
  int last_z = 0;
 
  if (!node_list.isEmpty()) {
    Iterator it = node_list.iterator ();
    Node N = (Node) it.next();
    last_x = N.x;
    last_y = N.y;
    last_z = N.z;
 
    if (g_show_nodes) {
      N.draw_radius();
    }
 
    while (it.hasNext()) {
      Node nextNode = (Node) it.next ();
      if (g_z_mode) {
        // use intensity as z-coordinate
        line(last_x, last_y, last_z, nextNode.x, nextNode.y, nextNode.z);
      }
      else {
        line(last_x, last_y, nextNode.x, nextNode.y);
      }
 
      last_x = nextNode.x;
      last_y = nextNode.y;
      last_z = nextNode.z;
 
      if (g_show_nodes) {
        nextNode.draw_radius();
      }
    }
  }
 
  //dxf End recording
  if (g_exportDXF) {
    endRaw();
    g_exportDXF = false;
  }
}
 
// ----------------------------------------------------------------------------
// UI CODE
// ----------------------------------------------------------------------------
 
void mousePressed()
{
  if(mouseX < screenWidth) {
    setup();
  }
}
 
 
void Analyze(int theIrrelevantValue) {
  println("Analyze button has been pressed...");
  // For some reason the button gets pressed at startup... ignore that:
  if (g_button_count > 0) {
    g_button_count--;
    return;
  }
 
  if (paused) {
    // raise the flag to end the analysis (see draw() function)
    g_resume = true;
    AnalyzeButton.setLabel("Analyze");
  }
  else {
    // raise the flag for the analysis (see the draw() function)
    g_analyze = true;
    AnalyzeButton.setLabel("Resume");
  }
}
 
void Clear_Analysis(int theIrrelevantValue) {
  println("Clear_Analysis button has been pressed...");
  // For some reason the button gets pressed at startup... ignore that:
  if (g_button_count > 0) {
    g_button_count--;
    return;
  }
 
  // just empty the node_list
  node_list.clear();
}
 
 
void Export_DXF(int theIrrelevantValue) {
  println("Export_DXF button has been pressed...");
  // For some reason the button gets pressed at startup... ignore that:
  if (g_button_count > 0) {
    g_button_count--;
    return;
  }
 
  g_exportDXF = true;
}
 
 
void Show_Nodes(int theIrrelevantValue) {
  println("Show_Nodes button has been pressed...");
  // For some reason the button gets pressed at startup... ignore that:
  if (g_button_count > 0) {
    g_button_count--;
    return;
  }
 
  if (g_show_nodes) {
    g_show_nodes = false;
    println("Turning nodes off");
  }
  else {
    g_show_nodes = true;
    println("Turning nodes on");
  }
}
 
void Z_Mode(int theIrrelevantValue) {
  println("Z_Mode button has been pressed...");
  // For some reason the button gets pressed at startup... ignore that:
  if (g_button_count > 0) {
    g_button_count--;
    return;
  }
 
  if (g_z_mode) {
    g_z_mode = false;
    println("Turning Z_Mode off");
  }
  else {
    g_z_mode = true;
    println("Turning Z_Mode on");
  }
}