From aa3af91889980336469182df72a428c84b33291c Mon Sep 17 00:00:00 2001 From: Revar Desmera Date: Wed, 1 May 2019 19:28:02 -0700 Subject: [PATCH] Renamed some geometry.scad functions. Added geometry.scad tests. --- geometry.scad | 50 +++++----- tests/test_geometry.scad | 202 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 224 insertions(+), 28 deletions(-) create mode 100644 tests/test_geometry.scad diff --git a/geometry.scad b/geometry.scad index d65e0b7..7fa0834 100644 --- a/geometry.scad +++ b/geometry.scad @@ -39,25 +39,25 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Section: Lines and Triangles -// Function: point_on_segment() +// Function: point_on_segment2d() // Usage: -// point_on_segment(point, edge); +// point_on_segment2d(point, edge); // Description: // Determine if the point is on the line segment between two points. // Returns true if yes, and false if not. // Arguments: // point = The point to test. // edge = Array of two points forming the line segment to test against. -function point_on_segment(point, edge) = +function point_on_segment2d(point, edge) = point==edge[0] || point==edge[1] || // The point is an endpoint sign(edge[0].x-point.x)==sign(point.x-edge[1].x) // point is in between the && sign(edge[0].y-point.y)==sign(point.y-edge[1].y) // edge endpoints - && point_left_of_segment(point, edge)==0; // and on the line defined by edge + && point_left_of_segment2d(point, edge)==0; // and on the line defined by edge -// Function: point_left_of_segment() +// Function: point_left_of_segment2d() // Usage: -// point_left_of_segment(point, edge); +// point_left_of_segment2d(point, edge); // Description: // Return >0 if point is left of the line defined by edge. // Return =0 if point is on the line. @@ -65,16 +65,16 @@ function point_on_segment(point, edge) = // Arguments: // point = The point to check position of. // edge = Array of two points forming the line segment to test against. -function point_left_of_segment(point, edge) = +function point_left_of_segment2d(point, edge) = (edge[1].x-edge[0].x) * (point.y-edge[0].y) - (point.x-edge[0].x) * (edge[1].y-edge[0].y); // Internal non-exposed function. function _point_above_below_segment(point, edge) = edge[0].y <= point.y? ( - (edge[1].y > point.y && point_left_of_segment(point, edge) > 0)? 1 : 0 + (edge[1].y > point.y && point_left_of_segment2d(point, edge) > 0)? 1 : 0 ) : ( - (edge[1].y <= point.y && point_left_of_segment(point, edge) < 0)? -1 : 0 + (edge[1].y <= point.y && point_left_of_segment2d(point, edge) < 0)? -1 : 0 ); @@ -82,7 +82,7 @@ function _point_above_below_segment(point, edge) = // Usage: // right_of_line2d(line, pt) // Description: -// Returns true if the given point is to the left of the given line. +// Returns true if the given point is to the left of the extended line defined by two points on it. // Arguments: // line = A list of two points. // pt = The point to test. @@ -255,15 +255,12 @@ function in_front_of_plane(plane, point) = // simplify_path(path, [eps]) // Arguments: // path = A list of 2D path points. -// eps = Largest angle variance allowed. Default: EPSILON (1-e9) degrees. -function simplify_path(path, eps=EPSILON, _a=0, _b=2, _acc=[]) = - (_b >= len(path))? concat([path[0]], _acc, [path[len(path)-1]]) : - simplify_path( - path, eps, - (collinear_indexed(path, _a, _b-1, _b, eps=eps)? _a : _b-1), - _b+1, - (collinear_indexed(path, _a, _b-1, _b, eps=eps)? _acc : concat(_acc, [path[_b-1]])) - ); +// eps = Largest positional variance allowed. Default: `EPSILON` (1-e9) +function simplify_path(path, eps=EPSILON) = + len(path)<=2? path : let( + indices = concat([0], [for (i=[1:len(path)-2]) if (!collinear_indexed(path, i-1, i, i+1, eps=eps)) i], [len(path)-1]) + ) [for (i = indices) path[i]]; + // Function: simplify_path_indexed() @@ -276,14 +273,11 @@ function simplify_path(path, eps=EPSILON, _a=0, _b=2, _acc=[]) = // points = A list of points. // path = A list of indexes into `points` that forms a path. // eps = Largest angle variance allowed. Default: EPSILON (1-e9) degrees. -function simplify_path_indexed(points, path, eps=EPSILON, _a=0, _b=2, _acc=[]) = - (_b >= len(path))? concat([path[0]], _acc, [path[len(path)-1]]) : - simplify_path_indexed( - points, path, eps, - (collinear_indexed(points, path[_a], path[_b-1], path[_b], eps=eps)? _a : _b-1), - _b+1, - (collinear_indexed(points, path[_a], path[_b-1], path[_b], eps=eps)? _acc : concat(_acc, [path[_b-1]])) - ); +function simplify_path_indexed(points, path, eps=EPSILON) = + len(path)<=2? path : let( + indices = concat([0], [for (i=[1:len(path)-2]) if (!collinear_indexed(points, path[i-1], path[i], path[i+1], eps=eps)) i], [len(path)-1]) + ) [for (i = indices) path[i]]; + // Function: point_in_polygon() @@ -304,7 +298,7 @@ function simplify_path_indexed(points, path, eps=EPSILON, _a=0, _b=2, _acc=[]) = // path = The list of 2D path points forming the perimeter of the polygon. function point_in_polygon(point, path) = // Does the point lie on any edges? If so return 0. - sum([for(i=[0:len(path)-1]) point_on_segment(point, select(path, i, i+1))?1:0])>0 ? 0 : + sum([for(i=[0:len(path)-1]) point_on_segment2d(point, select(path, i, i+1))?1:0])>0 ? 0 : // Otherwise compute winding number and return 1 for interior, -1 for exterior sum([for(i=[0:len(path)-1]) _point_above_below_segment(point, select(path, i, i+1))]) != 0 ? 1 : -1; diff --git a/tests/test_geometry.scad b/tests/test_geometry.scad new file mode 100644 index 0000000..da946bf --- /dev/null +++ b/tests/test_geometry.scad @@ -0,0 +1,202 @@ +include + + +module test_point_on_segment2d() { + assert(point_on_segment2d([-15,0], [[-10,0], [10,0]]) == false); + assert(point_on_segment2d([-10,0], [[-10,0], [10,0]]) == true); + assert(point_on_segment2d([-5,0], [[-10,0], [10,0]]) == true); + assert(point_on_segment2d([0,0], [[-10,0], [10,0]]) == true); + assert(point_on_segment2d([3,3], [[-10,0], [10,0]]) == false); + assert(point_on_segment2d([5,0], [[-10,0], [10,0]]) == true); + assert(point_on_segment2d([10,0], [[-10,0], [10,0]]) == true); + assert(point_on_segment2d([15,0], [[-10,0], [10,0]]) == false); + + assert(point_on_segment2d([0,-15], [[0,-10], [0,10]]) == false); + assert(point_on_segment2d([0,-10], [[0,-10], [0,10]]) == true); + assert(point_on_segment2d([0, -5], [[0,-10], [0,10]]) == true); + assert(point_on_segment2d([0, 0], [[0,-10], [0,10]]) == true); + assert(point_on_segment2d([3, 3], [[0,-10], [0,10]]) == false); + assert(point_on_segment2d([0, 5], [[0,-10], [0,10]]) == true); + assert(point_on_segment2d([0, 10], [[0,-10], [0,10]]) == true); + assert(point_on_segment2d([0, 15], [[0,-10], [0,10]]) == false); + + assert(point_on_segment2d([-15,-15], [[-10,-10], [10,10]]) == false); + assert(point_on_segment2d([-10,-10], [[-10,-10], [10,10]]) == true); + assert(point_on_segment2d([ -5, -5], [[-10,-10], [10,10]]) == true); + assert(point_on_segment2d([ 0, 0], [[-10,-10], [10,10]]) == true); + assert(point_on_segment2d([ 0, 3], [[-10,-10], [10,10]]) == false); + assert(point_on_segment2d([ 5, 5], [[-10,-10], [10,10]]) == true); + assert(point_on_segment2d([ 10, 10], [[-10,-10], [10,10]]) == true); + assert(point_on_segment2d([ 15, 15], [[-10,-10], [10,10]]) == false); +} +test_point_on_segment2d(); + + +module test_point_left_of_segment() { + assert(point_left_of_segment2d([ -3, 0], [[-10,-10], [10,10]]) > 0); + assert(point_left_of_segment2d([ 0, 0], [[-10,-10], [10,10]]) == 0); + assert(point_left_of_segment2d([ 3, 0], [[-10,-10], [10,10]]) < 0); +} +test_point_left_of_segment(); + + +module test_right_of_line2d() { + assert(right_of_line2d([[-10,-10], [10,10]], [ -3, 0]) == false); + assert(right_of_line2d([[-10,-10], [10,10]], [ 0, 0]) == false); + assert(right_of_line2d([[-10,-10], [10,10]], [ 3, 0]) == true); +} +test_right_of_line2d(); + + +module test_collinear() { + assert(collinear([-10,-10], [-15, -16], [10,10]) == false); + assert(collinear([-10,-10], [-15, -15], [10,10]) == true); + assert(collinear([-10,-10], [ -3, 0], [10,10]) == false); + assert(collinear([-10,-10], [ 0, 0], [10,10]) == true); + assert(collinear([-10,-10], [ 3, 0], [10,10]) == false); + assert(collinear([-10,-10], [ 15, 15], [10,10]) == true); + assert(collinear([-10,-10], [ 15, 16], [10,10]) == false); +} +test_collinear(); + + +module test_collinear_indexed() { + pts = [ + [-20,-20], [-10,-20], [0,-10], [10,0], [20,10], [20,20], [15,30] + ]; + assert(collinear_indexed(pts, 0,1,2) == false); + assert(collinear_indexed(pts, 1,2,3) == true); + assert(collinear_indexed(pts, 2,3,4) == true); + assert(collinear_indexed(pts, 3,4,5) == false); + assert(collinear_indexed(pts, 4,5,6) == false); + assert(collinear_indexed(pts, 4,3,2) == true); + assert(collinear_indexed(pts, 0,5,6) == false); +} +test_collinear_indexed(); + + +module test_distance_from_line() { + assert(abs(distance_from_line([[-10,-10,-10], [10,10,10]], [1,1,1])) < EPSILON); + assert(abs(distance_from_line([[-10,-10,-10], [10,10,10]], [-1,-1,-1])) < EPSILON); + assert(abs(distance_from_line([[-10,-10,-10], [10,10,10]], [1,-1,0]) - sqrt(2)) < EPSILON); + assert(abs(distance_from_line([[-10,-10,-10], [10,10,10]], [8,-8,0]) - 8*sqrt(2)) < EPSILON); +} +test_distance_from_line(); + + +module test_triangle_area2d() { + assert(abs(triangle_area2d([0,0], [0,10], [10,0]) + 50) < EPSILON); + assert(abs(triangle_area2d([0,0], [0,10], [0,15])) < EPSILON); + assert(abs(triangle_area2d([0,0], [10,0], [0,10]) - 50) < EPSILON); +} +test_triangle_area2d(); + + +module test_plane3pt() { + assert(plane3pt([0,0,20], [0,10,10], [0,0,0]) == [1,0,0,0]); + assert(plane3pt([2,0,20], [2,10,10], [2,0,0]) == [1,0,0,2]); + assert(plane3pt([0,0,0], [10,0,10], [0,0,20]) == [0,1,0,0]); + assert(plane3pt([0,2,0], [10,2,10], [0,2,20]) == [0,1,0,2]); + assert(plane3pt([0,0,0], [10,10,0], [20,0,0]) == [0,0,1,0]); + assert(plane3pt([0,0,2], [10,10,2], [20,0,2]) == [0,0,1,2]); +} +test_plane3pt(); + + +module test_plane3pt_indexed() { + pts = [ + [0,0,0], [10,0,0], [0,10,0], [0,0,10] + ]; + s13 = sqrt(1/3); + assert(plane3pt_indexed(pts, 0,3,2) == [1,0,0,0]); + assert(plane3pt_indexed(pts, 0,2,3) == [-1,0,0,0]); + assert(plane3pt_indexed(pts, 0,1,3) == [0,1,0,0]); + assert(plane3pt_indexed(pts, 0,3,1) == [0,-1,0,0]); + assert(plane3pt_indexed(pts, 0,2,1) == [0,0,1,0]); + assert(plane3pt_indexed(pts, 0,1,2) == [0,0,-1,0]); + assert(plane3pt_indexed(pts, 3,2,1) == [s13,s13,s13,10*s13]); + assert(plane3pt_indexed(pts, 1,2,3) == [-s13,-s13,-s13,-10*s13]); +} +test_plane3pt_indexed(); + + +module test_distance_from_plane() { + plane1 = plane3pt([-10,0,0], [0,10,0], [10,0,0]); + assert(distance_from_plane(plane1, [0,0,5]) == 5); + assert(distance_from_plane(plane1, [5,5,8]) == 8); +} +test_distance_from_plane(); + + +module test_coplanar() { + plane = plane3pt([0,0,0], [0,10,10], [10,0,10]); + assert(coplanar(plane, [5,5,10]) == true); + assert(coplanar(plane, [10/3,10/3,20/3]) == true); + assert(coplanar(plane, [0,0,0]) == true); + assert(coplanar(plane, [1,1,0]) == false); + assert(coplanar(plane, [-1,1,0]) == true); + assert(coplanar(plane, [1,-1,0]) == true); + assert(coplanar(plane, [5,5,5]) == false); +} +test_coplanar(); + + +module test_in_front_of_plane() { + plane = plane3pt([0,0,0], [0,10,10], [10,0,10]); + assert(in_front_of_plane(plane, [5,5,10]) == false); + assert(in_front_of_plane(plane, [-5,0,0]) == true); + assert(in_front_of_plane(plane, [5,0,0]) == false); + assert(in_front_of_plane(plane, [0,-5,0]) == true); + assert(in_front_of_plane(plane, [0,5,0]) == false); + assert(in_front_of_plane(plane, [0,0,5]) == true); + assert(in_front_of_plane(plane, [0,0,-5]) == false); +} +test_in_front_of_plane(); + + +module test_simplify_path() { + path = [[-20,-20], [-10,-20], [0,-10], [10,0], [20,10], [20,20], [15,30]]; + assert(simplify_path(path) == [[-20,-20], [-10,-20], [20,10], [20,20], [15,30]]); +} +test_simplify_path(); + + +module test_simplify_path_indexed() { + pts = [[10,0], [0,-10], [20,20], [20,10], [-20,-20], [15,30], [-10,-20]]; + path = [4,6,1,0,3,2,5]; + assert(simplify_path_indexed(pts, path) == [4,6,3,2,5]); +} +test_simplify_path_indexed(); + + +module test_point_in_polygon() { + poly = [for (a=[0:30:359]) 10*[cos(a),sin(a)]]; + assert(point_in_polygon([0,0], poly) == 1); + assert(point_in_polygon([20,0], poly) == -1); + assert(point_in_polygon([5,5], poly) == 1); + assert(point_in_polygon([-5,5], poly) == 1); + assert(point_in_polygon([-5,-5], poly) == 1); + assert(point_in_polygon([5,-5], poly) == 1); + assert(point_in_polygon([-10,-10], poly) == -1); + assert(point_in_polygon([10,0], poly) == 0); + assert(point_in_polygon([0,10], poly) == 0); + assert(point_in_polygon([0,-10], poly) == 0); +} +test_point_in_polygon(); + + +module test_pointlist_bounds() { + pts = [ + [-53,27,12], + [-63,97,36], + [84,-32,-5], + [63,-24,42], + [23,57,-42] + ]; + assert(pointlist_bounds(pts) == [[-63,-32,-42], [84,97,42]]); +} +test_pointlist_bounds(); + + + +// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap