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