merge the formfield patch from ooo-build
[ooovba.git] / svx / source / engine3d / helperhittest3d.cxx
blob401111be85d060331c49cc297041bbb399e04bf0
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: helperhittest3d.cxx,v $
10 * $Revision: 1.1.2.3 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_svx.hxx"
34 #include <svx/helperhittest3d.hxx>
35 #include <basegfx/point/b2dpoint.hxx>
36 #include <svx/svdpage.hxx>
37 #include <svx/scene3d.hxx>
38 #include <svditer.hxx>
39 #include <drawinglayer/processor3d/baseprocessor3d.hxx>
40 #include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
41 #include <drawinglayer/primitive3d/transformprimitive3d.hxx>
42 #include <drawinglayer/primitive3d/polygonprimitive3d.hxx>
43 #include <drawinglayer/primitive3d/polypolygonprimitive3d.hxx>
44 #include <svx/sdr/contact/viewcontactofe3d.hxx>
45 #include <svx/sdr/contact/viewcontactofe3dscene.hxx>
46 #include <basegfx/polygon/b3dpolygontools.hxx>
47 #include <basegfx/polygon/b3dpolypolygontools.hxx>
48 #include <drawinglayer/primitive3d/hittestprimitive3d.hxx>
49 #include <com/sun/star/uno/Sequence.h>
50 #include <drawinglayer/primitive3d/hatchtextureprimitive3d.hxx>
52 //////////////////////////////////////////////////////////////////////////////
54 using namespace com::sun::star;
56 //////////////////////////////////////////////////////////////////////////////
58 class ImplPairDephAndObject
60 private:
61 const E3dCompoundObject* mpObject;
62 double mfDepth;
64 public:
65 ImplPairDephAndObject(const E3dCompoundObject* pObject, double fDepth)
66 : mpObject(pObject),
67 mfDepth(fDepth)
70 // for ::std::sort
71 bool operator<(const ImplPairDephAndObject& rComp) const
73 return (mfDepth < rComp.mfDepth);
76 // data read access
77 const E3dCompoundObject* getObject() const { return mpObject; }
78 double getDepth() const { return mfDepth; }
81 //////////////////////////////////////////////////////////////////////////////
83 namespace drawinglayer
85 namespace processor3d
87 class CutFindProcessor : public BaseProcessor3D
89 private:
90 // the start and stop point for the cut vector
91 basegfx::B3DPoint maFront;
92 basegfx::B3DPoint maBack;
94 // the found cut points
95 ::std::vector< basegfx::B3DPoint > maResult;
97 // #i102956# the transformation change from TransformPrimitive3D processings
98 // needs to be remembered to be able to transform found cuts to the
99 // basic coordinate system the processor starts with
100 basegfx::B3DHomMatrix maCombinedTransform;
102 // as tooling, the process() implementation takes over API handling and calls this
103 // virtual render method when the primitive implementation is BasePrimitive3D-based.
104 virtual void processBasePrimitive3D(const primitive3d::BasePrimitive3D& rCandidate);
106 public:
107 CutFindProcessor(const geometry::ViewInformation3D& rViewInformation,
108 const basegfx::B3DPoint& rFront,
109 const basegfx::B3DPoint& rBack)
110 : BaseProcessor3D(rViewInformation),
111 maFront(rFront),
112 maBack(rBack),
113 maResult(),
114 maCombinedTransform()
117 // data access
118 const ::std::vector< basegfx::B3DPoint >& getCutPoints() const { return maResult; }
121 void CutFindProcessor::processBasePrimitive3D(const primitive3d::BasePrimitive3D& rCandidate)
123 // it is a BasePrimitive3D implementation, use getPrimitiveID() call for switch
124 switch(rCandidate.getPrimitiveID())
126 case PRIMITIVE3D_ID_TRANSFORMPRIMITIVE3D :
128 // transform group.
129 const primitive3d::TransformPrimitive3D& rPrimitive = static_cast< const primitive3d::TransformPrimitive3D& >(rCandidate);
131 // remember old and transform front, back to object coordinates
132 const basegfx::B3DPoint aLastFront(maFront);
133 const basegfx::B3DPoint aLastBack(maBack);
134 basegfx::B3DHomMatrix aInverseTrans(rPrimitive.getTransformation());
135 aInverseTrans.invert();
136 maFront *= aInverseTrans;
137 maBack *= aInverseTrans;
139 // remember current and create new transformation; add new object transform from right side
140 const geometry::ViewInformation3D aLastViewInformation3D(getViewInformation3D());
141 const geometry::ViewInformation3D aNewViewInformation3D(
142 aLastViewInformation3D.getObjectTransformation() * rPrimitive.getTransformation(),
143 aLastViewInformation3D.getOrientation(),
144 aLastViewInformation3D.getProjection(),
145 aLastViewInformation3D.getDeviceToView(),
146 aLastViewInformation3D.getViewTime(),
147 aLastViewInformation3D.getExtendedInformationSequence());
148 updateViewInformation(aNewViewInformation3D);
150 // #i102956# remember needed back-transform for found cuts (combine from right side)
151 const basegfx::B3DHomMatrix aLastCombinedTransform(maCombinedTransform);
152 maCombinedTransform = maCombinedTransform * rPrimitive.getTransformation();
154 // let break down
155 process(rPrimitive.getChildren());
157 // restore transformations and front, back
158 maCombinedTransform = aLastCombinedTransform;
159 updateViewInformation(aLastViewInformation3D);
160 maFront = aLastFront;
161 maBack = aLastBack;
162 break;
164 case PRIMITIVE3D_ID_POLYGONHAIRLINEPRIMITIVE3D :
166 // PolygonHairlinePrimitive3D, not used for hit test with planes, ignore. This
167 // means that also thick line expansion will not be hit-tested as
168 // PolyPolygonMaterialPrimitive3D
169 break;
171 case PRIMITIVE3D_ID_HATCHTEXTUREPRIMITIVE3D :
173 // #i97321#
174 // For HatchTexturePrimitive3D, do not use the decomposition since it will produce
175 // clipped hatch lines in 3D. It can be used when the hatch also has a filling, but for
176 // simplicity, just use the children which are the PolyPolygonMaterialPrimitive3D
177 // which define the hatched areas anyways; for HitTest this is more than adequate
178 const primitive3d::HatchTexturePrimitive3D& rPrimitive = static_cast< const primitive3d::HatchTexturePrimitive3D& >(rCandidate);
179 process(rPrimitive.getChildren());
180 break;
182 case PRIMITIVE3D_ID_HITTESTPRIMITIVE3D :
184 // HitTestPrimitive3D, force usage due to we are doing a hit test and this
185 // primitive only gets generated on 3d objects without fill, exactly for this
186 // purpose
187 const primitive3d::HitTestPrimitive3D& rPrimitive = static_cast< const primitive3d::HitTestPrimitive3D& >(rCandidate);
188 process(rPrimitive.getChildren());
189 break;
191 case PRIMITIVE3D_ID_POLYPOLYGONMATERIALPRIMITIVE3D :
193 // PolyPolygonMaterialPrimitive3D
194 const primitive3d::PolyPolygonMaterialPrimitive3D& rPrimitive = static_cast< const primitive3d::PolyPolygonMaterialPrimitive3D& >(rCandidate);
196 if(!maFront.equal(maBack))
198 const basegfx::B3DPolyPolygon& rPolyPolygon = rPrimitive.getB3DPolyPolygon();
199 const sal_uInt32 nPolyCount(rPolyPolygon.count());
201 if(nPolyCount)
203 const basegfx::B3DPolygon aPolygon(rPolyPolygon.getB3DPolygon(0));
204 const sal_uInt32 nPointCount(aPolygon.count());
206 if(nPointCount > 2)
208 const basegfx::B3DVector aPlaneNormal(aPolygon.getNormal());
210 if(!aPlaneNormal.equalZero())
212 const basegfx::B3DPoint aPointOnPlane(aPolygon.getB3DPoint(0));
213 double fCut(0.0);
215 if(basegfx::tools::getCutBetweenLineAndPlane(aPlaneNormal, aPointOnPlane, maFront, maBack, fCut))
217 const basegfx::B3DPoint aCutPoint(basegfx::interpolate(maFront, maBack, fCut));
219 if(basegfx::tools::isInside(rPolyPolygon, aCutPoint, false))
221 // #i102956# add result. Do not forget to do this in the coordinate
222 // system the processor get started with, so use the collected
223 // combined transformation from processed TransformPrimitive3D's
224 maResult.push_back(maCombinedTransform * aCutPoint);
232 break;
234 default :
236 // process recursively
237 process(rCandidate.get3DDecomposition(getViewInformation3D()));
238 break;
242 } // end of namespace processor3d
243 } // end of namespace drawinglayer
245 //////////////////////////////////////////////////////////////////////////////
247 void getAllHit3DObjectWithRelativePoint(
248 const basegfx::B3DPoint& rFront,
249 const basegfx::B3DPoint& rBack,
250 const E3dCompoundObject& rObject,
251 const drawinglayer::geometry::ViewInformation3D& rObjectViewInformation3D,
252 ::std::vector< basegfx::B3DPoint >& o_rResult)
254 o_rResult.clear();
256 if(!rFront.equal(rBack))
258 // rObject is a E3dCompoundObject, so it cannot be a scene (which is a E3dObject)
259 const sdr::contact::ViewContactOfE3d& rVCObject = static_cast< sdr::contact::ViewContactOfE3d& >(rObject.GetViewContact());
260 const drawinglayer::primitive3d::Primitive3DSequence aPrimitives(rVCObject.getViewIndependentPrimitive3DSequence());
262 if(aPrimitives.hasElements())
264 // make BoundVolume empty and overlapping test for speedup
265 const basegfx::B3DRange aObjectRange(drawinglayer::primitive3d::getB3DRangeFromPrimitive3DSequence(aPrimitives, rObjectViewInformation3D));
267 if(!aObjectRange.isEmpty())
269 const basegfx::B3DRange aFrontBackRange(rFront, rBack);
271 if(aObjectRange.overlaps(aFrontBackRange))
273 // bound volumes hit, geometric cut tests needed
274 drawinglayer::processor3d::CutFindProcessor aCutFindProcessor(rObjectViewInformation3D, rFront, rBack);
275 aCutFindProcessor.process(aPrimitives);
276 o_rResult = aCutFindProcessor.getCutPoints();
283 //////////////////////////////////////////////////////////////////////////////
285 E3dScene* fillViewInformation3DForCompoundObject(drawinglayer::geometry::ViewInformation3D& o_rViewInformation3D, const E3dCompoundObject& rCandidate)
287 // Search for root scene (outmost scene) of the 3d object since e.g. in chart, multiple scenes may
288 // be placed between object and outmost scene. On that search, remember the in-between scene's
289 // transformation for the correct complete ObjectTransformation. For historical reasons, the
290 // root scene's own object transformation is part of the scene's ViewTransformation, o do not
291 // add it. For more details, see ViewContactOfE3dScene::createViewInformation3D.
292 E3dScene* pParentScene = dynamic_cast< E3dScene* >(rCandidate.GetParentObj());
293 E3dScene* pRootScene = 0;
294 basegfx::B3DHomMatrix aInBetweenSceneMatrix;
296 while(pParentScene)
298 E3dScene* pParentParentScene = dynamic_cast< E3dScene* >(pParentScene->GetParentObj());
300 if(pParentParentScene)
302 // pParentScene is a in-between scene
303 aInBetweenSceneMatrix = pParentScene->GetTransform() * aInBetweenSceneMatrix;
305 else
307 // pParentScene is the root scene
308 pRootScene = pParentScene;
311 pParentScene = pParentParentScene;
314 if(pRootScene)
316 const sdr::contact::ViewContactOfE3dScene& rVCScene = static_cast< sdr::contact::ViewContactOfE3dScene& >(pRootScene->GetViewContact());
318 if(aInBetweenSceneMatrix.isIdentity())
320 o_rViewInformation3D = rVCScene.getViewInformation3D();
322 else
324 // build new ViewInformation containing all transforms for the candidate
325 const drawinglayer::geometry::ViewInformation3D aViewInfo3D(rVCScene.getViewInformation3D());
327 o_rViewInformation3D = drawinglayer::geometry::ViewInformation3D(
328 aViewInfo3D.getObjectTransformation() * aInBetweenSceneMatrix,
329 aViewInfo3D.getOrientation(),
330 aViewInfo3D.getProjection(),
331 aViewInfo3D.getDeviceToView(),
332 aViewInfo3D.getViewTime(),
333 aViewInfo3D.getExtendedInformationSequence());
336 else
338 const uno::Sequence< beans::PropertyValue > aEmptyParameters;
339 o_rViewInformation3D = drawinglayer::geometry::ViewInformation3D(aEmptyParameters);
342 return pRootScene;
345 //////////////////////////////////////////////////////////////////////////////
347 SVX_DLLPUBLIC void getAllHit3DObjectsSortedFrontToBack(
348 const basegfx::B2DPoint& rPoint,
349 const E3dScene& rScene,
350 ::std::vector< const E3dCompoundObject* >& o_rResult)
352 o_rResult.clear();
353 SdrObjList* pList = rScene.GetSubList();
355 if(pList && pList->GetObjCount())
357 // prepare relative HitPoint. To do so, get the VC of the 3DScene and from there
358 // the Scene's 2D transformation. Multiplying with the inverse transformation
359 // will create a point relative to the 3D scene as unit-2d-object
360 const sdr::contact::ViewContactOfE3dScene& rVCScene = static_cast< sdr::contact::ViewContactOfE3dScene& >(rScene.GetViewContact());
361 basegfx::B2DHomMatrix aInverseSceneTransform(rVCScene.getObjectTransformation());
362 aInverseSceneTransform.invert();
363 const basegfx::B2DPoint aRelativePoint(aInverseSceneTransform * rPoint);
365 // check if test point is inside scene's area at all
366 if(aRelativePoint.getX() >= 0.0 && aRelativePoint.getX() <= 1.0 && aRelativePoint.getY() >= 0.0 && aRelativePoint.getY() <= 1.0)
368 SdrObjListIter aIterator(*pList, IM_DEEPNOGROUPS);
369 ::std::vector< ImplPairDephAndObject > aDepthAndObjectResults;
370 const uno::Sequence< beans::PropertyValue > aEmptyParameters;
371 drawinglayer::geometry::ViewInformation3D aViewInfo3D(aEmptyParameters);
373 while(aIterator.IsMore())
375 const E3dCompoundObject* pCandidate = dynamic_cast< const E3dCompoundObject* >(aIterator.Next());
377 if(pCandidate)
379 fillViewInformation3DForCompoundObject(aViewInfo3D, *pCandidate);
381 // create HitPoint Front and Back, transform to object coordinates
382 basegfx::B3DHomMatrix aViewToObject(aViewInfo3D.getObjectToView());
383 aViewToObject.invert();
384 const basegfx::B3DPoint aFront(aViewToObject * basegfx::B3DPoint(aRelativePoint.getX(), aRelativePoint.getY(), 0.0));
385 const basegfx::B3DPoint aBack(aViewToObject * basegfx::B3DPoint(aRelativePoint.getX(), aRelativePoint.getY(), 1.0));
387 if(!aFront.equal(aBack))
389 // get all hit points with object
390 ::std::vector< basegfx::B3DPoint > aHitsWithObject;
391 getAllHit3DObjectWithRelativePoint(aFront, aBack, *pCandidate, aViewInfo3D, aHitsWithObject);
393 for(sal_uInt32 a(0); a < aHitsWithObject.size(); a++)
395 const basegfx::B3DPoint aPointInViewCoordinates(aViewInfo3D.getObjectToView() * aHitsWithObject[a]);
396 aDepthAndObjectResults.push_back(ImplPairDephAndObject(pCandidate, aPointInViewCoordinates.getZ()));
402 // fill nRetval
403 const sal_uInt32 nCount(aDepthAndObjectResults.size());
405 if(nCount)
407 // sort aDepthAndObjectResults by depth
408 ::std::sort(aDepthAndObjectResults.begin(), aDepthAndObjectResults.end());
410 // copy SdrObject pointers to return result set
411 ::std::vector< ImplPairDephAndObject >::iterator aIterator2(aDepthAndObjectResults.begin());
413 for(;aIterator2 != aDepthAndObjectResults.end(); aIterator2++)
415 o_rResult.push_back(aIterator2->getObject());
422 //////////////////////////////////////////////////////////////////////////////
424 bool checkHitSingle3DObject(
425 const basegfx::B2DPoint& rPoint,
426 const E3dCompoundObject& rCandidate)
428 const uno::Sequence< beans::PropertyValue > aEmptyParameters;
429 drawinglayer::geometry::ViewInformation3D aViewInfo3D(aEmptyParameters);
430 E3dScene* pRootScene = fillViewInformation3DForCompoundObject(aViewInfo3D, rCandidate);
432 if(pRootScene)
434 // prepare relative HitPoint. To do so, get the VC of the 3DScene and from there
435 // the Scene's 2D transformation. Multiplying with the inverse transformation
436 // will create a point relative to the 3D scene as unit-2d-object
437 const sdr::contact::ViewContactOfE3dScene& rVCScene = static_cast< sdr::contact::ViewContactOfE3dScene& >(pRootScene->GetViewContact());
438 basegfx::B2DHomMatrix aInverseSceneTransform(rVCScene.getObjectTransformation());
439 aInverseSceneTransform.invert();
440 const basegfx::B2DPoint aRelativePoint(aInverseSceneTransform * rPoint);
442 // check if test point is inside scene's area at all
443 if(aRelativePoint.getX() >= 0.0 && aRelativePoint.getX() <= 1.0 && aRelativePoint.getY() >= 0.0 && aRelativePoint.getY() <= 1.0)
445 // create HitPoint Front and Back, transform to object coordinates
446 basegfx::B3DHomMatrix aViewToObject(aViewInfo3D.getObjectToView());
447 aViewToObject.invert();
448 const basegfx::B3DPoint aFront(aViewToObject * basegfx::B3DPoint(aRelativePoint.getX(), aRelativePoint.getY(), 0.0));
449 const basegfx::B3DPoint aBack(aViewToObject * basegfx::B3DPoint(aRelativePoint.getX(), aRelativePoint.getY(), 1.0));
451 if(!aFront.equal(aBack))
453 // get all hit points with object
454 ::std::vector< basegfx::B3DPoint > aHitsWithObject;
455 getAllHit3DObjectWithRelativePoint(aFront, aBack, rCandidate, aViewInfo3D, aHitsWithObject);
457 if(aHitsWithObject.size())
459 return true;
465 return false;
468 //////////////////////////////////////////////////////////////////////////////
469 // eof