Coverage Report

Created: 2026-07-10 11:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/svx/source/engine3d/view3d.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
21
#include <svx/svdopath.hxx>
22
#include <svx/svditer.hxx>
23
#include <svx/svdmodel.hxx>
24
#include <svx/svdpagv.hxx>
25
#include <editeng/colritem.hxx>
26
#include <editeng/eeitem.hxx>
27
#include <svx/svdview.hxx>
28
#include <svx/strings.hrc>
29
#include <svx/dialmgr.hxx>
30
#include <svx/obj3d.hxx>
31
#include <svx/lathe3d.hxx>
32
#include <extrud3d.hxx>
33
#include <dragmt3d.hxx>
34
#include <svx/scene3d.hxx>
35
#include <svx/view3d.hxx>
36
#include <svx/svdundo.hxx>
37
#include <svx/xflclit.hxx>
38
#include <svx/xlnclit.hxx>
39
#include <svx/xfillit0.hxx>
40
#include <svx/xlineit0.hxx>
41
#include <basegfx/range/b2drange.hxx>
42
#include <basegfx/polygon/b2dpolypolygontools.hxx>
43
#include <svx/xlnwtit.hxx>
44
#include <svx/sdr/overlay/overlaypolypolygon.hxx>
45
#include <svx/sdr/overlay/overlaymanager.hxx>
46
#include <svx/sdrpaintwindow.hxx>
47
#include <svx/sdr/contact/viewcontact.hxx>
48
#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
49
#include <svx/sdr/overlay/overlayprimitive2dsequenceobject.hxx>
50
#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
51
#include <basegfx/matrix/b2dhommatrixtools.hxx>
52
#include <basegfx/polygon/b2dpolypolygoncutter.hxx>
53
#include <svx/e3dsceneupdater.hxx>
54
#include <utility>
55
56
using namespace com::sun::star;
57
58
59
// Migrate Marking
60
61
class Impl3DMirrorConstructOverlay
62
{
63
    // The OverlayObjects
64
    sdr::overlay::OverlayObjectList               maObjects;
65
66
    // the view
67
    const E3dView&                                  mrView;
68
69
    // the object count
70
    size_t mnCount;
71
72
    // the unmirrored polygons
73
    basegfx::B2DPolyPolygon*                        mpPolygons;
74
75
    // the overlay geometry from selected objects
76
    drawinglayer::primitive2d::Primitive2DContainer    maFullOverlay;
77
78
    // Copy assignment is forbidden and not implemented.
79
    Impl3DMirrorConstructOverlay (const Impl3DMirrorConstructOverlay &) = delete;
80
    Impl3DMirrorConstructOverlay & operator= (const Impl3DMirrorConstructOverlay &) = delete;
81
82
public:
83
    explicit Impl3DMirrorConstructOverlay(const E3dView& rView);
84
    ~Impl3DMirrorConstructOverlay();
85
86
    void SetMirrorAxis(Point aMirrorAxisA, Point aMirrorAxisB);
87
};
88
89
Impl3DMirrorConstructOverlay::Impl3DMirrorConstructOverlay(const E3dView& rView)
90
0
:   mrView(rView),
91
0
    mpPolygons(nullptr)
92
0
{
93
0
    const SdrMarkList& rMarkList = mrView.GetMarkedObjectList();
94
0
    mnCount = rMarkList.GetMarkCount();
95
0
    if(!mnCount)
96
0
        return;
97
98
0
    if(mrView.IsSolidDragging())
99
0
    {
100
0
        SdrPageView* pPV = rView.GetSdrPageView();
101
102
0
        if(pPV && pPV->PageWindowCount())
103
0
        {
104
0
            for(size_t a = 0; a < mnCount; ++a)
105
0
            {
106
0
                SdrObject* pObject = rMarkList.GetMark(a)->GetMarkedSdrObj();
107
108
0
                if(pObject)
109
0
                {
110
                    // use the view-independent primitive representation (without
111
                    // evtl. GridOffset, that may be applied to the DragEntry individually)
112
0
                    pObject->GetViewContact().getViewIndependentPrimitive2DContainer(maFullOverlay);
113
0
                }
114
0
            }
115
0
        }
116
0
    }
117
0
    else
118
0
    {
119
0
        mpPolygons = new basegfx::B2DPolyPolygon[mnCount];
120
121
0
        for(size_t a = 0; a < mnCount; ++a)
122
0
        {
123
0
            SdrObject* pObject = rMarkList.GetMark(a)->GetMarkedSdrObj();
124
0
            mpPolygons[mnCount - (a + 1)] = pObject->TakeXorPoly();
125
0
        }
126
0
    }
127
0
}
128
129
Impl3DMirrorConstructOverlay::~Impl3DMirrorConstructOverlay()
130
0
{
131
    // The OverlayObjects are cleared using the destructor of OverlayObjectList.
132
    // That destructor calls clear() at the list which removes all objects from the
133
    // OverlayManager and deletes them.
134
0
    delete[] mpPolygons;
135
0
}
136
137
void Impl3DMirrorConstructOverlay::SetMirrorAxis(Point aMirrorAxisA, Point aMirrorAxisB)
138
0
{
139
    // get rid of old overlay objects
140
0
    maObjects.clear();
141
142
    // create new ones
143
0
    for(sal_uInt32 a(0); a < mrView.PaintWindowCount(); a++)
144
0
    {
145
0
        SdrPaintWindow* pCandidate = mrView.GetPaintWindow(a);
146
0
        const rtl::Reference< sdr::overlay::OverlayManager >& xTargetOverlay = pCandidate->GetOverlayManager();
147
148
0
        if(xTargetOverlay.is())
149
0
        {
150
            // build transformation: translate and rotate so that given edge is
151
            // on x axis, them mirror in y and translate back
152
0
            const basegfx::B2DVector aEdge(aMirrorAxisB.X() - aMirrorAxisA.X(), aMirrorAxisB.Y() - aMirrorAxisA.Y());
153
0
            basegfx::B2DHomMatrix aMatrixTransform(basegfx::utils::createTranslateB2DHomMatrix(
154
0
                -aMirrorAxisA.X(), -aMirrorAxisA.Y()));
155
0
            aMatrixTransform.rotate(-atan2(aEdge.getY(), aEdge.getX()));
156
0
            aMatrixTransform.scale(1.0, -1.0);
157
0
            aMatrixTransform.rotate(atan2(aEdge.getY(), aEdge.getX()));
158
0
            aMatrixTransform.translate(aMirrorAxisA.X(), aMirrorAxisA.Y());
159
160
0
            if(mrView.IsSolidDragging())
161
0
            {
162
0
                if(!maFullOverlay.empty())
163
0
                {
164
0
                    drawinglayer::primitive2d::Primitive2DContainer aContent(maFullOverlay);
165
166
0
                    if(!aMatrixTransform.isIdentity())
167
0
                    {
168
                        // embed in transformation group
169
0
                        aContent = drawinglayer::primitive2d::Primitive2DContainer {
170
0
                            new drawinglayer::primitive2d::TransformPrimitive2D(aMatrixTransform, std::move(aContent))
171
0
                        };
172
0
                    }
173
174
                    // if we have full overlay from selected objects, embed with 50% transparence, the
175
                    // transformation is added to the OverlayPrimitive2DSequenceObject
176
0
                    aContent = drawinglayer::primitive2d::Primitive2DContainer {
177
0
                        new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(std::move(aContent), 0.5)
178
0
                    };
179
180
0
                    std::unique_ptr<sdr::overlay::OverlayPrimitive2DSequenceObject> pNew(new sdr::overlay::OverlayPrimitive2DSequenceObject(std::move(aContent)));
181
182
0
                    xTargetOverlay->add(*pNew);
183
0
                    maObjects.append(std::move(pNew));
184
0
                }
185
0
            }
186
0
            else
187
0
            {
188
0
                for(size_t b = 0; b < mnCount; ++b)
189
0
                {
190
                    // apply to polygon
191
0
                    basegfx::B2DPolyPolygon aPolyPolygon(mpPolygons[b]);
192
0
                    aPolyPolygon.transform(aMatrixTransform);
193
194
0
                    std::unique_ptr<sdr::overlay::OverlayPolyPolygonStripedAndFilled> pNew(new sdr::overlay::OverlayPolyPolygonStripedAndFilled(
195
0
                        std::move(aPolyPolygon)));
196
0
                    xTargetOverlay->add(*pNew);
197
0
                    maObjects.append(std::move(pNew));
198
0
                }
199
0
            }
200
0
        }
201
0
    }
202
0
}
203
204
E3dView::E3dView(
205
    SdrModel& rSdrModel,
206
    OutputDevice* pOut)
