463 lines
13 KiB
OpenSCAD
463 lines
13 KiB
OpenSCAD
// 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-2*d_clear-2*d_wall+d_div;
|
|
yl = gridy*length-2*d_clear-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()
|
|
translate([0,0,-d_height-h_base])
|
|
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() {
|
|
if (gridz > 0) {
|
|
difference() {
|
|
color("firebrick") block_bottom(height_internal==0?d_height-0.1:height_internal);
|
|
children();
|
|
}
|
|
color("royalblue") block_wall();
|
|
}
|
|
color("orange") block_base();
|
|
}
|
|
|
|
// 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) {
|
|
translate([0,0,height_internal==0?d_height+h_base:height_internal+h_base])
|
|
cut_move_unsafe(clp(x,0,gridx), clp(y,0,gridy), clp(w,0,gridx-x), clp(h,0,gridy-y))
|
|
children();
|
|
}
|
|
|
|
|
|
|
|
module block_negative_chamfer(depth = 10, top = 0, bot = 0) {
|
|
bc = abs(bot);
|
|
tc = abs(top);
|
|
|
|
hull() {
|
|
linear_extrude(2*(depth), center = true)
|
|
offset(-bc)
|
|
children();
|
|
|
|
linear_extrude(2*(depth-bc), center = true)
|
|
children();
|
|
}
|
|
|
|
if (tc != 0)
|
|
translate([0,0,tc])
|
|
block_negative_chamfer(depth = tc*2, bot = tc, top = 0)
|
|
offset(delta = tc-0.01)
|
|
children();
|
|
}
|
|
|
|
module block_negative_fillet(depth = 10, bot_fillet = 0) {
|
|
bf = abs(bot_fillet);
|
|
|
|
block_negative_chamfer(depth, 0, bf)
|
|
children();
|
|
|
|
minkowski() {
|
|
linear_extrude(2*(depth-bf), center = true)
|
|
offset(-bf)
|
|
children();
|
|
|
|
if (bf > 0) sphere(r = bf);
|
|
}
|
|
}
|
|
|
|
module block_negative(depth) {
|
|
linear_extrude(2*depth, center=true)
|
|
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);
|
|
|
|
intersection(){
|
|
translate([0,0,-1])
|
|
rounded_rectangle(gridx*length-0.5+0.005, gridy*length-0.5+0.005, h_base+h_bot/2*10, 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.05-2*r_c2-2*r_c1, h_base, r_fo3/2);
|
|
rounded_square(length-0.05-2*r_c2, h_base-r_c1, r_fo2/2);
|
|
}
|
|
hull() {
|
|
rounded_square(length-0.05-2*r_c2, r_c2, r_fo2/2);
|
|
mirror([0,0,1])
|
|
rounded_square(length-0.05, 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_sub() {
|
|
polygon([
|
|
[0,0],
|
|
[d_wall/2,0],
|
|
[d_wall/2,d_height-1.2-d_wall2+d_wall/2],
|
|
[d_wall2-d_clear,d_height-1.2],
|
|
[d_wall2-d_clear,d_height+h_base],
|
|
[0,d_height+h_base]
|
|
]);
|
|
}
|
|
|
|
module profile_wall_sub() {
|
|
difference() {
|
|
profile_wall_sub_sub();
|
|
color("red")
|
|
offset(delta = d_clear)
|
|
translate([r_base-d_clear,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, h, 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-d_clear;
|
|
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();
|
|
}
|
|
}
|
|
|