OpenScad circle calculations

I have been using OpenScad recently and evolved how I control rendering circle shapes.

I find during development quick rendering is desirable. When inspecting that shapes line up correctly, accuracy is needed. For final models you want a nice smooth even surface. Also certain objects have a fixed number of sides e.g. nuts and hex bolts.

There are three built in variables ($fa, $fs, and $fn) for controlling the number of faces a circle shape is constructed with. $fa, and $fs constrain the maximum angle between faces and outer length of faces respectively. OpenScad uses whichever results in less faces. If $fn is defined it overrides $fa and $fs, just setting the number of faces.

OpenScad circle variables, $fa, $fs, and $fn

  • For a quick preview I tend to use $fn = 12.
  • For an even final model I set $fn = 2 * PI * r;
  • For an accurate preview I use $fn = ceil(360 / (2 * asin( sqrt(pow(r, 2) - pow(r – 0.25, 2) ) / r)));
  • Also, to guarantee a minimum radius see HydraRaptor: Polyholes

The ‘even’ formula calculates the number of faces so that the outer length of each faces is 1. Similar to $fs = 1 but only when $fa is not the limiting variable.

The ‘accurate’ formula calculates the number of faces for rendering a circle with a maximum error between the ideal circle and approximation.

Error between ideal circle and approximation

Experimenting with circles in OpenScad
From left to right

  • Default behaviour
  • ‘Preview’
  • $fs = 1
  • ‘Even’
  • ‘Accurate’

To make life easier I define my own circle modules (with capital letters) and use instead of the normal OpenScad functions.

// Circles.scad
CircleModePreview = 1;
CircleModeAccurate = 2;
CircleModeEven = 3;


$CircleMode = CircleModeAccurate;


function FnPreview(r) = 12;
MaximumLength = 1;
function FnEven (r) = 2 * PI * r / MaximumLength;
MaximumError = 0.25;
function FnAccurate(r) = ceil(360 / (2 * asin(sqrt(pow(r, 2) - pow(r - MaximumError, 2)) / r)));
function FnDefault(r) = $fn == undef ? min(360 / $fa, 2 * PI * r / $fs) : $fn;


function Fn(r) = ($CircleMode == undef) ? 
	FnDefault(r)
: ($CircleMode == CircleModePreview) ? 
	FnPreview(r)
: ($CircleMode == CircleModeAccurate) ? 
	FnAccurate(r)
: ($CircleMode == CircleModeEven) ? 
	FnEven(r)
:
	FnDefault(r);


module Cylinder(r, h, r1, r2)
{
	if(r != undef)
	{
		cylinder(r = r, h = h, $fn = Fn(r));
	}else
	{
		cylinder(r1 = r1, r2 = r2, h = h, $fn = Fn(r));
	}
}


module Circle(r)
{
	circle(r = r, $fn = Fn(r));
}


module Sphere(r)
{
	sphere(r = r, $fn = Fn(r));
}


module Rotate_extrude (r)
{
	for (childIndex = [0 : $children-1])
	{
		rotate_extrude($fn = Fn(r))
		children(childIndex);
	}
}


// Example.scad
// default
cylinder(r = 10, h = 10);


translate([5, 110, 0])
rotate([0, -90, 0])
cylinder(r = 100, h = 10);


// preview
translate([25, 0, 0])
cylinder(r = 10, h = 10, $fn = 12);


translate([30, 110, 0])
rotate([0, -90, 0])
cylinder(r = 100, h = 10, $fn = 12);


// even
translate([50, 0, 0])
cylinder(r = 10, h = 10, $fn = 2 * PI * 10);


translate([55, 110, 0])
rotate([0, -90, 0])
cylinder(r = 100, h = 10, $fn = 2 * PI * 100);


// accurate
translate([75, 0, 0])
Cylinder(r = 10, h = 10);


translate([80, 110, 0])
rotate([0, -90, 0])
Cylinder(r = 100, h = 10);
2 Likes

thanks for this really well explained and documented info - have added it to my design notes and will be using it very soon as I have been running into some of these issues.

1 Like

Your welcome, I’m glad you like it.

I mentioned a link to this on twitter, resulting in this feedback: “And don’t forget, you can easily make nuts with $fn=6. @openscadhttps://twitter.com/greycells/status/713845085260156929

1 Like