// -------------------------------------------------------------------- // A collection of useful modules to create Fischertechnik parts, // mainly grooves, holes and pins to be added or substracted // -------------------------------------------------------------------- // Please be aware that OpenSCAD approximates the size of a hole by // using a polygon according to the $fn variable. The corner points of // the polygon are lying on the circle. Thus the resulting hole is // always smaller than the diameter of the hole! Increasing the $fn // variable helps but increases calculation time. The best is to try // out the size and adopt the settings accordingly. It does not help // to size the final parts in the slicer, because all of the part will // be resized, not only the hole! // See https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Other_Language_Features#Circle_resolution:_$fa,_$fs,_and_$fn // for details. // Constants used throughout the fischertechnik files. Although all // values in OpenSCAD are constants technically spoken, these are some // kind of special. They define standard values used everywhere and // changing them might influence how a part is redered and exported // to the finsal stl file for printing. // The standard grid size of fischertechnik parts is 15 mm. Most // parts fit into that grid. FT_GRID_SIZE = 15; // The basic block has the same size as the grid. The constant name // is just for convinience BASIC_BLOCK_SIZE = FT_GRID_SIZE; // Half of the block size is used quite often, so it has its own // constant BASIC_BLOCK_HALF = BASIC_BLOCK_SIZE / 2; // WALL_THICKNESS = 2; // The Length of the groove is the same as the grid/basic block size. GROOVE_LENGTH = FT_GRID_SIZE; // For values from different people see: // https://forum.ftcommunity.de/viewtopic.php?f=38&t=3709 // The radius of a groove, makes a diameter of 4.5 mm // GROOVE_RADIUS = 2.25; // GROOVE_DIAMETER = GROOVE_RADIUS * 2; GROOVE_RADIUS = 2.1; GROOVE_DIAMETER = GROOVE_RADIUS * 2; GROOVE_CENTER = 2.4; GROOVE_BREAKTHROUGH = 3.0; FLAT_GROOVE_OFFSET = 0.6; PIN_DIAMETER = 4; PIN_BASE_WIDTH = 3; // PIN_BASE_LENGTH = 1.2; PIN_BASE_LENGTH = 1.0; PLATE_HOLE_DIAMETER = 4.2; PLATE_BREAKTHROUGH = 3.2; PLATE_HOLE_CORRECTION = 0.8; PLATE_CUTOFF_HEIGHT = 2.8; // those settings work for the U-girder, // but not for the base plate where // PLATE_CO_TOP_LENGTH is too long! PLATE_CO_TOP_WIDTH = 3.0; PLATE_CO_TOP_LENGTH = 6.0; PLATE_CO_BOTTOM_WIDTH = 4.3; PLATE_CO_BOTTOM_LENGTH = 4.3; PLATE_CO_CIRCLE_DIAMETER = 4.1; PLATE_CO_CIRCLE_OFFSET = 0.2; EYELET_HEIGHT = 2.5; MANIFOLD_CORRECTION = 0.1; $fn=104; // -------------------------------------------------------------------- // part for cutting out a clip axle hole // -------------------------------------------------------------------- module clip_axle_hole(length = BASIC_BLOCK_HALF, diameter = PLATE_HOLE_DIAMETER, thickness = 2.7, hor_cutout = 0) { cliplen_without_dome = 5; intersection() { cylinder(length + MANIFOLD_CORRECTION, d = diameter, center = true); cube([diameter, thickness, length + MANIFOLD_CORRECTION], center = true); } if (hor_cutout != 0) { // position the horizontal cutout in a way that it is 5mm from the top // so that the cone part of the axle can get some grip translate([0,0, -(length - diameter) / 2 + cliplen_without_dome]) intersection() { cylinder(diameter + MANIFOLD_CORRECTION, d = hor_cutout, center = true); cube([hor_cutout + MANIFOLD_CORRECTION, thickness, diameter], center = true); } } } // -------------------------------------------------------------------- // A round groove to be substracted from other solids // Optionally with a cube on each end of the groove // The groove is centered on x and the z axis while it is slightly // moved to a negative y value // For the cutout rotate, translate and difference the groove // -------------------------------------------------------------------- module round_groove(len = GROOVE_LENGTH, endcube=0) { // although the union is implicit, it doesn't do any harm and // handles the groove as an entity union() { // first, the groove itselve translate([0, GROOVE_CENTER, 0] ) cylinder(r = GROOVE_RADIUS, h = len + 2 * MANIFOLD_CORRECTION, center=true); // Do we have cubes at either end of the groove? // Required if we have horzontal and vertical grooves if (endcube == 1 || endcube == 3) translate([0, GROOVE_CENTER - 2 * MANIFOLD_CORRECTION, -(len - GROOVE_DIAMETER) / 2 - MANIFOLD_CORRECTION] ) cube([GROOVE_DIAMETER, GROOVE_DIAMETER + 4 * MANIFOLD_CORRECTION, GROOVE_DIAMETER], center=true); if (endcube == 2 || endcube == 3) translate([0, GROOVE_CENTER - 2 * MANIFOLD_CORRECTION, (len - GROOVE_DIAMETER) / 2 + MANIFOLD_CORRECTION] ) cube([GROOVE_DIAMETER, GROOVE_DIAMETER + 4 * MANIFOLD_CORRECTION, GROOVE_DIAMETER], center=true); // now the 3 mm breakthrough for the grooves translate([0, 0.5, 0] ) cube([GROOVE_BREAKTHROUGH, 1.5, len + 2 * MANIFOLD_CORRECTION], center = true); } } // -------------------------------------------------------------------- // a flat groove to be substracted from other solids // -------------------------------------------------------------------- module flat_groove(len = GROOVE_LENGTH, endcube=0) { difference() { // make a round groove and cut off the top part round_groove(len, endcube); translate([-GROOVE_RADIUS, GROOVE_CENTER + FLAT_GROOVE_OFFSET, -len / 2 - (MANIFOLD_CORRECTION + 0.01)] ) cube([GROOVE_DIAMETER, GROOVE_CENTER - FLAT_GROOVE_OFFSET, len + 2 * (MANIFOLD_CORRECTION + 0.01)]); } } // Apply a size correction to the eyelets which are printed on // the build plate of the 3D printer? // On my Ender 3 the eyelets on the build plate are too tall so // I needed to widen them. The eyelets on the vertical wall of the // girder are ok, they need no correction. function apply_correction(val, correctionVal = 0.0, correction=false) = correction == true ? val + correctionVal : val; // -------------------------------------------------------------------- // a single eyelet for the girders and struts // -------------------------------------------------------------------- module eyelet(height = EYELET_HEIGHT, radius=2.05, cube_len = 7.2, cube_width = 3.1, correctionVal = 0.0) { // Currently used only in the lugs.scad file // See strut_eyelet to decide whether that one can be refactored // or replaced by this library module. render() union() { // radius = apply_correction(radius, correctionVal, horizontal); // cube_width = apply_correction(7.2, correctionVal, horizontal); // cube_height = apply_correction(3.1, correctionVal, horizontal); // Hmm, horizontal=true raises the eylet above the z axis while // horizontal=false move the eyelet below the z axis. // Is this intended? I assume no, all usages force the eyelet // into horizontal= true. I would suggest that the eyelet cutout // is centered on all four axises and the the horizontal= true // forces the eyelet into x direction while false forces it into // y direction. // translate([0, 0, horizontal == false ? -(height/2) + MANIFOLD_CORRECTION : (height/2) - MANIFOLD_CORRECTION ]) // { cylinder(h = height, r = radius, center = true); // cube([cube_height, cube_width, height], center = true); cube([cube_width, cube_len, height], center = true); // } } } // -------------------------------------------------------------------- // a row of eyelets // -------------------------------------------------------------------- module eyelet_row(len = 1, height = EYELET_HEIGHT, eyelet_dist = FT_GRID_SIZE) { // removed correction cnt = len / eyelet_dist; for (i = [0 : eyelet_dist : (cnt - 1) * eyelet_dist]) { translate([eyelet_dist / 2, eyelet_dist / 2 + i, 0]) eyelet(height = height); } } // -------------------------------------------------------------------- // The cutout for plates, girders and the like // -------------------------------------------------------------------- module plate_cutout(w1 = PLATE_CO_TOP_WIDTH, l1 = PLATE_CO_TOP_LENGTH, w2 = PLATE_CO_BOTTOM_WIDTH, l2 = PLATE_CO_BOTTOM_LENGTH, r = PLATE_CO_CIRCLE_DIAMETER, o = PLATE_CO_CIRCLE_OFFSET) { union () { translate([0,-0.01,0]) { translate([-w1/2, 0, 0]) cube([w1, r, l1]); translate([-w2/2, 0, l1]) cube([w2, r, l2]); translate([0, r / 2 + o, 0]) cylinder(h = l1 + l2, d = r); } } } // -------------------------------------------------------------------- // A cube with attached cylinder to cut out holes for the pins of the // basic blocks. Centered on the zero point of the coordinate system. // Supports the four directions up, down, left and right. // The build plate must be face down in the coordinate system to cut // out the holes with this module. // if the holes are too tall, adjust width (the width and height of // the larger square hole) or the segment width s (the width of the // the smaller rectangle) // Used by u_girder and build_plate. // -------------------------------------------------------------------- module cube_with_cylinder(w1 = PLATE_CO_TOP_WIDTH, l1 = PLATE_CO_TOP_LENGTH, w2 = PLATE_CO_BOTTOM_WIDTH, l2 = PLATE_CO_BOTTOM_LENGTH, r = PLATE_CO_CIRCLE_DIAMETER, o = PLATE_CO_CIRCLE_OFFSET, dir = "up") { if (dir == "up") { translate([0,(l1+l2)/2,0]) rotate([90,0,0]) plate_cutout(w1, l1, w2, l2, r, o); } else if (dir == "down") { translate([0,-(l1+l2)/2,0]) rotate([90,0,180]) plate_cutout(w1, l1, w2, l2, r, o); } else if (dir == "left") { translate([(l1+l2)/2,0,0]) rotate([90,0,270]) plate_cutout(w1, l1, w2, l2, r, o); } else if (dir == "right") { translate([-(l1+l2)/2,0,0]) rotate([90,0,90]) plate_cutout(w1, l1, w2, l2, r, o); } } // -------------------------------------------------------------------- // Pin which fits into the round and flat grooves. // The base_length is for pins placed on blocks or plates // Parameters: // diameter: the diameter of the hole for the pin // base_width: the width and length of the pin base // base_length: the height of the pin base // The pin is centered at the x and y axis while the z origin is 0 // -------------------------------------------------------------------- module pin(diameter = PIN_DIAMETER, base_width = PIN_BASE_WIDTH, base_length = PIN_BASE_LENGTH) { // check the whole thing! rotate([90,0,0]) translate([0, (base_width + base_length) / 2, 0]) union() { difference() { // build the rounded part of the pin intersection() { cylinder(d = diameter, h = diameter, center = true); rotate([0, 90, 0]) cylinder(d = diameter, h = diameter, center = true); } // cut off the top translate([0, diameter / 2 + 0.6, 0]) cube([diameter, diameter, diameter], center = true ); } // add the base below the rounded part translate([0, -(base_width / 2), 0]) cube([base_width, base_length, base_width], center = true ); } }