Sunday, January 19, 2014

Day 146 - Customizable Print-In-Place Fidget Cube

Today we finished our customizable print-in-place fidget cube model. By changing just a few parameters in OpenSCAD code, we can decide whether the fidget cube will have a snub configuration (as shown below), set the sizes of the blocks and the hinges, and fine-tune the clearance between moving parts. It still amazes me that this model can print in place, all in one piece, with functioning hinges - especially since during the print some of the hinges are horizontal while others are vertical!


STL file: http://www.geekhaus.com/makerhome/day146_fidgetcube_blue.stl
Thingiverse link: http://www.thingiverse.com/thing:230139

Settings: Replicator 2 with MakerWare .2mm/standard in about an hour and a half. In addition to looking cool, the snub sides reduce the print time by about 30 minutes and save some plastic. We used a custom profile to get linear fill instead of hexagons. No raft or support are needed! Customizer settings were as follows below (although I suggest setting hinge_clearance lower for future models).
snub = yes
cube_height = 20
stacking_clearance = .3
hinge_radius = 2.5
hinge_clearance = .5
Technical notes: I tried to keep my previous OpenSCAD projects (Days  96, 112, 113, and 133) well-written and commented so that people could learn from what I was learning. But I'm notoriously bad at rotating and reflecting objects mentally so this code is full of random transformations to put objects where I needed them to be. It isn't efficient and it isn't pretty, but here it is:

// mathgrrl print-in-place fidget cube

//////////////////////////////////////////////////////
// PARAMETERS ////////////////////////////////////////

// Decide whether you want the pieces to be full cubes or snub on one side
snub = "yes"; // [no:Cube Pieces,yes:Snub Pieces]

// Choose a side length for the cubes, in mm
cube_height = 20; 

// Choose a stacking clearance factor, in mm (controls distance between cubes)
stacking_clearance = .3;

// Choose a hinge radius, in mm
hinge_radius = 1.5; 

// Choose a hinge clearance factor, in mm (controls distance around hinge parts)
hinge_clearance = .45;

// Other variables that people don't get to choose
$fn=24*1;
fudge = .01*1;
corner_radius = .1*cube_height;
outside_height = .4*(cube_height-2*stacking_clearance);
inside_height = (cube_height-2*stacking_clearance)-2*outside_height;
cone_height = 1.4*hinge_radius; 

//////////////////////////////////////////////////////
// RENDERS ///////////////////////////////////////////

rotate(-45,[0,0,1])
translate([0,0,2*cube_height])
rotate(90,[0,1,0])
union(){
// one row of four cubes
row_assembly();
// another row of four cubes
translate([0,2*cube_height,2*cube_height])
rotate(-90,[1,0,0])
mirror([0,0,1])
mirror([1,0,0]) 
row_assembly();
}

//////////////////////////////////////////////////////
// MODULES ///////////////////////////////////////////

// one row of the assembly
module row_assembly(){
union(){
color("blue") 
hinged_cube(180,[1,0,0],[1,0,0]);

color("red")
translate([-cube_height/2,cube_height/2,0])
mirror([0,1,0])
translate([-cube_height/2,cube_height/2,0]) 
rotate(90,[1,0,0]) 
hinged_cube(0,[1,0,0],[0,0,0]);

color("green")
translate([cube_height,cube_height/2,cube_height/2])
rotate(-90,[0,1,0])
rotate(180,[1,0,0])
mirror([0,1,0])
translate([-cube_height/2,cube_height/2,0]) 
rotate(90,[1,0,0]) 
hinged_cube(180,[0,1,0],[0,1,0]);

color("yellow")
translate([-cube_height,cube_height,cube_height])
rotate(-90,[1,0,0])
rotate(90,[0,0,1])
rotate(-90,[1,0,0])
hinged_cube(0,[1,0,0],[0,0,0]);
}
}

