discuss@lists.openscad.org

OpenSCAD general discussion Mailing-list

View all threads

$fe - Tolerance based arc approximation

T
Trygon
Sun, Oct 25, 2015 9:31 AM

Objects such as cubes are precise when rendered, but objects such as
cylinders are not because the circular arc has to be approximated by facets.
This approximation process is controlled by the special variables $fa, $fs &
$fn.

Having come to OpenSCAD primarily from a design engineering background I am
used to specifying tolerances for specific features, e.g. 20mm +/- 0.1mm.
Wanting to use this principle for arc approximation I have adopted the
approach set out below which I hope might be useful to others.

The maximum error for a facet occurs at it centre, when it is furthest from
the true circular arc that it approximates. I use variable $fe in my
OpenSCAD scripts to specify the maximum acceptable value of this error (the
distance from the centre of the facet to the true circular arc, measured
normal to the facet).  I then use the following function to calculate a
value for $fa based on the arc radius and $fe:

// if $fn>0 it takes priority, returns 360/$fn
// if $fe<=0 or is undefined, returns $fa
// never returns more than 45 deg => circle -> octagon (if $fe>0 and
!($fn>0))
// Note $fs must be set to a small value so that it has no effect (does not
override $fa)
function
fa(r)=($fn>0)?360/$fn:($fe>0)?($fe<r)?min(45,2*acos(1-$fe/r)):45:$fa;

$fa, $fs & $fn need to be set such that the value for $fa calculated by
fa() is always used, my typical script header is:

$fs=0.01; // small => generally no effect
$fn=undef; // => ignored
$fa=360/5; // large, but not used, identifies errors where $fa not set &
passed

Examples:

  1. a cylinder with radius 20mm and tolerance +/-0.1mm is coded as:

$fe=0.1; // acceptable error dimension, arc facets to true arc path
r=20;
cylinder(r=r,h=20,$fa=fa(r));

  1. a torus:

$fe=0.1; // acceptable error dimension, arc facets to true arc path
major_r=40;
minor_r=20;
rotate_extrude($fa=fa(major_r+minor_r)) translate([major_r,0,0])
circle(minor_r,$fa=fa(minor_r));

  1. $fe can also be adjusted for specific objects within a model (rough
    cylinder with accurate bore):

$fe=0.5; // acceptable error dimension, arc facets to true arc path
r1=40;
r2=20;
h=20;
difference(){
cylinder(r=r1,h=h,center=true,$fa=fa(r1));
cylinder(r=r2,h=h+1,center=true,$fa=fa(r2,$fe=0.1));
}
// Note: The new value for $fe is explicitly passed to function fa() not to
cylinder().
// Coding as cylinder(r=r2,h=h+1,center=true,$fe=0.1,$fa=fa(r2)); is
unreliable,
//  it is unclear which value for $fe is within scope when fa(r2) is
evaluated

Having coded curved objects like this, the number of facets will
automatically adjust to keep the object within tolerance if the dimension
parameters are changed.

This method produces a mesh that is within the desired tolerance, but this
takes no account of the tolerance of any machine that might be used to
produce the object.

--
View this message in context: http://forum.openscad.org/fe-Tolerance-based-arc-approximation-tp14212.html
Sent from the OpenSCAD mailing list archive at Nabble.com.

Objects such as cubes are precise when rendered, but objects such as cylinders are not because the circular arc has to be approximated by facets. This approximation process is controlled by the special variables $fa, $fs & $fn. Having come to OpenSCAD primarily from a design engineering background I am used to specifying tolerances for specific features, e.g. 20mm +/- 0.1mm. Wanting to use this principle for arc approximation I have adopted the approach set out below which I hope might be useful to others. The maximum error for a facet occurs at it centre, when it is furthest from the true circular arc that it approximates. I use variable $fe in my OpenSCAD scripts to specify the maximum acceptable value of this error (the distance from the centre of the facet to the true circular arc, measured normal to the facet). I then use the following function to calculate a value for $fa based on the arc radius and $fe: // if $fn>0 it takes priority, returns 360/$fn // if $fe<=0 or is undefined, returns $fa // never returns more than 45 deg => circle -> octagon (if $fe>0 and !($fn>0)) // Note $fs must be set to a small value so that it has no effect (does not override $fa) function fa(r)=($fn>0)?360/$fn:($fe>0)?($fe<r)?min(45,2*acos(1-$fe/r)):45:$fa; $fa, $fs &amp; $fn need to be set such that the value for $fa calculated by fa() is always used, my typical script header is: $fs=0.01; // small => generally no effect $fn=undef; // => ignored $fa=360/5; // large, but not used, identifies errors where $fa not set & passed Examples: 1) a cylinder with radius 20mm and tolerance +/-0.1mm is coded as: $fe=0.1; // acceptable error dimension, arc facets to true arc path r=20; cylinder(r=r,h=20,$fa=fa(r)); 2) a torus: $fe=0.1; // acceptable error dimension, arc facets to true arc path major_r=40; minor_r=20; rotate_extrude($fa=fa(major_r+minor_r)) translate([major_r,0,0]) circle(minor_r,$fa=fa(minor_r)); 3) $fe can also be adjusted for specific objects within a model (rough cylinder with accurate bore): $fe=0.5; // acceptable error dimension, arc facets to true arc path r1=40; r2=20; h=20; difference(){ cylinder(r=r1,h=h,center=true,$fa=fa(r1)); cylinder(r=r2,h=h+1,center=true,$fa=fa(r2,$fe=0.1)); } // Note: The new value for $fe is explicitly passed to function fa() not to cylinder(). // Coding as cylinder(r=r2,h=h+1,center=true,$fe=0.1,$fa=fa(r2)); is unreliable, // it is unclear which value for $fe is within scope when fa(r2) is evaluated Having coded curved objects like this, the number of facets will automatically adjust to keep the object within tolerance if the dimension parameters are changed. This method produces a mesh that is within the desired tolerance, but this takes no account of the tolerance of any machine that might be used to produce the object. -- View this message in context: http://forum.openscad.org/fe-Tolerance-based-arc-approximation-tp14212.html Sent from the OpenSCAD mailing list archive at Nabble.com.
R
runsun
Wed, Oct 28, 2015 4:36 PM

