mirror of
https://github.com/revarbat/BOSL2.git
synced 2025-01-16 13:50:23 +01:00
modified linear solvers to handle matrix RHS, added error checking to
lerp and affine_frame_map, adde same_shape(), added square option to is_matrix.
This commit is contained in:
parent
affd269081
commit
8fc3af0264
14
affine.scad
14
affine.scad
@ -245,13 +245,14 @@ function affine3d_rot_from_to(from, to) = let(
|
||||
// Description:
|
||||
// Returns a transformation that maps one coordinate frame to another. You must specify two or three of `x`, `y`, and `z`. The specified
|
||||
// axes are mapped to the vectors you supplied. If you give two inputs, the third vector is mapped to the appropriate normal to maintain a right hand coordinate system.
|
||||
// If the vectors you give are orthogonal the result will be a rotation. The `reverse` parameter will supply the inverse map, which enables you
|
||||
// to map two arbitrary coordinate systems two each other by using the canonical coordinate system as an intermediary.
|
||||
// If the vectors you give are orthogonal the result will be a rotation and the `reverse` parameter will supply the inverse map, which enables you
|
||||
// to map two arbitrary coordinate systems two each other by using the canonical coordinate system as an intermediary. You cannot use the `reverse` option
|
||||
// with non-orthogonal inputs.
|
||||
// Arguments:
|
||||
// x = Destination vector for x axis
|
||||
// y = Destination vector for y axis
|
||||
// z = Destination vector for z axis
|
||||
// reverse = reverse direction of the map. Default: false
|
||||
// reverse = reverse direction of the map for orthogonal inputs. Default: false
|
||||
// Examples:
|
||||
// T = affine_frame_map(x=[1,1,0], y=[-1,1]); // This map is just a rotation around the z axis
|
||||
// T = affine_frame_map(x=[1,0,0], y=[1,1]); // This map is not a rotation because x and y aren't orthogonal
|
||||
@ -276,7 +277,12 @@ function affine_frame_map(x,y,z, reverse=false) =
|
||||
is_undef(z) ? [x, y, cross(x,y)] :
|
||||
[x, y, z]
|
||||
)
|
||||
reverse ? affine2d_to_3d(map) : affine2d_to_3d(transpose(map));
|
||||
reverse ?
|
||||
let( ocheck = approx(map[0]*map[1],0) && approx(map[0]*map[2],0) && approx(map[1]*map[2],0))
|
||||
assert(ocheck, "Inputs must be orthogonal when reverse==true")
|
||||
affine2d_to_3d(map)
|
||||
:
|
||||
affine2d_to_3d(transpose(map));
|
||||
|
||||
|
||||
|
||||
|
11
common.scad
11
common.scad
@ -116,6 +116,17 @@ function is_consistent(list) =
|
||||
is_list(list) && is_list_of(list, list[0]);
|
||||
|
||||
|
||||
// Function: same_shape()
|
||||
// Usage:
|
||||
// same_shape(a,b)
|
||||
// Description:
|
||||
// Tests whether the inputs `a` and `b` are both numeric and are the same shaped list.
|
||||
// Example:
|
||||
// same_shape([3,[4,5]],[7,[3,4]]); // Returns true
|
||||
// same_shape([3,4,5], [7,[3,4]]); // Returns false
|
||||
function same_shape(a,b) = a*0 == b*0;
|
||||
|
||||
|
||||
// Section: Handling `undef`s.
|
||||
|
||||
|
||||
|
61
math.scad
61
math.scad
@ -106,7 +106,9 @@ function factorial(n,d=1) = product([for (i=[n:-1:d]) i]);
|
||||
// // Points colored in ROYGBIV order.
|
||||
// rainbow(pts) translate($item) circle(d=3,$fn=8);
|
||||
function lerp(a,b,u) =
|
||||
assert(same_shape(a,b), "Bad or inconsistent inputs to lerp")
|
||||
is_num(u)? (1-u)*a + u*b :
|
||||
assert(is_vector(u), "Input u to lerp must be number or vector")
|
||||
[for (v = u) lerp(a,b,v)];
|
||||
|
||||
|
||||
@ -536,15 +538,17 @@ function mean(v) = sum(v)/len(v);
|
||||
// Description:
|
||||
// Solves the linear system Ax=b. If A is square and non-singular the unique solution is returned. If A is overdetermined
|
||||
// the least squares solution is returned. If A is underdetermined, the minimal norm solution is returned.
|
||||
// If A is rank deficient or singular then linear_solve returns `undef`.
|
||||
// If A is rank deficient or singular then linear_solve returns `undef`. If b is a matrix that is compatible with A
|
||||
// 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
|
||||
// transpose the returned value.
|
||||
function linear_solve(A,b) =
|
||||
assert(is_matrix(A))
|
||||
assert(is_vector(b))
|
||||
let(
|
||||
dim = array_dim(A),
|
||||
m=dim[0], n=dim[1]
|
||||
)
|
||||
assert(len(b)==m,str("Incompatible matrix and vector",dim,len(b)))
|
||||
let(
|
||||
m = len(A),
|
||||
n = len(A[0])
|
||||
)
|
||||
assert(is_vector(b,m) || is_matrix(b,m),"Incompatible matrix and right hand side")
|
||||
let (
|
||||
qr = m<n ? qr_factor(transpose(A)) : qr_factor(A),
|
||||
maxdim = max(n,m),
|
||||
@ -555,7 +559,19 @@ function linear_solve(A,b) =
|
||||
)
|
||||
zeros != [] ? undef :
|
||||
m<n ? Q*back_substitute(R,b,transpose=true) :
|
||||
back_substitute(R, transpose(Q)*b);
|
||||
back_substitute(R, transpose(Q)*b);
|
||||
|
||||
// Function: matrix_inverse()
|
||||
// Usage:
|
||||
// matrix_inverse(A)
|
||||
// Description:
|
||||
// Compute the matrix inverse of the square matrix A. If A is singular, returns undef.
|
||||
// Note that if you just want to solve a linear system of equations you should NOT
|
||||
// use this function. Instead use linear_solve, or use qr_factor. The computation
|
||||
// will be faster and more accurate.
|
||||
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: submatrix()
|
||||
@ -573,11 +589,9 @@ function submatrix(M,ind1,ind2) = [for(i=ind1) [for(j=ind2) M[i][j] ] ];
|
||||
function qr_factor(A) =
|
||||
assert(is_matrix(A))
|
||||
let(
|
||||
dim = array_dim(A),
|
||||
m = dim[0],
|
||||
n = dim[1]
|
||||
m = len(A),
|
||||
n = len(A[0])
|
||||
)
|
||||
assert(len(dim)==2)
|
||||
let(
|
||||
qr =_qr_factor(A, column=0, m = m, n=n, Q=ident(m)),
|
||||
Rzero = [
|
||||
@ -601,14 +615,18 @@ function _qr_factor(A,Q, column, m, n) =
|
||||
_qr_factor(Qf*A, Q*Qf, column+1, m, n);
|
||||
|
||||
|
||||
|
||||
// Function: back_substitute()
|
||||
// Usage: back_substitute(R, b, [transpose])
|
||||
// Description:
|
||||
// Solves the problem Rx=b where R is an upper triangular square matrix. No check is made that the lower triangular entries
|
||||
// are actually zero. If transpose==true then instead solve transpose(R)*x=b.
|
||||
// are actually zero. If transpose==true then instead solve transpose(R)*x=b.
|
||||
// You can supply a compatible matrix b and it will produce the solution for every column of b. Note that if you want to
|
||||
// solve Rx=b1 and Rx=b2 you must set b to transpose([b1,b2]) and then take the transpose of the result.
|
||||
function back_substitute(R, b, x=[],transpose = false) =
|
||||
let(n=len(b))
|
||||
assert(is_matrix(R, square=true))
|
||||
let(n=len(R))
|
||||
assert(is_vector(b,n) || is_matrix(b,n),"R and b are not compatible in back_substitute")
|
||||
!is_vector(b) ? transpose([for(i=[0:len(b[0])-1]) back_substitute(R,subindex(b,i))]) :
|
||||
transpose?
|
||||
reverse(back_substitute(
|
||||
[for(i=[0:n-1]) [for(j=[0:n-1]) R[n-1-j][n-1-i]]],
|
||||
@ -678,18 +696,27 @@ function determinant(M) =
|
||||
|
||||
// Function: is_matrix()
|
||||
// Usage:
|
||||
// is_matrix(A,[m],[n])
|
||||
// is_matrix(A,[m],[n],[square])
|
||||
// Description:
|
||||
// Returns true if A is a numeric matrix of height m and width n. If m or n
|
||||
// are omitted or set to undef then true is returned for any positive dimension.
|
||||
function is_matrix(A,m,n) =
|
||||
// If `square` is true then the matrix is required to be square. Note if you
|
||||
// specify m != n and require a square matrix then the result will always be false.
|
||||
// Arguments:
|
||||
// A = matrix to test
|
||||
// m = optional height of matrix
|
||||
// n = optional width of matrix
|
||||
// square = set to true to require a square matrix. Default: false
|
||||
function is_matrix(A,m,n, square=false) =
|
||||
is_list(A) && len(A)>0 &&
|
||||
(is_undef(m) || len(A)==m) &&
|
||||
is_vector(A[0]) &&
|
||||
(is_undef(n) || len(A[0])==n) &&
|
||||
(!square || n==m) &&
|
||||
is_consistent(A);
|
||||
|
||||
|
||||
|
||||
// Section: Comparisons and Logic
|
||||
|
||||
// Function: approx()
|
||||
|
Loading…
x
Reference in New Issue
Block a user