Saturday, 5 February 2011


When Reprap machines print holes they tend to come out undersized, even if the linear dimensions of an object are spot on. There are several effects that all make holes smaller than they should be: -

Faceting error
When CAD systems convert cylinders to triangles they produce a polygonal prism, so holes represented in an STL file are polygons with their vertices on the circumference of the original circle. That means the sides of the polygon are inside the circle, shrinking it by cos(π / n).
You need 10 vertices to reduce the error to 5% and 22 for 1%. So this error quickly becomes small as n increases but that creates another error:-

Segment pausing
When a circle is broken into a lot of little segments the start up time for a segment becomes significant. Reprap in the past has suffered from this really badly and I am unsure what the current status is. Slow serial comms and complex floating point firmware add pauses where extra filament can ooze from the nozzle.

I have never suffered from pausing because I use a 100Mbit Ethernet connection, which has a very low latency, and the data is transmitted in binary and in the units my firmware works in. This means that no further processing is required other than calculating which of the three axes has to go the furthest. However, I use trapezoidal acceleration on each segment, so for very short segments the average speed will be a little lower.

Arc shrinkage
When a flat strip of filament is bent into an arc there is too much plastic on the inside of the curve and too little on the outside. That makes both the inside and outside edges a smaller diameter than they should be. Adrian calculated a formula for it here: The formula comes out with a figure that is too small though. I think there is a secondary effect:

Corner cutting
When filament is dragged round a corner it likes to take a short-cut. This depends on how elastic the filament is and how much it is being stretched. I think when the nozzle moves in a circle the filament is continually trying to cut the corner and ends up forming a smaller diameter circle. I think this is the dominant effect on my machines.

Obviously, if you lie to Skeinforge about how wide your filament is that will make holes even smaller, but that is just a calibration problem.

Ideally all these effects should be compensated for in the slicing software but what has happened instead recently is that people are using parametric values in OpenScad to tweak the holes to come out right on their machines. That is the wrong approach because when the holes comes out smaller than they should be, without the slicing software compensating for it, then the infill doesn't meet it as tightly as it should do.

When I started printing Prusa Mendel parts I found the values in the configuration file far too big. I have also noticed this when downloading some designs from Thingiverse. That implies that my holes shrink less than a lot of other peoples, which is odd because all the effects above don't depend on the machine, apart from segment  pausing.

Some of the holes in Josef's parts are octagonal. That made me realise that polygons with low vertex counts don't shrink. The inside of the hole is defined by straight lines and they get extruded in the correct place. What does happen though is that the corners of the polygon are rounded. As long as the polygon has a small number of vertices, the corners are far enough from the circle that they can be rounded without impinging on it. The ideal number of vertices is when the corner cutting just meets the circle.

I decided to investigate this using OpenScad. I made a script that generates holes from 1 to 10mm with vertex counts from 3 to 8, 10, 16 and 32. The diameter of the holes is increased to make the polygon edges tangential to the circular hole. I.e. removing the faceting error by dividing by cos(π / n).

