/src/libreoffice/sd/source/ui/func/fumorph.cxx
Line | Count | Source |
1 | | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | /* |
3 | | * This file is part of the LibreOffice project. |
4 | | * |
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/. |
8 | | * |
9 | | * This file incorporates work covered by the following license notice: |
10 | | * |
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 . |
18 | | */ |
19 | | |
20 | | #include <fumorph.hxx> |
21 | | #include <svx/xfillit0.hxx> |
22 | | #include <svx/xlineit0.hxx> |
23 | | #include <svx/xlnclit.hxx> |
24 | | #include <svx/xlntrit.hxx> |
25 | | #include <svx/xlnwtit.hxx> |
26 | | #include <svx/xflclit.hxx> |
27 | | #include <svx/xfltrit.hxx> |
28 | | #include <svx/svdopath.hxx> |
29 | | #include <svx/svdogrp.hxx> |
30 | | #include <editeng/eeitem.hxx> |
31 | | |
32 | | #include <View.hxx> |
33 | | #include <Window.hxx> |
34 | | #include <basegfx/polygon/b2dpolygontools.hxx> |
35 | | #include <basegfx/polygon/b2dpolypolygontools.hxx> |
36 | | #include <basegfx/matrix/b2dhommatrix.hxx> |
37 | | #include <basegfx/matrix/b2dhommatrixtools.hxx> |
38 | | |
39 | | #include <strings.hrc> |
40 | | #include <sdresid.hxx> |
41 | | |
42 | | #include <sdabstdlg.hxx> |
43 | | |
44 | | #include <svx/svditer.hxx> |
45 | | |
46 | | #include <basegfx/color/bcolor.hxx> |
47 | | #include <com/sun/star/drawing/LineStyle.hpp> |
48 | | #include <svx/sdr/properties/properties.hxx> |
49 | | |
50 | | using namespace com::sun::star; |
51 | | |
52 | | namespace sd { |
53 | | |
54 | | FuMorph::FuMorph ( |
55 | | ViewShell& rViewSh, |
56 | | ::sd::Window* pWin, |
57 | | ::sd::View* pView, |
58 | | SdDrawDocument& rDoc, |
59 | | SfxRequest& rReq ) |
60 | 0 | : FuPoor(rViewSh, pWin, pView, rDoc, rReq) |
61 | 0 | { |
62 | 0 | } |
63 | | |
64 | | rtl::Reference<FuPoor> FuMorph::Create( |
65 | | ViewShell& rViewSh, |
66 | | ::sd::Window* pWin, |
67 | | ::sd::View* pView, |
68 | | SdDrawDocument& rDoc, |
69 | | SfxRequest& rReq |
70 | | ) |
71 | 0 | { |
72 | 0 | rtl::Reference<FuPoor> xFunc( new FuMorph( rViewSh, pWin, pView, rDoc, rReq ) ); |
73 | 0 | xFunc->DoExecute(rReq); |
74 | 0 | return xFunc; |
75 | 0 | } |
76 | | |
77 | | void FuMorph::DoExecute( SfxRequest& ) |
78 | 0 | { |
79 | 0 | const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); |
80 | |
|
81 | 0 | if(rMarkList.GetMarkCount() != 2) |
82 | 0 | return; |
83 | | |
84 | | // create clones |
85 | 0 | SdrObject* pObj1 = rMarkList.GetMark(0)->GetMarkedSdrObj(); |
86 | 0 | SdrObject* pObj2 = rMarkList.GetMark(1)->GetMarkedSdrObj(); |
87 | 0 | rtl::Reference<SdrObject> pCloneObj1(pObj1->CloneSdrObject(pObj1->getSdrModelFromSdrObject())); |
88 | 0 | rtl::Reference<SdrObject> pCloneObj2(pObj2->CloneSdrObject(pObj2->getSdrModelFromSdrObject())); |
89 | | |
90 | | // delete text at clone, otherwise we do not get a correct PathObj |
91 | 0 | pCloneObj1->SetOutlinerParaObject(std::nullopt); |
92 | 0 | pCloneObj2->SetOutlinerParaObject(std::nullopt); |
93 | | |
94 | | // create path objects |
95 | 0 | rtl::Reference<SdrObject> pPolyObj1 = pCloneObj1->ConvertToPolyObj(false, false); |
96 | 0 | rtl::Reference<SdrObject> pPolyObj2 = pCloneObj2->ConvertToPolyObj(false, false); |
97 | 0 | SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); |
98 | 0 | ScopedVclPtr<AbstractMorphDlg> pDlg( pFact->CreateMorphDlg(mpWindow ? mpWindow->GetFrameWeld() : nullptr, pObj1, pObj2) ); |
99 | 0 | if(pPolyObj1 && pPolyObj2 && (pDlg->Execute() == RET_OK)) |
100 | 0 | { |
101 | 0 | B2DPolyPolygonList_impl aPolyPolyList; |
102 | 0 | ::basegfx::B2DPolyPolygon aPolyPoly1; |
103 | 0 | ::basegfx::B2DPolyPolygon aPolyPoly2; |
104 | |
|
105 | 0 | pDlg->SaveSettings(); |
106 | | |
107 | | // #i48168# Not always is the pPolyObj1/pPolyObj2 a SdrPathObj, it may also be a group object |
108 | | // containing SdrPathObjs. To get the polygons, I add two iters here |
109 | 0 | SdrObjListIter aIter1(*pPolyObj1); |
110 | 0 | SdrObjListIter aIter2(*pPolyObj2); |
111 | |
|
112 | 0 | while(aIter1.IsMore()) |
113 | 0 | { |
114 | 0 | SdrObject* pObj = aIter1.Next(); |
115 | 0 | if(auto pPathObj = dynamic_cast< SdrPathObj *>( pObj )) |
116 | 0 | aPolyPoly1.append(pPathObj->GetPathPoly()); |
117 | 0 | } |
118 | |
|
119 | 0 | while(aIter2.IsMore()) |
120 | 0 | { |
121 | 0 | SdrObject* pObj = aIter2.Next(); |
122 | 0 | if(auto pPathObj = dynamic_cast< SdrPathObj *>( pObj )) |
123 | 0 | aPolyPoly2.append(pPathObj->GetPathPoly()); |
124 | 0 | } |
125 | | |
126 | | // perform morphing |
127 | 0 | if(aPolyPoly1.count() && aPolyPoly2.count()) |
128 | 0 | { |
129 | 0 | aPolyPoly1 = ::basegfx::utils::correctOrientations(aPolyPoly1); |
130 | 0 | aPolyPoly1.removeDoublePoints(); |
131 | 0 | ::basegfx::B2VectorOrientation eIsClockwise1(::basegfx::utils::getOrientation(aPolyPoly1.getB2DPolygon(0))); |
132 | |
|
133 | 0 | aPolyPoly2 = ::basegfx::utils::correctOrientations(aPolyPoly2); |
134 | 0 | aPolyPoly2.removeDoublePoints(); |
135 | 0 | ::basegfx::B2VectorOrientation eIsClockwise2(::basegfx::utils::getOrientation(aPolyPoly2.getB2DPolygon(0))); |
136 | | |
137 | | // set same orientation |
138 | 0 | if(eIsClockwise1 != eIsClockwise2) |
139 | 0 | aPolyPoly2.flip(); |
140 | | |
141 | | // force same poly count |
142 | 0 | if(aPolyPoly1.count() < aPolyPoly2.count()) |
143 | 0 | ImpAddPolys(aPolyPoly1, aPolyPoly2); |
144 | 0 | else if(aPolyPoly2.count() < aPolyPoly1.count()) |
145 | 0 | ImpAddPolys(aPolyPoly2, aPolyPoly1); |
146 | | |
147 | | // use orientation flag from dialog |
148 | 0 | if(!pDlg->IsOrientationFade()) |
149 | 0 | aPolyPoly2.flip(); |
150 | | |
151 | | // force same point counts |
152 | 0 | for( sal_uInt32 a(0); a < aPolyPoly1.count(); a++ ) |
153 | 0 | { |
154 | 0 | ::basegfx::B2DPolygon aSub1(aPolyPoly1.getB2DPolygon(a)); |
155 | 0 | ::basegfx::B2DPolygon aSub2(aPolyPoly2.getB2DPolygon(a)); |
156 | |
|
157 | 0 | if(aSub1.count() < aSub2.count()) |
158 | 0 | ImpEqualizePolyPointCount(aSub1, aSub2); |
159 | 0 | else if(aSub2.count() < aSub1.count()) |
160 | 0 | ImpEqualizePolyPointCount(aSub2, aSub1); |
161 | |
|
162 | 0 | aPolyPoly1.setB2DPolygon(a, aSub1); |
163 | 0 | aPolyPoly2.setB2DPolygon(a, aSub2); |
164 | 0 | } |
165 | |
|
166 | 0 | ImpMorphPolygons(aPolyPoly1, aPolyPoly2, pDlg->GetFadeSteps(), aPolyPolyList); |
167 | |
|
168 | 0 | OUString aString = rMarkList.GetMarkDescription() + |
169 | 0 | " " + SdResId(STR_UNDO_MORPHING); |
170 | |
|
171 | 0 | mpView->BegUndo(aString); |
172 | 0 | ImpInsertPolygons(aPolyPolyList, pDlg->IsAttributeFade(), pObj1, pObj2); |
173 | 0 | mpView->EndUndo(); |
174 | 0 | } |
175 | 0 | } |
176 | 0 | } |
177 | | |
178 | | static ::basegfx::B2DPolygon ImpGetExpandedPolygon( |
179 | | const ::basegfx::B2DPolygon& rCandidate, |
180 | | sal_uInt32 nNum |
181 | | ) |
182 | 0 | { |
183 | 0 | if(rCandidate.count() && nNum && rCandidate.count() != nNum) |
184 | 0 | { |
185 | | // length of step in dest poly |
186 | 0 | ::basegfx::B2DPolygon aRetval; |
187 | 0 | const double fStep(::basegfx::utils::getLength(rCandidate) / static_cast<double>(rCandidate.isClosed() ? nNum : nNum - 1)); |
188 | 0 | double fDestPos(0.0); |
189 | 0 | double fSrcPos(0.0); |
190 | 0 | sal_uInt32 nSrcPos(0); |
191 | 0 | sal_uInt32 nSrcPosNext((nSrcPos + 1 == rCandidate.count()) ? 0 : nSrcPos + 1); |
192 | 0 | double fNextSrcLen(::basegfx::B2DVector(rCandidate.getB2DPoint(nSrcPos) - rCandidate.getB2DPoint(nSrcPosNext)).getLength()); |
193 | |
|
194 | 0 | for(sal_uInt32 b(0); b < nNum; b++) |
195 | 0 | { |
196 | | // calc fDestPos in source |
197 | 0 | while(fSrcPos + fNextSrcLen < fDestPos) |
198 | 0 | { |
199 | 0 | fSrcPos += fNextSrcLen; |
200 | 0 | nSrcPos++; |
201 | 0 | nSrcPosNext = (nSrcPos + 1 == rCandidate.count()) ? 0 : nSrcPos + 1; |
202 | 0 | fNextSrcLen = ::basegfx::B2DVector(rCandidate.getB2DPoint(nSrcPos) - rCandidate.getB2DPoint(nSrcPosNext)).getLength(); |
203 | 0 | } |
204 | | |
205 | | // fDestPos is between fSrcPos and (fSrcPos + fNextSrcLen) |
206 | 0 | const double fLenA((fDestPos - fSrcPos) / fNextSrcLen); |
207 | 0 | const ::basegfx::B2DPoint aOld1(rCandidate.getB2DPoint(nSrcPos)); |
208 | 0 | const ::basegfx::B2DPoint aOld2(rCandidate.getB2DPoint(nSrcPosNext)); |
209 | 0 | ::basegfx::B2DPoint aNewPoint(basegfx::interpolate(aOld1, aOld2, fLenA)); |
210 | 0 | aRetval.append(aNewPoint); |
211 | | |
212 | | // next step |
213 | 0 | fDestPos += fStep; |
214 | 0 | } |
215 | |
|
216 | 0 | if(aRetval.count() >= 3) |
217 | 0 | { |
218 | 0 | aRetval.setClosed(rCandidate.isClosed()); |
219 | 0 | } |
220 | |
|
221 | 0 | return aRetval; |
222 | 0 | } |
223 | 0 | else |
224 | 0 | { |
225 | 0 | return rCandidate; |
226 | 0 | } |
227 | 0 | } |
228 | | |
229 | | /** |
230 | | * make the point count of the polygons equal in adding points |
231 | | */ |
232 | | void FuMorph::ImpEqualizePolyPointCount( |
233 | | ::basegfx::B2DPolygon& rSmall, |
234 | | const ::basegfx::B2DPolygon& rBig |
235 | | ) |
236 | 0 | { |
237 | | // create poly with equal point count |
238 | 0 | const sal_uInt32 nCnt(rBig.count()); |
239 | 0 | ::basegfx::B2DPolygon aPoly1(ImpGetExpandedPolygon(rSmall, nCnt)); |
240 | | |
241 | | // create transformation for rBig to do the compare |
242 | 0 | const ::basegfx::B2DRange aSrcSize(rBig.getB2DRange()); |
243 | 0 | const ::basegfx::B2DPoint aSrcPos(aSrcSize.getCenter()); |
244 | 0 | const ::basegfx::B2DRange aDstSize(rSmall.getB2DRange()); |
245 | 0 | const ::basegfx::B2DPoint aDstPos(aDstSize.getCenter()); |
246 | |
|
247 | 0 | basegfx::B2DHomMatrix aTrans(basegfx::utils::createTranslateB2DHomMatrix(-aSrcPos.getX(), -aSrcPos.getY())); |
248 | 0 | aTrans.scale(aDstSize.getWidth() / aSrcSize.getWidth(), aDstSize.getHeight() / aSrcSize.getHeight()); |
249 | 0 | aTrans.translate(aDstPos.getX(), aDstPos.getY()); |
250 | | |
251 | | // transpose points to have smooth linear blending |
252 | 0 | ::basegfx::B2DPolygon aPoly2; |
253 | 0 | aPoly2.append(::basegfx::B2DPoint(), nCnt); |
254 | 0 | sal_uInt32 nInd(ImpGetNearestIndex(aPoly1, aTrans * rBig.getB2DPoint(0))); |
255 | |
|
256 | 0 | for(sal_uInt32 a(0); a < nCnt; a++) |
257 | 0 | { |
258 | 0 | aPoly2.setB2DPoint((a + nCnt - nInd) % nCnt, aPoly1.getB2DPoint(a)); |
259 | 0 | } |
260 | |
|
261 | 0 | aPoly2.setClosed(rBig.isClosed()); |
262 | 0 | rSmall = std::move(aPoly2); |
263 | 0 | } |
264 | | |
265 | | sal_uInt32 FuMorph::ImpGetNearestIndex( |
266 | | const ::basegfx::B2DPolygon& rPoly, |
267 | | const ::basegfx::B2DPoint& rPos |
268 | | ) |
269 | 0 | { |
270 | 0 | double fMinDist = 0.0; |
271 | 0 | sal_uInt32 nActInd = 0; |
272 | |
|
273 | 0 | for(sal_uInt32 a(0); a < rPoly.count(); a++) |
274 | 0 | { |
275 | 0 | double fNewDist(::basegfx::B2DVector(rPoly.getB2DPoint(a) - rPos).getLength()); |
276 | |
|
277 | 0 | if(!a || fNewDist < fMinDist) |
278 | 0 | { |
279 | 0 | fMinDist = fNewDist; |
280 | 0 | nActInd = a; |
281 | 0 | } |
282 | 0 | } |
283 | |
|
284 | 0 | return nActInd; |
285 | 0 | } |
286 | | |
287 | | /** |
288 | | * add to a point reduced polys until count is same |
289 | | */ |
290 | | void FuMorph::ImpAddPolys( |
291 | | ::basegfx::B2DPolyPolygon& rSmaller, |
292 | | const ::basegfx::B2DPolyPolygon& rBigger |
293 | | ) |
294 | 0 | { |
295 | 0 | while(rSmaller.count() < rBigger.count()) |
296 | 0 | { |
297 | 0 | const ::basegfx::B2DPolygon& aToBeCopied(rBigger.getB2DPolygon(rSmaller.count())); |
298 | 0 | const ::basegfx::B2DRange aToBeCopiedPolySize(aToBeCopied.getB2DRange()); |
299 | 0 | ::basegfx::B2DPoint aNewPoint(aToBeCopiedPolySize.getCenter()); |
300 | 0 | ::basegfx::B2DPolygon aNewPoly; |
301 | |
|
302 | 0 | const ::basegfx::B2DRange aSrcSize(rBigger.getB2DPolygon(0).getB2DRange()); |
303 | 0 | const ::basegfx::B2DPoint aSrcPos(aSrcSize.getCenter()); |
304 | 0 | const ::basegfx::B2DRange aDstSize(rSmaller.getB2DPolygon(0).getB2DRange()); |
305 | 0 | const ::basegfx::B2DPoint aDstPos(aDstSize.getCenter()); |
306 | 0 | aNewPoint = aNewPoint - aSrcPos + aDstPos; |
307 | |
|
308 | 0 | for(sal_uInt32 a(0); a < aToBeCopied.count(); a++) |
309 | 0 | { |
310 | 0 | aNewPoly.append(aNewPoint); |
311 | 0 | } |
312 | |
|
313 | 0 | rSmaller.append(aNewPoly); |
314 | 0 | } |
315 | 0 | } |
316 | | |
317 | | /** |
318 | | * create group object with morphed polygons |
319 | | */ |
320 | | namespace |
321 | | { |
322 | | struct FillAndLineProps |
323 | | { |
324 | | drawing::FillStyle eFillStyle; |
325 | | Color aFillColor; |
326 | | sal_uInt16 nFillTransparence = 0; |
327 | | drawing::LineStyle eLineStyle; |
328 | | ::tools::Long nLineWidth = 0; |
329 | | Color aLineColor; |
330 | | sal_uInt16 nLineTransparence = 0; |
331 | | }; |
332 | | |
333 | | void getFillAndLinePropsFromObject(const SdrObject* pObj, FillAndLineProps& rFillAndLineProps) |
334 | 0 | { |
335 | 0 | const sdr::properties::BaseProperties& rProps = pObj->GetProperties(); |
336 | 0 | rFillAndLineProps.eFillStyle = rProps.GetItem(XATTR_FILLSTYLE).GetValue(); |
337 | 0 | if (rFillAndLineProps.eFillStyle == drawing::FillStyle_SOLID) |
338 | 0 | { |
339 | 0 | rFillAndLineProps.aFillColor = rProps.GetItem(XATTR_FILLCOLOR).GetColorValue(); |
340 | 0 | rFillAndLineProps.nFillTransparence = rProps.GetItem(XATTR_FILLTRANSPARENCE).GetValue(); |
341 | 0 | } |
342 | 0 | rFillAndLineProps.eLineStyle = rProps.GetItem(XATTR_LINESTYLE).GetValue(); |
343 | 0 | if (rFillAndLineProps.eLineStyle != drawing::LineStyle_NONE) |
344 | 0 | { |
345 | 0 | rFillAndLineProps.nLineWidth = rProps.GetItem(XATTR_LINEWIDTH).GetValue(); |
346 | 0 | rFillAndLineProps.aLineColor = rProps.GetItem(XATTR_LINECOLOR).GetColorValue(); |
347 | 0 | rFillAndLineProps.nLineTransparence = rProps.GetItem(XATTR_LINETRANSPARENCE).GetValue(); |
348 | 0 | } |
349 | 0 | } |
350 | | } // end namespace |
351 | | |
352 | | void FuMorph::ImpInsertPolygons(B2DPolyPolygonList_impl& rPolyPolyList3D, bool bAttributeFade, |
353 | | const SdrObject* pObj1, const SdrObject* pObj2) |
354 | 0 | { |
355 | 0 | SdrPageView* pPageView = mpView->GetSdrPageView(); |
356 | 0 | if ( !pPageView ) |
357 | 0 | return; // Should not happen. Would be error in caller. |
358 | | |
359 | 0 | const size_t nCount = rPolyPolyList3D.size(); |
360 | 0 | if (nCount == 0) |
361 | 0 | return; |
362 | | |
363 | 0 | SfxItemPool& rPool = pObj1->GetObjectItemPool(); |
364 | 0 | SfxItemSet aSet1(SfxItemSet::makeFixedSfxItemSet<SDRATTR_START,SDRATTR_NOTPERSIST_FIRST - 1, |
365 | 0 | EE_ITEMS_START,EE_ITEMS_END>(rPool)); |
366 | 0 | SfxItemSet aSet2(aSet1); |
367 | 0 | aSet1.Put(pObj1->GetMergedItemSet()); |
368 | 0 | aSet2.Put(pObj2->GetMergedItemSet()); |
369 | | |
370 | | // Prepare fill and line properties. |
371 | 0 | bool bIgnoreLine = false; |
372 | 0 | bool bIgnoreFill = false; |
373 | 0 | FillAndLineProps aStartProps; |
374 | 0 | FillAndLineProps aEndProps; |
375 | 0 | if (bAttributeFade) |
376 | 0 | { |
377 | 0 | getFillAndLinePropsFromObject(pObj1, aStartProps); |
378 | 0 | getFillAndLinePropsFromObject(pObj2, aEndProps); |
379 | | |
380 | | // Inspect fill properties |
381 | | // The view guarantees that only NONE or SOLID appear for fill. No need to test again. |
382 | 0 | if (aStartProps.eFillStyle == drawing::FillStyle_NONE |
383 | 0 | && aEndProps.eFillStyle == drawing::FillStyle_NONE) |
384 | 0 | bIgnoreFill = true; |
385 | 0 | else |
386 | 0 | { |
387 | | // We fake NONE with a solid, transparent fill to get a nice cross-fading. |
388 | 0 | if (aStartProps.eFillStyle == drawing::FillStyle_NONE) |
389 | 0 | { |
390 | 0 | aStartProps.eFillStyle = drawing::FillStyle_SOLID; |
391 | 0 | aStartProps.nFillTransparence = 100; |
392 | 0 | aStartProps.aFillColor = aEndProps.aFillColor; |
393 | 0 | } |
394 | 0 | if (aEndProps.eFillStyle == drawing::FillStyle_NONE) |
395 | 0 | { |
396 | 0 | aEndProps.eFillStyle = drawing::FillStyle_SOLID; |
397 | 0 | aEndProps.nFillTransparence = 100; |
398 | 0 | aEndProps.aFillColor = aEndProps.aFillColor; |
399 | 0 | } |
400 | 0 | } |
401 | | |
402 | | // Inspect line properties |
403 | 0 | if (aStartProps.eLineStyle == drawing::LineStyle_NONE |
404 | 0 | && aEndProps.eLineStyle == drawing::LineStyle_NONE) |
405 | 0 | bIgnoreLine = true; |
406 | 0 | else |
407 | 0 | { |
408 | | // We cannot morph any dash style. Instead we use SOLID in between. |
409 | 0 | if (aStartProps.eLineStyle != drawing::LineStyle_NONE) |
410 | 0 | aStartProps.eLineStyle = drawing::LineStyle_SOLID; |
411 | 0 | if (aEndProps.eLineStyle != drawing::LineStyle_NONE) |
412 | 0 | aEndProps.eLineStyle = drawing::LineStyle_SOLID; |
413 | | // Now type for start,end can be NONE,SOLID or SOLID,NONE or SOLID,SOLID. |
414 | | // We fake NONE with a solid, transparent hairline to get a nice cross-fading. |
415 | 0 | if (aStartProps.eLineStyle == drawing::LineStyle_NONE) |
416 | 0 | { |
417 | 0 | aStartProps.eLineStyle = drawing::LineStyle_SOLID; |
418 | 0 | aStartProps.nLineWidth = 0; |
419 | 0 | aStartProps.nLineTransparence = 100; |
420 | 0 | aStartProps.aLineColor = aEndProps.aLineColor; |
421 | 0 | } |
422 | 0 | if (aEndProps.eLineStyle == drawing::LineStyle_NONE) |
423 | 0 | { |
424 | 0 | aEndProps.eLineStyle = drawing::LineStyle_SOLID; |
425 | 0 | aEndProps.nLineWidth = 0; |
426 | 0 | aEndProps.nLineTransparence = 100; |
427 | 0 | aEndProps.aLineColor = aStartProps.aLineColor; |
428 | 0 | } |
429 | 0 | } |
430 | 0 | } |
431 | | |
432 | | // Initialize group object |
433 | 0 | rtl::Reference<SdrObjGroup> xObjGroup(new SdrObjGroup(mpView->getSdrModelFromSdrView())); |
434 | 0 | SdrObjList* pObjList = xObjGroup->GetSubList(); |
435 | 0 | pObjList->InsertObject(pObj1->CloneSdrObject(pObj1->getSdrModelFromSdrObject()).get()); |
436 | 0 | SfxItemSet aSet( std::move(aSet1) ); |
437 | 0 | aSet.Put(XLineStyleItem(drawing::LineStyle_SOLID)); |
438 | 0 | aSet.Put(XFillStyleItem(drawing::FillStyle_SOLID)); |
439 | | |
440 | | // Prepair linear interpolation of fill and line properties |
441 | 0 | const double fStep = 1. / ( nCount + 1 ); |
442 | 0 | double fFactor = fStep; |
443 | | |
444 | | // For each intermediate B2DPolyPolygon create a new SdrPathObj, apply interpolated fill and line |
445 | | // properties if applicable, and then add it to the group object. |
446 | 0 | for ( size_t i = 0; i < nCount; i++, fFactor += fStep ) |
447 | 0 | { |
448 | | // create object |
449 | 0 | const ::basegfx::B2DPolyPolygon& rPolyPoly3D = rPolyPolyList3D[ i ]; |
450 | 0 | rtl::Reference<SdrPathObj> pNewObj |
451 | 0 | = new SdrPathObj(mpView->getSdrModelFromSdrView(), SdrObjKind::Polygon, rPolyPoly3D); |
452 | |
|
453 | 0 | if (bAttributeFade) |
454 | 0 | { |
455 | | // line color and transparence |
456 | 0 | if (bIgnoreLine) |
457 | 0 | aSet.Put(XLineStyleItem(drawing::LineStyle_NONE)); |
458 | 0 | else |
459 | 0 | { |
460 | 0 | const basegfx::BColor aLineCol(basegfx::interpolate( |
461 | 0 | aStartProps.aLineColor.getBColor(), aEndProps.aLineColor.getBColor(), fFactor)); |
462 | 0 | aSet.Put(XLineColorItem( u""_ustr, Color(aLineCol))); |
463 | 0 | const double fLineTransparence = aEndProps.nLineTransparence * fFactor |
464 | 0 | + aStartProps.nLineTransparence * (1.0 - fFactor); |
465 | 0 | aSet.Put(XLineTransparenceItem(static_cast<sal_uInt16>(fLineTransparence + 0.5))); |
466 | 0 | const double fLineWidth |
467 | 0 | = aEndProps.nLineWidth * fFactor + aStartProps.nLineWidth * (1.0 - fFactor); |
468 | 0 | aSet.Put(XLineWidthItem(static_cast<::tools::Long>(fLineWidth + 0.5))); |
469 | 0 | } |
470 | | |
471 | | // fill color and transparence |
472 | 0 | if (bIgnoreFill) |
473 | 0 | aSet.Put( XFillStyleItem( drawing::FillStyle_NONE ) ); |
474 | 0 | else |
475 | 0 | { |
476 | 0 | const basegfx::BColor aFillCol(basegfx::interpolate( |
477 | 0 | aStartProps.aFillColor.getBColor(), aEndProps.aFillColor.getBColor(), fFactor)); |
478 | 0 | aSet.Put(XFillColorItem( u""_ustr, Color(aFillCol))); |
479 | 0 | const double fFillTransparence = aEndProps.nFillTransparence * fFactor |
480 | 0 | + aStartProps.nFillTransparence * (1.0 - fFactor); |
481 | 0 | aSet.Put(XFillTransparenceItem(static_cast<sal_uInt16>(fFillTransparence + 0.5))); |
482 | 0 | } |
483 | 0 | } |
484 | |
|
485 | 0 | pNewObj->SetMergedItemSetAndBroadcast(aSet); |
486 | 0 | pObjList->InsertObject( pNewObj.get() ); |
487 | 0 | } |
488 | 0 | pObjList->InsertObject(pObj2->CloneSdrObject(pObj2->getSdrModelFromSdrObject()).get()); |
489 | |
|
490 | 0 | mpView->DeleteMarked(); |
491 | 0 | mpView->InsertObjectAtView(xObjGroup.get(), *pPageView, SdrInsertFlags:: SETDEFLAYER); |
492 | 0 | } |
493 | | |
494 | | /** |
495 | | * create single morphed PolyPolygon |
496 | | */ |
497 | | ::basegfx::B2DPolyPolygon FuMorph::ImpCreateMorphedPolygon( |
498 | | const ::basegfx::B2DPolyPolygon& rPolyPolyStart, |
499 | | const ::basegfx::B2DPolyPolygon& rPolyPolyEnd, |
500 | | double fMorphingFactor |
501 | | ) |
502 | 0 | { |
503 | 0 | ::basegfx::B2DPolyPolygon aNewPolyPolygon; |
504 | 0 | const double fFactor = 1.0 - fMorphingFactor; |
505 | |
|
506 | 0 | for(sal_uInt32 a(0); a < rPolyPolyStart.count(); a++) |
507 | 0 | { |
508 | 0 | const ::basegfx::B2DPolygon& aPolyStart(rPolyPolyStart.getB2DPolygon(a)); |
509 | 0 | const ::basegfx::B2DPolygon& aPolyEnd(rPolyPolyEnd.getB2DPolygon(a)); |
510 | 0 | const sal_uInt32 nCount(aPolyStart.count()); |
511 | 0 | ::basegfx::B2DPolygon aNewPolygon; |
512 | |
|
513 | 0 | for(sal_uInt32 b(0); b < nCount; b++) |
514 | 0 | { |
515 | 0 | const ::basegfx::B2DPoint& aPtStart(aPolyStart.getB2DPoint(b)); |
516 | 0 | const ::basegfx::B2DPoint& aPtEnd(aPolyEnd.getB2DPoint(b)); |
517 | 0 | aNewPolygon.append(aPtEnd + ((aPtStart - aPtEnd) * fFactor)); |
518 | 0 | } |
519 | |
|
520 | 0 | aNewPolygon.setClosed(aPolyStart.isClosed() && aPolyEnd.isClosed()); |
521 | 0 | aNewPolyPolygon.append(aNewPolygon); |
522 | 0 | } |
523 | |
|
524 | 0 | return aNewPolyPolygon; |
525 | 0 | } |
526 | | |
527 | | /** |
528 | | * create morphed PolyPolygons |
529 | | */ |
530 | | void FuMorph::ImpMorphPolygons( |
531 | | const ::basegfx::B2DPolyPolygon& rPolyPoly1, |
532 | | const ::basegfx::B2DPolyPolygon& rPolyPoly2, |
533 | | const sal_uInt16 nSteps, |
534 | | B2DPolyPolygonList_impl& rPolyPolyList3D |
535 | | ) |
536 | 0 | { |
537 | 0 | if(!nSteps) |
538 | 0 | return; |
539 | | |
540 | 0 | const ::basegfx::B2DRange aStartPolySize(rPolyPoly1.getB2DRange()); |
541 | 0 | const ::basegfx::B2DPoint aStartCenter(aStartPolySize.getCenter()); |
542 | 0 | const ::basegfx::B2DRange aEndPolySize(rPolyPoly2.getB2DRange()); |
543 | 0 | const ::basegfx::B2DPoint aEndCenter(aEndPolySize.getCenter()); |
544 | 0 | const ::basegfx::B2DPoint aDelta(aEndCenter - aStartCenter); |
545 | 0 | const double fFactor(1.0 / (nSteps + 1)); |
546 | 0 | double fValue(0.0); |
547 | |
|
548 | 0 | for(sal_uInt16 i(0); i < nSteps; i++) |
549 | 0 | { |
550 | 0 | fValue += fFactor; |
551 | 0 | ::basegfx::B2DPolyPolygon aNewPolyPoly2D = ImpCreateMorphedPolygon(rPolyPoly1, rPolyPoly2, fValue); |
552 | |
|
553 | 0 | const ::basegfx::B2DRange aNewPolySize(aNewPolyPoly2D.getB2DRange()); |
554 | 0 | const ::basegfx::B2DPoint aNewS(aNewPolySize.getCenter()); |
555 | 0 | const ::basegfx::B2DPoint aRealS(aStartCenter + (aDelta * fValue)); |
556 | 0 | const ::basegfx::B2DPoint aDiff(aRealS - aNewS); |
557 | |
|
558 | 0 | aNewPolyPoly2D.translate(aDiff); |
559 | 0 | rPolyPolyList3D.push_back( std::move(aNewPolyPoly2D) ); |
560 | 0 | } |
561 | 0 | } |
562 | | |
563 | | } // end of namespace sd |
564 | | |
565 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |