Need help with trigonometry or circle geometry

Hello!

For a little project, I would like to put rectangles around a circle… with a twist : the length of the rectangle, the space between each rectangle, and the circle diameter, are user parameters, they must not be calculated or modified by code. So there will be a maximum of full rectangles around the circle, and the last rectangle will be shorter, to fill the gap. No problem with this part.

My problem is that I would like the rectangles to be placed so that the outside corners of all rectangles are exactly on the circle (see the pictures below). I have no idea how to do that programmatically, without using hardcoded values. It must work for any circle diameter and any rectangle length.

Here is my code, you can try it, just change “showTheExpectedResult” to true, to see the result that I’m expecting

let showTheExpectedResult = false;
let hardcodedCircleOffset = 22;
let hardcodedRectOffset = 6;
let circleDiameter = 500;
let rectLength = 150;
let rectWidth = 50;
let rectSpacing = -3;


function setup()
{
  createCanvas( 600, 600 );
  background( 200 );
  rectMode( CENTER );
  angleMode( DEGREES );
  translate( width / 2, height / 2 );
  
  
  // Draw circle
  fill( 255, 255, 0 );
  circle( 0, 0, circleDiameter + ( showTheExpectedResult ? hardcodedCircleOffset : 0 ) );
  
  
  // Draw full rectangles
  fill( 0, 255, 0, 128 );
  let numRect = PI * circleDiameter / ( rectLength + rectSpacing );
  let numRectFloored = floor( numRect );
  let rotation = 360 / numRect;
  let radius = ( circleDiameter - rectWidth ) / 2;
  
  for ( let i = 0; i < numRectFloored; i++ )
  {
    let angle = i * rotation;
    push();
    translate( radius * cos( angle ), radius * sin( angle ) );
    rotate( angle );
    rect( 0, 0, rectWidth, rectLength );
    pop();
  }
  
  
  // Draw last rectangle to fill the gap
  radius += showTheExpectedResult ? hardcodedRectOffset : 0;
  let angle = ( numRectFloored - 1 ) * rotation / 2 - 180;
  translate( radius * cos( angle ), radius * sin( angle ) );
  rotate( angle );
  rect( 0, 0, rectWidth, rectLength * ( numRect - numRectFloored ) );
}

This is the result of this code (note the smaller rectangle in the upper right):

And what I would like to do, without hardcoding anything:

I hope someone will be able to help ! Thanks in advance :slight_smile:

Using trigonometry - Find distance between point on tangent line and circle. - Mathematics Stack Exchange

Subtract sqrt(radius*radius - (rectLength*rectLength)/4) from the radius:

2 Likes

Hi Yom,

The first thing you wanna do when having a problem like this is to draw a figure and start giving names on things. Something like this for example:

The segment [BC] correspond to the top of your first rectangle and [DE] the one of the second.
Then you start labeling the things that you know:

  • l: the length of your rectangle
  • s: the space between your rectangles
  • r: the radius of your circle

And finally you add the elements that you will need in your code:

  • x: the distance from the top of one rectangle to the center of the circle
  • alpha: the angle covered by a rectangle
  • beta: the angle covered by the spacing

Now using basic trigonometry you can easily find formulas for those elements (see screenshot)

For the code, you can reuse the one that you have.
You need to rotate by alpha + beta every time you want to draw a new rectangle.
And you need to translate by x - width / 2 since you are using rectMode(CENTER).

To help you, here is the code I made. The last rectangle is missing, I’ll let you add it:

let r = 250;
let l = 130;
let w = 50;
let s = 15;


function setup()
{
  createCanvas( 600, 600 );
  background( 200 );
  rectMode(CENTER);
  angleMode(RADIANS);
  translate(width/2, height/2);
  
  
  // Draw circle
  fill( 255, 255, 0 );
  circle(0, 0, 2*r);
  
  
  // Compute main parameters
  let offset = sqrt(r*r-l*l/4)-w/2;
  let alpha = 2 * asin(l/(2*r));
  let beta = 2 * asin(s/(2*r));
  let rectNb = floor(TWO_PI / (alpha + beta));
  
  // Draw rectangles
  fill( 0, 255, 0, 128 ); // Set rect color
  for (let i = 0; i < rectNb; i++) {
    push();
    rotate(i * (alpha + beta));
    translate(0, -offset);
    rect(0, 0, l, w);
    pop();
  }
}
5 Likes

Hi,

Nice exercise here! :wink:

