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