20#include <osl/diagnose.h>
31#include <com/sun/star/drawing/LineCap.hpp>
42 double fCandidateLength,
43 double fDockingPosition,
44 double* pConsumedLength,
48 assert((rCandidate.
count() > 1) &&
"createAreaGeometryForLineStartEnd: Line polygon has too few points");
49 assert((rArrow.
count() > 0) &&
"createAreaGeometryForLineStartEnd: Empty arrow utils::PolyPolygon");
50 assert((fWidth > 0.0) &&
"createAreaGeometryForLineStartEnd: Width too small");
51 assert((fDockingPosition >= 0.0 && fDockingPosition <= 1.0) &&
52 "createAreaGeometryForLineStartEnd: fDockingPosition out of range [0.0 .. 1.0]");
61 if(fDockingPosition < 0.0)
63 fDockingPosition = 0.0;
65 else if(fDockingPosition > 1.0)
67 fDockingPosition = 1.0;
81 const double fArrowScale(fWidth / (aArrowSize.
getWidth()));
82 aArrowTransform.
scale(fArrowScale, fArrowScale);
86 aUpperCenter *= aArrowTransform;
90 aArrowTransform.
translate(0.0, -fArrowYLength * fDockingPosition + fShift);
99 const double fConsumedLength(fArrowYLength * (1.0 - fDockingPosition) - fShift);
102 bStart ? fConsumedLength : fCandidateLength - fConsumedLength, fCandidateLength));
105 const B2DVector aTargetDirection(aHead - aTail);
106 const double fRotation(atan2(aTargetDirection.
getY(), aTargetDirection.
getX()) + M_PI_2);
109 aArrowTransform.
rotate(fRotation);
121 *pConsumedLength = fConsumedLength;
134 bool impIsSimpleEdge(
const B2DCubicBezier& rCandidate,
double fMaxCosQuad,
double fMaxPartOfEdgeQuad)
137 const B2DVector aEdge(rCandidate.getEndPoint() - rCandidate.getStartPoint());
139 if(aEdge.equalZero())
147 const B2DVector aTangentA(rCandidate.getTangent(0.0));
148 const double fScalarAE(aEdge.scalar(aTangentA));
157 const double fScalarE(aEdge.scalar(aEdge));
158 const double fScalarA(aTangentA.scalar(aTangentA));
159 const double fLengthCompareE(fScalarE * fMaxPartOfEdgeQuad);
174 const B2DVector aTangentB(rCandidate.getTangent(1.0));
175 const double fScalarBE(aEdge.scalar(aTangentB));
184 const double fScalarB(aTangentB.scalar(aTangentB));
201 void impSubdivideToSimple(
const B2DCubicBezier& rCandidate, B2DPolygon& rTarget,
double fMaxCosQuad,
double fMaxPartOfEdgeQuad, sal_uInt32 nMaxRecursionDepth)
203 if(!nMaxRecursionDepth || impIsSimpleEdge(rCandidate, fMaxCosQuad, fMaxPartOfEdgeQuad))
205 rTarget.appendBezierSegment(rCandidate.getControlPointA(), rCandidate.getControlPointB(), rCandidate.getEndPoint());
209 B2DCubicBezier aLeft, aRight;
210 rCandidate.split(0.5, &aLeft, &aRight);
212 impSubdivideToSimple(aLeft, rTarget, fMaxCosQuad, fMaxPartOfEdgeQuad, nMaxRecursionDepth - 1);
213 impSubdivideToSimple(aRight, rTarget, fMaxCosQuad, fMaxPartOfEdgeQuad, nMaxRecursionDepth - 1);
217 B2DPolygon subdivideToSimple(
const B2DPolygon& rCandidate,
double fMaxCosQuad,
double fMaxPartOfEdgeQuad)
219 const sal_uInt32 nPointCount(rCandidate.count());
221 if(rCandidate.areControlPointsUsed() && nPointCount)
223 const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
225 B2DCubicBezier aEdge;
228 aEdge.setStartPoint(rCandidate.getB2DPoint(0));
229 aRetval.append(aEdge.getStartPoint());
231 for(sal_uInt32
a(0);
a < nEdgeCount;
a++)
234 const sal_uInt32 nNextIndex((a + 1) % nPointCount);
235 aEdge.setControlPointA(rCandidate.getNextControlPoint(a));
236 aEdge.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex));
237 aEdge.setEndPoint(rCandidate.getB2DPoint(nNextIndex));
240 aEdge.testAndSolveTrivialBezier();
246 std::vector< double > aExtremumPositions;
248 aExtremumPositions.reserve(4);
249 aEdge.getAllExtremumPositions(aExtremumPositions);
251 const sal_uInt32
nCount(aExtremumPositions.size());
258 std::sort(aExtremumPositions.begin(), aExtremumPositions.end());
261 for(sal_uInt32 b(0); b <
nCount;)
264 B2DCubicBezier aLeft;
265 const double fSplitPos(aExtremumPositions[b++]);
267 aEdge.split(fSplitPos, &aLeft, &aEdge);
268 aLeft.testAndSolveTrivialBezier();
273 impSubdivideToSimple(aLeft, aRetval, fMaxCosQuad, fMaxPartOfEdgeQuad, 6);
277 aRetval.append(aLeft.getEndPoint());
283 const double fScaleFactor(1.0 / (1.0 - fSplitPos));
285 for(sal_uInt32 c(b); c <
nCount; c++)
287 aExtremumPositions[c] = (aExtremumPositions[c] - fSplitPos) * fScaleFactor;
293 aEdge.testAndSolveTrivialBezier();
298 impSubdivideToSimple(aEdge, aRetval, fMaxCosQuad, fMaxPartOfEdgeQuad, 6);
302 aRetval.append(aEdge.getEndPoint());
307 impSubdivideToSimple(aEdge, aRetval, fMaxCosQuad, fMaxPartOfEdgeQuad, 6);
313 aRetval.append(aEdge.getEndPoint());
317 aEdge.setStartPoint(aEdge.getEndPoint());
321 aRetval.setClosed(rCandidate.isClosed());
322 aRetval.removeDoublePoints();
332 B2DPolygon createAreaGeometryForEdge(
333 const B2DCubicBezier& rEdge,
334 double fHalfLineWidth,
348 B2DPolygon aBezierPolygon;
349 const B2DVector aPureEdgeVector(rEdge.getEndPoint() - rEdge.getStartPoint());
350 const double fEdgeLength(aPureEdgeVector.getLength());
352 B2DVector aTangentA(rEdge.getTangent(0.0)); aTangentA.normalize();
353 B2DVector aTangentB(rEdge.getTangent(1.0)); aTangentB.normalize();
358 const B2DVector aPerpendStartA(aNormalizedPerpendicularA * -fHalfLineWidth);
359 const B2DVector aPerpendEndA(aNormalizedPerpendicularB * -fHalfLineWidth);
362 rEdge.getStartPoint(), aPerpendStartA,
363 rEdge.getEndPoint(), aPerpendEndA,
368 const B2DVector aPerpendStartB(aNormalizedPerpendicularA * fHalfLineWidth);
369 const B2DVector aPerpendEndB(aNormalizedPerpendicularB * fHalfLineWidth);
372 rEdge.getEndPoint(), aPerpendEndB,
373 rEdge.getStartPoint(), aPerpendStartB,
378 const bool bCut(bCutA || bCutB);
382 if(bStartRound || bStartSquare)
388 aStartPolygon.transform(
390 fHalfLineWidth, fHalfLineWidth,
392 atan2(aTangentA.getY(), aTangentA.getX()) + M_PI_2,
393 rEdge.getStartPoint().getX(), rEdge.getStartPoint().getY()));
394 aBezierPolygon.append(aStartPolygon);
398 const basegfx::B2DPoint aStart(rEdge.getStartPoint() - (aTangentA * fHalfLineWidth));
402 aBezierPolygon.append(rEdge.getStartPoint() + aPerpendStartB);
405 aBezierPolygon.append(aStart + aPerpendStartB);
406 aBezierPolygon.append(aStart + aPerpendStartA);
410 aBezierPolygon.append(rEdge.getStartPoint() + aPerpendStartA);
417 aBezierPolygon.append(rEdge.getStartPoint());
425 aCutPoint = rEdge.getStartPoint() + (aPerpendStartA * fCutA);
426 aBezierPolygon.append(aCutPoint);
431 const B2DPoint aStart(rEdge.getStartPoint() + aPerpendStartA);
432 const B2DPoint aEnd(rEdge.getEndPoint() + aPerpendEndA);
433 const B2DVector aEdge(aEnd - aStart);
434 const double fLength(aEdge.getLength());
435 const double fScale(bIsEdgeLengthZero ? 1.0 : fLength / fEdgeLength);
436 const B2DVector fRelNext(rEdge.getControlPointA() - rEdge.getStartPoint());
437 const B2DVector fRelPrev(rEdge.getControlPointB() - rEdge.getEndPoint());
439 aBezierPolygon.append(aStart);
440 aBezierPolygon.appendBezierSegment(aStart + (fRelNext * fScale), aEnd + (fRelPrev * fScale), aEnd);
445 if(bEndRound || bEndSquare)
451 aEndPolygon.transform(
453 fHalfLineWidth, fHalfLineWidth,
455 atan2(aTangentB.getY(), aTangentB.getX()) - M_PI_2,
456 rEdge.getEndPoint().getX(), rEdge.getEndPoint().getY()));
457 aBezierPolygon.append(aEndPolygon);
461 const basegfx::B2DPoint aEnd(rEdge.getEndPoint() + (aTangentB * fHalfLineWidth));
465 aBezierPolygon.append(rEdge.getEndPoint() + aPerpendEndA);
468 aBezierPolygon.append(aEnd + aPerpendEndA);
469 aBezierPolygon.append(aEnd + aPerpendEndB);
473 aBezierPolygon.append(rEdge.getEndPoint() + aPerpendEndB);
480 aBezierPolygon.append(rEdge.getEndPoint());
488 aCutPoint = rEdge.getEndPoint() + (aPerpendEndB * fCutB);
489 aBezierPolygon.append(aCutPoint);
494 const B2DPoint aStart(rEdge.getEndPoint() + aPerpendEndB);
495 const B2DPoint aEnd(rEdge.getStartPoint() + aPerpendStartB);
496 const B2DVector aEdge(aEnd - aStart);
497 const double fLength(aEdge.getLength());
498 const double fScale(bIsEdgeLengthZero ? 1.0 : fLength / fEdgeLength);
499 const B2DVector fRelNext(rEdge.getControlPointB() - rEdge.getEndPoint());
500 const B2DVector fRelPrev(rEdge.getControlPointA() - rEdge.getStartPoint());
502 aBezierPolygon.append(aStart);
503 aBezierPolygon.appendBezierSegment(aStart + (fRelNext * fScale), aEnd + (fRelPrev * fScale), aEnd);
508 aBezierPolygon.setClosed(
true);
510 if(bStartRound || bEndRound)
513 aBezierPolygon.removeDoublePoints();
516 if(bCut && ((bStartRound || bStartSquare) && (bEndRound || bEndSquare)))
525 const sal_uInt32 nTempCount(aTemp.count());
533 for (sal_uInt32
a(0);
a < nTempCount;
a++)
535 aBezierPolygon = aTemp.getB2DPolygon(a);
537 const sal_uInt32 nCandCount(aBezierPolygon.count());
539 for(sal_uInt32 b(0); b < nCandCount; b++)
541 if(aCutPoint.equal(aBezierPolygon.getB2DPoint(b)))
543 aBezierPolygon.clear();
548 if(aBezierPolygon.count())
554 OSL_ENSURE(aBezierPolygon.count(),
"Error in line geometry creation, could not solve self-intersection (!)");
559 aBezierPolygon = aTemp.getB2DPolygon(0);
564 OSL_ENSURE(
false,
"Error in line geometry creation, could not solve self-intersection (!)");
568 if(
nullptr != pTriangles)
573 pTriangles->insert(pTriangles->end(), aResult.begin(), aResult.end());
574 aBezierPolygon.clear();
578 return aBezierPolygon;
583 B2DVector aTangent(rEdge.getEndPoint() - rEdge.getStartPoint());
584 aTangent.setLength(fHalfLineWidth);
587 B2DPolygon aEdgePolygon;
595 bool bPerpend(
false);
601 fAngle = atan2(aTangent.getY(), aTangent.getX());
603 aEdgePolygon.transform(
605 fHalfLineWidth, fHalfLineWidth,
608 rEdge.getStartPoint().getX(), rEdge.getStartPoint().getY()));
612 aPerpend.setX(-aTangent.getY());
613 aPerpend.setY(aTangent.getX());
620 aEdgePolygon.append(aStart + aPerpend);
621 aEdgePolygon.append(aStart - aPerpend);
625 aEdgePolygon.append(rEdge.getStartPoint() + aPerpend);
626 aEdgePolygon.append(rEdge.getStartPoint());
627 aEdgePolygon.append(rEdge.getStartPoint() - aPerpend);
638 fAngle = atan2(aTangent.getY(), aTangent.getX());
641 aEndPolygon.transform(
643 fHalfLineWidth, fHalfLineWidth,
646 rEdge.getEndPoint().getX(), rEdge.getEndPoint().getY()));
647 aEdgePolygon.append(aEndPolygon);
653 aPerpend.setX(-aTangent.getY());
654 aPerpend.setY(aTangent.getX());
661 aEdgePolygon.append(aEnd - aPerpend);
662 aEdgePolygon.append(aEnd + aPerpend);
666 aEdgePolygon.append(rEdge.getEndPoint() - aPerpend);
667 aEdgePolygon.append(rEdge.getEndPoint());
668 aEdgePolygon.append(rEdge.getEndPoint() + aPerpend);
673 aEdgePolygon.setClosed(
true);
675 if(
nullptr != pTriangles)
680 pTriangles->insert(pTriangles->end(), aResult.begin(), aResult.end());
681 aEdgePolygon.clear();
688 B2DPolygon createAreaGeometryForJoin(
689 const B2DVector& rTangentPrev,
690 const B2DVector& rTangentEdge,
691 const B2DVector& rPerpendPrev,
692 const B2DVector& rPerpendEdge,
693 const B2DPoint& rPoint,
694 double fHalfLineWidth,
696 double fMiterMinimumAngle,
699 SAL_WARN_IF(fHalfLineWidth <= 0.0,
"basegfx",
"createAreaGeometryForJoin: LineWidth too small (!)");
700 assert((eJoin !=
B2DLineJoin::NONE) &&
"createAreaGeometryForJoin: B2DLineJoin::NONE not allowed (!)");
703 B2DPolygon aEdgePolygon;
704 const B2DPoint aStartPoint(rPoint + rPerpendPrev);
705 const B2DPoint aEndPoint(rPoint + rPerpendEdge);
711 const double fAngle(fabs(rPerpendPrev.angle(rPerpendEdge)));
713 if((M_PI - fAngle) < fMiterMinimumAngle)
724 if(
nullptr != pTriangles)
726 pTriangles->emplace_back(
733 aEdgePolygon.append(aEndPoint);
734 aEdgePolygon.append(rPoint);
735 aEdgePolygon.append(aStartPoint);
748 const B2DPoint aCutPoint(aStartPoint + (rTangentPrev * fCutPos));
750 if(
nullptr != pTriangles)
752 pTriangles->emplace_back(
759 aEdgePolygon.append(aCutPoint);
768 double fAngleStart(atan2(rPerpendPrev.getY(), rPerpendPrev.getX()));
769 double fAngleEnd(atan2(rPerpendEdge.getY(), rPerpendEdge.getX()));
772 if(fAngleStart < 0.0)
774 fAngleStart += 2 * M_PI;
779 fAngleEnd += 2 * M_PI;
786 if(
nullptr != pTriangles)
788 for(sal_uInt32
a(0);
a < aBow.count() - 1;
a++)
790 pTriangles->emplace_back(
791 0 == a ? aStartPoint : aBow.getB2DPoint(a),
793 aBow.count() - 1 == a + 1 ? aEndPoint : aBow.getB2DPoint(a + 1));
803 aEdgePolygon.setB2DPoint(0, aStartPoint);
804 aEdgePolygon.setB2DPoint(aEdgePolygon.count() - 1, aEndPoint);
805 aEdgePolygon.append(rPoint);
817 if(
nullptr != pTriangles)
819 pTriangles->emplace_back(
826 aEdgePolygon.append(aEndPoint);
827 aEdgePolygon.append(rPoint);
828 aEdgePolygon.append(aStartPoint);
836 aEdgePolygon.setClosed(
true);
846 double fHalfLineWidth,
848 css::drawing::LineCap eCap,
849 double fMaxAllowedAngle,
850 double fMaxPartOfEdge,
851 double fMiterMinimumAngle)
853 if(fMaxAllowedAngle > M_PI_2)
855 fMaxAllowedAngle = M_PI_2;
857 else if(fMaxAllowedAngle < 0.01 * M_PI_2)
859 fMaxAllowedAngle = 0.01 * M_PI_2;
862 if(fMaxPartOfEdge > 1.0)
864 fMaxPartOfEdge = 1.0;
866 else if(fMaxPartOfEdge < 0.01)
868 fMaxPartOfEdge = 0.01;
871 if(fMiterMinimumAngle > M_PI)
873 fMiterMinimumAngle = M_PI;
875 else if(fMiterMinimumAngle < 0.01 * M_PI)
877 fMiterMinimumAngle = 0.01 * M_PI;
881 const double fMaxCos(cos(fMaxAllowedAngle));
884 aCandidate = subdivideToSimple(aCandidate, fMaxCos * fMaxCos, fMaxPartOfEdge * fMaxPartOfEdge);
886 const sal_uInt32 nPointCount(aCandidate.
count());
891 const bool bIsClosed(aCandidate.
isClosed());
892 const sal_uInt32 nEdgeCount(bIsClosed ? nPointCount : nPointCount - 1);
893 const bool bLineCap(!bIsClosed && eCap != css::drawing::LineCap_BUTT);
904 if(bIsClosed && bEventuallyCreateLineJoin)
907 const sal_uInt32 nPrevIndex(nPointCount - 1);
914 for(sal_uInt32
a(0);
a < nEdgeCount;
a++)
917 const sal_uInt32 nNextIndex((
a + 1) % nPointCount);
923 if(bEventuallyCreateLineJoin && (bIsClosed ||
a != 0))
935 const double fAngle(fabs(aTangentPrev.
angle(aTangentEdge)));
952 createAreaGeometryForJoin(
969 createAreaGeometryForJoin(
983 const bool bLast(
a + 1 == nEdgeCount);
987 const bool bFirst(!
a);
990 createAreaGeometryForEdge(
993 bFirst && eCap == css::drawing::LineCap_ROUND,
994 bLast && eCap == css::drawing::LineCap_ROUND,
995 bFirst && eCap == css::drawing::LineCap_SQUARE,
996 bLast && eCap == css::drawing::LineCap_SQUARE,
1002 createAreaGeometryForEdge(
1015 if(bEventuallyCreateLineJoin)
void setStartPoint(const B2DPoint &rValue)
const B2DPoint & getStartPoint() const
void setControlPointA(const B2DPoint &rValue)
void setEndPoint(const B2DPoint &rValue)
void setControlPointB(const B2DPoint &rValue)
const B2DPoint & getEndPoint() const
B2DVector getTangent(double t) const
get the tangent in point t
void rotate(double fRadiant)
void translate(double fX, double fY)
void scale(double fX, double fY)
Base Point class with two double values.
void append(const B2DPolygon &rPolygon, sal_uInt32 nCount=1)
void transform(const basegfx::B2DHomMatrix &rMatrix)
void setClosed(bool bNew)
bool isClosed() const
closed state interface
basegfx::B2DPoint const & getB2DPoint(sal_uInt32 nIndex) const
Coordinate interface.
basegfx::B2DPoint getPrevControlPoint(sal_uInt32 nIndex) const
Basic ControlPoint interface.
void removeDoublePoints()
remove double points, at the begin/end and follow-ups, too
sal_uInt32 count() const
member count
basegfx::B2DPoint getNextControlPoint(sal_uInt32 nIndex) const
A two-dimensional interval over doubles.
B2DPoint getMaximum() const
get upper bound of the set. returns arbitrary values for empty sets.
B2DPoint getCenter() const
return center point of set. returns (0,0) for empty sets.
B2DPoint getMinimum() const
get lower bound of the set. returns arbitrary values for empty sets.
Base Point class with two double values.
double angle(const B2DVector &rVec) const
Calculate the Angle with another 2D Vector.
B2DVector & normalize()
Normalize this 2D Vector.
TYPE getWidth() const
return difference between upper and lower X value. returns 0 for empty sets.
TYPE getX() const
Get X-Coordinate of 2D Tuple.
TYPE getY() const
Get Y-Coordinate of 2D Tuple.
#define SAL_WARN_IF(condition, area, stream)
::std::vector< B2DTriangle > B2DTriangleVector
B2DTriangleVector triangulate(const B2DPolygon &rCandidate)
B2DPolygon const & createHalfUnitCircle()
create half circle centered on (0,0) from [0 .. M_PI]
CutFlagValue findCut(const B2DPoint &rEdge1Start, const B2DVector &rEdge1Delta, const B2DPoint &rEdge2Start, const B2DVector &rEdge2Delta, CutFlagValue aCutFlags, double *pCut1, double *pCut2)
double getLength(const B2DPolygon &rCandidate)
get length of polygon
B2DPolyPolygon createAreaGeometry(const B2DPolygon &rCandidate, double fHalfLineWidth, B2DLineJoin eJoin, css::drawing::LineCap eCap, double fMaxAllowedAngle, double fMaxPartOfEdge, double fMiterMinimumAngle)
create filled polygon geometry for lines with a line width
B2DPoint getPositionAbsolute(const B2DPolygon &rCandidate, double fDistance, double fLength)
B2VectorOrientation getOrientation(const B2DPolygon &rCandidate)
B2DPolygon createPolygonFromCircle(const B2DPoint &rCenter, double fRadius)
Create a circle polygon with given radius.
B2DPolygon createPolygonFromEllipseSegment(const B2DPoint &rCenter, double fRadiusX, double fRadiusY, double fStart, double fEnd)
Create a unit ellipse polygon with the given angles, from start to end.
B2DPolyPolygon solveCrossovers(const B2DPolyPolygon &rCandidate, size_t *pPointLimit)
Solve all crossovers (aka self-intersections) in a polyPolygon.
B2DHomMatrix createTranslateB2DHomMatrix(double fTranslateX, double fTranslateY)
B2DHomMatrix createScaleShearXRotateTranslateB2DHomMatrix(double fScaleX, double fScaleY, double fShearX, double fRadiant, double fTranslateX, double fTranslateY)
Tooling methods for faster completely combined matrix creation when scale, shearX,...
B2DRange getRange(const B2DPolygon &rCandidate)
Get the range of a polygon.
B2DPolyPolygon createAreaGeometryForLineStartEnd(const B2DPolygon &rCandidate, const B2DPolyPolygon &rArrow, bool bStart, double fWidth, double fCandidateLength, double fDockingPosition, double *pConsumedLength, double fShift)
Create line start/end geometry element, mostly arrows and things like that.
B2VectorOrientation
Descriptor for the mathematical orientations of two 2D Vectors.
@ Positive
mathematically positive oriented
@ Neutral
mathematically neutral, thus parallel
@ Negative
mathematically negative oriented
B2DVector getPerpendicular(const B2DVector &rNormalizedVec)
Calculate a perpendicular 2D Vector to the given one.
B2DLineJoin
Descriptor for possible line joins between two line segments.