316 lines
9.4 KiB
OpenSCAD
316 lines
9.4 KiB
OpenSCAD
|
$fa = 5;
|
||
|
$fs = 0.25;
|
||
|
|
||
|
|
||
|
// ===== Parameters =====
|
||
|
|
||
|
gridx = 2; // number of bases along x-axis
|
||
|
gridy = 2; // number of bases along y-axis
|
||
|
gridz = 3; // unit height along z-axis (2, 3, or 6, but can be anything)
|
||
|
n_div = 2; // number of compartments (ideally, coprime w/ gridx)
|
||
|
// set n_div to 0 for a solid bin (for custom bins)
|
||
|
|
||
|
length = 42; // base unit (if you want to go rogue ig)
|
||
|
|
||
|
// type of tab style. alignment only matters if tabs are large enough
|
||
|
// 0:full, 1:automatic, 2:right, 3:center, 4:left, 5:none
|
||
|
style_tab = 0;
|
||
|
|
||
|
enable_scoop = true; // the rounded edge that allows for easy removal
|
||
|
enable_holes = true; // holes on the base for magnet / screw
|
||
|
enable_holeslit = true; // extra cut within holes for better slicing
|
||
|
|
||
|
// ===== Info =====
|
||
|
// the red plane that is the top of the internal bin is d_height+h_base above z=0
|
||
|
// the tab cutter object causes serious lag in the preview, I think it has something to do with cutting the same surfaces as other cutting objects, but I cannot seem to fix it, apologies
|
||
|
// the magnet holes have an extra cut in them to make it easier to print without supports
|
||
|
|
||
|
// ===== Dimensions =====
|
||
|
|
||
|
// base
|
||
|
h_base = 5; // height of the base
|
||
|
r_base = 4; // outside rounded radius of bin
|
||
|
r_c1 = 0.8; // lower base chamfer "radius"
|
||
|
r_c2 = 2.4; // upper base chamfer "radius"
|
||
|
h_bot = 2.2; // bottom thiccness of bin
|
||
|
|
||
|
// base holes
|
||
|
r_hole1 = 1.5; // screw hole radius
|
||
|
r_hole2 = 3.25; // magnet hole radius
|
||
|
d_hole = 26; // center-to-center distance between holes
|
||
|
h_hole = 2.4; // magnet hole depth
|
||
|
|
||
|
// fillets
|
||
|
r_f1 = 0.6; // top edge fillet radius
|
||
|
r_f2 = 2.8; // internal fillet radius
|
||
|
r_f3 = 0.6; // lip fillet radius
|
||
|
|
||
|
// misc
|
||
|
d_div = 1.2; // width of divider between compartments
|
||
|
d_wall = 0.95; // minimum wall thickness
|
||
|
d_clear = 0.25; // tolerance fit factor
|
||
|
|
||
|
// tabs
|
||
|
d_tabh = 15.85; // height of tab (yaxis, measured from inner wall)
|
||
|
d_tabw = length; // maximum width of tab
|
||
|
a_tab = 32;
|
||
|
|
||
|
// calculations
|
||
|
d_height = (gridz-1)*7 + 2;
|
||
|
r_scoop = length*gridz/12; // scoop radius
|
||
|
d_wall2 = r_base-r_c1-d_clear*sqrt(2);
|
||
|
|
||
|
d_width = (gridx*length-2*d_wall-(n_div-1)*d_div)/n_div;
|
||
|
b_notab = style_tab == 5 || gridz < 3;
|
||
|
|
||
|
// magic numbers (cutter parameters)
|
||
|
v_tab = [r_f2, r_f3, d_height-h_bot-(d_tabh-d_wall)*tan(a_tab), d_tabh-d_wall-r_f3/tan(a_tab/2), d_height-h_bot-r_f3, 179, a_tab, r_f2];
|
||
|
v_edg = [r_f2, 0, d_height-h_bot-d_wall2, d_wall2-d_wall, d_height-h_bot-d_wall, 90, 45];
|
||
|
v_slo = [r_scoop, 0, 2*d_height, 0, 0, 30, 10];
|
||
|
|
||
|
|
||
|
|
||
|
gridfinity();
|
||
|
|
||
|
|
||
|
// ===== Modules =====
|
||
|
|
||
|
module gridfinity() {
|
||
|
difference() {
|
||
|
// solid bin
|
||
|
block_bottom(d_height);
|
||
|
|
||
|
// subtraction blocks
|
||
|
block_cutter();
|
||
|
}
|
||
|
block_base();
|
||
|
block_wall();
|
||
|
}
|
||
|
|
||
|
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() {
|
||
|
color("orange")
|
||
|
pattern_linear(gridx, gridy, length)
|
||
|
render() union() {
|
||
|
sweep_rounded(length-2*r_base,length-2*r_base) profile_base();
|
||
|
pattern_circular(4) difference() {
|
||
|
linear_extrude(h_base) square(length/2-r_base);
|
||
|
if (enable_holes)
|
||
|
translate([d_hole/2, d_hole/2, 0]) union() {
|
||
|
cylinder(h = 3*h_base, r = r_hole1, center=true);
|
||
|
cylinder(h = 2*h_hole, r = r_hole2, center=true);
|
||
|
if (enable_holeslit) intersection() {
|
||
|
cylinder(h = 2*(h_hole+0.2), r = r_hole2, center=true);
|
||
|
cube([r_hole1*2,r_hole2*3,2*(h_hole+0.4)], center=true);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
module profile_wall_sub() {
|
||
|
difference() {
|
||
|
polygon([
|
||
|
[0,0],
|
||
|
[d_wall/2,0],
|
||
|
[d_wall/2,d_height-d_wall2-d_wall/2],
|
||
|
[d_wall2,d_height-d_wall],
|
||
|
[d_wall2,d_height+h_base],
|
||
|
[0,d_height+h_base]
|
||
|
]);
|
||
|
offset(delta = 0.25)
|
||
|
translate([r_base,d_height,0])
|
||
|
mirror([1,0,0])
|
||
|
profile_base();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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() {
|
||
|
color("royalblue")
|
||
|
translate([0,0,h_base])
|
||
|
sweep_rounded(gridx*length-2*r_base, gridy*length-2*r_base)
|
||
|
profile_wall();
|
||
|
}
|
||
|
|
||
|
module block_bottom( h = 2.2 ) {
|
||
|
color("firebrick")
|
||
|
translate([0,0,h_base])
|
||
|
hull()
|
||
|
sweep_rounded(gridx*length-2*r_base, gridy*length-2*r_base)
|
||
|
translate([r_base-0.1,0,0])
|
||
|
mirror([1,0,0])
|
||
|
square([d_wall, d_height]);
|
||
|
}
|
||
|
|
||
|
module profile_cutter(r = 0, width = 1, stretch = 0) {
|
||
|
extent = width/2-r_f2+0.1;
|
||
|
translate([r-r_f2,0,0])
|
||
|
union() {
|
||
|
difference() {
|
||
|
union() {
|
||
|
translate([0,extent,0]) circle(r=r_f2);
|
||
|
square([r_f2,extent]);
|
||
|
}
|
||
|
translate([-r_f2,0,0]) square([r_f2,extent+r_f2]);
|
||
|
}
|
||
|
mirror([1,0,0]) square([stretch,extent+r_f2]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
module part_bend(t=[0,0], rot, ang, rad, width, s = 0) {
|
||
|
translate([t.x,t.y,0])
|
||
|
rotate([0,0,rot])
|
||
|
rotate_extrude(angle = ang, convexity = 4)
|
||
|
profile_cutter(rad,width,s);
|
||
|
}
|
||
|
|
||
|
module part_line(t=[0,0], rot, d, width, s = 0) {
|
||
|
translate([t.x,t.y,0])
|
||
|
rotate([90,0,rot])
|
||
|
translate([r_f2,0,0])
|
||
|
linear_extrude(d)
|
||
|
profile_cutter(0,width,s);
|
||
|
}
|
||
|
|
||
|
module cutter_main(arr, width, offset = 0) {
|
||
|
|
||
|
r1 = arr[0];
|
||
|
r2 = arr[1];
|
||
|
a_end = arr[5];
|
||
|
a_slo = arr[6];
|
||
|
p_y3 = arr[2] - r1*tan((90-a_slo)/2);
|
||
|
p_x4 = arr[3];
|
||
|
p_y4 = arr[4];
|
||
|
|
||
|
d_width = width;
|
||
|
d_extent = gridy*length/2 - d_wall + 0.1;
|
||
|
|
||
|
l_angle = ([p_x4-r1,p_y4-p_y3] * [[cos(a_slo), -sin(a_slo)], [sin(a_slo), cos(a_slo)]])[0];
|
||
|
|
||
|
translate([0,gridy*length/2-d_wall,h_base+h_bot])
|
||
|
rotate([90,0,-90])
|
||
|
union()
|
||
|
copy_mirror([0,0,1])
|
||
|
translate([offset,0,-0.1]) {
|
||
|
// outside of hull because of its concave geometry
|
||
|
if (p_x4 != 0 || p_y4 != 0) difference()
|
||
|
{
|
||
|
union() {
|
||
|
render() part_bend([p_x4, p_y4], a_slo+90, a_end-a_slo, -r2, d_width, 2*r_f2);
|
||
|
part_line([p_x4, p_y4]+(r_f2+r2)*ta(a_end-90)-0.1*ta(a_end), a_end+90, d_extent, d_width, 2*r_f2);
|
||
|
part_line([r1, p_y3]+(r1-r_f2)*-ta(-a_slo)+l_angle*ta(a_slo), a_slo+90, l_angle, d_width, 2*r_f2);
|
||
|
}
|
||
|
copy_mirror([0,1,0]) translate([0,-0.1,-0.1]) cube([d_extent+0.1,r_f2,d_width]);
|
||
|
}
|
||
|
hull()
|
||
|
{
|
||
|
// left bottom, angle, and scoop fillets
|
||
|
part_bend([r1, r1], -180, 90, r1, d_width);
|
||
|
part_bend([r1, p_y3], 90+a_slo, 90-a_slo, r1, d_width);
|
||
|
|
||
|
// bottom, left, right, angle (thin), angle (thicc)
|
||
|
part_line([d_extent, r_f2], -90, d_extent - r1, d_width);
|
||
|
part_line([r_f2, r1], 180, p_y3 - r1, d_width);
|
||
|
part_line([r1, p_y3]+(r1-r_f2)*-ta(-a_slo), a_slo+90, (d_extent-r1+(r1-r_f2)*cos(a_slo))/cos(a_slo), d_width);
|
||
|
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
module cutter_tab(s = 1) {
|
||
|
d_divw = (gridx*length-2*d_wall-(n_div-1)*d_div)/n_div;
|
||
|
if ((d_divw > d_tabw && s != 0 && d_divw - d_tabw > 4*r_f2 ) || s == 5) {
|
||
|
d_w = (d_divw - (s==5?0:length)) / (s==3?2:1);
|
||
|
mirror([s==2?1:0, 0, 0])
|
||
|
copy_mirror([s==3?1:0, 0, 0])
|
||
|
translate([(d_divw-d_w)/2,0,0])
|
||
|
cutter_main(v_edg, d_w);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
module block_cutter() {
|
||
|
if (n_div > 0) {
|
||
|
pattern_linear(n_div, 1, d_width + d_div)
|
||
|
cutter_main(b_notab ? v_edg : v_tab, d_width);
|
||
|
if (!b_notab) {
|
||
|
for (i = [1:n_div])
|
||
|
translate([((i-1)-(n_div-1)/2)*(d_width + d_div),0,0])
|
||
|
cutter_tab(style_tab==1?(i==1?4:(i==n_div?2:3)):style_tab);
|
||
|
}
|
||
|
mirror([0,1,0])
|
||
|
for (i = [1:n_div])
|
||
|
translate(((i-1)-(n_div-1)/2)*(d_width + d_div)*[1,0,0])
|
||
|
cutter_main(enable_scoop?v_slo:v_edg, d_width, enable_scoop?d_wall2-d_wall : 0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// ==== Utilities =====
|
||
|
|
||
|
ta = function (a) [cos(a), sin(a)];
|
||
|
|
||
|
module copy_mirror(vec=[0,1,0]) {
|
||
|
children();
|
||
|
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();
|
||
|
}
|
||
|
}
|
||
|
|