xref: /trunk/main/svx/source/svdraw/gradtrns.cxx (revision cdf0e10c)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_svx.hxx"
30 
31 #include "gradtrns.hxx"
32 #include <svx/svdobj.hxx>
33 #include <basegfx/range/b2drange.hxx>
34 #include <basegfx/matrix/b2dhommatrix.hxx>
35 #include <basegfx/matrix/b2dhommatrixtools.hxx>
36 #include <vcl/salbtype.hxx>		// FRound
37 
38 //////////////////////////////////////////////////////////////////////////////
39 
40 void GradTransformer::GradToVec(GradTransGradient& rG, GradTransVector& rV, const SdrObject* pObj)
41 {
42 	// handle start color
43 	rV.aCol1 = rG.aGradient.GetStartColor();
44 	if(100 != rG.aGradient.GetStartIntens())
45 	{
46 		const double fFact((double)rG.aGradient.GetStartIntens() / 100.0);
47         rV.aCol1 = Color(rV.aCol1.getBColor() * fFact);
48 	}
49 
50 	// handle end color
51 	rV.aCol2 = rG.aGradient.GetEndColor();
52 	if(100 != rG.aGradient.GetEndIntens())
53 	{
54 		const double fFact((double)rG.aGradient.GetEndIntens() / 100.0);
55         rV.aCol2 = Color(rV.aCol2.getBColor() * fFact);
56 	}
57 
58 	// calc the basic positions
59 	const Rectangle aObjectSnapRectangle(pObj->GetSnapRect());
60 	const basegfx::B2DRange aRange(aObjectSnapRectangle.Left(), aObjectSnapRectangle.Top(), aObjectSnapRectangle.Right(), aObjectSnapRectangle.Bottom());
61 	const basegfx::B2DPoint aCenter(aRange.getCenter());
62 	basegfx::B2DPoint aStartPos, aEndPos;
63 
64 	switch(rG.aGradient.GetGradientStyle())
65 	{
66 		case XGRAD_LINEAR :
67 		{
68 			aStartPos = basegfx::B2DPoint(aCenter.getX(), aRange.getMinY());
69 			aEndPos = basegfx::B2DPoint(aCenter.getX(), aRange.getMaximum().getY());
70 
71 			if(rG.aGradient.GetBorder())
72 			{
73 				basegfx::B2DVector aFullVec(aStartPos - aEndPos);
74 				const double fLen = (aFullVec.getLength() * (100.0 - (double)rG.aGradient.GetBorder())) / 100.0;
75 				aFullVec.normalize();
76 				aStartPos = aEndPos + (aFullVec * fLen);
77 			}
78 
79 			if(rG.aGradient.GetAngle())
80 			{
81 				const double fAngle = (double)rG.aGradient.GetAngle() * (F_PI180 / 10.0);
82                 const basegfx::B2DHomMatrix aTransformation(basegfx::tools::createRotateAroundPoint(aCenter, -fAngle));
83 
84 				aStartPos *= aTransformation;
85 				aEndPos *= aTransformation;
86 			}
87 			break;
88 		}
89 		case XGRAD_AXIAL :
90 		{
91 			aStartPos = aCenter;
92 			aEndPos = basegfx::B2DPoint(aCenter.getX(), aRange.getMaximum().getY());
93 
94 			if(rG.aGradient.GetBorder())
95 			{
96 				basegfx::B2DVector aFullVec(aEndPos - aStartPos);
97 				const double fLen = (aFullVec.getLength() * (100.0 - (double)rG.aGradient.GetBorder())) / 100.0;
98 				aFullVec.normalize();
99 				aEndPos = aStartPos + (aFullVec * fLen);
100 			}
101 
102 			if(rG.aGradient.GetAngle())
103 			{
104 				const double fAngle = (double)rG.aGradient.GetAngle() * (F_PI180 / 10.0);
105                 const basegfx::B2DHomMatrix aTransformation(basegfx::tools::createRotateAroundPoint(aCenter, -fAngle));
106 
107 				aStartPos *= aTransformation;
108 				aEndPos *= aTransformation;
109 			}
110 			break;
111 		}
112 		case XGRAD_RADIAL :
113 		case XGRAD_SQUARE :
114 		{
115 			aStartPos = basegfx::B2DPoint(aRange.getMinX(), aRange.getMaximum().getY());
116 			aEndPos = basegfx::B2DPoint(aRange.getMinX(), aRange.getMinY());
117 
118 			if(rG.aGradient.GetBorder())
119 			{
120 				basegfx::B2DVector aFullVec(aStartPos - aEndPos);
121 				const double fLen = (aFullVec.getLength() * (100.0 - (double)rG.aGradient.GetBorder())) / 100.0;
122 				aFullVec.normalize();
123 				aStartPos = aEndPos + (aFullVec * fLen);
124 			}
125 
126 			if(rG.aGradient.GetAngle())
127 			{
128 				const double fAngle = (double)rG.aGradient.GetAngle() * (F_PI180 / 10.0);
129                 const basegfx::B2DHomMatrix aTransformation(basegfx::tools::createRotateAroundPoint(aEndPos, -fAngle));
130 
131 				aStartPos *= aTransformation;
132 				aEndPos *= aTransformation;
133 			}
134 
135 			if(rG.aGradient.GetXOffset() || rG.aGradient.GetYOffset())
136 			{
137 				basegfx::B2DPoint aOffset(
138 					(aRange.getWidth() * rG.aGradient.GetXOffset()) / 100.0,
139 					(aRange.getHeight() * rG.aGradient.GetYOffset()) / 100.0);
140 
141 				aStartPos += aOffset;
142 				aEndPos += aOffset;
143 			}
144 
145 			break;
146 		}
147 		case XGRAD_ELLIPTICAL :
148 		case XGRAD_RECT :
149 		{
150 			aStartPos = basegfx::B2DPoint(aRange.getMinX(), aCenter.getY());
151 			aEndPos = basegfx::B2DPoint(aRange.getMinX(), aRange.getMinY());
152 
153 			if(rG.aGradient.GetBorder())
154 			{
155 				basegfx::B2DVector aFullVec(aStartPos - aEndPos);
156 				const double fLen = (aFullVec.getLength() * (100.0 - (double)rG.aGradient.GetBorder())) / 100.0;
157 				aFullVec.normalize();
158 				aStartPos = aEndPos + (aFullVec * fLen);
159 			}
160 
161 			if(rG.aGradient.GetAngle())
162 			{
163 				const double fAngle = (double)rG.aGradient.GetAngle() * (F_PI180 / 10.0);
164                 const basegfx::B2DHomMatrix aTransformation(basegfx::tools::createRotateAroundPoint(aEndPos, -fAngle));
165 
166 				aStartPos *= aTransformation;
167 				aEndPos *= aTransformation;
168 			}
169 
170 			if(rG.aGradient.GetXOffset() || rG.aGradient.GetYOffset())
171 			{
172 				basegfx::B2DPoint aOffset(
173 					(aRange.getWidth() * rG.aGradient.GetXOffset()) / 100.0,
174 					(aRange.getHeight() * rG.aGradient.GetYOffset()) / 100.0);
175 
176 				aStartPos += aOffset;
177 				aEndPos += aOffset;
178 			}
179 
180 			break;
181 		}
182 	}
183 
184 	// set values for vector positions now
185 	rV.maPositionA = aStartPos;
186 	rV.maPositionB = aEndPos;
187 }
188 
189 //////////////////////////////////////////////////////////////////////////////
190 
191 void GradTransformer::VecToGrad(GradTransVector& rV, GradTransGradient& rG, GradTransGradient& rGOld, const SdrObject* pObj,
192 	sal_Bool bMoveSingle, sal_Bool bMoveFirst)
193 {
194 	// fill old gradient to new gradient to have a base
195 	rG = rGOld;
196 
197 	// handle color changes
198 	if(rV.aCol1 != rGOld.aGradient.GetStartColor())
199 	{
200 		rG.aGradient.SetStartColor(rV.aCol1);
201 		rG.aGradient.SetStartIntens(100);
202 	}
203 	if(rV.aCol2 != rGOld.aGradient.GetEndColor())
204 	{
205 		rG.aGradient.SetEndColor(rV.aCol2);
206 		rG.aGradient.SetEndIntens(100);
207 	}
208 
209 	// calc the basic positions
210 	const Rectangle aObjectSnapRectangle(pObj->GetSnapRect());
211 	const basegfx::B2DRange aRange(aObjectSnapRectangle.Left(), aObjectSnapRectangle.Top(), aObjectSnapRectangle.Right(), aObjectSnapRectangle.Bottom());
212 	const basegfx::B2DPoint aCenter(aRange.getCenter());
213 	basegfx::B2DPoint aStartPos(rV.maPositionA);
214 	basegfx::B2DPoint aEndPos(rV.maPositionB);
215 
216 	switch(rG.aGradient.GetGradientStyle())
217 	{
218 		case XGRAD_LINEAR :
219 		{
220 			if(!bMoveSingle || (bMoveSingle && !bMoveFirst))
221 			{
222 				basegfx::B2DVector aFullVec(aEndPos - aStartPos);
223 
224 				if(bMoveSingle)
225 				{
226 					aFullVec = aEndPos - aCenter;
227 				}
228 
229 				aFullVec.normalize();
230 
231 				double fNewFullAngle(atan2(aFullVec.getY(), aFullVec.getX()));
232 				fNewFullAngle /= F_PI180;
233 				fNewFullAngle *= -10.0;
234 				fNewFullAngle += 900.0;
235 
236 				// clip
237 				while(fNewFullAngle < 0.0)
238 				{
239 					fNewFullAngle += 3600.0;
240 				}
241 
242 				while(fNewFullAngle >= 3600.0)
243 				{
244 					fNewFullAngle -= 3600.0;
245 				}
246 
247 				// to int and set
248 				sal_Int32 nNewAngle = FRound(fNewFullAngle);
249 
250 				if(nNewAngle != rGOld.aGradient.GetAngle())
251 				{
252 					rG.aGradient.SetAngle(nNewAngle);
253 				}
254 			}
255 
256 			if(!bMoveSingle || (bMoveSingle && bMoveFirst))
257 			{
258 				const basegfx::B2DVector aFullVec(aEndPos - aStartPos);
259 				const basegfx::B2DPoint aBottomLeft(aRange.getMinX(), aRange.getMaximum().getY());
260 				const basegfx::B2DPoint aTopLeft(aRange.getMinX(), aRange.getMinY());
261 				const basegfx::B2DVector aOldVec(aBottomLeft - aTopLeft);
262 				const double fFullLen(aFullVec.getLength());
263 				const double fOldLen(aOldVec.getLength());
264 				const double fNewBorder((fFullLen * 100.0) / fOldLen);
265 				sal_Int32 nNewBorder(100L - FRound(fNewBorder));
266 
267 				// clip
268 				if(nNewBorder < 0L)
269 				{
270 					nNewBorder = 0L;
271 				}
272 
273 				if(nNewBorder > 100L)
274 				{
275 					nNewBorder = 100L;
276 				}
277 
278 				// set
279 				if(nNewBorder != rG.aGradient.GetBorder())
280 				{
281 					rG.aGradient.SetBorder((sal_uInt16)nNewBorder);
282 				}
283 			}
284 
285 			break;
286 		}
287 		case XGRAD_AXIAL :
288 		{
289 			if(!bMoveSingle || (bMoveSingle && !bMoveFirst))
290 			{
291 				basegfx::B2DVector aFullVec(aEndPos - aCenter);
292 				const basegfx::B2DVector aOldVec(basegfx::B2DPoint(aCenter.getX(), aRange.getMaximum().getY()) - aCenter);
293 				const double fFullLen(aFullVec.getLength());
294 				const double fOldLen(aOldVec.getLength());
295 				const double fNewBorder((fFullLen * 100.0) / fOldLen);
296 				sal_Int32 nNewBorder = 100 - FRound(fNewBorder);
297 
298 				// clip
299 				if(nNewBorder < 0L)
300 				{
301 					nNewBorder = 0L;
302 				}
303 
304 				if(nNewBorder > 100L)
305 				{
306 					nNewBorder = 100L;
307 				}
308 
309 				// set
310 				if(nNewBorder != rG.aGradient.GetBorder())
311 				{
312 					rG.aGradient.SetBorder((sal_uInt16)nNewBorder);
313 				}
314 
315 				aFullVec.normalize();
316 				double fNewFullAngle(atan2(aFullVec.getY(), aFullVec.getX()));
317 				fNewFullAngle /= F_PI180;
318 				fNewFullAngle *= -10.0;
319 				fNewFullAngle += 900.0;
320 
321 				// clip
322 				while(fNewFullAngle < 0.0)
323 				{
324 					fNewFullAngle += 3600.0;
325 				}
326 
327 				while(fNewFullAngle >= 3600.0)
328 				{
329 					fNewFullAngle -= 3600.0;
330 				}
331 
332 				// to int and set
333 				const sal_Int32 nNewAngle(FRound(fNewFullAngle));
334 
335 				if(nNewAngle != rGOld.aGradient.GetAngle())
336 				{
337 					rG.aGradient.SetAngle(nNewAngle);
338 				}
339 			}
340 
341 			break;
342 		}
343 		case XGRAD_RADIAL :
344 		case XGRAD_SQUARE :
345 		{
346 			if(!bMoveSingle || (bMoveSingle && !bMoveFirst))
347 			{
348 				const basegfx::B2DPoint aTopLeft(aRange.getMinX(), aRange.getMinY());
349 				const basegfx::B2DPoint aOffset(aEndPos - aTopLeft);
350 				sal_Int32 nNewXOffset(FRound((aOffset.getX() * 100.0) / aRange.getWidth()));
351 				sal_Int32 nNewYOffset(FRound((aOffset.getY() * 100.0) / aRange.getHeight()));
352 
353 				// clip
354 				if(nNewXOffset < 0L)
355 				{
356 					nNewXOffset = 0L;
357 				}
358 
359 				if(nNewXOffset > 100L)
360 				{
361 					nNewXOffset = 100L;
362 				}
363 
364 				if(nNewYOffset < 0L)
365 				{
366 					nNewYOffset = 0L;
367 				}
368 
369 				if(nNewYOffset > 100L)
370 				{
371 					nNewYOffset = 100L;
372 				}
373 
374 				rG.aGradient.SetXOffset((sal_uInt16)nNewXOffset);
375 				rG.aGradient.SetYOffset((sal_uInt16)nNewYOffset);
376 
377 				aStartPos -= aOffset;
378 				aEndPos -= aOffset;
379 			}
380 
381 			if(!bMoveSingle || (bMoveSingle && bMoveFirst))
382 			{
383 				basegfx::B2DVector aFullVec(aStartPos - aEndPos);
384 				const basegfx::B2DPoint aBottomLeft(aRange.getMinX(), aRange.getMaximum().getY());
385 				const basegfx::B2DPoint aTopLeft(aRange.getMinX(), aRange.getMinY());
386 				const basegfx::B2DVector aOldVec(aBottomLeft - aTopLeft);
387 				const double fFullLen(aFullVec.getLength());
388 				const double fOldLen(aOldVec.getLength());
389 				const double fNewBorder((fFullLen * 100.0) / fOldLen);
390 				sal_Int32 nNewBorder(100L - FRound(fNewBorder));
391 
392 				// clip
393 				if(nNewBorder < 0L)
394 				{
395 					nNewBorder = 0L;
396 				}
397 
398 				if(nNewBorder > 100L)
399 				{
400 					nNewBorder = 100L;
401 				}
402 
403 				// set
404 				if(nNewBorder != rG.aGradient.GetBorder())
405 				{
406 					rG.aGradient.SetBorder((sal_uInt16)nNewBorder);
407 				}
408 
409 				// angle is not definitely necessary for these modes, but it makes
410 				// controlling more fun for the user
411 				aFullVec.normalize();
412 				double fNewFullAngle(atan2(aFullVec.getY(), aFullVec.getX()));
413 				fNewFullAngle /= F_PI180;
414 				fNewFullAngle *= -10.0;
415 				fNewFullAngle += 900.0;
416 
417 				// clip
418 				while(fNewFullAngle < 0.0)
419 				{
420 					fNewFullAngle += 3600.0;
421 				}
422 
423 				while(fNewFullAngle >= 3600.0)
424 				{
425 					fNewFullAngle -= 3600.0;
426 				}
427 
428 				// to int and set
429 				const sal_Int32 nNewAngle(FRound(fNewFullAngle));
430 
431 				if(nNewAngle != rGOld.aGradient.GetAngle())
432 				{
433 					rG.aGradient.SetAngle(nNewAngle);
434 				}
435 			}
436 
437 			break;
438 		}
439 		case XGRAD_ELLIPTICAL :
440 		case XGRAD_RECT :
441 		{
442 			if(!bMoveSingle || (bMoveSingle && !bMoveFirst))
443 			{
444 				const basegfx::B2DPoint aTopLeft(aRange.getMinX(), aRange.getMinY());
445 				const basegfx::B2DPoint aOffset(aEndPos - aTopLeft);
446 				sal_Int32 nNewXOffset(FRound((aOffset.getX() * 100.0) / aRange.getWidth()));
447 				sal_Int32 nNewYOffset(FRound((aOffset.getY() * 100.0) / aRange.getHeight()));
448 
449 				// clip
450 				if(nNewXOffset < 0L)
451 				{
452 					nNewXOffset = 0L;
453 				}
454 
455 				if(nNewXOffset > 100L)
456 				{
457 					nNewXOffset = 100L;
458 				}
459 
460 				if(nNewYOffset < 0L)
461 				{
462 					nNewYOffset = 0L;
463 				}
464 
465 				if(nNewYOffset > 100L)
466 				{
467 					nNewYOffset = 100L;
468 				}
469 
470 				rG.aGradient.SetXOffset((sal_uInt16)nNewXOffset);
471 				rG.aGradient.SetYOffset((sal_uInt16)nNewYOffset);
472 
473 				aStartPos -= aOffset;
474 				aEndPos -= aOffset;
475 			}
476 
477 			if(!bMoveSingle || (bMoveSingle && bMoveFirst))
478 			{
479 				basegfx::B2DVector aFullVec(aStartPos - aEndPos);
480 				const basegfx::B2DPoint aTopLeft(aRange.getMinX(), aRange.getMinY());
481 				const basegfx::B2DPoint aCenterLeft(aRange.getMinX(), aRange.getHeight());
482 				const basegfx::B2DVector aOldVec(aCenterLeft - aTopLeft);
483 				const double fFullLen(aFullVec.getLength());
484 				const double fOldLen(aOldVec.getLength());
485 				const double fNewBorder((fFullLen * 100.0) / fOldLen);
486 				sal_Int32 nNewBorder(100L - FRound(fNewBorder));
487 
488 				// clip
489 				if(nNewBorder < 0L)
490 				{
491 					nNewBorder = 0L;
492 				}
493 
494 				if(nNewBorder > 100L)
495 				{
496 					nNewBorder = 100L;
497 				}
498 
499 				// set
500 				if(nNewBorder != rG.aGradient.GetBorder())
501 				{
502 					rG.aGradient.SetBorder((sal_uInt16)nNewBorder);
503 				}
504 
505 				// angle is not definitely necessary for these modes, but it makes
506 				// controlling more fun for the user
507 				aFullVec.normalize();
508 				double fNewFullAngle(atan2(aFullVec.getY(), aFullVec.getX()));
509 				fNewFullAngle /= F_PI180;
510 				fNewFullAngle *= -10.0;
511 				fNewFullAngle += 900.0;
512 
513 				// clip
514 				while(fNewFullAngle < 0.0)
515 				{
516 					fNewFullAngle += 3600.0;
517 				}
518 
519 				while(fNewFullAngle >= 3600.0)
520 				{
521 					fNewFullAngle -= 3600.0;
522 				}
523 
524 				// to int and set
525 				const sal_Int32 nNewAngle(FRound(fNewFullAngle));
526 
527 				if(nNewAngle != rGOld.aGradient.GetAngle())
528 				{
529 					rG.aGradient.SetAngle(nNewAngle);
530 				}
531 			}
532 
533 			break;
534 		}
535 	}
536 }
537 
538 // eof
539