Very cool. Thx for sharing.


$  Runsun Pan, PhD

$ libs: doctest , faces ( git ), offline doc ( git ),runscad.py( 1 , 2 , git );

$ tips: hash( 1 , 2 ), sweep , var , lerp , animGif

--
View this message in context: http://forum.openscad.org/fe-Tolerance-based-arc-approximation-tp14212p14240.html
Sent from the OpenSCAD mailing list archive at Nabble.com.

Very cool. Thx for sharing. ----- $ Runsun Pan, PhD $ libs: doctest , faces ( git ), offline doc ( git ),runscad.py( 1 , 2 , git ); $ tips: hash( 1 , 2 ), sweep , var , lerp , animGif -- View this message in context: http://forum.openscad.org/fe-Tolerance-based-arc-approximation-tp14212p14240.html Sent from the OpenSCAD mailing list archive at Nabble.com.
T
Trygon
Fri, Dec 4, 2015 11:39 AM

New version of function fa():

// returns a value for $fa
// value is calculated from the acceptable linear distance error $fe,
// note: $fs must be set to a small value so that it has no effect,
// e.g. if $fe=0.1 & $fs=0.01 then the $fa=fa(r) will ensure that the
//  distance mid facet to the actual required radius = 0.1
// never returns more than 45 deg => circle -> octagon (if $fe>0 and not
$fn>0)
// if $fe<=0 or is undefined function returns the "standard" OpenSCAD
combination
//  of $fa & $fs [C code fragment: fmax(fmin(360.0 / fa, r2M_PI / fs),
5)]
// if $fn>0 it takes priority, fa() returns 360/max($fn,3)
function fa(r)=$fn>0?360/($fn>3?$fn:3):
$fe>0?$fe<r?min(45,2acos(1-$fe/r)):45:
360/max(min(360/($fa>0.01?$fa:0.01),
2
PI*r/($fs>0.01?$fs:0.01)),5);

This now takes account of $fa & $fs and can be used to help code user
modules and functions that draw arcs:

// equivalent to circle(20);
CircleToo(20);

module CircleToo(r){
n=ceil(360/fa(r)); // number of segments
p=[for(i=[0:n-1]) let(a=i360/n) [rcos(a),r*sin(a)]];
polygon(p);
}

Here the output of CircleToo() is modified by $fn, $fa & $fs in the same
manner that the built-in primitives are, however it is also modified by $fe
(see my 1st post):

$fe=0.05;
CircleToo(20);

Cheers,
Trygon

--
View this message in context: http://forum.openscad.org/fe-Tolerance-based-arc-approximation-tp14212p14933.html
Sent from the OpenSCAD mailing list archive at Nabble.com.

New version of function fa(): // returns a value for $fa // value is calculated from the acceptable linear distance error $fe, // note: $fs must be set to a small value so that it has no effect, // e.g. if $fe=0.1 & $fs=0.01 then the $fa=fa(r) will ensure that the // distance mid facet to the actual required radius = 0.1 // never returns more than 45 deg => circle -> octagon (if $fe>0 and not $fn>0) // if $fe<=0 or is undefined function returns the "standard" OpenSCAD combination // of $fa & $fs [C code fragment: fmax(fmin(360.0 / fa, r*2*M_PI / fs), 5)] // if $fn>0 it takes priority, fa() returns 360/max($fn,3) function fa(r)=$fn>0?360/($fn>3?$fn:3): $fe>0?$fe<r?min(45,2*acos(1-$fe/r)):45: 360/max(min(360/($fa>0.01?$fa:0.01), 2*PI*r/($fs>0.01?$fs:0.01)),5); This now takes account of $fa & $fs and can be used to help code user modules and functions that draw arcs: // equivalent to circle(20); CircleToo(20); module CircleToo(r){ n=ceil(360/fa(r)); // number of segments p=[for(i=[0:n-1]) let(a=i*360/n) [r*cos(a),r*sin(a)]]; polygon(p); } Here the output of CircleToo() is modified by $fn, $fa & $fs in the same manner that the built-in primitives are, however it is also modified by $fe (see my 1st post): $fe=0.05; CircleToo(20); Cheers, Trygon -- View this message in context: http://forum.openscad.org/fe-Tolerance-based-arc-approximation-tp14212p14933.html Sent from the OpenSCAD mailing list archive at Nabble.com.