mirror of
https://github.com/BelfrySCAD/BOSL2.git
synced 2025-01-04 03:09:45 +00:00
commit
64b4176202
5 changed files with 139 additions and 92 deletions
|
@ -577,7 +577,7 @@ function bezier_line_intersection(curve, line) =
|
||||||
// p2 = [30, 30];
|
// p2 = [30, 30];
|
||||||
// trace_path([p0,p1,p2], showpts=true, size=0.5, color="green");
|
// trace_path([p0,p1,p2], showpts=true, size=0.5, color="green");
|
||||||
// fbez = fillet3pts(p0,p1,p2, 10);
|
// fbez = fillet3pts(p0,p1,p2, 10);
|
||||||
// trace_bezier(select(fbez,1,-2), size=1);
|
// trace_bezier(slice(fbez, 1, -2), size=1);
|
||||||
function fillet3pts(p0, p1, p2, r, d, maxerr=0.1, w=0.5, dw=0.25) = let(
|
function fillet3pts(p0, p1, p2, r, d, maxerr=0.1, w=0.5, dw=0.25) = let(
|
||||||
r = get_radius(r=r,d=d),
|
r = get_radius(r=r,d=d),
|
||||||
v0 = unit(p0-p1),
|
v0 = unit(p0-p1),
|
||||||
|
|
|
@ -38,7 +38,6 @@ function typeof(x) =
|
||||||
"invalid";
|
"invalid";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Function: is_type()
|
// Function: is_type()
|
||||||
// Usage:
|
// Usage:
|
||||||
// bool = is_type(x, types);
|
// bool = is_type(x, types);
|
||||||
|
@ -274,7 +273,8 @@ function is_bool_list(list, length) =
|
||||||
// Topics: Undef Handling
|
// Topics: Undef Handling
|
||||||
// See Also: first_defined(), one_defined(), num_defined()
|
// See Also: first_defined(), one_defined(), num_defined()
|
||||||
// Description:
|
// Description:
|
||||||
// Returns the value given as `v` if it is not `undef`. Otherwise, returns the value of `dflt`.
|
// Returns the value given as `v` if it is not `undef`.
|
||||||
|
// Otherwise, returns the value of `dflt`.
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// v = Value to pass through if not `undef`.
|
// v = Value to pass through if not `undef`.
|
||||||
// dflt = Value to return if `v` *is* `undef`.
|
// dflt = Value to return if `v` *is* `undef`.
|
||||||
|
@ -292,6 +292,8 @@ function default(v,dflt=undef) = is_undef(v)? dflt : v;
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// v = The list whose items are being checked.
|
// v = The list whose items are being checked.
|
||||||
// recursive = If true, sublists are checked recursively for defined values. The first sublist that has a defined item is returned.
|
// recursive = If true, sublists are checked recursively for defined values. The first sublist that has a defined item is returned.
|
||||||
|
// Examples:
|
||||||
|
// val = first_defined([undef,7,undef,true]); // Returns: 1
|
||||||
function first_defined(v,recursive=false,_i=0) =
|
function first_defined(v,recursive=false,_i=0) =
|
||||||
_i<len(v) && (
|
_i<len(v) && (
|
||||||
is_undef(v[_i]) || (
|
is_undef(v[_i]) || (
|
||||||
|
@ -604,7 +606,6 @@ function segs(r) =
|
||||||
|
|
||||||
// Module: no_children()
|
// Module: no_children()
|
||||||
// Topics: Error Checking
|
// Topics: Error Checking
|
||||||
// See Also: no_function(), no_module()
|
|
||||||
// Usage:
|
// Usage:
|
||||||
// no_children($children);
|
// no_children($children);
|
||||||
// Description:
|
// Description:
|
||||||
|
@ -612,6 +613,7 @@ function segs(r) =
|
||||||
// as indicated by its argument.
|
// as indicated by its argument.
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// $children = number of children the module has.
|
// $children = number of children the module has.
|
||||||
|
// See Also: no_function(), no_module()
|
||||||
// Example:
|
// Example:
|
||||||
// module foo() {
|
// module foo() {
|
||||||
// no_children($children);
|
// no_children($children);
|
||||||
|
@ -651,6 +653,7 @@ module no_module() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Section: Testing Helpers
|
// Section: Testing Helpers
|
||||||
|
|
||||||
|
|
||||||
|
|
198
geometry.scad
198
geometry.scad
|
@ -17,10 +17,10 @@
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// point = The point to test.
|
// point = The point to test.
|
||||||
// edge = Array of two points forming the line segment to test against.
|
// edge = Array of two points forming the line segment to test against.
|
||||||
// eps = Acceptable variance. Default: `EPSILON` (1e-9)
|
// eps = Tolerance in geometric comparisons. Default: `EPSILON` (1e-9)
|
||||||
function point_on_segment2d(point, edge, eps=EPSILON) =
|
function point_on_segment2d(point, edge, eps=EPSILON) =
|
||||||
assert( is_vector(point,2), "Invalid point." )
|
assert( is_vector(point,2), "Invalid point." )
|
||||||
assert( is_finite(eps) && eps>=0, "The tolerance should be a positive number." )
|
assert( is_finite(eps) && (eps>=0), "The tolerance should be a non-negative value." )
|
||||||
assert( _valid_line(edge,2,eps=eps), "Invalid segment." )
|
assert( _valid_line(edge,2,eps=eps), "Invalid segment." )
|
||||||
let( dp = point-edge[0],
|
let( dp = point-edge[0],
|
||||||
de = edge[1]-edge[0],
|
de = edge[1]-edge[0],
|
||||||
|
@ -74,12 +74,12 @@ function point_left_of_line2d(point, line) =
|
||||||
// a = First point or list of points.
|
// a = First point or list of points.
|
||||||
// b = Second point or undef; it should be undef if `c` is undef
|
// b = Second point or undef; it should be undef if `c` is undef
|
||||||
// c = Third point or undef.
|
// c = Third point or undef.
|
||||||
// eps = Acceptable variance. Default: `EPSILON` (1e-9)
|
// eps = Tolerance in geometric comparisons. Default: `EPSILON` (1e-9)
|
||||||
function collinear(a, b, c, eps=EPSILON) =
|
function collinear(a, b, c, eps=EPSILON) =
|
||||||
assert( is_path([a,b,c],dim=undef)
|
assert( is_path([a,b,c],dim=undef)
|
||||||
|| ( is_undef(b) && is_undef(c) && is_path(a,dim=undef) ),
|
|| ( is_undef(b) && is_undef(c) && is_path(a,dim=undef) ),
|
||||||
"Input should be 3 points or a list of points with same dimension.")
|
"Input should be 3 points or a list of points with same dimension.")
|
||||||
assert( is_finite(eps) && eps>=0, "The tolerance should be a positive number." )
|
assert( is_finite(eps) && (eps>=0), "The tolerance should be a non-negative value." )
|
||||||
let( points = is_def(c) ? [a,b,c]: a )
|
let( points = is_def(c) ? [a,b,c]: a )
|
||||||
len(points)<3 ? true
|
len(points)<3 ? true
|
||||||
: noncollinear_triple(points,error=false,eps=eps)==[];
|
: noncollinear_triple(points,error=false,eps=eps)==[];
|
||||||
|
@ -151,10 +151,11 @@ function _general_line_intersection(s1,s2,eps=EPSILON) =
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// l1 = First 2D line, given as a list of two 2D points on the line.
|
// l1 = First 2D line, given as a list of two 2D points on the line.
|
||||||
// l2 = Second 2D line, given as a list of two 2D points on the line.
|
// l2 = Second 2D line, given as a list of two 2D points on the line.
|
||||||
// eps = Acceptable variance. Default: `EPSILON` (1e-9)
|
// eps = Tolerance in geometric comparisons. Default: `EPSILON` (1e-9)
|
||||||
function line_intersection(l1,l2,eps=EPSILON) =
|
function line_intersection(l1,l2,eps=EPSILON) =
|
||||||
assert( is_finite(eps) && eps>=0, "The tolerance should be a positive number." )
|
assert( is_finite(eps) && eps>=0, "The tolerance should be a positive number." )
|
||||||
assert( _valid_line(l1,dim=2,eps=eps) &&_valid_line(l2,dim=2,eps=eps), "Invalid line(s)." )
|
assert( _valid_line(l1,dim=2,eps=eps) &&_valid_line(l2,dim=2,eps=eps), "Invalid line(s)." )
|
||||||
|
assert( is_finite(eps) && (eps>=0), "The tolerance should be a non-negative value." )
|
||||||
let(isect = _general_line_intersection(l1,l2,eps=eps))
|
let(isect = _general_line_intersection(l1,l2,eps=eps))
|
||||||
isect[0];
|
isect[0];
|
||||||
|
|
||||||
|
@ -168,9 +169,9 @@ function line_intersection(l1,l2,eps=EPSILON) =
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// line = The unbounded 2D line, defined by two 2D points on the line.
|
// line = The unbounded 2D line, defined by two 2D points on the line.
|
||||||
// ray = The 2D ray, given as a list `[START,POINT]` of the 2D start-point START, and a 2D point POINT on the ray.
|
// ray = The 2D ray, given as a list `[START,POINT]` of the 2D start-point START, and a 2D point POINT on the ray.
|
||||||
// eps = Acceptable variance. Default: `EPSILON` (1e-9)
|
// eps = Tolerance in geometric comparisons. Default: `EPSILON` (1e-9)
|
||||||
function line_ray_intersection(line,ray,eps=EPSILON) =
|
function line_ray_intersection(line,ray,eps=EPSILON) =
|
||||||
assert( is_finite(eps) && eps>=0, "The tolerance should be a positive number." )
|
assert( is_finite(eps) && (eps>=0), "The tolerance should be a non-negative value." )
|
||||||
assert( _valid_line(line,dim=2,eps=eps) && _valid_line(ray,dim=2,eps=eps), "Invalid line or ray." )
|
assert( _valid_line(line,dim=2,eps=eps) && _valid_line(ray,dim=2,eps=eps), "Invalid line or ray." )
|
||||||
let(
|
let(
|
||||||
isect = _general_line_intersection(line,ray,eps=eps)
|
isect = _general_line_intersection(line,ray,eps=eps)
|
||||||
|
@ -188,9 +189,9 @@ function line_ray_intersection(line,ray,eps=EPSILON) =
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// line = The unbounded 2D line, defined by two 2D points on the line.
|
// line = The unbounded 2D line, defined by two 2D points on the line.
|
||||||
// segment = The bounded 2D line segment, given as a list of the two 2D endpoints of the segment.
|
// segment = The bounded 2D line segment, given as a list of the two 2D endpoints of the segment.
|
||||||
// eps = Acceptable variance. Default: `EPSILON` (1e-9)
|
// eps = Tolerance in geometric comparisons. Default: `EPSILON` (1e-9)
|
||||||
function line_segment_intersection(line,segment,eps=EPSILON) =
|
function line_segment_intersection(line,segment,eps=EPSILON) =
|
||||||
assert( is_finite(eps) && eps>=0, "The tolerance should be a positive number." )
|
assert( is_finite(eps) && (eps>=0), "The tolerance should be a non-negative value." )
|
||||||
assert( _valid_line(line, dim=2,eps=eps) &&_valid_line(segment,dim=2,eps=eps), "Invalid line or segment." )
|
assert( _valid_line(line, dim=2,eps=eps) &&_valid_line(segment,dim=2,eps=eps), "Invalid line or segment." )
|
||||||
let(
|
let(
|
||||||
isect = _general_line_intersection(line,segment,eps=eps)
|
isect = _general_line_intersection(line,segment,eps=eps)
|
||||||
|
@ -209,9 +210,9 @@ function line_segment_intersection(line,segment,eps=EPSILON) =
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// r1 = First 2D ray, given as a list `[START,POINT]` of the 2D start-point START, and a 2D point POINT on the ray.
|
// r1 = First 2D ray, given as a list `[START,POINT]` of the 2D start-point START, and a 2D point POINT on the ray.
|
||||||
// r2 = Second 2D ray, given as a list `[START,POINT]` of the 2D start-point START, and a 2D point POINT on the ray.
|
// r2 = Second 2D ray, given as a list `[START,POINT]` of the 2D start-point START, and a 2D point POINT on the ray.
|
||||||
// eps = Acceptable variance. Default: `EPSILON` (1e-9)
|
// eps = Tolerance in geometric comparisons. Default: `EPSILON` (1e-9)
|
||||||
function ray_intersection(r1,r2,eps=EPSILON) =
|
function ray_intersection(r1,r2,eps=EPSILON) =
|
||||||
assert( is_finite(eps) && eps>=0, "The tolerance should be a positive number." )
|
assert( is_finite(eps) && (eps>=0), "The tolerance should be a non-negative value." )
|
||||||
assert( _valid_line(r1,dim=2,eps=eps) && _valid_line(r2,dim=2,eps=eps), "Invalid ray(s)." )
|
assert( _valid_line(r1,dim=2,eps=eps) && _valid_line(r2,dim=2,eps=eps), "Invalid ray(s)." )
|
||||||
let(
|
let(
|
||||||
isect = _general_line_intersection(r1,r2,eps=eps)
|
isect = _general_line_intersection(r1,r2,eps=eps)
|
||||||
|
@ -229,10 +230,10 @@ function ray_intersection(r1,r2,eps=EPSILON) =
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// ray = The 2D ray, given as a list `[START,POINT]` of the 2D start-point START, and a 2D point POINT on the ray.
|
// ray = The 2D ray, given as a list `[START,POINT]` of the 2D start-point START, and a 2D point POINT on the ray.
|
||||||
// segment = The bounded 2D line segment, given as a list of the two 2D endpoints of the segment.
|
// segment = The bounded 2D line segment, given as a list of the two 2D endpoints of the segment.
|
||||||
// eps = Acceptable variance. Default: `EPSILON` (1e-9)
|
// eps = Tolerance in geometric comparisons. Default: `EPSILON` (1e-9)
|
||||||
function ray_segment_intersection(ray,segment,eps=EPSILON) =
|
function ray_segment_intersection(ray,segment,eps=EPSILON) =
|
||||||
assert( _valid_line(ray,dim=2,eps=eps) && _valid_line(segment,dim=2,eps=eps), "Invalid ray or segment." )
|
assert( _valid_line(ray,dim=2,eps=eps) && _valid_line(segment,dim=2,eps=eps), "Invalid ray or segment." )
|
||||||
assert( is_finite(eps) && eps>=0, "The tolerance should be a positive number." )
|
assert( is_finite(eps) && (eps>=0), "The tolerance should be a non-negative value." )
|
||||||
let(
|
let(
|
||||||
isect = _general_line_intersection(ray,segment,eps=eps)
|
isect = _general_line_intersection(ray,segment,eps=eps)
|
||||||
)
|
)
|
||||||
|
@ -250,10 +251,10 @@ function ray_segment_intersection(ray,segment,eps=EPSILON) =
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// s1 = First 2D segment, given as a list of the two 2D endpoints of the line segment.
|
// s1 = First 2D segment, given as a list of the two 2D endpoints of the line segment.
|
||||||
// s2 = Second 2D segment, given as a list of the two 2D endpoints of the line segment.
|
// s2 = Second 2D segment, given as a list of the two 2D endpoints of the line segment.
|
||||||
// eps = Acceptable variance. Default: `EPSILON` (1e-9)
|
// eps = Tolerance in geometric comparisons. Default: `EPSILON` (1e-9)
|
||||||
function segment_intersection(s1,s2,eps=EPSILON) =
|
function segment_intersection(s1,s2,eps=EPSILON) =
|
||||||
assert( _valid_line(s1,dim=2,eps=eps) && _valid_line(s2,dim=2,eps=eps), "Invalid segment(s)." )
|
assert( _valid_line(s1,dim=2,eps=eps) && _valid_line(s2,dim=2,eps=eps), "Invalid segment(s)." )
|
||||||
assert( is_finite(eps) && eps>=0, "The tolerance should be a positive number." )
|
assert( is_finite(eps) && (eps>=0), "The tolerance should be a non-negative value." )
|
||||||
let(
|
let(
|
||||||
isect = _general_line_intersection(s1,s2,eps=eps)
|
isect = _general_line_intersection(s1,s2,eps=eps)
|
||||||
)
|
)
|
||||||
|
@ -461,7 +462,7 @@ function segment_closest_point(seg,pt) =
|
||||||
// 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)
|
||||||
function line_from_points(points, fast=false, eps=EPSILON) =
|
function line_from_points(points, fast=false, eps=EPSILON) =
|
||||||
assert( is_path(points,dim=undef), "Improper point list." )
|
assert( is_path(points,dim=undef), "Improper point list." )
|
||||||
assert( is_finite(eps) && eps>=0, "The tolerance should be a positive number." )
|
assert( is_finite(eps) && (eps>=0), "The tolerance should be a non-negative value." )
|
||||||
let( pb = furthest_point(points[0],points) )
|
let( pb = furthest_point(points[0],points) )
|
||||||
approx(norm(points[pb]-points[0]),0) ? undef :
|
approx(norm(points[pb]-points[0]),0) ? undef :
|
||||||
fast || collinear(points) ? [points[pb], points[0]] : undef;
|
fast || collinear(points) ? [points[pb], points[0]] : undef;
|
||||||
|
@ -810,6 +811,10 @@ function adj_opp_to_ang(adj,opp) =
|
||||||
// Description:
|
// Description:
|
||||||
// Returns the area of a triangle formed between three 2D or 3D vertices.
|
// Returns the area of a triangle formed between three 2D or 3D vertices.
|
||||||
// Result will be negative if the points are 2D and in clockwise order.
|
// Result will be negative if the points are 2D and in clockwise order.
|
||||||
|
// Arguments:
|
||||||
|
// a = The first vertex of the triangle.
|
||||||
|
// b = The second vertex of the triangle.
|
||||||
|
// c = The third vertex of the triangle.
|
||||||
// Examples:
|
// Examples:
|
||||||
// triangle_area([0,0], [5,10], [10,0]); // Returns -50
|
// triangle_area([0,0], [5,10], [10,0]); // Returns -50
|
||||||
// triangle_area([10,0], [5,10], [0,0]); // Returns 50
|
// triangle_area([10,0], [5,10], [0,0]); // Returns 50
|
||||||
|
@ -877,6 +882,9 @@ function plane3pt_indexed(points, i1, i2, i3) =
|
||||||
// plane_from_normal(normal, [pt])
|
// plane_from_normal(normal, [pt])
|
||||||
// Description:
|
// Description:
|
||||||
// Returns a plane defined by a normal vector and a point.
|
// Returns a plane defined by a normal vector and a point.
|
||||||
|
// Arguments:
|
||||||
|
// normal = Normal vector to the plane to find.
|
||||||
|
// pt = Point 3D on the plane to find.
|
||||||
// Example:
|
// Example:
|
||||||
// plane_from_normal([0,0,1], [2,2,2]); // Returns the xy plane passing through the point (2,2,2)
|
// plane_from_normal([0,0,1], [2,2,2]); // Returns the xy plane passing through the point (2,2,2)
|
||||||
function plane_from_normal(normal, pt=[0,0,0]) =
|
function plane_from_normal(normal, pt=[0,0,0]) =
|
||||||
|
@ -896,7 +904,7 @@ function plane_from_normal(normal, pt=[0,0,0]) =
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// points = The list of points to find the plane of.
|
// points = The list of points to find the plane of.
|
||||||
// fast = If true, don't verify that all points in the list are coplanar. Default: false
|
// fast = If true, don't verify that all points in the list are coplanar. Default: false
|
||||||
// eps = How much variance is allowed in testing that each point is on the same plane. Default: `EPSILON` (1e-9)
|
// eps = Tolerance in geometric comparisons. Default: `EPSILON` (1e-9)
|
||||||
// Example(3D):
|
// Example(3D):
|
||||||
// xyzpath = rot(45, v=[-0.3,1,0], p=path3d(star(n=6,id=70,d=100), 70));
|
// xyzpath = rot(45, v=[-0.3,1,0], p=path3d(star(n=6,id=70,d=100), 70));
|
||||||
// plane = plane_from_points(xyzpath);
|
// plane = plane_from_points(xyzpath);
|
||||||
|
@ -905,9 +913,8 @@ function plane_from_normal(normal, pt=[0,0,0]) =
|
||||||
// move(cp) rot(from=UP,to=plane_normal(plane)) anchor_arrow();
|
// move(cp) rot(from=UP,to=plane_normal(plane)) anchor_arrow();
|
||||||
function plane_from_points(points, fast=false, eps=EPSILON) =
|
function plane_from_points(points, fast=false, eps=EPSILON) =
|
||||||
assert( is_path(points,dim=3), "Improper 3d point list." )
|
assert( is_path(points,dim=3), "Improper 3d point list." )
|
||||||
assert( is_finite(eps) && eps>=0, "The tolerance should be a positive number." )
|
assert( is_finite(eps) && (eps>=0), "The tolerance should be a non-negative value." )
|
||||||
let(
|
let(
|
||||||
points = deduplicate(points),
|
|
||||||
indices = noncollinear_triple(points,error=false)
|
indices = noncollinear_triple(points,error=false)
|
||||||
)
|
)
|
||||||
indices==[] ? undef :
|
indices==[] ? undef :
|
||||||
|
@ -931,7 +938,7 @@ function plane_from_points(points, fast=false, eps=EPSILON) =
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// poly = The planar 3D polygon to find the plane of.
|
// poly = The planar 3D polygon to find the plane of.
|
||||||
// fast = If true, doesn't verify that all points in the polygon are coplanar. Default: false
|
// fast = If true, doesn't verify that all points in the polygon are coplanar. Default: false
|
||||||
// eps = How much variance is allowed in testing that each point is on the same plane. Default: `EPSILON` (1e-9)
|
// eps = Tolerance in geometric comparisons. Default: `EPSILON` (1e-9)
|
||||||
// Example(3D):
|
// Example(3D):
|
||||||
// xyzpath = rot(45, v=[0,1,0], p=path3d(star(n=5,step=2,d=100), 70));
|
// xyzpath = rot(45, v=[0,1,0], p=path3d(star(n=5,step=2,d=100), 70));
|
||||||
// plane = plane_from_polygon(xyzpath);
|
// plane = plane_from_polygon(xyzpath);
|
||||||
|
@ -940,7 +947,7 @@ function plane_from_points(points, fast=false, eps=EPSILON) =
|
||||||
// move(cp) rot(from=UP,to=plane_normal(plane)) anchor_arrow();
|
// move(cp) rot(from=UP,to=plane_normal(plane)) anchor_arrow();
|
||||||
function plane_from_polygon(poly, fast=false, eps=EPSILON) =
|
function plane_from_polygon(poly, fast=false, eps=EPSILON) =
|
||||||
assert( is_path(poly,dim=3), "Invalid polygon." )
|
assert( is_path(poly,dim=3), "Invalid polygon." )
|
||||||
assert( is_finite(eps) && eps>=0, "The tolerance should be a positive number." )
|
assert( is_finite(eps) && (eps>=0), "The tolerance should be a non-negative value." )
|
||||||
let(
|
let(
|
||||||
poly = deduplicate(poly),
|
poly = deduplicate(poly),
|
||||||
n = polygon_normal(poly),
|
n = polygon_normal(poly),
|
||||||
|
@ -954,6 +961,8 @@ function plane_from_polygon(poly, fast=false, eps=EPSILON) =
|
||||||
// plane_normal(plane);
|
// plane_normal(plane);
|
||||||
// Description:
|
// Description:
|
||||||
// Returns the unit length normal vector for the given plane.
|
// Returns the unit length normal vector for the given plane.
|
||||||
|
// Arguments:
|
||||||
|
// plane = The `[A,B,C,D]` plane definition where `Ax+By+Cz=D` is the formula of the plane.
|
||||||
function plane_normal(plane) =
|
function plane_normal(plane) =
|
||||||
assert( _valid_plane(plane), "Invalid input plane." )
|
assert( _valid_plane(plane), "Invalid input plane." )
|
||||||
unit([plane.x, plane.y, plane.z]);
|
unit([plane.x, plane.y, plane.z]);
|
||||||
|
@ -966,6 +975,8 @@ function plane_normal(plane) =
|
||||||
// Returns coeficient D of the normalized plane equation `Ax+By+Cz=D`, or the scalar offset of the plane from the origin.
|
// Returns coeficient D of the normalized plane equation `Ax+By+Cz=D`, or the scalar offset of the plane from the origin.
|
||||||
// This value may be negative.
|
// This value may be negative.
|
||||||
// The absolute value of this coefficient is the distance of the plane from the origin.
|
// The absolute value of this coefficient is the distance of the plane from the origin.
|
||||||
|
// Arguments:
|
||||||
|
// plane = The `[A,B,C,D]` plane definition where `Ax+By+Cz=D` is the formula of the plane.
|
||||||
function plane_offset(plane) =
|
function plane_offset(plane) =
|
||||||
assert( _valid_plane(plane), "Invalid input plane." )
|
assert( _valid_plane(plane), "Invalid input plane." )
|
||||||
plane[3]/norm([plane.x, plane.y, plane.z]);
|
plane[3]/norm([plane.x, plane.y, plane.z]);
|
||||||
|
@ -1037,6 +1048,8 @@ function projection_on_plane(plane, points) =
|
||||||
// pt = plane_point_nearest_origin(plane);
|
// pt = plane_point_nearest_origin(plane);
|
||||||
// Description:
|
// Description:
|
||||||
// Returns the point on the plane that is closest to the origin.
|
// Returns the point on the plane that is closest to the origin.
|
||||||
|
// Arguments:
|
||||||
|
// plane = The `[A,B,C,D]` plane definition where `Ax+By+Cz=D` is the formula of the plane.
|
||||||
function plane_point_nearest_origin(plane) =
|
function plane_point_nearest_origin(plane) =
|
||||||
let( plane = normalize_plane(plane) )
|
let( plane = normalize_plane(plane) )
|
||||||
point3d(plane) * plane[3];
|
point3d(plane) * plane[3];
|
||||||
|
@ -1053,7 +1066,7 @@ function plane_point_nearest_origin(plane) =
|
||||||
// towards. If the point is behind the plane, then the distance returned
|
// towards. If the point is behind the plane, then the distance returned
|
||||||
// will be negative. The normal of the plane is the same as [A,B,C].
|
// will be negative. The normal of the plane is the same as [A,B,C].
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// plane = The [A,B,C,D] values for the equation of the plane.
|
// plane = The `[A,B,C,D]` plane definition where `Ax+By+Cz=D` is the formula of the plane.
|
||||||
// point = The distance evaluation point.
|
// point = The distance evaluation point.
|
||||||
function distance_from_plane(plane, point) =
|
function distance_from_plane(plane, point) =
|
||||||
assert( _valid_plane(plane), "Invalid input plane." )
|
assert( _valid_plane(plane), "Invalid input plane." )
|
||||||
|
@ -1061,8 +1074,6 @@ function distance_from_plane(plane, point) =
|
||||||
let( plane = normalize_plane(plane) )
|
let( plane = normalize_plane(plane) )
|
||||||
point3d(plane)* point - plane[3];
|
point3d(plane)* point - plane[3];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Returns [POINT, U] if line intersects plane at one point.
|
// Returns [POINT, U] if line intersects plane at one point.
|
||||||
// Returns [LINE, undef] if the line is on the plane.
|
// Returns [LINE, undef] if the line is on the plane.
|
||||||
// Returns undef if line is parallel to, but not on the given plane.
|
// Returns undef if line is parallel to, but not on the given plane.
|
||||||
|
@ -1119,7 +1130,7 @@ function plane_line_angle(plane, line) =
|
||||||
// plane = The [A,B,C,D] values for the equation of the 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.
|
// 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)
|
// 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 = The tolerance value in determining whether the line is parallel to the plane. Default: `EPSILON` (1e-9)
|
// eps = Tolerance in geometric comparisons. Default: `EPSILON` (1e-9)
|
||||||
function plane_line_intersection(plane, line, bounded=false, eps=EPSILON) =
|
function plane_line_intersection(plane, line, bounded=false, eps=EPSILON) =
|
||||||
assert( is_finite(eps) && eps>=0, "The tolerance should be a positive number." )
|
assert( is_finite(eps) && eps>=0, "The tolerance should be a positive number." )
|
||||||
assert(_valid_plane(plane,eps=eps) && _valid_line(line,dim=3,eps=eps), "Invalid plane and/or line.")
|
assert(_valid_plane(plane,eps=eps) && _valid_line(line,dim=3,eps=eps), "Invalid plane and/or line.")
|
||||||
|
@ -1148,7 +1159,7 @@ function plane_line_intersection(plane, line, bounded=false, eps=EPSILON) =
|
||||||
// poly = The 3D planar polygon to find the intersection with.
|
// poly = The 3D planar polygon to find the intersection with.
|
||||||
// line = A list of two distinct 3D points on the line.
|
// 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)
|
// 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 = The tolerance value in determining whether the line is parallel to the plane. Default: `EPSILON` (1e-9)
|
// eps = Tolerance in geometric comparisons. Default: `EPSILON` (1e-9)
|
||||||
function polygon_line_intersection(poly, line, bounded=false, eps=EPSILON) =
|
function polygon_line_intersection(poly, line, bounded=false, eps=EPSILON) =
|
||||||
assert( is_finite(eps) && eps>=0, "The tolerance should be a positive number." )
|
assert( is_finite(eps) && eps>=0, "The tolerance should be a positive number." )
|
||||||
assert(is_path(poly,dim=3), "Invalid polygon." )
|
assert(is_path(poly,dim=3), "Invalid polygon." )
|
||||||
|
@ -1203,6 +1214,10 @@ function polygon_line_intersection(poly, line, bounded=false, eps=EPSILON) =
|
||||||
// If you give three planes the intersection is returned as a point. If you give two planes the intersection
|
// If you give three planes the intersection is returned as a point. If you give two planes the intersection
|
||||||
// is returned as a list of two points on the line of intersection. If any two input planes are parallel
|
// is returned as a list of two points on the line of intersection. If any two input planes are parallel
|
||||||
// or coincident then returns undef.
|
// or coincident then returns undef.
|
||||||
|
// Arguments:
|
||||||
|
// plane1 = The [A,B,C,D] coefficients for the first plane equation `Ax+By+Cz=D`.
|
||||||
|
// plane2 = The [A,B,C,D] coefficients for the second plane equation `Ax+By+Cz=D`.
|
||||||
|
// plane3 = The [A,B,C,D] coefficients for the third plane equation `Ax+By+Cz=D`.
|
||||||
function plane_intersection(plane1,plane2,plane3) =
|
function plane_intersection(plane1,plane2,plane3) =
|
||||||
assert( _valid_plane(plane1) && _valid_plane(plane2) && (is_undef(plane3) ||_valid_plane(plane3)),
|
assert( _valid_plane(plane1) && _valid_plane(plane2) && (is_undef(plane3) ||_valid_plane(plane3)),
|
||||||
"The input must be 2 or 3 planes." )
|
"The input must be 2 or 3 planes." )
|
||||||
|
@ -1229,10 +1244,10 @@ function plane_intersection(plane1,plane2,plane3) =
|
||||||
// Returns true if the given 3D points are non-collinear and are on a plane.
|
// Returns true if the given 3D points are non-collinear and are on a plane.
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// points = The points to test.
|
// points = The points to test.
|
||||||
// eps = How much variance is allowed in the planarity test. Default: `EPSILON` (1e-9)
|
// eps = Tolerance in geometric comparisons. Default: `EPSILON` (1e-9)
|
||||||
function coplanar(points, eps=EPSILON) =
|
function coplanar(points, eps=EPSILON) =
|
||||||
assert( is_path(points,dim=3) , "Input should be a list of 3D points." )
|
assert( is_path(points,dim=3) , "Input should be a list of 3D points." )
|
||||||
assert( is_finite(eps) && eps>=0, "The tolerance should be a non-negative number." )
|
assert( is_finite(eps) && eps>=0, "The tolerance should be a non-negative value." )
|
||||||
len(points)<=2 ? false
|
len(points)<=2 ? false
|
||||||
: let( ip = noncollinear_triple(points,error=false,eps=eps) )
|
: let( ip = noncollinear_triple(points,error=false,eps=eps) )
|
||||||
ip == [] ? false :
|
ip == [] ? false :
|
||||||
|
@ -1249,7 +1264,7 @@ function coplanar(points, eps=EPSILON) =
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// plane = The plane to test the points on.
|
// plane = The plane to test the points on.
|
||||||
// points = The list of 3D points to test.
|
// points = The list of 3D points to test.
|
||||||
// eps = How much variance is allowed in the planarity testing. Default: `EPSILON` (1e-9)
|
// eps = Tolerance in geometric comparisons. Default: `EPSILON` (1e-9)
|
||||||
function points_on_plane(points, plane, eps=EPSILON) =
|
function points_on_plane(points, plane, eps=EPSILON) =
|
||||||
assert( _valid_plane(plane), "Invalid plane." )
|
assert( _valid_plane(plane), "Invalid plane." )
|
||||||
assert( is_matrix(points,undef,3) && len(points)>0, "Invalid pointlist." ) // using is_matrix it accepts len(points)==1
|
assert( is_matrix(points,undef,3) && len(points)>0, "Invalid pointlist." ) // using is_matrix it accepts len(points)==1
|
||||||
|
@ -1268,7 +1283,7 @@ function points_on_plane(points, plane, eps=EPSILON) =
|
||||||
// plane that the normal points towards. The normal of the plane is the
|
// plane that the normal points towards. The normal of the plane is the
|
||||||
// same as [A,B,C].
|
// same as [A,B,C].
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// plane = The [A,B,C,D] coefficients for the equation of the plane.
|
// plane = The [A,B,C,D] coefficients for the first plane equation `Ax+By+Cz=D`.
|
||||||
// point = The 3D point to test.
|
// point = The 3D point to test.
|
||||||
function in_front_of_plane(plane, point) =
|
function in_front_of_plane(plane, point) =
|
||||||
distance_from_plane(plane, point) > EPSILON;
|
distance_from_plane(plane, point) > EPSILON;
|
||||||
|
@ -1513,6 +1528,13 @@ function circle_point_tangents(r, d, cp, pt) =
|
||||||
// returns only two entries. If one circle is inside the other one then no tangents exist
|
// returns only two entries. If one circle is inside the other one then no tangents exist
|
||||||
// so the function returns the empty set. When the circles are tangent a degenerate tangent line
|
// so the function returns the empty set. When the circles are tangent a degenerate tangent line
|
||||||
// passes through the point of tangency of the two circles: this degenerate line is NOT returned.
|
// passes through the point of tangency of the two circles: this degenerate line is NOT returned.
|
||||||
|
// Arguments:
|
||||||
|
// c1 = Center of the first circle.
|
||||||
|
// r1 = Radius of the first circle.
|
||||||
|
// c2 = Center of the second circle.
|
||||||
|
// r2 = Radius of the second circle.
|
||||||
|
// d1 = Diameter of the first circle.
|
||||||
|
// d2 = Diameter of the second circle.
|
||||||
// Example(2D): Four tangents, first in green, second in black, third in blue, last in red.
|
// Example(2D): Four tangents, first in green, second in black, third in blue, last in red.
|
||||||
// $fn=32;
|
// $fn=32;
|
||||||
// c1 = [3,4]; r1 = 2;
|
// c1 = [3,4]; r1 = 2;
|
||||||
|
@ -1623,11 +1645,15 @@ function circle_line_intersection(c,r,line,d,bounded=false,eps=EPSILON) =
|
||||||
// Usage:
|
// Usage:
|
||||||
// noncollinear_triple(points);
|
// noncollinear_triple(points);
|
||||||
// Description:
|
// Description:
|
||||||
// Finds the indices of three good non-collinear points from the points list `points`.
|
// Finds the indices of three good non-collinear points from the pointlist `points`.
|
||||||
// If all points are collinear, returns [].
|
// If all points are collinear returns [] when `error=true` or an error otherwise .
|
||||||
|
// 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) =
|
function noncollinear_triple(points,error=true,eps=EPSILON) =
|
||||||
assert( is_path(points), "Invalid input points." )
|
assert( is_path(points), "Invalid input points." )
|
||||||
assert( is_finite(eps) && (eps>=0), "The tolerance should be a non-negative number." )
|
assert( is_finite(eps) && (eps>=0), "The tolerance should be a non-negative value." )
|
||||||
let(
|
let(
|
||||||
pa = points[0],
|
pa = points[0],
|
||||||
b = furthest_point(pa, points),
|
b = furthest_point(pa, points),
|
||||||
|
@ -1701,29 +1727,29 @@ function furthest_point(pt, points) =
|
||||||
// area = polygon_area(poly);
|
// area = polygon_area(poly);
|
||||||
// Description:
|
// Description:
|
||||||
// Given a 2D or 3D planar polygon, returns the area of that polygon.
|
// Given a 2D or 3D planar polygon, returns the area of that polygon.
|
||||||
// If the polygon is self-crossing, the results are undefined. For non-planar points the result is undef.
|
// If the polygon is self-crossing, the results are undefined. For non-planar 3D polygon the result is undef.
|
||||||
// When `signed` is true, a signed area is returned; a positive area indicates a counterclockwise polygon.
|
// When `signed` is true, a signed area is returned; a positive area indicates a clockwise polygon.
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// poly = polygon to compute the area of.
|
// poly = Polygon to compute the area of.
|
||||||
// signed = if true, a signed area is returned (default: false)
|
// signed = If true, a signed area is returned. Default: false.
|
||||||
function polygon_area(poly, signed=false) =
|
function polygon_area(poly, signed=false) =
|
||||||
assert(is_path(poly), "Invalid polygon." )
|
assert(is_path(poly), "Invalid polygon." )
|
||||||
len(poly)<3 ? 0 :
|
len(poly)<3 ? 0 :
|
||||||
let( cpoly = close_path(simplify_path(poly)) )
|
|
||||||
len(poly[0])==2
|
len(poly[0])==2
|
||||||
? sum([for(i=[1:1:len(poly)-2]) cross(poly[i]-poly[0],poly[i+1]-poly[0]) ])/2
|
? let( total = sum([for(i=[1:1:len(poly)-2]) cross(poly[i]-poly[0],poly[i+1]-poly[0]) ])/2 )
|
||||||
|
signed ? total : abs(total)
|
||||||
: let( plane = plane_from_points(poly) )
|
: let( plane = plane_from_points(poly) )
|
||||||
plane==undef? undef :
|
plane==undef? undef :
|
||||||
let(
|
let(
|
||||||
n = unit(plane_normal(plane)),
|
n = plane_normal(plane),
|
||||||
total = sum([
|
total = sum([
|
||||||
for(i=[1:1:len(cpoly)-2])
|
for(i=[1:1:len(poly)-2])
|
||||||
let(
|
let(
|
||||||
v1 = cpoly[i] - cpoly[0],
|
v1 = poly[i] - poly[0],
|
||||||
v2 = cpoly[i+1] - cpoly[0]
|
v2 = poly[i+1] - poly[0]
|
||||||
)
|
)
|
||||||
cross(v1,v2) * n
|
cross(v1,v2) * n
|
||||||
])/2
|
])/2
|
||||||
)
|
)
|
||||||
signed ? total : abs(total);
|
signed ? total : abs(total);
|
||||||
|
|
||||||
|
@ -1734,6 +1760,8 @@ function polygon_area(poly, signed=false) =
|
||||||
// Description:
|
// Description:
|
||||||
// Returns true if the given 2D polygon is convex. The result is meaningless if the polygon is not simple (self-intersecting).
|
// Returns true if the given 2D polygon is convex. The result is meaningless if the polygon is not simple (self-intersecting).
|
||||||
// If the points are collinear the result is true.
|
// If the points are collinear the result is true.
|
||||||
|
// Arguments:
|
||||||
|
// poly = Polygon to check.
|
||||||
// Example:
|
// Example:
|
||||||
// is_convex_polygon(circle(d=50)); // Returns: true
|
// is_convex_polygon(circle(d=50)); // Returns: true
|
||||||
// Example:
|
// Example:
|
||||||
|
@ -1773,6 +1801,9 @@ function polygon_shift(poly, i) =
|
||||||
// polygon_shift_to_closest_point(path, pt);
|
// polygon_shift_to_closest_point(path, pt);
|
||||||
// Description:
|
// Description:
|
||||||
// Given a polygon `poly`, rotates the point ordering so that the first point in the path is the one closest to the given point `pt`.
|
// Given a polygon `poly`, rotates the point ordering so that the first point in the path is the one closest to the given point `pt`.
|
||||||
|
// Arguments:
|
||||||
|
// poly = The list of points in the polygon path.
|
||||||
|
// pt = The reference point.
|
||||||
function polygon_shift_to_closest_point(poly, pt) =
|
function polygon_shift_to_closest_point(poly, pt) =
|
||||||
assert(is_vector(pt), "Invalid point." )
|
assert(is_vector(pt), "Invalid point." )
|
||||||
assert(is_path(poly,dim=len(pt)), "Invalid polygon or incompatible dimension with the point." )
|
assert(is_path(poly,dim=len(pt)), "Invalid polygon or incompatible dimension with the point." )
|
||||||
|
@ -1876,32 +1907,35 @@ function align_polygon(reference, poly, angles, cp) =
|
||||||
// Description:
|
// Description:
|
||||||
// Given a simple 2D polygon, returns the 2D coordinates of the polygon's centroid.
|
// Given a simple 2D polygon, returns the 2D coordinates of the polygon's centroid.
|
||||||
// Given a simple 3D planar polygon, returns the 3D coordinates of the polygon's centroid.
|
// Given a simple 3D planar polygon, returns the 3D coordinates of the polygon's centroid.
|
||||||
// If the polygon is self-intersecting, the results are undefined.
|
// Collinear points produce an error. The results are meaningless for self-intersecting
|
||||||
function centroid(poly) =
|
// 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 centroid(poly, eps=EPSILON) =
|
||||||
assert( is_path(poly,dim=[2,3]), "The input must be a 2D or 3D polygon." )
|
assert( is_path(poly,dim=[2,3]), "The input must be a 2D or 3D polygon." )
|
||||||
len(poly[0])==2
|
assert( is_finite(eps) && (eps>=0), "The tolerance should be a non-negative value." )
|
||||||
? sum([
|
let(
|
||||||
for(i=[0:len(poly)-1])
|
n = len(poly[0])==2 ? 1 :
|
||||||
let(segment=select(poly,i,i+1))
|
let(
|
||||||
det2(segment)*sum(segment)
|
plane = plane_from_points(poly, fast=true) )
|
||||||
]) / 6 / polygon_area(poly)
|
assert( !is_undef(plane), "The polygon must be planar." )
|
||||||
: let( plane = plane_from_points(poly, fast=true) )
|
plane_normal(plane),
|
||||||
assert( !is_undef(plane), "The polygon must be planar." )
|
v0 = poly[0] ,
|
||||||
let(
|
|
||||||
n = plane_normal(plane),
|
|
||||||
val = sum([for(i=[1:len(poly)-2])
|
val = sum([for(i=[1:len(poly)-2])
|
||||||
let(
|
let(
|
||||||
v0 = poly[0],
|
v1 = poly[i],
|
||||||
v1 = poly[i],
|
v2 = poly[i+1],
|
||||||
v2 = poly[i+1],
|
area = cross(v2-v0,v1-v0)*n
|
||||||
area = cross(v2-v0,v1-v0)*n
|
)
|
||||||
)
|
[ area, (v0+v1+v2)*area ]
|
||||||
[ area, (v0+v1+v2)*area ]
|
] )
|
||||||
] )
|
|
||||||
)
|
)
|
||||||
|
assert(!approx(val[0],0, eps), "The polygon is self-intersecting or its points are collinear.")
|
||||||
val[1]/val[0]/3;
|
val[1]/val[0]/3;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Function: point_in_polygon()
|
// Function: point_in_polygon()
|
||||||
// Usage:
|
// Usage:
|
||||||
// point_in_polygon(point, poly, <eps>)
|
// point_in_polygon(point, poly, <eps>)
|
||||||
|
@ -1910,7 +1944,7 @@ function centroid(poly) =
|
||||||
// the specified 2D polygon using either the Nonzero Winding rule or the Even-Odd rule.
|
// 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.
|
// See https://en.wikipedia.org/wiki/Nonzero-rule and https://en.wikipedia.org/wiki/Even–odd_rule.
|
||||||
// The polygon is given as a list of 2D points, not including the repeated end point.
|
// The polygon is given as a list of 2D points, not including the repeated end point.
|
||||||
// Returns -1 if the point is outside the polyon.
|
// Returns -1 if the point is outside the polygon.
|
||||||
// Returns 0 if the point is on the boundary.
|
// Returns 0 if the point is on the boundary.
|
||||||
// Returns 1 if the point lies in the interior.
|
// Returns 1 if the point lies in the interior.
|
||||||
// The polygon does not need to be simple: it can have self-intersections.
|
// The polygon does not need to be simple: it can have self-intersections.
|
||||||
|
@ -1920,17 +1954,17 @@ function centroid(poly) =
|
||||||
// point = The 2D point to check position of.
|
// point = The 2D point to check position of.
|
||||||
// poly = The list of 2D path points forming the perimeter of the polygon.
|
// 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 )
|
// nonzero = The rule to use: true for "Nonzero" rule and false for "Even-Odd" (Default: true )
|
||||||
// eps = Acceptable variance. Default: `EPSILON` (1e-9)
|
// eps = Tolerance in geometric comparisons. Default: `EPSILON` (1e-9)
|
||||||
function point_in_polygon(point, poly, eps=EPSILON, nonzero=true) =
|
function point_in_polygon(point, poly, nonzero=true, eps=EPSILON) =
|
||||||
// Original algorithms from http://geomalgorithms.com/a03-_inclusion.html
|
// Original algorithms from http://geomalgorithms.com/a03-_inclusion.html
|
||||||
assert( is_vector(point,2) && is_path(poly,dim=2) && len(poly)>2,
|
assert( is_vector(point,2) && is_path(poly,dim=2) && len(poly)>2,
|
||||||
"The point and polygon should be in 2D. The polygon should have more that 2 points." )
|
"The point and polygon should be in 2D. The polygon should have more that 2 points." )
|
||||||
assert( is_finite(eps) && eps>=0, "Invalid tolerance." )
|
assert( is_finite(eps) && (eps>=0), "The tolerance should be a non-negative value." )
|
||||||
// Does the point lie on any edges? If so return 0.
|
// Does the point lie on any edges? If so return 0.
|
||||||
let(
|
let(
|
||||||
on_brd = [for(i=[0:1:len(poly)-1])
|
on_brd = [for(i=[0:1:len(poly)-1])
|
||||||
let( seg = select(poly,i,i+1) )
|
let( seg = select(poly,i,i+1) )
|
||||||
if( !approx(seg[0],seg[1],eps=EPSILON) )
|
if( !approx(seg[0],seg[1],eps) )
|
||||||
point_on_segment2d(point, seg, eps=eps)? 1:0 ]
|
point_on_segment2d(point, seg, eps=eps)? 1:0 ]
|
||||||
)
|
)
|
||||||
sum(on_brd) > 0
|
sum(on_brd) > 0
|
||||||
|
@ -1954,12 +1988,12 @@ function point_in_polygon(point, poly, eps=EPSILON, nonzero=true) =
|
||||||
p0 = poly[i]-point,
|
p0 = poly[i]-point,
|
||||||
p1 = poly[(i+1)%n]-point
|
p1 = poly[(i+1)%n]-point
|
||||||
)
|
)
|
||||||
if( ( (p1.y>eps && p0.y<=0) || (p1.y<=0 && p0.y>eps) )
|
if( ( (p1.y>eps && p0.y<=eps) || (p1.y<=eps && p0.y>eps) )
|
||||||
&& 0 < p0.x - p0.y *(p1.x - p0.x)/(p1.y - p0.y) )
|
&& -eps < p0.x - p0.y *(p1.x - p0.x)/(p1.y - p0.y) )
|
||||||
1
|
1
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
2*(len(cross)%2)-1;;
|
2*(len(cross)%2)-1;
|
||||||
|
|
||||||
|
|
||||||
// Function: polygon_is_clockwise()
|
// Function: polygon_is_clockwise()
|
||||||
|
@ -1980,6 +2014,8 @@ function polygon_is_clockwise(poly) =
|
||||||
// clockwise_polygon(poly);
|
// clockwise_polygon(poly);
|
||||||
// Description:
|
// Description:
|
||||||
// Given a 2D polygon path, returns the clockwise winding version of that path.
|
// Given a 2D polygon path, returns the clockwise winding version of that path.
|
||||||
|
// Arguments:
|
||||||
|
// poly = The list of 2D path points for the perimeter of the polygon.
|
||||||
function clockwise_polygon(poly) =
|
function clockwise_polygon(poly) =
|
||||||
assert(is_path(poly,dim=2), "Input should be a 2d polygon")
|
assert(is_path(poly,dim=2), "Input should be a 2d polygon")
|
||||||
polygon_area(poly, signed=true)<0 ? poly : reverse_polygon(poly);
|
polygon_area(poly, signed=true)<0 ? poly : reverse_polygon(poly);
|
||||||
|
@ -1990,6 +2026,8 @@ function clockwise_polygon(poly) =
|
||||||
// ccw_polygon(poly);
|
// ccw_polygon(poly);
|
||||||
// Description:
|
// Description:
|
||||||
// Given a 2D polygon poly, returns the counter-clockwise winding version of that poly.
|
// Given a 2D polygon poly, returns the counter-clockwise winding version of that poly.
|
||||||
|
// Arguments:
|
||||||
|
// poly = The list of 2D path points for the perimeter of the polygon.
|
||||||
function ccw_polygon(poly) =
|
function ccw_polygon(poly) =
|
||||||
assert(is_path(poly,dim=2), "Input should be a 2d polygon")
|
assert(is_path(poly,dim=2), "Input should be a 2d polygon")
|
||||||
polygon_area(poly, signed=true)<0 ? reverse_polygon(poly) : poly;
|
polygon_area(poly, signed=true)<0 ? reverse_polygon(poly) : poly;
|
||||||
|
@ -2000,6 +2038,8 @@ function ccw_polygon(poly) =
|
||||||
// reverse_polygon(poly)
|
// reverse_polygon(poly)
|
||||||
// Description:
|
// Description:
|
||||||
// Reverses a polygon's winding direction, while still using the same start point.
|
// Reverses a polygon's winding direction, while still using the same start point.
|
||||||
|
// Arguments:
|
||||||
|
// poly = The list of the path points for the perimeter of the polygon.
|
||||||
function reverse_polygon(poly) =
|
function reverse_polygon(poly) =
|
||||||
assert(is_path(poly), "Input should be a polygon")
|
assert(is_path(poly), "Input should be a polygon")
|
||||||
let(lp=len(poly)) [for (i=idx(poly)) poly[(lp-i)%lp]];
|
let(lp=len(poly)) [for (i=idx(poly)) poly[(lp-i)%lp]];
|
||||||
|
@ -2010,7 +2050,9 @@ function reverse_polygon(poly) =
|
||||||
// n = polygon_normal(poly);
|
// n = polygon_normal(poly);
|
||||||
// Description:
|
// Description:
|
||||||
// Given a 3D planar polygon, returns a unit-length normal vector for the
|
// Given a 3D planar polygon, returns a unit-length normal vector for the
|
||||||
// clockwise orientation of the polygon.
|
// clockwise orientation of the polygon. If the polygon points are collinear, returns `undef`.
|
||||||
|
// Arguments:
|
||||||
|
// poly = The list of 3D path points for the perimeter of the polygon.
|
||||||
function polygon_normal(poly) =
|
function polygon_normal(poly) =
|
||||||
assert(is_path(poly,dim=3), "Invalid 3D polygon." )
|
assert(is_path(poly,dim=3), "Invalid 3D polygon." )
|
||||||
let(
|
let(
|
||||||
|
@ -2020,7 +2062,7 @@ function polygon_normal(poly) =
|
||||||
for (i=[1:1:len(poly)-2])
|
for (i=[1:1:len(poly)-2])
|
||||||
cross(poly[i+1]-p0, poly[i]-p0)
|
cross(poly[i+1]-p0, poly[i]-p0)
|
||||||
])
|
])
|
||||||
) unit(n);
|
) unit(n,undef);
|
||||||
|
|
||||||
|
|
||||||
function _split_polygon_at_x(poly, x) =
|
function _split_polygon_at_x(poly, x) =
|
||||||
|
|
|
@ -699,8 +699,7 @@ function regular_polyhedron_info(
|
||||||
face_normals,
|
face_normals,
|
||||||
radius_scale*entry[in_radius]
|
radius_scale*entry[in_radius]
|
||||||
] :
|
] :
|
||||||
// info == "vnf" ? [move(translation,p=scaled_points), stellate ? faces : face_triangles] :
|
info == "vnf" ? [move(translation,p=scaled_points), stellate ? faces : face_triangles] :
|
||||||
info == "vnf" ? [move(translation,p=scaled_points), faces] :
|
|
||||||
info == "vertices" ? move(translation,p=scaled_points) :
|
info == "vertices" ? move(translation,p=scaled_points) :
|
||||||
info == "faces" ? faces :
|
info == "faces" ? faces :
|
||||||
info == "face normals" ? face_normals :
|
info == "face normals" ? face_normals :
|
||||||
|
|
|
@ -835,7 +835,8 @@ module test_cleanup_path() {
|
||||||
|
|
||||||
module test_polygon_area() {
|
module test_polygon_area() {
|
||||||
assert(approx(polygon_area([[1,1],[-1,1],[-1,-1],[1,-1]]), 4));
|
assert(approx(polygon_area([[1,1],[-1,1],[-1,-1],[1,-1]]), 4));
|
||||||
assert(approx(polygon_area(circle(r=50,$fn=1000)), -PI*50*50, eps=0.1));
|
assert(approx(polygon_area(circle(r=50,$fn=1000),signed=true), -PI*50*50, eps=0.1));
|
||||||
|
assert(approx(polygon_area(rot([13,27,75],p=path3d(circle(r=50,$fn=1000),fill=23)),signed=true), PI*50*50, eps=0.1));
|
||||||
}
|
}
|
||||||
*test_polygon_area();
|
*test_polygon_area();
|
||||||
|
|
||||||
|
@ -909,8 +910,7 @@ module test_centroid() {
|
||||||
assert_approx(centroid(circle(d=100)), [0,0]);
|
assert_approx(centroid(circle(d=100)), [0,0]);
|
||||||
assert_approx(centroid(rect([40,60],rounding=10,anchor=LEFT)), [20,0]);
|
assert_approx(centroid(rect([40,60],rounding=10,anchor=LEFT)), [20,0]);
|
||||||
assert_approx(centroid(rect([40,60],rounding=10,anchor=FWD)), [0,30]);
|
assert_approx(centroid(rect([40,60],rounding=10,anchor=FWD)), [0,30]);
|
||||||
poly = [for(a=[0:90:360])
|
poly = move([1,2.5,3.1],p=rot([12,49,24], p=path3d(circle(10,$fn=33))));
|
||||||
move([1,2.5,3.1], rot(p=[cos(a),sin(a),0],from=[0,0,1],to=[1,1,1])) ];
|
|
||||||
assert_approx(centroid(poly), [1,2.5,3.1]);
|
assert_approx(centroid(poly), [1,2.5,3.1]);
|
||||||
}
|
}
|
||||||
*test_centroid();
|
*test_centroid();
|
||||||
|
@ -936,19 +936,22 @@ module test_point_in_polygon() {
|
||||||
poly2 = [ [-3,-3],[2,-3],[2,1],[-1,1],[-1,-1],[1,-1],[1,2],[-3,2] ];
|
poly2 = [ [-3,-3],[2,-3],[2,1],[-1,1],[-1,-1],[1,-1],[1,2],[-3,2] ];
|
||||||
assert(point_in_polygon([0,0], poly) == 1);
|
assert(point_in_polygon([0,0], poly) == 1);
|
||||||
assert(point_in_polygon([20,0], poly) == -1);
|
assert(point_in_polygon([20,0], poly) == -1);
|
||||||
assert(point_in_polygon([20,0], poly,EPSILON,nonzero=false) == -1);
|
assert(point_in_polygon([20,0], poly,nonzero=false) == -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([-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([5,-5], poly,EPSILON,nonzero=false) == 1);
|
assert(point_in_polygon([5,-5], poly,nonzero=false,eps=EPSILON) == 1);
|
||||||
assert(point_in_polygon([-10,-10], poly) == -1);
|
assert(point_in_polygon([-10,-10], poly) == -1);
|
||||||
assert(point_in_polygon([10,0], poly) == 0);
|
assert(point_in_polygon([10,0], poly) == 0);
|
||||||
assert(point_in_polygon([0,10], poly) == 0);
|
assert(point_in_polygon([0,10], poly) == 0);
|
||||||
assert(point_in_polygon([0,-10], poly) == 0);
|
assert(point_in_polygon([0,-10], poly) == 0);
|
||||||
assert(point_in_polygon([0,-10], poly,EPSILON,nonzero=false) == 0);
|
assert(point_in_polygon([0,-10], poly,nonzero=false) == 0);
|
||||||
assert(point_in_polygon([0,0], poly2,EPSILON,nonzero=true) == 1);
|
assert(point_in_polygon([0,0], poly2,nonzero=true) == 1);
|
||||||
assert(point_in_polygon([0,0], poly2,EPSILON,nonzero=false) == -1);
|
assert(point_in_polygon([0,1], poly2,nonzero=true) == 0);
|
||||||
|
assert(point_in_polygon([0,1], poly2,nonzero=false) == 0);
|
||||||
|
assert(point_in_polygon([1,0], poly2,nonzero=false) == 0);
|
||||||
|
assert(point_in_polygon([0,0], poly2,nonzero=false,eps=EPSILON) == -1);
|
||||||
}
|
}
|
||||||
*test_point_in_polygon();
|
*test_point_in_polygon();
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue