Bump version to 21.06.18.1
[LibreOffice.git] / tools / source / generic / poly2.cxx
blob8825ad049841863c5a1fbef989409de088045475
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/log.hxx>
21 #include <osl/diagnose.h>
22 #include <poly.h>
23 #include <tools/poly.hxx>
24 #include <tools/debug.hxx>
25 #include <tools/stream.hxx>
26 #include <tools/vcompat.hxx>
27 #include <tools/gen.hxx>
28 #include <basegfx/polygon/b2dpolypolygon.hxx>
29 #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
30 #include <basegfx/polygon/b2dpolygonclipper.hxx>
32 namespace tools {
34 PolyPolygon::PolyPolygon( sal_uInt16 nInitSize )
35 : mpImplPolyPolygon( ImplPolyPolygon( nInitSize ) )
39 PolyPolygon::PolyPolygon( const tools::Polygon& rPoly )
40 : mpImplPolyPolygon( rPoly )
44 PolyPolygon::PolyPolygon( const tools::PolyPolygon& rPolyPoly )
45 : mpImplPolyPolygon( rPolyPoly.mpImplPolyPolygon )
49 PolyPolygon::PolyPolygon( tools::PolyPolygon&& rPolyPoly ) noexcept
50 : mpImplPolyPolygon( std::move(rPolyPoly.mpImplPolyPolygon) )
54 PolyPolygon::~PolyPolygon()
58 void PolyPolygon::Insert( const tools::Polygon& rPoly, sal_uInt16 nPos )
60 assert ( mpImplPolyPolygon->mvPolyAry.size() < MAX_POLYGONS );
62 if ( nPos > mpImplPolyPolygon->mvPolyAry.size() )
63 nPos = mpImplPolyPolygon->mvPolyAry.size();
65 mpImplPolyPolygon->mvPolyAry.insert(mpImplPolyPolygon->mvPolyAry.begin() + nPos, rPoly);
68 void PolyPolygon::Remove( sal_uInt16 nPos )
70 assert(nPos < Count() && "PolyPolygon::Remove(): nPos >= nSize");
72 mpImplPolyPolygon->mvPolyAry.erase(mpImplPolyPolygon->mvPolyAry.begin() + nPos);
75 void PolyPolygon::Replace( const tools::Polygon& rPoly, sal_uInt16 nPos )
77 assert(nPos < Count() && "PolyPolygon::Replace(): nPos >= nSize");
79 mpImplPolyPolygon->mvPolyAry[nPos] = rPoly;
82 const tools::Polygon& PolyPolygon::GetObject( sal_uInt16 nPos ) const
84 assert(nPos < Count() && "PolyPolygon::GetObject(): nPos >= nSize");
86 return mpImplPolyPolygon->mvPolyAry[nPos];
89 bool PolyPolygon::IsRect() const
91 bool bIsRect = false;
92 if ( Count() == 1 )
93 bIsRect = mpImplPolyPolygon->mvPolyAry[ 0 ].IsRect();
94 return bIsRect;
97 void PolyPolygon::Clear()
99 mpImplPolyPolygon->mvPolyAry.clear();
102 void PolyPolygon::Optimize( PolyOptimizeFlags nOptimizeFlags )
104 if(!(bool(nOptimizeFlags) && Count()))
105 return;
107 // #115630# ImplDrawHatch does not work with beziers included in the polypolygon, take care of that
108 bool bIsCurve(false);
110 for(sal_uInt16 a(0); !bIsCurve && a < Count(); a++)
112 if((*this)[a].HasFlags())
114 bIsCurve = true;
118 if(bIsCurve)
120 OSL_ENSURE(false, "Optimize does *not* support curves, falling back to AdaptiveSubdivide()...");
121 tools::PolyPolygon aPolyPoly;
123 AdaptiveSubdivide(aPolyPoly);
124 aPolyPoly.Optimize(nOptimizeFlags);
125 *this = aPolyPoly;
127 else
129 double fArea;
130 const bool bEdges = ( nOptimizeFlags & PolyOptimizeFlags::EDGES ) == PolyOptimizeFlags::EDGES;
131 sal_uInt16 nPercent = 0;
133 if( bEdges )
135 const tools::Rectangle aBound( GetBoundRect() );
137 fArea = ( aBound.GetWidth() + aBound.GetHeight() ) * 0.5;
138 nPercent = 50;
139 nOptimizeFlags &= ~PolyOptimizeFlags::EDGES;
142 // Optimize polygons
143 for( sal_uInt16 i = 0, nPolyCount = mpImplPolyPolygon->mvPolyAry.size(); i < nPolyCount; i++ )
145 if( bEdges )
147 mpImplPolyPolygon->mvPolyAry[ i ].Optimize( PolyOptimizeFlags::NO_SAME );
148 tools::Polygon::ImplReduceEdges( mpImplPolyPolygon->mvPolyAry[ i ], fArea, nPercent );
151 if( bool(nOptimizeFlags) )
152 mpImplPolyPolygon->mvPolyAry[ i ].Optimize( nOptimizeFlags );
157 void PolyPolygon::AdaptiveSubdivide( tools::PolyPolygon& rResult ) const
159 rResult.Clear();
161 tools::Polygon aPolygon;
163 for( size_t i = 0; i < mpImplPolyPolygon->mvPolyAry.size(); i++ )
165 mpImplPolyPolygon->mvPolyAry[ i ].AdaptiveSubdivide( aPolygon, 1.0 );
166 rResult.Insert( aPolygon );
170 tools::PolyPolygon PolyPolygon::SubdivideBezier( const tools::PolyPolygon& rPolyPoly )
172 sal_uInt16 i, nPolys = rPolyPoly.Count();
173 tools::PolyPolygon aPolyPoly( nPolys );
174 for( i=0; i<nPolys; ++i )
175 aPolyPoly.Insert( tools::Polygon::SubdivideBezier( rPolyPoly.GetObject(i) ) );
177 return aPolyPoly;
181 void PolyPolygon::GetIntersection( const tools::PolyPolygon& rPolyPoly, tools::PolyPolygon& rResult ) const
183 ImplDoOperation( rPolyPoly, rResult, PolyClipOp::INTERSECT );
186 void PolyPolygon::GetUnion( const tools::PolyPolygon& rPolyPoly, tools::PolyPolygon& rResult ) const
188 ImplDoOperation( rPolyPoly, rResult, PolyClipOp::UNION );
191 void PolyPolygon::ImplDoOperation( const tools::PolyPolygon& rPolyPoly, tools::PolyPolygon& rResult, PolyClipOp nOperation ) const
193 // Convert to B2DPolyPolygon, temporarily. It might be
194 // advantageous in the future, to have a tools::PolyPolygon adaptor that
195 // just simulates a B2DPolyPolygon here...
196 basegfx::B2DPolyPolygon aMergePolyPolygonA( getB2DPolyPolygon() );
197 basegfx::B2DPolyPolygon aMergePolyPolygonB( rPolyPoly.getB2DPolyPolygon() );
199 // normalize the two polypolygons before. Force properly oriented
200 // polygons.
201 aMergePolyPolygonA = basegfx::utils::prepareForPolygonOperation( aMergePolyPolygonA );
202 aMergePolyPolygonB = basegfx::utils::prepareForPolygonOperation( aMergePolyPolygonB );
204 switch( nOperation )
206 // All code extracted from svx/source/svdraw/svedtv2.cxx
208 case PolyClipOp::UNION:
210 // merge A and B (OR)
211 aMergePolyPolygonA = basegfx::utils::solvePolygonOperationOr(aMergePolyPolygonA, aMergePolyPolygonB);
212 break;
215 default:
216 case PolyClipOp::INTERSECT:
218 // cut poly 1 against polys 2..n (AND)
219 aMergePolyPolygonA = basegfx::utils::solvePolygonOperationAnd(aMergePolyPolygonA, aMergePolyPolygonB);
220 break;
224 rResult = tools::PolyPolygon( aMergePolyPolygonA );
227 sal_uInt16 PolyPolygon::Count() const
229 return mpImplPolyPolygon->mvPolyAry.size();
232 void PolyPolygon::Move( tools::Long nHorzMove, tools::Long nVertMove )
234 // Required for DrawEngine
235 if( nHorzMove || nVertMove )
237 // move points
238 sal_uInt16 nPolyCount = mpImplPolyPolygon->mvPolyAry.size();
239 for ( sal_uInt16 i = 0; i < nPolyCount; i++ )
240 mpImplPolyPolygon->mvPolyAry[i].Move( nHorzMove, nVertMove );
244 void PolyPolygon::Translate( const Point& rTrans )
246 // move points
247 for ( sal_uInt16 i = 0, nCount = mpImplPolyPolygon->mvPolyAry.size(); i < nCount; i++ )
248 mpImplPolyPolygon->mvPolyAry[ i ].Translate( rTrans );
251 void PolyPolygon::Scale( double fScaleX, double fScaleY )
253 // Move points
254 for ( sal_uInt16 i = 0, nCount = mpImplPolyPolygon->mvPolyAry.size(); i < nCount; i++ )
255 mpImplPolyPolygon->mvPolyAry[ i ].Scale( fScaleX, fScaleY );
258 void PolyPolygon::Rotate( const Point& rCenter, Degree10 nAngle10 )
260 nAngle10 %= Degree10(3600);
262 if( nAngle10 )
264 const double fAngle = F_PI1800 * nAngle10.get();
265 Rotate( rCenter, sin( fAngle ), cos( fAngle ) );
269 void PolyPolygon::Rotate( const Point& rCenter, double fSin, double fCos )
271 // move points
272 for ( sal_uInt16 i = 0, nCount = mpImplPolyPolygon->mvPolyAry.size(); i < nCount; i++ )
273 mpImplPolyPolygon->mvPolyAry[ i ].Rotate( rCenter, fSin, fCos );
276 void PolyPolygon::Clip( const tools::Rectangle& rRect )
278 sal_uInt16 nPolyCount = mpImplPolyPolygon->mvPolyAry.size();
279 sal_uInt16 i;
281 if ( !nPolyCount )
282 return;
284 // If there are bezier curves involved, Polygon::Clip() is broken.
285 // Use a temporary B2DPolyPolygon for the clipping.
286 for ( i = 0; i < nPolyCount; i++ )
288 if(mpImplPolyPolygon->mvPolyAry[i].HasFlags())
290 const basegfx::B2DPolyPolygon aPoly(
291 basegfx::utils::clipPolyPolygonOnRange(
292 getB2DPolyPolygon(),
293 basegfx::B2DRange(
294 rRect.Left(),
295 rRect.Top(),
296 rRect.Right() + 1,
297 rRect.Bottom() + 1),
298 true,
299 false));
300 *this = PolyPolygon( aPoly );
301 return;
305 // Clip every polygon, deleting the empty ones
306 for ( i = 0; i < nPolyCount; i++ )
307 mpImplPolyPolygon->mvPolyAry[i].Clip( rRect );
308 while ( nPolyCount )
310 if ( GetObject( nPolyCount-1 ).GetSize() <= 2 )
311 Remove( nPolyCount-1 );
312 nPolyCount--;
316 tools::Rectangle PolyPolygon::GetBoundRect() const
318 tools::Long nXMin=0, nXMax=0, nYMin=0, nYMax=0;
319 bool bFirst = true;
320 sal_uInt16 nPolyCount = mpImplPolyPolygon->mvPolyAry.size();
322 for ( sal_uInt16 n = 0; n < nPolyCount; n++ )
324 const tools::Polygon* pPoly = &mpImplPolyPolygon->mvPolyAry[n];
325 const Point* pAry = pPoly->GetConstPointAry();
326 sal_uInt16 nPointCount = pPoly->GetSize();
328 for ( sal_uInt16 i = 0; i < nPointCount; i++ )
330 const Point* pPt = &pAry[ i ];
332 if ( bFirst )
334 nXMin = nXMax = pPt->X();
335 nYMin = nYMax = pPt->Y();
336 bFirst = false;
338 else
340 if ( pPt->X() < nXMin )
341 nXMin = pPt->X();
342 if ( pPt->X() > nXMax )
343 nXMax = pPt->X();
344 if ( pPt->Y() < nYMin )
345 nYMin = pPt->Y();
346 if ( pPt->Y() > nYMax )
347 nYMax = pPt->Y();
352 if ( !bFirst )
353 return tools::Rectangle( nXMin, nYMin, nXMax, nYMax );
354 else
355 return tools::Rectangle();
358 Polygon& PolyPolygon::operator[]( sal_uInt16 nPos )
360 assert(nPos < Count() && "PolyPolygon::[](): nPos >= nSize");
362 return mpImplPolyPolygon->mvPolyAry[nPos];
365 PolyPolygon& PolyPolygon::operator=( const tools::PolyPolygon& rPolyPoly )
367 mpImplPolyPolygon = rPolyPoly.mpImplPolyPolygon;
368 return *this;
371 PolyPolygon& PolyPolygon::operator=( tools::PolyPolygon&& rPolyPoly ) noexcept
373 mpImplPolyPolygon = std::move(rPolyPoly.mpImplPolyPolygon);
374 return *this;
377 bool PolyPolygon::operator==( const tools::PolyPolygon& rPolyPoly ) const
379 return rPolyPoly.mpImplPolyPolygon == mpImplPolyPolygon;
382 SvStream& ReadPolyPolygon( SvStream& rIStream, tools::PolyPolygon& rPolyPoly )
384 sal_uInt16 nPolyCount(0);
386 // Read number of polygons
387 rIStream.ReadUInt16( nPolyCount );
389 const size_t nMinRecordSize = sizeof(sal_uInt16);
390 const size_t nMaxRecords = rIStream.remainingSize() / nMinRecordSize;
391 if (nPolyCount > nMaxRecords)
393 SAL_WARN("tools", "Parsing error: " << nMaxRecords <<
394 " max possible entries, but " << nPolyCount << " claimed, truncating");
395 nPolyCount = nMaxRecords;
398 if( nPolyCount )
400 rPolyPoly.mpImplPolyPolygon->mvPolyAry.resize(nPolyCount);
402 tools::Polygon aTempPoly;
403 for ( sal_uInt16 i = 0; i < nPolyCount; i++ )
405 ReadPolygon( rIStream, aTempPoly );
406 rPolyPoly.mpImplPolyPolygon->mvPolyAry[i] = aTempPoly;
409 else
410 rPolyPoly = tools::PolyPolygon();
412 return rIStream;
415 SvStream& WritePolyPolygon( SvStream& rOStream, const tools::PolyPolygon& rPolyPoly )
417 // Write number of polygons
418 sal_uInt16 nPolyCount = rPolyPoly.mpImplPolyPolygon->mvPolyAry.size();
419 rOStream.WriteUInt16( nPolyCount );
421 // output polygons
422 for ( sal_uInt16 i = 0; i < nPolyCount; i++ )
423 WritePolygon( rOStream, rPolyPoly.mpImplPolyPolygon->mvPolyAry[i] );
425 return rOStream;
428 void PolyPolygon::Read( SvStream& rIStream )
430 VersionCompat aCompat( rIStream, StreamMode::READ );
432 sal_uInt16 nPolyCount(0);
434 // Read number of polygons
435 rIStream.ReadUInt16( nPolyCount );
437 const size_t nMinRecordSize = sizeof(sal_uInt16);
438 const size_t nMaxRecords = rIStream.remainingSize() / nMinRecordSize;
439 if (nPolyCount > nMaxRecords)
441 SAL_WARN("tools", "Parsing error: " << nMaxRecords <<
442 " max possible entries, but " << nPolyCount << " claimed, truncating");
443 nPolyCount = nMaxRecords;
446 if( nPolyCount )
448 mpImplPolyPolygon->mvPolyAry.clear();
450 for ( sal_uInt16 i = 0; i < nPolyCount; i++ )
452 tools::Polygon aTempPoly;
453 aTempPoly.ImplRead( rIStream );
454 mpImplPolyPolygon->mvPolyAry.emplace_back( aTempPoly );
457 else
458 *this = tools::PolyPolygon();
461 void PolyPolygon::Write( SvStream& rOStream ) const
463 VersionCompat aCompat( rOStream, StreamMode::WRITE, 1 );
465 // Write number of polygons
466 sal_uInt16 nPolyCount = mpImplPolyPolygon->mvPolyAry.size();
467 rOStream.WriteUInt16( nPolyCount );
469 // Output polygons
470 for ( sal_uInt16 i = 0; i < nPolyCount; i++ )
471 mpImplPolyPolygon->mvPolyAry[i].ImplWrite( rOStream );
474 // convert to basegfx::B2DPolyPolygon and return
475 basegfx::B2DPolyPolygon PolyPolygon::getB2DPolyPolygon() const
477 basegfx::B2DPolyPolygon aRetval;
479 for(size_t a(0); a < mpImplPolyPolygon->mvPolyAry.size(); a++)
481 tools::Polygon const & rCandidate = mpImplPolyPolygon->mvPolyAry[a];
482 aRetval.append(rCandidate.getB2DPolygon());
485 return aRetval;
488 // constructor to convert from basegfx::B2DPolyPolygon
489 PolyPolygon::PolyPolygon(const basegfx::B2DPolyPolygon& rPolyPolygon)
490 : mpImplPolyPolygon(rPolyPolygon)
494 } /* namespace tools */
496 ImplPolyPolygon::ImplPolyPolygon(const basegfx::B2DPolyPolygon& rPolyPolygon)
498 const sal_uInt16 nCount(sal_uInt16(rPolyPolygon.count()));
499 DBG_ASSERT(sal_uInt32(nCount) == rPolyPolygon.count(),
500 "PolyPolygon::PolyPolygon: Too many sub-polygons in given basegfx::B2DPolyPolygon (!)");
502 if ( nCount )
504 mvPolyAry.resize( nCount );
506 for(sal_uInt16 a(0); a < nCount; a++)
508 const basegfx::B2DPolygon& aCandidate(rPolyPolygon.getB2DPolygon(sal_uInt32(a)));
509 mvPolyAry[a] = tools::Polygon( aCandidate );
512 else
513 mvPolyAry.reserve(16);
516 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */