xref: /trunk/main/basegfx/source/polygon/b2dlinegeometry.cxx (revision d0f02d504f020883217ddafca9ad519609418cfb)
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 
22cdf0e10cSrcweir // MARKER(update_precomp.py): autogen include statement, do not remove
23cdf0e10cSrcweir #include "precompiled_basegfx.hxx"
24cdf0e10cSrcweir #include <cstdio>
25cdf0e10cSrcweir #include <osl/diagnose.h>
26cdf0e10cSrcweir #include <basegfx/polygon/b2dlinegeometry.hxx>
27cdf0e10cSrcweir #include <basegfx/point/b2dpoint.hxx>
28cdf0e10cSrcweir #include <basegfx/vector/b2dvector.hxx>
29cdf0e10cSrcweir #include <basegfx/polygon/b2dpolygontools.hxx>
30cdf0e10cSrcweir #include <basegfx/polygon/b2dpolypolygontools.hxx>
31cdf0e10cSrcweir #include <basegfx/range/b2drange.hxx>
32cdf0e10cSrcweir #include <basegfx/matrix/b2dhommatrix.hxx>
33cdf0e10cSrcweir #include <basegfx/curve/b2dcubicbezier.hxx>
34cdf0e10cSrcweir #include <basegfx/matrix/b2dhommatrixtools.hxx>
355aaf853bSArmin Le Grand #include <com/sun/star/drawing/LineCap.hpp>
36c514522bSArmin Le Grand #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
37cdf0e10cSrcweir 
38cdf0e10cSrcweir namespace basegfx
39cdf0e10cSrcweir {
40cdf0e10cSrcweir     namespace tools
41cdf0e10cSrcweir     {
createAreaGeometryForLineStartEnd(const B2DPolygon & rCandidate,const B2DPolyPolygon & rArrow,bool bStart,double fWidth,double fCandidateLength,double fDockingPosition,double * pConsumedLength)42cdf0e10cSrcweir         B2DPolyPolygon createAreaGeometryForLineStartEnd(
43cdf0e10cSrcweir             const B2DPolygon& rCandidate,
44cdf0e10cSrcweir             const B2DPolyPolygon& rArrow,
45cdf0e10cSrcweir             bool bStart,
46cdf0e10cSrcweir             double fWidth,
47cdf0e10cSrcweir             double fCandidateLength,
48cdf0e10cSrcweir             double fDockingPosition, // 0->top, 1->bottom
49cdf0e10cSrcweir             double* pConsumedLength)
50cdf0e10cSrcweir         {
51cdf0e10cSrcweir             B2DPolyPolygon aRetval;
52cdf0e10cSrcweir             OSL_ENSURE(rCandidate.count() > 1L, "createAreaGeometryForLineStartEnd: Line polygon has too less points (!)");
53cdf0e10cSrcweir             OSL_ENSURE(rArrow.count() > 0L, "createAreaGeometryForLineStartEnd: Empty arrow PolyPolygon (!)");
54cdf0e10cSrcweir             OSL_ENSURE(fWidth > 0.0, "createAreaGeometryForLineStartEnd: Width too small (!)");
55cdf0e10cSrcweir             OSL_ENSURE(fDockingPosition >= 0.0 && fDockingPosition <= 1.0,
56cdf0e10cSrcweir                 "createAreaGeometryForLineStartEnd: fDockingPosition out of range [0.0 .. 1.0] (!)");
57cdf0e10cSrcweir 
58cdf0e10cSrcweir             if(fWidth < 0.0)
59cdf0e10cSrcweir             {
60cdf0e10cSrcweir                 fWidth = -fWidth;
61cdf0e10cSrcweir             }
62cdf0e10cSrcweir 
63cdf0e10cSrcweir             if(rCandidate.count() > 1 && rArrow.count() && !fTools::equalZero(fWidth))
64cdf0e10cSrcweir             {
65cdf0e10cSrcweir                 if(fDockingPosition < 0.0)
66cdf0e10cSrcweir                 {
67cdf0e10cSrcweir                     fDockingPosition = 0.0;
68cdf0e10cSrcweir                 }
69cdf0e10cSrcweir                 else if(fDockingPosition > 1.0)
70cdf0e10cSrcweir                 {
71cdf0e10cSrcweir                     fDockingPosition = 1.0;
72cdf0e10cSrcweir                 }
73cdf0e10cSrcweir 
74cdf0e10cSrcweir                 // init return value from arrow
75cdf0e10cSrcweir                 aRetval.append(rArrow);
76cdf0e10cSrcweir 
77cdf0e10cSrcweir                 // get size of the arrow
78cdf0e10cSrcweir                 const B2DRange aArrowSize(getRange(rArrow));
79cdf0e10cSrcweir 
80cdf0e10cSrcweir                 // build ArrowTransform; center in X, align with axis in Y
81cdf0e10cSrcweir                 B2DHomMatrix aArrowTransform(basegfx::tools::createTranslateB2DHomMatrix(
82cdf0e10cSrcweir                     -aArrowSize.getCenter().getX(), -aArrowSize.getMinimum().getY()));
83cdf0e10cSrcweir 
84cdf0e10cSrcweir                 // scale to target size
85cdf0e10cSrcweir                 const double fArrowScale(fWidth / (aArrowSize.getRange().getX()));
86cdf0e10cSrcweir                 aArrowTransform.scale(fArrowScale, fArrowScale);
87cdf0e10cSrcweir 
88cdf0e10cSrcweir                 // get arrow size in Y
89cdf0e10cSrcweir                 B2DPoint aUpperCenter(aArrowSize.getCenter().getX(), aArrowSize.getMaximum().getY());
90cdf0e10cSrcweir                 aUpperCenter *= aArrowTransform;
91cdf0e10cSrcweir                 const double fArrowYLength(B2DVector(aUpperCenter).getLength());
92cdf0e10cSrcweir 
93cdf0e10cSrcweir                 // move arrow to have docking position centered
94cdf0e10cSrcweir                 aArrowTransform.translate(0.0, -fArrowYLength * fDockingPosition);
95cdf0e10cSrcweir 
96cdf0e10cSrcweir                 // prepare polygon length
97cdf0e10cSrcweir                 if(fTools::equalZero(fCandidateLength))
98cdf0e10cSrcweir                 {
99cdf0e10cSrcweir                     fCandidateLength = getLength(rCandidate);
100cdf0e10cSrcweir                 }
101cdf0e10cSrcweir 
102cdf0e10cSrcweir                 // get the polygon vector we want to plant this arrow on
103cdf0e10cSrcweir                 const double fConsumedLength(fArrowYLength * (1.0 - fDockingPosition));
104cdf0e10cSrcweir                 const B2DVector aHead(rCandidate.getB2DPoint((bStart) ? 0L : rCandidate.count() - 1L));
105cdf0e10cSrcweir                 const B2DVector aTail(getPositionAbsolute(rCandidate,
106cdf0e10cSrcweir                     (bStart) ? fConsumedLength : fCandidateLength - fConsumedLength, fCandidateLength));
107cdf0e10cSrcweir 
108cdf0e10cSrcweir                 // from that vector, take the needed rotation and add rotate for arrow to transformation
109cdf0e10cSrcweir                 const B2DVector aTargetDirection(aHead - aTail);
110cdf0e10cSrcweir                 const double fRotation(atan2(aTargetDirection.getY(), aTargetDirection.getX()) + (90.0 * F_PI180));
111cdf0e10cSrcweir 
112cdf0e10cSrcweir                 // rotate around docking position
113cdf0e10cSrcweir                 aArrowTransform.rotate(fRotation);
114cdf0e10cSrcweir 
115cdf0e10cSrcweir                 // move arrow docking position to polygon head
116cdf0e10cSrcweir                 aArrowTransform.translate(aHead.getX(), aHead.getY());
117cdf0e10cSrcweir 
118cdf0e10cSrcweir                 // transform retval and close
119cdf0e10cSrcweir                 aRetval.transform(aArrowTransform);
120cdf0e10cSrcweir                 aRetval.setClosed(true);
121cdf0e10cSrcweir 
122cdf0e10cSrcweir                 // if pConsumedLength is asked for, fill it
123cdf0e10cSrcweir                 if(pConsumedLength)
124cdf0e10cSrcweir                 {
125cdf0e10cSrcweir                     *pConsumedLength = fConsumedLength;
126cdf0e10cSrcweir                 }
127cdf0e10cSrcweir             }
128cdf0e10cSrcweir 
129cdf0e10cSrcweir             return aRetval;
130cdf0e10cSrcweir         }
131cdf0e10cSrcweir     } // end of namespace tools
132cdf0e10cSrcweir } // end of namespace basegfx
133cdf0e10cSrcweir 
134cdf0e10cSrcweir //////////////////////////////////////////////////////////////////////////////
135cdf0e10cSrcweir 
136cdf0e10cSrcweir namespace basegfx
137cdf0e10cSrcweir {
138cdf0e10cSrcweir     // anonymus namespace for local helpers
139cdf0e10cSrcweir     namespace
140cdf0e10cSrcweir     {
impIsSimpleEdge(const B2DCubicBezier & rCandidate,double fMaxCosQuad,double fMaxPartOfEdgeQuad)141cdf0e10cSrcweir         bool impIsSimpleEdge(const B2DCubicBezier& rCandidate, double fMaxCosQuad, double fMaxPartOfEdgeQuad)
142cdf0e10cSrcweir         {
143cdf0e10cSrcweir             // isBezier() is true, already tested by caller
144cdf0e10cSrcweir             const B2DVector aEdge(rCandidate.getEndPoint() - rCandidate.getStartPoint());
145cdf0e10cSrcweir 
146cdf0e10cSrcweir             if(aEdge.equalZero())
147cdf0e10cSrcweir             {
148*d0f02d50Smseidel                 // start and end point the same, but control vectors used -> balloon curve loop
149cdf0e10cSrcweir                 // is not a simple edge
150cdf0e10cSrcweir                 return false;
151cdf0e10cSrcweir             }
152cdf0e10cSrcweir 
153cdf0e10cSrcweir             // get tangentA and scalar with edge
154cdf0e10cSrcweir             const B2DVector aTangentA(rCandidate.getTangent(0.0));
155cdf0e10cSrcweir             const double fScalarAE(aEdge.scalar(aTangentA));
156cdf0e10cSrcweir 
157cdf0e10cSrcweir             if(fTools::lessOrEqual(fScalarAE, 0.0))
158cdf0e10cSrcweir             {
159cdf0e10cSrcweir                 // angle between TangentA and Edge is bigger or equal 90 degrees
160cdf0e10cSrcweir                 return false;
161cdf0e10cSrcweir             }
162cdf0e10cSrcweir 
163cdf0e10cSrcweir             // get self-scalars for E and A
164cdf0e10cSrcweir             const double fScalarE(aEdge.scalar(aEdge));
165cdf0e10cSrcweir             const double fScalarA(aTangentA.scalar(aTangentA));
166cdf0e10cSrcweir             const double fLengthCompareE(fScalarE * fMaxPartOfEdgeQuad);
167cdf0e10cSrcweir 
168cdf0e10cSrcweir             if(fTools::moreOrEqual(fScalarA, fLengthCompareE))
169cdf0e10cSrcweir             {
170cdf0e10cSrcweir                 // length of TangentA is more than fMaxPartOfEdge of length of edge
171cdf0e10cSrcweir                 return false;
172cdf0e10cSrcweir             }
173cdf0e10cSrcweir 
174cdf0e10cSrcweir             if(fTools::lessOrEqual(fScalarAE * fScalarAE, fScalarA * fScalarE * fMaxCosQuad))
175cdf0e10cSrcweir             {
176cdf0e10cSrcweir                 // angle between TangentA and Edge is bigger or equal angle defined by fMaxCos
177cdf0e10cSrcweir                 return false;
178cdf0e10cSrcweir             }
179cdf0e10cSrcweir 
180cdf0e10cSrcweir             // get tangentB and scalar with edge
181cdf0e10cSrcweir             const B2DVector aTangentB(rCandidate.getTangent(1.0));
182cdf0e10cSrcweir             const double fScalarBE(aEdge.scalar(aTangentB));
183cdf0e10cSrcweir 
184cdf0e10cSrcweir             if(fTools::lessOrEqual(fScalarBE, 0.0))
185cdf0e10cSrcweir             {
186cdf0e10cSrcweir                 // angle between TangentB and Edge is bigger or equal 90 degrees
187cdf0e10cSrcweir                 return false;
188cdf0e10cSrcweir             }
189cdf0e10cSrcweir 
190cdf0e10cSrcweir             // get self-scalar for B
191cdf0e10cSrcweir             const double fScalarB(aTangentB.scalar(aTangentB));
192cdf0e10cSrcweir 
193cdf0e10cSrcweir             if(fTools::moreOrEqual(fScalarB, fLengthCompareE))
194cdf0e10cSrcweir             {
195cdf0e10cSrcweir                 // length of TangentB is more than fMaxPartOfEdge of length of edge
196cdf0e10cSrcweir                 return false;
197cdf0e10cSrcweir             }
198cdf0e10cSrcweir 
199cdf0e10cSrcweir             if(fTools::lessOrEqual(fScalarBE * fScalarBE, fScalarB * fScalarE * fMaxCosQuad))
200cdf0e10cSrcweir             {
201cdf0e10cSrcweir                 // angle between TangentB and Edge is bigger or equal defined by fMaxCos
202cdf0e10cSrcweir                 return false;
203cdf0e10cSrcweir             }
204cdf0e10cSrcweir 
205cdf0e10cSrcweir             return true;
206cdf0e10cSrcweir         }
207cdf0e10cSrcweir 
impSubdivideToSimple(const B2DCubicBezier & rCandidate,B2DPolygon & rTarget,double fMaxCosQuad,double fMaxPartOfEdgeQuad,sal_uInt32 nMaxRecursionDepth)208cdf0e10cSrcweir         void impSubdivideToSimple(const B2DCubicBezier& rCandidate, B2DPolygon& rTarget, double fMaxCosQuad, double fMaxPartOfEdgeQuad, sal_uInt32 nMaxRecursionDepth)
209cdf0e10cSrcweir         {
210cdf0e10cSrcweir             if(!nMaxRecursionDepth || impIsSimpleEdge(rCandidate, fMaxCosQuad, fMaxPartOfEdgeQuad))
211cdf0e10cSrcweir             {
212cdf0e10cSrcweir                 rTarget.appendBezierSegment(rCandidate.getControlPointA(), rCandidate.getControlPointB(), rCandidate.getEndPoint());
213cdf0e10cSrcweir             }
214cdf0e10cSrcweir             else
215cdf0e10cSrcweir             {
216cdf0e10cSrcweir                 B2DCubicBezier aLeft, aRight;
217cdf0e10cSrcweir                 rCandidate.split(0.5, &aLeft, &aRight);
218cdf0e10cSrcweir 
219cdf0e10cSrcweir                 impSubdivideToSimple(aLeft, rTarget, fMaxCosQuad, fMaxPartOfEdgeQuad, nMaxRecursionDepth - 1);
220cdf0e10cSrcweir                 impSubdivideToSimple(aRight, rTarget, fMaxCosQuad, fMaxPartOfEdgeQuad, nMaxRecursionDepth - 1);
221cdf0e10cSrcweir             }
222cdf0e10cSrcweir         }
223cdf0e10cSrcweir 
subdivideToSimple(const B2DPolygon & rCandidate,double fMaxCosQuad,double fMaxPartOfEdgeQuad)224cdf0e10cSrcweir         B2DPolygon subdivideToSimple(const B2DPolygon& rCandidate, double fMaxCosQuad, double fMaxPartOfEdgeQuad)
225cdf0e10cSrcweir         {
226cdf0e10cSrcweir             const sal_uInt32 nPointCount(rCandidate.count());
227cdf0e10cSrcweir 
228cdf0e10cSrcweir             if(rCandidate.areControlPointsUsed() && nPointCount)
229cdf0e10cSrcweir             {
230cdf0e10cSrcweir                 const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
231cdf0e10cSrcweir                 B2DPolygon aRetval;
232cdf0e10cSrcweir                 B2DCubicBezier aEdge;
233cdf0e10cSrcweir 
234cdf0e10cSrcweir                 // prepare edge for loop
235cdf0e10cSrcweir                 aEdge.setStartPoint(rCandidate.getB2DPoint(0));
236cdf0e10cSrcweir                 aRetval.append(aEdge.getStartPoint());
237cdf0e10cSrcweir 
238cdf0e10cSrcweir                 for(sal_uInt32 a(0); a < nEdgeCount; a++)
239cdf0e10cSrcweir                 {
240cdf0e10cSrcweir                     // fill B2DCubicBezier
241cdf0e10cSrcweir                     const sal_uInt32 nNextIndex((a + 1) % nPointCount);
242cdf0e10cSrcweir                     aEdge.setControlPointA(rCandidate.getNextControlPoint(a));
243cdf0e10cSrcweir                     aEdge.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex));
244cdf0e10cSrcweir                     aEdge.setEndPoint(rCandidate.getB2DPoint(nNextIndex));
245cdf0e10cSrcweir 
246cdf0e10cSrcweir                     // get rid of unnecessary bezier segments
247cdf0e10cSrcweir                     aEdge.testAndSolveTrivialBezier();
248cdf0e10cSrcweir 
249cdf0e10cSrcweir                     if(aEdge.isBezier())
250cdf0e10cSrcweir                     {
251cdf0e10cSrcweir                         // before splitting recursively with internal simple criteria, use
252cdf0e10cSrcweir                         // ExtremumPosFinder to remove those
253cdf0e10cSrcweir                         ::std::vector< double > aExtremumPositions;
254cdf0e10cSrcweir 
255cdf0e10cSrcweir                         aExtremumPositions.reserve(4);
256cdf0e10cSrcweir                         aEdge.getAllExtremumPositions(aExtremumPositions);
257cdf0e10cSrcweir 
258cdf0e10cSrcweir                         const sal_uInt32 nCount(aExtremumPositions.size());
259cdf0e10cSrcweir 
260cdf0e10cSrcweir                         if(nCount)
261cdf0e10cSrcweir                         {
262cdf0e10cSrcweir                             if(nCount > 1)
263cdf0e10cSrcweir                             {
264cdf0e10cSrcweir                                 // create order from left to right
265cdf0e10cSrcweir                                 ::std::sort(aExtremumPositions.begin(), aExtremumPositions.end());
266cdf0e10cSrcweir                             }
267cdf0e10cSrcweir 
268cdf0e10cSrcweir                             for(sal_uInt32 b(0); b < nCount;)
269cdf0e10cSrcweir                             {
270cdf0e10cSrcweir                                 // split aEdge at next split pos
271cdf0e10cSrcweir                                 B2DCubicBezier aLeft;
272cdf0e10cSrcweir                                 const double fSplitPos(aExtremumPositions[b++]);
273cdf0e10cSrcweir 
274cdf0e10cSrcweir                                 aEdge.split(fSplitPos, &aLeft, &aEdge);
275cdf0e10cSrcweir                                 aLeft.testAndSolveTrivialBezier();
276cdf0e10cSrcweir 
277cdf0e10cSrcweir                                 // consume left part
278cdf0e10cSrcweir                                 if(aLeft.isBezier())
279cdf0e10cSrcweir                                 {
280cdf0e10cSrcweir                                     impSubdivideToSimple(aLeft, aRetval, fMaxCosQuad, fMaxPartOfEdgeQuad, 6);
281cdf0e10cSrcweir                                 }
282cdf0e10cSrcweir                                 else
283cdf0e10cSrcweir                                 {
284cdf0e10cSrcweir                                     aRetval.append(aLeft.getEndPoint());
285cdf0e10cSrcweir                                 }
286cdf0e10cSrcweir 
287cdf0e10cSrcweir                                 if(b < nCount)
288cdf0e10cSrcweir                                 {
289cdf0e10cSrcweir                                     // correct the remaining split positions to fit to shortened aEdge
290cdf0e10cSrcweir                                     const double fScaleFactor(1.0 / (1.0 - fSplitPos));
291cdf0e10cSrcweir 
292cdf0e10cSrcweir                                     for(sal_uInt32 c(b); c < nCount; c++)
293cdf0e10cSrcweir                                     {
294cdf0e10cSrcweir                                         aExtremumPositions[c] = (aExtremumPositions[c] - fSplitPos) * fScaleFactor;
295cdf0e10cSrcweir                                     }
296cdf0e10cSrcweir                                 }
297cdf0e10cSrcweir                             }
298cdf0e10cSrcweir 
299cdf0e10cSrcweir                             // test the shortened rest of aEdge
300cdf0e10cSrcweir                             aEdge.testAndSolveTrivialBezier();
301cdf0e10cSrcweir 
302cdf0e10cSrcweir                             // consume right part
303cdf0e10cSrcweir                             if(aEdge.isBezier())
304cdf0e10cSrcweir                             {
305cdf0e10cSrcweir                                 impSubdivideToSimple(aEdge, aRetval, fMaxCosQuad, fMaxPartOfEdgeQuad, 6);
306cdf0e10cSrcweir                             }
307cdf0e10cSrcweir                             else
308cdf0e10cSrcweir                             {
309cdf0e10cSrcweir                                 aRetval.append(aEdge.getEndPoint());
310cdf0e10cSrcweir                             }
311cdf0e10cSrcweir                         }
312cdf0e10cSrcweir                         else
313cdf0e10cSrcweir                         {
314cdf0e10cSrcweir                             impSubdivideToSimple(aEdge, aRetval, fMaxCosQuad, fMaxPartOfEdgeQuad, 6);
315cdf0e10cSrcweir                         }
316cdf0e10cSrcweir                     }
317cdf0e10cSrcweir                     else
318cdf0e10cSrcweir                     {
319cdf0e10cSrcweir                         // straight edge, add point
320cdf0e10cSrcweir                         aRetval.append(aEdge.getEndPoint());
321cdf0e10cSrcweir                     }
322cdf0e10cSrcweir 
323cdf0e10cSrcweir                     // prepare edge for next step
324cdf0e10cSrcweir                     aEdge.setStartPoint(aEdge.getEndPoint());
325cdf0e10cSrcweir                 }
326cdf0e10cSrcweir 
327cdf0e10cSrcweir                 // copy closed flag and check for double points
328cdf0e10cSrcweir                 aRetval.setClosed(rCandidate.isClosed());
329cdf0e10cSrcweir                 aRetval.removeDoublePoints();
330cdf0e10cSrcweir 
331cdf0e10cSrcweir                 return aRetval;
332cdf0e10cSrcweir             }
333cdf0e10cSrcweir             else
334cdf0e10cSrcweir             {
335cdf0e10cSrcweir                 return rCandidate;
336cdf0e10cSrcweir             }
337cdf0e10cSrcweir         }
338cdf0e10cSrcweir 
createAreaGeometryForEdge(const B2DCubicBezier & rEdge,double fHalfLineWidth,bool bStartRound,bool bEndRound,bool bStartSquare,bool bEndSquare)339c514522bSArmin Le Grand         B2DPolygon createAreaGeometryForEdge(
3405aaf853bSArmin Le Grand             const B2DCubicBezier& rEdge,
3415aaf853bSArmin Le Grand             double fHalfLineWidth,
3425aaf853bSArmin Le Grand             bool bStartRound,
3435aaf853bSArmin Le Grand             bool bEndRound,
3445aaf853bSArmin Le Grand             bool bStartSquare,
3455aaf853bSArmin Le Grand             bool bEndSquare)
346cdf0e10cSrcweir         {
347cdf0e10cSrcweir             // create polygon for edge
348cdf0e10cSrcweir             // Unfortunately, while it would be geometrically correct to not add
349cdf0e10cSrcweir             // the in-between points EdgeEnd and EdgeStart, it leads to rounding
350cdf0e10cSrcweir             // errors when converting to integer polygon coordinates for painting
351cdf0e10cSrcweir             if(rEdge.isBezier())
352cdf0e10cSrcweir             {
353cdf0e10cSrcweir                 // prepare target and data common for upper and lower
354cdf0e10cSrcweir                 B2DPolygon aBezierPolygon;
355cdf0e10cSrcweir                 const B2DVector aPureEdgeVector(rEdge.getEndPoint() - rEdge.getStartPoint());
356cdf0e10cSrcweir                 const double fEdgeLength(aPureEdgeVector.getLength());
357cdf0e10cSrcweir                 const bool bIsEdgeLengthZero(fTools::equalZero(fEdgeLength));
3585aaf853bSArmin Le Grand                 B2DVector aTangentA(rEdge.getTangent(0.0)); aTangentA.normalize();
3595aaf853bSArmin Le Grand                 B2DVector aTangentB(rEdge.getTangent(1.0)); aTangentB.normalize();
3605aaf853bSArmin Le Grand                 const B2DVector aNormalizedPerpendicularA(getPerpendicular(aTangentA));
3615aaf853bSArmin Le Grand                 const B2DVector aNormalizedPerpendicularB(getPerpendicular(aTangentB));
3625aaf853bSArmin Le Grand 
3635aaf853bSArmin Le Grand                 // create upper displacement vectors and check if they cut
3645aaf853bSArmin Le Grand                 const B2DVector aPerpendStartA(aNormalizedPerpendicularA * -fHalfLineWidth);
3655aaf853bSArmin Le Grand                 const B2DVector aPerpendEndA(aNormalizedPerpendicularB * -fHalfLineWidth);
3665aaf853bSArmin Le Grand                 double fCutA(0.0);
3675aaf853bSArmin Le Grand                 const tools::CutFlagValue aCutA(tools::findCut(
3685aaf853bSArmin Le Grand                     rEdge.getStartPoint(), aPerpendStartA,
3695aaf853bSArmin Le Grand                     rEdge.getEndPoint(), aPerpendEndA,
3705aaf853bSArmin Le Grand                     CUTFLAG_ALL, &fCutA));
3715aaf853bSArmin Le Grand                 const bool bCutA(CUTFLAG_NONE != aCutA);
3725aaf853bSArmin Le Grand 
3735aaf853bSArmin Le Grand                 // create lower displacement vectors and check if they cut
3745aaf853bSArmin Le Grand                 const B2DVector aPerpendStartB(aNormalizedPerpendicularA * fHalfLineWidth);
3755aaf853bSArmin Le Grand                 const B2DVector aPerpendEndB(aNormalizedPerpendicularB * fHalfLineWidth);
3765aaf853bSArmin Le Grand                 double fCutB(0.0);
3775aaf853bSArmin Le Grand                 const tools::CutFlagValue aCutB(tools::findCut(
3785aaf853bSArmin Le Grand                     rEdge.getEndPoint(), aPerpendEndB,
3795aaf853bSArmin Le Grand                     rEdge.getStartPoint(), aPerpendStartB,
3805aaf853bSArmin Le Grand                     CUTFLAG_ALL, &fCutB));
3815aaf853bSArmin Le Grand                 const bool bCutB(CUTFLAG_NONE != aCutB);
3825aaf853bSArmin Le Grand 
3835aaf853bSArmin Le Grand                 // check if cut happens
3845aaf853bSArmin Le Grand                 const bool bCut(bCutA || bCutB);
385c514522bSArmin Le Grand                 B2DPoint aCutPoint;
3865aaf853bSArmin Le Grand 
3875aaf853bSArmin Le Grand                 // create left edge
3885aaf853bSArmin Le Grand                 if(bStartRound || bStartSquare)
3895aaf853bSArmin Le Grand                 {
3905aaf853bSArmin Le Grand                     if(bStartRound)
3915aaf853bSArmin Le Grand                     {
392c514522bSArmin Le Grand                         basegfx::B2DPolygon aStartPolygon(tools::createHalfUnitCircle());
393c514522bSArmin Le Grand 
3945aaf853bSArmin Le Grand                         aStartPolygon.transform(
3955aaf853bSArmin Le Grand                             tools::createScaleShearXRotateTranslateB2DHomMatrix(
3965aaf853bSArmin Le Grand                                 fHalfLineWidth, fHalfLineWidth,
3975aaf853bSArmin Le Grand                                 0.0,
3985aaf853bSArmin Le Grand                                 atan2(aTangentA.getY(), aTangentA.getX()) + F_PI2,
3995aaf853bSArmin Le Grand                                 rEdge.getStartPoint().getX(), rEdge.getStartPoint().getY()));
400c514522bSArmin Le Grand                         aBezierPolygon.append(aStartPolygon);
4015aaf853bSArmin Le Grand                     }
4025aaf853bSArmin Le Grand                     else // bStartSquare
4035aaf853bSArmin Le Grand                     {
4045aaf853bSArmin Le Grand                         const basegfx::B2DPoint aStart(rEdge.getStartPoint() - (aTangentA * fHalfLineWidth));
4055aaf853bSArmin Le Grand 
406c514522bSArmin Le Grand                         if(bCutB)
4075aaf853bSArmin Le Grand                         {
408c514522bSArmin Le Grand                             aBezierPolygon.append(rEdge.getStartPoint() + aPerpendStartB);
4095aaf853bSArmin Le Grand                         }
4105aaf853bSArmin Le Grand 
411c514522bSArmin Le Grand                         aBezierPolygon.append(aStart + aPerpendStartB);
412c514522bSArmin Le Grand                         aBezierPolygon.append(aStart + aPerpendStartA);
4135aaf853bSArmin Le Grand 
414c514522bSArmin Le Grand                         if(bCutA)
4155aaf853bSArmin Le Grand                         {
416c514522bSArmin Le Grand                             aBezierPolygon.append(rEdge.getStartPoint() + aPerpendStartA);
4175aaf853bSArmin Le Grand                         }
4185aaf853bSArmin Le Grand                     }
4195aaf853bSArmin Le Grand                 }
4205aaf853bSArmin Le Grand                 else
4215aaf853bSArmin Le Grand                 {
4225aaf853bSArmin Le Grand                     // append original in-between point
4235aaf853bSArmin Le Grand                     aBezierPolygon.append(rEdge.getStartPoint());
4245aaf853bSArmin Le Grand                 }
425cdf0e10cSrcweir 
426cdf0e10cSrcweir                 // create upper edge.
427cdf0e10cSrcweir                 {
4285aaf853bSArmin Le Grand                     if(bCutA)
429cdf0e10cSrcweir                     {
430cdf0e10cSrcweir                         // calculate cut point and add
431c514522bSArmin Le Grand                         aCutPoint = rEdge.getStartPoint() + (aPerpendStartA * fCutA);
432cdf0e10cSrcweir                         aBezierPolygon.append(aCutPoint);
433cdf0e10cSrcweir                     }
434cdf0e10cSrcweir                     else
435cdf0e10cSrcweir                     {
436cdf0e10cSrcweir                         // create scaled bezier segment
4375aaf853bSArmin Le Grand                         const B2DPoint aStart(rEdge.getStartPoint() + aPerpendStartA);
4385aaf853bSArmin Le Grand                         const B2DPoint aEnd(rEdge.getEndPoint() + aPerpendEndA);
439cdf0e10cSrcweir                         const B2DVector aEdge(aEnd - aStart);
440cdf0e10cSrcweir                         const double fLength(aEdge.getLength());
441cdf0e10cSrcweir                         const double fScale(bIsEdgeLengthZero ? 1.0 : fLength / fEdgeLength);
442cdf0e10cSrcweir                         const B2DVector fRelNext(rEdge.getControlPointA() - rEdge.getStartPoint());
443cdf0e10cSrcweir                         const B2DVector fRelPrev(rEdge.getControlPointB() - rEdge.getEndPoint());
444cdf0e10cSrcweir 
445cdf0e10cSrcweir                         aBezierPolygon.append(aStart);
446cdf0e10cSrcweir                         aBezierPolygon.appendBezierSegment(aStart + (fRelNext * fScale), aEnd + (fRelPrev * fScale), aEnd);
447cdf0e10cSrcweir                     }
448cdf0e10cSrcweir                 }
449cdf0e10cSrcweir 
4505aaf853bSArmin Le Grand                 // create right edge
4515aaf853bSArmin Le Grand                 if(bEndRound || bEndSquare)
4525aaf853bSArmin Le Grand                 {
4535aaf853bSArmin Le Grand                     if(bEndRound)
4545aaf853bSArmin Le Grand                     {
455c514522bSArmin Le Grand                         basegfx::B2DPolygon aEndPolygon(tools::createHalfUnitCircle());
456c514522bSArmin Le Grand 
4575aaf853bSArmin Le Grand                         aEndPolygon.transform(
4585aaf853bSArmin Le Grand                             tools::createScaleShearXRotateTranslateB2DHomMatrix(
4595aaf853bSArmin Le Grand                                 fHalfLineWidth, fHalfLineWidth,
4605aaf853bSArmin Le Grand                                 0.0,
4615aaf853bSArmin Le Grand                                 atan2(aTangentB.getY(), aTangentB.getX()) - F_PI2,
4625aaf853bSArmin Le Grand                                 rEdge.getEndPoint().getX(), rEdge.getEndPoint().getY()));
463c514522bSArmin Le Grand                         aBezierPolygon.append(aEndPolygon);
4645aaf853bSArmin Le Grand                     }
4655aaf853bSArmin Le Grand                     else // bEndSquare
4665aaf853bSArmin Le Grand                     {
4675aaf853bSArmin Le Grand                         const basegfx::B2DPoint aEnd(rEdge.getEndPoint() + (aTangentB * fHalfLineWidth));
4685aaf853bSArmin Le Grand 
469c514522bSArmin Le Grand                         if(bCutA)
4705aaf853bSArmin Le Grand                         {
471c514522bSArmin Le Grand                             aBezierPolygon.append(rEdge.getEndPoint() + aPerpendEndA);
4725aaf853bSArmin Le Grand                         }
4735aaf853bSArmin Le Grand 
474c514522bSArmin Le Grand                         aBezierPolygon.append(aEnd + aPerpendEndA);
475c514522bSArmin Le Grand                         aBezierPolygon.append(aEnd + aPerpendEndB);
4765aaf853bSArmin Le Grand 
477c514522bSArmin Le Grand                         if(bCutB)
4785aaf853bSArmin Le Grand                         {
479c514522bSArmin Le Grand                             aBezierPolygon.append(rEdge.getEndPoint() + aPerpendEndB);
4805aaf853bSArmin Le Grand                         }
4815aaf853bSArmin Le Grand                     }
4825aaf853bSArmin Le Grand                 }
4835aaf853bSArmin Le Grand                 else
4845aaf853bSArmin Le Grand                 {
485cdf0e10cSrcweir                     // append original in-between point
486cdf0e10cSrcweir                     aBezierPolygon.append(rEdge.getEndPoint());
4875aaf853bSArmin Le Grand                 }
488cdf0e10cSrcweir 
489cdf0e10cSrcweir                 // create lower edge.
490cdf0e10cSrcweir                 {
4915aaf853bSArmin Le Grand                     if(bCutB)
492cdf0e10cSrcweir                     {
493cdf0e10cSrcweir                         // calculate cut point and add
494c514522bSArmin Le Grand                         aCutPoint = rEdge.getEndPoint() + (aPerpendEndB * fCutB);
495cdf0e10cSrcweir                         aBezierPolygon.append(aCutPoint);
496cdf0e10cSrcweir                     }
497cdf0e10cSrcweir                     else
498cdf0e10cSrcweir                     {
499cdf0e10cSrcweir                         // create scaled bezier segment
5005aaf853bSArmin Le Grand                         const B2DPoint aStart(rEdge.getEndPoint() + aPerpendEndB);
5015aaf853bSArmin Le Grand                         const B2DPoint aEnd(rEdge.getStartPoint() + aPerpendStartB);
502cdf0e10cSrcweir                         const B2DVector aEdge(aEnd - aStart);
503cdf0e10cSrcweir                         const double fLength(aEdge.getLength());
504cdf0e10cSrcweir                         const double fScale(bIsEdgeLengthZero ? 1.0 : fLength / fEdgeLength);
505cdf0e10cSrcweir                         const B2DVector fRelNext(rEdge.getControlPointB() - rEdge.getEndPoint());
506cdf0e10cSrcweir                         const B2DVector fRelPrev(rEdge.getControlPointA() - rEdge.getStartPoint());
507cdf0e10cSrcweir 
508cdf0e10cSrcweir                         aBezierPolygon.append(aStart);
509cdf0e10cSrcweir                         aBezierPolygon.appendBezierSegment(aStart + (fRelNext * fScale), aEnd + (fRelPrev * fScale), aEnd);
510cdf0e10cSrcweir                     }
511cdf0e10cSrcweir                 }
512cdf0e10cSrcweir 
513c514522bSArmin Le Grand                 // close
514cdf0e10cSrcweir                 aBezierPolygon.setClosed(true);
5155aaf853bSArmin Le Grand 
516c514522bSArmin Le Grand                 if(bStartRound || bEndRound)
517c514522bSArmin Le Grand                 {
518c514522bSArmin Le Grand                     // double points possible when round caps are used at start or end
519c514522bSArmin Le Grand                     aBezierPolygon.removeDoublePoints();
520c514522bSArmin Le Grand                 }
521c514522bSArmin Le Grand 
522c514522bSArmin Le Grand                 if(bCut && ((bStartRound || bStartSquare) && (bEndRound || bEndSquare)))
523c514522bSArmin Le Grand                 {
524c514522bSArmin Le Grand                     // When cut exists and both ends are extended with caps, a self-intersecting polygon
525c514522bSArmin Le Grand                     // is created; one cut point is known, but there is a 2nd one in the caps geometry.
526c514522bSArmin Le Grand                     // Solve by using tooling.
527c514522bSArmin Le Grand                     // Remark: This nearly never happens due to curve preparations to extreme points
528*d0f02d50Smseidel                     // and maximum angle turning, but I constructed a test case and checked that it is
529*d0f02d50Smseidel                     // working properly.
530c514522bSArmin Le Grand                     const B2DPolyPolygon aTemp(tools::solveCrossovers(aBezierPolygon));
531c514522bSArmin Le Grand                     const sal_uInt32 nTempCount(aTemp.count());
532c514522bSArmin Le Grand 
533c514522bSArmin Le Grand                     if(nTempCount)
534c514522bSArmin Le Grand                     {
535c514522bSArmin Le Grand                         if(nTempCount > 1)
536c514522bSArmin Le Grand                         {
537c514522bSArmin Le Grand                             // as expected, multiple polygons (with same orientation). Remove
538c514522bSArmin Le Grand                             // the one which contains aCutPoint, or better take the one without
539c514522bSArmin Le Grand                             for (sal_uInt32 a(0); a < nTempCount; a++)
540c514522bSArmin Le Grand                             {
541c514522bSArmin Le Grand                                 aBezierPolygon = aTemp.getB2DPolygon(a);
542c514522bSArmin Le Grand 
543c514522bSArmin Le Grand                                 const sal_uInt32 nCandCount(aBezierPolygon.count());
544c514522bSArmin Le Grand 
545c514522bSArmin Le Grand                                 for(sal_uInt32 b(0); b < nCandCount; b++)
546c514522bSArmin Le Grand                                 {
547c514522bSArmin Le Grand                                     if(aCutPoint.equal(aBezierPolygon.getB2DPoint(b)))
548c514522bSArmin Le Grand                                     {
549c514522bSArmin Le Grand                                         aBezierPolygon.clear();
550c514522bSArmin Le Grand                                         break;
551c514522bSArmin Le Grand                                     }
552c514522bSArmin Le Grand                                 }
553c514522bSArmin Le Grand 
554c514522bSArmin Le Grand                                 if(aBezierPolygon.count())
555c514522bSArmin Le Grand                                 {
556c514522bSArmin Le Grand                                     break;
557c514522bSArmin Le Grand                                 }
558c514522bSArmin Le Grand                             }
559c514522bSArmin Le Grand 
560c514522bSArmin Le Grand                             OSL_ENSURE(aBezierPolygon.count(), "Error in line geometry creation, could not solve self-intersection (!)");
561c514522bSArmin Le Grand                         }
562c514522bSArmin Le Grand                         else
563c514522bSArmin Le Grand                         {
564c514522bSArmin Le Grand                             // none found, use result
565c514522bSArmin Le Grand                             aBezierPolygon = aTemp.getB2DPolygon(0);
566c514522bSArmin Le Grand                         }
567c514522bSArmin Le Grand                     }
568c514522bSArmin Le Grand                     else
569c514522bSArmin Le Grand                     {
570c514522bSArmin Le Grand                         OSL_ENSURE(false, "Error in line geometry creation, could not solve self-intersection (!)");
571c514522bSArmin Le Grand                     }
572c514522bSArmin Le Grand                 }
573c514522bSArmin Le Grand 
574c514522bSArmin Le Grand                 // return
575c514522bSArmin Le Grand                 return aBezierPolygon;
576cdf0e10cSrcweir             }
577cdf0e10cSrcweir             else
578cdf0e10cSrcweir             {
5795aaf853bSArmin Le Grand                 // Get start and end point, create tangent and set to needed length
5805aaf853bSArmin Le Grand                 B2DVector aTangent(rEdge.getEndPoint() - rEdge.getStartPoint());
5815aaf853bSArmin Le Grand                 aTangent.setLength(fHalfLineWidth);
5825aaf853bSArmin Le Grand 
5835aaf853bSArmin Le Grand                 // prepare return value
584cdf0e10cSrcweir                 B2DPolygon aEdgePolygon;
585cdf0e10cSrcweir 
5865aaf853bSArmin Le Grand                 // buffered angle
5875aaf853bSArmin Le Grand                 double fAngle(0.0);
5885aaf853bSArmin Le Grand                 bool bAngle(false);
589cdf0e10cSrcweir 
5905aaf853bSArmin Le Grand                 // buffered perpendicular
5915aaf853bSArmin Le Grand                 B2DVector aPerpend;
5925aaf853bSArmin Le Grand                 bool bPerpend(false);
593cdf0e10cSrcweir 
5945aaf853bSArmin Le Grand                 // create left vertical
5955aaf853bSArmin Le Grand                 if(bStartRound)
5965aaf853bSArmin Le Grand                 {
5975aaf853bSArmin Le Grand                     aEdgePolygon = tools::createHalfUnitCircle();
5985aaf853bSArmin Le Grand                     fAngle = atan2(aTangent.getY(), aTangent.getX());
5995aaf853bSArmin Le Grand                     bAngle = true;
6005aaf853bSArmin Le Grand                     aEdgePolygon.transform(
6015aaf853bSArmin Le Grand                         tools::createScaleShearXRotateTranslateB2DHomMatrix(
6025aaf853bSArmin Le Grand                             fHalfLineWidth, fHalfLineWidth,
6035aaf853bSArmin Le Grand                             0.0,
6045aaf853bSArmin Le Grand                             fAngle + F_PI2,
6055aaf853bSArmin Le Grand                             rEdge.getStartPoint().getX(), rEdge.getStartPoint().getY()));
6065aaf853bSArmin Le Grand                 }
6075aaf853bSArmin Le Grand                 else
6085aaf853bSArmin Le Grand                 {
6095aaf853bSArmin Le Grand                     aPerpend.setX(-aTangent.getY());
6105aaf853bSArmin Le Grand                     aPerpend.setY(aTangent.getX());
6115aaf853bSArmin Le Grand                     bPerpend = true;
612cdf0e10cSrcweir 
6135aaf853bSArmin Le Grand                     if(bStartSquare)
6145aaf853bSArmin Le Grand                     {
6155aaf853bSArmin Le Grand                         const basegfx::B2DPoint aStart(rEdge.getStartPoint() - aTangent);
6165aaf853bSArmin Le Grand 
6175aaf853bSArmin Le Grand                         aEdgePolygon.append(aStart + aPerpend);
6185aaf853bSArmin Le Grand                         aEdgePolygon.append(aStart - aPerpend);
6195aaf853bSArmin Le Grand                     }
6205aaf853bSArmin Le Grand                     else
6215aaf853bSArmin Le Grand                     {
6225aaf853bSArmin Le Grand                         aEdgePolygon.append(rEdge.getStartPoint() + aPerpend);
6235aaf853bSArmin Le Grand                         aEdgePolygon.append(rEdge.getStartPoint()); // keep the in-between point for numerical reasons
6245aaf853bSArmin Le Grand                         aEdgePolygon.append(rEdge.getStartPoint() - aPerpend);
6255aaf853bSArmin Le Grand                     }
6265aaf853bSArmin Le Grand                 }
6275aaf853bSArmin Le Grand 
6285aaf853bSArmin Le Grand                 // create right vertical
6295aaf853bSArmin Le Grand                 if(bEndRound)
6305aaf853bSArmin Le Grand                 {
6315aaf853bSArmin Le Grand                     basegfx::B2DPolygon aEndPolygon(tools::createHalfUnitCircle());
6325aaf853bSArmin Le Grand 
6335aaf853bSArmin Le Grand                     if(!bAngle)
6345aaf853bSArmin Le Grand                     {
6355aaf853bSArmin Le Grand                         fAngle = atan2(aTangent.getY(), aTangent.getX());
6365aaf853bSArmin Le Grand                     }
6375aaf853bSArmin Le Grand 
6385aaf853bSArmin Le Grand                     aEndPolygon.transform(
6395aaf853bSArmin Le Grand                         tools::createScaleShearXRotateTranslateB2DHomMatrix(
6405aaf853bSArmin Le Grand                             fHalfLineWidth, fHalfLineWidth,
6415aaf853bSArmin Le Grand                             0.0,
6425aaf853bSArmin Le Grand                             fAngle - F_PI2,
6435aaf853bSArmin Le Grand                             rEdge.getEndPoint().getX(), rEdge.getEndPoint().getY()));
6445aaf853bSArmin Le Grand                     aEdgePolygon.append(aEndPolygon);
6455aaf853bSArmin Le Grand                 }
6465aaf853bSArmin Le Grand                 else
6475aaf853bSArmin Le Grand                 {
6485aaf853bSArmin Le Grand                     if(!bPerpend)
6495aaf853bSArmin Le Grand                     {
6505aaf853bSArmin Le Grand                         aPerpend.setX(-aTangent.getY());
6515aaf853bSArmin Le Grand                         aPerpend.setY(aTangent.getX());
6525aaf853bSArmin Le Grand                     }
6535aaf853bSArmin Le Grand 
6545aaf853bSArmin Le Grand                     if(bEndSquare)
6555aaf853bSArmin Le Grand                     {
6565aaf853bSArmin Le Grand                         const basegfx::B2DPoint aEnd(rEdge.getEndPoint() + aTangent);
6575aaf853bSArmin Le Grand 
6585aaf853bSArmin Le Grand                         aEdgePolygon.append(aEnd - aPerpend);
6595aaf853bSArmin Le Grand                         aEdgePolygon.append(aEnd + aPerpend);
6605aaf853bSArmin Le Grand                     }
6615aaf853bSArmin Le Grand                     else
6625aaf853bSArmin Le Grand                     {
6635aaf853bSArmin Le Grand                         aEdgePolygon.append(rEdge.getEndPoint() - aPerpend);
6645aaf853bSArmin Le Grand                         aEdgePolygon.append(rEdge.getEndPoint()); // keep the in-between point for numerical reasons
6655aaf853bSArmin Le Grand                         aEdgePolygon.append(rEdge.getEndPoint() + aPerpend);
6665aaf853bSArmin Le Grand                     }
6675aaf853bSArmin Le Grand                 }
668cdf0e10cSrcweir 
669cdf0e10cSrcweir                 // close and return
670cdf0e10cSrcweir                 aEdgePolygon.setClosed(true);
6715aaf853bSArmin Le Grand 
672c514522bSArmin Le Grand                 return aEdgePolygon;
673cdf0e10cSrcweir             }
674cdf0e10cSrcweir         }
675cdf0e10cSrcweir 
createAreaGeometryForJoin(const B2DVector & rTangentPrev,const B2DVector & rTangentEdge,const B2DVector & rPerpendPrev,const B2DVector & rPerpendEdge,const B2DPoint & rPoint,double fHalfLineWidth,B2DLineJoin eJoin,double fMiterMinimumAngle)676cdf0e10cSrcweir         B2DPolygon createAreaGeometryForJoin(
677cdf0e10cSrcweir             const B2DVector& rTangentPrev,
678cdf0e10cSrcweir             const B2DVector& rTangentEdge,
679cdf0e10cSrcweir             const B2DVector& rPerpendPrev,
680cdf0e10cSrcweir             const B2DVector& rPerpendEdge,
681cdf0e10cSrcweir             const B2DPoint& rPoint,
682cdf0e10cSrcweir             double fHalfLineWidth,
683cdf0e10cSrcweir             B2DLineJoin eJoin,
684cdf0e10cSrcweir             double fMiterMinimumAngle)
685cdf0e10cSrcweir         {
686cdf0e10cSrcweir             OSL_ENSURE(fHalfLineWidth > 0.0, "createAreaGeometryForJoin: LineWidth too small (!)");
687cdf0e10cSrcweir             OSL_ENSURE(B2DLINEJOIN_NONE != eJoin, "createAreaGeometryForJoin: B2DLINEJOIN_NONE not allowed (!)");
688cdf0e10cSrcweir 
689cdf0e10cSrcweir             // LineJoin from tangent rPerpendPrev to tangent rPerpendEdge in rPoint
690cdf0e10cSrcweir             B2DPolygon aEdgePolygon;
691cdf0e10cSrcweir             const B2DPoint aStartPoint(rPoint + rPerpendPrev);
692cdf0e10cSrcweir             const B2DPoint aEndPoint(rPoint + rPerpendEdge);
693cdf0e10cSrcweir 
694cdf0e10cSrcweir             // test if for Miter, the angle is too small and the fallback
695cdf0e10cSrcweir             // to bevel needs to be used
696cdf0e10cSrcweir             if(B2DLINEJOIN_MITER == eJoin)
697cdf0e10cSrcweir             {
698cdf0e10cSrcweir                 const double fAngle(fabs(rPerpendPrev.angle(rPerpendEdge)));
699cdf0e10cSrcweir 
700cdf0e10cSrcweir                 if((F_PI - fAngle) < fMiterMinimumAngle)
701cdf0e10cSrcweir                 {
702cdf0e10cSrcweir                     // fallback to bevel
703cdf0e10cSrcweir                     eJoin = B2DLINEJOIN_BEVEL;
704cdf0e10cSrcweir                 }
705cdf0e10cSrcweir             }
706cdf0e10cSrcweir 
707cdf0e10cSrcweir             switch(eJoin)
708cdf0e10cSrcweir             {
709cdf0e10cSrcweir                 case B2DLINEJOIN_MITER :
710cdf0e10cSrcweir                 {
711cdf0e10cSrcweir                     aEdgePolygon.append(aEndPoint);
712cdf0e10cSrcweir                     aEdgePolygon.append(rPoint);
713cdf0e10cSrcweir                     aEdgePolygon.append(aStartPoint);
714cdf0e10cSrcweir 
715cdf0e10cSrcweir                     // Look for the cut point between start point along rTangentPrev and
716cdf0e10cSrcweir                     // end point along rTangentEdge. -rTangentEdge should be used, but since
717cdf0e10cSrcweir                     // the cut value is used for interpolating along the first edge, the negation
718cdf0e10cSrcweir                     // is not needed since the same fCut will be found on the first edge.
719cdf0e10cSrcweir                     // If it exists, insert it to complete the mitered fill polygon.
720cdf0e10cSrcweir                     double fCutPos(0.0);
721cdf0e10cSrcweir                     tools::findCut(aStartPoint, rTangentPrev, aEndPoint, rTangentEdge, CUTFLAG_ALL, &fCutPos);
722cdf0e10cSrcweir 
723cdf0e10cSrcweir                     if(0.0 != fCutPos)
724cdf0e10cSrcweir                     {
7251fe6099dSArmin Le Grand                         const B2DPoint aCutPoint(aStartPoint + (rTangentPrev * fCutPos));
726cdf0e10cSrcweir                         aEdgePolygon.append(aCutPoint);
727cdf0e10cSrcweir                     }
728cdf0e10cSrcweir 
729cdf0e10cSrcweir                     break;
730cdf0e10cSrcweir                 }
731cdf0e10cSrcweir                 case B2DLINEJOIN_ROUND :
732cdf0e10cSrcweir                 {
733cdf0e10cSrcweir                     // use tooling to add needed EllipseSegment
734cdf0e10cSrcweir                     double fAngleStart(atan2(rPerpendPrev.getY(), rPerpendPrev.getX()));
735cdf0e10cSrcweir                     double fAngleEnd(atan2(rPerpendEdge.getY(), rPerpendEdge.getX()));
736cdf0e10cSrcweir 
737cdf0e10cSrcweir                     // atan2 results are [-PI .. PI], consolidate to [0.0 .. 2PI]
738cdf0e10cSrcweir                     if(fAngleStart < 0.0)
739cdf0e10cSrcweir                     {
740cdf0e10cSrcweir                         fAngleStart += F_2PI;
741cdf0e10cSrcweir                     }
742cdf0e10cSrcweir 
743cdf0e10cSrcweir                     if(fAngleEnd < 0.0)
744cdf0e10cSrcweir                     {
745cdf0e10cSrcweir                         fAngleEnd += F_2PI;
746cdf0e10cSrcweir                     }
747cdf0e10cSrcweir 
748cdf0e10cSrcweir                     const B2DPolygon aBow(tools::createPolygonFromEllipseSegment(rPoint, fHalfLineWidth, fHalfLineWidth, fAngleStart, fAngleEnd));
749cdf0e10cSrcweir 
750cdf0e10cSrcweir                     if(aBow.count() > 1)
751cdf0e10cSrcweir                     {
752cdf0e10cSrcweir                         // #i101491#
753cdf0e10cSrcweir                         // use the original start/end positions; the ones from bow creation may be numerically
754cdf0e10cSrcweir                         // different due to their different creation. To guarantee good merging quality with edges
755cdf0e10cSrcweir                         // and edge roundings (and to reduce point count)
756cdf0e10cSrcweir                         aEdgePolygon = aBow;
757cdf0e10cSrcweir                         aEdgePolygon.setB2DPoint(0, aStartPoint);
758cdf0e10cSrcweir                         aEdgePolygon.setB2DPoint(aEdgePolygon.count() - 1, aEndPoint);
759cdf0e10cSrcweir                         aEdgePolygon.append(rPoint);
760cdf0e10cSrcweir 
761cdf0e10cSrcweir                         break;
762cdf0e10cSrcweir                     }
763cdf0e10cSrcweir                     else
764cdf0e10cSrcweir                     {
765cdf0e10cSrcweir                         // wanted fall-through to default
766cdf0e10cSrcweir                     }
767cdf0e10cSrcweir                 }
768cdf0e10cSrcweir                 default: // B2DLINEJOIN_BEVEL
769cdf0e10cSrcweir                 {
770cdf0e10cSrcweir                     aEdgePolygon.append(aEndPoint);
771cdf0e10cSrcweir                     aEdgePolygon.append(rPoint);
772cdf0e10cSrcweir                     aEdgePolygon.append(aStartPoint);
773cdf0e10cSrcweir 
774cdf0e10cSrcweir                     break;
775cdf0e10cSrcweir                 }
776cdf0e10cSrcweir             }
777cdf0e10cSrcweir 
778cdf0e10cSrcweir             // create last polygon part for edge
779cdf0e10cSrcweir             aEdgePolygon.setClosed(true);
780cdf0e10cSrcweir 
781cdf0e10cSrcweir             return aEdgePolygon;
782cdf0e10cSrcweir         }
783*d0f02d50Smseidel     } // end of anonymous namespace
784cdf0e10cSrcweir 
785cdf0e10cSrcweir     namespace tools
786cdf0e10cSrcweir     {
createAreaGeometry(const B2DPolygon & rCandidate,double fHalfLineWidth,B2DLineJoin eJoin,com::sun::star::drawing::LineCap eCap,double fMaxAllowedAngle,double fMaxPartOfEdge,double fMiterMinimumAngle)787cdf0e10cSrcweir         B2DPolyPolygon createAreaGeometry(
788cdf0e10cSrcweir             const B2DPolygon& rCandidate,
789cdf0e10cSrcweir             double fHalfLineWidth,
790cdf0e10cSrcweir             B2DLineJoin eJoin,
7915aaf853bSArmin Le Grand             com::sun::star::drawing::LineCap eCap,
792cdf0e10cSrcweir             double fMaxAllowedAngle,
793cdf0e10cSrcweir             double fMaxPartOfEdge,
794cdf0e10cSrcweir             double fMiterMinimumAngle)
795cdf0e10cSrcweir         {
796cdf0e10cSrcweir             if(fMaxAllowedAngle > F_PI2)
797cdf0e10cSrcweir             {
798cdf0e10cSrcweir                 fMaxAllowedAngle = F_PI2;
799cdf0e10cSrcweir             }
800cdf0e10cSrcweir             else if(fMaxAllowedAngle < 0.01 * F_PI2)
801cdf0e10cSrcweir             {
802cdf0e10cSrcweir                 fMaxAllowedAngle = 0.01 * F_PI2;
803cdf0e10cSrcweir             }
804cdf0e10cSrcweir 
805cdf0e10cSrcweir             if(fMaxPartOfEdge > 1.0)
806cdf0e10cSrcweir             {
807cdf0e10cSrcweir                 fMaxPartOfEdge = 1.0;
808cdf0e10cSrcweir             }
809cdf0e10cSrcweir             else if(fMaxPartOfEdge < 0.01)
810cdf0e10cSrcweir             {
811cdf0e10cSrcweir                 fMaxPartOfEdge = 0.01;
812cdf0e10cSrcweir             }
813cdf0e10cSrcweir 
814cdf0e10cSrcweir             if(fMiterMinimumAngle > F_PI)
815cdf0e10cSrcweir             {
816cdf0e10cSrcweir                 fMiterMinimumAngle = F_PI;
817cdf0e10cSrcweir             }
818cdf0e10cSrcweir             else if(fMiterMinimumAngle < 0.01 * F_PI)
819cdf0e10cSrcweir             {
820cdf0e10cSrcweir                 fMiterMinimumAngle = 0.01 * F_PI;
821cdf0e10cSrcweir             }
822cdf0e10cSrcweir 
823cdf0e10cSrcweir             B2DPolygon aCandidate(rCandidate);
824cdf0e10cSrcweir             const double fMaxCos(cos(fMaxAllowedAngle));
825cdf0e10cSrcweir 
826cdf0e10cSrcweir             aCandidate.removeDoublePoints();
827cdf0e10cSrcweir             aCandidate = subdivideToSimple(aCandidate, fMaxCos * fMaxCos, fMaxPartOfEdge * fMaxPartOfEdge);
828cdf0e10cSrcweir 
829cdf0e10cSrcweir             const sal_uInt32 nPointCount(aCandidate.count());
830cdf0e10cSrcweir 
831cdf0e10cSrcweir             if(nPointCount)
832cdf0e10cSrcweir             {
833cdf0e10cSrcweir                 B2DPolyPolygon aRetval;
834cdf0e10cSrcweir                 const bool bEventuallyCreateLineJoin(B2DLINEJOIN_NONE != eJoin);
835cdf0e10cSrcweir                 const bool bIsClosed(aCandidate.isClosed());
836cdf0e10cSrcweir                 const sal_uInt32 nEdgeCount(bIsClosed ? nPointCount : nPointCount - 1);
8375aaf853bSArmin Le Grand                 const bool bLineCap(!bIsClosed && com::sun::star::drawing::LineCap_BUTT != eCap);
838cdf0e10cSrcweir 
839cdf0e10cSrcweir                 if(nEdgeCount)
840cdf0e10cSrcweir                 {
841cdf0e10cSrcweir                     B2DCubicBezier aEdge;
842cdf0e10cSrcweir                     B2DCubicBezier aPrev;
843cdf0e10cSrcweir 
844cdf0e10cSrcweir                     // prepare edge
845cdf0e10cSrcweir                     aEdge.setStartPoint(aCandidate.getB2DPoint(0));
846cdf0e10cSrcweir 
847cdf0e10cSrcweir                     if(bIsClosed && bEventuallyCreateLineJoin)
848cdf0e10cSrcweir                     {
849cdf0e10cSrcweir                         // prepare previous edge
850cdf0e10cSrcweir                         const sal_uInt32 nPrevIndex(nPointCount - 1);
851cdf0e10cSrcweir                         aPrev.setStartPoint(aCandidate.getB2DPoint(nPrevIndex));
852cdf0e10cSrcweir                         aPrev.setControlPointA(aCandidate.getNextControlPoint(nPrevIndex));
853cdf0e10cSrcweir                         aPrev.setControlPointB(aCandidate.getPrevControlPoint(0));
854cdf0e10cSrcweir                         aPrev.setEndPoint(aEdge.getStartPoint());
855cdf0e10cSrcweir                     }
856cdf0e10cSrcweir 
857cdf0e10cSrcweir                     for(sal_uInt32 a(0); a < nEdgeCount; a++)
858cdf0e10cSrcweir                     {
859cdf0e10cSrcweir                         // fill current Edge
860cdf0e10cSrcweir                         const sal_uInt32 nNextIndex((a + 1) % nPointCount);
861cdf0e10cSrcweir                         aEdge.setControlPointA(aCandidate.getNextControlPoint(a));
862cdf0e10cSrcweir                         aEdge.setControlPointB(aCandidate.getPrevControlPoint(nNextIndex));
863cdf0e10cSrcweir                         aEdge.setEndPoint(aCandidate.getB2DPoint(nNextIndex));
864cdf0e10cSrcweir 
865cdf0e10cSrcweir                         // check and create linejoin
866cdf0e10cSrcweir                         if(bEventuallyCreateLineJoin && (bIsClosed || 0 != a))
867cdf0e10cSrcweir                         {
8685aaf853bSArmin Le Grand                             B2DVector aTangentPrev(aPrev.getTangent(1.0)); aTangentPrev.normalize();
8695aaf853bSArmin Le Grand                             B2DVector aTangentEdge(aEdge.getTangent(0.0)); aTangentEdge.normalize();
870cdf0e10cSrcweir                             B2VectorOrientation aOrientation(getOrientation(aTangentPrev, aTangentEdge));
871cdf0e10cSrcweir 
872cdf0e10cSrcweir                             if(ORIENTATION_NEUTRAL == aOrientation)
873cdf0e10cSrcweir                             {
87430acf5e8Spfg                                    // they are parallel or empty; if they are both not zero and point
875cdf0e10cSrcweir                                    // in opposite direction, a half-circle is needed
876cdf0e10cSrcweir                                    if(!aTangentPrev.equalZero() && !aTangentEdge.equalZero())
877cdf0e10cSrcweir                                    {
878cdf0e10cSrcweir                                     const double fAngle(fabs(aTangentPrev.angle(aTangentEdge)));
879cdf0e10cSrcweir 
880cdf0e10cSrcweir                                     if(fTools::equal(fAngle, F_PI))
881cdf0e10cSrcweir                                     {
882cdf0e10cSrcweir                                         // for half-circle production, fallback to positive
883cdf0e10cSrcweir                                         // orientation
884cdf0e10cSrcweir                                         aOrientation = ORIENTATION_POSITIVE;
885cdf0e10cSrcweir                                     }
886cdf0e10cSrcweir                                 }
887cdf0e10cSrcweir                             }
888cdf0e10cSrcweir 
889cdf0e10cSrcweir                             if(ORIENTATION_POSITIVE == aOrientation)
890cdf0e10cSrcweir                             {
8915aaf853bSArmin Le Grand                                 const B2DVector aPerpendPrev(getPerpendicular(aTangentPrev) * -fHalfLineWidth);
8925aaf853bSArmin Le Grand                                 const B2DVector aPerpendEdge(getPerpendicular(aTangentEdge) * -fHalfLineWidth);
893cdf0e10cSrcweir 
8945aaf853bSArmin Le Grand                                 aRetval.append(
8955aaf853bSArmin Le Grand                                     createAreaGeometryForJoin(
8965aaf853bSArmin Le Grand                                         aTangentPrev,
8975aaf853bSArmin Le Grand                                         aTangentEdge,
8985aaf853bSArmin Le Grand                                         aPerpendPrev,
8995aaf853bSArmin Le Grand                                         aPerpendEdge,
9005aaf853bSArmin Le Grand                                         aEdge.getStartPoint(),
9015aaf853bSArmin Le Grand                                         fHalfLineWidth,
9025aaf853bSArmin Le Grand                                         eJoin,
9035aaf853bSArmin Le Grand                                         fMiterMinimumAngle));
904cdf0e10cSrcweir                             }
905cdf0e10cSrcweir                             else if(ORIENTATION_NEGATIVE == aOrientation)
906cdf0e10cSrcweir                             {
9075aaf853bSArmin Le Grand                                 const B2DVector aPerpendPrev(getPerpendicular(aTangentPrev) * fHalfLineWidth);
9085aaf853bSArmin Le Grand                                 const B2DVector aPerpendEdge(getPerpendicular(aTangentEdge) * fHalfLineWidth);
909cdf0e10cSrcweir 
9105aaf853bSArmin Le Grand                                 aRetval.append(
9115aaf853bSArmin Le Grand                                     createAreaGeometryForJoin(
9125aaf853bSArmin Le Grand                                         aTangentEdge,
9135aaf853bSArmin Le Grand                                         aTangentPrev,
9145aaf853bSArmin Le Grand                                         aPerpendEdge,
9155aaf853bSArmin Le Grand                                         aPerpendPrev,
9165aaf853bSArmin Le Grand                                         aEdge.getStartPoint(),
9175aaf853bSArmin Le Grand                                         fHalfLineWidth,
9185aaf853bSArmin Le Grand                                         eJoin,
9195aaf853bSArmin Le Grand                                         fMiterMinimumAngle));
920cdf0e10cSrcweir                             }
921cdf0e10cSrcweir                         }
922cdf0e10cSrcweir 
923cdf0e10cSrcweir                         // create geometry for edge
9245aaf853bSArmin Le Grand                         const bool bLast(a + 1 == nEdgeCount);
9255aaf853bSArmin Le Grand 
9265aaf853bSArmin Le Grand                         if(bLineCap)
9275aaf853bSArmin Le Grand                         {
9285aaf853bSArmin Le Grand                             const bool bFirst(!a);
9295aaf853bSArmin Le Grand 
9305aaf853bSArmin Le Grand                             aRetval.append(
9315aaf853bSArmin Le Grand                                 createAreaGeometryForEdge(
9325aaf853bSArmin Le Grand                                     aEdge,
9335aaf853bSArmin Le Grand                                     fHalfLineWidth,
9345aaf853bSArmin Le Grand                                     bFirst && com::sun::star::drawing::LineCap_ROUND == eCap,
9355aaf853bSArmin Le Grand                                     bLast && com::sun::star::drawing::LineCap_ROUND == eCap,
9365aaf853bSArmin Le Grand                                     bFirst && com::sun::star::drawing::LineCap_SQUARE == eCap,
9375aaf853bSArmin Le Grand                                     bLast && com::sun::star::drawing::LineCap_SQUARE == eCap));
9385aaf853bSArmin Le Grand                         }
9395aaf853bSArmin Le Grand                         else
9405aaf853bSArmin Le Grand                         {
9415aaf853bSArmin Le Grand                             aRetval.append(
9425aaf853bSArmin Le Grand                                 createAreaGeometryForEdge(
9435aaf853bSArmin Le Grand                                     aEdge,
9445aaf853bSArmin Le Grand                                     fHalfLineWidth,
9455aaf853bSArmin Le Grand                                     false,
9465aaf853bSArmin Le Grand                                     false,
9475aaf853bSArmin Le Grand                                     false,
9485aaf853bSArmin Le Grand                                     false));
9495aaf853bSArmin Le Grand                         }
950cdf0e10cSrcweir 
951cdf0e10cSrcweir                         // prepare next step
9525aaf853bSArmin Le Grand                         if(!bLast)
9535aaf853bSArmin Le Grand                         {
954cdf0e10cSrcweir                             if(bEventuallyCreateLineJoin)
955cdf0e10cSrcweir                             {
956cdf0e10cSrcweir                                 aPrev = aEdge;
957cdf0e10cSrcweir                             }
958cdf0e10cSrcweir 
959cdf0e10cSrcweir                             aEdge.setStartPoint(aEdge.getEndPoint());
960cdf0e10cSrcweir                         }
961cdf0e10cSrcweir                     }
9625aaf853bSArmin Le Grand                 }
963cdf0e10cSrcweir 
964cdf0e10cSrcweir                 return aRetval;
965cdf0e10cSrcweir             }
966cdf0e10cSrcweir             else
967cdf0e10cSrcweir             {
968cdf0e10cSrcweir                 return B2DPolyPolygon(rCandidate);
969cdf0e10cSrcweir             }
970cdf0e10cSrcweir         }
971cdf0e10cSrcweir     } // end of namespace tools
972cdf0e10cSrcweir } // end of namespace basegfx
973cdf0e10cSrcweir 
974*d0f02d50Smseidel /* vim: set noet sw=4 ts=4: */
975