DIviding a circle into rings of equal area - howto?

edited November 2014 in p5.js

I was attempting to make a little sketch to divide a circle into rings of equal area. I first wrote this in Processing:

// Circular rings of equal area
// D = diameter of the widest circle
// rings = number of rings we want

int D = 100;
float A = PI*D*D/4;
int rings = 3;

void setup(){
  size(800,800, OPENGL);
  smooth();
  strokeWeight(1);
}

void draw(){
  int D = 100;
  float A = PI*D*D/4;
  background(255);

  for(int y = 0; y < rings; y++){
  float r = random(255);
  float g = random(255);
  float b = random(255);
  float R = D/2;
//  fill(r, g, b);
  ellipse(width/2, height/2, D, D);
  D += 2*A/PI/R;
  }
}

I wanted each ring to have its own color but either I'm tired or just not very good and I can't do it. But the 3 rings draw. Another thing is that I want it to stop after creating the set number of rings, yet at the same time I want to be able to use mousepressed() to capture images with save() or saveFrame(), but I don't get the interaction with noLoop();. Anyways, I've removed that now to focus on the basics. So I'm trying this in p5.js:

function setup(){
  createCanvas(800,800);
  smooth();
  strokeWeight(1);
}

function draw(){
  var D = 100;
  var rings = 3;
  var A = PI * D * D / 4;
  background(255);

  for(var y = 0; y < rings; y++){
  var r = random(255);
  var g = random(255);
  var b = random(255);
  var R = D / 2;
 // fill(r, g, b);
  ellipse(width / 2, height / 2, D, D);
  D += 2 * A / PI / R;
  }
}

But this draws one circle only. What is it I'm not doing right here? If I put something like var A = PI * D * D / 4; before draw() and setup(), I get ReferenceError: PI is not defined but it's a constant so I must be handling something incorrectly...


Looking at this further I think I'm drawing this from the center outward... is my math even right??... investigating.

Tagged:

