1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 #ifndef _HOMMATRIX_TEMPLATE_HXX
25 #define _HOMMATRIX_TEMPLATE_HXX
26 
27 #include <sal/types.h>
28 #include <basegfx/numeric/ftools.hxx>
29 #include <math.h>
30 #include <string.h>
31 
32 namespace basegfx
33 {
34     namespace internal
35     {
36 
implGetDefaultValue(sal_uInt16 nRow,sal_uInt16 nColumn)37         inline double implGetDefaultValue(sal_uInt16 nRow, sal_uInt16 nColumn)
38         {
39             if(nRow == nColumn)
40                 return 1.0;
41             return 0.0;
42         }
43 
44         template < unsigned int _RowSize > class ImplMatLine
45         {
46             enum { RowSize = _RowSize };
47 
48             double											mfValue[RowSize];
49 
50         public:
ImplMatLine()51             ImplMatLine()
52             {
53             }
54 
ImplMatLine(sal_uInt16 nRow,ImplMatLine<RowSize> * pToBeCopied=0L)55             ImplMatLine(sal_uInt16 nRow, ImplMatLine< RowSize >* pToBeCopied = 0L)
56             {
57                 if(pToBeCopied)
58                 {
59                     memcpy(&mfValue, pToBeCopied, sizeof(double) * RowSize);
60                 }
61                 else
62                 {
63                     for(sal_uInt16 a(0); a < RowSize; a++)
64                     {
65                         mfValue[a] = implGetDefaultValue(nRow, a);
66                     }
67                 }
68             }
69 
get(sal_uInt16 nColumn) const70             double get(sal_uInt16 nColumn) const
71             {
72                 return mfValue[nColumn];
73             }
74 
set(sal_uInt16 nColumn,const double & rValue)75             void set(sal_uInt16 nColumn, const double& rValue)
76             {
77                 mfValue[nColumn] = rValue;
78             }
79         };
80 
81         template < unsigned int _RowSize > class ImplHomMatrixTemplate
82         {
83             enum { RowSize = _RowSize };
84 
85             ImplMatLine< RowSize >							maLine[RowSize - 1];
86             ImplMatLine< RowSize >*							mpLine;
87 
88         public:
89             // Is last line used?
isLastLineDefault() const90             bool isLastLineDefault() const
91             {
92                 if(!mpLine)
93                     return true;
94 
95                 for(sal_uInt16 a(0); a < RowSize; a++)
96                 {
97                     const double fDefault(implGetDefaultValue((RowSize - 1), a));
98                     const double fLineValue(mpLine->get(a));
99 
100                     if(!::basegfx::fTools::equal(fDefault, fLineValue))
101                     {
102                         return false;
103                     }
104                 }
105 
106                 // reset last line, it equals default
107                 delete ((ImplHomMatrixTemplate< RowSize >*)this)->mpLine;
108                 ((ImplHomMatrixTemplate< RowSize >*)this)->mpLine = 0L;
109 
110                 return true;
111             }
112 
ImplHomMatrixTemplate()113             ImplHomMatrixTemplate()
114                 :	mpLine(0L)
115             {
116                 // complete initialization with identity matrix, all lines
117                 // were initialized with a trailing 1 followed by 0's.
118                 for(sal_uInt16 a(0); a < RowSize-1; a++)
119                 {
120                     for(sal_uInt16 b(0); b < RowSize; b++)
121                         maLine[a].set(b, implGetDefaultValue(a, b) );
122                 }
123             }
124 
ImplHomMatrixTemplate(const ImplHomMatrixTemplate & rToBeCopied)125             ImplHomMatrixTemplate(const ImplHomMatrixTemplate& rToBeCopied)
126                 :	mpLine(0L)
127             {
128                 // complete initialization using copy
129                 for(sal_uInt16 a(0); a < (RowSize - 1); a++)
130                 {
131                     memcpy(&maLine[a], &rToBeCopied.maLine[a], sizeof(ImplMatLine< RowSize >));
132                 }
133 
134                 if(rToBeCopied.mpLine)
135                 {
136                     mpLine = new ImplMatLine< RowSize >((RowSize - 1), rToBeCopied.mpLine);
137                 }
138             }
139 
~ImplHomMatrixTemplate()140             ~ImplHomMatrixTemplate()
141             {
142                 if(mpLine)
143                 {
144                     delete mpLine;
145                 }
146             }
147 
getEdgeLength() const148             sal_uInt16 getEdgeLength() const { return RowSize; }
149 
get(sal_uInt16 nRow,sal_uInt16 nColumn) const150             double get(sal_uInt16 nRow, sal_uInt16 nColumn) const
151             {
152                 if(nRow < (RowSize - 1))
153                 {
154                     return maLine[nRow].get(nColumn);
155                 }
156 
157                 if(mpLine)
158                 {
159                     return mpLine->get(nColumn);
160                 }
161 
162                 return implGetDefaultValue((RowSize - 1), nColumn);
163             }
164 
set(sal_uInt16 nRow,sal_uInt16 nColumn,const double & rValue)165             void set(sal_uInt16 nRow, sal_uInt16 nColumn, const double& rValue)
166             {
167                 if(nRow < (RowSize - 1))
168                 {
169                     maLine[nRow].set(nColumn, rValue);
170                 }
171                 else if(mpLine)
172                 {
173                     mpLine->set(nColumn, rValue);
174                 }
175                 else
176                 {
177                     const double fDefault(implGetDefaultValue((RowSize - 1), nColumn));
178 
179                     if(!::basegfx::fTools::equal(fDefault, rValue))
180                     {
181                         mpLine = new ImplMatLine< RowSize >((RowSize - 1), 0L);
182                         mpLine->set(nColumn, rValue);
183                     }
184                 }
185             }
186 
testLastLine()187             void testLastLine()
188             {
189                 if(mpLine)
190                 {
191                     bool bNecessary(false);
192 
193                     for(sal_uInt16 a(0);!bNecessary && a < RowSize; a++)
194                     {
195                         const double fDefault(implGetDefaultValue((RowSize - 1), a));
196                         const double fLineValue(mpLine->get(a));
197 
198                         if(!::basegfx::fTools::equal(fDefault, fLineValue))
199                         {
200                             bNecessary = true;
201                         }
202                     }
203 
204                     if(!bNecessary)
205                     {
206                         delete mpLine;
207                         mpLine = 0L;
208                     }
209                 }
210             }
211 
212             // Left-upper decompositon
ludcmp(sal_uInt16 nIndex[],sal_Int16 & nParity)213             bool ludcmp(sal_uInt16 nIndex[], sal_Int16& nParity)
214             {
215                 double fBig, fSum, fDum;
216                 double fStorage[RowSize];
217                 sal_uInt16 a, b, c;
218 
219                 // #i30874# Initialize nAMax (compiler warns)
220                 sal_uInt16 nAMax = 0;
221 
222                 nParity = 1;
223 
224                 // Calc the max of each line. If a line is empty,
225                 // stop immediately since matrix is not invertible then.
226                 for(a = 0; a < RowSize; a++)
227                 {
228                     fBig = 0.0;
229 
230                     for(b = 0; b < RowSize; b++)
231                     {
232                         double fTemp(fabs(get(a, b)));
233 
234                         if(::basegfx::fTools::more(fTemp, fBig))
235                         {
236                             fBig = fTemp;
237                         }
238                     }
239 
240                     if(::basegfx::fTools::equalZero(fBig))
241                     {
242                         return false;
243                     }
244 
245                     fStorage[a] = 1.0 / fBig;
246                 }
247 
248                 // start normalizing
249                 for(b = 0; b < RowSize; b++)
250                 {
251                     for(a = 0; a < b; a++)
252                     {
253                         fSum = get(a, b);
254 
255                         for(c = 0; c < a; c++)
256                         {
257                             fSum -= get(a, c) * get(c, b);
258                         }
259 
260                         set(a, b, fSum);
261                     }
262 
263                     fBig = 0.0;
264 
265                     for(a = b; a < RowSize; a++)
266                     {
267                         fSum = get(a, b);
268 
269                         for(c = 0; c < b; c++)
270                         {
271                             fSum -= get(a, c) * get(c, b);
272                         }
273 
274                         set(a, b, fSum);
275                         fDum = fStorage[a] * fabs(fSum);
276 
277                         if(::basegfx::fTools::moreOrEqual(fDum, fBig))
278                         {
279                             fBig = fDum;
280                             nAMax = a;
281                         }
282                     }
283 
284                     if(b != nAMax)
285                     {
286                         for(c = 0; c < RowSize; c++)
287                         {
288                             fDum = get(nAMax, c);
289                             set(nAMax, c, get(b, c));
290                             set(b, c, fDum);
291                         }
292 
293                         nParity = -nParity;
294                         fStorage[nAMax] = fStorage[b];
295                     }
296 
297                     nIndex[b] = nAMax;
298 
299                     // here the failure of precision occurs
300                     const double fValBB(fabs(get(b, b)));
301 
302                     if(::basegfx::fTools::equalZero(fValBB))
303                     {
304                         return false;
305                     }
306 
307                     if(b != (RowSize - 1))
308                     {
309                         fDum = 1.0 / get(b, b);
310 
311                         for(a = b + 1; a < RowSize; a++)
312                         {
313                             set(a, b, get(a, b) * fDum);
314                         }
315                     }
316                 }
317 
318                 return true;
319             }
320 
lubksb(const sal_uInt16 nIndex[],double fRow[]) const321             void lubksb(const sal_uInt16 nIndex[], double fRow[]) const
322             {
323                 sal_uInt16 b, ip;
324                 sal_Int16 a, a2 = -1;
325                 double fSum;
326 
327                 for(a = 0; a < RowSize; a++)
328                 {
329                     ip = nIndex[a];
330                     fSum = fRow[ip];
331                     fRow[ip] = fRow[a];
332 
333                     if(a2 >= 0)
334                     {
335                         for(b = a2; b < a; b++)
336                         {
337                             fSum -= get(a, b) * fRow[b];
338                         }
339                     }
340                     else if(!::basegfx::fTools::equalZero(fSum))
341                     {
342                         a2 = a;
343                     }
344 
345                     fRow[a] = fSum;
346                 }
347 
348                 for(a = (RowSize - 1); a >= 0; a--)
349                 {
350                     fSum = fRow[a];
351 
352                     for(b = a + 1; b < RowSize; b++)
353                     {
354                         fSum -= get(a, b) * fRow[b];
355                     }
356 
357                     const double fValueAA(get(a, a));
358 
359                     if(!::basegfx::fTools::equalZero(fValueAA))
360                     {
361                         fRow[a] = fSum / get(a, a);
362                     }
363                 }
364             }
365 
isIdentity() const366             bool isIdentity() const
367             {
368                 // last line needs no testing if not existing
369                 const sal_uInt16 nMaxLine(
370                     sal::static_int_cast<sal_uInt16>(mpLine ? RowSize : (RowSize - 1)) );
371 
372                 for(sal_uInt16 a(0); a < nMaxLine; a++)
373                 {
374                     for(sal_uInt16 b(0); b < RowSize; b++)
375                     {
376                         const double fDefault(implGetDefaultValue(a, b));
377                         const double fValueAB(get(a, b));
378 
379                         if(!::basegfx::fTools::equal(fDefault, fValueAB))
380                         {
381                             return false;
382                         }
383                     }
384                 }
385 
386                 return true;
387             }
388 
isInvertible() const389             bool isInvertible() const
390             {
391                 ImplHomMatrixTemplate aWork(*this);
392                 sal_uInt16 nIndex[RowSize];
393                 sal_Int16 nParity;
394 
395                 return aWork.ludcmp(nIndex, nParity);
396             }
397 
isNormalized() const398             bool isNormalized() const
399             {
400                 if(!mpLine)
401                     return true;
402 
403                 const double fHomValue(get((RowSize - 1), (RowSize - 1)));
404 
405                 if(::basegfx::fTools::equalZero(fHomValue))
406                 {
407                     return true;
408                 }
409 
410                 const double fOne(1.0);
411 
412                 if(::basegfx::fTools::equal(fOne, fHomValue))
413                 {
414                     return true;
415                 }
416 
417                 return false;
418             }
419 
doInvert(const ImplHomMatrixTemplate & rWork,const sal_uInt16 nIndex[])420             void doInvert(const ImplHomMatrixTemplate& rWork, const sal_uInt16 nIndex[])
421             {
422                 double fArray[RowSize];
423 
424                 for(sal_uInt16 a(0); a < RowSize; a++)
425                 {
426                     // prepare line
427 		    sal_uInt16 b;
428                     for( b = 0; b < RowSize; b++)
429                     {
430                         fArray[b] = implGetDefaultValue(a, b);
431                     }
432 
433                     // expand line
434                     rWork.lubksb(nIndex, fArray);
435 
436                     // copy line transposed to this matrix
437                     for( b = 0; b < RowSize; b++)
438                     {
439                         set(b, a, fArray[b]);
440                     }
441                 }
442 
443                 // evtl. get rid of last matrix line
444                 testLastLine();
445             }
446 
doNormalize()447             void doNormalize()
448             {
449                 if(mpLine)
450                 {
451                     const double fHomValue(get((RowSize - 1), (RowSize - 1)));
452 
453                     for(sal_uInt16 a(0); a < RowSize; a++)
454                     {
455                         for(sal_uInt16 b(0); b < RowSize; b++)
456                         {
457                             set(a, b, get(a, b) / fHomValue);
458                         }
459                     }
460 
461                     // evtl. get rid of last matrix line
462                     testLastLine();
463                 }
464             }
465 
doDeterminant() const466             double doDeterminant() const
467             {
468                 ImplHomMatrixTemplate aWork(*this);
469                 sal_uInt16 nIndex[RowSize];
470                 sal_Int16 nParity;
471                 double fRetval(0.0);
472 
473                 if(aWork.ludcmp(nIndex, nParity))
474                 {
475                     fRetval = (double)nParity;
476 
477                     // last line needs no multiply if not existing; default value would be 1.
478                     const sal_uInt16 nMaxLine(
479                         sal::static_int_cast<sal_uInt16>(aWork.mpLine ? RowSize : (RowSize - 1)) );
480 
481                     for(sal_uInt16 a(0); a < nMaxLine; a++)
482                     {
483                         fRetval *= aWork.get(a, a);
484                     }
485                 }
486 
487                 return fRetval;
488             }
489 
doTrace() const490             double doTrace() const
491             {
492                 double fTrace = (mpLine) ? 0.0 : 1.0;
493                 const sal_uInt16 nMaxLine(
494                     sal::static_int_cast<sal_uInt16>(mpLine ? RowSize : (RowSize - 1)) );
495 
496                 for(sal_uInt16 a(0); a < nMaxLine; a++)
497                 {
498                     fTrace += get(a, a);
499                 }
500 
501                 return fTrace;
502             }
503 
doTranspose()504             void doTranspose()
505             {
506                 for(sal_uInt16 a(0); a < (RowSize - 1); a++)
507                 {
508                     for(sal_uInt16 b(a + 1); b < RowSize; b++)
509                     {
510                         const double fTemp(get(a, b));
511                         set(a, b, get(b, a));
512                         set(b, a, fTemp);
513                     }
514                 }
515 
516                 testLastLine();
517             }
518 
doAddMatrix(const ImplHomMatrixTemplate & rMat)519             void doAddMatrix(const ImplHomMatrixTemplate& rMat)
520             {
521                 for(sal_uInt16 a(0); a < RowSize; a++)
522                 {
523                     for(sal_uInt16 b(0); b < RowSize; b++)
524                     {
525                         set(a, b, get(a, b) + rMat.get(a, b));
526                     }
527                 }
528 
529                 testLastLine();
530             }
531 
doSubMatrix(const ImplHomMatrixTemplate & rMat)532             void doSubMatrix(const ImplHomMatrixTemplate& rMat)
533             {
534                 for(sal_uInt16 a(0); a < RowSize; a++)
535                 {
536                     for(sal_uInt16 b(0); b < RowSize; b++)
537                     {
538                         set(a, b, get(a, b) - rMat.get(a, b));
539                     }
540                 }
541 
542                 testLastLine();
543             }
544 
doMulMatrix(const double & rfValue)545             void doMulMatrix(const double& rfValue)
546             {
547                 for(sal_uInt16 a(0); a < RowSize; a++)
548                 {
549                     for(sal_uInt16 b(0); b < RowSize; b++)
550                     {
551                         set(a, b, get(a, b) * rfValue);
552                     }
553                 }
554 
555                 testLastLine();
556             }
557 
doMulMatrix(const ImplHomMatrixTemplate & rMat)558             void doMulMatrix(const ImplHomMatrixTemplate& rMat)
559             {
560                 // create a copy as source for the original values
561                 const ImplHomMatrixTemplate aCopy(*this);
562 
563                 // TODO: maybe optimize cases where last line is [0 0 1].
564 
565                 double fValue(0.0);
566 
567                 for(sal_uInt16 a(0); a < RowSize; ++a)
568                 {
569                     for(sal_uInt16 b(0); b < RowSize; ++b)
570                     {
571                         fValue = 0.0;
572 
573                         for(sal_uInt16 c(0); c < RowSize; ++c)
574                             fValue += aCopy.get(c, b) * rMat.get(a, c);
575 
576                         set(a, b, fValue);
577                     }
578                 }
579 
580                 testLastLine();
581             }
582 
isEqual(const ImplHomMatrixTemplate & rMat) const583             bool isEqual(const ImplHomMatrixTemplate& rMat) const
584             {
585                 const sal_uInt16 nMaxLine(
586                     sal::static_int_cast<sal_uInt16>((mpLine || rMat.mpLine) ? RowSize : (RowSize - 1)) );
587 
588                 for(sal_uInt16 a(0); a < nMaxLine; a++)
589                 {
590                     for(sal_uInt16 b(0); b < RowSize; b++)
591                     {
592                         const double fValueA(get(a, b));
593                         const double fValueB(rMat.get(a, b));
594 
595                         if(!::basegfx::fTools::equal(fValueA, fValueB))
596                         {
597                             return false;
598                         }
599                     }
600                 }
601 
602                 return true;
603             }
604         };
605 
606     } // namespace internal
607 } // namespace basegfx
608 
609 #endif /* _HOMMATRIX_TEMPLATE_HXX */
610