/* -*- 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.