207
64.2k
:   SdrView(rSdrModel, pOut)
208
64.2k
{
209
64.2k
    InitView();
210
64.2k
}
211
212
// DrawMarkedObj override, since possibly only a single 3D object is to be
213
// drawn
214
215
void E3dView::DrawMarkedObj(OutputDevice& rOut) const
216
0
{
217
    // Does 3D objects exist which scenes are not selected?
218
0
    bool bSpecialHandling = false;
219
0
    E3dScene *pScene = nullptr;
220
221
0
    const SdrMarkList& rMarkList = GetMarkedObjectList();
222
0
    const size_t nCnt = rMarkList.GetMarkCount();
223
0
    for(size_t nObjs = 0; nObjs < nCnt; ++nObjs)
224
0
    {
225
0
        SdrObject *pObj = rMarkList.GetMark(nObjs)->GetMarkedSdrObj();
226
0
        if(auto pCompoundObject = dynamic_cast<E3dCompoundObject*>(pObj))
227
0
        {
228
            // related scene
229
0
            pScene = pCompoundObject->getRootE3dSceneFromE3dObject();
230
231
0
            if(nullptr != pScene && !IsObjMarked(pScene))
232
0
            {
233
0
                bSpecialHandling = true;
234
0
            }
235
0
        }
236
        // Reset all selection flags
237
0
        if(auto p3dObject = DynCastE3dObject(pObj))
238
0
        {
239
0
            pScene = p3dObject->getRootE3dSceneFromE3dObject();
240
241
0
            if(nullptr != pScene)
242
0
            {
243
0
                pScene->SetSelected(false);
244
0
            }
245
0
        }
246
0
    }
247
248
0
    if(bSpecialHandling)
249
0
    {
250
        // Set selection flag to "not selected" for scenes related to all 3D
251
        // objects
252
0
        for(size_t nObjs = 0; nObjs < nCnt; ++nObjs)
253
0
        {
254
0
            SdrObject *pObj = rMarkList.GetMark(nObjs)->GetMarkedSdrObj();
255
0
            if(auto pCompoundObject = dynamic_cast<E3dCompoundObject*>(pObj))
256
0
            {
257
                // related scene
258
0
                pScene = pCompoundObject->getRootE3dSceneFromE3dObject();
259
260
0
                if(nullptr != pScene)
261
0
                {
262
0
                    pScene->SetSelected(false);
263
0
                }
264
0
            }
265
0
        }
266
267
0
        for(size_t nObjs = 0; nObjs < nCnt; ++nObjs)
268
0
        {
269
0
            SdrObject *pObj = rMarkList.GetMark(nObjs)->GetMarkedSdrObj();
270
0
            if(auto p3DObj = DynCastE3dObject(pObj))
271
0
            {
272
                // Select object
273
0
                p3DObj->SetSelected(true);
274
0
                pScene = p3DObj->getRootE3dSceneFromE3dObject();
275
0
            }
276
0
        }
277
278
0
        if(nullptr != pScene)
279
0
        {
280
            // code from parent
281
0
            rMarkList.ForceSort();
282
283
0
            pScene->SetDrawOnlySelected(true);
284
0
            pScene->SingleObjectPainter(rOut);
285
0
            pScene->SetDrawOnlySelected(false);
286
0
        }
287
288
        // Reset selection flag
289
0
        for(size_t nObjs = 0; nObjs < nCnt; ++nObjs)
290
0
        {
291
0
            SdrObject *pObj = rMarkList.GetMark(nObjs)->GetMarkedSdrObj();
292
0
            if(auto pCompoundObject = dynamic_cast<E3dCompoundObject*>(pObj))
293
0
            {
294
                // related scene
295
0
                pScene = pCompoundObject->getRootE3dSceneFromE3dObject();
296
297
0
                if(nullptr != pScene)
298
0
                {
299
0
                    pScene->SetSelected(false);
300
0
                }
301
0
            }
302
0
        }
303
0
    }
304
0
    else
305
0
    {
306
        // call parent
307
0
        SdrExchangeView::DrawMarkedObj(rOut);
308
0
    }
309
0
}
310
311
// override get model, since in some 3D objects an additional scene
312
// must be pushed in
313
314
std::unique_ptr<SdrModel> E3dView::CreateMarkedObjModel() const
315
0
{
316
    // Does 3D objects exist which scenes are not selected?
317
0
    bool bSpecialHandling(false);
318
0
    const SdrMarkList& rMarkList = GetMarkedObjectList();
319
0
    const size_t nCount(rMarkList.GetMarkCount());
320
0
    E3dScene *pScene = nullptr;
321
322
0
    for(size_t nObjs = 0; nObjs < nCount; ++nObjs)
323
0
    {
324
0
        const SdrObject* pObj = rMarkList.GetMark(nObjs)->GetMarkedSdrObj();
325
326
0
        if(!bSpecialHandling)
327
0
            if(auto pCompoundObj = dynamic_cast< const E3dCompoundObject*>(pObj))
328
0
            {
329
                // if the object is selected, but it's scene not,
330
                // we need special handling
331
0
                pScene = pCompoundObj->getRootE3dSceneFromE3dObject();
332
333
0
                if(nullptr != pScene && !IsObjMarked(pScene))
334
0
                {
335
0
                    bSpecialHandling = true;
336
0
                }
337
0
            }
338
339
0
        if(auto p3dObject = DynCastE3dObject(pObj))
340
0
        {
341
            // reset all selection flags at 3D objects
342
0
            pScene = p3dObject->getRootE3dSceneFromE3dObject();
343
344
0
            if(nullptr != pScene)
345
0
            {
346
0
                pScene->SetSelected(false);
347
0
            }
348
0
        }
349
0
    }
350
351
0
    if(!bSpecialHandling)
352
0
    {
353
        // call parent
354
0
        return SdrView::CreateMarkedObjModel();
355
0
    }
356
357
0
    std::unique_ptr<SdrModel> pNewModel;
358
0
    tools::Rectangle aSelectedSnapRect;
359
360
    // set 3d selection flags at all directly selected objects
361
    // and collect SnapRect of selected objects
362
0
    for(size_t nObjs = 0; nObjs < nCount; ++nObjs)
363
0
    {
364
0
        SdrObject *pObj = rMarkList.GetMark(nObjs)->GetMarkedSdrObj();
365
366
0
        if(auto p3DObj = dynamic_cast<E3dCompoundObject*>(pObj))
367
0
        {
368
            // mark object, but not scenes
369
0
            p3DObj->SetSelected(true);
370
0
            aSelectedSnapRect.Union(p3DObj->GetSnapRect());
371
0
        }
372
0
    }
373
374
    // create new mark list which contains all indirectly selected3d
375
    // scenes as selected objects
376
0
    SdrMarkList aOldML(rMarkList);
377
0
    SdrMarkList aNewML;
378
0
    SdrMarkList& rCurrentMarkList = const_cast<E3dView*>(this)->GetMarkedObjectListWriteAccess();
379
0
    rCurrentMarkList = aNewML;
380
381
0
    for(size_t nObjs = 0; nObjs < nCount; ++nObjs)
382
0
    {
383
0
        SdrObject *pObj = aOldML.GetMark(nObjs)->GetMarkedSdrObj();
384
385
0
        if(auto p3dObject = DynCastE3dObject(pObj))
386
0
        {
387
0
            pScene = p3dObject->getRootE3dSceneFromE3dObject();
388
389
0
            if(nullptr != pScene && !IsObjMarked(pScene) && GetSdrPageView())
390
0
            {
391
0
                const_cast<E3dView*>(this)->MarkObj(pScene, GetSdrPageView(), false, true);
392
0
            }
393
0
        }
394
0
    }
395
396
    // call parent. This will copy all scenes and the selection flags at the 3D objects. So
397
    // it will be possible to delete all non-selected 3d objects from the cloned 3d scenes
398
0
    pNewModel = SdrView::CreateMarkedObjModel();
399
400
0
    if(pNewModel)
401
0
    {
402
0
        for(sal_uInt16 nPg(0); nPg < pNewModel->GetPageCount(); nPg++)
403
0
        {
404
0
            const SdrPage* pSrcPg=pNewModel->GetPage(nPg);
405
406
0
            for (const rtl::Reference<SdrObject>& pSrcOb : *pSrcPg)
407
0
            {
408
0
                if(const E3dScene* p3dscene = DynCastE3dScene( pSrcOb.get()))
409
0
                {
410
0
                    pScene = const_cast<E3dScene*>(p3dscene);
411
412
                    // delete all not intentionally cloned 3d objects
413
0
                    pScene->removeAllNonSelectedObjects();
414
415
                    // reset select flags and set SnapRect of all selected objects
416
0
                    pScene->SetSelected(false);
417
0
                    pScene->SetSnapRect(aSelectedSnapRect);
418
0
                }
419
0
            }
420
0
        }
421
0
    }
422
423
    // restore old selection
424
0
    rCurrentMarkList = aOldML;
425
426
0
    return pNewModel;
427
0
}
428
429
// When pasting objects have to integrated if a scene is inserted, but
430
// not the scene itself
431
432
bool E3dView::Paste(
433
    const SdrModel& rMod, const Point& rPos, SdrObjList* pLst, SdrInsertFlags nOptions)
