1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
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>
47 #include "ViewShell.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"
59 #include <svx/svditer.hxx>
61 #include <basegfx/color/bcolor.hxx>
65 #define ITEMVALUE( ItemSet, Id, Cast ) ( ( (const Cast&) (ItemSet).Get( (Id) ) ).GetValue() )
66 TYPEINIT1( FuMorph
, FuPoor
);
68 //////////////////////////////////////////////////////////////////////////////
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
);
88 void FuMorph::DoExecute( SfxRequest
& )
90 const SdrMarkList
& rMarkList
= mpView
->GetMarkedObjectList();
92 if(rMarkList
.GetMarkCount() == 2)
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
))
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
)
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())
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
);
189 // erzeugte Polygone wieder loeschen
190 for(pPolyPoly
= (::basegfx::B2DPolyPolygon
*)aPolyPolyList
.First(); pPolyPoly
; pPolyPoly
= (::basegfx::B2DPolyPolygon
*)aPolyPolyList
.Next())
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);
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
;
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
);
240 if(aRetval
.count() >= 3L)
242 aRetval
.setClosed(rCandidate
.isClosed());
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());
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
)
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
)
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
) )
382 if ( ( eFillStyle1
== XFILL_SOLID
) && ( eFillStyle2
== XFILL_SOLID
) )
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
) )
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
);
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
);
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
)
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));
499 for(sal_uInt16
i(0); i
< nSteps
; i
++)
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
);
518 } // end of namespace sd