difference() {
    cube(size = [95,125,3]);
    for(i = [1:9]) {
        assign(v=[3,4,5,6,7,8,10,16,32][i - 1]) {
     assign(shrink =  cos (180 / v)) {
                for(d = [1:9]) {
                    translate([d * d + 5 - ((v == 3) ? 3 : 0), 13 * i, 0]) 
                         cylinder(h= 20, r = (d/2)/shrink, $fn= v);

I printed the resulting shape on HydraRaptor and used drill shanks to gauge the hole sizes. Not terribly accurate as the shanks tend to be a little smaller than the tip. I inserted the drills in the highest vertex count hole that it would fit in.

A pattern emerged that the seemed to indicate the maximum number of vertices you can have before the hole shrinks is twice the hole size in mm. The only drill I couldn't fit was the 1mm drill because you can't have a polygon with only two sides. The "1mm" triangular hole did at least leave a hole though, whereas higher polygon counts fill in completely.

To test this simple rule I made a new shape with holes from 1mm to 10.5mm in 0.5mm steps with the number of vertices set to twice the diameter and the diameter increased by cos(π / n).

module polyhole(h, d) {
    n = max(round(2 * d),3);
        cylinder(h = h, r = (d / 2) / cos (180 / n), $fn = n);

difference() {
 cube(size = [100,27,3]);
    union() {
     for(i = [1:10]) {
            translate([(i * i + i)/2 + 3 * i , 8,-1])
                polyhole(h = 5, d = i);
            assign(d = i + 0.5)
                translate([(d * d + d)/2 + 3 * d, 19,-1])
                    polyhole(h = 5, d = d);

I found that all my drills bigger than 1mm fit. The large ones are a snug fit and the smaller ones a little loose, probably because with only a few tangential points touching there is little friction.

These two tests where done on HydraRaptor extruding 0.375mm filament from a 0.4mm nozzle. I printed this the test again on my Mendel with 0.6mm filament through a 0.5mm nozzle and the drills still fit, so it seems universal, at least amongst my machines. It would be interesting to see if others get the same result, so I have put the files on Thingiverse.

My goal is to work out how to print circular holes the correct size, but this seems like a good hack for OpenScad designs to allow holes to come out the right size, regardless of the printer or whether it compensates hole diameters. For example, one would expect circular holes to come out right on a professional printer, so if you have oversized circular holes in your model they will come out too big. However, if you use these low vertex count polygonal holes they should still come out the right size as one would also expect a professional printer to print polygons at least as accurately.


  1. Is it possible that keeping a fixed side length $fs achieves a similar effect, if you scale the radius appropriately ?

  2. I think for large holes the length of the sides tends to pi/2 but it is less for small holes, so no.

  3. Chris!

    You've really answered a lot of questions I had about this very problem. Thanks!

  4. Your documentation and method of problem solving is outstanding!! Thank you for sharing your work in such a consise way it is very much appricated.

  5. Great post, as always :) but it solves only the openSCAD design.

    I still believe the solution need to be embedded into slicer rather then designing STL to fit the printer. I was playing with stretch plugin in Skeinforge for a while and when calibrated properly, I can "in most cases" get "almost all" holes to have exactly the designed size (so if your hole in STL is 8mm you will measure 8mm hole on printed part). Now looks like either Enrique changed the plugin a bit or my heated bed is too important variable but I can't get it right since I reactivated my heated bed and got this new batch of ABS..


  6. Awesome with this I can do function like "accurateHole" which will be based on your findings :-)

  7. I think the reason you are seeing a lot less shrinkage on your holes is due other printers suffering from a phenomenom where the first few mm (or more) of an extruded path is thinner than predicted in software. This tends towards a lack of inter-layer adhesion during the path and results in the corner-cutting being exagerated. A non-reversing extruder oozes as its not extruding, the barrel pressure drops as a result and all that gives a delay before expected extrusion rate begins. Oozebane is intended to compensate for this but very few people seem to bother trying to get it dialed in. I'm now running a stepper driven extruder and am having to re-adjust, although I think I'm now getting more accurate hole sizes.

  8. Thanks, nophead. Your explorations are great!
    Btw., do you know any possibility in CoCreate (i'm using it, too) to port/use your transition scripts from OpenSCAD?

  9. I think CoCreate is script-able with LISP, so it should be possible but I have no idea how you enter and run scripts.

  10. Why is it, when I get a bee in my bonnet about a nophead post, I come to post comments and after typing about 3 pages of argument and counter argument it all becomes clear and I delete it all and say "Good work there nophead".. ;-)

  11. Hi, nophead,
    I keep coming back to your blog every couple months when I get excited about building my own 3D printer and am always faced with the same conundrum: How do I find the latest, "best" design that you have without reading back through all your posts? Sure would be nice if you had an "index", that pointed to the latest design for each component, e.g., extruder, filament driver, table heater, etc.
    Any hope that you (or some ambitious librarian) could provide something like this?
    Thanks for all the great ideas and explorations,

    Palo Alto, CA, USA

  12. "I think for large holes the length of the sides tends to pi/2 but it is less for small holes, so no."

    It does seem that it is a linear issue, based on your own formula of 2x diameter... as for small holes, I don't understand your "no"

    but it does seem that a 1.6 mm minimum segment length in STLs could be used for most holes, even non-circular holes... does it follow or did I miss something?

  13. Ohh... I see, Cos(pi/n) is the shrinkage at the midpoint of the segment... it would all be just fine if the midpoint of the segments were on the circumference, then your pi/2 segment length would work for anything more or less.. but your point was about the polygons being circumscribed and shrinking the hole size... dur.

  14. Chris (nophead),

    Can you supply the answer as to what SF setting to adjust to print a hole to drawing/STL dimension? My holes and interior features are too small but exterior is good.

    Thanks ---

  15. I am not sure SF can do the kind of correction I use. The stretch module will make holes bigger I think, but it seems to distort things like hexagons according to the documentation. I have never tried it.

  16. Hi, thanks for interesting article.
    please take a look at this post:,109845,110409#msg-110409 - and the attached picture.

    - I too don't actually get it, why outer dimension is so perfect, and the inside is so bad..

  17. Round holes come out too small for the reasons stated above. If internal squares and hexagons come out too small you are probably extruding filament that is wider than SF thinks it is. I.e. the flow rate is a little high.

    If the outside dimensions are correct it could be because you have calibrated your axes to compensate for the extra outline thickness and the shrinkage due to thermal contraction.

    The outside dimensions are the distance the axis travels plus the filament width reduced by the shrinkage (usually about 0.5%). The inside dimensions are the distance the axis travels MINUS the filament width reduced by the shrinkage. That is how you can have one correct but not the other.

    By measuring inside and outside (or a big object and a small object) and solving simultaneous equations you can separate the two errors.

  18. Thanks. my axes are calibrated to true position, not edge if a print. If I place a 25cm ruler on the hotbed, and align 0mm with the hole of the nozzle, then ask "" to travel 25cm, I'll get exactly there - my hardware is calibrated by movement, not extrusion. Extrusion width is .6mm , same as measured width, and Extrusion height is 0.4mm.

  19. Well if that was the case the internal square would come out the right size. Since is doesn't one of your assertions must be false. Either that or the version of SF you are using has a bug not present in the version I am using.

    There is no difference mechanically when drawing an internal or external square. The only thing that affects the dimensions differently is the filament width.

    You can rule out SF by checking the figures in the gcode. The external movements should be the original dimensions - 0.6 and the internal ones plus 0.6. Then both internal and external dimensions should be a little bit too small due to shrinkage when the plastic cools.

  20. I wrote an extension to OpenSCAD today so that cylinder() can take ir= ir1= ir2= for internal radius. It does the same cos() computation to go from the near part of the facet tangent to an inner circle to the vertex positions that it normally describes.
    Hopefully I'll get this patch cleaned up and submitted to the OpenSCAD maintainers soon.

  21. I hacked OpenSCAD today for cylinder() to optionally take ir= ir1= ir2= inner radius, doing the same cos() calculation to set up vertecies that outline facets which are tangent to a circle of some inner radius.
    Hopefully I'll get this patch cleaned up and submitted to the maintainers soon.

  22. I'm working on python scripting for Alibre Design and I have baked in a polyhole function. Works nicely! See: