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