Merge pull request #1037 from adrianVmariano/master

slice bug fix
This commit is contained in:
Revar Desmera 2023-02-03 12:08:41 -08:00 committed by GitHub
commit 495c761461
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 569 additions and 249 deletions

View file

@ -11,6 +11,46 @@
//////////////////////////////////////////////////////////////////////
// Section: Adaptive Children Using `$` Variables
// The distributor methods create multiple copies of their children and place them in various ways. While there are many occasions where
// a model demands multiple identical copies of an object, this framework is more powerful than
// might be immediately obvious because of `$` variables. The distributors set `$` variables that the children can use to change their
// behavior from one child to the next within a single distributor invocation. This means the copies need not be identical.
// The {{xcopies()}} module sets `$idx` to the index number of the copy, and in the examples below we use `$idx`, but the various
// distributors offer a variety of `$` variables that you can use in your children. Check the "Side Effects" section for each module
// to learn what variables that module provides.
// .
// Two gotchas may lead to models that don't behave as expected. While `if` statements work to control modules, you cannot
// use them to make variable assignments in your child object. If you write a statement like
// `if (condition) { c="red";}` then the `c` variable is set only in the scope of the `if` statement and is not available later on.
// Instead you must use the ternary operator. The second complication is
// that in OpenSCAD version 2021.01 and earlier, assignments in children were executed before their
// parent. This means that `$` variables like `$idx` are not available in assignments, so if you use them you will get a warning about an unknown variable.
// Two workarounds exist, neither of which are needed in newer versions of OpenSCAD. The workarounds solve the problem because
// **modules** execute after their parent, so the `$` variables **are** available in modules. You can put your assignments
// in a `let()` module, or you can wrap your child in a `union()`. Both methods appear below in the examples.
// Example(2D): This example shows how we can use `$idx` to produce **different** geometry at each index.
// xcopies(n=10, spacing=10)
// text(str($idx));
// Example(2D): Here the children are sometimes squares and sometimes circles as determined by the conditional `if` module. This use of `if` is OK because no variables are assigned.
// xcopies(n=4, spacing=10)
// if($idx%2==0) circle(r=3,$fn=16);
// else rect(6);
// Example(2D): Suppose we would like to color odd and even index copies differently. In this example we compute the color for a given child from `$idx` using the ternary operator. The `let()` module is a module that sets variables and makes them available to its children. Note that multiple assignments in `let()` are separated by commas, not semicolons.
// xcopies(n=6, spacing=10){
// let(c = $idx % 2 == 0 ? "red" : "green")
// color(c) rect(6);
// }
// Example(2D): This example shows how you can change the position of children adaptively. If you want to avoid repeating your code for each case, this requires storing a transformation matrix in a variable and then applying it using `multmatrix()`. We wrap our code in `union()` to ensure that it works in OpenSCAD 2021.01.
// xcopies(n=5,spacing=10)
// union()
// {
// shiftback = $idx%2==0 ? back(5) : IDENT;
// spin = zrot(180*$idx/4);
// multmatrix(shiftback*spin) stroke([[-4,0],[4,0]],endcap2="arrow2",width=1/2);
// }
//////////////////////////////////////////////////////////////////////
// Section: Translating copies of all the children
//////////////////////////////////////////////////////////////////////
@ -96,7 +136,7 @@ function move_copies(a=[[0,0,0]],p=_NO_ARG) =
// spacing = Given a scalar, specifies a uniform spacing between copies. Given a list of scalars, each one gives a specific position along the line. (Default: 1.0)
// n = Number of copies to place. (Default: 2)
// l = Length to place copies over.
// sp = If given as a point, copies will be placed on a line to the right of starting position `sp`. If given as a scalar, copies will be placed on a line to the right of starting position `[sp,0,0]`. If not given, copies will be placed along a line that is centered at [0,0,0].
// sp = If given as a point, copies will be placed on a line to the right of starting position `sp`. If given as a scalar, copies will be placed on a line segment to the right of starting position `[sp,0,0]`. If not given, copies will be placed along a line segment that is centered at [0,0,0].
// p = Either a point, pointlist, VNF or Bezier patch to be translated when used as a function.
//
// Side Effects:
@ -119,6 +159,8 @@ function move_copies(a=[[0,0,0]],p=_NO_ARG) =
// xcopies([1,2,3,5,7]) sphere(d=1);
module xcopies(spacing, n, l, sp)
{
assert(is_undef(n) || num_defined([l,spacing])==1, "When n is given must give exactly one of spacing or l")
assert(is_def(n) || num_defined([l,spacing])>=1, "When n is not given must give at least one of spacing or l")
req_children($children);
dir = RIGHT;
sp = is_finite(sp)? (sp*dir) : sp;
@ -141,6 +183,8 @@ module xcopies(spacing, n, l, sp)
function xcopies(spacing, n, l, sp, p=_NO_ARG) =
assert(is_undef(n) || num_defined([l,spacing])==1, "When n is given must give exactly one of spacing or l")
assert(is_def(n) || num_defined([l,spacing])>=1, "When n is not given must give at least one of spacing or l")
let(
dir = RIGHT,
sp = is_finite(sp)? (sp*dir) : sp,
@ -201,6 +245,8 @@ function xcopies(spacing, n, l, sp, p=_NO_ARG) =
// ycopies([1,2,3,5,7]) sphere(d=1);
module ycopies(spacing, n, l, sp)
{
assert(is_undef(n) || num_defined([l,spacing])==1, "When n is given must give exactly one of spacing or l")
assert(is_def(n) || num_defined([l,spacing])>=1, "When n is not given must give at least one of spacing or l")
req_children($children);
dir = BACK;
sp = is_finite(sp)? (sp*dir) : sp;
@ -223,6 +269,8 @@ module ycopies(spacing, n, l, sp)
function ycopies(spacing, n, l, sp, p=_NO_ARG) =
assert(is_undef(n) || num_defined([l,spacing])==1, "When n is given must give exactly one of spacing or l")
assert(is_def(n) || num_defined([l,spacing])>=1, "When n is not given must give at least one of spacing or l")
let(
dir = BACK,
sp = is_finite(sp)? (sp*dir) : sp,
@ -297,6 +345,8 @@ function ycopies(spacing, n, l, sp, p=_NO_ARG) =
// zcopies([1,2,3,5,7]) sphere(d=1);
module zcopies(spacing, n, l, sp)
{
assert(is_undef(n) || num_defined([l,spacing])==1, "When n is given must give exactly one of spacing or l")
assert(is_def(n) || num_defined([l,spacing])>=1, "When n is not given must give at least one of spacing or l")
req_children($children);
dir = UP;
sp = is_finite(sp)? (sp*dir) : sp;
@ -319,6 +369,8 @@ module zcopies(spacing, n, l, sp)
function zcopies(spacing, n, l, sp, p=_NO_ARG) =
assert(is_undef(n) || num_defined([l,spacing])==1, "When n is given must give exactly one of spacing or l")
assert(is_def(n) || num_defined([l,spacing])>=1, "When n is not given must give at least one of spacing or l")
let(
dir = UP,
sp = is_finite(sp)? (sp*dir) : sp,
@ -441,18 +493,22 @@ function line_copies(spacing, n, l, p1, p2, p=_NO_ARG) =
assert(is_undef(l) || is_finite(l) || is_vector(l))
assert(is_undef(p1) || is_vector(p1))
assert(is_undef(p2) || is_vector(p2))
assert(is_undef(p2) || is_def(p1), "If p2 is given must also give p1")
assert(is_undef(p2) || is_undef(l), "Cannot give both p2 and l")
assert(is_undef(n) || num_defined([l,spacing,p2])==1,"If n is given then must give exactly one of 'l', 'spacing', or the 'p1'/'p2' pair")
assert(is_def(n) || num_defined([l,spacing,p2])>=1,"If n is given then must give at least one of 'l', 'spacing', or the 'p1'/'p2' pair")
let(
ll = !is_undef(l)? scalar_vec3(l, 0) :
(!is_undef(spacing) && !is_undef(n))? ((n-1) * scalar_vec3(spacing, 0)) :
(!is_undef(p1) && !is_undef(p2))? point3d(p2-p1) :
undef,
cnt = !is_undef(n)? n :
(!is_undef(spacing) && !is_undef(ll))? floor(norm(ll) / norm(scalar_vec3(spacing, 0)) + 1.000001) :
2,
spc = cnt<=1? [0,0,0] :
is_undef(spacing)? (ll/(cnt-1)) :
is_num(spacing) && !is_undef(ll)? (ll/(cnt-1)) :
scalar_vec3(spacing, 0)
ll = is_def(l)? scalar_vec3(l, 0)
: is_def(spacing) && is_def(n)? (n-1) * scalar_vec3(spacing, 0)
: is_def(p1) && is_def(p2)? point3d(p2-p1)
: undef,
cnt = is_def(n)? n
: is_def(spacing) && is_def(ll) ? floor(norm(ll) / norm(scalar_vec3(spacing, 0)) + 1.000001)
: 2,
spc = cnt<=1? [0,0,0]
: is_undef(spacing) && is_def(ll)? ll/(cnt-1)
: is_num(spacing) && is_def(ll)? (ll/(cnt-1))
: scalar_vec3(spacing, 0)
)
assert(!is_undef(cnt), "Need two of `spacing`, 'l', 'n', or `p1`/`p2` arguments in `line_copies()`.")
let( spos = !is_undef(p1)? point3d(p1) : -(cnt-1)/2 * spc )

449
hinges.scad Normal file
View file

@ -0,0 +1,449 @@
//////////////////////////////////////////////////////////////////////
// LibFile: hinges.scad
// Functions and modules for creating hinges and snap-locking hinged parts.
// Includes:
// include <BOSL2/std.scad>
// include <BOSL2/hinges.scad>
// FileGroup: Parts
// FileSummary: Hinges and snap-locking hinged parts.
//////////////////////////////////////////////////////////////////////
include <rounding.scad>
include <screws.scad>
// Section: Hinges
// Module: knuckle_hinge()
// Usage:
// knuckle_hinge(length, offset, segs, [inner], [arm_height=], [arm_angle=], [fill=], [clear_top=], [gap=], [round_top=], [round_bot=], [knuckle_diam=], [pin_diam=], [pin_fn=], [anchor=], [spin=], [orient=]) [ATTACHMENTS];
// Description:
// Construct standard knuckle hinge in two parts using a hinge pin that must be separately supplied. The default is configured to use a piece of 1.75 mm filament as the hinge pin,
// but you can select any dimensions you like to use a screw or other available pin material. The BOTTOM of the hinge is its mount point, which is aligned with
// the hinge pin centersurface, and the hinge pin hole is the CENTER of the hinge.
// The offset is the distance from a vertical mounting point to the center of the hinge pin. The hinge barrel is held by an angled support and
// vertical support. The length of the angled support is determined by its angle and the offset. You specify the length of the vertical support with the
// arm_height parameter.
// Figure(2D,NoScales): The basic hinge form appears on the left. If fill is set to true the gap between the mount surface and hinge arm is filled as shown on the right.
// _hinge_profile(4, 5, $fn=32, fill=false);
// right(13)_hinge_profile(4, 5, $fn=32, fill=true);
// fwd(9)stroke([[0,0],[4,4],[4,9]], width=.3,color="black");
// stroke([[5,-5],[5,0]], endcaps="arrow2", color="blue",width=.15);
// color("blue"){move([6.2,-2.5])text("arm_height",size=.75,valign="center");
// stroke(arc(r=3, cp=[0,-9], angle=[47,90],$fn=64),width=.15,endcaps="arrow2");
// move([-.5,-6])text("arm_angle", size=0.75,halign="right");
// move([14,-4])text("fill=true", size=1);
// }
// Continues:
// As shown in the above figure, the fill option fills the gap between the hinge arm and the mount surface to make a stronger connection. When the
// arm height is set to zero, only a single segment connects the hinge barrel to the mount surface.
// Figure(2D,NoScales): Zero arm height with 45 deg arm
// right(10) _hinge_profile(4, 0, $fn=32);
// _hinge_profile(4, 0, $fn=32,fill=false);
// right(11)fwd(-3)color("blue")text("fill=true",size=1);
// right(.5)fwd(-3)color("blue")text("fill=false",size=1);
// Continues:
// Figure(2D,NoScales): Zero arm height with 90 deg arm. The clear_top parameter removes the hinge support material that is above the x axis
// _hinge_profile(4, 0, 90, $fn=32);
// right(10) _hinge_profile(4, 0, 90, $fn=32,clear_top=true);
// right(9.5)fwd(-3)color("blue")text("clear_top=true",size=.76);
// right(.5)fwd(-3)color("blue")text("clear_top=false",size=.76);
// Continues:
// For 3D printability, you may want to make the hinge pin hole octagonal. To do this without
// changing the other parts of the design, set `pin_fn=8`. You can round off the joint to the
// mount surface with `round_top` and `round_bot`. You specify the amount of thickness to add.
// If you make this parameter too large you will get an error that the rounding doesn't fit.
// The default pin hole size admits a piece of 1.75 mm filament. If you prefer to use a machine
// screw you can set the pin_diam to a screw specification like `"M3"` or "#6". In this case,
// a clearance hole is created through most of the hinge with a self-tap hole for the last segment.
// If the last segment is very long you may shrink the self-tap portion using the tap_depth parameter.
// The pin hole diameter is enlarged by the `2*$slop` for numerically specified holes.
// Screw holes are made using {{screw_hole()} which enlarges the hole by `4*$slop`.
// Arguments:
// length = total length of the entire hinge
// offset = horizontal offset of the hinge pin center from the mount point
// segs = number of hinge segments
// inner = set to true for the "inner" hinge. Default: false
// ---
// arm_height = vertical height of the arm that holds the hinge barrel. Default: 0
// arm_angle = angle of the arm down from the vertical. Default: 45
// fill = if true fill in space between arm and mount surface. Default: true
// clear_top = if true remove any excess arm geometry that appears above the top of the mount surface. Default: false
// gap = gap between hinge segments. Default: 0.2
// round_top = rounding amount to add where top of hinge arm joins the mount surface. Generally only useful when fill=false. Default: 0
// round_bot = rounding amount to add where bottom of hinge arm joins the mount surface. Default: 0
// knuckle_diam = diameter of hinge barrel. Default: 4
// pin_diam = diameter of hinge pin hole as a number of screw specification. Default: 1.75
// screw_head = screw head to use for countersink
// screw_tolerance = screw hole tolerance. Default: "close"
// tap_depth = Don't make the tapped part of the screw hole larger than this.
// $slop = increases pin hole diameter
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `BOTTOM`
// 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`
// Example: Basic hinge, inner=false in front and inner=true in the back
// $fn=32;
// ydistribute(30){
// knuckle_hinge(length=35, segs=4, offset=3, arm_height=1);
// knuckle_hinge(length=35, segs=4, offset=3, arm_height=1,inner=true);
// }
// Example(NoScales): Basic hinge, mounted. Odd segment count means the "outside" hinge is on the outside at both ends.
// $fn=32;
// cuboid([2,40,15])
// position(TOP+RIGHT) orient(anchor=RIGHT)
// knuckle_hinge(length=35, segs=9, offset=3, arm_height=1);
// Example(NoScales): Corresponding inner hinge to go with previous example. Note that the total number of hinge segments adds to the 9 specified.
// $fn=32;
// cuboid([2,40,15])
// position(TOP+RIGHT) orient(anchor=RIGHT)
// knuckle_hinge(length=35, segs=9, offset=3, arm_height=1, inner=true);
// Example(NoScales): This example shows how to position and orient the hinge onto the front of an object instead of the right side.
// $fn=32;
// cuboid([40,2,15])
// position(TOP+FRONT) orient(anchor=FWD)
// knuckle_hinge(length=35, segs=9, offset=3, arm_height=1);
// Example(NoScales): Hinge with round_bot set to create a smooth transition, but octagonal hinge pin holes for printing
// $fn=32;
// cuboid([2,40,15])
// position(TOP+RIGHT) orient(anchor=RIGHT)
// knuckle_hinge(length=35, segs=9, offset=3, arm_height=1,
// round_bot=1, pin_fn=8);
// Example(NoScales): Hinge with no vertical arm, just angled arm
// $fn=32;
// cuboid([2,40,15])
// position(TOP+RIGHT) orient(anchor=RIGHT)
// knuckle_hinge(length=35, segs=9, offset=3, pin_fn=8);
// Example(NoScales): Setting the arm_angle to a large value like 90 produces a hinge that doesn't look great
// $fn=32;
// cuboid([2,40,15])
// position(TOP+RIGHT) orient(anchor=RIGHT)
// knuckle_hinge(length=35, segs=9, offset=3, arm_angle=90,
// arm_height=0, pin_fn=8);
// Example(NoScales): The above hinge is improved with clear_top, which allows nice attachment to a shape half the thickness of the hinge barrel
// $fn=32;
// cuboid([20,40,2])
// position(TOP+RIGHT) orient(anchor=RIGHT)
// knuckle_hinge(length=35, segs=9, offset=3, arm_height=0,
// arm_angle=90, pin_fn=8, clear_top=true);
// Example(NoScales): Uneven hinge using seg_ratio. Here the inner hinge segments are 1/3 the outer, a rather extreme difference. Note also that it's a little simpler to mount the inner hinge on the LEFT side of the top section to interface with the hinge mounted on the RIGHT.
// $fn=32;
// cuboid([2,40,15]){
// position(TOP+RIGHT) orient(anchor=RIGHT)
// knuckle_hinge(length=35, segs=9, offset=3, arm_height=1,
// seg_ratio=1/3);
// attach(TOP,TOP) color("green")
// cuboid([2,40,15],anchor=TOP)
// position(TOP+LEFT) orient(anchor=LEFT)
// knuckle_hinge(length=35, segs=9, offset=3, arm_height=1,
// seg_ratio=1/3, inner=true);
// }
// Example(NoScales): A single hinge with an even number of segments will probably look strange, but they work together neatly in a pair. This example also shows that the arm_height can change between the inner and outer hinge parts and they will still interface properly.
// $fn=32;
// cuboid([2,40,15]){
// yflip_copy()
// position(TOP+RIGHT+FRONT) orient(anchor=RIGHT)
// knuckle_hinge(length=12, segs=2, offset=2, arm_height=2,
// anchor=BOT+LEFT);
// attach(TOP,TOP) color("green")
// cuboid([2,40,15],anchor=TOP)
// yflip_copy()
// position(TOP+LEFT+FRONT) orient(anchor=LEFT)
// knuckle_hinge(length=12, segs=2, offset=2, arm_height=0,
// inner=true, anchor=BOT+RIGHT);
// }
// Example(NoScales): Hinge with self-tapping screw hole. Note that last segment has smaller diameter for screw to bite, whereas other segments have clearance holes.
// $fn=32;
// bottom_half(z=.01)
// cuboid([2,40,15],anchor=TOP)
// position(TOP+RIGHT) orient(anchor=RIGHT)
// knuckle_hinge(length=35, segs=5, offset=5, knuckle_diam=9, pin_diam="#6", fill=false,inner=false, screw_head="flat");
// Example(NoScales): If you give a non-flat screw head then a counterbore for that head is generated. If you don't want the counterbore, don't give a head type. In this example, tap_depth limits the narrower self-tap section of the hole.
// $fn=32;
// bottom_half(z=.01)
// cuboid([2,40,15],anchor=TOP)
// position(TOP+RIGHT) orient(anchor=RIGHT)
// knuckle_hinge(length=35, segs=3, offset=5, knuckle_diam=9, pin_diam="#6",
// fill=false, inner=false, tap_depth=6, screw_head="socket");
function knuckle_hinge(length, offset, segs, inner=false, arm_height=0, arm_angle=45, gap=0.2,
seg_ratio=1, knuckle_diam=4, pin_diam=1.75, fill=true, clear_top=false,
round_bot=0, round_top=0, pin_fn,
tap_depth, screw_head, screw_tolerance="close",
anchor=BOT,orient,spin) = no_function("hinge");
module knuckle_hinge(length, offset, segs, inner=false, arm_height=0, arm_angle=45, gap=0.2,
seg_ratio=1, knuckle_diam=4, pin_diam=1.75, fill=true, clear_top=false,
round_bot=0, round_top=0, pin_fn,
tap_depth, screw_head, screw_tolerance="close",
anchor=BOT,orient,spin)
{
dummy =
assert(is_str(pin_diam) || all_positive([pin_diam]), "pin_diam must be a screw spec string or a positive number")
assert(all_positive(length), "length must be a postive number")
assert(is_int(segs) && segs>=1, "segs must be an integer 1 or greater")
assert(is_finite(offset) && offset>=knuckle_diam/2, "offset must be a valid number that is not smaller than radius of the hinge barrel")
assert(is_finite(arm_angle) && arm_angle>0 && arm_angle<=90, "arm_angle must be greater than zero and less than or equal to 90");
segs1 = ceil(segs/2);
segs2 = floor(segs/2);
seglen1 = gap + (length-(segs-1)*gap) / (segs1 + segs2*seg_ratio);
seglen2 = gap + (length-(segs-1)*gap) / (segs1 + segs2*seg_ratio) * seg_ratio;
z_adjust = segs%2==1 ? 0
: inner? seglen1/2
: seglen2/2;
attachable(anchor,spin,orient,
size=[length,
arm_height+offset/tan(arm_angle)+knuckle_diam/2+knuckle_diam/2/sin(arm_angle),
offset+knuckle_diam/2],
offset=[0,
-arm_height/2-offset/tan(arm_angle)/2-knuckle_diam/sin(arm_angle)/4+knuckle_diam/4,
-offset/2+knuckle_diam/4]
)
{
down(offset)
yrot(-90)
zmove(z_adjust)
difference()
{
zcopies(n=inner?segs2:segs1, spacing=seglen1+seglen2)
linear_extrude((inner?seglen2:seglen1)-gap,center=true)
_hinge_profile(offset=offset, arm_height=arm_height, arm_angle=arm_angle, knuckle_diam=knuckle_diam, pin_diam=pin_diam,
fill=fill, clear_top=clear_top, round_bot=round_bot, round_top=round_top, pin_fn=pin_fn);
if (is_str(pin_diam)) right(offset) up(length/2-(inner?1:1)*z_adjust){
tap_depth = min(segs%2==1?seglen1-gap/2:seglen2-gap/2, default(tap_depth, length));
screw_hole(pin_diam, length=length+.01, tolerance="self tap", bevel=false, anchor=TOP);
multmatrix(inner ? zflip(z=-length/2) : IDENT)
if (is_undef(screw_head) || screw_head=="none" || starts_with(screw_head,"flat"))
screw_hole(pin_diam, length=length-tap_depth, tolerance=screw_tolerance, bevel=false, anchor=TOP, head=screw_head);
else {
screw_hole(pin_diam, length=length-tap_depth, tolerance=screw_tolerance, bevel=false, anchor=TOP);
screw_hole(pin_diam, length=.01, tolerance=screw_tolerance, bevel=false, anchor=TOP, head=screw_head);
}
}
}
children();
}
}
module _hinge_profile(offset, arm_height, arm_angle=45, knuckle_diam=4, pin_diam=1.75, fill=true, clear_top=false, round_bot=0, round_top=0, pin_fn)
{
extra = .01;
skel = turtle(["left", 90-arm_angle, "untilx", offset+extra, "left", arm_angle,
if (arm_height>0) each ["move", arm_height]]);
ofs = arm_height+offset/tan(arm_angle);
start=round_bot==0 && round_top==0 ? os_flat(abs_angle=90)
: os_round(abs_angle=90, cut=[-round_top,-round_bot],k=.8);
difference(){
union(){
difference(){
fwd(ofs){
left(extra)offset_stroke(skel, width=knuckle_diam, start=start);
if (fill) polygon([each skel,[-extra,ofs]]);
}
if (clear_top) left(.1) rect([offset+knuckle_diam,knuckle_diam+1],anchor=BOT+LEFT);
}
right(offset)ellipse(d=knuckle_diam,realign=true,circum=true);
}
if (is_num(pin_diam) && pin_diam>0)
right(offset)ellipse(d=pin_diam+2*get_slop(), realign=true, circum=true, $fn=default(pin_fn,$fn));
}
}
// Module: living_hinge_mask()
// Usage:
// living_hinge_mask(l, thick, [layerheight=], [foldangle=], [hingegap=], [$slop=], [anchor=], [spin=], [orient=]) [ATTACHMENTS];
// Description:
// Creates a mask to be differenced away from a plate to create a "live" hinge, where a thin layer of plastic holds two parts together.
// Center the mask at the bottom of the part you want to make a hinge in.
// The mask will leave hinge material `2*layerheight` thick on the bottom of the hinge.
// Arguments:
// l = Length of the hinge in mm.
// thick = Thickness in mm of the material to make the hinge in.
// ---
// layerheight = The expected printing layer height in mm.
// foldangle = The interior angle in degrees of the joint to be created with the hinge. Default: 90
// hingegap = Size in mm of the gap at the bottom of the hinge, to make room for folding.
// $slop = Increase size of hinge gap by double this amount
// 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. See [spin](attachments.scad#subsection-spin). Default: `0`
// orient = Vector to rotate top towards. See [orient](attachments.scad#subsection-orient). Default: `UP`
// Example:
// living_hinge_mask(l=100, thick=3, foldangle=60);
module living_hinge_mask(l, thick, layerheight=0.2, foldangle=90, hingegap=undef, anchor=CENTER, spin=0, orient=UP)
{
hingegap = default(hingegap, layerheight)+2*get_slop();
size = [l, hingegap, 2*thick];
size2 = [l, hingegap+2*thick*tan(foldangle/2)];
attachable(anchor,spin,orient, size=size, size2=size2) {
up(layerheight*2) prismoid([l,hingegap], [l, hingegap+2*thick/tan(foldangle/2)], h=thick, anchor=BOT);
children();
}
}
module folding_hinge_mask(l, thick, layerheight=0.2, foldangle=90, hingegap=undef, anchor=CENTER, spin=0, orient=UP)
{
deprecate("living_hinge_mask");
living_hinge_mask(l, thick, layerheight, foldangle, hingegap, anchor, spin, orient);
}
// Section: Snap Locks
// Module: apply_folding_hinges_and_snaps()
// Usage:
// apply_folding_hinges_and_snaps(thick, [foldangle=], [hinges=], [snaps=], [sockets=], [snaplen=], [snapdiam=], [hingegap=], [layerheight=], [$slop=]) CHILDREN;
// Description:
// Adds snaplocks and create hinges in children at the given positions.
// Arguments:
// thick = Thickness in mm of the material to make the hinge in.
// foldangle = The interior angle in degrees of the joint to be created with the hinge. Default: 90
// hinges = List of [LENGTH, POSITION, SPIN] for each hinge to difference from the children.
// snaps = List of [POSITION, SPIN] for each central snaplock to add to the children.
// sockets = List of [POSITION, SPIN] for each outer snaplock sockets to add to the children.
// snaplen = Length of locking snaps.
// snapdiam = Diameter/width of locking snaps.
// hingegap = Size in mm of the gap at the bottom of the hinge, to make room for folding.
// layerheight = The expected printing layer height in mm.
// ---
// $slop = increase hinge gap by twice this amount
// Example(Med):
// size=100;
// apply_folding_hinges_and_snaps(
// thick=3, foldangle=54.74,
// hinges=[
// for (a=[0,120,240], b=[-size/2,size/4]) each [
// [200, polar_to_xy(b,a), a+90]
// ]
// ],
// snaps=[
// for (a=[0,120,240]) each [
// [rot(a,p=[ size/4, 0 ]), a+90],
// [rot(a,p=[-size/2,-size/2.33]), a-90]
// ]
// ],
// sockets=[
// for (a=[0,120,240]) each [
// [rot(a,p=[ size/4, 0 ]), a+90],
// [rot(a,p=[-size/2, size/2.33]), a+90]
// ]
// ]
// ) {
// $fn=3;
// difference() {
// cylinder(r=size-1, h=3);
// down(0.01) cylinder(r=size/4.5, h=3.1, spin=180);
// down(0.01) for (a=[0:120:359.9]) zrot(a) right(size/2) cylinder(r=size/4.5, h=3.1);
// }
// }
module apply_folding_hinges_and_snaps(thick, foldangle=90, hinges=[], snaps=[], sockets=[], snaplen=5, snapdiam=5, hingegap=undef, layerheight=0.2)
{
hingegap = default(hingegap, layerheight)+2*get_slop();
difference() {
children();
for (hinge = hinges) {
translate(hinge[1]) {
living_hinge_mask(
l=hinge[0], thick=thick, layerheight=layerheight,
foldangle=foldangle, hingegap=hingegap, spin=hinge[2]
);
}
}
}
for (snap = snaps) {
translate(snap[0]) {
snap_lock(
thick=thick, snaplen=snaplen, snapdiam=snapdiam,
layerheight=layerheight, foldangle=foldangle,
hingegap=hingegap, spin=snap[1]
);
}
}
for (socket = sockets) {
translate(socket[0]) {
snap_socket(
thick=thick, snaplen=snaplen, snapdiam=snapdiam,
layerheight=layerheight, foldangle=foldangle,
hingegap=hingegap, spin=socket[1]
);
}
}
}
// Module: snap_lock()
// Usage:
// snap_lock(thick, [snaplen=], [snapdiam=], [layerheight=], [foldangle=], [hingegap=], [$slop=], [anchor=], [spin=], [orient=]) [ATTACHMENTS];
// Description:
// Creates the central snaplock part.
// Arguments:
// thick = Thickness in mm of the material to make the hinge in.
// ---
// snaplen = Length of locking snaps.
// snapdiam = Diameter/width of locking snaps.
// layerheight = The expected printing layer height in mm.
// foldangle = The interior angle in degrees of the joint to be created with the hinge. Default: 90
// hingegap = Size in mm of the gap at the bottom of the hinge, to make room for folding.
// $slop = increase size of hinge gap by double this amount
// 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. See [spin](attachments.scad#subsection-spin). Default: `0`
// orient = Vector to rotate top towards. See [orient](attachments.scad#subsection-orient). Default: `UP`
// Example:
// snap_lock(thick=3, foldangle=60);
module snap_lock(thick, snaplen=5, snapdiam=5, layerheight=0.2, foldangle=90, hingegap=undef, anchor=CENTER, spin=0, orient=UP)
{
hingegap = default(hingegap, layerheight)+2*get_slop();
snap_x = (snapdiam/2) / tan(foldangle/2) + (thick-2*layerheight)/tan(foldangle/2) + hingegap/2;
size = [snaplen, snapdiam, 2*thick];
attachable(anchor,spin,orient, size=size) {
back(snap_x) {
cube([snaplen, snapdiam, snapdiam/2+thick], anchor=BOT) {
attach(TOP) xcyl(l=snaplen, d=snapdiam, $fn=16);
attach(TOP) xcopies(snaplen-snapdiam/4/3) xscale(0.333) sphere(d=snapdiam*0.8, $fn=12);
}
}
children();
}
}
// Module: snap_socket()
// Usage:
// snap_socket(thick, [snaplen=], [snapdiam=], [layerheight=], [foldangle=], [hingegap=], [$slop=], [anchor=], [spin=], [orient=]) [ATTACHMENTS];
// Description:
// Creates the outside snaplock socketed part.
// Arguments:
// thick = Thickness in mm of the material to make the hinge in.
// ---
// snaplen = Length of locking snaps.
// snapdiam = Diameter/width of locking snaps.
// layerheight = The expected printing layer height in mm.
// foldangle = The interior angle in degrees of the joint to be created with the hinge. Default: 90
// hingegap = Size in mm of the gap at the bottom of the hinge, to make room for folding.
// $slop = Increase size of hinge gap by double this amount
// 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. See [spin](attachments.scad#subsection-spin). Default: `0`
// orient = Vector to rotate top towards. See [orient](attachments.scad#subsection-orient). Default: `UP`
// Example:
// snap_socket(thick=3, foldangle=60);
module snap_socket(thick, snaplen=5, snapdiam=5, layerheight=0.2, foldangle=90, hingegap=undef, anchor=CENTER, spin=0, orient=UP)
{
hingegap = default(hingegap, layerheight)+2*get_slop();
snap_x = (snapdiam/2) / tan(foldangle/2) + (thick-2*layerheight)/tan(foldangle/2) + hingegap/2;
size = [snaplen, snapdiam, 2*thick];
attachable(anchor,spin,orient, size=size) {
fwd(snap_x) {
zrot_copies([0,180], r=snaplen+get_slop()) {
diff("divot")
cube([snaplen, snapdiam, snapdiam/2+thick], anchor=BOT) {
attach(TOP) xcyl(l=snaplen, d=snapdiam, $fn=16);
tag("divot") attach(TOP) left((snaplen+snapdiam/4/3)/2) xscale(0.333) sphere(d=snapdiam*0.8, $fn=12);
}
}
}
children();
}
}

View file

@ -1,205 +0,0 @@
//////////////////////////////////////////////////////////////////////
// LibFile: hingesnaps.scad
// Modules for creating snap-locking foldable hined parts. Includes the mask to create the hinge and
// modules to create the snap-locks.
// Includes:
// include <BOSL2/std.scad>
// include <BOSL2/hingesnaps.scad>
// FileGroup: Parts
// FileSummary: Foldable, snap-locking parts.
//////////////////////////////////////////////////////////////////////
// Section: Hinges and Snaps
// Module: apply_folding_hinges_and_snaps()
// Usage:
// apply_folding_hinges_and_snaps(thick, [foldangle=], [hinges=], [snaps=], [sockets=], [snaplen=], [snapdiam=], [hingegap=], [layerheight=], [$slop=]) CHILDREN;
// Description:
// Adds snaplocks and create hinges in children at the given positions.
// Arguments:
// thick = Thickness in mm of the material to make the hinge in.
// foldangle = The interior angle in degrees of the joint to be created with the hinge. Default: 90
// hinges = List of [LENGTH, POSITION, SPIN] for each hinge to difference from the children.
// snaps = List of [POSITION, SPIN] for each central snaplock to add to the children.
// sockets = List of [POSITION, SPIN] for each outer snaplock sockets to add to the children.
// snaplen = Length of locking snaps.
// snapdiam = Diameter/width of locking snaps.
// hingegap = Size in mm of the gap at the bottom of the hinge, to make room for folding.
// layerheight = The expected printing layer height in mm.
// ---
// $slop = increase hinge gap by twice this amount
// Example(Med):
// size=100;
// apply_folding_hinges_and_snaps(
// thick=3, foldangle=54.74,
// hinges=[
// for (a=[0,120,240], b=[-size/2,size/4]) each [
// [200, polar_to_xy(b,a), a+90]
// ]
// ],
// snaps=[
// for (a=[0,120,240]) each [
// [rot(a,p=[ size/4, 0 ]), a+90],
// [rot(a,p=[-size/2,-size/2.33]), a-90]
// ]
// ],
// sockets=[
// for (a=[0,120,240]) each [
// [rot(a,p=[ size/4, 0 ]), a+90],
// [rot(a,p=[-size/2, size/2.33]), a+90]
// ]
// ]
// ) {
// $fn=3;
// difference() {
// cylinder(r=size-1, h=3);
// down(0.01) cylinder(r=size/4.5, h=3.1, spin=180);
// down(0.01) for (a=[0:120:359.9]) zrot(a) right(size/2) cylinder(r=size/4.5, h=3.1);
// }
// }
module apply_folding_hinges_and_snaps(thick, foldangle=90, hinges=[], snaps=[], sockets=[], snaplen=5, snapdiam=5, hingegap=undef, layerheight=0.2)
{
hingegap = default(hingegap, layerheight)+2*get_slop();
difference() {
children();
for (hinge = hinges) {
translate(hinge[1]) {
folding_hinge_mask(
l=hinge[0], thick=thick, layerheight=layerheight,
foldangle=foldangle, hingegap=hingegap, spin=hinge[2]
);
}
}
}
for (snap = snaps) {
translate(snap[0]) {
snap_lock(
thick=thick, snaplen=snaplen, snapdiam=snapdiam,
layerheight=layerheight, foldangle=foldangle,
hingegap=hingegap, spin=snap[1]
);
}
}
for (socket = sockets) {
translate(socket[0]) {
snap_socket(
thick=thick, snaplen=snaplen, snapdiam=snapdiam,
layerheight=layerheight, foldangle=foldangle,
hingegap=hingegap, spin=socket[1]
);
}
}
}
// Module: folding_hinge_mask()
// Usage:
// folding_hinge_mask(l, thick, [layerheight=], [foldangle=], [hingegap=], [$slop=], [anchor=], [spin=], [orient=]) [ATTACHMENTS];
// Description:
// Creates a mask to be differenced away from a plate to create a foldable hinge.
// Center the mask at the bottom of the plate you want to make a hinge in.
// The mask will leave hinge material two `layerheight`s thick on the bottom of the hinge.
// Arguments:
// l = Length of the hinge in mm.
// thick = Thickness in mm of the material to make the hinge in.
// ---
// layerheight = The expected printing layer height in mm.
// foldangle = The interior angle in degrees of the joint to be created with the hinge. Default: 90
// hingegap = Size in mm of the gap at the bottom of the hinge, to make room for folding.
// $slop = Increase size of hinge gap by double this amount
// 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. See [spin](attachments.scad#subsection-spin). Default: `0`
// orient = Vector to rotate top towards. See [orient](attachments.scad#subsection-orient). Default: `UP`
// Example:
// folding_hinge_mask(l=100, thick=3, foldangle=60);
module folding_hinge_mask(l, thick, layerheight=0.2, foldangle=90, hingegap=undef, anchor=CENTER, spin=0, orient=UP)
{
hingegap = default(hingegap, layerheight)+2*get_slop();
size = [l, hingegap, 2*thick];
size2 = [l, hingegap+2*thick*tan(foldangle/2)];
attachable(anchor,spin,orient, size=size, size2=size2) {
up(layerheight*2) prismoid([l,hingegap], [l, hingegap+2*thick/tan(foldangle/2)], h=thick, anchor=BOT);
children();
}
}
// Module: snap_lock()
// Usage:
// snap_lock(thick, [snaplen=], [snapdiam=], [layerheight=], [foldangle=], [hingegap=], [$slop=], [anchor=], [spin=], [orient=]) [ATTACHMENTS];
// Description:
// Creates the central snaplock part.
// Arguments:
// thick = Thickness in mm of the material to make the hinge in.
// ---
// snaplen = Length of locking snaps.
// snapdiam = Diameter/width of locking snaps.
// layerheight = The expected printing layer height in mm.
// foldangle = The interior angle in degrees of the joint to be created with the hinge. Default: 90
// hingegap = Size in mm of the gap at the bottom of the hinge, to make room for folding.
// $slop = increase size of hinge gap by double this amount
// 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. See [spin](attachments.scad#subsection-spin). Default: `0`
// orient = Vector to rotate top towards. See [orient](attachments.scad#subsection-orient). Default: `UP`
// Example:
// snap_lock(thick=3, foldangle=60);
module snap_lock(thick, snaplen=5, snapdiam=5, layerheight=0.2, foldangle=90, hingegap=undef, anchor=CENTER, spin=0, orient=UP)
{
hingegap = default(hingegap, layerheight)+2*get_slop();
snap_x = (snapdiam/2) / tan(foldangle/2) + (thick-2*layerheight)/tan(foldangle/2) + hingegap/2;
size = [snaplen, snapdiam, 2*thick];
attachable(anchor,spin,orient, size=size) {
back(snap_x) {
cube([snaplen, snapdiam, snapdiam/2+thick], anchor=BOT) {
attach(TOP) xcyl(l=snaplen, d=snapdiam, $fn=16);
attach(TOP) xcopies(snaplen-snapdiam/4/3) xscale(0.333) sphere(d=snapdiam*0.8, $fn=12);
}
}
children();
}
}
// Module: snap_socket()
// Usage:
// snap_socket(thick, [snaplen=], [snapdiam=], [layerheight=], [foldangle=], [hingegap=], [$slop=], [anchor=], [spin=], [orient=]) [ATTACHMENTS];
// Description:
// Creates the outside snaplock socketed part.
// Arguments:
// thick = Thickness in mm of the material to make the hinge in.
// ---
// snaplen = Length of locking snaps.
// snapdiam = Diameter/width of locking snaps.
// layerheight = The expected printing layer height in mm.
// foldangle = The interior angle in degrees of the joint to be created with the hinge. Default: 90
// hingegap = Size in mm of the gap at the bottom of the hinge, to make room for folding.
// $slop = Increase size of hinge gap by double this amount
// 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. See [spin](attachments.scad#subsection-spin). Default: `0`
// orient = Vector to rotate top towards. See [orient](attachments.scad#subsection-orient). Default: `UP`
// Example:
// snap_socket(thick=3, foldangle=60);
module snap_socket(thick, snaplen=5, snapdiam=5, layerheight=0.2, foldangle=90, hingegap=undef, anchor=CENTER, spin=0, orient=UP)
{
hingegap = default(hingegap, layerheight)+2*get_slop();
snap_x = (snapdiam/2) / tan(foldangle/2) + (thick-2*layerheight)/tan(foldangle/2) + hingegap/2;
size = [snaplen, snapdiam, 2*thick];
attachable(anchor,spin,orient, size=size) {
fwd(snap_x) {
zrot_copies([0,180], r=snaplen+get_slop()) {
diff("divot")
cube([snaplen, snapdiam, snapdiam/2+thick], anchor=BOT) {
attach(TOP) xcyl(l=snaplen, d=snapdiam, $fn=16);
tag("divot") attach(TOP) left((snaplen+snapdiam/4/3)/2) xscale(0.333) sphere(d=snapdiam*0.8, $fn=12);
}
}
}
children();
}
}
// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap

View file

@ -229,8 +229,9 @@ function select(list, start, end) =
// list = slice(list, s, e);
// Description:
// Returns a slice of a list, from the first position `s` up to and including the last position `e`.
// The first item in the list is at index 0. Negative indexes are counted back from the end.
// An index of -1 refers to the last list item.
// The first item in the list is at index 0. Negative indexes are counted back from the end, with
// -1 referring to the last list item. If `s` is after `e` then the empty list is returned.
// If an index is off the start/end of the list it will refer to the list start/end.
// Arguments:
// list = The list to get the slice of.
// start = The index of the first item to return. Default: 0
@ -243,6 +244,8 @@ function select(list, start, end) =
// d = slice([3,4,5,6,7,8,9], 5); // Returns [8,9]
// e = slice([3,4,5,6,7,8,9], 2, -2); // Returns [5,6,7,8]
// f = slice([3,4,5,6,7,8,9], 4, 3; // Returns []
// g = slice([3,4,5], 1, 5; // Returns [4,5]
// h = slice([3,4,5], 5, 7); // Returns []
function slice(list,start=0,end=-1) =
assert(is_list(list))
assert(is_int(start))
@ -250,11 +253,10 @@ function slice(list,start=0,end=-1) =
!list? [] :
let(
l = len(list),
start = constrain(start + (start<0? l : 0), 0, l-1),
end = constrain(end + (end<0? l : 0), 0, l-1)
start = start+(start<0 ? l : 0),
end = end + (end<0? l : 0)
)
[if (end>=start) for (i=[start:1:end]) list[i]];
[if (start<=end && end>=0 && start<=l) for (i=[max(start,0):1:min(end,l-1)]) list[i]];
// Function: last()
// Usage:

View file

@ -1203,7 +1203,8 @@ function _stroke_end(width,left, right, spec) =
rightdelete = intright? pathcutright[1] + pathclip[1] -1 : pathcutright[1],
leftcorner = line_intersection([pathcutleft[0], newleft[pathcutleft[1]]], [newright[0],newleft[0]]),
rightcorner = line_intersection([pathcutright[0], newright[pathcutright[1]]], [newright[0],newleft[0]]),
roundover_fits = jointleft+jointright < norm(rightcorner-leftcorner)
roundover_fits = is_def(rightcorner) && is_def(leftcorner) &&
jointleft+jointright < norm(rightcorner-leftcorner)
)
assert(roundover_fits,"Roundover too large to fit")
let(

View file

@ -235,7 +235,7 @@ Torx values: https://www.stanleyengineeredfastening.com/-/media/web/sef/resourc
// length / l = length of screw (in mm)
// thread = thread type or specification. See [screw pitch](#subsection-standard-screw-pitch). Default: "coarse"
// drive_size = size of drive recess to override computed value
// thread_len = length of threaded portoin of screw (in mm), for making partly threaded screws. Default: fully threaded
// thread_len = length of threaded portion of screw (in mm), for making partly threaded screws. Default: fully threaded
// details = toggle some details in rendering. Default: true
// tolerance = screw tolerance. Determines actual screw thread geometry based on nominal sizing. See [tolerance](#subsection-tolerance). Default is "2A" for UTS and "6g" for ISO.
// undersize = amount to decrease screw diameter, a scalar to apply to all parts, or a 2-vector to control shaft and head. Default: 0
@ -540,7 +540,7 @@ module screw(spec, head, drive, thread, drive_size,
: _counterbore;
head = struct_val(tempspec,"head");
headless = head=="none";
flathead = starts_with(head,"flat");
flathead = is_def(head) && starts_with(head,"flat");
reset_headsize = _internal && flathead ? struct_val(tempspec,"head_size_sharp") : undef;
spec=_struct_reset(tempspec,[
["length", l],
@ -718,7 +718,7 @@ module screw(spec, head, drive, thread, drive_size,
// need to adjust $slop for best results. Some people screw machine screws directly into plastic without tapping. This works better with a somewhat larger hole, so
// a tolerance of "self tap" produces such a hole. Note that this tolerance also makes the default bevel2=true to bevel the top, which makes it much easier
// to start the screw. The "self tap" tolerance subtracts `0.72 * pitch` when pitch is below 1mm, `0.6 * pitch` when the pitch is over 1.5mm, and it interpolates between.
// It was tested in PLA with a Prusa MK3S and $slop=0.5 and worked on UTS screws from #2 up to 1/2 inch.
// It was tested in PLA with a Prusa MK3S and $slop=0.05 and worked on UTS screws from #2 up to 1/2 inch.
// .
// The counterbore parameter adds a cylindrical clearance hole above the screw shaft. For flat heads it extends above the flathead and for other screw types it
// replaces the head with a cylinder large enough in diameter for the head to fit. For a flat head you must specify the length of the counterbore. For other heads you can
@ -1406,9 +1406,11 @@ module screw_head(screw_info,details=false, counterbore=0,flat_height,oversize=0
head=="cheese" ? .7 * head_height :
0.1 * head_height; // round and button
head_size2 = head=="cheese" ? head_size-2*tan(5)*head_height : head_size; // 5 deg slope on cheese head
cyl(l=base, d1=head_size, d2=head_size2,anchor=BOTTOM)
segs = segs(head_size);
cyl(l=base, d1=head_size, d2=head_size2,anchor=BOTTOM, $fn=segs)
attach(TOP)
rotate_extrude()
zrot(180) // Needed to align facets when $fn is odd
rotate_extrude($fn=segs) // ensure same number of segments for cap as for head body
intersection(){
arc(points=[[-head_size2/2,0], [0,-base+head_height * (head=="button"?4/3:1)], [head_size2/2,0]]);
square([head_size2, head_height-base]);
@ -1790,6 +1792,7 @@ function screw_info(name, head, drive, thread, drive_size, threads_oversize=0, h
: type[0] == "metric" ? _screw_info_metric(type[1], type[2], head, thread, drive)
: []
)
assert(is_def(struct_val(screwdata,"head")),str("Screw head \"",head,"\" unknown or unsupported for specified screw"))
_struct_reset(screwdata,
[
["drive_depth", drive_info[2]],
@ -2622,7 +2625,7 @@ function _screw_info_metric(diam, pitch, head, thread, drive) =
[48, [72, 36]],
],
entry = struct_val(metric_socket, diam),
dummy=assert(is_def(entry), str("Screw size M",diam," unsupported for headless screws")),
dummy=assert(is_def(entry), str("Screw size M",diam," unsupported for head type \"",head,"\"")),
drive_size = drive=="hex" ? [["drive_size",entry[1]],["drive_depth",diam/2]] :
drive=="torx" ? [["drive_size", entry[2]], ["drive_depth", entry[3]]] :
[]
@ -2646,7 +2649,7 @@ function _screw_info_metric(diam, pitch, head, thread, drive) =
type = head=="pan" ? (drive=="slot" ? "pan flat" : "pan round") : head,
htind = drive=="slot" ? 1 : 2,
entry = struct_val(metric_pan, diam),
dummy=assert(is_def(entry), str("Screw size M",diam," unsupported for headless screws")),
dummy=assert(is_def(entry), str("Screw size M",diam," unsupported for head type \"",head,"\"")),
drive_size = drive=="phillips" ? [["drive_size", entry[3]],
//["drive_diameter", entry[4]],
["drive_depth",entry[5]],
@ -2709,7 +2712,7 @@ function _screw_info_metric(diam, pitch, head, thread, drive) =
: drive=="torx"? metric_cheese_torx
: metric_cheese,
diam),
dummy=assert(is_def(entry), str("Screw size M",diam," unsupported for headless screws")),
dummy=assert(is_def(entry), str("Screw size M",diam," unsupported for head type \"",head,"\"")),
drive_index = drive=="phillips" ? 3
: drive=="hex" ? 2
: undef,
@ -2770,7 +2773,7 @@ function _screw_info_metric(diam, pitch, head, thread, drive) =
[20, [40 , 35.7 ]]
],
entry = struct_val(small ? metric_flat_small : metric_flat_large, diam),
dummy=assert(is_def(entry), str("Screw size M",diam," unsupported for headless screws")),
dummy=assert(is_def(entry), str("Screw size M",diam," unsupported for head type \"",head,"\"")),
driveind = small && drive=="phillips" ? 2
: !small && drive=="hex" ? 2
: !small && drive=="torx" ? 4

View file

@ -139,7 +139,7 @@
// a vertex duplicating method on one side and a resampling method on the other side, then
// `refine` must be set so that the resulting number of vertices matches the number that is
// used for the resampled profiles. The best way to avoid confusion is to ensure that the
// profiles connected by "direct" or "realign" all have the same number of points and at the
// profiles connected by "direct" or "reindex" all have the same number of points and at the
// transition, the refined number of points matches.
// .
// Arguments:
@ -1079,6 +1079,7 @@ module rotate_sweep(
// poly = [[-10,0], [-3,-5], [3,-5], [10,0], [0,-30]];
// spiral_sweep(poly, h=200, r=50, turns=3, $fn=36);
function _taperfunc(x) =
x>1 ? 1 : x<0 ? 0:
let(higofs = pow(0.05,2)) // Smallest hig scale is the square root of this value
sqrt((1-higofs)*x+higofs);
function _taperfunc_ellipse(x) =
@ -1087,7 +1088,8 @@ function _ss_polygon_r(N,theta) =
let( alpha = 360/N )
cos(alpha/2)/(cos(posmod(theta,alpha)-alpha/2));
function spiral_sweep(poly, h, r, turns=1, taper, center, r1, r2, d, d1, d2, taper1, taper2, internal=false, anchor=CENTER, spin=0, orient=UP) =
assert(is_num(turns) && turns != 0)
assert(is_num(turns) && turns != 0, "turns must be a nonzero number")
assert(all_positive([h]), "Spiral height must be a positive number")
let(
tapersample = 10, // Oversample factor for higbee tapering
dir = sign(turns),
@ -1148,7 +1150,7 @@ function spiral_sweep(poly, h, r, turns=1, taper, center, r1, r2, d, d1, d2, tap
u = a/(360*turns),
r = lerp(r1,r2,u),
mat = affine3d_zrot(dir*a)
* affine3d_translate([_ss_polygon_r(sides,dir*a)*r, 0, dir*h * (u-0.5)])
* affine3d_translate([_ss_polygon_r(sides,dir*a)*r, 0, h * (u-0.5)])
* affine3d_xrot(90)
* skewmat
* scale([hsc,lerp(hsc,1,0.25),1], cp=[internal ? xmax : xmin, yctr, 0]),
@ -2637,7 +2639,7 @@ function associate_vertices(polygons, split, curpoly=0) =
// Figure(3D): This is the "hexgrid" VNF tile, which creates a hexagonal grid texture, something which doesn't work well with a height field because the edges of the hexagon don't align with the grid. Note how the tile ranges between 0 and 1 in both X, Y and Z.
// tex = texture("hex_grid");
// vnf_polyhedron(tex);
// Figure(3D): This is an example of a tile that has no edges at the top or bottom, so it creates disconnected rings. See below for examples showing this tile in use.
// Figure(3D): This is an example of a tile that has no edges at the top or bottom, so it creates disconnected rings. See {{linear_sweep()}} for examples showing this tile in use.
// shape = skin([
// rect(2/5),
// rect(2/3),

View file

@ -37,6 +37,14 @@ module test_slice() {
assert(slice(l, 3, 3) == [6]);
assert(slice(l, 4) == [7,8,9]);
assert(slice(l, -2) == [8,9]);
assert(slice(l,-10,-8) == []);
assert(slice(l,10,12) == []);
assert(slice(l,12,10) == []);
assert(slice(l,4,12) == [7,8,9]);
assert(slice(l,-10,2) == [3,4,5]);
assert(slice(l,-10,-4) == [3,4,5,6]);
assert(slice(l,-1,1) == []);
assert(slice(l,5,4) == []);
}
test_slice();

View file

@ -1175,15 +1175,16 @@ module generic_threaded_rod(
higbee2 = thigbee2==0 ? true : thigbee2;
extra_thread1 = higbee1==false && internal ? 1 : 0;
extra_thread2 = higbee2==false && internal ? 1 : 0;
r1 = get_radius(d1=d1, d=d);
r2 = get_radius(d1=d2, d=d);
dummy0 =
assert(all_positive([pitch]),"Thread pitch must be a positive value")
assert(all_positive([l]),"Length must be a postive value")
assert(is_path(profile),"Profile must be a path")
assert(is_finite(higbee1) || is_bool(higbee1), str("higbee",is_undef(higbee)?"1":""," must be boolean or a number"))
assert(is_finite(higbee2) || is_bool(higbee2), str("higbee",is_undef(higbee)?"1":""," must be boolean or a number"))
assert(is_bool(left_handed));
r1 = get_radius(d1=d1, d=d);
r2 = get_radius(d1=d2, d=d);
assert(is_bool(left_handed))
assert(all_positive([r1,r2]), "Must give d or both d1 and d2 as positive values");
sides = quantup(segs(max(r1,r2)), starts);
rsc = internal? (1/cos(180/sides)) : 1;
islop = internal? 2*get_slop() : 0;
@ -1216,6 +1217,8 @@ module generic_threaded_rod(
* frame_map(x=[0,0,1], y=[1,0,0]) // Map profile to 3d, parallel to z axis
* scale(pitch); // scale profile by pitch
start_steps = sides / starts;
higlen = 4/32*360;//360*max(pitch/2, pmax-depth)/(2*PI*_r2);
echo(higlen=higlen);
thread_verts = [
// Outer loop constructs a vertical column of the screw at each angle
// covering 1/starts * 360 degrees of the cylinder.
@ -1227,14 +1230,15 @@ module generic_threaded_rod(
full_profile = [ // profile for the entire rod
for (thread = [-threads/2:1:threads/2-1])
let(
tang = (thread/starts) * 360 + ang,
adjusted_prof3d = tang < -twist/2+higang1 || tang > twist/2-higang2
? [for(v=prof3d) [v.x,internal?pmax/pitch:-pdepth,v.z]]
: prof3d
tang = thread/starts * 360 + ang,
hsc = tang < -twist/2+higang1 ? _taperfunc(1-(-twist/2+higang1-tang)/higlen )
: tang > twist/2-higang2 ? _taperfunc(1-(tang-twist/2+higang2)/higlen )
: 1,
higscale=scale([lerp(hsc,1,0.25),hsc,1], cp=[0,internal ? pmax/pitch:-pdepth, 0])
)
// The right movement finds the position of the thread along
// what will be the z axis after the profile is mapped to 3d
each apply(right(dz + thread) , adjusted_prof3d)
each apply(right(dz + thread) * higscale, prof3d)
]
)
[
@ -1271,13 +1275,12 @@ module generic_threaded_rod(
slope = (_r1-_r2)/l;
maxlen = 5*pitch;
attachable(anchor,spin,orient, r1=_r1, r2=_r2, l=l) {
union(){
// This method is faster but more complex code and it produces green tops
difference() {
vnf_polyhedron(vnf_quantize(thread_vnfs),convexity=10);
if (!internal){
if (bevel1 || bevel2)
rotate_extrude(){
@ -1544,8 +1547,9 @@ module thread_helix(
d1, d2, taper, taper1, taper2,
anchor, spin, orient
) {
dummy1=assert(is_undef(profile) || !any_defined([thread_depth, flank_angle]),"Cannot give thread_depth or flank_angle with a profile");
h = pitch*starts*turns;
dummy1=assert(is_undef(profile) || !any_defined([thread_depth, flank_angle]),"Cannot give thread_depth or flank_angle with a profile")
assert(all_positive([turns]), "The turns parameter must be a positive number");
h = pitch*starts*abs(turns);
r1 = get_radius(d1=d1, d=d, dflt=10);
r2 = get_radius(d1=d2, d=d, dflt=10);
profile = is_def(profile) ? profile :