File: | home/maarten/src/libreoffice/core/basegfx/source/polygon/b3dpolygontools.cxx |
Warning: | line 222, column 17 Value stored to 'fDotDashLength' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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/polygon/b3dpolypolygon.hxx> |
24 | #include <basegfx/numeric/ftools.hxx> |
25 | #include <basegfx/range/b3drange.hxx> |
26 | #include <basegfx/point/b2dpoint.hxx> |
27 | #include <basegfx/tuple/b3ituple.hxx> |
28 | #include <rtl/math.hxx> |
29 | #include <numeric> |
30 | |
31 | namespace 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 (!)")do { if (true && (!(nIndex < rCandidate.count()))) { sal_detail_logFormat((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl" ), ("/home/maarten/src/libreoffice/core/basegfx/source/polygon/b3dpolygontools.cxx" ":" "46" ": "), "%s", "getIndexOfPredecessor: Access to polygon out of range (!)" ); } } while (false); |
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 | |
94 | void applyLineDashing( |
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 |
113 | applyLineDashing( |
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 | |
148 | static void implHandleFirstLast( |
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 | |
173 | void applyLineDashing( |
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 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 (!)")(static_cast <bool> (true && "applyLineDashing: potentially too expensive to do the requested dismantle - please consider stretched LineDash pattern (!)" ) ? void (0) : __assert_fail ("true && \"applyLineDashing: potentially too expensive to do the requested dismantle - please consider stretched LineDash pattern (!)\"" , "/home/maarten/src/libreoffice/core/basegfx/source/polygon/b3dpolygontools.cxx" , 216, __extension__ __PRETTY_FUNCTION__)); |
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 | fDotDashLength *= fFactor; |
Value stored to 'fDotDashLength' is never read | |
223 | } |
224 | |
225 | // prepare current edge's start |
226 | B3DPoint aCurrentPoint(rCandidate.getB3DPoint(0)); |
227 | const bool bIsClosed(rCandidate.isClosed()); |
228 | const sal_uInt32 nEdgeCount(bIsClosed ? nPointCount : nPointCount - 1); |
229 | |
230 | // prepare DotDashArray iteration and the line/gap switching bool |
231 | sal_uInt32 nDotDashIndex(0); |
232 | bool bIsLine(true); |
233 | double fDotDashMovingLength(aDotDashArray[0]); |
234 | B3DPolygon aSnippet; |
235 | |
236 | // remember 1st and last snippets to try to merge after execution |
237 | // is complete and hand to callback |
238 | B3DPolygon aFirstLine, aLastLine; |
239 | |
240 | // iterate over all edges |
241 | for(sal_uInt32 a(0); a < nEdgeCount; a++) |
242 | { |
243 | // update current edge |
244 | const sal_uInt32 nNextIndex((a + 1) % nPointCount); |
245 | const B3DPoint aNextPoint(rCandidate.getB3DPoint(nNextIndex)); |
246 | const double fEdgeLength(B3DVector(aNextPoint - aCurrentPoint).getLength()); |
247 | |
248 | if(!fTools::equalZero(fEdgeLength)) |
249 | { |
250 | double fLastDotDashMovingLength(0.0); |
251 | while(fTools::less(fDotDashMovingLength, fEdgeLength)) |
252 | { |
253 | // new split is inside edge, create and append snippet [fLastDotDashMovingLength, fDotDashMovingLength] |
254 | const bool bHandleLine(bIsLine && aLineTargetCallback); |
255 | |
256 | if(bHandleLine) |
257 | { |
258 | if(!aSnippet.count()) |
259 | { |
260 | aSnippet.append(interpolate(aCurrentPoint, aNextPoint, fLastDotDashMovingLength / fEdgeLength)); |
261 | } |
262 | |
263 | aSnippet.append(interpolate(aCurrentPoint, aNextPoint, fDotDashMovingLength / fEdgeLength)); |
264 | |
265 | implHandleSnippet(aSnippet, aLineTargetCallback, aFirstLine, aLastLine); |
266 | |
267 | aSnippet.clear(); |
268 | } |
269 | |
270 | // prepare next DotDashArray step and flip line/gap flag |
271 | fLastDotDashMovingLength = fDotDashMovingLength; |
272 | fDotDashMovingLength += aDotDashArray[(++nDotDashIndex) % nDotDashCount]; |
273 | bIsLine = !bIsLine; |
274 | } |
275 | |
276 | // append snippet [fLastDotDashMovingLength, fEdgeLength] |
277 | const bool bHandleLine(bIsLine && aLineTargetCallback); |
278 | |
279 | if(bHandleLine) |
280 | { |
281 | if(!aSnippet.count()) |
282 | { |
283 | aSnippet.append(interpolate(aCurrentPoint, aNextPoint, fLastDotDashMovingLength / fEdgeLength)); |
284 | } |
285 | |
286 | aSnippet.append(aNextPoint); |
287 | } |
288 | |
289 | // prepare move to next edge |
290 | fDotDashMovingLength -= fEdgeLength; |
291 | } |
292 | |
293 | // prepare next edge step (end point gets new start point) |
294 | aCurrentPoint = aNextPoint; |
295 | } |
296 | |
297 | // append last intermediate results (if exists) |
298 | if(aSnippet.count()) |
299 | { |
300 | const bool bHandleLine(bIsLine && aLineTargetCallback); |
301 | |
302 | if(bHandleLine) |
303 | { |
304 | implHandleSnippet(aSnippet, aLineTargetCallback, aFirstLine, aLastLine); |
305 | } |
306 | } |
307 | |
308 | if(bIsClosed && aLineTargetCallback) |
309 | { |
310 | implHandleFirstLast(aLineTargetCallback, aFirstLine, aLastLine); |
311 | } |
312 | } |
313 | |
314 | B3DPolygon applyDefaultNormalsSphere( const B3DPolygon& rCandidate, const B3DPoint& rCenter) |
315 | { |
316 | B3DPolygon aRetval(rCandidate); |
317 | |
318 | for(sal_uInt32 a(0); a < aRetval.count(); a++) |
319 | { |
320 | B3DVector aVector(aRetval.getB3DPoint(a) - rCenter); |
321 | aVector.normalize(); |
322 | aRetval.setNormal(a, aVector); |
323 | } |
324 | |
325 | return aRetval; |
326 | } |
327 | |
328 | B3DPolygon invertNormals( const B3DPolygon& rCandidate) |
329 | { |
330 | B3DPolygon aRetval(rCandidate); |
331 | |
332 | if(aRetval.areNormalsUsed()) |
333 | { |
334 | for(sal_uInt32 a(0); a < aRetval.count(); a++) |
335 | { |
336 | aRetval.setNormal(a, -aRetval.getNormal(a)); |
337 | } |
338 | } |
339 | |
340 | return aRetval; |
341 | } |
342 | |
343 | B3DPolygon applyDefaultTextureCoordinatesParallel( const B3DPolygon& rCandidate, const B3DRange& rRange, bool bChangeX, bool bChangeY) |
344 | { |
345 | B3DPolygon aRetval(rCandidate); |
346 | |
347 | if(bChangeX || bChangeY) |
348 | { |
349 | // create projection of standard texture coordinates in (X, Y) onto |
350 | // the 3d coordinates straight |
351 | const double fWidth(rRange.getWidth()); |
352 | const double fHeight(rRange.getHeight()); |
353 | const bool bWidthSet(!fTools::equalZero(fWidth)); |
354 | const bool bHeightSet(!fTools::equalZero(fHeight)); |
355 | const double fOne(1.0); |
356 | |
357 | for(sal_uInt32 a(0); a < aRetval.count(); a++) |
358 | { |
359 | const B3DPoint aPoint(aRetval.getB3DPoint(a)); |
360 | B2DPoint aTextureCoordinate(aRetval.getTextureCoordinate(a)); |
361 | |
362 | if(bChangeX) |
363 | { |
364 | if(bWidthSet) |
365 | { |
366 | aTextureCoordinate.setX((aPoint.getX() - rRange.getMinX()) / fWidth); |
367 | } |
368 | else |
369 | { |
370 | aTextureCoordinate.setX(0.0); |
371 | } |
372 | } |
373 | |
374 | if(bChangeY) |
375 | { |
376 | if(bHeightSet) |
377 | { |
378 | aTextureCoordinate.setY(fOne - ((aPoint.getY() - rRange.getMinY()) / fHeight)); |
379 | } |
380 | else |
381 | { |
382 | aTextureCoordinate.setY(fOne); |
383 | } |
384 | } |
385 | |
386 | aRetval.setTextureCoordinate(a, aTextureCoordinate); |
387 | } |
388 | } |
389 | |
390 | return aRetval; |
391 | } |
392 | |
393 | B3DPolygon applyDefaultTextureCoordinatesSphere( const B3DPolygon& rCandidate, const B3DPoint& rCenter, bool bChangeX, bool bChangeY) |
394 | { |
395 | B3DPolygon aRetval(rCandidate); |
396 | |
397 | if(bChangeX || bChangeY) |
398 | { |
399 | // create texture coordinates using sphere projection to cartesian coordinates, |
400 | // use object's center as base |
401 | const double fOne(1.0); |
402 | const sal_uInt32 nPointCount(aRetval.count()); |
403 | bool bPolarPoints(false); |
404 | sal_uInt32 a; |
405 | |
406 | // create center cartesian coordinates to have a possibility to decide if on boundary |
407 | // transitions which value to choose |
408 | const B3DRange aPlaneRange(getRange(rCandidate)); |
409 | const B3DPoint aPlaneCenter(aPlaneRange.getCenter() - rCenter); |
410 | const double fXCenter(fOne - ((atan2(aPlaneCenter.getZ(), aPlaneCenter.getX()) + F_PI3.14159265358979323846) / F_2PI(2.0*3.14159265358979323846))); |
411 | |
412 | for(a = 0; a < nPointCount; a++) |
413 | { |
414 | const B3DVector aVector(aRetval.getB3DPoint(a) - rCenter); |
415 | const double fY(fOne - ((atan2(aVector.getY(), aVector.getXZLength()) + F_PI21.57079632679489661923) / F_PI3.14159265358979323846)); |
416 | B2DPoint aTexCoor(aRetval.getTextureCoordinate(a)); |
417 | |
418 | if(fTools::equalZero(fY)) |
419 | { |
420 | // point is a north polar point, no useful X-coordinate can be created. |
421 | if(bChangeY) |
422 | { |
423 | aTexCoor.setY(0.0); |
424 | |
425 | if(bChangeX) |
426 | { |
427 | bPolarPoints = true; |
428 | } |
429 | } |
430 | } |
431 | else if(fTools::equal(fY, fOne)) |
432 | { |
433 | // point is a south polar point, no useful X-coordinate can be created. Set |
434 | // Y-coordinate, though |
435 | if(bChangeY) |
436 | { |
437 | aTexCoor.setY(fOne); |
438 | |
439 | if(bChangeX) |
440 | { |
441 | bPolarPoints = true; |
442 | } |
443 | } |
444 | } |
445 | else |
446 | { |
447 | double fX(fOne - ((atan2(aVector.getZ(), aVector.getX()) + F_PI3.14159265358979323846) / F_2PI(2.0*3.14159265358979323846))); |
448 | |
449 | // correct cartesian point coordinate dependent from center value |
450 | if(fX > fXCenter + 0.5) |
451 | { |
452 | fX -= fOne; |
453 | } |
454 | else if(fX < fXCenter - 0.5) |
455 | { |
456 | fX += fOne; |
457 | } |
458 | |
459 | if(bChangeX) |
460 | { |
461 | aTexCoor.setX(fX); |
462 | } |
463 | |
464 | if(bChangeY) |
465 | { |
466 | aTexCoor.setY(fY); |
467 | } |
468 | } |
469 | |
470 | aRetval.setTextureCoordinate(a, aTexCoor); |
471 | } |
472 | |
473 | if(bPolarPoints) |
474 | { |
475 | // correct X-texture coordinates if polar points are contained. Those |
476 | // coordinates cannot be correct, so use prev or next X-coordinate |
477 | for(a = 0; a < nPointCount; a++) |
478 | { |
479 | B2DPoint aTexCoor(aRetval.getTextureCoordinate(a)); |
480 | |
481 | if(fTools::equalZero(aTexCoor.getY()) || fTools::equal(aTexCoor.getY(), fOne)) |
482 | { |
483 | // get prev, next TexCoor and test for pole |
484 | const B2DPoint aPrevTexCoor(aRetval.getTextureCoordinate(a ? a - 1 : nPointCount - 1)); |
485 | const B2DPoint aNextTexCoor(aRetval.getTextureCoordinate((a + 1) % nPointCount)); |
486 | const bool bPrevPole(fTools::equalZero(aPrevTexCoor.getY()) || fTools::equal(aPrevTexCoor.getY(), fOne)); |
487 | const bool bNextPole(fTools::equalZero(aNextTexCoor.getY()) || fTools::equal(aNextTexCoor.getY(), fOne)); |
488 | |
489 | if(!bPrevPole && !bNextPole) |
490 | { |
491 | // both no poles, mix them |
492 | aTexCoor.setX((aPrevTexCoor.getX() + aNextTexCoor.getX()) / 2.0); |
493 | } |
494 | else if(!bNextPole) |
495 | { |
496 | // copy next |
497 | aTexCoor.setX(aNextTexCoor.getX()); |
498 | } |
499 | else |
500 | { |
501 | // copy prev, even if it's a pole, hopefully it is already corrected |
502 | aTexCoor.setX(aPrevTexCoor.getX()); |
503 | } |
504 | |
505 | aRetval.setTextureCoordinate(a, aTexCoor); |
506 | } |
507 | } |
508 | } |
509 | } |
510 | |
511 | return aRetval; |
512 | } |
513 | |
514 | bool isInside(const B3DPolygon& rCandidate, const B3DPoint& rPoint, bool bWithBorder) |
515 | { |
516 | if(bWithBorder && isPointOnPolygon(rCandidate, rPoint)) |
517 | { |
518 | return true; |
519 | } |
520 | else |
521 | { |
522 | bool bRetval(false); |
523 | const B3DVector aPlaneNormal(rCandidate.getNormal()); |
524 | |
525 | if(!aPlaneNormal.equalZero()) |
526 | { |
527 | const sal_uInt32 nPointCount(rCandidate.count()); |
528 | |
529 | if(nPointCount) |
530 | { |
531 | B3DPoint aCurrentPoint(rCandidate.getB3DPoint(nPointCount - 1)); |
532 | const double fAbsX(fabs(aPlaneNormal.getX())); |
533 | const double fAbsY(fabs(aPlaneNormal.getY())); |
534 | const double fAbsZ(fabs(aPlaneNormal.getZ())); |
535 | |
536 | if(fAbsX > fAbsY && fAbsX > fAbsZ) |
537 | { |
538 | // normal points mostly in X-Direction, use YZ-Polygon projection for check |
539 | // x -> y, y -> z |
540 | for(sal_uInt32 a(0); a < nPointCount; a++) |
541 | { |
542 | const B3DPoint aPreviousPoint(aCurrentPoint); |
543 | aCurrentPoint = rCandidate.getB3DPoint(a); |
544 | |
545 | // cross-over in Z? |
546 | const bool bCompZA(fTools::more(aPreviousPoint.getZ(), rPoint.getZ())); |
547 | const bool bCompZB(fTools::more(aCurrentPoint.getZ(), rPoint.getZ())); |
548 | |
549 | if(bCompZA != bCompZB) |
550 | { |
551 | // cross-over in Y? |
552 | const bool bCompYA(fTools::more(aPreviousPoint.getY(), rPoint.getY())); |
553 | const bool bCompYB(fTools::more(aCurrentPoint.getY(), rPoint.getY())); |
554 | |
555 | if(bCompYA == bCompYB) |
556 | { |
557 | if(bCompYA) |
558 | { |
559 | bRetval = !bRetval; |
560 | } |
561 | } |
562 | else |
563 | { |
564 | const double fCompare( |
565 | aCurrentPoint.getY() - (aCurrentPoint.getZ() - rPoint.getZ()) * |
566 | (aPreviousPoint.getY() - aCurrentPoint.getY()) / |
567 | (aPreviousPoint.getZ() - aCurrentPoint.getZ())); |
568 | |
569 | if(fTools::more(fCompare, rPoint.getY())) |
570 | { |
571 | bRetval = !bRetval; |
572 | } |
573 | } |
574 | } |
575 | } |
576 | } |
577 | else if(fAbsY > fAbsX && fAbsY > fAbsZ) |
578 | { |
579 | // normal points mostly in Y-Direction, use XZ-Polygon projection for check |
580 | // x -> x, y -> z |
581 | for(sal_uInt32 a(0); a < nPointCount; a++) |
582 | { |
583 | const B3DPoint aPreviousPoint(aCurrentPoint); |
584 | aCurrentPoint = rCandidate.getB3DPoint(a); |
585 | |
586 | // cross-over in Z? |
587 | const bool bCompZA(fTools::more(aPreviousPoint.getZ(), rPoint.getZ())); |
588 | const bool bCompZB(fTools::more(aCurrentPoint.getZ(), rPoint.getZ())); |
589 | |
590 | if(bCompZA != bCompZB) |
591 | { |
592 | // cross-over in X? |
593 | const bool bCompXA(fTools::more(aPreviousPoint.getX(), rPoint.getX())); |
594 | const bool bCompXB(fTools::more(aCurrentPoint.getX(), rPoint.getX())); |
595 | |
596 | if(bCompXA == bCompXB) |
597 | { |
598 | if(bCompXA) |
599 | { |
600 | bRetval = !bRetval; |
601 | } |
602 | } |
603 | else |
604 | { |
605 | const double fCompare( |
606 | aCurrentPoint.getX() - (aCurrentPoint.getZ() - rPoint.getZ()) * |
607 | (aPreviousPoint.getX() - aCurrentPoint.getX()) / |
608 | (aPreviousPoint.getZ() - aCurrentPoint.getZ())); |
609 | |
610 | if(fTools::more(fCompare, rPoint.getX())) |
611 | { |
612 | bRetval = !bRetval; |
613 | } |
614 | } |
615 | } |
616 | } |
617 | } |
618 | else |
619 | { |
620 | // normal points mostly in Z-Direction, use XY-Polygon projection for check |
621 | // x -> x, y -> y |
622 | for(sal_uInt32 a(0); a < nPointCount; a++) |
623 | { |
624 | const B3DPoint aPreviousPoint(aCurrentPoint); |
625 | aCurrentPoint = rCandidate.getB3DPoint(a); |
626 | |
627 | // cross-over in Y? |
628 | const bool bCompYA(fTools::more(aPreviousPoint.getY(), rPoint.getY())); |
629 | const bool bCompYB(fTools::more(aCurrentPoint.getY(), rPoint.getY())); |
630 | |
631 | if(bCompYA != bCompYB) |
632 | { |
633 | // cross-over in X? |
634 | const bool bCompXA(fTools::more(aPreviousPoint.getX(), rPoint.getX())); |
635 | const bool bCompXB(fTools::more(aCurrentPoint.getX(), rPoint.getX())); |
636 | |
637 | if(bCompXA == bCompXB) |
638 | { |
639 | if(bCompXA) |
640 | { |
641 | bRetval = !bRetval; |
642 | } |
643 | } |
644 | else |
645 | { |
646 | const double fCompare( |
647 | aCurrentPoint.getX() - (aCurrentPoint.getY() - rPoint.getY()) * |
648 | (aPreviousPoint.getX() - aCurrentPoint.getX()) / |
649 | (aPreviousPoint.getY() - aCurrentPoint.getY())); |
650 | |
651 | if(fTools::more(fCompare, rPoint.getX())) |
652 | { |
653 | bRetval = !bRetval; |
654 | } |
655 | } |
656 | } |
657 | } |
658 | } |
659 | } |
660 | } |
661 | |
662 | return bRetval; |
663 | } |
664 | } |
665 | |
666 | bool isPointOnLine(const B3DPoint& rStart, const B3DPoint& rEnd, const B3DPoint& rCandidate, bool bWithPoints) |
667 | { |
668 | if(rCandidate.equal(rStart) || rCandidate.equal(rEnd)) |
669 | { |
670 | // candidate is in epsilon around start or end -> inside |
671 | return bWithPoints; |
672 | } |
673 | else if(rStart.equal(rEnd)) |
674 | { |
675 | // start and end are equal, but candidate is outside their epsilon -> outside |
676 | return false; |
677 | } |
678 | else |
679 | { |
680 | const B3DVector aEdgeVector(rEnd - rStart); |
681 | const B3DVector aTestVector(rCandidate - rStart); |
682 | |
683 | if(areParallel(aEdgeVector, aTestVector)) |
684 | { |
685 | double fParamTestOnCurr(0.0); |
686 | |
687 | if(aEdgeVector.getX() > aEdgeVector.getY()) |
688 | { |
689 | if(aEdgeVector.getX() > aEdgeVector.getZ()) |
690 | { |
691 | // X is biggest |
692 | fParamTestOnCurr = aTestVector.getX() / aEdgeVector.getX(); |
693 | } |
694 | else |
695 | { |
696 | // Z is biggest |
697 | fParamTestOnCurr = aTestVector.getZ() / aEdgeVector.getZ(); |
698 | } |
699 | } |
700 | else |
701 | { |
702 | if(aEdgeVector.getY() > aEdgeVector.getZ()) |
703 | { |
704 | // Y is biggest |
705 | fParamTestOnCurr = aTestVector.getY() / aEdgeVector.getY(); |
706 | } |
707 | else |
708 | { |
709 | // Z is biggest |
710 | fParamTestOnCurr = aTestVector.getZ() / aEdgeVector.getZ(); |
711 | } |
712 | } |
713 | |
714 | if(fTools::more(fParamTestOnCurr, 0.0) && fTools::less(fParamTestOnCurr, 1.0)) |
715 | { |
716 | return true; |
717 | } |
718 | } |
719 | |
720 | return false; |
721 | } |
722 | } |
723 | |
724 | bool isPointOnPolygon(const B3DPolygon& rCandidate, const B3DPoint& rPoint) |
725 | { |
726 | const sal_uInt32 nPointCount(rCandidate.count()); |
727 | |
728 | if(nPointCount > 1) |
729 | { |
730 | const sal_uInt32 nLoopCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1); |
731 | B3DPoint aCurrentPoint(rCandidate.getB3DPoint(0)); |
732 | |
733 | for(sal_uInt32 a(0); a < nLoopCount; a++) |
734 | { |
735 | const B3DPoint aNextPoint(rCandidate.getB3DPoint((a + 1) % nPointCount)); |
736 | |
737 | if(isPointOnLine(aCurrentPoint, aNextPoint, rPoint, true/*bWithPoints*/)) |
738 | { |
739 | return true; |
740 | } |
741 | |
742 | aCurrentPoint = aNextPoint; |
743 | } |
744 | } |
745 | else if(nPointCount) |
746 | { |
747 | return rPoint.equal(rCandidate.getB3DPoint(0)); |
748 | } |
749 | |
750 | return false; |
751 | } |
752 | |
753 | bool getCutBetweenLineAndPlane(const B3DVector& rPlaneNormal, const B3DPoint& rPlanePoint, const B3DPoint& rEdgeStart, const B3DPoint& rEdgeEnd, double& fCut) |
754 | { |
755 | if(!rPlaneNormal.equalZero() && !rEdgeStart.equal(rEdgeEnd)) |
756 | { |
757 | const B3DVector aTestEdge(rEdgeEnd - rEdgeStart); |
758 | const double fScalarEdge(rPlaneNormal.scalar(aTestEdge)); |
759 | |
760 | if(!fTools::equalZero(fScalarEdge)) |
761 | { |
762 | const B3DVector aCompareEdge(rPlanePoint - rEdgeStart); |
763 | const double fScalarCompare(rPlaneNormal.scalar(aCompareEdge)); |
764 | |
765 | fCut = fScalarCompare / fScalarEdge; |
766 | return true; |
767 | } |
768 | } |
769 | |
770 | return false; |
771 | } |
772 | |
773 | // snap points of horizontal or vertical edges to discrete values |
774 | B3DPolygon snapPointsOfHorizontalOrVerticalEdges(const B3DPolygon& rCandidate) |
775 | { |
776 | const sal_uInt32 nPointCount(rCandidate.count()); |
777 | |
778 | if(nPointCount > 1) |
779 | { |
780 | // Start by copying the source polygon to get a writeable copy. The closed state is |
781 | // copied by aRetval's initialisation, too, so no need to copy it in this method |
782 | B3DPolygon aRetval(rCandidate); |
783 | |
784 | // prepare geometry data. Get rounded from original |
785 | B3ITuple aPrevTuple(basegfx::fround(rCandidate.getB3DPoint(nPointCount - 1))); |
786 | B3DPoint aCurrPoint(rCandidate.getB3DPoint(0)); |
787 | B3ITuple aCurrTuple(basegfx::fround(aCurrPoint)); |
788 | |
789 | // loop over all points. This will also snap the implicit closing edge |
790 | // even when not closed, but that's no problem here |
791 | for(sal_uInt32 a(0); a < nPointCount; a++) |
792 | { |
793 | // get next point. Get rounded from original |
794 | const bool bLastRun(a + 1 == nPointCount); |
795 | const sal_uInt32 nNextIndex(bLastRun ? 0 : a + 1); |
796 | const B3DPoint aNextPoint(rCandidate.getB3DPoint(nNextIndex)); |
797 | const B3ITuple aNextTuple(basegfx::fround(aNextPoint)); |
798 | |
799 | // get the states |
800 | const bool bPrevVertical(aPrevTuple.getX() == aCurrTuple.getX()); |
801 | const bool bNextVertical(aNextTuple.getX() == aCurrTuple.getX()); |
802 | const bool bPrevHorizontal(aPrevTuple.getY() == aCurrTuple.getY()); |
803 | const bool bNextHorizontal(aNextTuple.getY() == aCurrTuple.getY()); |
804 | const bool bSnapX(bPrevVertical || bNextVertical); |
805 | const bool bSnapY(bPrevHorizontal || bNextHorizontal); |
806 | |
807 | if(bSnapX || bSnapY) |
808 | { |
809 | const B3DPoint aSnappedPoint( |
810 | bSnapX ? aCurrTuple.getX() : aCurrPoint.getX(), |
811 | bSnapY ? aCurrTuple.getY() : aCurrPoint.getY(), |
812 | aCurrPoint.getZ()); |
813 | |
814 | aRetval.setB3DPoint(a, aSnappedPoint); |
815 | } |
816 | |
817 | // prepare next point |
818 | if(!bLastRun) |
819 | { |
820 | aPrevTuple = aCurrTuple; |
821 | aCurrPoint = aNextPoint; |
822 | aCurrTuple = aNextTuple; |
823 | } |
824 | } |
825 | |
826 | return aRetval; |
827 | } |
828 | else |
829 | { |
830 | return rCandidate; |
831 | } |
832 | } |
833 | |
834 | } // end of namespace |
835 | |
836 | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |