LibreOffice Module tools (master) 1
poly.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
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>
38
39#include <memory>
40#include <vector>
41#include <algorithm>
42#include <cassert>
43#include <cstring>
44#include <limits.h>
45#include <cmath>
46
47constexpr int EDGE_LEFT = 1;
48constexpr int EDGE_TOP = 2;
49constexpr int EDGE_RIGHT = 4;
50constexpr int EDGE_BOTTOM = 8;
51constexpr int EDGE_HORZ = EDGE_RIGHT | EDGE_LEFT;
52constexpr int EDGE_VERT = EDGE_TOP | EDGE_BOTTOM;
53constexpr double SMALL_DVALUE = 0.0000001;
54#define FSQRT2 1.4142135623730950488016887242097
55
56static double ImplGetParameter( const Point& rCenter, const Point& rPt, double fWR, double fHR )
57{
58 const double nDX = static_cast<double>(rPt.X()) - rCenter.X();
59 const double nDY = static_cast<double>(rCenter.Y()) - rPt.Y();
60 double fAngle = atan2(nDY, nDX);
61
62 return atan2(fWR*sin(fAngle), fHR*cos(fAngle));
63}
64
65ImplPolygon::ImplPolygon( sal_uInt16 nInitSize )
66{
67 ImplInitSize(nInitSize, false);
68}
69
71{
72 if ( rImpPoly.mnPoints )
73 {
74 mxPointAry.reset(new Point[rImpPoly.mnPoints]);
75 memcpy(mxPointAry.get(), rImpPoly.mxPointAry.get(), rImpPoly.mnPoints * sizeof(Point));
76
77 if( rImpPoly.mxFlagAry )
78 {
79 mxFlagAry.reset(new PolyFlags[rImpPoly.mnPoints]);
80 memcpy(mxFlagAry.get(), rImpPoly.mxFlagAry.get(), rImpPoly.mnPoints);
81 }
82 }
83
84 mnPoints = rImpPoly.mnPoints;
85}
86
87ImplPolygon::ImplPolygon( sal_uInt16 nInitSize, const Point* pInitAry, const PolyFlags* pInitFlags )
88{
89 if ( nInitSize )
90 {
91 mxPointAry.reset(new Point[nInitSize]);
92 memcpy(mxPointAry.get(), pInitAry, nInitSize * sizeof(Point));
93
94 if( pInitFlags )
95 {
96 mxFlagAry.reset(new PolyFlags[nInitSize]);
97 memcpy(mxFlagAry.get(), pInitFlags, nInitSize);
98 }
99 }
100
101 mnPoints = nInitSize;
102}
103
105{
106 if ( !rRect.IsEmpty() )
107 {
108 ImplInitSize(5);
109 mxPointAry[0] = rRect.TopLeft();
110 mxPointAry[1] = rRect.TopRight();
111 mxPointAry[2] = rRect.BottomRight();
112 mxPointAry[3] = rRect.BottomLeft();
113 mxPointAry[4] = rRect.TopLeft();
114 }
115 else
116 mnPoints = 0;
117}
118
119ImplPolygon::ImplPolygon( const tools::Rectangle& rRect, sal_uInt32 nHorzRound, sal_uInt32 nVertRound )
120{
121 if ( !rRect.IsEmpty() )
122 {
123 tools::Rectangle aRect( rRect );
124 aRect.Normalize(); // SJ: i9140
125
126 nHorzRound = std::min( nHorzRound, static_cast<sal_uInt32>(std::abs( aRect.GetWidth() >> 1 )) );
127 nVertRound = std::min( nVertRound, static_cast<sal_uInt32>(std::abs( aRect.GetHeight() >> 1 )) );
128
129 if( !nHorzRound && !nVertRound )
130 {
131 ImplInitSize(5);
132 mxPointAry[0] = aRect.TopLeft();
133 mxPointAry[1] = aRect.TopRight();
134 mxPointAry[2] = aRect.BottomRight();
135 mxPointAry[3] = aRect.BottomLeft();
136 mxPointAry[4] = aRect.TopLeft();
137 }
138 else
139 {
140 const Point aTL( aRect.Left() + nHorzRound, aRect.Top() + nVertRound );
141 const Point aTR( aRect.Right() - nHorzRound, aRect.Top() + nVertRound );
142 const Point aBR( aRect.Right() - nHorzRound, aRect.Bottom() - nVertRound );
143 const Point aBL( aRect.Left() + nHorzRound, aRect.Bottom() - nVertRound );
144 tools::Polygon aEllipsePoly( Point(), nHorzRound, nVertRound );
145 sal_uInt16 i, nEnd, nSize4 = aEllipsePoly.GetSize() >> 2;
146
147 ImplInitSize(aEllipsePoly.GetSize() + 1);
148
149 const Point* pSrcAry = aEllipsePoly.GetConstPointAry();
150 Point* pDstAry = mxPointAry.get();
151
152 for( i = 0, nEnd = nSize4; i < nEnd; i++ )
153 pDstAry[ i ] = pSrcAry[ i ] + aTR;
154
155 for( nEnd = nEnd + nSize4; i < nEnd; i++ )
156 pDstAry[ i ] = pSrcAry[ i ] + aTL;
157
158 for( nEnd = nEnd + nSize4; i < nEnd; i++ )
159 pDstAry[ i ] = pSrcAry[ i ] + aBL;
160
161 for( nEnd = nEnd + nSize4; i < nEnd; i++ )
162 pDstAry[ i ] = pSrcAry[ i ] + aBR;
163
164 pDstAry[ nEnd ] = pDstAry[ 0 ];
165 }
166 }
167 else
168 mnPoints = 0;
169}
170
172{
173 if( nRadX && nRadY )
174 {
175 sal_uInt16 nPoints;
176 // Compute default (depends on size)
177 tools::Long nRadXY;
178 const bool bOverflow = o3tl::checked_multiply(nRadX, nRadY, nRadXY);
179 if (!bOverflow)
180 {
181 nPoints = static_cast<sal_uInt16>(MinMax(
182 ( M_PI * ( 1.5 * ( nRadX + nRadY ) -
183 sqrt( static_cast<double>(std::abs(nRadXY)) ) ) ),
184 32, 256 ));
185 }
186 else
187 {
188 nPoints = 256;
189 }
190
191 if( ( nRadX > 32 ) && ( nRadY > 32 ) && ( nRadX + nRadY ) < 8192 )
192 nPoints >>= 1;
193
194 // Ceil number of points until divisible by four
195 nPoints = (nPoints + 3) & ~3;
196 ImplInitSize(nPoints);
197
198 sal_uInt16 i;
199 sal_uInt16 nPoints2 = nPoints >> 1;
200 sal_uInt16 nPoints4 = nPoints >> 2;
201 double nAngle;
202 double nAngleStep = M_PI_2 / ( nPoints4 - 1 );
203
204 for( i=0, nAngle = 0.0; i < nPoints4; i++, nAngle += nAngleStep )
205 {
206 tools::Long nX = FRound( nRadX * cos( nAngle ) );
207 tools::Long nY = FRound( -nRadY * sin( nAngle ) );
208
209 Point* pPt = &(mxPointAry[i]);
210 pPt->setX( nX + rCenter.X() );
211 pPt->setY( nY + rCenter.Y() );
212 pPt = &(mxPointAry[nPoints2-i-1]);
213 pPt->setX( -nX + rCenter.X() );
214 pPt->setY( nY + rCenter.Y() );
215 pPt = &(mxPointAry[i+nPoints2]);
216 pPt->setX( -nX + rCenter.X() );
217 pPt->setY( -nY + rCenter.Y() );
218 pPt = &(mxPointAry[nPoints-i-1]);
219 pPt->setX( nX + rCenter.X() );
220 pPt->setY( -nY + rCenter.Y() );
221 }
222 }
223 else
224 mnPoints = 0;
225}
226
227ImplPolygon::ImplPolygon(const tools::Rectangle& rBound, const Point& rStart, const Point& rEnd,
228 PolyStyle eStyle, const bool bClockWiseArcDirection)
229{
230 const auto nWidth = rBound.GetWidth();
231 const auto nHeight = rBound.GetHeight();
232
233 if ((nWidth != 0) && (nHeight != 0))
234 {
235 const Point aCenter(rBound.Center());
236 // tdf#142268 Get Top Left corner of rectangle (the rectangle is not always correctly created)
237 const auto aBoundLeft = rBound.Left() < aCenter.X() ? rBound.Left() : rBound.Right();
238 const auto aBoundTop = rBound.Top() < aCenter.Y() ? rBound.Top() : rBound.Bottom();
239 const auto nRadX = o3tl::saturating_sub(aCenter.X(), aBoundLeft);
240 const auto nRadY = o3tl::saturating_sub(aCenter.Y(), aBoundTop);
241 sal_uInt16 nPoints;
242
243 tools::Long nRadXY;
244 const bool bOverflow = o3tl::checked_multiply(nRadX, nRadY, nRadXY);
245 if (!bOverflow)
246 {
247 nPoints = static_cast<sal_uInt16>(MinMax(
248 ( M_PI * ( 1.5 * ( nRadX + nRadY ) -
249 sqrt( static_cast<double>(std::abs(nRadXY)) ) ) ),
250 32, 256 ));
251 }
252 else
253 {
254 nPoints = 256;
255 }
256
257
258 if (nRadX > 32 && nRadY > 32 && o3tl::saturating_add(nRadX, nRadY) < 8192)
259 nPoints >>= 1;
260
261 // compute threshold
262 const double fRadX = nRadX;
263 const double fRadY = nRadY;
264 const double fCenterX = aCenter.X();
265 const double fCenterY = aCenter.Y();
266 double fStart = ImplGetParameter( aCenter, rStart, fRadX, fRadY );
267 double fEnd = ImplGetParameter( aCenter, rEnd, fRadX, fRadY );
268 double fDiff = fEnd - fStart;
269 double fStep;
270 sal_uInt16 nStart;
271 sal_uInt16 nEnd;
272
273 if (bClockWiseArcDirection == false)
274 {
275 // #i73608# If startPoint is equal to endPoint, then draw full circle instead of nothing (as Metafiles spec)
276 if (fDiff <= 0.)
277 fDiff += 2. * M_PI;
278 }
279 else
280 {
281 fDiff = (2. * M_PI) - fDiff;
282 if (fDiff > 2. * M_PI)
283 fDiff -= 2. * M_PI;
284 }
285
286 // Proportionally shrink number of points( fDiff / (2PI) );
287 nPoints = std::max(static_cast<sal_uInt16>((fDiff / (2. * M_PI)) * nPoints), sal_uInt16(16));
288 fStep = fDiff / (nPoints - 1);
289 if (bClockWiseArcDirection == true)
290 fStep = -fStep;
291
292 if (PolyStyle::Pie == eStyle)
293 {
294 const Point aCenter2(FRound(fCenterX), FRound(fCenterY));
295
296 nStart = 1;
297 nEnd = nPoints + 1;
298 ImplInitSize(nPoints + 2);
299 mxPointAry[0] = aCenter2;
300 mxPointAry[nEnd] = aCenter2;
301 }
302 else
303 {
304 ImplInitSize( ( PolyStyle::Chord == eStyle ) ? ( nPoints + 1 ) : nPoints );
305 nStart = 0;
306 nEnd = nPoints;
307 }
308
309 for(; nStart < nEnd; nStart++, fStart += fStep )
310 {
311 Point& rPt = mxPointAry[nStart];
312
313 rPt.setX( FRound( fCenterX + fRadX * cos( fStart ) ) );
314 rPt.setY( FRound( fCenterY - fRadY * sin( fStart ) ) );
315 }
316
317 if( PolyStyle::Chord == eStyle )
318 mxPointAry[nPoints] = mxPointAry[0];
319 }
320 else
321 mnPoints = 0;
322}
323
324ImplPolygon::ImplPolygon( const Point& rBezPt1, const Point& rCtrlPt1,
325 const Point& rBezPt2, const Point& rCtrlPt2, sal_uInt16 nPoints )
326{
327 nPoints = ( 0 == nPoints ) ? 25 : ( ( nPoints < 2 ) ? 2 : nPoints );
328
329 const double fInc = 1.0 / ( nPoints - 1 );
330 double fK_1 = 0.0, fK1_1 = 1.0;
331 double fK_2, fK_3, fK1_2, fK1_3;
332 const double fX0 = rBezPt1.X();
333 const double fY0 = rBezPt1.Y();
334 const double fX1 = 3.0 * rCtrlPt1.X();
335 const double fY1 = 3.0 * rCtrlPt1.Y();
336 const double fX2 = 3.0 * rCtrlPt2.X();
337 const double fY2 = 3.0 * rCtrlPt2.Y();
338 const double fX3 = rBezPt2.X();
339 const double fY3 = rBezPt2.Y();
340
341 ImplInitSize(nPoints);
342
343 for( sal_uInt16 i = 0; i < nPoints; i++, fK_1 += fInc, fK1_1 -= fInc )
344 {
345 Point& rPt = mxPointAry[i];
346
347 fK_2 = fK_1;
348 fK_2 *= fK_1;
349 fK_3 = fK_2;
350 fK_3 *= fK_1;
351 fK1_2 = fK1_1;
352 fK1_2 *= fK1_1;
353 fK1_3 = fK1_2;
354 fK1_3 *= fK1_1;
355 double fK12 = fK_1 * fK1_2;
356 double fK21 = fK_2 * fK1_1;
357
358 rPt.setX( FRound( fK1_3 * fX0 + fK12 * fX1 + fK21 * fX2 + fK_3 * fX3 ) );
359 rPt.setY( FRound( fK1_3 * fY0 + fK12 * fY1 + fK21 * fY2 + fK_3 * fY3 ) );
360 }
361}
362
363// constructor to convert from basegfx::B2DPolygon
364// #i76891# Needed to change from adding all control points (even for unused
365// edges) and creating a fixed-size Polygon in the first run to creating the
366// minimal Polygon. This requires a temporary Point- and Flag-Array for curves
367// and a memcopy at ImplPolygon creation, but contains no zero-controlpoints
368// for straight edges.
370 : mnPoints(0)
371{
372 const bool bCurve(rPolygon.areControlPointsUsed());
373 const bool bClosed(rPolygon.isClosed());
374 sal_uInt32 nB2DLocalCount(rPolygon.count());
375
376 if(bCurve)
377 {
378 // #127979# Reduce source point count hard to the limit of the tools Polygon
379 if(nB2DLocalCount > ((0x0000ffff / 3) - 1))
380 {
381 OSL_FAIL("Polygon::Polygon: Too many points in given B2DPolygon, need to reduce hard to maximum of tools Polygon (!)");
382 nB2DLocalCount = ((0x0000ffff / 3) - 1);
383 }
384
385 // calculate target point count
386 const sal_uInt32 nLoopCount(bClosed ? nB2DLocalCount : (nB2DLocalCount ? nB2DLocalCount - 1 : 0 ));
387
388 if(nLoopCount)
389 {
390 // calculate maximum array size and allocate; prepare insert index
391 const sal_uInt32 nMaxTargetCount((nLoopCount * 3) + 1);
392 ImplInitSize(static_cast< sal_uInt16 >(nMaxTargetCount), true);
393
394 // prepare insert index and current point
395 sal_uInt32 nArrayInsert(0);
397 aBezier.setStartPoint(rPolygon.getB2DPoint(0));
398
399 for(sal_uInt32 a(0); a < nLoopCount; a++)
400 {
401 // add current point (always) and remember StartPointIndex for evtl. later corrections
402 const Point aStartPoint(FRound(aBezier.getStartPoint().getX()), FRound(aBezier.getStartPoint().getY()));
403 const sal_uInt32 nStartPointIndex(nArrayInsert);
404 mxPointAry[nStartPointIndex] = aStartPoint;
405 mxFlagAry[nStartPointIndex] = PolyFlags::Normal;
406 nArrayInsert++;
407
408 // prepare next segment
409 const sal_uInt32 nNextIndex((a + 1) % nB2DLocalCount);
410 aBezier.setEndPoint(rPolygon.getB2DPoint(nNextIndex));
411 aBezier.setControlPointA(rPolygon.getNextControlPoint(a));
412 aBezier.setControlPointB(rPolygon.getPrevControlPoint(nNextIndex));
413
414 if(aBezier.isBezier())
415 {
416 // if one is used, add always two control points due to the old schema
417 mxPointAry[nArrayInsert] = Point(FRound(aBezier.getControlPointA().getX()), FRound(aBezier.getControlPointA().getY()));
418 mxFlagAry[nArrayInsert] = PolyFlags::Control;
419 nArrayInsert++;
420
421 mxPointAry[nArrayInsert] = Point(FRound(aBezier.getControlPointB().getX()), FRound(aBezier.getControlPointB().getY()));
422 mxFlagAry[nArrayInsert] = PolyFlags::Control;
423 nArrayInsert++;
424 }
425
426 // test continuity with previous control point to set flag value
427 if(aBezier.getControlPointA() != aBezier.getStartPoint() && (bClosed || a))
428 {
430
432 {
433 mxFlagAry[nStartPointIndex] = PolyFlags::Smooth;
434 }
435 else if(basegfx::B2VectorContinuity::C2 == eCont)
436 {
437 mxFlagAry[nStartPointIndex] = PolyFlags::Symmetric;
438 }
439 }
440
441 // prepare next polygon step
442 aBezier.setStartPoint(aBezier.getEndPoint());
443 }
444
445 if(bClosed)
446 {
447 // add first point again as closing point due to old definition
448 mxPointAry[nArrayInsert] = mxPointAry[0];
449 mxFlagAry[nArrayInsert] = PolyFlags::Normal;
450 nArrayInsert++;
451 }
452 else
453 {
454 // add last point as closing point
455 const basegfx::B2DPoint aClosingPoint(rPolygon.getB2DPoint(nB2DLocalCount - 1));
456 const Point aEnd(FRound(aClosingPoint.getX()), FRound(aClosingPoint.getY()));
457 mxPointAry[nArrayInsert] = aEnd;
458 mxFlagAry[nArrayInsert] = PolyFlags::Normal;
459 nArrayInsert++;
460 }
461
462 DBG_ASSERT(nArrayInsert <= nMaxTargetCount, "Polygon::Polygon from basegfx::B2DPolygon: wrong max point count estimation (!)");
463
464 if(nArrayInsert != nMaxTargetCount)
465 {
466 ImplSetSize(static_cast< sal_uInt16 >(nArrayInsert));
467 }
468 }
469 }
470 else
471 {
472 // #127979# Reduce source point count hard to the limit of the tools Polygon
473 if(nB2DLocalCount > (0x0000ffff - 1))
474 {
475 OSL_FAIL("Polygon::Polygon: Too many points in given B2DPolygon, need to reduce hard to maximum of tools Polygon (!)");
476 nB2DLocalCount = (0x0000ffff - 1);
477 }
478
479 if(nB2DLocalCount)
480 {
481 // point list creation
482 const sal_uInt32 nTargetCount(nB2DLocalCount + (bClosed ? 1 : 0));
483 ImplInitSize(static_cast< sal_uInt16 >(nTargetCount));
484 sal_uInt16 nIndex(0);
485
486 for(sal_uInt32 a(0); a < nB2DLocalCount; a++)
487 {
488 basegfx::B2DPoint aB2DPoint(rPolygon.getB2DPoint(a));
489 Point aPoint(FRound(aB2DPoint.getX()), FRound(aB2DPoint.getY()));
490 mxPointAry[nIndex++] = aPoint;
491 }
492
493 if(bClosed)
494 {
495 // add first point as closing point
497 }
498 }
499 }
500}
501
502bool ImplPolygon::operator==( const ImplPolygon& rCandidate) const
503{
504 return mnPoints == rCandidate.mnPoints &&
505 mxFlagAry.get() == rCandidate.mxFlagAry.get() &&
506 mxPointAry.get() == rCandidate.mxPointAry.get();
507}
508
509void ImplPolygon::ImplInitSize(sal_uInt16 nInitSize, bool bFlags)
510{
511 if (nInitSize)
512 {
513 mxPointAry.reset(new Point[nInitSize]);
514 }
515
516 if (bFlags)
517 {
518 mxFlagAry.reset(new PolyFlags[nInitSize]);
519 memset(mxFlagAry.get(), 0, nInitSize);
520 }
521
522 mnPoints = nInitSize;
523}
524
525void ImplPolygon::ImplSetSize( sal_uInt16 nNewSize, bool bResize )
526{
527 if( mnPoints == nNewSize )
528 return;
529
530 std::unique_ptr<Point[]> xNewAry;
531
532 if (nNewSize)
533 {
534 const std::size_t nNewSz(static_cast<std::size_t>(nNewSize)*sizeof(Point));
535 xNewAry.reset(new Point[nNewSize]);
536
537 if ( bResize )
538 {
539 // Copy the old points
540 if ( mnPoints < nNewSize )
541 {
542 // New points are already implicitly initialized to zero
543 const std::size_t nOldSz(mnPoints * sizeof(Point));
544 if (mxPointAry)
545 memcpy(xNewAry.get(), mxPointAry.get(), nOldSz);
546 }
547 else
548 {
549 if (mxPointAry)
550 memcpy(xNewAry.get(), mxPointAry.get(), nNewSz);
551 }
552 }
553 }
554
555 mxPointAry = std::move(xNewAry);
556
557 // take FlagArray into account, if applicable
558 if( mxFlagAry )
559 {
560 std::unique_ptr<PolyFlags[]> xNewFlagAry;
561
562 if( nNewSize )
563 {
564 xNewFlagAry.reset(new PolyFlags[nNewSize]);
565
566 if( bResize )
567 {
568 // copy the old flags
569 if ( mnPoints < nNewSize )
570 {
571 // initialize new flags to zero
572 memset(xNewFlagAry.get() + mnPoints, 0, nNewSize-mnPoints);
573 memcpy(xNewFlagAry.get(), mxFlagAry.get(), mnPoints);
574 }
575 else
576 memcpy(xNewFlagAry.get(), mxFlagAry.get(), nNewSize);
577 }
578 }
579
580 mxFlagAry = std::move(xNewFlagAry);
581 }
582
583 mnPoints = nNewSize;
584}
585
586bool ImplPolygon::ImplSplit( sal_uInt16 nPos, sal_uInt16 nSpace, ImplPolygon const * pInitPoly )
587{
588 //Can't fit this in :-(, throw ?
589 if (mnPoints + nSpace > USHRT_MAX)
590 {
591 SAL_WARN("tools", "Polygon needs " << mnPoints + nSpace << " points, but only " << USHRT_MAX << " possible");
592 return false;
593 }
594
595 const sal_uInt16 nNewSize = mnPoints + nSpace;
596 const std::size_t nSpaceSize = static_cast<std::size_t>(nSpace) * sizeof(Point);
597
598 if( nPos >= mnPoints )
599 {
600 // Append at the back
601 nPos = mnPoints;
602 ImplSetSize( nNewSize );
603
604 if( pInitPoly )
605 {
606 memcpy(mxPointAry.get() + nPos, pInitPoly->mxPointAry.get(), nSpaceSize);
607
608 if (pInitPoly->mxFlagAry)
609 memcpy(mxFlagAry.get() + nPos, pInitPoly->mxFlagAry.get(), nSpace);
610 }
611 }
612 else
613 {
614 const sal_uInt16 nSecPos = nPos + nSpace;
615 const sal_uInt16 nRest = mnPoints - nPos;
616
617 std::unique_ptr<Point[]> xNewAry(new Point[nNewSize]);
618 memcpy(xNewAry.get(), mxPointAry.get(), nPos * sizeof(Point));
619
620 if( pInitPoly )
621 memcpy(xNewAry.get() + nPos, pInitPoly->mxPointAry.get(), nSpaceSize);
622
623 memcpy(xNewAry.get() + nSecPos, mxPointAry.get() + nPos, nRest * sizeof(Point));
624 mxPointAry = std::move(xNewAry);
625
626 // consider FlagArray
627 if (mxFlagAry)
628 {
629 std::unique_ptr<PolyFlags[]> xNewFlagAry(new PolyFlags[nNewSize]);
630
631 memcpy(xNewFlagAry.get(), mxFlagAry.get(), nPos);
632
633 if (pInitPoly && pInitPoly->mxFlagAry)
634 memcpy(xNewFlagAry.get() + nPos, pInitPoly->mxFlagAry.get(), nSpace);
635 else
636 memset(xNewFlagAry.get() + nPos, 0, nSpace);
637
638 memcpy(xNewFlagAry.get() + nSecPos, mxFlagAry.get() + nPos, nRest);
639 mxFlagAry = std::move(xNewFlagAry);
640 }
641
642 mnPoints = nNewSize;
643 }
644
645 return true;
646}
647
649{
650 if (!mxFlagAry)
651 {
652 mxFlagAry.reset(new PolyFlags[mnPoints]);
653 memset(mxFlagAry.get(), 0, mnPoints);
654 }
655}
656
657namespace {
658
659class ImplPointFilter
660{
661public:
662 virtual void LastPoint() = 0;
663 virtual void Input( const Point& rPoint ) = 0;
664
665protected:
666 ~ImplPointFilter() {}
667};
668
669class ImplPolygonPointFilter : public ImplPointFilter
670{
671 ImplPolygon maPoly;
672 sal_uInt16 mnSize;
673public:
674 explicit ImplPolygonPointFilter(sal_uInt16 nDestSize)
675 : maPoly(nDestSize)
676 , mnSize(0)
677 {
678 }
679
680 virtual ~ImplPolygonPointFilter()
681 {
682 }
683
684 virtual void LastPoint() override;
685 virtual void Input( const Point& rPoint ) override;
686
687 ImplPolygon& get() { return maPoly; }
688};
689
690}
691
692void ImplPolygonPointFilter::Input( const Point& rPoint )
693{
694 if ( !mnSize || (rPoint != maPoly.mxPointAry[mnSize-1]) )
695 {
696 mnSize++;
697 if ( mnSize > maPoly.mnPoints )
698 maPoly.ImplSetSize( mnSize );
699 maPoly.mxPointAry[mnSize-1] = rPoint;
700 }
701}
702
703void ImplPolygonPointFilter::LastPoint()
704{
705 if ( mnSize < maPoly.mnPoints )
706 maPoly.ImplSetSize( mnSize );
707};
708
709namespace {
710
711class ImplEdgePointFilter : public ImplPointFilter
712{
713 Point maFirstPoint;
714 Point maLastPoint;
715 ImplPointFilter& mrNextFilter;
716 const tools::Long mnLow;
717 const tools::Long mnHigh;
718 const int mnEdge;
719 int mnLastOutside;
720 bool mbFirst;
721
722public:
723 ImplEdgePointFilter( int nEdge, tools::Long nLow, tools::Long nHigh,
724 ImplPointFilter& rNextFilter ) :
725 mrNextFilter( rNextFilter ),
726 mnLow( nLow ),
727 mnHigh( nHigh ),
728 mnEdge( nEdge ),
729 mnLastOutside( 0 ),
730 mbFirst( true )
731 {
732 }
733
734 virtual ~ImplEdgePointFilter() {}
735
736 Point EdgeSection( const Point& rPoint, int nEdge ) const;
737 int VisibleSide( const Point& rPoint ) const;
738 bool IsPolygon() const
739 { return maFirstPoint == maLastPoint; }
740
741 virtual void Input( const Point& rPoint ) override;
742 virtual void LastPoint() override;
743};
744
745}
746
747inline int ImplEdgePointFilter::VisibleSide( const Point& rPoint ) const
748{
749 if ( mnEdge & EDGE_HORZ )
750 {
751 return rPoint.X() < mnLow ? EDGE_LEFT :
752 rPoint.X() > mnHigh ? EDGE_RIGHT : 0;
753 }
754 else
755 {
756 return rPoint.Y() < mnLow ? EDGE_TOP :
757 rPoint.Y() > mnHigh ? EDGE_BOTTOM : 0;
758 }
759}
760
761Point ImplEdgePointFilter::EdgeSection( const Point& rPoint, int nEdge ) const
762{
763 tools::Long lx = maLastPoint.X();
764 tools::Long ly = maLastPoint.Y();
765 tools::Long md = rPoint.X() - lx;
766 tools::Long mn = rPoint.Y() - ly;
767 tools::Long nNewX;
768 tools::Long nNewY;
769
770 if ( nEdge & EDGE_VERT )
771 {
772 nNewY = (nEdge == EDGE_TOP) ? mnLow : mnHigh;
773 tools::Long dy = nNewY - ly;
774 if ( !md )
775 nNewX = lx;
776 else if ( (LONG_MAX / std::abs(md)) >= std::abs(dy) )
777 nNewX = (dy * md) / mn + lx;
778 else
779 {
780 BigInt ady = dy;
781 ady *= md;
782 if( ady.IsNeg() )
783 if( mn < 0 )
784 ady += mn/2;
785 else
786 ady -= (mn-1)/2;
787 else
788 if( mn < 0 )
789 ady -= (mn+1)/2;
790 else
791 ady += mn/2;
792 ady /= mn;
793 nNewX = static_cast<tools::Long>(ady) + lx;
794 }
795 }
796 else
797 {
798 nNewX = (nEdge == EDGE_LEFT) ? mnLow : mnHigh;
799 tools::Long dx = nNewX - lx;
800 if ( !mn )
801 nNewY = ly;
802 else if ( (LONG_MAX / std::abs(mn)) >= std::abs(dx) )
803 nNewY = (dx * mn) / md + ly;
804 else
805 {
806 BigInt adx = dx;
807 adx *= mn;
808 if( adx.IsNeg() )
809 if( md < 0 )
810 adx += md/2;
811 else
812 adx -= (md-1)/2;
813 else
814 if( md < 0 )
815 adx -= (md+1)/2;
816 else
817 adx += md/2;
818 adx /= md;
819 nNewY = static_cast<tools::Long>(adx) + ly;
820 }
821 }
822
823 return Point( nNewX, nNewY );
824}
825
826void ImplEdgePointFilter::Input( const Point& rPoint )
827{
828 int nOutside = VisibleSide( rPoint );
829
830 if ( mbFirst )
831 {
832 maFirstPoint = rPoint;
833 mbFirst = false;
834 if ( !nOutside )
835 mrNextFilter.Input( rPoint );
836 }
837 else if ( rPoint == maLastPoint )
838 return;
839 else if ( !nOutside )
840 {
841 if ( mnLastOutside )
842 mrNextFilter.Input( EdgeSection( rPoint, mnLastOutside ) );
843 mrNextFilter.Input( rPoint );
844 }
845 else if ( !mnLastOutside )
846 mrNextFilter.Input( EdgeSection( rPoint, nOutside ) );
847 else if ( nOutside != mnLastOutside )
848 {
849 mrNextFilter.Input( EdgeSection( rPoint, mnLastOutside ) );
850 mrNextFilter.Input( EdgeSection( rPoint, nOutside ) );
851 }
852
853 maLastPoint = rPoint;
854 mnLastOutside = nOutside;
855}
856
857void ImplEdgePointFilter::LastPoint()
858{
859 if ( !mbFirst )
860 {
861 int nOutside = VisibleSide( maFirstPoint );
862
863 if ( nOutside != mnLastOutside )
864 Input( maFirstPoint );
865 mrNextFilter.LastPoint();
866 }
867}
868
869namespace tools
870{
871
873{
874 tools::Polygon aPoly;
875
876 // #100127# Use adaptive subdivide instead of fixed 25 segments
877 rPoly.AdaptiveSubdivide( aPoly );
878
879 return aPoly;
880}
881
882Polygon::Polygon() : mpImplPolygon(ImplPolygon())
883{
884}
885
886Polygon::Polygon( sal_uInt16 nSize ) : mpImplPolygon(ImplPolygon(nSize))
887{
888}
889
890Polygon::Polygon( sal_uInt16 nPoints, const Point* pPtAry, const PolyFlags* pFlagAry ) : mpImplPolygon(ImplPolygon(nPoints, pPtAry, pFlagAry))
891{
892}
893
894Polygon::Polygon( const tools::Polygon& rPoly ) : mpImplPolygon(rPoly.mpImplPolygon)
895{
896}
897
899 : mpImplPolygon(std::move(rPoly.mpImplPolygon))
900{
901}
902
903Polygon::Polygon( const tools::Rectangle& rRect ) : mpImplPolygon(ImplPolygon(rRect))
904{
905}
906
907Polygon::Polygon( const tools::Rectangle& rRect, sal_uInt32 nHorzRound, sal_uInt32 nVertRound )
908 : mpImplPolygon(ImplPolygon(rRect, nHorzRound, nVertRound))
909{
910}
911
912Polygon::Polygon( const Point& rCenter, tools::Long nRadX, tools::Long nRadY )
913 : mpImplPolygon(ImplPolygon(rCenter, nRadX, nRadY))
914{
915}
916
917Polygon::Polygon(const tools::Rectangle& rBound, const Point& rStart, const Point& rEnd,
918 PolyStyle eStyle, const bool bClockWiseArcDirection)
919 : mpImplPolygon(ImplPolygon(rBound, rStart, rEnd, eStyle, bClockWiseArcDirection))
920{
921}
922
923Polygon::Polygon( const Point& rBezPt1, const Point& rCtrlPt1,
924 const Point& rBezPt2, const Point& rCtrlPt2,
925 sal_uInt16 nPoints ) : mpImplPolygon(ImplPolygon(rBezPt1, rCtrlPt1, rBezPt2, rCtrlPt2, nPoints))
926{
927}
928
930{
931}
932
934{
935 return mpImplPolygon->mxPointAry.get();
936}
937
939{
940 return mpImplPolygon->mxPointAry.get();
941}
942
944{
945 return mpImplPolygon->mxFlagAry.get();
946}
947
948void Polygon::SetPoint( const Point& rPt, sal_uInt16 nPos )
949{
950 DBG_ASSERT( nPos < mpImplPolygon->mnPoints,
951 "Polygon::SetPoint(): nPos >= nPoints" );
952
953 mpImplPolygon->mxPointAry[nPos] = rPt;
954}
955
956void Polygon::SetFlags( sal_uInt16 nPos, PolyFlags eFlags )
957{
958 DBG_ASSERT( nPos < mpImplPolygon->mnPoints,
959 "Polygon::SetFlags(): nPos >= nPoints" );
960
961 // we do only want to create the flag array if there
962 // is at least one flag different to PolyFlags::Normal
963 if ( eFlags != PolyFlags::Normal )
964 {
965 mpImplPolygon->ImplCreateFlagArray();
966 mpImplPolygon->mxFlagAry[ nPos ] = eFlags;
967 }
968}
969
970const Point& Polygon::GetPoint( sal_uInt16 nPos ) const
971{
972 DBG_ASSERT( nPos < mpImplPolygon->mnPoints,
973 "Polygon::GetPoint(): nPos >= nPoints" );
974
975 return mpImplPolygon->mxPointAry[nPos];
976}
977
978PolyFlags Polygon::GetFlags( sal_uInt16 nPos ) const
979{
980 DBG_ASSERT( nPos < mpImplPolygon->mnPoints,
981 "Polygon::GetFlags(): nPos >= nPoints" );
982 return mpImplPolygon->mxFlagAry
983 ? mpImplPolygon->mxFlagAry[ nPos ]
985}
986
988{
989 return bool(mpImplPolygon->mxFlagAry);
990}
991
992bool Polygon::IsRect() const
993{
994 bool bIsRect = false;
995 if (!mpImplPolygon->mxFlagAry)
996 {
997 if ( ( ( mpImplPolygon->mnPoints == 5 ) && ( mpImplPolygon->mxPointAry[ 0 ] == mpImplPolygon->mxPointAry[ 4 ] ) ) ||
998 ( mpImplPolygon->mnPoints == 4 ) )
999 {
1000 if ( ( mpImplPolygon->mxPointAry[ 0 ].X() == mpImplPolygon->mxPointAry[ 3 ].X() ) &&
1001 ( mpImplPolygon->mxPointAry[ 0 ].Y() == mpImplPolygon->mxPointAry[ 1 ].Y() ) &&
1002 ( mpImplPolygon->mxPointAry[ 1 ].X() == mpImplPolygon->mxPointAry[ 2 ].X() ) &&
1003 ( mpImplPolygon->mxPointAry[ 2 ].Y() == mpImplPolygon->mxPointAry[ 3 ].Y() ) )
1004 bIsRect = true;
1005 }
1006 }
1007 return bIsRect;
1008}
1009
1010void Polygon::SetSize( sal_uInt16 nNewSize )
1011{
1012 if( nNewSize != mpImplPolygon->mnPoints )
1013 {
1014 mpImplPolygon->ImplSetSize( nNewSize );
1015 }
1016}
1017
1018sal_uInt16 Polygon::GetSize() const
1019{
1020 return mpImplPolygon->mnPoints;
1021}
1022
1024{
1026}
1027
1028double Polygon::CalcDistance( sal_uInt16 nP1, sal_uInt16 nP2 ) const
1029{
1030 DBG_ASSERT( nP1 < mpImplPolygon->mnPoints,
1031 "Polygon::CalcDistance(): nPos1 >= nPoints" );
1032 DBG_ASSERT( nP2 < mpImplPolygon->mnPoints,
1033 "Polygon::CalcDistance(): nPos2 >= nPoints" );
1034
1035 const Point& rP1 = mpImplPolygon->mxPointAry[ nP1 ];
1036 const Point& rP2 = mpImplPolygon->mxPointAry[ nP2 ];
1037 const double fDx = rP2.X() - rP1.X();
1038 const double fDy = rP2.Y() - rP1.Y();
1039
1040 return std::hypot( fDx, fDy );
1041}
1042
1044{
1045 sal_uInt16 nSize = mpImplPolygon->mnPoints;
1046
1047 if( !(bool(nOptimizeFlags) && nSize) )
1048 return;
1049
1050 if( nOptimizeFlags & PolyOptimizeFlags::EDGES )
1051 {
1052 const tools::Rectangle aBound( GetBoundRect() );
1053 const double fArea = ( aBound.GetWidth() + aBound.GetHeight() ) * 0.5;
1054 const sal_uInt16 nPercent = 50;
1055
1057 ImplReduceEdges( *this, fArea, nPercent );
1058 }
1059 else if( nOptimizeFlags & PolyOptimizeFlags::NO_SAME )
1060 {
1061 tools::Polygon aNewPoly;
1062 const Point& rFirst = mpImplPolygon->mxPointAry[ 0 ];
1063
1064 while( nSize && ( mpImplPolygon->mxPointAry[ nSize - 1 ] == rFirst ) )
1065 nSize--;
1066
1067 if( nSize > 1 )
1068 {
1069 sal_uInt16 nLast = 0, nNewCount = 1;
1070
1071 aNewPoly.SetSize( nSize );
1072 aNewPoly[ 0 ] = rFirst;
1073
1074 for( sal_uInt16 i = 1; i < nSize; i++ )
1075 {
1076 if( mpImplPolygon->mxPointAry[ i ] != mpImplPolygon->mxPointAry[ nLast ])
1077 {
1078 nLast = i;
1079 aNewPoly[ nNewCount++ ] = mpImplPolygon->mxPointAry[ i ];
1080 }
1081 }
1082
1083 if( nNewCount == 1 )
1084 aNewPoly.Clear();
1085 else
1086 aNewPoly.SetSize( nNewCount );
1087 }
1088
1089 *this = aNewPoly;
1090 }
1091
1092 nSize = mpImplPolygon->mnPoints;
1093
1094 if( nSize > 1 )
1095 {
1096 if( ( nOptimizeFlags & PolyOptimizeFlags::CLOSE ) &&
1097 ( mpImplPolygon->mxPointAry[ 0 ] != mpImplPolygon->mxPointAry[ nSize - 1 ] ) )
1098 {
1099 SetSize( mpImplPolygon->mnPoints + 1 );
1100 mpImplPolygon->mxPointAry[ mpImplPolygon->mnPoints - 1 ] = mpImplPolygon->mxPointAry[ 0 ];
1101 }
1102 }
1103}
1104
1105
1122static void ImplAdaptiveSubdivide( std::vector<Point>& rPoints,
1123 const double old_d2,
1124 int recursionDepth,
1125 const double d2,
1126 const double P1x, const double P1y,
1127 const double P2x, const double P2y,
1128 const double P3x, const double P3y,
1129 const double P4x, const double P4y )
1130{
1131 // Hard limit on recursion depth, empiric number.
1132 enum {maxRecursionDepth=128};
1133
1134 // Perform bezier flatness test (lecture notes from R. Schaback,
1135 // Mathematics of Computer-Aided Design, Uni Goettingen, 2000)
1136
1137 // ||P(t) - L(t)|| <= max ||b_j - b_0 - j/n(b_n - b_0)||
1138 // 0<=j<=n
1139
1140 // What is calculated here is an upper bound to the distance from
1141 // a line through b_0 and b_3 (P1 and P4 in our notation) and the
1142 // curve. We can drop 0 and n from the running indices, since the
1143 // argument of max becomes zero for those cases.
1144 const double fJ1x( P2x - P1x - 1.0/3.0*(P4x - P1x) );
1145 const double fJ1y( P2y - P1y - 1.0/3.0*(P4y - P1y) );
1146 const double fJ2x( P3x - P1x - 2.0/3.0*(P4x - P1x) );
1147 const double fJ2y( P3y - P1y - 2.0/3.0*(P4y - P1y) );
1148 const double distance2( ::std::max( fJ1x*fJ1x + fJ1y*fJ1y,
1149 fJ2x*fJ2x + fJ2y*fJ2y) );
1150
1151 // stop if error measure does not improve anymore. This is a
1152 // safety guard against floating point inaccuracies.
1153 // stop at recursion level 128. This is a safety guard against
1154 // floating point inaccuracies.
1155 // stop if distance from line is guaranteed to be bounded by d
1156 if( old_d2 > d2 &&
1157 recursionDepth < maxRecursionDepth &&
1158 distance2 >= d2 &&
1159 rPoints.size() < SAL_MAX_UINT16 )
1160 {
1161 // deCasteljau bezier arc, split at t=0.5
1162 // Foley/vanDam, p. 508
1163 const double L1x( P1x ), L1y( P1y );
1164 const double L2x( (P1x + P2x)*0.5 ), L2y( (P1y + P2y)*0.5 );
1165 const double Hx ( (P2x + P3x)*0.5 ), Hy ( (P2y + P3y)*0.5 );
1166 const double L3x( (L2x + Hx)*0.5 ), L3y( (L2y + Hy)*0.5 );
1167 const double R4x( P4x ), R4y( P4y );
1168 const double R3x( (P3x + P4x)*0.5 ), R3y( (P3y + P4y)*0.5 );
1169 const double R2x( (Hx + R3x)*0.5 ), R2y( (Hy + R3y)*0.5 );
1170 const double R1x( (L3x + R2x)*0.5 ), R1y( (L3y + R2y)*0.5 );
1171 const double L4x( R1x ), L4y( R1y );
1172
1173 // subdivide further
1174 ++recursionDepth;
1175 ImplAdaptiveSubdivide(rPoints, distance2, recursionDepth, d2, L1x, L1y, L2x, L2y, L3x, L3y, L4x, L4y);
1176 ImplAdaptiveSubdivide(rPoints, distance2, recursionDepth, d2, R1x, R1y, R2x, R2y, R3x, R3y, R4x, R4y);
1177 }
1178 else
1179 {
1180 // requested resolution reached.
1181 // Add end points to output iterator.
1182 // order is preserved, since this is so to say depth first traversal.
1183 rPoints.push_back(Point(FRound(P1x), FRound(P1y)));
1184 }
1185}
1186
1187void Polygon::AdaptiveSubdivide( Polygon& rResult, const double d ) const
1188{
1189 if (!mpImplPolygon->mxFlagAry)
1190 {
1191 rResult = *this;
1192 }
1193 else
1194 {
1195 sal_uInt16 i;
1196 sal_uInt16 nPts( GetSize() );
1197 ::std::vector< Point > aPoints;
1198 aPoints.reserve( nPts );
1199
1200 for(i=0; i<nPts;)
1201 {
1202 if( ( i + 3 ) < nPts )
1203 {
1204 PolyFlags P1( mpImplPolygon->mxFlagAry[ i ] );
1205 PolyFlags P4( mpImplPolygon->mxFlagAry[ i + 3 ] );
1206
1208 ( PolyFlags::Control == mpImplPolygon->mxFlagAry[ i + 1 ] ) &&
1209 ( PolyFlags::Control == mpImplPolygon->mxFlagAry[ i + 2 ] ) &&
1210 ( PolyFlags::Normal == P4 || PolyFlags::Smooth == P4 || PolyFlags::Symmetric == P4 ) )
1211 {
1212 ImplAdaptiveSubdivide( aPoints, d*d+1.0, 0, d*d,
1213 mpImplPolygon->mxPointAry[ i ].X(), mpImplPolygon->mxPointAry[ i ].Y(),
1214 mpImplPolygon->mxPointAry[ i+1 ].X(), mpImplPolygon->mxPointAry[ i+1 ].Y(),
1215 mpImplPolygon->mxPointAry[ i+2 ].X(), mpImplPolygon->mxPointAry[ i+2 ].Y(),
1216 mpImplPolygon->mxPointAry[ i+3 ].X(), mpImplPolygon->mxPointAry[ i+3 ].Y() );
1217 i += 3;
1218 continue;
1219 }
1220 }
1221
1222 aPoints.push_back(mpImplPolygon->mxPointAry[i++]);
1223
1224 if (aPoints.size() >= SAL_MAX_UINT16)
1225 {
1226 OSL_ENSURE(aPoints.size() < SAL_MAX_UINT16,
1227 "Polygon::AdaptiveSubdivision created polygon too many points;"
1228 " using original polygon instead");
1229
1230 // The resulting polygon can not hold all the points
1231 // that we have created so far. Stop the subdivision
1232 // and return a copy of the unmodified polygon.
1233 rResult = *this;
1234 return;
1235 }
1236 }
1237
1238 // fill result polygon
1239 rResult = tools::Polygon( static_cast<sal_uInt16>(aPoints.size()) ); // ensure sufficient size for copy
1240 ::std::copy(aPoints.begin(), aPoints.end(), rResult.mpImplPolygon->mxPointAry.get());
1241 }
1242}
1243
1244namespace {
1245
1246class Vector2D
1247{
1248private:
1249 double mfX;
1250 double mfY;
1251public:
1252 explicit Vector2D( const Point& rPoint ) : mfX( rPoint.X() ), mfY( rPoint.Y() ) {};
1253 double GetLength() const { return hypot( mfX, mfY ); }
1254 Vector2D& operator-=( const Vector2D& rVec ) { mfX -= rVec.mfX; mfY -= rVec.mfY; return *this; }
1255 double Scalar( const Vector2D& rVec ) const { return mfX * rVec.mfX + mfY * rVec.mfY ; }
1256 Vector2D& Normalize();
1257 bool IsPositive( Vector2D const & rVec ) const { return ( mfX * rVec.mfY - mfY * rVec.mfX ) >= 0.0; }
1258 bool IsNegative( Vector2D const & rVec ) const { return !IsPositive( rVec ); }
1259};
1260
1261}
1262
1263Vector2D& Vector2D::Normalize()
1264{
1265 double fLen = Scalar( *this );
1266
1267 if( ( fLen != 0.0 ) && ( fLen != 1.0 ) )
1268 {
1269 fLen = sqrt( fLen );
1270 if( fLen != 0.0 )
1271 {
1272 mfX /= fLen;
1273 mfY /= fLen;
1274 }
1275 }
1276
1277 return *this;
1278}
1279
1280void Polygon::ImplReduceEdges( tools::Polygon& rPoly, const double& rArea, sal_uInt16 nPercent )
1281{
1282 const double fBound = 2000.0 * ( 100 - nPercent ) * 0.01;
1283 sal_uInt16 nNumNoChange = 0,
1284 nNumRuns = 0;
1285
1286 while( nNumNoChange < 2 )
1287 {
1288 sal_uInt16 nPntCnt = rPoly.GetSize(), nNewPos = 0;
1289 tools::Polygon aNewPoly( nPntCnt );
1290 bool bChangeInThisRun = false;
1291
1292 for( sal_uInt16 n = 0; n < nPntCnt; n++ )
1293 {
1294 bool bDeletePoint = false;
1295
1296 if( ( n + nNumRuns ) % 2 )
1297 {
1298 sal_uInt16 nIndPrev = !n ? nPntCnt - 1 : n - 1;
1299 sal_uInt16 nIndPrevPrev = !nIndPrev ? nPntCnt - 1 : nIndPrev - 1;
1300 sal_uInt16 nIndNext = ( n == nPntCnt-1 ) ? 0 : n + 1;
1301 sal_uInt16 nIndNextNext = ( nIndNext == nPntCnt - 1 ) ? 0 : nIndNext + 1;
1302 Vector2D aVec1( rPoly[ nIndPrev ] ); aVec1 -= Vector2D(rPoly[ nIndPrevPrev ]);
1303 Vector2D aVec2( rPoly[ n ] ); aVec2 -= Vector2D(rPoly[ nIndPrev ]);
1304 Vector2D aVec3( rPoly[ nIndNext ] ); aVec3 -= Vector2D(rPoly[ n ]);
1305 Vector2D aVec4( rPoly[ nIndNextNext ] ); aVec4 -= Vector2D(rPoly[ nIndNext ]);
1306 double fDist1 = aVec1.GetLength(), fDist2 = aVec2.GetLength();
1307 double fDist3 = aVec3.GetLength(), fDist4 = aVec4.GetLength();
1308 double fTurnB = aVec2.Normalize().Scalar( aVec3.Normalize() );
1309
1310 if( fabs( fTurnB ) < ( 1.0 + SMALL_DVALUE ) && fabs( fTurnB ) > ( 1.0 - SMALL_DVALUE ) )
1311 bDeletePoint = true;
1312 else
1313 {
1314 Vector2D aVecB( rPoly[ nIndNext ] );
1315 aVecB -= Vector2D(rPoly[ nIndPrev ] );
1316 double fDistB = aVecB.GetLength();
1317 double fLenWithB = fDist2 + fDist3;
1318 double fLenFact = ( fDistB != 0.0 ) ? fLenWithB / fDistB : 1.0;
1319 double fTurnPrev = aVec1.Normalize().Scalar( aVec2 );
1320 double fTurnNext = aVec3.Scalar( aVec4.Normalize() );
1321 double fGradPrev, fGradB, fGradNext;
1322
1323 if( fabs( fTurnPrev ) < ( 1.0 + SMALL_DVALUE ) && fabs( fTurnPrev ) > ( 1.0 - SMALL_DVALUE ) )
1324 fGradPrev = 0.0;
1325 else
1326 fGradPrev = basegfx::rad2deg(acos(fTurnPrev)) * (aVec1.IsNegative(aVec2) ? -1 : 1);
1327
1328 fGradB = basegfx::rad2deg(acos(fTurnB)) * (aVec2.IsNegative(aVec3) ? -1 : 1);
1329
1330 if( fabs( fTurnNext ) < ( 1.0 + SMALL_DVALUE ) && fabs( fTurnNext ) > ( 1.0 - SMALL_DVALUE ) )
1331 fGradNext = 0.0;
1332 else
1333 fGradNext = basegfx::rad2deg(acos(fTurnNext)) * (aVec3.IsNegative(aVec4) ? -1 : 1);
1334
1335 if( ( fGradPrev > 0.0 && fGradB < 0.0 && fGradNext > 0.0 ) ||
1336 ( fGradPrev < 0.0 && fGradB > 0.0 && fGradNext < 0.0 ) )
1337 {
1338 if( ( fLenFact < ( FSQRT2 + SMALL_DVALUE ) ) &&
1339 ( ( ( fDist1 + fDist4 ) / ( fDist2 + fDist3 ) ) * 2000.0 ) > fBound )
1340 {
1341 bDeletePoint = true;
1342 }
1343 }
1344 else
1345 {
1346 double fRelLen = 1.0 - sqrt( fDistB / rArea );
1347
1348 if( fRelLen < 0.0 )
1349 fRelLen = 0.0;
1350 else if( fRelLen > 1.0 )
1351 fRelLen = 1.0;
1352
1353 if( ( std::round( ( fLenFact - 1.0 ) * 1000000.0 ) < fBound ) &&
1354 ( fabs( fGradB ) <= ( fRelLen * fBound * 0.01 ) ) )
1355 {
1356 bDeletePoint = true;
1357 }
1358 }
1359 }
1360 }
1361
1362 if( !bDeletePoint )
1363 aNewPoly[ nNewPos++ ] = rPoly[ n ];
1364 else
1365 bChangeInThisRun = true;
1366 }
1367
1368 if( bChangeInThisRun && nNewPos )
1369 {
1370 aNewPoly.SetSize( nNewPos );
1371 rPoly = aNewPoly;
1372 nNumNoChange = 0;
1373 }
1374 else
1375 nNumNoChange++;
1376
1377 nNumRuns++;
1378 }
1379}
1380
1381void Polygon::Move( tools::Long nHorzMove, tools::Long nVertMove )
1382{
1383 // This check is required for DrawEngine
1384 if ( !nHorzMove && !nVertMove )
1385 return;
1386
1387 // Move points
1388 sal_uInt16 nCount = mpImplPolygon->mnPoints;
1389 for ( sal_uInt16 i = 0; i < nCount; i++ )
1390 {
1391 Point& rPt = mpImplPolygon->mxPointAry[i];
1392 rPt.AdjustX(nHorzMove );
1393 rPt.AdjustY(nVertMove );
1394 }
1395}
1396
1397void Polygon::Translate(const Point& rTrans)
1398{
1399 for ( sal_uInt16 i = 0, nCount = mpImplPolygon->mnPoints; i < nCount; i++ )
1400 mpImplPolygon->mxPointAry[ i ] += rTrans;
1401}
1402
1403void Polygon::Scale( double fScaleX, double fScaleY )
1404{
1405 for ( sal_uInt16 i = 0, nCount = mpImplPolygon->mnPoints; i < nCount; i++ )
1406 {
1407 Point& rPnt = mpImplPolygon->mxPointAry[i];
1408 rPnt.setX( static_cast<tools::Long>( fScaleX * rPnt.X() ) );
1409 rPnt.setY( static_cast<tools::Long>( fScaleY * rPnt.Y() ) );
1410 }
1411}
1412
1413void Polygon::Rotate( const Point& rCenter, Degree10 nAngle10 )
1414{
1415 nAngle10 %= 3600_deg10;
1416
1417 if( nAngle10 )
1418 {
1419 const double fAngle = toRadians(nAngle10);
1420 Rotate( rCenter, sin( fAngle ), cos( fAngle ) );
1421 }
1422}
1423
1424void Polygon::Rotate( const Point& rCenter, double fSin, double fCos )
1425{
1426 tools::Long nCenterX = rCenter.X();
1427 tools::Long nCenterY = rCenter.Y();
1428
1429 for( sal_uInt16 i = 0, nCount = mpImplPolygon->mnPoints; i < nCount; i++ )
1430 {
1431 Point& rPt = mpImplPolygon->mxPointAry[ i ];
1432
1433 const tools::Long nX = rPt.X() - nCenterX;
1434 const tools::Long nY = rPt.Y() - nCenterY;
1435 rPt.setX( FRound(fCos * nX + fSin * nY + nCenterX) );
1436 rPt.setY( FRound(-(fSin * nX - fCos * nY - nCenterY)) );
1437 }
1438}
1439
1441{
1442 // This algorithm is broken for bezier curves, they would get converted to lines.
1443 // Use PolyPolygon::Clip().
1444 assert( !HasFlags());
1445
1446 // #105251# Normalize rect before edge filtering
1447 tools::Rectangle aJustifiedRect( rRect );
1448 aJustifiedRect.Normalize();
1449
1450 sal_uInt16 nSourceSize = mpImplPolygon->mnPoints;
1451 ImplPolygonPointFilter aPolygon( nSourceSize );
1452 ImplEdgePointFilter aHorzFilter( EDGE_HORZ, aJustifiedRect.Left(), aJustifiedRect.Right(),
1453 aPolygon );
1454 ImplEdgePointFilter aVertFilter( EDGE_VERT, aJustifiedRect.Top(), aJustifiedRect.Bottom(),
1455 aHorzFilter );
1456
1457 for ( sal_uInt16 i = 0; i < nSourceSize; i++ )
1458 aVertFilter.Input( mpImplPolygon->mxPointAry[i] );
1459 if ( aVertFilter.IsPolygon() )
1460 aVertFilter.LastPoint();
1461 else
1462 aPolygon.LastPoint();
1463
1464 mpImplPolygon = ImplType(aPolygon.get());
1465}
1466
1468{
1469 // Removing the assert. Bezier curves have the attribute that each single
1470 // curve segment defined by four points can not exit the four-point polygon
1471 // defined by that points. This allows to say that the curve segment can also
1472 // never leave the Range of its defining points.
1473 // The result is that Polygon::GetBoundRect() may not create the minimal
1474 // BoundRect of the Polygon (to get that, use basegfx::B2DPolygon classes),
1475 // but will always create a valid BoundRect, at least as long as this method
1476 // 'blindly' travels over all points, including control points.
1477
1478 // DBG_ASSERT( !mpImplPolygon->mxFlagAry.get(), "GetBoundRect could fail with beziers!" );
1479
1480 sal_uInt16 nCount = mpImplPolygon->mnPoints;
1481 if( ! nCount )
1482 return tools::Rectangle();
1483
1484 tools::Long nXMin, nXMax, nYMin, nYMax;
1485
1486 const Point& pFirstPt = mpImplPolygon->mxPointAry[0];
1487 nXMin = nXMax = pFirstPt.X();
1488 nYMin = nYMax = pFirstPt.Y();
1489
1490 for ( sal_uInt16 i = 0; i < nCount; i++ )
1491 {
1492 const Point& rPt = mpImplPolygon->mxPointAry[i];
1493
1494 if (rPt.X() < nXMin)
1495 nXMin = rPt.X();
1496 if (rPt.X() > nXMax)
1497 nXMax = rPt.X();
1498 if (rPt.Y() < nYMin)
1499 nYMin = rPt.Y();
1500 if (rPt.Y() > nYMax)
1501 nYMax = rPt.Y();
1502 }
1503
1504 return tools::Rectangle( nXMin, nYMin, nXMax, nYMax );
1505}
1506
1507bool Polygon::Contains( const Point& rPoint ) const
1508{
1509 DBG_ASSERT( !mpImplPolygon->mxFlagAry, "IsInside could fail with beziers!" );
1510
1511 const tools::Rectangle aBound( GetBoundRect() );
1512 const Line aLine( rPoint, Point( aBound.Right() + 100, rPoint.Y() ) );
1513 sal_uInt16 nCount = mpImplPolygon->mnPoints;
1514 sal_uInt16 nPCounter = 0;
1515
1516 if ( ( nCount > 2 ) && aBound.Contains( rPoint ) )
1517 {
1518 Point aPt1( mpImplPolygon->mxPointAry[ 0 ] );
1519 Point aIntersection;
1520 Point aLastIntersection;
1521
1522 while ( ( aPt1 == mpImplPolygon->mxPointAry[ nCount - 1 ] ) && ( nCount > 3 ) )
1523 nCount--;
1524
1525 for ( sal_uInt16 i = 1; i <= nCount; i++ )
1526 {
1527 const Point& rPt2 = mpImplPolygon->mxPointAry[ ( i < nCount ) ? i : 0 ];
1528
1529 if ( aLine.Intersection( Line( aPt1, rPt2 ), aIntersection ) )
1530 {
1531 // This avoids insertion of double intersections
1532 if ( nPCounter )
1533 {
1534 if ( aIntersection != aLastIntersection )
1535 {
1536 aLastIntersection = aIntersection;
1537 nPCounter++;
1538 }
1539 }
1540 else
1541 {
1542 aLastIntersection = aIntersection;
1543 nPCounter++;
1544 }
1545 }
1546
1547 aPt1 = rPt2;
1548 }
1549 }
1550
1551 // is inside, if number of intersection points is odd
1552 return ( ( nPCounter & 1 ) == 1 );
1553}
1554
1555void Polygon::Insert( sal_uInt16 nPos, const Point& rPt )
1556{
1557 if( nPos >= mpImplPolygon->mnPoints )
1558 nPos = mpImplPolygon->mnPoints;
1559
1560 if (mpImplPolygon->ImplSplit(nPos, 1))
1561 mpImplPolygon->mxPointAry[ nPos ] = rPt;
1562}
1563
1564void Polygon::Insert( sal_uInt16 nPos, const tools::Polygon& rPoly )
1565{
1566 const sal_uInt16 nInsertCount = rPoly.mpImplPolygon->mnPoints;
1567
1568 if( nInsertCount )
1569 {
1570 if( nPos >= mpImplPolygon->mnPoints )
1571 nPos = mpImplPolygon->mnPoints;
1572
1573 if (rPoly.mpImplPolygon->mxFlagAry)
1574 mpImplPolygon->ImplCreateFlagArray();
1575
1576 mpImplPolygon->ImplSplit( nPos, nInsertCount, rPoly.mpImplPolygon.get() );
1577 }
1578}
1579
1580Point& Polygon::operator[]( sal_uInt16 nPos )
1581{
1582 assert( nPos < mpImplPolygon->mnPoints );
1583
1584 return mpImplPolygon->mxPointAry[nPos];
1585}
1586
1588{
1590 return *this;
1591}
1592
1594{
1595 mpImplPolygon = std::move(rPoly.mpImplPolygon);
1596 return *this;
1597}
1598
1599bool Polygon::operator==( const tools::Polygon& rPoly ) const
1600{
1601 return (mpImplPolygon == rPoly.mpImplPolygon);
1602}
1603
1604bool Polygon::IsEqual( const tools::Polygon& rPoly ) const
1605{
1606 bool bIsEqual = true;
1607 sal_uInt16 i;
1608 if ( GetSize() != rPoly.GetSize() )
1609 bIsEqual = false;
1610 else
1611 {
1612 for ( i = 0; i < GetSize(); i++ )
1613 {
1614 if ( ( GetPoint( i ) != rPoly.GetPoint( i ) ) ||
1615 ( GetFlags( i ) != rPoly.GetFlags( i ) ) )
1616 {
1617 bIsEqual = false;
1618 break;
1619 }
1620 }
1621 }
1622 return bIsEqual;
1623}
1624
1626{
1627 sal_uInt16 nPoints(0);
1628
1629 // read all points and create array
1630 rIStream.ReadUInt16( nPoints );
1631
1632 const size_t nMaxRecordsPossible = rIStream.remainingSize() / (2 * sizeof(sal_Int32));
1633 if (nPoints > nMaxRecordsPossible)
1634 {
1635 SAL_WARN("tools", "Polygon claims " << nPoints << " records, but only " << nMaxRecordsPossible << " possible");
1636 nPoints = nMaxRecordsPossible;
1637 }
1638
1639 rPoly.mpImplPolygon->ImplSetSize( nPoints, false );
1640
1641 for (sal_uInt16 i = 0; i < nPoints; i++)
1642 {
1643 sal_Int32 nTmpX(0), nTmpY(0);
1644 rIStream.ReadInt32(nTmpX).ReadInt32(nTmpY);
1645 rPoly.mpImplPolygon->mxPointAry[i].setX(nTmpX);
1646 rPoly.mpImplPolygon->mxPointAry[i].setY(nTmpY);
1647 }
1648
1649 return rIStream;
1650}
1651
1653{
1654 sal_uInt16 nPoints = rPoly.GetSize();
1655
1656 // Write number of points
1657 rOStream.WriteUInt16( nPoints );
1658
1659 for (sal_uInt16 i = 0; i < nPoints; i++)
1660 {
1661 rOStream.WriteInt32(rPoly.mpImplPolygon->mxPointAry[i].X())
1662 .WriteInt32(rPoly.mpImplPolygon->mxPointAry[i].Y());
1663 }
1664
1665 return rOStream;
1666}
1667
1669{
1670 sal_uInt8 bHasPolyFlags(0);
1671
1672 ReadPolygon( rIStream, *this );
1673 rIStream.ReadUChar( bHasPolyFlags );
1674
1675 if ( bHasPolyFlags )
1676 {
1677 mpImplPolygon->mxFlagAry.reset(new PolyFlags[mpImplPolygon->mnPoints]);
1678 auto nRead = rIStream.ReadBytes(mpImplPolygon->mxFlagAry.get(), mpImplPolygon->mnPoints);
1679 if (nRead != mpImplPolygon->mnPoints)
1680 {
1681 SAL_WARN("tools", "Short read");
1682 memset(mpImplPolygon->mxFlagAry.get() + nRead, 0, mpImplPolygon->mnPoints - nRead);
1683 }
1684 }
1685}
1686
1687void Polygon::Read( SvStream& rIStream )
1688{
1689 VersionCompatRead aCompat(rIStream);
1690
1691 ImplRead( rIStream );
1692}
1693
1694void Polygon::ImplWrite( SvStream& rOStream ) const
1695{
1696 bool bHasPolyFlags(mpImplPolygon->mxFlagAry);
1697 WritePolygon( rOStream, *this );
1698 rOStream.WriteBool(bHasPolyFlags);
1699
1700 if ( bHasPolyFlags )
1701 rOStream.WriteBytes(mpImplPolygon->mxFlagAry.get(), mpImplPolygon->mnPoints);
1702}
1703
1704void Polygon::Write( SvStream& rOStream ) const
1705{
1706 VersionCompatWrite aCompat(rOStream, 1);
1707
1708 ImplWrite( rOStream );
1709}
1710
1711// #i74631#/#i115917# numerical correction method for B2DPolygon
1712static void impCorrectContinuity(basegfx::B2DPolygon& roPolygon, sal_uInt32 nIndex, PolyFlags nCFlag)
1713{
1714 const sal_uInt32 nPointCount(roPolygon.count());
1715 OSL_ENSURE(nIndex < nPointCount, "impCorrectContinuity: index access out of range (!)");
1716
1717 if(nIndex >= nPointCount || (PolyFlags::Smooth != nCFlag && PolyFlags::Symmetric != nCFlag))
1718 return;
1719
1720 if(!roPolygon.isPrevControlPointUsed(nIndex) || !roPolygon.isNextControlPointUsed(nIndex))
1721 return;
1722
1723 // #i115917# Patch from osnola (modified, thanks for showing the problem)
1724
1725 // The correction is needed because an integer polygon with control points
1726 // is converted to double precision. When C1 or C2 is used the involved vectors
1727 // may not have the same directions/lengths since these come from integer coordinates
1728 // and may have been snapped to different nearest integer coordinates. The snap error
1729 // is in the range of +-1 in y and y, thus 0.0 <= error <= sqrt(2.0). Nonetheless,
1730 // it needs to be corrected to be able to detect the continuity in this points
1731 // correctly.
1732
1733 // We only have the integer data here (already in double precision form, but no mantissa
1734 // used), so the best correction is to use:
1735
1736 // for C1: The longest vector since it potentially has best preserved the original vector.
1737 // Even better the sum of the vectors, weighted by their length. This gives the
1738 // normal vector addition to get the vector itself, lengths need to be preserved.
1739 // for C2: The mediated vector(s) since both should be the same, but mirrored
1740
1741 // extract the point and vectors
1742 const basegfx::B2DPoint aPoint(roPolygon.getB2DPoint(nIndex));
1743 const basegfx::B2DVector aNext(roPolygon.getNextControlPoint(nIndex) - aPoint);
1744 const basegfx::B2DVector aPrev(aPoint - roPolygon.getPrevControlPoint(nIndex));
1745
1746 // calculate common direction vector, normalize
1747 const basegfx::B2DVector aDirection(aNext + aPrev);
1748 const double fDirectionLen = aDirection.getLength();
1749 if (fDirectionLen == 0.0)
1750 return;
1751
1752 if (PolyFlags::Smooth == nCFlag)
1753 {
1754 // C1: apply common direction vector, preserve individual lengths
1755 const double fInvDirectionLen(1.0 / fDirectionLen);
1756 roPolygon.setNextControlPoint(nIndex, basegfx::B2DPoint(aPoint + (aDirection * (aNext.getLength() * fInvDirectionLen))));
1757 roPolygon.setPrevControlPoint(nIndex, basegfx::B2DPoint(aPoint - (aDirection * (aPrev.getLength() * fInvDirectionLen))));
1758 }
1759 else // PolyFlags::Symmetric
1760 {
1761 // C2: get mediated length. Taking half of the unnormalized direction would be
1762 // an approximation, but not correct.
1763 const double fMedLength((aNext.getLength() + aPrev.getLength()) * (0.5 / fDirectionLen));
1764 const basegfx::B2DVector aScaledDirection(aDirection * fMedLength);
1765
1766 // Bring Direction to correct length and apply
1767 roPolygon.setNextControlPoint(nIndex, basegfx::B2DPoint(aPoint + aScaledDirection));
1768 roPolygon.setPrevControlPoint(nIndex, basegfx::B2DPoint(aPoint - aScaledDirection));
1769 }
1770}
1771
1772// convert to basegfx::B2DPolygon and return
1774{
1775 basegfx::B2DPolygon aRetval;
1776 const sal_uInt16 nCount(mpImplPolygon->mnPoints);
1777
1778 if (nCount)
1779 {
1780 if (mpImplPolygon->mxFlagAry)
1781 {
1782 // handling for curves. Add start point
1783 const Point aStartPoint(mpImplPolygon->mxPointAry[0]);
1784 PolyFlags nPointFlag(mpImplPolygon->mxFlagAry[0]);
1785 aRetval.append(basegfx::B2DPoint(aStartPoint.X(), aStartPoint.Y()));
1786 Point aControlA, aControlB;
1787
1788 for(sal_uInt16 a(1); a < nCount;)
1789 {
1790 bool bControlA(false);
1791 bool bControlB(false);
1792
1793 if(PolyFlags::Control == mpImplPolygon->mxFlagAry[a])
1794 {
1795 aControlA = mpImplPolygon->mxPointAry[a++];
1796 bControlA = true;
1797 }
1798
1799 if(a < nCount && PolyFlags::Control == mpImplPolygon->mxFlagAry[a])
1800 {
1801 aControlB = mpImplPolygon->mxPointAry[a++];
1802 bControlB = true;
1803 }
1804
1805 // assert invalid polygons
1806 OSL_ENSURE(bControlA == bControlB, "Polygon::getB2DPolygon: Invalid source polygon (!)");
1807
1808 if(a < nCount)
1809 {
1810 const Point aEndPoint(mpImplPolygon->mxPointAry[a]);
1811
1812 if(bControlA)
1813 {
1814 // bezier edge, add
1815 aRetval.appendBezierSegment(
1816 basegfx::B2DPoint(aControlA.X(), aControlA.Y()),
1817 basegfx::B2DPoint(aControlB.X(), aControlB.Y()),
1818 basegfx::B2DPoint(aEndPoint.X(), aEndPoint.Y()));
1819
1820 impCorrectContinuity(aRetval, aRetval.count() - 2, nPointFlag);
1821 }
1822 else
1823 {
1824 // no bezier edge, add end point
1825 aRetval.append(basegfx::B2DPoint(aEndPoint.X(), aEndPoint.Y()));
1826 }
1827
1828 nPointFlag = mpImplPolygon->mxFlagAry[a++];
1829 }
1830 }
1831
1832 // if exist, remove double first/last points, set closed and correct control points
1834
1835 if(aRetval.isClosed())
1836 {
1837 // closeWithGeometryChange did really close, so last point(s) were removed.
1838 // Correct the continuity in the changed point
1839 impCorrectContinuity(aRetval, 0, mpImplPolygon->mxFlagAry[0]);
1840 }
1841 }
1842 else
1843 {
1844 // extra handling for non-curves (most-used case) for speedup
1845 for(sal_uInt16 a(0); a < nCount; a++)
1846 {
1847 // get point and add
1848 const Point aPoint(mpImplPolygon->mxPointAry[a]);
1849 aRetval.append(basegfx::B2DPoint(aPoint.X(), aPoint.Y()));
1850 }
1851
1852 // set closed flag
1854 }
1855 }
1856
1857 return aRetval;
1858}
1859
1860Polygon::Polygon(const basegfx::B2DPolygon& rPolygon) : mpImplPolygon(ImplPolygon(rPolygon))
1861{
1862}
1863
1864} // namespace tools
1865
1866/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Sequence< double > P1
double d
bool IsNeg() const
Definition: bigint.hxx:175
ImplPolygon()
Definition: poly.h:33
void ImplCreateFlagArray()
Definition: poly.cxx:648
void ImplInitSize(sal_uInt16 nInitSize, bool bFlags=false)
Definition: poly.cxx:509
sal_uInt16 mnPoints
Definition: poly.h:30
std::unique_ptr< Point[]> mxPointAry
Definition: poly.h:28
bool ImplSplit(sal_uInt16 nPos, sal_uInt16 nSpace, ImplPolygon const *pInitPoly=nullptr)
Definition: poly.cxx:586
void ImplSetSize(sal_uInt16 nSize, bool bResize=true)
Definition: poly.cxx:525
bool operator==(const ImplPolygon &rCandidate) const
Definition: poly.cxx:502
std::unique_ptr< PolyFlags[]> mxFlagAry
Definition: poly.h:29
Definition: gen.hxx:78
constexpr tools::Long Y() const
Definition: gen.hxx:84
void setX(tools::Long nX)
Definition: gen.hxx:107
void setY(tools::Long nY)
Definition: gen.hxx:108
tools::Long AdjustY(tools::Long nVertMove)
Definition: gen.hxx:89
tools::Long AdjustX(tools::Long nHorzMove)
Definition: gen.hxx:88
constexpr tools::Long X() const
Definition: gen.hxx:83
SvStream & WriteInt32(sal_Int32 nInt32)
Definition: stream.cxx:953
std::size_t WriteBytes(const void *pData, std::size_t nSize)
Definition: stream.cxx:1192
SvStream & WriteBool(bool b)
Definition: stream.hxx:256
SvStream & WriteUInt16(sal_uInt16 nUInt16)
Definition: stream.cxx:949
SvStream & ReadInt32(sal_Int32 &rInt32)
Definition: stream.cxx:825
std::size_t ReadBytes(void *pData, std::size_t nSize)
Definition: stream.cxx:1114
SvStream & ReadUInt16(sal_uInt16 &rUInt16)
Definition: stream.cxx:821
sal_uInt64 remainingSize()
Definition: stream.cxx:1320
SvStream & ReadUChar(unsigned char &rChar)
Definition: stream.cxx:858
void setStartPoint(const B2DPoint &rValue)
const B2DPoint & getStartPoint() const
const B2DPoint & getControlPointB() const
void setControlPointA(const B2DPoint &rValue)
void setEndPoint(const B2DPoint &rValue)
void setControlPointB(const B2DPoint &rValue)
const B2DPoint & getEndPoint() const
const B2DPoint & getControlPointA() const
bool isPrevControlPointUsed(sal_uInt32 nIndex) const
bool isNextControlPointUsed(sal_uInt32 nIndex) const
void setPrevControlPoint(sal_uInt32 nIndex, const basegfx::B2DPoint &rValue)
bool isClosed() const
basegfx::B2DPoint const & getB2DPoint(sal_uInt32 nIndex) const
void setNextControlPoint(sal_uInt32 nIndex, const basegfx::B2DPoint &rValue)
basegfx::B2DPoint getPrevControlPoint(sal_uInt32 nIndex) const
bool areControlPointsUsed() const
B2VectorContinuity getContinuityInPoint(sal_uInt32 nIndex) const
void append(const basegfx::B2DPoint &rPoint, sal_uInt32 nCount)
sal_uInt32 count() const
basegfx::B2DPoint getNextControlPoint(sal_uInt32 nIndex) const
void appendBezierSegment(const basegfx::B2DPoint &rNextControlPoint, const basegfx::B2DPoint &rPrevControlPoint, const basegfx::B2DPoint &rPoint)
double getLength() const
TYPE getX() const
TYPE getY() const
bool Intersection(const tools::Line &rLine, double &rIntersectionX, double &rIntersectionY) const
Definition: line.cxx:50
const Point & operator[](sal_uInt16 nPos) const
Definition: poly.hxx:156
bool operator==(const tools::Polygon &rPoly) const
Definition: poly.cxx:1599
void Insert(sal_uInt16 nPos, const Point &rPt)
Definition: poly.cxx:1555
bool Contains(const Point &rPt) const
Definition: poly.cxx:1507
::basegfx::B2DPolygon getB2DPolygon() const
Definition: poly.cxx:1773
ImplType mpImplPolygon
Definition: poly.hxx:77
double CalcDistance(sal_uInt16 nPt1, sal_uInt16 nPt2) const
Definition: poly.cxx:1028
TOOLS_DLLPUBLIC friend SvStream & ReadPolygon(SvStream &rIStream, tools::Polygon &rPoly)
Definition: poly.cxx:1625
PolyFlags GetFlags(sal_uInt16 nPos) const
Definition: poly.cxx:978
const Point * GetConstPointAry() const
Definition: poly.cxx:938
bool HasFlags() const
Definition: poly.cxx:987
void SetSize(sal_uInt16 nNewSize)
Definition: poly.cxx:1010
void Read(SvStream &rIStream)
Definition: poly.cxx:1687
void SetPoint(const Point &rPt, sal_uInt16 nPos)
Definition: poly.cxx:948
sal_uInt16 GetSize() const
Definition: poly.cxx:1018
bool IsRect() const
Definition: poly.cxx:992
void ImplRead(SvStream &rIStream)
Definition: poly.cxx:1668
void Write(SvStream &rOStream) const
Definition: poly.cxx:1704
static Polygon SubdivideBezier(const Polygon &rPoly)
Definition: poly.cxx:872
void Move(tools::Long nHorzMove, tools::Long nVertMove)
Definition: poly.cxx:1381
const Point & GetPoint(sal_uInt16 nPos) const
Definition: poly.cxx:970
const PolyFlags * GetConstFlagAry() const
Definition: poly.cxx:943
tools::Rectangle GetBoundRect() const
Definition: poly.cxx:1467
o3tl::cow_wrapper< ImplPolygon > ImplType
Definition: poly.hxx:75
Point * GetPointAry()
Definition: poly.cxx:933
void Scale(double fScaleX, double fScaleY)
Definition: poly.cxx:1403
static void ImplReduceEdges(tools::Polygon &rPoly, const double &rArea, sal_uInt16 nPercent)
Definition: poly.cxx:1280
void Rotate(const Point &rCenter, double fSin, double fCos)
Definition: poly.cxx:1424
bool IsEqual(const tools::Polygon &rPoly) const
Definition: poly.cxx:1604
TOOLS_DLLPUBLIC friend SvStream & WritePolygon(SvStream &rOStream, const tools::Polygon &rPoly)
Definition: poly.cxx:1652
void Optimize(PolyOptimizeFlags nOptimizeFlags)
Definition: poly.cxx:1043
void AdaptiveSubdivide(tools::Polygon &rResult, const double d=1.0) const
Adaptive subdivision of polygons with curves.
Definition: poly.cxx:1187
void ImplWrite(SvStream &rOStream) const
Definition: poly.cxx:1694
void Clip(const tools::Rectangle &rRect)
Definition: poly.cxx:1440
void SetFlags(sal_uInt16 nPos, PolyFlags eFlags)
Definition: poly.cxx:956
void Translate(const Point &rTrans)
Definition: poly.cxx:1397
void Clear()
Definition: poly.cxx:1023
tools::Polygon & operator=(const tools::Polygon &rPoly)
Definition: poly.cxx:1587
constexpr Point Center() const
Definition: gen.hxx:518
constexpr tools::Long GetWidth() const
Returns the difference between right and left, assuming the range is inclusive.
Definition: gen.hxx:694
bool Contains(const Point &rPOINT) const
Definition: gen.cxx:133
static constexpr Rectangle Normalize(const Point &rLT, const Point &rRB)
Definition: gen.hxx:631
constexpr tools::Long Top() const
Definition: gen.hxx:502
constexpr Point TopLeft() const
Definition: gen.hxx:510
constexpr tools::Long Right() const
Definition: gen.hxx:501
constexpr Point BottomRight() const
Definition: gen.hxx:514
constexpr Point TopRight() const
Definition: gen.hxx:511
constexpr tools::Long GetHeight() const
Returns the difference between bottom and top, assuming the range is inclusive.
Definition: gen.hxx:710
constexpr tools::Long Left() const
Definition: gen.hxx:500
constexpr tools::Long Bottom() const
Definition: gen.hxx:503
constexpr bool IsEmpty() const
Definition: gen.hxx:558
constexpr Point BottomLeft() const
Definition: gen.hxx:513
int nCount
#define DBG_ASSERT(sCon, aError)
Definition: debug.hxx:57
double toRadians(D x)
Definition: degree.hxx:57
Degree100 abs(Degree100 x)
Definition: degree.hxx:42
sal_uInt32 mnSize
tools::Long FRound(double fVal)
Definition: helpers.hxx:74
std::enable_if< std::is_signed< T >::value||std::is_floating_point< T >::value, long >::type MinMax(T nVal, tools::Long nMin, tools::Long nMax)
Definition: helpers.hxx:22
sal_Int32 nIndex
sal_Int64 n
uno_Any a
sal_uInt16 nPos
#define SAL_WARN(area, stream)
def Input(s)
void checkClosed(B2DPolygon &rCandidate)
B2VectorContinuity
constexpr double rad2deg(double v)
int i
constexpr sal_Int64 md(U i, U)
std::enable_if< std::is_signed< T >::value, bool >::type checked_multiply(T a, T b, T &result)
constexpr T saturating_add(T a, T b)
constexpr T saturating_sub(T a, T b)
css::uno::Reference< css::linguistic2::XProofreadingIterator > get(css::uno::Reference< css::uno::XComponentContext > const &context)
Note: this class is a true marvel of engineering: because the author could not decide whether it's be...
static void ImplAdaptiveSubdivide(std::vector< Point > &rPoints, const double old_d2, int recursionDepth, const double d2, const double P1x, const double P1y, const double P2x, const double P2y, const double P3x, const double P3y, const double P4x, const double P4y)
Recursively subdivide cubic bezier curve via deCasteljau.
Definition: poly.cxx:1122
static void impCorrectContinuity(basegfx::B2DPolygon &roPolygon, sal_uInt32 nIndex, PolyFlags nCFlag)
Definition: poly.cxx:1712
long Long
Definition: long.hxx:34
SvStream & ReadPolygon(SvStream &rIStream, tools::Polygon &rPoly)
Definition: poly.cxx:1625
SvStream & WritePolygon(SvStream &rOStream, const tools::Polygon &rPoly)
Definition: poly.cxx:1652
#define Y
static double ImplGetParameter(const Point &rCenter, const Point &rPt, double fWR, double fHR)
Definition: poly.cxx:56
#define FSQRT2
Definition: poly.cxx:54
constexpr int EDGE_VERT
Definition: poly.cxx:52
constexpr double SMALL_DVALUE
Definition: poly.cxx:53
double mfY
Definition: poly.cxx:1250
constexpr int EDGE_HORZ
Definition: poly.cxx:51
constexpr int EDGE_LEFT
Definition: poly.cxx:47
constexpr int EDGE_BOTTOM
Definition: poly.cxx:50
constexpr int EDGE_RIGHT
Definition: poly.cxx:49
constexpr int EDGE_TOP
Definition: poly.cxx:48
double mfX
Definition: poly.cxx:1249
PolyFlags
Definition: poly.hxx:53
PolyStyle
Definition: poly.hxx:46
PolyOptimizeFlags
Definition: poly.hxx:34
timeval & operator-=(timeval &t1, const timeval &t2)
css::drawing::Direction3D aDirection
unsigned char sal_uInt8
#define SAL_MAX_UINT16