mirror of
https://github.com/BelfrySCAD/BOSL2.git
synced 2026-01-04 06:28:08 +00:00
Update geometry.scad
This commit is contained in:
parent
5659fa9ef5
commit
daf7ca24dc
1 changed files with 72 additions and 69 deletions
141
geometry.scad
141
geometry.scad
|
|
@ -11,6 +11,9 @@
|
|||
// FileFootnotes: STD=Included in std.scad
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
_BOSL2_GEOMETRY = is_undef(_BOSL2_STD) && (is_undef(BOSL2_NO_STD_WARNING) || !BOSL2_NO_STD_WARNING) ?
|
||||
echo("Warning: geometry.scad included without std.scad; dependencies may be missing\nSet BOSL2_NO_STD_WARNING = true to mute this warning.") true : true;
|
||||
|
||||
|
||||
// Section: Lines, Rays, and Segments
|
||||
|
||||
|
|
@ -29,14 +32,14 @@
|
|||
// point = The point to test.
|
||||
// line = Array of two points defining the line, ray, or segment to test against.
|
||||
// bounded = boolean or list of two booleans defining endpoint conditions for the line. If false treat the line as an unbounded line. If true treat it as a segment. If [true,false] treat as a ray, based at the first endpoint. Default: false
|
||||
// eps = Tolerance in geometric comparisons. Default: `EPSILON` (1e-9)
|
||||
function is_point_on_line(point, line, bounded=false, eps=EPSILON) =
|
||||
// eps = Tolerance in geometric comparisons. Default: `_EPSILON` (1e-9)
|
||||
function is_point_on_line(point, line, bounded=false, eps=_EPSILON) =
|
||||
assert(is_finite(eps) && (eps>=0), "\nThe tolerance should be a non-negative value." )
|
||||
assert(is_vector(point), "\nPoint must be a vector.")
|
||||
assert(_valid_line(line, len(point),eps),"\nGiven line is not valid.")
|
||||
_is_point_on_line(point, line, bounded,eps);
|
||||
|
||||
function _is_point_on_line(point, line, bounded=false, eps=EPSILON) =
|
||||
function _is_point_on_line(point, line, bounded=false, eps=_EPSILON) =
|
||||
let(
|
||||
v1 = (line[1]-line[0]),
|
||||
v0 = (point-line[0]),
|
||||
|
|
@ -55,12 +58,12 @@ function _dist2line(d,n) = norm(d-(d * n) * n);
|
|||
|
||||
|
||||
///Internal
|
||||
function _valid_line(line,dim,eps=EPSILON) =
|
||||
function _valid_line(line,dim,eps=_EPSILON) =
|
||||
is_matrix(line,2,dim)
|
||||
&& norm(line[1]-line[0])>eps*max(norm(line[1]),norm(line[0]));
|
||||
|
||||
//Internal
|
||||
function _valid_plane(p, eps=EPSILON) = is_vector(p,4) && ! approx(norm(p),0,eps);
|
||||
function _valid_plane(p, eps=_EPSILON) = is_vector(p,4) && ! approx(norm(p),0,eps);
|
||||
|
||||
|
||||
/// Internal Function: _is_at_left()
|
||||
|
|
@ -73,7 +76,7 @@ function _valid_plane(p, eps=EPSILON) = is_vector(p,4) && ! approx(norm(p),0,eps
|
|||
/// pt = The 2d point to check position of.
|
||||
/// line = Array of two 2d points forming the line segment to test against.
|
||||
/// eps = Tolerance in the geometrical tests.
|
||||
function _is_at_left(pt,line,eps=EPSILON) = _tri_class([pt,line[0],line[1]],eps) <= 0;
|
||||
function _is_at_left(pt,line,eps=_EPSILON) = _tri_class([pt,line[0],line[1]],eps) <= 0;
|
||||
|
||||
|
||||
/// Internal Function: _degenerate_tri()
|
||||
|
|
@ -100,7 +103,7 @@ function _degenerate_tri(tri,eps) =
|
|||
/// Arguments:
|
||||
/// tri = A list of the three 2d vertices of a triangle.
|
||||
/// eps = Tolerance in the geometrical tests.
|
||||
function _tri_class(tri, eps=EPSILON) =
|
||||
function _tri_class(tri, eps=_EPSILON) =
|
||||
let( crx = cross(tri[1]-tri[2],tri[0]-tri[2]) )
|
||||
abs( crx ) <= eps*norm(tri[1]-tri[2])*norm(tri[0]-tri[2]) ? 0 : sign( crx );
|
||||
|
||||
|
|
@ -118,7 +121,7 @@ function _tri_class(tri, eps=EPSILON) =
|
|||
/// point = The point to check position of.
|
||||
/// tri = A list of the three 2d vertices of a triangle.
|
||||
/// eps = Tolerance in the geometrical tests.
|
||||
function _pt_in_tri(point, tri, eps=EPSILON) =
|
||||
function _pt_in_tri(point, tri, eps=_EPSILON) =
|
||||
min( _tri_class([tri[0],tri[1],point],eps),
|
||||
_tri_class([tri[1],tri[2],point],eps),
|
||||
_tri_class([tri[2],tri[0],point],eps) );
|
||||
|
|
@ -135,7 +138,7 @@ function _pt_in_tri(point, tri, eps=EPSILON) =
|
|||
/// Arguments:
|
||||
/// point = The point to check position of.
|
||||
/// line = Array of two points forming the line segment to test against.
|
||||
function _point_left_of_line2d(point, line, eps=EPSILON) =
|
||||
function _point_left_of_line2d(point, line, eps=_EPSILON) =
|
||||
assert( is_vector(point,2) && is_vector(line*point, 2), "\nImproper input." )
|
||||
// cross(line[0]-point, line[1]-line[0]);
|
||||
_tri_class([point,line[1],line[0]],eps);
|
||||
|
|
@ -153,8 +156,8 @@ function _point_left_of_line2d(point, line, eps=EPSILON) =
|
|||
// a = First point or list of points.
|
||||
// b = Second point or undef; it should be undef if `c` is undef
|
||||
// c = Third point or undef.
|
||||
// eps = Tolerance in geometric comparisons. Default: `EPSILON` (1e-9)
|
||||
function is_collinear(a, b, c, eps=EPSILON) =
|
||||
// eps = Tolerance in geometric comparisons. Default: `_EPSILON` (1e-9)
|
||||
function is_collinear(a, b, c, eps=_EPSILON) =
|
||||
assert( is_path([a,b,c],dim=undef)
|
||||
|| ( is_undef(b) && is_undef(c) && is_path(a,dim=undef) ),
|
||||
"\nInput should be 3 points or a list of points with same dimension.")
|
||||
|
|
@ -205,7 +208,7 @@ function point_line_distance(pt, line, bounded=false) =
|
|||
// Example:
|
||||
// dist = segment_distance([[-14,3], [-15,9]], [[-10,0], [10,0]]); // Returns: 5
|
||||
// dist2 = segment_distance([[-5,5], [5,-5]], [[-10,3], [10,-3]]); // Returns: 0
|
||||
function segment_distance(seg1, seg2,eps=EPSILON) =
|
||||
function segment_distance(seg1, seg2,eps=_EPSILON) =
|
||||
assert( is_matrix(concat(seg1,seg2),4), "\nInputs should be two valid segments." )
|
||||
convex_distance(seg1,seg2,eps);
|
||||
|
||||
|
|
@ -247,7 +250,7 @@ function line_normal(p1,p2) =
|
|||
// the extension of the segment. If lines are parallel or coincident then
|
||||
// it returns undef.
|
||||
|
||||
function _general_line_intersection(s1,s2,eps=EPSILON) =
|
||||
function _general_line_intersection(s1,s2,eps=_EPSILON) =
|
||||
let(
|
||||
denominator = cross(s1[0]-s1[1],s2[0]-s2[1])
|
||||
)
|
||||
|
|
@ -281,7 +284,7 @@ function _general_line_intersection(s1,s2,eps=EPSILON) =
|
|||
// bounded2 = boolean or list of two booleans defining which ends are bounded for line2. Default: [false,false]
|
||||
// ---
|
||||
// bounded = boolean or list of two booleans defining which ends are bounded for both lines. The bounded1 and bounded2 parameters override this if both are given.
|
||||
// eps = tolerance for geometric comparisons. Default: `EPSILON` (1e-9)
|
||||
// eps = tolerance for geometric comparisons. Default: `_EPSILON` (1e-9)
|
||||
// Example(2D): The segments do not intersect but the lines do in this example.
|
||||
// line1 = 10*[[9, 4], [5, 7]];
|
||||
// line2 = 10*[[2, 3], [6, 5]];
|
||||
|
|
@ -302,7 +305,7 @@ function _general_line_intersection(s1,s2,eps=EPSILON) =
|
|||
// stroke(line1);
|
||||
// stroke(line2);
|
||||
// isect = line_intersection(line1, line2, bounded=true); // Returns undef
|
||||
function line_intersection(line1, line2, bounded1, bounded2, bounded, eps=EPSILON) =
|
||||
function line_intersection(line1, line2, bounded1, bounded2, bounded, eps=_EPSILON) =
|
||||
assert( is_finite(eps) && (eps>=0), "\nThe tolerance should be a non-negative value." )
|
||||
assert( _valid_line(line1,dim=2,eps=eps), "\nFirst line invalid.")
|
||||
assert( _valid_line(line2,dim=2,eps=eps), "\nSecond line invalid.")
|
||||
|
|
@ -421,7 +424,7 @@ function line_closest_point(line, pt, bounded=false) =
|
|||
// Arguments:
|
||||
// points = The list of points to find the line through.
|
||||
// check_collinear = If true, don't verify that all points are collinear. Default: false
|
||||
// eps = How much variance is allowed in testing each point against the line. Default: `EPSILON` (1e-9)
|
||||
// eps = How much variance is allowed in testing each point against the line. Default: `_EPSILON` (1e-9)
|
||||
// Example(FlatSpin,VPD=250): A line fitted to a cloud of points.
|
||||
// points = rot(45, v=[-0.5,1,0],
|
||||
// p=random_points(100,3,scale=[5,5,50],seed=47));
|
||||
|
|
@ -434,7 +437,7 @@ function _line_greatest_distance(points,line) = // internal function
|
|||
: let(d = [ for(p=points) point_line_distance(p, line) ])
|
||||
max(d);
|
||||
|
||||
function line_from_points(points, check_collinear=false, eps=EPSILON, fast) =
|
||||
function line_from_points(points, check_collinear=false, eps=_EPSILON, fast) =
|
||||
assert( is_path(points), "\nInvalid point list." )
|
||||
assert( is_finite(eps) && (eps>=0), "\nThe tolerance should be a non-negative value." )
|
||||
len(points) == 2
|
||||
|
|
@ -468,8 +471,8 @@ function line_from_points(points, check_collinear=false, eps=EPSILON, fast) =
|
|||
// Returns true if the given 3D points are non-collinear and are on a plane.
|
||||
// Arguments:
|
||||
// points = The points to test.
|
||||
// eps = Tolerance in geometric comparisons. Default: `EPSILON` (1e-9)
|
||||
function is_coplanar(points, eps=EPSILON) =
|
||||
// eps = Tolerance in geometric comparisons. Default: `_EPSILON` (1e-9)
|
||||
function is_coplanar(points, eps=_EPSILON) =
|
||||
assert( is_path(points,dim=3) , "\nInput should be a list of 3D points." )
|
||||
assert( is_finite(eps) && eps>=0, "\nThe tolerance should be a non-negative value." )
|
||||
len(points)<=2 ? false
|
||||
|
|
@ -560,7 +563,7 @@ function plane_from_normal(normal, pt=[0,0,0]) =
|
|||
// Based on: https://en.wikipedia.org/wiki/Eigenvalue_algorithm
|
||||
function _eigenvals_symm_3(M) =
|
||||
let( p1 = pow(M[0][1],2) + pow(M[0][2],2) + pow(M[1][2],2) )
|
||||
(p1<EPSILON)
|
||||
(p1<_EPSILON)
|
||||
? -sort(-[ M[0][0], M[1][1], M[2][2] ]) // diagonal matrix: eigenvals in decreasing order
|
||||
: let( q = (M[0][0]+M[1][1]+M[2][2])/3,
|
||||
B = (M - q*ident(3)),
|
||||
|
|
@ -584,7 +587,7 @@ function _eigenvec_symm_3(M,evals,i=0) =
|
|||
A = (M - evals[(i+1)%3]*I) * (M - evals[(i+2)%3]*I) ,
|
||||
k = max_index( [for(i=[0:2]) norm(A[i]) ])
|
||||
)
|
||||
norm(A[k])<EPSILON ? I[k] : A[k]/norm(A[k]);
|
||||
norm(A[k])<_EPSILON ? I[k] : A[k]/norm(A[k]);
|
||||
|
||||
|
||||
// finds the eigenvector corresponding to the smallest eigenvalue of the covariance matrix of a pointlist
|
||||
|
|
@ -615,7 +618,7 @@ function _covariance_evec_eval(points, eigenvalue_id) =
|
|||
// Arguments:
|
||||
// points = The list of points to find the best-fit plane.
|
||||
// check_coplanar = If true, verify the point coplanarity within `eps` tolerance. Default: false
|
||||
// eps = Tolerance in geometric comparisons. Default: `EPSILON` (1e-9)
|
||||
// eps = Tolerance in geometric comparisons. Default: `_EPSILON` (1e-9)
|
||||
// Example(FlatSpin,VPD=320,VPT=[-2,5,-2]): 100 non-coplanar random points (yellow spheres) distributed in a volume, showing the best-fit plane (transparent square) with its normal vector.
|
||||
// points = rot(45, v=[-0.3,1,0],
|
||||
// p=random_points(100,3,scale=[50,50,15],seed=47));
|
||||
|
|
@ -626,7 +629,7 @@ function _covariance_evec_eval(points, eigenvalue_id) =
|
|||
// color("#06f") anchor_arrow(50, flag=false);
|
||||
// %linear_extrude(0.1) square(100, center=true);
|
||||
// }
|
||||
function plane_from_points(points, check_coplanar=false, eps=EPSILON, fast) =
|
||||
function plane_from_points(points, check_coplanar=false, eps=_EPSILON, fast) =
|
||||
assert( is_path(points,dim=3), "\nImproper 3d point list." )
|
||||
assert( is_finite(eps) && (eps>=0), "\nThe tolerance should be a non-negative value." )
|
||||
len(points) == 3
|
||||
|
|
@ -660,14 +663,14 @@ function plane_from_points(points, check_coplanar=false, eps=EPSILON, fast) =
|
|||
// Arguments:
|
||||
// poly = The planar 3D polygon to find the plane of.
|
||||
// check_coplanar = If false, doesn't verify that all points in the polygon are coplanar. Default: true
|
||||
// eps = Tolerance in geometric comparisons. Default: `EPSILON` (1e-9)
|
||||
// eps = Tolerance in geometric comparisons. Default: `_EPSILON` (1e-9)
|
||||
// Example(3D):
|
||||
// xyzpath = rot(45, v=[0,1,0], p=path3d(star(n=5,step=2,d=100), 70));
|
||||
// plane = plane_from_polygon(xyzpath);
|
||||
// #stroke(xyzpath,closed=true,width=3);
|
||||
// cp = centroid(xyzpath);
|
||||
// move(cp) rot(from=UP,to=plane_normal(plane)) anchor_arrow(45);
|
||||
function plane_from_polygon(poly, check_coplanar=true, eps=EPSILON, fast) =
|
||||
function plane_from_polygon(poly, check_coplanar=true, eps=_EPSILON, fast) =
|
||||
assert( is_path(poly,dim=3), "\nInvalid polygon." )
|
||||
assert( is_finite(eps) && (eps>=0), "\nThe tolerance should be a non-negative value." )
|
||||
let(
|
||||
|
|
@ -718,7 +721,7 @@ function plane_offset(plane) =
|
|||
// Returns [POINT, U] if line intersects plane at one point, where U is zero at line[0] and 1 at line[1]
|
||||
// Returns [LINE, undef] if the line is on the plane.
|
||||
// Returns undef if line is parallel to, but not on the given plane.
|
||||
function _general_plane_line_intersection(plane, line, eps=EPSILON) =
|
||||
function _general_plane_line_intersection(plane, line, eps=_EPSILON) =
|
||||
let(
|
||||
a = plane*[each line[0],-1], // evaluation of the plane expression at line[0]
|
||||
b = plane*[each(line[1]-line[0]),0] // difference between the plane expression evaluation at line[1] and at line[0]
|
||||
|
|
@ -756,8 +759,8 @@ function _normalize_plane(plane) =
|
|||
// plane = The [A,B,C,D] values for the equation of the plane.
|
||||
// line = A list of two distinct 3D points that are on the line.
|
||||
// bounded = If false, the line is considered unbounded. If true, it is treated as a bounded line segment. If given as `[true, false]` or `[false, true]`, the boundedness of the points are specified individually, allowing the line to be treated as a half-bounded ray. Default: false (unbounded)
|
||||
// eps = Tolerance in geometric comparisons. Default: `EPSILON` (1e-9)
|
||||
function plane_line_intersection(plane, line, bounded=false, eps=EPSILON) =
|
||||
// eps = Tolerance in geometric comparisons. Default: `_EPSILON` (1e-9)
|
||||
function plane_line_intersection(plane, line, bounded=false, eps=_EPSILON) =
|
||||
assert( is_finite(eps) && eps>=0, "\nThe tolerance should be a positive number." )
|
||||
assert(_valid_plane(plane,eps=eps) && _valid_line(line,dim=3,eps=eps), "\nInvalid plane and/or 3d line.")
|
||||
assert(is_bool(bounded) || is_bool_list(bounded,2), "\nInvalid bound condition.")
|
||||
|
|
@ -912,8 +915,8 @@ function _pointlist_greatest_distance(points,plane) =
|
|||
// Arguments:
|
||||
// plane = The plane to test the points on.
|
||||
// points = The list of 3D points to test.
|
||||
// eps = Tolerance in geometric comparisons. Default: `EPSILON` (1e-9)
|
||||
function are_points_on_plane(points, plane, eps=EPSILON) =
|
||||
// eps = Tolerance in geometric comparisons. Default: `_EPSILON` (1e-9)
|
||||
function are_points_on_plane(points, plane, eps=_EPSILON) =
|
||||
assert( _valid_plane(plane), "\nInvalid plane." )
|
||||
assert( is_matrix(points,undef,3) && len(points)>0, "\nInvalid pointlist." ) // using is_matrix it accepts len(points)==1
|
||||
assert( is_finite(eps) && eps>=0, "\nThe tolerance should be a positive number." )
|
||||
|
|
@ -933,7 +936,7 @@ function are_points_on_plane(points, plane, eps=EPSILON) =
|
|||
/// plane = The [A,B,C,D] coefficients for the first plane equation `Ax+By+Cz=D`.
|
||||
/// point = The 3D point to test.
|
||||
function _is_point_above_plane(plane, point) =
|
||||
point_plane_distance(plane, point) > EPSILON;
|
||||
point_plane_distance(plane, point) > _EPSILON;
|
||||
|
||||
|
||||
// Module: show_plane()
|
||||
|
|
@ -1028,14 +1031,14 @@ module show_plane(plane, size, offset=0)
|
|||
// color("black") stroke(line, endcaps="arrow2", width=0.5);
|
||||
// isects = circle_line_intersection(r=r, cp=cp, line=line);
|
||||
// color("#f44") move_copies(isects) circle(d=1);
|
||||
function circle_line_intersection(r, cp, line, bounded=false, d, eps=EPSILON) =
|
||||
function circle_line_intersection(r, cp, line, bounded=false, d, eps=_EPSILON) =
|
||||
assert(_valid_line(line,2), "\nInvalid 2d line.")
|
||||
assert(is_vector(cp,2), "\nCircle center must be a 2-vector")
|
||||
_circle_or_sphere_line_intersection(r, cp, line, bounded, d, eps);
|
||||
|
||||
|
||||
|
||||
function _circle_or_sphere_line_intersection(r, cp, line, bounded=false, d, eps=EPSILON) =
|
||||
function _circle_or_sphere_line_intersection(r, cp, line, bounded=false, d, eps=_EPSILON) =
|
||||
let(r=get_radius(r=r,d=d,dflt=undef))
|
||||
assert(is_num(r) && r>0, "\nRadius must be positive")
|
||||
assert(is_bool(bounded) || is_bool_list(bounded,2), "\nInvalid bound condition")
|
||||
|
|
@ -1071,7 +1074,7 @@ function _circle_or_sphere_line_intersection(r, cp, line, bounded=false, d, eps=
|
|||
// cp1 = Centerpoint of the first circle.
|
||||
// r2 = Radius of the second circle.
|
||||
// cp2 = Centerpoint of the second circle.
|
||||
// eps = Tolerance for detecting tangent circles. Default: EPSILON
|
||||
// eps = Tolerance for detecting tangent circles. Default: _EPSILON
|
||||
// ---
|
||||
// d1 = Diameter of the first circle.
|
||||
// d2 = Diameter of the second circle.
|
||||
|
|
@ -1107,7 +1110,7 @@ function _circle_or_sphere_line_intersection(r, cp, line, bounded=false, d, eps=
|
|||
// move(cp1) stroke(circle(r=r1), width=0.2, closed=true);
|
||||
// move(cp2) stroke(circle(r=r2), width=0.2, closed=true);
|
||||
// color("red") move_copies(pts) circle(r=.3);
|
||||
function circle_circle_intersection(r1, cp1, r2, cp2, eps=EPSILON, d1, d2) =
|
||||
function circle_circle_intersection(r1, cp1, r2, cp2, eps=_EPSILON, d1, d2) =
|
||||
assert( is_path([cp1,cp2],dim=2), "\nInvalid center point(s)." )
|
||||
let(
|
||||
r1 = get_radius(r1=r1,d1=d1),
|
||||
|
|
@ -1418,8 +1421,8 @@ function circle_circle_tangents(r1, cp1, r2, cp2, d1, d2) =
|
|||
/// Arguments:
|
||||
/// points = List of input points.
|
||||
/// error = Defines the behaviour for collinear input points. When `true`, produces an error, otherwise returns []. Default: `true`.
|
||||
/// eps = Tolerance for collinearity test. Default: EPSILON.
|
||||
function _noncollinear_triple(points,error=true,eps=EPSILON) =
|
||||
/// eps = Tolerance for collinearity test. Default: _EPSILON.
|
||||
function _noncollinear_triple(points,error=true,eps=_EPSILON) =
|
||||
assert( is_path(points), "\nInvalid input points." )
|
||||
assert( is_finite(eps) && (eps>=0), "The tolerance should be a non-negative value." )
|
||||
len(points)<3 ? [] :
|
||||
|
|
@ -1468,7 +1471,7 @@ function _noncollinear_triple(points,error=true,eps=EPSILON) =
|
|||
// color("cyan") stroke(line);
|
||||
// move(cp) sphere(r=r, $fn=72);
|
||||
// color("red") move_copies(isects) sphere(d=3, $fn=12);
|
||||
function sphere_line_intersection(r, cp, line, bounded=false, d, eps=EPSILON) =
|
||||
function sphere_line_intersection(r, cp, line, bounded=false, d, eps=_EPSILON) =
|
||||
assert(_valid_line(line,3), "\nInvalid 3d line.")
|
||||
assert(is_vector(cp,3), "\nSphere center must be a 3-vector")
|
||||
_circle_or_sphere_line_intersection(r, cp, line, bounded, d, eps);
|
||||
|
|
@ -1543,7 +1546,7 @@ function polygon_area(poly, signed=false) =
|
|||
// linear_extrude(height=0.01) polygon(path);
|
||||
// cp = centroid(path);
|
||||
// color("red") move(cp) sphere(d=2);
|
||||
function centroid(object,eps=EPSILON) =
|
||||
function centroid(object,eps=_EPSILON) =
|
||||
assert(is_finite(eps) && (eps>=0), "\nThe tolerance should a non-negative value." )
|
||||
is_vnf(object) ? _vnf_centroid(object,eps)
|
||||
: is_path(object,[2,3]) ? _polygon_centroid(object,eps)
|
||||
|
|
@ -1553,7 +1556,7 @@ function centroid(object,eps=EPSILON) =
|
|||
|
||||
/// Internal Function: _region_centroid()
|
||||
/// Compute centroid of region
|
||||
function _region_centroid(region,eps=EPSILON) =
|
||||
function _region_centroid(region,eps=_EPSILON) =
|
||||
let(
|
||||
region=force_region(region),
|
||||
parts = region_parts(region),
|
||||
|
|
@ -1578,8 +1581,8 @@ function _region_centroid(region,eps=EPSILON) =
|
|||
/// polygons or an error is produced.
|
||||
/// Arguments:
|
||||
/// poly = Points of the polygon from which the centroid is calculated.
|
||||
/// eps = Tolerance in geometric comparisons. Default: `EPSILON` (1e-9)
|
||||
function _polygon_centroid(poly, eps=EPSILON) =
|
||||
/// eps = Tolerance in geometric comparisons. Default: `_EPSILON` (1e-9)
|
||||
function _polygon_centroid(poly, eps=_EPSILON) =
|
||||
assert( is_path(poly,dim=[2,3]), "\nThe input must be a 2D or 3D polygon." )
|
||||
let(
|
||||
n = len(poly[0])==2 ? 1 :
|
||||
|
|
@ -1680,7 +1683,7 @@ function polygon_normal(poly) =
|
|||
// point = The 2D point to check
|
||||
// poly = The list of 2D points forming the perimeter of the polygon.
|
||||
// nonzero = The rule to use: true for "Nonzero" rule and false for "Even-Odd". Default: false (Even-Odd)
|
||||
// eps = Tolerance in geometric comparisons. Default: `EPSILON` (1e-9)
|
||||
// eps = Tolerance in geometric comparisons. Default: `_EPSILON` (1e-9)
|
||||
// Example(2D): With nonzero set to false (the default), we get this result. Green dots are inside the polygon and red are outside:
|
||||
// a=20*2/3;
|
||||
// b=30*2/3;
|
||||
|
|
@ -1723,7 +1726,7 @@ function _point_above_below_segment(point, edge) =
|
|||
: (edge[1].y <= 0 && cross(edge[0], edge[1]-edge[0]) < 0) ? -1 : 0;
|
||||
|
||||
|
||||
function point_in_polygon(point, poly, nonzero=false, eps=EPSILON) =
|
||||
function point_in_polygon(point, poly, nonzero=false, eps=_EPSILON) =
|
||||
// Original algorithms from http://geomalgorithms.com/a03-_inclusion.html
|
||||
assert( is_vector(point,2) && is_path(poly,dim=2) && len(poly)>2,
|
||||
"\nThe point and polygon should be in 2D. The polygon should have more that 2 points." )
|
||||
|
|
@ -1802,7 +1805,7 @@ function point_in_polygon(point, poly, nonzero=false, eps=EPSILON) =
|
|||
// line = A list of two distinct 3D points on the line.
|
||||
// bounded = If false, the line is considered unbounded. If true, it is treated as a bounded line segment. If given as `[true, false]` or `[false, true]`, the boundedness of the points are specified individually, allowing the line to be treated as a half-bounded ray. Default: false (unbounded)
|
||||
// nonzero = set to true to use the nonzero rule for determining it points are in a polygon. See point_in_polygon. Default: false.
|
||||
// eps = Tolerance in geometric comparisons. Default: `EPSILON` (1e-9)
|
||||
// eps = Tolerance in geometric comparisons. Default: `_EPSILON` (1e-9)
|
||||
// Example(3D): The line intersects the 3d hexagon in a single point.
|
||||
// hex = zrot(140,p=rot([-45,40,20],p=path3d(hexagon(r=15))));
|
||||
// line = [[5,0,-13],[-3,-5,13]];
|
||||
|
|
@ -1906,7 +1909,7 @@ function point_in_polygon(point, poly, nonzero=false, eps=EPSILON) =
|
|||
// move(part[0]) circle(r=1,$fn=12);
|
||||
// else
|
||||
// stroke(part);
|
||||
function polygon_line_intersection(poly, line, bounded=false, nonzero=false, eps=EPSILON) =
|
||||
function polygon_line_intersection(poly, line, bounded=false, nonzero=false, eps=_EPSILON) =
|
||||
assert( is_finite(eps) && eps>=0, "\nThe tolerance should be a positive number." )
|
||||
assert(is_path(poly,dim=[2,3]), "\nInvalid polygon." )
|
||||
assert(is_bool(bounded) || is_bool_list(bounded,2), "\nInvalid bound condition.")
|
||||
|
|
@ -2000,7 +2003,7 @@ function _merge_segments(insegs,outsegs, eps, i=1) =
|
|||
// poly = Array of the polygon vertices.
|
||||
// ind = If given, a list of indices indexing the vertices of the polygon in `poly`. Default: use all the points of poly
|
||||
// error = If false, returns `undef` when the polygon cannot be triangulated; otherwise, issues an assert error. Default: true.
|
||||
// eps = A maximum tolerance in geometrical tests. Default: EPSILON
|
||||
// eps = A maximum tolerance in geometrical tests. Default: _EPSILON
|
||||
// Example(2D,NoAxes): a simple polygon; see from above
|
||||
// poly = star(id=10, od=15,n=11);
|
||||
// tris = polygon_triangulate(poly);
|
||||
|
|
@ -2037,7 +2040,7 @@ function _merge_segments(insegs,outsegs, eps, i=1) =
|
|||
// vnf_tri = [vnf[0], [for(face=vnf[1]) each polygon_triangulate(vnf[0], face) ] ];
|
||||
// color("blue")
|
||||
// vnf_wireframe(vnf_tri, width=.15);
|
||||
function polygon_triangulate(poly, ind, error=true, eps=EPSILON) =
|
||||
function polygon_triangulate(poly, ind, error=true, eps=_EPSILON) =
|
||||
assert(is_path(poly) && len(poly)>=3, "\nPolygon `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)<len(poly) ),
|
||||
"Improper or out of bounds list of indices")
|
||||
|
|
@ -2086,7 +2089,7 @@ function polygon_triangulate(poly, ind, error=true, eps=EPSILON) =
|
|||
// implements a modified version of ear cut method for non-twisted polygons
|
||||
// the polygons accepted by this function are those decomposable in simple
|
||||
// CW polygons.
|
||||
function _triangulate(poly, ind, error, eps=EPSILON, tris=[]) =
|
||||
function _triangulate(poly, ind, error, eps=_EPSILON, tris=[]) =
|
||||
len(ind)==3
|
||||
? _degenerate_tri(select(poly,ind),eps)
|
||||
? tris // if last 3 pts perform a degenerate triangle, ignore it
|
||||
|
|
@ -2349,7 +2352,7 @@ function align_polygon(reference, poly, angles, cp, trans, return_ind=false) =
|
|||
],
|
||||
scores = column(alignments,1),
|
||||
minscore = min(scores),
|
||||
minind = [for(i=idx(scores)) if (scores[i]<minscore+EPSILON) i],
|
||||
minind = [for(i=idx(scores)) if (scores[i]<minscore+_EPSILON) i],
|
||||
dummy = is_def(angles) ? echo(best_angles = select(list(angles), minind)):0,
|
||||
best = minind[0]
|
||||
)
|
||||
|
|
@ -2375,7 +2378,7 @@ function align_polygon(reference, poly, angles, cp, trans, return_ind=false) =
|
|||
// rot(360/5, p=pentagon(r=4))); // returns true
|
||||
// are_polygons_equal(pentagon(r=4),
|
||||
// rot(90, p=pentagon(r=4))); // returns false
|
||||
function are_polygons_equal(poly1, poly2, eps=EPSILON) =
|
||||
function are_polygons_equal(poly1, poly2, eps=_EPSILON) =
|
||||
let(
|
||||
poly1 = list_unwrap(poly1),
|
||||
poly2 = list_unwrap(poly2),
|
||||
|
|
@ -2497,8 +2500,8 @@ function _backtracking(i,points,h,t,m,all) =
|
|||
|
||||
// clockwise check (2d)
|
||||
function _is_cw(a,b,c,all) =
|
||||
all ? cross(a-c,b-c)<=EPSILON*norm(a-c)*norm(b-c) :
|
||||
cross(a-c,b-c)<-EPSILON*norm(a-c)*norm(b-c);
|
||||
all ? cross(a-c,b-c)<=_EPSILON*norm(a-c)*norm(b-c) :
|
||||
cross(a-c,b-c)<-_EPSILON*norm(a-c)*norm(b-c);
|
||||
|
||||
|
||||
// Function: hull2d_path()
|
||||
|
|
@ -2624,7 +2627,7 @@ function hull3d_faces(points) =
|
|||
|
||||
|
||||
// Adds the remaining points one by one to the convex hull
|
||||
function _hull3d_iterative(points, triangles, planes, remaining, _i=0) = //let( EPSILON=1e-12 )
|
||||
function _hull3d_iterative(points, triangles, planes, remaining, _i=0) = //let( _EPSILON=1e-12 )
|
||||
_i >= len(remaining) ? triangles :
|
||||
let (
|
||||
// pick a point
|
||||
|
|
@ -2632,7 +2635,7 @@ function _hull3d_iterative(points, triangles, planes, remaining, _i=0) = //let(
|
|||
// evaluate the triangle plane equations at point i
|
||||
planeq_val = planes*[each points[i], -1],
|
||||
// find the triangles that are in conflict with the point (point not inside)
|
||||
conflicts = [for (i = [0:1:len(planeq_val)-1]) if (planeq_val[i]>EPSILON) i ],
|
||||
conflicts = [for (i = [0:1:len(planeq_val)-1]) if (planeq_val[i]>_EPSILON) i ],
|
||||
// collect the halfedges of all triangles that are in conflict
|
||||
halfedges = [
|
||||
for(c = conflicts, i = [0:2])
|
||||
|
|
@ -2645,12 +2648,12 @@ function _hull3d_iterative(points, triangles, planes, remaining, _i=0) = //let(
|
|||
// add tria2add and remove conflict triangles
|
||||
new_triangles =
|
||||
concat( tri2add,
|
||||
[ for (i = [0:1:len(planes)-1]) if (planeq_val[i]<=EPSILON) triangles[i] ]
|
||||
[ for (i = [0:1:len(planes)-1]) if (planeq_val[i]<=_EPSILON) triangles[i] ]
|
||||
),
|
||||
// add the plane equations of new added triangles and remove the plane equations of the conflict ones
|
||||
new_planes =
|
||||
[ for (t = tri2add) plane3pt_indexed(points, t[0], t[1], t[2]) ,
|
||||
for (i = [0:1:len(planes)-1]) if (planeq_val[i]<=EPSILON) planes[i] ]
|
||||
for (i = [0:1:len(planes)-1]) if (planeq_val[i]<=_EPSILON) planes[i] ]
|
||||
) _hull3d_iterative(
|
||||
points,
|
||||
new_triangles,
|
||||
|
|
@ -2692,13 +2695,13 @@ function _find_first_noncoplanar(plane, points, i=0) =
|
|||
// If the points are collinear or not coplanar an error may be generated.
|
||||
// Arguments:
|
||||
// poly = Polygon to check.
|
||||
// eps = Tolerance for the collinearity and coplanarity tests. Default: EPSILON.
|
||||
// eps = Tolerance for the collinearity and coplanarity tests. Default: _EPSILON.
|
||||
// Example:
|
||||
// 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
|
||||
// spiral = [for (i=[0:36]) let(a=-i*10) (10+i)*[cos(a),sin(a)]];
|
||||
// test = is_polygon_convex(spiral); // Returns: false
|
||||
function is_polygon_convex(poly,eps=EPSILON) =
|
||||
function is_polygon_convex(poly,eps=_EPSILON) =
|
||||
assert(is_path(poly), "\nThe input should be a 2D or 3D polygon." )
|
||||
let(
|
||||
lp = len(poly),
|
||||
|
|
@ -2741,7 +2744,7 @@ function is_polygon_convex(poly,eps=EPSILON) =
|
|||
// Arguments:
|
||||
// points1 = first list of 2d or 3d points.
|
||||
// points2 = second list of 2d or 3d points.
|
||||
// eps = tolerance in distance evaluations. Default: EPSILON.
|
||||
// eps = tolerance in distance evaluations. Default: _EPSILON.
|
||||
// Example(2D):
|
||||
// pts1 = move([-3,0], p=square(3,center=true));
|
||||
// pts2 = rot(a=45, p=square(2,center=true));
|
||||
|
|
@ -2759,7 +2762,7 @@ function is_polygon_convex(poly,eps=EPSILON) =
|
|||
// vnf_polyhedron(sphr2);
|
||||
// echo(convex_distance(sphr1[0], sphr2[0])); // Returns: 0
|
||||
// echo(convex_distance(sphr1[0], sphr3[0])); // Returns: 0.5
|
||||
function convex_distance(points1, points2, eps=EPSILON) =
|
||||
function convex_distance(points1, points2, eps=_EPSILON) =
|
||||
assert(is_matrix(points1) && is_matrix(points2,undef,len(points1[0])),
|
||||
"\nThe input lists should be compatible consistent non empty lists of points.")
|
||||
assert(len(points1[0])==2 || len(points1[0])==3 ,
|
||||
|
|
@ -2773,7 +2776,7 @@ function convex_distance(points1, points2, eps=EPSILON) =
|
|||
// Finds the vector difference between the hulls of the two pointsets by the GJK algorithm
|
||||
// Based on:
|
||||
// http://www.dtecta.com/papers/jgt98convex.pdf
|
||||
function _GJK_distance(points1, points2, eps=EPSILON, lbd, d, simplex=[]) =
|
||||
function _GJK_distance(points1, points2, eps=_EPSILON, lbd, d, simplex=[]) =
|
||||
let( nrd = norm(d) ) // distance upper bound
|
||||
nrd<eps ? d :
|
||||
let(
|
||||
|
|
@ -2801,7 +2804,7 @@ function _GJK_distance(points1, points2, eps=EPSILON, lbd, d, simplex=[]) =
|
|||
// Arguments:
|
||||
// points1 = first list of 2d or 3d points.
|
||||
// points2 = second list of 2d or 3d points.
|
||||
// eps - tolerance for the intersection tests. Default: EPSILON.
|
||||
// eps - tolerance for the intersection tests. Default: _EPSILON.
|
||||
// Example(2D):
|
||||
// pts1 = move([-3,0], p=square(3,center=true));
|
||||
// pts2 = rot(a=45, p=square(2,center=true));
|
||||
|
|
@ -2820,7 +2823,7 @@ function _GJK_distance(points1, points2, eps=EPSILON, lbd, d, simplex=[]) =
|
|||
// echo(convex_collision(sphr1[0], sphr2[0])); // Returns: true
|
||||
// echo(convex_collision(sphr1[0], sphr3[0])); // Returns: false
|
||||
//
|
||||
function convex_collision(points1, points2, eps=EPSILON) =
|
||||
function convex_collision(points1, points2, eps=_EPSILON) =
|
||||
assert(is_matrix(points1) && is_matrix(points2,undef,len(points1[0])),
|
||||
"\nThe input lists should be compatible consistent non empty lists of points.")
|
||||
assert(len(points1[0])==2 || len(points1[0])==3 ,
|
||||
|
|
@ -2835,7 +2838,7 @@ function convex_collision(points1, points2, eps=EPSILON) =
|
|||
// http://uu.diva-portal.org/smash/get/diva2/FFULLTEXT01.pdf
|
||||
// or
|
||||
// http://www.dtecta.com/papers/jgt98convex.pdf
|
||||
function _GJK_collide(points1, points2, d, simplex, eps=EPSILON) =
|
||||
function _GJK_collide(points1, points2, d, simplex, eps=_EPSILON) =
|
||||
norm(d) < eps ? true : // does collide
|
||||
let( v = _support_diff(points1,points2,-d) )
|
||||
v*d > eps*eps ? false : // no collision
|
||||
|
|
@ -2847,7 +2850,7 @@ function _GJK_collide(points1, points2, d, simplex, eps=EPSILON) =
|
|||
// given a simplex s, returns a pair:
|
||||
// - the point of the s closest to the origin
|
||||
// - the smallest sub-simplex of s that contains that point
|
||||
function _closest_simplex(s,eps=EPSILON) =
|
||||
function _closest_simplex(s,eps=_EPSILON) =
|
||||
len(s)==2 ? _closest_s1(s,eps) :
|
||||
len(s)==3 ? _closest_s2(s,eps) :
|
||||
len(s)==4 ? _closest_s3(s,eps) :
|
||||
|
|
@ -2855,7 +2858,7 @@ function _closest_simplex(s,eps=EPSILON) =
|
|||
|
||||
|
||||
// find the point of a 1-simplex closest to the origin
|
||||
function _closest_s1(s,eps=EPSILON) =
|
||||
function _closest_s1(s,eps=_EPSILON) =
|
||||
norm(s[1]-s[0])<=eps*(norm(s[0])+norm(s[1]))/2 ? [ s[0], [s[0]] ] :
|
||||
let(
|
||||
c = s[1]-s[0],
|
||||
|
|
@ -2867,7 +2870,7 @@ function _closest_s1(s,eps=EPSILON) =
|
|||
|
||||
|
||||
// find the point of a 2-simplex closest to the origin
|
||||
function _closest_s2(s, eps=EPSILON) =
|
||||
function _closest_s2(s, eps=_EPSILON) =
|
||||
// considering that s[2] was the last inserted vertex in s by GJK,
|
||||
// the plane orthogonal to the triangle [ origin, s[0], s[1] ] that
|
||||
// contains [s[0],s[1]] have the origin and s[2] on the same side;
|
||||
|
|
@ -2898,7 +2901,7 @@ function _closest_s2(s, eps=EPSILON) =
|
|||
|
||||
|
||||
// find the point of a 3-simplex closest to the origin
|
||||
function _closest_s3(s,eps=EPSILON) =
|
||||
function _closest_s3(s,eps=_EPSILON) =
|
||||
let( nr = cross(s[1]-s[0],s[2]-s[0]),
|
||||
sz = [ norm(s[0]-s[1]), norm(s[1]-s[2]), norm(s[2]-s[0]) ] )
|
||||
norm(nr)<=eps*pow(max(sz),2)
|
||||
|
|
|
|||
Loading…
Reference in a new issue