1 /*************************************************************************
3 * OpenOffice.org - a multi-platform office productivity suite
5 * $RCSfile: contourextractor2d.cxx,v $
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,
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
58 HitTestProcessor2D::HitTestProcessor2D(const geometry::ViewInformation2D
& rViewInformation
,
59 const basegfx::B2DPoint
& rLogicHitPosition
,
60 double fLogicHitTolerance
,
62 : BaseProcessor2D(rViewInformation
),
63 maDiscreteHitPosition(),
64 mfDiscreteHitTolerance(0.0),
66 mbHitToleranceUsed(false),
67 mbUseHitTestPrimitiveContent(true),
68 mbHitTextOnly(bHitTextOnly
)
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(
117 getDiscreteHitPosition(),
118 fDiscreteHitTolerance
);
124 bool HitTestProcessor2D::checkFillHitWithTolerance(
125 const basegfx::B2DPolyPolygon
& rPolyPolygon
,
126 double fDiscreteHitTolerance
)
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(
148 getDiscreteHitPosition(),
149 fDiscreteHitTolerance
))
154 // check for hit in filled polyPolygon
155 if(!bRetval
&& basegfx::tools::isInside(
157 getDiscreteHitPosition(),
167 void HitTestProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D
& rCandidate
)
171 // stop processing as soon as a hit was recognized
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
);
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
));
209 mbHit
= checkHairlineHitWithTolerance(rPolygonCandidate
.getB2DPolygon(), getDiscreteHitTolerance());
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
));
222 mbHit
= checkHairlineHitWithTolerance(rPolygonCandidate
.getB2DPolygon(), getDiscreteHitTolerance());
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()));
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());
255 // hairline; fallback to hairline test. Do not decompose
256 // since this may decompose the hairline to dashes
257 mbHit
= checkHairlineHitWithTolerance(rPolygonCandidate
.getB2DPolygon(), getDiscreteHitTolerance());
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());
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
));
301 mbHit
= checkFillHitWithTolerance(rPolygonCandidate
.getB2DPolyPolygon(), getDiscreteHitTolerance());
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());
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
));
325 if(checkFillHitWithTolerance(rMaskCandidate
.getMask(), getDiscreteHitTolerance()))
327 // recursively HitTest children
328 process(rMaskCandidate
.getChildren());
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
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
);
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());
360 case PRIMITIVE2D_ID_WRONGSPELLPRIMITIVE2D
:
361 case PRIMITIVE2D_ID_MARKERARRAYPRIMITIVE2D
:
362 case PRIMITIVE2D_ID_GRIDPRIMITIVE2D
:
363 case PRIMITIVE2D_ID_HELPLINEPRIMITIVE2D
:
365 // ignorable primitives
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());
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());
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());
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())
439 // process recursively
440 process(rCandidate
.get2DDecomposition(getViewInformation2D()));
447 } // end of namespace processor2d
448 } // end of namespace drawinglayer
450 //////////////////////////////////////////////////////////////////////////////