From a81b6c6931cc0c98167ab7f6cf99bd687bedd931 Mon Sep 17 00:00:00 2001 From: Adrian Mariano Date: Tue, 9 Nov 2021 22:27:55 -0500 Subject: [PATCH] misc tweaks --- comparisons.scad | 47 +++++++++++++ cubetruss.scad | 7 +- drawing.scad | 86 ++++++++++++++++++++++- lists.scad | 133 ++++++------------------------------ mutators.scad | 2 +- nema_steppers.scad | 10 +-- paths.scad | 4 +- shapes2d.scad | 81 ++-------------------- sliders.scad | 4 +- tests/test_all.scad | 32 --------- tests/test_comparisons.scad | 16 +++++ tests/test_lists.scad | 23 ------- tests/test_vectors.scad | 15 +--- vectors.scad | 121 ++++++++++++++------------------ 14 files changed, 242 insertions(+), 339 deletions(-) delete mode 100644 tests/test_all.scad diff --git a/comparisons.scad b/comparisons.scad index 9e33734..3ea5d07 100644 --- a/comparisons.scad +++ b/comparisons.scad @@ -290,6 +290,53 @@ function compare_lists(a, b) = +// Section: Finding the index of the minimum or maximum of a list + + +// Function: min_index() +// Usage: +// idx = min_index(vals); +// idxlist = min_index(vals, all=true); +// Topics: List Handling +// See Also: max_index(), is_increasing(), is_decreasing() +// Description: +// Returns the index of the first occurrence of the minimum value in the given list. +// If `all` is true then returns a list of all indices where the minimum value occurs. +// Arguments: +// vals = vector of values +// all = set to true to return indices of all occurences of the minimum. Default: false +// Example: +// a = min_index([5,3,9,6,2,7,8,2,1]); // Returns: 8 +// b = min_index([5,3,9,6,2,7,8,2,7],all=true); // Returns: [4,7] +function min_index(vals, all=false) = + assert( is_vector(vals) && len(vals)>0 , "Invalid or empty list of numbers.") + all ? search(min(vals),vals,0) : search(min(vals), vals)[0]; + + +// Function: max_index() +// Usage: +// idx = max_index(vals); +// idxlist = max_index(vals, all=true); +// Topics: List Handling +// See Also: min_index(), is_increasing(), is_decreasing() +// Description: +// Returns the index of the first occurrence of the maximum value in the given list. +// If `all` is true then returns a list of all indices where the maximum value occurs. +// Arguments: +// vals = vector of values +// all = set to true to return indices of all occurences of the maximum. Default: false +// Example: +// max_index([5,3,9,6,2,7,8,9,1]); // Returns: 2 +// max_index([5,3,9,6,2,7,8,9,1],all=true); // Returns: [2,7] +function max_index(vals, all=false) = + assert( is_vector(vals) && len(vals)>0 , "Invalid or empty list of numbers.") + all ? search(max(vals),vals,0) : search(max(vals), vals)[0]; + + + + + + // Section: Dealing with duplicate list entries diff --git a/cubetruss.scad b/cubetruss.scad index 369036a..7ee58f4 100644 --- a/cubetruss.scad +++ b/cubetruss.scad @@ -255,7 +255,7 @@ module cubetruss_foot(w=1, size, strut, clipthick, anchor=CENTER, spin=0, orient down(clipthick) { // Base up(clipthick/2) { - cuboid([w*(size-strut)+strut+2*clipthick, size-2*strut, clipthick], chamfer=strut, edges=edges("Z")); + cuboid([w*(size-strut)+strut+2*clipthick, size-2*strut, clipthick], chamfer=strut, edges="Z"); } // Walls @@ -490,7 +490,7 @@ module cubetruss(extents=6, clips=[], bracing, size, strut, clipthick, anchor=CE // Creates a corner cubetruss with extents jutting out in one or more directions. // Arguments: // h = The number of cubes high to make the base and horizontal extents. -// extents = The number of cubes to extend beyond the corner. If given as a vector of cube counts, gives the number of cubes to extend right, back, left, front, and up in order. +// extents = The number of cubes to extend beyond the corner. If given as a vector of cube counts, gives the number of cubes to extend right, back, left, front, and up in order. If the vector is shorter than length 5 the extra cube counts are taken to be zero. // size = The length of each side of the cubetruss cubes. Default: `$cubetruss_size` (usually 30) // strut = The width of the struts on the cubetruss cubes. Default: `$cubetruss_strut_size` (usually 3) // bracing = If true, adds internal cross-braces. Default: `$cubetruss_bracing` (usually true) @@ -511,7 +511,8 @@ module cubetruss_corner(h=1, extents=[1,1,0,0,1], bracing, size, strut, clipthic strut = is_undef(strut)? $cubetruss_strut_size : strut; bracing = is_undef(bracing)? $cubetruss_bracing : bracing; clipthick = is_undef(clipthick)? $cubetruss_clip_thickness : clipthick; - exts = is_vector(extents)? list_fit(extents,5,fill=0) : [extents, extents, 0, 0, extents]; + exts = is_vector(extents)? list_pad(extents,5,fill=0) : [extents, extents, 0, 0, extents]; + dummy = assert(len(exts)==5, "Input extents must be a scalar or vector with length 5 or less."); s = [cubetruss_dist(exts[0]+1+exts[2],1), cubetruss_dist(exts[1]+1+exts[3],1), cubetruss_dist(h+exts[4],1)]; offset = [cubetruss_dist(exts[0]-exts[2],0), cubetruss_dist(exts[1]-exts[3],0), cubetruss_dist(h+exts[4]-1,0)]/2; attachable(anchor,spin,orient, size=s, offset=offset) { diff --git a/drawing.scad b/drawing.scad index 2a8726f..7968a0a 100644 --- a/drawing.scad +++ b/drawing.scad @@ -699,10 +699,13 @@ function arc(N, r, angle, d, cp, points, width, thickness, start, wedge=false, l ); -module arc(N, r, angle, d, cp, points, width, thickness, start, wedge=false) +module arc(N, r, angle, d, cp, points, width, thickness, start, wedge=false, anchor=CENTER, spin=0) { path = arc(N=N, r=r, angle=angle, d=d, cp=cp, points=points, width=width, thickness=thickness, start=start, wedge=wedge); - polygon(path); + attachable(anchor,spin, two_d=true, path=path) { + polygon(path); + children(); + } } @@ -1018,4 +1021,83 @@ function _turtle_command(command, parm, parm2, state, index) = []; +// Section: Debugging polygons + +// Module: debug_polygon() +// Usage: +// debug_polygon(points, paths, [vertices=], [edges=], [convexity=], [size=]); +// Description: +// A drop-in replacement for `polygon()` that renders and labels the path points and +// edges. The start of each path is marked with a blue circle and the end with a pink diamond. +// You can suppress the display of vertex or edge labeling using the `vertices` and `edges` arguments. +// Arguments: +// points = The array of 2D polygon vertices. +// paths = The path connections between the vertices. +// --- +// vertices = if true display vertex labels and start/end markers. Default: true +// edges = if true display edge labels. Default: true +// convexity = The max number of walls a ray can pass through the given polygon paths. +// size = The base size of the line and labels. +// Example(Big2D): +// debug_polygon( +// points=concat( +// regular_ngon(or=10, n=8), +// regular_ngon(or=8, n=8) +// ), +// paths=[ +// [for (i=[0:7]) i], +// [for (i=[15:-1:8]) i] +// ] +// ); +module debug_polygon(points, paths, vertices=true, edges=true, convexity=2, size=1) +{ + paths = is_undef(paths)? [count(points)] : + is_num(paths[0])? [paths] : + paths; + echo(points=points); + echo(paths=paths); + linear_extrude(height=0.01, convexity=convexity, center=true) { + polygon(points=points, paths=paths, convexity=convexity); + } + dups = vector_search(points, EPSILON, points); + + if (vertices) color("red") { + for (ind=dups){ + numstr = str_join([for(i=ind) str(i)],","); + up(0.2) { + translate(points[ind[0]]) { + linear_extrude(height=0.1, convexity=10, center=true) { + text(text=numstr, size=size, halign="center", valign="center"); + } + } + } + } + } + if (edges) + for (j = [0:1:len(paths)-1]) { + path = paths[j]; + if (vertices){ + translate(points[path[0]]) { + color("cyan") up(0.1) cylinder(d=size*1.5, h=0.01, center=false, $fn=12); + } + translate(points[path[len(path)-1]]) { + color("pink") up(0.11) cylinder(d=size*1.5, h=0.01, center=false, $fn=4); + } + } + for (i = [0:1:len(path)-1]) { + midpt = (points[path[i]] + points[path[(i+1)%len(path)]])/2; + color("blue") { + up(0.2) { + translate(midpt) { + linear_extrude(height=0.1, convexity=10, center=true) { + text(text=str(chr(65+j),i), size=size/2, halign="center", valign="center"); + } + } + } + } + } + } +} + + // vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/lists.scad b/lists.scad index 5da22ed..ab557e6 100644 --- a/lists.scad +++ b/lists.scad @@ -172,24 +172,6 @@ function in_list(val,list,idx) = : [for(hit=allhits) if (list[hit][idx]==val) 1] != []; -// Function: add_scalar() -// Usage: -// v = add_scalar(v, s); -// Topics: List Handling -// Description: -// Given a list and a scalar, returns the list with the scalar added to each item in it. -// If given a list of arrays, recursively adds the scalar to the each array. -// Arguments: -// v = The initial array. -// s = A scalar value to add to every item in the array. -// Example: -// a = add_scalar([1,2,3],3); // Returns: [4,5,6] -// b = add_scalar([[1,2,3],[3,4,5]],3); // Returns: [[4,5,6],[6,7,8]] -function add_scalar(v,s) = - is_finite(s) ? [for (x=v) is_list(x)? add_scalar(x,s) : is_finite(x) ? x+s: x] : v; - - - // Section: List Indexing @@ -620,6 +602,25 @@ function repeat_entries(list, N, exact=true) = [for(i=[0:length-1]) each repeat(list[i],reps[i])]; +// Function: list_pad() +// Usage: +// arr = list_pad(array, minlen, [fill]); +// Topics: List Handling +// See Also: force_list(), scalar_vec3() +// Description: +// If the list `array` is shorter than `minlen` length, pad it to length with the value given in `fill`. +// Arguments: +// array = A list. +// minlen = The minimum length to pad the list to. +// fill = The value to pad the list with. Default: `undef` +// Example: +// list = [3,4,5]; +// nlist = list_pad(list,5,23); // Returns: [3,4,5,23,23] +function list_pad(array, minlen, fill) = + assert(is_list(array), "Invalid input." ) + concat(array,repeat(fill,minlen-len(array))); + + // Function: list_set() // Usage: // list = list_set(list, indices, values, [dflt], [minlen]); @@ -812,70 +813,6 @@ function list_remove_values(list,values=[],all=false) = ]; -// Section: List Length Manipulation - - -// Function: list_pad() -// Usage: -// arr = list_pad(array, minlen, [fill]); -// Topics: List Handling -// See Also: list_trim(), list_fit() -// Description: -// If the list `array` is shorter than `minlen` length, pad it to length with the value given in `fill`. -// Arguments: -// array = A list. -// minlen = The minimum length to pad the list to. -// fill = The value to pad the list with. Default: `undef` -// Example: -// list = [3,4,5]; -// nlist = list_pad(list,5,23); // Returns: [3,4,5,23,23] -function list_pad(array, minlen, fill) = - assert(is_list(array), "Invalid input." ) - concat(array,repeat(fill,minlen-len(array))); - - -// Function: list_trim() -// Usage: -// arr = list_trim(array, maxlen); -// Topics: List Handling -// See Also: list_pad(), list_fit() -// Description: -// If the list `array` is longer than `maxlen` length, truncates it to be `maxlen` items long. -// Arguments: -// array = A list. -// minlen = The minimum length to pad the list to. -// Example: -// list = [3,4,5,6,7,8]; -// nlist = list_trim(list,4); // Returns: [3,4,5,6] -function list_trim(array, maxlen) = - assert(is_list(array), "Invalid input." ) - [for (i=[0:1:min(len(array),maxlen)-1]) array[i]]; - - -// Function: list_fit() -// Usage: -// arr = list_fit(array, length, fill); -// Topics: List Handling -// See Also: list_pad(), list_trim() -// Description: -// If the list `array` is longer than `length` items long, truncates it to be exactly `length` items long. -// If the list `array` is shorter than `length` items long, pad it to length with the value given in `fill`. -// Arguments: -// array = A list. -// minlen = The minimum length to pad the list to. -// fill = The value to pad the list with. Default: `undef` -// Example: -// list = [3,4,5,6]; -// nlist = list_fit(list,3); // Returns: [3,4,5] -// Example: -// list = [3,4,5,6]; -// nlist = list_fit(list,6,23); // Returns: [3,4,5,6,23,23] -function list_fit(array, length, fill) = - assert(is_list(array), "Invalid input." ) - let(l=len(array)) - l==length ? array : - l> length ? list_trim(array,length) - : list_pad(array,length,fill); // Section: Iteration Helpers @@ -1057,8 +994,6 @@ function permutations(l,n=2) = // Section: Changing list structure - - // Function: list_to_matrix() // Usage: // groups = list_to_matrix(v, [cnt], [dflt]); @@ -1142,36 +1077,6 @@ function zip(a,b,c) = [for (i=[0:1:n-1]) [for (x=a) x[i]]]; -// Function: zip_long() -// Usage: -// pairs = zip_long(a,b); -// triples = zip_long(a,b,c); -// quads = zip_long([LIST1,LIST2,LIST3,LIST4]); -// Topics: List Handling, Iteration -// See Also: zip() -// Description: -// Zips together two or more lists into a single list. For example, if you have two -// lists [3,4,5], and [8,7,6], and zip them together, you get [ [3,8],[4,7],[5,6] ]. -// The list returned will be as long as the longest list passed to zip_long(), with -// shorter lists padded by the value in `fill`. -// Arguments: -// a = The first list, or a list of lists if b and c are not given. -// b = The second list, if given. -// c = The third list, if given. -// fill = The value to pad shorter lists with. Default: undef -// Example: -// a = [9,8,7,6]; b = [1,2,3]; -// for (p=zip_long(a,b,fill=88)) echo(p); -// // ECHO: [9,1] -// // ECHO: [8,2] -// // ECHO: [7,3] -// // ECHO: [6,88]] -function zip_long(a,b,c,fill) = - b!=undef? zip_long([a,b,if (c!=undef) c],fill=fill) : - let(n = max_length(a)) - [for (i=[0:1:n-1]) [for (x=a) i -include -include -include -include -include -include -include -include -include -include -include -include -include -include -include -include -include -include -include -include -include -include -include -include -include -include -include -include -include -include -include diff --git a/tests/test_comparisons.scad b/tests/test_comparisons.scad index e78f336..b732521 100644 --- a/tests/test_comparisons.scad +++ b/tests/test_comparisons.scad @@ -391,3 +391,19 @@ module test_compare_lists() { } test_compare_lists(); + + +module test_min_index() { + assert(min_index([5,3,9,6,2,7,8,2,1])==8); + assert(min_index([5,3,9,6,2,7,8,2,7],all=true)==[4,7]); +} +test_min_index(); + + +module test_max_index() { + assert(max_index([5,3,9,6,2,7,8,9,1])==2); + assert(max_index([5,3,9,6,2,7,8,9,7],all=true)==[2,7]); +} +test_max_index(); + + diff --git a/tests/test_lists.scad b/tests/test_lists.scad index 1c0aa62..1260a15 100644 --- a/tests/test_lists.scad +++ b/tests/test_lists.scad @@ -241,22 +241,6 @@ module test_list_pad() { test_list_pad(); -module test_list_trim() { - assert(list_trim([4,5,6], 5) == [4,5,6]); - assert(list_trim([4,5,6,7,8], 5) == [4,5,6,7,8]); - assert(list_trim([3,4,5,6,7,8,9], 5) == [3,4,5,6,7]); -} -test_list_trim(); - - -module test_list_fit() { - assert(list_fit([4,5,6], 5, 8) == [4,5,6,8,8]); - assert(list_fit([4,5,6,7,8], 5, 8) == [4,5,6,7,8]); - assert(list_fit([3,4,5,6,7,8,9], 5, 8) == [3,4,5,6,7]); -} -test_list_fit(); - - module test_idx() { colors = ["red", "green", "blue", "cyan"]; assert([for (i=idx(colors)) i] == [0,1,2,3]); @@ -327,13 +311,6 @@ test_set_intersection(); // Arrays -module test_add_scalar() { - assert(add_scalar([1,2,3],3) == [4,5,6]); - assert(add_scalar([[1,2,3],[3,4,5]],3) == [[4,5,6],[6,7,8]]); -} -test_add_scalar(); - - module test_force_list() { assert_equal(force_list([3,4,5]), [3,4,5]); diff --git a/tests/test_vectors.scad b/tests/test_vectors.scad index 4c6408a..e9dea30 100644 --- a/tests/test_vectors.scad +++ b/tests/test_vectors.scad @@ -242,19 +242,10 @@ module test_vector_nearest(){ test_vector_nearest(); - -module test_min_index() { - assert(min_index([5,3,9,6,2,7,8,2,1])==8); - assert(min_index([5,3,9,6,2,7,8,2,7],all=true)==[4,7]); +module test_add_scalar() { + assert(add_scalar([1,2,3],3) == [4,5,6]); } -test_min_index(); - - -module test_max_index() { - assert(max_index([5,3,9,6,2,7,8,9,1])==2); - assert(max_index([5,3,9,6,2,7,8,9,7],all=true)==[2,7]); -} -test_max_index(); +test_add_scalar(); diff --git a/vectors.scad b/vectors.scad index 687f0d4..819b2bc 100644 --- a/vectors.scad +++ b/vectors.scad @@ -6,7 +6,7 @@ ////////////////////////////////////////////////////////////////////// -// Section: Vector Manipulation +// Section: Vector Testing // Function: is_vector() @@ -42,14 +42,24 @@ function is_vector(v, length, zero, all_nonzero=false, eps=EPSILON) = && (!all_nonzero || all_nonzero(v)) ; -// Function: v_theta() -// Usage: -// theta = v_theta([X,Y]); + +// Section: Scalar operations on vectors + +// Function: add_scalar() +// Usage: +// v_new = add_scalar(v, s); +// Topics: List Handling // Description: -// Given a vector, returns the angle in degrees counter-clockwise from X+ on the XY plane. -function v_theta(v) = - assert( is_vector(v,2) || is_vector(v,3) , "Invalid vector") - atan2(v.y,v.x); +// Given a vector and a scalar, returns the vector with the scalar added to each item in it. +// Arguments: +// v = The initial array. +// s = A scalar value to add to every item in the array. +// Example: +// a = add_scalar([1,2,3],3); // Returns: [4,5,6] +function add_scalar(v,s) = + assert(is_vector(v), "Input v must be a vector") + assert(is_finite(s), "Input s must be a finite scalar") + [for(entry=v) entry+s]; // Function: v_mul() @@ -132,26 +142,7 @@ function v_lookup(x, v) = lerp(lo,hi,u); -// Function: pointlist_bounds() -// Usage: -// pt_pair = pointlist_bounds(pts); -// Topics: Geometry, Bounding Boxes, Bounds -// Description: -// Finds the bounds containing all the points in `pts` which can be a list of points in any dimension. -// Returns a list of two items: a list of the minimums and a list of the maximums. For example, with -// 3d points `[[MINX, MINY, MINZ], [MAXX, MAXY, MAXZ]]` -// Arguments: -// pts = List of points. -function pointlist_bounds(pts) = - assert(is_path(pts,dim=undef,fast=true) , "Invalid pointlist." ) - let( - select = ident(len(pts[0])), - spread = [ - for(i=[0:len(pts[0])-1]) - let( spreadi = pts*select[i] ) - [ min(spreadi), max(spreadi) ] - ] - ) transpose(spread); +// Section: Vector Properties // Function: unit() @@ -176,6 +167,17 @@ function unit(v, error=[[["ASSERT"]]]) = v/norm(v); +// Function: v_theta() +// Usage: +// theta = v_theta([X,Y]); +// Description: +// Given a vector, returns the angle in degrees counter-clockwise from X+ on the XY plane. +function v_theta(v) = + assert( is_vector(v,2) || is_vector(v,3) , "Invalid vector") + atan2(v.y,v.x); + + + // Function: vector_angle() // Usage: // vector_angle(v1,v2); @@ -263,50 +265,33 @@ function vector_axis(v1,v2=undef,v3=undef) = -// Function: min_index() -// Usage: -// idx = min_index(vals); -// idxlist = min_index(vals, all=true); -// Topics: List Handling -// See Also: max_index(), is_increasing(), is_decreasing() -// Description: -// Returns the index of the first occurrence of the minimum value in the given list. -// If `all` is true then returns a list of all indices where the minimum value occurs. -// Arguments: -// vals = vector of values -// all = set to true to return indices of all occurences of the minimum. Default: false -// Example: -// a = min_index([5,3,9,6,2,7,8,2,1]); // Returns: 8 -// b = min_index([5,3,9,6,2,7,8,2,7],all=true); // Returns: [4,7] -function min_index(vals, all=false) = - assert( is_vector(vals) && len(vals)>0 , "Invalid or empty list of numbers.") - all ? search(min(vals),vals,0) : search(min(vals), vals)[0]; - - -// Function: max_index() -// Usage: -// idx = max_index(vals); -// idxlist = max_index(vals, all=true); -// Topics: List Handling -// See Also: min_index(), is_increasing(), is_decreasing() -// Description: -// Returns the index of the first occurrence of the maximum value in the given list. -// If `all` is true then returns a list of all indices where the maximum value occurs. -// Arguments: -// vals = vector of values -// all = set to true to return indices of all occurences of the maximum. Default: false -// Example: -// max_index([5,3,9,6,2,7,8,9,1]); // Returns: 2 -// max_index([5,3,9,6,2,7,8,9,1],all=true); // Returns: [2,7] -function max_index(vals, all=false) = - assert( is_vector(vals) && len(vals)>0 , "Invalid or empty list of numbers.") - all ? search(max(vals),vals,0) : search(max(vals), vals)[0]; - - // Section: Vector Searching +// Function: pointlist_bounds() +// Usage: +// pt_pair = pointlist_bounds(pts); +// Topics: Geometry, Bounding Boxes, Bounds +// Description: +// Finds the bounds containing all the points in `pts` which can be a list of points in any dimension. +// Returns a list of two items: a list of the minimums and a list of the maximums. For example, with +// 3d points `[[MINX, MINY, MINZ], [MAXX, MAXY, MAXZ]]` +// Arguments: +// pts = List of points. +function pointlist_bounds(pts) = + assert(is_path(pts,dim=undef,fast=true) , "Invalid pointlist." ) + let( + select = ident(len(pts[0])), + spread = [ + for(i=[0:len(pts[0])-1]) + let( spreadi = pts*select[i] ) + [ min(spreadi), max(spreadi) ] + ] + ) transpose(spread); + + + // Function: closest_point() // Usage: // index = closest_point(pt, points);