Answers

  • edited November 2014

    Your Processing sketch is buggy! It only works when renderer is P3D/OPENGL! 8-}
    In order to fix it, you should include noFill(), so it stops the bigger ellipses "devouring" the smaller 1s!

    Here's a fixed version which should work in whatever renderer:

    // forum.processing.org/two/discussion/8119/
    // dividing-a-circle-into-rings-of-equal-area-howto
    
    final int QTY = 3, DIAM = 100, RAD = DIAM>>1;
    final float A = QUARTER_PI*DIAM*DIAM;
    final int GROWTH = (int) (2.0*A/PI/RAD);
    
    size(400, 400, JAVA2D);
    noLoop();
    stroke(0);
    noFill();
    smooth(4);
    strokeWeight(2);
    background(0350);
    ellipseMode(CENTER);
    
    int d, i = QTY, cw = width>>1, ch = height>>1;
    while (i-- != 0)  ellipse(cw, ch, d = GROWTH*i + DIAM, d);
    

    And while I was at it, I did a p5.js version of it as well: :-bd

    // forum.processing.org/two/discussion/8119/
    // dividing-a-circle-into-rings-of-equal-area-howto
    
    const QTY = 3, DIAM = 100, RAD = DIAM>>1;
    const A = QUARTER_PI*DIAM*DIAM, GROWTH = 2*A/PI/RAD;
    
    function setup() {
      createCanvas(400, 400);
      ellipseMode(CENTER).noLoop();
      stroke(0), strokeWeight(2);
      smooth(), noFill(), background(0350);
    
      const cw = width>>1, ch = height>>1;
      for (var d, i = QTY; i--; ellipse(cw, ch, d = GROWTH*i + DIAM, d));
    }
    
  • Thank you!! You must have thought that because the thickness of the rings varied that it was a bug, or where you talking about all the other numerous bugs I have loll. No, actually I want the rings to be of equal surface/area, not thickness!! See for instance this. In that context all thicknesses are different as its the area of the ring that is equal... I think I'm going to have to sit down with the math... but thanks for the equal rings and the port, I appreciate!

  • edited November 2014

    So I think the following is very close - Processing:

    // Circular rings of equal area - paHern_pAni(
    // D = diameter of the smallest circle - this will be drawn   
    // outward, so not as predictable as I wanted but will do for now.
    // rings = number of rings we want
    
    float D = 100;
    float R = D/2;
    float A = PI*R*R;
    
    int rings = 3;
    
    void setup(){
    size(300, 300);
     smooth();
     noLoop();
     noStroke();
    }
    
    void draw(){
    background(255);
    ellipse(width/2, height/2, D, D);
    
    for (int y = 0; y < rings; y++){
      float r = random(255);
      float g = random(255);
      float b = random(255);
      fill(r, g, b, 50);
      float cur = R * ( sqrt( y+1/rings+1) );
      ellipse(width/2, height/2, 2*cur, 2*cur);
      }
    }
    

    p5.js

    // Circular rings of equal area - p5.js v.
    // D = diameter of the widest circle
    // rings = number of rings we want
    
    function setup(){
      createCanvas(300,300);
        smooth();
        noLoop();
        noStroke();
    }
    
    function draw(){
    var rings = 3;
    var D = 100;
    var R = D/2;
    var A = PI*R*R;
      background(255);
    //  ellipse(width/2, height/2, D, D);
    
    for (var y = 0; y < rings; y++){
      var r = random(255);
      var g = random(255);
      var b = random(255);
      fill(r, g, b, 50);
      var cur = R * ( sqrt( y+1/rings+1) );
      ellipse(width/2, height/2, 2*cur, 2*cur);
      }
    }
    

    I'm taking screenshots and I'm measuring that to make sure it complies lolll. As you can see, it draws outward; I'm thinking about whether it's possible to reverse that to make it more predictable! Thank you for your help!

  • Answer ✓

    This might help

    void setup() {
      size(600, 600);
      float area = TWO_PI * 100 * 100;
      float[] r = new float[8];
      background(255);
      ellipseMode(CENTER);
      for (int i = 0; i < r.length; i++) {
        r[i] = sqrt(area*(i+1)/TWO_PI);
      }
      // Draw circles
      strokeWeight(1);
      stroke(0);
      for (int i = r.length-1; i >= 0; i--) {
        fill(96 + i*(160.0 /r.length));
        ellipse(width/2, height/2, 2*r[i], 2*r[i]);
      }
      // Display radii
      fill(200, 0, 0);
      for (int i = 0; i < r.length; i++) {
        float deltaY =  (i == 0) ? r[0]/2 : (r[i] + r[i-1])/2;
        text("" + r[i], width/2, 4 - deltaY + height/2);
      }
    }
    

    circles

  • edited November 2014

    Thank you, your version works and also helps confirm that mine works too! After many hours and bugs!! The thing is I wish it were more predictable for graphic design usage i.e. I would like to specify the widest circle as value and have the circles drawn outside in and not inside out... it's all lots of fun! Thank you!


    Actually, let me make sure my stuff really matches. I can't integrate it for now but it looked pretty close running both side by side. I'll check it out. Also, yes, TWO_PI etc. lots of tidbits to learn from. Thanks again!


    WIP - see this.

  • I would like to specify the widest circle as value and have the circles drawn outside in and not inside out.

    That is easy enough but the radii of the inner circles will depend on the number of them so you would have to specify both the maximum rasius and the number of circles.

  • edited November 2014

    Thank you for your advice. I'm very much a beginner. The link I provided in the post above shows you the solution I try to use to leverage the same values to generate both inward and outward circles, but in the former case, I can't draw the first ring but the ones that follow seem fine. Otherwise it seems ok. I'll try to implement text with measurements like you showed and I'll see if I'm off!


    So I am off, by 10px loll. I have lots to learn! Thanks again!

    p.s. finally funkier than I thought; when I pasted the PS and p5.js examples above yours(@quark), finally I had not noticed but the PS one lines up perfectly with yours graphically... whereas the p5.js version I made from it 10px off.

  • edited November 2014 Answer ✓

    This version starts with a fixed size circle. Change the number of inner circles with the n and m keys

    int nbr = 6;
    float[] r;
    float MAX_RADIUS = 296;
    
    void setup() {
      size(600, 600);
      ellipseMode(CENTER);
      r = getCircleRadii(MAX_RADIUS, nbr);
    }
    
    void draw() {
      background(255);
      // Draw circles
      strokeWeight(1);
      stroke(0);
      for (int i = r.length-1; i >= 0; i--) {
        fill(96 + i*(160.0 /r.length));
        ellipse(width/2, height/2, 2*r[i], 2*r[i]);
      }
      // Display radii
      fill(200, 0, 0);
      for (int i = 0; i < r.length; i++) {
        float deltaY =  (i == 0) ? r[0]/2 : (r[i] + r[i-1])/2;
        text("" + r[i], width/2, 4 - deltaY + height/2);
      }
    }
    
    // Decrease and increase number of circles with the
    // n and m keys
    void keyTyped() {
      if (key == 'n' && nbr > 1) {
        nbr--;
        r = getCircleRadii(MAX_RADIUS, nbr);
      }
      if (key == 'm' && nbr < 10) {
        nbr++;
        r = getCircleRadii(MAX_RADIUS, nbr);
      }
    }
    
    float[] getCircleRadii(float maxRadius, int nbrCircs) {
      float area = TWO_PI * maxRadius * maxRadius / nbrCircs;
      float[] radii = new float[nbrCircs];
      for (int i = 0; i < radii.length; i++) {
        radii[i] = sqrt(area*(i+1)/TWO_PI);
      }
      return radii;
    }
    
  • edited November 2014

    Thanks for the top notch answers, just tried this last one! This is really a blast! Can I select 2 answers, let me try that lolll.(yep). This will be useful! I will backtrack to my unsuccessful port and try to make it happen! Thanks for you help!

Sign In or Register to comment.