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