1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
31 #include "ViewShell.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"
44 #include <svx/svditer.hxx>
46 #include <basegfx/color/bcolor.hxx>
50 #define ITEMVALUE( ItemSet, Id, Cast ) ( ( (const Cast&) (ItemSet).Get( (Id) ) ).GetValue() )
51 TYPEINIT1( FuMorph
, FuPoor
);
60 : FuPoor(pViewSh
, pWin
, pView
, pDoc
, rReq
)
64 FunctionReference
FuMorph::Create(
72 FunctionReference
xFunc( new FuMorph( pViewSh
, pWin
, pView
, pDoc
, rReq
) );
73 xFunc
->DoExecute(rReq
);
77 void FuMorph::DoExecute( SfxRequest
& )
79 const SdrMarkList
& rMarkList
= mpView
->GetMarkedObjectList();
81 if(rMarkList
.GetMarkCount() == 2)
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());
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
)
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())
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
);
177 for( size_t i
= 0, n
= aPolyPolyList
.size(); i
< n
; ++i
) {
178 delete aPolyPolyList
[ i
];
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
,
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);
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
;
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
);
229 if(aRetval
.count() >= 3L)
231 aRetval
.setClosed(rCandidate
.isClosed());
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());
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
)
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
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
;
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
);
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
) );
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
) );
432 aSet
.Put( XLineWidthItem( nStartLineWidth
+ (long) ( fFactor
* fDelta
+ 0.5 ) ) );
434 pNewObj
->SetMergedItemSetAndBroadcast(aSet
);
436 pObjList
->InsertObject( pNewObj
, LIST_APPEND
);
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
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));
502 for(sal_uInt16
i(0); i
< nSteps
; i
++)
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
);
519 } // end of namespace sd
521 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */