Insert 3D HSB Colormodel in Space Colonization Algorithm

Hi there!

I’m trying to create generative plants with a space colonization algorithm. I found a tutorial/code from Daniel Shiffman who’s using random dots in a 3D coordinate System which then connect with each other.

I’d love to use the pixel color of an image and transfer them into dots in a 3D Color Model. I want to use these instead of the random dots to let the „plant“ grow.

I tried to insert the code from Timo Hausmann (who’s doing this kind of 3D color models) in the space colonization code. I failed again and again.

I’d like to upload my attempts but i think it makes everything more confusing. I would be very happy about a few tips on how to combine the two codes.

Thanks in advance!

All the best

Theresa

  1. COLOR SPACE (you have to create a folder called “in” with a picture in it) sorry I’ve never done this before – if there’s a smarter solution let me know :wink:

/*
 * 3D Color Space
 * https://github.com/timohausmann/colorspace
 * @license MIT
 */

import peasy.*;
import controlP5.*;
import java.io.File;


float maxPoints = 8000;         //the maximum number of plotted points
float scaleDiameter = 1.5;      //XY scale factor
float scaleHeight = 2;          //Z scale factor

boolean is_cone = false;        //display as cone
boolean is_scale = false;       //display scale
boolean is_rotate = true;       //scene auto rotate
float sceneRotation = 0;
float sceneRotationDelta = 0.1;

PImage img;       //the current image
IntList imgData;  //the processed image pixel data as a list of colors

PeasyCam cam;
ControlP5 cp5;
Slider cpScaleXZ;
Slider cpScaleY;

String folderIn;
String folderOut;
int fileIndex;
String[] fileList;


void setup() {

  size(1280, 800, P3D);
  colorMode(HSB); 

  cam = new PeasyCam(this, 400);
  cam.setMinimumDistance(100);
  cam.setMaximumDistance(3200);
  cam.setDistance(1000);

  folderIn = sketchPath() + "/in/";     //folder with source images
  folderOut = sketchPath() + "/out/";   //output folder for screenshots

  fileList = listFileNames(folderIn);
  fileIndex = 0;
  loadFile(fileList[0]);

  
}


void draw() {
  
  background(255);

  rotateY( radians(sceneRotation) );
  drawPlot();

}


void drawPlot() {

  pushMatrix();
  noStroke();
  sphereDetail(4);
  translate(0, 255*scaleHeight/2, 0);
  
  for(int i=0;i<imgData.size();i++) {
    
    int hsb = imgData.get(i);
    
    float h = hue(hsb);
    float s = saturation(hsb);
    float b = brightness(hsb);
    fill(h,s,b);

    pushMatrix();
    PVector pos = hsb2Vector(h,s,b);
    translate(pos.x, pos.y, pos.z);
    sphere(5);
    popMatrix();
  }
  popMatrix();
}





/** 
 * Get Pixels
 * returns an IntList representing pixels from the image
 * the IntList is limited by maxPoints
 * (Processing stores color() as type int)
 */
IntList getPixels(PImage img) {

  int counter = 0;
  IntList tmpImgData = new IntList();
  int pixelCount = img.width * img.height;
  float step = pixelCount/maxPoints;
  if(step < 1 ) step = 1;
  
  loadPixels();  
  for (float i=0; i<pixelCount; i+=step,counter++ ) {
    
      int loc = round(i);        
      float h = hue(img.pixels[loc]);
      float s = saturation(img.pixels[loc]);
      float b = brightness(img.pixels[loc]);
      
      int hsb = color(h,s,b);
      tmpImgData.append(hsb);
  }
  updatePixels();

  println("scanned " + counter + " pixels (pixel count: " + pixelCount + " | pixel step: " + step + ")");
  return tmpImgData;
}


/** 
 * HSB to Vector
 * Converts HSB-Values (0-255) to Colorspace XYZ
 */
