Loading...
Logo
Processing Forum

distribution of points

in General Discussion  •  Other  •  1 year ago  
Hi,
 
I want to fill a surface with aleatory points, but if I do it with random I dont like the way it ends because there are some areas with many points and others empty. If I use noise, it doesnt fill  the borders of the area.
 
I want something as particles of a fluid, or as trees in a forest. With an impression of dissorder, but without concentration or empty spaces.
 
I can do it with random and after separating points creating a force, but it has to be a more eficient way.
 
 

Replies(7)

Another technique would be to randomly 'grow points' from existing points. Something like this...

Code Example
Copy code
  1. ArrayList <PVector> points = new ArrayList <PVector> ();
  2. float range = 10; // minimum distance between points
  3. int maxPoints = 400; // maximum number of points
  4.  
  5. void setup() {
  6.   size(500, 500);
  7. }
  8.  
  9. void draw() {
  10.   background(255);
  11.   if (points.size() < maxPoints) {
  12. //    range = random(10, 60); // uncomment to change range continuously
  13.     float dR = range*2;
  14.     for (int i=points.size()-1; i>=0; i--) {
  15. //    for (int i=points.size()-1; i>=max(0,points.size()-30); i--) { // or something like this...
  16.       PVector p = points.get(i);
  17.       PVector n = PVector.add(p, new PVector(random(-dR, dR), random(-dR, dR)));
  18.       if (!withinRange(points, n, range) && onScreen(n)) { points.add(n); }
  19.     }
  20.   }
  21.   for (PVector p : points) {
  22.     fill(p.x, p.y, 255-p.x);
  23.     rect(p.x, p.y, 4, 4);
  24.   }
  25. }
  26.  
  27. void mousePressed() {
  28.   points.clear();
  29.   points.add(new PVector(mouseX, mouseY));
  30. }
  31.  
  32. boolean withinRange(ArrayList <PVector> points, PVector n, float range) {
  33.   for (PVector p : points) {
  34.     if (n.dist(p) < range) {
  35.       return true;
  36.     }
  37.   }
  38.   return false;
  39. }
  40.  
  41. boolean onScreen(PVector n) {
  42.   return n.x >= 0 && n.x <= width && n.y >= 0 && n.y <= height;
  43. }
i would actually say noise is the way to go. and i cant think of a reason why the borders are not filled. Probably depends on how you use noise to fill the area. 
amnon idea is interesting, but I agree with cedrick that I fell noise should work and I dont know why not. This is an example of sing noise. I´ve tried to fill a line, and you see the borders are not filled even using a whole lot of points.
 
for (int i=0; i<3000;i++){
  set (50, int(noise(i)*height), color(0));
}
ok i see... yeah thats because you dont get the really low and really high values.
Something that is perfectly shown by a video by amnon  http://amnonp5.wordpress.com/2010/05/05/comparing-number-distributions/

but if you want to fill a 2d surface with points you can use noise in different ways i guess.
Just took the noise example from the processing website and just placed a point when noise it above a random value.
hold down a key to move through noise in 3d space.

Copy code
  1. float increment = 0.02;

  2. void setup() {
  3.   size(200, 200);
  4.   noLoop();
  5. }

  6. void draw() {
  7.   background(0);

  8.   noiseDetail(1, 0.65f);
  9.   randomSeed(0); 
  10.   loadPixels();

  11.   float xoff = 0.0; // Start xoff at 0

  12.     for (int x = 0; x < width; x++) {
  13.     xoff += increment;   // Increment xoff 
  14.     float yoff = 0.0;   // For every xoff, start yoff at 0
  15.     for (int y = 0; y < height; y++) {
  16.       yoff += increment; // Increment yoff

  17.       float bright = noise(xoff, yoff, frameCount/15.0);

  18.       if (random(1)<bright)   pixels[x+y*width] = color(255);
  19.     }
  20.   }

  21.   updatePixels();
  22. }
  23. void keyPressed() {
  24.   redraw();
  25. }

On a sidenote. As discussed in this thread, Processing's noise function appears to be something other than Perlin noise. Only the german demo group farbrausch knows exactly what it is as the code apparantly originates from a 64k they made in 2001 called Art. One for the Processing trivia book.

Anyway, as cedric suggested, there may be indirect ways to use noise for generating point distributions. With the default settings, noise will rarely-to-never generate some outer values. If you really, really want to use noise directly and have a whatever works attitude, you can use 1 octave and double the outcome.
Copy code
  1. noiseDetail(1,0);
  2. for (int i=0; i<3000;i++){
  3.   set(50, int(2*noise(i)*height), color(0));
  4. }
I made a try with amnon idea (second post), and I filled all the area, but the impression is not what I was looking for. The first amnon idea produced the impression I wanted, but if I fill all the area, it needs too much time. Maybe the best idea is to generate points with random and after separate them. The purpose of generating the points is to simulate a flow in a pipe. This is my actual version. I simply wanted to simplificate with a better way of generating the points. In my code what I do is separate the balls that were generated with random
 
