/src/libreoffice/svx/source/engine3d/helperhittest3d.cxx
Line | Count | Source |
1 | | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | /* |
3 | | * This file is part of the LibreOffice project. |
4 | | * |
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/. |
8 | | * |
9 | | * This file incorporates work covered by the following license notice: |
10 | | * |
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 . |
18 | | */ |
19 | | |
20 | | |
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> |
30 | | |
31 | | |
32 | | using namespace com::sun::star; |
33 | | |
34 | | namespace { |
35 | | |
36 | | class ImplPairDephAndObject |
37 | | { |
38 | | private: |
39 | | const E3dCompoundObject* mpObject; |
40 | | double mfDepth; |
41 | | |
42 | | public: |
43 | | ImplPairDephAndObject(const E3dCompoundObject* pObject, double fDepth) |
44 | 0 | : mpObject(pObject), |
45 | 0 | mfDepth(fDepth) |
46 | 0 | {} |
47 | | |
48 | | // for ::std::sort |
49 | | bool operator<(const ImplPairDephAndObject& rComp) const |
50 | 0 | { |
51 | 0 | return (mfDepth < rComp.mfDepth); |
52 | 0 | } |
53 | | |
54 | | // data read access |
55 | 0 | const E3dCompoundObject* getObject() const { return mpObject; } |
56 | | }; |
57 | | |
58 | | } |
59 | | |
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, |
66 | | bool bAnyHit) |
67 | 0 | { |
68 | 0 | o_rResult.clear(); |
69 | |
|
70 | 0 | if(rFront.equal(rBack)) |
71 | 0 | return; |
72 | | |
73 | | // rObject is an E3dCompoundObject, so it cannot be a scene (which is an E3dObject) |
74 | 0 | const sdr::contact::ViewContactOfE3d& rVCObject = static_cast< sdr::contact::ViewContactOfE3d& >(rObject.GetViewContact()); |
75 | 0 | const drawinglayer::primitive3d::Primitive3DContainer aPrimitives(rVCObject.getViewIndependentPrimitive3DContainer()); |
76 | |
|
77 | 0 | if(aPrimitives.empty()) |
78 | 0 | return; |
79 | | |
80 | | // make BoundVolume empty and overlapping test for speedup |
81 | 0 | const basegfx::B3DRange aObjectRange(aPrimitives.getB3DRange(rObjectViewInformation3D)); |
82 | |
|
83 | 0 | if(!aObjectRange.isEmpty()) |
84 | 0 | { |
85 | 0 | const basegfx::B3DRange aFrontBackRange(rFront, rBack); |
86 | |
|
87 | 0 | if(aObjectRange.overlaps(aFrontBackRange)) |
88 | 0 | { |
89 | | // bound volumes hit, geometric cut tests needed |
90 | 0 | drawinglayer::processor3d::CutFindProcessor aCutFindProcessor(rObjectViewInformation3D, rFront, rBack, bAnyHit); |
91 | 0 | aCutFindProcessor.process(aPrimitives); |
92 | 0 | o_rResult = aCutFindProcessor.getCutPoints(); |
93 | 0 | } |
94 | 0 | } |
95 | 0 | } |
96 | | |
97 | | |
98 | | E3dScene* fillViewInformation3DForCompoundObject(drawinglayer::geometry::ViewInformation3D& o_rViewInformation3D, const E3dCompoundObject& rCandidate) |
99 | 0 | { |
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 | 0 | E3dScene* pParentScene(rCandidate.getParentE3dSceneFromE3dObject()); |
106 | 0 | E3dScene* pRootScene(nullptr); |
107 | 0 | basegfx::B3DHomMatrix aInBetweenSceneMatrix; |
108 | |
|
109 | 0 | while(pParentScene) |
110 | 0 | { |
111 | 0 | E3dScene* pParentParentScene(pParentScene->getParentE3dSceneFromE3dObject()); |
112 | |
|
113 | 0 | if(pParentParentScene) |
114 | 0 | { |
115 | | // pParentScene is an in-between scene |
116 | 0 | aInBetweenSceneMatrix = pParentScene->GetTransform() * aInBetweenSceneMatrix; |
117 | 0 | } |
118 | 0 | else |
119 | 0 | { |
120 | | // pParentScene is the root scene |
121 | 0 | pRootScene = pParentScene; |
122 | 0 | } |
123 | |
|
124 | 0 | pParentScene = pParentParentScene; |
125 | 0 | } |
126 | |
|
127 | 0 | if(pRootScene) |
128 | 0 | { |
129 | 0 | const sdr::contact::ViewContactOfE3dScene& rVCScene = static_cast< sdr::contact::ViewContactOfE3dScene& >(pRootScene->GetViewContact()); |
130 | |
|
131 | 0 | if(aInBetweenSceneMatrix.isIdentity()) |
132 | 0 | { |
133 | 0 | o_rViewInformation3D = rVCScene.getViewInformation3D(); |
134 | 0 | } |
135 | 0 | else |
136 | 0 | { |
137 | | // build new ViewInformation containing all transforms for the candidate |
138 | 0 | const drawinglayer::geometry::ViewInformation3D& aViewInfo3D(rVCScene.getViewInformation3D()); |
139 | |
|
140 | 0 | o_rViewInformation3D = drawinglayer::geometry::ViewInformation3D( |
141 | 0 | aViewInfo3D.getObjectTransformation() * aInBetweenSceneMatrix, |
142 | 0 | aViewInfo3D.getOrientation(), |
143 | 0 | aViewInfo3D.getProjection(), |
144 | 0 | aViewInfo3D.getDeviceToView(), |
145 | 0 | aViewInfo3D.getViewTime(), |
146 | 0 | aViewInfo3D.getExtendedInformationSequence()); |
147 | 0 | } |
148 | 0 | } |
149 | 0 | else |
150 | 0 | { |
151 | 0 | const uno::Sequence< beans::PropertyValue > aEmptyParameters; |
152 | 0 | o_rViewInformation3D = drawinglayer::geometry::ViewInformation3D(aEmptyParameters); |
153 | 0 | } |
154 | |
|
155 | 0 | return pRootScene; |
156 | 0 | } |
157 | | |
158 | | |
159 | | void getAllHit3DObjectsSortedFrontToBack( |
160 | | const basegfx::B2DPoint& rPoint, |
161 | | const E3dScene& rScene, |
162 | | ::std::vector< const E3dCompoundObject* >& o_rResult) |
163 | 0 | { |
164 | 0 | o_rResult.clear(); |
165 | 0 | SdrObjList* pList = rScene.GetSubList(); |
166 | |
|
167 | 0 | if(nullptr == pList || 0 == pList->GetObjCount()) |
168 | 0 | return; |
169 | | |
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 | 0 | const sdr::contact::ViewContactOfE3dScene& rVCScene = static_cast< sdr::contact::ViewContactOfE3dScene& >(rScene.GetViewContact()); |
174 | 0 | basegfx::B2DHomMatrix aInverseSceneTransform(rVCScene.getObjectTransformation()); |
175 | 0 | aInverseSceneTransform.invert(); |
176 | 0 | const basegfx::B2DPoint aRelativePoint(aInverseSceneTransform * rPoint); |
177 | | |
178 | | // check if test point is inside scene's area at all |
179 | 0 | if(!(aRelativePoint.getX() >= 0.0 && aRelativePoint.getX() <= 1.0 && aRelativePoint.getY() >= 0.0 && aRelativePoint.getY() <= 1.0)) |
180 | 0 | return; |
181 | | |
182 | 0 | SdrObjListIter aIterator(pList, SdrIterMode::DeepNoGroups); |
183 | 0 | ::std::vector< ImplPairDephAndObject > aDepthAndObjectResults; |
184 | 0 | const uno::Sequence< beans::PropertyValue > aEmptyParameters; |
185 | 0 | drawinglayer::geometry::ViewInformation3D aViewInfo3D(aEmptyParameters); |
186 | |
|
187 | 0 | while(aIterator.IsMore()) |
188 | 0 | { |
189 | 0 | const E3dCompoundObject* pCandidate = dynamic_cast< const E3dCompoundObject* >(aIterator.Next()); |
190 | |
|
191 | 0 | if(pCandidate) |
192 | 0 | { |
193 | 0 | fillViewInformation3DForCompoundObject(aViewInfo3D, *pCandidate); |
194 | | |
195 | | // create HitPoint Front and Back, transform to object coordinates |
196 | 0 | basegfx::B3DHomMatrix aViewToObject(aViewInfo3D.getObjectToView()); |
197 | 0 | aViewToObject.invert(); |
198 | 0 | const basegfx::B3DPoint aFront(aViewToObject * basegfx::B3DPoint(aRelativePoint.getX(), aRelativePoint.getY(), 0.0)); |
199 | 0 | const basegfx::B3DPoint aBack(aViewToObject * basegfx::B3DPoint(aRelativePoint.getX(), aRelativePoint.getY(), 1.0)); |
200 | |
|
201 | 0 | if(!aFront.equal(aBack)) |
202 | 0 | { |
203 | | // get all hit points with object |
204 | 0 | ::std::vector< basegfx::B3DPoint > aHitsWithObject; |
205 | 0 | getAllHit3DObjectWithRelativePoint(aFront, aBack, *pCandidate, aViewInfo3D, aHitsWithObject, false); |
206 | |
|
207 | 0 | for(const basegfx::B3DPoint & a : aHitsWithObject) |
208 | 0 | { |
209 | 0 | const basegfx::B3DPoint aPointInViewCoordinates(aViewInfo3D.getObjectToView() * a); |
210 | 0 | aDepthAndObjectResults.emplace_back(pCandidate, aPointInViewCoordinates.getZ()); |
211 | 0 | } |
212 | 0 | } |
213 | 0 | } |
214 | 0 | } |
215 | | |
216 | | // fill nRetval |
217 | 0 | const sal_uInt32 nCount(aDepthAndObjectResults.size()); |
218 | |
|
219 | 0 | if(nCount) |
220 | 0 | { |
221 | | // sort aDepthAndObjectResults by depth |
222 | 0 | ::std::sort(aDepthAndObjectResults.begin(), aDepthAndObjectResults.end()); |
223 | | |
224 | | // copy SdrObject pointers to return result set |
225 | 0 | for(const auto& rResult : aDepthAndObjectResults) |
226 | 0 | { |
227 | 0 | o_rResult.push_back(rResult.getObject()); |
228 | 0 | } |
229 | 0 | } |
230 | 0 | } |
231 | | |
232 | | |
233 | | bool checkHitSingle3DObject( |
234 | | const basegfx::B2DPoint& rPoint, |
235 | | const E3dCompoundObject& rCandidate) |
236 | 0 | { |
237 | 0 | const uno::Sequence< beans::PropertyValue > aEmptyParameters; |
238 | 0 | drawinglayer::geometry::ViewInformation3D aViewInfo3D(aEmptyParameters); |
239 | 0 | E3dScene* pRootScene = fillViewInformation3DForCompoundObject(aViewInfo3D, rCandidate); |
240 | |
|
241 | 0 | if(pRootScene) |
242 | 0 | { |
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 | 0 | const sdr::contact::ViewContactOfE3dScene& rVCScene = static_cast< sdr::contact::ViewContactOfE3dScene& >(pRootScene->GetViewContact()); |
247 | 0 | basegfx::B2DHomMatrix aInverseSceneTransform(rVCScene.getObjectTransformation()); |
248 | 0 | aInverseSceneTransform.invert(); |
249 | 0 | const basegfx::B2DPoint aRelativePoint(aInverseSceneTransform * rPoint); |
250 | | |
251 | | // check if test point is inside scene's area at all |
252 | 0 | if(aRelativePoint.getX() >= 0.0 && aRelativePoint.getX() <= 1.0 && aRelativePoint.getY() >= 0.0 && aRelativePoint.getY() <= 1.0) |
253 | 0 | { |
254 | | // create HitPoint Front and Back, transform to object coordinates |
255 | 0 | basegfx::B3DHomMatrix aViewToObject(aViewInfo3D.getObjectToView()); |
256 | 0 | aViewToObject.invert(); |
257 | 0 | const basegfx::B3DPoint aFront(aViewToObject * basegfx::B3DPoint(aRelativePoint.getX(), aRelativePoint.getY(), 0.0)); |
258 | 0 | const basegfx::B3DPoint aBack(aViewToObject * basegfx::B3DPoint(aRelativePoint.getX(), aRelativePoint.getY(), 1.0)); |
259 | |
|
260 | 0 | if(!aFront.equal(aBack)) |
261 | 0 | { |
262 | | // get all hit points with object |
263 | 0 | ::std::vector< basegfx::B3DPoint > aHitsWithObject; |
264 | 0 | getAllHit3DObjectWithRelativePoint(aFront, aBack, rCandidate, aViewInfo3D, aHitsWithObject, true); |
265 | |
|
266 | 0 | if(!aHitsWithObject.empty()) |
267 | 0 | { |
268 | 0 | return true; |
269 | 0 | } |
270 | 0 | } |
271 | 0 | } |
272 | 0 | } |
273 | | |
274 | 0 | return false; |
275 | 0 | } |
276 | | |
277 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |