Added ability to make completely custom bins
Redid the system of how bins are generated and placed in order to facilitate manual construction of compartments. Added small improvement where left and right wall lips will have fillets.
This commit is contained in:
parent
0940d399ae
commit
1cb21ef1ed
2 changed files with 237 additions and 114 deletions
|
@ -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,17 +211,14 @@ 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;
|
||||
|
@ -207,74 +227,114 @@ module cutter(i,j) {
|
|||
v_ang_tab = a_tab;
|
||||
v_ang_lip = 45;
|
||||
|
||||
enable_tab = style_tab != 5;
|
||||
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 = (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);
|
||||
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 (j==1) 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 || style_tab == 5) && j != 1))
|
||||
profile_cutter_tab(height-h_bot, tab, ang);
|
||||
if (!((gridz < 3 || t == 5) && !ylast))
|
||||
profile_cutter_tab(height-h_bot, tab, ang);
|
||||
|
||||
if (!enable_scoop && j == n_divy)
|
||||
translate([d_pitchy-extent,0,0])
|
||||
mirror([1,0,0])
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}*/
|
||||
|
||||
|
|
BIN
images/custom_dimension.gif
Normal file
BIN
images/custom_dimension.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 166 KiB |
Loading…
Reference in a new issue