xref: /AOO41X/main/basegfx/source/polygon/b3dpolygontools.cxx (revision 5aaf853b3ba91aa8a4f8154519fb0bf086e1a428)
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
checkClosed(B3DPolygon & rCandidate)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.
getIndexOfPredecessor(sal_uInt32 nIndex,const B3DPolygon & rCandidate)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 
getIndexOfSuccessor(sal_uInt32 nIndex,const B3DPolygon & rCandidate)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 
getRange(const B3DPolygon & rCandidate)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 
getNormal(const B3DPolygon & rCandidate)103         B3DVector getNormal(const B3DPolygon& rCandidate)
104         {
105             return rCandidate.getNormal();
106         }
107 
getPositiveOrientedNormal(const B3DPolygon & rCandidate)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 
getOrientation(const B3DPolygon & rCandidate)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 
getSignedArea(const B3DPolygon & rCandidate)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 
getArea(const B3DPolygon & rCandidate)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 
getEdgeLength(const B3DPolygon & rCandidate,sal_uInt32 nIndex)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 
getLength(const B3DPolygon & rCandidate)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 
getPositionAbsolute(const B3DPolygon & rCandidate,double fDistance,double fLength)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 
getPositionRelative(const B3DPolygon & rCandidate,double fDistance,double fLength)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 
applyLineDashing(const B3DPolygon & rCandidate,const::std::vector<double> & rDotDashArray,B3DPolyPolygon * pLineTarget,B3DPolyPolygon * pGapTarget,double fDotDashLength)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                     if(!fTools::equalZero(fEdgeLength))
422                     {
423                         while(fTools::less(fDotDashMovingLength, fEdgeLength))
424                         {
425                             // new split is inside edge, create and append snippet [fLastDotDashMovingLength, fDotDashMovingLength]
426                             const bool bHandleLine(bIsLine && pLineTarget);
427                             const bool bHandleGap(!bIsLine && pGapTarget);
428 
429                             if(bHandleLine || bHandleGap)
430                             {
431                                 if(!aSnippet.count())
432                                 {
433                                     aSnippet.append(interpolate(aCurrentPoint, aNextPoint, fLastDotDashMovingLength / fEdgeLength));
434                                 }
435 
436                                 aSnippet.append(interpolate(aCurrentPoint, aNextPoint, fDotDashMovingLength / fEdgeLength));
437 
438                                 if(bHandleLine)
439                                 {
440                                     pLineTarget->append(aSnippet);
441                                 }
442                                 else
443                                 {
444                                     pGapTarget->append(aSnippet);
445                                 }
446 
447                                 aSnippet.clear();
448                             }
449 
450                             // prepare next DotDashArray step and flip line/gap flag
451                             fLastDotDashMovingLength = fDotDashMovingLength;
452                             fDotDashMovingLength += rDotDashArray[(++nDotDashIndex) % nDotDashCount];
453                             bIsLine = !bIsLine;
454                         }
455 
456                         // append snippet [fLastDotDashMovingLength, fEdgeLength]
457                         const bool bHandleLine(bIsLine && pLineTarget);
458                         const bool bHandleGap(!bIsLine && pGapTarget);
459 
460                         if(bHandleLine || bHandleGap)
461                         {
462                             if(!aSnippet.count())
463                             {
464                                 aSnippet.append(interpolate(aCurrentPoint, aNextPoint, fLastDotDashMovingLength / fEdgeLength));
465                             }
466 
467                             aSnippet.append(aNextPoint);
468                         }
469 
470                         // prepare move to next edge
471                         fDotDashMovingLength -= fEdgeLength;
472                     }
473 
474                     // prepare next edge step (end point gets new start point)
475                     aCurrentPoint = aNextPoint;
476                 }
477 
478                 // append last intermediate results (if exists)
479                 if(aSnippet.count())
480                 {
481                     if(bIsLine && pLineTarget)
482                     {
483                         pLineTarget->append(aSnippet);
484                     }
485                     else if(!bIsLine && pGapTarget)
486                     {
487                         pGapTarget->append(aSnippet);
488                     }
489                 }
490 
491                 // check if start and end polygon may be merged
492                 if(pLineTarget)
493                 {
494                     const sal_uInt32 nCount(pLineTarget->count());
495 
496                     if(nCount > 1)
497                     {
498                         // these polygons were created above, there exists none with less than two points,
499                         // thus dircet point access below is allowed
500                         const B3DPolygon aFirst(pLineTarget->getB3DPolygon(0));
501                         B3DPolygon aLast(pLineTarget->getB3DPolygon(nCount - 1));
502 
503                         if(aFirst.getB3DPoint(0).equal(aLast.getB3DPoint(aLast.count() - 1)))
504                         {
505                             // start of first and end of last are the same -> merge them
506                             aLast.append(aFirst);
507                             aLast.removeDoublePoints();
508                             pLineTarget->setB3DPolygon(0, aLast);
509                             pLineTarget->remove(nCount - 1);
510                         }
511                     }
512                 }
513 
514                 if(pGapTarget)
515                 {
516                     const sal_uInt32 nCount(pGapTarget->count());
517 
518                     if(nCount > 1)
519                     {
520                         // these polygons were created above, there exists none with less than two points,
521                         // thus dircet point access below is allowed
522                         const B3DPolygon aFirst(pGapTarget->getB3DPolygon(0));
523                         B3DPolygon aLast(pGapTarget->getB3DPolygon(nCount - 1));
524 
525                         if(aFirst.getB3DPoint(0).equal(aLast.getB3DPoint(aLast.count() - 1)))
526                         {
527                             // start of first and end of last are the same -> merge them
528                             aLast.append(aFirst);
529                             aLast.removeDoublePoints();
530                             pGapTarget->setB3DPolygon(0, aLast);
531                             pGapTarget->remove(nCount - 1);
532                         }
533                     }
534                 }
535             }
536             else
537             {
538                 // parameters make no sense, just add source to targets
539                 if(pLineTarget)
540                 {
541                     pLineTarget->append(rCandidate);
542                 }
543 
544                 if(pGapTarget)
545                 {
546                     pGapTarget->append(rCandidate);
547                 }
548             }
549         }
550 
applyDefaultNormalsSphere(const B3DPolygon & rCandidate,const B3DPoint & rCenter)551         B3DPolygon applyDefaultNormalsSphere( const B3DPolygon& rCandidate, const B3DPoint& rCenter)
552         {
553             B3DPolygon aRetval(rCandidate);
554 
555             for(sal_uInt32 a(0L); a < aRetval.count(); a++)
556             {
557                 B3DVector aVector(aRetval.getB3DPoint(a) - rCenter);
558                 aVector.normalize();
559                 aRetval.setNormal(a, aVector);
560             }
561 
562             return aRetval;
563         }
564 
invertNormals(const B3DPolygon & rCandidate)565         B3DPolygon invertNormals( const B3DPolygon& rCandidate)
566         {
567             B3DPolygon aRetval(rCandidate);
568 
569             if(aRetval.areNormalsUsed())
570             {
571                 for(sal_uInt32 a(0L); a < aRetval.count(); a++)
572                 {
573                     aRetval.setNormal(a, -aRetval.getNormal(a));
574                 }
575             }
576 
577             return aRetval;
578         }
579 
applyDefaultTextureCoordinatesParallel(const B3DPolygon & rCandidate,const B3DRange & rRange,bool bChangeX,bool bChangeY)580         B3DPolygon applyDefaultTextureCoordinatesParallel( const B3DPolygon& rCandidate, const B3DRange& rRange, bool bChangeX, bool bChangeY)
581         {
582             B3DPolygon aRetval(rCandidate);
583 
584             if(bChangeX || bChangeY)
585             {
586                 // create projection of standard texture coordinates in (X, Y) onto
587                 // the 3d coordinates straight
588                 const double fWidth(rRange.getWidth());
589                 const double fHeight(rRange.getHeight());
590                 const bool bWidthSet(!fTools::equalZero(fWidth));
591                 const bool bHeightSet(!fTools::equalZero(fHeight));
592                 const double fOne(1.0);
593 
594                 for(sal_uInt32 a(0L); a < aRetval.count(); a++)
595                 {
596                     const B3DPoint aPoint(aRetval.getB3DPoint(a));
597                     B2DPoint aTextureCoordinate(aRetval.getTextureCoordinate(a));
598 
599                     if(bChangeX)
600                     {
601                         if(bWidthSet)
602                         {
603                             aTextureCoordinate.setX((aPoint.getX() - rRange.getMinX()) / fWidth);
604                         }
605                         else
606                         {
607                             aTextureCoordinate.setX(0.0);
608                         }
609                     }
610 
611                     if(bChangeY)
612                     {
613                         if(bHeightSet)
614                         {
615                             aTextureCoordinate.setY(fOne - ((aPoint.getY() - rRange.getMinY()) / fHeight));
616                         }
617                         else
618                         {
619                             aTextureCoordinate.setY(fOne);
620                         }
621                     }
622 
623                     aRetval.setTextureCoordinate(a, aTextureCoordinate);
624                 }
625             }
626 
627             return aRetval;
628         }
629 
applyDefaultTextureCoordinatesSphere(const B3DPolygon & rCandidate,const B3DPoint & rCenter,bool bChangeX,bool bChangeY)630         B3DPolygon applyDefaultTextureCoordinatesSphere( const B3DPolygon& rCandidate, const B3DPoint& rCenter, bool bChangeX, bool bChangeY)
631         {
632             B3DPolygon aRetval(rCandidate);
633 
634             if(bChangeX || bChangeY)
635             {
636                 // create texture coordinates using sphere projection to cartesian coordinates,
637                 // use object's center as base
638                 const double fOne(1.0);
639                 const sal_uInt32 nPointCount(aRetval.count());
640                 bool bPolarPoints(false);
641                 sal_uInt32 a;
642 
643                 // create center cartesian coordinates to have a possibility to decide if on boundary
644                 // transitions which value to choose
645                 const B3DRange aPlaneRange(getRange(rCandidate));
646                 const B3DPoint aPlaneCenter(aPlaneRange.getCenter() - rCenter);
647                 const double fXCenter(fOne - ((atan2(aPlaneCenter.getZ(), aPlaneCenter.getX()) + F_PI) / F_2PI));
648 
649                 for(a = 0L; a < nPointCount; a++)
650                 {
651                     const B3DVector aVector(aRetval.getB3DPoint(a) - rCenter);
652                     const double fY(fOne - ((atan2(aVector.getY(), aVector.getXZLength()) + F_PI2) / F_PI));
653                     B2DPoint aTexCoor(aRetval.getTextureCoordinate(a));
654 
655                     if(fTools::equalZero(fY))
656                     {
657                         // point is a north polar point, no useful X-coordinate can be created.
658                         if(bChangeY)
659                         {
660                             aTexCoor.setY(0.0);
661 
662                             if(bChangeX)
663                             {
664                                 bPolarPoints = true;
665                             }
666                         }
667                     }
668                     else if(fTools::equal(fY, fOne))
669                     {
670                         // point is a south polar point, no useful X-coordinate can be created. Set
671                         // Y-coordinte, though
672                         if(bChangeY)
673                         {
674                             aTexCoor.setY(fOne);
675 
676                             if(bChangeX)
677                             {
678                                 bPolarPoints = true;
679                             }
680                         }
681                     }
682                     else
683                     {
684                         double fX(fOne - ((atan2(aVector.getZ(), aVector.getX()) + F_PI) / F_2PI));
685 
686                         // correct cartesinan point coordiante dependent from center value
687                         if(fX > fXCenter + 0.5)
688                         {
689                             fX -= fOne;
690                         }
691                         else if(fX < fXCenter - 0.5)
692                         {
693                             fX += fOne;
694                         }
695 
696                         if(bChangeX)
697                         {
698                             aTexCoor.setX(fX);
699                         }
700 
701                         if(bChangeY)
702                         {
703                             aTexCoor.setY(fY);
704                         }
705                     }
706 
707                     aRetval.setTextureCoordinate(a, aTexCoor);
708                 }
709 
710                 if(bPolarPoints)
711                 {
712                     // correct X-texture coordinates if polar points are contained. Those
713                     // coordinates cannot be correct, so use prev or next X-coordinate
714                     for(a = 0L; a < nPointCount; a++)
715                     {
716                         B2DPoint aTexCoor(aRetval.getTextureCoordinate(a));
717 
718                         if(fTools::equalZero(aTexCoor.getY()) || fTools::equal(aTexCoor.getY(), fOne))
719                         {
720                             // get prev, next TexCoor and test for pole
721                             const B2DPoint aPrevTexCoor(aRetval.getTextureCoordinate(a ? a - 1L : nPointCount - 1L));
722                             const B2DPoint aNextTexCoor(aRetval.getTextureCoordinate((a + 1L) % nPointCount));
723                             const bool bPrevPole(fTools::equalZero(aPrevTexCoor.getY()) || fTools::equal(aPrevTexCoor.getY(), fOne));
724                             const bool bNextPole(fTools::equalZero(aNextTexCoor.getY()) || fTools::equal(aNextTexCoor.getY(), fOne));
725 
726                             if(!bPrevPole && !bNextPole)
727                             {
728                                 // both no poles, mix them
729                                 aTexCoor.setX((aPrevTexCoor.getX() + aNextTexCoor.getX()) / 2.0);
730                             }
731                             else if(!bNextPole)
732                             {
733                                 // copy next
734                                 aTexCoor.setX(aNextTexCoor.getX());
735                             }
736                             else
737                             {
738                                 // copy prev, even if it's a pole, hopefully it is already corrected
739                                 aTexCoor.setX(aPrevTexCoor.getX());
740                             }
741 
742                             aRetval.setTextureCoordinate(a, aTexCoor);
743                         }
744                     }
745                 }
746             }
747 
748             return aRetval;
749         }
750 
isInEpsilonRange(const B3DPoint & rEdgeStart,const B3DPoint & rEdgeEnd,const B3DPoint & rTestPosition,double fDistance)751         bool isInEpsilonRange(const B3DPoint& rEdgeStart, const B3DPoint& rEdgeEnd, const B3DPoint& rTestPosition, double fDistance)
752         {
753             // build edge vector
754             const B3DVector aEdge(rEdgeEnd - rEdgeStart);
755             bool bDoDistanceTestStart(false);
756             bool bDoDistanceTestEnd(false);
757 
758             if(aEdge.equalZero())
759             {
760                 // no edge, just a point. Do one of the distance tests.
761                 bDoDistanceTestStart = true;
762             }
763             else
764             {
765                 // calculate fCut in aEdge
766                 const B3DVector aTestEdge(rTestPosition - rEdgeStart);
767                 const double fScalarTestEdge(aEdge.scalar(aTestEdge));
768                 const double fScalarStartEdge(aEdge.scalar(rEdgeStart));
769                 const double fScalarEdge(aEdge.scalar(aEdge));
770                 const double fCut((fScalarTestEdge - fScalarStartEdge) / fScalarEdge);
771                 const double fZero(0.0);
772                 const double fOne(1.0);
773 
774                 if(fTools::less(fCut, fZero))
775                 {
776                     // left of rEdgeStart
777                     bDoDistanceTestStart = true;
778                 }
779                 else if(fTools::more(fCut, fOne))
780                 {
781                     // right of rEdgeEnd
782                     bDoDistanceTestEnd = true;
783                 }
784                 else
785                 {
786                     // inside line [0.0 .. 1.0]
787                     const B3DPoint aCutPoint(interpolate(rEdgeStart, rEdgeEnd, fCut));
788                     const B3DVector aDelta(rTestPosition - aCutPoint);
789                     const double fDistanceSquare(aDelta.scalar(aDelta));
790 
791                     if(fDistanceSquare <= fDistance * fDistance * fDistance)
792                     {
793                         return true;
794                     }
795                     else
796                     {
797                         return false;
798                     }
799                 }
800             }
801 
802             if(bDoDistanceTestStart)
803             {
804                 const B3DVector aDelta(rTestPosition - rEdgeStart);
805                 const double fDistanceSquare(aDelta.scalar(aDelta));
806 
807                 if(fDistanceSquare <= fDistance * fDistance * fDistance)
808                 {
809                     return true;
810                 }
811             }
812             else if(bDoDistanceTestEnd)
813             {
814                 const B3DVector aDelta(rTestPosition - rEdgeEnd);
815                 const double fDistanceSquare(aDelta.scalar(aDelta));
816 
817                 if(fDistanceSquare <= fDistance * fDistance * fDistance)
818                 {
819                     return true;
820                 }
821             }
822 
823             return false;
824         }
825 
isInEpsilonRange(const B3DPolygon & rCandidate,const B3DPoint & rTestPosition,double fDistance)826         bool isInEpsilonRange(const B3DPolygon& rCandidate, const B3DPoint& rTestPosition, double fDistance)
827         {
828             const sal_uInt32 nPointCount(rCandidate.count());
829 
830             if(nPointCount)
831             {
832                 const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1L);
833                 B3DPoint aCurrent(rCandidate.getB3DPoint(0));
834 
835                 if(nEdgeCount)
836                 {
837                     // edges
838                     for(sal_uInt32 a(0); a < nEdgeCount; a++)
839                     {
840                         const sal_uInt32 nNextIndex((a + 1) % nPointCount);
841                         const B3DPoint aNext(rCandidate.getB3DPoint(nNextIndex));
842 
843                         if(isInEpsilonRange(aCurrent, aNext, rTestPosition, fDistance))
844                         {
845                             return true;
846                         }
847 
848                         // prepare next step
849                         aCurrent = aNext;
850                     }
851                 }
852                 else
853                 {
854                     // no edges, but points -> not closed. Check single point. Just
855                     // use isInEpsilonRange with twice the same point, it handles those well
856                     if(isInEpsilonRange(aCurrent, aCurrent, rTestPosition, fDistance))
857                     {
858                         return true;
859                     }
860                 }
861             }
862 
863             return false;
864         }
865 
isInside(const B3DPolygon & rCandidate,const B3DPoint & rPoint,bool bWithBorder)866         bool isInside(const B3DPolygon& rCandidate, const B3DPoint& rPoint, bool bWithBorder)
867         {
868             if(bWithBorder && isPointOnPolygon(rCandidate, rPoint, true))
869             {
870                 return true;
871             }
872             else
873             {
874                 bool bRetval(false);
875                 const B3DVector aPlaneNormal(rCandidate.getNormal());
876 
877                 if(!aPlaneNormal.equalZero())
878                 {
879                     const sal_uInt32 nPointCount(rCandidate.count());
880 
881                     if(nPointCount)
882                     {
883                         B3DPoint aCurrentPoint(rCandidate.getB3DPoint(nPointCount - 1));
884                         const double fAbsX(fabs(aPlaneNormal.getX()));
885                         const double fAbsY(fabs(aPlaneNormal.getY()));
886                         const double fAbsZ(fabs(aPlaneNormal.getZ()));
887 
888                         if(fAbsX > fAbsY && fAbsX > fAbsZ)
889                         {
890                             // normal points mostly in X-Direction, use YZ-Polygon projection for check
891                             // x -> y, y -> z
892                             for(sal_uInt32 a(0); a < nPointCount; a++)
893                             {
894                                 const B3DPoint aPreviousPoint(aCurrentPoint);
895                                 aCurrentPoint = rCandidate.getB3DPoint(a);
896 
897                                 // cross-over in Z?
898                                 const bool bCompZA(fTools::more(aPreviousPoint.getZ(), rPoint.getZ()));
899                                 const bool bCompZB(fTools::more(aCurrentPoint.getZ(), rPoint.getZ()));
900 
901                                 if(bCompZA != bCompZB)
902                                 {
903                                     // cross-over in Y?
904                                     const bool bCompYA(fTools::more(aPreviousPoint.getY(), rPoint.getY()));
905                                     const bool bCompYB(fTools::more(aCurrentPoint.getY(), rPoint.getY()));
906 
907                                     if(bCompYA == bCompYB)
908                                     {
909                                         if(bCompYA)
910                                         {
911                                             bRetval = !bRetval;
912                                         }
913                                     }
914                                     else
915                                     {
916                                         const double fCompare(
917                                             aCurrentPoint.getY() - (aCurrentPoint.getZ() - rPoint.getZ()) *
918                                             (aPreviousPoint.getY() - aCurrentPoint.getY()) /
919                                             (aPreviousPoint.getZ() - aCurrentPoint.getZ()));
920 
921                                         if(fTools::more(fCompare, rPoint.getY()))
922                                         {
923                                             bRetval = !bRetval;
924                                         }
925                                     }
926                                 }
927                             }
928                         }
929                         else if(fAbsY > fAbsX && fAbsY > fAbsZ)
930                         {
931                             // normal points mostly in Y-Direction, use XZ-Polygon projection for check
932                             // x -> x, y -> z
933                             for(sal_uInt32 a(0); a < nPointCount; a++)
934                             {
935                                 const B3DPoint aPreviousPoint(aCurrentPoint);
936                                 aCurrentPoint = rCandidate.getB3DPoint(a);
937 
938                                 // cross-over in Z?
939                                 const bool bCompZA(fTools::more(aPreviousPoint.getZ(), rPoint.getZ()));
940                                 const bool bCompZB(fTools::more(aCurrentPoint.getZ(), rPoint.getZ()));
941 
942                                 if(bCompZA != bCompZB)
943                                 {
944                                     // cross-over in X?
945                                     const bool bCompXA(fTools::more(aPreviousPoint.getX(), rPoint.getX()));
946                                     const bool bCompXB(fTools::more(aCurrentPoint.getX(), rPoint.getX()));
947 
948                                     if(bCompXA == bCompXB)
949                                     {
950                                         if(bCompXA)
951                                         {
952                                             bRetval = !bRetval;
953                                         }
954                                     }
955                                     else
956                                     {
957                                         const double fCompare(
958                                             aCurrentPoint.getX() - (aCurrentPoint.getZ() - rPoint.getZ()) *
959                                             (aPreviousPoint.getX() - aCurrentPoint.getX()) /
960                                             (aPreviousPoint.getZ() - aCurrentPoint.getZ()));
961 
962                                         if(fTools::more(fCompare, rPoint.getX()))
963                                         {
964                                             bRetval = !bRetval;
965                                         }
966                                     }
967                                 }
968                             }
969                         }
970                         else
971                         {
972                             // normal points mostly in Z-Direction, use XY-Polygon projection for check
973                             // x -> x, y -> y
974                             for(sal_uInt32 a(0); a < nPointCount; a++)
975                             {
976                                 const B3DPoint aPreviousPoint(aCurrentPoint);
977                                 aCurrentPoint = rCandidate.getB3DPoint(a);
978 
979                                 // cross-over in Y?
980                                 const bool bCompYA(fTools::more(aPreviousPoint.getY(), rPoint.getY()));
981                                 const bool bCompYB(fTools::more(aCurrentPoint.getY(), rPoint.getY()));
982 
983                                 if(bCompYA != bCompYB)
984                                 {
985                                     // cross-over in X?
986                                     const bool bCompXA(fTools::more(aPreviousPoint.getX(), rPoint.getX()));
987                                     const bool bCompXB(fTools::more(aCurrentPoint.getX(), rPoint.getX()));
988 
989                                     if(bCompXA == bCompXB)
990                                     {
991                                         if(bCompXA)
992                                         {
993                                             bRetval = !bRetval;
994                                         }
995                                     }
996                                     else
997                                     {
998                                         const double fCompare(
999                                             aCurrentPoint.getX() - (aCurrentPoint.getY() - rPoint.getY()) *
1000                                             (aPreviousPoint.getX() - aCurrentPoint.getX()) /
1001                                             (aPreviousPoint.getY() - aCurrentPoint.getY()));
1002 
1003                                         if(fTools::more(fCompare, rPoint.getX()))
1004                                         {
1005                                             bRetval = !bRetval;
1006                                         }
1007                                     }
1008                                 }
1009                             }
1010                         }
1011                     }
1012                 }
1013 
1014                 return bRetval;
1015             }
1016         }
1017 
isInside(const B3DPolygon & rCandidate,const B3DPolygon & rPolygon,bool bWithBorder)1018         bool isInside(const B3DPolygon& rCandidate, const B3DPolygon& rPolygon, bool bWithBorder)
1019         {
1020             const sal_uInt32 nPointCount(rPolygon.count());
1021 
1022             for(sal_uInt32 a(0L); a < nPointCount; a++)
1023             {
1024                 const B3DPoint aTestPoint(rPolygon.getB3DPoint(a));
1025 
1026                 if(!isInside(rCandidate, aTestPoint, bWithBorder))
1027                 {
1028                     return false;
1029                 }
1030             }
1031 
1032             return true;
1033         }
1034 
isPointOnLine(const B3DPoint & rStart,const B3DPoint & rEnd,const B3DPoint & rCandidate,bool bWithPoints)1035         bool isPointOnLine(const B3DPoint& rStart, const B3DPoint& rEnd, const B3DPoint& rCandidate, bool bWithPoints)
1036         {
1037             if(rCandidate.equal(rStart) || rCandidate.equal(rEnd))
1038             {
1039                 // candidate is in epsilon around start or end -> inside
1040                 return bWithPoints;
1041             }
1042             else if(rStart.equal(rEnd))
1043             {
1044                 // start and end are equal, but candidate is outside their epsilon -> outside
1045                 return false;
1046             }
1047             else
1048             {
1049                 const B3DVector aEdgeVector(rEnd - rStart);
1050                 const B3DVector aTestVector(rCandidate - rStart);
1051 
1052                 if(areParallel(aEdgeVector, aTestVector))
1053                 {
1054                     const double fZero(0.0);
1055                     const double fOne(1.0);
1056                     double fParamTestOnCurr(0.0);
1057 
1058                     if(aEdgeVector.getX() > aEdgeVector.getY())
1059                     {
1060                         if(aEdgeVector.getX() > aEdgeVector.getZ())
1061                         {
1062                             // X is biggest
1063                             fParamTestOnCurr = aTestVector.getX() / aEdgeVector.getX();
1064                         }
1065                         else
1066                         {
1067                             // Z is biggest
1068                             fParamTestOnCurr = aTestVector.getZ() / aEdgeVector.getZ();
1069                         }
1070                     }
1071                     else
1072                     {
1073                         if(aEdgeVector.getY() > aEdgeVector.getZ())
1074                         {
1075                             // Y is biggest
1076                             fParamTestOnCurr = aTestVector.getY() / aEdgeVector.getY();
1077                         }
1078                         else
1079                         {
1080                             // Z is biggest
1081                             fParamTestOnCurr = aTestVector.getZ() / aEdgeVector.getZ();
1082                         }
1083                     }
1084 
1085                     if(fTools::more(fParamTestOnCurr, fZero) && fTools::less(fParamTestOnCurr, fOne))
1086                     {
1087                         return true;
1088                     }
1089                 }
1090 
1091                 return false;
1092             }
1093         }
1094 
isPointOnPolygon(const B3DPolygon & rCandidate,const B3DPoint & rPoint,bool bWithPoints)1095         bool isPointOnPolygon(const B3DPolygon& rCandidate, const B3DPoint& rPoint, bool bWithPoints)
1096         {
1097             const sal_uInt32 nPointCount(rCandidate.count());
1098 
1099             if(nPointCount > 1L)
1100             {
1101                 const sal_uInt32 nLoopCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1L);
1102                 B3DPoint aCurrentPoint(rCandidate.getB3DPoint(0));
1103 
1104                 for(sal_uInt32 a(0); a < nLoopCount; a++)
1105                 {
1106                     const B3DPoint aNextPoint(rCandidate.getB3DPoint((a + 1) % nPointCount));
1107 
1108                     if(isPointOnLine(aCurrentPoint, aNextPoint, rPoint, bWithPoints))
1109                     {
1110                         return true;
1111                     }
1112 
1113                     aCurrentPoint = aNextPoint;
1114                 }
1115             }
1116             else if(nPointCount && bWithPoints)
1117             {
1118                 return rPoint.equal(rCandidate.getB3DPoint(0));
1119             }
1120 
1121             return false;
1122         }
1123 
getCutBetweenLineAndPlane(const B3DVector & rPlaneNormal,const B3DPoint & rPlanePoint,const B3DPoint & rEdgeStart,const B3DPoint & rEdgeEnd,double & fCut)1124         bool getCutBetweenLineAndPlane(const B3DVector& rPlaneNormal, const B3DPoint& rPlanePoint, const B3DPoint& rEdgeStart, const B3DPoint& rEdgeEnd, double& fCut)
1125         {
1126             if(!rPlaneNormal.equalZero() && !rEdgeStart.equal(rEdgeEnd))
1127             {
1128                 const B3DVector aTestEdge(rEdgeEnd - rEdgeStart);
1129                 const double fScalarEdge(rPlaneNormal.scalar(aTestEdge));
1130 
1131                 if(!fTools::equalZero(fScalarEdge))
1132                 {
1133                     const B3DVector aCompareEdge(rPlanePoint - rEdgeStart);
1134                     const double fScalarCompare(rPlaneNormal.scalar(aCompareEdge));
1135 
1136                     fCut = fScalarCompare / fScalarEdge;
1137                     return true;
1138                 }
1139             }
1140 
1141             return false;
1142         }
1143 
getCutBetweenLineAndPolygon(const B3DPolygon & rCandidate,const B3DPoint & rEdgeStart,const B3DPoint & rEdgeEnd,double & fCut)1144         bool getCutBetweenLineAndPolygon(const B3DPolygon& rCandidate, const B3DPoint& rEdgeStart, const B3DPoint& rEdgeEnd, double& fCut)
1145         {
1146             const sal_uInt32 nPointCount(rCandidate.count());
1147 
1148             if(nPointCount > 2 && !rEdgeStart.equal(rEdgeEnd))
1149             {
1150                 const B3DVector aPlaneNormal(rCandidate.getNormal());
1151 
1152                 if(!aPlaneNormal.equalZero())
1153                 {
1154                     const B3DPoint aPointOnPlane(rCandidate.getB3DPoint(0));
1155 
1156                     return getCutBetweenLineAndPlane(aPlaneNormal, aPointOnPlane, rEdgeStart, rEdgeEnd, fCut);
1157                 }
1158             }
1159 
1160             return false;
1161         }
1162 
1163         //////////////////////////////////////////////////////////////////////
1164         // comparators with tolerance for 3D Polygons
1165 
equal(const B3DPolygon & rCandidateA,const B3DPolygon & rCandidateB,const double & rfSmallValue)1166         bool equal(const B3DPolygon& rCandidateA, const B3DPolygon& rCandidateB, const double& rfSmallValue)
1167         {
1168             const sal_uInt32 nPointCount(rCandidateA.count());
1169 
1170             if(nPointCount != rCandidateB.count())
1171                 return false;
1172 
1173             const bool bClosed(rCandidateA.isClosed());
1174 
1175             if(bClosed != rCandidateB.isClosed())
1176                 return false;
1177 
1178             for(sal_uInt32 a(0); a < nPointCount; a++)
1179             {
1180                 const B3DPoint aPoint(rCandidateA.getB3DPoint(a));
1181 
1182                 if(!aPoint.equal(rCandidateB.getB3DPoint(a), rfSmallValue))
1183                     return false;
1184             }
1185 
1186             return true;
1187         }
1188 
equal(const B3DPolygon & rCandidateA,const B3DPolygon & rCandidateB)1189         bool equal(const B3DPolygon& rCandidateA, const B3DPolygon& rCandidateB)
1190         {
1191             const double fSmallValue(fTools::getSmallValue());
1192 
1193             return equal(rCandidateA, rCandidateB, fSmallValue);
1194         }
1195 
1196         // snap points of horizontal or vertical edges to discrete values
snapPointsOfHorizontalOrVerticalEdges(const B3DPolygon & rCandidate)1197         B3DPolygon snapPointsOfHorizontalOrVerticalEdges(const B3DPolygon& rCandidate)
1198         {
1199             const sal_uInt32 nPointCount(rCandidate.count());
1200 
1201             if(nPointCount > 1)
1202             {
1203                 // Start by copying the source polygon to get a writeable copy. The closed state is
1204                 // copied by aRetval's initialisation, too, so no need to copy it in this method
1205                 B3DPolygon aRetval(rCandidate);
1206 
1207                 // prepare geometry data. Get rounded from original
1208                 B3ITuple aPrevTuple(basegfx::fround(rCandidate.getB3DPoint(nPointCount - 1)));
1209                 B3DPoint aCurrPoint(rCandidate.getB3DPoint(0));
1210                 B3ITuple aCurrTuple(basegfx::fround(aCurrPoint));
1211 
1212                 // loop over all points. This will also snap the implicit closing edge
1213                 // even when not closed, but that's no problem here
1214                 for(sal_uInt32 a(0); a < nPointCount; a++)
1215                 {
1216                     // get next point. Get rounded from original
1217                     const bool bLastRun(a + 1 == nPointCount);
1218                     const sal_uInt32 nNextIndex(bLastRun ? 0 : a + 1);
1219                     const B3DPoint aNextPoint(rCandidate.getB3DPoint(nNextIndex));
1220                     const B3ITuple aNextTuple(basegfx::fround(aNextPoint));
1221 
1222                     // get the states
1223                     const bool bPrevVertical(aPrevTuple.getX() == aCurrTuple.getX());
1224                     const bool bNextVertical(aNextTuple.getX() == aCurrTuple.getX());
1225                     const bool bPrevHorizontal(aPrevTuple.getY() == aCurrTuple.getY());
1226                     const bool bNextHorizontal(aNextTuple.getY() == aCurrTuple.getY());
1227                     const bool bSnapX(bPrevVertical || bNextVertical);
1228                     const bool bSnapY(bPrevHorizontal || bNextHorizontal);
1229 
1230                     if(bSnapX || bSnapY)
1231                     {
1232                         const B3DPoint aSnappedPoint(
1233                             bSnapX ? aCurrTuple.getX() : aCurrPoint.getX(),
1234                             bSnapY ? aCurrTuple.getY() : aCurrPoint.getY(),
1235                             aCurrPoint.getZ());
1236 
1237                         aRetval.setB3DPoint(a, aSnappedPoint);
1238                     }
1239 
1240                     // prepare next point
1241                     if(!bLastRun)
1242                     {
1243                         aPrevTuple = aCurrTuple;
1244                         aCurrPoint = aNextPoint;
1245                         aCurrTuple = aNextTuple;
1246                     }
1247                 }
1248 
1249                 return aRetval;
1250             }
1251             else
1252             {
1253                 return rCandidate;
1254             }
1255         }
1256 
1257     } // end of namespace tools
1258 } // end of namespace basegfx
1259 
1260 //////////////////////////////////////////////////////////////////////////////
1261 
1262 // eof
1263