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