Help needed to create irregular circles


#1

I intend to create irregular circles like this

but unsure how to get smooth noisy lines in circular manner.

function setup() {
  createCanvas(600, 600);
  background(255);
  angleMode(DEGREES);
}

function draw() {  
    circ()
}

function circ(){
    let xoff = 0; 
    beginShape();
    translate(300,300);
    for (i=0;i<90;i++) {
        radius=100 + map(noise(xoff),0,1,10,20);
        x=sin(4*i);
        y=cos(4*i);
        vertex(radius*x, radius*y);
    }
    xoff+=0.05;
    endShape(CLOSE);
    noLoop()
}

Any help appreciated. Thanks!


#2

The following code tries to demonstrate how to generate a single irregular contour. Mouse click will generate a new contour. Think of a contour made of a circle with “npts” number of points. These are the steps to generate the contour:

  1. Generate center of contour (in setup)
  2. Generate contour’s radius (in setup)
  3. Generate irregular distributed angle sample. This means, if I request 50 points in my circle, this function will generate 50 angles but not evenly spaced
  4. Generate noise radius at every angle position generated in the prev step
  5. Draw contour using either points or lines

Further explanation:
r_irr[i] = r + (r*rFraction)*(noise(noiseStep*i)-0.5);
This line shows that every point has a noise radius, where the noise depends on noisestep and rfraction , each being mapped to mouseX and mouseY. So every time one clicks, it generates a new contour based on the position of mouseX and mouseY in the sketch. Finally, I subtract 0.5 as perlin noise returns a value from 0…1, effectively making this value work in a range from -0.5 to 0.5.

This is not a final solution but just an approach. Notice that nPts is set to 50. It would be better if this number was proportional to the radius. For instance, smaller radius would require a smaller nPts and larger radius would required npts to be larger. This is just a thought but not that important.

Kf

'use strict';

const npts=50;
let dataPts=[];
let locx, locy, r;

function setup() {
  createCanvas(600, 600);
  angleMode(DEGREES);  
  strokeWeight(3);

  //Center of contour
  locx=random(width/4.0, 3*width/4.0);  //[0.25 .. 0.75]
  locy=random(height/4.0, 3*height/4.0); //[0.25 .. 0.75]

  //Generate random radius
  r=random(width/4.0, width/2.0);
  
  noLoop();
}

function draw() {  
  background(120);
  generateIrregularSteppingAngle();
  showCircularCountour(locx, locy, r, 0);
  showIrregularCountour(locx, locy, r, 1);
}

function mouseReleased() {
  redraw();
}

function generateIrregularSteppingAngle() {

  let delta=360.0/npts; //360 degrees / n_points
  print("Delta="+delta);

  for (let i=0; i<npts; i++) {
    let angle = i*delta;
    angle = angle + random(-delta/2.0, delta/2.0);
    dataPts[i]=angle;
  }
}

//Mode: 0=points 1:kines 2:curve
function showCircularCountour(x, y, r, m) {

  push();
  if (m==0) {
    stroke(250);
    translate(locx, locy);
    for (let i=0; i<dataPts.length; i++) {
      let px = r * cos(dataPts[i]);
      let py = r * sin(dataPts[i]);
      point(px, py);
    }
  }
  pop();
}

//Mode: 0=points 1:kines 2:curve
function showIrregularCountour(x, y, r, m) {

  push();  
  translate(locx, locy);

  //GENERATES irregular radius
  let r_irr=[];
  for (let i=0; i<dataPts.length; i++) {  // begins @ 1

    let noiseStep=map(mouseX, 0, width, 0.001, 10);
    let rFraction=map(mouseY, 0, height, 0.05, 0.5);
    r_irr[i] = r + (r*rFraction)*(noise(noiseStep*i)-0.5);
  }


  if (m==0) {
    stroke(0, 255, 0);
    for (let i=0; i<dataPts.length-1; i++) {

      let px = r_irr[i] * cos(dataPts[i]);
      let py = r_irr[i] * sin(dataPts[i]);
      point(px, py);
    }
  }

  if (m==1) {   
    stroke(150, 255, 0);

    for (let i=0; i<dataPts.length-1; i++) {  

      let px0 = r_irr[i] * cos(dataPts[i]);
      let py0 = r_irr[i] * sin(dataPts[i]);
      let px = r_irr[i+1] * cos(dataPts[i+1]);
      let py = r_irr[i+1] * sin(dataPts[i+1]);
      line(px0, py0, px, py);
    }

    let last_idx=dataPts.length-1;
    let px0 = r_irr[last_idx] * cos(dataPts[last_idx]);
    let py0 = r_irr[last_idx] * sin(dataPts[last_idx]);
    let px = r_irr[0] * cos(dataPts[0]);
    let py = r_irr[0] * sin(dataPts[0]);
    line(px0, py0, px, py);
  }


  pop();
}

