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