Let’s first see how you can display a rectangle, you have two ways : either from it’s center (by using the rectMode(CENTER) function or by its upper left corner (by default).

Since you want the corners to touch, it will be easier to draw the rectangles from their corner.

So you’ll have this :

The idea is that we are going to draw each rectangle from its corner and rotate each time (full circle divided by the number of rectangles).

After one rectangle, we need to translate to the next location, therefore translating on the x axis by the length of the side of the polygon.

Now let’s do the math part, with the help of a geometric figure :

In this case, we divided the circle into 8 parts (hence 8 rectangles) and we annotated the different segments.

We only know alpha and b (which is the radius of the circle) so let’s solve some equations :

So we have this code :

void rectanglesAroundCircle(float x, float y, int sides, float radius) {
  pushMatrix();
  translate(x, y);
  
  fill(255, 255, 0);
  circle(0, 0, radius * 2);
  
  // The angle between two positions
  float alpha = TWO_PI / sides;
  
  // Translate to the first position
  translate(radius, 0);
  
  // We first do the initial rotation
  float oppositeAngle = (PI - alpha) / 2.0;
  rotate(PI - oppositeAngle);
  
  // Compute the length of a side of the polygon
  float sideLength = 2 * radius * sqrt(1 - pow(cos(alpha / 2), 2));
  
  fill(0, 255, 0, 100);
  
  // Display the rectangles
  for (int i = 0; i < sides; i++) {
    rect(0, 0, sideLength, 30);
    
    // Translate then rotate to the next position
    translate(sideLength, 0);
    rotate(alpha);
  }
  
  popMatrix();
}

void setup() {
  size(500, 500);
}

void draw() {
  background(200);
  
  rectanglesAroundCircle(width / 2, height / 2, 8, 200);
  
  noLoop();
}

rect_circle

This is maybe not the ideal solution but I had fun doing it! :grinning:

Have fun too!

Note : I realized that you wanted to have gaps between your rectangles, this code doesn’t provide this and @jb4x 's answer is the way to go!

3 Likes

Thank you so much for your help guys! jb4x won, but all answers were useful :slight_smile:

I couldn’t find how to do the last rectangle until I realized that my calculation of its length was wrong!

So here is the final code, it works quite well :slight_smile: But do you see anything wrong, or which could be simplified maybe ?

let r = 250;
let l = 130;
let w = 50;
let s = 15;


function setup()
{
  createCanvas( 600, 600 );
  background( 200 );
  rectMode(CENTER);
  angleMode(RADIANS);
  translate(width/2, height/2);
  
  
  // Draw circle
  fill( 255, 255, 0 );
  circle(0, 0, 2*r);
  
  
  // Compute main parameters
  let offset = sqrt(r*r-l*l/4)-w/2;
  let alpha = 2 * asin(l/(2*r));
  let beta = 2 * asin(s/(2*r));
  let rectNbF = TWO_PI / (alpha + beta);
  let rectNb = floor(rectNbF);
  
  // Draw rectangles
  fill( 0, 255, 0, 128 ); // Set rect color
  for (let i = 0; i < rectNb; i++) {
    push();
    rotate(i * (alpha + beta));
    translate(0, -offset);
    rect(0, 0, l, w);
    pop();
  }
  
  // Draw last rectangle if it's not too small
  l = 2 * r * sin((TWO_PI-(rectNb*(alpha+beta))-beta)/2);
  if (l >= 5)
  {
    offset = sqrt(r*r-l*l/4)-w/2;
    alpha = (alpha + (2 * asin(l/(2*r)))) / 2;
    rotate( -(alpha + beta) );
    translate( 0, -offset );
    rect( 0, 0, l, w );
  }
}


I’m always amazed by the beauty and power of maths!

2 Likes

Yey, looks good! :wink:

Next step : find a way for the inside corners to touch which mean determining the the appropriate height of the rectangles.

I thought I had it, but the length of the last rectangle was still wrong ! I noticed it after I “zoomed” a lot on the corners.

Here is, I think, the correct way to calculate it (but correct me if I’m still wrong!)

l = 2 * r * sin((TWO_PI-(rectNb*(alpha+beta))-beta)/2);

I have edited the previous code to hide my shame :smiley:

1 Like

Maybe as an another exercice, and that shouldn’t be too hard now, but for my needs I really needed the external space between rectangles, and the rectangle dimensions to be user parameters :slight_smile:

And thanks for your detailed answer, even if the result wasn’t what I expected, I’m sure it will help other people :wink:

1 Like

Hi Yom,

Happy to see that you are making nice progress.
You got it right :+1:

2 Likes

understanding polar coordinates is a good place to start.

https://www.mathsisfun.com/polar-cartesian-coordinates.html

1 Like

here is a little sketch for rose petals that may help to understand a bit more about trig in context. Change the value of osc to change the number of petals, and change the function as you desire for different effects.

Then change the value of the offset for even more complexity. This is the basics for visualising most curves so you arent restricted to just rose petals, but in that case you need to pas different osc variables to x and y and different functions to x and y

int total = 1000;
float x,y,radius = 100;
PVector pos;
void setup(){
  size(400,400);
  pos = new PVector(width/2,height/2);
};

void draw(){
  background(255);
  //radius = 
  for(int i=0;i<total;i++){
    
    float angle = ((2*PI)/total)*i;
    int osc = 4;
    float offset = 0;
    //offset = cos(angle);
    //offset = sin(angle);
    //offset = tan(angle);
    //offset = 1/cos(angle);
    //offset = 1/sin(angle);
    //offset = 1/tan(angle);
    
    //offset = sin(angle*osc);
    //offset = tan(angle*osc);
    //offset = 1/cos(angle*osc);
    //offset = 1/sin(angle*osc);
    //offset = 1/tan(angle*osc);
    float func = 0;
    
    func = sin((angle+offset)*osc);
    //func = cos((angle+offset)*osc);
    //func = tan((angle+offset)*osc);
    
    //func = sin(angle+offset*osc);
    //func = cos(angle+offset*osc);
    //func = tan(angle+offset*osc);
    
    //func = sin((angle+offset)*osc)*cos((angle+offset)*osc);
    //func = sin((angle+offset)*osc)*cos(angle+offset*osc);
    //func = sin((angle+offset)*osc)*cos(angle+offset*osc);
    //func = sin((angle+offset)*osc)*cos(angle+offset*osc);
    //func = sin((angle+offset)*osc)*cos(angle+offset*osc);
    //func = sin((angle+offset)*osc)*cos(angle+offset*osc);
    x = pos.x+radius * cos(angle)*func;
    y = pos.y+radius * sin(angle)*func;
    stroke(0);
    strokeWeight(1);
    point(x,y);
  }
};