From f7b67232b2cf63984b2e0a42df55e22cc33974a4 Mon Sep 17 00:00:00 2001 From: Adrian Mariano Date: Tue, 2 Aug 2022 22:49:10 -0400 Subject: [PATCH 1/3] doc fix for ellipse --- shapes2d.scad | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/shapes2d.scad b/shapes2d.scad index f12b41e..9767581 100644 --- a/shapes2d.scad +++ b/shapes2d.scad @@ -305,9 +305,9 @@ module circle(r, d, points, corner, anchor=CENTER, spin=0) { // Function&Module: ellipse() // Usage: As a Module -// ellipse(r|d=, [realign=], [circum=], ...) [ATTACHMENTS]; +// ellipse(r|d=, [realign=], [circum=], [uniform=], ...) [ATTACHMENTS]; // Usage: As a Function -// path = ellipse(r|d=, [realign=], [circum=], ...); +// path = ellipse(r|d=, [realign=], [circum=], [uniform=], ...); // Topics: Shapes (2D), Paths (2D), Path Generators, Attachable // See Also: circle(), circle_2tangents(), circle_3points() // Description: @@ -315,13 +315,16 @@ module circle(r, d, points, corner, anchor=CENTER, spin=0) { // When called as a function, returns a 2D list of points (path) for a polygon that approximates a circle or ellipse of the given size. // By default the point list or shape is the same as the one you would get by scaling the output of {{circle()}}, but with this module your // attachments to the ellipse will retain their dimensions, whereas scaling a circle with attachments will also scale the attachments. -// If you set unifom to true then you will get a polygon with congruent sides whose vertices lie on the ellipse. +// If you set `uniform` to true then you will get a polygon with congruent sides whose vertices lie on the ellipse. The `circum` option +// requests a polygon that circumscribes the requested ellipse (so the specified ellipse will fit into the resulting polygon). Note that +// you cannot gives `circum=true` and `uniform=true`. // Arguments: // r = Radius of the circle or pair of semiaxes of ellipse // --- // d = Diameter of the circle or a pair giving the full X and Y axis lengths. // realign = If false starts the approximate ellipse with a point on the X+ axis. If true the midpoint of a side is on the X+ axis and the first point of the polygon is below the X+ axis. This can result in a very different polygon when $fn is small. Default: false -// circum = If true, the polygon that approximates the circle will be upsized slightly to circumscribe the theoretical circle. If false, it inscribes the theoretical circle. Default: false +// uniform = If true, the polygon that approximates the circle will have segments of equal length. Only works if `circum=false`. Default: false +// circum = If true, the polygon that approximates the circle will be upsized slightly to circumscribe the theoretical circle. If false, it inscribes the theoretical circle. If this is true then `uniform` must be false. 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` // Example(2D): By Radius From b9713845f79cc078f7fd92b23bb48a84364ffa8e Mon Sep 17 00:00:00 2001 From: Adrian Mariano Date: Sat, 6 Aug 2022 10:28:36 -0400 Subject: [PATCH 2/3] added n= option to supershape to give number of points --- shapes2d.scad | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/shapes2d.scad b/shapes2d.scad index 9767581..52d5efa 100644 --- a/shapes2d.scad +++ b/shapes2d.scad @@ -1399,18 +1399,22 @@ function _superformula(theta,m1,m2,n1,n2=1,n3=1,a=1,b=1) = // Function&Module: supershape() // Usage: As Module -// supershape(step, [m1=], [m2=], [n1=], [n2=], [n3=], [a=], [b=], [r=/d=]) [ATTACHMENTS]; +// supershape([step],[n=], [m1=], [m2=], [n1=], [n2=], [n3=], [a=], [b=], [r=/d=]) [ATTACHMENTS]; // Usage: As Function -// path = supershape(step, [m1=], [m2=], [n1=], [n2=], [n3=], [a=], [b=], [r=/d=]); +// path = supershape([step], [n=], [m1=], [m2=], [n1=], [n2=], [n3=], [a=], [b=], [r=/d=]); // Topics: Shapes (2D), Paths (2D), Path Generators, Attachable // See Also: circle(), ellipse() // Description: // When called as a function, returns a 2D path for the outline of the [Superformula](https://en.wikipedia.org/wiki/Superformula) shape. // When called as a module, creates a 2D [Superformula](https://en.wikipedia.org/wiki/Superformula) shape. // Note that the "hull" type anchoring (the default) is more intuitive for concave star-like shapes, but the anchor points do not -// necesarily lie on the line of the anchor vector, which can be confusing, especially for simpler, ellipse-like shapes. +// necesarily lie on the line of the anchor vector, which can be confusing, especially for simpler, ellipse-like shapes. +// Note that the default step angle of 0.5 is very fine and can be slow, but due to the complex curves of the supershape, +// many points are often required to give a good result. // Arguments: -// step = The angle step size for sampling the superformula shape. Smaller steps are slower but more accurate. +// step = The angle step size for sampling the superformula shape. Smaller steps are slower but more accurate. Default: 0.5 +// --- +// n = Produce n points as output. Alternative to step. Not to be confused with shape parameters n1 and n2. // m1 = The m1 argument for the superformula. Default: 4. // m2 = The m2 argument for the superformula. Default: m1. // n1 = The n1 argument for the superformula. Default: 1. @@ -1419,7 +1423,6 @@ function _superformula(theta,m1,m2,n1,n2=1,n3=1,a=1,b=1) = // a = The a argument for the superformula. Default: 1. // b = The b argument for the superformula. Default: a. // r = Radius of the shape. Scale shape to fit in a circle of radius r. -// --- // d = Diameter of the shape. Scale shape to fit in a circle of diameter d. // 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` @@ -1449,26 +1452,25 @@ function _superformula(theta,m1,m2,n1,n2=1,n3=1,a=1,b=1) = // Examples: // linear_extrude(height=0.3, scale=0) supershape(step=1, m1=6, n1=0.4, n2=0, n3=6); // linear_extrude(height=5, scale=0) supershape(step=1, b=3, m1=6, n1=3.8, n2=16, n3=10); -function supershape(step=0.5, m1=4, m2, n1=1, n2, n3, a=1, b, r, d,anchor=CENTER, spin=0, atype="hull") = +function supershape(step=0.5, n, m1=4, m2, n1=1, n2, n3, a=1, b, r, d,anchor=CENTER, spin=0, atype="hull") = assert(in_list(atype, _ANCHOR_TYPES), "Anchor type must be \"hull\" or \"intersect\"") let( - + n = first_defined([n, ceil(360/step)]), + angs = lerpn(360,0,n,endpoint=false), r = get_radius(r=r, d=d, dflt=undef), m2 = is_def(m2) ? m2 : m1, n2 = is_def(n2) ? n2 : n1, n3 = is_def(n3) ? n3 : n2, b = is_def(b) ? b : a, - steps = ceil(360/step), - step = 360/steps, - angs = [for (i = [0:steps]) step*i], - rads = [for (theta = angs) _superformula(theta=theta,m1=m1,m2=m2,n1=n1,n2=n2,n3=n3,a=a,b=b)], - scale = is_def(r) ? r/max(rads) : 1, - path = [for (i = [steps:-1:1]) let(a=angs[i]) scale*rads[i]*[cos(a), sin(a)]] + // superformula returns r(theta), the point in polar coordinates + rvals = [for (theta = angs) _superformula(theta=theta,m1=m1,m2=m2,n1=n1,n2=n2,n3=n3,a=a,b=b)], + scale = is_def(r) ? r/max(rvals) : 1, + path = [for (i=idx(angs)) scale*rvals[i]*[cos(angs[i]), sin(angs[i])]] ) reorient(anchor,spin, two_d=true, path=path, p=path, extent=atype=="hull"); -module supershape(step=0.5,m1=4,m2=undef,n1,n2=undef,n3=undef,a=1,b=undef, r=undef, d=undef, anchor=CENTER, spin=0, atype="hull") { +module supershape(step=0.5,n,m1=4,m2=undef,n1,n2=undef,n3=undef,a=1,b=undef, r=undef, d=undef, anchor=CENTER, spin=0, atype="hull") { check = assert(in_list(atype, _ANCHOR_TYPES), "Anchor type must be \"hull\" or \"intersect\""); - path = supershape(step=step,m1=m1,m2=m2,n1=n1,n2=n2,n3=n3,a=a,b=b,r=r,d=d); + path = supershape(step=step,n=n,m1=m1,m2=m2,n1=n1,n2=n2,n3=n3,a=a,b=b,r=r,d=d); attachable(anchor,spin,extent=atype=="hull", two_d=true, path=path) { polygon(path); children(); From 07805a1918d244e9b5647925b9155fefa528ee3d Mon Sep 17 00:00:00 2001 From: Adrian Mariano Date: Sun, 14 Aug 2022 20:33:31 -0400 Subject: [PATCH 3/3] expose _str_cmp as substr_match and doc tweaks --- strings.scad | 79 +++++++++++++++++++++++++---------------- tests/test_strings.scad | 11 ++++++ 2 files changed, 60 insertions(+), 30 deletions(-) diff --git a/strings.scad b/strings.scad index 3682779..cef528e 100644 --- a/strings.scad +++ b/strings.scad @@ -13,7 +13,7 @@ // Function: substr() // Usage: -// substr(str, [pos], [len]) +// newstr = substr(str, [pos], [len]); // Description: // Returns a substring from a string start at position `pos` with length `len`, or // if `len` isn't given, the rest of the string. @@ -39,7 +39,7 @@ function _substr(str,pos,len,substr="") = // Function: suffix() // Usage: -// suffix(str,len) +// newstr = suffix(str,len); // Description: // Returns the last `len` characters from the input string `str`. // If `len` is longer than the length of `str`, then the entirety of `str` is returned. @@ -56,7 +56,7 @@ function suffix(str,len) = // Function: str_find() // Usage: -// str_find(str,pattern,[last=],[all=],[start=]) +// ind = str_find(str,pattern,[last=],[all=],[start=]); // Description: // Searches input string `str` for the string `pattern` and returns the index or indices of the matches in `str`. // By default `str_find()` returns the index of the first match in `str`. If `last` is true then it returns the index of the last match. @@ -94,37 +94,56 @@ function str_find(str,pattern,start=undef,last=false,all=false) = _str_find_first(str,pattern,len(str)-len(pattern),start); function _str_find_first(str,pattern,max_sindex,sindex) = - sindex<=max_sindex && !_str_cmp(str,sindex, pattern)? + sindex<=max_sindex && !substr_match(str,sindex, pattern)? _str_find_first(str,pattern,max_sindex,sindex+1) : (sindex <= max_sindex ? sindex : undef); function _str_find_last(str,pattern,sindex) = - sindex>=0 && !_str_cmp(str,sindex, pattern)? + sindex>=0 && !substr_match(str,sindex, pattern)? _str_find_last(str,pattern,sindex-1) : (sindex >=0 ? sindex : undef); function _str_find_all(str,pattern) = pattern == "" ? count(len(str)) : - [for(i=[0:1:len(str)-len(pattern)]) if (_str_cmp(str,i,pattern)) i]; + [for(i=[0:1:len(str)-len(pattern)]) if (substr_match(str,i,pattern)) i]; + +// Function: substr_match() +// Usage +// bool = substr_match(str,start,pattern); +// Description: +// Returns true if the string `pattern` matches the string `str` starting +// at `str[start]`. If the string is too short for the pattern, or +// `start` is out of bounds---either negative or beyond the end of the +// string---then substr_match returns false. +// Arguments: +// str = String to search +// start = Starting index for search in str +// pattern = String pattern to search for +// Examples: +// substr_match("abcde",2,"cd"); // Returns true +// substr_match("abcde",2,"cx"); // Returns false +// substr_match("abcde",2,"cdef"); // Returns false +// substr_match("abcde",-2,"cd"); // Returns false +// substr_match("abcde",19,"cd"); // Returns false +// substr_match("abc",1,""); // Returns true -// _str_cmp(str,sindex,pattern) -// returns true if the string pattern matches the string -// starting at index position sindex in the string. // // This is carefully optimized for speed. Precomputing the length // cuts run time in half when the string is long. Two other string // comparison methods were slower. -function _str_cmp(str,sindex,pattern) = - len(str)-sindex