xref: /trunk/main/drawinglayer/source/processor2d/hittestprocessor2d.cxx (revision 585a78999909dc459bf73df17df32793da6d9163)
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