/* -*- 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 <svx/svdopath.hxx>
#include <svx/svditer.hxx>
#include <svx/svdmodel.hxx>
#include <svx/svdpagv.hxx>
#include <editeng/colritem.hxx>
#include <editeng/eeitem.hxx>
#include <svx/svdview.hxx>
#include <svx/strings.hrc>
#include <svx/dialmgr.hxx>
#include <svx/obj3d.hxx>
#include <svx/lathe3d.hxx>
#include <extrud3d.hxx>
#include <dragmt3d.hxx>
#include <svx/scene3d.hxx>
#include <svx/view3d.hxx>
#include <svx/svdundo.hxx>
#include <svx/xflclit.hxx>
#include <svx/xlnclit.hxx>
#include <svx/xfillit0.hxx>
#include <svx/xlineit0.hxx>
#include <basegfx/range/b2drange.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/viewcontact.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>
#include <svx/e3dsceneupdater.hxx>
#include <utility>
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)
: mrView(rView),
mpPolygons(nullptr)
{
const SdrMarkList& rMarkList = mrView.GetMarkedObjectList();
mnCount = rMarkList.GetMarkCount();
if(!mnCount)
return;
if(mrView.IsSolidDragging())
{
SdrPageView* pPV = rView.GetSdrPageView();
if(pPV && pPV->PageWindowCount())
{
for(size_t a = 0; a < mnCount; ++a)
{
SdrObject* pObject = rMarkList.GetMark(a)->GetMarkedSdrObj();
if(pObject)
{
// use the view-independent primitive representation (without
// evtl. GridOffset, that may be applied to the DragEntry individually)
pObject->GetViewContact().getViewIndependentPrimitive2DContainer(maFullOverlay);
}
}
}
}
else
{
mpPolygons = new basegfx::B2DPolyPolygon[mnCount];
for(size_t a = 0; a < mnCount; ++a)
{
SdrObject* pObject = rMarkList.GetMark(a)->GetMarkedSdrObj();
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.
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);
const 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
aContent = drawinglayer::primitive2d::Primitive2DContainer {
new drawinglayer::primitive2d::TransformPrimitive2D(aMatrixTransform, std::move(aContent))
};
}
// if we have full overlay from selected objects, embed with 50% transparence, the
// transformation is added to the OverlayPrimitive2DSequenceObject
aContent = drawinglayer::primitive2d::Primitive2DContainer {
new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(std::move(aContent), 0.5)
};
std::unique_ptr<sdr::overlay::OverlayPrimitive2DSequenceObject> pNew(new sdr::overlay::OverlayPrimitive2DSequenceObject(std::move(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(
std::move(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 SdrMarkList& rMarkList = GetMarkedObjectList();
const size_t nCnt = rMarkList.GetMarkCount();
for(size_t nObjs = 0; nObjs < nCnt; ++nObjs)
{
SdrObject *pObj = rMarkList.GetMark(nObjs)->GetMarkedSdrObj();
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 = DynCastE3dObject(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 = rMarkList.GetMark(nObjs)->GetMarkedSdrObj();
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 = rMarkList.GetMark(nObjs)->GetMarkedSdrObj();
if(auto p3DObj = DynCastE3dObject(pObj))
{
// Select object
p3DObj->SetSelected(true);
pScene = p3DObj->getRootE3dSceneFromE3dObject();
}
}
if(nullptr != pScene)
{
// code from parent
rMarkList.ForceSort();
pScene->SetDrawOnlySelected(true);
pScene->SingleObjectPainter(rOut);
pScene->SetDrawOnlySelected(false);
}
// Reset selection flag
for(size_t nObjs = 0; nObjs < nCnt; ++nObjs)
{
SdrObject *pObj = rMarkList.GetMark(nObjs)->GetMarkedSdrObj();
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 SdrMarkList& rMarkList = GetMarkedObjectList();
const size_t nCount(rMarkList.GetMarkCount());
E3dScene *pScene = nullptr;
for(size_t nObjs = 0; nObjs < nCount; ++nObjs)
{
const SdrObject* pObj = rMarkList.GetMark(nObjs)->GetMarkedSdrObj();
if(!bSpecialHandling)
if(auto pCompoundObj = dynamic_cast< const E3dCompoundObject*>(pObj))
{
// if the object is selected, but it's scene not,
// we need special handling
pScene = pCompoundObj->getRootE3dSceneFromE3dObject();
if(nullptr != pScene && !IsObjMarked(pScene))
{
bSpecialHandling = true;
}
}
if(auto p3dObject = DynCastE3dObject(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 = rMarkList.GetMark(nObjs)->GetMarkedSdrObj();
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(rMarkList);
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 = DynCastE3dObject(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);
for (const rtl::Reference<SdrObject>& pSrcOb : *pSrcPg)
{
if(const E3dScene* p3dscene = DynCastE3dScene( pSrcOb.get()))
{
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(DynCastE3dScene(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);
// calculate offset for paste
tools::Rectangle aR = pSrcPg->GetAllObjBoundRect();
Point aDist(aPos - aR.Center());
// Insert sub-objects for scenes
for (const rtl::Reference<SdrObject>& pSrcOb : *pSrcPg)
{
if(const E3dScene* p3dscene = DynCastE3dScene(pSrcOb.get()))
{
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 (const rtl::Reference<SdrObject>& pObj : *pSrcScene->GetSubList())
{
E3dCompoundObject* pCompoundObj = dynamic_cast< E3dCompoundObject* >(pObj.get());
if(pCompoundObj)
{
rtl::Reference<E3dCompoundObject> pNewCompoundObj = SdrObject::Clone(*pCompoundObj, 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 adaptation
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 adaptation
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.get());
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);
const SdrMarkList& rMarkList = GetMarkedObjectList();
for(size_t a=0; !bAny3D && a<rMarkList.GetMarkCount(); ++a)
{
SdrObject *pObj = rMarkList.GetMark(a)->GetMarkedSdrObj();
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)
return;
if(DynCastE3dObject(pObj))
{
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(DynCastSdrTextObj( pObj) == nullptr)
return;
const SfxItemSet& rSet = pObj->GetMergedItemSet();
const SvxColorItem& rTextColorItem = rSet.Get(EE_CHAR_COLOR);
if(rTextColorItem.GetValue() != COL_BLACK)
return;
//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)
{
auto pPathObj = dynamic_cast<const SdrPathObj*>( pObj);
if(!pPathObj)
return;
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(pPathObj->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)
return;
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_Attribute 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
rtl::Reference<E3dObject> p3DObj;
if(bExtrude)
{
p3DObj = new E3dExtrudeObj(pObj->getSdrModelFromSdrObject(), aDefault, pPath->GetPathPoly(), fDepth);
}
else
{
// rLatheMat expects coordinates with y-axis up, pPath uses y-axis down
basegfx::B2DHomMatrix aFlipVerticalMat(1.0, 0.0, 0.0, 0.0, -1.0, 0.0);
basegfx::B2DPolyPolygon aPolyPoly2D(pPath->GetPathPoly());
aPolyPoly2D.transform(aFlipVerticalMat);
aPolyPoly2D.transform(rLatheMat);
// ctor E3dLatheObj expects coordinates with y-axis down
aPolyPoly2D.transform(aFlipVerticalMat);
p3DObj = new E3dLatheObj(pObj->getSdrModelFromSdrObject(), aDefault, std::move(aPolyPoly2D));
}
// Set attribute
p3DObj->NbcSetLayer(pObj->GetLayer());
p3DObj->SetMergedItemSet(aSet);
p3DObj->NbcSetStyleSheet(pObj->GetStyleSheet(), true);
// Insert a new extrude object
pScene->InsertObject(p3DObj.get());
}
void E3dView::ImpCreate3DObject(E3dScene* pScene, SdrObject* pObj, bool bExtrude, double fDepth, basegfx::B2DHomMatrix const & rLatheMat)
{
if(!pObj)
return;
// 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
rtl::Reference<SdrObject> pNewObj1 = pObj->ConvertToPolyObj(false, false);
if(!pNewObj1)
return;
// 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.get());
// convert completely to path objects
rtl::Reference<SdrObject> pNewObj2 = pObj->ConvertToContourObj(pNewObj1.get(), 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.get(), bExtrude, fDepth, rLatheMat);
}
}
void E3dView::ConvertMarkedObjTo3D(bool bExtrude, const basegfx::B2DPoint& rPnt1, const basegfx::B2DPoint& rPnt2)
{
const SdrMarkList& rMarkList = GetMarkedObjectList();
if(rMarkList.GetMarkCount() == 0)
return;
// Create undo
if(bExtrude)
BegUndo(SvxResId(RID_SVX_3D_UNDO_EXTRUDE));
else
BegUndo(SvxResId(RID_SVX_3D_UNDO_LATHE));
SdrModel& rSdrModel(rMarkList.GetMark(0)->GetMarkedSdrObj()->getSdrModelFromSdrObject());
// Create a new scene for the created 3D object
rtl::Reference<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)
{
fDepth = std::hypot(aRect.GetWidth(), aRect.GetHeight()) / 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()) - M_PI_2;
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<rMarkList.GetMarkCount(); ++a)
{
SdrMark* pMark = rMarkList.GetMark(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<tools::Long>(aRot.getX() + 0.5), static_cast<tools::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<tools::Long>(aRot.getX() + 0.5), static_cast<tools::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<tools::Long>(aRot.getX() + 0.5), static_cast<tools::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<tools::Long>(aRot.getX() + 0.5), static_cast<tools::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<rMarkList.GetMarkCount(); ++a)
{
SdrMark* pMark = rMarkList.GetMark(a);
SdrObject* pObj = pMark->GetMarkedSdrObj();
ImpCreate3DObject(pScene.get(), pObj, bExtrude, fDepth, aLatheMat);
}
if(pScene->GetSubList() && pScene->GetSubList()->GetObjCount() != 0)
{
// Arrange all created objects by depth
if(bExtrude)
DoDepthArrange(pScene.get(), 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.get(), 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
SdrMark* pMark = rMarkList.GetMark(0);
if (pMark)
{
SdrObject* pRepObj = pMark->GetMarkedSdrObj();
SdrPageView* pPV = pMark->GetPageView();
MarkObj(pRepObj, pPV, true);
ReplaceObjectAtView(pRepObj, *pPV, pScene.get(), false);
DeleteMarked();
MarkObj(pScene.get(), pPV);
}
// Rotate Rotation body around the axis of rotation
if(!bExtrude && fRot3D != 0.0)
{
basegfx::B3DHomMatrix aRotate;
aRotate.rotate(0.0, 0.0, fRot3D);
pScene->SetTransform(aRotate * pScene->GetTransform());
}
// Set default rotation
{
basegfx::B3DHomMatrix aRotate;
aRotate.rotate(basegfx::deg2rad(20.0), 0.0, 0.0);
// E3DModifySceneSnapRectUpdater updates the 2D representation of the scene.
// It prepares things in ctor and acts in dtor.
E3DModifySceneSnapRectUpdater aUpdater(pScene->getSdrObjectFromSdrObjList());
pScene->SetTransform(aRotate * pScene->GetTransform());
}
}
else
pScene.clear();
EndUndo();
}
//Arrange all created extrude objects by depth
namespace {
struct E3dDepthNeighbour
{
E3dExtrudeObj* mpObj;
basegfx::B2DPolyPolygon maPreparedPolyPolygon;
E3dDepthNeighbour(E3dExtrudeObj* pObj, basegfx::B2DPolyPolygon aPreparedPolyPolygon)
: mpObj(pObj),
maPreparedPolyPolygon(std::move(aPreparedPolyPolygon))
{
}
};
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))
return;
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);
for(const auto& rAct : pLayer->mvNeighbours)
{
// do rAct.mpObj and pExtrudeObj overlap? Check by
// using logical AND clipping
const basegfx::B2DPolyPolygon aAndPolyPolygon(
basegfx::utils::solvePolygonOperationAnd(
aExtrudePoly,
rAct.maPreparedPolyPolygon));
if(aAndPolyPolygon.count() != 0)
{
// second criteria: is another fillstyle or color used?
const SfxItemSet& rCompareSet = rAct.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)
{
continue;
}
}
else if(eLocalFillStyle == drawing::FillStyle_NONE)
{
continue;
}
}
bOverlap = true;
break;
}
}
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
for(auto& rAct : pLayer->mvNeighbours)
{
// adapt extrude value
rAct.mpObj->SetMergedItem(SfxUInt32Item(SDRATTR_3DOBJ_DEPTH, sal_uInt32(fMinDepth + 0.5)));
}
// 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)
{
const SdrMarkList& rMarkList = GetMarkedObjectList();
if(Is3DRotationCreationActive() && rMarkList.GetMarkCount())
{
// 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 && rMarkList.GetMarkCount() > 0)
{
E3dDragConstraint eConstraint = E3dDragConstraint::XYZ;
bool bThereAreRootScenes = false;
bool bThereAre3DObjects = false;
const size_t nCnt = rMarkList.GetMarkCount();
for(size_t nObjs = 0; nObjs < nCnt; ++nObjs)
{
SdrObject *pObj = rMarkList.GetMark(nObjs)->GetMarkedSdrObj();
if(pObj)
{
if( const E3dScene* pScene = DynCastE3dScene(pObj) )
if( pScene->getRootE3dSceneFromE3dObject() == pObj )
bThereAreRootScenes = true;
if(DynCastE3dObject(pObj))
{
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::XYZ;
pForcedMeth = new E3dDragRotate(*this, rMarkList, eConstraint, IsSolidDragging());
}
break;
case SdrDragMode::Move:
{
if(!bThereAreRootScenes)
{
pForcedMeth = new E3dDragMove(*this, rMarkList, 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
rtl::Reference<E3dScene> E3dView::SetCurrent3DObj(E3dObject* p3DObj)
{
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<tools::Long>(fW), static_cast<tools::Long>(fH));
rtl::Reference<E3dScene> pScene = new E3dScene(p3DObj->getSdrModelFromSdrObject());
InitScene(pScene.get(), 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());
pScene->SetCamera(aCam);
}
void E3dView::Start3DCreation()
{
const SdrMarkList& rMarkList = GetMarkedObjectList();
if (!rMarkList.GetMarkCount())
return;
//positioned
tools::Long nOutMin = 0;
tools::Long nOutMax = 0;
tools::Long nMinLen = 0;
tools::Long nObjDst = 0;
tools::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();
tools::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;
tools::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 < rMarkList.GetMarkCount(); ++nMark)
{
SdrObject* pMark = rMarkList.GetMark(nMark)->GetMarkedSdrObj();
basegfx::B2DPolyPolygon aXPP(pMark->TakeXorPoly());
aR.expand(basegfx::utils::getRange(aXPP));
}
basegfx::B2DPoint aCenter(aR.getCenter());
tools::Long nMarkHgt = basegfx::fround<tools::Long>(aR.getHeight()) - 1;
tools::Long nHgt = nMarkHgt + nObjDst * 2;
if (nHgt < nMinLen) nHgt = nMinLen;
tools::Long nY1 = basegfx::fround<tools::Long>(aCenter.getY()) - (nHgt + 1) / 2;
tools::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<tools::Long>(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 (rMarkList.GetMarkCount() != 0) 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();
const SdrMarkList& rMarkList = GetMarkedObjectList();
if(rMarkList.GetMarkCount() == 0)
return;
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 ()
{
mpMirrorOverlay = nullptr;
}
bool E3dView::IsBreak3DObjPossible() const
{
const SdrMarkList& rMarkList = GetMarkedObjectList();
const size_t nCount = rMarkList.GetMarkCount();
if (nCount > 0)
{
for (size_t i = 0; i < nCount; ++i)
{
SdrObject* pObj = rMarkList.GetMark(i)->GetMarkedSdrObj();
if (auto p3dObject = DynCastE3dObject(pObj))
{
if(!p3dObject->IsBreakObjPossible())
return false;
}
else
{
return false;
}
}
}
else
{
return false;
}
return true;
}
void E3dView::Break3DObj()
{
if(!IsBreak3DObjPossible())
return;
const SdrMarkList& rMarkList = GetMarkedObjectList();
// ALL selected objects are changed
const size_t nCount = rMarkList.GetMarkCount();
BegUndo(SvxResId(RID_SVX_3D_UNDO_BREAK_LATHE));
for(size_t a=0; a<nCount; ++a)
{
E3dObject* pObj = static_cast<E3dObject*>(rMarkList.GetMark(a)->GetMarkedSdrObj());
BreakSingle3DObj(pObj);
}
DeleteMarked();
EndUndo();
}
void E3dView::BreakSingle3DObj(E3dObject* pObj)
{
if(DynCastE3dScene(pObj))
{
SdrObjList* pSubList = pObj->GetSubList();
SdrObjListIter aIter(pSubList, SdrIterMode::Flat);
while(aIter.IsMore())
{
E3dObject* pSubObj = static_cast<E3dObject*>(aIter.Next());
BreakSingle3DObj(pSubObj);
}
}
else
{
rtl::Reference<SdrAttrObj> pNewObj = pObj->GetBreakObj();
if (pNewObj)
{
if (InsertObjectAtView(pNewObj.get(), *GetSdrPageView(), SdrInsertFlags::DONTMARK))
{
pNewObj->SetChanged();
pNewObj->BroadcastObjectChange();
}
}
}
}
void E3dView::CheckPossibilities()
{
// call parent
SdrView::CheckPossibilities();
// Set other flags
if(!(m_bGroupPossible || m_bUnGroupPossible || m_bGrpEnterPossible))
return;
const SdrMarkList& rMarkList = GetMarkedObjectList();
const size_t nMarkCnt = rMarkList.GetMarkCount();
bool bCompound = false;
bool b3DObject = false;
for(size_t nObjs = 0; (nObjs < nMarkCnt) && !bCompound; ++nObjs)
{
SdrObject *pObj = rMarkList.GetMark(nObjs)->GetMarkedSdrObj();
if(dynamic_cast< const E3dCompoundObject* >(pObj))
bCompound = true;
if(DynCastE3dObject(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(m_bGroupPossible && bCompound)
m_bGroupPossible = false;
if(m_bUnGroupPossible && b3DObject)
m_bUnGroupPossible = false;
if(m_bGrpEnterPossible && bCompound)
m_bGrpEnterPossible = false;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V530 The return value of function 'Union' is required to be utilized.
↑ V530 The return value of function 'Union' is required to be utilized.
↑ V530 The return value of function 'Union' is required to be utilized.
↑ V530 The return value of function 'Union' is required to be utilized.
↑ V530 The return value of function 'Union' is required to be utilized.