PVector hsb2Vector(float h, float s, float b) {

    PVector tmpVector = new PVector();

    float xyDist = s; //get the distance from the center (saturation)
    if( is_cone ) xyDist *= b/255.0; //multiply with brightness factor for cone effect
    xyDist *= scaleDiameter; //scale
    
    //convert hue to radians and multiply it with satuation to get x/y positions
    tmpVector.x = sin(radians((h/255.0)*360)) * xyDist;
    tmpVector.z = cos(radians((h/255.0)*360)) * xyDist;
    //convert brightness to y position
    tmpVector.y = -b*scaleHeight;

    return tmpVector;
}


/**
 * loadFile
 * load image file and store its data
 */
void loadFile(String path) {
  img = loadImage(folderIn + path);
  imgData = getPixels(img);
}

// This function returns all the files in a directory as an array of Strings  
String[] listFileNames(String dir) {
  java.io.File file = new java.io.File(dir);
  if (file.isDirectory()) {
    String names[] = file.list();
    return names;
  } else {
    // If it's not a directory
    println("directory not found");
    return null;
  }
}
  1. SPACE COLONIZATION ALGORITHM

this one is still the original from Daniel Shiffman:

Sources:

Space Colonization Algorithm

Daniel Shiffman

3D Color Model

Timo Hausmann

1 Like

Here is some of Dan’s code from the image manipulation series, it might help.

class Cell {
  float x, y, wid, length;
  int xpos, ypos, id, oid, t;
  //boolean[] walls;
  ArrayList<Boolean> walls =new ArrayList<Boolean>();
  ArrayList<float[]> edge =new ArrayList<float[]>();
  ArrayList<float[]> vertex =new ArrayList<float[]>();
  boolean visited,hilight,mpos;
  color col, col2, pcol;
  int rpos, score;

  Cell (float _x, float _y, float _wid, int _id, int _ypos, int _xpos) {

    x     = _x;
    y     = _y;
    xpos = _xpos;
    ypos = _ypos;
    id    = _id;
    oid   = _id;
    rpos = -1;
    //mpos = false;
    //this.a = 0;
    hilight = false;
    visited = false;
    score = 0;
    color col   = color(0, 255, 234);
    color pcol  = col;
    color col2  = color(0, 0, 0);
    length = 0;
    t = 0;
  }
  void init() {
    for (int i=0; i<sides; i++) {
      float thetab =  theta * i;
      float thetac =  theta * i+theta;
      float []a = {x+w/2*sin(thetab), y+w/2*cos(thetab), x+w/2*sin(thetac), y+w/2*cos(thetac)};
      float []b = {x+w/2*sin(thetac), y+w/2*cos(thetac)};
      walls.add(true);
      edge.add( a);
      vertex.add(b);
      length = dist(x+w/2*sin(thetab), y+w/2*cos(thetab), x+w/2*sin(thetac), y+w/2*cos(thetac));
    }
  };

  void showid() {
    //color a = color(255, 0, 0);
    fill(255, 0, 0);
    fill(0, 13, 255);
    text(oid, x, y);
  };

  void draw (float c) {

    if (hilight) {
      col = color(255, 0, 102);
    }
    lShape(x, y, col2, walls);
    if (t==1) {
      visited = true;
    } else {
      //visited = false;
    }
    if (visited ==true) {
      fill(255, 0, 0);
    }
  };

  void fillshape() {
    beginShape();
    for (int k=0; k<sides; k++) {


      float thetab =  theta * k;
      float thetac =  theta * k+theta;
      fill(col);
      if (visited) {
        fill(pcol);
      }
      noStroke();
      vertex(x+w/2*sin(thetac), y+w/2*cos(thetac));
    }
    endShape();
  };

  void highlight() {
    beginShape();
    for (float k=0; k<sides; k++) {
      noStroke();
      float thetab =  theta * k;
      float thetac =  theta * k+theta;
      fill(0, 0, 255, 100);
      vertex(x+w/2*sin(thetac), y+w/2*cos(thetac));
    }
    endShape();
  };

