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