1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <svx/sdr/contact/viewcontactofe3dscene.hxx>
21 #include <svx/sdr/contact/viewobjectcontact.hxx>
22 #include <sdr/primitive2d/sdrattributecreator.hxx>
23 #include <sdr/contact/viewobjectcontactofe3dscene.hxx>
24 #include <basegfx/matrix/b2dhommatrix.hxx>
25 #include <basegfx/range/b3drange.hxx>
26 #include <drawinglayer/primitive3d/baseprimitive3d.hxx>
27 #include <sdr/contact/viewcontactofe3d.hxx>
28 #include <drawinglayer/primitive2d/sceneprimitive2d.hxx>
29 #include <drawinglayer/primitive3d/transformprimitive3d.hxx>
30 #include <drawinglayer/primitive2d/sdrdecompositiontools2d.hxx>
31 #include <osl/diagnose.h>
33 using namespace com::sun::star
;
37 // pActiveVC is only true if ghosted is still activated and maybe needs to be switched off in this path
38 void createSubPrimitive3DVector(
39 const sdr::contact::ViewContact
& rCandidate
,
40 drawinglayer::primitive3d::Primitive3DContainer
& o_rAllTarget
,
41 drawinglayer::primitive3d::Primitive3DContainer
* o_pVisibleTarget
,
42 const SdrLayerIDSet
* pVisibleSdrLayerIDSet
,
43 const bool bTestSelectedVisibility
)
45 const sdr::contact::ViewContactOfE3dScene
* pViewContactOfE3dScene
= dynamic_cast< const sdr::contact::ViewContactOfE3dScene
* >(&rCandidate
);
47 if(pViewContactOfE3dScene
)
49 const sal_uInt32
nChildrenCount(rCandidate
.GetObjectCount());
53 // provide new collection sequences
54 drawinglayer::primitive3d::Primitive3DContainer aNewAllTarget
;
55 drawinglayer::primitive3d::Primitive3DContainer aNewVisibleTarget
;
57 // add children recursively
58 for(sal_uInt32
a(0); a
< nChildrenCount
; a
++)
60 createSubPrimitive3DVector(
61 rCandidate
.GetViewContact(a
),
63 o_pVisibleTarget
? &aNewVisibleTarget
: nullptr,
64 pVisibleSdrLayerIDSet
,
65 bTestSelectedVisibility
);
68 // create transform primitive for the created content combining content and transformtion
69 const drawinglayer::primitive3d::Primitive3DReference
xReference(new drawinglayer::primitive3d::TransformPrimitive3D(
70 pViewContactOfE3dScene
->GetE3dScene().GetTransform(),
73 // add created content to all target
74 o_rAllTarget
.push_back(xReference
);
76 // add created content to visible target if exists
79 o_pVisibleTarget
->push_back(xReference
);
85 // access view independent representation of rCandidate
86 const sdr::contact::ViewContactOfE3d
* pViewContactOfE3d
= dynamic_cast< const sdr::contact::ViewContactOfE3d
* >(&rCandidate
);
90 drawinglayer::primitive3d::Primitive3DContainer
xPrimitive3DSeq(pViewContactOfE3d
->getViewIndependentPrimitive3DContainer());
92 if(!xPrimitive3DSeq
.empty())
94 // add to all target vector
95 o_rAllTarget
.append(xPrimitive3DSeq
);
99 // test visibility. Primitive is visible when both tests are true (AND)
102 if(pVisibleSdrLayerIDSet
)
104 // test layer visibility
105 const E3dObject
& rE3dObject
= pViewContactOfE3d
->GetE3dObject();
106 const SdrLayerID
aLayerID(rE3dObject
.GetLayer());
108 bVisible
= pVisibleSdrLayerIDSet
->IsSet(aLayerID
);
111 if(bVisible
&& bTestSelectedVisibility
)
113 // test selected visibility (see 3D View's DrawMarkedObj implementation)
114 const E3dObject
& rE3dObject
= pViewContactOfE3d
->GetE3dObject();
116 bVisible
= rE3dObject
.GetSelected();
121 // add to visible target vector
122 o_pVisibleTarget
->append(xPrimitive3DSeq
);
132 namespace sdr::contact
{
134 // Create an Object-Specific ViewObjectContact, set ViewContact and
135 // ObjectContact. Always needs to return something.
136 ViewObjectContact
& ViewContactOfE3dScene::CreateObjectSpecificViewObjectContact(ObjectContact
& rObjectContact
)
138 ViewObjectContact
* pRetval
= new ViewObjectContactOfE3dScene(rObjectContact
, *this);
139 DBG_ASSERT(pRetval
, "ViewContactOfE3dScene::CreateObjectSpecificViewObjectContact() failed (!)");
144 ViewContactOfE3dScene::ViewContactOfE3dScene(E3dScene
& rScene
)
145 : ViewContactOfSdrObj(rScene
)
149 void ViewContactOfE3dScene::createViewInformation3D(const basegfx::B3DRange
& rContentRange
)
151 basegfx::B3DHomMatrix aTransformation
;
152 basegfx::B3DHomMatrix aOrientation
;
153 basegfx::B3DHomMatrix aProjection
;
154 basegfx::B3DHomMatrix aDeviceToView
;
156 // create transformation (scene as group's transformation)
157 // For historical reasons, the outmost scene's transformation is handles as part of the
158 // view transformation. This means that the BoundRect of the contained 3D Objects is
159 // without that transformation and makes it necessary to NOT add the first scene to the
160 // Primitive3DContainer of contained objects.
162 aTransformation
= GetE3dScene().GetTransform();
165 // create orientation (world to camera coordinate system)
167 // calculate orientation from VRP, VPN and VUV
168 const B3dCamera
& rSceneCamera
= GetE3dScene().GetCameraSet();
169 const basegfx::B3DPoint
& aVRP(rSceneCamera
.GetVRP());
170 const basegfx::B3DVector
& aVPN(rSceneCamera
.GetVPN());
171 const basegfx::B3DVector
& aVUV(rSceneCamera
.GetVUV());
173 aOrientation
.orientation(aVRP
, aVPN
, aVUV
);
176 // create projection (camera coordinate system to relative 2d where X,Y and Z are [0.0 .. 1.0])
178 const basegfx::B3DHomMatrix
aWorldToCamera(aOrientation
* aTransformation
);
179 basegfx::B3DRange
aCameraRange(rContentRange
);
180 aCameraRange
.transform(aWorldToCamera
);
182 // remember Z-Values, but change orientation
183 const double fMinZ(-aCameraRange
.getMaxZ());
184 const double fMaxZ(-aCameraRange
.getMinZ());
186 // construct temporary matrix from world to device. Use unit values here to measure expansion
187 basegfx::B3DHomMatrix
aWorldToDevice(aWorldToCamera
);
188 const drawinglayer::attribute::SdrSceneAttribute
& rSdrSceneAttribute
= getSdrSceneAttribute();
190 if(css::drawing::ProjectionMode_PERSPECTIVE
== rSdrSceneAttribute
.getProjectionMode())
192 aWorldToDevice
.frustum(-1.0, 1.0, -1.0, 1.0, fMinZ
, fMaxZ
);
196 aWorldToDevice
.ortho(-1.0, 1.0, -1.0, 1.0, fMinZ
, fMaxZ
);
199 // create B3DRange in device. This will create the real used ranges
200 // in camera space. Do not use the Z-Values, though.
201 basegfx::B3DRange
aDeviceRange(rContentRange
);
202 aDeviceRange
.transform(aWorldToDevice
);
205 if(css::drawing::ProjectionMode_PERSPECTIVE
== rSdrSceneAttribute
.getProjectionMode())
208 aDeviceRange
.getMinX(), aDeviceRange
.getMaxX(),
209 aDeviceRange
.getMinY(), aDeviceRange
.getMaxY(),
215 aDeviceRange
.getMinX(), aDeviceRange
.getMaxX(),
216 aDeviceRange
.getMinY(), aDeviceRange
.getMaxY(),
221 // create device to view transform
223 // create standard deviceToView projection for geometry
224 // input is [-1.0 .. 1.0] in X,Y and Z. bring to [0.0 .. 1.0]. Also
225 // necessary to flip Y due to screen orientation
226 // Z is not needed, but will also be brought to [0.0 .. 1.0]
227 aDeviceToView
.scale(0.5, -0.5, 0.5);
228 aDeviceToView
.translate(0.5, 0.5, 0.5);
231 const uno::Sequence
< beans::PropertyValue
> aEmptyProperties
;
232 maViewInformation3D
= drawinglayer::geometry::ViewInformation3D(
233 aTransformation
, aOrientation
, aProjection
,
234 aDeviceToView
, 0.0, aEmptyProperties
);
237 void ViewContactOfE3dScene::createObjectTransformation()
239 // create 2d Object Transformation from relative point in 2d scene to world
240 const tools::Rectangle
aRectangle(GetE3dScene().GetSnapRect());
242 maObjectTransformation
.set(0, 0, aRectangle
.getOpenWidth());
243 maObjectTransformation
.set(1, 1, aRectangle
.getOpenHeight());
244 maObjectTransformation
.set(0, 2, aRectangle
.Left());
245 maObjectTransformation
.set(1, 2, aRectangle
.Top());
248 void ViewContactOfE3dScene::createSdrSceneAttribute()
250 const SfxItemSet
& rItemSet
= GetE3dScene().GetMergedItemSet();
251 maSdrSceneAttribute
= drawinglayer::primitive2d::createNewSdrSceneAttribute(rItemSet
);
254 void ViewContactOfE3dScene::createSdrLightingAttribute()
256 const SfxItemSet
& rItemSet
= GetE3dScene().GetMergedItemSet();
257 maSdrLightingAttribute
= drawinglayer::primitive2d::createNewSdrLightingAttribute(rItemSet
);
260 drawinglayer::primitive2d::Primitive2DContainer
ViewContactOfE3dScene::createScenePrimitive2DSequence(
261 const SdrLayerIDSet
* pLayerVisibility
) const
263 drawinglayer::primitive2d::Primitive2DContainer xRetval
;
264 const sal_uInt32
nChildrenCount(GetObjectCount());
268 // create 3d scene primitive with visible content tested against rLayerVisibility
269 drawinglayer::primitive3d::Primitive3DContainer aAllSequence
;
270 drawinglayer::primitive3d::Primitive3DContainer aVisibleSequence
;
271 const bool bTestLayerVisibility(nullptr != pLayerVisibility
);
272 const bool bTestSelectedVisibility(GetE3dScene().GetDrawOnlySelected());
273 const bool bTestVisibility(bTestLayerVisibility
|| bTestSelectedVisibility
);
275 // add children recursively. Do NOT start with (*this), this would create
276 // a 3D transformPrimitive for the start scene. While this is theoretically not
277 // a bad thing, for historical reasons the transformation of the outmost scene
278 // is seen as part of the ViewTransformation (see text in createViewInformation3D)
279 for(sal_uInt32
a(0); a
< nChildrenCount
; a
++)
281 createSubPrimitive3DVector(
284 bTestLayerVisibility
? &aVisibleSequence
: nullptr,
285 bTestLayerVisibility
? pLayerVisibility
: nullptr,
286 bTestSelectedVisibility
);
289 const size_t nAllSize(!aAllSequence
.empty() ? aAllSequence
.size() : 0);
290 const size_t nVisibleSize(!aVisibleSequence
.empty() ? aVisibleSequence
.size() : 0);
292 if((bTestVisibility
&& nVisibleSize
) || nAllSize
)
294 // for getting the 3D range using getB3DRangeFromPrimitive3DContainer a ViewInformation3D
295 // needs to be given for evtl. decompositions. At the same time createViewInformation3D
296 // currently is based on creating the target-ViewInformation3D using a given range. To
297 // get the true range, use a neutral ViewInformation3D here. This leaves all matrices
298 // on identity and the time on 0.0.
299 const uno::Sequence
< beans::PropertyValue
> aEmptyProperties
;
300 const drawinglayer::geometry::ViewInformation3D
aNeutralViewInformation3D(aEmptyProperties
);
301 const basegfx::B3DRange
aContentRange(aAllSequence
.getB3DRange(aNeutralViewInformation3D
));
303 // create 2d primitive 3dscene with generated sub-list from collector
304 const drawinglayer::primitive2d::Primitive2DReference
xReference(
305 new drawinglayer::primitive2d::ScenePrimitive2D(
306 bTestVisibility
? aVisibleSequence
: aAllSequence
,
307 getSdrSceneAttribute(),
308 getSdrLightingAttribute(),
309 getObjectTransformation(),
310 getViewInformation3D(aContentRange
)));
312 xRetval
= drawinglayer::primitive2d::Primitive2DContainer
{ xReference
};
316 // always append an invisible outline for the cases where no visible content exists
318 drawinglayer::primitive2d::createHiddenGeometryPrimitives2D(
319 getObjectTransformation()));
324 void ViewContactOfE3dScene::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor
& rVisitor
) const
328 // create a default ScenePrimitive2D (without visibility test of members)
329 rVisitor
.visit(createScenePrimitive2DSequence(nullptr));
333 void ViewContactOfE3dScene::ActionChanged()
336 ViewContactOfSdrObj::ActionChanged();
338 // mark locally cached values as invalid
339 maViewInformation3D
= drawinglayer::geometry::ViewInformation3D();
340 maObjectTransformation
.identity();
341 maSdrSceneAttribute
= drawinglayer::attribute::SdrSceneAttribute();
342 maSdrLightingAttribute
= drawinglayer::attribute::SdrLightingAttribute();
345 const drawinglayer::geometry::ViewInformation3D
& ViewContactOfE3dScene::getViewInformation3D() const
347 if(maViewInformation3D
.isDefault())
349 // this version will create the content range on demand locally and thus is less
350 // performant than the other one. Since the information is buffered the planned
351 // behaviour is that the version with the given range is used initially.
352 basegfx::B3DRange
aContentRange(getAllContentRange3D());
354 if(aContentRange
.isEmpty())
356 // empty scene, no 3d action should be necessary. Prepare some
358 OSL_FAIL("No need to get ViewInformation3D from an empty scene (!)");
359 aContentRange
.expand(basegfx::B3DPoint(-100.0, -100.0, -100.0));
360 aContentRange
.expand(basegfx::B3DPoint( 100.0, 100.0, 100.0));
363 const_cast < ViewContactOfE3dScene
* >(this)->createViewInformation3D(aContentRange
);
366 return maViewInformation3D
;
369 const drawinglayer::geometry::ViewInformation3D
& ViewContactOfE3dScene::getViewInformation3D(const basegfx::B3DRange
& rContentRange
) const
371 if(maViewInformation3D
.isDefault())
373 const_cast < ViewContactOfE3dScene
* >(this)->createViewInformation3D(rContentRange
);
376 return maViewInformation3D
;
379 const basegfx::B2DHomMatrix
& ViewContactOfE3dScene::getObjectTransformation() const
381 if(maObjectTransformation
.isIdentity())
383 const_cast < ViewContactOfE3dScene
* >(this)->createObjectTransformation();
386 return maObjectTransformation
;
389 const drawinglayer::attribute::SdrSceneAttribute
& ViewContactOfE3dScene::getSdrSceneAttribute() const
391 if(maSdrSceneAttribute
.isDefault())
393 const_cast < ViewContactOfE3dScene
* >(this)->createSdrSceneAttribute();
396 return maSdrSceneAttribute
;
399 const drawinglayer::attribute::SdrLightingAttribute
& ViewContactOfE3dScene::getSdrLightingAttribute() const
401 if(maSdrLightingAttribute
.isDefault())
403 const_cast < ViewContactOfE3dScene
* >(this)->createSdrLightingAttribute();
406 return maSdrLightingAttribute
;
409 drawinglayer::primitive3d::Primitive3DContainer
ViewContactOfE3dScene::getAllPrimitive3DContainer() const
411 drawinglayer::primitive3d::Primitive3DContainer aAllPrimitive3DContainer
;
412 const sal_uInt32
nChildrenCount(GetObjectCount());
414 // add children recursively. Do NOT start with (*this), this would create
415 // a 3D transformPrimitive for the start scene. While this is theoretically not
416 // a bad thing, for historical reasons the transformation of the outmost scene
417 // is seen as part of the ViewTransformation (see text in createViewInformation3D)
418 for(sal_uInt32
a(0); a
< nChildrenCount
; a
++)
420 createSubPrimitive3DVector(GetViewContact(a
), aAllPrimitive3DContainer
, nullptr, nullptr, false);
423 return aAllPrimitive3DContainer
;
426 basegfx::B3DRange
ViewContactOfE3dScene::getAllContentRange3D() const
428 const drawinglayer::primitive3d::Primitive3DContainer
xAllSequence(getAllPrimitive3DContainer());
429 basegfx::B3DRange aAllContentRange3D
;
431 if(!xAllSequence
.empty())
433 // for getting the 3D range using getB3DRangeFromPrimitive3DContainer a ViewInformation3D
434 // needs to be given for evtl. decompositions. Use a neutral ViewInformation3D here. This
435 // leaves all matrices on identity and the time on 0.0.
436 const uno::Sequence
< beans::PropertyValue
> aEmptyProperties
;
437 const drawinglayer::geometry::ViewInformation3D
aNeutralViewInformation3D(aEmptyProperties
);
439 aAllContentRange3D
= xAllSequence
.getB3DRange(aNeutralViewInformation3D
);
442 return aAllContentRange3D
;
447 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */