diff options
Diffstat (limited to '')
-rw-r--r-- | svx/source/sdr/contact/viewcontactofe3dscene.cxx | 446 |
1 files changed, 446 insertions, 0 deletions
diff --git a/svx/source/sdr/contact/viewcontactofe3dscene.cxx b/svx/source/sdr/contact/viewcontactofe3dscene.cxx new file mode 100644 index 000000000..6bae55263 --- /dev/null +++ b/svx/source/sdr/contact/viewcontactofe3dscene.cxx @@ -0,0 +1,446 @@ +/* -*- 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/sdr/contact/viewcontactofe3dscene.hxx> +#include <svx/sdr/contact/viewobjectcontact.hxx> +#include <sdr/primitive2d/sdrattributecreator.hxx> +#include <sdr/contact/viewobjectcontactofe3dscene.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <basegfx/range/b3drange.hxx> +#include <drawinglayer/primitive3d/baseprimitive3d.hxx> +#include <sdr/contact/viewcontactofe3d.hxx> +#include <drawinglayer/primitive2d/sceneprimitive2d.hxx> +#include <drawinglayer/primitive3d/transformprimitive3d.hxx> +#include <drawinglayer/primitive2d/sdrdecompositiontools2d.hxx> + +using namespace com::sun::star; + +namespace { + +// pActiveVC is only true if ghosted is still activated and maybe needs to be switched off in this path +void createSubPrimitive3DVector( + const sdr::contact::ViewContact& rCandidate, + drawinglayer::primitive3d::Primitive3DContainer& o_rAllTarget, + drawinglayer::primitive3d::Primitive3DContainer* o_pVisibleTarget, + const SdrLayerIDSet* pVisibleSdrLayerIDSet, + const bool bTestSelectedVisibility) +{ + const sdr::contact::ViewContactOfE3dScene* pViewContactOfE3dScene = dynamic_cast< const sdr::contact::ViewContactOfE3dScene* >(&rCandidate); + + if(pViewContactOfE3dScene) + { + const sal_uInt32 nChildrenCount(rCandidate.GetObjectCount()); + + if(nChildrenCount) + { + // provide new collection sequences + drawinglayer::primitive3d::Primitive3DContainer aNewAllTarget; + drawinglayer::primitive3d::Primitive3DContainer aNewVisibleTarget; + + // add children recursively + for(sal_uInt32 a(0); a < nChildrenCount; a++) + { + createSubPrimitive3DVector( + rCandidate.GetViewContact(a), + aNewAllTarget, + o_pVisibleTarget ? &aNewVisibleTarget : nullptr, + pVisibleSdrLayerIDSet, + bTestSelectedVisibility); + } + + // create transform primitive for the created content combining content and transformtion + const drawinglayer::primitive3d::Primitive3DReference xReference(new drawinglayer::primitive3d::TransformPrimitive3D( + pViewContactOfE3dScene->GetE3dScene().GetTransform(), + aNewAllTarget)); + + // add created content to all target + o_rAllTarget.push_back(xReference); + + // add created content to visible target if exists + if(o_pVisibleTarget) + { + o_pVisibleTarget->push_back(xReference); + } + } + } + else + { + // access view independent representation of rCandidate + const sdr::contact::ViewContactOfE3d* pViewContactOfE3d = dynamic_cast< const sdr::contact::ViewContactOfE3d* >(&rCandidate); + + if(pViewContactOfE3d) + { + drawinglayer::primitive3d::Primitive3DContainer xPrimitive3DSeq(pViewContactOfE3d->getViewIndependentPrimitive3DContainer()); + + if(!xPrimitive3DSeq.empty()) + { + // add to all target vector + o_rAllTarget.append(xPrimitive3DSeq); + + if(o_pVisibleTarget) + { + // test visibility. Primitive is visible when both tests are true (AND) + bool bVisible(true); + + if(pVisibleSdrLayerIDSet) + { + // test layer visibility + const E3dObject& rE3dObject = pViewContactOfE3d->GetE3dObject(); + const SdrLayerID aLayerID(rE3dObject.GetLayer()); + + bVisible = pVisibleSdrLayerIDSet->IsSet(aLayerID); + } + + if(bVisible && bTestSelectedVisibility) + { + // test selected visibility (see 3D View's DrawMarkedObj implementation) + const E3dObject& rE3dObject = pViewContactOfE3d->GetE3dObject(); + + bVisible = rE3dObject.GetSelected(); + } + + if (bVisible) + { + // add to visible target vector + o_pVisibleTarget->append(xPrimitive3DSeq); + } + } + } + } + } +} + +} + +namespace sdr::contact { + +// Create an Object-Specific ViewObjectContact, set ViewContact and +// ObjectContact. Always needs to return something. +ViewObjectContact& ViewContactOfE3dScene::CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact) +{ + ViewObjectContact* pRetval = new ViewObjectContactOfE3dScene(rObjectContact, *this); + DBG_ASSERT(pRetval, "ViewContactOfE3dScene::CreateObjectSpecificViewObjectContact() failed (!)"); + + return *pRetval; +} + +ViewContactOfE3dScene::ViewContactOfE3dScene(E3dScene& rScene) +: ViewContactOfSdrObj(rScene) +{ +} + +void ViewContactOfE3dScene::createViewInformation3D(const basegfx::B3DRange& rContentRange) +{ + basegfx::B3DHomMatrix aTransformation; + basegfx::B3DHomMatrix aOrientation; + basegfx::B3DHomMatrix aProjection; + basegfx::B3DHomMatrix aDeviceToView; + + // create transformation (scene as group's transformation) + // For historical reasons, the outmost scene's transformation is handles as part of the + // view transformation. This means that the BoundRect of the contained 3D Objects is + // without that transformation and makes it necessary to NOT add the first scene to the + // Primitive3DContainer of contained objects. + { + aTransformation = GetE3dScene().GetTransform(); + } + + // create orientation (world to camera coordinate system) + { + // calculate orientation from VRP, VPN and VUV + const B3dCamera& rSceneCamera = GetE3dScene().GetCameraSet(); + const basegfx::B3DPoint& aVRP(rSceneCamera.GetVRP()); + const basegfx::B3DVector& aVPN(rSceneCamera.GetVPN()); + const basegfx::B3DVector& aVUV(rSceneCamera.GetVUV()); + + aOrientation.orientation(aVRP, aVPN, aVUV); + } + + // create projection (camera coordinate system to relative 2d where X,Y and Z are [0.0 .. 1.0]) + { + const basegfx::B3DHomMatrix aWorldToCamera(aOrientation * aTransformation); + basegfx::B3DRange aCameraRange(rContentRange); + aCameraRange.transform(aWorldToCamera); + + // remember Z-Values, but change orientation + const double fMinZ(-aCameraRange.getMaxZ()); + const double fMaxZ(-aCameraRange.getMinZ()); + + // construct temporary matrix from world to device. Use unit values here to measure expansion + basegfx::B3DHomMatrix aWorldToDevice(aWorldToCamera); + const drawinglayer::attribute::SdrSceneAttribute& rSdrSceneAttribute = getSdrSceneAttribute(); + + if(css::drawing::ProjectionMode_PERSPECTIVE == rSdrSceneAttribute.getProjectionMode()) + { + aWorldToDevice.frustum(-1.0, 1.0, -1.0, 1.0, fMinZ, fMaxZ); + } + else + { + aWorldToDevice.ortho(-1.0, 1.0, -1.0, 1.0, fMinZ, fMaxZ); + } + + // create B3DRange in device. This will create the real used ranges + // in camera space. Do not use the Z-Values, though. + basegfx::B3DRange aDeviceRange(rContentRange); + aDeviceRange.transform(aWorldToDevice); + + // set projection + if(css::drawing::ProjectionMode_PERSPECTIVE == rSdrSceneAttribute.getProjectionMode()) + { + aProjection.frustum( + aDeviceRange.getMinX(), aDeviceRange.getMaxX(), + aDeviceRange.getMinY(), aDeviceRange.getMaxY(), + fMinZ, fMaxZ); + } + else + { + aProjection.ortho( + aDeviceRange.getMinX(), aDeviceRange.getMaxX(), + aDeviceRange.getMinY(), aDeviceRange.getMaxY(), + fMinZ, fMaxZ); + } + } + + // create device to view transform + { + // create standard deviceToView projection for geometry + // input is [-1.0 .. 1.0] in X,Y and Z. bring to [0.0 .. 1.0]. Also + // necessary to flip Y due to screen orientation + // Z is not needed, but will also be brought to [0.0 .. 1.0] + aDeviceToView.scale(0.5, -0.5, 0.5); + aDeviceToView.translate(0.5, 0.5, 0.5); + } + + const uno::Sequence< beans::PropertyValue > aEmptyProperties; + maViewInformation3D = drawinglayer::geometry::ViewInformation3D( + aTransformation, aOrientation, aProjection, + aDeviceToView, 0.0, aEmptyProperties); +} + +void ViewContactOfE3dScene::createObjectTransformation() +{ + // create 2d Object Transformation from relative point in 2d scene to world + const tools::Rectangle aRectangle(GetE3dScene().GetSnapRect()); + + maObjectTransformation.set(0, 0, aRectangle.getWidth()); + maObjectTransformation.set(1, 1, aRectangle.getHeight()); + maObjectTransformation.set(0, 2, aRectangle.Left()); + maObjectTransformation.set(1, 2, aRectangle.Top()); +} + +void ViewContactOfE3dScene::createSdrSceneAttribute() +{ + const SfxItemSet& rItemSet = GetE3dScene().GetMergedItemSet(); + maSdrSceneAttribute = drawinglayer::primitive2d::createNewSdrSceneAttribute(rItemSet); +} + +void ViewContactOfE3dScene::createSdrLightingAttribute() +{ + const SfxItemSet& rItemSet = GetE3dScene().GetMergedItemSet(); + maSdrLightingAttribute = drawinglayer::primitive2d::createNewSdrLightingAttribute(rItemSet); +} + +drawinglayer::primitive2d::Primitive2DContainer ViewContactOfE3dScene::createScenePrimitive2DSequence( + const SdrLayerIDSet* pLayerVisibility) const +{ + drawinglayer::primitive2d::Primitive2DContainer xRetval; + const sal_uInt32 nChildrenCount(GetObjectCount()); + + if(nChildrenCount) + { + // create 3d scene primitive with visible content tested against rLayerVisibility + drawinglayer::primitive3d::Primitive3DContainer aAllSequence; + drawinglayer::primitive3d::Primitive3DContainer aVisibleSequence; + const bool bTestLayerVisibility(nullptr != pLayerVisibility); + const bool bTestSelectedVisibility(GetE3dScene().GetDrawOnlySelected()); + const bool bTestVisibility(bTestLayerVisibility || bTestSelectedVisibility); + + // add children recursively. Do NOT start with (*this), this would create + // a 3D transformPrimitive for the start scene. While this is theoretically not + // a bad thing, for historical reasons the transformation of the outmost scene + // is seen as part of the ViewTransformation (see text in createViewInformation3D) + for(sal_uInt32 a(0); a < nChildrenCount; a++) + { + createSubPrimitive3DVector( + GetViewContact(a), + aAllSequence, + bTestLayerVisibility ? &aVisibleSequence : nullptr, + bTestLayerVisibility ? pLayerVisibility : nullptr, + bTestSelectedVisibility); + } + + const size_t nAllSize(!aAllSequence.empty() ? aAllSequence.size() : 0); + const size_t nVisibleSize(!aVisibleSequence.empty() ? aVisibleSequence.size() : 0); + + if((bTestVisibility && nVisibleSize) || nAllSize) + { + // for getting the 3D range using getB3DRangeFromPrimitive3DContainer a ViewInformation3D + // needs to be given for evtl. decompositions. At the same time createViewInformation3D + // currently is based on creating the target-ViewInformation3D using a given range. To + // get the true range, use a neutral ViewInformation3D here. This leaves all matrices + // on identity and the time on 0.0. + const uno::Sequence< beans::PropertyValue > aEmptyProperties; + const drawinglayer::geometry::ViewInformation3D aNeutralViewInformation3D(aEmptyProperties); + const basegfx::B3DRange aContentRange(aAllSequence.getB3DRange(aNeutralViewInformation3D)); + + // create 2d primitive 3dscene with generated sub-list from collector + const drawinglayer::primitive2d::Primitive2DReference xReference( + new drawinglayer::primitive2d::ScenePrimitive2D( + bTestVisibility ? aVisibleSequence : aAllSequence, + getSdrSceneAttribute(), + getSdrLightingAttribute(), + getObjectTransformation(), + getViewInformation3D(aContentRange))); + + xRetval = drawinglayer::primitive2d::Primitive2DContainer{ xReference }; + } + } + + // always append an invisible outline for the cases where no visible content exists + xRetval.push_back( + drawinglayer::primitive2d::createHiddenGeometryPrimitives2D( + getObjectTransformation())); + + return xRetval; +} + +void ViewContactOfE3dScene::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const +{ + if(GetObjectCount()) + { + // create a default ScenePrimitive2D (without visibility test of members) + rVisitor.visit(createScenePrimitive2DSequence(nullptr)); + } +} + +void ViewContactOfE3dScene::ActionChanged() +{ + // call parent + ViewContactOfSdrObj::ActionChanged(); + + // mark locally cached values as invalid + maViewInformation3D = drawinglayer::geometry::ViewInformation3D(); + maObjectTransformation.identity(); + maSdrSceneAttribute = drawinglayer::attribute::SdrSceneAttribute(); + maSdrLightingAttribute = drawinglayer::attribute::SdrLightingAttribute(); +} + +const drawinglayer::geometry::ViewInformation3D& ViewContactOfE3dScene::getViewInformation3D() const +{ + if(maViewInformation3D.isDefault()) + { + // this version will create the content range on demand locally and thus is less + // performant than the other one. Since the information is buffered the planned + // behaviour is that the version with the given range is used initially. + basegfx::B3DRange aContentRange(getAllContentRange3D()); + + if(aContentRange.isEmpty()) + { + // empty scene, no 3d action should be necessary. Prepare some + // fallback size + OSL_FAIL("No need to get ViewInformation3D from an empty scene (!)"); + aContentRange.expand(basegfx::B3DPoint(-100.0, -100.0, -100.0)); + aContentRange.expand(basegfx::B3DPoint( 100.0, 100.0, 100.0)); + } + + const_cast < ViewContactOfE3dScene* >(this)->createViewInformation3D(aContentRange); + } + + return maViewInformation3D; +} + +const drawinglayer::geometry::ViewInformation3D& ViewContactOfE3dScene::getViewInformation3D(const basegfx::B3DRange& rContentRange) const +{ + if(maViewInformation3D.isDefault()) + { + const_cast < ViewContactOfE3dScene* >(this)->createViewInformation3D(rContentRange); + } + + return maViewInformation3D; +} + +const basegfx::B2DHomMatrix& ViewContactOfE3dScene::getObjectTransformation() const +{ + if(maObjectTransformation.isIdentity()) + { + const_cast < ViewContactOfE3dScene* >(this)->createObjectTransformation(); + } + + return maObjectTransformation; +} + +const drawinglayer::attribute::SdrSceneAttribute& ViewContactOfE3dScene::getSdrSceneAttribute() const +{ + if(maSdrSceneAttribute.isDefault()) + { + const_cast < ViewContactOfE3dScene* >(this)->createSdrSceneAttribute(); + } + + return maSdrSceneAttribute; +} + +const drawinglayer::attribute::SdrLightingAttribute& ViewContactOfE3dScene::getSdrLightingAttribute() const +{ + if(maSdrLightingAttribute.isDefault()) + { + const_cast < ViewContactOfE3dScene* >(this)->createSdrLightingAttribute(); + } + + return maSdrLightingAttribute; +} + +drawinglayer::primitive3d::Primitive3DContainer ViewContactOfE3dScene::getAllPrimitive3DContainer() const +{ + drawinglayer::primitive3d::Primitive3DContainer aAllPrimitive3DContainer; + const sal_uInt32 nChildrenCount(GetObjectCount()); + + // add children recursively. Do NOT start with (*this), this would create + // a 3D transformPrimitive for the start scene. While this is theoretically not + // a bad thing, for historical reasons the transformation of the outmost scene + // is seen as part of the ViewTransformation (see text in createViewInformation3D) + for(sal_uInt32 a(0); a < nChildrenCount; a++) + { + createSubPrimitive3DVector(GetViewContact(a), aAllPrimitive3DContainer, nullptr, nullptr, false); + } + + return aAllPrimitive3DContainer; +} + +basegfx::B3DRange ViewContactOfE3dScene::getAllContentRange3D() const +{ + const drawinglayer::primitive3d::Primitive3DContainer xAllSequence(getAllPrimitive3DContainer()); + basegfx::B3DRange aAllContentRange3D; + + if(!xAllSequence.empty()) + { + // for getting the 3D range using getB3DRangeFromPrimitive3DContainer a ViewInformation3D + // needs to be given for evtl. decompositions. Use a neutral ViewInformation3D here. This + // leaves all matrices on identity and the time on 0.0. + const uno::Sequence< beans::PropertyValue > aEmptyProperties; + const drawinglayer::geometry::ViewInformation3D aNeutralViewInformation3D(aEmptyProperties); + + aAllContentRange3D = xAllSequence.getB3DRange(aNeutralViewInformation3D); + } + + return aAllContentRange3D; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |