xref: /AOO41X/main/basegfx/source/polygon/b2dlinegeometry.cxx (revision 09dbbe930366fe6f99ae3b8ae1cf8690b638dbda)
1*09dbbe93SAndrew Rist /**************************************************************
2cdf0e10cSrcweir  *
3*09dbbe93SAndrew Rist  * Licensed to the Apache Software Foundation (ASF) under one
4*09dbbe93SAndrew Rist  * or more contributor license agreements.  See the NOTICE file
5*09dbbe93SAndrew Rist  * distributed with this work for additional information
6*09dbbe93SAndrew Rist  * regarding copyright ownership.  The ASF licenses this file
7*09dbbe93SAndrew Rist  * to you under the Apache License, Version 2.0 (the
8*09dbbe93SAndrew Rist  * "License"); you may not use this file except in compliance
9*09dbbe93SAndrew Rist  * with the License.  You may obtain a copy of the License at
10cdf0e10cSrcweir  *
11*09dbbe93SAndrew Rist  *   http://www.apache.org/licenses/LICENSE-2.0
12cdf0e10cSrcweir  *
13*09dbbe93SAndrew Rist  * Unless required by applicable law or agreed to in writing,
14*09dbbe93SAndrew Rist  * software distributed under the License is distributed on an
15*09dbbe93SAndrew Rist  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16*09dbbe93SAndrew Rist  * KIND, either express or implied.  See the License for the
17*09dbbe93SAndrew Rist  * specific language governing permissions and limitations
18*09dbbe93SAndrew Rist  * under the License.
19cdf0e10cSrcweir  *
20*09dbbe93SAndrew Rist  *************************************************************/
21*09dbbe93SAndrew Rist 
22*09dbbe93SAndrew Rist 
23cdf0e10cSrcweir 
24cdf0e10cSrcweir // MARKER(update_precomp.py): autogen include statement, do not remove
25cdf0e10cSrcweir #include "precompiled_basegfx.hxx"
26cdf0e10cSrcweir #include <cstdio>
27cdf0e10cSrcweir #include <osl/diagnose.h>
28cdf0e10cSrcweir #include <basegfx/polygon/b2dlinegeometry.hxx>
29cdf0e10cSrcweir #include <basegfx/point/b2dpoint.hxx>
30cdf0e10cSrcweir #include <basegfx/vector/b2dvector.hxx>
31cdf0e10cSrcweir #include <basegfx/polygon/b2dpolygontools.hxx>
32cdf0e10cSrcweir #include <basegfx/polygon/b2dpolypolygontools.hxx>
33cdf0e10cSrcweir #include <basegfx/range/b2drange.hxx>
34cdf0e10cSrcweir #include <basegfx/matrix/b2dhommatrix.hxx>
35cdf0e10cSrcweir #include <basegfx/curve/b2dcubicbezier.hxx>
36cdf0e10cSrcweir #include <basegfx/matrix/b2dhommatrixtools.hxx>
37cdf0e10cSrcweir 
38cdf0e10cSrcweir //////////////////////////////////////////////////////////////////////////////
39cdf0e10cSrcweir 
40cdf0e10cSrcweir namespace basegfx
41cdf0e10cSrcweir {
42cdf0e10cSrcweir 	namespace tools
43cdf0e10cSrcweir 	{
44cdf0e10cSrcweir 		B2DPolyPolygon createAreaGeometryForLineStartEnd(
45cdf0e10cSrcweir 			const B2DPolygon& rCandidate,
46cdf0e10cSrcweir 			const B2DPolyPolygon& rArrow,
47cdf0e10cSrcweir 			bool bStart,
48cdf0e10cSrcweir 			double fWidth,
49cdf0e10cSrcweir 			double fCandidateLength,
50cdf0e10cSrcweir 			double fDockingPosition, // 0->top, 1->bottom
51cdf0e10cSrcweir 			double* pConsumedLength)
52cdf0e10cSrcweir 		{
53cdf0e10cSrcweir 			B2DPolyPolygon aRetval;
54cdf0e10cSrcweir 			OSL_ENSURE(rCandidate.count() > 1L, "createAreaGeometryForLineStartEnd: Line polygon has too less points (!)");
55cdf0e10cSrcweir 			OSL_ENSURE(rArrow.count() > 0L, "createAreaGeometryForLineStartEnd: Empty arrow PolyPolygon (!)");
56cdf0e10cSrcweir 			OSL_ENSURE(fWidth > 0.0, "createAreaGeometryForLineStartEnd: Width too small (!)");
57cdf0e10cSrcweir 			OSL_ENSURE(fDockingPosition >= 0.0 && fDockingPosition <= 1.0,
58cdf0e10cSrcweir 				"createAreaGeometryForLineStartEnd: fDockingPosition out of range [0.0 .. 1.0] (!)");
59cdf0e10cSrcweir 
60cdf0e10cSrcweir             if(fWidth < 0.0)
61cdf0e10cSrcweir             {
62cdf0e10cSrcweir                 fWidth = -fWidth;
63cdf0e10cSrcweir             }
64cdf0e10cSrcweir 
65cdf0e10cSrcweir             if(rCandidate.count() > 1 && rArrow.count() && !fTools::equalZero(fWidth))
66cdf0e10cSrcweir             {
67cdf0e10cSrcweir                 if(fDockingPosition < 0.0)
68cdf0e10cSrcweir                 {
69cdf0e10cSrcweir                     fDockingPosition = 0.0;
70cdf0e10cSrcweir                 }
71cdf0e10cSrcweir                 else if(fDockingPosition > 1.0)
72cdf0e10cSrcweir                 {
73cdf0e10cSrcweir                     fDockingPosition = 1.0;
74cdf0e10cSrcweir                 }
75cdf0e10cSrcweir 
76cdf0e10cSrcweir 			    // init return value from arrow
77cdf0e10cSrcweir 			    aRetval.append(rArrow);
78cdf0e10cSrcweir 
79cdf0e10cSrcweir 			    // get size of the arrow
80cdf0e10cSrcweir 			    const B2DRange aArrowSize(getRange(rArrow));
81cdf0e10cSrcweir 
82cdf0e10cSrcweir 			    // build ArrowTransform; center in X, align with axis in Y
83cdf0e10cSrcweir                 B2DHomMatrix aArrowTransform(basegfx::tools::createTranslateB2DHomMatrix(
84cdf0e10cSrcweir                     -aArrowSize.getCenter().getX(), -aArrowSize.getMinimum().getY()));
85cdf0e10cSrcweir 
86cdf0e10cSrcweir 			    // scale to target size
87cdf0e10cSrcweir 			    const double fArrowScale(fWidth / (aArrowSize.getRange().getX()));
88cdf0e10cSrcweir 			    aArrowTransform.scale(fArrowScale, fArrowScale);
89cdf0e10cSrcweir 
90cdf0e10cSrcweir 			    // get arrow size in Y
91cdf0e10cSrcweir 			    B2DPoint aUpperCenter(aArrowSize.getCenter().getX(), aArrowSize.getMaximum().getY());
92cdf0e10cSrcweir 			    aUpperCenter *= aArrowTransform;
93cdf0e10cSrcweir 			    const double fArrowYLength(B2DVector(aUpperCenter).getLength());
94cdf0e10cSrcweir 
95cdf0e10cSrcweir 			    // move arrow to have docking position centered
96cdf0e10cSrcweir 			    aArrowTransform.translate(0.0, -fArrowYLength * fDockingPosition);
97cdf0e10cSrcweir 
98cdf0e10cSrcweir 			    // prepare polygon length
99cdf0e10cSrcweir 			    if(fTools::equalZero(fCandidateLength))
100cdf0e10cSrcweir 			    {
101cdf0e10cSrcweir 				    fCandidateLength = getLength(rCandidate);
102cdf0e10cSrcweir 			    }
103cdf0e10cSrcweir 
104cdf0e10cSrcweir 			    // get the polygon vector we want to plant this arrow on
105cdf0e10cSrcweir 			    const double fConsumedLength(fArrowYLength * (1.0 - fDockingPosition));
106cdf0e10cSrcweir 			    const B2DVector aHead(rCandidate.getB2DPoint((bStart) ? 0L : rCandidate.count() - 1L));
107cdf0e10cSrcweir 			    const B2DVector aTail(getPositionAbsolute(rCandidate,
108cdf0e10cSrcweir 				    (bStart) ? fConsumedLength : fCandidateLength - fConsumedLength, fCandidateLength));
109cdf0e10cSrcweir 
110cdf0e10cSrcweir 			    // from that vector, take the needed rotation and add rotate for arrow to transformation
111cdf0e10cSrcweir 			    const B2DVector aTargetDirection(aHead - aTail);
112cdf0e10cSrcweir 			    const double fRotation(atan2(aTargetDirection.getY(), aTargetDirection.getX()) + (90.0 * F_PI180));
113cdf0e10cSrcweir 
114cdf0e10cSrcweir 			    // rotate around docking position
115cdf0e10cSrcweir 			    aArrowTransform.rotate(fRotation);
116cdf0e10cSrcweir 
117cdf0e10cSrcweir 			    // move arrow docking position to polygon head
118cdf0e10cSrcweir 			    aArrowTransform.translate(aHead.getX(), aHead.getY());
119cdf0e10cSrcweir 
120cdf0e10cSrcweir 			    // transform retval and close
121cdf0e10cSrcweir 			    aRetval.transform(aArrowTransform);
122cdf0e10cSrcweir 			    aRetval.setClosed(true);
123cdf0e10cSrcweir 
124cdf0e10cSrcweir 			    // if pConsumedLength is asked for, fill it
125cdf0e10cSrcweir 			    if(pConsumedLength)
126cdf0e10cSrcweir 			    {
127cdf0e10cSrcweir 				    *pConsumedLength = fConsumedLength;
128cdf0e10cSrcweir 			    }
129cdf0e10cSrcweir             }
130cdf0e10cSrcweir 
131cdf0e10cSrcweir 			return aRetval;
132cdf0e10cSrcweir 		}
133cdf0e10cSrcweir 	} // end of namespace tools
134cdf0e10cSrcweir } // end of namespace basegfx
135cdf0e10cSrcweir 
136cdf0e10cSrcweir //////////////////////////////////////////////////////////////////////////////
137cdf0e10cSrcweir 
138cdf0e10cSrcweir namespace basegfx
139cdf0e10cSrcweir {
140cdf0e10cSrcweir 	// anonymus namespace for local helpers
141cdf0e10cSrcweir 	namespace
142cdf0e10cSrcweir 	{
143cdf0e10cSrcweir         bool impIsSimpleEdge(const B2DCubicBezier& rCandidate, double fMaxCosQuad, double fMaxPartOfEdgeQuad)
144cdf0e10cSrcweir         {
145cdf0e10cSrcweir             // isBezier() is true, already tested by caller
146cdf0e10cSrcweir             const B2DVector aEdge(rCandidate.getEndPoint() - rCandidate.getStartPoint());
147cdf0e10cSrcweir 
148cdf0e10cSrcweir             if(aEdge.equalZero())
149cdf0e10cSrcweir             {
150cdf0e10cSrcweir                 // start and end point the same, but control vectors used -> baloon curve loop
151cdf0e10cSrcweir                 // is not a simple edge
152cdf0e10cSrcweir                 return false;
153cdf0e10cSrcweir             }
154cdf0e10cSrcweir 
155cdf0e10cSrcweir             // get tangentA and scalar with edge
156cdf0e10cSrcweir             const B2DVector aTangentA(rCandidate.getTangent(0.0));
157cdf0e10cSrcweir     		const double fScalarAE(aEdge.scalar(aTangentA));
158cdf0e10cSrcweir 
159cdf0e10cSrcweir             if(fTools::lessOrEqual(fScalarAE, 0.0))
160cdf0e10cSrcweir             {
161cdf0e10cSrcweir                 // angle between TangentA and Edge is bigger or equal 90 degrees
162cdf0e10cSrcweir                 return false;
163cdf0e10cSrcweir             }
164cdf0e10cSrcweir 
165cdf0e10cSrcweir             // get self-scalars for E and A
166cdf0e10cSrcweir     		const double fScalarE(aEdge.scalar(aEdge));
167cdf0e10cSrcweir     		const double fScalarA(aTangentA.scalar(aTangentA));
168cdf0e10cSrcweir 			const double fLengthCompareE(fScalarE * fMaxPartOfEdgeQuad);
169cdf0e10cSrcweir 
170cdf0e10cSrcweir 			if(fTools::moreOrEqual(fScalarA, fLengthCompareE))
171cdf0e10cSrcweir 			{
172cdf0e10cSrcweir 				// length of TangentA is more than fMaxPartOfEdge of length of edge
173cdf0e10cSrcweir 				return false;
174cdf0e10cSrcweir 			}
175cdf0e10cSrcweir 
176cdf0e10cSrcweir             if(fTools::lessOrEqual(fScalarAE * fScalarAE, fScalarA * fScalarE * fMaxCosQuad))
177cdf0e10cSrcweir             {
178cdf0e10cSrcweir                 // angle between TangentA and Edge is bigger or equal angle defined by fMaxCos
179cdf0e10cSrcweir                 return false;
180cdf0e10cSrcweir             }
181cdf0e10cSrcweir 
182cdf0e10cSrcweir             // get tangentB and scalar with edge
183cdf0e10cSrcweir             const B2DVector aTangentB(rCandidate.getTangent(1.0));
184cdf0e10cSrcweir     		const double fScalarBE(aEdge.scalar(aTangentB));
185cdf0e10cSrcweir 
186cdf0e10cSrcweir             if(fTools::lessOrEqual(fScalarBE, 0.0))
187cdf0e10cSrcweir             {
188cdf0e10cSrcweir                 // angle between TangentB and Edge is bigger or equal 90 degrees
189cdf0e10cSrcweir                 return false;
190cdf0e10cSrcweir             }
191cdf0e10cSrcweir 
192cdf0e10cSrcweir             // get self-scalar for B
193cdf0e10cSrcweir     		const double fScalarB(aTangentB.scalar(aTangentB));
194cdf0e10cSrcweir 
195cdf0e10cSrcweir 			if(fTools::moreOrEqual(fScalarB, fLengthCompareE))
196cdf0e10cSrcweir 			{
197cdf0e10cSrcweir 				// length of TangentB is more than fMaxPartOfEdge of length of edge
198cdf0e10cSrcweir 				return false;
199cdf0e10cSrcweir 			}
200cdf0e10cSrcweir 
201cdf0e10cSrcweir 			if(fTools::lessOrEqual(fScalarBE * fScalarBE, fScalarB * fScalarE * fMaxCosQuad))
202cdf0e10cSrcweir             {
203cdf0e10cSrcweir                 // angle between TangentB and Edge is bigger or equal defined by fMaxCos
204cdf0e10cSrcweir                 return false;
205cdf0e10cSrcweir             }
206cdf0e10cSrcweir 
207cdf0e10cSrcweir             return true;
208cdf0e10cSrcweir         }
209cdf0e10cSrcweir 
210cdf0e10cSrcweir         void impSubdivideToSimple(const B2DCubicBezier& rCandidate, B2DPolygon& rTarget, double fMaxCosQuad, double fMaxPartOfEdgeQuad, sal_uInt32 nMaxRecursionDepth)
211cdf0e10cSrcweir         {
212cdf0e10cSrcweir             if(!nMaxRecursionDepth || impIsSimpleEdge(rCandidate, fMaxCosQuad, fMaxPartOfEdgeQuad))
213cdf0e10cSrcweir             {
214cdf0e10cSrcweir                 rTarget.appendBezierSegment(rCandidate.getControlPointA(), rCandidate.getControlPointB(), rCandidate.getEndPoint());
215cdf0e10cSrcweir             }
216cdf0e10cSrcweir             else
217cdf0e10cSrcweir             {
218cdf0e10cSrcweir                 B2DCubicBezier aLeft, aRight;
219cdf0e10cSrcweir                 rCandidate.split(0.5, &aLeft, &aRight);
220cdf0e10cSrcweir 
221cdf0e10cSrcweir                 impSubdivideToSimple(aLeft, rTarget, fMaxCosQuad, fMaxPartOfEdgeQuad, nMaxRecursionDepth - 1);
222cdf0e10cSrcweir                 impSubdivideToSimple(aRight, rTarget, fMaxCosQuad, fMaxPartOfEdgeQuad, nMaxRecursionDepth - 1);
223cdf0e10cSrcweir             }
224cdf0e10cSrcweir         }
225cdf0e10cSrcweir 
226cdf0e10cSrcweir         B2DPolygon subdivideToSimple(const B2DPolygon& rCandidate, double fMaxCosQuad, double fMaxPartOfEdgeQuad)
227cdf0e10cSrcweir         {
228cdf0e10cSrcweir             const sal_uInt32 nPointCount(rCandidate.count());
229cdf0e10cSrcweir 
230cdf0e10cSrcweir             if(rCandidate.areControlPointsUsed() && nPointCount)
231cdf0e10cSrcweir             {
232cdf0e10cSrcweir                 const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
233cdf0e10cSrcweir                 B2DPolygon aRetval;
234cdf0e10cSrcweir 				B2DCubicBezier aEdge;
235cdf0e10cSrcweir 
236cdf0e10cSrcweir 				// prepare edge for loop
237cdf0e10cSrcweir 				aEdge.setStartPoint(rCandidate.getB2DPoint(0));
238cdf0e10cSrcweir 				aRetval.append(aEdge.getStartPoint());
239cdf0e10cSrcweir 
240cdf0e10cSrcweir                 for(sal_uInt32 a(0); a < nEdgeCount; a++)
241cdf0e10cSrcweir                 {
242cdf0e10cSrcweir                     // fill B2DCubicBezier
243cdf0e10cSrcweir                     const sal_uInt32 nNextIndex((a + 1) % nPointCount);
244cdf0e10cSrcweir                     aEdge.setControlPointA(rCandidate.getNextControlPoint(a));
245cdf0e10cSrcweir                     aEdge.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex));
246cdf0e10cSrcweir                     aEdge.setEndPoint(rCandidate.getB2DPoint(nNextIndex));
247cdf0e10cSrcweir 
248cdf0e10cSrcweir                     // get rid of unnecessary bezier segments
249cdf0e10cSrcweir                     aEdge.testAndSolveTrivialBezier();
250cdf0e10cSrcweir 
251cdf0e10cSrcweir                     if(aEdge.isBezier())
252cdf0e10cSrcweir                     {
253cdf0e10cSrcweir 						// before splitting recursively with internal simple criteria, use
254cdf0e10cSrcweir 						// ExtremumPosFinder to remove those
255cdf0e10cSrcweir                 		::std::vector< double > aExtremumPositions;
256cdf0e10cSrcweir 
257cdf0e10cSrcweir                         aExtremumPositions.reserve(4);
258cdf0e10cSrcweir 		                aEdge.getAllExtremumPositions(aExtremumPositions);
259cdf0e10cSrcweir 
260cdf0e10cSrcweir                         const sal_uInt32 nCount(aExtremumPositions.size());
261cdf0e10cSrcweir 
262cdf0e10cSrcweir                         if(nCount)
263cdf0e10cSrcweir                         {
264cdf0e10cSrcweir                             if(nCount > 1)
265cdf0e10cSrcweir                             {
266cdf0e10cSrcweir                                 // create order from left to right
267cdf0e10cSrcweir                                 ::std::sort(aExtremumPositions.begin(), aExtremumPositions.end());
268cdf0e10cSrcweir                             }
269cdf0e10cSrcweir 
270cdf0e10cSrcweir                             for(sal_uInt32 b(0); b < nCount;)
271cdf0e10cSrcweir                             {
272cdf0e10cSrcweir                                 // split aEdge at next split pos
273cdf0e10cSrcweir                                 B2DCubicBezier aLeft;
274cdf0e10cSrcweir                                 const double fSplitPos(aExtremumPositions[b++]);
275cdf0e10cSrcweir 
276cdf0e10cSrcweir                                 aEdge.split(fSplitPos, &aLeft, &aEdge);
277cdf0e10cSrcweir                                 aLeft.testAndSolveTrivialBezier();
278cdf0e10cSrcweir 
279cdf0e10cSrcweir                                 // consume left part
280cdf0e10cSrcweir                                 if(aLeft.isBezier())
281cdf0e10cSrcweir 						        {
282cdf0e10cSrcweir 	                                impSubdivideToSimple(aLeft, aRetval, fMaxCosQuad, fMaxPartOfEdgeQuad, 6);
283cdf0e10cSrcweir 						        }
284cdf0e10cSrcweir 						        else
285cdf0e10cSrcweir 						        {
286cdf0e10cSrcweir 	                                aRetval.append(aLeft.getEndPoint());
287cdf0e10cSrcweir 						        }
288cdf0e10cSrcweir 
289cdf0e10cSrcweir                                 if(b < nCount)
290cdf0e10cSrcweir                                 {
291cdf0e10cSrcweir                                     // correct the remaining split positions to fit to shortened aEdge
292cdf0e10cSrcweir                                     const double fScaleFactor(1.0 / (1.0 - fSplitPos));
293cdf0e10cSrcweir 
294cdf0e10cSrcweir                                     for(sal_uInt32 c(b); c < nCount; c++)
295cdf0e10cSrcweir                                     {
296cdf0e10cSrcweir                                         aExtremumPositions[c] = (aExtremumPositions[c] - fSplitPos) * fScaleFactor;
297cdf0e10cSrcweir                                     }
298cdf0e10cSrcweir                                 }
299cdf0e10cSrcweir                             }
300cdf0e10cSrcweir 
301cdf0e10cSrcweir                             // test the shortened rest of aEdge
302cdf0e10cSrcweir                             aEdge.testAndSolveTrivialBezier();
303cdf0e10cSrcweir 
304cdf0e10cSrcweir                             // consume right part
305cdf0e10cSrcweir                             if(aEdge.isBezier())
306cdf0e10cSrcweir 					        {
307cdf0e10cSrcweir                                 impSubdivideToSimple(aEdge, aRetval, fMaxCosQuad, fMaxPartOfEdgeQuad, 6);
308cdf0e10cSrcweir 					        }
309cdf0e10cSrcweir 					        else
310cdf0e10cSrcweir 					        {
311cdf0e10cSrcweir                                 aRetval.append(aEdge.getEndPoint());
312cdf0e10cSrcweir 					        }
313cdf0e10cSrcweir                         }
314cdf0e10cSrcweir                         else
315cdf0e10cSrcweir                         {
316cdf0e10cSrcweir 	                        impSubdivideToSimple(aEdge, aRetval, fMaxCosQuad, fMaxPartOfEdgeQuad, 6);
317cdf0e10cSrcweir                         }
318cdf0e10cSrcweir                     }
319cdf0e10cSrcweir                     else
320cdf0e10cSrcweir                     {
321cdf0e10cSrcweir                         // straight edge, add point
322cdf0e10cSrcweir                         aRetval.append(aEdge.getEndPoint());
323cdf0e10cSrcweir                     }
324cdf0e10cSrcweir 
325cdf0e10cSrcweir 					// prepare edge for next step
326cdf0e10cSrcweir 					aEdge.setStartPoint(aEdge.getEndPoint());
327cdf0e10cSrcweir                 }
328cdf0e10cSrcweir 
329cdf0e10cSrcweir 				// copy closed flag and check for double points
330cdf0e10cSrcweir                 aRetval.setClosed(rCandidate.isClosed());
331cdf0e10cSrcweir                 aRetval.removeDoublePoints();
332cdf0e10cSrcweir 
333cdf0e10cSrcweir                 return aRetval;
334cdf0e10cSrcweir             }
335cdf0e10cSrcweir             else
336cdf0e10cSrcweir             {
337cdf0e10cSrcweir                 return rCandidate;
338cdf0e10cSrcweir             }
339cdf0e10cSrcweir         }
340cdf0e10cSrcweir 
341cdf0e10cSrcweir 		B2DPolygon createAreaGeometryForEdge(const B2DCubicBezier& rEdge, double fHalfLineWidth)
342cdf0e10cSrcweir         {
343cdf0e10cSrcweir             // create polygon for edge
344cdf0e10cSrcweir             // Unfortunately, while it would be geometrically correct to not add
345cdf0e10cSrcweir             // the in-between points EdgeEnd and EdgeStart, it leads to rounding
346cdf0e10cSrcweir             // errors when converting to integer polygon coordinates for painting
347cdf0e10cSrcweir             if(rEdge.isBezier())
348cdf0e10cSrcweir             {
349cdf0e10cSrcweir                 // prepare target and data common for upper and lower
350cdf0e10cSrcweir                 B2DPolygon aBezierPolygon;
351cdf0e10cSrcweir 	            const B2DVector aPureEdgeVector(rEdge.getEndPoint() - rEdge.getStartPoint());
352cdf0e10cSrcweir                 const double fEdgeLength(aPureEdgeVector.getLength());
353cdf0e10cSrcweir                 const bool bIsEdgeLengthZero(fTools::equalZero(fEdgeLength));
354cdf0e10cSrcweir                 const B2DVector aTangentA(rEdge.getTangent(0.0));
355cdf0e10cSrcweir                 const B2DVector aTangentB(rEdge.getTangent(1.0));
356cdf0e10cSrcweir 
357cdf0e10cSrcweir                 // create upper edge.
358cdf0e10cSrcweir                 {
359cdf0e10cSrcweir                     // create displacement vectors and check if they cut
360cdf0e10cSrcweir                     const B2DVector aPerpendStart(getNormalizedPerpendicular(aTangentA) * -fHalfLineWidth);
361cdf0e10cSrcweir                     const B2DVector aPerpendEnd(getNormalizedPerpendicular(aTangentB) * -fHalfLineWidth);
362cdf0e10cSrcweir                     double fCut(0.0);
363cdf0e10cSrcweir                     const tools::CutFlagValue aCut(tools::findCut(
364cdf0e10cSrcweir                         rEdge.getStartPoint(), aPerpendStart,
365cdf0e10cSrcweir                         rEdge.getEndPoint(), aPerpendEnd,
366cdf0e10cSrcweir                         CUTFLAG_ALL, &fCut));
367cdf0e10cSrcweir 
368cdf0e10cSrcweir                     if(CUTFLAG_NONE != aCut)
369cdf0e10cSrcweir                     {
370cdf0e10cSrcweir                         // calculate cut point and add
371cdf0e10cSrcweir                         const B2DPoint aCutPoint(rEdge.getStartPoint() + (aPerpendStart * fCut));
372cdf0e10cSrcweir                         aBezierPolygon.append(aCutPoint);
373cdf0e10cSrcweir                     }
374cdf0e10cSrcweir                     else
375cdf0e10cSrcweir                     {
376cdf0e10cSrcweir                         // create scaled bezier segment
377cdf0e10cSrcweir                         const B2DPoint aStart(rEdge.getStartPoint() + aPerpendStart);
378cdf0e10cSrcweir                         const B2DPoint aEnd(rEdge.getEndPoint() + aPerpendEnd);
379cdf0e10cSrcweir                         const B2DVector aEdge(aEnd - aStart);
380cdf0e10cSrcweir                         const double fLength(aEdge.getLength());
381cdf0e10cSrcweir                         const double fScale(bIsEdgeLengthZero ? 1.0 : fLength / fEdgeLength);
382cdf0e10cSrcweir                         const B2DVector fRelNext(rEdge.getControlPointA() - rEdge.getStartPoint());
383cdf0e10cSrcweir                         const B2DVector fRelPrev(rEdge.getControlPointB() - rEdge.getEndPoint());
384cdf0e10cSrcweir 
385cdf0e10cSrcweir                         aBezierPolygon.append(aStart);
386cdf0e10cSrcweir                         aBezierPolygon.appendBezierSegment(aStart + (fRelNext * fScale), aEnd + (fRelPrev * fScale), aEnd);
387cdf0e10cSrcweir                     }
388cdf0e10cSrcweir                 }
389cdf0e10cSrcweir 
390cdf0e10cSrcweir                 // append original in-between point
391cdf0e10cSrcweir                 aBezierPolygon.append(rEdge.getEndPoint());
392cdf0e10cSrcweir 
393cdf0e10cSrcweir                 // create lower edge.
394cdf0e10cSrcweir                 {
395cdf0e10cSrcweir                     // create displacement vectors and check if they cut
396cdf0e10cSrcweir                     const B2DVector aPerpendStart(getNormalizedPerpendicular(aTangentA) * fHalfLineWidth);
397cdf0e10cSrcweir                     const B2DVector aPerpendEnd(getNormalizedPerpendicular(aTangentB) * fHalfLineWidth);
398cdf0e10cSrcweir                     double fCut(0.0);
399cdf0e10cSrcweir                     const tools::CutFlagValue aCut(tools::findCut(
400cdf0e10cSrcweir                         rEdge.getEndPoint(), aPerpendEnd,
401cdf0e10cSrcweir                         rEdge.getStartPoint(), aPerpendStart,
402cdf0e10cSrcweir                         CUTFLAG_ALL, &fCut));
403cdf0e10cSrcweir 
404cdf0e10cSrcweir                     if(CUTFLAG_NONE != aCut)
405cdf0e10cSrcweir                     {
406cdf0e10cSrcweir                         // calculate cut point and add
407cdf0e10cSrcweir                         const B2DPoint aCutPoint(rEdge.getEndPoint() + (aPerpendEnd * fCut));
408cdf0e10cSrcweir                         aBezierPolygon.append(aCutPoint);
409cdf0e10cSrcweir                     }
410cdf0e10cSrcweir                     else
411cdf0e10cSrcweir                     {
412cdf0e10cSrcweir                         // create scaled bezier segment
413cdf0e10cSrcweir                         const B2DPoint aStart(rEdge.getEndPoint() + aPerpendEnd);
414cdf0e10cSrcweir                         const B2DPoint aEnd(rEdge.getStartPoint() + aPerpendStart);
415cdf0e10cSrcweir                         const B2DVector aEdge(aEnd - aStart);
416cdf0e10cSrcweir                         const double fLength(aEdge.getLength());
417cdf0e10cSrcweir                         const double fScale(bIsEdgeLengthZero ? 1.0 : fLength / fEdgeLength);
418cdf0e10cSrcweir                         const B2DVector fRelNext(rEdge.getControlPointB() - rEdge.getEndPoint());
419cdf0e10cSrcweir                         const B2DVector fRelPrev(rEdge.getControlPointA() - rEdge.getStartPoint());
420cdf0e10cSrcweir 
421cdf0e10cSrcweir                         aBezierPolygon.append(aStart);
422cdf0e10cSrcweir                         aBezierPolygon.appendBezierSegment(aStart + (fRelNext * fScale), aEnd + (fRelPrev * fScale), aEnd);
423cdf0e10cSrcweir                     }
424cdf0e10cSrcweir                 }
425cdf0e10cSrcweir 
426cdf0e10cSrcweir                 // append original in-between point
427cdf0e10cSrcweir                 aBezierPolygon.append(rEdge.getStartPoint());
428cdf0e10cSrcweir 
429cdf0e10cSrcweir                 // close and return
430cdf0e10cSrcweir                 aBezierPolygon.setClosed(true);
431cdf0e10cSrcweir                 return aBezierPolygon;
432cdf0e10cSrcweir             }
433cdf0e10cSrcweir             else
434cdf0e10cSrcweir             {
435cdf0e10cSrcweir 				// #i101491# emulate rEdge.getTangent call which applies a factor of 0.3 to the
436cdf0e10cSrcweir 				// full-length edge vector to have numerically exactly the same results as in the
437cdf0e10cSrcweir 				// createAreaGeometryForJoin implementation
438cdf0e10cSrcweir 	            const B2DVector aEdgeTangent((rEdge.getEndPoint() - rEdge.getStartPoint()) * 0.3);
439cdf0e10cSrcweir                 const B2DVector aPerpendEdgeVector(getNormalizedPerpendicular(aEdgeTangent) * fHalfLineWidth);
440cdf0e10cSrcweir                 B2DPolygon aEdgePolygon;
441cdf0e10cSrcweir 
442cdf0e10cSrcweir                 // create upper edge
443cdf0e10cSrcweir                 aEdgePolygon.append(rEdge.getStartPoint() - aPerpendEdgeVector);
444cdf0e10cSrcweir                 aEdgePolygon.append(rEdge.getEndPoint() - aPerpendEdgeVector);
445cdf0e10cSrcweir 
446cdf0e10cSrcweir                 // append original in-between point
447cdf0e10cSrcweir                 aEdgePolygon.append(rEdge.getEndPoint());
448cdf0e10cSrcweir 
449cdf0e10cSrcweir                 // create lower edge
450cdf0e10cSrcweir                 aEdgePolygon.append(rEdge.getEndPoint() + aPerpendEdgeVector);
451cdf0e10cSrcweir                 aEdgePolygon.append(rEdge.getStartPoint() + aPerpendEdgeVector);
452cdf0e10cSrcweir 
453cdf0e10cSrcweir                 // append original in-between point
454cdf0e10cSrcweir                 aEdgePolygon.append(rEdge.getStartPoint());
455cdf0e10cSrcweir 
456cdf0e10cSrcweir                 // close and return
457cdf0e10cSrcweir                 aEdgePolygon.setClosed(true);
458cdf0e10cSrcweir                 return aEdgePolygon;
459cdf0e10cSrcweir             }
460cdf0e10cSrcweir         }
461cdf0e10cSrcweir 
462cdf0e10cSrcweir         B2DPolygon createAreaGeometryForJoin(
463cdf0e10cSrcweir             const B2DVector& rTangentPrev,
464cdf0e10cSrcweir             const B2DVector& rTangentEdge,
465cdf0e10cSrcweir             const B2DVector& rPerpendPrev,
466cdf0e10cSrcweir             const B2DVector& rPerpendEdge,
467cdf0e10cSrcweir             const B2DPoint& rPoint,
468cdf0e10cSrcweir             double fHalfLineWidth,
469cdf0e10cSrcweir             B2DLineJoin eJoin,
470cdf0e10cSrcweir             double fMiterMinimumAngle)
471cdf0e10cSrcweir 		{
472cdf0e10cSrcweir 			OSL_ENSURE(fHalfLineWidth > 0.0, "createAreaGeometryForJoin: LineWidth too small (!)");
473cdf0e10cSrcweir 			OSL_ENSURE(B2DLINEJOIN_NONE != eJoin, "createAreaGeometryForJoin: B2DLINEJOIN_NONE not allowed (!)");
474cdf0e10cSrcweir 
475cdf0e10cSrcweir             // LineJoin from tangent rPerpendPrev to tangent rPerpendEdge in rPoint
476cdf0e10cSrcweir             B2DPolygon aEdgePolygon;
477cdf0e10cSrcweir 			const B2DPoint aStartPoint(rPoint + rPerpendPrev);
478cdf0e10cSrcweir 			const B2DPoint aEndPoint(rPoint + rPerpendEdge);
479cdf0e10cSrcweir 
480cdf0e10cSrcweir 			// test if for Miter, the angle is too small and the fallback
481cdf0e10cSrcweir 			// to bevel needs to be used
482cdf0e10cSrcweir 			if(B2DLINEJOIN_MITER == eJoin)
483cdf0e10cSrcweir 			{
484cdf0e10cSrcweir 				const double fAngle(fabs(rPerpendPrev.angle(rPerpendEdge)));
485cdf0e10cSrcweir 
486cdf0e10cSrcweir 				if((F_PI - fAngle) < fMiterMinimumAngle)
487cdf0e10cSrcweir 				{
488cdf0e10cSrcweir 					// fallback to bevel
489cdf0e10cSrcweir 					eJoin = B2DLINEJOIN_BEVEL;
490cdf0e10cSrcweir 				}
491cdf0e10cSrcweir 			}
492cdf0e10cSrcweir 
493cdf0e10cSrcweir 			switch(eJoin)
494cdf0e10cSrcweir 			{
495cdf0e10cSrcweir 				case B2DLINEJOIN_MITER :
496cdf0e10cSrcweir 				{
497cdf0e10cSrcweir 					aEdgePolygon.append(aEndPoint);
498cdf0e10cSrcweir 					aEdgePolygon.append(rPoint);
499cdf0e10cSrcweir 					aEdgePolygon.append(aStartPoint);
500cdf0e10cSrcweir 
501cdf0e10cSrcweir 					// Look for the cut point between start point along rTangentPrev and
502cdf0e10cSrcweir 					// end point along rTangentEdge. -rTangentEdge should be used, but since
503cdf0e10cSrcweir 					// the cut value is used for interpolating along the first edge, the negation
504cdf0e10cSrcweir 					// is not needed since the same fCut will be found on the first edge.
505cdf0e10cSrcweir 					// If it exists, insert it to complete the mitered fill polygon.
506cdf0e10cSrcweir     				double fCutPos(0.0);
507cdf0e10cSrcweir 					tools::findCut(aStartPoint, rTangentPrev, aEndPoint, rTangentEdge, CUTFLAG_ALL, &fCutPos);
508cdf0e10cSrcweir 
509cdf0e10cSrcweir 					if(0.0 != fCutPos)
510cdf0e10cSrcweir 					{
511cdf0e10cSrcweir 						const B2DPoint aCutPoint(interpolate(aStartPoint, aStartPoint + rTangentPrev, fCutPos));
512cdf0e10cSrcweir 						aEdgePolygon.append(aCutPoint);
513cdf0e10cSrcweir 					}
514cdf0e10cSrcweir 
515cdf0e10cSrcweir 					break;
516cdf0e10cSrcweir 				}
517cdf0e10cSrcweir 				case B2DLINEJOIN_ROUND :
518cdf0e10cSrcweir 				{
519cdf0e10cSrcweir 					// use tooling to add needed EllipseSegment
520cdf0e10cSrcweir 					double fAngleStart(atan2(rPerpendPrev.getY(), rPerpendPrev.getX()));
521cdf0e10cSrcweir 					double fAngleEnd(atan2(rPerpendEdge.getY(), rPerpendEdge.getX()));
522cdf0e10cSrcweir 
523cdf0e10cSrcweir 					// atan2 results are [-PI .. PI], consolidate to [0.0 .. 2PI]
524cdf0e10cSrcweir 					if(fAngleStart < 0.0)
525cdf0e10cSrcweir 					{
526cdf0e10cSrcweir 						fAngleStart += F_2PI;
527cdf0e10cSrcweir 					}
528cdf0e10cSrcweir 
529cdf0e10cSrcweir 					if(fAngleEnd < 0.0)
530cdf0e10cSrcweir 					{
531cdf0e10cSrcweir 						fAngleEnd += F_2PI;
532cdf0e10cSrcweir 					}
533cdf0e10cSrcweir 
534cdf0e10cSrcweir 					const B2DPolygon aBow(tools::createPolygonFromEllipseSegment(rPoint, fHalfLineWidth, fHalfLineWidth, fAngleStart, fAngleEnd));
535cdf0e10cSrcweir 
536cdf0e10cSrcweir 					if(aBow.count() > 1)
537cdf0e10cSrcweir 					{
538cdf0e10cSrcweir 						// #i101491#
539cdf0e10cSrcweir 						// use the original start/end positions; the ones from bow creation may be numerically
540cdf0e10cSrcweir 						// different due to their different creation. To guarantee good merging quality with edges
541cdf0e10cSrcweir 						// and edge roundings (and to reduce point count)
542cdf0e10cSrcweir 						aEdgePolygon = aBow;
543cdf0e10cSrcweir 						aEdgePolygon.setB2DPoint(0, aStartPoint);
544cdf0e10cSrcweir 						aEdgePolygon.setB2DPoint(aEdgePolygon.count() - 1, aEndPoint);
545cdf0e10cSrcweir 						aEdgePolygon.append(rPoint);
546cdf0e10cSrcweir 
547cdf0e10cSrcweir 						break;
548cdf0e10cSrcweir 					}
549cdf0e10cSrcweir 					else
550cdf0e10cSrcweir 					{
551cdf0e10cSrcweir 						// wanted fall-through to default
552cdf0e10cSrcweir 					}
553cdf0e10cSrcweir 				}
554cdf0e10cSrcweir 				default: // B2DLINEJOIN_BEVEL
555cdf0e10cSrcweir 				{
556cdf0e10cSrcweir 					aEdgePolygon.append(aEndPoint);
557cdf0e10cSrcweir 					aEdgePolygon.append(rPoint);
558cdf0e10cSrcweir 					aEdgePolygon.append(aStartPoint);
559cdf0e10cSrcweir 
560cdf0e10cSrcweir 					break;
561cdf0e10cSrcweir 				}
562cdf0e10cSrcweir 			}
563cdf0e10cSrcweir 
564cdf0e10cSrcweir             // create last polygon part for edge
565cdf0e10cSrcweir             aEdgePolygon.setClosed(true);
566cdf0e10cSrcweir 
567cdf0e10cSrcweir             return aEdgePolygon;
568cdf0e10cSrcweir         }
569cdf0e10cSrcweir     } // end of anonymus namespace
570cdf0e10cSrcweir 
571cdf0e10cSrcweir 	namespace tools
572cdf0e10cSrcweir 	{
573cdf0e10cSrcweir         B2DPolyPolygon createAreaGeometry(
574cdf0e10cSrcweir             const B2DPolygon& rCandidate,
575cdf0e10cSrcweir             double fHalfLineWidth,
576cdf0e10cSrcweir             B2DLineJoin eJoin,
577cdf0e10cSrcweir             double fMaxAllowedAngle,
578cdf0e10cSrcweir 			double fMaxPartOfEdge,
579cdf0e10cSrcweir             double fMiterMinimumAngle)
580cdf0e10cSrcweir 		{
581cdf0e10cSrcweir             if(fMaxAllowedAngle > F_PI2)
582cdf0e10cSrcweir             {
583cdf0e10cSrcweir                 fMaxAllowedAngle = F_PI2;
584cdf0e10cSrcweir             }
585cdf0e10cSrcweir             else if(fMaxAllowedAngle < 0.01 * F_PI2)
586cdf0e10cSrcweir             {
587cdf0e10cSrcweir                 fMaxAllowedAngle = 0.01 * F_PI2;
588cdf0e10cSrcweir             }
589cdf0e10cSrcweir 
590cdf0e10cSrcweir             if(fMaxPartOfEdge > 1.0)
591cdf0e10cSrcweir             {
592cdf0e10cSrcweir                 fMaxPartOfEdge = 1.0;
593cdf0e10cSrcweir             }
594cdf0e10cSrcweir             else if(fMaxPartOfEdge < 0.01)
595cdf0e10cSrcweir             {
596cdf0e10cSrcweir                 fMaxPartOfEdge = 0.01;
597cdf0e10cSrcweir             }
598cdf0e10cSrcweir 
599cdf0e10cSrcweir             if(fMiterMinimumAngle > F_PI)
600cdf0e10cSrcweir             {
601cdf0e10cSrcweir                 fMiterMinimumAngle = F_PI;
602cdf0e10cSrcweir             }
603cdf0e10cSrcweir             else if(fMiterMinimumAngle < 0.01 * F_PI)
604cdf0e10cSrcweir             {
605cdf0e10cSrcweir                 fMiterMinimumAngle = 0.01 * F_PI;
606cdf0e10cSrcweir             }
607cdf0e10cSrcweir 
608cdf0e10cSrcweir             B2DPolygon aCandidate(rCandidate);
609cdf0e10cSrcweir             const double fMaxCos(cos(fMaxAllowedAngle));
610cdf0e10cSrcweir 
611cdf0e10cSrcweir             aCandidate.removeDoublePoints();
612cdf0e10cSrcweir             aCandidate = subdivideToSimple(aCandidate, fMaxCos * fMaxCos, fMaxPartOfEdge * fMaxPartOfEdge);
613cdf0e10cSrcweir 
614cdf0e10cSrcweir             const sal_uInt32 nPointCount(aCandidate.count());
615cdf0e10cSrcweir 
616cdf0e10cSrcweir 			if(nPointCount)
617cdf0e10cSrcweir 			{
618cdf0e10cSrcweir                 B2DPolyPolygon aRetval;
619cdf0e10cSrcweir 				const bool bEventuallyCreateLineJoin(B2DLINEJOIN_NONE != eJoin);
620cdf0e10cSrcweir                 const bool bIsClosed(aCandidate.isClosed());
621cdf0e10cSrcweir                 const sal_uInt32 nEdgeCount(bIsClosed ? nPointCount : nPointCount - 1);
622cdf0e10cSrcweir 
623cdf0e10cSrcweir                 if(nEdgeCount)
624cdf0e10cSrcweir                 {
625cdf0e10cSrcweir                     B2DCubicBezier aEdge;
626cdf0e10cSrcweir                     B2DCubicBezier aPrev;
627cdf0e10cSrcweir 
628cdf0e10cSrcweir                     // prepare edge
629cdf0e10cSrcweir                     aEdge.setStartPoint(aCandidate.getB2DPoint(0));
630cdf0e10cSrcweir 
631cdf0e10cSrcweir                     if(bIsClosed && bEventuallyCreateLineJoin)
632cdf0e10cSrcweir                     {
633cdf0e10cSrcweir                         // prepare previous edge
634cdf0e10cSrcweir                         const sal_uInt32 nPrevIndex(nPointCount - 1);
635cdf0e10cSrcweir                         aPrev.setStartPoint(aCandidate.getB2DPoint(nPrevIndex));
636cdf0e10cSrcweir                         aPrev.setControlPointA(aCandidate.getNextControlPoint(nPrevIndex));
637cdf0e10cSrcweir                         aPrev.setControlPointB(aCandidate.getPrevControlPoint(0));
638cdf0e10cSrcweir                         aPrev.setEndPoint(aEdge.getStartPoint());
639cdf0e10cSrcweir                     }
640cdf0e10cSrcweir 
641cdf0e10cSrcweir                     for(sal_uInt32 a(0); a < nEdgeCount; a++)
642cdf0e10cSrcweir                     {
643cdf0e10cSrcweir                         // fill current Edge
644cdf0e10cSrcweir                         const sal_uInt32 nNextIndex((a + 1) % nPointCount);
645cdf0e10cSrcweir                         aEdge.setControlPointA(aCandidate.getNextControlPoint(a));
646cdf0e10cSrcweir                         aEdge.setControlPointB(aCandidate.getPrevControlPoint(nNextIndex));
647cdf0e10cSrcweir                         aEdge.setEndPoint(aCandidate.getB2DPoint(nNextIndex));
648cdf0e10cSrcweir 
649cdf0e10cSrcweir                         // check and create linejoin
650cdf0e10cSrcweir                         if(bEventuallyCreateLineJoin && (bIsClosed || 0 != a))
651cdf0e10cSrcweir                         {
652cdf0e10cSrcweir 				            const B2DVector aTangentPrev(aPrev.getTangent(1.0));
653cdf0e10cSrcweir 				            const B2DVector aTangentEdge(aEdge.getTangent(0.0));
654cdf0e10cSrcweir                             B2VectorOrientation aOrientation(getOrientation(aTangentPrev, aTangentEdge));
655cdf0e10cSrcweir 
656cdf0e10cSrcweir 							if(ORIENTATION_NEUTRAL == aOrientation)
657cdf0e10cSrcweir 							{
658cdf0e10cSrcweir 								// they are parallell or empty; if they are both not zero and point
659cdf0e10cSrcweir 								// in opposite direction, a half-circle is needed
660cdf0e10cSrcweir 								if(!aTangentPrev.equalZero() && !aTangentEdge.equalZero())
661cdf0e10cSrcweir 								{
662cdf0e10cSrcweir 									const double fAngle(fabs(aTangentPrev.angle(aTangentEdge)));
663cdf0e10cSrcweir 
664cdf0e10cSrcweir 									if(fTools::equal(fAngle, F_PI))
665cdf0e10cSrcweir 									{
666cdf0e10cSrcweir                                         // for half-circle production, fallback to positive
667cdf0e10cSrcweir                                         // orientation
668cdf0e10cSrcweir 										aOrientation = ORIENTATION_POSITIVE;
669cdf0e10cSrcweir 									}
670cdf0e10cSrcweir 								}
671cdf0e10cSrcweir 							}
672cdf0e10cSrcweir 
673cdf0e10cSrcweir                             if(ORIENTATION_POSITIVE == aOrientation)
674cdf0e10cSrcweir                             {
675cdf0e10cSrcweir 								const B2DVector aPerpendPrev(getNormalizedPerpendicular(aTangentPrev) * -fHalfLineWidth);
676cdf0e10cSrcweir 								const B2DVector aPerpendEdge(getNormalizedPerpendicular(aTangentEdge) * -fHalfLineWidth);
677cdf0e10cSrcweir 
678cdf0e10cSrcweir 								aRetval.append(createAreaGeometryForJoin(
679cdf0e10cSrcweir                                     aTangentPrev, aTangentEdge,
680cdf0e10cSrcweir                                     aPerpendPrev, aPerpendEdge,
681cdf0e10cSrcweir 									aEdge.getStartPoint(), fHalfLineWidth,
682cdf0e10cSrcweir                                     eJoin, fMiterMinimumAngle));
683cdf0e10cSrcweir                             }
684cdf0e10cSrcweir                             else if(ORIENTATION_NEGATIVE == aOrientation)
685cdf0e10cSrcweir                             {
686cdf0e10cSrcweir 								const B2DVector aPerpendPrev(getNormalizedPerpendicular(aTangentPrev) * fHalfLineWidth);
687cdf0e10cSrcweir 								const B2DVector aPerpendEdge(getNormalizedPerpendicular(aTangentEdge) * fHalfLineWidth);
688cdf0e10cSrcweir 
689cdf0e10cSrcweir 								aRetval.append(createAreaGeometryForJoin(
690cdf0e10cSrcweir                                     aTangentEdge, aTangentPrev,
691cdf0e10cSrcweir                                     aPerpendEdge, aPerpendPrev,
692cdf0e10cSrcweir 									aEdge.getStartPoint(), fHalfLineWidth,
693cdf0e10cSrcweir                                     eJoin, fMiterMinimumAngle));
694cdf0e10cSrcweir                             }
695cdf0e10cSrcweir                         }
696cdf0e10cSrcweir 
697cdf0e10cSrcweir                         // create geometry for edge
698cdf0e10cSrcweir                 		aRetval.append(createAreaGeometryForEdge(aEdge, fHalfLineWidth));
699cdf0e10cSrcweir 
700cdf0e10cSrcweir                         // prepare next step
701cdf0e10cSrcweir                         if(bEventuallyCreateLineJoin)
702cdf0e10cSrcweir                         {
703cdf0e10cSrcweir                             aPrev = aEdge;
704cdf0e10cSrcweir                         }
705cdf0e10cSrcweir 
706cdf0e10cSrcweir                         aEdge.setStartPoint(aEdge.getEndPoint());
707cdf0e10cSrcweir                     }
708cdf0e10cSrcweir                 }
709cdf0e10cSrcweir 
710cdf0e10cSrcweir                 return aRetval;
711cdf0e10cSrcweir 			}
712cdf0e10cSrcweir             else
713cdf0e10cSrcweir             {
714cdf0e10cSrcweir                 return B2DPolyPolygon(rCandidate);
715cdf0e10cSrcweir             }
716cdf0e10cSrcweir 		}
717cdf0e10cSrcweir 	} // end of namespace tools
718cdf0e10cSrcweir } // end of namespace basegfx
719cdf0e10cSrcweir 
720cdf0e10cSrcweir //////////////////////////////////////////////////////////////////////////////
721cdf0e10cSrcweir // eof
722