kf_keyword: irregular patterns contours noise noisy perlin concentric


#3

Yes! and I think specifically if it was directly proportional to the circumference. So, to calculate the number of points you should have, calculate the circumference for your current radius: 2 * PI * radius.

A contour map rule for topographic maps is that the lines cannot cross – they can only touch, which signifies a sheer drop, but a point can never have two elevations. This may be harder to enforce if the points are not aligned across rings – you don’t know the position of the inner ring, so you have to either discover it or to simply set the noise ranges such that crossing is impossible – but that gives your contours a very specific distribution and makes close lines quite rare (as they only arise as a combination of 2 extremes).


#4

Thank you both for the inputs. This is what I got so far, I can make multiple concentric irregular circle, but they have the exact same shape, because i declared random noise within the object. What changes is needed to make each object different? And afterwards how can I animate by frame as it’s static now? Thanks a lot.

//shapes that resemble the cross-section of cut crystal
//dense blue rings interspersed by white rings

let cross=[];
let cross_num=2;
let w=1200, h=600;


function setup() {
  createCanvas(w, h);
  noiseSeed(99);
  background(255);
  angleMode(DEGREES);
  colorMode(RGB);
  //start n stop of color range

  for (let i=0; i<cross_num; i++){
                            // x           , y             ,rad, 
    cross[i] = new CrossSec(random(80, 500), 
    200, 
    random(160,30), 
    random(2,5));
  }
}

function draw() {  
    for (let i=0; i<cross_num; i++){
        cross[i].display();
      }

}


class CrossSec{
    //isolines
    constructor(tempX, tempY, tempR,tempN) {
        this.x=tempX;
        this.y=tempY;
        this.r=tempR;
        this.n=tempN;

    }
    
    display() {
        for (let j=0;j<this.n; j++){
            //repeat light to dark color gradation
            beginShape();
            push()
            translate(this.x, this.y);
            for (let i=0;i<90;i++) {
                let radius=this.r+ map(noise(i/5),0,1,6,60) - 20 * j;
                let x=sin(4*i);
                let y=cos(4*i);
                vertex(radius*x, radius*y);
            }
            endShape(CLOSE);
            pop();
           
            }
        }
}


#5

Try this. If you click in the canvas, you will get a new pattern.
Notice the small change in the noise function.

Kf

//shapes that resemble the cross-section of cut crystal
//dense blue rings interspersed by white rings

let cross=[];
let cross_num=2;
let w=1200, h=600;


function setup() {
  createCanvas(w, h);
  noiseSeed(99);
  background(255);
  angleMode(DEGREES);
  colorMode(RGB);
  noLoop();
  //start n stop of color range

  for (let i=0; i<cross_num; i++) {
    // x           , y             ,rad, 
    cross[i] = new CrossSec(random(80, 500), 
      200, 
      random(160, 30), 
      random(2, 5));
  }
}

function draw() {  
  for (let i=0; i<cross_num; i++) {
    cross[i].display();
  }
}

function mouseReleased() {
  redraw();
}


class CrossSec {
  //isolines
  constructor(tempX, tempY, tempR, tempN) {
    this.x=tempX;
    this.y=tempY;
    this.r=tempR;
    this.n=tempN;
  }

  display() {
    for (let j=0; j<this.n; j++) {
      //repeat light to dark color gradation
      beginShape();
      push();
      translate(this.x, this.y);
      let mynoiseseed=random(1000);
      for (let i=0; i<90; i++) {
        let radius=this.r+ map(noise(mynoiseseed+i/5.0), 0, 1, 6, 60) - 20 * j;
        let x=sin(4*i);
        let y=cos(4*i);
        vertex(radius*x, radius*y);
      }
      endShape(CLOSE);
      pop();
    }
  }
}


#6

Thanks! works really well.