diff --git a/gridfinity-rebuilt.scad b/gridfinity-rebuilt.scad index 59f76ae..c9b05aa 100644 --- a/gridfinity-rebuilt.scad +++ b/gridfinity-rebuilt.scad @@ -1,44 +1,84 @@ +// ===== Info ===== +// IMPORTANT: rendering will be better for analyzing the model if fast-csg is enabled. As of writing, this feature is only available in the development builds and not the official release of OpenSCAD, but it makes rendering only take a couple seconds, even for comically large bins. Enable it in Edit > Preferences > Features > fast-csg +// the plane that is the top of the internal bin solid is d_height+h_base above z=0 +// the magnet holes can have an extra cut in them to make it easier to print without supports +// tabs will automatically be disabled when gridz is less than 3, as the tabs take up too much space +// examples are at the end of the file +// =============== + + $fa = 8; $fs = 0.25; -// number of bases along x-axis -gridx = 2; +gridx = 3; // number of bases along x-axis +gridy = 3; // number of bases along y-axis +gridz = 6; // unit height along z-axis (2, 3, or 6, but can be any) +length = 42;// base unit (if you want to go rogue ig) -// number of bases along y-axis -gridy = 2; +enable_holes = true; // holes on the base for magnet / screw +enable_hole_slit = true; // extra cut within holes for better slicing -// unit height along z-axis (2, 3, or 6, but can be any) -gridz = 6; +color("tomato") +gridfinityEqual(n_divx = 2, n_divy = 2, style_tab = 1, enable_scoop = true); -// number of x compartments (ideally, coprime w/ gridx) -n_divx = 2; -// number of y compartments (ideally, coprime w/ gridy) -n_divy = 1; -// set n_div values to 0 for a solid bin (for custom bins) -// base unit (if you want to go rogue ig) -length = 42; -// type of tab. alignment only matters if tabs are large enough -// tab style. 0:full, 1:automatic, 2:left, 3:center, 4:right, 5:none -style_tab = 0; -// the rounded edge that allows for easy removal -enable_scoop = true; -// holes on the base for magnet / screw -enable_holes = true; -// extra cut within holes for better slicing -enable_hole_slit = true; +// ===== User Modules ===== + +// Creates an equally divided gridfinity bin. +// +// n_divx: number of x compartments (ideally, coprime w/ gridx) +// n_divy: number of y compartments (ideally, coprime w/ gridy) +// set n_div values to 0 for a solid bin +// style_tab: tab style for all compartments. see cut() +// enable_scoop: scoop toggle for all compartments. see cut() +module gridfinityEqual(n_divx=1, n_divy=1, style_tab=1, enable_scoop=true) { + gridfinityCustom() + for (i = [1:n_divx]) + for (j = [1:n_divy]) + cut((i-1)*gridx/n_divx,(j-1)*gridy/n_divy, gridx/n_divx, gridy/n_divy, style_tab, enable_scoop); +} + +// wrapper module +// DOES NOT CHECK FOR VALID COMPARTMENT STRUCTURE +module gridfinityCustom() { + difference() { + color("firebrick") block_bottom(d_height); + children(); + } + color("orange") block_base(); + color("royalblue") block_wall(); +} + +// Function to include in the custom() module to individually slice bins +// Will try to clamp values to fit inside the provided base size +// +// x: start coord. x=1 is the left side of the bin. +// y: start coord. y=1 is the bottom side of the bin. +// w: width of compartment, in # of bases covered +// h: height of compartment, in # of basese covered +// t: tab style of this specific compartment. +// alignment only matters if the compartment size is larger than d_tabw +// 0:full, 1:auto, 2:left, 3:center, 4:right, 5:none +// Automatic alignment will use left tabs for bins on the left edge, right tabs for bins on the right edge, and center tabs everywhere else. +// s: toggle the rounded back corner that allows for easy removal +module cut(x=0, y=0, w=1, h=1, t=1, s=true) { + cut_move(x,y,w,h) + block_cutter(clp(x,0,gridx), clp(y,0,gridy), clp(w,0,gridx-x), clp(h,0,gridy-y), t, s); +} + +// Translates an object from the origin point to the center of the requested compartment block, can be used to add custom cuts in the bin +// See cut() module for parameter descriptions +module cut_move(x, y, w, h) { + cut_move_unsafe(clp(x,0,gridx), clp(y,0,gridy), clp(w,0,gridx-x), clp(h,0,gridy-y)) + children(); +} -// ===== Info ===== -// rendering will be better for analyzing the model if fast-csg is enabled. This feature is only available in the nightly build and not the official release of OpenSCAD, but if makes rendering only take a couple seconds. Enable it in Edit > Preferences > Features > fast-csg -// the plane that is the top of the internal bin solid is d_height+h_base above z=0 -// the magnet holes have an extra cut in them to make it easier to print without supports -// tabs will automatically be disabled when gridz is less than 3, as the tabs take up too much space // ===== Dimensions ===== @@ -58,43 +98,26 @@ h_hole = 2.4; // magnet hole depth r_f1 = 0.6; // top edge fillet radius r_f2 = 2.8; // internal fillet radius -r_f3 = 0.6; // lip fillet radius d_div = 1.2; // width of divider between compartments d_wall = 0.95; // minimum wall thickness d_clear = 0.25; // tolerance fit factor -d_tabh = 15.85; // height of tab (yaxis, measured from inner wall) -d_tabw = length; // maximum width of tab +d_tabh = 15.85; // height of tab (yaxis, measured from inner wall) +d_tabw = length; // maximum width of tab a_tab = 36; d_height = (gridz-1)*7 + 2; -r_scoop = enable_scoop ? length*gridz/12 - r_f2 : 0; // scoop radius +r_scoop = length*gridz/12 - r_f2; // scoop radius d_wall2 = r_base-r_c1-d_clear*sqrt(2); -d_pitchx = (gridx*length-0.5-2*d_wall-(n_divx-1)*d_div)/n_divx; -d_pitchy = (gridy*length-0.5-2*d_wall-(n_divy-1)*d_div)/n_divy; + +xl = gridx*length-0.5-2*d_wall+d_div; +yl = gridy*length-0.5-2*d_wall+d_div; - -color("tomato") -gridfinity(); - // ===== Modules ===== -module gridfinity() { - difference() { - // solid bin - color("firebrick") block_bottom(d_height); - - // subtraction blocks - color("sienna") block_cutter(); - } - - color("orange") block_base(); - color("royalblue") block_wall(); -} - module profile_base() { polygon([ [0,0], @@ -188,93 +211,130 @@ module block_bottom( h = 2.2 ) { rounded_rectangle(gridx*length-0.5-d_wall/4, gridy*length-0.5-d_wall/4, d_height-0.1, r_base+0.01); } -module block_cutter() { - for (j = [1:n_divy]) - translate(-(j-1)*(d_pitchy + d_div)*[0,1,0]) - for (i = [1:n_divx]) - translate(((i-1)-(n_divx-1)/2)*(d_pitchx + d_div)*[1,0,0]) - translate([0,gridy*length/2-0.25-d_wall,h_base+h_bot]) - rotate([90,0,-90]) - cutter(i,j); +module cut_move_unsafe(x, y, w, h) { + translate([(x)*xl/gridx,(y)*yl/gridy,0]) + translate([(-xl+d_div)/2,(-yl+d_div)/2,0]) + translate([(w*xl/gridx-d_div)/2,(h*yl/gridy-d_div)/2,0]) + children(); } -module cutter(i,j) { +module block_cutter(x,y,w,h,t,s) { v_len_tab = d_tabh; v_len_lip = d_wall2-d_wall+1.2; v_cut_tab = d_tabh - (2*r_f1)/tan(a_tab); v_cut_lip = d_wall2-d_wall; v_ang_tab = a_tab; - v_ang_lip = 45; + v_ang_lip = 45; + + ylast = abs(y+h-gridy)<0.001; + xlast = abs(x+w-gridx)<0.001; + ylen = h*yl/gridy-d_div; + xlen = w*xl/gridx-d_div; - enable_tab = style_tab != 5; height = d_height; - extent = (enable_scoop && j==n_divy ? d_wall2-d_wall : 0); - tab = ((gridz < 3 || style_tab == 5) && j == 1) ? v_len_lip : v_len_tab; - ang = ((gridz < 3 || style_tab == 5) && j == 1) ? v_ang_lip : v_ang_tab; - cut = ((gridz < 3 || style_tab == 5) && j == 1) ? v_cut_lip : v_cut_tab; - style = (style_tab > 1 && style_tab < 5) ? style_tab-3 : (i == 1 ? -1 : i == n_divx ? 1 : 0); + extent = (s && y==0 ? d_wall2-d_wall : 0); + tab = (gridz < 3 || t == 5) ? (ylast?v_len_lip:0) : v_len_tab; + ang = (gridz < 3 || t == 5) ? (ylast?v_ang_lip:0) : v_ang_tab; + cut = (gridz < 3 || t == 5) ? (ylast?v_cut_lip:0) : v_cut_tab; + style = (t > 1 && t < 5) ? t-3 : (x == 0 ? -1 : xlast ? 1 : 0); - if (gridz >= 3 && d_pitchx - d_tabw > 4*r_f2) { - if (style_tab != 0 && style_tab != 5 && j == 1) + translate([0,ylen/2,h_base+h_bot]) + rotate([90,0,-90]) { + + if (gridz >= 3 && xlen - d_tabw > 4*r_f2 && t != 0) { fillet_cutter(3,"bisque") - transform_tab(style) - translate([d_wall2-d_wall,0]) - profile_cutter(height-h_bot, d_pitchy/2); - - - if (style_tab != 0 && style_tab != 5) - fillet_cutter(2,"indigo") - transform_tab(style) difference() { - intersection() { - profile_cutter(height-h_bot, d_pitchy-extent); - profile_cutter_tab(height-h_bot, v_len_tab, v_ang_tab); - } - if (j==1) profile_cutter_tab(height-h_bot, v_len_lip, 45); - } + transform_tab(style, xlen, ((x==0&&style==-1)||(xlast&&style==1))?v_cut_lip:0) + translate([ylast?d_wall2-d_wall:0,0]) + profile_cutter(height-h_bot, ylen/2, s); + + if (x==0) + translate([0,0,(xlen/2-r_f2)-v_cut_lip]) + cube([ylen,height,v_cut_lip*2]); + + if (xlast) + translate([0,0,-(xlen/2-r_f2)-v_cut_lip]) + cube([ylen,height,v_cut_lip*2]); + } + if (t != 0 && t != 5) + fillet_cutter(2,"indigo") + difference() { + transform_tab(style, xlen, ((x==0&&style==-1)||(xlast&&style==1))?v_cut_lip:0) + difference() { + intersection() { + profile_cutter(height-h_bot, ylen-extent, s); + profile_cutter_tab(height-h_bot, v_len_tab, v_ang_tab); + } + if (ylast) profile_cutter_tab(height-h_bot, v_len_lip, 45); + } + + if (x == 0) + translate([ylen/2,0,xlen/2]) + rotate([0,90,0]) + transform_main(2*ylen) + profile_cutter_tab(height-h_bot, v_len_lip, v_ang_lip); + + if (xlast) + translate([ylen/2,0,-xlen/2]) + rotate([0,-90,0]) + transform_main(2*ylen) + profile_cutter_tab(height-h_bot, v_len_lip, v_ang_lip); + } } - if (!(style_tab == 5 && j != 1)) fillet_cutter(1,"seagreen") - transform_main() + translate([0,0,xlast?v_cut_lip/2:0]) + translate([0,0,x==0?-v_cut_lip/2:0]) + transform_main(xlen-(x==0?v_cut_lip:0)-(xlast?v_cut_lip:0)) translate([cut,0]) - profile_cutter(height-h_bot,d_pitchy/2); + profile_cutter(height-h_bot, ylen-extent-cut-(!s&&y==0?v_cut_lip:0), s); fillet_cutter(0,"hotpink") - transform_main() difference() { - profile_cutter(height-h_bot, d_pitchy-extent); + transform_main(xlen) + difference() { + profile_cutter(height-h_bot, ylen-extent, s); + + if (!((gridz < 3 || t == 5) && !ylast)) + profile_cutter_tab(height-h_bot, tab, ang); + + if (!s && y == 0) + translate([ylen-extent,0,0]) + mirror([1,0,0]) + profile_cutter_tab(height-h_bot, v_len_lip, v_ang_lip); + } - if (!((gridz < 3 || style_tab == 5) && j != 1)) - profile_cutter_tab(height-h_bot, tab, ang); + if (x == 0) + color("indigo") + translate([ylen/2,0,xlen/2]) + rotate([0,90,0]) + transform_main(2*ylen) + profile_cutter_tab(height-h_bot, v_len_lip, v_ang_lip); - if (!enable_scoop && j == n_divy) - translate([d_pitchy-extent,0,0]) - mirror([1,0,0]) + if (xlast) + color("indigo") + translate([ylen/2,0,-xlen/2]) + rotate([0,-90,0]) + transform_main(2*ylen) profile_cutter_tab(height-h_bot, v_len_lip, v_ang_lip); } - - if (!enable_scoop && j == n_divy) { - fillet_cutter(5,"darkslategray") - translate([d_pitchy-(d_wall2-d_wall+2*r_f2)-v_cut_lip,0,0]) - transform_main() - profile_cutter(height-h_bot,d_wall2-d_wall+2*r_f2); + } } -module transform_main() { - translate([0,0,-(d_pitchx-2*r_f2)/2]) - linear_extrude(d_pitchx-2*r_f2) +module transform_main(xlen) { + translate([0,0,-(xlen-2*r_f2)/2]) + linear_extrude(xlen-2*r_f2) children(); } -module transform_tab(type) { +module transform_tab(type, xlen, cut) { mirror([0,0,type==1?1:0]) copy_mirror([0,0,-(abs(type)-1)]) - translate([0,0,-d_pitchx/2]) + translate([0,0,-(xlen)/2]) translate([0,0,r_f2]) - linear_extrude((d_pitchx-length)/(1-(abs(type)-1))-2*r_f2) + linear_extrude((xlen-d_tabw-abs(cut))/(1-(abs(type)-1))-2*r_f2) children(); } @@ -286,12 +346,13 @@ module fillet_cutter(t = 0, c = "goldenrod") { } } -module profile_cutter(h, length) { +module profile_cutter(h, length, s) { + scoop = s ? r_scoop : 0; translate([r_f2,r_f2]) hull() { - if (length-r_scoop-2*r_f2 > 0) + if (length-scoop-2*r_f2 > 0) square(0.1); - if (r_scoop < h) { + if (scoop < h) { translate([length-2*r_f2,h-r_f2/2]) mirror([1,1]) square(0.1); @@ -301,18 +362,18 @@ module profile_cutter(h, length) { square(0.1); } difference() { - translate([length-r_scoop-2*r_f2, r_scoop]) - if (r_scoop != 0) { + translate([length-scoop-2*r_f2, scoop]) + if (scoop != 0) { intersection() { - circle(r_scoop); - mirror([0,1]) square(2*r_scoop); + circle(scoop); + mirror([0,1]) square(2*scoop); } } else mirror([1,0]) square(0.1); - translate([length-r_scoop-2*r_f2,-1]) - square([-(length-r_scoop-2*r_f2),2*h]); + translate([length-scoop-2*r_f2,-1]) + square([-(length-scoop-2*r_f2),2*h]); translate([0,h]) - square([2*length,r_scoop]); + square([2*length,scoop]); } } } @@ -327,6 +388,8 @@ module profile_cutter_tab(h, tab, ang) { // ==== Utilities ===== +function clp(x,a,b) = min(max(x,a),b); + module rounded_rectangle(length, width, height, rad) { linear_extrude(height) offset(rad) @@ -379,3 +442,63 @@ module sweep_rounded(w=10, h=10) { } } + +// ===== Examples ===== + +// All examples assume gridx == 3, gridy == 3, and gridz == 6, but some may work with other settings + +// 3x3 even spaced grid +//gridfinityEqual(3, 3, 0, true); + +// Compartments can be placed anywhere (this includes non-integer positions like 1/2 or 1/3). The grid is defined as (0,0) being the bottom left corner of the bin, with each unit being 1 base long. Each cut() module is a compartment, with the first four values defining the area that should be made into a compartment (X coord, Y coord, width, and height). These values should all be positive. t is the tab style of the compartment (0:full, 1:auto, 2:left, 3:center, 4:right, 5:none). s is a toggle for the bottom scoop. +/* +gridfinityCustom() { + cut(x=0, y=0, w=1.5, h=0.5, t=5, s=false); + cut(0, 0.5, 1.5, 0.5, 5, false); + cut(0, 1, 1.5, 0.5, 5, false); + + cut(0,1.5,0.5,1.5,5,false); + cut(0.5,1.5,0.5,1.5,5,false); + cut(1,1.5,0.5,1.5,5,false); + + cut(1.5, 0, 1.5, 5/3, 2); + cut(1.5, 5/3, 1.5, 4/3, 4); +}*/ + +// Compartments can overlap! This allows for weirdly shaped compartments, such as this "2" bin. +/* +gridfinityCustom() { + cut(0,2,2,1,5,false); + cut(1,0,1,3,5); + cut(1,0,2,1,5); + cut(0,0,1,2); + cut(2,1,1,2); +}*/ + +// Areas without a compartment are solid material, where you can put your own cutout shapes. using the cut_move() function, you can select an area, and any child shapes will be moved from the origin to the center of that area, and subtracted from the block. For example, a pattern of three cylinderical holes. +/* +gridfinityCustom() { + cut(x=0, y=0, w=2, h=3); + cut(x=0, y=0, w=3, h=1, t=5); + cut_move(x=2, y=1, w=1, h=2) + pattern_linear(x=1, y=3, spacing=length/2) + cylinder(r=5, h=10*d_height, center=true); +}*/ + +// You can use loops as well as the bin dimensions to make different parametric functions, such as this one, which divides the box into columns, with a small 1x1 top compartment and a long vertical compartment below + +/*gridfinityCustom() { + for(i=[0:gridx-1]) { + cut(i,0,1,gridx-1); + cut(i,gridx-1,1,1); + } +}*/ + +// Pyramid scheme bin +/* +gridfinityCustom() { + for (i = [0:gridx-1]) + for (j = [0:i]) + cut(j*gridx/(i+1),gridy-i-1,gridx/(i+1),1,0); +}*/ + diff --git a/images/custom_dimension.gif b/images/custom_dimension.gif new file mode 100644 index 0000000..f6a2331 Binary files /dev/null and b/images/custom_dimension.gif differ