LibreOffice Module svx (master) 1
_xpoly.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 <sal/config.h>
21
22#include <algorithm>
23
24#include <tools/debug.hxx>
25#include <tools/poly.hxx>
26#include <tools/helpers.hxx>
27#include <tools/gen.hxx>
28
29#include <svx/xpoly.hxx>
30#include <xpolyimp.hxx>
34
35
36ImpXPolygon::ImpXPolygon(sal_uInt16 nInitSize, sal_uInt16 _nResize)
37 : pOldPointAry(nullptr)
38 , bDeleteOldPoints(false)
39 , nSize(0)
40 , nResize(_nResize)
41 , nPoints(0)
42{
43 Resize(nInitSize);
44}
45
47 : pOldPointAry(nullptr)
48 , bDeleteOldPoints(false)
49 , nSize(0)
50 , nResize(rImpXPoly.nResize)
51 , nPoints(0)
52{
53 rImpXPoly.CheckPointDelete();
54
55 Resize( rImpXPoly.nSize );
56
57 // copy
58 nPoints = rImpXPoly.nPoints;
59 memcpy( pPointAry.get(), rImpXPoly.pPointAry.get(), nSize*sizeof( Point ) );
60 memcpy( pFlagAry.get(), rImpXPoly.pFlagAry.get(), nSize );
61}
62
64{
65 pPointAry.reset();
66 if ( bDeleteOldPoints )
67 {
68 delete[] pOldPointAry;
69 pOldPointAry = nullptr;
70 }
71}
72
73bool ImpXPolygon::operator==(const ImpXPolygon& rImpXPoly) const
74{
75 return nPoints==rImpXPoly.nPoints &&
76 (nPoints==0 ||
77 (memcmp(pPointAry.get(), rImpXPoly.pPointAry.get(), nPoints*sizeof(Point))==0 &&
78 memcmp(pFlagAry.get(), rImpXPoly.pFlagAry.get(), nPoints)==0));
79}
80
89void ImpXPolygon::Resize( sal_uInt16 nNewSize, bool bDeletePoints )
90{
91 if( nNewSize == nSize )
92 return;
93
94 PolyFlags* pOldFlagAry = pFlagAry.release();
95 sal_uInt16 nOldSize = nSize;
96
98 pOldPointAry = pPointAry.release();
99
100 // Round the new size to a multiple of nResize, if
101 // the object was not newly created (nSize != 0)
102 if ( nSize != 0 && nNewSize > nSize )
103 {
104 DBG_ASSERT(nResize, "Trying to resize but nResize = 0 !");
105 nNewSize = nSize + ((nNewSize-nSize-1) / nResize + 1) * nResize;
106 }
107 // create point array
108 nSize = nNewSize;
109 pPointAry.reset( new Point[ nSize ] );
110
111 // create flag array
112 pFlagAry.reset( new PolyFlags[ nSize ] );
113 memset( pFlagAry.get(), 0, nSize );
114
115 // copy if needed
116 if (nOldSize)
117 {
118 if( nOldSize < nSize )
119 {
120 memcpy( pPointAry.get(), pOldPointAry, nOldSize*sizeof( Point ) );
121 memcpy( pFlagAry.get(), pOldFlagAry, nOldSize );
122 }
123 else
124 {
125 memcpy( pPointAry.get(), pOldPointAry, nSize*sizeof( Point ) );
126 memcpy( pFlagAry.get(), pOldFlagAry, nSize );
127
128 // adjust number of valid points
129 if( nPoints > nSize )
130 nPoints = nSize;
131 }
132 }
133 if ( bDeletePoints )
134 {
135 delete[] pOldPointAry;
136 pOldPointAry = nullptr;
137 }
138 else
139 bDeleteOldPoints = true;
140 delete[] pOldFlagAry;
141}
142
143void ImpXPolygon::InsertSpace( sal_uInt16 nPos, sal_uInt16 nCount )
144{
146
147 if ( nPos > nPoints )
148 nPos = nPoints;
149
150 // if the polygon is too small then enlarge it
151 if( (nPoints + nCount) > nSize )
152 Resize( nPoints + nCount );
153
154 // If the insert is not at the last position, move everything after backwards
155 if( nPos < nPoints )
156 {
157 sal_uInt16 nMove = nPoints - nPos;
158 memmove( &pPointAry[nPos+nCount], &pPointAry[nPos],
159 nMove * sizeof(Point) );
160 memmove( &pFlagAry[nPos+nCount], &pFlagAry[nPos], nMove );
161 }
162 std::fill(pPointAry.get() + nPos, pPointAry.get() + nPos + nCount, Point());
163 memset( &pFlagAry [nPos], 0, nCount );
164
166}
167
168void ImpXPolygon::Remove( sal_uInt16 nPos, sal_uInt16 nCount )
169{
171
172 if( (nPos + nCount) > nPoints )
173 return;
174
175 sal_uInt16 nMove = nPoints - nPos - nCount;
176
177 if( nMove )
178 {
179 memmove( &pPointAry[nPos], &pPointAry[nPos+nCount],
180 nMove * sizeof(Point) );
181 memmove( &pFlagAry[nPos], &pFlagAry[nPos+nCount], nMove );
182 }
183 std::fill(pPointAry.get() + (nPoints - nCount), pPointAry.get() + nPoints, Point());
184 memset( &pFlagAry [nPoints - nCount], 0, nCount );
186}
187
189{
190 if ( bDeleteOldPoints )
191 {
192 delete[] pOldPointAry;
193 const_cast< ImpXPolygon* >(this)->pOldPointAry = nullptr;
194 const_cast< ImpXPolygon* >(this)->bDeleteOldPoints = false;
195 }
196}
197
198XPolygon::XPolygon( sal_uInt16 nSize )
199 : pImpXPolygon( ImpXPolygon( nSize, 16 ) )
200{
201}
202
203XPolygon::XPolygon( const XPolygon& ) = default;
204
205XPolygon::XPolygon( XPolygon&& ) = default;
206
209 : pImpXPolygon( rPoly.GetSize() )
210{
211 sal_uInt16 nSize = rPoly.GetSize();
212 pImpXPolygon->nPoints = nSize;
213
214 for( sal_uInt16 i = 0; i < nSize; i++ )
215 {
216 pImpXPolygon->pPointAry[i] = rPoly[i];
217 pImpXPolygon->pFlagAry[i] = rPoly.GetFlags( i );
218 }
219}
220
223 : pImpXPolygon( 17 )
224{
225 tools::Long nWh = (rRect.GetWidth() - 1) / 2;
226 tools::Long nHh = (rRect.GetHeight() - 1) / 2;
227
228 if ( nRx > nWh ) nRx = nWh;
229 if ( nRy > nHh ) nRy = nHh;
230
231 // negate Rx => circle clockwise
232 nRx = -nRx;
233
234 // factor for control points of the Bézier curve: 8/3 * (sin(45g) - 0.5)
235 tools::Long nXHdl = static_cast<tools::Long>(0.552284749 * nRx);
236 tools::Long nYHdl = static_cast<tools::Long>(0.552284749 * nRy);
237 sal_uInt16 nPos = 0;
238
239 if ( nRx && nRy )
240 {
241 Point aCenter;
242
243 for (sal_uInt16 nQuad = 0; nQuad < 4; nQuad++)
244 {
245 switch ( nQuad )
246 {
247 case 0: aCenter = rRect.TopLeft();
248 aCenter.AdjustX( -nRx );
249 aCenter.AdjustY(nRy );
250 break;
251 case 1: aCenter = rRect.TopRight();
252 aCenter.AdjustX(nRx );
253 aCenter.AdjustY(nRy );
254 break;
255 case 2: aCenter = rRect.BottomRight();
256 aCenter.AdjustX(nRx );
257 aCenter.AdjustY( -nRy );
258 break;
259 case 3: aCenter = rRect.BottomLeft();
260 aCenter.AdjustX( -nRx );
261 aCenter.AdjustY( -nRy );
262 break;
263 }
264 GenBezArc(aCenter, nRx, nRy, nXHdl, nYHdl, 0_deg100, 9000_deg100, nQuad, nPos);
265 pImpXPolygon->pFlagAry[nPos ] = PolyFlags::Smooth;
266 pImpXPolygon->pFlagAry[nPos+3] = PolyFlags::Smooth;
267 nPos += 4;
268 }
269 }
270 else
271 {
272 pImpXPolygon->pPointAry[nPos++] = rRect.TopLeft();
273 pImpXPolygon->pPointAry[nPos++] = rRect.TopRight();
274 pImpXPolygon->pPointAry[nPos++] = rRect.BottomRight();
275 pImpXPolygon->pPointAry[nPos++] = rRect.BottomLeft();
276 }
277 pImpXPolygon->pPointAry[nPos] = pImpXPolygon->pPointAry[0];
278 pImpXPolygon->nPoints = nPos + 1;
279}
280
283 Degree100 nStartAngle, Degree100 nEndAngle, bool bClose)
284 : pImpXPolygon( 17 )
285{
286 nStartAngle %= 36000_deg100;
287 if ( nEndAngle > 36000_deg100 ) nEndAngle %= 36000_deg100;
288 bool bFull = (nStartAngle == 0_deg100 && nEndAngle == 36000_deg100);
289
290 // factor for control points of the Bézier curve: 8/3 * (sin(45g) - 0.5)
291 tools::Long nXHdl = static_cast<tools::Long>(0.552284749 * nRx);
292 tools::Long nYHdl = static_cast<tools::Long>(0.552284749 * nRy);
293 sal_uInt16 nPos = 0;
294 bool bLoopEnd = false;
295
296 do
297 {
298 Degree100 nA1, nA2;
299 sal_uInt16 nQuad = nStartAngle.get() / 9000;
300 if ( nQuad == 4 ) nQuad = 0;
301 bLoopEnd = CheckAngles(nStartAngle, nEndAngle, nA1, nA2);
302 GenBezArc(rCenter, nRx, nRy, nXHdl, nYHdl, nA1, nA2, nQuad, nPos);
303 nPos += 3;
304 if ( !bLoopEnd )
305 pImpXPolygon->pFlagAry[nPos] = PolyFlags::Smooth;
306
307 } while ( !bLoopEnd );
308
309 // if not a full circle then connect edges with center point if necessary
310 if ( !bFull && bClose )
311 pImpXPolygon->pPointAry[++nPos] = rCenter;
312
313 if ( bFull )
314 {
315 pImpXPolygon->pFlagAry[0 ] = PolyFlags::Smooth;
316 pImpXPolygon->pFlagAry[nPos] = PolyFlags::Smooth;
317 }
318 pImpXPolygon->nPoints = nPos + 1;
319}
320
321XPolygon::~XPolygon() = default;
322
323void XPolygon::SetPointCount( sal_uInt16 nPoints )
324{
325 std::as_const(pImpXPolygon)->CheckPointDelete();
326
327 if( pImpXPolygon->nSize < nPoints )
328 pImpXPolygon->Resize( nPoints );
329
330 if ( nPoints < pImpXPolygon->nPoints )
331 {
332 sal_uInt16 nSize = pImpXPolygon->nPoints - nPoints;
333 std::fill(
334 pImpXPolygon->pPointAry.get() + nPoints, pImpXPolygon->pPointAry.get() + nPoints + nSize, Point());
335 memset( &pImpXPolygon->pFlagAry [nPoints], 0, nSize );
336 }
337 pImpXPolygon->nPoints = nPoints;
338}
339
340sal_uInt16 XPolygon::GetSize() const
341{
342 pImpXPolygon->CheckPointDelete();
343 return pImpXPolygon->nSize;
344}
345
346sal_uInt16 XPolygon::GetPointCount() const
347{
348 pImpXPolygon->CheckPointDelete();
349 return pImpXPolygon->nPoints;
350}
351
352void XPolygon::Insert( sal_uInt16 nPos, const Point& rPt, PolyFlags eFlags )
353{
354 if (nPos>pImpXPolygon->nPoints) nPos=pImpXPolygon->nPoints;
355 pImpXPolygon->InsertSpace( nPos, 1 );
356 pImpXPolygon->pPointAry[nPos] = rPt;
357 pImpXPolygon->pFlagAry[nPos] = eFlags;
358}
359
360void XPolygon::Insert( sal_uInt16 nPos, const XPolygon& rXPoly )
361{
362 if (nPos>pImpXPolygon->nPoints) nPos=pImpXPolygon->nPoints;
363
364 sal_uInt16 nPoints = rXPoly.GetPointCount();
365
366 pImpXPolygon->InsertSpace( nPos, nPoints );
367
368 memcpy( &(pImpXPolygon->pPointAry[nPos]),
369 rXPoly.pImpXPolygon->pPointAry.get(),
370 nPoints*sizeof( Point ) );
371 memcpy( &(pImpXPolygon->pFlagAry[nPos]),
372 rXPoly.pImpXPolygon->pFlagAry.get(),
373 nPoints );
374}
375
376void XPolygon::Remove( sal_uInt16 nPos, sal_uInt16 nCount )
377{
378 pImpXPolygon->Remove( nPos, nCount );
379}
380
381void XPolygon::Move( tools::Long nHorzMove, tools::Long nVertMove )
382{
383 if ( !nHorzMove && !nVertMove )
384 return;
385
386 // move points
387 sal_uInt16 nCount = pImpXPolygon->nPoints;
388 for ( sal_uInt16 i = 0; i < nCount; i++ )
389 {
390 Point* pPt = &(pImpXPolygon->pPointAry[i]);
391 pPt->AdjustX( nHorzMove );
392 pPt->AdjustY( nVertMove );
393 }
394}
395
397{
398 pImpXPolygon->CheckPointDelete();
399 tools::Rectangle aRetval;
400
401 if(pImpXPolygon->nPoints)
402 {
403 // #i37709#
404 // For historical reasons the control points are not part of the
405 // BoundRect. This makes it necessary to subdivide the polygon to
406 // get a relatively correct BoundRect. Numerically, this is not
407 // correct and never was.
408
410 aRetval = tools::Rectangle(
411 FRound(aPolygonRange.getMinX()), FRound(aPolygonRange.getMinY()),
412 FRound(aPolygonRange.getMaxX()), FRound(aPolygonRange.getMaxY()));
413 }
414
415 return aRetval;
416}
417
418const Point& XPolygon::operator[]( sal_uInt16 nPos ) const
419{
420 DBG_ASSERT(nPos < pImpXPolygon->nPoints, "Invalid index at const array access to XPolygon");
421
422 pImpXPolygon->CheckPointDelete();
423 return pImpXPolygon->pPointAry[nPos];
424}
425
426Point& XPolygon::operator[]( sal_uInt16 nPos )
427{
428 std::as_const(pImpXPolygon)->CheckPointDelete();
429
430 if( nPos >= pImpXPolygon->nSize )
431 {
432 DBG_ASSERT(pImpXPolygon->nResize, "Invalid index at array access to XPolygon");
433 pImpXPolygon->Resize(nPos + 1, false);
434 }
435 if( nPos >= pImpXPolygon->nPoints )
436 pImpXPolygon->nPoints = nPos + 1;
437
438 return pImpXPolygon->pPointAry[nPos];
439}
440
441XPolygon& XPolygon::operator=( const XPolygon& ) = default;
442
444
445bool XPolygon::operator==( const XPolygon& rXPoly ) const
446{
447 pImpXPolygon->CheckPointDelete();
448 return rXPoly.pImpXPolygon == pImpXPolygon;
449}
450
452PolyFlags XPolygon::GetFlags( sal_uInt16 nPos ) const
453{
454 pImpXPolygon->CheckPointDelete();
455 return pImpXPolygon->pFlagAry[nPos];
456}
457
459void XPolygon::SetFlags( sal_uInt16 nPos, PolyFlags eFlags )
460{
461 std::as_const(pImpXPolygon)->CheckPointDelete();
462 pImpXPolygon->pFlagAry[nPos] = eFlags;
463}
464
466bool XPolygon::IsControl(sal_uInt16 nPos) const
467{
468 return pImpXPolygon->pFlagAry[nPos] == PolyFlags::Control;
469}
470
472bool XPolygon::IsSmooth(sal_uInt16 nPos) const
473{
474 PolyFlags eFlag = pImpXPolygon->pFlagAry[nPos];
475 return ( eFlag == PolyFlags::Smooth || eFlag == PolyFlags::Symmetric );
476}
477
483double XPolygon::CalcDistance(sal_uInt16 nP1, sal_uInt16 nP2)
484{
485 const Point& rP1 = pImpXPolygon->pPointAry[nP1];
486 const Point& rP2 = pImpXPolygon->pPointAry[nP2];
487 double fDx = rP2.X() - rP1.X();
488 double fDy = rP2.Y() - rP1.Y();
489 return std::hypot(fDx, fDy);
490}
491
492void XPolygon::SubdivideBezier(sal_uInt16 nPos, bool bCalcFirst, double fT)
493{
494 Point* pPoints = pImpXPolygon->pPointAry.get();
495 double fT2 = fT * fT;
496 double fT3 = fT * fT2;
497 double fU = 1.0 - fT;
498 double fU2 = fU * fU;
499 double fU3 = fU * fU2;
500 sal_uInt16 nIdx = nPos;
501 short nPosInc, nIdxInc;
502
503 if ( bCalcFirst )
504 {
505 nPos += 3;
506 nPosInc = -1;
507 nIdxInc = 0;
508 }
509 else
510 {
511 nPosInc = 1;
512 nIdxInc = 1;
513 }
514 pPoints[nPos].setX( static_cast<tools::Long>(fU3 * pPoints[nIdx ].X() +
515 fT * fU2 * pPoints[nIdx+1].X() * 3 +
516 fT2 * fU * pPoints[nIdx+2].X() * 3 +
517 fT3 * pPoints[nIdx+3].X()) );
518 pPoints[nPos].setY( static_cast<tools::Long>(fU3 * pPoints[nIdx ].Y() +
519 fT * fU2 * pPoints[nIdx+1].Y() * 3 +
520 fT2 * fU * pPoints[nIdx+2].Y() * 3 +
521 fT3 * pPoints[nIdx+3].Y()) );
522 nPos = nPos + nPosInc;
523 nIdx = nIdx + nIdxInc;
524 pPoints[nPos].setX( static_cast<tools::Long>(fU2 * pPoints[nIdx ].X() +
525 fT * fU * pPoints[nIdx+1].X() * 2 +
526 fT2 * pPoints[nIdx+2].X()) );
527 pPoints[nPos].setY( static_cast<tools::Long>(fU2 * pPoints[nIdx ].Y() +
528 fT * fU * pPoints[nIdx+1].Y() * 2 +
529 fT2 * pPoints[nIdx+2].Y()) );
530 nPos = nPos + nPosInc;
531 nIdx = nIdx + nIdxInc;
532 pPoints[nPos].setX( static_cast<tools::Long>(fU * pPoints[nIdx ].X() +
533 fT * pPoints[nIdx+1].X()) );
534 pPoints[nPos].setY( static_cast<tools::Long>(fU * pPoints[nIdx ].Y() +
535 fT * pPoints[nIdx+1].Y()) );
536}
537
539void XPolygon::GenBezArc(const Point& rCenter, tools::Long nRx, tools::Long nRy,
540 tools::Long nXHdl, tools::Long nYHdl, Degree100 nStart, Degree100 nEnd,
541 sal_uInt16 nQuad, sal_uInt16 nFirst)
542{
543 Point* pPoints = pImpXPolygon->pPointAry.get();
544 pPoints[nFirst ] = rCenter;
545 pPoints[nFirst+3] = rCenter;
546
547 if ( nQuad == 1 || nQuad == 2 )
548 {
549 nRx = -nRx; nXHdl = -nXHdl;
550 }
551 if ( nQuad == 0 || nQuad == 1 )
552 {
553 nRy = -nRy; nYHdl = -nYHdl;
554 }
555
556 if ( nQuad == 0 || nQuad == 2 )
557 {
558 pPoints[nFirst].AdjustX( nRx );
559 pPoints[nFirst+3].AdjustY( nRy );
560 }
561 else
562 {
563 pPoints[nFirst].AdjustY( nRy );
564 pPoints[nFirst+3].AdjustX( nRx );
565 }
566 pPoints[nFirst+1] = pPoints[nFirst];
567 pPoints[nFirst+2] = pPoints[nFirst+3];
568
569 if ( nQuad == 0 || nQuad == 2 )
570 {
571 pPoints[nFirst+1].AdjustY( nYHdl );
572 pPoints[nFirst+2].AdjustX( nXHdl );
573 }
574 else
575 {
576 pPoints[nFirst+1].AdjustX( nXHdl );
577 pPoints[nFirst+2].AdjustY( nYHdl );
578 }
579 if ( nStart > 0_deg100 )
580 SubdivideBezier(nFirst, false, static_cast<double>(nStart.get()) / 9000);
581 if ( nEnd < 9000_deg100 )
582 SubdivideBezier(nFirst, true, static_cast<double>((nEnd-nStart).get()) / (9000_deg100-nStart).get());
583 SetFlags(nFirst+1, PolyFlags::Control);
584 SetFlags(nFirst+2, PolyFlags::Control);
585}
586
588{
589 if ( nStart == 36000_deg100 ) nStart = 0_deg100;
590 if ( nEnd == 0_deg100 ) nEnd = 36000_deg100;
591 Degree100 nStPrev = nStart;
592 Degree100 nMax((nStart.get() / 9000 + 1) * 9000);
593 Degree100 nMin = nMax - 9000_deg100;
594
595 if ( nEnd >= nMax || nEnd <= nStart ) nA2 = 9000_deg100;
596 else nA2 = nEnd - nMin;
597 nA1 = nStart - nMin;
598 nStart = nMax;
599
600 // returns true when the last segment was calculated
601 return (nStPrev < nEnd && nStart >= nEnd);
602}
603
615void XPolygon::CalcSmoothJoin(sal_uInt16 nCenter, sal_uInt16 nDrag, sal_uInt16 nPnt)
616{
617 // If nPoint is no control point, i.e. cannot be moved, then
618 // move nDrag instead on the line between nCenter and nPnt
619 if ( !IsControl(nPnt) )
620 std::swap( nDrag, nPnt );
621 Point* pPoints = pImpXPolygon->pPointAry.get();
622 Point aDiff = pPoints[nDrag] - pPoints[nCenter];
623 double fDiv = CalcDistance(nCenter, nDrag);
624
625 if ( fDiv )
626 {
627 double fRatio = CalcDistance(nCenter, nPnt) / fDiv;
628 // keep the length if SMOOTH
629 if ( GetFlags(nCenter) == PolyFlags::Smooth || !IsControl(nDrag) )
630 {
631 aDiff.setX( static_cast<tools::Long>(fRatio * aDiff.X()) );
632 aDiff.setY( static_cast<tools::Long>(fRatio * aDiff.Y()) );
633 }
634 pPoints[nPnt] = pPoints[nCenter] - aDiff;
635 }
636}
637
644void XPolygon::CalcTangent(sal_uInt16 nCenter, sal_uInt16 nPrev, sal_uInt16 nNext)
645{
646 double fAbsLen = CalcDistance(nNext, nPrev);
647
648 if ( !fAbsLen )
649 return;
650
651 const Point& rCenter = pImpXPolygon->pPointAry[nCenter];
652 Point& rNext = pImpXPolygon->pPointAry[nNext];
653 Point& rPrev = pImpXPolygon->pPointAry[nPrev];
654 Point aDiff = rNext - rPrev;
655 double fNextLen = CalcDistance(nCenter, nNext) / fAbsLen;
656 double fPrevLen = CalcDistance(nCenter, nPrev) / fAbsLen;
657
658 // same length for both sides if SYMMTR
659 if ( GetFlags(nCenter) == PolyFlags::Symmetric )
660 {
661 fPrevLen = (fNextLen + fPrevLen) / 2;
662 fNextLen = fPrevLen;
663 }
664 rNext.setX( rCenter.X() + static_cast<tools::Long>(fNextLen * aDiff.X()) );
665 rNext.setY( rCenter.Y() + static_cast<tools::Long>(fNextLen * aDiff.Y()) );
666 rPrev.setX( rCenter.X() - static_cast<tools::Long>(fPrevLen * aDiff.X()) );
667 rPrev.setY( rCenter.Y() - static_cast<tools::Long>(fPrevLen * aDiff.Y()) );
668}
669
671void XPolygon::PointsToBezier(sal_uInt16 nFirst)
672{
673 double nFullLength, nPart1Length, nPart2Length;
674 double fX0, fY0, fX1, fY1, fX2, fY2, fX3, fY3;
675 double fTx1, fTx2, fTy1, fTy2;
676 double fT1, fU1, fT2, fU2, fV;
677 Point* pPoints = pImpXPolygon->pPointAry.get();
678
679 if ( nFirst > pImpXPolygon->nPoints - 4 || IsControl(nFirst) ||
680 IsControl(nFirst+1) || IsControl(nFirst+2) || IsControl(nFirst+3) )
681 return;
682
683 fTx1 = pPoints[nFirst+1].X();
684 fTy1 = pPoints[nFirst+1].Y();
685 fTx2 = pPoints[nFirst+2].X();
686 fTy2 = pPoints[nFirst+2].Y();
687 fX0 = pPoints[nFirst ].X();
688 fY0 = pPoints[nFirst ].Y();
689 fX3 = pPoints[nFirst+3].X();
690 fY3 = pPoints[nFirst+3].Y();
691
692 nPart1Length = CalcDistance(nFirst, nFirst+1);
693 nPart2Length = nPart1Length + CalcDistance(nFirst+1, nFirst+2);
694 nFullLength = nPart2Length + CalcDistance(nFirst+2, nFirst+3);
695 if ( nFullLength < 20 )
696 return;
697
698 if ( nPart2Length == nFullLength )
699 nPart2Length -= 1;
700 if ( nPart1Length == nFullLength )
701 nPart1Length = nPart2Length - 1;
702 if ( nPart1Length <= 0 )
703 nPart1Length = 1;
704 if ( nPart2Length <= 0 || nPart2Length == nPart1Length )
705 nPart2Length = nPart1Length + 1;
706
707 fT1 = nPart1Length / nFullLength;
708 fU1 = 1.0 - fT1;
709 fT2 = nPart2Length / nFullLength;
710 fU2 = 1.0 - fT2;
711 fV = 3 * (1.0 - (fT1 * fU2) / (fT2 * fU1));
712
713 fX1 = fTx1 / (fT1 * fU1 * fU1) - fTx2 * fT1 / (fT2 * fT2 * fU1 * fU2);
714 fX1 /= fV;
715 fX1 -= fX0 * ( fU1 / fT1 + fU2 / fT2) / 3;
716 fX1 += fX3 * ( fT1 * fT2 / (fU1 * fU2)) / 3;
717
718 fY1 = fTy1 / (fT1 * fU1 * fU1) - fTy2 * fT1 / (fT2 * fT2 * fU1 * fU2);
719 fY1 /= fV;
720 fY1 -= fY0 * ( fU1 / fT1 + fU2 / fT2) / 3;
721 fY1 += fY3 * ( fT1 * fT2 / (fU1 * fU2)) / 3;
722
723 fX2 = fTx2 / (fT2 * fT2 * fU2 * 3) - fX0 * fU2 * fU2 / ( fT2 * fT2 * 3);
724 fX2 -= fX1 * fU2 / fT2;
725 fX2 -= fX3 * fT2 / (fU2 * 3);
726
727 fY2 = fTy2 / (fT2 * fT2 * fU2 * 3) - fY0 * fU2 * fU2 / ( fT2 * fT2 * 3);
728 fY2 -= fY1 * fU2 / fT2;
729 fY2 -= fY3 * fT2 / (fU2 * 3);
730
731 pPoints[nFirst+1] = Point(static_cast<tools::Long>(fX1), static_cast<tools::Long>(fY1));
732 pPoints[nFirst+2] = Point(static_cast<tools::Long>(fX2), static_cast<tools::Long>(fY2));
733 SetFlags(nFirst+1, PolyFlags::Control);
734 SetFlags(nFirst+2, PolyFlags::Control);
735}
736
738void XPolygon::Scale(double fSx, double fSy)
739{
740 std::as_const(pImpXPolygon)->CheckPointDelete();
741
742 sal_uInt16 nPntCnt = pImpXPolygon->nPoints;
743
744 for (sal_uInt16 i = 0; i < nPntCnt; i++)
745 {
746 Point& rPnt = pImpXPolygon->pPointAry[i];
747 rPnt.setX( static_cast<tools::Long>(fSx * rPnt.X()) );
748 rPnt.setY( static_cast<tools::Long>(fSy * rPnt.Y()) );
749 }
750}
751
763 const XPolygon& rDistortedRect)
764{
765 std::as_const(pImpXPolygon)->CheckPointDelete();
766
767 tools::Long Xr, Wr;
768 tools::Long Yr, Hr;
769
770 Xr = rRefRect.Left();
771 Yr = rRefRect.Top();
772 Wr = rRefRect.GetWidth();
773 Hr = rRefRect.GetHeight();
774
775 if ( !Wr || !Hr )
776 return;
777
778 tools::Long X1, X2, X3, X4;
779 tools::Long Y1, Y2, Y3, Y4;
780 DBG_ASSERT(rDistortedRect.pImpXPolygon->nPoints >= 4,
781 "Distort: rectangle too small");
782
783 X1 = rDistortedRect[0].X();
784 Y1 = rDistortedRect[0].Y();
785 X2 = rDistortedRect[1].X();
786 Y2 = rDistortedRect[1].Y();
787 X3 = rDistortedRect[3].X();
788 Y3 = rDistortedRect[3].Y();
789 X4 = rDistortedRect[2].X();
790 Y4 = rDistortedRect[2].Y();
791
792 sal_uInt16 nPntCnt = pImpXPolygon->nPoints;
793
794 for (sal_uInt16 i = 0; i < nPntCnt; i++)
795 {
796 double fTx, fTy, fUx, fUy;
797 Point& rPnt = pImpXPolygon->pPointAry[i];
798
799 fTx = static_cast<double>(rPnt.X() - Xr) / Wr;
800 fTy = static_cast<double>(rPnt.Y() - Yr) / Hr;
801 fUx = 1.0 - fTx;
802 fUy = 1.0 - fTy;
803
804 rPnt.setX( static_cast<tools::Long>( fUy * (fUx * X1 + fTx * X2) +
805 fTy * (fUx * X3 + fTx * X4) ) );
806 rPnt.setY( static_cast<tools::Long>( fUx * (fUy * Y1 + fTy * Y3) +
807 fTx * (fUy * Y2 + fTy * Y4) ) );
808 }
809}
810
812{
813 // #i74631# use tools Polygon class for conversion to not have the code doubled
814 // here. This needs one more conversion but avoids different converters in
815 // the long run
816 const tools::Polygon aSource(GetPointCount(), pImpXPolygon->pPointAry.get(), pImpXPolygon->pFlagAry.get());
817
818 return aSource.getB2DPolygon();
819}
820
822 : pImpXPolygon( tools::Polygon( rPolygon ).GetSize() )
823{
824 // #i74631# use tools Polygon class for conversion to not have the code doubled
825 // here. This needs one more conversion but avoids different converters in
826 // the long run
827
828 const tools::Polygon aSource(rPolygon);
829 sal_uInt16 nSize = aSource.GetSize();
830 pImpXPolygon->nPoints = nSize;
831
832 for( sal_uInt16 i = 0; i < nSize; i++ )
833 {
834 pImpXPolygon->pPointAry[i] = aSource[i];
835 pImpXPolygon->pFlagAry[i] = aSource.GetFlags( i );
836 }
837}
838
839// XPolyPolygon
841
842XPolyPolygon::XPolyPolygon( const XPolyPolygon& ) = default;
843
845
847{
848 for(auto const& rCandidate : rPolyPolygon)
849 {
850 Insert(XPolygon(rCandidate));
851 }
852}
853
855
857{
858 pImpXPolyPolygon->aXPolyList.emplace_back( std::move(rXPoly) );
859}
860
862void XPolyPolygon::Insert( const XPolyPolygon& rXPolyPoly )
863{
864 for ( size_t i = 0; i < rXPolyPoly.Count(); i++)
865 {
866 pImpXPolyPolygon->aXPolyList.emplace_back( rXPolyPoly[i] );
867 }
868}
869
870void XPolyPolygon::Remove( sal_uInt16 nPos )
871{
872 pImpXPolyPolygon->aXPolyList.erase( pImpXPolyPolygon->aXPolyList.begin() + nPos );
873}
874
875const XPolygon& XPolyPolygon::GetObject( sal_uInt16 nPos ) const
876{
877 return pImpXPolyPolygon->aXPolyList[ nPos ];
878}
879
881{
882 pImpXPolyPolygon->aXPolyList.clear();
883}
884
885sal_uInt16 XPolyPolygon::Count() const
886{
887 return static_cast<sal_uInt16>(pImpXPolyPolygon->aXPolyList.size());
888}
889
891{
892 size_t nXPoly = pImpXPolyPolygon->aXPolyList.size();
893 tools::Rectangle aRect;
894
895 for ( size_t n = 0; n < nXPoly; n++ )
896 {
897 XPolygon const & rXPoly = pImpXPolyPolygon->aXPolyList[ n ];
898 aRect.Union( rXPoly.GetBoundRect() );
899 }
900
901 return aRect;
902}
903
905{
906 return pImpXPolyPolygon->aXPolyList[ nPos ];
907}
908
910
912
924 const XPolygon& rDistortedRect)
925{
926 for (size_t i = 0; i < Count(); i++)
927 pImpXPolyPolygon->aXPolyList[ i ].Distort(rRefRect, rDistortedRect);
928}
929
931{
933
934 for(sal_uInt16 a(0); a < Count(); a++)
935 {
936 const XPolygon& rPoly = (*this)[a];
937 aRetval.append(rPoly.getB2DPolygon());
938 }
939
940 return aRetval;
941}
942
943/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
bool operator==(const ImpXPolygon &rImpXPoly) const
Definition: _xpoly.cxx:73
void Resize(sal_uInt16 nNewSize, bool bDeletePoints=true)
Change polygon size.
Definition: _xpoly.cxx:89
ImpXPolygon(sal_uInt16 nInitSize, sal_uInt16 nResize=16)
Definition: _xpoly.cxx:36
void Remove(sal_uInt16 nPos, sal_uInt16 nCount)
Definition: _xpoly.cxx:168
void InsertSpace(sal_uInt16 nPos, sal_uInt16 nCount)
Definition: _xpoly.cxx:143
std::unique_ptr< PolyFlags[]> pFlagAry
Definition: xpolyimp.hxx:34
Point * pOldPointAry
Definition: xpolyimp.hxx:35
sal_uInt16 nSize
Definition: xpolyimp.hxx:37
std::unique_ptr< Point[]> pPointAry
Definition: xpolyimp.hxx:32
sal_uInt16 nPoints
Definition: xpolyimp.hxx:39
~ImpXPolygon()
Definition: _xpoly.cxx:63
void CheckPointDelete() const
Definition: _xpoly.cxx:188
sal_uInt16 nResize
Definition: xpolyimp.hxx:38
bool bDeleteOldPoints
Definition: xpolyimp.hxx:36
constexpr tools::Long Y() const
void setX(tools::Long nX)
void setY(tools::Long nY)
tools::Long AdjustY(tools::Long nVertMove)
tools::Long AdjustX(tools::Long nHorzMove)
constexpr tools::Long X() const
o3tl::cow_wrapper< ImpXPolyPolygon > pImpXPolyPolygon
Definition: xpoly.hxx:116
tools::Rectangle GetBoundRect() const
Definition: _xpoly.cxx:890
const XPolygon & operator[](sal_uInt16 nPos) const
Definition: xpoly.hxx:135
void Clear()
Definition: _xpoly.cxx:880
void Remove(sal_uInt16 nPos)
Definition: _xpoly.cxx:870
basegfx::B2DPolyPolygon getB2DPolyPolygon() const
Definition: _xpoly.cxx:930
XPolyPolygon & operator=(const XPolyPolygon &)
void Insert(XPolygon &&rXPoly)
Definition: _xpoly.cxx:856
const XPolygon & GetObject(sal_uInt16 nPos) const
Definition: _xpoly.cxx:875
sal_uInt16 Count() const
Definition: _xpoly.cxx:885
void Distort(const tools::Rectangle &rRefRect, const XPolygon &rDistortedRect)
Distort a polygon by scaling its coordinates relative to a reference rectangle into an arbitrary rect...
Definition: _xpoly.cxx:923
sal_uInt16 GetSize() const
Definition: _xpoly.cxx:340
void Remove(sal_uInt16 nPos, sal_uInt16 nCount)
Definition: _xpoly.cxx:376
void Move(tools::Long nHorzMove, tools::Long nVertMove)
Definition: _xpoly.cxx:381
void CalcSmoothJoin(sal_uInt16 nCenter, sal_uInt16 nDrag, sal_uInt16 nPnt)
Calculate a smooth transition to connect two Bézier curves.
Definition: _xpoly.cxx:615
const Point & operator[](sal_uInt16 nPos) const
Definition: _xpoly.cxx:418
bool IsControl(sal_uInt16 nPos) const
short path to read the CONTROL flag directly (TODO: better explain what the sense behind this flag is...
Definition: _xpoly.cxx:466
sal_uInt16 GetPointCount() const
Definition: _xpoly.cxx:346
void GenBezArc(const Point &rCenter, tools::Long nRx, tools::Long nRy, tools::Long nXHdl, tools::Long nYHdl, Degree100 nStart, Degree100 nEnd, sal_uInt16 nQuad, sal_uInt16 nFirst)
Generate a Bézier arc.
Definition: _xpoly.cxx:539
void Insert(sal_uInt16 nPos, const Point &rPt, PolyFlags eFlags)
Definition: _xpoly.cxx:352
double CalcDistance(sal_uInt16 nP1, sal_uInt16 nP2)
calculate the euclidean distance between two points
Definition: _xpoly.cxx:483
bool operator==(const XPolygon &rXPoly) const
Definition: _xpoly.cxx:445
void SetFlags(sal_uInt16 nPos, PolyFlags eFlags)
set the flags for the point at the given position
Definition: _xpoly.cxx:459
XPolygon & operator=(const XPolygon &)
XPolygon(sal_uInt16 nSize=16)
Definition: _xpoly.cxx:198
void Scale(double fSx, double fSy)
scale in X- and/or Y-direction
Definition: _xpoly.cxx:738
void PointsToBezier(sal_uInt16 nFirst)
convert four polygon points into a Bézier curve
Definition: _xpoly.cxx:671
bool IsSmooth(sal_uInt16 nPos) const
short path to read the SMOOTH and SYMMTR flag directly (TODO: better explain what the sense behind th...
Definition: _xpoly.cxx:472
void Distort(const tools::Rectangle &rRefRect, const XPolygon &rDistortedRect)
Distort a polygon by scaling its coordinates relative to a reference rectangle into an arbitrary rect...
Definition: _xpoly.cxx:762
void SetPointCount(sal_uInt16 nPoints)
Definition: _xpoly.cxx:323
void CalcTangent(sal_uInt16 nCenter, sal_uInt16 nPrev, sal_uInt16 nNext)
Calculate tangent between two Bézier curves.
Definition: _xpoly.cxx:644
void SubdivideBezier(sal_uInt16 nPos, bool bCalcFirst, double fT)
Definition: _xpoly.cxx:492
basegfx::B2DPolygon getB2DPolygon() const
Definition: _xpoly.cxx:811
PolyFlags GetFlags(sal_uInt16 nPos) const
get the flags for the point at the given position
Definition: _xpoly.cxx:452
o3tl::cow_wrapper< ImpXPolygon > pImpXPolygon
Definition: xpoly.hxx:47
static bool CheckAngles(Degree100 &nStart, Degree100 nEnd, Degree100 &nA1, Degree100 &nA2)
Definition: _xpoly.cxx:587
tools::Rectangle GetBoundRect() const
Definition: _xpoly.cxx:396
void append(const B2DPolygon &rPolygon, sal_uInt32 nCount=1)
TYPE getMaxX() const
TYPE getMinX() const
TYPE getMinY() const
TYPE getMaxY() const
::basegfx::B2DPolygon getB2DPolygon() const
PolyFlags GetFlags(sal_uInt16 nPos) const
sal_uInt16 GetSize() const
constexpr tools::Long GetWidth() const
constexpr tools::Long Top() const
constexpr Point TopLeft() const
constexpr Point BottomRight() const
constexpr Point TopRight() const
constexpr tools::Long GetHeight() const
tools::Rectangle & Union(const tools::Rectangle &rRect)
constexpr tools::Long Left() const
constexpr Point BottomLeft() const
int nCount
#define DBG_ASSERT(sCon, aError)
tools::Long FRound(double fVal)
sal_Int64 n
uno_Any a
sal_uInt16 nPos
B2DRange getRange(const B2DPolygon &rCandidate)
int i
css::uno::Reference< css::linguistic2::XProofreadingIterator > get(css::uno::Reference< css::uno::XComponentContext > const &context)
long Long
#define Y
PolyFlags
UNDERLYING_TYPE get() const