LibreOffice Module drawinglayer (master) 1
sdrextrudelathetools3d.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
21
22#include <osl/diagnose.h>
35#include <numeric>
36
37
38// decomposition helpers for extrude/lathe (rotation) objects
39
40namespace
41{
42
43 // common helpers
44
45 basegfx::B2DPolyPolygon impScalePolyPolygonOnCenter(
46 const basegfx::B2DPolyPolygon& rSource,
47 double fScale)
48 {
49 basegfx::B2DPolyPolygon aRetval(rSource);
50
52 {
53 const basegfx::B2DRange aRange(basegfx::utils::getRange(rSource));
54 const basegfx::B2DPoint aCenter(aRange.getCenter());
56
57 aTrans.translate(-aCenter.getX(), -aCenter.getY());
58 aTrans.scale(fScale, fScale);
59 aTrans.translate(aCenter.getX(), aCenter.getY());
60 aRetval.transform(aTrans);
61 }
62
63 return aRetval;
64 }
65
66 void impGetOuterPolyPolygon(
68 basegfx::B2DPolyPolygon& rOuterPolyPolygon,
69 double fOffset,
70 bool bCharacterMode)
71 {
72 rOuterPolyPolygon = rPolygon;
73
74 if(!basegfx::fTools::more(fOffset, 0.0))
75 return;
76
77 if(bCharacterMode)
78 {
79 // grow the outside polygon and scale all polygons to original size. This is done
80 // to avoid a shrink which potentially would lead to self-intersections, but changes
81 // the original polygon -> not a precision step, so e.g. not usable for charts
82 const basegfx::B2DRange aRange(basegfx::utils::getRange(rPolygon));
83 rPolygon = basegfx::utils::growInNormalDirection(rPolygon, fOffset);
84 const basegfx::B2DRange aGrownRange(basegfx::utils::getRange(rPolygon));
85 const double fScaleX(basegfx::fTools::equalZero(aGrownRange.getWidth()) ? 1.0 : aRange.getWidth() / aGrownRange.getWidth());
86 const double fScaleY(basegfx::fTools::equalZero(aGrownRange.getHeight())? 1.0 : aRange.getHeight() / aGrownRange.getHeight());
87 basegfx::B2DHomMatrix aScaleTrans;
88
89 aScaleTrans.translate(-aGrownRange.getMinX(), -aGrownRange.getMinY());
90 aScaleTrans.scale(fScaleX, fScaleY);
91 aScaleTrans.translate(aRange.getMinX(), aRange.getMinY());
92 rPolygon.transform(aScaleTrans);
93 rOuterPolyPolygon.transform(aScaleTrans);
94 }
95 else
96 {
97 // use more precision, shrink the outer polygons. Since this may lead to self-intersections,
98 // some kind of correction should be applied here after that step
99 rOuterPolyPolygon = basegfx::utils::growInNormalDirection(rPolygon, -fOffset);
100 // basegfx::utils::correctGrowShrinkPolygonPair(rPolygon, rOuterPolyPolygon);
101 }
102 }
103
104 void impAddInBetweenFill(
106 const basegfx::B3DPolyPolygon& rPolA,
107 const basegfx::B3DPolyPolygon& rPolB,
108 double fTexVerStart,
109 double fTexVerStop,
110 bool bCreateNormals,
111 bool bCreateTextureCoordinates)
112 {
113 OSL_ENSURE(rPolA.count() == rPolB.count(), "impAddInBetweenFill: unequally sized polygons (!)");
114 const sal_uInt32 nPolygonCount(std::min(rPolA.count(), rPolB.count()));
115
116 for(sal_uInt32 a(0); a < nPolygonCount; a++)
117 {
118 const basegfx::B3DPolygon& aSubA(rPolA.getB3DPolygon(a));
119 const basegfx::B3DPolygon& aSubB(rPolB.getB3DPolygon(a));
120 OSL_ENSURE(aSubA.count() == aSubB.count(), "impAddInBetweenFill: unequally sized polygons (!)");
121 const sal_uInt32 nPointCount(std::min(aSubA.count(), aSubB.count()));
122
123 if(nPointCount)
124 {
125 const sal_uInt32 nEdgeCount(aSubA.isClosed() ? nPointCount : nPointCount - 1);
126 double fTexHorMultiplicatorA(0.0), fTexHorMultiplicatorB(0.0);
127 double fPolygonPosA(0.0), fPolygonPosB(0.0);
128
129 if(bCreateTextureCoordinates)
130 {
131 const double fPolygonLengthA(basegfx::utils::getLength(aSubA));
132 fTexHorMultiplicatorA = basegfx::fTools::equalZero(fPolygonLengthA) ? 1.0 : 1.0 / fPolygonLengthA;
133
134 const double fPolygonLengthB(basegfx::utils::getLength(aSubB));
135 fTexHorMultiplicatorB = basegfx::fTools::equalZero(fPolygonLengthB) ? 1.0 : 1.0 / fPolygonLengthB;
136 }
137
138 for(sal_uInt32 b(0); b < nEdgeCount; b++)
139 {
140 const sal_uInt32 nIndexA(b);
141 const sal_uInt32 nIndexB((b + 1) % nPointCount);
142
143 const basegfx::B3DPoint aStartA(aSubA.getB3DPoint(nIndexA));
144 const basegfx::B3DPoint aEndA(aSubA.getB3DPoint(nIndexB));
145 const basegfx::B3DPoint aStartB(aSubB.getB3DPoint(nIndexA));
146 const basegfx::B3DPoint aEndB(aSubB.getB3DPoint(nIndexB));
147
149 aNew.setClosed(true);
150
151 aNew.append(aStartA);
152 aNew.append(aStartB);
153 aNew.append(aEndB);
154 aNew.append(aEndA);
155
156 if(bCreateNormals)
157 {
158 aNew.setNormal(0, aSubA.getNormal(nIndexA));
159 aNew.setNormal(1, aSubB.getNormal(nIndexA));
160 aNew.setNormal(2, aSubB.getNormal(nIndexB));
161 aNew.setNormal(3, aSubA.getNormal(nIndexB));
162 }
163
164 if(bCreateTextureCoordinates)
165 {
166 const double fRelTexAL(fPolygonPosA * fTexHorMultiplicatorA);
167 const double fEdgeLengthA(basegfx::B3DVector(aEndA - aStartA).getLength());
168 fPolygonPosA += fEdgeLengthA;
169 const double fRelTexAR(fPolygonPosA * fTexHorMultiplicatorA);
170
171 const double fRelTexBL(fPolygonPosB * fTexHorMultiplicatorB);
172 const double fEdgeLengthB(basegfx::B3DVector(aEndB - aStartB).getLength());
173 fPolygonPosB += fEdgeLengthB;
174 const double fRelTexBR(fPolygonPosB * fTexHorMultiplicatorB);
175
176 aNew.setTextureCoordinate(0, basegfx::B2DPoint(fRelTexAL, fTexVerStart));
177 aNew.setTextureCoordinate(1, basegfx::B2DPoint(fRelTexBL, fTexVerStop));
178 aNew.setTextureCoordinate(2, basegfx::B2DPoint(fRelTexBR, fTexVerStop));
179 aNew.setTextureCoordinate(3, basegfx::B2DPoint(fRelTexAR, fTexVerStart));
180 }
181
182 rTarget.append(aNew);
183 }
184 }
185 }
186 }
187
188 void impSetNormal(
189 basegfx::B3DPolyPolygon& rCandidate,
190 const basegfx::B3DVector& rNormal)
191 {
192 for(sal_uInt32 a(0); a < rCandidate.count(); a++)
193 {
194 basegfx::B3DPolygon aSub(rCandidate.getB3DPolygon(a));
195
196 for(sal_uInt32 b(0); b < aSub.count(); b++)
197 {
198 aSub.setNormal(b, rNormal);
199 }
200
201 rCandidate.setB3DPolygon(a, aSub);
202 }
203 }
204
205 void impCreateInBetweenNormals(
208 {
209 OSL_ENSURE(rPolA.count() == rPolB.count(), "sdrExtrudePrimitive3D: unequally sized polygons (!)");
210 const sal_uInt32 nPolygonCount(std::min(rPolA.count(), rPolB.count()));
211
212 for(sal_uInt32 a(0); a < nPolygonCount; a++)
213 {
214 basegfx::B3DPolygon aSubA(rPolA.getB3DPolygon(a));
215 basegfx::B3DPolygon aSubB(rPolB.getB3DPolygon(a));
216 OSL_ENSURE(aSubA.count() == aSubB.count(), "sdrExtrudePrimitive3D: unequally sized polygons (!)");
217 const sal_uInt32 nPointCount(std::min(aSubA.count(), aSubB.count()));
218
219 if(nPointCount)
220 {
221 basegfx::B3DPoint aPrevA(aSubA.getB3DPoint(nPointCount - 1));
222 basegfx::B3DPoint aCurrA(aSubA.getB3DPoint(0));
223 const bool bClosed(aSubA.isClosed());
224
225 for(sal_uInt32 b(0); b < nPointCount; b++)
226 {
227 const sal_uInt32 nIndNext((b + 1) % nPointCount);
228 const basegfx::B3DPoint aNextA(aSubA.getB3DPoint(nIndNext));
229 const basegfx::B3DPoint aCurrB(aSubB.getB3DPoint(b));
230
231 // vector to back
232 basegfx::B3DVector aDepth(aCurrB - aCurrA);
233 aDepth.normalize();
234
235 if(aDepth.equalZero())
236 {
237 // no difference, try to get depth from next point
238 const basegfx::B3DPoint aNextB(aSubB.getB3DPoint(nIndNext));
239 aDepth = aNextB - aNextA;
240 aDepth.normalize();
241 }
242
243 // vector to left (correct for non-closed lines)
244 const bool bFirstAndNotClosed(!bClosed && 0 == b);
245 basegfx::B3DVector aLeft(bFirstAndNotClosed ? aCurrA - aNextA : aPrevA - aCurrA);
246 aLeft.normalize();
247
248 // create left normal
249 const basegfx::B3DVector aNormalLeft(aDepth.getPerpendicular(aLeft));
250
251 // smooth horizontal normals
252 {
253 // vector to right (correct for non-closed lines)
254 const bool bLastAndNotClosed(!bClosed && b + 1 == nPointCount);
255 basegfx::B3DVector aRight(bLastAndNotClosed ? aCurrA - aPrevA : aNextA - aCurrA);
256 aRight.normalize();
257
258 // create right normal
259 const basegfx::B3DVector aNormalRight(aRight.getPerpendicular(aDepth));
260
261 // create smoothed in-between normal
262 basegfx::B3DVector aNewNormal(aNormalLeft + aNormalRight);
263 aNewNormal.normalize();
264
265 // set as new normal at polygons
266 aSubA.setNormal(b, aNewNormal);
267 aSubB.setNormal(b, aNewNormal);
268 }
269
270 // prepare next step
271 aPrevA = aCurrA;
272 aCurrA = aNextA;
273 }
274
275 rPolA.setB3DPolygon(a, aSubA);
276 rPolB.setB3DPolygon(a, aSubB);
277 }
278 }
279 }
280
281 void impMixNormals(
283 const basegfx::B3DPolyPolygon& rPolB,
284 double fWeightA)
285 {
286 const double fWeightB(1.0 - fWeightA);
287 OSL_ENSURE(rPolA.count() == rPolB.count(), "sdrExtrudePrimitive3D: unequally sized polygons (!)");
288 const sal_uInt32 nPolygonCount(std::min(rPolA.count(), rPolB.count()));
289
290 for(sal_uInt32 a(0); a < nPolygonCount; a++)
291 {
292 basegfx::B3DPolygon aSubA(rPolA.getB3DPolygon(a));
293 const basegfx::B3DPolygon& aSubB(rPolB.getB3DPolygon(a));
294 OSL_ENSURE(aSubA.count() == aSubB.count(), "sdrExtrudePrimitive3D: unequally sized polygons (!)");
295 const sal_uInt32 nPointCount(std::min(aSubA.count(), aSubB.count()));
296
297 for(sal_uInt32 b(0); b < nPointCount; b++)
298 {
299 const basegfx::B3DVector aVA(aSubA.getNormal(b) * fWeightA);
300 const basegfx::B3DVector aVB(aSubB.getNormal(b) * fWeightB);
301 basegfx::B3DVector aVNew(aVA + aVB);
302 aVNew.normalize();
303 aSubA.setNormal(b, aVNew);
304 }
305
306 rPolA.setB3DPolygon(a, aSubA);
307 }
308 }
309
310 bool impHasCutWith(const basegfx::B2DPolygon& rPoly, const basegfx::B2DPoint& rStart, const basegfx::B2DPoint& rEnd)
311 {
312 // polygon is closed, one of the points is a member
313 const sal_uInt32 nPointCount(rPoly.count());
314
315 if(!nPointCount)
316 return false;
317
318 basegfx::B2DPoint aCurrent(rPoly.getB2DPoint(0));
319 const basegfx::B2DVector aVector(rEnd - rStart);
320
321 for(sal_uInt32 a(0); a < nPointCount; a++)
322 {
323 const sal_uInt32 nNextIndex((a + 1) % nPointCount);
324 const basegfx::B2DPoint aNext(rPoly.getB2DPoint(nNextIndex));
325 const basegfx::B2DVector aEdgeVector(aNext - aCurrent);
326
328 rStart, aVector,
329 aCurrent, aEdgeVector) != CutFlagValue::NONE)
330 {
331 return true;
332 }
333
334 aCurrent = aNext;
335 }
336
337 return false;
338 }
339} // end of anonymous namespace
340
341
343{
345 Slice3DVector& rSliceVector,
346 const basegfx::B2DPolyPolygon& rSource,
347 double fBackScale,
348 double fDiagonal,
349 double fRotation,
350 sal_uInt32 nSteps,
351 bool bCharacterMode,
352 bool bCloseFront,
353 bool bCloseBack)
354 {
355 if(basegfx::fTools::equalZero(fRotation) || 0 == nSteps)
356 {
357 // no rotation or no steps, just one plane
358 rSliceVector.emplace_back(rSource, basegfx::B3DHomMatrix());
359 }
360 else
361 {
362 const bool bBackScale(!basegfx::fTools::equal(fBackScale, 1.0));
363 const bool bClosedRotation(!bBackScale && basegfx::fTools::equal(fRotation, 2 * M_PI));
364 basegfx::B2DPolyPolygon aFront(rSource);
365 basegfx::B2DPolyPolygon aBack(rSource);
366 basegfx::B3DHomMatrix aTransformBack;
367 basegfx::B2DPolyPolygon aOuterBack;
368
369 if(bClosedRotation)
370 {
371 bCloseFront = bCloseBack = false;
372 }
373
374 if(bBackScale)
375 {
376 // avoid null zoom
377 if(basegfx::fTools::equalZero(fBackScale))
378 {
379 fBackScale = 0.000001;
380 }
381
382 // back is scaled compared to front, create scaled version
383 aBack = impScalePolyPolygonOnCenter(aBack, fBackScale);
384 }
385
386 if(bCloseFront || bCloseBack)
387 {
388 const basegfx::B2DRange aBaseRange(basegfx::utils::getRange(aFront));
389 const double fOuterLength(aBaseRange.getMaxX() * fRotation);
390 const double fInnerLength(aBaseRange.getMinX() * fRotation);
391 const double fAverageLength((fOuterLength + fInnerLength) * 0.5);
392
393 if(bCloseFront)
394 {
395 const double fOffsetLen((fAverageLength / 12.0) * fDiagonal);
396 basegfx::B2DPolyPolygon aOuterFront;
397 impGetOuterPolyPolygon(aFront, aOuterFront, fOffsetLen, bCharacterMode);
398 basegfx::B3DHomMatrix aTransform;
399 aTransform.translate(0.0, 0.0, fOffsetLen);
400 rSliceVector.emplace_back(aOuterFront, aTransform, SLICETYPE3D_FRONTCAP);
401 }
402
403 if(bCloseBack)
404 {
405 const double fOffsetLen((fAverageLength / 12.0) * fDiagonal);
406 impGetOuterPolyPolygon(aBack, aOuterBack, fOffsetLen, bCharacterMode);
407 aTransformBack.translate(0.0, 0.0, -fOffsetLen);
408 aTransformBack.rotate(0.0, fRotation, 0.0);
409 }
410 }
411
412 // add start polygon (a = 0)
413 if(!bClosedRotation)
414 {
415 rSliceVector.emplace_back(aFront, basegfx::B3DHomMatrix());
416 }
417
418 // create segments (a + 1 .. nSteps)
419 const double fStepSize(1.0 / static_cast<double>(nSteps));
420
421 for(sal_uInt32 a(0); a < nSteps; a++)
422 {
423 const double fStep(static_cast<double>(a + 1) * fStepSize);
424 basegfx::B2DPolyPolygon aNewPoly(bBackScale ? basegfx::utils::interpolate(aFront, aBack, fStep) : aFront);
425 basegfx::B3DHomMatrix aNewMat;
426 aNewMat.rotate(0.0, fRotation * fStep, 0.0);
427 rSliceVector.emplace_back(aNewPoly, aNewMat);
428 }
429
430 if(bCloseBack)
431 {
432 rSliceVector.emplace_back(aOuterBack, aTransformBack, SLICETYPE3D_BACKCAP);
433 }
434 }
435 }
436
438 Slice3DVector& rSliceVector,
439 const basegfx::B2DPolyPolygon& rSource,
440 double fBackScale,
441 double fDiagonal,
442 double fDepth,
443 bool bCharacterMode,
444 bool bCloseFront,
445 bool bCloseBack)
446 {
448 {
449 // no depth, just one plane
450 rSliceVector.emplace_back(rSource, basegfx::B3DHomMatrix());
451 }
452 else
453 {
454 // there is depth, create Polygons for front,back and their default depth positions
455 basegfx::B2DPolyPolygon aFront(rSource);
456 basegfx::B2DPolyPolygon aBack(rSource);
457 const bool bBackScale(!basegfx::fTools::equal(fBackScale, 1.0));
458 double fZFront(fDepth); // default depth for aFront
459 double fZBack(0.0); // default depth for aBack
460 basegfx::B2DPolyPolygon aOuterBack;
461
462 if(bBackScale)
463 {
464 // avoid null zoom
465 if(basegfx::fTools::equalZero(fBackScale))
466 {
467 fBackScale = 0.000001;
468 }
469
470 // aFront is scaled compared to aBack, create scaled version
471 aFront = impScalePolyPolygonOnCenter(aFront, fBackScale);
472 }
473
474 if(bCloseFront)
475 {
476 const double fOffset(fDepth * fDiagonal * 0.5);
477 fZFront = fDepth - fOffset;
478 basegfx::B2DPolyPolygon aOuterFront;
479 impGetOuterPolyPolygon(aFront, aOuterFront, fOffset, bCharacterMode);
480 basegfx::B3DHomMatrix aTransformFront;
481 aTransformFront.translate(0.0, 0.0, fDepth);
482 rSliceVector.emplace_back(aOuterFront, aTransformFront, SLICETYPE3D_FRONTCAP);
483 }
484
485 if(bCloseBack)
486 {
487 const double fOffset(fDepth * fDiagonal * 0.5);
488 fZBack = fOffset;
489 impGetOuterPolyPolygon(aBack, aOuterBack, fOffset, bCharacterMode);
490 }
491
492 // add front and back polygons at evtl. changed depths
493 {
494 basegfx::B3DHomMatrix aTransformA, aTransformB;
495
496 aTransformA.translate(0.0, 0.0, fZFront);
497 rSliceVector.emplace_back(aFront, aTransformA);
498
499 aTransformB.translate(0.0, 0.0, fZBack);
500 rSliceVector.emplace_back(aBack, aTransformB);
501 }
502
503 if(bCloseBack)
504 {
505 rSliceVector.emplace_back(aOuterBack, basegfx::B3DHomMatrix(), SLICETYPE3D_BACKCAP);
506 }
507 }
508 }
509
511 {
513 const sal_uInt32 nNumSlices(rSliceVector.size());
514
515 if(nNumSlices)
516 {
517 const sal_uInt32 nSlideSubPolygonCount(rSliceVector[0].getB3DPolyPolygon().count());
518
519 for(sal_uInt32 b(0); b < nSlideSubPolygonCount; b++)
520 {
521 const sal_uInt32 nSubPolygonPointCount(rSliceVector[0].getB3DPolyPolygon().getB3DPolygon(b).count());
522
523 for(sal_uInt32 c(0); c < nSubPolygonPointCount; c++)
524 {
526
527 for(sal_uInt32 d(0); d < nNumSlices; d++)
528 {
529 const bool bSamePolygonCount(nSlideSubPolygonCount == rSliceVector[d].getB3DPolyPolygon().count());
530 const bool bSamePointCount(nSubPolygonPointCount == rSliceVector[d].getB3DPolyPolygon().getB3DPolygon(b).count());
531
532 if(bSamePolygonCount && bSamePointCount)
533 {
534 aNew.append(rSliceVector[d].getB3DPolyPolygon().getB3DPolygon(b).getB3DPoint(c));
535 }
536 else
537 {
538 OSL_ENSURE(bSamePolygonCount, "Slice tools::PolyPolygon with different Polygon count (!)");
539 OSL_ENSURE(bSamePointCount, "Slice Polygon with different point count (!)");
540 }
541 }
542
543 aNew.setClosed(bCloseHorLines);
544 aRetval.append(aNew);
545 }
546 }
547 }
548
549 return aRetval;
550 }
551
553 {
555 const sal_uInt32 nNumSlices(rSliceVector.size());
556
557 for(sal_uInt32 a(0); a < nNumSlices; a++)
558 {
559 aRetval.append(rSliceVector[a].getB3DPolyPolygon());
560 }
561
562 return aRetval;
563 }
564
566 std::vector< basegfx::B3DPolyPolygon >& rFill,
567 const Slice3DVector& rSliceVector,
568 bool bCreateNormals,
569 bool bSmoothNormals,
570 bool bSmoothLids,
571 bool bClosed,
572 double fSmoothNormalsMix,
573 double fSmoothLidsMix,
574 bool bCreateTextureCoordinates,
575 const basegfx::B2DHomMatrix& rTexTransform)
576 {
577 const sal_uInt32 nNumSlices(rSliceVector.size());
578
579 if(!nNumSlices)
580 return;
581
582 // common parameters
583 const sal_uInt32 nLoopCount(bClosed ? nNumSlices : nNumSlices - 1);
584 basegfx::B3DPolyPolygon aEdgeRounding;
585 sal_uInt32 a;
586
587 // texture parameters
588 double fInvTexHeight(1.0);
589 std::vector<double> aTexHeightArray;
590 basegfx::B3DRange aTexRangeFront;
591 basegfx::B3DRange aTexRangeBack;
592
593 if(bCreateTextureCoordinates)
594 {
595 aTexRangeFront = basegfx::utils::getRange(rSliceVector[0].getB3DPolyPolygon());
596 aTexRangeBack = basegfx::utils::getRange(rSliceVector[nNumSlices - 1].getB3DPolyPolygon());
597
598 if(aTexRangeBack.getDepth() > aTexRangeBack.getWidth())
599 {
600 // last polygon is rotated so that depth is bigger than width, exchange X and Z
601 // for making applyDefaultTextureCoordinatesParallel use Z instead of X for
602 // horizontal texture coordinate
603 aTexRangeBack = basegfx::B3DRange(
604 aTexRangeBack.getMinZ(), aTexRangeBack.getMinY(), aTexRangeBack.getMinX(),
605 aTexRangeBack.getMaxZ(), aTexRangeBack.getMaxY(), aTexRangeBack.getMaxX());
606 }
607
608 basegfx::B3DPoint aCenter(basegfx::utils::getRange(rSliceVector[0].getB3DPolyPolygon()).getCenter());
609
610 for(a = 0; a < nLoopCount; a++)
611 {
612 const basegfx::B3DPoint aNextCenter(basegfx::utils::getRange(rSliceVector[(a + 1) % nNumSlices].getB3DPolyPolygon()).getCenter());
613 const double fLength(basegfx::B3DVector(aNextCenter - aCenter).getLength());
614 aTexHeightArray.push_back(fLength);
615 aCenter = aNextCenter;
616 }
617
618 const double fTexHeight(std::accumulate(aTexHeightArray.begin(), aTexHeightArray.end(), 0.0));
619
620 if(!basegfx::fTools::equalZero(fTexHeight))
621 {
622 fInvTexHeight = 1.0 / fTexHeight;
623 }
624 }
625
626 if(nLoopCount)
627 {
628 double fTexHeightPos(0.0);
629 for(a = 0; a < nLoopCount; a++)
630 {
631 const Slice3D& rSliceA(rSliceVector[a]);
632 const Slice3D& rSliceB(rSliceVector[(a + 1) % nNumSlices]);
633 const bool bAcceptPair(SLICETYPE3D_REGULAR == rSliceA.getSliceType() && SLICETYPE3D_REGULAR == rSliceB.getSliceType());
636
637 if(bAcceptPair)
638 {
639 if(bCreateNormals)
640 {
641 impCreateInBetweenNormals(aPolB, aPolA);
642 }
643
644 {
645 const sal_uInt32 nIndPrev((a + nNumSlices - 1) % nNumSlices);
646 const Slice3D& rSlicePrev(rSliceVector[nIndPrev]);
647 basegfx::B3DPolyPolygon aPrev(rSlicePrev.getB3DPolyPolygon());
649
650 if(SLICETYPE3D_FRONTCAP == rSlicePrev.getSliceType())
651 {
652 basegfx::B3DPolyPolygon aFront(rSlicePrev.getB3DPolyPolygon());
653 const bool bHasSlant(aPolAA != aPrev);
654
655 if(bCreateTextureCoordinates)
656 {
657 aFront = basegfx::utils::applyDefaultTextureCoordinatesParallel(aFront, aTexRangeFront);
658 }
659
660 if(bCreateNormals)
661 {
662 basegfx::B3DVector aNormal(0.0, 0.0, -1.0);
663
664 if(aFront.count())
665 {
666 aNormal = -aFront.getB3DPolygon(0).getNormal();
667 }
668
669 impSetNormal(aFront, aNormal);
670
671 if(bHasSlant)
672 {
673 impCreateInBetweenNormals(aPolAA, aPrev);
674
675 if(bSmoothNormals)
676 {
677 // smooth and copy
678 impMixNormals(aPolA, aPolAA, fSmoothNormalsMix);
679 aPolAA = aPolA;
680 }
681 else
682 {
683 // take over from surface
684 aPolAA = aPolA;
685 }
686
687 if(bSmoothLids)
688 {
689 // smooth and copy
690 impMixNormals(aFront, aPrev, fSmoothLidsMix);
691 aPrev = aFront;
692 }
693 else
694 {
695 // take over from front
696 aPrev = aFront;
697 }
698 }
699 else
700 {
701 if(bSmoothNormals)
702 {
703 // smooth
704 impMixNormals(aPolA, aFront, fSmoothNormalsMix);
705 }
706
707 if(bSmoothLids)
708 {
709 // smooth and copy
710 impMixNormals(aFront, aPolA, fSmoothLidsMix);
711 aPolA = aFront;
712 }
713 }
714 }
715
716 if(bHasSlant)
717 {
718 double fTexStart{};
719 double fTexStop{};
720 if(bCreateTextureCoordinates)
721 {
722 fTexStart = fTexHeightPos * fInvTexHeight;
723 fTexStop = (fTexHeightPos - aTexHeightArray[(a + nLoopCount - 1) % nLoopCount]) * fInvTexHeight;
724 }
725
726 impAddInBetweenFill(aEdgeRounding, aPolAA, aPrev, fTexStart, fTexStop, bCreateNormals, bCreateTextureCoordinates);
727 }
728
729 aFront.flip();
730 rFill.push_back(aFront);
731 }
732 else
733 {
734 if(bCreateNormals && bSmoothNormals && (nIndPrev != a + 1))
735 {
736 impCreateInBetweenNormals(aPolAA, aPrev);
737 impMixNormals(aPolA, aPolAA, 0.5);
738 }
739 }
740 }
741
742 {
743 const sal_uInt32 nIndNext((a + 2) % nNumSlices);
744 const Slice3D& rSliceNext(rSliceVector[nIndNext]);
745 basegfx::B3DPolyPolygon aNext(rSliceNext.getB3DPolyPolygon());
747
748 if(SLICETYPE3D_BACKCAP == rSliceNext.getSliceType())
749 {
750 basegfx::B3DPolyPolygon aBack(rSliceNext.getB3DPolyPolygon());
751 const bool bHasSlant(aPolBB != aNext);
752
753 if(bCreateTextureCoordinates)
754 {
755 aBack = basegfx::utils::applyDefaultTextureCoordinatesParallel(aBack, aTexRangeBack);
756 }
757
758 if(bCreateNormals)
759 {
760 const basegfx::B3DVector aNormal(aBack.count() ? aBack.getB3DPolygon(0).getNormal() : basegfx::B3DVector(0.0, 0.0, 1.0));
761 impSetNormal(aBack, aNormal);
762
763 if(bHasSlant)
764 {
765 impCreateInBetweenNormals(aNext, aPolBB);
766
767 if(bSmoothNormals)
768 {
769 // smooth and copy
770 impMixNormals(aPolB, aPolBB, fSmoothNormalsMix);
771 aPolBB = aPolB;
772 }
773 else
774 {
775 // take over from surface
776 aPolBB = aPolB;
777 }
778
779 if(bSmoothLids)
780 {
781 // smooth and copy
782 impMixNormals(aBack, aNext, fSmoothLidsMix);
783 aNext = aBack;
784 }
785 else
786 {
787 // take over from back
788 aNext = aBack;
789 }
790 }
791 else
792 {
793 if(bSmoothNormals)
794 {
795 // smooth
796 impMixNormals(aPolB, aBack, fSmoothNormalsMix);
797 }
798
799 if(bSmoothLids)
800 {
801 // smooth and copy
802 impMixNormals(aBack, aPolB, fSmoothLidsMix);
803 aPolB = aBack;
804 }
805 }
806 }
807
808 if(bHasSlant)
809 {
810 double fTexStart{};
811 double fTexStop{};
812 if(bCreateTextureCoordinates)
813 {
814 fTexStart = (fTexHeightPos + aTexHeightArray[a] + aTexHeightArray[(a + 1) % nLoopCount]) * fInvTexHeight;
815 fTexStop = (fTexHeightPos + aTexHeightArray[a]) * fInvTexHeight;
816 }
817
818 impAddInBetweenFill(aEdgeRounding, aNext, aPolBB, fTexStart, fTexStop, bCreateNormals, bCreateTextureCoordinates);
819 }
820
821 rFill.push_back(aBack);
822 }
823 else
824 {
825 if(bCreateNormals && bSmoothNormals && (nIndNext != a))
826 {
827 impCreateInBetweenNormals(aNext, aPolBB);
828 impMixNormals(aPolB, aPolBB, 0.5);
829 }
830 }
831 }
832
833 double fTexStart{};
834 double fTexStop{};
835 if(bCreateTextureCoordinates)
836 {
837 fTexStart = (fTexHeightPos + aTexHeightArray[a]) * fInvTexHeight;
838 fTexStop = fTexHeightPos * fInvTexHeight;
839 }
840
841 impAddInBetweenFill(aEdgeRounding, aPolB, aPolA, fTexStart, fTexStop, bCreateNormals, bCreateTextureCoordinates);
842 }
843
844 if(bCreateTextureCoordinates)
845 {
846 fTexHeightPos += aTexHeightArray[a];
847 }
848 }
849 }
850 else
851 {
852 // no loop, but a single slice (1 == nNumSlices), create a filling from the single
853 // front plane
854 const Slice3D& rSlice(rSliceVector[0]);
856
857 if(bCreateTextureCoordinates)
858 {
859 aFront = basegfx::utils::applyDefaultTextureCoordinatesParallel(aFront, aTexRangeFront);
860 }
861
862 if(bCreateNormals)
863 {
864 basegfx::B3DVector aNormal(0.0, 0.0, -1.0);
865
866 if(aFront.count())
867 {
868 aNormal = -aFront.getB3DPolygon(0).getNormal();
869 }
870
871 impSetNormal(aFront, aNormal);
872 }
873
874 aFront.flip();
875 rFill.push_back(aFront);
876 }
877
878 if(bCreateTextureCoordinates)
879 {
880 aEdgeRounding.transformTextureCoordinates(rTexTransform);
881 }
882
883 for(a = 0; a < aEdgeRounding.count(); a++)
884 {
885 rFill.emplace_back(aEdgeRounding.getB3DPolygon(a));
886 }
887 }
888
890 const geometry::ViewInformation3D& rViewInformation,
891 const basegfx::B3DHomMatrix& rObjectTransform,
892 const basegfx::B3DPolygon& rLoopA,
893 const basegfx::B3DPolygon& rLoopB,
895 {
896 const sal_uInt32 nPointCount(rLoopA.count());
897
898 // with identical polygons there are no outlines
899 if(rLoopA == rLoopB)
900 return;
901
902 if(!(nPointCount && nPointCount == rLoopB.count()))
903 return;
904
905 const basegfx::B3DHomMatrix aObjectTransform(rViewInformation.getObjectToView() * rObjectTransform);
906 const basegfx::B2DPolygon a2DLoopA(basegfx::utils::createB2DPolygonFromB3DPolygon(rLoopA, aObjectTransform));
907 const basegfx::B2DPolygon a2DLoopB(basegfx::utils::createB2DPolygonFromB3DPolygon(rLoopB, aObjectTransform));
908 const basegfx::B2DPoint a2DCenterA(a2DLoopA.getB2DRange().getCenter());
909 const basegfx::B2DPoint a2DCenterB(a2DLoopB.getB2DRange().getCenter());
910
911 // without detectable Y-Axis there are no outlines
912 if(a2DCenterA.equal(a2DCenterB))
913 return;
914
915 // search for outmost left and right inter-loop-edges which do not cut the loops
916 const basegfx::B2DPoint aCommonCenter(basegfx::average(a2DCenterA, a2DCenterB));
917 const basegfx::B2DVector aAxisVector(a2DCenterA - a2DCenterB);
918 double fMaxLeft(0.0);
919 double fMaxRight(0.0);
920 sal_uInt32 nIndexLeft(0);
921 sal_uInt32 nIndexRight(0);
922
923 for(sal_uInt32 a(0); a < nPointCount; a++)
924 {
925 const basegfx::B2DPoint aStart(a2DLoopA.getB2DPoint(a));
926 const basegfx::B2DPoint aEnd(a2DLoopB.getB2DPoint(a));
927 const basegfx::B2DPoint aMiddle(basegfx::average(aStart, aEnd));
928
929 if(!basegfx::utils::isInside(a2DLoopA, aMiddle))
930 {
931 if(!basegfx::utils::isInside(a2DLoopB, aMiddle))
932 {
933 if(!impHasCutWith(a2DLoopA, aStart, aEnd))
934 {
935 if(!impHasCutWith(a2DLoopB, aStart, aEnd))
936 {
937 const basegfx::B2DVector aCandidateVector(aMiddle - aCommonCenter);
938 const double fCross(aCandidateVector.cross(aAxisVector));
939 const double fDistance(aCandidateVector.getLength());
940
941 if(fCross > 0.0)
942 {
943 if(fDistance > fMaxLeft)
944 {
945 fMaxLeft = fDistance;
946 nIndexLeft = a;
947 }
948 }
949 else if(fCross < 0.0)
950 {
951 if(fDistance > fMaxRight)
952 {
953 fMaxRight = fDistance;
954 nIndexRight = a;
955 }
956 }
957 }
958 }
959 }
960 }
961 }
962
963 if(fMaxLeft != 0.0)
964 {
965 basegfx::B3DPolygon aToBeAdded;
966 aToBeAdded.append(rLoopA.getB3DPoint(nIndexLeft));
967 aToBeAdded.append(rLoopB.getB3DPoint(nIndexLeft));
968 rTarget.append(aToBeAdded);
969 }
970
971 if(fMaxRight != 0.0)
972 {
973 basegfx::B3DPolygon aToBeAdded;
974 aToBeAdded.append(rLoopA.getB3DPoint(nIndexRight));
975 aToBeAdded.append(rLoopB.getB3DPoint(nIndexRight));
976 rTarget.append(aToBeAdded);
977 }
978 }
979
980} // end of namespace
981
982/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
double d
void translate(double fX, double fY)
void scale(double fX, double fY)
void transform(const basegfx::B2DHomMatrix &rMatrix)
basegfx::B2DPoint const & getB2DPoint(sal_uInt32 nIndex) const
B2DRange const & getB2DRange() const
sal_uInt32 count() const
B2DPoint getCenter() const
double cross(const B2DVector &rVec) const
double getLength() const
void rotate(double fAngleX, double fAngleY, double fAngleZ)
void translate(double fX, double fY, double fZ)
void transformTextureCoordinates(const B2DHomMatrix &rMatrix)
sal_uInt32 count() const
void setB3DPolygon(sal_uInt32 nIndex, const B3DPolygon &rPolygon)
void append(const B3DPolygon &rPolygon, sal_uInt32 nCount=1)
B3DPolygon const & getB3DPolygon(sal_uInt32 nIndex) const
void append(const B3DPoint &rPoint, sal_uInt32 nCount=1)
void setTextureCoordinate(sal_uInt32 nIndex, const B2DPoint &rValue)
B3DPoint const & getB3DPoint(sal_uInt32 nIndex) const
sal_uInt32 count() const
void setNormal(sal_uInt32 nIndex, const B3DVector &rValue)
B3DVector const & getNormal() const
void setClosed(bool bNew)
double getMinZ() const
double getMaxZ() const
double getMinX() const
double getDepth() const
double getWidth() const
double getMinY() const
double getMaxX() const
double getMaxY() const
TYPE getMaxX() const
TYPE getMinX() const
bool equal(const Tuple2D< TYPE > &rTup) const
const basegfx::B3DHomMatrix & getObjectToView() const
for convenience, the linear combination of the above four transformations is offered
const basegfx::B3DPolyPolygon & getB3DPolyPolygon() const
FilterGroup & rTarget
uno_Any a
bool more(const T &rfValA, const T &rfValB)
bool equalZero(const T &rfVal)
bool equal(T const &rfValA, T const &rfValB)
CutFlagValue findCut(const B2DPoint &rEdge1Start, const B2DVector &rEdge1Delta, const B2DPoint &rEdge2Start, const B2DVector &rEdge2Delta, CutFlagValue aCutFlags, double *pCut1, double *pCut2)
double getLength(const B2DPolygon &rCandidate)
B2DPolygon interpolate(const B2DPolygon &rOld1, const B2DPolygon &rOld2, double t)
B2DPolygon growInNormalDirection(const B2DPolygon &rCandidate, double fValue)
bool isInside(const B2DPolygon &rCandidate, const B2DPoint &rPoint, bool bWithBorder)
B2DPolygon createB2DPolygonFromB3DPolygon(const B3DPolygon &rCandidate, const B3DHomMatrix &rMat)
B3DPolygon applyDefaultTextureCoordinatesParallel(const B3DPolygon &rCandidate, const B3DRange &rRange, bool bChangeX, bool bChangeY)
B2DRange getRange(const B2DPolygon &rCandidate)
B2DTuple average(const B2DTuple &rOld1, const B2DTuple &rOld2)
SdrPrimitive3D class.
void createLatheSlices(Slice3DVector &rSliceVector, const basegfx::B2DPolyPolygon &rSource, double fBackScale, double fDiagonal, double fRotation, sal_uInt32 nSteps, bool bCharacterMode, bool bCloseFront, bool bCloseBack)
helpers for creation
basegfx::B3DPolyPolygon extractHorizontalLinesFromSlice(const Slice3DVector &rSliceVector, bool bCloseHorLines)
helpers for geometry extraction
void extractPlanesFromSlice(std::vector< basegfx::B3DPolyPolygon > &rFill, const Slice3DVector &rSliceVector, bool bCreateNormals, bool bSmoothNormals, bool bSmoothLids, bool bClosed, double fSmoothNormalsMix, double fSmoothLidsMix, bool bCreateTextureCoordinates, const basegfx::B2DHomMatrix &rTexTransform)
void createExtrudeSlices(Slice3DVector &rSliceVector, const basegfx::B2DPolyPolygon &rSource, double fBackScale, double fDiagonal, double fDepth, bool bCharacterMode, bool bCloseFront, bool bCloseBack)
::std::vector< Slice3D > Slice3DVector
typedef for a group of Slice3Ds
void createReducedOutlines(const geometry::ViewInformation3D &rViewInformation, const basegfx::B3DHomMatrix &rObjectTransform, const basegfx::B3DPolygon &rLoopA, const basegfx::B3DPolygon &rLoopB, basegfx::B3DPolyPolygon &rTarget)
basegfx::B3DPolyPolygon extractVerticalLinesFromSlice(const Slice3DVector &rSliceVector)