chainGuide.frink

Download or view chainGuide.frink in plain text format


/** This prints a chain guide for a lightswitch.  This is for 3-D printing.
    This model begins with two cylinders.  One that points straight up/down
    for the pullchain to fall down, and one at the top that guides the chain
    toward the light fixture.

    These two cylinders are joined by a tricky bit:  a curved tube that 
    smoothly rotates (and potentially tapers) to exactly join the bottom
    cylinder and the top cylinder.

    The shape of the tricky joining tube is found by a few insights:
    1.)  The joining tube should be an annulus (ring) rotated as a solid of
         rotation.  This will make a curved, hollow cylinder.
    
    2.)  The joining tube can be rotated around an essentially arbitrary point.
    
    3.)  The top end of the joining tube must match the angle of the top tube
         (that is, pointing to the light fixture)
    
    4.)  How do we find the axis to curve the solid of rotation around?  Well,
         if we have two vectors, and want to find a third vector that is
         perpendicular to them both, that's what the cross product is for!

    5.)  How do we find the angle to rotate the solid of rotation around?
         Well, that's what the dot product is for!  The arccos of the dot
         product gives an angle which is the angle to rotate the solid of
         rotation through.

    6.)  The cylinder functions like to be given the x,y,z coordinates of the
         centers of their endpoints.  We can generate those coordinates by
         taking the endpoints of a cylinder as if it's sitting "upward" on the
         top of the bottom cylinder and then transforming those coordinates
         using a frink.graphics.CoordinateTransformer3DFloat
*/


res = 254/inch

wallThickness = 3.21 mm

baseHeight = .8 cm
thinRadiusInner = 1.3/2 cm
thinRadiusOuter = thinRadiusInner + wallThickness

thickRadiusInner = 1.3/2 cm
thickRadiusOuter = thickRadiusInner + wallThickness

topHeight = .8 cm
topRadiusInner = 1.5/2 cm
topRadiusOuter = thickRadiusInner + wallThickness

// Bottom cylinder
baseOuter = callJava["frink.graphics.VoxelArray", "makeTaperedCylinder", [0,0,0,0,0, baseHeight res, thickRadiusOuter res, thinRadiusOuter res]]
baseInner = callJava["frink.graphics.VoxelArray", "makeTaperedCylinder", [0,0,0,0,0, baseHeight res, thickRadiusInner res, thinRadiusInner res]]
baseOuter.remove[baseInner]

// Make a thin ring that will be rotated and extruded as a solid of rotation.
ringOuter = callJava["frink.graphics.VoxelArray", "makeCylinder", [0,0,baseHeight res,0,0, (baseHeight+0.15 mm) res, thinRadiusOuter res]]
ringInner = callJava["frink.graphics.VoxelArray", "makeCylinder", [0,0,baseHeight res,0,0, (baseHeight + 0.15 mm) res, thinRadiusInner res]]
ringOuter.remove[ringInner]
                                                
// A vector pointing straight up (this is the alignment of the bottom cylinder)
vup = newJava["frink.graphics.Point3DFloat", [0,0,1]]

// A vector pointing toward the light (the alignment of the upper cylinder)
vlight = newJava["frink.graphics.Point3DFloat", [-4.25, 25.5, 17]].normalize[]

// Find an axis that is perpendicular to both vup and vlight to rotate around
// (that's precisely what the "cross product" operator does!)
vaxis = vup.crossProduct[vlight]

// Find the angle to rotate around to turn vup to vlight (that's precisely
// what the arccos of the "dot product" operator does!)
angle = arccos[vup.dotProduct[vlight]]

// Arbitrary center point to rotate around
center = newJava["frink.graphics.Point3DFloat", [-2 thinRadiusOuter res, 2 thinRadiusOuter res, (baseHeight) res]]
println["Angle is " + format[angle,"deg",3]]

// This generates the solid of rotation for the ring around the point
// "center" and the rotation axis "vaxis"        
bend = ringOuter.solidOfRotation[center, vaxis, 0 deg, angle]

// Make a coordinate transformer that will rotate points to calculate the ends
// of the top cylinder.  This makes a rotation transformation
// around "vaxis" and passing through point "center", rotated by "angle"
ct = callJava["frink.graphics.CoordinateTransformer3DFloat", "makeRotate", [center, vaxis, angle]]

// Transform centers of the ends of the top cylinder.  We do this by imagining
// placing the top cylinder on top of the bottom cylinder and then transforming
// the placement of its endpoints.       
rc1 = ct.transform[0, 0, baseHeight res, undef]
rc2 = ct.transform[0, 0, (baseHeight+topHeight) res, undef]

// Now render the top (tapered) cylinder.  It gets wider toward the top.
topOuter = callJava["frink.graphics.VoxelArray", "makeTaperedCylinder", [rc1, rc2, thinRadiusOuter res, topRadiusOuter res]]
topInner = callJava["frink.graphics.VoxelArray", "makeTaperedCylinder", [rc1, rc2, thinRadiusInner res, topRadiusInner res]]
topOuter.remove[topInner]

v = baseOuter.union[bend]
v = v.union[topOuter]

// Mounting flange
minX = (thickRadiusOuter-wallThickness) res
maxX = thickRadiusOuter res
minY = -thickRadiusOuter res
maxY = .95 in res
minZ = 0
maxZ = 1.43 in res

flange = v.cube[minX, maxX,
                minY, maxY,
                minZ, maxZ,
                true]

// Tapered countersunk screw holes for mounting
// Top hole      
c1 = callJava["frink.graphics.VoxelArray", "makeTaperedCylinder", [minX, minY+4.5 mm res, maxZ-4.5 mm res,maxX, minY+4.5 mm res, maxZ-4.5 mm res, 5.8 mm/2 res, 3.0 mm/2 res]]

// Bottom hole
c2 = callJava["frink.graphics.VoxelArray", "makeTaperedCylinder", [minX, maxY-4.5 mm res, minZ+4.5 mm res, maxX, maxY-4.5 mm res, minZ+4.5 mm res, 5.8 mm/2 res, 3.0 mm/2 res]]
flange.remove[c1]
flange.remove[c2]

v = v.union[flange]

v.projectX[undef].show["X"]
v.projectY[undef].show["Y"]
v.projectZ[undef].show["Z"]

filename = "chainGuide.obj"
print["Writing $filename..."]
w = new Writer[filename]
w.println[v.toObjFormat["v", 1/(res mm)]]
w.close[]
println["done."]


Download or view chainGuide.frink in plain text format


This is a program written in the programming language Frink.
For more information, view the Frink Documentation or see More Sample Frink Programs.

Alan Eliasen was born 19346 days, 23 hours, 11 minutes ago.