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