1*cdf0e10cSrcweir /*************************************************************************
2*cdf0e10cSrcweir  *
3*cdf0e10cSrcweir  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4*cdf0e10cSrcweir  *
5*cdf0e10cSrcweir  * Copyright 2000, 2010 Oracle and/or its affiliates.
6*cdf0e10cSrcweir  *
7*cdf0e10cSrcweir  * OpenOffice.org - a multi-platform office productivity suite
8*cdf0e10cSrcweir  *
9*cdf0e10cSrcweir  * This file is part of OpenOffice.org.
10*cdf0e10cSrcweir  *
11*cdf0e10cSrcweir  * OpenOffice.org is free software: you can redistribute it and/or modify
12*cdf0e10cSrcweir  * it under the terms of the GNU Lesser General Public License version 3
13*cdf0e10cSrcweir  * only, as published by the Free Software Foundation.
14*cdf0e10cSrcweir  *
15*cdf0e10cSrcweir  * OpenOffice.org is distributed in the hope that it will be useful,
16*cdf0e10cSrcweir  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17*cdf0e10cSrcweir  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18*cdf0e10cSrcweir  * GNU Lesser General Public License version 3 for more details
19*cdf0e10cSrcweir  * (a copy is included in the LICENSE file that accompanied this code).
20*cdf0e10cSrcweir  *
21*cdf0e10cSrcweir  * You should have received a copy of the GNU Lesser General Public License
22*cdf0e10cSrcweir  * version 3 along with OpenOffice.org.  If not, see
23*cdf0e10cSrcweir  * <http://www.openoffice.org/license.html>
24*cdf0e10cSrcweir  * for a copy of the LGPLv3 License.
25*cdf0e10cSrcweir  *
26*cdf0e10cSrcweir  ************************************************************************/
27*cdf0e10cSrcweir 
28*cdf0e10cSrcweir // MARKER(update_precomp.py): autogen include statement, do not remove
29*cdf0e10cSrcweir #include "precompiled_drawinglayer.hxx"
30*cdf0e10cSrcweir 
31*cdf0e10cSrcweir #include <drawinglayer/processor2d/hittestprocessor2d.hxx>
32*cdf0e10cSrcweir #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
33*cdf0e10cSrcweir #include <drawinglayer/primitive2d/transformprimitive2d.hxx>
34*cdf0e10cSrcweir #include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
35*cdf0e10cSrcweir #include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx>
36*cdf0e10cSrcweir #include <basegfx/polygon/b2dpolygontools.hxx>
37*cdf0e10cSrcweir #include <basegfx/polygon/b2dpolypolygontools.hxx>
38*cdf0e10cSrcweir #include <drawinglayer/primitive2d/transparenceprimitive2d.hxx>
39*cdf0e10cSrcweir #include <drawinglayer/primitive2d/maskprimitive2d.hxx>
40*cdf0e10cSrcweir #include <drawinglayer/primitive2d/sceneprimitive2d.hxx>
41*cdf0e10cSrcweir #include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx>
42*cdf0e10cSrcweir #include <basegfx/matrix/b3dhommatrix.hxx>
43*cdf0e10cSrcweir #include <drawinglayer/processor3d/cutfindprocessor3d.hxx>
44*cdf0e10cSrcweir #include <drawinglayer/primitive2d/hiddengeometryprimitive2d.hxx>
45*cdf0e10cSrcweir #include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
46*cdf0e10cSrcweir 
47*cdf0e10cSrcweir //////////////////////////////////////////////////////////////////////////////
48*cdf0e10cSrcweir 
49*cdf0e10cSrcweir namespace drawinglayer
50*cdf0e10cSrcweir {
51*cdf0e10cSrcweir 	namespace processor2d
52*cdf0e10cSrcweir 	{
53*cdf0e10cSrcweir 		HitTestProcessor2D::HitTestProcessor2D(const geometry::ViewInformation2D& rViewInformation,
54*cdf0e10cSrcweir 			const basegfx::B2DPoint& rLogicHitPosition,
55*cdf0e10cSrcweir 			double fLogicHitTolerance,
56*cdf0e10cSrcweir             bool bHitTextOnly)
57*cdf0e10cSrcweir 		:	BaseProcessor2D(rViewInformation),
58*cdf0e10cSrcweir 			maDiscreteHitPosition(),
59*cdf0e10cSrcweir 			mfDiscreteHitTolerance(0.0),
60*cdf0e10cSrcweir 			mbHit(false),
61*cdf0e10cSrcweir 			mbHitToleranceUsed(false),
62*cdf0e10cSrcweir 			mbUseInvisiblePrimitiveContent(true),
63*cdf0e10cSrcweir             mbHitTextOnly(bHitTextOnly)
64*cdf0e10cSrcweir 		{
65*cdf0e10cSrcweir 			// init hit tolerance
66*cdf0e10cSrcweir 			mfDiscreteHitTolerance = fLogicHitTolerance;
67*cdf0e10cSrcweir 
68*cdf0e10cSrcweir 			if(basegfx::fTools::less(mfDiscreteHitTolerance, 0.0))
69*cdf0e10cSrcweir 			{
70*cdf0e10cSrcweir 				// ensure input parameter for hit tolerance is >= 0.0
71*cdf0e10cSrcweir 				mfDiscreteHitTolerance = 0.0;
72*cdf0e10cSrcweir 			}
73*cdf0e10cSrcweir 			else if(basegfx::fTools::more(mfDiscreteHitTolerance, 0.0))
74*cdf0e10cSrcweir 			{
75*cdf0e10cSrcweir 				// generate discrete hit tolerance
76*cdf0e10cSrcweir 				mfDiscreteHitTolerance = (getViewInformation2D().getObjectToViewTransformation()
77*cdf0e10cSrcweir 					* basegfx::B2DVector(mfDiscreteHitTolerance, 0.0)).getLength();
78*cdf0e10cSrcweir 			}
79*cdf0e10cSrcweir 
80*cdf0e10cSrcweir 			// gererate discrete hit position
81*cdf0e10cSrcweir 			maDiscreteHitPosition = getViewInformation2D().getObjectToViewTransformation() * rLogicHitPosition;
82*cdf0e10cSrcweir 
83*cdf0e10cSrcweir 			// check if HitTolerance is used
84*cdf0e10cSrcweir 			mbHitToleranceUsed = basegfx::fTools::more(getDiscreteHitTolerance(), 0.0);
85*cdf0e10cSrcweir 		}
86*cdf0e10cSrcweir 
87*cdf0e10cSrcweir 		HitTestProcessor2D::~HitTestProcessor2D()
88*cdf0e10cSrcweir 		{
89*cdf0e10cSrcweir 		}
90*cdf0e10cSrcweir 
91*cdf0e10cSrcweir 		bool HitTestProcessor2D::checkHairlineHitWithTolerance(
92*cdf0e10cSrcweir 			const basegfx::B2DPolygon& rPolygon,
93*cdf0e10cSrcweir 			double fDiscreteHitTolerance)
94*cdf0e10cSrcweir 		{
95*cdf0e10cSrcweir 			basegfx::B2DPolygon aLocalPolygon(rPolygon);
96*cdf0e10cSrcweir 			aLocalPolygon.transform(getViewInformation2D().getObjectToViewTransformation());
97*cdf0e10cSrcweir 
98*cdf0e10cSrcweir 			// get discrete range
99*cdf0e10cSrcweir 			basegfx::B2DRange aPolygonRange(aLocalPolygon.getB2DRange());
100*cdf0e10cSrcweir 
101*cdf0e10cSrcweir 			if(basegfx::fTools::more(fDiscreteHitTolerance, 0.0))
102*cdf0e10cSrcweir 			{
103*cdf0e10cSrcweir 				aPolygonRange.grow(fDiscreteHitTolerance);
104*cdf0e10cSrcweir 			}
105*cdf0e10cSrcweir 
106*cdf0e10cSrcweir 			// do rough range test first
107*cdf0e10cSrcweir 			if(aPolygonRange.isInside(getDiscreteHitPosition()))
108*cdf0e10cSrcweir 			{
109*cdf0e10cSrcweir 				// check if a polygon edge is hit
110*cdf0e10cSrcweir 				return basegfx::tools::isInEpsilonRange(
111*cdf0e10cSrcweir 					aLocalPolygon,
112*cdf0e10cSrcweir 					getDiscreteHitPosition(),
113*cdf0e10cSrcweir 					fDiscreteHitTolerance);
114*cdf0e10cSrcweir 			}
115*cdf0e10cSrcweir 
116*cdf0e10cSrcweir 			return false;
117*cdf0e10cSrcweir 		}
118*cdf0e10cSrcweir 
119*cdf0e10cSrcweir 		bool HitTestProcessor2D::checkFillHitWithTolerance(
120*cdf0e10cSrcweir 			const basegfx::B2DPolyPolygon& rPolyPolygon,
121*cdf0e10cSrcweir 			double fDiscreteHitTolerance)
122*cdf0e10cSrcweir 		{
123*cdf0e10cSrcweir 			bool bRetval(false);
124*cdf0e10cSrcweir 			basegfx::B2DPolyPolygon aLocalPolyPolygon(rPolyPolygon);
125*cdf0e10cSrcweir 			aLocalPolyPolygon.transform(getViewInformation2D().getObjectToViewTransformation());
126*cdf0e10cSrcweir 
127*cdf0e10cSrcweir 			// get discrete range
128*cdf0e10cSrcweir 			basegfx::B2DRange aPolygonRange(aLocalPolyPolygon.getB2DRange());
129*cdf0e10cSrcweir 			const bool bDiscreteHitToleranceUsed(basegfx::fTools::more(fDiscreteHitTolerance, 0.0));
130*cdf0e10cSrcweir 
131*cdf0e10cSrcweir 			if(bDiscreteHitToleranceUsed)
132*cdf0e10cSrcweir 			{
133*cdf0e10cSrcweir 				aPolygonRange.grow(fDiscreteHitTolerance);
134*cdf0e10cSrcweir 			}
135*cdf0e10cSrcweir 
136*cdf0e10cSrcweir 			// do rough range test first
137*cdf0e10cSrcweir 			if(aPolygonRange.isInside(getDiscreteHitPosition()))
138*cdf0e10cSrcweir 			{
139*cdf0e10cSrcweir 				// if a HitTolerance is given, check for polygon edge hit in epsilon first
140*cdf0e10cSrcweir 				if(bDiscreteHitToleranceUsed &&
141*cdf0e10cSrcweir 					basegfx::tools::isInEpsilonRange(
142*cdf0e10cSrcweir 						aLocalPolyPolygon,
143*cdf0e10cSrcweir 						getDiscreteHitPosition(),
144*cdf0e10cSrcweir 						fDiscreteHitTolerance))
145*cdf0e10cSrcweir 				{
146*cdf0e10cSrcweir 					bRetval = true;
147*cdf0e10cSrcweir 				}
148*cdf0e10cSrcweir 
149*cdf0e10cSrcweir 				// check for hit in filled polyPolygon
150*cdf0e10cSrcweir 				if(!bRetval && basegfx::tools::isInside(
151*cdf0e10cSrcweir 					aLocalPolyPolygon,
152*cdf0e10cSrcweir 					getDiscreteHitPosition(),
153*cdf0e10cSrcweir 					true))
154*cdf0e10cSrcweir 				{
155*cdf0e10cSrcweir 					bRetval = true;
156*cdf0e10cSrcweir 				}
157*cdf0e10cSrcweir 			}
158*cdf0e10cSrcweir 
159*cdf0e10cSrcweir 			return bRetval;
160*cdf0e10cSrcweir 		}
161*cdf0e10cSrcweir 
162*cdf0e10cSrcweir 		void HitTestProcessor2D::check3DHit(const primitive2d::ScenePrimitive2D& rCandidate)
163*cdf0e10cSrcweir 		{
164*cdf0e10cSrcweir 			// calculate relative point in unified 2D scene
165*cdf0e10cSrcweir             const basegfx::B2DPoint aLogicHitPosition(getViewInformation2D().getInverseObjectToViewTransformation() * getDiscreteHitPosition());
166*cdf0e10cSrcweir 
167*cdf0e10cSrcweir             // use bitmap check in ScenePrimitive2D
168*cdf0e10cSrcweir             bool bTryFastResult(false);
169*cdf0e10cSrcweir 
170*cdf0e10cSrcweir             if(rCandidate.tryToCheckLastVisualisationDirectHit(aLogicHitPosition, bTryFastResult))
171*cdf0e10cSrcweir             {
172*cdf0e10cSrcweir                 mbHit = bTryFastResult;
173*cdf0e10cSrcweir             }
174*cdf0e10cSrcweir             else
175*cdf0e10cSrcweir             {
176*cdf0e10cSrcweir                 basegfx::B2DHomMatrix aInverseSceneTransform(rCandidate.getObjectTransformation());
177*cdf0e10cSrcweir                 aInverseSceneTransform.invert();
178*cdf0e10cSrcweir                 const basegfx::B2DPoint aRelativePoint(aInverseSceneTransform * aLogicHitPosition);
179*cdf0e10cSrcweir 
180*cdf0e10cSrcweir                 // check if test point is inside scene's unified area at all
181*cdf0e10cSrcweir                 if(aRelativePoint.getX() >= 0.0 && aRelativePoint.getX() <= 1.0
182*cdf0e10cSrcweir                     && aRelativePoint.getY() >= 0.0 && aRelativePoint.getY() <= 1.0)
183*cdf0e10cSrcweir                 {
184*cdf0e10cSrcweir                     // get 3D view information
185*cdf0e10cSrcweir     			    const geometry::ViewInformation3D& rObjectViewInformation3D = rCandidate.getViewInformation3D();
186*cdf0e10cSrcweir 
187*cdf0e10cSrcweir                     // create HitPoint Front and Back, transform to object coordinates
188*cdf0e10cSrcweir                     basegfx::B3DHomMatrix aViewToObject(rObjectViewInformation3D.getObjectToView());
189*cdf0e10cSrcweir                     aViewToObject.invert();
190*cdf0e10cSrcweir                     const basegfx::B3DPoint aFront(aViewToObject * basegfx::B3DPoint(aRelativePoint.getX(), aRelativePoint.getY(), 0.0));
191*cdf0e10cSrcweir                     const basegfx::B3DPoint aBack(aViewToObject * basegfx::B3DPoint(aRelativePoint.getX(), aRelativePoint.getY(), 1.0));
192*cdf0e10cSrcweir 
193*cdf0e10cSrcweir                     if(!aFront.equal(aBack))
194*cdf0e10cSrcweir                     {
195*cdf0e10cSrcweir         			    const primitive3d::Primitive3DSequence& rPrimitives = rCandidate.getChildren3D();
196*cdf0e10cSrcweir 
197*cdf0e10cSrcweir                         if(rPrimitives.hasElements())
198*cdf0e10cSrcweir                         {
199*cdf0e10cSrcweir                             // make BoundVolume empty and overlapping test for speedup
200*cdf0e10cSrcweir                             const basegfx::B3DRange aObjectRange(
201*cdf0e10cSrcweir                                 drawinglayer::primitive3d::getB3DRangeFromPrimitive3DSequence(
202*cdf0e10cSrcweir                                     rPrimitives, rObjectViewInformation3D));
203*cdf0e10cSrcweir 
204*cdf0e10cSrcweir                             if(!aObjectRange.isEmpty())
205*cdf0e10cSrcweir                             {
206*cdf0e10cSrcweir                                 const basegfx::B3DRange aFrontBackRange(aFront, aBack);
207*cdf0e10cSrcweir 
208*cdf0e10cSrcweir                                 if(aObjectRange.overlaps(aFrontBackRange))
209*cdf0e10cSrcweir                                 {
210*cdf0e10cSrcweir                                     // bound volumes hit, geometric cut tests needed
211*cdf0e10cSrcweir                                     drawinglayer::processor3d::CutFindProcessor aCutFindProcessor(
212*cdf0e10cSrcweir                                         rObjectViewInformation3D,
213*cdf0e10cSrcweir                                         aFront,
214*cdf0e10cSrcweir                                         aBack,
215*cdf0e10cSrcweir                                         true);
216*cdf0e10cSrcweir                                     aCutFindProcessor.process(rPrimitives);
217*cdf0e10cSrcweir 
218*cdf0e10cSrcweir                                     mbHit = (0 != aCutFindProcessor.getCutPoints().size());
219*cdf0e10cSrcweir                                 }
220*cdf0e10cSrcweir                             }
221*cdf0e10cSrcweir                         }
222*cdf0e10cSrcweir                     }
223*cdf0e10cSrcweir                 }
224*cdf0e10cSrcweir 
225*cdf0e10cSrcweir 			    // This is needed to check hit with 3D shadows, too. HitTest is without shadow
226*cdf0e10cSrcweir 			    // to keep compatible with previous versions. Keeping here as reference
227*cdf0e10cSrcweir 			    //
228*cdf0e10cSrcweir                 // if(!getHit())
229*cdf0e10cSrcweir                 // {
230*cdf0e10cSrcweir                 //     // if scene has shadow, check hit with shadow, too
231*cdf0e10cSrcweir 		        //     const primitive2d::Primitive2DSequence xExtracted2DSceneShadow(rCandidate.getShadow2D(getViewInformation2D()));
232*cdf0e10cSrcweir 			    //
233*cdf0e10cSrcweir                 //     if(xExtracted2DSceneShadow.hasElements())
234*cdf0e10cSrcweir 	            //     {
235*cdf0e10cSrcweir 		        //         // proccess extracted 2D content
236*cdf0e10cSrcweir 		        //         process(xExtracted2DSceneShadow);
237*cdf0e10cSrcweir 	            //     }
238*cdf0e10cSrcweir                 // }
239*cdf0e10cSrcweir 
240*cdf0e10cSrcweir                 if(!getHit())
241*cdf0e10cSrcweir                 {
242*cdf0e10cSrcweir 		            // empty 3D scene; Check for border hit
243*cdf0e10cSrcweir 		            basegfx::B2DPolygon aOutline(basegfx::tools::createUnitPolygon());
244*cdf0e10cSrcweir                     aOutline.transform(rCandidate.getObjectTransformation());
245*cdf0e10cSrcweir 
246*cdf0e10cSrcweir 		            mbHit = checkHairlineHitWithTolerance(aOutline, getDiscreteHitTolerance());
247*cdf0e10cSrcweir                 }
248*cdf0e10cSrcweir 
249*cdf0e10cSrcweir 			    // This is what the previous version did. Keeping it here for reference
250*cdf0e10cSrcweir 			    //
251*cdf0e10cSrcweir 			    // // 2D Scene primitive containing 3D stuff; extract 2D contour in world coordinates
252*cdf0e10cSrcweir 	            // // This may be refined later to an own 3D HitTest renderer which processes the 3D
253*cdf0e10cSrcweir 	            // // geometry directly
254*cdf0e10cSrcweir 	            // const primitive2d::ScenePrimitive2D& rScenePrimitive2DCandidate(static_cast< const primitive2d::ScenePrimitive2D& >(rCandidate));
255*cdf0e10cSrcweir 	            // const primitive2d::Primitive2DSequence xExtracted2DSceneGeometry(rScenePrimitive2DCandidate.getGeometry2D());
256*cdf0e10cSrcweir 	            // const primitive2d::Primitive2DSequence xExtracted2DSceneShadow(rScenePrimitive2DCandidate.getShadow2D(getViewInformation2D()));
257*cdf0e10cSrcweir 			    //
258*cdf0e10cSrcweir 	            // if(xExtracted2DSceneGeometry.hasElements() || xExtracted2DSceneShadow.hasElements())
259*cdf0e10cSrcweir 	            // {
260*cdf0e10cSrcweir 		        //     // proccess extracted 2D content
261*cdf0e10cSrcweir 		        //     process(xExtracted2DSceneGeometry);
262*cdf0e10cSrcweir 		        //     process(xExtracted2DSceneShadow);
263*cdf0e10cSrcweir 	            // }
264*cdf0e10cSrcweir 	            // else
265*cdf0e10cSrcweir 	            // {
266*cdf0e10cSrcweir 		        //     // empty 3D scene; Check for border hit
267*cdf0e10cSrcweir 		        //     const basegfx::B2DRange aRange(rCandidate.getB2DRange(getViewInformation2D()));
268*cdf0e10cSrcweir                 //     if(!aRange.isEmpty())
269*cdf0e10cSrcweir                 //     {
270*cdf0e10cSrcweir 	            //          const basegfx::B2DPolygon aOutline(basegfx::tools::createPolygonFromRect(aRange));
271*cdf0e10cSrcweir 	            //          mbHit = checkHairlineHitWithTolerance(aOutline, getDiscreteHitTolerance());
272*cdf0e10cSrcweir                 //     }
273*cdf0e10cSrcweir 	            // }
274*cdf0e10cSrcweir             }
275*cdf0e10cSrcweir 		}
276*cdf0e10cSrcweir 
277*cdf0e10cSrcweir 		void HitTestProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate)
278*cdf0e10cSrcweir 		{
279*cdf0e10cSrcweir             if(getHit())
280*cdf0e10cSrcweir 			{
281*cdf0e10cSrcweir 				// stop processing as soon as a hit was recognized
282*cdf0e10cSrcweir 				return;
283*cdf0e10cSrcweir 			}
284*cdf0e10cSrcweir 
285*cdf0e10cSrcweir 			switch(rCandidate.getPrimitive2DID())
286*cdf0e10cSrcweir 			{
287*cdf0e10cSrcweir 				case PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D :
288*cdf0e10cSrcweir 				{
289*cdf0e10cSrcweir 					// remember current ViewInformation2D
290*cdf0e10cSrcweir 					const primitive2d::TransformPrimitive2D& rTransformCandidate(static_cast< const primitive2d::TransformPrimitive2D& >(rCandidate));
291*cdf0e10cSrcweir 					const geometry::ViewInformation2D aLastViewInformation2D(getViewInformation2D());
292*cdf0e10cSrcweir 
293*cdf0e10cSrcweir 					// create new local ViewInformation2D containing transformation
294*cdf0e10cSrcweir 					const geometry::ViewInformation2D aViewInformation2D(
295*cdf0e10cSrcweir 						getViewInformation2D().getObjectTransformation() * rTransformCandidate.getTransformation(),
296*cdf0e10cSrcweir 						getViewInformation2D().getViewTransformation(),
297*cdf0e10cSrcweir 						getViewInformation2D().getViewport(),
298*cdf0e10cSrcweir 						getViewInformation2D().getVisualizedPage(),
299*cdf0e10cSrcweir 						getViewInformation2D().getViewTime(),
300*cdf0e10cSrcweir 						getViewInformation2D().getExtendedInformationSequence());
301*cdf0e10cSrcweir 					updateViewInformation(aViewInformation2D);
302*cdf0e10cSrcweir 
303*cdf0e10cSrcweir 					// proccess child content recursively
304*cdf0e10cSrcweir 					process(rTransformCandidate.getChildren());
305*cdf0e10cSrcweir 
306*cdf0e10cSrcweir 					// restore transformations
307*cdf0e10cSrcweir 					updateViewInformation(aLastViewInformation2D);
308*cdf0e10cSrcweir 
309*cdf0e10cSrcweir 					break;
310*cdf0e10cSrcweir 				}
311*cdf0e10cSrcweir 				case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D :
312*cdf0e10cSrcweir 				{
313*cdf0e10cSrcweir                     if(!getHitTextOnly())
314*cdf0e10cSrcweir                     {
315*cdf0e10cSrcweir 					    // create hairline in discrete coordinates
316*cdf0e10cSrcweir 					    const primitive2d::PolygonHairlinePrimitive2D& rPolygonCandidate(static_cast< const primitive2d::PolygonHairlinePrimitive2D& >(rCandidate));
317*cdf0e10cSrcweir 
318*cdf0e10cSrcweir 					    // use hairline test
319*cdf0e10cSrcweir     					mbHit = checkHairlineHitWithTolerance(rPolygonCandidate.getB2DPolygon(), getDiscreteHitTolerance());
320*cdf0e10cSrcweir                     }
321*cdf0e10cSrcweir 
322*cdf0e10cSrcweir 					break;
323*cdf0e10cSrcweir 				}
324*cdf0e10cSrcweir 				case PRIMITIVE2D_ID_POLYGONMARKERPRIMITIVE2D :
325*cdf0e10cSrcweir 				{
326*cdf0e10cSrcweir                     if(!getHitTextOnly())
327*cdf0e10cSrcweir                     {
328*cdf0e10cSrcweir 					    // handle marker like hairline; no need to decompose in dashes
329*cdf0e10cSrcweir 					    const primitive2d::PolygonMarkerPrimitive2D& rPolygonCandidate(static_cast< const primitive2d::PolygonMarkerPrimitive2D& >(rCandidate));
330*cdf0e10cSrcweir 
331*cdf0e10cSrcweir     					// use hairline test
332*cdf0e10cSrcweir     					mbHit = checkHairlineHitWithTolerance(rPolygonCandidate.getB2DPolygon(), getDiscreteHitTolerance());
333*cdf0e10cSrcweir                     }
334*cdf0e10cSrcweir 
335*cdf0e10cSrcweir 					break;
336*cdf0e10cSrcweir 				}
337*cdf0e10cSrcweir 				case PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D :
338*cdf0e10cSrcweir 				{
339*cdf0e10cSrcweir                     if(!getHitTextOnly())
340*cdf0e10cSrcweir                     {
341*cdf0e10cSrcweir 					    // handle stroke evtl. directly; no need to decompose to filled polygon outlines
342*cdf0e10cSrcweir 					    const primitive2d::PolygonStrokePrimitive2D& rPolygonCandidate(static_cast< const primitive2d::PolygonStrokePrimitive2D& >(rCandidate));
343*cdf0e10cSrcweir 					    const attribute::LineAttribute& rLineAttribute = rPolygonCandidate.getLineAttribute();
344*cdf0e10cSrcweir 
345*cdf0e10cSrcweir 					    if(basegfx::fTools::more(rLineAttribute.getWidth(), 0.0))
346*cdf0e10cSrcweir 					    {
347*cdf0e10cSrcweir 						    if(basegfx::B2DLINEJOIN_MITER == rLineAttribute.getLineJoin())
348*cdf0e10cSrcweir 						    {
349*cdf0e10cSrcweir 							    // if line is mitered, use decomposition since mitered line
350*cdf0e10cSrcweir 							    // geometry may use more space than the geometry grown by half line width
351*cdf0e10cSrcweir 							    process(rCandidate.get2DDecomposition(getViewInformation2D()));
352*cdf0e10cSrcweir 						    }
353*cdf0e10cSrcweir 						    else
354*cdf0e10cSrcweir 						    {
355*cdf0e10cSrcweir 							    // for all other B2DLINEJOIN_* do a hairline HitTest with expanded tolerance
356*cdf0e10cSrcweir 							    const basegfx::B2DVector aDiscreteHalfLineVector(getViewInformation2D().getObjectToViewTransformation()
357*cdf0e10cSrcweir 								    * basegfx::B2DVector(rLineAttribute.getWidth() * 0.5, 0.0));
358*cdf0e10cSrcweir 							    mbHit = checkHairlineHitWithTolerance(
359*cdf0e10cSrcweir 								    rPolygonCandidate.getB2DPolygon(),
360*cdf0e10cSrcweir 								    getDiscreteHitTolerance() + aDiscreteHalfLineVector.getLength());
361*cdf0e10cSrcweir 						    }
362*cdf0e10cSrcweir 					    }
363*cdf0e10cSrcweir 					    else
364*cdf0e10cSrcweir 					    {
365*cdf0e10cSrcweir 						    // hairline; fallback to hairline test. Do not decompose
366*cdf0e10cSrcweir 						    // since this may decompose the hairline to dashes
367*cdf0e10cSrcweir 						    mbHit = checkHairlineHitWithTolerance(rPolygonCandidate.getB2DPolygon(), getDiscreteHitTolerance());
368*cdf0e10cSrcweir 					    }
369*cdf0e10cSrcweir                     }
370*cdf0e10cSrcweir 
371*cdf0e10cSrcweir 					break;
372*cdf0e10cSrcweir 				}
373*cdf0e10cSrcweir 				case PRIMITIVE2D_ID_POLYGONWAVEPRIMITIVE2D :
374*cdf0e10cSrcweir 				{
375*cdf0e10cSrcweir                     if(!getHitTextOnly())
376*cdf0e10cSrcweir                     {
377*cdf0e10cSrcweir 					    // do not use decompose; just handle like a line with width
378*cdf0e10cSrcweir 					    const primitive2d::PolygonWavePrimitive2D& rPolygonCandidate(static_cast< const primitive2d::PolygonWavePrimitive2D& >(rCandidate));
379*cdf0e10cSrcweir 					    double fLogicHitTolerance(0.0);
380*cdf0e10cSrcweir 
381*cdf0e10cSrcweir 					    // if WaveHeight, grow by it
382*cdf0e10cSrcweir 					    if(basegfx::fTools::more(rPolygonCandidate.getWaveHeight(), 0.0))
383*cdf0e10cSrcweir 					    {
384*cdf0e10cSrcweir 						    fLogicHitTolerance += rPolygonCandidate.getWaveHeight();
385*cdf0e10cSrcweir 					    }
386*cdf0e10cSrcweir 
387*cdf0e10cSrcweir 					    // if line width, grow by it
388*cdf0e10cSrcweir 					    if(basegfx::fTools::more(rPolygonCandidate.getLineAttribute().getWidth(), 0.0))
389*cdf0e10cSrcweir 					    {
390*cdf0e10cSrcweir 						    fLogicHitTolerance += rPolygonCandidate.getLineAttribute().getWidth() * 0.5;
391*cdf0e10cSrcweir 					    }
392*cdf0e10cSrcweir 
393*cdf0e10cSrcweir 					    const basegfx::B2DVector aDiscreteHalfLineVector(getViewInformation2D().getObjectToViewTransformation()
394*cdf0e10cSrcweir 						    * basegfx::B2DVector(fLogicHitTolerance, 0.0));
395*cdf0e10cSrcweir 
396*cdf0e10cSrcweir 					    mbHit = checkHairlineHitWithTolerance(
397*cdf0e10cSrcweir 						    rPolygonCandidate.getB2DPolygon(),
398*cdf0e10cSrcweir 						    getDiscreteHitTolerance() + aDiscreteHalfLineVector.getLength());
399*cdf0e10cSrcweir                     }
400*cdf0e10cSrcweir 
401*cdf0e10cSrcweir 					break;
402*cdf0e10cSrcweir 				}
403*cdf0e10cSrcweir 				case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D :
404*cdf0e10cSrcweir 				{
405*cdf0e10cSrcweir                     if(!getHitTextOnly())
406*cdf0e10cSrcweir                     {
407*cdf0e10cSrcweir 					    // create filled polyPolygon in discrete coordinates
408*cdf0e10cSrcweir 					    const primitive2d::PolyPolygonColorPrimitive2D& rPolygonCandidate(static_cast< const primitive2d::PolyPolygonColorPrimitive2D& >(rCandidate));
409*cdf0e10cSrcweir 
410*cdf0e10cSrcweir 					    // use fill hit test
411*cdf0e10cSrcweir     					mbHit = checkFillHitWithTolerance(rPolygonCandidate.getB2DPolyPolygon(), getDiscreteHitTolerance());
412*cdf0e10cSrcweir                     }
413*cdf0e10cSrcweir 
414*cdf0e10cSrcweir 					break;
415*cdf0e10cSrcweir 				}
416*cdf0e10cSrcweir 				case PRIMITIVE2D_ID_TRANSPARENCEPRIMITIVE2D :
417*cdf0e10cSrcweir 				{
418*cdf0e10cSrcweir 					// sub-transparence group
419*cdf0e10cSrcweir 					const primitive2d::TransparencePrimitive2D& rTransCandidate(static_cast< const primitive2d::TransparencePrimitive2D& >(rCandidate));
420*cdf0e10cSrcweir 
421*cdf0e10cSrcweir 					// Currently the transparence content is not taken into account; only
422*cdf0e10cSrcweir 					// the children are recursively checked for hit. This may be refined for
423*cdf0e10cSrcweir 					// parts where the content is completely transparent if needed.
424*cdf0e10cSrcweir 					process(rTransCandidate.getChildren());
425*cdf0e10cSrcweir 
426*cdf0e10cSrcweir 					break;
427*cdf0e10cSrcweir 				}
428*cdf0e10cSrcweir 				case PRIMITIVE2D_ID_MASKPRIMITIVE2D :
429*cdf0e10cSrcweir 				{
430*cdf0e10cSrcweir 					// create mask in discrete coordinates; only recursively continue
431*cdf0e10cSrcweir 					// with content when HitTest position is inside the mask
432*cdf0e10cSrcweir 					const primitive2d::MaskPrimitive2D& rMaskCandidate(static_cast< const primitive2d::MaskPrimitive2D& >(rCandidate));
433*cdf0e10cSrcweir 
434*cdf0e10cSrcweir 					// use fill hit test
435*cdf0e10cSrcweir 					if(checkFillHitWithTolerance(rMaskCandidate.getMask(), getDiscreteHitTolerance()))
436*cdf0e10cSrcweir 					{
437*cdf0e10cSrcweir 						// recursively HitTest children
438*cdf0e10cSrcweir 						process(rMaskCandidate.getChildren());
439*cdf0e10cSrcweir 					}
440*cdf0e10cSrcweir 
441*cdf0e10cSrcweir 					break;
442*cdf0e10cSrcweir 				}
443*cdf0e10cSrcweir 				case PRIMITIVE2D_ID_SCENEPRIMITIVE2D :
444*cdf0e10cSrcweir 				{
445*cdf0e10cSrcweir                     if(!getHitTextOnly())
446*cdf0e10cSrcweir                     {
447*cdf0e10cSrcweir 				        const primitive2d::ScenePrimitive2D& rScenePrimitive2D(
448*cdf0e10cSrcweir                             static_cast< const primitive2d::ScenePrimitive2D& >(rCandidate));
449*cdf0e10cSrcweir 						check3DHit(rScenePrimitive2D);
450*cdf0e10cSrcweir                     }
451*cdf0e10cSrcweir 
452*cdf0e10cSrcweir 					break;
453*cdf0e10cSrcweir 				}
454*cdf0e10cSrcweir                 case PRIMITIVE2D_ID_WRONGSPELLPRIMITIVE2D :
455*cdf0e10cSrcweir 				case PRIMITIVE2D_ID_MARKERARRAYPRIMITIVE2D :
456*cdf0e10cSrcweir 				case PRIMITIVE2D_ID_GRIDPRIMITIVE2D :
457*cdf0e10cSrcweir 				case PRIMITIVE2D_ID_HELPLINEPRIMITIVE2D :
458*cdf0e10cSrcweir                 {
459*cdf0e10cSrcweir 					// ignorable primitives
460*cdf0e10cSrcweir 					break;
461*cdf0e10cSrcweir 				}
462*cdf0e10cSrcweir 				case PRIMITIVE2D_ID_SHADOWPRIMITIVE2D :
463*cdf0e10cSrcweir                 {
464*cdf0e10cSrcweir 					// Ignore shadows; we do not want to have shadows hittable.
465*cdf0e10cSrcweir 					// Remove this one to make shadows hittable on demand.
466*cdf0e10cSrcweir 					break;
467*cdf0e10cSrcweir 				}
468*cdf0e10cSrcweir 				case PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D :
469*cdf0e10cSrcweir 				case PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D :
470*cdf0e10cSrcweir                 {
471*cdf0e10cSrcweir                     // for text use the BoundRect of the primitive itself
472*cdf0e10cSrcweir 				    const basegfx::B2DRange aRange(rCandidate.getB2DRange(getViewInformation2D()));
473*cdf0e10cSrcweir 
474*cdf0e10cSrcweir                     if(!aRange.isEmpty())
475*cdf0e10cSrcweir                     {
476*cdf0e10cSrcweir 				        const basegfx::B2DPolygon aOutline(basegfx::tools::createPolygonFromRect(aRange));
477*cdf0e10cSrcweir 				        mbHit = checkFillHitWithTolerance(basegfx::B2DPolyPolygon(aOutline), getDiscreteHitTolerance());
478*cdf0e10cSrcweir                     }
479*cdf0e10cSrcweir 
480*cdf0e10cSrcweir 					break;
481*cdf0e10cSrcweir 				}
482*cdf0e10cSrcweir 				case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D :
483*cdf0e10cSrcweir                 {
484*cdf0e10cSrcweir                     if(!getHitTextOnly())
485*cdf0e10cSrcweir                     {
486*cdf0e10cSrcweir                         // The recently added BitmapEx::GetTransparency() makes it easy to extend
487*cdf0e10cSrcweir                         // the BitmapPrimitive2D HitTest to take the contained BotmapEx and it's
488*cdf0e10cSrcweir                         // transparency into account
489*cdf0e10cSrcweir 					    const basegfx::B2DRange aRange(rCandidate.getB2DRange(getViewInformation2D()));
490*cdf0e10cSrcweir 
491*cdf0e10cSrcweir                         if(!aRange.isEmpty())
492*cdf0e10cSrcweir                         {
493*cdf0e10cSrcweir     					    const primitive2d::BitmapPrimitive2D& rBitmapCandidate(static_cast< const primitive2d::BitmapPrimitive2D& >(rCandidate));
494*cdf0e10cSrcweir             			    const BitmapEx& rBitmapEx = rBitmapCandidate.getBitmapEx();
495*cdf0e10cSrcweir                             const Size& rSizePixel(rBitmapEx.GetSizePixel());
496*cdf0e10cSrcweir 
497*cdf0e10cSrcweir                             if(rSizePixel.Width() && rSizePixel.Height())
498*cdf0e10cSrcweir                             {
499*cdf0e10cSrcweir                                 basegfx::B2DHomMatrix aBackTransform(
500*cdf0e10cSrcweir                                     getViewInformation2D().getObjectToViewTransformation() *
501*cdf0e10cSrcweir                                     rBitmapCandidate.getTransform());
502*cdf0e10cSrcweir                                 aBackTransform.invert();
503*cdf0e10cSrcweir 
504*cdf0e10cSrcweir                                 const basegfx::B2DPoint aRelativePoint(aBackTransform * getDiscreteHitPosition());
505*cdf0e10cSrcweir                                 const basegfx::B2DRange aUnitRange(0.0, 0.0, 1.0, 1.0);
506*cdf0e10cSrcweir 
507*cdf0e10cSrcweir                                 if(aUnitRange.isInside(aRelativePoint))
508*cdf0e10cSrcweir                                 {
509*cdf0e10cSrcweir                                     const sal_Int32 nX(basegfx::fround(aRelativePoint.getX() * rSizePixel.Width()));
510*cdf0e10cSrcweir                                     const sal_Int32 nY(basegfx::fround(aRelativePoint.getY() * rSizePixel.Height()));
511*cdf0e10cSrcweir 
512*cdf0e10cSrcweir                                     mbHit = (0xff != rBitmapEx.GetTransparency(nX, nY));
513*cdf0e10cSrcweir                                 }
514*cdf0e10cSrcweir                             }
515*cdf0e10cSrcweir                             else
516*cdf0e10cSrcweir                             {
517*cdf0e10cSrcweir                                 // fallback to standard HitTest
518*cdf0e10cSrcweir 					            const basegfx::B2DPolygon aOutline(basegfx::tools::createPolygonFromRect(aRange));
519*cdf0e10cSrcweir 					            mbHit = checkFillHitWithTolerance(basegfx::B2DPolyPolygon(aOutline), getDiscreteHitTolerance());
520*cdf0e10cSrcweir                             }
521*cdf0e10cSrcweir                         }
522*cdf0e10cSrcweir                     }
523*cdf0e10cSrcweir 
524*cdf0e10cSrcweir                     break;
525*cdf0e10cSrcweir                 }
526*cdf0e10cSrcweir 				case PRIMITIVE2D_ID_METAFILEPRIMITIVE2D :
527*cdf0e10cSrcweir 				case PRIMITIVE2D_ID_CONTROLPRIMITIVE2D :
528*cdf0e10cSrcweir 				case PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D :
529*cdf0e10cSrcweir 				case PRIMITIVE2D_ID_FILLHATCHPRIMITIVE2D :
530*cdf0e10cSrcweir 				case PRIMITIVE2D_ID_PAGEPREVIEWPRIMITIVE2D :
531*cdf0e10cSrcweir                 case PRIMITIVE2D_ID_MEDIAPRIMITIVE2D:
532*cdf0e10cSrcweir                 case PRIMITIVE2D_ID_RENDERGRAPHICPRIMITIVE2D:
533*cdf0e10cSrcweir                 {
534*cdf0e10cSrcweir                     if(!getHitTextOnly())
535*cdf0e10cSrcweir                     {
536*cdf0e10cSrcweir 					    // Class of primitives for which just the BoundRect of the primitive itself
537*cdf0e10cSrcweir 					    // will be used for HitTest currently.
538*cdf0e10cSrcweir 					    //
539*cdf0e10cSrcweir 					    // This may be refined in the future, e.g:
540*cdf0e10cSrcweir 					    // - For Bitamps, the mask and/or transparence information may be used
541*cdf0e10cSrcweir 					    // - For MetaFiles, the MetaFile content may be used
542*cdf0e10cSrcweir 					    const basegfx::B2DRange aRange(rCandidate.getB2DRange(getViewInformation2D()));
543*cdf0e10cSrcweir 
544*cdf0e10cSrcweir                         if(!aRange.isEmpty())
545*cdf0e10cSrcweir                         {
546*cdf0e10cSrcweir 					        const basegfx::B2DPolygon aOutline(basegfx::tools::createPolygonFromRect(aRange));
547*cdf0e10cSrcweir 					        mbHit = checkFillHitWithTolerance(basegfx::B2DPolyPolygon(aOutline), getDiscreteHitTolerance());
548*cdf0e10cSrcweir                         }
549*cdf0e10cSrcweir                     }
550*cdf0e10cSrcweir 
551*cdf0e10cSrcweir 					break;
552*cdf0e10cSrcweir 				}
553*cdf0e10cSrcweir 				case PRIMITIVE2D_ID_HIDDENGEOMETRYPRIMITIVE2D :
554*cdf0e10cSrcweir 				{
555*cdf0e10cSrcweir 					// HiddenGeometryPrimitive2D; the default decomposition would return an empty seqence,
556*cdf0e10cSrcweir 					// so force this primitive to process it's children directly if the switch is set
557*cdf0e10cSrcweir 					// (which is the default). Else, ignore invisible content
558*cdf0e10cSrcweir 				    const primitive2d::HiddenGeometryPrimitive2D& rHiddenGeometry(static_cast< const primitive2d::HiddenGeometryPrimitive2D& >(rCandidate));
559*cdf0e10cSrcweir        			    const primitive2d::Primitive2DSequence& rChildren = rHiddenGeometry.getChildren();
560*cdf0e10cSrcweir 
561*cdf0e10cSrcweir                     if(rChildren.hasElements())
562*cdf0e10cSrcweir                     {
563*cdf0e10cSrcweir                         if(getUseInvisiblePrimitiveContent())
564*cdf0e10cSrcweir 					    {
565*cdf0e10cSrcweir                             process(rChildren);
566*cdf0e10cSrcweir 					    }
567*cdf0e10cSrcweir                     }
568*cdf0e10cSrcweir 
569*cdf0e10cSrcweir 					break;
570*cdf0e10cSrcweir 				}
571*cdf0e10cSrcweir 				case PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D :
572*cdf0e10cSrcweir 				{
573*cdf0e10cSrcweir                     if(!getHitTextOnly())
574*cdf0e10cSrcweir                     {
575*cdf0e10cSrcweir 					    const primitive2d::PointArrayPrimitive2D& rPointArrayCandidate(static_cast< const primitive2d::PointArrayPrimitive2D& >(rCandidate));
576*cdf0e10cSrcweir 					    const std::vector< basegfx::B2DPoint >& rPositions = rPointArrayCandidate.getPositions();
577*cdf0e10cSrcweir 					    const sal_uInt32 nCount(rPositions.size());
578*cdf0e10cSrcweir 
579*cdf0e10cSrcweir 					    for(sal_uInt32 a(0); !getHit() && a < nCount; a++)
580*cdf0e10cSrcweir 					    {
581*cdf0e10cSrcweir 						    const basegfx::B2DPoint aPosition(getViewInformation2D().getObjectToViewTransformation() * rPositions[a]);
582*cdf0e10cSrcweir 						    const basegfx::B2DVector aDistance(aPosition - getDiscreteHitPosition());
583*cdf0e10cSrcweir 
584*cdf0e10cSrcweir 						    if(aDistance.getLength() <= getDiscreteHitTolerance())
585*cdf0e10cSrcweir 						    {
586*cdf0e10cSrcweir 							    mbHit = true;
587*cdf0e10cSrcweir 						    }
588*cdf0e10cSrcweir 					    }
589*cdf0e10cSrcweir                     }
590*cdf0e10cSrcweir 
591*cdf0e10cSrcweir 					break;
592*cdf0e10cSrcweir 				}
593*cdf0e10cSrcweir 				default :
594*cdf0e10cSrcweir 				{
595*cdf0e10cSrcweir 					// process recursively
596*cdf0e10cSrcweir 					process(rCandidate.get2DDecomposition(getViewInformation2D()));
597*cdf0e10cSrcweir 
598*cdf0e10cSrcweir 					break;
599*cdf0e10cSrcweir 				}
600*cdf0e10cSrcweir 			}
601*cdf0e10cSrcweir 		}
602*cdf0e10cSrcweir 
603*cdf0e10cSrcweir 	} // end of namespace processor2d
604*cdf0e10cSrcweir } // end of namespace drawinglayer
605*cdf0e10cSrcweir 
606*cdf0e10cSrcweir //////////////////////////////////////////////////////////////////////////////
607*cdf0e10cSrcweir // eof
608