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/primitive2d/sceneprimitive2d.hxx>
28 #include <basegfx/tools/canvastools.hxx>
29 #include <basegfx/polygon/b2dpolygontools.hxx>
30 #include <basegfx/polygon/b2dpolygon.hxx>
31 #include <basegfx/polygon/b2dpolygonclipper.hxx>
32 #include <basegfx/polygon/b2dpolypolygontools.hxx>
33 #include <basegfx/matrix/b2dhommatrix.hxx>
34 #include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
35 #include <drawinglayer/processor3d/zbufferprocessor3d.hxx>
36 #include <drawinglayer/processor3d/shadow3dextractor.hxx>
37 #include <drawinglayer/geometry/viewinformation2d.hxx>
38 #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
39 #include <svtools/optionsdrawinglayer.hxx>
40 #include <drawinglayer/processor3d/geometry2dextractor.hxx>
41 #include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
42 
43 //////////////////////////////////////////////////////////////////////////////
44 
45 using namespace com::sun::star;
46 
47 //////////////////////////////////////////////////////////////////////////////
48 
49 namespace drawinglayer
50 {
51 	namespace primitive2d
52 	{
impGetShadow3D(const geometry::ViewInformation2D &) const53 		bool ScenePrimitive2D::impGetShadow3D(const geometry::ViewInformation2D& /*rViewInformation*/) const
54 		{
55             ::osl::MutexGuard aGuard( m_aMutex );
56 
57 			// create on demand
58 			if(!mbShadow3DChecked && getChildren3D().hasElements())
59 			{
60 				basegfx::B3DVector aLightNormal;
61                 const double fShadowSlant(getSdrSceneAttribute().getShadowSlant());
62 				const basegfx::B3DRange aScene3DRange(primitive3d::getB3DRangeFromPrimitive3DSequence(getChildren3D(), getViewInformation3D()));
63 
64 				if(maSdrLightingAttribute.getLightVector().size())
65 				{
66 					// get light normal from first light and normalize
67 					aLightNormal = maSdrLightingAttribute.getLightVector()[0].getDirection();
68 					aLightNormal.normalize();
69 				}
70 
71 				// create shadow extraction processor
72 				processor3d::Shadow3DExtractingProcessor aShadowProcessor(
73 					getViewInformation3D(),
74 					getObjectTransformation(),
75 					aLightNormal,
76 					fShadowSlant,
77                     aScene3DRange);
78 
79 				// process local primitives
80 				aShadowProcessor.process(getChildren3D());
81 
82 				// fetch result and set checked flag
83 				const_cast< ScenePrimitive2D* >(this)->maShadowPrimitives = aShadowProcessor.getPrimitive2DSequence();
84 				const_cast< ScenePrimitive2D* >(this)->mbShadow3DChecked = true;
85 			}
86 
87 			// return if there are shadow primitives
88 			return maShadowPrimitives.hasElements();
89 		}
90 
calculateDiscreteSizes(const geometry::ViewInformation2D & rViewInformation,basegfx::B2DRange & rDiscreteRange,basegfx::B2DRange & rVisibleDiscreteRange,basegfx::B2DRange & rUnitVisibleRange) const91         void ScenePrimitive2D::calculateDiscreteSizes(
92 			const geometry::ViewInformation2D& rViewInformation,
93 			basegfx::B2DRange& rDiscreteRange,
94 			basegfx::B2DRange& rVisibleDiscreteRange,
95 			basegfx::B2DRange& rUnitVisibleRange) const
96 		{
97 			// use unit range and transform to discrete coordinates
98 			rDiscreteRange = basegfx::B2DRange(0.0, 0.0, 1.0, 1.0);
99 			rDiscreteRange.transform(rViewInformation.getObjectToViewTransformation() * getObjectTransformation());
100 
101 			// clip it against discrete Viewport (if set)
102 			rVisibleDiscreteRange = rDiscreteRange;
103 
104 			if(!rViewInformation.getViewport().isEmpty())
105 			{
106 				rVisibleDiscreteRange.intersect(rViewInformation.getDiscreteViewport());
107 			}
108 
109 			if(rVisibleDiscreteRange.isEmpty())
110 			{
111 				rUnitVisibleRange = rVisibleDiscreteRange;
112 			}
113 			else
114 			{
115 				// create UnitVisibleRange containing unit range values [0.0 .. 1.0] describing
116 				// the relative position of rVisibleDiscreteRange inside rDiscreteRange
117 				const double fDiscreteScaleFactorX(basegfx::fTools::equalZero(rDiscreteRange.getWidth()) ? 1.0 : 1.0 / rDiscreteRange.getWidth());
118 				const double fDiscreteScaleFactorY(basegfx::fTools::equalZero(rDiscreteRange.getHeight()) ? 1.0 : 1.0 / rDiscreteRange.getHeight());
119 
120 				const double fMinX(basegfx::fTools::equal(rVisibleDiscreteRange.getMinX(), rDiscreteRange.getMinX())
121 					? 0.0
122 					: (rVisibleDiscreteRange.getMinX() - rDiscreteRange.getMinX()) * fDiscreteScaleFactorX);
123 				const double fMinY(basegfx::fTools::equal(rVisibleDiscreteRange.getMinY(), rDiscreteRange.getMinY())
124 					? 0.0
125 					: (rVisibleDiscreteRange.getMinY() - rDiscreteRange.getMinY()) * fDiscreteScaleFactorY);
126 
127 				const double fMaxX(basegfx::fTools::equal(rVisibleDiscreteRange.getMaxX(), rDiscreteRange.getMaxX())
128 					? 1.0
129 					: (rVisibleDiscreteRange.getMaxX() - rDiscreteRange.getMinX()) * fDiscreteScaleFactorX);
130 				const double fMaxY(basegfx::fTools::equal(rVisibleDiscreteRange.getMaxY(), rDiscreteRange.getMaxY())
131 					? 1.0
132 					: (rVisibleDiscreteRange.getMaxY() - rDiscreteRange.getMinY()) * fDiscreteScaleFactorY);
133 
134 				rUnitVisibleRange = basegfx::B2DRange(fMinX, fMinY, fMaxX, fMaxY);
135 			}
136 		}
137 
create2DDecomposition(const geometry::ViewInformation2D & rViewInformation) const138 		Primitive2DSequence ScenePrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const
139 		{
140 			Primitive2DSequence aRetval;
141 
142 			// create 2D shadows from contained 3D primitives. This creates the shadow primitives on demand and tells if
143 			// there are some or not. Do this at start, the shadow might still be visible even when the scene is not
144 			if(impGetShadow3D(rViewInformation))
145 			{
146 				// test visibility
147 				const basegfx::B2DRange aShadow2DRange(
148                     getB2DRangeFromPrimitive2DSequence(maShadowPrimitives, rViewInformation));
149 				const basegfx::B2DRange aViewRange(
150                     rViewInformation.getViewport());
151 
152 				if(aViewRange.isEmpty() || aShadow2DRange.overlaps(aViewRange))
153 				{
154 					// add extracted 2d shadows (before 3d scene creations itself)
155 					aRetval = maShadowPrimitives;
156 				}
157 			}
158 
159 			// get the involved ranges (see helper method calculateDiscreteSizes for details)
160 			basegfx::B2DRange aDiscreteRange;
161 			basegfx::B2DRange aVisibleDiscreteRange;
162 			basegfx::B2DRange aUnitVisibleRange;
163 
164             calculateDiscreteSizes(rViewInformation, aDiscreteRange, aVisibleDiscreteRange, aUnitVisibleRange);
165 
166 			if(!aVisibleDiscreteRange.isEmpty())
167 			{
168 				// test if discrete view size (pixel) maybe too big and limit it
169 				double fViewSizeX(aVisibleDiscreteRange.getWidth());
170 				double fViewSizeY(aVisibleDiscreteRange.getHeight());
171 				const double fViewVisibleArea(fViewSizeX * fViewSizeY);
172                 const SvtOptionsDrawinglayer aDrawinglayerOpt;
173 				const double fMaximumVisibleArea(aDrawinglayerOpt.GetQuadratic3DRenderLimit());
174 				double fReduceFactor(1.0);
175 
176 				if(fViewVisibleArea > fMaximumVisibleArea)
177 				{
178 					fReduceFactor = sqrt(fMaximumVisibleArea / fViewVisibleArea);
179 					fViewSizeX *= fReduceFactor;
180 					fViewSizeY *= fReduceFactor;
181 				}
182 
183 				if(rViewInformation.getReducedDisplayQuality())
184 				{
185 					// when reducing the visualisation is allowed (e.g. an OverlayObject
186 					// only needed for dragging), reduce resolution extra
187 					// to speed up dragging interactions
188 					const double fArea(fViewSizeX * fViewSizeY);
189 					double fReducedVisualisationFactor(1.0 / (sqrt(fArea) * (1.0 / 170.0)));
190 
191 					if(fReducedVisualisationFactor > 1.0)
192 					{
193 						fReducedVisualisationFactor = 1.0;
194 					}
195 					else if(fReducedVisualisationFactor < 0.20)
196 					{
197 						fReducedVisualisationFactor = 0.20;
198 					}
199 
200 					if(fReducedVisualisationFactor != 1.0)
201 					{
202 						fReduceFactor *= fReducedVisualisationFactor;
203 						fViewSizeX *= fReducedVisualisationFactor;
204 						fViewSizeY *= fReducedVisualisationFactor;
205 					}
206 				}
207 
208                 // determine the oversample value
209                 static sal_uInt16 nDefaultOversampleValue(3);
210                 const sal_uInt16 nOversampleValue(aDrawinglayerOpt.IsAntiAliasing() ? nDefaultOversampleValue : 0);
211 
212                 geometry::ViewInformation3D aViewInformation3D(getViewInformation3D());
213                 {
214                     // calculate a transformation from DiscreteRange to evtl. rotated/sheared content.
215                     // Start with full transformation from object to discrete units
216                     basegfx::B2DHomMatrix aObjToUnit(rViewInformation.getObjectToViewTransformation() * getObjectTransformation());
217 
218                     // bring to unit coordinates by applying inverse DiscreteRange
219                     aObjToUnit.translate(-aDiscreteRange.getMinX(), -aDiscreteRange.getMinY());
220                     aObjToUnit.scale(1.0 / aDiscreteRange.getWidth(), 1.0 / aDiscreteRange.getHeight());
221 
222                     // calculate transformed user coordinate system
223                     const basegfx::B2DPoint aStandardNull(0.0, 0.0);
224                     const basegfx::B2DPoint aUnitRangeTopLeft(aObjToUnit * aStandardNull);
225                     const basegfx::B2DVector aStandardXAxis(1.0, 0.0);
226                     const basegfx::B2DVector aUnitRangeXAxis(aObjToUnit * aStandardXAxis);
227                     const basegfx::B2DVector aStandardYAxis(0.0, 1.0);
228                     const basegfx::B2DVector aUnitRangeYAxis(aObjToUnit * aStandardYAxis);
229 
230                     if(!aUnitRangeTopLeft.equal(aStandardNull) || !aUnitRangeXAxis.equal(aStandardXAxis) || !aUnitRangeYAxis.equal(aStandardYAxis))
231                     {
232                         // build transformation from unit range to user coordinate system; the unit range
233                         // X and Y axes are the column vectors, the null point is the offset
234                         basegfx::B2DHomMatrix aUnitRangeToUser;
235 
236                         aUnitRangeToUser.set3x2(
237                             aUnitRangeXAxis.getX(), aUnitRangeYAxis.getX(), aUnitRangeTopLeft.getX(),
238                             aUnitRangeXAxis.getY(), aUnitRangeYAxis.getY(), aUnitRangeTopLeft.getY());
239 
240                         // decompose to allow to apply this to the 3D transformation
241                         basegfx::B2DVector aScale, aTranslate;
242                         double fRotate, fShearX;
243                         aUnitRangeToUser.decompose(aScale, aTranslate, fRotate, fShearX);
244 
245                         // apply before DeviceToView and after Projection, 3D is in range [-1.0 .. 1.0] in X,Y and Z
246                         // and not yet flipped in Y
247                         basegfx::B3DHomMatrix aExtendedProjection(aViewInformation3D.getProjection());
248 
249                         // bring to unit coordiantes, flip Y, leave Z unchanged
250                         aExtendedProjection.scale(0.5, -0.5, 1.0);
251                         aExtendedProjection.translate(0.5, 0.5, 0.0);
252 
253                         // apply extra; Y is flipped now, go with positive shear and rotate values
254                         aExtendedProjection.scale(aScale.getX(), aScale.getY(), 1.0);
255                         aExtendedProjection.shearXZ(fShearX, 0.0);
256                         aExtendedProjection.rotate(0.0, 0.0, fRotate);
257                         aExtendedProjection.translate(aTranslate.getX(), aTranslate.getY(), 0.0);
258 
259                         // back to state after projection
260                         aExtendedProjection.translate(-0.5, -0.5, 0.0);
261                         aExtendedProjection.scale(2.0, -2.0, 1.0);
262 
263                         aViewInformation3D = geometry::ViewInformation3D(
264                             aViewInformation3D.getObjectTransformation(),
265                             aViewInformation3D.getOrientation(),
266                             aExtendedProjection,
267                             aViewInformation3D.getDeviceToView(),
268                             aViewInformation3D.getViewTime(),
269                             aViewInformation3D.getExtendedInformationSequence());
270                     }
271                 }
272 
273                 // calculate logic render size in world coordinates for usage in renderer
274                 const basegfx::B2DHomMatrix aInverseOToV(rViewInformation.getInverseObjectToViewTransformation());
275                 const double fLogicX((aInverseOToV * basegfx::B2DVector(aDiscreteRange.getWidth() * fReduceFactor, 0.0)).getLength());
276                 const double fLogicY((aInverseOToV * basegfx::B2DVector(0.0, aDiscreteRange.getHeight() * fReduceFactor)).getLength());
277 
278 			    // use default 3D primitive processor to create BitmapEx for aUnitVisiblePart and process
279 			    processor3d::ZBufferProcessor3D aZBufferProcessor3D(
280                     aViewInformation3D,
281 					rViewInformation,
282 				    getSdrSceneAttribute(),
283 				    getSdrLightingAttribute(),
284                     fLogicX,
285                     fLogicY,
286 				    aUnitVisibleRange,
287                     nOversampleValue);
288 
289 			    aZBufferProcessor3D.process(getChildren3D());
290 			    aZBufferProcessor3D.finish();
291 
292                 const_cast< ScenePrimitive2D* >(this)->maOldRenderedBitmap = aZBufferProcessor3D.getBitmapEx();
293 				const Size aBitmapSizePixel(maOldRenderedBitmap.GetSizePixel());
294 
295 				if(aBitmapSizePixel.getWidth() && aBitmapSizePixel.getHeight())
296 				{
297 					// create transform for the created bitmap in discrete coordinates first.
298 					basegfx::B2DHomMatrix aNew2DTransform;
299 
300                     aNew2DTransform.set(0, 0, aVisibleDiscreteRange.getWidth());
301 					aNew2DTransform.set(1, 1, aVisibleDiscreteRange.getHeight());
302 					aNew2DTransform.set(0, 2, aVisibleDiscreteRange.getMinX());
303 					aNew2DTransform.set(1, 2, aVisibleDiscreteRange.getMinY());
304 
305 					// transform back to world coordinates for usage in primitive creation
306 					aNew2DTransform *= aInverseOToV;
307 
308 					// create bitmap primitive and add
309 					const Primitive2DReference xRef(new BitmapPrimitive2D(maOldRenderedBitmap, aNew2DTransform));
310 					appendPrimitive2DReferenceToPrimitive2DSequence(aRetval, xRef);
311 
312 					// test: Allow to add an outline in the debugger when tests are needed
313 					static bool bAddOutlineToCreated3DSceneRepresentation(false);
314 
315 					if(bAddOutlineToCreated3DSceneRepresentation)
316 					{
317 						basegfx::B2DPolygon aOutline(basegfx::tools::createUnitPolygon());
318 						aOutline.transform(aNew2DTransform);
319 						const Primitive2DReference xRef2(new PolygonHairlinePrimitive2D(aOutline, basegfx::BColor(1.0, 0.0, 0.0)));
320 						appendPrimitive2DReferenceToPrimitive2DSequence(aRetval, xRef2);
321 					}
322 				}
323 			}
324 
325 			return aRetval;
326 		}
327 
getGeometry2D() const328 		Primitive2DSequence ScenePrimitive2D::getGeometry2D() const
329 		{
330 			Primitive2DSequence aRetval;
331 
332             // create 2D projected geometry from 3D geometry
333 			if(getChildren3D().hasElements())
334 			{
335 				// create 2D geometry extraction processor
336 				processor3d::Geometry2DExtractingProcessor aGeometryProcessor(
337 					getViewInformation3D(),
338 					getObjectTransformation());
339 
340 				// process local primitives
341 				aGeometryProcessor.process(getChildren3D());
342 
343 				// fetch result
344 				aRetval = aGeometryProcessor.getPrimitive2DSequence();
345 			}
346 
347 			return aRetval;
348 		}
349 
getShadow2D(const geometry::ViewInformation2D & rViewInformation) const350 		Primitive2DSequence ScenePrimitive2D::getShadow2D(const geometry::ViewInformation2D& rViewInformation) const
351 		{
352 			Primitive2DSequence aRetval;
353 
354 			// create 2D shadows from contained 3D primitives
355 			if(impGetShadow3D(rViewInformation))
356 			{
357 				// add extracted 2d shadows (before 3d scene creations itself)
358 				aRetval = maShadowPrimitives;
359 			}
360 
361 			return aRetval;
362 		}
363 
tryToCheckLastVisualisationDirectHit(const basegfx::B2DPoint & rLogicHitPoint,bool & o_rResult) const364         bool ScenePrimitive2D::tryToCheckLastVisualisationDirectHit(const basegfx::B2DPoint& rLogicHitPoint, bool& o_rResult) const
365         {
366             if(!maOldRenderedBitmap.IsEmpty() && !maOldUnitVisiblePart.isEmpty())
367             {
368                 basegfx::B2DHomMatrix aInverseSceneTransform(getObjectTransformation());
369                 aInverseSceneTransform.invert();
370                 const basegfx::B2DPoint aRelativePoint(aInverseSceneTransform * rLogicHitPoint);
371 
372                 if(maOldUnitVisiblePart.isInside(aRelativePoint))
373                 {
374                     // calculate coordinates relative to visualized part
375                     double fDivisorX(maOldUnitVisiblePart.getWidth());
376                     double fDivisorY(maOldUnitVisiblePart.getHeight());
377 
378                     if(basegfx::fTools::equalZero(fDivisorX))
379                     {
380                         fDivisorX = 1.0;
381                     }
382 
383                     if(basegfx::fTools::equalZero(fDivisorY))
384                     {
385                         fDivisorY = 1.0;
386                     }
387 
388                     const double fRelativeX((aRelativePoint.getX() - maOldUnitVisiblePart.getMinX()) / fDivisorX);
389                     const double fRelativeY((aRelativePoint.getY() - maOldUnitVisiblePart.getMinY()) / fDivisorY);
390 
391                     // combine with real BitmapSizePixel to get bitmap coordinates
392     				const Size aBitmapSizePixel(maOldRenderedBitmap.GetSizePixel());
393                     const sal_Int32 nX(basegfx::fround(fRelativeX * aBitmapSizePixel.Width()));
394                     const sal_Int32 nY(basegfx::fround(fRelativeY * aBitmapSizePixel.Height()));
395 
396                     // try to get a statement about transparency in that pixel
397                     o_rResult = (0xff != maOldRenderedBitmap.GetTransparency(nX, nY));
398                     return true;
399                 }
400             }
401 
402             return false;
403         }
404 
ScenePrimitive2D(const primitive3d::Primitive3DSequence & rxChildren3D,const attribute::SdrSceneAttribute & rSdrSceneAttribute,const attribute::SdrLightingAttribute & rSdrLightingAttribute,const basegfx::B2DHomMatrix & rObjectTransformation,const geometry::ViewInformation3D & rViewInformation3D)405 		ScenePrimitive2D::ScenePrimitive2D(
406 			const primitive3d::Primitive3DSequence& rxChildren3D,
407 			const attribute::SdrSceneAttribute& rSdrSceneAttribute,
408 			const attribute::SdrLightingAttribute& rSdrLightingAttribute,
409 			const basegfx::B2DHomMatrix& rObjectTransformation,
410 			const geometry::ViewInformation3D& rViewInformation3D)
411 		:	BufferedDecompositionPrimitive2D(),
412 			mxChildren3D(rxChildren3D),
413 			maSdrSceneAttribute(rSdrSceneAttribute),
414 			maSdrLightingAttribute(rSdrLightingAttribute),
415 			maObjectTransformation(rObjectTransformation),
416 			maViewInformation3D(rViewInformation3D),
417             maShadowPrimitives(),
418 			mbShadow3DChecked(false),
419 			mfOldDiscreteSizeX(0.0),
420 			mfOldDiscreteSizeY(0.0),
421 			maOldUnitVisiblePart(),
422             maOldRenderedBitmap()
423 		{
424 		}
425 
operator ==(const BasePrimitive2D & rPrimitive) const426 		bool ScenePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
427 		{
428 			if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
429 			{
430 				const ScenePrimitive2D& rCompare = (ScenePrimitive2D&)rPrimitive;
431 
432 				return (primitive3d::arePrimitive3DSequencesEqual(getChildren3D(), rCompare.getChildren3D())
433 					&& getSdrSceneAttribute() == rCompare.getSdrSceneAttribute()
434 					&& getSdrLightingAttribute() == rCompare.getSdrLightingAttribute()
435 					&& getObjectTransformation() == rCompare.getObjectTransformation()
436 					&& getViewInformation3D() == rCompare.getViewInformation3D());
437 			}
438 
439 			return false;
440 		}
441 
getB2DRange(const geometry::ViewInformation2D & rViewInformation) const442 		basegfx::B2DRange ScenePrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
443 		{
444 			// transform unit range to discrete coordinate range
445 			basegfx::B2DRange aRetval(0.0, 0.0, 1.0, 1.0);
446 			aRetval.transform(rViewInformation.getObjectToViewTransformation() * getObjectTransformation());
447 
448 			// force to discrete expanded bounds (it grows, so expanding works perfectly well)
449 			aRetval.expand(basegfx::B2DTuple(floor(aRetval.getMinX()), floor(aRetval.getMinY())));
450 			aRetval.expand(basegfx::B2DTuple(ceil(aRetval.getMaxX()), ceil(aRetval.getMaxY())));
451 
452 			// transform back from discrete (view) to world coordinates
453 			aRetval.transform(rViewInformation.getInverseObjectToViewTransformation());
454 
455 			// expand by evtl. existing shadow primitives
456 			if(impGetShadow3D(rViewInformation))
457 			{
458 				const basegfx::B2DRange aShadow2DRange(getB2DRangeFromPrimitive2DSequence(maShadowPrimitives, rViewInformation));
459 
460 				if(!aShadow2DRange.isEmpty())
461 				{
462 					aRetval.expand(aShadow2DRange);
463 				}
464 			}
465 
466 			return aRetval;
467 		}
468 
get2DDecomposition(const geometry::ViewInformation2D & rViewInformation) const469 		Primitive2DSequence ScenePrimitive2D::get2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const
470 		{
471 			::osl::MutexGuard aGuard( m_aMutex );
472 
473 			// get the involved ranges (see helper method calculateDiscreteSizes for details)
474 		    basegfx::B2DRange aDiscreteRange;
475 		    basegfx::B2DRange aUnitVisibleRange;
476 			bool bNeedNewDecomposition(false);
477 			bool bDiscreteSizesAreCalculated(false);
478 
479 			if(getBuffered2DDecomposition().hasElements())
480 			{
481 			    basegfx::B2DRange aVisibleDiscreteRange;
482 			    calculateDiscreteSizes(rViewInformation, aDiscreteRange, aVisibleDiscreteRange, aUnitVisibleRange);
483 				bDiscreteSizesAreCalculated = true;
484 
485 				// needs to be painted when the new part is not part of the last
486                 // decomposition
487 				if(!maOldUnitVisiblePart.isInside(aUnitVisibleRange))
488                 {
489 					bNeedNewDecomposition = true;
490                 }
491 
492                 // display has changed and cannot be reused when resolution got bigger. It
493                 // can be reused when resolution got smaller, though.
494 				if(!bNeedNewDecomposition)
495 				{
496 				    if(basegfx::fTools::more(aDiscreteRange.getWidth(), mfOldDiscreteSizeX) ||
497 					    basegfx::fTools::more(aDiscreteRange.getHeight(), mfOldDiscreteSizeY))
498 				    {
499 					    bNeedNewDecomposition = true;
500 				    }
501 				}
502 			}
503 
504 			if(bNeedNewDecomposition)
505 			{
506 				// conditions of last local decomposition have changed, delete
507 				const_cast< ScenePrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DSequence());
508 			}
509 
510 			if(!getBuffered2DDecomposition().hasElements())
511 			{
512 				if(!bDiscreteSizesAreCalculated)
513 				{
514 					basegfx::B2DRange aVisibleDiscreteRange;
515 					calculateDiscreteSizes(rViewInformation, aDiscreteRange, aVisibleDiscreteRange, aUnitVisibleRange);
516 				}
517 
518 				// remember last used NewDiscreteSize and NewUnitVisiblePart
519 				ScenePrimitive2D* pThat = const_cast< ScenePrimitive2D* >(this);
520 				pThat->mfOldDiscreteSizeX = aDiscreteRange.getWidth();
521 				pThat->mfOldDiscreteSizeY = aDiscreteRange.getHeight();
522 				pThat->maOldUnitVisiblePart = aUnitVisibleRange;
523 			}
524 
525 			// use parent implementation
526 			return BufferedDecompositionPrimitive2D::get2DDecomposition(rViewInformation);
527 		}
528 
529 		// provide unique ID
530 		ImplPrimitrive2DIDBlock(ScenePrimitive2D, PRIMITIVE2D_ID_SCENEPRIMITIVE2D)
531 
532 	} // end of namespace primitive2d
533 } // end of namespace drawinglayer
534 
535 //////////////////////////////////////////////////////////////////////////////
536 // eof
537