xref: /AOO41X/main/drawinglayer/source/processor2d/hittestprocessor2d.cxx (revision ddde725d65c83fe3ba1186d46f6e3e08f12ba47e)
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                 //         // proccess 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                 //     // proccess 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                     // proccess 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