Fix manifold issues with trapezoidal_threaded_rod().

This commit is contained in:
Garth Minette 2021-02-02 02:14:59 -08:00
parent 99072122ce
commit 323a42177d
3 changed files with 101 additions and 109 deletions

View file

@ -103,7 +103,7 @@ module thread_helix(
// internal = If true, make this a mask for making internal threads.
// d1 = Bottom outside diameter of threads.
// d2 = Top outside diameter of threads.
// higbee = Length to taper thread ends over. Default: 0
// higbee = Length to taper thread ends over. Default: 0 (No higbee thread tapering)
// higbee1 = Length to taper bottom thread end over.
// higbee2 = Length to taper top thread end over.
// center = If given, overrides `anchor`. A true value sets `anchor=CENTER`, false sets `anchor=UP`.
@ -148,90 +148,92 @@ module trapezoidal_threaded_rod(
profile,
internal=false,
d1, d2,
higbee=0, higbee1, higbee2,
higbee, higbee1, higbee2,
center, anchor, spin, orient
) {
_r1 = get_radius(d1=d1, d=d, dflt=10);
_r2 = get_radius(d1=d2, d=d, dflt=10);
sides = quantup(segs(max(_r1,_r2)), starts);
rsc = internal? (1/cos(180/sides) + $slop*3) : 1;
threads = ceil(l/pitch/starts)+(starts<4?4-starts:1);
threads = ceil(l/pitch/starts) + 2;
ll = threads * pitch * starts;
depth = min((thread_depth==undef? pitch/2 : thread_depth), pitch/2/tan(thread_angle));
pa_delta = min(pitch/4-0.01,depth*tan(thread_angle)/2)/pitch;
dir = left_handed? -1 : 1;
twist = 360 * l / pitch / starts;
higbee1 = first_defined([higbee1, higbee, 0]);
higbee2 = first_defined([higbee2, higbee, 0]);
higang1 = 360 * higbee1 / (2 * _r1 * PI);
higang2 = 360 * higbee2 / (2 * _r2 * PI);
higsteps1 = ceil(higang1/360*sides);
higsteps2 = ceil(higang2/360*sides);
_higbee1 = first_defined([higbee1, higbee, 0]);
_higbee2 = first_defined([higbee2, higbee, 0]);
higang1 = 360 * _higbee1 / (2 * PI * _r1);
higang2 = 360 * _higbee2 / (2 * PI * _r2);
assert(higang1 < twist/2);
assert(higang2 < twist/2);
higstart = twist/2 + 360/starts/4;
higbee_table = [
[-twist, 0.01],
[-twist/2, 0.01],
[-twist/2+higang1, 1],
[ twist/2-higang2, 1],
[ twist/2, 0.01],
[ twist, 0.01]
[-higstart*2, 0.01],
[-higstart-0.001, 0.01],
[-higstart+higang1, 1 ],
[+higstart-higang2, 1 ],
[+higstart+0.001, 0.01],
[+higstart*2, 0.01]
];
echo(higbee_table);
r1 = -depth/pitch;
z1 = 1/4-pa_delta;
z2 = 1/4+pa_delta;
profile = profile!=undef? profile : [
[-z2, r1],
[-z1, 0],
[ z1, 0],
[ z2, r1],
];
profile = pitch * (
profile!=undef? profile : [
[-z2, r1],
[-z1, 0],
[ z1, 0],
[ z2, r1],
]
);
pdepth = -min(subindex(profile,1));
eprofile = [
[-0.5, 0],
each move([0,pdepth], p=profile),
[ 0.5, 0],
] * pitch;
move([pitch,pdepth], p=profile[0]),
];
angstep = 360 / sides;
angsteps = ceil(twist / (360 / sides)) + sides;
angsteps = ceil(sides * (twist / 360 + 2));
zang = atan2(_r2-_r1,l);
thread_verts = [
[for (x = eprofile) [0,0,-l/2]],
for (a = [0:1:angsteps]) let (
u = (a-angsteps/2) / (angsteps-sides),
ang = u * twist,
[for (i = idx(eprofile)) [0,0,-ll/2]],
for (thread = [0:1:threads-1], side=[0:1:sides-1]) let(
ang = ((thread - threads/2) + (side / sides)) * 360,
u = ang / twist,
r = lerp(_r1, _r2, u) * rsc,
hsc = higbee1==0 && higbee2==0? 1 : lookup(ang, higbee_table),
mat = affine3d_zrot(ang*dir) *
affine3d_translate([r-pdepth*pitch, 0, l*u-0*pitch]) *
affine3d_translate([r-pdepth*pitch, 0, l*u]) *
affine3d_xrot(90) *
affine3d_skew_xz(xa=zang) *
affine3d_mirror([-1,1]) *
affine3d_scale([1,hsc,1]),
pts = apply(mat, path3d(eprofile))
) pts,
[for (x = eprofile) [0,0, l/2]],
[for (x = eprofile) [0,0,+ll/2]],
];
thread_vnf = vnf_vertex_array(thread_verts, reverse=left_handed);
eplen = len(eprofile);
vlen = len(thread_vnf[0]);
thread_vnf2 = [
concat(thread_vnf[0], [[0,0,-l/2], [0,0,l/2]]),
concat(thread_vnf[0], [[0,0,-ll/2], [0,0,+ll/2]]),
concat(thread_vnf[1], [
for (i = [0:1:sides/starts]) each
left_handed? [
[eplen*(i+1), eplen*i, vlen],
[vlen-eplen*(i+1)-1, vlen-eplen*i-1, vlen+1]
[vlen-eplen*(i+1)-1, vlen-eplen*(i+0)-1, vlen+1]
] : [
[eplen*i, eplen*(i+1), vlen],
[vlen-eplen*i-1, vlen-eplen*(i+1)-1, vlen+1]
[eplen*i, eplen*(i+1), vlen],
[vlen-eplen*(i+0)-1, vlen-eplen*(i+1)-1, vlen+1]
]
])
];
thread_vnfs = vnf_merge([
for (start = [0:1:starts-1]) zrot(start*360/starts, p=thread_vnf2)
]);
], cleanup=true);
anchor = get_anchor(anchor, center, BOT, CENTER);
attachable(anchor,spin,orient, r1=_r1, r2=_r2, l=l) {
difference() {

View file

@ -6,7 +6,7 @@
//////////////////////////////////////////////////////////////////////
BOSL_VERSION = [2,0,555];
BOSL_VERSION = [2,0,556];
// Section: BOSL Library Version Functions

134
vnf.scad
View file

@ -145,18 +145,31 @@ function vnf_add_faces(vnf=EMPTY_VNF, faces) =
// Function: vnf_merge()
// Usage:
// vnf = vnf_merge([VNF, VNF, VNF, ...]);
// vnf = vnf_merge([VNF, VNF, VNF, ...], <cleanup>);
// Description:
// Given a list of VNF structures, merges them all into a single VNF structure.
function vnf_merge(vnfs=[],_i=0,_acc=EMPTY_VNF) =
(assert(is_vnf_list(vnfs)) _i>=len(vnfs))? _acc :
vnf_merge(
vnfs, _i=_i+1,
_acc = let(base=len(_acc[0])) [
concat(_acc[0], vnfs[_i][0]),
concat(_acc[1], [for (f=vnfs[_i][1]) [for (i=f) i+base]]),
function vnf_merge(vnfs, cleanup=false) =
let (
offs = cumsum([
0, for (vnf = vnfs) len(vnf[0])
])
) [
[for (vnf=vnfs) each vnf[0]],
[
for (i = idx(vnfs)) let(
vnf = vnfs[i],
verts = vnf[0],
faces = vnf[1]
)
for (face = faces) let(
dface = !cleanup ? face :
deduplicate_indexed(verts, face, closed=true)
)
if (len(dface) >= 3)
[ for (j = dface) offs[i] + j ]
]
);
];
// Function: vnf_compact()
// Usage:
@ -281,7 +294,7 @@ function vnf_vertex_array(
rowcnt = rows - (row_wrap?0:1)
)
rows<=1 || cols<=1 ? vnf :
vnf_merge([
vnf_merge(cleanup=true, [
vnf, [
concat(
pts,
@ -681,13 +694,8 @@ function vnf_validate(vnf, show_warns=true, check_isects=false) =
uniq_edges = edgecnts[0],
big_faces = !show_warns? [] : [
for (face = faces)
if (len(face) > 3) [
"WARNING",
"BIG_FACE",
"Face has more than 3 vertices, and may confuse CGAL",
[for (i=face) varr[i]],
"yellow"
]
if (len(face) > 3)
_vnf_validate_err("BIG_FACE", [for (i=face) varr[i]])
],
null_faces = !show_warns? [] : [
for (face = faces) let(
@ -696,13 +704,9 @@ function vnf_validate(vnf, show_warns=true, check_isects=false) =
if (len(face)>=3) let(
faceverts = [for (k=face) varr[k]],
area = polygon_area(faceverts)
) if (is_num(area) && abs(area) < EPSILON) [
"WARNING",
"NULL_FACE",
str("Face has zero area: ",fmt_float(abs(area),15)),
faceverts,
"brown"
]
)
if (is_num(area) && abs(area) < EPSILON)
_vnf_validate_err("NULL_FACE", faceverts)
],
nonplanars = unique([
for (face = faces) let(
@ -710,23 +714,13 @@ function vnf_validate(vnf, show_warns=true, check_isects=false) =
area = polygon_area(faceverts)
)
if (is_num(area) && abs(area) > EPSILON)
if (!coplanar(faceverts)) [
"ERROR",
"NONPLANAR",
"Face vertices are not coplanar",
faceverts,
"cyan"
]
if (!coplanar(faceverts))
_vnf_validate_err("NONPLANAR", faceverts)
]),
overpop_edges = unique([
for (i=idx(uniq_edges))
if (edgecnts[1][i]>2) [
"ERROR",
"OVRPOP_EDGE",
"Too many faces attached at Edge",
[for (i=uniq_edges[i]) varr[i]],
"#f70"
]
if (edgecnts[1][i]>2)
_vnf_validate_err("OVRPOP_EDGE", [for (i=uniq_edges[i]) varr[i]])
]),
reversals = unique([
for(i = idx(faces), j = idx(faces)) if(i != j)
@ -736,13 +730,7 @@ function vnf_validate(vnf, show_warns=true, check_isects=false) =
for(edge2 = pair(faces[j],true))
if(edge1 == edge2) // Valid adjacent faces will never have the same vertex ordering.
if(_edge_not_reported(edge1, varr, overpop_edges))
[
"ERROR",
"REVERSAL",
"Faces Reverse Across Edge",
[for (i=edge1) varr[i]],
"violet"
]
_vnf_validate_err("REVERSAL", [for (i=edge1) varr[i]])
]),
t_juncts = unique([
for (v=idx(varr), edge=uniq_edges)
@ -754,13 +742,8 @@ function vnf_validate(vnf, show_warns=true, check_isects=false) =
if (a != b && b != c && a != c) let(
pt = segment_closest_point([a,c],b)
)
if (pt == b) [
"ERROR",
"T_JUNCTION",
"Vertex is mid-edge on another Face",
[b],
"red"
]
if (pt == b)
_vnf_validate_err("T_JUNCTION", [b])
]),
isect_faces = !check_isects? [] : unique([
for (i = [0:1:len(faces)-2])
@ -791,26 +774,15 @@ function vnf_validate(vnf, show_warns=true, check_isects=false) =
)
if (!is_undef(isects2))
for (seg=isects2)
if (seg[0] != seg[1]) [
"ERROR",
"FACE_ISECT",
"Faces intersect",
seg,
"blue"
]
if (seg[0] != seg[1])
_vnf_validate_err("FACE_ISECT", seg)
]),
hole_edges = unique([
for (i=idx(uniq_edges))
if (edgecnts[1][i]<2)
if (_pts_not_reported(uniq_edges[i], varr, t_juncts))
if (_pts_not_reported(uniq_edges[i], varr, isect_faces))
[
"ERROR",
"HOLE_EDGE",
"Edge bounds Hole",
[for (i=uniq_edges[i]) varr[i]],
"magenta"
]
_vnf_validate_err("HOLE_EDGE", [for (i=uniq_edges[i]) varr[i]])
])
) concat(
big_faces,
@ -824,6 +796,24 @@ function vnf_validate(vnf, show_warns=true, check_isects=false) =
);
_vnf_validate_errs = [
["BIG_FACE", "WARNING", "cyan", "Face has more than 3 vertices, and may confuse CGAL"],
["NULL_FACE", "WARNING", "blue", "Face has zero area."],
["NONPLANAR", "ERROR", "yellow", "Face vertices are not coplanar"],
["OVRPOP_EDGE", "ERROR", "orange", "Too many faces attached at Edge"],
["REVERSAL", "ERROR", "violet", "Faces Reverse Across Edge"],
["T_JUNCTION", "ERROR", "magenta", "Vertex is mid-edge on another Face"],
["FACE_ISECT", "ERROR", "brown", "Faces intersect"],
["HOLE_EDGE", "ERROR", "red", "Edge bounds Hole"]
];
function _vnf_validate_err(name, extra) =
let(
info = [for (x = _vnf_validate_errs) if (x[0] == name) x][0]
) concat(info, [extra]);
function _pts_not_reported(pts, varr, reports) =
[
for (i = pts, report = reports, pt = report[3])
@ -847,12 +837,12 @@ module vnf_validate(vnf, size=1, show_warns=true, check_isects=false) {
check_isects=check_isects
);
for (fault = faults) {
typ = fault[0];
err = fault[1];
msg = fault[2];
pts = fault[3];
clr = fault[4];
echo(str(typ, " ", err, ": ", msg, " at ", pts));
err = fault[0];
typ = fault[1];
clr = fault[2];
msg = fault[3];
pts = fault[4];
echo(str(typ, " ", err, " (", clr ,"): ", msg, " at ", pts));
color(clr) {
if (len(pts)==2) {
stroke(pts, width=size);