Version 7.1.7.1, tag libreoffice-7.1.7.1
[LibreOffice.git] / svx / source / engine3d / view3d.cxx
blob3c0fb45a0df0534e758f2021826a05925c8a3764
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 .
21 #include <svx/svdopath.hxx>
22 #include <svx/svditer.hxx>
23 #include <svx/svdmodel.hxx>
24 #include <svx/svdpagv.hxx>
25 #include <editeng/colritem.hxx>
26 #include <editeng/eeitem.hxx>
27 #include <svx/svdview.hxx>
28 #include <svx/strings.hrc>
29 #include <svx/dialmgr.hxx>
30 #include <svx/obj3d.hxx>
31 #include <svx/lathe3d.hxx>
32 #include <extrud3d.hxx>
33 #include <dragmt3d.hxx>
34 #include <svx/scene3d.hxx>
35 #include <svx/view3d.hxx>
36 #include <svx/svdundo.hxx>
37 #include <svx/xflclit.hxx>
38 #include <svx/xlnclit.hxx>
39 #include <svx/xfillit0.hxx>
40 #include <svx/xlineit0.hxx>
41 #include <basegfx/range/b2drange.hxx>
42 #include <basegfx/polygon/b2dpolypolygontools.hxx>
43 #include <svx/xlnwtit.hxx>
44 #include <svx/sdr/overlay/overlaypolypolygon.hxx>
45 #include <svx/sdr/overlay/overlaymanager.hxx>
46 #include <svx/sdrpaintwindow.hxx>
47 #include <svx/sdr/contact/viewcontact.hxx>
48 #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
49 #include <svx/sdr/overlay/overlayprimitive2dsequenceobject.hxx>
50 #include <drawinglayer/primitive2d/transformprimitive2d.hxx>
51 #include <basegfx/matrix/b2dhommatrixtools.hxx>
52 #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
53 #include <svx/e3dsceneupdater.hxx>
55 using namespace com::sun::star;
58 // Migrate Marking
60 class Impl3DMirrorConstructOverlay
62 // The OverlayObjects
63 sdr::overlay::OverlayObjectList maObjects;
65 // the view
66 const E3dView& mrView;
68 // the object count
69 size_t mnCount;
71 // the unmirrored polygons
72 basegfx::B2DPolyPolygon* mpPolygons;
74 // the overlay geometry from selected objects
75 drawinglayer::primitive2d::Primitive2DContainer maFullOverlay;
77 // Copy assignment is forbidden and not implemented.
78 Impl3DMirrorConstructOverlay (const Impl3DMirrorConstructOverlay &) = delete;
79 Impl3DMirrorConstructOverlay & operator= (const Impl3DMirrorConstructOverlay &) = delete;
81 public:
82 explicit Impl3DMirrorConstructOverlay(const E3dView& rView);
83 ~Impl3DMirrorConstructOverlay();
85 void SetMirrorAxis(Point aMirrorAxisA, Point aMirrorAxisB);
88 Impl3DMirrorConstructOverlay::Impl3DMirrorConstructOverlay(const E3dView& rView)
89 : maObjects(),
90 mrView(rView),
91 mnCount(rView.GetMarkedObjectCount()),
92 mpPolygons(nullptr),
93 maFullOverlay()
95 if(!mnCount)
96 return;
98 if(mrView.IsSolidDragging())
100 SdrPageView* pPV = rView.GetSdrPageView();
102 if(pPV && pPV->PageWindowCount())
104 for(size_t a = 0; a < mnCount; ++a)
106 SdrObject* pObject = mrView.GetMarkedObjectByIndex(a);
108 if(pObject)
110 // use the view-independent primitive representation (without
111 // evtl. GridOffset, that may be applied to the DragEntry individually)
112 const drawinglayer::primitive2d::Primitive2DContainer& aNewSequence(
113 pObject->GetViewContact().getViewIndependentPrimitive2DContainer());
114 maFullOverlay.append(aNewSequence);
119 else
121 mpPolygons = new basegfx::B2DPolyPolygon[mnCount];
123 for(size_t a = 0; a < mnCount; ++a)
125 SdrObject* pObject = mrView.GetMarkedObjectByIndex(a);
126 mpPolygons[mnCount - (a + 1)] = pObject->TakeXorPoly();
131 Impl3DMirrorConstructOverlay::~Impl3DMirrorConstructOverlay()
133 // The OverlayObjects are cleared using the destructor of OverlayObjectList.
134 // That destructor calls clear() at the list which removes all objects from the
135 // OverlayManager and deletes them.
136 if(!mrView.IsSolidDragging())
138 delete[] mpPolygons;
142 void Impl3DMirrorConstructOverlay::SetMirrorAxis(Point aMirrorAxisA, Point aMirrorAxisB)
144 // get rid of old overlay objects
145 maObjects.clear();
147 // create new ones
148 for(sal_uInt32 a(0); a < mrView.PaintWindowCount(); a++)
150 SdrPaintWindow* pCandidate = mrView.GetPaintWindow(a);
151 const rtl::Reference< sdr::overlay::OverlayManager >& xTargetOverlay = pCandidate->GetOverlayManager();
153 if(xTargetOverlay.is())
155 // build transformation: translate and rotate so that given edge is
156 // on x axis, them mirror in y and translate back
157 const basegfx::B2DVector aEdge(aMirrorAxisB.X() - aMirrorAxisA.X(), aMirrorAxisB.Y() - aMirrorAxisA.Y());
158 basegfx::B2DHomMatrix aMatrixTransform(basegfx::utils::createTranslateB2DHomMatrix(
159 -aMirrorAxisA.X(), -aMirrorAxisA.Y()));
160 aMatrixTransform.rotate(-atan2(aEdge.getY(), aEdge.getX()));
161 aMatrixTransform.scale(1.0, -1.0);
162 aMatrixTransform.rotate(atan2(aEdge.getY(), aEdge.getX()));
163 aMatrixTransform.translate(aMirrorAxisA.X(), aMirrorAxisA.Y());
165 if(mrView.IsSolidDragging())
167 if(!maFullOverlay.empty())
169 drawinglayer::primitive2d::Primitive2DContainer aContent(maFullOverlay);
171 if(!aMatrixTransform.isIdentity())
173 // embed in transformation group
174 drawinglayer::primitive2d::Primitive2DReference aTransformPrimitive2D(new drawinglayer::primitive2d::TransformPrimitive2D(aMatrixTransform, aContent));
175 aContent = drawinglayer::primitive2d::Primitive2DContainer { aTransformPrimitive2D };
178 // if we have full overlay from selected objects, embed with 50% transparence, the
179 // transformation is added to the OverlayPrimitive2DSequenceObject
180 drawinglayer::primitive2d::Primitive2DReference aUnifiedTransparencePrimitive2D(new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(aContent, 0.5));
181 aContent = drawinglayer::primitive2d::Primitive2DContainer { aUnifiedTransparencePrimitive2D };
183 std::unique_ptr<sdr::overlay::OverlayPrimitive2DSequenceObject> pNew(new sdr::overlay::OverlayPrimitive2DSequenceObject(aContent));
185 xTargetOverlay->add(*pNew);
186 maObjects.append(std::move(pNew));
189 else
191 for(size_t b = 0; b < mnCount; ++b)
193 // apply to polygon
194 basegfx::B2DPolyPolygon aPolyPolygon(mpPolygons[b]);
195 aPolyPolygon.transform(aMatrixTransform);
197 std::unique_ptr<sdr::overlay::OverlayPolyPolygonStripedAndFilled> pNew(new sdr::overlay::OverlayPolyPolygonStripedAndFilled(
198 aPolyPolygon));
199 xTargetOverlay->add(*pNew);
200 maObjects.append(std::move(pNew));
207 E3dView::E3dView(
208 SdrModel& rSdrModel,
209 OutputDevice* pOut)
210 : SdrView(rSdrModel, pOut)
212 InitView();
215 // DrawMarkedObj override, since possibly only a single 3D object is to be
216 // drawn
218 void E3dView::DrawMarkedObj(OutputDevice& rOut) const
220 // Does 3D objects exist which scenes are not selected?
221 bool bSpecialHandling = false;
222 E3dScene *pScene = nullptr;
224 const size_t nCnt = GetMarkedObjectCount();
225 for(size_t nObjs = 0; nObjs < nCnt; ++nObjs)
227 SdrObject *pObj = GetMarkedObjectByIndex(nObjs);
228 if(auto pCompoundObject = dynamic_cast<E3dCompoundObject*>(pObj))
230 // related scene
231 pScene = pCompoundObject->getRootE3dSceneFromE3dObject();
233 if(nullptr != pScene && !IsObjMarked(pScene))
235 bSpecialHandling = true;
238 // Reset all selection flags
239 if(auto p3dObject = dynamic_cast< const E3dObject*>(pObj))
241 pScene = p3dObject->getRootE3dSceneFromE3dObject();
243 if(nullptr != pScene)
245 pScene->SetSelected(false);
250 if(bSpecialHandling)
252 // Set selection flag to "not selected" for scenes related to all 3D
253 // objects
254 for(size_t nObjs = 0; nObjs < nCnt; ++nObjs)
256 SdrObject *pObj = GetMarkedObjectByIndex(nObjs);
257 if(auto pCompoundObject = dynamic_cast<E3dCompoundObject*>(pObj))
259 // related scene
260 pScene = pCompoundObject->getRootE3dSceneFromE3dObject();
262 if(nullptr != pScene)
264 pScene->SetSelected(false);
269 for(size_t nObjs = 0; nObjs < nCnt; ++nObjs)
271 SdrObject *pObj = GetMarkedObjectByIndex(nObjs);
272 if(auto p3DObj = dynamic_cast<E3dObject*>(pObj))
274 // Select object
275 p3DObj->SetSelected(true);
276 pScene = p3DObj->getRootE3dSceneFromE3dObject();
280 if(nullptr != pScene)
282 // code from parent
283 SortMarkedObjects();
285 pScene->SetDrawOnlySelected(true);
286 pScene->SingleObjectPainter(rOut);
287 pScene->SetDrawOnlySelected(false);
290 // Reset selection flag
291 for(size_t nObjs = 0; nObjs < nCnt; ++nObjs)
293 SdrObject *pObj = GetMarkedObjectByIndex(nObjs);
294 if(auto pCompoundObject = dynamic_cast<E3dCompoundObject*>(pObj))
296 // related scene
297 pScene = pCompoundObject->getRootE3dSceneFromE3dObject();
299 if(nullptr != pScene)
301 pScene->SetSelected(false);
306 else
308 // call parent
309 SdrExchangeView::DrawMarkedObj(rOut);
313 // override get model, since in some 3D objects an additional scene
314 // must be pushed in
316 std::unique_ptr<SdrModel> E3dView::CreateMarkedObjModel() const
318 // Does 3D objects exist which scenes are not selected?
319 bool bSpecialHandling(false);
320 const size_t nCount(GetMarkedObjectCount());
321 E3dScene *pScene = nullptr;
323 for(size_t nObjs = 0; nObjs < nCount; ++nObjs)
325 const SdrObject* pObj = GetMarkedObjectByIndex(nObjs);
327 if(!bSpecialHandling && dynamic_cast< const E3dCompoundObject*>(pObj))
329 // if the object is selected, but it's scene not,
330 // we need special handling
331 pScene = static_cast<const E3dCompoundObject*>(pObj)->getRootE3dSceneFromE3dObject();
333 if(nullptr != pScene && !IsObjMarked(pScene))
335 bSpecialHandling = true;
339 if(auto p3dObject = dynamic_cast< const E3dObject*>(pObj))
341 // reset all selection flags at 3D objects
342 pScene = p3dObject->getRootE3dSceneFromE3dObject();
344 if(nullptr != pScene)
346 pScene->SetSelected(false);
351 if(!bSpecialHandling)
353 // call parent
354 return SdrView::CreateMarkedObjModel();
357 std::unique_ptr<SdrModel> pNewModel;
358 tools::Rectangle aSelectedSnapRect;
360 // set 3d selection flags at all directly selected objects
361 // and collect SnapRect of selected objects
362 for(size_t nObjs = 0; nObjs < nCount; ++nObjs)
364 SdrObject *pObj = GetMarkedObjectByIndex(nObjs);
366 if(auto p3DObj = dynamic_cast<E3dCompoundObject*>(pObj))
368 // mark object, but not scenes
369 p3DObj->SetSelected(true);
370 aSelectedSnapRect.Union(p3DObj->GetSnapRect());
374 // create new mark list which contains all indirectly selected3d
375 // scenes as selected objects
376 SdrMarkList aOldML(GetMarkedObjectList());
377 SdrMarkList aNewML;
378 SdrMarkList& rCurrentMarkList = const_cast<E3dView*>(this)->GetMarkedObjectListWriteAccess();
379 rCurrentMarkList = aNewML;
381 for(size_t nObjs = 0; nObjs < nCount; ++nObjs)
383 SdrObject *pObj = aOldML.GetMark(nObjs)->GetMarkedSdrObj();
385 if(auto p3dObject = dynamic_cast< E3dObject* >(pObj))
387 pScene = p3dObject->getRootE3dSceneFromE3dObject();
389 if(nullptr != pScene && !IsObjMarked(pScene) && GetSdrPageView())
391 const_cast<E3dView*>(this)->MarkObj(pScene, GetSdrPageView(), false, true);
396 // call parent. This will copy all scenes and the selection flags at the 3D objects. So
397 // it will be possible to delete all non-selected 3d objects from the cloned 3d scenes
398 pNewModel = SdrView::CreateMarkedObjModel();
400 if(pNewModel)
402 for(sal_uInt16 nPg(0); nPg < pNewModel->GetPageCount(); nPg++)
404 const SdrPage* pSrcPg=pNewModel->GetPage(nPg);
405 const size_t nObjCount(pSrcPg->GetObjCount());
407 for(size_t nOb = 0; nOb < nObjCount; ++nOb)
409 const SdrObject* pSrcOb=pSrcPg->GetObj(nOb);
411 if(auto p3dscene = dynamic_cast< const E3dScene* >( pSrcOb))
413 pScene = const_cast<E3dScene*>(p3dscene);
415 // delete all not intentionally cloned 3d objects
416 pScene->removeAllNonSelectedObjects();
418 // reset select flags and set SnapRect of all selected objects
419 pScene->SetSelected(false);
420 pScene->SetSnapRect(aSelectedSnapRect);
426 // restore old selection
427 rCurrentMarkList = aOldML;
429 return pNewModel;
432 // When pasting objects have to integrated if a scene is inserted, but
433 // not the scene itself
435 bool E3dView::Paste(
436 const SdrModel& rMod, const Point& rPos, SdrObjList* pLst, SdrInsertFlags nOptions)
438 bool bRetval = false;
440 // Get list
441 Point aPos(rPos);
442 SdrObjList* pDstList = pLst;
443 ImpGetPasteObjList(aPos, pDstList);
445 if(!pDstList)
446 return false;
448 // Get owner of the list
449 E3dScene* pDstScene(dynamic_cast< E3dScene* >(pDstList->getSdrObjectFromSdrObjList()));
451 if(nullptr != pDstScene)
453 BegUndo(SvxResId(RID_SVX_3D_UNDO_EXCHANGE_PASTE));
455 // Copy all objects from E3dScenes and insert them directly
456 for(sal_uInt16 nPg(0); nPg < rMod.GetPageCount(); nPg++)
458 const SdrPage* pSrcPg=rMod.GetPage(nPg);
459 const size_t nObjCount(pSrcPg->GetObjCount());
461 // calculate offset for paste
462 tools::Rectangle aR = pSrcPg->GetAllObjBoundRect();
463 Point aDist(aPos - aR.Center());
465 // Insert sub-objects for scenes
466 for(size_t nOb = 0; nOb < nObjCount; ++nOb)
468 const SdrObject* pSrcOb = pSrcPg->GetObj(nOb);
469 if(auto p3dscene = dynamic_cast< const E3dScene* >(pSrcOb))
471 E3dScene* pSrcScene = const_cast<E3dScene*>(p3dscene);
472 ImpCloneAll3DObjectsToDestScene(pSrcScene, pDstScene, aDist);
476 EndUndo();
478 else
480 // call parent
481 bRetval = SdrView::Paste(rMod, rPos, pLst, nOptions);
484 return bRetval;
487 // Service routine used from local Clone() and from SdrCreateView::EndCreateObj(...)
488 bool E3dView::ImpCloneAll3DObjectsToDestScene(E3dScene const * pSrcScene, E3dScene* pDstScene, Point /*aOffset*/)
490 bool bRetval(false);
492 if(pSrcScene && pDstScene)
494 for(size_t i = 0; i < pSrcScene->GetSubList()->GetObjCount(); ++i)
496 E3dCompoundObject* pCompoundObj = dynamic_cast< E3dCompoundObject* >(pSrcScene->GetSubList()->GetObj(i));
498 if(pCompoundObj)
500 E3dCompoundObject* pNewCompoundObj(pCompoundObj->CloneSdrObject(pDstScene->getSdrModelFromSdrObject()));
502 if(pNewCompoundObj)
504 // get dest scene's current range in 3D world coordinates
505 const basegfx::B3DHomMatrix aSceneToWorldTrans(pDstScene->GetFullTransform());
506 basegfx::B3DRange aSceneRange(pDstScene->GetBoundVolume());
507 aSceneRange.transform(aSceneToWorldTrans);
509 // get new object's implied object transformation
510 const basegfx::B3DHomMatrix aNewObjectTrans(pNewCompoundObj->GetTransform());
512 // get new object's range in 3D world coordinates in dest scene
513 // as if it were already added
514 const basegfx::B3DHomMatrix aObjectToWorldTrans(aSceneToWorldTrans * aNewObjectTrans);
515 basegfx::B3DRange aObjectRange(pNewCompoundObj->GetBoundVolume());
516 aObjectRange.transform(aObjectToWorldTrans);
518 // get scale adaptation
519 const basegfx::B3DVector aSceneScale(aSceneRange.getRange());
520 const basegfx::B3DVector aObjectScale(aObjectRange.getRange());
521 double fScale(1.0);
523 // if new object's size in X,Y or Z is bigger that 80% of dest scene, adapt scale
524 // to not change the scene by the inserted object
525 const double fSizeFactor(0.5);
527 if(aObjectScale.getX() * fScale > aSceneScale.getX() * fSizeFactor)
529 const double fObjSize(aObjectScale.getX() * fScale);
530 const double fFactor((aSceneScale.getX() * fSizeFactor) / (basegfx::fTools::equalZero(fObjSize) ? 1.0 : fObjSize));
531 fScale *= fFactor;
534 if(aObjectScale.getY() * fScale > aSceneScale.getY() * fSizeFactor)
536 const double fObjSize(aObjectScale.getY() * fScale);
537 const double fFactor((aSceneScale.getY() * fSizeFactor) / (basegfx::fTools::equalZero(fObjSize) ? 1.0 : fObjSize));
538 fScale *= fFactor;
541 if(aObjectScale.getZ() * fScale > aSceneScale.getZ() * fSizeFactor)
543 const double fObjSize(aObjectScale.getZ() * fScale);
544 const double fFactor((aSceneScale.getZ() * fSizeFactor) / (basegfx::fTools::equalZero(fObjSize) ? 1.0 : fObjSize));
545 fScale *= fFactor;
548 // get translation adaptation
549 const basegfx::B3DPoint aSceneCenter(aSceneRange.getCenter());
550 const basegfx::B3DPoint aObjectCenter(aObjectRange.getCenter());
552 // build full modification transform. The object's transformation
553 // shall be modified, so start at object coordinates; transform to 3d world coor
554 basegfx::B3DHomMatrix aModifyingTransform(aObjectToWorldTrans);
556 // translate to absolute center in 3d world coor
557 aModifyingTransform.translate(-aObjectCenter.getX(), -aObjectCenter.getY(), -aObjectCenter.getZ());
559 // scale to dest size in 3d world coor
560 aModifyingTransform.scale(fScale, fScale, fScale);
562 // translate to dest scene center in 3d world coor
563 aModifyingTransform.translate(aSceneCenter.getX(), aSceneCenter.getY(), aSceneCenter.getZ());
565 // transform from 3d world to dest object coordinates
566 basegfx::B3DHomMatrix aWorldToObject(aObjectToWorldTrans);
567 aWorldToObject.invert();
568 aModifyingTransform = aWorldToObject * aModifyingTransform;
570 // correct implied object transform by applying changing one in object coor
571 pNewCompoundObj->SetTransform(aModifyingTransform * aNewObjectTrans);
573 // fill and insert new object
574 pNewCompoundObj->NbcSetLayer(pCompoundObj->GetLayer());
575 pNewCompoundObj->NbcSetStyleSheet(pCompoundObj->GetStyleSheet(), true);
576 pDstScene->InsertObject(pNewCompoundObj);
577 bRetval = true;
579 // Create undo
580 if( GetModel()->IsUndoEnabled() )
581 AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoNewObject(*pNewCompoundObj));
587 return bRetval;
590 bool E3dView::IsConvertTo3DObjPossible() const
592 bool bAny3D(false);
593 bool bGroupSelected(false);
594 bool bRetval(true);
596 for(size_t a=0; !bAny3D && a<GetMarkedObjectCount(); ++a)
598 SdrObject *pObj = GetMarkedObjectByIndex(a);
599 if(pObj)
601 ImpIsConvertTo3DPossible(pObj, bAny3D, bGroupSelected);
605 bRetval = !bAny3D
606 && (
607 IsConvertToPolyObjPossible()
608 || IsConvertToPathObjPossible()
609 || IsImportMtfPossible());
610 return bRetval;
613 void E3dView::ImpIsConvertTo3DPossible(SdrObject const * pObj, bool& rAny3D,
614 bool& rGroupSelected) const
616 if(!pObj)
617 return;
619 if(dynamic_cast< const E3dObject* >(pObj) != nullptr)
621 rAny3D = true;
623 else
625 if(pObj->IsGroupObject())
627 SdrObjListIter aIter(*pObj, SdrIterMode::DeepNoGroups);
628 while(aIter.IsMore())
630 SdrObject* pNewObj = aIter.Next();
631 ImpIsConvertTo3DPossible(pNewObj, rAny3D, rGroupSelected);
633 rGroupSelected = true;
638 void E3dView::ImpChangeSomeAttributesFor3DConversion(SdrObject* pObj)
640 if(dynamic_cast<const SdrTextObj*>( pObj) == nullptr)
641 return;
643 const SfxItemSet& rSet = pObj->GetMergedItemSet();
644 const SvxColorItem& rTextColorItem = rSet.Get(EE_CHAR_COLOR);
645 if(rTextColorItem.GetValue() != COL_BLACK)
646 return;
648 //For black text objects, the color set to gray
649 if(pObj->getSdrPageFromSdrObject())
651 // if black is only default attribute from
652 // pattern set it hard so that it is used in undo.
653 pObj->SetMergedItem(SvxColorItem(COL_BLACK, EE_CHAR_COLOR));
655 // add undo now
656 if( GetModel()->IsUndoEnabled() )
657 AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoAttrObject(*pObj));
660 pObj->SetMergedItem(SvxColorItem(COL_GRAY, EE_CHAR_COLOR));
663 void E3dView::ImpChangeSomeAttributesFor3DConversion2(SdrObject* pObj)
665 if(dynamic_cast<const SdrPathObj*>( pObj) == nullptr)
666 return;
668 const SfxItemSet& rSet = pObj->GetMergedItemSet();
669 sal_Int32 nLineWidth = rSet.Get(XATTR_LINEWIDTH).GetValue();
670 drawing::LineStyle eLineStyle = rSet.Get(XATTR_LINESTYLE).GetValue();
671 drawing::FillStyle eFillStyle = rSet.Get(XATTR_FILLSTYLE).GetValue();
673 if(static_cast<SdrPathObj*>(pObj)->IsClosed()
674 && eLineStyle == drawing::LineStyle_SOLID
675 && !nLineWidth
676 && eFillStyle != drawing::FillStyle_NONE)
678 if(pObj->getSdrPageFromSdrObject() && GetModel()->IsUndoEnabled() )
680 AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoAttrObject(*pObj));
683 pObj->SetMergedItem(XLineStyleItem(drawing::LineStyle_NONE));
684 pObj->SetMergedItem(XLineWidthItem(0));
688 void E3dView::ImpCreateSingle3DObjectFlat(E3dScene* pScene, SdrObject* pObj, bool bExtrude, double fDepth, basegfx::B2DHomMatrix const & rLatheMat)
690 // Single PathObject, transform this
691 SdrPathObj* pPath = dynamic_cast<SdrPathObj*>( pObj );
693 if(!pPath)
694 return;
696 E3dDefaultAttributes aDefault = Get3DDefaultAttributes();
698 if(bExtrude)
700 aDefault.SetDefaultExtrudeCharacterMode(true);
702 else
704 aDefault.SetDefaultLatheCharacterMode(true);
707 // Get Itemset of the original object
708 SfxItemSet aSet(pObj->GetMergedItemSet());
710 drawing::FillStyle eFillStyle = aSet.Get(XATTR_FILLSTYLE).GetValue();
712 // line style turned off
713 aSet.Put(XLineStyleItem(drawing::LineStyle_NONE));
715 //Determining if FILL_Attribute is set.
716 if(!pPath->IsClosed() || eFillStyle == drawing::FillStyle_NONE)
718 // This SdrPathObj is not filled, leave the front and rear face out.
719 // Moreover, a two-sided representation necessary.
720 aDefault.SetDefaultExtrudeCloseFront(false);
721 aDefault.SetDefaultExtrudeCloseBack(false);
723 aSet.Put(makeSvx3DDoubleSidedItem(true));
725 // Set fill attribute
726 aSet.Put(XFillStyleItem(drawing::FillStyle_SOLID));
728 // Fill color must be the color line, because the object was
729 // previously just a line
730 Color aColorLine = aSet.Get(XATTR_LINECOLOR).GetColorValue();
731 aSet.Put(XFillColorItem(OUString(), aColorLine));
734 // Create a new extrude object
735 E3dObject* p3DObj = nullptr;
736 if(bExtrude)
738 p3DObj = new E3dExtrudeObj(pObj->getSdrModelFromSdrObject(), aDefault, pPath->GetPathPoly(), fDepth);
740 else
742 // rLatheMat expects coordinates with y-axis up, pPath uses y-axis down
743 basegfx::B2DHomMatrix aFlipVerticalMat(1.0, 0.0, 0.0, 0.0, -1.0, 0.0);
744 basegfx::B2DPolyPolygon aPolyPoly2D(pPath->GetPathPoly());
745 aPolyPoly2D.transform(aFlipVerticalMat);
746 aPolyPoly2D.transform(rLatheMat);
747 // ctor E3dLatheObj expects coordinates with y-axis down
748 aPolyPoly2D.transform(aFlipVerticalMat);
749 p3DObj = new E3dLatheObj(pObj->getSdrModelFromSdrObject(), aDefault, aPolyPoly2D);
752 // Set attribute
753 p3DObj->NbcSetLayer(pObj->GetLayer());
755 p3DObj->SetMergedItemSet(aSet);
757 p3DObj->NbcSetStyleSheet(pObj->GetStyleSheet(), true);
759 // Insert a new extrude object
760 pScene->InsertObject(p3DObj);
763 void E3dView::ImpCreate3DObject(E3dScene* pScene, SdrObject* pObj, bool bExtrude, double fDepth, basegfx::B2DHomMatrix const & rLatheMat)
765 if(!pObj)
766 return;
768 // change text color attribute for not so dark colors
769 if(pObj->IsGroupObject())
771 SdrObjListIter aIter(*pObj, SdrIterMode::DeepWithGroups);
772 while(aIter.IsMore())
774 SdrObject* pGroupMember = aIter.Next();
775 ImpChangeSomeAttributesFor3DConversion(pGroupMember);
778 else
779 ImpChangeSomeAttributesFor3DConversion(pObj);
781 // convert completely to path objects
782 SdrObject* pNewObj1 = pObj->ConvertToPolyObj(false, false).release();
784 if(!pNewObj1)
785 return;
787 // change text color attribute for not so dark colors
788 if(pNewObj1->IsGroupObject())
790 SdrObjListIter aIter(*pNewObj1, SdrIterMode::DeepWithGroups);
791 while(aIter.IsMore())
793 SdrObject* pGroupMember = aIter.Next();
794 ImpChangeSomeAttributesFor3DConversion2(pGroupMember);
797 else
798 ImpChangeSomeAttributesFor3DConversion2(pNewObj1);
800 // convert completely to path objects
801 SdrObject* pNewObj2 = pObj->ConvertToContourObj(pNewObj1, true);
803 if(pNewObj2)
805 // add all to flat scene
806 if(pNewObj2->IsGroupObject())
808 SdrObjListIter aIter(*pNewObj2, SdrIterMode::DeepWithGroups);
809 while(aIter.IsMore())
811 SdrObject* pGroupMember = aIter.Next();
812 ImpCreateSingle3DObjectFlat(pScene, pGroupMember, bExtrude, fDepth, rLatheMat);
815 else
816 ImpCreateSingle3DObjectFlat(pScene, pNewObj2, bExtrude, fDepth, rLatheMat);
818 // delete object in between
819 if (pNewObj2 != pObj && pNewObj2 != pNewObj1)
820 SdrObject::Free( pNewObj2 );
823 // delete object in between
824 if (pNewObj1 != pObj)
825 SdrObject::Free( pNewObj1 );
828 void E3dView::ConvertMarkedObjTo3D(bool bExtrude, const basegfx::B2DPoint& rPnt1, const basegfx::B2DPoint& rPnt2)
830 if(!AreObjectsMarked())
831 return;
833 // Create undo
834 if(bExtrude)
835 BegUndo(SvxResId(RID_SVX_3D_UNDO_EXTRUDE));
836 else
837 BegUndo(SvxResId(RID_SVX_3D_UNDO_LATHE));
839 SdrModel& rSdrModel(GetSdrMarkByIndex(0)->GetMarkedSdrObj()->getSdrModelFromSdrObject());
841 // Create a new scene for the created 3D object
842 E3dScene* pScene = new E3dScene(rSdrModel);
844 // Determine rectangle and possibly correct it
845 tools::Rectangle aRect = GetAllMarkedRect();
846 if(aRect.GetWidth() <= 1)
847 aRect.SetSize(Size(500, aRect.GetHeight()));
848 if(aRect.GetHeight() <= 1)
849 aRect.SetSize(Size(aRect.GetWidth(), 500));
851 // Determine the depth relative to the size of the selection
852 double fDepth = 0.0;
853 double fRot3D = 0.0;
854 basegfx::B2DHomMatrix aLatheMat;
856 if(bExtrude)
858 double fW = static_cast<double>(aRect.GetWidth());
859 double fH = static_cast<double>(aRect.GetHeight());
860 fDepth = sqrt(fW*fW + fH*fH) / 6.0;
862 if(!bExtrude)
864 // Create transformation for the polygons rotating body
865 if (rPnt1 != rPnt2)
867 // Rotation around control point #1 with set angle
868 // for 3D coordinates
869 basegfx::B2DPoint aDiff(rPnt1 - rPnt2);
870 fRot3D = atan2(aDiff.getY(), aDiff.getX()) - F_PI2;
872 if(basegfx::fTools::equalZero(fabs(fRot3D)))
873 fRot3D = 0.0;
875 if(fRot3D != 0.0)
877 aLatheMat = basegfx::utils::createRotateAroundPoint(rPnt2, -fRot3D)
878 * aLatheMat;
882 if (rPnt2.getX() != 0.0)
884 // Translation to Y=0 - axis
885 aLatheMat.translate(-rPnt2.getX(), 0.0);
887 else
889 aLatheMat.translate(static_cast<double>(-aRect.Left()), 0.0);
892 // Form the inverse matrix to determine the target expansion
893 basegfx::B2DHomMatrix aInvLatheMat(aLatheMat);
894 aInvLatheMat.invert();
896 // SnapRect extension enables mirroring in the axis of rotation
897 for(size_t a=0; a<GetMarkedObjectCount(); ++a)
899 SdrMark* pMark = GetSdrMarkByIndex(a);
900 SdrObject* pObj = pMark->GetMarkedSdrObj();
901 tools::Rectangle aTurnRect = pObj->GetSnapRect();
902 basegfx::B2DPoint aRot;
903 Point aRotPnt;
905 aRot = basegfx::B2DPoint(aTurnRect.Left(), -aTurnRect.Top());
906 aRot *= aLatheMat;
907 aRot.setX(-aRot.getX());
908 aRot *= aInvLatheMat;
909 aRotPnt = Point(static_cast<tools::Long>(aRot.getX() + 0.5), static_cast<tools::Long>(-aRot.getY() - 0.5));
910 aRect.Union(tools::Rectangle(aRotPnt, aRotPnt));
912 aRot = basegfx::B2DPoint(aTurnRect.Left(), -aTurnRect.Bottom());
913 aRot *= aLatheMat;
914 aRot.setX(-aRot.getX());
915 aRot *= aInvLatheMat;
916 aRotPnt = Point(static_cast<tools::Long>(aRot.getX() + 0.5), static_cast<tools::Long>(-aRot.getY() - 0.5));
917 aRect.Union(tools::Rectangle(aRotPnt, aRotPnt));
919 aRot = basegfx::B2DPoint(aTurnRect.Right(), -aTurnRect.Top());
920 aRot *= aLatheMat;
921 aRot.setX(-aRot.getX());
922 aRot *= aInvLatheMat;
923 aRotPnt = Point(static_cast<tools::Long>(aRot.getX() + 0.5), static_cast<tools::Long>(-aRot.getY() - 0.5));
924 aRect.Union(tools::Rectangle(aRotPnt, aRotPnt));
926 aRot = basegfx::B2DPoint(aTurnRect.Right(), -aTurnRect.Bottom());
927 aRot *= aLatheMat;
928 aRot.setX(-aRot.getX());
929 aRot *= aInvLatheMat;
930 aRotPnt = Point(static_cast<tools::Long>(aRot.getX() + 0.5), static_cast<tools::Long>(-aRot.getY() - 0.5));
931 aRect.Union(tools::Rectangle(aRotPnt, aRotPnt));
935 // Walk through the selection and convert it into 3D, complete with
936 // Conversion to SdrPathObject, also fonts
937 for(size_t a=0; a<GetMarkedObjectCount(); ++a)
939 SdrMark* pMark = GetSdrMarkByIndex(a);
940 SdrObject* pObj = pMark->GetMarkedSdrObj();
942 ImpCreate3DObject(pScene, pObj, bExtrude, fDepth, aLatheMat);
945 if(pScene->GetSubList() && pScene->GetSubList()->GetObjCount() != 0)
947 // Arrange all created objects by depth
948 if(bExtrude)
949 DoDepthArrange(pScene, fDepth);
951 // Center 3D objects in the middle of the overall rectangle
952 basegfx::B3DPoint aCenter(pScene->GetBoundVolume().getCenter());
953 basegfx::B3DHomMatrix aMatrix;
955 aMatrix.translate(-aCenter.getX(), -aCenter.getY(), -aCenter.getZ());
956 pScene->SetTransform(aMatrix * pScene->GetTransform());
958 // Initialize scene
959 pScene->NbcSetSnapRect(aRect);
960 basegfx::B3DRange aBoundVol = pScene->GetBoundVolume();
961 InitScene(pScene, static_cast<double>(aRect.GetWidth()), static_cast<double>(aRect.GetHeight()), aBoundVol.getDepth());
963 // Insert scene instead of the first selected object and throw away
964 // all the old objects
965 SdrObject* pRepObj = GetMarkedObjectByIndex(0);
966 SdrPageView* pPV = GetSdrPageViewOfMarkedByIndex(0);
967 MarkObj(pRepObj, pPV, true);
968 ReplaceObjectAtView(pRepObj, *pPV, pScene, false);
969 DeleteMarked();
970 MarkObj(pScene, pPV);
972 // Rotate Rotation body around the axis of rotation
973 if(!bExtrude && fRot3D != 0.0)
975 basegfx::B3DHomMatrix aRotate;
976 aRotate.rotate(0.0, 0.0, fRot3D);
977 pScene->SetTransform(aRotate * pScene->GetTransform());
980 // Set default rotation
982 basegfx::B3DHomMatrix aRotate;
983 aRotate.rotate(DEG2RAD(20.0), 0.0, 0.0);
984 // E3DModifySceneSnapRectUpdater updates the 2D representation of the scene.
985 // It prepares things in ctor and acts in dtor.
986 E3DModifySceneSnapRectUpdater aUpdater(pScene->getSdrObjectFromSdrObjList());
987 pScene->SetTransform(aRotate * pScene->GetTransform());
990 else
992 // No 3D object was created, throw away everything
993 // always use SdrObject::Free(...) for SdrObjects (!)
994 SdrObject* pTemp(pScene);
995 SdrObject::Free(pTemp);
998 EndUndo();
1001 //Arrange all created extrude objects by depth
1003 namespace {
1005 struct E3dDepthNeighbour
1007 E3dExtrudeObj* mpObj;
1008 basegfx::B2DPolyPolygon maPreparedPolyPolygon;
1010 E3dDepthNeighbour(E3dExtrudeObj* pObj, basegfx::B2DPolyPolygon const & rPreparedPolyPolygon)
1011 : mpObj(pObj),
1012 maPreparedPolyPolygon(rPreparedPolyPolygon)
1017 struct E3dDepthLayer
1019 E3dDepthLayer* mpDown;
1020 std::vector<E3dDepthNeighbour> mvNeighbours;
1022 E3dDepthLayer()
1023 : mpDown(nullptr)
1030 void E3dView::DoDepthArrange(E3dScene const * pScene, double fDepth)
1032 if(!(pScene && pScene->GetSubList() && pScene->GetSubList()->GetObjCount() > 1))
1033 return;
1035 SdrObjList* pSubList = pScene->GetSubList();
1036 SdrObjListIter aIter(pSubList, SdrIterMode::Flat);
1037 E3dDepthLayer* pBaseLayer = nullptr;
1038 E3dDepthLayer* pLayer = nullptr;
1039 sal_Int32 nNumLayers = 0;
1041 while(aIter.IsMore())
1043 E3dExtrudeObj* pExtrudeObj = dynamic_cast< E3dExtrudeObj* >(aIter.Next());
1045 if(pExtrudeObj)
1047 const basegfx::B2DPolyPolygon aExtrudePoly(
1048 basegfx::utils::prepareForPolygonOperation(pExtrudeObj->GetExtrudePolygon()));
1049 const SfxItemSet& rLocalSet = pExtrudeObj->GetMergedItemSet();
1050 const drawing::FillStyle eLocalFillStyle = rLocalSet.Get(XATTR_FILLSTYLE).GetValue();
1051 const Color aLocalColor = rLocalSet.Get(XATTR_FILLCOLOR).GetColorValue();
1053 // sort in ExtrudeObj
1054 if(pLayer)
1056 // do we have overlap with an object of this layer?
1057 bool bOverlap(false);
1059 for(const auto& rAct : pLayer->mvNeighbours)
1061 // do rAct.mpObj and pExtrudeObj overlap? Check by
1062 // using logical AND clipping
1063 const basegfx::B2DPolyPolygon aAndPolyPolygon(
1064 basegfx::utils::solvePolygonOperationAnd(
1065 aExtrudePoly,
1066 rAct.maPreparedPolyPolygon));
1068 if(aAndPolyPolygon.count() != 0)
1070 // second criteria: is another fillstyle or color used?
1071 const SfxItemSet& rCompareSet = rAct.mpObj->GetMergedItemSet();
1073 drawing::FillStyle eCompareFillStyle = rCompareSet.Get(XATTR_FILLSTYLE).GetValue();
1075 if(eLocalFillStyle == eCompareFillStyle)
1077 if(eLocalFillStyle == drawing::FillStyle_SOLID)
1079 Color aCompareColor = rCompareSet.Get(XATTR_FILLCOLOR).GetColorValue();
1081 if(aCompareColor == aLocalColor)
1083 continue;
1086 else if(eLocalFillStyle == drawing::FillStyle_NONE)
1088 continue;
1092 bOverlap = true;
1093 break;
1097 if(bOverlap)
1099 // yes, start a new layer
1100 pLayer->mpDown = new E3dDepthLayer;
1101 pLayer = pLayer->mpDown;
1102 nNumLayers++;
1103 pLayer->mvNeighbours.emplace_back(pExtrudeObj, aExtrudePoly);
1105 else
1107 // no, add to current layer
1108 pLayer->mvNeighbours.emplace(pLayer->mvNeighbours.begin(), pExtrudeObj, aExtrudePoly);
1111 else
1113 // first layer ever
1114 pBaseLayer = new E3dDepthLayer;
1115 pLayer = pBaseLayer;
1116 nNumLayers++;
1117 pLayer->mvNeighbours.emplace_back(pExtrudeObj, aExtrudePoly);
1122 // number of layers is done
1123 if(nNumLayers > 1)
1125 // need to be arranged
1126 double fMinDepth = fDepth * 0.8;
1127 double fStep = (fDepth - fMinDepth) / static_cast<double>(nNumLayers);
1128 pLayer = pBaseLayer;
1130 while(pLayer)
1132 // move along layer
1133 for(auto& rAct : pLayer->mvNeighbours)
1135 // adapt extrude value
1136 rAct.mpObj->SetMergedItem(SfxUInt32Item(SDRATTR_3DOBJ_DEPTH, sal_uInt32(fMinDepth + 0.5)));
1139 // next layer
1140 pLayer = pLayer->mpDown;
1141 fMinDepth += fStep;
1145 // cleanup
1146 while(pBaseLayer)
1148 pLayer = pBaseLayer->mpDown;
1149 delete pBaseLayer;
1150 pBaseLayer = pLayer;
1154 // Start drag, create for 3D objects before possibly drag method
1156 bool E3dView::BegDragObj(const Point& rPnt, OutputDevice* pOut,
1157 SdrHdl* pHdl, short nMinMov,
1158 SdrDragMethod* pForcedMeth)
1160 if(Is3DRotationCreationActive() && GetMarkedObjectCount())
1162 // Determine all selected polygons and return the mirrored helper overlay
1163 mpMirrorOverlay->SetMirrorAxis(maRef1, maRef2);
1165 else
1167 bool bOwnActionNecessary;
1168 if (pHdl == nullptr)
1170 bOwnActionNecessary = true;
1172 else if (pHdl->IsVertexHdl() || pHdl->IsCornerHdl())
1174 bOwnActionNecessary = true;
1176 else
1178 bOwnActionNecessary = false;
1181 if(bOwnActionNecessary && GetMarkedObjectCount() > 0)
1183 E3dDragConstraint eConstraint = E3dDragConstraint::XYZ;
1184 bool bThereAreRootScenes = false;
1185 bool bThereAre3DObjects = false;
1186 const size_t nCnt = GetMarkedObjectCount();
1187 for(size_t nObjs = 0; nObjs < nCnt; ++nObjs)
1189 SdrObject *pObj = GetMarkedObjectByIndex(nObjs);
1190 if(pObj)
1192 if(nullptr != dynamic_cast< const E3dScene* >(pObj) && static_cast< E3dScene* >(pObj)->getRootE3dSceneFromE3dObject() == pObj)
1194 bThereAreRootScenes = true;
1197 if(dynamic_cast< const E3dObject* >(pObj) != nullptr)
1199 bThereAre3DObjects = true;
1203 if( bThereAre3DObjects )
1205 meDragHdl = ( pHdl == nullptr ? SdrHdlKind::Move : pHdl->GetKind() );
1206 switch ( meDragMode )
1208 case SdrDragMode::Rotate:
1209 case SdrDragMode::Shear:
1211 switch ( meDragHdl )
1213 case SdrHdlKind::Left:
1214 case SdrHdlKind::Right:
1216 eConstraint = E3dDragConstraint::X;
1218 break;
1220 case SdrHdlKind::Upper:
1221 case SdrHdlKind::Lower:
1223 eConstraint = E3dDragConstraint::Y;
1225 break;
1227 case SdrHdlKind::UpperLeft:
1228 case SdrHdlKind::UpperRight:
1229 case SdrHdlKind::LowerLeft:
1230 case SdrHdlKind::LowerRight:
1232 eConstraint = E3dDragConstraint::Z;
1234 break;
1235 default: break;
1238 // do not mask the allowed rotations
1239 eConstraint &= E3dDragConstraint::XYZ;
1240 pForcedMeth = new E3dDragRotate(*this, GetMarkedObjectList(), eConstraint, IsSolidDragging());
1242 break;
1244 case SdrDragMode::Move:
1246 if(!bThereAreRootScenes)
1248 pForcedMeth = new E3dDragMove(*this, GetMarkedObjectList(), meDragHdl, eConstraint, IsSolidDragging());
1251 break;
1253 // later on
1254 case SdrDragMode::Mirror:
1255 case SdrDragMode::Crook:
1256 case SdrDragMode::Transparence:
1257 case SdrDragMode::Gradient:
1258 default:
1261 break;
1266 return SdrView::BegDragObj(rPnt, pOut, pHdl, nMinMov, pForcedMeth);
1269 // Set current 3D drawing object, create the scene for this
1270 E3dScene* E3dView::SetCurrent3DObj(E3dObject* p3DObj)
1272 DBG_ASSERT(p3DObj != nullptr, "Who puts in a NULL-pointer here");
1274 // get transformed BoundVolume of the object
1275 basegfx::B3DRange aVolume(p3DObj->GetBoundVolume());
1276 aVolume.transform(p3DObj->GetTransform());
1277 double fW(aVolume.getWidth());
1278 double fH(aVolume.getHeight());
1280 tools::Rectangle aRect(0,0, static_cast<tools::Long>(fW), static_cast<tools::Long>(fH));
1282 E3dScene* pScene = new E3dScene(p3DObj->getSdrModelFromSdrObject());
1284 InitScene(pScene, fW, fH, aVolume.getMaxZ() + ((fW + fH) / 4.0));
1286 pScene->InsertObject(p3DObj);
1287 pScene->NbcSetSnapRect(aRect);
1289 return pScene;
1292 void E3dView::InitScene(E3dScene* pScene, double fW, double fH, double fCamZ)
1294 Camera3D aCam(pScene->GetCamera());
1296 aCam.SetAutoAdjustProjection(false);
1297 aCam.SetViewWindow(- fW / 2, - fH / 2, fW, fH);
1298 basegfx::B3DPoint aLookAt;
1300 double fDefaultCamPosZ = GetDefaultCamPosZ();
1301 basegfx::B3DPoint aCamPos(0.0, 0.0, fCamZ < fDefaultCamPosZ ? fDefaultCamPosZ : fCamZ);
1303 aCam.SetPosAndLookAt(aCamPos, aLookAt);
1304 aCam.SetFocalLength(GetDefaultCamFocal());
1305 pScene->SetCamera(aCam);
1308 void E3dView::Start3DCreation()
1310 if (!GetMarkedObjectCount())
1311 return;
1313 //positioned
1314 tools::Long nOutMin = 0;
1315 tools::Long nOutMax = 0;
1316 tools::Long nMinLen = 0;
1317 tools::Long nObjDst = 0;
1318 tools::Long nOutHgt = 0;
1319 OutputDevice* pOut = GetFirstOutputDevice();
1321 // first determine representation boundaries
1322 if (pOut != nullptr)
1324 nMinLen = pOut->PixelToLogic(Size(0,50)).Height();
1325 nObjDst = pOut->PixelToLogic(Size(0,20)).Height();
1327 tools::Long nDst = pOut->PixelToLogic(Size(0,10)).Height();
1329 nOutMin = -pOut->GetMapMode().GetOrigin().Y();
1330 nOutMax = pOut->GetOutputSize().Height() - 1 + nOutMin;
1331 nOutMin += nDst;
1332 nOutMax -= nDst;
1334 if (nOutMax - nOutMin < nDst)
1336 nOutMin += nOutMax + 1;
1337 nOutMin /= 2;
1338 nOutMin -= (nDst + 1) / 2;
1339 nOutMax = nOutMin + nDst;
1342 nOutHgt = nOutMax - nOutMin;
1344 tools::Long nTemp = nOutHgt / 4;
1345 if (nTemp > nMinLen) nMinLen = nTemp;
1348 // and then attach the marks at the top and bottom of the object
1349 basegfx::B2DRange aR;
1350 for(size_t nMark = 0; nMark < GetMarkedObjectCount(); ++nMark)
1352 SdrObject* pMark = GetMarkedObjectByIndex(nMark);
1353 basegfx::B2DPolyPolygon aXPP(pMark->TakeXorPoly());
1354 aR.expand(basegfx::utils::getRange(aXPP));
1357 basegfx::B2DPoint aCenter(aR.getCenter());
1358 tools::Long nMarkHgt = basegfx::fround(aR.getHeight()) - 1;
1359 tools::Long nHgt = nMarkHgt + nObjDst * 2;
1361 if (nHgt < nMinLen) nHgt = nMinLen;
1363 tools::Long nY1 = basegfx::fround(aCenter.getY()) - (nHgt + 1) / 2;
1364 tools::Long nY2 = nY1 + nHgt;
1366 if (pOut && (nMinLen > nOutHgt)) nMinLen = nOutHgt;
1367 if (pOut)
1369 if (nY1 < nOutMin)
1371 nY1 = nOutMin;
1372 if (nY2 < nY1 + nMinLen) nY2 = nY1 + nMinLen;
1374 if (nY2 > nOutMax)
1376 nY2 = nOutMax;
1377 if (nY1 > nY2 - nMinLen) nY1 = nY2 - nMinLen;
1381 maRef1.setX( basegfx::fround(aR.getMinX()) ); // Initial move axis 2/100mm to the left
1382 maRef1.setY( nY1 );
1383 maRef2.setX( maRef1.X() );
1384 maRef2.setY( nY2 );
1386 // Turn on marks
1387 SetMarkHandles(nullptr);
1389 //HMHif (bVis) ShowMarkHdl();
1390 if (AreObjectsMarked()) MarkListHasChanged();
1392 // Show mirror polygon IMMEDIATELY
1393 const SdrHdlList &aHdlList = GetHdlList();
1394 mpMirrorOverlay.reset(new Impl3DMirrorConstructOverlay(*this));
1395 mpMirrorOverlay->SetMirrorAxis(aHdlList.GetHdl(SdrHdlKind::Ref1)->GetPos(), aHdlList.GetHdl(SdrHdlKind::Ref2)->GetPos());
1398 // what happens with a mouse movement when the object is created?
1400 void E3dView::MovAction(const Point& rPnt)
1402 if(Is3DRotationCreationActive())
1404 SdrHdl* pHdl = GetDragHdl();
1406 if (pHdl)
1408 SdrHdlKind eHdlKind = pHdl->GetKind();
1410 // reacts only due to a mirror axis
1411 if ((eHdlKind == SdrHdlKind::Ref1) ||
1412 (eHdlKind == SdrHdlKind::Ref2) ||
1413 (eHdlKind == SdrHdlKind::MirrorAxis))
1415 const SdrHdlList &aHdlList = GetHdlList ();
1417 // delete the mirrored polygon, mirrors the original and draws
1418 // it anew
1419 SdrView::MovAction (rPnt);
1420 mpMirrorOverlay->SetMirrorAxis(
1421 aHdlList.GetHdl (SdrHdlKind::Ref1)->GetPos(),
1422 aHdlList.GetHdl (SdrHdlKind::Ref2)->GetPos());
1425 else
1427 SdrView::MovAction (rPnt);
1430 else
1432 SdrView::MovAction (rPnt);
1436 // The End. Create object and any child objects through ImpCreate3DLathe.
1437 // With the parameter value sal_True (SDefault: sal_False) is simply a
1438 // rotation body created, without letting the user set the position of the
1439 // axis. It is sufficient with this call, if an object is selected.
1440 // (No initialization necessary)
1442 void E3dView::End3DCreation(bool bUseDefaultValuesForMirrorAxes)
1444 ResetCreationActive();
1446 if(!AreObjectsMarked())
1447 return;
1449 if(bUseDefaultValuesForMirrorAxes)
1451 tools::Rectangle aRect = GetAllMarkedRect();
1452 if(aRect.GetWidth() <= 1)
1453 aRect.SetSize(Size(500, aRect.GetHeight()));
1454 if(aRect.GetHeight() <= 1)
1455 aRect.SetSize(Size(aRect.GetWidth(), 500));
1457 basegfx::B2DPoint aPnt1(aRect.Left(), -aRect.Top());
1458 basegfx::B2DPoint aPnt2(aRect.Left(), -aRect.Bottom());
1460 ConvertMarkedObjTo3D(false, aPnt1, aPnt2);
1462 else
1464 // Turn off helper overlay
1465 // Determine from the handle positions and the displacement of
1466 // the points
1467 const SdrHdlList &aHdlList = GetHdlList();
1468 Point aMirrorRef1 = aHdlList.GetHdl(SdrHdlKind::Ref1)->GetPos();
1469 Point aMirrorRef2 = aHdlList.GetHdl(SdrHdlKind::Ref2)->GetPos();
1471 basegfx::B2DPoint aPnt1(aMirrorRef1.X(), -aMirrorRef1.Y());
1472 basegfx::B2DPoint aPnt2(aMirrorRef2.X(), -aMirrorRef2.Y());
1474 ConvertMarkedObjTo3D(false, aPnt1, aPnt2);
1478 E3dView::~E3dView ()
1482 void E3dView::ResetCreationActive ()
1484 mpMirrorOverlay.reset();
1487 void E3dView::InitView ()
1489 mpMirrorOverlay = nullptr;
1492 bool E3dView::IsBreak3DObjPossible() const
1494 const size_t nCount = GetMarkedObjectCount();
1496 if (nCount > 0)
1498 for (size_t i = 0; i < nCount; ++i)
1500 SdrObject* pObj = GetMarkedObjectByIndex(i);
1502 if (auto p3dObject = dynamic_cast< E3dObject* >(pObj))
1504 if(!p3dObject->IsBreakObjPossible())
1505 return false;
1507 else
1509 return false;
1513 else
1515 return false;
1518 return true;
1521 void E3dView::Break3DObj()
1523 if(!IsBreak3DObjPossible())
1524 return;
1526 // ALL selected objects are changed
1527 const size_t nCount = GetMarkedObjectCount();
1529 BegUndo(SvxResId(RID_SVX_3D_UNDO_BREAK_LATHE));
1530 for(size_t a=0; a<nCount; ++a)
1532 E3dObject* pObj = static_cast<E3dObject*>(GetMarkedObjectByIndex(a));
1533 BreakSingle3DObj(pObj);
1535 DeleteMarked();
1536 EndUndo();
1539 void E3dView::BreakSingle3DObj(E3dObject* pObj)
1541 if(dynamic_cast< const E3dScene* >(pObj) != nullptr)
1543 SdrObjList* pSubList = pObj->GetSubList();
1544 SdrObjListIter aIter(pSubList, SdrIterMode::Flat);
1546 while(aIter.IsMore())
1548 E3dObject* pSubObj = static_cast<E3dObject*>(aIter.Next());
1549 BreakSingle3DObj(pSubObj);
1552 else
1554 SdrAttrObj* pNewObj = pObj->GetBreakObj().release();
1555 if (pNewObj)
1557 if (InsertObjectAtView(pNewObj, *GetSdrPageView(), SdrInsertFlags::DONTMARK))
1559 pNewObj->SetChanged();
1560 pNewObj->BroadcastObjectChange();
1566 void E3dView::CheckPossibilities()
1568 // call parent
1569 SdrView::CheckPossibilities();
1571 // Set other flags
1572 if(!(m_bGroupPossible || m_bUnGroupPossible || m_bGrpEnterPossible))
1573 return;
1575 const size_t nMarkCnt = GetMarkedObjectCount();
1576 bool bCompound = false;
1577 bool b3DObject = false;
1578 for(size_t nObjs = 0; (nObjs < nMarkCnt) && !bCompound; ++nObjs)
1580 SdrObject *pObj = GetMarkedObjectByIndex(nObjs);
1581 if(dynamic_cast< const E3dCompoundObject* >(pObj))
1582 bCompound = true;
1583 if(dynamic_cast< const E3dObject* >(pObj))
1584 b3DObject = true;
1587 // So far: there are two or more of any objects selected. See if
1588 // compound objects are involved. If yes, ban grouping.
1589 if(m_bGroupPossible && bCompound)
1590 m_bGroupPossible = false;
1592 if(m_bUnGroupPossible && b3DObject)
1593 m_bUnGroupPossible = false;
1595 if(m_bGrpEnterPossible && bCompound)
1596 m_bGrpEnterPossible = false;
1599 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */