diff options
Diffstat (limited to '')
-rw-r--r-- | basegfx/source/inc/hommatrixtemplate.hxx | 525 |
1 files changed, 525 insertions, 0 deletions
diff --git a/basegfx/source/inc/hommatrixtemplate.hxx b/basegfx/source/inc/hommatrixtemplate.hxx new file mode 100644 index 000000000..a80df114c --- /dev/null +++ b/basegfx/source/inc/hommatrixtemplate.hxx @@ -0,0 +1,525 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#pragma once + +#include <sal/types.h> +#include <basegfx/numeric/ftools.hxx> +#include <cmath> +#include <string.h> + +#include <memory> + +namespace basegfx::internal + { + + inline double implGetDefaultValue(sal_uInt16 nRow, sal_uInt16 nColumn) + { + if(nRow == nColumn) + return 1.0; + return 0.0; + } + + template < sal_uInt16 RowSize > class ImplMatLine + { + double mfValue[RowSize]; + + public: + ImplMatLine() + { + } + + explicit ImplMatLine(sal_uInt16 nRow, ImplMatLine< RowSize >* pToBeCopied) + { + 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 < sal_uInt16 RowSize > class ImplHomMatrixTemplate + { + ImplMatLine< RowSize > maLine[RowSize - 1]; + std::unique_ptr<ImplMatLine< RowSize >> mutable 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 + mpLine.reset(); + + return true; + } + + ImplHomMatrixTemplate() + { + // 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) + { + operator=(rToBeCopied); + } + + ImplHomMatrixTemplate& operator=(const ImplHomMatrixTemplate& rToBeCopied) + { + if (this != &rToBeCopied) + { + // 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.reset( new ImplMatLine< RowSize >((RowSize - 1), rToBeCopied.mpLine.get()) ); + } + } + return *this; + } + + static sal_uInt16 getEdgeLength() { 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.reset(new ImplMatLine< RowSize >((RowSize - 1), nullptr)); + mpLine->set(nColumn, rValue); + } + } + } + + void testLastLine() + { + if(!mpLine) + return; + + 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) + { + mpLine.reset(); + } + } + + // Left-upper decomposition + 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); + } + + 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(); + } + + double doDeterminant() const + { + ImplHomMatrixTemplate aWork(*this); + sal_uInt16 nIndex[RowSize]; + sal_Int16 nParity; + double fRetval(0.0); + + if(aWork.ludcmp(nIndex, nParity)) + { + fRetval = static_cast<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; + } + + 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 basegfx::internal + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |