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