434
0
{
435
0
    bool bRetval = false;
436
437
    // Get list
438
0
    Point aPos(rPos);
439
0
    SdrObjList* pDstList = pLst;
440
0
    ImpGetPasteObjList(aPos, pDstList);
441
442
0
    if(!pDstList)
443
0
        return false;
444
445
    // Get owner of the list
446
0
    E3dScene* pDstScene(DynCastE3dScene(pDstList->getSdrObjectFromSdrObjList()));
447
448
0
    if(nullptr != pDstScene)
449
0
    {
450
0
        BegUndo(SvxResId(RID_SVX_3D_UNDO_EXCHANGE_PASTE));
451
452
        // Copy all objects from E3dScenes and insert them directly
453
0
        for(sal_uInt16 nPg(0); nPg < rMod.GetPageCount(); nPg++)
454
0
        {
455
0
            const SdrPage* pSrcPg=rMod.GetPage(nPg);
456
457
            // calculate offset for paste
458
0
            tools::Rectangle aR = pSrcPg->GetAllObjBoundRect();
459
0
            Point aDist(aPos - aR.Center());
460
461
            // Insert sub-objects for scenes
462
0
            for (const rtl::Reference<SdrObject>& pSrcOb : *pSrcPg)
463
0
            {
464
0
                if(const E3dScene* p3dscene = DynCastE3dScene(pSrcOb.get()))
465
0
                {
466
0
                    E3dScene* pSrcScene = const_cast<E3dScene*>(p3dscene);
467
0
                    ImpCloneAll3DObjectsToDestScene(pSrcScene, pDstScene, aDist);
468
0
                }
469
0
            }
470
0
        }
471
0
        EndUndo();
472
0
    }
473
0
    else
474
0
    {
475
        // call parent
476
0
        bRetval = SdrView::Paste(rMod, rPos, pLst, nOptions);
477
0
    }
478
479
0
    return bRetval;
480
0
}
481
482
// Service routine used from local Clone() and from SdrCreateView::EndCreateObj(...)
483
bool E3dView::ImpCloneAll3DObjectsToDestScene(E3dScene const * pSrcScene, E3dScene* pDstScene, Point /*aOffset*/)
484
0
{
485
0
    bool bRetval(false);
486
487
0
    if(pSrcScene && pDstScene)
488
0
    {
489
0
        for (const rtl::Reference<SdrObject>& pObj : *pSrcScene->GetSubList())
490
0
        {
491
0
            E3dCompoundObject* pCompoundObj = dynamic_cast< E3dCompoundObject* >(pObj.get());
492
493
0
            if(pCompoundObj)
494
0
            {
495
0
                rtl::Reference<E3dCompoundObject> pNewCompoundObj = SdrObject::Clone(*pCompoundObj, pDstScene->getSdrModelFromSdrObject());
496
497
0
                if(pNewCompoundObj)
498
0
                {
499
                    // get dest scene's current range in 3D world coordinates
500
0
                    const basegfx::B3DHomMatrix aSceneToWorldTrans(pDstScene->GetFullTransform());
501
0
                    basegfx::B3DRange aSceneRange(pDstScene->GetBoundVolume());
502
0
                    aSceneRange.transform(aSceneToWorldTrans);
503
504
                    // get new object's implied object transformation
505
0
                    const basegfx::B3DHomMatrix aNewObjectTrans(pNewCompoundObj->GetTransform());
506
507
                    // get new object's range in 3D world coordinates in dest scene
508
                    // as if it were already added
509
0
                    const basegfx::B3DHomMatrix aObjectToWorldTrans(aSceneToWorldTrans * aNewObjectTrans);
510
0
                    basegfx::B3DRange aObjectRange(pNewCompoundObj->GetBoundVolume());
511
0
                    aObjectRange.transform(aObjectToWorldTrans);
512
513
                    // get scale adaptation
514
0
                    const basegfx::B3DVector aSceneScale(aSceneRange.getRange());
515
0
                    const basegfx::B3DVector aObjectScale(aObjectRange.getRange());
516
0
                    double fScale(1.0);
517
518
                    // if new object's size in X,Y or Z is bigger that 80% of dest scene, adapt scale
519
                    // to not change the scene by the inserted object
520
0
                    const double fSizeFactor(0.5);
521
522
0
                    if(aObjectScale.getX() * fScale > aSceneScale.getX() * fSizeFactor)
523
0
                    {
524
0
                        const double fObjSize(aObjectScale.getX() * fScale);
525
0
                        const double fFactor((aSceneScale.getX() * fSizeFactor) / (basegfx::fTools::equalZero(fObjSize) ? 1.0 : fObjSize));
526
0
                        fScale *= fFactor;
527
0
                    }
528
529
0
                    if(aObjectScale.getY() * fScale > aSceneScale.getY() * fSizeFactor)
530
0
                    {
531
0
                        const double fObjSize(aObjectScale.getY() * fScale);
532
0
                        const double fFactor((aSceneScale.getY() * fSizeFactor) / (basegfx::fTools::equalZero(fObjSize) ? 1.0 : fObjSize));
533
0
                        fScale *= fFactor;
534
0
                    }
535
536
0
                    if(aObjectScale.getZ() * fScale > aSceneScale.getZ() * fSizeFactor)
537
0
                    {
538
0
                        const double fObjSize(aObjectScale.getZ() * fScale);
539
0
                        const double fFactor((aSceneScale.getZ() * fSizeFactor) / (basegfx::fTools::equalZero(fObjSize) ? 1.0 : fObjSize));
540
0
                        fScale *= fFactor;
541
0
                    }
542
543
                    // get translation adaptation
544
0
                    const basegfx::B3DPoint aSceneCenter(aSceneRange.getCenter());
545
0
                    const basegfx::B3DPoint aObjectCenter(aObjectRange.getCenter());
546
547
                    // build full modification transform. The object's transformation
548
                    // shall be modified, so start at object coordinates; transform to 3d world coor
549
0
                    basegfx::B3DHomMatrix aModifyingTransform(aObjectToWorldTrans);
550
551
                    // translate to absolute center in 3d world coor
552
0
                    aModifyingTransform.translate(-aObjectCenter.getX(), -aObjectCenter.getY(), -aObjectCenter.getZ());
553
554
                    // scale to dest size in 3d world coor
555
0
                    aModifyingTransform.scale(fScale, fScale, fScale);
556
557
                    // translate to dest scene center in 3d world coor
558
0
                    aModifyingTransform.translate(aSceneCenter.getX(), aSceneCenter.getY(), aSceneCenter.getZ());
559
560
                    // transform from 3d world to dest object coordinates
561
0
                    basegfx::B3DHomMatrix aWorldToObject(aObjectToWorldTrans);
562
0
                    aWorldToObject.invert();
563
0
                    aModifyingTransform = aWorldToObject * aModifyingTransform;
564
565
                    // correct implied object transform by applying changing one in object coor
566
0
                    pNewCompoundObj->SetTransform(aModifyingTransform * aNewObjectTrans);
567
568
                    // fill and insert new object
569
0
                    pNewCompoundObj->NbcSetLayer(pCompoundObj->GetLayer());
570
0
                    pNewCompoundObj->NbcSetStyleSheet(pCompoundObj->GetStyleSheet(), true);
571
0
                    pDstScene->InsertObject(pNewCompoundObj.get());
572
0
                    bRetval = true;
573
574
                    // Create undo
575
0
                    if( GetModel().IsUndoEnabled() )
576
0
                        AddUndo(GetModel().GetSdrUndoFactory().CreateUndoNewObject(*pNewCompoundObj));
577
0
                }
578
0
            }
579
0
        }
580
0
    }
581
582
0
    return bRetval;
583
0
}
584
585
bool E3dView::IsConvertTo3DObjPossible() const
586
0
{
587
0
    bool bAny3D(false);
588
0
    bool bGroupSelected(false);
589
0
    bool bRetval(true);
590
591
0
    const SdrMarkList& rMarkList = GetMarkedObjectList();
592
0
    for(size_t a=0; !bAny3D && a<rMarkList.GetMarkCount(); ++a)
593
0
    {
594
0
        SdrObject *pObj = rMarkList.GetMark(a)->GetMarkedSdrObj();
595
0
        if(pObj)
596
0
        {
597
0
            ImpIsConvertTo3DPossible(pObj, bAny3D, bGroupSelected);
598
0
        }
599
0
    }
600
601
0
    bRetval = !bAny3D
602
0
        && (
603
0
           IsConvertToPolyObjPossible()
604
0
        || IsConvertToPathObjPossible()
605
0
        || IsImportMtfPossible());
606
0
    return bRetval;
607
0
}
608
609
void E3dView::ImpIsConvertTo3DPossible(SdrObject const * pObj, bool& rAny3D,
610
    bool& rGroupSelected) const
