mirror of
https://github.com/kennetek/gridfinity-rebuilt-openscad.git
synced 2024-11-17 22:10:50 +00:00
5a4396f480
- screw holes toggle - renamed base file to utility to avoid confusion - base dividing will now always be safe to use on baseplates (cannot divide into sections that cannot be multiplied by an integer to equal 1)
446 lines
13 KiB
OpenSCAD
446 lines
13 KiB
OpenSCAD
// UTILITY FILE, DO NOT EDIT
|
|
// EDIT OTHER FILES IN REPO FOR RESULTS
|
|
|
|
dht = (gridz_define==0)?gridz*7 : (gridz_define==1)?h_bot+gridz+h_base : gridz-(enable_lip?3.8:0);
|
|
d_height = (enable_zsnap?((abs(dht)%7==0)?dht:dht+7-abs(dht)%7):dht)-h_base;
|
|
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 && dht > 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();
|
|
}
|
|
|
|
// ===== 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() {
|
|
dbnxt = [for (i=[1:10]) if (abs(gridx*i)%1 < 0.001 || abs(gridx*i)%1 > 0.999) i];
|
|
dbnyt = [for (i=[1:10]) if (abs(gridy*i)%1 < 0.001 || abs(gridy*i)%1 > 0.999) i];
|
|
dbnx = 1/(div_base_auto ? len(dbnxt) > 0 ? dbnxt[0] : 1 : round(div_base_x));
|
|
dbny = 1/(div_base_auto ? len(dbnyt) > 0 ? dbnyt[0] : 1 : round(div_base_y));
|
|
xx = gridx*length-0.5;
|
|
yy = gridy*length-0.5;
|
|
|
|
translate([0,0,h_base])
|
|
rounded_rectangle(xx+0.002, yy+0.002, h_bot/1.5, r_fo1/2+0.001);
|
|
|
|
intersection(){
|
|
translate([0,0,-1])
|
|
rounded_rectangle(xx+0.005, yy+0.005, h_base+h_bot/2*10, r_fo1/2+0.001);
|
|
|
|
render()
|
|
difference() {
|
|
pattern_linear2(gridx/dbnx, gridy/dbny, dbnx*length, dbny*length)
|
|
block_base_solid(dbnx, dbny);
|
|
|
|
|
|
if (enable_holes)
|
|
pattern_linear(gridx,gridy,length)
|
|
block_base_hole();
|
|
}
|
|
}
|
|
}
|
|
|
|
module block_base_solid(dbnx, dbny) {
|
|
xx = dbnx*length-0.05;
|
|
yy = dbny*length-0.05;
|
|
translate([0,0,h_base])
|
|
mirror([0,0,1])
|
|
union() {
|
|
hull() {
|
|
rounded_rectangle(xx-2*r_c2-2*r_c1,yy-2*r_c2-2*r_c1, h_base, r_fo3/2);
|
|
rounded_rectangle(xx-2*r_c2, yy-2*r_c2, h_base-r_c1, r_fo2/2);
|
|
}
|
|
hull() {
|
|
rounded_rectangle(xx-2*r_c2, yy-2*r_c2,r_c2, r_fo2/2);
|
|
mirror([0,0,1])
|
|
rounded_rectangle(xx, yy, h_bot/2, r_fo1/2);
|
|
}
|
|
}
|
|
}
|
|
|
|
module block_base_hole() {
|
|
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 && enable_screw)
|
|
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]);
|
|
}
|
|
if (enable_screw)
|
|
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, l, s) {
|
|
scoop = s ? (length*((d_height-2)/7+1)/12-r_f2) : 0;
|
|
translate([r_f2,r_f2])
|
|
hull() {
|
|
if (l-scoop-2*r_f2 > 0)
|
|
square(0.1);
|
|
if (scoop < h) {
|
|
translate([l-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([l-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([l-scoop-2*r_f2,-1])
|
|
square([-(l-scoop-2*r_f2),2*h]);
|
|
|
|
translate([0,h])
|
|
square([2*l,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:ceil(x)])
|
|
for (j = [1:ceil(y)])
|
|
translate([(i-1)*spacing,(j-1)*spacing,0])
|
|
children();
|
|
}
|
|
|
|
module pattern_linear2(x = 1, y = 1, xs = 0, ys = 0) {
|
|
translate([-(x-1)*xs/2,-(y-1)*ys/2,0])
|
|
for (i = [1:ceil(x)])
|
|
for (j = [1:ceil(y)])
|
|
translate([(i-1)*xs,(j-1)*ys,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();
|
|
}
|
|
}
|
|
|