From ec900943df533f0b3d3dca639ebba3acf4139e56 Mon Sep 17 00:00:00 2001 From: Adrian Mariano Date: Mon, 10 Jan 2022 19:46:51 -0500 Subject: [PATCH 1/7] strings reorg --- linalg.scad | 4 +- regions.scad | 2 + screws.scad | 20 +-- shapes3d.scad | 2 +- strings.scad | 255 ++++++++++++++++++--------------------- tests/test_mutators.scad | 4 +- tests/test_strings.scad | 214 ++++++++++++++++---------------- utility.scad | 4 +- 8 files changed, 242 insertions(+), 263 deletions(-) diff --git a/linalg.scad b/linalg.scad index f42d43b..a8a47b0 100644 --- a/linalg.scad +++ b/linalg.scad @@ -73,9 +73,9 @@ function is_matrix_symmetric(A,eps=1e-12) = function echo_matrix(M,description,sig=4,eps=1e-9) = let( horiz_line = chr(8213), - matstr = matrix_strings(M,sig=sig,eps=eps), + matstr = _format_matrix(M,sig=sig,eps=eps), separator = str_join(repeat(horiz_line,10)), - dummy=echo(str(separator," ",is_def(description) ? description : "")) + dummy=echo(str(separator,is_def(description) ? str(" ",description) : "")) [for(row=matstr) echo(row)] ) echo(separator); diff --git a/regions.scad b/regions.scad index 5fbc38c..16d82b2 100644 --- a/regions.scad +++ b/regions.scad @@ -971,6 +971,8 @@ function offset( sharpcorners = [for(i=[0:len(goodsegs)-1]) _segment_extension(select(goodsegs,i-1), select(goodsegs,i))], // If some segments are parallel then the extended segments are undefined. This case is not handled // Note if !closed the last corner doesn't matter, so exclude it +fd= echo(sharpcorners=sharpcorners)echo(alldef=all_defined(sharpcorners))echo(goodsegs=goodsegs), + parallelcheck = (len(sharpcorners)==2 && !closed) || all_defined(closed? sharpcorners : select(sharpcorners, 1,-2)) diff --git a/screws.scad b/screws.scad index 79a9b20..d1ab6c3 100644 --- a/screws.scad +++ b/screws.scad @@ -30,16 +30,16 @@ Torx values: https://www.stanleyengineeredfastening.com/-/media/web/sef/resourc function _parse_screw_name(name) = let( commasplit = str_split(name,","), - length = str_num(commasplit[1]), + length = parse_num(commasplit[1]), xdash = str_split(commasplit[0], "-x"), type = xdash[0], - thread = str_float(xdash[1]) + thread = parse_float(xdash[1]) ) - type[0] == "M" || type[0] == "m" ? ["metric", str_float(substr(type,1)), thread, length] : + type[0] == "M" || type[0] == "m" ? ["metric", parse_float(substr(type,1)), thread, length] : let( diam = type[0] == "#" ? type : - suffix(type,2)=="''" ? str_float(substr(type,0,len(type)-2)) : - let(val=str_num(type)) + suffix(type,2)=="''" ? parse_float(substr(type,0,len(type)-2)) : + let(val=parse_num(type)) val == floor(val) && val>=0 && val<=12 ? str("#",type) : val ) ["english", diam, thread, u_mul(25.4,length)]; @@ -51,8 +51,8 @@ function _parse_drive(drive=undef, drive_size=undef) = is_undef(drive) ? ["none",undef] : let(drive = downcase(drive)) in_list(drive,["hex","phillips", "slot", "torx", "phillips", "none"]) ? [drive, drive_size] : - drive[0]=="t" ? ["torx", str_int(substr(drive,1))] : - substr(drive,0,2)=="ph" ? ["phillips", str_int(substr(drive,2))] : + drive[0]=="t" ? ["torx", parse_int(substr(drive,1))] : + substr(drive,0,2)=="ph" ? ["phillips", parse_int(substr(drive,2))] : assert(str("Unknown screw drive type ",drive)); @@ -178,7 +178,7 @@ function screw_info(name, head, thread="coarse", drive, drive_size=undef, oversi function _screw_info_english(diam, threadcount, head, thread, drive) = let( - diameter = is_string(diam) ? str_int(substr(diam,1))*0.013 +0.06 : + diameter = is_string(diam) ? parse_int(substr(diam,1))*0.013 +0.06 : diam, pitch = is_def(threadcount) ? INCH/threadcount : @@ -1121,8 +1121,8 @@ function _ISO_thread_tolerance(diameter, pitch, internal=false, tolerance=undef) assert(internalok,str("Invalid internal thread tolerance, ",tolerance,". Must have form ")) assert(externalok,str("invalid external thread tolerance, ",tolerance,". Must have form or ")) let( - tol_num_pitch = str_num(tol_str[0]), - tol_num_crest = str_num(tol_str[2]), + tol_num_pitch = parse_num(tol_str[0]), + tol_num_crest = parse_num(tol_str[2]), tol_letter = tol_str[1] ) assert(tol_letter==tol_str[3],str("Invalid tolerance, ",tolerance,". Cannot mix different letters")) diff --git a/shapes3d.scad b/shapes3d.scad index e81f9d6..25ef33e 100644 --- a/shapes3d.scad +++ b/shapes3d.scad @@ -2083,7 +2083,7 @@ module atext(text, h=1, size=9, font="Courier", anchor="baseline", spin=0, orien anch = !any([for (c=anchor) c=="["])? anchor : let( parts = str_split(str_split(str_split(anchor,"]")[0],"[")[1],","), - vec = [for (p=parts) str_float(str_strip(p," ",start=true))] + vec = [for (p=parts) parse_float(str_strip(p," ",start=true))] ) vec; ha = anchor=="baseline"? "left" : anchor==anch && is_string(anchor)? "center" : diff --git a/strings.scad b/strings.scad index e9b2b25..e11e720 100644 --- a/strings.scad +++ b/strings.scad @@ -334,11 +334,11 @@ function upcase(str) = -// Section: Converting strings to numbers +// Section: Parsing strings into numbers -// Function: str_int() +// Function: parse_int() // Usage: -// str_int(str, [base]) +// parse_int(str, [base]) // Description: // Converts a string into an integer with any base up to 16. Returns NaN if // conversion fails. Digits above 9 are represented using letters A-F in either @@ -347,62 +347,62 @@ function upcase(str) = // str = String to convert. // base = Base for conversion, from 2-16. Default: 10 // Example: -// str_int("349"); // Returns 349 -// str_int("-37"); // Returns -37 -// str_int("+97"); // Returns 97 -// str_int("43.9"); // Returns nan -// str_int("1011010",2); // Returns 90 -// str_int("13",2); // Returns nan -// str_int("dead",16); // Returns 57005 -// str_int("CEDE", 16); // Returns 52958 -// str_int(""); // Returns 0 -function str_int(str,base=10) = +// parse_int("349"); // Returns 349 +// parse_int("-37"); // Returns -37 +// parse_int("+97"); // Returns 97 +// parse_int("43.9"); // Returns nan +// parse_int("1011010",2); // Returns 90 +// parse_int("13",2); // Returns nan +// parse_int("dead",16); // Returns 57005 +// parse_int("CEDE", 16); // Returns 52958 +// parse_int(""); // Returns 0 +function parse_int(str,base=10) = str==undef ? undef : len(str)==0 ? 0 : let(str=downcase(str)) - str[0] == "-" ? -_str_int_recurse(substr(str,1),base,len(str)-2) : - str[0] == "+" ? _str_int_recurse(substr(str,1),base,len(str)-2) : - _str_int_recurse(str,base,len(str)-1); + str[0] == "-" ? -_parse_int_recurse(substr(str,1),base,len(str)-2) : + str[0] == "+" ? _parse_int_recurse(substr(str,1),base,len(str)-2) : + _parse_int_recurse(str,base,len(str)-1); -function _str_int_recurse(str,base,i) = +function _parse_int_recurse(str,base,i) = let( digit = search(str[i],"0123456789abcdef"), last_digit = digit == [] || digit[0] >= base ? (0/0) : digit[0] ) i==0 ? last_digit : - _str_int_recurse(str,base,i-1)*base + last_digit; + _parse_int_recurse(str,base,i-1)*base + last_digit; -// Function: str_float() +// Function: parse_float() // Usage: -// str_float(str) +// parse_float(str) // Description: // Converts a string to a floating point number. Returns NaN if the // conversion fails. // Arguments: // str = String to convert. // Example: -// str_float("44"); // Returns 44 -// str_float("3.4"); // Returns 3.4 -// str_float("-99.3332"); // Returns -99.3332 -// str_float("3.483e2"); // Returns 348.3 -// str_float("-44.9E2"); // Returns -4490 -// str_float("7.342e-4"); // Returns 0.0007342 -// str_float(""); // Returns 0 -function str_float(str) = +// parse_float("44"); // Returns 44 +// parse_float("3.4"); // Returns 3.4 +// parse_float("-99.3332"); // Returns -99.3332 +// parse_float("3.483e2"); // Returns 348.3 +// parse_float("-44.9E2"); // Returns -4490 +// parse_float("7.342e-4"); // Returns 0.0007342 +// parse_float(""); // Returns 0 +function parse_float(str) = str==undef ? undef : len(str) == 0 ? 0 : in_list(str[1], ["+","-"]) ? (0/0) : // Don't allow --3, or +-3 - str[0]=="-" ? -str_float(substr(str,1)) : - str[0]=="+" ? str_float(substr(str,1)) : + str[0]=="-" ? -parse_float(substr(str,1)) : + str[0]=="+" ? parse_float(substr(str,1)) : let(esplit = str_split(str,"eE") ) - len(esplit)==2 ? str_float(esplit[0]) * pow(10,str_int(esplit[1])) : + len(esplit)==2 ? parse_float(esplit[0]) * pow(10,parse_int(esplit[1])) : let( dsplit = str_split(str,["."])) - str_int(dsplit[0])+str_int(dsplit[1])/pow(10,len(dsplit[1])); + parse_int(dsplit[0])+parse_int(dsplit[1])/pow(10,len(dsplit[1])); -// Function: str_frac() +// Function: parse_frac() // Usage: -// str_frac(str,[mixed],[improper],[signed]) +// parse_frac(str,[mixed],[improper],[signed]) // Description: // Converts a string fraction to a floating point number. A string fraction has the form `[-][# ][#/#]` where each `#` is one or more of the // digits 0-9, and there is an optional sign character at the beginning. @@ -418,64 +418,64 @@ function str_float(str) = // improper = set to true to accept improper fractions, false to reject them. Default: true // signed = set to true to accept a leading sign character, false to reject. Default: true // Example: -// str_frac("3/4"); // Returns 0.75 -// str_frac("-77/9"); // Returns -8.55556 -// str_frac("+1/3"); // Returns 0.33333 -// str_frac("19"); // Returns 19 -// str_frac("2 3/4"); // Returns 2.75 -// str_frac("-2 12/4"); // Returns -5 -// str_frac(""); // Returns 0 -// str_frac("3/0"); // Returns inf -// str_frac("0/0"); // Returns nan -// str_frac("-77/9",improper=false); // Returns nan -// str_frac("-2 12/4",improper=false); // Returns nan -// str_frac("-2 12/4",signed=false); // Returns nan -// str_frac("-2 12/4",mixed=false); // Returns nan -// str_frac("2 1/4",mixed=false); // Returns nan -function str_frac(str,mixed=true,improper=true,signed=true) = +// parse_frac("3/4"); // Returns 0.75 +// parse_frac("-77/9"); // Returns -8.55556 +// parse_frac("+1/3"); // Returns 0.33333 +// parse_frac("19"); // Returns 19 +// parse_frac("2 3/4"); // Returns 2.75 +// parse_frac("-2 12/4"); // Returns -5 +// parse_frac(""); // Returns 0 +// parse_frac("3/0"); // Returns inf +// parse_frac("0/0"); // Returns nan +// parse_frac("-77/9",improper=false); // Returns nan +// parse_frac("-2 12/4",improper=false); // Returns nan +// parse_frac("-2 12/4",signed=false); // Returns nan +// parse_frac("-2 12/4",mixed=false); // Returns nan +// parse_frac("2 1/4",mixed=false); // Returns nan +function parse_frac(str,mixed=true,improper=true,signed=true) = str == undef ? undef : len(str)==0 ? 0 : - signed && str[0]=="-" ? -str_frac(substr(str,1),mixed=mixed,improper=improper,signed=false) : - signed && str[0]=="+" ? str_frac(substr(str,1),mixed=mixed,improper=improper,signed=false) : + signed && str[0]=="-" ? -parse_frac(substr(str,1),mixed=mixed,improper=improper,signed=false) : + signed && str[0]=="+" ? parse_frac(substr(str,1),mixed=mixed,improper=improper,signed=false) : mixed ? ( !in_list(str_find(str," "), [undef,0]) || is_undef(str_find(str,"/"))? ( let(whole = str_split(str,[" "])) - _str_int_recurse(whole[0],10,len(whole[0])-1) + str_frac(whole[1], mixed=false, improper=improper, signed=false) - ) : str_frac(str,mixed=false, improper=improper) + _parse_int_recurse(whole[0],10,len(whole[0])-1) + parse_frac(whole[1], mixed=false, improper=improper, signed=false) + ) : parse_frac(str,mixed=false, improper=improper) ) : ( let(split = str_split(str,"/")) len(split)!=2 ? (0/0) : let( - numerator = _str_int_recurse(split[0],10,len(split[0])-1), - denominator = _str_int_recurse(split[1],10,len(split[1])-1) + numerator = _parse_int_recurse(split[0],10,len(split[0])-1), + denominator = _parse_int_recurse(split[1],10,len(split[1])-1) ) !improper && numerator>=denominator? (0/0) : denominator<0 ? (0/0) : numerator/denominator ); -// Function: str_num() +// Function: parse_num() // Usage: -// str_num(str) +// parse_num(str) // Description: // Converts a string to a number. The string can be either a fraction (two integers separated by a "/") or a floating point number. // Returns NaN if the conversion fails. // Example: -// str_num("3/4"); // Returns 0.75 -// str_num("3.4e-2"); // Returns 0.034 -function str_num(str) = +// parse_num("3/4"); // Returns 0.75 +// parse_num("3.4e-2"); // Returns 0.034 +function parse_num(str) = str == undef ? undef : - let( val = str_frac(str) ) + let( val = parse_frac(str) ) val == val ? val : - str_float(str); + parse_float(str); -// Section: Formatting data +// Section: Formatting numbers into strings -// Function: fmt_int() +// Function: format_int() // Usage: -// fmt_int(i, [mindigits]); +// format_int(i, [mindigits]); // Description: // Formats an integer number into a string. This can handle larger numbers than `str()`. // Arguments: @@ -483,10 +483,10 @@ function str_num(str) = // mindigits = If the number has fewer than this many digits, pad the front with zeros until it does. Default: 1. // Example: // str(123456789012345); // Returns "1.23457e+14" -// fmt_int(123456789012345); // Returns "123456789012345" -// fmt_int(-123456789012345); // Returns "-123456789012345" -function fmt_int(i,mindigits=1) = - i<0? str("-", fmt_int(-i,mindigits)) : +// format_int(123456789012345); // Returns "123456789012345" +// format_int(-123456789012345); // Returns "-123456789012345" +function format_int(i,mindigits=1) = + i<0? str("-", format_int(-i,mindigits)) : let(i=floor(i), e=floor(log(i))) i==0? str_join([for (j=[0:1:mindigits-1]) "0"]) : str_join( @@ -497,33 +497,33 @@ function fmt_int(i,mindigits=1) = ); -// Function: fmt_fixed() +// Function: format_fixed() // Usage: -// s = fmt_fixed(f, [digits]); +// s = format_fixed(f, [digits]); // Description: // Given a floating point number, formats it into a string with the given number of digits after the decimal point. // Arguments: // f = The floating point number to format. // digits = The number of digits after the decimal to show. Default: 6 -function fmt_fixed(f,digits=6) = +function format_fixed(f,digits=6) = assert(is_int(digits)) assert(digits>0) - is_list(f)? str("[",str_join(sep=", ", [for (g=f) fmt_fixed(g,digits=digits)]),"]") : + is_list(f)? str("[",str_join(sep=", ", [for (g=f) format_fixed(g,digits=digits)]),"]") : str(f)=="nan"? "nan" : str(f)=="inf"? "inf" : - f<0? str("-",fmt_fixed(-f,digits=digits)) : + f<0? str("-",format_fixed(-f,digits=digits)) : assert(is_num(f)) let( sc = pow(10,digits), scaled = floor(f * sc + 0.5), whole = floor(scaled/sc), part = floor(scaled-(whole*sc)) - ) str(fmt_int(whole),".",fmt_int(part,digits)); + ) str(format_int(whole),".",format_int(part,digits)); -// Function: fmt_float() +// Function: format_float() // Usage: -// fmt_float(f,[sig]); +// format_float(f,[sig]); // Description: // Formats the given floating point number `f` into a string with `sig` significant digits. // Strips trailing `0`s after the decimal point. Strips trailing decimal point. @@ -533,22 +533,22 @@ function fmt_fixed(f,digits=6) = // f = The floating point number to format. // sig = The number of significant digits to display. Default: 12 // Example: -// fmt_float(PI,12); // Returns: "3.14159265359" -// fmt_float([PI,-16.75],12); // Returns: "[3.14159265359, -16.75]" -function fmt_float(f,sig=12) = +// format_float(PI,12); // Returns: "3.14159265359" +// format_float([PI,-16.75],12); // Returns: "[3.14159265359, -16.75]" +function format_float(f,sig=12) = assert(is_int(sig)) assert(sig>0) - is_list(f)? str("[",str_join(sep=", ", [for (g=f) fmt_float(g,sig=sig)]),"]") : + is_list(f)? str("[",str_join(sep=", ", [for (g=f) format_float(g,sig=sig)]),"]") : f==0? "0" : str(f)=="nan"? "nan" : str(f)=="inf"? "inf" : - f<0? str("-",fmt_float(-f,sig=sig)) : + f<0? str("-",format_float(-f,sig=sig)) : assert(is_num(f)) let( e = floor(log(f)), mv = sig - e - 1 - ) mv == 0? fmt_int(floor(f + 0.5)) : - (e<-sig/2||mv<0)? str(fmt_float(f*pow(10,-e),sig=sig),"e",e) : + ) mv == 0? format_int(floor(f + 0.5)) : + (e<-sig/2||mv<0)? str(format_float(f*pow(10,-e),sig=sig),"e",e) : let( ff = f + pow(10,-mv)*0.5, whole = floor(ff), @@ -559,26 +559,26 @@ function fmt_float(f,sig=12) = str_strip(end=true, str_join([ ".", - fmt_int(part, mindigits=mv) + format_int(part, mindigits=mv) ]), "0." ) ]); -// Function: matrix_strings() -// Usage: -// matrix_strings(M, [sig], [eps]) -// Description: -// Convert a numerical matrix into a matrix of strings where every column -// is the same width so it will display in neat columns when printed. -// Values below eps will display as zero. The matrix can include nans, infs -// or undefs and the rows can be different lengths. -// Arguments: -// M = numerical matrix to convert -// sig = significant digits to display. Default: 4 -// eps = values smaller than this are shown as zero. Default: 1e-9 -function matrix_strings(M, sig=4, eps=1e-9) = +/// Function: _format_matrix() +/// Usage: +/// _format_matrix(M, [sig], [eps]) +/// Description: +/// Convert a numerical matrix into a matrix of strings where every column +/// is the same width so it will display in neat columns when printed. +/// Values below eps will display as zero. The matrix can include nans, infs +/// or undefs and the rows can be different lengths. +/// Arguments: +/// M = numerical matrix to convert +/// sig = significant digits to display. Default: 4 +/// eps = values smaller than this are shown as zero. Default: 1e-9 +function _format_matrix(M, sig=4, eps=1e-9) = let( columngap = 1, figure_dash = chr(8210), @@ -590,7 +590,7 @@ function matrix_strings(M, sig=4, eps=1e-9) = let( text = is_undef(entry) ? "und" : abs(entry) < eps ? "0" // Replace hyphens with figure dashes - : str_replace_char(fmt_float(entry, sig),"-",figure_dash), + : str_replace_char(format_float(entry, sig),"-",figure_dash), have_dot = is_def(str_find(text, ".")) ) // If the text lacks a dot we add a space the same width as a dot to @@ -616,9 +616,9 @@ function matrix_strings(M, sig=4, eps=1e-9) = -// Function: str_format() +// Function: format() // Usage: -// s = str_format(fmt, vals); +// s = format(fmt, vals); // Description: // Given a format string and a list of values, inserts the values into the placeholders in the format string and returns it. // Formatting placeholders have the following syntax: @@ -645,13 +645,13 @@ function matrix_strings(M, sig=4, eps=1e-9) = // fmt = The formatting string, with placeholders to format the values into. // vals = The list of values to format. // Example(NORENDER): -// str_format("The value of {} is {:.14f}.", ["pi", PI]); // Returns: "The value of pi is 3.14159265358979." -// str_format("The value {1:f} is known as {0}.", ["pi", PI]); // Returns: "The value 3.141593 is known as pi." -// str_format("We use a very small value {1:.6g} as {0}.", ["EPSILON", EPSILON]); // Returns: "We use a very small value 1e-9 as EPSILON." -// str_format("{:-5s}{:i}{:b}", ["foo", 12e3, 5]); // Returns: "foo 12000true" -// str_format("{:-10s}{:.3f}", ["plecostamus",27.43982]); // Returns: "plecostamus27.440" -// str_format("{:-10.9s}{:.3f}", ["plecostamus",27.43982]); // Returns: "plecostam 27.440" -function str_format(fmt, vals) = +// format("The value of {} is {:.14f}.", ["pi", PI]); // Returns: "The value of pi is 3.14159265358979." +// format("The value {1:f} is known as {0}.", ["pi", PI]); // Returns: "The value 3.141593 is known as pi." +// format("We use a very small value {1:.6g} as {0}.", ["EPSILON", EPSILON]); // Returns: "We use a very small value 1e-9 as EPSILON." +// format("{:-5s}{:i}{:b}", ["foo", 12e3, 5]); // Returns: "foo 12000true" +// format("{:-10s}{:.3f}", ["plecostamus",27.43982]); // Returns: "plecostamus27.440" +// format("{:-10.9s}{:.3f}", ["plecostamus",27.43982]); // Returns: "plecostam 27.440" +function format(fmt, vals) = let( parts = str_split(fmt,"{") ) str_join([ @@ -666,7 +666,7 @@ function str_format(fmt, vals) = assert(i<99) is_undef(fmta)? "" : let( fmtb = str_split(fmta,":"), - num = is_digit(fmtb[0])? str_int(fmtb[0]) : (i-1), + num = is_digit(fmtb[0])? parse_int(fmtb[0]) : (i-1), left = fmtb[1][0] == "-", fmtb1 = default(fmtb[1],""), fmtc = left? substr(fmtb1,1) : fmtb1, @@ -676,21 +676,21 @@ function str_format(fmt, vals) = typ = hastyp? lch : "s", fmtd = hastyp? substr(fmtc,0,len(fmtc)-1) : fmtc, fmte = str_split((zero? substr(fmtd,1) : fmtd), "."), - wid = str_int(fmte[0]), - prec = str_int(fmte[1]), + wid = parse_int(fmte[0]), + prec = parse_int(fmte[1]), val = assert(num>=0&&num Date: Mon, 10 Jan 2022 19:55:54 -0500 Subject: [PATCH 2/7] hide is_polygon_on_list remove debug echo --- geometry.scad | 28 ++++++++++++++-------------- regions.scad | 4 +--- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/geometry.scad b/geometry.scad index 2903283..e9762a6 100644 --- a/geometry.scad +++ b/geometry.scad @@ -2171,23 +2171,23 @@ function _are_polygons_equal(poly1, poly2, eps, st) = max([for(d=poly1-select(poly2,st,st-1)) d*d])= len(polys)? false : are_polygons_equal(poly, polys[i])? true : - __is_polygon_in_list(poly, polys, i+1); + ___is_polygon_in_list(poly, polys, i+1); // Section: Convex Hull diff --git a/regions.scad b/regions.scad index 16d82b2..4f686f9 100644 --- a/regions.scad +++ b/regions.scad @@ -398,7 +398,7 @@ function are_regions_equal(region1, region2, either_winding=false) = function __are_regions_equal(region1, region2, i) = i >= len(region1)? true : - !is_polygon_in_list(region1[i], region2)? false : + !_is_polygon_in_list(region1[i], region2)? false : __are_regions_equal(region1, region2, i+1); @@ -971,8 +971,6 @@ function offset( sharpcorners = [for(i=[0:len(goodsegs)-1]) _segment_extension(select(goodsegs,i-1), select(goodsegs,i))], // If some segments are parallel then the extended segments are undefined. This case is not handled // Note if !closed the last corner doesn't matter, so exclude it -fd= echo(sharpcorners=sharpcorners)echo(alldef=all_defined(sharpcorners))echo(goodsegs=goodsegs), - parallelcheck = (len(sharpcorners)==2 && !closed) || all_defined(closed? sharpcorners : select(sharpcorners, 1,-2)) From 9a71c5072bd5686b15fce558495a11efc21e2922 Mon Sep 17 00:00:00 2001 From: Adrian Mariano Date: Mon, 10 Jan 2022 20:22:11 -0500 Subject: [PATCH 3/7] fix format call --- constants.scad | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constants.scad b/constants.scad index df79e34..1e2a1d3 100644 --- a/constants.scad +++ b/constants.scad @@ -52,7 +52,7 @@ _UNDEF="LRG+HX7dy89RyHvDlAKvb9Y04OTuaikpx205CTh8BSI"; // cuboid([holesize.x + 2*s, holesize.y + 2*s, h+0.2]); // fwd(w/2-1) xrot(90) linear_extrude(1.1) { // text( -// text=fmt_fixed(s,2), +// text=format_fixed(s,2), // size=0.4*holesize.x, // halign="center", // valign="center" From dd9b1976780b79eb6eda7ace1a03fe82bc294518 Mon Sep 17 00:00:00 2001 From: Adrian Mariano Date: Mon, 10 Jan 2022 21:00:39 -0500 Subject: [PATCH 4/7] move linear_sweep and spiral_sweep to skin.scad --- bottlecaps.scad | 14 +-- mutators.scad | 93 -------------------- regions.scad | 120 -------------------------- skin.scad | 225 ++++++++++++++++++++++++++++++++++++++++++++++++ threading.scad | 16 ++-- 5 files changed, 240 insertions(+), 228 deletions(-) diff --git a/bottlecaps.scad b/bottlecaps.scad index 00e715a..a91b9ea 100644 --- a/bottlecaps.scad +++ b/bottlecaps.scad @@ -117,7 +117,7 @@ module pco1810_neck(wall=2, anchor="support-ring", spin=0, orient=UP) pitch=thread_pitch, thread_depth=thread_h+0.1, flank_angle=flank_angle, - twist=810, + turns=810/360, higbee=thread_h*2, anchor=TOP ); @@ -195,7 +195,7 @@ module pco1810_cap(wall=2, texture="none", anchor=BOTTOM, spin=0, orient=UP) } up(wall) cyl(d=cap_id, h=tamper_ring_h+wall, anchor=BOTTOM); } - up(wall+2) thread_helix(d=thread_od-thread_depth*2, pitch=thread_pitch, thread_depth=thread_depth, flank_angle=flank_angle, twist=810, higbee=thread_depth, internal=true, anchor=BOTTOM); + up(wall+2) thread_helix(d=thread_od-thread_depth*2, pitch=thread_pitch, thread_depth=thread_depth, flank_angle=flank_angle, twist=810/360, higbee=thread_depth, internal=true, anchor=BOTTOM); } children(); } @@ -310,7 +310,7 @@ module pco1881_neck(wall=2, anchor="support-ring", spin=0, orient=UP) pitch=thread_pitch, thread_depth=thread_h+0.1, flank_angle=flank_angle, - twist=650, + twist=650/360, higbee=thread_h*2, anchor=TOP ); @@ -379,7 +379,7 @@ module pco1881_cap(wall=2, texture="none", anchor=BOTTOM, spin=0, orient=UP) } up(wall) cyl(d=28.58, h=11.2+wall, anchor=BOTTOM); } - up(wall+2) thread_helix(d=25.5, pitch=2.7, thread_depth=1.6, flank_angle=15, twist=650, higbee=1.6, internal=true, anchor=BOTTOM); + up(wall+2) thread_helix(d=25.5, pitch=2.7, thread_depth=1.6, flank_angle=15, twist=650/360, higbee=1.6, internal=true, anchor=BOTTOM); } children(); } @@ -482,7 +482,7 @@ module generic_bottle_neck( pitch = thread_pitch, thread_depth = thread_h + 0.1 * diamMagMult, flank_angle = flank_angle, - twist = 360 * (height - pitch - lip_roundover_r) * .6167 / pitch, + turns = (height - pitch - lip_roundover_r) * .6167 / pitch, higbee = thread_h * 2, anchor = TOP ); @@ -590,7 +590,7 @@ module generic_bottle_cap( } difference(){ up(wall + pitch / 2) { - thread_helix(d = neckOuterDTol, pitch = pitch, thread_depth = threadDepth, flank_angle = flank_angle, twist = 360 * ((height - pitch) / pitch), higbee = threadDepth, internal = true, anchor = BOTTOM); + thread_helix(d = neckOuterDTol, pitch = pitch, thread_depth = threadDepth, flank_angle = flank_angle, turns = ((height - pitch) / pitch), higbee = threadDepth, internal = true, anchor = BOTTOM); } } } @@ -1130,7 +1130,7 @@ module sp_neck(diam,type,wall,id,style="L",bead=false, anchor, spin, orient) up((H+extra_bot)/2){ difference(){ union(){ - thread_helix(d=T-.01, profile=profile, pitch = INCH/tpi, twist=twist+2*higang, higbee=higlen, anchor=TOP); + thread_helix(d=T-.01, profile=profile, pitch = INCH/tpi, turns=(twist+2*higang)/360, higbee=higlen, anchor=TOP); cylinder(d=T-depth*2,l=H,anchor=TOP); if (bead) down(bead_shift) diff --git a/mutators.scad b/mutators.scad index 5e75517..c0fc682 100644 --- a/mutators.scad +++ b/mutators.scad @@ -348,99 +348,6 @@ module extrude_from_to(pt1, pt2, convexity, twist, scale, slices) { -// Module: spiral_sweep() -// Description: -// Takes a closed 2D polygon path, centered on the XY plane, and sweeps/extrudes it along a 3D spiral path -// of a given radius, height and twist. The origin in the profile traces out the helix of the specified radius. -// If twist is positive the path will be right-handed; if twist is negative the path will be left-handed. -// . -// Higbee specifies tapering applied to the ends of the extrusion and is given as the linear distance -// over which to taper. -// Arguments: -// poly = Array of points of a polygon path, to be extruded. -// h = height of the spiral to extrude along. -// r = Radius of the spiral to extrude along. Default: 50 -// twist = number of degrees of rotation to spiral up along height. -// --- -// d = Diameter of the spiral to extrude along. -// higbee = Length to taper thread ends over. -// higbee1 = Taper length at start -// higbee2 = Taper length at end -// internal = direction to taper the threads with higbee. If true threads taper outward; if false they taper inward. Default: false -// 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` -// center = If given, overrides `anchor`. A true value sets `anchor=CENTER`, false sets `anchor=BOTTOM`. -// Example: -// poly = [[-10,0], [-3,-5], [3,-5], [10,0], [0,-30]]; -// spiral_sweep(poly, h=200, r=50, twist=1080, $fn=36); -module spiral_sweep(poly, h, r, twist=360, higbee, center, r1, r2, d, d1, d2, higbee1, higbee2, internal=false, anchor, spin=0, orient=UP) { - higsample = 10; // Oversample factor for higbee tapering - dummy1=assert(is_num(twist) && twist != 0); - bounds = pointlist_bounds(poly); - yctr = (bounds[0].y+bounds[1].y)/2; - xmin = bounds[0].x; - xmax = bounds[1].x; - poly = path3d(clockwise_polygon(poly)); - anchor = get_anchor(anchor,center,BOT,BOT); - r1 = get_radius(r1=r1, r=r, d1=d1, d=d, dflt=50); - r2 = get_radius(r1=r2, r=r, d1=d2, d=d, dflt=50); - sides = segs(max(r1,r2)); - dir = sign(twist); - ang_step = 360/sides*dir; - anglist = [for(ang = [0:ang_step:twist-EPSILON]) ang, - twist]; - higbee1 = first_defined([higbee1, higbee, 0]); - higbee2 = first_defined([higbee2, higbee, 0]); - higang1 = 360 * higbee1 / (2 * r1 * PI); - higang2 = 360 * higbee2 / (2 * r2 * PI); - dummy2=assert(higbee1>=0 && higbee2>=0) - assert(higang1 < dir*twist/2,"Higbee1 is more than half the threads") - assert(higang2 < dir*twist/2,"Higbee2 is more than half the threads"); - function polygon_r(N,theta) = - let( alpha = 360/N ) - cos(alpha/2)/(cos(posmod(theta,alpha)-alpha/2)); - higofs = pow(0.05,2); // Smallest hig scale is the square root of this value - function taperfunc(x) = sqrt((1-higofs)*x+higofs); - interp_ang = [ - for(i=idx(anglist,e=-2)) - each lerpn(anglist[i],anglist[i+1], - (higang1>0 && higang1>dir*anglist[i+1] - || (higang2>0 && higang2>dir*(twist-anglist[i]))) ? ceil((anglist[i+1]-anglist[i])/ang_step*higsample) - : 1, - endpoint=false), - last(anglist) - ]; - skewmat = affine3d_skew_xz(xa=atan2(r2-r1,h)); - points = [ - for (a = interp_ang) let ( - hsc = dir*a0?true:false, - style=higbee1>0 || higbee2>0 ? "quincunx" : "alt" - ); - - attachable(anchor,spin,orient, r1=r1, r2=r2, l=h) { - vnf_polyhedron(vnf, convexity=ceil(2*dir*twist/360)); - children(); - } -} - - - // Module: path_extrude() // Description: // Extrudes 2D children along a 3D path. This may be slow. diff --git a/regions.scad b/regions.scad index 4f686f9..e9cd822 100644 --- a/regions.scad +++ b/regions.scad @@ -591,126 +591,6 @@ function region_parts(region) = ]; -// Section: Region Extrusion and VNFs - - - -// Function&Module: linear_sweep() -// Usage: -// linear_sweep(region, height, [center], [slices], [twist], [scale], [style], [convexity]) {attachments}; -// Description: -// If called as a module, creates a polyhedron that is the linear extrusion of the given 2D region or polygon. -// If called as a function, returns a VNF that can be used to generate a polyhedron of the linear extrusion -// of the given 2D region or polygon. The benefit of using this, over using `linear_extrude region(rgn)` is -// that it supports `anchor`, `spin`, `orient` and attachments. You can also make more refined -// twisted extrusions by using `maxseg` to subsample flat faces. -// Note that the center option centers vertically using the named anchor "zcenter" whereas -// `anchor=CENTER` centers the entire shape relative to -// the shape's centroid, or other centerpoint you specify. The centerpoint can be "centroid", "mean", "box" or -// a custom point location. -// Arguments: -// region = The 2D [Region](regions.scad) or polygon that is to be extruded. -// height = The height to extrude the region. Default: 1 -// center = If true, the created polyhedron will be vertically centered. If false, it will be extruded upwards from the XY plane. Default: `false` -// slices = The number of slices to divide the shape into along the Z axis, to allow refinement of detail, especially when working with a twist. Default: `twist/5` -// maxseg = If given, then any long segments of the region will be subdivided to be shorter than this length. This can refine twisting flat faces a lot. Default: `undef` (no subsampling) -// twist = The number of degrees to rotate the shape clockwise around the Z axis, as it rises from bottom to top. Default: 0 -// scale = The amount to scale the shape, from bottom to top. Default: 1 -// style = The style to use when triangulating the surface of the object. Valid values are `"default"`, `"alt"`, or `"quincunx"`. -// convexity = Max number of surfaces any single ray could pass through. Module use only. -// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `"origin"` -// atype = Set to "hull" or "intersect" to select anchor type. Default: "hull" -// cp = Centerpoint for determining intersection anchors or centering the shape. Determintes the base of the anchor vector. Can be "centroid", "mean", "box" or a 3D point. Default: "centroid" -// 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: Extruding a Compound Region. -// rgn1 = [for (d=[10:10:60]) circle(d=d,$fn=8)]; -// rgn2 = [square(30,center=false)]; -// rgn3 = [for (size=[10:10:20]) move([15,15],p=square(size=size, center=true))]; -// mrgn = union(rgn1,rgn2); -// orgn = difference(mrgn,rgn3); -// linear_sweep(orgn,height=20,convexity=16); -// Example: With Twist, Scale, Slices and Maxseg. -// rgn1 = [for (d=[10:10:60]) circle(d=d,$fn=8)]; -// rgn2 = [square(30,center=false)]; -// rgn3 = [for (size=[10:10:20]) move([15,15],p=square(size=size, center=true))]; -// mrgn = union(rgn1,rgn2); -// orgn = difference(mrgn,rgn3); -// linear_sweep(orgn,height=50,maxseg=2,slices=40,twist=180,scale=0.5,convexity=16); -// Example: Anchors on an Extruded Region -// rgn1 = [for (d=[10:10:60]) circle(d=d,$fn=8)]; -// rgn2 = [square(30,center=false)]; -// rgn3 = [for (size=[10:10:20]) move([15,15],p=square(size=size, center=true))]; -// mrgn = union(rgn1,rgn2); -// orgn = difference(mrgn,rgn3); -// linear_sweep(orgn,height=20,convexity=16) show_anchors(); -module linear_sweep(region, height=1, center, twist=0, scale=1, slices, maxseg, style="default", convexity, - spin=0, orient=UP, cp="centroid", anchor="origin", atype="hull") { - region = force_region(region); - dummy=assert(is_region(region),"Input is not a region"); - anchor = center ? "zcenter" : anchor; - anchors = [named_anchor("zcenter", [0,0,height/2], UP)]; - vnf = linear_sweep( - region, height=height, - twist=twist, scale=scale, - slices=slices, maxseg=maxseg, - style=style - ); - attachable(anchor,spin,orient, cp=cp, region=region, h=height, extent=atype=="hull", anchors=anchors) { - vnf_polyhedron(vnf, convexity=convexity); - children(); - } -} - - -function linear_sweep(region, height=1, center, twist=0, scale=1, slices, - maxseg, style="default", cp="centroid", atype="hull", anchor, spin=0, orient=UP) = - let( - region = force_region(region) - ) - assert(is_region(region), "Input is not a region") - let( - anchor = center ? "zcenter" : anchor, - anchors = [named_anchor("zcenter", [0,0,height/2], UP)], - regions = region_parts(region), - slices = default(slices, floor(twist/5+1)), - step = twist/slices, - hstep = height/slices, - trgns = [ - for (rgn=regions) [ - for (path=rgn) let( - p = cleanup_path(path), - path = is_undef(maxseg)? p : [ - for (seg=pair(p,true)) each - let(steps=ceil(norm(seg.y-seg.x)/maxseg)) - lerpn(seg.x, seg.y, steps, false) - ] - ) - rot(twist, p=scale([scale,scale],p=path)) - ] - ], - vnf = vnf_join([ - for (rgn = regions) - for (pathnum = idx(rgn)) let( - p = cleanup_path(rgn[pathnum]), - path = is_undef(maxseg)? p : [ - for (seg=pair(p,true)) each - let(steps=ceil(norm(seg.y-seg.x)/maxseg)) - lerpn(seg.x, seg.y, steps, false) - ], - verts = [ - for (i=[0:1:slices]) let( - sc = lerp(1, scale, i/slices), - ang = i * step, - h = i * hstep //- height/2 - ) scale([sc,sc,1], p=rot(ang, p=path3d(path,h))) - ] - ) vnf_vertex_array(verts, caps=false, col_wrap=true, style=style), - for (rgn = regions) vnf_from_region(rgn, ident(4), reverse=true), - for (rgn = trgns) vnf_from_region(rgn, up(height), reverse=false) - ]) - ) reorient(anchor,spin,orient, cp=cp, vnf=vnf, extent=atype=="hull", p=vnf, anchors=anchors); - // Section: Offset and 2D Boolean Set Operations diff --git a/skin.scad b/skin.scad index 03cc471..1f19886 100644 --- a/skin.scad +++ b/skin.scad @@ -501,6 +501,231 @@ function skin(profiles, slices, refine=1, method="direct", sampling, caps, close reorient(anchor,spin,orient,vnf=vnf,p=vnf,extent=atype=="hull",cp=cp); + +// Function&Module: linear_sweep() +// Usage: +// linear_sweep(region, height, [center], [slices], [twist], [scale], [style], [convexity]) {attachments}; +// Description: +// If called as a module, creates a polyhedron that is the linear extrusion of the given 2D region or polygon. +// If called as a function, returns a VNF that can be used to generate a polyhedron of the linear extrusion +// of the given 2D region or polygon. The benefit of using this, over using `linear_extrude region(rgn)` is +// that it supports `anchor`, `spin`, `orient` and attachments. You can also make more refined +// twisted extrusions by using `maxseg` to subsample flat faces. +// Note that the center option centers vertically using the named anchor "zcenter" whereas +// `anchor=CENTER` centers the entire shape relative to +// the shape's centroid, or other centerpoint you specify. The centerpoint can be "centroid", "mean", "box" or +// a custom point location. +// Arguments: +// region = The 2D [Region](regions.scad) or polygon that is to be extruded. +// height = The height to extrude the region. Default: 1 +// center = If true, the created polyhedron will be vertically centered. If false, it will be extruded upwards from the XY plane. Default: `false` +// slices = The number of slices to divide the shape into along the Z axis, to allow refinement of detail, especially when working with a twist. Default: `twist/5` +// maxseg = If given, then any long segments of the region will be subdivided to be shorter than this length. This can refine twisting flat faces a lot. Default: `undef` (no subsampling) +// twist = The number of degrees to rotate the shape clockwise around the Z axis, as it rises from bottom to top. Default: 0 +// scale = The amount to scale the shape, from bottom to top. Default: 1 +// style = The style to use when triangulating the surface of the object. Valid values are `"default"`, `"alt"`, or `"quincunx"`. +// convexity = Max number of surfaces any single ray could pass through. Module use only. +// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `"origin"` +// atype = Set to "hull" or "intersect" to select anchor type. Default: "hull" +// cp = Centerpoint for determining intersection anchors or centering the shape. Determintes the base of the anchor vector. Can be "centroid", "mean", "box" or a 3D point. Default: "centroid" +// 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: Extruding a Compound Region. +// rgn1 = [for (d=[10:10:60]) circle(d=d,$fn=8)]; +// rgn2 = [square(30,center=false)]; +// rgn3 = [for (size=[10:10:20]) move([15,15],p=square(size=size, center=true))]; +// mrgn = union(rgn1,rgn2); +// orgn = difference(mrgn,rgn3); +// linear_sweep(orgn,height=20,convexity=16); +// Example: With Twist, Scale, Slices and Maxseg. +// rgn1 = [for (d=[10:10:60]) circle(d=d,$fn=8)]; +// rgn2 = [square(30,center=false)]; +// rgn3 = [for (size=[10:10:20]) move([15,15],p=square(size=size, center=true))]; +// mrgn = union(rgn1,rgn2); +// orgn = difference(mrgn,rgn3); +// linear_sweep(orgn,height=50,maxseg=2,slices=40,twist=180,scale=0.5,convexity=16); +// Example: Anchors on an Extruded Region +// rgn1 = [for (d=[10:10:60]) circle(d=d,$fn=8)]; +// rgn2 = [square(30,center=false)]; +// rgn3 = [for (size=[10:10:20]) move([15,15],p=square(size=size, center=true))]; +// mrgn = union(rgn1,rgn2); +// orgn = difference(mrgn,rgn3); +// linear_sweep(orgn,height=20,convexity=16) show_anchors(); +module linear_sweep(region, height=1, center, twist=0, scale=1, slices, maxseg, style="default", convexity, + spin=0, orient=UP, cp="centroid", anchor="origin", atype="hull") { + region = force_region(region); + dummy=assert(is_region(region),"Input is not a region"); + anchor = center ? "zcenter" : anchor; + anchors = [named_anchor("zcenter", [0,0,height/2], UP)]; + vnf = linear_sweep( + region, height=height, + twist=twist, scale=scale, + slices=slices, maxseg=maxseg, + style=style + ); + attachable(anchor,spin,orient, cp=cp, region=region, h=height, extent=atype=="hull", anchors=anchors) { + vnf_polyhedron(vnf, convexity=convexity); + children(); + } +} + + +function linear_sweep(region, height=1, center, twist=0, scale=1, slices, + maxseg, style="default", cp="centroid", atype="hull", anchor, spin=0, orient=UP) = + let( + region = force_region(region) + ) + assert(is_region(region), "Input is not a region") + let( + anchor = center ? "zcenter" : anchor, + anchors = [named_anchor("zcenter", [0,0,height/2], UP)], + regions = region_parts(region), + slices = default(slices, floor(twist/5+1)), + step = twist/slices, + hstep = height/slices, + trgns = [ + for (rgn=regions) [ + for (path=rgn) let( + p = cleanup_path(path), + path = is_undef(maxseg)? p : [ + for (seg=pair(p,true)) each + let(steps=ceil(norm(seg.y-seg.x)/maxseg)) + lerpn(seg.x, seg.y, steps, false) + ] + ) + rot(twist, p=scale([scale,scale],p=path)) + ] + ], + vnf = vnf_join([ + for (rgn = regions) + for (pathnum = idx(rgn)) let( + p = cleanup_path(rgn[pathnum]), + path = is_undef(maxseg)? p : [ + for (seg=pair(p,true)) each + let(steps=ceil(norm(seg.y-seg.x)/maxseg)) + lerpn(seg.x, seg.y, steps, false) + ], + verts = [ + for (i=[0:1:slices]) let( + sc = lerp(1, scale, i/slices), + ang = i * step, + h = i * hstep //- height/2 + ) scale([sc,sc,1], p=rot(ang, p=path3d(path,h))) + ] + ) vnf_vertex_array(verts, caps=false, col_wrap=true, style=style), + for (rgn = regions) vnf_from_region(rgn, ident(4), reverse=true), + for (rgn = trgns) vnf_from_region(rgn, up(height), reverse=false) + ]) + ) reorient(anchor,spin,orient, cp=cp, vnf=vnf, extent=atype=="hull", p=vnf, anchors=anchors); + + + +// Function&Module: spiral_sweep() +// Usage: +// spiral_sweep(poly, h, r, turns, [higbee], [center], [r1], [r2], [d], [d1], [d2], [higbee1], [higbee2], [internal], [anchor], [spin], [orient]); +// vnf = spiral_sweep(poly, h, r, turns, ...); +// Description: +// Takes a closed 2D polygon path, centered on the XY plane, and sweeps/extrudes it along a 3D spiral path +// of a given radius, height and degrees of rotation. The origin in the profile traces out the helix of the specified radius. +// If turns is positive the path will be right-handed; if turns is negative the path will be left-handed. +// . +// Higbee specifies tapering applied to the ends of the extrusion and is given as the linear distance +// over which to taper. +// Arguments: +// poly = Array of points of a polygon path, to be extruded. +// h = height of the spiral to extrude along. +// r = Radius of the spiral to extrude along. Default: 50 +// turns = number of revolutions to spiral up along the height. +// --- +// d = Diameter of the spiral to extrude along. +// higbee = Length to taper thread ends over. +// higbee1 = Taper length at start +// higbee2 = Taper length at end +// internal = direction to taper the threads with higbee. If true threads taper outward; if false they taper inward. Default: false +// 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` +// center = If given, overrides `anchor`. A true value sets `anchor=CENTER`, false sets `anchor=BOTTOM`. +// Example: +// 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) = + let(higofs = pow(0.05,2)) // Smallest hig scale is the square root of this value + sqrt((1-higofs)*x+higofs); +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, higbee, center, r1, r2, d, d1, d2, higbee1, higbee2, internal=false, anchor=CENTER, spin=0, orient=UP) = + assert(is_num(turns) && turns != 0) + let( + twist = 360*turns, + higsample = 10, // Oversample factor for higbee tapering + bounds = pointlist_bounds(poly), + yctr = (bounds[0].y+bounds[1].y)/2, + xmin = bounds[0].x, + xmax = bounds[1].x, + poly = path3d(clockwise_polygon(poly)), + anchor = get_anchor(anchor,center,BOT,BOT), + r1 = get_radius(r1=r1, r=r, d1=d1, d=d, dflt=50), + r2 = get_radius(r1=r2, r=r, d1=d2, d=d, dflt=50), + sides = segs(max(r1,r2)), + dir = sign(twist), + ang_step = 360/sides*dir, + anglist = [for(ang = [0:ang_step:twist-EPSILON]) ang, + twist], + higbee1 = first_defined([higbee1, higbee, 0]), + higbee2 = first_defined([higbee2, higbee, 0]), + higang1 = 360 * higbee1 / (2 * r1 * PI), + higang2 = 360 * higbee2 / (2 * r2 * PI) + ) + assert(higbee1>=0 && higbee2>=0) + assert(higang1 < dir*twist/2,"Higbee1 is more than half the threads") + assert(higang2 < dir*twist/2,"Higbee2 is more than half the threads") + let( + interp_ang = [ + for(i=idx(anglist,e=-2)) + each lerpn(anglist[i],anglist[i+1], + (higang1>0 && higang1>dir*anglist[i+1] + || (higang2>0 && higang2>dir*(twist-anglist[i]))) ? ceil((anglist[i+1]-anglist[i])/ang_step*higsample) + : 1, + endpoint=false), + last(anglist) + ], + skewmat = affine3d_skew_xz(xa=atan2(r2-r1,h)), + points = [ + for (a = interp_ang) let ( + hsc = dir*a0?true:false, + style=higbee1>0 || higbee2>0 ? "quincunx" : "alt" + ) + ) + reorient(anchor,spin,orient, vnf=vnf, r1=r1, r2=r2, l=h, p=vnf); + + + +module spiral_sweep(poly, h, r, turns=1, higbee, center, r1, r2, d, d1, d2, higbee1, higbee2, internal=false, anchor=CENTER, spin=0, orient=UP) { + vnf = spiral_sweep(poly, h, r, turns, higbee, center, r1, r2, d, d1, d2, higbee1, higbee2, internal); + attachable(anchor,spin,orient, r1=r1, r2=r2, l=h) { + vnf_polyhedron(vnf, convexity=ceil(abs(2*turns))); + children(); + } +} + + + // Function&Module: path_sweep() // Usage: As module // path_sweep(shape, path, [method], [normal=], [closed=], [twist=], [twist_by_length=], [symmetry=], [last_normal=], [tangent=], [relaxed=], [caps=], [style=], [convexity=], [anchor=], [cp=], [spin=], [orient=], [atype=]) {attachments}; diff --git a/threading.scad b/threading.scad index fd03d9f..e55d019 100644 --- a/threading.scad +++ b/threading.scad @@ -1154,7 +1154,7 @@ module generic_threaded_nut( // pitch = Distance between threads. Default: 2mm/thread // thread_depth = Depth of threads from top to bottom. // flank_angle = Angle of thread faces to plane perpendicular to screw. Default: 15 degrees. -// twist = Number of degrees to rotate thread around. Default: 720 degrees. +// turns = Number of revolutions to rotate thread around. Default: 2. // --- // profile = If an asymmetrical thread profile is needed, it can be specified here. // starts = The number of thread starts. Default: 1 @@ -1203,18 +1203,18 @@ module generic_threaded_nut( // right(14)back(14)text("angle",size=4,halign="center"); // } // Examples: -// thread_helix(d=10, pitch=2, thread_depth=0.75, flank_angle=15, twist=900, $fn=72); -// thread_helix(d=10, pitch=2, thread_depth=0.75, flank_angle=15, twist=900, higbee=1, $fn=72); -// thread_helix(d=10, pitch=2, thread_depth=0.75, flank_angle=15, twist=720, higbee=2, internal=true, $fn=72); -// thread_helix(d=10, pitch=2, thread_depth=0.75, flank_angle=15, twist=360, left_handed=true, higbee=1, $fn=36); +// thread_helix(d=10, pitch=2, thread_depth=0.75, flank_angle=15, turns=2.5, $fn=72); +// thread_helix(d=10, pitch=2, thread_depth=0.75, flank_angle=15, turns=2.5, higbee=1, $fn=72); +// thread_helix(d=10, pitch=2, thread_depth=0.75, flank_angle=15, turns=2, higbee=2, internal=true, $fn=72); +// thread_helix(d=10, pitch=2, thread_depth=0.75, flank_angle=15, turns=1, left_handed=true, higbee=1, $fn=36); module thread_helix( - d, pitch, thread_depth, flank_angle, twist=720, + d, pitch, thread_depth, flank_angle, turns=2, profile, starts=1, left_handed=false, internal=false, d1, d2, higbee, higbee1, higbee2, 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*twist/360; + h = pitch*starts*turns; r1 = get_radius(d1=d1, d=d, dflt=10); r2 = get_radius(d1=d2, d=d, dflt=10); profile = is_def(profile) ? profile : @@ -1241,7 +1241,7 @@ module thread_helix( dir = left_handed? -1 : 1; attachable(anchor,spin,orient, r1=r1, r2=r2, l=h) { zrot_copies(n=starts) { - spiral_sweep(pline, h=h, r1=r1, r2=r2, twist=twist*dir, higbee=higbee, higbee1=higbee1, higbee2=higbee2, internal=internal, anchor=CENTER); + spiral_sweep(pline, h=h, r1=r1, r2=r2, turns=turns*dir, higbee=higbee, higbee1=higbee1, higbee2=higbee2, internal=internal, anchor=CENTER); } children(); } From 7e388293e87a186c7733ffe9db4d0e04066b7caf Mon Sep 17 00:00:00 2001 From: Adrian Mariano Date: Mon, 10 Jan 2022 21:23:09 -0500 Subject: [PATCH 5/7] add centering option to move() --- tests/test_transforms.scad | 4 ++++ transforms.scad | 18 ++++++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/tests/test_transforms.scad b/tests/test_transforms.scad index 2314594..2721d56 100644 --- a/tests/test_transforms.scad +++ b/tests/test_transforms.scad @@ -23,6 +23,10 @@ module test_move() { // Verify that module at least doesn't crash. move(x=-5) move(y=-5) move(z=-5) move([-5,-5,-5]) union(){}; move(x=5) move(y=5) move(z=5) move([5,5,5]) union(){}; + sq = square(10); + assert_equal(move("centroid", sq), move(-centroid(sq),sq)); + assert_equal(move("mean", vals), move(-mean(vals), vals)); + assert_equal(move("box", vals), move(-mean(pointlist_bounds(vals)),vals)); } test_move(); diff --git a/transforms.scad b/transforms.scad index d4eb4cc..53cfc1f 100644 --- a/transforms.scad +++ b/transforms.scad @@ -77,9 +77,10 @@ _NO_ARG = [true,[123232345],false]; // Usage: As Module // move([x=], [y=], [z=]) ... // move(v) ... -// Usage: Translate Points +// Usage: As a function to translate points, VNF, or Bezier patch // pts = move(v, p); // pts = move([x=], [y=], [z=], p=); +// pts = move(STRING, p); // Usage: Get Translation Matrix // mat = move(v); // mat = move([x=], [y=], [z=]); @@ -95,11 +96,12 @@ _NO_ARG = [true,[123232345],false]; // * Called as a function with a [bezier patch](beziers.scad) in the `p` argument, returns the translated patch. // * Called as a function with a [VNF structure](vnf.scad) in the `p` argument, returns the translated VNF. // * Called as a function with the `p` argument, returns the translated point or list of points. +// * Called as a function with the `p` argument set to a VNF or a polygon and `v` set to "centroid", "mean" or "box", translates the argument to the centroid, mean, or bounding box center respectively. // * Called as a function without a `p` argument, with a 2D offset vector `v`, returns an affine2d translation matrix. // * Called as a function without a `p` argument, with a 3D offset vector `v`, returns an affine3d translation matrix. // // Arguments: -// v = An [X,Y,Z] vector to translate by. +// v = An [X,Y,Z] vector to translate by. For function form with `p` is a point list or VNF, can be "centroid", "mean" or "box". // p = Either a point, or a list of points to be translated when used as a function. // --- // x = X axis translation. @@ -139,12 +141,24 @@ _NO_ARG = [true,[123232345],false]; // mat2d = move([2,3]); // Returns: [[1,0,2],[0,1,3],[0,0,1]] // mat3d = move([2,3,4]); // Returns: [[1,0,0,2],[0,1,0,3],[0,0,1,4],[0,0,0,1]] module move(v=[0,0,0], p, x=0, y=0, z=0) { + assert(!is_string(v),"Module form of `move()` does not accept string `v` arguments"); assert(is_undef(p), "Module form `move()` does not accept p= argument."); assert(is_vector(v) && (len(v)==3 || len(v)==2), "Invalid value for `v`") translate(point3d(v)+[x,y,z]) children(); } function move(v=[0,0,0], p=_NO_ARG, x=0, y=0, z=0) = + is_string(v) ? ( + assert(is_vnf(p) || is_path(p),"String movements only work with point lists and VNFs") + let( + center = v=="centroid" ? centroid(p) + : v=="mean" ? mean(p) + : v=="box" ? mean(pointlist_bounds(p)) + : assert(false,str("Unknown string movement ",v)) + ) + move(-center,p=p, x=x,y=y,z=z) + ) + : assert(is_vector(v) && (len(v)==3 || len(v)==2), "Invalid value for `v`") let( m = affine3d_translate(point3d(v)+[x,y,z]) From 05c5e4efe5d4d885713a479ce5ffef287c000d3a Mon Sep 17 00:00:00 2001 From: Adrian Mariano Date: Mon, 10 Jan 2022 21:50:32 -0500 Subject: [PATCH 6/7] bugfix --- skin.scad | 2 ++ 1 file changed, 2 insertions(+) diff --git a/skin.scad b/skin.scad index 1f19886..b7dfd9c 100644 --- a/skin.scad +++ b/skin.scad @@ -718,6 +718,8 @@ function spiral_sweep(poly, h, r, turns=1, higbee, center, r1, r2, d, d1, d2, hi module spiral_sweep(poly, h, r, turns=1, higbee, center, r1, r2, d, d1, d2, higbee1, higbee2, internal=false, anchor=CENTER, spin=0, orient=UP) { vnf = spiral_sweep(poly, h, r, turns, higbee, center, r1, r2, d, d1, d2, higbee1, higbee2, internal); + r1 = get_radius(r1=r1, r=r, d1=d1, d=d, dflt=50); + r2 = get_radius(r1=r2, r=r, d1=d2, d=d, dflt=50); attachable(anchor,spin,orient, r1=r1, r2=r2, l=h) { vnf_polyhedron(vnf, convexity=ceil(abs(2*turns))); children(); From 837c74c10a52b527c3b805b5adffefb4fed23bc0 Mon Sep 17 00:00:00 2001 From: Adrian Mariano Date: Mon, 10 Jan 2022 22:28:35 -0500 Subject: [PATCH 7/7] bugfixes --- bottlecaps.scad | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bottlecaps.scad b/bottlecaps.scad index a91b9ea..68ed811 100644 --- a/bottlecaps.scad +++ b/bottlecaps.scad @@ -195,7 +195,7 @@ module pco1810_cap(wall=2, texture="none", anchor=BOTTOM, spin=0, orient=UP) } up(wall) cyl(d=cap_id, h=tamper_ring_h+wall, anchor=BOTTOM); } - up(wall+2) thread_helix(d=thread_od-thread_depth*2, pitch=thread_pitch, thread_depth=thread_depth, flank_angle=flank_angle, twist=810/360, higbee=thread_depth, internal=true, anchor=BOTTOM); + up(wall+2) thread_helix(d=thread_od-thread_depth*2, pitch=thread_pitch, thread_depth=thread_depth, flank_angle=flank_angle, turns=810/360, higbee=thread_depth, internal=true, anchor=BOTTOM); } children(); } @@ -310,7 +310,7 @@ module pco1881_neck(wall=2, anchor="support-ring", spin=0, orient=UP) pitch=thread_pitch, thread_depth=thread_h+0.1, flank_angle=flank_angle, - twist=650/360, + turns=650/360, higbee=thread_h*2, anchor=TOP ); @@ -379,7 +379,7 @@ module pco1881_cap(wall=2, texture="none", anchor=BOTTOM, spin=0, orient=UP) } up(wall) cyl(d=28.58, h=11.2+wall, anchor=BOTTOM); } - up(wall+2) thread_helix(d=25.5, pitch=2.7, thread_depth=1.6, flank_angle=15, twist=650/360, higbee=1.6, internal=true, anchor=BOTTOM); + up(wall+2) thread_helix(d=25.5, pitch=2.7, thread_depth=1.6, flank_angle=15, turns=650/360, higbee=1.6, internal=true, anchor=BOTTOM); } children(); }