mirror of
https://github.com/BelfrySCAD/BOSL2.git
synced 2025-01-06 04:09:47 +00:00
Merge branch 'master' of https://github.com/revarbat/BOSL2
This commit is contained in:
commit
f8f5b113a7
12 changed files with 626 additions and 256 deletions
|
@ -1224,6 +1224,7 @@ function block_matrix(M) =
|
|||
// its diagonal. The off diagonal entries are set to offdiag,
|
||||
// which is zero by default.
|
||||
function diagonal_matrix(diag,offdiag=0) =
|
||||
assert(is_list(diag) && len(diag)>0)
|
||||
[for(i=[0:1:len(diag)-1]) [for(j=[0:len(diag)-1]) i==j?diag[i] : offdiag]];
|
||||
|
||||
|
||||
|
@ -1237,6 +1238,8 @@ function diagonal_matrix(diag,offdiag=0) =
|
|||
function submatrix_set(M,A,m=0,n=0) =
|
||||
assert(is_list(M))
|
||||
assert(is_list(A))
|
||||
assert(is_int(m))
|
||||
assert(is_int(n))
|
||||
let( badrows = [for(i=idx(A)) if (!is_list(A[i])) i])
|
||||
assert(badrows==[], str("Input submatrix malformed rows: ",badrows))
|
||||
[for(i=[0:1:len(M)-1])
|
||||
|
|
22
coords.scad
22
coords.scad
|
@ -142,24 +142,24 @@ function xy_to_polar(x,y=undef) = let(
|
|||
|
||||
|
||||
// Function: project_plane()
|
||||
// Usage: With 3 Points
|
||||
// Usage: With the plane defined by 3 Points
|
||||
// xyz = project_plane(point, a, b, c);
|
||||
// Usage: With Pointlist
|
||||
// Usage: With the plane defined by Pointlist
|
||||
// xyz = project_plane(point, POINTLIST);
|
||||
// Usage: With Plane Definition [A,B,C,D] Where Ax+By+Cz=D
|
||||
// Usage: With the plane defined by Plane Definition [A,B,C,D] Where Ax+By+Cz=D
|
||||
// xyz = project_plane(point, PLANE);
|
||||
// Description:
|
||||
// Converts the given 3D point from global coordinates to the 2D planar coordinates of the closest
|
||||
// point on the plane. This coordinate system can be useful in taking a set of nearly coplanar
|
||||
// Converts the given 3D points from global coordinates to the 2D planar coordinates of the closest
|
||||
// points on the plane. This coordinate system can be useful in taking a set of nearly coplanar
|
||||
// points, and converting them to a pure XY set of coordinates for manipulation, before converting
|
||||
// them back to the original 3D plane.
|
||||
// Can be called one of three ways:
|
||||
// - Given three points, `a`, `b`, and `c`, the planar coordinate system will have `[0,0]` at point `a`, and the Y+ axis will be towards point `b`.
|
||||
// - Given a list of points, finds three reasonably spaced non-collinear points in the list and uses them as points `a`, `b`, and `c` as above.
|
||||
// - Given a plane definition `[A,B,C,D]` where `Ax+By+Cz=D`, the closest point on that plane to the global origin at `[0,0,0]` will be the planar coordinate origin `[0,0]`.
|
||||
// them back to the original 3D plane. The parameter `point` may be a single point or a list of points
|
||||
// The plane may be given in one of three ways:
|
||||
// - by three points, `a`, `b`, and `c`, the planar coordinate system will have `[0,0]` at point `a`, and the Y+ axis will be towards point `b`.
|
||||
// - by a list of points passed by `a`, finds three reasonably spaced non-collinear points in the list and uses them as points `a`, `b`, and `c` as above.
|
||||
// - by a plane definition `[A,B,C,D]` passed by `a` where `Ax+By+Cz=D`, the closest point on that plane to the global origin at `[0,0,0]` will be the planar coordinate origin `[0,0]`.
|
||||
// Arguments:
|
||||
// point = The 3D point, or list of 3D points to project into the plane's 2D coordinate system.
|
||||
// a = A 3D point that the plane passes through. Used to define the plane.
|
||||
// a = A 3D point that the plane passes through or a list of points or a plane definition vector.
|
||||
// b = A 3D point that the plane passes through. Used to define the plane.
|
||||
// c = A 3D point that the plane passes through. Used to define the plane.
|
||||
// Example:
|
||||
|
|
|
@ -762,8 +762,8 @@ function triangle_area(a,b,c) =
|
|||
// Usage:
|
||||
// plane3pt(p1, p2, p3);
|
||||
// Description:
|
||||
// Generates the cartesian equation of a plane from three 3d points.
|
||||
// Returns [A,B,C,D] where Ax + By + Cz = D is the equation of a plane.
|
||||
// Generates the normalized cartesian equation of a plane from three 3d points.
|
||||
// Returns [A,B,C,D] where Ax + By + Cz = D is the equation of a plane.
|
||||
// Returns [], if the points are collinear.
|
||||
// Arguments:
|
||||
// p1 = The first point on the plane.
|
||||
|
@ -777,7 +777,7 @@ function plane3pt(p1, p2, p3) =
|
|||
nrm = norm(crx)
|
||||
)
|
||||
approx(nrm,0) ? [] :
|
||||
concat(crx/nrm, [crx*p1]/nrm);
|
||||
concat(crx, crx*p1)/nrm;
|
||||
|
||||
|
||||
// Function: plane3pt_indexed()
|
||||
|
@ -785,7 +785,7 @@ function plane3pt(p1, p2, p3) =
|
|||
// plane3pt_indexed(points, i1, i2, i3);
|
||||
// Description:
|
||||
// Given a list of 3d points, and the indices of three of those points,
|
||||
// generates the cartesian equation of a plane that those points all
|
||||
// generates the normalized cartesian equation of a plane that those points all
|
||||
// lie on. If the points are not collinear, returns [A,B,C,D] where Ax+By+Cz=D is the equation of a plane.
|
||||
// If they are collinear, returns [].
|
||||
// Arguments:
|
||||
|
@ -816,15 +816,15 @@ function plane3pt_indexed(points, i1, i2, i3) =
|
|||
function plane_from_normal(normal, pt=[0,0,0]) =
|
||||
assert( is_matrix([normal,pt],2,3) && !approx(norm(normal),0),
|
||||
"Inputs `normal` and `pt` should 3d vectors/points and `normal` cannot be zero." )
|
||||
concat(normal, [normal*pt]);
|
||||
concat(normal, normal*pt)/norm(normal);
|
||||
|
||||
|
||||
// Function: plane_from_points()
|
||||
// Usage:
|
||||
// plane_from_points(points, <fast>, <eps>);
|
||||
// Description:
|
||||
// Given a list of 3 or more coplanar 3D points, returns the coefficients of the cartesian equation of a plane,
|
||||
// that is [A,B,C,D] where Ax+By+Cz=D is the equation of the plane.
|
||||
// Given a list of 3 or more coplanar 3D points, returns the coefficients of the normalized cartesian equation of a plane,
|
||||
// that is [A,B,C,D] where Ax+By+Cz=D is the equation of the plane where norm([A,B,C])=1.
|
||||
// If `fast` is false and the points in the list are collinear or not coplanar, then `undef` is returned.
|
||||
// if `fast` is true, then the coplanarity test is skipped and a plane passing through 3 non-collinear arbitrary points is returned.
|
||||
// Arguments:
|
||||
|
@ -858,8 +858,8 @@ function plane_from_points(points, fast=false, eps=EPSILON) =
|
|||
// Usage:
|
||||
// plane_from_polygon(points, [fast], [eps]);
|
||||
// Description:
|
||||
// Given a 3D planar polygon, returns the cartesian equation of its plane.
|
||||
// Returns [A,B,C,D] where Ax+By+Cz=D is the equation of the plane.
|
||||
// Given a 3D planar polygon, returns the normalized cartesian equation of its plane.
|
||||
// Returns [A,B,C,D] where Ax+By+Cz=D is the equation of the plane where norm([A,B,C])=1.
|
||||
// If not all the points in the polygon are coplanar, then [] is returned.
|
||||
// If `fast` is true, the polygon coplanarity check is skipped and the plane may not contain all polygon points.
|
||||
// Arguments:
|
||||
|
@ -897,8 +897,9 @@ function plane_normal(plane) =
|
|||
// Usage:
|
||||
// d = plane_offset(plane);
|
||||
// Description:
|
||||
// Returns D, or the scalar offset of the plane from the origin. This can be a negative value.
|
||||
// The absolute value of this is the distance of the plane from the origin at its closest approach.
|
||||
// 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.
|
||||
// The absolute value of this coefficient is the distance of the plane from the origin.
|
||||
function plane_offset(plane) =
|
||||
assert( _valid_plane(plane), "Invalid input plane." )
|
||||
plane[3]/norm([plane.x, plane.y, plane.z]);
|
||||
|
@ -923,7 +924,8 @@ function plane_offset(plane) =
|
|||
// stroke(xypath,closed=true);
|
||||
function plane_transform(plane) =
|
||||
let(
|
||||
n = plane_normal(plane),
|
||||
plane = normalize_plane(plane),
|
||||
n = point3d(plane),
|
||||
cp = n * plane[3]
|
||||
)
|
||||
rot(from=n, to=UP) * move(-cp);
|
||||
|
@ -949,8 +951,8 @@ function projection_on_plane(plane, points) =
|
|||
p = len(points[0])==2
|
||||
? [for(pi=points) point3d(pi) ]
|
||||
: points,
|
||||
plane = plane/norm([plane.x,plane.y,plane.z]),
|
||||
n = [plane.x,plane.y,plane.z]
|
||||
plane = normalize_plane(plane),
|
||||
n = point3d(plane)
|
||||
)
|
||||
[for(pi=p) pi - (pi*n - plane[3])*n];
|
||||
|
||||
|
@ -961,7 +963,8 @@ function projection_on_plane(plane, points) =
|
|||
// Description:
|
||||
// Returns the point on the plane that is closest to the origin.
|
||||
function plane_point_nearest_origin(plane) =
|
||||
plane_normal(plane) * plane[3];
|
||||
let( plane = normalize_plane(plane) )
|
||||
point3d(plane) * plane[3];
|
||||
|
||||
|
||||
// Function: distance_from_plane()
|
||||
|
@ -980,8 +983,8 @@ function plane_point_nearest_origin(plane) =
|
|||
function distance_from_plane(plane, point) =
|
||||
assert( _valid_plane(plane), "Invalid input plane." )
|
||||
assert( is_vector(point,3), "The point should be a 3D point." )
|
||||
let( nrml = [plane.x, plane.y, plane.z] )
|
||||
( nrml* point - plane[3])/norm(nrml);
|
||||
let( plane = normalize_plane(plane) )
|
||||
point3d(plane)* point - plane[3];
|
||||
|
||||
|
||||
// Function: closest_point_on_plane()
|
||||
|
@ -996,9 +999,9 @@ function distance_from_plane(plane, point) =
|
|||
function closest_point_on_plane(plane, point) =
|
||||
assert( _valid_plane(plane), "Invalid input plane." )
|
||||
assert( is_vector(point,3), "Invalid point." )
|
||||
let(
|
||||
n = unit([plane.x, plane.y, plane.z]),
|
||||
d = distance_from_plane(plane, point)
|
||||
let( plane = normalize_plane(plane),
|
||||
n = point3d(plane),
|
||||
d = n*point - plane[3] // distance from plane
|
||||
)
|
||||
point - n*d;
|
||||
|
||||
|
@ -1008,23 +1011,29 @@ function closest_point_on_plane(plane, point) =
|
|||
// Returns undef if line is parallel to, but not on the given plane.
|
||||
function _general_plane_line_intersection(plane, line, eps=EPSILON) =
|
||||
let(
|
||||
l0 = line[0], // Ray start point
|
||||
u = line[1] - l0, // Ray direction vector
|
||||
n = plane_normal(plane),
|
||||
p0 = n * plane[3], // A point on the plane
|
||||
w = l0 - p0 // Vector from plane point to ray start
|
||||
) approx(n*u, 0, eps=eps) ? (
|
||||
// Line is parallel to plane.
|
||||
approx(n*w, 0, eps=eps)
|
||||
? [line, undef] // Line is on the plane.
|
||||
: undef // Line never intersects the plane.
|
||||
) : let(
|
||||
t = (-n * w) / (n * u) // Distance ratio along ray
|
||||
) [ l0 + u*t, t ];
|
||||
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]
|
||||
)
|
||||
approx(b,0,eps) // is (line[1]-line[0]) "parallel" to the plane ?
|
||||
? approx(a,0,eps) // is line[0] on the plane ?
|
||||
? [line,undef] // line is on the plane
|
||||
: undef // line is parallel but not on the plane
|
||||
: [ line[0]-a/b*(line[1]-line[0]), -a/b ];
|
||||
|
||||
|
||||
// Function: normalize_plane()
|
||||
// Usage:
|
||||
// nplane = normalize_plane(plane);
|
||||
// Description:
|
||||
// Returns a new representation [A,B,C,D] of `plane` where norm([A,B,C]) is equal to one.
|
||||
function normalize_plane(plane) =
|
||||
assert( _valid_plane(plane), "Invalid plane." )
|
||||
plane/norm(point3d(plane));
|
||||
|
||||
|
||||
// Function: plane_line_angle()
|
||||
// Usage: plane_line_angle(plane,line)
|
||||
// Usage:
|
||||
// angle = plane_line_angle(plane,line);
|
||||
// Description:
|
||||
// Compute the angle between a plane [A, B, C, D] and a line, specified as a pair of points [p1,p2].
|
||||
// The resulting angle is signed, with the sign positive if the vector p2-p1 lies on
|
||||
|
@ -1033,11 +1042,12 @@ function plane_line_angle(plane, line) =
|
|||
assert( _valid_plane(plane), "Invalid plane." )
|
||||
assert( _valid_line(line), "Invalid line." )
|
||||
let(
|
||||
vect = line[1]-line[0],
|
||||
zplane = plane_normal(plane),
|
||||
sin_angle = vect*zplane/norm(zplane)/norm(vect)
|
||||
linedir = unit(line[1]-line[0]),
|
||||
normal = plane_normal(plane),
|
||||
sin_angle = linedir*normal,
|
||||
cos_angle = norm(cross(linedir,normal))
|
||||
)
|
||||
asin(constrain(sin_angle,-1,1));
|
||||
atan2(sin_angle,cos_angle);
|
||||
|
||||
|
||||
// Function: plane_line_intersection()
|
||||
|
@ -1085,7 +1095,7 @@ function plane_line_intersection(plane, 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_path(poly,dim=3), "Invalid polygon." )
|
||||
assert(is_bool(bounded) || (is_list(bounded) && len(bounded)==2), "Invalid bound condition(s).")
|
||||
assert(!is_list(bounded) || len(bounded)==2, "Invalid bound condition(s).")
|
||||
assert(_valid_line(line,dim=3,eps=eps), "Invalid line." )
|
||||
let(
|
||||
bounded = is_list(bounded)? bounded : [bounded, bounded],
|
||||
|
@ -1094,7 +1104,6 @@ function polygon_line_intersection(poly, line, bounded=false, eps=EPSILON) =
|
|||
)
|
||||
indices==[] ? undef :
|
||||
let(
|
||||
indices = sort(indices),
|
||||
p1 = poly[indices[0]],
|
||||
p2 = poly[indices[1]],
|
||||
p3 = poly[indices[2]],
|
||||
|
@ -1120,8 +1129,8 @@ function polygon_line_intersection(poly, line, bounded=false, eps=EPSILON) =
|
|||
)
|
||||
isegs
|
||||
)
|
||||
: bounded[0]&&res[1]<0? [] :
|
||||
bounded[1]&&res[1]>1? [] :
|
||||
: bounded[0] && res[1]<0? undef :
|
||||
bounded[1] && res[1]>1? undef :
|
||||
let(
|
||||
proj = clockwise_polygon(project_plane(poly, p1, p2, p3)),
|
||||
pt = project_plane(res[0], p1, p2, p3)
|
||||
|
@ -1142,15 +1151,15 @@ function plane_intersection(plane1,plane2,plane3) =
|
|||
"The input must be 2 or 3 planes." )
|
||||
is_def(plane3)
|
||||
? let(
|
||||
matrix = [for(p=[plane1,plane2,plane3]) select(p,0,2)],
|
||||
matrix = [for(p=[plane1,plane2,plane3]) point3d(p)],
|
||||
rhs = [for(p=[plane1,plane2,plane3]) p[3]]
|
||||
)
|
||||
linear_solve(matrix,rhs)
|
||||
: let( normal = cross(plane_normal(plane1), plane_normal(plane2)) )
|
||||
approx(norm(normal),0) ? undef :
|
||||
let(
|
||||
matrix = [for(p=[plane1,plane2]) select(p,0,2)],
|
||||
rhs = [for(p=[plane1,plane2]) p[3]],
|
||||
matrix = [for(p=[plane1,plane2]) point3d(p)],
|
||||
rhs = [plane1[3], plane2[3]],
|
||||
point = linear_solve(matrix,rhs)
|
||||
)
|
||||
point==[]? undef: [point, point+normal];
|
||||
|
|
|
@ -517,8 +517,10 @@ module dovetail(gender, length, l, width, w, height, h, angle, slope, taper, bac
|
|||
assert(count3<=1 || (radius==0 && chamfer==0), "Do not specify both chamfer and radius");
|
||||
slope = is_def(slope) ? slope :
|
||||
is_def(angle) ? 1/tan(angle) : 6;
|
||||
width = gender == "male" ? w : w + 2*$slop;
|
||||
height = h + (gender == "female" ? 2*$slop : 0);
|
||||
extra_slop = gender == "female" ? 2*$slop : 0;
|
||||
width = w + extra_slop;
|
||||
height = h + extra_slop;
|
||||
back_width = back_width + extra_slop;
|
||||
|
||||
front_offset = is_def(taper) ? -extra * tan(taper) :
|
||||
is_def(back_width) ? extra * (back_width-width)/length/2 : 0;
|
||||
|
|
152
math.scad
152
math.scad
|
@ -712,6 +712,7 @@ function matrix_inverse(A) =
|
|||
assert(is_matrix(A,square=true),"Input to matrix_inverse() must be a square matrix")
|
||||
linear_solve(A,ident(len(A)));
|
||||
|
||||
|
||||
// Function: null_space()
|
||||
// Usage:
|
||||
// null_space(A)
|
||||
|
@ -723,7 +724,7 @@ function null_space(A,eps=1e-12) =
|
|||
let(
|
||||
Q_R=qr_factor(transpose(A),pivot=true),
|
||||
R=Q_R[1],
|
||||
zrow = [for(i=idx(R)) if (is_zero(R[i],eps)) i]
|
||||
zrow = [for(i=idx(R)) if (all_zero(R[i],eps)) i]
|
||||
)
|
||||
len(zrow)==0
|
||||
? []
|
||||
|
@ -894,125 +895,148 @@ function is_matrix(A,m,n,square=false) =
|
|||
// squares of all of the entries of the matrix. On vectors it is the same as the usual 2-norm.
|
||||
// This is an easily computed norm that is convenient for comparing two matrices.
|
||||
function norm_fro(A) =
|
||||
sqrt(sum([for(entry=A) sum_of_squares(entry)]));
|
||||
assert(is_matrix(A) || is_vector(A))
|
||||
norm(flatten(A));
|
||||
|
||||
|
||||
// Section: Comparisons and Logic
|
||||
|
||||
// Function: is_zero()
|
||||
// Function: all_zero()
|
||||
// Usage:
|
||||
// is_zero(x);
|
||||
// all_zero(x);
|
||||
// Description:
|
||||
// Returns true if the number passed to it is approximately zero, to within `eps`.
|
||||
// Returns true if the finite number passed to it is approximately zero, to within `eps`.
|
||||
// If passed a list, recursively checks if all items in the list are approximately zero.
|
||||
// Otherwise, returns false.
|
||||
// Arguments:
|
||||
// x = The value to check.
|
||||
// eps = The maximum allowed variance. Default: `EPSILON` (1e-9)
|
||||
// Example:
|
||||
// is_zero(0); // Returns: true.
|
||||
// is_zero(1e-3); // Returns: false.
|
||||
// is_zero([0,0,0]); // Returns: true.
|
||||
// is_zero([0,0,1e-3]); // Returns: false.
|
||||
function is_zero(x, eps=EPSILON) =
|
||||
is_list(x)? (x != [] && [for (xx=x) if(!is_zero(xx,eps=eps)) 1] == []) :
|
||||
is_num(x)? approx(x,eps) :
|
||||
// all_zero(0); // Returns: true.
|
||||
// all_zero(1e-3); // Returns: false.
|
||||
// all_zero([0,0,0]); // Returns: true.
|
||||
// all_zero([0,0,1e-3]); // Returns: false.
|
||||
function all_zero(x, eps=EPSILON) =
|
||||
is_finite(x)? approx(x,eps) :
|
||||
is_list(x)? (x != [] && [for (xx=x) if(!all_zero(xx,eps=eps)) 1] == []) :
|
||||
false;
|
||||
|
||||
|
||||
// Function: is_positive()
|
||||
// Function: all_nonzero()
|
||||
// Usage:
|
||||
// is_positive(x);
|
||||
// all_nonzero(x);
|
||||
// Description:
|
||||
// Returns true if the number passed to it is greater than zero.
|
||||
// Returns true if the finite number passed to it is not almost zero, to within `eps`.
|
||||
// If passed a list, recursively checks if all items in the list are not almost zero.
|
||||
// Otherwise, returns false.
|
||||
// Arguments:
|
||||
// x = The value to check.
|
||||
// eps = The maximum allowed variance. Default: `EPSILON` (1e-9)
|
||||
// Example:
|
||||
// all_nonzero(0); // Returns: false.
|
||||
// all_nonzero(1e-3); // Returns: true.
|
||||
// all_nonzero([0,0,0]); // Returns: false.
|
||||
// all_nonzero([0,0,1e-3]); // Returns: false.
|
||||
// all_nonzero([1e-3,1e-3,1e-3]); // Returns: true.
|
||||
function all_nonzero(x, eps=EPSILON) =
|
||||
is_finite(x)? !approx(x,eps) :
|
||||
is_list(x)? (x != [] && [for (xx=x) if(!all_nonzero(xx,eps=eps)) 1] == []) :
|
||||
false;
|
||||
|
||||
|
||||
// Function: all_positive()
|
||||
// Usage:
|
||||
// all_positive(x);
|
||||
// Description:
|
||||
// Returns true if the finite number passed to it is greater than zero.
|
||||
// If passed a list, recursively checks if all items in the list are positive.
|
||||
// Otherwise, returns false.
|
||||
// Arguments:
|
||||
// x = The value to check.
|
||||
// Example:
|
||||
// is_positive(-2); // Returns: false.
|
||||
// is_positive(0); // Returns: false.
|
||||
// is_positive(2); // Returns: true.
|
||||
// is_positive([0,0,0]); // Returns: false.
|
||||
// is_positive([0,1,2]); // Returns: false.
|
||||
// is_positive([3,1,2]); // Returns: true.
|
||||
// is_positive([3,-1,2]); // Returns: false.
|
||||
function is_positive(x) =
|
||||
is_list(x)? (x != [] && [for (xx=x) if(!is_positive(xx)) 1] == []) :
|
||||
// all_positive(-2); // Returns: false.
|
||||
// all_positive(0); // Returns: false.
|
||||
// all_positive(2); // Returns: true.
|
||||
// all_positive([0,0,0]); // Returns: false.
|
||||
// all_positive([0,1,2]); // Returns: false.
|
||||
// all_positive([3,1,2]); // Returns: true.
|
||||
// all_positive([3,-1,2]); // Returns: false.
|
||||
function all_positive(x) =
|
||||
is_num(x)? x>0 :
|
||||
is_list(x)? (x != [] && [for (xx=x) if(!all_positive(xx)) 1] == []) :
|
||||
false;
|
||||
|
||||
|
||||
// Function: is_negative()
|
||||
// Function: all_negative()
|
||||
// Usage:
|
||||
// is_negative(x);
|
||||
// all_negative(x);
|
||||
// Description:
|
||||
// Returns true if the number passed to it is less than zero.
|
||||
// Returns true if the finite number passed to it is less than zero.
|
||||
// If passed a list, recursively checks if all items in the list are negative.
|
||||
// Otherwise, returns false.
|
||||
// Arguments:
|
||||
// x = The value to check.
|
||||
// Example:
|
||||
// is_negative(-2); // Returns: true.
|
||||
// is_negative(0); // Returns: false.
|
||||
// is_negative(2); // Returns: false.
|
||||
// is_negative([0,0,0]); // Returns: false.
|
||||
// is_negative([0,1,2]); // Returns: false.
|
||||
// is_negative([3,1,2]); // Returns: false.
|
||||
// is_negative([3,-1,2]); // Returns: false.
|
||||
// is_negative([-3,-1,-2]); // Returns: true.
|
||||
function is_negative(x) =
|
||||
is_list(x)? (x != [] && [for (xx=x) if(!is_negative(xx)) 1] == []) :
|
||||
// all_negative(-2); // Returns: true.
|
||||
// all_negative(0); // Returns: false.
|
||||
// all_negative(2); // Returns: false.
|
||||
// all_negative([0,0,0]); // Returns: false.
|
||||
// all_negative([0,1,2]); // Returns: false.
|
||||
// all_negative([3,1,2]); // Returns: false.
|
||||
// all_negative([3,-1,2]); // Returns: false.
|
||||
// all_negative([-3,-1,-2]); // Returns: true.
|
||||
function all_negative(x) =
|
||||
is_num(x)? x<0 :
|
||||
is_list(x)? (x != [] && [for (xx=x) if(!all_negative(xx)) 1] == []) :
|
||||
false;
|
||||
|
||||
|
||||
// Function: is_nonpositive()
|
||||
// Function: all_nonpositive()
|
||||
// Usage:
|
||||
// is_nonpositive(x);
|
||||
// all_nonpositive(x);
|
||||
// Description:
|
||||
// Returns true if the number passed to it is less than or equal to zero.
|
||||
// Returns true if the finite number passed to it is less than or equal to zero.
|
||||
// If passed a list, recursively checks if all items in the list are nonpositive.
|
||||
// Otherwise, returns false.
|
||||
// Arguments:
|
||||
// x = The value to check.
|
||||
// Example:
|
||||
// is_nonpositive(-2); // Returns: true.
|
||||
// is_nonpositive(0); // Returns: true.
|
||||
// is_nonpositive(2); // Returns: false.
|
||||
// is_nonpositive([0,0,0]); // Returns: true.
|
||||
// is_nonpositive([0,1,2]); // Returns: false.
|
||||
// is_nonpositive([3,1,2]); // Returns: false.
|
||||
// is_nonpositive([3,-1,2]); // Returns: false.
|
||||
// is_nonpositive([-3,-1,-2]); // Returns: true.
|
||||
function is_nonpositive(x) =
|
||||
is_list(x)? (x != [] && [for (xx=x) if(!is_nonpositive(xx)) 1] == []) :
|
||||
// all_nonpositive(-2); // Returns: true.
|
||||
// all_nonpositive(0); // Returns: true.
|
||||
// all_nonpositive(2); // Returns: false.
|
||||
// all_nonpositive([0,0,0]); // Returns: true.
|
||||
// all_nonpositive([0,1,2]); // Returns: false.
|
||||
// all_nonpositive([3,1,2]); // Returns: false.
|
||||
// all_nonpositive([3,-1,2]); // Returns: false.
|
||||
// all_nonpositive([-3,-1,-2]); // Returns: true.
|
||||
function all_nonpositive(x) =
|
||||
is_num(x)? x<=0 :
|
||||
is_list(x)? (x != [] && [for (xx=x) if(!all_nonpositive(xx)) 1] == []) :
|
||||
false;
|
||||
|
||||
|
||||
// Function: is_nonnegative()
|
||||
// Function: all_nonnegative()
|
||||
// Usage:
|
||||
// is_nonnegative(x);
|
||||
// all_nonnegative(x);
|
||||
// Description:
|
||||
// Returns true if the number passed to it is greater than or equal to zero.
|
||||
// Returns true if the finite number passed to it is greater than or equal to zero.
|
||||
// If passed a list, recursively checks if all items in the list are nonnegative.
|
||||
// Otherwise, returns false.
|
||||
// Arguments:
|
||||
// x = The value to check.
|
||||
// Example:
|
||||
// is_nonnegative(-2); // Returns: false.
|
||||
// is_nonnegative(0); // Returns: true.
|
||||
// is_nonnegative(2); // Returns: true.
|
||||
// is_nonnegative([0,0,0]); // Returns: true.
|
||||
// is_nonnegative([0,1,2]); // Returns: true.
|
||||
// is_nonnegative([0,-1,-2]); // Returns: false.
|
||||
// is_nonnegative([3,1,2]); // Returns: true.
|
||||
// is_nonnegative([3,-1,2]); // Returns: false.
|
||||
// is_nonnegative([-3,-1,-2]); // Returns: false.
|
||||
function is_nonnegative(x) =
|
||||
is_list(x)? (x != [] && [for (xx=x) if(!is_nonnegative(xx)) 1] == []) :
|
||||
// all_nonnegative(-2); // Returns: false.
|
||||
// all_nonnegative(0); // Returns: true.
|
||||
// all_nonnegative(2); // Returns: true.
|
||||
// all_nonnegative([0,0,0]); // Returns: true.
|
||||
// all_nonnegative([0,1,2]); // Returns: true.
|
||||
// all_nonnegative([0,-1,-2]); // Returns: false.
|
||||
// all_nonnegative([3,1,2]); // Returns: true.
|
||||
// all_nonnegative([3,-1,2]); // Returns: false.
|
||||
// all_nonnegative([-3,-1,-2]); // Returns: false.
|
||||
function all_nonnegative(x) =
|
||||
is_num(x)? x>=0 :
|
||||
is_list(x)? (x != [] && [for (xx=x) if(!all_nonnegative(xx)) 1] == []) :
|
||||
false;
|
||||
|
||||
|
||||
|
|
|
@ -9,9 +9,61 @@
|
|||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Section: Halving Mutators
|
||||
// Section: Volume Division Mutators
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Module: bounding_box()
|
||||
// Usage:
|
||||
// bounding_box() ...
|
||||
// Description:
|
||||
// Returns an axis-aligned cube shape that exactly contains all the 3D children given.
|
||||
// Arguments:
|
||||
// excess = The amount that the bounding box should be larger than needed to bound the children, in each axis.
|
||||
// Example:
|
||||
// #bounding_box() {
|
||||
// translate([10,8,4]) cube(5);
|
||||
// translate([3,0,12]) cube(2);
|
||||
// }
|
||||
// translate([10,8,4]) cube(5);
|
||||
// translate([3,0,12]) cube(2);
|
||||
module bounding_box(excess=0) {
|
||||
xs = excess>0? excess : 1;
|
||||
// a 3D approx. of the children projection on X axis
|
||||
module _xProjection()
|
||||
linear_extrude(xs, center=true)
|
||||
hull()
|
||||
projection()
|
||||
rotate([90,0,0])
|
||||
linear_extrude(xs, center=true)
|
||||
projection()
|
||||
children();
|
||||
|
||||
// a bounding box with an offset of 1 in all axis
|
||||
module _oversize_bbox() {
|
||||
minkowski() {
|
||||
_xProjection() children(); // x axis
|
||||
rotate(-90) _xProjection() rotate(90) children(); // y axis
|
||||
rotate([0,-90,0]) _xProjection() rotate([0,90,0]) children(); // z axis
|
||||
}
|
||||
}
|
||||
|
||||
// offset children() (a cube) by -1 in all axis
|
||||
module _shrink_cube() {
|
||||
intersection() {
|
||||
translate([ 1, 1, 1]) children();
|
||||
translate([-1,-1,-1]) children();
|
||||
}
|
||||
}
|
||||
|
||||
render(convexity=2)
|
||||
if (excess>0) {
|
||||
_oversize_bbox() children();
|
||||
} else {
|
||||
_shrink_cube() _oversize_bbox() children();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Module: half_of()
|
||||
//
|
||||
// Usage:
|
||||
|
@ -445,7 +497,6 @@ module offset3d(r=1, size=100, convexity=10) {
|
|||
}
|
||||
|
||||
|
||||
|
||||
// Module: round2d()
|
||||
// Usage:
|
||||
// round2d(r) ...
|
||||
|
@ -512,6 +563,39 @@ module shell2d(thickness, or=0, ir=0, fill=0, round=0)
|
|||
}
|
||||
|
||||
|
||||
// Module: minkowski_difference()
|
||||
// Usage:
|
||||
// minkowski_difference() { base_shape(); diff_shape(); ... }
|
||||
// Description:
|
||||
// Takes a 3D base shape and one or more 3D diff shapes, carves out the diff shapes from the
|
||||
// surface of the base shape, in a way complementary to how `minkowski()` unions shapes to the
|
||||
// surface of its base shape.
|
||||
// Example:
|
||||
// minkowski_difference() {
|
||||
// union() {
|
||||
// cube([120,70,70], center=true);
|
||||
// cube([70,120,70], center=true);
|
||||
// cube([70,70,120], center=true);
|
||||
// }
|
||||
// sphere(r=10);
|
||||
// }
|
||||
module minkowski_difference() {
|
||||
difference() {
|
||||
bounding_box(excess=0) children(0);
|
||||
render(convexity=10) {
|
||||
minkowski() {
|
||||
difference() {
|
||||
bounding_box(excess=1) children(0);
|
||||
children(0);
|
||||
}
|
||||
for (i=[1,1,$children-1]) children(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Section: Colors
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -180,8 +180,9 @@ function split_path_at_region_crossings(path, region, closed=true, eps=EPSILON)
|
|||
),
|
||||
subpaths = [
|
||||
for (p = pair(crossings))
|
||||
deduplicate(eps=eps,
|
||||
path_subselect(path, p[0][0], p[0][1], p[1][0], p[1][1], closed=closed)
|
||||
deduplicate(
|
||||
path_subselect(path, p[0][0], p[0][1], p[1][0], p[1][1], closed=closed),
|
||||
eps=eps
|
||||
)
|
||||
]
|
||||
)
|
||||
|
|
|
@ -4,6 +4,8 @@ include <../std.scad>
|
|||
//the commented lines are for tests to be written
|
||||
//the tests are ordered as they appear in geometry.scad
|
||||
|
||||
|
||||
|
||||
test_point_on_segment2d();
|
||||
test_point_left_of_line2d();
|
||||
test_collinear();
|
||||
|
@ -35,33 +37,30 @@ test_tri_calc();
|
|||
test_triangle_area();
|
||||
test_plane3pt();
|
||||
test_plane3pt_indexed();
|
||||
//test_plane_from_normal();
|
||||
test_plane_from_normal();
|
||||
test_plane_from_points();
|
||||
//test_plane_from_polygon();
|
||||
test_plane_from_polygon();
|
||||
test_plane_normal();
|
||||
//test_plane_offset();
|
||||
//test_plane_transform();
|
||||
test_plane_offset();
|
||||
test_plane_transform();
|
||||
test_projection_on_plane();
|
||||
//test_plane_point_nearest_origin();
|
||||
test_plane_point_nearest_origin();
|
||||
test_distance_from_plane();
|
||||
|
||||
test_find_circle_2tangents();
|
||||
test_find_circle_3points();
|
||||
test_circle_point_tangents();
|
||||
test_tri_functions();
|
||||
//test_closest_point_on_plane();
|
||||
//test__general_plane_line_intersection();
|
||||
//test_plane_line_angle();
|
||||
//test_plane_line_intersection();
|
||||
test_closest_point_on_plane();
|
||||
test__general_plane_line_intersection();
|
||||
test_plane_line_angle();
|
||||
test_normalize_plane();
|
||||
test_plane_line_intersection();
|
||||
test_polygon_line_intersection();
|
||||
//test_plane_intersection();
|
||||
test_plane_intersection();
|
||||
test_coplanar();
|
||||
test_points_on_plane();
|
||||
test_in_front_of_plane();
|
||||
//test_find_circle_2tangents();
|
||||
//test_find_circle_3points();
|
||||
//test_circle_point_tangents();
|
||||
//test_circle_circle_tangents();
|
||||
test_find_circle_2tangents();
|
||||
test_find_circle_3points();
|
||||
test_circle_point_tangents();
|
||||
|
||||
test_noncollinear_triple();
|
||||
test_pointlist_bounds();
|
||||
test_closest_point();
|
||||
|
@ -92,24 +91,204 @@ test_simplify_path();
|
|||
test_simplify_path_indexed();
|
||||
test_is_region();
|
||||
|
||||
|
||||
// to be used when there are two alternative symmetrical outcomes
|
||||
// from a function like a plane output.
|
||||
// from a function like a plane output; v must be a vector
|
||||
function standardize(v) =
|
||||
v==[]? [] :
|
||||
sign([for(vi=v) if( ! approx(vi,0)) vi,0 ][0])*v;
|
||||
let( i = max_index([for(vi=v) abs(vi) ]),
|
||||
s = sign(v[i]) )
|
||||
v*s;
|
||||
|
||||
|
||||
module assert_std(vc,ve,info) { assert_approx(standardize(vc),standardize(ve),info); }
|
||||
|
||||
|
||||
function info_str(list,i=0,string=chr(10)) =
|
||||
assert(i>=len(list) || (is_list(list[i])&&len(list[i])>=2), "Invalid list for info_str." )
|
||||
i>=len(list)
|
||||
? str(string)
|
||||
: info_str(list,i+1,str(string,str(list[i][0],_valstr(list[i][1]),chr(10))));
|
||||
|
||||
|
||||
module test_closest_point_on_plane(){
|
||||
plane = rands(-5,5,4)+[10,0,0,0];
|
||||
point = rands(-1,1,3);
|
||||
point2 = closest_point_on_plane(plane,point);
|
||||
assert_approx(norm(point-point2), abs(distance_from_plane(plane,point)));
|
||||
}
|
||||
*test_closest_point_on_plane();
|
||||
|
||||
|
||||
module test_normalize_plane(){
|
||||
plane = rands(-5,5,4)+[10,0,0,0];
|
||||
plane2 = normalize_plane(plane);
|
||||
assert_approx(norm(point3d(plane2)),1);
|
||||
assert_approx(plane*plane2[3],plane2*plane[3]);
|
||||
}
|
||||
*test_normalize_plane();
|
||||
|
||||
module test_plane_line_intersection(){
|
||||
line = [rands(-1,1,3),rands(-1,1,3)+[2,0,0]];
|
||||
plane1 = plane_from_normal(line[1]-line[0],2*line[0]-line[1]); // plane disjoint from segment
|
||||
plane2 = plane_from_normal(line[1]-line[0],(line[0]+line[1])/2); // through middle point of line
|
||||
plane3 = plane3pt(line[1],line[0], rands(-1,1,3)+[0,3,0]); // containing line
|
||||
plane4 = plane3pt(line[1],line[0], rands(-1,1,3)+[0,3,0])+[0,0,0,1]; // parallel to line
|
||||
info1 = info_str([ ["line = ",line],["plane = ",plane1]]);
|
||||
assert_approx(plane_line_intersection(plane1, line),2*line[0]-line[1],info1);
|
||||
assert_approx(plane_line_intersection(plane1, line,[true,false]),undef,info1);
|
||||
assert_approx(plane_line_intersection(plane1, line,[false,true]),2*line[0]-line[1],info1);
|
||||
assert_approx(plane_line_intersection(plane1, line,[true, true]),undef,info1);
|
||||
info2 = info_str([ ["line = ",line],["plane = ",plane2]]);
|
||||
assert_approx(plane_line_intersection(plane2, line),(line[0]+line[1])/2,info2);
|
||||
assert_approx(plane_line_intersection(plane2, line,[true,false]),(line[0]+line[1])/2,info2);
|
||||
assert_approx(plane_line_intersection(plane2, line,[false,true]),(line[0]+line[1])/2,info2);
|
||||
assert_approx(plane_line_intersection(plane2, line,[true, true]),(line[0]+line[1])/2,info2);
|
||||
info3 = info_str([ ["line = ",line],["plane = ",plane3]]);
|
||||
assert_approx(plane_line_intersection(plane3, line),line,info3);
|
||||
assert_approx(plane_line_intersection(plane3, line,[true,false]),line,info3);
|
||||
assert_approx(plane_line_intersection(plane3, line,[false,true]),line,info3);
|
||||
assert_approx(plane_line_intersection(plane3, line,[true, true]),line,info3);
|
||||
info4 = info_str([ ["line = ",line],["plane = ",plane4]]);
|
||||
assert_approx(plane_line_intersection(plane4, line),undef,info4);
|
||||
assert_approx(plane_line_intersection(plane4, line,[true,false]),undef,info4);
|
||||
assert_approx(plane_line_intersection(plane4, line,[false,true]),undef,info4);
|
||||
assert_approx(plane_line_intersection(plane4, line,[true, true]),undef,info4);
|
||||
}
|
||||
*test_plane_line_intersection();
|
||||
|
||||
|
||||
module test_plane_intersection(){
|
||||
line = [ rands(-1,1,3), rands(-1,1,3)+[2,0,0] ]; // a valid line
|
||||
pt0 = line[0]-[2,0,0]; // 2 points not on the line
|
||||
pt1 = line[1]-[0,2,0];
|
||||
plane01 = plane3pt(line[0],line[1],pt0);
|
||||
plane02 = plane3pt(line[0],line[1],pt1);
|
||||
plane03 = plane3pt(line[0],pt0,pt1);
|
||||
info = info_str([["plane1 = ",plane01],["plane2 = ",plane02],["plane3 = ",plane03]]);
|
||||
assert_approx(plane_intersection(plane01,plane02,plane03),line[0],info);
|
||||
assert_approx(plane_intersection(plane01,2*plane01),undef,info);
|
||||
lineInters = plane_intersection(plane01,plane02);
|
||||
assert_approx(line_closest_point(lineInters,line[0]), line[0], info);
|
||||
assert_approx(line_closest_point(lineInters,line[1]), line[1], info);
|
||||
}
|
||||
*test_plane_intersection();
|
||||
|
||||
|
||||
module test_plane_point_nearest_origin(){
|
||||
point = rands(-1,1,3)+[2,0,0]; // a non zero vector
|
||||
plane = [ each point, point*point]; // a plane containing `point`
|
||||
info = info_str([["point = ",point],["plane = ",plane]]);
|
||||
assert_approx(plane_point_nearest_origin(plane),point,info);
|
||||
assert_approx(plane_point_nearest_origin([each point,5]),5*unit(point)/norm(point),info);
|
||||
}
|
||||
test_plane_point_nearest_origin();
|
||||
|
||||
|
||||
module test_plane_transform(){
|
||||
normal = rands(-1,1,3)+[2,0,0];
|
||||
offset = rands(-1,1,1)[0];
|
||||
info = info_str([["normal = ",normal],["offset = ",offset]]);
|
||||
assert_approx(plane_transform([0,0,1,offset]),move([0,0,-offset]),info );
|
||||
assert_approx(plane_transform([0,1,0,offset]),xrot(90)*move([0,-offset,0]),info );
|
||||
}
|
||||
*test_plane_transform();
|
||||
|
||||
|
||||
module test_plane_offset(){
|
||||
plane = rands(-1,1,4)+[2,0,0,0]; // a valid plane
|
||||
info = info_str([["plane = ",plane]]);
|
||||
assert_approx(plane_offset(plane), normalize_plane(plane)[3],info);
|
||||
assert_approx(plane_offset([1,1,1,1]), 1/sqrt(3),info);
|
||||
}
|
||||
*test_plane_offset();
|
||||
|
||||
module test_plane_from_polygon(){
|
||||
poly1 = [ rands(-1,1,3), rands(-1,1,3)+[2,0,0], rands(-1,1,3)+[0,2,2] ];
|
||||
poly2 = concat(poly1, [sum(poly1)/3] );
|
||||
info = info_str([["poly1 = ",poly1],["poly2 = ",poly2]]);
|
||||
assert_std(plane_from_polygon(poly1),plane3pt(poly1[0],poly1[1],poly1[2]),info);
|
||||
assert_std(plane_from_polygon(poly2),plane3pt(poly1[0],poly1[1],poly1[2]),info);
|
||||
}
|
||||
*test_plane_from_polygon();
|
||||
|
||||
module test_plane_from_normal(){
|
||||
normal = rands(-1,1,3)+[2,0,0];
|
||||
point = rands(-1,1,3);
|
||||
displ = normal*point;
|
||||
info = info_str([["normal = ",normal],["point = ",point],["displ = ",displ]]);
|
||||
assert_approx(plane_from_normal(normal,point)*[each point,-1],0,info);
|
||||
assert_std(plane_from_normal(normal,point),normalize_plane([each normal,displ]),info);
|
||||
assert_std(plane_from_normal([1,1,1],[1,2,3]),[0.57735026919,0.57735026919,0.57735026919,3.46410161514]);
|
||||
}
|
||||
*test_plane_from_normal();
|
||||
|
||||
module test_plane_line_angle() {
|
||||
angs = rands(0,360,3);
|
||||
displ = rands(-1,1,1)[0];
|
||||
info = info_str([["angs = ",angs],["displ = ",displ]]);
|
||||
assert_approx(plane_line_angle([each rot(angs,p=[0,0,1]),displ],[[0,0,0],rot(angs,p=[0,0,1])]),90,info);
|
||||
assert_approx(plane_line_angle([each rot(angs,p=[0,0,1]),displ],[[0,0,0],rot(angs,p=[0,1,1])]),45,info);
|
||||
assert_approx(plane_line_angle([each rot(angs,p=[0,0,1]),0],[[0,0,0],rot(angs,p=[1,1,1])]),35.2643896828);
|
||||
}
|
||||
*test_plane_line_angle();
|
||||
|
||||
module test__general_plane_line_intersection() {
|
||||
CRLF = chr(10);
|
||||
// general line
|
||||
plane1 = rands(-1,1,4)+[2,0,0,0]; // a random valid plane (normal!=0)
|
||||
line1 = [ rands(-1,1,3), rands(-1,1,3)+[2,0,0] ]; // a random valid line (line1[0]!=line1[1])
|
||||
inters1 = _general_plane_line_intersection(plane1, line1);
|
||||
info1 = info_str([["line = ",line1],["plane = ",plane1]]);
|
||||
if(inters1==undef) { // parallel to the plane ?
|
||||
assert_approx( point3d(plane1)*(line1[1]-line1[0]), 0, info1);
|
||||
assert( point3d(plane1)*line1[0]== plane1[3], info1); // not on the plane
|
||||
}
|
||||
if( inters1[1]==undef) { // on the plane ?
|
||||
assert_approx( point3d(plane1)*(line1[1]-line1[0]), 0, info1);
|
||||
assert_approx(point3d(plane1)*line1[0],plane1[3], info1) ; // on the plane
|
||||
}
|
||||
else {
|
||||
interspoint = line1[0]+inters1[1]*(line1[1]-line1[0]);
|
||||
assert_approx(inters1[0],interspoint, info1);
|
||||
assert_approx(point3d(plane1)*inters1[0], plane1[3], info1); // interspoint on the plane
|
||||
assert_approx(distance_from_plane(plane1, inters1[0]), 0, info1); // inters1[0] on the plane
|
||||
}
|
||||
|
||||
// line parallel to the plane
|
||||
line2 = [ rands(-1,1,3)+[0,2,0], rands(-1,1,3)+[2,0,0] ]; // a random valid line2
|
||||
// not containing the origin
|
||||
plane0 = plane_from_points([line2[0], line2[1], [0,0,0]]); // plane cointaining the line
|
||||
plane2 = plane_from_normal(plane_normal(plane0), [5,5,5]);
|
||||
inters2 = _general_plane_line_intersection(plane2, line2);
|
||||
info2 = info_str([["line = ",line2],["plane = ",plane2]]);
|
||||
assert(inters2==undef, info2);
|
||||
|
||||
// line on the plane
|
||||
line3 = [ rands(-1,1,3), rands(-1,1,3)+[2,0,0] ]; // a random valid line
|
||||
imax = max_index(line3[1]-line3[0]);
|
||||
w = [for(j=[0:2]) imax==j? 0: 3 ];
|
||||
p3 = line3[0] + cross(line3[1]-line3[0],w); // a point not on the line
|
||||
plane3 = plane_from_points([line3[0], line3[1], p3]); // plane containing line
|
||||
inters3 = _general_plane_line_intersection(plane3, line3);
|
||||
info3 = info_str([["line = ",line3],["plane = ",plane3]]);
|
||||
assert(!is_undef(inters3) && inters3[1]==undef, info3);
|
||||
assert_approx(inters3[0], line3, info3);
|
||||
}
|
||||
*test__general_plane_line_intersection();
|
||||
|
||||
module assert_std(vc,ve) { assert(standardize(vc)==standardize(ve)); }
|
||||
|
||||
module test_points_on_plane() {
|
||||
pts = [for(i=[0:40]) rands(-1,1,3) ];
|
||||
dir = rands(-10,10,3);
|
||||
normal0 = unit([1,2,3]);
|
||||
normal0 = [1,2,3];
|
||||
ang = rands(0,360,1)[0];
|
||||
normal = rot(a=ang,p=normal0);
|
||||
plane = [each normal, normal*dir];
|
||||
prj_pts = projection_on_plane(plane,pts);
|
||||
assert(points_on_plane(prj_pts,plane));
|
||||
assert(!points_on_plane(concat(pts,[normal-dir]),plane));
|
||||
info = info_str([["pts = ",pts],["dir = ",dir],["ang = ",ang]]);
|
||||
assert(points_on_plane(prj_pts,plane),info);
|
||||
assert(!points_on_plane(concat(pts,[normal-dir]),plane),info);
|
||||
}
|
||||
*test_points_on_plane();
|
||||
|
||||
|
@ -122,14 +301,15 @@ module test_projection_on_plane(){
|
|||
plane = [each normal, 0];
|
||||
planem = [each normal, normal*dir];
|
||||
pts = [for(i=[1:10]) rands(-1,1,3)];
|
||||
info = info_str([["ang = ",ang],["dir = ",dir]]);
|
||||
assert_approx( projection_on_plane(plane,pts),
|
||||
projection_on_plane(plane,projection_on_plane(plane,pts)));
|
||||
projection_on_plane(plane,projection_on_plane(plane,pts)),info);
|
||||
assert_approx( projection_on_plane(plane,pts),
|
||||
rot(a=ang,p=projection_on_plane(plane0,rot(a=-ang,p=pts))));
|
||||
rot(a=ang,p=projection_on_plane(plane0,rot(a=-ang,p=pts))),info);
|
||||
assert_approx( move((-normal*dir)*normal,p=projection_on_plane(planem,pts)),
|
||||
projection_on_plane(plane,pts));
|
||||
projection_on_plane(plane,pts),info);
|
||||
assert_approx( move((normal*dir)*normal,p=projection_on_plane(plane,pts)),
|
||||
projection_on_plane(planem,pts));
|
||||
projection_on_plane(planem,pts),info);
|
||||
}
|
||||
*test_projection_on_plane();
|
||||
|
||||
|
@ -543,12 +723,43 @@ module test_distance_from_plane() {
|
|||
|
||||
|
||||
module test_polygon_line_intersection() {
|
||||
poly1 = [[50,50,50], [50,-50,50], [-50,-50,50]];
|
||||
assert_approx(polygon_line_intersection(poly1, [CENTER, UP]), [0,0,50]);
|
||||
assert_approx(polygon_line_intersection(poly1, [CENTER, UP+RIGHT]), [50,0,50]);
|
||||
assert_approx(polygon_line_intersection(poly1, [CENTER, UP+BACK+RIGHT]), [50,50,50]);
|
||||
assert_approx(polygon_line_intersection(poly1, [[0,0,50], [1,0,50]]), [[[0,0,50], [50,0,50]]]);
|
||||
assert_approx(polygon_line_intersection(poly1, [[0,0,0], [1,0,0]]), undef);
|
||||
poly0 = [ [-10,-10, 0],[10,-10, 0],[10,10,0],[0,5,0],[-10,10,0] ];
|
||||
line0 = [ [-3,7.5,0],[3,7.5,0] ]; // a segment on poly0 plane, out of poly0
|
||||
angs = rands(0,360,3);
|
||||
poly = rot(angs,p=poly0);
|
||||
lineon = rot(angs,p=line0);
|
||||
info = info_str([["angs = ",angs],["line = ",lineon],["poly = ",poly]]);
|
||||
// line on polygon plane
|
||||
assert_approx(polygon_line_intersection(poly,lineon,bounded=[true,true]),
|
||||
undef, info);
|
||||
assert_approx(polygon_line_intersection(poly,lineon,bounded=[true,false]),
|
||||
[rot(angs,p=[[5,7.5,0],[10,7.5,0]])], info);
|
||||
assert_approx(polygon_line_intersection(poly,lineon,bounded=[false,true]),
|
||||
[rot(angs,p=[[-10,7.5,0],[-5,7.5,0]])], info);
|
||||
assert_approx(polygon_line_intersection(poly,lineon,bounded=[false,false]),
|
||||
rot(angs,p=[[[-10,7.5,0],[-5,7.5,0]],[[5,7.5,0],[10,7.5,0]]]), info);
|
||||
// line parallel to polygon plane
|
||||
linepll = move([0,0,1],lineon);
|
||||
assert_approx(polygon_line_intersection(poly,linepll,bounded=[true,true]),
|
||||
undef, info);
|
||||
assert_approx(polygon_line_intersection(poly,linepll,bounded=[true,false]),
|
||||
undef, info);
|
||||
assert_approx(polygon_line_intersection(poly,linepll,bounded=[false,true]),
|
||||
undef, info);
|
||||
assert_approx(polygon_line_intersection(poly,linepll,bounded=[false,false]),
|
||||
undef, info);
|
||||
// general case
|
||||
trnsl = [0,0,1];
|
||||
linegnr = move(trnsl,rot(angs,p=[[5,5,5],[3,3,3]]));
|
||||
polygnr = move(trnsl,rot(angs,p=poly0));
|
||||
assert_approx(polygon_line_intersection(polygnr,linegnr,bounded=[true,true]),
|
||||
undef, info);
|
||||
assert_approx(polygon_line_intersection(polygnr,linegnr,bounded=[true,false]),
|
||||
trnsl, info);
|
||||
assert_approx(polygon_line_intersection(polygnr,linegnr,bounded=[false,true]),
|
||||
undef, info);
|
||||
assert_approx(polygon_line_intersection(polygnr,linegnr,bounded=[false,false]),
|
||||
trnsl, info);
|
||||
}
|
||||
*test_polygon_line_intersection();
|
||||
|
||||
|
@ -576,6 +787,8 @@ module test_in_front_of_plane() {
|
|||
|
||||
|
||||
module test_is_path() {
|
||||
assert(is_path([[1,2,3],[4,5,6]]));
|
||||
assert(is_path([[1,2,3],[4,5,6],[7,8,9]]));
|
||||
assert(!is_path(123));
|
||||
assert(!is_path("foo"));
|
||||
assert(!is_path(true));
|
||||
|
@ -584,8 +797,6 @@ module test_is_path() {
|
|||
assert(!is_path([["foo","bar","baz"]]));
|
||||
assert(!is_path([[1,2,3]]));
|
||||
assert(!is_path([["foo","bar","baz"],["qux","quux","quuux"]]));
|
||||
assert(is_path([[1,2,3],[4,5,6]]));
|
||||
assert(is_path([[1,2,3],[4,5,6],[7,8,9]]));
|
||||
}
|
||||
*test_is_path();
|
||||
|
||||
|
|
|
@ -100,104 +100,128 @@ module test_is_matrix() {
|
|||
test_is_matrix();
|
||||
|
||||
|
||||
module test_is_zero() {
|
||||
assert(is_zero(0));
|
||||
assert(is_zero([0,0,0]));
|
||||
assert(is_zero([[0,0,0],[0,0]]));
|
||||
assert(is_zero([EPSILON/2,EPSILON/2,EPSILON/2]));
|
||||
assert(!is_zero(1e-3));
|
||||
assert(!is_zero([0,0,1e-3]));
|
||||
assert(!is_zero([EPSILON*10,0,0]));
|
||||
assert(!is_zero([0,EPSILON*10,0]));
|
||||
assert(!is_zero([0,0,EPSILON*10]));
|
||||
assert(!is_zero(true));
|
||||
assert(!is_zero(false));
|
||||
assert(!is_zero(INF));
|
||||
assert(!is_zero(-INF));
|
||||
assert(!is_zero(NAN));
|
||||
assert(!is_zero("foo"));
|
||||
assert(!is_zero([]));
|
||||
assert(!is_zero([0:1:2]));
|
||||
module test_all_zero() {
|
||||
assert(all_zero(0));
|
||||
assert(all_zero([0,0,0]));
|
||||
assert(all_zero([[0,0,0],[0,0]]));
|
||||
assert(all_zero([EPSILON/2,EPSILON/2,EPSILON/2]));
|
||||
assert(!all_zero(1e-3));
|
||||
assert(!all_zero([0,0,1e-3]));
|
||||
assert(!all_zero([EPSILON*10,0,0]));
|
||||
assert(!all_zero([0,EPSILON*10,0]));
|
||||
assert(!all_zero([0,0,EPSILON*10]));
|
||||
assert(!all_zero(true));
|
||||
assert(!all_zero(false));
|
||||
assert(!all_zero(INF));
|
||||
assert(!all_zero(-INF));
|
||||
assert(!all_zero(NAN));
|
||||
assert(!all_zero("foo"));
|
||||
assert(!all_zero([]));
|
||||
assert(!all_zero([0:1:2]));
|
||||
}
|
||||
test_is_zero();
|
||||
test_all_zero();
|
||||
|
||||
|
||||
module test_is_positive() {
|
||||
assert(!is_positive(-2));
|
||||
assert(!is_positive(0));
|
||||
assert(is_positive(2));
|
||||
assert(!is_positive([0,0,0]));
|
||||
assert(!is_positive([0,1,2]));
|
||||
assert(is_positive([3,1,2]));
|
||||
assert(!is_positive([3,-1,2]));
|
||||
assert(!is_positive([]));
|
||||
assert(!is_positive(true));
|
||||
assert(!is_positive(false));
|
||||
assert(!is_positive("foo"));
|
||||
assert(!is_positive([0:1:2]));
|
||||
module test_all_nonzero() {
|
||||
assert(!all_nonzero(0));
|
||||
assert(!all_nonzero([0,0,0]));
|
||||
assert(!all_nonzero([[0,0,0],[0,0]]));
|
||||
assert(!all_nonzero([EPSILON/2,EPSILON/2,EPSILON/2]));
|
||||
assert(all_nonzero(1e-3));
|
||||
assert(!all_nonzero([0,0,1e-3]));
|
||||
assert(!all_nonzero([EPSILON*10,0,0]));
|
||||
assert(!all_nonzero([0,EPSILON*10,0]));
|
||||
assert(!all_nonzero([0,0,EPSILON*10]));
|
||||
assert(all_nonzero([1e-3,1e-3,1e-3]));
|
||||
assert(all_nonzero([EPSILON*10,EPSILON*10,EPSILON*10]));
|
||||
assert(!all_nonzero(true));
|
||||
assert(!all_nonzero(false));
|
||||
assert(!all_nonzero(INF));
|
||||
assert(!all_nonzero(-INF));
|
||||
assert(!all_nonzero(NAN));
|
||||
assert(!all_nonzero("foo"));
|
||||
assert(!all_nonzero([]));
|
||||
assert(!all_nonzero([0:1:2]));
|
||||
}
|
||||
test_is_positive();
|
||||
test_all_nonzero();
|
||||
|
||||
|
||||
module test_is_negative() {
|
||||
assert(is_negative(-2));
|
||||
assert(!is_negative(0));
|
||||
assert(!is_negative(2));
|
||||
assert(!is_negative([0,0,0]));
|
||||
assert(!is_negative([0,1,2]));
|
||||
assert(!is_negative([3,1,2]));
|
||||
assert(!is_negative([3,-1,2]));
|
||||
assert(is_negative([-3,-1,-2]));
|
||||
assert(!is_negative([-3,1,-2]));
|
||||
assert(is_negative([[-5,-7],[-3,-1,-2]]));
|
||||
assert(!is_negative([[-5,-7],[-3,1,-2]]));
|
||||
assert(!is_negative([]));
|
||||
assert(!is_negative(true));
|
||||
assert(!is_negative(false));
|
||||
assert(!is_negative("foo"));
|
||||
assert(!is_negative([0:1:2]));
|
||||
module test_all_positive() {
|
||||
assert(!all_positive(-2));
|
||||
assert(!all_positive(0));
|
||||
assert(all_positive(2));
|
||||
assert(!all_positive([0,0,0]));
|
||||
assert(!all_positive([0,1,2]));
|
||||
assert(all_positive([3,1,2]));
|
||||
assert(!all_positive([3,-1,2]));
|
||||
assert(!all_positive([]));
|
||||
assert(!all_positive(true));
|
||||
assert(!all_positive(false));
|
||||
assert(!all_positive("foo"));
|
||||
assert(!all_positive([0:1:2]));
|
||||
}
|
||||
test_is_negative();
|
||||
test_all_positive();
|
||||
|
||||
|
||||
module test_is_nonpositive() {
|
||||
assert(is_nonpositive(-2));
|
||||
assert(is_nonpositive(0));
|
||||
assert(!is_nonpositive(2));
|
||||
assert(is_nonpositive([0,0,0]));
|
||||
assert(!is_nonpositive([0,1,2]));
|
||||
assert(is_nonpositive([0,-1,-2]));
|
||||
assert(!is_nonpositive([3,1,2]));
|
||||
assert(!is_nonpositive([3,-1,2]));
|
||||
assert(!is_nonpositive([]));
|
||||
assert(!is_nonpositive(true));
|
||||
assert(!is_nonpositive(false));
|
||||
assert(!is_nonpositive("foo"));
|
||||
assert(!is_nonpositive([0:1:2]));
|
||||
module test_all_negative() {
|
||||
assert(all_negative(-2));
|
||||
assert(!all_negative(0));
|
||||
assert(!all_negative(2));
|
||||
assert(!all_negative([0,0,0]));
|
||||
assert(!all_negative([0,1,2]));
|
||||
assert(!all_negative([3,1,2]));
|
||||
assert(!all_negative([3,-1,2]));
|
||||
assert(all_negative([-3,-1,-2]));
|
||||
assert(!all_negative([-3,1,-2]));
|
||||
assert(all_negative([[-5,-7],[-3,-1,-2]]));
|
||||
assert(!all_negative([[-5,-7],[-3,1,-2]]));
|
||||
assert(!all_negative([]));
|
||||
assert(!all_negative(true));
|
||||
assert(!all_negative(false));
|
||||
assert(!all_negative("foo"));
|
||||
assert(!all_negative([0:1:2]));
|
||||
}
|
||||
test_is_nonpositive();
|
||||
test_all_negative();
|
||||
|
||||
|
||||
module test_is_nonnegative() {
|
||||
assert(!is_nonnegative(-2));
|
||||
assert(is_nonnegative(0));
|
||||
assert(is_nonnegative(2));
|
||||
assert(is_nonnegative([0,0,0]));
|
||||
assert(is_nonnegative([0,1,2]));
|
||||
assert(is_nonnegative([3,1,2]));
|
||||
assert(!is_nonnegative([3,-1,2]));
|
||||
assert(!is_nonnegative([-3,-1,-2]));
|
||||
assert(!is_nonnegative([[-5,-7],[-3,-1,-2]]));
|
||||
assert(!is_nonnegative([[-5,-7],[-3,1,-2]]));
|
||||
assert(!is_nonnegative([[5,7],[3,-1,2]]));
|
||||
assert(is_nonnegative([[5,7],[3,1,2]]));
|
||||
assert(!is_nonnegative([]));
|
||||
assert(!is_nonnegative(true));
|
||||
assert(!is_nonnegative(false));
|
||||
assert(!is_nonnegative("foo"));
|
||||
assert(!is_nonnegative([0:1:2]));
|
||||
module test_all_nonpositive() {
|
||||
assert(all_nonpositive(-2));
|
||||
assert(all_nonpositive(0));
|
||||
assert(!all_nonpositive(2));
|
||||
assert(all_nonpositive([0,0,0]));
|
||||
assert(!all_nonpositive([0,1,2]));
|
||||
assert(all_nonpositive([0,-1,-2]));
|
||||
assert(!all_nonpositive([3,1,2]));
|
||||
assert(!all_nonpositive([3,-1,2]));
|
||||
assert(!all_nonpositive([]));
|
||||
assert(!all_nonpositive(true));
|
||||
assert(!all_nonpositive(false));
|
||||
assert(!all_nonpositive("foo"));
|
||||
assert(!all_nonpositive([0:1:2]));
|
||||
}
|
||||
test_is_nonnegative();
|
||||
test_all_nonpositive();
|
||||
|
||||
|
||||
module test_all_nonnegative() {
|
||||
assert(!all_nonnegative(-2));
|
||||
assert(all_nonnegative(0));
|
||||
assert(all_nonnegative(2));
|
||||
assert(all_nonnegative([0,0,0]));
|
||||
assert(all_nonnegative([0,1,2]));
|
||||
assert(all_nonnegative([3,1,2]));
|
||||
assert(!all_nonnegative([3,-1,2]));
|
||||
assert(!all_nonnegative([-3,-1,-2]));
|
||||
assert(!all_nonnegative([[-5,-7],[-3,-1,-2]]));
|
||||
assert(!all_nonnegative([[-5,-7],[-3,1,-2]]));
|
||||
assert(!all_nonnegative([[5,7],[3,-1,2]]));
|
||||
assert(all_nonnegative([[5,7],[3,1,2]]));
|
||||
assert(!all_nonnegative([]));
|
||||
assert(!all_nonnegative(true));
|
||||
assert(!all_nonnegative(false));
|
||||
assert(!all_nonnegative("foo"));
|
||||
assert(!all_nonnegative([0:1:2]));
|
||||
}
|
||||
test_all_nonnegative();
|
||||
|
||||
|
||||
module test_approx() {
|
||||
|
@ -975,7 +999,7 @@ module test_null_space(){
|
|||
|
||||
function nullcheck(A,dim) =
|
||||
let(v=null_space(A))
|
||||
len(v)==dim && is_zero(A*transpose(v),eps=1e-12);
|
||||
len(v)==dim && all_zero(A*transpose(v),eps=1e-12);
|
||||
|
||||
A = [[-1, 2, -5, 2],[-3,-1,3,-3],[5,0,5,0],[3,-4,11,-4]];
|
||||
assert(nullcheck(A,1));
|
||||
|
|
|
@ -14,6 +14,14 @@ module test_is_vector() {
|
|||
assert(is_vector([0,0,0],zero=false) == false);
|
||||
assert(is_vector([0,1,0],zero=true) == false);
|
||||
assert(is_vector([0,0,1],zero=false) == true);
|
||||
assert(is_vector([1,1,1],zero=false) == true);
|
||||
|
||||
assert(is_vector([0,0,0],all_nonzero=true) == false);
|
||||
assert(is_vector([0,1,0],all_nonzero=true) == false);
|
||||
assert(is_vector([0,0,1],all_nonzero=true) == false);
|
||||
assert(is_vector([1,1,1],all_nonzero=true) == true);
|
||||
assert(is_vector([-1,1,1],all_nonzero=true) == true);
|
||||
assert(is_vector([-1,-1,-1],all_nonzero=true) == true);
|
||||
}
|
||||
test_is_vector();
|
||||
|
||||
|
|
18
vectors.scad
18
vectors.scad
|
@ -19,7 +19,8 @@
|
|||
// Arguments:
|
||||
// v = The value to test to see if it is a vector.
|
||||
// length = If given, make sure the vector is `length` items long.
|
||||
// zero = If false, require that the length of the vector is not approximately zero. If true, require the length of the vector to be approximately zero-length. Default: `undef` (don't check vector length.)
|
||||
// zero = If false, require that the length/`norm()` of the vector is not approximately zero. If true, require the length/`norm()` of the vector to be approximately zero-length. Default: `undef` (don't check vector length/`norm()`.)
|
||||
// all_nonzero = If true, requires all elements of the vector to be more than `eps` different from zero. Default: `false`
|
||||
// eps = The minimum vector length that is considered non-zero. Default: `EPSILON` (`1e-9`)
|
||||
// Example:
|
||||
// is_vector(4); // Returns false
|
||||
|
@ -30,14 +31,17 @@
|
|||
// is_vector([3,4,5],3); // Returns true
|
||||
// is_vector([3,4,5],4); // Returns true
|
||||
// is_vector([]); // Returns false
|
||||
// is_vector([0,4,0],3,zero=false); // Returns true
|
||||
// is_vector([0,0,0],zero=false); // Returns false
|
||||
// is_vector([0,0,1e-12],zero=false); // Returns false
|
||||
// is_vector([],zero=false); // Returns false
|
||||
function is_vector(v,length,zero,eps=EPSILON) =
|
||||
// is_vector([0,4,0],3,zero=false); // Returns true
|
||||
// is_vector([0,0,0],zero=false); // Returns false
|
||||
// is_vector([0,0,1e-12],zero=false); // Returns false
|
||||
// is_vector([0,1,0],all_nonzero=false); // Returns false
|
||||
// is_vector([1,1,1],all_nonzero=false); // Returns true
|
||||
// is_vector([],zero=false); // Returns false
|
||||
function is_vector(v, length, zero, all_nonzero=false, eps=EPSILON) =
|
||||
is_list(v) && is_num(0*(v*v))
|
||||
&& (is_undef(length) || len(v)==length)
|
||||
&& (is_undef(zero) || ((norm(v) >= eps) == !zero));
|
||||
&& (is_undef(zero) || ((norm(v) >= eps) == !zero))
|
||||
&& (!all_nonzero || all_nonzero(v)) ;
|
||||
|
||||
|
||||
// Function: vang()
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
BOSL_VERSION = [2,0,419];
|
||||
BOSL_VERSION = [2,0,422];
|
||||
|
||||
|
||||
// Section: BOSL Library Version Functions
|
||||
|
|
Loading…
Reference in a new issue