mirror of
https://github.com/BelfrySCAD/BOSL2.git
synced 2025-01-01 09:49:45 +00:00
commit
6fb16cc8e4
12 changed files with 554 additions and 254 deletions
5
.github/workflows/docsgen.yml
vendored
5
.github/workflows/docsgen.yml
vendored
|
@ -41,6 +41,11 @@ jobs:
|
||||||
cd $GITHUB_WORKSPACE
|
cd $GITHUB_WORKSPACE
|
||||||
./scripts/genindex.sh
|
./scripts/genindex.sh
|
||||||
|
|
||||||
|
- name: Generate Cheat Sheet
|
||||||
|
run: |
|
||||||
|
cd $GITHUB_WORKSPACE
|
||||||
|
./scripts/gencheat.sh
|
||||||
|
|
||||||
- name: Generating Docs
|
- name: Generating Docs
|
||||||
env:
|
env:
|
||||||
GH_PAT: ${{ secrets.GH_PAT }}
|
GH_PAT: ${{ secrets.GH_PAT }}
|
||||||
|
|
49
arrays.scad
49
arrays.scad
|
@ -1199,6 +1199,55 @@ function zip(vecs, v2, v3, fit=false, fill=undef) =
|
||||||
: [for(i=[0:1:minlen-1]) [for(v=vecs) for(x=v[i]) x] ];
|
: [for(i=[0:1:minlen-1]) [for(v=vecs) for(x=v[i]) x] ];
|
||||||
|
|
||||||
|
|
||||||
|
// Function: block_matrix()
|
||||||
|
// Usage:
|
||||||
|
// block_matrix([[M11, M12,...],[M21, M22,...], ... ])
|
||||||
|
// Description:
|
||||||
|
// Create a block matrix by supplying a matrix of matrices, which will
|
||||||
|
// be combined into one unified matrix. Every matrix in one row
|
||||||
|
// must have the same height, and the combined width of the matrices
|
||||||
|
// in each row must be equal.
|
||||||
|
function block_matrix(M) =
|
||||||
|
let(
|
||||||
|
bigM = [for(bigrow = M) each zip(bigrow)],
|
||||||
|
len0=len(bigM[0]),
|
||||||
|
badrows = [for(row=bigM) if (len(row)!=len0) 1]
|
||||||
|
)
|
||||||
|
assert(badrows==[], "Inconsistent or invalid input")
|
||||||
|
bigM;
|
||||||
|
|
||||||
|
// Function: diagonal_matrix()
|
||||||
|
// Usage:
|
||||||
|
// diagonal_matrix(diag, [offdiag])
|
||||||
|
// Description:
|
||||||
|
// Creates a square matrix with the items in the list `diag` on
|
||||||
|
// 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]];
|
||||||
|
|
||||||
|
|
||||||
|
// Function: submatrix_set()
|
||||||
|
// Usage: submatrix_set(M,A,[m],[n])
|
||||||
|
// Description:
|
||||||
|
// Sets a submatrix of M equal to the matrix A. By default the top left corner of M is set to A, but
|
||||||
|
// you can specify offset coordinates m and n. If A (as adjusted by m and n) extends beyond the bounds
|
||||||
|
// of M then the extra entries are ignored. You can pass in A=[[]], a null matrix, and M will be
|
||||||
|
// returned unchanged. Note that the input M need not be rectangular in shape.
|
||||||
|
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])
|
||||||
|
assert(is_list(M[i]), str("Row ",i," of input matrix is not a list"))
|
||||||
|
[for(j=[0:1:len(M[i])-1])
|
||||||
|
i>=m && i <len(A)+m && j>=n && j<len(A[0])+n ? A[i-m][j-n] : M[i][j]]];
|
||||||
|
|
||||||
|
|
||||||
// Function: array_group()
|
// Function: array_group()
|
||||||
// Description:
|
// Description:
|
||||||
// Takes a flat array of values, and groups items in sets of `cnt` length.
|
// Takes a flat array of values, and groups items in sets of `cnt` length.
|
||||||
|
|
|
@ -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");
|
assert(count3<=1 || (radius==0 && chamfer==0), "Do not specify both chamfer and radius");
|
||||||
slope = is_def(slope) ? slope :
|
slope = is_def(slope) ? slope :
|
||||||
is_def(angle) ? 1/tan(angle) : 6;
|
is_def(angle) ? 1/tan(angle) : 6;
|
||||||
width = gender == "male" ? w : w + 2*$slop;
|
extra_slop = gender == "female" ? 2*$slop : 0;
|
||||||
height = h + (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) :
|
front_offset = is_def(taper) ? -extra * tan(taper) :
|
||||||
is_def(back_width) ? extra * (back_width-width)/length/2 : 0;
|
is_def(back_width) ? extra * (back_width-width)/length/2 : 0;
|
||||||
|
|
266
math.scad
266
math.scad
|
@ -680,7 +680,7 @@ function convolve(p,q) =
|
||||||
// then the problem is solved for the matrix valued right hand side and a matrix is returned. Note that if you
|
// then the problem is solved for the matrix valued right hand side and a matrix is returned. Note that if you
|
||||||
// want to solve Ax=b1 and Ax=b2 that you need to form the matrix transpose([b1,b2]) for the right hand side and then
|
// want to solve Ax=b1 and Ax=b2 that you need to form the matrix transpose([b1,b2]) for the right hand side and then
|
||||||
// transpose the returned value.
|
// transpose the returned value.
|
||||||
function linear_solve(A,b) =
|
function linear_solve(A,b,pivot=true) =
|
||||||
assert(is_matrix(A), "Input should be a matrix.")
|
assert(is_matrix(A), "Input should be a matrix.")
|
||||||
let(
|
let(
|
||||||
m = len(A),
|
m = len(A),
|
||||||
|
@ -688,19 +688,17 @@ function linear_solve(A,b) =
|
||||||
)
|
)
|
||||||
assert(is_vector(b,m) || is_matrix(b,m),"Invalid right hand side or incompatible with the matrix")
|
assert(is_vector(b,m) || is_matrix(b,m),"Invalid right hand side or incompatible with the matrix")
|
||||||
let (
|
let (
|
||||||
qr = m<n? qr_factor(transpose(A)) : qr_factor(A),
|
qr = m<n? qr_factor(transpose(A),pivot) : qr_factor(A,pivot),
|
||||||
maxdim = max(n,m),
|
maxdim = max(n,m),
|
||||||
mindim = min(n,m),
|
mindim = min(n,m),
|
||||||
Q = submatrix(qr[0],[0:maxdim-1], [0:mindim-1]),
|
Q = submatrix(qr[0],[0:maxdim-1], [0:mindim-1]),
|
||||||
R = submatrix(qr[1],[0:mindim-1], [0:mindim-1]),
|
R = submatrix(qr[1],[0:mindim-1], [0:mindim-1]),
|
||||||
|
P = qr[2],
|
||||||
zeros = [for(i=[0:mindim-1]) if (approx(R[i][i],0)) i]
|
zeros = [for(i=[0:mindim-1]) if (approx(R[i][i],0)) i]
|
||||||
)
|
)
|
||||||
zeros != [] ? [] :
|
zeros != [] ? [] :
|
||||||
m<n
|
m<n ? Q*back_substitute(R,transpose(P)*b,transpose=true) // Too messy to avoid input checks here
|
||||||
// avoiding input validation in back_substitute
|
: P*_back_substitute(R, transpose(Q)*b); // Calling internal version skips input checks
|
||||||
? let( n = len(R) )
|
|
||||||
Q*reverse(_back_substitute(transpose(R, reverse=true), reverse(b)))
|
|
||||||
: _back_substitute(R, transpose(Q)*b);
|
|
||||||
|
|
||||||
// Function: matrix_inverse()
|
// Function: matrix_inverse()
|
||||||
// Usage:
|
// Usage:
|
||||||
|
@ -715,19 +713,40 @@ function matrix_inverse(A) =
|
||||||
linear_solve(A,ident(len(A)));
|
linear_solve(A,ident(len(A)));
|
||||||
|
|
||||||
|
|
||||||
// Function: qr_factor()
|
// Function: null_space()
|
||||||
// Usage: qr = qr_factor(A)
|
// Usage:
|
||||||
|
// null_space(A)
|
||||||
// Description:
|
// Description:
|
||||||
// Calculates the QR factorization of the input matrix A and returns it as the list [Q,R]. This factorization can be
|
// Returns an orthonormal basis for the null space of A, namely the vectors {x} such that Ax=0. If the null space
|
||||||
// used to solve linear systems of equations.
|
// is just the origin then returns an empty list.
|
||||||
function qr_factor(A) =
|
function null_space(A,eps=1e-12) =
|
||||||
|
assert(is_matrix(A))
|
||||||
|
let(
|
||||||
|
Q_R=qr_factor(transpose(A),pivot=true),
|
||||||
|
R=Q_R[1],
|
||||||
|
zrow = [for(i=idx(R)) if (all_zero(R[i],eps)) i]
|
||||||
|
)
|
||||||
|
len(zrow)==0
|
||||||
|
? []
|
||||||
|
: transpose(subindex(Q_R[0],zrow));
|
||||||
|
|
||||||
|
|
||||||
|
// Function: qr_factor()
|
||||||
|
// Usage: qr = qr_factor(A,[pivot])
|
||||||
|
// Description:
|
||||||
|
// Calculates the QR factorization of the input matrix A and returns it as the list [Q,R,P]. This factorization can be
|
||||||
|
// used to solve linear systems of equations. The factorization is A = Q*R*transpose(P). If pivot is false (the default)
|
||||||
|
// then P is the identity matrix and A = Q*R. If pivot is true then column pivoting results in an R matrix where the diagonal
|
||||||
|
// is non-decreasing. The use of pivoting is supposed to increase accuracy for poorly conditioned problems, and is necessary
|
||||||
|
// for rank estimation or computation of the null space, but it may be slower.
|
||||||
|
function qr_factor(A, pivot=false) =
|
||||||
assert(is_matrix(A), "Input must be a matrix." )
|
assert(is_matrix(A), "Input must be a matrix." )
|
||||||
let(
|
let(
|
||||||
m = len(A),
|
m = len(A),
|
||||||
n = len(A[0])
|
n = len(A[0])
|
||||||
)
|
)
|
||||||
let(
|
let(
|
||||||
qr = _qr_factor(A, Q=ident(m), column=0, m = m, n=n),
|
qr =_qr_factor(A, Q=ident(m),P=ident(n), pivot=pivot, column=0, m = m, n=n),
|
||||||
Rzero =
|
Rzero =
|
||||||
let( R = qr[1] )
|
let( R = qr[1] )
|
||||||
[ for(i=[0:m-1]) [
|
[ for(i=[0:m-1]) [
|
||||||
|
@ -735,25 +754,31 @@ function qr_factor(A) =
|
||||||
for(j=[0:n-1]) i>j ? 0 : ri[j]
|
for(j=[0:n-1]) i>j ? 0 : ri[j]
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
) [qr[0],Rzero];
|
) [qr[0],Rzero,qr[2]];
|
||||||
|
|
||||||
function _qr_factor(A,Q, column, m, n) =
|
function _qr_factor(A,Q,P, pivot, column, m, n) =
|
||||||
column >= min(m-1,n) ? [Q,A] :
|
column >= min(m-1,n) ? [Q,A,P] :
|
||||||
let(
|
let(
|
||||||
|
swap = !pivot ? 1
|
||||||
|
: _swap_matrix(n,column,column+max_index([for(i=[column:n-1]) sum_of_squares([for(j=[column:m-1]) A[j][i]])])),
|
||||||
|
A = pivot ? A*swap : A,
|
||||||
x = [for(i=[column:1:m-1]) A[i][column]],
|
x = [for(i=[column:1:m-1]) A[i][column]],
|
||||||
alpha = (x[0]<=0 ? 1 : -1) * norm(x),
|
alpha = (x[0]<=0 ? 1 : -1) * norm(x),
|
||||||
u = x - concat([alpha],repeat(0,m-1)),
|
u = x - concat([alpha],repeat(0,m-1)),
|
||||||
v = alpha==0 ? u : u / norm(u),
|
v = alpha==0 ? u : u / norm(u),
|
||||||
Qc = ident(len(x)) - 2*outer_product(v,v),
|
Qc = ident(len(x)) - 2*outer_product(v,v),
|
||||||
Qf = [for(i=[0:m-1])
|
Qf = [for(i=[0:m-1]) [for(j=[0:m-1]) i<column || j<column ? (i==j ? 1 : 0) : Qc[i-column][j-column]]]
|
||||||
[for(j=[0:m-1])
|
|
||||||
i<column || j<column
|
|
||||||
? (i==j ? 1 : 0)
|
|
||||||
: Qc[i-column][j-column]
|
|
||||||
]
|
|
||||||
]
|
|
||||||
)
|
)
|
||||||
_qr_factor(Qf*A, Q*Qf, column+1, m, n);
|
_qr_factor(Qf*A, Q*Qf, P*swap, pivot, column+1, m, n);
|
||||||
|
|
||||||
|
// Produces an n x n matrix that swaps column i and j (when multiplied on the right)
|
||||||
|
function _swap_matrix(n,i,j) =
|
||||||
|
assert(i<n && j<n && i>=0 && j>=0, "Swap indices out of bounds")
|
||||||
|
[for(y=[0:n-1]) [for (x=[0:n-1])
|
||||||
|
x==i ? (y==j ? 1 : 0)
|
||||||
|
: x==j ? (y==i ? 1 : 0)
|
||||||
|
: x==y ? 1 : 0]];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Function: back_substitute()
|
// Function: back_substitute()
|
||||||
|
@ -862,122 +887,156 @@ function is_matrix(A,m,n,square=false) =
|
||||||
&& ( !square || len(A)==len(A[0]));
|
&& ( !square || len(A)==len(A[0]));
|
||||||
|
|
||||||
|
|
||||||
|
// Function: norm_fro()
|
||||||
|
// Usage:
|
||||||
|
// norm_fro(A)
|
||||||
|
// Description:
|
||||||
|
// Computes frobenius norm of input matrix. The frobenius norm is the square root of the sum of the
|
||||||
|
// 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) =
|
||||||
|
assert(is_matrix(A) || is_vector(A))
|
||||||
|
norm(flatten(A));
|
||||||
|
|
||||||
|
|
||||||
// Section: Comparisons and Logic
|
// Section: Comparisons and Logic
|
||||||
|
|
||||||
// Function: is_zero()
|
// Function: all_zero()
|
||||||
// Usage:
|
// Usage:
|
||||||
// is_zero(x);
|
// all_zero(x);
|
||||||
// Description:
|
// 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.
|
// If passed a list, recursively checks if all items in the list are approximately zero.
|
||||||
// Otherwise, returns false.
|
// Otherwise, returns false.
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// x = The value to check.
|
// x = The value to check.
|
||||||
// eps = The maximum allowed variance. Default: `EPSILON` (1e-9)
|
// eps = The maximum allowed variance. Default: `EPSILON` (1e-9)
|
||||||
// Example:
|
// Example:
|
||||||
// is_zero(0); // Returns: true.
|
// all_zero(0); // Returns: true.
|
||||||
// is_zero(1e-3); // Returns: false.
|
// all_zero(1e-3); // Returns: false.
|
||||||
// is_zero([0,0,0]); // Returns: true.
|
// all_zero([0,0,0]); // Returns: true.
|
||||||
// is_zero([0,0,1e-3]); // Returns: false.
|
// all_zero([0,0,1e-3]); // Returns: false.
|
||||||
function is_zero(x, eps=EPSILON) =
|
function all_zero(x, eps=EPSILON) =
|
||||||
is_list(x)? (x != [] && [for (xx=x) if(!is_zero(xx,eps=eps)) 1] == []) :
|
is_finite(x)? approx(x,eps) :
|
||||||
is_num(x)? approx(x,eps) :
|
is_list(x)? (x != [] && [for (xx=x) if(!all_zero(xx,eps=eps)) 1] == []) :
|
||||||
false;
|
false;
|
||||||
|
|
||||||
|
|
||||||
// Function: is_positive()
|
// Function: all_nonzero()
|
||||||
// Usage:
|
// Usage:
|
||||||
// is_positive(x);
|
// all_nonzero(x);
|
||||||
// Description:
|
// 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.
|
// If passed a list, recursively checks if all items in the list are positive.
|
||||||
// Otherwise, returns false.
|
// Otherwise, returns false.
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// x = The value to check.
|
// x = The value to check.
|
||||||
// Example:
|
// Example:
|
||||||
// is_positive(-2); // Returns: false.
|
// all_positive(-2); // Returns: false.
|
||||||
// is_positive(0); // Returns: false.
|
// all_positive(0); // Returns: false.
|
||||||
// is_positive(2); // Returns: true.
|
// all_positive(2); // Returns: true.
|
||||||
// is_positive([0,0,0]); // Returns: false.
|
// all_positive([0,0,0]); // Returns: false.
|
||||||
// is_positive([0,1,2]); // Returns: false.
|
// all_positive([0,1,2]); // Returns: false.
|
||||||
// is_positive([3,1,2]); // Returns: true.
|
// all_positive([3,1,2]); // Returns: true.
|
||||||
// is_positive([3,-1,2]); // Returns: false.
|
// all_positive([3,-1,2]); // Returns: false.
|
||||||
function is_positive(x) =
|
function all_positive(x) =
|
||||||
is_list(x)? (x != [] && [for (xx=x) if(!is_positive(xx)) 1] == []) :
|
|
||||||
is_num(x)? x>0 :
|
is_num(x)? x>0 :
|
||||||
|
is_list(x)? (x != [] && [for (xx=x) if(!all_positive(xx)) 1] == []) :
|
||||||
false;
|
false;
|
||||||
|
|
||||||
|
|
||||||
// Function: is_negative()
|
// Function: all_negative()
|
||||||
// Usage:
|
// Usage:
|
||||||
// is_negative(x);
|
// all_negative(x);
|
||||||
// Description:
|
// 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.
|
// If passed a list, recursively checks if all items in the list are negative.
|
||||||
// Otherwise, returns false.
|
// Otherwise, returns false.
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// x = The value to check.
|
// x = The value to check.
|
||||||
// Example:
|
// Example:
|
||||||
// is_negative(-2); // Returns: true.
|
// all_negative(-2); // Returns: true.
|
||||||
// is_negative(0); // Returns: false.
|
// all_negative(0); // Returns: false.
|
||||||
// is_negative(2); // Returns: false.
|
// all_negative(2); // Returns: false.
|
||||||
// is_negative([0,0,0]); // Returns: false.
|
// all_negative([0,0,0]); // Returns: false.
|
||||||
// is_negative([0,1,2]); // Returns: false.
|
// all_negative([0,1,2]); // Returns: false.
|
||||||
// is_negative([3,1,2]); // Returns: false.
|
// all_negative([3,1,2]); // Returns: false.
|
||||||
// is_negative([3,-1,2]); // Returns: false.
|
// all_negative([3,-1,2]); // Returns: false.
|
||||||
// is_negative([-3,-1,-2]); // Returns: true.
|
// all_negative([-3,-1,-2]); // Returns: true.
|
||||||
function is_negative(x) =
|
function all_negative(x) =
|
||||||
is_list(x)? (x != [] && [for (xx=x) if(!is_negative(xx)) 1] == []) :
|
|
||||||
is_num(x)? x<0 :
|
is_num(x)? x<0 :
|
||||||
|
is_list(x)? (x != [] && [for (xx=x) if(!all_negative(xx)) 1] == []) :
|
||||||
false;
|
false;
|
||||||
|
|
||||||
|
|
||||||
// Function: is_nonpositive()
|
// Function: all_nonpositive()
|
||||||
// Usage:
|
// Usage:
|
||||||
// is_nonpositive(x);
|
// all_nonpositive(x);
|
||||||
// Description:
|
// 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.
|
// If passed a list, recursively checks if all items in the list are nonpositive.
|
||||||
// Otherwise, returns false.
|
// Otherwise, returns false.
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// x = The value to check.
|
// x = The value to check.
|
||||||
// Example:
|
// Example:
|
||||||
// is_nonpositive(-2); // Returns: true.
|
// all_nonpositive(-2); // Returns: true.
|
||||||
// is_nonpositive(0); // Returns: true.
|
// all_nonpositive(0); // Returns: true.
|
||||||
// is_nonpositive(2); // Returns: false.
|
// all_nonpositive(2); // Returns: false.
|
||||||
// is_nonpositive([0,0,0]); // Returns: true.
|
// all_nonpositive([0,0,0]); // Returns: true.
|
||||||
// is_nonpositive([0,1,2]); // Returns: false.
|
// all_nonpositive([0,1,2]); // Returns: false.
|
||||||
// is_nonpositive([3,1,2]); // Returns: false.
|
// all_nonpositive([3,1,2]); // Returns: false.
|
||||||
// is_nonpositive([3,-1,2]); // Returns: false.
|
// all_nonpositive([3,-1,2]); // Returns: false.
|
||||||
// is_nonpositive([-3,-1,-2]); // Returns: true.
|
// all_nonpositive([-3,-1,-2]); // Returns: true.
|
||||||
function is_nonpositive(x) =
|
function all_nonpositive(x) =
|
||||||
is_list(x)? (x != [] && [for (xx=x) if(!is_nonpositive(xx)) 1] == []) :
|
|
||||||
is_num(x)? x<=0 :
|
is_num(x)? x<=0 :
|
||||||
|
is_list(x)? (x != [] && [for (xx=x) if(!all_nonpositive(xx)) 1] == []) :
|
||||||
false;
|
false;
|
||||||
|
|
||||||
|
|
||||||
// Function: is_nonnegative()
|
// Function: all_nonnegative()
|
||||||
// Usage:
|
// Usage:
|
||||||
// is_nonnegative(x);
|
// all_nonnegative(x);
|
||||||
// Description:
|
// 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.
|
// If passed a list, recursively checks if all items in the list are nonnegative.
|
||||||
// Otherwise, returns false.
|
// Otherwise, returns false.
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// x = The value to check.
|
// x = The value to check.
|
||||||
// Example:
|
// Example:
|
||||||
// is_nonnegative(-2); // Returns: false.
|
// all_nonnegative(-2); // Returns: false.
|
||||||
// is_nonnegative(0); // Returns: true.
|
// all_nonnegative(0); // Returns: true.
|
||||||
// is_nonnegative(2); // Returns: true.
|
// all_nonnegative(2); // Returns: true.
|
||||||
// is_nonnegative([0,0,0]); // Returns: true.
|
// all_nonnegative([0,0,0]); // Returns: true.
|
||||||
// is_nonnegative([0,1,2]); // Returns: true.
|
// all_nonnegative([0,1,2]); // Returns: true.
|
||||||
// is_nonnegative([0,-1,-2]); // Returns: false.
|
// all_nonnegative([0,-1,-2]); // Returns: false.
|
||||||
// is_nonnegative([3,1,2]); // Returns: true.
|
// all_nonnegative([3,1,2]); // Returns: true.
|
||||||
// is_nonnegative([3,-1,2]); // Returns: false.
|
// all_nonnegative([3,-1,2]); // Returns: false.
|
||||||
// is_nonnegative([-3,-1,-2]); // Returns: false.
|
// all_nonnegative([-3,-1,-2]); // Returns: false.
|
||||||
function is_nonnegative(x) =
|
function all_nonnegative(x) =
|
||||||
is_list(x)? (x != [] && [for (xx=x) if(!is_nonnegative(xx)) 1] == []) :
|
|
||||||
is_num(x)? x>=0 :
|
is_num(x)? x>=0 :
|
||||||
|
is_list(x)? (x != [] && [for (xx=x) if(!all_nonnegative(xx)) 1] == []) :
|
||||||
false;
|
false;
|
||||||
|
|
||||||
|
|
||||||
|
@ -1309,6 +1368,41 @@ function C_div(z1,z2) =
|
||||||
|
|
||||||
// Section: Polynomials
|
// Section: Polynomials
|
||||||
|
|
||||||
|
// Function: quadratic_roots()
|
||||||
|
// Usage:
|
||||||
|
// roots = quadratic_roots(a,b,c,[real])
|
||||||
|
// Description:
|
||||||
|
// Computes roots of the quadratic equation a*x^2+b*x+c==0, where the
|
||||||
|
// coefficients are real numbers. If real is true then returns only the
|
||||||
|
// real roots. Otherwise returns a pair of complex values. This method
|
||||||
|
// may be more reliable than the general root finder at distinguishing
|
||||||
|
// real roots from complex roots.
|
||||||
|
|
||||||
|
// https://people.csail.mit.edu/bkph/articles/Quadratics.pdf
|
||||||
|
|
||||||
|
function quadratic_roots(a,b,c,real=false) =
|
||||||
|
real ? [for(root = quadratic_roots(a,b,c,real=false)) if (root.y==0) root.x]
|
||||||
|
:
|
||||||
|
is_undef(b) && is_undef(c) && is_vector(a,3) ? quadratic_roots(a[0],a[1],a[2]) :
|
||||||
|
assert(is_num(a) && is_num(b) && is_num(c))
|
||||||
|
assert(a!=0 || b!=0 || c!=0, "Quadratic must have a nonzero coefficient")
|
||||||
|
a==0 && b==0 ? [] : // No solutions
|
||||||
|
a==0 ? [[-c/b,0]] :
|
||||||
|
let(
|
||||||
|
descrim = b*b-4*a*c,
|
||||||
|
sqrt_des = sqrt(abs(descrim))
|
||||||
|
)
|
||||||
|
descrim < 0 ? // Complex case
|
||||||
|
[[-b, sqrt_des],
|
||||||
|
[-b, -sqrt_des]]/2/a :
|
||||||
|
b<0 ? // b positive
|
||||||
|
[[2*c/(-b+sqrt_des),0],
|
||||||
|
[(-b+sqrt_des)/a/2,0]]
|
||||||
|
: // b negative
|
||||||
|
[[(-b-sqrt_des)/2/a, 0],
|
||||||
|
[2*c/(-b-sqrt_des),0]];
|
||||||
|
|
||||||
|
|
||||||
// Function: polynomial()
|
// Function: polynomial()
|
||||||
// Usage:
|
// Usage:
|
||||||
// polynomial(p, z)
|
// polynomial(p, z)
|
||||||
|
|
|
@ -13,53 +13,55 @@ function lcase
|
||||||
|
|
||||||
function columnize
|
function columnize
|
||||||
{
|
{
|
||||||
cols=4
|
cols=$2
|
||||||
TMPFILE=$(mktemp -t $(basename $0).XXXXXX) || exit 1
|
TMPFILE=$(mktemp -t $(basename $0).XXXXXX) || exit 1
|
||||||
cat >>$TMPFILE
|
cat >>$TMPFILE
|
||||||
totcnt=$(wc -l $TMPFILE | awk '{print $1}')
|
if [[ $(wc -l $TMPFILE | awk '{print $1}') -gt 1 ]] ; then
|
||||||
maxrows=$((($totcnt+$cols-1)/$cols))
|
totcnt=$(wc -l $TMPFILE | awk '{print $1}')
|
||||||
maxcols=$cols
|
maxrows=$((($totcnt+$cols-1)/$cols))
|
||||||
if [[ $maxcols -gt $totcnt ]] ; then
|
maxcols=$cols
|
||||||
maxcols=$totcnt
|
if [[ $maxcols -gt $totcnt ]] ; then
|
||||||
fi
|
maxcols=$totcnt
|
||||||
cnt=0
|
|
||||||
hdrln1="| $(ucase $1) "
|
|
||||||
hdrln2='|:-----'
|
|
||||||
n=1
|
|
||||||
while [[ $n < $maxcols ]] ; do
|
|
||||||
hdrln1+=' | '
|
|
||||||
hdrln2+=' |:------'
|
|
||||||
n=$(($n+1))
|
|
||||||
done
|
|
||||||
hdrln1+=' |'
|
|
||||||
hdrln2+=' |'
|
|
||||||
n=0
|
|
||||||
while [[ $n < $maxrows ]] ; do
|
|
||||||
lines[$n]=""
|
|
||||||
n=$(($n+1))
|
|
||||||
done
|
|
||||||
col=0
|
|
||||||
while IFS= read -r line; do
|
|
||||||
if [[ $col != 0 ]] ; then
|
|
||||||
lines[$cnt]+=" | "
|
|
||||||
fi
|
fi
|
||||||
lines[$cnt]+="$line"
|
cnt=0
|
||||||
cnt=$(($cnt+1))
|
hdrln1="| $(ucase $1) "
|
||||||
if [[ $cnt = $maxrows ]] ; then
|
hdrln2='|:-----'
|
||||||
cnt=0
|
n=1
|
||||||
col=$(($col+1))
|
while [[ $n -lt $maxcols ]] ; do
|
||||||
fi
|
hdrln1+=' | '
|
||||||
done <$TMPFILE
|
hdrln2+=' |:------'
|
||||||
rm -f $TMPFILE
|
n=$(($n+1))
|
||||||
|
done
|
||||||
|
hdrln1+=' |'
|
||||||
|
hdrln2+=' |'
|
||||||
|
n=0
|
||||||
|
while [[ $n -lt $maxrows ]] ; do
|
||||||
|
lines[$n]=""
|
||||||
|
n=$(($n+1))
|
||||||
|
done
|
||||||
|
col=0
|
||||||
|
while IFS= read -r line; do
|
||||||
|
if [[ $col != 0 ]] ; then
|
||||||
|
lines[$cnt]+=" | "
|
||||||
|
fi
|
||||||
|
lines[$cnt]+="$line"
|
||||||
|
cnt=$(($cnt+1))
|
||||||
|
if [[ $cnt = $maxrows ]] ; then
|
||||||
|
cnt=0
|
||||||
|
col=$(($col+1))
|
||||||
|
fi
|
||||||
|
done <$TMPFILE
|
||||||
|
rm -f $TMPFILE
|
||||||
|
|
||||||
echo
|
echo
|
||||||
echo $hdrln1
|
echo $hdrln1
|
||||||
echo $hdrln2
|
echo $hdrln2
|
||||||
n=0
|
n=0
|
||||||
while [[ $n < $maxrows ]] ; do
|
while [[ $n -lt $maxrows ]] ; do
|
||||||
echo "| ${lines[$n]} |"
|
echo "| ${lines[$n]} |"
|
||||||
n=$(($n+1))
|
n=$(($n+1))
|
||||||
done
|
done
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
function mkconstindex
|
function mkconstindex
|
||||||
|
@ -67,24 +69,29 @@ function mkconstindex
|
||||||
sed 's/([^)]*)//g' | sed 's/[^a-zA-Z0-9_.:$]//g' | awk -F ':' '{printf "[%s](%s#%s)\n", $3, $1, $3}'
|
sed 's/([^)]*)//g' | sed 's/[^a-zA-Z0-9_.:$]//g' | awk -F ':' '{printf "[%s](%s#%s)\n", $3, $1, $3}'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function mkconstindex2
|
||||||
|
{
|
||||||
|
sed 's/ *=.*$//' | sed 's/[^a-zA-Z0-9_.:$]//g' | awk -F ':' '{printf "[%s](%s#%s)\n", $2, $1, $2}'
|
||||||
|
}
|
||||||
|
|
||||||
function mkotherindex
|
function mkotherindex
|
||||||
{
|
{
|
||||||
sed 's/([^)]*)//g' | sed 's/[^a-zA-Z0-9_.:$]//g' | awk -F ':' '{printf "[%s()](%s#%s)\n", $3, $1, $3}'
|
sed 's/([^)]*)//g' | sed 's/[^a-zA-Z0-9_.:$]//g' | awk -F ':' '{printf "[%s()](%s#%s)\n", $3, $1, $3}'
|
||||||
}
|
}
|
||||||
|
|
||||||
CHEAT_FILES=$(grep '^include' std.scad | sed 's/^.*<\([a-zA-Z0-9.]*\)>/\1/'|grep -v 'version.scad')
|
CHEAT_FILES=$(grep '^include' std.scad | sed 's/^.*<\([a-zA-Z0-9.]*\)>/\1/' | grep -v 'version.scad' | grep -v 'primitives.scad')
|
||||||
|
|
||||||
(
|
(
|
||||||
echo '## Belfry OpenScad Library Cheat Sheet'
|
echo '## Belfry OpenScad Library Cheat Sheet'
|
||||||
echo
|
echo
|
||||||
echo '( [Alphabetic Index](Index) )'
|
echo '( [Alphabetic Index](Index) )'
|
||||||
echo
|
echo
|
||||||
|
(
|
||||||
|
grep -H '// Constant: ' $CHEAT_FILES | mkconstindex
|
||||||
|
grep -H '^[A-Z$][A-Z0-9_]* *=.*//' $CHEAT_FILES | mkconstindex2
|
||||||
|
) | sort -u | columnize 'Constants' 6
|
||||||
for f in $CHEAT_FILES ; do
|
for f in $CHEAT_FILES ; do
|
||||||
#echo "### $f"
|
egrep -H '// Function: |// Function&Module: |// Module: ' $f | mkotherindex | columnize "[$f]($f)" 4
|
||||||
(
|
|
||||||
egrep -H 'Constant: ' $f | mkconstindex
|
|
||||||
egrep -H 'Function: |Function&Module: |Module: ' $f | mkotherindex
|
|
||||||
) | columnize $f
|
|
||||||
echo
|
echo
|
||||||
done
|
done
|
||||||
) > BOSL2.wiki/CheatSheet.md
|
) > BOSL2.wiki/CheatSheet.md
|
||||||
|
|
|
@ -6,16 +6,18 @@ function ucase
|
||||||
echo "$1" | tr '[:lower:]' '[:upper:]'
|
echo "$1" | tr '[:lower:]' '[:upper:]'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function lcase
|
function lcase
|
||||||
{
|
{
|
||||||
echo "$1" | tr '[:upper:]' '[:lower:]'
|
echo "$1" | tr '[:upper:]' '[:lower:]'
|
||||||
}
|
}
|
||||||
|
|
||||||
function mkindex
|
|
||||||
|
function alphaindex
|
||||||
{
|
{
|
||||||
TMPFILE=$(mktemp -t $(basename $0).XXXXXX) || exit 1
|
|
||||||
sed 's/([^)]*)//g' | sed 's/[^a-zA-Z0-9_.:$]//g' | awk -F ':' '{printf "- [%s](%s#%s)\n", $3, $1, $3}' | sort -d -f -u >> $TMPFILE
|
|
||||||
alpha="A B C D E F G H I J K L M N O P Q R S T U V W X Y Z"
|
alpha="A B C D E F G H I J K L M N O P Q R S T U V W X Y Z"
|
||||||
|
TMPFILE=$(mktemp -t $(basename $0).XXXXXX) || exit 1
|
||||||
|
sort -d -f >> $TMPFILE
|
||||||
for a in $alpha; do
|
for a in $alpha; do
|
||||||
echo -n "[$a](#$(lcase "$a")) "
|
echo -n "[$a](#$(lcase "$a")) "
|
||||||
done
|
done
|
||||||
|
@ -33,13 +35,31 @@ function mkindex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function constlist
|
||||||
|
{
|
||||||
|
sed 's/([^)]*)//g' | sed 's/[^a-zA-Z0-9_.:$]//g' | awk -F ':' '{printf "- [%s](%s#%s) (in %s)\n", $3, $1, $3, $1}'
|
||||||
|
}
|
||||||
|
|
||||||
|
function constlist2
|
||||||
|
{
|
||||||
|
sed 's/ *=.*$//' | sed 's/[^a-zA-Z0-9_.:$]//g' | awk -F ':' '{printf "- [%s](%s#%s) (in %s)\n", $2, $1, $2, $1}'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function funclist
|
||||||
|
{
|
||||||
|
sed 's/([^)]*)//g' | sed 's/[^a-zA-Z0-9_.:$]//g' | awk -F ':' '{printf "- [%s()](%s#%s) (in %s)\n", $3, $1, $3, $1}'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
(
|
(
|
||||||
echo "## Belfry OpenScad Library Index"
|
echo "## Belfry OpenScad Library Index"
|
||||||
(
|
(
|
||||||
grep 'Constant: ' *.scad
|
(
|
||||||
grep 'Function: ' *.scad
|
grep 'Constant: ' *.scad | constlist
|
||||||
grep 'Function&Module: ' *.scad
|
grep '^[A-Z]* *=.*//' *.scad | constlist2
|
||||||
grep 'Module: ' *.scad
|
) | sort -u
|
||||||
) | mkindex
|
egrep 'Function: |Function&Module: |Module: ' *.scad | sort -u | funclist
|
||||||
|
) | sort | alphaindex
|
||||||
) > BOSL2.wiki/Index.md
|
) > BOSL2.wiki/Index.md
|
||||||
|
|
||||||
|
|
28
std.scad
28
std.scad
|
@ -12,21 +12,6 @@ assert(version_num()>=20190301, "BOSL2 requires OpenSCAD version 2019.03.01 or l
|
||||||
include <version.scad>
|
include <version.scad>
|
||||||
|
|
||||||
include <constants.scad>
|
include <constants.scad>
|
||||||
include <edges.scad>
|
|
||||||
include <common.scad>
|
|
||||||
include <arrays.scad>
|
|
||||||
include <strings.scad>
|
|
||||||
include <vnf.scad>
|
|
||||||
include <debug.scad>
|
|
||||||
|
|
||||||
include <math.scad>
|
|
||||||
include <vectors.scad>
|
|
||||||
include <quaternions.scad>
|
|
||||||
include <affine.scad>
|
|
||||||
include <coords.scad>
|
|
||||||
include <geometry.scad>
|
|
||||||
include <regions.scad>
|
|
||||||
|
|
||||||
include <transforms.scad>
|
include <transforms.scad>
|
||||||
include <distributors.scad>
|
include <distributors.scad>
|
||||||
include <mutators.scad>
|
include <mutators.scad>
|
||||||
|
@ -36,6 +21,19 @@ include <shapes.scad>
|
||||||
include <shapes2d.scad>
|
include <shapes2d.scad>
|
||||||
include <masks.scad>
|
include <masks.scad>
|
||||||
include <paths.scad>
|
include <paths.scad>
|
||||||
|
include <edges.scad>
|
||||||
|
include <arrays.scad>
|
||||||
|
include <math.scad>
|
||||||
|
include <vectors.scad>
|
||||||
|
include <quaternions.scad>
|
||||||
|
include <affine.scad>
|
||||||
|
include <coords.scad>
|
||||||
|
include <geometry.scad>
|
||||||
|
include <regions.scad>
|
||||||
|
include <strings.scad>
|
||||||
|
include <vnf.scad>
|
||||||
|
include <common.scad>
|
||||||
|
include <debug.scad>
|
||||||
|
|
||||||
|
|
||||||
// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap
|
// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap
|
||||||
|
|
|
@ -470,6 +470,40 @@ module test_zip() {
|
||||||
}
|
}
|
||||||
test_zip();
|
test_zip();
|
||||||
|
|
||||||
|
module test_block_matrix() {
|
||||||
|
A = [[1,2],[3,4]];
|
||||||
|
B = ident(2);
|
||||||
|
assert_equal(block_matrix([[A,B],[B,A],[A,B]]), [[1,2,1,0],[3,4,0,1],[1,0,1,2],[0,1,3,4],[1,2,1,0],[3,4,0,1]]);
|
||||||
|
assert_equal(block_matrix([[A,B],ident(4)]), [[1,2,1,0],[3,4,0,1],[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]);
|
||||||
|
text = [["a","b"],["c","d"]];
|
||||||
|
assert_equal(block_matrix([[text,B]]), [["a","b",1,0],["c","d",0,1]]);
|
||||||
|
}
|
||||||
|
test_block_matrix();
|
||||||
|
|
||||||
|
|
||||||
|
module test_diagonal_matrix() {
|
||||||
|
assert_equal(diagonal_matrix([1,2,3]), [[1,0,0],[0,2,0],[0,0,3]]);
|
||||||
|
assert_equal(diagonal_matrix([1,"c",2]), [[1,0,0],[0,"c",0],[0,0,2]]);
|
||||||
|
assert_equal(diagonal_matrix([1,"c",2],"X"), [[1,"X","X"],["X","c","X"],["X","X",2]]);
|
||||||
|
assert_equal(diagonal_matrix([[1,1],[2,2],[3,3]], [0,0]), [[ [1,1],[0,0],[0,0]], [[0,0],[2,2],[0,0]], [[0,0],[0,0],[3,3]]]);
|
||||||
|
}
|
||||||
|
test_diagonal_matrix();
|
||||||
|
|
||||||
|
module test_submatrix_set() {
|
||||||
|
test = [[1,2,3,4,5],[6,7,8,9,10],[11,12,13,14,15], [16,17,18,19,20]];
|
||||||
|
ragged = [[1,2,3,4,5],[6,7,8,9,10],[11,12], [16,17]];
|
||||||
|
assert_equal(submatrix_set(test,[[9,8],[7,6]]), [[9,8,3,4,5],[7,6,8,9,10],[11,12,13,14,15], [16,17,18,19,20]]);
|
||||||
|
assert_equal(submatrix_set(test,[[9,7],[8,6]],1),[[1,2,3,4,5],[9,7,8,9,10],[8,6,13,14,15], [16,17,18,19,20]]);
|
||||||
|
assert_equal(submatrix_set(test,[[9,8],[7,6]],n=1), [[1,9,8,4,5],[6,7,6,9,10],[11,12,13,14,15], [16,17,18,19,20]]);
|
||||||
|
assert_equal(submatrix_set(test,[[9,8],[7,6]],1,2), [[1,2,3,4,5],[6,7,9,8,10],[11,12,7,6,15], [16,17,18,19,20]]);
|
||||||
|
assert_equal(submatrix_set(test,[[9,8],[7,6]],-1,-1), [[6,2,3,4,5],[6,7,8,9,10],[11,12,13,14,15], [16,17,18,19,20]]);
|
||||||
|
assert_equal(submatrix_set(test,[[9,8],[7,6]],n=4), [[1,2,3,4,9],[6,7,8,9,7],[11,12,13,14,15], [16,17,18,19,20]]);
|
||||||
|
assert_equal(submatrix_set(test,[[9,8],[7,6]],7,7), [[1,2,3,4,5],[6,7,8,9,10],[11,12,13,14,15], [16,17,18,19,20]]);
|
||||||
|
assert_equal(submatrix_set(ragged, [["a","b"],["c","d"]], 1, 1), [[1,2,3,4,5],[6,"a","b",9,10],[11,"c"], [16,17]]);
|
||||||
|
assert_equal(submatrix_set(test, [[]]), test);
|
||||||
|
}
|
||||||
|
test_submatrix_set();
|
||||||
|
|
||||||
|
|
||||||
module test_array_group() {
|
module test_array_group() {
|
||||||
v = [1,2,3,4,5,6];
|
v = [1,2,3,4,5,6];
|
||||||
|
|
|
@ -100,104 +100,128 @@ module test_is_matrix() {
|
||||||
test_is_matrix();
|
test_is_matrix();
|
||||||
|
|
||||||
|
|
||||||
module test_is_zero() {
|
module test_all_zero() {
|
||||||
assert(is_zero(0));
|
assert(all_zero(0));
|
||||||
assert(is_zero([0,0,0]));
|
assert(all_zero([0,0,0]));
|
||||||
assert(is_zero([[0,0,0],[0,0]]));
|
assert(all_zero([[0,0,0],[0,0]]));
|
||||||
assert(is_zero([EPSILON/2,EPSILON/2,EPSILON/2]));
|
assert(all_zero([EPSILON/2,EPSILON/2,EPSILON/2]));
|
||||||
assert(!is_zero(1e-3));
|
assert(!all_zero(1e-3));
|
||||||
assert(!is_zero([0,0,1e-3]));
|
assert(!all_zero([0,0,1e-3]));
|
||||||
assert(!is_zero([EPSILON*10,0,0]));
|
assert(!all_zero([EPSILON*10,0,0]));
|
||||||
assert(!is_zero([0,EPSILON*10,0]));
|
assert(!all_zero([0,EPSILON*10,0]));
|
||||||
assert(!is_zero([0,0,EPSILON*10]));
|
assert(!all_zero([0,0,EPSILON*10]));
|
||||||
assert(!is_zero(true));
|
assert(!all_zero(true));
|
||||||
assert(!is_zero(false));
|
assert(!all_zero(false));
|
||||||
assert(!is_zero(INF));
|
assert(!all_zero(INF));
|
||||||
assert(!is_zero(-INF));
|
assert(!all_zero(-INF));
|
||||||
assert(!is_zero(NAN));
|
assert(!all_zero(NAN));
|
||||||
assert(!is_zero("foo"));
|
assert(!all_zero("foo"));
|
||||||
assert(!is_zero([]));
|
assert(!all_zero([]));
|
||||||
assert(!is_zero([0:1:2]));
|
assert(!all_zero([0:1:2]));
|
||||||
}
|
}
|
||||||
test_is_zero();
|
test_all_zero();
|
||||||
|
|
||||||
|
|
||||||
module test_is_positive() {
|
module test_all_nonzero() {
|
||||||
assert(!is_positive(-2));
|
assert(!all_nonzero(0));
|
||||||
assert(!is_positive(0));
|
assert(!all_nonzero([0,0,0]));
|
||||||
assert(is_positive(2));
|
assert(!all_nonzero([[0,0,0],[0,0]]));
|
||||||
assert(!is_positive([0,0,0]));
|
assert(!all_nonzero([EPSILON/2,EPSILON/2,EPSILON/2]));
|
||||||
assert(!is_positive([0,1,2]));
|
assert(all_nonzero(1e-3));
|
||||||
assert(is_positive([3,1,2]));
|
assert(!all_nonzero([0,0,1e-3]));
|
||||||
assert(!is_positive([3,-1,2]));
|
assert(!all_nonzero([EPSILON*10,0,0]));
|
||||||
assert(!is_positive([]));
|
assert(!all_nonzero([0,EPSILON*10,0]));
|
||||||
assert(!is_positive(true));
|
assert(!all_nonzero([0,0,EPSILON*10]));
|
||||||
assert(!is_positive(false));
|
assert(all_nonzero([1e-3,1e-3,1e-3]));
|
||||||
assert(!is_positive("foo"));
|
assert(all_nonzero([EPSILON*10,EPSILON*10,EPSILON*10]));
|
||||||
assert(!is_positive([0:1:2]));
|
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() {
|
module test_all_positive() {
|
||||||
assert(is_negative(-2));
|
assert(!all_positive(-2));
|
||||||
assert(!is_negative(0));
|
assert(!all_positive(0));
|
||||||
assert(!is_negative(2));
|
assert(all_positive(2));
|
||||||
assert(!is_negative([0,0,0]));
|
assert(!all_positive([0,0,0]));
|
||||||
assert(!is_negative([0,1,2]));
|
assert(!all_positive([0,1,2]));
|
||||||
assert(!is_negative([3,1,2]));
|
assert(all_positive([3,1,2]));
|
||||||
assert(!is_negative([3,-1,2]));
|
assert(!all_positive([3,-1,2]));
|
||||||
assert(is_negative([-3,-1,-2]));
|
assert(!all_positive([]));
|
||||||
assert(!is_negative([-3,1,-2]));
|
assert(!all_positive(true));
|
||||||
assert(is_negative([[-5,-7],[-3,-1,-2]]));
|
assert(!all_positive(false));
|
||||||
assert(!is_negative([[-5,-7],[-3,1,-2]]));
|
assert(!all_positive("foo"));
|
||||||
assert(!is_negative([]));
|
assert(!all_positive([0:1:2]));
|
||||||
assert(!is_negative(true));
|
|
||||||
assert(!is_negative(false));
|
|
||||||
assert(!is_negative("foo"));
|
|
||||||
assert(!is_negative([0:1:2]));
|
|
||||||
}
|
}
|
||||||
test_is_negative();
|
test_all_positive();
|
||||||
|
|
||||||
|
|
||||||
module test_is_nonpositive() {
|
module test_all_negative() {
|
||||||
assert(is_nonpositive(-2));
|
assert(all_negative(-2));
|
||||||
assert(is_nonpositive(0));
|
assert(!all_negative(0));
|
||||||
assert(!is_nonpositive(2));
|
assert(!all_negative(2));
|
||||||
assert(is_nonpositive([0,0,0]));
|
assert(!all_negative([0,0,0]));
|
||||||
assert(!is_nonpositive([0,1,2]));
|
assert(!all_negative([0,1,2]));
|
||||||
assert(is_nonpositive([0,-1,-2]));
|
assert(!all_negative([3,1,2]));
|
||||||
assert(!is_nonpositive([3,1,2]));
|
assert(!all_negative([3,-1,2]));
|
||||||
assert(!is_nonpositive([3,-1,2]));
|
assert(all_negative([-3,-1,-2]));
|
||||||
assert(!is_nonpositive([]));
|
assert(!all_negative([-3,1,-2]));
|
||||||
assert(!is_nonpositive(true));
|
assert(all_negative([[-5,-7],[-3,-1,-2]]));
|
||||||
assert(!is_nonpositive(false));
|
assert(!all_negative([[-5,-7],[-3,1,-2]]));
|
||||||
assert(!is_nonpositive("foo"));
|
assert(!all_negative([]));
|
||||||
assert(!is_nonpositive([0:1:2]));
|
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() {
|
module test_all_nonpositive() {
|
||||||
assert(!is_nonnegative(-2));
|
assert(all_nonpositive(-2));
|
||||||
assert(is_nonnegative(0));
|
assert(all_nonpositive(0));
|
||||||
assert(is_nonnegative(2));
|
assert(!all_nonpositive(2));
|
||||||
assert(is_nonnegative([0,0,0]));
|
assert(all_nonpositive([0,0,0]));
|
||||||
assert(is_nonnegative([0,1,2]));
|
assert(!all_nonpositive([0,1,2]));
|
||||||
assert(is_nonnegative([3,1,2]));
|
assert(all_nonpositive([0,-1,-2]));
|
||||||
assert(!is_nonnegative([3,-1,2]));
|
assert(!all_nonpositive([3,1,2]));
|
||||||
assert(!is_nonnegative([-3,-1,-2]));
|
assert(!all_nonpositive([3,-1,2]));
|
||||||
assert(!is_nonnegative([[-5,-7],[-3,-1,-2]]));
|
assert(!all_nonpositive([]));
|
||||||
assert(!is_nonnegative([[-5,-7],[-3,1,-2]]));
|
assert(!all_nonpositive(true));
|
||||||
assert(!is_nonnegative([[5,7],[3,-1,2]]));
|
assert(!all_nonpositive(false));
|
||||||
assert(is_nonnegative([[5,7],[3,1,2]]));
|
assert(!all_nonpositive("foo"));
|
||||||
assert(!is_nonnegative([]));
|
assert(!all_nonpositive([0:1:2]));
|
||||||
assert(!is_nonnegative(true));
|
|
||||||
assert(!is_nonnegative(false));
|
|
||||||
assert(!is_nonnegative("foo"));
|
|
||||||
assert(!is_nonnegative([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() {
|
module test_approx() {
|
||||||
|
@ -781,6 +805,12 @@ test_back_substitute();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
module test_norm_fro(){
|
||||||
|
assert_approx(norm_fro([[2,3,4],[4,5,6]]), 10.29563014098700);
|
||||||
|
|
||||||
|
} test_norm_fro();
|
||||||
|
|
||||||
|
|
||||||
module test_linear_solve(){
|
module test_linear_solve(){
|
||||||
M = [[-2,-5,-1,3],
|
M = [[-2,-5,-1,3],
|
||||||
[3,7,6,2],
|
[3,7,6,2],
|
||||||
|
@ -954,6 +984,38 @@ module test_real_roots(){
|
||||||
test_real_roots();
|
test_real_roots();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
module test_quadratic_roots(){
|
||||||
|
assert_approx(quadratic_roots([1,4,4]),[[-2,0],[-2,0]]);
|
||||||
|
assert_approx(quadratic_roots([1,4,4],real=true),[-2,-2]);
|
||||||
|
assert_approx(quadratic_roots([1,-5,6],real=true), [2,3]);
|
||||||
|
assert_approx(quadratic_roots([1,-5,6]), [[2,0],[3,0]]);
|
||||||
|
}
|
||||||
|
test_quadratic_roots();
|
||||||
|
|
||||||
|
|
||||||
|
module test_null_space(){
|
||||||
|
assert_equal(null_space([[3,2,1],[3,6,3],[3,9,-3]]),[]);
|
||||||
|
|
||||||
|
function nullcheck(A,dim) =
|
||||||
|
let(v=null_space(A))
|
||||||
|
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));
|
||||||
|
|
||||||
|
B = [
|
||||||
|
[ 4, 1, 8, 6, -2, 3],
|
||||||
|
[ 10, 5, 10, 10, 0, 5],
|
||||||
|
[ 8, 1, 8, 8, -6, 1],
|
||||||
|
[ -8, -8, 6, -1, -8, -1],
|
||||||
|
[ 2, 2, 0, 1, 2, 1],
|
||||||
|
[ 2, -3, 10, 6, -8, 1],
|
||||||
|
];
|
||||||
|
assert(nullcheck(B,3));
|
||||||
|
}
|
||||||
|
test_null_space();
|
||||||
|
|
||||||
module test_qr_factor() {
|
module test_qr_factor() {
|
||||||
// Check that R is upper triangular
|
// Check that R is upper triangular
|
||||||
function is_ut(R) =
|
function is_ut(R) =
|
||||||
|
@ -962,7 +1024,15 @@ module test_qr_factor() {
|
||||||
|
|
||||||
// Test the R is upper trianglar, Q is orthogonal and qr=M
|
// Test the R is upper trianglar, Q is orthogonal and qr=M
|
||||||
function qrok(qr,M) =
|
function qrok(qr,M) =
|
||||||
is_ut(qr[1]) && approx(qr[0]*transpose(qr[0]), ident(len(qr[0]))) && approx(qr[0]*qr[1],M);
|
is_ut(qr[1]) && approx(qr[0]*transpose(qr[0]), ident(len(qr[0]))) && approx(qr[0]*qr[1],M) && qr[2]==ident(len(qr[2]));
|
||||||
|
|
||||||
|
// Test the R is upper trianglar, Q is orthogonal, R diagonal non-increasing and qrp=M
|
||||||
|
function qrokpiv(qr,M) =
|
||||||
|
is_ut(qr[1])
|
||||||
|
&& approx(qr[0]*transpose(qr[0]), ident(len(qr[0])))
|
||||||
|
&& approx(qr[0]*qr[1]*transpose(qr[2]),M)
|
||||||
|
&& list_decreasing([for(i=[0:1:min(len(qr[1]),len(qr[1][0]))-1]) abs(qr[1][i][i])]);
|
||||||
|
|
||||||
|
|
||||||
M = [[1,2,9,4,5],
|
M = [[1,2,9,4,5],
|
||||||
[6,7,8,19,10],
|
[6,7,8,19,10],
|
||||||
|
@ -991,6 +1061,15 @@ module test_qr_factor() {
|
||||||
assert(qrok(qr_factor([[7]]), [[7]]));
|
assert(qrok(qr_factor([[7]]), [[7]]));
|
||||||
assert(qrok(qr_factor([[1,2,3]]), [[1,2,3]]));
|
assert(qrok(qr_factor([[1,2,3]]), [[1,2,3]]));
|
||||||
assert(qrok(qr_factor([[1],[2],[3]]), [[1],[2],[3]]));
|
assert(qrok(qr_factor([[1],[2],[3]]), [[1],[2],[3]]));
|
||||||
|
|
||||||
|
|
||||||
|
assert(qrokpiv(qr_factor(M,pivot=true),M));
|
||||||
|
assert(qrokpiv(qr_factor(select(M,0,3),pivot=true),select(M,0,3)));
|
||||||
|
assert(qrokpiv(qr_factor(transpose(select(M,0,3)),pivot=true),transpose(select(M,0,3))));
|
||||||
|
assert(qrokpiv(qr_factor(B,pivot=true),B));
|
||||||
|
assert(qrokpiv(qr_factor([[7]],pivot=true), [[7]]));
|
||||||
|
assert(qrokpiv(qr_factor([[1,2,3]],pivot=true), [[1,2,3]]));
|
||||||
|
assert(qrokpiv(qr_factor([[1],[2],[3]],pivot=true), [[1],[2],[3]]));
|
||||||
}
|
}
|
||||||
test_qr_factor();
|
test_qr_factor();
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,14 @@ module test_is_vector() {
|
||||||
assert(is_vector([0,0,0],zero=false) == false);
|
assert(is_vector([0,0,0],zero=false) == false);
|
||||||
assert(is_vector([0,1,0],zero=true) == false);
|
assert(is_vector([0,1,0],zero=true) == false);
|
||||||
assert(is_vector([0,0,1],zero=false) == true);
|
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();
|
test_is_vector();
|
||||||
|
|
||||||
|
|
18
vectors.scad
18
vectors.scad
|
@ -19,7 +19,8 @@
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// v = The value to test to see if it is a vector.
|
// v = The value to test to see if it is a vector.
|
||||||
// length = If given, make sure the vector is `length` items long.
|
// 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`)
|
// eps = The minimum vector length that is considered non-zero. Default: `EPSILON` (`1e-9`)
|
||||||
// Example:
|
// Example:
|
||||||
// is_vector(4); // Returns false
|
// is_vector(4); // Returns false
|
||||||
|
@ -30,14 +31,17 @@
|
||||||
// is_vector([3,4,5],3); // Returns true
|
// is_vector([3,4,5],3); // Returns true
|
||||||
// is_vector([3,4,5],4); // Returns true
|
// is_vector([3,4,5],4); // Returns true
|
||||||
// is_vector([]); // Returns false
|
// is_vector([]); // Returns false
|
||||||
// is_vector([0,4,0],3,zero=false); // Returns true
|
// is_vector([0,4,0],3,zero=false); // Returns true
|
||||||
// is_vector([0,0,0],zero=false); // Returns false
|
// is_vector([0,0,0],zero=false); // Returns false
|
||||||
// is_vector([0,0,1e-12],zero=false); // Returns false
|
// is_vector([0,0,1e-12],zero=false); // Returns false
|
||||||
// is_vector([],zero=false); // Returns false
|
// is_vector([0,1,0],all_nonzero=false); // Returns false
|
||||||
function is_vector(v,length,zero,eps=EPSILON) =
|
// 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_list(v) && is_num(0*(v*v))
|
||||||
&& (is_undef(length) || len(v)==length)
|
&& (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()
|
// Function: vang()
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
BOSL_VERSION = [2,0,415];
|
BOSL_VERSION = [2,0,421];
|
||||||
|
|
||||||
|
|
||||||
// Section: BOSL Library Version Functions
|
// Section: BOSL Library Version Functions
|
||||||
|
|
Loading…
Reference in a new issue