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