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