| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| #ifndef _HOMMATRIX_TEMPLATE_HXX |
| #define _HOMMATRIX_TEMPLATE_HXX |
| |
| #include <sal/types.h> |
| #include <basegfx/numeric/ftools.hxx> |
| #include <math.h> |
| #include <string.h> |
| |
| namespace basegfx |
| { |
| namespace internal |
| { |
| |
| inline double implGetDefaultValue(sal_uInt16 nRow, sal_uInt16 nColumn) |
| { |
| if(nRow == nColumn) |
| return 1.0; |
| return 0.0; |
| } |
| |
| template < unsigned int _RowSize > class ImplMatLine |
| { |
| enum { RowSize = _RowSize }; |
| |
| double mfValue[RowSize]; |
| |
| public: |
| ImplMatLine() |
| { |
| } |
| |
| ImplMatLine(sal_uInt16 nRow, ImplMatLine< RowSize >* pToBeCopied = 0L) |
| { |
| if(pToBeCopied) |
| { |
| memcpy(&mfValue, pToBeCopied, sizeof(double) * RowSize); |
| } |
| else |
| { |
| for(sal_uInt16 a(0); a < RowSize; a++) |
| { |
| mfValue[a] = implGetDefaultValue(nRow, a); |
| } |
| } |
| } |
| |
| double get(sal_uInt16 nColumn) const |
| { |
| return mfValue[nColumn]; |
| } |
| |
| void set(sal_uInt16 nColumn, const double& rValue) |
| { |
| mfValue[nColumn] = rValue; |
| } |
| }; |
| |
| template < unsigned int _RowSize > class ImplHomMatrixTemplate |
| { |
| enum { RowSize = _RowSize }; |
| |
| ImplMatLine< RowSize > maLine[RowSize - 1]; |
| ImplMatLine< RowSize >* mpLine; |
| |
| public: |
| // Is last line used? |
| bool isLastLineDefault() const |
| { |
| if(!mpLine) |
| return true; |
| |
| for(sal_uInt16 a(0); a < RowSize; a++) |
| { |
| const double fDefault(implGetDefaultValue((RowSize - 1), a)); |
| const double fLineValue(mpLine->get(a)); |
| |
| if(!::basegfx::fTools::equal(fDefault, fLineValue)) |
| { |
| return false; |
| } |
| } |
| |
| // reset last line, it equals default |
| delete ((ImplHomMatrixTemplate< RowSize >*)this)->mpLine; |
| ((ImplHomMatrixTemplate< RowSize >*)this)->mpLine = 0L; |
| |
| return true; |
| } |
| |
| ImplHomMatrixTemplate() |
| : mpLine(0L) |
| { |
| // complete initialization with identity matrix, all lines |
| // were initialized with a trailing 1 followed by 0's. |
| for(sal_uInt16 a(0); a < RowSize-1; a++) |
| { |
| for(sal_uInt16 b(0); b < RowSize; b++) |
| maLine[a].set(b, implGetDefaultValue(a, b) ); |
| } |
| } |
| |
| ImplHomMatrixTemplate(const ImplHomMatrixTemplate& rToBeCopied) |
| : mpLine(0L) |
| { |
| // complete initialization using copy |
| for(sal_uInt16 a(0); a < (RowSize - 1); a++) |
| { |
| memcpy(&maLine[a], &rToBeCopied.maLine[a], sizeof(ImplMatLine< RowSize >)); |
| } |
| |
| if(rToBeCopied.mpLine) |
| { |
| mpLine = new ImplMatLine< RowSize >((RowSize - 1), rToBeCopied.mpLine); |
| } |
| } |
| |
| ~ImplHomMatrixTemplate() |
| { |
| if(mpLine) |
| { |
| delete mpLine; |
| } |
| } |
| |
| sal_uInt16 getEdgeLength() const { return RowSize; } |
| |
| double get(sal_uInt16 nRow, sal_uInt16 nColumn) const |
| { |
| if(nRow < (RowSize - 1)) |
| { |
| return maLine[nRow].get(nColumn); |
| } |
| |
| if(mpLine) |
| { |
| return mpLine->get(nColumn); |
| } |
| |
| return implGetDefaultValue((RowSize - 1), nColumn); |
| } |
| |
| void set(sal_uInt16 nRow, sal_uInt16 nColumn, const double& rValue) |
| { |
| if(nRow < (RowSize - 1)) |
| { |
| maLine[nRow].set(nColumn, rValue); |
| } |
| else if(mpLine) |
| { |
| mpLine->set(nColumn, rValue); |
| } |
| else |
| { |
| const double fDefault(implGetDefaultValue((RowSize - 1), nColumn)); |
| |
| if(!::basegfx::fTools::equal(fDefault, rValue)) |
| { |
| mpLine = new ImplMatLine< RowSize >((RowSize - 1), 0L); |
| mpLine->set(nColumn, rValue); |
| } |
| } |
| } |
| |
| void testLastLine() |
| { |
| if(mpLine) |
| { |
| bool bNecessary(false); |
| |
| for(sal_uInt16 a(0);!bNecessary && a < RowSize; a++) |
| { |
| const double fDefault(implGetDefaultValue((RowSize - 1), a)); |
| const double fLineValue(mpLine->get(a)); |
| |
| if(!::basegfx::fTools::equal(fDefault, fLineValue)) |
| { |
| bNecessary = true; |
| } |
| } |
| |
| if(!bNecessary) |
| { |
| delete mpLine; |
| mpLine = 0L; |
| } |
| } |
| } |
| |
| // Left-upper decompositon |
| bool ludcmp(sal_uInt16 nIndex[], sal_Int16& nParity) |
| { |
| double fBig, fSum, fDum; |
| double fStorage[RowSize]; |
| sal_uInt16 a, b, c; |
| |
| // #i30874# Initialize nAMax (compiler warns) |
| sal_uInt16 nAMax = 0; |
| |
| nParity = 1; |
| |
| // Calc the max of each line. If a line is empty, |
| // stop immediately since matrix is not invertible then. |
| for(a = 0; a < RowSize; a++) |
| { |
| fBig = 0.0; |
| |
| for(b = 0; b < RowSize; b++) |
| { |
| double fTemp(fabs(get(a, b))); |
| |
| if(::basegfx::fTools::more(fTemp, fBig)) |
| { |
| fBig = fTemp; |
| } |
| } |
| |
| if(::basegfx::fTools::equalZero(fBig)) |
| { |
| return false; |
| } |
| |
| fStorage[a] = 1.0 / fBig; |
| } |
| |
| // start normalizing |
| for(b = 0; b < RowSize; b++) |
| { |
| for(a = 0; a < b; a++) |
| { |
| fSum = get(a, b); |
| |
| for(c = 0; c < a; c++) |
| { |
| fSum -= get(a, c) * get(c, b); |
| } |
| |
| set(a, b, fSum); |
| } |
| |
| fBig = 0.0; |
| |
| for(a = b; a < RowSize; a++) |
| { |
| fSum = get(a, b); |
| |
| for(c = 0; c < b; c++) |
| { |
| fSum -= get(a, c) * get(c, b); |
| } |
| |
| set(a, b, fSum); |
| fDum = fStorage[a] * fabs(fSum); |
| |
| if(::basegfx::fTools::moreOrEqual(fDum, fBig)) |
| { |
| fBig = fDum; |
| nAMax = a; |
| } |
| } |
| |
| if(b != nAMax) |
| { |
| for(c = 0; c < RowSize; c++) |
| { |
| fDum = get(nAMax, c); |
| set(nAMax, c, get(b, c)); |
| set(b, c, fDum); |
| } |
| |
| nParity = -nParity; |
| fStorage[nAMax] = fStorage[b]; |
| } |
| |
| nIndex[b] = nAMax; |
| |
| // here the failure of precision occurs |
| const double fValBB(fabs(get(b, b))); |
| |
| if(::basegfx::fTools::equalZero(fValBB)) |
| { |
| return false; |
| } |
| |
| if(b != (RowSize - 1)) |
| { |
| fDum = 1.0 / get(b, b); |
| |
| for(a = b + 1; a < RowSize; a++) |
| { |
| set(a, b, get(a, b) * fDum); |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| void lubksb(const sal_uInt16 nIndex[], double fRow[]) const |
| { |
| sal_uInt16 b, ip; |
| sal_Int16 a, a2 = -1; |
| double fSum; |
| |
| for(a = 0; a < RowSize; a++) |
| { |
| ip = nIndex[a]; |
| fSum = fRow[ip]; |
| fRow[ip] = fRow[a]; |
| |
| if(a2 >= 0) |
| { |
| for(b = a2; b < a; b++) |
| { |
| fSum -= get(a, b) * fRow[b]; |
| } |
| } |
| else if(!::basegfx::fTools::equalZero(fSum)) |
| { |
| a2 = a; |
| } |
| |
| fRow[a] = fSum; |
| } |
| |
| for(a = (RowSize - 1); a >= 0; a--) |
| { |
| fSum = fRow[a]; |
| |
| for(b = a + 1; b < RowSize; b++) |
| { |
| fSum -= get(a, b) * fRow[b]; |
| } |
| |
| const double fValueAA(get(a, a)); |
| |
| if(!::basegfx::fTools::equalZero(fValueAA)) |
| { |
| fRow[a] = fSum / get(a, a); |
| } |
| } |
| } |
| |
| bool isIdentity() const |
| { |
| // last line needs no testing if not existing |
| const sal_uInt16 nMaxLine( |
| sal::static_int_cast<sal_uInt16>(mpLine ? RowSize : (RowSize - 1)) ); |
| |
| for(sal_uInt16 a(0); a < nMaxLine; a++) |
| { |
| for(sal_uInt16 b(0); b < RowSize; b++) |
| { |
| const double fDefault(implGetDefaultValue(a, b)); |
| const double fValueAB(get(a, b)); |
| |
| if(!::basegfx::fTools::equal(fDefault, fValueAB)) |
| { |
| return false; |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| bool isInvertible() const |
| { |
| ImplHomMatrixTemplate aWork(*this); |
| sal_uInt16 nIndex[RowSize]; |
| sal_Int16 nParity; |
| |
| return aWork.ludcmp(nIndex, nParity); |
| } |
| |
| bool isNormalized() const |
| { |
| if(!mpLine) |
| return true; |
| |
| const double fHomValue(get((RowSize - 1), (RowSize - 1))); |
| |
| if(::basegfx::fTools::equalZero(fHomValue)) |
| { |
| return true; |
| } |
| |
| const double fOne(1.0); |
| |
| if(::basegfx::fTools::equal(fOne, fHomValue)) |
| { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| void doInvert(const ImplHomMatrixTemplate& rWork, const sal_uInt16 nIndex[]) |
| { |
| double fArray[RowSize]; |
| |
| for(sal_uInt16 a(0); a < RowSize; a++) |
| { |
| // prepare line |
| sal_uInt16 b; |
| for( b = 0; b < RowSize; b++) |
| { |
| fArray[b] = implGetDefaultValue(a, b); |
| } |
| |
| // expand line |
| rWork.lubksb(nIndex, fArray); |
| |
| // copy line transposed to this matrix |
| for( b = 0; b < RowSize; b++) |
| { |
| set(b, a, fArray[b]); |
| } |
| } |
| |
| // evtl. get rid of last matrix line |
| testLastLine(); |
| } |
| |
| void doNormalize() |
| { |
| if(mpLine) |
| { |
| const double fHomValue(get((RowSize - 1), (RowSize - 1))); |
| |
| for(sal_uInt16 a(0); a < RowSize; a++) |
| { |
| for(sal_uInt16 b(0); b < RowSize; b++) |
| { |
| set(a, b, get(a, b) / fHomValue); |
| } |
| } |
| |
| // evtl. get rid of last matrix line |
| testLastLine(); |
| } |
| } |
| |
| double doDeterminant() const |
| { |
| ImplHomMatrixTemplate aWork(*this); |
| sal_uInt16 nIndex[RowSize]; |
| sal_Int16 nParity; |
| double fRetval(0.0); |
| |
| if(aWork.ludcmp(nIndex, nParity)) |
| { |
| fRetval = (double)nParity; |
| |
| // last line needs no multiply if not existing; default value would be 1. |
| const sal_uInt16 nMaxLine( |
| sal::static_int_cast<sal_uInt16>(aWork.mpLine ? RowSize : (RowSize - 1)) ); |
| |
| for(sal_uInt16 a(0); a < nMaxLine; a++) |
| { |
| fRetval *= aWork.get(a, a); |
| } |
| } |
| |
| return fRetval; |
| } |
| |
| double doTrace() const |
| { |
| double fTrace = (mpLine) ? 0.0 : 1.0; |
| const sal_uInt16 nMaxLine( |
| sal::static_int_cast<sal_uInt16>(mpLine ? RowSize : (RowSize - 1)) ); |
| |
| for(sal_uInt16 a(0); a < nMaxLine; a++) |
| { |
| fTrace += get(a, a); |
| } |
| |
| return fTrace; |
| } |
| |
| void doTranspose() |
| { |
| for(sal_uInt16 a(0); a < (RowSize - 1); a++) |
| { |
| for(sal_uInt16 b(a + 1); b < RowSize; b++) |
| { |
| const double fTemp(get(a, b)); |
| set(a, b, get(b, a)); |
| set(b, a, fTemp); |
| } |
| } |
| |
| testLastLine(); |
| } |
| |
| void doAddMatrix(const ImplHomMatrixTemplate& rMat) |
| { |
| for(sal_uInt16 a(0); a < RowSize; a++) |
| { |
| for(sal_uInt16 b(0); b < RowSize; b++) |
| { |
| set(a, b, get(a, b) + rMat.get(a, b)); |
| } |
| } |
| |
| testLastLine(); |
| } |
| |
| void doSubMatrix(const ImplHomMatrixTemplate& rMat) |
| { |
| for(sal_uInt16 a(0); a < RowSize; a++) |
| { |
| for(sal_uInt16 b(0); b < RowSize; b++) |
| { |
| set(a, b, get(a, b) - rMat.get(a, b)); |
| } |
| } |
| |
| testLastLine(); |
| } |
| |
| void doMulMatrix(const double& rfValue) |
| { |
| for(sal_uInt16 a(0); a < RowSize; a++) |
| { |
| for(sal_uInt16 b(0); b < RowSize; b++) |
| { |
| set(a, b, get(a, b) * rfValue); |
| } |
| } |
| |
| testLastLine(); |
| } |
| |
| void doMulMatrix(const ImplHomMatrixTemplate& rMat) |
| { |
| // create a copy as source for the original values |
| const ImplHomMatrixTemplate aCopy(*this); |
| |
| // TODO: maybe optimize cases where last line is [0 0 1]. |
| |
| double fValue(0.0); |
| |
| for(sal_uInt16 a(0); a < RowSize; ++a) |
| { |
| for(sal_uInt16 b(0); b < RowSize; ++b) |
| { |
| fValue = 0.0; |
| |
| for(sal_uInt16 c(0); c < RowSize; ++c) |
| fValue += aCopy.get(c, b) * rMat.get(a, c); |
| |
| set(a, b, fValue); |
| } |
| } |
| |
| testLastLine(); |
| } |
| |
| bool isEqual(const ImplHomMatrixTemplate& rMat) const |
| { |
| const sal_uInt16 nMaxLine( |
| sal::static_int_cast<sal_uInt16>((mpLine || rMat.mpLine) ? RowSize : (RowSize - 1)) ); |
| |
| for(sal_uInt16 a(0); a < nMaxLine; a++) |
| { |
| for(sal_uInt16 b(0); b < RowSize; b++) |
| { |
| const double fValueA(get(a, b)); |
| const double fValueB(rMat.get(a, b)); |
| |
| if(!::basegfx::fTools::equal(fValueA, fValueB)) |
| { |
| return false; |
| } |
| } |
| } |
| |
| return true; |
| } |
| }; |
| |
| } // namespace internal |
| } // namespace basegfx |
| |
| #endif /* _HOMMATRIX_TEMPLATE_HXX */ |