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