/src/libreoffice/svx/source/svdraw/svdtrans.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 | | |
21 | | #include <svx/svdtrans.hxx> |
22 | | #include <math.h> |
23 | | #include <svx/xpoly.hxx> |
24 | | #include <rtl/ustrbuf.hxx> |
25 | | |
26 | | #include <vcl/virdev.hxx> |
27 | | #include <tools/bigint.hxx> |
28 | | #include <tools/UnitConversion.hxx> |
29 | | #include <unotools/syslocale.hxx> |
30 | | #include <unotools/localedatawrapper.hxx> |
31 | | #include <sal/log.hxx> |
32 | | |
33 | | void MoveXPoly(XPolygon& rPoly, const Size& S) |
34 | 2.84k | { |
35 | 2.84k | rPoly.Move(S.Width(),S.Height()); |
36 | 2.84k | } |
37 | | |
38 | | void ResizeRect(tools::Rectangle& rRect, const Point& rRef, double aXFact, double aYFact) |
39 | 6.20k | { |
40 | 6.20k | rRect.SetLeft( rRef.X() + basegfx::fround<tools::Long>( (rRect.Left() - rRef.X()) * aXFact ) ); |
41 | 6.20k | rRect.SetRight( rRef.X() + basegfx::fround<tools::Long>( (rRect.Right() - rRef.X()) * aXFact ) ); |
42 | | |
43 | 6.20k | rRect.SetTop( rRef.Y() + basegfx::fround<tools::Long>( (rRect.Top() - rRef.Y()) * aYFact ) ); |
44 | 6.20k | rRect.SetBottom( rRef.Y() + basegfx::fround<tools::Long>( (rRect.Bottom() - rRef.Y()) * aYFact ) ); |
45 | | |
46 | 6.20k | rRect.Normalize(); |
47 | 6.20k | } |
48 | | |
49 | | void ResizePoly(tools::Polygon& rPoly, const Point& rRef, double xFact, double yFact) |
50 | 0 | { |
51 | 0 | sal_uInt16 nCount=rPoly.GetSize(); |
52 | 0 | for (sal_uInt16 i=0; i<nCount; i++) { |
53 | 0 | ResizePoint(rPoly[i],rRef,xFact,yFact); |
54 | 0 | } |
55 | 0 | } |
56 | | |
57 | | void ResizeXPoly(XPolygon& rPoly, const Point& rRef, double xFact, double yFact) |
58 | 4.43k | { |
59 | 4.43k | sal_uInt16 nCount=rPoly.GetPointCount(); |
60 | 915k | for (sal_uInt16 i=0; i<nCount; i++) { |
61 | 910k | ResizePoint(rPoly[i],rRef,xFact,yFact); |
62 | 910k | } |
63 | 4.43k | } |
64 | | |
65 | | void RotatePoly(tools::Polygon& rPoly, const Point& rRef, double sn, double cs) |
66 | 71.9k | { |
67 | 71.9k | sal_uInt16 nCount=rPoly.GetSize(); |
68 | 431k | for (sal_uInt16 i=0; i<nCount; i++) { |
69 | 359k | RotatePoint(rPoly[i],rRef,sn,cs); |
70 | 359k | } |
71 | 71.9k | } |
72 | | |
73 | | void RotateXPoly(XPolygon& rPoly, const Point& rRef, double sn, double cs) |
74 | 282 | { |
75 | 282 | sal_uInt16 nCount=rPoly.GetPointCount(); |
76 | 886 | for (sal_uInt16 i=0; i<nCount; i++) { |
77 | 604 | RotatePoint(rPoly[i],rRef,sn,cs); |
78 | 604 | } |
79 | 282 | } |
80 | | |
81 | | void RotateXPoly(XPolyPolygon& rPoly, const Point& rRef, double sn, double cs) |
82 | 0 | { |
83 | 0 | sal_uInt16 nCount=rPoly.Count(); |
84 | 0 | for (sal_uInt16 i=0; i<nCount; i++) { |
85 | 0 | RotateXPoly(rPoly[i],rRef,sn,cs); |
86 | 0 | } |
87 | 0 | } |
88 | | |
89 | | void MirrorPoint(Point& rPnt, const Point& rRef1, const Point& rRef2) |
90 | 477k | { |
91 | 477k | tools::Long mx=rRef2.X()-rRef1.X(); |
92 | 477k | tools::Long my=rRef2.Y()-rRef1.Y(); |
93 | 477k | if (mx==0) { // vertical axis |
94 | 232k | tools::Long dx=rRef1.X()-rPnt.X(); |
95 | 232k | rPnt.AdjustX(2*dx ); |
96 | 245k | } else if (my==0) { // horizontal axis |
97 | 245k | tools::Long dy=rRef1.Y()-rPnt.Y(); |
98 | 245k | rPnt.AdjustY(2*dy ); |
99 | 245k | } else if (mx==my) { // diagonal axis '\' |
100 | 0 | tools::Long dx1=rPnt.X()-rRef1.X(); |
101 | 0 | tools::Long dy1=rPnt.Y()-rRef1.Y(); |
102 | 0 | rPnt.setX(rRef1.X()+dy1 ); |
103 | 0 | rPnt.setY(rRef1.Y()+dx1 ); |
104 | 0 | } else if (mx==-my) { // diagonal axis '/' |
105 | 0 | tools::Long dx1=rPnt.X()-rRef1.X(); |
106 | 0 | tools::Long dy1=rPnt.Y()-rRef1.Y(); |
107 | 0 | rPnt.setX(rRef1.X()-dy1 ); |
108 | 0 | rPnt.setY(rRef1.Y()-dx1 ); |
109 | 0 | } else { // arbitrary axis |
110 | | // TODO: Optimize this! Raise perpendicular on the mirroring axis..? |
111 | 0 | Degree100 nRefAngle=GetAngle(rRef2-rRef1); |
112 | 0 | rPnt-=rRef1; |
113 | 0 | Degree100 nPntAngle=GetAngle(rPnt); |
114 | 0 | Degree100 nAngle=2_deg100*(nRefAngle-nPntAngle); |
115 | 0 | double a = toRadians(nAngle); |
116 | 0 | double nSin=sin(a); |
117 | 0 | double nCos=cos(a); |
118 | 0 | RotatePoint(rPnt,Point(),nSin,nCos); |
119 | 0 | rPnt+=rRef1; |
120 | 0 | } |
121 | 477k | } |
122 | | |
123 | | void MirrorXPoly(XPolygon& rPoly, const Point& rRef1, const Point& rRef2) |
124 | 763 | { |
125 | 763 | sal_uInt16 nCount=rPoly.GetPointCount(); |
126 | 2.69k | for (sal_uInt16 i=0; i<nCount; i++) { |
127 | 1.93k | MirrorPoint(rPoly[i],rRef1,rRef2); |
128 | 1.93k | } |
129 | 763 | } |
130 | | |
131 | | void ShearPoly(tools::Polygon& rPoly, const Point& rRef, double tn) |
132 | 54 | { |
133 | 54 | sal_uInt16 nCount=rPoly.GetSize(); |
134 | 324 | for (sal_uInt16 i=0; i<nCount; i++) { |
135 | 270 | ShearPoint(rPoly[i],rRef,tn); |
136 | 270 | } |
137 | 54 | } |
138 | | |
139 | | void ShearXPoly(XPolygon& rPoly, const Point& rRef, double tn, bool bVShear) |
140 | 0 | { |
141 | 0 | sal_uInt16 nCount=rPoly.GetPointCount(); |
142 | 0 | for (sal_uInt16 i=0; i<nCount; i++) { |
143 | 0 | ShearPoint(rPoly[i],rRef,tn,bVShear); |
144 | 0 | } |
145 | 0 | } |
146 | | |
147 | | double CrookRotateXPoint(Point& rPnt, Point* pC1, Point* pC2, const Point& rCenter, |
148 | | const Point& rRad, double& rSin, double& rCos, bool bVert) |
149 | 0 | { |
150 | 0 | bool bC1=pC1!=nullptr; |
151 | 0 | bool bC2=pC2!=nullptr; |
152 | 0 | tools::Long x0=rPnt.X(); |
153 | 0 | tools::Long y0=rPnt.Y(); |
154 | 0 | tools::Long cx=rCenter.X(); |
155 | 0 | tools::Long cy=rCenter.Y(); |
156 | 0 | double nAngle=GetCrookAngle(rPnt,rCenter,rRad,bVert); |
157 | 0 | double sn=sin(nAngle); |
158 | 0 | double cs=cos(nAngle); |
159 | 0 | RotatePoint(rPnt,rCenter,sn,cs); |
160 | 0 | if (bC1) { |
161 | 0 | if (bVert) { |
162 | | // move into the direction of the center, as a basic position for the rotation |
163 | 0 | pC1->AdjustY( -y0 ); |
164 | | // resize, account for the distance from the center |
165 | 0 | pC1->setY(basegfx::fround<tools::Long>(static_cast<double>(pC1->Y()) /rRad.X()*(cx-pC1->X())) ); |
166 | 0 | pC1->AdjustY(cy ); |
167 | 0 | } else { |
168 | | // move into the direction of the center, as a basic position for the rotation |
169 | 0 | pC1->AdjustX( -x0 ); |
170 | | // resize, account for the distance from the center |
171 | 0 | tools::Long nPntRad=cy-pC1->Y(); |
172 | 0 | double nFact=static_cast<double>(nPntRad)/static_cast<double>(rRad.Y()); |
173 | 0 | pC1->setX(basegfx::fround<tools::Long>(static_cast<double>(pC1->X()) * nFact)); |
174 | 0 | pC1->AdjustX(cx ); |
175 | 0 | } |
176 | 0 | RotatePoint(*pC1,rCenter,sn,cs); |
177 | 0 | } |
178 | 0 | if (bC2) { |
179 | 0 | if (bVert) { |
180 | | // move into the direction of the center, as a basic position for the rotation |
181 | 0 | pC2->AdjustY( -y0 ); |
182 | | // resize, account for the distance from the center |
183 | 0 | pC2->setY(basegfx::fround<tools::Long>(static_cast<double>(pC2->Y()) /rRad.X()*(rCenter.X()-pC2->X())) ); |
184 | 0 | pC2->AdjustY(cy ); |
185 | 0 | } else { |
186 | | // move into the direction of the center, as a basic position for the rotation |
187 | 0 | pC2->AdjustX( -x0 ); |
188 | | // resize, account for the distance from the center |
189 | 0 | tools::Long nPntRad=rCenter.Y()-pC2->Y(); |
190 | 0 | double nFact=static_cast<double>(nPntRad)/static_cast<double>(rRad.Y()); |
191 | 0 | pC2->setX(basegfx::fround<tools::Long>(static_cast<double>(pC2->X()) * nFact)); |
192 | 0 | pC2->AdjustX(cx ); |
193 | 0 | } |
194 | 0 | RotatePoint(*pC2,rCenter,sn,cs); |
195 | 0 | } |
196 | 0 | rSin=sn; |
197 | 0 | rCos=cs; |
198 | 0 | return nAngle; |
199 | 0 | } |
200 | | |
201 | | double CrookSlantXPoint(Point& rPnt, Point* pC1, Point* pC2, const Point& rCenter, |
202 | | const Point& rRad, double& rSin, double& rCos, bool bVert) |
203 | 0 | { |
204 | 0 | bool bC1=pC1!=nullptr; |
205 | 0 | bool bC2=pC2!=nullptr; |
206 | 0 | tools::Long x0=rPnt.X(); |
207 | 0 | tools::Long y0=rPnt.Y(); |
208 | 0 | tools::Long dx1=0,dy1=0; |
209 | 0 | tools::Long dxC1=0,dyC1=0; |
210 | 0 | tools::Long dxC2=0,dyC2=0; |
211 | 0 | if (bVert) { |
212 | 0 | tools::Long nStart=rCenter.X()-rRad.X(); |
213 | 0 | dx1=rPnt.X()-nStart; |
214 | 0 | rPnt.setX(nStart ); |
215 | 0 | if (bC1) { |
216 | 0 | dxC1=pC1->X()-nStart; |
217 | 0 | pC1->setX(nStart ); |
218 | 0 | } |
219 | 0 | if (bC2) { |
220 | 0 | dxC2=pC2->X()-nStart; |
221 | 0 | pC2->setX(nStart ); |
222 | 0 | } |
223 | 0 | } else { |
224 | 0 | tools::Long nStart=rCenter.Y()-rRad.Y(); |
225 | 0 | dy1=rPnt.Y()-nStart; |
226 | 0 | rPnt.setY(nStart ); |
227 | 0 | if (bC1) { |
228 | 0 | dyC1=pC1->Y()-nStart; |
229 | 0 | pC1->setY(nStart ); |
230 | 0 | } |
231 | 0 | if (bC2) { |
232 | 0 | dyC2=pC2->Y()-nStart; |
233 | 0 | pC2->setY(nStart ); |
234 | 0 | } |
235 | 0 | } |
236 | 0 | double nAngle=GetCrookAngle(rPnt,rCenter,rRad,bVert); |
237 | 0 | double sn=sin(nAngle); |
238 | 0 | double cs=cos(nAngle); |
239 | 0 | RotatePoint(rPnt,rCenter,sn,cs); |
240 | 0 | if (bC1) { if (bVert) pC1->AdjustY( -(y0-rCenter.Y()) ); else pC1->AdjustX( -(x0-rCenter.X()) ); RotatePoint(*pC1,rCenter,sn,cs); } |
241 | 0 | if (bC2) { if (bVert) pC2->AdjustY( -(y0-rCenter.Y()) ); else pC2->AdjustX( -(x0-rCenter.X()) ); RotatePoint(*pC2,rCenter,sn,cs); } |
242 | 0 | if (bVert) { |
243 | 0 | rPnt.AdjustX(dx1 ); |
244 | 0 | if (bC1) pC1->AdjustX(dxC1 ); |
245 | 0 | if (bC2) pC2->AdjustX(dxC2 ); |
246 | 0 | } else { |
247 | 0 | rPnt.AdjustY(dy1 ); |
248 | 0 | if (bC1) pC1->AdjustY(dyC1 ); |
249 | 0 | if (bC2) pC2->AdjustY(dyC2 ); |
250 | 0 | } |
251 | 0 | rSin=sn; |
252 | 0 | rCos=cs; |
253 | 0 | return nAngle; |
254 | 0 | } |
255 | | |
256 | | double CrookStretchXPoint(Point& rPnt, Point* pC1, Point* pC2, const Point& rCenter, |
257 | | const Point& rRad, double& rSin, double& rCos, bool bVert, |
258 | | const tools::Rectangle& rRefRect) |
259 | 0 | { |
260 | 0 | tools::Long y0=rPnt.Y(); |
261 | 0 | CrookSlantXPoint(rPnt,pC1,pC2,rCenter,rRad,rSin,rCos,bVert); |
262 | 0 | if (bVert) { |
263 | 0 | } else { |
264 | 0 | tools::Long nTop=rRefRect.Top(); |
265 | 0 | tools::Long nBtm=rRefRect.Bottom(); |
266 | 0 | tools::Long nHgt=nBtm-nTop; |
267 | 0 | tools::Long dy=rPnt.Y()-y0; |
268 | 0 | double a=static_cast<double>(y0-nTop)/nHgt; |
269 | 0 | a*=dy; |
270 | 0 | rPnt.setY(y0 + basegfx::fround<tools::Long>(a)); |
271 | 0 | } |
272 | 0 | return 0.0; |
273 | 0 | } |
274 | | |
275 | | |
276 | | void CrookRotatePoly(XPolygon& rPoly, const Point& rCenter, const Point& rRad, bool bVert) |
277 | 0 | { |
278 | 0 | double nSin,nCos; |
279 | 0 | sal_uInt16 nPointCnt=rPoly.GetPointCount(); |
280 | 0 | sal_uInt16 i=0; |
281 | 0 | while (i<nPointCnt) { |
282 | 0 | Point* pPnt=&rPoly[i]; |
283 | 0 | Point* pC1=nullptr; |
284 | 0 | Point* pC2=nullptr; |
285 | 0 | if (i+1<nPointCnt && rPoly.IsControl(i)) { // control point to the left |
286 | 0 | pC1=pPnt; |
287 | 0 | i++; |
288 | 0 | pPnt=&rPoly[i]; |
289 | 0 | } |
290 | 0 | i++; |
291 | 0 | if (i<nPointCnt && rPoly.IsControl(i)) { // control point to the right |
292 | 0 | pC2=&rPoly[i]; |
293 | 0 | i++; |
294 | 0 | } |
295 | 0 | CrookRotateXPoint(*pPnt,pC1,pC2,rCenter,rRad,nSin,nCos,bVert); |
296 | 0 | } |
297 | 0 | } |
298 | | |
299 | | void CrookSlantPoly(XPolygon& rPoly, const Point& rCenter, const Point& rRad, bool bVert) |
300 | 0 | { |
301 | 0 | double nSin,nCos; |
302 | 0 | sal_uInt16 nPointCnt=rPoly.GetPointCount(); |
303 | 0 | sal_uInt16 i=0; |
304 | 0 | while (i<nPointCnt) { |
305 | 0 | Point* pPnt=&rPoly[i]; |
306 | 0 | Point* pC1=nullptr; |
307 | 0 | Point* pC2=nullptr; |
308 | 0 | if (i+1<nPointCnt && rPoly.IsControl(i)) { // control point to the left |
309 | 0 | pC1=pPnt; |
310 | 0 | i++; |
311 | 0 | pPnt=&rPoly[i]; |
312 | 0 | } |
313 | 0 | i++; |
314 | 0 | if (i<nPointCnt && rPoly.IsControl(i)) { // control point to the right |
315 | 0 | pC2=&rPoly[i]; |
316 | 0 | i++; |
317 | 0 | } |
318 | 0 | CrookSlantXPoint(*pPnt,pC1,pC2,rCenter,rRad,nSin,nCos,bVert); |
319 | 0 | } |
320 | 0 | } |
321 | | |
322 | | void CrookStretchPoly(XPolygon& rPoly, const Point& rCenter, const Point& rRad, bool bVert, const tools::Rectangle& rRefRect) |
323 | 0 | { |
324 | 0 | double nSin,nCos; |
325 | 0 | sal_uInt16 nPointCnt=rPoly.GetPointCount(); |
326 | 0 | sal_uInt16 i=0; |
327 | 0 | while (i<nPointCnt) { |
328 | 0 | Point* pPnt=&rPoly[i]; |
329 | 0 | Point* pC1=nullptr; |
330 | 0 | Point* pC2=nullptr; |
331 | 0 | if (i+1<nPointCnt && rPoly.IsControl(i)) { // control point to the left |
332 | 0 | pC1=pPnt; |
333 | 0 | i++; |
334 | 0 | pPnt=&rPoly[i]; |
335 | 0 | } |
336 | 0 | i++; |
337 | 0 | if (i<nPointCnt && rPoly.IsControl(i)) { // control point to the right |
338 | 0 | pC2=&rPoly[i]; |
339 | 0 | i++; |
340 | 0 | } |
341 | 0 | CrookStretchXPoint(*pPnt,pC1,pC2,rCenter,rRad,nSin,nCos,bVert,rRefRect); |
342 | 0 | } |
343 | 0 | } |
344 | | |
345 | | |
346 | | void CrookRotatePoly(XPolyPolygon& rPoly, const Point& rCenter, const Point& rRad, bool bVert) |
347 | 0 | { |
348 | 0 | sal_uInt16 nPolyCount=rPoly.Count(); |
349 | 0 | for (sal_uInt16 nPolyNum=0; nPolyNum<nPolyCount; nPolyNum++) { |
350 | 0 | CrookRotatePoly(rPoly[nPolyNum],rCenter,rRad,bVert); |
351 | 0 | } |
352 | 0 | } |
353 | | |
354 | | void CrookSlantPoly(XPolyPolygon& rPoly, const Point& rCenter, const Point& rRad, bool bVert) |
355 | 0 | { |
356 | 0 | sal_uInt16 nPolyCount=rPoly.Count(); |
357 | 0 | for (sal_uInt16 nPolyNum=0; nPolyNum<nPolyCount; nPolyNum++) { |
358 | 0 | CrookSlantPoly(rPoly[nPolyNum],rCenter,rRad,bVert); |
359 | 0 | } |
360 | 0 | } |
361 | | |
362 | | void CrookStretchPoly(XPolyPolygon& rPoly, const Point& rCenter, const Point& rRad, bool bVert, const tools::Rectangle& rRefRect) |
363 | 0 | { |
364 | 0 | sal_uInt16 nPolyCount=rPoly.Count(); |
365 | 0 | for (sal_uInt16 nPolyNum=0; nPolyNum<nPolyCount; nPolyNum++) { |
366 | 0 | CrookStretchPoly(rPoly[nPolyNum],rCenter,rRad,bVert,rRefRect); |
367 | 0 | } |
368 | 0 | } |
369 | | |
370 | | |
371 | | Degree100 GetAngle(const Point& rPnt) |
372 | 256k | { |
373 | 256k | Degree100 a; |
374 | 256k | if (rPnt.Y()==0) { |
375 | 117k | if (rPnt.X()<0) a=-18000_deg100; |
376 | 138k | } else if (rPnt.X()==0) { |
377 | 69.8k | if (rPnt.Y()>0) a=-9000_deg100; |
378 | 6.47k | else a=9000_deg100; |
379 | 69.8k | } else { |
380 | 69.0k | a = Degree100(basegfx::fround(basegfx::rad2deg<100>(atan2(-static_cast<double>(rPnt.Y()), static_cast<double>(rPnt.X()))))); |
381 | 69.0k | } |
382 | 256k | return a; |
383 | 256k | } |
384 | | |
385 | | Degree100 NormAngle18000(Degree100 a) |
386 | 78.1k | { |
387 | 78.1k | while (a<-18000_deg100) a+=36000_deg100; |
388 | 156k | while (a>=18000_deg100) a-=36000_deg100; |
389 | 78.1k | return a; |
390 | 78.1k | } |
391 | | |
392 | | Degree100 NormAngle36000(Degree100 a) |
393 | 562k | { |
394 | 562k | a %= 36000_deg100; |
395 | 562k | if (a < 0_deg100) |
396 | 157k | a += 36000_deg100; |
397 | 562k | return a; |
398 | 562k | } |
399 | | |
400 | 0 | sal_uInt16 GetAngleSector(Degree100 nAngle) { return (NormAngle36000(nAngle) / 9000_deg100).get(); } |
401 | | |
402 | | tools::Long GetLen(const Point& rPnt) |
403 | 0 | { |
404 | 0 | tools::Long x=std::abs(rPnt.X()); |
405 | 0 | tools::Long y=std::abs(rPnt.Y()); |
406 | 0 | if (x+y<0x8000) { // because 7FFF * 7FFF * 2 = 7FFE0002 |
407 | 0 | x*=x; |
408 | 0 | y*=y; |
409 | 0 | x+=y; |
410 | 0 | x = basegfx::fround<tools::Long>(sqrt(x)); |
411 | 0 | return x; |
412 | 0 | } else { |
413 | 0 | double nx=x; |
414 | 0 | double ny=y; |
415 | 0 | nx*=nx; |
416 | 0 | ny*=ny; |
417 | 0 | nx+=ny; |
418 | 0 | nx=sqrt(nx); |
419 | 0 | if (nx>0x7FFFFFFF) { |
420 | 0 | return 0x7FFFFFFF; // we can't go any further, for fear of an overrun! |
421 | 0 | } else { |
422 | 0 | return basegfx::fround<tools::Long>(nx); |
423 | 0 | } |
424 | 0 | } |
425 | 0 | } |
426 | | |
427 | | |
428 | | void GeoStat::RecalcSinCos() |
429 | 280k | { |
430 | 280k | if (m_nRotationAngle==0_deg100) { |
431 | 162k | mfSinRotationAngle=0.0; |
432 | 162k | mfCosRotationAngle=1.0; |
433 | 162k | } else { |
434 | 118k | double a = toRadians(m_nRotationAngle); |
435 | 118k | mfSinRotationAngle=sin(a); |
436 | 118k | mfCosRotationAngle=cos(a); |
437 | 118k | } |
438 | 280k | } |
439 | | |
440 | | void GeoStat::RecalcTan() |
441 | 276k | { |
442 | 276k | if (m_nShearAngle==0_deg100) { |
443 | 254k | mfTanShearAngle=0.0; |
444 | 254k | } else { |
445 | 21.9k | double a = toRadians(m_nShearAngle); |
446 | 21.9k | mfTanShearAngle=tan(a); |
447 | 21.9k | } |
448 | 276k | } |
449 | | |
450 | | |
451 | | tools::Polygon Rect2Poly(const tools::Rectangle& rRect, const GeoStat& rGeo) |
452 | 117k | { |
453 | 117k | tools::Polygon aPol(5); |
454 | 117k | aPol[0]=rRect.TopLeft(); |
455 | 117k | aPol[1]=rRect.TopRight(); |
456 | 117k | aPol[2]=rRect.BottomRight(); |
457 | 117k | aPol[3]=rRect.BottomLeft(); |
458 | 117k | aPol[4]=rRect.TopLeft(); |
459 | 117k | if (rGeo.m_nShearAngle) ShearPoly(aPol,rRect.TopLeft(),rGeo.mfTanShearAngle); |
460 | 117k | if (rGeo.m_nRotationAngle) RotatePoly(aPol,rRect.TopLeft(),rGeo.mfSinRotationAngle,rGeo.mfCosRotationAngle); |
461 | 117k | return aPol; |
462 | 117k | } |
463 | | |
464 | | namespace svx |
465 | | { |
466 | | tools::Rectangle polygonToRectangle(const tools::Polygon& rPolygon, GeoStat& rGeo) |
467 | 78.1k | { |
468 | 78.1k | rGeo.m_nRotationAngle = GetAngle(rPolygon[1] - rPolygon[0]); |
469 | 78.1k | rGeo.m_nRotationAngle = NormAngle36000(rGeo.m_nRotationAngle); |
470 | | |
471 | | // rotation successful |
472 | 78.1k | rGeo.RecalcSinCos(); |
473 | | |
474 | 78.1k | Point aPoint1(rPolygon[1] - rPolygon[0]); |
475 | 78.1k | if (rGeo.m_nRotationAngle) |
476 | 51.5k | RotatePoint(aPoint1, Point(0,0), -rGeo.mfSinRotationAngle, rGeo.mfCosRotationAngle); // -Sin to reverse rotation |
477 | 78.1k | tools::Long nWidth = aPoint1.X(); |
478 | | |
479 | 78.1k | Point aPoint0(rPolygon[0]); |
480 | 78.1k | Point aPoint3(rPolygon[3] - rPolygon[0]); |
481 | 78.1k | if (rGeo.m_nRotationAngle) |
482 | 51.5k | RotatePoint(aPoint3, Point(0,0), -rGeo.mfSinRotationAngle, rGeo.mfCosRotationAngle); // -Sin to reverse rotation |
483 | 78.1k | tools::Long nHeight = aPoint3.Y(); |
484 | | |
485 | 78.1k | Degree100 nShearAngle = GetAngle(aPoint3); |
486 | 78.1k | nShearAngle -= 27000_deg100; // ShearWink is measured against a vertical line |
487 | 78.1k | nShearAngle = -nShearAngle; // negating, because '+' is shearing clock-wise |
488 | | |
489 | 78.1k | bool bMirror = aPoint3.Y() < 0; |
490 | 78.1k | if (bMirror) |
491 | 1.40k | { // "exchange of points" when mirroring |
492 | 1.40k | nHeight = -nHeight; |
493 | 1.40k | nShearAngle += 18000_deg100; |
494 | 1.40k | aPoint0 = rPolygon[3]; |
495 | 1.40k | } |
496 | | |
497 | 78.1k | nShearAngle = NormAngle18000(nShearAngle); |
498 | 78.1k | if (nShearAngle < -9000_deg100 || nShearAngle > 9000_deg100) |
499 | 0 | { |
500 | 0 | nShearAngle = NormAngle18000(nShearAngle + 18000_deg100); |
501 | 0 | } |
502 | | |
503 | 78.1k | if (nShearAngle < -SDRMAXSHEAR) |
504 | 17.2k | nShearAngle = -SDRMAXSHEAR; // limit ShearWinkel (shear angle) to +/- 89.00 deg |
505 | | |
506 | 78.1k | if (nShearAngle > SDRMAXSHEAR) |
507 | 740 | nShearAngle = SDRMAXSHEAR; |
508 | | |
509 | 78.1k | rGeo.m_nShearAngle = nShearAngle; |
510 | 78.1k | rGeo.RecalcTan(); |
511 | | |
512 | 78.1k | Point aRU(aPoint0); |
513 | 78.1k | aRU.AdjustX(nWidth); |
514 | 78.1k | aRU.AdjustY(nHeight); |
515 | | |
516 | 78.1k | return tools::Rectangle(aPoint0, aRU); |
517 | 78.1k | } |
518 | | |
519 | | } // end svx |
520 | | |
521 | | void OrthoDistance8(const Point& rPt0, Point& rPt, bool bBigOrtho) |
522 | 0 | { |
523 | 0 | tools::Long dx=rPt.X()-rPt0.X(); |
524 | 0 | tools::Long dy=rPt.Y()-rPt0.Y(); |
525 | 0 | tools::Long dxa=std::abs(dx); |
526 | 0 | tools::Long dya=std::abs(dy); |
527 | 0 | if (dx==0 || dy==0 || dxa==dya) return; |
528 | 0 | if (dxa>=dya*2) { rPt.setY(rPt0.Y() ); return; } |
529 | 0 | if (dya>=dxa*2) { rPt.setX(rPt0.X() ); return; } |
530 | 0 | if ((dxa<dya) != bBigOrtho) { |
531 | 0 | rPt.setY(rPt0.Y()+(dxa* (dy>=0 ? 1 : -1) ) ); |
532 | 0 | } else { |
533 | 0 | rPt.setX(rPt0.X()+(dya* (dx>=0 ? 1 : -1) ) ); |
534 | 0 | } |
535 | 0 | } |
536 | | |
537 | | void OrthoDistance4(const Point& rPt0, Point& rPt, bool bBigOrtho) |
538 | 0 | { |
539 | 0 | tools::Long dx=rPt.X()-rPt0.X(); |
540 | 0 | tools::Long dy=rPt.Y()-rPt0.Y(); |
541 | 0 | tools::Long dxa=std::abs(dx); |
542 | 0 | tools::Long dya=std::abs(dy); |
543 | 0 | if ((dxa<dya) != bBigOrtho) { |
544 | 0 | rPt.setY(rPt0.Y()+(dxa* (dy>=0 ? 1 : -1) ) ); |
545 | 0 | } else { |
546 | 0 | rPt.setX(rPt0.X()+(dya* (dx>=0 ? 1 : -1) ) ); |
547 | 0 | } |
548 | 0 | } |
549 | | |
550 | | |
551 | | tools::Long BigMulDiv(tools::Long nVal, tools::Long nMul, tools::Long nDiv) |
552 | 287k | { |
553 | 287k | if (!nDiv) |
554 | 0 | return 0x7fffffff; |
555 | 287k | return BigInt::Scale(nVal, nMul, nDiv); |
556 | 287k | } |
557 | | |
558 | | static FrPair toPair(o3tl::Length eFrom, o3tl::Length eTo) |
559 | 87.7k | { |
560 | 87.7k | const auto [nNum, nDen] = o3tl::getConversionMulDiv(eFrom, eTo); |
561 | 87.7k | return FrPair(nNum, nDen, nNum, nDen); |
562 | 87.7k | } |
563 | | |
564 | | // How many eU units fit into a mm, respectively an inch? |
565 | | // Or: How many mm, respectively inches, are there in an eU (and then give me the inverse) |
566 | | |
567 | | static FrPair GetInchOrMM(MapUnit eU) |
568 | 0 | { |
569 | 0 | switch (eU) { |
570 | 0 | case MapUnit::Map1000thInch: return toPair(o3tl::Length::in, o3tl::Length::in1000); |
571 | 0 | case MapUnit::Map100thInch : return toPair(o3tl::Length::in, o3tl::Length::in100); |
572 | 0 | case MapUnit::Map10thInch : return toPair(o3tl::Length::in, o3tl::Length::in10); |
573 | 0 | case MapUnit::MapInch : return toPair(o3tl::Length::in, o3tl::Length::in); |
574 | 0 | case MapUnit::MapPoint : return toPair(o3tl::Length::in, o3tl::Length::pt); |
575 | 0 | case MapUnit::MapTwip : return toPair(o3tl::Length::in, o3tl::Length::twip); |
576 | 0 | case MapUnit::Map100thMM : return toPair(o3tl::Length::mm, o3tl::Length::mm100); |
577 | 0 | case MapUnit::Map10thMM : return toPair(o3tl::Length::mm, o3tl::Length::mm10); |
578 | 0 | case MapUnit::MapMM : return toPair(o3tl::Length::mm, o3tl::Length::mm); |
579 | 0 | case MapUnit::MapCM : return toPair(o3tl::Length::mm, o3tl::Length::cm); |
580 | 0 | case MapUnit::MapPixel : { |
581 | 0 | ScopedVclPtrInstance< VirtualDevice > pVD; |
582 | 0 | pVD->SetMapMode(MapMode(MapUnit::Map100thMM)); |
583 | 0 | Point aP(pVD->PixelToLogic(Point(64,64))); // 64 pixels for more accuracy |
584 | 0 | return FrPair(6400,aP.X(),6400,aP.Y()); |
585 | 0 | } |
586 | 0 | case MapUnit::MapAppFont: case MapUnit::MapSysFont: { |
587 | 0 | ScopedVclPtrInstance< VirtualDevice > pVD; |
588 | 0 | pVD->SetMapMode(MapMode(eU)); |
589 | 0 | Point aP(pVD->LogicToPixel(Point(32,32))); // 32 units for more accuracy |
590 | 0 | pVD->SetMapMode(MapMode(MapUnit::Map100thMM)); |
591 | 0 | aP=pVD->PixelToLogic(aP); |
592 | 0 | return FrPair(3200,aP.X(),3200,aP.Y()); |
593 | 0 | } |
594 | 0 | default: break; |
595 | 0 | } |
596 | 0 | return 1.0; |
597 | 0 | } |
598 | | |
599 | | // Calculate the factor that we need to convert units from eS to eD. |
600 | | // e. g. GetMapFactor(UNIT_MM,UNIT_100TH_MM) => 100. |
601 | | |
602 | | FrPair GetMapFactor(MapUnit eS, MapUnit eD) |
603 | 103k | { |
604 | 103k | if (eS==eD) return FrPair(1,1,1,1); |
605 | 87.7k | const auto eFrom = MapToO3tlLength(eS, o3tl::Length::invalid); |
606 | 87.7k | const auto eTo = MapToO3tlLength(eD, o3tl::Length::invalid); |
607 | 87.7k | if (eFrom != o3tl::Length::invalid && eTo != o3tl::Length::invalid) |
608 | 87.7k | return toPair(eFrom, eTo); |
609 | 0 | FrPair aS(GetInchOrMM(eS)); |
610 | 0 | FrPair aD(GetInchOrMM(eD)); |
611 | 0 | bool bSInch=IsInch(eS); |
612 | 0 | bool bDInch=IsInch(eD); |
613 | 0 | FrPair aRet(aD.X()/aS.X(),aD.Y()/aS.Y()); |
614 | 0 | if (bSInch && !bDInch) { aRet.X() *= 127.0/5; aRet.Y() *= 127.0/5; } |
615 | 0 | if (!bSInch && bDInch) { aRet.X() *= 5.0/127; aRet.Y() *= 5.0/127; } |
616 | 0 | return aRet; |
617 | 87.7k | }; |
618 | | |
619 | | FrPair GetMapFactor(FieldUnit eS, FieldUnit eD) |
620 | 0 | { |
621 | 0 | if (eS==eD) return FrPair(1,1,1,1); |
622 | 0 | auto eFrom = FieldToO3tlLength(eS), eTo = FieldToO3tlLength(eD); |
623 | 0 | if (eFrom == o3tl::Length::invalid) |
624 | 0 | { |
625 | 0 | if (eTo == o3tl::Length::invalid) |
626 | 0 | return FrPair(1,1,1,1); |
627 | 0 | eFrom = IsInch(eD) ? o3tl::Length::in : o3tl::Length::mm; |
628 | 0 | } |
629 | 0 | else if (eTo == o3tl::Length::invalid) |
630 | 0 | eTo = IsInch(eS) ? o3tl::Length::in : o3tl::Length::mm; |
631 | 0 | return toPair(eFrom, eTo); |
632 | 0 | }; |
633 | | |
634 | | void SdrFormatter::Undirty() |
635 | 0 | { |
636 | 0 | const o3tl::Length eFrom = MapToO3tlLength(m_eSrcMU, o3tl::Length::invalid); |
637 | 0 | const o3tl::Length eTo = MapToO3tlLength(m_eDstMU, o3tl::Length::invalid); |
638 | 0 | if (eFrom != o3tl::Length::invalid && eTo != o3tl::Length::invalid) |
639 | 0 | { |
640 | 0 | const auto [mul, div] = o3tl::getConversionMulDiv(eFrom, eTo); |
641 | 0 | sal_Int64 nMul = mul; |
642 | 0 | sal_Int64 nDiv = div; |
643 | 0 | short nComma = 0; |
644 | | |
645 | | // shorten trailing zeros for dividend |
646 | 0 | while (0 == (nMul % 10)) |
647 | 0 | { |
648 | 0 | nComma--; |
649 | 0 | nMul /= 10; |
650 | 0 | } |
651 | | |
652 | | // shorten trailing zeros for divisor |
653 | 0 | while (0 == (nDiv % 10)) |
654 | 0 | { |
655 | 0 | nComma++; |
656 | 0 | nDiv /= 10; |
657 | 0 | } |
658 | 0 | m_nMul = nMul; |
659 | 0 | m_nDiv = nDiv; |
660 | 0 | m_nComma = nComma; |
661 | 0 | } |
662 | 0 | else |
663 | 0 | { |
664 | 0 | m_nMul = m_nDiv = 1; |
665 | 0 | m_nComma = 0; |
666 | 0 | } |
667 | 0 | m_bDirty=false; |
668 | 0 | } |
669 | | |
670 | | |
671 | | OUString SdrFormatter::GetStr(tools::Long nVal) const |
672 | 0 | { |
673 | 0 | static constexpr OUString aNullCode(u"0"_ustr); |
674 | |
|
675 | 0 | if(!nVal) |
676 | 0 | { |
677 | 0 | return aNullCode; |
678 | 0 | } |
679 | | |
680 | | // we may lose some decimal places here, because of MulDiv instead of Real |
681 | 0 | bool bNeg(nVal < 0); |
682 | 0 | SvtSysLocale aSysLoc; |
683 | 0 | const LocaleDataWrapper& rLoc = aSysLoc.GetLocaleData(); |
684 | |
|
685 | 0 | if (m_bDirty) |
686 | 0 | const_cast<SdrFormatter*>(this)->Undirty(); |
687 | |
|
688 | 0 | sal_Int16 nC(m_nComma); |
689 | |
|
690 | 0 | if(bNeg) |
691 | 0 | nVal = -nVal; |
692 | |
|
693 | 0 | while(nC <= -3) |
694 | 0 | { |
695 | 0 | nVal *= 1000; |
696 | 0 | nC += 3; |
697 | 0 | } |
698 | |
|
699 | 0 | while(nC <= -1) |
700 | 0 | { |
701 | 0 | nVal *= 10; |
702 | 0 | nC++; |
703 | 0 | } |
704 | |
|
705 | 0 | if(m_nMul != m_nDiv) |
706 | 0 | nVal = BigMulDiv(nVal, m_nMul, m_nDiv); |
707 | |
|
708 | 0 | OUStringBuffer aStr = OUString::number(nVal); |
709 | |
|
710 | 0 | if(nC > 0 && aStr.getLength() <= nC ) |
711 | 0 | { |
712 | | // decimal separator necessary |
713 | 0 | sal_Int32 nCount(nC - aStr.getLength()); |
714 | |
|
715 | 0 | if(nCount >= 0 && LocaleDataWrapper::isNumLeadingZero()) |
716 | 0 | nCount++; |
717 | |
|
718 | 0 | for(sal_Int32 i=0; i<nCount; i++) |
719 | 0 | aStr.insert(0, aNullCode); |
720 | | |
721 | | // remove superfluous decimal points |
722 | 0 | sal_Int32 nNumDigits(LocaleDataWrapper::getNumDigits()); |
723 | 0 | sal_Int32 nWeg(nC - nNumDigits); |
724 | |
|
725 | 0 | if(nWeg > 0) |
726 | 0 | { |
727 | | // TODO: we should round here |
728 | 0 | aStr.remove(aStr.getLength() - nWeg, nWeg); |
729 | 0 | nC = nNumDigits; |
730 | 0 | } |
731 | 0 | } |
732 | | |
733 | | // remember everything before the decimal separator for later |
734 | 0 | sal_Int32 nForComma(aStr.getLength() - nC); |
735 | |
|
736 | 0 | if(nC > 0) |
737 | 0 | { |
738 | | // insert comma char (decimal separator) |
739 | | // remove trailing zeros |
740 | 0 | while(nC > 0 && aStr[aStr.getLength() - 1] == aNullCode.getStr()[0]) |
741 | 0 | { |
742 | 0 | aStr.remove(aStr.getLength() - 1, 1); |
743 | 0 | nC--; |
744 | 0 | } |
745 | |
|
746 | 0 | if(nC > 0) |
747 | 0 | { |
748 | | // do we still have decimal places? |
749 | 0 | sal_Unicode cDec(rLoc.getNumDecimalSep()[0]); |
750 | 0 | aStr.insert(nForComma, cDec); |
751 | 0 | } |
752 | 0 | } |
753 | | |
754 | | // add in thousands separator (if necessary) |
755 | 0 | if( nForComma > 3 ) |
756 | 0 | { |
757 | 0 | const OUString& aThoSep( rLoc.getNumThousandSep() ); |
758 | 0 | if ( aThoSep.getLength() > 0 ) |
759 | 0 | { |
760 | 0 | sal_Unicode cTho( aThoSep[0] ); |
761 | 0 | sal_Int32 i(nForComma - 3); |
762 | |
|
763 | 0 | while(i > 0) |
764 | 0 | { |
765 | 0 | aStr.insert(i, cTho); |
766 | 0 | i -= 3; |
767 | 0 | } |
768 | 0 | } |
769 | 0 | } |
770 | |
|
771 | 0 | if(aStr.isEmpty()) |
772 | 0 | aStr.append(aNullCode); |
773 | |
|
774 | 0 | if(bNeg && (aStr.getLength() > 1 || aStr[0] != aNullCode.getStr()[0])) |
775 | 0 | { |
776 | 0 | aStr.insert(0, "-"); |
777 | 0 | } |
778 | |
|
779 | 0 | return aStr.makeStringAndClear(); |
780 | 0 | } |
781 | | |
782 | | OUString SdrFormatter::GetUnitStr(MapUnit eUnit) |
783 | 0 | { |
784 | 0 | switch(eUnit) |
785 | 0 | { |
786 | | // metrically |
787 | 0 | case MapUnit::Map100thMM : |
788 | 0 | return u"/100mm"_ustr; |
789 | 0 | case MapUnit::Map10thMM : |
790 | 0 | return u"/10mm"_ustr; |
791 | 0 | case MapUnit::MapMM : |
792 | 0 | return u"mm"_ustr; |
793 | 0 | case MapUnit::MapCM : |
794 | 0 | return u"cm"_ustr; |
795 | | |
796 | | // Inch |
797 | 0 | case MapUnit::Map1000thInch: |
798 | 0 | return u"/1000\""_ustr; |
799 | 0 | case MapUnit::Map100thInch : |
800 | 0 | return u"/100\""_ustr; |
801 | 0 | case MapUnit::Map10thInch : |
802 | 0 | return u"/10\""_ustr; |
803 | 0 | case MapUnit::MapInch : |
804 | 0 | return u"\""_ustr; |
805 | 0 | case MapUnit::MapPoint : |
806 | 0 | return u"pt"_ustr; |
807 | 0 | case MapUnit::MapTwip : |
808 | 0 | return u"twip"_ustr; |
809 | | |
810 | | // others |
811 | 0 | case MapUnit::MapPixel : |
812 | 0 | return u"pixel"_ustr; |
813 | 0 | case MapUnit::MapSysFont : |
814 | 0 | return u"sysfont"_ustr; |
815 | 0 | case MapUnit::MapAppFont : |
816 | 0 | return u"appfont"_ustr; |
817 | 0 | case MapUnit::MapRelative : |
818 | 0 | return u"%"_ustr; |
819 | 0 | default: |
820 | 0 | return OUString(); |
821 | 0 | } |
822 | 0 | } |
823 | | |
824 | | OUString SdrFormatter::GetUnitStr(FieldUnit eUnit) |
825 | 0 | { |
826 | 0 | switch(eUnit) |
827 | 0 | { |
828 | 0 | default : |
829 | 0 | case FieldUnit::NONE : |
830 | 0 | case FieldUnit::CUSTOM : |
831 | 0 | return OUString(); |
832 | | |
833 | | // metrically |
834 | 0 | case FieldUnit::MM_100TH: |
835 | 0 | return u"/100mm"_ustr; |
836 | 0 | case FieldUnit::MM : |
837 | 0 | return u"mm"_ustr; |
838 | 0 | case FieldUnit::CM : |
839 | 0 | return u"cm"_ustr; |
840 | 0 | case FieldUnit::M : |
841 | 0 | return u"m"_ustr; |
842 | 0 | case FieldUnit::KM : |
843 | 0 | return u"km"_ustr; |
844 | | |
845 | | // Inch |
846 | 0 | case FieldUnit::TWIP : |
847 | 0 | return u"twip"_ustr; |
848 | 0 | case FieldUnit::POINT : |
849 | 0 | return u"pt"_ustr; |
850 | 0 | case FieldUnit::PICA : |
851 | 0 | return u"pica"_ustr; |
852 | 0 | case FieldUnit::INCH : |
853 | 0 | return u"\""_ustr; |
854 | 0 | case FieldUnit::FOOT : |
855 | 0 | return u"ft"_ustr; |
856 | 0 | case FieldUnit::MILE : |
857 | 0 | return u"mile(s)"_ustr; |
858 | | |
859 | | // others |
860 | 0 | case FieldUnit::PERCENT: |
861 | 0 | return u"%"_ustr; |
862 | 0 | } |
863 | 0 | } |
864 | | |
865 | | |
866 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |