merged tag ooo/OOO330_m14
[LibreOffice.git] / basegfx / source / polygon / b2dlinegeometry.cxx
blob0db5efbfb86d0b502befaf80700f16be2dc386a9
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2000, 2010 Oracle and/or its affiliates.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * This file is part of OpenOffice.org.
11 * OpenOffice.org is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU Lesser General Public License version 3
13 * only, as published by the Free Software Foundation.
15 * OpenOffice.org is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License version 3 for more details
19 * (a copy is included in the LICENSE file that accompanied this code).
21 * You should have received a copy of the GNU Lesser General Public License
22 * version 3 along with OpenOffice.org. If not, see
23 * <http://www.openoffice.org/license.html>
24 * for a copy of the LGPLv3 License.
26 ************************************************************************/
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_basegfx.hxx"
30 #include <cstdio>
31 #include <osl/diagnose.h>
32 #include <basegfx/polygon/b2dlinegeometry.hxx>
33 #include <basegfx/point/b2dpoint.hxx>
34 #include <basegfx/vector/b2dvector.hxx>
35 #include <basegfx/polygon/b2dpolygontools.hxx>
36 #include <basegfx/polygon/b2dpolypolygontools.hxx>
37 #include <basegfx/range/b2drange.hxx>
38 #include <basegfx/matrix/b2dhommatrix.hxx>
39 #include <basegfx/curve/b2dcubicbezier.hxx>
40 #include <basegfx/matrix/b2dhommatrixtools.hxx>
42 //////////////////////////////////////////////////////////////////////////////
44 namespace basegfx
46 namespace tools
48 B2DPolyPolygon createAreaGeometryForLineStartEnd(
49 const B2DPolygon& rCandidate,
50 const B2DPolyPolygon& rArrow,
51 bool bStart,
52 double fWidth,
53 double fCandidateLength,
54 double fDockingPosition, // 0->top, 1->bottom
55 double* pConsumedLength)
57 B2DPolyPolygon aRetval;
58 OSL_ENSURE(rCandidate.count() > 1L, "createAreaGeometryForLineStartEnd: Line polygon has too less points (!)");
59 OSL_ENSURE(rArrow.count() > 0L, "createAreaGeometryForLineStartEnd: Empty arrow PolyPolygon (!)");
60 OSL_ENSURE(fWidth > 0.0, "createAreaGeometryForLineStartEnd: Width too small (!)");
61 OSL_ENSURE(fDockingPosition >= 0.0 && fDockingPosition <= 1.0,
62 "createAreaGeometryForLineStartEnd: fDockingPosition out of range [0.0 .. 1.0] (!)");
64 if(fWidth < 0.0)
66 fWidth = -fWidth;
69 if(rCandidate.count() > 1 && rArrow.count() && !fTools::equalZero(fWidth))
71 if(fDockingPosition < 0.0)
73 fDockingPosition = 0.0;
75 else if(fDockingPosition > 1.0)
77 fDockingPosition = 1.0;
80 // init return value from arrow
81 aRetval.append(rArrow);
83 // get size of the arrow
84 const B2DRange aArrowSize(getRange(rArrow));
86 // build ArrowTransform; center in X, align with axis in Y
87 B2DHomMatrix aArrowTransform(basegfx::tools::createTranslateB2DHomMatrix(
88 -aArrowSize.getCenter().getX(), -aArrowSize.getMinimum().getY()));
90 // scale to target size
91 const double fArrowScale(fWidth / (aArrowSize.getRange().getX()));
92 aArrowTransform.scale(fArrowScale, fArrowScale);
94 // get arrow size in Y
95 B2DPoint aUpperCenter(aArrowSize.getCenter().getX(), aArrowSize.getMaximum().getY());
96 aUpperCenter *= aArrowTransform;
97 const double fArrowYLength(B2DVector(aUpperCenter).getLength());
99 // move arrow to have docking position centered
100 aArrowTransform.translate(0.0, -fArrowYLength * fDockingPosition);
102 // prepare polygon length
103 if(fTools::equalZero(fCandidateLength))
105 fCandidateLength = getLength(rCandidate);
108 // get the polygon vector we want to plant this arrow on
109 const double fConsumedLength(fArrowYLength * (1.0 - fDockingPosition));
110 const B2DVector aHead(rCandidate.getB2DPoint((bStart) ? 0L : rCandidate.count() - 1L));
111 const B2DVector aTail(getPositionAbsolute(rCandidate,
112 (bStart) ? fConsumedLength : fCandidateLength - fConsumedLength, fCandidateLength));
114 // from that vector, take the needed rotation and add rotate for arrow to transformation
115 const B2DVector aTargetDirection(aHead - aTail);
116 const double fRotation(atan2(aTargetDirection.getY(), aTargetDirection.getX()) + (90.0 * F_PI180));
118 // rotate around docking position
119 aArrowTransform.rotate(fRotation);
121 // move arrow docking position to polygon head
122 aArrowTransform.translate(aHead.getX(), aHead.getY());
124 // transform retval and close
125 aRetval.transform(aArrowTransform);
126 aRetval.setClosed(true);
128 // if pConsumedLength is asked for, fill it
129 if(pConsumedLength)
131 *pConsumedLength = fConsumedLength;
135 return aRetval;
137 } // end of namespace tools
138 } // end of namespace basegfx
140 //////////////////////////////////////////////////////////////////////////////
142 namespace basegfx
144 // anonymus namespace for local helpers
145 namespace
147 bool impIsSimpleEdge(const B2DCubicBezier& rCandidate, double fMaxCosQuad, double fMaxPartOfEdgeQuad)
149 // isBezier() is true, already tested by caller
150 const B2DVector aEdge(rCandidate.getEndPoint() - rCandidate.getStartPoint());
152 if(aEdge.equalZero())
154 // start and end point the same, but control vectors used -> baloon curve loop
155 // is not a simple edge
156 return false;
159 // get tangentA and scalar with edge
160 const B2DVector aTangentA(rCandidate.getTangent(0.0));
161 const double fScalarAE(aEdge.scalar(aTangentA));
163 if(fTools::lessOrEqual(fScalarAE, 0.0))
165 // angle between TangentA and Edge is bigger or equal 90 degrees
166 return false;
169 // get self-scalars for E and A
170 const double fScalarE(aEdge.scalar(aEdge));
171 const double fScalarA(aTangentA.scalar(aTangentA));
172 const double fLengthCompareE(fScalarE * fMaxPartOfEdgeQuad);
174 if(fTools::moreOrEqual(fScalarA, fLengthCompareE))
176 // length of TangentA is more than fMaxPartOfEdge of length of edge
177 return false;
180 if(fTools::lessOrEqual(fScalarAE * fScalarAE, fScalarA * fScalarE * fMaxCosQuad))
182 // angle between TangentA and Edge is bigger or equal angle defined by fMaxCos
183 return false;
186 // get tangentB and scalar with edge
187 const B2DVector aTangentB(rCandidate.getTangent(1.0));
188 const double fScalarBE(aEdge.scalar(aTangentB));
190 if(fTools::lessOrEqual(fScalarBE, 0.0))
192 // angle between TangentB and Edge is bigger or equal 90 degrees
193 return false;
196 // get self-scalar for B
197 const double fScalarB(aTangentB.scalar(aTangentB));
199 if(fTools::moreOrEqual(fScalarB, fLengthCompareE))
201 // length of TangentB is more than fMaxPartOfEdge of length of edge
202 return false;
205 if(fTools::lessOrEqual(fScalarBE * fScalarBE, fScalarB * fScalarE * fMaxCosQuad))
207 // angle between TangentB and Edge is bigger or equal defined by fMaxCos
208 return false;
211 return true;
214 void impSubdivideToSimple(const B2DCubicBezier& rCandidate, B2DPolygon& rTarget, double fMaxCosQuad, double fMaxPartOfEdgeQuad, sal_uInt32 nMaxRecursionDepth)
216 if(!nMaxRecursionDepth || impIsSimpleEdge(rCandidate, fMaxCosQuad, fMaxPartOfEdgeQuad))
218 rTarget.appendBezierSegment(rCandidate.getControlPointA(), rCandidate.getControlPointB(), rCandidate.getEndPoint());
220 else
222 B2DCubicBezier aLeft, aRight;
223 rCandidate.split(0.5, &aLeft, &aRight);
225 impSubdivideToSimple(aLeft, rTarget, fMaxCosQuad, fMaxPartOfEdgeQuad, nMaxRecursionDepth - 1);
226 impSubdivideToSimple(aRight, rTarget, fMaxCosQuad, fMaxPartOfEdgeQuad, nMaxRecursionDepth - 1);
230 B2DPolygon subdivideToSimple(const B2DPolygon& rCandidate, double fMaxCosQuad, double fMaxPartOfEdgeQuad)
232 const sal_uInt32 nPointCount(rCandidate.count());
234 if(rCandidate.areControlPointsUsed() && nPointCount)
236 const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
237 B2DPolygon aRetval;
238 B2DCubicBezier aEdge;
240 // prepare edge for loop
241 aEdge.setStartPoint(rCandidate.getB2DPoint(0));
242 aRetval.append(aEdge.getStartPoint());
244 for(sal_uInt32 a(0); a < nEdgeCount; a++)
246 // fill B2DCubicBezier
247 const sal_uInt32 nNextIndex((a + 1) % nPointCount);
248 aEdge.setControlPointA(rCandidate.getNextControlPoint(a));
249 aEdge.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex));
250 aEdge.setEndPoint(rCandidate.getB2DPoint(nNextIndex));
252 // get rid of unnecessary bezier segments
253 aEdge.testAndSolveTrivialBezier();
255 if(aEdge.isBezier())
257 // before splitting recursively with internal simple criteria, use
258 // ExtremumPosFinder to remove those
259 ::std::vector< double > aExtremumPositions;
261 aExtremumPositions.reserve(4);
262 aEdge.getAllExtremumPositions(aExtremumPositions);
264 const sal_uInt32 nCount(aExtremumPositions.size());
266 if(nCount)
268 if(nCount > 1)
270 // create order from left to right
271 ::std::sort(aExtremumPositions.begin(), aExtremumPositions.end());
274 for(sal_uInt32 b(0); b < nCount;)
276 // split aEdge at next split pos
277 B2DCubicBezier aLeft;
278 const double fSplitPos(aExtremumPositions[b++]);
280 aEdge.split(fSplitPos, &aLeft, &aEdge);
281 aLeft.testAndSolveTrivialBezier();
283 // consume left part
284 if(aLeft.isBezier())
286 impSubdivideToSimple(aLeft, aRetval, fMaxCosQuad, fMaxPartOfEdgeQuad, 6);
288 else
290 aRetval.append(aLeft.getEndPoint());
293 if(b < nCount)
295 // correct the remaining split positions to fit to shortened aEdge
296 const double fScaleFactor(1.0 / (1.0 - fSplitPos));
298 for(sal_uInt32 c(b); c < nCount; c++)
300 aExtremumPositions[c] = (aExtremumPositions[c] - fSplitPos) * fScaleFactor;
305 // test the shortened rest of aEdge
306 aEdge.testAndSolveTrivialBezier();
308 // consume right part
309 if(aEdge.isBezier())
311 impSubdivideToSimple(aEdge, aRetval, fMaxCosQuad, fMaxPartOfEdgeQuad, 6);
313 else
315 aRetval.append(aEdge.getEndPoint());
318 else
320 impSubdivideToSimple(aEdge, aRetval, fMaxCosQuad, fMaxPartOfEdgeQuad, 6);
323 else
325 // straight edge, add point
326 aRetval.append(aEdge.getEndPoint());
329 // prepare edge for next step
330 aEdge.setStartPoint(aEdge.getEndPoint());
333 // copy closed flag and check for double points
334 aRetval.setClosed(rCandidate.isClosed());
335 aRetval.removeDoublePoints();
337 return aRetval;
339 else
341 return rCandidate;
345 B2DPolygon createAreaGeometryForEdge(const B2DCubicBezier& rEdge, double fHalfLineWidth)
347 // create polygon for edge
348 // Unfortunately, while it would be geometrically correct to not add
349 // the in-between points EdgeEnd and EdgeStart, it leads to rounding
350 // errors when converting to integer polygon coordinates for painting
351 if(rEdge.isBezier())
353 // prepare target and data common for upper and lower
354 B2DPolygon aBezierPolygon;
355 const B2DVector aPureEdgeVector(rEdge.getEndPoint() - rEdge.getStartPoint());
356 const double fEdgeLength(aPureEdgeVector.getLength());
357 const bool bIsEdgeLengthZero(fTools::equalZero(fEdgeLength));
358 const B2DVector aTangentA(rEdge.getTangent(0.0));
359 const B2DVector aTangentB(rEdge.getTangent(1.0));
361 // create upper edge.
363 // create displacement vectors and check if they cut
364 const B2DVector aPerpendStart(getNormalizedPerpendicular(aTangentA) * -fHalfLineWidth);
365 const B2DVector aPerpendEnd(getNormalizedPerpendicular(aTangentB) * -fHalfLineWidth);
366 double fCut(0.0);
367 const tools::CutFlagValue aCut(tools::findCut(
368 rEdge.getStartPoint(), aPerpendStart,
369 rEdge.getEndPoint(), aPerpendEnd,
370 CUTFLAG_ALL, &fCut));
372 if(CUTFLAG_NONE != aCut)
374 // calculate cut point and add
375 const B2DPoint aCutPoint(rEdge.getStartPoint() + (aPerpendStart * fCut));
376 aBezierPolygon.append(aCutPoint);
378 else
380 // create scaled bezier segment
381 const B2DPoint aStart(rEdge.getStartPoint() + aPerpendStart);
382 const B2DPoint aEnd(rEdge.getEndPoint() + aPerpendEnd);
383 const B2DVector aEdge(aEnd - aStart);
384 const double fLength(aEdge.getLength());
385 const double fScale(bIsEdgeLengthZero ? 1.0 : fLength / fEdgeLength);
386 const B2DVector fRelNext(rEdge.getControlPointA() - rEdge.getStartPoint());
387 const B2DVector fRelPrev(rEdge.getControlPointB() - rEdge.getEndPoint());
389 aBezierPolygon.append(aStart);
390 aBezierPolygon.appendBezierSegment(aStart + (fRelNext * fScale), aEnd + (fRelPrev * fScale), aEnd);
394 // append original in-between point
395 aBezierPolygon.append(rEdge.getEndPoint());
397 // create lower edge.
399 // create displacement vectors and check if they cut
400 const B2DVector aPerpendStart(getNormalizedPerpendicular(aTangentA) * fHalfLineWidth);
401 const B2DVector aPerpendEnd(getNormalizedPerpendicular(aTangentB) * fHalfLineWidth);
402 double fCut(0.0);
403 const tools::CutFlagValue aCut(tools::findCut(
404 rEdge.getEndPoint(), aPerpendEnd,
405 rEdge.getStartPoint(), aPerpendStart,
406 CUTFLAG_ALL, &fCut));
408 if(CUTFLAG_NONE != aCut)
410 // calculate cut point and add
411 const B2DPoint aCutPoint(rEdge.getEndPoint() + (aPerpendEnd * fCut));
412 aBezierPolygon.append(aCutPoint);
414 else
416 // create scaled bezier segment
417 const B2DPoint aStart(rEdge.getEndPoint() + aPerpendEnd);
418 const B2DPoint aEnd(rEdge.getStartPoint() + aPerpendStart);
419 const B2DVector aEdge(aEnd - aStart);
420 const double fLength(aEdge.getLength());
421 const double fScale(bIsEdgeLengthZero ? 1.0 : fLength / fEdgeLength);
422 const B2DVector fRelNext(rEdge.getControlPointB() - rEdge.getEndPoint());
423 const B2DVector fRelPrev(rEdge.getControlPointA() - rEdge.getStartPoint());
425 aBezierPolygon.append(aStart);
426 aBezierPolygon.appendBezierSegment(aStart + (fRelNext * fScale), aEnd + (fRelPrev * fScale), aEnd);
430 // append original in-between point
431 aBezierPolygon.append(rEdge.getStartPoint());
433 // close and return
434 aBezierPolygon.setClosed(true);
435 return aBezierPolygon;
437 else
439 // #i101491# emulate rEdge.getTangent call which applies a factor of 0.3 to the
440 // full-length edge vector to have numerically exactly the same results as in the
441 // createAreaGeometryForJoin implementation
442 const B2DVector aEdgeTangent((rEdge.getEndPoint() - rEdge.getStartPoint()) * 0.3);
443 const B2DVector aPerpendEdgeVector(getNormalizedPerpendicular(aEdgeTangent) * fHalfLineWidth);
444 B2DPolygon aEdgePolygon;
446 // create upper edge
447 aEdgePolygon.append(rEdge.getStartPoint() - aPerpendEdgeVector);
448 aEdgePolygon.append(rEdge.getEndPoint() - aPerpendEdgeVector);
450 // append original in-between point
451 aEdgePolygon.append(rEdge.getEndPoint());
453 // create lower edge
454 aEdgePolygon.append(rEdge.getEndPoint() + aPerpendEdgeVector);
455 aEdgePolygon.append(rEdge.getStartPoint() + aPerpendEdgeVector);
457 // append original in-between point
458 aEdgePolygon.append(rEdge.getStartPoint());
460 // close and return
461 aEdgePolygon.setClosed(true);
462 return aEdgePolygon;
466 B2DPolygon createAreaGeometryForJoin(
467 const B2DVector& rTangentPrev,
468 const B2DVector& rTangentEdge,
469 const B2DVector& rPerpendPrev,
470 const B2DVector& rPerpendEdge,
471 const B2DPoint& rPoint,
472 double fHalfLineWidth,
473 B2DLineJoin eJoin,
474 double fMiterMinimumAngle)
476 OSL_ENSURE(fHalfLineWidth > 0.0, "createAreaGeometryForJoin: LineWidth too small (!)");
477 OSL_ENSURE(B2DLINEJOIN_NONE != eJoin, "createAreaGeometryForJoin: B2DLINEJOIN_NONE not allowed (!)");
479 // LineJoin from tangent rPerpendPrev to tangent rPerpendEdge in rPoint
480 B2DPolygon aEdgePolygon;
481 const B2DPoint aStartPoint(rPoint + rPerpendPrev);
482 const B2DPoint aEndPoint(rPoint + rPerpendEdge);
484 // test if for Miter, the angle is too small and the fallback
485 // to bevel needs to be used
486 if(B2DLINEJOIN_MITER == eJoin)
488 const double fAngle(fabs(rPerpendPrev.angle(rPerpendEdge)));
490 if((F_PI - fAngle) < fMiterMinimumAngle)
492 // fallback to bevel
493 eJoin = B2DLINEJOIN_BEVEL;
497 switch(eJoin)
499 case B2DLINEJOIN_MITER :
501 aEdgePolygon.append(aEndPoint);
502 aEdgePolygon.append(rPoint);
503 aEdgePolygon.append(aStartPoint);
505 // Look for the cut point between start point along rTangentPrev and
506 // end point along rTangentEdge. -rTangentEdge should be used, but since
507 // the cut value is used for interpolating along the first edge, the negation
508 // is not needed since the same fCut will be found on the first edge.
509 // If it exists, insert it to complete the mitered fill polygon.
510 double fCutPos(0.0);
511 tools::findCut(aStartPoint, rTangentPrev, aEndPoint, rTangentEdge, CUTFLAG_ALL, &fCutPos);
513 if(0.0 != fCutPos)
515 const B2DPoint aCutPoint(interpolate(aStartPoint, aStartPoint + rTangentPrev, fCutPos));
516 aEdgePolygon.append(aCutPoint);
519 break;
521 case B2DLINEJOIN_ROUND :
523 // use tooling to add needed EllipseSegment
524 double fAngleStart(atan2(rPerpendPrev.getY(), rPerpendPrev.getX()));
525 double fAngleEnd(atan2(rPerpendEdge.getY(), rPerpendEdge.getX()));
527 // atan2 results are [-PI .. PI], consolidate to [0.0 .. 2PI]
528 if(fAngleStart < 0.0)
530 fAngleStart += F_2PI;
533 if(fAngleEnd < 0.0)
535 fAngleEnd += F_2PI;
538 const B2DPolygon aBow(tools::createPolygonFromEllipseSegment(rPoint, fHalfLineWidth, fHalfLineWidth, fAngleStart, fAngleEnd));
540 if(aBow.count() > 1)
542 // #i101491#
543 // use the original start/end positions; the ones from bow creation may be numerically
544 // different due to their different creation. To guarantee good merging quality with edges
545 // and edge roundings (and to reduce point count)
546 aEdgePolygon = aBow;
547 aEdgePolygon.setB2DPoint(0, aStartPoint);
548 aEdgePolygon.setB2DPoint(aEdgePolygon.count() - 1, aEndPoint);
549 aEdgePolygon.append(rPoint);
551 break;
553 else
555 // wanted fall-through to default
558 default: // B2DLINEJOIN_BEVEL
560 aEdgePolygon.append(aEndPoint);
561 aEdgePolygon.append(rPoint);
562 aEdgePolygon.append(aStartPoint);
564 break;
568 // create last polygon part for edge
569 aEdgePolygon.setClosed(true);
571 return aEdgePolygon;
573 } // end of anonymus namespace
575 namespace tools
577 B2DPolyPolygon createAreaGeometry(
578 const B2DPolygon& rCandidate,
579 double fHalfLineWidth,
580 B2DLineJoin eJoin,
581 double fMaxAllowedAngle,
582 double fMaxPartOfEdge,
583 double fMiterMinimumAngle)
585 if(fMaxAllowedAngle > F_PI2)
587 fMaxAllowedAngle = F_PI2;
589 else if(fMaxAllowedAngle < 0.01 * F_PI2)
591 fMaxAllowedAngle = 0.01 * F_PI2;
594 if(fMaxPartOfEdge > 1.0)
596 fMaxPartOfEdge = 1.0;
598 else if(fMaxPartOfEdge < 0.01)
600 fMaxPartOfEdge = 0.01;
603 if(fMiterMinimumAngle > F_PI)
605 fMiterMinimumAngle = F_PI;
607 else if(fMiterMinimumAngle < 0.01 * F_PI)
609 fMiterMinimumAngle = 0.01 * F_PI;
612 B2DPolygon aCandidate(rCandidate);
613 const double fMaxCos(cos(fMaxAllowedAngle));
615 aCandidate.removeDoublePoints();
616 aCandidate = subdivideToSimple(aCandidate, fMaxCos * fMaxCos, fMaxPartOfEdge * fMaxPartOfEdge);
618 const sal_uInt32 nPointCount(aCandidate.count());
620 if(nPointCount)
622 B2DPolyPolygon aRetval;
623 const bool bEventuallyCreateLineJoin(B2DLINEJOIN_NONE != eJoin);
624 const bool bIsClosed(aCandidate.isClosed());
625 const sal_uInt32 nEdgeCount(bIsClosed ? nPointCount : nPointCount - 1);
627 if(nEdgeCount)
629 B2DCubicBezier aEdge;
630 B2DCubicBezier aPrev;
632 // prepare edge
633 aEdge.setStartPoint(aCandidate.getB2DPoint(0));
635 if(bIsClosed && bEventuallyCreateLineJoin)
637 // prepare previous edge
638 const sal_uInt32 nPrevIndex(nPointCount - 1);
639 aPrev.setStartPoint(aCandidate.getB2DPoint(nPrevIndex));
640 aPrev.setControlPointA(aCandidate.getNextControlPoint(nPrevIndex));
641 aPrev.setControlPointB(aCandidate.getPrevControlPoint(0));
642 aPrev.setEndPoint(aEdge.getStartPoint());
645 for(sal_uInt32 a(0); a < nEdgeCount; a++)
647 // fill current Edge
648 const sal_uInt32 nNextIndex((a + 1) % nPointCount);
649 aEdge.setControlPointA(aCandidate.getNextControlPoint(a));
650 aEdge.setControlPointB(aCandidate.getPrevControlPoint(nNextIndex));
651 aEdge.setEndPoint(aCandidate.getB2DPoint(nNextIndex));
653 // check and create linejoin
654 if(bEventuallyCreateLineJoin && (bIsClosed || 0 != a))
656 const B2DVector aTangentPrev(aPrev.getTangent(1.0));
657 const B2DVector aTangentEdge(aEdge.getTangent(0.0));
658 B2VectorOrientation aOrientation(getOrientation(aTangentPrev, aTangentEdge));
660 if(ORIENTATION_NEUTRAL == aOrientation)
662 // they are parallell or empty; if they are both not zero and point
663 // in opposite direction, a half-circle is needed
664 if(!aTangentPrev.equalZero() && !aTangentEdge.equalZero())
666 const double fAngle(fabs(aTangentPrev.angle(aTangentEdge)));
668 if(fTools::equal(fAngle, F_PI))
670 // for half-circle production, fallback to positive
671 // orientation
672 aOrientation = ORIENTATION_POSITIVE;
677 if(ORIENTATION_POSITIVE == aOrientation)
679 const B2DVector aPerpendPrev(getNormalizedPerpendicular(aTangentPrev) * -fHalfLineWidth);
680 const B2DVector aPerpendEdge(getNormalizedPerpendicular(aTangentEdge) * -fHalfLineWidth);
682 aRetval.append(createAreaGeometryForJoin(
683 aTangentPrev, aTangentEdge,
684 aPerpendPrev, aPerpendEdge,
685 aEdge.getStartPoint(), fHalfLineWidth,
686 eJoin, fMiterMinimumAngle));
688 else if(ORIENTATION_NEGATIVE == aOrientation)
690 const B2DVector aPerpendPrev(getNormalizedPerpendicular(aTangentPrev) * fHalfLineWidth);
691 const B2DVector aPerpendEdge(getNormalizedPerpendicular(aTangentEdge) * fHalfLineWidth);
693 aRetval.append(createAreaGeometryForJoin(
694 aTangentEdge, aTangentPrev,
695 aPerpendEdge, aPerpendPrev,
696 aEdge.getStartPoint(), fHalfLineWidth,
697 eJoin, fMiterMinimumAngle));
701 // create geometry for edge
702 aRetval.append(createAreaGeometryForEdge(aEdge, fHalfLineWidth));
704 // prepare next step
705 if(bEventuallyCreateLineJoin)
707 aPrev = aEdge;
710 aEdge.setStartPoint(aEdge.getEndPoint());
714 return aRetval;
716 else
718 return B2DPolyPolygon(rCandidate);
721 } // end of namespace tools
722 } // end of namespace basegfx
724 //////////////////////////////////////////////////////////////////////////////
725 // eof