Skip to content

Instantly share code, notes, and snippets.

@pyrho
Created July 23, 2023 15:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pyrho/6617719f4a8401f3086e003576d7fe34 to your computer and use it in GitHub Desktop.
Save pyrho/6617719f4a8401f3086e003576d7fe34 to your computer and use it in GitHub Desktop.
$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