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