611
0
{
612
0
    if(!pObj)
613
0
        return;
614
615
0
    if(DynCastE3dObject(pObj))
616
0
    {
617
0
        rAny3D = true;
618
0
    }
619
0
    else
620
0
    {
621
0
        if(pObj->IsGroupObject())
622
0
        {
623
0
            SdrObjListIter aIter(*pObj, SdrIterMode::DeepNoGroups);
624
0
            while(aIter.IsMore())
625
0
            {
626
0
                SdrObject* pNewObj = aIter.Next();
627
0
                ImpIsConvertTo3DPossible(pNewObj, rAny3D, rGroupSelected);
628
0
            }
629
0
            rGroupSelected = true;
630
0
        }
631
0
    }
632
0
}
633
634
void E3dView::ImpChangeSomeAttributesFor3DConversion(SdrObject* pObj)
635
0
{
636
0
    if(DynCastSdrTextObj( pObj) ==  nullptr)
637
0
        return;
638
639
0
    const SfxItemSet& rSet = pObj->GetMergedItemSet();
640
0
    const SvxColorItem& rTextColorItem = rSet.Get(EE_CHAR_COLOR);
641
0
    if(rTextColorItem.GetValue() != COL_BLACK)
642
0
        return;
643
644
    //For black text objects, the color set to gray
645
0
    if(pObj->getSdrPageFromSdrObject())
646
0
    {
647
        // if black is only default attribute from
648
        // pattern set it hard so that it is used in undo.
649
0
        pObj->SetMergedItem(SvxColorItem(COL_BLACK, EE_CHAR_COLOR));
650
651
        // add undo now
652
0
        if (GetModel().IsUndoEnabled())
653
0
            AddUndo(GetModel().GetSdrUndoFactory().CreateUndoAttrObject(*pObj));
654
0
    }
655
656
0
    pObj->SetMergedItem(SvxColorItem(COL_GRAY, EE_CHAR_COLOR));
657
0
}
658
659
void E3dView::ImpChangeSomeAttributesFor3DConversion2(SdrObject* pObj)
660
0
{
661
0
    auto pPathObj = dynamic_cast<const SdrPathObj*>( pObj);
662
0
    if(!pPathObj)
663
0
        return;
664
665
0
    const SfxItemSet& rSet = pObj->GetMergedItemSet();
666
0
    sal_Int32 nLineWidth = rSet.Get(XATTR_LINEWIDTH).GetValue();
667
0
    drawing::LineStyle eLineStyle = rSet.Get(XATTR_LINESTYLE).GetValue();
668
0
    drawing::FillStyle eFillStyle = rSet.Get(XATTR_FILLSTYLE).GetValue();
669
670
0
    if(pPathObj->IsClosed()
671
0
        && eLineStyle == drawing::LineStyle_SOLID
672
0
        && !nLineWidth
673
0
        && eFillStyle != drawing::FillStyle_NONE)
674
0
    {
675
0
        if (pObj->getSdrPageFromSdrObject() && GetModel().IsUndoEnabled())
676
0
        {
677
0
            AddUndo(GetModel().GetSdrUndoFactory().CreateUndoAttrObject(*pObj));
678
0
        }
679
680
0
        pObj->SetMergedItem(XLineStyleItem(drawing::LineStyle_NONE));
681
0
        pObj->SetMergedItem(XLineWidthItem(0));
682
0
    }
683
0
}
684
685
void E3dView::ImpCreateSingle3DObjectFlat(E3dScene* pScene, SdrObject* pObj, bool bExtrude, double fDepth, basegfx::B2DHomMatrix const & rLatheMat)
686
0
{
687
    // Single PathObject, transform this
688
0
    SdrPathObj* pPath = dynamic_cast<SdrPathObj*>( pObj );
689
690
0
    if(!pPath)
691
0
        return;
692
693
0
    E3dDefaultAttributes aDefault = Get3DDefaultAttributes();
694
695
0
    if(bExtrude)
696
0
    {
697
0
        aDefault.SetDefaultExtrudeCharacterMode(true);
698
0
    }
699
0
    else
700
0
    {
701
0
        aDefault.SetDefaultLatheCharacterMode(true);
702
0
    }
703
704
    // Get Itemset of the original object
705
0
    SfxItemSet aSet(pObj->GetMergedItemSet());
706
707
0
    drawing::FillStyle eFillStyle = aSet.Get(XATTR_FILLSTYLE).GetValue();
708
709
    // line style turned off
710
0
    aSet.Put(XLineStyleItem(drawing::LineStyle_NONE));
711
712
    //Determining if FILL_Attribute is set.
713
0
    if(!pPath->IsClosed() || eFillStyle == drawing::FillStyle_NONE)
714
0
    {
715
        // This SdrPathObj is not filled, leave the front and rear face out.
716
        // Moreover, a two-sided representation necessary.
717
0
        aDefault.SetDefaultExtrudeCloseFront(false);
718
0
        aDefault.SetDefaultExtrudeCloseBack(false);
719
720
0
        aSet.Put(makeSvx3DDoubleSidedItem(true));
721
722
        // Set fill attribute
723
0
        aSet.Put(XFillStyleItem(drawing::FillStyle_SOLID));
724
725
        // Fill color must be the color line, because the object was
726
        // previously just a line
727
0
        Color aColorLine = aSet.Get(XATTR_LINECOLOR).GetColorValue();
728
0
        aSet.Put(XFillColorItem(OUString(), aColorLine));
729
0
    }
730
731
    // Create a new extrude object
732
0
    rtl::Reference<E3dObject> p3DObj;
733
0
    if(bExtrude)
734
0
    {
735
0
        p3DObj = new E3dExtrudeObj(pObj->getSdrModelFromSdrObject(), aDefault, pPath->GetPathPoly(), fDepth);
736
0
    }
737
0
    else
738
0
    {
739
        // rLatheMat expects coordinates with y-axis up, pPath uses y-axis down
740
0
        basegfx::B2DHomMatrix aFlipVerticalMat(1.0, 0.0, 0.0, 0.0, -1.0, 0.0);
741
0
        basegfx::B2DPolyPolygon aPolyPoly2D(pPath->GetPathPoly());
742
0
        aPolyPoly2D.transform(aFlipVerticalMat);
743
0
        aPolyPoly2D.transform(rLatheMat);
744
        // ctor E3dLatheObj expects coordinates with y-axis down
745
0
        aPolyPoly2D.transform(aFlipVerticalMat);
746
0
        p3DObj = new E3dLatheObj(pObj->getSdrModelFromSdrObject(), aDefault, std::move(aPolyPoly2D));
747
0
    }
748
749
    // Set attribute
750
0
    p3DObj->NbcSetLayer(pObj->GetLayer());
751
752
0
    p3DObj->SetMergedItemSet(aSet);
753
754
0
    p3DObj->NbcSetStyleSheet(pObj->GetStyleSheet(), true);
755
756
    // Insert a new extrude object
757
0
    pScene->InsertObject(p3DObj.get());
758
0
}
759
760
void E3dView::ImpCreate3DObject(E3dScene* pScene, SdrObject* pObj, bool bExtrude, double fDepth, basegfx::B2DHomMatrix const & rLatheMat)
761
0
{
762
0
    if(!pObj)
763
0
        return;
764
765
    // change text color attribute for not so dark colors
766
0
    if(pObj->IsGroupObject())
767
0
    {
768
0
        SdrObjListIter aIter(*pObj, SdrIterMode::DeepWithGroups);
769
0
        while(aIter.IsMore())
770
0
        {
771
0
            SdrObject* pGroupMember = aIter.Next();
772
0
            ImpChangeSomeAttributesFor3DConversion(pGroupMember);
773
0
        }
774
0
    }
775
0
    else
776
0
        ImpChangeSomeAttributesFor3DConversion(pObj);
777
778
    // convert completely to path objects
779
0
    rtl::Reference<SdrObject> pNewObj1 = pObj->ConvertToPolyObj(false, false);
780
781
0
    if(!pNewObj1)
782
0
        return;
783
784
    // change text color attribute for not so dark colors
785
0
    if(pNewObj1->IsGroupObject())
786
0
    {
787
0
        SdrObjListIter aIter(*pNewObj1, SdrIterMode::DeepWithGroups);
788
0
        while(aIter.IsMore())
789
0
        {
790
0
            SdrObject* pGroupMember = aIter.Next();
791
0
            ImpChangeSomeAttributesFor3DConversion2(pGroupMember);
792
0
        }
793
0
    }
794
0
    else
795
0
        ImpChangeSomeAttributesFor3DConversion2(pNewObj1.get());
796
797
    // convert completely to path objects
798
0
    rtl::Reference<SdrObject> pNewObj2 = pObj->ConvertToContourObj(pNewObj1.get(), true);
799
800
0
    if(pNewObj2)
801
0
    {
802
        // add all to flat scene
803
0
        if(pNewObj2->IsGroupObject())
804
0
        {
805
0
            SdrObjListIter aIter(*pNewObj2, SdrIterMode::DeepWithGroups);
806
0
            while(aIter.IsMore())
807
0
            {
808
0
                SdrObject* pGroupMember = aIter.Next();
809
0
                ImpCreateSingle3DObjectFlat(pScene, pGroupMember, bExtrude, fDepth, rLatheMat);
810
0
            }
811
0
        }
812
0
        else
813
0
            ImpCreateSingle3DObjectFlat(pScene, pNewObj2.get(), bExtrude, fDepth, rLatheMat);
814
0
    }
815
0
}
816
817
void E3dView::ConvertMarkedObjTo3D(bool bExtrude, const basegfx::B2DPoint& rPnt1, const basegfx::B2DPoint& rPnt2)
818
0
{
819
0
    const SdrMarkList& rMarkList = GetMarkedObjectList();
820
0
    if(rMarkList.GetMarkCount() == 0)
821
0
        return;
822
823
    // Create undo
824
0
    if(bExtrude)
825
0
        BegUndo(SvxResId(RID_SVX_3D_UNDO_EXTRUDE));
826
0
    else
827
0
        BegUndo(SvxResId(RID_SVX_3D_UNDO_LATHE));
828
829
0
    SdrModel& rSdrModel(rMarkList.GetMark(0)->GetMarkedSdrObj()->getSdrModelFromSdrObject());
830
831
    // Create a new scene for the created 3D object
832
0
    rtl::Reference<E3dScene> pScene = new E3dScene(rSdrModel);
833
834
    // Determine rectangle and possibly correct it
835
0
    tools::Rectangle aRect = GetAllMarkedRect();
836
0
    if(aRect.GetWidth() <= 1)
837
0
        aRect.SetSize(Size(500, aRect.GetHeight()));
838
0
    if(aRect.GetHeight() <= 1)
839
0
        aRect.SetSize(Size(aRect.GetWidth(), 500));
840
841
    // Determine the depth relative to the size of the selection
842
0
    double fDepth = 0.0;
843
0
    double fRot3D = 0.0;
844
0
    basegfx::B2DHomMatrix aLatheMat;
845
846
0
    if(bExtrude)
847
0
    {
848
0
        fDepth = std::hypot(aRect.GetWidth(), aRect.GetHeight()) / 6.0;
849
0
    }
850
0
    if(!bExtrude)
851
0
    {
852
        // Create transformation for the polygons rotating body
853
0
        if (rPnt1 != rPnt2)
854
0
        {
855
            // Rotation around control point #1 with set angle
856
            // for 3D coordinates
857
0
            basegfx::B2DPoint aDiff(rPnt1 - rPnt2);
858
0
            fRot3D = atan2(aDiff.getY(), aDiff.getX()) - M_PI_2;
859
860
0
            if(basegfx::fTools::equalZero(fabs(fRot3D)))
861
0
                fRot3D = 0.0;
862
863
0
            if(fRot3D != 0.0)
864
0
            {
865
0
                aLatheMat = basegfx::utils::createRotateAroundPoint(rPnt2, -fRot3D)
866
0
                    * aLatheMat;
867
0
            }
868
0
        }
869
870
0
        if (rPnt2.getX() != 0.0)
871
0
        {
872
            // Translation to Y=0 - axis
873
0
            aLatheMat.translate(-rPnt2.getX(), 0.0);
874
0
        }
875
0
        else
876
0
        {
877
0
            aLatheMat.translate(static_cast<double>(-aRect.Left()), 0.0);
878
0
        }
879
880
        // Form the inverse matrix to determine the target expansion
881
0
        basegfx::B2DHomMatrix aInvLatheMat(aLatheMat);
882
0
        aInvLatheMat.invert();
883
884
        // SnapRect extension enables mirroring in the axis of rotation
885
0
        for(size_t a=0; a<rMarkList.GetMarkCount(); ++a)
886
0
        {
887
0
            SdrMark* pMark = rMarkList.GetMark(a);
888
0
            SdrObject* pObj = pMark->GetMarkedSdrObj();
889
0
            tools::Rectangle aTurnRect = pObj->GetSnapRect();
890
0
            basegfx::B2DPoint aRot;
891
0
            Point aRotPnt;
892
893
0
            aRot = basegfx::B2DPoint(aTurnRect.Left(), -aTurnRect.Top());
894
0
            aRot *= aLatheMat;
895
0
            aRot.setX(-aRot.getX());
896
0
            aRot *= aInvLatheMat;
897
0
            aRotPnt = Point(static_cast<tools::Long>(aRot.getX() + 0.5), static_cast<tools::Long>(-aRot.getY() - 0.5));
898
0
            aRect.Union(tools::Rectangle(aRotPnt, aRotPnt));
899
900
0
            aRot = basegfx::B2DPoint(aTurnRect.Left(), -aTurnRect.Bottom());
901
0
            aRot *= aLatheMat;
902
0
            aRot.setX(-aRot.getX());
903
0
            aRot *= aInvLatheMat;
904
0
            aRotPnt = Point(static_cast<tools::Long>(aRot.getX() + 0.5), static_cast<tools::Long>(-aRot.getY() - 0.5));
905
0
            aRect.Union(tools::Rectangle(aRotPnt, aRotPnt));
906
907
0
            aRot = basegfx::B2DPoint(aTurnRect.Right(), -aTurnRect.Top());
908
0
            aRot *= aLatheMat;
909
0
            aRot.setX(-aRot.getX());
910
0
            aRot *= aInvLatheMat;
911
0
            aRotPnt = Point(static_cast<tools::Long>(aRot.getX() + 0.5), static_cast<tools::Long>(-aRot.getY() - 0.5));
912
0
            aRect.Union(tools::Rectangle(aRotPnt, aRotPnt));
913
914
0
            aRot = basegfx::B2DPoint(aTurnRect.Right(), -aTurnRect.Bottom());
915
0
            aRot *= aLatheMat;
916
0
            aRot.setX(-aRot.getX());
917
0
            aRot *= aInvLatheMat;
918
0
            aRotPnt = Point(static_cast<tools::Long>(aRot.getX() + 0.5), static_cast<tools::Long>(-aRot.getY() - 0.5));
919
0
            aRect.Union(tools::Rectangle(aRotPnt, aRotPnt));
920
0
        }
921
0
    }
922
923
    // Walk through the selection and convert it into 3D, complete with
924
    // Conversion to SdrPathObject, also fonts
925
0
    for(size_t a=0; a<rMarkList.GetMarkCount(); ++a)
926
0
    {
927
0
        SdrMark* pMark = rMarkList.GetMark(a);
928
0
        SdrObject* pObj = pMark->GetMarkedSdrObj();
929
930
0
        ImpCreate3DObject(pScene.get(), pObj, bExtrude, fDepth, aLatheMat);
931
0
    }
932
933
0
    if(pScene->GetSubList() && pScene->GetSubList()->GetObjCount() != 0)
934
0
    {
935
        // Arrange all created objects by depth
936
0
        if(bExtrude)
937
0
            DoDepthArrange(pScene.get(), fDepth);
938
939
        // Center 3D objects in the middle of the overall rectangle
940
0
        basegfx::B3DPoint aCenter(pScene->GetBoundVolume().getCenter());
941
0
        basegfx::B3DHomMatrix aMatrix;
942
943
0
        aMatrix.translate(-aCenter.getX(), -aCenter.getY(), -aCenter.getZ());
944
0
        pScene->SetTransform(aMatrix * pScene->GetTransform());
945
946
        // Initialize scene
947
0
        pScene->NbcSetSnapRect(aRect);
948
0
        basegfx::B3DRange aBoundVol = pScene->GetBoundVolume();
949
0
        InitScene(pScene.get(), static_cast<double>(aRect.GetWidth()), static_cast<double>(aRect.GetHeight()), aBoundVol.getDepth());
950
951
        // Insert scene instead of the first selected object and throw away
952
        // all the old objects
953
0
        SdrMark* pMark = rMarkList.GetMark(0);
954
0
        if (pMark)
955
0
        {
956
0
            SdrObject* pRepObj = pMark->GetMarkedSdrObj();
957
0
            SdrPageView* pPV = pMark->GetPageView();
958
0
            MarkObj(pRepObj, pPV, true);
959
0
            ReplaceObjectAtView(pRepObj, *pPV, pScene.get(), false);
960
0
            DeleteMarked();
961
0
            MarkObj(pScene.get(), pPV);
962
0
        }
963
964
        // Rotate Rotation body around the axis of rotation
965
0
        if(!bExtrude && fRot3D != 0.0)
966
0
        {
967
0
            basegfx::B3DHomMatrix aRotate;
968
0
            aRotate.rotate(0.0, 0.0, fRot3D);
969
0
            pScene->SetTransform(aRotate * pScene->GetTransform());
970
0
        }
971
972
        // Set default rotation
973
0
        {
974
0
            basegfx::B3DHomMatrix aRotate;
975
0
            aRotate.rotate(basegfx::deg2rad(20.0), 0.0, 0.0);
976
            // E3DModifySceneSnapRectUpdater updates the 2D representation of the scene.
977
            // It prepares things in ctor and acts in dtor.
978
0
            E3DModifySceneSnapRectUpdater aUpdater(pScene->getSdrObjectFromSdrObjList());
979
0
            pScene->SetTransform(aRotate * pScene->GetTransform());
980
0
        }
981
0
    }
982
0
    else
983
0
        pScene.clear();
984
985
0
    EndUndo();
986
0
}
987
988
//Arrange all created extrude objects by depth
989
990
namespace {
991
992
struct E3dDepthNeighbour
993
{
994
    E3dExtrudeObj*              mpObj;
995
    basegfx::B2DPolyPolygon     maPreparedPolyPolygon;
996
997
    E3dDepthNeighbour(E3dExtrudeObj* pObj, basegfx::B2DPolyPolygon aPreparedPolyPolygon)
998
0
    :   mpObj(pObj),
999
0
        maPreparedPolyPolygon(std::move(aPreparedPolyPolygon))
1000
0
    {
1001
0
    }
1002
};
1003
1004
struct E3dDepthLayer
1005
{
1006
    E3dDepthLayer*              mpDown;
1007
    std::vector<E3dDepthNeighbour> mvNeighbours;
1008
1009
    E3dDepthLayer()
1010
0
    :   mpDown(nullptr)
1011
0
    {
1012
0
    }
1013
};
1014
1015
}
1016
1017
void E3dView::DoDepthArrange(E3dScene const * pScene, double fDepth)
1018
0
{
1019
0
    if(!(pScene && pScene->GetSubList() && pScene->GetSubList()->GetObjCount() > 1))
1020
0
        return;
1021
1022
0
    SdrObjList* pSubList = pScene->GetSubList();
1023
0
    SdrObjListIter aIter(pSubList, SdrIterMode::Flat);
1024
0
    E3dDepthLayer* pBaseLayer = nullptr;
1025
0
    E3dDepthLayer* pLayer = nullptr;
1026
0
    sal_Int32 nNumLayers = 0;
1027
1028
0
    while(aIter.IsMore())
1029
0
    {
1030
0
        E3dExtrudeObj* pExtrudeObj = dynamic_cast< E3dExtrudeObj* >(aIter.Next());
1031
1032
0
        if(pExtrudeObj)
1033
0
        {
1034
0
            const basegfx::B2DPolyPolygon aExtrudePoly(
1035
0
                basegfx::utils::prepareForPolygonOperation(pExtrudeObj->GetExtrudePolygon()));
1036
0
            const SfxItemSet& rLocalSet = pExtrudeObj->GetMergedItemSet();
1037
0
            const drawing::FillStyle eLocalFillStyle = rLocalSet.Get(XATTR_FILLSTYLE).GetValue();
1038
0
            const Color aLocalColor = rLocalSet.Get(XATTR_FILLCOLOR).GetColorValue();
1039
1040
            // sort in ExtrudeObj
1041
0
            if(pLayer)
1042
0
            {
1043
                // do we have overlap with an object of this layer?
1044
0
                bool bOverlap(false);
1045
1046
0
                for(const auto& rAct : pLayer->mvNeighbours)
1047
0
                {
1048
                    // do rAct.mpObj and pExtrudeObj overlap? Check by
1049
                    // using logical AND clipping
1050
0
                    const basegfx::B2DPolyPolygon aAndPolyPolygon(
1051
0
                        basegfx::utils::solvePolygonOperationAnd(
1052
0
                            aExtrudePoly,
1053
0
                            rAct.maPreparedPolyPolygon));
1054
1055
0
                    if(aAndPolyPolygon.count() != 0)
1056
0
                    {
1057
                        // second criteria: is another fillstyle or color used?
1058
0
                        const SfxItemSet& rCompareSet = rAct.mpObj->GetMergedItemSet();
1059
1060
0
                        drawing::FillStyle eCompareFillStyle = rCompareSet.Get(XATTR_FILLSTYLE).GetValue();
1061
1062
0
                        if(eLocalFillStyle == eCompareFillStyle)
1063
0
                        {
1064
0
                            if(eLocalFillStyle == drawing::FillStyle_SOLID)
1065
0
                            {
1066
0
                                Color aCompareColor = rCompareSet.Get(XATTR_FILLCOLOR).GetColorValue();
1067
1068
0
                                if(aCompareColor == aLocalColor)
1069
0
                                {
1070
0
                                    continue;
1071
0
                                }
1072
0
                            }
1073
0
                            else if(eLocalFillStyle == drawing::FillStyle_NONE)
1074
0
                            {
1075
0
                                continue;
1076
0
                            }
1077
0
                        }
1078
1079
0
                        bOverlap = true;
1080
0
                        break;
1081
0
                    }
1082
0
                }
1083
1084
0
                if(bOverlap)
1085
0
                {
1086
                    // yes, start a new layer
1087
0
                    pLayer->mpDown = new E3dDepthLayer;
1088
0
                    pLayer = pLayer->mpDown;
1089
0
                    nNumLayers++;
1090
0
                    pLayer->mvNeighbours.emplace_back(pExtrudeObj, aExtrudePoly);
1091
0
                }
1092
0
                else
1093
0
                {
1094
                    // no, add to current layer
1095
0
                    pLayer->mvNeighbours.emplace(pLayer->mvNeighbours.begin(), pExtrudeObj, aExtrudePoly);
1096
0
                }
1097
0
            }
1098
0
            else
1099
0
            {
1100
                // first layer ever
1101
0
                pBaseLayer = new E3dDepthLayer;
1102
0
                pLayer = pBaseLayer;
1103
0
                nNumLayers++;
1104
0
                pLayer->mvNeighbours.emplace_back(pExtrudeObj, aExtrudePoly);
1105
0
            }
1106
0
        }
1107
0
    }
1108
1109
    // number of layers is done
1110
0
    if(nNumLayers > 1)
1111
0
    {
1112
        // need to be arranged
1113
0
        double fMinDepth = fDepth * 0.8;
1114
0
        double fStep = (fDepth - fMinDepth) / static_cast<double>(nNumLayers);
1115
0
        pLayer = pBaseLayer;
1116
1117
0
        while(pLayer)
1118
0
        {
1119
            // move along layer
1120
0
            for(auto& rAct : pLayer->mvNeighbours)
1121
0
            {
1122
                // adapt extrude value
1123
0
                rAct.mpObj->SetMergedItem(SfxUInt32Item(SDRATTR_3DOBJ_DEPTH, sal_uInt32(fMinDepth + 0.5)));
1124
0
            }
1125
1126
            // next layer
1127
0
            pLayer = pLayer->mpDown;
1128
0
            fMinDepth += fStep;
1129
0
        }
1130
0
    }
1131
1132
    // cleanup
1133
0
    while(pBaseLayer)
1134
0
    {
1135
0
        pLayer = pBaseLayer->mpDown;
1136
0
        delete pBaseLayer;
1137
0
        pBaseLayer = pLayer;
1138
0
    }
1139
0
}
1140
1141
// Start drag, create for 3D objects before possibly drag method
1142
1143
bool E3dView::BegDragObj(const Point& rPnt, OutputDevice* pOut,
1144
    SdrHdl* pHdl, short nMinMov,
1145
    SdrDragMethod* pForcedMeth)