// one cube to rule them all
module hinged_cube(the_angle,the_vector,the_mirror){
difference(){
// cube and cylinders to start
union(){
// cube with inside top and bottom cylinders carved out
difference(){
// start with clearance cube in corner
translate([cube_height/2,cube_height/2,cube_height/2])
rotate(the_angle,the_vector)
mirror(the_mirror)
rounded_cube([cube_height,cube_height,cube_height]); 
// take away inside top cylinder with clearance
translate([cube_height,0,0])
rotate(90,[0,0,1])
rotate(90,[0,1,0])
translate([0,0,stacking_clearance+outside_height+inside_height])
cylinder( h=outside_height+fudge, 
r1=hinge_radius+fudge+hinge_clearance, 
r2=hinge_radius+fudge+hinge_clearance);
// take away inside bottom cylinder with clearance
translate([cube_height,0,0])
rotate(90,[0,0,1])
rotate(90,[0,1,0])
translate([0,0,stacking_clearance-fudge])
cylinder( h=outside_height+fudge, 
r1=hinge_radius+fudge+hinge_clearance, 
r2=hinge_radius+fudge+hinge_clearance);
}
// top cylinder on outside hinge
translate([0,0,stacking_clearance+outside_height+inside_height+hinge_clearance])
cylinder( h=outside_height-hinge_clearance-corner_radius/2, 
r1=hinge_radius, 
r2=hinge_radius);
// bottom cylinder on outside hinge
translate([0,0,stacking_clearance+corner_radius/2])
cylinder( h=outside_height-hinge_clearance-corner_radius/2,
r1=hinge_radius,
r2=hinge_radius);
// inside hinge cylinder
translate([cube_height,0,0])
rotate(90,[0,0,1])
rotate(90,[0,1,0])
translate([0,0,stacking_clearance+outside_height])
cylinder( h=inside_height, 
r1=hinge_radius, 
r2=hinge_radius);
// attacher for inside hinge cylinder
translate([cube_height,0,0])
rotate(90,[0,0,1])
rotate(90,[0,1,0])
translate([0,0,stacking_clearance+outside_height])
rotate(45,[0,0,1])
translate([-.8*hinge_radius,0,0])
cube([1.6*hinge_radius,2*hinge_radius,inside_height]);
// inside hinge top cone 
translate([cube_height,0,0])
rotate(90,[0,0,1])
rotate(90,[0,1,0])
translate([0,0,stacking_clearance+outside_height+inside_height])
cylinder( h=cone_height, 
r1=hinge_radius, 
r2=0);
// inside hinge bottom cone 
translate([cube_height,0,0])
rotate(90,[0,0,1])
rotate(90,[0,1,0])
translate([0,0,stacking_clearance+outside_height-cone_height])
cylinder( h=cone_height, 
r1=0, 
r2=hinge_radius);
}
// take away middle cylinder with clearance
translate([0,0,stacking_clearance+outside_height-hinge_clearance-fudge])
cylinder( h=inside_height+2*hinge_clearance+2*fudge, 
r1=hinge_radius+fudge+hinge_clearance, 
r2=hinge_radius+fudge+hinge_clearance);
// take away top cone with clearance
translate([0,0,stacking_clearance+outside_height+inside_height+hinge_clearance-fudge])
cylinder(h=cone_height, r1=hinge_radius, r2=0);
// take away bottom cone with clearance
translate([0,0,stacking_clearance+outside_height-cone_height-hinge_clearance+fudge])
cylinder(h=cone_height, r1=0, r2=hinge_radius);
}
}

// module for making rounded cubes from convex hull of corners
module rounded_cube() {
dist = cube_height/2-corner_radius-stacking_clearance;
hull() {
// seven of the eight vertices of a cube
translate([dist,dist,dist])
sphere(r=corner_radius); 
translate([-dist,dist,dist]) 
sphere(r=corner_radius);
translate([dist,-dist,dist])
sphere(r=corner_radius);
translate([-dist,-dist,dist]) 
sphere(r=corner_radius);
translate([dist,dist,-dist])
sphere(r=corner_radius);
translate([dist,-dist,-dist]) 
sphere(r=corner_radius);
translate([-dist,-dist,-dist]) 
sphere(r=corner_radius);
// don't include the eighth if snub desired
if (snub=="yes"){
}
else{
translate([-dist,dist,-dist]) 
sphere(r=corner_radius);
}
}
}

UPDATE: Featured on 1/29/2014 as Thing-of-the-Day by AlexHuff at 3dideaform.com.

UPDATE: The print-in-place fidget cube made an appearance on the Adafruit blog with a comment from emmet himself (original creator of the printable snap-together fidget cube): 
Guest Curator Emmett Lalish has this to say about this 3DxMechanicals project: “This is why I love Thingiverse: I post a snap-assemble design, then someone figures out how to print it pre-assembled. It definitely pushes the boundaries of what’s printable and how the slicers work. This is how we push the technology forward.”
UPDATE: From this post by Kevin Osborn on the Ultimaker forum, I learned that you can pick up prints of my fidget cubes at the MakerBot retail store!

No comments:

Post a Comment