Coverage Report

Created: 2026-08-14 10:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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: */