1146
0
{
1147
0
    const SdrMarkList& rMarkList = GetMarkedObjectList();
1148
0
    if(Is3DRotationCreationActive() && rMarkList.GetMarkCount())
1149
0
    {
1150
        // Determine all selected polygons and return the mirrored helper overlay
1151
0
        mpMirrorOverlay->SetMirrorAxis(maRef1, maRef2);
1152
0
    }
1153
0
    else
1154
0
    {
1155
0
        bool bOwnActionNecessary;
1156
0
        if (pHdl == nullptr)
1157
0
        {
1158
0
           bOwnActionNecessary = true;
1159
0
        }
1160
0
        else if (pHdl->IsVertexHdl() || pHdl->IsCornerHdl())
1161
0
        {
1162
0
           bOwnActionNecessary = true;
1163
0
        }
1164
0
        else
1165
0
        {
1166
0
           bOwnActionNecessary = false;
1167
0
        }
1168
1169
0
        if(bOwnActionNecessary && rMarkList.GetMarkCount() > 0)
1170
0
        {
1171
0
            E3dDragConstraint eConstraint = E3dDragConstraint::XYZ;
1172
0
            bool bThereAreRootScenes = false;
1173
0
            bool bThereAre3DObjects = false;
1174
0
            const size_t nCnt = rMarkList.GetMarkCount();
1175
0
            for(size_t nObjs = 0; nObjs < nCnt; ++nObjs)
1176
0
            {
1177
0
                SdrObject *pObj = rMarkList.GetMark(nObjs)->GetMarkedSdrObj();
1178
0
                if(pObj)
1179
0
                {
1180
0
                    if( const E3dScene* pScene = DynCastE3dScene(pObj) )
1181
0
                        if( pScene->getRootE3dSceneFromE3dObject() == pObj )
1182
0
                            bThereAreRootScenes = true;
1183
1184
0
                    if(DynCastE3dObject(pObj))
1185
0
                    {
1186
0
                        bThereAre3DObjects = true;
1187
0
                    }
1188
0
                }
1189
0
            }
1190
0
            if( bThereAre3DObjects )
1191
0
            {
1192
0
                meDragHdl = ( pHdl == nullptr ? SdrHdlKind::Move : pHdl->GetKind() );
1193
0
                switch ( meDragMode )
1194
0
                {
1195
0
                    case SdrDragMode::Rotate:
1196
0
                    case SdrDragMode::Shear:
1197
0
                    {
1198
0
                        switch ( meDragHdl )
1199
0
                        {
1200
0
                            case SdrHdlKind::Left:
1201
0
                            case SdrHdlKind::Right:
1202
0
                            {
1203
0
                                eConstraint = E3dDragConstraint::X;
1204
0
                            }
1205
0
                            break;
1206
1207
0
                            case SdrHdlKind::Upper:
1208
0
                            case SdrHdlKind::Lower:
1209
0
                            {
1210
0
                                eConstraint = E3dDragConstraint::Y;
1211
0
                            }
1212
0
                            break;
1213
1214
0
                            case SdrHdlKind::UpperLeft:
1215
0
                            case SdrHdlKind::UpperRight:
1216
0
                            case SdrHdlKind::LowerLeft:
1217
0
                            case SdrHdlKind::LowerRight:
1218
0
                            {
1219
0
                                eConstraint = E3dDragConstraint::Z;
1220
0
                            }
1221
0
                            break;
1222
0
                            default: break;
1223
0
                        }
1224
1225
                        // do not mask the allowed rotations
1226
0
                        eConstraint &= E3dDragConstraint::XYZ;
1227
0
                        pForcedMeth = new E3dDragRotate(*this, rMarkList, eConstraint, IsSolidDragging());
1228
0
                    }
1229
0
                    break;
1230
1231
0
                    case SdrDragMode::Move:
1232
0
                    {
1233
0
                        if(!bThereAreRootScenes)
1234
0
                        {
1235
0
                            pForcedMeth = new E3dDragMove(*this, rMarkList, meDragHdl, eConstraint, IsSolidDragging());
1236
0
                        }
1237
0
                    }
1238
0
                    break;
1239
1240
                    // later on
1241
0
                    case SdrDragMode::Mirror:
1242
0
                    case SdrDragMode::Crook:
1243
0
                    case SdrDragMode::Transparence:
1244
0
                    case SdrDragMode::Gradient:
1245
0
                    default:
1246
0
                    {
1247
0
                    }
1248
0
                    break;
1249
0
                }
1250
0
            }
1251
0
        }
1252
0
    }
1253
0
    return SdrView::BegDragObj(rPnt, pOut, pHdl, nMinMov, pForcedMeth);
1254
0
}
1255
1256
// Set current 3D drawing object, create the scene for this
1257
rtl::Reference<E3dScene> E3dView::SetCurrent3DObj(E3dObject* p3DObj)
1258
0
{
1259
0
    assert(p3DObj != nullptr && "Who puts in a NULL-pointer here");
1260
1261
    // get transformed BoundVolume of the object
1262
0
    basegfx::B3DRange aVolume(p3DObj->GetBoundVolume());
1263
0
    aVolume.transform(p3DObj->GetTransform());
1264
0
    double fW(aVolume.getWidth());
1265
0
    double fH(aVolume.getHeight());
1266
1267
0
    tools::Rectangle aRect(0,0, static_cast<tools::Long>(fW), static_cast<tools::Long>(fH));
1268
1269
0
    rtl::Reference<E3dScene> pScene = new E3dScene(p3DObj->getSdrModelFromSdrObject());
1270
1271
0
    InitScene(pScene.get(), fW, fH, aVolume.getMaxZ() + ((fW + fH) / 4.0));
1272
1273
0
    pScene->InsertObject(p3DObj);
1274
0
    pScene->NbcSetSnapRect(aRect);
1275
1276
0
    return pScene;
1277
0
}
1278
1279
void E3dView::InitScene(E3dScene* pScene, double fW, double fH, double fCamZ)
1280
0
{
1281
0
    Camera3D aCam(pScene->GetCamera());
1282
1283
0
    aCam.SetAutoAdjustProjection(false);
1284
0
    aCam.SetViewWindow(- fW / 2, - fH / 2, fW, fH);
1285
0
    basegfx::B3DPoint aLookAt;
1286
1287
0
    double fDefaultCamPosZ = GetDefaultCamPosZ();
1288
0
    basegfx::B3DPoint aCamPos(0.0, 0.0, fCamZ < fDefaultCamPosZ ? fDefaultCamPosZ : fCamZ);
1289
1290
0
    aCam.SetPosAndLookAt(aCamPos, aLookAt);
1291
0
    aCam.SetFocalLength(GetDefaultCamFocal());
1292
0
    pScene->SetCamera(aCam);
1293
0
}
1294
1295
void E3dView::Start3DCreation()
1296
0
{
1297
0
    const SdrMarkList& rMarkList = GetMarkedObjectList();
1298
0
    if (!rMarkList.GetMarkCount())
1299
0
        return;
1300
1301
    //positioned
1302
0
    tools::Long          nOutMin = 0;
1303
0
    tools::Long          nOutMax = 0;
1304
0
    tools::Long          nMinLen = 0;
1305
0
    tools::Long          nObjDst = 0;
1306
0
    tools::Long          nOutHgt = 0;
1307
0
    OutputDevice* pOut    = GetFirstOutputDevice();
1308
1309
    // first determine representation boundaries
1310
0
    if (pOut != nullptr)
1311
0
    {
1312
0
        nMinLen = pOut->PixelToLogic(Size(0,50)).Height();
1313
0
        nObjDst = pOut->PixelToLogic(Size(0,20)).Height();
1314
1315
0
        tools::Long nDst = pOut->PixelToLogic(Size(0,10)).Height();
1316
1317
0
        nOutMin =  -pOut->GetMapMode().GetOrigin().Y();
1318
0
        nOutMax =  pOut->GetOutputSize().Height() - 1 + nOutMin;
1319
0
        nOutMin += nDst;
1320
0
        nOutMax -= nDst;
1321
1322
0
        if (nOutMax - nOutMin < nDst)
1323
0
        {
1324
0
            nOutMin += nOutMax + 1;
1325
0
            nOutMin /= 2;
1326
0
            nOutMin -= (nDst + 1) / 2;
1327
0
            nOutMax  = nOutMin + nDst;
1328
0
        }
1329
1330
0
        nOutHgt = nOutMax - nOutMin;
1331
1332
0
        tools::Long nTemp = nOutHgt / 4;
1333
0
        if (nTemp > nMinLen) nMinLen = nTemp;
1334
0
    }
1335
1336
    // and then attach the marks at the top and bottom of the object
1337
0
    basegfx::B2DRange aR;
1338
0
    for(size_t nMark = 0; nMark < rMarkList.GetMarkCount(); ++nMark)
1339
0
    {
1340
0
        SdrObject* pMark = rMarkList.GetMark(nMark)->GetMarkedSdrObj();
1341
0
        basegfx::B2DPolyPolygon aXPP(pMark->TakeXorPoly());
1342
0
        aR.expand(aXPP.getB2DRange());
1343
0
    }
1344
1345
0
    basegfx::B2DPoint aCenter(aR.getCenter());
1346
0
    tools::Long      nMarkHgt = basegfx::fround<tools::Long>(aR.getHeight()) - 1;
1347
0
    tools::Long      nHgt     = nMarkHgt + nObjDst * 2;
1348
1349
0
    if (nHgt < nMinLen) nHgt = nMinLen;
1350
1351
0
    tools::Long nY1 = basegfx::fround<tools::Long>(aCenter.getY()) - (nHgt + 1) / 2;
1352
0
    tools::Long nY2 = nY1 + nHgt;
1353
1354
0
    if (pOut && (nMinLen > nOutHgt)) nMinLen = nOutHgt;
1355
0
    if (pOut)
1356
0
    {
1357
0
        if (nY1 < nOutMin)
1358
0
        {
1359
0
            nY1 = nOutMin;
1360
0
            if (nY2 < nY1 + nMinLen) nY2 = nY1 + nMinLen;
1361
0
        }
1362
0
        if (nY2 > nOutMax)
1363
0
        {
1364
0
            nY2 = nOutMax;
1365
0
            if (nY1 > nY2 - nMinLen) nY1 = nY2 - nMinLen;
1366
0
        }
1367
0
    }
1368
1369
0
    maRef1.setX( basegfx::fround<tools::Long>(aR.getMinX()) );    // Initial move axis 2/100mm to the left
1370
0
    maRef1.setY( nY1 );
1371
0
    maRef2.setX( maRef1.X() );
1372
0
    maRef2.setY( nY2 );
1373
1374
    // Turn on marks
1375
0
    SetMarkHandles(nullptr);
1376
1377
    //HMHif (bVis) ShowMarkHdl();
1378
0
    if (rMarkList.GetMarkCount() != 0) MarkListHasChanged();
1379
1380
    // Show mirror polygon IMMEDIATELY
1381
0
    const SdrHdlList &aHdlList = GetHdlList();
1382
0
    mpMirrorOverlay.reset(new Impl3DMirrorConstructOverlay(*this));
1383
0
    mpMirrorOverlay->SetMirrorAxis(aHdlList.GetHdl(SdrHdlKind::Ref1)->GetPos(), aHdlList.GetHdl(SdrHdlKind::Ref2)->GetPos());
1384
0
}
1385
1386
// what happens with a mouse movement when the object is created?
1387
1388
void E3dView::MovAction(const Point& rPnt)
1389
0
{
1390
0
    if(Is3DRotationCreationActive())
1391
0
    {
1392
0
        SdrHdl* pHdl = GetDragHdl();
1393
1394
0
        if (pHdl)
1395
0
        {
1396
0
            SdrHdlKind eHdlKind = pHdl->GetKind();
1397
1398
            // reacts only due to a mirror axis
1399
0
            if ((eHdlKind == SdrHdlKind::Ref1) ||
1400
0
                (eHdlKind == SdrHdlKind::Ref2) ||
1401
0
                (eHdlKind == SdrHdlKind::MirrorAxis))
1402
0
            {
1403
0
                const SdrHdlList &aHdlList = GetHdlList ();
1404
1405
                // delete the mirrored polygon, mirrors the original and draws
1406
                // it anew
1407
0
                SdrView::MovAction (rPnt);
1408
0
                mpMirrorOverlay->SetMirrorAxis(
1409
0
                    aHdlList.GetHdl (SdrHdlKind::Ref1)->GetPos(),
1410
0
                    aHdlList.GetHdl (SdrHdlKind::Ref2)->GetPos());
1411
0
            }
1412
0
        }
1413
0
        else
1414
0
        {
1415
0
            SdrView::MovAction (rPnt);
1416
0
        }
1417
0
    }
1418
0
    else
1419
0
    {
1420
0
        SdrView::MovAction (rPnt);
1421
0
    }
1422
0
}
1423
1424
// The End. Create object and any child objects through ImpCreate3DLathe.
1425
// With the parameter value sal_True (SDefault: sal_False) is simply a
1426
// rotation body  created, without letting the user set the position of the
1427
// axis. It is sufficient with this call, if an object is selected.
1428
// (No initialization necessary)
1429
1430
void E3dView::End3DCreation(bool bUseDefaultValuesForMirrorAxes)
1431
0
{
1432
0
    ResetCreationActive();
1433
1434
0
    const SdrMarkList& rMarkList = GetMarkedObjectList();
1435
0
    if(rMarkList.GetMarkCount() == 0)
1436
0
        return;
1437
1438
0
    if(bUseDefaultValuesForMirrorAxes)
1439
0
    {
1440
0
        tools::Rectangle aRect = GetAllMarkedRect();
1441
0
        if(aRect.GetWidth() <= 1)
1442
0
            aRect.SetSize(Size(500, aRect.GetHeight()));
1443
0
        if(aRect.GetHeight() <= 1)
1444
0
            aRect.SetSize(Size(aRect.GetWidth(), 500));
1445
1446
0
        basegfx::B2DPoint aPnt1(aRect.Left(), -aRect.Top());
1447
0
        basegfx::B2DPoint aPnt2(aRect.Left(), -aRect.Bottom());
1448
1449
0
        ConvertMarkedObjTo3D(false, aPnt1, aPnt2);
1450
0
    }
1451
0
    else
1452
0
    {
1453
        // Turn off helper overlay
1454
        // Determine from the handle positions and the displacement of
1455
        // the points
1456
0
        const SdrHdlList &aHdlList = GetHdlList();
1457
0
        Point aMirrorRef1 = aHdlList.GetHdl(SdrHdlKind::Ref1)->GetPos();
1458
0
        Point aMirrorRef2 = aHdlList.GetHdl(SdrHdlKind::Ref2)->GetPos();
1459
1460
0
        basegfx::B2DPoint aPnt1(aMirrorRef1.X(), -aMirrorRef1.Y());
1461
0
        basegfx::B2DPoint aPnt2(aMirrorRef2.X(), -aMirrorRef2.Y());
1462
1463
0
        ConvertMarkedObjTo3D(false, aPnt1, aPnt2);
1464
0
    }
1465
0
}
1466
1467
E3dView::~E3dView ()
1468
64.2k
{
1469
64.2k
}
1470
1471
void E3dView::ResetCreationActive ()
1472
0
{
1473
0
    mpMirrorOverlay.reset();
1474
0
}
1475
1476
void E3dView::InitView ()
1477
64.2k
{
1478
64.2k
    mpMirrorOverlay          = nullptr;
1479
64.2k
}
1480
1481
bool E3dView::IsBreak3DObjPossible() const
1482
0
{
1483
0
    const SdrMarkList& rMarkList = GetMarkedObjectList();
1484
0
    const size_t nCount = rMarkList.GetMarkCount();
1485
1486
0
    if (nCount > 0)
1487
0
    {
1488
0
        for (size_t i = 0; i < nCount; ++i)
1489
0
        {
1490
0
            SdrObject* pObj = rMarkList.GetMark(i)->GetMarkedSdrObj();
1491
1492
0
            if (auto p3dObject = DynCastE3dObject(pObj))
1493
0
            {
1494
0
                if(!p3dObject->IsBreakObjPossible())
1495
0
                    return false;
1496
0
            }
1497
0
            else
1498
0
            {
1499
0
                return false;
1500
0
            }
1501
0
        }
1502
0
    }
1503
0
    else
1504
0
    {
1505
0
        return false;
1506
0
    }
1507
1508
0
    return true;
1509
0
}
1510
1511
void E3dView::Break3DObj()
1512
0
{
1513
0
    if(!IsBreak3DObjPossible())
1514
0
        return;
1515
1516
0
    const SdrMarkList& rMarkList = GetMarkedObjectList();
1517
    // ALL selected objects are changed
1518
0
    const size_t nCount = rMarkList.GetMarkCount();
1519
1520
0
    BegUndo(SvxResId(RID_SVX_3D_UNDO_BREAK_LATHE));
1521
0
    for(size_t a=0; a<nCount; ++a)
1522
0
    {
1523
0
        E3dObject* pObj = static_cast<E3dObject*>(rMarkList.GetMark(a)->GetMarkedSdrObj());
1524
0
        BreakSingle3DObj(pObj);
1525
0
    }
1526
0
    DeleteMarked();
1527
0
    EndUndo();
1528
0
}
1529
1530
void E3dView::BreakSingle3DObj(E3dObject* pObj)
1531
0
{
1532
0
    if(DynCastE3dScene(pObj))
1533
0
    {
1534
0
        SdrObjList* pSubList = pObj->GetSubList();
1535
0
        SdrObjListIter aIter(pSubList, SdrIterMode::Flat);
1536
1537
0
        while(aIter.IsMore())
1538
0
        {
1539
0
            E3dObject* pSubObj = static_cast<E3dObject*>(aIter.Next());
1540
0
            BreakSingle3DObj(pSubObj);
1541
0
        }
1542
0
    }
1543
0
    else
1544
0
    {
1545
0
        rtl::Reference<SdrAttrObj> pNewObj = pObj->GetBreakObj();
1546
0
        if (pNewObj)
1547
0
        {
1548
0
            if (InsertObjectAtView(pNewObj.get(), *GetSdrPageView(), SdrInsertFlags::DONTMARK))
1549
0
            {
1550
0
                pNewObj->SetChanged();
1551
0
                pNewObj->BroadcastObjectChange();
1552
0
            }
1553
0
        }
1554
0
    }
1555
0
}
1556
1557
void E3dView::CheckPossibilities()
1558
0
{
1559
    // call parent
1560
0
    SdrView::CheckPossibilities();
1561
1562
    // Set other flags
1563
0
    if(!(m_bGroupPossible || m_bUnGroupPossible || m_bGrpEnterPossible))
1564
0
        return;
1565
1566
0
    const SdrMarkList& rMarkList = GetMarkedObjectList();
1567
0
    const size_t nMarkCnt = rMarkList.GetMarkCount();
1568
0
    bool bCompound = false;
1569
0
    bool b3DObject = false;
1570
0
    for(size_t nObjs = 0; (nObjs < nMarkCnt) && !bCompound; ++nObjs)
1571
0
    {
1572
0
        SdrObject *pObj = rMarkList.GetMark(nObjs)->GetMarkedSdrObj();
1573
0
        if(dynamic_cast< const E3dCompoundObject* >(pObj))
1574
0
            bCompound = true;
1575
0
        if(DynCastE3dObject(pObj))
1576
0
            b3DObject = true;
1577
0
    }
1578
1579
    // So far: there are two or more of any objects selected. See if
1580
    // compound objects are involved. If yes, ban grouping.
1581
0
    if(m_bGroupPossible && bCompound)
1582
0
        m_bGroupPossible = false;
1583
1584
0
    if(m_bUnGroupPossible && b3DObject)
1585
0
        m_bUnGroupPossible = false;
1586
1587
0
    if(m_bGrpEnterPossible && bCompound)
1588
0
        m_bGrpEnterPossible = false;
1589
0
}
1590
1591
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */