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 hidden or 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