1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <basegfx/matrix/b2dhommatrixtools.hxx>
21 #include <basegfx/range/b2drange.hxx>
23 #include <osl/diagnose.h>
25 namespace basegfx::utils
27 void createSinCosOrthogonal(double& o_rSin
, double& o_rCos
, double fRadiant
)
29 if( fTools::equalZero( fmod( fRadiant
, M_PI_2
) ) )
32 const sal_Int32
nQuad(
33 (4 + fround( M_2_PI
*fmod( fRadiant
, 2 * M_PI
) )) % 4 );
41 case 1: // -3/2pi,1/2pi
51 case 3: // -1/2pi,3/2pi
57 OSL_FAIL( "createSinCos: Impossible case reached" );
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
);
69 B2DHomMatrix
createScaleB2DHomMatrix(double fScaleX
, double fScaleY
)
72 const double fOne(1.0);
74 if(!fTools::equal(fScaleX
, fOne
))
76 aRetval
.set(0, 0, fScaleX
);
79 if(!fTools::equal(fScaleY
, fOne
))
81 aRetval
.set(1, 1, fScaleY
);
87 B2DHomMatrix
createShearXB2DHomMatrix(double fShearX
)
91 if(!fTools::equalZero(fShearX
))
93 aRetval
.set(0, 1, fShearX
);
99 B2DHomMatrix
createShearYB2DHomMatrix(double fShearY
)
101 B2DHomMatrix aRetval
;
103 if(!fTools::equalZero(fShearY
))
105 aRetval
.set(1, 0, fShearY
);
111 B2DHomMatrix
createRotateB2DHomMatrix(double fRadiant
)
113 B2DHomMatrix aRetval
;
115 if(!fTools::equalZero(fRadiant
))
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
);
130 B2DHomMatrix
createTranslateB2DHomMatrix(double fTranslateX
, double fTranslateY
)
132 B2DHomMatrix aRetval
;
134 if(!(fTools::equalZero(fTranslateX
) && fTools::equalZero(fTranslateY
)))
136 aRetval
.set(0, 2, fTranslateX
);
137 aRetval
.set(1, 2, fTranslateY
);
143 B2DHomMatrix
createScaleShearXRotateTranslateB2DHomMatrix(
144 double fScaleX
, double fScaleY
,
147 double fTranslateX
, double fTranslateY
)
149 const double fOne(1.0);
151 if(fTools::equal(fScaleX
, fOne
) && fTools::equal(fScaleY
, fOne
))
153 /// no scale, take shortcut
154 return createShearXRotateTranslateB2DHomMatrix(fShearX
, fRadiant
, fTranslateX
, fTranslateY
);
159 if(fTools::equalZero(fShearX
))
162 if(fTools::equalZero(fRadiant
))
164 /// no rotate, take shortcut
165 return createScaleTranslateB2DHomMatrix(fScaleX
, fScaleY
, fTranslateX
, fTranslateY
);
169 /// rotate and scale used, no shear
173 createSinCosOrthogonal(fSin
, fCos
, fRadiant
);
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
);
188 /// scale and shear used
189 if(fTools::equalZero(fRadiant
))
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
);
204 /// scale, shear and rotate used
208 createSinCosOrthogonal(fSin
, fCos
, fRadiant
);
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
);
224 B2DHomMatrix
createShearXRotateTranslateB2DHomMatrix(
227 double fTranslateX
, double fTranslateY
)
229 if(fTools::equalZero(fShearX
))
232 if(fTools::equalZero(fRadiant
))
234 /// no shear, no rotate, take shortcut
235 return createTranslateB2DHomMatrix(fTranslateX
, fTranslateY
);
239 /// no shear, but rotate used
243 createSinCosOrthogonal(fSin
, fCos
, fRadiant
);
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
);
259 if(fTools::equalZero(fRadiant
))
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
);
274 /// shear and rotate used
278 createSinCosOrthogonal(fSin
, fCos
, fRadiant
);
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
);
293 B2DHomMatrix
createScaleTranslateB2DHomMatrix(
294 double fScaleX
, double fScaleY
,
295 double fTranslateX
, double fTranslateY
)
297 const double fOne(1.0);
299 if(fTools::equal(fScaleX
, fOne
) && fTools::equal(fScaleY
, fOne
))
301 /// no scale, take shortcut
302 return createTranslateB2DHomMatrix(fTranslateX
, fTranslateY
);
307 if(fTools::equalZero(fTranslateX
) && fTools::equalZero(fTranslateY
))
309 /// no translate, but scale.
310 B2DHomMatrix aRetval
;
312 aRetval
.set(0, 0, fScaleX
);
313 aRetval
.set(1, 1, fScaleY
);
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
);
333 B2DHomMatrix
createRotateAroundPoint(
334 double fPointX
, double fPointY
,
337 B2DHomMatrix aRetval
;
339 if(!fTools::equalZero(fRadiant
))
344 createSinCosOrthogonal(fSin
, fCos
, fRadiant
);
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
));
358 B2DHomMatrix
createRotateAroundCenterKeepAspectRatioStayInsideRange(
359 const basegfx::B2DRange
& rTargetRange
,
362 basegfx::B2DHomMatrix aRetval
;
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
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
);
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
);
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
));
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
);
387 rTargetRange
.getCenterX() - (0.5 * fScaleMin
* aFullRange
.getWidth()),
388 rTargetRange
.getCenterY() - (0.5 * fScaleMin
* aFullRange
.getHeight()));
392 // just scale/translate needed
393 aRetval
*= createScaleTranslateB2DHomMatrix(
394 rTargetRange
.getRange(),
395 rTargetRange
.getMinimum());
401 /// special for the case to map from source range to target range
402 B2DHomMatrix
createSourceRangeTargetRangeTransform(
403 const B2DRange
& rSourceRange
,
404 const B2DRange
& rTargetRange
)
406 B2DHomMatrix aRetval
;
408 if(&rSourceRange
== &rTargetRange
)
413 if(!fTools::equalZero(rSourceRange
.getMinX()) || !fTools::equalZero(rSourceRange
.getMinY()))
415 aRetval
.set(0, 2, -rSourceRange
.getMinX());
416 aRetval
.set(1, 2, -rSourceRange
.getMinY());
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());
426 if(!fTools::equalZero(fScaleX
) || !fTools::equalZero(fScaleY
))
428 aRetval
.scale(fScaleX
, fScaleY
);
431 if(!fTools::equalZero(rTargetRange
.getMinX()) || !fTools::equalZero(rTargetRange
.getMinY()))
434 rTargetRange
.getMinX(),
435 rTargetRange
.getMinY());
441 B2DHomMatrix
createCoordinateSystemTransform(
442 const B2DPoint
& rOrigin
,
446 return basegfx::B2DHomMatrix(
447 rX
.getX(), rY
.getX(), rOrigin
.getX(),
448 rX
.getY(), rY
.getY(), rOrigin
.getY());
451 B2DTuple
getColumn(const B2DHomMatrix
& rMatrix
, sal_uInt16 nCol
)
453 return B2DTuple(rMatrix
.get(0, nCol
), rMatrix
.get(1, nCol
));
455 } // end of namespace
457 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */