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"
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 //////////////////////////////////////////////////////////////////////////////
48 B2DPolyPolygon
createAreaGeometryForLineStartEnd(
49 const B2DPolygon
& rCandidate
,
50 const B2DPolyPolygon
& rArrow
,
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] (!)");
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
131 *pConsumedLength
= fConsumedLength
;
137 } // end of namespace tools
138 } // end of namespace basegfx
140 //////////////////////////////////////////////////////////////////////////////
144 // anonymus namespace for local helpers
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
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
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
180 if(fTools::lessOrEqual(fScalarAE
* fScalarAE
, fScalarA
* fScalarE
* fMaxCosQuad
))
182 // angle between TangentA and Edge is bigger or equal angle defined by fMaxCos
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
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
205 if(fTools::lessOrEqual(fScalarBE
* fScalarBE
, fScalarB
* fScalarE
* fMaxCosQuad
))
207 // angle between TangentB and Edge is bigger or equal defined by fMaxCos
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());
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);
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();
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());
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();
286 impSubdivideToSimple(aLeft
, aRetval
, fMaxCosQuad
, fMaxPartOfEdgeQuad
, 6);
290 aRetval
.append(aLeft
.getEndPoint());
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
311 impSubdivideToSimple(aEdge
, aRetval
, fMaxCosQuad
, fMaxPartOfEdgeQuad
, 6);
315 aRetval
.append(aEdge
.getEndPoint());
320 impSubdivideToSimple(aEdge
, aRetval
, fMaxCosQuad
, fMaxPartOfEdgeQuad
, 6);
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();
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
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
);
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
);
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
);
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
);
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());
434 aBezierPolygon
.setClosed(true);
435 return aBezierPolygon
;
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
;
447 aEdgePolygon
.append(rEdge
.getStartPoint() - aPerpendEdgeVector
);
448 aEdgePolygon
.append(rEdge
.getEndPoint() - aPerpendEdgeVector
);
450 // append original in-between point
451 aEdgePolygon
.append(rEdge
.getEndPoint());
454 aEdgePolygon
.append(rEdge
.getEndPoint() + aPerpendEdgeVector
);
455 aEdgePolygon
.append(rEdge
.getStartPoint() + aPerpendEdgeVector
);
457 // append original in-between point
458 aEdgePolygon
.append(rEdge
.getStartPoint());
461 aEdgePolygon
.setClosed(true);
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
,
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
)
493 eJoin
= B2DLINEJOIN_BEVEL
;
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.
511 tools::findCut(aStartPoint
, rTangentPrev
, aEndPoint
, rTangentEdge
, CUTFLAG_ALL
, &fCutPos
);
515 const B2DPoint
aCutPoint(interpolate(aStartPoint
, aStartPoint
+ rTangentPrev
, fCutPos
));
516 aEdgePolygon
.append(aCutPoint
);
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
;
538 const B2DPolygon
aBow(tools::createPolygonFromEllipseSegment(rPoint
, fHalfLineWidth
, fHalfLineWidth
, fAngleStart
, fAngleEnd
));
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)
547 aEdgePolygon
.setB2DPoint(0, aStartPoint
);
548 aEdgePolygon
.setB2DPoint(aEdgePolygon
.count() - 1, aEndPoint
);
549 aEdgePolygon
.append(rPoint
);
555 // wanted fall-through to default
558 default: // B2DLINEJOIN_BEVEL
560 aEdgePolygon
.append(aEndPoint
);
561 aEdgePolygon
.append(rPoint
);
562 aEdgePolygon
.append(aStartPoint
);
568 // create last polygon part for edge
569 aEdgePolygon
.setClosed(true);
573 } // end of anonymus namespace
577 B2DPolyPolygon
createAreaGeometry(
578 const B2DPolygon
& rCandidate
,
579 double fHalfLineWidth
,
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());
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);
629 B2DCubicBezier aEdge
;
630 B2DCubicBezier aPrev
;
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
++)
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
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
));
705 if(bEventuallyCreateLineJoin
)
710 aEdge
.setStartPoint(aEdge
.getEndPoint());
718 return B2DPolyPolygon(rCandidate
);
721 } // end of namespace tools
722 } // end of namespace basegfx
724 //////////////////////////////////////////////////////////////////////////////