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