diff options
Diffstat (limited to 'svx/source/engine3d')
-rw-r--r-- | svx/source/engine3d/camera3d.cxx | 180 | ||||
-rw-r--r-- | svx/source/engine3d/cube3d.cxx | 157 | ||||
-rw-r--r-- | svx/source/engine3d/deflt3d.cxx | 56 | ||||
-rw-r--r-- | svx/source/engine3d/dragmt3d.cxx | 732 | ||||
-rw-r--r-- | svx/source/engine3d/e3dsceneupdater.cxx | 111 | ||||
-rw-r--r-- | svx/source/engine3d/e3dundo.cxx | 91 | ||||
-rw-r--r-- | svx/source/engine3d/extrud3d.cxx | 223 | ||||
-rw-r--r-- | svx/source/engine3d/float3d.cxx | 2949 | ||||
-rw-r--r-- | svx/source/engine3d/helperhittest3d.cxx | 277 | ||||
-rw-r--r-- | svx/source/engine3d/helperminimaldepth3d.cxx | 201 | ||||
-rw-r--r-- | svx/source/engine3d/helperminimaldepth3d.hxx | 48 | ||||
-rw-r--r-- | svx/source/engine3d/lathe3d.cxx | 205 | ||||
-rw-r--r-- | svx/source/engine3d/obj3d.cxx | 625 | ||||
-rw-r--r-- | svx/source/engine3d/objfac3d.cxx | 73 | ||||
-rw-r--r-- | svx/source/engine3d/polygn3d.cxx | 248 | ||||
-rw-r--r-- | svx/source/engine3d/scene3d.cxx | 909 | ||||
-rw-r--r-- | svx/source/engine3d/sphere3d.cxx | 153 | ||||
-rw-r--r-- | svx/source/engine3d/svx3ditems.cxx | 266 | ||||
-rw-r--r-- | svx/source/engine3d/view3d.cxx | 1599 | ||||
-rw-r--r-- | svx/source/engine3d/view3d1.cxx | 177 | ||||
-rw-r--r-- | svx/source/engine3d/viewpt3d2.cxx | 153 |
21 files changed, 9433 insertions, 0 deletions
diff --git a/svx/source/engine3d/camera3d.cxx b/svx/source/engine3d/camera3d.cxx new file mode 100644 index 000000000..abb7b3991 --- /dev/null +++ b/svx/source/engine3d/camera3d.cxx @@ -0,0 +1,180 @@ +/* -*- 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/camera3d.hxx> + +Camera3D::Camera3D(const basegfx::B3DPoint& rPos, const basegfx::B3DPoint& rLookAt, + double fFocalLen) : + fBankAngle(0), + bAutoAdjustProjection(true) +{ + SetPosition(rPos); + SetLookAt(rLookAt); + SetFocalLength(fFocalLen); +} + +Camera3D::Camera3D() + : fFocalLength(35.0) + , fBankAngle(0.0) + , bAutoAdjustProjection(false) +{ +} + +// Set ViewWindow and adjust PRP + +void Camera3D::SetViewWindow(double fX, double fY, double fW, double fH) +{ + Viewport3D::SetViewWindow(fX, fY, fW, fH); + if ( bAutoAdjustProjection ) + SetFocalLength(fFocalLength); +} + +void Camera3D::SetPosition(const basegfx::B3DPoint& rNewPos) +{ + if ( rNewPos != aPosition ) + { + aPosition = rNewPos; + SetVRP(aPosition); + SetVPN(aPosition - aLookAt); + SetBankAngle(fBankAngle); + } +} + +void Camera3D::SetLookAt(const basegfx::B3DPoint& rNewLookAt) +{ + if ( rNewLookAt != aLookAt ) + { + aLookAt = rNewLookAt; + SetVPN(aPosition - aLookAt); + SetBankAngle(fBankAngle); + } +} + +void Camera3D::SetPosAndLookAt(const basegfx::B3DPoint& rNewPos, + const basegfx::B3DPoint& rNewLookAt) +{ + if ( rNewPos != aPosition || rNewLookAt != aLookAt ) + { + aPosition = rNewPos; + aLookAt = rNewLookAt; + + SetVRP(aPosition); + SetVPN(aPosition - aLookAt); + SetBankAngle(fBankAngle); + } +} + +void Camera3D::SetBankAngle(double fAngle) +{ + basegfx::B3DVector aDiff(aPosition - aLookAt); + basegfx::B3DVector aPrj(aDiff); + fBankAngle = fAngle; + + if ( aDiff.getY() == 0 ) + { + aPrj.setY(-1.0); + } + else + { // aPrj = Projection from aDiff on the XZ-plane + aPrj.setY(0.0); + + if ( aDiff.getY() < 0.0 ) + { + aPrj = -aPrj; + } + } + + // Calculate from aDiff to upwards pointing View-Up-Vector + // duplicated line is intentional! + aPrj = aPrj.getPerpendicular(aDiff); + aPrj = aPrj.getPerpendicular(aDiff); + aDiff.normalize(); + + // Rotate on Z axis, to rotate the BankAngle and back + basegfx::B3DHomMatrix aTf; + const double fV(sqrt(aDiff.getY() * aDiff.getY() + aDiff.getZ() * aDiff.getZ())); + + if ( fV != 0.0 ) + { + basegfx::B3DHomMatrix aTemp; + const double fSin(aDiff.getY() / fV); + const double fCos(aDiff.getZ() / fV); + + aTemp.set(1, 1, fCos); + aTemp.set(2, 2, fCos); + aTemp.set(2, 1, fSin); + aTemp.set(1, 2, -fSin); + + aTf *= aTemp; + } + + { + basegfx::B3DHomMatrix aTemp; + const double fSin(-aDiff.getX()); + const double fCos(fV); + + aTemp.set(0, 0, fCos); + aTemp.set(2, 2, fCos); + aTemp.set(0, 2, fSin); + aTemp.set(2, 0, -fSin); + + aTf *= aTemp; + } + + aTf.rotate(0.0, 0.0, fBankAngle); + + { + basegfx::B3DHomMatrix aTemp; + const double fSin(aDiff.getX()); + const double fCos(fV); + + aTemp.set(0, 0, fCos); + aTemp.set(2, 2, fCos); + aTemp.set(0, 2, fSin); + aTemp.set(2, 0, -fSin); + + aTf *= aTemp; + } + + if ( fV != 0.0 ) + { + basegfx::B3DHomMatrix aTemp; + const double fSin(-aDiff.getY() / fV); + const double fCos(aDiff.getZ() / fV); + + aTemp.set(1, 1, fCos); + aTemp.set(2, 2, fCos); + aTemp.set(2, 1, fSin); + aTemp.set(1, 2, -fSin); + + aTf *= aTemp; + } + + SetVUV(aTf * aPrj); +} + +void Camera3D::SetFocalLength(double fLen) +{ + if ( fLen < 5 ) + fLen = 5; + SetPRP(basegfx::B3DPoint(0.0, 0.0, fLen / 35.0 * aViewWin.W)); + fFocalLength = fLen; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/engine3d/cube3d.cxx b/svx/source/engine3d/cube3d.cxx new file mode 100644 index 000000000..401e1b085 --- /dev/null +++ b/svx/source/engine3d/cube3d.cxx @@ -0,0 +1,157 @@ +/* -*- 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/strings.hrc> +#include <svx/deflt3d.hxx> +#include <svx/dialmgr.hxx> +#include <svx/cube3d.hxx> +#include <svx/globl3d.hxx> +#include <basegfx/point/b3dpoint.hxx> +#include <sdr/contact/viewcontactofe3dcube.hxx> +#include <rtl/ustrbuf.hxx> + + +// DrawContact section + +std::unique_ptr<sdr::contact::ViewContact> E3dCubeObj::CreateObjectSpecificViewContact() +{ + return std::make_unique<sdr::contact::ViewContactOfE3dCube>(*this); +} + + +E3dCubeObj::E3dCubeObj( + SdrModel& rSdrModel, + const E3dDefaultAttributes& rDefault, + const basegfx::B3DPoint& aPos, + const basegfx::B3DVector& r3DSize) +: E3dCompoundObject(rSdrModel) +{ + // Set Defaults + SetDefaultAttributes(rDefault); + + // position centre or left, bottom, back (dependent on bPosIsCenter) + aCubePos = aPos; + aCubeSize = r3DSize; +} + +E3dCubeObj::E3dCubeObj(SdrModel& rSdrModel) +: E3dCompoundObject(rSdrModel) +{ + // Set Defaults + const E3dDefaultAttributes aDefault; + + SetDefaultAttributes(aDefault); +} + +E3dCubeObj::~E3dCubeObj() +{ +} + +void E3dCubeObj::SetDefaultAttributes(const E3dDefaultAttributes& rDefault) +{ + aCubePos = rDefault.GetDefaultCubePos(); + aCubeSize = rDefault.GetDefaultCubeSize(); + bPosIsCenter = rDefault.GetDefaultCubePosIsCenter(); +} + +sal_uInt16 E3dCubeObj::GetObjIdentifier() const +{ + return E3D_CUBEOBJ_ID; +} + +// Convert the object into a group object consisting of 6 polygons + +SdrObjectUniquePtr E3dCubeObj::DoConvertToPolyObj(bool /*bBezier*/, bool /*bAddText*/) const +{ + return nullptr; +} + +E3dCubeObj* E3dCubeObj::CloneSdrObject(SdrModel& rTargetModel) const +{ + return CloneHelper< E3dCubeObj >(rTargetModel); +} + +E3dCubeObj& E3dCubeObj::operator=(const E3dCubeObj& rObj) +{ + if( this == &rObj ) + return *this; + E3dCompoundObject::operator=(rObj); + + aCubePos = rObj.aCubePos; + aCubeSize = rObj.aCubeSize; + bPosIsCenter = rObj.bPosIsCenter; + + return *this; +} + +// Set local parameters with geometry re-creating + +void E3dCubeObj::SetCubePos(const basegfx::B3DPoint& rNew) +{ + if(aCubePos != rNew) + { + aCubePos = rNew; + ActionChanged(); + } +} + +void E3dCubeObj::SetCubeSize(const basegfx::B3DVector& rNew) +{ + if(aCubeSize != rNew) + { + aCubeSize = rNew; + ActionChanged(); + } +} + +void E3dCubeObj::SetPosIsCenter(bool bNew) +{ + if(bPosIsCenter != bNew) + { + bPosIsCenter = bNew; + ActionChanged(); + } +} + +// Get the name of the object (singular) + +OUString E3dCubeObj::TakeObjNameSingul() const +{ + OUStringBuffer sName(SvxResId(STR_ObjNameSingulCube3d)); + + OUString aName(GetName()); + if (!aName.isEmpty()) + { + sName.append(' '); + sName.append('\''); + sName.append(aName); + sName.append('\''); + } + return sName.makeStringAndClear(); +} + +// Get the name of the object (plural) + +OUString E3dCubeObj::TakeObjNamePlural() const +{ + return SvxResId(STR_ObjNamePluralCube3d); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/engine3d/deflt3d.cxx b/svx/source/engine3d/deflt3d.cxx new file mode 100644 index 000000000..76589032b --- /dev/null +++ b/svx/source/engine3d/deflt3d.cxx @@ -0,0 +1,56 @@ +/* -*- 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/deflt3d.hxx> + +// Class to manage the 3D default attributes + +E3dDefaultAttributes::E3dDefaultAttributes() +{ + Reset(); +} + +void E3dDefaultAttributes::Reset() +{ + // Cube object + aDefaultCubePos = basegfx::B3DPoint(-500.0, -500.0, -500.0); + aDefaultCubeSize = basegfx::B3DVector(1000.0, 1000.0, 1000.0); + bDefaultCubePosIsCenter = false; + + // Sphere object + aDefaultSphereCenter = basegfx::B3DPoint(0.0, 0.0, 0.0); + aDefaultSphereSize = basegfx::B3DPoint(1000.0, 1000.0, 1000.0); + + // Lathe object + bDefaultLatheSmoothed = true; + bDefaultLatheSmoothFrontBack = false; + bDefaultLatheCharacterMode = false; + bDefaultLatheCloseFront = true; + bDefaultLatheCloseBack = true; + + // Extrude object + bDefaultExtrudeSmoothed = true; + bDefaultExtrudeSmoothFrontBack = false; + bDefaultExtrudeCharacterMode = false; + bDefaultExtrudeCloseFront = true; + bDefaultExtrudeCloseBack = true; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/engine3d/dragmt3d.cxx b/svx/source/engine3d/dragmt3d.cxx new file mode 100644 index 000000000..231305eea --- /dev/null +++ b/svx/source/engine3d/dragmt3d.cxx @@ -0,0 +1,732 @@ +/* -*- 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 <dragmt3d.hxx> +#include <o3tl/numeric.hxx> +#include <svx/svdpagv.hxx> +#include <svx/dialmgr.hxx> +#include <svx/svddrgmt.hxx> +#include <svx/svdtrans.hxx> +#include <svx/obj3d.hxx> +#include <svx/e3dundo.hxx> +#include <svx/strings.hrc> +#include <svx/sdr/overlay/overlaypolypolygon.hxx> +#include <svx/sdr/overlay/overlaymanager.hxx> +#include <basegfx/polygon/b2dpolypolygontools.hxx> +#include <svx/sdr/contact/viewcontactofe3dscene.hxx> +#include <drawinglayer/geometry/viewinformation3d.hxx> +#include <svx/e3dsceneupdater.hxx> +#include <vcl/ptrstyle.hxx> +#include <comphelper/lok.hxx> + + +E3dDragMethod::E3dDragMethod ( + SdrDragView &_rView, + const SdrMarkList& rMark, + E3dDragConstraint eConstr, + bool bFull) +: SdrDragMethod(_rView), + meConstraint(eConstr), + mbMoveFull(bFull), + mbMovedAtAll(false) +{ + // Create a unit for all the 3D objects present in the selection + const size_t nCnt(rMark.GetMarkCount()); + + if(mbMoveFull) + { + // for non-visible 3D objects fallback to wireframe interaction + for(size_t nObjs = 0; nObjs < nCnt; ++nObjs) + { + E3dObject* pE3dObj = dynamic_cast< E3dObject* >(rMark.GetMark(nObjs)->GetMarkedSdrObj()); + + if(pE3dObj) + { + if(!pE3dObj->HasFillStyle() && !pE3dObj->HasLineStyle()) + { + mbMoveFull = false; + break; + } + } + } + } + + for(size_t nObjs = 0; nObjs < nCnt; ++nObjs) + { + E3dObject* pE3dObj = dynamic_cast< E3dObject* >(rMark.GetMark(nObjs)->GetMarkedSdrObj()); + + if(pE3dObj) + { + // fill new interaction unit + E3dDragMethodUnit aNewUnit(*pE3dObj); + + // get transformations + aNewUnit.maInitTransform = aNewUnit.maTransform = pE3dObj->GetTransform(); + + if(nullptr != pE3dObj->getParentE3dSceneFromE3dObject()) + { + // get transform between object and world, normally scene transform + aNewUnit.maInvDisplayTransform = aNewUnit.maDisplayTransform = pE3dObj->getParentE3dSceneFromE3dObject()->GetFullTransform(); + aNewUnit.maInvDisplayTransform.invert(); + } + + if(!mbMoveFull) + { + // create wireframe visualisation for parent coordinate system + aNewUnit.maWireframePoly.clear(); + aNewUnit.maWireframePoly = pE3dObj->CreateWireframe(); + aNewUnit.maWireframePoly.transform(aNewUnit.maTransform); + } + + // Determine FullBound + maFullBound.Union(pE3dObj->GetSnapRect()); + + // Insert Unit + maGrp.push_back(aNewUnit); + } + } +} + +OUString E3dDragMethod::GetSdrDragComment() const +{ + return OUString(); +} + +// Create the wireframe model for all actions + +bool E3dDragMethod::BeginSdrDrag() +{ + if(E3dDragConstraint::Z == meConstraint) + { + const sal_uInt32 nCnt(maGrp.size()); + DragStat().SetRef1( maFullBound.Center() ); + + for(sal_uInt32 nOb(0); nOb < nCnt; nOb++) + { + E3dDragMethodUnit& rCandidate = maGrp[nOb]; + rCandidate.mnStartAngle = GetAngle(DragStat().GetStart() - DragStat().GetRef1()); + rCandidate.mnLastAngle = 0; + } + } + else + { + maLastPos = DragStat().GetStart(); + } + + if(!mbMoveFull) + { + Show(); + } + + return true; +} + +bool E3dDragMethod::EndSdrDrag(bool /*bCopy*/) +{ + const sal_uInt32 nCnt(maGrp.size()); + + if(!mbMoveFull) + { + // Hide wireframe + Hide(); + } + + // Apply all transformations and create undo's + if(mbMovedAtAll) + { + const bool bUndo = getSdrDragView().IsUndoEnabled(); + if( bUndo ) + getSdrDragView().BegUndo(SvxResId(RID_SVX_3D_UNDO_ROTATE)); + sal_uInt32 nOb(0); + + for(nOb=0;nOb<nCnt;nOb++) + { + E3dDragMethodUnit& rCandidate = maGrp[nOb]; + E3DModifySceneSnapRectUpdater aUpdater(&rCandidate.mr3DObj); + rCandidate.mr3DObj.SetTransform(rCandidate.maTransform); + if( bUndo ) + { + getSdrDragView().AddUndo( + std::make_unique<E3dRotateUndoAction>( + rCandidate.mr3DObj, + rCandidate.maInitTransform, + rCandidate.maTransform)); + } + } + if( bUndo ) + getSdrDragView().EndUndo(); + } + + return true; +} + +void E3dDragMethod::CancelSdrDrag() +{ + if(mbMoveFull) + { + if(mbMovedAtAll) + { + const sal_uInt32 nCnt(maGrp.size()); + + for(sal_uInt32 nOb(0); nOb < nCnt; nOb++) + { + // Restore transformation + E3dDragMethodUnit& rCandidate = maGrp[nOb]; + E3DModifySceneSnapRectUpdater aUpdater(&rCandidate.mr3DObj); + rCandidate.mr3DObj.SetTransform(rCandidate.maInitTransform); + } + } + } + else + { + // Hide WireFrame + Hide(); + } +} + +// Common MoveSdrDrag() + +void E3dDragMethod::MoveSdrDrag(const Point& /*rPnt*/) +{ + mbMovedAtAll = true; +} + +// Draw the wire frame model + +// for migration from XOR to overlay +void E3dDragMethod::CreateOverlayGeometry( + sdr::overlay::OverlayManager& rOverlayManager, + const sdr::contact::ObjectContact& rObjectContact) +{ + // We do client-side object manipulation with the Kit API + if (comphelper::LibreOfficeKit::isActive()) + return; + + const sal_uInt32 nCnt(maGrp.size()); + basegfx::B2DPolyPolygon aResult; + + for(sal_uInt32 nOb(0); nOb < nCnt; nOb++) + { + E3dDragMethodUnit& rCandidate = maGrp[nOb]; + SdrPageView* pPV = getSdrDragView().GetSdrPageView(); + + if(pPV && pPV->HasMarkedObjPageView()) + { + const basegfx::B3DPolyPolygon aCandidate(rCandidate.maWireframePoly); + const sal_uInt32 nPlyCnt(aCandidate.count()); + + if(nPlyCnt) + { + const E3dScene* pScene(rCandidate.mr3DObj.getRootE3dSceneFromE3dObject()); + + if(nullptr != pScene) + { + const sdr::contact::ViewContactOfE3dScene& rVCScene = static_cast< sdr::contact::ViewContactOfE3dScene& >(pScene->GetViewContact()); + const drawinglayer::geometry::ViewInformation3D& aViewInfo3D(rVCScene.getViewInformation3D()); + const basegfx::B3DHomMatrix aWorldToView(aViewInfo3D.getDeviceToView() * aViewInfo3D.getProjection() * aViewInfo3D.getOrientation()); + const basegfx::B3DHomMatrix aTransform(aWorldToView * rCandidate.maDisplayTransform); + + // transform to relative scene coordinates + basegfx::B2DPolyPolygon aPolyPolygon(basegfx::utils::createB2DPolyPolygonFromB3DPolyPolygon(aCandidate, aTransform)); + + // transform to 2D view coordinates + aPolyPolygon.transform(rVCScene.getObjectTransformation()); + + aResult.append(aPolyPolygon); + } + } + } + } + + if(aResult.count()) + { + std::unique_ptr<sdr::overlay::OverlayPolyPolygonStripedAndFilled> pNew( + new sdr::overlay::OverlayPolyPolygonStripedAndFilled( + aResult)); + + insertNewlyCreatedOverlayObjectForSdrDragMethod( + std::move(pNew), + rObjectContact, + rOverlayManager); + } +} + + +E3dDragRotate::E3dDragRotate(SdrDragView &_rView, + const SdrMarkList& rMark, + E3dDragConstraint eConstr, + bool bFull) +: E3dDragMethod(_rView, rMark, eConstr, bFull) +{ + // Get center of all selected objects in eye coordinates + const sal_uInt32 nCnt(maGrp.size()); + + if(nCnt) + { + const E3dScene* pScene(maGrp[0].mr3DObj.getRootE3dSceneFromE3dObject()); + + if(nullptr != pScene) + { + const sdr::contact::ViewContactOfE3dScene& rVCScene = static_cast< sdr::contact::ViewContactOfE3dScene& >(pScene->GetViewContact()); + const drawinglayer::geometry::ViewInformation3D& aViewInfo3D(rVCScene.getViewInformation3D()); + + for(sal_uInt32 nOb(0); nOb < nCnt; nOb++) + { + E3dDragMethodUnit& rCandidate = maGrp[nOb]; + basegfx::B3DPoint aObjCenter = rCandidate.mr3DObj.GetBoundVolume().getCenter(); + const basegfx::B3DHomMatrix aTransform(aViewInfo3D.getOrientation() * rCandidate.maDisplayTransform * rCandidate.maInitTransform); + + aObjCenter = aTransform * aObjCenter; + maGlobalCenter += aObjCenter; + } + + // Divide by the number + if(nCnt > 1) + { + maGlobalCenter /= static_cast<double>(nCnt); + } + + // get rotate center and transform to 3D eye coordinates + basegfx::B2DPoint aRotCenter2D(Ref1().X(), Ref1().Y()); + + // from world to relative scene using inverse getObjectTransformation() + basegfx::B2DHomMatrix aInverseObjectTransform(rVCScene.getObjectTransformation()); + aInverseObjectTransform.invert(); + aRotCenter2D = aInverseObjectTransform * aRotCenter2D; + + // from 3D view to 3D eye + basegfx::B3DPoint aRotCenter3D(aRotCenter2D.getX(), aRotCenter2D.getY(), 0.0); + basegfx::B3DHomMatrix aInverseViewToEye(aViewInfo3D.getDeviceToView() * aViewInfo3D.getProjection()); + aInverseViewToEye.invert(); + aRotCenter3D = aInverseViewToEye * aRotCenter3D; + + // Use X,Y of the RotCenter and depth of the common object centre + // as rotation point in the space + maGlobalCenter.setX(aRotCenter3D.getX()); + maGlobalCenter.setY(aRotCenter3D.getY()); + } + } +} + + +//The object is moved, determine the angle + +void E3dDragRotate::MoveSdrDrag(const Point& rPnt) +{ + // call parent + E3dDragMethod::MoveSdrDrag(rPnt); + + if(DragStat().CheckMinMoved(rPnt)) + { + // Get modifier + sal_uInt16 nModifier = 0; + if(dynamic_cast<const E3dView*>(&getSdrDragView())) + { + const MouseEvent& rLastMouse = static_cast<E3dView&>(getSdrDragView()).GetMouseEvent(); + nModifier = rLastMouse.GetModifier(); + } + + // Rotate all objects + const sal_uInt32 nCnt(maGrp.size()); + + for(sal_uInt32 nOb(0); nOb < nCnt; nOb++) + { + // Determine rotation angle + double fWAngle, fHAngle; + E3dDragMethodUnit& rCandidate = maGrp[nOb]; + + if(E3dDragConstraint::Z == meConstraint) + { + fWAngle = NormAngle36000(GetAngle(rPnt - DragStat().GetRef1()) - + rCandidate.mnStartAngle) - rCandidate.mnLastAngle; + rCandidate.mnLastAngle = static_cast<long>(fWAngle) + rCandidate.mnLastAngle; + fWAngle /= 100.0; + fHAngle = 0.0; + } + else + { + if ((maFullBound.GetWidth() == 0) || (maFullBound.GetHeight() == 0)) + throw o3tl::divide_by_zero(); + fWAngle = 90.0 * static_cast<double>(rPnt.X() - maLastPos.X()) + / static_cast<double>(maFullBound.GetWidth()); + fHAngle = 90.0 * static_cast<double>(rPnt.Y() - maLastPos.Y()) + / static_cast<double>(maFullBound.GetHeight()); + } + long nSnap = 0; + + if(!getSdrDragView().IsRotateAllowed()) + nSnap = 90; + + if(nSnap != 0) + { + fWAngle = static_cast<double>((static_cast<long>(fWAngle) + nSnap/2) / nSnap * nSnap); + fHAngle = static_cast<double>((static_cast<long>(fHAngle) + nSnap/2) / nSnap * nSnap); + } + + // to radians + fWAngle = basegfx::deg2rad(fWAngle); + fHAngle = basegfx::deg2rad(fHAngle); + + // Determine transformation + basegfx::B3DHomMatrix aRotMat; + if(E3dDragConstraint::Y & meConstraint) + { + if(nModifier & KEY_MOD2) + aRotMat.rotate(0.0, 0.0, fWAngle); + else + aRotMat.rotate(0.0, fWAngle, 0.0); + } + else if(E3dDragConstraint::Z & meConstraint) + { + if(nModifier & KEY_MOD2) + aRotMat.rotate(0.0, fWAngle, 0.0); + else + aRotMat.rotate(0.0, 0.0, fWAngle); + } + if(E3dDragConstraint::X & meConstraint) + { + aRotMat.rotate(fHAngle, 0.0, 0.0); + } + + const E3dScene* pScene(rCandidate.mr3DObj.getRootE3dSceneFromE3dObject()); + + if(nullptr != pScene) + { + // Transformation in eye coordinates, there rotate then and back + const sdr::contact::ViewContactOfE3dScene& rVCScene = static_cast< sdr::contact::ViewContactOfE3dScene& >(pScene->GetViewContact()); + const drawinglayer::geometry::ViewInformation3D& aViewInfo3D(rVCScene.getViewInformation3D()); + basegfx::B3DHomMatrix aInverseOrientation(aViewInfo3D.getOrientation()); + aInverseOrientation.invert(); + + basegfx::B3DHomMatrix aTransMat(rCandidate.maDisplayTransform); + aTransMat *= aViewInfo3D.getOrientation(); + aTransMat.translate(-maGlobalCenter.getX(), -maGlobalCenter.getY(), -maGlobalCenter.getZ()); + aTransMat *= aRotMat; + aTransMat.translate(maGlobalCenter.getX(), maGlobalCenter.getY(), maGlobalCenter.getZ()); + aTransMat *= aInverseOrientation; + aTransMat *= rCandidate.maInvDisplayTransform; + + // ...and apply + rCandidate.maTransform *= aTransMat; + + if(mbMoveFull) + { + E3DModifySceneSnapRectUpdater aUpdater(&rCandidate.mr3DObj); + rCandidate.mr3DObj.SetTransform(rCandidate.maTransform); + } + else + { + Hide(); + rCandidate.maWireframePoly.transform(aTransMat); + Show(); + } + } + } + maLastPos = rPnt; + DragStat().NextMove(rPnt); + } +} + +PointerStyle E3dDragRotate::GetSdrDragPointer() const +{ + return PointerStyle::Rotate; +} + +// E3dDragMove. This drag method is only required for translations inside +// 3D scenes. If a 3D-scene itself moved, then this drag method will drag +// not be used. + + +E3dDragMove::E3dDragMove(SdrDragView &_rView, + const SdrMarkList& rMark, + SdrHdlKind eDrgHdl, + E3dDragConstraint eConstr, + bool bFull) +: E3dDragMethod(_rView, rMark, eConstr, bFull), + meWhatDragHdl(eDrgHdl) +{ + switch(meWhatDragHdl) + { + case SdrHdlKind::Left: + maScaleFixPos = maFullBound.RightCenter(); + break; + case SdrHdlKind::Right: + maScaleFixPos = maFullBound.LeftCenter(); + break; + case SdrHdlKind::Upper: + maScaleFixPos = maFullBound.BottomCenter(); + break; + case SdrHdlKind::Lower: + maScaleFixPos = maFullBound.TopCenter(); + break; + case SdrHdlKind::UpperLeft: + maScaleFixPos = maFullBound.BottomRight(); + break; + case SdrHdlKind::UpperRight: + maScaleFixPos = maFullBound.BottomLeft(); + break; + case SdrHdlKind::LowerLeft: + maScaleFixPos = maFullBound.TopRight(); + break; + case SdrHdlKind::LowerRight: + maScaleFixPos = maFullBound.TopLeft(); + break; + default: + // Moving the object, SdrHdlKind::Move + break; + } + + // Override when IsResizeAtCenter() + if(getSdrDragView().IsResizeAtCenter()) + { + meWhatDragHdl = SdrHdlKind::User; + maScaleFixPos = maFullBound.Center(); + } +} + +// The object is moved, determine the translations + +void E3dDragMove::MoveSdrDrag(const Point& rPnt) +{ + // call parent + E3dDragMethod::MoveSdrDrag(rPnt); + + if(DragStat().CheckMinMoved(rPnt)) + { + if(SdrHdlKind::Move == meWhatDragHdl) + { + // Translation + // Determine the motion vector + const sal_uInt32 nCnt(maGrp.size()); + + // Get modifier + sal_uInt16 nModifier(0); + + if(dynamic_cast<const E3dView*>(&getSdrDragView())) + { + const MouseEvent& rLastMouse = static_cast<E3dView&>(getSdrDragView()).GetMouseEvent(); + nModifier = rLastMouse.GetModifier(); + } + + for(sal_uInt32 nOb(0); nOb < nCnt; nOb++) + { + E3dDragMethodUnit& rCandidate = maGrp[nOb]; + const E3dScene* pScene(rCandidate.mr3DObj.getRootE3dSceneFromE3dObject()); + + if(nullptr != pScene) + { + const sdr::contact::ViewContactOfE3dScene& rVCScene = static_cast< sdr::contact::ViewContactOfE3dScene& >(pScene->GetViewContact()); + const drawinglayer::geometry::ViewInformation3D& aViewInfo3D(rVCScene.getViewInformation3D()); + + // move coor from 2d world to 3d Eye + basegfx::B2DPoint aGlobalMoveHead2D(static_cast<double>(rPnt.X() - maLastPos.X()), static_cast<double>(rPnt.Y() - maLastPos.Y())); + basegfx::B2DPoint aGlobalMoveTail2D(0.0, 0.0); + basegfx::B2DHomMatrix aInverseSceneTransform(rVCScene.getObjectTransformation()); + + aInverseSceneTransform.invert(); + aGlobalMoveHead2D = aInverseSceneTransform * aGlobalMoveHead2D; + aGlobalMoveTail2D = aInverseSceneTransform * aGlobalMoveTail2D; + + basegfx::B3DPoint aMoveHead3D(aGlobalMoveHead2D.getX(), aGlobalMoveHead2D.getY(), 0.5); + basegfx::B3DPoint aMoveTail3D(aGlobalMoveTail2D.getX(), aGlobalMoveTail2D.getY(), 0.5); + basegfx::B3DHomMatrix aInverseViewToEye(aViewInfo3D.getDeviceToView() * aViewInfo3D.getProjection()); + aInverseViewToEye.invert(); + + aMoveHead3D = aInverseViewToEye * aMoveHead3D; + aMoveTail3D = aInverseViewToEye * aMoveTail3D; + + // eventually switch movement from XY to XZ plane + if(nModifier & KEY_MOD2) + { + double fZwi = aMoveHead3D.getY(); + aMoveHead3D.setY(aMoveHead3D.getZ()); + aMoveHead3D.setZ(fZwi); + + fZwi = aMoveTail3D.getY(); + aMoveTail3D.setY(aMoveTail3D.getZ()); + aMoveTail3D.setZ(fZwi); + } + + // Motion vector from eye coordinates to parent coordinates + basegfx::B3DHomMatrix aInverseOrientation(aViewInfo3D.getOrientation()); + aInverseOrientation.invert(); + basegfx::B3DHomMatrix aCompleteTrans(rCandidate.maInvDisplayTransform * aInverseOrientation); + + aMoveHead3D = aCompleteTrans * aMoveHead3D; + aMoveTail3D = aCompleteTrans* aMoveTail3D; + + // build transformation + basegfx::B3DHomMatrix aTransMat; + basegfx::B3DPoint aTranslate(aMoveHead3D - aMoveTail3D); + aTransMat.translate(aTranslate.getX(), aTranslate.getY(), aTranslate.getZ()); + + // ...and apply + rCandidate.maTransform *= aTransMat; + + if(mbMoveFull) + { + E3DModifySceneSnapRectUpdater aUpdater(&rCandidate.mr3DObj); + rCandidate.mr3DObj.SetTransform(rCandidate.maTransform); + } + else + { + Hide(); + rCandidate.maWireframePoly.transform(aTransMat); + Show(); + } + } + } + } + else + { + // Scaling + // Determine scaling vector + Point aStartPos = DragStat().GetStart(); + const sal_uInt32 nCnt(maGrp.size()); + + for(sal_uInt32 nOb(0); nOb < nCnt; nOb++) + { + E3dDragMethodUnit& rCandidate = maGrp[nOb]; + const basegfx::B3DPoint aObjectCenter(rCandidate.mr3DObj.GetBoundVolume().getCenter()); + const E3dScene* pScene(rCandidate.mr3DObj.getRootE3dSceneFromE3dObject()); + + if(nullptr != pScene) + { + // transform from 2D world view to 3D eye + const sdr::contact::ViewContactOfE3dScene& rVCScene = static_cast< sdr::contact::ViewContactOfE3dScene& >(pScene->GetViewContact()); + const drawinglayer::geometry::ViewInformation3D& aViewInfo3D(rVCScene.getViewInformation3D()); + + basegfx::B2DPoint aGlobalScaleStart2D(static_cast<double>(aStartPos.X()), static_cast<double>(aStartPos.Y())); + basegfx::B2DPoint aGlobalScaleNext2D(static_cast<double>(rPnt.X()), static_cast<double>(rPnt.Y())); + basegfx::B2DPoint aGlobalScaleFixPos2D(static_cast<double>(maScaleFixPos.X()), static_cast<double>(maScaleFixPos.Y())); + basegfx::B2DHomMatrix aInverseSceneTransform(rVCScene.getObjectTransformation()); + + aInverseSceneTransform.invert(); + aGlobalScaleStart2D = aInverseSceneTransform * aGlobalScaleStart2D; + aGlobalScaleNext2D = aInverseSceneTransform * aGlobalScaleNext2D; + aGlobalScaleFixPos2D = aInverseSceneTransform * aGlobalScaleFixPos2D; + + basegfx::B3DPoint aGlobalScaleStart3D(aGlobalScaleStart2D.getX(), aGlobalScaleStart2D.getY(), aObjectCenter.getZ()); + basegfx::B3DPoint aGlobalScaleNext3D(aGlobalScaleNext2D.getX(), aGlobalScaleNext2D.getY(), aObjectCenter.getZ()); + basegfx::B3DPoint aGlobalScaleFixPos3D(aGlobalScaleFixPos2D.getX(), aGlobalScaleFixPos2D.getY(), aObjectCenter.getZ()); + basegfx::B3DHomMatrix aInverseViewToEye(aViewInfo3D.getDeviceToView() * aViewInfo3D.getProjection()); + + aInverseViewToEye.invert(); + basegfx::B3DPoint aScStart(aInverseViewToEye * aGlobalScaleStart3D); + basegfx::B3DPoint aScNext(aInverseViewToEye * aGlobalScaleNext3D); + basegfx::B3DPoint aScFixPos(aInverseViewToEye * aGlobalScaleFixPos3D); + + // constraints? + switch(meWhatDragHdl) + { + case SdrHdlKind::Left: + case SdrHdlKind::Right: + // to constrain on X -> Y equal + aScNext.setY(aScFixPos.getY()); + break; + case SdrHdlKind::Upper: + case SdrHdlKind::Lower: + // constrain to Y -> X equal + aScNext.setX(aScFixPos.getX()); + break; + default: + break; + } + + // get scale vector in eye coordinates + basegfx::B3DPoint aScaleVec(aScStart - aScFixPos); + aScaleVec.setZ(1.0); + + if(aScaleVec.getX() != 0.0) + { + aScaleVec.setX((aScNext.getX() - aScFixPos.getX()) / aScaleVec.getX()); + } + else + { + aScaleVec.setX(1.0); + } + + if(aScaleVec.getY() != 0.0) + { + aScaleVec.setY((aScNext.getY() - aScFixPos.getY()) / aScaleVec.getY()); + } + else + { + aScaleVec.setY(1.0); + } + + // SHIFT-key used? + if(getSdrDragView().IsOrtho()) + { + if(fabs(aScaleVec.getX()) > fabs(aScaleVec.getY())) + { + // X is biggest + aScaleVec.setY(aScaleVec.getX()); + } + else + { + // Y is biggest + aScaleVec.setX(aScaleVec.getY()); + } + } + + // build transformation + basegfx::B3DHomMatrix aInverseOrientation(aViewInfo3D.getOrientation()); + aInverseOrientation.invert(); + + basegfx::B3DHomMatrix aNewTrans = rCandidate.maInitTransform; + aNewTrans *= rCandidate.maDisplayTransform; + aNewTrans *= aViewInfo3D.getOrientation(); + aNewTrans.translate(-aScFixPos.getX(), -aScFixPos.getY(), -aScFixPos.getZ()); + aNewTrans.scale(aScaleVec.getX(), aScaleVec.getY(), aScaleVec.getZ()); + aNewTrans.translate(aScFixPos.getX(), aScFixPos.getY(), aScFixPos.getZ()); + aNewTrans *= aInverseOrientation; + aNewTrans *= rCandidate.maInvDisplayTransform; + + // ...and apply + rCandidate.maTransform = aNewTrans; + + if(mbMoveFull) + { + E3DModifySceneSnapRectUpdater aUpdater(&rCandidate.mr3DObj); + rCandidate.mr3DObj.SetTransform(rCandidate.maTransform); + } + else + { + Hide(); + rCandidate.maWireframePoly.clear(); + rCandidate.maWireframePoly = rCandidate.mr3DObj.CreateWireframe(); + rCandidate.maWireframePoly.transform(rCandidate.maTransform); + Show(); + } + } + } + } + maLastPos = rPnt; + DragStat().NextMove(rPnt); + } +} + +PointerStyle E3dDragMove::GetSdrDragPointer() const +{ + return PointerStyle::Move; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/engine3d/e3dsceneupdater.cxx b/svx/source/engine3d/e3dsceneupdater.cxx new file mode 100644 index 000000000..07df1af05 --- /dev/null +++ b/svx/source/engine3d/e3dsceneupdater.cxx @@ -0,0 +1,111 @@ +/* -*- 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/e3dsceneupdater.hxx> +#include <drawinglayer/geometry/viewinformation3d.hxx> +#include <svx/obj3d.hxx> +#include <svx/scene3d.hxx> +#include <svx/sdr/contact/viewcontactofe3dscene.hxx> + + +E3DModifySceneSnapRectUpdater::E3DModifySceneSnapRectUpdater(const SdrObject* pObject) +: mpScene(nullptr) +{ + // Secure old 3D transformation stack before modification + if(const E3dObject* pE3dObject = dynamic_cast< const E3dObject* >(pObject)) + { + mpScene = pE3dObject->getRootE3dSceneFromE3dObject(); + + if(nullptr != mpScene && mpScene->getRootE3dSceneFromE3dObject() == mpScene) + { + // if there is a scene and it's the outmost scene, get current 3D range + const sdr::contact::ViewContactOfE3dScene& rVCScene = static_cast< sdr::contact::ViewContactOfE3dScene& >(mpScene->GetViewContact()); + const basegfx::B3DRange aAllContentRange(rVCScene.getAllContentRange3D()); + + if(aAllContentRange.isEmpty()) + { + // no content, nothing to do + mpScene = nullptr; + } + else + { + // secure current 3D transformation stack + mpViewInformation3D.reset( + new drawinglayer::geometry::ViewInformation3D( + rVCScene.getViewInformation3D(aAllContentRange))); + } + } + } +} + +E3DModifySceneSnapRectUpdater::~E3DModifySceneSnapRectUpdater() +{ + if(mpScene && mpViewInformation3D) + { + // after changing parts of the scene, use the secured last 3d transformation stack and the new content + // range to calculate a new, eventually expanded or shrunk, 2D geometry for the scene and apply it. + // Get new content range + const sdr::contact::ViewContactOfE3dScene& rVCScene = static_cast< sdr::contact::ViewContactOfE3dScene& >(mpScene->GetViewContact()); + basegfx::B3DRange aAllContentRange(rVCScene.getAllContentRange3D()); + + // only change when there is still content; else let scene stay at old SnapRect + if(!aAllContentRange.isEmpty()) + { + // check if object transform of scene has changed + if(mpViewInformation3D->getObjectTransformation() != mpScene->GetTransform()) + { + // If Yes, it needs to be updated since it's - for historical reasons - + // part of the basic 3d transformation stack of the scene + drawinglayer::geometry::ViewInformation3D* pNew = new drawinglayer::geometry::ViewInformation3D( + mpScene->GetTransform(), // replace object transformation with new local transform + mpViewInformation3D->getOrientation(), + mpViewInformation3D->getProjection(), + mpViewInformation3D->getDeviceToView(), + mpViewInformation3D->getViewTime(), + mpViewInformation3D->getExtendedInformationSequence()); + mpViewInformation3D.reset(pNew); + } + + // transform content range to scene-relative coordinates using old 3d transformation stack + aAllContentRange.transform(mpViewInformation3D->getObjectToView()); + + // build 2d relative content range + basegfx::B2DRange aSnapRange( + aAllContentRange.getMinX(), aAllContentRange.getMinY(), + aAllContentRange.getMaxX(), aAllContentRange.getMaxY()); + + // transform to 2D world coordinates using scene's 2D transformation + aSnapRange.transform(rVCScene.getObjectTransformation()); + + // snap to (old) integer + const tools::Rectangle aNewSnapRect( + sal_Int32(floor(aSnapRange.getMinX())), sal_Int32(floor(aSnapRange.getMinY())), + sal_Int32(ceil(aSnapRange.getMaxX())), sal_Int32(ceil(aSnapRange.getMaxY()))); + + // set as new SnapRect and invalidate bound volume + if(mpScene->GetSnapRect() != aNewSnapRect) + { + mpScene->SetSnapRect(aNewSnapRect); + mpScene->InvalidateBoundVolume(); + } + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/engine3d/e3dundo.cxx b/svx/source/engine3d/e3dundo.cxx new file mode 100644 index 000000000..b1d99ddfd --- /dev/null +++ b/svx/source/engine3d/e3dundo.cxx @@ -0,0 +1,91 @@ +/* -*- 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/e3dundo.hxx> +#include <svx/e3dsceneupdater.hxx> + + +E3dUndoAction::~E3dUndoAction () +{ +} + +// Repeat does not exist + +bool E3dUndoAction::CanRepeat(SfxRepeatTarget&) const +{ + return false; +} + + +// Undo destructor for 3D-Rotation +E3dRotateUndoAction::~E3dRotateUndoAction() +{ +} + +// Undo for 3D-Rotation on the Rotation matrix +void E3dRotateUndoAction::Undo() +{ + E3DModifySceneSnapRectUpdater aUpdater(&mrMy3DObj); + mrMy3DObj.SetTransform(maMyOldRotation); +} + +// Redo for 3D-Rotation on the Rotation matrix +void E3dRotateUndoAction::Redo() +{ + E3DModifySceneSnapRectUpdater aUpdater(&mrMy3DObj); + mrMy3DObj.SetTransform(maMyNewRotation); +} + +E3dAttributesUndoAction::E3dAttributesUndoAction( + E3dObject& rInObject, + const SfxItemSet& rNewSet, + const SfxItemSet& rOldSet) +: SdrUndoAction(rInObject.getSdrModelFromSdrObject()) + ,mrObject(rInObject) + ,maNewSet(rNewSet) + ,maOldSet(rOldSet) +{ +} + +E3dAttributesUndoAction::~E3dAttributesUndoAction() +{ +} + +// Undo() implemented through Set3DAttributes() to only maintain the attributes +// in one place + +void E3dAttributesUndoAction::Undo() +{ + E3DModifySceneSnapRectUpdater aUpdater(&mrObject); + mrObject.SetMergedItemSetAndBroadcast(maOldSet); +} + +void E3dAttributesUndoAction::Redo() +{ + E3DModifySceneSnapRectUpdater aUpdater(&mrObject); + mrObject.SetMergedItemSetAndBroadcast(maNewSet); +} + +// Multiple Undo is not possible +bool E3dAttributesUndoAction::CanRepeat(SfxRepeatTarget& /*rView*/) const +{ + return false; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/engine3d/extrud3d.cxx b/svx/source/engine3d/extrud3d.cxx new file mode 100644 index 000000000..d4aa1f024 --- /dev/null +++ b/svx/source/engine3d/extrud3d.cxx @@ -0,0 +1,223 @@ +/* -*- 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/strings.hrc> +#include <svx/deflt3d.hxx> +#include <svx/dialmgr.hxx> +#include <svx/globl3d.hxx> +#include <extrud3d.hxx> + +#include <svx/svdopath.hxx> +#include <svx/svdmodel.hxx> +#include <svx/svx3ditems.hxx> +#include <svx/xlineit0.hxx> +#include <sdr/properties/e3dextrudeproperties.hxx> +#include <sdr/contact/viewcontactofe3dextrude.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <basegfx/polygon/b2dpolypolygontools.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/polygon/b3dpolypolygontools.hxx> +#include <rtl/ustrbuf.hxx> + + +// DrawContact section +std::unique_ptr<sdr::contact::ViewContact> E3dExtrudeObj::CreateObjectSpecificViewContact() +{ + return std::make_unique<sdr::contact::ViewContactOfE3dExtrude>(*this); +} + +std::unique_ptr<sdr::properties::BaseProperties> E3dExtrudeObj::CreateObjectSpecificProperties() +{ + return std::make_unique<sdr::properties::E3dExtrudeProperties>(*this); +} + +// Constructor creates a two cover surface tools::PolyPolygon and (point-count 1) side +// surfaces rectangles from the passed PolyPolygon +E3dExtrudeObj::E3dExtrudeObj( + SdrModel& rSdrModel, + const E3dDefaultAttributes& rDefault, + const basegfx::B2DPolyPolygon& rPP, + double fDepth) +: E3dCompoundObject(rSdrModel), + maExtrudePolygon(rPP) +{ + // since the old class PolyPolygon3D did mirror the given PolyPolygons in Y, do the same here + basegfx::B2DHomMatrix aMirrorY; + aMirrorY.scale(1.0, -1.0); + maExtrudePolygon.transform(aMirrorY); + + // Set Defaults + SetDefaultAttributes(rDefault); + + // set extrude depth + GetProperties().SetObjectItemDirect(makeSvx3DDepthItem(static_cast<sal_uInt32>(fDepth + 0.5))); +} + +E3dExtrudeObj::E3dExtrudeObj(SdrModel& rSdrModel) +: E3dCompoundObject(rSdrModel) +{ + // Set Defaults + const E3dDefaultAttributes aDefault; + + SetDefaultAttributes(aDefault); +} + +E3dExtrudeObj::~E3dExtrudeObj() +{ +} + +void E3dExtrudeObj::SetDefaultAttributes(const E3dDefaultAttributes& rDefault) +{ + GetProperties().SetObjectItemDirect(Svx3DSmoothNormalsItem(rDefault.GetDefaultExtrudeSmoothed())); + GetProperties().SetObjectItemDirect(Svx3DSmoothLidsItem(rDefault.GetDefaultExtrudeSmoothFrontBack())); + GetProperties().SetObjectItemDirect(Svx3DCharacterModeItem(rDefault.GetDefaultExtrudeCharacterMode())); + GetProperties().SetObjectItemDirect(Svx3DCloseFrontItem(rDefault.GetDefaultExtrudeCloseFront())); + GetProperties().SetObjectItemDirect(Svx3DCloseBackItem(rDefault.GetDefaultExtrudeCloseBack())); + + // For extrudes use StdTexture in X and Y by default + GetProperties().SetObjectItemDirect(Svx3DTextureProjectionXItem(1)); + GetProperties().SetObjectItemDirect(Svx3DTextureProjectionYItem(1)); +} + +sal_uInt16 E3dExtrudeObj::GetObjIdentifier() const +{ + return E3D_EXTRUDEOBJ_ID; +} + +E3dExtrudeObj* E3dExtrudeObj::CloneSdrObject(SdrModel& rTargetModel) const +{ + return CloneHelper< E3dExtrudeObj >(rTargetModel); +} + +E3dExtrudeObj& E3dExtrudeObj::operator=(const E3dExtrudeObj& rObj) +{ + if( this == &rObj ) + return *this; + E3dCompoundObject::operator=(rObj); + + maExtrudePolygon = rObj.maExtrudePolygon; + + return *this; +} + +// Set local parameters with geometry re-creating + +void E3dExtrudeObj::SetExtrudePolygon(const basegfx::B2DPolyPolygon &rNew) +{ + if(maExtrudePolygon != rNew) + { + maExtrudePolygon = rNew; + ActionChanged(); + } +} + +// Get the name of the object (singular) + +OUString E3dExtrudeObj::TakeObjNameSingul() const +{ + OUStringBuffer sName(SvxResId(STR_ObjNameSingulExtrude3d)); + + OUString aName(GetName()); + if (!aName.isEmpty()) + { + sName.append(' '); + sName.append('\''); + sName.append(aName); + sName.append('\''); + } + return sName.makeStringAndClear(); +} + +// Get the name of the object (plural) + +OUString E3dExtrudeObj::TakeObjNamePlural() const +{ + return SvxResId(STR_ObjNamePluralExtrude3d); +} + +bool E3dExtrudeObj::IsBreakObjPossible() +{ + return true; +} + +std::unique_ptr<SdrAttrObj,SdrObjectFreeOp> E3dExtrudeObj::GetBreakObj() +{ + basegfx::B3DPolyPolygon aFrontSide; + basegfx::B3DPolyPolygon aBackSide; + + if(maExtrudePolygon.count()) + { + basegfx::B2DPolyPolygon aTemp(maExtrudePolygon); + aTemp.removeDoublePoints(); + aTemp = basegfx::utils::correctOrientations(aTemp); + const basegfx::B2VectorOrientation aOrient = basegfx::utils::getOrientation(aTemp.getB2DPolygon(0)); + + if(basegfx::B2VectorOrientation::Positive == aOrient) + { + aTemp.flip(); + } + + aFrontSide = basegfx::utils::createB3DPolyPolygonFromB2DPolyPolygon(aTemp); + } + + if(aFrontSide.count()) + { + aBackSide = aFrontSide; + + if(GetExtrudeDepth()) + { + basegfx::B3DHomMatrix aTransform; + + if(100 != GetPercentBackScale()) + { + // scale polygon from center + const double fScaleFactor(GetPercentBackScale() / 100.0); + const basegfx::B3DRange aPolyPolyRange(basegfx::utils::getRange(aBackSide)); + const basegfx::B3DPoint aCenter(aPolyPolyRange.getCenter()); + + aTransform.translate(-aCenter.getX(), -aCenter.getY(), -aCenter.getZ()); + aTransform.scale(fScaleFactor, fScaleFactor, fScaleFactor); + aTransform.translate(aCenter.getX(), aCenter.getY(), aCenter.getZ()); + } + + // translate by extrude depth + aTransform.translate(0.0, 0.0, static_cast<double>(GetExtrudeDepth())); + + aBackSide.transform(aTransform); + } + } + + if(aBackSide.count()) + { + // create PathObj + basegfx::B2DPolyPolygon aPoly = TransformToScreenCoor(aBackSide); + std::unique_ptr<SdrPathObj,SdrObjectFreeOp> pPathObj(new SdrPathObj(getSdrModelFromSdrObject(), OBJ_PLIN, aPoly)); + + SfxItemSet aSet(GetObjectItemSet()); + aSet.Put(XLineStyleItem(css::drawing::LineStyle_SOLID)); + pPathObj->SetMergedItemSet(aSet); + + return std::unique_ptr<SdrAttrObj,SdrObjectFreeOp>(pPathObj.release()); + } + + return nullptr; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/engine3d/float3d.cxx b/svx/source/engine3d/float3d.cxx new file mode 100644 index 000000000..8636cfc2b --- /dev/null +++ b/svx/source/engine3d/float3d.cxx @@ -0,0 +1,2949 @@ +/* -*- 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 <sal/config.h> + +#include <sfx2/dispatch.hxx> +#include <sfx2/module.hxx> +#include <sfx2/viewfrm.hxx> +#include <svl/eitem.hxx> +#include <svl/intitem.hxx> +#include <svl/itempool.hxx> +#include <svtools/colrdlg.hxx> +#include <svx/colorbox.hxx> +#include <svx/f3dchild.hxx> +#include <svx/xfillit0.hxx> +#include <svx/xflclit.hxx> +#include <svx/xlineit0.hxx> +#include <svx/fmmodel.hxx> +#include <svx/dlgutil.hxx> +#include <svx/sdshitm.hxx> +#include <svx/svx3ditems.hxx> + +#include <svx/dialmgr.hxx> +#include <svx/viewpt3d.hxx> + +#include <svx/svxids.hrc> +#include <svx/strings.hrc> + +#include <editeng/colritem.hxx> +#include <osl/diagnose.h> +#include <svx/e3ditem.hxx> +#include <svl/whiter.hxx> +#include <svtools/unitconv.hxx> + +#include <svx/float3d.hxx> +#include <bitmaps.hlst> + +using namespace com::sun::star; + +SFX_IMPL_DOCKINGWINDOW_WITHID( Svx3DChildWindow, SID_3D_WIN ) + +struct Svx3DWinImpl +{ + SfxItemPool* pPool; +}; + +namespace { + /** Get the dispatcher from the current view frame, or, if that is not + available, from the given bindings. + @param pBindings + May be NULL. + @returns NULL when both the current view frame is NULL and the given + bindings are NULL. + */ + SfxDispatcher* LocalGetDispatcher (const SfxBindings* pBindings) + { + SfxDispatcher* pDispatcher = nullptr; + + if (SfxViewFrame::Current() != nullptr) + pDispatcher = SfxViewFrame::Current()->GetDispatcher(); + else if (pBindings != nullptr) + pDispatcher = pBindings->GetDispatcher(); + + return pDispatcher; + } +} + +Svx3DWin::Svx3DWin(SfxBindings* pInBindings, SfxChildWindow *pCW, vcl::Window* pParent) + : SfxDockingWindow(pInBindings, pCW, pParent, + "Docking3DEffects", "svx/ui/docking3deffects.ui") + + , m_xBtnGeo(m_xBuilder->weld_toggle_button("geometry")) + , m_xBtnRepresentation(m_xBuilder->weld_toggle_button("representation")) + , m_xBtnLight(m_xBuilder->weld_toggle_button("light")) + , m_xBtnTexture(m_xBuilder->weld_toggle_button("texture")) + , m_xBtnMaterial(m_xBuilder->weld_toggle_button("material")) + , m_xBtnUpdate(m_xBuilder->weld_toggle_button("update")) + , m_xBtnAssign(m_xBuilder->weld_button("assign")) + + , m_xFLGeometrie(m_xBuilder->weld_container("geoframe")) + , m_xFtPercentDiagonal(m_xBuilder->weld_label("diagonalft")) + , m_xMtrPercentDiagonal(m_xBuilder->weld_metric_spin_button("diagonal", FieldUnit::PERCENT)) + , m_xFtBackscale(m_xBuilder->weld_label("scaleddepthft")) + , m_xMtrBackscale(m_xBuilder->weld_metric_spin_button("scaleddepth", FieldUnit::PERCENT)) + , m_xFtEndAngle(m_xBuilder->weld_label("angleft")) + , m_xMtrEndAngle(m_xBuilder->weld_metric_spin_button("angle", FieldUnit::DEGREE)) + , m_xFtDepth(m_xBuilder->weld_label("depthft")) + , m_xMtrDepth(m_xBuilder->weld_metric_spin_button("depth", FieldUnit::CM)) + + , m_xFLSegments(m_xBuilder->weld_container("segmentsframe")) + , m_xNumHorizontal(m_xBuilder->weld_spin_button("hori")) + , m_xNumVertical(m_xBuilder->weld_spin_button("veri")) + + , m_xFLNormals(m_xBuilder->weld_container("normals")) + , m_xBtnNormalsObj(m_xBuilder->weld_toggle_button("objspecific")) + , m_xBtnNormalsFlat(m_xBuilder->weld_toggle_button("flat")) + , m_xBtnNormalsSphere(m_xBuilder->weld_toggle_button("spherical")) + , m_xBtnNormalsInvert(m_xBuilder->weld_toggle_button("invertnormals")) + , m_xBtnTwoSidedLighting(m_xBuilder->weld_toggle_button("doublesidedillum")) + , m_xBtnDoubleSided(m_xBuilder->weld_toggle_button("doublesided")) + + , m_xFLRepresentation(m_xBuilder->weld_container("shadingframe")) + , m_xLbShademode(m_xBuilder->weld_combo_box("mode")) + + , m_xFLShadow(m_xBuilder->weld_container("shadowframe")) + , m_xBtnShadow3d(m_xBuilder->weld_toggle_button("shadow")) + , m_xFtSlant(m_xBuilder->weld_label("slantft")) + , m_xMtrSlant(m_xBuilder->weld_metric_spin_button("slant", FieldUnit::DEGREE)) + + , m_xFLCamera(m_xBuilder->weld_container("cameraframe")) + , m_xMtrDistance(m_xBuilder->weld_metric_spin_button("distance", FieldUnit::CM)) + , m_xMtrFocalLength(m_xBuilder->weld_metric_spin_button("focal", FieldUnit::CM)) + + , m_xFLLight(m_xBuilder->weld_container("illumframe")) + , m_xBtnLight1(new LightButton(m_xBuilder->weld_toggle_button("light1"))) + , m_xBtnLight2(new LightButton(m_xBuilder->weld_toggle_button("light2"))) + , m_xBtnLight3(new LightButton(m_xBuilder->weld_toggle_button("light3"))) + , m_xBtnLight4(new LightButton(m_xBuilder->weld_toggle_button("light4"))) + , m_xBtnLight5(new LightButton(m_xBuilder->weld_toggle_button("light5"))) + , m_xBtnLight6(new LightButton(m_xBuilder->weld_toggle_button("light6"))) + , m_xBtnLight7(new LightButton(m_xBuilder->weld_toggle_button("light7"))) + , m_xBtnLight8(new LightButton(m_xBuilder->weld_toggle_button("light8"))) + , m_xLbLight1(new ColorListBox(m_xBuilder->weld_menu_button("lightcolor1"), GetFrameWeld())) + , m_xLbLight2(new ColorListBox(m_xBuilder->weld_menu_button("lightcolor2"), GetFrameWeld())) + , m_xLbLight3(new ColorListBox(m_xBuilder->weld_menu_button("lightcolor3"), GetFrameWeld())) + , m_xLbLight4(new ColorListBox(m_xBuilder->weld_menu_button("lightcolor4"), GetFrameWeld())) + , m_xLbLight5(new ColorListBox(m_xBuilder->weld_menu_button("lightcolor5"), GetFrameWeld())) + , m_xLbLight6(new ColorListBox(m_xBuilder->weld_menu_button("lightcolor6"), GetFrameWeld())) + , m_xLbLight7(new ColorListBox(m_xBuilder->weld_menu_button("lightcolor7"), GetFrameWeld())) + , m_xLbLight8(new ColorListBox(m_xBuilder->weld_menu_button("lightcolor8"), GetFrameWeld())) + , m_xBtnLightColor(m_xBuilder->weld_button("colorbutton1")) + , m_xLbAmbientlight(new ColorListBox(m_xBuilder->weld_menu_button("ambientcolor"), GetFrameWeld())) + , m_xBtnAmbientColor(m_xBuilder->weld_button("colorbutton2")) + + , m_xFLTexture(m_xBuilder->weld_container("textureframe")) + , m_xBtnTexLuminance(m_xBuilder->weld_toggle_button("textype")) + , m_xBtnTexColor(m_xBuilder->weld_toggle_button("texcolor")) + , m_xBtnTexReplace(m_xBuilder->weld_toggle_button("texreplace")) + , m_xBtnTexModulate(m_xBuilder->weld_toggle_button("texmodulate")) + , m_xBtnTexBlend(m_xBuilder->weld_toggle_button("texblend")) + , m_xBtnTexObjectX(m_xBuilder->weld_toggle_button("texobjx")) + , m_xBtnTexParallelX(m_xBuilder->weld_toggle_button("texparallelx")) + , m_xBtnTexCircleX(m_xBuilder->weld_toggle_button("texcirclex")) + , m_xBtnTexObjectY(m_xBuilder->weld_toggle_button("texobjy")) + , m_xBtnTexParallelY(m_xBuilder->weld_toggle_button("texparallely")) + , m_xBtnTexCircleY(m_xBuilder->weld_toggle_button("texcircley")) + , m_xBtnTexFilter(m_xBuilder->weld_toggle_button("texfilter")) + + , m_xFLMaterial(m_xBuilder->weld_container("materialframe")) + , m_xLbMatFavorites(m_xBuilder->weld_combo_box("favorites")) + , m_xLbMatColor(new ColorListBox(m_xBuilder->weld_menu_button("objcolor"), GetFrameWeld())) + , m_xBtnMatColor(m_xBuilder->weld_button("colorbutton3")) + , m_xLbMatEmission(new ColorListBox(m_xBuilder->weld_menu_button("illumcolor"), GetFrameWeld())) + , m_xBtnEmissionColor(m_xBuilder->weld_button("colorbutton4")) + + , m_xFLMatSpecular(m_xBuilder->weld_container("specframe")) + , m_xLbMatSpecular(new ColorListBox(m_xBuilder->weld_menu_button("speccolor"), GetFrameWeld())) + , m_xBtnSpecularColor(m_xBuilder->weld_button("colorbutton5")) + , m_xMtrMatSpecularIntensity(m_xBuilder->weld_metric_spin_button("intensity", FieldUnit::PERCENT)) + + , m_xCtlPreview(new Svx3DPreviewControl) + , m_xCtlPreviewWin(new weld::CustomWeld(*m_xBuilder, "preview", *m_xCtlPreview)) + + , m_xLightPreviewGrid(m_xBuilder->weld_container("lightpreviewgrid")) + , m_xHoriScale(m_xBuilder->weld_scale("horiscale")) + , m_xVertScale(m_xBuilder->weld_scale("vertscale")) + , m_xBtn_Corner(m_xBuilder->weld_button("corner")) + , m_xLightPreview(new Svx3DLightControl) + , m_xCtlLightPreviewWin(new weld::CustomWeld(*m_xBuilder, "lightpreview", *m_xLightPreview)) + , m_xCtlLightPreview(new SvxLightCtl3D(*m_xLightPreview, *m_xHoriScale, *m_xVertScale, *m_xBtn_Corner)) // TODO might be other body widget as arg 1 + + , m_xBtnConvertTo3D(m_xBuilder->weld_button("to3d")) + , m_xBtnLatheObject(m_xBuilder->weld_button("tolathe")) + , m_xBtnPerspective(m_xBuilder->weld_toggle_button("perspective")) + + , bUpdate(false) + , eViewType(ViewType3D::Geo) + , pBindings(pInBindings) + , mpImpl(new Svx3DWinImpl) + , ePoolUnit(MapUnit::MapMM) +{ + SetText(SvxResId(RID_SVXDLG_FLOAT3D_STR_TITLE)); + + weld::DrawingArea* pDrawingArea = m_xCtlPreview->GetDrawingArea(); + Size aSize(pDrawingArea->get_ref_device().LogicToPixel(Size(83, 76), MapMode(MapUnit::MapAppFont))); + pDrawingArea->set_size_request(aSize.Width(), aSize.Height()); + m_xCtlPreview->SetOutputSizePixel(aSize); + + m_xLightPreviewGrid->set_size_request(aSize.Width(), aSize.Height()); + pDrawingArea = m_xLightPreview->GetDrawingArea(); + pDrawingArea->set_size_request(42, 42); // small to fit to m_xLightPreviewGrid + + mpImpl->pPool = nullptr; + + // Set Metric + eFUnit = pInBindings->GetDispatcher()->GetModule()->GetFieldUnit(); + + m_xMtrDepth->set_unit( eFUnit ); + m_xMtrDistance->set_unit( eFUnit ); + m_xMtrFocalLength->set_unit( eFUnit ); + + pControllerItem.reset( new Svx3DCtrlItem(SID_3D_STATE, pBindings) ); + pConvertTo3DItem.reset( new SvxConvertTo3DItem(SID_CONVERT_TO_3D, pBindings) ); + pConvertTo3DLatheItem.reset( new SvxConvertTo3DItem(SID_CONVERT_TO_3D_LATHE_FAST, pBindings) ); + + m_xBtnAssign->connect_clicked( LINK( this, Svx3DWin, ClickAssignHdl ) ); + m_xBtnUpdate->connect_toggled( LINK( this, Svx3DWin, ClickUpdateHdl ) ); + + Link<weld::Button&,void> aLink( LINK( this, Svx3DWin, ClickViewTypeHdl ) ); + m_xBtnGeo->connect_clicked( aLink ); + m_xBtnRepresentation->connect_clicked( aLink ); + m_xBtnLight->connect_clicked( aLink ); + m_xBtnTexture->connect_clicked( aLink ); + m_xBtnMaterial->connect_clicked( aLink ); + + aLink = LINK( this, Svx3DWin, ClickHdl ); + m_xBtnPerspective->connect_clicked( aLink ); + m_xBtnConvertTo3D->connect_clicked( aLink ); + m_xBtnLatheObject->connect_clicked( aLink ); + + // Geometry + m_xBtnNormalsObj->connect_clicked( aLink ); + m_xBtnNormalsFlat->connect_clicked( aLink ); + m_xBtnNormalsSphere->connect_clicked( aLink ); + m_xBtnTwoSidedLighting->connect_clicked( aLink ); + m_xBtnNormalsInvert->connect_clicked( aLink ); + m_xBtnDoubleSided->connect_clicked( aLink ); + + // Representation + m_xBtnShadow3d->connect_clicked( aLink ); + + // Lighting + m_xBtnLight1->connect_clicked( aLink ); + m_xBtnLight2->connect_clicked( aLink ); + m_xBtnLight3->connect_clicked( aLink ); + m_xBtnLight4->connect_clicked( aLink ); + m_xBtnLight5->connect_clicked( aLink ); + m_xBtnLight6->connect_clicked( aLink ); + m_xBtnLight7->connect_clicked( aLink ); + m_xBtnLight8->connect_clicked( aLink ); + + // Textures + m_xBtnTexLuminance->connect_clicked( aLink ); + m_xBtnTexColor->connect_clicked( aLink ); + m_xBtnTexReplace->connect_clicked( aLink ); + m_xBtnTexModulate->connect_clicked( aLink ); + m_xBtnTexParallelX->connect_clicked( aLink ); + m_xBtnTexCircleX->connect_clicked( aLink ); + m_xBtnTexObjectX->connect_clicked( aLink ); + m_xBtnTexParallelY->connect_clicked( aLink ); + m_xBtnTexCircleY->connect_clicked( aLink ); + m_xBtnTexObjectY->connect_clicked( aLink ); + m_xBtnTexFilter->connect_clicked( aLink ); + + // Material + aLink = LINK( this, Svx3DWin, ClickColorHdl ); + m_xBtnLightColor->connect_clicked( aLink ); + m_xBtnAmbientColor->connect_clicked( aLink ); + m_xBtnMatColor->connect_clicked( aLink ); + m_xBtnEmissionColor->connect_clicked( aLink ); + m_xBtnSpecularColor->connect_clicked( aLink ); + + + Link<weld::ComboBox&,void> aLink2 = LINK( this, Svx3DWin, SelectHdl ); + Link<ColorListBox&,void> aLink4 = LINK( this, Svx3DWin, SelectColorHdl ); + m_xLbMatFavorites->connect_changed( aLink2 ); + m_xLbMatColor->SetSelectHdl( aLink4 ); + m_xLbMatEmission->SetSelectHdl( aLink4 ); + m_xLbMatSpecular->SetSelectHdl( aLink4 ); + m_xLbLight1->SetSelectHdl( aLink4 ); + m_xLbLight2->SetSelectHdl( aLink4 ); + m_xLbLight3->SetSelectHdl( aLink4 ); + m_xLbLight4->SetSelectHdl( aLink4 ); + m_xLbLight5->SetSelectHdl( aLink4 ); + m_xLbLight6->SetSelectHdl( aLink4 ); + m_xLbLight7->SetSelectHdl( aLink4 ); + m_xLbLight8->SetSelectHdl( aLink4 ); + m_xLbAmbientlight->SetSelectHdl( aLink4 ); + m_xLbShademode->connect_changed( aLink2 ); + + Link<weld::MetricSpinButton&,void> aLink3 = LINK( this, Svx3DWin, ModifyMetricHdl ); + Link<weld::SpinButton&,void> aLink5 = LINK( this, Svx3DWin, ModifySpinHdl ); + m_xMtrMatSpecularIntensity->connect_value_changed( aLink3 ); + m_xNumHorizontal->connect_value_changed( aLink5 ); + m_xNumVertical->connect_value_changed( aLink5 ); + m_xMtrSlant->connect_value_changed( aLink3 ); + + // Preview callback + m_xCtlLightPreview->SetUserSelectionChangeCallback(LINK( this, Svx3DWin, ChangeSelectionCallbackHdl )); + + aSize = GetOutputSizePixel(); + SetMinOutputSizePixel( aSize ); + + Construct(); + + // Initiation of the initialization of the ColorLBs + SfxDispatcher* pDispatcher = LocalGetDispatcher(pBindings); + if (pDispatcher != nullptr) + { + SfxBoolItem aItem( SID_3D_INIT, true ); + pDispatcher->ExecuteList(SID_3D_INIT, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, { &aItem }); + } + + Reset(); + + //lock down the size of the initial largest default mode as the permanent size + aSize = get_preferred_size(); + set_width_request(aSize.Width()); + set_height_request(aSize.Height()); +} + +Svx3DWin::~Svx3DWin() +{ + disposeOnce(); +} + +void Svx3DWin::dispose() +{ + pModel.reset(); + + pControllerItem.reset(); + pConvertTo3DItem.reset(); + pConvertTo3DLatheItem.reset(); + + mpImpl.reset(); + + m_xBtnGeo.reset(); + m_xBtnRepresentation.reset(); + m_xBtnLight.reset(); + m_xBtnTexture.reset(); + m_xBtnMaterial.reset(); + m_xBtnUpdate.reset(); + m_xBtnAssign.reset(); + m_xFLGeometrie.reset(); + m_xFtPercentDiagonal.reset(); + m_xMtrPercentDiagonal.reset(); + m_xFtBackscale.reset(); + m_xMtrBackscale.reset(); + m_xFtEndAngle.reset(); + m_xMtrEndAngle.reset(); + m_xFtDepth.reset(); + m_xMtrDepth.reset(); + m_xFLSegments.reset(); + m_xNumHorizontal.reset(); + m_xNumVertical.reset(); + m_xFLNormals.reset(); + m_xBtnNormalsObj.reset(); + m_xBtnNormalsFlat.reset(); + m_xBtnNormalsSphere.reset(); + m_xBtnNormalsInvert.reset(); + m_xBtnTwoSidedLighting.reset(); + m_xBtnDoubleSided.reset(); + m_xFLRepresentation.reset(); + m_xLbShademode.reset(); + m_xFLShadow.reset(); + m_xBtnShadow3d.reset(); + m_xFtSlant.reset(); + m_xMtrSlant.reset(); + m_xFLCamera.reset(); + m_xMtrDistance.reset(); + m_xMtrFocalLength.reset(); + m_xFLLight.reset(); + m_xBtnLight1.reset(); + m_xBtnLight2.reset(); + m_xBtnLight3.reset(); + m_xBtnLight4.reset(); + m_xBtnLight5.reset(); + m_xBtnLight6.reset(); + m_xBtnLight7.reset(); + m_xBtnLight8.reset(); + m_xLbLight1.reset(); + m_xLbLight2.reset(); + m_xLbLight3.reset(); + m_xLbLight4.reset(); + m_xLbLight5.reset(); + m_xLbLight6.reset(); + m_xLbLight7.reset(); + m_xLbLight8.reset(); + m_xBtnLightColor.reset(); + m_xLbAmbientlight.reset(); + m_xBtnAmbientColor.reset(); + m_xFLTexture.reset(); + m_xBtnTexLuminance.reset(); + m_xBtnTexColor.reset(); + m_xBtnTexReplace.reset(); + m_xBtnTexModulate.reset(); + m_xBtnTexBlend.reset(); + m_xBtnTexObjectX.reset(); + m_xBtnTexParallelX.reset(); + m_xBtnTexCircleX.reset(); + m_xBtnTexObjectY.reset(); + m_xBtnTexParallelY.reset(); + m_xBtnTexCircleY.reset(); + m_xBtnTexFilter.reset(); + m_xFLMaterial.reset(); + m_xLbMatFavorites.reset(); + m_xLbMatColor.reset(); + m_xBtnMatColor.reset(); + m_xLbMatEmission.reset(); + m_xBtnEmissionColor.reset(); + m_xFLMatSpecular.reset(); + m_xLbMatSpecular.reset(); + m_xBtnSpecularColor.reset(); + m_xMtrMatSpecularIntensity.reset(); + m_xCtlPreviewWin.reset(); + m_xCtlPreview.reset(); + + m_xCtlLightPreview.reset(); + m_xCtlLightPreviewWin.reset(); + m_xLightPreview.reset(); + m_xBtn_Corner.reset(); + m_xVertScale.reset(); + m_xHoriScale.reset(); + m_xLightPreviewGrid.reset(); + + m_xBtnConvertTo3D.reset(); + m_xBtnLatheObject.reset(); + m_xBtnPerspective.reset(); + + SfxDockingWindow::dispose(); +} + +void Svx3DWin::Construct() +{ + m_xBtnGeo->set_active(true); + ClickViewTypeHdl(*m_xBtnGeo); + m_xLightPreviewGrid->hide(); +} + +void Svx3DWin::Reset() +{ + // Various initializations, default is AllAttributes + m_xLbShademode->set_active( 0 ); + m_xMtrMatSpecularIntensity->set_value( 50, FieldUnit::PERCENT ); + + m_xBtnLight1->set_active(true); + m_xBtnUpdate->set_active(true); + ClickUpdateHdl(*m_xBtnUpdate); + + // Select nothing, to avoid errors when selecting the first + m_xCtlLightPreview->GetSvx3DLightControl().SelectLight(0); + m_xCtlLightPreview->CheckSelection(); +} + +void Svx3DWin::Update( SfxItemSet const & rAttrs ) +{ + // remember 2d attributes + if(mpRemember2DAttributes) + mpRemember2DAttributes->ClearItem(); + else + mpRemember2DAttributes = std::make_unique<SfxItemSet>(*rAttrs.GetPool(), + svl::Items<SDRATTR_START, SDRATTR_SHADOW_LAST, + SDRATTR_3D_FIRST, SDRATTR_3D_LAST>{}); + + SfxWhichIter aIter(*mpRemember2DAttributes); + sal_uInt16 nWhich(aIter.FirstWhich()); + + while(nWhich) + { + SfxItemState eState = rAttrs.GetItemState(nWhich, false); + if(SfxItemState::DONTCARE == eState) + mpRemember2DAttributes->InvalidateItem(nWhich); + else if(SfxItemState::SET == eState) + mpRemember2DAttributes->Put(rAttrs.Get(nWhich, false)); + + nWhich = aIter.NextWhich(); + } + + // construct field values + const SfxPoolItem* pItem; + + // Possible determine PoolUnit + if( !mpImpl->pPool ) + { + mpImpl->pPool = rAttrs.GetPool(); + DBG_ASSERT( mpImpl->pPool, "Where is the Pool? "); + ePoolUnit = mpImpl->pPool->GetMetric( SID_ATTR_LINE_WIDTH ); + } + eFUnit = GetModuleFieldUnit( rAttrs ); + + + // Segment Number Can be changed? and other states + SfxItemState eState = rAttrs.GetItemState( SID_ATTR_3D_INTERN, false, &pItem ); + if( SfxItemState::SET == eState ) + { + sal_uInt32 nState = static_cast<const SfxUInt32Item*>(pItem)->GetValue(); + bool bExtrude = ( nState & 2 ); + bool bSphere = ( nState & 4 ); + bool bCube = ( nState & 8 ); + + bool bChart = ( nState & 32 ); // Chart + + if( !bChart ) + { + // For cube objects are no segments set + m_xFLSegments->set_sensitive(!bCube); + + m_xFtPercentDiagonal->set_sensitive( !bCube && !bSphere ); + m_xMtrPercentDiagonal->set_sensitive( !bCube && !bSphere ); + m_xFtBackscale->set_sensitive( !bCube && !bSphere ); + m_xMtrBackscale->set_sensitive( !bCube && !bSphere ); + m_xFtDepth->set_sensitive( !bCube && !bSphere ); + m_xMtrDepth->set_sensitive( !bCube && !bSphere ); + if( bCube ) + { + m_xNumHorizontal->set_text(""); + m_xNumVertical->set_text(""); + } + if( bCube || bSphere ) + { + m_xMtrPercentDiagonal->set_text(""); + m_xMtrBackscale->set_text(""); + m_xMtrDepth->set_text(""); + } + + // There is a final angle only for Lathe objects. + m_xFtEndAngle->set_sensitive( !bExtrude && !bCube && !bSphere ); + m_xMtrEndAngle->set_sensitive( !bExtrude && !bCube && !bSphere ); + if( bExtrude || bCube || bSphere ) + m_xMtrEndAngle->set_text(""); + } + else + { + // Geometry + m_xNumHorizontal->set_text(""); + m_xNumVertical->set_text(""); + m_xFLSegments->set_sensitive( false ); + m_xFtEndAngle->set_sensitive( false ); + m_xMtrEndAngle->set_sensitive( false ); + m_xMtrEndAngle->set_text(""); + m_xFtDepth->set_sensitive( false ); + m_xMtrDepth->set_sensitive( false ); + m_xMtrDepth->set_text(""); + + // Representation + m_xFLShadow->set_sensitive(false); + + m_xMtrDistance->set_text(""); + m_xMtrFocalLength->set_text(""); + m_xFLCamera->set_sensitive( false ); + + //Lower Range + m_xBtnConvertTo3D->set_sensitive( false ); + m_xBtnLatheObject->set_sensitive( false ); + } + } + // Bitmap fill ? -> Status + bool bBitmap(false); + eState = rAttrs.GetItemState(XATTR_FILLSTYLE); + if(eState != SfxItemState::DONTCARE) + { + drawing::FillStyle eXFS = rAttrs.Get(XATTR_FILLSTYLE).GetValue(); + bBitmap = (eXFS == drawing::FillStyle_BITMAP || eXFS == drawing::FillStyle_GRADIENT || eXFS == drawing::FillStyle_HATCH); + } + + m_xFLTexture->set_sensitive(bBitmap); + + // Geometry + // Number of segments (horizontal) + if( m_xNumHorizontal->get_sensitive() ) + { + eState = rAttrs.GetItemState(SDRATTR_3DOBJ_HORZ_SEGS); + if(eState != SfxItemState::DONTCARE) + { + sal_uInt32 nValue = rAttrs.Get(SDRATTR_3DOBJ_HORZ_SEGS).GetValue(); + if (nValue != static_cast<sal_uInt32>(m_xNumHorizontal->get_value())) + { + m_xNumHorizontal->set_value( nValue ); + bUpdate = true; + } + else if( m_xNumHorizontal->get_text().isEmpty() ) + m_xNumHorizontal->set_value( nValue ); + } + else + { + if( !m_xNumHorizontal->get_text().isEmpty() ) + { + m_xNumHorizontal->set_text(""); + bUpdate = true; + } + } + } + + //Number of segments (vertical) + if( m_xNumVertical->get_sensitive() ) + { + eState = rAttrs.GetItemState(SDRATTR_3DOBJ_VERT_SEGS); + if( eState != SfxItemState::DONTCARE ) + { + sal_uInt32 nValue = rAttrs.Get(SDRATTR_3DOBJ_VERT_SEGS).GetValue(); + if( nValue != static_cast<sal_uInt32>(m_xNumVertical->get_value()) ) + { + m_xNumVertical->set_value( nValue ); + bUpdate = true; + } + else if( m_xNumVertical->get_text().isEmpty() ) + m_xNumVertical->set_value( nValue ); + } + else + { + if( !m_xNumVertical->get_text().isEmpty() ) + { + m_xNumVertical->set_text(""); + bUpdate = true; + } + } + } + + // Depth + if( m_xMtrDepth->get_sensitive() ) + { + eState = rAttrs.GetItemState(SDRATTR_3DOBJ_DEPTH); + if( eState != SfxItemState::DONTCARE ) + { + sal_uInt32 nValue = rAttrs.Get(SDRATTR_3DOBJ_DEPTH).GetValue(); + sal_uInt32 nValue2 = GetCoreValue(*m_xMtrDepth, ePoolUnit); + if( nValue != nValue2 ) + { + if( eFUnit != m_xMtrDepth->get_unit() ) + SetFieldUnit(*m_xMtrDepth, eFUnit); + + SetMetricValue(*m_xMtrDepth, nValue, ePoolUnit); + bUpdate = true; + } + else if( m_xMtrDepth->get_text().isEmpty() ) + m_xMtrDepth->set_value(m_xMtrDepth->get_value(FieldUnit::NONE), FieldUnit::NONE); + } + else + { + if( !m_xMtrDepth->get_text().isEmpty() ) + { + m_xMtrDepth->set_text(""); + bUpdate = true; + } + } + } + + // Double walled / Double sided + eState = rAttrs.GetItemState(SDRATTR_3DOBJ_DOUBLE_SIDED); + if( eState != SfxItemState::DONTCARE ) + { + bool bValue = rAttrs.Get(SDRATTR_3DOBJ_DOUBLE_SIDED).GetValue(); + if( bValue != m_xBtnDoubleSided->get_active() ) + { + m_xBtnDoubleSided->set_active( bValue ); + bUpdate = true; + } + else if( m_xBtnDoubleSided->get_state() == TRISTATE_INDET ) + m_xBtnDoubleSided->set_active( bValue ); + } + else + { + if( m_xBtnDoubleSided->get_state() != TRISTATE_INDET ) + { + m_xBtnDoubleSided->set_state( TRISTATE_INDET ); + bUpdate = true; + } + } + + // Edge rounding + if( m_xMtrPercentDiagonal->get_sensitive() ) + { + eState = rAttrs.GetItemState(SDRATTR_3DOBJ_PERCENT_DIAGONAL); + if( eState != SfxItemState::DONTCARE ) + { + sal_uInt16 nValue = rAttrs.Get(SDRATTR_3DOBJ_PERCENT_DIAGONAL).GetValue(); + if( nValue != m_xMtrPercentDiagonal->get_value(FieldUnit::PERCENT) ) + { + m_xMtrPercentDiagonal->set_value(nValue, FieldUnit::PERCENT); + bUpdate = true; + } + else if( m_xMtrPercentDiagonal->get_text().isEmpty() ) + m_xMtrPercentDiagonal->set_value(nValue, FieldUnit::PERCENT); + } + else + { + if( !m_xMtrPercentDiagonal->get_text().isEmpty() ) + { + m_xMtrPercentDiagonal->set_text(""); + bUpdate = true; + } + } + } + + // Depth scaling + if( m_xMtrBackscale->get_sensitive() ) + { + eState = rAttrs.GetItemState(SDRATTR_3DOBJ_BACKSCALE); + if( eState != SfxItemState::DONTCARE ) + { + sal_uInt16 nValue = rAttrs.Get(SDRATTR_3DOBJ_BACKSCALE).GetValue(); + if( nValue != m_xMtrBackscale->get_value(FieldUnit::PERCENT) ) + { + m_xMtrBackscale->set_value(nValue, FieldUnit::PERCENT); + bUpdate = true; + } + else if( m_xMtrBackscale->get_text().isEmpty() ) + m_xMtrBackscale->set_value(nValue, FieldUnit::PERCENT); + } + else + { + if( !m_xMtrBackscale->get_text().isEmpty() ) + { + m_xMtrBackscale->set_text(""); + bUpdate = true; + } + } + } + + // End angle + if( m_xMtrEndAngle->get_sensitive() ) + { + eState = rAttrs.GetItemState(SDRATTR_3DOBJ_END_ANGLE); + if( eState != SfxItemState::DONTCARE ) + { + sal_Int32 nValue = rAttrs.Get(SDRATTR_3DOBJ_END_ANGLE).GetValue(); + if( nValue != m_xMtrEndAngle->get_value(FieldUnit::DEGREE) ) + { + m_xMtrEndAngle->set_value(nValue, FieldUnit::DEGREE); + bUpdate = true; + } + } + else + { + if( !m_xMtrEndAngle->get_text().isEmpty() ) + { + m_xMtrEndAngle->set_text(""); + bUpdate = true; + } + } + } + + // Normal type + eState = rAttrs.GetItemState(SDRATTR_3DOBJ_NORMALS_KIND); + if( eState != SfxItemState::DONTCARE ) + { + sal_uInt16 nValue = rAttrs.Get(SDRATTR_3DOBJ_NORMALS_KIND).GetValue(); + + if( ( !m_xBtnNormalsObj->get_active() && nValue == 0 ) || + ( !m_xBtnNormalsFlat->get_active() && nValue == 1 ) || + ( !m_xBtnNormalsSphere->get_active() && nValue == 2 ) ) + { + m_xBtnNormalsObj->set_active( nValue == 0 ); + m_xBtnNormalsFlat->set_active( nValue == 1 ); + m_xBtnNormalsSphere->set_active( nValue == 2 ); + bUpdate = true; + } + } + else + { + if( m_xBtnNormalsObj->get_active() || + m_xBtnNormalsFlat->get_active() || + m_xBtnNormalsSphere->get_active() ) + { + m_xBtnNormalsObj->set_active( false ); + m_xBtnNormalsFlat->set_active( false ); + m_xBtnNormalsSphere->set_active( false ); + bUpdate = true; + } + } + + // Normal inverted + eState = rAttrs.GetItemState(SDRATTR_3DOBJ_NORMALS_INVERT); + if( eState != SfxItemState::DONTCARE ) + { + bool bValue = rAttrs.Get(SDRATTR_3DOBJ_NORMALS_INVERT).GetValue(); + if( bValue != m_xBtnNormalsInvert->get_active() ) + { + m_xBtnNormalsInvert->set_active( bValue ); + bUpdate = true; + } + else if( m_xBtnNormalsInvert->get_state() == TRISTATE_INDET ) + m_xBtnNormalsInvert->set_active( bValue ); + } + else + { + if( m_xBtnNormalsInvert->get_state() != TRISTATE_INDET ) + { + m_xBtnNormalsInvert->set_state( TRISTATE_INDET ); + bUpdate = true; + } + } + + // 2-sided lighting + eState = rAttrs.GetItemState(SDRATTR_3DSCENE_TWO_SIDED_LIGHTING); + if( eState != SfxItemState::DONTCARE ) + { + bool bValue = rAttrs.Get(SDRATTR_3DSCENE_TWO_SIDED_LIGHTING).GetValue(); + if( bValue != m_xBtnTwoSidedLighting->get_active() ) + { + m_xBtnTwoSidedLighting->set_active( bValue ); + bUpdate = true; + } + else if( m_xBtnTwoSidedLighting->get_state() == TRISTATE_INDET ) + m_xBtnTwoSidedLighting->set_active( bValue ); + } + else + { + if( m_xBtnTwoSidedLighting->get_state() != TRISTATE_INDET ) + { + m_xBtnTwoSidedLighting->set_state( TRISTATE_INDET ); + bUpdate = true; + } + } + + // Representation + // Shademode + eState = rAttrs.GetItemState(SDRATTR_3DSCENE_SHADE_MODE); + if( eState != SfxItemState::DONTCARE ) + { + sal_uInt16 nValue = rAttrs.Get(SDRATTR_3DSCENE_SHADE_MODE).GetValue(); + if( nValue != m_xLbShademode->get_active() ) + { + m_xLbShademode->set_active( nValue ); + bUpdate = true; + } + } + else + { + if( m_xLbShademode->get_active() != 0 ) + { + m_xLbShademode->set_active(-1); + bUpdate = true; + } + } + + // 3D-Shadow + eState = rAttrs.GetItemState(SDRATTR_3DOBJ_SHADOW_3D); + if( eState != SfxItemState::DONTCARE ) + { + bool bValue = rAttrs.Get(SDRATTR_3DOBJ_SHADOW_3D).GetValue(); + if( bValue != m_xBtnShadow3d->get_active() ) + { + m_xBtnShadow3d->set_active( bValue ); + m_xFtSlant->set_sensitive( bValue ); + m_xMtrSlant->set_sensitive( bValue ); + bUpdate = true; + } + else if( m_xBtnShadow3d->get_state() == TRISTATE_INDET ) + m_xBtnShadow3d->set_active( bValue ); + } + else + { + if( m_xBtnShadow3d->get_state() != TRISTATE_INDET ) + { + m_xBtnShadow3d->set_state( TRISTATE_INDET ); + bUpdate = true; + } + } + + // Inclination (Shadow) + eState = rAttrs.GetItemState(SDRATTR_3DSCENE_SHADOW_SLANT); + if( eState != SfxItemState::DONTCARE ) + { + sal_uInt16 nValue = rAttrs.Get(SDRATTR_3DSCENE_SHADOW_SLANT).GetValue(); + if( nValue != m_xMtrSlant->get_value(FieldUnit::DEGREE) ) + { + m_xMtrSlant->set_value(nValue, FieldUnit::DEGREE); + bUpdate = true; + } + } + else + { + if( !m_xMtrSlant->get_text().isEmpty() ) + { + m_xMtrSlant->set_text(""); + bUpdate = true; + } + } + + // Distance + eState = rAttrs.GetItemState(SDRATTR_3DSCENE_DISTANCE); + if( eState != SfxItemState::DONTCARE ) + { + sal_uInt32 nValue = rAttrs.Get(SDRATTR_3DSCENE_DISTANCE).GetValue(); + sal_uInt32 nValue2 = GetCoreValue(*m_xMtrDistance, ePoolUnit); + if( nValue != nValue2 ) + { + if( eFUnit != m_xMtrDistance->get_unit() ) + SetFieldUnit(*m_xMtrDistance, eFUnit); + + SetMetricValue(*m_xMtrDistance, nValue, ePoolUnit); + bUpdate = true; + } + } + else + { + if( !m_xMtrDepth->get_text().isEmpty() ) + { + m_xMtrDepth->set_text(""); + bUpdate = true; + } + } + + // Focal length + eState = rAttrs.GetItemState(SDRATTR_3DSCENE_FOCAL_LENGTH); + if( eState != SfxItemState::DONTCARE ) + { + sal_uInt32 nValue = rAttrs.Get(SDRATTR_3DSCENE_FOCAL_LENGTH).GetValue(); + sal_uInt32 nValue2 = GetCoreValue(*m_xMtrFocalLength, ePoolUnit); + if( nValue != nValue2 ) + { + if( eFUnit != m_xMtrFocalLength->get_unit() ) + SetFieldUnit(*m_xMtrFocalLength, eFUnit); + + SetMetricValue(*m_xMtrFocalLength, nValue, ePoolUnit); + bUpdate = true; + } + } + else + { + if( !m_xMtrFocalLength->get_text().isEmpty() ) + { + m_xMtrFocalLength->set_text(""); + bUpdate = true; + } + } + +// Lighting + Color aColor; + // Light 1 (Color) + eState = rAttrs.GetItemState(SDRATTR_3DSCENE_LIGHTCOLOR_1); + if( eState != SfxItemState::DONTCARE ) + { + aColor = rAttrs.Get(SDRATTR_3DSCENE_LIGHTCOLOR_1).GetValue(); + ColorListBox* pLb = m_xLbLight1.get(); + if( aColor != pLb->GetSelectEntryColor() ) + { + LBSelectColor( pLb, aColor ); + bUpdate = true; + } + } + else + { + if (!m_xLbLight1->IsNoSelection()) + { + m_xLbLight1->SetNoSelection(); + bUpdate = true; + } + } + // Light 1 (on/off) + eState = rAttrs.GetItemState(SDRATTR_3DSCENE_LIGHTON_1); + if( eState != SfxItemState::DONTCARE ) + { + bool bOn = rAttrs.Get(SDRATTR_3DSCENE_LIGHTON_1).GetValue(); + if (bOn != m_xBtnLight1->isLightOn()) + { + m_xBtnLight1->switchLightOn(bOn); + bUpdate = true; + } + if( m_xBtnLight1->get_state() == TRISTATE_INDET ) + m_xBtnLight1->set_active( m_xBtnLight1->get_active() ); + } + else + { + if( m_xBtnLight1->get_state() != TRISTATE_INDET ) + { + m_xBtnLight1->set_state( TRISTATE_INDET ); + bUpdate = true; + } + } + // Light 1 (direction) + eState = rAttrs.GetItemState(SDRATTR_3DSCENE_LIGHTDIRECTION_1); + if( eState != SfxItemState::DONTCARE ) + { + bUpdate = true; + } + + //Light 2 (color) + eState = rAttrs.GetItemState(SDRATTR_3DSCENE_LIGHTCOLOR_2); + if( eState != SfxItemState::DONTCARE ) + { + aColor = rAttrs.Get(SDRATTR_3DSCENE_LIGHTCOLOR_2).GetValue(); + ColorListBox* pLb = m_xLbLight2.get(); + if( aColor != pLb->GetSelectEntryColor() ) + { + LBSelectColor( pLb, aColor ); + bUpdate = true; + } + } + else + { + if (!m_xLbLight2->IsNoSelection()) + { + m_xLbLight2->SetNoSelection(); + bUpdate = true; + } + } + // Light 2 (on/off) + eState = rAttrs.GetItemState(SDRATTR_3DSCENE_LIGHTON_2); + if( eState != SfxItemState::DONTCARE ) + { + bool bOn = rAttrs.Get(SDRATTR_3DSCENE_LIGHTON_2).GetValue(); + if (bOn != m_xBtnLight2->isLightOn()) + { + m_xBtnLight2->switchLightOn(bOn); + bUpdate = true; + } + if( m_xBtnLight2->get_state() == TRISTATE_INDET ) + m_xBtnLight2->set_active( m_xBtnLight2->get_active() ); + } + else + { + if( m_xBtnLight2->get_state() != TRISTATE_INDET ) + { + m_xBtnLight2->set_state( TRISTATE_INDET ); + bUpdate = true; + } + } + //Light 2 (Direction) + eState = rAttrs.GetItemState(SDRATTR_3DSCENE_LIGHTDIRECTION_2); + if( eState != SfxItemState::DONTCARE ) + { + bUpdate = true; + } + + //Light 3 (color) + eState = rAttrs.GetItemState(SDRATTR_3DSCENE_LIGHTCOLOR_3); + if( eState != SfxItemState::DONTCARE ) + { + aColor = rAttrs.Get(SDRATTR_3DSCENE_LIGHTCOLOR_3).GetValue(); + ColorListBox* pLb = m_xLbLight3.get(); + if( aColor != pLb->GetSelectEntryColor() ) + { + LBSelectColor( pLb, aColor ); + bUpdate = true; + } + } + else + { + if (!m_xLbLight3->IsNoSelection()) + { + m_xLbLight3->SetNoSelection(); + bUpdate = true; + } + } + // Light 3 (on/off) + eState = rAttrs.GetItemState(SDRATTR_3DSCENE_LIGHTON_3); + if( eState != SfxItemState::DONTCARE ) + { + bool bOn = rAttrs.Get(SDRATTR_3DSCENE_LIGHTON_3).GetValue(); + if (bOn != m_xBtnLight3->isLightOn()) + { + m_xBtnLight3->switchLightOn(bOn); + bUpdate = true; + } + if( m_xBtnLight3->get_state() == TRISTATE_INDET ) + m_xBtnLight3->set_active( m_xBtnLight3->get_active() ); + } + else + { + if( m_xBtnLight3->get_state() != TRISTATE_INDET ) + { + m_xBtnLight3->set_state( TRISTATE_INDET ); + bUpdate = true; + } + } + // Light 3 (Direction) + eState = rAttrs.GetItemState(SDRATTR_3DSCENE_LIGHTDIRECTION_3); + if( eState != SfxItemState::DONTCARE ) + { + bUpdate = true; + } + + // Light 4 (Color) + eState = rAttrs.GetItemState(SDRATTR_3DSCENE_LIGHTCOLOR_4); + if( eState != SfxItemState::DONTCARE ) + { + aColor = rAttrs.Get(SDRATTR_3DSCENE_LIGHTCOLOR_4).GetValue(); + ColorListBox* pLb = m_xLbLight4.get(); + if( aColor != pLb->GetSelectEntryColor() ) + { + LBSelectColor( pLb, aColor ); + bUpdate = true; + } + } + else + { + if (!m_xLbLight4->IsNoSelection()) + { + m_xLbLight4->SetNoSelection(); + bUpdate = true; + } + } + // Light 4 (on/off) + eState = rAttrs.GetItemState(SDRATTR_3DSCENE_LIGHTON_4); + if( eState != SfxItemState::DONTCARE ) + { + bool bOn = rAttrs.Get(SDRATTR_3DSCENE_LIGHTON_4).GetValue(); + if (bOn != m_xBtnLight4->isLightOn()) + { + m_xBtnLight4->switchLightOn(bOn); + bUpdate = true; + } + if( m_xBtnLight4->get_state() == TRISTATE_INDET ) + m_xBtnLight4->set_active( m_xBtnLight4->get_active() ); + } + else + { + if( m_xBtnLight4->get_state() != TRISTATE_INDET ) + { + m_xBtnLight4->set_state( TRISTATE_INDET ); + bUpdate = true; + } + } + // Light 4 (direction) + eState = rAttrs.GetItemState(SDRATTR_3DSCENE_LIGHTDIRECTION_4); + if( eState != SfxItemState::DONTCARE ) + { + bUpdate = true; + } + + // Light 5 (color) + eState = rAttrs.GetItemState(SDRATTR_3DSCENE_LIGHTCOLOR_5); + if( eState != SfxItemState::DONTCARE ) + { + aColor = rAttrs.Get(SDRATTR_3DSCENE_LIGHTCOLOR_5).GetValue(); + ColorListBox* pLb = m_xLbLight5.get(); + if( aColor != pLb->GetSelectEntryColor() ) + { + LBSelectColor( pLb, aColor ); + bUpdate = true; + } + } + else + { + if (!m_xLbLight5->IsNoSelection()) + { + m_xLbLight5->SetNoSelection(); + bUpdate = true; + } + } + // Light 5 (on/off) + eState = rAttrs.GetItemState(SDRATTR_3DSCENE_LIGHTON_5); + if( eState != SfxItemState::DONTCARE ) + { + bool bOn = rAttrs.Get(SDRATTR_3DSCENE_LIGHTON_5).GetValue(); + if (bOn != m_xBtnLight5->isLightOn()) + { + m_xBtnLight5->switchLightOn(bOn); + bUpdate = true; + } + if( m_xBtnLight5->get_state() == TRISTATE_INDET ) + m_xBtnLight5->set_active( m_xBtnLight5->get_active() ); + } + else + { + if( m_xBtnLight5->get_state() != TRISTATE_INDET ) + { + m_xBtnLight5->set_state( TRISTATE_INDET ); + bUpdate = true; + } + } + // Light 5 (direction) + eState = rAttrs.GetItemState(SDRATTR_3DSCENE_LIGHTDIRECTION_5); + if( eState != SfxItemState::DONTCARE ) + { + bUpdate = true; + } + + // Light 6 (color) + eState = rAttrs.GetItemState(SDRATTR_3DSCENE_LIGHTCOLOR_6); + if( eState != SfxItemState::DONTCARE ) + { + aColor = rAttrs.Get(SDRATTR_3DSCENE_LIGHTCOLOR_6).GetValue(); + ColorListBox* pLb = m_xLbLight6.get(); + if( aColor != pLb->GetSelectEntryColor() ) + { + LBSelectColor( pLb, aColor ); + bUpdate = true; + } + } + else + { + if (!m_xLbLight6->IsNoSelection()) + { + m_xLbLight6->SetNoSelection(); + bUpdate = true; + } + } + // Light 6 (on/off) + eState = rAttrs.GetItemState(SDRATTR_3DSCENE_LIGHTON_6); + if( eState != SfxItemState::DONTCARE ) + { + bool bOn = rAttrs.Get(SDRATTR_3DSCENE_LIGHTON_6).GetValue(); + if (bOn != m_xBtnLight6->isLightOn()) + { + m_xBtnLight6->switchLightOn(bOn); + bUpdate = true; + } + if( m_xBtnLight6->get_state() == TRISTATE_INDET ) + m_xBtnLight6->set_active( m_xBtnLight6->get_active() ); + } + else + { + if( m_xBtnLight6->get_state() != TRISTATE_INDET ) + { + m_xBtnLight6->set_state( TRISTATE_INDET ); + bUpdate = true; + } + } + // Light 6 (direction) + eState = rAttrs.GetItemState(SDRATTR_3DSCENE_LIGHTDIRECTION_6); + if( eState != SfxItemState::DONTCARE ) + { + bUpdate = true; + } + + // Light 7 (color) + eState = rAttrs.GetItemState(SDRATTR_3DSCENE_LIGHTCOLOR_7); + if( eState != SfxItemState::DONTCARE ) + { + aColor = rAttrs.Get(SDRATTR_3DSCENE_LIGHTCOLOR_7).GetValue(); + ColorListBox* pLb = m_xLbLight7.get(); + if( aColor != pLb->GetSelectEntryColor() ) + { + LBSelectColor( pLb, aColor ); + bUpdate = true; + } + } + else + { + if (!m_xLbLight7->IsNoSelection()) + { + m_xLbLight7->SetNoSelection(); + bUpdate = true; + } + } + // Light 7 (on/off) + eState = rAttrs.GetItemState(SDRATTR_3DSCENE_LIGHTON_7); + if( eState != SfxItemState::DONTCARE ) + { + bool bOn = rAttrs.Get(SDRATTR_3DSCENE_LIGHTON_7).GetValue(); + if (bOn != m_xBtnLight7->isLightOn()) + { + m_xBtnLight7->switchLightOn(bOn); + bUpdate = true; + } + if( m_xBtnLight7->get_state() == TRISTATE_INDET ) + m_xBtnLight7->set_active( m_xBtnLight7->get_active() ); + } + else + { + if( m_xBtnLight7->get_state() != TRISTATE_INDET ) + { + m_xBtnLight7->set_state( TRISTATE_INDET ); + bUpdate = true; + } + } + // Light 7 (direction) + eState = rAttrs.GetItemState(SDRATTR_3DSCENE_LIGHTDIRECTION_7); + if( eState != SfxItemState::DONTCARE ) + { + bUpdate = true; + } + + // Light 8 (color) + eState = rAttrs.GetItemState(SDRATTR_3DSCENE_LIGHTCOLOR_8); + if( eState != SfxItemState::DONTCARE ) + { + aColor = rAttrs.Get(SDRATTR_3DSCENE_LIGHTCOLOR_8).GetValue(); + ColorListBox* pLb = m_xLbLight8.get(); + if( aColor != pLb->GetSelectEntryColor() ) + { + LBSelectColor( pLb, aColor ); + bUpdate = true; + } + } + else + { + if (!m_xLbLight8->IsNoSelection()) + { + m_xLbLight8->SetNoSelection(); + bUpdate = true; + } + } + // Light 8 (on/off) + eState = rAttrs.GetItemState(SDRATTR_3DSCENE_LIGHTON_8); + if( eState != SfxItemState::DONTCARE ) + { + bool bOn = rAttrs.Get(SDRATTR_3DSCENE_LIGHTON_8).GetValue(); + if (bOn != m_xBtnLight8->isLightOn()) + { + m_xBtnLight8->switchLightOn(bOn); + bUpdate = true; + } + if( m_xBtnLight8->get_state() == TRISTATE_INDET ) + m_xBtnLight8->set_active( m_xBtnLight8->get_active() ); + } + else + { + if( m_xBtnLight8->get_state() != TRISTATE_INDET ) + { + m_xBtnLight8->set_state( TRISTATE_INDET ); + bUpdate = true; + } + } + // Light 8 (direction) + eState = rAttrs.GetItemState(SDRATTR_3DSCENE_LIGHTDIRECTION_8); + if( eState != SfxItemState::DONTCARE ) + { + bUpdate = true; + } + + // Ambient light + eState = rAttrs.GetItemState(SDRATTR_3DSCENE_AMBIENTCOLOR); + if( eState != SfxItemState::DONTCARE ) + { + aColor = rAttrs.Get(SDRATTR_3DSCENE_AMBIENTCOLOR).GetValue(); + ColorListBox* pLb = m_xLbAmbientlight.get(); + if( aColor != pLb->GetSelectEntryColor() ) + { + LBSelectColor( pLb, aColor ); + bUpdate = true; + } + } + else + { + if (!m_xLbAmbientlight->IsNoSelection()) + { + m_xLbAmbientlight->SetNoSelection(); + bUpdate = true; + } + } + + +// Textures + // Art + if( bBitmap ) + { + eState = rAttrs.GetItemState(SDRATTR_3DOBJ_TEXTURE_KIND); + if( eState != SfxItemState::DONTCARE ) + { + sal_uInt16 nValue = rAttrs.Get(SDRATTR_3DOBJ_TEXTURE_KIND).GetValue(); + + if( ( !m_xBtnTexLuminance->get_active() && nValue == 1 ) || + ( !m_xBtnTexColor->get_active() && nValue == 3 ) ) + { + m_xBtnTexLuminance->set_active( nValue == 1 ); + m_xBtnTexColor->set_active( nValue == 3 ); + bUpdate = true; + } + } + else + { + if( m_xBtnTexLuminance->get_active() || + m_xBtnTexColor->get_active() ) + { + m_xBtnTexLuminance->set_active( false ); + m_xBtnTexColor->set_active( false ); + bUpdate = true; + } + } + + // Mode + eState = rAttrs.GetItemState(SDRATTR_3DOBJ_TEXTURE_MODE); + if( eState != SfxItemState::DONTCARE ) + { + sal_uInt16 nValue = rAttrs.Get(SDRATTR_3DOBJ_TEXTURE_MODE).GetValue(); + + if( ( !m_xBtnTexReplace->get_active() && nValue == 1 ) || + ( !m_xBtnTexModulate->get_active() && nValue == 2 ) ) + { + m_xBtnTexReplace->set_active( nValue == 1 ); + m_xBtnTexModulate->set_active( nValue == 2 ); + bUpdate = true; + } + } + else + { + if( m_xBtnTexReplace->get_active() || + m_xBtnTexModulate->get_active() ) + { + m_xBtnTexReplace->set_active( false ); + m_xBtnTexModulate->set_active( false ); + bUpdate = true; + } + } + + // Projection X + eState = rAttrs.GetItemState(SDRATTR_3DOBJ_TEXTURE_PROJ_X); + if( eState != SfxItemState::DONTCARE ) + { + sal_uInt16 nValue = rAttrs.Get(SDRATTR_3DOBJ_TEXTURE_PROJ_X).GetValue(); + + if( ( !m_xBtnTexObjectX->get_active() && nValue == 0 ) || + ( !m_xBtnTexParallelX->get_active() && nValue == 1 ) || + ( !m_xBtnTexCircleX->get_active() && nValue == 2 ) ) + { + m_xBtnTexObjectX->set_active( nValue == 0 ); + m_xBtnTexParallelX->set_active( nValue == 1 ); + m_xBtnTexCircleX->set_active( nValue == 2 ); + bUpdate = true; + } + } + else + { + if( m_xBtnTexObjectX->get_active() || + m_xBtnTexParallelX->get_active() || + m_xBtnTexCircleX->get_active() ) + { + m_xBtnTexObjectX->set_active( false ); + m_xBtnTexParallelX->set_active( false ); + m_xBtnTexCircleX->set_active( false ); + bUpdate = true; + } + } + + // Projection Y + eState = rAttrs.GetItemState(SDRATTR_3DOBJ_TEXTURE_PROJ_Y); + if( eState != SfxItemState::DONTCARE ) + { + sal_uInt16 nValue = rAttrs.Get(SDRATTR_3DOBJ_TEXTURE_PROJ_Y).GetValue(); + + if( ( !m_xBtnTexObjectY->get_active() && nValue == 0 ) || + ( !m_xBtnTexParallelY->get_active() && nValue == 1 ) || + ( !m_xBtnTexCircleY->get_active() && nValue == 2 ) ) + { + m_xBtnTexObjectY->set_active( nValue == 0 ); + m_xBtnTexParallelY->set_active( nValue == 1 ); + m_xBtnTexCircleY->set_active( nValue == 2 ); + bUpdate = true; + } + } + else + { + if( m_xBtnTexObjectY->get_active() || + m_xBtnTexParallelY->get_active() || + m_xBtnTexCircleY->get_active() ) + { + m_xBtnTexObjectY->set_active( false ); + m_xBtnTexParallelY->set_active( false ); + m_xBtnTexCircleY->set_active( false ); + bUpdate = true; + } + } + + // Filter + eState = rAttrs.GetItemState(SDRATTR_3DOBJ_TEXTURE_FILTER); + if( eState != SfxItemState::DONTCARE ) + { + bool bValue = rAttrs.Get(SDRATTR_3DOBJ_TEXTURE_FILTER).GetValue(); + if( bValue != m_xBtnTexFilter->get_active() ) + { + m_xBtnTexFilter->set_active( bValue ); + bUpdate = true; + } + if( m_xBtnTexFilter->get_state() == TRISTATE_INDET ) + m_xBtnTexFilter->set_active( bValue ); + } + else + { + if( m_xBtnTexFilter->get_state() != TRISTATE_INDET ) + { + m_xBtnTexFilter->set_state( TRISTATE_INDET ); + bUpdate = true; + } + } + } + + + // Material Favorites + m_xLbMatFavorites->set_active( 0 ); + + // Object color + eState = rAttrs.GetItemState(XATTR_FILLCOLOR); + if( eState != SfxItemState::DONTCARE ) + { + aColor = rAttrs.Get(XATTR_FILLCOLOR).GetColorValue(); + ColorListBox* pLb = m_xLbMatColor.get(); + if( aColor != pLb->GetSelectEntryColor() ) + { + LBSelectColor( pLb, aColor ); + bUpdate = true; + } + } + else + { + if (!m_xLbMatColor->IsNoSelection()) + { + m_xLbMatColor->SetNoSelection(); + bUpdate = true; + } + } + + // Self-luminous color + eState = rAttrs.GetItemState(SDRATTR_3DOBJ_MAT_EMISSION); + if( eState != SfxItemState::DONTCARE ) + { + aColor = rAttrs.Get(SDRATTR_3DOBJ_MAT_EMISSION).GetValue(); + ColorListBox* pLb = m_xLbMatEmission.get(); + if( aColor != pLb->GetSelectEntryColor() ) + { + LBSelectColor( pLb, aColor ); + bUpdate = true; + } + } + else + { + if (!m_xLbMatEmission->IsNoSelection()) + { + m_xLbMatEmission->SetNoSelection(); + bUpdate = true; + } + } + + // Specular + eState = rAttrs.GetItemState(SDRATTR_3DOBJ_MAT_SPECULAR); + if( eState != SfxItemState::DONTCARE ) + { + aColor = rAttrs.Get(SDRATTR_3DOBJ_MAT_SPECULAR).GetValue(); + ColorListBox* pLb = m_xLbMatSpecular.get(); + if( aColor != pLb->GetSelectEntryColor() ) + { + LBSelectColor( pLb, aColor ); + bUpdate = true; + } + } + else + { + if (!m_xLbMatSpecular->IsNoSelection()) + { + m_xLbMatSpecular->SetNoSelection(); + bUpdate = true; + } + } + + // Specular Intensity + eState = rAttrs.GetItemState(SDRATTR_3DOBJ_MAT_SPECULAR_INTENSITY); + if( eState != SfxItemState::DONTCARE ) + { + sal_uInt16 nValue = rAttrs.Get(SDRATTR_3DOBJ_MAT_SPECULAR_INTENSITY).GetValue(); + if( nValue != m_xMtrMatSpecularIntensity->get_value(FieldUnit::PERCENT) ) + { + m_xMtrMatSpecularIntensity->set_value(nValue, FieldUnit::PERCENT); + bUpdate = true; + } + } + else + { + if( !m_xMtrMatSpecularIntensity->get_text().isEmpty() ) + { + m_xMtrMatSpecularIntensity->set_text(""); + bUpdate = true; + } + } + + +// Other + // Perspective + eState = rAttrs.GetItemState(SDRATTR_3DSCENE_PERSPECTIVE); + if( eState != SfxItemState::DONTCARE ) + { + ProjectionType ePT = static_cast<ProjectionType>(rAttrs.Get(SDRATTR_3DSCENE_PERSPECTIVE).GetValue()); + if( ( !m_xBtnPerspective->get_active() && ePT == ProjectionType::Perspective ) || + ( m_xBtnPerspective->get_active() && ePT == ProjectionType::Parallel ) ) + { + m_xBtnPerspective->set_active( ePT == ProjectionType::Perspective ); + bUpdate = true; + } + if( m_xBtnPerspective->get_state() == TRISTATE_INDET ) + m_xBtnPerspective->set_active( ePT == ProjectionType::Perspective ); + } + else + { + if( m_xBtnPerspective->get_state() != TRISTATE_INDET ) + { + m_xBtnPerspective->set_state( TRISTATE_INDET ); + bUpdate = true; + } + } + + if( !bUpdate ) + { + // however the 2D attributes may be different. Compare these and decide + + bUpdate = true; + } + + // Update preview + SfxItemSet aSet(rAttrs); + + // set LineStyle hard to drawing::LineStyle_NONE when it's not set so that + // the default (drawing::LineStyle_SOLID) is not used for 3d preview + if(SfxItemState::SET != aSet.GetItemState(XATTR_LINESTYLE, false)) + aSet.Put(XLineStyleItem(drawing::LineStyle_NONE)); + + // set FillColor hard to WHITE when it's SfxItemState::DONTCARE so that + // the default (Blue7) is not used for 3d preview + if(SfxItemState::DONTCARE == aSet.GetItemState(XATTR_FILLCOLOR, false)) + aSet.Put(XFillColorItem(OUString(), COL_WHITE)); + + m_xCtlPreview->Set3DAttributes(aSet); + m_xCtlLightPreview->GetSvx3DLightControl().Set3DAttributes(aSet); + + // try to select light corresponding to active button + sal_uInt32 nNumber(0xffffffff); + + if(m_xBtnLight1->get_active()) + nNumber = 0; + else if(m_xBtnLight2->get_active()) + nNumber = 1; + else if(m_xBtnLight3->get_active()) + nNumber = 2; + else if(m_xBtnLight4->get_active()) + nNumber = 3; + else if(m_xBtnLight5->get_active()) + nNumber = 4; + else if(m_xBtnLight6->get_active()) + nNumber = 5; + else if(m_xBtnLight7->get_active()) + nNumber = 6; + else if(m_xBtnLight8->get_active()) + nNumber = 7; + + if(nNumber != 0xffffffff) + { + m_xCtlLightPreview->GetSvx3DLightControl().SelectLight(nNumber); + } + + // handle state of converts possible + m_xBtnConvertTo3D->set_sensitive(pConvertTo3DItem->GetState()); + m_xBtnLatheObject->set_sensitive(pConvertTo3DLatheItem->GetState()); +} + + +void Svx3DWin::GetAttr( SfxItemSet& rAttrs ) +{ + // get remembered 2d attributes from the dialog + if(mpRemember2DAttributes) + { + SfxWhichIter aIter(*mpRemember2DAttributes); + sal_uInt16 nWhich(aIter.FirstWhich()); + + while(nWhich) + { + SfxItemState eState = mpRemember2DAttributes->GetItemState(nWhich, false); + if(SfxItemState::DONTCARE == eState) + rAttrs.InvalidateItem(nWhich); + else if(SfxItemState::SET == eState) + rAttrs.Put(mpRemember2DAttributes->Get(nWhich, false)); + + nWhich = aIter.NextWhich(); + } + } + +//Others must stand as the front on all sides + // Perspective + if( m_xBtnPerspective->get_state() != TRISTATE_INDET ) + { + ProjectionType nValue; + if( m_xBtnPerspective->get_active() ) + nValue = ProjectionType::Perspective; + else + nValue = ProjectionType::Parallel; + rAttrs.Put(Svx3DPerspectiveItem(nValue)); + } + else + rAttrs.InvalidateItem(SDRATTR_3DSCENE_PERSPECTIVE); + +// Geometry + // Possible determine PoolUnit (in this case this has not happened in Update() ) + if( !mpImpl->pPool ) + { + OSL_FAIL( "No Pool in GetAttr()! May be incompatible to drviewsi.cxx ?" ); + mpImpl->pPool = rAttrs.GetPool(); + DBG_ASSERT( mpImpl->pPool, "Where is the Pool?" ); + ePoolUnit = mpImpl->pPool->GetMetric( SID_ATTR_LINE_WIDTH ); + + eFUnit = GetModuleFieldUnit( rAttrs ); + } + + // Number of segments (horizontal) + if( !m_xNumHorizontal->get_text().isEmpty() ) + { + sal_uInt32 nValue = static_cast<sal_uInt32>(m_xNumHorizontal->get_value()); + rAttrs.Put(makeSvx3DHorizontalSegmentsItem(nValue)); + } + else + rAttrs.InvalidateItem(SDRATTR_3DOBJ_HORZ_SEGS); + + // Number of segments (vertical) + if( !m_xNumVertical->get_text().isEmpty() ) + { + sal_uInt32 nValue = static_cast<sal_uInt32>(m_xNumVertical->get_value()); + rAttrs.Put(makeSvx3DVerticalSegmentsItem(nValue)); + } + else + rAttrs.InvalidateItem(SDRATTR_3DOBJ_VERT_SEGS); + + // Depth + if( !m_xMtrDepth->get_text().isEmpty() ) + { + sal_uInt32 nValue = GetCoreValue(*m_xMtrDepth, ePoolUnit); + rAttrs.Put(makeSvx3DDepthItem(nValue)); + } + else + rAttrs.InvalidateItem(SDRATTR_3DOBJ_DEPTH); + + // Double-sided + TriState eState = m_xBtnDoubleSided->get_state(); + if( eState != TRISTATE_INDET ) + { + bool bValue = TRISTATE_TRUE == eState; + rAttrs.Put(makeSvx3DDoubleSidedItem(bValue)); + } + else + rAttrs.InvalidateItem(SDRATTR_3DOBJ_DOUBLE_SIDED); + + // Edge rounding + if( !m_xMtrPercentDiagonal->get_text().isEmpty() ) + { + sal_uInt16 nValue = static_cast<sal_uInt16>(m_xMtrPercentDiagonal->get_value(FieldUnit::PERCENT)); + rAttrs.Put(makeSvx3DPercentDiagonalItem(nValue)); + } + else + rAttrs.InvalidateItem(SDRATTR_3DOBJ_PERCENT_DIAGONAL); + + // Depth scale + if( !m_xMtrBackscale->get_text().isEmpty() ) + { + sal_uInt16 nValue = static_cast<sal_uInt16>(m_xMtrBackscale->get_value(FieldUnit::PERCENT)); + rAttrs.Put(makeSvx3DBackscaleItem(nValue)); + } + else + rAttrs.InvalidateItem(SDRATTR_3DOBJ_BACKSCALE); + + // End angle + if( !m_xMtrEndAngle->get_text().isEmpty() ) + { + sal_uInt16 nValue = static_cast<sal_uInt16>(m_xMtrEndAngle->get_value(FieldUnit::DEGREE)); + rAttrs.Put(makeSvx3DEndAngleItem(nValue)); + } + else + rAttrs.InvalidateItem(SDRATTR_3DOBJ_END_ANGLE); + + // Normal type + sal_uInt16 nValue = 99; + if( m_xBtnNormalsObj->get_active() ) + nValue = 0; + else if( m_xBtnNormalsFlat->get_active() ) + nValue = 1; + else if( m_xBtnNormalsSphere->get_active() ) + nValue = 2; + + if( nValue <= 2 ) + rAttrs.Put(Svx3DNormalsKindItem(nValue)); + else + rAttrs.InvalidateItem(SDRATTR_3DOBJ_NORMALS_KIND); + + // Normal inverted + eState = m_xBtnNormalsInvert->get_state(); + if( eState != TRISTATE_INDET ) + { + bool bValue = TRISTATE_TRUE == eState; + rAttrs.Put(makeSvx3DNormalsInvertItem(bValue)); + } + else + rAttrs.InvalidateItem(SDRATTR_3DOBJ_NORMALS_INVERT); + + // 2-sided lighting + eState = m_xBtnTwoSidedLighting->get_state(); + if( eState != TRISTATE_INDET ) + { + bool bValue = TRISTATE_TRUE == eState; + rAttrs.Put(makeSvx3DTwoSidedLightingItem(bValue)); + } + else + rAttrs.InvalidateItem(SDRATTR_3DSCENE_TWO_SIDED_LIGHTING); + +// Representation + // Shade mode + if( m_xLbShademode->get_active() != -1 ) + { + nValue = m_xLbShademode->get_active(); + rAttrs.Put(Svx3DShadeModeItem(nValue)); + } + else + rAttrs.InvalidateItem(SDRATTR_3DSCENE_SHADE_MODE); + + // 3D-Shadow + eState = m_xBtnShadow3d->get_state(); + if( eState != TRISTATE_INDET ) + { + bool bValue = TRISTATE_TRUE == eState; + rAttrs.Put(makeSvx3DShadow3DItem(bValue)); + rAttrs.Put(makeSdrShadowItem(bValue)); + } + else + { + rAttrs.InvalidateItem(SDRATTR_3DOBJ_SHADOW_3D); + rAttrs.InvalidateItem(SDRATTR_SHADOW); + } + + // Slant (Shadow) + if( !m_xMtrSlant->get_text().isEmpty() ) + { + sal_uInt16 nValue2 = static_cast<sal_uInt16>(m_xMtrSlant->get_value(FieldUnit::DEGREE)); + rAttrs.Put(makeSvx3DShadowSlantItem(nValue2)); + } + else + rAttrs.InvalidateItem(SDRATTR_3DSCENE_SHADOW_SLANT); + + // Distance + if( !m_xMtrDistance->get_text().isEmpty() ) + { + sal_uInt32 nValue2 = GetCoreValue(*m_xMtrDistance, ePoolUnit); + rAttrs.Put(makeSvx3DDistanceItem(nValue2)); + } + else + rAttrs.InvalidateItem(SDRATTR_3DSCENE_DISTANCE); + + // Focal length + if( !m_xMtrFocalLength->get_text().isEmpty() ) + { + sal_uInt32 nValue2 = GetCoreValue(*m_xMtrFocalLength, ePoolUnit); + rAttrs.Put(makeSvx3DFocalLengthItem(nValue2)); + } + else + rAttrs.InvalidateItem(SDRATTR_3DSCENE_FOCAL_LENGTH); + + // Lighting + Color aColor; + const SfxItemSet aLightItemSet(m_xCtlLightPreview->GetSvx3DLightControl().Get3DAttributes()); + + // Light 1 color + if (!m_xLbLight1->IsNoSelection()) + { + aColor = m_xLbLight1->GetSelectEntryColor(); + rAttrs.Put(makeSvx3DLightcolor1Item(aColor)); + } + else + rAttrs.InvalidateItem(SDRATTR_3DSCENE_LIGHTCOLOR_1); + // Light 1 (on/off) + eState = m_xBtnLight1->get_state(); + if( eState != TRISTATE_INDET ) + { + bool bValue = m_xBtnLight1->isLightOn(); + rAttrs.Put(makeSvx3DLightOnOff1Item(bValue)); + + // Light 1 (direction) + if( bValue ) + { + rAttrs.Put(aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_1)); + } + } + else + rAttrs.InvalidateItem(SDRATTR_3DSCENE_LIGHTON_1); + + + // Light 2 color + if (!m_xLbLight2->IsNoSelection()) + { + aColor = m_xLbLight2->GetSelectEntryColor(); + rAttrs.Put(makeSvx3DLightcolor2Item(aColor)); + } + else + rAttrs.InvalidateItem(SDRATTR_3DSCENE_LIGHTCOLOR_2); + // Light 2 (on/off) + eState = m_xBtnLight2->get_state(); + if( eState != TRISTATE_INDET ) + { + bool bValue = m_xBtnLight2->isLightOn(); + rAttrs.Put(makeSvx3DLightOnOff2Item(bValue)); + + // Light 2 (direction) + if( bValue ) + { + rAttrs.Put(aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_2)); + } + } + else + rAttrs.InvalidateItem(SDRATTR_3DSCENE_LIGHTON_2); + + // Light 3 color + if (!m_xLbLight3->IsNoSelection()) + { + aColor = m_xLbLight3->GetSelectEntryColor(); + rAttrs.Put(makeSvx3DLightcolor3Item(aColor)); + } + else + rAttrs.InvalidateItem(SDRATTR_3DSCENE_LIGHTCOLOR_3); + // Light 3 (on/off) + eState = m_xBtnLight3->get_state(); + if( eState != TRISTATE_INDET ) + { + bool bValue = m_xBtnLight3->isLightOn(); + rAttrs.Put(makeSvx3DLightOnOff3Item(bValue)); + + // Light 3 (direction) + if( bValue ) + { + rAttrs.Put(aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_3)); + } + } + else + rAttrs.InvalidateItem(SDRATTR_3DSCENE_LIGHTON_3); + + // Light 4 color + if (!m_xLbLight4->IsNoSelection()) + { + aColor = m_xLbLight4->GetSelectEntryColor(); + rAttrs.Put(makeSvx3DLightcolor4Item(aColor)); + } + else + rAttrs.InvalidateItem(SDRATTR_3DSCENE_LIGHTCOLOR_4); + // Light 4 (on/off) + eState = m_xBtnLight4->get_state(); + if( eState != TRISTATE_INDET ) + { + bool bValue = m_xBtnLight4->isLightOn(); + rAttrs.Put(makeSvx3DLightOnOff4Item(bValue)); + + // Light 4 (direction) + if( bValue ) + { + rAttrs.Put(aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_4)); + } + } + else + rAttrs.InvalidateItem(SDRATTR_3DSCENE_LIGHTON_4); + + // Light 5 color + if (!m_xLbLight5->IsNoSelection()) + { + aColor = m_xLbLight5->GetSelectEntryColor(); + rAttrs.Put(makeSvx3DLightcolor5Item(aColor)); + } + else + rAttrs.InvalidateItem(SDRATTR_3DSCENE_LIGHTCOLOR_5); + // Light 5 (on/off) + eState = m_xBtnLight5->get_state(); + if( eState != TRISTATE_INDET ) + { + bool bValue = m_xBtnLight5->isLightOn(); + rAttrs.Put(makeSvx3DLightOnOff5Item(bValue)); + + // Light 5 (direction) + if( bValue ) + { + rAttrs.Put(aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_5)); + } + } + else + rAttrs.InvalidateItem(SDRATTR_3DSCENE_LIGHTON_5); + + // Light 6 color + if (!m_xLbLight6->IsNoSelection()) + { + aColor = m_xLbLight6->GetSelectEntryColor(); + rAttrs.Put(makeSvx3DLightcolor6Item(aColor)); + } + else + rAttrs.InvalidateItem(SDRATTR_3DSCENE_LIGHTCOLOR_6); + // Light 6 (on/off) + eState = m_xBtnLight6->get_state(); + if( eState != TRISTATE_INDET ) + { + bool bValue = m_xBtnLight6->isLightOn(); + rAttrs.Put(makeSvx3DLightOnOff6Item(bValue)); + + // Light 6 (direction) + if( bValue ) + { + rAttrs.Put(aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_6)); + } + } + else + rAttrs.InvalidateItem(SDRATTR_3DSCENE_LIGHTON_6); + + // Light 7 color + if (!m_xLbLight7->IsNoSelection()) + { + aColor = m_xLbLight7->GetSelectEntryColor(); + rAttrs.Put(makeSvx3DLightcolor7Item(aColor)); + } + else + rAttrs.InvalidateItem(SDRATTR_3DSCENE_LIGHTCOLOR_7); + // Light 7 (on/off) + eState = m_xBtnLight7->get_state(); + if( eState != TRISTATE_INDET ) + { + bool bValue = m_xBtnLight7->isLightOn(); + rAttrs.Put(makeSvx3DLightOnOff7Item(bValue)); + + // Light 7 (direction) + if( bValue ) + { + rAttrs.Put(aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_7)); + } + } + else + rAttrs.InvalidateItem(SDRATTR_3DSCENE_LIGHTON_7); + + // Light 8 color + if (!m_xLbLight8->IsNoSelection()) + { + aColor = m_xLbLight8->GetSelectEntryColor(); + rAttrs.Put(makeSvx3DLightcolor8Item(aColor)); + } + else + rAttrs.InvalidateItem(SDRATTR_3DSCENE_LIGHTCOLOR_8); + // Light 8 (on/off) + eState = m_xBtnLight8->get_state(); + if( eState != TRISTATE_INDET ) + { + bool bValue = m_xBtnLight8->isLightOn(); + rAttrs.Put(makeSvx3DLightOnOff8Item(bValue)); + + // Light 8 (direction) + if( bValue ) + { + rAttrs.Put(aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_8)); + } + } + else + rAttrs.InvalidateItem(SDRATTR_3DSCENE_LIGHTON_8); + + // Ambient light + if (!m_xLbAmbientlight->IsNoSelection()) + { + aColor = m_xLbAmbientlight->GetSelectEntryColor(); + rAttrs.Put(makeSvx3DAmbientcolorItem(aColor)); + } + else + rAttrs.InvalidateItem(SDRATTR_3DSCENE_AMBIENTCOLOR); + +// Textures + // Art + nValue = 99; + if( m_xBtnTexLuminance->get_active() ) + nValue = 1; + else if( m_xBtnTexColor->get_active() ) + nValue = 3; + + if( nValue == 1 || nValue == 3 ) + rAttrs.Put(Svx3DTextureKindItem(nValue)); + else + rAttrs.InvalidateItem(SDRATTR_3DOBJ_TEXTURE_KIND); + + + // Mode + nValue = 99; + if( m_xBtnTexReplace->get_active() ) + nValue = 1; + else if( m_xBtnTexModulate->get_active() ) + nValue = 2; + + if( nValue == 1 || nValue == 2 ) + rAttrs.Put(Svx3DTextureModeItem(nValue)); + else + rAttrs.InvalidateItem(SDRATTR_3DOBJ_TEXTURE_MODE); + + // X projection + nValue = 99; + if( m_xBtnTexObjectX->get_active() ) + nValue = 0; + else if( m_xBtnTexParallelX->get_active() ) + nValue = 1; + else if( m_xBtnTexCircleX->get_active() ) + nValue = 2; + + if( nValue <= 2 ) + rAttrs.Put(Svx3DTextureProjectionXItem(nValue)); + else + rAttrs.InvalidateItem(SDRATTR_3DOBJ_TEXTURE_PROJ_X); + + // Y projection + nValue = 99; + if( m_xBtnTexObjectY->get_active() ) + nValue = 0; + else if( m_xBtnTexParallelY->get_active() ) + nValue = 1; + else if( m_xBtnTexCircleY->get_active() ) + nValue = 2; + + if( nValue <= 2 ) + rAttrs.Put(Svx3DTextureProjectionYItem(nValue)); + else + rAttrs.InvalidateItem(SDRATTR_3DOBJ_TEXTURE_PROJ_Y); + + + // Filter + eState = m_xBtnTexFilter->get_state(); + if( eState != TRISTATE_INDET ) + { + bool bValue = TRISTATE_TRUE == eState; + rAttrs.Put(makeSvx3DTextureFilterItem(bValue)); + } + else + rAttrs.InvalidateItem(SDRATTR_3DOBJ_TEXTURE_FILTER); + + +// Material + // Object color + if (!m_xLbMatColor->IsNoSelection()) + { + aColor = m_xLbMatColor->GetSelectEntryColor(); + rAttrs.Put( XFillColorItem( "", aColor) ); + } + else + { + rAttrs.InvalidateItem( XATTR_FILLCOLOR ); + } + + // luminous color + if (!m_xLbMatEmission->IsNoSelection()) + { + aColor = m_xLbMatEmission->GetSelectEntryColor(); + rAttrs.Put(makeSvx3DMaterialEmissionItem(aColor)); + } + else + rAttrs.InvalidateItem(SDRATTR_3DOBJ_MAT_EMISSION); + + // Specular + if (!m_xLbMatSpecular->IsNoSelection()) + { + aColor = m_xLbMatSpecular->GetSelectEntryColor(); + rAttrs.Put(makeSvx3DMaterialSpecularItem(aColor)); + } + else + rAttrs.InvalidateItem(SDRATTR_3DOBJ_MAT_SPECULAR); + + // Specular intensity + if( !m_xMtrMatSpecularIntensity->get_text().isEmpty() ) + { + sal_uInt16 nValue2 = static_cast<sal_uInt16>(m_xMtrMatSpecularIntensity->get_value(FieldUnit::PERCENT)); + rAttrs.Put(makeSvx3DMaterialSpecularIntensityItem(nValue2)); + } + else + rAttrs.InvalidateItem(SDRATTR_3DOBJ_MAT_SPECULAR_INTENSITY); +} + +void Svx3DWin::Resize() +{ + if ( !IsFloatingMode() || + !GetFloatingWindow()->IsRollUp() ) + { + Size aWinSize( GetOutputSizePixel() ); // why rSize in Resizing()? + + if( aWinSize.Height() >= GetMinOutputSizePixel().Height() && + aWinSize.Width() >= GetMinOutputSizePixel().Width() ) + { + // Hide + m_xBtnUpdate->hide(); + m_xBtnAssign->hide(); + + m_xBtnConvertTo3D->hide(); + m_xBtnLatheObject->hide(); + m_xBtnPerspective->hide(); + + m_xCtlPreview->Hide(); + m_xLightPreviewGrid->hide(); + + m_xFLGeometrie->hide(); + m_xFLRepresentation->hide(); + m_xFLLight->hide(); + m_xFLTexture->hide(); + m_xFLMaterial->hide(); + + // Show + m_xBtnUpdate->show(); + m_xBtnAssign->show(); + + m_xBtnConvertTo3D->show(); + m_xBtnLatheObject->show(); + m_xBtnPerspective->show(); + + if( m_xBtnGeo->get_active() ) + ClickViewTypeHdl(*m_xBtnGeo); + if( m_xBtnRepresentation->get_active() ) + ClickViewTypeHdl(*m_xBtnRepresentation); + if( m_xBtnLight->get_active() ) + ClickViewTypeHdl(*m_xBtnLight); + if( m_xBtnTexture->get_active() ) + ClickViewTypeHdl(*m_xBtnTexture); + if( m_xBtnMaterial->get_active() ) + ClickViewTypeHdl(*m_xBtnMaterial); + } + } + + SfxDockingWindow::Resize(); +} + +IMPL_LINK_NOARG(Svx3DWin, ClickUpdateHdl, weld::ToggleButton&, void) +{ + bUpdate = m_xBtnUpdate->get_active(); + + if( bUpdate ) + { + SfxDispatcher* pDispatcher = LocalGetDispatcher(pBindings); + if (pDispatcher != nullptr) + { + SfxBoolItem aItem( SID_3D_STATE, true ); + pDispatcher->ExecuteList(SID_3D_STATE, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, { &aItem }); + } + } + else + { + // Controls can be disabled during certain circumstances + } +} + +IMPL_LINK_NOARG(Svx3DWin, ClickAssignHdl, weld::Button&, void) +{ + SfxDispatcher* pDispatcher = LocalGetDispatcher(pBindings); + if (pDispatcher != nullptr) + { + SfxBoolItem aItem( SID_3D_ASSIGN, true ); + pDispatcher->ExecuteList(SID_3D_ASSIGN, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, { &aItem }); + } +} + +IMPL_LINK( Svx3DWin, ClickViewTypeHdl, weld::Button&, rBtn, void ) +{ + // Since the permanent updating of the preview would be too expensive + bool bUpdatePreview = m_xBtnLight->get_active(); + + m_xBtnGeo->set_active(m_xBtnGeo.get() == &rBtn); + m_xBtnRepresentation->set_active(m_xBtnRepresentation.get() == &rBtn); + m_xBtnLight->set_active(m_xBtnLight.get() == &rBtn); + m_xBtnTexture->set_active(m_xBtnTexture.get() == &rBtn); + m_xBtnMaterial->set_active(m_xBtnMaterial.get() == &rBtn); + + if( m_xBtnGeo->get_active() ) + eViewType = ViewType3D::Geo; + if( m_xBtnRepresentation->get_active() ) + eViewType = ViewType3D::Representation; + if( m_xBtnLight->get_active() ) + eViewType = ViewType3D::Light; + if( m_xBtnTexture->get_active() ) + eViewType = ViewType3D::Texture; + if( m_xBtnMaterial->get_active() ) + eViewType = ViewType3D::Material; + + // Geometry + if( eViewType == ViewType3D::Geo ) + { + m_xFLSegments->show(); + m_xFLGeometrie->show(); + m_xFLNormals->show(); + } + else + { + m_xFLSegments->hide(); + m_xFLGeometrie->hide(); + m_xFLNormals->hide(); + } + + // Representation + if( eViewType == ViewType3D::Representation ) + { + m_xFLShadow->show(); + m_xFLCamera->show(); + m_xFLRepresentation->show(); + } + else + { + m_xFLShadow->hide(); + m_xFLCamera->hide(); + m_xFLRepresentation->hide(); + } + + // Lighting + if( eViewType == ViewType3D::Light ) + { + m_xFLLight->show(); + + ColorListBox* pLb = GetCLbByButton(); + if( pLb ) + pLb->show(); + + m_xLightPreviewGrid->show(); + m_xCtlPreview->Hide(); + } + else + { + m_xFLLight->hide(); + + if( !m_xCtlPreview->IsVisible() ) + { + m_xCtlPreview->Show(); + m_xLightPreviewGrid->hide(); + } + } + + // Textures + if (eViewType == ViewType3D::Texture) + m_xFLTexture->show(); + else + m_xFLTexture->hide(); + + // Material + if( eViewType == ViewType3D::Material ) + { + m_xFLMatSpecular->show(); + m_xFLMaterial->show(); + } + else + { + m_xFLMatSpecular->hide(); + m_xFLMaterial->hide(); + } + if( bUpdatePreview && !m_xBtnLight->get_active() ) + UpdatePreview(); +} + +IMPL_LINK( Svx3DWin, ClickHdl, weld::Button&, rBtn, void ) +{ + bool bUpdatePreview = false; + sal_uInt16 nSId = 0; + + if( &rBtn == m_xBtnConvertTo3D.get() ) + { + nSId = SID_CONVERT_TO_3D; + } + else if( &rBtn == m_xBtnLatheObject.get() ) + { + nSId = SID_CONVERT_TO_3D_LATHE_FAST; + } + // Geometry + else if( &rBtn == m_xBtnNormalsObj.get() || + &rBtn == m_xBtnNormalsFlat.get() || + &rBtn == m_xBtnNormalsSphere.get() ) + { + m_xBtnNormalsObj->set_active( &rBtn == m_xBtnNormalsObj.get() ); + m_xBtnNormalsFlat->set_active( &rBtn == m_xBtnNormalsFlat.get() ); + m_xBtnNormalsSphere->set_active( &rBtn == m_xBtnNormalsSphere.get() ); + bUpdatePreview = true; + } + else if( &rBtn == m_xBtnLight1->get_widget() || + &rBtn == m_xBtnLight2->get_widget() || + &rBtn == m_xBtnLight3->get_widget() || + &rBtn == m_xBtnLight4->get_widget() || + &rBtn == m_xBtnLight5->get_widget() || + &rBtn == m_xBtnLight6->get_widget() || + &rBtn == m_xBtnLight7->get_widget() || + &rBtn == m_xBtnLight8->get_widget() ) + { + // Lighting + LightButton* pToggleBtn = GetLbByButton(&rBtn); + + ColorListBox* pLb = GetCLbByButton(pToggleBtn); + pLb->show(); + + bool bIsChecked = pToggleBtn->get_prev_active(); + + if (pToggleBtn != m_xBtnLight1.get() && m_xBtnLight1->get_active()) + { + m_xBtnLight1->set_active( false ); + m_xBtnLight1->set_prev_active(false); + m_xLbLight1->hide(); + } + if (pToggleBtn != m_xBtnLight2.get() && m_xBtnLight2->get_active()) + { + m_xBtnLight2->set_active( false ); + m_xBtnLight2->set_prev_active(false); + m_xLbLight2->hide(); + } + if( pToggleBtn != m_xBtnLight3.get() && m_xBtnLight3->get_active() ) + { + m_xBtnLight3->set_active( false ); + m_xBtnLight3->set_prev_active(false); + m_xLbLight3->hide(); + } + if( pToggleBtn != m_xBtnLight4.get() && m_xBtnLight4->get_active() ) + { + m_xBtnLight4->set_active( false ); + m_xBtnLight4->set_prev_active(false); + m_xLbLight4->hide(); + } + if( pToggleBtn != m_xBtnLight5.get() && m_xBtnLight5->get_active() ) + { + m_xBtnLight5->set_active( false ); + m_xBtnLight5->set_prev_active(false); + m_xLbLight5->hide(); + } + if( pToggleBtn != m_xBtnLight6.get() && m_xBtnLight6->get_active() ) + { + m_xBtnLight6->set_active( false ); + m_xBtnLight6->set_prev_active(false); + m_xLbLight6->hide(); + } + if( pToggleBtn != m_xBtnLight7.get() && m_xBtnLight7->get_active() ) + { + m_xBtnLight7->set_active( false ); + m_xBtnLight7->set_prev_active(false); + m_xLbLight7->hide(); + } + if( pToggleBtn != m_xBtnLight8.get() && m_xBtnLight8->get_active() ) + { + m_xBtnLight8->set_active( false ); + m_xBtnLight8->set_prev_active(false); + m_xLbLight8->hide(); + } + + //update light button + pToggleBtn->set_active(true); + pToggleBtn->set_prev_active(true); + if (bIsChecked) + pToggleBtn->switchLightOn(!pToggleBtn->isLightOn()); + + bool bEnable = pToggleBtn->isLightOn(); + m_xBtnLightColor->set_sensitive( bEnable ); + pLb->set_sensitive( bEnable ); + + ClickLight(*pToggleBtn); + bUpdatePreview = true; + } + // Textures + else if( &rBtn == m_xBtnTexLuminance.get() || + &rBtn == m_xBtnTexColor.get() ) + { + m_xBtnTexLuminance->set_active( &rBtn == m_xBtnTexLuminance.get() ); + m_xBtnTexColor->set_active( &rBtn == m_xBtnTexColor.get() ); + bUpdatePreview = true; + } + else if( &rBtn == m_xBtnTexReplace.get() || + &rBtn == m_xBtnTexModulate.get() ) + { + m_xBtnTexReplace->set_active( &rBtn == m_xBtnTexReplace.get() ); + m_xBtnTexModulate->set_active( &rBtn == m_xBtnTexModulate.get() ); + bUpdatePreview = true; + } + else if( &rBtn == m_xBtnTexParallelX.get() || + &rBtn == m_xBtnTexCircleX.get() || + &rBtn == m_xBtnTexObjectX.get() ) + { + m_xBtnTexParallelX->set_active( &rBtn == m_xBtnTexParallelX.get() ); + m_xBtnTexCircleX->set_active( &rBtn == m_xBtnTexCircleX.get() ); + m_xBtnTexObjectX->set_active( &rBtn == m_xBtnTexObjectX.get() ); + bUpdatePreview = true; + } + else if( &rBtn == m_xBtnTexParallelY.get() || + &rBtn == m_xBtnTexCircleY.get() || + &rBtn == m_xBtnTexObjectY.get() ) + { + m_xBtnTexParallelY->set_active( &rBtn == m_xBtnTexParallelY.get() ); + m_xBtnTexCircleY->set_active( &rBtn == m_xBtnTexCircleY.get() ); + m_xBtnTexObjectY->set_active( &rBtn == m_xBtnTexObjectY.get() ); + bUpdatePreview = true; + } + else if (&rBtn == m_xBtnShadow3d.get()) + { + m_xFtSlant->set_sensitive( m_xBtnShadow3d->get_active() ); + m_xMtrSlant->set_sensitive( m_xBtnShadow3d->get_active() ); + bUpdatePreview = true; + } + // Other (no groups) + else + { + bUpdatePreview = true; + } + + if( nSId > 0 ) + { + SfxDispatcher* pDispatcher = LocalGetDispatcher(pBindings); + if (pDispatcher != nullptr) + { + SfxBoolItem aItem( nSId, true ); + pDispatcher->ExecuteList(nSId, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, { &aItem }); + } + } + else if( bUpdatePreview ) + UpdatePreview(); +} + +IMPL_LINK( Svx3DWin, ClickColorHdl, weld::Button&, rBtn, void) +{ + SvColorDialog aColorDlg; + ColorListBox* pLb; + + if( &rBtn == m_xBtnLightColor.get() ) + pLb = GetCLbByButton(); + else if( &rBtn == m_xBtnAmbientColor.get() ) + pLb = m_xLbAmbientlight.get(); + else if( &rBtn == m_xBtnMatColor.get() ) + pLb = m_xLbMatColor.get(); + else if( &rBtn == m_xBtnEmissionColor.get() ) + pLb = m_xLbMatEmission.get(); + else // if( &rBtn == m_xBtnSpecularColor.get() ) + pLb = m_xLbMatSpecular.get(); + + Color aColor = pLb->GetSelectEntryColor(); + + aColorDlg.SetColor( aColor ); + if( aColorDlg.Execute(GetFrameWeld()) == RET_OK ) + { + aColor = aColorDlg.GetColor(); + LBSelectColor(pLb, aColor); + SelectColorHdl(*pLb); + } +} + +IMPL_LINK( Svx3DWin, SelectHdl, weld::ComboBox&, rListBox, void ) +{ + bool bUpdatePreview = false; + + // Material + if (&rListBox == m_xLbMatFavorites.get()) + { + Color aColObj( COL_WHITE ); + Color aColEmis( COL_BLACK ); + Color aColSpec( COL_WHITE ); + sal_uInt16 nSpecIntens = 20; + + switch( m_xLbMatFavorites->get_active() ) + { + case 1: // Metall + { + aColObj = Color(230,230,255); + aColEmis = Color(10,10,30); + aColSpec = Color(200,200,200); + nSpecIntens = 20; + } + break; + + case 2: // Gold + { + aColObj = Color(230,255,0); + aColEmis = Color(51,0,0); + aColSpec = Color(255,255,240); + nSpecIntens = 20; + } + break; + + case 3: // Chrome + { + aColObj = Color(36,117,153); + aColEmis = Color(18,30,51); + aColSpec = Color(230,230,255); + nSpecIntens = 2; + } + break; + + case 4: // Plastic + { + aColObj = Color(255,48,57); + aColEmis = Color(35,0,0); + aColSpec = Color(179,202,204); + nSpecIntens = 60; + } + break; + + case 5: // Wood + { + aColObj = Color(153,71,1); + aColEmis = Color(21,22,0); + aColSpec = Color(255,255,153); + nSpecIntens = 75; + } + break; + } + LBSelectColor( m_xLbMatColor.get(), aColObj ); + LBSelectColor( m_xLbMatEmission.get(), aColEmis ); + LBSelectColor( m_xLbMatSpecular.get(), aColSpec ); + m_xMtrMatSpecularIntensity->set_value(nSpecIntens, FieldUnit::PERCENT); + + bUpdatePreview = true; + } + else if (&rListBox == m_xLbShademode.get()) + bUpdatePreview = true; + + if( bUpdatePreview ) + UpdatePreview(); +} + +IMPL_LINK( Svx3DWin, SelectColorHdl, ColorListBox&, rListBox, void ) +{ + bool bUpdatePreview = false; + + if( &rListBox == m_xLbMatColor.get() || + &rListBox == m_xLbMatEmission.get() || + &rListBox == m_xLbMatSpecular.get() ) + { + m_xLbMatFavorites->set_active( 0 ); + bUpdatePreview = true; + } + // Lighting + else if( &rListBox == m_xLbAmbientlight.get() ) + { + bUpdatePreview = true; + } + else if( &rListBox == m_xLbLight1.get() || + &rListBox == m_xLbLight2.get() || + &rListBox == m_xLbLight3.get() || + &rListBox == m_xLbLight4.get() || + &rListBox == m_xLbLight5.get() || + &rListBox == m_xLbLight6.get() || + &rListBox == m_xLbLight7.get() || + &rListBox == m_xLbLight8.get() ) + { + bUpdatePreview = true; + } + + if( bUpdatePreview ) + UpdatePreview(); +} + +IMPL_LINK_NOARG( Svx3DWin, ModifyMetricHdl, weld::MetricSpinButton&, void ) +{ + UpdatePreview(); +} + +IMPL_LINK_NOARG( Svx3DWin, ModifySpinHdl, weld::SpinButton&, void ) +{ + UpdatePreview(); +} + +void Svx3DWin::ClickLight(const LightButton& rBtn) +{ + sal_uInt16 nLightSource = GetLightSource( &rBtn ); + ColorListBox* pLb = GetCLbByButton( &rBtn ); + Color aColor( pLb->GetSelectEntryColor() ); + SfxItemSet aLightItemSet(m_xCtlLightPreview->GetSvx3DLightControl().Get3DAttributes()); + const bool bOnOff(rBtn.isLightOn()); + + switch(nLightSource) + { + case 0: aLightItemSet.Put(makeSvx3DLightcolor1Item(aColor)); aLightItemSet.Put(makeSvx3DLightOnOff1Item(bOnOff)); break; + case 1: aLightItemSet.Put(makeSvx3DLightcolor2Item(aColor)); aLightItemSet.Put(makeSvx3DLightOnOff2Item(bOnOff)); break; + case 2: aLightItemSet.Put(makeSvx3DLightcolor3Item(aColor)); aLightItemSet.Put(makeSvx3DLightOnOff3Item(bOnOff)); break; + case 3: aLightItemSet.Put(makeSvx3DLightcolor4Item(aColor)); aLightItemSet.Put(makeSvx3DLightOnOff4Item(bOnOff)); break; + case 4: aLightItemSet.Put(makeSvx3DLightcolor5Item(aColor)); aLightItemSet.Put(makeSvx3DLightOnOff5Item(bOnOff)); break; + case 5: aLightItemSet.Put(makeSvx3DLightcolor6Item(aColor)); aLightItemSet.Put(makeSvx3DLightOnOff6Item(bOnOff)); break; + case 6: aLightItemSet.Put(makeSvx3DLightcolor7Item(aColor)); aLightItemSet.Put(makeSvx3DLightOnOff7Item(bOnOff)); break; + default: + case 7: aLightItemSet.Put(makeSvx3DLightcolor8Item(aColor)); aLightItemSet.Put(makeSvx3DLightOnOff8Item(bOnOff)); break; + } + + m_xCtlLightPreview->GetSvx3DLightControl().Set3DAttributes(aLightItemSet); + m_xCtlLightPreview->GetSvx3DLightControl().SelectLight(nLightSource); + m_xCtlLightPreview->CheckSelection(); +} + +IMPL_LINK_NOARG(Svx3DWin, ChangeSelectionCallbackHdl, SvxLightCtl3D*, void) +{ + const sal_uInt32 nLight(m_xCtlLightPreview->GetSvx3DLightControl().GetSelectedLight()); + weld::Button* pBtn = nullptr; + + switch( nLight ) + { + case 0: pBtn = m_xBtnLight1->get_widget(); break; + case 1: pBtn = m_xBtnLight2->get_widget(); break; + case 2: pBtn = m_xBtnLight3->get_widget(); break; + case 3: pBtn = m_xBtnLight4->get_widget(); break; + case 4: pBtn = m_xBtnLight5->get_widget(); break; + case 5: pBtn = m_xBtnLight6->get_widget(); break; + case 6: pBtn = m_xBtnLight7->get_widget(); break; + case 7: pBtn = m_xBtnLight8->get_widget(); break; + default: break; + } + + if (pBtn) + ClickHdl(*pBtn); + else + { + // Status: No lamp selected + if( m_xBtnLight1->get_active() ) + { + m_xBtnLight1->set_active( false ); + m_xLbLight1->set_sensitive( false ); + } + else if( m_xBtnLight2->get_active() ) + { + m_xBtnLight2->set_active( false ); + m_xLbLight2->set_sensitive( false ); + } + else if( m_xBtnLight3->get_active() ) + { + m_xBtnLight3->set_active( false ); + m_xLbLight3->set_sensitive( false ); + } + else if( m_xBtnLight4->get_active() ) + { + m_xBtnLight4->set_active( false ); + m_xLbLight4->set_sensitive( false ); + } + else if( m_xBtnLight5->get_active() ) + { + m_xBtnLight5->set_active( false ); + m_xLbLight5->set_sensitive( false ); + } + else if( m_xBtnLight6->get_active() ) + { + m_xBtnLight6->set_active( false ); + m_xLbLight6->set_sensitive( false ); + } + else if( m_xBtnLight7->get_active() ) + { + m_xBtnLight7->set_active( false ); + m_xLbLight7->set_sensitive( false ); + } + else if( m_xBtnLight8->get_active() ) + { + m_xBtnLight8->set_active( false ); + m_xLbLight8->set_sensitive( false ); + } + m_xBtnLightColor->set_sensitive( false ); + } +} + +namespace +{ + OUString lcl_makeColorName(const Color& rColor) + { + OUString aStr = SvxResId(RID_SVXFLOAT3D_FIX_R) + + OUString::number(rColor.GetRed()) + + " " + + SvxResId(RID_SVXFLOAT3D_FIX_G) + + OUString::number(rColor.GetGreen()) + + " " + + SvxResId(RID_SVXFLOAT3D_FIX_B) + + OUString::number(rColor.GetBlue()); + return aStr; + } +} + +// Method to ensure that the LB is also associated with a color +void Svx3DWin::LBSelectColor( ColorListBox* pLb, const Color& rColor ) +{ + pLb->SetNoSelection(); + pLb->SelectEntry(std::make_pair(rColor, lcl_makeColorName(rColor))); +} + +void Svx3DWin::UpdatePreview() +{ + if(!pModel) + { + pModel.reset(new FmFormModel()); + } + + // Get Itemset + SfxItemSet aSet( pModel->GetItemPool(), svl::Items<SDRATTR_START, SDRATTR_END>{}); + + // Get Attributes and set the preview + GetAttr( aSet ); + m_xCtlPreview->Set3DAttributes( aSet ); + m_xCtlLightPreview->GetSvx3DLightControl().Set3DAttributes( aSet ); +} + + +// document is to be reloaded, destroy remembered ItemSet +void Svx3DWin::DocumentReload() +{ + mpRemember2DAttributes.reset(); +} + +void Svx3DWin::InitColorLB() +{ + // First... + Color aColWhite( COL_WHITE ); + Color aColBlack( COL_BLACK ); + m_xLbLight1->SelectEntry( aColWhite ); + m_xLbLight2->SelectEntry( aColWhite ); + m_xLbLight3->SelectEntry( aColWhite ); + m_xLbLight4->SelectEntry( aColWhite ); + m_xLbLight5->SelectEntry( aColWhite ); + m_xLbLight6->SelectEntry( aColWhite ); + m_xLbLight7->SelectEntry( aColWhite ); + m_xLbLight8->SelectEntry( aColWhite ); + m_xLbAmbientlight->SelectEntry( aColBlack ); + m_xLbMatColor->SelectEntry( aColWhite ); + m_xLbMatEmission->SelectEntry( aColBlack ); + m_xLbMatSpecular->SelectEntry( aColWhite ); +} + +sal_uInt16 Svx3DWin::GetLightSource( const LightButton* pBtn ) +{ + sal_uInt16 nLight = 8; + + if (pBtn == m_xBtnLight1.get()) + nLight = 0; + else if (pBtn == m_xBtnLight2.get()) + nLight = 1; + else if( pBtn == m_xBtnLight3.get() ) + nLight = 2; + else if( pBtn == m_xBtnLight4.get() ) + nLight = 3; + else if( pBtn == m_xBtnLight5.get() ) + nLight = 4; + else if( pBtn == m_xBtnLight6.get() ) + nLight = 5; + else if( pBtn == m_xBtnLight7.get() ) + nLight = 6; + else if( pBtn == m_xBtnLight8.get() ) + nLight = 7; + + return nLight; +}; + +ColorListBox* Svx3DWin::GetCLbByButton( const LightButton* pBtn ) +{ + ColorListBox* pLb = nullptr; + + if( pBtn == nullptr ) + { + if( m_xBtnLight1->get_active() ) + pLb = m_xLbLight1.get(); + else if( m_xBtnLight2->get_active() ) + pLb = m_xLbLight2.get(); + else if( m_xBtnLight3->get_active() ) + pLb = m_xLbLight3.get(); + else if( m_xBtnLight4->get_active() ) + pLb = m_xLbLight4.get(); + else if( m_xBtnLight5->get_active() ) + pLb = m_xLbLight5.get(); + else if( m_xBtnLight6->get_active() ) + pLb = m_xLbLight6.get(); + else if( m_xBtnLight7->get_active() ) + pLb = m_xLbLight7.get(); + else if( m_xBtnLight8->get_active() ) + pLb = m_xLbLight8.get(); + } + else + { + if( pBtn == m_xBtnLight1.get() ) + pLb = m_xLbLight1.get(); + else if (pBtn == m_xBtnLight2.get()) + pLb = m_xLbLight2.get(); + else if( pBtn == m_xBtnLight3.get() ) + pLb = m_xLbLight3.get(); + else if( pBtn == m_xBtnLight4.get() ) + pLb = m_xLbLight4.get(); + else if( pBtn == m_xBtnLight5.get() ) + pLb = m_xLbLight5.get(); + else if( pBtn == m_xBtnLight6.get() ) + pLb = m_xLbLight6.get(); + else if( pBtn == m_xBtnLight7.get() ) + pLb = m_xLbLight7.get(); + else if( pBtn == m_xBtnLight8.get() ) + pLb = m_xLbLight8.get(); + } + return pLb; +}; + +LightButton* Svx3DWin::GetLbByButton( const weld::Button* pBtn ) +{ + LightButton* pLb = nullptr; + + if( pBtn == m_xBtnLight1->get_widget() ) + pLb = m_xBtnLight1.get(); + else if (pBtn == m_xBtnLight2->get_widget() ) + pLb = m_xBtnLight2.get(); + else if( pBtn == m_xBtnLight3->get_widget() ) + pLb = m_xBtnLight3.get(); + else if( pBtn == m_xBtnLight4->get_widget() ) + pLb = m_xBtnLight4.get(); + else if( pBtn == m_xBtnLight5->get_widget() ) + pLb = m_xBtnLight5.get(); + else if( pBtn == m_xBtnLight6->get_widget() ) + pLb = m_xBtnLight6.get(); + else if( pBtn == m_xBtnLight7->get_widget() ) + pLb = m_xBtnLight7.get(); + else if( pBtn == m_xBtnLight8->get_widget() ) + pLb = m_xBtnLight8.get(); + + return pLb; +}; + +// Derivation from SfxChildWindow as "containers" for effects +Svx3DChildWindow::Svx3DChildWindow( vcl::Window* _pParent, + sal_uInt16 nId, + SfxBindings* pBindings, + SfxChildWinInfo* pInfo ) : + SfxChildWindow( _pParent, nId ) +{ + VclPtr<Svx3DWin> pWin = VclPtr<Svx3DWin>::Create( pBindings, this, _pParent ); + SetWindow(pWin); + + pWin->Initialize( pInfo ); +} + +Svx3DCtrlItem::Svx3DCtrlItem( sal_uInt16 _nId, + SfxBindings* _pBindings) : + SfxControllerItem( _nId, *_pBindings ) +{ +} + + +void Svx3DCtrlItem::StateChanged( sal_uInt16 /*nSId*/, + SfxItemState /*eState*/, const SfxPoolItem* /*pItem*/ ) +{ +} + +// ControllerItem for Status Slot SID_CONVERT_TO_3D + +SvxConvertTo3DItem::SvxConvertTo3DItem(sal_uInt16 _nId, SfxBindings* _pBindings) +: SfxControllerItem(_nId, *_pBindings), + bState(false) +{ +} + +void SvxConvertTo3DItem::StateChanged(sal_uInt16 /*_nId*/, SfxItemState eState, const SfxPoolItem* /*pState*/) +{ + bool bNewState = (eState != SfxItemState::DISABLED); + if(bNewState != bState) + { + bState = bNewState; + SfxDispatcher* pDispatcher = LocalGetDispatcher(&GetBindings()); + if (pDispatcher != nullptr) + { + SfxBoolItem aItem( SID_3D_STATE, true ); + pDispatcher->ExecuteList(SID_3D_STATE, + SfxCallMode::ASYNCHRON|SfxCallMode::RECORD, { &aItem }); + } + } +} + +LightButton::LightButton(std::unique_ptr<weld::ToggleButton> xButton) + : m_xButton(std::move(xButton)) + , m_bLightOn(false) + , m_bButtonPrevActive(false) +{ + m_xButton->set_from_icon_name(RID_SVXBMP_LAMP_OFF); +} + +void LightButton::switchLightOn(bool bOn) +{ + if (m_bLightOn == bOn) + return; + m_bLightOn = bOn; + if (m_bLightOn) + m_xButton->set_from_icon_name(RID_SVXBMP_LAMP_ON); + else + m_xButton->set_from_icon_name(RID_SVXBMP_LAMP_OFF); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/engine3d/helperhittest3d.cxx b/svx/source/engine3d/helperhittest3d.cxx new file mode 100644 index 000000000..f553b4d0e --- /dev/null +++ b/svx/source/engine3d/helperhittest3d.cxx @@ -0,0 +1,277 @@ +/* -*- 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/helperhittest3d.hxx> +#include <basegfx/point/b2dpoint.hxx> +#include <svx/svdpage.hxx> +#include <svx/scene3d.hxx> +#include <svx/svditer.hxx> +#include <drawinglayer/processor3d/cutfindprocessor3d.hxx> +#include <sdr/contact/viewcontactofe3d.hxx> +#include <svx/sdr/contact/viewcontactofe3dscene.hxx> +#include <com/sun/star/uno/Sequence.h> + + +using namespace com::sun::star; + +namespace { + +class ImplPairDephAndObject +{ +private: + const E3dCompoundObject* mpObject; + double mfDepth; + +public: + ImplPairDephAndObject(const E3dCompoundObject* pObject, double fDepth) + : mpObject(pObject), + mfDepth(fDepth) + {} + + // for ::std::sort + bool operator<(const ImplPairDephAndObject& rComp) const + { + return (mfDepth < rComp.mfDepth); + } + + // data read access + const E3dCompoundObject* getObject() const { return mpObject; } +}; + +} + +static void getAllHit3DObjectWithRelativePoint( + const basegfx::B3DPoint& rFront, + const basegfx::B3DPoint& rBack, + const E3dCompoundObject& rObject, + const drawinglayer::geometry::ViewInformation3D& rObjectViewInformation3D, + ::std::vector< basegfx::B3DPoint >& o_rResult, + bool bAnyHit) +{ + o_rResult.clear(); + + if(!rFront.equal(rBack)) + { + // rObject is an E3dCompoundObject, so it cannot be a scene (which is an E3dObject) + const sdr::contact::ViewContactOfE3d& rVCObject = static_cast< sdr::contact::ViewContactOfE3d& >(rObject.GetViewContact()); + const drawinglayer::primitive3d::Primitive3DContainer aPrimitives(rVCObject.getViewIndependentPrimitive3DContainer()); + + if(!aPrimitives.empty()) + { + // make BoundVolume empty and overlapping test for speedup + const basegfx::B3DRange aObjectRange(aPrimitives.getB3DRange(rObjectViewInformation3D)); + + if(!aObjectRange.isEmpty()) + { + const basegfx::B3DRange aFrontBackRange(rFront, rBack); + + if(aObjectRange.overlaps(aFrontBackRange)) + { + // bound volumes hit, geometric cut tests needed + drawinglayer::processor3d::CutFindProcessor aCutFindProcessor(rObjectViewInformation3D, rFront, rBack, bAnyHit); + aCutFindProcessor.process(aPrimitives); + o_rResult = aCutFindProcessor.getCutPoints(); + } + } + } + } +} + + +E3dScene* fillViewInformation3DForCompoundObject(drawinglayer::geometry::ViewInformation3D& o_rViewInformation3D, const E3dCompoundObject& rCandidate) +{ + // Search for root scene (outmost scene) of the 3d object since e.g. in chart, multiple scenes may + // be placed between object and outmost scene. On that search, remember the in-between scene's + // transformation for the correct complete ObjectTransformation. For historical reasons, the + // root scene's own object transformation is part of the scene's ViewTransformation, o do not + // add it. For more details, see ViewContactOfE3dScene::createViewInformation3D. + E3dScene* pParentScene(rCandidate.getParentE3dSceneFromE3dObject()); + E3dScene* pRootScene(nullptr); + basegfx::B3DHomMatrix aInBetweenSceneMatrix; + + while(pParentScene) + { + E3dScene* pParentParentScene(pParentScene->getParentE3dSceneFromE3dObject()); + + if(pParentParentScene) + { + // pParentScene is an in-between scene + aInBetweenSceneMatrix = pParentScene->GetTransform() * aInBetweenSceneMatrix; + } + else + { + // pParentScene is the root scene + pRootScene = pParentScene; + } + + pParentScene = pParentParentScene; + } + + if(pRootScene) + { + const sdr::contact::ViewContactOfE3dScene& rVCScene = static_cast< sdr::contact::ViewContactOfE3dScene& >(pRootScene->GetViewContact()); + + if(aInBetweenSceneMatrix.isIdentity()) + { + o_rViewInformation3D = rVCScene.getViewInformation3D(); + } + else + { + // build new ViewInformation containing all transforms for the candidate + const drawinglayer::geometry::ViewInformation3D& aViewInfo3D(rVCScene.getViewInformation3D()); + + o_rViewInformation3D = drawinglayer::geometry::ViewInformation3D( + aViewInfo3D.getObjectTransformation() * aInBetweenSceneMatrix, + aViewInfo3D.getOrientation(), + aViewInfo3D.getProjection(), + aViewInfo3D.getDeviceToView(), + aViewInfo3D.getViewTime(), + aViewInfo3D.getExtendedInformationSequence()); + } + } + else + { + const uno::Sequence< beans::PropertyValue > aEmptyParameters; + o_rViewInformation3D = drawinglayer::geometry::ViewInformation3D(aEmptyParameters); + } + + return pRootScene; +} + + +void getAllHit3DObjectsSortedFrontToBack( + const basegfx::B2DPoint& rPoint, + const E3dScene& rScene, + ::std::vector< const E3dCompoundObject* >& o_rResult) +{ + o_rResult.clear(); + SdrObjList* pList = rScene.GetSubList(); + + if(nullptr != pList && 0 != pList->GetObjCount()) + { + // prepare relative HitPoint. To do so, get the VC of the 3DScene and from there + // the Scene's 2D transformation. Multiplying with the inverse transformation + // will create a point relative to the 3D scene as unit-2d-object + const sdr::contact::ViewContactOfE3dScene& rVCScene = static_cast< sdr::contact::ViewContactOfE3dScene& >(rScene.GetViewContact()); + basegfx::B2DHomMatrix aInverseSceneTransform(rVCScene.getObjectTransformation()); + aInverseSceneTransform.invert(); + const basegfx::B2DPoint aRelativePoint(aInverseSceneTransform * rPoint); + + // check if test point is inside scene's area at all + if(aRelativePoint.getX() >= 0.0 && aRelativePoint.getX() <= 1.0 && aRelativePoint.getY() >= 0.0 && aRelativePoint.getY() <= 1.0) + { + SdrObjListIter aIterator(pList, SdrIterMode::DeepNoGroups); + ::std::vector< ImplPairDephAndObject > aDepthAndObjectResults; + const uno::Sequence< beans::PropertyValue > aEmptyParameters; + drawinglayer::geometry::ViewInformation3D aViewInfo3D(aEmptyParameters); + + while(aIterator.IsMore()) + { + const E3dCompoundObject* pCandidate = dynamic_cast< const E3dCompoundObject* >(aIterator.Next()); + + if(pCandidate) + { + fillViewInformation3DForCompoundObject(aViewInfo3D, *pCandidate); + + // create HitPoint Front and Back, transform to object coordinates + basegfx::B3DHomMatrix aViewToObject(aViewInfo3D.getObjectToView()); + aViewToObject.invert(); + const basegfx::B3DPoint aFront(aViewToObject * basegfx::B3DPoint(aRelativePoint.getX(), aRelativePoint.getY(), 0.0)); + const basegfx::B3DPoint aBack(aViewToObject * basegfx::B3DPoint(aRelativePoint.getX(), aRelativePoint.getY(), 1.0)); + + if(!aFront.equal(aBack)) + { + // get all hit points with object + ::std::vector< basegfx::B3DPoint > aHitsWithObject; + getAllHit3DObjectWithRelativePoint(aFront, aBack, *pCandidate, aViewInfo3D, aHitsWithObject, false); + + for(const basegfx::B3DPoint & a : aHitsWithObject) + { + const basegfx::B3DPoint aPointInViewCoordinates(aViewInfo3D.getObjectToView() * a); + aDepthAndObjectResults.emplace_back(pCandidate, aPointInViewCoordinates.getZ()); + } + } + } + } + + // fill nRetval + const sal_uInt32 nCount(aDepthAndObjectResults.size()); + + if(nCount) + { + // sort aDepthAndObjectResults by depth + ::std::sort(aDepthAndObjectResults.begin(), aDepthAndObjectResults.end()); + + // copy SdrObject pointers to return result set + for(const auto& rResult : aDepthAndObjectResults) + { + o_rResult.push_back(rResult.getObject()); + } + } + } + } +} + + +bool checkHitSingle3DObject( + const basegfx::B2DPoint& rPoint, + const E3dCompoundObject& rCandidate) +{ + const uno::Sequence< beans::PropertyValue > aEmptyParameters; + drawinglayer::geometry::ViewInformation3D aViewInfo3D(aEmptyParameters); + E3dScene* pRootScene = fillViewInformation3DForCompoundObject(aViewInfo3D, rCandidate); + + if(pRootScene) + { + // prepare relative HitPoint. To do so, get the VC of the 3DScene and from there + // the Scene's 2D transformation. Multiplying with the inverse transformation + // will create a point relative to the 3D scene as unit-2d-object + const sdr::contact::ViewContactOfE3dScene& rVCScene = static_cast< sdr::contact::ViewContactOfE3dScene& >(pRootScene->GetViewContact()); + basegfx::B2DHomMatrix aInverseSceneTransform(rVCScene.getObjectTransformation()); + aInverseSceneTransform.invert(); + const basegfx::B2DPoint aRelativePoint(aInverseSceneTransform * rPoint); + + // check if test point is inside scene's area at all + if(aRelativePoint.getX() >= 0.0 && aRelativePoint.getX() <= 1.0 && aRelativePoint.getY() >= 0.0 && aRelativePoint.getY() <= 1.0) + { + // create HitPoint Front and Back, transform to object coordinates + basegfx::B3DHomMatrix aViewToObject(aViewInfo3D.getObjectToView()); + aViewToObject.invert(); + const basegfx::B3DPoint aFront(aViewToObject * basegfx::B3DPoint(aRelativePoint.getX(), aRelativePoint.getY(), 0.0)); + const basegfx::B3DPoint aBack(aViewToObject * basegfx::B3DPoint(aRelativePoint.getX(), aRelativePoint.getY(), 1.0)); + + if(!aFront.equal(aBack)) + { + // get all hit points with object + ::std::vector< basegfx::B3DPoint > aHitsWithObject; + getAllHit3DObjectWithRelativePoint(aFront, aBack, rCandidate, aViewInfo3D, aHitsWithObject, true); + + if(!aHitsWithObject.empty()) + { + return true; + } + } + } + } + + return false; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/engine3d/helperminimaldepth3d.cxx b/svx/source/engine3d/helperminimaldepth3d.cxx new file mode 100644 index 000000000..cfcc2c7d1 --- /dev/null +++ b/svx/source/engine3d/helperminimaldepth3d.cxx @@ -0,0 +1,201 @@ +/* -*- 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 "helperminimaldepth3d.hxx" +#include <drawinglayer/processor3d/baseprocessor3d.hxx> +#include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx> +#include <drawinglayer/primitive3d/transformprimitive3d.hxx> +#include <drawinglayer/primitive3d/polygonprimitive3d.hxx> +#include <drawinglayer/primitive3d/polypolygonprimitive3d.hxx> +#include <sdr/contact/viewcontactofe3d.hxx> +#include <svx/sdr/contact/viewcontactofe3dscene.hxx> +#include <svx/obj3d.hxx> +#include <svx/scene3d.hxx> + + +namespace drawinglayer::processor3d +{ + namespace { + + class MinimalDephInViewExtractor : public BaseProcessor3D + { + private: + // the value which will be fetched as result + double mfMinimalDepth; + + // as tooling, the process() implementation takes over API handling and calls this + // virtual render method when the primitive implementation is BasePrimitive3D-based. + virtual void processBasePrimitive3D(const primitive3d::BasePrimitive3D& rCandidate) override; + + public: + explicit MinimalDephInViewExtractor(const geometry::ViewInformation3D& rViewInformation) + : BaseProcessor3D(rViewInformation), + mfMinimalDepth(DBL_MAX) + {} + + // data access + double getMinimalDepth() const { return mfMinimalDepth; } + }; + + } + + void MinimalDephInViewExtractor::processBasePrimitive3D(const primitive3d::BasePrimitive3D& rCandidate) + { + // it is a BasePrimitive3D implementation, use getPrimitive3DID() call for switch + switch(rCandidate.getPrimitive3DID()) + { + case PRIMITIVE3D_ID_TRANSFORMPRIMITIVE3D : + { + // transform group. Remember current transformations + const primitive3d::TransformPrimitive3D& rPrimitive = static_cast< const primitive3d::TransformPrimitive3D& >(rCandidate); + const geometry::ViewInformation3D aLastViewInformation3D(getViewInformation3D()); + + // create new transformation; add new object transform from right side + const geometry::ViewInformation3D aNewViewInformation3D( + aLastViewInformation3D.getObjectTransformation() * rPrimitive.getTransformation(), + aLastViewInformation3D.getOrientation(), + aLastViewInformation3D.getProjection(), + aLastViewInformation3D.getDeviceToView(), + aLastViewInformation3D.getViewTime(), + aLastViewInformation3D.getExtendedInformationSequence()); + updateViewInformation(aNewViewInformation3D); + + // let break down + process(rPrimitive.getChildren()); + + // restore transformations + updateViewInformation(aLastViewInformation3D); + break; + } + case PRIMITIVE3D_ID_POLYGONHAIRLINEPRIMITIVE3D : + { + // PolygonHairlinePrimitive3D + const primitive3d::PolygonHairlinePrimitive3D& rPrimitive = static_cast< const primitive3d::PolygonHairlinePrimitive3D& >(rCandidate); + const basegfx::B3DPolygon& rPolygon = rPrimitive.getB3DPolygon(); + const sal_uInt32 nCount(rPolygon.count()); + + for(sal_uInt32 a(0); a < nCount; a++) + { + const basegfx::B3DPoint aPointInView(getViewInformation3D().getObjectToView() * rPolygon.getB3DPoint(a)); + + if(aPointInView.getZ() < mfMinimalDepth) + { + mfMinimalDepth = aPointInView.getZ(); + } + } + + break; + } + case PRIMITIVE3D_ID_POLYPOLYGONMATERIALPRIMITIVE3D : + { + // PolyPolygonMaterialPrimitive3D + const primitive3d::PolyPolygonMaterialPrimitive3D& rPrimitive = static_cast< const primitive3d::PolyPolygonMaterialPrimitive3D& >(rCandidate); + const basegfx::B3DPolyPolygon& rPolyPolygon = rPrimitive.getB3DPolyPolygon(); + const sal_uInt32 nPolyCount(rPolyPolygon.count()); + + for(sal_uInt32 a(0); a < nPolyCount; a++) + { + const basegfx::B3DPolygon& aPolygon(rPolyPolygon.getB3DPolygon(a)); + const sal_uInt32 nCount(aPolygon.count()); + + for(sal_uInt32 b(0); b < nCount; b++) + { + const basegfx::B3DPoint aPointInView(getViewInformation3D().getObjectToView() * aPolygon.getB3DPoint(b)); + + if(aPointInView.getZ() < mfMinimalDepth) + { + mfMinimalDepth = aPointInView.getZ(); + } + } + } + + break; + } + default : + { + // process recursively + process(rCandidate.get3DDecomposition(getViewInformation3D())); + break; + } + } + } +} // end of namespace + + +// changed to create values using VCs, Primitive3DContainer and ViewInformation3D to allow +// removal of old 3D bucket geometry. There is one slight difference in the result, it's +// in [0.0 .. 1.0] for Z-Depth since the scaling of the scene as 2D object is no longer +// part of the 3D transformations. This could be added since the ViewContactOfE3dScene is +// given, but is not needed since the permutation of the depth values needs only be correct +// relative to each other + +double getMinimalDepthInViewCoordinates(const E3dCompoundObject& rObject) +{ + // this is an E3dCompoundObject, so it cannot be a scene (which is an E3dObject). + // Get primitive sequence using VC + const sdr::contact::ViewContactOfE3d& rVCObject = static_cast< sdr::contact::ViewContactOfE3d& >(rObject.GetViewContact()); + const drawinglayer::primitive3d::Primitive3DContainer aPrimitives = rVCObject.getViewIndependentPrimitive3DContainer(); + double fRetval(DBL_MAX); + + if(!aPrimitives.empty()) + { + const E3dScene* pScene(rObject.getRootE3dSceneFromE3dObject()); + + if(pScene) + { + // get ViewInformation3D from scene using VC + const sdr::contact::ViewContactOfE3dScene& rVCScene = static_cast< sdr::contact::ViewContactOfE3dScene& >(pScene->GetViewContact()); + const drawinglayer::geometry::ViewInformation3D& aViewInfo3D(rVCScene.getViewInformation3D()); + + // the scene's object transformation is already part of aViewInfo3D.getObjectTransformation() + // for historical reasons (see ViewContactOfE3dScene::createViewInformation3D for more info) + // and the object's transform is part of aPrimitives (and taken into account when decomposing + // to PolygonHairlinePrimitive3D and PolyPolygonMaterialPrimitive3D). The missing part may be + // some Scene SdrObjects lying in-between which may need to be added. This is e.g. used in chart, + // and generally allowed in 3d scenes and their 3d object hierarchy + basegfx::B3DHomMatrix aInBetweenSceneMatrix; + E3dScene* pParentScene(rObject.getParentE3dSceneFromE3dObject()); + + while(pParentScene && pParentScene != pScene) + { + aInBetweenSceneMatrix = pParentScene->GetTransform() * aInBetweenSceneMatrix; + pParentScene = pParentScene->getParentE3dSceneFromE3dObject(); + } + + // build new ViewInformation containing all transforms + const drawinglayer::geometry::ViewInformation3D aNewViewInformation3D( + aViewInfo3D.getObjectTransformation() * aInBetweenSceneMatrix, + aViewInfo3D.getOrientation(), + aViewInfo3D.getProjection(), + aViewInfo3D.getDeviceToView(), + aViewInfo3D.getViewTime(), + aViewInfo3D.getExtendedInformationSequence()); + + // create extractor helper, process geometry and get return value + drawinglayer::processor3d::MinimalDephInViewExtractor aExtractor(aNewViewInformation3D); + aExtractor.process(aPrimitives); + fRetval = aExtractor.getMinimalDepth(); + } + } + + return fRetval; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/engine3d/helperminimaldepth3d.hxx b/svx/source/engine3d/helperminimaldepth3d.hxx new file mode 100644 index 000000000..15da40d09 --- /dev/null +++ b/svx/source/engine3d/helperminimaldepth3d.hxx @@ -0,0 +1,48 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_SVX_SOURCE_ENGINE3D_HELPERMINIMALDEPTH3D_HXX +#define INCLUDED_SVX_SOURCE_ENGINE3D_HELPERMINIMALDEPTH3D_HXX + + +// predefines + +class E3dCompoundObject; + + +/** support extracting the minimal depth of a 3d object in its scene + + @param rObject + The 3D Object from which the minimal depth needs to be calculated. The scene + is defined by the object already + + @return + The minimal depth of this object in unified ViewCoordinates. This is the + Z-Coordinate of one object point in the range of [0.0 .. 1.0]. ViewCoordinates + means the transformations (esp. rotation) of the scene are taken into account + +*/ +// support extracting the minimal depth of a 3d object in its scene + +double getMinimalDepthInViewCoordinates(const E3dCompoundObject& rObject); + + +#endif // INCLUDED_SVX_SOURCE_ENGINE3D_HELPERMINIMALDEPTH3D_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/engine3d/lathe3d.cxx b/svx/source/engine3d/lathe3d.cxx new file mode 100644 index 000000000..37060f96e --- /dev/null +++ b/svx/source/engine3d/lathe3d.cxx @@ -0,0 +1,205 @@ +/* -*- 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/deflt3d.hxx> +#include <svx/strings.hrc> +#include <svx/dialmgr.hxx> +#include <svx/globl3d.hxx> +#include <svx/lathe3d.hxx> +#include <svx/svdopath.hxx> +#include <svx/svdmodel.hxx> +#include <svx/svx3ditems.hxx> +#include <svx/xlineit0.hxx> +#include <sdr/properties/e3dlatheproperties.hxx> +#include <sdr/contact/viewcontactofe3dlathe.hxx> +#include <basegfx/polygon/b2dpolypolygontools.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <rtl/ustrbuf.hxx> + + +// DrawContact section +std::unique_ptr<sdr::contact::ViewContact> E3dLatheObj::CreateObjectSpecificViewContact() +{ + return std::make_unique<sdr::contact::ViewContactOfE3dLathe>(*this); +} + +std::unique_ptr<sdr::properties::BaseProperties> E3dLatheObj::CreateObjectSpecificProperties() +{ + return std::make_unique<sdr::properties::E3dLatheProperties>(*this); +} + +// Constructor from 3D polygon, scale is the conversion factor for the coordinates +E3dLatheObj::E3dLatheObj( + SdrModel& rSdrModel, + const E3dDefaultAttributes& rDefault, + const basegfx::B2DPolyPolygon& rPoly2D) +: E3dCompoundObject(rSdrModel), + maPolyPoly2D(rPoly2D) +{ + // since the old class PolyPolygon3D did mirror the given PolyPolygons in Y, do the same here + basegfx::B2DHomMatrix aMirrorY; + aMirrorY.scale(1.0, -1.0); + maPolyPoly2D.transform(aMirrorY); + + // Set Defaults + SetDefaultAttributes(rDefault); + + // Superfluous items removed, in particular to prevent duplicate + // start and end points + maPolyPoly2D.removeDoublePoints(); + + if(maPolyPoly2D.count()) + { + const basegfx::B2DPolygon rPoly(maPolyPoly2D.getB2DPolygon(0)); + sal_uInt32 nSegCnt(rPoly.count()); + + if(nSegCnt && !rPoly.isClosed()) + { + nSegCnt -= 1; + } + + GetProperties().SetObjectItemDirect(makeSvx3DVerticalSegmentsItem(nSegCnt)); + } +} + +E3dLatheObj::E3dLatheObj(SdrModel& rSdrModel) +: E3dCompoundObject(rSdrModel) +{ + // Set Defaults + const E3dDefaultAttributes aDefault; + + SetDefaultAttributes(aDefault); +} + +E3dLatheObj::~E3dLatheObj() +{ +} + +void E3dLatheObj::SetDefaultAttributes(const E3dDefaultAttributes& rDefault) +{ + GetProperties().SetObjectItemDirect(Svx3DSmoothNormalsItem(rDefault.GetDefaultLatheSmoothed())); + GetProperties().SetObjectItemDirect(Svx3DSmoothLidsItem(rDefault.GetDefaultLatheSmoothFrontBack())); + GetProperties().SetObjectItemDirect(Svx3DCharacterModeItem(rDefault.GetDefaultLatheCharacterMode())); + GetProperties().SetObjectItemDirect(Svx3DCloseFrontItem(rDefault.GetDefaultLatheCloseFront())); + GetProperties().SetObjectItemDirect(Svx3DCloseBackItem(rDefault.GetDefaultLatheCloseBack())); +} + +sal_uInt16 E3dLatheObj::GetObjIdentifier() const +{ + return E3D_LATHEOBJ_ID; +} + +E3dLatheObj* E3dLatheObj::CloneSdrObject(SdrModel& rTargetModel) const +{ + return CloneHelper< E3dLatheObj >(rTargetModel); +} + +E3dLatheObj& E3dLatheObj::operator=(const E3dLatheObj& rObj) +{ + if( this == &rObj ) + return *this; + E3dCompoundObject::operator=(rObj); + + maPolyPoly2D = rObj.maPolyPoly2D; + + return *this; +} + +// Convert the object to group object consisting of n polygons + +SdrObjectUniquePtr E3dLatheObj::DoConvertToPolyObj(bool /*bBezier*/, bool /*bAddText*/) const +{ + return nullptr; +} + +// Set Local parameters set to re-create geometry + +void E3dLatheObj::SetPolyPoly2D(const basegfx::B2DPolyPolygon& rNew) +{ + if(maPolyPoly2D != rNew) + { + maPolyPoly2D = rNew; + maPolyPoly2D.removeDoublePoints(); + + if(maPolyPoly2D.count()) + { + const basegfx::B2DPolygon rPoly(maPolyPoly2D.getB2DPolygon(0)); + sal_uInt32 nSegCnt(rPoly.count()); + + if(nSegCnt && !rPoly.isClosed()) + { + nSegCnt -= 1; + } + + GetProperties().SetObjectItemDirect(makeSvx3DVerticalSegmentsItem(nSegCnt)); + } + + ActionChanged(); + } +} + +// Get the name of the object (singular) + +OUString E3dLatheObj::TakeObjNameSingul() const +{ + OUStringBuffer sName(SvxResId(STR_ObjNameSingulLathe3d)); + + OUString aName(GetName()); + if (!aName.isEmpty()) + { + sName.append(' '); + sName.append('\''); + sName.append(aName); + sName.append('\''); + } + return sName.makeStringAndClear(); +} + +// Get the name of the object (plural) + +OUString E3dLatheObj::TakeObjNamePlural() const +{ + return SvxResId(STR_ObjNamePluralLathe3d); +} + +bool E3dLatheObj::IsBreakObjPossible() +{ + return true; +} + +std::unique_ptr<SdrAttrObj,SdrObjectFreeOp> E3dLatheObj::GetBreakObj() +{ + // create PathObj + basegfx::B3DPolyPolygon aLathePoly3D(basegfx::utils::createB3DPolyPolygonFromB2DPolyPolygon(maPolyPoly2D)); + basegfx::B2DPolyPolygon aTransPoly(TransformToScreenCoor(aLathePoly3D)); + std::unique_ptr<SdrPathObj,SdrObjectFreeOp> pPathObj(new SdrPathObj(getSdrModelFromSdrObject(), OBJ_PLIN, aTransPoly)); + + // Set Attribute + SfxItemSet aSet(GetObjectItemSet()); + + // Enable lines to guarantee that the object becomes visible + aSet.Put(XLineStyleItem(css::drawing::LineStyle_SOLID)); + + pPathObj->SetMergedItemSet(aSet); + + return std::unique_ptr<SdrAttrObj,SdrObjectFreeOp>(pPathObj.release()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/engine3d/obj3d.cxx b/svx/source/engine3d/obj3d.cxx new file mode 100644 index 000000000..eca597bb4 --- /dev/null +++ b/svx/source/engine3d/obj3d.cxx @@ -0,0 +1,625 @@ +/* -*- 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 <o3tl/numeric.hxx> + +#include <svx/strings.hrc> +#include <svx/dialmgr.hxx> +#include <svx/svdhdl.hxx> +#include <svx/svdmodel.hxx> +#include <svx/globl3d.hxx> +#include <svx/scene3d.hxx> +#include <svx/obj3d.hxx> +#include <sdr/properties/e3dproperties.hxx> +#include <sdr/properties/e3dcompoundproperties.hxx> +#include <basegfx/polygon/b3dpolypolygontools.hxx> +#include <basegfx/point/b3dpoint.hxx> +#include <basegfx/matrix/b3dhommatrix.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <basegfx/polygon/b2dpolypolygontools.hxx> +#include <svx/helperhittest3d.hxx> +#include <sdr/contact/viewcontactofe3d.hxx> +#include <drawinglayer/geometry/viewinformation3d.hxx> +#include <com/sun/star/uno/Sequence.h> +#include <svx/sdr/contact/viewcontactofe3dscene.hxx> +#include <svx/e3dsceneupdater.hxx> +#include <rtl/ustrbuf.hxx> + +using namespace com::sun::star; + +std::unique_ptr<sdr::properties::BaseProperties> E3dObject::CreateObjectSpecificProperties() +{ + return std::make_unique<sdr::properties::E3dProperties>(*this); +} + +E3dObject::E3dObject(SdrModel& rSdrModel) +: SdrAttrObj(rSdrModel), + maLocalBoundVol(), + maTransformation(), + maFullTransform(), + mbTfHasChanged(true), + mbIsSelected(false) +{ + bIs3DObj = true; + bClosedObj = true; +} + +E3dObject::~E3dObject() +{ +} + +void E3dObject::SetSelected(bool bNew) +{ + if(mbIsSelected != bNew) + { + mbIsSelected = bNew; + } +} + +// Break, default implementations +bool E3dObject::IsBreakObjPossible() +{ + return false; +} + +std::unique_ptr<SdrAttrObj,SdrObjectFreeOp> E3dObject::GetBreakObj() +{ + return nullptr; +} + +SdrInventor E3dObject::GetObjInventor() const +{ + return SdrInventor::E3d; +} + +sal_uInt16 E3dObject::GetObjIdentifier() const +{ + return E3D_OBJECT_ID; +} + +// Determine the capabilities of the object +void E3dObject::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const +{ + rInfo.bResizeFreeAllowed = true; + rInfo.bResizePropAllowed = true; + rInfo.bRotateFreeAllowed = true; + rInfo.bRotate90Allowed = true; + rInfo.bMirrorFreeAllowed = false; + rInfo.bMirror45Allowed = false; + rInfo.bMirror90Allowed = false; + rInfo.bShearAllowed = false; + rInfo.bEdgeRadiusAllowed = false; + rInfo.bCanConvToPath = false; + + // no transparence for 3d objects + rInfo.bTransparenceAllowed = false; + + // Convert 3D objects in a group of polygons: + // At first not only possible, because the creation of a group of + // 2D polygons would be required which need to be sorted by depth, + // ie at intersections be cut relative to each other. Also the texture + // coordinates were an unsolved problem. + rInfo.bCanConvToPoly = false; + rInfo.bCanConvToContour = false; + rInfo.bCanConvToPathLineToArea = false; + rInfo.bCanConvToPolyLineToArea = false; +} + +// resize object, used from old 2d interfaces, e.g. in Move/Scale dialog (F4) +void E3dObject::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact) +{ + // Movement in X, Y in the eye coordinate system + E3dScene* pScene(getRootE3dSceneFromE3dObject()); + + if(nullptr == pScene) + { + return; + } + + // transform pos from 2D world to 3D eye + const sdr::contact::ViewContactOfE3dScene& rVCScene = static_cast< sdr::contact::ViewContactOfE3dScene& >(pScene->GetViewContact()); + const drawinglayer::geometry::ViewInformation3D& aViewInfo3D(rVCScene.getViewInformation3D()); + basegfx::B2DPoint aScaleCenter2D(static_cast<double>(rRef.X()), static_cast<double>(rRef.Y())); + basegfx::B2DHomMatrix aInverseSceneTransform(rVCScene.getObjectTransformation()); + + aInverseSceneTransform.invert(); + aScaleCenter2D = aInverseSceneTransform * aScaleCenter2D; + + basegfx::B3DPoint aScaleCenter3D(aScaleCenter2D.getX(), aScaleCenter2D.getY(), 0.5); + basegfx::B3DHomMatrix aInverseViewToEye(aViewInfo3D.getDeviceToView() * aViewInfo3D.getProjection()); + + aInverseViewToEye.invert(); + aScaleCenter3D = aInverseViewToEye * aScaleCenter3D; + + // Get scale factors + double fScaleX(xFact); + double fScaleY(yFact); + + // build transform + basegfx::B3DHomMatrix aInverseOrientation(aViewInfo3D.getOrientation()); + aInverseOrientation.invert(); + basegfx::B3DHomMatrix aFullTransform(GetFullTransform()); + basegfx::B3DHomMatrix aTrans(aFullTransform); + + aTrans *= aViewInfo3D.getOrientation(); + aTrans.translate(-aScaleCenter3D.getX(), -aScaleCenter3D.getY(), -aScaleCenter3D.getZ()); + aTrans.scale(fScaleX, fScaleY, 1.0); + aTrans.translate(aScaleCenter3D.getX(), aScaleCenter3D.getY(), aScaleCenter3D.getZ()); + aTrans *= aInverseOrientation; + aFullTransform.invert(); + aTrans *= aFullTransform; + + // Apply + basegfx::B3DHomMatrix aObjTrans(GetTransform()); + aObjTrans *= aTrans; + + E3DModifySceneSnapRectUpdater aUpdater(this); + SetTransform(aObjTrans); +} + +// Move object in 2D is needed when using cursor keys +void E3dObject::NbcMove(const Size& rSize) +{ + // Movement in X, Y in the eye coordinate system + E3dScene* pScene(getRootE3dSceneFromE3dObject()); + + if(nullptr == pScene) + { + return; + } + + //Dimensions of the scene in 3D and 2D for comparison + tools::Rectangle aRect = pScene->GetSnapRect(); + basegfx::B3DHomMatrix aInvDispTransform; + E3dScene* pParent(getParentE3dSceneFromE3dObject()); + + if(nullptr != pParent) + { + aInvDispTransform = pParent->GetFullTransform(); + aInvDispTransform.invert(); + } + + // BoundVolume from 3d world to 3d eye + const sdr::contact::ViewContactOfE3dScene& rVCScene = static_cast< sdr::contact::ViewContactOfE3dScene& >(pScene->GetViewContact()); + const drawinglayer::geometry::ViewInformation3D& aViewInfo3D(rVCScene.getViewInformation3D()); + basegfx::B3DRange aEyeVol(pScene->GetBoundVolume()); + aEyeVol.transform(aViewInfo3D.getOrientation()); + + if ((aRect.GetWidth() == 0) || (aRect.GetHeight() == 0)) + throw o3tl::divide_by_zero(); + + // build relative movement vector in eye coordinates + basegfx::B3DPoint aMove( + static_cast<double>(rSize.Width()) * aEyeVol.getWidth() / static_cast<double>(aRect.GetWidth()), + static_cast<double>(-rSize.Height()) * aEyeVol.getHeight() / static_cast<double>(aRect.GetHeight()), + 0.0); + basegfx::B3DPoint aPos(0.0, 0.0, 0.0); + + // movement vector to local coordinates of objects' parent + basegfx::B3DHomMatrix aInverseOrientation(aViewInfo3D.getOrientation()); + aInverseOrientation.invert(); + basegfx::B3DHomMatrix aCompleteTrans(aInvDispTransform * aInverseOrientation); + + aMove = aCompleteTrans * aMove; + aPos = aCompleteTrans * aPos; + + // build transformation and apply + basegfx::B3DHomMatrix aTranslate; + aTranslate.translate(aMove.getX() - aPos.getX(), aMove.getY() - aPos.getY(), aMove.getZ() - aPos.getZ()); + + E3DModifySceneSnapRectUpdater aUpdater(pScene); + SetTransform(aTranslate * GetTransform()); +} + +void E3dObject::RecalcSnapRect() +{ + maSnapRect = tools::Rectangle(); +} + +// Inform parent of changes in the structure (eg by transformation), in this +// process the object in which the change has occurred is returned. +void E3dObject::StructureChanged() +{ + E3dScene* pParent(getParentE3dSceneFromE3dObject()); + + if(nullptr != pParent) + { + pParent->InvalidateBoundVolume(); + pParent->StructureChanged(); + } +} + +E3dScene* E3dObject::getParentE3dSceneFromE3dObject() const +{ + return dynamic_cast< E3dScene* >(getParentSdrObjectFromSdrObject()); +} + +// Determine the top-level scene object +E3dScene* E3dObject::getRootE3dSceneFromE3dObject() const +{ + E3dScene* pParent(getParentE3dSceneFromE3dObject()); + + if(nullptr != pParent) + { + return pParent->getRootE3dSceneFromE3dObject(); + } + + return nullptr; +} + +// Calculate enclosed volume, including all child objects +basegfx::B3DRange E3dObject::RecalcBoundVolume() const +{ + basegfx::B3DRange aRetval; + const sdr::contact::ViewContactOfE3d* pVCOfE3D = dynamic_cast< const sdr::contact::ViewContactOfE3d* >(&GetViewContact()); + + if(pVCOfE3D) + { + // BoundVolume is without 3D object transformation, use correct sequence + const drawinglayer::primitive3d::Primitive3DContainer& xLocalSequence(pVCOfE3D->getVIP3DSWithoutObjectTransform()); + + if(!xLocalSequence.empty()) + { + const uno::Sequence< beans::PropertyValue > aEmptyParameters; + const drawinglayer::geometry::ViewInformation3D aLocalViewInformation3D(aEmptyParameters); + + aRetval = xLocalSequence.getB3DRange(aLocalViewInformation3D); + } + } + + return aRetval; +} + +// Get enclosed volume and possibly recalculate it +const basegfx::B3DRange& E3dObject::GetBoundVolume() const +{ + if(maLocalBoundVol.isEmpty()) + { + const_cast< E3dObject* >(this)->maLocalBoundVol = RecalcBoundVolume(); + } + + return maLocalBoundVol; +} + +void E3dObject::InvalidateBoundVolume() +{ + maLocalBoundVol.reset(); +} + +// Pass on the changes in transformation to all child objects +void E3dObject::SetTransformChanged() +{ + InvalidateBoundVolume(); + mbTfHasChanged = true; +} + +// Define the hierarchical transformation over all Parents, store in +// maFullTransform and return them +const basegfx::B3DHomMatrix& E3dObject::GetFullTransform() const +{ + if(mbTfHasChanged) + { + basegfx::B3DHomMatrix aNewFullTransformation(maTransformation); + E3dScene* pParent(getParentE3dSceneFromE3dObject()); + + if(nullptr != pParent) + { + aNewFullTransformation = pParent->GetFullTransform() * aNewFullTransformation; + } + + const_cast< E3dObject* >(this)->maFullTransform = aNewFullTransformation; + const_cast< E3dObject* >(this)->mbTfHasChanged = false; + } + + return maFullTransform; +} + +void E3dObject::NbcSetTransform(const basegfx::B3DHomMatrix& rMatrix) +{ + if(maTransformation != rMatrix) + { + maTransformation = rMatrix; + SetTransformChanged(); + StructureChanged(); + } +} + +// Set transformation matrix with repaint broadcast +void E3dObject::SetTransform(const basegfx::B3DHomMatrix& rMatrix) +{ + if(rMatrix != maTransformation) + { + NbcSetTransform(rMatrix); + SetChanged(); + BroadcastObjectChange(); + if (pUserCall != nullptr) pUserCall->Changed(*this, SdrUserCallType::Resize, tools::Rectangle()); + } +} + +basegfx::B3DPolyPolygon E3dObject::CreateWireframe() const +{ + const basegfx::B3DRange aBoundVolume(GetBoundVolume()); + return basegfx::utils::createCubePolyPolygonFromB3DRange(aBoundVolume); +} + +// Get the name of the object (singular) +OUString E3dObject::TakeObjNameSingul() const +{ + OUStringBuffer sName(SvxResId(STR_ObjNameSingulObj3d)); + + OUString aName(GetName()); + if (!aName.isEmpty()) + { + sName.append(' '); + sName.append('\''); + sName.append(aName); + sName.append('\''); + } + return sName.makeStringAndClear(); +} + +// Get the name of the object (plural) +OUString E3dObject::TakeObjNamePlural() const +{ + return SvxResId(STR_ObjNamePluralObj3d); +} + +E3dObject* E3dObject::CloneSdrObject(SdrModel& rTargetModel) const +{ + return CloneHelper< E3dObject >(rTargetModel); +} + +E3dObject& E3dObject::operator=(const E3dObject& rSource) +{ + if(this != &rSource) + { + // call parent + SdrAttrObj::operator=(rSource); + + // BoundVol can be copied since also the children are copied + maLocalBoundVol = rSource.maLocalBoundVol; + maTransformation = rSource.maTransformation; + + // Because the parent may have changed, definitely redefine the total + // transformation next time + SetTransformChanged(); + + // Copy selection status + mbIsSelected = rSource.mbIsSelected; + } + + return *this; +} + +SdrObjGeoData *E3dObject::NewGeoData() const +{ + return new E3DObjGeoData; +} + +void E3dObject::SaveGeoData(SdrObjGeoData& rGeo) const +{ + SdrAttrObj::SaveGeoData (rGeo); + + static_cast<E3DObjGeoData &>(rGeo).maLocalBoundVol = maLocalBoundVol; + static_cast<E3DObjGeoData &>(rGeo).maTransformation = maTransformation; +} + +void E3dObject::RestGeoData(const SdrObjGeoData& rGeo) +{ + maLocalBoundVol = static_cast<const E3DObjGeoData &>(rGeo).maLocalBoundVol; + E3DModifySceneSnapRectUpdater aUpdater(this); + NbcSetTransform(static_cast<const E3DObjGeoData &>(rGeo).maTransformation); + SdrAttrObj::RestGeoData (rGeo); +} + +// 2D-rotation of a 3D-body, normally this is done by the scene itself. +// This is however a correct implementation, because everything that has +// happened is a rotation around the axis perpendicular to the screen and that +// is regardless of how the scene has been rotated up until now. +void E3dObject::NbcRotate(const Point& rRef, long nAngle, double sn, double cs) +{ + // So currently the glue points are defined relative to the scene aOutRect. + // Before turning the glue points are defined relative to the page. They + // take no part in the rotation of the scene. To ensure this, there is the + // SetGlueReallyAbsolute(sal_True); + double fAngleInRad = basegfx::deg2rad(nAngle/100.0); + + basegfx::B3DHomMatrix aRotateZ; + aRotateZ.rotate(0.0, 0.0, fAngleInRad); + NbcSetTransform(aRotateZ * GetTransform()); + + SetRectsDirty(); // This forces a recalculation of all BoundRects + NbcRotateGluePoints(rRef,nAngle,sn,cs); // Rotate the glue points (who still + // have coordinates relative to the + // original page) + SetGlueReallyAbsolute(false); // from now they are again relative to BoundRect (that is defined as aOutRect) +} + +std::unique_ptr<sdr::properties::BaseProperties> E3dCompoundObject::CreateObjectSpecificProperties() +{ + return std::make_unique<sdr::properties::E3dCompoundProperties>(*this); +} + +E3dCompoundObject::E3dCompoundObject(SdrModel& rSdrModel) +: E3dObject(rSdrModel) +{ +} + +E3dCompoundObject::~E3dCompoundObject () +{ +} + +basegfx::B2DPolyPolygon E3dCompoundObject::TakeXorPoly() const +{ + basegfx::B2DPolyPolygon aRetval; + const uno::Sequence< beans::PropertyValue > aEmptyParameters; + drawinglayer::geometry::ViewInformation3D aViewInfo3D(aEmptyParameters); + E3dScene* pRootScene = fillViewInformation3DForCompoundObject(aViewInfo3D, *this); + + if(pRootScene) + { + const sdr::contact::ViewContactOfE3dScene& rVCScene = static_cast< sdr::contact::ViewContactOfE3dScene& >(pRootScene->GetViewContact()); + const basegfx::B3DPolyPolygon aCubePolyPolygon(CreateWireframe()); + aRetval = basegfx::utils::createB2DPolyPolygonFromB3DPolyPolygon(aCubePolyPolygon, + aViewInfo3D.getObjectToView() * GetTransform()); + aRetval.transform(rVCScene.getObjectTransformation()); + } + + return aRetval; +} + +sal_uInt32 E3dCompoundObject::GetHdlCount() const +{ + // 8 corners + 1 E3dVolumeMarker (= Wireframe representation) + return 9; +} + +void E3dCompoundObject::AddToHdlList(SdrHdlList& rHdlList) const +{ + const uno::Sequence< beans::PropertyValue > aEmptyParameters; + drawinglayer::geometry::ViewInformation3D aViewInfo3D(aEmptyParameters); + E3dScene* pRootScene = fillViewInformation3DForCompoundObject(aViewInfo3D, *this); + + if(pRootScene) + { + const basegfx::B3DRange aBoundVolume(GetBoundVolume()); + + if(!aBoundVolume.isEmpty()) + { + const sdr::contact::ViewContactOfE3dScene& rVCScene = static_cast< sdr::contact::ViewContactOfE3dScene& >(pRootScene->GetViewContact()); + + for(sal_uInt32 a(0); a < 8; a++) + { + basegfx::B3DPoint aPos3D; + + switch(a) + { + case 0 : aPos3D.setX(aBoundVolume.getMinX()); aPos3D.setY(aBoundVolume.getMinY()); aPos3D.setZ(aBoundVolume.getMinZ()); break; + case 1 : aPos3D.setX(aBoundVolume.getMinX()); aPos3D.setY(aBoundVolume.getMinY()); aPos3D.setZ(aBoundVolume.getMaxZ()); break; + case 2 : aPos3D.setX(aBoundVolume.getMinX()); aPos3D.setY(aBoundVolume.getMaxY()); aPos3D.setZ(aBoundVolume.getMinZ()); break; + case 3 : aPos3D.setX(aBoundVolume.getMinX()); aPos3D.setY(aBoundVolume.getMaxY()); aPos3D.setZ(aBoundVolume.getMaxZ()); break; + case 4 : aPos3D.setX(aBoundVolume.getMaxX()); aPos3D.setY(aBoundVolume.getMinY()); aPos3D.setZ(aBoundVolume.getMinZ()); break; + case 5 : aPos3D.setX(aBoundVolume.getMaxX()); aPos3D.setY(aBoundVolume.getMinY()); aPos3D.setZ(aBoundVolume.getMaxZ()); break; + case 6 : aPos3D.setX(aBoundVolume.getMaxX()); aPos3D.setY(aBoundVolume.getMaxY()); aPos3D.setZ(aBoundVolume.getMinZ()); break; + case 7 : aPos3D.setX(aBoundVolume.getMaxX()); aPos3D.setY(aBoundVolume.getMaxY()); aPos3D.setZ(aBoundVolume.getMaxZ()); break; + } + + // to 3d view coor + aPos3D *= aViewInfo3D.getObjectToView() * GetTransform(); + + // create 2d relative scene + basegfx::B2DPoint aPos2D(aPos3D.getX(), aPos3D.getY()); + + // to 2d world coor + aPos2D *= rVCScene.getObjectTransformation(); + + rHdlList.AddHdl(std::make_unique<SdrHdl>(Point(basegfx::fround(aPos2D.getX()), basegfx::fround(aPos2D.getY())), SdrHdlKind::BezierWeight)); + } + } + } + + const basegfx::B2DPolyPolygon aPolyPolygon(TakeXorPoly()); + + if(aPolyPolygon.count()) + { + rHdlList.AddHdl(std::make_unique<E3dVolumeMarker>(aPolyPolygon)); + } +} + +sal_uInt16 E3dCompoundObject::GetObjIdentifier() const +{ + return E3D_COMPOUNDOBJ_ID; +} + +void E3dCompoundObject::RecalcSnapRect() +{ + const uno::Sequence< beans::PropertyValue > aEmptyParameters; + drawinglayer::geometry::ViewInformation3D aViewInfo3D(aEmptyParameters); + E3dScene* pRootScene = fillViewInformation3DForCompoundObject(aViewInfo3D, *this); + maSnapRect = tools::Rectangle(); + + if(pRootScene) + { + // get VC of 3D candidate + const sdr::contact::ViewContactOfE3d* pVCOfE3D = dynamic_cast< const sdr::contact::ViewContactOfE3d* >(&GetViewContact()); + + if(pVCOfE3D) + { + // get 3D primitive sequence + const drawinglayer::primitive3d::Primitive3DContainer xLocalSequence(pVCOfE3D->getViewIndependentPrimitive3DContainer()); + + if(!xLocalSequence.empty()) + { + // get BoundVolume + basegfx::B3DRange aBoundVolume(xLocalSequence.getB3DRange(aViewInfo3D)); + + // transform bound volume to relative scene coordinates + aBoundVolume.transform(aViewInfo3D.getObjectToView()); + + // build 2d relative scene range + basegfx::B2DRange aSnapRange( + aBoundVolume.getMinX(), aBoundVolume.getMinY(), + aBoundVolume.getMaxX(), aBoundVolume.getMaxY()); + + // transform to 2D world coordinates + const sdr::contact::ViewContactOfE3dScene& rVCScene = static_cast< sdr::contact::ViewContactOfE3dScene& >(pRootScene->GetViewContact()); + aSnapRange.transform(rVCScene.getObjectTransformation()); + + // snap to integer + maSnapRect = tools::Rectangle( + sal_Int32(floor(aSnapRange.getMinX())), sal_Int32(floor(aSnapRange.getMinY())), + sal_Int32(ceil(aSnapRange.getMaxX())), sal_Int32(ceil(aSnapRange.getMaxY()))); + } + } + } +} + +E3dCompoundObject* E3dCompoundObject::CloneSdrObject(SdrModel& rTargetModel) const +{ + return CloneHelper< E3dCompoundObject >(rTargetModel); +} + +E3dCompoundObject& E3dCompoundObject::operator=(const E3dCompoundObject& rObj) +{ + if( this == &rObj ) + return *this; + E3dObject::operator=(rObj); + return *this; +} + +// convert given basegfx::B3DPolyPolygon to screen coor +basegfx::B2DPolyPolygon E3dCompoundObject::TransformToScreenCoor(const basegfx::B3DPolyPolygon& rCandidate) +{ + const uno::Sequence< beans::PropertyValue > aEmptyParameters; + drawinglayer::geometry::ViewInformation3D aViewInfo3D(aEmptyParameters); + E3dScene* pRootScene = fillViewInformation3DForCompoundObject(aViewInfo3D, *this); + basegfx::B2DPolyPolygon aRetval; + + if(pRootScene) + { + aRetval = basegfx::utils::createB2DPolyPolygonFromB3DPolyPolygon(rCandidate, + aViewInfo3D.getObjectToView() * GetTransform()); + const sdr::contact::ViewContactOfE3dScene& rVCScene = static_cast< sdr::contact::ViewContactOfE3dScene& >(pRootScene->GetViewContact()); + aRetval.transform(rVCScene.getObjectTransformation()); + } + + return aRetval; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/engine3d/objfac3d.cxx b/svx/source/engine3d/objfac3d.cxx new file mode 100644 index 000000000..5c617127b --- /dev/null +++ b/svx/source/engine3d/objfac3d.cxx @@ -0,0 +1,73 @@ +/* -*- 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/globl3d.hxx> +#include <svx/cube3d.hxx> +#include <svx/sphere3d.hxx> +#include <extrud3d.hxx> +#include <svx/lathe3d.hxx> +#include <polygn3d.hxx> +#include <svx/objfac3d.hxx> +#include <svx/svdobj.hxx> +#include <svx/scene3d.hxx> + +static bool bInit = false; + +E3dObjFactory::E3dObjFactory() +{ + if ( !bInit ) + { + SdrObjFactory::InsertMakeObjectHdl(LINK(this, E3dObjFactory, MakeObject)); + bInit = true; + } +} + +E3dObjFactory::~E3dObjFactory() +{ +} + +// Generate chart internal objects + +IMPL_STATIC_LINK( E3dObjFactory, MakeObject, SdrObjCreatorParams, aParams, SdrObject* ) +{ + if ( aParams.nInventor == SdrInventor::E3d ) + { + switch ( aParams.nObjIdentifier ) + { + case E3D_SCENE_ID: + return new E3dScene(aParams.rSdrModel); + case E3D_POLYGONOBJ_ID : + return new E3dPolygonObj(aParams.rSdrModel); + case E3D_CUBEOBJ_ID : + return new E3dCubeObj(aParams.rSdrModel); + case E3D_SPHEREOBJ_ID: + return new E3dSphereObj(aParams.rSdrModel); + case E3D_EXTRUDEOBJ_ID: + return new E3dExtrudeObj(aParams.rSdrModel); + case E3D_LATHEOBJ_ID: + return new E3dLatheObj(aParams.rSdrModel); + case E3D_COMPOUNDOBJ_ID: + return new E3dCompoundObject(aParams.rSdrModel); + } + } + return nullptr; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/engine3d/polygn3d.cxx b/svx/source/engine3d/polygn3d.cxx new file mode 100644 index 000000000..d8c1b5d0c --- /dev/null +++ b/svx/source/engine3d/polygn3d.cxx @@ -0,0 +1,248 @@ +/* -*- 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 <polygn3d.hxx> +#include <svx/globl3d.hxx> +#include <basegfx/point/b3dpoint.hxx> +#include <sdr/contact/viewcontactofe3dpolygon.hxx> +#include <basegfx/polygon/b3dpolygon.hxx> +#include <basegfx/polygon/b3dpolygontools.hxx> + +// DrawContact section +std::unique_ptr<sdr::contact::ViewContact> E3dPolygonObj::CreateObjectSpecificViewContact() +{ + return std::make_unique<sdr::contact::ViewContactOfE3dPolygon>(*this); +} + +E3dPolygonObj::E3dPolygonObj( + SdrModel& rSdrModel, + const basegfx::B3DPolyPolygon& rPolyPoly3D) +: E3dCompoundObject(rSdrModel), + bLineOnly(true) +{ + // Set geometry + SetPolyPolygon3D(rPolyPoly3D); + + // Create default normals + CreateDefaultNormals(); + + // Create default texture coordinates + CreateDefaultTexture(); +} + +E3dPolygonObj::E3dPolygonObj(SdrModel& rSdrModel) +: E3dCompoundObject(rSdrModel), + bLineOnly(false) +{ + // Create no geometry +} + +void E3dPolygonObj::CreateDefaultNormals() +{ + basegfx::B3DPolyPolygon aPolyNormals; + + // Create a complete tools::PolyPolygon with the plane normal + for(sal_uInt32 a(0); a < aPolyPoly3D.count(); a++) + { + // Find source polygon + const basegfx::B3DPolygon aPolygon(aPolyPoly3D.getB3DPolygon(a)); + + // Creating a new polygon for the normal + basegfx::B3DPolygon aNormals; + + // Get normal (and invert) + basegfx::B3DVector aNormal(-aPolygon.getNormal()); + + // Fill new polygon + for(sal_uInt32 b(0); b < aPolygon.count(); b++) + { + aNormals.append(aNormal); + } + + // Insert new polygon into the PolyPolygon + aPolyNormals.append(aNormals); + } + + // Set default normal + SetPolyNormals3D(aPolyNormals); +} + +void E3dPolygonObj::CreateDefaultTexture() +{ + basegfx::B2DPolyPolygon aPolyTexture; + // Create a complete tools::PolyPolygon with the texture coordinates + // The texture coordinates extend over X,Y and Z + // on the whole extreme values in the range 0.0 .. 1.0 + for(sal_uInt32 a(0); a < aPolyPoly3D.count(); a++) + { + // Find source polygon + const basegfx::B3DPolygon& aPolygon(aPolyPoly3D.getB3DPolygon(a)); + + // Determine the total size of the object + basegfx::B3DRange aVolume(basegfx::utils::getRange(aPolygon)); + + // Get normal + basegfx::B3DVector aNormal(aPolygon.getNormal()); + aNormal.setX(fabs(aNormal.getX())); + aNormal.setY(fabs(aNormal.getY())); + aNormal.setZ(fabs(aNormal.getZ())); + + // Decide which coordinates should be used as a source for the mapping + sal_uInt16 nSourceMode = 0; + + // Determine the greatest degree of freedom + if(!(aNormal.getX() > aNormal.getY() && aNormal.getX() > aNormal.getZ())) + { + if(aNormal.getY() > aNormal.getZ()) + { + // Y is the largest, use X,Z as mapping + nSourceMode = 1; + } + else + { + // Z is the largest, use X,Y as mapping + nSourceMode = 2; + } + } + + // Create new polygon for texture coordinates + basegfx::B2DPolygon aTexture; + + // Fill new polygon + for(sal_uInt32 b(0); b < aPolygon.count(); b++) + { + basegfx::B2DPoint aTex; + const basegfx::B3DPoint aCandidate(aPolygon.getB3DPoint(b)); + + switch(nSourceMode) + { + case 0: //Source is Y,Z + if(aVolume.getHeight()) + aTex.setX((aCandidate.getY() - aVolume.getMinY()) / aVolume.getHeight()); + if(aVolume.getDepth()) + aTex.setY((aCandidate.getZ() - aVolume.getMinZ()) / aVolume.getDepth()); + break; + + case 1: // Source is X,Z + if(aVolume.getWidth()) + aTex.setX((aCandidate.getX() - aVolume.getMinX()) / aVolume.getWidth()); + if(aVolume.getDepth()) + aTex.setY((aCandidate.getZ() - aVolume.getMinZ()) / aVolume.getDepth()); + break; + + case 2: // Source is X,Y + if(aVolume.getWidth()) + aTex.setX((aCandidate.getX() - aVolume.getMinX()) / aVolume.getWidth()); + if(aVolume.getHeight()) + aTex.setY((aCandidate.getY() - aVolume.getMinY()) / aVolume.getHeight()); + break; + } + + aTexture.append(aTex); + } + + // Insert new polygon into the PolyPolygon + aPolyTexture.append(aTexture); + } + + // Set default Texture coordinates + SetPolyTexture2D(aPolyTexture); +} + +E3dPolygonObj::~E3dPolygonObj() +{ +} + +sal_uInt16 E3dPolygonObj::GetObjIdentifier() const +{ + return E3D_POLYGONOBJ_ID; +} + +void E3dPolygonObj::SetPolyPolygon3D(const basegfx::B3DPolyPolygon& rNewPolyPoly3D) +{ + if ( aPolyPoly3D != rNewPolyPoly3D ) + { + // New PolyPolygon; copying + aPolyPoly3D = rNewPolyPoly3D; + + // Create new geometry + ActionChanged(); + } +} + +void E3dPolygonObj::SetPolyNormals3D(const basegfx::B3DPolyPolygon& rNewPolyNormals3D) +{ + if ( aPolyNormals3D != rNewPolyNormals3D ) + { + // New PolyPolygon; copying + aPolyNormals3D = rNewPolyNormals3D; + + // Create new geometry + ActionChanged(); + } +} + +void E3dPolygonObj::SetPolyTexture2D(const basegfx::B2DPolyPolygon& rNewPolyTexture2D) +{ + if ( aPolyTexture2D != rNewPolyTexture2D ) + { + // New PolyPolygon; copying + aPolyTexture2D = rNewPolyTexture2D; + + // Create new geometry + ActionChanged(); + } +} + +// Convert the object into a group object consisting of 6 polygons + +SdrObjectUniquePtr E3dPolygonObj::DoConvertToPolyObj(bool /*bBezier*/, bool /*bAddText*/) const +{ + return nullptr; +} + +E3dPolygonObj* E3dPolygonObj::CloneSdrObject(SdrModel& rTargetModel) const +{ + return CloneHelper< E3dPolygonObj >(rTargetModel); +} + +E3dPolygonObj& E3dPolygonObj::operator=(const E3dPolygonObj& rObj) +{ + if( this == &rObj ) + return *this; + E3dCompoundObject::operator=(rObj); + + aPolyPoly3D = rObj.aPolyPoly3D; + aPolyNormals3D = rObj.aPolyNormals3D; + aPolyTexture2D = rObj.aPolyTexture2D; + bLineOnly = rObj.bLineOnly; + + return *this; +} + +void E3dPolygonObj::SetLineOnly(bool bNew) +{ + if(bNew != bLineOnly) + { + bLineOnly = bNew; + ActionChanged(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/engine3d/scene3d.cxx b/svx/source/engine3d/scene3d.cxx new file mode 100644 index 000000000..66b134770 --- /dev/null +++ b/svx/source/engine3d/scene3d.cxx @@ -0,0 +1,909 @@ +/* -*- 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/strings.hrc> +#include <svx/dialmgr.hxx> +#include <svx/svditer.hxx> + +#include <stdlib.h> +#include <svx/globl3d.hxx> +#include <svx/svdpage.hxx> +#include <svx/scene3d.hxx> +#include <svx/svdtrans.hxx> +#include <sdr/properties/e3dsceneproperties.hxx> +#include <svx/sdr/contact/viewcontactofe3dscene.hxx> +#include <svx/svddrag.hxx> +#include "helperminimaldepth3d.hxx" +#include <algorithm> +#include <drawinglayer/geometry/viewinformation3d.hxx> +#include <basegfx/polygon/b2dpolypolygontools.hxx> +#include <svx/e3dsceneupdater.hxx> +#include <svx/svdmodel.hxx> +#include <rtl/ustrbuf.hxx> + +namespace { + +class ImpRemap3DDepth +{ + sal_uInt32 mnOrdNum; + double mfMinimalDepth; + + // bit field + bool mbIsScene : 1; + +public: + ImpRemap3DDepth(sal_uInt32 nOrdNum, double fMinimalDepth); + explicit ImpRemap3DDepth(sal_uInt32 nOrdNum); + + // for ::std::sort + bool operator<(const ImpRemap3DDepth& rComp) const; + + sal_uInt32 GetOrdNum() const { return mnOrdNum; } + bool IsScene() const { return mbIsScene; } +}; + +} + +ImpRemap3DDepth::ImpRemap3DDepth(sal_uInt32 nOrdNum, double fMinimalDepth) +: mnOrdNum(nOrdNum), + mfMinimalDepth(fMinimalDepth), + mbIsScene(false) +{ +} + +ImpRemap3DDepth::ImpRemap3DDepth(sal_uInt32 nOrdNum) +: mnOrdNum(nOrdNum), + mfMinimalDepth(0.0), + mbIsScene(true) +{ +} + +bool ImpRemap3DDepth::operator<(const ImpRemap3DDepth& rComp) const +{ + if(IsScene()) + { + return false; + } + else + { + if(rComp.IsScene()) + { + return true; + } + else + { + return mfMinimalDepth < rComp.mfMinimalDepth; + } + } +} + +class Imp3DDepthRemapper +{ + std::vector< ImpRemap3DDepth > maVector; + +public: + explicit Imp3DDepthRemapper(E3dScene const & rScene); + + sal_uInt32 RemapOrdNum(sal_uInt32 nOrdNum) const; +}; + +Imp3DDepthRemapper::Imp3DDepthRemapper(E3dScene const & rScene) +{ + // only called when rScene.GetSubList() and nObjCount > 1 + SdrObjList* pList = rScene.GetSubList(); + const size_t nObjCount(pList->GetObjCount()); + + for(size_t a = 0; a < nObjCount; ++a) + { + SdrObject* pCandidate = pList->GetObj(a); + + if(pCandidate) + { + if(dynamic_cast< const E3dCompoundObject*>(pCandidate)) + { + // single 3d object, calc depth + const double fMinimalDepth(getMinimalDepthInViewCoordinates(static_cast< const E3dCompoundObject& >(*pCandidate))); + ImpRemap3DDepth aEntry(a, fMinimalDepth); + maVector.push_back(aEntry); + } + else + { + // scene, use standard entry for scene + ImpRemap3DDepth aEntry(a); + maVector.push_back(aEntry); + } + } + } + + // now, we need to sort the maVector by its members minimal depth. The + // smaller, the nearer to the viewer. + ::std::sort(maVector.begin(), maVector.end()); +} + +sal_uInt32 Imp3DDepthRemapper::RemapOrdNum(sal_uInt32 nOrdNum) const +{ + if(nOrdNum < maVector.size()) + { + nOrdNum = maVector[(maVector.size() - 1) - nOrdNum].GetOrdNum(); + } + + return nOrdNum; +} + + +// BaseProperties section + +std::unique_ptr<sdr::properties::BaseProperties> E3dScene::CreateObjectSpecificProperties() +{ + return std::make_unique<sdr::properties::E3dSceneProperties>(*this); +} + + +// DrawContact section + +std::unique_ptr<sdr::contact::ViewContact> E3dScene::CreateObjectSpecificViewContact() +{ + return std::make_unique<sdr::contact::ViewContactOfE3dScene>(*this); +} + + +E3dScene::E3dScene(SdrModel& rSdrModel) +: E3dObject(rSdrModel), + SdrObjList(), + aCamera(basegfx::B3DPoint(0.0, 0.0, 4.0), basegfx::B3DPoint()), + bDrawOnlySelected(false), + mbSkipSettingDirty(false) +{ + // Set defaults + SetDefaultAttributes(); +} + +void E3dScene::SetDefaultAttributes() +{ + // For WIN95/NT turn off the FP-Exceptions +#if defined(_WIN32) + _control87( _MCW_EM, _MCW_EM ); +#endif + + // Set defaults + aCamera.SetViewWindow(-2, -2, 4, 4); + aCameraSet.SetDeviceRectangle(-2, 2, -2, 2); + aCamera.SetDeviceWindow(tools::Rectangle(0, 0, 10, 10)); + tools::Rectangle aRect(0, 0, 10, 10); + aCameraSet.SetViewportRectangle(aRect); + + // set defaults for Camera from ItemPool + aCamera.SetProjection(GetPerspective()); + basegfx::B3DPoint aActualPosition(aCamera.GetPosition()); + double fNew = GetDistance(); + + if(fabs(fNew - aActualPosition.getZ()) > 1.0) + { + aCamera.SetPosition( basegfx::B3DPoint( aActualPosition.getX(), aActualPosition.getY(), fNew) ); + } + + fNew = GetFocalLength() / 100.0; + aCamera.SetFocalLength(fNew); +} + +E3dScene::~E3dScene() +{ + ImpCleanup3DDepthMapper(); +} + +SdrPage* E3dScene::getSdrPageFromSdrObjList() const +{ + return getSdrPageFromSdrObject(); +} + +SdrObject* E3dScene::getSdrObjectFromSdrObjList() const +{ + return const_cast< E3dScene* >(this); +} + +SdrObjList* E3dScene::getChildrenOfSdrObject() const +{ + return const_cast< E3dScene* >(this); +} + +basegfx::B2DPolyPolygon E3dScene::TakeXorPoly() const +{ + const sdr::contact::ViewContactOfE3dScene& rVCScene = static_cast< sdr::contact::ViewContactOfE3dScene& >(GetViewContact()); + const drawinglayer::geometry::ViewInformation3D& aViewInfo3D(rVCScene.getViewInformation3D()); + const basegfx::B3DPolyPolygon aCubePolyPolygon(CreateWireframe()); + + basegfx::B2DPolyPolygon aRetval(basegfx::utils::createB2DPolyPolygonFromB3DPolyPolygon(aCubePolyPolygon, + aViewInfo3D.getObjectToView())); + aRetval.transform(rVCScene.getObjectTransformation()); + + return aRetval; +} + +void E3dScene::ImpCleanup3DDepthMapper() +{ + mp3DDepthRemapper.reset(); +} + +sal_uInt32 E3dScene::RemapOrdNum(sal_uInt32 nNewOrdNum) const +{ + if(!mp3DDepthRemapper) + { + const size_t nObjCount(GetSubList() ? GetSubList()->GetObjCount() : 0); + + if(nObjCount > 1) + { + mp3DDepthRemapper.reset(new Imp3DDepthRemapper(*this)); + } + } + + if(mp3DDepthRemapper) + { + return mp3DDepthRemapper->RemapOrdNum(nNewOrdNum); + } + + return nNewOrdNum; +} + +sal_uInt16 E3dScene::GetObjIdentifier() const +{ + return E3D_SCENE_ID; +} + +void E3dScene::SetBoundRectDirty() +{ + E3dScene* pScene(getRootE3dSceneFromE3dObject()); + + if(pScene == this) + { + // avoid resetting aOutRect which in case of a 3D scene used as 2d object + // is model data,not re-creatable view data + } + else + { + // if not the outmost scene it is used as group in 3d, call parent + E3dObject::SetBoundRectDirty(); + } +} + +void E3dScene::NbcSetSnapRect(const tools::Rectangle& rRect) +{ + SetRectsDirty(); + E3dObject::NbcSetSnapRect(rRect); + aCamera.SetDeviceWindow(rRect); + aCameraSet.SetViewportRectangle(rRect); + + ImpCleanup3DDepthMapper(); +} + +void E3dScene::NbcMove(const Size& rSize) +{ + tools::Rectangle aNewSnapRect = GetSnapRect(); + aNewSnapRect.Move(rSize); + NbcSetSnapRect(aNewSnapRect); +} + +void E3dScene::NbcResize(const Point& rRef, const Fraction& rXFact, + const Fraction& rYFact) +{ + tools::Rectangle aNewSnapRect = GetSnapRect(); + ResizeRect(aNewSnapRect, rRef, rXFact, rYFact); + NbcSetSnapRect(aNewSnapRect); +} + +// Set new camera, and thus mark the scene and if possible the bound volume +// as changed + +void E3dScene::SetCamera(const Camera3D& rNewCamera) +{ + aCamera = rNewCamera; + static_cast<sdr::properties::E3dSceneProperties&>(GetProperties()).SetSceneItemsFromCamera(); + + SetRectsDirty(); + + // Turn off ratio + GetCameraSet().SetRatio(0.0); + + // Set Imaging geometry + basegfx::B3DPoint aVRP(aCamera.GetViewPoint()); + basegfx::B3DVector aVPN(aVRP - aCamera.GetVRP()); + basegfx::B3DVector aVUV(aCamera.GetVUV()); + + // use SetViewportValues() to set VRP, VPN and VUV as vectors, too. + // Else these values would not be exported/imported correctly. + GetCameraSet().SetViewportValues(aVRP, aVPN, aVUV); + + // Set perspective + GetCameraSet().SetPerspective(aCamera.GetProjection() == ProjectionType::Perspective); + GetCameraSet().SetViewportRectangle(aCamera.GetDeviceWindow()); + + ImpCleanup3DDepthMapper(); +} + +// Inform parent of changes of a child + +void E3dScene::StructureChanged() +{ + E3dObject::StructureChanged(); + + E3dScene* pScene(getRootE3dSceneFromE3dObject()); + + if(nullptr != pScene && !pScene->mbSkipSettingDirty) + { + SetRectsDirty(); + } + + ImpCleanup3DDepthMapper(); +} + +// Determine the overall scene object + +E3dScene* E3dScene::getRootE3dSceneFromE3dObject() const +{ + E3dScene* pParent(getParentE3dSceneFromE3dObject()); + + if(nullptr != pParent) + { + return pParent->getRootE3dSceneFromE3dObject(); + } + + return const_cast< E3dScene* >(this); +} + +void E3dScene::removeAllNonSelectedObjects() +{ + E3DModifySceneSnapRectUpdater aUpdater(this); + + for(size_t a = 0; a < GetObjCount(); ++a) + { + SdrObject* pObj = GetObj(a); + + if(pObj) + { + bool bRemoveObject(false); + + if(dynamic_cast< const E3dScene*>(pObj)) + { + E3dScene* pScene = static_cast<E3dScene*>(pObj); + + // iterate over this sub-scene + pScene->removeAllNonSelectedObjects(); + + // check object count. Empty scenes can be deleted + const size_t nObjCount(pScene->GetSubList() ? pScene->GetSubList()->GetObjCount() : 0); + + if(!nObjCount) + { + // all objects removed, scene can be removed, too + bRemoveObject = true; + } + } + else if(dynamic_cast< const E3dCompoundObject*>(pObj)) + { + E3dCompoundObject* pCompound = static_cast<E3dCompoundObject*>(pObj); + + if(!pCompound->GetSelected()) + { + bRemoveObject = true; + } + } + + if(bRemoveObject) + { + NbcRemoveObject(pObj->GetOrdNum()); + a--; + SdrObject::Free(pObj); + } + } + } +} + +E3dScene* E3dScene::CloneSdrObject(SdrModel& rTargetModel) const +{ + return CloneHelper< E3dScene >(rTargetModel); +} + +E3dScene& E3dScene::operator=(const E3dScene& rSource) +{ + if(this != &rSource) + { + // call parent + E3dObject::operator=(rSource); + + // copy child SdrObjects + if (rSource.GetSubList()) + { + CopyObjects(*rSource.GetSubList()); + + // tdf#116979: needed here, we need bSnapRectDirty to be true + // which it is after using SdrObject::operator= (see above), + // but set to false again using CopyObjects + SetRectsDirty(); + } + + // copy local data + aCamera = rSource.aCamera; + aCameraSet = rSource.aCameraSet; + static_cast<sdr::properties::E3dSceneProperties&>(GetProperties()).SetSceneItemsFromCamera(); + InvalidateBoundVolume(); + RebuildLists(); + ImpCleanup3DDepthMapper(); + GetViewContact().ActionChanged(); + } + + return *this; +} + +void E3dScene::SuspendReportingDirtyRects() +{ + E3dScene* pScene(getRootE3dSceneFromE3dObject()); + + if(nullptr != pScene) + { + pScene->mbSkipSettingDirty = true; + } +} + +void E3dScene::ResumeReportingDirtyRects() +{ + E3dScene* pScene(getRootE3dSceneFromE3dObject()); + + if(nullptr != pScene) + { + pScene->mbSkipSettingDirty = false; + } +} + +void E3dScene::SetAllSceneRectsDirty() +{ + E3dScene* pScene(getRootE3dSceneFromE3dObject()); + + if(nullptr != pScene) + { + pScene->SetRectsDirty(); + } +} + +// Rebuild Light- and label- object lists rebuild (after loading, allocation) + +void E3dScene::RebuildLists() +{ + // first delete + const SdrLayerID nCurrLayerID(GetLayer()); + SdrObjListIter a3DIterator(GetSubList(), SdrIterMode::Flat); + + // then examine all the objects in the scene + while(a3DIterator.IsMore()) + { + E3dObject* p3DObj(static_cast< E3dObject* >(a3DIterator.Next())); + p3DObj->NbcSetLayer(nCurrLayerID); + } + + ImpCleanup3DDepthMapper(); +} + +SdrObjGeoData *E3dScene::NewGeoData() const +{ + return new E3DSceneGeoData; +} + +void E3dScene::SaveGeoData(SdrObjGeoData& rGeo) const +{ + E3dObject::SaveGeoData (rGeo); + + static_cast<E3DSceneGeoData &>(rGeo).aCamera = aCamera; +} + +void E3dScene::RestGeoData(const SdrObjGeoData& rGeo) +{ + // #i94832# removed E3DModifySceneSnapRectUpdater here. + // It should not be needed, is already part of E3dObject::RestGeoData + E3dObject::RestGeoData (rGeo); + SetCamera (static_cast<const E3DSceneGeoData &>(rGeo).aCamera); +} + +// Something was changed in the style sheet, so change scene + +void E3dScene::Notify(SfxBroadcaster &rBC, const SfxHint &rHint) +{ + SetRectsDirty(); + E3dObject::Notify(rBC, rHint); +} + +void E3dScene::RotateScene (const Point& rRef, double sn, double cs) +{ + Point UpperLeft, LowerRight, Center, NewCenter; + + UpperLeft = aOutRect.TopLeft(); + LowerRight = aOutRect.BottomRight(); + + long dxOutRectHalf = labs(UpperLeft.X() - LowerRight.X()); + dxOutRectHalf /= 2; + long dyOutRectHalf = labs(UpperLeft.Y() - LowerRight.Y()); + dyOutRectHalf /= 2; + + // Only the center is moved. The corners are moved by NbcMove. For the + // rotation a cartesian coordinate system is used in which the pivot + // point is the origin, and the y-axis increases upward, the X-axis to + // the right. This must be especially noted for the Y-values. + // (When considering a flat piece of paper the Y-axis pointing downwards + Center.setX( (UpperLeft.X() + dxOutRectHalf) - rRef.X() ); + Center.setY( -((UpperLeft.Y() + dyOutRectHalf) - rRef.Y()) ); + // A few special cases has to be dealt with first (n * 90 degrees n integer) + if (sn==1.0 && cs==0.0) { // 90deg + NewCenter.setX( -Center.Y() ); + NewCenter.setY( -Center.X() ); + } else if (sn==0.0 && cs==-1.0) { // 180deg + NewCenter.setX( -Center.X() ); + NewCenter.setY( -Center.Y() ); + } else if (sn==-1.0 && cs==0.0) { // 270deg + NewCenter.setX( Center.Y() ); + NewCenter.setY( -Center.X() ); + } + else // Here it is rotated to any angle in the mathematically + // positive direction! + { // xnew = x * cos(alpha) - y * sin(alpha) + // ynew = x * sin(alpha) + y * cos(alpha) + // Bottom Right is not rotated: the pages of aOutRect must + // remain parallel to the coordinate axes. + NewCenter.setX( static_cast<long>(Center.X() * cs - Center.Y() * sn) ); + NewCenter.setY( static_cast<long>(Center.X() * sn + Center.Y() * cs) ); + } + + Size Differenz; + Point DiffPoint = NewCenter - Center; + Differenz.setWidth( DiffPoint.X() ); + Differenz.setHeight( -DiffPoint.Y() ); // Note that the Y-axis is counted ad positive downward. + NbcMove (Differenz); // Actually executes the coordinate transformation. +} + +OUString E3dScene::TakeObjNameSingul() const +{ + OUStringBuffer sName(SvxResId(STR_ObjNameSingulScene3d)); + + OUString aName(GetName()); + if (!aName.isEmpty()) + { + sName.append(' '); + sName.append('\''); + sName.append(aName); + sName.append('\''); + } + return sName.makeStringAndClear(); +} + +OUString E3dScene::TakeObjNamePlural() const +{ + return SvxResId(STR_ObjNamePluralScene3d); +} + +// The NbcRotate routine overrides the one of the SdrObject. The idea is +// to be able to rotate the scene relative to the position of the scene +// and then the objects in the scene + +void E3dScene::NbcSetTransform(const basegfx::B3DHomMatrix& rMatrix) +{ + if(maTransformation != rMatrix) + { + // call parent + E3dObject::NbcSetTransform(rMatrix); + } +} + +void E3dScene::SetTransform(const basegfx::B3DHomMatrix& rMatrix) +{ + if(rMatrix != maTransformation) + { + // call parent + E3dObject::SetTransform(rMatrix); + } +} + +void E3dScene::NbcRotate(const Point& rRef, long nAngle, double sn, double cs) +{ + // So currently the glue points are defined relative to the scene aOutRect. + // Before turning the glue points are defined relative to the page. They + // take no part in the rotation of the scene. To ensure this, there is the + // SetGlueReallyAbsolute(sal_True); + + // So that was the scene, now the objects used in the scene + // 3D objects, if there is only one it can still have multiple surfaces but + // the surfaces do not have to be connected. This allows you to access child + // objects. So going through the entire list and rotate around the Z axis + // through the enter of aOutRect's (Steiner's theorem), so RotateZ + + RotateScene (rRef, sn, cs); // Rotates the scene + double fAngleInRad = basegfx::deg2rad(nAngle/100.0); + + basegfx::B3DHomMatrix aRotation; + aRotation.rotate(0.0, 0.0, fAngleInRad); + NbcSetTransform(aRotation * GetTransform()); + + SetRectsDirty(); // This forces a recalculation of all BoundRects + NbcRotateGluePoints(rRef,nAngle,sn,cs); // Rotate the glue points (who still + // have coordinates relative to the + // original page) + SetGlueReallyAbsolute(false); // from now they are again relative to BoundRect (that is defined as aOutRect) + SetRectsDirty(); +} + +void E3dScene::RecalcSnapRect() +{ + E3dScene* pScene(getRootE3dSceneFromE3dObject()); + + if(pScene == this) + { + // The Scene is used as a 2D-Object, take the SnapRect from the + // 2D Display settings + maSnapRect = pScene->aCamera.GetDeviceWindow(); + } + else + { + // The Scene itself is a member of another scene, get the SnapRect + // as a composite object + // call parent + E3dObject::RecalcSnapRect(); + + for(size_t a = 0; a < GetObjCount(); ++a) + { + E3dObject* pCandidate(dynamic_cast< E3dObject* >(GetObj(a))); + + if(pCandidate) + { + maSnapRect.Union(pCandidate->GetSnapRect()); + } + } + } +} + +bool E3dScene::IsBreakObjPossible() +{ + // Break scene, if all members are able to break + SdrObjListIter a3DIterator(GetSubList(), SdrIterMode::DeepWithGroups); + + while ( a3DIterator.IsMore() ) + { + E3dObject* pObj = static_cast<E3dObject*>(a3DIterator.Next()); + DBG_ASSERT(dynamic_cast< const E3dObject*>(pObj), "only 3D objects are allowed in scenes!"); + if(!pObj->IsBreakObjPossible()) + return false; + } + + return true; +} + +basegfx::B2DPolyPolygon E3dScene::TakeCreatePoly(const SdrDragStat& /*rDrag*/) const +{ + return TakeXorPoly(); +} + +bool E3dScene::BegCreate(SdrDragStat& rStat) +{ + rStat.SetOrtho4Possible(); + tools::Rectangle aRect1(rStat.GetStart(), rStat.GetNow()); + aRect1.Justify(); + rStat.SetActionRect(aRect1); + NbcSetSnapRect(aRect1); + return true; +} + +bool E3dScene::MovCreate(SdrDragStat& rStat) +{ + tools::Rectangle aRect1; + rStat.TakeCreateRect(aRect1); + aRect1.Justify(); + rStat.SetActionRect(aRect1); + NbcSetSnapRect(aRect1); + SetBoundRectDirty(); + bSnapRectDirty=true; + return true; +} + +bool E3dScene::EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd) +{ + tools::Rectangle aRect1; + rStat.TakeCreateRect(aRect1); + aRect1.Justify(); + NbcSetSnapRect(aRect1); + SetRectsDirty(); + return (eCmd==SdrCreateCmd::ForceEnd || rStat.GetPointCount()>=2); +} + +bool E3dScene::BckCreate(SdrDragStat& /*rStat*/) +{ + return false; +} + +void E3dScene::BrkCreate(SdrDragStat& /*rStat*/) +{ +} + +void E3dScene::SetSelected(bool bNew) +{ + // call parent + E3dObject::SetSelected(bNew); + + for(size_t a(0); a < GetObjCount(); a++) + { + E3dObject* pCandidate(dynamic_cast< E3dObject* >(GetObj(a))); + + if(pCandidate) + { + pCandidate->SetSelected(bNew); + } + } +} + +void E3dScene::NbcInsertObject(SdrObject* pObj, size_t nPos) +{ + // Is it even a 3D object? + if(nullptr != dynamic_cast< const E3dObject* >(pObj)) + { + // Normal 3D object, insert means call parent + SdrObjList::NbcInsertObject(pObj, nPos); + + // local needed stuff + InvalidateBoundVolume(); + StructureChanged(); + } + else + { + // No 3D object, inserted a page in place in a scene ... + getSdrObjectFromSdrObjList()->getSdrPageFromSdrObject()->InsertObject(pObj, nPos); + } +} + +void E3dScene::InsertObject(SdrObject* pObj, size_t nPos) +{ + // Is it even a 3D object? + if(nullptr != dynamic_cast< const E3dObject* >(pObj)) + { + // call parent + SdrObjList::InsertObject(pObj, nPos); + + // local needed stuff + InvalidateBoundVolume(); + StructureChanged(); + } + else + { + // No 3D object, inserted a page in place in a scene ... + getSdrObjectFromSdrObjList()->getSdrPageFromSdrObject()->InsertObject(pObj, nPos); + } +} + +SdrObject* E3dScene::NbcRemoveObject(size_t nObjNum) +{ + // call parent + SdrObject* pRetval = SdrObjList::NbcRemoveObject(nObjNum); + + InvalidateBoundVolume(); + StructureChanged(); + + return pRetval; +} + +SdrObject* E3dScene::RemoveObject(size_t nObjNum) +{ + // call parent + SdrObject* pRetval(SdrObjList::RemoveObject(nObjNum)); + + InvalidateBoundVolume(); + StructureChanged(); + + return pRetval; +} + +void E3dScene::SetRectsDirty(bool bNotMyself, bool bRecursive) +{ + // call parent + E3dObject::SetRectsDirty(bNotMyself, bRecursive); + + for(size_t a = 0; a < GetObjCount(); ++a) + { + E3dObject* pCandidate = dynamic_cast< E3dObject* >(GetObj(a)); + + if(pCandidate) + { + pCandidate->SetRectsDirty(bNotMyself, false); + } + } +} + +void E3dScene::NbcSetLayer(SdrLayerID nLayer) +{ + // call parent + E3dObject::NbcSetLayer(nLayer); + + for(size_t a = 0; a < GetObjCount(); ++a) + { + E3dObject* pCandidate = dynamic_cast< E3dObject* >(GetObj(a)); + + if(pCandidate) + { + pCandidate->NbcSetLayer(nLayer); + } + } +} + +void E3dScene::handlePageChange(SdrPage* pOldPage, SdrPage* pNewPage) +{ + if(pOldPage != pNewPage) + { + // call parent + E3dObject::handlePageChange(pOldPage, pNewPage); + + for(size_t a(0); a < GetObjCount(); a++) + { + E3dObject* pCandidate = dynamic_cast< E3dObject* >(GetObj(a)); + + if(pCandidate) + { + pCandidate->handlePageChange(pOldPage, pNewPage); + } + else + { + OSL_ENSURE(false, "E3dScene::handlePageChange invalid object list (!)"); + } + } + } +} + +SdrObjList* E3dScene::GetSubList() const +{ + return const_cast< E3dScene* >(this); +} + +basegfx::B3DRange E3dScene::RecalcBoundVolume() const +{ + basegfx::B3DRange aRetval; + const size_t nObjCnt(GetObjCount()); + + for(size_t a = 0; a < nObjCnt; ++a) + { + const E3dObject* p3DObject = dynamic_cast< const E3dObject* >(GetObj(a)); + + if(p3DObject) + { + basegfx::B3DRange aLocalRange(p3DObject->GetBoundVolume()); + aLocalRange.transform(p3DObject->GetTransform()); + aRetval.expand(aLocalRange); + } + } + + return aRetval; +} + +void E3dScene::SetTransformChanged() +{ + // call parent + E3dObject::SetTransformChanged(); + + for(size_t a = 0; a < GetObjCount(); ++a) + { + E3dObject* pCandidate = dynamic_cast< E3dObject* >(GetObj(a)); + + if(pCandidate) + { + pCandidate->SetTransformChanged(); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/engine3d/sphere3d.cxx b/svx/source/engine3d/sphere3d.cxx new file mode 100644 index 000000000..e5e6939c1 --- /dev/null +++ b/svx/source/engine3d/sphere3d.cxx @@ -0,0 +1,153 @@ +/* -*- 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/strings.hrc> +#include <svx/deflt3d.hxx> +#include <svx/dialmgr.hxx> +#include <svx/svdmodel.hxx> +#include <svx/globl3d.hxx> +#include <svx/sphere3d.hxx> + +#include <sdr/properties/e3dsphereproperties.hxx> +#include <basegfx/vector/b3dvector.hxx> +#include <basegfx/point/b3dpoint.hxx> +#include <sdr/contact/viewcontactofe3dsphere.hxx> +#include <rtl/ustrbuf.hxx> + +// DrawContact section +std::unique_ptr<sdr::contact::ViewContact> E3dSphereObj::CreateObjectSpecificViewContact() +{ + return std::make_unique<sdr::contact::ViewContactOfE3dSphere>(*this); +} + +std::unique_ptr<sdr::properties::BaseProperties> E3dSphereObj::CreateObjectSpecificProperties() +{ + return std::make_unique<sdr::properties::E3dSphereProperties>(*this); +} + +// Build Sphere from polygon facets in latitude and longitude +E3dSphereObj::E3dSphereObj( + SdrModel& rSdrModel, + const E3dDefaultAttributes& rDefault, + const basegfx::B3DPoint& rCenter, + const basegfx::B3DVector& r3DSize) +: E3dCompoundObject(rSdrModel) +{ + // Set defaults + SetDefaultAttributes(rDefault); + + aCenter = rCenter; + aSize = r3DSize; +} + +E3dSphereObj::E3dSphereObj(SdrModel& rSdrModel) +: E3dCompoundObject(rSdrModel) +{ + // Set defaults + const E3dDefaultAttributes aDefault; + + SetDefaultAttributes(aDefault); +} + +E3dSphereObj::~E3dSphereObj() +{ +} + +void E3dSphereObj::SetDefaultAttributes(const E3dDefaultAttributes& rDefault) +{ + // Set defaults + aCenter = rDefault.GetDefaultSphereCenter(); + aSize = rDefault.GetDefaultSphereSize(); +} + +sal_uInt16 E3dSphereObj::GetObjIdentifier() const +{ + return E3D_SPHEREOBJ_ID; +} + +// Convert the object into a group object consisting of n polygons + +SdrObjectUniquePtr E3dSphereObj::DoConvertToPolyObj(bool /*bBezier*/, bool /*bAddText*/) const +{ + return nullptr; +} + +E3dSphereObj* E3dSphereObj::CloneSdrObject(SdrModel& rTargetModel) const +{ + return CloneHelper< E3dSphereObj >(rTargetModel); +} + +E3dSphereObj& E3dSphereObj::operator=(const E3dSphereObj& rObj) +{ + if( this == &rObj ) + return *this; + E3dCompoundObject::operator=(rObj); + + aCenter = rObj.aCenter; + aSize = rObj.aSize; + + return *this; +} + +// Set local parameters with geometry re-creating + +void E3dSphereObj::SetCenter(const basegfx::B3DPoint& rNew) +{ + if(aCenter != rNew) + { + aCenter = rNew; + ActionChanged(); + } +} + +void E3dSphereObj::SetSize(const basegfx::B3DVector& rNew) +{ + if(aSize != rNew) + { + aSize = rNew; + ActionChanged(); + } +} + +// Get the name of the object (singular) + +OUString E3dSphereObj::TakeObjNameSingul() const +{ + OUStringBuffer sName(SvxResId(STR_ObjNameSingulSphere3d)); + + OUString aName(GetName()); + if (!aName.isEmpty()) + { + sName.append(' '); + sName.append('\''); + sName.append(aName); + sName.append('\''); + } + return sName.makeStringAndClear(); +} + +// Get the name of the object (plural) + +OUString E3dSphereObj::TakeObjNamePlural() const +{ + return SvxResId(STR_ObjNamePluralSphere3d); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/engine3d/svx3ditems.cxx b/svx/source/engine3d/svx3ditems.cxx new file mode 100644 index 000000000..1468fb0ff --- /dev/null +++ b/svx/source/engine3d/svx3ditems.cxx @@ -0,0 +1,266 @@ +/* -*- 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/svx3ditems.hxx> +#include <com/sun/star/drawing/NormalsKind.hpp> +#include <com/sun/star/drawing/TextureProjectionMode.hpp> +#include <com/sun/star/drawing/TextureKind.hpp> +#include <com/sun/star/drawing/TextureMode.hpp> +#include <com/sun/star/drawing/ProjectionMode.hpp> +#include <com/sun/star/drawing/ShadeMode.hpp> + + +using namespace ::com::sun::star; + +// #i28528# +// Added extra Item (Bool) for chart2 to be able to show reduced line geometry + +Svx3DReducedLineGeometryItem::Svx3DReducedLineGeometryItem(bool bVal) +: SfxBoolItem(SDRATTR_3DOBJ_REDUCED_LINE_GEOMETRY, bVal) +{} + +Svx3DReducedLineGeometryItem* Svx3DReducedLineGeometryItem::Clone(SfxItemPool *) const +{ + return new Svx3DReducedLineGeometryItem(*this); +} + +Svx3DNormalsKindItem::Svx3DNormalsKindItem(sal_uInt16 nVal) +: SfxUInt16Item(SDRATTR_3DOBJ_NORMALS_KIND, nVal) +{} + +Svx3DTextureProjectionXItem::Svx3DTextureProjectionXItem(sal_uInt16 nVal) +: SfxUInt16Item(SDRATTR_3DOBJ_TEXTURE_PROJ_X, nVal) +{} + +Svx3DTextureProjectionYItem::Svx3DTextureProjectionYItem(sal_uInt16 nVal) +: SfxUInt16Item(SDRATTR_3DOBJ_TEXTURE_PROJ_Y, nVal) +{} + +Svx3DTextureKindItem::Svx3DTextureKindItem(sal_uInt16 nVal) +: SfxUInt16Item(SDRATTR_3DOBJ_TEXTURE_KIND, nVal) +{} + +Svx3DTextureModeItem::Svx3DTextureModeItem(sal_uInt16 nVal) +: SfxUInt16Item(SDRATTR_3DOBJ_TEXTURE_MODE, nVal) +{} + +Svx3DPerspectiveItem::Svx3DPerspectiveItem(ProjectionType nVal) +: SfxUInt16Item(SDRATTR_3DSCENE_PERSPECTIVE, static_cast<sal_uInt16>(nVal)) +{} + +Svx3DShadeModeItem::Svx3DShadeModeItem(sal_uInt16 nVal) +: SfxUInt16Item(SDRATTR_3DSCENE_SHADE_MODE, nVal) +{} + + +Svx3DSmoothNormalsItem::Svx3DSmoothNormalsItem(bool bVal) +: SfxBoolItem(SDRATTR_3DOBJ_SMOOTH_NORMALS, bVal) +{} + +Svx3DSmoothNormalsItem* Svx3DSmoothNormalsItem::Clone(SfxItemPool *) const +{ + return new Svx3DSmoothNormalsItem(*this); +} + +Svx3DSmoothLidsItem::Svx3DSmoothLidsItem(bool bVal) +: SfxBoolItem(SDRATTR_3DOBJ_SMOOTH_LIDS, bVal) +{} + +Svx3DSmoothLidsItem* Svx3DSmoothLidsItem::Clone(SfxItemPool *) const +{ + return new Svx3DSmoothLidsItem(*this); +} + +Svx3DCharacterModeItem::Svx3DCharacterModeItem(bool bVal) +: SfxBoolItem(SDRATTR_3DOBJ_CHARACTER_MODE, bVal) +{} + +Svx3DCharacterModeItem* Svx3DCharacterModeItem::Clone(SfxItemPool *) const +{ + return new Svx3DCharacterModeItem(*this); +} + +Svx3DCloseFrontItem::Svx3DCloseFrontItem(bool bVal) +: SfxBoolItem(SDRATTR_3DOBJ_CLOSE_FRONT, bVal) +{} + +Svx3DCloseFrontItem* Svx3DCloseFrontItem::Clone(SfxItemPool *) const +{ + return new Svx3DCloseFrontItem(*this); +} + +Svx3DCloseBackItem::Svx3DCloseBackItem(bool bVal) +: SfxBoolItem(SDRATTR_3DOBJ_CLOSE_BACK, bVal) +{} + +Svx3DCloseBackItem* Svx3DCloseBackItem::Clone(SfxItemPool *) const +{ + return new Svx3DCloseBackItem(*this); +} + +// Svx3DNormalsKindItem: use drawing::NormalsKind +bool Svx3DNormalsKindItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/) const +{ + rVal <<= static_cast<drawing::NormalsKind>(GetValue()); + return true; +} + +bool Svx3DNormalsKindItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/) +{ + drawing::NormalsKind eVar; + if(!(rVal >>= eVar)) + return false; + SetValue(static_cast<sal_Int16>(eVar)); + return true; +} + +Svx3DNormalsKindItem* Svx3DNormalsKindItem::Clone(SfxItemPool* /*pPool*/) const +{ + return new Svx3DNormalsKindItem(*this); +} + +// Svx3DTextureProjectionXItem: use drawing::TextureProjectionMode +bool Svx3DTextureProjectionXItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/) const +{ + rVal <<= static_cast<drawing::TextureProjectionMode>(GetValue()); + return true; +} + +bool Svx3DTextureProjectionXItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/) +{ + drawing::TextureProjectionMode eVar; + if(!(rVal >>= eVar)) + return false; + SetValue(static_cast<sal_Int16>(eVar)); + return true; +} + +Svx3DTextureProjectionXItem* Svx3DTextureProjectionXItem::Clone(SfxItemPool* /*pPool*/) const +{ + return new Svx3DTextureProjectionXItem(*this); +} + +// Svx3DTextureProjectionYItem: use drawing::TextureProjectionMode +bool Svx3DTextureProjectionYItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/) const +{ + rVal <<= static_cast<drawing::TextureProjectionMode>(GetValue()); + return true; +} + +bool Svx3DTextureProjectionYItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/) +{ + drawing::TextureProjectionMode eVar; + if(!(rVal >>= eVar)) + return false; + SetValue(static_cast<sal_Int16>(eVar)); + return true; +} + +Svx3DTextureProjectionYItem* Svx3DTextureProjectionYItem::Clone(SfxItemPool* /*pPool*/) const +{ + return new Svx3DTextureProjectionYItem(*this); +} + +// Svx3DTextureKindItem: use drawing::TextureKind +bool Svx3DTextureKindItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/) const +{ + rVal <<= static_cast<drawing::TextureKind>(GetValue()); + return true; +} + +bool Svx3DTextureKindItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/) +{ + drawing::TextureKind eVar; + if(!(rVal >>= eVar)) + return false; + SetValue(static_cast<sal_Int16>(eVar)); + return true; +} + +Svx3DTextureKindItem* Svx3DTextureKindItem::Clone(SfxItemPool* /*pPool*/) const +{ + return new Svx3DTextureKindItem(*this); +} + +// Svx3DTextureModeItem: use drawing:TextureMode +bool Svx3DTextureModeItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/) const +{ + rVal <<= static_cast<drawing::TextureMode>(GetValue()); + return true; +} + +bool Svx3DTextureModeItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/) +{ + drawing::TextureMode eVar; + if(!(rVal >>= eVar)) + return false; + SetValue(static_cast<sal_Int16>(eVar)); + return true; +} + +Svx3DTextureModeItem* Svx3DTextureModeItem::Clone(SfxItemPool* /*pPool*/) const +{ + return new Svx3DTextureModeItem(*this); +} + +// Svx3DPerspectiveItem: use drawing::ProjectionMode +bool Svx3DPerspectiveItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/) const +{ + rVal <<= static_cast<drawing::ProjectionMode>(GetValue()); + return true; +} + +bool Svx3DPerspectiveItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/) +{ + drawing::ProjectionMode eVar; + if(!(rVal >>= eVar)) + return false; + SetValue(static_cast<sal_Int16>(eVar)); + return true; +} + +Svx3DPerspectiveItem* Svx3DPerspectiveItem::Clone(SfxItemPool* /*pPool*/) const +{ + return new Svx3DPerspectiveItem(*this); +} + +// Svx3DShadeModeItem: use drawing::ShadeMode +bool Svx3DShadeModeItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/) const +{ + rVal <<= static_cast<drawing::ShadeMode>(GetValue()); + return true; +} + +bool Svx3DShadeModeItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/) +{ + drawing::ShadeMode eVar; + if(!(rVal >>= eVar)) + return false; + SetValue(static_cast<sal_Int16>(eVar)); + return true; +} + +Svx3DShadeModeItem* Svx3DShadeModeItem::Clone(SfxItemPool* /*pPool*/) const +{ + return new Svx3DShadeModeItem(*this); +} + +// EOF + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/engine3d/view3d.cxx b/svx/source/engine3d/view3d.cxx new file mode 100644 index 000000000..77d94ab1c --- /dev/null +++ b/svx/source/engine3d/view3d.cxx @@ -0,0 +1,1599 @@ +/* -*- 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> + +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()) + { + for(size_t a = 0; a < mnCount; ++a) + { + SdrObject* pObject = mrView.GetMarkedObjectByIndex(a); + + if(pObject) + { + // use the view-independent primitive representation (without + // evtl. GridOffset, that may be applied to the DragEntry individually) + const drawinglayer::primitive2d::Primitive2DContainer& aNewSequence( + pObject->GetViewContact().getViewIndependentPrimitive2DContainer()); + 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); + 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 + 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 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); + 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 + { + // 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, aPolyPoly2D); + } + + // Set attribute + 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).release(); + + 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) + SdrObject::Free( pNewObj2 ); + } + + // delete object in between + if (pNewObj1 != pObj) + 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 + 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(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 + { + // 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 + +namespace { + +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); + + 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) +{ + 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::XYZ; + 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()); + 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 () +{ + 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().release(); + if (pNewObj) + { + if (InsertObjectAtView(pNewObj, *GetSdrPageView(), SdrInsertFlags::DONTMARK)) + { + pNewObj->SetChanged(); + pNewObj->BroadcastObjectChange(); + } + } + } +} + +void E3dView::CheckPossibilities() +{ + // call parent + SdrView::CheckPossibilities(); + + // Set other flags + if(m_bGroupPossible || m_bUnGroupPossible || m_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(m_bGroupPossible && bCoumpound) + m_bGroupPossible = false; + + if(m_bUnGroupPossible && b3DObject) + m_bUnGroupPossible = false; + + if(m_bGrpEnterPossible && bCoumpound) + m_bGrpEnterPossible = false; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/engine3d/view3d1.cxx b/svx/source/engine3d/view3d1.cxx new file mode 100644 index 000000000..7f0acf713 --- /dev/null +++ b/svx/source/engine3d/view3d1.cxx @@ -0,0 +1,177 @@ +/* -*- 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/svdpool.hxx> +#include <svx/svdmodel.hxx> +#include <svx/svxids.hrc> +#include <svx/strings.hrc> +#include <svx/dialmgr.hxx> +#include <svx/lathe3d.hxx> +#include <svx/scene3d.hxx> +#include <svx/sphere3d.hxx> +#include <extrud3d.hxx> +#include <svx/view3d.hxx> +#include <svx/cube3d.hxx> +#include <svx/xlineit0.hxx> +#include <com/sun/star/drawing/LineStyle.hpp> + +void E3dView::ConvertMarkedToPolyObj() +{ + SdrObject* pNewObj = nullptr; + + if (GetMarkedObjectCount() == 1) + { + SdrObject* pObj = GetMarkedObjectByIndex(0); + + if (pObj) + { + auto pScene = dynamic_cast< const E3dScene* >(pObj); + if (pScene) + { + pNewObj = pScene->ConvertToPolyObj(false/*bBezier*/, false/*bLineToArea*/).release(); + if (pNewObj) + { + BegUndo(SvxResId(RID_SVX_3D_UNDO_EXTRUDE)); + ReplaceObjectAtView(pObj, *GetSdrPageView(), pNewObj); + EndUndo(); + } + } + } + } + + if (!pNewObj) + { + SdrView::ConvertMarkedToPolyObj(); + } +} + +static void Imp_E3dView_InorderRun3DObjects(const SdrObject* pObj, sal_uInt32& rMask) +{ + if(dynamic_cast< const E3dLatheObj* >(pObj) != nullptr) + { + rMask |= 0x0001; + } + else if(dynamic_cast< const E3dExtrudeObj* >(pObj) != nullptr) + { + rMask |= 0x0002; + } + else if(dynamic_cast< const E3dSphereObj* >(pObj) != nullptr) + { + rMask |= 0x0004; + } + else if(dynamic_cast< const E3dCubeObj* >(pObj) != nullptr) + { + rMask |= 0x0008; + } + else if(pObj->IsGroupObject()) + { + SdrObjList* pList = pObj->GetSubList(); + for(size_t a = 0; a < pList->GetObjCount(); ++a) + Imp_E3dView_InorderRun3DObjects(pList->GetObj(a), rMask); + } +} + +SfxItemSet E3dView::Get3DAttributes() const +{ + // Creating itemset with corresponding field + SfxItemSet aSet( + mpModel->GetItemPool(), + svl::Items<SDRATTR_START, SDRATTR_END, + SID_ATTR_3D_INTERN, SID_ATTR_3D_INTERN>{}); + + sal_uInt32 nSelectedItems(0); + + // get attributes from all selected objects + MergeAttrFromMarked(aSet, false); + + // calc flags for SID_ATTR_3D_INTERN + const SdrMarkList& rMarkList = GetMarkedObjectList(); + const size_t nMarkCnt(rMarkList.GetMarkCount()); + + for(size_t a = 0; a < nMarkCnt; ++a) + { + SdrObject* pObj = GetMarkedObjectByIndex(a); + Imp_E3dView_InorderRun3DObjects(pObj, nSelectedItems); + } + + // Set SID_ATTR_3D_INTERN on the status of the selected objects + aSet.Put(SfxUInt32Item(SID_ATTR_3D_INTERN, nSelectedItems)); + + // maintain default values + if(!nSelectedItems) + { + // Get defaults and apply + SfxItemSet aDefaultSet(mpModel->GetItemPool(), svl::Items<SDRATTR_3D_FIRST, SDRATTR_3D_LAST>{}); + GetAttributes(aDefaultSet); + aSet.Put(aDefaultSet); + + // ... but no lines for 3D + aSet.Put(XLineStyleItem (css::drawing::LineStyle_NONE)); + + // new defaults for distance and focal length + aSet.Put(makeSvx3DDistanceItem(100)); + aSet.Put(makeSvx3DFocalLengthItem(10000)); + } + + // return ItemSet + return aSet; +} + +void E3dView::Set3DAttributes( const SfxItemSet& rAttr) +{ + sal_uInt32 nSelectedItems(0); + + // #i94832# removed usage of E3DModifySceneSnapRectUpdater here. + // They are not needed here, they are already handled in SetAttrToMarked + + // set at selected objects + SetAttrToMarked(rAttr, false/*bReplaceAll*/); + + // old run + const SdrMarkList& rMarkList = GetMarkedObjectList(); + const size_t nMarkCnt(rMarkList.GetMarkCount()); + + for(size_t a = 0; a < nMarkCnt; ++a) + { + SdrObject* pObj = GetMarkedObjectByIndex(a); + Imp_E3dView_InorderRun3DObjects(pObj, nSelectedItems); + } + + // Maintain default values + if(!nSelectedItems) + { + // Set defaults + SfxItemSet aDefaultSet(mpModel->GetItemPool(), svl::Items<SDRATTR_3D_FIRST, SDRATTR_3D_LAST>{}); + aDefaultSet.Put(rAttr); + SetAttributes(aDefaultSet); + } +} + +double E3dView::GetDefaultCamPosZ() +{ + return static_cast<double>(mpModel->GetItemPool().GetDefaultItem(SDRATTR_3DSCENE_DISTANCE).GetValue()); +} + +double E3dView::GetDefaultCamFocal() +{ + return static_cast<double>(mpModel->GetItemPool().GetDefaultItem(SDRATTR_3DSCENE_FOCAL_LENGTH).GetValue()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/engine3d/viewpt3d2.cxx b/svx/source/engine3d/viewpt3d2.cxx new file mode 100644 index 000000000..660af6adf --- /dev/null +++ b/svx/source/engine3d/viewpt3d2.cxx @@ -0,0 +1,153 @@ +/* -*- 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/viewpt3d.hxx> + +Viewport3D::Viewport3D() : + aVRP(0, 0, 5), + aVPN(0, 0, 1), + aVUV(0, 1, 1), + aPRP(0, 0, 2), + eProjection(ProjectionType::Perspective), + aDeviceRect(Point(0,0), Size(-1,-1)), + aViewPoint (0, 0, 5000), + bTfValid(false) +{ + aViewWin.X = -1; aViewWin.Y = -1; + aViewWin.W = 2; aViewWin.H = 2; +} + +// Set ViewWindow (in View coordinates) + +void Viewport3D::SetViewWindow(double fX, double fY, double fW, double fH) +{ + aViewWin.X = fX; + aViewWin.Y = fY; + if ( fW > 0 ) aViewWin.W = fW; + else aViewWin.W = 1.0; + if ( fH > 0 ) aViewWin.H = fH; + else aViewWin.H = 1.0; +} + +// Returns observer position (PRP) in world coordinates + +const basegfx::B3DPoint& Viewport3D::GetViewPoint() +{ + // Calculate View transformations matrix + if ( !bTfValid ) + { + double fV, fXupVp, fYupVp; + aViewPoint = aVRP + aVPN * aPRP.getZ(); + + // Reset to Identity matrix + aViewTf.identity(); + + // shift in the origin + aViewTf.translate(-aVRP.getX(), -aVRP.getY(), -aVRP.getZ()); + + // fV = Length of the projection of aVPN on the yz plane: + fV = aVPN.getYZLength(); + + if ( fV != 0 ) + { + basegfx::B3DHomMatrix aTemp; + const double fSin(aVPN.getY() / fV); + const double fCos(aVPN.getZ() / fV); + aTemp.set(2, 2, fCos); + aTemp.set(1, 1, fCos); + aTemp.set(2, 1, fSin); + aTemp.set(1, 2, -fSin); + aViewTf *= aTemp; + } + + { + basegfx::B3DHomMatrix aTemp; + const double fSin(-aVPN.getX()); + const double fCos(fV); + aTemp.set(2, 2, fCos); + aTemp.set(0, 0, fCos); + aTemp.set(0, 2, fSin); + aTemp.set(2, 0, -fSin); + aViewTf *= aTemp; + } + + // Convert X- and Y- coordinates of the view up vector to the + // (preliminary) view coordinate system. + fXupVp = aViewTf.get(0, 0) * aVUV.getX() + aViewTf.get(0, 1) * aVUV.getY() + aViewTf.get(0, 2) * aVUV.getZ(); + fYupVp = aViewTf.get(1, 0) * aVUV.getX() + aViewTf.get(1, 1) * aVUV.getY() + aViewTf.get(1, 2) * aVUV.getZ(); + fV = sqrt(fXupVp * fXupVp + fYupVp * fYupVp); + + if ( fV != 0 ) + { + basegfx::B3DHomMatrix aTemp; + const double fSin(fXupVp / fV); + const double fCos(fYupVp / fV); + aTemp.set(1, 1, fCos); + aTemp.set(0, 0, fCos); + aTemp.set(1, 0, fSin); + aTemp.set(0, 1, -fSin); + aViewTf *= aTemp; + } + + bTfValid = true; + } + return aViewPoint; +} + +void Viewport3D::SetDeviceWindow(const tools::Rectangle& rRect) +{ + aDeviceRect = rRect; +} + +// Set View Reference Point + +void Viewport3D::SetVRP(const basegfx::B3DPoint& rNewVRP) +{ + aVRP = rNewVRP; + bTfValid = false; +} + +// Set View Plane Normal + +void Viewport3D::SetVPN(const basegfx::B3DVector& rNewVPN) +{ + aVPN = rNewVPN; + aVPN.normalize(); + bTfValid = false; +} + +// Set View Up Vector + +void Viewport3D::SetVUV(const basegfx::B3DVector& rNewVUV) +{ + aVUV = rNewVUV; + bTfValid = false; +} + +// Set Center Of Projection + +void Viewport3D::SetPRP(const basegfx::B3DPoint& rNewPRP) +{ + aPRP = rNewPRP; + aPRP.setX(0.0); + aPRP.setY(0.0); + bTfValid = false; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |