Created
July 23, 2023 15:18
-
-
Save pyrho/6617719f4a8401f3086e003576d7fe34 to your computer and use it in GitHub Desktop.
Tiny modification to https://www.printables.com/model/453323-customizable-bike-bottle-holder
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$fn= $preview ? 16:32; | |
// bottle | |
bottleD=73-10; | |
bottleHullTol=0.2; // render tolerance | |
// bike frame | |
frameD=35+.5; | |
framePadD=11+.5; // diameter of pads around frame holes | |
framePadH=0; // 0 for flat back | |
boltZOffset=-15; | |
// bolts | |
bolt=5+.5; | |
boltPad=2; | |
boltHead=8.5+.5; | |
boltHeadL=5+.5; | |
boltSpan=64; | |
// main bar | |
mainBarShiftZ=0; | |
mainBarAroundFrame=0; // extra thickness around frame | |
mainBar=[framePadH+boltPad+boltHeadL,20,142]; // basic dimensions | |
mainBarExtraX=1; // extra thickness for curved shape | |
stopper=25; // size of foot | |
stopperF=10; | |
lockH=5; // size of top lock | |
lockR=3; | |
lockPos=140; // distance from the foot to the center of the lock | |
// flexible hand holding the bottle | |
handH=140+5; // total height of the hand | |
handH2=25; // extra height at tips | |
handAngle=150; | |
handZShift=mainBarShiftZ; | |
fingerW=3; // thickness of fingers | |
fingerH=15; // width of fingers | |
renderSteps=20; // render precision of the hand | |
wall=2; | |
// fillets | |
f=2; | |
f3=5; | |
inf=500; | |
not=0.001; | |
///////////////////////////////////////////// | |
d(){ | |
u(){ | |
// main bar | |
d(){ | |
t([-mainBarAroundFrame,0,mainBarShiftZ]) cubeRounded(mainBar+[mainBarExtraX+mainBarAroundFrame,0,0],center=[0,1,1],r=[f3,f,f],r02=[wall/2,wall/2,f3],r13=[wall/2,wall/2,f],r46=[f3,f,f3]); | |
t([bottleD/2+mainBar.x,0,0]) bottle(); | |
} | |
t([0,0,mainBarShiftZ]) t([0,0,-mainBar.z/2]){ | |
//stopper | |
cubeRounded([stopper,mainBar.y,wall],center=[0,1,0],r=[wall/2,wall/2,f3],r13=[wall/2,wall/2,stopperF],r57=[wall/2,wall/2,stopperF]); | |
// lock | |
t([0,0,lockPos]) lock(); | |
} | |
//hand | |
tr([bottleD/2+mainBar.x,0,handZShift],[0,-90,0]) { | |
for(b=[0,1]) mirror([0,b,0]) hand(); | |
} | |
} | |
frame(); | |
} | |
///////////////////////////////////////////// | |
module lock(){ | |
if (lockH>0) hull(){ | |
t([mainBar.x/2,0,-lockH]) cubeRounded([not,mainBar.y-1,not],center=[0,1,1]); | |
for(s=[-1,1]) t([mainBar.x-lockR+lockH,s*mainBar.y/4,0]) sphere(r=lockR); | |
} | |
} | |
module hand(){ | |
step=1/renderSteps; | |
extraD=bottleD/2+bottleHullTol; | |
fingerCylinderCut=.1; | |
i(){ | |
d(){ | |
u(){ | |
// fingers | |
for(p=[0:step:1-step]) for(s=[1,-1]) { | |
hull() | |
for(p2=[0,step]){ | |
p3=p+p2; | |
tr([(1-p3)*s*(handH/2-fingerH/2)+s*p3*handH2/2,0,0],[p3*handAngle,0,0]) t([0,0,extraD]) i() { | |
cylinderRounded(d=[0,fingerH],h=fingerW,r=[0,fingerW/2,0,fingerW/2],chamfer=30); | |
cubeRounded([inf,fingerCylinderCut,inf],center=[1,1,1]); | |
} | |
} | |
} | |
// endpoints | |
for(s=[1,-1]){ | |
p3=1; | |
tr([(1-p3)*s*(handH/2-fingerH/2)+s*p3*handH2/2,0,0],[p3*handAngle,0,0]) t([0,0,extraD]) i(){ | |
cylinderRounded(d=[0,fingerH],h=fingerW,r=[0,fingerW/2,0,fingerW/2],chamfer=30); | |
cubeRounded([inf,inf,fingerW],center=[1,2,0]); | |
} | |
} | |
// mid connection | |
hull() for(s=[1,-1]){ | |
p3=1; | |
tr([(1-p3)*s*(handH/2-fingerH/2)+s*p3*handH2/2,0,0],[p3*handAngle,0,0]) t([0,0,extraD]) i(){ | |
cylinderRounded(d=[0,fingerH],h=fingerW,r=[0,fingerW/2,0,fingerW/2],chamfer=30); | |
cubeRounded([inf,inf,fingerW],center=[1,2,0],r=[fingerW/2,fingerW/2,fingerW/2]); | |
} | |
} | |
} | |
r([0,90,0]) bottle(); | |
cubeRounded([inf,mainBar.y-f3,inf]+[mainBarExtraX,0,0],center=[1,1,0]); | |
} | |
// outer cylinder | |
//r([0,90,0]) cylinder(d=bottleD+2*fingerW,h=inf, center=true,$fn=2*$fn); | |
} | |
} | |
module frame(){ | |
t([-frameD/2,0,0]) cylinder(d=frameD,h=inf,center=true,$fn=2*$fn); | |
t([0, 0, boltZOffset]) | |
for(z=[0.5,-0.5]) t([0,0,(z*boltSpan)]){ | |
t([framePadH,0,0]) r([0,-90,0]) cylinder(d=framePadD,h=inf); | |
t([framePadH,0,0]) r([0,90,0]) bolt(bolt=bolt,boltHead=boltHead,boltHeadL=inf,boltPad=boltPad,nut=0); | |
} | |
} | |
module bottle(){ | |
cylinder(d=bottleD,h=inf, center=true,$fn=2*$fn); | |
} | |
///////////////////////////////////////////// | |
///////////////////////////////////////////// | |
///////////////////////////////////////////// | |
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], | |
r0123=[-1,-1,-1], | |
r4567=[-1,-1,-1], | |
r01=[-1,-1,-1], | |
r02=[-1,-1,-1], | |
r13=[-1,-1,-1], | |
r23=[-1,-1,-1], | |
r45=[-1,-1,-1], | |
r46=[-1,-1,-1], | |
r57=[-1,-1,-1], | |
r67=[-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],$fn=$fn){ | |
not=0.0001; | |
step= $fn<1 ? 4/8 : 4/$fn; | |
tr([r.z,r.z,0],180) | |
for(i=[0:step:1-step]){ | |
r(i*90) //hull() | |
{ | |
mix1=(1-i)*r.y+i*r.x; | |
r([90,0,0]) linear_extrude(not) t([r.z-mix1,mix1,0]) intersection(){ | |
circle(r=mix1); | |
mirror([0,1,0]) mirror([0,0,1]) square([mix1,mix1]); | |
} | |
i1=i+step; | |
mix2=(1-i1)*r.y+i1*r.x; | |
r([90,0,step*90]) linear_extrude(not) t([r.z-mix2,mix2,0]) intersection(){ | |
circle(r=mix2); | |
mirror([0,1,0]) mirror([0,0,1]) square([mix1,mix1]); | |
} | |
} | |
} | |
} | |
module rotatedCorner(r=[0.5,1,3]){ | |
if(r.x==max3(r)){ | |
mirror([1,0,0]) r([0,-90,0]) corner([r.z,r.y,r.x]); | |
} else if(r.y==max3(r)){ | |
mirror([0,1,0]) r([90,0,0]) corner([r.x,r.z,r.y]); | |
} else { | |
corner(r); | |
} | |
} | |
module halfTriangle3D(angle=45,h=10,z=10){ | |
linear_extrude(z) polygon([[0,0],[h*tan(angle),0],[0,h]]); | |
} | |
_r0 = sat3(chg(r0,chg(r01,chg(r02,chg(r0123,r))))); | |
_r1 = sat3(chg(r1,chg(r01,chg(r13,chg(r0123,r))))); | |
_r2 = sat3(chg(r2,chg(r23,chg(r02,chg(r0123,r))))); | |
_r3 = sat3(chg(r3,chg(r23,chg(r13,chg(r0123,r))))); | |
_r4 = sat3(chg(r4,chg(r45,chg(r46,chg(r4567,r))))); | |
_r5 = sat3(chg(r5,chg(r45,chg(r57,chg(r4567,r))))); | |
_r6 = sat3(chg(r6,chg(r67,chg(r46,chg(r4567,r))))); | |
_r7 = sat3(chg(r7,chg(r67,chg(r57,chg(r4567,r))))); | |
module cubeR(){ | |
hull() | |
{ | |
rotatedCorner(_r0); | |
t([size.x,0,0]) mirror([1,0,0]) rotatedCorner(_r1); | |
t([0,size.y,0]) mirror([0,1,0]) rotatedCorner(_r2); | |
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); | |
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); | |
} | |
//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 bolt(bolt=3, boltHead=6, boltPad=3, boltHeadL=10, boltL=30, nut=6.3, nutPad=5, nutL=4, nutR=0, sideCutL=20, sideCutSlot=4, layer1=0, layer2=0, tol=0, not=0.001){ | |
bottom=layer1>0?true:false; | |
top=layer2>0?true:false; | |
t([0,0,-boltL+not+boltPad]) cylinder(d=bolt+tol,h=boltL); | |
t([0,0,boltPad-2*layer1]) difference(){ | |
cylinder(d=boltHead+tol,h=boltHeadL+2*layer1); | |
//easy print bolt | |
if(layer1>0){ | |
t([(bolt+tol)/2,-(boltHead+tol)/2,-not]) cube([(boltHead-bolt)/2,boltHead+tol,2*layer1+not]); | |
t([-(boltHead+tol)/2,-(boltHead+tol)/2,-not]) cube([(boltHead-bolt)/2,boltHead+tol,2*layer1+not]); | |
t([-(boltHead+tol)/2,-(boltHead+tol)/2,-not]) cube([boltHead+tol,(boltHead-bolt)/2,layer1+not]); | |
t([-(boltHead+tol)/2,(bolt+tol)/2,-not]) cube([boltHead+tol,(boltHead-bolt)/2,layer1+not]); | |
} | |
} | |
//nut | |
if(nut>0 && nutL>0){ | |
rotate([0,0,nutR]) difference(){ | |
t([0,0,-nutPad-nutL-sideCutSlot-2*layer1]){ | |
cylinder(d=nut+tol,h=nutL+sideCutSlot+2*layer1+2*layer2,$fn=6); | |
hull(){ | |
for(x=[0,sideCutL]) t([x,0,0]) cylinder(d=nut+tol,h=nutL+2*layer1,$fn=6); | |
} | |
} | |
//easy print nut slot | |
if(layer1>0){ | |
t([(bolt+tol)/2,-nut/2,-nutPad-nutL-sideCutSlot-2*layer1-not]) cube([sideCutL+nut+tol,nut,2*layer1+not]); | |
t([-(bolt+tol)/2-nut,-nut/2,-nutPad-nutL-sideCutSlot-2*layer1-not]) cube([nut,nut,2*layer1+not]); | |
t([-nut/2,-(bolt+tol)/2-nut,-nutPad-nutL-sideCutSlot-2*layer1-not]) cube([nut,nut,layer1+not]); | |
t([-nut/2,+(bolt+tol)/2,-nutPad-nutL-sideCutSlot-2*layer1-not]) cube([nut,nut,layer1+not]); | |
} | |
//easy print nut | |
if(layer2>0){ | |
t([(bolt+tol)/2,-nut/2,-nutPad]) cube([nut,nut,2*layer2+not]); | |
t([-(bolt+tol)/2-nut,-nut/2,-nutPad]) cube([nut,nut,2*layer2+not]); | |
t([-nut/2,-(bolt+tol)/2-nut,-nutPad+layer2]) cube([nut,nut,layer2+not]); | |
t([-nut/2,+(bolt+tol)/2,-nutPad+layer2]) cube([nut,nut,layer2+not]); | |
} | |
} | |
} | |
} | |
module cylinderRounded(d=[20,40],h=30,center=false,r=[0,0,0,0],chamfer=0.001, 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 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);} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment