    /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
    /*
     * This file is part of the LibreOffice project.
     *
     * This Source Code Form is subject to the terms of the Mozilla Public
     * License, v. 2.0. If a copy of the MPL was not distributed with this
     * file, You can obtain one at http://mozilla.org/MPL/2.0/.
     *
     * This file incorporates work covered by the following license notice:
     *
     *   Licensed to the Apache Software Foundation (ASF) under one or more
     *   contributor license agreements. See the NOTICE file distributed
     *   with this work for additional information regarding copyright
     *   ownership. The ASF licenses this file to you under the Apache
     *   License, Version 2.0 (the "License"); you may not use this file
     *   except in compliance with the License. You may obtain a copy of
     *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
     */

    #include <vcl/wrkwin.hxx>
    #include <svx/svdogrp.hxx>
    #include <svx/svdopath.hxx>
    #include <svx/svditer.hxx>
    #include <svx/svdpool.hxx>
    #include <svx/svdorect.hxx>
    #include <svx/svdmodel.hxx>
    #include <svx/svdpagv.hxx>
    #include <svx/svxids.hrc>
    #include <editeng/colritem.hxx>
    #include <editeng/eeitem.hxx>
    #include <svx/xtable.hxx>
    #include <svx/svdview.hxx>
    #include <svx/strings.hrc>
    #include <svx/dialmgr.hxx>
    #include <svx/globl3d.hxx>
    #include <svx/obj3d.hxx>
    #include <svx/lathe3d.hxx>
    #include <svx/sphere3d.hxx>
    #include <svx/extrud3d.hxx>
    #include <svx/cube3d.hxx>
    #include <dragmt3d.hxx>
    #include <svx/view3d.hxx>
    #include <svx/svdundo.hxx>
    #include <svx/xflclit.hxx>
    #include <svx/xlnclit.hxx>
    #include <svx/svdograf.hxx>
    #include <svx/xbtmpit.hxx>
    #include <svx/xflbmtit.hxx>
    #include <basegfx/range/b2drange.hxx>
    #include <basegfx/polygon/b2dpolygontools.hxx>
    #include <basegfx/polygon/b2dpolypolygontools.hxx>
    #include <svx/xlnwtit.hxx>
    #include <svx/sdr/overlay/overlaypolypolygon.hxx>
    #include <svx/sdr/overlay/overlaymanager.hxx>
    #include <svx/sdrpaintwindow.hxx>
    #include <svx/sdr/contact/viewcontactofe3dscene.hxx>
    #include <drawinglayer/geometry/viewinformation3d.hxx>
    #include <svx/sdrpagewindow.hxx>
    #include <svx/sdr/contact/displayinfo.hxx>
    #include <svx/sdr/contact/objectcontact.hxx>
    #include <svx/sdr/contact/viewobjectcontact.hxx>
    #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
    #include <svx/sdr/overlay/overlayprimitive2dsequenceobject.hxx>
    #include <drawinglayer/primitive2d/transformprimitive2d.hxx>
    #include <basegfx/matrix/b2dhommatrixtools.hxx>
    #include <basegfx/polygon/b2dpolypolygoncutter.hxx>

    using namespace com::sun::star;

    // Migrate Marking

    class Impl3DMirrorConstructOverlay
    {
        // The OverlayObjects
        sdr::overlay::OverlayObjectList               maObjects;

        // the view
        const E3dView&                                  mrView;

        // the object count
        size_t mnCount;

        // the unmirrored polygons
        basegfx::B2DPolyPolygon*                        mpPolygons;

        // the overlay geometry from selected objects
        drawinglayer::primitive2d::Primitive2DContainer    maFullOverlay;

        // Copy assignment is forbidden and not implemented.
        Impl3DMirrorConstructOverlay (const Impl3DMirrorConstructOverlay &) = delete;
        Impl3DMirrorConstructOverlay & operator= (const Impl3DMirrorConstructOverlay &) = delete;

    public:
        explicit Impl3DMirrorConstructOverlay(const E3dView& rView);
        ~Impl3DMirrorConstructOverlay();

        void SetMirrorAxis(Point aMirrorAxisA, Point aMirrorAxisB);
    };

    Impl3DMirrorConstructOverlay::Impl3DMirrorConstructOverlay(const E3dView& rView)
    :   maObjects(),
        mrView(rView),
        mnCount(rView.GetMarkedObjectCount()),
        mpPolygons(nullptr),
        maFullOverlay()
    {
        if(mnCount)
        {
            if(mrView.IsSolidDragging())
            {
                SdrPageView* pPV = rView.GetSdrPageView();

                if(pPV && pPV->PageWindowCount())
                {
                    sdr::contact::ObjectContact& rOC = pPV->GetPageWindow(0)->GetObjectContact();
                    sdr::contact::DisplayInfo aDisplayInfo;

                    // Do not use the last ViewPort set at the OC at the last ProcessDisplay()
                    rOC.resetViewPort();

                    for(size_t a = 0; a < mnCount; ++a)
                    {
                        SdrObject* pObject = mrView.GetMarkedObjectByIndex(a);

                        if(pObject)
                        {
                            sdr::contact::ViewContact& rVC = pObject->GetViewContact();
                            sdr::contact::ViewObjectContact& rVOC = rVC.GetViewObjectContact(rOC);

                            const drawinglayer::primitive2d::Primitive2DContainer aNewSequence(rVOC.getPrimitive2DSequenceHierarchy(aDisplayInfo));
                            maFullOverlay.append(aNewSequence);
                        }
                    }
                }
            }
            else
            {
                mpPolygons = new basegfx::B2DPolyPolygon[mnCount];

                for(size_t a = 0; a < mnCount; ++a)
                {
                    SdrObject* pObject = mrView.GetMarkedObjectByIndex(a);
                    mpPolygons[mnCount - (a + 1)] = pObject->TakeXorPoly();
                }
            }
        }
    }

    Impl3DMirrorConstructOverlay::~Impl3DMirrorConstructOverlay()
    {
        // The OverlayObjects are cleared using the destructor of OverlayObjectList.
        // That destructor calls clear() at the list which removes all objects from the
        // OverlayManager and deletes them.
        if(!mrView.IsSolidDragging())
        {
            delete[] mpPolygons;
        }
    }

    void Impl3DMirrorConstructOverlay::SetMirrorAxis(Point aMirrorAxisA, Point aMirrorAxisB)
    {
        // get rid of old overlay objects
        maObjects.clear();

        // create new ones
        for(sal_uInt32 a(0); a < mrView.PaintWindowCount(); a++)
        {
            SdrPaintWindow* pCandidate = mrView.GetPaintWindow(a);
            rtl::Reference< sdr::overlay::OverlayManager > xTargetOverlay = pCandidate->GetOverlayManager();

            if(xTargetOverlay.is())
            {
                // build transformation: translate and rotate so that given edge is
                // on x axis, them mirror in y and translate back
                const basegfx::B2DVector aEdge(aMirrorAxisB.X() - aMirrorAxisA.X(), aMirrorAxisB.Y() - aMirrorAxisA.Y());
                basegfx::B2DHomMatrix aMatrixTransform(basegfx::utils::createTranslateB2DHomMatrix(
                    -aMirrorAxisA.X(), -aMirrorAxisA.Y()));
                aMatrixTransform.rotate(-atan2(aEdge.getY(), aEdge.getX()));
                aMatrixTransform.scale(1.0, -1.0);
                aMatrixTransform.rotate(atan2(aEdge.getY(), aEdge.getX()));
                aMatrixTransform.translate(aMirrorAxisA.X(), aMirrorAxisA.Y());

                if(mrView.IsSolidDragging())
                {
                    if(!maFullOverlay.empty())
                    {
                        drawinglayer::primitive2d::Primitive2DContainer aContent(maFullOverlay);

                        if(!aMatrixTransform.isIdentity())
                        {
                            // embed in transformation group
                            drawinglayer::primitive2d::Primitive2DReference aTransformPrimitive2D(new drawinglayer::primitive2d::TransformPrimitive2D(aMatrixTransform, aContent));
                            aContent = drawinglayer::primitive2d::Primitive2DContainer { aTransformPrimitive2D };
                        }

                        // if we have full overlay from selected objects, embed with 50% transparence, the
                        // transformation is added to the OverlayPrimitive2DSequenceObject
                        drawinglayer::primitive2d::Primitive2DReference aUnifiedTransparencePrimitive2D(new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(aContent, 0.5));
                        aContent = drawinglayer::primitive2d::Primitive2DContainer { aUnifiedTransparencePrimitive2D };

                        std::unique_ptr<sdr::overlay::OverlayPrimitive2DSequenceObject> pNew(new sdr::overlay::OverlayPrimitive2DSequenceObject(aContent));

                        xTargetOverlay->add(*pNew);
                        maObjects.append(std::move(pNew));
                    }
                }
                else
                {
                    for(size_t b = 0; b < mnCount; ++b)
                    {
                        // apply to polygon
                        basegfx::B2DPolyPolygon aPolyPolygon(mpPolygons[b]);
                        aPolyPolygon.transform(aMatrixTransform);

                        std::unique_ptr<sdr::overlay::OverlayPolyPolygonStripedAndFilled> pNew(new sdr::overlay::OverlayPolyPolygonStripedAndFilled(
                            aPolyPolygon));
                        xTargetOverlay->add(*pNew);
                        maObjects.append(std::move(pNew));
                    }
                }
            }
        }
    }

    E3dView::E3dView(
        SdrModel& rSdrModel,
        OutputDevice* pOut)
    :   SdrView(rSdrModel, pOut)
    {
        InitView();
    }

    // DrawMarkedObj override, since possibly only a single 3D object is to be
    // drawn

    void E3dView::DrawMarkedObj(OutputDevice& rOut) const
    {
        // Does 3D objects exist which scenes are not selected?
        bool bSpecialHandling = false;
        E3dScene *pScene = nullptr;

        const size_t nCnt = GetMarkedObjectCount();
        for(size_t nObjs = 0; nObjs < nCnt; ++nObjs)
        {
            SdrObject *pObj = GetMarkedObjectByIndex(nObjs);
            if(auto pCompoundObject = dynamic_cast<E3dCompoundObject*>(pObj))
            {
                // related scene
                pScene = pCompoundObject->getRootE3dSceneFromE3dObject();

                if(nullptr != pScene && !IsObjMarked(pScene))
                {
                    bSpecialHandling = true;
                }
            }
            // Reset all selection flags
            if(auto p3dObject = dynamic_cast< const E3dObject*>(pObj))
            {
                pScene = p3dObject->getRootE3dSceneFromE3dObject();

                if(nullptr != pScene)
                {
                    pScene->SetSelected(false);
                }
            }
        }

        if(bSpecialHandling)
        {
            // Set selection flag to "not selected" for scenes related to all 3D
            // objects
            for(size_t nObjs = 0; nObjs < nCnt; ++nObjs)
            {
                SdrObject *pObj = GetMarkedObjectByIndex(nObjs);
                if(auto pCompoundObject = dynamic_cast<E3dCompoundObject*>(pObj))
                {
                    // related scene
                    pScene = pCompoundObject->getRootE3dSceneFromE3dObject();

                    if(nullptr != pScene)
                    {
                        pScene->SetSelected(false);
                    }
                }
            }

            for(size_t nObjs = 0; nObjs < nCnt; ++nObjs)
            {
                SdrObject *pObj = GetMarkedObjectByIndex(nObjs);
                if(auto p3DObj = dynamic_cast<E3dObject*>(pObj))
                {
                    // Select object
                    p3DObj->SetSelected(true);
                    pScene = p3DObj->getRootE3dSceneFromE3dObject();
                }
            }

            if(nullptr != pScene)
            {
                // code from parent
                SortMarkedObjects();

                pScene->SetDrawOnlySelected(true);
                pScene->SingleObjectPainter(rOut);
                pScene->SetDrawOnlySelected(false);
            }

            // Reset selection flag
            for(size_t nObjs = 0; nObjs < nCnt; ++nObjs)
            {
                SdrObject *pObj = GetMarkedObjectByIndex(nObjs);
                if(auto pCompoundObject = dynamic_cast<E3dCompoundObject*>(pObj))
                {
                    // related scene
                    pScene = pCompoundObject->getRootE3dSceneFromE3dObject();

                    if(nullptr != pScene)
                    {
                        pScene->SetSelected(false);
                    }
                }
            }
        }
        else
        {
            // call parent
            SdrExchangeView::DrawMarkedObj(rOut);
        }
    }

    // override get model, since in some 3D objects an additional scene
    // must be pushed in

    std::unique_ptr<SdrModel> E3dView::CreateMarkedObjModel() const
    {
        // Does 3D objects exist which scenes are not selected?
        bool bSpecialHandling(false);
        const size_t nCount(GetMarkedObjectCount());
        E3dScene *pScene = nullptr;

        for(size_t nObjs = 0; nObjs < nCount; ++nObjs)
        {
            const SdrObject* pObj = GetMarkedObjectByIndex(nObjs);

            if(!bSpecialHandling && dynamic_cast< const E3dCompoundObject*>(pObj))
            {
                // if the object is selected, but it's scene not,
                // we need special handling
                pScene = static_cast<const E3dCompoundObject*>(pObj)->getRootE3dSceneFromE3dObject();

                if(nullptr != pScene && !IsObjMarked(pScene))
                {
                    bSpecialHandling = true;
                }
            }

            if(auto p3dObject = dynamic_cast< const E3dObject*>(pObj))
            {
                // reset all selection flags at 3D objects
                pScene = p3dObject->getRootE3dSceneFromE3dObject();

                if(nullptr != pScene)
                {
                    pScene->SetSelected(false);
                }
            }
        }

        if(!bSpecialHandling)
        {
            // call parent
            return SdrView::CreateMarkedObjModel();
        }

        std::unique_ptr<SdrModel> pNewModel;
        tools::Rectangle aSelectedSnapRect;

        // set 3d selection flags at all directly selected objects
        // and collect SnapRect of selected objects
        for(size_t nObjs = 0; nObjs < nCount; ++nObjs)
        {
            SdrObject *pObj = GetMarkedObjectByIndex(nObjs);

            if(auto p3DObj = dynamic_cast<E3dCompoundObject*>(pObj))
            {
                // mark object, but not scenes
                p3DObj->SetSelected(true);
                aSelectedSnapRect.Union(p3DObj->GetSnapRect());
            }
        }

        // create new mark list which contains all indirectly selected3d
        // scenes as selected objects
        SdrMarkList aOldML(GetMarkedObjectList());
        SdrMarkList aNewML;
        SdrMarkList& rCurrentMarkList = const_cast<E3dView*>(this)->GetMarkedObjectListWriteAccess();
        rCurrentMarkList = aNewML;

        for(size_t nObjs = 0; nObjs < nCount; ++nObjs)
        {
            SdrObject *pObj = aOldML.GetMark(nObjs)->GetMarkedSdrObj();

            if(auto p3dObject = dynamic_cast< E3dObject* >(pObj))
            {
                pScene = p3dObject->getRootE3dSceneFromE3dObject();

                if(nullptr != pScene && !IsObjMarked(pScene) && GetSdrPageView())
                {
                    const_cast<E3dView*>(this)->MarkObj(pScene, GetSdrPageView(), false, true);
                }
            }
        }

        // call parent. This will copy all scenes and the selection flags at the 3D objects. So
        // it will be possible to delete all non-selected 3d objects from the cloned 3d scenes
        pNewModel = SdrView::CreateMarkedObjModel();

        if(pNewModel)
        {
            for(sal_uInt16 nPg(0); nPg < pNewModel->GetPageCount(); nPg++)
            {
                const SdrPage* pSrcPg=pNewModel->GetPage(nPg);
                const size_t nObjCount(pSrcPg->GetObjCount());

                for(size_t nOb = 0; nOb < nObjCount; ++nOb)
                {
                    const SdrObject* pSrcOb=pSrcPg->GetObj(nOb);

                    if(auto p3dscene = dynamic_cast< const E3dScene* >( pSrcOb))
                    {
                        pScene = const_cast<E3dScene*>(p3dscene);

                        // delete all not intentionally cloned 3d objects
                        pScene->removeAllNonSelectedObjects();

                        // reset select flags and set SnapRect of all selected objects
                        pScene->SetSelected(false);
                        pScene->SetSnapRect(aSelectedSnapRect);
                    }
                }
            }
        }

        // restore old selection
        rCurrentMarkList = aOldML;

        return pNewModel;
    }

    // When pasting objects have to integrated if a scene is inserted, but
    // not the scene itself

    bool E3dView::Paste(
        const SdrModel& rMod, const Point& rPos, SdrObjList* pLst, SdrInsertFlags nOptions)
    {
        bool bRetval = false;

        // Get list
        Point aPos(rPos);
        SdrObjList* pDstList = pLst;
        ImpGetPasteObjList(aPos, pDstList);

        if(!pDstList)
            return false;

        // Get owner of the list
        E3dScene* pDstScene(dynamic_cast< E3dScene* >(pDstList->getSdrObjectFromSdrObjList()));

        if(nullptr != pDstScene)
        {
            BegUndo(SvxResId(RID_SVX_3D_UNDO_EXCHANGE_PASTE));

            // Copy all objects from E3dScenes and insert them directly
            for(sal_uInt16 nPg(0); nPg < rMod.GetPageCount(); nPg++)
            {
                const SdrPage* pSrcPg=rMod.GetPage(nPg);
                const size_t nObjCount(pSrcPg->GetObjCount());

                // calculate offset for paste
                tools::Rectangle aR = pSrcPg->GetAllObjBoundRect();
                Point aDist(aPos - aR.Center());

                // Insert sub-objects for scenes
                for(size_t nOb = 0; nOb < nObjCount; ++nOb)
                {
                    const SdrObject* pSrcOb = pSrcPg->GetObj(nOb);
                    if(auto p3dscene = dynamic_cast< const E3dScene* >(pSrcOb))
                    {
                        E3dScene* pSrcScene = const_cast<E3dScene*>(p3dscene);
                        ImpCloneAll3DObjectsToDestScene(pSrcScene, pDstScene, aDist);
                    }
                }
            }
            EndUndo();
        }
        else
        {
            // call parent
            bRetval = SdrView::Paste(rMod, rPos, pLst, nOptions);
        }

        return bRetval;
    }

    // Service routine used from local Clone() and from SdrCreateView::EndCreateObj(...)
    bool E3dView::ImpCloneAll3DObjectsToDestScene(E3dScene const * pSrcScene, E3dScene* pDstScene, Point /*aOffset*/)
    {
        bool bRetval(false);

        if(pSrcScene && pDstScene)
        {
            for(size_t i = 0; i < pSrcScene->GetSubList()->GetObjCount(); ++i)
            {
                E3dCompoundObject* pCompoundObj = dynamic_cast< E3dCompoundObject* >(pSrcScene->GetSubList()->GetObj(i));

                if(pCompoundObj)
                {
                    E3dCompoundObject* pNewCompoundObj(pCompoundObj->CloneSdrObject(pDstScene->getSdrModelFromSdrObject()));

                    if(pNewCompoundObj)
                    {
                        // get dest scene's current range in 3D world coordinates
                        const basegfx::B3DHomMatrix aSceneToWorldTrans(pDstScene->GetFullTransform());
                        basegfx::B3DRange aSceneRange(pDstScene->GetBoundVolume());
                        aSceneRange.transform(aSceneToWorldTrans);

                        // get new object's implied object transformation
                        const basegfx::B3DHomMatrix aNewObjectTrans(pNewCompoundObj->GetTransform());

                        // get new object's range in 3D world coordinates in dest scene
                        // as if it were already added
                        const basegfx::B3DHomMatrix aObjectToWorldTrans(aSceneToWorldTrans * aNewObjectTrans);
                        basegfx::B3DRange aObjectRange(pNewCompoundObj->GetBoundVolume());
                        aObjectRange.transform(aObjectToWorldTrans);

                        // get scale adaption
                        const basegfx::B3DVector aSceneScale(aSceneRange.getRange());
                        const basegfx::B3DVector aObjectScale(aObjectRange.getRange());
                        double fScale(1.0);

                        // if new object's size in X,Y or Z is bigger that 80% of dest scene, adapt scale
                        // to not change the scene by the inserted object
                        const double fSizeFactor(0.5);

                        if(aObjectScale.getX() * fScale > aSceneScale.getX() * fSizeFactor)
                        {
                            const double fObjSize(aObjectScale.getX() * fScale);
                            const double fFactor((aSceneScale.getX() * fSizeFactor) / (basegfx::fTools::equalZero(fObjSize) ? 1.0 : fObjSize));
                            fScale *= fFactor;
                        }

                        if(aObjectScale.getY() * fScale > aSceneScale.getY() * fSizeFactor)
                        {
                            const double fObjSize(aObjectScale.getY() * fScale);
                            const double fFactor((aSceneScale.getY() * fSizeFactor) / (basegfx::fTools::equalZero(fObjSize) ? 1.0 : fObjSize));
                            fScale *= fFactor;
                        }

                        if(aObjectScale.getZ() * fScale > aSceneScale.getZ() * fSizeFactor)
                        {
                            const double fObjSize(aObjectScale.getZ() * fScale);
                            const double fFactor((aSceneScale.getZ() * fSizeFactor) / (basegfx::fTools::equalZero(fObjSize) ? 1.0 : fObjSize));
                            fScale *= fFactor;
                        }

                        // get translation adaption
                        const basegfx::B3DPoint aSceneCenter(aSceneRange.getCenter());
                        const basegfx::B3DPoint aObjectCenter(aObjectRange.getCenter());

                        // build full modification transform. The object's transformation
                        // shall be modified, so start at object coordinates; transform to 3d world coor
                        basegfx::B3DHomMatrix aModifyingTransform(aObjectToWorldTrans);

                        // translate to absolute center in 3d world coor
                        aModifyingTransform.translate(-aObjectCenter.getX(), -aObjectCenter.getY(), -aObjectCenter.getZ());

                        // scale to dest size in 3d world coor
                        aModifyingTransform.scale(fScale, fScale, fScale);

                        // translate to dest scene center in 3d world coor
                        aModifyingTransform.translate(aSceneCenter.getX(), aSceneCenter.getY(), aSceneCenter.getZ());

                        // transform from 3d world to dest object coordinates
                        basegfx::B3DHomMatrix aWorldToObject(aObjectToWorldTrans);
                        aWorldToObject.invert();
                        aModifyingTransform = aWorldToObject * aModifyingTransform;

                        // correct implied object transform by applying changing one in object coor
                        pNewCompoundObj->SetTransform(aModifyingTransform * aNewObjectTrans);

                        // fill and insert new object
                        pNewCompoundObj->NbcSetLayer(pCompoundObj->GetLayer());
                        pNewCompoundObj->NbcSetStyleSheet(pCompoundObj->GetStyleSheet(), true);
                        pDstScene->InsertObject(pNewCompoundObj);
                        bRetval = true;

                        // Create undo
                        if( GetModel()->IsUndoEnabled() )
                            AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoNewObject(*pNewCompoundObj));
                    }
                }
            }
        }

        return bRetval;
    }

    bool E3dView::IsConvertTo3DObjPossible() const
    {
        bool bAny3D(false);
        bool bGroupSelected(false);
        bool bRetval(true);

        for(size_t a=0; !bAny3D && a<GetMarkedObjectCount(); ++a)
        {
            SdrObject *pObj = GetMarkedObjectByIndex(a);
            if(pObj)
            {
                ImpIsConvertTo3DPossible(pObj, bAny3D, bGroupSelected);
            }
        }

        bRetval = !bAny3D
            && (
               IsConvertToPolyObjPossible()
            || IsConvertToPathObjPossible()
            || IsImportMtfPossible());
        return bRetval;
    }

    void E3dView::ImpIsConvertTo3DPossible(SdrObject const * pObj, bool& rAny3D,
        bool& rGroupSelected) const
    {
        if(pObj)
        {
            if(dynamic_cast< const E3dObject* >(pObj) !=  nullptr)
            {
                rAny3D = true;
            }
            else
            {
                if(pObj->IsGroupObject())
                {
                    SdrObjListIter aIter(*pObj, SdrIterMode::DeepNoGroups);
                    while(aIter.IsMore())
                    {
                        SdrObject* pNewObj = aIter.Next();
                        ImpIsConvertTo3DPossible(pNewObj, rAny3D, rGroupSelected);
                    }
                    rGroupSelected = true;
                }
            }
        }
    }

    void E3dView::ImpChangeSomeAttributesFor3DConversion(SdrObject* pObj)
    {
        if(dynamic_cast<const SdrTextObj*>( pObj) !=  nullptr)
        {
            const SfxItemSet& rSet = pObj->GetMergedItemSet();
            const SvxColorItem& rTextColorItem = rSet.Get(EE_CHAR_COLOR);
            if(rTextColorItem.GetValue() == COL_BLACK)
            {
                //For black text objects, the color set to gray
                if(pObj->getSdrPageFromSdrObject())
                {
                    // if black is only default attribute from
                    // pattern set it hard so that it is used in undo.
                    pObj->SetMergedItem(SvxColorItem(COL_BLACK, EE_CHAR_COLOR));

                    // add undo now
                    if( GetModel()->IsUndoEnabled() )
                        AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoAttrObject(*pObj));
                }

                pObj->SetMergedItem(SvxColorItem(COL_GRAY, EE_CHAR_COLOR));
            }
        }
    }

    void E3dView::ImpChangeSomeAttributesFor3DConversion2(SdrObject* pObj)
    {
        if(dynamic_cast<const SdrPathObj*>( pObj) !=  nullptr)
        {
            const SfxItemSet& rSet = pObj->GetMergedItemSet();
            sal_Int32 nLineWidth = rSet.Get(XATTR_LINEWIDTH).GetValue();
            drawing::LineStyle eLineStyle = rSet.Get(XATTR_LINESTYLE).GetValue();
            drawing::FillStyle eFillStyle = rSet.Get(XATTR_FILLSTYLE).GetValue();

            if(static_cast<SdrPathObj*>(pObj)->IsClosed()
                && eLineStyle == drawing::LineStyle_SOLID
                && !nLineWidth
                && eFillStyle != drawing::FillStyle_NONE)
            {
                if(pObj->getSdrPageFromSdrObject() && GetModel()->IsUndoEnabled() )
                {
                    AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoAttrObject(*pObj));
                }

                pObj->SetMergedItem(XLineStyleItem(drawing::LineStyle_NONE));
                pObj->SetMergedItem(XLineWidthItem(0));
            }
        }
    }

    void E3dView::ImpCreateSingle3DObjectFlat(E3dScene* pScene, SdrObject* pObj, bool bExtrude, double fDepth, basegfx::B2DHomMatrix const & rLatheMat)
    {
        // Single PathObject, transform this
        SdrPathObj* pPath = dynamic_cast<SdrPathObj*>( pObj );

        if(pPath)
        {
            E3dDefaultAttributes aDefault = Get3DDefaultAttributes();

            if(bExtrude)
            {
                aDefault.SetDefaultExtrudeCharacterMode(true);
            }
            else
            {
                aDefault.SetDefaultLatheCharacterMode(true);
            }

            // Get Itemset of the original object
            SfxItemSet aSet(pObj->GetMergedItemSet());

            drawing::FillStyle eFillStyle = aSet.Get(XATTR_FILLSTYLE).GetValue();

            // line style turned off
            aSet.Put(XLineStyleItem(drawing::LineStyle_NONE));

            //Determining if FILL_Attribut is set.
            if(!pPath->IsClosed() || eFillStyle == drawing::FillStyle_NONE)
            {
                // This SdrPathObj is not filled, leave the front and rear face out.
                // Moreover, a two-sided representation necessary.
                aDefault.SetDefaultExtrudeCloseFront(false);
                aDefault.SetDefaultExtrudeCloseBack(false);

                aSet.Put(makeSvx3DDoubleSidedItem(true));

                // Set fill attribute
                aSet.Put(XFillStyleItem(drawing::FillStyle_SOLID));

                // Fill color must be the color line, because the object was
                // previously just a line
                Color aColorLine = aSet.Get(XATTR_LINECOLOR).GetColorValue();
                aSet.Put(XFillColorItem(OUString(), aColorLine));
            }

            // Create a new extrude object
            E3dObject* p3DObj = nullptr;
            if(bExtrude)
            {
                p3DObj = new E3dExtrudeObj(pObj->getSdrModelFromSdrObject(), aDefault, pPath->GetPathPoly(), fDepth);
            }
            else
            {
                basegfx::B2DPolyPolygon aPolyPoly2D(pPath->GetPathPoly());
                aPolyPoly2D.transform(rLatheMat);
                p3DObj = new E3dLatheObj(pObj->getSdrModelFromSdrObject(), aDefault, aPolyPoly2D);
            }

            // Set attribute
            if(p3DObj)
            {
                p3DObj->NbcSetLayer(pObj->GetLayer());

                p3DObj->SetMergedItemSet(aSet);

                p3DObj->NbcSetStyleSheet(pObj->GetStyleSheet(), true);

                // Insert a new extrude object
                pScene->InsertObject(p3DObj);
            }
        }
    }

    void E3dView::ImpCreate3DObject(E3dScene* pScene, SdrObject* pObj, bool bExtrude, double fDepth, basegfx::B2DHomMatrix const & rLatheMat)
    {
        if(pObj)
        {
            // change text color attribute for not so dark colors
            if(pObj->IsGroupObject())
            {
                SdrObjListIter aIter(*pObj, SdrIterMode::DeepWithGroups);
                while(aIter.IsMore())
                {
                    SdrObject* pGroupMember = aIter.Next();
                    ImpChangeSomeAttributesFor3DConversion(pGroupMember);
                }
            }
            else
                ImpChangeSomeAttributesFor3DConversion(pObj);

            // convert completely to path objects
            SdrObject* pNewObj1 = pObj->ConvertToPolyObj(false, false);

            if(pNewObj1)
            {
                // change text color attribute for not so dark colors
                if(pNewObj1->IsGroupObject())
                {
                    SdrObjListIter aIter(*pNewObj1, SdrIterMode::DeepWithGroups);
                    while(aIter.IsMore())
                    {
                        SdrObject* pGroupMember = aIter.Next();
                        ImpChangeSomeAttributesFor3DConversion2(pGroupMember);
                    }
                }
                else
                    ImpChangeSomeAttributesFor3DConversion2(pNewObj1);

                // convert completely to path objects
                SdrObject* pNewObj2 = pObj->ConvertToContourObj(pNewObj1, true);

                if(pNewObj2)
                {
                    // add all to flat scene
                    if(pNewObj2->IsGroupObject())
                    {
                        SdrObjListIter aIter(*pNewObj2, SdrIterMode::DeepWithGroups);
                        while(aIter.IsMore())
                        {
                            SdrObject* pGroupMember = aIter.Next();
                            ImpCreateSingle3DObjectFlat(pScene, pGroupMember, bExtrude, fDepth, rLatheMat);
                        }
                    }
                    else
                        ImpCreateSingle3DObjectFlat(pScene, pNewObj2, bExtrude, fDepth, rLatheMat);

                    // delete object in between
                    if(pNewObj2 != pObj && pNewObj2 != pNewObj1 && pNewObj2)
                        SdrObject::Free( pNewObj2 );
                }

                // delete object in between
                if(pNewObj1 != pObj && pNewObj1)
                    SdrObject::Free( pNewObj1 );
            }
        }
    }

    void E3dView::ConvertMarkedObjTo3D(bool bExtrude, const basegfx::B2DPoint& rPnt1, const basegfx::B2DPoint& rPnt2)
    {
        if(!AreObjectsMarked())
            return;

        // Create undo
        if(bExtrude)
            BegUndo(SvxResId(RID_SVX_3D_UNDO_EXTRUDE));
        else
            BegUndo(SvxResId(RID_SVX_3D_UNDO_LATHE));

        SdrModel& rSdrModel(GetSdrMarkByIndex(0)->GetMarkedSdrObj()->getSdrModelFromSdrObject());

        // Create a new scene for the created 3D object
        E3dScene* pScene = new E3dScene(rSdrModel);

        // Determine rectangle and possibly correct it
        tools::Rectangle aRect = GetAllMarkedRect();
        if(aRect.GetWidth() <= 1)
            aRect.SetSize(Size(500, aRect.GetHeight()));
        if(aRect.GetHeight() <= 1)
            aRect.SetSize(Size(aRect.GetWidth(), 500));

        // Determine the depth relative to the size of the selection
        double fDepth = 0.0;
        double fRot3D = 0.0;
        basegfx::B2DHomMatrix aLatheMat;

        if(bExtrude)
        {
            double fW = static_cast<double>(aRect.GetWidth());
            double fH = static_cast<double>(aRect.GetHeight());
            fDepth = sqrt(fW*fW + fH*fH) / 6.0;
        }
        if(!bExtrude)
        {
            // Create transformation for the polygons rotating body
            if (rPnt1 != rPnt2)
            {
                // Rotation around control point #1 with set angle
                // for 3D coordinates
                basegfx::B2DPoint aDiff(rPnt1 - rPnt2);
                fRot3D = atan2(aDiff.getY(), aDiff.getX()) - F_PI2;

                if(basegfx::fTools::equalZero(fabs(fRot3D)))
                    fRot3D = 0.0;

                if(fRot3D != 0.0)
                {
                    aLatheMat = basegfx::utils::createRotateAroundPoint(rPnt2, -fRot3D)
                        * aLatheMat;
                }
            }

            if (rPnt2.getX() != 0.0)
            {
                // Translation to Y=0 - axis
                aLatheMat.translate(-rPnt2.getX(), 0.0);
            }
            else
            {
                aLatheMat.translate(static_cast<double>(-aRect.Left()), 0.0);
            }

            // Form the inverse matrix to determine the target expansion
            basegfx::B2DHomMatrix aInvLatheMat(aLatheMat);
            aInvLatheMat.invert();

            // SnapRect extension enables mirroring in the axis of rotation
            for(size_t a=0; a<GetMarkedObjectCount(); ++a)
            {
                SdrMark* pMark = GetSdrMarkByIndex(a);
                SdrObject* pObj = pMark->GetMarkedSdrObj();
                tools::Rectangle aTurnRect = pObj->GetSnapRect();
                basegfx::B2DPoint aRot;
                Point aRotPnt;

                aRot = basegfx::B2DPoint(aTurnRect.Left(), -aTurnRect.Top());
                aRot *= aLatheMat;
                aRot.setX(-aRot.getX());
                aRot *= aInvLatheMat;
                aRotPnt = Point(static_cast<long>(aRot.getX() + 0.5), static_cast<long>(-aRot.getY() - 0.5));
                aRect.Union(tools::Rectangle(aRotPnt, aRotPnt));

                aRot = basegfx::B2DPoint(aTurnRect.Left(), -aTurnRect.Bottom());
                aRot *= aLatheMat;
                aRot.setX(-aRot.getX());
                aRot *= aInvLatheMat;
                aRotPnt = Point(static_cast<long>(aRot.getX() + 0.5), static_cast<long>(-aRot.getY() - 0.5));
                aRect.Union(tools::Rectangle(aRotPnt, aRotPnt));

                aRot = basegfx::B2DPoint(aTurnRect.Right(), -aTurnRect.Top());
                aRot *= aLatheMat;
                aRot.setX(-aRot.getX());
                aRot *= aInvLatheMat;
                aRotPnt = Point(static_cast<long>(aRot.getX() + 0.5), static_cast<long>(-aRot.getY() - 0.5));
                aRect.Union(tools::Rectangle(aRotPnt, aRotPnt));

                aRot = basegfx::B2DPoint(aTurnRect.Right(), -aTurnRect.Bottom());
                aRot *= aLatheMat;
                aRot.setX(-aRot.getX());
                aRot *= aInvLatheMat;
                aRotPnt = Point(static_cast<long>(aRot.getX() + 0.5), static_cast<long>(-aRot.getY() - 0.5));
                aRect.Union(tools::Rectangle(aRotPnt, aRotPnt));
            }
        }

        // Walk through the selection and convert it into 3D, complete with
        // Conversion to SdrPathObject, also fonts
        for(size_t a=0; a<GetMarkedObjectCount(); ++a)
        {
            SdrMark* pMark = GetSdrMarkByIndex(a);
            SdrObject* pObj = pMark->GetMarkedSdrObj();

            ImpCreate3DObject(pScene, pObj, bExtrude, fDepth, aLatheMat);
        }

        if(pScene->GetSubList() && pScene->GetSubList()->GetObjCount() != 0)
        {
            // Arrange all created objects by depth
            if(bExtrude)
                DoDepthArrange(pScene, fDepth);

            // Center 3D objects in the middle of the overall rectangle
            basegfx::B3DPoint aCenter(pScene->GetBoundVolume().getCenter());
            basegfx::B3DHomMatrix aMatrix;

            aMatrix.translate(-aCenter.getX(), -aCenter.getY(), -aCenter.getZ());
            pScene->SetTransform(aMatrix * pScene->GetTransform());

            // Initialize scene
            pScene->NbcSetSnapRect(aRect);
            basegfx::B3DRange aBoundVol = pScene->GetBoundVolume();
            InitScene(pScene, static_cast<double>(aRect.GetWidth()), static_cast<double>(aRect.GetHeight()), aBoundVol.getDepth());

            // Insert scene instead of the first selected object and throw away
            // all the old objects
            SdrObject* pRepObj = GetMarkedObjectByIndex(0);
            SdrPageView* pPV = GetSdrPageViewOfMarkedByIndex(0);
            MarkObj(pRepObj, pPV, true);
            ReplaceObjectAtView(pRepObj, *pPV, pScene, false);
            DeleteMarked();
            MarkObj(pScene, pPV);

            // Rotate Rotation body around the axis of rotation
            basegfx::B3DHomMatrix aRotate;

            if(!bExtrude && fRot3D != 0.0)
            {
                aRotate.rotate(0.0, 0.0, fRot3D);
            }

            // Set default rotation
            aRotate.rotate(DEG2RAD(20.0), 0.0, 0.0);

            if(!aRotate.isIdentity())
            {
                pScene->SetTransform(aRotate * pScene->GetTransform());
            }

            // Invalid SnapRects of objects
            pScene->SetSnapRect(aRect);
        }
        else
        {
            // No 3D object was created, throw away everything
            // always use SdrObject::Free(...) for SdrObjects (!)
            SdrObject* pTemp(pScene);
            SdrObject::Free(pTemp);
        }

        EndUndo();
    }

    //Arrange all created extrude objects by depth

    struct E3dDepthNeighbour
    {
        E3dExtrudeObj*              mpObj;
        basegfx::B2DPolyPolygon     maPreparedPolyPolygon;

        E3dDepthNeighbour(E3dExtrudeObj* pObj, basegfx::B2DPolyPolygon const & rPreparedPolyPolygon)
        :   mpObj(pObj),
            maPreparedPolyPolygon(rPreparedPolyPolygon)
        {
        }
    };

    struct E3dDepthLayer
    {
        E3dDepthLayer*              mpDown;
        std::vector<E3dDepthNeighbour> mvNeighbours;

        E3dDepthLayer()
        :   mpDown(nullptr)
        {
        }
    };

    void E3dView::DoDepthArrange(E3dScene const * pScene, double fDepth)
    {
        if(pScene && pScene->GetSubList() && pScene->GetSubList()->GetObjCount() > 1)
        {
            SdrObjList* pSubList = pScene->GetSubList();
            SdrObjListIter aIter(pSubList, SdrIterMode::Flat);
            E3dDepthLayer* pBaseLayer = nullptr;
            E3dDepthLayer* pLayer = nullptr;
            sal_Int32 nNumLayers = 0;

            while(aIter.IsMore())
            {
                E3dExtrudeObj* pExtrudeObj = dynamic_cast< E3dExtrudeObj* >(aIter.Next());

                if(pExtrudeObj)
                {
                    const basegfx::B2DPolyPolygon aExtrudePoly(
                        basegfx::utils::prepareForPolygonOperation(pExtrudeObj->GetExtrudePolygon()));
                    const SfxItemSet& rLocalSet = pExtrudeObj->GetMergedItemSet();
                    const drawing::FillStyle eLocalFillStyle = rLocalSet.Get(XATTR_FILLSTYLE).GetValue();
                    const Color aLocalColor = rLocalSet.Get(XATTR_FILLCOLOR).GetColorValue();

                    // sort in ExtrudeObj
                    if(pLayer)
                    {
                        // do we have overlap with an object of this layer?
                        bool bOverlap(false);
                        auto itAct = pLayer->mvNeighbours.begin();

                        while(!bOverlap && itAct != pLayer->mvNeighbours.end())
                        {
                            // do itAct->mpObj and pExtrudeObj overlap? Check by
                            // using logical AND clipping
                            const basegfx::B2DPolyPolygon aAndPolyPolygon(
                                basegfx::utils::solvePolygonOperationAnd(
                                    aExtrudePoly,
                                    itAct->maPreparedPolyPolygon));

                            bOverlap = (0 != aAndPolyPolygon.count());

                            if(bOverlap)
                            {
                                // second criteria: is another fillstyle or color used?
                                const SfxItemSet& rCompareSet = itAct->mpObj->GetMergedItemSet();

                                drawing::FillStyle eCompareFillStyle = rCompareSet.Get(XATTR_FILLSTYLE).GetValue();

                                if(eLocalFillStyle == eCompareFillStyle)
                                {
                                    if(eLocalFillStyle == drawing::FillStyle_SOLID)
                                    {
                                        Color aCompareColor = rCompareSet.Get(XATTR_FILLCOLOR).GetColorValue();

                                        if(aCompareColor == aLocalColor)
                                        {
                                            bOverlap = false;
                                        }
                                    }
                                    else if(eLocalFillStyle == drawing::FillStyle_NONE)
                                    {
                                        bOverlap = false;
                                    }
                                }
                            }

                            ++itAct;
                        }

                        if(bOverlap)
                        {
                            // yes, start a new layer
                            pLayer->mpDown = new E3dDepthLayer;
                            pLayer = pLayer->mpDown;
                            nNumLayers++;
                            pLayer->mvNeighbours.emplace_back(pExtrudeObj, aExtrudePoly);
                        }
                        else
                        {
                            // no, add to current layer
                            pLayer->mvNeighbours.emplace(pLayer->mvNeighbours.begin(), pExtrudeObj, aExtrudePoly);
                        }
                    }
                    else
                    {
                        // first layer ever
                        pBaseLayer = new E3dDepthLayer;
                        pLayer = pBaseLayer;
                        nNumLayers++;
                        pLayer->mvNeighbours.emplace_back(pExtrudeObj, aExtrudePoly);
                    }
                }
            }

            // number of layers is done
            if(nNumLayers > 1)
            {
                // need to be arranged
                double fMinDepth = fDepth * 0.8;
                double fStep = (fDepth - fMinDepth) / static_cast<double>(nNumLayers);
                pLayer = pBaseLayer;

                while(pLayer)
                {
                    // move along layer
                    auto itAct = pLayer->mvNeighbours.begin();

                    while(itAct != pLayer->mvNeighbours.end())
                    {
                        // adapt extrude value
                        itAct->mpObj->SetMergedItem(SfxUInt32Item(SDRATTR_3DOBJ_DEPTH, sal_uInt32(fMinDepth + 0.5)));

                        // next
                        ++itAct;
                    }

                    // next layer
                    pLayer = pLayer->mpDown;
                    fMinDepth += fStep;
                }
            }

            // cleanup
            while(pBaseLayer)
            {
                pLayer = pBaseLayer->mpDown;
                delete pBaseLayer;
                pBaseLayer = pLayer;
            }
        }
    }

    // Start drag, create for 3D objects before possibly drag method

    bool E3dView::BegDragObj(const Point& rPnt, OutputDevice* pOut,
        SdrHdl* pHdl, short nMinMov,
        SdrDragMethod* pForcedMeth)
    {
        if(Is3DRotationCreationActive() && GetMarkedObjectCount())
        {
            // Determine all selected polygons and return the mirrored helper overlay
            mpMirrorOverlay->SetMirrorAxis(maRef1, maRef2);
        }
        else
        {
            bool bOwnActionNecessary;
            if (pHdl == nullptr)
            {
               bOwnActionNecessary = true;
            }
            else if (pHdl->IsVertexHdl() || pHdl->IsCornerHdl())
            {
               bOwnActionNecessary = true;
            }
            else
            {
               bOwnActionNecessary = false;
            }

            if(bOwnActionNecessary && GetMarkedObjectCount() > 0)
            {
                E3dDragConstraint eConstraint = E3dDragConstraint::XYZ;
                bool bThereAreRootScenes = false;
                bool bThereAre3DObjects = false;
                const size_t nCnt = GetMarkedObjectCount();
                for(size_t nObjs = 0; nObjs < nCnt; ++nObjs)
                {
                    SdrObject *pObj = GetMarkedObjectByIndex(nObjs);
                    if(pObj)
                    {
                        if(nullptr != dynamic_cast< const E3dScene* >(pObj) && static_cast< E3dScene* >(pObj)->getRootE3dSceneFromE3dObject() == pObj)
                        {
                            bThereAreRootScenes = true;
                        }

                        if(dynamic_cast< const E3dObject* >(pObj) !=  nullptr)
                        {
                            bThereAre3DObjects = true;
                        }
                    }
                }
                if( bThereAre3DObjects )
                {
                    meDragHdl = ( pHdl == nullptr ? SdrHdlKind::Move : pHdl->GetKind() );
                    switch ( meDragMode )
                    {
                        case SdrDragMode::Rotate:
                        case SdrDragMode::Shear:
                        {
                            switch ( meDragHdl )
                            {
                                case SdrHdlKind::Left:
                                case SdrHdlKind::Right:
                                {
                                    eConstraint = E3dDragConstraint::X;
                                }
                                break;

                                case SdrHdlKind::Upper:
                                case SdrHdlKind::Lower:
                                {
                                    eConstraint = E3dDragConstraint::Y;
                                }
                                break;

                                case SdrHdlKind::UpperLeft:
                                case SdrHdlKind::UpperRight:
                                case SdrHdlKind::LowerLeft:
                                case SdrHdlKind::LowerRight:
                                {
                                    eConstraint = E3dDragConstraint::Z;
                                }
                                break;
                                default: break;
                            }

                            // do not mask the allowed rotations
                            eConstraint = E3dDragConstraint(eConstraint& eDragConstraint);
                            pForcedMeth = new E3dDragRotate(*this, GetMarkedObjectList(), eConstraint, IsSolidDragging());
                        }
                        break;

                        case SdrDragMode::Move:
                        {
                            if(!bThereAreRootScenes)
                            {
                                pForcedMeth = new E3dDragMove(*this, GetMarkedObjectList(), meDragHdl, eConstraint, IsSolidDragging());
                            }
                        }
                        break;

                        // later on
                        case SdrDragMode::Mirror:
                        case SdrDragMode::Crook:
                        case SdrDragMode::Transparence:
                        case SdrDragMode::Gradient:
                        default:
                        {
                        }
                        break;
                    }
                }
            }
        }
        return SdrView::BegDragObj(rPnt, pOut, pHdl, nMinMov, pForcedMeth);
    }

    // Set current 3D drawing object, create the scene for this
    E3dScene* E3dView::SetCurrent3DObj(E3dObject* p3DObj)
    {
        DBG_ASSERT(p3DObj != nullptr, "Who puts in a NULL-pointer here");

        // get transformed BoundVolume of the object
        basegfx::B3DRange aVolume(p3DObj->GetBoundVolume());
        aVolume.transform(p3DObj->GetTransform());
        double fW(aVolume.getWidth());
        double fH(aVolume.getHeight());

        tools::Rectangle aRect(0,0, static_cast<long>(fW), static_cast<long>(fH));

        E3dScene* pScene = new E3dScene(p3DObj->getSdrModelFromSdrObject());

        InitScene(pScene, fW, fH, aVolume.getMaxZ() + ((fW + fH) / 4.0));

        pScene->InsertObject(p3DObj);
        pScene->NbcSetSnapRect(aRect);

        return pScene;
    }

    void E3dView::InitScene(E3dScene* pScene, double fW, double fH, double fCamZ)
    {
        Camera3D aCam(pScene->GetCamera());

        aCam.SetAutoAdjustProjection(false);
        aCam.SetViewWindow(- fW / 2, - fH / 2, fW, fH);
        basegfx::B3DPoint aLookAt;

        double fDefaultCamPosZ = GetDefaultCamPosZ();
        basegfx::B3DPoint aCamPos(0.0, 0.0, fCamZ < fDefaultCamPosZ ? fDefaultCamPosZ : fCamZ);

        aCam.SetPosAndLookAt(aCamPos, aLookAt);
        aCam.SetFocalLength(GetDefaultCamFocal());
        aCam.SetDefaults(basegfx::B3DPoint(0.0, 0.0, fDefaultCamPosZ), aLookAt);
        pScene->SetCamera(aCam);
    }

    void E3dView::Start3DCreation()
    {
        if (!GetMarkedObjectCount())
            return;

        //positioned
        long          nOutMin = 0;
        long          nOutMax = 0;
        long          nMinLen = 0;
        long          nObjDst = 0;
        long          nOutHgt = 0;
        OutputDevice* pOut    = GetFirstOutputDevice();

        // first determine representation boundaries
        if (pOut != nullptr)
        {
            nMinLen = pOut->PixelToLogic(Size(0,50)).Height();
            nObjDst = pOut->PixelToLogic(Size(0,20)).Height();

            long nDst = pOut->PixelToLogic(Size(0,10)).Height();

            nOutMin =  -pOut->GetMapMode().GetOrigin().Y();
            nOutMax =  pOut->GetOutputSize().Height() - 1 + nOutMin;
            nOutMin += nDst;
            nOutMax -= nDst;

            if (nOutMax - nOutMin < nDst)
            {
                nOutMin += nOutMax + 1;
                nOutMin /= 2;
                nOutMin -= (nDst + 1) / 2;
                nOutMax  = nOutMin + nDst;
            }

            nOutHgt = nOutMax - nOutMin;

            long nTemp = nOutHgt / 4;
            if (nTemp > nMinLen) nMinLen = nTemp;
        }

        // and then attach the marks at the top and bottom of the object
        basegfx::B2DRange aR;
        for(size_t nMark = 0; nMark < GetMarkedObjectCount(); ++nMark)
        {
            SdrObject* pMark = GetMarkedObjectByIndex(nMark);
            basegfx::B2DPolyPolygon aXPP(pMark->TakeXorPoly());
            aR.expand(basegfx::utils::getRange(aXPP));
        }

        basegfx::B2DPoint aCenter(aR.getCenter());
        long      nMarkHgt = basegfx::fround(aR.getHeight()) - 1;
        long      nHgt     = nMarkHgt + nObjDst * 2;

        if (nHgt < nMinLen) nHgt = nMinLen;

        long nY1 = basegfx::fround(aCenter.getY()) - (nHgt + 1) / 2;
        long nY2 = nY1 + nHgt;

        if (pOut && (nMinLen > nOutHgt)) nMinLen = nOutHgt;
        if (pOut)
        {
            if (nY1 < nOutMin)
            {
                nY1 = nOutMin;
                if (nY2 < nY1 + nMinLen) nY2 = nY1 + nMinLen;
            }
            if (nY2 > nOutMax)
            {
                nY2 = nOutMax;
                if (nY1 > nY2 - nMinLen) nY1 = nY2 - nMinLen;
            }
        }

        maRef1.setX( basegfx::fround(aR.getMinX()) );    // Initial move axis 2/100mm to the left
        maRef1.setY( nY1 );
        maRef2.setX( maRef1.X() );
        maRef2.setY( nY2 );

        // Turn on marks
        SetMarkHandles(nullptr);

        //HMHif (bVis) ShowMarkHdl();
        if (AreObjectsMarked()) MarkListHasChanged();

        // Show mirror polygon IMMEDIATELY
        const SdrHdlList &aHdlList = GetHdlList();
        mpMirrorOverlay.reset(new Impl3DMirrorConstructOverlay(*this));
        mpMirrorOverlay->SetMirrorAxis(aHdlList.GetHdl(SdrHdlKind::Ref1)->GetPos(), aHdlList.GetHdl(SdrHdlKind::Ref2)->GetPos());
    }

    // what happens with a mouse movement when the object is created?

    void E3dView::MovAction(const Point& rPnt)
    {
        if(Is3DRotationCreationActive())
        {
            SdrHdl* pHdl = GetDragHdl();

            if (pHdl)
            {
                SdrHdlKind eHdlKind = pHdl->GetKind();

                // reacts only due to a mirror axis
                if ((eHdlKind == SdrHdlKind::Ref1) ||
                    (eHdlKind == SdrHdlKind::Ref2) ||
                    (eHdlKind == SdrHdlKind::MirrorAxis))
                {
                    const SdrHdlList &aHdlList = GetHdlList ();

                    // delete the mirrored polygon, mirrors the original and draws
                    // it anew
                    SdrView::MovAction (rPnt);
                    mpMirrorOverlay->SetMirrorAxis(
                        aHdlList.GetHdl (SdrHdlKind::Ref1)->GetPos(),
                        aHdlList.GetHdl (SdrHdlKind::Ref2)->GetPos());
                }
            }
            else
            {
                SdrView::MovAction (rPnt);
            }
        }
        else
        {
            SdrView::MovAction (rPnt);
        }
    }

    // The End. Create object and any child objects through ImpCreate3DLathe.
    // With the parameter value sal_True (SDefault: sal_False) is simply a
    // rotation body  created, without letting the user set the position of the
    // axis. It is sufficient with this call, if an object is selected.
    // (No initialization necessary)

    void E3dView::End3DCreation(bool bUseDefaultValuesForMirrorAxes)
    {
        ResetCreationActive();

        if(AreObjectsMarked())
        {
            if(bUseDefaultValuesForMirrorAxes)
            {
                tools::Rectangle aRect = GetAllMarkedRect();
                if(aRect.GetWidth() <= 1)
                    aRect.SetSize(Size(500, aRect.GetHeight()));
                if(aRect.GetHeight() <= 1)
                    aRect.SetSize(Size(aRect.GetWidth(), 500));

                basegfx::B2DPoint aPnt1(aRect.Left(), -aRect.Top());
                basegfx::B2DPoint aPnt2(aRect.Left(), -aRect.Bottom());

                ConvertMarkedObjTo3D(false, aPnt1, aPnt2);
            }
            else
            {
                // Turn off helper overlay
                // Determine from the handle positions and the displacement of
                // the points
                const SdrHdlList &aHdlList = GetHdlList();
                Point aMirrorRef1 = aHdlList.GetHdl(SdrHdlKind::Ref1)->GetPos();
                Point aMirrorRef2 = aHdlList.GetHdl(SdrHdlKind::Ref2)->GetPos();

                basegfx::B2DPoint aPnt1(aMirrorRef1.X(), -aMirrorRef1.Y());
                basegfx::B2DPoint aPnt2(aMirrorRef2.X(), -aMirrorRef2.Y());

                ConvertMarkedObjTo3D(false, aPnt1, aPnt2);
            }
        }
    }

    E3dView::~E3dView ()
    {
    }

    void E3dView::ResetCreationActive ()
    {
        mpMirrorOverlay.reset();
    }

    void E3dView::InitView ()
    {
        eDragConstraint          = E3dDragConstraint::XYZ;
        aDefaultLightColor       = COL_WHITE;
        aDefaultAmbientColor     = COL_BLACK;
        mpMirrorOverlay          = nullptr;
    }

    bool E3dView::IsBreak3DObjPossible() const
    {
        const size_t nCount = GetMarkedObjectCount();

        if (nCount > 0)
        {
            for (size_t i = 0; i < nCount; ++i)
            {
                SdrObject* pObj = GetMarkedObjectByIndex(i);

                if (auto p3dObject = dynamic_cast< E3dObject* >(pObj))
                {
                    if(!p3dObject->IsBreakObjPossible())
                        return false;
                }
                else
                {
                    return false;
                }
            }
        }
        else
        {
            return false;
        }

        return true;
    }

    void E3dView::Break3DObj()
    {
        if(IsBreak3DObjPossible())
        {
            // ALL selected objects are changed
            const size_t nCount = GetMarkedObjectCount();

            BegUndo(SvxResId(RID_SVX_3D_UNDO_BREAK_LATHE));
            for(size_t a=0; a<nCount; ++a)
            {
                E3dObject* pObj = static_cast<E3dObject*>(GetMarkedObjectByIndex(a));
                BreakSingle3DObj(pObj);
            }
            DeleteMarked();
            EndUndo();
        }
    }

    void E3dView::BreakSingle3DObj(E3dObject* pObj)
    {
        if(dynamic_cast< const E3dScene* >(pObj) !=  nullptr)
        {
            SdrObjList* pSubList = pObj->GetSubList();
            SdrObjListIter aIter(pSubList, SdrIterMode::Flat);

            while(aIter.IsMore())
            {
                E3dObject* pSubObj = static_cast<E3dObject*>(aIter.Next());
                BreakSingle3DObj(pSubObj);
            }
        }
        else
        {
            SdrAttrObj* pNewObj = pObj->GetBreakObj();
            if(pNewObj)
            {
                InsertObjectAtView(pNewObj, *GetSdrPageView(), SdrInsertFlags::DONTMARK);
                pNewObj->SetChanged();
                pNewObj->BroadcastObjectChange();
            }
        }
    }

    void E3dView::CheckPossibilities()
    {
        // call parent
        SdrView::CheckPossibilities();

        // Set other flags
        if(bGroupPossible || bUnGroupPossible || bGrpEnterPossible)
        {
            const size_t nMarkCnt = GetMarkedObjectCount();
            bool bCoumpound = false;
            bool b3DObject = false;
            for(size_t nObjs = 0; (nObjs < nMarkCnt) && !bCoumpound; ++nObjs)
            {
                SdrObject *pObj = GetMarkedObjectByIndex(nObjs);
                if(dynamic_cast< const E3dCompoundObject* >(pObj))
                    bCoumpound = true;
                if(dynamic_cast< const E3dObject* >(pObj))
                    b3DObject = true;
            }

            // So far: there are two or more of any objects selected. See if
            // compound objects are involved. If yes, ban grouping.
            if(bGroupPossible && bCoumpound)
                bGroupPossible = false;

            if(bUnGroupPossible && b3DObject)
                bUnGroupPossible = false;

            if(bGrpEnterPossible && bCoumpound)
                bGrpEnterPossible = false;
        }
    }

    /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

↑ V668 There is no sense in testing the 'p3DObj' pointer against null, as the memory was allocated using the 'new' operator. The exception will be generated in the case of memory allocation error.

↑ V560 A part of conditional expression is always true: pNewObj2.

↑ V560 A part of conditional expression is always true: pNewObj1.