// Remix of https://www.thingiverse.com/thing:82533 // Remix of https://www.thingiverse.com/thing:82620 // License: Creative Commons - Attribution - Non-Commercial license. // inner dimensions width=47; depth=85; // check the minimal height height=45; simpleLatch = true; wall = 1.5; hingeOuter = 7; // outer hinge diameter hingeInner = 3; // inner rod diameter hingeInnerSlop = .5; // inner hole gap hingeOverhang=45; // easier to print inner hole hingeTopCut=0.2; // does not leave excess material on top hingeBottomCut=0.2; //easier to print inner rod hingeFingerSlop = .4; fingers = 9; // hinge fingers on one sides fingerMaxSize = 5; latchWidth = 15; latches = 2; latchWall=2; latchPin=1.5; // fillets latchF=0.5; latchF2=2; hingeF=0.5; f=[wall/2,wall/2,2]; //////////////////////////////////////////////// // private parameters $fn= $preview ? 16:64; widthOut = width+2*wall; depthOut = depth+2*wall; heightOut = height/2+2*wall; size=[widthOut,depthOut,heightOut]; fingerLength = hingeOuter/1.65; fingersOdd = fingers - (fingers%2)+1; fingerSize = max(fingerMaxSize, (depth-2*f.z-fingersOdd*hingeFingerSlop)/fingers/2); topFingerSize = fingerSize; latchStep = depth/latches; // set to 0 for printing latchRotate=0; topRotate=0; inf=3*max(size[0],max(size[1],size[2])); not=0.001; //////////////////////////////////////////////// //////////////////////////////////////////////// //////////////////////////////////////////////// d(){ u(){ bottom(); r([0,-topRotate,0],[0, -depthOut/2+f.z, heightOut]) top(); for(latchY=[latchStep/2:latchStep:depth]) t([0,latchY-depth/2,0]) { if(!simpleLatch) { r([0,latchRotate,0],[-(fingerLength*2) -widthOut, -latchWidth/2 - hingeFingerSlop, heightOut-2.25]) bottomLatch(); } } } // cut in half if(latchRotate != 0 && topRotate != 0) t([-inf,0,0]) cube([inf,depthOut/2,inf]); } //////////////////////////////////////////////// module bottom() { // main box and cutout t([-size.x/2 - fingerLength,0, 0]) d() { cubeRounded(size, center=[1,1,0],r=f); t([0,0,wall]) cubeRounded(size-[2*wall,2*wall,-inf], center=[1,1,0],r=f-[wall,wall,wall]); fxy=max(f.x,f.y); t([0,0,size.z-fxy+not]) cubeRoundedNegative([size.x-2*wall,size.y-2*wall,fxy],center=[1,1,0],r=f-[0,0,wall]); for(latchY=[latchStep/2:latchStep:depth]) t([0,latchY-depth/2,0]) if (simpleLatch) { // latch cutout t([-size.x/2+wall/2,0,wall+size.z-6]) cubeRounded([wall/2 + .1, latchWidth + hingeFingerSlop, 6], center=[0,1,0]); } } for(latchY=[latchStep/2:latchStep:depth]) t([0,latchY-depth/2,0]) { if (simpleLatch) { //latch cylinder t([-widthOut-fingerLength,0,0]) d() { tr([wall/2, -latchWidth/2-latchF, heightOut - 1],[-90,0,0]) cylinderRounded(d =[0,2], h = latchWidth+2*latchF,r=[0,latchF,0,latchF]); // front wall wipe t([wall/2,0,0]) cubeRounded([wall,depthOut,heightOut],center=[2,1,0]); } } else { // new latch for(y=[-latchWidth/2-hingeFingerSlop-latchWall,latchWidth/2+hingeFingerSlop]) { t([-2*fingerLength- widthOut,y,heightOut-2.25]) hull() { r([-90,0,0]) cylinderRounded(d =[0,hingeOuter], h = latchWall,r=[0,latchF,0,latchF]); t([fingerLength, 0,-hingeOuter]) cubeRounded([not, latchWall, hingeOuter],r=[latchF,0,0]); cubeRounded([fingerLength,latchWall,not],r=[0,0,latchF]); r([0,-20,0]) cubeRounded([hingeOuter-wall,latchWall,not]); } } // latch rod tr([-(fingerLength*2) -widthOut, -latchWidth/2 - hingeFingerSlop, heightOut-2.25],[-90,0,0]) cylinder(r = hingeInner /2, h = latchWidth + (hingeFingerSlop*2)); } } // hinge t([0,f.z,0]) d() { t([0,-depthOut/2,heightOut]) hull() { r([-90,0,0]) cylinderRounded(d=[0,hingeOuter], h = depthOut-2*f.z,r=[0,hingeF,0,hingeF]); t([-fingerLength - not,0,-hingeOuter]) cubeRounded([not,depthOut-2*f.z,hingeOuter],r=[hingeF,0,0]); t([-fingerLength,0,-.1]) cubeRounded([fingerLength,depthOut-2*f.z,not],r=[0,0,hingeF]); r([0,45,0]) cubeRounded([hingeOuter/2,depthOut-2*f.z,not],r=[0,0,hingeF]); } // finger cutouts for (y = [-depthOut/2+fingerSize/2:2*(fingerSize+hingeFingerSlop):depthOut/2-2*f.z]) t([-2*fingerLength,y,0]) cube([fingerLength*3,fingerSize + 2*hingeFingerSlop,heightOut*2]); // top cut t([0,hingeInner /2 -hingeBottomCut,heightOut+hingeOuter/2-hingeTopCut]) cubeRounded([inf,inf,size.y+1],center=[1,1,0]); } // center rod tr([0, -depthOut/2+f.z, heightOut],[-90,0,0]) d() { cylinder(r = hingeInner /2, h = depthOut-2*f.z); // cut rod bottom flat t([0,hingeInner /2 -hingeBottomCut,-1]) cubeRounded([inf,inf,size.y+1],center=[1,0,0]); } } module top() { magic=0.5; t([size.x/2+fingerLength,0,0]) difference() { cubeRounded(size-[0,0,magic],center=[1,1,0],r=f); t([0,0, wall]) cubeRounded(size+[-2*wall,-2*wall,inf], center=[1,1,0],r=f-[wall,wall,wall]); fxy=max(f.x,f.y); t([0,0,size.z-fxy-magic+not]) cubeRoundedNegative([size.x-2*wall,size.y-2*wall,fxy],center=[1,1,0],r=f-[0,0,wall]); } for(latchY=[latchStep/2:latchStep:depth]) t([0,latchY-depth/2,0]) { if (!simpleLatch) { // new latch magicZ=6.5+height/300; for(y=[-latchWidth/2-hingeFingerSlop-latchWall,latchWidth/2+hingeFingerSlop]) { t([fingerLength + widthOut,y,magicZ]) hull() { tr([fingerLength,0,0],[-90,0,0]) cylinderRounded(d=[0,hingeOuter],h=latchWall,r=[0,latchF,0,latchF]); t([-wall,0,-magicZ]) cubeRounded([wall,latchWall, hingeOuter],r=[latchF,latchF,latchF]); t([not,0,0]) cubeRounded([fingerLength,latchWall,not],r=[0,0,latchF]); tr([not,0,hingeOuter/1.5],[0,45,0]) cubeRounded([hingeOuter,latchWall,not],r=[0,0,latchF]); } } // latch rod tr([(fingerLength*2) + widthOut, -latchWidth/2 - hingeFingerSlop, magicZ],[-90,0,0]) cylinder(r = hingeInner /2, h = latchWidth + (hingeFingerSlop*2)); } } // hinge t([0,f.z,0]) d() { t([0,-depthOut/2,heightOut]) hull() { tr([0,0,0],[-90,0,0]) cylinder(r = hingeOuter/2, h = depthOut-2*f.z); t([fingerLength-.1,0,- hingeOuter -.5]) cube([.1,depthOut-2*f.z,hingeOuter -.5]); t([-fingerLength/2-not,0,-.1]) cube([fingerLength,depthOut-2*f.z,.1]); r([0,45,0]) cube([hingeOuter/2,depthOut-2*f.z,.01]); } // finger cutouts for(y = [-depthOut/2-fingerSize/2-hingeFingerSlop:2*(fingerSize+hingeFingerSlop):depthOut/2]) { t([-fingerLength,y,0]) { cube([fingerLength*3,fingerSize + 2*hingeFingerSlop,heightOut*2]); } //if (depthOut/2 - i < (fingerSize * 1.5)) { // t([-fingerLength,i - (fingerSize/2) - (hingeFingerSlop/2),0]) { // cube([fingerLength*2,depthOut,heightOut*2]); // } //} } // center cutout tr([0,0,heightOut],[-90,0,0]) r(-90) hull() cylinderRoundedH(d=[0,hingeInner+2*hingeInnerSlop], h = depthOut, center=true, chamfer=hingeOverhang); // top cut t([0,hingeInner /2 -hingeBottomCut,heightOut+hingeOuter/2-hingeTopCut]) cubeRounded([inf,inf,size.y+1],center=[1,1,0]); } for(latchY=[latchStep/2:latchStep:depth]) t([0,latchY-depth/2,0]) { if (simpleLatch) { //latch t([size.x + fingerLength - wall, (-latchWidth/2), 0]) { t([-latchWall+not,0, size.z-4]) cubeRounded([latchWall, latchWidth, 4 -.5 + 4],r=[latchF,latchF,latchF],r1=[latchF,0,0],r3=[latchF,0,0],chamfer=[0,0,0,1],chamferAngle=60); hull() for(x=[0,1-latchWall]) tr([x,0, heightOut - .5 + 3],[-90,0,0]) cylinderRounded(d=[0,2], h=latchWidth,r=[0,latchF,0,latchF]); } } } } module bottomLatch() { t([-2*fingerLength-widthOut,-latchWidth/2,0]) d() { union() { t([0,0,heightOut-2.25]) hull() { r([-90,0,0]) cylinderRounded( d=[0,hingeOuter], h = latchWidth,r=[0,latchF,0,latchF]); tr([0,0,-hingeOuter],[0,20,0]) cubeRounded([.1,latchWidth,hingeOuter],r=[latchF,latchF,latchF]); } t([-2.6 + hingeOuter/2 - wall,0,0]) cubeRounded([2.5,latchWidth,heightOut-4.5],r=[latchF,latchF,latchF],r1=[latchF,latchF2,latchF],r3=[latchF,latchF2,latchF]); // latch foot t([-fingerLength- 2.6,0,0]) cubeRounded([hingeOuter/2 + fingerLength,latchWidth,wall],r=[latchF,latchF,latchF]); // latch cylinder catch hull() for(z=[1,latchPin]) tr([-fingerLength+ 1 - 2.6,0,z],[-90,0,0]) cylinderRounded(d = [0,2], h = latchWidth,r=[0,latchF,0,latchF]); } tr([0,0,heightOut-2.25],[-90,0,0]) cylinder( r = hingeInner /2 + hingeInnerSlop, h = inf, center=true); } } ///////////////////////////////////////////// ///////////////////////////////////////////// ///////////////////////////////////////////// module cylinderRoundedH(d=[20,40],h=30,center=false,r=[not,not,not,not],chamfer=0, ellipse=[1,1]){ module c(){ cylinderRounded(d=[0,d[1]],h=h,center=false,r=r,chamfer=0, ellipse=[1,1]); } module circleChmafered(d=40,chamfer=[45,45]){ module chamfer(chamfer=45){ for(b=[0,1]) mirror([b,0]) polygon([[0,-d/2],[-d/2*cos(90-chamfer),-d/2*sin(90-chamfer)],[-d/2*cos(90-chamfer)+tan(90-chamfer)*(d/2-d/2*sin(90-chamfer)),-d/2]]); } circle(d = d); chamfer(chamfer[0]); mirror([0,1]) chamfer(chamfer[1]); } module cylinderChamfered(d=40,h=20,chamfer=45){ r(90) linear_extrude(h) circleChmafered(d=d,chamfer=[chamfer,0]); } module cc(){ i(){ hull(){ c(); rt([0,0,90-chamfer],v=[d[1],0,0]) c(); } hull(){ tr([d[1]/2-r[1],0,r[1]],[90,0,0]) cylinderChamfered(2*r[1],d[1]/2,chamfer); tr([d[1]/2-r[3],0,h-r[3]],[90,0,0]) cylinderChamfered(2*r[3],d[1]/2,chamfer); t([-d[1],-d[1],0]) cube([d[1],d[1],h]); } } } not=0.0001; d(){ t([0,0,-h/2* (center?1:0)]) scale([ellipse.y,ellipse.x,1]){ cc(); mirror([0,1,0]) cc(); } t([0,0,-not]) cylinderRounded(d=[0,d[0]],h=h+2*not,center=false,r=[0,-r[0],0,-r[2]]); } } module cubeRounded( size=[3,3,3], r=[0,0,0], r0=[-1,-1,-1], r1=[-1,-1,-1], r2=[-1,-1,-1], r3=[-1,-1,-1], r4=[-1,-1,-1], r5=[-1,-1,-1], r6=[-1,-1,-1], r7=[-1,-1,-1], chamfer=[0,0,0,0], chamferAngle=60, center=[0,0,0], not=0.0001 ){ function max3(v)=max(v.x,max(v.y,v.z)); function sat3(a) = [max(not,a.x),max(not,a.y),max(not,a.z)]; function chg(a, b) = (a.x==-1 && a.y==-1 && a.z==-1) ? b : a; module corner(r=[0.5,1,3],shift=0,$fn=$fn){ not=0.0001; n=max($fn/4,2); step= $fn<1 ? 4/8 : 4/$fn; tr([r.z,r.z,0],180) for(j=[0:n]) { i=j/n; mix1=(1-i)*r.y+i*r.x; r(i*90) r([90,0,0]) linear_extrude(not) t([r.z-mix1,mix1,0]) intersection(){ r(360/$fn*(shift)) circle(r=mix1,$fn=$fn/2); // r(360/$fn*(shift-j%2)) circle(r=mix1,$fn=$fn/2); mirror([0,1,0]) mirror([0,0,1]) square([mix1,mix1]); } } } module rotatedCorner(r=[0.5,1,3],shift=0){ if(r.x==max3(r)){ mirror([1,0,0]) r([0,-90,0]) corner([r.z,r.y,r.x],shift); } else if(r.y==max3(r)){ mirror([0,1,0]) r([90,0,0]) corner([r.x,r.z,r.y],shift); } else { corner(r,shift); } } module halfTriangle3D(angle=45,h=10,z=10){ linear_extrude(z) polygon([[0,0],[h*tan(angle),0],[0,h]]); } _r0 = sat3(chg(r0, r)); _r1 = sat3(chg(r1, r)); _r2 = sat3(chg(r2, r)); _r3 = sat3(chg(r3, r)); _r4 = sat3(chg(r4, r)); _r5 = sat3(chg(r5, r)); _r6 = sat3(chg(r6, r)); _r7 = sat3(chg(r7, r)); module cubeR(){ hull() { rotatedCorner(_r0); t([size.x,0,0]) mirror([1,0,0]) rotatedCorner(_r1,1); t([0,size.y,0]) mirror([0,1,0]) rotatedCorner(_r2,1); t([size.x,size.y,0]) mirror([0,1,0]) mirror([1,0,0]) rotatedCorner(_r3); t([0,0,size.z]) mirror([0,0,1]) rotatedCorner(_r4,1); t([size.x,0,size.z]) mirror([0,0,1]) mirror([1,0,0]) rotatedCorner(_r5); t([0,size.y,size.z]) mirror([0,0,1]) mirror([0,1,0]) rotatedCorner(_r6); t([size.x,size.y,size.z]) mirror([0,0,1]) mirror([0,1,0]) mirror([1,0,0]) rotatedCorner(_r7,1); } //chamfering if(chamfer[0]) r([0,90,0]) halfTriangle3D(90-chamferAngle,size.y/(chamfer[2]+1),size.x); if(chamfer[2]) tr([0,size.y,0],[0,90,0]) mirror([0,1,0]) halfTriangle3D(90-chamferAngle,size.y/(chamfer[0]+1),size.x); if(chamfer[1]) tr([0,size.y,0],[90,90,0]) halfTriangle3D(90-chamferAngle,size.x/(chamfer[3]+1),size.y); if(chamfer[3]) tr([size.x,size.y,0],[90,90,0]) mirror([0,1,0]) halfTriangle3D(90-chamferAngle,size.x/(chamfer[1]+1),size.y); } t([-size.x/2*center.x,-size.y/2*center.y,-size.z/2*center.z]) cubeR(); } module cylinderRounded(d=[20,40],h=30,center=false,r=[0,0,0,0],chamfer=0, ellipse=[1,1]){ t([0,0,-h/2* (center?1:0)]) scale([ellipse.y,ellipse.x,1]) rotate_extrude(convexity = 10) { //square difference(){ t([d[0]/2,0,0]) square([(d[1]-d[0])/2,h]); if(r[0]>0) t([d[0]/2,0,0]) square([r[0],r[0]]); if(r[1]>0) t([d[1]/2-r[1],0,0]) square([r[1],r[1]]); if(r[2]>0) t([d[0]/2,h-r[2],0]) square([r[2],r[2]]); if(r[3]>0) t([d[1]/2-r[3],h-r[3],0]) square([r[3],r[3]]); } //corners if(r[0]>0){ intersection(){ t([d[0]/2+r[0], r[0], 0]) circle(r = r[0]); t([d[0]/2,0,0]) square([r[0],r[0]]); } polygon([[d[0]/2+r[0],0],[d[0]/2+r[0]-r[0]*cos(90-chamfer),r[0]-r[0]*sin(90-chamfer)],[d[0]/2+r[0]-r[0]*cos(90-chamfer)+tan(90-chamfer)*(r[0]-r[0]*sin(90-chamfer)),0]]); } else { difference(){ t([d[0]/2+r[0], 0, 0]) square([-r[0],-r[0]]); t([d[0]/2+r[0], -r[0], 0]) circle(r = -r[0]); } } if(r[1]>0){ intersection(){ t([d[1]/2-r[1], r[1], 0]) circle(r = r[1]); t([d[1]/2-r[1],0,0]) square([r[1],r[1]]); } polygon([[d[1]/2-r[1],0],[d[1]/2-r[1]+r[1]*cos(90-chamfer),r[1]-r[1]*sin(90-chamfer)],[d[1]/2-r[1]+r[1]*cos(90-chamfer)-tan(90-chamfer)*(r[1]-r[1]*sin(90-chamfer)),0]]); } else { difference(){ t([d[1]/2, 0, 0]) square([-r[1],-r[1]]); t([d[1]/2-r[1], -r[1], 0]) circle(r = -r[1]); } } if(r[2]>0){ intersection(){ t([d[0]/2+r[2], h-r[2], 0]) circle(r = r[2]); t([d[0]/2,h-r[2],0]) square([r[2],r[2]]); } } else { difference(){ t([d[0]/2+r[2], h+r[2], 0]) square([-r[2],-r[2]]); t([d[0]/2+r[2], h+r[2], 0]) circle(r = -r[2]); } } if(r[3]>0){ intersection(){ t([d[1]/2-r[3], h-r[3], 0]) circle(r = r[3]); t([d[1]/2-r[3],h-r[3],0]) square([r[3],r[3]]); } } else { difference(){ t([d[1]/2, h+r[3], 0]) square([-r[3],-r[3]]); t([d[1]/2-r[3], h+r[3], 0]) circle(r = -r[3]); } } } } module cubeRoundedNegative( size=[3,3,3], r=[0,0,0], chamfer=[0,0,0,0], chamferAngle=60, center=[0,0,0], not=0.0001 ){ _r=[max(r[0],not),max(r[1],not),max(r[2],not)]; module squareTorus(size=[15,30,3],r=5){ t(size/2){ for(s=[-1,1]) tr([s*(size.x+size.z)/2,0,0],[-90,0,0]) cylinder(d=size.z,h=size.y-2*r+2*not,center=true); for(s=[-1,1]) tr([0,s*(size.y+size.z)/2,0],[0,90,0]) cylinder(d=size.z,h=size.x-2*r+2*not,center=true); //corners for(x=[-1,1]) for(y=[-1,1]){ t([x*(size.x/2-r),y*(size.y/2-r),0]) mirror([1-x,1-y,0]){ rotate_extrude(angle=90) t([size.z/2+r,0]) circle(d=size.z); } } } } module halfTriangle3D(angle=45,h=10,z=10){ linear_extrude(z) polygon([[0,0],[h*tan(angle),0],[0,h]]); } module roundedBase(size,r){ linear_extrude(size.z) hull() for(x=[r,size.x-r]) for(y=[r,size.y-r]) t([x,y,0]) circle(r=r); } module cubeR(){ difference(){ u(){ *t([-_r[0],-_r[0],0]) roundedBase([size.x+2*_r[0],size.y+2*_r[0],_r[0]],_r[0]+_r[2]); roundedBase(size,_r[2]); t([-_r[1],-_r[1],size.z-_r[1]]) roundedBase([size.x+2*_r[1],size.y+2*_r[1],_r[1]],_r[1]+_r[2]); } *squareTorus([size.x,size.y,2*_r[0]],r=_r[2]); t([0,0,size.z-2*_r[1]]) squareTorus([size.x,size.y,2*_r[1]],r=_r[2]); } //chamfering if(chamfer[0]) r([0,90,0]) halfTriangle3D(90-chamferAngle,size.y/(chamfer[2]+1),size.x); if(chamfer[2]) tr([0,size.y,0],[0,90,0]) mirror([0,1,0]) halfTriangle3D(90-chamferAngle,size.y/(chamfer[0]+1),size.x); if(chamfer[1]) tr([0,size.y,0],[90,90,0]) halfTriangle3D(90-chamferAngle,size.x/(chamfer[3]+1),size.y); if(chamfer[3]) tr([size.x,size.y,0],[90,90,0]) mirror([0,1,0]) halfTriangle3D(90-chamferAngle,size.x/(chamfer[1]+1),size.y); } t([-size.x/2*center.x,-size.y/2*center.y,-size.z/2*center.z]) cubeR(); } module t(v=[0,0,0]){translate(v) children();} module r(a=[0,0,0],rp=[0,0,0]){translate(rp) rotate(a) translate(-rp) children();} module tr(v=[0,0,0],a=[0,0,0],rp=[0,0,0]){t(v) r(a,rp) children();} module rt(a=[0,0,0],rp=[0,0,0],v=[0,0,0]){r(a,rp) t(v) children();} module u(){union() children();} module d(){if($children<=1) children(); if($children>1) difference(){children(0); children([1:$children-1]);}} module i(){if($children<=1) children(); else intersection_for(i=[0:$children-1]) children(i);}