class ball{
  PVector pos;
  PVector v;
  PVector force;
  color c;
  int r=3;
  float f = 0.1;
  float g = 0.1;
  float vaverage = 2;
  ball(int x0, int y0){
    pos=new PVector(x0,y0);
    v=new PVector(0,4);
    force = new PVector (0,0);
  }
  void update(){
    v.y = vaverage * (1 - sq(pos.x - middle)/sq(pipediam/2));  
    v.add (force);
    //add some chaos
    v.x = v.x + random(-0.3,0.3);
    count++;
    //v.y = v.y + g ;
    pos.add(v);
    v.x = v.x*0.5;
    force.x = 0;
    force.y = 0;
  }
  void render(){
    //if (abs(x.x-middle)<0.4 * pipediam){
      fill(20,20,2000);
      ellipse(int(pos.x),int(pos.y),2.5,2.5);
      if ((pos.y > respoint - reslenght/2)&& (pos.y > respoint + reslenght/2)){
     
      
      }
    //}
  }
}
float count;
int width=800;
int height=600;
float g=2;
ArrayList balls= new ArrayList();
float k=1;//factor to increment distance between balls
float c= 4;//maximun penetrance in walls
float vlim=10;//maximum speed
float population=1000;//population limit
float suck = 10;// distance of influence
float leftborder;
float rightborder;
float visc;
float pipediam = 100;
float middle;
float repulsion = 0.1;
float reslenght = 50;
float respoint;
void setup(){
  noStroke();
  //frameRate(48);
  size(800,600);
  leftborder = width/2-pipediam/2;
  rightborder = width/2 + pipediam/2;
  middle =(leftborder + rightborder)/2;
   respoint = height/2;
  for (int i = 0; i < population; i++){
    balls.add(new ball(int(random(leftborder,rightborder)), int(random(0,height))));
  }
}
void draw(){
  fill (250,100);
  rect(0,0,width, height);
  for(int i=0;i<balls.size();i++){  
    ball b = (ball) balls.get(i);  
    //separate from walls
    if(b.pos.x>rightborder-b.r+c){b.pos.x=rightborder;}
    if(b.pos.x<leftborder + b.r-c){b.pos.x=leftborder;}
    if(b.pos.y>height-b.r+c){b.pos.y-=5;}
    //repulsion between balls 
    for(int j=i+1;j<balls.size();j++){
      ball b2 = (ball) balls.get(j);
      PVector dx=PVector.sub(b2.pos,b.pos);
      if (dx.mag() < suck){
        dx.normalize();
        dx.mult(repulsion);
        b2.force.add( dx);
        dx.mult(-1);
        b.force.add( dx);
      }
    }
    //repulsion to walls
    if (b.pos.x < suck + leftborder){
      b.force.x = b.force.x + repulsion;
    }
    if (b.pos.x > rightborder - suck){
      b.force.x = b.force.x - repulsion;
    }
    b.update();
    // corrections
    if (b.v.y < 0){b.v.y = 0;}
   
    b.render();
    if (b.pos.y > height){
     b.pos.y = 0;
    }  
  } 
}
 
That reminds me of sketch i made for testing the randomness of a implementation of " Fisher-Yattes shuffle" i tried. It seems very random and even and organic to me. Actually there are two methods both uses a pre filled array with numbers in  sequence that are shuffled. Then you got a random index to access the array. Also the x position is not random, so is like a line of random points what make it evenly distributed. May have nothing to do with what you need but... may be an idea.

Copy code
  1. // with code from WikiPedia; Fisher–Yates shuffle 
  2. // http://en.wikipedia.org/wiki/Fisher–Yates_shuffle
  3. import java.util.Random;
  4. Random rng = new Random();
  5. int[] numbers = new int[240];
  6. int[] index = new int[7]; // numbers of ponits to  draw each line
  7. int[] indexReto = new int[7];// numbers of ponits to  draw each line
  8. color c1 =  color(69,31,90);
  9. float leng=0;
  10. int xisp=-40;

  11. void setup() {
  12.   size(1500,480);
  13.   for (int i = 0; i < numbers.length; i++) {
  14.     numbers[i]=i+1;
  15.   }
  16.   for (int i = 0; i < index.length; i++) {
  17.     index[i]=i;
  18.     indexReto[i]=i;
  19.   }
  20.   background(c1);

  21. void draw() {
  22.   stroke(0);
  23.   strokeWeight(2);
  24.   line(0,height/2,width,height/2);
  25.   
  26.   
  27.   shuffle(numbers);
  28.   shuffle(index);

  29.   for (int i = 0; i < index.length; i++) {
  30.     fill(115,139,11);
  31.     noStroke();
  32.     ellipse(xisp+i,height/2-numbers[index[i]],2,2);
  33.     stroke(0);
  34.     
  35.     fill(120,142,18);
  36.     noStroke();
  37.     int ind=int(random(0,60));
  38.     ellipse(xisp+i,height-numbers[ind],2,2);
  39.     stroke(0);
  40.      }

  41.    if( xisp<=width){
  42.    xisp=(xisp+1);}else{xisp=-40;}
  43.   
  44. }

  45. void keyReleased() {
  46.   
  47. }


  48. void shuffle(int[] array) {
  49.   // i is the number of items remaining to be shuffled.
  50.   for (int i = array.length; i > 1; i--) {
  51.     // Pick a random element to swap with the i-th element.
  52.     int j = rng.nextInt(i);  // 0 <= j <= i-1 (0-based array)
  53.     // Swap array elements.
  54.     int tmp = array[j];
  55.     array[j] = array[i-1];
  56.     array[i-1] = tmp;
  57.   }
  58. }