Coverage Report

Created: 2026-06-30 11:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/basegfx/source/matrix/b3dhommatrix.cxx
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
5
 * This Source Code Form is subject to the terms of the Mozilla Public
6
 * License, v. 2.0. If a copy of the MPL was not distributed with this
7
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
 *
9
 * This file incorporates work covered by the following license notice:
10
 *
11
 *   Licensed to the Apache Software Foundation (ASF) under one or more
12
 *   contributor license agreements. See the NOTICE file distributed
13
 *   with this work for additional information regarding copyright
14
 *   ownership. The ASF licenses this file to you under the Apache
15
 *   License, Version 2.0 (the "License"); you may not use this file
16
 *   except in compliance with the License. You may obtain a copy of
17
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18
 */
19
20
#include <basegfx/matrix/b3dhommatrix.hxx>
21
#include <basegfx/matrix/hommatrixtemplate.hxx>
22
#include <basegfx/vector/b3dvector.hxx>
23
#include <memory>
24
25
namespace basegfx
26
{
27
    typedef ::basegfx::internal::ImplHomMatrixTemplate< 4 >Impl3DHomMatrix_Base;
28
    class Impl3DHomMatrix : public Impl3DHomMatrix_Base
29
    {
30
    };
31
32
130k
    B3DHomMatrix::B3DHomMatrix() = default;
33
34
6.54k
    B3DHomMatrix::B3DHomMatrix(const B3DHomMatrix&) = default;
35
36
0
    B3DHomMatrix::B3DHomMatrix(B3DHomMatrix&&) = default;
37
38
137k
    B3DHomMatrix::~B3DHomMatrix() = default;
39
40
37.9k
    B3DHomMatrix& B3DHomMatrix::operator=(const B3DHomMatrix&) = default;
41
42
0
    B3DHomMatrix& B3DHomMatrix::operator=(B3DHomMatrix&&) = default;
43
44
    double B3DHomMatrix::get(sal_uInt16 nRow, sal_uInt16 nColumn) const
45
555k
    {
46
555k
        return mpImpl->get(nRow, nColumn);
47
555k
    }
48
49
    void B3DHomMatrix::set(sal_uInt16 nRow, sal_uInt16 nColumn, double fValue)
50
382k
    {
51
382k
        mpImpl->set(nRow, nColumn, fValue);
52
382k
    }
53
54
    bool B3DHomMatrix::isLastLineDefault() const
55
28.5k
    {
56
28.5k
        return mpImpl->isLastLineDefault();
57
28.5k
    }
58
59
    bool B3DHomMatrix::isIdentity() const
60
77.1k
    {
61
77.1k
        return mpImpl->isIdentity();
62
77.1k
    }
63
64
    void B3DHomMatrix::identity()
65
22.8k
    {
66
22.8k
        *mpImpl = Impl3DHomMatrix();
67
22.8k
    }
68
69
    void B3DHomMatrix::invert()
70
22.8k
    {
71
22.8k
        Impl3DHomMatrix aWork(*mpImpl);
72
22.8k
        std::unique_ptr<sal_uInt16[]> pIndex( new sal_uInt16[Impl3DHomMatrix_Base::getEdgeLength()] );
73
22.8k
        sal_Int16 nParity;
74
75
22.8k
        if(aWork.ludcmp(pIndex.get(), nParity))
76
22.8k
        {
77
22.8k
            mpImpl->doInvert(aWork, pIndex.get());
78
22.8k
        }
79
22.8k
    }
80
81
    double B3DHomMatrix::determinant() const
82
0
    {
83
0
        return mpImpl->doDeterminant();
84
0
    }
85
86
    B3DHomMatrix& B3DHomMatrix::operator+=(const B3DHomMatrix& rMat)
87
0
    {
88
0
        mpImpl->doAddMatrix(*rMat.mpImpl);
89
0
        return *this;
90
0
    }
91
92
    B3DHomMatrix& B3DHomMatrix::operator-=(const B3DHomMatrix& rMat)
93
0
    {
94
0
        mpImpl->doSubMatrix(*rMat.mpImpl);
95
0
        return *this;
96
0
    }
97
98
    B3DHomMatrix& B3DHomMatrix::operator*=(const B3DHomMatrix& rMat)
99
71.2k
    {
100
71.2k
        if(rMat.isIdentity())
101
68.2k
        {
102
            // multiply with identity, no change -> nothing to do
103
68.2k
        }
104
3.02k
        else if(isIdentity())
105
2.88k
        {
106
            // we are identity, result will be rMat -> assign
107
2.88k
            *this = rMat;
108
2.88k
        }
109
139
        else
110
139
        {
111
            // multiply
112
139
            mpImpl->doMulMatrix(*rMat.mpImpl);
113
139
        }
114
71.2k
        return *this;
115
71.2k
    }
116
117
    bool B3DHomMatrix::operator==(const B3DHomMatrix& rMat) const
118
6.52k
    {
119
6.52k
        if(mpImpl.same_object(rMat.mpImpl))
120
0
            return true;
121
122
6.52k
        return mpImpl->isEqual(*rMat.mpImpl);
123
6.52k
    }
124
125
    void B3DHomMatrix::rotate(double fAngleX,double fAngleY,double fAngleZ)
126
15.3k
    {
127
15.3k
        if(fTools::equalZero(fAngleX) && fTools::equalZero(fAngleY) && fTools::equalZero(fAngleZ))
128
8.56k
            return;
129
130
6.74k
        if(!fTools::equalZero(fAngleX))
131
292
        {
132
292
            Impl3DHomMatrix aRotMatX;
133
292
            double fSin(sin(fAngleX));
134
292
            double fCos(cos(fAngleX));
135
136
292
            aRotMatX.set(1, 1, fCos);
137
292
            aRotMatX.set(2, 2, fCos);
138
292
            aRotMatX.set(2, 1, fSin);
139
292
            aRotMatX.set(1, 2, -fSin);
140
141
292
            mpImpl->doMulMatrix(aRotMatX);
142
292
        }
143
144
6.74k
        if(!fTools::equalZero(fAngleY))
145
2.78k
        {
146
2.78k
            Impl3DHomMatrix aRotMatY;
147
2.78k
            double fSin(sin(fAngleY));
148
2.78k
            double fCos(cos(fAngleY));
149
150
2.78k
            aRotMatY.set(0, 0, fCos);
151
2.78k
            aRotMatY.set(2, 2, fCos);
152
2.78k
            aRotMatY.set(0, 2, fSin);
153
2.78k
            aRotMatY.set(2, 0, -fSin);
154
155
2.78k
            mpImpl->doMulMatrix(aRotMatY);
156
2.78k
        }
157
158
6.74k
        if(fTools::equalZero(fAngleZ))
159
3.01k
            return;
160
161
3.72k
        Impl3DHomMatrix aRotMatZ;
162
3.72k
        double fSin(sin(fAngleZ));
163
3.72k
        double fCos(cos(fAngleZ));
164
165
3.72k
        aRotMatZ.set(0, 0, fCos);
166
3.72k
        aRotMatZ.set(1, 1, fCos);
167
3.72k
        aRotMatZ.set(1, 0, fSin);
168
3.72k
        aRotMatZ.set(0, 1, -fSin);
169
170
3.72k
        mpImpl->doMulMatrix(aRotMatZ);
171
3.72k
    }
172
173
    void B3DHomMatrix::rotate(const B3DTuple& rRotation)
174
0
    {
175
0
        rotate(rRotation.getX(), rRotation.getY(), rRotation.getZ());
176
0
    }
177
178
    void B3DHomMatrix::translate(double fX, double fY, double fZ)
179
32.1k
    {
180
32.1k
        if(!fTools::equalZero(fX) || !fTools::equalZero(fY) || !fTools::equalZero(fZ))
181
23.6k
        {
182
23.6k
            Impl3DHomMatrix aTransMat;
183
184
23.6k
            aTransMat.set(0, 3, fX);
185
23.6k
            aTransMat.set(1, 3, fY);
186
23.6k
            aTransMat.set(2, 3, fZ);
187
188
23.6k
            mpImpl->doMulMatrix(aTransMat);
189
23.6k
        }
190
32.1k
    }
191
192
    void B3DHomMatrix::translate(const B3DTuple& rRotation)
193
0
    {
194
0
        translate(rRotation.getX(), rRotation.getY(), rRotation.getZ());
195
0
    }
196
197
    void B3DHomMatrix::scale(double fX, double fY, double fZ)
198
2.32k
    {
199
2.32k
        const double fOne(1.0);
200
201
2.32k
        if(!fTools::equal(fOne, fX) || !fTools::equal(fOne, fY) ||!fTools::equal(fOne, fZ))
202
2.32k
        {
203
2.32k
            Impl3DHomMatrix aScaleMat;
204
205
2.32k
            aScaleMat.set(0, 0, fX);
206
2.32k
            aScaleMat.set(1, 1, fY);
207
2.32k
            aScaleMat.set(2, 2, fZ);
208
209
2.32k
            mpImpl->doMulMatrix(aScaleMat);
210
2.32k
        }
211
2.32k
    }
212
213
    void B3DHomMatrix::scale(const B3DTuple& rRotation)
214
0
    {
215
0
        scale(rRotation.getX(), rRotation.getY(), rRotation.getZ());
216
0
    }
217
218
    void B3DHomMatrix::shearXY(double fSx, double fSy)
219
2.56k
    {
220
        // #i76239# do not test against 1.0, but against 0.0. We are talking about a value not on the diagonal (!)
221
2.56k
        if(!fTools::equalZero(fSx) || !fTools::equalZero(fSy))
222
2.56k
        {
223
2.56k
            Impl3DHomMatrix aShearXYMat;
224
225
2.56k
            aShearXYMat.set(0, 2, fSx);
226
2.56k
            aShearXYMat.set(1, 2, fSy);
227
228
2.56k
            mpImpl->doMulMatrix(aShearXYMat);
229
2.56k
        }
230
2.56k
    }
231
232
    void B3DHomMatrix::shearXZ(double fSx, double fSz)
233
0
    {
234
        // #i76239# do not test against 1.0, but against 0.0. We are talking about a value not on the diagonal (!)
235
0
        if(!fTools::equalZero(fSx) || !fTools::equalZero(fSz))
236
0
        {
237
0
            Impl3DHomMatrix aShearXZMat;
238
239
0
            aShearXZMat.set(0, 1, fSx);
240
0
            aShearXZMat.set(2, 1, fSz);
241
242
0
            mpImpl->doMulMatrix(aShearXZMat);
243
0
        }
244
0
    }
245
    void B3DHomMatrix::frustum(double fLeft, double fRight, double fBottom, double fTop, double fNear, double fFar)
246
0
    {
247
0
        const double fZero(0.0);
248
0
        const double fOne(1.0);
249
250
0
        if(!fTools::more(fNear, fZero))
251
0
        {
252
0
            fNear = 0.001;
253
0
        }
254
255
0
        if(!fTools::more(fFar, fZero))
256
0
        {
257
0
            fFar = fOne;
258
0
        }
259
260
0
        if(fTools::equal(fNear, fFar))
261
0
        {
262
0
            fFar = fNear + fOne;
263
0
        }
264
265
0
        if(fTools::equal(fLeft, fRight))
266
0
        {
267
0
            fLeft -= fOne;
268
0
            fRight += fOne;
269
0
        }
270
271
0
        if(fTools::equal(fTop, fBottom))
272
0
        {
273
0
            fBottom -= fOne;
274
0
            fTop += fOne;
275
0
        }
276
277
0
        Impl3DHomMatrix aFrustumMat;
278
279
0
        aFrustumMat.set(0, 0, 2.0 * fNear / (fRight - fLeft));
280
0
        aFrustumMat.set(1, 1, 2.0 * fNear / (fTop - fBottom));
281
0
        aFrustumMat.set(0, 2, (fRight + fLeft) / (fRight - fLeft));
282
0
        aFrustumMat.set(1, 2, (fTop + fBottom) / (fTop - fBottom));
283
0
        aFrustumMat.set(2, 2, -fOne * ((fFar + fNear) / (fFar - fNear)));
284
0
        aFrustumMat.set(3, 2, -fOne);
285
0
        aFrustumMat.set(2, 3, -fOne * ((2.0 * fFar * fNear) / (fFar - fNear)));
286
0
        aFrustumMat.set(3, 3, fZero);
287
288
0
        mpImpl->doMulMatrix(aFrustumMat);
289
0
    }
290
291
    void B3DHomMatrix::ortho(double fLeft, double fRight, double fBottom, double fTop, double fNear, double fFar)
292
0
    {
293
0
        if(fTools::equal(fNear, fFar))
294
0
        {
295
0
            fFar = fNear + 1.0;
296
0
        }
297
298
0
        if(fTools::equal(fLeft, fRight))
299
0
        {
300
0
            fLeft -= 1.0;
301
0
            fRight += 1.0;
302
0
        }
303
304
0
        if(fTools::equal(fTop, fBottom))
305
0
        {
306
0
            fBottom -= 1.0;
307
0
            fTop += 1.0;
308
0
        }
309
310
0
        Impl3DHomMatrix aOrthoMat;
311
312
0
        aOrthoMat.set(0, 0, 2.0 / (fRight - fLeft));
313
0
        aOrthoMat.set(1, 1, 2.0 / (fTop - fBottom));
314
0
        aOrthoMat.set(2, 2, -1.0 * (2.0 / (fFar - fNear)));
315
0
        aOrthoMat.set(0, 3, -1.0 * ((fRight + fLeft) / (fRight - fLeft)));
316
0
        aOrthoMat.set(1, 3, -1.0 * ((fTop + fBottom) / (fTop - fBottom)));
317
0
        aOrthoMat.set(2, 3, -1.0 * ((fFar + fNear) / (fFar - fNear)));
318
319
0
        mpImpl->doMulMatrix(aOrthoMat);
320
0
    }
321
322
    void B3DHomMatrix::orientation(const B3DPoint& rVRP, B3DVector aVPN, B3DVector aVUV)
323
0
    {
324
0
        Impl3DHomMatrix aOrientationMat;
325
326
        // translate -VRP
327
0
        aOrientationMat.set(0, 3, -rVRP.getX());
328
0
        aOrientationMat.set(1, 3, -rVRP.getY());
329
0
        aOrientationMat.set(2, 3, -rVRP.getZ());
330
331
        // build rotation
332
0
        aVUV.normalize();
333
0
        aVPN.normalize();
334
335
        // build x-axis as perpendicular from aVUV and aVPN
336
0
        B3DVector aRx(aVUV.getPerpendicular(aVPN));
337
0
        aRx.normalize();
338
339
        // y-axis perpendicular to that
340
0
        B3DVector aRy(aVPN.getPerpendicular(aRx));
341
0
        aRy.normalize();
342
343
        // the calculated normals are the line vectors of the rotation matrix,
344
        // set them to create rotation
345
0
        aOrientationMat.set(0, 0, aRx.getX());
346
0
        aOrientationMat.set(0, 1, aRx.getY());
347
0
        aOrientationMat.set(0, 2, aRx.getZ());
348
0
        aOrientationMat.set(1, 0, aRy.getX());
349
0
        aOrientationMat.set(1, 1, aRy.getY());
350
0
        aOrientationMat.set(1, 2, aRy.getZ());
351
0
        aOrientationMat.set(2, 0, aVPN.getX());
352
0
        aOrientationMat.set(2, 1, aVPN.getY());
353
0
        aOrientationMat.set(2, 2, aVPN.getZ());
354
355
0
        mpImpl->doMulMatrix(aOrientationMat);
356
0
    }
357
358
    void B3DHomMatrix::decompose(B3DTuple& rScale, B3DTuple& rTranslate, B3DTuple& rRotate, B3DTuple& rShear) const
359
0
    {
360
        // when perspective is used, decompose is not made here
361
0
        if(!mpImpl->isLastLineDefault())
362
0
            return;
363
364
        // If determinant is zero, decomposition is not possible
365
0
        if(determinant() == 0.0)
366
0
            return;
367
368
        // isolate translation
369
0
        rTranslate.setX(mpImpl->get(0, 3));
370
0
        rTranslate.setY(mpImpl->get(1, 3));
371
0
        rTranslate.setZ(mpImpl->get(2, 3));
372
373
        // correct translate values
374
0
        rTranslate.correctValues();
375
376
        // get scale and shear
377
0
        B3DVector aCol0(mpImpl->get(0, 0), mpImpl->get(1, 0), mpImpl->get(2, 0));
378
0
        B3DVector aCol1(mpImpl->get(0, 1), mpImpl->get(1, 1), mpImpl->get(2, 1));
379
0
        B3DVector aCol2(mpImpl->get(0, 2), mpImpl->get(1, 2), mpImpl->get(2, 2));
380
0
        B3DVector aTemp;
381
382
        // get ScaleX
383
0
        rScale.setX(aCol0.getLength());
384
0
        aCol0.normalize();
385
386
        // get ShearXY
387
0
        rShear.setX(aCol0.scalar(aCol1));
388
389
0
        if(fTools::equalZero(rShear.getX()))
390
0
        {
391
0
            rShear.setX(0.0);
392
0
        }
393
0
        else
394
0
        {
395
0
            aTemp.setX(aCol1.getX() - rShear.getX() * aCol0.getX());
396
0
            aTemp.setY(aCol1.getY() - rShear.getX() * aCol0.getY());
397
0
            aTemp.setZ(aCol1.getZ() - rShear.getX() * aCol0.getZ());
398
0
            aCol1 = aTemp;
399
0
        }
400
401
        // get ScaleY
402
0
        rScale.setY(aCol1.getLength());
403
0
        aCol1.normalize();
404
405
0
        const double fShearX(rShear.getX());
406
407
0
        if(!fTools::equalZero(fShearX))
408
0
        {
409
0
            rShear.setX(rShear.getX() / rScale.getY());
410
0
        }
411
412
        // get ShearXZ
413
0
        rShear.setY(aCol0.scalar(aCol2));
414
415
0
        if(fTools::equalZero(rShear.getY()))
416
0
        {
417
0
            rShear.setY(0.0);
418
0
        }
419
0
        else
420
0
        {
421
0
            aTemp.setX(aCol2.getX() - rShear.getY() * aCol0.getX());
422
0
            aTemp.setY(aCol2.getY() - rShear.getY() * aCol0.getY());
423
0
            aTemp.setZ(aCol2.getZ() - rShear.getY() * aCol0.getZ());
424
0
            aCol2 = aTemp;
425
0
        }
426
427
        // get ShearYZ
428
0
        rShear.setZ(aCol1.scalar(aCol2));
429
430
0
        if(fTools::equalZero(rShear.getZ()))
431
0
        {
432
0
            rShear.setZ(0.0);
433
0
        }
434
0
        else
435
0
        {
436
0
            aTemp.setX(aCol2.getX() - rShear.getZ() * aCol1.getX());
437
0
            aTemp.setY(aCol2.getY() - rShear.getZ() * aCol1.getY());
438
0
            aTemp.setZ(aCol2.getZ() - rShear.getZ() * aCol1.getZ());
439
0
            aCol2 = aTemp;
440
0
        }
441
442
        // get ScaleZ
443
0
        rScale.setZ(aCol2.getLength());
444
0
        aCol2.normalize();
445
446
0
        const double fShearY(rShear.getY());
447
448
0
        if(!fTools::equalZero(fShearY))
449
0
        {
450
0
            rShear.setY(rShear.getY() / rScale.getZ());
451
0
        }
452
453
0
        const double fShearZ(rShear.getZ());
454
455
0
        if(!fTools::equalZero(fShearZ))
456
0
        {
457
0
            rShear.setZ(rShear.getZ() / rScale.getZ());
458
0
        }
459
460
        // correct shear values
461
0
        rShear.correctValues();
462
463
        // Coordinate system flip?
464
0
        if(0.0 > aCol0.scalar(aCol1.getPerpendicular(aCol2)))
465
0
        {
466
0
            rScale = -rScale;
467
0
            aCol0 = -aCol0;
468
0
            aCol1 = -aCol1;
469
0
            aCol2 = -aCol2;
470
0
        }
471
472
        // correct scale values
473
0
        rScale.correctValues(1.0);
474
475
        // Get rotations
476
0
        {
477
0
            double fy=0;
478
0
            double cy=0;
479
480
0
            if( ::basegfx::fTools::equal( aCol0.getZ(), 1.0 )
481
0
                || aCol0.getZ() > 1.0 )
482
0
            {
483
0
                fy = -M_PI/2.0;
484
0
                cy = 0.0;
485
0
            }
486
0
            else if( ::basegfx::fTools::equal( aCol0.getZ(), -1.0 )
487
0
                || aCol0.getZ() < -1.0 )
488
0
            {
489
0
                fy = M_PI/2.0;
490
0
                cy = 0.0;
491
0
            }
492
0
            else
493
0
            {
494
0
                fy = asin( -aCol0.getZ() );
495
0
                cy = cos(fy);
496
0
            }
497
498
0
            rRotate.setY(fy);
499
0
            if( ::basegfx::fTools::equalZero( cy ) )
500
0
            {
501
0
                if( aCol0.getZ() > 0.0 )
502
0
                    rRotate.setX(atan2(-1.0*aCol1.getX(), aCol1.getY()));
503
0
                else
504
0
                    rRotate.setX(atan2(aCol1.getX(), aCol1.getY()));
505
0
                rRotate.setZ(0.0);
506
0
            }
507
0
            else
508
0
            {
509
0
                rRotate.setX(atan2(aCol1.getZ(), aCol2.getZ()));
510
0
                rRotate.setZ(atan2(aCol0.getY(), aCol0.getX()));
511
0
            }
512
513
            // correct rotate values
514
0
            rRotate.correctValues();
515
0
        }
516
0
    }
517
} // end of namespace basegfx
518
519
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */