bump product version to 4.1.6.2
[LibreOffice.git] / sd / source / ui / func / fumorph.cxx
blob6f432909a83cd3c459749e5d0add08322044aa58
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "fumorph.hxx"
21 #include <svx/xfillit.hxx>
22 #include <svx/xlineit.hxx>
23 #include <vcl/msgbox.hxx>
24 #include <svx/svdpool.hxx>
25 #include <tools/poly.hxx>
26 #include <svx/svdopath.hxx>
27 #include <svx/svdogrp.hxx>
28 #include <editeng/eeitem.hxx>
30 #include "View.hxx"
31 #include "ViewShell.hxx"
32 #include "Window.hxx"
33 #include <basegfx/polygon/b2dpolygontools.hxx>
34 #include <basegfx/polygon/b2dpolypolygontools.hxx>
35 #include <basegfx/matrix/b2dhommatrix.hxx>
36 #include <basegfx/matrix/b2dhommatrixtools.hxx>
38 #include "strings.hrc"
39 #include "sdresid.hxx"
41 #include "sdabstdlg.hxx"
43 // #i48168#
44 #include <svx/svditer.hxx>
46 #include <basegfx/color/bcolor.hxx>
48 namespace sd {
50 #define ITEMVALUE( ItemSet, Id, Cast ) ( ( (const Cast&) (ItemSet).Get( (Id) ) ).GetValue() )
51 TYPEINIT1( FuMorph, FuPoor );
54 FuMorph::FuMorph (
55 ViewShell* pViewSh,
56 ::sd::Window* pWin,
57 ::sd::View* pView,
58 SdDrawDocument* pDoc,
59 SfxRequest& rReq )
60 : FuPoor(pViewSh, pWin, pView, pDoc, rReq)
64 FunctionReference FuMorph::Create(
65 ViewShell* pViewSh,
66 ::sd::Window* pWin,
67 ::sd::View* pView,
68 SdDrawDocument* pDoc,
69 SfxRequest& rReq
72 FunctionReference xFunc( new FuMorph( pViewSh, pWin, pView, pDoc, rReq ) );
73 xFunc->DoExecute(rReq);
74 return xFunc;
77 void FuMorph::DoExecute( SfxRequest& )
79 const SdrMarkList& rMarkList = mpView->GetMarkedObjectList();
81 if(rMarkList.GetMarkCount() == 2)
83 // create clones
84 SdrObject* pObj1 = rMarkList.GetMark(0)->GetMarkedSdrObj();
85 SdrObject* pObj2 = rMarkList.GetMark(1)->GetMarkedSdrObj();
86 SdrObject* pCloneObj1 = pObj1->Clone();
87 SdrObject* pCloneObj2 = pObj2->Clone();
89 // delete text at clone, otherwise we do net get a correct PathObj
90 pCloneObj1->SetOutlinerParaObject(NULL);
91 pCloneObj2->SetOutlinerParaObject(NULL);
93 // create path objects
94 SdrObject* pPolyObj1 = pCloneObj1->ConvertToPolyObj(sal_False, sal_False);
95 SdrObject* pPolyObj2 = pCloneObj2->ConvertToPolyObj(sal_False, sal_False);
96 SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create();
97 AbstractMorphDlg* pDlg = pFact ? pFact->CreateMorphDlg( static_cast< ::Window*>(mpWindow), pObj1, pObj2 ) : 0;
98 if(pPolyObj1 && pPolyObj2 && pDlg && (pDlg->Execute() == RET_OK))
100 B2DPolyPolygonList_impl aPolyPolyList;
101 ::basegfx::B2DPolyPolygon aPolyPoly1;
102 ::basegfx::B2DPolyPolygon aPolyPoly2;
104 pDlg->SaveSettings();
106 // #i48168# Not always is the pPolyObj1/pPolyObj2 a SdrPathObj, it may also be a group object
107 // containing SdrPathObjs. To get the polygons, i add two iters here
108 SdrObjListIter aIter1(*pPolyObj1);
109 SdrObjListIter aIter2(*pPolyObj2);
111 while(aIter1.IsMore())
113 SdrObject* pObj = aIter1.Next();
114 if(pObj && pObj->ISA(SdrPathObj))
115 aPolyPoly1.append(((SdrPathObj*)pObj)->GetPathPoly());
118 while(aIter2.IsMore())
120 SdrObject* pObj = aIter2.Next();
121 if(pObj && pObj->ISA(SdrPathObj))
122 aPolyPoly2.append(((SdrPathObj*)pObj)->GetPathPoly());
125 // perform morphing
126 if(aPolyPoly1.count() && aPolyPoly2.count())
128 aPolyPoly1 = ::basegfx::tools::correctOrientations(aPolyPoly1);
129 aPolyPoly1.removeDoublePoints();
130 ::basegfx::B2VectorOrientation eIsClockwise1(::basegfx::tools::getOrientation(aPolyPoly1.getB2DPolygon(0L)));
132 aPolyPoly2 = ::basegfx::tools::correctOrientations(aPolyPoly2);
133 aPolyPoly2.removeDoublePoints();
134 ::basegfx::B2VectorOrientation eIsClockwise2(::basegfx::tools::getOrientation(aPolyPoly2.getB2DPolygon(0L)));
136 // set same orientation
137 if(eIsClockwise1 != eIsClockwise2)
138 aPolyPoly2.flip();
140 // force same poly count
141 if(aPolyPoly1.count() < aPolyPoly2.count())
142 ImpAddPolys(aPolyPoly1, aPolyPoly2);
143 else if(aPolyPoly2.count() < aPolyPoly1.count())
144 ImpAddPolys(aPolyPoly2, aPolyPoly1);
146 // use orientation flag from dialog
147 if(!pDlg->IsOrientationFade())
148 aPolyPoly2.flip();
150 // force same point counts
151 for( sal_uInt32 a(0L); a < aPolyPoly1.count(); a++ )
153 ::basegfx::B2DPolygon aSub1(aPolyPoly1.getB2DPolygon(a));
154 ::basegfx::B2DPolygon aSub2(aPolyPoly2.getB2DPolygon(a));
156 if(aSub1.count() < aSub2.count())
157 ImpEqualizePolyPointCount(aSub1, aSub2);
158 else if(aSub2.count() < aSub1.count())
159 ImpEqualizePolyPointCount(aSub2, aSub1);
161 aPolyPoly1.setB2DPolygon(a, aSub1);
162 aPolyPoly2.setB2DPolygon(a, aSub2);
165 if(ImpMorphPolygons(aPolyPoly1, aPolyPoly2, pDlg->GetFadeSteps(), aPolyPolyList))
167 String aString(mpView->GetDescriptionOfMarkedObjects());
169 aString.Append(sal_Unicode(' '));
170 aString.Append(String(SdResId(STR_UNDO_MORPHING)));
172 mpView->BegUndo(aString);
173 ImpInsertPolygons(aPolyPolyList, pDlg->IsAttributeFade(), pObj1, pObj2);
174 mpView->EndUndo();
177 for( size_t i = 0, n = aPolyPolyList.size(); i < n; ++i ) {
178 delete aPolyPolyList[ i ];
182 delete pDlg;
183 SdrObject::Free( pCloneObj1 );
184 SdrObject::Free( pCloneObj2 );
186 SdrObject::Free( pPolyObj1 );
187 SdrObject::Free( pPolyObj2 );
191 ::basegfx::B2DPolygon ImpGetExpandedPolygon(
192 const ::basegfx::B2DPolygon& rCandidate,
193 sal_uInt32 nNum
196 if(rCandidate.count() && nNum && rCandidate.count() != nNum)
198 // length of step in dest poly
199 ::basegfx::B2DPolygon aRetval;
200 const double fStep(::basegfx::tools::getLength(rCandidate) / (double)(rCandidate.isClosed() ? nNum : nNum - 1L));
201 double fDestPos(0.0);
202 double fSrcPos(0.0);
203 sal_uInt32 nSrcPos(0L);
204 sal_uInt32 nSrcPosNext((nSrcPos + 1L == rCandidate.count()) ? 0L : nSrcPos + 1L);
205 double fNextSrcLen(::basegfx::B2DVector(rCandidate.getB2DPoint(nSrcPos) - rCandidate.getB2DPoint(nSrcPosNext)).getLength());
207 for(sal_uInt32 b(0L); b < nNum; b++)
209 // calc fDestPos in source
210 while(fSrcPos + fNextSrcLen < fDestPos)
212 fSrcPos += fNextSrcLen;
213 nSrcPos++;
214 nSrcPosNext = (nSrcPos + 1L == rCandidate.count()) ? 0L : nSrcPos + 1L;
215 fNextSrcLen = ::basegfx::B2DVector(rCandidate.getB2DPoint(nSrcPos) - rCandidate.getB2DPoint(nSrcPosNext)).getLength();
218 // fDestPos is between fSrcPos and (fSrcPos + fNextSrcLen)
219 const double fLenA((fDestPos - fSrcPos) / fNextSrcLen);
220 const ::basegfx::B2DPoint aOld1(rCandidate.getB2DPoint(nSrcPos));
221 const ::basegfx::B2DPoint aOld2(rCandidate.getB2DPoint(nSrcPosNext));
222 ::basegfx::B2DPoint aNewPoint(basegfx::interpolate(aOld1, aOld2, fLenA));
223 aRetval.append(aNewPoint);
225 // next step
226 fDestPos += fStep;
229 if(aRetval.count() >= 3L)
231 aRetval.setClosed(rCandidate.isClosed());
234 return aRetval;
236 else
238 return rCandidate;
243 * make the point count of the polygons equal in adding points
245 void FuMorph::ImpEqualizePolyPointCount(
246 ::basegfx::B2DPolygon& rSmall,
247 const ::basegfx::B2DPolygon& rBig
250 // create poly with equal point count
251 const sal_uInt32 nCnt(rBig.count());
252 ::basegfx::B2DPolygon aPoly1(ImpGetExpandedPolygon(rSmall, nCnt));
254 // create transformation for rBig to do the compare
255 const ::basegfx::B2DRange aSrcSize(::basegfx::tools::getRange(rBig));
256 const ::basegfx::B2DPoint aSrcPos(aSrcSize.getCenter());
257 const ::basegfx::B2DRange aDstSize(::basegfx::tools::getRange(rSmall));
258 const ::basegfx::B2DPoint aDstPos(aDstSize.getCenter());
260 basegfx::B2DHomMatrix aTrans(basegfx::tools::createTranslateB2DHomMatrix(-aSrcPos.getX(), -aSrcPos.getY()));
261 aTrans.scale(aDstSize.getWidth() / aSrcSize.getWidth(), aDstSize.getHeight() / aSrcSize.getHeight());
262 aTrans.translate(aDstPos.getX(), aDstPos.getY());
264 // transpose points to have smooth linear blending
265 ::basegfx::B2DPolygon aPoly2;
266 aPoly2.append(::basegfx::B2DPoint(), nCnt);
267 sal_uInt32 nInd(ImpGetNearestIndex(aPoly1, aTrans * rBig.getB2DPoint(0L)));
269 for(sal_uInt32 a(0L); a < nCnt; a++)
271 aPoly2.setB2DPoint((a + nCnt - nInd) % nCnt, aPoly1.getB2DPoint(a));
274 aPoly2.setClosed(rBig.isClosed());
275 rSmall = aPoly2;
279 sal_uInt32 FuMorph::ImpGetNearestIndex(
280 const ::basegfx::B2DPolygon& rPoly,
281 const ::basegfx::B2DPoint& rPos
284 double fMinDist = 0.0;
285 sal_uInt32 nActInd = 0;
287 for(sal_uInt32 a(0L); a < rPoly.count(); a++)
289 double fNewDist(::basegfx::B2DVector(rPoly.getB2DPoint(a) - rPos).getLength());
291 if(!a || fNewDist < fMinDist)
293 fMinDist = fNewDist;
294 nActInd = a;
298 return nActInd;
302 * add to a point reduced polys until count is same
304 void FuMorph::ImpAddPolys(
305 ::basegfx::B2DPolyPolygon& rSmaller,
306 const ::basegfx::B2DPolyPolygon& rBigger
309 while(rSmaller.count() < rBigger.count())
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;
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;
322 for(sal_uInt32 a(0L); a < aToBeCopied.count(); a++)
324 aNewPoly.append(aNewPoint);
327 rSmaller.append(aNewPoly);
332 * create group object with morphed polygons
334 void FuMorph::ImpInsertPolygons(
335 B2DPolyPolygonList_impl& rPolyPolyList3D,
336 sal_Bool bAttributeFade,
337 const SdrObject* pObj1,
338 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 sal_Bool bLineColor = sal_False;
352 sal_Bool bFillColor = sal_False;
353 sal_Bool bLineWidth = sal_False;
354 sal_Bool bIgnoreLine = sal_False;
355 sal_Bool bIgnoreFill = sal_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 = sal_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 = sal_True;
382 if ( ( eFillStyle1 == XFILL_SOLID ) && ( eFillStyle2 == XFILL_SOLID ) )
384 bFillColor = sal_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 = sal_True;
394 if ( pPageView )
396 SfxItemSet aSet( aSet1 );
397 SdrObjGroup* pObjGroup = new SdrObjGroup;
398 SdrObjList* pObjList = pObjGroup->GetSubList();
399 const size_t nCount = rPolyPolyList3D.size();
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 ( size_t i = 0; i < nCount; i++, fFactor += fStep )
409 const ::basegfx::B2DPolyPolygon& rPolyPoly3D = *rPolyPolyList3D[ i ];
410 SdrPathObj* pNewObj = new SdrPathObj(OBJ_POLY, rPolyPoly3D);
412 // line color
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 // fill color
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 // line width
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 );
450 * create single morphed PolyPolygon
452 ::basegfx::B2DPolyPolygon* FuMorph::ImpCreateMorphedPolygon(
453 const ::basegfx::B2DPolyPolygon& rPolyPolyStart,
454 const ::basegfx::B2DPolyPolygon& rPolyPolyEnd,
455 double fMorphingFactor
458 ::basegfx::B2DPolyPolygon* pNewPolyPolygon = new ::basegfx::B2DPolyPolygon();
459 const double fFactor = 1.0 - fMorphingFactor;
461 for(sal_uInt32 a(0L); a < rPolyPolyStart.count(); a++)
463 const ::basegfx::B2DPolygon aPolyStart(rPolyPolyStart.getB2DPolygon(a));
464 const ::basegfx::B2DPolygon aPolyEnd(rPolyPolyEnd.getB2DPolygon(a));
465 const sal_uInt32 nCount(aPolyStart.count());
466 ::basegfx::B2DPolygon aNewPolygon;
468 for(sal_uInt32 b(0L); b < nCount; b++)
470 const ::basegfx::B2DPoint& aPtStart(aPolyStart.getB2DPoint(b));
471 const ::basegfx::B2DPoint& aPtEnd(aPolyEnd.getB2DPoint(b));
472 aNewPolygon.append(aPtEnd + ((aPtStart - aPtEnd) * fFactor));
475 aNewPolygon.setClosed(aPolyStart.isClosed() && aPolyEnd.isClosed());
476 pNewPolyPolygon->append(aNewPolygon);
479 return pNewPolyPolygon;
483 * create morphed PolyPolygons
485 sal_Bool FuMorph::ImpMorphPolygons(
486 const ::basegfx::B2DPolyPolygon& rPolyPoly1,
487 const ::basegfx::B2DPolyPolygon& rPolyPoly2,
488 const sal_uInt16 nSteps,
489 B2DPolyPolygonList_impl& rPolyPolyList3D
492 if(nSteps)
494 const ::basegfx::B2DRange aStartPolySize(::basegfx::tools::getRange(rPolyPoly1));
495 const ::basegfx::B2DPoint aStartCenter(aStartPolySize.getCenter());
496 const ::basegfx::B2DRange aEndPolySize(::basegfx::tools::getRange(rPolyPoly2));
497 const ::basegfx::B2DPoint aEndCenter(aEndPolySize.getCenter());
498 const ::basegfx::B2DPoint aDelta(aEndCenter - aStartCenter);
499 const double fFactor(1.0 / (nSteps + 1));
500 double fValue(0.0);
502 for(sal_uInt16 i(0); i < nSteps; i++)
504 fValue += fFactor;
505 ::basegfx::B2DPolyPolygon* pNewPolyPoly2D = ImpCreateMorphedPolygon(rPolyPoly1, rPolyPoly2, fValue);
507 const ::basegfx::B2DRange aNewPolySize(::basegfx::tools::getRange(*pNewPolyPoly2D));
508 const ::basegfx::B2DPoint aNewS(aNewPolySize.getCenter());
509 const ::basegfx::B2DPoint aRealS(aStartCenter + (aDelta * fValue));
510 const ::basegfx::B2DPoint aDiff(aRealS - aNewS);
512 pNewPolyPoly2D->transform(basegfx::tools::createTranslateB2DHomMatrix(aDiff));
513 rPolyPolyList3D.push_back( pNewPolyPoly2D );
516 return sal_True;
519 } // end of namespace sd
521 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */