Update ooo320-m1
[ooovba.git] / drawinglayer / source / processor2d / hittestprocessor2d.cxx
blobcc71ea19ab6b4a48a9e06114f072b81b107c1426
1 /*************************************************************************
3 * OpenOffice.org - a multi-platform office productivity suite
5 * $RCSfile: contourextractor2d.cxx,v $
7 * $Revision: 1.6 $
9 * last change: $Author: aw $ $Date: 2008-06-24 15:31:08 $
11 * The Contents of this file are made available subject to
12 * the terms of GNU Lesser General Public License Version 2.1.
15 * GNU Lesser General Public License Version 2.1
16 * =============================================
17 * Copyright 2005 by Sun Microsystems, Inc.
18 * 901 San Antonio Road, Palo Alto, CA 94303, USA
20 * This library is free software; you can redistribute it and/or
21 * modify it under the terms of the GNU Lesser General Public
22 * License version 2.1, as published by the Free Software Foundation.
24 * This library is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27 * Lesser General Public License for more details.
29 * You should have received a copy of the GNU Lesser General Public
30 * License along with this library; if not, write to the Free Software
31 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
32 * MA 02111-1307 USA
34 ************************************************************************/
36 // MARKER(update_precomp.py): autogen include statement, do not remove
37 #include "precompiled_drawinglayer.hxx"
39 #include <drawinglayer/processor2d/hittestprocessor2d.hxx>
40 #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
41 #include <drawinglayer/primitive2d/transformprimitive2d.hxx>
42 #include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
43 #include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx>
44 #include <basegfx/polygon/b2dpolygontools.hxx>
45 #include <basegfx/polygon/b2dpolypolygontools.hxx>
46 #include <drawinglayer/primitive2d/alphaprimitive2d.hxx>
47 #include <drawinglayer/primitive2d/maskprimitive2d.hxx>
48 #include <drawinglayer/primitive2d/sceneprimitive2d.hxx>
49 #include <drawinglayer/primitive2d/hittestprimitive2d.hxx>
50 #include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx>
52 //////////////////////////////////////////////////////////////////////////////
54 namespace drawinglayer
56 namespace processor2d
58 HitTestProcessor2D::HitTestProcessor2D(const geometry::ViewInformation2D& rViewInformation,
59 const basegfx::B2DPoint& rLogicHitPosition,
60 double fLogicHitTolerance,
61 bool bHitTextOnly)
62 : BaseProcessor2D(rViewInformation),
63 maDiscreteHitPosition(),
64 mfDiscreteHitTolerance(0.0),
65 mbHit(false),
66 mbHitToleranceUsed(false),
67 mbUseHitTestPrimitiveContent(true),
68 mbHitTextOnly(bHitTextOnly)
70 // init hit tolerance
71 mfDiscreteHitTolerance = fLogicHitTolerance;
73 if(basegfx::fTools::less(mfDiscreteHitTolerance, 0.0))
75 // ensure input parameter for hit tolerance is >= 0.0
76 mfDiscreteHitTolerance = 0.0;
78 else if(basegfx::fTools::more(mfDiscreteHitTolerance, 0.0))
80 // generate discrete hit tolerance
81 mfDiscreteHitTolerance = (getViewInformation2D().getObjectToViewTransformation()
82 * basegfx::B2DVector(mfDiscreteHitTolerance, 0.0)).getLength();
85 // gererate discrete hit position
86 maDiscreteHitPosition = getViewInformation2D().getObjectToViewTransformation() * rLogicHitPosition;
88 // check if HitTolerance is used
89 mbHitToleranceUsed = basegfx::fTools::more(getDiscreteHitTolerance(), 0.0);
92 HitTestProcessor2D::~HitTestProcessor2D()
96 bool HitTestProcessor2D::checkHairlineHitWithTolerance(
97 const basegfx::B2DPolygon& rPolygon,
98 double fDiscreteHitTolerance)
100 basegfx::B2DPolygon aLocalPolygon(rPolygon);
101 aLocalPolygon.transform(getViewInformation2D().getObjectToViewTransformation());
103 // get discrete range
104 basegfx::B2DRange aPolygonRange(aLocalPolygon.getB2DRange());
106 if(basegfx::fTools::more(fDiscreteHitTolerance, 0.0))
108 aPolygonRange.grow(fDiscreteHitTolerance);
111 // do rough range test first
112 if(aPolygonRange.isInside(getDiscreteHitPosition()))
114 // check if a polygon edge is hit
115 return basegfx::tools::isInEpsilonRange(
116 aLocalPolygon,
117 getDiscreteHitPosition(),
118 fDiscreteHitTolerance);
121 return false;
124 bool HitTestProcessor2D::checkFillHitWithTolerance(
125 const basegfx::B2DPolyPolygon& rPolyPolygon,
126 double fDiscreteHitTolerance)
128 bool bRetval(false);
129 basegfx::B2DPolyPolygon aLocalPolyPolygon(rPolyPolygon);
130 aLocalPolyPolygon.transform(getViewInformation2D().getObjectToViewTransformation());
132 // get discrete range
133 basegfx::B2DRange aPolygonRange(aLocalPolyPolygon.getB2DRange());
134 const bool bDiscreteHitToleranceUsed(basegfx::fTools::more(fDiscreteHitTolerance, 0.0));
136 if(bDiscreteHitToleranceUsed)
138 aPolygonRange.grow(fDiscreteHitTolerance);
141 // do rough range test first
142 if(aPolygonRange.isInside(getDiscreteHitPosition()))
144 // if a HitTolerance is given, check for polygon edge hit in epsilon first
145 if(bDiscreteHitToleranceUsed &&
146 basegfx::tools::isInEpsilonRange(
147 aLocalPolyPolygon,
148 getDiscreteHitPosition(),
149 fDiscreteHitTolerance))
151 bRetval = true;
154 // check for hit in filled polyPolygon
155 if(!bRetval && basegfx::tools::isInside(
156 aLocalPolyPolygon,
157 getDiscreteHitPosition(),
158 true))
160 bRetval = true;
164 return bRetval;
167 void HitTestProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate)
169 if(getHit())
171 // stop processing as soon as a hit was recognized
172 return;
175 switch(rCandidate.getPrimitiveID())
177 case PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D :
179 // remember current ViewInformation2D
180 const primitive2d::TransformPrimitive2D& rTransformCandidate(static_cast< const primitive2d::TransformPrimitive2D& >(rCandidate));
181 const geometry::ViewInformation2D aLastViewInformation2D(getViewInformation2D());
183 // create new local ViewInformation2D containing transformation
184 const geometry::ViewInformation2D aViewInformation2D(
185 getViewInformation2D().getObjectTransformation() * rTransformCandidate.getTransformation(),
186 getViewInformation2D().getViewTransformation(),
187 getViewInformation2D().getViewport(),
188 getViewInformation2D().getVisualizedPage(),
189 getViewInformation2D().getViewTime(),
190 getViewInformation2D().getExtendedInformationSequence());
191 updateViewInformation(aViewInformation2D);
193 // proccess child content recursively
194 process(rTransformCandidate.getChildren());
196 // restore transformations
197 updateViewInformation(aLastViewInformation2D);
199 break;
201 case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D :
203 if(!getHitTextOnly())
205 // create hairline in discrete coordinates
206 const primitive2d::PolygonHairlinePrimitive2D& rPolygonCandidate(static_cast< const primitive2d::PolygonHairlinePrimitive2D& >(rCandidate));
208 // use hairline test
209 mbHit = checkHairlineHitWithTolerance(rPolygonCandidate.getB2DPolygon(), getDiscreteHitTolerance());
212 break;
214 case PRIMITIVE2D_ID_POLYGONMARKERPRIMITIVE2D :
216 if(!getHitTextOnly())
218 // handle marker like hairline; no need to decompose in dashes
219 const primitive2d::PolygonMarkerPrimitive2D& rPolygonCandidate(static_cast< const primitive2d::PolygonMarkerPrimitive2D& >(rCandidate));
221 // use hairline test
222 mbHit = checkHairlineHitWithTolerance(rPolygonCandidate.getB2DPolygon(), getDiscreteHitTolerance());
225 break;
227 case PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D :
229 if(!getHitTextOnly())
231 // handle stroke evtl. directly; no need to decompose to filled polygon outlines
232 const primitive2d::PolygonStrokePrimitive2D& rPolygonCandidate(static_cast< const primitive2d::PolygonStrokePrimitive2D& >(rCandidate));
233 const attribute::LineAttribute& rLineAttribute = rPolygonCandidate.getLineAttribute();
235 if(basegfx::fTools::more(rLineAttribute.getWidth(), 0.0))
237 if(basegfx::B2DLINEJOIN_MITER == rLineAttribute.getLineJoin())
239 // if line is mitered, use decomposition since mitered line
240 // geometry may use more space than the geometry grown by half line width
241 process(rCandidate.get2DDecomposition(getViewInformation2D()));
243 else
245 // for all other B2DLINEJOIN_* do a hairline HitTest with expanded tolerance
246 const basegfx::B2DVector aDiscreteHalfLineVector(getViewInformation2D().getObjectToViewTransformation()
247 * basegfx::B2DVector(rLineAttribute.getWidth() * 0.5, 0.0));
248 mbHit = checkHairlineHitWithTolerance(
249 rPolygonCandidate.getB2DPolygon(),
250 getDiscreteHitTolerance() + aDiscreteHalfLineVector.getLength());
253 else
255 // hairline; fallback to hairline test. Do not decompose
256 // since this may decompose the hairline to dashes
257 mbHit = checkHairlineHitWithTolerance(rPolygonCandidate.getB2DPolygon(), getDiscreteHitTolerance());
261 break;
263 case PRIMITIVE2D_ID_POLYGONWAVEPRIMITIVE2D :
265 if(!getHitTextOnly())
267 // do not use decompose; just handle like a line with width
268 const primitive2d::PolygonWavePrimitive2D& rPolygonCandidate(static_cast< const primitive2d::PolygonWavePrimitive2D& >(rCandidate));
269 double fLogicHitTolerance(0.0);
271 // if WaveHeight, grow by it
272 if(basegfx::fTools::more(rPolygonCandidate.getWaveHeight(), 0.0))
274 fLogicHitTolerance += rPolygonCandidate.getWaveHeight();
277 // if line width, grow by it
278 if(basegfx::fTools::more(rPolygonCandidate.getLineAttribute().getWidth(), 0.0))
280 fLogicHitTolerance += rPolygonCandidate.getLineAttribute().getWidth() * 0.5;
283 const basegfx::B2DVector aDiscreteHalfLineVector(getViewInformation2D().getObjectToViewTransformation()
284 * basegfx::B2DVector(fLogicHitTolerance, 0.0));
286 mbHit = checkHairlineHitWithTolerance(
287 rPolygonCandidate.getB2DPolygon(),
288 getDiscreteHitTolerance() + aDiscreteHalfLineVector.getLength());
291 break;
293 case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D :
295 if(!getHitTextOnly())
297 // create filled polyPolygon in discrete coordinates
298 const primitive2d::PolyPolygonColorPrimitive2D& rPolygonCandidate(static_cast< const primitive2d::PolyPolygonColorPrimitive2D& >(rCandidate));
300 // use fill hit test
301 mbHit = checkFillHitWithTolerance(rPolygonCandidate.getB2DPolyPolygon(), getDiscreteHitTolerance());
304 break;
306 case PRIMITIVE2D_ID_ALPHAPRIMITIVE2D :
308 // sub-transparence group
309 const primitive2d::AlphaPrimitive2D& rTransCandidate(static_cast< const primitive2d::AlphaPrimitive2D& >(rCandidate));
311 // Currently the transparence content is not taken into account; only
312 // the children are recursively checked for hit. This may be refined for
313 // parts where the content is completely transparent if needed.
314 process(rTransCandidate.getChildren());
316 break;
318 case PRIMITIVE2D_ID_MASKPRIMITIVE2D :
320 // create mask in discrete coordinates; only recursively continue
321 // with content when HitTest position is inside the mask
322 const primitive2d::MaskPrimitive2D& rMaskCandidate(static_cast< const primitive2d::MaskPrimitive2D& >(rCandidate));
324 // use fill hit test
325 if(checkFillHitWithTolerance(rMaskCandidate.getMask(), getDiscreteHitTolerance()))
327 // recursively HitTest children
328 process(rMaskCandidate.getChildren());
331 break;
333 case PRIMITIVE2D_ID_SCENEPRIMITIVE2D :
335 if(!getHitTextOnly())
337 // 2D Scene primitive containing 3D stuff; extract 2D contour in world coordinates
338 // This may be refined later to an own 3D HitTest renderer which processes the 3D
339 // geometry directly
340 const primitive2d::ScenePrimitive2D& rScenePrimitive2DCandidate(static_cast< const primitive2d::ScenePrimitive2D& >(rCandidate));
341 const primitive2d::Primitive2DSequence xExtracted2DSceneGeometry(rScenePrimitive2DCandidate.getGeometry2D(getViewInformation2D()));
343 if(xExtracted2DSceneGeometry.hasElements())
345 // proccess extracted 2D content
346 process(xExtracted2DSceneGeometry);
348 else
350 // empty 3D scene; Check for border hit
351 const basegfx::B2DRange aRange(rCandidate.getB2DRange(getViewInformation2D()));
352 basegfx::B2DPolygon aOutline(basegfx::tools::createPolygonFromRect(aRange));
354 mbHit = checkHairlineHitWithTolerance(aOutline, getDiscreteHitTolerance());
358 break;
360 case PRIMITIVE2D_ID_WRONGSPELLPRIMITIVE2D :
361 case PRIMITIVE2D_ID_MARKERARRAYPRIMITIVE2D :
362 case PRIMITIVE2D_ID_GRIDPRIMITIVE2D :
363 case PRIMITIVE2D_ID_HELPLINEPRIMITIVE2D :
365 // ignorable primitives
366 break;
368 case PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D :
369 case PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D :
371 // for text use the BoundRect of the primitive itself
372 const basegfx::B2DRange aRange(rCandidate.getB2DRange(getViewInformation2D()));
373 basegfx::B2DPolygon aOutline(basegfx::tools::createPolygonFromRect(aRange));
375 mbHit = checkFillHitWithTolerance(basegfx::B2DPolyPolygon(aOutline), getDiscreteHitTolerance());
377 break;
379 case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D :
380 case PRIMITIVE2D_ID_METAFILEPRIMITIVE2D :
381 case PRIMITIVE2D_ID_CONTROLPRIMITIVE2D :
382 case PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D :
383 case PRIMITIVE2D_ID_FILLHATCHPRIMITIVE2D :
384 case PRIMITIVE2D_ID_PAGEPREVIEWPRIMITIVE2D :
386 if(!getHitTextOnly())
388 // Class of primitives for which just the BoundRect of the primitive itself
389 // will be used for HitTest currently.
391 // This may be refined in the future, e.g:
392 // - For Bitamps, the mask and/or alpha information may be used
393 // - For MetaFiles, the MetaFile content may be used
394 const basegfx::B2DRange aRange(rCandidate.getB2DRange(getViewInformation2D()));
395 basegfx::B2DPolygon aOutline(basegfx::tools::createPolygonFromRect(aRange));
397 mbHit = checkFillHitWithTolerance(basegfx::B2DPolyPolygon(aOutline), getDiscreteHitTolerance());
400 break;
402 case PRIMITIVE2D_ID_HITTESTPRIMITIVE2D :
404 // HitTest primitive; the default decomposition would return an empty seqence,
405 // so force this primitive to process it's children directly if the switch is set
406 // (which is the default). Else, ignore invisible content
407 if(getUseHitTestPrimitiveContent())
409 const primitive2d::HitTestPrimitive2D& rHitTestCandidate(static_cast< const primitive2d::HitTestPrimitive2D& >(rCandidate));
410 process(rHitTestCandidate.getChildren());
413 break;
415 case PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D :
417 if(!getHitTextOnly())
419 const primitive2d::PointArrayPrimitive2D& rPointArrayCandidate(static_cast< const primitive2d::PointArrayPrimitive2D& >(rCandidate));
420 const std::vector< basegfx::B2DPoint >& rPositions = rPointArrayCandidate.getPositions();
421 const sal_uInt32 nCount(rPositions.size());
423 for(sal_uInt32 a(0); !getHit() && a < nCount; a++)
425 const basegfx::B2DPoint aPosition(getViewInformation2D().getObjectToViewTransformation() * rPositions[a]);
426 const basegfx::B2DVector aDistance(aPosition - getDiscreteHitPosition());
428 if(aDistance.getLength() <= getDiscreteHitTolerance())
430 mbHit = true;
435 break;
437 default :
439 // process recursively
440 process(rCandidate.get2DDecomposition(getViewInformation2D()));
442 break;
447 } // end of namespace processor2d
448 } // end of namespace drawinglayer
450 //////////////////////////////////////////////////////////////////////////////
451 // eof