xref: /AOO41X/main/basegfx/source/polygon/b2dlinegeometry.cxx (revision 5aaf853b3ba91aa8a4f8154519fb0bf086e1a428)
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
10cdf0e10cSrcweir  *
1109dbbe93SAndrew Rist  *   http://www.apache.org/licenses/LICENSE-2.0
12cdf0e10cSrcweir  *
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.
19cdf0e10cSrcweir  *
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 <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>
37*5aaf853bSArmin Le Grand #include <com/sun/star/drawing/LineCap.hpp>
38cdf0e10cSrcweir 
39cdf0e10cSrcweir //////////////////////////////////////////////////////////////////////////////
40cdf0e10cSrcweir 
41cdf0e10cSrcweir namespace basegfx
42cdf0e10cSrcweir {
43cdf0e10cSrcweir 	namespace tools
44cdf0e10cSrcweir 	{
45cdf0e10cSrcweir 		B2DPolyPolygon createAreaGeometryForLineStartEnd(
46cdf0e10cSrcweir 			const B2DPolygon& rCandidate,
47cdf0e10cSrcweir 			const B2DPolyPolygon& rArrow,
48cdf0e10cSrcweir 			bool bStart,
49cdf0e10cSrcweir 			double fWidth,
50cdf0e10cSrcweir 			double fCandidateLength,
51cdf0e10cSrcweir 			double fDockingPosition, // 0->top, 1->bottom
52cdf0e10cSrcweir 			double* pConsumedLength)
53cdf0e10cSrcweir 		{
54cdf0e10cSrcweir 			B2DPolyPolygon aRetval;
55cdf0e10cSrcweir 			OSL_ENSURE(rCandidate.count() > 1L, "createAreaGeometryForLineStartEnd: Line polygon has too less points (!)");
56cdf0e10cSrcweir 			OSL_ENSURE(rArrow.count() > 0L, "createAreaGeometryForLineStartEnd: Empty arrow PolyPolygon (!)");
57cdf0e10cSrcweir 			OSL_ENSURE(fWidth > 0.0, "createAreaGeometryForLineStartEnd: Width too small (!)");
58cdf0e10cSrcweir 			OSL_ENSURE(fDockingPosition >= 0.0 && fDockingPosition <= 1.0,
59cdf0e10cSrcweir 				"createAreaGeometryForLineStartEnd: fDockingPosition out of range [0.0 .. 1.0] (!)");
60cdf0e10cSrcweir 
61cdf0e10cSrcweir             if(fWidth < 0.0)
62cdf0e10cSrcweir             {
63cdf0e10cSrcweir                 fWidth = -fWidth;
64cdf0e10cSrcweir             }
65cdf0e10cSrcweir 
66cdf0e10cSrcweir             if(rCandidate.count() > 1 && rArrow.count() && !fTools::equalZero(fWidth))
67cdf0e10cSrcweir             {
68cdf0e10cSrcweir                 if(fDockingPosition < 0.0)
69cdf0e10cSrcweir                 {
70cdf0e10cSrcweir                     fDockingPosition = 0.0;
71cdf0e10cSrcweir                 }
72cdf0e10cSrcweir                 else if(fDockingPosition > 1.0)
73cdf0e10cSrcweir                 {
74cdf0e10cSrcweir                     fDockingPosition = 1.0;
75cdf0e10cSrcweir                 }
76cdf0e10cSrcweir 
77cdf0e10cSrcweir 			    // init return value from arrow
78cdf0e10cSrcweir 			    aRetval.append(rArrow);
79cdf0e10cSrcweir 
80cdf0e10cSrcweir 			    // get size of the arrow
81cdf0e10cSrcweir 			    const B2DRange aArrowSize(getRange(rArrow));
82cdf0e10cSrcweir 
83cdf0e10cSrcweir 			    // build ArrowTransform; center in X, align with axis in Y
84cdf0e10cSrcweir                 B2DHomMatrix aArrowTransform(basegfx::tools::createTranslateB2DHomMatrix(
85cdf0e10cSrcweir                     -aArrowSize.getCenter().getX(), -aArrowSize.getMinimum().getY()));
86cdf0e10cSrcweir 
87cdf0e10cSrcweir 			    // scale to target size
88cdf0e10cSrcweir 			    const double fArrowScale(fWidth / (aArrowSize.getRange().getX()));
89cdf0e10cSrcweir 			    aArrowTransform.scale(fArrowScale, fArrowScale);
90cdf0e10cSrcweir 
91cdf0e10cSrcweir 			    // get arrow size in Y
92cdf0e10cSrcweir 			    B2DPoint aUpperCenter(aArrowSize.getCenter().getX(), aArrowSize.getMaximum().getY());
93cdf0e10cSrcweir 			    aUpperCenter *= aArrowTransform;
94cdf0e10cSrcweir 			    const double fArrowYLength(B2DVector(aUpperCenter).getLength());
95cdf0e10cSrcweir 
96cdf0e10cSrcweir 			    // move arrow to have docking position centered
97cdf0e10cSrcweir 			    aArrowTransform.translate(0.0, -fArrowYLength * fDockingPosition);
98cdf0e10cSrcweir 
99cdf0e10cSrcweir 			    // prepare polygon length
100cdf0e10cSrcweir 			    if(fTools::equalZero(fCandidateLength))
101cdf0e10cSrcweir 			    {
102cdf0e10cSrcweir 				    fCandidateLength = getLength(rCandidate);
103cdf0e10cSrcweir 			    }
104cdf0e10cSrcweir 
105cdf0e10cSrcweir 			    // get the polygon vector we want to plant this arrow on
106cdf0e10cSrcweir 			    const double fConsumedLength(fArrowYLength * (1.0 - fDockingPosition));
107cdf0e10cSrcweir 			    const B2DVector aHead(rCandidate.getB2DPoint((bStart) ? 0L : rCandidate.count() - 1L));
108cdf0e10cSrcweir 			    const B2DVector aTail(getPositionAbsolute(rCandidate,
109cdf0e10cSrcweir 				    (bStart) ? fConsumedLength : fCandidateLength - fConsumedLength, fCandidateLength));
110cdf0e10cSrcweir 
111cdf0e10cSrcweir 			    // from that vector, take the needed rotation and add rotate for arrow to transformation
112cdf0e10cSrcweir 			    const B2DVector aTargetDirection(aHead - aTail);
113cdf0e10cSrcweir 			    const double fRotation(atan2(aTargetDirection.getY(), aTargetDirection.getX()) + (90.0 * F_PI180));
114cdf0e10cSrcweir 
115cdf0e10cSrcweir 			    // rotate around docking position
116cdf0e10cSrcweir 			    aArrowTransform.rotate(fRotation);
117cdf0e10cSrcweir 
118cdf0e10cSrcweir 			    // move arrow docking position to polygon head
119cdf0e10cSrcweir 			    aArrowTransform.translate(aHead.getX(), aHead.getY());
120cdf0e10cSrcweir 
121cdf0e10cSrcweir 			    // transform retval and close
122cdf0e10cSrcweir 			    aRetval.transform(aArrowTransform);
123cdf0e10cSrcweir 			    aRetval.setClosed(true);
124cdf0e10cSrcweir 
125cdf0e10cSrcweir 			    // if pConsumedLength is asked for, fill it
126cdf0e10cSrcweir 			    if(pConsumedLength)
127cdf0e10cSrcweir 			    {
128cdf0e10cSrcweir 				    *pConsumedLength = fConsumedLength;
129cdf0e10cSrcweir 			    }
130cdf0e10cSrcweir             }
131cdf0e10cSrcweir 
132cdf0e10cSrcweir 			return aRetval;
133cdf0e10cSrcweir 		}
134cdf0e10cSrcweir 	} // end of namespace tools
135cdf0e10cSrcweir } // end of namespace basegfx
136cdf0e10cSrcweir 
137cdf0e10cSrcweir //////////////////////////////////////////////////////////////////////////////
138cdf0e10cSrcweir 
139cdf0e10cSrcweir namespace basegfx
140cdf0e10cSrcweir {
141cdf0e10cSrcweir 	// anonymus namespace for local helpers
142cdf0e10cSrcweir 	namespace
143cdf0e10cSrcweir 	{
144cdf0e10cSrcweir         bool impIsSimpleEdge(const B2DCubicBezier& rCandidate, double fMaxCosQuad, double fMaxPartOfEdgeQuad)
145cdf0e10cSrcweir         {
146cdf0e10cSrcweir             // isBezier() is true, already tested by caller
147cdf0e10cSrcweir             const B2DVector aEdge(rCandidate.getEndPoint() - rCandidate.getStartPoint());
148cdf0e10cSrcweir 
149cdf0e10cSrcweir             if(aEdge.equalZero())
150cdf0e10cSrcweir             {
151cdf0e10cSrcweir                 // start and end point the same, but control vectors used -> baloon curve loop
152cdf0e10cSrcweir                 // is not a simple edge
153cdf0e10cSrcweir                 return false;
154cdf0e10cSrcweir             }
155cdf0e10cSrcweir 
156cdf0e10cSrcweir             // get tangentA and scalar with edge
157cdf0e10cSrcweir             const B2DVector aTangentA(rCandidate.getTangent(0.0));
158cdf0e10cSrcweir     		const double fScalarAE(aEdge.scalar(aTangentA));
159cdf0e10cSrcweir 
160cdf0e10cSrcweir             if(fTools::lessOrEqual(fScalarAE, 0.0))
161cdf0e10cSrcweir             {
162cdf0e10cSrcweir                 // angle between TangentA and Edge is bigger or equal 90 degrees
163cdf0e10cSrcweir                 return false;
164cdf0e10cSrcweir             }
165cdf0e10cSrcweir 
166cdf0e10cSrcweir             // get self-scalars for E and A
167cdf0e10cSrcweir     		const double fScalarE(aEdge.scalar(aEdge));
168cdf0e10cSrcweir     		const double fScalarA(aTangentA.scalar(aTangentA));
169cdf0e10cSrcweir 			const double fLengthCompareE(fScalarE * fMaxPartOfEdgeQuad);
170cdf0e10cSrcweir 
171cdf0e10cSrcweir 			if(fTools::moreOrEqual(fScalarA, fLengthCompareE))
172cdf0e10cSrcweir 			{
173cdf0e10cSrcweir 				// length of TangentA is more than fMaxPartOfEdge of length of edge
174cdf0e10cSrcweir 				return false;
175cdf0e10cSrcweir 			}
176cdf0e10cSrcweir 
177cdf0e10cSrcweir             if(fTools::lessOrEqual(fScalarAE * fScalarAE, fScalarA * fScalarE * fMaxCosQuad))
178cdf0e10cSrcweir             {
179cdf0e10cSrcweir                 // angle between TangentA and Edge is bigger or equal angle defined by fMaxCos
180cdf0e10cSrcweir                 return false;
181cdf0e10cSrcweir             }
182cdf0e10cSrcweir 
183cdf0e10cSrcweir             // get tangentB and scalar with edge
184cdf0e10cSrcweir             const B2DVector aTangentB(rCandidate.getTangent(1.0));
185cdf0e10cSrcweir     		const double fScalarBE(aEdge.scalar(aTangentB));
186cdf0e10cSrcweir 
187cdf0e10cSrcweir             if(fTools::lessOrEqual(fScalarBE, 0.0))
188cdf0e10cSrcweir             {
189cdf0e10cSrcweir                 // angle between TangentB and Edge is bigger or equal 90 degrees
190cdf0e10cSrcweir                 return false;
191cdf0e10cSrcweir             }
192cdf0e10cSrcweir 
193cdf0e10cSrcweir             // get self-scalar for B
194cdf0e10cSrcweir     		const double fScalarB(aTangentB.scalar(aTangentB));
195cdf0e10cSrcweir 
196cdf0e10cSrcweir 			if(fTools::moreOrEqual(fScalarB, fLengthCompareE))
197cdf0e10cSrcweir 			{
198cdf0e10cSrcweir 				// length of TangentB is more than fMaxPartOfEdge of length of edge
199cdf0e10cSrcweir 				return false;
200cdf0e10cSrcweir 			}
201cdf0e10cSrcweir 
202cdf0e10cSrcweir 			if(fTools::lessOrEqual(fScalarBE * fScalarBE, fScalarB * fScalarE * fMaxCosQuad))
203cdf0e10cSrcweir             {
204cdf0e10cSrcweir                 // angle between TangentB and Edge is bigger or equal defined by fMaxCos
205cdf0e10cSrcweir                 return false;
206cdf0e10cSrcweir             }
207cdf0e10cSrcweir 
208cdf0e10cSrcweir             return true;
209cdf0e10cSrcweir         }
210cdf0e10cSrcweir 
211cdf0e10cSrcweir         void impSubdivideToSimple(const B2DCubicBezier& rCandidate, B2DPolygon& rTarget, double fMaxCosQuad, double fMaxPartOfEdgeQuad, sal_uInt32 nMaxRecursionDepth)
212cdf0e10cSrcweir         {
213cdf0e10cSrcweir             if(!nMaxRecursionDepth || impIsSimpleEdge(rCandidate, fMaxCosQuad, fMaxPartOfEdgeQuad))
214cdf0e10cSrcweir             {
215cdf0e10cSrcweir                 rTarget.appendBezierSegment(rCandidate.getControlPointA(), rCandidate.getControlPointB(), rCandidate.getEndPoint());
216cdf0e10cSrcweir             }
217cdf0e10cSrcweir             else
218cdf0e10cSrcweir             {
219cdf0e10cSrcweir                 B2DCubicBezier aLeft, aRight;
220cdf0e10cSrcweir                 rCandidate.split(0.5, &aLeft, &aRight);
221cdf0e10cSrcweir 
222cdf0e10cSrcweir                 impSubdivideToSimple(aLeft, rTarget, fMaxCosQuad, fMaxPartOfEdgeQuad, nMaxRecursionDepth - 1);
223cdf0e10cSrcweir                 impSubdivideToSimple(aRight, rTarget, fMaxCosQuad, fMaxPartOfEdgeQuad, nMaxRecursionDepth - 1);
224cdf0e10cSrcweir             }
225cdf0e10cSrcweir         }
226cdf0e10cSrcweir 
227cdf0e10cSrcweir         B2DPolygon subdivideToSimple(const B2DPolygon& rCandidate, double fMaxCosQuad, double fMaxPartOfEdgeQuad)
228cdf0e10cSrcweir         {
229cdf0e10cSrcweir             const sal_uInt32 nPointCount(rCandidate.count());
230cdf0e10cSrcweir 
231cdf0e10cSrcweir             if(rCandidate.areControlPointsUsed() && nPointCount)
232cdf0e10cSrcweir             {
233cdf0e10cSrcweir                 const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
234cdf0e10cSrcweir                 B2DPolygon aRetval;
235cdf0e10cSrcweir 				B2DCubicBezier aEdge;
236cdf0e10cSrcweir 
237cdf0e10cSrcweir 				// prepare edge for loop
238cdf0e10cSrcweir 				aEdge.setStartPoint(rCandidate.getB2DPoint(0));
239cdf0e10cSrcweir 				aRetval.append(aEdge.getStartPoint());
240cdf0e10cSrcweir 
241cdf0e10cSrcweir                 for(sal_uInt32 a(0); a < nEdgeCount; a++)
242cdf0e10cSrcweir                 {
243cdf0e10cSrcweir                     // fill B2DCubicBezier
244cdf0e10cSrcweir                     const sal_uInt32 nNextIndex((a + 1) % nPointCount);
245cdf0e10cSrcweir                     aEdge.setControlPointA(rCandidate.getNextControlPoint(a));
246cdf0e10cSrcweir                     aEdge.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex));
247cdf0e10cSrcweir                     aEdge.setEndPoint(rCandidate.getB2DPoint(nNextIndex));
248cdf0e10cSrcweir 
249cdf0e10cSrcweir                     // get rid of unnecessary bezier segments
250cdf0e10cSrcweir                     aEdge.testAndSolveTrivialBezier();
251cdf0e10cSrcweir 
252cdf0e10cSrcweir                     if(aEdge.isBezier())
253cdf0e10cSrcweir                     {
254cdf0e10cSrcweir 						// before splitting recursively with internal simple criteria, use
255cdf0e10cSrcweir 						// ExtremumPosFinder to remove those
256cdf0e10cSrcweir                 		::std::vector< double > aExtremumPositions;
257cdf0e10cSrcweir 
258cdf0e10cSrcweir                         aExtremumPositions.reserve(4);
259cdf0e10cSrcweir 		                aEdge.getAllExtremumPositions(aExtremumPositions);
260cdf0e10cSrcweir 
261cdf0e10cSrcweir                         const sal_uInt32 nCount(aExtremumPositions.size());
262cdf0e10cSrcweir 
263cdf0e10cSrcweir                         if(nCount)
264cdf0e10cSrcweir                         {
265cdf0e10cSrcweir                             if(nCount > 1)
266cdf0e10cSrcweir                             {
267cdf0e10cSrcweir                                 // create order from left to right
268cdf0e10cSrcweir                                 ::std::sort(aExtremumPositions.begin(), aExtremumPositions.end());
269cdf0e10cSrcweir                             }
270cdf0e10cSrcweir 
271cdf0e10cSrcweir                             for(sal_uInt32 b(0); b < nCount;)
272cdf0e10cSrcweir                             {
273cdf0e10cSrcweir                                 // split aEdge at next split pos
274cdf0e10cSrcweir                                 B2DCubicBezier aLeft;
275cdf0e10cSrcweir                                 const double fSplitPos(aExtremumPositions[b++]);
276cdf0e10cSrcweir 
277cdf0e10cSrcweir                                 aEdge.split(fSplitPos, &aLeft, &aEdge);
278cdf0e10cSrcweir                                 aLeft.testAndSolveTrivialBezier();
279cdf0e10cSrcweir 
280cdf0e10cSrcweir                                 // consume left part
281cdf0e10cSrcweir                                 if(aLeft.isBezier())
282cdf0e10cSrcweir 						        {
283cdf0e10cSrcweir 	                                impSubdivideToSimple(aLeft, aRetval, fMaxCosQuad, fMaxPartOfEdgeQuad, 6);
284cdf0e10cSrcweir 						        }
285cdf0e10cSrcweir 						        else
286cdf0e10cSrcweir 						        {
287cdf0e10cSrcweir 	                                aRetval.append(aLeft.getEndPoint());
288cdf0e10cSrcweir 						        }
289cdf0e10cSrcweir 
290cdf0e10cSrcweir                                 if(b < nCount)
291cdf0e10cSrcweir                                 {
292cdf0e10cSrcweir                                     // correct the remaining split positions to fit to shortened aEdge
293cdf0e10cSrcweir                                     const double fScaleFactor(1.0 / (1.0 - fSplitPos));
294cdf0e10cSrcweir 
295cdf0e10cSrcweir                                     for(sal_uInt32 c(b); c < nCount; c++)
296cdf0e10cSrcweir                                     {
297cdf0e10cSrcweir                                         aExtremumPositions[c] = (aExtremumPositions[c] - fSplitPos) * fScaleFactor;
298cdf0e10cSrcweir                                     }
299cdf0e10cSrcweir                                 }
300cdf0e10cSrcweir                             }
301cdf0e10cSrcweir 
302cdf0e10cSrcweir                             // test the shortened rest of aEdge
303cdf0e10cSrcweir                             aEdge.testAndSolveTrivialBezier();
304cdf0e10cSrcweir 
305cdf0e10cSrcweir                             // consume right part
306cdf0e10cSrcweir                             if(aEdge.isBezier())
307cdf0e10cSrcweir 					        {
308cdf0e10cSrcweir                                 impSubdivideToSimple(aEdge, aRetval, fMaxCosQuad, fMaxPartOfEdgeQuad, 6);
309cdf0e10cSrcweir 					        }
310cdf0e10cSrcweir 					        else
311cdf0e10cSrcweir 					        {
312cdf0e10cSrcweir                                 aRetval.append(aEdge.getEndPoint());
313cdf0e10cSrcweir 					        }
314cdf0e10cSrcweir                         }
315cdf0e10cSrcweir                         else
316cdf0e10cSrcweir                         {
317cdf0e10cSrcweir 	                        impSubdivideToSimple(aEdge, aRetval, fMaxCosQuad, fMaxPartOfEdgeQuad, 6);
318cdf0e10cSrcweir                         }
319cdf0e10cSrcweir                     }
320cdf0e10cSrcweir                     else
321cdf0e10cSrcweir                     {
322cdf0e10cSrcweir                         // straight edge, add point
323cdf0e10cSrcweir                         aRetval.append(aEdge.getEndPoint());
324cdf0e10cSrcweir                     }
325cdf0e10cSrcweir 
326cdf0e10cSrcweir 					// prepare edge for next step
327cdf0e10cSrcweir 					aEdge.setStartPoint(aEdge.getEndPoint());
328cdf0e10cSrcweir                 }
329cdf0e10cSrcweir 
330cdf0e10cSrcweir 				// copy closed flag and check for double points
331cdf0e10cSrcweir                 aRetval.setClosed(rCandidate.isClosed());
332cdf0e10cSrcweir                 aRetval.removeDoublePoints();
333cdf0e10cSrcweir 
334cdf0e10cSrcweir                 return aRetval;
335cdf0e10cSrcweir             }
336cdf0e10cSrcweir             else
337cdf0e10cSrcweir             {
338cdf0e10cSrcweir                 return rCandidate;
339cdf0e10cSrcweir             }
340cdf0e10cSrcweir         }
341cdf0e10cSrcweir 
342*5aaf853bSArmin Le Grand         B2DPolyPolygon createAreaGeometryForEdge(
343*5aaf853bSArmin Le Grand             const B2DCubicBezier& rEdge,
344*5aaf853bSArmin Le Grand             double fHalfLineWidth,
345*5aaf853bSArmin Le Grand             bool bStartRound,
346*5aaf853bSArmin Le Grand             bool bEndRound,
347*5aaf853bSArmin Le Grand             bool bStartSquare,
348*5aaf853bSArmin Le Grand             bool bEndSquare)
349cdf0e10cSrcweir         {
350cdf0e10cSrcweir             // create polygon for edge
351cdf0e10cSrcweir             // Unfortunately, while it would be geometrically correct to not add
352cdf0e10cSrcweir             // the in-between points EdgeEnd and EdgeStart, it leads to rounding
353cdf0e10cSrcweir             // errors when converting to integer polygon coordinates for painting
354cdf0e10cSrcweir             if(rEdge.isBezier())
355cdf0e10cSrcweir             {
356cdf0e10cSrcweir                 // prepare target and data common for upper and lower
357*5aaf853bSArmin Le Grand                 B2DPolyPolygon aRetval;
358cdf0e10cSrcweir                 B2DPolygon aBezierPolygon;
359cdf0e10cSrcweir                 const B2DVector aPureEdgeVector(rEdge.getEndPoint() - rEdge.getStartPoint());
360cdf0e10cSrcweir                 const double fEdgeLength(aPureEdgeVector.getLength());
361cdf0e10cSrcweir                 const bool bIsEdgeLengthZero(fTools::equalZero(fEdgeLength));
362*5aaf853bSArmin Le Grand                 B2DVector aTangentA(rEdge.getTangent(0.0)); aTangentA.normalize();
363*5aaf853bSArmin Le Grand                 B2DVector aTangentB(rEdge.getTangent(1.0)); aTangentB.normalize();
364*5aaf853bSArmin Le Grand                 const B2DVector aNormalizedPerpendicularA(getPerpendicular(aTangentA));
365*5aaf853bSArmin Le Grand                 const B2DVector aNormalizedPerpendicularB(getPerpendicular(aTangentB));
366*5aaf853bSArmin Le Grand 
367*5aaf853bSArmin Le Grand                 // create upper displacement vectors and check if they cut
368*5aaf853bSArmin Le Grand                 const B2DVector aPerpendStartA(aNormalizedPerpendicularA * -fHalfLineWidth);
369*5aaf853bSArmin Le Grand                 const B2DVector aPerpendEndA(aNormalizedPerpendicularB * -fHalfLineWidth);
370*5aaf853bSArmin Le Grand                 double fCutA(0.0);
371*5aaf853bSArmin Le Grand                 const tools::CutFlagValue aCutA(tools::findCut(
372*5aaf853bSArmin Le Grand                     rEdge.getStartPoint(), aPerpendStartA,
373*5aaf853bSArmin Le Grand                     rEdge.getEndPoint(), aPerpendEndA,
374*5aaf853bSArmin Le Grand                     CUTFLAG_ALL, &fCutA));
375*5aaf853bSArmin Le Grand                 const bool bCutA(CUTFLAG_NONE != aCutA);
376*5aaf853bSArmin Le Grand 
377*5aaf853bSArmin Le Grand                 // create lower displacement vectors and check if they cut
378*5aaf853bSArmin Le Grand                 const B2DVector aPerpendStartB(aNormalizedPerpendicularA * fHalfLineWidth);
379*5aaf853bSArmin Le Grand                 const B2DVector aPerpendEndB(aNormalizedPerpendicularB * fHalfLineWidth);
380*5aaf853bSArmin Le Grand                 double fCutB(0.0);
381*5aaf853bSArmin Le Grand                 const tools::CutFlagValue aCutB(tools::findCut(
382*5aaf853bSArmin Le Grand                     rEdge.getEndPoint(), aPerpendEndB,
383*5aaf853bSArmin Le Grand                     rEdge.getStartPoint(), aPerpendStartB,
384*5aaf853bSArmin Le Grand                     CUTFLAG_ALL, &fCutB));
385*5aaf853bSArmin Le Grand                 const bool bCutB(CUTFLAG_NONE != aCutB);
386*5aaf853bSArmin Le Grand 
387*5aaf853bSArmin Le Grand                 // check if cut happens
388*5aaf853bSArmin Le Grand                 const bool bCut(bCutA || bCutB);
389*5aaf853bSArmin Le Grand 
390*5aaf853bSArmin Le Grand                 // create left edge
391*5aaf853bSArmin Le Grand                 if(bStartRound || bStartSquare)
392*5aaf853bSArmin Le Grand                 {
393*5aaf853bSArmin Le Grand                     basegfx::B2DPolygon aStartPolygon;
394*5aaf853bSArmin Le Grand 
395*5aaf853bSArmin Le Grand                     if(bStartRound)
396*5aaf853bSArmin Le Grand                     {
397*5aaf853bSArmin Le Grand                         aStartPolygon = tools::createHalfUnitCircle();
398*5aaf853bSArmin Le Grand                         aStartPolygon.transform(
399*5aaf853bSArmin Le Grand                             tools::createScaleShearXRotateTranslateB2DHomMatrix(
400*5aaf853bSArmin Le Grand                                 fHalfLineWidth, fHalfLineWidth,
401*5aaf853bSArmin Le Grand                                 0.0,
402*5aaf853bSArmin Le Grand                                 atan2(aTangentA.getY(), aTangentA.getX()) + F_PI2,
403*5aaf853bSArmin Le Grand                                 rEdge.getStartPoint().getX(), rEdge.getStartPoint().getY()));
404*5aaf853bSArmin Le Grand                     }
405*5aaf853bSArmin Le Grand                     else // bStartSquare
406*5aaf853bSArmin Le Grand                     {
407*5aaf853bSArmin Le Grand                         const basegfx::B2DPoint aStart(rEdge.getStartPoint() - (aTangentA * fHalfLineWidth));
408*5aaf853bSArmin Le Grand 
409*5aaf853bSArmin Le Grand                         if(bCut)
410*5aaf853bSArmin Le Grand                         {
411*5aaf853bSArmin Le Grand                             aStartPolygon.append(rEdge.getStartPoint() + aPerpendStartB);
412*5aaf853bSArmin Le Grand                         }
413*5aaf853bSArmin Le Grand 
414*5aaf853bSArmin Le Grand                         aStartPolygon.append(aStart + aPerpendStartB);
415*5aaf853bSArmin Le Grand                         aStartPolygon.append(aStart + aPerpendStartA);
416*5aaf853bSArmin Le Grand 
417*5aaf853bSArmin Le Grand                         if(bCut)
418*5aaf853bSArmin Le Grand                         {
419*5aaf853bSArmin Le Grand                             aStartPolygon.append(rEdge.getStartPoint() + aPerpendStartA);
420*5aaf853bSArmin Le Grand                         }
421*5aaf853bSArmin Le Grand                     }
422*5aaf853bSArmin Le Grand 
423*5aaf853bSArmin Le Grand                     if(bCut)
424*5aaf853bSArmin Le Grand                     {
425*5aaf853bSArmin Le Grand                         aStartPolygon.append(rEdge.getStartPoint());
426*5aaf853bSArmin Le Grand                         aStartPolygon.setClosed(true);
427*5aaf853bSArmin Le Grand                         aRetval.append(aStartPolygon);
428*5aaf853bSArmin Le Grand                     }
429*5aaf853bSArmin Le Grand                     else
430*5aaf853bSArmin Le Grand                     {
431*5aaf853bSArmin Le Grand                         aBezierPolygon.append(aStartPolygon);
432*5aaf853bSArmin Le Grand                     }
433*5aaf853bSArmin Le Grand                 }
434*5aaf853bSArmin Le Grand                 else
435*5aaf853bSArmin Le Grand                 {
436*5aaf853bSArmin Le Grand                     // append original in-between point
437*5aaf853bSArmin Le Grand                     aBezierPolygon.append(rEdge.getStartPoint());
438*5aaf853bSArmin Le Grand                 }
439cdf0e10cSrcweir 
440cdf0e10cSrcweir                 // create upper edge.
441cdf0e10cSrcweir                 {
442*5aaf853bSArmin Le Grand                     if(bCutA)
443cdf0e10cSrcweir                     {
444cdf0e10cSrcweir                         // calculate cut point and add
445*5aaf853bSArmin Le Grand                         const B2DPoint aCutPoint(rEdge.getStartPoint() + (aPerpendStartA * fCutA));
446cdf0e10cSrcweir                         aBezierPolygon.append(aCutPoint);
447cdf0e10cSrcweir                     }
448cdf0e10cSrcweir                     else
449cdf0e10cSrcweir                     {
450cdf0e10cSrcweir                         // create scaled bezier segment
451*5aaf853bSArmin Le Grand                         const B2DPoint aStart(rEdge.getStartPoint() + aPerpendStartA);
452*5aaf853bSArmin Le Grand                         const B2DPoint aEnd(rEdge.getEndPoint() + aPerpendEndA);
453cdf0e10cSrcweir                         const B2DVector aEdge(aEnd - aStart);
454cdf0e10cSrcweir                         const double fLength(aEdge.getLength());
455cdf0e10cSrcweir                         const double fScale(bIsEdgeLengthZero ? 1.0 : fLength / fEdgeLength);
456cdf0e10cSrcweir                         const B2DVector fRelNext(rEdge.getControlPointA() - rEdge.getStartPoint());
457cdf0e10cSrcweir                         const B2DVector fRelPrev(rEdge.getControlPointB() - rEdge.getEndPoint());
458cdf0e10cSrcweir 
459cdf0e10cSrcweir                         aBezierPolygon.append(aStart);
460cdf0e10cSrcweir                         aBezierPolygon.appendBezierSegment(aStart + (fRelNext * fScale), aEnd + (fRelPrev * fScale), aEnd);
461cdf0e10cSrcweir                     }
462cdf0e10cSrcweir                 }
463cdf0e10cSrcweir 
464*5aaf853bSArmin Le Grand                 // create right edge
465*5aaf853bSArmin Le Grand                 if(bEndRound || bEndSquare)
466*5aaf853bSArmin Le Grand                 {
467*5aaf853bSArmin Le Grand                     basegfx::B2DPolygon aEndPolygon;
468*5aaf853bSArmin Le Grand 
469*5aaf853bSArmin Le Grand                     if(bEndRound)
470*5aaf853bSArmin Le Grand                     {
471*5aaf853bSArmin Le Grand                         aEndPolygon = tools::createHalfUnitCircle();
472*5aaf853bSArmin Le Grand                         aEndPolygon.transform(
473*5aaf853bSArmin Le Grand                             tools::createScaleShearXRotateTranslateB2DHomMatrix(
474*5aaf853bSArmin Le Grand                                 fHalfLineWidth, fHalfLineWidth,
475*5aaf853bSArmin Le Grand                                 0.0,
476*5aaf853bSArmin Le Grand                                 atan2(aTangentB.getY(), aTangentB.getX()) - F_PI2,
477*5aaf853bSArmin Le Grand                                 rEdge.getEndPoint().getX(), rEdge.getEndPoint().getY()));
478*5aaf853bSArmin Le Grand                     }
479*5aaf853bSArmin Le Grand                     else // bEndSquare
480*5aaf853bSArmin Le Grand                     {
481*5aaf853bSArmin Le Grand                         const basegfx::B2DPoint aEnd(rEdge.getEndPoint() + (aTangentB * fHalfLineWidth));
482*5aaf853bSArmin Le Grand 
483*5aaf853bSArmin Le Grand                         if(bCut)
484*5aaf853bSArmin Le Grand                         {
485*5aaf853bSArmin Le Grand                             aEndPolygon.append(rEdge.getEndPoint() + aPerpendEndA);
486*5aaf853bSArmin Le Grand                         }
487*5aaf853bSArmin Le Grand 
488*5aaf853bSArmin Le Grand                         aEndPolygon.append(aEnd + aPerpendEndA);
489*5aaf853bSArmin Le Grand                         aEndPolygon.append(aEnd + aPerpendEndB);
490*5aaf853bSArmin Le Grand 
491*5aaf853bSArmin Le Grand                         if(bCut)
492*5aaf853bSArmin Le Grand                         {
493*5aaf853bSArmin Le Grand                             aEndPolygon.append(rEdge.getEndPoint() + aPerpendEndB);
494*5aaf853bSArmin Le Grand                         }
495*5aaf853bSArmin Le Grand                     }
496*5aaf853bSArmin Le Grand 
497*5aaf853bSArmin Le Grand                     if(bCut)
498*5aaf853bSArmin Le Grand                     {
499*5aaf853bSArmin Le Grand                         aEndPolygon.append(rEdge.getEndPoint());
500*5aaf853bSArmin Le Grand                         aEndPolygon.setClosed(true);
501*5aaf853bSArmin Le Grand                         aRetval.append(aEndPolygon);
502*5aaf853bSArmin Le Grand                     }
503*5aaf853bSArmin Le Grand                     else
504*5aaf853bSArmin Le Grand                     {
505*5aaf853bSArmin Le Grand                         aBezierPolygon.append(aEndPolygon);
506*5aaf853bSArmin Le Grand                     }
507*5aaf853bSArmin Le Grand                 }
508*5aaf853bSArmin Le Grand                 else
509*5aaf853bSArmin Le Grand                 {
510cdf0e10cSrcweir                     // append original in-between point
511cdf0e10cSrcweir                     aBezierPolygon.append(rEdge.getEndPoint());
512*5aaf853bSArmin Le Grand                 }
513cdf0e10cSrcweir 
514cdf0e10cSrcweir                 // create lower edge.
515cdf0e10cSrcweir                 {
516*5aaf853bSArmin Le Grand                     if(bCutB)
517cdf0e10cSrcweir                     {
518cdf0e10cSrcweir                         // calculate cut point and add
519*5aaf853bSArmin Le Grand                         const B2DPoint aCutPoint(rEdge.getEndPoint() + (aPerpendEndB * fCutB));
520cdf0e10cSrcweir                         aBezierPolygon.append(aCutPoint);
521cdf0e10cSrcweir                     }
522cdf0e10cSrcweir                     else
523cdf0e10cSrcweir                     {
524cdf0e10cSrcweir                         // create scaled bezier segment
525*5aaf853bSArmin Le Grand                         const B2DPoint aStart(rEdge.getEndPoint() + aPerpendEndB);
526*5aaf853bSArmin Le Grand                         const B2DPoint aEnd(rEdge.getStartPoint() + aPerpendStartB);
527cdf0e10cSrcweir                         const B2DVector aEdge(aEnd - aStart);
528cdf0e10cSrcweir                         const double fLength(aEdge.getLength());
529cdf0e10cSrcweir                         const double fScale(bIsEdgeLengthZero ? 1.0 : fLength / fEdgeLength);
530cdf0e10cSrcweir                         const B2DVector fRelNext(rEdge.getControlPointB() - rEdge.getEndPoint());
531cdf0e10cSrcweir                         const B2DVector fRelPrev(rEdge.getControlPointA() - rEdge.getStartPoint());
532cdf0e10cSrcweir 
533cdf0e10cSrcweir                         aBezierPolygon.append(aStart);
534cdf0e10cSrcweir                         aBezierPolygon.appendBezierSegment(aStart + (fRelNext * fScale), aEnd + (fRelPrev * fScale), aEnd);
535cdf0e10cSrcweir                     }
536cdf0e10cSrcweir                 }
537cdf0e10cSrcweir 
538cdf0e10cSrcweir                 // close and return
539cdf0e10cSrcweir                 aBezierPolygon.setClosed(true);
540*5aaf853bSArmin Le Grand                 aRetval.append(aBezierPolygon);
541*5aaf853bSArmin Le Grand 
542*5aaf853bSArmin Le Grand                 return aRetval;
543cdf0e10cSrcweir             }
544cdf0e10cSrcweir             else
545cdf0e10cSrcweir             {
546*5aaf853bSArmin Le Grand                 // Get start and  end point, create tangent and set to needed length
547*5aaf853bSArmin Le Grand                 B2DVector aTangent(rEdge.getEndPoint() - rEdge.getStartPoint());
548*5aaf853bSArmin Le Grand                 aTangent.setLength(fHalfLineWidth);
549*5aaf853bSArmin Le Grand 
550*5aaf853bSArmin Le Grand                 // prepare return value
551cdf0e10cSrcweir                 B2DPolygon aEdgePolygon;
552cdf0e10cSrcweir 
553*5aaf853bSArmin Le Grand                 // buffered angle
554*5aaf853bSArmin Le Grand                 double fAngle(0.0);
555*5aaf853bSArmin Le Grand                 bool bAngle(false);
556cdf0e10cSrcweir 
557*5aaf853bSArmin Le Grand                 // buffered perpendicular
558*5aaf853bSArmin Le Grand                 B2DVector aPerpend;
559*5aaf853bSArmin Le Grand                 bool bPerpend(false);
560cdf0e10cSrcweir 
561*5aaf853bSArmin Le Grand                 // create left vertical
562*5aaf853bSArmin Le Grand                 if(bStartRound)
563*5aaf853bSArmin Le Grand                 {
564*5aaf853bSArmin Le Grand                     aEdgePolygon = tools::createHalfUnitCircle();
565*5aaf853bSArmin Le Grand                     fAngle = atan2(aTangent.getY(), aTangent.getX());
566*5aaf853bSArmin Le Grand                     bAngle = true;
567*5aaf853bSArmin Le Grand                     aEdgePolygon.transform(
568*5aaf853bSArmin Le Grand                         tools::createScaleShearXRotateTranslateB2DHomMatrix(
569*5aaf853bSArmin Le Grand                             fHalfLineWidth, fHalfLineWidth,
570*5aaf853bSArmin Le Grand                             0.0,
571*5aaf853bSArmin Le Grand                             fAngle + F_PI2,
572*5aaf853bSArmin Le Grand                             rEdge.getStartPoint().getX(), rEdge.getStartPoint().getY()));
573*5aaf853bSArmin Le Grand                 }
574*5aaf853bSArmin Le Grand                 else
575*5aaf853bSArmin Le Grand                 {
576*5aaf853bSArmin Le Grand                     aPerpend.setX(-aTangent.getY());
577*5aaf853bSArmin Le Grand                     aPerpend.setY(aTangent.getX());
578*5aaf853bSArmin Le Grand                     bPerpend = true;
579cdf0e10cSrcweir 
580*5aaf853bSArmin Le Grand                     if(bStartSquare)
581*5aaf853bSArmin Le Grand                     {
582*5aaf853bSArmin Le Grand                         const basegfx::B2DPoint aStart(rEdge.getStartPoint() - aTangent);
583*5aaf853bSArmin Le Grand 
584*5aaf853bSArmin Le Grand                         aEdgePolygon.append(aStart + aPerpend);
585*5aaf853bSArmin Le Grand                         aEdgePolygon.append(aStart - aPerpend);
586*5aaf853bSArmin Le Grand                     }
587*5aaf853bSArmin Le Grand                     else
588*5aaf853bSArmin Le Grand                     {
589*5aaf853bSArmin Le Grand                         aEdgePolygon.append(rEdge.getStartPoint() + aPerpend);
590*5aaf853bSArmin Le Grand                         aEdgePolygon.append(rEdge.getStartPoint()); // keep the in-between point for numerical reasons
591*5aaf853bSArmin Le Grand                         aEdgePolygon.append(rEdge.getStartPoint() - aPerpend);
592*5aaf853bSArmin Le Grand                     }
593*5aaf853bSArmin Le Grand                 }
594*5aaf853bSArmin Le Grand 
595*5aaf853bSArmin Le Grand                 // create right vertical
596*5aaf853bSArmin Le Grand                 if(bEndRound)
597*5aaf853bSArmin Le Grand                 {
598*5aaf853bSArmin Le Grand                     basegfx::B2DPolygon aEndPolygon(tools::createHalfUnitCircle());
599*5aaf853bSArmin Le Grand 
600*5aaf853bSArmin Le Grand                     if(!bAngle)
601*5aaf853bSArmin Le Grand                     {
602*5aaf853bSArmin Le Grand                         fAngle = atan2(aTangent.getY(), aTangent.getX());
603*5aaf853bSArmin Le Grand                     }
604*5aaf853bSArmin Le Grand 
605*5aaf853bSArmin Le Grand                     aEndPolygon.transform(
606*5aaf853bSArmin Le Grand                         tools::createScaleShearXRotateTranslateB2DHomMatrix(
607*5aaf853bSArmin Le Grand                             fHalfLineWidth, fHalfLineWidth,
608*5aaf853bSArmin Le Grand                             0.0,
609*5aaf853bSArmin Le Grand                             fAngle - F_PI2,
610*5aaf853bSArmin Le Grand                             rEdge.getEndPoint().getX(), rEdge.getEndPoint().getY()));
611*5aaf853bSArmin Le Grand                     aEdgePolygon.append(aEndPolygon);
612*5aaf853bSArmin Le Grand                 }
613*5aaf853bSArmin Le Grand                 else
614*5aaf853bSArmin Le Grand                 {
615*5aaf853bSArmin Le Grand                     if(!bPerpend)
616*5aaf853bSArmin Le Grand                     {
617*5aaf853bSArmin Le Grand                         aPerpend.setX(-aTangent.getY());
618*5aaf853bSArmin Le Grand                         aPerpend.setY(aTangent.getX());
619*5aaf853bSArmin Le Grand                     }
620*5aaf853bSArmin Le Grand 
621*5aaf853bSArmin Le Grand                     if(bEndSquare)
622*5aaf853bSArmin Le Grand                     {
623*5aaf853bSArmin Le Grand                         const basegfx::B2DPoint aEnd(rEdge.getEndPoint() + aTangent);
624*5aaf853bSArmin Le Grand 
625*5aaf853bSArmin Le Grand                         aEdgePolygon.append(aEnd - aPerpend);
626*5aaf853bSArmin Le Grand                         aEdgePolygon.append(aEnd + aPerpend);
627*5aaf853bSArmin Le Grand                     }
628*5aaf853bSArmin Le Grand                     else
629*5aaf853bSArmin Le Grand                     {
630*5aaf853bSArmin Le Grand                         aEdgePolygon.append(rEdge.getEndPoint() - aPerpend);
631*5aaf853bSArmin Le Grand                         aEdgePolygon.append(rEdge.getEndPoint()); // keep the in-between point for numerical reasons
632*5aaf853bSArmin Le Grand                         aEdgePolygon.append(rEdge.getEndPoint() + aPerpend);
633*5aaf853bSArmin Le Grand                     }
634*5aaf853bSArmin Le Grand                 }
635cdf0e10cSrcweir 
636cdf0e10cSrcweir                 // close and return
637cdf0e10cSrcweir                 aEdgePolygon.setClosed(true);
638*5aaf853bSArmin Le Grand 
639*5aaf853bSArmin Le Grand                 return B2DPolyPolygon(aEdgePolygon);
640cdf0e10cSrcweir             }
641cdf0e10cSrcweir         }
642cdf0e10cSrcweir 
643cdf0e10cSrcweir         B2DPolygon createAreaGeometryForJoin(
644cdf0e10cSrcweir             const B2DVector& rTangentPrev,
645cdf0e10cSrcweir             const B2DVector& rTangentEdge,
646cdf0e10cSrcweir             const B2DVector& rPerpendPrev,
647cdf0e10cSrcweir             const B2DVector& rPerpendEdge,
648cdf0e10cSrcweir             const B2DPoint& rPoint,
649cdf0e10cSrcweir             double fHalfLineWidth,
650cdf0e10cSrcweir             B2DLineJoin eJoin,
651cdf0e10cSrcweir             double fMiterMinimumAngle)
652cdf0e10cSrcweir 		{
653cdf0e10cSrcweir 			OSL_ENSURE(fHalfLineWidth > 0.0, "createAreaGeometryForJoin: LineWidth too small (!)");
654cdf0e10cSrcweir 			OSL_ENSURE(B2DLINEJOIN_NONE != eJoin, "createAreaGeometryForJoin: B2DLINEJOIN_NONE not allowed (!)");
655cdf0e10cSrcweir 
656cdf0e10cSrcweir             // LineJoin from tangent rPerpendPrev to tangent rPerpendEdge in rPoint
657cdf0e10cSrcweir             B2DPolygon aEdgePolygon;
658cdf0e10cSrcweir 			const B2DPoint aStartPoint(rPoint + rPerpendPrev);
659cdf0e10cSrcweir 			const B2DPoint aEndPoint(rPoint + rPerpendEdge);
660cdf0e10cSrcweir 
661cdf0e10cSrcweir 			// test if for Miter, the angle is too small and the fallback
662cdf0e10cSrcweir 			// to bevel needs to be used
663cdf0e10cSrcweir 			if(B2DLINEJOIN_MITER == eJoin)
664cdf0e10cSrcweir 			{
665cdf0e10cSrcweir 				const double fAngle(fabs(rPerpendPrev.angle(rPerpendEdge)));
666cdf0e10cSrcweir 
667cdf0e10cSrcweir 				if((F_PI - fAngle) < fMiterMinimumAngle)
668cdf0e10cSrcweir 				{
669cdf0e10cSrcweir 					// fallback to bevel
670cdf0e10cSrcweir 					eJoin = B2DLINEJOIN_BEVEL;
671cdf0e10cSrcweir 				}
672cdf0e10cSrcweir 			}
673cdf0e10cSrcweir 
674cdf0e10cSrcweir 			switch(eJoin)
675cdf0e10cSrcweir 			{
676cdf0e10cSrcweir 				case B2DLINEJOIN_MITER :
677cdf0e10cSrcweir 				{
678cdf0e10cSrcweir 					aEdgePolygon.append(aEndPoint);
679cdf0e10cSrcweir 					aEdgePolygon.append(rPoint);
680cdf0e10cSrcweir 					aEdgePolygon.append(aStartPoint);
681cdf0e10cSrcweir 
682cdf0e10cSrcweir 					// Look for the cut point between start point along rTangentPrev and
683cdf0e10cSrcweir 					// end point along rTangentEdge. -rTangentEdge should be used, but since
684cdf0e10cSrcweir 					// the cut value is used for interpolating along the first edge, the negation
685cdf0e10cSrcweir 					// is not needed since the same fCut will be found on the first edge.
686cdf0e10cSrcweir 					// If it exists, insert it to complete the mitered fill polygon.
687cdf0e10cSrcweir     				double fCutPos(0.0);
688cdf0e10cSrcweir 					tools::findCut(aStartPoint, rTangentPrev, aEndPoint, rTangentEdge, CUTFLAG_ALL, &fCutPos);
689cdf0e10cSrcweir 
690cdf0e10cSrcweir 					if(0.0 != fCutPos)
691cdf0e10cSrcweir 					{
692cdf0e10cSrcweir 						const B2DPoint aCutPoint(interpolate(aStartPoint, aStartPoint + rTangentPrev, fCutPos));
693cdf0e10cSrcweir 						aEdgePolygon.append(aCutPoint);
694cdf0e10cSrcweir 					}
695cdf0e10cSrcweir 
696cdf0e10cSrcweir 					break;
697cdf0e10cSrcweir 				}
698cdf0e10cSrcweir 				case B2DLINEJOIN_ROUND :
699cdf0e10cSrcweir 				{
700cdf0e10cSrcweir 					// use tooling to add needed EllipseSegment
701cdf0e10cSrcweir 					double fAngleStart(atan2(rPerpendPrev.getY(), rPerpendPrev.getX()));
702cdf0e10cSrcweir 					double fAngleEnd(atan2(rPerpendEdge.getY(), rPerpendEdge.getX()));
703cdf0e10cSrcweir 
704cdf0e10cSrcweir 					// atan2 results are [-PI .. PI], consolidate to [0.0 .. 2PI]
705cdf0e10cSrcweir 					if(fAngleStart < 0.0)
706cdf0e10cSrcweir 					{
707cdf0e10cSrcweir 						fAngleStart += F_2PI;
708cdf0e10cSrcweir 					}
709cdf0e10cSrcweir 
710cdf0e10cSrcweir 					if(fAngleEnd < 0.0)
711cdf0e10cSrcweir 					{
712cdf0e10cSrcweir 						fAngleEnd += F_2PI;
713cdf0e10cSrcweir 					}
714cdf0e10cSrcweir 
715cdf0e10cSrcweir 					const B2DPolygon aBow(tools::createPolygonFromEllipseSegment(rPoint, fHalfLineWidth, fHalfLineWidth, fAngleStart, fAngleEnd));
716cdf0e10cSrcweir 
717cdf0e10cSrcweir 					if(aBow.count() > 1)
718cdf0e10cSrcweir 					{
719cdf0e10cSrcweir 						// #i101491#
720cdf0e10cSrcweir 						// use the original start/end positions; the ones from bow creation may be numerically
721cdf0e10cSrcweir 						// different due to their different creation. To guarantee good merging quality with edges
722cdf0e10cSrcweir 						// and edge roundings (and to reduce point count)
723cdf0e10cSrcweir 						aEdgePolygon = aBow;
724cdf0e10cSrcweir 						aEdgePolygon.setB2DPoint(0, aStartPoint);
725cdf0e10cSrcweir 						aEdgePolygon.setB2DPoint(aEdgePolygon.count() - 1, aEndPoint);
726cdf0e10cSrcweir 						aEdgePolygon.append(rPoint);
727cdf0e10cSrcweir 
728cdf0e10cSrcweir 						break;
729cdf0e10cSrcweir 					}
730cdf0e10cSrcweir 					else
731cdf0e10cSrcweir 					{
732cdf0e10cSrcweir 						// wanted fall-through to default
733cdf0e10cSrcweir 					}
734cdf0e10cSrcweir 				}
735cdf0e10cSrcweir 				default: // B2DLINEJOIN_BEVEL
736cdf0e10cSrcweir 				{
737cdf0e10cSrcweir 					aEdgePolygon.append(aEndPoint);
738cdf0e10cSrcweir 					aEdgePolygon.append(rPoint);
739cdf0e10cSrcweir 					aEdgePolygon.append(aStartPoint);
740cdf0e10cSrcweir 
741cdf0e10cSrcweir 					break;
742cdf0e10cSrcweir 				}
743cdf0e10cSrcweir 			}
744cdf0e10cSrcweir 
745cdf0e10cSrcweir             // create last polygon part for edge
746cdf0e10cSrcweir             aEdgePolygon.setClosed(true);
747cdf0e10cSrcweir 
748cdf0e10cSrcweir             return aEdgePolygon;
749cdf0e10cSrcweir         }
750cdf0e10cSrcweir     } // end of anonymus namespace
751cdf0e10cSrcweir 
752cdf0e10cSrcweir 	namespace tools
753cdf0e10cSrcweir 	{
754cdf0e10cSrcweir         B2DPolyPolygon createAreaGeometry(
755cdf0e10cSrcweir             const B2DPolygon& rCandidate,
756cdf0e10cSrcweir             double fHalfLineWidth,
757cdf0e10cSrcweir             B2DLineJoin eJoin,
758*5aaf853bSArmin Le Grand             com::sun::star::drawing::LineCap eCap,
759cdf0e10cSrcweir             double fMaxAllowedAngle,
760cdf0e10cSrcweir 			double fMaxPartOfEdge,
761cdf0e10cSrcweir             double fMiterMinimumAngle)
762cdf0e10cSrcweir 		{
763cdf0e10cSrcweir             if(fMaxAllowedAngle > F_PI2)
764cdf0e10cSrcweir             {
765cdf0e10cSrcweir                 fMaxAllowedAngle = F_PI2;
766cdf0e10cSrcweir             }
767cdf0e10cSrcweir             else if(fMaxAllowedAngle < 0.01 * F_PI2)
768cdf0e10cSrcweir             {
769cdf0e10cSrcweir                 fMaxAllowedAngle = 0.01 * F_PI2;
770cdf0e10cSrcweir             }
771cdf0e10cSrcweir 
772cdf0e10cSrcweir             if(fMaxPartOfEdge > 1.0)
773cdf0e10cSrcweir             {
774cdf0e10cSrcweir                 fMaxPartOfEdge = 1.0;
775cdf0e10cSrcweir             }
776cdf0e10cSrcweir             else if(fMaxPartOfEdge < 0.01)
777cdf0e10cSrcweir             {
778cdf0e10cSrcweir                 fMaxPartOfEdge = 0.01;
779cdf0e10cSrcweir             }
780cdf0e10cSrcweir 
781cdf0e10cSrcweir             if(fMiterMinimumAngle > F_PI)
782cdf0e10cSrcweir             {
783cdf0e10cSrcweir                 fMiterMinimumAngle = F_PI;
784cdf0e10cSrcweir             }
785cdf0e10cSrcweir             else if(fMiterMinimumAngle < 0.01 * F_PI)
786cdf0e10cSrcweir             {
787cdf0e10cSrcweir                 fMiterMinimumAngle = 0.01 * F_PI;
788cdf0e10cSrcweir             }
789cdf0e10cSrcweir 
790cdf0e10cSrcweir             B2DPolygon aCandidate(rCandidate);
791cdf0e10cSrcweir             const double fMaxCos(cos(fMaxAllowedAngle));
792cdf0e10cSrcweir 
793cdf0e10cSrcweir             aCandidate.removeDoublePoints();
794cdf0e10cSrcweir             aCandidate = subdivideToSimple(aCandidate, fMaxCos * fMaxCos, fMaxPartOfEdge * fMaxPartOfEdge);
795cdf0e10cSrcweir 
796cdf0e10cSrcweir             const sal_uInt32 nPointCount(aCandidate.count());
797cdf0e10cSrcweir 
798cdf0e10cSrcweir 			if(nPointCount)
799cdf0e10cSrcweir 			{
800cdf0e10cSrcweir                 B2DPolyPolygon aRetval;
801cdf0e10cSrcweir 				const bool bEventuallyCreateLineJoin(B2DLINEJOIN_NONE != eJoin);
802cdf0e10cSrcweir                 const bool bIsClosed(aCandidate.isClosed());
803cdf0e10cSrcweir                 const sal_uInt32 nEdgeCount(bIsClosed ? nPointCount : nPointCount - 1);
804*5aaf853bSArmin Le Grand                 const bool bLineCap(!bIsClosed && com::sun::star::drawing::LineCap_BUTT != eCap);
805cdf0e10cSrcweir 
806cdf0e10cSrcweir                 if(nEdgeCount)
807cdf0e10cSrcweir                 {
808cdf0e10cSrcweir                     B2DCubicBezier aEdge;
809cdf0e10cSrcweir                     B2DCubicBezier aPrev;
810cdf0e10cSrcweir 
811cdf0e10cSrcweir                     // prepare edge
812cdf0e10cSrcweir                     aEdge.setStartPoint(aCandidate.getB2DPoint(0));
813cdf0e10cSrcweir 
814cdf0e10cSrcweir                     if(bIsClosed && bEventuallyCreateLineJoin)
815cdf0e10cSrcweir                     {
816cdf0e10cSrcweir                         // prepare previous edge
817cdf0e10cSrcweir                         const sal_uInt32 nPrevIndex(nPointCount - 1);
818cdf0e10cSrcweir                         aPrev.setStartPoint(aCandidate.getB2DPoint(nPrevIndex));
819cdf0e10cSrcweir                         aPrev.setControlPointA(aCandidate.getNextControlPoint(nPrevIndex));
820cdf0e10cSrcweir                         aPrev.setControlPointB(aCandidate.getPrevControlPoint(0));
821cdf0e10cSrcweir                         aPrev.setEndPoint(aEdge.getStartPoint());
822cdf0e10cSrcweir                     }
823cdf0e10cSrcweir 
824cdf0e10cSrcweir                     for(sal_uInt32 a(0); a < nEdgeCount; a++)
825cdf0e10cSrcweir                     {
826cdf0e10cSrcweir                         // fill current Edge
827cdf0e10cSrcweir                         const sal_uInt32 nNextIndex((a + 1) % nPointCount);
828cdf0e10cSrcweir                         aEdge.setControlPointA(aCandidate.getNextControlPoint(a));
829cdf0e10cSrcweir                         aEdge.setControlPointB(aCandidate.getPrevControlPoint(nNextIndex));
830cdf0e10cSrcweir                         aEdge.setEndPoint(aCandidate.getB2DPoint(nNextIndex));
831cdf0e10cSrcweir 
832cdf0e10cSrcweir                         // check and create linejoin
833cdf0e10cSrcweir                         if(bEventuallyCreateLineJoin && (bIsClosed || 0 != a))
834cdf0e10cSrcweir                         {
835*5aaf853bSArmin Le Grand                             B2DVector aTangentPrev(aPrev.getTangent(1.0)); aTangentPrev.normalize();
836*5aaf853bSArmin Le Grand                             B2DVector aTangentEdge(aEdge.getTangent(0.0)); aTangentEdge.normalize();
837cdf0e10cSrcweir                             B2VectorOrientation aOrientation(getOrientation(aTangentPrev, aTangentEdge));
838cdf0e10cSrcweir 
839cdf0e10cSrcweir                             if(ORIENTATION_NEUTRAL == aOrientation)
840cdf0e10cSrcweir                             {
841cdf0e10cSrcweir                                    // they are parallell or empty; if they are both not zero and point
842cdf0e10cSrcweir                                    // in opposite direction, a half-circle is needed
843cdf0e10cSrcweir                                    if(!aTangentPrev.equalZero() && !aTangentEdge.equalZero())
844cdf0e10cSrcweir                                    {
845cdf0e10cSrcweir                                     const double fAngle(fabs(aTangentPrev.angle(aTangentEdge)));
846cdf0e10cSrcweir 
847cdf0e10cSrcweir                                     if(fTools::equal(fAngle, F_PI))
848cdf0e10cSrcweir                                     {
849cdf0e10cSrcweir                                         // for half-circle production, fallback to positive
850cdf0e10cSrcweir                                         // orientation
851cdf0e10cSrcweir                                         aOrientation = ORIENTATION_POSITIVE;
852cdf0e10cSrcweir                                     }
853cdf0e10cSrcweir                                 }
854cdf0e10cSrcweir                             }
855cdf0e10cSrcweir 
856cdf0e10cSrcweir                             if(ORIENTATION_POSITIVE == aOrientation)
857cdf0e10cSrcweir                             {
858*5aaf853bSArmin Le Grand                                 const B2DVector aPerpendPrev(getPerpendicular(aTangentPrev) * -fHalfLineWidth);
859*5aaf853bSArmin Le Grand                                 const B2DVector aPerpendEdge(getPerpendicular(aTangentEdge) * -fHalfLineWidth);
860cdf0e10cSrcweir 
861*5aaf853bSArmin Le Grand                                 aRetval.append(
862*5aaf853bSArmin Le Grand                                     createAreaGeometryForJoin(
863*5aaf853bSArmin Le Grand                                         aTangentPrev,
864*5aaf853bSArmin Le Grand                                         aTangentEdge,
865*5aaf853bSArmin Le Grand                                         aPerpendPrev,
866*5aaf853bSArmin Le Grand                                         aPerpendEdge,
867*5aaf853bSArmin Le Grand                                         aEdge.getStartPoint(),
868*5aaf853bSArmin Le Grand                                         fHalfLineWidth,
869*5aaf853bSArmin Le Grand                                         eJoin,
870*5aaf853bSArmin Le Grand                                         fMiterMinimumAngle));
871cdf0e10cSrcweir                             }
872cdf0e10cSrcweir                             else if(ORIENTATION_NEGATIVE == aOrientation)
873cdf0e10cSrcweir                             {
874*5aaf853bSArmin Le Grand                                 const B2DVector aPerpendPrev(getPerpendicular(aTangentPrev) * fHalfLineWidth);
875*5aaf853bSArmin Le Grand                                 const B2DVector aPerpendEdge(getPerpendicular(aTangentEdge) * fHalfLineWidth);
876cdf0e10cSrcweir 
877*5aaf853bSArmin Le Grand                                 aRetval.append(
878*5aaf853bSArmin Le Grand                                     createAreaGeometryForJoin(
879*5aaf853bSArmin Le Grand                                         aTangentEdge,
880*5aaf853bSArmin Le Grand                                         aTangentPrev,
881*5aaf853bSArmin Le Grand                                         aPerpendEdge,
882*5aaf853bSArmin Le Grand                                         aPerpendPrev,
883*5aaf853bSArmin Le Grand                                         aEdge.getStartPoint(),
884*5aaf853bSArmin Le Grand                                         fHalfLineWidth,
885*5aaf853bSArmin Le Grand                                         eJoin,
886*5aaf853bSArmin Le Grand                                         fMiterMinimumAngle));
887cdf0e10cSrcweir                             }
888cdf0e10cSrcweir                         }
889cdf0e10cSrcweir 
890cdf0e10cSrcweir                         // create geometry for edge
891*5aaf853bSArmin Le Grand                         const bool bLast(a + 1 == nEdgeCount);
892*5aaf853bSArmin Le Grand 
893*5aaf853bSArmin Le Grand                         if(bLineCap)
894*5aaf853bSArmin Le Grand                         {
895*5aaf853bSArmin Le Grand                             const bool bFirst(!a);
896*5aaf853bSArmin Le Grand 
897*5aaf853bSArmin Le Grand                             aRetval.append(
898*5aaf853bSArmin Le Grand                                 createAreaGeometryForEdge(
899*5aaf853bSArmin Le Grand                                     aEdge,
900*5aaf853bSArmin Le Grand                                     fHalfLineWidth,
901*5aaf853bSArmin Le Grand                                     bFirst && com::sun::star::drawing::LineCap_ROUND == eCap,
902*5aaf853bSArmin Le Grand                                     bLast && com::sun::star::drawing::LineCap_ROUND == eCap,
903*5aaf853bSArmin Le Grand                                     bFirst && com::sun::star::drawing::LineCap_SQUARE == eCap,
904*5aaf853bSArmin Le Grand                                     bLast && com::sun::star::drawing::LineCap_SQUARE == eCap));
905*5aaf853bSArmin Le Grand                         }
906*5aaf853bSArmin Le Grand                         else
907*5aaf853bSArmin Le Grand                         {
908*5aaf853bSArmin Le Grand                             aRetval.append(
909*5aaf853bSArmin Le Grand                                 createAreaGeometryForEdge(
910*5aaf853bSArmin Le Grand                                     aEdge,
911*5aaf853bSArmin Le Grand                                     fHalfLineWidth,
912*5aaf853bSArmin Le Grand                                     false,
913*5aaf853bSArmin Le Grand                                     false,
914*5aaf853bSArmin Le Grand                                     false,
915*5aaf853bSArmin Le Grand                                     false));
916*5aaf853bSArmin Le Grand                         }
917cdf0e10cSrcweir 
918cdf0e10cSrcweir                         // prepare next step
919*5aaf853bSArmin Le Grand                         if(!bLast)
920*5aaf853bSArmin Le Grand                         {
921cdf0e10cSrcweir                             if(bEventuallyCreateLineJoin)
922cdf0e10cSrcweir                             {
923cdf0e10cSrcweir                                 aPrev = aEdge;
924cdf0e10cSrcweir                             }
925cdf0e10cSrcweir 
926cdf0e10cSrcweir                             aEdge.setStartPoint(aEdge.getEndPoint());
927cdf0e10cSrcweir                         }
928cdf0e10cSrcweir                     }
929*5aaf853bSArmin Le Grand                 }
930cdf0e10cSrcweir 
931cdf0e10cSrcweir                 return aRetval;
932cdf0e10cSrcweir             }
933cdf0e10cSrcweir             else
934cdf0e10cSrcweir             {
935cdf0e10cSrcweir                 return B2DPolyPolygon(rCandidate);
936cdf0e10cSrcweir             }
937cdf0e10cSrcweir         }
938cdf0e10cSrcweir     } // end of namespace tools
939cdf0e10cSrcweir } // end of namespace basegfx
940cdf0e10cSrcweir 
941cdf0e10cSrcweir //////////////////////////////////////////////////////////////////////////////
942cdf0e10cSrcweir // eof
943