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