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_svx.hxx"
30 #include <svx/sdr/contact/viewcontactofe3dscene.hxx>
31 #include <svx/polysc3d.hxx>
32 #include <svx/sdr/contact/displayinfo.hxx>
33 #include <svx/sdr/contact/viewobjectcontact.hxx>
34 #include <basegfx/polygon/b2dpolygontools.hxx>
35 #include <basegfx/color/bcolor.hxx>
36 #include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
37 #include <svx/sdr/primitive2d/sdrattributecreator.hxx>
38 #include <svx/sdr/contact/viewobjectcontactofe3dscene.hxx>
39 #include <basegfx/matrix/b2dhommatrix.hxx>
40 #include <basegfx/range/b3drange.hxx>
41 #include <drawinglayer/primitive3d/baseprimitive3d.hxx>
42 #include <svx/sdr/contact/viewcontactofe3d.hxx>
43 #include <drawinglayer/primitive2d/sceneprimitive2d.hxx>
44 #include <drawinglayer/primitive3d/transformprimitive3d.hxx>
45 #include <drawinglayer/primitive2d/sdrdecompositiontools2d.hxx>
46 
47 //////////////////////////////////////////////////////////////////////////////
48 
49 using namespace com::sun::star;
50 
51 //////////////////////////////////////////////////////////////////////////////
52 
53 namespace
54 {
55 	// pActiveVC is only true if ghosted is still activated and maybe needs to be switched off in this path
56 	void createSubPrimitive3DVector(
57 		const sdr::contact::ViewContact& rCandidate,
58 		drawinglayer::primitive3d::Primitive3DSequence& o_rAllTarget,
59 		drawinglayer::primitive3d::Primitive3DSequence* o_pVisibleTarget,
60 		const SetOfByte* pVisibleLayerSet,
61 		const bool bTestSelectedVisibility)
62 	{
63 		const sdr::contact::ViewContactOfE3dScene* pViewContactOfE3dScene = dynamic_cast< const sdr::contact::ViewContactOfE3dScene* >(&rCandidate);
64 
65 		if(pViewContactOfE3dScene)
66 		{
67 			const sal_uInt32 nChildrenCount(rCandidate.GetObjectCount());
68 
69 			if(nChildrenCount)
70 			{
71 				// provide new collection sequences
72 				drawinglayer::primitive3d::Primitive3DSequence aNewAllTarget;
73 				drawinglayer::primitive3d::Primitive3DSequence aNewVisibleTarget;
74 
75 				// add children recursively
76 				for(sal_uInt32 a(0L); a < nChildrenCount; a++)
77 				{
78 					createSubPrimitive3DVector(
79 						rCandidate.GetViewContact(a),
80 						aNewAllTarget,
81 						o_pVisibleTarget ? &aNewVisibleTarget : 0,
82 						pVisibleLayerSet,
83 						bTestSelectedVisibility);
84 				}
85 
86 				// create transform primitive for the created content combining content and transformtion
87 				const drawinglayer::primitive3d::Primitive3DReference xReference(new drawinglayer::primitive3d::TransformPrimitive3D(
88 					pViewContactOfE3dScene->GetE3dScene().GetTransform(),
89 					aNewAllTarget));
90 
91 				// add created content to all target
92 				drawinglayer::primitive3d::appendPrimitive3DReferenceToPrimitive3DSequence(o_rAllTarget, xReference);
93 
94 				// add created content to visibiel target if exists
95 				if(o_pVisibleTarget)
96 				{
97 					drawinglayer::primitive3d::appendPrimitive3DReferenceToPrimitive3DSequence(*o_pVisibleTarget, xReference);
98 				}
99 			}
100 		}
101 		else
102 		{
103 			// access view independent representation of rCandidate
104 			const sdr::contact::ViewContactOfE3d* pViewContactOfE3d = dynamic_cast< const sdr::contact::ViewContactOfE3d* >(&rCandidate);
105 
106 			if(pViewContactOfE3d)
107 			{
108 				drawinglayer::primitive3d::Primitive3DSequence xPrimitive3DSeq(pViewContactOfE3d->getViewIndependentPrimitive3DSequence());
109 
110 				if(xPrimitive3DSeq.hasElements())
111 				{
112 					// add to all target vector
113 					drawinglayer::primitive3d::appendPrimitive3DSequenceToPrimitive3DSequence(o_rAllTarget, xPrimitive3DSeq);
114 
115 					if(o_pVisibleTarget)
116 					{
117 						// test visibility. Primitive is visible when both tests are true (AND)
118 						bool bVisible(true);
119 
120 						if(pVisibleLayerSet)
121 						{
122 							// test layer visibility
123 							const E3dObject& rE3dObject = pViewContactOfE3d->GetE3dObject();
124 							const SdrLayerID aLayerID(rE3dObject.GetLayer());
125 
126 							bVisible = pVisibleLayerSet->IsSet(aLayerID);
127 						}
128 
129 						if(bVisible && bTestSelectedVisibility)
130 						{
131 							// test selected visibility (see 3D View's DrawMarkedObj implementation)
132 							const E3dObject& rE3dObject = pViewContactOfE3d->GetE3dObject();
133 
134 							bVisible = rE3dObject.GetSelected();
135 						}
136 
137 						if(bVisible && o_pVisibleTarget)
138 						{
139 							// add to visible target vector
140 							drawinglayer::primitive3d::appendPrimitive3DSequenceToPrimitive3DSequence(*o_pVisibleTarget, xPrimitive3DSeq);
141 						}
142 					}
143 				}
144 			}
145 		}
146 	}
147 } // end of anonymous namespace
148 
149 //////////////////////////////////////////////////////////////////////////////
150 
151 namespace sdr
152 {
153 	namespace contact
154 	{
155 		// Create a Object-Specific ViewObjectContact, set ViewContact and
156 		// ObjectContact. Always needs to return something.
157 		ViewObjectContact& ViewContactOfE3dScene::CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact)
158 		{
159 			ViewObjectContact* pRetval = new ViewObjectContactOfE3dScene(rObjectContact, *this);
160 			DBG_ASSERT(pRetval, "ViewContactOfE3dScene::CreateObjectSpecificViewObjectContact() failed (!)");
161 
162 			return *pRetval;
163 		}
164 
165 		ViewContactOfE3dScene::ViewContactOfE3dScene(E3dScene& rScene)
166 		:	ViewContactOfSdrObj(rScene),
167 			maViewInformation3D(),
168 			maObjectTransformation(),
169 			maSdrSceneAttribute(),
170 			maSdrLightingAttribute()
171 		{
172 		}
173 
174 		void ViewContactOfE3dScene::createViewInformation3D(const basegfx::B3DRange& rContentRange)
175 		{
176 			basegfx::B3DHomMatrix aTransformation;
177 			basegfx::B3DHomMatrix aOrientation;
178 			basegfx::B3DHomMatrix aProjection;
179 			basegfx::B3DHomMatrix aDeviceToView;
180 
181 			// create transformation (scene as group's transformation)
182 			// For historical reasons, the outmost scene's transformation is handles as part of the
183 			// view transformation. This means that the BoundRect of the contained 3D Objects is
184 			// without that transformation and makes it necessary to NOT add the first scene to the
185 			// Primitive3DSequence of contained objects.
186 			{
187 				aTransformation = GetE3dScene().GetTransform();
188 			}
189 
190 			// create orientation (world to camera coordinate system)
191 			{
192 				// calculate orientation from VRP, VPN and VUV
193 				const B3dCamera& rSceneCamera = GetE3dScene().GetCameraSet();
194 				const basegfx::B3DPoint aVRP(rSceneCamera.GetVRP());
195 				const basegfx::B3DVector aVPN(rSceneCamera.GetVRP());
196 				const basegfx::B3DVector aVUV(rSceneCamera.GetVUV());
197 
198                 aOrientation.orientation(aVRP, aVPN, aVUV);
199 			}
200 
201 			// create projection (camera coordinate system to relative 2d where X,Y and Z are [0.0 .. 1.0])
202 			{
203 				const basegfx::B3DHomMatrix aWorldToCamera(aOrientation * aTransformation);
204 				basegfx::B3DRange aCameraRange(rContentRange);
205 				aCameraRange.transform(aWorldToCamera);
206 
207 				// remember Z-Values, but change orientation
208 				const double fMinZ(-aCameraRange.getMaxZ());
209 				const double fMaxZ(-aCameraRange.getMinZ());
210 
211 				// construct temorary matrix from world to device. Use unit values here to measure expansion
212 				basegfx::B3DHomMatrix aWorldToDevice(aWorldToCamera);
213 				const drawinglayer::attribute::SdrSceneAttribute& rSdrSceneAttribute = getSdrSceneAttribute();
214 
215 				if(::com::sun::star::drawing::ProjectionMode_PERSPECTIVE == rSdrSceneAttribute.getProjectionMode())
216 				{
217 					aWorldToDevice.frustum(-1.0, 1.0, -1.0, 1.0, fMinZ, fMaxZ);
218 				}
219 				else
220 				{
221 					aWorldToDevice.ortho(-1.0, 1.0, -1.0, 1.0, fMinZ, fMaxZ);
222 				}
223 
224 				// create B3DRange in device. This will create the real used ranges
225 				// in camera space. Do not use the Z-Values, though.
226 				basegfx::B3DRange aDeviceRange(rContentRange);
227 				aDeviceRange.transform(aWorldToDevice);
228 
229 				// set projection
230 				if(::com::sun::star::drawing::ProjectionMode_PERSPECTIVE == rSdrSceneAttribute.getProjectionMode())
231 				{
232 					aProjection.frustum(
233 						aDeviceRange.getMinX(), aDeviceRange.getMaxX(),
234 						aDeviceRange.getMinY(), aDeviceRange.getMaxY(),
235 						fMinZ, fMaxZ);
236 				}
237 				else
238 				{
239 					aProjection.ortho(
240 						aDeviceRange.getMinX(), aDeviceRange.getMaxX(),
241 						aDeviceRange.getMinY(), aDeviceRange.getMaxY(),
242 						fMinZ, fMaxZ);
243 				}
244 			}
245 
246 			// create device to view transform
247 			{
248 				// create standard deviceToView projection for geometry
249 				// input is [-1.0 .. 1.0] in X,Y and Z. bring to [0.0 .. 1.0]. Also
250 				// necessary to flip Y due to screen orientation
251 				// Z is not needed, but will also be brought to [0.0 .. 1.0]
252 				aDeviceToView.scale(0.5, -0.5, 0.5);
253 				aDeviceToView.translate(0.5, 0.5, 0.5);
254 			}
255 
256 			const uno::Sequence< beans::PropertyValue > aEmptyProperties;
257 			maViewInformation3D = drawinglayer::geometry::ViewInformation3D(
258 				aTransformation, aOrientation, aProjection,
259 				aDeviceToView, 0.0, aEmptyProperties);
260 		}
261 
262 		void ViewContactOfE3dScene::createObjectTransformation()
263 		{
264 			// create 2d Object Transformation from relative point in 2d scene to world
265 			const Rectangle& rRectangle = GetE3dScene().GetSnapRect();
266 
267 			maObjectTransformation.set(0, 0, rRectangle.getWidth());
268 			maObjectTransformation.set(1, 1, rRectangle.getHeight());
269 			maObjectTransformation.set(0, 2, rRectangle.Left());
270 			maObjectTransformation.set(1, 2, rRectangle.Top());
271 		}
272 
273 		void ViewContactOfE3dScene::createSdrSceneAttribute()
274 		{
275 			const SfxItemSet& rItemSet = GetE3dScene().GetMergedItemSet();
276 			maSdrSceneAttribute = drawinglayer::primitive2d::createNewSdrSceneAttribute(rItemSet);
277 		}
278 
279 		void ViewContactOfE3dScene::createSdrLightingAttribute()
280 		{
281 			const SfxItemSet& rItemSet = GetE3dScene().GetMergedItemSet();
282 			maSdrLightingAttribute = drawinglayer::primitive2d::createNewSdrLightingAttribute(rItemSet);
283 		}
284 
285 		drawinglayer::primitive2d::Primitive2DSequence ViewContactOfE3dScene::createScenePrimitive2DSequence(
286             const SetOfByte* pLayerVisibility) const
287 		{
288 			drawinglayer::primitive2d::Primitive2DSequence xRetval;
289 			const sal_uInt32 nChildrenCount(GetObjectCount());
290 
291 			if(nChildrenCount)
292 			{
293 				// create 3d scene primitive with visible content tested against rLayerVisibility
294 				drawinglayer::primitive3d::Primitive3DSequence aAllSequence;
295 				drawinglayer::primitive3d::Primitive3DSequence aVisibleSequence;
296 				const bool bTestLayerVisibility(0 != pLayerVisibility);
297 				const bool bTestSelectedVisibility(GetE3dScene().GetDrawOnlySelected());
298 				const bool bTestVisibility(bTestLayerVisibility || bTestSelectedVisibility);
299 
300 				// add children recursively. Do NOT start with (*this), this would create
301 				// a 3D transformPrimitive for the start scene. While this is theoretically not
302 				// a bad thing, for historical reasons the transformation of the outmost scene
303 				// is seen as part of the ViewTransformation (see text in createViewInformation3D)
304 				for(sal_uInt32 a(0L); a < nChildrenCount; a++)
305 				{
306 					createSubPrimitive3DVector(
307 						GetViewContact(a),
308 						aAllSequence,
309 						bTestLayerVisibility ? &aVisibleSequence : 0,
310 						bTestLayerVisibility ? pLayerVisibility : 0,
311 						bTestSelectedVisibility);
312 				}
313 
314 				const sal_uInt32 nAllSize(aAllSequence.hasElements() ? aAllSequence.getLength() : 0);
315 				const sal_uInt32 nVisibleSize(aVisibleSequence.hasElements() ? aVisibleSequence.getLength() : 0);
316 
317 				if((bTestVisibility && nVisibleSize) || nAllSize)
318 				{
319 					// for getting the 3D range using getB3DRangeFromPrimitive3DSequence a ViewInformation3D
320 					// needs to be given for evtl. decompositions. At the same time createViewInformation3D
321 					// currently is based on creating the target-ViewInformation3D using a given range. To
322 					// get the true range, use a neutral ViewInformation3D here. This leaves all matrices
323 					// on identity and the time on 0.0.
324 					const uno::Sequence< beans::PropertyValue > aEmptyProperties;
325 					const drawinglayer::geometry::ViewInformation3D aNeutralViewInformation3D(aEmptyProperties);
326 					const basegfx::B3DRange aContentRange(
327 						drawinglayer::primitive3d::getB3DRangeFromPrimitive3DSequence(aAllSequence, aNeutralViewInformation3D));
328 
329 					// create 2d primitive 3dscene with generated sub-list from collector
330 					const drawinglayer::primitive2d::Primitive2DReference xReference(
331 						new drawinglayer::primitive2d::ScenePrimitive2D(
332 							bTestVisibility ? aVisibleSequence : aAllSequence,
333 							getSdrSceneAttribute(),
334 							getSdrLightingAttribute(),
335 							getObjectTransformation(),
336 							getViewInformation3D(aContentRange)));
337 
338 					xRetval = drawinglayer::primitive2d::Primitive2DSequence(&xReference, 1);
339 				}
340 			}
341 
342 			// always append an invisible outline for the cases where no visible content exists
343 			drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(xRetval,
344 				drawinglayer::primitive2d::createHiddenGeometryPrimitives2D(
345 					false, getObjectTransformation()));
346 
347 			return xRetval;
348 		}
349 
350 		drawinglayer::primitive2d::Primitive2DSequence ViewContactOfE3dScene::createViewIndependentPrimitive2DSequence() const
351 		{
352 			drawinglayer::primitive2d::Primitive2DSequence xRetval;
353 
354 			if(GetObjectCount())
355 			{
356 				// create a default ScenePrimitive2D (without visibility test of members)
357 				xRetval = createScenePrimitive2DSequence(0);
358 			}
359 
360             return xRetval;
361 		}
362 
363 		void ViewContactOfE3dScene::ActionChanged()
364 		{
365 			// call parent
366 			ViewContactOfSdrObj::ActionChanged();
367 
368 			// mark locally cached values as invalid
369     		maViewInformation3D = drawinglayer::geometry::ViewInformation3D();
370 			maObjectTransformation.identity();
371 			maSdrSceneAttribute = drawinglayer::attribute::SdrSceneAttribute();
372 			maSdrLightingAttribute = drawinglayer::attribute::SdrLightingAttribute();
373 		}
374 
375 		const drawinglayer::geometry::ViewInformation3D& ViewContactOfE3dScene::getViewInformation3D() const
376 		{
377 			if(maViewInformation3D.isDefault())
378 			{
379 				// this version will create the content range on demand locally and thus is less
380 				// performant than the other one. Since the information is buffered the planned
381 				// behaviour is that the version with the given range is used initially.
382                 basegfx::B3DRange aContentRange(getAllContentRange3D());
383 
384 				if(aContentRange.isEmpty())
385 				{
386 					// empty scene, no 3d action should be necessary. Prepare some
387                     // fallback size
388 					OSL_ENSURE(false, "No need to get ViewInformation3D from an empty scene (!)");
389 					aContentRange.expand(basegfx::B3DPoint(-100.0, -100.0, -100.0));
390 					aContentRange.expand(basegfx::B3DPoint( 100.0,  100.0,  100.0));
391 				}
392 
393 				const_cast < ViewContactOfE3dScene* >(this)->createViewInformation3D(aContentRange);
394 			}
395 
396 			return maViewInformation3D;
397 		}
398 
399 		const drawinglayer::geometry::ViewInformation3D& ViewContactOfE3dScene::getViewInformation3D(const basegfx::B3DRange& rContentRange) const
400 		{
401             if(maViewInformation3D.isDefault())
402 			{
403 				const_cast < ViewContactOfE3dScene* >(this)->createViewInformation3D(rContentRange);
404 			}
405 
406 			return maViewInformation3D;
407 		}
408 
409 		const basegfx::B2DHomMatrix& ViewContactOfE3dScene::getObjectTransformation() const
410 		{
411             if(maObjectTransformation.isIdentity())
412 			{
413 				const_cast < ViewContactOfE3dScene* >(this)->createObjectTransformation();
414 			}
415 
416 			return maObjectTransformation;
417 		}
418 
419 		const drawinglayer::attribute::SdrSceneAttribute& ViewContactOfE3dScene::getSdrSceneAttribute() const
420 		{
421             if(maSdrSceneAttribute.isDefault())
422 			{
423 				const_cast < ViewContactOfE3dScene* >(this)->createSdrSceneAttribute();
424 			}
425 
426 			return maSdrSceneAttribute;
427 		}
428 
429 		const drawinglayer::attribute::SdrLightingAttribute& ViewContactOfE3dScene::getSdrLightingAttribute() const
430 		{
431             if(maSdrLightingAttribute.isDefault())
432 			{
433 				const_cast < ViewContactOfE3dScene* >(this)->createSdrLightingAttribute();
434 			}
435 
436 			return maSdrLightingAttribute;
437 		}
438 
439         drawinglayer::primitive3d::Primitive3DSequence ViewContactOfE3dScene::getAllPrimitive3DSequence() const
440         {
441             drawinglayer::primitive3d::Primitive3DSequence aAllPrimitive3DSequence;
442 			const sal_uInt32 nChildrenCount(GetObjectCount());
443 
444 		    // add children recursively. Do NOT start with (*this), this would create
445 		    // a 3D transformPrimitive for the start scene. While this is theoretically not
446 		    // a bad thing, for historical reasons the transformation of the outmost scene
447 		    // is seen as part of the ViewTransformation (see text in createViewInformation3D)
448             for(sal_uInt32 a(0L); a < nChildrenCount; a++)
449 		    {
450 			    createSubPrimitive3DVector(GetViewContact(a), aAllPrimitive3DSequence, 0, 0, false);
451 		    }
452 
453             return aAllPrimitive3DSequence;
454         }
455 
456         basegfx::B3DRange ViewContactOfE3dScene::getAllContentRange3D() const
457         {
458             const drawinglayer::primitive3d::Primitive3DSequence xAllSequence(getAllPrimitive3DSequence());
459             basegfx::B3DRange aAllContentRange3D;
460 
461             if(xAllSequence.hasElements())
462 			{
463 				// for getting the 3D range using getB3DRangeFromPrimitive3DSequence a ViewInformation3D
464 				// needs to be given for evtl. decompositions. Use a neutral ViewInformation3D here. This
465                 // leaves all matrices on identity and the time on 0.0.
466 				const uno::Sequence< beans::PropertyValue > aEmptyProperties;
467 				const drawinglayer::geometry::ViewInformation3D aNeutralViewInformation3D(aEmptyProperties);
468 
469 				aAllContentRange3D = drawinglayer::primitive3d::getB3DRangeFromPrimitive3DSequence(xAllSequence, aNeutralViewInformation3D);
470             }
471 
472             return aAllContentRange3D;
473         }
474 	} // end of namespace contact
475 } // end of namespace sdr
476 
477 //////////////////////////////////////////////////////////////////////////////
478 // eof
479