merge the formfield patch from ooo-build
[ooovba.git] / sd / source / ui / func / fumorph.cxx
blob3148e4eaec3076dbd8ad3df6ae1ac0de0c43d669
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: fumorph.cxx,v $
10 * $Revision: 1.19.114.2 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_sd.hxx"
34 //#define _FUMORPH_PRIVATE
35 #include "fumorph.hxx"
36 #include <svx/xfillit.hxx>
37 #include <svx/xlineit.hxx>
38 #include <vcl/msgbox.hxx>
39 #include <svx/svdpool.hxx>
40 #include <tools/poly.hxx>
41 #include <svx/svdopath.hxx>
42 #include <svx/svdogrp.hxx>
43 #include <svx/eeitem.hxx>
46 #include "View.hxx"
47 #include "ViewShell.hxx"
48 #include "Window.hxx"
49 #include <basegfx/polygon/b2dpolygontools.hxx>
50 #include <basegfx/polygon/b2dpolypolygontools.hxx>
51 #include <basegfx/matrix/b2dhommatrix.hxx>
53 #include "strings.hrc"
54 #include "sdresid.hxx"
56 #include "sdabstdlg.hxx"
58 // #i48168#
59 #include <svx/svditer.hxx>
61 #include <basegfx/color/bcolor.hxx>
63 namespace sd {
65 #define ITEMVALUE( ItemSet, Id, Cast ) ( ( (const Cast&) (ItemSet).Get( (Id) ) ).GetValue() )
66 TYPEINIT1( FuMorph, FuPoor );
68 //////////////////////////////////////////////////////////////////////////////
69 // constructor
71 FuMorph::FuMorph (
72 ViewShell* pViewSh,
73 ::sd::Window* pWin,
74 ::sd::View* pView,
75 SdDrawDocument* pDoc,
76 SfxRequest& rReq )
77 : FuPoor(pViewSh, pWin, pView, pDoc, rReq)
81 FunctionReference FuMorph::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq )
83 FunctionReference xFunc( new FuMorph( pViewSh, pWin, pView, pDoc, rReq ) );
84 xFunc->DoExecute(rReq);
85 return xFunc;
88 void FuMorph::DoExecute( SfxRequest& )
90 const SdrMarkList& rMarkList = mpView->GetMarkedObjectList();
92 if(rMarkList.GetMarkCount() == 2)
94 // Clones erzeugen
95 SdrObject* pObj1 = rMarkList.GetMark(0)->GetMarkedSdrObj();
96 SdrObject* pObj2 = rMarkList.GetMark(1)->GetMarkedSdrObj();
97 SdrObject* pCloneObj1 = pObj1->Clone();
98 SdrObject* pCloneObj2 = pObj2->Clone();
100 // Text am Clone loeschen, da wir sonst kein richtiges PathObj bekommen
101 pCloneObj1->SetOutlinerParaObject(NULL);
102 pCloneObj2->SetOutlinerParaObject(NULL);
104 // Path-Objekte erzeugen
105 SdrObject* pPolyObj1 = pCloneObj1->ConvertToPolyObj(FALSE, FALSE);
106 SdrObject* pPolyObj2 = pCloneObj2->ConvertToPolyObj(FALSE, FALSE);
107 SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create();
108 AbstractMorphDlg* pDlg = pFact ? pFact->CreateMorphDlg( static_cast< ::Window*>(mpWindow), pObj1, pObj2 ) : 0;
109 if(pPolyObj1 && pPolyObj2 && pDlg && (pDlg->Execute() == RET_OK))
111 List aPolyPolyList;
112 ::basegfx::B2DPolyPolygon aPolyPoly1;
113 ::basegfx::B2DPolyPolygon aPolyPoly2;
114 ::basegfx::B2DPolyPolygon* pPolyPoly;
116 pDlg->SaveSettings();
118 // #i48168# Not always is the pPolyObj1/pPolyObj2 a SdrPathObj, it may also be a group object
119 // containing SdrPathObjs. To get the polygons, i add two iters here
120 SdrObjListIter aIter1(*pPolyObj1);
121 SdrObjListIter aIter2(*pPolyObj2);
123 while(aIter1.IsMore())
125 SdrObject* pObj = aIter1.Next();
126 if(pObj && pObj->ISA(SdrPathObj))
127 aPolyPoly1.append(((SdrPathObj*)pObj)->GetPathPoly());
130 while(aIter2.IsMore())
132 SdrObject* pObj = aIter2.Next();
133 if(pObj && pObj->ISA(SdrPathObj))
134 aPolyPoly2.append(((SdrPathObj*)pObj)->GetPathPoly());
137 // Morphing durchfuehren
138 if(aPolyPoly1.count() && aPolyPoly2.count())
140 aPolyPoly1 = ::basegfx::tools::correctOrientations(aPolyPoly1);
141 aPolyPoly1.removeDoublePoints();
142 ::basegfx::B2VectorOrientation eIsClockwise1(::basegfx::tools::getOrientation(aPolyPoly1.getB2DPolygon(0L)));
144 aPolyPoly2 = ::basegfx::tools::correctOrientations(aPolyPoly2);
145 aPolyPoly2.removeDoublePoints();
146 ::basegfx::B2VectorOrientation eIsClockwise2(::basegfx::tools::getOrientation(aPolyPoly2.getB2DPolygon(0L)));
148 // set same orientation
149 if(eIsClockwise1 != eIsClockwise2)
150 aPolyPoly2.flip();
152 // force same poly count
153 if(aPolyPoly1.count() < aPolyPoly2.count())
154 ImpAddPolys(aPolyPoly1, aPolyPoly2);
155 else if(aPolyPoly2.count() < aPolyPoly1.count())
156 ImpAddPolys(aPolyPoly2, aPolyPoly1);
158 // use orientation flag from dialog
159 if(!pDlg->IsOrientationFade())
160 aPolyPoly2.flip();
162 // force same point counts
163 for( sal_uInt32 a(0L); a < aPolyPoly1.count(); a++ )
165 ::basegfx::B2DPolygon aSub1(aPolyPoly1.getB2DPolygon(a));
166 ::basegfx::B2DPolygon aSub2(aPolyPoly2.getB2DPolygon(a));
168 if(aSub1.count() < aSub2.count())
169 ImpEqualizePolyPointCount(aSub1, aSub2);
170 else if(aSub2.count() < aSub1.count())
171 ImpEqualizePolyPointCount(aSub2, aSub1);
173 aPolyPoly1.setB2DPolygon(a, aSub1);
174 aPolyPoly2.setB2DPolygon(a, aSub2);
177 if(ImpMorphPolygons(aPolyPoly1, aPolyPoly2, pDlg->GetFadeSteps(), aPolyPolyList))
179 String aString(mpView->GetDescriptionOfMarkedObjects());
181 aString.Append(sal_Unicode(' '));
182 aString.Append(String(SdResId(STR_UNDO_MORPHING)));
184 mpView->BegUndo(aString);
185 ImpInsertPolygons(aPolyPolyList, pDlg->IsAttributeFade(), pObj1, pObj2);
186 mpView->EndUndo();
189 // erzeugte Polygone wieder loeschen
190 for(pPolyPoly = (::basegfx::B2DPolyPolygon*)aPolyPolyList.First(); pPolyPoly; pPolyPoly = (::basegfx::B2DPolyPolygon *)aPolyPolyList.Next())
192 delete pPolyPoly;
196 delete pDlg;
197 SdrObject::Free( pCloneObj1 );
198 SdrObject::Free( pCloneObj2 );
200 SdrObject::Free( pPolyObj1 );
201 SdrObject::Free( pPolyObj2 );
205 ::basegfx::B2DPolygon ImpGetExpandedPolygon(const ::basegfx::B2DPolygon& rCandidate, sal_uInt32 nNum)
207 if(rCandidate.count() && nNum && rCandidate.count() != nNum)
209 // length of step in dest poly
210 ::basegfx::B2DPolygon aRetval;
211 const double fStep(::basegfx::tools::getLength(rCandidate) / (double)(rCandidate.isClosed() ? nNum : nNum - 1L));
212 double fDestPos(0.0);
213 double fSrcPos(0.0);
214 sal_uInt32 nSrcPos(0L);
215 sal_uInt32 nSrcPosNext((nSrcPos + 1L == rCandidate.count()) ? 0L : nSrcPos + 1L);
216 double fNextSrcLen(::basegfx::B2DVector(rCandidate.getB2DPoint(nSrcPos) - rCandidate.getB2DPoint(nSrcPosNext)).getLength());
218 for(sal_uInt32 b(0L); b < nNum; b++)
220 // calc fDestPos in source
221 while(fSrcPos + fNextSrcLen < fDestPos)
223 fSrcPos += fNextSrcLen;
224 nSrcPos++;
225 nSrcPosNext = (nSrcPos + 1L == rCandidate.count()) ? 0L : nSrcPos + 1L;
226 fNextSrcLen = ::basegfx::B2DVector(rCandidate.getB2DPoint(nSrcPos) - rCandidate.getB2DPoint(nSrcPosNext)).getLength();
229 // fDestPos is between fSrcPos and (fSrcPos + fNextSrcLen)
230 const double fLenA((fDestPos - fSrcPos) / fNextSrcLen);
231 const ::basegfx::B2DPoint aOld1(rCandidate.getB2DPoint(nSrcPos));
232 const ::basegfx::B2DPoint aOld2(rCandidate.getB2DPoint(nSrcPosNext));
233 ::basegfx::B2DPoint aNewPoint(basegfx::interpolate(aOld1, aOld2, fLenA));
234 aRetval.append(aNewPoint);
236 // next step
237 fDestPos += fStep;
240 if(aRetval.count() >= 3L)
242 aRetval.setClosed(rCandidate.isClosed());
245 return aRetval;
247 else
249 return rCandidate;
253 //////////////////////////////////////////////////////////////////////////////
254 // make the point count of the polygons equal in adding points
256 void FuMorph::ImpEqualizePolyPointCount(::basegfx::B2DPolygon& rSmall, const ::basegfx::B2DPolygon& rBig)
258 // create poly with equal point count
259 const sal_uInt32 nCnt(rBig.count());
260 ::basegfx::B2DPolygon aPoly1(ImpGetExpandedPolygon(rSmall, nCnt));
262 // create transformation for rBig to do the compare
263 const ::basegfx::B2DRange aSrcSize(::basegfx::tools::getRange(rBig));
264 const ::basegfx::B2DPoint aSrcPos(aSrcSize.getCenter());
265 const ::basegfx::B2DRange aDstSize(::basegfx::tools::getRange(rSmall));
266 const ::basegfx::B2DPoint aDstPos(aDstSize.getCenter());
267 ::basegfx::B2DHomMatrix aTrans;
269 aTrans.translate(-aSrcPos.getX(), -aSrcPos.getY());
270 aTrans.scale(aDstSize.getWidth() / aSrcSize.getWidth(), aDstSize.getHeight() / aSrcSize.getHeight());
271 aTrans.translate(aDstPos.getX(), aDstPos.getY());
273 // transpose points to have smooth linear blending
274 ::basegfx::B2DPolygon aPoly2;
275 aPoly2.append(::basegfx::B2DPoint(), nCnt);
276 sal_uInt32 nInd(ImpGetNearestIndex(aPoly1, aTrans * rBig.getB2DPoint(0L)));
278 for(sal_uInt32 a(0L); a < nCnt; a++)
280 aPoly2.setB2DPoint((a + nCnt - nInd) % nCnt, aPoly1.getB2DPoint(a));
283 aPoly2.setClosed(rBig.isClosed());
284 rSmall = aPoly2;
287 //////////////////////////////////////////////////////////////////////////////
289 sal_uInt32 FuMorph::ImpGetNearestIndex(const ::basegfx::B2DPolygon& rPoly, const ::basegfx::B2DPoint& rPos)
291 double fMinDist = 0.0;
292 sal_uInt32 nActInd = 0;
294 for(sal_uInt32 a(0L); a < rPoly.count(); a++)
296 double fNewDist(::basegfx::B2DVector(rPoly.getB2DPoint(a) - rPos).getLength());
298 if(!a || fNewDist < fMinDist)
300 fMinDist = fNewDist;
301 nActInd = a;
305 return nActInd;
308 //////////////////////////////////////////////////////////////////////////////
309 // add to a point reduced polys until count is same
311 void FuMorph::ImpAddPolys(::basegfx::B2DPolyPolygon& rSmaller, const ::basegfx::B2DPolyPolygon& rBigger)
313 while(rSmaller.count() < rBigger.count())
315 const ::basegfx::B2DPolygon aToBeCopied(rBigger.getB2DPolygon(rSmaller.count()));
316 const ::basegfx::B2DRange aToBeCopiedPolySize(::basegfx::tools::getRange(aToBeCopied));
317 ::basegfx::B2DPoint aNewPoint(aToBeCopiedPolySize.getCenter());
318 ::basegfx::B2DPolygon aNewPoly;
320 const ::basegfx::B2DRange aSrcSize(::basegfx::tools::getRange(rBigger.getB2DPolygon(0L)));
321 const ::basegfx::B2DPoint aSrcPos(aSrcSize.getCenter());
322 const ::basegfx::B2DRange aDstSize(::basegfx::tools::getRange(rSmaller.getB2DPolygon(0L)));
323 const ::basegfx::B2DPoint aDstPos(aDstSize.getCenter());
324 aNewPoint = aNewPoint - aSrcPos + aDstPos;
326 for(sal_uInt32 a(0L); a < aToBeCopied.count(); a++)
328 aNewPoly.append(aNewPoint);
331 rSmaller.append(aNewPoly);
335 //////////////////////////////////////////////////////////////////////////////
336 // create group object with morphed polygons
338 void FuMorph::ImpInsertPolygons(List& rPolyPolyList3D, BOOL bAttributeFade,
339 const SdrObject* pObj1, const SdrObject* pObj2)
341 Color aStartFillCol;
342 Color aEndFillCol;
343 Color aStartLineCol;
344 Color aEndLineCol;
345 long nStartLineWidth = 0;
346 long nEndLineWidth = 0;
347 SdrPageView* pPageView = mpView->GetSdrPageView();
348 SfxItemPool* pPool = pObj1->GetObjectItemPool();
349 SfxItemSet aSet1( *pPool,SDRATTR_START,SDRATTR_NOTPERSIST_FIRST-1,EE_ITEMS_START,EE_ITEMS_END,0 );
350 SfxItemSet aSet2( aSet1 );
351 BOOL bLineColor = FALSE;
352 BOOL bFillColor = FALSE;
353 BOOL bLineWidth = FALSE;
354 BOOL bIgnoreLine = FALSE;
355 BOOL bIgnoreFill = FALSE;
357 aSet1.Put(pObj1->GetMergedItemSet());
358 aSet2.Put(pObj2->GetMergedItemSet());
360 const XLineStyle eLineStyle1 = ITEMVALUE( aSet1, XATTR_LINESTYLE, XLineStyleItem );
361 const XLineStyle eLineStyle2 = ITEMVALUE( aSet2, XATTR_LINESTYLE, XLineStyleItem );
362 const XFillStyle eFillStyle1 = ITEMVALUE( aSet1, XATTR_FILLSTYLE, XFillStyleItem );
363 const XFillStyle eFillStyle2 = ITEMVALUE( aSet2, XATTR_FILLSTYLE, XFillStyleItem );
365 if ( bAttributeFade )
367 if ( ( eLineStyle1 != XLINE_NONE ) && ( eLineStyle2 != XLINE_NONE ) )
369 bLineWidth = bLineColor = TRUE;
371 aStartLineCol = static_cast< XLineColorItem const & >(
372 aSet1.Get(XATTR_LINECOLOR)).GetColorValue();
373 aEndLineCol = static_cast< XLineColorItem const & >(
374 aSet2.Get(XATTR_LINECOLOR)).GetColorValue();
376 nStartLineWidth = ITEMVALUE( aSet1, XATTR_LINEWIDTH, XLineWidthItem );
377 nEndLineWidth = ITEMVALUE( aSet2, XATTR_LINEWIDTH, XLineWidthItem );
379 else if ( ( eLineStyle1 == XLINE_NONE ) && ( eLineStyle2 == XLINE_NONE ) )
380 bIgnoreLine = TRUE;
382 if ( ( eFillStyle1 == XFILL_SOLID ) && ( eFillStyle2 == XFILL_SOLID ) )
384 bFillColor = TRUE;
385 aStartFillCol = static_cast< XFillColorItem const & >(
386 aSet1.Get(XATTR_FILLCOLOR)).GetColorValue();
387 aEndFillCol = static_cast< XFillColorItem const & >(
388 aSet2.Get(XATTR_FILLCOLOR)).GetColorValue();
390 else if ( ( eFillStyle1 == XFILL_NONE ) && ( eFillStyle2 == XFILL_NONE ) )
391 bIgnoreFill = TRUE;
394 if ( pPageView )
396 SfxItemSet aSet( aSet1 );
397 SdrObjGroup* pObjGroup = new SdrObjGroup;
398 SdrObjList* pObjList = pObjGroup->GetSubList();
399 const ULONG nCount = rPolyPolyList3D.Count();
400 const double fStep = 1. / ( nCount + 1 );
401 const double fDelta = nEndLineWidth - nStartLineWidth;
402 double fFactor = fStep;
404 aSet.Put( XLineStyleItem( XLINE_SOLID ) );
405 aSet.Put( XFillStyleItem( XFILL_SOLID ) );
407 for ( ULONG i = 0; i < nCount; i++, fFactor += fStep )
409 const ::basegfx::B2DPolyPolygon& rPolyPoly3D = *(::basegfx::B2DPolyPolygon*)rPolyPolyList3D.GetObject(i);
410 SdrPathObj* pNewObj = new SdrPathObj(OBJ_POLY, rPolyPoly3D);
412 // Linienfarbe
413 if ( bLineColor )
415 const basegfx::BColor aLineColor(basegfx::interpolate(aStartLineCol.getBColor(), aEndLineCol.getBColor(), fFactor));
416 aSet.Put( XLineColorItem( aEmptyStr, Color(aLineColor)));
418 else if ( bIgnoreLine )
419 aSet.Put( XLineStyleItem( XLINE_NONE ) );
421 // Fuellfarbe
422 if ( bFillColor )
424 const basegfx::BColor aFillColor(basegfx::interpolate(aStartFillCol.getBColor(), aEndFillCol.getBColor(), fFactor));
425 aSet.Put( XFillColorItem( aEmptyStr, Color(aFillColor)));
427 else if ( bIgnoreFill )
428 aSet.Put( XFillStyleItem( XFILL_NONE ) );
430 // Linienstaerke
431 if ( bLineWidth )
432 aSet.Put( XLineWidthItem( nStartLineWidth + (long) ( fFactor * fDelta + 0.5 ) ) );
434 pNewObj->SetMergedItemSetAndBroadcast(aSet);
436 pObjList->InsertObject( pNewObj, LIST_APPEND );
439 if ( nCount )
441 pObjList->InsertObject( pObj1->Clone(), 0 );
442 pObjList->InsertObject( pObj2->Clone(), LIST_APPEND );
443 mpView->DeleteMarked();
444 mpView->InsertObjectAtView( pObjGroup, *pPageView, SDRINSERT_SETDEFLAYER );
449 //////////////////////////////////////////////////////////////////////////////
450 // create single morphed PolyPolygon
452 ::basegfx::B2DPolyPolygon* FuMorph::ImpCreateMorphedPolygon(
453 const ::basegfx::B2DPolyPolygon& rPolyPolyStart,
454 const ::basegfx::B2DPolyPolygon& rPolyPolyEnd,
455 double fMorphingFactor)
457 ::basegfx::B2DPolyPolygon* pNewPolyPolygon = new ::basegfx::B2DPolyPolygon();
458 const double fFactor = 1.0 - fMorphingFactor;
460 for(sal_uInt32 a(0L); a < rPolyPolyStart.count(); a++)
462 const ::basegfx::B2DPolygon aPolyStart(rPolyPolyStart.getB2DPolygon(a));
463 const ::basegfx::B2DPolygon aPolyEnd(rPolyPolyEnd.getB2DPolygon(a));
464 const sal_uInt32 nCount(aPolyStart.count());
465 ::basegfx::B2DPolygon aNewPolygon;
467 for(sal_uInt32 b(0L); b < nCount; b++)
469 const ::basegfx::B2DPoint& aPtStart(aPolyStart.getB2DPoint(b));
470 const ::basegfx::B2DPoint& aPtEnd(aPolyEnd.getB2DPoint(b));
471 aNewPolygon.append(aPtEnd + ((aPtStart - aPtEnd) * fFactor));
474 aNewPolygon.setClosed(aPolyStart.isClosed() && aPolyEnd.isClosed());
475 pNewPolyPolygon->append(aNewPolygon);
478 return pNewPolyPolygon;
481 //////////////////////////////////////////////////////////////////////////////
482 // create morphed PolyPolygons
484 sal_Bool FuMorph::ImpMorphPolygons(
485 const ::basegfx::B2DPolyPolygon& rPolyPoly1,
486 const ::basegfx::B2DPolyPolygon& rPolyPoly2,
487 const sal_uInt16 nSteps, List& rPolyPolyList3D)
489 if(nSteps)
491 const ::basegfx::B2DRange aStartPolySize(::basegfx::tools::getRange(rPolyPoly1));
492 const ::basegfx::B2DPoint aStartCenter(aStartPolySize.getCenter());
493 const ::basegfx::B2DRange aEndPolySize(::basegfx::tools::getRange(rPolyPoly2));
494 const ::basegfx::B2DPoint aEndCenter(aEndPolySize.getCenter());
495 const ::basegfx::B2DPoint aDelta(aEndCenter - aStartCenter);
496 const double fFactor(1.0 / (nSteps + 1));
497 double fValue(0.0);
499 for(sal_uInt16 i(0); i < nSteps; i++)
501 fValue += fFactor;
502 ::basegfx::B2DPolyPolygon* pNewPolyPoly3D = ImpCreateMorphedPolygon(rPolyPoly1, rPolyPoly2, fValue);
504 const ::basegfx::B2DRange aNewPolySize(::basegfx::tools::getRange(*pNewPolyPoly3D));
505 const ::basegfx::B2DPoint aNewS(aNewPolySize.getCenter());
506 const ::basegfx::B2DPoint aRealS(aStartCenter + (aDelta * fValue));
507 ::basegfx::B2DHomMatrix aTrans;
508 const ::basegfx::B2DPoint aDiff(aRealS - aNewS);
509 aTrans.translate(aDiff.getX(), aDiff.getY());
510 pNewPolyPoly3D->transform(aTrans);
511 rPolyPolyList3D.Insert(pNewPolyPoly3D, LIST_APPEND);
514 return TRUE;
518 } // end of namespace sd