1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
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>
34 PolyPolygon::PolyPolygon( sal_uInt16 nInitSize
)
35 : mpImplPolyPolygon( ImplPolyPolygon( nInitSize
) )
39 PolyPolygon::PolyPolygon( const tools::Polygon
& rPoly
)
40 : mpImplPolyPolygon( rPoly
)
43 PolyPolygon::PolyPolygon( const tools::Rectangle
& rRect
)
44 : mpImplPolyPolygon( tools::Polygon(rRect
) )
48 PolyPolygon::PolyPolygon( const tools::PolyPolygon
& rPolyPoly
)
49 : mpImplPolyPolygon( rPolyPoly
.mpImplPolyPolygon
)
53 PolyPolygon::PolyPolygon( tools::PolyPolygon
&& rPolyPoly
) noexcept
54 : mpImplPolyPolygon( std::move(rPolyPoly
.mpImplPolyPolygon
) )
58 PolyPolygon::~PolyPolygon()
62 void PolyPolygon::Insert( const tools::Polygon
& rPoly
, sal_uInt16 nPos
)
64 assert ( mpImplPolyPolygon
->mvPolyAry
.size() < MAX_POLYGONS
);
66 if ( nPos
> mpImplPolyPolygon
->mvPolyAry
.size() )
67 nPos
= mpImplPolyPolygon
->mvPolyAry
.size();
69 mpImplPolyPolygon
->mvPolyAry
.insert(mpImplPolyPolygon
->mvPolyAry
.begin() + nPos
, rPoly
);
72 void PolyPolygon::Remove( sal_uInt16 nPos
)
74 assert(nPos
< Count() && "PolyPolygon::Remove(): nPos >= nSize");
76 mpImplPolyPolygon
->mvPolyAry
.erase(mpImplPolyPolygon
->mvPolyAry
.begin() + nPos
);
79 void PolyPolygon::Replace( const tools::Polygon
& rPoly
, sal_uInt16 nPos
)
81 assert(nPos
< Count() && "PolyPolygon::Replace(): nPos >= nSize");
83 mpImplPolyPolygon
->mvPolyAry
[nPos
] = rPoly
;
86 const tools::Polygon
& PolyPolygon::GetObject( sal_uInt16 nPos
) const
88 assert(nPos
< Count() && "PolyPolygon::GetObject(): nPos >= nSize");
90 return mpImplPolyPolygon
->mvPolyAry
[nPos
];
93 bool PolyPolygon::IsRect() const
97 bIsRect
= mpImplPolyPolygon
->mvPolyAry
[ 0 ].IsRect();
101 void PolyPolygon::Clear()
103 mpImplPolyPolygon
->mvPolyAry
.clear();
106 void PolyPolygon::Optimize( PolyOptimizeFlags nOptimizeFlags
)
108 if(!(bool(nOptimizeFlags
) && Count()))
111 // #115630# ImplDrawHatch does not work with beziers included in the polypolygon, take care of that
112 bool bIsCurve(false);
114 for(sal_uInt16
a(0); !bIsCurve
&& a
< Count(); a
++)
116 if((*this)[a
].HasFlags())
124 OSL_ENSURE(false, "Optimize does *not* support curves, falling back to AdaptiveSubdivide()...");
125 tools::PolyPolygon aPolyPoly
;
127 AdaptiveSubdivide(aPolyPoly
);
128 aPolyPoly
.Optimize(nOptimizeFlags
);
129 *this = std::move(aPolyPoly
);
134 const bool bEdges
= ( nOptimizeFlags
& PolyOptimizeFlags::EDGES
) == PolyOptimizeFlags::EDGES
;
135 sal_uInt16 nPercent
= 0;
139 const tools::Rectangle
aBound( GetBoundRect() );
141 fArea
= ( aBound
.GetWidth() + aBound
.GetHeight() ) * 0.5;
143 nOptimizeFlags
&= ~PolyOptimizeFlags::EDGES
;
147 for( sal_uInt16 i
= 0, nPolyCount
= mpImplPolyPolygon
->mvPolyAry
.size(); i
< nPolyCount
; i
++ )
151 mpImplPolyPolygon
->mvPolyAry
[ i
].Optimize( PolyOptimizeFlags::NO_SAME
);
152 tools::Polygon::ImplReduceEdges( mpImplPolyPolygon
->mvPolyAry
[ i
], fArea
, nPercent
);
155 if( bool(nOptimizeFlags
) )
156 mpImplPolyPolygon
->mvPolyAry
[ i
].Optimize( nOptimizeFlags
);
161 void PolyPolygon::AdaptiveSubdivide( tools::PolyPolygon
& rResult
) const
165 tools::Polygon aPolygon
;
167 for( size_t i
= 0; i
< mpImplPolyPolygon
->mvPolyAry
.size(); i
++ )
169 mpImplPolyPolygon
->mvPolyAry
[ i
].AdaptiveSubdivide( aPolygon
, 1.0 );
170 rResult
.Insert( aPolygon
);
174 tools::PolyPolygon
PolyPolygon::SubdivideBezier( const tools::PolyPolygon
& rPolyPoly
)
176 sal_uInt16 i
, nPolys
= rPolyPoly
.Count();
177 tools::PolyPolygon
aPolyPoly( nPolys
);
178 for( i
=0; i
<nPolys
; ++i
)
179 aPolyPoly
.Insert( tools::Polygon::SubdivideBezier( rPolyPoly
.GetObject(i
) ) );
185 void PolyPolygon::GetIntersection( const tools::PolyPolygon
& rPolyPoly
, tools::PolyPolygon
& rResult
) const
187 ImplDoOperation( rPolyPoly
, rResult
, PolyClipOp::INTERSECT
);
190 void PolyPolygon::GetUnion( const tools::PolyPolygon
& rPolyPoly
, tools::PolyPolygon
& rResult
) const
192 ImplDoOperation( rPolyPoly
, rResult
, PolyClipOp::UNION
);
195 void PolyPolygon::ImplDoOperation( const tools::PolyPolygon
& rPolyPoly
, tools::PolyPolygon
& rResult
, PolyClipOp nOperation
) const
197 // Convert to B2DPolyPolygon, temporarily. It might be
198 // advantageous in the future, to have a tools::PolyPolygon adaptor that
199 // just simulates a B2DPolyPolygon here...
200 basegfx::B2DPolyPolygon
aMergePolyPolygonA( getB2DPolyPolygon() );
201 basegfx::B2DPolyPolygon
aMergePolyPolygonB( rPolyPoly
.getB2DPolyPolygon() );
203 // normalize the two polypolygons before. Force properly oriented
205 aMergePolyPolygonA
= basegfx::utils::prepareForPolygonOperation( aMergePolyPolygonA
);
206 aMergePolyPolygonB
= basegfx::utils::prepareForPolygonOperation( aMergePolyPolygonB
);
210 // All code extracted from svx/source/svdraw/svedtv2.cxx
212 case PolyClipOp::UNION
:
214 // merge A and B (OR)
215 aMergePolyPolygonA
= basegfx::utils::solvePolygonOperationOr(aMergePolyPolygonA
, aMergePolyPolygonB
);
220 case PolyClipOp::INTERSECT
:
222 // cut poly 1 against polys 2..n (AND)
223 aMergePolyPolygonA
= basegfx::utils::solvePolygonOperationAnd(aMergePolyPolygonA
, aMergePolyPolygonB
);
228 rResult
= tools::PolyPolygon( aMergePolyPolygonA
);
231 sal_uInt16
PolyPolygon::Count() const
233 return mpImplPolyPolygon
->mvPolyAry
.size();
236 void PolyPolygon::Move( tools::Long nHorzMove
, tools::Long nVertMove
)
238 // Required for DrawEngine
239 if( nHorzMove
|| nVertMove
)
242 sal_uInt16 nPolyCount
= mpImplPolyPolygon
->mvPolyAry
.size();
243 for ( sal_uInt16 i
= 0; i
< nPolyCount
; i
++ )
244 mpImplPolyPolygon
->mvPolyAry
[i
].Move( nHorzMove
, nVertMove
);
248 void PolyPolygon::Translate( const Point
& rTrans
)
251 for ( sal_uInt16 i
= 0, nCount
= mpImplPolyPolygon
->mvPolyAry
.size(); i
< nCount
; i
++ )
252 mpImplPolyPolygon
->mvPolyAry
[ i
].Translate( rTrans
);
255 void PolyPolygon::Scale( double fScaleX
, double fScaleY
)
258 for ( sal_uInt16 i
= 0, nCount
= mpImplPolyPolygon
->mvPolyAry
.size(); i
< nCount
; i
++ )
259 mpImplPolyPolygon
->mvPolyAry
[ i
].Scale( fScaleX
, fScaleY
);
262 void PolyPolygon::Rotate( const Point
& rCenter
, Degree10 nAngle10
)
264 nAngle10
%= 3600_deg10
;
268 const double fAngle
= toRadians(nAngle10
);
269 Rotate( rCenter
, sin( fAngle
), cos( fAngle
) );
273 void PolyPolygon::Rotate( const Point
& rCenter
, double fSin
, double fCos
)
276 for ( sal_uInt16 i
= 0, nCount
= mpImplPolyPolygon
->mvPolyAry
.size(); i
< nCount
; i
++ )
277 mpImplPolyPolygon
->mvPolyAry
[ i
].Rotate( rCenter
, fSin
, fCos
);
280 void PolyPolygon::Clip( const tools::Rectangle
& rRect
)
282 sal_uInt16 nPolyCount
= mpImplPolyPolygon
->mvPolyAry
.size();
288 // If there are bezier curves involved, Polygon::Clip() is broken.
289 // Use a temporary B2DPolyPolygon for the clipping.
290 for ( i
= 0; i
< nPolyCount
; i
++ )
292 if(mpImplPolyPolygon
->mvPolyAry
[i
].HasFlags())
294 const basegfx::B2DPolyPolygon
aPoly(
295 basegfx::utils::clipPolyPolygonOnRange(
304 *this = PolyPolygon( aPoly
);
309 // Clip every polygon, deleting the empty ones
310 for ( i
= 0; i
< nPolyCount
; i
++ )
311 mpImplPolyPolygon
->mvPolyAry
[i
].Clip( rRect
);
314 if ( GetObject( nPolyCount
-1 ).GetSize() <= 2 )
315 Remove( nPolyCount
-1 );
320 tools::Rectangle
PolyPolygon::GetBoundRect() const
322 tools::Long nXMin
=0, nXMax
=0, nYMin
=0, nYMax
=0;
324 sal_uInt16 nPolyCount
= mpImplPolyPolygon
->mvPolyAry
.size();
326 for ( sal_uInt16 n
= 0; n
< nPolyCount
; n
++ )
328 const tools::Polygon
* pPoly
= &mpImplPolyPolygon
->mvPolyAry
[n
];
329 const Point
* pAry
= pPoly
->GetConstPointAry();
330 sal_uInt16 nPointCount
= pPoly
->GetSize();
332 for ( sal_uInt16 i
= 0; i
< nPointCount
; i
++ )
334 const Point
* pPt
= &pAry
[ i
];
338 nXMin
= nXMax
= pPt
->X();
339 nYMin
= nYMax
= pPt
->Y();
344 if ( pPt
->X() < nXMin
)
346 if ( pPt
->X() > nXMax
)
348 if ( pPt
->Y() < nYMin
)
350 if ( pPt
->Y() > nYMax
)
357 return tools::Rectangle( nXMin
, nYMin
, nXMax
, nYMax
);
359 return tools::Rectangle();
362 Polygon
& PolyPolygon::operator[]( sal_uInt16 nPos
)
364 assert(nPos
< Count() && "PolyPolygon::[](): nPos >= nSize");
366 return mpImplPolyPolygon
->mvPolyAry
[nPos
];
369 PolyPolygon
& PolyPolygon::operator=( const tools::PolyPolygon
& rPolyPoly
)
371 mpImplPolyPolygon
= rPolyPoly
.mpImplPolyPolygon
;
375 PolyPolygon
& PolyPolygon::operator=( tools::PolyPolygon
&& rPolyPoly
) noexcept
377 mpImplPolyPolygon
= std::move(rPolyPoly
.mpImplPolyPolygon
);
381 bool PolyPolygon::operator==( const tools::PolyPolygon
& rPolyPoly
) const
383 return rPolyPoly
.mpImplPolyPolygon
== mpImplPolyPolygon
;
386 SvStream
& ReadPolyPolygon( SvStream
& rIStream
, tools::PolyPolygon
& rPolyPoly
)
388 sal_uInt16
nPolyCount(0);
390 // Read number of polygons
391 rIStream
.ReadUInt16( nPolyCount
);
393 const size_t nMinRecordSize
= sizeof(sal_uInt16
);
394 const size_t nMaxRecords
= rIStream
.remainingSize() / nMinRecordSize
;
395 if (nPolyCount
> nMaxRecords
)
397 SAL_WARN("tools", "Parsing error: " << nMaxRecords
<<
398 " max possible entries, but " << nPolyCount
<< " claimed, truncating");
399 nPolyCount
= nMaxRecords
;
404 rPolyPoly
.mpImplPolyPolygon
->mvPolyAry
.resize(nPolyCount
);
406 tools::Polygon aTempPoly
;
407 for ( sal_uInt16 i
= 0; i
< nPolyCount
; i
++ )
409 ReadPolygon( rIStream
, aTempPoly
);
410 rPolyPoly
.mpImplPolyPolygon
->mvPolyAry
[i
] = aTempPoly
;
414 rPolyPoly
= tools::PolyPolygon();
419 SvStream
& WritePolyPolygon( SvStream
& rOStream
, const tools::PolyPolygon
& rPolyPoly
)
421 // Write number of polygons
422 sal_uInt16 nPolyCount
= rPolyPoly
.mpImplPolyPolygon
->mvPolyAry
.size();
423 rOStream
.WriteUInt16( nPolyCount
);
426 for ( sal_uInt16 i
= 0; i
< nPolyCount
; i
++ )
427 WritePolygon( rOStream
, rPolyPoly
.mpImplPolyPolygon
->mvPolyAry
[i
] );
432 void PolyPolygon::Read( SvStream
& rIStream
)
434 VersionCompatRead
aCompat(rIStream
);
436 sal_uInt16
nPolyCount(0);
438 // Read number of polygons
439 rIStream
.ReadUInt16( nPolyCount
);
441 const size_t nMinRecordSize
= sizeof(sal_uInt16
);
442 const size_t nMaxRecords
= rIStream
.remainingSize() / nMinRecordSize
;
443 if (nPolyCount
> nMaxRecords
)
445 SAL_WARN("tools", "Parsing error: " << nMaxRecords
<<
446 " max possible entries, but " << nPolyCount
<< " claimed, truncating");
447 nPolyCount
= nMaxRecords
;
452 mpImplPolyPolygon
->mvPolyAry
.clear();
454 for ( sal_uInt16 i
= 0; i
< nPolyCount
; i
++ )
456 tools::Polygon aTempPoly
;
457 aTempPoly
.ImplRead( rIStream
);
458 mpImplPolyPolygon
->mvPolyAry
.emplace_back( aTempPoly
);
462 *this = tools::PolyPolygon();
465 void PolyPolygon::Write( SvStream
& rOStream
) const
467 VersionCompatWrite
aCompat(rOStream
, 1);
469 // Write number of polygons
470 sal_uInt16 nPolyCount
= mpImplPolyPolygon
->mvPolyAry
.size();
471 rOStream
.WriteUInt16( nPolyCount
);
474 for ( sal_uInt16 i
= 0; i
< nPolyCount
; i
++ )
475 mpImplPolyPolygon
->mvPolyAry
[i
].ImplWrite( rOStream
);
478 // convert to basegfx::B2DPolyPolygon and return
479 basegfx::B2DPolyPolygon
PolyPolygon::getB2DPolyPolygon() const
481 basegfx::B2DPolyPolygon aRetval
;
483 for(size_t a(0); a
< mpImplPolyPolygon
->mvPolyAry
.size(); a
++)
485 tools::Polygon
const & rCandidate
= mpImplPolyPolygon
->mvPolyAry
[a
];
486 aRetval
.append(rCandidate
.getB2DPolygon());
492 // constructor to convert from basegfx::B2DPolyPolygon
493 PolyPolygon::PolyPolygon(const basegfx::B2DPolyPolygon
& rPolyPolygon
)
494 : mpImplPolyPolygon(rPolyPolygon
)
498 PolyPolygon::iterator
PolyPolygon::begin()
500 return mpImplPolyPolygon
->begin();
503 PolyPolygon::iterator
PolyPolygon::end()
505 return mpImplPolyPolygon
->end();
508 PolyPolygon::const_iterator
PolyPolygon::begin() const
510 return mpImplPolyPolygon
->begin();
513 PolyPolygon::const_iterator
PolyPolygon::end() const
515 return mpImplPolyPolygon
->end();
518 } /* namespace tools */
520 ImplPolyPolygon::ImplPolyPolygon(const basegfx::B2DPolyPolygon
& rPolyPolygon
)
522 const sal_uInt16
nCount(sal_uInt16(rPolyPolygon
.count()));
523 DBG_ASSERT(sal_uInt32(nCount
) == rPolyPolygon
.count(),
524 "PolyPolygon::PolyPolygon: Too many sub-polygons in given basegfx::B2DPolyPolygon (!)");
528 mvPolyAry
.resize( nCount
);
530 for(sal_uInt16
a(0); a
< nCount
; a
++)
532 const basegfx::B2DPolygon
& aCandidate(rPolyPolygon
.getB2DPolygon(sal_uInt32(a
)));
533 mvPolyAry
[a
] = tools::Polygon( aCandidate
);
537 mvPolyAry
.reserve(16);
540 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */