xref: /aoo41x/main/sd/source/ui/func/fumorph.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_sd.hxx"
30 
31 //#define _FUMORPH_PRIVATE
32 #include "fumorph.hxx"
33 #include <svx/xfillit.hxx>
34 #include <svx/xlineit.hxx>
35 #include <vcl/msgbox.hxx>
36 #include <svx/svdpool.hxx>
37 #include <tools/poly.hxx>
38 #include <svx/svdopath.hxx>
39 #include <svx/svdogrp.hxx>
40 #include <editeng/eeitem.hxx>
41 
42 #include "View.hxx"
43 #include "ViewShell.hxx"
44 #include "Window.hxx"
45 #include <basegfx/polygon/b2dpolygontools.hxx>
46 #include <basegfx/polygon/b2dpolypolygontools.hxx>
47 #include <basegfx/matrix/b2dhommatrix.hxx>
48 #include <basegfx/matrix/b2dhommatrixtools.hxx>
49 
50 #include "strings.hrc"
51 #include "sdresid.hxx"
52 
53 #include "sdabstdlg.hxx"
54 
55 // #i48168#
56 #include <svx/svditer.hxx>
57 
58 #include <basegfx/color/bcolor.hxx>
59 
60 namespace sd {
61 
62 #define  ITEMVALUE( ItemSet, Id, Cast ) ( ( (const Cast&) (ItemSet).Get( (Id) ) ).GetValue() )
63 TYPEINIT1( FuMorph, FuPoor );
64 
65 //////////////////////////////////////////////////////////////////////////////
66 // constructor
67 //
68 FuMorph::FuMorph (
69     ViewShell* pViewSh,
70     ::sd::Window* pWin,
71     ::sd::View* pView,
72 	SdDrawDocument* pDoc,
73     SfxRequest& rReq )
74     :	FuPoor(pViewSh, pWin, pView, pDoc, rReq)
75 {
76 }
77 
78 FunctionReference FuMorph::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq )
79 {
80 	FunctionReference xFunc( new FuMorph( pViewSh, pWin, pView, pDoc, rReq ) );
81 	xFunc->DoExecute(rReq);
82 	return xFunc;
83 }
84 
85 void FuMorph::DoExecute( SfxRequest& )
86 {
87 	const SdrMarkList&	rMarkList = mpView->GetMarkedObjectList();
88 
89 	if(rMarkList.GetMarkCount() == 2)
90 	{
91 		// Clones erzeugen
92 		SdrObject*	pObj1 = rMarkList.GetMark(0)->GetMarkedSdrObj();
93 		SdrObject*	pObj2 = rMarkList.GetMark(1)->GetMarkedSdrObj();
94 		SdrObject*	pCloneObj1 = pObj1->Clone();
95 		SdrObject*	pCloneObj2 = pObj2->Clone();
96 
97 		// Text am Clone loeschen, da wir sonst kein richtiges PathObj bekommen
98 		pCloneObj1->SetOutlinerParaObject(NULL);
99 		pCloneObj2->SetOutlinerParaObject(NULL);
100 
101 		// Path-Objekte erzeugen
102 		SdrObject*	pPolyObj1 = pCloneObj1->ConvertToPolyObj(sal_False, sal_False);
103 		SdrObject*	pPolyObj2 = pCloneObj2->ConvertToPolyObj(sal_False, sal_False);
104 		SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create();
105 		AbstractMorphDlg* pDlg = pFact ? pFact->CreateMorphDlg( static_cast< ::Window*>(mpWindow), pObj1, pObj2 ) : 0;
106 		if(pPolyObj1 && pPolyObj2 && pDlg && (pDlg->Execute() == RET_OK))
107 		{
108 			List aPolyPolyList;
109 			::basegfx::B2DPolyPolygon aPolyPoly1;
110 			::basegfx::B2DPolyPolygon aPolyPoly2;
111 			::basegfx::B2DPolyPolygon* pPolyPoly;
112 
113 			pDlg->SaveSettings();
114 
115 			// #i48168# Not always is the pPolyObj1/pPolyObj2 a SdrPathObj, it may also be a group object
116 			// containing SdrPathObjs. To get the polygons, i add two iters here
117 			SdrObjListIter aIter1(*pPolyObj1);
118 			SdrObjListIter aIter2(*pPolyObj2);
119 
120 			while(aIter1.IsMore())
121 			{
122 				SdrObject* pObj = aIter1.Next();
123 				if(pObj && pObj->ISA(SdrPathObj))
124 					aPolyPoly1.append(((SdrPathObj*)pObj)->GetPathPoly());
125 			}
126 
127 			while(aIter2.IsMore())
128 			{
129 				SdrObject* pObj = aIter2.Next();
130 				if(pObj && pObj->ISA(SdrPathObj))
131 					aPolyPoly2.append(((SdrPathObj*)pObj)->GetPathPoly());
132 			}
133 
134 			// Morphing durchfuehren
135 			if(aPolyPoly1.count() && aPolyPoly2.count())
136 			{
137 				aPolyPoly1 = ::basegfx::tools::correctOrientations(aPolyPoly1);
138 				aPolyPoly1.removeDoublePoints();
139 				::basegfx::B2VectorOrientation eIsClockwise1(::basegfx::tools::getOrientation(aPolyPoly1.getB2DPolygon(0L)));
140 
141 				aPolyPoly2 = ::basegfx::tools::correctOrientations(aPolyPoly2);
142 				aPolyPoly2.removeDoublePoints();
143 				::basegfx::B2VectorOrientation eIsClockwise2(::basegfx::tools::getOrientation(aPolyPoly2.getB2DPolygon(0L)));
144 
145 				// set same orientation
146 				if(eIsClockwise1 != eIsClockwise2)
147 					aPolyPoly2.flip();
148 
149 				// force same poly count
150 				if(aPolyPoly1.count() < aPolyPoly2.count())
151 					ImpAddPolys(aPolyPoly1, aPolyPoly2);
152 				else if(aPolyPoly2.count() < aPolyPoly1.count())
153 					ImpAddPolys(aPolyPoly2, aPolyPoly1);
154 
155 				// use orientation flag from dialog
156 				if(!pDlg->IsOrientationFade())
157 					aPolyPoly2.flip();
158 
159 				// force same point counts
160 				for( sal_uInt32 a(0L); a < aPolyPoly1.count(); a++ )
161 				{
162 					::basegfx::B2DPolygon aSub1(aPolyPoly1.getB2DPolygon(a));
163 					::basegfx::B2DPolygon aSub2(aPolyPoly2.getB2DPolygon(a));
164 
165 					if(aSub1.count() < aSub2.count())
166 						ImpEqualizePolyPointCount(aSub1, aSub2);
167 					else if(aSub2.count() < aSub1.count())
168 						ImpEqualizePolyPointCount(aSub2, aSub1);
169 
170 					aPolyPoly1.setB2DPolygon(a, aSub1);
171 					aPolyPoly2.setB2DPolygon(a, aSub2);
172 				}
173 
174 				if(ImpMorphPolygons(aPolyPoly1, aPolyPoly2, pDlg->GetFadeSteps(), aPolyPolyList))
175 				{
176 					String aString(mpView->GetDescriptionOfMarkedObjects());
177 
178 					aString.Append(sal_Unicode(' '));
179 					aString.Append(String(SdResId(STR_UNDO_MORPHING)));
180 
181 					mpView->BegUndo(aString);
182 					ImpInsertPolygons(aPolyPolyList, pDlg->IsAttributeFade(), pObj1, pObj2);
183 					mpView->EndUndo();
184 				}
185 
186 				// erzeugte Polygone wieder loeschen
187 				for(pPolyPoly = (::basegfx::B2DPolyPolygon*)aPolyPolyList.First(); pPolyPoly; pPolyPoly = (::basegfx::B2DPolyPolygon *)aPolyPolyList.Next())
188 				{
189 					delete pPolyPoly;
190 				}
191 			}
192 		}
193 		delete pDlg;
194 		SdrObject::Free( pCloneObj1 );
195 		SdrObject::Free( pCloneObj2 );
196 
197 		SdrObject::Free( pPolyObj1 );
198 		SdrObject::Free( pPolyObj2 );
199 	}
200 }
201 
202 ::basegfx::B2DPolygon ImpGetExpandedPolygon(const ::basegfx::B2DPolygon& rCandidate, sal_uInt32 nNum)
203 {
204 	if(rCandidate.count() && nNum && rCandidate.count() != nNum)
205 	{
206 		// length of step in dest poly
207 		::basegfx::B2DPolygon aRetval;
208 		const double fStep(::basegfx::tools::getLength(rCandidate) / (double)(rCandidate.isClosed() ? nNum : nNum - 1L));
209 		double fDestPos(0.0);
210 		double fSrcPos(0.0);
211 		sal_uInt32 nSrcPos(0L);
212 		sal_uInt32 nSrcPosNext((nSrcPos + 1L == rCandidate.count()) ? 0L : nSrcPos + 1L);
213 		double fNextSrcLen(::basegfx::B2DVector(rCandidate.getB2DPoint(nSrcPos) - rCandidate.getB2DPoint(nSrcPosNext)).getLength());
214 
215 		for(sal_uInt32 b(0L); b < nNum; b++)
216 		{
217 			// calc fDestPos in source
218 			while(fSrcPos + fNextSrcLen < fDestPos)
219 			{
220 				fSrcPos += fNextSrcLen;
221 				nSrcPos++;
222 				nSrcPosNext = (nSrcPos + 1L == rCandidate.count()) ? 0L : nSrcPos + 1L;
223 				fNextSrcLen = ::basegfx::B2DVector(rCandidate.getB2DPoint(nSrcPos) - rCandidate.getB2DPoint(nSrcPosNext)).getLength();
224 			}
225 
226 			// fDestPos is between fSrcPos and (fSrcPos + fNextSrcLen)
227 			const double fLenA((fDestPos - fSrcPos) / fNextSrcLen);
228 			const ::basegfx::B2DPoint aOld1(rCandidate.getB2DPoint(nSrcPos));
229 			const ::basegfx::B2DPoint aOld2(rCandidate.getB2DPoint(nSrcPosNext));
230 			::basegfx::B2DPoint aNewPoint(basegfx::interpolate(aOld1, aOld2, fLenA));
231 			aRetval.append(aNewPoint);
232 
233 			// next step
234 			fDestPos += fStep;
235 		}
236 
237 		if(aRetval.count() >= 3L)
238 		{
239 			aRetval.setClosed(rCandidate.isClosed());
240 		}
241 
242 		return aRetval;
243 	}
244 	else
245 	{
246 		return rCandidate;
247 	}
248 }
249 
250 //////////////////////////////////////////////////////////////////////////////
251 // make the point count of the polygons equal in adding points
252 //
253 void FuMorph::ImpEqualizePolyPointCount(::basegfx::B2DPolygon& rSmall, const ::basegfx::B2DPolygon& rBig)
254 {
255 	// create poly with equal point count
256 	const sal_uInt32 nCnt(rBig.count());
257 	::basegfx::B2DPolygon aPoly1(ImpGetExpandedPolygon(rSmall, nCnt));
258 
259 	// create transformation for rBig to do the compare
260 	const ::basegfx::B2DRange aSrcSize(::basegfx::tools::getRange(rBig));
261 	const ::basegfx::B2DPoint aSrcPos(aSrcSize.getCenter());
262 	const ::basegfx::B2DRange aDstSize(::basegfx::tools::getRange(rSmall));
263 	const ::basegfx::B2DPoint aDstPos(aDstSize.getCenter());
264 
265     basegfx::B2DHomMatrix aTrans(basegfx::tools::createTranslateB2DHomMatrix(-aSrcPos.getX(), -aSrcPos.getY()));
266 	aTrans.scale(aDstSize.getWidth() / aSrcSize.getWidth(), aDstSize.getHeight() / aSrcSize.getHeight());
267 	aTrans.translate(aDstPos.getX(), aDstPos.getY());
268 
269 	// transpose points to have smooth linear blending
270 	::basegfx::B2DPolygon aPoly2;
271 	aPoly2.append(::basegfx::B2DPoint(), nCnt);
272 	sal_uInt32 nInd(ImpGetNearestIndex(aPoly1, aTrans * rBig.getB2DPoint(0L)));
273 
274 	for(sal_uInt32 a(0L); a < nCnt; a++)
275 	{
276 		aPoly2.setB2DPoint((a + nCnt - nInd) % nCnt, aPoly1.getB2DPoint(a));
277 	}
278 
279 	aPoly2.setClosed(rBig.isClosed());
280 	rSmall = aPoly2;
281 }
282 
283 //////////////////////////////////////////////////////////////////////////////
284 //
285 sal_uInt32 FuMorph::ImpGetNearestIndex(const ::basegfx::B2DPolygon& rPoly, const ::basegfx::B2DPoint& rPos)
286 {
287 	double fMinDist = 0.0;
288 	sal_uInt32 nActInd = 0;
289 
290 	for(sal_uInt32 a(0L); a < rPoly.count(); a++)
291 	{
292 		double fNewDist(::basegfx::B2DVector(rPoly.getB2DPoint(a) - rPos).getLength());
293 
294 		if(!a || fNewDist < fMinDist)
295 		{
296 			fMinDist = fNewDist;
297 			nActInd = a;
298 		}
299 	}
300 
301 	return nActInd;
302 }
303 
304 //////////////////////////////////////////////////////////////////////////////
305 // add to a point reduced polys until count is same
306 //
307 void FuMorph::ImpAddPolys(::basegfx::B2DPolyPolygon& rSmaller, const ::basegfx::B2DPolyPolygon& rBigger)
308 {
309 	while(rSmaller.count() < rBigger.count())
310 	{
311 		const ::basegfx::B2DPolygon aToBeCopied(rBigger.getB2DPolygon(rSmaller.count()));
312 		const ::basegfx::B2DRange aToBeCopiedPolySize(::basegfx::tools::getRange(aToBeCopied));
313 		::basegfx::B2DPoint aNewPoint(aToBeCopiedPolySize.getCenter());
314 		::basegfx::B2DPolygon aNewPoly;
315 
316 		const ::basegfx::B2DRange aSrcSize(::basegfx::tools::getRange(rBigger.getB2DPolygon(0L)));
317 		const ::basegfx::B2DPoint aSrcPos(aSrcSize.getCenter());
318 		const ::basegfx::B2DRange aDstSize(::basegfx::tools::getRange(rSmaller.getB2DPolygon(0L)));
319 		const ::basegfx::B2DPoint aDstPos(aDstSize.getCenter());
320 		aNewPoint = aNewPoint - aSrcPos + aDstPos;
321 
322 		for(sal_uInt32 a(0L); a < aToBeCopied.count(); a++)
323 		{
324 			aNewPoly.append(aNewPoint);
325 		}
326 
327 		rSmaller.append(aNewPoly);
328 	}
329 }
330 
331 //////////////////////////////////////////////////////////////////////////////
332 // create group object with morphed polygons
333 //
334 void FuMorph::ImpInsertPolygons(List& rPolyPolyList3D, sal_Bool bAttributeFade,
335 	const SdrObject* pObj1, const SdrObject* pObj2)
336 {
337 	Color				aStartFillCol;
338 	Color				aEndFillCol;
339 	Color				aStartLineCol;
340 	Color				aEndLineCol;
341 	long				nStartLineWidth = 0;
342 	long				nEndLineWidth = 0;
343 	SdrPageView*		pPageView = mpView->GetSdrPageView();
344 	SfxItemPool*		pPool = pObj1->GetObjectItemPool();
345 	SfxItemSet			aSet1( *pPool,SDRATTR_START,SDRATTR_NOTPERSIST_FIRST-1,EE_ITEMS_START,EE_ITEMS_END,0 );
346 	SfxItemSet			aSet2( aSet1 );
347 	sal_Bool				bLineColor = sal_False;
348 	sal_Bool				bFillColor = sal_False;
349 	sal_Bool				bLineWidth = sal_False;
350 	sal_Bool				bIgnoreLine = sal_False;
351 	sal_Bool				bIgnoreFill = sal_False;
352 
353 	aSet1.Put(pObj1->GetMergedItemSet());
354 	aSet2.Put(pObj2->GetMergedItemSet());
355 
356 	const XLineStyle eLineStyle1 = ITEMVALUE( aSet1, XATTR_LINESTYLE, XLineStyleItem );
357 	const XLineStyle eLineStyle2 = ITEMVALUE( aSet2, XATTR_LINESTYLE, XLineStyleItem );
358 	const XFillStyle eFillStyle1 = ITEMVALUE( aSet1, XATTR_FILLSTYLE, XFillStyleItem );
359 	const XFillStyle eFillStyle2 = ITEMVALUE( aSet2, XATTR_FILLSTYLE, XFillStyleItem );
360 
361 	if ( bAttributeFade )
362 	{
363 		if ( ( eLineStyle1 != XLINE_NONE ) && ( eLineStyle2 != XLINE_NONE ) )
364 		{
365 			bLineWidth = bLineColor = sal_True;
366 
367 			aStartLineCol = static_cast< XLineColorItem const & >(
368                 aSet1.Get(XATTR_LINECOLOR)).GetColorValue();
369 			aEndLineCol = static_cast< XLineColorItem const & >(
370                 aSet2.Get(XATTR_LINECOLOR)).GetColorValue();
371 
372 			nStartLineWidth = ITEMVALUE( aSet1, XATTR_LINEWIDTH, XLineWidthItem );
373 			nEndLineWidth = ITEMVALUE( aSet2, XATTR_LINEWIDTH, XLineWidthItem );
374 		}
375 		else if ( ( eLineStyle1 == XLINE_NONE ) && ( eLineStyle2 == XLINE_NONE ) )
376 			bIgnoreLine = sal_True;
377 
378 		if ( ( eFillStyle1 == XFILL_SOLID ) && ( eFillStyle2 == XFILL_SOLID ) )
379 		{
380 			bFillColor = sal_True;
381 			aStartFillCol = static_cast< XFillColorItem const & >(
382                 aSet1.Get(XATTR_FILLCOLOR)).GetColorValue();
383 			aEndFillCol = static_cast< XFillColorItem const & >(
384                 aSet2.Get(XATTR_FILLCOLOR)).GetColorValue();
385 		}
386 		else if ( ( eFillStyle1 == XFILL_NONE ) && ( eFillStyle2 == XFILL_NONE ) )
387 			bIgnoreFill = sal_True;
388 	}
389 
390 	if ( pPageView )
391 	{
392 		SfxItemSet		aSet( aSet1 );
393 		SdrObjGroup*	pObjGroup = new SdrObjGroup;
394 		SdrObjList*		pObjList = pObjGroup->GetSubList();
395 		const sal_uLong		nCount = rPolyPolyList3D.Count();
396 		const double	fStep = 1. / ( nCount + 1 );
397 		const double	fDelta = nEndLineWidth - nStartLineWidth;
398 		double			fFactor = fStep;
399 
400 		aSet.Put( XLineStyleItem( XLINE_SOLID ) );
401 		aSet.Put( XFillStyleItem( XFILL_SOLID ) );
402 
403 		for ( sal_uLong i = 0; i < nCount; i++, fFactor += fStep )
404 		{
405 			const ::basegfx::B2DPolyPolygon& rPolyPoly3D = *(::basegfx::B2DPolyPolygon*)rPolyPolyList3D.GetObject(i);
406 			SdrPathObj* pNewObj = new SdrPathObj(OBJ_POLY, rPolyPoly3D);
407 
408 			// Linienfarbe
409 			if ( bLineColor )
410             {
411                 const basegfx::BColor aLineColor(basegfx::interpolate(aStartLineCol.getBColor(), aEndLineCol.getBColor(), fFactor));
412 				aSet.Put( XLineColorItem( aEmptyStr, Color(aLineColor)));
413             }
414 			else if ( bIgnoreLine )
415 				aSet.Put( XLineStyleItem( XLINE_NONE ) );
416 
417 			// Fuellfarbe
418 			if ( bFillColor )
419             {
420                 const basegfx::BColor aFillColor(basegfx::interpolate(aStartFillCol.getBColor(), aEndFillCol.getBColor(), fFactor));
421 				aSet.Put( XFillColorItem( aEmptyStr, Color(aFillColor)));
422             }
423 			else if ( bIgnoreFill )
424 				aSet.Put( XFillStyleItem( XFILL_NONE ) );
425 
426 			// Linienstaerke
427 			if ( bLineWidth )
428 				aSet.Put( XLineWidthItem( nStartLineWidth + (long) ( fFactor * fDelta + 0.5 ) ) );
429 
430 			pNewObj->SetMergedItemSetAndBroadcast(aSet);
431 
432 			pObjList->InsertObject( pNewObj, LIST_APPEND );
433 		}
434 
435 		if ( nCount )
436 		{
437 			pObjList->InsertObject( pObj1->Clone(), 0 );
438 			pObjList->InsertObject( pObj2->Clone(), LIST_APPEND );
439 			mpView->DeleteMarked();
440 			mpView->InsertObjectAtView( pObjGroup, *pPageView, SDRINSERT_SETDEFLAYER );
441 		}
442 	}
443 }
444 
445 //////////////////////////////////////////////////////////////////////////////
446 // create single morphed PolyPolygon
447 //
448 ::basegfx::B2DPolyPolygon* FuMorph::ImpCreateMorphedPolygon(
449 	const ::basegfx::B2DPolyPolygon& rPolyPolyStart,
450 	const ::basegfx::B2DPolyPolygon& rPolyPolyEnd,
451 	double fMorphingFactor)
452 {
453 	::basegfx::B2DPolyPolygon* pNewPolyPolygon = new ::basegfx::B2DPolyPolygon();
454 	const double fFactor = 1.0 - fMorphingFactor;
455 
456 	for(sal_uInt32 a(0L); a < rPolyPolyStart.count(); a++)
457 	{
458 		const ::basegfx::B2DPolygon aPolyStart(rPolyPolyStart.getB2DPolygon(a));
459 		const ::basegfx::B2DPolygon aPolyEnd(rPolyPolyEnd.getB2DPolygon(a));
460 		const sal_uInt32 nCount(aPolyStart.count());
461 		::basegfx::B2DPolygon aNewPolygon;
462 
463 		for(sal_uInt32 b(0L); b < nCount; b++)
464 		{
465 			const ::basegfx::B2DPoint& aPtStart(aPolyStart.getB2DPoint(b));
466 			const ::basegfx::B2DPoint& aPtEnd(aPolyEnd.getB2DPoint(b));
467 			aNewPolygon.append(aPtEnd + ((aPtStart - aPtEnd) * fFactor));
468 		}
469 
470 		aNewPolygon.setClosed(aPolyStart.isClosed() && aPolyEnd.isClosed());
471 		pNewPolyPolygon->append(aNewPolygon);
472 	}
473 
474 	return pNewPolyPolygon;
475 }
476 
477 //////////////////////////////////////////////////////////////////////////////
478 // create morphed PolyPolygons
479 //
480 sal_Bool FuMorph::ImpMorphPolygons(
481 	const ::basegfx::B2DPolyPolygon& rPolyPoly1,
482 	const ::basegfx::B2DPolyPolygon& rPolyPoly2,
483 	const sal_uInt16 nSteps, List& rPolyPolyList3D)
484 {
485 	if(nSteps)
486 	{
487 		const ::basegfx::B2DRange aStartPolySize(::basegfx::tools::getRange(rPolyPoly1));
488 		const ::basegfx::B2DPoint aStartCenter(aStartPolySize.getCenter());
489 		const ::basegfx::B2DRange aEndPolySize(::basegfx::tools::getRange(rPolyPoly2));
490 		const ::basegfx::B2DPoint aEndCenter(aEndPolySize.getCenter());
491 		const ::basegfx::B2DPoint aDelta(aEndCenter - aStartCenter);
492 		const double fFactor(1.0 / (nSteps + 1));
493 		double fValue(0.0);
494 
495 		for(sal_uInt16 i(0); i < nSteps; i++)
496 		{
497 			fValue += fFactor;
498 			::basegfx::B2DPolyPolygon* pNewPolyPoly2D = ImpCreateMorphedPolygon(rPolyPoly1, rPolyPoly2, fValue);
499 
500 			const ::basegfx::B2DRange aNewPolySize(::basegfx::tools::getRange(*pNewPolyPoly2D));
501 			const ::basegfx::B2DPoint aNewS(aNewPolySize.getCenter());
502 			const ::basegfx::B2DPoint aRealS(aStartCenter + (aDelta * fValue));
503 			const ::basegfx::B2DPoint aDiff(aRealS - aNewS);
504 
505             pNewPolyPoly2D->transform(basegfx::tools::createTranslateB2DHomMatrix(aDiff));
506 			rPolyPolyList3D.Insert(pNewPolyPoly2D, LIST_APPEND);
507 		}
508 	}
509 	return sal_True;
510 }
511 
512 
513 } // end of namespace sd
514