1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <o3tl/numeric.hxx>
21 
22 #include <svx/strings.hrc>
23 #include <svx/dialmgr.hxx>
24 #include <svx/svdhdl.hxx>
25 #include <svx/svdmodel.hxx>
26 #include <svx/svdobjkind.hxx>
27 #include <svx/scene3d.hxx>
28 #include <svx/obj3d.hxx>
29 #include <sdr/properties/e3dproperties.hxx>
30 #include <sdr/properties/e3dcompoundproperties.hxx>
31 #include <basegfx/polygon/b3dpolypolygontools.hxx>
32 #include <basegfx/point/b3dpoint.hxx>
33 #include <basegfx/matrix/b3dhommatrix.hxx>
34 #include <basegfx/matrix/b2dhommatrix.hxx>
35 #include <basegfx/polygon/b2dpolypolygontools.hxx>
36 #include <svx/helperhittest3d.hxx>
37 #include <sdr/contact/viewcontactofe3d.hxx>
38 #include <drawinglayer/geometry/viewinformation3d.hxx>
39 #include <com/sun/star/uno/Sequence.h>
40 #include <svx/sdr/contact/viewcontactofe3dscene.hxx>
41 #include <svx/e3dsceneupdater.hxx>
42 #include <rtl/ustrbuf.hxx>
43 #include <unotools/configmgr.hxx>
44 
45 using namespace com::sun::star;
46 
CreateObjectSpecificProperties()47 std::unique_ptr<sdr::properties::BaseProperties> E3dObject::CreateObjectSpecificProperties()
48 {
49     return std::make_unique<sdr::properties::E3dProperties>(*this);
50 }
51 
E3dObject(SdrModel & rSdrModel)52 E3dObject::E3dObject(SdrModel& rSdrModel)
53 :   SdrAttrObj(rSdrModel),
54     maLocalBoundVol(),
55     maTransformation(),
56     maFullTransform(),
57     mbTfHasChanged(true),
58     mbIsSelected(false)
59 {
60     m_bIs3DObj = true;
61     m_bClosedObj = true;
62 }
63 
E3dObject(SdrModel & rSdrModel,E3dObject const & rSource)64 E3dObject::E3dObject(SdrModel& rSdrModel, E3dObject const & rSource)
65 :   SdrAttrObj(rSdrModel, rSource),
66     maLocalBoundVol(),
67     maTransformation(),
68     maFullTransform(),
69     mbTfHasChanged(true),
70     mbIsSelected(false)
71 {
72     m_bIs3DObj = true;
73     m_bClosedObj = true;
74 
75     // BoundVol can be copied since also the children are copied
76     maLocalBoundVol  = rSource.maLocalBoundVol;
77     maTransformation = rSource.maTransformation;
78 
79     // Because the parent may have changed, definitely redefine the total
80     // transformation next time
81     SetTransformChanged();
82 
83     // Copy selection status
84     mbIsSelected = rSource.mbIsSelected;
85 }
86 
~E3dObject()87 E3dObject::~E3dObject()
88 {
89 }
90 
SetSelected(bool bNew)91 void E3dObject::SetSelected(bool bNew)
92 {
93     if(mbIsSelected != bNew)
94     {
95         mbIsSelected = bNew;
96     }
97 }
98 
99 // Break, default implementations
IsBreakObjPossible()100 bool E3dObject::IsBreakObjPossible()
101 {
102     return false;
103 }
104 
GetBreakObj()105 std::unique_ptr<SdrAttrObj,SdrObjectFreeOp> E3dObject::GetBreakObj()
106 {
107     return nullptr;
108 }
109 
GetObjInventor() const110 SdrInventor E3dObject::GetObjInventor() const
111 {
112     return SdrInventor::E3d;
113 }
114 
GetObjIdentifier() const115 SdrObjKind E3dObject::GetObjIdentifier() const
116 {
117     return E3D_OBJECT_ID;
118 }
119 
120 // Determine the capabilities of the object
TakeObjInfo(SdrObjTransformInfoRec & rInfo) const121 void E3dObject::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
122 {
123     rInfo.bResizeFreeAllowed    = true;
124     rInfo.bResizePropAllowed    = true;
125     rInfo.bRotateFreeAllowed    = true;
126     rInfo.bRotate90Allowed      = true;
127     rInfo.bMirrorFreeAllowed    = false;
128     rInfo.bMirror45Allowed      = false;
129     rInfo.bMirror90Allowed      = false;
130     rInfo.bShearAllowed         = false;
131     rInfo.bEdgeRadiusAllowed    = false;
132     rInfo.bCanConvToPath        = false;
133 
134     // no transparence for 3d objects
135     rInfo.bTransparenceAllowed = false;
136 
137     // Convert 3D objects in a group of polygons:
138     // At first not only possible, because the creation of a group of
139     // 2D polygons would be required which need to be sorted by depth,
140     // ie at intersections be cut relative to each other. Also the texture
141     // coordinates were an unsolved problem.
142     rInfo.bCanConvToPoly = false;
143     rInfo.bCanConvToContour = false;
144     rInfo.bCanConvToPathLineToArea = false;
145     rInfo.bCanConvToPolyLineToArea = false;
146 }
147 
148 // resize object, used from old 2d interfaces, e.g. in Move/Scale dialog (F4)
NbcResize(const Point & rRef,const Fraction & xFact,const Fraction & yFact)149 void E3dObject::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact)
150 {
151     // Movement in X, Y in the eye coordinate system
152     E3dScene* pScene(getRootE3dSceneFromE3dObject());
153 
154     if(nullptr == pScene)
155     {
156         return;
157     }
158 
159     // transform pos from 2D world to 3D eye
160     const sdr::contact::ViewContactOfE3dScene& rVCScene = static_cast< sdr::contact::ViewContactOfE3dScene& >(pScene->GetViewContact());
161     const drawinglayer::geometry::ViewInformation3D& aViewInfo3D(rVCScene.getViewInformation3D());
162     basegfx::B2DPoint aScaleCenter2D(static_cast<double>(rRef.X()), static_cast<double>(rRef.Y()));
163     basegfx::B2DHomMatrix aInverseSceneTransform(rVCScene.getObjectTransformation());
164 
165     aInverseSceneTransform.invert();
166     aScaleCenter2D = aInverseSceneTransform * aScaleCenter2D;
167 
168     basegfx::B3DPoint aScaleCenter3D(aScaleCenter2D.getX(), aScaleCenter2D.getY(), 0.5);
169     basegfx::B3DHomMatrix aInverseViewToEye(aViewInfo3D.getDeviceToView() * aViewInfo3D.getProjection());
170 
171     aInverseViewToEye.invert();
172     aScaleCenter3D = aInverseViewToEye * aScaleCenter3D;
173 
174     // Get scale factors
175     double fScaleX(xFact);
176     double fScaleY(yFact);
177 
178     // build transform
179     basegfx::B3DHomMatrix aInverseOrientation(aViewInfo3D.getOrientation());
180     aInverseOrientation.invert();
181     basegfx::B3DHomMatrix aFullTransform(GetFullTransform());
182     basegfx::B3DHomMatrix aTrans(aFullTransform);
183 
184     aTrans *= aViewInfo3D.getOrientation();
185     aTrans.translate(-aScaleCenter3D.getX(), -aScaleCenter3D.getY(), -aScaleCenter3D.getZ());
186     aTrans.scale(fScaleX, fScaleY, 1.0);
187     aTrans.translate(aScaleCenter3D.getX(), aScaleCenter3D.getY(), aScaleCenter3D.getZ());
188     aTrans *= aInverseOrientation;
189     aFullTransform.invert();
190     aTrans *= aFullTransform;
191 
192     // Apply
193     basegfx::B3DHomMatrix aObjTrans(GetTransform());
194     aObjTrans *= aTrans;
195 
196     E3DModifySceneSnapRectUpdater aUpdater(this);
197     SetTransform(aObjTrans);
198 }
199 
200 // Move object in 2D is needed when using cursor keys
NbcMove(const Size & rSize)201 void E3dObject::NbcMove(const Size& rSize)
202 {
203     // Movement in X, Y in the eye coordinate system
204     E3dScene* pScene(getRootE3dSceneFromE3dObject());
205 
206     if(nullptr == pScene)
207     {
208         return;
209     }
210 
211     //Dimensions of the scene in 3D and 2D for comparison
212     tools::Rectangle aRect = pScene->GetSnapRect();
213     basegfx::B3DHomMatrix aInvDispTransform;
214     E3dScene* pParent(getParentE3dSceneFromE3dObject());
215 
216     if(nullptr != pParent)
217     {
218         aInvDispTransform = pParent->GetFullTransform();
219         aInvDispTransform.invert();
220     }
221 
222     // BoundVolume from 3d world to 3d eye
223     const sdr::contact::ViewContactOfE3dScene& rVCScene = static_cast< sdr::contact::ViewContactOfE3dScene& >(pScene->GetViewContact());
224     const drawinglayer::geometry::ViewInformation3D& aViewInfo3D(rVCScene.getViewInformation3D());
225     basegfx::B3DRange aEyeVol(pScene->GetBoundVolume());
226     aEyeVol.transform(aViewInfo3D.getOrientation());
227 
228     if ((aRect.GetWidth() == 0) || (aRect.GetHeight() == 0))
229         throw o3tl::divide_by_zero();
230 
231     // build relative movement vector in eye coordinates
232     basegfx::B3DPoint aMove(
233         static_cast<double>(rSize.Width()) * aEyeVol.getWidth() / static_cast<double>(aRect.GetWidth()),
234         static_cast<double>(-rSize.Height()) * aEyeVol.getHeight() / static_cast<double>(aRect.GetHeight()),
235         0.0);
236     basegfx::B3DPoint aPos(0.0, 0.0, 0.0);
237 
238     // movement vector to local coordinates of objects' parent
239     basegfx::B3DHomMatrix aInverseOrientation(aViewInfo3D.getOrientation());
240     aInverseOrientation.invert();
241     basegfx::B3DHomMatrix aCompleteTrans(aInvDispTransform * aInverseOrientation);
242 
243     aMove = aCompleteTrans * aMove;
244     aPos = aCompleteTrans * aPos;
245 
246     // build transformation and apply
247     basegfx::B3DHomMatrix aTranslate;
248     aTranslate.translate(aMove.getX() - aPos.getX(), aMove.getY() - aPos.getY(), aMove.getZ() - aPos.getZ());
249 
250     E3DModifySceneSnapRectUpdater aUpdater(pScene);
251     SetTransform(aTranslate * GetTransform());
252 }
253 
RecalcSnapRect()254 void E3dObject::RecalcSnapRect()
255 {
256     maSnapRect = tools::Rectangle();
257 }
258 
259 // Inform parent of changes in the structure (eg by transformation), in this
260 // process the object in which the change has occurred is returned.
StructureChanged()261 void E3dObject::StructureChanged()
262 {
263     E3dScene* pParent(getParentE3dSceneFromE3dObject());
264 
265     if(nullptr != pParent)
266     {
267         pParent->InvalidateBoundVolume();
268         pParent->StructureChanged();
269     }
270 }
271 
getParentE3dSceneFromE3dObject() const272 E3dScene* E3dObject::getParentE3dSceneFromE3dObject() const
273 {
274     return dynamic_cast< E3dScene* >(getParentSdrObjectFromSdrObject());
275 }
276 
277 // Determine the top-level scene object
getRootE3dSceneFromE3dObject() const278 E3dScene* E3dObject::getRootE3dSceneFromE3dObject() const
279 {
280     E3dScene* pParent(getParentE3dSceneFromE3dObject());
281 
282     if(nullptr != pParent)
283     {
284         return pParent->getRootE3dSceneFromE3dObject();
285     }
286 
287     return nullptr;
288 }
289 
290 // Calculate enclosed volume, including all child objects
RecalcBoundVolume() const291 basegfx::B3DRange E3dObject::RecalcBoundVolume() const
292 {
293     basegfx::B3DRange aRetval;
294     if (utl::ConfigManager::IsFuzzing()) // skip slow path for fuzzing
295         return aRetval;
296 
297     const sdr::contact::ViewContactOfE3d* pVCOfE3D = dynamic_cast< const sdr::contact::ViewContactOfE3d* >(&GetViewContact());
298 
299     if(pVCOfE3D)
300     {
301         // BoundVolume is without 3D object transformation, use correct sequence
302         const drawinglayer::primitive3d::Primitive3DContainer& xLocalSequence(pVCOfE3D->getVIP3DSWithoutObjectTransform());
303 
304         if(!xLocalSequence.empty())
305         {
306             const uno::Sequence< beans::PropertyValue > aEmptyParameters;
307             const drawinglayer::geometry::ViewInformation3D aLocalViewInformation3D(aEmptyParameters);
308 
309             aRetval = xLocalSequence.getB3DRange(aLocalViewInformation3D);
310         }
311     }
312 
313     return aRetval;
314 }
315 
316 // Get enclosed volume and possibly recalculate it
GetBoundVolume() const317 const basegfx::B3DRange& E3dObject::GetBoundVolume() const
318 {
319     if(maLocalBoundVol.isEmpty())
320     {
321         const_cast< E3dObject* >(this)->maLocalBoundVol = RecalcBoundVolume();
322     }
323 
324     return maLocalBoundVol;
325 }
326 
InvalidateBoundVolume()327 void E3dObject::InvalidateBoundVolume()
328 {
329     maLocalBoundVol.reset();
330 }
331 
332 // Pass on the changes in transformation to all child objects
SetTransformChanged()333 void E3dObject::SetTransformChanged()
334 {
335     InvalidateBoundVolume();
336     mbTfHasChanged = true;
337 }
338 
339 // Define the hierarchical transformation over all Parents, store in
340 // maFullTransform and return them
GetFullTransform() const341 const basegfx::B3DHomMatrix& E3dObject::GetFullTransform() const
342 {
343     if(mbTfHasChanged)
344     {
345         basegfx::B3DHomMatrix aNewFullTransformation(maTransformation);
346         E3dScene* pParent(getParentE3dSceneFromE3dObject());
347 
348         if(nullptr != pParent)
349         {
350             aNewFullTransformation = pParent->GetFullTransform() * aNewFullTransformation;
351         }
352 
353         const_cast< E3dObject* >(this)->maFullTransform = aNewFullTransformation;
354         const_cast< E3dObject* >(this)->mbTfHasChanged = false;
355     }
356 
357     return maFullTransform;
358 }
359 
NbcSetTransform(const basegfx::B3DHomMatrix & rMatrix)360 void E3dObject::NbcSetTransform(const basegfx::B3DHomMatrix& rMatrix)
361 {
362     if(maTransformation != rMatrix)
363     {
364         maTransformation = rMatrix;
365         SetTransformChanged();
366         StructureChanged();
367     }
368 }
369 
370 // Set transformation matrix with repaint broadcast
SetTransform(const basegfx::B3DHomMatrix & rMatrix)371 void E3dObject::SetTransform(const basegfx::B3DHomMatrix& rMatrix)
372 {
373     if(rMatrix != maTransformation)
374     {
375         NbcSetTransform(rMatrix);
376         SetChanged();
377         BroadcastObjectChange();
378         if (m_pUserCall != nullptr) m_pUserCall->Changed(*this, SdrUserCallType::Resize, tools::Rectangle());
379     }
380 }
381 
CreateWireframe() const382 basegfx::B3DPolyPolygon E3dObject::CreateWireframe() const
383 {
384     const basegfx::B3DRange aBoundVolume(GetBoundVolume());
385     return basegfx::utils::createCubePolyPolygonFromB3DRange(aBoundVolume);
386 }
387 
388 // Get the name of the object (singular)
TakeObjNameSingul() const389 OUString E3dObject::TakeObjNameSingul() const
390 {
391     OUString sName = SvxResId(STR_ObjNameSingulObj3d);
392 
393     OUString aName(GetName());
394     if (!aName.isEmpty())
395     {
396         sName += " '" + aName + "'";
397     }
398     return sName;
399 }
400 
401 // Get the name of the object (plural)
TakeObjNamePlural() const402 OUString E3dObject::TakeObjNamePlural() const
403 {
404     return SvxResId(STR_ObjNamePluralObj3d);
405 }
406 
CloneSdrObject(SdrModel & rTargetModel) const407 E3dObject* E3dObject::CloneSdrObject(SdrModel& rTargetModel) const
408 {
409     return new E3dObject(rTargetModel, *this);
410 }
411 
NewGeoData() const412 std::unique_ptr<SdrObjGeoData> E3dObject::NewGeoData() const
413 {
414     return std::make_unique<E3DObjGeoData>();
415 }
416 
SaveGeoData(SdrObjGeoData & rGeo) const417 void E3dObject::SaveGeoData(SdrObjGeoData& rGeo) const
418 {
419     SdrAttrObj::SaveGeoData (rGeo);
420 
421     static_cast<E3DObjGeoData &>(rGeo).maLocalBoundVol  = maLocalBoundVol;
422     static_cast<E3DObjGeoData &>(rGeo).maTransformation = maTransformation;
423 }
424 
RestoreGeoData(const SdrObjGeoData & rGeo)425 void E3dObject::RestoreGeoData(const SdrObjGeoData& rGeo)
426 {
427     maLocalBoundVol = static_cast<const E3DObjGeoData &>(rGeo).maLocalBoundVol;
428     E3DModifySceneSnapRectUpdater aUpdater(this);
429     NbcSetTransform(static_cast<const E3DObjGeoData &>(rGeo).maTransformation);
430     SdrAttrObj::RestoreGeoData (rGeo);
431 }
432 
433 // 2D-rotation of a 3D-body, normally this is done by the scene itself.
434 // This is however a correct implementation, because everything that has
435 // happened is a rotation around the axis perpendicular to the screen and that
436 // is regardless of how the scene has been rotated up until now.
NbcRotate(const Point & rRef,Degree100 nAngle,double sn,double cs)437 void E3dObject::NbcRotate(const Point& rRef, Degree100 nAngle, double sn, double cs)
438 {
439     // So currently the glue points are defined relative to the scene aOutRect.
440     // Before turning the glue points are defined relative to the page. They
441     // take no part in the rotation of the scene. To ensure this, there is the
442     // SetGlueReallyAbsolute(sal_True);
443     double fAngleInRad = toRadians(nAngle);
444 
445     basegfx::B3DHomMatrix aRotateZ;
446     aRotateZ.rotate(0.0, 0.0, fAngleInRad);
447     NbcSetTransform(aRotateZ * GetTransform());
448 
449     SetRectsDirty();        // This forces a recalculation of all BoundRects
450     NbcRotateGluePoints(rRef,nAngle,sn,cs);  // Rotate the glue points (who still
451                                             // have coordinates relative to the
452                                             // original page)
453     SetGlueReallyAbsolute(false);       // from now they are again relative to BoundRect (that is defined as aOutRect)
454 }
455 
CreateObjectSpecificProperties()456 std::unique_ptr<sdr::properties::BaseProperties> E3dCompoundObject::CreateObjectSpecificProperties()
457 {
458     return std::make_unique<sdr::properties::E3dCompoundProperties>(*this);
459 }
460 
E3dCompoundObject(SdrModel & rSdrModel)461 E3dCompoundObject::E3dCompoundObject(SdrModel& rSdrModel)
462 :   E3dObject(rSdrModel)
463 {
464 }
465 
E3dCompoundObject(SdrModel & rSdrModel,E3dCompoundObject const & rSource)466 E3dCompoundObject::E3dCompoundObject(SdrModel& rSdrModel, E3dCompoundObject const & rSource)
467 :   E3dObject(rSdrModel, rSource)
468 {
469 }
470 
~E3dCompoundObject()471 E3dCompoundObject::~E3dCompoundObject ()
472 {
473 }
474 
TakeXorPoly() const475 basegfx::B2DPolyPolygon E3dCompoundObject::TakeXorPoly() const
476 {
477     basegfx::B2DPolyPolygon aRetval;
478     const uno::Sequence< beans::PropertyValue > aEmptyParameters;
479     drawinglayer::geometry::ViewInformation3D aViewInfo3D(aEmptyParameters);
480     E3dScene* pRootScene = fillViewInformation3DForCompoundObject(aViewInfo3D, *this);
481 
482     if(pRootScene)
483     {
484         const sdr::contact::ViewContactOfE3dScene& rVCScene = static_cast< sdr::contact::ViewContactOfE3dScene& >(pRootScene->GetViewContact());
485         const basegfx::B3DPolyPolygon aCubePolyPolygon(CreateWireframe());
486         aRetval = basegfx::utils::createB2DPolyPolygonFromB3DPolyPolygon(aCubePolyPolygon,
487             aViewInfo3D.getObjectToView() * GetTransform());
488         aRetval.transform(rVCScene.getObjectTransformation());
489     }
490 
491     return aRetval;
492 }
493 
GetHdlCount() const494 sal_uInt32 E3dCompoundObject::GetHdlCount() const
495 {
496     // 8 corners + 1 E3dVolumeMarker (= Wireframe representation)
497     return 9;
498 }
499 
AddToHdlList(SdrHdlList & rHdlList) const500 void E3dCompoundObject::AddToHdlList(SdrHdlList& rHdlList) const
501 {
502     const uno::Sequence< beans::PropertyValue > aEmptyParameters;
503     drawinglayer::geometry::ViewInformation3D aViewInfo3D(aEmptyParameters);
504     E3dScene* pRootScene = fillViewInformation3DForCompoundObject(aViewInfo3D, *this);
505 
506     if(pRootScene)
507     {
508         const basegfx::B3DRange aBoundVolume(GetBoundVolume());
509 
510         if(!aBoundVolume.isEmpty())
511         {
512             const sdr::contact::ViewContactOfE3dScene& rVCScene = static_cast< sdr::contact::ViewContactOfE3dScene& >(pRootScene->GetViewContact());
513 
514             for(sal_uInt32 a(0); a < 8; a++)
515             {
516                 basegfx::B3DPoint aPos3D;
517 
518                 switch(a)
519                 {
520                     case 0 : aPos3D.setX(aBoundVolume.getMinX()); aPos3D.setY(aBoundVolume.getMinY()); aPos3D.setZ(aBoundVolume.getMinZ()); break;
521                     case 1 : aPos3D.setX(aBoundVolume.getMinX()); aPos3D.setY(aBoundVolume.getMinY()); aPos3D.setZ(aBoundVolume.getMaxZ()); break;
522                     case 2 : aPos3D.setX(aBoundVolume.getMinX()); aPos3D.setY(aBoundVolume.getMaxY()); aPos3D.setZ(aBoundVolume.getMinZ()); break;
523                     case 3 : aPos3D.setX(aBoundVolume.getMinX()); aPos3D.setY(aBoundVolume.getMaxY()); aPos3D.setZ(aBoundVolume.getMaxZ()); break;
524                     case 4 : aPos3D.setX(aBoundVolume.getMaxX()); aPos3D.setY(aBoundVolume.getMinY()); aPos3D.setZ(aBoundVolume.getMinZ()); break;
525                     case 5 : aPos3D.setX(aBoundVolume.getMaxX()); aPos3D.setY(aBoundVolume.getMinY()); aPos3D.setZ(aBoundVolume.getMaxZ()); break;
526                     case 6 : aPos3D.setX(aBoundVolume.getMaxX()); aPos3D.setY(aBoundVolume.getMaxY()); aPos3D.setZ(aBoundVolume.getMinZ()); break;
527                     case 7 : aPos3D.setX(aBoundVolume.getMaxX()); aPos3D.setY(aBoundVolume.getMaxY()); aPos3D.setZ(aBoundVolume.getMaxZ()); break;
528                 }
529 
530                 // to 3d view coor
531                 aPos3D *= aViewInfo3D.getObjectToView() * GetTransform();
532 
533                 // create 2d relative scene
534                 basegfx::B2DPoint aPos2D(aPos3D.getX(), aPos3D.getY());
535 
536                 // to 2d world coor
537                 aPos2D *= rVCScene.getObjectTransformation();
538 
539                 rHdlList.AddHdl(std::make_unique<SdrHdl>(Point(basegfx::fround(aPos2D.getX()), basegfx::fround(aPos2D.getY())), SdrHdlKind::BezierWeight));
540             }
541         }
542     }
543 
544     const basegfx::B2DPolyPolygon aPolyPolygon(TakeXorPoly());
545 
546     if(aPolyPolygon.count())
547     {
548         rHdlList.AddHdl(std::make_unique<E3dVolumeMarker>(aPolyPolygon));
549     }
550 }
551 
GetObjIdentifier() const552 SdrObjKind E3dCompoundObject::GetObjIdentifier() const
553 {
554     return E3D_COMPOUNDOBJ_ID;
555 }
556 
RecalcSnapRect()557 void E3dCompoundObject::RecalcSnapRect()
558 {
559     const uno::Sequence< beans::PropertyValue > aEmptyParameters;
560     drawinglayer::geometry::ViewInformation3D aViewInfo3D(aEmptyParameters);
561     E3dScene* pRootScene = fillViewInformation3DForCompoundObject(aViewInfo3D, *this);
562     maSnapRect = tools::Rectangle();
563 
564     if(!pRootScene)
565         return;
566 
567     // get VC of 3D candidate
568     const sdr::contact::ViewContactOfE3d* pVCOfE3D = dynamic_cast< const sdr::contact::ViewContactOfE3d* >(&GetViewContact());
569 
570     if(!pVCOfE3D)
571         return;
572 
573     // get 3D primitive sequence
574     const drawinglayer::primitive3d::Primitive3DContainer xLocalSequence(pVCOfE3D->getViewIndependentPrimitive3DContainer());
575 
576     if(xLocalSequence.empty())
577         return;
578 
579     // get BoundVolume
580     basegfx::B3DRange aBoundVolume(xLocalSequence.getB3DRange(aViewInfo3D));
581 
582     // transform bound volume to relative scene coordinates
583     aBoundVolume.transform(aViewInfo3D.getObjectToView());
584 
585     // build 2d relative scene range
586     basegfx::B2DRange aSnapRange(
587         aBoundVolume.getMinX(), aBoundVolume.getMinY(),
588         aBoundVolume.getMaxX(), aBoundVolume.getMaxY());
589 
590     // transform to 2D world coordinates
591     const sdr::contact::ViewContactOfE3dScene& rVCScene = static_cast< sdr::contact::ViewContactOfE3dScene& >(pRootScene->GetViewContact());
592     aSnapRange.transform(rVCScene.getObjectTransformation());
593 
594     // snap to integer
595     maSnapRect = tools::Rectangle(
596         sal_Int32(floor(aSnapRange.getMinX())), sal_Int32(floor(aSnapRange.getMinY())),
597         sal_Int32(ceil(aSnapRange.getMaxX())), sal_Int32(ceil(aSnapRange.getMaxY())));
598 }
599 
CloneSdrObject(SdrModel & rTargetModel) const600 E3dCompoundObject* E3dCompoundObject::CloneSdrObject(SdrModel& rTargetModel) const
601 {
602     return new E3dCompoundObject(rTargetModel, *this);
603 }
604 
605 // convert given basegfx::B3DPolyPolygon to screen coor
TransformToScreenCoor(const basegfx::B3DPolyPolygon & rCandidate)606 basegfx::B2DPolyPolygon E3dCompoundObject::TransformToScreenCoor(const basegfx::B3DPolyPolygon& rCandidate)
607 {
608     const uno::Sequence< beans::PropertyValue > aEmptyParameters;
609     drawinglayer::geometry::ViewInformation3D aViewInfo3D(aEmptyParameters);
610     E3dScene* pRootScene = fillViewInformation3DForCompoundObject(aViewInfo3D, *this);
611     basegfx::B2DPolyPolygon aRetval;
612 
613     if(pRootScene)
614     {
615         aRetval = basegfx::utils::createB2DPolyPolygonFromB3DPolyPolygon(rCandidate,
616             aViewInfo3D.getObjectToView() * GetTransform());
617         const sdr::contact::ViewContactOfE3dScene& rVCScene = static_cast< sdr::contact::ViewContactOfE3dScene& >(pRootScene->GetViewContact());
618         aRetval.transform(rVCScene.getObjectTransformation());
619     }
620 
621     return aRetval;
622 }
623 
624 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
625