109dbbe93SAndrew Rist /**************************************************************
2cdf0e10cSrcweir  *
309dbbe93SAndrew Rist  * Licensed to the Apache Software Foundation (ASF) under one
409dbbe93SAndrew Rist  * or more contributor license agreements.  See the NOTICE file
509dbbe93SAndrew Rist  * distributed with this work for additional information
609dbbe93SAndrew Rist  * regarding copyright ownership.  The ASF licenses this file
709dbbe93SAndrew Rist  * to you under the Apache License, Version 2.0 (the
809dbbe93SAndrew Rist  * "License"); you may not use this file except in compliance
909dbbe93SAndrew Rist  * with the License.  You may obtain a copy of the License at
1009dbbe93SAndrew Rist  *
1109dbbe93SAndrew Rist  *   http://www.apache.org/licenses/LICENSE-2.0
1209dbbe93SAndrew Rist  *
1309dbbe93SAndrew Rist  * Unless required by applicable law or agreed to in writing,
1409dbbe93SAndrew Rist  * software distributed under the License is distributed on an
1509dbbe93SAndrew Rist  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
1609dbbe93SAndrew Rist  * KIND, either express or implied.  See the License for the
1709dbbe93SAndrew Rist  * specific language governing permissions and limitations
1809dbbe93SAndrew Rist  * under the License.
1909dbbe93SAndrew Rist  *
2009dbbe93SAndrew Rist  *************************************************************/
2109dbbe93SAndrew Rist 
2209dbbe93SAndrew Rist 
23cdf0e10cSrcweir 
24cdf0e10cSrcweir // MARKER(update_precomp.py): autogen include statement, do not remove
25cdf0e10cSrcweir #include "precompiled_basegfx.hxx"
26cdf0e10cSrcweir #include <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>
375aaf853bSArmin Le Grand #include <com/sun/star/drawing/LineCap.hpp>
38c514522bSArmin Le Grand #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
39cdf0e10cSrcweir 
40cdf0e10cSrcweir //////////////////////////////////////////////////////////////////////////////
41cdf0e10cSrcweir 
42cdf0e10cSrcweir namespace basegfx
43cdf0e10cSrcweir {
44cdf0e10cSrcweir 	namespace tools
45cdf0e10cSrcweir 	{
createAreaGeometryForLineStartEnd(const B2DPolygon & rCandidate,const B2DPolyPolygon & rArrow,bool bStart,double fWidth,double fCandidateLength,double fDockingPosition,double * pConsumedLength)46cdf0e10cSrcweir 		B2DPolyPolygon createAreaGeometryForLineStartEnd(
47cdf0e10cSrcweir 			const B2DPolygon& rCandidate,
48cdf0e10cSrcweir 			const B2DPolyPolygon& rArrow,
49cdf0e10cSrcweir 			bool bStart,
50cdf0e10cSrcweir 			double fWidth,
51cdf0e10cSrcweir 			double fCandidateLength,
52cdf0e10cSrcweir 			double fDockingPosition, // 0->top, 1->bottom
53cdf0e10cSrcweir 			double* pConsumedLength)
54cdf0e10cSrcweir 		{
55cdf0e10cSrcweir 			B2DPolyPolygon aRetval;
56cdf0e10cSrcweir 			OSL_ENSURE(rCandidate.count() > 1L, "createAreaGeometryForLineStartEnd: Line polygon has too less points (!)");
57cdf0e10cSrcweir 			OSL_ENSURE(rArrow.count() > 0L, "createAreaGeometryForLineStartEnd: Empty arrow PolyPolygon (!)");
58cdf0e10cSrcweir 			OSL_ENSURE(fWidth > 0.0, "createAreaGeometryForLineStartEnd: Width too small (!)");
59cdf0e10cSrcweir 			OSL_ENSURE(fDockingPosition >= 0.0 && fDockingPosition <= 1.0,
60cdf0e10cSrcweir 				"createAreaGeometryForLineStartEnd: fDockingPosition out of range [0.0 .. 1.0] (!)");
61cdf0e10cSrcweir 
62cdf0e10cSrcweir             if(fWidth < 0.0)
63cdf0e10cSrcweir             {
64cdf0e10cSrcweir                 fWidth = -fWidth;
65cdf0e10cSrcweir             }
66cdf0e10cSrcweir 
67cdf0e10cSrcweir             if(rCandidate.count() > 1 && rArrow.count() && !fTools::equalZero(fWidth))
68cdf0e10cSrcweir             {
69cdf0e10cSrcweir                 if(fDockingPosition < 0.0)
70cdf0e10cSrcweir                 {
71cdf0e10cSrcweir                     fDockingPosition = 0.0;
72cdf0e10cSrcweir                 }
73cdf0e10cSrcweir                 else if(fDockingPosition > 1.0)
74cdf0e10cSrcweir                 {
75cdf0e10cSrcweir                     fDockingPosition = 1.0;
76cdf0e10cSrcweir                 }
77cdf0e10cSrcweir 
78cdf0e10cSrcweir 			    // init return value from arrow
79cdf0e10cSrcweir 			    aRetval.append(rArrow);
80cdf0e10cSrcweir 
81cdf0e10cSrcweir 			    // get size of the arrow
82cdf0e10cSrcweir 			    const B2DRange aArrowSize(getRange(rArrow));
83cdf0e10cSrcweir 
84cdf0e10cSrcweir 			    // build ArrowTransform; center in X, align with axis in Y
85cdf0e10cSrcweir                 B2DHomMatrix aArrowTransform(basegfx::tools::createTranslateB2DHomMatrix(
86cdf0e10cSrcweir                     -aArrowSize.getCenter().getX(), -aArrowSize.getMinimum().getY()));
87cdf0e10cSrcweir 
88cdf0e10cSrcweir 			    // scale to target size
89cdf0e10cSrcweir 			    const double fArrowScale(fWidth / (aArrowSize.getRange().getX()));
90cdf0e10cSrcweir 			    aArrowTransform.scale(fArrowScale, fArrowScale);
91cdf0e10cSrcweir 
92cdf0e10cSrcweir 			    // get arrow size in Y
93cdf0e10cSrcweir 			    B2DPoint aUpperCenter(aArrowSize.getCenter().getX(), aArrowSize.getMaximum().getY());
94cdf0e10cSrcweir 			    aUpperCenter *= aArrowTransform;
95cdf0e10cSrcweir 			    const double fArrowYLength(B2DVector(aUpperCenter).getLength());
96cdf0e10cSrcweir 
97cdf0e10cSrcweir 			    // move arrow to have docking position centered
98cdf0e10cSrcweir 			    aArrowTransform.translate(0.0, -fArrowYLength * fDockingPosition);
99cdf0e10cSrcweir 
100cdf0e10cSrcweir 			    // prepare polygon length
101cdf0e10cSrcweir 			    if(fTools::equalZero(fCandidateLength))
102cdf0e10cSrcweir 			    {
103cdf0e10cSrcweir 				    fCandidateLength = getLength(rCandidate);
104cdf0e10cSrcweir 			    }
105cdf0e10cSrcweir 
106cdf0e10cSrcweir 			    // get the polygon vector we want to plant this arrow on
107cdf0e10cSrcweir 			    const double fConsumedLength(fArrowYLength * (1.0 - fDockingPosition));
108cdf0e10cSrcweir 			    const B2DVector aHead(rCandidate.getB2DPoint((bStart) ? 0L : rCandidate.count() - 1L));
109cdf0e10cSrcweir 			    const B2DVector aTail(getPositionAbsolute(rCandidate,
110cdf0e10cSrcweir 				    (bStart) ? fConsumedLength : fCandidateLength - fConsumedLength, fCandidateLength));
111cdf0e10cSrcweir 
112cdf0e10cSrcweir 			    // from that vector, take the needed rotation and add rotate for arrow to transformation
113cdf0e10cSrcweir 			    const B2DVector aTargetDirection(aHead - aTail);
114cdf0e10cSrcweir 			    const double fRotation(atan2(aTargetDirection.getY(), aTargetDirection.getX()) + (90.0 * F_PI180));
115cdf0e10cSrcweir 
116cdf0e10cSrcweir 			    // rotate around docking position
117cdf0e10cSrcweir 			    aArrowTransform.rotate(fRotation);
118cdf0e10cSrcweir 
119cdf0e10cSrcweir 			    // move arrow docking position to polygon head
120cdf0e10cSrcweir 			    aArrowTransform.translate(aHead.getX(), aHead.getY());
121cdf0e10cSrcweir 
122cdf0e10cSrcweir 			    // transform retval and close
123cdf0e10cSrcweir 			    aRetval.transform(aArrowTransform);
124cdf0e10cSrcweir 			    aRetval.setClosed(true);
125cdf0e10cSrcweir 
126cdf0e10cSrcweir 			    // if pConsumedLength is asked for, fill it
127cdf0e10cSrcweir 			    if(pConsumedLength)
128cdf0e10cSrcweir 			    {
129cdf0e10cSrcweir 				    *pConsumedLength = fConsumedLength;
130cdf0e10cSrcweir 			    }
131cdf0e10cSrcweir             }
132cdf0e10cSrcweir 
133cdf0e10cSrcweir 			return aRetval;
134cdf0e10cSrcweir 		}
135cdf0e10cSrcweir 	} // end of namespace tools
136cdf0e10cSrcweir } // end of namespace basegfx
137cdf0e10cSrcweir 
138cdf0e10cSrcweir //////////////////////////////////////////////////////////////////////////////
139cdf0e10cSrcweir 
140cdf0e10cSrcweir namespace basegfx
141cdf0e10cSrcweir {
142cdf0e10cSrcweir 	// anonymus namespace for local helpers
143cdf0e10cSrcweir 	namespace
144cdf0e10cSrcweir 	{
impIsSimpleEdge(const B2DCubicBezier & rCandidate,double fMaxCosQuad,double fMaxPartOfEdgeQuad)145cdf0e10cSrcweir         bool impIsSimpleEdge(const B2DCubicBezier& rCandidate, double fMaxCosQuad, double fMaxPartOfEdgeQuad)
146cdf0e10cSrcweir         {
147cdf0e10cSrcweir             // isBezier() is true, already tested by caller
148cdf0e10cSrcweir             const B2DVector aEdge(rCandidate.getEndPoint() - rCandidate.getStartPoint());
149cdf0e10cSrcweir 
150cdf0e10cSrcweir             if(aEdge.equalZero())
151cdf0e10cSrcweir             {
152cdf0e10cSrcweir                 // start and end point the same, but control vectors used -> baloon curve loop
153cdf0e10cSrcweir                 // is not a simple edge
154cdf0e10cSrcweir                 return false;
155cdf0e10cSrcweir             }
156cdf0e10cSrcweir 
157cdf0e10cSrcweir             // get tangentA and scalar with edge
158cdf0e10cSrcweir             const B2DVector aTangentA(rCandidate.getTangent(0.0));
159cdf0e10cSrcweir     		const double fScalarAE(aEdge.scalar(aTangentA));
160cdf0e10cSrcweir 
161cdf0e10cSrcweir             if(fTools::lessOrEqual(fScalarAE, 0.0))
162cdf0e10cSrcweir             {
163cdf0e10cSrcweir                 // angle between TangentA and Edge is bigger or equal 90 degrees
164cdf0e10cSrcweir                 return false;
165cdf0e10cSrcweir             }
166cdf0e10cSrcweir 
167cdf0e10cSrcweir             // get self-scalars for E and A
168cdf0e10cSrcweir     		const double fScalarE(aEdge.scalar(aEdge));
169cdf0e10cSrcweir     		const double fScalarA(aTangentA.scalar(aTangentA));
170cdf0e10cSrcweir 			const double fLengthCompareE(fScalarE * fMaxPartOfEdgeQuad);
171cdf0e10cSrcweir 
172cdf0e10cSrcweir 			if(fTools::moreOrEqual(fScalarA, fLengthCompareE))
173cdf0e10cSrcweir 			{
174cdf0e10cSrcweir 				// length of TangentA is more than fMaxPartOfEdge of length of edge
175cdf0e10cSrcweir 				return false;
176cdf0e10cSrcweir 			}
177cdf0e10cSrcweir 
178cdf0e10cSrcweir             if(fTools::lessOrEqual(fScalarAE * fScalarAE, fScalarA * fScalarE * fMaxCosQuad))
179cdf0e10cSrcweir             {
180cdf0e10cSrcweir                 // angle between TangentA and Edge is bigger or equal angle defined by fMaxCos
181cdf0e10cSrcweir                 return false;
182cdf0e10cSrcweir             }
183cdf0e10cSrcweir 
184cdf0e10cSrcweir             // get tangentB and scalar with edge
185cdf0e10cSrcweir             const B2DVector aTangentB(rCandidate.getTangent(1.0));
186cdf0e10cSrcweir     		const double fScalarBE(aEdge.scalar(aTangentB));
187cdf0e10cSrcweir 
188cdf0e10cSrcweir             if(fTools::lessOrEqual(fScalarBE, 0.0))
189cdf0e10cSrcweir             {
190cdf0e10cSrcweir                 // angle between TangentB and Edge is bigger or equal 90 degrees
191cdf0e10cSrcweir                 return false;
192cdf0e10cSrcweir             }
193cdf0e10cSrcweir 
194cdf0e10cSrcweir             // get self-scalar for B
195cdf0e10cSrcweir     		const double fScalarB(aTangentB.scalar(aTangentB));
196cdf0e10cSrcweir 
197cdf0e10cSrcweir 			if(fTools::moreOrEqual(fScalarB, fLengthCompareE))
198cdf0e10cSrcweir 			{
199cdf0e10cSrcweir 				// length of TangentB is more than fMaxPartOfEdge of length of edge
200cdf0e10cSrcweir 				return false;
201cdf0e10cSrcweir 			}
202cdf0e10cSrcweir 
203cdf0e10cSrcweir 			if(fTools::lessOrEqual(fScalarBE * fScalarBE, fScalarB * fScalarE * fMaxCosQuad))
204cdf0e10cSrcweir             {
205cdf0e10cSrcweir                 // angle between TangentB and Edge is bigger or equal defined by fMaxCos
206cdf0e10cSrcweir                 return false;
207cdf0e10cSrcweir             }
208cdf0e10cSrcweir 
209cdf0e10cSrcweir             return true;
210cdf0e10cSrcweir         }
211cdf0e10cSrcweir 
impSubdivideToSimple(const B2DCubicBezier & rCandidate,B2DPolygon & rTarget,double fMaxCosQuad,double fMaxPartOfEdgeQuad,sal_uInt32 nMaxRecursionDepth)212cdf0e10cSrcweir         void impSubdivideToSimple(const B2DCubicBezier& rCandidate, B2DPolygon& rTarget, double fMaxCosQuad, double fMaxPartOfEdgeQuad, sal_uInt32 nMaxRecursionDepth)
213cdf0e10cSrcweir         {
214cdf0e10cSrcweir             if(!nMaxRecursionDepth || impIsSimpleEdge(rCandidate, fMaxCosQuad, fMaxPartOfEdgeQuad))
215cdf0e10cSrcweir             {
216cdf0e10cSrcweir                 rTarget.appendBezierSegment(rCandidate.getControlPointA(), rCandidate.getControlPointB(), rCandidate.getEndPoint());
217cdf0e10cSrcweir             }
218cdf0e10cSrcweir             else
219cdf0e10cSrcweir             {
220cdf0e10cSrcweir                 B2DCubicBezier aLeft, aRight;
221cdf0e10cSrcweir                 rCandidate.split(0.5, &aLeft, &aRight);
222cdf0e10cSrcweir 
223cdf0e10cSrcweir                 impSubdivideToSimple(aLeft, rTarget, fMaxCosQuad, fMaxPartOfEdgeQuad, nMaxRecursionDepth - 1);
224cdf0e10cSrcweir                 impSubdivideToSimple(aRight, rTarget, fMaxCosQuad, fMaxPartOfEdgeQuad, nMaxRecursionDepth - 1);
225cdf0e10cSrcweir             }
226cdf0e10cSrcweir         }
227cdf0e10cSrcweir 
subdivideToSimple(const B2DPolygon & rCandidate,double fMaxCosQuad,double fMaxPartOfEdgeQuad)228cdf0e10cSrcweir         B2DPolygon subdivideToSimple(const B2DPolygon& rCandidate, double fMaxCosQuad, double fMaxPartOfEdgeQuad)
229cdf0e10cSrcweir         {
230cdf0e10cSrcweir             const sal_uInt32 nPointCount(rCandidate.count());
231cdf0e10cSrcweir 
232cdf0e10cSrcweir             if(rCandidate.areControlPointsUsed() && nPointCount)
233cdf0e10cSrcweir             {
234cdf0e10cSrcweir                 const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
235cdf0e10cSrcweir                 B2DPolygon aRetval;
236cdf0e10cSrcweir 				B2DCubicBezier aEdge;
237cdf0e10cSrcweir 
238cdf0e10cSrcweir 				// prepare edge for loop
239cdf0e10cSrcweir 				aEdge.setStartPoint(rCandidate.getB2DPoint(0));
240cdf0e10cSrcweir 				aRetval.append(aEdge.getStartPoint());
241cdf0e10cSrcweir 
242cdf0e10cSrcweir                 for(sal_uInt32 a(0); a < nEdgeCount; a++)
243cdf0e10cSrcweir                 {
244cdf0e10cSrcweir                     // fill B2DCubicBezier
245cdf0e10cSrcweir                     const sal_uInt32 nNextIndex((a + 1) % nPointCount);
246cdf0e10cSrcweir                     aEdge.setControlPointA(rCandidate.getNextControlPoint(a));
247cdf0e10cSrcweir                     aEdge.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex));
248cdf0e10cSrcweir                     aEdge.setEndPoint(rCandidate.getB2DPoint(nNextIndex));
249cdf0e10cSrcweir 
250cdf0e10cSrcweir                     // get rid of unnecessary bezier segments
251cdf0e10cSrcweir                     aEdge.testAndSolveTrivialBezier();
252cdf0e10cSrcweir 
253cdf0e10cSrcweir                     if(aEdge.isBezier())
254cdf0e10cSrcweir                     {
255cdf0e10cSrcweir 						// before splitting recursively with internal simple criteria, use
256cdf0e10cSrcweir 						// ExtremumPosFinder to remove those
257cdf0e10cSrcweir                 		::std::vector< double > aExtremumPositions;
258cdf0e10cSrcweir 
259cdf0e10cSrcweir                         aExtremumPositions.reserve(4);
260cdf0e10cSrcweir 		                aEdge.getAllExtremumPositions(aExtremumPositions);
261cdf0e10cSrcweir 
262cdf0e10cSrcweir                         const sal_uInt32 nCount(aExtremumPositions.size());
263cdf0e10cSrcweir 
264cdf0e10cSrcweir                         if(nCount)
265cdf0e10cSrcweir                         {
266cdf0e10cSrcweir                             if(nCount > 1)
267cdf0e10cSrcweir                             {
268cdf0e10cSrcweir                                 // create order from left to right
269cdf0e10cSrcweir                                 ::std::sort(aExtremumPositions.begin(), aExtremumPositions.end());
270cdf0e10cSrcweir                             }
271cdf0e10cSrcweir 
272cdf0e10cSrcweir                             for(sal_uInt32 b(0); b < nCount;)
273cdf0e10cSrcweir                             {
274cdf0e10cSrcweir                                 // split aEdge at next split pos
275cdf0e10cSrcweir                                 B2DCubicBezier aLeft;
276cdf0e10cSrcweir                                 const double fSplitPos(aExtremumPositions[b++]);
277cdf0e10cSrcweir 
278cdf0e10cSrcweir                                 aEdge.split(fSplitPos, &aLeft, &aEdge);
279cdf0e10cSrcweir                                 aLeft.testAndSolveTrivialBezier();
280cdf0e10cSrcweir 
281cdf0e10cSrcweir                                 // consume left part
282cdf0e10cSrcweir                                 if(aLeft.isBezier())
283cdf0e10cSrcweir 						        {
284cdf0e10cSrcweir 	                                impSubdivideToSimple(aLeft, aRetval, fMaxCosQuad, fMaxPartOfEdgeQuad, 6);
285cdf0e10cSrcweir 						        }
286cdf0e10cSrcweir 						        else
287cdf0e10cSrcweir 						        {
288cdf0e10cSrcweir 	                                aRetval.append(aLeft.getEndPoint());
289cdf0e10cSrcweir 						        }
290cdf0e10cSrcweir 
291cdf0e10cSrcweir                                 if(b < nCount)
292cdf0e10cSrcweir                                 {
293cdf0e10cSrcweir                                     // correct the remaining split positions to fit to shortened aEdge
294cdf0e10cSrcweir                                     const double fScaleFactor(1.0 / (1.0 - fSplitPos));
295cdf0e10cSrcweir 
296cdf0e10cSrcweir                                     for(sal_uInt32 c(b); c < nCount; c++)
297cdf0e10cSrcweir                                     {
298cdf0e10cSrcweir                                         aExtremumPositions[c] = (aExtremumPositions[c] - fSplitPos) * fScaleFactor;
299cdf0e10cSrcweir                                     }
300cdf0e10cSrcweir                                 }
301cdf0e10cSrcweir                             }
302cdf0e10cSrcweir 
303cdf0e10cSrcweir                             // test the shortened rest of aEdge
304cdf0e10cSrcweir                             aEdge.testAndSolveTrivialBezier();
305cdf0e10cSrcweir 
306cdf0e10cSrcweir                             // consume right part
307cdf0e10cSrcweir                             if(aEdge.isBezier())
308cdf0e10cSrcweir 					        {
309cdf0e10cSrcweir                                 impSubdivideToSimple(aEdge, aRetval, fMaxCosQuad, fMaxPartOfEdgeQuad, 6);
310cdf0e10cSrcweir 					        }
311cdf0e10cSrcweir 					        else
312cdf0e10cSrcweir 					        {
313cdf0e10cSrcweir                                 aRetval.append(aEdge.getEndPoint());
314cdf0e10cSrcweir 					        }
315cdf0e10cSrcweir                         }
316cdf0e10cSrcweir                         else
317cdf0e10cSrcweir                         {
318cdf0e10cSrcweir 	                        impSubdivideToSimple(aEdge, aRetval, fMaxCosQuad, fMaxPartOfEdgeQuad, 6);
319cdf0e10cSrcweir                         }
320cdf0e10cSrcweir                     }
321cdf0e10cSrcweir                     else
322cdf0e10cSrcweir                     {
323cdf0e10cSrcweir                         // straight edge, add point
324cdf0e10cSrcweir                         aRetval.append(aEdge.getEndPoint());
325cdf0e10cSrcweir                     }
326cdf0e10cSrcweir 
327cdf0e10cSrcweir 					// prepare edge for next step
328cdf0e10cSrcweir 					aEdge.setStartPoint(aEdge.getEndPoint());
329cdf0e10cSrcweir                 }
330cdf0e10cSrcweir 
331cdf0e10cSrcweir 				// copy closed flag and check for double points
332cdf0e10cSrcweir                 aRetval.setClosed(rCandidate.isClosed());
333cdf0e10cSrcweir                 aRetval.removeDoublePoints();
334cdf0e10cSrcweir 
335cdf0e10cSrcweir                 return aRetval;
336cdf0e10cSrcweir             }
337cdf0e10cSrcweir             else
338cdf0e10cSrcweir             {
339cdf0e10cSrcweir                 return rCandidate;
340cdf0e10cSrcweir             }
341cdf0e10cSrcweir         }
342cdf0e10cSrcweir 
createAreaGeometryForEdge(const B2DCubicBezier & rEdge,double fHalfLineWidth,bool bStartRound,bool bEndRound,bool bStartSquare,bool bEndSquare)343c514522bSArmin Le Grand         B2DPolygon createAreaGeometryForEdge(
3445aaf853bSArmin Le Grand             const B2DCubicBezier& rEdge,
3455aaf853bSArmin Le Grand             double fHalfLineWidth,
3465aaf853bSArmin Le Grand             bool bStartRound,
3475aaf853bSArmin Le Grand             bool bEndRound,
3485aaf853bSArmin Le Grand             bool bStartSquare,
3495aaf853bSArmin Le Grand             bool bEndSquare)
350cdf0e10cSrcweir         {
351cdf0e10cSrcweir             // create polygon for edge
352cdf0e10cSrcweir             // Unfortunately, while it would be geometrically correct to not add
353cdf0e10cSrcweir             // the in-between points EdgeEnd and EdgeStart, it leads to rounding
354cdf0e10cSrcweir             // errors when converting to integer polygon coordinates for painting
355cdf0e10cSrcweir             if(rEdge.isBezier())
356cdf0e10cSrcweir             {
357cdf0e10cSrcweir                 // prepare target and data common for upper and lower
358cdf0e10cSrcweir                 B2DPolygon aBezierPolygon;
3595aaf853bSArmin Le Grand                 const B2DVector aPureEdgeVector(rEdge.getEndPoint() - rEdge.getStartPoint());
360cdf0e10cSrcweir                 const double fEdgeLength(aPureEdgeVector.getLength());
361cdf0e10cSrcweir                 const bool bIsEdgeLengthZero(fTools::equalZero(fEdgeLength));
3625aaf853bSArmin Le Grand                 B2DVector aTangentA(rEdge.getTangent(0.0)); aTangentA.normalize();
3635aaf853bSArmin Le Grand                 B2DVector aTangentB(rEdge.getTangent(1.0)); aTangentB.normalize();
3645aaf853bSArmin Le Grand                 const B2DVector aNormalizedPerpendicularA(getPerpendicular(aTangentA));
3655aaf853bSArmin Le Grand                 const B2DVector aNormalizedPerpendicularB(getPerpendicular(aTangentB));
3665aaf853bSArmin Le Grand 
3675aaf853bSArmin Le Grand                 // create upper displacement vectors and check if they cut
3685aaf853bSArmin Le Grand                 const B2DVector aPerpendStartA(aNormalizedPerpendicularA * -fHalfLineWidth);
3695aaf853bSArmin Le Grand                 const B2DVector aPerpendEndA(aNormalizedPerpendicularB * -fHalfLineWidth);
3705aaf853bSArmin Le Grand                 double fCutA(0.0);
3715aaf853bSArmin Le Grand                 const tools::CutFlagValue aCutA(tools::findCut(
3725aaf853bSArmin Le Grand                     rEdge.getStartPoint(), aPerpendStartA,
3735aaf853bSArmin Le Grand                     rEdge.getEndPoint(), aPerpendEndA,
3745aaf853bSArmin Le Grand                     CUTFLAG_ALL, &fCutA));
3755aaf853bSArmin Le Grand                 const bool bCutA(CUTFLAG_NONE != aCutA);
3765aaf853bSArmin Le Grand 
3775aaf853bSArmin Le Grand                 // create lower displacement vectors and check if they cut
3785aaf853bSArmin Le Grand                 const B2DVector aPerpendStartB(aNormalizedPerpendicularA * fHalfLineWidth);
3795aaf853bSArmin Le Grand                 const B2DVector aPerpendEndB(aNormalizedPerpendicularB * fHalfLineWidth);
3805aaf853bSArmin Le Grand                 double fCutB(0.0);
3815aaf853bSArmin Le Grand                 const tools::CutFlagValue aCutB(tools::findCut(
3825aaf853bSArmin Le Grand                     rEdge.getEndPoint(), aPerpendEndB,
3835aaf853bSArmin Le Grand                     rEdge.getStartPoint(), aPerpendStartB,
3845aaf853bSArmin Le Grand                     CUTFLAG_ALL, &fCutB));
3855aaf853bSArmin Le Grand                 const bool bCutB(CUTFLAG_NONE != aCutB);
3865aaf853bSArmin Le Grand 
3875aaf853bSArmin Le Grand                 // check if cut happens
3885aaf853bSArmin Le Grand                 const bool bCut(bCutA || bCutB);
389c514522bSArmin Le Grand                 B2DPoint aCutPoint;
3905aaf853bSArmin Le Grand 
3915aaf853bSArmin Le Grand                 // create left edge
3925aaf853bSArmin Le Grand                 if(bStartRound || bStartSquare)
3935aaf853bSArmin Le Grand                 {
3945aaf853bSArmin Le Grand                     if(bStartRound)
3955aaf853bSArmin Le Grand                     {
396c514522bSArmin Le Grand                         basegfx::B2DPolygon aStartPolygon(tools::createHalfUnitCircle());
397c514522bSArmin Le Grand 
3985aaf853bSArmin Le Grand                         aStartPolygon.transform(
3995aaf853bSArmin Le Grand                             tools::createScaleShearXRotateTranslateB2DHomMatrix(
4005aaf853bSArmin Le Grand                                 fHalfLineWidth, fHalfLineWidth,
4015aaf853bSArmin Le Grand                                 0.0,
4025aaf853bSArmin Le Grand                                 atan2(aTangentA.getY(), aTangentA.getX()) + F_PI2,
4035aaf853bSArmin Le Grand                                 rEdge.getStartPoint().getX(), rEdge.getStartPoint().getY()));
404c514522bSArmin Le Grand                         aBezierPolygon.append(aStartPolygon);
4055aaf853bSArmin Le Grand                     }
4065aaf853bSArmin Le Grand                     else // bStartSquare
4075aaf853bSArmin Le Grand                     {
4085aaf853bSArmin Le Grand                         const basegfx::B2DPoint aStart(rEdge.getStartPoint() - (aTangentA * fHalfLineWidth));
4095aaf853bSArmin Le Grand 
410c514522bSArmin Le Grand                         if(bCutB)
4115aaf853bSArmin Le Grand                         {
412c514522bSArmin Le Grand                             aBezierPolygon.append(rEdge.getStartPoint() + aPerpendStartB);
4135aaf853bSArmin Le Grand                         }
4145aaf853bSArmin Le Grand 
415c514522bSArmin Le Grand                         aBezierPolygon.append(aStart + aPerpendStartB);
416c514522bSArmin Le Grand                         aBezierPolygon.append(aStart + aPerpendStartA);
4175aaf853bSArmin Le Grand 
418c514522bSArmin Le Grand                         if(bCutA)
4195aaf853bSArmin Le Grand                         {
420c514522bSArmin Le Grand                             aBezierPolygon.append(rEdge.getStartPoint() + aPerpendStartA);
4215aaf853bSArmin Le Grand                         }
4225aaf853bSArmin Le Grand                     }
4235aaf853bSArmin Le Grand                 }
4245aaf853bSArmin Le Grand                 else
4255aaf853bSArmin Le Grand                 {
4265aaf853bSArmin Le Grand                     // append original in-between point
4275aaf853bSArmin Le Grand                     aBezierPolygon.append(rEdge.getStartPoint());
4285aaf853bSArmin Le Grand                 }
429cdf0e10cSrcweir 
430cdf0e10cSrcweir                 // create upper edge.
431cdf0e10cSrcweir                 {
4325aaf853bSArmin Le Grand                     if(bCutA)
433cdf0e10cSrcweir                     {
434cdf0e10cSrcweir                         // calculate cut point and add
435c514522bSArmin Le Grand                         aCutPoint = rEdge.getStartPoint() + (aPerpendStartA * fCutA);
436cdf0e10cSrcweir                         aBezierPolygon.append(aCutPoint);
437cdf0e10cSrcweir                     }
438cdf0e10cSrcweir                     else
439cdf0e10cSrcweir                     {
440cdf0e10cSrcweir                         // create scaled bezier segment
4415aaf853bSArmin Le Grand                         const B2DPoint aStart(rEdge.getStartPoint() + aPerpendStartA);
4425aaf853bSArmin Le Grand                         const B2DPoint aEnd(rEdge.getEndPoint() + aPerpendEndA);
443cdf0e10cSrcweir                         const B2DVector aEdge(aEnd - aStart);
444cdf0e10cSrcweir                         const double fLength(aEdge.getLength());
445cdf0e10cSrcweir                         const double fScale(bIsEdgeLengthZero ? 1.0 : fLength / fEdgeLength);
446cdf0e10cSrcweir                         const B2DVector fRelNext(rEdge.getControlPointA() - rEdge.getStartPoint());
447cdf0e10cSrcweir                         const B2DVector fRelPrev(rEdge.getControlPointB() - rEdge.getEndPoint());
448cdf0e10cSrcweir 
449cdf0e10cSrcweir                         aBezierPolygon.append(aStart);
450cdf0e10cSrcweir                         aBezierPolygon.appendBezierSegment(aStart + (fRelNext * fScale), aEnd + (fRelPrev * fScale), aEnd);
451cdf0e10cSrcweir                     }
452cdf0e10cSrcweir                 }
453cdf0e10cSrcweir 
4545aaf853bSArmin Le Grand                 // create right edge
4555aaf853bSArmin Le Grand                 if(bEndRound || bEndSquare)
4565aaf853bSArmin Le Grand                 {
4575aaf853bSArmin Le Grand                     if(bEndRound)
4585aaf853bSArmin Le Grand                     {
459c514522bSArmin Le Grand                         basegfx::B2DPolygon aEndPolygon(tools::createHalfUnitCircle());
460c514522bSArmin Le Grand 
4615aaf853bSArmin Le Grand                         aEndPolygon.transform(
4625aaf853bSArmin Le Grand                             tools::createScaleShearXRotateTranslateB2DHomMatrix(
4635aaf853bSArmin Le Grand                                 fHalfLineWidth, fHalfLineWidth,
4645aaf853bSArmin Le Grand                                 0.0,
4655aaf853bSArmin Le Grand                                 atan2(aTangentB.getY(), aTangentB.getX()) - F_PI2,
4665aaf853bSArmin Le Grand                                 rEdge.getEndPoint().getX(), rEdge.getEndPoint().getY()));
467c514522bSArmin Le Grand                         aBezierPolygon.append(aEndPolygon);
4685aaf853bSArmin Le Grand                     }
4695aaf853bSArmin Le Grand                     else // bEndSquare
4705aaf853bSArmin Le Grand                     {
4715aaf853bSArmin Le Grand                         const basegfx::B2DPoint aEnd(rEdge.getEndPoint() + (aTangentB * fHalfLineWidth));
4725aaf853bSArmin Le Grand 
473c514522bSArmin Le Grand                         if(bCutA)
4745aaf853bSArmin Le Grand                         {
475c514522bSArmin Le Grand                             aBezierPolygon.append(rEdge.getEndPoint() + aPerpendEndA);
4765aaf853bSArmin Le Grand                         }
4775aaf853bSArmin Le Grand 
478c514522bSArmin Le Grand                         aBezierPolygon.append(aEnd + aPerpendEndA);
479c514522bSArmin Le Grand                         aBezierPolygon.append(aEnd + aPerpendEndB);
4805aaf853bSArmin Le Grand 
481c514522bSArmin Le Grand                         if(bCutB)
4825aaf853bSArmin Le Grand                         {
483c514522bSArmin Le Grand                             aBezierPolygon.append(rEdge.getEndPoint() + aPerpendEndB);
4845aaf853bSArmin Le Grand                         }
4855aaf853bSArmin Le Grand                     }
4865aaf853bSArmin Le Grand                 }
4875aaf853bSArmin Le Grand                 else
4885aaf853bSArmin Le Grand                 {
4895aaf853bSArmin Le Grand                     // append original in-between point
4905aaf853bSArmin Le Grand                     aBezierPolygon.append(rEdge.getEndPoint());
4915aaf853bSArmin Le Grand                 }
492cdf0e10cSrcweir 
493cdf0e10cSrcweir                 // create lower edge.
494cdf0e10cSrcweir                 {
4955aaf853bSArmin Le Grand                     if(bCutB)
496cdf0e10cSrcweir                     {
497cdf0e10cSrcweir                         // calculate cut point and add
498c514522bSArmin Le Grand                         aCutPoint = rEdge.getEndPoint() + (aPerpendEndB * fCutB);
499cdf0e10cSrcweir                         aBezierPolygon.append(aCutPoint);
500cdf0e10cSrcweir                     }
501cdf0e10cSrcweir                     else
502cdf0e10cSrcweir                     {
503cdf0e10cSrcweir                         // create scaled bezier segment
5045aaf853bSArmin Le Grand                         const B2DPoint aStart(rEdge.getEndPoint() + aPerpendEndB);
5055aaf853bSArmin Le Grand                         const B2DPoint aEnd(rEdge.getStartPoint() + aPerpendStartB);
506cdf0e10cSrcweir                         const B2DVector aEdge(aEnd - aStart);
507cdf0e10cSrcweir                         const double fLength(aEdge.getLength());
508cdf0e10cSrcweir                         const double fScale(bIsEdgeLengthZero ? 1.0 : fLength / fEdgeLength);
509cdf0e10cSrcweir                         const B2DVector fRelNext(rEdge.getControlPointB() - rEdge.getEndPoint());
510cdf0e10cSrcweir                         const B2DVector fRelPrev(rEdge.getControlPointA() - rEdge.getStartPoint());
511cdf0e10cSrcweir 
512cdf0e10cSrcweir                         aBezierPolygon.append(aStart);
513cdf0e10cSrcweir                         aBezierPolygon.appendBezierSegment(aStart + (fRelNext * fScale), aEnd + (fRelPrev * fScale), aEnd);
514cdf0e10cSrcweir                     }
515cdf0e10cSrcweir                 }
516cdf0e10cSrcweir 
517c514522bSArmin Le Grand                 // close
518cdf0e10cSrcweir                 aBezierPolygon.setClosed(true);
519c514522bSArmin Le Grand 
520c514522bSArmin Le Grand                 if(bStartRound || bEndRound)
521c514522bSArmin Le Grand                 {
522c514522bSArmin Le Grand                     // double points possible when round caps are used at start or end
523c514522bSArmin Le Grand                     aBezierPolygon.removeDoublePoints();
524c514522bSArmin Le Grand                 }
525c514522bSArmin Le Grand 
526c514522bSArmin Le Grand                 if(bCut && ((bStartRound || bStartSquare) && (bEndRound || bEndSquare)))
527c514522bSArmin Le Grand                 {
528c514522bSArmin Le Grand                     // When cut exists and both ends are extended with caps, a self-intersecting polygon
529c514522bSArmin Le Grand                     // is created; one cut point is known, but there is a 2nd one in the caps geometry.
530c514522bSArmin Le Grand                     // Solve by using tooling.
531c514522bSArmin Le Grand                     // Remark: This nearly never happens due to curve preparations to extreme points
532c514522bSArmin Le Grand                     // and maximum angle turning, but I constructed a test case and checkd that it is
533c514522bSArmin Le Grand                     // working propery.
534c514522bSArmin Le Grand                     const B2DPolyPolygon aTemp(tools::solveCrossovers(aBezierPolygon));
535c514522bSArmin Le Grand                     const sal_uInt32 nTempCount(aTemp.count());
536c514522bSArmin Le Grand 
537c514522bSArmin Le Grand                     if(nTempCount)
538c514522bSArmin Le Grand                     {
539c514522bSArmin Le Grand                         if(nTempCount > 1)
540c514522bSArmin Le Grand                         {
541c514522bSArmin Le Grand                             // as expected, multiple polygons (with same orientation). Remove
542c514522bSArmin Le Grand                             // the one which contains aCutPoint, or better take the one without
543c514522bSArmin Le Grand                             for (sal_uInt32 a(0); a < nTempCount; a++)
544c514522bSArmin Le Grand                             {
545c514522bSArmin Le Grand                                 aBezierPolygon = aTemp.getB2DPolygon(a);
546c514522bSArmin Le Grand 
547c514522bSArmin Le Grand                                 const sal_uInt32 nCandCount(aBezierPolygon.count());
548c514522bSArmin Le Grand 
549c514522bSArmin Le Grand                                 for(sal_uInt32 b(0); b < nCandCount; b++)
550c514522bSArmin Le Grand                                 {
551c514522bSArmin Le Grand                                     if(aCutPoint.equal(aBezierPolygon.getB2DPoint(b)))
552c514522bSArmin Le Grand                                     {
553c514522bSArmin Le Grand                                         aBezierPolygon.clear();
554c514522bSArmin Le Grand                                         break;
555c514522bSArmin Le Grand                                     }
556c514522bSArmin Le Grand                                 }
557c514522bSArmin Le Grand 
558c514522bSArmin Le Grand                                 if(aBezierPolygon.count())
559c514522bSArmin Le Grand                                 {
560c514522bSArmin Le Grand                                     break;
561c514522bSArmin Le Grand                                 }
562c514522bSArmin Le Grand                             }
563c514522bSArmin Le Grand 
564c514522bSArmin Le Grand                             OSL_ENSURE(aBezierPolygon.count(), "Error in line geometry creation, could not solve self-intersection (!)");
565c514522bSArmin Le Grand                         }
566c514522bSArmin Le Grand                         else
567c514522bSArmin Le Grand                         {
568c514522bSArmin Le Grand                             // none found, use result
569c514522bSArmin Le Grand                             aBezierPolygon = aTemp.getB2DPolygon(0);
570c514522bSArmin Le Grand                         }
571c514522bSArmin Le Grand                     }
572c514522bSArmin Le Grand                     else
573c514522bSArmin Le Grand                     {
574c514522bSArmin Le Grand                         OSL_ENSURE(false, "Error in line geometry creation, could not solve self-intersection (!)");
575c514522bSArmin Le Grand                     }
576c514522bSArmin Le Grand                 }
577c514522bSArmin Le Grand 
578c514522bSArmin Le Grand                 // return
579c514522bSArmin Le Grand                 return aBezierPolygon;
580cdf0e10cSrcweir             }
581cdf0e10cSrcweir             else
582cdf0e10cSrcweir             {
5835aaf853bSArmin Le Grand                 // Get start and  end point, create tangent and set to needed length
5845aaf853bSArmin Le Grand                 B2DVector aTangent(rEdge.getEndPoint() - rEdge.getStartPoint());
5855aaf853bSArmin Le Grand                 aTangent.setLength(fHalfLineWidth);
586cdf0e10cSrcweir 
5875aaf853bSArmin Le Grand                 // prepare return value
5885aaf853bSArmin Le Grand                 B2DPolygon aEdgePolygon;
5895aaf853bSArmin Le Grand 
5905aaf853bSArmin Le Grand                 // buffered angle
5915aaf853bSArmin Le Grand                 double fAngle(0.0);
5925aaf853bSArmin Le Grand                 bool bAngle(false);
593cdf0e10cSrcweir 
5945aaf853bSArmin Le Grand                 // buffered perpendicular
5955aaf853bSArmin Le Grand                 B2DVector aPerpend;
5965aaf853bSArmin Le Grand                 bool bPerpend(false);
597cdf0e10cSrcweir 
5985aaf853bSArmin Le Grand                 // create left vertical
5995aaf853bSArmin Le Grand                 if(bStartRound)
6005aaf853bSArmin Le Grand                 {
6015aaf853bSArmin Le Grand                     aEdgePolygon = tools::createHalfUnitCircle();
6025aaf853bSArmin Le Grand                     fAngle = atan2(aTangent.getY(), aTangent.getX());
6035aaf853bSArmin Le Grand                     bAngle = true;
6045aaf853bSArmin Le Grand                     aEdgePolygon.transform(
6055aaf853bSArmin Le Grand                         tools::createScaleShearXRotateTranslateB2DHomMatrix(
6065aaf853bSArmin Le Grand                             fHalfLineWidth, fHalfLineWidth,
6075aaf853bSArmin Le Grand                             0.0,
6085aaf853bSArmin Le Grand                             fAngle + F_PI2,
6095aaf853bSArmin Le Grand                             rEdge.getStartPoint().getX(), rEdge.getStartPoint().getY()));
6105aaf853bSArmin Le Grand                 }
6115aaf853bSArmin Le Grand                 else
6125aaf853bSArmin Le Grand                 {
6135aaf853bSArmin Le Grand                     aPerpend.setX(-aTangent.getY());
6145aaf853bSArmin Le Grand                     aPerpend.setY(aTangent.getX());
6155aaf853bSArmin Le Grand                     bPerpend = true;
6165aaf853bSArmin Le Grand 
6175aaf853bSArmin Le Grand                     if(bStartSquare)
6185aaf853bSArmin Le Grand                     {
6195aaf853bSArmin Le Grand                         const basegfx::B2DPoint aStart(rEdge.getStartPoint() - aTangent);
6205aaf853bSArmin Le Grand 
6215aaf853bSArmin Le Grand                         aEdgePolygon.append(aStart + aPerpend);
6225aaf853bSArmin Le Grand                         aEdgePolygon.append(aStart - aPerpend);
6235aaf853bSArmin Le Grand                     }
6245aaf853bSArmin Le Grand                     else
6255aaf853bSArmin Le Grand                     {
6265aaf853bSArmin Le Grand                         aEdgePolygon.append(rEdge.getStartPoint() + aPerpend);
6275aaf853bSArmin Le Grand                         aEdgePolygon.append(rEdge.getStartPoint()); // keep the in-between point for numerical reasons
6285aaf853bSArmin Le Grand                         aEdgePolygon.append(rEdge.getStartPoint() - aPerpend);
6295aaf853bSArmin Le Grand                     }
6305aaf853bSArmin Le Grand                 }
6315aaf853bSArmin Le Grand 
6325aaf853bSArmin Le Grand                 // create right vertical
6335aaf853bSArmin Le Grand                 if(bEndRound)
6345aaf853bSArmin Le Grand                 {
6355aaf853bSArmin Le Grand                     basegfx::B2DPolygon aEndPolygon(tools::createHalfUnitCircle());
6365aaf853bSArmin Le Grand 
6375aaf853bSArmin Le Grand                     if(!bAngle)
6385aaf853bSArmin Le Grand                     {
6395aaf853bSArmin Le Grand                         fAngle = atan2(aTangent.getY(), aTangent.getX());
6405aaf853bSArmin Le Grand                     }
6415aaf853bSArmin Le Grand 
6425aaf853bSArmin Le Grand                     aEndPolygon.transform(
6435aaf853bSArmin Le Grand                         tools::createScaleShearXRotateTranslateB2DHomMatrix(
6445aaf853bSArmin Le Grand                             fHalfLineWidth, fHalfLineWidth,
6455aaf853bSArmin Le Grand                             0.0,
6465aaf853bSArmin Le Grand                             fAngle - F_PI2,
6475aaf853bSArmin Le Grand                             rEdge.getEndPoint().getX(), rEdge.getEndPoint().getY()));
6485aaf853bSArmin Le Grand                     aEdgePolygon.append(aEndPolygon);
6495aaf853bSArmin Le Grand                 }
6505aaf853bSArmin Le Grand                 else
6515aaf853bSArmin Le Grand                 {
6525aaf853bSArmin Le Grand                     if(!bPerpend)
6535aaf853bSArmin Le Grand                     {
6545aaf853bSArmin Le Grand                         aPerpend.setX(-aTangent.getY());
6555aaf853bSArmin Le Grand                         aPerpend.setY(aTangent.getX());
6565aaf853bSArmin Le Grand                     }
6575aaf853bSArmin Le Grand 
6585aaf853bSArmin Le Grand                     if(bEndSquare)
6595aaf853bSArmin Le Grand                     {
6605aaf853bSArmin Le Grand                         const basegfx::B2DPoint aEnd(rEdge.getEndPoint() + aTangent);
6615aaf853bSArmin Le Grand 
6625aaf853bSArmin Le Grand                         aEdgePolygon.append(aEnd - aPerpend);
6635aaf853bSArmin Le Grand                         aEdgePolygon.append(aEnd + aPerpend);
6645aaf853bSArmin Le Grand                     }
6655aaf853bSArmin Le Grand                     else
6665aaf853bSArmin Le Grand                     {
6675aaf853bSArmin Le Grand                         aEdgePolygon.append(rEdge.getEndPoint() - aPerpend);
6685aaf853bSArmin Le Grand                         aEdgePolygon.append(rEdge.getEndPoint()); // keep the in-between point for numerical reasons
6695aaf853bSArmin Le Grand                         aEdgePolygon.append(rEdge.getEndPoint() + aPerpend);
6705aaf853bSArmin Le Grand                     }
6715aaf853bSArmin Le Grand                 }
672cdf0e10cSrcweir 
673cdf0e10cSrcweir                 // close and return
674cdf0e10cSrcweir                 aEdgePolygon.setClosed(true);
6755aaf853bSArmin Le Grand 
676c514522bSArmin Le Grand                 return aEdgePolygon;
677cdf0e10cSrcweir             }
678cdf0e10cSrcweir         }
679cdf0e10cSrcweir 
createAreaGeometryForJoin(const B2DVector & rTangentPrev,const B2DVector & rTangentEdge,const B2DVector & rPerpendPrev,const B2DVector & rPerpendEdge,const B2DPoint & rPoint,double fHalfLineWidth,B2DLineJoin eJoin,double fMiterMinimumAngle)680cdf0e10cSrcweir         B2DPolygon createAreaGeometryForJoin(
681cdf0e10cSrcweir             const B2DVector& rTangentPrev,
682cdf0e10cSrcweir             const B2DVector& rTangentEdge,
683cdf0e10cSrcweir             const B2DVector& rPerpendPrev,
684cdf0e10cSrcweir             const B2DVector& rPerpendEdge,
685cdf0e10cSrcweir             const B2DPoint& rPoint,
686cdf0e10cSrcweir             double fHalfLineWidth,
687cdf0e10cSrcweir             B2DLineJoin eJoin,
688cdf0e10cSrcweir             double fMiterMinimumAngle)
689cdf0e10cSrcweir 		{
690cdf0e10cSrcweir 			OSL_ENSURE(fHalfLineWidth > 0.0, "createAreaGeometryForJoin: LineWidth too small (!)");
691cdf0e10cSrcweir 			OSL_ENSURE(B2DLINEJOIN_NONE != eJoin, "createAreaGeometryForJoin: B2DLINEJOIN_NONE not allowed (!)");
692cdf0e10cSrcweir 
693cdf0e10cSrcweir             // LineJoin from tangent rPerpendPrev to tangent rPerpendEdge in rPoint
694cdf0e10cSrcweir             B2DPolygon aEdgePolygon;
695cdf0e10cSrcweir 			const B2DPoint aStartPoint(rPoint + rPerpendPrev);
696cdf0e10cSrcweir 			const B2DPoint aEndPoint(rPoint + rPerpendEdge);
697cdf0e10cSrcweir 
698cdf0e10cSrcweir 			// test if for Miter, the angle is too small and the fallback
699cdf0e10cSrcweir 			// to bevel needs to be used
700cdf0e10cSrcweir 			if(B2DLINEJOIN_MITER == eJoin)
701cdf0e10cSrcweir 			{
702cdf0e10cSrcweir 				const double fAngle(fabs(rPerpendPrev.angle(rPerpendEdge)));
703cdf0e10cSrcweir 
704cdf0e10cSrcweir 				if((F_PI - fAngle) < fMiterMinimumAngle)
705cdf0e10cSrcweir 				{
706cdf0e10cSrcweir 					// fallback to bevel
707cdf0e10cSrcweir 					eJoin = B2DLINEJOIN_BEVEL;
708cdf0e10cSrcweir 				}
709cdf0e10cSrcweir 			}
710cdf0e10cSrcweir 
711cdf0e10cSrcweir 			switch(eJoin)
712cdf0e10cSrcweir 			{
713cdf0e10cSrcweir 				case B2DLINEJOIN_MITER :
714cdf0e10cSrcweir 				{
715cdf0e10cSrcweir 					aEdgePolygon.append(aEndPoint);
716cdf0e10cSrcweir 					aEdgePolygon.append(rPoint);
717cdf0e10cSrcweir 					aEdgePolygon.append(aStartPoint);
718cdf0e10cSrcweir 
719cdf0e10cSrcweir 					// Look for the cut point between start point along rTangentPrev and
720cdf0e10cSrcweir 					// end point along rTangentEdge. -rTangentEdge should be used, but since
721cdf0e10cSrcweir 					// the cut value is used for interpolating along the first edge, the negation
722cdf0e10cSrcweir 					// is not needed since the same fCut will be found on the first edge.
723cdf0e10cSrcweir 					// If it exists, insert it to complete the mitered fill polygon.
724cdf0e10cSrcweir     				double fCutPos(0.0);
725cdf0e10cSrcweir 					tools::findCut(aStartPoint, rTangentPrev, aEndPoint, rTangentEdge, CUTFLAG_ALL, &fCutPos);
726cdf0e10cSrcweir 
727cdf0e10cSrcweir 					if(0.0 != fCutPos)
728cdf0e10cSrcweir 					{
7291fe6099dSArmin Le Grand 						const B2DPoint aCutPoint(aStartPoint + (rTangentPrev * fCutPos));
730cdf0e10cSrcweir 						aEdgePolygon.append(aCutPoint);
731cdf0e10cSrcweir 					}
732cdf0e10cSrcweir 
733cdf0e10cSrcweir 					break;
734cdf0e10cSrcweir 				}
735cdf0e10cSrcweir 				case B2DLINEJOIN_ROUND :
736cdf0e10cSrcweir 				{
737cdf0e10cSrcweir 					// use tooling to add needed EllipseSegment
738cdf0e10cSrcweir 					double fAngleStart(atan2(rPerpendPrev.getY(), rPerpendPrev.getX()));
739cdf0e10cSrcweir 					double fAngleEnd(atan2(rPerpendEdge.getY(), rPerpendEdge.getX()));
740cdf0e10cSrcweir 
741cdf0e10cSrcweir 					// atan2 results are [-PI .. PI], consolidate to [0.0 .. 2PI]
742cdf0e10cSrcweir 					if(fAngleStart < 0.0)
743cdf0e10cSrcweir 					{
744cdf0e10cSrcweir 						fAngleStart += F_2PI;
745cdf0e10cSrcweir 					}
746cdf0e10cSrcweir 
747cdf0e10cSrcweir 					if(fAngleEnd < 0.0)
748cdf0e10cSrcweir 					{
749cdf0e10cSrcweir 						fAngleEnd += F_2PI;
750cdf0e10cSrcweir 					}
751cdf0e10cSrcweir 
752cdf0e10cSrcweir 					const B2DPolygon aBow(tools::createPolygonFromEllipseSegment(rPoint, fHalfLineWidth, fHalfLineWidth, fAngleStart, fAngleEnd));
753cdf0e10cSrcweir 
754cdf0e10cSrcweir 					if(aBow.count() > 1)
755cdf0e10cSrcweir 					{
756cdf0e10cSrcweir 						// #i101491#
757cdf0e10cSrcweir 						// use the original start/end positions; the ones from bow creation may be numerically
758cdf0e10cSrcweir 						// different due to their different creation. To guarantee good merging quality with edges
759cdf0e10cSrcweir 						// and edge roundings (and to reduce point count)
760cdf0e10cSrcweir 						aEdgePolygon = aBow;
761cdf0e10cSrcweir 						aEdgePolygon.setB2DPoint(0, aStartPoint);
762cdf0e10cSrcweir 						aEdgePolygon.setB2DPoint(aEdgePolygon.count() - 1, aEndPoint);
763cdf0e10cSrcweir 						aEdgePolygon.append(rPoint);
764cdf0e10cSrcweir 
765cdf0e10cSrcweir 						break;
766cdf0e10cSrcweir 					}
767cdf0e10cSrcweir 					else
768cdf0e10cSrcweir 					{
769cdf0e10cSrcweir 						// wanted fall-through to default
770cdf0e10cSrcweir 					}
771cdf0e10cSrcweir 				}
772cdf0e10cSrcweir 				default: // B2DLINEJOIN_BEVEL
773cdf0e10cSrcweir 				{
774cdf0e10cSrcweir 					aEdgePolygon.append(aEndPoint);
775cdf0e10cSrcweir 					aEdgePolygon.append(rPoint);
776cdf0e10cSrcweir 					aEdgePolygon.append(aStartPoint);
777cdf0e10cSrcweir 
778cdf0e10cSrcweir 					break;
779cdf0e10cSrcweir 				}
780cdf0e10cSrcweir 			}
781cdf0e10cSrcweir 
782cdf0e10cSrcweir             // create last polygon part for edge
783cdf0e10cSrcweir             aEdgePolygon.setClosed(true);
784cdf0e10cSrcweir 
785cdf0e10cSrcweir             return aEdgePolygon;
786cdf0e10cSrcweir         }
787cdf0e10cSrcweir     } // end of anonymus namespace
788cdf0e10cSrcweir 
789cdf0e10cSrcweir 	namespace tools
790cdf0e10cSrcweir 	{
createAreaGeometry(const B2DPolygon & rCandidate,double fHalfLineWidth,B2DLineJoin eJoin,com::sun::star::drawing::LineCap eCap,double fMaxAllowedAngle,double fMaxPartOfEdge,double fMiterMinimumAngle)791cdf0e10cSrcweir         B2DPolyPolygon createAreaGeometry(
792cdf0e10cSrcweir             const B2DPolygon& rCandidate,
793cdf0e10cSrcweir             double fHalfLineWidth,
794cdf0e10cSrcweir             B2DLineJoin eJoin,
7955aaf853bSArmin Le Grand             com::sun::star::drawing::LineCap eCap,
796cdf0e10cSrcweir             double fMaxAllowedAngle,
797cdf0e10cSrcweir 			double fMaxPartOfEdge,
798cdf0e10cSrcweir             double fMiterMinimumAngle)
799cdf0e10cSrcweir 		{
800cdf0e10cSrcweir             if(fMaxAllowedAngle > F_PI2)
801cdf0e10cSrcweir             {
802cdf0e10cSrcweir                 fMaxAllowedAngle = F_PI2;
803cdf0e10cSrcweir             }
804cdf0e10cSrcweir             else if(fMaxAllowedAngle < 0.01 * F_PI2)
805cdf0e10cSrcweir             {
806cdf0e10cSrcweir                 fMaxAllowedAngle = 0.01 * F_PI2;
807cdf0e10cSrcweir             }
808cdf0e10cSrcweir 
809cdf0e10cSrcweir             if(fMaxPartOfEdge > 1.0)
810cdf0e10cSrcweir             {
811cdf0e10cSrcweir                 fMaxPartOfEdge = 1.0;
812cdf0e10cSrcweir             }
813cdf0e10cSrcweir             else if(fMaxPartOfEdge < 0.01)
814cdf0e10cSrcweir             {
815cdf0e10cSrcweir                 fMaxPartOfEdge = 0.01;
816cdf0e10cSrcweir             }
817cdf0e10cSrcweir 
818cdf0e10cSrcweir             if(fMiterMinimumAngle > F_PI)
819cdf0e10cSrcweir             {
820cdf0e10cSrcweir                 fMiterMinimumAngle = F_PI;
821cdf0e10cSrcweir             }
822cdf0e10cSrcweir             else if(fMiterMinimumAngle < 0.01 * F_PI)
823cdf0e10cSrcweir             {
824cdf0e10cSrcweir                 fMiterMinimumAngle = 0.01 * F_PI;
825cdf0e10cSrcweir             }
826cdf0e10cSrcweir 
827cdf0e10cSrcweir             B2DPolygon aCandidate(rCandidate);
828cdf0e10cSrcweir             const double fMaxCos(cos(fMaxAllowedAngle));
829cdf0e10cSrcweir 
830cdf0e10cSrcweir             aCandidate.removeDoublePoints();
831cdf0e10cSrcweir             aCandidate = subdivideToSimple(aCandidate, fMaxCos * fMaxCos, fMaxPartOfEdge * fMaxPartOfEdge);
832cdf0e10cSrcweir 
833cdf0e10cSrcweir             const sal_uInt32 nPointCount(aCandidate.count());
834cdf0e10cSrcweir 
835cdf0e10cSrcweir 			if(nPointCount)
836cdf0e10cSrcweir 			{
837cdf0e10cSrcweir                 B2DPolyPolygon aRetval;
838cdf0e10cSrcweir 				const bool bEventuallyCreateLineJoin(B2DLINEJOIN_NONE != eJoin);
839cdf0e10cSrcweir                 const bool bIsClosed(aCandidate.isClosed());
840cdf0e10cSrcweir                 const sal_uInt32 nEdgeCount(bIsClosed ? nPointCount : nPointCount - 1);
8415aaf853bSArmin Le Grand                 const bool bLineCap(!bIsClosed && com::sun::star::drawing::LineCap_BUTT != eCap);
842cdf0e10cSrcweir 
843cdf0e10cSrcweir                 if(nEdgeCount)
844cdf0e10cSrcweir                 {
845cdf0e10cSrcweir                     B2DCubicBezier aEdge;
846cdf0e10cSrcweir                     B2DCubicBezier aPrev;
847cdf0e10cSrcweir 
848cdf0e10cSrcweir                     // prepare edge
849cdf0e10cSrcweir                     aEdge.setStartPoint(aCandidate.getB2DPoint(0));
850cdf0e10cSrcweir 
851cdf0e10cSrcweir                     if(bIsClosed && bEventuallyCreateLineJoin)
852cdf0e10cSrcweir                     {
853cdf0e10cSrcweir                         // prepare previous edge
854cdf0e10cSrcweir                         const sal_uInt32 nPrevIndex(nPointCount - 1);
855cdf0e10cSrcweir                         aPrev.setStartPoint(aCandidate.getB2DPoint(nPrevIndex));
856cdf0e10cSrcweir                         aPrev.setControlPointA(aCandidate.getNextControlPoint(nPrevIndex));
857cdf0e10cSrcweir                         aPrev.setControlPointB(aCandidate.getPrevControlPoint(0));
858cdf0e10cSrcweir                         aPrev.setEndPoint(aEdge.getStartPoint());
859cdf0e10cSrcweir                     }
860cdf0e10cSrcweir 
861cdf0e10cSrcweir                     for(sal_uInt32 a(0); a < nEdgeCount; a++)
862cdf0e10cSrcweir                     {
863cdf0e10cSrcweir                         // fill current Edge
864cdf0e10cSrcweir                         const sal_uInt32 nNextIndex((a + 1) % nPointCount);
865cdf0e10cSrcweir                         aEdge.setControlPointA(aCandidate.getNextControlPoint(a));
866cdf0e10cSrcweir                         aEdge.setControlPointB(aCandidate.getPrevControlPoint(nNextIndex));
867cdf0e10cSrcweir                         aEdge.setEndPoint(aCandidate.getB2DPoint(nNextIndex));
868cdf0e10cSrcweir 
869cdf0e10cSrcweir                         // check and create linejoin
870cdf0e10cSrcweir                         if(bEventuallyCreateLineJoin && (bIsClosed || 0 != a))
871cdf0e10cSrcweir                         {
8725aaf853bSArmin Le Grand                             B2DVector aTangentPrev(aPrev.getTangent(1.0)); aTangentPrev.normalize();
8735aaf853bSArmin Le Grand                             B2DVector aTangentEdge(aEdge.getTangent(0.0)); aTangentEdge.normalize();
874cdf0e10cSrcweir                             B2VectorOrientation aOrientation(getOrientation(aTangentPrev, aTangentEdge));
875cdf0e10cSrcweir 
8765aaf853bSArmin Le Grand                             if(ORIENTATION_NEUTRAL == aOrientation)
8775aaf853bSArmin Le Grand                             {
878*30acf5e8Spfg                                    // they are parallel or empty; if they are both not zero and point
8795aaf853bSArmin Le Grand                                    // in opposite direction, a half-circle is needed
8805aaf853bSArmin Le Grand                                    if(!aTangentPrev.equalZero() && !aTangentEdge.equalZero())
8815aaf853bSArmin Le Grand                                    {
8825aaf853bSArmin Le Grand                                     const double fAngle(fabs(aTangentPrev.angle(aTangentEdge)));
8835aaf853bSArmin Le Grand 
8845aaf853bSArmin Le Grand                                     if(fTools::equal(fAngle, F_PI))
8855aaf853bSArmin Le Grand                                     {
886cdf0e10cSrcweir                                         // for half-circle production, fallback to positive
887cdf0e10cSrcweir                                         // orientation
8885aaf853bSArmin Le Grand                                         aOrientation = ORIENTATION_POSITIVE;
8895aaf853bSArmin Le Grand                                     }
8905aaf853bSArmin Le Grand                                 }
8915aaf853bSArmin Le Grand                             }
892cdf0e10cSrcweir 
893cdf0e10cSrcweir                             if(ORIENTATION_POSITIVE == aOrientation)
894cdf0e10cSrcweir                             {
8955aaf853bSArmin Le Grand                                 const B2DVector aPerpendPrev(getPerpendicular(aTangentPrev) * -fHalfLineWidth);
8965aaf853bSArmin Le Grand                                 const B2DVector aPerpendEdge(getPerpendicular(aTangentEdge) * -fHalfLineWidth);
897cdf0e10cSrcweir 
8985aaf853bSArmin Le Grand                                 aRetval.append(
8995aaf853bSArmin Le Grand                                     createAreaGeometryForJoin(
9005aaf853bSArmin Le Grand                                         aTangentPrev,
9015aaf853bSArmin Le Grand                                         aTangentEdge,
9025aaf853bSArmin Le Grand                                         aPerpendPrev,
9035aaf853bSArmin Le Grand                                         aPerpendEdge,
9045aaf853bSArmin Le Grand                                         aEdge.getStartPoint(),
9055aaf853bSArmin Le Grand                                         fHalfLineWidth,
9065aaf853bSArmin Le Grand                                         eJoin,
9075aaf853bSArmin Le Grand                                         fMiterMinimumAngle));
908cdf0e10cSrcweir                             }
909cdf0e10cSrcweir                             else if(ORIENTATION_NEGATIVE == aOrientation)
910cdf0e10cSrcweir                             {
9115aaf853bSArmin Le Grand                                 const B2DVector aPerpendPrev(getPerpendicular(aTangentPrev) * fHalfLineWidth);
9125aaf853bSArmin Le Grand                                 const B2DVector aPerpendEdge(getPerpendicular(aTangentEdge) * fHalfLineWidth);
9135aaf853bSArmin Le Grand 
9145aaf853bSArmin Le Grand                                 aRetval.append(
9155aaf853bSArmin Le Grand                                     createAreaGeometryForJoin(
9165aaf853bSArmin Le Grand                                         aTangentEdge,
9175aaf853bSArmin Le Grand                                         aTangentPrev,
9185aaf853bSArmin Le Grand                                         aPerpendEdge,
9195aaf853bSArmin Le Grand                                         aPerpendPrev,
9205aaf853bSArmin Le Grand                                         aEdge.getStartPoint(),
9215aaf853bSArmin Le Grand                                         fHalfLineWidth,
9225aaf853bSArmin Le Grand                                         eJoin,
9235aaf853bSArmin Le Grand                                         fMiterMinimumAngle));
924cdf0e10cSrcweir                             }
925cdf0e10cSrcweir                         }
926cdf0e10cSrcweir 
927cdf0e10cSrcweir                         // create geometry for edge
9285aaf853bSArmin Le Grand                         const bool bLast(a + 1 == nEdgeCount);
929cdf0e10cSrcweir 
9305aaf853bSArmin Le Grand                         if(bLineCap)
9315aaf853bSArmin Le Grand                         {
9325aaf853bSArmin Le Grand                             const bool bFirst(!a);
9335aaf853bSArmin Le Grand 
9345aaf853bSArmin Le Grand                             aRetval.append(
9355aaf853bSArmin Le Grand                                 createAreaGeometryForEdge(
9365aaf853bSArmin Le Grand                                     aEdge,
9375aaf853bSArmin Le Grand                                     fHalfLineWidth,
9385aaf853bSArmin Le Grand                                     bFirst && com::sun::star::drawing::LineCap_ROUND == eCap,
9395aaf853bSArmin Le Grand                                     bLast && com::sun::star::drawing::LineCap_ROUND == eCap,
9405aaf853bSArmin Le Grand                                     bFirst && com::sun::star::drawing::LineCap_SQUARE == eCap,
9415aaf853bSArmin Le Grand                                     bLast && com::sun::star::drawing::LineCap_SQUARE == eCap));
9425aaf853bSArmin Le Grand                         }
9435aaf853bSArmin Le Grand                         else
944cdf0e10cSrcweir                         {
9455aaf853bSArmin Le Grand                             aRetval.append(
9465aaf853bSArmin Le Grand                                 createAreaGeometryForEdge(
9475aaf853bSArmin Le Grand                                     aEdge,
9485aaf853bSArmin Le Grand                                     fHalfLineWidth,
9495aaf853bSArmin Le Grand                                     false,
9505aaf853bSArmin Le Grand                                     false,
9515aaf853bSArmin Le Grand                                     false,
9525aaf853bSArmin Le Grand                                     false));
953cdf0e10cSrcweir                         }
954cdf0e10cSrcweir 
9555aaf853bSArmin Le Grand                         // prepare next step
9565aaf853bSArmin Le Grand                         if(!bLast)
9575aaf853bSArmin Le Grand                         {
9585aaf853bSArmin Le Grand                             if(bEventuallyCreateLineJoin)
9595aaf853bSArmin Le Grand                             {
9605aaf853bSArmin Le Grand                                 aPrev = aEdge;
9615aaf853bSArmin Le Grand                             }
9625aaf853bSArmin Le Grand 
9635aaf853bSArmin Le Grand                             aEdge.setStartPoint(aEdge.getEndPoint());
9645aaf853bSArmin Le Grand                         }
965cdf0e10cSrcweir                     }
966cdf0e10cSrcweir                 }
967cdf0e10cSrcweir 
968cdf0e10cSrcweir                 return aRetval;
9695aaf853bSArmin Le Grand             }
970cdf0e10cSrcweir             else
971cdf0e10cSrcweir             {
972cdf0e10cSrcweir                 return B2DPolyPolygon(rCandidate);
973cdf0e10cSrcweir             }
9745aaf853bSArmin Le Grand         }
9755aaf853bSArmin Le Grand     } // end of namespace tools
976cdf0e10cSrcweir } // end of namespace basegfx
977cdf0e10cSrcweir 
978cdf0e10cSrcweir //////////////////////////////////////////////////////////////////////////////
979cdf0e10cSrcweir // eof
980