Bump version to 6.4-15
[LibreOffice.git] / tools / source / generic / poly.cxx
blob0a597fdff1fc2bae4bf44b068c49acee9244e357
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <osl/endian.h>
21 #include <osl/diagnose.h>
22 #include <sal/log.hxx>
23 #include <tools/bigint.hxx>
24 #include <tools/debug.hxx>
25 #include <tools/helpers.hxx>
26 #include <tools/stream.hxx>
27 #include <tools/vcompat.hxx>
28 #include <tools/gen.hxx>
29 #include <poly.h>
30 #include <o3tl/safeint.hxx>
31 #include <tools/line.hxx>
32 #include <tools/poly.hxx>
33 #include <basegfx/polygon/b2dpolygon.hxx>
34 #include <basegfx/point/b2dpoint.hxx>
35 #include <basegfx/vector/b2dvector.hxx>
36 #include <basegfx/polygon/b2dpolygontools.hxx>
37 #include <basegfx/curve/b2dcubicbezier.hxx>
39 #include <memory>
40 #include <vector>
41 #include <iterator>
42 #include <algorithm>
43 #include <cstring>
44 #include <limits.h>
45 #include <cmath>
47 #define EDGE_LEFT 1
48 #define EDGE_TOP 2
49 #define EDGE_RIGHT 4
50 #define EDGE_BOTTOM 8
51 #define EDGE_HORZ (EDGE_RIGHT | EDGE_LEFT)
52 #define EDGE_VERT (EDGE_TOP | EDGE_BOTTOM)
53 #define SMALL_DVALUE 0.0000001
54 #define FSQRT2 1.4142135623730950488016887242097
56 static double ImplGetParameter( const Point& rCenter, const Point& rPt, double fWR, double fHR )
58 const long nDX = rPt.X() - rCenter.X();
59 double fAngle = atan2( -rPt.Y() + rCenter.Y(), ( ( nDX == 0 ) ? 0.000000001 : nDX ) );
61 return atan2(fWR*sin(fAngle), fHR*cos(fAngle));
64 ImplPolygon::ImplPolygon( sal_uInt16 nInitSize )
66 ImplInitSize(nInitSize, false);
69 ImplPolygon::ImplPolygon( const ImplPolygon& rImpPoly )
71 if ( rImpPoly.mnPoints )
73 mxPointAry.reset(new Point[rImpPoly.mnPoints]);
74 memcpy(mxPointAry.get(), rImpPoly.mxPointAry.get(), rImpPoly.mnPoints * sizeof(Point));
76 if( rImpPoly.mxFlagAry )
78 mxFlagAry.reset(new PolyFlags[rImpPoly.mnPoints]);
79 memcpy(mxFlagAry.get(), rImpPoly.mxFlagAry.get(), rImpPoly.mnPoints);
83 mnPoints = rImpPoly.mnPoints;
86 ImplPolygon::ImplPolygon( sal_uInt16 nInitSize, const Point* pInitAry, const PolyFlags* pInitFlags )
88 if ( nInitSize )
90 mxPointAry.reset(new Point[nInitSize]);
91 memcpy(mxPointAry.get(), pInitAry, nInitSize * sizeof(Point));
93 if( pInitFlags )
95 mxFlagAry.reset(new PolyFlags[nInitSize]);
96 memcpy(mxFlagAry.get(), pInitFlags, nInitSize);
100 mnPoints = nInitSize;
103 ImplPolygon::ImplPolygon( const tools::Rectangle& rRect )
105 if ( !rRect.IsEmpty() )
107 ImplInitSize(5);
108 mxPointAry[0] = rRect.TopLeft();
109 mxPointAry[1] = rRect.TopRight();
110 mxPointAry[2] = rRect.BottomRight();
111 mxPointAry[3] = rRect.BottomLeft();
112 mxPointAry[4] = rRect.TopLeft();
114 else
115 mnPoints = 0;
118 ImplPolygon::ImplPolygon( const tools::Rectangle& rRect, sal_uInt32 nHorzRound, sal_uInt32 nVertRound )
120 if ( !rRect.IsEmpty() )
122 tools::Rectangle aRect( rRect );
123 aRect.Justify(); // SJ: i9140
125 nHorzRound = std::min( nHorzRound, static_cast<sal_uInt32>(labs( aRect.GetWidth() >> 1 )) );
126 nVertRound = std::min( nVertRound, static_cast<sal_uInt32>(labs( aRect.GetHeight() >> 1 )) );
128 if( !nHorzRound && !nVertRound )
130 ImplInitSize(5);
131 mxPointAry[0] = aRect.TopLeft();
132 mxPointAry[1] = aRect.TopRight();
133 mxPointAry[2] = aRect.BottomRight();
134 mxPointAry[3] = aRect.BottomLeft();
135 mxPointAry[4] = aRect.TopLeft();
137 else
139 const Point aTL( aRect.Left() + nHorzRound, aRect.Top() + nVertRound );
140 const Point aTR( aRect.Right() - nHorzRound, aRect.Top() + nVertRound );
141 const Point aBR( aRect.Right() - nHorzRound, aRect.Bottom() - nVertRound );
142 const Point aBL( aRect.Left() + nHorzRound, aRect.Bottom() - nVertRound );
143 std::unique_ptr<tools::Polygon> pEllipsePoly( new tools::Polygon( Point(), nHorzRound, nVertRound ) );
144 sal_uInt16 i, nEnd, nSize4 = pEllipsePoly->GetSize() >> 2;
146 ImplInitSize((pEllipsePoly->GetSize() + 1));
148 const Point* pSrcAry = pEllipsePoly->GetConstPointAry();
149 Point* pDstAry = mxPointAry.get();
151 for( i = 0, nEnd = nSize4; i < nEnd; i++ )
152 pDstAry[ i ] = pSrcAry[ i ] + aTR;
154 for( nEnd = nEnd + nSize4; i < nEnd; i++ )
155 pDstAry[ i ] = pSrcAry[ i ] + aTL;
157 for( nEnd = nEnd + nSize4; i < nEnd; i++ )
158 pDstAry[ i ] = pSrcAry[ i ] + aBL;
160 for( nEnd = nEnd + nSize4; i < nEnd; i++ )
161 pDstAry[ i ] = pSrcAry[ i ] + aBR;
163 pDstAry[ nEnd ] = pDstAry[ 0 ];
166 else
167 mnPoints = 0;
170 ImplPolygon::ImplPolygon( const Point& rCenter, long nRadX, long nRadY )
172 if( nRadX && nRadY )
174 sal_uInt16 nPoints;
175 // Compute default (depends on size)
176 long nRadXY;
177 const bool bOverflow = o3tl::checked_multiply(nRadX, nRadY, nRadXY);
178 if (!bOverflow)
180 nPoints = static_cast<sal_uInt16>(MinMax(
181 ( F_PI * ( 1.5 * ( nRadX + nRadY ) -
182 sqrt( static_cast<double>(labs(nRadXY)) ) ) ),
183 32, 256 ));
185 else
187 nPoints = 256;
190 if( ( nRadX > 32 ) && ( nRadY > 32 ) && ( nRadX + nRadY ) < 8192 )
191 nPoints >>= 1;
193 // Ceil number of points until divisible by four
194 nPoints = (nPoints + 3) & ~3;
195 ImplInitSize(nPoints);
197 sal_uInt16 i;
198 sal_uInt16 nPoints2 = nPoints >> 1;
199 sal_uInt16 nPoints4 = nPoints >> 2;
200 double nAngle;
201 double nAngleStep = F_PI2 / ( nPoints4 - 1 );
203 for( i=0, nAngle = 0.0; i < nPoints4; i++, nAngle += nAngleStep )
205 long nX = FRound( nRadX * cos( nAngle ) );
206 long nY = FRound( -nRadY * sin( nAngle ) );
208 Point* pPt = &(mxPointAry[i]);
209 pPt->setX( nX + rCenter.X() );
210 pPt->setY( nY + rCenter.Y() );
211 pPt = &(mxPointAry[nPoints2-i-1]);
212 pPt->setX( -nX + rCenter.X() );
213 pPt->setY( nY + rCenter.Y() );
214 pPt = &(mxPointAry[i+nPoints2]);
215 pPt->setX( -nX + rCenter.X() );
216 pPt->setY( -nY + rCenter.Y() );
217 pPt = &(mxPointAry[nPoints-i-1]);
218 pPt->setX( nX + rCenter.X() );
219 pPt->setY( -nY + rCenter.Y() );
222 else
223 mnPoints = 0;
226 ImplPolygon::ImplPolygon( const tools::Rectangle& rBound, const Point& rStart, const Point& rEnd,
227 PolyStyle eStyle, bool bFullCircle )
229 const long nWidth = rBound.GetWidth();
230 const long nHeight = rBound.GetHeight();
232 if( ( nWidth > 1 ) && ( nHeight > 1 ) )
234 const Point aCenter( rBound.Center() );
235 const long nRadX = aCenter.X() - rBound.Left();
236 const long nRadY = aCenter.Y() - rBound.Top();
237 sal_uInt16 nPoints;
239 long nRadXY;
240 const bool bOverflow = o3tl::checked_multiply(nRadX, nRadY, nRadXY);
241 if (!bOverflow)
243 nPoints = static_cast<sal_uInt16>(MinMax(
244 ( F_PI * ( 1.5 * ( nRadX + nRadY ) -
245 sqrt( static_cast<double>(labs(nRadXY)) ) ) ),
246 32, 256 ));
248 else
250 nPoints = 256;
254 if( ( nRadX > 32 ) && ( nRadY > 32 ) && ( nRadX + nRadY ) < 8192 )
255 nPoints >>= 1;
257 // compute threshold
258 const double fRadX = nRadX;
259 const double fRadY = nRadY;
260 const double fCenterX = aCenter.X();
261 const double fCenterY = aCenter.Y();
262 double fStart = ImplGetParameter( aCenter, rStart, fRadX, fRadY );
263 double fEnd = ImplGetParameter( aCenter, rEnd, fRadX, fRadY );
264 double fDiff = fEnd - fStart;
265 double fStep;
266 sal_uInt16 nStart;
267 sal_uInt16 nEnd;
269 if( fDiff < 0. )
270 fDiff += F_2PI;
272 if ( bFullCircle )
273 fDiff = F_2PI;
275 // Proportionally shrink number of points( fDiff / (2PI) );
276 nPoints = std::max( static_cast<sal_uInt16>( ( fDiff * 0.1591549 ) * nPoints ), sal_uInt16(16) );
277 fStep = fDiff / ( nPoints - 1 );
279 if( PolyStyle::Pie == eStyle )
281 const Point aCenter2( FRound( fCenterX ), FRound( fCenterY ) );
283 nStart = 1;
284 nEnd = nPoints + 1;
285 ImplInitSize((nPoints + 2));
286 mxPointAry[0] = aCenter2;
287 mxPointAry[nEnd] = aCenter2;
289 else
291 ImplInitSize( ( PolyStyle::Chord == eStyle ) ? ( nPoints + 1 ) : nPoints );
292 nStart = 0;
293 nEnd = nPoints;
296 for(; nStart < nEnd; nStart++, fStart += fStep )
298 Point& rPt = mxPointAry[nStart];
300 rPt.setX( FRound( fCenterX + fRadX * cos( fStart ) ) );
301 rPt.setY( FRound( fCenterY - fRadY * sin( fStart ) ) );
304 if( PolyStyle::Chord == eStyle )
305 mxPointAry[nPoints] = mxPointAry[0];
307 else
308 mnPoints = 0;
311 ImplPolygon::ImplPolygon( const Point& rBezPt1, const Point& rCtrlPt1,
312 const Point& rBezPt2, const Point& rCtrlPt2, sal_uInt16 nPoints )
314 nPoints = ( 0 == nPoints ) ? 25 : ( ( nPoints < 2 ) ? 2 : nPoints );
316 const double fInc = 1.0 / ( nPoints - 1 );
317 double fK_1 = 0.0, fK1_1 = 1.0;
318 double fK_2, fK_3, fK1_2, fK1_3;
319 const double fX0 = rBezPt1.X();
320 const double fY0 = rBezPt1.Y();
321 const double fX1 = 3.0 * rCtrlPt1.X();
322 const double fY1 = 3.0 * rCtrlPt1.Y();
323 const double fX2 = 3.0 * rCtrlPt2.X();
324 const double fY2 = 3.0 * rCtrlPt2.Y();
325 const double fX3 = rBezPt2.X();
326 const double fY3 = rBezPt2.Y();
328 ImplInitSize(nPoints);
330 for( sal_uInt16 i = 0; i < nPoints; i++, fK_1 += fInc, fK1_1 -= fInc )
332 Point& rPt = mxPointAry[i];
334 fK_2 = fK_1;
335 fK_3 = ( fK_2 *= fK_1 );
336 fK_3 *= fK_1;
337 fK1_2 = fK1_1;
338 fK1_3 = ( fK1_2 *= fK1_1 );
339 fK1_3 *= fK1_1;
340 double fK12 = fK_1 * fK1_2;
341 double fK21 = fK_2 * fK1_1;
343 rPt.setX( FRound( fK1_3 * fX0 + fK12 * fX1 + fK21 * fX2 + fK_3 * fX3 ) );
344 rPt.setY( FRound( fK1_3 * fY0 + fK12 * fY1 + fK21 * fY2 + fK_3 * fY3 ) );
348 // constructor to convert from basegfx::B2DPolygon
349 // #i76891# Needed to change from adding all control points (even for unused
350 // edges) and creating a fixed-size Polygon in the first run to creating the
351 // minimal Polygon. This requires a temporary Point- and Flag-Array for curves
352 // and a memcopy at ImplPolygon creation, but contains no zero-controlpoints
353 // for straight edges.
354 ImplPolygon::ImplPolygon(const basegfx::B2DPolygon& rPolygon)
355 : mnPoints(0)
357 const bool bCurve(rPolygon.areControlPointsUsed());
358 const bool bClosed(rPolygon.isClosed());
359 sal_uInt32 nB2DLocalCount(rPolygon.count());
361 if(bCurve)
363 // #127979# Reduce source point count hard to the limit of the tools Polygon
364 if(nB2DLocalCount > ((0x0000ffff / 3) - 1))
366 OSL_FAIL("Polygon::Polygon: Too many points in given B2DPolygon, need to reduce hard to maximum of tools Polygon (!)");
367 nB2DLocalCount = ((0x0000ffff / 3) - 1);
370 // calculate target point count
371 const sal_uInt32 nLoopCount(bClosed ? nB2DLocalCount : (nB2DLocalCount ? nB2DLocalCount - 1 : 0 ));
373 if(nLoopCount)
375 // calculate maximum array size and allocate; prepare insert index
376 const sal_uInt32 nMaxTargetCount((nLoopCount * 3) + 1);
377 ImplInitSize(static_cast< sal_uInt16 >(nMaxTargetCount), true);
379 // prepare insert index and current point
380 sal_uInt32 nArrayInsert(0);
381 basegfx::B2DCubicBezier aBezier;
382 aBezier.setStartPoint(rPolygon.getB2DPoint(0));
384 for(sal_uInt32 a(0); a < nLoopCount; a++)
386 // add current point (always) and remember StartPointIndex for evtl. later corrections
387 const Point aStartPoint(FRound(aBezier.getStartPoint().getX()), FRound(aBezier.getStartPoint().getY()));
388 const sal_uInt32 nStartPointIndex(nArrayInsert);
389 mxPointAry[nStartPointIndex] = aStartPoint;
390 mxFlagAry[nStartPointIndex] = PolyFlags::Normal;
391 nArrayInsert++;
393 // prepare next segment
394 const sal_uInt32 nNextIndex((a + 1) % nB2DLocalCount);
395 aBezier.setEndPoint(rPolygon.getB2DPoint(nNextIndex));
396 aBezier.setControlPointA(rPolygon.getNextControlPoint(a));
397 aBezier.setControlPointB(rPolygon.getPrevControlPoint(nNextIndex));
399 if(aBezier.isBezier())
401 // if one is used, add always two control points due to the old schema
402 mxPointAry[nArrayInsert] = Point(FRound(aBezier.getControlPointA().getX()), FRound(aBezier.getControlPointA().getY()));
403 mxFlagAry[nArrayInsert] = PolyFlags::Control;
404 nArrayInsert++;
406 mxPointAry[nArrayInsert] = Point(FRound(aBezier.getControlPointB().getX()), FRound(aBezier.getControlPointB().getY()));
407 mxFlagAry[nArrayInsert] = PolyFlags::Control;
408 nArrayInsert++;
411 // test continuity with previous control point to set flag value
412 if(aBezier.getControlPointA() != aBezier.getStartPoint() && (bClosed || a))
414 const basegfx::B2VectorContinuity eCont(rPolygon.getContinuityInPoint(a));
416 if(basegfx::B2VectorContinuity::C1 == eCont)
418 mxFlagAry[nStartPointIndex] = PolyFlags::Smooth;
420 else if(basegfx::B2VectorContinuity::C2 == eCont)
422 mxFlagAry[nStartPointIndex] = PolyFlags::Symmetric;
426 // prepare next polygon step
427 aBezier.setStartPoint(aBezier.getEndPoint());
430 if(bClosed)
432 // add first point again as closing point due to old definition
433 mxPointAry[nArrayInsert] = mxPointAry[0];
434 mxFlagAry[nArrayInsert] = PolyFlags::Normal;
435 nArrayInsert++;
437 else
439 // add last point as closing point
440 const basegfx::B2DPoint aClosingPoint(rPolygon.getB2DPoint(nB2DLocalCount - 1));
441 const Point aEnd(FRound(aClosingPoint.getX()), FRound(aClosingPoint.getY()));
442 mxPointAry[nArrayInsert] = aEnd;
443 mxFlagAry[nArrayInsert] = PolyFlags::Normal;
444 nArrayInsert++;
447 DBG_ASSERT(nArrayInsert <= nMaxTargetCount, "Polygon::Polygon from basegfx::B2DPolygon: wrong max point count estimation (!)");
449 if(nArrayInsert != nMaxTargetCount)
451 ImplSetSize(static_cast< sal_uInt16 >(nArrayInsert));
455 else
457 // #127979# Reduce source point count hard to the limit of the tools Polygon
458 if(nB2DLocalCount > (0x0000ffff - 1))
460 OSL_FAIL("Polygon::Polygon: Too many points in given B2DPolygon, need to reduce hard to maximum of tools Polygon (!)");
461 nB2DLocalCount = (0x0000ffff - 1);
464 if(nB2DLocalCount)
466 // point list creation
467 const sal_uInt32 nTargetCount(nB2DLocalCount + (bClosed ? 1 : 0));
468 ImplInitSize(static_cast< sal_uInt16 >(nTargetCount));
469 sal_uInt16 nIndex(0);
471 for(sal_uInt32 a(0); a < nB2DLocalCount; a++)
473 basegfx::B2DPoint aB2DPoint(rPolygon.getB2DPoint(a));
474 Point aPoint(FRound(aB2DPoint.getX()), FRound(aB2DPoint.getY()));
475 mxPointAry[nIndex++] = aPoint;
478 if(bClosed)
480 // add first point as closing point
481 mxPointAry[nIndex] = mxPointAry[0];
487 bool ImplPolygon::operator==( const ImplPolygon& rCandidate) const
489 return mnPoints == rCandidate.mnPoints &&
490 mxFlagAry.get() == rCandidate.mxFlagAry.get() &&
491 mxPointAry.get() == rCandidate.mxPointAry.get();
494 void ImplPolygon::ImplInitSize(sal_uInt16 nInitSize, bool bFlags)
496 if (nInitSize)
498 mxPointAry.reset(new Point[nInitSize]);
501 if (bFlags)
503 mxFlagAry.reset(new PolyFlags[nInitSize]);
504 memset(mxFlagAry.get(), 0, nInitSize);
507 mnPoints = nInitSize;
510 void ImplPolygon::ImplSetSize( sal_uInt16 nNewSize, bool bResize )
512 if( mnPoints == nNewSize )
513 return;
515 std::unique_ptr<Point[]> xNewAry;
517 if (nNewSize)
519 const std::size_t nNewSz(static_cast<std::size_t>(nNewSize)*sizeof(Point));
520 xNewAry.reset(new Point[nNewSize]);
522 if ( bResize )
524 // Copy the old points
525 if ( mnPoints < nNewSize )
527 // New points are already implicitly initialized to zero
528 const std::size_t nOldSz(mnPoints * sizeof(Point));
529 if (mxPointAry)
530 memcpy(xNewAry.get(), mxPointAry.get(), nOldSz);
532 else
534 if (mxPointAry)
535 memcpy(xNewAry.get(), mxPointAry.get(), nNewSz);
540 mxPointAry = std::move(xNewAry);
542 // take FlagArray into account, if applicable
543 if( mxFlagAry )
545 std::unique_ptr<PolyFlags[]> xNewFlagAry;
547 if( nNewSize )
549 xNewFlagAry.reset(new PolyFlags[nNewSize]);
551 if( bResize )
553 // copy the old flags
554 if ( mnPoints < nNewSize )
556 // initialize new flags to zero
557 memset(xNewFlagAry.get() + mnPoints, 0, nNewSize-mnPoints);
558 memcpy(xNewFlagAry.get(), mxFlagAry.get(), mnPoints);
560 else
561 memcpy(xNewFlagAry.get(), mxFlagAry.get(), nNewSize);
565 mxFlagAry = std::move(xNewFlagAry);
568 mnPoints = nNewSize;
571 bool ImplPolygon::ImplSplit( sal_uInt16 nPos, sal_uInt16 nSpace, ImplPolygon const * pInitPoly )
573 //Can't fit this in :-(, throw ?
574 if (mnPoints + nSpace > USHRT_MAX)
576 SAL_WARN("tools", "Polygon needs " << mnPoints + nSpace << " points, but only " << USHRT_MAX << " possible");
577 return false;
580 const sal_uInt16 nNewSize = mnPoints + nSpace;
581 const std::size_t nSpaceSize = static_cast<std::size_t>(nSpace) * sizeof(Point);
583 if( nPos >= mnPoints )
585 // Append at the back
586 nPos = mnPoints;
587 ImplSetSize( nNewSize );
589 if( pInitPoly )
591 memcpy(mxPointAry.get() + nPos, pInitPoly->mxPointAry.get(), nSpaceSize);
593 if (pInitPoly->mxFlagAry)
594 memcpy(mxFlagAry.get() + nPos, pInitPoly->mxFlagAry.get(), nSpace);
597 else
599 const sal_uInt16 nSecPos = nPos + nSpace;
600 const sal_uInt16 nRest = mnPoints - nPos;
602 std::unique_ptr<Point[]> xNewAry(new Point[nNewSize]);
603 memcpy(xNewAry.get(), mxPointAry.get(), nPos * sizeof(Point));
605 if( pInitPoly )
606 memcpy(xNewAry.get() + nPos, pInitPoly->mxPointAry.get(), nSpaceSize);
608 memcpy(xNewAry.get() + nSecPos, mxPointAry.get() + nPos, nRest * sizeof(Point));
609 mxPointAry = std::move(xNewAry);
611 // consider FlagArray
612 if (mxFlagAry)
614 std::unique_ptr<PolyFlags[]> xNewFlagAry(new PolyFlags[nNewSize]);
616 memcpy(xNewFlagAry.get(), mxFlagAry.get(), nPos);
618 if (pInitPoly && pInitPoly->mxFlagAry)
619 memcpy(xNewFlagAry.get() + nPos, pInitPoly->mxFlagAry.get(), nSpace);
620 else
621 memset(xNewFlagAry.get() + nPos, 0, nSpace);
623 memcpy(xNewFlagAry.get() + nSecPos, mxFlagAry.get() + nPos, nRest);
624 mxFlagAry = std::move(xNewFlagAry);
627 mnPoints = nNewSize;
630 return true;
633 void ImplPolygon::ImplCreateFlagArray()
635 if (!mxFlagAry)
637 mxFlagAry.reset(new PolyFlags[mnPoints]);
638 memset(mxFlagAry.get(), 0, mnPoints);
642 class ImplPointFilter
644 public:
645 virtual void LastPoint() = 0;
646 virtual void Input( const Point& rPoint ) = 0;
648 protected:
649 ~ImplPointFilter() {}
652 class ImplPolygonPointFilter : public ImplPointFilter
654 ImplPolygon maPoly;
655 sal_uInt16 mnSize;
656 public:
657 explicit ImplPolygonPointFilter(sal_uInt16 nDestSize)
658 : maPoly(nDestSize)
659 , mnSize(0)
663 virtual ~ImplPolygonPointFilter()
667 virtual void LastPoint() override;
668 virtual void Input( const Point& rPoint ) override;
670 ImplPolygon& get() { return maPoly; }
673 void ImplPolygonPointFilter::Input( const Point& rPoint )
675 if ( !mnSize || (rPoint != maPoly.mxPointAry[mnSize-1]) )
677 mnSize++;
678 if ( mnSize > maPoly.mnPoints )
679 maPoly.ImplSetSize( mnSize );
680 maPoly.mxPointAry[mnSize-1] = rPoint;
684 void ImplPolygonPointFilter::LastPoint()
686 if ( mnSize < maPoly.mnPoints )
687 maPoly.ImplSetSize( mnSize );
690 class ImplEdgePointFilter : public ImplPointFilter
692 Point maFirstPoint;
693 Point maLastPoint;
694 ImplPointFilter& mrNextFilter;
695 const long mnLow;
696 const long mnHigh;
697 const int mnEdge;
698 int mnLastOutside;
699 bool mbFirst;
701 public:
702 ImplEdgePointFilter( int nEdge, long nLow, long nHigh,
703 ImplPointFilter& rNextFilter ) :
704 mrNextFilter( rNextFilter ),
705 mnLow( nLow ),
706 mnHigh( nHigh ),
707 mnEdge( nEdge ),
708 mnLastOutside( 0 ),
709 mbFirst( true )
713 virtual ~ImplEdgePointFilter() {}
715 Point EdgeSection( const Point& rPoint, int nEdge ) const;
716 int VisibleSide( const Point& rPoint ) const;
717 bool IsPolygon() const
718 { return maFirstPoint == maLastPoint; }
720 virtual void Input( const Point& rPoint ) override;
721 virtual void LastPoint() override;
724 inline int ImplEdgePointFilter::VisibleSide( const Point& rPoint ) const
726 if ( mnEdge & EDGE_HORZ )
728 return rPoint.X() < mnLow ? EDGE_LEFT :
729 rPoint.X() > mnHigh ? EDGE_RIGHT : 0;
731 else
733 return rPoint.Y() < mnLow ? EDGE_TOP :
734 rPoint.Y() > mnHigh ? EDGE_BOTTOM : 0;
738 Point ImplEdgePointFilter::EdgeSection( const Point& rPoint, int nEdge ) const
740 long lx = maLastPoint.X();
741 long ly = maLastPoint.Y();
742 long md = rPoint.X() - lx;
743 long mn = rPoint.Y() - ly;
744 long nNewX;
745 long nNewY;
747 if ( nEdge & EDGE_VERT )
749 nNewY = (nEdge == EDGE_TOP) ? mnLow : mnHigh;
750 long dy = nNewY - ly;
751 if ( !md )
752 nNewX = lx;
753 else if ( (LONG_MAX / std::abs(md)) >= std::abs(dy) )
754 nNewX = (dy * md) / mn + lx;
755 else
757 BigInt ady = dy;
758 ady *= md;
759 if( ady.IsNeg() )
760 if( mn < 0 )
761 ady += mn/2;
762 else
763 ady -= (mn-1)/2;
764 else
765 if( mn < 0 )
766 ady -= (mn+1)/2;
767 else
768 ady += mn/2;
769 ady /= mn;
770 nNewX = static_cast<long>(ady) + lx;
773 else
775 nNewX = (nEdge == EDGE_LEFT) ? mnLow : mnHigh;
776 long dx = nNewX - lx;
777 if ( !mn )
778 nNewY = ly;
779 else if ( (LONG_MAX / std::abs(mn)) >= std::abs(dx) )
780 nNewY = (dx * mn) / md + ly;
781 else
783 BigInt adx = dx;
784 adx *= mn;
785 if( adx.IsNeg() )
786 if( md < 0 )
787 adx += md/2;
788 else
789 adx -= (md-1)/2;
790 else
791 if( md < 0 )
792 adx -= (md+1)/2;
793 else
794 adx += md/2;
795 adx /= md;
796 nNewY = static_cast<long>(adx) + ly;
800 return Point( nNewX, nNewY );
803 void ImplEdgePointFilter::Input( const Point& rPoint )
805 int nOutside = VisibleSide( rPoint );
807 if ( mbFirst )
809 maFirstPoint = rPoint;
810 mbFirst = false;
811 if ( !nOutside )
812 mrNextFilter.Input( rPoint );
814 else if ( rPoint == maLastPoint )
815 return;
816 else if ( !nOutside )
818 if ( mnLastOutside )
819 mrNextFilter.Input( EdgeSection( rPoint, mnLastOutside ) );
820 mrNextFilter.Input( rPoint );
822 else if ( !mnLastOutside )
823 mrNextFilter.Input( EdgeSection( rPoint, nOutside ) );
824 else if ( nOutside != mnLastOutside )
826 mrNextFilter.Input( EdgeSection( rPoint, mnLastOutside ) );
827 mrNextFilter.Input( EdgeSection( rPoint, nOutside ) );
830 maLastPoint = rPoint;
831 mnLastOutside = nOutside;
834 void ImplEdgePointFilter::LastPoint()
836 if ( !mbFirst )
838 int nOutside = VisibleSide( maFirstPoint );
840 if ( nOutside != mnLastOutside )
841 Input( maFirstPoint );
842 mrNextFilter.LastPoint();
846 namespace tools
849 tools::Polygon Polygon::SubdivideBezier( const tools::Polygon& rPoly )
851 tools::Polygon aPoly;
853 // #100127# Use adaptive subdivide instead of fixed 25 segments
854 rPoly.AdaptiveSubdivide( aPoly );
856 return aPoly;
859 Polygon::Polygon() : mpImplPolygon(ImplPolygon())
863 Polygon::Polygon( sal_uInt16 nSize ) : mpImplPolygon(ImplPolygon(nSize))
867 Polygon::Polygon( sal_uInt16 nPoints, const Point* pPtAry, const PolyFlags* pFlagAry ) : mpImplPolygon(ImplPolygon(nPoints, pPtAry, pFlagAry))
871 Polygon::Polygon( const tools::Polygon& rPoly ) : mpImplPolygon(rPoly.mpImplPolygon)
875 Polygon::Polygon( tools::Polygon&& rPoly) noexcept
876 : mpImplPolygon(std::move(rPoly.mpImplPolygon))
880 Polygon::Polygon( const tools::Rectangle& rRect ) : mpImplPolygon(ImplPolygon(rRect))
884 Polygon::Polygon( const tools::Rectangle& rRect, sal_uInt32 nHorzRound, sal_uInt32 nVertRound )
885 : mpImplPolygon(ImplPolygon(rRect, nHorzRound, nVertRound))
889 Polygon::Polygon( const Point& rCenter, long nRadX, long nRadY )
890 : mpImplPolygon(ImplPolygon(rCenter, nRadX, nRadY))
894 Polygon::Polygon( const tools::Rectangle& rBound, const Point& rStart, const Point& rEnd,
895 PolyStyle eStyle, bool bFullCircle ) : mpImplPolygon(ImplPolygon(rBound, rStart, rEnd, eStyle, bFullCircle))
899 Polygon::Polygon( const Point& rBezPt1, const Point& rCtrlPt1,
900 const Point& rBezPt2, const Point& rCtrlPt2,
901 sal_uInt16 nPoints ) : mpImplPolygon(ImplPolygon(rBezPt1, rCtrlPt1, rBezPt2, rCtrlPt2, nPoints))
905 Polygon::~Polygon()
909 Point * Polygon::GetPointAry()
911 return mpImplPolygon->mxPointAry.get();
914 const Point* Polygon::GetConstPointAry() const
916 return mpImplPolygon->mxPointAry.get();
919 const PolyFlags* Polygon::GetConstFlagAry() const
921 return mpImplPolygon->mxFlagAry.get();
924 void Polygon::SetPoint( const Point& rPt, sal_uInt16 nPos )
926 DBG_ASSERT( nPos < mpImplPolygon->mnPoints,
927 "Polygon::SetPoint(): nPos >= nPoints" );
929 mpImplPolygon->mxPointAry[nPos] = rPt;
932 void Polygon::SetFlags( sal_uInt16 nPos, PolyFlags eFlags )
934 DBG_ASSERT( nPos < mpImplPolygon->mnPoints,
935 "Polygon::SetFlags(): nPos >= nPoints" );
937 // we do only want to create the flag array if there
938 // is at least one flag different to PolyFlags::Normal
939 if ( eFlags != PolyFlags::Normal )
941 mpImplPolygon->ImplCreateFlagArray();
942 mpImplPolygon->mxFlagAry[ nPos ] = eFlags;
946 const Point& Polygon::GetPoint( sal_uInt16 nPos ) const
948 DBG_ASSERT( nPos < mpImplPolygon->mnPoints,
949 "Polygon::GetPoint(): nPos >= nPoints" );
951 return mpImplPolygon->mxPointAry[nPos];
954 PolyFlags Polygon::GetFlags( sal_uInt16 nPos ) const
956 DBG_ASSERT( nPos < mpImplPolygon->mnPoints,
957 "Polygon::GetFlags(): nPos >= nPoints" );
958 return mpImplPolygon->mxFlagAry
959 ? mpImplPolygon->mxFlagAry[ nPos ]
960 : PolyFlags::Normal;
963 bool Polygon::HasFlags() const
965 return bool(mpImplPolygon->mxFlagAry);
968 bool Polygon::IsRect() const
970 bool bIsRect = false;
971 if (!mpImplPolygon->mxFlagAry)
973 if ( ( ( mpImplPolygon->mnPoints == 5 ) && ( mpImplPolygon->mxPointAry[ 0 ] == mpImplPolygon->mxPointAry[ 4 ] ) ) ||
974 ( mpImplPolygon->mnPoints == 4 ) )
976 if ( ( mpImplPolygon->mxPointAry[ 0 ].X() == mpImplPolygon->mxPointAry[ 3 ].X() ) &&
977 ( mpImplPolygon->mxPointAry[ 0 ].Y() == mpImplPolygon->mxPointAry[ 1 ].Y() ) &&
978 ( mpImplPolygon->mxPointAry[ 1 ].X() == mpImplPolygon->mxPointAry[ 2 ].X() ) &&
979 ( mpImplPolygon->mxPointAry[ 2 ].Y() == mpImplPolygon->mxPointAry[ 3 ].Y() ) )
980 bIsRect = true;
983 return bIsRect;
986 void Polygon::SetSize( sal_uInt16 nNewSize )
988 if( nNewSize != mpImplPolygon->mnPoints )
990 mpImplPolygon->ImplSetSize( nNewSize );
994 sal_uInt16 Polygon::GetSize() const
996 return mpImplPolygon->mnPoints;
999 void Polygon::Clear()
1001 mpImplPolygon = ImplType(ImplPolygon());
1004 double Polygon::CalcDistance( sal_uInt16 nP1, sal_uInt16 nP2 ) const
1006 DBG_ASSERT( nP1 < mpImplPolygon->mnPoints,
1007 "Polygon::CalcDistance(): nPos1 >= nPoints" );
1008 DBG_ASSERT( nP2 < mpImplPolygon->mnPoints,
1009 "Polygon::CalcDistance(): nPos2 >= nPoints" );
1011 const Point& rP1 = mpImplPolygon->mxPointAry[ nP1 ];
1012 const Point& rP2 = mpImplPolygon->mxPointAry[ nP2 ];
1013 const double fDx = rP2.X() - rP1.X();
1014 const double fDy = rP2.Y() - rP1.Y();
1016 return sqrt( fDx * fDx + fDy * fDy );
1019 void Polygon::Optimize( PolyOptimizeFlags nOptimizeFlags )
1021 DBG_ASSERT( !mpImplPolygon->mxFlagAry.get(), "Optimizing could fail with beziers!" );
1023 sal_uInt16 nSize = mpImplPolygon->mnPoints;
1025 if( bool(nOptimizeFlags) && nSize )
1027 if( nOptimizeFlags & PolyOptimizeFlags::EDGES )
1029 const tools::Rectangle aBound( GetBoundRect() );
1030 const double fArea = ( aBound.GetWidth() + aBound.GetHeight() ) * 0.5;
1031 const sal_uInt16 nPercent = 50;
1033 Optimize( PolyOptimizeFlags::NO_SAME );
1034 ImplReduceEdges( *this, fArea, nPercent );
1036 else if( nOptimizeFlags & PolyOptimizeFlags::NO_SAME )
1038 tools::Polygon aNewPoly;
1039 const Point& rFirst = mpImplPolygon->mxPointAry[ 0 ];
1041 while( nSize && ( mpImplPolygon->mxPointAry[ nSize - 1 ] == rFirst ) )
1042 nSize--;
1044 if( nSize > 1 )
1046 sal_uInt16 nLast = 0, nNewCount = 1;
1048 aNewPoly.SetSize( nSize );
1049 aNewPoly[ 0 ] = rFirst;
1051 for( sal_uInt16 i = 1; i < nSize; i++ )
1053 if( mpImplPolygon->mxPointAry[ i ] != mpImplPolygon->mxPointAry[ nLast ])
1055 nLast = i;
1056 aNewPoly[ nNewCount++ ] = mpImplPolygon->mxPointAry[ i ];
1060 if( nNewCount == 1 )
1061 aNewPoly.Clear();
1062 else
1063 aNewPoly.SetSize( nNewCount );
1066 *this = aNewPoly;
1069 nSize = mpImplPolygon->mnPoints;
1071 if( nSize > 1 )
1073 if( ( nOptimizeFlags & PolyOptimizeFlags::CLOSE ) &&
1074 ( mpImplPolygon->mxPointAry[ 0 ] != mpImplPolygon->mxPointAry[ nSize - 1 ] ) )
1076 SetSize( mpImplPolygon->mnPoints + 1 );
1077 mpImplPolygon->mxPointAry[ mpImplPolygon->mnPoints - 1 ] = mpImplPolygon->mxPointAry[ 0 ];
1084 /** Recursively subdivide cubic bezier curve via deCasteljau.
1086 @param rPointIter
1087 Output iterator, where the subdivided polylines are written to.
1089 @param d
1090 Squared difference of curve to a straight line
1092 @param P*
1093 Exactly four points, interpreted as support and control points of
1094 a cubic bezier curve. Must be in device coordinates, since stop
1095 criterion is based on the following assumption: the device has a
1096 finite resolution, it is thus sufficient to stop subdivision if the
1097 curve does not deviate more than one pixel from a straight line.
1100 static void ImplAdaptiveSubdivide( ::std::back_insert_iterator< ::std::vector< Point > >& rPointIter,
1101 const double old_d2,
1102 int recursionDepth,
1103 const double d2,
1104 const double P1x, const double P1y,
1105 const double P2x, const double P2y,
1106 const double P3x, const double P3y,
1107 const double P4x, const double P4y )
1109 // Hard limit on recursion depth, empiric number.
1110 enum {maxRecursionDepth=128};
1112 // Perform bezier flatness test (lecture notes from R. Schaback,
1113 // Mathematics of Computer-Aided Design, Uni Goettingen, 2000)
1115 // ||P(t) - L(t)|| <= max ||b_j - b_0 - j/n(b_n - b_0)||
1116 // 0<=j<=n
1118 // What is calculated here is an upper bound to the distance from
1119 // a line through b_0 and b_3 (P1 and P4 in our notation) and the
1120 // curve. We can drop 0 and n from the running indices, since the
1121 // argument of max becomes zero for those cases.
1122 const double fJ1x( P2x - P1x - 1.0/3.0*(P4x - P1x) );
1123 const double fJ1y( P2y - P1y - 1.0/3.0*(P4y - P1y) );
1124 const double fJ2x( P3x - P1x - 2.0/3.0*(P4x - P1x) );
1125 const double fJ2y( P3y - P1y - 2.0/3.0*(P4y - P1y) );
1126 const double distance2( ::std::max( fJ1x*fJ1x + fJ1y*fJ1y,
1127 fJ2x*fJ2x + fJ2y*fJ2y) );
1129 // stop if error measure does not improve anymore. This is a
1130 // safety guard against floating point inaccuracies.
1131 // stop at recursion level 128. This is a safety guard against
1132 // floating point inaccuracies.
1133 // stop if distance from line is guaranteed to be bounded by d
1134 if( old_d2 > d2 &&
1135 recursionDepth < maxRecursionDepth &&
1136 distance2 >= d2 )
1138 // deCasteljau bezier arc, split at t=0.5
1139 // Foley/vanDam, p. 508
1140 const double L1x( P1x ), L1y( P1y );
1141 const double L2x( (P1x + P2x)*0.5 ), L2y( (P1y + P2y)*0.5 );
1142 const double Hx ( (P2x + P3x)*0.5 ), Hy ( (P2y + P3y)*0.5 );
1143 const double L3x( (L2x + Hx)*0.5 ), L3y( (L2y + Hy)*0.5 );
1144 const double R4x( P4x ), R4y( P4y );
1145 const double R3x( (P3x + P4x)*0.5 ), R3y( (P3y + P4y)*0.5 );
1146 const double R2x( (Hx + R3x)*0.5 ), R2y( (Hy + R3y)*0.5 );
1147 const double R1x( (L3x + R2x)*0.5 ), R1y( (L3y + R2y)*0.5 );
1148 const double L4x( R1x ), L4y( R1y );
1150 // subdivide further
1151 ++recursionDepth;
1152 ImplAdaptiveSubdivide(rPointIter, distance2, recursionDepth, d2, L1x, L1y, L2x, L2y, L3x, L3y, L4x, L4y);
1153 ImplAdaptiveSubdivide(rPointIter, distance2, recursionDepth, d2, R1x, R1y, R2x, R2y, R3x, R3y, R4x, R4y);
1155 else
1157 // requested resolution reached.
1158 // Add end points to output iterator.
1159 // order is preserved, since this is so to say depth first traversal.
1160 *rPointIter++ = Point( FRound(P1x), FRound(P1y) );
1164 void Polygon::AdaptiveSubdivide( Polygon& rResult, const double d ) const
1166 if (!mpImplPolygon->mxFlagAry)
1168 rResult = *this;
1170 else
1172 sal_uInt16 i;
1173 sal_uInt16 nPts( GetSize() );
1174 ::std::vector< Point > aPoints;
1175 aPoints.reserve( nPts );
1176 ::std::back_insert_iterator< ::std::vector< Point > > aPointIter( aPoints );
1178 for(i=0; i<nPts;)
1180 if( ( i + 3 ) < nPts )
1182 PolyFlags P1( mpImplPolygon->mxFlagAry[ i ] );
1183 PolyFlags P4( mpImplPolygon->mxFlagAry[ i + 3 ] );
1185 if( ( PolyFlags::Normal == P1 || PolyFlags::Smooth == P1 || PolyFlags::Symmetric == P1 ) &&
1186 ( PolyFlags::Control == mpImplPolygon->mxFlagAry[ i + 1 ] ) &&
1187 ( PolyFlags::Control == mpImplPolygon->mxFlagAry[ i + 2 ] ) &&
1188 ( PolyFlags::Normal == P4 || PolyFlags::Smooth == P4 || PolyFlags::Symmetric == P4 ) )
1190 ImplAdaptiveSubdivide( aPointIter, d*d+1.0, 0, d*d,
1191 mpImplPolygon->mxPointAry[ i ].X(), mpImplPolygon->mxPointAry[ i ].Y(),
1192 mpImplPolygon->mxPointAry[ i+1 ].X(), mpImplPolygon->mxPointAry[ i+1 ].Y(),
1193 mpImplPolygon->mxPointAry[ i+2 ].X(), mpImplPolygon->mxPointAry[ i+2 ].Y(),
1194 mpImplPolygon->mxPointAry[ i+3 ].X(), mpImplPolygon->mxPointAry[ i+3 ].Y() );
1195 i += 3;
1196 continue;
1200 *aPointIter++ = mpImplPolygon->mxPointAry[ i++ ];
1202 if (aPoints.size() >= SAL_MAX_UINT16)
1204 OSL_ENSURE(aPoints.size() < SAL_MAX_UINT16,
1205 "Polygon::AdaptiveSubdivision created polygon too many points;"
1206 " using original polygon instead");
1208 // The resulting polygon can not hold all the points
1209 // that we have created so far. Stop the subdivision
1210 // and return a copy of the unmodified polygon.
1211 rResult = *this;
1212 return;
1216 // fill result polygon
1217 rResult = tools::Polygon( static_cast<sal_uInt16>(aPoints.size()) ); // ensure sufficient size for copy
1218 ::std::copy(aPoints.begin(), aPoints.end(), rResult.mpImplPolygon->mxPointAry.get());
1222 class Vector2D
1224 private:
1225 double mfX;
1226 double mfY;
1227 public:
1228 explicit Vector2D( const Point& rPoint ) : mfX( rPoint.X() ), mfY( rPoint.Y() ) {};
1229 double GetLength() const { return hypot( mfX, mfY ); }
1230 Vector2D& operator-=( const Vector2D& rVec ) { mfX -= rVec.mfX; mfY -= rVec.mfY; return *this; }
1231 double Scalar( const Vector2D& rVec ) const { return mfX * rVec.mfX + mfY * rVec.mfY ; }
1232 Vector2D& Normalize();
1233 bool IsPositive( Vector2D const & rVec ) const { return ( mfX * rVec.mfY - mfY * rVec.mfX ) >= 0.0; }
1234 bool IsNegative( Vector2D const & rVec ) const { return !IsPositive( rVec ); }
1236 Vector2D& Vector2D::Normalize()
1238 double fLen = Scalar( *this );
1240 if( ( fLen != 0.0 ) && ( fLen != 1.0 ) && ( ( fLen = sqrt( fLen ) ) != 0.0 ) )
1242 mfX /= fLen;
1243 mfY /= fLen;
1246 return *this;
1249 void Polygon::ImplReduceEdges( tools::Polygon& rPoly, const double& rArea, sal_uInt16 nPercent )
1251 const double fBound = 2000.0 * ( 100 - nPercent ) * 0.01;
1252 sal_uInt16 nNumNoChange = 0,
1253 nNumRuns = 0;
1255 while( nNumNoChange < 2 )
1257 sal_uInt16 nPntCnt = rPoly.GetSize(), nNewPos = 0;
1258 tools::Polygon aNewPoly( nPntCnt );
1259 bool bChangeInThisRun = false;
1261 for( sal_uInt16 n = 0; n < nPntCnt; n++ )
1263 bool bDeletePoint = false;
1265 if( ( n + nNumRuns ) % 2 )
1267 sal_uInt16 nIndPrev = !n ? nPntCnt - 1 : n - 1;
1268 sal_uInt16 nIndPrevPrev = !nIndPrev ? nPntCnt - 1 : nIndPrev - 1;
1269 sal_uInt16 nIndNext = ( n == nPntCnt-1 ) ? 0 : n + 1;
1270 sal_uInt16 nIndNextNext = ( nIndNext == nPntCnt - 1 ) ? 0 : nIndNext + 1;
1271 Vector2D aVec1( rPoly[ nIndPrev ] ); aVec1 -= Vector2D(rPoly[ nIndPrevPrev ]);
1272 Vector2D aVec2( rPoly[ n ] ); aVec2 -= Vector2D(rPoly[ nIndPrev ]);
1273 Vector2D aVec3( rPoly[ nIndNext ] ); aVec3 -= Vector2D(rPoly[ n ]);
1274 Vector2D aVec4( rPoly[ nIndNextNext ] ); aVec4 -= Vector2D(rPoly[ nIndNext ]);
1275 double fDist1 = aVec1.GetLength(), fDist2 = aVec2.GetLength();
1276 double fDist3 = aVec3.GetLength(), fDist4 = aVec4.GetLength();
1277 double fTurnB = aVec2.Normalize().Scalar( aVec3.Normalize() );
1279 if( fabs( fTurnB ) < ( 1.0 + SMALL_DVALUE ) && fabs( fTurnB ) > ( 1.0 - SMALL_DVALUE ) )
1280 bDeletePoint = true;
1281 else
1283 Vector2D aVecB( rPoly[ nIndNext ] );
1284 aVecB -= Vector2D(rPoly[ nIndPrev ] );
1285 double fDistB = aVecB.GetLength();
1286 double fLenWithB = fDist2 + fDist3;
1287 double fLenFact = ( fDistB != 0.0 ) ? fLenWithB / fDistB : 1.0;
1288 double fTurnPrev = aVec1.Normalize().Scalar( aVec2 );
1289 double fTurnNext = aVec3.Scalar( aVec4.Normalize() );
1290 double fGradPrev, fGradB, fGradNext;
1292 if( fabs( fTurnPrev ) < ( 1.0 + SMALL_DVALUE ) && fabs( fTurnPrev ) > ( 1.0 - SMALL_DVALUE ) )
1293 fGradPrev = 0.0;
1294 else
1295 fGradPrev = basegfx::rad2deg(acos(fTurnPrev)) * (aVec1.IsNegative(aVec2) ? -1 : 1);
1297 fGradB = basegfx::rad2deg(acos(fTurnB)) * (aVec2.IsNegative(aVec3) ? -1 : 1);
1299 if( fabs( fTurnNext ) < ( 1.0 + SMALL_DVALUE ) && fabs( fTurnNext ) > ( 1.0 - SMALL_DVALUE ) )
1300 fGradNext = 0.0;
1301 else
1302 fGradNext = basegfx::rad2deg(acos(fTurnNext)) * (aVec3.IsNegative(aVec4) ? -1 : 1);
1304 if( ( fGradPrev > 0.0 && fGradB < 0.0 && fGradNext > 0.0 ) ||
1305 ( fGradPrev < 0.0 && fGradB > 0.0 && fGradNext < 0.0 ) )
1307 if( ( fLenFact < ( FSQRT2 + SMALL_DVALUE ) ) &&
1308 ( ( ( fDist1 + fDist4 ) / ( fDist2 + fDist3 ) ) * 2000.0 ) > fBound )
1310 bDeletePoint = true;
1313 else
1315 double fRelLen = 1.0 - sqrt( fDistB / rArea );
1317 if( fRelLen < 0.0 )
1318 fRelLen = 0.0;
1319 else if( fRelLen > 1.0 )
1320 fRelLen = 1.0;
1322 if( ( std::round( ( fLenFact - 1.0 ) * 1000000.0 ) < fBound ) &&
1323 ( fabs( fGradB ) <= ( fRelLen * fBound * 0.01 ) ) )
1325 bDeletePoint = true;
1331 if( !bDeletePoint )
1332 aNewPoly[ nNewPos++ ] = rPoly[ n ];
1333 else
1334 bChangeInThisRun = true;
1337 if( bChangeInThisRun && nNewPos )
1339 aNewPoly.SetSize( nNewPos );
1340 rPoly = aNewPoly;
1341 nNumNoChange = 0;
1343 else
1344 nNumNoChange++;
1346 nNumRuns++;
1350 void Polygon::Move( long nHorzMove, long nVertMove )
1352 // This check is required for DrawEngine
1353 if ( !nHorzMove && !nVertMove )
1354 return;
1356 // Move points
1357 sal_uInt16 nCount = mpImplPolygon->mnPoints;
1358 for ( sal_uInt16 i = 0; i < nCount; i++ )
1360 Point& rPt = mpImplPolygon->mxPointAry[i];
1361 rPt.AdjustX(nHorzMove );
1362 rPt.AdjustY(nVertMove );
1366 void Polygon::Translate(const Point& rTrans)
1368 for ( sal_uInt16 i = 0, nCount = mpImplPolygon->mnPoints; i < nCount; i++ )
1369 mpImplPolygon->mxPointAry[ i ] += rTrans;
1372 void Polygon::Scale( double fScaleX, double fScaleY )
1374 for ( sal_uInt16 i = 0, nCount = mpImplPolygon->mnPoints; i < nCount; i++ )
1376 Point& rPnt = mpImplPolygon->mxPointAry[i];
1377 rPnt.setX( static_cast<long>( fScaleX * rPnt.X() ) );
1378 rPnt.setY( static_cast<long>( fScaleY * rPnt.Y() ) );
1382 void Polygon::Rotate( const Point& rCenter, sal_uInt16 nAngle10 )
1384 nAngle10 %= 3600;
1386 if( nAngle10 )
1388 const double fAngle = F_PI1800 * nAngle10;
1389 Rotate( rCenter, sin( fAngle ), cos( fAngle ) );
1393 void Polygon::Rotate( const Point& rCenter, double fSin, double fCos )
1395 long nCenterX = rCenter.X();
1396 long nCenterY = rCenter.Y();
1398 for( sal_uInt16 i = 0, nCount = mpImplPolygon->mnPoints; i < nCount; i++ )
1400 Point& rPt = mpImplPolygon->mxPointAry[ i ];
1402 const long nX = rPt.X() - nCenterX;
1403 const long nY = rPt.Y() - nCenterY;
1404 rPt.setX( FRound( fCos * nX + fSin * nY ) + nCenterX );
1405 rPt.setY( - FRound( fSin * nX - fCos * nY ) + nCenterY );
1409 void Polygon::Clip( const tools::Rectangle& rRect )
1411 // #105251# Justify rect before edge filtering
1412 tools::Rectangle aJustifiedRect( rRect );
1413 aJustifiedRect.Justify();
1415 sal_uInt16 nSourceSize = mpImplPolygon->mnPoints;
1416 ImplPolygonPointFilter aPolygon( nSourceSize );
1417 ImplEdgePointFilter aHorzFilter( EDGE_HORZ, aJustifiedRect.Left(), aJustifiedRect.Right(),
1418 aPolygon );
1419 ImplEdgePointFilter aVertFilter( EDGE_VERT, aJustifiedRect.Top(), aJustifiedRect.Bottom(),
1420 aHorzFilter );
1422 for ( sal_uInt16 i = 0; i < nSourceSize; i++ )
1423 aVertFilter.Input( mpImplPolygon->mxPointAry[i] );
1424 if ( aVertFilter.IsPolygon() )
1425 aVertFilter.LastPoint();
1426 else
1427 aPolygon.LastPoint();
1429 mpImplPolygon = ImplType(aPolygon.get());
1432 tools::Rectangle Polygon::GetBoundRect() const
1434 // Removing the assert. Bezier curves have the attribute that each single
1435 // curve segment defined by four points can not exit the four-point polygon
1436 // defined by that points. This allows to say that the curve segment can also
1437 // never leave the Range of its defining points.
1438 // The result is that Polygon::GetBoundRect() may not create the minimal
1439 // BoundRect of the Polygon (to get that, use basegfx::B2DPolygon classes),
1440 // but will always create a valid BoundRect, at least as long as this method
1441 // 'blindly' travels over all points, including control points.
1443 // DBG_ASSERT( !mpImplPolygon->mxFlagAry.get(), "GetBoundRect could fail with beziers!" );
1445 sal_uInt16 nCount = mpImplPolygon->mnPoints;
1446 if( ! nCount )
1447 return tools::Rectangle();
1449 long nXMin, nXMax, nYMin, nYMax;
1451 const Point& pFirstPt = mpImplPolygon->mxPointAry[0];
1452 nXMin = nXMax = pFirstPt.X();
1453 nYMin = nYMax = pFirstPt.Y();
1455 for ( sal_uInt16 i = 0; i < nCount; i++ )
1457 const Point& rPt = mpImplPolygon->mxPointAry[i];
1459 if (rPt.X() < nXMin)
1460 nXMin = rPt.X();
1461 if (rPt.X() > nXMax)
1462 nXMax = rPt.X();
1463 if (rPt.Y() < nYMin)
1464 nYMin = rPt.Y();
1465 if (rPt.Y() > nYMax)
1466 nYMax = rPt.Y();
1469 return tools::Rectangle( nXMin, nYMin, nXMax, nYMax );
1472 bool Polygon::IsInside( const Point& rPoint ) const
1474 DBG_ASSERT( !mpImplPolygon->mxFlagAry.get(), "IsInside could fail with beziers!" );
1476 const tools::Rectangle aBound( GetBoundRect() );
1477 const Line aLine( rPoint, Point( aBound.Right() + 100, rPoint.Y() ) );
1478 sal_uInt16 nCount = mpImplPolygon->mnPoints;
1479 sal_uInt16 nPCounter = 0;
1481 if ( ( nCount > 2 ) && aBound.IsInside( rPoint ) )
1483 Point aPt1( mpImplPolygon->mxPointAry[ 0 ] );
1484 Point aIntersection;
1485 Point aLastIntersection;
1487 while ( ( aPt1 == mpImplPolygon->mxPointAry[ nCount - 1 ] ) && ( nCount > 3 ) )
1488 nCount--;
1490 for ( sal_uInt16 i = 1; i <= nCount; i++ )
1492 const Point& rPt2 = mpImplPolygon->mxPointAry[ ( i < nCount ) ? i : 0 ];
1494 if ( aLine.Intersection( Line( aPt1, rPt2 ), aIntersection ) )
1496 // This avoids insertion of double intersections
1497 if ( nPCounter )
1499 if ( aIntersection != aLastIntersection )
1501 aLastIntersection = aIntersection;
1502 nPCounter++;
1505 else
1507 aLastIntersection = aIntersection;
1508 nPCounter++;
1512 aPt1 = rPt2;
1516 // is inside, if number of intersection points is odd
1517 return ( ( nPCounter & 1 ) == 1 );
1520 void Polygon::Insert( sal_uInt16 nPos, const Point& rPt )
1522 if( nPos >= mpImplPolygon->mnPoints )
1523 nPos = mpImplPolygon->mnPoints;
1525 if (mpImplPolygon->ImplSplit(nPos, 1))
1526 mpImplPolygon->mxPointAry[ nPos ] = rPt;
1529 void Polygon::Insert( sal_uInt16 nPos, const tools::Polygon& rPoly )
1531 const sal_uInt16 nInsertCount = rPoly.mpImplPolygon->mnPoints;
1533 if( nInsertCount )
1535 if( nPos >= mpImplPolygon->mnPoints )
1536 nPos = mpImplPolygon->mnPoints;
1538 if (rPoly.mpImplPolygon->mxFlagAry)
1539 mpImplPolygon->ImplCreateFlagArray();
1541 mpImplPolygon->ImplSplit( nPos, nInsertCount, rPoly.mpImplPolygon.get() );
1545 Point& Polygon::operator[]( sal_uInt16 nPos )
1547 DBG_ASSERT( nPos < mpImplPolygon->mnPoints, "Polygon::[]: nPos >= nPoints" );
1549 return mpImplPolygon->mxPointAry[nPos];
1552 tools::Polygon& Polygon::operator=( const tools::Polygon& rPoly )
1554 mpImplPolygon = rPoly.mpImplPolygon;
1555 return *this;
1558 tools::Polygon& Polygon::operator=( tools::Polygon&& rPoly ) noexcept
1560 mpImplPolygon = std::move(rPoly.mpImplPolygon);
1561 return *this;
1564 bool Polygon::operator==( const tools::Polygon& rPoly ) const
1566 return (mpImplPolygon == rPoly.mpImplPolygon);
1569 bool Polygon::IsEqual( const tools::Polygon& rPoly ) const
1571 bool bIsEqual = true;
1572 sal_uInt16 i;
1573 if ( GetSize() != rPoly.GetSize() )
1574 bIsEqual = false;
1575 else
1577 for ( i = 0; i < GetSize(); i++ )
1579 if ( ( GetPoint( i ) != rPoly.GetPoint( i ) ) ||
1580 ( GetFlags( i ) != rPoly.GetFlags( i ) ) )
1582 bIsEqual = false;
1583 break;
1587 return bIsEqual;
1590 SvStream& ReadPolygon( SvStream& rIStream, tools::Polygon& rPoly )
1592 sal_uInt16 i;
1593 sal_uInt16 nPoints(0);
1595 // read all points and create array
1596 rIStream.ReadUInt16( nPoints );
1598 const size_t nMaxRecordsPossible = rIStream.remainingSize() / (2 * sizeof(sal_Int32));
1599 if (nPoints > nMaxRecordsPossible)
1601 SAL_WARN("tools", "Polygon claims " << nPoints << " records, but only " << nMaxRecordsPossible << " possible");
1602 nPoints = nMaxRecordsPossible;
1605 rPoly.mpImplPolygon->ImplSetSize( nPoints, false );
1607 // Determine whether we need to write through operators
1608 #if (SAL_TYPES_SIZEOFLONG) == 4
1609 #ifdef OSL_BIGENDIAN
1610 if ( rIStream.GetEndian() == SvStreamEndian::BIG )
1611 #else
1612 if ( rIStream.GetEndian() == SvStreamEndian::LITTLE )
1613 #endif
1614 rIStream.ReadBytes(rPoly.mpImplPolygon->mxPointAry.get(), nPoints*sizeof(Point));
1615 else
1616 #endif
1618 for( i = 0; i < nPoints; i++ )
1620 sal_Int32 nTmpX(0), nTmpY(0);
1621 rIStream.ReadInt32( nTmpX ).ReadInt32( nTmpY );
1622 rPoly.mpImplPolygon->mxPointAry[i].setX( nTmpX );
1623 rPoly.mpImplPolygon->mxPointAry[i].setY( nTmpY );
1627 return rIStream;
1630 SvStream& WritePolygon( SvStream& rOStream, const tools::Polygon& rPoly )
1632 sal_uInt16 i;
1633 sal_uInt16 nPoints = rPoly.GetSize();
1635 // Write number of points
1636 rOStream.WriteUInt16( nPoints );
1638 // Determine whether we need to write through operators
1639 #if (SAL_TYPES_SIZEOFLONG) == 4
1640 #ifdef OSL_BIGENDIAN
1641 if ( rOStream.GetEndian() == SvStreamEndian::BIG )
1642 #else
1643 if ( rOStream.GetEndian() == SvStreamEndian::LITTLE )
1644 #endif
1646 if ( nPoints )
1647 rOStream.WriteBytes(rPoly.mpImplPolygon->mxPointAry.get(), nPoints*sizeof(Point));
1649 else
1650 #endif
1652 for( i = 0; i < nPoints; i++ )
1654 rOStream.WriteInt32( rPoly.mpImplPolygon->mxPointAry[i].X() )
1655 .WriteInt32( rPoly.mpImplPolygon->mxPointAry[i].Y() );
1659 return rOStream;
1662 void Polygon::ImplRead( SvStream& rIStream )
1664 sal_uInt8 bHasPolyFlags(0);
1666 ReadPolygon( rIStream, *this );
1667 rIStream.ReadUChar( bHasPolyFlags );
1669 if ( bHasPolyFlags )
1671 mpImplPolygon->mxFlagAry.reset(new PolyFlags[mpImplPolygon->mnPoints]);
1672 rIStream.ReadBytes(mpImplPolygon->mxFlagAry.get(), mpImplPolygon->mnPoints);
1676 void Polygon::Read( SvStream& rIStream )
1678 VersionCompat aCompat( rIStream, StreamMode::READ );
1680 ImplRead( rIStream );
1683 void Polygon::ImplWrite( SvStream& rOStream ) const
1685 bool bHasPolyFlags(mpImplPolygon->mxFlagAry);
1686 WritePolygon( rOStream, *this );
1687 rOStream.WriteBool(bHasPolyFlags);
1689 if ( bHasPolyFlags )
1690 rOStream.WriteBytes(mpImplPolygon->mxFlagAry.get(), mpImplPolygon->mnPoints);
1693 void Polygon::Write( SvStream& rOStream ) const
1695 VersionCompat aCompat( rOStream, StreamMode::WRITE, 1 );
1697 ImplWrite( rOStream );
1700 // #i74631#/#i115917# numerical correction method for B2DPolygon
1701 static void impCorrectContinuity(basegfx::B2DPolygon& roPolygon, sal_uInt32 nIndex, PolyFlags nCFlag)
1703 const sal_uInt32 nPointCount(roPolygon.count());
1704 OSL_ENSURE(nIndex < nPointCount, "impCorrectContinuity: index access out of range (!)");
1706 if(nIndex < nPointCount && (PolyFlags::Smooth == nCFlag || PolyFlags::Symmetric == nCFlag))
1708 if(roPolygon.isPrevControlPointUsed(nIndex) && roPolygon.isNextControlPointUsed(nIndex))
1710 // #i115917# Patch from osnola (modified, thanks for showing the problem)
1712 // The correction is needed because an integer polygon with control points
1713 // is converted to double precision. When C1 or C2 is used the involved vectors
1714 // may not have the same directions/lengths since these come from integer coordinates
1715 // and may have been snapped to different nearest integer coordinates. The snap error
1716 // is in the range of +-1 in y and y, thus 0.0 <= error <= sqrt(2.0). Nonetheless,
1717 // it needs to be corrected to be able to detect the continuity in this points
1718 // correctly.
1720 // We only have the integer data here (already in double precision form, but no mantisse
1721 // used), so the best correction is to use:
1723 // for C1: The longest vector since it potentially has best preserved the original vector.
1724 // Even better the sum of the vectors, weighted by their length. This gives the
1725 // normal vector addition to get the vector itself, lengths need to be preserved.
1726 // for C2: The mediated vector(s) since both should be the same, but mirrored
1728 // extract the point and vectors
1729 const basegfx::B2DPoint aPoint(roPolygon.getB2DPoint(nIndex));
1730 const basegfx::B2DVector aNext(roPolygon.getNextControlPoint(nIndex) - aPoint);
1731 const basegfx::B2DVector aPrev(aPoint - roPolygon.getPrevControlPoint(nIndex));
1733 // calculate common direction vector, normalize
1734 const basegfx::B2DVector aDirection(aNext + aPrev);
1735 const double fDirectionLen = aDirection.getLength();
1736 if (fDirectionLen == 0.0)
1737 return;
1739 if (PolyFlags::Smooth == nCFlag)
1741 // C1: apply common direction vector, preserve individual lengths
1742 const double fInvDirectionLen(1.0 / fDirectionLen);
1743 roPolygon.setNextControlPoint(nIndex, basegfx::B2DPoint(aPoint + (aDirection * (aNext.getLength() * fInvDirectionLen))));
1744 roPolygon.setPrevControlPoint(nIndex, basegfx::B2DPoint(aPoint - (aDirection * (aPrev.getLength() * fInvDirectionLen))));
1746 else // PolyFlags::Symmetric
1748 // C2: get mediated length. Taking half of the unnormalized direction would be
1749 // an approximation, but not correct.
1750 const double fMedLength((aNext.getLength() + aPrev.getLength()) * (0.5 / fDirectionLen));
1751 const basegfx::B2DVector aScaledDirection(aDirection * fMedLength);
1753 // Bring Direction to correct length and apply
1754 roPolygon.setNextControlPoint(nIndex, basegfx::B2DPoint(aPoint + aScaledDirection));
1755 roPolygon.setPrevControlPoint(nIndex, basegfx::B2DPoint(aPoint - aScaledDirection));
1761 // convert to basegfx::B2DPolygon and return
1762 basegfx::B2DPolygon Polygon::getB2DPolygon() const
1764 basegfx::B2DPolygon aRetval;
1765 const sal_uInt16 nCount(mpImplPolygon->mnPoints);
1767 if (nCount)
1769 if (mpImplPolygon->mxFlagAry)
1771 // handling for curves. Add start point
1772 const Point aStartPoint(mpImplPolygon->mxPointAry[0]);
1773 PolyFlags nPointFlag(mpImplPolygon->mxFlagAry[0]);
1774 aRetval.append(basegfx::B2DPoint(aStartPoint.X(), aStartPoint.Y()));
1775 Point aControlA, aControlB;
1777 for(sal_uInt16 a(1); a < nCount;)
1779 bool bControlA(false);
1780 bool bControlB(false);
1782 if(PolyFlags::Control == mpImplPolygon->mxFlagAry[a])
1784 aControlA = mpImplPolygon->mxPointAry[a++];
1785 bControlA = true;
1788 if(a < nCount && PolyFlags::Control == mpImplPolygon->mxFlagAry[a])
1790 aControlB = mpImplPolygon->mxPointAry[a++];
1791 bControlB = true;
1794 // assert invalid polygons
1795 OSL_ENSURE(bControlA == bControlB, "Polygon::getB2DPolygon: Invalid source polygon (!)");
1797 if(a < nCount)
1799 const Point aEndPoint(mpImplPolygon->mxPointAry[a]);
1801 if(bControlA)
1803 // bezier edge, add
1804 aRetval.appendBezierSegment(
1805 basegfx::B2DPoint(aControlA.X(), aControlA.Y()),
1806 basegfx::B2DPoint(aControlB.X(), aControlB.Y()),
1807 basegfx::B2DPoint(aEndPoint.X(), aEndPoint.Y()));
1809 impCorrectContinuity(aRetval, aRetval.count() - 2, nPointFlag);
1811 else
1813 // no bezier edge, add end point
1814 aRetval.append(basegfx::B2DPoint(aEndPoint.X(), aEndPoint.Y()));
1817 nPointFlag = mpImplPolygon->mxFlagAry[a++];
1821 // if exist, remove double first/last points, set closed and correct control points
1822 basegfx::utils::checkClosed(aRetval);
1824 if(aRetval.isClosed())
1826 // closeWithGeometryChange did really close, so last point(s) were removed.
1827 // Correct the continuity in the changed point
1828 impCorrectContinuity(aRetval, 0, mpImplPolygon->mxFlagAry[0]);
1831 else
1833 // extra handling for non-curves (most-used case) for speedup
1834 for(sal_uInt16 a(0); a < nCount; a++)
1836 // get point and add
1837 const Point aPoint(mpImplPolygon->mxPointAry[a]);
1838 aRetval.append(basegfx::B2DPoint(aPoint.X(), aPoint.Y()));
1841 // set closed flag
1842 basegfx::utils::checkClosed(aRetval);
1846 return aRetval;
1849 Polygon::Polygon(const basegfx::B2DPolygon& rPolygon) : mpImplPolygon(ImplPolygon(rPolygon))
1853 } // namespace tools
1855 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */