Added different height specifications
Height can now be defined by the unit height, internal height, or external height. Added lip toggle. Added option to snap bin to unit height. Fixed small protrusion at the bottom of the outside wall edge that made the STL look less clean. Split file into base file and command file, to allow for ease of use and possible future module expansion.
This commit is contained in:
parent
755adde9c9
commit
84d0625008
2 changed files with 452 additions and 405 deletions
409
gridfinity-rebuilt-base.scad
Normal file
409
gridfinity-rebuilt-base.scad
Normal file
|
@ -0,0 +1,409 @@
|
||||||
|
// UTILITY FILE, DO NOT EDIT
|
||||||
|
|
||||||
|
// ===== Extra Math ===== //
|
||||||
|
|
||||||
|
gzd = gridz_define;
|
||||||
|
dht = (gzd==0)?gridz*7 : (gzd==1)?h_bot+gridz+h_base : gridz-(enable_lip?3.8:0);
|
||||||
|
assert(dht > 0, "Height is too small!");
|
||||||
|
dht2 = enable_zsnap?((abs(dht)%7==0)?dht:dht+7-abs(dht)%7):dht;
|
||||||
|
d_height = dht2-h_base;
|
||||||
|
r_scoop = length*((d_height-2)/7+1)/12 - r_f2; // scoop radius
|
||||||
|
d_wall2 = r_base-r_c1-d_clear*sqrt(2);
|
||||||
|
|
||||||
|
xl = gridx*length-0.5-2*d_wall+d_div;
|
||||||
|
yl = gridy*length-0.5-2*d_wall+d_div;
|
||||||
|
|
||||||
|
echo("=====");
|
||||||
|
echo(height_total=d_height+h_base+(enable_lip?3.8:0));
|
||||||
|
echo(effective_units=(d_height+h_base)/7);
|
||||||
|
echo("=====");
|
||||||
|
// ===== 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ===== Modules ===== //
|
||||||
|
|
||||||
|
module profile_base() {
|
||||||
|
polygon([
|
||||||
|
[0,0],
|
||||||
|
[0,h_base],
|
||||||
|
[r_base,h_base],
|
||||||
|
[r_base-r_c2,h_base-r_c2],
|
||||||
|
[r_base-r_c2,r_c1],
|
||||||
|
[r_base-r_c2-r_c1,0]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
module block_base() {
|
||||||
|
translate([0,0,h_base])
|
||||||
|
rounded_rectangle(gridx*length-0.5+0.002, gridy*length-0.5+0.002, h_bot/1.5, r_fo1/2+0.001);
|
||||||
|
pattern_linear(gridx, gridy, length)
|
||||||
|
render()
|
||||||
|
difference() {
|
||||||
|
translate([0,0,h_base])
|
||||||
|
mirror([0,0,1])
|
||||||
|
union() {
|
||||||
|
hull() {
|
||||||
|
rounded_square(length-0.5-2*r_c2-2*r_c1, h_base, r_fo3/2);
|
||||||
|
rounded_square(length-0.5-2*r_c2, h_base-r_c1, r_fo2/2);
|
||||||
|
}
|
||||||
|
hull() {
|
||||||
|
rounded_square(length-0.5-2*r_c2, r_c2, r_fo2/2);
|
||||||
|
mirror([0,0,1])
|
||||||
|
rounded_square(length-0.5, h_bot/2, r_fo1/2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enable_holes)
|
||||||
|
pattern_circular(4)
|
||||||
|
translate([d_hole/2, d_hole/2, 0]) {
|
||||||
|
union() {
|
||||||
|
difference() {
|
||||||
|
cylinder(h = 2*(h_hole+(enable_hole_slit?0.2:0)), r = r_hole2, center=true);
|
||||||
|
if (enable_hole_slit)
|
||||||
|
copy_mirror([0,1,0])
|
||||||
|
translate([-1.5*r_hole2,r_hole1+0.1,h_hole])
|
||||||
|
cube([r_hole2*3,r_hole2*3, 0.4]);
|
||||||
|
}
|
||||||
|
cylinder(h = 3*h_base, r = r_hole1, center=true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module profile_wall_sub() {
|
||||||
|
difference() {
|
||||||
|
polygon([
|
||||||
|
[0,0],
|
||||||
|
[d_wall/2,0],
|
||||||
|
[d_wall/2,d_height-1.2-d_wall2+d_wall/2],
|
||||||
|
[d_wall2,d_height-1.2],
|
||||||
|
[d_wall2,d_height+h_base],
|
||||||
|
[0,d_height+h_base]
|
||||||
|
]);
|
||||||
|
color("red")
|
||||||
|
offset(delta = 0.25)
|
||||||
|
translate([r_base,d_height,0])
|
||||||
|
mirror([1,0,0])
|
||||||
|
profile_base();
|
||||||
|
square([d_wall,0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module profile_wall() {
|
||||||
|
translate([r_base,0,0])
|
||||||
|
mirror([1,0,0])
|
||||||
|
difference() {
|
||||||
|
profile_wall_sub();
|
||||||
|
difference() {
|
||||||
|
translate([0, d_height+h_base-d_clear*sqrt(2), 0])
|
||||||
|
circle(r_base/2);
|
||||||
|
offset(r = r_f1)
|
||||||
|
offset(delta = -r_f1)
|
||||||
|
profile_wall_sub();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// lipless profile
|
||||||
|
module profile_wall2() {
|
||||||
|
translate([r_base,0,0])
|
||||||
|
mirror([1,0,0])
|
||||||
|
square([d_wall,d_height]);
|
||||||
|
}
|
||||||
|
|
||||||
|
module block_wall() {
|
||||||
|
translate([0,0,h_base])
|
||||||
|
sweep_rounded(gridx*length-2*r_base-0.5-0.001, gridy*length-2*r_base-0.5-0.001)
|
||||||
|
if (enable_lip) profile_wall();
|
||||||
|
else profile_wall2();
|
||||||
|
}
|
||||||
|
|
||||||
|
module block_bottom( h = 2.2 ) {
|
||||||
|
translate([0,0,h_base+0.1])
|
||||||
|
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 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 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;
|
||||||
|
|
||||||
|
ycutfirst = y == 0 && enable_lip;
|
||||||
|
ycutlast = abs(y+h-gridy)<0.001 && enable_lip;
|
||||||
|
xcutfirst = x == 0 && enable_lip;
|
||||||
|
xcutlast = abs(x+w-gridx)<0.001 && enable_lip;
|
||||||
|
zsmall = (d_height+h_base)/7 < 3;
|
||||||
|
|
||||||
|
ylen = h*yl/gridy-d_div;
|
||||||
|
xlen = w*xl/gridx-d_div;
|
||||||
|
|
||||||
|
height = d_height;
|
||||||
|
extent = (s && ycutfirst ? d_wall2-d_wall : 0);
|
||||||
|
tab = (zsmall || t == 5) ? (ycutlast?v_len_lip:0) : v_len_tab;
|
||||||
|
ang = (zsmall || t == 5) ? (ycutlast?v_ang_lip:0) : v_ang_tab;
|
||||||
|
cut = (zsmall || t == 5) ? (ycutlast?v_cut_lip:0) : v_cut_tab;
|
||||||
|
style = (t > 1 && t < 5) ? t-3 : (x == 0 ? -1 : xcutlast ? 1 : 0);
|
||||||
|
|
||||||
|
translate([0,ylen/2,h_base+h_bot])
|
||||||
|
rotate([90,0,-90]) {
|
||||||
|
|
||||||
|
if (!zsmall && xlen - d_tabw > 4*r_f2 && t != 0) {
|
||||||
|
fillet_cutter(3,"bisque")
|
||||||
|
difference() {
|
||||||
|
transform_tab(style, xlen, ((xcutfirst&&style==-1)||(xcutlast&&style==1))?v_cut_lip:0)
|
||||||
|
translate([ycutlast?d_wall2-d_wall:0,0])
|
||||||
|
profile_cutter(height-h_bot, ylen/2, s);
|
||||||
|
|
||||||
|
if (xcutfirst)
|
||||||
|
translate([0,0,(xlen/2-r_f2)-v_cut_lip])
|
||||||
|
cube([ylen,height,v_cut_lip*2]);
|
||||||
|
|
||||||
|
if (xcutlast)
|
||||||
|
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, ((xcutfirst&&style==-1)||(xcutlast&&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 (ycutlast) profile_cutter_tab(height-h_bot, v_len_lip, 45);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xcutfirst)
|
||||||
|
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 (xcutlast)
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fillet_cutter(1,"seagreen")
|
||||||
|
translate([0,0,xcutlast?v_cut_lip/2:0])
|
||||||
|
translate([0,0,xcutfirst?-v_cut_lip/2:0])
|
||||||
|
transform_main(xlen-(xcutfirst?v_cut_lip:0)-(xcutlast?v_cut_lip:0))
|
||||||
|
translate([cut,0])
|
||||||
|
profile_cutter(height-h_bot, ylen-extent-cut-(!s&&ycutfirst?v_cut_lip:0), s);
|
||||||
|
|
||||||
|
fillet_cutter(0,"hotpink")
|
||||||
|
difference() {
|
||||||
|
transform_main(xlen)
|
||||||
|
difference() {
|
||||||
|
profile_cutter(height-h_bot, ylen-extent, s);
|
||||||
|
|
||||||
|
if (!((zsmall || t == 5) && !ycutlast))
|
||||||
|
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 (xcutfirst)
|
||||||
|
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 (xcutlast)
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module transform_main(xlen) {
|
||||||
|
translate([0,0,-(xlen-2*r_f2)/2])
|
||||||
|
linear_extrude(xlen-2*r_f2)
|
||||||
|
children();
|
||||||
|
}
|
||||||
|
|
||||||
|
module transform_tab(type, xlen, cut) {
|
||||||
|
mirror([0,0,type==1?1:0])
|
||||||
|
copy_mirror([0,0,-(abs(type)-1)])
|
||||||
|
translate([0,0,-(xlen)/2])
|
||||||
|
translate([0,0,r_f2])
|
||||||
|
linear_extrude((xlen-d_tabw-abs(cut))/(1-(abs(type)-1))-2*r_f2)
|
||||||
|
children();
|
||||||
|
}
|
||||||
|
|
||||||
|
module fillet_cutter(t = 0, c = "goldenrod") {
|
||||||
|
color(c)
|
||||||
|
minkowski() {
|
||||||
|
children();
|
||||||
|
sphere(r = r_f2-t/1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module profile_cutter(h, length, s) {
|
||||||
|
scoop = s ? r_scoop : 0;
|
||||||
|
translate([r_f2,r_f2])
|
||||||
|
hull() {
|
||||||
|
if (length-scoop-2*r_f2 > 0)
|
||||||
|
square(0.1);
|
||||||
|
if (scoop < h) {
|
||||||
|
translate([length-2*r_f2,h-r_f2/2])
|
||||||
|
mirror([1,1])
|
||||||
|
square(0.1);
|
||||||
|
|
||||||
|
translate([0,h-r_f2/2])
|
||||||
|
mirror([0,1])
|
||||||
|
square(0.1);
|
||||||
|
}
|
||||||
|
difference() {
|
||||||
|
translate([length-scoop-2*r_f2, scoop])
|
||||||
|
if (scoop != 0) {
|
||||||
|
intersection() {
|
||||||
|
circle(scoop);
|
||||||
|
mirror([0,1]) square(2*scoop);
|
||||||
|
}
|
||||||
|
} else mirror([1,0]) square(0.1);
|
||||||
|
translate([length-scoop-2*r_f2,-1])
|
||||||
|
square([-(length-scoop-2*r_f2),2*h]);
|
||||||
|
|
||||||
|
translate([0,h])
|
||||||
|
square([2*length,scoop]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module profile_cutter_tab(h, tab, ang) {
|
||||||
|
if (tab > 0)
|
||||||
|
color("blue")
|
||||||
|
offset(delta = r_f2)
|
||||||
|
polygon([[0,h],[tab,h],[0,h-tab*tan(ang)]]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==== Utilities =====
|
||||||
|
|
||||||
|
function clp(x,a,b) = min(max(x,a),b);
|
||||||
|
|
||||||
|
module rounded_rectangle(length, width, height, rad) {
|
||||||
|
linear_extrude(height)
|
||||||
|
offset(rad)
|
||||||
|
offset(-rad)
|
||||||
|
square([length,width], center = true);
|
||||||
|
}
|
||||||
|
|
||||||
|
module rounded_square(length, height, rad) {
|
||||||
|
rounded_rectangle(length, length, height, rad);
|
||||||
|
}
|
||||||
|
|
||||||
|
module copy_mirror(vec=[0,1,0]) {
|
||||||
|
children();
|
||||||
|
if (vec != [0,0,0])
|
||||||
|
mirror(vec)
|
||||||
|
children();
|
||||||
|
}
|
||||||
|
|
||||||
|
module pattern_linear(x = 1, y = 1, spacing = 0) {
|
||||||
|
translate([-(x-1)*spacing/2,-(y-1)*spacing/2,0])
|
||||||
|
for (i = [1:x])
|
||||||
|
for (j = [1:y])
|
||||||
|
translate([(i-1)*spacing,(j-1)*spacing,0])
|
||||||
|
children();
|
||||||
|
}
|
||||||
|
|
||||||
|
module pattern_circular(n=2) {
|
||||||
|
for (i = [1:n])
|
||||||
|
rotate(i*360/n)
|
||||||
|
children();
|
||||||
|
}
|
||||||
|
|
||||||
|
module sweep_rounded(w=10, h=10) {
|
||||||
|
union() pattern_circular(2) {
|
||||||
|
copy_mirror([1,0,0])
|
||||||
|
translate([w/2,h/2,0])
|
||||||
|
rotate_extrude(angle = 90, convexity = 4)
|
||||||
|
children();
|
||||||
|
|
||||||
|
translate([w/2,0,0])
|
||||||
|
rotate([90,0,0])
|
||||||
|
linear_extrude(height = h, center = true)
|
||||||
|
children();
|
||||||
|
|
||||||
|
rotate([0,0,90])
|
||||||
|
translate([h/2,0,0])
|
||||||
|
rotate([90,0,0])
|
||||||
|
linear_extrude(height = w, center = true)
|
||||||
|
children();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,93 +1,61 @@
|
||||||
// ===== Info =====
|
// ===== 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
|
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 magnet holes can have an extra cut in them to make it easier to print without supports
|
the plane that is the top of the internal bin solid is d_height+h_base above z=0
|
||||||
// tabs will automatically be disabled when gridz is less than 3, as the tabs take up too much space
|
the magnet holes can have an extra cut in them to make it easier to print without supports
|
||||||
// examples are at the end of the file
|
tabs will automatically be disabled when gridz is less than 3, as the tabs take up too much space
|
||||||
// ===============
|
base functions can be found in "gridfinity-rebuilt-base.scad"
|
||||||
|
examples at end of file
|
||||||
|
|
||||||
|
BIN HEIGHT
|
||||||
|
the original gridfinity bins had the overall height defined by 7mm increments
|
||||||
|
a bin would be 7*u millimeters tall
|
||||||
|
the lip at the top of the bin (3.8mm) added onto this height
|
||||||
|
The stock bins have unit heights of 2, 3, and 6:
|
||||||
|
Z unit 2 -> 7*2 + 3.8 -> 17.8mm
|
||||||
|
Z unit 3 -> 7*3 + 3.8 -> 24.8mm
|
||||||
|
Z unit 6 -> 7*6 + 3.8 -> 45.8mm
|
||||||
|
|
||||||
|
https://github.com/kennetek/gridfinity-rebuilt-openscad
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
$fa = 8;
|
$fa = 8;
|
||||||
$fs = 0.25;
|
$fs = 0.25;
|
||||||
|
|
||||||
gridx = 3; // number of bases along x-axis
|
// ===== General Settings ===== //
|
||||||
gridy = 3; // number of bases along y-axis
|
|
||||||
gridz = 6; // unit height along z-axis (2, 3, or 6, but can be any)
|
gridx = 1; // number of bases along x-axis
|
||||||
|
gridy = 1; // number of bases along y-axis
|
||||||
|
gridz = 6; // bin height. See bin height information and "gridz_define" below.
|
||||||
length = 42;// base unit (if you want to go rogue ig)
|
length = 42;// base unit (if you want to go rogue ig)
|
||||||
|
|
||||||
enable_holes = true; // holes on the base for magnet / screw
|
enable_holes = true; // toggle holes on the base for magnet/screw
|
||||||
enable_hole_slit = true; // extra cut within holes for better slicing
|
enable_hole_slit = true; // extra cut within holes for better slicing
|
||||||
|
enable_zsnap = false; // round up the bin height to match the closest 7mm unit
|
||||||
|
enable_lip = true; // toggle the lip on the top of the bin that allows stacking
|
||||||
|
|
||||||
|
// determine what the variable "gridz" applies to based on your use case
|
||||||
|
// 0: gridz is the height of bins in units (7mm increments)
|
||||||
|
// 1: gridz is the internal height in millimeters, or how tall an item inside the bin can be
|
||||||
|
// 2: gridz is the overall external height of the bin in millimeters
|
||||||
|
gridz_define = 0;
|
||||||
|
|
||||||
|
// ===== Commands ===== //
|
||||||
|
|
||||||
color("tomato")
|
color("tomato")
|
||||||
gridfinityEqual(n_divx = 2, n_divy = 2, style_tab = 1, enable_scoop = true);
|
gridfinityEqual(n_divx = 2, n_divy = 1, style_tab = 1, enable_scoop = true);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// ===== Reference Dimensions ===== //
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ===== 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();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ===== Dimensions =====
|
|
||||||
|
|
||||||
h_base = 5; // height of the base
|
h_base = 5; // height of the base
|
||||||
r_base = 4; // outside rounded radius of bin
|
r_base = 4; // outside rounded radius of bin
|
||||||
r_c1 = 0.8; // lower base chamfer "radius"
|
r_c1 = 0.8; // lower base chamfer "radius"
|
||||||
r_c2 = 2.4; // upper base chamfer "radius"
|
r_c2 = 2.4; // upper base chamfer "radius"
|
||||||
h_bot = 2.2; // bottom thiccness of bin
|
h_bot = 2.2; // bottom thiccness of bin
|
||||||
r_fo1 = 7.5; // outside radii
|
r_fo1 = 7.5+1; // outside radii
|
||||||
r_fo2 = 3.2;
|
r_fo2 = 3.2;
|
||||||
r_fo3 = 1.6;
|
r_fo3 = 1.6;
|
||||||
|
|
||||||
|
@ -107,348 +75,18 @@ d_tabh = 15.85; // height of tab (yaxis, measured from inner wall)
|
||||||
d_tabw = length; // maximum width of tab
|
d_tabw = length; // maximum width of tab
|
||||||
a_tab = 36;
|
a_tab = 36;
|
||||||
|
|
||||||
d_height = (gridz-1)*7 + 2;
|
// ===== Include ===== //
|
||||||
r_scoop = length*gridz/12 - r_f2; // scoop radius
|
|
||||||
d_wall2 = r_base-r_c1-d_clear*sqrt(2);
|
|
||||||
|
|
||||||
xl = gridx*length-0.5-2*d_wall+d_div;
|
include <gridfinity-rebuilt-base.scad>
|
||||||
yl = gridy*length-0.5-2*d_wall+d_div;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ===== Modules =====
|
|
||||||
|
|
||||||
module profile_base() {
|
|
||||||
polygon([
|
|
||||||
[0,0],
|
|
||||||
[0,h_base],
|
|
||||||
[r_base,h_base],
|
|
||||||
[r_base-r_c2,h_base-r_c2],
|
|
||||||
[r_base-r_c2,r_c1],
|
|
||||||
[r_base-r_c2-r_c1,0]
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
module block_base() {
|
|
||||||
translate([0,0,h_base])
|
|
||||||
rounded_rectangle(gridx*length-0.5+0.002, gridy*length-0.5+0.002, h_bot/1.5, r_fo1/2+0.001);
|
|
||||||
pattern_linear(gridx, gridy, length)
|
|
||||||
render()
|
|
||||||
difference() {
|
|
||||||
translate([0,0,h_base])
|
|
||||||
mirror([0,0,1])
|
|
||||||
union() {
|
|
||||||
hull() {
|
|
||||||
rounded_square(length-0.5-2*r_c2-2*r_c1, h_base, r_fo3/2);
|
|
||||||
rounded_square(length-0.5-2*r_c2, h_base-r_c1, r_fo2/2);
|
|
||||||
}
|
|
||||||
hull() {
|
|
||||||
rounded_square(length-0.5-2*r_c2, r_c2, r_fo2/2);
|
|
||||||
mirror([0,0,1])
|
|
||||||
rounded_square(length-0.5, h_bot/2, r_fo1/2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (enable_holes)
|
|
||||||
pattern_circular(4)
|
|
||||||
translate([d_hole/2, d_hole/2, 0]) {
|
|
||||||
union() {
|
|
||||||
difference() {
|
|
||||||
cylinder(h = 2*(h_hole+(enable_hole_slit?0.2:0)), r = r_hole2, center=true);
|
|
||||||
if (enable_hole_slit)
|
|
||||||
copy_mirror([0,1,0])
|
|
||||||
translate([-1.5*r_hole2,r_hole1+0.1,h_hole])
|
|
||||||
cube([r_hole2*3,r_hole2*3, 0.4]);
|
|
||||||
}
|
|
||||||
cylinder(h = 3*h_base, r = r_hole1, center=true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module profile_wall_sub() {
|
|
||||||
difference() {
|
|
||||||
polygon([
|
|
||||||
[0,0],
|
|
||||||
[d_wall/2,0],
|
|
||||||
[d_wall/2,d_height-1.2-d_wall2+d_wall/2],
|
|
||||||
[d_wall2,d_height-1.2],
|
|
||||||
[d_wall2,d_height+h_base],
|
|
||||||
[0,d_height+h_base]
|
|
||||||
]);
|
|
||||||
color("red")
|
|
||||||
offset(delta = 0.25)
|
|
||||||
translate([r_base,d_height,0])
|
|
||||||
mirror([1,0,0])
|
|
||||||
profile_base();
|
|
||||||
square([d_wall,0.1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module profile_wall() {
|
|
||||||
translate([r_base,0,0])
|
|
||||||
mirror([1,0,0])
|
|
||||||
difference() {
|
|
||||||
profile_wall_sub();
|
|
||||||
difference() {
|
|
||||||
translate([0, d_height+h_base-d_clear*sqrt(2), 0])
|
|
||||||
circle(r_base/2);
|
|
||||||
offset(r = r_f1)
|
|
||||||
offset(delta = -r_f1)
|
|
||||||
profile_wall_sub();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module block_wall() {
|
|
||||||
translate([0,0,h_base])
|
|
||||||
sweep_rounded(gridx*length-2*r_base-0.5-0.001, gridy*length-2*r_base-0.5-0.001)
|
|
||||||
profile_wall();
|
|
||||||
}
|
|
||||||
|
|
||||||
module block_bottom( h = 2.2 ) {
|
|
||||||
translate([0,0,h_base+0.1])
|
|
||||||
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 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 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;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
height = d_height;
|
|
||||||
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);
|
|
||||||
|
|
||||||
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")
|
|
||||||
difference() {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fillet_cutter(1,"seagreen")
|
|
||||||
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, ylen-extent-cut-(!s&&y==0?v_cut_lip:0), s);
|
|
||||||
|
|
||||||
fillet_cutter(0,"hotpink")
|
|
||||||
difference() {
|
|
||||||
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 (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 (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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module transform_main(xlen) {
|
|
||||||
translate([0,0,-(xlen-2*r_f2)/2])
|
|
||||||
linear_extrude(xlen-2*r_f2)
|
|
||||||
children();
|
|
||||||
}
|
|
||||||
|
|
||||||
module transform_tab(type, xlen, cut) {
|
|
||||||
mirror([0,0,type==1?1:0])
|
|
||||||
copy_mirror([0,0,-(abs(type)-1)])
|
|
||||||
translate([0,0,-(xlen)/2])
|
|
||||||
translate([0,0,r_f2])
|
|
||||||
linear_extrude((xlen-d_tabw-abs(cut))/(1-(abs(type)-1))-2*r_f2)
|
|
||||||
children();
|
|
||||||
}
|
|
||||||
|
|
||||||
module fillet_cutter(t = 0, c = "goldenrod") {
|
|
||||||
color(c)
|
|
||||||
minkowski() {
|
|
||||||
children();
|
|
||||||
sphere(r = r_f2-t/1000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module profile_cutter(h, length, s) {
|
|
||||||
scoop = s ? r_scoop : 0;
|
|
||||||
translate([r_f2,r_f2])
|
|
||||||
hull() {
|
|
||||||
if (length-scoop-2*r_f2 > 0)
|
|
||||||
square(0.1);
|
|
||||||
if (scoop < h) {
|
|
||||||
translate([length-2*r_f2,h-r_f2/2])
|
|
||||||
mirror([1,1])
|
|
||||||
square(0.1);
|
|
||||||
|
|
||||||
translate([0,h-r_f2/2])
|
|
||||||
mirror([0,1])
|
|
||||||
square(0.1);
|
|
||||||
}
|
|
||||||
difference() {
|
|
||||||
translate([length-scoop-2*r_f2, scoop])
|
|
||||||
if (scoop != 0) {
|
|
||||||
intersection() {
|
|
||||||
circle(scoop);
|
|
||||||
mirror([0,1]) square(2*scoop);
|
|
||||||
}
|
|
||||||
} else mirror([1,0]) square(0.1);
|
|
||||||
translate([length-scoop-2*r_f2,-1])
|
|
||||||
square([-(length-scoop-2*r_f2),2*h]);
|
|
||||||
|
|
||||||
translate([0,h])
|
|
||||||
square([2*length,scoop]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module profile_cutter_tab(h, tab, ang) {
|
|
||||||
if (tab > 0)
|
|
||||||
color("blue")
|
|
||||||
offset(delta = r_f2)
|
|
||||||
polygon([[0,h],[tab,h],[0,h-tab*tan(ang)]]);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==== Utilities =====
|
|
||||||
|
|
||||||
function clp(x,a,b) = min(max(x,a),b);
|
|
||||||
|
|
||||||
module rounded_rectangle(length, width, height, rad) {
|
|
||||||
linear_extrude(height)
|
|
||||||
offset(rad)
|
|
||||||
offset(-rad)
|
|
||||||
square([length,width], center = true);
|
|
||||||
}
|
|
||||||
|
|
||||||
module rounded_square(length, height, rad) {
|
|
||||||
rounded_rectangle(length, length, height, rad);
|
|
||||||
}
|
|
||||||
|
|
||||||
module copy_mirror(vec=[0,1,0]) {
|
|
||||||
children();
|
|
||||||
if (vec != [0,0,0])
|
|
||||||
mirror(vec)
|
|
||||||
children();
|
|
||||||
}
|
|
||||||
|
|
||||||
module pattern_linear(x = 1, y = 1, spacing = 0) {
|
|
||||||
translate([-(x-1)*spacing/2,-(y-1)*spacing/2,0])
|
|
||||||
for (i = [1:x])
|
|
||||||
for (j = [1:y])
|
|
||||||
translate([(i-1)*spacing,(j-1)*spacing,0])
|
|
||||||
children();
|
|
||||||
}
|
|
||||||
|
|
||||||
module pattern_circular(n=2) {
|
|
||||||
for (i = [1:n])
|
|
||||||
rotate(i*360/n)
|
|
||||||
children();
|
|
||||||
}
|
|
||||||
|
|
||||||
module sweep_rounded(w=10, h=10) {
|
|
||||||
union() pattern_circular(2) {
|
|
||||||
copy_mirror([1,0,0])
|
|
||||||
translate([w/2,h/2,0])
|
|
||||||
rotate_extrude(angle = 90, convexity = 4)
|
|
||||||
children();
|
|
||||||
|
|
||||||
translate([w/2,0,0])
|
|
||||||
rotate([90,0,0])
|
|
||||||
linear_extrude(height = h, center = true)
|
|
||||||
children();
|
|
||||||
|
|
||||||
rotate([0,0,90])
|
|
||||||
translate([h/2,0,0])
|
|
||||||
rotate([90,0,0])
|
|
||||||
linear_extrude(height = w, center = true)
|
|
||||||
children();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// ===== Examples =====
|
// ===== Examples =====
|
||||||
|
|
||||||
// All examples assume gridx == 3, gridy == 3, and gridz == 6, but some may work with other settings
|
// ALL EXAMPLES ASSUME gridx == 3 AND gridy == 3 but some may work with other settings
|
||||||
|
|
||||||
// 3x3 even spaced grid
|
// 3x3 even spaced grid
|
||||||
//gridfinityEqual(3, 3, 0, true);
|
//gridfinityEqual(n_divx = 3, n_divy = 3, style_tab = 0, enable_scoop = 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.
|
// 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.
|
||||||
/*
|
/*
|
||||||
|
|
Loading…
Reference in a new issue