1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <svx/helperhittest3d.hxx>
22 #include <basegfx/point/b2dpoint.hxx>
23 #include <svx/svdpage.hxx>
24 #include <svx/scene3d.hxx>
25 #include <svx/svditer.hxx>
26 #include <drawinglayer/processor3d/cutfindprocessor3d.hxx>
27 #include <sdr/contact/viewcontactofe3d.hxx>
28 #include <svx/sdr/contact/viewcontactofe3dscene.hxx>
29 #include <com/sun/star/uno/Sequence.h>
32 using namespace com::sun::star
;
36 class ImplPairDephAndObject
39 const E3dCompoundObject
* mpObject
;
43 ImplPairDephAndObject(const E3dCompoundObject
* pObject
, double fDepth
)
49 bool operator<(const ImplPairDephAndObject
& rComp
) const
51 return (mfDepth
< rComp
.mfDepth
);
55 const E3dCompoundObject
* getObject() const { return mpObject
; }
60 static void getAllHit3DObjectWithRelativePoint(
61 const basegfx::B3DPoint
& rFront
,
62 const basegfx::B3DPoint
& rBack
,
63 const E3dCompoundObject
& rObject
,
64 const drawinglayer::geometry::ViewInformation3D
& rObjectViewInformation3D
,
65 ::std::vector
< basegfx::B3DPoint
>& o_rResult
,
70 if(rFront
.equal(rBack
))
73 // rObject is an E3dCompoundObject, so it cannot be a scene (which is an E3dObject)
74 const sdr::contact::ViewContactOfE3d
& rVCObject
= static_cast< sdr::contact::ViewContactOfE3d
& >(rObject
.GetViewContact());
75 const drawinglayer::primitive3d::Primitive3DContainer
aPrimitives(rVCObject
.getViewIndependentPrimitive3DContainer());
77 if(aPrimitives
.empty())
80 // make BoundVolume empty and overlapping test for speedup
81 const basegfx::B3DRange
aObjectRange(aPrimitives
.getB3DRange(rObjectViewInformation3D
));
83 if(!aObjectRange
.isEmpty())
85 const basegfx::B3DRange
aFrontBackRange(rFront
, rBack
);
87 if(aObjectRange
.overlaps(aFrontBackRange
))
89 // bound volumes hit, geometric cut tests needed
90 drawinglayer::processor3d::CutFindProcessor
aCutFindProcessor(rObjectViewInformation3D
, rFront
, rBack
, bAnyHit
);
91 aCutFindProcessor
.process(aPrimitives
);
92 o_rResult
= aCutFindProcessor
.getCutPoints();
98 E3dScene
* fillViewInformation3DForCompoundObject(drawinglayer::geometry::ViewInformation3D
& o_rViewInformation3D
, const E3dCompoundObject
& rCandidate
)
100 // Search for root scene (outmost scene) of the 3d object since e.g. in chart, multiple scenes may
101 // be placed between object and outmost scene. On that search, remember the in-between scene's
102 // transformation for the correct complete ObjectTransformation. For historical reasons, the
103 // root scene's own object transformation is part of the scene's ViewTransformation, o do not
104 // add it. For more details, see ViewContactOfE3dScene::createViewInformation3D.
105 E3dScene
* pParentScene(rCandidate
.getParentE3dSceneFromE3dObject());
106 E3dScene
* pRootScene(nullptr);
107 basegfx::B3DHomMatrix aInBetweenSceneMatrix
;
111 E3dScene
* pParentParentScene(pParentScene
->getParentE3dSceneFromE3dObject());
113 if(pParentParentScene
)
115 // pParentScene is an in-between scene
116 aInBetweenSceneMatrix
= pParentScene
->GetTransform() * aInBetweenSceneMatrix
;
120 // pParentScene is the root scene
121 pRootScene
= pParentScene
;
124 pParentScene
= pParentParentScene
;
129 const sdr::contact::ViewContactOfE3dScene
& rVCScene
= static_cast< sdr::contact::ViewContactOfE3dScene
& >(pRootScene
->GetViewContact());
131 if(aInBetweenSceneMatrix
.isIdentity())
133 o_rViewInformation3D
= rVCScene
.getViewInformation3D();
137 // build new ViewInformation containing all transforms for the candidate
138 const drawinglayer::geometry::ViewInformation3D
& aViewInfo3D(rVCScene
.getViewInformation3D());
140 o_rViewInformation3D
= drawinglayer::geometry::ViewInformation3D(
141 aViewInfo3D
.getObjectTransformation() * aInBetweenSceneMatrix
,
142 aViewInfo3D
.getOrientation(),
143 aViewInfo3D
.getProjection(),
144 aViewInfo3D
.getDeviceToView(),
145 aViewInfo3D
.getViewTime(),
146 aViewInfo3D
.getExtendedInformationSequence());
151 const uno::Sequence
< beans::PropertyValue
> aEmptyParameters
;
152 o_rViewInformation3D
= drawinglayer::geometry::ViewInformation3D(aEmptyParameters
);
159 void getAllHit3DObjectsSortedFrontToBack(
160 const basegfx::B2DPoint
& rPoint
,
161 const E3dScene
& rScene
,
162 ::std::vector
< const E3dCompoundObject
* >& o_rResult
)
165 SdrObjList
* pList
= rScene
.GetSubList();
167 if(nullptr == pList
|| 0 == pList
->GetObjCount())
170 // prepare relative HitPoint. To do so, get the VC of the 3DScene and from there
171 // the Scene's 2D transformation. Multiplying with the inverse transformation
172 // will create a point relative to the 3D scene as unit-2d-object
173 const sdr::contact::ViewContactOfE3dScene
& rVCScene
= static_cast< sdr::contact::ViewContactOfE3dScene
& >(rScene
.GetViewContact());
174 basegfx::B2DHomMatrix
aInverseSceneTransform(rVCScene
.getObjectTransformation());
175 aInverseSceneTransform
.invert();
176 const basegfx::B2DPoint
aRelativePoint(aInverseSceneTransform
* rPoint
);
178 // check if test point is inside scene's area at all
179 if(!(aRelativePoint
.getX() >= 0.0 && aRelativePoint
.getX() <= 1.0 && aRelativePoint
.getY() >= 0.0 && aRelativePoint
.getY() <= 1.0))
182 SdrObjListIter
aIterator(pList
, SdrIterMode::DeepNoGroups
);
183 ::std::vector
< ImplPairDephAndObject
> aDepthAndObjectResults
;
184 const uno::Sequence
< beans::PropertyValue
> aEmptyParameters
;
185 drawinglayer::geometry::ViewInformation3D
aViewInfo3D(aEmptyParameters
);
187 while(aIterator
.IsMore())
189 const E3dCompoundObject
* pCandidate
= dynamic_cast< const E3dCompoundObject
* >(aIterator
.Next());
193 fillViewInformation3DForCompoundObject(aViewInfo3D
, *pCandidate
);
195 // create HitPoint Front and Back, transform to object coordinates
196 basegfx::B3DHomMatrix
aViewToObject(aViewInfo3D
.getObjectToView());
197 aViewToObject
.invert();
198 const basegfx::B3DPoint
aFront(aViewToObject
* basegfx::B3DPoint(aRelativePoint
.getX(), aRelativePoint
.getY(), 0.0));
199 const basegfx::B3DPoint
aBack(aViewToObject
* basegfx::B3DPoint(aRelativePoint
.getX(), aRelativePoint
.getY(), 1.0));
201 if(!aFront
.equal(aBack
))
203 // get all hit points with object
204 ::std::vector
< basegfx::B3DPoint
> aHitsWithObject
;
205 getAllHit3DObjectWithRelativePoint(aFront
, aBack
, *pCandidate
, aViewInfo3D
, aHitsWithObject
, false);
207 for(const basegfx::B3DPoint
& a
: aHitsWithObject
)
209 const basegfx::B3DPoint
aPointInViewCoordinates(aViewInfo3D
.getObjectToView() * a
);
210 aDepthAndObjectResults
.emplace_back(pCandidate
, aPointInViewCoordinates
.getZ());
217 const sal_uInt32
nCount(aDepthAndObjectResults
.size());
221 // sort aDepthAndObjectResults by depth
222 ::std::sort(aDepthAndObjectResults
.begin(), aDepthAndObjectResults
.end());
224 // copy SdrObject pointers to return result set
225 for(const auto& rResult
: aDepthAndObjectResults
)
227 o_rResult
.push_back(rResult
.getObject());
233 bool checkHitSingle3DObject(
234 const basegfx::B2DPoint
& rPoint
,
235 const E3dCompoundObject
& rCandidate
)
237 const uno::Sequence
< beans::PropertyValue
> aEmptyParameters
;
238 drawinglayer::geometry::ViewInformation3D
aViewInfo3D(aEmptyParameters
);
239 E3dScene
* pRootScene
= fillViewInformation3DForCompoundObject(aViewInfo3D
, rCandidate
);
243 // prepare relative HitPoint. To do so, get the VC of the 3DScene and from there
244 // the Scene's 2D transformation. Multiplying with the inverse transformation
245 // will create a point relative to the 3D scene as unit-2d-object
246 const sdr::contact::ViewContactOfE3dScene
& rVCScene
= static_cast< sdr::contact::ViewContactOfE3dScene
& >(pRootScene
->GetViewContact());
247 basegfx::B2DHomMatrix
aInverseSceneTransform(rVCScene
.getObjectTransformation());
248 aInverseSceneTransform
.invert();
249 const basegfx::B2DPoint
aRelativePoint(aInverseSceneTransform
* rPoint
);
251 // check if test point is inside scene's area at all
252 if(aRelativePoint
.getX() >= 0.0 && aRelativePoint
.getX() <= 1.0 && aRelativePoint
.getY() >= 0.0 && aRelativePoint
.getY() <= 1.0)
254 // create HitPoint Front and Back, transform to object coordinates
255 basegfx::B3DHomMatrix
aViewToObject(aViewInfo3D
.getObjectToView());
256 aViewToObject
.invert();
257 const basegfx::B3DPoint
aFront(aViewToObject
* basegfx::B3DPoint(aRelativePoint
.getX(), aRelativePoint
.getY(), 0.0));
258 const basegfx::B3DPoint
aBack(aViewToObject
* basegfx::B3DPoint(aRelativePoint
.getX(), aRelativePoint
.getY(), 1.0));
260 if(!aFront
.equal(aBack
))
262 // get all hit points with object
263 ::std::vector
< basegfx::B3DPoint
> aHitsWithObject
;
264 getAllHit3DObjectWithRelativePoint(aFront
, aBack
, rCandidate
, aViewInfo3D
, aHitsWithObject
, true);
266 if(!aHitsWithObject
.empty())
277 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */