From f22d24ee15926a51384e03d2688d4613e6110ae5 Mon Sep 17 00:00:00 2001 From: RonaldoCMP Date: Wed, 13 Oct 2021 21:20:42 -0300 Subject: [PATCH 01/11] Minor changes in triangulate code and docs, --- arrays.scad | 2 +- geometry.scad | 172 ++++++++++++++++++++++---------------------------- math.scad | 13 ++-- 3 files changed, 82 insertions(+), 105 deletions(-) diff --git a/arrays.scad b/arrays.scad index be3bd7c..947e3a6 100644 --- a/arrays.scad +++ b/arrays.scad @@ -90,7 +90,7 @@ function select(list, start, end) = : end==undef ? is_num(start) ? list[ (start%l+l)%l ] - : assert( is_list(start) || is_range(start), "Invalid start parameter") + : assert( is_vector(start) || is_range(start), "Invalid start parameter") [for (i=start) list[ (i%l+l)%l ] ] : assert(is_finite(start), "When `end` is given, `start` parameter should be a number.") assert(is_finite(end), "Invalid end parameter.") diff --git a/geometry.scad b/geometry.scad index b6b419e..0c6855a 100644 --- a/geometry.scad +++ b/geometry.scad @@ -1603,17 +1603,25 @@ function point_in_polygon(point, poly, nonzero=false, eps=EPSILON) = // Description: // Given a simple polygon in 2D or 3D, triangulates it and returns a list // of triples indexing into the polygon vertices. When the optional argument `ind` is -// given, the it is used as an index list into `poly` to define the polygon. In that case, -// `poly` may have a length greater than `ind`. Otherwise, all points in `poly` +// given, it is used as an index list into `poly` to define the polygon. In that case, +// `poly` may have a length greater than `ind`. When `ind` is undefined, all points in `poly` // are considered as vertices of the polygon. // . -// The function may issue an error if it finds that the polygon is not simple -// (self-intersecting) or its vertices are collinear. It can work for 3d non-planar polygons -// if they are close enough to planar but may otherwise issue an error for this case. -// . // For 2d polygons, the output triangles will have the same winding (CW or CCW) of // the input polygon. For 3d polygons, the triangle windings will induce a normal // vector with the same direction of the polygon normal. +// . +// The function produce correct triangulations for some non-twisted non-simple polygons. +// A polygon is non-twisted iff it is simple or there is a partition of it in +// simple polygons with the same winding. These polygons may have "touching" vertices +// (two vertices having the same coordinates, but distinct adjacencies) and "contact" edges +// (edges whose vertex pairs have the same pairwise coordinates but are in reversed order) but has +// no self-crossing. See examples bellow. If all polygon edges are contact edges, returns an empty list. +// . +// Self-crossing polygons have no consistent winding and usually produce an error but +// when an error is not issued the outputs are not correct triangulations. The function +// can work for 3d non-planar polygons if they are close enough to planar but may otherwise +// issue an error for this case. // Arguments: // poly = Array of vertices for the polygon. // ind = A list indexing the vertices of the polygon in `poly`. @@ -1621,7 +1629,28 @@ function point_in_polygon(point, poly, nonzero=false, eps=EPSILON) = // Example(2D,NoAxes): // poly = star(id=10, od=15,n=11); // tris = polygon_triangulate(poly); -// for(tri=tris) stroke(select(poly,tri), width=.2, closed=true); +// color("lightblue") for(tri=tris) polygon(select(poly,tri)); +// color("magenta") up(2) stroke(poly,.25,closed=true); +// color("black") up(3) vnf_debug([poly,[]],faces=false,size=1); +// Example(2D,NoAxes): a polygon with a hole and one "contact" edge +// poly = [ [-10,0], [10,0], [0,10], [-10,0], [-4,4], [4,4], [0,2], [-4,4] ]; +// tris = polygon_triangulate(poly); +// color("lightblue") for(tri=tris) polygon(select(poly,tri)); +// color("magenta") up(2) stroke(poly,.25,closed=true); +// color("black") up(3) vnf_debug([poly,[]],faces=false,size=1); +// Example(2D,NoAxes): a polygon with "touching" vertices and no holes +// poly = [ [0,0], [5,5], [-5,5], [0,0], [-5,-5], [5,-5] ]; +// tris = polygon_triangulate(poly); +// color("lightblue") for(tri=tris) polygon(select(poly,tri)); +// color("magenta") up(2) stroke(poly,.25,closed=true); +// color("black") up(3) vnf_debug([poly,[]],faces=false,size=1); +// Example(2D,NoAxes): a polygon with "contact" edges and no holes +// poly = [ [0,0], [10,0], [10,10], [0,10], [0,0], [3,3], [7,3], +// [7,7], [7,3], [3,3] ]; +// tris = polygon_triangulate(poly); // see from the top +// color("lightblue") for(tri=tris) polygon(select(poly,tri)); +// color("magenta") up(2) stroke(poly,.25,closed=true); +// color("black") up(3) vnf_debug([poly,[]],faces=false,size=1); // Example(3D): // include // vnf = regular_polyhedron_info(name="dodecahedron",side=5,info="vnf"); @@ -1630,82 +1659,39 @@ function point_in_polygon(point, poly, nonzero=false, eps=EPSILON) = // color("blue") // vnf_wireframe(vnf_tri, width=.15); function polygon_triangulate(poly, ind, eps=EPSILON) = - assert(is_path(poly), "Polygon `poly` should be a list of 2d or 3d points") + assert(is_path(poly) && len(poly)>=3, "Polygon `poly` should be a list of at least three 2d or 3d points") assert(is_undef(ind) || (is_vector(ind) && min(ind)>=0 && max(ind) 2*eps, + "The polygon vertices are collinear.") + [ind] + : len(poly[ind[0]]) == 3 + ? // represents the polygon projection on its plane as a 2d polygon + let( + pts = select(poly,ind), + nrm = polygon_normal(pts) + ) + // here, instead of an error, it might return [] or undef + assert( nrm!=undef, + "The polygon has self-intersections or its vertices are collinear or non coplanar.") + let( + imax = max_index([for(p=pts) norm(p-pts[0]) ]), + v1 = unit( pts[imax] - pts[0] ), + v2 = cross(v1,nrm), + prpts = pts*transpose([v1,v2]) + ) + [for(tri=_triangulate(prpts, count(len(ind)), eps)) select(ind,tri) ] + : let( cw = is_polygon_clockwise(select(poly, ind)) ) + cw + ? [for(tri=_triangulate( poly, reverse(ind), eps )) reverse(tri) ] + : _triangulate( poly, ind, eps ); - -// requires ccw 2d polygons -// returns ccw triangles -function _old_triangulate(poly, ind, eps=EPSILON, tris=[]) = - len(ind)==3 ? concat(tris,[ind]) : - let( ear = _get_ear(poly,ind,eps) ) - assert( ear!=undef, - "The polygon has self-intersections or its vertices are collinear or non coplanar.") - let( - ear_tri = select(ind,ear,ear+2), - indr = select(ind,ear+2, ear) // indices of the remaining points - ) - _triangulate(poly, indr, eps, concat(tris,[ear_tri])); - -// search a valid ear from the remaining polygon -function _old_get_ear(poly, ind, eps, _i=0) = - _i>=len(ind) ? undef : // poly has no ears - let( // the _i-th ear candidate - p0 = poly[ind[_i]], - p1 = poly[ind[(_i+1)%len(ind)]], - p2 = poly[ind[(_i+2)%len(ind)]] - ) - // if it is not a convex vertex, try the next one - _is_cw2(p0,p1,p2,eps) ? _get_ear(poly,ind,eps, _i=_i+1) : - let( // vertex p1 is convex; check if the triangle contains any other point - to_tst = select(ind,_i+3, _i-1), - pt2tst = select(poly,to_tst), // points other than p0, p1 and p2 - q = [(p0-p2).y, (p2-p0).x], // orthogonal to ray [p0,p2] pointing right - q0 = q*p0, - atleft = [for(p=pt2tst) if(p*q<=q0) p ] - ) - atleft==[] ? _i : // no point inside -> an ear - let( - q = [(p2-p1).y, (p1-p2).x], // orthogonal to ray [p1,p2] pointing right - q0 = q*p2, - atleft = [for(p=atleft) if(p*q<=q0) p ] - ) - atleft==[] ? _i : // no point inside -> an ear - let( - q = [(p1-p0).y, (p0-p1).x], // orthogonal to ray [p1,p0] pointing right - q0 = q*p1, - atleft = [for(p=atleft) if(p*q<=q0) p ] - ) - atleft==[] ? _i : // no point inside -> an ear - // check the next ear candidate - _get_ear(poly, ind, eps, _i=_i+1); - function _triangulate(poly, ind, eps=EPSILON, tris=[]) = len(ind)==3 ? _is_degenerate(select(poly,ind),eps) @@ -1714,18 +1700,18 @@ function _triangulate(poly, ind, eps=EPSILON, tris=[]) = : let( ear = _get_ear(poly,ind,eps) ) assert( ear!=undef, "The polygon has self-intersections or its vertices are collinear or non coplanar.") - ear<0 // degenerate ear - ? let( indr = select(ind,-ear+1, -ear-1) ) // discard it - _triangulate(poly, indr, eps, tris) + is_list(ear) // degenerate ear + ? _triangulate(poly, select(ind,ear[0]+2, ear[0]), eps, tris) // discard it : let( ear_tri = select(ind,ear,ear+2), - indr = select(ind,ear+2, ear) // indices of the remaining points + indr = select(ind,ear+2, ear) // remaining point indices ) _triangulate(poly, indr, eps, concat(tris,[ear_tri])); // a returned ear will be: -// 1. a CCW triangle without points inside except possibly at its vertices +// 1. a CCW (non-degenerate) triangle, made of subsequent vertices, without other +// points inside except possibly at its vertices // 2. or a degenerate triangle where two vertices are coincident // the returned ear is specified by the index of `ind` of its first vertex function _get_ear(poly, ind, eps, _i=0) = @@ -1735,29 +1721,25 @@ function _get_ear(poly, ind, eps, _i=0) = p1 = poly[ind[(_i+1)%len(ind)]], p2 = poly[ind[(_i+2)%len(ind)]] ) - // if it is a degenerate triangle, return it (codified) - _is_degenerate([p0,p1,p2],eps) ? -(_i+1) : - // if it is not a convex vertex, try the next one + // degenerate triangles are returned codified + _is_degenerate([p0,p1,p2],eps) ? [_i] : + // if it is not a convex vertex, check the next one _is_cw2(p0,p1,p2,eps) ? _get_ear(poly,ind,eps, _i=_i+1) : let( // vertex p1 is convex // check if the triangle contains any other point // except possibly its own vertices to_tst = select(ind,_i+3, _i-1), - pt2tst = select(poly,to_tst), // points other than p0, p1 and p2 q = [(p0-p2).y, (p2-p0).x], // orthogonal to ray [p0,p2] pointing right - q0 = q*p0, r = [(p2-p1).y, (p1-p2).x], // orthogonal to ray [p2,p1] pointing right - r0 = r*p2, s = [(p1-p0).y, (p0-p1).x], // orthogonal to ray [p1,p0] pointing right - s0 = s*p1, - inside = [for(p=pt2tst) - if( p*q<=q0 && p*r<=r0 && p*s<=s0 ) // p is in the triangle - if( norm(p-p0)>eps // and doesn't coincide with - && norm(p-p1)>eps // any of its vertices + inside = [for(p=select(poly,to_tst)) // for vertices other than p0, p1 and p2 + if( (p-p0)*q<=0 && (p-p2)*r<=0 && (p-p1)*s<=0 // p is on the triangle + && norm(p-p0)>eps // but not on any vertex of it + && norm(p-p1)>eps && norm(p-p2)>eps ) p ] ) - inside==[] ? _i : // no point inside -> an ear + inside==[] ? _i : // found an ear // check the next ear candidate _get_ear(poly, ind, eps, _i=_i+1); diff --git a/math.scad b/math.scad index 4f46258..360989d 100644 --- a/math.scad +++ b/math.scad @@ -689,17 +689,12 @@ function _sum(v,_total,_i=0) = _i>=len(v) ? _total : _sum(v,_total+v[_i], _i+1); // cumsum([[1,2,3], [3,4,5], [5,6,7]]); // returns [[1,2,3], [4,6,8], [9,12,15]] function cumsum(v) = assert(is_consistent(v), "The input is not consistent." ) - _cumsum(v,_i=0,_acc=[]); + len(v)<=1 ? v : + _cumsum(v,_i=1,_acc=[v[0]]); function _cumsum(v,_i=0,_acc=[]) = - _i==len(v) ? _acc : - _cumsum( - v, _i+1, - concat( - _acc, - [_i==0 ? v[_i] : last(_acc) + v[_i]] - ) - ); + _i>=len(v) ? _acc : + _cumsum( v, _i+1, [ each _acc, _acc[len(_acc)-1] + v[_i] ] ); // Function: sum_of_sines() From 8cdd10fd82536773ff604240d2159776261f7959 Mon Sep 17 00:00:00 2001 From: RonaldoCMP Date: Wed, 13 Oct 2021 21:30:48 -0300 Subject: [PATCH 02/11] Revert arrays.scad change --- arrays.scad | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arrays.scad b/arrays.scad index 947e3a6..be3bd7c 100644 --- a/arrays.scad +++ b/arrays.scad @@ -90,7 +90,7 @@ function select(list, start, end) = : end==undef ? is_num(start) ? list[ (start%l+l)%l ] - : assert( is_vector(start) || is_range(start), "Invalid start parameter") + : assert( is_list(start) || is_range(start), "Invalid start parameter") [for (i=start) list[ (i%l+l)%l ] ] : assert(is_finite(start), "When `end` is given, `start` parameter should be a number.") assert(is_finite(end), "Invalid end parameter.") From 30fc691b4068e6428104c4fa52b315513cfa9012 Mon Sep 17 00:00:00 2001 From: RonaldoCMP Date: Wed, 13 Oct 2021 21:40:51 -0300 Subject: [PATCH 03/11] Update test_geometry.scad --- tests/test_geometry.scad | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/test_geometry.scad b/tests/test_geometry.scad index 60359b6..1b62076 100644 --- a/tests/test_geometry.scad +++ b/tests/test_geometry.scad @@ -48,6 +48,7 @@ test_reindex_polygon(); test_align_polygon(); test_polygon_centroid(); test_point_in_polygon(); +test_polygon_triangulate(); test_is_polygon_clockwise(); test_clockwise_polygon(); test_ccw_polygon(); @@ -78,6 +79,21 @@ function info_str(list,i=0,string=chr(10)) = : info_str(list,i+1,str(string,str(list[i][0],_valstr(list[i][1]),chr(10)))); +module test_polygon_triangulate() { + poly0 = [ [0,0,1], [10,0,2], [10,10,0] ]; + poly1 = [ [-10,0,-10], [10,0,10], [0,10,0], [-10,0,-10], [-4,4,-4], [4,4,4], [0,2,0], [-4,4,-4] ]; + poly2 = [ [0,0], [5,5], [-5,5], [0,0], [-5,-5], [5,-5] ]; + poly3 = [ [0,0], [10,0], [10,10], [10,13], [10,10], [0,10], [0,0], [3,3], [7,3], [7,7], [7,3], [3,3] ]; + tris0 = sort(polygon_triangulate(poly0)); + assert(approx(tris0, [[0, 1, 2]]); + tris1 = sort(polygon_triangulate(poly1)); + assert(approx*tris1,sort([[2, 3, 4], [8, 9, 0], [2, 4, 6], [8, 0, 1], [1, 2, 6], [6, 8, 1]])); + tris2 = sort(polygon_triangulate(poly2)); + assert(approx*tris2,sort([[0, 1, 2], [3, 4, 5]])); + tris3 = sort(polygon_triangulate(poly3)); + assert(approx*tris3,sort( [[5, 6, 7], [7, 8, 9], [10, 11, 0], [5, 7, 9], [10, 0, 1], [4, 5, 9], [9, 10, 1], [1, 4, 9]])); +} + module test__normalize_plane(){ plane = rands(-5,5,4,seed=333)+[10,0,0,0]; plane2 = _normalize_plane(plane); From 06419a678685fcdbc68835bff461aa630097a585 Mon Sep 17 00:00:00 2001 From: RonaldoCMP Date: Wed, 13 Oct 2021 21:52:56 -0300 Subject: [PATCH 04/11] Update test_geometry.scad --- tests/test_geometry.scad | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/test_geometry.scad b/tests/test_geometry.scad index 1b62076..d7405df 100644 --- a/tests/test_geometry.scad +++ b/tests/test_geometry.scad @@ -85,13 +85,13 @@ module test_polygon_triangulate() { poly2 = [ [0,0], [5,5], [-5,5], [0,0], [-5,-5], [5,-5] ]; poly3 = [ [0,0], [10,0], [10,10], [10,13], [10,10], [0,10], [0,0], [3,3], [7,3], [7,7], [7,3], [3,3] ]; tris0 = sort(polygon_triangulate(poly0)); - assert(approx(tris0, [[0, 1, 2]]); - tris1 = sort(polygon_triangulate(poly1)); - assert(approx*tris1,sort([[2, 3, 4], [8, 9, 0], [2, 4, 6], [8, 0, 1], [1, 2, 6], [6, 8, 1]])); - tris2 = sort(polygon_triangulate(poly2)); - assert(approx*tris2,sort([[0, 1, 2], [3, 4, 5]])); - tris3 = sort(polygon_triangulate(poly3)); - assert(approx*tris3,sort( [[5, 6, 7], [7, 8, 9], [10, 11, 0], [5, 7, 9], [10, 0, 1], [4, 5, 9], [9, 10, 1], [1, 4, 9]])); + assert(approx(tris0, [[0, 1, 2]])); + tris1 = (polygon_triangulate(poly1)); + assert(approx(tris1,( [[2, 3, 4], [6, 7, 0], [2, 4, 5], [6, 0, 1], [1, 2, 5], [5, 6, 1]]))); + tris2 = (polygon_triangulate(poly2)); + assert(approx(tris2,([[0, 1, 2], [3, 4, 5]]))); + tris3 = (polygon_triangulate(poly3)); + assert(approx(tris3,( [[5, 6, 7], [7, 8, 9], [10, 11, 0], [5, 7, 9], [10, 0, 1], [4, 5, 9], [9, 10, 1], [1, 4, 9]]))); } module test__normalize_plane(){ From aef60324fdfe562b5e4e18d450cddab7886a5352 Mon Sep 17 00:00:00 2001 From: RonaldoCMP Date: Wed, 13 Oct 2021 23:16:14 -0300 Subject: [PATCH 05/11] update geometry.scad --- geometry.scad | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/geometry.scad b/geometry.scad index 0c6855a..dd6ace0 100644 --- a/geometry.scad +++ b/geometry.scad @@ -1673,9 +1673,11 @@ function polygon_triangulate(poly, ind, eps=EPSILON) = : len(poly[ind[0]]) == 3 ? // represents the polygon projection on its plane as a 2d polygon let( + ind = deduplicate_indexed(poly, ind, eps), pts = select(poly,ind), nrm = polygon_normal(pts) - ) + ) + len(ind)<3 ? [] : // here, instead of an error, it might return [] or undef assert( nrm!=undef, "The polygon has self-intersections or its vertices are collinear or non coplanar.") From da751e1e13edf3553dea0bb7758163cdba6953a2 Mon Sep 17 00:00:00 2001 From: RonaldoCMP Date: Wed, 13 Oct 2021 23:27:49 -0300 Subject: [PATCH 06/11] Update geometry.scad --- geometry.scad | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/geometry.scad b/geometry.scad index dd6ace0..54dc27c 100644 --- a/geometry.scad +++ b/geometry.scad @@ -1673,11 +1673,13 @@ function polygon_triangulate(poly, ind, eps=EPSILON) = : len(poly[ind[0]]) == 3 ? // represents the polygon projection on its plane as a 2d polygon let( - ind = deduplicate_indexed(poly, ind, eps), + ind = deduplicate_indexed(poly, ind, eps) + ) + len(ind)<3 ? [] : + let( pts = select(poly,ind), nrm = polygon_normal(pts) - ) - len(ind)<3 ? [] : + ) // here, instead of an error, it might return [] or undef assert( nrm!=undef, "The polygon has self-intersections or its vertices are collinear or non coplanar.") From 4d9a1d36ac8ad4ad3a220f3d86f6af4164e1c09d Mon Sep 17 00:00:00 2001 From: RonaldoCMP Date: Wed, 13 Oct 2021 23:31:26 -0300 Subject: [PATCH 07/11] Update geometry.scad --- geometry.scad | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/geometry.scad b/geometry.scad index 54dc27c..6de43fa 100644 --- a/geometry.scad +++ b/geometry.scad @@ -1674,9 +1674,9 @@ function polygon_triangulate(poly, ind, eps=EPSILON) = ? // represents the polygon projection on its plane as a 2d polygon let( ind = deduplicate_indexed(poly, ind, eps) - ) - len(ind)<3 ? [] : - let( + ) + len(ind)<3 ? [] : + let( pts = select(poly,ind), nrm = polygon_normal(pts) ) From 6b542191cedc9ee7f86f17ea5f471ef65ada7b1a Mon Sep 17 00:00:00 2001 From: RonaldoCMP Date: Thu, 14 Oct 2021 11:48:41 -0300 Subject: [PATCH 08/11] Update geometry.scad --- geometry.scad | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/geometry.scad b/geometry.scad index 6de43fa..1a58aaf 100644 --- a/geometry.scad +++ b/geometry.scad @@ -488,7 +488,7 @@ function _covariance_evec_eval(points) = evals = _eigenvals_symm_3(M), // eigenvalues in decreasing order evec = _eigenvec_symm_3(M,evals,i=2) ) [pm, evec, evals[0] ]; - + // Function: plane_from_points() // Usage: @@ -1616,12 +1616,13 @@ function point_in_polygon(point, poly, nonzero=false, eps=EPSILON) = // simple polygons with the same winding. These polygons may have "touching" vertices // (two vertices having the same coordinates, but distinct adjacencies) and "contact" edges // (edges whose vertex pairs have the same pairwise coordinates but are in reversed order) but has -// no self-crossing. See examples bellow. If all polygon edges are contact edges, returns an empty list. +// no self-crossing. See examples bellow. If all polygon edges are contact edges, +// it returns an empty list for 2d polygons and issues an error for 3d polygons. // . // Self-crossing polygons have no consistent winding and usually produce an error but // when an error is not issued the outputs are not correct triangulations. The function // can work for 3d non-planar polygons if they are close enough to planar but may otherwise -// issue an error for this case. +// issue an error for this case. // Arguments: // poly = Array of vertices for the polygon. // ind = A list indexing the vertices of the polygon in `poly`. @@ -1630,18 +1631,21 @@ function point_in_polygon(point, poly, nonzero=false, eps=EPSILON) = // poly = star(id=10, od=15,n=11); // tris = polygon_triangulate(poly); // color("lightblue") for(tri=tris) polygon(select(poly,tri)); +// color("blue") up(1) for(tri=tris) { stroke(select(poly,tri),.15,closed=true); } // color("magenta") up(2) stroke(poly,.25,closed=true); // color("black") up(3) vnf_debug([poly,[]],faces=false,size=1); // Example(2D,NoAxes): a polygon with a hole and one "contact" edge // poly = [ [-10,0], [10,0], [0,10], [-10,0], [-4,4], [4,4], [0,2], [-4,4] ]; // tris = polygon_triangulate(poly); // color("lightblue") for(tri=tris) polygon(select(poly,tri)); +// color("blue") up(1) for(tri=tris) { stroke(select(poly,tri),.15,closed=true); } // color("magenta") up(2) stroke(poly,.25,closed=true); // color("black") up(3) vnf_debug([poly,[]],faces=false,size=1); // Example(2D,NoAxes): a polygon with "touching" vertices and no holes // poly = [ [0,0], [5,5], [-5,5], [0,0], [-5,-5], [5,-5] ]; // tris = polygon_triangulate(poly); // color("lightblue") for(tri=tris) polygon(select(poly,tri)); +// color("blue") up(1) for(tri=tris) { stroke(select(poly,tri),.15,closed=true); } // color("magenta") up(2) stroke(poly,.25,closed=true); // color("black") up(3) vnf_debug([poly,[]],faces=false,size=1); // Example(2D,NoAxes): a polygon with "contact" edges and no holes @@ -1649,6 +1653,7 @@ function point_in_polygon(point, poly, nonzero=false, eps=EPSILON) = // [7,7], [7,3], [3,3] ]; // tris = polygon_triangulate(poly); // see from the top // color("lightblue") for(tri=tris) polygon(select(poly,tri)); +// color("blue") up(1) for(tri=tris) { stroke(select(poly,tri),.15,closed=true); } // color("magenta") up(2) stroke(poly,.25,closed=true); // color("black") up(3) vnf_debug([poly,[]],faces=false,size=1); // Example(3D): @@ -1666,21 +1671,20 @@ function polygon_triangulate(poly, ind, eps=EPSILON) = let( ind = is_undef(ind) ? count(len(poly)) : ind ) len(ind) == 3 ? _is_degenerate([poly[ind[0]], poly[ind[1]], poly[ind[2]]], eps) ? [] : - // no zero area + // non zero area assert( norm(scalar_vec3(cross(poly[ind[1]]-poly[ind[0]], poly[ind[2]]-poly[ind[0]]))) > 2*eps, "The polygon vertices are collinear.") [ind] : len(poly[ind[0]]) == 3 ? // represents the polygon projection on its plane as a 2d polygon let( - ind = deduplicate_indexed(poly, ind, eps) + ind = deduplicate_indexed(poly, ind, eps) ) - len(ind)<3 ? [] : + assert(len(ind)>=3 , "The polygon vertices are collinear.") let( pts = select(poly,ind), nrm = polygon_normal(pts) ) - // here, instead of an error, it might return [] or undef assert( nrm!=undef, "The polygon has self-intersections or its vertices are collinear or non coplanar.") let( From 2d5ce3b1919522d9af7dd3f5809370c8702cd681 Mon Sep 17 00:00:00 2001 From: RonaldoCMP Date: Thu, 14 Oct 2021 12:37:48 -0300 Subject: [PATCH 09/11] Update geometry.scad --- geometry.scad | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geometry.scad b/geometry.scad index 1a58aaf..c1e35b5 100644 --- a/geometry.scad +++ b/geometry.scad @@ -1680,7 +1680,7 @@ function polygon_triangulate(poly, ind, eps=EPSILON) = let( ind = deduplicate_indexed(poly, ind, eps) ) - assert(len(ind)>=3 , "The polygon vertices are collinear.") + len(ind)<3 ? [] : let( pts = select(poly,ind), nrm = polygon_normal(pts) From f1ea2a97992609b074b6e07f216d0ed92cf94b42 Mon Sep 17 00:00:00 2001 From: Garth Minette Date: Sat, 16 Oct 2021 23:14:46 -0700 Subject: [PATCH 10/11] Added find_all(), re-did find_first() interface. Made f_gt(), f_lt(), f_gte(), f_lte() with one arg be the second argument. --- fnliterals.scad | 315 +++++++++++++++++++++++++----------------------- 1 file changed, 166 insertions(+), 149 deletions(-) diff --git a/fnliterals.scad b/fnliterals.scad index c0cde8b..1cdd9c4 100644 --- a/fnliterals.scad +++ b/fnliterals.scad @@ -48,7 +48,7 @@ function map(func, list) = // Topics: Function Literals, Looping, Filters // Usage: // lst = filter(func, list); -// lst = filter(function (x) x+1, list); +// lst = filter(function (x) x>1, list); // Description: // Returns all items in `list` that the function `func` returns true for. // In pseudo-code, this is effectively: @@ -235,35 +235,72 @@ function for_n(n,init,func) = a(init, n[0]); +// Function: find_all() +// Topics: Function Literals, Looping, Filters +// Usage: +// indices = find_all(func, list); +// indices = find_all(function (x) x>1, list); +// Description: +// Returns the indices of all items in `list` that the function `func` returns true for. +// In pseudo-code, this is effectively: +// ``` +// function find_all(func,list): +// out = []; +// foreach item in list: +// if func(item) is true: +// append item index to out; +// return out; +// ``` +// Arguments: +// func = The function of signature `function (x)` to evaluate for each item in `list`. +// list = The input list. +// See Also: find_all(), map(), reduce(), accumulate(), while(), for_n() +// Example: +// func = function(x) x>5; +// echo(find_all(func, [3,4,5,6,7])); +// // ECHO: [3,4] +function find_all(func, list) = + assert(is_function(func)) + assert(is_list(list)) + [for (indexnum=idx(list)) if (func(list[indexnum])) indexnum]; + + // Function: find_first() // Topics: Function Literals, Searching // Usage: -// idx = find_first(val, list, [start=], [func=]); +// idx = find_first(func, list, [start=]); // Description: -// Finds the first item in `list` which, when compared against `val` using the function literal -// `func` gets a true result. By default, `func` just calls `approx()`. The signature of the -// function literal in `func` is `function (val,x)`, and it is expected to return true when the -// two values compare as matching. It should return false otherwise. -// If you need to find *all* matching items in the list, you should probably use {{filter()}} instead. -// See Also: map(), filter(), reduce(), accumulate(), while(), for_n(), binsearch() +// Finds the first item in `list`, after index `start`, which the function literal in `func` will return true for. +// The signature of the function literal in `func` is `function (x)`, and it is expected to return true when the +// value compares as matching. It should return false otherwise. If you need to find *all* matching items in the +// list, you should use {{find_all()}} instead. +// See Also: find_all(), map(), filter(), reduce(), accumulate(), while(), for_n(), binsearch() // Arguments: -// val = The value to look for. +// func = The function literal to use to check each item in `list`. Expects the signature `function (x)`, and a boolean return value. // list = The list to search. // --- // start = The first item to check. -// func = The function literal to use to compare `val` against the items in `list`. Expects the signature `function (a,b)`, and a boolean return value. Default: `f_approx()` -function find_first(val, list, start=0, func=f_approx()) = +// Example: +// data = [8,5,3,7,4,2,9]; +// echo(find_first(f_lte(4), data)); +// // ECHO: 2 +// Example: +// data = [8,5,3,7,4,2,9]; +// echo(find_first(f_lte(4), data, start=3)); +// // ECHO: 4 +function find_first(func, list, start=0) = + assert(is_function(func)) assert(is_list(list)) assert(is_finite(start)) - assert(is_function(func)) let( - l = len(list), - a = function(i) - i >= l? undef : - func(val, list[i])? i : - a(i+1) + listlen = len(list), + _find_first = function(indexnum) ( + indexnum >= listlen? undef : + func(list[indexnum])? indexnum : + _find_first(indexnum+1) + ) ) - a(start); + _find_first(start); // Function: binsearch() @@ -287,7 +324,7 @@ function find_first(val, list, start=0, func=f_approx()) = // idx = binsearch(44, items, cmp=function(a,b) a-b); // Example: // items = [for (i=[32:126]) [chr(i), i]]; -// idx = binsearch("G"", items, idx=0); +// idx = binsearch("G", items, idx=0); function binsearch(key, list, idx, cmp=f_cmp()) = let( a = function(s,e) @@ -417,17 +454,17 @@ function hashmap(hashsize=127,items,table) = // f_str = f_1arg(function(a) str(a)); // fn_str = f_str(); // = function(a) str(a); // fn_str3 = f_str(3); // = function() str(3); -function f_1arg(func) = +function f_1arg(target_func) = function(a) - a==undef? function(x) func(x) : - function() func(a); + a==undef? function(x) target_func(x) : + function() target_func(a); // Function: f_2arg() // Topics: Function Literals, Function Literal Factories // See Also: f_1arg(), f_3arg() // Usage: -// fn = f_2arg(func); +// fn = f_2arg(target_func); // Description: // Takes a function literal that accepts two arguments, and returns a function // literal factory that can be used to pre-fill out one or both of those arguments @@ -439,22 +476,44 @@ function f_1arg(func) = // fn_3lt = f_lt(a=3); // = function(b) 3 b`, where either -// or both of the `a` or `b` arguments can be replaced with constants. -// Arguments: -// a = If given, replaces the first argument. -// b = If given, replaces the second argument. +// A factory that generates function literals that compare `a` and `b`, where one or +// both arguments can be replaced with constants. If `a` and `b` are equal, the function +// literal will return 0. If ab then 1 is returned. // Example: -// fn_cmp = f_cmp(); // = function(a,b) a==b?0: a>b?1: -1; +// fn_cmp = f_cmp(); // = function(a,b) a==b?0: a>b?1: -1; // fn_cmp3 = f_cmp(3); // = function(a) a==3?0: a>3?1: -1; -// fn_3cmp = f_cmp(a=3); // = function(b) 3==b?0: 3>b?1: -1; -// fn_3cmp4 = f_cmp(a=3,b=4); // = function() 3==4?0: 3>4?1: -1; -function f_cmp(a,b) = f_2arg(function (a,b) a==b?0: a>b?1: -1)(a,b); +// fn_3cmp4 = f_cmp(3,4); // = function() 3==4?0: 3>4?1: -1; +function f_cmp(a,b) = f_2arg_simple(function (a,b) a==b?0: a>b?1: -1)(a,b); // Function: f_gt() // Usage: // fn = f_gt(); -// fn = f_gt(a=); -// fn = f_gt(b=); -// fn = f_gt(a=,b=); +// fn = f_gt(b); +// fn = f_gt(a,b); // Description: -// A factory that generates function literals based on `a > b`, where either -// or both of the `a` or `b` arguments can be replaced with constants. -// Arguments: -// a = If given, replaces the first argument. -// b = If given, replaces the second argument. +// A factory that generates function literals based on `a > b`, where one +// or both of the arguments can be replaced with constants. // Example: -// fn_gt = f_gt(); // = function(a,b) a>b; +// fn_gt = f_gt(); // = function(a,b) a>b; // fn_gt3 = f_gt(3); // = function(a) a>3; -// fn_3gt = f_gt(a=3); // = function(b) 3>b; -// fn_3gt4 = f_gt(a=3,b=4); // = function() 3>4; -function f_gt(a,b) = f_2arg(function (a,b) a>b)(a,b); +// fn_3gt4 = f_gt(3,4); // = function() 3>4; +function f_gt(a,b) = f_2arg_simple(function (a,b) a>b)(a,b); // Function: f_lt() // Usage: // fn = f_lt(); -// fn = f_lt(a=); -// fn = f_lt(b=); -// fn = f_lt(a=,b=); +// fn = f_lt(b); +// fn = f_lt(a,b); // Description: -// A factory that generates function literals based on `a < b`, where either -// or both of the `a` or `b` arguments can be replaced with constants. -// Arguments: -// a = If given, replaces the first argument. -// b = If given, replaces the second argument. +// A factory that generates function literals based on `a < b`, where one +// or both of the arguments can be replaced with constants. // Example: -// fn_lt = f_lt(); // = function(a,b) a= b`, where either -// or both of the `a` or `b` arguments can be replaced with constants. -// Arguments: -// a = If given, replaces the first argument. -// b = If given, replaces the second argument. +// A factory that generates function literals based on `a >= b`, where one +// or both of the arguments can be replaced with constants. // Example: -// fn_gte = f_gte(); // = function(a,b) a>=b; +// fn_gte = f_gte(); // = function(a,b) a>=b; // fn_gte3 = f_gte(3); // = function(a) a>=3; -// fn_3gte = f_gte(a=3); // = function(b) 3>=b; -// fn_3gte4 = f_gte(a=3,b=4); // = function() 3>=4; -function f_gte(a,b) = f_2arg(function (a,b) a>=b)(a,b); +// fn_3gte4 = f_gte(3,4); // = function() 3>=4; +function f_gte(a,b) = f_2arg_simple(function (a,b) a>=b)(a,b); // Function: f_lte() // Usage: // fn = f_lte(); -// fn = f_lte(a=); -// fn = f_lte(b=); -// fn = f_lte(a=,b=); +// fn = f_lte(b); +// fn = f_lte(a,b); // Description: -// A factory that generates function literals based on `a <= b`, where either -// or both of the `a` or `b` arguments can be replaced with constants. -// Arguments: -// a = If given, replaces the first argument. -// b = If given, replaces the second argument. +// A factory that generates function literals based on `a <= b`, where +// one or both arguments can be replaced with constants. // Example: -// fn_lte = f_lte(); // = function(a,b) a<=b; +// fn_lte = f_lte(); // = function(a,b) a<=b; // fn_lte3 = f_lte(3); // = function(a) a<=3; -// fn_3lte = f_lte(a=3); // = function(b) 3<=b; -// fn_3lte4 = f_lte(a=3,b=4); // = function() 3<=4; -function f_lte(a,b) = f_2arg(function (a,b) a<=b)(a,b); +// fn_3lte4 = f_lte(3,4); // = function() 3<=4; +function f_lte(a,b) = f_2arg_simple(function (a,b) a<=b)(a,b); // Function: f_eq() // Usage: // fn = f_eq(); -// fn = f_eq(a=); -// fn = f_eq(b=); -// fn = f_eq(a=,b=); +// fn = f_eq(b); +// fn = f_eq(a,b); // Description: -// A factory that generates function literals based on `a == b`, where either -// or both of the `a` or `b` arguments can be replaced with constants. -// Arguments: -// a = If given, replaces the first argument. -// b = If given, replaces the second argument. +// A factory that generates function literals based on `a == b`, where +// one or both arguments can be replaced with constants. // Example: // fn_eq = f_eq(); // = function(a,b) a==b; -// fn_eq3 = f_eq(3); // = function(a) a==3; -// fn_3eq4 = f_eq(a=3,b=4); // = function() 3==4; -function f_eq(a,b) = f_2arg(function (a,b) a==b)(a,b); +// fn_eq3 = f_eq(3); // = function(a) a==3; +// fn_3eq4 = f_eq(3,4); // = function() 3==4; +function f_eq(a,b) = f_2arg_simple(function (a,b) a==b)(a,b); // Function: f_neq() // Usage: // fn = f_neq(); -// fn = f_neq(a=); -// fn = f_neq(b=); -// fn = f_neq(a=,b=); +// fn = f_neq(b); +// fn = f_neq(a,b); // Description: -// A factory that generates function literals based on `a != b`, where either -// or both of the `a` or `b` arguments can be replaced with constants. -// Arguments: -// a = If given, replaces the first argument. -// b = If given, replaces the second argument. +// A factory that generates function literals based on `a != b`, where +// one or both arguments can be replaced with constants. // Example: // fn_neq = f_neq(); // = function(a,b) a!=b; -// fn_neq3 = f_neq(3); // = function(a) a!=3; -// fn_3neq4 = f_neq(a=3,b=4); // = function() 3!=4; -function f_neq(a,b) = f_2arg(function (a,b) a!=b)(a,b); +// fn_neq3 = f_neq(3); // = function(a) a!=3; +// fn_3neq4 = f_neq(3,4); // = function() 3!=4; +function f_neq(a,b) = f_2arg_simple(function (a,b) a!=b)(a,b); // Function: f_approx() // Usage: // fn = f_approx(); -// fn = f_approx(a=); -// fn = f_approx(b=); -// fn = f_approx(a=,b=); +// fn = f_approx(b); +// fn = f_approx(a,b); // Description: // A factory that generates function literals based on `approx(a,b)`, where -// either or both of the `a` or `b` arguments can be replaced with constants. -// Arguments: -// a = If given, replaces the first argument. -// b = If given, replaces the second argument. +// one or both arguments can be replaced with constants. // Example: -// fn_approx = f_approx(); // = function(a,b) approx(a,b); +// fn_approx = f_approx(); // = function(a,b) approx(a,b); // fn_approx3 = f_approx(3); // = function(a) approx(a,3); -// fn_3approx = f_approx(a=3); // = function(b) approx(3,b); -// fn_3approx4 = f_approx(a=3,b=4); // = function() approx(3,4); -function f_approx(a,b) = f_2arg(function (a,b) approx(a,b))(a,b); +// fn_3approx4 = f_approx(3,4); // = function() approx(3,4); +function f_approx(a,b) = f_2arg_simple(function (a,b) approx(a,b))(a,b); // Function: f_napprox() // Usage: // fn = f_napprox(); -// fn = f_napprox(a=); -// fn = f_napprox(b=); -// fn = f_napprox(a=,b=); +// fn = f_napprox(b); +// fn = f_napprox(a,b); // Description: -// A factory that generates function literals based on `napprox(a,b)`, where -// either or both of the `a` or `b` arguments can be replaced with constants. -// Arguments: -// a = If given, replaces the first argument. -// b = If given, replaces the second argument. +// A factory that generates function literals based on `!approx(a,b)`, where +// one or both arguments can be replaced with constants. // Example: -// fn_napprox = f_napprox(); // = function(a,b) napprox(a,b); +// fn_napprox = f_napprox(); // = function(a,b) napprox(a,b); // fn_napprox3 = f_napprox(3); // = function(a) napprox(a,3); -// fn_3napprox = f_napprox(a=3); // = function(b) napprox(3,b); -// fn_3napprox4 = f_napprox(a=3,b=4); // = function() napprox(3,4); -function f_napprox(a,b) = f_2arg(function (a,b) !approx(a,b))(a,b); +// fn_3napprox4 = f_napprox(3,4); // = function() napprox(3,4); +function f_napprox(a,b) = f_2arg_simple(function (a,b) !approx(a,b))(a,b); From a215b2b5907e5b3f1aef4a6b0db96eaefb57e983 Mon Sep 17 00:00:00 2001 From: Garth Minette Date: Sun, 17 Oct 2021 00:35:04 -0700 Subject: [PATCH 11/11] Fix tests for fnliteral changes. --- fnliterals.scad | 6 +- tests/test_fnliterals.scad | 118 +++++++++++++++++++++---------------- 2 files changed, 70 insertions(+), 54 deletions(-) diff --git a/fnliterals.scad b/fnliterals.scad index 1cdd9c4..c17563e 100644 --- a/fnliterals.scad +++ b/fnliterals.scad @@ -502,7 +502,7 @@ function f_2arg(target_func) = function f_2arg_simple(target_func) = function(a,b) a==undef && b==undef? function(x,y) target_func(x,y) : - b==undef? function(x) target_func(a,x) : + b==undef? function(x) target_func(x,a) : function() target_func(a,b); @@ -547,7 +547,7 @@ function f_3arg(target_func) = // b = The argumen that will be discarded. // Example: // x = while(0, ival(f_lt(5)), xval(fngen_add(1))); -function ival(target_func) = function(a,b) func(a); +function ival(target_func) = function(a,b) target_func(a); // Function: xval() @@ -563,7 +563,7 @@ function ival(target_func) = function(a,b) func(a); // b = The argumen that will be discarded. // Example: // x = while(0, ival(f_lt(5)), xval(fngen_add(1))); -function xval(target_func) = function(a,b) func(b); +function xval(target_func) = function(a,b) target_func(b); diff --git a/tests/test_fnliterals.scad b/tests/test_fnliterals.scad index 5d6202f..edb07a5 100644 --- a/tests/test_fnliterals.scad +++ b/tests/test_fnliterals.scad @@ -83,23 +83,23 @@ test_for_n(); module test_find_first() { - l = [7,3,9,1,6,1,3,2]; + l = [7,3,8,1,6,1,3,2,9]; lt = function (val,x) val < x; lte = function (val,x) val <= x; gt = function (val,x) val > x; gte = function (val,x) val >= x; - assert_equal(find_first(1,l), 3); - assert_equal(find_first(1,l,start=4), 5); - assert_equal(find_first(6,l), 4); - assert_equal(find_first(3,l,func=gt ), 3); - assert_equal(find_first(3,l,func=gte), 1); - assert_equal(find_first(3,l,func=lt ), 0); - assert_equal(find_first(7,l,func=lt ), 2); - assert_equal(find_first(7,l,func=lte), 0); - assert_equal(find_first(7,l,start=1,func=gte), 1); - assert_equal(find_first(7,l,start=3,func=gte), 3); + assert_equal(find_first(f_eq(1),l), 3); + assert_equal(find_first(f_eq(1),l,start=4), 5); + assert_equal(find_first(f_eq(6),l), 4); + assert_equal(find_first(f_gt(8),l), 8); + assert_equal(find_first(f_gte(8),l), 2); + assert_equal(find_first(f_lt(3),l), 3); + assert_equal(find_first(f_lt(7),l), 1); + assert_equal(find_first(f_lte(8),l), 0); + assert_equal(find_first(f_gte(8),l,start=1), 2); + assert_equal(find_first(f_gte(8),l,start=3), 8); } -//test_find_first(); +test_find_first(); module test_binsearch() { @@ -127,8 +127,8 @@ test_simple_hash(); module test_f_1arg() { - assert_equal(str(f_1arg(function (x) x)), "function(a) ((a == undef) ? function(x) func(x) : function() func(a))"); - assert_equal(str(f_1arg(function (x) x)(3)), "function() func(a)"); + assert_equal(str(f_1arg(function (x) x)), "function(a) ((a == undef) ? function(x) target_func(x) : function() target_func(a))"); + assert_equal(str(f_1arg(function (x) x)(3)), "function() target_func(a)"); assert_equal(f_1arg(function (x) x)()(4), 4); assert_equal(f_1arg(function (x) x)(3)(), 3); } @@ -136,11 +136,11 @@ test_f_1arg(); module test_f_2arg() { - assert_equal(str(f_2arg(function (a,b) a+b)), "function(a, b) (((a == undef) && (b == undef)) ? function(x, y) func(x, y) : ((a == undef) ? function(x) func(x, b) : ((b == undef) ? function(x) func(a, x) : function() func(a, b))))"); - assert_equal(str(f_2arg(function (a,b) a+b)(3)), "function(x) func(a, x)"); - assert_equal(str(f_2arg(function (a,b) a+b)(a=3)), "function(x) func(a, x)"); - assert_equal(str(f_2arg(function (a,b) a+b)(b=3)), "function(x) func(x, b)"); - assert_equal(str(f_2arg(function (a,b) a+b)(3,4)), "function() func(a, b)"); + assert_equal(str(f_2arg(function (a,b) a+b)), "function(a, b) (((a == undef) && (b == undef)) ? function(x, y) target_func(x, y) : ((a == undef) ? function(x) target_func(x, b) : ((b == undef) ? function(x) target_func(a, x) : function() target_func(a, b))))"); + assert_equal(str(f_2arg(function (a,b) a+b)(3)), "function(x) target_func(a, x)"); + assert_equal(str(f_2arg(function (a,b) a+b)(a=3)), "function(x) target_func(a, x)"); + assert_equal(str(f_2arg(function (a,b) a+b)(b=3)), "function(x) target_func(x, b)"); + assert_equal(str(f_2arg(function (a,b) a+b)(3,4)), "function() target_func(a, b)"); assert_equal(f_2arg(function (a,b) a+b)()(4,2), 6); assert_equal(f_2arg(function (a,b) a+b)(3)(7), 10); assert_equal(f_2arg(function (a,b) a+b)(a=2)(7), 9); @@ -150,10 +150,10 @@ test_f_2arg(); module test_f_3arg() { - assert_equal(str(f_3arg(function (a,b,c) a+b+c)), "function(a, b, c) ((((a == undef) && (b == undef)) && (c == undef)) ? function(x, y, z) func(x, y, z) : (((a == undef) && (b == undef)) ? function(x, y) func(x, y, c) : (((a == undef) && (c == undef)) ? function(x, y) func(x, b, y) : (((b == undef) && (c == undef)) ? function(x, y) func(a, x, y) : ((a == undef) ? function(x) func(x, b, c) : ((b == undef) ? function(x) func(a, x, c) : ((c == undef) ? function(x) func(a, b, x) : function() func(a, b, c))))))))"); - assert_equal(str(f_3arg(function (a,b,c) a+b+c)(3)), "function(x, y) func(a, x, y)"); - assert_equal(str(f_3arg(function (a,b,c) a+b+c)(3,4)), "function(x) func(a, b, x)"); - assert_equal(str(f_3arg(function (a,b,c) a+b+c)(3,4,1)), "function() func(a, b, c)"); + assert_equal(str(f_3arg(function (a,b,c) a+b+c)), "function(a, b, c) ((((a == undef) && (b == undef)) && (c == undef)) ? function(x, y, z) target_func(x, y, z) : (((a == undef) && (b == undef)) ? function(x, y) target_func(x, y, c) : (((a == undef) && (c == undef)) ? function(x, y) target_func(x, b, y) : (((b == undef) && (c == undef)) ? function(x, y) target_func(a, x, y) : ((a == undef) ? function(x) target_func(x, b, c) : ((b == undef) ? function(x) target_func(a, x, c) : ((c == undef) ? function(x) target_func(a, b, x) : function() target_func(a, b, c))))))))"); + assert_equal(str(f_3arg(function (a,b,c) a+b+c)(3)), "function(x, y) target_func(a, x, y)"); + assert_equal(str(f_3arg(function (a,b,c) a+b+c)(3,4)), "function(x) target_func(a, b, x)"); + assert_equal(str(f_3arg(function (a,b,c) a+b+c)(3,4,1)), "function() target_func(a, b, c)"); assert_equal(f_3arg(function (a,b,c) a+b+c)()(4,2,1), 7); assert_equal(f_3arg(function (a,b,c) a+b+c)(3)(7,3), 13); assert_equal(f_3arg(function (a,b,c) a+b+c)(a=2)(7,1), 10); @@ -165,22 +165,22 @@ test_f_3arg(); module test_ival() { - assert_equal(str(ival(function (a) a)), "function(a, b) func(a)"); + assert_equal(str(ival(function (a) a)), "function(a, b) target_func(a)"); assert_equal(ival(function (a) a)(3,5), 3); } test_ival(); module test_xval() { - assert_equal(str(xval(function (a) a)), "function(a, b) func(b)"); + assert_equal(str(xval(function (a) a)), "function(a, b) target_func(b)"); assert_equal(xval(function (a) a)(3,5), 5); } test_xval(); module _test_fn1arg(dafunc,tests) { - assert_equal(str(dafunc()), "function(x) func(x)"); - assert_equal(str(dafunc(3)), "function() func(a)"); + assert_equal(str(dafunc()), "function(x) target_func(x)"); + assert_equal(str(dafunc(3)), "function() target_func(a)"); for (test = tests) { a = test[0]; r = test[1]; @@ -191,11 +191,11 @@ module _test_fn1arg(dafunc,tests) { module _test_fn2arg(dafunc,tests) { - assert_equal(str(dafunc()), "function(x, y) func(x, y)"); - assert_equal(str(dafunc(3)), "function(x) func(a, x)"); - assert_equal(str(dafunc(a=3)), "function(x) func(a, x)"); - assert_equal(str(dafunc(b=3)), "function(x) func(x, b)"); - assert_equal(str(dafunc(3,4)), "function() func(a, b)"); + assert_equal(str(dafunc()), "function(x, y) target_func(x, y)"); + assert_equal(str(dafunc(3)), "function(x) target_func(a, x)"); + assert_equal(str(dafunc(a=3)), "function(x) target_func(a, x)"); + assert_equal(str(dafunc(b=3)), "function(x) target_func(x, b)"); + assert_equal(str(dafunc(3,4)), "function() target_func(a, b)"); for (test = tests) { a = test[0]; b = test[1]; @@ -210,17 +210,33 @@ module _test_fn2arg(dafunc,tests) { } +module _test_fn2arg_simple(dafunc,tests) { + assert_equal(str(dafunc()), "function(x, y) target_func(x, y)"); + assert_equal(str(dafunc(3)), "function(x) target_func(x, a)"); + assert_equal(str(dafunc(3,4)), "function() target_func(a, b)"); + for (test = tests) { + a = test[0]; + b = test[1]; + r = test[2]; + assert_equal(dafunc(a=a,b=b)(), r); + assert_equal(dafunc(a,b)(), r); + assert_equal(dafunc(b)(a), r); + assert_equal(dafunc()(a,b), r); + } +} + + module _test_fn3arg(dafunc,tests) { - assert_equal(str(dafunc()), "function(x, y, z) func(x, y, z)"); - assert_equal(str(dafunc(3)), "function(x, y) func(a, x, y)"); - assert_equal(str(dafunc(a=3)), "function(x, y) func(a, x, y)"); - assert_equal(str(dafunc(b=3)), "function(x, y) func(x, b, y)"); - assert_equal(str(dafunc(c=3)), "function(x, y) func(x, y, c)"); - assert_equal(str(dafunc(3,4)), "function(x) func(a, b, x)"); - assert_equal(str(dafunc(a=3,b=4)), "function(x) func(a, b, x)"); - assert_equal(str(dafunc(a=3,c=4)), "function(x) func(a, x, c)"); - assert_equal(str(dafunc(b=3,c=4)), "function(x) func(x, b, c)"); - assert_equal(str(dafunc(3,4,5)), "function() func(a, b, c)"); + assert_equal(str(dafunc()), "function(x, y, z) target_func(x, y, z)"); + assert_equal(str(dafunc(3)), "function(x, y) target_func(a, x, y)"); + assert_equal(str(dafunc(a=3)), "function(x, y) target_func(a, x, y)"); + assert_equal(str(dafunc(b=3)), "function(x, y) target_func(x, b, y)"); + assert_equal(str(dafunc(c=3)), "function(x, y) target_func(x, y, c)"); + assert_equal(str(dafunc(3,4)), "function(x) target_func(a, b, x)"); + assert_equal(str(dafunc(a=3,b=4)), "function(x) target_func(a, b, x)"); + assert_equal(str(dafunc(a=3,c=4)), "function(x) target_func(a, x, c)"); + assert_equal(str(dafunc(b=3,c=4)), "function(x) target_func(x, b, c)"); + assert_equal(str(dafunc(3,4,5)), "function() target_func(a, b, c)"); for (test = tests) { a = test[0]; b = test[1]; @@ -241,7 +257,7 @@ module _test_fn3arg(dafunc,tests) { module test_f_cmp() { - _test_fn2arg( + _test_fn2arg_simple( function (a,b) f_cmp(a,b), [[4,3,1],[3,3,0],[3,4,-1]] ); @@ -250,7 +266,7 @@ test_f_cmp(); module test_f_gt() { - _test_fn2arg( + _test_fn2arg_simple( function (a,b) f_gt(a,b), [[4,3,true],[3,3,false],[3,4,false]] ); @@ -259,7 +275,7 @@ test_f_gt(); module test_f_gte() { - _test_fn2arg( + _test_fn2arg_simple( function (a,b) f_gte(a,b), [[4,3,true],[3,3,true],[3,4,false]] ); @@ -268,7 +284,7 @@ test_f_gte(); module test_f_lt() { - _test_fn2arg( + _test_fn2arg_simple( function (a,b) f_lt(a,b), [[4,3,false],[3,3,false],[3,4,true]] ); @@ -277,7 +293,7 @@ test_f_lt(); module test_f_lte() { - _test_fn2arg( + _test_fn2arg_simple( function (a,b) f_lte(a,b), [[4,3,false],[3,3,true],[3,4,true]] ); @@ -286,7 +302,7 @@ test_f_lte(); module test_f_eq() { - _test_fn2arg( + _test_fn2arg_simple( function (a,b) f_eq(a,b), [[4,3,false],[3,3,true],[3,4,false]] ); @@ -295,7 +311,7 @@ test_f_eq(); module test_f_neq() { - _test_fn2arg( + _test_fn2arg_simple( function (a,b) f_neq(a,b), [[4,3,true],[3,3,false],[3,4,true]] ); @@ -304,7 +320,7 @@ test_f_neq(); module test_f_approx() { - _test_fn2arg( + _test_fn2arg_simple( function (a,b) f_approx(a,b), [[4,3,false],[3,3,true],[3,4,false],[1/3,0.33333333333333333333333333,true]] ); @@ -313,7 +329,7 @@ test_f_approx(); module test_f_napprox() { - _test_fn2arg( + _test_fn2arg_simple( function (a,b) f_napprox(a,b), [[4,3,true],[3,3,false],[3,4,true],[1/3,0.33333333333333333333333333,false]] );