xref: /trunk/main/sd/source/ui/func/fumorph.cxx (revision cdf0e10c4e3984b49a9502b011690b615761d4a3)
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