LibreOffice Module tools (master) 1
poly2.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/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>
31
32namespace tools {
33
34PolyPolygon::PolyPolygon( sal_uInt16 nInitSize )
35 : mpImplPolyPolygon( ImplPolyPolygon( nInitSize ) )
36{
37}
38
40 : mpImplPolyPolygon( rPoly )
41{
42}
44 : mpImplPolyPolygon( tools::Polygon(rRect) )
45{
46}
47
49 : mpImplPolyPolygon( rPolyPoly.mpImplPolyPolygon )
50{
51}
52
54 : mpImplPolyPolygon( std::move(rPolyPoly.mpImplPolyPolygon) )
55{
56}
57
59{
60}
61
62void PolyPolygon::Insert( const tools::Polygon& rPoly, sal_uInt16 nPos )
63{
64 assert ( mpImplPolyPolygon->mvPolyAry.size() < MAX_POLYGONS );
65
66 if ( nPos > mpImplPolyPolygon->mvPolyAry.size() )
67 nPos = mpImplPolyPolygon->mvPolyAry.size();
68
69 mpImplPolyPolygon->mvPolyAry.insert(mpImplPolyPolygon->mvPolyAry.begin() + nPos, rPoly);
70}
71
72void PolyPolygon::Remove( sal_uInt16 nPos )
73{
74 assert(nPos < Count() && "PolyPolygon::Remove(): nPos >= nSize");
75
76 mpImplPolyPolygon->mvPolyAry.erase(mpImplPolyPolygon->mvPolyAry.begin() + nPos);
77}
78
79void PolyPolygon::Replace( const tools::Polygon& rPoly, sal_uInt16 nPos )
80{
81 assert(nPos < Count() && "PolyPolygon::Replace(): nPos >= nSize");
82
83 mpImplPolyPolygon->mvPolyAry[nPos] = rPoly;
84}
85
86const tools::Polygon& PolyPolygon::GetObject( sal_uInt16 nPos ) const
87{
88 assert(nPos < Count() && "PolyPolygon::GetObject(): nPos >= nSize");
89
90 return mpImplPolyPolygon->mvPolyAry[nPos];
91}
92
94{
95 bool bIsRect = false;
96 if ( Count() == 1 )
97 bIsRect = mpImplPolyPolygon->mvPolyAry[ 0 ].IsRect();
98 return bIsRect;
99}
100
102{
103 mpImplPolyPolygon->mvPolyAry.clear();
104}
105
107{
108 if(!(bool(nOptimizeFlags) && Count()))
109 return;
110
111 // #115630# ImplDrawHatch does not work with beziers included in the polypolygon, take care of that
112 bool bIsCurve(false);
113
114 for(sal_uInt16 a(0); !bIsCurve && a < Count(); a++)
115 {
116 if((*this)[a].HasFlags())
117 {
118 bIsCurve = true;
119 }
120 }
121
122 if(bIsCurve)
123 {
124 OSL_ENSURE(false, "Optimize does *not* support curves, falling back to AdaptiveSubdivide()...");
125 tools::PolyPolygon aPolyPoly;
126
127 AdaptiveSubdivide(aPolyPoly);
128 aPolyPoly.Optimize(nOptimizeFlags);
129 *this = aPolyPoly;
130 }
131 else
132 {
133 double fArea;
134 const bool bEdges = ( nOptimizeFlags & PolyOptimizeFlags::EDGES ) == PolyOptimizeFlags::EDGES;
135 sal_uInt16 nPercent = 0;
136
137 if( bEdges )
138 {
139 const tools::Rectangle aBound( GetBoundRect() );
140
141 fArea = ( aBound.GetWidth() + aBound.GetHeight() ) * 0.5;
142 nPercent = 50;
143 nOptimizeFlags &= ~PolyOptimizeFlags::EDGES;
144 }
145
146 // Optimize polygons
147 for( sal_uInt16 i = 0, nPolyCount = mpImplPolyPolygon->mvPolyAry.size(); i < nPolyCount; i++ )
148 {
149 if( bEdges )
150 {
151 mpImplPolyPolygon->mvPolyAry[ i ].Optimize( PolyOptimizeFlags::NO_SAME );
152 tools::Polygon::ImplReduceEdges( mpImplPolyPolygon->mvPolyAry[ i ], fArea, nPercent );
153 }
154
155 if( bool(nOptimizeFlags) )
156 mpImplPolyPolygon->mvPolyAry[ i ].Optimize( nOptimizeFlags );
157 }
158 }
159}
160
162{
163 rResult.Clear();
164
165 tools::Polygon aPolygon;
166
167 for( size_t i = 0; i < mpImplPolyPolygon->mvPolyAry.size(); i++ )
168 {
169 mpImplPolyPolygon->mvPolyAry[ i ].AdaptiveSubdivide( aPolygon, 1.0 );
170 rResult.Insert( aPolygon );
171 }
172}
173
175{
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) ) );
180
181 return aPolyPoly;
182}
183
184
186{
187 ImplDoOperation( rPolyPoly, rResult, PolyClipOp::INTERSECT );
188}
189
190void PolyPolygon::GetUnion( const tools::PolyPolygon& rPolyPoly, tools::PolyPolygon& rResult ) const
191{
192 ImplDoOperation( rPolyPoly, rResult, PolyClipOp::UNION );
193}
194
195void PolyPolygon::ImplDoOperation( const tools::PolyPolygon& rPolyPoly, tools::PolyPolygon& rResult, PolyClipOp nOperation ) const
196{
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() );
202
203 // normalize the two polypolygons before. Force properly oriented
204 // polygons.
205 aMergePolyPolygonA = basegfx::utils::prepareForPolygonOperation( aMergePolyPolygonA );
206 aMergePolyPolygonB = basegfx::utils::prepareForPolygonOperation( aMergePolyPolygonB );
207
208 switch( nOperation )
209 {
210 // All code extracted from svx/source/svdraw/svedtv2.cxx
211
213 {
214 // merge A and B (OR)
215 aMergePolyPolygonA = basegfx::utils::solvePolygonOperationOr(aMergePolyPolygonA, aMergePolyPolygonB);
216 break;
217 }
218
219 default:
221 {
222 // cut poly 1 against polys 2..n (AND)
223 aMergePolyPolygonA = basegfx::utils::solvePolygonOperationAnd(aMergePolyPolygonA, aMergePolyPolygonB);
224 break;
225 }
226 }
227
228 rResult = tools::PolyPolygon( aMergePolyPolygonA );
229}
230
231sal_uInt16 PolyPolygon::Count() const
232{
233 return mpImplPolyPolygon->mvPolyAry.size();
234}
235
236void PolyPolygon::Move( tools::Long nHorzMove, tools::Long nVertMove )
237{
238 // Required for DrawEngine
239 if( nHorzMove || nVertMove )
240 {
241 // move points
242 sal_uInt16 nPolyCount = mpImplPolyPolygon->mvPolyAry.size();
243 for ( sal_uInt16 i = 0; i < nPolyCount; i++ )
244 mpImplPolyPolygon->mvPolyAry[i].Move( nHorzMove, nVertMove );
245 }
246}
247
248void PolyPolygon::Translate( const Point& rTrans )
249{
250 // move points
251 for ( sal_uInt16 i = 0, nCount = mpImplPolyPolygon->mvPolyAry.size(); i < nCount; i++ )
252 mpImplPolyPolygon->mvPolyAry[ i ].Translate( rTrans );
253}
254
255void PolyPolygon::Scale( double fScaleX, double fScaleY )
256{
257 // Move points
258 for ( sal_uInt16 i = 0, nCount = mpImplPolyPolygon->mvPolyAry.size(); i < nCount; i++ )
259 mpImplPolyPolygon->mvPolyAry[ i ].Scale( fScaleX, fScaleY );
260}
261
262void PolyPolygon::Rotate( const Point& rCenter, Degree10 nAngle10 )
263{
264 nAngle10 %= 3600_deg10;
265
266 if( nAngle10 )
267 {
268 const double fAngle = toRadians(nAngle10);
269 Rotate( rCenter, sin( fAngle ), cos( fAngle ) );
270 }
271}
272
273void PolyPolygon::Rotate( const Point& rCenter, double fSin, double fCos )
274{
275 // move points
276 for ( sal_uInt16 i = 0, nCount = mpImplPolyPolygon->mvPolyAry.size(); i < nCount; i++ )
277 mpImplPolyPolygon->mvPolyAry[ i ].Rotate( rCenter, fSin, fCos );
278}
279
281{
282 sal_uInt16 nPolyCount = mpImplPolyPolygon->mvPolyAry.size();
283 sal_uInt16 i;
284
285 if ( !nPolyCount )
286 return;
287
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++ )
291 {
292 if(mpImplPolyPolygon->mvPolyAry[i].HasFlags())
293 {
294 const basegfx::B2DPolyPolygon aPoly(
298 rRect.Left(),
299 rRect.Top(),
300 rRect.Right() + 1,
301 rRect.Bottom() + 1),
302 true,
303 false));
304 *this = PolyPolygon( aPoly );
305 return;
306 }
307 }
308
309 // Clip every polygon, deleting the empty ones
310 for ( i = 0; i < nPolyCount; i++ )
311 mpImplPolyPolygon->mvPolyAry[i].Clip( rRect );
312 while ( nPolyCount )
313 {
314 if ( GetObject( nPolyCount-1 ).GetSize() <= 2 )
315 Remove( nPolyCount-1 );
316 nPolyCount--;
317 }
318}
319
321{
322 tools::Long nXMin=0, nXMax=0, nYMin=0, nYMax=0;
323 bool bFirst = true;
324 sal_uInt16 nPolyCount = mpImplPolyPolygon->mvPolyAry.size();
325
326 for ( sal_uInt16 n = 0; n < nPolyCount; n++ )
327 {
328 const tools::Polygon* pPoly = &mpImplPolyPolygon->mvPolyAry[n];
329 const Point* pAry = pPoly->GetConstPointAry();
330 sal_uInt16 nPointCount = pPoly->GetSize();
331
332 for ( sal_uInt16 i = 0; i < nPointCount; i++ )
333 {
334 const Point* pPt = &pAry[ i ];
335
336 if ( bFirst )
337 {
338 nXMin = nXMax = pPt->X();
339 nYMin = nYMax = pPt->Y();
340 bFirst = false;
341 }
342 else
343 {
344 if ( pPt->X() < nXMin )
345 nXMin = pPt->X();
346 if ( pPt->X() > nXMax )
347 nXMax = pPt->X();
348 if ( pPt->Y() < nYMin )
349 nYMin = pPt->Y();
350 if ( pPt->Y() > nYMax )
351 nYMax = pPt->Y();
352 }
353 }
354 }
355
356 if ( !bFirst )
357 return tools::Rectangle( nXMin, nYMin, nXMax, nYMax );
358 else
359 return tools::Rectangle();
360}
361
363{
364 assert(nPos < Count() && "PolyPolygon::[](): nPos >= nSize");
365
366 return mpImplPolyPolygon->mvPolyAry[nPos];
367}
368
370{
372 return *this;
373}
374
376{
377 mpImplPolyPolygon = std::move(rPolyPoly.mpImplPolyPolygon);
378 return *this;
379}
380
381bool PolyPolygon::operator==( const tools::PolyPolygon& rPolyPoly ) const
382{
383 return rPolyPoly.mpImplPolyPolygon == mpImplPolyPolygon;
384}
385
387{
388 sal_uInt16 nPolyCount(0);
389
390 // Read number of polygons
391 rIStream.ReadUInt16( nPolyCount );
392
393 const size_t nMinRecordSize = sizeof(sal_uInt16);
394 const size_t nMaxRecords = rIStream.remainingSize() / nMinRecordSize;
395 if (nPolyCount > nMaxRecords)
396 {
397 SAL_WARN("tools", "Parsing error: " << nMaxRecords <<
398 " max possible entries, but " << nPolyCount << " claimed, truncating");
399 nPolyCount = nMaxRecords;
400 }
401
402 if( nPolyCount )
403 {
404 rPolyPoly.mpImplPolyPolygon->mvPolyAry.resize(nPolyCount);
405
406 tools::Polygon aTempPoly;
407 for ( sal_uInt16 i = 0; i < nPolyCount; i++ )
408 {
409 ReadPolygon( rIStream, aTempPoly );
410 rPolyPoly.mpImplPolyPolygon->mvPolyAry[i] = aTempPoly;
411 }
412 }
413 else
414 rPolyPoly = tools::PolyPolygon();
415
416 return rIStream;
417}
418
420{
421 // Write number of polygons
422 sal_uInt16 nPolyCount = rPolyPoly.mpImplPolyPolygon->mvPolyAry.size();
423 rOStream.WriteUInt16( nPolyCount );
424
425 // output polygons
426 for ( sal_uInt16 i = 0; i < nPolyCount; i++ )
427 WritePolygon( rOStream, rPolyPoly.mpImplPolyPolygon->mvPolyAry[i] );
428
429 return rOStream;
430}
431
433{
434 VersionCompatRead aCompat(rIStream);
435
436 sal_uInt16 nPolyCount(0);
437
438 // Read number of polygons
439 rIStream.ReadUInt16( nPolyCount );
440
441 const size_t nMinRecordSize = sizeof(sal_uInt16);
442 const size_t nMaxRecords = rIStream.remainingSize() / nMinRecordSize;
443 if (nPolyCount > nMaxRecords)
444 {
445 SAL_WARN("tools", "Parsing error: " << nMaxRecords <<
446 " max possible entries, but " << nPolyCount << " claimed, truncating");
447 nPolyCount = nMaxRecords;
448 }
449
450 if( nPolyCount )
451 {
452 mpImplPolyPolygon->mvPolyAry.clear();
453
454 for ( sal_uInt16 i = 0; i < nPolyCount; i++ )
455 {
456 tools::Polygon aTempPoly;
457 aTempPoly.ImplRead( rIStream );
458 mpImplPolyPolygon->mvPolyAry.emplace_back( aTempPoly );
459 }
460 }
461 else
462 *this = tools::PolyPolygon();
463}
464
465void PolyPolygon::Write( SvStream& rOStream ) const
466{
467 VersionCompatWrite aCompat(rOStream, 1);
468
469 // Write number of polygons
470 sal_uInt16 nPolyCount = mpImplPolyPolygon->mvPolyAry.size();
471 rOStream.WriteUInt16( nPolyCount );
472
473 // Output polygons
474 for ( sal_uInt16 i = 0; i < nPolyCount; i++ )
475 mpImplPolyPolygon->mvPolyAry[i].ImplWrite( rOStream );
476}
477
478// convert to basegfx::B2DPolyPolygon and return
480{
482
483 for(size_t a(0); a < mpImplPolyPolygon->mvPolyAry.size(); a++)
484 {
485 tools::Polygon const & rCandidate = mpImplPolyPolygon->mvPolyAry[a];
486 aRetval.append(rCandidate.getB2DPolygon());
487 }
488
489 return aRetval;
490}
491
492// constructor to convert from basegfx::B2DPolyPolygon
494 : mpImplPolyPolygon(rPolyPolygon)
495{
496}
497
498} /* namespace tools */
499
501{
502 const sal_uInt16 nCount(sal_uInt16(rPolyPolygon.count()));
503 DBG_ASSERT(sal_uInt32(nCount) == rPolyPolygon.count(),
504 "PolyPolygon::PolyPolygon: Too many sub-polygons in given basegfx::B2DPolyPolygon (!)");
505
506 if ( nCount )
507 {
508 mvPolyAry.resize( nCount );
509
510 for(sal_uInt16 a(0); a < nCount; a++)
511 {
512 const basegfx::B2DPolygon& aCandidate(rPolyPolygon.getB2DPolygon(sal_uInt32(a)));
513 mvPolyAry[a] = tools::Polygon( aCandidate );
514 }
515 }
516 else
517 mvPolyAry.reserve(16);
518}
519
520/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Definition: gen.hxx:78
constexpr tools::Long Y() const
Definition: gen.hxx:84
constexpr tools::Long X() const
Definition: gen.hxx:83
SvStream & WriteUInt16(sal_uInt16 nUInt16)
Definition: stream.cxx:949
SvStream & ReadUInt16(sal_uInt16 &rUInt16)
Definition: stream.cxx:821
sal_uInt64 remainingSize()
Definition: stream.cxx:1320
B2DPolygon const & getB2DPolygon(sal_uInt32 nIndex) const
void append(const B2DPolygon &rPolygon, sal_uInt32 nCount=1)
sal_uInt32 count() const
TOOLS_DLLPRIVATE void ImplDoOperation(const tools::PolyPolygon &rPolyPoly, tools::PolyPolygon &rResult, PolyClipOp nOperation) const
Definition: poly2.cxx:195
sal_uInt16 Count() const
Definition: poly2.cxx:231
void Translate(const Point &rTrans)
Definition: poly2.cxx:248
void AdaptiveSubdivide(tools::PolyPolygon &rResult) const
Adaptive subdivision of polygons with curves.
Definition: poly2.cxx:161
void GetIntersection(const tools::PolyPolygon &rPolyPoly, tools::PolyPolygon &rResult) const
Definition: poly2.cxx:185
tools::PolyPolygon & operator=(const tools::PolyPolygon &rPolyPoly)
Definition: poly2.cxx:369
static tools::PolyPolygon SubdivideBezier(const tools::PolyPolygon &rPolyPoly)
Definition: poly2.cxx:174
void Write(SvStream &rOStream) const
Definition: poly2.cxx:465
::basegfx::B2DPolyPolygon getB2DPolyPolygon() const
Definition: poly2.cxx:479
void Read(SvStream &rIStream)
Definition: poly2.cxx:432
void Clip(const tools::Rectangle &rRect)
Definition: poly2.cxx:280
PolyPolygon(sal_uInt16 nInitSize=16)
Definition: poly2.cxx:34
bool IsRect() const
Definition: poly2.cxx:93
bool operator==(const tools::PolyPolygon &rPolyPoly) const
Definition: poly2.cxx:381
o3tl::cow_wrapper< ImplPolyPolygon > mpImplPolyPolygon
Definition: poly.hxx:190
void Move(tools::Long nHorzMove, tools::Long nVertMove)
Definition: poly2.cxx:236
void Insert(const tools::Polygon &rPoly, sal_uInt16 nPos=POLYPOLY_APPEND)
Definition: poly2.cxx:62
void Rotate(const Point &rCenter, double fSin, double fCos)
Definition: poly2.cxx:273
const tools::Polygon & GetObject(sal_uInt16 nPos) const
Definition: poly2.cxx:86
tools::Rectangle GetBoundRect() const
Definition: poly2.cxx:320
void Scale(double fScaleX, double fScaleY)
Definition: poly2.cxx:255
void GetUnion(const tools::PolyPolygon &rPolyPoly, tools::PolyPolygon &rResult) const
Definition: poly2.cxx:190
void Replace(const Polygon &rPoly, sal_uInt16 nPos)
Definition: poly2.cxx:79
void Remove(sal_uInt16 nPos)
Definition: poly2.cxx:72
const tools::Polygon & operator[](sal_uInt16 nPos) const
Definition: poly.hxx:245
void Optimize(PolyOptimizeFlags nOptimizeFlags)
Definition: poly2.cxx:106
::basegfx::B2DPolygon getB2DPolygon() const
Definition: poly.cxx:1773
const Point * GetConstPointAry() const
Definition: poly.cxx:938
sal_uInt16 GetSize() const
Definition: poly.cxx:1018
void ImplRead(SvStream &rIStream)
Definition: poly.cxx:1668
static Polygon SubdivideBezier(const Polygon &rPoly)
Definition: poly.cxx:872
static void ImplReduceEdges(tools::Polygon &rPoly, const double &rArea, sal_uInt16 nPercent)
Definition: poly.cxx:1280
constexpr tools::Long GetWidth() const
Returns the difference between right and left, assuming the range is inclusive.
Definition: gen.hxx:694
constexpr tools::Long Top() const
Definition: gen.hxx:502
constexpr tools::Long Right() const
Definition: gen.hxx:501
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
int nCount
#define DBG_ASSERT(sCon, aError)
Definition: debug.hxx:57
double toRadians(D x)
Definition: degree.hxx:57
sal_Int64 n
uno_Any a
sal_uInt16 nPos
#define SAL_WARN(area, stream)
B2DPolyPolygon prepareForPolygonOperation(const B2DPolygon &rCandidate)
B2DPolyPolygon solvePolygonOperationOr(const B2DPolyPolygon &rCandidateA, const B2DPolyPolygon &rCandidateB)
B2DPolyPolygon clipPolyPolygonOnRange(const B2DPolyPolygon &rCandidate, const B2DRange &rRange, bool bInside, bool bStroke)
B2DPolyPolygon solvePolygonOperationAnd(const B2DPolyPolygon &rCandidateA, const B2DPolyPolygon &rCandidateB)
int i
Note: this class is a true marvel of engineering: because the author could not decide whether it's be...
SvStream & WritePolyPolygon(SvStream &rOStream, const tools::PolyPolygon &rPolyPoly)
Definition: poly2.cxx:419
long Long
Definition: long.hxx:34
SvStream & ReadPolyPolygon(SvStream &rIStream, tools::PolyPolygon &rPolyPoly)
Definition: poly2.cxx:386
SvStream & ReadPolygon(SvStream &rIStream, tools::Polygon &rPoly)
Definition: poly.cxx:1625
SvStream & WritePolygon(SvStream &rOStream, const tools::Polygon &rPoly)
Definition: poly.cxx:1652
#define MAX_POLYGONS
Definition: poly.h:54
PolyOptimizeFlags
Definition: poly.hxx:34
ImplPolyPolygon(sal_uInt16 nInitSize)
Definition: poly.h:60
std::vector< tools::Polygon > mvPolyAry
Definition: poly.h:58