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