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);
```