xref: /AOO41X/main/basegfx/source/polygon/b2dpolygonclipper.cxx (revision cdf0e10c4e3984b49a9502b011690b615761d4a3)
1*cdf0e10cSrcweir /*************************************************************************
2*cdf0e10cSrcweir  *
3*cdf0e10cSrcweir  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4*cdf0e10cSrcweir  *
5*cdf0e10cSrcweir  * Copyright 2000, 2010 Oracle and/or its affiliates.
6*cdf0e10cSrcweir  *
7*cdf0e10cSrcweir  * OpenOffice.org - a multi-platform office productivity suite
8*cdf0e10cSrcweir  *
9*cdf0e10cSrcweir  * This file is part of OpenOffice.org.
10*cdf0e10cSrcweir  *
11*cdf0e10cSrcweir  * OpenOffice.org is free software: you can redistribute it and/or modify
12*cdf0e10cSrcweir  * it under the terms of the GNU Lesser General Public License version 3
13*cdf0e10cSrcweir  * only, as published by the Free Software Foundation.
14*cdf0e10cSrcweir  *
15*cdf0e10cSrcweir  * OpenOffice.org is distributed in the hope that it will be useful,
16*cdf0e10cSrcweir  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17*cdf0e10cSrcweir  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18*cdf0e10cSrcweir  * GNU Lesser General Public License version 3 for more details
19*cdf0e10cSrcweir  * (a copy is included in the LICENSE file that accompanied this code).
20*cdf0e10cSrcweir  *
21*cdf0e10cSrcweir  * You should have received a copy of the GNU Lesser General Public License
22*cdf0e10cSrcweir  * version 3 along with OpenOffice.org.  If not, see
23*cdf0e10cSrcweir  * <http://www.openoffice.org/license.html>
24*cdf0e10cSrcweir  * for a copy of the LGPLv3 License.
25*cdf0e10cSrcweir  *
26*cdf0e10cSrcweir  ************************************************************************/
27*cdf0e10cSrcweir 
28*cdf0e10cSrcweir // MARKER(update_precomp.py): autogen include statement, do not remove
29*cdf0e10cSrcweir #include "precompiled_basegfx.hxx"
30*cdf0e10cSrcweir #include <basegfx/polygon/b2dpolygonclipper.hxx>
31*cdf0e10cSrcweir #include <osl/diagnose.h>
32*cdf0e10cSrcweir #include <basegfx/polygon/b2dpolygontools.hxx>
33*cdf0e10cSrcweir #include <basegfx/numeric/ftools.hxx>
34*cdf0e10cSrcweir #include <basegfx/matrix/b2dhommatrix.hxx>
35*cdf0e10cSrcweir #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
36*cdf0e10cSrcweir #include <basegfx/polygon/b2dpolygoncutandtouch.hxx>
37*cdf0e10cSrcweir #include <basegfx/polygon/b2dpolypolygontools.hxx>
38*cdf0e10cSrcweir #include <basegfx/curve/b2dcubicbezier.hxx>
39*cdf0e10cSrcweir #include <basegfx/tools/rectcliptools.hxx>
40*cdf0e10cSrcweir #include <basegfx/matrix/b2dhommatrixtools.hxx>
41*cdf0e10cSrcweir 
42*cdf0e10cSrcweir //////////////////////////////////////////////////////////////////////////////
43*cdf0e10cSrcweir 
44*cdf0e10cSrcweir namespace basegfx
45*cdf0e10cSrcweir {
46*cdf0e10cSrcweir 	namespace tools
47*cdf0e10cSrcweir 	{
48*cdf0e10cSrcweir 		B2DPolyPolygon clipPolygonOnParallelAxis(const B2DPolygon& rCandidate, bool bParallelToXAxis, bool bAboveAxis, double fValueOnOtherAxis, bool bStroke)
49*cdf0e10cSrcweir 		{
50*cdf0e10cSrcweir 			B2DPolyPolygon aRetval;
51*cdf0e10cSrcweir 
52*cdf0e10cSrcweir 			if(rCandidate.count())
53*cdf0e10cSrcweir 			{
54*cdf0e10cSrcweir 				const B2DRange aCandidateRange(getRange(rCandidate));
55*cdf0e10cSrcweir 
56*cdf0e10cSrcweir 				if(bParallelToXAxis && fTools::moreOrEqual(aCandidateRange.getMinY(), fValueOnOtherAxis))
57*cdf0e10cSrcweir 				{
58*cdf0e10cSrcweir 					// completely above and on the clip line. also true for curves.
59*cdf0e10cSrcweir 					if(bAboveAxis)
60*cdf0e10cSrcweir 					{
61*cdf0e10cSrcweir 						// add completely
62*cdf0e10cSrcweir 						aRetval.append(rCandidate);
63*cdf0e10cSrcweir 					}
64*cdf0e10cSrcweir 				}
65*cdf0e10cSrcweir 				else if(bParallelToXAxis && fTools::lessOrEqual(aCandidateRange.getMaxY(), fValueOnOtherAxis))
66*cdf0e10cSrcweir 				{
67*cdf0e10cSrcweir 					// completely below and on the clip line. also true for curves.
68*cdf0e10cSrcweir 					if(!bAboveAxis)
69*cdf0e10cSrcweir 					{
70*cdf0e10cSrcweir 						// add completely
71*cdf0e10cSrcweir 						aRetval.append(rCandidate);
72*cdf0e10cSrcweir 					}
73*cdf0e10cSrcweir 				}
74*cdf0e10cSrcweir 				else if(!bParallelToXAxis && fTools::moreOrEqual(aCandidateRange.getMinX(), fValueOnOtherAxis))
75*cdf0e10cSrcweir 				{
76*cdf0e10cSrcweir 					// completely right of and on the clip line. also true for curves.
77*cdf0e10cSrcweir 					if(bAboveAxis)
78*cdf0e10cSrcweir 					{
79*cdf0e10cSrcweir 						// add completely
80*cdf0e10cSrcweir 						aRetval.append(rCandidate);
81*cdf0e10cSrcweir 					}
82*cdf0e10cSrcweir 				}
83*cdf0e10cSrcweir 				else if(!bParallelToXAxis && fTools::lessOrEqual(aCandidateRange.getMaxX(), fValueOnOtherAxis))
84*cdf0e10cSrcweir 				{
85*cdf0e10cSrcweir 					// completely left of and on the clip line. also true for curves.
86*cdf0e10cSrcweir 					if(!bAboveAxis)
87*cdf0e10cSrcweir 					{
88*cdf0e10cSrcweir 						// add completely
89*cdf0e10cSrcweir 						aRetval.append(rCandidate);
90*cdf0e10cSrcweir 					}
91*cdf0e10cSrcweir 				}
92*cdf0e10cSrcweir 				else
93*cdf0e10cSrcweir 				{
94*cdf0e10cSrcweir                     // add cuts with axis to polygon, including bezier segments
95*cdf0e10cSrcweir                     // Build edge to cut with. Make it a little big longer than needed for
96*cdf0e10cSrcweir                     // numerical stability. We want to cut against the edge seen as endless
97*cdf0e10cSrcweir                     // ray here, but addPointsAtCuts() will limit itself to the
98*cdf0e10cSrcweir                     // edge's range ]0.0 .. 1.0[.
99*cdf0e10cSrcweir                     const double fSmallExtension((aCandidateRange.getWidth() + aCandidateRange.getHeight()) * (0.5 * 0.1));
100*cdf0e10cSrcweir                     const B2DPoint aStart(
101*cdf0e10cSrcweir                         bParallelToXAxis ? aCandidateRange.getMinX() - fSmallExtension : fValueOnOtherAxis,
102*cdf0e10cSrcweir                         bParallelToXAxis ? fValueOnOtherAxis : aCandidateRange.getMinY() - fSmallExtension);
103*cdf0e10cSrcweir                     const B2DPoint aEnd(
104*cdf0e10cSrcweir                         bParallelToXAxis ? aCandidateRange.getMaxX() + fSmallExtension : fValueOnOtherAxis,
105*cdf0e10cSrcweir                         bParallelToXAxis ? fValueOnOtherAxis : aCandidateRange.getMaxY() + fSmallExtension);
106*cdf0e10cSrcweir                     const B2DPolygon aCandidate(addPointsAtCuts(rCandidate, aStart, aEnd));
107*cdf0e10cSrcweir     			    const sal_uInt32 nPointCount(aCandidate.count());
108*cdf0e10cSrcweir                     const sal_uInt32 nEdgeCount(aCandidate.isClosed() ? nPointCount : nPointCount - 1L);
109*cdf0e10cSrcweir                     B2DCubicBezier aEdge;
110*cdf0e10cSrcweir                     B2DPolygon aRun;
111*cdf0e10cSrcweir 
112*cdf0e10cSrcweir                     for(sal_uInt32 a(0L); a < nEdgeCount; a++)
113*cdf0e10cSrcweir                     {
114*cdf0e10cSrcweir                         aCandidate.getBezierSegment(a, aEdge);
115*cdf0e10cSrcweir                         const B2DPoint aTestPoint(aEdge.interpolatePoint(0.5));
116*cdf0e10cSrcweir 			            const bool bInside(bParallelToXAxis ?
117*cdf0e10cSrcweir 				            fTools::moreOrEqual(aTestPoint.getY(), fValueOnOtherAxis) == bAboveAxis :
118*cdf0e10cSrcweir 				            fTools::moreOrEqual(aTestPoint.getX(), fValueOnOtherAxis) == bAboveAxis);
119*cdf0e10cSrcweir 
120*cdf0e10cSrcweir 						if(bInside)
121*cdf0e10cSrcweir 						{
122*cdf0e10cSrcweir 							if(!aRun.count() || !aRun.getB2DPoint(aRun.count() - 1).equal(aEdge.getStartPoint()))
123*cdf0e10cSrcweir 							{
124*cdf0e10cSrcweir 								aRun.append(aEdge.getStartPoint());
125*cdf0e10cSrcweir 							}
126*cdf0e10cSrcweir 
127*cdf0e10cSrcweir 							if(aEdge.isBezier())
128*cdf0e10cSrcweir 							{
129*cdf0e10cSrcweir 								aRun.appendBezierSegment(aEdge.getControlPointA(), aEdge.getControlPointB(), aEdge.getEndPoint());
130*cdf0e10cSrcweir 							}
131*cdf0e10cSrcweir 							else
132*cdf0e10cSrcweir 							{
133*cdf0e10cSrcweir 								aRun.append(aEdge.getEndPoint());
134*cdf0e10cSrcweir 							}
135*cdf0e10cSrcweir 						}
136*cdf0e10cSrcweir 						else
137*cdf0e10cSrcweir 						{
138*cdf0e10cSrcweir                             if(bStroke && aRun.count())
139*cdf0e10cSrcweir                             {
140*cdf0e10cSrcweir 								aRetval.append(aRun);
141*cdf0e10cSrcweir 								aRun.clear();
142*cdf0e10cSrcweir                             }
143*cdf0e10cSrcweir 						}
144*cdf0e10cSrcweir                     }
145*cdf0e10cSrcweir 
146*cdf0e10cSrcweir                     if(aRun.count())
147*cdf0e10cSrcweir 					{
148*cdf0e10cSrcweir                         if(bStroke)
149*cdf0e10cSrcweir                         {
150*cdf0e10cSrcweir                             // try to merge this last and first polygon; they may have been
151*cdf0e10cSrcweir                             // the former polygon's start/end point
152*cdf0e10cSrcweir                             if(aRetval.count())
153*cdf0e10cSrcweir                             {
154*cdf0e10cSrcweir                                 const B2DPolygon aStartPolygon(aRetval.getB2DPolygon(0));
155*cdf0e10cSrcweir 
156*cdf0e10cSrcweir                                 if(aStartPolygon.count() && aStartPolygon.getB2DPoint(0).equal(aRun.getB2DPoint(aRun.count() - 1)))
157*cdf0e10cSrcweir                                 {
158*cdf0e10cSrcweir                                     // append start polygon to aRun, remove from result set
159*cdf0e10cSrcweir                                     aRun.append(aStartPolygon); aRun.removeDoublePoints();
160*cdf0e10cSrcweir                                     aRetval.remove(0);
161*cdf0e10cSrcweir                                 }
162*cdf0e10cSrcweir                             }
163*cdf0e10cSrcweir 
164*cdf0e10cSrcweir 							aRetval.append(aRun);
165*cdf0e10cSrcweir                         }
166*cdf0e10cSrcweir                         else
167*cdf0e10cSrcweir                         {
168*cdf0e10cSrcweir 			                // set closed flag and correct last point (which is added double now).
169*cdf0e10cSrcweir 			                closeWithGeometryChange(aRun);
170*cdf0e10cSrcweir                             aRetval.append(aRun);
171*cdf0e10cSrcweir                         }
172*cdf0e10cSrcweir 					}
173*cdf0e10cSrcweir 				}
174*cdf0e10cSrcweir 			}
175*cdf0e10cSrcweir 
176*cdf0e10cSrcweir 			return aRetval;
177*cdf0e10cSrcweir 		}
178*cdf0e10cSrcweir 
179*cdf0e10cSrcweir 		B2DPolyPolygon clipPolyPolygonOnParallelAxis(const B2DPolyPolygon& rCandidate, bool bParallelToXAxis, bool bAboveAxis, double fValueOnOtherAxis, bool bStroke)
180*cdf0e10cSrcweir 		{
181*cdf0e10cSrcweir 			const sal_uInt32 nPolygonCount(rCandidate.count());
182*cdf0e10cSrcweir 			B2DPolyPolygon aRetval;
183*cdf0e10cSrcweir 
184*cdf0e10cSrcweir 			for(sal_uInt32 a(0L); a < nPolygonCount; a++)
185*cdf0e10cSrcweir 			{
186*cdf0e10cSrcweir 				const B2DPolyPolygon aClippedPolyPolygon(clipPolygonOnParallelAxis(rCandidate.getB2DPolygon(a), bParallelToXAxis, bAboveAxis, fValueOnOtherAxis, bStroke));
187*cdf0e10cSrcweir 
188*cdf0e10cSrcweir                 if(aClippedPolyPolygon.count())
189*cdf0e10cSrcweir                 {
190*cdf0e10cSrcweir     				aRetval.append(aClippedPolyPolygon);
191*cdf0e10cSrcweir                 }
192*cdf0e10cSrcweir 			}
193*cdf0e10cSrcweir 
194*cdf0e10cSrcweir 			return aRetval;
195*cdf0e10cSrcweir 		}
196*cdf0e10cSrcweir 
197*cdf0e10cSrcweir 		B2DPolyPolygon clipPolygonOnRange(const B2DPolygon& rCandidate, const B2DRange& rRange, bool bInside, bool bStroke)
198*cdf0e10cSrcweir 		{
199*cdf0e10cSrcweir             const sal_uInt32 nCount(rCandidate.count());
200*cdf0e10cSrcweir 			B2DPolyPolygon aRetval;
201*cdf0e10cSrcweir 
202*cdf0e10cSrcweir             if(!nCount)
203*cdf0e10cSrcweir             {
204*cdf0e10cSrcweir                 // source is empty
205*cdf0e10cSrcweir                 return aRetval;
206*cdf0e10cSrcweir             }
207*cdf0e10cSrcweir 
208*cdf0e10cSrcweir             if(rRange.isEmpty())
209*cdf0e10cSrcweir             {
210*cdf0e10cSrcweir                 if(bInside)
211*cdf0e10cSrcweir                 {
212*cdf0e10cSrcweir                     // nothing is inside an empty range
213*cdf0e10cSrcweir                     return aRetval;
214*cdf0e10cSrcweir                 }
215*cdf0e10cSrcweir                 else
216*cdf0e10cSrcweir                 {
217*cdf0e10cSrcweir                     // everything is outside an empty range
218*cdf0e10cSrcweir                     return B2DPolyPolygon(rCandidate);
219*cdf0e10cSrcweir                 }
220*cdf0e10cSrcweir             }
221*cdf0e10cSrcweir 
222*cdf0e10cSrcweir 			const B2DRange aCandidateRange(getRange(rCandidate));
223*cdf0e10cSrcweir 
224*cdf0e10cSrcweir 			if(rRange.isInside(aCandidateRange))
225*cdf0e10cSrcweir 			{
226*cdf0e10cSrcweir   				// candidate is completely inside given range
227*cdf0e10cSrcweir 				if(bInside)
228*cdf0e10cSrcweir 				{
229*cdf0e10cSrcweir     				// nothing to do
230*cdf0e10cSrcweir 					return B2DPolyPolygon(rCandidate);
231*cdf0e10cSrcweir 				}
232*cdf0e10cSrcweir                 else
233*cdf0e10cSrcweir                 {
234*cdf0e10cSrcweir                     // nothing is outside, then
235*cdf0e10cSrcweir                     return aRetval;
236*cdf0e10cSrcweir                 }
237*cdf0e10cSrcweir 			}
238*cdf0e10cSrcweir 
239*cdf0e10cSrcweir             if(!bInside)
240*cdf0e10cSrcweir             {
241*cdf0e10cSrcweir                 // cutting off the outer parts of filled polygons at parallell
242*cdf0e10cSrcweir                 // lines to the axes is only possible for the inner part, not for
243*cdf0e10cSrcweir                 // the outer part which means cutting a hole into the original polygon.
244*cdf0e10cSrcweir                 // This is because the inner part is a logical AND-operation of
245*cdf0e10cSrcweir                 // the four implied half-planes, but the outer part is not.
246*cdf0e10cSrcweir                 // It is possible for strokes, but with creating unnecessary extra
247*cdf0e10cSrcweir                 // cuts, so using clipPolygonOnPolyPolygon is better there, too.
248*cdf0e10cSrcweir                 // This needs to be done with the topology knowlegde and is unfurtunately
249*cdf0e10cSrcweir                 // more expensive, too.
250*cdf0e10cSrcweir         		const B2DPolygon aClip(createPolygonFromRect(rRange));
251*cdf0e10cSrcweir 
252*cdf0e10cSrcweir                 return clipPolygonOnPolyPolygon(rCandidate, B2DPolyPolygon(aClip), bInside, bStroke);
253*cdf0e10cSrcweir             }
254*cdf0e10cSrcweir 
255*cdf0e10cSrcweir 			// clip against the four axes of the range
256*cdf0e10cSrcweir 			// against X-Axis, lower value
257*cdf0e10cSrcweir 			aRetval = clipPolygonOnParallelAxis(rCandidate, true, bInside, rRange.getMinY(), bStroke);
258*cdf0e10cSrcweir 
259*cdf0e10cSrcweir 			if(aRetval.count())
260*cdf0e10cSrcweir 			{
261*cdf0e10cSrcweir 				// against Y-Axis, lower value
262*cdf0e10cSrcweir 				if(1L == aRetval.count())
263*cdf0e10cSrcweir 				{
264*cdf0e10cSrcweir 					aRetval = clipPolygonOnParallelAxis(aRetval.getB2DPolygon(0L), false, bInside, rRange.getMinX(), bStroke);
265*cdf0e10cSrcweir 				}
266*cdf0e10cSrcweir 				else
267*cdf0e10cSrcweir 				{
268*cdf0e10cSrcweir 					aRetval = clipPolyPolygonOnParallelAxis(aRetval, false, bInside, rRange.getMinX(), bStroke);
269*cdf0e10cSrcweir 				}
270*cdf0e10cSrcweir 
271*cdf0e10cSrcweir 				if(aRetval.count())
272*cdf0e10cSrcweir 				{
273*cdf0e10cSrcweir 					// against X-Axis, higher value
274*cdf0e10cSrcweir 					if(1L == aRetval.count())
275*cdf0e10cSrcweir 					{
276*cdf0e10cSrcweir 						aRetval = clipPolygonOnParallelAxis(aRetval.getB2DPolygon(0L), true, !bInside, rRange.getMaxY(), bStroke);
277*cdf0e10cSrcweir 					}
278*cdf0e10cSrcweir 					else
279*cdf0e10cSrcweir 					{
280*cdf0e10cSrcweir 						aRetval = clipPolyPolygonOnParallelAxis(aRetval, true, !bInside, rRange.getMaxY(), bStroke);
281*cdf0e10cSrcweir 					}
282*cdf0e10cSrcweir 
283*cdf0e10cSrcweir 					if(aRetval.count())
284*cdf0e10cSrcweir 					{
285*cdf0e10cSrcweir 						// against Y-Axis, higher value
286*cdf0e10cSrcweir 						if(1L == aRetval.count())
287*cdf0e10cSrcweir 						{
288*cdf0e10cSrcweir 							aRetval = clipPolygonOnParallelAxis(aRetval.getB2DPolygon(0L), false, !bInside, rRange.getMaxX(), bStroke);
289*cdf0e10cSrcweir 						}
290*cdf0e10cSrcweir 						else
291*cdf0e10cSrcweir 						{
292*cdf0e10cSrcweir 							aRetval = clipPolyPolygonOnParallelAxis(aRetval, false, !bInside, rRange.getMaxX(), bStroke);
293*cdf0e10cSrcweir 						}
294*cdf0e10cSrcweir 					}
295*cdf0e10cSrcweir 				}
296*cdf0e10cSrcweir 			}
297*cdf0e10cSrcweir 
298*cdf0e10cSrcweir 			return aRetval;
299*cdf0e10cSrcweir 		}
300*cdf0e10cSrcweir 
301*cdf0e10cSrcweir 		B2DPolyPolygon clipPolyPolygonOnRange(const B2DPolyPolygon& rCandidate, const B2DRange& rRange, bool bInside, bool bStroke)
302*cdf0e10cSrcweir 		{
303*cdf0e10cSrcweir 			const sal_uInt32 nPolygonCount(rCandidate.count());
304*cdf0e10cSrcweir 			B2DPolyPolygon aRetval;
305*cdf0e10cSrcweir 
306*cdf0e10cSrcweir             if(!nPolygonCount)
307*cdf0e10cSrcweir             {
308*cdf0e10cSrcweir                 // source is empty
309*cdf0e10cSrcweir                 return aRetval;
310*cdf0e10cSrcweir             }
311*cdf0e10cSrcweir 
312*cdf0e10cSrcweir             if(rRange.isEmpty())
313*cdf0e10cSrcweir             {
314*cdf0e10cSrcweir                 if(bInside)
315*cdf0e10cSrcweir                 {
316*cdf0e10cSrcweir                     // nothing is inside an empty range
317*cdf0e10cSrcweir                     return aRetval;
318*cdf0e10cSrcweir                 }
319*cdf0e10cSrcweir                 else
320*cdf0e10cSrcweir                 {
321*cdf0e10cSrcweir                     // everything is outside an empty range
322*cdf0e10cSrcweir                     return rCandidate;
323*cdf0e10cSrcweir                 }
324*cdf0e10cSrcweir             }
325*cdf0e10cSrcweir 
326*cdf0e10cSrcweir             if(bInside)
327*cdf0e10cSrcweir             {
328*cdf0e10cSrcweir 			    for(sal_uInt32 a(0L); a < nPolygonCount; a++)
329*cdf0e10cSrcweir 			    {
330*cdf0e10cSrcweir 				    const B2DPolyPolygon aClippedPolyPolygon(clipPolygonOnRange(rCandidate.getB2DPolygon(a), rRange, bInside, bStroke));
331*cdf0e10cSrcweir 
332*cdf0e10cSrcweir                     if(aClippedPolyPolygon.count())
333*cdf0e10cSrcweir                     {
334*cdf0e10cSrcweir     				    aRetval.append(aClippedPolyPolygon);
335*cdf0e10cSrcweir                     }
336*cdf0e10cSrcweir 			    }
337*cdf0e10cSrcweir             }
338*cdf0e10cSrcweir             else
339*cdf0e10cSrcweir             {
340*cdf0e10cSrcweir                 // for details, see comment in clipPolygonOnRange for the "cutting off
341*cdf0e10cSrcweir                 // the outer parts of filled polygons at parallell lines" explanations
342*cdf0e10cSrcweir         		const B2DPolygon aClip(createPolygonFromRect(rRange));
343*cdf0e10cSrcweir 
344*cdf0e10cSrcweir                 return clipPolyPolygonOnPolyPolygon(rCandidate, B2DPolyPolygon(aClip), bInside, bStroke);
345*cdf0e10cSrcweir             }
346*cdf0e10cSrcweir 
347*cdf0e10cSrcweir 			return aRetval;
348*cdf0e10cSrcweir 		}
349*cdf0e10cSrcweir 
350*cdf0e10cSrcweir 		B2DPolyPolygon clipPolygonOnEdge(const B2DPolygon& rCandidate, const B2DPoint& rPointA, const B2DPoint& rPointB, bool bAbove, bool bStroke)
351*cdf0e10cSrcweir 		{
352*cdf0e10cSrcweir 			B2DPolyPolygon aRetval;
353*cdf0e10cSrcweir 
354*cdf0e10cSrcweir 			if(rPointA.equal(rPointB))
355*cdf0e10cSrcweir 			{
356*cdf0e10cSrcweir 				// edge has no length, return polygon
357*cdf0e10cSrcweir 				aRetval.append(rCandidate);
358*cdf0e10cSrcweir 			}
359*cdf0e10cSrcweir 			else if(rCandidate.count())
360*cdf0e10cSrcweir 			{
361*cdf0e10cSrcweir 				const B2DVector aEdge(rPointB - rPointA);
362*cdf0e10cSrcweir 				B2DPolygon aCandidate(rCandidate);
363*cdf0e10cSrcweir 
364*cdf0e10cSrcweir 				// translate and rotate polygon so that given edge is on x axis
365*cdf0e10cSrcweir                 B2DHomMatrix aMatrixTransform(basegfx::tools::createTranslateB2DHomMatrix(-rPointA.getX(), -rPointA.getY()));
366*cdf0e10cSrcweir 				aMatrixTransform.rotate(-atan2(aEdge.getY(), aEdge.getX()));
367*cdf0e10cSrcweir 				aCandidate.transform(aMatrixTransform);
368*cdf0e10cSrcweir 
369*cdf0e10cSrcweir 				// call clip method on X-Axis
370*cdf0e10cSrcweir 				aRetval = clipPolygonOnParallelAxis(aCandidate, true, bAbove, 0.0, bStroke);
371*cdf0e10cSrcweir 
372*cdf0e10cSrcweir 				if(aRetval.count())
373*cdf0e10cSrcweir 				{
374*cdf0e10cSrcweir 					// if there is a result, it needs to be transformed back
375*cdf0e10cSrcweir 					aMatrixTransform.invert();
376*cdf0e10cSrcweir 					aRetval.transform(aMatrixTransform);
377*cdf0e10cSrcweir 				}
378*cdf0e10cSrcweir 			}
379*cdf0e10cSrcweir 
380*cdf0e10cSrcweir 			return aRetval;
381*cdf0e10cSrcweir 		}
382*cdf0e10cSrcweir 
383*cdf0e10cSrcweir 		B2DPolyPolygon clipPolyPolygonOnEdge(const B2DPolyPolygon& rCandidate, const B2DPoint& rPointA, const B2DPoint& rPointB, bool bAbove, bool bStroke)
384*cdf0e10cSrcweir 		{
385*cdf0e10cSrcweir 			B2DPolyPolygon aRetval;
386*cdf0e10cSrcweir 
387*cdf0e10cSrcweir 			if(rPointA.equal(rPointB))
388*cdf0e10cSrcweir 			{
389*cdf0e10cSrcweir 				// edge has no length, return polygon
390*cdf0e10cSrcweir 				aRetval = rCandidate;
391*cdf0e10cSrcweir 			}
392*cdf0e10cSrcweir 			else if(rCandidate.count())
393*cdf0e10cSrcweir 			{
394*cdf0e10cSrcweir 				const B2DVector aEdge(rPointB - rPointA);
395*cdf0e10cSrcweir 				B2DPolyPolygon aCandidate(rCandidate);
396*cdf0e10cSrcweir 
397*cdf0e10cSrcweir 				// translate and rotate polygon so that given edge is on x axis
398*cdf0e10cSrcweir                 B2DHomMatrix aMatrixTransform(basegfx::tools::createTranslateB2DHomMatrix(-rPointA.getX(), -rPointA.getY()));
399*cdf0e10cSrcweir 				aMatrixTransform.rotate(-atan2(aEdge.getY(), aEdge.getX()));
400*cdf0e10cSrcweir 				aCandidate.transform(aMatrixTransform);
401*cdf0e10cSrcweir 
402*cdf0e10cSrcweir 				// call clip method on X-Axis
403*cdf0e10cSrcweir 				aRetval = clipPolyPolygonOnParallelAxis(aCandidate, true, bAbove, 0.0, bStroke);
404*cdf0e10cSrcweir 
405*cdf0e10cSrcweir 				if(aRetval.count())
406*cdf0e10cSrcweir 				{
407*cdf0e10cSrcweir 					// if there is a result, it needs to be transformed back
408*cdf0e10cSrcweir 					aMatrixTransform.invert();
409*cdf0e10cSrcweir 					aRetval.transform(aMatrixTransform);
410*cdf0e10cSrcweir 				}
411*cdf0e10cSrcweir 			}
412*cdf0e10cSrcweir 
413*cdf0e10cSrcweir 			return aRetval;
414*cdf0e10cSrcweir 		}
415*cdf0e10cSrcweir 
416*cdf0e10cSrcweir 		//////////////////////////////////////////////////////////////////////////////
417*cdf0e10cSrcweir 
418*cdf0e10cSrcweir 		B2DPolyPolygon clipPolyPolygonOnPolyPolygon(const B2DPolyPolygon& rCandidate, const B2DPolyPolygon& rClip, bool bInside, bool bStroke)
419*cdf0e10cSrcweir 		{
420*cdf0e10cSrcweir 			B2DPolyPolygon aRetval;
421*cdf0e10cSrcweir 
422*cdf0e10cSrcweir 			if(rCandidate.count() && rClip.count())
423*cdf0e10cSrcweir 			{
424*cdf0e10cSrcweir 				if(bStroke)
425*cdf0e10cSrcweir 				{
426*cdf0e10cSrcweir 					// line clipping, create line snippets by first adding all cut points and
427*cdf0e10cSrcweir                     // then marching along the edges and detecting if they are inside or outside
428*cdf0e10cSrcweir                     // the clip polygon
429*cdf0e10cSrcweir 					for(sal_uInt32 a(0); a < rCandidate.count(); a++)
430*cdf0e10cSrcweir 					{
431*cdf0e10cSrcweir                         // add cuts with clip to polygon, including bezier segments
432*cdf0e10cSrcweir                         const B2DPolygon aCandidate(addPointsAtCuts(rCandidate.getB2DPolygon(a), rClip));
433*cdf0e10cSrcweir     			        const sal_uInt32 nPointCount(aCandidate.count());
434*cdf0e10cSrcweir                         const sal_uInt32 nEdgeCount(aCandidate.isClosed() ? nPointCount : nPointCount - 1L);
435*cdf0e10cSrcweir                         B2DCubicBezier aEdge;
436*cdf0e10cSrcweir                         B2DPolygon aRun;
437*cdf0e10cSrcweir 
438*cdf0e10cSrcweir                         for(sal_uInt32 b(0); b < nEdgeCount; b++)
439*cdf0e10cSrcweir                         {
440*cdf0e10cSrcweir                             aCandidate.getBezierSegment(b, aEdge);
441*cdf0e10cSrcweir                             const B2DPoint aTestPoint(aEdge.interpolatePoint(0.5));
442*cdf0e10cSrcweir                             const bool bIsInside(tools::isInside(rClip, aTestPoint) == bInside);
443*cdf0e10cSrcweir 
444*cdf0e10cSrcweir 						    if(bIsInside)
445*cdf0e10cSrcweir 						    {
446*cdf0e10cSrcweir 							    if(!aRun.count())
447*cdf0e10cSrcweir 							    {
448*cdf0e10cSrcweir 								    aRun.append(aEdge.getStartPoint());
449*cdf0e10cSrcweir 							    }
450*cdf0e10cSrcweir 
451*cdf0e10cSrcweir 							    if(aEdge.isBezier())
452*cdf0e10cSrcweir 							    {
453*cdf0e10cSrcweir 								    aRun.appendBezierSegment(aEdge.getControlPointA(), aEdge.getControlPointB(), aEdge.getEndPoint());
454*cdf0e10cSrcweir 							    }
455*cdf0e10cSrcweir 							    else
456*cdf0e10cSrcweir 							    {
457*cdf0e10cSrcweir 								    aRun.append(aEdge.getEndPoint());
458*cdf0e10cSrcweir 							    }
459*cdf0e10cSrcweir 						    }
460*cdf0e10cSrcweir 						    else
461*cdf0e10cSrcweir 						    {
462*cdf0e10cSrcweir                                 if(aRun.count())
463*cdf0e10cSrcweir                                 {
464*cdf0e10cSrcweir 								    aRetval.append(aRun);
465*cdf0e10cSrcweir 								    aRun.clear();
466*cdf0e10cSrcweir                                 }
467*cdf0e10cSrcweir 						    }
468*cdf0e10cSrcweir                         }
469*cdf0e10cSrcweir 
470*cdf0e10cSrcweir                         if(aRun.count())
471*cdf0e10cSrcweir 					    {
472*cdf0e10cSrcweir                             // try to merge this last and first polygon; they may have been
473*cdf0e10cSrcweir                             // the former polygon's start/end point
474*cdf0e10cSrcweir                             if(aRetval.count())
475*cdf0e10cSrcweir                             {
476*cdf0e10cSrcweir                                 const B2DPolygon aStartPolygon(aRetval.getB2DPolygon(0));
477*cdf0e10cSrcweir 
478*cdf0e10cSrcweir                                 if(aStartPolygon.count() && aStartPolygon.getB2DPoint(0).equal(aRun.getB2DPoint(aRun.count() - 1)))
479*cdf0e10cSrcweir                                 {
480*cdf0e10cSrcweir                                     // append start polygon to aRun, remove from result set
481*cdf0e10cSrcweir                                     aRun.append(aStartPolygon); aRun.removeDoublePoints();
482*cdf0e10cSrcweir                                     aRetval.remove(0);
483*cdf0e10cSrcweir                                 }
484*cdf0e10cSrcweir                             }
485*cdf0e10cSrcweir 
486*cdf0e10cSrcweir 						    aRetval.append(aRun);
487*cdf0e10cSrcweir 					    }
488*cdf0e10cSrcweir 					}
489*cdf0e10cSrcweir 				}
490*cdf0e10cSrcweir 				else
491*cdf0e10cSrcweir 				{
492*cdf0e10cSrcweir 					// area clipping
493*cdf0e10cSrcweir 					B2DPolyPolygon aMergePolyPolygonA(rClip);
494*cdf0e10cSrcweir 
495*cdf0e10cSrcweir                     // First solve all polygon-self and polygon-polygon intersections.
496*cdf0e10cSrcweir                     // Also get rid of some not-needed polygons (neutral, no area -> when
497*cdf0e10cSrcweir                     // no intersections, these are tubes).
498*cdf0e10cSrcweir                     // Now it is possible to correct the orientations in the cut-free
499*cdf0e10cSrcweir                     // polygons to values corresponding to painting the PolyPolygon with
500*cdf0e10cSrcweir                     // a XOR-WindingRule.
501*cdf0e10cSrcweir                     aMergePolyPolygonA = solveCrossovers(aMergePolyPolygonA);
502*cdf0e10cSrcweir 					aMergePolyPolygonA = stripNeutralPolygons(aMergePolyPolygonA);
503*cdf0e10cSrcweir                     aMergePolyPolygonA = correctOrientations(aMergePolyPolygonA);
504*cdf0e10cSrcweir 
505*cdf0e10cSrcweir 					if(!bInside)
506*cdf0e10cSrcweir 					{
507*cdf0e10cSrcweir                         // if we want to get the outside of the clip polygon, make
508*cdf0e10cSrcweir                         // it a 'Hole' in topological sense
509*cdf0e10cSrcweir 						aMergePolyPolygonA.flip();
510*cdf0e10cSrcweir 					}
511*cdf0e10cSrcweir 
512*cdf0e10cSrcweir 					B2DPolyPolygon aMergePolyPolygonB(rCandidate);
513*cdf0e10cSrcweir 
514*cdf0e10cSrcweir                     // prepare 2nd source polygon in same way
515*cdf0e10cSrcweir                     aMergePolyPolygonB = solveCrossovers(aMergePolyPolygonB);
516*cdf0e10cSrcweir 					aMergePolyPolygonB = stripNeutralPolygons(aMergePolyPolygonB);
517*cdf0e10cSrcweir                     aMergePolyPolygonB = correctOrientations(aMergePolyPolygonB);
518*cdf0e10cSrcweir 
519*cdf0e10cSrcweir                     // to clip against each other, concatenate and solve all
520*cdf0e10cSrcweir                     // polygon-polygon crossovers. polygon-self do not need to
521*cdf0e10cSrcweir                     // be solved again, they were solved in the preparation.
522*cdf0e10cSrcweir 					aRetval.append(aMergePolyPolygonA);
523*cdf0e10cSrcweir 					aRetval.append(aMergePolyPolygonB);
524*cdf0e10cSrcweir 					aRetval = solveCrossovers(aRetval);
525*cdf0e10cSrcweir 
526*cdf0e10cSrcweir                     // now remove neutral polygons (closed, but no area). In a last
527*cdf0e10cSrcweir                     // step throw away all polygons which have a depth of less than 1
528*cdf0e10cSrcweir                     // which means there was no logical AND at their position. For the
529*cdf0e10cSrcweir                     // not-inside solution, the clip was flipped to define it as 'Hole',
530*cdf0e10cSrcweir                     // so the removal rule is different here; remove all with a depth
531*cdf0e10cSrcweir                     // of less than 0 (aka holes).
532*cdf0e10cSrcweir 					aRetval = stripNeutralPolygons(aRetval);
533*cdf0e10cSrcweir 					aRetval = stripDispensablePolygons(aRetval, bInside);
534*cdf0e10cSrcweir 				}
535*cdf0e10cSrcweir 			}
536*cdf0e10cSrcweir 
537*cdf0e10cSrcweir 			return aRetval;
538*cdf0e10cSrcweir 		}
539*cdf0e10cSrcweir 
540*cdf0e10cSrcweir 		//////////////////////////////////////////////////////////////////////////////
541*cdf0e10cSrcweir 
542*cdf0e10cSrcweir 		B2DPolyPolygon clipPolygonOnPolyPolygon(const B2DPolygon& rCandidate, const B2DPolyPolygon& rClip, bool bInside, bool bStroke)
543*cdf0e10cSrcweir 		{
544*cdf0e10cSrcweir 			B2DPolyPolygon aRetval;
545*cdf0e10cSrcweir 
546*cdf0e10cSrcweir 			if(rCandidate.count() && rClip.count())
547*cdf0e10cSrcweir 			{
548*cdf0e10cSrcweir 				aRetval = clipPolyPolygonOnPolyPolygon(B2DPolyPolygon(rCandidate), rClip, bInside, bStroke);
549*cdf0e10cSrcweir 			}
550*cdf0e10cSrcweir 
551*cdf0e10cSrcweir 			return aRetval;
552*cdf0e10cSrcweir 		}
553*cdf0e10cSrcweir 
554*cdf0e10cSrcweir 		//////////////////////////////////////////////////////////////////////////////
555*cdf0e10cSrcweir 
556*cdf0e10cSrcweir 		/*
557*cdf0e10cSrcweir 		* let a plane be defined as
558*cdf0e10cSrcweir 		*
559*cdf0e10cSrcweir 		*     v.n+d=0
560*cdf0e10cSrcweir 		*
561*cdf0e10cSrcweir 		* and a ray be defined as
562*cdf0e10cSrcweir 		*
563*cdf0e10cSrcweir 		*     a+(b-a)*t=0
564*cdf0e10cSrcweir 		*
565*cdf0e10cSrcweir 		* substitute and rearranging yields
566*cdf0e10cSrcweir 		*
567*cdf0e10cSrcweir 		*     t = -(a.n+d)/(n.(b-a))
568*cdf0e10cSrcweir 		*
569*cdf0e10cSrcweir 		* if the denominator is zero, the line is either
570*cdf0e10cSrcweir 		* contained in the plane or parallel to the plane.
571*cdf0e10cSrcweir 		* in either case, there is no intersection.
572*cdf0e10cSrcweir 		* if numerator and denominator are both zero, the
573*cdf0e10cSrcweir 		* ray is contained in the plane.
574*cdf0e10cSrcweir 		*
575*cdf0e10cSrcweir 		*/
576*cdf0e10cSrcweir 		struct scissor_plane {
577*cdf0e10cSrcweir 			double nx,ny;			// plane normal
578*cdf0e10cSrcweir 			double d;				// [-] minimum distance from origin
579*cdf0e10cSrcweir 			sal_uInt32 clipmask;	// clipping mask, e.g. 1000 1000
580*cdf0e10cSrcweir 		};
581*cdf0e10cSrcweir 
582*cdf0e10cSrcweir 		/*
583*cdf0e10cSrcweir 		*
584*cdf0e10cSrcweir 		* polygon clipping rules  (straight out of Foley and Van Dam)
585*cdf0e10cSrcweir 		* ===========================================================
586*cdf0e10cSrcweir 		* current	|next		|emit
587*cdf0e10cSrcweir 		* ____________________________________
588*cdf0e10cSrcweir 		* inside	|inside		|next
589*cdf0e10cSrcweir 		* inside	|outside	|intersect with clip plane
590*cdf0e10cSrcweir 		* outside	|outside	|nothing
591*cdf0e10cSrcweir 		* outside	|inside		|intersect with clip plane follwed by next
592*cdf0e10cSrcweir 		*
593*cdf0e10cSrcweir 		*/
594*cdf0e10cSrcweir 		sal_uInt32 scissorLineSegment( ::basegfx::B2DPoint			 *in_vertex,	// input buffer
595*cdf0e10cSrcweir                                        sal_uInt32					  in_count,		// number of verts in input buffer
596*cdf0e10cSrcweir                                        ::basegfx::B2DPoint			 *out_vertex,	// output buffer
597*cdf0e10cSrcweir                                        scissor_plane				 *pPlane,		// scissoring plane
598*cdf0e10cSrcweir                                        const ::basegfx::B2DRectangle &rR )			// clipping rectangle
599*cdf0e10cSrcweir 		{
600*cdf0e10cSrcweir 			::basegfx::B2DPoint *curr;
601*cdf0e10cSrcweir 			::basegfx::B2DPoint *next;
602*cdf0e10cSrcweir 
603*cdf0e10cSrcweir 			sal_uInt32 out_count=0;
604*cdf0e10cSrcweir 
605*cdf0e10cSrcweir 			// process all the verts
606*cdf0e10cSrcweir 			for(sal_uInt32 i=0; i<in_count; i++) {
607*cdf0e10cSrcweir 
608*cdf0e10cSrcweir 				// vertices are relative to the coordinate
609*cdf0e10cSrcweir 				// system defined by the rectangle.
610*cdf0e10cSrcweir 				curr = &in_vertex[i];
611*cdf0e10cSrcweir 				next = &in_vertex[(i+1)%in_count];
612*cdf0e10cSrcweir 
613*cdf0e10cSrcweir 				// perform clipping judgement & mask against current plane.
614*cdf0e10cSrcweir 				sal_uInt32 clip = pPlane->clipmask & ((getCohenSutherlandClipFlags(*curr,rR)<<4)|getCohenSutherlandClipFlags(*next,rR));
615*cdf0e10cSrcweir 
616*cdf0e10cSrcweir 				if(clip==0) { // both verts are inside
617*cdf0e10cSrcweir 					out_vertex[out_count++] = *next;
618*cdf0e10cSrcweir 				}
619*cdf0e10cSrcweir 				else if((clip&0x0f) && (clip&0xf0)) { // both verts are outside
620*cdf0e10cSrcweir 				}
621*cdf0e10cSrcweir 				else if((clip&0x0f) && (clip&0xf0)==0) { // curr is inside, next is outside
622*cdf0e10cSrcweir 
623*cdf0e10cSrcweir 					// direction vector from 'current' to 'next', *not* normalized
624*cdf0e10cSrcweir 					// to bring 't' into the [0<=x<=1] intervall.
625*cdf0e10cSrcweir 					::basegfx::B2DPoint dir((*next)-(*curr));
626*cdf0e10cSrcweir 
627*cdf0e10cSrcweir 					double denominator = ( pPlane->nx*dir.getX() +
628*cdf0e10cSrcweir 										pPlane->ny*dir.getY() );
629*cdf0e10cSrcweir 					double numerator = ( pPlane->nx*curr->getX() +
630*cdf0e10cSrcweir 										pPlane->ny*curr->getY() +
631*cdf0e10cSrcweir 										pPlane->d );
632*cdf0e10cSrcweir 					double t = -numerator/denominator;
633*cdf0e10cSrcweir 
634*cdf0e10cSrcweir 					// calculate the actual point of intersection
635*cdf0e10cSrcweir 					::basegfx::B2DPoint intersection( curr->getX()+t*dir.getX(),
636*cdf0e10cSrcweir 													curr->getY()+t*dir.getY() );
637*cdf0e10cSrcweir 
638*cdf0e10cSrcweir 					out_vertex[out_count++] = intersection;
639*cdf0e10cSrcweir 				}
640*cdf0e10cSrcweir 				else if((clip&0x0f)==0 && (clip&0xf0)) { // curr is outside, next is inside
641*cdf0e10cSrcweir 
642*cdf0e10cSrcweir 					// direction vector from 'current' to 'next', *not* normalized
643*cdf0e10cSrcweir 					// to bring 't' into the [0<=x<=1] intervall.
644*cdf0e10cSrcweir 					::basegfx::B2DPoint dir((*next)-(*curr));
645*cdf0e10cSrcweir 
646*cdf0e10cSrcweir 					double denominator = ( pPlane->nx*dir.getX() +
647*cdf0e10cSrcweir 										pPlane->ny*dir.getY() );
648*cdf0e10cSrcweir 					double numerator = ( pPlane->nx*curr->getX() +
649*cdf0e10cSrcweir 										pPlane->ny*curr->getY() +
650*cdf0e10cSrcweir 										pPlane->d );
651*cdf0e10cSrcweir 					double t = -numerator/denominator;
652*cdf0e10cSrcweir 
653*cdf0e10cSrcweir 					// calculate the actual point of intersection
654*cdf0e10cSrcweir 					::basegfx::B2DPoint intersection( curr->getX()+t*dir.getX(),
655*cdf0e10cSrcweir 													curr->getY()+t*dir.getY() );
656*cdf0e10cSrcweir 
657*cdf0e10cSrcweir 					out_vertex[out_count++] = intersection;
658*cdf0e10cSrcweir 					out_vertex[out_count++] = *next;
659*cdf0e10cSrcweir 				}
660*cdf0e10cSrcweir 			}
661*cdf0e10cSrcweir 
662*cdf0e10cSrcweir 			return out_count;
663*cdf0e10cSrcweir 		}
664*cdf0e10cSrcweir 
665*cdf0e10cSrcweir 		B2DPolygon clipTriangleListOnRange( const B2DPolygon& rCandidate,
666*cdf0e10cSrcweir                                             const B2DRange&   rRange )
667*cdf0e10cSrcweir 		{
668*cdf0e10cSrcweir 			B2DPolygon aResult;
669*cdf0e10cSrcweir 
670*cdf0e10cSrcweir 			if( !(rCandidate.count()%3) )
671*cdf0e10cSrcweir 			{
672*cdf0e10cSrcweir 				const int scissor_plane_count = 4;
673*cdf0e10cSrcweir 
674*cdf0e10cSrcweir 				scissor_plane sp[scissor_plane_count];
675*cdf0e10cSrcweir 
676*cdf0e10cSrcweir 				sp[0].nx = +1.0;
677*cdf0e10cSrcweir 				sp[0].ny = +0.0;
678*cdf0e10cSrcweir 				sp[0].d = -(rRange.getMinX());
679*cdf0e10cSrcweir 				sp[0].clipmask = (RectClipFlags::LEFT << 4) | RectClipFlags::LEFT; // 0001 0001
680*cdf0e10cSrcweir 				sp[1].nx = -1.0;
681*cdf0e10cSrcweir 				sp[1].ny = +0.0;
682*cdf0e10cSrcweir 				sp[1].d = +(rRange.getMaxX());
683*cdf0e10cSrcweir 				sp[1].clipmask = (RectClipFlags::RIGHT << 4) | RectClipFlags::RIGHT; // 0010 0010
684*cdf0e10cSrcweir 				sp[2].nx = +0.0;
685*cdf0e10cSrcweir 				sp[2].ny = +1.0;
686*cdf0e10cSrcweir 				sp[2].d = -(rRange.getMinY());
687*cdf0e10cSrcweir 				sp[2].clipmask = (RectClipFlags::TOP << 4) | RectClipFlags::TOP; // 0100 0100
688*cdf0e10cSrcweir 				sp[3].nx = +0.0;
689*cdf0e10cSrcweir 				sp[3].ny = -1.0;
690*cdf0e10cSrcweir 				sp[3].d = +(rRange.getMaxY());
691*cdf0e10cSrcweir 				sp[3].clipmask = (RectClipFlags::BOTTOM << 4) | RectClipFlags::BOTTOM; // 1000 1000
692*cdf0e10cSrcweir 
693*cdf0e10cSrcweir 				// retrieve the number of vertices of the triangulated polygon
694*cdf0e10cSrcweir 				const sal_uInt32 nVertexCount = rCandidate.count();
695*cdf0e10cSrcweir 
696*cdf0e10cSrcweir 				if(nVertexCount)
697*cdf0e10cSrcweir 				{
698*cdf0e10cSrcweir 					////////////////////////////////////////////////////////////////////////
699*cdf0e10cSrcweir 					////////////////////////////////////////////////////////////////////////
700*cdf0e10cSrcweir 					////////////////////////////////////////////////////////////////////////
701*cdf0e10cSrcweir 					//
702*cdf0e10cSrcweir 					// Upper bound for the maximal number of vertices when intersecting an
703*cdf0e10cSrcweir 					// axis-aligned rectangle with a triangle in E2
704*cdf0e10cSrcweir 					//
705*cdf0e10cSrcweir 					// The rectangle and the triangle are in general position, and have 4 and 3
706*cdf0e10cSrcweir 					// vertices, respectively.
707*cdf0e10cSrcweir 					//
708*cdf0e10cSrcweir 					//   Lemma: Since the rectangle is a convex polygon ( see
709*cdf0e10cSrcweir 					//   http://mathworld.wolfram.com/ConvexPolygon.html for a definition), and
710*cdf0e10cSrcweir 					//   has no holes, it follows that any straight line will intersect the
711*cdf0e10cSrcweir 					//   rectangle's border line at utmost two times (with the usual
712*cdf0e10cSrcweir 					//   tie-breaking rule, if the intersection exactly hits an already existing
713*cdf0e10cSrcweir 					//   rectangle vertex, that this intersection is only attributed to one of
714*cdf0e10cSrcweir 					//   the adjoining edges). Thus, having a rectangle intersected with
715*cdf0e10cSrcweir 					//   a half-plane (one side of a straight line denotes 'inside', the
716*cdf0e10cSrcweir 					//   other 'outside') will at utmost add _one_  vertex to the resulting
717*cdf0e10cSrcweir 					//   intersection polygon (adding two intersection vertices, and removing at
718*cdf0e10cSrcweir 					//   least one rectangle vertex):
719*cdf0e10cSrcweir 					//
720*cdf0e10cSrcweir 					//         *
721*cdf0e10cSrcweir 					//     +--+-----------------+
722*cdf0e10cSrcweir 					//     | *                  |
723*cdf0e10cSrcweir 					//     |*                   |
724*cdf0e10cSrcweir 					//     +                    |
725*cdf0e10cSrcweir 					//    *|                    |
726*cdf0e10cSrcweir 					//   * |                    |
727*cdf0e10cSrcweir 					//     +--------------------+
728*cdf0e10cSrcweir 					//
729*cdf0e10cSrcweir 					//   Proof: If the straight line intersects the rectangle two
730*cdf0e10cSrcweir 					//   times, it does so for distinct edges, i.e. the intersection has
731*cdf0e10cSrcweir 					//   minimally one of the rectangle's vertices on either side of the straight
732*cdf0e10cSrcweir 					//   line (but maybe more). Thus, the intersection with a half-plane has
733*cdf0e10cSrcweir 					//   minimally _one_ rectangle vertex removed from the resulting clip
734*cdf0e10cSrcweir 					//   polygon, and therefore, a clip against a half-plane has the net effect
735*cdf0e10cSrcweir 					//   of adding at utmost _one_ vertex to the resulting clip polygon.
736*cdf0e10cSrcweir 					//
737*cdf0e10cSrcweir 					// Theorem: The intersection of a rectangle and a triangle results in a
738*cdf0e10cSrcweir 					// polygon with at utmost 7 vertices.
739*cdf0e10cSrcweir 					//
740*cdf0e10cSrcweir 					// Proof: The inside of the triangle can be described as the consecutive
741*cdf0e10cSrcweir 					// intersection with three half-planes. Together with the lemma above, this
742*cdf0e10cSrcweir 					// results in at utmost 3 additional vertices added to the already existing 4
743*cdf0e10cSrcweir 					// rectangle vertices.
744*cdf0e10cSrcweir 					//
745*cdf0e10cSrcweir 					// This upper bound is attained with the following example configuration:
746*cdf0e10cSrcweir 					//
747*cdf0e10cSrcweir 					//                               *
748*cdf0e10cSrcweir 					//                             ***
749*cdf0e10cSrcweir 					//                           ** *
750*cdf0e10cSrcweir 					//                         **  *
751*cdf0e10cSrcweir 					//                       **   *
752*cdf0e10cSrcweir 					//                     **    *
753*cdf0e10cSrcweir 					//                   **     *
754*cdf0e10cSrcweir 					//                 **      *
755*cdf0e10cSrcweir 					//               **       *
756*cdf0e10cSrcweir 					//             **        *
757*cdf0e10cSrcweir 					//           **         *
758*cdf0e10cSrcweir 					//     ----*2--------3 *
759*cdf0e10cSrcweir 					//     | **          |*
760*cdf0e10cSrcweir 					//     1*            4
761*cdf0e10cSrcweir 					//   **|            *|
762*cdf0e10cSrcweir 					// **  |           * |
763*cdf0e10cSrcweir 					//   **|          *  |
764*cdf0e10cSrcweir 					//     7*        *   |
765*cdf0e10cSrcweir 					//     --*6-----5-----
766*cdf0e10cSrcweir 					//         **  *
767*cdf0e10cSrcweir 					//           **
768*cdf0e10cSrcweir 					//
769*cdf0e10cSrcweir 					// As we need to scissor all triangles against the
770*cdf0e10cSrcweir 					// output rectangle we employ an output buffer for the
771*cdf0e10cSrcweir 					// resulting vertices.  the question is how large this
772*cdf0e10cSrcweir 					// buffer needs to be compared to the number of
773*cdf0e10cSrcweir 					// incoming vertices.  this buffer needs to hold at
774*cdf0e10cSrcweir 					// most the number of original vertices times '7'. see
775*cdf0e10cSrcweir 					// figure above for an example.  scissoring triangles
776*cdf0e10cSrcweir 					// with the cohen-sutherland line clipping algorithm
777*cdf0e10cSrcweir 					// as implemented here will result in a triangle fan
778*cdf0e10cSrcweir 					// which will be rendered as separate triangles to
779*cdf0e10cSrcweir 					// avoid pipeline stalls for each scissored
780*cdf0e10cSrcweir 					// triangle. creating separate triangles from a
781*cdf0e10cSrcweir 					// triangle fan produces (n-2)*3 vertices where n is
782*cdf0e10cSrcweir 					// the number of vertices of the original triangle
783*cdf0e10cSrcweir 					// fan.  for the maximum number of 7 vertices of
784*cdf0e10cSrcweir 					// resulting triangle fans we therefore need 15 times
785*cdf0e10cSrcweir 					// the number of original vertices.
786*cdf0e10cSrcweir 					//
787*cdf0e10cSrcweir 					////////////////////////////////////////////////////////////////////////
788*cdf0e10cSrcweir 					////////////////////////////////////////////////////////////////////////
789*cdf0e10cSrcweir 					////////////////////////////////////////////////////////////////////////
790*cdf0e10cSrcweir 
791*cdf0e10cSrcweir 					//const size_t nBufferSize = sizeof(vertex)*(nVertexCount*16);
792*cdf0e10cSrcweir 					//vertex *pVertices = (vertex*)alloca(nBufferSize);
793*cdf0e10cSrcweir 					//sal_uInt32 nNumOutput = 0;
794*cdf0e10cSrcweir 
795*cdf0e10cSrcweir 					// we need to clip this triangle against the output rectangle
796*cdf0e10cSrcweir 					// to ensure that the resulting texture coordinates are in
797*cdf0e10cSrcweir 					// the valid range from [0<=st<=1]. under normal circustances
798*cdf0e10cSrcweir 					// we could use the BORDERCOLOR renderstate but some cards
799*cdf0e10cSrcweir 					// seem to ignore this feature.
800*cdf0e10cSrcweir 					::basegfx::B2DPoint stack[3];
801*cdf0e10cSrcweir 					unsigned int clipflag = 0;
802*cdf0e10cSrcweir 
803*cdf0e10cSrcweir 					for(sal_uInt32 nIndex=0; nIndex<nVertexCount; ++nIndex)
804*cdf0e10cSrcweir 					{
805*cdf0e10cSrcweir 						// rotate stack
806*cdf0e10cSrcweir 						stack[0] = stack[1];
807*cdf0e10cSrcweir 						stack[1] = stack[2];
808*cdf0e10cSrcweir 						stack[2] = rCandidate.getB2DPoint(nIndex);
809*cdf0e10cSrcweir 
810*cdf0e10cSrcweir 						// clipping judgement
811*cdf0e10cSrcweir 						clipflag |= !(rRange.isInside(stack[2]));
812*cdf0e10cSrcweir 
813*cdf0e10cSrcweir 						if(nIndex > 1)
814*cdf0e10cSrcweir 						{
815*cdf0e10cSrcweir 							// consume vertices until a single seperate triangle has been visited.
816*cdf0e10cSrcweir 							if(!((nIndex+1)%3))
817*cdf0e10cSrcweir 							{
818*cdf0e10cSrcweir 								// if any of the last three vertices was outside
819*cdf0e10cSrcweir 								// we need to scissor against the destination rectangle
820*cdf0e10cSrcweir 								if(clipflag & 7)
821*cdf0e10cSrcweir 								{
822*cdf0e10cSrcweir 									::basegfx::B2DPoint buf0[16];
823*cdf0e10cSrcweir 									::basegfx::B2DPoint buf1[16];
824*cdf0e10cSrcweir 
825*cdf0e10cSrcweir 									sal_uInt32 vertex_count = 3;
826*cdf0e10cSrcweir 
827*cdf0e10cSrcweir 									// clip against all 4 planes passing the result of
828*cdf0e10cSrcweir 									// each plane as the input to the next using a double buffer
829*cdf0e10cSrcweir 									vertex_count = scissorLineSegment(stack,vertex_count,buf1,&sp[0],rRange);
830*cdf0e10cSrcweir 									vertex_count = scissorLineSegment(buf1,vertex_count,buf0,&sp[1],rRange);
831*cdf0e10cSrcweir 									vertex_count = scissorLineSegment(buf0,vertex_count,buf1,&sp[2],rRange);
832*cdf0e10cSrcweir 									vertex_count = scissorLineSegment(buf1,vertex_count,buf0,&sp[3],rRange);
833*cdf0e10cSrcweir 
834*cdf0e10cSrcweir 									if(vertex_count >= 3)
835*cdf0e10cSrcweir 									{
836*cdf0e10cSrcweir 										// convert triangle fan back to triangle list.
837*cdf0e10cSrcweir 										::basegfx::B2DPoint v0(buf0[0]);
838*cdf0e10cSrcweir 										::basegfx::B2DPoint v1(buf0[1]);
839*cdf0e10cSrcweir 										for(sal_uInt32 i=2; i<vertex_count; ++i)
840*cdf0e10cSrcweir 										{
841*cdf0e10cSrcweir 											::basegfx::B2DPoint v2(buf0[i]);
842*cdf0e10cSrcweir 											aResult.append(v0);
843*cdf0e10cSrcweir 											aResult.append(v1);
844*cdf0e10cSrcweir 											aResult.append(v2);
845*cdf0e10cSrcweir 											v1 = v2;
846*cdf0e10cSrcweir 										}
847*cdf0e10cSrcweir 									}
848*cdf0e10cSrcweir 								}
849*cdf0e10cSrcweir 								else
850*cdf0e10cSrcweir 								{
851*cdf0e10cSrcweir 									// the last triangle has not been altered, simply copy to result
852*cdf0e10cSrcweir 									for(sal_uInt32 i=0; i<3; ++i)
853*cdf0e10cSrcweir 										aResult.append(stack[i]);
854*cdf0e10cSrcweir 								}
855*cdf0e10cSrcweir 							}
856*cdf0e10cSrcweir 						}
857*cdf0e10cSrcweir 
858*cdf0e10cSrcweir 						clipflag <<= 1;
859*cdf0e10cSrcweir 					}
860*cdf0e10cSrcweir 				}
861*cdf0e10cSrcweir 			}
862*cdf0e10cSrcweir 
863*cdf0e10cSrcweir 			return aResult;
864*cdf0e10cSrcweir 		}
865*cdf0e10cSrcweir 
866*cdf0e10cSrcweir 		//////////////////////////////////////////////////////////////////////////////
867*cdf0e10cSrcweir 
868*cdf0e10cSrcweir 	} // end of namespace tools
869*cdf0e10cSrcweir } // end of namespace basegfx
870*cdf0e10cSrcweir 
871*cdf0e10cSrcweir //////////////////////////////////////////////////////////////////////////////
872*cdf0e10cSrcweir 
873*cdf0e10cSrcweir // eof
874