xref: /AOO41X/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