/src/libreoffice/basegfx/source/polygon/b2dpolygoncutandtouch.cxx
Line | Count | Source |
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 <basegfx/polygon/b2dpolygoncutandtouch.hxx> |
21 | | #include <osl/diagnose.h> |
22 | | #include <sal/log.hxx> |
23 | | #include <basegfx/numeric/ftools.hxx> |
24 | | #include <basegfx/point/b2dpoint.hxx> |
25 | | #include <basegfx/vector/b2dvector.hxx> |
26 | | #include <basegfx/vector/b2enums.hxx> |
27 | | #include <basegfx/range/b2drange.hxx> |
28 | | #include <basegfx/polygon/b2dpolygon.hxx> |
29 | | #include <basegfx/polygon/b2dpolypolygon.hxx> |
30 | | #include <basegfx/polygon/b2dpolygontools.hxx> |
31 | | #include <basegfx/curve/b2dcubicbezier.hxx> |
32 | | |
33 | | #include <vector> |
34 | | #include <algorithm> |
35 | | #include <memory> |
36 | | |
37 | 0 | #define SUBDIVIDE_FOR_CUT_TEST_COUNT (50) |
38 | | |
39 | | namespace basegfx |
40 | | { |
41 | | namespace |
42 | | { |
43 | | |
44 | | class temporaryPoint |
45 | | { |
46 | | B2DPoint maPoint; // the new point |
47 | | sal_uInt32 mnIndex; // index after which to insert |
48 | | double mfCut; // parametric cut description [0.0 .. 1.0] |
49 | | |
50 | | public: |
51 | | temporaryPoint(const B2DPoint& rNewPoint, sal_uInt32 nIndex, double fCut) |
52 | 12.0M | : maPoint(rNewPoint), |
53 | 12.0M | mnIndex(nIndex), |
54 | 12.0M | mfCut(fCut) |
55 | 12.0M | { |
56 | 12.0M | } |
57 | | |
58 | | bool operator<(const temporaryPoint& rComp) const |
59 | 31.7M | { |
60 | 31.7M | if(mnIndex == rComp.mnIndex) |
61 | 11.4M | { |
62 | 11.4M | return (mfCut < rComp.mfCut); |
63 | 11.4M | } |
64 | | |
65 | 20.3M | return (mnIndex < rComp.mnIndex); |
66 | 31.7M | } |
67 | | |
68 | 2.69M | const B2DPoint& getPoint() const { return maPoint; } |
69 | 4.10M | sal_uInt32 getIndex() const { return mnIndex; } |
70 | 0 | double getCut() const { return mfCut; } |
71 | | }; |
72 | | |
73 | | typedef std::vector< temporaryPoint > temporaryPointVector; |
74 | | |
75 | | class temporaryPolygonData |
76 | | { |
77 | | B2DPolygon maPolygon; |
78 | | B2DRange maRange; |
79 | | temporaryPointVector maPoints; |
80 | | |
81 | | public: |
82 | 426k | const B2DPolygon& getPolygon() const { return maPolygon; } |
83 | 31.8k | void setPolygon(const B2DPolygon& rNew) { maPolygon = rNew; maRange = maPolygon.getB2DRange(); } |
84 | 6.06M | const B2DRange& getRange() const { return maRange; } |
85 | 291k | temporaryPointVector& getTemporaryPointVector() { return maPoints; } |
86 | | }; |
87 | | |
88 | | B2DPolygon mergeTemporaryPointsAndPolygon(const B2DPolygon& rCandidate, temporaryPointVector& rTempPoints) |
89 | 490k | { |
90 | | // #i76891# mergeTemporaryPointsAndPolygon redesigned to be able to correctly handle |
91 | | // single edges with/without control points |
92 | | // #i101491# added counter for non-changing element count |
93 | 490k | const sal_uInt32 nTempPointCount(rTempPoints.size()); |
94 | | |
95 | 490k | if(nTempPointCount) |
96 | 5.71k | { |
97 | 5.71k | B2DPolygon aRetval; |
98 | 5.71k | const sal_uInt32 nCount(rCandidate.count()); |
99 | | |
100 | 5.71k | if(nCount) |
101 | 5.71k | { |
102 | | // sort temp points to assure increasing fCut values and increasing indices |
103 | 5.71k | std::sort(rTempPoints.begin(), rTempPoints.end()); |
104 | | |
105 | | // prepare loop |
106 | 5.71k | B2DCubicBezier aEdge; |
107 | 5.71k | sal_uInt32 nNewInd(0); |
108 | | |
109 | | // add start point |
110 | 5.71k | aRetval.append(rCandidate.getB2DPoint(0)); |
111 | | |
112 | 1.61M | for(sal_uInt32 a(0); a < nCount; a++) |
113 | 1.61M | { |
114 | | // get edge |
115 | 1.61M | rCandidate.getBezierSegment(a, aEdge); |
116 | | |
117 | 1.61M | if(aEdge.isBezier()) |
118 | 0 | { |
119 | | // control vectors involved for this edge |
120 | 0 | double fLeftStart(0.0); |
121 | | |
122 | | // now add all points targeted to be at this index |
123 | 0 | while (nNewInd < nTempPointCount && rTempPoints[nNewInd].getIndex() == a && fLeftStart < 1.0) |
124 | 0 | { |
125 | 0 | const temporaryPoint& rTempPoint = rTempPoints[nNewInd++]; |
126 | | |
127 | | // split curve segment. Splits need to come sorted and need to be < 1.0. Also, |
128 | | // since original segment is consumed from left to right, the cut values need |
129 | | // to be scaled to the remaining part |
130 | 0 | B2DCubicBezier aLeftPart; |
131 | 0 | const double fRelativeSplitPoint((rTempPoint.getCut() - fLeftStart) / (1.0 - fLeftStart)); |
132 | 0 | aEdge.split(fRelativeSplitPoint, &aLeftPart, &aEdge); |
133 | 0 | fLeftStart = rTempPoint.getCut(); |
134 | | |
135 | | // add left bow |
136 | 0 | aRetval.appendBezierSegment(aLeftPart.getControlPointA(), aLeftPart.getControlPointB(), rTempPoint.getPoint()); |
137 | 0 | } |
138 | | |
139 | | // add remaining bow |
140 | 0 | aRetval.appendBezierSegment(aEdge.getControlPointA(), aEdge.getControlPointB(), aEdge.getEndPoint()); |
141 | 0 | } |
142 | 1.61M | else |
143 | 1.61M | { |
144 | | // add all points targeted to be at this index |
145 | 4.30M | while(nNewInd < nTempPointCount && rTempPoints[nNewInd].getIndex() == a) |
146 | 2.69M | { |
147 | 2.69M | const temporaryPoint& rTempPoint = rTempPoints[nNewInd++]; |
148 | 2.69M | const B2DPoint& aNewPoint(rTempPoint.getPoint()); |
149 | | |
150 | | // do not add points double |
151 | 2.69M | if(!aRetval.getB2DPoint(aRetval.count() - 1).equal(aNewPoint)) |
152 | 817k | { |
153 | 817k | aRetval.append(aNewPoint); |
154 | 817k | } |
155 | 2.69M | } |
156 | | |
157 | | // add edge end point |
158 | 1.61M | aRetval.append(aEdge.getEndPoint()); |
159 | 1.61M | } |
160 | 1.61M | } |
161 | 5.71k | } |
162 | | |
163 | 5.71k | if(rCandidate.isClosed()) |
164 | 4.25k | { |
165 | | // set closed flag and correct last point (which is added double now). |
166 | 4.25k | utils::closeWithGeometryChange(aRetval); |
167 | 4.25k | } |
168 | | |
169 | 5.71k | return aRetval; |
170 | 5.71k | } |
171 | 484k | else |
172 | 484k | { |
173 | 484k | return rCandidate; |
174 | 484k | } |
175 | 490k | } |
176 | | |
177 | | void adaptAndTransferCutsWithBezierSegment( |
178 | | const temporaryPointVector& rPointVector, const B2DPolygon& rPolygon, |
179 | | sal_uInt32 nInd, temporaryPointVector& rTempPoints) |
180 | 0 | { |
181 | | // assuming that the subdivision to create rPolygon used equidistant pieces |
182 | | // (as in adaptiveSubdivideByCount) it is now possible to calculate back the |
183 | | // cut positions in the polygon to relative cut positions on the original bezier |
184 | | // segment. |
185 | 0 | const sal_uInt32 nEdgeCount(rPolygon.count() ? rPolygon.count() - 1 : 0); |
186 | |
|
187 | 0 | if(!rPointVector.empty() && nEdgeCount) |
188 | 0 | { |
189 | 0 | for( const auto& rTempPoint : rPointVector ) |
190 | 0 | { |
191 | 0 | const double fCutPosInPolygon(static_cast<double>(rTempPoint.getIndex()) + rTempPoint.getCut()); |
192 | 0 | const double fRelativeCutPos(fCutPosInPolygon / static_cast<double>(nEdgeCount)); |
193 | 0 | rTempPoints.emplace_back(rTempPoint.getPoint(), nInd, fRelativeCutPos); |
194 | 0 | } |
195 | 0 | } |
196 | 0 | } |
197 | | |
198 | | } // end of anonymous namespace |
199 | | } // end of namespace basegfx |
200 | | |
201 | | namespace basegfx |
202 | | { |
203 | | namespace |
204 | | { |
205 | | |
206 | | // predefines for calls to this methods before method implementation |
207 | | |
208 | | void findCuts(const B2DPolygon& rCandidate, temporaryPointVector& rTempPoints, size_t* pPointLimit = nullptr); |
209 | | void findTouches(const B2DPolygon& rEdgePolygon, const B2DPolygon& rPointPolygon, temporaryPointVector& rTempPoints); |
210 | | void findCuts(const B2DPolygon& rCandidateA, const B2DPolygon& rCandidateB, temporaryPointVector& rTempPointsA, temporaryPointVector& rTempPointsB); |
211 | | |
212 | | void findEdgeCutsTwoEdges( |
213 | | const B2DPoint& rCurrA, const B2DPoint& rNextA, |
214 | | const B2DPoint& rCurrB, const B2DPoint& rNextB, |
215 | | sal_uInt32 nIndA, sal_uInt32 nIndB, |
216 | | temporaryPointVector& rTempPointsA, temporaryPointVector& rTempPointsB) |
217 | 13.4M | { |
218 | | // no null length edges |
219 | 13.4M | if(rCurrA.equal(rNextA) || rCurrB.equal(rNextB)) |
220 | 3.12M | return; |
221 | | |
222 | | // no common start/end points, this can be no cuts |
223 | 10.3M | if(rCurrB.equal(rCurrA) || rCurrB.equal(rNextA) || rNextB.equal(rCurrA) || rNextB.equal(rNextA)) |
224 | 7.25M | return; |
225 | | |
226 | 3.09M | const B2DVector aVecA(rNextA - rCurrA); |
227 | 3.09M | const B2DVector aVecB(rNextB - rCurrB); |
228 | 3.09M | double fCut(aVecA.cross(aVecB)); |
229 | | |
230 | 3.09M | if(fTools::equalZero(fCut)) |
231 | 560k | return; |
232 | | |
233 | 2.53M | const double fZero(0.0); |
234 | 2.53M | const double fOne(1.0); |
235 | 2.53M | fCut = (aVecB.getY() * (rCurrB.getX() - rCurrA.getX()) + aVecB.getX() * (rCurrA.getY() - rCurrB.getY())) / fCut; |
236 | | |
237 | 2.53M | if (!fTools::betweenOrEqualEither(fCut, fZero, fOne)) |
238 | 891k | return; |
239 | | |
240 | | // it's a candidate, but also need to test parameter value of cut on line 2 |
241 | 1.64M | double fCut2; |
242 | | |
243 | | // choose the more precise version |
244 | 1.64M | if(fabs(aVecB.getX()) > fabs(aVecB.getY())) |
245 | 1.12M | { |
246 | 1.12M | fCut2 = (rCurrA.getX() + (fCut * aVecA.getX()) - rCurrB.getX()) / aVecB.getX(); |
247 | 1.12M | } |
248 | 519k | else |
249 | 519k | { |
250 | 519k | fCut2 = (rCurrA.getY() + (fCut * aVecA.getY()) - rCurrB.getY()) / aVecB.getY(); |
251 | 519k | } |
252 | | |
253 | 1.64M | if (fTools::betweenOrEqualEither(fCut2, fZero, fOne)) |
254 | 816k | { |
255 | | // cut is in range, add point. Two edges can have only one cut, but |
256 | | // add a cut point to each list. The lists may be the same for |
257 | | // self intersections. |
258 | 816k | const B2DPoint aCutPoint(interpolate(rCurrA, rNextA, fCut)); |
259 | 816k | rTempPointsA.emplace_back(aCutPoint, nIndA, fCut); |
260 | 816k | rTempPointsB.emplace_back(aCutPoint, nIndB, fCut2); |
261 | 816k | } |
262 | 1.64M | } |
263 | | |
264 | | void findCutsAndTouchesAndCommonForBezier(const B2DPolygon& rCandidateA, const B2DPolygon& rCandidateB, temporaryPointVector& rTempPointsA, temporaryPointVector& rTempPointsB) |
265 | 0 | { |
266 | | // #i76891# |
267 | | // This new method is necessary since in findEdgeCutsBezierAndEdge and in findEdgeCutsTwoBeziers |
268 | | // it is not sufficient to use findCuts() recursively. This will indeed find the cuts between the |
269 | | // segments of the two temporarily adaptive subdivided bezier segments, but not the touches or |
270 | | // equal points of them. |
271 | | // It would be possible to find the touches using findTouches(), but at last with common points |
272 | | // the adding of cut points (temporary points) would fail. But for these temporarily adaptive |
273 | | // subdivided bezier segments, common points may be not very likely, but the bug shows that it |
274 | | // happens. |
275 | | // Touch points are a little bit more likely than common points. All in all it is best to use |
276 | | // a specialized method here which can profit from knowing that it is working on a special |
277 | | // family of B2DPolygons: no curve segments included and not closed. |
278 | 0 | OSL_ENSURE(!rCandidateA.areControlPointsUsed() && !rCandidateB.areControlPointsUsed(), "findCutsAndTouchesAndCommonForBezier only works with subdivided polygons (!)"); |
279 | 0 | OSL_ENSURE(!rCandidateA.isClosed() && !rCandidateB.isClosed(), "findCutsAndTouchesAndCommonForBezier only works with opened polygons (!)"); |
280 | 0 | const sal_uInt32 nPointCountA(rCandidateA.count()); |
281 | 0 | const sal_uInt32 nPointCountB(rCandidateB.count()); |
282 | |
|
283 | 0 | if(nPointCountA <= 1 || nPointCountB <= 1) |
284 | 0 | return; |
285 | | |
286 | 0 | const sal_uInt32 nEdgeCountA(nPointCountA - 1); |
287 | 0 | const sal_uInt32 nEdgeCountB(nPointCountB - 1); |
288 | 0 | B2DPoint aCurrA(rCandidateA.getB2DPoint(0)); |
289 | |
|
290 | 0 | for(sal_uInt32 a(0); a < nEdgeCountA; a++) |
291 | 0 | { |
292 | 0 | const B2DPoint aNextA(rCandidateA.getB2DPoint(a + 1)); |
293 | 0 | const B2DRange aRangeA(aCurrA, aNextA); |
294 | 0 | B2DPoint aCurrB(rCandidateB.getB2DPoint(0)); |
295 | |
|
296 | 0 | for(sal_uInt32 b(0); b < nEdgeCountB; b++) |
297 | 0 | { |
298 | 0 | const B2DPoint aNextB(rCandidateB.getB2DPoint(b + 1)); |
299 | 0 | const B2DRange aRangeB(aCurrB, aNextB); |
300 | |
|
301 | 0 | if(aRangeA.overlaps(aRangeB)) |
302 | 0 | { |
303 | | // no null length edges |
304 | 0 | if(!(aCurrA.equal(aNextA) || aCurrB.equal(aNextB))) |
305 | 0 | { |
306 | 0 | const B2DVector aVecA(aNextA - aCurrA); |
307 | 0 | const B2DVector aVecB(aNextB - aCurrB); |
308 | 0 | double fCutA(aVecA.cross(aVecB)); |
309 | |
|
310 | 0 | if(!fTools::equalZero(fCutA)) |
311 | 0 | { |
312 | 0 | const double fZero(0.0); |
313 | 0 | const double fOne(1.0); |
314 | 0 | fCutA = (aVecB.getY() * (aCurrB.getX() - aCurrA.getX()) + aVecB.getX() * (aCurrA.getY() - aCurrB.getY())) / fCutA; |
315 | | |
316 | | // use range [0.0 .. 1.0[, thus in the loop, all direct aCurrA cuts will be registered |
317 | | // as 0.0 cut. The 1.0 cut will be registered in the next loop step |
318 | 0 | if(fTools::moreOrEqual(fCutA, fZero) && fTools::less(fCutA, fOne)) |
319 | 0 | { |
320 | | // it's a candidate, but also need to test parameter value of cut on line 2 |
321 | 0 | double fCutB; |
322 | | |
323 | | // choose the more precise version |
324 | 0 | if(fabs(aVecB.getX()) > fabs(aVecB.getY())) |
325 | 0 | { |
326 | 0 | fCutB = (aCurrA.getX() + (fCutA * aVecA.getX()) - aCurrB.getX()) / aVecB.getX(); |
327 | 0 | } |
328 | 0 | else |
329 | 0 | { |
330 | 0 | fCutB = (aCurrA.getY() + (fCutA * aVecA.getY()) - aCurrB.getY()) / aVecB.getY(); |
331 | 0 | } |
332 | | |
333 | | // use range [0.0 .. 1.0[, thus in the loop, all direct aCurrA cuts will be registered |
334 | | // as 0.0 cut. The 1.0 cut will be registered in the next loop step |
335 | 0 | if(fTools::moreOrEqual(fCutB, fZero) && fTools::less(fCutB, fOne)) |
336 | 0 | { |
337 | | // cut is in both ranges. Add points for A and B |
338 | | // #i111715# use fTools::equal instead of fTools::equalZero for better accuracy |
339 | 0 | if(fTools::equal(fCutA, fZero)) |
340 | 0 | { |
341 | | // ignore for start point in first edge; this is handled |
342 | | // by outer methods and would just produce a double point |
343 | 0 | if(a) |
344 | 0 | { |
345 | 0 | rTempPointsA.emplace_back(aCurrA, a, 0.0); |
346 | 0 | } |
347 | 0 | } |
348 | 0 | else |
349 | 0 | { |
350 | 0 | const B2DPoint aCutPoint(interpolate(aCurrA, aNextA, fCutA)); |
351 | 0 | rTempPointsA.emplace_back(aCutPoint, a, fCutA); |
352 | 0 | } |
353 | | |
354 | | // #i111715# use fTools::equal instead of fTools::equalZero for better accuracy |
355 | 0 | if(fTools::equal(fCutB, fZero)) |
356 | 0 | { |
357 | | // ignore for start point in first edge; this is handled |
358 | | // by outer methods and would just produce a double point |
359 | 0 | if(b) |
360 | 0 | { |
361 | 0 | rTempPointsB.emplace_back(aCurrB, b, 0.0); |
362 | 0 | } |
363 | 0 | } |
364 | 0 | else |
365 | 0 | { |
366 | 0 | const B2DPoint aCutPoint(interpolate(aCurrB, aNextB, fCutB)); |
367 | 0 | rTempPointsB.emplace_back(aCutPoint, b, fCutB); |
368 | 0 | } |
369 | 0 | } |
370 | 0 | } |
371 | 0 | } |
372 | 0 | } |
373 | 0 | } |
374 | | |
375 | | // prepare next step |
376 | 0 | aCurrB = aNextB; |
377 | 0 | } |
378 | | |
379 | | // prepare next step |
380 | 0 | aCurrA = aNextA; |
381 | 0 | } |
382 | 0 | } |
383 | | |
384 | | void findEdgeCutsBezierAndEdge( |
385 | | const B2DCubicBezier& rCubicA, |
386 | | const B2DPoint& rCurrB, const B2DPoint& rNextB, |
387 | | sal_uInt32 nIndA, sal_uInt32 nIndB, |
388 | | temporaryPointVector& rTempPointsA, temporaryPointVector& rTempPointsB) |
389 | 0 | { |
390 | | // find all cuts between given bezier segment and edge. Add an entry to the tempPoints |
391 | | // for each common point with the cut value describing the relative position on given |
392 | | // bezier segment and edge. |
393 | 0 | B2DPolygon aTempPolygonA; |
394 | 0 | B2DPolygon aTempPolygonEdge; |
395 | 0 | temporaryPointVector aTempPointVectorA; |
396 | 0 | temporaryPointVector aTempPointVectorEdge; |
397 | | |
398 | | // create subdivided polygons and find cuts between them |
399 | | // Keep adaptiveSubdivideByCount due to needed quality |
400 | 0 | aTempPolygonA.reserve(SUBDIVIDE_FOR_CUT_TEST_COUNT + 8); |
401 | 0 | aTempPolygonA.append(rCubicA.getStartPoint()); |
402 | 0 | rCubicA.adaptiveSubdivideByCount(aTempPolygonA, SUBDIVIDE_FOR_CUT_TEST_COUNT); |
403 | 0 | aTempPolygonEdge.append(rCurrB); |
404 | 0 | aTempPolygonEdge.append(rNextB); |
405 | | |
406 | | // #i76891# using findCuts recursively is not sufficient here |
407 | 0 | findCutsAndTouchesAndCommonForBezier(aTempPolygonA, aTempPolygonEdge, aTempPointVectorA, aTempPointVectorEdge); |
408 | |
|
409 | 0 | if(!aTempPointVectorA.empty()) |
410 | 0 | { |
411 | | // adapt tempVector entries to segment |
412 | 0 | adaptAndTransferCutsWithBezierSegment(aTempPointVectorA, aTempPolygonA, nIndA, rTempPointsA); |
413 | 0 | } |
414 | | |
415 | | // append remapped tempVector entries for edge to tempPoints for edge |
416 | 0 | for(const temporaryPoint & rTempPoint : aTempPointVectorEdge) |
417 | 0 | { |
418 | 0 | rTempPointsB.emplace_back(rTempPoint.getPoint(), nIndB, rTempPoint.getCut()); |
419 | 0 | } |
420 | 0 | } |
421 | | |
422 | | void findEdgeCutsTwoBeziers( |
423 | | const B2DCubicBezier& rCubicA, |
424 | | const B2DCubicBezier& rCubicB, |
425 | | sal_uInt32 nIndA, sal_uInt32 nIndB, |
426 | | temporaryPointVector& rTempPointsA, temporaryPointVector& rTempPointsB) |
427 | 0 | { |
428 | | // find all cuts between the two given bezier segments. Add an entry to the tempPoints |
429 | | // for each common point with the cut value describing the relative position on given |
430 | | // bezier segments. |
431 | 0 | B2DPolygon aTempPolygonA; |
432 | 0 | B2DPolygon aTempPolygonB; |
433 | 0 | temporaryPointVector aTempPointVectorA; |
434 | 0 | temporaryPointVector aTempPointVectorB; |
435 | | |
436 | | // create subdivided polygons and find cuts between them |
437 | | // Keep adaptiveSubdivideByCount due to needed quality |
438 | 0 | aTempPolygonA.reserve(SUBDIVIDE_FOR_CUT_TEST_COUNT + 8); |
439 | 0 | aTempPolygonA.append(rCubicA.getStartPoint()); |
440 | 0 | rCubicA.adaptiveSubdivideByCount(aTempPolygonA, SUBDIVIDE_FOR_CUT_TEST_COUNT); |
441 | 0 | aTempPolygonB.reserve(SUBDIVIDE_FOR_CUT_TEST_COUNT + 8); |
442 | 0 | aTempPolygonB.append(rCubicB.getStartPoint()); |
443 | 0 | rCubicB.adaptiveSubdivideByCount(aTempPolygonB, SUBDIVIDE_FOR_CUT_TEST_COUNT); |
444 | | |
445 | | // #i76891# using findCuts recursively is not sufficient here |
446 | 0 | findCutsAndTouchesAndCommonForBezier(aTempPolygonA, aTempPolygonB, aTempPointVectorA, aTempPointVectorB); |
447 | |
|
448 | 0 | if(!aTempPointVectorA.empty()) |
449 | 0 | { |
450 | | // adapt tempVector entries to segment |
451 | 0 | adaptAndTransferCutsWithBezierSegment(aTempPointVectorA, aTempPolygonA, nIndA, rTempPointsA); |
452 | 0 | } |
453 | |
|
454 | 0 | if(!aTempPointVectorB.empty()) |
455 | 0 | { |
456 | | // adapt tempVector entries to segment |
457 | 0 | adaptAndTransferCutsWithBezierSegment(aTempPointVectorB, aTempPolygonB, nIndB, rTempPointsB); |
458 | 0 | } |
459 | 0 | } |
460 | | |
461 | | void findEdgeCutsOneBezier( |
462 | | const B2DCubicBezier& rCubicA, |
463 | | sal_uInt32 nInd, temporaryPointVector& rTempPoints) |
464 | 0 | { |
465 | | // avoid expensive part of this method if possible |
466 | | // TODO: use hasAnyExtremum() method instead when it becomes available |
467 | 0 | double fDummy; |
468 | 0 | const bool bHasAnyExtremum = rCubicA.getMinimumExtremumPosition( fDummy ); |
469 | 0 | if( !bHasAnyExtremum ) |
470 | 0 | return; |
471 | | |
472 | | // find all self-intersections on the given bezier segment. Add an entry to the tempPoints |
473 | | // for each self intersection point with the cut value describing the relative position on given |
474 | | // bezier segment. |
475 | 0 | B2DPolygon aTempPolygon; |
476 | 0 | temporaryPointVector aTempPointVector; |
477 | | |
478 | | // create subdivided polygon and find cuts on it |
479 | | // Keep adaptiveSubdivideByCount due to needed quality |
480 | 0 | aTempPolygon.reserve(SUBDIVIDE_FOR_CUT_TEST_COUNT + 8); |
481 | 0 | aTempPolygon.append(rCubicA.getStartPoint()); |
482 | 0 | rCubicA.adaptiveSubdivideByCount(aTempPolygon, SUBDIVIDE_FOR_CUT_TEST_COUNT); |
483 | 0 | findCuts(aTempPolygon, aTempPointVector); |
484 | |
|
485 | 0 | if(!aTempPointVector.empty()) |
486 | 0 | { |
487 | | // adapt tempVector entries to segment |
488 | 0 | adaptAndTransferCutsWithBezierSegment(aTempPointVector, aTempPolygon, nInd, rTempPoints); |
489 | 0 | } |
490 | 0 | } |
491 | | |
492 | | void findCuts(const B2DPolygon& rCandidate, temporaryPointVector& rTempPoints, size_t* pPointLimit) |
493 | 477k | { |
494 | | // find out if there are edges with intersections (self-cuts). If yes, add |
495 | | // entries to rTempPoints accordingly |
496 | 477k | const sal_uInt32 nPointCount(rCandidate.count()); |
497 | | |
498 | 477k | if(!nPointCount) |
499 | 0 | return; |
500 | | |
501 | 477k | const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1); |
502 | | |
503 | 477k | if(!nEdgeCount) |
504 | 2 | return; |
505 | | |
506 | 477k | const bool bCurvesInvolved(rCandidate.areControlPointsUsed()); |
507 | | |
508 | 477k | if(bCurvesInvolved) |
509 | 0 | { |
510 | 0 | B2DCubicBezier aCubicA; |
511 | 0 | B2DCubicBezier aCubicB; |
512 | |
|
513 | 0 | for(sal_uInt32 a(0); a < nEdgeCount - 1; a++) |
514 | 0 | { |
515 | 0 | rCandidate.getBezierSegment(a, aCubicA); |
516 | 0 | aCubicA.testAndSolveTrivialBezier(); |
517 | 0 | const bool bEdgeAIsCurve(aCubicA.isBezier()); |
518 | 0 | const B2DRange aRangeA(aCubicA.getRange()); |
519 | |
|
520 | 0 | if(bEdgeAIsCurve) |
521 | 0 | { |
522 | | // curved segments may have self-intersections, do not forget those (!) |
523 | 0 | findEdgeCutsOneBezier(aCubicA, a, rTempPoints); |
524 | 0 | } |
525 | |
|
526 | 0 | for(sal_uInt32 b(a + 1); b < nEdgeCount; b++) |
527 | 0 | { |
528 | 0 | rCandidate.getBezierSegment(b, aCubicB); |
529 | 0 | aCubicB.testAndSolveTrivialBezier(); |
530 | 0 | const B2DRange aRangeB(aCubicB.getRange()); |
531 | | |
532 | | // only overlapping segments need to be tested |
533 | | // consecutive segments touch of course |
534 | 0 | bool bOverlap = false; |
535 | 0 | if( b > a+1) |
536 | 0 | bOverlap = aRangeA.overlaps(aRangeB); |
537 | 0 | else |
538 | 0 | bOverlap = aRangeA.overlapsMore(aRangeB); |
539 | 0 | if( bOverlap) |
540 | 0 | { |
541 | 0 | const bool bEdgeBIsCurve(aCubicB.isBezier()); |
542 | 0 | if(bEdgeAIsCurve && bEdgeBIsCurve) |
543 | 0 | { |
544 | | // test for bezier-bezier cuts |
545 | 0 | findEdgeCutsTwoBeziers(aCubicA, aCubicB, a, b, rTempPoints, rTempPoints); |
546 | 0 | } |
547 | 0 | else if(bEdgeAIsCurve) |
548 | 0 | { |
549 | | // test for bezier-edge cuts |
550 | 0 | findEdgeCutsBezierAndEdge(aCubicA, aCubicB.getStartPoint(), aCubicB.getEndPoint(), a, b, rTempPoints, rTempPoints); |
551 | 0 | } |
552 | 0 | else if(bEdgeBIsCurve) |
553 | 0 | { |
554 | | // test for bezier-edge cuts |
555 | 0 | findEdgeCutsBezierAndEdge(aCubicB, aCubicA.getStartPoint(), aCubicA.getEndPoint(), b, a, rTempPoints, rTempPoints); |
556 | 0 | } |
557 | 0 | else |
558 | 0 | { |
559 | | // test for simple edge-edge cuts |
560 | 0 | findEdgeCutsTwoEdges(aCubicA.getStartPoint(), aCubicA.getEndPoint(), aCubicB.getStartPoint(), aCubicB.getEndPoint(), |
561 | 0 | a, b, rTempPoints, rTempPoints); |
562 | 0 | } |
563 | 0 | } |
564 | 0 | } |
565 | 0 | } |
566 | 0 | } |
567 | 477k | else |
568 | 477k | { |
569 | 477k | B2DPoint aCurrA(rCandidate.getB2DPoint(0)); |
570 | | |
571 | 2.64M | for(sal_uInt32 a(0); a < nEdgeCount - 1; a++) |
572 | 2.16M | { |
573 | 2.16M | const B2DPoint aNextA(rCandidate.getB2DPoint(a + 1 == nPointCount ? 0 : a + 1)); |
574 | 2.16M | const B2DRange aRangeA(aCurrA, aNextA); |
575 | 2.16M | B2DPoint aCurrB(rCandidate.getB2DPoint(a + 1)); |
576 | | |
577 | 159M | for(sal_uInt32 b(a + 1); b < nEdgeCount; b++) |
578 | 158M | { |
579 | 158M | const B2DPoint aNextB(rCandidate.getB2DPoint(b + 1 == nPointCount ? 0 : b + 1)); |
580 | 158M | const B2DRange aRangeB(aCurrB, aNextB); |
581 | | |
582 | | // consecutive segments touch of course |
583 | 158M | bool bOverlap = false; |
584 | 158M | if( b > a+1) |
585 | 155M | bOverlap = aRangeA.overlaps(aRangeB); |
586 | 2.16M | else |
587 | 2.16M | bOverlap = aRangeA.overlapsMore(aRangeB); |
588 | 158M | if( bOverlap) |
589 | 11.2M | { |
590 | 11.2M | findEdgeCutsTwoEdges(aCurrA, aNextA, aCurrB, aNextB, a, b, rTempPoints, rTempPoints); |
591 | 11.2M | } |
592 | | |
593 | 158M | if (pPointLimit && rTempPoints.size() > *pPointLimit) |
594 | 214k | break; |
595 | | |
596 | | // prepare next step |
597 | 157M | aCurrB = aNextB; |
598 | 157M | } |
599 | | |
600 | | // prepare next step |
601 | 2.16M | aCurrA = aNextA; |
602 | 2.16M | } |
603 | 477k | } |
604 | | |
605 | 477k | if (pPointLimit) |
606 | 31.9k | { |
607 | 31.9k | if (rTempPoints.size() > *pPointLimit) |
608 | 334 | *pPointLimit = 0; |
609 | 31.5k | else |
610 | 31.5k | *pPointLimit -= rTempPoints.size(); |
611 | 31.9k | } |
612 | 477k | } |
613 | | |
614 | | } // end of anonymous namespace |
615 | | } // end of namespace basegfx |
616 | | |
617 | | namespace basegfx |
618 | | { |
619 | | namespace |
620 | | { |
621 | | |
622 | | void findTouchesOnEdge( |
623 | | const B2DPoint& rCurr, const B2DPoint& rNext, const B2DPolygon& rPointPolygon, |
624 | | sal_uInt32 nInd, temporaryPointVector& rTempPoints) |
625 | 30.3M | { |
626 | | // find out if points from rPointPolygon are positioned on given edge. If Yes, add |
627 | | // points there to represent touches (which may be enter or leave nodes later). |
628 | 30.3M | const sal_uInt32 nPointCount(rPointPolygon.count()); |
629 | | |
630 | 30.3M | if(!nPointCount) |
631 | 0 | return; |
632 | | |
633 | 30.3M | const B2DRange aRange(rCurr, rNext); |
634 | 30.3M | const B2DVector aEdgeVector(rNext - rCurr); |
635 | 30.3M | bool bTestUsingX(fabs(aEdgeVector.getX()) > fabs(aEdgeVector.getY())); |
636 | | |
637 | 1.35G | for(sal_uInt32 a(0); a < nPointCount; a++) |
638 | 1.32G | { |
639 | 1.32G | const B2DPoint aTestPoint(rPointPolygon.getB2DPoint(a)); |
640 | | |
641 | 1.32G | if(aRange.isInside(aTestPoint)) |
642 | 108M | { |
643 | 108M | if(!aTestPoint.equal(rCurr) && !aTestPoint.equal(rNext)) |
644 | 32.4M | { |
645 | 32.4M | const B2DVector aTestVector(aTestPoint - rCurr); |
646 | | |
647 | 32.4M | if(areParallel(aEdgeVector, aTestVector)) |
648 | 10.4M | { |
649 | 10.4M | const double fCut(bTestUsingX |
650 | 10.4M | ? aTestVector.getX() / aEdgeVector.getX() |
651 | 10.4M | : aTestVector.getY() / aEdgeVector.getY()); |
652 | 10.4M | const double fZero(0.0); |
653 | 10.4M | const double fOne(1.0); |
654 | | |
655 | 10.4M | if(fTools::more(fCut, fZero) && fTools::less(fCut, fOne)) |
656 | 10.4M | { |
657 | 10.4M | rTempPoints.emplace_back(aTestPoint, nInd, fCut); |
658 | 10.4M | } |
659 | 10.4M | } |
660 | 32.4M | } |
661 | 108M | } |
662 | 1.32G | } |
663 | 30.3M | } |
664 | | |
665 | | void findTouchesOnCurve( |
666 | | const B2DCubicBezier& rCubicA, const B2DPolygon& rPointPolygon, |
667 | | sal_uInt32 nInd, temporaryPointVector& rTempPoints) |
668 | 0 | { |
669 | | // find all points from rPointPolygon which touch the given bezier segment. Add an entry |
670 | | // for each touch to the given pointVector. The cut for that entry is the relative position on |
671 | | // the given bezier segment. |
672 | 0 | B2DPolygon aTempPolygon; |
673 | 0 | temporaryPointVector aTempPointVector; |
674 | | |
675 | | // create subdivided polygon and find cuts on it |
676 | | // Keep adaptiveSubdivideByCount due to needed quality |
677 | 0 | aTempPolygon.reserve(SUBDIVIDE_FOR_CUT_TEST_COUNT + 8); |
678 | 0 | aTempPolygon.append(rCubicA.getStartPoint()); |
679 | 0 | rCubicA.adaptiveSubdivideByCount(aTempPolygon, SUBDIVIDE_FOR_CUT_TEST_COUNT); |
680 | 0 | findTouches(aTempPolygon, rPointPolygon, aTempPointVector); |
681 | |
|
682 | 0 | if(!aTempPointVector.empty()) |
683 | 0 | { |
684 | | // adapt tempVector entries to segment |
685 | 0 | adaptAndTransferCutsWithBezierSegment(aTempPointVector, aTempPolygon, nInd, rTempPoints); |
686 | 0 | } |
687 | 0 | } |
688 | | |
689 | | void findTouches(const B2DPolygon& rEdgePolygon, const B2DPolygon& rPointPolygon, temporaryPointVector& rTempPoints) |
690 | 612k | { |
691 | | // find out if points from rPointPolygon touch edges from rEdgePolygon. If yes, |
692 | | // add entries to rTempPoints |
693 | 612k | const sal_uInt32 nPointCount(rPointPolygon.count()); |
694 | 612k | const sal_uInt32 nEdgePointCount(rEdgePolygon.count()); |
695 | | |
696 | 612k | if(!(nPointCount && nEdgePointCount)) |
697 | 0 | return; |
698 | | |
699 | 612k | const sal_uInt32 nEdgeCount(rEdgePolygon.isClosed() ? nEdgePointCount : nEdgePointCount - 1); |
700 | 612k | B2DPoint aCurr(rEdgePolygon.getB2DPoint(0)); |
701 | | |
702 | 31.2M | for(sal_uInt32 a(0); a < nEdgeCount; a++) |
703 | 30.5M | { |
704 | 30.5M | const sal_uInt32 nNextIndex((a + 1) % nEdgePointCount); |
705 | 30.5M | const B2DPoint aNext(rEdgePolygon.getB2DPoint(nNextIndex)); |
706 | | |
707 | 30.5M | if(!aCurr.equal(aNext)) |
708 | 30.3M | { |
709 | 30.3M | bool bHandleAsSimpleEdge(true); |
710 | | |
711 | 30.3M | if(rEdgePolygon.areControlPointsUsed()) |
712 | 0 | { |
713 | 0 | const B2DPoint aNextControlPoint(rEdgePolygon.getNextControlPoint(a)); |
714 | 0 | const B2DPoint aPrevControlPoint(rEdgePolygon.getPrevControlPoint(nNextIndex)); |
715 | 0 | const bool bEdgeIsCurve(!aNextControlPoint.equal(aCurr) || !aPrevControlPoint.equal(aNext)); |
716 | |
|
717 | 0 | if(bEdgeIsCurve) |
718 | 0 | { |
719 | 0 | bHandleAsSimpleEdge = false; |
720 | 0 | const B2DCubicBezier aCubicA(aCurr, aNextControlPoint, aPrevControlPoint, aNext); |
721 | 0 | findTouchesOnCurve(aCubicA, rPointPolygon, a, rTempPoints); |
722 | 0 | } |
723 | 0 | } |
724 | | |
725 | 30.3M | if(bHandleAsSimpleEdge) |
726 | 30.3M | { |
727 | 30.3M | findTouchesOnEdge(aCurr, aNext, rPointPolygon, a, rTempPoints); |
728 | 30.3M | } |
729 | 30.3M | } |
730 | | |
731 | | // next step |
732 | 30.5M | aCurr = aNext; |
733 | 30.5M | } |
734 | 612k | } |
735 | | |
736 | | } // end of anonymous namespace |
737 | | } // end of namespace basegfx |
738 | | |
739 | | namespace basegfx |
740 | | { |
741 | | namespace |
742 | | { |
743 | | |
744 | | void findCuts(const B2DPolygon& rCandidateA, const B2DPolygon& rCandidateB, temporaryPointVector& rTempPointsA, temporaryPointVector& rTempPointsB) |
745 | 67.5k | { |
746 | | // find out if edges from both polygons cut. If so, add entries to rTempPoints which |
747 | | // should be added to the polygons accordingly |
748 | 67.5k | const sal_uInt32 nPointCountA(rCandidateA.count()); |
749 | 67.5k | const sal_uInt32 nPointCountB(rCandidateB.count()); |
750 | | |
751 | 67.5k | if(!(nPointCountA && nPointCountB)) |
752 | 0 | return; |
753 | | |
754 | 67.5k | const sal_uInt32 nEdgeCountA(rCandidateA.isClosed() ? nPointCountA : nPointCountA - 1); |
755 | 67.5k | const sal_uInt32 nEdgeCountB(rCandidateB.isClosed() ? nPointCountB : nPointCountB - 1); |
756 | | |
757 | 67.5k | if(!(nEdgeCountA && nEdgeCountB)) |
758 | 2 | return; |
759 | | |
760 | 67.5k | const bool bCurvesInvolved(rCandidateA.areControlPointsUsed() || rCandidateB.areControlPointsUsed()); |
761 | | |
762 | 67.5k | if(bCurvesInvolved) |
763 | 0 | { |
764 | 0 | B2DCubicBezier aCubicA; |
765 | 0 | B2DCubicBezier aCubicB; |
766 | |
|
767 | 0 | for(sal_uInt32 a(0); a < nEdgeCountA; a++) |
768 | 0 | { |
769 | 0 | rCandidateA.getBezierSegment(a, aCubicA); |
770 | 0 | aCubicA.testAndSolveTrivialBezier(); |
771 | 0 | const bool bEdgeAIsCurve(aCubicA.isBezier()); |
772 | 0 | const B2DRange aRangeA(aCubicA.getRange()); |
773 | |
|
774 | 0 | for(sal_uInt32 b(0); b < nEdgeCountB; b++) |
775 | 0 | { |
776 | 0 | rCandidateB.getBezierSegment(b, aCubicB); |
777 | 0 | aCubicB.testAndSolveTrivialBezier(); |
778 | 0 | const B2DRange aRangeB(aCubicB.getRange()); |
779 | | |
780 | | // consecutive segments touch of course |
781 | 0 | bool bOverlap = false; |
782 | 0 | if( b > a+1) |
783 | 0 | bOverlap = aRangeA.overlaps(aRangeB); |
784 | 0 | else |
785 | 0 | bOverlap = aRangeA.overlapsMore(aRangeB); |
786 | 0 | if( bOverlap) |
787 | 0 | { |
788 | 0 | const bool bEdgeBIsCurve(aCubicB.isBezier()); |
789 | 0 | if(bEdgeAIsCurve && bEdgeBIsCurve) |
790 | 0 | { |
791 | | // test for bezier-bezier cuts |
792 | 0 | findEdgeCutsTwoBeziers(aCubicA, aCubicB, a, b, rTempPointsA, rTempPointsB); |
793 | 0 | } |
794 | 0 | else if(bEdgeAIsCurve) |
795 | 0 | { |
796 | | // test for bezier-edge cuts |
797 | 0 | findEdgeCutsBezierAndEdge(aCubicA, aCubicB.getStartPoint(), aCubicB.getEndPoint(), a, b, rTempPointsA, rTempPointsB); |
798 | 0 | } |
799 | 0 | else if(bEdgeBIsCurve) |
800 | 0 | { |
801 | | // test for bezier-edge cuts |
802 | 0 | findEdgeCutsBezierAndEdge(aCubicB, aCubicA.getStartPoint(), aCubicA.getEndPoint(), b, a, rTempPointsB, rTempPointsA); |
803 | 0 | } |
804 | 0 | else |
805 | 0 | { |
806 | | // test for simple edge-edge cuts |
807 | 0 | findEdgeCutsTwoEdges(aCubicA.getStartPoint(), aCubicA.getEndPoint(), aCubicB.getStartPoint(), aCubicB.getEndPoint(), |
808 | 0 | a, b, rTempPointsA, rTempPointsB); |
809 | 0 | } |
810 | 0 | } |
811 | 0 | } |
812 | 0 | } |
813 | 0 | } |
814 | 67.5k | else |
815 | 67.5k | { |
816 | 67.5k | B2DPoint aCurrA(rCandidateA.getB2DPoint(0)); |
817 | | |
818 | 26.3M | for(sal_uInt32 a(0); a < nEdgeCountA; a++) |
819 | 26.2M | { |
820 | 26.2M | const B2DPoint aNextA(rCandidateA.getB2DPoint(a + 1 == nPointCountA ? 0 : a + 1)); |
821 | 26.2M | const B2DRange aRangeA(aCurrA, aNextA); |
822 | 26.2M | B2DPoint aCurrB(rCandidateB.getB2DPoint(0)); |
823 | | |
824 | 357M | for(sal_uInt32 b(0); b < nEdgeCountB; b++) |
825 | 331M | { |
826 | 331M | const B2DPoint aNextB(rCandidateB.getB2DPoint(b + 1 == nPointCountB ? 0 : b + 1)); |
827 | 331M | const B2DRange aRangeB(aCurrB, aNextB); |
828 | | |
829 | | // consecutive segments touch of course |
830 | 331M | bool bOverlap = false; |
831 | 331M | if( b > a+1) |
832 | 66.9M | bOverlap = aRangeA.overlaps(aRangeB); |
833 | 264M | else |
834 | 264M | bOverlap = aRangeA.overlapsMore(aRangeB); |
835 | 331M | if( bOverlap) |
836 | 2.18M | { |
837 | | // test for simple edge-edge cuts |
838 | 2.18M | findEdgeCutsTwoEdges(aCurrA, aNextA, aCurrB, aNextB, a, b, rTempPointsA, rTempPointsB); |
839 | 2.18M | } |
840 | | |
841 | | // prepare next step |
842 | 331M | aCurrB = aNextB; |
843 | 331M | } |
844 | | |
845 | | // prepare next step |
846 | 26.2M | aCurrA = aNextA; |
847 | 26.2M | } |
848 | 67.5k | } |
849 | 67.5k | } |
850 | | |
851 | | } // end of anonymous namespace |
852 | | } // end of namespace basegfx |
853 | | |
854 | | namespace basegfx::utils |
855 | | { |
856 | | |
857 | | B2DPolygon addPointsAtCutsAndTouches(const B2DPolygon& rCandidate, size_t* pPointLimit) |
858 | 477k | { |
859 | 477k | if(rCandidate.count()) |
860 | 477k | { |
861 | 477k | temporaryPointVector aTempPoints; |
862 | | |
863 | 477k | findTouches(rCandidate, rCandidate, aTempPoints); |
864 | 477k | findCuts(rCandidate, aTempPoints, pPointLimit); |
865 | 477k | if (pPointLimit && !*pPointLimit) |
866 | 10.3k | { |
867 | 10.3k | SAL_WARN("basegfx", "addPointsAtCutsAndTouches hit point limit"); |
868 | 10.3k | return rCandidate; |
869 | 10.3k | } |
870 | | |
871 | 467k | return mergeTemporaryPointsAndPolygon(rCandidate, aTempPoints); |
872 | 477k | } |
873 | 0 | else |
874 | 0 | { |
875 | 0 | return rCandidate; |
876 | 0 | } |
877 | 477k | } |
878 | | |
879 | | B2DPolyPolygon addPointsAtCutsAndTouches(const B2DPolyPolygon& rCandidate, size_t* pPointLimit) |
880 | 446k | { |
881 | 446k | const sal_uInt32 nCount(rCandidate.count()); |
882 | | |
883 | 446k | if(nCount) |
884 | 446k | { |
885 | 446k | B2DPolyPolygon aRetval; |
886 | | |
887 | 446k | if(nCount == 1) |
888 | 445k | { |
889 | | // remove self intersections |
890 | 445k | aRetval.append(addPointsAtCutsAndTouches(rCandidate.getB2DPolygon(0), pPointLimit)); |
891 | 445k | } |
892 | 932 | else |
893 | 932 | { |
894 | | // first solve self cuts and self touches for all contained single polygons |
895 | 932 | std::unique_ptr<temporaryPolygonData[]> pTempData(new temporaryPolygonData[nCount]); |
896 | 932 | sal_uInt32 a, b; |
897 | | |
898 | 32.7k | for(a = 0; a < nCount; a++) |
899 | 31.8k | { |
900 | | // use polygons with solved self intersections |
901 | 31.8k | pTempData[a].setPolygon(addPointsAtCutsAndTouches(rCandidate.getB2DPolygon(a), pPointLimit)); |
902 | 31.8k | } |
903 | | |
904 | 932 | if (pPointLimit && !*pPointLimit) |
905 | 139 | { |
906 | 139 | SAL_WARN("basegfx", "addPointsAtCutsAndTouches hit point limit"); |
907 | 139 | return rCandidate; |
908 | 139 | } |
909 | | |
910 | | // now cuts and touches between the polygons |
911 | 21.9k | for(a = 0; a < nCount; a++) |
912 | 21.1k | { |
913 | 2.06M | for(b = 0; b < nCount; b++) |
914 | 2.04M | { |
915 | 2.04M | if(a != b) |
916 | 2.02M | { |
917 | | // look for touches, compare each edge polygon to all other points |
918 | 2.02M | if(pTempData[a].getRange().overlaps(pTempData[b].getRange())) |
919 | 135k | { |
920 | 135k | findTouches(pTempData[a].getPolygon(), pTempData[b].getPolygon(), pTempData[a].getTemporaryPointVector()); |
921 | 135k | } |
922 | 2.02M | } |
923 | | |
924 | 2.04M | if(a < b) |
925 | 1.01M | { |
926 | | // look for cuts, compare each edge polygon to following ones |
927 | 1.01M | if(pTempData[a].getRange().overlaps(pTempData[b].getRange())) |
928 | 67.5k | { |
929 | 67.5k | findCuts(pTempData[a].getPolygon(), pTempData[b].getPolygon(), pTempData[a].getTemporaryPointVector(), pTempData[b].getTemporaryPointVector()); |
930 | 67.5k | } |
931 | 1.01M | } |
932 | 2.04M | } |
933 | 21.1k | } |
934 | | |
935 | | // consolidate the result |
936 | 21.9k | for(a = 0; a < nCount; a++) |
937 | 21.1k | { |
938 | 21.1k | aRetval.append(mergeTemporaryPointsAndPolygon(pTempData[a].getPolygon(), pTempData[a].getTemporaryPointVector())); |
939 | 21.1k | } |
940 | 793 | } |
941 | | |
942 | 446k | return aRetval; |
943 | 446k | } |
944 | 0 | else |
945 | 0 | { |
946 | 0 | return rCandidate; |
947 | 0 | } |
948 | 446k | } |
949 | | |
950 | | B2DPolygon addPointsAtCuts(const B2DPolygon& rCandidate, const B2DPoint& rStart, const B2DPoint& rEnd) |
951 | 1.76k | { |
952 | 1.76k | const sal_uInt32 nCount(rCandidate.count()); |
953 | | |
954 | 1.76k | if(nCount && !rStart.equal(rEnd)) |
955 | 1.76k | { |
956 | 1.76k | const B2DRange aPolygonRange(rCandidate.getB2DRange()); |
957 | 1.76k | const B2DRange aEdgeRange(rStart, rEnd); |
958 | | |
959 | 1.76k | if(aPolygonRange.overlaps(aEdgeRange)) |
960 | 1.76k | { |
961 | 1.76k | const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nCount : nCount - 1); |
962 | 1.76k | temporaryPointVector aTempPoints; |
963 | 1.76k | temporaryPointVector aUnusedTempPoints; |
964 | 1.76k | B2DCubicBezier aCubic; |
965 | | |
966 | 153k | for(sal_uInt32 a(0); a < nEdgeCount; a++) |
967 | 151k | { |
968 | 151k | rCandidate.getBezierSegment(a, aCubic); |
969 | 151k | B2DRange aCubicRange(aCubic.getStartPoint(), aCubic.getEndPoint()); |
970 | | |
971 | 151k | if(aCubic.isBezier()) |
972 | 0 | { |
973 | 0 | aCubicRange.expand(aCubic.getControlPointA()); |
974 | 0 | aCubicRange.expand(aCubic.getControlPointB()); |
975 | |
|
976 | 0 | if(aCubicRange.overlaps(aEdgeRange)) |
977 | 0 | { |
978 | 0 | findEdgeCutsBezierAndEdge(aCubic, rStart, rEnd, a, 0, aTempPoints, aUnusedTempPoints); |
979 | 0 | } |
980 | 0 | } |
981 | 151k | else |
982 | 151k | { |
983 | 151k | if(aCubicRange.overlaps(aEdgeRange)) |
984 | 42.8k | { |
985 | 42.8k | findEdgeCutsTwoEdges(aCubic.getStartPoint(), aCubic.getEndPoint(), rStart, rEnd, a, 0, aTempPoints, aUnusedTempPoints); |
986 | 42.8k | } |
987 | 151k | } |
988 | 151k | } |
989 | | |
990 | 1.76k | return mergeTemporaryPointsAndPolygon(rCandidate, aTempPoints); |
991 | 1.76k | } |
992 | 1.76k | } |
993 | | |
994 | 0 | return rCandidate; |
995 | 1.76k | } |
996 | | |
997 | | B2DPolygon addPointsAtCuts(const B2DPolygon& rCandidate, const B2DPolyPolygon& rPolyMask) |
998 | 0 | { |
999 | 0 | const sal_uInt32 nCountA(rCandidate.count()); |
1000 | 0 | const sal_uInt32 nCountM(rPolyMask.count()); |
1001 | |
|
1002 | 0 | if(nCountA && nCountM) |
1003 | 0 | { |
1004 | 0 | const B2DRange aRangeA(rCandidate.getB2DRange()); |
1005 | 0 | const B2DRange aRangeM(rPolyMask.getB2DRange()); |
1006 | |
|
1007 | 0 | if(aRangeA.overlaps(aRangeM)) |
1008 | 0 | { |
1009 | 0 | const sal_uInt32 nEdgeCountA(rCandidate.isClosed() ? nCountA : nCountA - 1); |
1010 | 0 | temporaryPointVector aTempPointsA; |
1011 | 0 | temporaryPointVector aUnusedTempPointsB; |
1012 | |
|
1013 | 0 | for(sal_uInt32 m(0); m < nCountM; m++) |
1014 | 0 | { |
1015 | 0 | const B2DPolygon& aMask(rPolyMask.getB2DPolygon(m)); |
1016 | 0 | const sal_uInt32 nCountB(aMask.count()); |
1017 | |
|
1018 | 0 | if(nCountB) |
1019 | 0 | { |
1020 | 0 | B2DCubicBezier aCubicA; |
1021 | 0 | B2DCubicBezier aCubicB; |
1022 | |
|
1023 | 0 | for(sal_uInt32 a(0); a < nEdgeCountA; a++) |
1024 | 0 | { |
1025 | 0 | rCandidate.getBezierSegment(a, aCubicA); |
1026 | 0 | const bool bCubicAIsCurve(aCubicA.isBezier()); |
1027 | 0 | B2DRange aCubicRangeA(aCubicA.getStartPoint(), aCubicA.getEndPoint()); |
1028 | |
|
1029 | 0 | if(bCubicAIsCurve) |
1030 | 0 | { |
1031 | 0 | aCubicRangeA.expand(aCubicA.getControlPointA()); |
1032 | 0 | aCubicRangeA.expand(aCubicA.getControlPointB()); |
1033 | 0 | } |
1034 | |
|
1035 | 0 | for(sal_uInt32 b(0); b < nCountB; b++) |
1036 | 0 | { |
1037 | 0 | aMask.getBezierSegment(b, aCubicB); |
1038 | 0 | const bool bCubicBIsCurve(aCubicB.isBezier()); |
1039 | 0 | B2DRange aCubicRangeB(aCubicB.getStartPoint(), aCubicB.getEndPoint()); |
1040 | |
|
1041 | 0 | if(bCubicBIsCurve) |
1042 | 0 | { |
1043 | 0 | aCubicRangeB.expand(aCubicB.getControlPointA()); |
1044 | 0 | aCubicRangeB.expand(aCubicB.getControlPointB()); |
1045 | 0 | } |
1046 | |
|
1047 | 0 | if(aCubicRangeA.overlaps(aCubicRangeB)) |
1048 | 0 | { |
1049 | 0 | if(bCubicAIsCurve && bCubicBIsCurve) |
1050 | 0 | { |
1051 | 0 | findEdgeCutsTwoBeziers(aCubicA, aCubicB, a, b, aTempPointsA, aUnusedTempPointsB); |
1052 | 0 | } |
1053 | 0 | else if(bCubicAIsCurve) |
1054 | 0 | { |
1055 | 0 | findEdgeCutsBezierAndEdge(aCubicA, aCubicB.getStartPoint(), aCubicB.getEndPoint(), a, b, aTempPointsA, aUnusedTempPointsB); |
1056 | 0 | } |
1057 | 0 | else if(bCubicBIsCurve) |
1058 | 0 | { |
1059 | 0 | findEdgeCutsBezierAndEdge(aCubicB, aCubicA.getStartPoint(), aCubicA.getEndPoint(), b, a, aUnusedTempPointsB, aTempPointsA); |
1060 | 0 | } |
1061 | 0 | else |
1062 | 0 | { |
1063 | 0 | findEdgeCutsTwoEdges(aCubicA.getStartPoint(), aCubicA.getEndPoint(), aCubicB.getStartPoint(), aCubicB.getEndPoint(), a, b, aTempPointsA, aUnusedTempPointsB); |
1064 | 0 | } |
1065 | 0 | } |
1066 | 0 | } |
1067 | 0 | } |
1068 | 0 | } |
1069 | 0 | } |
1070 | |
|
1071 | 0 | return mergeTemporaryPointsAndPolygon(rCandidate, aTempPointsA); |
1072 | 0 | } |
1073 | 0 | } |
1074 | | |
1075 | 0 | return rCandidate; |
1076 | 0 | } |
1077 | | |
1078 | | } // end of namespace |
1079 | | |
1080 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |