add attachable to bottlecaps & mutators

This commit is contained in:
Adrian Mariano 2023-04-27 22:15:10 -04:00
parent 25aef90fc7
commit 1bee8c96f9
3 changed files with 418 additions and 352 deletions

View file

@ -443,15 +443,17 @@ module generic_bottle_neck(
diamMagMult = neck_d / 26.19; diamMagMult = neck_d / 26.19;
heightMagMult = height / 17.00; heightMagMult = height / 17.00;
assert(all_nonnegative([support_d]),"support_d must be a nonnegative number");
sup_r = 0.30 * (heightMagMult > 1 ? heightMagMult : 1); sup_r = 0.30 * (heightMagMult > 1 ? heightMagMult : 1);
support_r = floor(((supp_d == neck_d) ? sup_r : min(sup_r, (supp_d - neck_d) / 2)) * 5000) / 10000; support_r = floor(((supp_d == neck_d) ? sup_r : min(sup_r, (supp_d - neck_d) / 2)) * 5000) / 10000;
support_rad = (wall == undef || !round_supp) ? support_r : support_rad = (wall == undef || !round_supp) ? support_r :
min(support_r, floor((supp_d - (inner_d + 2 * wall)) * 5000) / 10000); min(support_r, floor((supp_d - (inner_d + 2 * wall)) * 5000) / 10000);
//Too small of a radius will cause errors with the arc, this limits granularity to .0001mm //Too small of a radius will cause errors with the arc, this limits granularity to .0001mm
support_width = 1 * (heightMagMult > 1 ? heightMagMult : 1) * sign(support_d); support_width = max(heightMagMult,1) * sign(support_d);
roundover = 0.58 * diamMagMult; roundover = 0.58 * diamMagMult;
lip_roundover_r = (roundover > (neck_d - inner_d) / 2) ? 0 : roundover; lip_roundover_r = (roundover > (neck_d - inner_d) / 2) ? 0 : roundover;
h = height + support_width; h = height + support_width;
echo(h=h);
threadbase_d = neck_d - 0.8 * diamMagMult; threadbase_d = neck_d - 0.8 * diamMagMult;
$fn = segs(33 / 2); $fn = segs(33 / 2);
@ -459,7 +461,7 @@ module generic_bottle_neck(
anchors = [ anchors = [
named_anchor("support-ring", [0, 0, 0 - h / 2]) named_anchor("support-ring", [0, 0, 0 - h / 2])
]; ];
attachable(anchor, spin, orient, d1 = neck_d, d2 = 0, l = h, anchors = anchors) { attachable(anchor, spin, orient, d = neck_d, l = h, anchors = anchors) {
down(h / 2) { down(h / 2) {
rotate_extrude(convexity = 10) { rotate_extrude(convexity = 10) {
polygon(turtle( polygon(turtle(
@ -555,7 +557,7 @@ module generic_bottle_cap(
wall = 2, wall = 2,
texture = "none", texture = "none",
height = 11.2, height = 11.2,
thread_od = 28.58, thread_depth = 2.34,
tolerance = .2, tolerance = .2,
neck_od = 25.5, neck_od = 25.5,
flank_angle = 15, flank_angle = 15,
@ -565,11 +567,10 @@ module generic_bottle_cap(
orient = UP orient = UP
) { ) {
$fn = segs(33 / 2); $fn = segs(33 / 2);
threadOuterDTol = thread_od + 2 * tolerance; threadOuterDTol = neck_od + 2*(thread_depth - 0.8) + 2 * tolerance; // WTF; Engineered for consistency with old code, but
w = threadOuterDTol + 2 * wall; w = threadOuterDTol + 2 * wall; // no clue why this was chosen
h = height + wall; h = height + wall;
neckOuterDTol = neck_od + 2 * tolerance; neckOuterDTol = neck_od + 2 * tolerance;
threadDepth = (thread_od - neck_od) / 2 + .8;
diamMagMult = (w > 32.58) ? w / 32.58 : 1; diamMagMult = (w > 32.58) ? w / 32.58 : 1;
heightMagMult = (height > 11.2) ? height / 11.2 : 1; heightMagMult = (height > 11.2) ? height / 11.2 : 1;
@ -596,8 +597,8 @@ module generic_bottle_cap(
} }
difference(){ difference(){
up(wall + pitch / 2) { up(wall + pitch / 2) {
thread_helix(d = neckOuterDTol, pitch = pitch, thread_depth = threadDepth, flank_angle = flank_angle, thread_helix(d = neckOuterDTol, pitch = pitch, thread_depth = thread_depth, flank_angle = flank_angle,
turns = ((height - pitch) / pitch), lead_in = -threadDepth, internal = true, anchor = BOTTOM); turns = ((height - pitch) / pitch), lead_in = -thread_depth, internal = true, anchor = BOTTOM);
} }
} }
} }
@ -626,7 +627,7 @@ function generic_bottle_cap(
// texture = The surface texture of the cap. Valid values are "none", "knurled", or "ribbed". Default: "none" // texture = The surface texture of the cap. Valid values are "none", "knurled", or "ribbed". Default: "none"
// cap_wall = Wall thickness of the cap in mm. // cap_wall = Wall thickness of the cap in mm.
// cap_h = Interior height of the cap in mm. // cap_h = Interior height of the cap in mm.
// cap_thread_od = Outer diameter of cap threads in mm. // cap_thread_depth = Cap thread depth. Default: 2.34
// tolerance = Extra space to add to the outer diameter of threads and neck in mm. Applied to radius. // tolerance = Extra space to add to the outer diameter of threads and neck in mm. Applied to radius.
// cap_neck_od = Inner diameter of the cap threads. // cap_neck_od = Inner diameter of the cap threads.
// cap_neck_id = Inner diameter of the hole through the cap. // cap_neck_id = Inner diameter of the hole through the cap.
@ -640,6 +641,9 @@ function generic_bottle_cap(
// neck_support_od = Outer diameter of neck support ring. Leave undefined to set equal to OD of cap. Set to 0 for no ring. Default: undef // neck_support_od = Outer diameter of neck support ring. Leave undefined to set equal to OD of cap. Set to 0 for no ring. Default: undef
// d = Distance between bottom of neck and top of cap // d = Distance between bottom of neck and top of cap
// taper_lead_in = Length to leave straight before tapering on tube between neck and cap if exists. // taper_lead_in = Length to leave straight before tapering on tube between neck and cap if exists.
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER`
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0`
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP`
// Examples: // Examples:
// bottle_adapter_neck_to_cap(); // bottle_adapter_neck_to_cap();
module bottle_adapter_neck_to_cap( module bottle_adapter_neck_to_cap(
@ -647,7 +651,7 @@ module bottle_adapter_neck_to_cap(
texture = "none", texture = "none",
cap_wall = 2, cap_wall = 2,
cap_h = 11.2, cap_h = 11.2,
cap_thread_od = 28.58, cap_thread_depth = 2.34,
tolerance = .2, tolerance = .2,
cap_neck_od = 25.5, cap_neck_od = 25.5,
cap_neck_id, cap_neck_id,
@ -660,69 +664,79 @@ module bottle_adapter_neck_to_cap(
neck_thread_pitch = 3.2, neck_thread_pitch = 3.2,
neck_support_od, neck_support_od,
d = 0, d = 0,
taper_lead_in = 0 taper_lead_in = 0, anchor, spin,orient
) { ) {
neck_support_od = (neck_support_od == undef || (d == 0 && neck_support_od < cap_thread_od + 2 * tolerance)) ? cap_thread_od + 2 * (cap_wall + tolerance) : neck_support_od; cap_od = cap_neck_od + 2*(cap_thread_depth - 0.8) + 2 * tolerance;
cap_neck_id = (cap_neck_id == undef) ? neck_id : cap_neck_id; neck_support_od = (neck_support_od == undef || (d == 0 && neck_support_od < cap_od)) ? cap_od+2*cap_wall
wall = (wall == undef) ? neck_support_od + neck_d + cap_thread_od + neck_id : wall; : neck_support_od;
cap_neck_id = default(cap_neck_id,neck_id);
wall = default(wall, neck_support_od + neck_d + cap_od + neck_id - 2*tolerance);
echo(wall=wall);
$fn = segs(33 / 2); $fn = segs(33 / 2);
wallt1 = min(wall, (max(neck_support_od, neck_d) - neck_id) / 2); wallt1 = min(wall, (max(neck_support_od, neck_d) - neck_id) / 2);
wallt2 = min(wall, (cap_thread_od + 2 * (cap_wall + tolerance) - cap_neck_id) / 2); wallt2 = min(wall, (cap_od + 2 * cap_wall - cap_neck_id) / 2);
difference(){ top_h = neck_h + max(1,neck_h/17)*sign(neck_support_od);
union(){ echo(top_h=top_h);
up(d / 2) { bot_h = cap_h + cap_wall;
generic_bottle_neck(neck_d = neck_d, attachable(anchor=anchor,orient=orient,spin=spin, r=max([neck_id/2+wallt1, cap_neck_id/2+wallt2, neck_support_od/2]), h=top_h+bot_h+d) {
id = neck_id, zmove((bot_h-top_h)/2)
thread_od = neck_thread_od, difference(){
height = neck_h, union(){
support_d = neck_support_od, up(d / 2) {
pitch = neck_thread_pitch, generic_bottle_neck(neck_d = neck_d,
round_supp = ((wallt1 < (neck_support_od - neck_id) / 2) && (d > 0 || neck_support_od > (cap_thread_od + 2 * (cap_wall + tolerance)))), id = neck_id,
wall = (d > 0) ? wallt1 : min(wallt1, ((cap_thread_od + 2 * (cap_wall + tolerance) - neck_id) / 2)) thread_od = neck_thread_od,
); height = neck_h,
} support_d = neck_support_od,
if (d != 0) { pitch = neck_thread_pitch,
rotate_extrude(){ round_supp = ((wallt1 < (neck_support_od - neck_id) / 2) && (d > 0 || neck_support_od > (cap_thread_od + 2 * (cap_wall + tolerance)))),
polygon(points = [ wall = (d > 0) ? wallt1 : min(wallt1, ((cap_od + 2 * (cap_wall) - neck_id) / 2))
[0, d / 2], );
[neck_id / 2 + wallt1, d / 2], }
[neck_id / 2 + wallt1, d / 2 - taper_lead_in], if (d != 0) {
[cap_neck_id / 2 + wallt2, taper_lead_in - d / 2], rotate_extrude(){
[cap_neck_id / 2 + wallt2, -d / 2], polygon(points = [
[0, -d / 2] [0, d / 2],
]); [neck_id / 2 + wallt1, d / 2],
[neck_id / 2 + wallt1, d / 2 - taper_lead_in],
[cap_neck_id / 2 + wallt2, taper_lead_in - d / 2],
[cap_neck_id / 2 + wallt2, -d / 2],
[0, -d / 2]
]);
}
}
down(d / 2){
generic_bottle_cap(wall = cap_wall,
texture = texture,
height = cap_h,
thread_depth = cap_thread_depth,
tolerance = tolerance,
neck_od = cap_neck_od,
flank_angle = cap_thread_taper,
orient = DOWN,
pitch = cap_thread_pitch
);
} }
} }
down(d / 2){ rotate_extrude() {
generic_bottle_cap(wall = cap_wall, polygon(points = [
texture = texture, [0, d / 2 + 0.1],
height = cap_h, [neck_id / 2, d / 2],
thread_od = cap_thread_od, [neck_id / 2, d / 2 - taper_lead_in],
tolerance = tolerance, [cap_neck_id / 2, taper_lead_in - d / 2],
neck_od = cap_neck_od, [cap_neck_id / 2, -d / 2 - cap_wall],
flank_angle = cap_thread_taper, [0, -d / 2 - cap_wall - 0.1]
orient = DOWN, ]);
pitch = cap_thread_pitch
);
} }
} }
rotate_extrude() { children();
polygon(points = [
[0, d / 2 + 0.1],
[neck_id / 2, d / 2],
[neck_id / 2, d / 2 - taper_lead_in],
[cap_neck_id / 2, taper_lead_in - d / 2],
[cap_neck_id / 2, -d / 2 - cap_wall],
[0, -d / 2 - cap_wall - 0.1]
]);
}
} }
} }
function bottle_adapter_neck_to_cap( function bottle_adapter_neck_to_cap(
wall, texture, cap_wall, cap_h, cap_thread_od, wall, texture, cap_wall, cap_h, cap_thread_depth1,
tolerance, cap_neck_od, cap_neck_id, cap_thread_taper, tolerance, cap_neck_od, cap_neck_id, cap_thread_taper,
cap_thread_pitch, neck_d, neck_id, neck_thread_od, cap_thread_pitch, neck_d, neck_id, neck_thread_od,
neck_h, neck_thread_pitch, neck_support_od, d, taper_lead_in neck_h, neck_thread_pitch, neck_support_od, d, taper_lead_in
@ -734,107 +748,115 @@ function bottle_adapter_neck_to_cap(
// Topics: Bottles, Threading // Topics: Bottles, Threading
// See Also: bottle_adapter_neck_to_cap(), bottle_adapter_neck_to_neck() // See Also: bottle_adapter_neck_to_cap(), bottle_adapter_neck_to_neck()
// Usage: // Usage:
// bottle_adapter_cap_to_cap(wall, [texture]); // bottle_adapter_cap_to_cap(wall, [texture]) [ATTACHMENTS];
// Description: // Description:
// Creates a threaded cap to cap adapter. // Creates a threaded cap to cap adapter.
// Arguments: // Arguments:
// wall = Wall thickness in mm. // wall = Wall thickness in mm.
// texture = The surface texture of the cap. Valid values are "none", "knurled", or "ribbed". Default: "none" // texture = The surface texture of the cap. Valid values are "none", "knurled", or "ribbed". Default: "none"
// cap_h1 = Interior height of top cap. // cap_h1 = Interior height of top cap.
// cap_thread_od1 = Outer diameter of threads on top cap. // cap_thread_depth1 = Thread depth on top cap. Default: 2.34
// tolerance = Extra space to add to the outer diameter of threads and neck in mm. Applied to radius. // tolerance = Extra space to add to the outer diameter of threads and neck in mm. Applied to radius.
// cap_neck_od1 = Inner diameter of threads on top cap. // cap_neck_od1 = Inner diameter of threads on top cap.
// cap_thread_pitch1 = Thread pitch of top cap in mm. // cap_thread_pitch1 = Thread pitch of top cap in mm.
// cap_h2 = Interior height of bottom cap. Leave undefined to duplicate cap_h1. // cap_h2 = Interior height of bottom cap. Leave undefined to duplicate cap_h1.
// cap_thread_od2 = Outer diameter of threads on bottom cap. Leave undefined to duplicate capThread1. // cap_thread_depth2 = Thread depth on bottom cap. Default: same as cap_thread_depth1
// cap_neck_od2 = Inner diameter of threads on top cap. Leave undefined to duplicate cap_neck_od1. // cap_neck_od2 = Inner diameter of threads on top cap. Leave undefined to duplicate cap_neck_od1.
// cap_thread_pitch2 = Thread pitch of bottom cap in mm. Leave undefinced to duplicate cap_thread_pitch1. // cap_thread_pitch2 = Thread pitch of bottom cap in mm. Leave undefinced to duplicate cap_thread_pitch1.
// d = Distance between caps. // d = Distance between caps.
// neck_id1 = Inner diameter of cutout in top cap. // neck_id1 = Inner diameter of cutout in top cap.
// neck_id2 = Inner diameter of cutout in bottom cap. // neck_id2 = Inner diameter of cutout in bottom cap.
// taper_lead_in = Length to leave straight before tapering on tube between caps if exists. // taper_lead_in = Length to leave straight before tapering on tube between caps if exists.
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER`
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0`
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP`
// Examples: // Examples:
// bottle_adapter_cap_to_cap(); // bottle_adapter_cap_to_cap();
module bottle_adapter_cap_to_cap( module bottle_adapter_cap_to_cap(
wall = 2, wall = 2,
texture = "none", texture = "none",
cap_h1 = 11.2, cap_h1 = 11.2,
cap_thread_od1 = 28.58, cap_thread_depth1 = 2.34,
tolerance = .2, tolerance = .2,
cap_neck_od1 = 25.5, cap_neck_od1 = 25.5,
cap_thread_pitch1 = 4, cap_thread_pitch1 = 4,
cap_h2, cap_h2,
cap_thread_od2, cap_thread_depth2,
cap_neck_od2, cap_neck_od2,
cap_thread_pitch2, cap_thread_pitch2,
d = 0, d = 0,
neck_id1, neck_id2, neck_id,
taper_lead_in = 0 taper_lead_in = 0, anchor, spin,orient
) { ) {
cap_h2 = (cap_h2 == undef) ? cap_h1 : cap_h2; cap_h2 = default(cap_h2,cap_h1);
cap_thread_od2 = (cap_thread_od2 == undef) ? cap_thread_od1 : cap_thread_od2; cap_thread_depth2 = default(cap_thread_depth2,cap_thread_depth1);
cap_neck_od2 = (cap_neck_od2 == undef) ? cap_neck_od1 : cap_neck_od2; cap_neck_od2 = default(cap_neck_od2,cap_neck_od1);
cap_thread_pitch2 = (cap_thread_pitch2 == undef) ? cap_thread_pitch1 : cap_thread_pitch2; cap_thread_pitch2 = default(cap_thread_pitch2,cap_thread_pitch1);
neck_id2 = (neck_id2 == undef && neck_id1 != undef) ? neck_id1 : neck_id2;
taper_lead_in = (d >= taper_lead_in * 2) ? taper_lead_in : d / 2; taper_lead_in = (d >= taper_lead_in * 2) ? taper_lead_in : d / 2;
neck_id = min(cap_neck_od1 - cap_thread_depth1, cap_neck_od2-cap_thread_depth2);
top_h = cap_h1+wall;
bot_h = cap_h2+wall;
cap_od1 = cap_neck_od1 + 2*(cap_thread_depth1 - 0.8) + 2 * tolerance; // WTF; Engineered for consistency with old code, but
cap_od2 = cap_neck_od2 + 2*(cap_thread_depth2 - 0.8) + 2 * tolerance; // WTF; Engineered for consistency with old code, but
$fn = segs(33 / 2); $fn = segs(33 / 2);
attachable(anchor=anchor,spin=spin,orient=orient, h=top_h+bot_h+d, d=max(cap_od1,cap_od2)+2*wall){
difference(){ zmove((bot_h-top_h)/2)
union(){ difference(){
up(d / 2){ union(){
generic_bottle_cap( up(d / 2){
orient = UP, generic_bottle_cap(
wall = wall, orient = UP,
texture = texture, wall = wall,
height = cap_h1, texture = texture,
thread_od = cap_thread_od1, height = cap_h1,
tolerance = tolerance, thread_depth = cap_thread_depth1,
neck_od = cap_neck_od1, tolerance = tolerance,
pitch = cap_thread_pitch1 neck_od = cap_neck_od1,
); pitch = cap_thread_pitch1
} );
if (d != 0) { }
rotate_extrude() { if (d != 0) {
polygon(points = [ rotate_extrude() {
[0, d / 2], polygon(points = [
[cap_thread_od1 / 2 + (wall + tolerance), d / 2], [0, d / 2],
[cap_thread_od1 / 2 + (wall + tolerance), d / 2 - taper_lead_in], [cap_od1 / 2 + wall, d / 2],
[cap_thread_od2 / 2 + (wall + tolerance), taper_lead_in - d / 2], [cap_od1 / 2 + wall, d / 2 - taper_lead_in],
[cap_thread_od2 / 2 + (wall + tolerance), -d / 2], [cap_od2 / 2 + wall, taper_lead_in - d / 2],
[0, -d / 2] [cap_od2 / 2 + wall, -d / 2],
]); [0, -d / 2]
} ]);
} }
down(d / 2){ }
generic_bottle_cap( down(d / 2){
orient = DOWN, generic_bottle_cap(
wall = wall, orient = DOWN,
texture = texture, wall = wall,
height = cap_h2, texture = texture,
thread_od = cap_thread_od2, height = cap_h2,
tolerance = tolerance, thread_depth = cap_thread_depth2,
neck_od = cap_neck_od2, tolerance = tolerance,
pitch = cap_thread_pitch2 neck_od = cap_neck_od2,
); pitch = cap_thread_pitch2
} );
} }
if (neck_id1 != undef || neck_id2 != undef) { }
neck_id1 = (neck_id1 == undef) ? neck_id2 : neck_id1; rotate_extrude() {
neck_id2 = (neck_id2 == undef) ? neck_id1 : neck_id2; polygon(points = [
[0, wall + d / 2 + 0.1],
rotate_extrude() { [neck_id / 2, wall + d / 2],
polygon(points = [ [neck_id / 2, wall + d / 2 - taper_lead_in],
[0, wall + d / 2 + 0.1], [neck_id / 2, taper_lead_in - d / 2 - wall],
[neck_id1 / 2, wall + d / 2], [neck_id / 2, -d / 2 - wall],
[neck_id1 / 2, wall + d / 2 - taper_lead_in], [0, -d / 2 - wall - 0.1]
[neck_id2 / 2, taper_lead_in - d / 2 - wall], ]);
[neck_id2 / 2, -d / 2 - wall], }
[0, -d / 2 - wall - 0.1] }
]); children();
}
}
} }
} }
@ -850,7 +872,7 @@ function bottle_adapter_cap_to_cap(
// Topics: Bottles, Threading // Topics: Bottles, Threading
// See Also: bottle_adapter_neck_to_cap(), bottle_adapter_cap_to_cap() // See Also: bottle_adapter_neck_to_cap(), bottle_adapter_cap_to_cap()
// Usage: // Usage:
// bottle_adapter_neck_to_neck(...); // bottle_adapter_neck_to_neck(...) [ATTACHMENTS];
// Description: // Description:
// Creates a threaded neck to neck adapter. // Creates a threaded neck to neck adapter.
// Arguments: // Arguments:
@ -869,7 +891,10 @@ function bottle_adapter_cap_to_cap(
// support_od2 = Outer diameter of the support ring on bottom neck. Set to 0 for no ring. Leave undefined to duplicate support_od1 // support_od2 = Outer diameter of the support ring on bottom neck. Set to 0 for no ring. Leave undefined to duplicate support_od1
// pitch2 = Thread pitch of bottom neck. Leave undefined to duplicate thread_pitch1 // pitch2 = Thread pitch of bottom neck. Leave undefined to duplicate thread_pitch1
// taper_lead_in = Length to leave straight before tapering on tube between necks if exists. // taper_lead_in = Length to leave straight before tapering on tube between necks if exists.
// wall = Thickness of tube wall between necks. Leave undefined to match outer diameters with the neckODs/supportODs. // wall = Thickness of tube wall between necks. Leave undefined to match outer diameters with the neckODs/supportODs.
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER`
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0`
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP`
// Examples: // Examples:
// bottle_adapter_neck_to_neck(); // bottle_adapter_neck_to_neck();
module bottle_adapter_neck_to_neck( module bottle_adapter_neck_to_neck(
@ -882,10 +907,9 @@ module bottle_adapter_neck_to_neck(
thread_pitch1 = 3.2, thread_pitch1 = 3.2,
neck_od2, neck_id2, neck_od2, neck_id2,
thread_od2, height2, thread_od2, height2,
support_od2, pitch2, support_od2, pitch2,
taper_lead_in = 0, wall taper_lead_in = 0, wall, anchor, spin, orient
) { ) {
no_children($children);
neck_od2 = (neck_od2 == undef) ? neck_od1 : neck_od2; neck_od2 = (neck_od2 == undef) ? neck_od1 : neck_od2;
neck_id2 = (neck_id2 == undef) ? neck_id1 : neck_id2; neck_id2 = (neck_id2 == undef) ? neck_id1 : neck_id2;
thread_od2 = (thread_od2 == undef) ? thread_od1 : thread_od2; thread_od2 = (thread_od2 == undef) ? thread_od1 : thread_od2;
@ -903,60 +927,67 @@ module bottle_adapter_neck_to_neck(
taper_lead_in = (d >= taper_lead_in * 2) ? taper_lead_in : d / 2; taper_lead_in = (d >= taper_lead_in * 2) ? taper_lead_in : d / 2;
difference(){ top_h = height1 + max(1,height1/17)*sign(support_od1);
union(){ bot_h = height2 + max(1,height2/17)*sign(support_od2);
up(d / 2){
generic_bottle_neck(orient = UP, attachable(anchor=anchor,orient=orient,spin=spin, h=top_h+bot_h+d, d=max(neck_od1,neck_od2)){
neck_d = neck_od1, zmove((bot_h-top_h)/2)
id = neck_id1, difference(){
thread_od = thread_od1, union(){
height = height1, up(d / 2){
support_d = supprtOD1, generic_bottle_neck(orient = UP,
pitch = thread_pitch1, neck_d = neck_od1,
round_supp = ((wallt1 < (supprtOD1 - neck_id1) / 2) || (support_od1 > max(neck_od2, support_od2) && d == 0)), id = neck_id1,
wall = (d > 0) ? wallt1 : min(wallt1, ((max(neck_od2, support_od2)) - neck_id1) / 2) thread_od = thread_od1,
); height = height1,
} support_d = supprtOD1,
if (d != 0) { pitch = thread_pitch1,
rotate_extrude() { round_supp = ((wallt1 < (supprtOD1 - neck_id1) / 2) || (support_od1 > max(neck_od2, support_od2) && d == 0)),
polygon(points = [ wall = (d > 0) ? wallt1 : min(wallt1, ((max(neck_od2, support_od2)) - neck_id1) / 2)
[0, d / 2], );
[neck_id1 / 2 + wallt1, d / 2], }
[neck_id1 / 2 + wallt1, d / 2 - taper_lead_in], if (d != 0) {
[neck_id2 / 2 + wallt2, taper_lead_in - d / 2], rotate_extrude() {
[neck_id2 / 2 + wallt2, -d / 2], polygon(points = [
[0, -d / 2] [0, d / 2],
]); [neck_id1 / 2 + wallt1, d / 2],
} [neck_id1 / 2 + wallt1, d / 2 - taper_lead_in],
} [neck_id2 / 2 + wallt2, taper_lead_in - d / 2],
down(d / 2){ [neck_id2 / 2 + wallt2, -d / 2],
generic_bottle_neck(orient = DOWN, [0, -d / 2]
neck_d = neck_od2, ]);
id = neck_id2, }
thread_od = thread_od2, }
height = height2, down(d / 2){
support_d = supprtOD2, generic_bottle_neck(orient = DOWN,
pitch = pitch2, neck_d = neck_od2,
round_supp = ((wallt2 < (supprtOD2 - neck_id2) / 2) || (support_od2 > max(neck_od1, support_od1) && d == 0)), id = neck_id2,
wall = (d > 0) ? wallt2 : min(wallt2, ((max(neck_od1, support_od1)) - neck_id2) / 2) thread_od = thread_od2,
); height = height2,
} support_d = supprtOD2,
} pitch = pitch2,
if (neck_id1 != undef || neck_id2 != undef) { round_supp = ((wallt2 < (supprtOD2 - neck_id2) / 2) || (support_od2 > max(neck_od1, support_od1) && d == 0)),
neck_id1 = (neck_id1 == undef) ? neck_id2 : neck_id1; wall = (d > 0) ? wallt2 : min(wallt2, ((max(neck_od1, support_od1)) - neck_id2) / 2)
neck_id2 = (neck_id2 == undef) ? neck_id1 : neck_id2; );
}
}
if (neck_id1 != undef || neck_id2 != undef) {
neck_id1 = (neck_id1 == undef) ? neck_id2 : neck_id1;
neck_id2 = (neck_id2 == undef) ? neck_id1 : neck_id2;
rotate_extrude() { rotate_extrude() {
polygon(points = [ polygon(points = [
[0, d / 2], [0, d / 2],
[neck_id1 / 2, d / 2], [neck_id1 / 2, d / 2],
[neck_id1 / 2, d / 2 - taper_lead_in], [neck_id1 / 2, d / 2 - taper_lead_in],
[neck_id2 / 2, taper_lead_in - d / 2], [neck_id2 / 2, taper_lead_in - d / 2],
[neck_id2 / 2, -d / 2], [neck_id2 / 2, -d / 2],
[0, -d / 2] [0, -d / 2]
]); ]);
} }
} }
}
children();
} }
} }

View file

@ -85,15 +85,19 @@ module bounding_box(excess=0, planar=false) {
} }
} }
if(planar) { req_children($children);
offset(excess-1/2) _oversize_bbox() children(); attachable(){
} else { if(planar) {
render(convexity=2) offset(excess-1/2) _oversize_bbox() children();
if (excess>.1) { } else {
_oversize_bbox() children(); render(convexity=2)
} else { if (excess>.1) {
_shrink_cube() _oversize_bbox() children(); _oversize_bbox() children();
} } else {
_shrink_cube() _oversize_bbox() children();
}
}
union();
} }
} }
@ -137,10 +141,12 @@ module bounding_box(excess=0, planar=false) {
// } // }
module chain_hull() module chain_hull()
{ {
union() { req_children($children);
attachable(){
if ($children == 1) { if ($children == 1) {
children(); children();
} else if ($children > 1) { }
else {
for (i =[1:1:$children-1]) { for (i =[1:1:$children-1]) {
$idx = i; $idx = i;
hull() { hull() {
@ -149,6 +155,7 @@ module chain_hull()
} }
} }
} }
union();
} }
} }
@ -203,6 +210,7 @@ module chain_hull()
// move_copies([[-3.5,1.5],[0.0,3.0],[3.5,1.5]]) // move_copies([[-3.5,1.5],[0.0,3.0],[3.5,1.5]])
// circle(r=1.5); // circle(r=1.5);
module path_extrude2d(path, caps=false, closed=false, s, convexity=10) { module path_extrude2d(path, caps=false, closed=false, s, convexity=10) {
req_children($children);
extra_ang = 0.1; // Extra angle for overlap of joints extra_ang = 0.1; // Extra angle for overlap of joints
check = check =
assert(caps==false || closed==false, "Cannot have caps on a closed extrusion") assert(caps==false || closed==false, "Cannot have caps on a closed extrusion")
@ -213,58 +221,63 @@ module path_extrude2d(path, caps=false, closed=false, s, convexity=10) {
norm(b[1]-b[0]); norm(b[1]-b[0]);
check2 = assert(is_finite(s)); check2 = assert(is_finite(s));
L = len(path); L = len(path);
for (i = [0:1:L-(closed?1:2)]) { attachable(){
seg = select(path, i, i+1); union(){
segv = seg[1] - seg[0]; for (i = [0:1:L-(closed?1:2)]) {
seglen = norm(segv); seg = select(path, i, i+1);
translate((seg[0]+seg[1])/2) { segv = seg[1] - seg[0];
rot(from=BACK, to=segv) { seglen = norm(segv);
difference() { translate((seg[0]+seg[1])/2) {
xrot(90) { rot(from=BACK, to=segv) {
linear_extrude(height=seglen, center=true, convexity=convexity) { difference() {
children(); xrot(90) {
linear_extrude(height=seglen, center=true, convexity=convexity) {
children();
}
}
if (closed || i>0) {
pt = select(path, i-1);
pang = v_theta(rot(from=-segv, to=RIGHT, p=pt - seg[0]));
fwd(seglen/2+0.01) zrot(pang/2) cube(s, anchor=BACK);
}
if (closed || i<L-2) {
pt = select(path, i+2);
pang = v_theta(rot(from=segv, to=RIGHT, p=pt - seg[1]));
back(seglen/2+0.01) zrot(pang/2) cube(s, anchor=FWD);
} }
}
if (closed || i>0) {
pt = select(path, i-1);
pang = v_theta(rot(from=-segv, to=RIGHT, p=pt - seg[0]));
fwd(seglen/2+0.01) zrot(pang/2) cube(s, anchor=BACK);
}
if (closed || i<L-2) {
pt = select(path, i+2);
pang = v_theta(rot(from=segv, to=RIGHT, p=pt - seg[1]));
back(seglen/2+0.01) zrot(pang/2) cube(s, anchor=FWD);
} }
} }
} }
} }
} for (t=triplet(path,wrap=closed)) {
for (t=triplet(path,wrap=closed)) { ang = -(180-vector_angle(t)) * sign(_point_left_of_line2d(t[2],[t[0],t[1]]));
ang = -(180-vector_angle(t)) * sign(_point_left_of_line2d(t[2],[t[0],t[1]])); delt = point3d(t[2] - t[1]);
delt = point3d(t[2] - t[1]); if (ang!=0)
if (ang!=0) translate(t[1]) {
translate(t[1]) { frame_map(y=delt, z=UP)
frame_map(y=delt, z=UP) rotate(-sign(ang)*extra_ang/2)
rotate(-sign(ang)*extra_ang/2) rotate_extrude(angle=ang+sign(ang)*extra_ang)
rotate_extrude(angle=ang+sign(ang)*extra_ang) if (ang<0)
if (ang<0) right_half(planar=true) children();
right_half(planar=true) children(); else
else left_half(planar=true) children();
left_half(planar=true) children(); }
}
}
} if (caps) {
if (caps) { bseg = select(path,0,1);
bseg = select(path,0,1); move(bseg[0])
move(bseg[0]) rot(from=BACK, to=bseg[0]-bseg[1])
rot(from=BACK, to=bseg[0]-bseg[1]) rotate_extrude(angle=180)
rotate_extrude(angle=180) right_half(planar=true) children();
right_half(planar=true) children(); eseg = select(path,-2,-1);
eseg = select(path,-2,-1); move(eseg[1])
move(eseg[1]) rot(from=BACK, to=eseg[1]-eseg[0])
rot(from=BACK, to=eseg[1]-eseg[0]) rotate_extrude(angle=180)
rotate_extrude(angle=180) right_half(planar=true) children();
right_half(planar=true) children(); }
}
union();
} }
} }
@ -298,6 +311,7 @@ module path_extrude2d(path, caps=false, closed=false, s, convexity=10) {
// cylindrical_extrude(or=40, ir=35, orient=BACK) // cylindrical_extrude(or=40, ir=35, orient=BACK)
// text(text="Hello World!", size=10, halign="center", valign="center"); // text(text="Hello World!", size=10, halign="center", valign="center");
module cylindrical_extrude(ir, or, od, id, size=1000, convexity=10, spin=0, orient=UP) { module cylindrical_extrude(ir, or, od, id, size=1000, convexity=10, spin=0, orient=UP) {
req_children($children);
check1 = assert(is_num(size) || is_vector(size,2)); check1 = assert(is_num(size) || is_vector(size,2));
size = is_num(size)? [size,size] : size; size = is_num(size)? [size,size] : size;
ir = get_radius(r=ir,d=id); ir = get_radius(r=ir,d=id);
@ -310,23 +324,26 @@ module cylindrical_extrude(ir, or, od, id, size=1000, convexity=10, spin=0, orie
sides = segs(or); sides = segs(or);
step = circumf / sides; step = circumf / sides;
steps = ceil(width / step); steps = ceil(width / step);
rot(from=UP, to=orient) rot(spin) { attachable() {
for (i=[0:1:steps-2]) { rot(from=UP, to=orient) rot(spin) {
x = (i+0.5-steps/2) * step; for (i=[0:1:steps-2]) {
zrot(360 * x / circumf) { x = (i+0.5-steps/2) * step;
fwd(or*cos(180/sides)) { zrot(360 * x / circumf) {
xrot(-90) { fwd(or*cos(180/sides)) {
linear_extrude(height=or-ir, scale=[ir/or,1], center=false, convexity=convexity) { xrot(-90) {
yflip() linear_extrude(height=or-ir, scale=[ir/or,1], center=false, convexity=convexity) {
intersection() { yflip()
left(x) children(); intersection() {
rect([quantup(step,pow(2,-15)),size.y]); left(x) children();
} rect([quantup(step,pow(2,-15)),size.y]);
} }
} }
} }
} }
} }
}
}
union();
} }
} }
@ -353,6 +370,7 @@ module cylindrical_extrude(ir, or, od, id, size=1000, convexity=10, spin=0, orie
// xcopies(3) circle(3, $fn=32); // xcopies(3) circle(3, $fn=32);
// } // }
module extrude_from_to(pt1, pt2, convexity, twist, scale, slices) { module extrude_from_to(pt1, pt2, convexity, twist, scale, slices) {
req_children($children);
check = check =
assert(is_vector(pt1),"First point must be a vector") assert(is_vector(pt1),"First point must be a vector")
assert(is_vector(pt2),"Second point must be a vector"); assert(is_vector(pt2),"Second point must be a vector");
@ -391,6 +409,7 @@ module extrude_from_to(pt1, pt2, convexity, twist, scale, slices) {
// path = [ [0, 0, 0], [33, 33, 33], [66, 33, 40], [100, 0, 0], [150,0,0] ]; // path = [ [0, 0, 0], [33, 33, 33], [66, 33, 40], [100, 0, 0], [150,0,0] ];
// path_extrude(path) circle(r=10, $fn=6); // path_extrude(path) circle(r=10, $fn=6);
module path_extrude(path, convexity=10, clipsize=100) { module path_extrude(path, convexity=10, clipsize=100) {
req_children($children);
rotmats = cumprod([ rotmats = cumprod([
for (i = idx(path,e=-2)) let( for (i = idx(path,e=-2)) let(
vec1 = i==0? UP : unit(path[i]-path[i-1], UP), vec1 = i==0? UP : unit(path[i]-path[i-1], UP),
@ -401,32 +420,35 @@ module path_extrude(path, convexity=10, clipsize=100) {
interp = rot_resample(rotmats,n=2,method="count"); interp = rot_resample(rotmats,n=2,method="count");
epsilon = 0.0001; // Make segments ever so slightly too long so they overlap. epsilon = 0.0001; // Make segments ever so slightly too long so they overlap.
ptcount = len(path); ptcount = len(path);
for (i = [0:1:ptcount-2]) { attachable(){
pt1 = path[i]; for (i = [0:1:ptcount-2]) {
pt2 = path[i+1]; pt1 = path[i];
dist = norm(pt2-pt1); pt2 = path[i+1];
T = rotmats[i]; dist = norm(pt2-pt1);
difference() { T = rotmats[i];
translate(pt1) { difference() {
multmatrix(T) { translate(pt1) {
down(clipsize/2/2) { multmatrix(T) {
if ((dist+clipsize/2) > 0) { down(clipsize/2/2) {
linear_extrude(height=dist+clipsize/2, convexity=convexity) { if ((dist+clipsize/2) > 0) {
children(); linear_extrude(height=dist+clipsize/2, convexity=convexity) {
} children();
} }
} }
} }
} }
translate(pt1) { }
hq = (i > 0)? interp[2*i-1] : T; translate(pt1) {
multmatrix(hq) down(clipsize/2+epsilon) cube(clipsize, center=true); hq = (i > 0)? interp[2*i-1] : T;
} multmatrix(hq) down(clipsize/2+epsilon) cube(clipsize, center=true);
translate(pt2) { }
hq = (i < ptcount-2)? interp[2*i+1] : T; translate(pt2) {
multmatrix(hq) up(clipsize/2+epsilon) cube(clipsize, center=true); hq = (i < ptcount-2)? interp[2*i+1] : T;
} multmatrix(hq) up(clipsize/2+epsilon) cube(clipsize, center=true);
} }
}
}
union();
} }
} }
@ -459,17 +481,21 @@ module path_extrude(path, convexity=10, clipsize=100) {
// sphere(r=10); // sphere(r=10);
// } // }
module minkowski_difference(planar=false) { module minkowski_difference(planar=false) {
difference() { req_children($children);
bounding_box(excess=0, planar=planar) children(0); attachable(){
render(convexity=20) { difference() {
minkowski() { bounding_box(excess=0, planar=planar) children(0);
difference() { render(convexity=20) {
bounding_box(excess=1, planar=planar) children(0); minkowski() {
children(0); difference() {
} bounding_box(excess=1, planar=planar) children(0);
for (i=[1:1:$children-1]) children(i); children(0);
} }
} for (i=[1:1:$children-1]) children(i);
}
}
}
union();
} }
} }
@ -491,29 +517,33 @@ module minkowski_difference(planar=false) {
// size = Maximum size of object to be contracted, given as a scalar. Default: 100 // size = Maximum size of object to be contracted, given as a scalar. Default: 100
// convexity = Max number of times a line could intersect the walls of the object. Default: 10 // convexity = Max number of times a line could intersect the walls of the object. Default: 10
module offset3d(r, size=100, convexity=10) { module offset3d(r, size=100, convexity=10) {
req_children($children);
n = quant(max(8,segs(abs(r))),4); n = quant(max(8,segs(abs(r))),4);
if (r==0) { attachable(){
children(); if (r==0) {
} else if (r>0) { children();
render(convexity=convexity) } else if (r>0) {
minkowski() { render(convexity=convexity)
children(); minkowski() {
sphere(r, $fn=n); children();
} sphere(r, $fn=n);
} else { }
size2 = size * [1,1,1]; } else {
size1 = size2 * 1.02; size2 = size * [1,1,1];
render(convexity=convexity) size1 = size2 * 1.02;
difference() { render(convexity=convexity)
cube(size2, center=true); difference() {
minkowski() { cube(size2, center=true);
difference() { minkowski() {
cube(size1, center=true); difference() {
children(); cube(size1, center=true);
} children();
sphere(-r, $fn=n); }
} sphere(-r, $fn=n);
} }
}
}
union();
} }
} }
@ -540,12 +570,16 @@ module offset3d(r, size=100, convexity=10) {
// ir = Radius to round only inside (concave) corners to. Use instead of `r`. // ir = Radius to round only inside (concave) corners to. Use instead of `r`.
module round3d(r, or, ir, size=100) module round3d(r, or, ir, size=100)
{ {
req_children($children);
or = get_radius(r1=or, r=r, dflt=0); or = get_radius(r1=or, r=r, dflt=0);
ir = get_radius(r1=ir, r=r, dflt=0); ir = get_radius(r1=ir, r=r, dflt=0);
offset3d(or, size=size) attachable(){
offset3d(-ir-or, size=size) offset3d(or, size=size)
offset3d(ir, size=size) offset3d(-ir-or, size=size)
children(); offset3d(ir, size=size)
children();
union();
}
} }

View file

@ -329,37 +329,38 @@ module regular_polyhedron(
faces = entry[3]; faces = entry[3];
face_normals = entry[4]; face_normals = entry[4];
in_radius = entry[5]; in_radius = entry[5];
if (draw){ translate(translation){
if (rounding==0) if (draw){
polyhedron(move(translation, p=scaled_points), faces = face_triangles); if (rounding==0)
else { polyhedron(scaled_points, faces = face_triangles);
fn = segs(rounding); else {
rounding = rounding/cos(180/fn); fn = segs(rounding);
adjusted_scale = 1 - rounding / in_radius; rounding = rounding/cos(180/fn);
minkowski(){ adjusted_scale = 1 - rounding / in_radius;
sphere(r=rounding, $fn=fn); minkowski(){
polyhedron(move(translation,p=adjusted_scale*scaled_points), faces = face_triangles); sphere(r=rounding, $fn=fn);
polyhedron(adjusted_scale*scaled_points, faces = face_triangles);
}
} }
} }
} if ($children>0) {
translate(translation) maxrange = repeat ? len(faces)-1 : $children-1;
if ($children>0) { for(i=[0:1:maxrange]) {
maxrange = repeat ? len(faces)-1 : $children-1; // Would like to orient so an edge (longest edge?) is parallel to x axis
for(i=[0:1:maxrange]) { facepts = select(scaled_points, faces[i]);
// Would like to orient so an edge (longest edge?) is parallel to x axis $center = -mean(facepts);
facepts = select(scaled_points, faces[i]); cfacepts = move($center, p=facepts);
$center = -mean(facepts); $face = rotate_children
cfacepts = move($center, p=facepts); ? path2d(frame_map(z=face_normals[i], x=facepts[0]-facepts[1], reverse=true, p=cfacepts))
$face = rotate_children : cfacepts;
? path2d(frame_map(z=face_normals[i], x=facepts[0]-facepts[1], reverse=true, p=cfacepts)) $faceindex = i;
: cfacepts; translate(-$center)
$faceindex = i; if (rotate_children) {
translate(-$center) frame_map(z=face_normals[i], x=facepts[0]-facepts[1])
if (rotate_children) { children(i % $children);
frame_map(z=face_normals[i], x=facepts[0]-facepts[1]) } else {
children(i % $children); children(i % $children);
} else { }
children(i % $children);
} }
} }
} }