  Cell checkNeighbors() {
    int x = xpos;
    int y = ypos;
    ArrayList<Cell> neighbours = new ArrayList<Cell>();
    //due to the offset introduced to make the hexagonal grid a slight adjustment needs to be made for the x values
    float k = pow(-1, y);
    if (k==1) {
      x=x+1;
    }
    //ArrayList<Cell> btmL = new ArrayList<Cell>();
    Cell btmL = grid.get(index(x-1, y+1));
    Cell btmR = grid.get(index(x, y+1));
    Cell topL = grid.get(index(x-1, y-1));
    if (k==-1) {
      x=x-1;
    }
    if (k==1) {
      x=x-1;
    }
    Cell topR = grid.get(index(x+1, y-1));
    if (k==-1) {
      x=x+1;
    }
    Cell left = grid.get(index(x-1, y));
    Cell right = grid.get(index(x+1, y));


    fill(255, 0, 0);
    if (right != null) {
      //text(right.id,x,y);
    }
    if (topL != null&& !topL.visited) {
      topL.rpos = 3;
      neighbours.add(topL);
    }
    if (topR != null && !topR.visited) {
      topR.rpos = 2;
      neighbours.add(topR);
    }
    if (right != null && !right.visited) {
      right.rpos = 1;
      neighbours.add(right);
    }
    if (btmR != null &&!btmR.visited) {
      btmR.rpos = 0;
      neighbours.add(btmR);
    }
    if (btmL != null &&!btmL.visited) {
      btmL.rpos = 5;
      neighbours.add(btmL);
    }
    if (left != null &&!left.visited) {
      left.rpos = 4;
      neighbours.add(left);
    }
    int n = neighbours.size()-1;
    if (neighbours.size()>0) {
      //text(neighbours.aid,x,y);
      int r = round(random(0, n));
      n = n;

      return neighbours.get(r);
    } else {
      return null;
    }
  };

  Cell nthPass() {
    int x = xpos;
    int y = ypos;
    ArrayList<Cell> neighbours = new ArrayList<Cell>();
    //due to the offset introduced to make the hexagonal grid a slight adjustment needs to be made for the x values
    float k = pow(-1, y);
    if (k==1) {
      x=x+1;
    }
    //ArrayList<Cell> btmL = new ArrayList<Cell>();
    Cell btmL = grid.get(index(x-1, y+1));
    Cell btmR = grid.get(index(x, y+1));
    Cell topL = grid.get(index(x-1, y-1));
    if (k==-1) {
      x=x-1;
    }
    if (k==1) {
      x=x-1;
    }
    Cell topR = grid.get(index(x+1, y-1));
    if (k==-1) {
      x=x+1;
    }
    Cell left = grid.get(index(x-1, y));
    Cell right = grid.get(index(x+1, y));

    if (topL != null && id!=topL.id) {
      topL.rpos = 3;
      neighbours.add(topL);
    }
    if (topR != null && id!=topR.id) {
      topR.rpos = 2;
      neighbours.add(topR);
    }
    if (right != null && id!=right.id) {
      right.rpos = 1;
      neighbours.add(right);
    }
    if (btmR != null && id!=btmR.id) {
      btmR.rpos = 0;
      neighbours.add(btmR);
    }
    if (btmL != null && id!=btmL.id) {
      btmL.rpos = 5;
      neighbours.add(btmL);
    }
    if (left != null && id!=left.id) {
      left.rpos = 4;
      neighbours.add(left);
    }
    if (neighbours.size()>0) {
      int r = round(random(0, neighbours.size()-1));
      return neighbours.get(r);
    } else {
      return null;
    }
  };

