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