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