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