1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_svx.hxx"
26 
27 #include <svx/helperhittest3d.hxx>
28 #include <basegfx/point/b2dpoint.hxx>
29 #include <svx/svdpage.hxx>
30 #include <svx/scene3d.hxx>
31 #include <svx/svditer.hxx>
32 #include <drawinglayer/processor3d/cutfindprocessor3d.hxx>
33 #include <svx/sdr/contact/viewcontactofe3d.hxx>
34 #include <svx/sdr/contact/viewcontactofe3dscene.hxx>
35 #include <com/sun/star/uno/Sequence.h>
36 
37 //////////////////////////////////////////////////////////////////////////////
38 
39 using namespace com::sun::star;
40 
41 //////////////////////////////////////////////////////////////////////////////
42 
43 class ImplPairDephAndObject
44 {
45 private:
46 	const E3dCompoundObject*    mpObject;
47 	double                      mfDepth;
48 
49 public:
ImplPairDephAndObject(const E3dCompoundObject * pObject,double fDepth)50     ImplPairDephAndObject(const E3dCompoundObject* pObject, double fDepth)
51     :   mpObject(pObject),
52         mfDepth(fDepth)
53     {}
54 
55     // for ::std::sort
operator <(const ImplPairDephAndObject & rComp) const56 	bool operator<(const ImplPairDephAndObject& rComp) const
57     {
58     	return (mfDepth < rComp.mfDepth);
59     }
60 
61     // data read access
getObject() const62     const E3dCompoundObject* getObject() const { return mpObject; }
getDepth() const63     double getDepth() const { return mfDepth; }
64 };
65 
66 //////////////////////////////////////////////////////////////////////////////
67 
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)68 void getAllHit3DObjectWithRelativePoint(
69     const basegfx::B3DPoint& rFront,
70     const basegfx::B3DPoint& rBack,
71     const E3dCompoundObject& rObject,
72     const drawinglayer::geometry::ViewInformation3D& rObjectViewInformation3D,
73     ::std::vector< basegfx::B3DPoint >& o_rResult,
74     bool bAnyHit)
75 {
76     o_rResult.clear();
77 
78     if(!rFront.equal(rBack))
79     {
80         // rObject is a E3dCompoundObject, so it cannot be a scene (which is a E3dObject)
81         const sdr::contact::ViewContactOfE3d& rVCObject = static_cast< sdr::contact::ViewContactOfE3d& >(rObject.GetViewContact());
82 	    const drawinglayer::primitive3d::Primitive3DSequence aPrimitives(rVCObject.getViewIndependentPrimitive3DSequence());
83 
84         if(aPrimitives.hasElements())
85         {
86             // make BoundVolume empty and overlapping test for speedup
87             const basegfx::B3DRange aObjectRange(drawinglayer::primitive3d::getB3DRangeFromPrimitive3DSequence(aPrimitives, rObjectViewInformation3D));
88 
89             if(!aObjectRange.isEmpty())
90             {
91                 const basegfx::B3DRange aFrontBackRange(rFront, rBack);
92 
93                 if(aObjectRange.overlaps(aFrontBackRange))
94                 {
95                     // bound volumes hit, geometric cut tests needed
96                     drawinglayer::processor3d::CutFindProcessor aCutFindProcessor(rObjectViewInformation3D, rFront, rBack, bAnyHit);
97                     aCutFindProcessor.process(aPrimitives);
98                     o_rResult = aCutFindProcessor.getCutPoints();
99                 }
100             }
101         }
102     }
103 }
104 
105 //////////////////////////////////////////////////////////////////////////////
106 
fillViewInformation3DForCompoundObject(drawinglayer::geometry::ViewInformation3D & o_rViewInformation3D,const E3dCompoundObject & rCandidate)107 E3dScene* fillViewInformation3DForCompoundObject(drawinglayer::geometry::ViewInformation3D& o_rViewInformation3D, const E3dCompoundObject& rCandidate)
108 {
109 	// Search for root scene (outmost scene) of the 3d object since e.g. in chart, multiple scenes may
110 	// be placed between object and outmost scene. On that search, remember the in-between scene's
111 	// transformation for the correct complete ObjectTransformation. For historical reasons, the
112 	// root scene's own object transformation is part of the scene's ViewTransformation, o do not
113 	// add it. For more details, see ViewContactOfE3dScene::createViewInformation3D.
114 	E3dScene* pParentScene = dynamic_cast< E3dScene* >(rCandidate.GetParentObj());
115 	E3dScene* pRootScene = 0;
116     basegfx::B3DHomMatrix aInBetweenSceneMatrix;
117 
118 	while(pParentScene)
119 	{
120 		E3dScene* pParentParentScene = dynamic_cast< E3dScene* >(pParentScene->GetParentObj());
121 
122 		if(pParentParentScene)
123 		{
124 			// pParentScene is a in-between scene
125             aInBetweenSceneMatrix = pParentScene->GetTransform() * aInBetweenSceneMatrix;
126 		}
127 		else
128 		{
129 			// pParentScene is the root scene
130 			pRootScene = pParentScene;
131 		}
132 
133 		pParentScene = pParentParentScene;
134 	}
135 
136 	if(pRootScene)
137 	{
138         const sdr::contact::ViewContactOfE3dScene& rVCScene = static_cast< sdr::contact::ViewContactOfE3dScene& >(pRootScene->GetViewContact());
139 
140         if(aInBetweenSceneMatrix.isIdentity())
141 		{
142 			o_rViewInformation3D = rVCScene.getViewInformation3D();
143 		}
144 		else
145         {
146             // build new ViewInformation containing all transforms for the candidate
147 	        const drawinglayer::geometry::ViewInformation3D aViewInfo3D(rVCScene.getViewInformation3D());
148 
149 			o_rViewInformation3D = drawinglayer::geometry::ViewInformation3D(
150                 aViewInfo3D.getObjectTransformation() * aInBetweenSceneMatrix,
151                 aViewInfo3D.getOrientation(),
152                 aViewInfo3D.getProjection(),
153                 aViewInfo3D.getDeviceToView(),
154                 aViewInfo3D.getViewTime(),
155                 aViewInfo3D.getExtendedInformationSequence());
156         }
157 	}
158 	else
159 	{
160 		const uno::Sequence< beans::PropertyValue > aEmptyParameters;
161 		o_rViewInformation3D = drawinglayer::geometry::ViewInformation3D(aEmptyParameters);
162 	}
163 
164 	return pRootScene;
165 }
166 
167 //////////////////////////////////////////////////////////////////////////////
168 
getAllHit3DObjectsSortedFrontToBack(const basegfx::B2DPoint & rPoint,const E3dScene & rScene,::std::vector<const E3dCompoundObject * > & o_rResult)169 SVX_DLLPUBLIC void getAllHit3DObjectsSortedFrontToBack(
170     const basegfx::B2DPoint& rPoint,
171     const E3dScene& rScene,
172     ::std::vector< const E3dCompoundObject* >& o_rResult)
173 {
174 	o_rResult.clear();
175     SdrObjList* pList = rScene.GetSubList();
176 
177     if(pList && pList->GetObjCount())
178     {
179         // prepare relative HitPoint. To do so, get the VC of the 3DScene and from there
180         // the Scene's 2D transformation. Multiplying with the inverse transformation
181         // will create a point relative to the 3D scene as unit-2d-object
182         const sdr::contact::ViewContactOfE3dScene& rVCScene = static_cast< sdr::contact::ViewContactOfE3dScene& >(rScene.GetViewContact());
183 		basegfx::B2DHomMatrix aInverseSceneTransform(rVCScene.getObjectTransformation());
184         aInverseSceneTransform.invert();
185         const basegfx::B2DPoint aRelativePoint(aInverseSceneTransform * rPoint);
186 
187         // check if test point is inside scene's area at all
188         if(aRelativePoint.getX() >= 0.0 && aRelativePoint.getX() <= 1.0 && aRelativePoint.getY() >= 0.0 && aRelativePoint.getY() <= 1.0)
189         {
190 	        SdrObjListIter aIterator(*pList, IM_DEEPNOGROUPS);
191 	        ::std::vector< ImplPairDephAndObject > aDepthAndObjectResults;
192 			const uno::Sequence< beans::PropertyValue > aEmptyParameters;
193 			drawinglayer::geometry::ViewInformation3D aViewInfo3D(aEmptyParameters);
194 
195 	        while(aIterator.IsMore())
196 	        {
197 		        const E3dCompoundObject* pCandidate = dynamic_cast< const E3dCompoundObject* >(aIterator.Next());
198 
199 		        if(pCandidate)
200 		        {
201 					fillViewInformation3DForCompoundObject(aViewInfo3D, *pCandidate);
202 
203 				    // create HitPoint Front and Back, transform to object coordinates
204                     basegfx::B3DHomMatrix aViewToObject(aViewInfo3D.getObjectToView());
205                     aViewToObject.invert();
206 				    const basegfx::B3DPoint aFront(aViewToObject * basegfx::B3DPoint(aRelativePoint.getX(), aRelativePoint.getY(), 0.0));
207 				    const basegfx::B3DPoint aBack(aViewToObject * basegfx::B3DPoint(aRelativePoint.getX(), aRelativePoint.getY(), 1.0));
208 
209                     if(!aFront.equal(aBack))
210                     {
211                         // get all hit points with object
212                         ::std::vector< basegfx::B3DPoint > aHitsWithObject;
213                         getAllHit3DObjectWithRelativePoint(aFront, aBack, *pCandidate, aViewInfo3D, aHitsWithObject, false);
214 
215                         for(sal_uInt32 a(0); a < aHitsWithObject.size(); a++)
216                         {
217                             const basegfx::B3DPoint aPointInViewCoordinates(aViewInfo3D.getObjectToView() * aHitsWithObject[a]);
218                             aDepthAndObjectResults.push_back(ImplPairDephAndObject(pCandidate, aPointInViewCoordinates.getZ()));
219                         }
220                     }
221                 }
222 	        }
223 
224 	        // fill nRetval
225 	        const sal_uInt32 nCount(aDepthAndObjectResults.size());
226 
227 	        if(nCount)
228 	        {
229 		        // sort aDepthAndObjectResults by depth
230 		        ::std::sort(aDepthAndObjectResults.begin(), aDepthAndObjectResults.end());
231 
232 		        // copy SdrObject pointers to return result set
233 		        ::std::vector< ImplPairDephAndObject >::iterator aIterator2(aDepthAndObjectResults.begin());
234 
235 		        for(;aIterator2 != aDepthAndObjectResults.end(); aIterator2++)
236 		        {
237 			        o_rResult.push_back(aIterator2->getObject());
238 		        }
239 	        }
240         }
241     }
242 }
243 
244 //////////////////////////////////////////////////////////////////////////////
245 
checkHitSingle3DObject(const basegfx::B2DPoint & rPoint,const E3dCompoundObject & rCandidate)246 bool checkHitSingle3DObject(
247     const basegfx::B2DPoint& rPoint,
248 	const E3dCompoundObject& rCandidate)
249 {
250 	const uno::Sequence< beans::PropertyValue > aEmptyParameters;
251 	drawinglayer::geometry::ViewInformation3D aViewInfo3D(aEmptyParameters);
252 	E3dScene* pRootScene = fillViewInformation3DForCompoundObject(aViewInfo3D, rCandidate);
253 
254 	if(pRootScene)
255 	{
256         // prepare relative HitPoint. To do so, get the VC of the 3DScene and from there
257         // the Scene's 2D transformation. Multiplying with the inverse transformation
258         // will create a point relative to the 3D scene as unit-2d-object
259         const sdr::contact::ViewContactOfE3dScene& rVCScene = static_cast< sdr::contact::ViewContactOfE3dScene& >(pRootScene->GetViewContact());
260 		basegfx::B2DHomMatrix aInverseSceneTransform(rVCScene.getObjectTransformation());
261         aInverseSceneTransform.invert();
262         const basegfx::B2DPoint aRelativePoint(aInverseSceneTransform * rPoint);
263 
264         // check if test point is inside scene's area at all
265         if(aRelativePoint.getX() >= 0.0 && aRelativePoint.getX() <= 1.0 && aRelativePoint.getY() >= 0.0 && aRelativePoint.getY() <= 1.0)
266         {
267 		    // create HitPoint Front and Back, transform to object coordinates
268             basegfx::B3DHomMatrix aViewToObject(aViewInfo3D.getObjectToView());
269             aViewToObject.invert();
270 		    const basegfx::B3DPoint aFront(aViewToObject * basegfx::B3DPoint(aRelativePoint.getX(), aRelativePoint.getY(), 0.0));
271 		    const basegfx::B3DPoint aBack(aViewToObject * basegfx::B3DPoint(aRelativePoint.getX(), aRelativePoint.getY(), 1.0));
272 
273             if(!aFront.equal(aBack))
274             {
275                 // get all hit points with object
276                 ::std::vector< basegfx::B3DPoint > aHitsWithObject;
277                 getAllHit3DObjectWithRelativePoint(aFront, aBack, rCandidate, aViewInfo3D, aHitsWithObject, true);
278 
279                 if(aHitsWithObject.size())
280                 {
281 					return true;
282 				}
283 			}
284 		}
285 	}
286 
287 	return false;
288 }
289 
290 //////////////////////////////////////////////////////////////////////////////
291 // eof
292