| |
| |
| #include "toonz/ikjacobian.h" |
| |
| #include <stdlib.h> |
| #include <math.h> |
| #include <assert.h> |
| #include <iostream> |
| |
| #include "tstopwatch.h" |
| using namespace std; |
| |
| inline bool NearZero(double x, double tolerance) |
| { |
| return (fabs(x) <= tolerance); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| VectorRn VectorRn::WorkVector; |
| |
| double VectorRn::MaxAbs() const |
| { |
| double result = 0.0; |
| double *t = x; |
| for (long i = length; i > 0; i--) { |
| if ((*t) > result) { |
| result = *t; |
| } else if (-(*t) > result) { |
| result = -(*t); |
| } |
| t++; |
| } |
| return result; |
| } |
| |
| |
| |
| |
| MatrixRmn MatrixRmn::WorkMatrix; |
| |
| |
| void MatrixRmn::SetDiagonalEntries(double d) |
| { |
| long diagLen = tmin(NumRows, NumCols); |
| double *dPtr = x; |
| for (; diagLen > 0; diagLen--) { |
| *dPtr = d; |
| dPtr += NumRows + 1; |
| } |
| } |
| |
| |
| void MatrixRmn::SetDiagonalEntries(const VectorRn &d) |
| { |
| long diagLen = tmin(NumRows, NumCols); |
| assert(d.length == diagLen); |
| double *dPtr = x; |
| double *from = d.x; |
| for (; diagLen > 0; diagLen--) { |
| *dPtr = *(from++); |
| dPtr += NumRows + 1; |
| } |
| } |
| |
| |
| void MatrixRmn::SetSuperDiagonalEntries(double d) |
| { |
| long sDiagLen = tmin(NumRows, (long)(NumCols - 1)); |
| double *to = x + NumRows; |
| for (; sDiagLen > 0; sDiagLen--) { |
| *to = d; |
| to += NumRows + 1; |
| } |
| } |
| |
| |
| void MatrixRmn::SetSuperDiagonalEntries(const VectorRn &d) |
| { |
| long sDiagLen = tmin((long)(NumRows - 1), NumCols); |
| assert(sDiagLen == d.length); |
| double *to = x + NumRows; |
| double *from = d.x; |
| for (; sDiagLen > 0; sDiagLen--) { |
| *to = *(from++); |
| to += NumRows + 1; |
| } |
| } |
| |
| |
| void MatrixRmn::SetSubDiagonalEntries(double d) |
| { |
| long sDiagLen = tmin(NumRows, NumCols) - 1; |
| double *to = x + 1; |
| for (; sDiagLen > 0; sDiagLen--) { |
| *to = d; |
| to += NumRows + 1; |
| } |
| } |
| |
| |
| void MatrixRmn::SetSubDiagonalEntries(const VectorRn &d) |
| { |
| long sDiagLen = tmin(NumRows, NumCols) - 1; |
| assert(sDiagLen == d.length); |
| double *to = x + 1; |
| double *from = d.x; |
| for (; sDiagLen > 0; sDiagLen--) { |
| *to = *(from++); |
| to += NumRows + 1; |
| } |
| } |
| |
| |
| void MatrixRmn::SetColumn(long i, const VectorRn &d) |
| { |
| assert(NumRows == d.GetLength()); |
| double *to = x + i * NumRows; |
| const double *from = d.x; |
| for (i = NumRows; i > 0; i--) { |
| *(to++) = *(from++); |
| } |
| } |
| |
| |
| void MatrixRmn::SetRow(long i, const VectorRn &d) |
| { |
| assert(NumCols == d.GetLength()); |
| double *to = x + i; |
| const double *from = d.x; |
| for (i = NumRows; i > 0; i--) { |
| *to = *(from++); |
| to += NumRows; |
| } |
| } |
| |
| |
| |
| |
| |
| void MatrixRmn::SetSequence(const VectorRn &d, long startRow, long startCol, long deltaRow, long deltaCol) |
| { |
| long length = d.length; |
| assert(startRow >= 0 && startRow < NumRows && startCol >= 0 && startCol < NumCols); |
| assert(startRow + (length - 1) * deltaRow >= 0 && startRow + (length - 1) * deltaRow < NumRows); |
| assert(startCol + (length - 1) * deltaCol >= 0 && startCol + (length - 1) * deltaCol < NumCols); |
| double *to = x + startRow + NumRows * startCol; |
| double *from = d.x; |
| long stride = deltaRow + NumRows * deltaCol; |
| for (; length > 0; length--) { |
| *to = *(from++); |
| to += stride; |
| } |
| } |
| |
| |
| |
| |
| |
| void MatrixRmn::LoadAsSubmatrix(const MatrixRmn &A) |
| { |
| assert(A.NumRows <= NumRows && A.NumCols <= NumCols); |
| int extraColStep = NumRows - A.NumRows; |
| double *to = x; |
| double *from = A.x; |
| for (long i = A.NumCols; i > 0; i--) { |
| for (long j = A.NumRows; j > 0; j--) { |
| *(to++) = *(from++); |
| } |
| to += extraColStep; |
| } |
| } |
| |
| |
| |
| |
| void MatrixRmn::LoadAsSubmatrixTranspose(const MatrixRmn &A) |
| { |
| assert(A.NumRows <= NumCols && A.NumCols <= NumRows); |
| double *rowPtr = x; |
| double *from = A.x; |
| for (long i = A.NumCols; i > 0; i--) { |
| double *to = rowPtr; |
| for (long j = A.NumRows; j > 0; j--) { |
| *to = *(from++); |
| to += NumRows; |
| } |
| rowPtr++; |
| } |
| } |
| |
| |
| double MatrixRmn::FrobeniusNorm() const |
| { |
| return sqrt(FrobeniusNormSq()); |
| } |
| |
| |
| |
| void MatrixRmn::Multiply(const VectorRn &v, VectorRn &result) const |
| { |
| assert(v.GetLength() == NumCols && result.GetLength() == NumRows); |
| double *out = result.GetPtr(); |
| const double *rowPtr = x; |
| for (long j = NumRows; j > 0; j--) { |
| const double *in = v.GetPtr(); |
| const double *m = rowPtr++; |
| *out = 0.0f; |
| for (long i = NumCols; i > 0; i--) { |
| *out += (*(in++)) * (*m); |
| m += NumRows; |
| } |
| out++; |
| } |
| } |
| |
| |
| |
| |
| void MatrixRmn::MultiplyTranspose(const VectorRn &v, VectorRn &result) const |
| { |
| assert(v.GetLength() == NumRows && result.GetLength() == NumCols); |
| double *out = result.GetPtr(); |
| const double *colPtr = x; |
| for (long i = NumCols; i > 0; i--) { |
| const double *in = v.GetPtr(); |
| *out = 0.0f; |
| for (long j = NumRows; j > 0; j--) { |
| *out += (*(in++)) * (*(colPtr++)); |
| } |
| out++; |
| } |
| } |
| |
| |
| double MatrixRmn::DotProductColumn(const VectorRn &v, long colNum) const |
| { |
| assert(v.GetLength() == NumRows); |
| double *ptrC = x + colNum * NumRows; |
| double *ptrV = v.x; |
| double ret = 0.0; |
| for (long i = NumRows; i > 0; i--) { |
| ret += (*(ptrC++)) * (*(ptrV++)); |
| } |
| return ret; |
| } |
| |
| |
| MatrixRmn &MatrixRmn::AddToDiagonal(double d) |
| { |
| long diagLen = tmin(NumRows, NumCols); |
| double *dPtr = x; |
| for (; diagLen > 0; diagLen--) { |
| *dPtr += d; |
| dPtr += NumRows + 1; |
| } |
| return *this; |
| } |
| |
| |
| MatrixRmn &MatrixRmn::AddToDiagonal(const VectorRn &v) |
| { |
| long diagLen = tmin(NumRows, NumCols); |
| double *dPtr = x; |
| const double *dv = v.x; |
| for (; diagLen > 0; diagLen--) { |
| *dPtr += *(dv++); |
| dPtr += NumRows + 1; |
| } |
| return *this; |
| } |
| |
| MatrixRmn &MatrixRmn::MultiplyScalar(const MatrixRmn &A, double k, MatrixRmn &dst) |
| { |
| |
| long length = A.NumCols; |
| double *dPtr = dst.x; |
| for (long i = dst.NumCols; i > 0; i--) { |
| double *aPtr = A.x; |
| |
| for (long j = dst.NumRows; j > 0; j--) { |
| *dPtr = *aPtr * k; |
| dPtr++; |
| aPtr++; |
| } |
| aPtr += A.NumRows; |
| } |
| |
| return dst; |
| } |
| |
| MatrixRmn &MatrixRmn::Multiply(const MatrixRmn &A, const MatrixRmn &B, MatrixRmn &dst) |
| { |
| assert(A.NumCols == B.NumRows && A.NumRows == dst.NumRows && B.NumCols == dst.NumCols); |
| long length = A.NumCols; |
| double *bPtr = B.x; |
| double *dPtr = dst.x; |
| for (long i = dst.NumCols; i > 0; i--) { |
| double *aPtr = A.x; |
| for (long j = dst.NumRows; j > 0; j--) { |
| *dPtr = DotArray(length, aPtr, A.NumRows, bPtr, 1); |
| dPtr++; |
| aPtr++; |
| } |
| bPtr += B.NumRows; |
| } |
| |
| return dst; |
| } |
| |
| |
| MatrixRmn &MatrixRmn::TransposeMultiply(const MatrixRmn &A, const MatrixRmn &B, MatrixRmn &dst) |
| { |
| assert(A.NumRows == B.NumRows && A.NumCols == dst.NumRows && B.NumCols == dst.NumCols); |
| long length = A.NumRows; |
| |
| double *bPtr = B.x; |
| double *dPtr = dst.x; |
| for (long i = dst.NumCols; i > 0; i--) { |
| double *aPtr = A.x; |
| for (long j = dst.NumRows; j > 0; j--) { |
| *dPtr = DotArray(length, aPtr, 1, bPtr, 1); |
| dPtr++; |
| aPtr += A.NumRows; |
| } |
| bPtr += B.NumRows; |
| } |
| |
| return dst; |
| } |
| |
| |
| MatrixRmn &MatrixRmn::MultiplyTranspose(const MatrixRmn &A, const MatrixRmn &B, MatrixRmn &dst) |
| { |
| assert(A.NumCols == B.NumCols && A.NumRows == dst.NumRows && B.NumRows == dst.NumCols); |
| long length = A.NumCols; |
| |
| double *bPtr = B.x; |
| double *dPtr = dst.x; |
| for (long i = dst.NumCols; i > 0; i--) { |
| double *aPtr = A.x; |
| for (long j = dst.NumRows; j > 0; j--) { |
| *dPtr = DotArray(length, aPtr, A.NumRows, bPtr, B.NumRows); |
| dPtr++; |
| aPtr++; |
| } |
| bPtr++; |
| } |
| |
| return dst; |
| } |
| |
| |
| |
| |
| void MatrixRmn::Solve(const VectorRn &b, VectorRn *xVec) const |
| { |
| assert(NumRows == NumCols && NumCols == xVec->GetLength() && NumRows == b.GetLength()); |
| |
| |
| MatrixRmn &AugMat = GetWorkMatrix(NumRows, NumCols + 1); |
| AugMat.LoadAsSubmatrix(*this); |
| AugMat.SetColumn(NumRows, b); |
| |
| |
| AugMat.ConvertToRefNoFree(); |
| |
| |
| double *xLast = xVec->x + NumRows - 1; |
| double *endRow = AugMat.x + NumRows * NumCols - 1; |
| double *bPtr = endRow + NumRows; |
| for (long i = NumRows; i > 0; i--) { |
| double accum = *(bPtr--); |
| |
| double *rowPtr = endRow; |
| double *xPtr = xLast; |
| for (long j = NumRows - i; j > 0; j--) { |
| accum -= (*rowPtr) * (*(xPtr--)); |
| rowPtr -= NumCols; |
| } |
| assert(*rowPtr != 0.0); |
| *xPtr = accum / (*rowPtr); |
| endRow--; |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| void MatrixRmn::ConvertToRefNoFree() |
| { |
| |
| |
| |
| |
| long numIters = tmin(NumRows, NumCols); |
| double *rowPtr1 = x; |
| const long diagStep = NumRows + 1; |
| long lenRowLeft = NumCols; |
| for (; numIters > 1; numIters--) { |
| |
| double *rowPtr2 = rowPtr1; |
| double maxAbs = fabs(*rowPtr1); |
| double *rowPivot = rowPtr1; |
| long i; |
| for (i = numIters - 1; i > 0; i--) { |
| const double &newMax = *(++rowPivot); |
| if (newMax > maxAbs) { |
| maxAbs = *rowPivot; |
| rowPtr2 = rowPivot; |
| } else if (-newMax > maxAbs) { |
| maxAbs = -newMax; |
| rowPtr2 = rowPivot; |
| } |
| } |
| |
| if (rowPtr1 != rowPtr2) { |
| double *to = rowPtr1; |
| for (long i = lenRowLeft; i > 0; i--) { |
| double temp = *to; |
| *to = *rowPtr2; |
| *rowPtr2 = temp; |
| to += NumRows; |
| rowPtr2 += NumRows; |
| } |
| } |
| |
| rowPtr2 = rowPtr1; |
| for (i = numIters - 1; i > 0; i--) { |
| rowPtr2++; |
| double *to = rowPtr2; |
| double *from = rowPtr1; |
| assert(*from != 0.0); |
| double alpha = (*to) / (*from); |
| *to = 0.0; |
| for (long j = lenRowLeft - 1; j > 0; j--) { |
| to += NumRows; |
| from += NumRows; |
| *to -= (*from) * alpha; |
| } |
| } |
| |
| rowPtr1 += diagStep; |
| lenRowLeft--; |
| } |
| } |
| |
| |
| |
| |
| void MatrixRmn::CalcGivensValues(double a, double b, double *c, double *s) |
| { |
| double denomInv = sqrt(a * a + b * b); |
| if (denomInv == 0.0) { |
| *c = 1.0; |
| *s = 0.0; |
| } else { |
| denomInv = 1.0 / denomInv; |
| *c = a * denomInv; |
| *s = -b * denomInv; |
| } |
| } |
| |
| |
| |
| |
| |
| |
| void MatrixRmn::PostApplyGivens(double c, double s, long idx) |
| { |
| assert(0 <= idx && idx < NumCols); |
| double *colA = x + idx * NumRows; |
| double *colB = colA + NumRows; |
| for (long i = NumRows; i > 0; i--) { |
| double temp = *colA; |
| *colA = (*colA) * c + (*colB) * s; |
| *colB = (*colB) * c - temp * s; |
| colA++; |
| colB++; |
| } |
| } |
| |
| |
| |
| |
| |
| |
| void MatrixRmn::PostApplyGivens(double c, double s, long idx1, long idx2) |
| { |
| assert(idx1 != idx2 && 0 <= idx1 && idx1 < NumCols && 0 <= idx2 && idx2 < NumCols); |
| double *colA = x + idx1 * NumRows; |
| double *colB = x + idx2 * NumRows; |
| for (long i = NumRows; i > 0; i--) { |
| double temp = *colA; |
| *colA = (*colA) * c + (*colB) * s; |
| *colB = (*colB) * c - temp * s; |
| colA++; |
| colB++; |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| void MatrixRmn::ComputeSVD(MatrixRmn &U, VectorRn &w, MatrixRmn &V) const |
| { |
| assert(U.NumRows == NumRows && V.NumCols == NumCols && U.NumRows == U.NumCols && V.NumRows == V.NumCols && w.GetLength() == tmin(NumRows, NumCols)); |
| |
| double temp = 0.0; |
| VectorRn &superDiag = VectorRn::GetWorkVector(w.GetLength() - 1); |
| |
| |
| |
| |
| |
| MatrixRmn *leftMatrix; |
| MatrixRmn *rightMatrix; |
| if (NumRows >= NumCols) { |
| U.LoadAsSubmatrix(*this); |
| leftMatrix = &U; |
| rightMatrix = &V; |
| } else { |
| V.LoadAsSubmatrixTranspose(*this); |
| leftMatrix = &V; |
| rightMatrix = &U; |
| } |
| |
| |
| |
| |
| CalcBidiagonal(*leftMatrix, *rightMatrix, w, superDiag); |
| ConvertBidiagToDiagonal(*leftMatrix, *rightMatrix, w, superDiag); |
| } |
| |
| |
| |
| |
| |
| |
| |
| void MatrixRmn::CalcBidiagonal(MatrixRmn &U, MatrixRmn &V, VectorRn &w, VectorRn &superDiag) |
| { |
| assert(U.NumRows >= V.NumRows); |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| const long rowStep = U.NumCols; |
| const long diagStep = U.NumCols + 1; |
| double *diagPtr = U.x; |
| double *wPtr = w.x; |
| double *superDiagPtr = superDiag.x; |
| long colLengthLeft = U.NumRows; |
| long rowLengthLeft = V.NumCols; |
| while (true) { |
| |
| SvdHouseholder(diagPtr, colLengthLeft, rowLengthLeft, 1, rowStep, wPtr); |
| |
| if (rowLengthLeft == 2) { |
| *superDiagPtr = *(diagPtr + rowStep); |
| break; |
| } |
| |
| |
| SvdHouseholder(diagPtr + rowStep, rowLengthLeft - 1, colLengthLeft, rowStep, 1, superDiagPtr); |
| |
| rowLengthLeft--; |
| colLengthLeft--; |
| diagPtr += diagStep; |
| wPtr++; |
| superDiagPtr++; |
| } |
| |
| int extra = 0; |
| diagPtr += diagStep; |
| wPtr++; |
| if (colLengthLeft > 2) { |
| extra = 1; |
| |
| colLengthLeft--; |
| SvdHouseholder(diagPtr, colLengthLeft, 1, 1, 0, wPtr); |
| } else { |
| *wPtr = *diagPtr; |
| } |
| |
| |
| V.ExpandHouseholders(V.NumCols - 2, 1, U.x + U.NumRows, U.NumRows, 1); |
| U.ExpandHouseholders(V.NumCols - 1 + extra, 0, U.x, 1, U.NumRows); |
| |
| |
| return; |
| } |
| |
| |
| |
| |
| |
| |
| |
| void MatrixRmn::SvdHouseholder(double *basePt, |
| long colLength, long numCols, long colStride, long rowStride, |
| double *retFirstEntry) |
| { |
| |
| |
| double *cPtr = basePt; |
| double norm = 0.0; |
| long i; |
| |
| double aa0 = *cPtr; |
| double aa1 = *basePt; |
| double aa2 = *retFirstEntry; |
| |
| for (i = colLength; i > 0; i--) { |
| norm += Square(*cPtr); |
| cPtr += colStride; |
| } |
| norm = sqrt(norm); |
| |
| |
| double imageVal; |
| if ((*basePt) < 0.0) { |
| imageVal = norm; |
| norm = 2.0 * norm * (norm - (*basePt)); |
| } else { |
| imageVal = -norm; |
| norm = 2.0 * norm * (norm + (*basePt)); |
| } |
| norm = sqrt(norm); |
| |
| if (norm == 0.0) { |
| |
| cPtr = basePt; |
| for (i = colLength; i > 0; i--) { |
| *cPtr = 0.0; |
| cPtr += colStride; |
| } |
| *retFirstEntry = 0.0; |
| return; |
| } |
| |
| *retFirstEntry = imageVal; |
| |
| |
| *basePt -= imageVal; |
| |
| norm = 1.0 / norm; |
| cPtr = basePt; |
| for (i = colLength; i > 0; i--) { |
| *cPtr *= norm; |
| cPtr += colStride; |
| } |
| |
| |
| double *rPtr = basePt; |
| for (long j = numCols - 1; j > 0; j--) { |
| rPtr += rowStride; |
| |
| double dotP = DotArray(colLength, basePt, colStride, rPtr, colStride); |
| |
| AddArrayScale(colLength, basePt, colStride, rPtr, colStride, -2.0 * dotP); |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| void MatrixRmn::ExpandHouseholders(long numXforms, int numZerosSkipped, const double *basePt, long colStride, long rowStride) |
| { |
| |
| |
| long numToTransform = NumCols - numXforms + 1 - numZerosSkipped; |
| assert(numToTransform > 0); |
| |
| if (numXforms == 0) { |
| SetIdentity(); |
| return; |
| } |
| |
| |
| |
| long hDiagStride = rowStride + colStride; |
| const double *hBase = basePt + hDiagStride * (numXforms - 1); |
| const double *hDiagPtr = hBase + colStride * (numToTransform - 1); |
| long i; |
| double *diagPtr = x + NumCols * NumRows - 1; |
| double *colPtr = diagPtr - (numToTransform - 1); |
| for (i = numToTransform; i > 0; i--) { |
| CopyArrayScale(numToTransform, hBase, colStride, colPtr, 1, -2.0 * (*hDiagPtr)); |
| *diagPtr += 1.0; |
| diagPtr -= (NumRows + 1); |
| colPtr -= NumRows; |
| hDiagPtr -= colStride; |
| } |
| |
| |
| |
| double *colLastPtr = x + NumRows * NumCols - numToTransform - 1; |
| for (i = numXforms - 1; i > 0; i--) { |
| numToTransform++; |
| hBase -= hDiagStride; |
| colPtr = colLastPtr; |
| for (long j = numToTransform - 1; j > 0; j--) { |
| |
| double dotProd2N = -2.0 * DotArray(numToTransform - 1, hBase + colStride, colStride, colPtr + 1, 1); |
| *colPtr = dotProd2N * (*hBase); |
| AddArrayScale(numToTransform - 1, hBase + colStride, colStride, colPtr + 1, 1, dotProd2N); |
| colPtr -= NumRows; |
| } |
| |
| CopyArrayScale(numToTransform, hBase, colStride, colPtr, 1, -2.0 * (*hBase)); |
| *colPtr += 1.0; |
| |
| colLastPtr--; |
| } |
| |
| if (numZerosSkipped != 0) { |
| assert(numZerosSkipped == 1); |
| |
| double *d = x; |
| *d = 1; |
| double *d2 = d; |
| for (i = NumRows - 1; i > 0; i--) { |
| *(++d) = 0; |
| *(d2 += NumRows) = 0; |
| } |
| } |
| } |
| |
| |
| |
| |
| |
| void MatrixRmn::ConvertBidiagToDiagonal(MatrixRmn &U, MatrixRmn &V, VectorRn &w, VectorRn &superDiag) const |
| { |
| |
| |
| long lastBidiagIdx = V.NumRows - 1; |
| long firstBidiagIdx = 0; |
| |
| double aa = w.MaxAbs(); |
| double bb = superDiag.MaxAbs(); |
| |
| double eps = 1.0e-15 * tmax(w.MaxAbs(), superDiag.MaxAbs()); |
| |
| while (true) { |
| bool workLeft = UpdateBidiagIndices(&firstBidiagIdx, &lastBidiagIdx, w, superDiag, eps); |
| if (!workLeft) { |
| break; |
| } |
| |
| |
| |
| double *wPtr = w.x + firstBidiagIdx; |
| double *sdPtr = superDiag.x + firstBidiagIdx; |
| double extraOffDiag = 0.0; |
| if ((*wPtr) == 0.0) { |
| ClearRowWithDiagonalZero(firstBidiagIdx, lastBidiagIdx, U, wPtr, sdPtr, eps); |
| if (firstBidiagIdx > 0) { |
| if (NearZero(*(--sdPtr), eps)) { |
| *sdPtr = 0.0; |
| } else { |
| ClearColumnWithDiagonalZero(firstBidiagIdx, V, wPtr, sdPtr, eps); |
| } |
| } |
| continue; |
| } |
| |
| |
| |
| |
| double A; |
| A = (firstBidiagIdx < lastBidiagIdx - 1) ? Square(superDiag[lastBidiagIdx - 2]) : 0.0; |
| double BSq = Square(w[lastBidiagIdx - 1]); |
| A += BSq; |
| double C = Square(superDiag[lastBidiagIdx - 1]); |
| BSq *= C; |
| C += Square(w[lastBidiagIdx]); |
| double lambda; |
| lambda = sqrt(Square((A - C) * 0.5) + BSq); |
| if (A > C) { |
| lambda = -lambda; |
| } |
| lambda += (A + C) * 0.5; |
| double t11 = Square(w[firstBidiagIdx]); |
| double t12 = w[firstBidiagIdx] * superDiag[firstBidiagIdx]; |
| |
| double c, s; |
| CalcGivensValues(t11 - lambda, t12, &c, &s); |
| ApplyGivensCBTD(c, s, wPtr, sdPtr, &extraOffDiag, wPtr + 1); |
| V.PostApplyGivens(c, -s, firstBidiagIdx); |
| long i; |
| for (i = firstBidiagIdx; i < lastBidiagIdx - 1; i++) { |
| |
| CalcGivensValues(*wPtr, extraOffDiag, &c, &s); |
| ApplyGivensCBTD(c, s, wPtr, sdPtr, &extraOffDiag, extraOffDiag, wPtr + 1, sdPtr + 1); |
| U.PostApplyGivens(c, -s, i); |
| |
| CalcGivensValues(*sdPtr, extraOffDiag, &c, &s); |
| ApplyGivensCBTD(c, s, sdPtr, wPtr + 1, &extraOffDiag, extraOffDiag, sdPtr + 1, wPtr + 2); |
| V.PostApplyGivens(c, -s, i + 1); |
| wPtr++; |
| sdPtr++; |
| } |
| |
| CalcGivensValues(*wPtr, extraOffDiag, &c, &s); |
| ApplyGivensCBTD(c, s, wPtr, &extraOffDiag, sdPtr, wPtr + 1); |
| U.PostApplyGivens(c, -s, i); |
| |
| |
| |
| } |
| } |
| |
| |
| |
| |
| |
| void MatrixRmn::ClearRowWithDiagonalZero(long firstBidiagIdx, long lastBidiagIdx, MatrixRmn &U, double *wPtr, double *sdPtr, double eps) |
| { |
| double curSd = *sdPtr; |
| *sdPtr = 0.0; |
| long i = firstBidiagIdx + 1; |
| while (true) { |
| |
| double c, s; |
| CalcGivensValues(*(++wPtr), curSd, &c, &s); |
| U.PostApplyGivens(c, -s, i, firstBidiagIdx); |
| *wPtr = c * (*wPtr) - s * curSd; |
| if (i == lastBidiagIdx) { |
| break; |
| } |
| curSd = s * (*(++sdPtr)); |
| *sdPtr = c * (*sdPtr); |
| i++; |
| } |
| } |
| |
| |
| |
| |
| |
| void MatrixRmn::ClearColumnWithDiagonalZero(long endIdx, MatrixRmn &V, double *wPtr, double *sdPtr, double eps) |
| { |
| double curSd = *sdPtr; |
| *sdPtr = 0.0; |
| long i = endIdx - 1; |
| while (true) { |
| double c, s; |
| CalcGivensValues(*(--wPtr), curSd, &c, &s); |
| V.PostApplyGivens(c, -s, i, endIdx); |
| *wPtr = c * (*wPtr) - s * curSd; |
| if (i == 0) { |
| break; |
| } |
| curSd = s * (*(--sdPtr)); |
| if (NearZero(curSd, eps)) { |
| break; |
| } |
| *sdPtr = c * (*sdPtr); |
| i--; |
| } |
| } |
| |
| |
| |
| void MatrixRmn::ApplyGivensCBTD(double cosine, double sine, double *a, double *b, double *c, double *d) |
| { |
| double temp = *a; |
| *a = cosine * (*a) - sine * (*b); |
| *b = sine * temp + cosine * (*b); |
| temp = *c; |
| *c = cosine * (*c) - sine * (*d); |
| *d = sine * temp + cosine * (*d); |
| } |
| |
| |
| |
| |
| void MatrixRmn::ApplyGivensCBTD(double cosine, double sine, double *a, double *b, double *c, |
| double d, double *e, double *f) |
| { |
| *a = cosine * (*a) - sine * d; |
| double temp = *b; |
| *b = cosine * (*b) - sine * (*e); |
| *e = sine * temp + cosine * (*e); |
| *c = -sine * (*f); |
| *f = cosine * (*f); |
| } |
| |
| |
| bool MatrixRmn::UpdateBidiagIndices(long *firstBidiagIdx, long *lastBidiagIdx, VectorRn &w, VectorRn &superDiag, double eps) |
| { |
| long lastIdx = *lastBidiagIdx; |
| double *sdPtr = superDiag.GetPtr(lastIdx - 1); |
| while (NearZero(*sdPtr, eps)) { |
| *(sdPtr--) = 0.0; |
| lastIdx--; |
| if (lastIdx == 0) { |
| return false; |
| } |
| } |
| *lastBidiagIdx = lastIdx; |
| long firstIdx = lastIdx - 1; |
| double *wPtr = w.GetPtr(firstIdx); |
| while (firstIdx > 0) { |
| if (NearZero(*wPtr, eps)) { |
| *wPtr = 0.0; |
| break; |
| } |
| if (NearZero(*(--sdPtr), eps)) { |
| *sdPtr = 0.0; |
| break; |
| } |
| wPtr--; |
| firstIdx--; |
| } |
| *firstBidiagIdx = firstIdx; |
| return true; |
| } |
| |
| |
| |
| bool MatrixRmn::DebugCheckSVD(const MatrixRmn &U, const VectorRn &w, const MatrixRmn &V) const |
| { |
| |
| |
| MatrixRmn IV(V.getNumRows(), V.getNumColumns()); |
| IV.SetIdentity(); |
| MatrixRmn VTV(V.getNumRows(), V.getNumColumns()); |
| MatrixRmn::TransposeMultiply(V, V, VTV); |
| IV -= VTV; |
| double error = IV.FrobeniusNorm(); |
| |
| MatrixRmn IU(U.getNumRows(), U.getNumColumns()); |
| IU.SetIdentity(); |
| MatrixRmn UTU(U.getNumRows(), U.getNumColumns()); |
| MatrixRmn::TransposeMultiply(U, U, UTU); |
| IU -= UTU; |
| error += IU.FrobeniusNorm(); |
| |
| MatrixRmn Diag(U.getNumRows(), V.getNumRows()); |
| Diag.SetZero(); |
| Diag.SetDiagonalEntries(w); |
| MatrixRmn B(U.getNumRows(), V.getNumRows()); |
| MatrixRmn C(U.getNumRows(), V.getNumRows()); |
| MatrixRmn::Multiply(U, Diag, B); |
| MatrixRmn::MultiplyTranspose(B, V, C); |
| C -= *this; |
| error += C.FrobeniusNorm(); |
| |
| bool ret = (fabs(error) <= 1.0e-13 * w.MaxAbs()); |
| assert(ret); |
| return ret; |
| } |
| |
| |
| |
| const double PI = 3.1415926535897932384626433832795028841972; |
| const double RadiansToDegrees = 180.0 / PI; |
| const double DegreesToRadians = PI / 180; |
| |
| const double Jacobian::DefaultDampingLambda = 1.1; |
| |
| const double Jacobian::PseudoInverseThresholdFactor = 0.001; |
| const double Jacobian::MaxAngleJtranspose = 30.0 * DegreesToRadians; |
| const double Jacobian::MaxAnglePseudoinverse = 5.0 * DegreesToRadians; |
| const double Jacobian::MaxAngleDLS = 5.0 * DegreesToRadians; |
| const double Jacobian::MaxAngleSDLS = 45.0 * DegreesToRadians; |
| const double Jacobian::BaseMaxTargetDist = 3.4; |
| |
| Jacobian::Jacobian(IKSkeleton *skeleton, std::vector<TPointD> &targetPos) |
| { |
| Jacobian::skeleton = skeleton; |
| nEffector = skeleton->getNumEffector(); |
| nJoint = skeleton->getNodeCount() - nEffector; |
| nRow = 2 * nEffector; |
| nCol = nJoint; |
| |
| target = (targetPos); |
| |
| Jend.SetSize(nRow, nCol); |
| Jend.SetZero(); |
| |
| Jtarget.SetSize(nRow, nCol); |
| Jtarget.SetZero(); |
| |
| U.SetSize(nRow, nRow); |
| w.SetLength(min(nRow, nCol)); |
| V.SetSize(nCol, nCol); |
| |
| dS.SetLength(nRow); |
| dTheta.SetLength(nCol); |
| dPreTheta.SetLength(nCol); |
| |
| |
| dT.SetLength(nRow); |
| |
| |
| dSclamp.SetLength(nEffector); |
| |
| Jnorms.SetSize(nEffector, nCol); |
| |
| DampingLambdaSqV.SetLength(nRow); |
| diagMatIdentity.SetLength(nCol); |
| |
| Reset(); |
| } |
| |
| void Jacobian::Reset() |
| { |
| |
| DampingLambda = DefaultDampingLambda; |
| DampingLambdaSq = Square(DampingLambda); |
| for (int i = 0; i < DampingLambdaSqV.GetLength(); i++) |
| DampingLambdaSqV[i] = DampingLambdaSq; |
| for (int i = 0; i < diagMatIdentity.GetLength(); i++) |
| diagMatIdentity[i] = 1.0; |
| |
| |
| dSclamp.Fill(HUGE_VAL); |
| } |
| |
| |
| |
| void Jacobian::computeJacobian() |
| { |
| |
| |
| int numNode = skeleton->getNodeCount(); |
| for (int index = 0; index < numNode; index++) { |
| IKNode *n = skeleton->getNode(index); |
| int effectorCount = skeleton->getNumEffector(); |
| if (n->IsEffector()) { |
| int i = n->getEffectorNum(); |
| const TPointD &targetPos = target[i]; |
| TPointD temp; |
| |
| temp = targetPos; |
| TPointD a = n->GetS(); |
| temp -= n->GetS(); |
| if (i < effectorCount - 1) { |
| temp.x = 100 * temp.x; |
| temp.y = 100 * temp.y; |
| } |
| dS.SetCouple(i, temp); |
| |
| |
| |
| IKNode *m = skeleton->getParent(n); |
| |
| while (m) { |
| int j = m->getJointNum(); |
| |
| int numnode = skeleton->getNodeCount(); |
| assert(0 <= i && i < nEffector && 0 <= j && j < (skeleton->getNodeCount() - skeleton->getNumEffector())); |
| if (m->isFrozen()) { |
| Jend.SetCouple(i, j, TPointD(0.0, 0.0)); |
| |
| } else { |
| temp = m->GetS(); |
| temp -= n->GetS(); |
| |
| double tx = temp.x; |
| temp.x = temp.y; |
| temp.y = -tx; |
| |
| if (i < effectorCount - 1) { |
| temp.x = 100 * temp.x; |
| temp.y = 100 * temp.y; |
| } |
| Jend.SetCouple(i, j, temp); |
| } |
| m = skeleton->getParent(m); |
| } |
| } |
| } |
| } |
| |
| |
| |
| |
| void Jacobian::UpdateThetas() |
| { |
| |
| for (int index = 0; index < skeleton->getNodeCount(); index++) { |
| IKNode *n = skeleton->getNode(index); |
| if (n->IsJoint()) { |
| int i = n->getJointNum(); |
| n->AddToTheta(dTheta[i]); |
| } |
| } |
| |
| skeleton->compute(); |
| } |
| |
| bool Jacobian::checkJointsLimit() |
| { |
| bool clampingDetected = false; |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| return clampingDetected; |
| } |
| |
| void Jacobian::ZeroDeltaThetas() |
| { |
| dTheta.SetZero(); |
| } |
| |
| |
| |
| void Jacobian::CalcDeltaThetasTranspose() |
| { |
| const MatrixRmn &J = Jend; |
| |
| J.MultiplyTranspose(dS, dTheta); |
| |
| |
| J.Multiply(dTheta, dT); |
| double alpha = Dot(dS, dT) / dT.NormSq(); |
| assert(alpha > 0.0); |
| |
| double maxChange = dTheta.MaxAbs(); |
| double beta = MaxAngleJtranspose / maxChange; |
| dTheta *= min(alpha, beta); |
| } |
| |
| void Jacobian::CalcDeltaThetasPseudoinverse() |
| { |
| MatrixRmn &J = const_cast<MatrixRmn &>(Jend); |
| |
| |
| MatrixRmn J1; |
| J1.SetSize(2, J.getNumColumns()); |
| |
| for (int i = 0; i < 2; i++) |
| for (int j = 0; j < J.getNumColumns(); j++) |
| J1.Set(i, j, J.Get(i, j)); |
| |
| |
| VectorRn dS1(2); |
| |
| for (int i = 0; i < 2; i++) |
| dS1.Set(i, dS.Get(i)); |
| |
| |
| MatrixRmn U, V; |
| VectorRn w; |
| |
| U.SetSize(J1.getNumRows(), J1.getNumRows()); |
| w.SetLength(min(J1.getNumRows(), J1.getNumColumns())); |
| V.SetSize(J1.getNumColumns(), J1.getNumColumns()); |
| |
| J1.ComputeSVD(U, w, V); |
| |
| |
| assert(J1.DebugCheckSVD(U, w, V)); |
| |
| |
| |
| |
| |
| double pseudoInverseThreshold = PseudoInverseThresholdFactor * w.MaxAbs(); |
| |
| long diagLength = w.GetLength(); |
| double *wPtr = w.GetPtr(); |
| dTheta.SetZero(); |
| for (long i = 0; i < diagLength; i++) { |
| double dotProdCol = U.DotProductColumn(dS1, i); |
| double alpha = *(wPtr++); |
| if (fabs(alpha) > pseudoInverseThreshold) { |
| alpha = 1.0 / alpha; |
| MatrixRmn::AddArrayScale(V.getNumRows(), V.GetColumnPtr(i), 1, dTheta.GetPtr(), 1, dotProdCol * alpha); |
| } |
| } |
| |
| MatrixRmn JcurrentPinv(V.getNumRows(), J1.getNumRows()); |
| MatrixRmn JProjPre(JcurrentPinv.getNumRows(), J1.getNumColumns()); |
| if (skeleton->getNumEffector() > 1) { |
| |
| MatrixRmn VD(V.getNumRows(), J1.getNumRows()); |
| |
| double *wPtr = w.GetPtr(); |
| pseudoInverseThreshold = PseudoInverseThresholdFactor * w.MaxAbs(); |
| for (int j = 0; j < VD.getNumColumns(); j++) { |
| double *VPtr = V.GetColumnPtr(j); |
| double alpha = *(wPtr++); |
| for (int i = 0; i < V.getNumRows(); i++) { |
| if (fabs(alpha) > pseudoInverseThreshold) { |
| double entry = *(VPtr++); |
| VD.Set(i, j, entry * (1.0 / alpha)); |
| } |
| } |
| } |
| MatrixRmn::MultiplyTranspose(VD, U, JcurrentPinv); |
| |
| |
| MatrixRmn::Multiply(JcurrentPinv, J1, JProjPre); |
| |
| for (int j = 0; j < JProjPre.getNumColumns(); j++) |
| for (int i = 0; i < JProjPre.getNumRows(); i++) { |
| double temp = JProjPre.Get(i, j); |
| JProjPre.Set(i, j, -1.0 * temp); |
| } |
| JProjPre.AddToDiagonal(diagMatIdentity); |
| } |
| |
| |
| for (int i = 1; i < skeleton->getNumEffector(); i++) { |
| |
| MatrixRmn Jcurrent(2, J.getNumColumns()); |
| for (int j = 0; j < J.getNumColumns(); j++) |
| for (int k = 0; k < 2; k++) |
| Jcurrent.Set(k, j, J.Get(k + 2 * i, j)); |
| |
| |
| VectorRn dScurrent(2); |
| for (int k = 0; k < 2; k++) |
| dScurrent.Set(k, dS.Get(k + 2 * i)); |
| |
| |
| MatrixRmn Jdst(Jcurrent.getNumRows(), JProjPre.getNumColumns()); |
| MatrixRmn::Multiply(Jcurrent, JProjPre, Jdst); |
| |
| |
| MatrixRmn UU(Jdst.getNumRows(), Jdst.getNumRows()), VV(Jdst.getNumColumns(), Jdst.getNumColumns()); |
| VectorRn ww(min(Jdst.getNumRows(), Jdst.getNumColumns())); |
| |
| Jdst.ComputeSVD(UU, ww, VV); |
| assert(Jdst.DebugCheckSVD(UU, ww, VV)); |
| |
| MatrixRmn VVD(VV.getNumRows(), J1.getNumRows()); |
| VVD.SetZero(); |
| pseudoInverseThreshold = PseudoInverseThresholdFactor * ww.MaxAbs(); |
| double *wwPtr = ww.GetPtr(); |
| for (int j = 0; j < VVD.getNumColumns(); j++) { |
| double *VVPtr = VV.GetColumnPtr(j); |
| double alpha = 50 * (*(wwPtr++)); |
| for (int i = 0; i < VV.getNumRows(); i++) { |
| if (fabs(alpha) > 100 * pseudoInverseThreshold) { |
| double entry = *(VVPtr++); |
| VVD.Set(i, j, entry * (1.0 / alpha)); |
| } |
| } |
| } |
| MatrixRmn JdstPinv(VV.getNumRows(), J1.getNumRows()); |
| MatrixRmn::MultiplyTranspose(VVD, UU, JdstPinv); |
| |
| VectorRn dTemp(J1.getNumRows()); |
| Jcurrent.Multiply(dTheta, dTemp); |
| |
| VectorRn dTemp2(dScurrent.GetLength()); |
| for (int k = 0; k < dScurrent.GetLength(); k++) |
| dTemp2[k] = dScurrent[k] - dTemp[k]; |
| |
| |
| VectorRn dThetaCurrent(JdstPinv.getNumRows()); |
| JdstPinv.Multiply(dTemp2, dThetaCurrent); |
| for (int k = 0; k < dTheta.GetLength(); k++) |
| dTheta[k] += dThetaCurrent[k]; |
| |
| |
| |
| |
| Jcurrent.ComputeSVD(U, w, V); |
| assert(Jcurrent.DebugCheckSVD(U, w, V)); |
| |
| MatrixRmn VD(V.getNumRows(), Jcurrent.getNumRows()); |
| |
| double *wPtr = w.GetPtr(); |
| pseudoInverseThreshold = PseudoInverseThresholdFactor * w.MaxAbs(); |
| for (int j = 0; j < VVD.getNumColumns(); j++) { |
| double *VPtr = V.GetColumnPtr(j); |
| double alpha = *(wPtr++); |
| for (int i = 0; i < V.getNumRows(); i++) { |
| if (fabs(alpha) > pseudoInverseThreshold) { |
| double entry = *(VPtr++); |
| VD.Set(i, j, entry * (1.0 / alpha)); |
| } |
| } |
| } |
| MatrixRmn::MultiplyTranspose(VD, U, JcurrentPinv); |
| |
| |
| MatrixRmn::Multiply(JcurrentPinv, Jcurrent, JProjPre); |
| |
| for (int j = 0; j < JProjPre.getNumColumns(); j++) |
| for (int k = 0; k < JProjPre.getNumRows(); k++) { |
| double temp = JProjPre.Get(k, j); |
| JProjPre.Set(k, j, -1.0 * temp); |
| } |
| JProjPre.AddToDiagonal(diagMatIdentity); |
| } |
| |
| |
| |
| |
| |
| |
| |
| double maxChange = 10 * dTheta.MaxAbs(); |
| if (maxChange > MaxAnglePseudoinverse) { |
| dTheta *= MaxAnglePseudoinverse / maxChange; |
| } |
| } |
| |
| void Jacobian::CalcDeltaThetasDLS() |
| { |
| const MatrixRmn &J = Jend; |
| |
| MatrixRmn::MultiplyTranspose(J, J, U); |
| |
| U.AddToDiagonal(DampingLambdaSqV); |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| U.Solve(dS, &dT); |
| J.MultiplyTranspose(dT, dTheta); |
| |
| |
| double maxChange = 100 * dTheta.MaxAbs(); |
| if (maxChange > MaxAngleDLS) { |
| dTheta *= MaxAngleDLS / maxChange; |
| } |
| } |
| |
| void Jacobian::CalcDeltaThetasDLSwithSVD() |
| { |
| const MatrixRmn &J = Jend; |
| |
| J.ComputeSVD(U, w, V); |
| |
| |
| assert(J.DebugCheckSVD(U, w, V)); |
| |
| |
| |
| |
| long diagLength = w.GetLength(); |
| double *wPtr = w.GetPtr(); |
| dTheta.SetZero(); |
| for (long i = 0; i < diagLength; i++) { |
| double dotProdCol = U.DotProductColumn(dS, i); |
| double alpha = *(wPtr++); |
| alpha = alpha / (Square(alpha) + DampingLambdaSq); |
| MatrixRmn::AddArrayScale(V.getNumRows(), V.GetColumnPtr(i), 1, dTheta.GetPtr(), 1, dotProdCol * alpha); |
| } |
| |
| |
| double maxChange = dTheta.MaxAbs(); |
| if (maxChange > MaxAngleDLS) { |
| dTheta *= MaxAngleDLS / maxChange; |
| } |
| } |
| |
| void Jacobian::CalcDeltaThetasSDLS() |
| { |
| const MatrixRmn &J = Jend; |
| |
| |
| |
| J.ComputeSVD(U, w, V); |
| |
| |
| assert(J.DebugCheckSVD(U, w, V)); |
| |
| |
| |
| int nRows = J.getNumRows(); |
| int numEndEffectors = skeleton->getNumEffector(); |
| int nCols = J.getNumColumns(); |
| dTheta.SetZero(); |
| |
| |
| long i; |
| const double *jx = J.GetPtr(); |
| double *jnx = Jnorms.GetPtr(); |
| for (i = nCols * numEndEffectors; i > 0; i--) { |
| double accumSq = Square(*(jx++)); |
| accumSq += Square(*(jx++)); |
| accumSq += Square(*(jx++)); |
| *(jnx++) = sqrt(accumSq); |
| } |
| |
| |
| CalcdTClampedFromdS(); |
| |
| |
| for (i = 0; i < nRows; i++) { |
| |
| double wiInv = w[i]; |
| if (NearZero(wiInv, 1.0e-10)) { |
| continue; |
| } |
| wiInv = 1.0 / wiInv; |
| |
| double N = 0.0; |
| double alpha = 0.0; |
| |
| const double *dTx = dT.GetPtr(); |
| const double *ux = U.GetColumnPtr(i); |
| long j; |
| for (j = numEndEffectors; j > 0; j--) { |
| double tmp; |
| alpha += (*ux) * (*(dTx++)); |
| tmp = Square(*(ux++)); |
| alpha += (*ux) * (*(dTx++)); |
| tmp += Square(*(ux++)); |
| alpha += (*ux) * (*(dTx++)); |
| tmp += Square(*(ux++)); |
| N += sqrt(tmp); |
| } |
| |
| |
| |
| double M = 0.0; |
| double *vx = V.GetColumnPtr(i); |
| jnx = Jnorms.GetPtr(); |
| for (j = nCols; j > 0; j--) { |
| double accum = 0.0; |
| for (long k = numEndEffectors; k > 0; k--) { |
| accum += *(jnx++); |
| } |
| M += fabs((*(vx++))) * accum; |
| } |
| M *= fabs(wiInv); |
| |
| double gamma = MaxAngleSDLS; |
| if (N < M) { |
| gamma *= N / M; |
| } |
| |
| |
| double scale = alpha * wiInv; |
| dPreTheta.LoadScaled(V.GetColumnPtr(i), scale); |
| |
| double max = dPreTheta.MaxAbs(); |
| double rescale = (gamma) / (gamma + max); |
| dTheta.AddScaled(dPreTheta, rescale); |
| |
| |
| |
| |
| |
| |
| } |
| |
| |
| double maxChange = dTheta.MaxAbs(); |
| if (maxChange > 100 * MaxAngleSDLS) { |
| dTheta *= MaxAngleSDLS / (MaxAngleSDLS + maxChange); |
| |
| } |
| } |
| |
| void Jacobian::CalcdTClampedFromdS() |
| { |
| long len = dS.GetLength(); |
| long j = 0; |
| for (long i = 0; i < len; i += 2, j++) { |
| double normSq = Square(dS[i]) + Square(dS[i + 1]); |
| if (normSq > Square(dSclamp[j])) { |
| double factor = dSclamp[j] / sqrt(normSq); |
| dT[i] = dS[i] * factor; |
| dT[i + 1] = dS[i + 1] * factor; |
| |
| } else { |
| dT[i] = dS[i]; |
| dT[i + 1] = dS[i + 1]; |
| |
| } |
| } |
| } |
| |
| void Jacobian::UpdatedSClampValue() |
| { |
| |
| TPointD temp; |
| |
| int numNode = skeleton->getNodeCount(); |
| for (int i = 0; i < numNode; i++) { |
| IKNode *n = skeleton->getNode(i); |
| if (n->IsEffector()) { |
| int i = n->getEffectorNum(); |
| const TPointD &targetPos = target[i]; |
| |
| |
| |
| temp = targetPos; |
| temp -= n->GetS(); |
| double normSi = sqrt(Square(dS[i]) + Square(dS[i + 1])); |
| double norma = sqrt(temp.x * temp.x + temp.y * temp.y); |
| double changedDist = norma - normSi; |
| |
| if (changedDist > 0.0) { |
| dSclamp[i] = BaseMaxTargetDist + changedDist; |
| } else { |
| dSclamp[i] = BaseMaxTargetDist; |
| } |
| } |
| } |
| } |
| |