1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
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
61 const E3dCompoundObject
* mpObject
;
65 ImplPairDephAndObject(const E3dCompoundObject
* pObject
, double fDepth
)
71 bool operator<(const ImplPairDephAndObject
& rComp
) const
73 return (mfDepth
< rComp
.mfDepth
);
77 const E3dCompoundObject
* getObject() const { return mpObject
; }
78 double getDepth() const { return mfDepth
; }
81 //////////////////////////////////////////////////////////////////////////////
83 namespace drawinglayer
87 class CutFindProcessor
: public BaseProcessor3D
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
);
107 CutFindProcessor(const geometry::ViewInformation3D
& rViewInformation
,
108 const basegfx::B3DPoint
& rFront
,
109 const basegfx::B3DPoint
& rBack
)
110 : BaseProcessor3D(rViewInformation
),
114 maCombinedTransform()
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
:
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();
155 process(rPrimitive
.getChildren());
157 // restore transformations and front, back
158 maCombinedTransform
= aLastCombinedTransform
;
159 updateViewInformation(aLastViewInformation3D
);
160 maFront
= aLastFront
;
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
171 case PRIMITIVE3D_ID_HATCHTEXTUREPRIMITIVE3D
:
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());
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
187 const primitive3d::HitTestPrimitive3D
& rPrimitive
= static_cast< const primitive3d::HitTestPrimitive3D
& >(rCandidate
);
188 process(rPrimitive
.getChildren());
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());
203 const basegfx::B3DPolygon
aPolygon(rPolyPolygon
.getB3DPolygon(0));
204 const sal_uInt32
nPointCount(aPolygon
.count());
208 const basegfx::B3DVector
aPlaneNormal(aPolygon
.getNormal());
210 if(!aPlaneNormal
.equalZero())
212 const basegfx::B3DPoint
aPointOnPlane(aPolygon
.getB3DPoint(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
);
236 // process recursively
237 process(rCandidate
.get3DDecomposition(getViewInformation3D()));
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
)
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
;
298 E3dScene
* pParentParentScene
= dynamic_cast< E3dScene
* >(pParentScene
->GetParentObj());
300 if(pParentParentScene
)
302 // pParentScene is a in-between scene
303 aInBetweenSceneMatrix
= pParentScene
->GetTransform() * aInBetweenSceneMatrix
;
307 // pParentScene is the root scene
308 pRootScene
= pParentScene
;
311 pParentScene
= pParentParentScene
;
316 const sdr::contact::ViewContactOfE3dScene
& rVCScene
= static_cast< sdr::contact::ViewContactOfE3dScene
& >(pRootScene
->GetViewContact());
318 if(aInBetweenSceneMatrix
.isIdentity())
320 o_rViewInformation3D
= rVCScene
.getViewInformation3D();
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());
338 const uno::Sequence
< beans::PropertyValue
> aEmptyParameters
;
339 o_rViewInformation3D
= drawinglayer::geometry::ViewInformation3D(aEmptyParameters
);
345 //////////////////////////////////////////////////////////////////////////////
347 SVX_DLLPUBLIC
void getAllHit3DObjectsSortedFrontToBack(
348 const basegfx::B2DPoint
& rPoint
,
349 const E3dScene
& rScene
,
350 ::std::vector
< const E3dCompoundObject
* >& o_rResult
)
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());
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()));
403 const sal_uInt32
nCount(aDepthAndObjectResults
.size());
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
);
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())
468 //////////////////////////////////////////////////////////////////////////////