MakerHome

One 3D print every day for a year

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: 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 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!