xref: /AOO41X/main/basegfx/source/polygon/b3dpolygontools.cxx (revision b2937f997bda0a05141a2d862a64f7be893955b7)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_basegfx.hxx"
26 #include <osl/diagnose.h>
27 #include <basegfx/polygon/b3dpolygontools.hxx>
28 #include <basegfx/polygon/b3dpolygon.hxx>
29 #include <basegfx/numeric/ftools.hxx>
30 #include <basegfx/range/b3drange.hxx>
31 #include <basegfx/point/b2dpoint.hxx>
32 #include <basegfx/matrix/b3dhommatrix.hxx>
33 #include <basegfx/polygon/b2dpolygon.hxx>
34 #include <basegfx/polygon/b2dpolygontools.hxx>
35 #include <basegfx/tuple/b3ituple.hxx>
36 #include <numeric>
37 
38 //////////////////////////////////////////////////////////////////////////////
39 
40 namespace basegfx
41 {
42     namespace tools
43     {
44         // B3DPolygon tools
45         void checkClosed(B3DPolygon& rCandidate)
46         {
47             while(rCandidate.count() > 1L
48                 && rCandidate.getB3DPoint(0L).equal(rCandidate.getB3DPoint(rCandidate.count() - 1L)))
49             {
50                 rCandidate.setClosed(true);
51                 rCandidate.remove(rCandidate.count() - 1L);
52             }
53         }
54 
55         // Get successor and predecessor indices. Returning the same index means there
56         // is none. Same for successor.
57         sal_uInt32 getIndexOfPredecessor(sal_uInt32 nIndex, const B3DPolygon& rCandidate)
58         {
59             OSL_ENSURE(nIndex < rCandidate.count(), "getIndexOfPredecessor: Access to polygon out of range (!)");
60 
61             if(nIndex)
62             {
63                 return nIndex - 1L;
64             }
65             else if(rCandidate.count())
66             {
67                 return rCandidate.count() - 1L;
68             }
69             else
70             {
71                 return nIndex;
72             }
73         }
74 
75         sal_uInt32 getIndexOfSuccessor(sal_uInt32 nIndex, const B3DPolygon& rCandidate)
76         {
77             OSL_ENSURE(nIndex < rCandidate.count(), "getIndexOfPredecessor: Access to polygon out of range (!)");
78 
79             if(nIndex + 1L < rCandidate.count())
80             {
81                 return nIndex + 1L;
82             }
83             else
84             {
85                 return 0L;
86             }
87         }
88 
89         B3DRange getRange(const B3DPolygon& rCandidate)
90         {
91             B3DRange aRetval;
92             const sal_uInt32 nPointCount(rCandidate.count());
93 
94             for(sal_uInt32 a(0L); a < nPointCount; a++)
95             {
96                 const B3DPoint aTestPoint(rCandidate.getB3DPoint(a));
97                 aRetval.expand(aTestPoint);
98             }
99 
100             return aRetval;
101         }
102 
103         B3DVector getNormal(const B3DPolygon& rCandidate)
104         {
105             return rCandidate.getNormal();
106         }
107 
108         B3DVector getPositiveOrientedNormal(const B3DPolygon& rCandidate)
109         {
110             B3DVector aRetval(rCandidate.getNormal());
111 
112             if(ORIENTATION_NEGATIVE == getOrientation(rCandidate))
113             {
114                 aRetval = -aRetval;
115             }
116 
117             return aRetval;
118         }
119 
120         B2VectorOrientation getOrientation(const B3DPolygon& rCandidate)
121         {
122             B2VectorOrientation eRetval(ORIENTATION_NEUTRAL);
123 
124             if(rCandidate.count() > 2L)
125             {
126                 const double fSignedArea(getSignedArea(rCandidate));
127 
128                 if(fSignedArea > 0.0)
129                 {
130                     eRetval = ORIENTATION_POSITIVE;
131                 }
132                 else if(fSignedArea < 0.0)
133                 {
134                     eRetval = ORIENTATION_NEGATIVE;
135                 }
136             }
137 
138             return eRetval;
139         }
140 
141         double getSignedArea(const B3DPolygon& rCandidate)
142         {
143             double fRetval(0.0);
144             const sal_uInt32 nPointCount(rCandidate.count());
145 
146             if(nPointCount > 2)
147             {
148                 const B3DVector aAbsNormal(absolute(getNormal(rCandidate)));
149                 sal_uInt16 nCase(3); // default: ignore z
150 
151                 if(aAbsNormal.getX() > aAbsNormal.getY())
152                 {
153                     if(aAbsNormal.getX() > aAbsNormal.getZ())
154                     {
155                         nCase = 1; // ignore x
156                     }
157                 }
158                 else if(aAbsNormal.getY() > aAbsNormal.getZ())
159                 {
160                     nCase = 2; // ignore y
161                 }
162 
163                 B3DPoint aPreviousPoint(rCandidate.getB3DPoint(nPointCount - 1L));
164 
165                 for(sal_uInt32 a(0L); a < nPointCount; a++)
166                 {
167                     const B3DPoint aCurrentPoint(rCandidate.getB3DPoint(a));
168 
169                     switch(nCase)
170                     {
171                         case 1: // ignore x
172                             fRetval += aPreviousPoint.getZ() * aCurrentPoint.getY();
173                             fRetval -= aPreviousPoint.getY() * aCurrentPoint.getZ();
174                             break;
175                         case 2: // ignore y
176                             fRetval += aPreviousPoint.getX() * aCurrentPoint.getZ();
177                             fRetval -= aPreviousPoint.getZ() * aCurrentPoint.getX();
178                             break;
179                         case 3: // ignore z
180                             fRetval += aPreviousPoint.getX() * aCurrentPoint.getY();
181                             fRetval -= aPreviousPoint.getY() * aCurrentPoint.getX();
182                             break;
183                     }
184 
185                     // prepare next step
186                     aPreviousPoint = aCurrentPoint;
187                 }
188 
189                 switch(nCase)
190                 {
191                     case 1: // ignore x
192                         fRetval /= 2.0 * aAbsNormal.getX();
193                         break;
194                     case 2: // ignore y
195                         fRetval /= 2.0 * aAbsNormal.getY();
196                         break;
197                     case 3: // ignore z
198                         fRetval /= 2.0 * aAbsNormal.getZ();
199                         break;
200                 }
201             }
202 
203             return fRetval;
204         }
205 
206         double getArea(const B3DPolygon& rCandidate)
207         {
208             double fRetval(0.0);
209 
210             if(rCandidate.count() > 2)
211             {
212                 fRetval = getSignedArea(rCandidate);
213                 const double fZero(0.0);
214 
215                 if(fTools::less(fRetval, fZero))
216                 {
217                     fRetval = -fRetval;
218                 }
219             }
220 
221             return fRetval;
222         }
223 
224         double getEdgeLength(const B3DPolygon& rCandidate, sal_uInt32 nIndex)
225         {
226             OSL_ENSURE(nIndex < rCandidate.count(), "getEdgeLength: Access to polygon out of range (!)");
227             double fRetval(0.0);
228             const sal_uInt32 nPointCount(rCandidate.count());
229 
230             if(nIndex < nPointCount)
231             {
232                 if(rCandidate.isClosed() || ((nIndex + 1L) != nPointCount))
233                 {
234                     const sal_uInt32 nNextIndex(getIndexOfSuccessor(nIndex, rCandidate));
235                     const B3DPoint aCurrentPoint(rCandidate.getB3DPoint(nIndex));
236                     const B3DPoint aNextPoint(rCandidate.getB3DPoint(nNextIndex));
237                     const B3DVector aVector(aNextPoint - aCurrentPoint);
238                     fRetval = aVector.getLength();
239                 }
240             }
241 
242             return fRetval;
243         }
244 
245         double getLength(const B3DPolygon& rCandidate)
246         {
247             double fRetval(0.0);
248             const sal_uInt32 nPointCount(rCandidate.count());
249 
250             if(nPointCount > 1L)
251             {
252                 const sal_uInt32 nLoopCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1L);
253 
254                 for(sal_uInt32 a(0L); a < nLoopCount; a++)
255                 {
256                     const sal_uInt32 nNextIndex(getIndexOfSuccessor(a, rCandidate));
257                     const B3DPoint aCurrentPoint(rCandidate.getB3DPoint(a));
258                     const B3DPoint aNextPoint(rCandidate.getB3DPoint(nNextIndex));
259                     const B3DVector aVector(aNextPoint - aCurrentPoint);
260                     fRetval += aVector.getLength();
261                 }
262             }
263 
264             return fRetval;
265         }
266 
267         B3DPoint getPositionAbsolute(const B3DPolygon& rCandidate, double fDistance, double fLength)
268         {
269             B3DPoint aRetval;
270             const sal_uInt32 nPointCount(rCandidate.count());
271 
272             if(nPointCount > 1L)
273             {
274                 sal_uInt32 nIndex(0L);
275                 bool bIndexDone(false);
276                 const double fZero(0.0);
277                 double fEdgeLength(fZero);
278 
279                 // get length if not given
280                 if(fTools::equalZero(fLength))
281                 {
282                     fLength = getLength(rCandidate);
283                 }
284 
285                 // handle fDistance < 0.0
286                 if(fTools::less(fDistance, fZero))
287                 {
288                     if(rCandidate.isClosed())
289                     {
290                         // if fDistance < 0.0 increment with multiple of fLength
291                         sal_uInt32 nCount(sal_uInt32(-fDistance / fLength));
292                         fDistance += double(nCount + 1L) * fLength;
293                     }
294                     else
295                     {
296                         // crop to polygon start
297                         fDistance = fZero;
298                         bIndexDone = true;
299                     }
300                 }
301 
302                 // handle fDistance >= fLength
303                 if(fTools::moreOrEqual(fDistance, fLength))
304                 {
305                     if(rCandidate.isClosed())
306                     {
307                         // if fDistance >= fLength decrement with multiple of fLength
308                         sal_uInt32 nCount(sal_uInt32(fDistance / fLength));
309                         fDistance -= (double)(nCount) * fLength;
310                     }
311                     else
312                     {
313                         // crop to polygon end
314                         fDistance = fZero;
315                         nIndex = nPointCount - 1L;
316                         bIndexDone = true;
317                     }
318                 }
319 
320                 // look for correct index. fDistance is now [0.0 .. fLength[
321                 if(!bIndexDone)
322                 {
323                     do
324                     {
325                         // get length of next edge
326                         fEdgeLength = getEdgeLength(rCandidate, nIndex);
327 
328                         if(fTools::moreOrEqual(fDistance, fEdgeLength))
329                         {
330                             // go to next edge
331                             fDistance -= fEdgeLength;
332                             nIndex++;
333                         }
334                         else
335                         {
336                             // it's on this edge, stop
337                             bIndexDone = true;
338                         }
339                     } while (!bIndexDone);
340                 }
341 
342                 // get the point using nIndex
343                 aRetval = rCandidate.getB3DPoint(nIndex);
344 
345                 // if fDistance != 0.0, move that length on the edge. The edge
346                 // length is in fEdgeLength.
347                 if(!fTools::equalZero(fDistance))
348                 {
349                     sal_uInt32 nNextIndex(getIndexOfSuccessor(nIndex, rCandidate));
350                     const B3DPoint aNextPoint(rCandidate.getB3DPoint(nNextIndex));
351                     double fRelative(fZero);
352 
353                     if(!fTools::equalZero(fEdgeLength))
354                     {
355                         fRelative = fDistance / fEdgeLength;
356                     }
357 
358                     // add calculated average value to the return value
359                     aRetval += interpolate(aRetval, aNextPoint, fRelative);
360                 }
361             }
362 
363             return aRetval;
364         }
365 
366         B3DPoint getPositionRelative(const B3DPolygon& rCandidate, double fDistance, double fLength)
367         {
368             // get length if not given
369             if(fTools::equalZero(fLength))
370             {
371                 fLength = getLength(rCandidate);
372             }
373 
374             // multiply fDistance with real length to get absolute position and
375             // use getPositionAbsolute
376             return getPositionAbsolute(rCandidate, fDistance * fLength, fLength);
377         }
378 
379         void applyLineDashing(const B3DPolygon& rCandidate, const ::std::vector<double>& rDotDashArray, B3DPolyPolygon* pLineTarget, B3DPolyPolygon* pGapTarget, double fDotDashLength)
380         {
381             const sal_uInt32 nPointCount(rCandidate.count());
382             const sal_uInt32 nDotDashCount(rDotDashArray.size());
383 
384             if(fTools::lessOrEqual(fDotDashLength, 0.0))
385             {
386                 fDotDashLength = ::std::accumulate(rDotDashArray.begin(), rDotDashArray.end(), 0.0);
387             }
388 
389             if(fTools::more(fDotDashLength, 0.0) && (pLineTarget || pGapTarget) && nPointCount)
390             {
391                 // clear targets
392                 if(pLineTarget)
393                 {
394                     pLineTarget->clear();
395                 }
396 
397                 if(pGapTarget)
398                 {
399                     pGapTarget->clear();
400                 }
401 
402                 // prepare current edge's start
403                 B3DPoint aCurrentPoint(rCandidate.getB3DPoint(0));
404                 const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
405 
406                 // prepare DotDashArray iteration and the line/gap switching bool
407                 sal_uInt32 nDotDashIndex(0);
408                 bool bIsLine(true);
409                 double fDotDashMovingLength(rDotDashArray[0]);
410                 B3DPolygon aSnippet;
411 
412                 // iterate over all edges
413                 for(sal_uInt32 a(0); a < nEdgeCount; a++)
414                 {
415                     // update current edge
416                     double fLastDotDashMovingLength(0.0);
417                     const sal_uInt32 nNextIndex((a + 1) % nPointCount);
418                     const B3DPoint aNextPoint(rCandidate.getB3DPoint(nNextIndex));
419                     const double fEdgeLength(B3DVector(aNextPoint - aCurrentPoint).getLength());
420 
421                     while(fTools::less(fDotDashMovingLength, fEdgeLength))
422                     {
423                         // new split is inside edge, create and append snippet [fLastDotDashMovingLength, fDotDashMovingLength]
424                         const bool bHandleLine(bIsLine && pLineTarget);
425                         const bool bHandleGap(!bIsLine && pGapTarget);
426 
427                         if(bHandleLine || bHandleGap)
428                         {
429                             if(!aSnippet.count())
430                             {
431                                 aSnippet.append(interpolate(aCurrentPoint, aNextPoint, fLastDotDashMovingLength / fEdgeLength));
432                             }
433 
434                             aSnippet.append(interpolate(aCurrentPoint, aNextPoint, fDotDashMovingLength / fEdgeLength));
435 
436                             if(bHandleLine)
437                             {
438                                 pLineTarget->append(aSnippet);
439                             }
440                             else
441                             {
442                                 pGapTarget->append(aSnippet);
443                             }
444 
445                             aSnippet.clear();
446                         }
447 
448                         // prepare next DotDashArray step and flip line/gap flag
449                         fLastDotDashMovingLength = fDotDashMovingLength;
450                         fDotDashMovingLength += rDotDashArray[(++nDotDashIndex) % nDotDashCount];
451                         bIsLine = !bIsLine;
452                     }
453 
454                     // append snippet [fLastDotDashMovingLength, fEdgeLength]
455                     const bool bHandleLine(bIsLine && pLineTarget);
456                     const bool bHandleGap(!bIsLine && pGapTarget);
457 
458                     if(bHandleLine || bHandleGap)
459                     {
460                         if(!aSnippet.count())
461                         {
462                             aSnippet.append(interpolate(aCurrentPoint, aNextPoint, fLastDotDashMovingLength / fEdgeLength));
463                         }
464 
465                         aSnippet.append(aNextPoint);
466                     }
467 
468                     // prepare move to next edge
469                     fDotDashMovingLength -= fEdgeLength;
470 
471                     // prepare next edge step (end point gets new start point)
472                     aCurrentPoint = aNextPoint;
473                 }
474 
475                 // append last intermediate results (if exists)
476                 if(aSnippet.count())
477                 {
478                     if(bIsLine && pLineTarget)
479                     {
480                         pLineTarget->append(aSnippet);
481                     }
482                     else if(!bIsLine && pGapTarget)
483                     {
484                         pGapTarget->append(aSnippet);
485                     }
486                 }
487 
488                 // check if start and end polygon may be merged
489                 if(pLineTarget)
490                 {
491                     const sal_uInt32 nCount(pLineTarget->count());
492 
493                     if(nCount > 1)
494                     {
495                         // these polygons were created above, there exists none with less than two points,
496                         // thus dircet point access below is allowed
497                         const B3DPolygon aFirst(pLineTarget->getB3DPolygon(0));
498                         B3DPolygon aLast(pLineTarget->getB3DPolygon(nCount - 1));
499 
500                         if(aFirst.getB3DPoint(0).equal(aLast.getB3DPoint(aLast.count() - 1)))
501                         {
502                             // start of first and end of last are the same -> merge them
503                             aLast.append(aFirst);
504                             aLast.removeDoublePoints();
505                             pLineTarget->setB3DPolygon(0, aLast);
506                             pLineTarget->remove(nCount - 1);
507                         }
508                     }
509                 }
510 
511                 if(pGapTarget)
512                 {
513                     const sal_uInt32 nCount(pGapTarget->count());
514 
515                     if(nCount > 1)
516                     {
517                         // these polygons were created above, there exists none with less than two points,
518                         // thus dircet point access below is allowed
519                         const B3DPolygon aFirst(pGapTarget->getB3DPolygon(0));
520                         B3DPolygon aLast(pGapTarget->getB3DPolygon(nCount - 1));
521 
522                         if(aFirst.getB3DPoint(0).equal(aLast.getB3DPoint(aLast.count() - 1)))
523                         {
524                             // start of first and end of last are the same -> merge them
525                             aLast.append(aFirst);
526                             aLast.removeDoublePoints();
527                             pGapTarget->setB3DPolygon(0, aLast);
528                             pGapTarget->remove(nCount - 1);
529                         }
530                     }
531                 }
532             }
533             else
534             {
535                 // parameters make no sense, just add source to targets
536                 if(pLineTarget)
537                 {
538                     pLineTarget->append(rCandidate);
539                 }
540 
541                 if(pGapTarget)
542                 {
543                     pGapTarget->append(rCandidate);
544                 }
545             }
546         }
547 
548         B3DPolygon applyDefaultNormalsSphere( const B3DPolygon& rCandidate, const B3DPoint& rCenter)
549         {
550             B3DPolygon aRetval(rCandidate);
551 
552             for(sal_uInt32 a(0L); a < aRetval.count(); a++)
553             {
554                 B3DVector aVector(aRetval.getB3DPoint(a) - rCenter);
555                 aVector.normalize();
556                 aRetval.setNormal(a, aVector);
557             }
558 
559             return aRetval;
560         }
561 
562         B3DPolygon invertNormals( const B3DPolygon& rCandidate)
563         {
564             B3DPolygon aRetval(rCandidate);
565 
566             if(aRetval.areNormalsUsed())
567             {
568                 for(sal_uInt32 a(0L); a < aRetval.count(); a++)
569                 {
570                     aRetval.setNormal(a, -aRetval.getNormal(a));
571                 }
572             }
573 
574             return aRetval;
575         }
576 
577         B3DPolygon applyDefaultTextureCoordinatesParallel( const B3DPolygon& rCandidate, const B3DRange& rRange, bool bChangeX, bool bChangeY)
578         {
579             B3DPolygon aRetval(rCandidate);
580 
581             if(bChangeX || bChangeY)
582             {
583                 // create projection of standard texture coordinates in (X, Y) onto
584                 // the 3d coordinates straight
585                 const double fWidth(rRange.getWidth());
586                 const double fHeight(rRange.getHeight());
587                 const bool bWidthSet(!fTools::equalZero(fWidth));
588                 const bool bHeightSet(!fTools::equalZero(fHeight));
589                 const double fOne(1.0);
590 
591                 for(sal_uInt32 a(0L); a < aRetval.count(); a++)
592                 {
593                     const B3DPoint aPoint(aRetval.getB3DPoint(a));
594                     B2DPoint aTextureCoordinate(aRetval.getTextureCoordinate(a));
595 
596                     if(bChangeX)
597                     {
598                         if(bWidthSet)
599                         {
600                             aTextureCoordinate.setX((aPoint.getX() - rRange.getMinX()) / fWidth);
601                         }
602                         else
603                         {
604                             aTextureCoordinate.setX(0.0);
605                         }
606                     }
607 
608                     if(bChangeY)
609                     {
610                         if(bHeightSet)
611                         {
612                             aTextureCoordinate.setY(fOne - ((aPoint.getY() - rRange.getMinY()) / fHeight));
613                         }
614                         else
615                         {
616                             aTextureCoordinate.setY(fOne);
617                         }
618                     }
619 
620                     aRetval.setTextureCoordinate(a, aTextureCoordinate);
621                 }
622             }
623 
624             return aRetval;
625         }
626 
627         B3DPolygon applyDefaultTextureCoordinatesSphere( const B3DPolygon& rCandidate, const B3DPoint& rCenter, bool bChangeX, bool bChangeY)
628         {
629             B3DPolygon aRetval(rCandidate);
630 
631             if(bChangeX || bChangeY)
632             {
633                 // create texture coordinates using sphere projection to cartesian coordinates,
634                 // use object's center as base
635                 const double fOne(1.0);
636                 const sal_uInt32 nPointCount(aRetval.count());
637                 bool bPolarPoints(false);
638                 sal_uInt32 a;
639 
640                 // create center cartesian coordinates to have a possibility to decide if on boundary
641                 // transitions which value to choose
642                 const B3DRange aPlaneRange(getRange(rCandidate));
643                 const B3DPoint aPlaneCenter(aPlaneRange.getCenter() - rCenter);
644                 const double fXCenter(fOne - ((atan2(aPlaneCenter.getZ(), aPlaneCenter.getX()) + F_PI) / F_2PI));
645 
646                 for(a = 0L; a < nPointCount; a++)
647                 {
648                     const B3DVector aVector(aRetval.getB3DPoint(a) - rCenter);
649                     const double fY(fOne - ((atan2(aVector.getY(), aVector.getXZLength()) + F_PI2) / F_PI));
650                     B2DPoint aTexCoor(aRetval.getTextureCoordinate(a));
651 
652                     if(fTools::equalZero(fY))
653                     {
654                         // point is a north polar point, no useful X-coordinate can be created.
655                         if(bChangeY)
656                         {
657                             aTexCoor.setY(0.0);
658 
659                             if(bChangeX)
660                             {
661                                 bPolarPoints = true;
662                             }
663                         }
664                     }
665                     else if(fTools::equal(fY, fOne))
666                     {
667                         // point is a south polar point, no useful X-coordinate can be created. Set
668                         // Y-coordinte, though
669                         if(bChangeY)
670                         {
671                             aTexCoor.setY(fOne);
672 
673                             if(bChangeX)
674                             {
675                                 bPolarPoints = true;
676                             }
677                         }
678                     }
679                     else
680                     {
681                         double fX(fOne - ((atan2(aVector.getZ(), aVector.getX()) + F_PI) / F_2PI));
682 
683                         // correct cartesinan point coordiante dependent from center value
684                         if(fX > fXCenter + 0.5)
685                         {
686                             fX -= fOne;
687                         }
688                         else if(fX < fXCenter - 0.5)
689                         {
690                             fX += fOne;
691                         }
692 
693                         if(bChangeX)
694                         {
695                             aTexCoor.setX(fX);
696                         }
697 
698                         if(bChangeY)
699                         {
700                             aTexCoor.setY(fY);
701                         }
702                     }
703 
704                     aRetval.setTextureCoordinate(a, aTexCoor);
705                 }
706 
707                 if(bPolarPoints)
708                 {
709                     // correct X-texture coordinates if polar points are contained. Those
710                     // coordinates cannot be correct, so use prev or next X-coordinate
711                     for(a = 0L; a < nPointCount; a++)
712                     {
713                         B2DPoint aTexCoor(aRetval.getTextureCoordinate(a));
714 
715                         if(fTools::equalZero(aTexCoor.getY()) || fTools::equal(aTexCoor.getY(), fOne))
716                         {
717                             // get prev, next TexCoor and test for pole
718                             const B2DPoint aPrevTexCoor(aRetval.getTextureCoordinate(a ? a - 1L : nPointCount - 1L));
719                             const B2DPoint aNextTexCoor(aRetval.getTextureCoordinate((a + 1L) % nPointCount));
720                             const bool bPrevPole(fTools::equalZero(aPrevTexCoor.getY()) || fTools::equal(aPrevTexCoor.getY(), fOne));
721                             const bool bNextPole(fTools::equalZero(aNextTexCoor.getY()) || fTools::equal(aNextTexCoor.getY(), fOne));
722 
723                             if(!bPrevPole && !bNextPole)
724                             {
725                                 // both no poles, mix them
726                                 aTexCoor.setX((aPrevTexCoor.getX() + aNextTexCoor.getX()) / 2.0);
727                             }
728                             else if(!bNextPole)
729                             {
730                                 // copy next
731                                 aTexCoor.setX(aNextTexCoor.getX());
732                             }
733                             else
734                             {
735                                 // copy prev, even if it's a pole, hopefully it is already corrected
736                                 aTexCoor.setX(aPrevTexCoor.getX());
737                             }
738 
739                             aRetval.setTextureCoordinate(a, aTexCoor);
740                         }
741                     }
742                 }
743             }
744 
745             return aRetval;
746         }
747 
748         bool isInEpsilonRange(const B3DPoint& rEdgeStart, const B3DPoint& rEdgeEnd, const B3DPoint& rTestPosition, double fDistance)
749         {
750             // build edge vector
751             const B3DVector aEdge(rEdgeEnd - rEdgeStart);
752             bool bDoDistanceTestStart(false);
753             bool bDoDistanceTestEnd(false);
754 
755             if(aEdge.equalZero())
756             {
757                 // no edge, just a point. Do one of the distance tests.
758                 bDoDistanceTestStart = true;
759             }
760             else
761             {
762                 // calculate fCut in aEdge
763                 const B3DVector aTestEdge(rTestPosition - rEdgeStart);
764                 const double fScalarTestEdge(aEdge.scalar(aTestEdge));
765                 const double fScalarStartEdge(aEdge.scalar(rEdgeStart));
766                 const double fScalarEdge(aEdge.scalar(aEdge));
767                 const double fCut((fScalarTestEdge - fScalarStartEdge) / fScalarEdge);
768                 const double fZero(0.0);
769                 const double fOne(1.0);
770 
771                 if(fTools::less(fCut, fZero))
772                 {
773                     // left of rEdgeStart
774                     bDoDistanceTestStart = true;
775                 }
776                 else if(fTools::more(fCut, fOne))
777                 {
778                     // right of rEdgeEnd
779                     bDoDistanceTestEnd = true;
780                 }
781                 else
782                 {
783                     // inside line [0.0 .. 1.0]
784                     const B3DPoint aCutPoint(interpolate(rEdgeStart, rEdgeEnd, fCut));
785                     const B3DVector aDelta(rTestPosition - aCutPoint);
786                     const double fDistanceSquare(aDelta.scalar(aDelta));
787 
788                     if(fDistanceSquare <= fDistance * fDistance * fDistance)
789                     {
790                         return true;
791                     }
792                     else
793                     {
794                         return false;
795                     }
796                 }
797             }
798 
799             if(bDoDistanceTestStart)
800             {
801                 const B3DVector aDelta(rTestPosition - rEdgeStart);
802                 const double fDistanceSquare(aDelta.scalar(aDelta));
803 
804                 if(fDistanceSquare <= fDistance * fDistance * fDistance)
805                 {
806                     return true;
807                 }
808             }
809             else if(bDoDistanceTestEnd)
810             {
811                 const B3DVector aDelta(rTestPosition - rEdgeEnd);
812                 const double fDistanceSquare(aDelta.scalar(aDelta));
813 
814                 if(fDistanceSquare <= fDistance * fDistance * fDistance)
815                 {
816                     return true;
817                 }
818             }
819 
820             return false;
821         }
822 
823         bool isInEpsilonRange(const B3DPolygon& rCandidate, const B3DPoint& rTestPosition, double fDistance)
824         {
825             const sal_uInt32 nPointCount(rCandidate.count());
826 
827             if(nPointCount)
828             {
829                 const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1L);
830                 B3DPoint aCurrent(rCandidate.getB3DPoint(0));
831 
832                 if(nEdgeCount)
833                 {
834                     // edges
835                     for(sal_uInt32 a(0); a < nEdgeCount; a++)
836                     {
837                         const sal_uInt32 nNextIndex((a + 1) % nPointCount);
838                         const B3DPoint aNext(rCandidate.getB3DPoint(nNextIndex));
839 
840                         if(isInEpsilonRange(aCurrent, aNext, rTestPosition, fDistance))
841                         {
842                             return true;
843                         }
844 
845                         // prepare next step
846                         aCurrent = aNext;
847                     }
848                 }
849                 else
850                 {
851                     // no edges, but points -> not closed. Check single point. Just
852                     // use isInEpsilonRange with twice the same point, it handles those well
853                     if(isInEpsilonRange(aCurrent, aCurrent, rTestPosition, fDistance))
854                     {
855                         return true;
856                     }
857                 }
858             }
859 
860             return false;
861         }
862 
863         bool isInside(const B3DPolygon& rCandidate, const B3DPoint& rPoint, bool bWithBorder)
864         {
865             if(bWithBorder && isPointOnPolygon(rCandidate, rPoint, true))
866             {
867                 return true;
868             }
869             else
870             {
871                 bool bRetval(false);
872                 const B3DVector aPlaneNormal(rCandidate.getNormal());
873 
874                 if(!aPlaneNormal.equalZero())
875                 {
876                     const sal_uInt32 nPointCount(rCandidate.count());
877 
878                     if(nPointCount)
879                     {
880                         B3DPoint aCurrentPoint(rCandidate.getB3DPoint(nPointCount - 1));
881                         const double fAbsX(fabs(aPlaneNormal.getX()));
882                         const double fAbsY(fabs(aPlaneNormal.getY()));
883                         const double fAbsZ(fabs(aPlaneNormal.getZ()));
884 
885                         if(fAbsX > fAbsY && fAbsX > fAbsZ)
886                         {
887                             // normal points mostly in X-Direction, use YZ-Polygon projection for check
888                             // x -> y, y -> z
889                             for(sal_uInt32 a(0); a < nPointCount; a++)
890                             {
891                                 const B3DPoint aPreviousPoint(aCurrentPoint);
892                                 aCurrentPoint = rCandidate.getB3DPoint(a);
893 
894                                 // cross-over in Z?
895                                 const bool bCompZA(fTools::more(aPreviousPoint.getZ(), rPoint.getZ()));
896                                 const bool bCompZB(fTools::more(aCurrentPoint.getZ(), rPoint.getZ()));
897 
898                                 if(bCompZA != bCompZB)
899                                 {
900                                     // cross-over in Y?
901                                     const bool bCompYA(fTools::more(aPreviousPoint.getY(), rPoint.getY()));
902                                     const bool bCompYB(fTools::more(aCurrentPoint.getY(), rPoint.getY()));
903 
904                                     if(bCompYA == bCompYB)
905                                     {
906                                         if(bCompYA)
907                                         {
908                                             bRetval = !bRetval;
909                                         }
910                                     }
911                                     else
912                                     {
913                                         const double fCompare(
914                                             aCurrentPoint.getY() - (aCurrentPoint.getZ() - rPoint.getZ()) *
915                                             (aPreviousPoint.getY() - aCurrentPoint.getY()) /
916                                             (aPreviousPoint.getZ() - aCurrentPoint.getZ()));
917 
918                                         if(fTools::more(fCompare, rPoint.getY()))
919                                         {
920                                             bRetval = !bRetval;
921                                         }
922                                     }
923                                 }
924                             }
925                         }
926                         else if(fAbsY > fAbsX && fAbsY > fAbsZ)
927                         {
928                             // normal points mostly in Y-Direction, use XZ-Polygon projection for check
929                             // x -> x, y -> z
930                             for(sal_uInt32 a(0); a < nPointCount; a++)
931                             {
932                                 const B3DPoint aPreviousPoint(aCurrentPoint);
933                                 aCurrentPoint = rCandidate.getB3DPoint(a);
934 
935                                 // cross-over in Z?
936                                 const bool bCompZA(fTools::more(aPreviousPoint.getZ(), rPoint.getZ()));
937                                 const bool bCompZB(fTools::more(aCurrentPoint.getZ(), rPoint.getZ()));
938 
939                                 if(bCompZA != bCompZB)
940                                 {
941                                     // cross-over in X?
942                                     const bool bCompXA(fTools::more(aPreviousPoint.getX(), rPoint.getX()));
943                                     const bool bCompXB(fTools::more(aCurrentPoint.getX(), rPoint.getX()));
944 
945                                     if(bCompXA == bCompXB)
946                                     {
947                                         if(bCompXA)
948                                         {
949                                             bRetval = !bRetval;
950                                         }
951                                     }
952                                     else
953                                     {
954                                         const double fCompare(
955                                             aCurrentPoint.getX() - (aCurrentPoint.getZ() - rPoint.getZ()) *
956                                             (aPreviousPoint.getX() - aCurrentPoint.getX()) /
957                                             (aPreviousPoint.getZ() - aCurrentPoint.getZ()));
958 
959                                         if(fTools::more(fCompare, rPoint.getX()))
960                                         {
961                                             bRetval = !bRetval;
962                                         }
963                                     }
964                                 }
965                             }
966                         }
967                         else
968                         {
969                             // normal points mostly in Z-Direction, use XY-Polygon projection for check
970                             // x -> x, y -> y
971                             for(sal_uInt32 a(0); a < nPointCount; a++)
972                             {
973                                 const B3DPoint aPreviousPoint(aCurrentPoint);
974                                 aCurrentPoint = rCandidate.getB3DPoint(a);
975 
976                                 // cross-over in Y?
977                                 const bool bCompYA(fTools::more(aPreviousPoint.getY(), rPoint.getY()));
978                                 const bool bCompYB(fTools::more(aCurrentPoint.getY(), rPoint.getY()));
979 
980                                 if(bCompYA != bCompYB)
981                                 {
982                                     // cross-over in X?
983                                     const bool bCompXA(fTools::more(aPreviousPoint.getX(), rPoint.getX()));
984                                     const bool bCompXB(fTools::more(aCurrentPoint.getX(), rPoint.getX()));
985 
986                                     if(bCompXA == bCompXB)
987                                     {
988                                         if(bCompXA)
989                                         {
990                                             bRetval = !bRetval;
991                                         }
992                                     }
993                                     else
994                                     {
995                                         const double fCompare(
996                                             aCurrentPoint.getX() - (aCurrentPoint.getY() - rPoint.getY()) *
997                                             (aPreviousPoint.getX() - aCurrentPoint.getX()) /
998                                             (aPreviousPoint.getY() - aCurrentPoint.getY()));
999 
1000                                         if(fTools::more(fCompare, rPoint.getX()))
1001                                         {
1002                                             bRetval = !bRetval;
1003                                         }
1004                                     }
1005                                 }
1006                             }
1007                         }
1008                     }
1009                 }
1010 
1011                 return bRetval;
1012             }
1013         }
1014 
1015         bool isInside(const B3DPolygon& rCandidate, const B3DPolygon& rPolygon, bool bWithBorder)
1016         {
1017             const sal_uInt32 nPointCount(rPolygon.count());
1018 
1019             for(sal_uInt32 a(0L); a < nPointCount; a++)
1020             {
1021                 const B3DPoint aTestPoint(rPolygon.getB3DPoint(a));
1022 
1023                 if(!isInside(rCandidate, aTestPoint, bWithBorder))
1024                 {
1025                     return false;
1026                 }
1027             }
1028 
1029             return true;
1030         }
1031 
1032         bool isPointOnLine(const B3DPoint& rStart, const B3DPoint& rEnd, const B3DPoint& rCandidate, bool bWithPoints)
1033         {
1034             if(rCandidate.equal(rStart) || rCandidate.equal(rEnd))
1035             {
1036                 // candidate is in epsilon around start or end -> inside
1037                 return bWithPoints;
1038             }
1039             else if(rStart.equal(rEnd))
1040             {
1041                 // start and end are equal, but candidate is outside their epsilon -> outside
1042                 return false;
1043             }
1044             else
1045             {
1046                 const B3DVector aEdgeVector(rEnd - rStart);
1047                 const B3DVector aTestVector(rCandidate - rStart);
1048 
1049                 if(areParallel(aEdgeVector, aTestVector))
1050                 {
1051                     const double fZero(0.0);
1052                     const double fOne(1.0);
1053                     double fParamTestOnCurr(0.0);
1054 
1055                     if(aEdgeVector.getX() > aEdgeVector.getY())
1056                     {
1057                         if(aEdgeVector.getX() > aEdgeVector.getZ())
1058                         {
1059                             // X is biggest
1060                             fParamTestOnCurr = aTestVector.getX() / aEdgeVector.getX();
1061                         }
1062                         else
1063                         {
1064                             // Z is biggest
1065                             fParamTestOnCurr = aTestVector.getZ() / aEdgeVector.getZ();
1066                         }
1067                     }
1068                     else
1069                     {
1070                         if(aEdgeVector.getY() > aEdgeVector.getZ())
1071                         {
1072                             // Y is biggest
1073                             fParamTestOnCurr = aTestVector.getY() / aEdgeVector.getY();
1074                         }
1075                         else
1076                         {
1077                             // Z is biggest
1078                             fParamTestOnCurr = aTestVector.getZ() / aEdgeVector.getZ();
1079                         }
1080                     }
1081 
1082                     if(fTools::more(fParamTestOnCurr, fZero) && fTools::less(fParamTestOnCurr, fOne))
1083                     {
1084                         return true;
1085                     }
1086                 }
1087 
1088                 return false;
1089             }
1090         }
1091 
1092         bool isPointOnPolygon(const B3DPolygon& rCandidate, const B3DPoint& rPoint, bool bWithPoints)
1093         {
1094             const sal_uInt32 nPointCount(rCandidate.count());
1095 
1096             if(nPointCount > 1L)
1097             {
1098                 const sal_uInt32 nLoopCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1L);
1099                 B3DPoint aCurrentPoint(rCandidate.getB3DPoint(0));
1100 
1101                 for(sal_uInt32 a(0); a < nLoopCount; a++)
1102                 {
1103                     const B3DPoint aNextPoint(rCandidate.getB3DPoint((a + 1) % nPointCount));
1104 
1105                     if(isPointOnLine(aCurrentPoint, aNextPoint, rPoint, bWithPoints))
1106                     {
1107                         return true;
1108                     }
1109 
1110                     aCurrentPoint = aNextPoint;
1111                 }
1112             }
1113             else if(nPointCount && bWithPoints)
1114             {
1115                 return rPoint.equal(rCandidate.getB3DPoint(0));
1116             }
1117 
1118             return false;
1119         }
1120 
1121         bool getCutBetweenLineAndPlane(const B3DVector& rPlaneNormal, const B3DPoint& rPlanePoint, const B3DPoint& rEdgeStart, const B3DPoint& rEdgeEnd, double& fCut)
1122         {
1123             if(!rPlaneNormal.equalZero() && !rEdgeStart.equal(rEdgeEnd))
1124             {
1125                 const B3DVector aTestEdge(rEdgeEnd - rEdgeStart);
1126                 const double fScalarEdge(rPlaneNormal.scalar(aTestEdge));
1127 
1128                 if(!fTools::equalZero(fScalarEdge))
1129                 {
1130                     const B3DVector aCompareEdge(rPlanePoint - rEdgeStart);
1131                     const double fScalarCompare(rPlaneNormal.scalar(aCompareEdge));
1132 
1133                     fCut = fScalarCompare / fScalarEdge;
1134                     return true;
1135                 }
1136             }
1137 
1138             return false;
1139         }
1140 
1141         bool getCutBetweenLineAndPolygon(const B3DPolygon& rCandidate, const B3DPoint& rEdgeStart, const B3DPoint& rEdgeEnd, double& fCut)
1142         {
1143             const sal_uInt32 nPointCount(rCandidate.count());
1144 
1145             if(nPointCount > 2 && !rEdgeStart.equal(rEdgeEnd))
1146             {
1147                 const B3DVector aPlaneNormal(rCandidate.getNormal());
1148 
1149                 if(!aPlaneNormal.equalZero())
1150                 {
1151                     const B3DPoint aPointOnPlane(rCandidate.getB3DPoint(0));
1152 
1153                     return getCutBetweenLineAndPlane(aPlaneNormal, aPointOnPlane, rEdgeStart, rEdgeEnd, fCut);
1154                 }
1155             }
1156 
1157             return false;
1158         }
1159 
1160         //////////////////////////////////////////////////////////////////////
1161         // comparators with tolerance for 3D Polygons
1162 
1163         bool equal(const B3DPolygon& rCandidateA, const B3DPolygon& rCandidateB, const double& rfSmallValue)
1164         {
1165             const sal_uInt32 nPointCount(rCandidateA.count());
1166 
1167             if(nPointCount != rCandidateB.count())
1168                 return false;
1169 
1170             const bool bClosed(rCandidateA.isClosed());
1171 
1172             if(bClosed != rCandidateB.isClosed())
1173                 return false;
1174 
1175             for(sal_uInt32 a(0); a < nPointCount; a++)
1176             {
1177                 const B3DPoint aPoint(rCandidateA.getB3DPoint(a));
1178 
1179                 if(!aPoint.equal(rCandidateB.getB3DPoint(a), rfSmallValue))
1180                     return false;
1181             }
1182 
1183             return true;
1184         }
1185 
1186         bool equal(const B3DPolygon& rCandidateA, const B3DPolygon& rCandidateB)
1187         {
1188             const double fSmallValue(fTools::getSmallValue());
1189 
1190             return equal(rCandidateA, rCandidateB, fSmallValue);
1191         }
1192 
1193         // snap points of horizontal or vertical edges to discrete values
1194         B3DPolygon snapPointsOfHorizontalOrVerticalEdges(const B3DPolygon& rCandidate)
1195         {
1196             const sal_uInt32 nPointCount(rCandidate.count());
1197 
1198             if(nPointCount > 1)
1199             {
1200                 // Start by copying the source polygon to get a writeable copy. The closed state is
1201                 // copied by aRetval's initialisation, too, so no need to copy it in this method
1202                 B3DPolygon aRetval(rCandidate);
1203 
1204                 // prepare geometry data. Get rounded from original
1205                 B3ITuple aPrevTuple(basegfx::fround(rCandidate.getB3DPoint(nPointCount - 1)));
1206                 B3DPoint aCurrPoint(rCandidate.getB3DPoint(0));
1207                 B3ITuple aCurrTuple(basegfx::fround(aCurrPoint));
1208 
1209                 // loop over all points. This will also snap the implicit closing edge
1210                 // even when not closed, but that's no problem here
1211                 for(sal_uInt32 a(0); a < nPointCount; a++)
1212                 {
1213                     // get next point. Get rounded from original
1214                     const bool bLastRun(a + 1 == nPointCount);
1215                     const sal_uInt32 nNextIndex(bLastRun ? 0 : a + 1);
1216                     const B3DPoint aNextPoint(rCandidate.getB3DPoint(nNextIndex));
1217                     const B3ITuple aNextTuple(basegfx::fround(aNextPoint));
1218 
1219                     // get the states
1220                     const bool bPrevVertical(aPrevTuple.getX() == aCurrTuple.getX());
1221                     const bool bNextVertical(aNextTuple.getX() == aCurrTuple.getX());
1222                     const bool bPrevHorizontal(aPrevTuple.getY() == aCurrTuple.getY());
1223                     const bool bNextHorizontal(aNextTuple.getY() == aCurrTuple.getY());
1224                     const bool bSnapX(bPrevVertical || bNextVertical);
1225                     const bool bSnapY(bPrevHorizontal || bNextHorizontal);
1226 
1227                     if(bSnapX || bSnapY)
1228                     {
1229                         const B3DPoint aSnappedPoint(
1230                             bSnapX ? aCurrTuple.getX() : aCurrPoint.getX(),
1231                             bSnapY ? aCurrTuple.getY() : aCurrPoint.getY(),
1232                             aCurrPoint.getZ());
1233 
1234                         aRetval.setB3DPoint(a, aSnappedPoint);
1235                     }
1236 
1237                     // prepare next point
1238                     if(!bLastRun)
1239                     {
1240                         aPrevTuple = aCurrTuple;
1241                         aCurrPoint = aNextPoint;
1242                         aCurrTuple = aNextTuple;
1243                     }
1244                 }
1245 
1246                 return aRetval;
1247             }
1248             else
1249             {
1250                 return rCandidate;
1251             }
1252         }
1253 
1254     } // end of namespace tools
1255 } // end of namespace basegfx
1256 
1257 //////////////////////////////////////////////////////////////////////////////
1258 
1259 // eof
1260