Branch data Line data Source code
1 : : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : : /*************************************************************************
3 : : *
4 : : * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 : : *
6 : : * Copyright 2000, 2010 Oracle and/or its affiliates.
7 : : *
8 : : * OpenOffice.org - a multi-platform office productivity suite
9 : : *
10 : : * This file is part of OpenOffice.org.
11 : : *
12 : : * OpenOffice.org is free software: you can redistribute it and/or modify
13 : : * it under the terms of the GNU Lesser General Public License version 3
14 : : * only, as published by the Free Software Foundation.
15 : : *
16 : : * OpenOffice.org is distributed in the hope that it will be useful,
17 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 : : * GNU Lesser General Public License version 3 for more details
20 : : * (a copy is included in the LICENSE file that accompanied this code).
21 : : *
22 : : * You should have received a copy of the GNU Lesser General Public License
23 : : * version 3 along with OpenOffice.org. If not, see
24 : : * <http://www.openoffice.org/license.html>
25 : : * for a copy of the LGPLv3 License.
26 : : *
27 : : ************************************************************************/
28 : :
29 : : #include <cstdio>
30 : : #include <osl/diagnose.h>
31 : : #include <basegfx/polygon/b2dlinegeometry.hxx>
32 : : #include <basegfx/point/b2dpoint.hxx>
33 : : #include <basegfx/vector/b2dvector.hxx>
34 : : #include <basegfx/polygon/b2dpolygontools.hxx>
35 : : #include <basegfx/polygon/b2dpolypolygontools.hxx>
36 : : #include <basegfx/range/b2drange.hxx>
37 : : #include <basegfx/matrix/b2dhommatrix.hxx>
38 : : #include <basegfx/curve/b2dcubicbezier.hxx>
39 : : #include <basegfx/matrix/b2dhommatrixtools.hxx>
40 : :
41 : : //////////////////////////////////////////////////////////////////////////////
42 : :
43 : : namespace basegfx
44 : : {
45 : : namespace tools
46 : : {
47 : 15 : B2DPolyPolygon createAreaGeometryForLineStartEnd(
48 : : const B2DPolygon& rCandidate,
49 : : const B2DPolyPolygon& rArrow,
50 : : bool bStart,
51 : : double fWidth,
52 : : double fCandidateLength,
53 : : double fDockingPosition, // 0->top, 1->bottom
54 : : double* pConsumedLength)
55 : : {
56 : 15 : B2DPolyPolygon aRetval;
57 : : OSL_ENSURE(rCandidate.count() > 1L, "createAreaGeometryForLineStartEnd: Line polygon has too less points (!)");
58 : : OSL_ENSURE(rArrow.count() > 0L, "createAreaGeometryForLineStartEnd: Empty arrow PolyPolygon (!)");
59 : : OSL_ENSURE(fWidth > 0.0, "createAreaGeometryForLineStartEnd: Width too small (!)");
60 : : OSL_ENSURE(fDockingPosition >= 0.0 && fDockingPosition <= 1.0,
61 : : "createAreaGeometryForLineStartEnd: fDockingPosition out of range [0.0 .. 1.0] (!)");
62 : :
63 [ - + ]: 15 : if(fWidth < 0.0)
64 : : {
65 : 0 : fWidth = -fWidth;
66 : : }
67 : :
68 [ + - ][ + - ]: 15 : if(rCandidate.count() > 1 && rArrow.count() && !fTools::equalZero(fWidth))
[ + - ][ + - ]
[ + - ][ + - ]
69 : : {
70 [ - + ]: 15 : if(fDockingPosition < 0.0)
71 : : {
72 : 0 : fDockingPosition = 0.0;
73 : : }
74 [ - + ]: 15 : else if(fDockingPosition > 1.0)
75 : : {
76 : 0 : fDockingPosition = 1.0;
77 : : }
78 : :
79 : : // init return value from arrow
80 [ + - ]: 15 : aRetval.append(rArrow);
81 : :
82 : : // get size of the arrow
83 [ + - ]: 15 : const B2DRange aArrowSize(getRange(rArrow));
84 : :
85 : : // build ArrowTransform; center in X, align with axis in Y
86 : : B2DHomMatrix aArrowTransform(basegfx::tools::createTranslateB2DHomMatrix(
87 [ + - ][ + - ]: 15 : -aArrowSize.getCenter().getX(), -aArrowSize.getMinimum().getY()));
[ + - ]
88 : :
89 : : // scale to target size
90 [ + - ]: 15 : const double fArrowScale(fWidth / (aArrowSize.getRange().getX()));
91 [ + - ]: 15 : aArrowTransform.scale(fArrowScale, fArrowScale);
92 : :
93 : : // get arrow size in Y
94 [ + - ][ + - ]: 15 : B2DPoint aUpperCenter(aArrowSize.getCenter().getX(), aArrowSize.getMaximum().getY());
95 [ + - ]: 15 : aUpperCenter *= aArrowTransform;
96 [ + - ]: 15 : const double fArrowYLength(B2DVector(aUpperCenter).getLength());
97 : :
98 : : // move arrow to have docking position centered
99 [ + - ]: 15 : aArrowTransform.translate(0.0, -fArrowYLength * fDockingPosition);
100 : :
101 : : // prepare polygon length
102 [ - + ]: 15 : if(fTools::equalZero(fCandidateLength))
103 : : {
104 [ # # ]: 0 : fCandidateLength = getLength(rCandidate);
105 : : }
106 : :
107 : : // get the polygon vector we want to plant this arrow on
108 : 15 : const double fConsumedLength(fArrowYLength * (1.0 - fDockingPosition));
109 [ + + ][ + - ]: 15 : const B2DVector aHead(rCandidate.getB2DPoint((bStart) ? 0L : rCandidate.count() - 1L));
[ + - ]
110 : : const B2DVector aTail(getPositionAbsolute(rCandidate,
111 [ + - ][ + + ]: 15 : (bStart) ? fConsumedLength : fCandidateLength - fConsumedLength, fCandidateLength));
112 : :
113 : : // from that vector, take the needed rotation and add rotate for arrow to transformation
114 : 15 : const B2DVector aTargetDirection(aHead - aTail);
115 : 15 : const double fRotation(atan2(aTargetDirection.getY(), aTargetDirection.getX()) + (90.0 * F_PI180));
116 : :
117 : : // rotate around docking position
118 [ + - ]: 15 : aArrowTransform.rotate(fRotation);
119 : :
120 : : // move arrow docking position to polygon head
121 [ + - ]: 15 : aArrowTransform.translate(aHead.getX(), aHead.getY());
122 : :
123 : : // transform retval and close
124 [ + - ]: 15 : aRetval.transform(aArrowTransform);
125 [ + - ]: 15 : aRetval.setClosed(true);
126 : :
127 : : // if pConsumedLength is asked for, fill it
128 [ + + ]: 15 : if(pConsumedLength)
129 : : {
130 : 10 : *pConsumedLength = fConsumedLength;
131 [ + - ]: 15 : }
132 : : }
133 : :
134 : 15 : return aRetval;
135 : : }
136 : : } // end of namespace tools
137 : : } // end of namespace basegfx
138 : :
139 : : //////////////////////////////////////////////////////////////////////////////
140 : :
141 : : namespace basegfx
142 : : {
143 : : // anonymus namespace for local helpers
144 : : namespace
145 : : {
146 : 0 : bool impIsSimpleEdge(const B2DCubicBezier& rCandidate, double fMaxCosQuad, double fMaxPartOfEdgeQuad)
147 : : {
148 : : // isBezier() is true, already tested by caller
149 : 0 : const B2DVector aEdge(rCandidate.getEndPoint() - rCandidate.getStartPoint());
150 : :
151 [ # # ][ # # ]: 0 : if(aEdge.equalZero())
152 : : {
153 : : // start and end point the same, but control vectors used -> baloon curve loop
154 : : // is not a simple edge
155 : 0 : return false;
156 : : }
157 : :
158 : : // get tangentA and scalar with edge
159 [ # # ]: 0 : const B2DVector aTangentA(rCandidate.getTangent(0.0));
160 [ # # ]: 0 : const double fScalarAE(aEdge.scalar(aTangentA));
161 : :
162 [ # # ]: 0 : if(fTools::lessOrEqual(fScalarAE, 0.0))
163 : : {
164 : : // angle between TangentA and Edge is bigger or equal 90 degrees
165 : 0 : return false;
166 : : }
167 : :
168 : : // get self-scalars for E and A
169 [ # # ]: 0 : const double fScalarE(aEdge.scalar(aEdge));
170 [ # # ]: 0 : const double fScalarA(aTangentA.scalar(aTangentA));
171 : 0 : const double fLengthCompareE(fScalarE * fMaxPartOfEdgeQuad);
172 : :
173 [ # # ]: 0 : if(fTools::moreOrEqual(fScalarA, fLengthCompareE))
174 : : {
175 : : // length of TangentA is more than fMaxPartOfEdge of length of edge
176 : 0 : return false;
177 : : }
178 : :
179 [ # # ]: 0 : if(fTools::lessOrEqual(fScalarAE * fScalarAE, fScalarA * fScalarE * fMaxCosQuad))
180 : : {
181 : : // angle between TangentA and Edge is bigger or equal angle defined by fMaxCos
182 : 0 : return false;
183 : : }
184 : :
185 : : // get tangentB and scalar with edge
186 [ # # ]: 0 : const B2DVector aTangentB(rCandidate.getTangent(1.0));
187 [ # # ]: 0 : const double fScalarBE(aEdge.scalar(aTangentB));
188 : :
189 [ # # ]: 0 : if(fTools::lessOrEqual(fScalarBE, 0.0))
190 : : {
191 : : // angle between TangentB and Edge is bigger or equal 90 degrees
192 : 0 : return false;
193 : : }
194 : :
195 : : // get self-scalar for B
196 [ # # ]: 0 : const double fScalarB(aTangentB.scalar(aTangentB));
197 : :
198 [ # # ]: 0 : if(fTools::moreOrEqual(fScalarB, fLengthCompareE))
199 : : {
200 : : // length of TangentB is more than fMaxPartOfEdge of length of edge
201 : 0 : return false;
202 : : }
203 : :
204 [ # # ]: 0 : if(fTools::lessOrEqual(fScalarBE * fScalarBE, fScalarB * fScalarE * fMaxCosQuad))
205 : : {
206 : : // angle between TangentB and Edge is bigger or equal defined by fMaxCos
207 : 0 : return false;
208 : : }
209 : :
210 : 0 : return true;
211 : : }
212 : :
213 : 0 : void impSubdivideToSimple(const B2DCubicBezier& rCandidate, B2DPolygon& rTarget, double fMaxCosQuad, double fMaxPartOfEdgeQuad, sal_uInt32 nMaxRecursionDepth)
214 : : {
215 [ # # ][ # # ]: 0 : if(!nMaxRecursionDepth || impIsSimpleEdge(rCandidate, fMaxCosQuad, fMaxPartOfEdgeQuad))
[ # # ]
216 : : {
217 [ # # ]: 0 : rTarget.appendBezierSegment(rCandidate.getControlPointA(), rCandidate.getControlPointB(), rCandidate.getEndPoint());
218 : : }
219 : : else
220 : : {
221 [ # # ][ # # ]: 0 : B2DCubicBezier aLeft, aRight;
222 [ # # ]: 0 : rCandidate.split(0.5, &aLeft, &aRight);
223 : :
224 [ # # ]: 0 : impSubdivideToSimple(aLeft, rTarget, fMaxCosQuad, fMaxPartOfEdgeQuad, nMaxRecursionDepth - 1);
225 [ # # ][ # # ]: 0 : impSubdivideToSimple(aRight, rTarget, fMaxCosQuad, fMaxPartOfEdgeQuad, nMaxRecursionDepth - 1);
[ # # ]
226 : : }
227 : 0 : }
228 : :
229 : 9861 : B2DPolygon subdivideToSimple(const B2DPolygon& rCandidate, double fMaxCosQuad, double fMaxPartOfEdgeQuad)
230 : : {
231 : 9861 : const sal_uInt32 nPointCount(rCandidate.count());
232 : :
233 [ # # ][ - + ]: 9861 : if(rCandidate.areControlPointsUsed() && nPointCount)
[ - + ]
234 : : {
235 [ # # ][ # # ]: 0 : const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
236 [ # # ]: 0 : B2DPolygon aRetval;
237 [ # # ]: 0 : B2DCubicBezier aEdge;
238 : :
239 : : // prepare edge for loop
240 [ # # ]: 0 : aEdge.setStartPoint(rCandidate.getB2DPoint(0));
241 [ # # ]: 0 : aRetval.append(aEdge.getStartPoint());
242 : :
243 [ # # ]: 0 : for(sal_uInt32 a(0); a < nEdgeCount; a++)
244 : : {
245 : : // fill B2DCubicBezier
246 : 0 : const sal_uInt32 nNextIndex((a + 1) % nPointCount);
247 [ # # ]: 0 : aEdge.setControlPointA(rCandidate.getNextControlPoint(a));
248 [ # # ]: 0 : aEdge.setControlPointB(rCandidate.getPrevControlPoint(nNextIndex));
249 [ # # ]: 0 : aEdge.setEndPoint(rCandidate.getB2DPoint(nNextIndex));
250 : :
251 : : // get rid of unnecessary bezier segments
252 [ # # ]: 0 : aEdge.testAndSolveTrivialBezier();
253 : :
254 [ # # ][ # # ]: 0 : if(aEdge.isBezier())
255 : : {
256 : : // before splitting recursively with internal simple criteria, use
257 : : // ExtremumPosFinder to remove those
258 [ # # ]: 0 : ::std::vector< double > aExtremumPositions;
259 : :
260 [ # # ]: 0 : aExtremumPositions.reserve(4);
261 [ # # ]: 0 : aEdge.getAllExtremumPositions(aExtremumPositions);
262 : :
263 : 0 : const sal_uInt32 nCount(aExtremumPositions.size());
264 : :
265 [ # # ]: 0 : if(nCount)
266 : : {
267 [ # # ]: 0 : if(nCount > 1)
268 : : {
269 : : // create order from left to right
270 [ # # ]: 0 : ::std::sort(aExtremumPositions.begin(), aExtremumPositions.end());
271 : : }
272 : :
273 [ # # ]: 0 : for(sal_uInt32 b(0); b < nCount;)
274 : : {
275 : : // split aEdge at next split pos
276 [ # # ]: 0 : B2DCubicBezier aLeft;
277 [ # # ]: 0 : const double fSplitPos(aExtremumPositions[b++]);
278 : :
279 [ # # ]: 0 : aEdge.split(fSplitPos, &aLeft, &aEdge);
280 [ # # ]: 0 : aLeft.testAndSolveTrivialBezier();
281 : :
282 : : // consume left part
283 [ # # ][ # # ]: 0 : if(aLeft.isBezier())
284 : : {
285 [ # # ]: 0 : impSubdivideToSimple(aLeft, aRetval, fMaxCosQuad, fMaxPartOfEdgeQuad, 6);
286 : : }
287 : : else
288 : : {
289 [ # # ]: 0 : aRetval.append(aLeft.getEndPoint());
290 : : }
291 : :
292 [ # # ]: 0 : if(b < nCount)
293 : : {
294 : : // correct the remaining split positions to fit to shortened aEdge
295 : 0 : const double fScaleFactor(1.0 / (1.0 - fSplitPos));
296 : :
297 [ # # ]: 0 : for(sal_uInt32 c(b); c < nCount; c++)
298 : : {
299 [ # # ][ # # ]: 0 : aExtremumPositions[c] = (aExtremumPositions[c] - fSplitPos) * fScaleFactor;
300 : : }
301 : : }
302 [ # # ]: 0 : }
303 : :
304 : : // test the shortened rest of aEdge
305 [ # # ]: 0 : aEdge.testAndSolveTrivialBezier();
306 : :
307 : : // consume right part
308 [ # # ][ # # ]: 0 : if(aEdge.isBezier())
309 : : {
310 [ # # ]: 0 : impSubdivideToSimple(aEdge, aRetval, fMaxCosQuad, fMaxPartOfEdgeQuad, 6);
311 : : }
312 : : else
313 : : {
314 [ # # ]: 0 : aRetval.append(aEdge.getEndPoint());
315 : : }
316 : : }
317 : : else
318 : : {
319 [ # # ]: 0 : impSubdivideToSimple(aEdge, aRetval, fMaxCosQuad, fMaxPartOfEdgeQuad, 6);
320 : 0 : }
321 : : }
322 : : else
323 : : {
324 : : // straight edge, add point
325 [ # # ]: 0 : aRetval.append(aEdge.getEndPoint());
326 : : }
327 : :
328 : : // prepare edge for next step
329 : 0 : aEdge.setStartPoint(aEdge.getEndPoint());
330 : : }
331 : :
332 : : // copy closed flag and check for double points
333 [ # # ][ # # ]: 0 : aRetval.setClosed(rCandidate.isClosed());
334 [ # # ]: 0 : aRetval.removeDoublePoints();
335 : :
336 [ # # ][ # # ]: 0 : return aRetval;
[ # # ]
337 : : }
338 : : else
339 : : {
340 : 9861 : return rCandidate;
341 : : }
342 : : }
343 : :
344 : 10729 : B2DPolygon createAreaGeometryForEdge(const B2DCubicBezier& rEdge, double fHalfLineWidth)
345 : : {
346 : : // create polygon for edge
347 : : // Unfortunately, while it would be geometrically correct to not add
348 : : // the in-between points EdgeEnd and EdgeStart, it leads to rounding
349 : : // errors when converting to integer polygon coordinates for painting
350 [ - + ]: 10729 : if(rEdge.isBezier())
351 : : {
352 : : // prepare target and data common for upper and lower
353 [ # # ]: 0 : B2DPolygon aBezierPolygon;
354 : 0 : const B2DVector aPureEdgeVector(rEdge.getEndPoint() - rEdge.getStartPoint());
355 [ # # ]: 0 : const double fEdgeLength(aPureEdgeVector.getLength());
356 : 0 : const bool bIsEdgeLengthZero(fTools::equalZero(fEdgeLength));
357 [ # # ]: 0 : const B2DVector aTangentA(rEdge.getTangent(0.0));
358 [ # # ]: 0 : const B2DVector aTangentB(rEdge.getTangent(1.0));
359 : :
360 : : // create upper edge.
361 : : {
362 : : // create displacement vectors and check if they cut
363 [ # # ]: 0 : const B2DVector aPerpendStart(getNormalizedPerpendicular(aTangentA) * -fHalfLineWidth);
364 [ # # ]: 0 : const B2DVector aPerpendEnd(getNormalizedPerpendicular(aTangentB) * -fHalfLineWidth);
365 : 0 : double fCut(0.0);
366 : : const tools::CutFlagValue aCut(tools::findCut(
367 : : rEdge.getStartPoint(), aPerpendStart,
368 : : rEdge.getEndPoint(), aPerpendEnd,
369 [ # # ]: 0 : CUTFLAG_ALL, &fCut));
370 : :
371 [ # # ]: 0 : if(CUTFLAG_NONE != aCut)
372 : : {
373 : : // calculate cut point and add
374 : 0 : const B2DPoint aCutPoint(rEdge.getStartPoint() + (aPerpendStart * fCut));
375 [ # # ]: 0 : aBezierPolygon.append(aCutPoint);
376 : : }
377 : : else
378 : : {
379 : : // create scaled bezier segment
380 : 0 : const B2DPoint aStart(rEdge.getStartPoint() + aPerpendStart);
381 : 0 : const B2DPoint aEnd(rEdge.getEndPoint() + aPerpendEnd);
382 : 0 : const B2DVector aEdge(aEnd - aStart);
383 [ # # ]: 0 : const double fLength(aEdge.getLength());
384 [ # # ]: 0 : const double fScale(bIsEdgeLengthZero ? 1.0 : fLength / fEdgeLength);
385 : 0 : const B2DVector fRelNext(rEdge.getControlPointA() - rEdge.getStartPoint());
386 : 0 : const B2DVector fRelPrev(rEdge.getControlPointB() - rEdge.getEndPoint());
387 : :
388 [ # # ]: 0 : aBezierPolygon.append(aStart);
389 [ # # ]: 0 : aBezierPolygon.appendBezierSegment(aStart + (fRelNext * fScale), aEnd + (fRelPrev * fScale), aEnd);
390 : 0 : }
391 : : }
392 : :
393 : : // append original in-between point
394 [ # # ]: 0 : aBezierPolygon.append(rEdge.getEndPoint());
395 : :
396 : : // create lower edge.
397 : : {
398 : : // create displacement vectors and check if they cut
399 [ # # ]: 0 : const B2DVector aPerpendStart(getNormalizedPerpendicular(aTangentA) * fHalfLineWidth);
400 [ # # ]: 0 : const B2DVector aPerpendEnd(getNormalizedPerpendicular(aTangentB) * fHalfLineWidth);
401 : 0 : double fCut(0.0);
402 : : const tools::CutFlagValue aCut(tools::findCut(
403 : : rEdge.getEndPoint(), aPerpendEnd,
404 : : rEdge.getStartPoint(), aPerpendStart,
405 [ # # ]: 0 : CUTFLAG_ALL, &fCut));
406 : :
407 [ # # ]: 0 : if(CUTFLAG_NONE != aCut)
408 : : {
409 : : // calculate cut point and add
410 : 0 : const B2DPoint aCutPoint(rEdge.getEndPoint() + (aPerpendEnd * fCut));
411 [ # # ]: 0 : aBezierPolygon.append(aCutPoint);
412 : : }
413 : : else
414 : : {
415 : : // create scaled bezier segment
416 : 0 : const B2DPoint aStart(rEdge.getEndPoint() + aPerpendEnd);
417 : 0 : const B2DPoint aEnd(rEdge.getStartPoint() + aPerpendStart);
418 : 0 : const B2DVector aEdge(aEnd - aStart);
419 [ # # ]: 0 : const double fLength(aEdge.getLength());
420 [ # # ]: 0 : const double fScale(bIsEdgeLengthZero ? 1.0 : fLength / fEdgeLength);
421 : 0 : const B2DVector fRelNext(rEdge.getControlPointB() - rEdge.getEndPoint());
422 : 0 : const B2DVector fRelPrev(rEdge.getControlPointA() - rEdge.getStartPoint());
423 : :
424 [ # # ]: 0 : aBezierPolygon.append(aStart);
425 [ # # ]: 0 : aBezierPolygon.appendBezierSegment(aStart + (fRelNext * fScale), aEnd + (fRelPrev * fScale), aEnd);
426 : 0 : }
427 : : }
428 : :
429 : : // append original in-between point
430 [ # # ]: 0 : aBezierPolygon.append(rEdge.getStartPoint());
431 : :
432 : : // close and return
433 [ # # ]: 0 : aBezierPolygon.setClosed(true);
434 [ # # ][ # # ]: 0 : return aBezierPolygon;
435 : : }
436 : : else
437 : : {
438 : : // #i101491# emulate rEdge.getTangent call which applies a factor of 0.3 to the
439 : : // full-length edge vector to have numerically exactly the same results as in the
440 : : // createAreaGeometryForJoin implementation
441 : 10729 : const B2DVector aEdgeTangent((rEdge.getEndPoint() - rEdge.getStartPoint()) * 0.3);
442 [ + - ]: 10729 : const B2DVector aPerpendEdgeVector(getNormalizedPerpendicular(aEdgeTangent) * fHalfLineWidth);
443 [ + - ]: 10729 : B2DPolygon aEdgePolygon;
444 : :
445 : : // create upper edge
446 [ + - ]: 10729 : aEdgePolygon.append(rEdge.getStartPoint() - aPerpendEdgeVector);
447 [ + - ]: 10729 : aEdgePolygon.append(rEdge.getEndPoint() - aPerpendEdgeVector);
448 : :
449 : : // append original in-between point
450 [ + - ]: 10729 : aEdgePolygon.append(rEdge.getEndPoint());
451 : :
452 : : // create lower edge
453 [ + - ]: 10729 : aEdgePolygon.append(rEdge.getEndPoint() + aPerpendEdgeVector);
454 [ + - ]: 10729 : aEdgePolygon.append(rEdge.getStartPoint() + aPerpendEdgeVector);
455 : :
456 : : // append original in-between point
457 [ + - ]: 10729 : aEdgePolygon.append(rEdge.getStartPoint());
458 : :
459 : : // close and return
460 [ + - ]: 10729 : aEdgePolygon.setClosed(true);
461 [ + - ][ + - ]: 10729 : return aEdgePolygon;
462 : : }
463 : : }
464 : :
465 : 868 : B2DPolygon createAreaGeometryForJoin(
466 : : const B2DVector& rTangentPrev,
467 : : const B2DVector& rTangentEdge,
468 : : const B2DVector& rPerpendPrev,
469 : : const B2DVector& rPerpendEdge,
470 : : const B2DPoint& rPoint,
471 : : double fHalfLineWidth,
472 : : B2DLineJoin eJoin,
473 : : double fMiterMinimumAngle)
474 : : {
475 : : OSL_ENSURE(fHalfLineWidth > 0.0, "createAreaGeometryForJoin: LineWidth too small (!)");
476 : : OSL_ENSURE(B2DLINEJOIN_NONE != eJoin, "createAreaGeometryForJoin: B2DLINEJOIN_NONE not allowed (!)");
477 : :
478 : : // LineJoin from tangent rPerpendPrev to tangent rPerpendEdge in rPoint
479 [ + - ]: 868 : B2DPolygon aEdgePolygon;
480 : 868 : const B2DPoint aStartPoint(rPoint + rPerpendPrev);
481 : 868 : const B2DPoint aEndPoint(rPoint + rPerpendEdge);
482 : :
483 : : // test if for Miter, the angle is too small and the fallback
484 : : // to bevel needs to be used
485 [ - + ]: 868 : if(B2DLINEJOIN_MITER == eJoin)
486 : : {
487 [ # # ]: 0 : const double fAngle(fabs(rPerpendPrev.angle(rPerpendEdge)));
488 : :
489 [ # # ]: 0 : if((F_PI - fAngle) < fMiterMinimumAngle)
490 : : {
491 : : // fallback to bevel
492 : 0 : eJoin = B2DLINEJOIN_BEVEL;
493 : : }
494 : : }
495 : :
496 [ - + - ]: 868 : switch(eJoin)
497 : : {
498 : : case B2DLINEJOIN_MITER :
499 : : {
500 [ # # ]: 0 : aEdgePolygon.append(aEndPoint);
501 [ # # ]: 0 : aEdgePolygon.append(rPoint);
502 [ # # ]: 0 : aEdgePolygon.append(aStartPoint);
503 : :
504 : : // Look for the cut point between start point along rTangentPrev and
505 : : // end point along rTangentEdge. -rTangentEdge should be used, but since
506 : : // the cut value is used for interpolating along the first edge, the negation
507 : : // is not needed since the same fCut will be found on the first edge.
508 : : // If it exists, insert it to complete the mitered fill polygon.
509 : 0 : double fCutPos(0.0);
510 [ # # ]: 0 : tools::findCut(aStartPoint, rTangentPrev, aEndPoint, rTangentEdge, CUTFLAG_ALL, &fCutPos);
511 : :
512 [ # # ]: 0 : if(0.0 != fCutPos)
513 : : {
514 : 0 : const B2DPoint aCutPoint(interpolate(aStartPoint, aStartPoint + rTangentPrev, fCutPos));
515 [ # # ]: 0 : aEdgePolygon.append(aCutPoint);
516 : : }
517 : :
518 : : break;
519 : : }
520 : : case B2DLINEJOIN_ROUND :
521 : : {
522 : : // use tooling to add needed EllipseSegment
523 : 868 : double fAngleStart(atan2(rPerpendPrev.getY(), rPerpendPrev.getX()));
524 : 868 : double fAngleEnd(atan2(rPerpendEdge.getY(), rPerpendEdge.getX()));
525 : :
526 : : // atan2 results are [-PI .. PI], consolidate to [0.0 .. 2PI]
527 [ + + ]: 868 : if(fAngleStart < 0.0)
528 : : {
529 : 486 : fAngleStart += F_2PI;
530 : : }
531 : :
532 [ + + ]: 868 : if(fAngleEnd < 0.0)
533 : : {
534 : 486 : fAngleEnd += F_2PI;
535 : : }
536 : :
537 [ + - ]: 868 : const B2DPolygon aBow(tools::createPolygonFromEllipseSegment(rPoint, fHalfLineWidth, fHalfLineWidth, fAngleStart, fAngleEnd));
538 : :
539 [ + - ][ + - ]: 868 : if(aBow.count() > 1)
540 : : {
541 : : // #i101491#
542 : : // use the original start/end positions; the ones from bow creation may be numerically
543 : : // different due to their different creation. To guarantee good merging quality with edges
544 : : // and edge roundings (and to reduce point count)
545 [ + - ]: 868 : aEdgePolygon = aBow;
546 [ + - ]: 868 : aEdgePolygon.setB2DPoint(0, aStartPoint);
547 [ + - ][ + - ]: 868 : aEdgePolygon.setB2DPoint(aEdgePolygon.count() - 1, aEndPoint);
548 [ + - ]: 868 : aEdgePolygon.append(rPoint);
549 : :
550 : : break;
551 : : }
552 : : else
553 : : {
554 : : // wanted fall-through to default
555 [ + - ][ - + ]: 868 : }
556 : : }
557 : : default: // B2DLINEJOIN_BEVEL
558 : : {
559 [ # # ]: 0 : aEdgePolygon.append(aEndPoint);
560 [ # # ]: 0 : aEdgePolygon.append(rPoint);
561 [ # # ]: 0 : aEdgePolygon.append(aStartPoint);
562 : :
563 : 0 : break;
564 : : }
565 : : }
566 : :
567 : : // create last polygon part for edge
568 [ + - ]: 868 : aEdgePolygon.setClosed(true);
569 : :
570 : 868 : return aEdgePolygon;
571 : : }
572 : : } // end of anonymus namespace
573 : :
574 : : namespace tools
575 : : {
576 : 9861 : B2DPolyPolygon createAreaGeometry(
577 : : const B2DPolygon& rCandidate,
578 : : double fHalfLineWidth,
579 : : B2DLineJoin eJoin,
580 : : double fMaxAllowedAngle,
581 : : double fMaxPartOfEdge,
582 : : double fMiterMinimumAngle)
583 : : {
584 [ - + ]: 9861 : if(fMaxAllowedAngle > F_PI2)
585 : : {
586 : 0 : fMaxAllowedAngle = F_PI2;
587 : : }
588 [ - + ]: 9861 : else if(fMaxAllowedAngle < 0.01 * F_PI2)
589 : : {
590 : 0 : fMaxAllowedAngle = 0.01 * F_PI2;
591 : : }
592 : :
593 [ - + ]: 9861 : if(fMaxPartOfEdge > 1.0)
594 : : {
595 : 0 : fMaxPartOfEdge = 1.0;
596 : : }
597 [ - + ]: 9861 : else if(fMaxPartOfEdge < 0.01)
598 : : {
599 : 0 : fMaxPartOfEdge = 0.01;
600 : : }
601 : :
602 [ - + ]: 9861 : if(fMiterMinimumAngle > F_PI)
603 : : {
604 : 0 : fMiterMinimumAngle = F_PI;
605 : : }
606 [ - + ]: 9861 : else if(fMiterMinimumAngle < 0.01 * F_PI)
607 : : {
608 : 0 : fMiterMinimumAngle = 0.01 * F_PI;
609 : : }
610 : :
611 [ + - ]: 9861 : B2DPolygon aCandidate(rCandidate);
612 : 9861 : const double fMaxCos(cos(fMaxAllowedAngle));
613 : :
614 [ + - ]: 9861 : aCandidate.removeDoublePoints();
615 [ + - ][ + - ]: 9861 : aCandidate = subdivideToSimple(aCandidate, fMaxCos * fMaxCos, fMaxPartOfEdge * fMaxPartOfEdge);
[ + - ]
616 : :
617 [ + - ]: 9861 : const sal_uInt32 nPointCount(aCandidate.count());
618 : :
619 [ + - ]: 9861 : if(nPointCount)
620 : : {
621 [ + - ]: 9861 : B2DPolyPolygon aRetval;
622 [ + - ]: 9861 : const bool bIsClosed(aCandidate.isClosed());
623 [ - + ]: 9861 : const sal_uInt32 nEdgeCount(bIsClosed ? nPointCount : nPointCount - 1);
624 : :
625 [ + - ]: 9861 : if(nEdgeCount)
626 : : {
627 [ + - ]: 9861 : B2DCubicBezier aEdge;
628 [ + - ]: 9861 : B2DCubicBezier aPrev;
629 : :
630 : 9861 : const bool bEventuallyCreateLineJoin(B2DLINEJOIN_NONE != eJoin);
631 : : // prepare edge
632 [ + - ]: 9861 : aEdge.setStartPoint(aCandidate.getB2DPoint(0));
633 : :
634 [ # # ][ - + ]: 9861 : if(bIsClosed && bEventuallyCreateLineJoin)
635 : : {
636 : : // prepare previous edge
637 : 0 : const sal_uInt32 nPrevIndex(nPointCount - 1);
638 [ # # ]: 0 : aPrev.setStartPoint(aCandidate.getB2DPoint(nPrevIndex));
639 [ # # ]: 0 : aPrev.setControlPointA(aCandidate.getNextControlPoint(nPrevIndex));
640 [ # # ]: 0 : aPrev.setControlPointB(aCandidate.getPrevControlPoint(0));
641 : 0 : aPrev.setEndPoint(aEdge.getStartPoint());
642 : : }
643 : :
644 [ + + ]: 20590 : for(sal_uInt32 a(0); a < nEdgeCount; a++)
645 : : {
646 : : // fill current Edge
647 : 10729 : const sal_uInt32 nNextIndex((a + 1) % nPointCount);
648 [ + - ]: 10729 : aEdge.setControlPointA(aCandidate.getNextControlPoint(a));
649 [ + - ]: 10729 : aEdge.setControlPointB(aCandidate.getPrevControlPoint(nNextIndex));
650 [ + - ]: 10729 : aEdge.setEndPoint(aCandidate.getB2DPoint(nNextIndex));
651 : :
652 : : // check and create linejoin
653 [ + - ][ + + ]: 10729 : if(bEventuallyCreateLineJoin && (bIsClosed || 0 != a))
[ + + ]
654 : : {
655 [ + - ]: 868 : const B2DVector aTangentPrev(aPrev.getTangent(1.0));
656 [ + - ]: 868 : const B2DVector aTangentEdge(aEdge.getTangent(0.0));
657 [ + - ]: 868 : B2VectorOrientation aOrientation(getOrientation(aTangentPrev, aTangentEdge));
658 : :
659 [ - + ]: 868 : if(ORIENTATION_NEUTRAL == aOrientation)
660 : : {
661 : : // they are parallell or empty; if they are both not zero and point
662 : : // in opposite direction, a half-circle is needed
663 [ # # ][ # # ]: 0 : if(!aTangentPrev.equalZero() && !aTangentEdge.equalZero())
[ # # ][ # # ]
[ # # ]
664 : : {
665 [ # # ]: 0 : const double fAngle(fabs(aTangentPrev.angle(aTangentEdge)));
666 : :
667 [ # # ]: 0 : if(fTools::equal(fAngle, F_PI))
668 : : {
669 : : // for half-circle production, fallback to positive
670 : : // orientation
671 : 0 : aOrientation = ORIENTATION_POSITIVE;
672 : : }
673 : : }
674 : : }
675 : :
676 [ + + ]: 868 : if(ORIENTATION_POSITIVE == aOrientation)
677 : : {
678 [ + - ]: 486 : const B2DVector aPerpendPrev(getNormalizedPerpendicular(aTangentPrev) * -fHalfLineWidth);
679 [ + - ]: 486 : const B2DVector aPerpendEdge(getNormalizedPerpendicular(aTangentEdge) * -fHalfLineWidth);
680 : :
681 : : aRetval.append(createAreaGeometryForJoin(
682 : : aTangentPrev, aTangentEdge,
683 : : aPerpendPrev, aPerpendEdge,
684 : : aEdge.getStartPoint(), fHalfLineWidth,
685 [ + - ][ + - ]: 486 : eJoin, fMiterMinimumAngle));
[ + - ]
686 : : }
687 [ + - ]: 382 : else if(ORIENTATION_NEGATIVE == aOrientation)
688 : : {
689 [ + - ]: 382 : const B2DVector aPerpendPrev(getNormalizedPerpendicular(aTangentPrev) * fHalfLineWidth);
690 [ + - ]: 382 : const B2DVector aPerpendEdge(getNormalizedPerpendicular(aTangentEdge) * fHalfLineWidth);
691 : :
692 : : aRetval.append(createAreaGeometryForJoin(
693 : : aTangentEdge, aTangentPrev,
694 : : aPerpendEdge, aPerpendPrev,
695 : : aEdge.getStartPoint(), fHalfLineWidth,
696 [ + - ][ + - ]: 382 : eJoin, fMiterMinimumAngle));
[ + - ]
697 : 868 : }
698 : : }
699 : :
700 : : // create geometry for edge
701 [ + - ][ + - ]: 10729 : aRetval.append(createAreaGeometryForEdge(aEdge, fHalfLineWidth));
[ + - ]
702 : :
703 : : // prepare next step
704 [ + + ]: 10729 : if(bEventuallyCreateLineJoin)
705 : : {
706 [ + - ]: 1540 : aPrev = aEdge;
707 : : }
708 : :
709 : 10729 : aEdge.setStartPoint(aEdge.getEndPoint());
710 [ + - ][ + - ]: 9861 : }
711 : : }
712 : :
713 [ + - ][ + - ]: 9861 : return aRetval;
714 : : }
715 : : else
716 : : {
717 [ # # ]: 0 : return B2DPolyPolygon(rCandidate);
718 [ + - ]: 9861 : }
719 : : }
720 : : } // end of namespace tools
721 : : } // end of namespace basegfx
722 : :
723 : : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|