merge the formfield patch from ooo-build
[ooovba.git] / basegfx / source / polygon / b2dlinegeometry.cxx
blobcb147663cf1e3f21f41f7b3231bd97968da0cf23
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: b2dlinegeometry.cxx,v $
10 * $Revision: 1.7 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_basegfx.hxx"
33 #include <cstdio>
34 #include <osl/diagnose.h>
35 #include <basegfx/polygon/b2dlinegeometry.hxx>
36 #include <basegfx/point/b2dpoint.hxx>
37 #include <basegfx/vector/b2dvector.hxx>
38 #include <basegfx/polygon/b2dpolygontools.hxx>
39 #include <basegfx/polygon/b2dpolypolygontools.hxx>
40 #include <basegfx/range/b2drange.hxx>
41 #include <basegfx/matrix/b2dhommatrix.hxx>
42 #include <basegfx/curve/b2dcubicbezier.hxx>
44 //////////////////////////////////////////////////////////////////////////////
46 namespace basegfx
48 namespace tools
50 B2DPolyPolygon createAreaGeometryForLineStartEnd(
51 const B2DPolygon& rCandidate,
52 const B2DPolyPolygon& rArrow,
53 bool bStart,
54 double fWidth,
55 double fCandidateLength,
56 double fDockingPosition, // 0->top, 1->bottom
57 double* pConsumedLength)
59 B2DPolyPolygon aRetval;
60 OSL_ENSURE(rCandidate.count() > 1L, "createAreaGeometryForLineStartEnd: Line polygon has too less points (!)");
61 OSL_ENSURE(rArrow.count() > 0L, "createAreaGeometryForLineStartEnd: Empty arrow PolyPolygon (!)");
62 OSL_ENSURE(fWidth > 0.0, "createAreaGeometryForLineStartEnd: Width too small (!)");
63 OSL_ENSURE(fDockingPosition >= 0.0 && fDockingPosition <= 1.0,
64 "createAreaGeometryForLineStartEnd: fDockingPosition out of range [0.0 .. 1.0] (!)");
66 if(fWidth < 0.0)
68 fWidth = -fWidth;
71 if(rCandidate.count() > 1 && rArrow.count() && !fTools::equalZero(fWidth))
73 if(fDockingPosition < 0.0)
75 fDockingPosition = 0.0;
77 else if(fDockingPosition > 1.0)
79 fDockingPosition = 1.0;
82 // init return value from arrow
83 aRetval.append(rArrow);
85 // get size of the arrow
86 const B2DRange aArrowSize(getRange(rArrow));
88 // build ArrowTransform
89 B2DHomMatrix aArrowTransform;
91 // center in X, align with axis in Y
92 aArrowTransform.translate(-aArrowSize.getCenter().getX(), -aArrowSize.getMinimum().getY());
94 // scale to target size
95 const double fArrowScale(fWidth / (aArrowSize.getRange().getX()));
96 aArrowTransform.scale(fArrowScale, fArrowScale);
98 // get arrow size in Y
99 B2DPoint aUpperCenter(aArrowSize.getCenter().getX(), aArrowSize.getMaximum().getY());
100 aUpperCenter *= aArrowTransform;
101 const double fArrowYLength(B2DVector(aUpperCenter).getLength());
103 // move arrow to have docking position centered
104 aArrowTransform.translate(0.0, -fArrowYLength * fDockingPosition);
106 // prepare polygon length
107 if(fTools::equalZero(fCandidateLength))
109 fCandidateLength = getLength(rCandidate);
112 // get the polygon vector we want to plant this arrow on
113 const double fConsumedLength(fArrowYLength * (1.0 - fDockingPosition));
114 const B2DVector aHead(rCandidate.getB2DPoint((bStart) ? 0L : rCandidate.count() - 1L));
115 const B2DVector aTail(getPositionAbsolute(rCandidate,
116 (bStart) ? fConsumedLength : fCandidateLength - fConsumedLength, fCandidateLength));
118 // from that vector, take the needed rotation and add rotate for arrow to transformation
119 const B2DVector aTargetDirection(aHead - aTail);
120 const double fRotation(atan2(aTargetDirection.getY(), aTargetDirection.getX()) + (90.0 * F_PI180));
122 // rotate around docking position
123 aArrowTransform.rotate(fRotation);
125 // move arrow docking position to polygon head
126 aArrowTransform.translate(aHead.getX(), aHead.getY());
128 // transform retval and close
129 aRetval.transform(aArrowTransform);
130 aRetval.setClosed(true);
132 // if pConsumedLength is asked for, fill it
133 if(pConsumedLength)
135 *pConsumedLength = fConsumedLength;
139 return aRetval;
141 } // end of namespace tools
142 } // end of namespace basegfx
144 //////////////////////////////////////////////////////////////////////////////
146 namespace basegfx
148 // anonymus namespace for local helpers
149 namespace
151 bool impIsSimpleEdge(const B2DCubicBezier& rCandidate, double fMaxCosQuad, double fMaxPartOfEdgeQuad)
153 // isBezier() is true, already tested by caller
154 const B2DVector aEdge(rCandidate.getEndPoint() - rCandidate.getStartPoint());
156 if(aEdge.equalZero())
158 // start and end point the same, but control vectors used -> baloon curve loop
159 // is not a simple edge
160 return false;
163 // get tangentA and scalar with edge
164 const B2DVector aTangentA(rCandidate.getTangent(0.0));
165 const double fScalarAE(aEdge.scalar(aTangentA));
167 if(fTools::lessOrEqual(fScalarAE, 0.0))
169 // angle between TangentA and Edge is bigger or equal 90 degrees
170 return false;
173 // get self-scalars for E and A
174 const double fScalarE(aEdge.scalar(aEdge));
175 const double fScalarA(aTangentA.scalar(aTangentA));
176 const double fLengthCompareE(fScalarE * fMaxPartOfEdgeQuad);
178 if(fTools::moreOrEqual(fScalarA, fLengthCompareE))
180 // length of TangentA is more than fMaxPartOfEdge of length of edge
181 return false;
184 if(fTools::lessOrEqual(fScalarAE * fScalarAE, fScalarA * fScalarE * fMaxCosQuad))
186 // angle between TangentA and Edge is bigger or equal angle defined by fMaxCos
187 return false;
190 // get tangentB and scalar with edge
191 const B2DVector aTangentB(rCandidate.getTangent(1.0));
192 const double fScalarBE(aEdge.scalar(aTangentB));
194 if(fTools::lessOrEqual(fScalarBE, 0.0))
196 // angle between TangentB and Edge is bigger or equal 90 degrees
197 return false;
200 // get self-scalar for B
201 const double fScalarB(aTangentB.scalar(aTangentB));
203 if(fTools::moreOrEqual(fScalarB, fLengthCompareE))
205 // length of TangentB is more than fMaxPartOfEdge of length of edge
206 return false;
209 if(fTools::lessOrEqual(fScalarBE * fScalarBE, fScalarB * fScalarE * fMaxCosQuad))
211 // angle between TangentB and Edge is bigger or equal defined by fMaxCos
212 return false;
215 return true;
218 void impSubdivideToSimple(const B2DCubicBezier& rCandidate, B2DPolygon& rTarget, double fMaxCosQuad, double fMaxPartOfEdgeQuad, sal_uInt32 nMaxRecursionDepth)
220 if(!nMaxRecursionDepth || impIsSimpleEdge(rCandidate, fMaxCosQuad, fMaxPartOfEdgeQuad))
222 rTarget.appendBezierSegment(rCandidate.getControlPointA(), rCandidate.getControlPointB(), rCandidate.getEndPoint());
224 else
226 B2DCubicBezier aLeft, aRight;
227 rCandidate.split(0.5, &aLeft, &aRight);
229 impSubdivideToSimple(aLeft, rTarget, fMaxCosQuad, fMaxPartOfEdgeQuad, nMaxRecursionDepth - 1);
230 impSubdivideToSimple(aRight, rTarget, fMaxCosQuad, fMaxPartOfEdgeQuad, nMaxRecursionDepth - 1);
234 B2DPolygon subdivideToSimple(const B2DPolygon& rCandidate, double fMaxCosQuad, double fMaxPartOfEdgeQuad)
236 const sal_uInt32 nPointCount(rCandidate.count());
238 if(rCandidate.areControlPointsUsed() && nPointCount)
240 const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
241 B2DPolygon aRetval;
242 B2DCubicBezier aEdge;
244 // prepare edge for loop
245 aEdge.setStartPoint(rCandidate.getB2DPoint(0));
246 aRetval.append(aEdge.getStartPoint());
248 for(sal_uInt32 a(0); a < nEdgeCount; a++)
250 // fill B2DCubicBezier
251 const sal_uInt32 nNextIndex((a + 1) % nPointCount);
252 aEdge.setControlPointA(rCandidate.getNextControlPoint(a));
253 aEdge.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex));
254 aEdge.setEndPoint(rCandidate.getB2DPoint(nNextIndex));
256 // get rid of unnecessary bezier segments
257 aEdge.testAndSolveTrivialBezier();
259 if(aEdge.isBezier())
261 // before splitting recursively with internal simple criteria, use
262 // ExtremumPosFinder to remove those
263 ::std::vector< double > aExtremumPositions;
265 aExtremumPositions.reserve(4);
266 aEdge.getAllExtremumPositions(aExtremumPositions);
268 const sal_uInt32 nCount(aExtremumPositions.size());
270 if(nCount)
272 if(nCount > 1)
274 // create order from left to right
275 ::std::sort(aExtremumPositions.begin(), aExtremumPositions.end());
278 for(sal_uInt32 b(0); b < nCount;)
280 // split aEdge at next split pos
281 B2DCubicBezier aLeft;
282 const double fSplitPos(aExtremumPositions[b++]);
284 aEdge.split(fSplitPos, &aLeft, &aEdge);
285 aLeft.testAndSolveTrivialBezier();
287 // consume left part
288 if(aLeft.isBezier())
290 impSubdivideToSimple(aLeft, aRetval, fMaxCosQuad, fMaxPartOfEdgeQuad, 6);
292 else
294 aRetval.append(aLeft.getEndPoint());
297 if(b < nCount)
299 // correct the remaining split positions to fit to shortened aEdge
300 const double fScaleFactor(1.0 / (1.0 - fSplitPos));
302 for(sal_uInt32 c(b); c < nCount; c++)
304 aExtremumPositions[c] = (aExtremumPositions[c] - fSplitPos) * fScaleFactor;
309 // test the shortened rest of aEdge
310 aEdge.testAndSolveTrivialBezier();
312 // consume right part
313 if(aEdge.isBezier())
315 impSubdivideToSimple(aEdge, aRetval, fMaxCosQuad, fMaxPartOfEdgeQuad, 6);
317 else
319 aRetval.append(aEdge.getEndPoint());
322 else
324 impSubdivideToSimple(aEdge, aRetval, fMaxCosQuad, fMaxPartOfEdgeQuad, 6);
327 else
329 // straight edge, add point
330 aRetval.append(aEdge.getEndPoint());
333 // prepare edge for next step
334 aEdge.setStartPoint(aEdge.getEndPoint());
337 // copy closed flag and check for double points
338 aRetval.setClosed(rCandidate.isClosed());
339 aRetval.removeDoublePoints();
341 return aRetval;
343 else
345 return rCandidate;
349 B2DPolygon createAreaGeometryForEdge(const B2DCubicBezier& rEdge, double fHalfLineWidth)
351 // create polygon for edge
352 // Unfortunately, while it would be geometrically correct to not add
353 // the in-between points EdgeEnd and EdgeStart, it leads to rounding
354 // errors when converting to integer polygon coordinates for painting
355 if(rEdge.isBezier())
357 // prepare target and data common for upper and lower
358 B2DPolygon aBezierPolygon;
359 const B2DVector aPureEdgeVector(rEdge.getEndPoint() - rEdge.getStartPoint());
360 const double fEdgeLength(aPureEdgeVector.getLength());
361 const bool bIsEdgeLengthZero(fTools::equalZero(fEdgeLength));
362 const B2DVector aTangentA(rEdge.getTangent(0.0));
363 const B2DVector aTangentB(rEdge.getTangent(1.0));
365 // create upper edge.
367 // create displacement vectors and check if they cut
368 const B2DVector aPerpendStart(getNormalizedPerpendicular(aTangentA) * -fHalfLineWidth);
369 const B2DVector aPerpendEnd(getNormalizedPerpendicular(aTangentB) * -fHalfLineWidth);
370 double fCut(0.0);
371 const tools::CutFlagValue aCut(tools::findCut(
372 rEdge.getStartPoint(), aPerpendStart,
373 rEdge.getEndPoint(), aPerpendEnd,
374 CUTFLAG_ALL, &fCut));
376 if(CUTFLAG_NONE != aCut)
378 // calculate cut point and add
379 const B2DPoint aCutPoint(rEdge.getStartPoint() + (aPerpendStart * fCut));
380 aBezierPolygon.append(aCutPoint);
382 else
384 // create scaled bezier segment
385 const B2DPoint aStart(rEdge.getStartPoint() + aPerpendStart);
386 const B2DPoint aEnd(rEdge.getEndPoint() + aPerpendEnd);
387 const B2DVector aEdge(aEnd - aStart);
388 const double fLength(aEdge.getLength());
389 const double fScale(bIsEdgeLengthZero ? 1.0 : fLength / fEdgeLength);
390 const B2DVector fRelNext(rEdge.getControlPointA() - rEdge.getStartPoint());
391 const B2DVector fRelPrev(rEdge.getControlPointB() - rEdge.getEndPoint());
393 aBezierPolygon.append(aStart);
394 aBezierPolygon.appendBezierSegment(aStart + (fRelNext * fScale), aEnd + (fRelPrev * fScale), aEnd);
398 // append original in-between point
399 aBezierPolygon.append(rEdge.getEndPoint());
401 // create lower edge.
403 // create displacement vectors and check if they cut
404 const B2DVector aPerpendStart(getNormalizedPerpendicular(aTangentA) * fHalfLineWidth);
405 const B2DVector aPerpendEnd(getNormalizedPerpendicular(aTangentB) * fHalfLineWidth);
406 double fCut(0.0);
407 const tools::CutFlagValue aCut(tools::findCut(
408 rEdge.getEndPoint(), aPerpendEnd,
409 rEdge.getStartPoint(), aPerpendStart,
410 CUTFLAG_ALL, &fCut));
412 if(CUTFLAG_NONE != aCut)
414 // calculate cut point and add
415 const B2DPoint aCutPoint(rEdge.getEndPoint() + (aPerpendEnd * fCut));
416 aBezierPolygon.append(aCutPoint);
418 else
420 // create scaled bezier segment
421 const B2DPoint aStart(rEdge.getEndPoint() + aPerpendEnd);
422 const B2DPoint aEnd(rEdge.getStartPoint() + aPerpendStart);
423 const B2DVector aEdge(aEnd - aStart);
424 const double fLength(aEdge.getLength());
425 const double fScale(bIsEdgeLengthZero ? 1.0 : fLength / fEdgeLength);
426 const B2DVector fRelNext(rEdge.getControlPointB() - rEdge.getEndPoint());
427 const B2DVector fRelPrev(rEdge.getControlPointA() - rEdge.getStartPoint());
429 aBezierPolygon.append(aStart);
430 aBezierPolygon.appendBezierSegment(aStart + (fRelNext * fScale), aEnd + (fRelPrev * fScale), aEnd);
434 // append original in-between point
435 aBezierPolygon.append(rEdge.getStartPoint());
437 // close and return
438 aBezierPolygon.setClosed(true);
439 return aBezierPolygon;
441 else
443 // #i101491# emulate rEdge.getTangent call which applies a factor of 0.3 to the
444 // full-length edge vector to have numerically exactly the same results as in the
445 // createAreaGeometryForJoin implementation
446 const B2DVector aEdgeTangent((rEdge.getEndPoint() - rEdge.getStartPoint()) * 0.3);
447 const B2DVector aPerpendEdgeVector(getNormalizedPerpendicular(aEdgeTangent) * fHalfLineWidth);
448 B2DPolygon aEdgePolygon;
450 // create upper edge
451 aEdgePolygon.append(rEdge.getStartPoint() - aPerpendEdgeVector);
452 aEdgePolygon.append(rEdge.getEndPoint() - aPerpendEdgeVector);
454 // append original in-between point
455 aEdgePolygon.append(rEdge.getEndPoint());
457 // create lower edge
458 aEdgePolygon.append(rEdge.getEndPoint() + aPerpendEdgeVector);
459 aEdgePolygon.append(rEdge.getStartPoint() + aPerpendEdgeVector);
461 // append original in-between point
462 aEdgePolygon.append(rEdge.getStartPoint());
464 // close and return
465 aEdgePolygon.setClosed(true);
466 return aEdgePolygon;
470 B2DPolygon createAreaGeometryForJoin(
471 const B2DVector& rTangentPrev,
472 const B2DVector& rTangentEdge,
473 const B2DVector& rPerpendPrev,
474 const B2DVector& rPerpendEdge,
475 const B2DPoint& rPoint,
476 double fHalfLineWidth,
477 B2DLineJoin eJoin,
478 double fMiterMinimumAngle)
480 OSL_ENSURE(fHalfLineWidth > 0.0, "createAreaGeometryForJoin: LineWidth too small (!)");
481 OSL_ENSURE(B2DLINEJOIN_NONE != eJoin, "createAreaGeometryForJoin: B2DLINEJOIN_NONE not allowed (!)");
483 // LineJoin from tangent rPerpendPrev to tangent rPerpendEdge in rPoint
484 B2DPolygon aEdgePolygon;
485 const B2DPoint aStartPoint(rPoint + rPerpendPrev);
486 const B2DPoint aEndPoint(rPoint + rPerpendEdge);
488 // test if for Miter, the angle is too small and the fallback
489 // to bevel needs to be used
490 if(B2DLINEJOIN_MITER == eJoin)
492 const double fAngle(fabs(rPerpendPrev.angle(rPerpendEdge)));
494 if((F_PI - fAngle) < fMiterMinimumAngle)
496 // fallback to bevel
497 eJoin = B2DLINEJOIN_BEVEL;
501 switch(eJoin)
503 case B2DLINEJOIN_MITER :
505 aEdgePolygon.append(aEndPoint);
506 aEdgePolygon.append(rPoint);
507 aEdgePolygon.append(aStartPoint);
509 // Look for the cut point between start point along rTangentPrev and
510 // end point along rTangentEdge. -rTangentEdge should be used, but since
511 // the cut value is used for interpolating along the first edge, the negation
512 // is not needed since the same fCut will be found on the first edge.
513 // If it exists, insert it to complete the mitered fill polygon.
514 double fCutPos(0.0);
515 tools::findCut(aStartPoint, rTangentPrev, aEndPoint, rTangentEdge, CUTFLAG_ALL, &fCutPos);
517 if(0.0 != fCutPos)
519 const B2DPoint aCutPoint(interpolate(aStartPoint, aStartPoint + rTangentPrev, fCutPos));
520 aEdgePolygon.append(aCutPoint);
523 break;
525 case B2DLINEJOIN_ROUND :
527 // use tooling to add needed EllipseSegment
528 double fAngleStart(atan2(rPerpendPrev.getY(), rPerpendPrev.getX()));
529 double fAngleEnd(atan2(rPerpendEdge.getY(), rPerpendEdge.getX()));
531 // atan2 results are [-PI .. PI], consolidate to [0.0 .. 2PI]
532 if(fAngleStart < 0.0)
534 fAngleStart += F_2PI;
537 if(fAngleEnd < 0.0)
539 fAngleEnd += F_2PI;
542 const B2DPolygon aBow(tools::createPolygonFromEllipseSegment(rPoint, fHalfLineWidth, fHalfLineWidth, fAngleStart, fAngleEnd));
544 if(aBow.count() > 1)
546 // #i101491#
547 // use the original start/end positions; the ones from bow creation may be numerically
548 // different due to their different creation. To guarantee good merging quality with edges
549 // and edge roundings (and to reduce point count)
550 aEdgePolygon = aBow;
551 aEdgePolygon.setB2DPoint(0, aStartPoint);
552 aEdgePolygon.setB2DPoint(aEdgePolygon.count() - 1, aEndPoint);
553 aEdgePolygon.append(rPoint);
555 break;
557 else
559 // wanted fall-through to default
562 default: // B2DLINEJOIN_BEVEL
564 aEdgePolygon.append(aEndPoint);
565 aEdgePolygon.append(rPoint);
566 aEdgePolygon.append(aStartPoint);
568 break;
572 // create last polygon part for edge
573 aEdgePolygon.setClosed(true);
575 return aEdgePolygon;
577 } // end of anonymus namespace
579 namespace tools
581 B2DPolyPolygon createAreaGeometry(
582 const B2DPolygon& rCandidate,
583 double fHalfLineWidth,
584 B2DLineJoin eJoin,
585 double fMaxAllowedAngle,
586 double fMaxPartOfEdge,
587 double fMiterMinimumAngle)
589 if(fMaxAllowedAngle > F_PI2)
591 fMaxAllowedAngle = F_PI2;
593 else if(fMaxAllowedAngle < 0.01 * F_PI2)
595 fMaxAllowedAngle = 0.01 * F_PI2;
598 if(fMaxPartOfEdge > 1.0)
600 fMaxPartOfEdge = 1.0;
602 else if(fMaxPartOfEdge < 0.01)
604 fMaxPartOfEdge = 0.01;
607 if(fMiterMinimumAngle > F_PI)
609 fMiterMinimumAngle = F_PI;
611 else if(fMiterMinimumAngle < 0.01 * F_PI)
613 fMiterMinimumAngle = 0.01 * F_PI;
616 B2DPolygon aCandidate(rCandidate);
617 const double fMaxCos(cos(fMaxAllowedAngle));
619 aCandidate.removeDoublePoints();
620 aCandidate = subdivideToSimple(aCandidate, fMaxCos * fMaxCos, fMaxPartOfEdge * fMaxPartOfEdge);
622 const sal_uInt32 nPointCount(aCandidate.count());
624 if(nPointCount)
626 B2DPolyPolygon aRetval;
627 const bool bEventuallyCreateLineJoin(B2DLINEJOIN_NONE != eJoin);
628 const bool bIsClosed(aCandidate.isClosed());
629 const sal_uInt32 nEdgeCount(bIsClosed ? nPointCount : nPointCount - 1);
631 if(nEdgeCount)
633 B2DCubicBezier aEdge;
634 B2DCubicBezier aPrev;
636 // prepare edge
637 aEdge.setStartPoint(aCandidate.getB2DPoint(0));
639 if(bIsClosed && bEventuallyCreateLineJoin)
641 // prepare previous edge
642 const sal_uInt32 nPrevIndex(nPointCount - 1);
643 aPrev.setStartPoint(aCandidate.getB2DPoint(nPrevIndex));
644 aPrev.setControlPointA(aCandidate.getNextControlPoint(nPrevIndex));
645 aPrev.setControlPointB(aCandidate.getPrevControlPoint(0));
646 aPrev.setEndPoint(aEdge.getStartPoint());
649 for(sal_uInt32 a(0); a < nEdgeCount; a++)
651 // fill current Edge
652 const sal_uInt32 nNextIndex((a + 1) % nPointCount);
653 aEdge.setControlPointA(aCandidate.getNextControlPoint(a));
654 aEdge.setControlPointB(aCandidate.getPrevControlPoint(nNextIndex));
655 aEdge.setEndPoint(aCandidate.getB2DPoint(nNextIndex));
657 // check and create linejoin
658 if(bEventuallyCreateLineJoin && (bIsClosed || 0 != a))
660 const B2DVector aTangentPrev(aPrev.getTangent(1.0));
661 const B2DVector aTangentEdge(aEdge.getTangent(0.0));
662 B2VectorOrientation aOrientation(getOrientation(aTangentPrev, aTangentEdge));
664 if(ORIENTATION_NEUTRAL == aOrientation)
666 // they are parallell or empty; if they are both not zero and point
667 // in opposite direction, a half-circle is needed
668 if(!aTangentPrev.equalZero() && !aTangentEdge.equalZero())
670 const double fAngle(fabs(aTangentPrev.angle(aTangentEdge)));
672 if(fTools::equal(fAngle, F_PI))
674 // for half-circle production, fallback to positive
675 // orientation
676 aOrientation = ORIENTATION_POSITIVE;
681 if(ORIENTATION_POSITIVE == aOrientation)
683 const B2DVector aPerpendPrev(getNormalizedPerpendicular(aTangentPrev) * -fHalfLineWidth);
684 const B2DVector aPerpendEdge(getNormalizedPerpendicular(aTangentEdge) * -fHalfLineWidth);
686 aRetval.append(createAreaGeometryForJoin(
687 aTangentPrev, aTangentEdge,
688 aPerpendPrev, aPerpendEdge,
689 aEdge.getStartPoint(), fHalfLineWidth,
690 eJoin, fMiterMinimumAngle));
692 else if(ORIENTATION_NEGATIVE == aOrientation)
694 const B2DVector aPerpendPrev(getNormalizedPerpendicular(aTangentPrev) * fHalfLineWidth);
695 const B2DVector aPerpendEdge(getNormalizedPerpendicular(aTangentEdge) * fHalfLineWidth);
697 aRetval.append(createAreaGeometryForJoin(
698 aTangentEdge, aTangentPrev,
699 aPerpendEdge, aPerpendPrev,
700 aEdge.getStartPoint(), fHalfLineWidth,
701 eJoin, fMiterMinimumAngle));
705 // create geometry for edge
706 aRetval.append(createAreaGeometryForEdge(aEdge, fHalfLineWidth));
708 // prepare next step
709 if(bEventuallyCreateLineJoin)
711 aPrev = aEdge;
714 aEdge.setStartPoint(aEdge.getEndPoint());
718 return aRetval;
720 else
722 return B2DPolyPolygon(rCandidate);
725 } // end of namespace tools
726 } // end of namespace basegfx
728 //////////////////////////////////////////////////////////////////////////////
729 // eof