Line data Source code
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 <osl/diagnose.h>
21 : #include <basegfx/polygon/b3dpolygontools.hxx>
22 : #include <basegfx/polygon/b3dpolygon.hxx>
23 : #include <basegfx/numeric/ftools.hxx>
24 : #include <basegfx/range/b3drange.hxx>
25 : #include <basegfx/point/b2dpoint.hxx>
26 : #include <basegfx/matrix/b3dhommatrix.hxx>
27 : #include <basegfx/polygon/b2dpolygon.hxx>
28 : #include <basegfx/polygon/b2dpolygontools.hxx>
29 : #include <basegfx/tuple/b3ituple.hxx>
30 : #include <numeric>
31 :
32 :
33 :
34 : namespace basegfx
35 : {
36 : namespace tools
37 : {
38 : // B3DPolygon tools
39 0 : void checkClosed(B3DPolygon& rCandidate)
40 : {
41 0 : while(rCandidate.count() > 1L
42 0 : && rCandidate.getB3DPoint(0L).equal(rCandidate.getB3DPoint(rCandidate.count() - 1L)))
43 : {
44 0 : rCandidate.setClosed(true);
45 0 : rCandidate.remove(rCandidate.count() - 1L);
46 : }
47 0 : }
48 :
49 0 : sal_uInt32 getIndexOfSuccessor(sal_uInt32 nIndex, const B3DPolygon& rCandidate)
50 : {
51 : OSL_ENSURE(nIndex < rCandidate.count(), "getIndexOfPredecessor: Access to polygon out of range (!)");
52 :
53 0 : if(nIndex + 1L < rCandidate.count())
54 : {
55 0 : return nIndex + 1L;
56 : }
57 : else
58 : {
59 0 : return 0L;
60 : }
61 : }
62 :
63 0 : B3DRange getRange(const B3DPolygon& rCandidate)
64 : {
65 0 : B3DRange aRetval;
66 0 : const sal_uInt32 nPointCount(rCandidate.count());
67 :
68 0 : for(sal_uInt32 a(0L); a < nPointCount; a++)
69 : {
70 0 : const B3DPoint aTestPoint(rCandidate.getB3DPoint(a));
71 0 : aRetval.expand(aTestPoint);
72 0 : }
73 :
74 0 : return aRetval;
75 : }
76 :
77 0 : B3DVector getNormal(const B3DPolygon& rCandidate)
78 : {
79 0 : return rCandidate.getNormal();
80 : }
81 :
82 0 : double getLength(const B3DPolygon& rCandidate)
83 : {
84 0 : double fRetval(0.0);
85 0 : const sal_uInt32 nPointCount(rCandidate.count());
86 :
87 0 : if(nPointCount > 1L)
88 : {
89 0 : const sal_uInt32 nLoopCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1L);
90 :
91 0 : for(sal_uInt32 a(0L); a < nLoopCount; a++)
92 : {
93 0 : const sal_uInt32 nNextIndex(getIndexOfSuccessor(a, rCandidate));
94 0 : const B3DPoint aCurrentPoint(rCandidate.getB3DPoint(a));
95 0 : const B3DPoint aNextPoint(rCandidate.getB3DPoint(nNextIndex));
96 0 : const B3DVector aVector(aNextPoint - aCurrentPoint);
97 0 : fRetval += aVector.getLength();
98 0 : }
99 : }
100 :
101 0 : return fRetval;
102 : }
103 :
104 0 : void applyLineDashing(const B3DPolygon& rCandidate, const ::std::vector<double>& rDotDashArray, B3DPolyPolygon* pLineTarget, B3DPolyPolygon* pGapTarget, double fDotDashLength)
105 : {
106 0 : const sal_uInt32 nPointCount(rCandidate.count());
107 0 : const sal_uInt32 nDotDashCount(rDotDashArray.size());
108 :
109 0 : if(fTools::lessOrEqual(fDotDashLength, 0.0))
110 : {
111 0 : fDotDashLength = ::std::accumulate(rDotDashArray.begin(), rDotDashArray.end(), 0.0);
112 : }
113 :
114 0 : if(fTools::more(fDotDashLength, 0.0) && (pLineTarget || pGapTarget) && nPointCount)
115 : {
116 : // clear targets
117 0 : if(pLineTarget)
118 : {
119 0 : pLineTarget->clear();
120 : }
121 :
122 0 : if(pGapTarget)
123 : {
124 0 : pGapTarget->clear();
125 : }
126 :
127 : // prepare current edge's start
128 0 : B3DPoint aCurrentPoint(rCandidate.getB3DPoint(0));
129 0 : const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
130 :
131 : // prepare DotDashArray iteration and the line/gap switching bool
132 0 : sal_uInt32 nDotDashIndex(0);
133 0 : bool bIsLine(true);
134 0 : double fDotDashMovingLength(rDotDashArray[0]);
135 0 : B3DPolygon aSnippet;
136 :
137 : // iterate over all edges
138 0 : for(sal_uInt32 a(0); a < nEdgeCount; a++)
139 : {
140 : // update current edge
141 0 : double fLastDotDashMovingLength(0.0);
142 0 : const sal_uInt32 nNextIndex((a + 1) % nPointCount);
143 0 : const B3DPoint aNextPoint(rCandidate.getB3DPoint(nNextIndex));
144 0 : const double fEdgeLength(B3DVector(aNextPoint - aCurrentPoint).getLength());
145 :
146 0 : if(!fTools::equalZero(fEdgeLength))
147 : {
148 0 : while(fTools::less(fDotDashMovingLength, fEdgeLength))
149 : {
150 : // new split is inside edge, create and append snippet [fLastDotDashMovingLength, fDotDashMovingLength]
151 0 : const bool bHandleLine(bIsLine && pLineTarget);
152 0 : const bool bHandleGap(!bIsLine && pGapTarget);
153 :
154 0 : if(bHandleLine || bHandleGap)
155 : {
156 0 : if(!aSnippet.count())
157 : {
158 0 : aSnippet.append(interpolate(aCurrentPoint, aNextPoint, fLastDotDashMovingLength / fEdgeLength));
159 : }
160 :
161 0 : aSnippet.append(interpolate(aCurrentPoint, aNextPoint, fDotDashMovingLength / fEdgeLength));
162 :
163 0 : if(bHandleLine)
164 : {
165 0 : pLineTarget->append(aSnippet);
166 : }
167 : else
168 : {
169 0 : pGapTarget->append(aSnippet);
170 : }
171 :
172 0 : aSnippet.clear();
173 : }
174 :
175 : // prepare next DotDashArray step and flip line/gap flag
176 0 : fLastDotDashMovingLength = fDotDashMovingLength;
177 0 : fDotDashMovingLength += rDotDashArray[(++nDotDashIndex) % nDotDashCount];
178 0 : bIsLine = !bIsLine;
179 : }
180 :
181 : // append snippet [fLastDotDashMovingLength, fEdgeLength]
182 0 : const bool bHandleLine(bIsLine && pLineTarget);
183 0 : const bool bHandleGap(!bIsLine && pGapTarget);
184 :
185 0 : if(bHandleLine || bHandleGap)
186 : {
187 0 : if(!aSnippet.count())
188 : {
189 0 : aSnippet.append(interpolate(aCurrentPoint, aNextPoint, fLastDotDashMovingLength / fEdgeLength));
190 : }
191 :
192 0 : aSnippet.append(aNextPoint);
193 : }
194 :
195 : // prepare move to next edge
196 0 : fDotDashMovingLength -= fEdgeLength;
197 : }
198 :
199 : // prepare next edge step (end point gets new start point)
200 0 : aCurrentPoint = aNextPoint;
201 0 : }
202 :
203 : // append last intermediate results (if exists)
204 0 : if(aSnippet.count())
205 : {
206 0 : if(bIsLine && pLineTarget)
207 : {
208 0 : pLineTarget->append(aSnippet);
209 : }
210 0 : else if(!bIsLine && pGapTarget)
211 : {
212 0 : pGapTarget->append(aSnippet);
213 : }
214 : }
215 :
216 : // check if start and end polygon may be merged
217 0 : if(pLineTarget)
218 : {
219 0 : const sal_uInt32 nCount(pLineTarget->count());
220 :
221 0 : if(nCount > 1)
222 : {
223 : // these polygons were created above, there exists none with less than two points,
224 : // thus dircet point access below is allowed
225 0 : const B3DPolygon aFirst(pLineTarget->getB3DPolygon(0));
226 0 : B3DPolygon aLast(pLineTarget->getB3DPolygon(nCount - 1));
227 :
228 0 : if(aFirst.getB3DPoint(0).equal(aLast.getB3DPoint(aLast.count() - 1)))
229 : {
230 : // start of first and end of last are the same -> merge them
231 0 : aLast.append(aFirst);
232 0 : aLast.removeDoublePoints();
233 0 : pLineTarget->setB3DPolygon(0, aLast);
234 0 : pLineTarget->remove(nCount - 1);
235 0 : }
236 : }
237 : }
238 :
239 0 : if(pGapTarget)
240 : {
241 0 : const sal_uInt32 nCount(pGapTarget->count());
242 :
243 0 : if(nCount > 1)
244 : {
245 : // these polygons were created above, there exists none with less than two points,
246 : // thus dircet point access below is allowed
247 0 : const B3DPolygon aFirst(pGapTarget->getB3DPolygon(0));
248 0 : B3DPolygon aLast(pGapTarget->getB3DPolygon(nCount - 1));
249 :
250 0 : if(aFirst.getB3DPoint(0).equal(aLast.getB3DPoint(aLast.count() - 1)))
251 : {
252 : // start of first and end of last are the same -> merge them
253 0 : aLast.append(aFirst);
254 0 : aLast.removeDoublePoints();
255 0 : pGapTarget->setB3DPolygon(0, aLast);
256 0 : pGapTarget->remove(nCount - 1);
257 0 : }
258 : }
259 0 : }
260 : }
261 : else
262 : {
263 : // parameters make no sense, just add source to targets
264 0 : if(pLineTarget)
265 : {
266 0 : pLineTarget->append(rCandidate);
267 : }
268 :
269 0 : if(pGapTarget)
270 : {
271 0 : pGapTarget->append(rCandidate);
272 : }
273 : }
274 0 : }
275 :
276 0 : B3DPolygon applyDefaultNormalsSphere( const B3DPolygon& rCandidate, const B3DPoint& rCenter)
277 : {
278 0 : B3DPolygon aRetval(rCandidate);
279 :
280 0 : for(sal_uInt32 a(0L); a < aRetval.count(); a++)
281 : {
282 0 : B3DVector aVector(aRetval.getB3DPoint(a) - rCenter);
283 0 : aVector.normalize();
284 0 : aRetval.setNormal(a, aVector);
285 0 : }
286 :
287 0 : return aRetval;
288 : }
289 :
290 0 : B3DPolygon invertNormals( const B3DPolygon& rCandidate)
291 : {
292 0 : B3DPolygon aRetval(rCandidate);
293 :
294 0 : if(aRetval.areNormalsUsed())
295 : {
296 0 : for(sal_uInt32 a(0L); a < aRetval.count(); a++)
297 : {
298 0 : aRetval.setNormal(a, -aRetval.getNormal(a));
299 : }
300 : }
301 :
302 0 : return aRetval;
303 : }
304 :
305 0 : B3DPolygon applyDefaultTextureCoordinatesParallel( const B3DPolygon& rCandidate, const B3DRange& rRange, bool bChangeX, bool bChangeY)
306 : {
307 0 : B3DPolygon aRetval(rCandidate);
308 :
309 0 : if(bChangeX || bChangeY)
310 : {
311 : // create projection of standard texture coordinates in (X, Y) onto
312 : // the 3d coordinates straight
313 0 : const double fWidth(rRange.getWidth());
314 0 : const double fHeight(rRange.getHeight());
315 0 : const bool bWidthSet(!fTools::equalZero(fWidth));
316 0 : const bool bHeightSet(!fTools::equalZero(fHeight));
317 0 : const double fOne(1.0);
318 :
319 0 : for(sal_uInt32 a(0L); a < aRetval.count(); a++)
320 : {
321 0 : const B3DPoint aPoint(aRetval.getB3DPoint(a));
322 0 : B2DPoint aTextureCoordinate(aRetval.getTextureCoordinate(a));
323 :
324 0 : if(bChangeX)
325 : {
326 0 : if(bWidthSet)
327 : {
328 0 : aTextureCoordinate.setX((aPoint.getX() - rRange.getMinX()) / fWidth);
329 : }
330 : else
331 : {
332 0 : aTextureCoordinate.setX(0.0);
333 : }
334 : }
335 :
336 0 : if(bChangeY)
337 : {
338 0 : if(bHeightSet)
339 : {
340 0 : aTextureCoordinate.setY(fOne - ((aPoint.getY() - rRange.getMinY()) / fHeight));
341 : }
342 : else
343 : {
344 0 : aTextureCoordinate.setY(fOne);
345 : }
346 : }
347 :
348 0 : aRetval.setTextureCoordinate(a, aTextureCoordinate);
349 0 : }
350 : }
351 :
352 0 : return aRetval;
353 : }
354 :
355 0 : B3DPolygon applyDefaultTextureCoordinatesSphere( const B3DPolygon& rCandidate, const B3DPoint& rCenter, bool bChangeX, bool bChangeY)
356 : {
357 0 : B3DPolygon aRetval(rCandidate);
358 :
359 0 : if(bChangeX || bChangeY)
360 : {
361 : // create texture coordinates using sphere projection to cartesian coordinates,
362 : // use object's center as base
363 0 : const double fOne(1.0);
364 0 : const sal_uInt32 nPointCount(aRetval.count());
365 0 : bool bPolarPoints(false);
366 : sal_uInt32 a;
367 :
368 : // create center cartesian coordinates to have a possibility to decide if on boundary
369 : // transitions which value to choose
370 0 : const B3DRange aPlaneRange(getRange(rCandidate));
371 0 : const B3DPoint aPlaneCenter(aPlaneRange.getCenter() - rCenter);
372 0 : const double fXCenter(fOne - ((atan2(aPlaneCenter.getZ(), aPlaneCenter.getX()) + F_PI) / F_2PI));
373 :
374 0 : for(a = 0L; a < nPointCount; a++)
375 : {
376 0 : const B3DVector aVector(aRetval.getB3DPoint(a) - rCenter);
377 0 : const double fY(fOne - ((atan2(aVector.getY(), aVector.getXZLength()) + F_PI2) / F_PI));
378 0 : B2DPoint aTexCoor(aRetval.getTextureCoordinate(a));
379 :
380 0 : if(fTools::equalZero(fY))
381 : {
382 : // point is a north polar point, no useful X-coordinate can be created.
383 0 : if(bChangeY)
384 : {
385 0 : aTexCoor.setY(0.0);
386 :
387 0 : if(bChangeX)
388 : {
389 0 : bPolarPoints = true;
390 : }
391 : }
392 : }
393 0 : else if(fTools::equal(fY, fOne))
394 : {
395 : // point is a south polar point, no useful X-coordinate can be created. Set
396 : // Y-coordinte, though
397 0 : if(bChangeY)
398 : {
399 0 : aTexCoor.setY(fOne);
400 :
401 0 : if(bChangeX)
402 : {
403 0 : bPolarPoints = true;
404 : }
405 : }
406 : }
407 : else
408 : {
409 0 : double fX(fOne - ((atan2(aVector.getZ(), aVector.getX()) + F_PI) / F_2PI));
410 :
411 : // correct cartesinan point coordiante dependent from center value
412 0 : if(fX > fXCenter + 0.5)
413 : {
414 0 : fX -= fOne;
415 : }
416 0 : else if(fX < fXCenter - 0.5)
417 : {
418 0 : fX += fOne;
419 : }
420 :
421 0 : if(bChangeX)
422 : {
423 0 : aTexCoor.setX(fX);
424 : }
425 :
426 0 : if(bChangeY)
427 : {
428 0 : aTexCoor.setY(fY);
429 : }
430 : }
431 :
432 0 : aRetval.setTextureCoordinate(a, aTexCoor);
433 0 : }
434 :
435 0 : if(bPolarPoints)
436 : {
437 : // correct X-texture coordinates if polar points are contained. Those
438 : // coordinates cannot be correct, so use prev or next X-coordinate
439 0 : for(a = 0L; a < nPointCount; a++)
440 : {
441 0 : B2DPoint aTexCoor(aRetval.getTextureCoordinate(a));
442 :
443 0 : if(fTools::equalZero(aTexCoor.getY()) || fTools::equal(aTexCoor.getY(), fOne))
444 : {
445 : // get prev, next TexCoor and test for pole
446 0 : const B2DPoint aPrevTexCoor(aRetval.getTextureCoordinate(a ? a - 1L : nPointCount - 1L));
447 0 : const B2DPoint aNextTexCoor(aRetval.getTextureCoordinate((a + 1L) % nPointCount));
448 0 : const bool bPrevPole(fTools::equalZero(aPrevTexCoor.getY()) || fTools::equal(aPrevTexCoor.getY(), fOne));
449 0 : const bool bNextPole(fTools::equalZero(aNextTexCoor.getY()) || fTools::equal(aNextTexCoor.getY(), fOne));
450 :
451 0 : if(!bPrevPole && !bNextPole)
452 : {
453 : // both no poles, mix them
454 0 : aTexCoor.setX((aPrevTexCoor.getX() + aNextTexCoor.getX()) / 2.0);
455 : }
456 0 : else if(!bNextPole)
457 : {
458 : // copy next
459 0 : aTexCoor.setX(aNextTexCoor.getX());
460 : }
461 : else
462 : {
463 : // copy prev, even if it's a pole, hopefully it is already corrected
464 0 : aTexCoor.setX(aPrevTexCoor.getX());
465 : }
466 :
467 0 : aRetval.setTextureCoordinate(a, aTexCoor);
468 : }
469 0 : }
470 0 : }
471 : }
472 :
473 0 : return aRetval;
474 : }
475 :
476 0 : bool isInside(const B3DPolygon& rCandidate, const B3DPoint& rPoint, bool bWithBorder)
477 : {
478 0 : if(bWithBorder && isPointOnPolygon(rCandidate, rPoint, true))
479 : {
480 0 : return true;
481 : }
482 : else
483 : {
484 0 : bool bRetval(false);
485 0 : const B3DVector aPlaneNormal(rCandidate.getNormal());
486 :
487 0 : if(!aPlaneNormal.equalZero())
488 : {
489 0 : const sal_uInt32 nPointCount(rCandidate.count());
490 :
491 0 : if(nPointCount)
492 : {
493 0 : B3DPoint aCurrentPoint(rCandidate.getB3DPoint(nPointCount - 1));
494 0 : const double fAbsX(fabs(aPlaneNormal.getX()));
495 0 : const double fAbsY(fabs(aPlaneNormal.getY()));
496 0 : const double fAbsZ(fabs(aPlaneNormal.getZ()));
497 :
498 0 : if(fAbsX > fAbsY && fAbsX > fAbsZ)
499 : {
500 : // normal points mostly in X-Direction, use YZ-Polygon projection for check
501 : // x -> y, y -> z
502 0 : for(sal_uInt32 a(0); a < nPointCount; a++)
503 : {
504 0 : const B3DPoint aPreviousPoint(aCurrentPoint);
505 0 : aCurrentPoint = rCandidate.getB3DPoint(a);
506 :
507 : // cross-over in Z?
508 0 : const bool bCompZA(fTools::more(aPreviousPoint.getZ(), rPoint.getZ()));
509 0 : const bool bCompZB(fTools::more(aCurrentPoint.getZ(), rPoint.getZ()));
510 :
511 0 : if(bCompZA != bCompZB)
512 : {
513 : // cross-over in Y?
514 0 : const bool bCompYA(fTools::more(aPreviousPoint.getY(), rPoint.getY()));
515 0 : const bool bCompYB(fTools::more(aCurrentPoint.getY(), rPoint.getY()));
516 :
517 0 : if(bCompYA == bCompYB)
518 : {
519 0 : if(bCompYA)
520 : {
521 0 : bRetval = !bRetval;
522 : }
523 : }
524 : else
525 : {
526 : const double fCompare(
527 0 : aCurrentPoint.getY() - (aCurrentPoint.getZ() - rPoint.getZ()) *
528 0 : (aPreviousPoint.getY() - aCurrentPoint.getY()) /
529 0 : (aPreviousPoint.getZ() - aCurrentPoint.getZ()));
530 :
531 0 : if(fTools::more(fCompare, rPoint.getY()))
532 : {
533 0 : bRetval = !bRetval;
534 : }
535 : }
536 : }
537 0 : }
538 : }
539 0 : else if(fAbsY > fAbsX && fAbsY > fAbsZ)
540 : {
541 : // normal points mostly in Y-Direction, use XZ-Polygon projection for check
542 : // x -> x, y -> z
543 0 : for(sal_uInt32 a(0); a < nPointCount; a++)
544 : {
545 0 : const B3DPoint aPreviousPoint(aCurrentPoint);
546 0 : aCurrentPoint = rCandidate.getB3DPoint(a);
547 :
548 : // cross-over in Z?
549 0 : const bool bCompZA(fTools::more(aPreviousPoint.getZ(), rPoint.getZ()));
550 0 : const bool bCompZB(fTools::more(aCurrentPoint.getZ(), rPoint.getZ()));
551 :
552 0 : if(bCompZA != bCompZB)
553 : {
554 : // cross-over in X?
555 0 : const bool bCompXA(fTools::more(aPreviousPoint.getX(), rPoint.getX()));
556 0 : const bool bCompXB(fTools::more(aCurrentPoint.getX(), rPoint.getX()));
557 :
558 0 : if(bCompXA == bCompXB)
559 : {
560 0 : if(bCompXA)
561 : {
562 0 : bRetval = !bRetval;
563 : }
564 : }
565 : else
566 : {
567 : const double fCompare(
568 0 : aCurrentPoint.getX() - (aCurrentPoint.getZ() - rPoint.getZ()) *
569 0 : (aPreviousPoint.getX() - aCurrentPoint.getX()) /
570 0 : (aPreviousPoint.getZ() - aCurrentPoint.getZ()));
571 :
572 0 : if(fTools::more(fCompare, rPoint.getX()))
573 : {
574 0 : bRetval = !bRetval;
575 : }
576 : }
577 : }
578 0 : }
579 : }
580 : else
581 : {
582 : // normal points mostly in Z-Direction, use XY-Polygon projection for check
583 : // x -> x, y -> y
584 0 : for(sal_uInt32 a(0); a < nPointCount; a++)
585 : {
586 0 : const B3DPoint aPreviousPoint(aCurrentPoint);
587 0 : aCurrentPoint = rCandidate.getB3DPoint(a);
588 :
589 : // cross-over in Y?
590 0 : const bool bCompYA(fTools::more(aPreviousPoint.getY(), rPoint.getY()));
591 0 : const bool bCompYB(fTools::more(aCurrentPoint.getY(), rPoint.getY()));
592 :
593 0 : if(bCompYA != bCompYB)
594 : {
595 : // cross-over in X?
596 0 : const bool bCompXA(fTools::more(aPreviousPoint.getX(), rPoint.getX()));
597 0 : const bool bCompXB(fTools::more(aCurrentPoint.getX(), rPoint.getX()));
598 :
599 0 : if(bCompXA == bCompXB)
600 : {
601 0 : if(bCompXA)
602 : {
603 0 : bRetval = !bRetval;
604 : }
605 : }
606 : else
607 : {
608 : const double fCompare(
609 0 : aCurrentPoint.getX() - (aCurrentPoint.getY() - rPoint.getY()) *
610 0 : (aPreviousPoint.getX() - aCurrentPoint.getX()) /
611 0 : (aPreviousPoint.getY() - aCurrentPoint.getY()));
612 :
613 0 : if(fTools::more(fCompare, rPoint.getX()))
614 : {
615 0 : bRetval = !bRetval;
616 : }
617 : }
618 : }
619 0 : }
620 0 : }
621 : }
622 : }
623 :
624 0 : return bRetval;
625 : }
626 : }
627 :
628 0 : bool isPointOnLine(const B3DPoint& rStart, const B3DPoint& rEnd, const B3DPoint& rCandidate, bool bWithPoints)
629 : {
630 0 : if(rCandidate.equal(rStart) || rCandidate.equal(rEnd))
631 : {
632 : // candidate is in epsilon around start or end -> inside
633 0 : return bWithPoints;
634 : }
635 0 : else if(rStart.equal(rEnd))
636 : {
637 : // start and end are equal, but candidate is outside their epsilon -> outside
638 0 : return false;
639 : }
640 : else
641 : {
642 0 : const B3DVector aEdgeVector(rEnd - rStart);
643 0 : const B3DVector aTestVector(rCandidate - rStart);
644 :
645 0 : if(areParallel(aEdgeVector, aTestVector))
646 : {
647 0 : const double fZero(0.0);
648 0 : const double fOne(1.0);
649 0 : double fParamTestOnCurr(0.0);
650 :
651 0 : if(aEdgeVector.getX() > aEdgeVector.getY())
652 : {
653 0 : if(aEdgeVector.getX() > aEdgeVector.getZ())
654 : {
655 : // X is biggest
656 0 : fParamTestOnCurr = aTestVector.getX() / aEdgeVector.getX();
657 : }
658 : else
659 : {
660 : // Z is biggest
661 0 : fParamTestOnCurr = aTestVector.getZ() / aEdgeVector.getZ();
662 : }
663 : }
664 : else
665 : {
666 0 : if(aEdgeVector.getY() > aEdgeVector.getZ())
667 : {
668 : // Y is biggest
669 0 : fParamTestOnCurr = aTestVector.getY() / aEdgeVector.getY();
670 : }
671 : else
672 : {
673 : // Z is biggest
674 0 : fParamTestOnCurr = aTestVector.getZ() / aEdgeVector.getZ();
675 : }
676 : }
677 :
678 0 : if(fTools::more(fParamTestOnCurr, fZero) && fTools::less(fParamTestOnCurr, fOne))
679 : {
680 0 : return true;
681 : }
682 : }
683 :
684 0 : return false;
685 : }
686 : }
687 :
688 0 : bool isPointOnPolygon(const B3DPolygon& rCandidate, const B3DPoint& rPoint, bool bWithPoints)
689 : {
690 0 : const sal_uInt32 nPointCount(rCandidate.count());
691 :
692 0 : if(nPointCount > 1L)
693 : {
694 0 : const sal_uInt32 nLoopCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1L);
695 0 : B3DPoint aCurrentPoint(rCandidate.getB3DPoint(0));
696 :
697 0 : for(sal_uInt32 a(0); a < nLoopCount; a++)
698 : {
699 0 : const B3DPoint aNextPoint(rCandidate.getB3DPoint((a + 1) % nPointCount));
700 :
701 0 : if(isPointOnLine(aCurrentPoint, aNextPoint, rPoint, bWithPoints))
702 : {
703 0 : return true;
704 : }
705 :
706 0 : aCurrentPoint = aNextPoint;
707 0 : }
708 : }
709 0 : else if(nPointCount && bWithPoints)
710 : {
711 0 : return rPoint.equal(rCandidate.getB3DPoint(0));
712 : }
713 :
714 0 : return false;
715 : }
716 :
717 0 : bool getCutBetweenLineAndPlane(const B3DVector& rPlaneNormal, const B3DPoint& rPlanePoint, const B3DPoint& rEdgeStart, const B3DPoint& rEdgeEnd, double& fCut)
718 : {
719 0 : if(!rPlaneNormal.equalZero() && !rEdgeStart.equal(rEdgeEnd))
720 : {
721 0 : const B3DVector aTestEdge(rEdgeEnd - rEdgeStart);
722 0 : const double fScalarEdge(rPlaneNormal.scalar(aTestEdge));
723 :
724 0 : if(!fTools::equalZero(fScalarEdge))
725 : {
726 0 : const B3DVector aCompareEdge(rPlanePoint - rEdgeStart);
727 0 : const double fScalarCompare(rPlaneNormal.scalar(aCompareEdge));
728 :
729 0 : fCut = fScalarCompare / fScalarEdge;
730 0 : return true;
731 0 : }
732 : }
733 :
734 0 : return false;
735 : }
736 :
737 :
738 : // comparators with tolerance for 3D Polygons
739 :
740 0 : bool equal(const B3DPolygon& rCandidateA, const B3DPolygon& rCandidateB, const double& rfSmallValue)
741 : {
742 0 : const sal_uInt32 nPointCount(rCandidateA.count());
743 :
744 0 : if(nPointCount != rCandidateB.count())
745 0 : return false;
746 :
747 0 : const bool bClosed(rCandidateA.isClosed());
748 :
749 0 : if(bClosed != rCandidateB.isClosed())
750 0 : return false;
751 :
752 0 : for(sal_uInt32 a(0); a < nPointCount; a++)
753 : {
754 0 : const B3DPoint aPoint(rCandidateA.getB3DPoint(a));
755 :
756 0 : if(!aPoint.equal(rCandidateB.getB3DPoint(a), rfSmallValue))
757 0 : return false;
758 0 : }
759 :
760 0 : return true;
761 : }
762 :
763 : // snap points of horizontal or vertical edges to discrete values
764 0 : B3DPolygon snapPointsOfHorizontalOrVerticalEdges(const B3DPolygon& rCandidate)
765 : {
766 0 : const sal_uInt32 nPointCount(rCandidate.count());
767 :
768 0 : if(nPointCount > 1)
769 : {
770 : // Start by copying the source polygon to get a writeable copy. The closed state is
771 : // copied by aRetval's initialisation, too, so no need to copy it in this method
772 0 : B3DPolygon aRetval(rCandidate);
773 :
774 : // prepare geometry data. Get rounded from original
775 0 : B3ITuple aPrevTuple(basegfx::fround(rCandidate.getB3DPoint(nPointCount - 1)));
776 0 : B3DPoint aCurrPoint(rCandidate.getB3DPoint(0));
777 0 : B3ITuple aCurrTuple(basegfx::fround(aCurrPoint));
778 :
779 : // loop over all points. This will also snap the implicit closing edge
780 : // even when not closed, but that's no problem here
781 0 : for(sal_uInt32 a(0); a < nPointCount; a++)
782 : {
783 : // get next point. Get rounded from original
784 0 : const bool bLastRun(a + 1 == nPointCount);
785 0 : const sal_uInt32 nNextIndex(bLastRun ? 0 : a + 1);
786 0 : const B3DPoint aNextPoint(rCandidate.getB3DPoint(nNextIndex));
787 0 : const B3ITuple aNextTuple(basegfx::fround(aNextPoint));
788 :
789 : // get the states
790 0 : const bool bPrevVertical(aPrevTuple.getX() == aCurrTuple.getX());
791 0 : const bool bNextVertical(aNextTuple.getX() == aCurrTuple.getX());
792 0 : const bool bPrevHorizontal(aPrevTuple.getY() == aCurrTuple.getY());
793 0 : const bool bNextHorizontal(aNextTuple.getY() == aCurrTuple.getY());
794 0 : const bool bSnapX(bPrevVertical || bNextVertical);
795 0 : const bool bSnapY(bPrevHorizontal || bNextHorizontal);
796 :
797 0 : if(bSnapX || bSnapY)
798 : {
799 : const B3DPoint aSnappedPoint(
800 0 : bSnapX ? aCurrTuple.getX() : aCurrPoint.getX(),
801 0 : bSnapY ? aCurrTuple.getY() : aCurrPoint.getY(),
802 0 : aCurrPoint.getZ());
803 :
804 0 : aRetval.setB3DPoint(a, aSnappedPoint);
805 : }
806 :
807 : // prepare next point
808 0 : if(!bLastRun)
809 : {
810 0 : aPrevTuple = aCurrTuple;
811 0 : aCurrPoint = aNextPoint;
812 0 : aCurrTuple = aNextTuple;
813 : }
814 0 : }
815 :
816 0 : return aRetval;
817 : }
818 : else
819 : {
820 0 : return rCandidate;
821 : }
822 : }
823 :
824 : } // end of namespace tools
825 : } // end of namespace basegfx
826 :
827 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|