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/b2dhommatrixtools.hxx>
21 #include <basegfx/range/b2drange.hxx>
22 
23 #include <osl/diagnose.h>
24 
25 namespace basegfx::utils
26 {
createSinCosOrthogonal(double & o_rSin,double & o_rCos,double fRadiant)27         void createSinCosOrthogonal(double& o_rSin, double& o_rCos, double fRadiant)
28         {
29             if( fTools::equalZero( fmod( fRadiant, F_PI2 ) ) )
30             {
31                 // determine quadrant
32                 const sal_Int32 nQuad(
33                     (4 + fround( 4/F_2PI*fmod( fRadiant, F_2PI ) )) % 4 );
34                 switch( nQuad )
35                 {
36                     case 0: // -2pi,0,2pi
37                         o_rSin = 0.0;
38                         o_rCos = 1.0;
39                         break;
40 
41                     case 1: // -3/2pi,1/2pi
42                         o_rSin = 1.0;
43                         o_rCos = 0.0;
44                         break;
45 
46                     case 2: // -pi,pi
47                         o_rSin = 0.0;
48                         o_rCos = -1.0;
49                         break;
50 
51                     case 3: // -1/2pi,3/2pi
52                         o_rSin = -1.0;
53                         o_rCos = 0.0;
54                         break;
55 
56                     default:
57                         OSL_FAIL( "createSinCos: Impossible case reached" );
58                 }
59             }
60             else
61             {
62                 // TODO(P1): Maybe use glibc's sincos here (though
63                 // that's kinda non-portable...)
64                 o_rSin = sin(fRadiant);
65                 o_rCos = cos(fRadiant);
66             }
67         }
68 
createScaleB2DHomMatrix(double fScaleX,double fScaleY)69         B2DHomMatrix createScaleB2DHomMatrix(double fScaleX, double fScaleY)
70         {
71             B2DHomMatrix aRetval;
72             const double fOne(1.0);
73 
74             if(!fTools::equal(fScaleX, fOne))
75             {
76                 aRetval.set(0, 0, fScaleX);
77             }
78 
79             if(!fTools::equal(fScaleY, fOne))
80             {
81                 aRetval.set(1, 1, fScaleY);
82             }
83 
84             return aRetval;
85         }
86 
createShearXB2DHomMatrix(double fShearX)87         B2DHomMatrix createShearXB2DHomMatrix(double fShearX)
88         {
89             B2DHomMatrix aRetval;
90 
91             if(!fTools::equalZero(fShearX))
92             {
93                 aRetval.set(0, 1, fShearX);
94             }
95 
96             return aRetval;
97         }
98 
createShearYB2DHomMatrix(double fShearY)99         B2DHomMatrix createShearYB2DHomMatrix(double fShearY)
100         {
101             B2DHomMatrix aRetval;
102 
103             if(!fTools::equalZero(fShearY))
104             {
105                 aRetval.set(1, 0, fShearY);
106             }
107 
108             return aRetval;
109         }
110 
createRotateB2DHomMatrix(double fRadiant)111         B2DHomMatrix createRotateB2DHomMatrix(double fRadiant)
112         {
113             B2DHomMatrix aRetval;
114 
115             if(!fTools::equalZero(fRadiant))
116             {
117                 double fSin(0.0);
118                 double fCos(1.0);
119 
120                 createSinCosOrthogonal(fSin, fCos, fRadiant);
121                 aRetval.set(0, 0, fCos);
122                 aRetval.set(1, 1, fCos);
123                 aRetval.set(1, 0, fSin);
124                 aRetval.set(0, 1, -fSin);
125             }
126 
127             return aRetval;
128         }
129 
createTranslateB2DHomMatrix(double fTranslateX,double fTranslateY)130         B2DHomMatrix createTranslateB2DHomMatrix(double fTranslateX, double fTranslateY)
131         {
132             B2DHomMatrix aRetval;
133 
134             if(!(fTools::equalZero(fTranslateX) && fTools::equalZero(fTranslateY)))
135             {
136                 aRetval.set(0, 2, fTranslateX);
137                 aRetval.set(1, 2, fTranslateY);
138             }
139 
140             return aRetval;
141         }
142 
createScaleShearXRotateTranslateB2DHomMatrix(double fScaleX,double fScaleY,double fShearX,double fRadiant,double fTranslateX,double fTranslateY)143         B2DHomMatrix createScaleShearXRotateTranslateB2DHomMatrix(
144             double fScaleX, double fScaleY,
145             double fShearX,
146             double fRadiant,
147             double fTranslateX, double fTranslateY)
148         {
149             const double fOne(1.0);
150 
151             if(fTools::equal(fScaleX, fOne) && fTools::equal(fScaleY, fOne))
152             {
153                 /// no scale, take shortcut
154                 return createShearXRotateTranslateB2DHomMatrix(fShearX, fRadiant, fTranslateX, fTranslateY);
155             }
156             else
157             {
158                 /// scale used
159                 if(fTools::equalZero(fShearX))
160                 {
161                     /// no shear
162                     if(fTools::equalZero(fRadiant))
163                     {
164                         /// no rotate, take shortcut
165                         return createScaleTranslateB2DHomMatrix(fScaleX, fScaleY, fTranslateX, fTranslateY);
166                     }
167                     else
168                     {
169                         /// rotate and scale used, no shear
170                         double fSin(0.0);
171                         double fCos(1.0);
172 
173                         createSinCosOrthogonal(fSin, fCos, fRadiant);
174 
175                         B2DHomMatrix aRetval(
176                             /* Row 0, Column 0 */ fCos * fScaleX,
177                             /* Row 0, Column 1 */ fScaleY * -fSin,
178                             /* Row 0, Column 2 */ fTranslateX,
179                             /* Row 1, Column 0 */ fSin * fScaleX,
180                             /* Row 1, Column 1 */ fScaleY * fCos,
181                             /* Row 1, Column 2 */ fTranslateY);
182 
183                         return aRetval;
184                     }
185                 }
186                 else
187                 {
188                     /// scale and shear used
189                     if(fTools::equalZero(fRadiant))
190                     {
191                         /// scale and shear, but no rotate
192                         B2DHomMatrix aRetval(
193                             /* Row 0, Column 0 */ fScaleX,
194                             /* Row 0, Column 1 */ fScaleY * fShearX,
195                             /* Row 0, Column 2 */ fTranslateX,
196                             /* Row 1, Column 0 */ 0.0,
197                             /* Row 1, Column 1 */ fScaleY,
198                             /* Row 1, Column 2 */ fTranslateY);
199 
200                         return aRetval;
201                     }
202                     else
203                     {
204                         /// scale, shear and rotate used
205                         double fSin(0.0);
206                         double fCos(1.0);
207 
208                         createSinCosOrthogonal(fSin, fCos, fRadiant);
209 
210                         B2DHomMatrix aRetval(
211                             /* Row 0, Column 0 */ fCos * fScaleX,
212                             /* Row 0, Column 1 */ fScaleY * ((fCos * fShearX) - fSin),
213                             /* Row 0, Column 2 */ fTranslateX,
214                             /* Row 1, Column 0 */ fSin * fScaleX,
215                             /* Row 1, Column 1 */ fScaleY * ((fSin * fShearX) + fCos),
216                             /* Row 1, Column 2 */ fTranslateY);
217 
218                         return aRetval;
219                     }
220                 }
221             }
222         }
223 
createShearXRotateTranslateB2DHomMatrix(double fShearX,double fRadiant,double fTranslateX,double fTranslateY)224         B2DHomMatrix createShearXRotateTranslateB2DHomMatrix(
225             double fShearX,
226             double fRadiant,
227             double fTranslateX, double fTranslateY)
228         {
229             if(fTools::equalZero(fShearX))
230             {
231                 /// no shear
232                 if(fTools::equalZero(fRadiant))
233                 {
234                     /// no shear, no rotate, take shortcut
235                     return createTranslateB2DHomMatrix(fTranslateX, fTranslateY);
236                 }
237                 else
238                 {
239                     /// no shear, but rotate used
240                     double fSin(0.0);
241                     double fCos(1.0);
242 
243                     createSinCosOrthogonal(fSin, fCos, fRadiant);
244 
245                     B2DHomMatrix aRetval(
246                         /* Row 0, Column 0 */ fCos,
247                         /* Row 0, Column 1 */ -fSin,
248                         /* Row 0, Column 2 */ fTranslateX,
249                         /* Row 1, Column 0 */ fSin,
250                         /* Row 1, Column 1 */ fCos,
251                         /* Row 1, Column 2 */ fTranslateY);
252 
253                     return aRetval;
254                 }
255             }
256             else
257             {
258                 /// shear used
259                 if(fTools::equalZero(fRadiant))
260                 {
261                     /// no rotate, but shear used
262                     B2DHomMatrix aRetval(
263                         /* Row 0, Column 0 */ 1.0,
264                         /* Row 0, Column 1 */ fShearX,
265                         /* Row 0, Column 2 */ fTranslateX,
266                         /* Row 1, Column 0 */ 0.0,
267                         /* Row 1, Column 1 */ 1.0,
268                         /* Row 1, Column 2 */ fTranslateY);
269 
270                     return aRetval;
271                 }
272                 else
273                 {
274                     /// shear and rotate used
275                     double fSin(0.0);
276                     double fCos(1.0);
277 
278                     createSinCosOrthogonal(fSin, fCos, fRadiant);
279 
280                     B2DHomMatrix aRetval(
281                         /* Row 0, Column 0 */ fCos,
282                         /* Row 0, Column 1 */ (fCos * fShearX) - fSin,
283                         /* Row 0, Column 2 */ fTranslateX,
284                         /* Row 1, Column 0 */ fSin,
285                         /* Row 1, Column 1 */ (fSin * fShearX) + fCos,
286                         /* Row 1, Column 2 */ fTranslateY);
287 
288                     return aRetval;
289                 }
290             }
291         }
292 
createScaleTranslateB2DHomMatrix(double fScaleX,double fScaleY,double fTranslateX,double fTranslateY)293         B2DHomMatrix createScaleTranslateB2DHomMatrix(
294             double fScaleX, double fScaleY,
295             double fTranslateX, double fTranslateY)
296         {
297             const double fOne(1.0);
298 
299             if(fTools::equal(fScaleX, fOne) && fTools::equal(fScaleY, fOne))
300             {
301                 /// no scale, take shortcut
302                 return createTranslateB2DHomMatrix(fTranslateX, fTranslateY);
303             }
304             else
305             {
306                 /// scale used
307                 if(fTools::equalZero(fTranslateX) && fTools::equalZero(fTranslateY))
308                 {
309                     /// no translate, but scale.
310                     B2DHomMatrix aRetval;
311 
312                     aRetval.set(0, 0, fScaleX);
313                     aRetval.set(1, 1, fScaleY);
314 
315                     return aRetval;
316                 }
317                 else
318                 {
319                     /// translate and scale
320                     B2DHomMatrix aRetval(
321                         /* Row 0, Column 0 */ fScaleX,
322                         /* Row 0, Column 1 */ 0.0,
323                         /* Row 0, Column 2 */ fTranslateX,
324                         /* Row 1, Column 0 */ 0.0,
325                         /* Row 1, Column 1 */ fScaleY,
326                         /* Row 1, Column 2 */ fTranslateY);
327 
328                     return aRetval;
329                 }
330             }
331         }
332 
createRotateAroundPoint(double fPointX,double fPointY,double fRadiant)333         B2DHomMatrix createRotateAroundPoint(
334             double fPointX, double fPointY,
335             double fRadiant)
336         {
337             B2DHomMatrix aRetval;
338 
339             if(!fTools::equalZero(fRadiant))
340             {
341                 double fSin(0.0);
342                 double fCos(1.0);
343 
344                 createSinCosOrthogonal(fSin, fCos, fRadiant);
345 
346                 aRetval.set3x2(
347                     /* Row 0, Column 0 */ fCos,
348                     /* Row 0, Column 1 */ -fSin,
349                     /* Row 0, Column 2 */ (fPointX * (1.0 - fCos)) + (fSin * fPointY),
350                     /* Row 1, Column 0 */ fSin,
351                     /* Row 1, Column 1 */ fCos,
352                     /* Row 1, Column 2 */ (fPointY * (1.0 - fCos)) - (fSin * fPointX));
353             }
354 
355             return aRetval;
356         }
357 
createRotateAroundCenterKeepAspectRatioStayInsideRange(const basegfx::B2DRange & rTargetRange,double fRotate)358         BASEGFX_DLLPUBLIC B2DHomMatrix createRotateAroundCenterKeepAspectRatioStayInsideRange(
359             const basegfx::B2DRange& rTargetRange,
360             double fRotate)
361         {
362             basegfx::B2DHomMatrix aRetval;
363 
364             // RotGrfFlyFrame: Create a transformation that maps the range inside of itself
365             // so that it fits, takes as much space as possible and keeps the aspect ratio
366             if(0.0 != fRotate)
367             {
368                 // Fit rotated graphic to center of available space, keeping page ratio:
369                 // Adapt scaling ratio of unit object and rotate it
370                 aRetval.scale(1.0, rTargetRange.getHeight() / rTargetRange.getWidth());
371                 aRetval.rotate(fRotate);
372 
373                 // get the range to see where we are in unit coordinates
374                 basegfx::B2DRange aFullRange(0.0, 0.0, 1.0, 1.0);
375                 aFullRange.transform(aRetval);
376 
377                 // detect needed scales in X/Y and choose the smallest for staying inside the
378                 // available space while keeping aspect ratio of the source
379                 const double fScaleX(rTargetRange.getWidth() / aFullRange.getWidth());
380                 const double fScaleY(rTargetRange.getHeight() / aFullRange.getHeight());
381                 const double fScaleMin(std::min(fScaleX, fScaleY));
382 
383                 // TopLeft to zero, then scale, then move to center of available space
384                 aRetval.translate(-aFullRange.getMinX(), -aFullRange.getMinY());
385                 aRetval.scale(fScaleMin, fScaleMin);
386                 aRetval.translate(
387                     rTargetRange.getCenterX() - (0.5 * fScaleMin * aFullRange.getWidth()),
388                     rTargetRange.getCenterY() - (0.5 * fScaleMin * aFullRange.getHeight()));
389             }
390             else
391             {
392                 // just scale/translate needed
393                 aRetval *= createScaleTranslateB2DHomMatrix(
394                     rTargetRange.getRange(),
395                     rTargetRange.getMinimum());
396             }
397 
398             return aRetval;
399         }
400 
401         /// special for the case to map from source range to target range
createSourceRangeTargetRangeTransform(const B2DRange & rSourceRange,const B2DRange & rTargetRange)402         B2DHomMatrix createSourceRangeTargetRangeTransform(
403             const B2DRange& rSourceRange,
404             const B2DRange& rTargetRange)
405         {
406             B2DHomMatrix aRetval;
407 
408             if(&rSourceRange == &rTargetRange)
409             {
410                 return aRetval;
411             }
412 
413             if(!fTools::equalZero(rSourceRange.getMinX()) || !fTools::equalZero(rSourceRange.getMinY()))
414             {
415                 aRetval.set(0, 2, -rSourceRange.getMinX());
416                 aRetval.set(1, 2, -rSourceRange.getMinY());
417             }
418 
419             const double fSourceW(rSourceRange.getWidth());
420             const double fSourceH(rSourceRange.getHeight());
421             const bool bDivX(!fTools::equalZero(fSourceW) && !fTools::equal(fSourceW, 1.0));
422             const bool bDivY(!fTools::equalZero(fSourceH) && !fTools::equal(fSourceH, 1.0));
423             const double fScaleX(bDivX ? rTargetRange.getWidth() / fSourceW : rTargetRange.getWidth());
424             const double fScaleY(bDivY ? rTargetRange.getHeight() / fSourceH : rTargetRange.getHeight());
425 
426             if(!fTools::equalZero(fScaleX) || !fTools::equalZero(fScaleY))
427             {
428                 aRetval.scale(fScaleX, fScaleY);
429             }
430 
431             if(!fTools::equalZero(rTargetRange.getMinX()) || !fTools::equalZero(rTargetRange.getMinY()))
432             {
433                 aRetval.translate(
434                     rTargetRange.getMinX(),
435                     rTargetRange.getMinY());
436             }
437 
438             return aRetval;
439         }
440 
createCoordinateSystemTransform(const B2DPoint & rOrigin,const B2DVector & rX,const B2DVector & rY)441         B2DHomMatrix createCoordinateSystemTransform(
442             const B2DPoint& rOrigin,
443             const B2DVector& rX,
444             const B2DVector& rY)
445         {
446             return basegfx::B2DHomMatrix(
447                 rX.getX(), rY.getX(), rOrigin.getX(),
448                 rX.getY(), rY.getY(), rOrigin.getY());
449         }
450 
getColumn(const B2DHomMatrix & rMatrix,sal_uInt16 nCol)451         B2DTuple getColumn(const B2DHomMatrix& rMatrix, sal_uInt16 nCol)
452         {
453             return B2DTuple(rMatrix.get(0, nCol), rMatrix.get(1, nCol));
454         }
455 } // end of namespace
456 
457 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
458