  void histScore() {

    int x = xpos;
    int y = ypos;
    ArrayList<Cell> neighbours = new ArrayList<Cell>();
    //due to the offset introduced to make the hexagonal grid a slight adjustment needs to be made for the x values
    float k = pow(-1, y);
    if (k==1) {
      x=x+1;
    }
    //ArrayList<Cell> btmL = new ArrayList<Cell>();
    Cell btmL = grid.get(index(x-1, y+1));
    Cell btmR = grid.get(index(x, y+1));
    Cell topL = grid.get(index(x-1, y-1));
    if (k==-1) {
      x=x-1;
    }
    if (k==1) {
      x=x-1;
    }
    Cell topR = grid.get(index(x+1, y-1));
    if (k==-1) {
      x=x+1;
    }
    Cell left = grid.get(index(x-1, y));
    Cell right = grid.get(index(x+1, y));

    int a = 0;
    if (topL != null &&topL.walls.get(0)==false&&walls.get(3)==false&&id!=topL.id) {
      topL.rpos = 3;
      neighbours.add(topL);
      a ++;
    }
    if (topR != null &&topR.walls.get(5)==false&&walls.get(2)==false&&id!=topR.id) {
      topR.rpos = 2;
      neighbours.add(topR);
      a ++;
    }
    if (right != null &&right.walls.get(4)==false&&walls.get(1)==false&&id!=right.id) {
      right.rpos = 1;
      neighbours.add(right);
      a ++;
    }
    if (btmR != null &&btmR.walls.get(3)==false&&walls.get(0)==false&&id!=btmR.id) {
      btmR.rpos = 0;
      neighbours.add(btmR);
      a ++;
    }
    if (btmL != null &&btmL.walls.get(2)==false&&walls.get(5)==false&&id!=btmL.id) {
      btmL.rpos = 5;
      neighbours.add(btmL);
      a ++ ;
    }
    if (left != null &&left.walls.get(1)==false&&walls.get(4)==false&&id!=left.id) {
      left.rpos = 4;
      neighbours.add(left);
      a ++;
    }
    int n = neighbours.size();
    if (n > 0) {
      fill(255, 0, 0);
      score = 1;
    } else {
    }
  };

  void hover() {
    //noLoop();
    float x = mouseX;
    float y = mouseY;
    //float[] a = edge;
    ArrayList<float []> a = edge;
    //text(a.length,10,10);
    for (int i=0; i<a.size(); i++) {

      float[] b = a.get(i);
      float x1 = b[0];
      float y1 = b[1];
      float x2 = b[2];
      float y2 = b[3];

      float d1 = dist(x, y, x1, y1);
      float d2 = dist(x, y, x2, y2);
      float d3 = dist(x1, y1, x2, y2);
      float d4 = d1+d2;
      float d5 = dist(x, y, x, y);
      float dy = y1-y2;
      float dx = x1-x2;
      float dy2 = y2-y1;
      float dx2 = x2-x1;
      float dmx = x-x1;
      float dmy = y-y1;
      float dmx2 = x-x2;
      float dmy2 = y-y2;
      //fill(255, 0, 0);

      float thetaXY1 = atan2(dy, dx);
      float thetaXY2= atan2(dy2, dx2);
      float thetaMXY2 = atan2(dmy, dmx);
      float thetaMXY1 = atan2(dmy2, dmx2);

      if ((thetaXY1>=0&&thetaMXY1>=thetaXY1&&thetaMXY1>=0&&d1<=length&&d2<=length)||(thetaXY1<=0&&thetaMXY1<=0&&thetaMXY1>=thetaXY1&&d1<=length&&d2<=length)||(thetaXY2>=0&&thetaMXY2>=0&&thetaMXY2<=thetaXY2&&d1<=length&&d2<=length)||(thetaXY2<=0&&thetaXY2<=0&&thetaMXY2<=thetaXY2&&d1<=length&&d2<=length)||d5<=length/1.5) {
        text("hello",x,y);
        mpos = true;
      }
      else{
        mpos = false;
      }
    }
    //loop();
  };

  void checkp() {

    if (mpos==true) {
      col =  color(29, 131, 240);
      col2 = color(212, 8, 8);
    } else {
      col = color(0, 204, 255);
      col2 = color(162, 0, 255);
    }
  };

  Boolean toggle() {
    if(mousePressed){
    if (mpos==true) {
      t++;
      if (t==2) {
        t=0;
      }
    }
    }
    return true;
  };
};
}

I’m assuming you can transfer the image data to an array and therefore retrieve its color value by calling the index in the array, then you can manipulate that however you’d like. In this case pixels is the array but you could save it into another array.

Here is an example using an arrayList, to get all three rgb values you would have to make the ArrayList type an array with float type,

  ArrayList<float[]> pixels =new ArrayList<float[]>();

and then define the rgb array

float []rgb = {r,g,b};

then add the colors with

array.add(b);

you can then retrieve them with

array.get(index_value)
1 Like