From 47f0c88c4af37ec19970e3c494535f49c2b3dd55 Mon Sep 17 00:00:00 2001 From: Adrian Mariano Date: Sat, 11 Sep 2021 23:11:18 -0400 Subject: [PATCH] added example and explantion to point_in_polygon --- geometry.scad | 76 +++++++++++++++++++++++++++++++++++----- tests/test_geometry.scad | 16 ++++----- 2 files changed, 76 insertions(+), 16 deletions(-) diff --git a/geometry.scad b/geometry.scad index 51175c7..cfd711e 100644 --- a/geometry.scad +++ b/geometry.scad @@ -1317,8 +1317,7 @@ function polygon_normal(poly) = // Topics: Geometry, Polygons // Description: // This function tests whether the given 2D point is inside, outside or on the boundary of -// the specified 2D polygon using either the Nonzero Winding rule or the Even-Odd rule. -// See https://en.wikipedia.org/wiki/Nonzero-rule and https://en.wikipedia.org/wiki/Even–odd_rule. +// the specified 2D polygon. // The polygon is given as a list of 2D points, not including the repeated end point. // Returns -1 if the point is outside the polygon. // Returns 0 if the point is on the boundary. @@ -1326,11 +1325,72 @@ function polygon_normal(poly) = // The polygon does not need to be simple: it may have self-intersections. // But the polygon cannot have holes (it must be simply connected). // Rounding errors may give mixed results for points on or near the boundary. +// . +// When polygons intersect themselves different definitions exist for determining which points +// are inside the polygon. The figure below shows the difference. +// OpenSCAD uses the Even-Odd rule when creating polygons, where membership in overlapping regions +// depends on how many times they overlap. The Nonzero rule considers point inside the polygon if +// the polygon overlaps them any number of times. For more information see +// https://en.wikipedia.org/wiki/Nonzero-rule and https://en.wikipedia.org/wiki/Even–odd_rule. +// Figure(2D,Med): +// a=20*2/3; +// b=30*2/3; +// ofs = 17*2/3; +// curve = [for(theta=[0:10:140]) [a * theta/360*2*PI - b*sin(theta), a-b*cos(theta)-20*2/3]]; +// path = deduplicate(concat( reverse(offset(curve,r=ofs)), +// xflip(offset(curve,r=ofs)), +// xflip(reverse(curve)), +// curve +// )); +// left(30){ +// polygon(path); +// color("red")stroke(path, width=1, closed=true); +// color("red")back(28)text("Even-Odd", size=5, halign="center"); +// } +// right(30){ +// dp = decompose_path(path,closed=true); +// region(dp); +// color("red"){stroke(path,width=1,closed=true); +// back(28)text("Nonzero", size=5, halign="center"); +// } +// } // Arguments: // point = The 2D point to check position of. // poly = The list of 2D path points forming the perimeter of the polygon. // nonzero = The rule to use: true for "Nonzero" rule and false for "Even-Odd" (Default: true ) // eps = Tolerance in geometric comparisons. Default: `EPSILON` (1e-9) +// Example: With nonzero set to true, we get this result. Green dots are inside the polygon and red are outside: +// a=20*2/3; +// b=30*2/3; +// ofs = 17*2/3; +// curve = [for(theta=[0:10:140]) [a * theta/360*2*PI - b*sin(theta), a-b*cos(theta)]]; +// path = deduplicate(concat( reverse(offset(curve,r=ofs)), +// xflip(offset(curve,r=ofs)), +// xflip(reverse(curve)), +// curve +// )); +// stroke(path); +// pts = [[0,0],[10,0],[0,20]]; +// for(p=pts){ +// color(point_in_polygon(p,path)==1 ? "green" : "red") +// move(p)circle(r=1, $fn=12); +// } +// Example: With nonzero set to false, one dot changes color: +// a=20*2/3; +// b=30*2/3; +// ofs = 17*2/3; +// curve = [for(theta=[0:10:140]) [a * theta/360*2*PI - b*sin(theta), a-b*cos(theta)]]; +// path = deduplicate(concat( reverse(offset(curve,r=ofs)), +// xflip(offset(curve,r=ofs)), +// xflip(reverse(curve)), +// curve +// )); +// stroke(path); +// pts = [[0,0],[10,0],[0,20]]; +// for(p=pts){ +// color(point_in_polygon(p,path,nonzero=false)==1 ? "green" : "red") +// move(p)circle(r=1, $fn=12); +// } function point_in_polygon(point, poly, nonzero=true, eps=EPSILON) = // Original algorithms from http://geomalgorithms.com/a03-_inclusion.html assert( is_vector(point,2) && is_path(poly,dim=2) && len(poly)>2, @@ -1561,9 +1621,9 @@ function align_polygon(reference, poly, angles, cp) = // Section: Convex Sets -// Function: is_convex_polygon() +// Function: is_polygon_convex() // Usage: -// test = is_convex_polygon(poly); +// test = is_polygon_convex(poly); // Topics: Geometry, Convexity, Test // Description: // Returns true if the given 2D or 3D polygon is convex. @@ -1573,12 +1633,12 @@ function align_polygon(reference, poly, angles, cp) = // poly = Polygon to check. // eps = Tolerance for the collinearity test. Default: EPSILON. // Example: -// test1 = is_convex_polygon(circle(d=50)); // Returns: true -// test2 = is_convex_polygon(rot([50,120,30], p=path3d(circle(1,$fn=50)))); // Returns: true +// test1 = is_polygon_convex(circle(d=50)); // Returns: true +// test2 = is_polygon_convex(rot([50,120,30], p=path3d(circle(1,$fn=50)))); // Returns: true // Example: // spiral = [for (i=[0:36]) let(a=-i*10) (10+i)*[cos(a),sin(a)]]; -// test = is_convex_polygon(spiral); // Returns: false -function is_convex_polygon(poly,eps=EPSILON) = +// test = is_polygon_convex(spiral); // Returns: false +function is_polygon_convex(poly,eps=EPSILON) = assert(is_path(poly), "The input should be a 2D or 3D polygon." ) let( lp = len(poly), diff --git a/tests/test_geometry.scad b/tests/test_geometry.scad index 86a459c..8deb425 100644 --- a/tests/test_geometry.scad +++ b/tests/test_geometry.scad @@ -42,7 +42,7 @@ test_circle_point_tangents(); test_noncollinear_triple(); test_polygon_area(); -test_is_convex_polygon(); +test_is_polygon_convex(); test_polygon_shift(); test_polygon_shift_to_closest_point(); test_reindex_polygon(); @@ -726,14 +726,14 @@ module test_polygon_area() { *test_polygon_area(); -module test_is_convex_polygon() { - assert(is_convex_polygon([[1,1],[-1,1],[-1,-1],[1,-1]])); - assert(is_convex_polygon(circle(r=50,$fn=1000))); - assert(is_convex_polygon(rot([50,120,30], p=path3d(circle(1,$fn=50))))); - assert(!is_convex_polygon([[1,1],[0,0],[-1,1],[-1,-1],[1,-1]])); - assert(!is_convex_polygon([for (i=[0:36]) let(a=-i*10) (10+i)*[cos(a),sin(a)]])); // spiral +module test_is_polygon_convex() { + assert(is_polygon_convex([[1,1],[-1,1],[-1,-1],[1,-1]])); + assert(is_polygon_convex(circle(r=50,$fn=1000))); + assert(is_polygon_convex(rot([50,120,30], p=path3d(circle(1,$fn=50))))); + assert(!is_polygon_convex([[1,1],[0,0],[-1,1],[-1,-1],[1,-1]])); + assert(!is_polygon_convex([for (i=[0:36]) let(a=-i*10) (10+i)*[cos(a),sin(a)]])); // spiral } -*test_is_convex_polygon(); +*test_is_polygon_convex(); module test_polygon_shift() {