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