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