For reasons that aren’t relevant here, Mary asked me to make four sets of improved lipstick / lip balm / sunscreen holders with five smaller tubes plus the central one and an inscription on the bottom. I ran off one with the last of the cyan PETG and the other three in natural PETG:

I embossed the text into the bottom three layers. The tiny spots of detached infill for lowercase letters like a didn’t adhere to the platform, mostly because the retraction settings that work well for larger areas don’t push enough plastic out to bond with the platform before retracting and moving away.
The bridging layer over the text shows Slic3r doing the best it can (clicky for more far more dots). Laying a uniform patch over all the letters in one shot would work better, but I don’t know how you’d define an algorithm that specifies when such a situation occurs:

The solid infill layer directly over the Hilbert Curve bottom layer came out grossly severely excessively overstuffed, to the extent that the accumulation reduced the flow of molten plastic and caused the filament drive to strip:

Previewing the G-Code show nothing out of the ordinary and, after considerable flailing around, I finally set Slic3r to begin the 3D Honeycomb infill directly atop the Hilbert Curve bottom layer. That provided enough open space to complete the mission, but more debugging was in order.
The OpenSCAD source code as a GitHub gist:
// Lipstick and Balm Tube Holder | |
// Ed Nisley KE4ZNU - February 2016 | |
//- Extrusion parameters - must match reality! | |
ThreadThick = 0.25; | |
ThreadWidth = 0.40; | |
function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit); | |
Protrusion = 0.1; | |
HoleWindage = 0.2; | |
//------ | |
// Dimensions | |
RawDia = [27,18,16,18,16,18]; // actual tube diameters in desired order, center = largest first | |
NumTubes = len(RawDia); | |
Clearance = 2.0; | |
TubeDia = [for (i=[0:NumTubes-1]) (RawDia[i] + Clearance)]; // actual tube diameters | |
TubeRad = TubeDia / 2; | |
echo(str("NumTubes: ",NumTubes)); | |
Wall = 2.0; | |
BaseThick = 2.0; | |
BaseFactor = 2.0; | |
NumSides = 12*4; | |
// per-tube info, first element forced to 0 to make entries match RawDia vector indexes | |
Radius = [0, for (i=[1:NumTubes-1]) (TubeRad[0] + TubeRad[i] + Wall)]; // Tube[i] distance to center point | |
echo(str("Radius: ",Radius)); | |
CtrToCtr = [0, for (i=[1:NumTubes-2]) (TubeRad[i] + TubeRad[i+1] + Wall)]; // Tube[i] distance to Tube[i+1] | |
echo(str("CtrToCtr: ",CtrToCtr)); | |
Angle = [0, for (i=[1:NumTubes-2]) acos((pow(Radius[i],2) + pow(Radius[i+1],2) - pow(CtrToCtr[i],2)) / (2 * Radius[i] * Radius[i+1]))]; | |
echo(str("Angle: ",Angle)); | |
TotalAngle = sumv(Angle,len(Angle)-1); | |
echo(str("TotalAngle: ",TotalAngle)); | |
//---------------------- | |
// Useful routines | |
// vector sum cribbed from doc | |
function sumv(v,i,s=0) = (i==s ? v[i] : v[i] + sumv(v,i-1,s)); | |
//---------------------- | |
//- Build it | |
difference() { | |
union() { | |
for (i=[0:NumTubes-1]) | |
rotate(90 - TotalAngle/2 + sumv(Angle, (i>0) ? (i-1) : 0)) | |
translate([Radius[i],0,0]) { | |
resize([0,0,2*BaseThick]) // bases | |
difference() { | |
sphere(r=BaseFactor*TubeRad[i],$fn=NumSides); | |
translate([0,0,-BaseFactor*TubeDia[i]]) | |
cube(2*BaseFactor*TubeDia[i],center=true); | |
} | |
difference() { // tubes | |
cylinder(r=TubeRad[i] + Wall,h=1.5*TubeDia[i] + BaseThick,$fn=NumSides); | |
cylinder(d=TubeDia[i],h=1.5*TubeDia[i] + BaseThick + Protrusion,$fn=NumSides); | |
} | |
} | |
for (i=[1:NumTubes-2]) // gap plugs | |
rotate(90 - TotalAngle/2 + sumv(Angle,i-1) + (Angle[i])/2) | |
translate([(TubeRad[0] + Wall),0,0]) | |
cylinder(r=2*Wall,h=1.5*min(TubeDia[i],TubeDia[i+1]) + BaseThick,$fn=3); | |
} | |
translate([0,0,-Protrusion]) // text | |
mirror([1,0,0]) | |
linear_extrude(height=3*ThreadThick + Protrusion) { | |
translate([0,25,0]) | |
text(text="Linda",size=7,spacing=1.05,font="Arial:style=Bold Italic",halign="center"); | |
translate([0,15,0]) | |
text(text="FDQ ExComm",size=5,spacing=1.05,font="Arial:style=Regular",halign="center"); | |
translate([0,7,0]) | |
text(text="2014 - 2016",size=5,spacing=1.05,font="Arial:style=Regular",halign="center"); | |
translate([0,-3,0]) | |
text(text="Thank you!",size=6,spacing=1.05,font="Arial:style=Regular",halign="center"); | |
translate([0,-15,0]) | |
text(text="Mary Nisley",size=7,spacing=1.10,font="ITC Zapf Chancery:style=Medium Italic",halign="center"); | |
} | |
} |
Why does Slic3r change the bridging direction right in the middle of letters? Here’s what S3D gives: https://dl.dropboxusercontent.com/u/3845046/bridging.png
Your use of [0, for …] requires a development snapshot to compile, btw.
It looks like S3D expects a single (or double?) thread width anchor to work perfectly every time, while Slic3r goes for a broad anchor that flat-out doesn’t work with a zillion close & tiny bridges. In this case, a few failures don’t matter, but I can see how either algorithm could get ugly.
Running perimeter threads around the infill seems overly fussy. Why not ram the infill up against the bridges and be done with it?
Unless there’s a killer problem, like Slic3r’s bridging mess earlier this year, I fetch whatever’s at the head of the
master
branch for both Slic3r and OpenSCAD whenever I start something new. In general, that works well enough and I get to follow the discussions about whatever the bulging brains come up with …