diff --git a/readme.md b/readme.md index 68e733d..bde06fe 100644 --- a/readme.md +++ b/readme.md @@ -5856,11 +5856,13 @@ Global constants, functions and modules. This file is used directly or indirectl | ```foot(x)``` | Foot to mm conversion | | ```in(list, x)``` | Returns true if ```x``` is an element in the ```list``` | | ```inch(x)``` | Inch to mm conversion (For fractional inches, 'inch(1 + 7/8)' will work as expected.) | +| ```limit(x, min, max)``` | Force x in range min <= x <= max | | ```m(x)``` | m to mm conversion | | ```mm(x)``` | Explicit mm specified | | ```no_point(str)``` | Replace decimal point in string with 'p' | | ```r2sides(r)``` | Replicates the OpenSCAD logic to calculate the number of sides from the radius | | ```r2sides4n(r)``` | Round up the number of sides to a multiple of 4 to ensure points land on all axes | +| ```slice(list, start = 0, end = undef)``` | Slice a list or string with Python type semantics | | ```sqr(x)``` | Returns the square of ```x``` | | ```yard(x)``` | Yard to mm conversion | diff --git a/tests/global.scad b/tests/global.scad index 3c34400..0f1006d 100644 --- a/tests/global.scad +++ b/tests/global.scad @@ -29,6 +29,23 @@ module globals() { translate([50, 0]) right_triangle(10, 20, 0); } + assert(slice("ABCD") == "ABCD"); + assert(slice("ABCD", 1) == "BCD"); + assert(slice("ABCD", 2) == "CD"); + assert(slice("ABCD", 3) == "D"); + assert(slice("ABCD", 4) == ""); + assert(slice("ABCD", 1, -1) == "BC"); + assert(slice("ABCD", 2, -1) == "C"); + assert(slice("ABCD", 3, -1) == ""); + assert(slice("ABCD", 4, -1) == ""); + assert(slice("ABCD", 0, -1) == "ABC"); + assert(slice("ABCD", 0, -2) == "AB"); + assert(slice("ABCD", 0, -3) == "A"); + assert(slice("ABCD", 0, -4) == ""); + assert(slice("ABCD", 0, 0) == ""); + assert(slice("ABCD", 0, 1) == "A"); + assert(slice("ABCD", 0, 2) == "AB"); + assert(slice("ABCD", 0, 3) == "ABC"); } rotate([70, 0, 315]) globals(); diff --git a/utils/core/global.scad b/utils/core/global.scad index b018de7..2a2bbeb 100644 --- a/utils/core/global.scad +++ b/utils/core/global.scad @@ -36,12 +36,22 @@ function in(list, x) = !!len([for(v = list) if(v == x) true]); function Len(x) = is_list(x) ? len(x) : 0; //! Returns the length of a list or 0 if ```x``` is not a list function r2sides(r) = $fn ? $fn : ceil(max(min(360/ $fa, r * 2 * PI / $fs), 5)); //! Replicates the OpenSCAD logic to calculate the number of sides from the radius function r2sides4n(r) = floor((r2sides(r) + 3) / 4) * 4; //! Round up the number of sides to a multiple of 4 to ensure points land on all axes +function limit(x, min, max) = max(min(x, max), min); //! Force x in range min <= x <= max module translate_z(z) translate([0, 0, z]) children(); //! Shortcut for Z only translations module vflip() rotate([180, 0, 0]) children(); //! Invert children by doing a 180° flip around the X axis module hflip() rotate([0, 180, 0]) children(); //! Invert children by doing a 180° flip around the Y axis module ellipse(xr, yr) scale([1, yr / xr]) circle4n(xr); //! Draw an ellipse +function slice_str(str, start, end, s ="") = start >= end ? s : slice_str(str, start + 1, end, str(s, str[start])); // Helper for slice() + +function slice(list, start = 0, end = undef) = let( //! Slice a list or string with Python type semantics + len = len(list), + start = limit(start < 0 ? len + start : start, 0, len), + end = is_undef(end) ? len : limit(end < 0 ? len + end : end, 0, len) + ) is_string(list) ? slice_str(list, start, end) : [for(i = [start : 1 : end - 1]) list[i]]; + + module extrude_if(h, center = true) //! Extrudes 2D object to 3D when ```h``` is nonzero, otherwise leaves it 2D if(h) linear_extrude(h, center = center, convexity = 2) // 3D