Revert "Revert "Revert "stronger typing for SwClient::GetRegisteredIn"" and fix SwIte...
[LibreOffice.git] / svx / source / engine3d / view3d.cxx
blob132eca98536b90fac2a5f20f163189e85c526cd5
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>
54 #include <utility>
56 using namespace com::sun::star;
59 // Migrate Marking
61 class Impl3DMirrorConstructOverlay
63 // The OverlayObjects
64 sdr::overlay::OverlayObjectList maObjects;
66 // the view
67 const E3dView& mrView;
69 // the object count
70 size_t mnCount;
72 // the unmirrored polygons
73 basegfx::B2DPolyPolygon* mpPolygons;
75 // the overlay geometry from selected objects
76 drawinglayer::primitive2d::Primitive2DContainer maFullOverlay;
78 // Copy assignment is forbidden and not implemented.
79 Impl3DMirrorConstructOverlay (const Impl3DMirrorConstructOverlay &) = delete;
80 Impl3DMirrorConstructOverlay & operator= (const Impl3DMirrorConstructOverlay &) = delete;
82 public:
83 explicit Impl3DMirrorConstructOverlay(const E3dView& rView);
84 ~Impl3DMirrorConstructOverlay();
86 void SetMirrorAxis(Point aMirrorAxisA, Point aMirrorAxisB);
89 Impl3DMirrorConstructOverlay::Impl3DMirrorConstructOverlay(const E3dView& rView)
90 : mrView(rView),
91 mpPolygons(nullptr)
93 const SdrMarkList& rMarkList = mrView.GetMarkedObjectList();
94 mnCount = rMarkList.GetMarkCount();
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 = rMarkList.GetMark(a)->GetMarkedSdrObj();
108 if(pObject)
110 // use the view-independent primitive representation (without
111 // evtl. GridOffset, that may be applied to the DragEntry individually)
112 pObject->GetViewContact().getViewIndependentPrimitive2DContainer(maFullOverlay);
117 else
119 mpPolygons = new basegfx::B2DPolyPolygon[mnCount];
121 for(size_t a = 0; a < mnCount; ++a)
123 SdrObject* pObject = rMarkList.GetMark(a)->GetMarkedSdrObj();
124 mpPolygons[mnCount - (a + 1)] = pObject->TakeXorPoly();
129 Impl3DMirrorConstructOverlay::~Impl3DMirrorConstructOverlay()
131 // The OverlayObjects are cleared using the destructor of OverlayObjectList.
132 // That destructor calls clear() at the list which removes all objects from the
133 // OverlayManager and deletes them.
134 delete[] mpPolygons;
137 void Impl3DMirrorConstructOverlay::SetMirrorAxis(Point aMirrorAxisA, Point aMirrorAxisB)
139 // get rid of old overlay objects
140 maObjects.clear();
142 // create new ones
143 for(sal_uInt32 a(0); a < mrView.PaintWindowCount(); a++)
145 SdrPaintWindow* pCandidate = mrView.GetPaintWindow(a);
146 const rtl::Reference< sdr::overlay::OverlayManager >& xTargetOverlay = pCandidate->GetOverlayManager();
148 if(xTargetOverlay.is())
150 // build transformation: translate and rotate so that given edge is
151 // on x axis, them mirror in y and translate back
152 const basegfx::B2DVector aEdge(aMirrorAxisB.X() - aMirrorAxisA.X(), aMirrorAxisB.Y() - aMirrorAxisA.Y());
153 basegfx::B2DHomMatrix aMatrixTransform(basegfx::utils::createTranslateB2DHomMatrix(
154 -aMirrorAxisA.X(), -aMirrorAxisA.Y()));
155 aMatrixTransform.rotate(-atan2(aEdge.getY(), aEdge.getX()));
156 aMatrixTransform.scale(1.0, -1.0);
157 aMatrixTransform.rotate(atan2(aEdge.getY(), aEdge.getX()));
158 aMatrixTransform.translate(aMirrorAxisA.X(), aMirrorAxisA.Y());
160 if(mrView.IsSolidDragging())
162 if(!maFullOverlay.empty())
164 drawinglayer::primitive2d::Primitive2DContainer aContent(maFullOverlay);
166 if(!aMatrixTransform.isIdentity())
168 // embed in transformation group
169 aContent = drawinglayer::primitive2d::Primitive2DContainer {
170 new drawinglayer::primitive2d::TransformPrimitive2D(aMatrixTransform, std::move(aContent))
174 // if we have full overlay from selected objects, embed with 50% transparence, the
175 // transformation is added to the OverlayPrimitive2DSequenceObject
176 aContent = drawinglayer::primitive2d::Primitive2DContainer {
177 new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(std::move(aContent), 0.5)
180 std::unique_ptr<sdr::overlay::OverlayPrimitive2DSequenceObject> pNew(new sdr::overlay::OverlayPrimitive2DSequenceObject(std::move(aContent)));
182 xTargetOverlay->add(*pNew);
183 maObjects.append(std::move(pNew));
186 else
188 for(size_t b = 0; b < mnCount; ++b)
190 // apply to polygon
191 basegfx::B2DPolyPolygon aPolyPolygon(mpPolygons[b]);
192 aPolyPolygon.transform(aMatrixTransform);
194 std::unique_ptr<sdr::overlay::OverlayPolyPolygonStripedAndFilled> pNew(new sdr::overlay::OverlayPolyPolygonStripedAndFilled(
195 std::move(aPolyPolygon)));
196 xTargetOverlay->add(*pNew);
197 maObjects.append(std::move(pNew));
204 E3dView::E3dView(
205 SdrModel& rSdrModel,
206 OutputDevice* pOut)
207 : SdrView(rSdrModel, pOut)
209 InitView();
212 // DrawMarkedObj override, since possibly only a single 3D object is to be
213 // drawn
215 void E3dView::DrawMarkedObj(OutputDevice& rOut) const
217 // Does 3D objects exist which scenes are not selected?
218 bool bSpecialHandling = false;
219 E3dScene *pScene = nullptr;
221 const SdrMarkList& rMarkList = GetMarkedObjectList();
222 const size_t nCnt = rMarkList.GetMarkCount();
223 for(size_t nObjs = 0; nObjs < nCnt; ++nObjs)
225 SdrObject *pObj = rMarkList.GetMark(nObjs)->GetMarkedSdrObj();
226 if(auto pCompoundObject = dynamic_cast<E3dCompoundObject*>(pObj))
228 // related scene
229 pScene = pCompoundObject->getRootE3dSceneFromE3dObject();
231 if(nullptr != pScene && !IsObjMarked(pScene))
233 bSpecialHandling = true;
236 // Reset all selection flags
237 if(auto p3dObject = DynCastE3dObject(pObj))
239 pScene = p3dObject->getRootE3dSceneFromE3dObject();
241 if(nullptr != pScene)
243 pScene->SetSelected(false);
248 if(bSpecialHandling)
250 // Set selection flag to "not selected" for scenes related to all 3D
251 // objects
252 for(size_t nObjs = 0; nObjs < nCnt; ++nObjs)
254 SdrObject *pObj = rMarkList.GetMark(nObjs)->GetMarkedSdrObj();
255 if(auto pCompoundObject = dynamic_cast<E3dCompoundObject*>(pObj))
257 // related scene
258 pScene = pCompoundObject->getRootE3dSceneFromE3dObject();
260 if(nullptr != pScene)
262 pScene->SetSelected(false);
267 for(size_t nObjs = 0; nObjs < nCnt; ++nObjs)
269 SdrObject *pObj = rMarkList.GetMark(nObjs)->GetMarkedSdrObj();
270 if(auto p3DObj = DynCastE3dObject(pObj))
272 // Select object
273 p3DObj->SetSelected(true);
274 pScene = p3DObj->getRootE3dSceneFromE3dObject();
278 if(nullptr != pScene)
280 // code from parent
281 rMarkList.ForceSort();
283 pScene->SetDrawOnlySelected(true);
284 pScene->SingleObjectPainter(rOut);
285 pScene->SetDrawOnlySelected(false);
288 // Reset selection flag
289 for(size_t nObjs = 0; nObjs < nCnt; ++nObjs)
291 SdrObject *pObj = rMarkList.GetMark(nObjs)->GetMarkedSdrObj();
292 if(auto pCompoundObject = dynamic_cast<E3dCompoundObject*>(pObj))
294 // related scene
295 pScene = pCompoundObject->getRootE3dSceneFromE3dObject();
297 if(nullptr != pScene)
299 pScene->SetSelected(false);
304 else
306 // call parent
307 SdrExchangeView::DrawMarkedObj(rOut);
311 // override get model, since in some 3D objects an additional scene
312 // must be pushed in
314 std::unique_ptr<SdrModel> E3dView::CreateMarkedObjModel() const
316 // Does 3D objects exist which scenes are not selected?
317 bool bSpecialHandling(false);
318 const SdrMarkList& rMarkList = GetMarkedObjectList();
319 const size_t nCount(rMarkList.GetMarkCount());
320 E3dScene *pScene = nullptr;
322 for(size_t nObjs = 0; nObjs < nCount; ++nObjs)
324 const SdrObject* pObj = rMarkList.GetMark(nObjs)->GetMarkedSdrObj();
326 if(!bSpecialHandling)
327 if(auto pCompoundObj = dynamic_cast< const E3dCompoundObject*>(pObj))
329 // if the object is selected, but it's scene not,
330 // we need special handling
331 pScene = pCompoundObj->getRootE3dSceneFromE3dObject();
333 if(nullptr != pScene && !IsObjMarked(pScene))
335 bSpecialHandling = true;
339 if(auto p3dObject = DynCastE3dObject(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 = rMarkList.GetMark(nObjs)->GetMarkedSdrObj();
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(rMarkList);
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 = DynCastE3dObject(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);
406 for (const rtl::Reference<SdrObject>& pSrcOb : *pSrcPg)
408 if(const E3dScene* p3dscene = DynCastE3dScene( pSrcOb.get()))
410 pScene = const_cast<E3dScene*>(p3dscene);
412 // delete all not intentionally cloned 3d objects
413 pScene->removeAllNonSelectedObjects();
415 // reset select flags and set SnapRect of all selected objects
416 pScene->SetSelected(false);
417 pScene->SetSnapRect(aSelectedSnapRect);
423 // restore old selection
424 rCurrentMarkList = aOldML;
426 return pNewModel;
429 // When pasting objects have to integrated if a scene is inserted, but
430 // not the scene itself
432 bool E3dView::Paste(
433 const SdrModel& rMod, const Point& rPos, SdrObjList* pLst, SdrInsertFlags nOptions)
435 bool bRetval = false;
437 // Get list
438 Point aPos(rPos);
439 SdrObjList* pDstList = pLst;
440 ImpGetPasteObjList(aPos, pDstList);
442 if(!pDstList)
443 return false;
445 // Get owner of the list
446 E3dScene* pDstScene(DynCastE3dScene(pDstList->getSdrObjectFromSdrObjList()));
448 if(nullptr != pDstScene)
450 BegUndo(SvxResId(RID_SVX_3D_UNDO_EXCHANGE_PASTE));
452 // Copy all objects from E3dScenes and insert them directly
453 for(sal_uInt16 nPg(0); nPg < rMod.GetPageCount(); nPg++)
455 const SdrPage* pSrcPg=rMod.GetPage(nPg);
457 // calculate offset for paste
458 tools::Rectangle aR = pSrcPg->GetAllObjBoundRect();
459 Point aDist(aPos - aR.Center());
461 // Insert sub-objects for scenes
462 for (const rtl::Reference<SdrObject>& pSrcOb : *pSrcPg)
464 if(const E3dScene* p3dscene = DynCastE3dScene(pSrcOb.get()))
466 E3dScene* pSrcScene = const_cast<E3dScene*>(p3dscene);
467 ImpCloneAll3DObjectsToDestScene(pSrcScene, pDstScene, aDist);
471 EndUndo();
473 else
475 // call parent
476 bRetval = SdrView::Paste(rMod, rPos, pLst, nOptions);
479 return bRetval;
482 // Service routine used from local Clone() and from SdrCreateView::EndCreateObj(...)
483 bool E3dView::ImpCloneAll3DObjectsToDestScene(E3dScene const * pSrcScene, E3dScene* pDstScene, Point /*aOffset*/)
485 bool bRetval(false);
487 if(pSrcScene && pDstScene)
489 for (const rtl::Reference<SdrObject>& pObj : *pSrcScene->GetSubList())
491 E3dCompoundObject* pCompoundObj = dynamic_cast< E3dCompoundObject* >(pObj.get());
493 if(pCompoundObj)
495 rtl::Reference<E3dCompoundObject> pNewCompoundObj = SdrObject::Clone(*pCompoundObj, pDstScene->getSdrModelFromSdrObject());
497 if(pNewCompoundObj)
499 // get dest scene's current range in 3D world coordinates
500 const basegfx::B3DHomMatrix aSceneToWorldTrans(pDstScene->GetFullTransform());
501 basegfx::B3DRange aSceneRange(pDstScene->GetBoundVolume());
502 aSceneRange.transform(aSceneToWorldTrans);
504 // get new object's implied object transformation
505 const basegfx::B3DHomMatrix aNewObjectTrans(pNewCompoundObj->GetTransform());
507 // get new object's range in 3D world coordinates in dest scene
508 // as if it were already added
509 const basegfx::B3DHomMatrix aObjectToWorldTrans(aSceneToWorldTrans * aNewObjectTrans);
510 basegfx::B3DRange aObjectRange(pNewCompoundObj->GetBoundVolume());
511 aObjectRange.transform(aObjectToWorldTrans);
513 // get scale adaptation
514 const basegfx::B3DVector aSceneScale(aSceneRange.getRange());
515 const basegfx::B3DVector aObjectScale(aObjectRange.getRange());
516 double fScale(1.0);
518 // if new object's size in X,Y or Z is bigger that 80% of dest scene, adapt scale
519 // to not change the scene by the inserted object
520 const double fSizeFactor(0.5);
522 if(aObjectScale.getX() * fScale > aSceneScale.getX() * fSizeFactor)
524 const double fObjSize(aObjectScale.getX() * fScale);
525 const double fFactor((aSceneScale.getX() * fSizeFactor) / (basegfx::fTools::equalZero(fObjSize) ? 1.0 : fObjSize));
526 fScale *= fFactor;
529 if(aObjectScale.getY() * fScale > aSceneScale.getY() * fSizeFactor)
531 const double fObjSize(aObjectScale.getY() * fScale);
532 const double fFactor((aSceneScale.getY() * fSizeFactor) / (basegfx::fTools::equalZero(fObjSize) ? 1.0 : fObjSize));
533 fScale *= fFactor;
536 if(aObjectScale.getZ() * fScale > aSceneScale.getZ() * fSizeFactor)
538 const double fObjSize(aObjectScale.getZ() * fScale);
539 const double fFactor((aSceneScale.getZ() * fSizeFactor) / (basegfx::fTools::equalZero(fObjSize) ? 1.0 : fObjSize));
540 fScale *= fFactor;
543 // get translation adaptation
544 const basegfx::B3DPoint aSceneCenter(aSceneRange.getCenter());
545 const basegfx::B3DPoint aObjectCenter(aObjectRange.getCenter());
547 // build full modification transform. The object's transformation
548 // shall be modified, so start at object coordinates; transform to 3d world coor
549 basegfx::B3DHomMatrix aModifyingTransform(aObjectToWorldTrans);
551 // translate to absolute center in 3d world coor
552 aModifyingTransform.translate(-aObjectCenter.getX(), -aObjectCenter.getY(), -aObjectCenter.getZ());
554 // scale to dest size in 3d world coor
555 aModifyingTransform.scale(fScale, fScale, fScale);
557 // translate to dest scene center in 3d world coor
558 aModifyingTransform.translate(aSceneCenter.getX(), aSceneCenter.getY(), aSceneCenter.getZ());
560 // transform from 3d world to dest object coordinates
561 basegfx::B3DHomMatrix aWorldToObject(aObjectToWorldTrans);
562 aWorldToObject.invert();
563 aModifyingTransform = aWorldToObject * aModifyingTransform;
565 // correct implied object transform by applying changing one in object coor
566 pNewCompoundObj->SetTransform(aModifyingTransform * aNewObjectTrans);
568 // fill and insert new object
569 pNewCompoundObj->NbcSetLayer(pCompoundObj->GetLayer());
570 pNewCompoundObj->NbcSetStyleSheet(pCompoundObj->GetStyleSheet(), true);
571 pDstScene->InsertObject(pNewCompoundObj.get());
572 bRetval = true;
574 // Create undo
575 if( GetModel().IsUndoEnabled() )
576 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoNewObject(*pNewCompoundObj));
582 return bRetval;
585 bool E3dView::IsConvertTo3DObjPossible() const
587 bool bAny3D(false);
588 bool bGroupSelected(false);
589 bool bRetval(true);
591 const SdrMarkList& rMarkList = GetMarkedObjectList();
592 for(size_t a=0; !bAny3D && a<rMarkList.GetMarkCount(); ++a)
594 SdrObject *pObj = rMarkList.GetMark(a)->GetMarkedSdrObj();
595 if(pObj)
597 ImpIsConvertTo3DPossible(pObj, bAny3D, bGroupSelected);
601 bRetval = !bAny3D
602 && (
603 IsConvertToPolyObjPossible()
604 || IsConvertToPathObjPossible()
605 || IsImportMtfPossible());
606 return bRetval;
609 void E3dView::ImpIsConvertTo3DPossible(SdrObject const * pObj, bool& rAny3D,
610 bool& rGroupSelected) const
612 if(!pObj)
613 return;
615 if(DynCastE3dObject(pObj))
617 rAny3D = true;
619 else
621 if(pObj->IsGroupObject())
623 SdrObjListIter aIter(*pObj, SdrIterMode::DeepNoGroups);
624 while(aIter.IsMore())
626 SdrObject* pNewObj = aIter.Next();
627 ImpIsConvertTo3DPossible(pNewObj, rAny3D, rGroupSelected);
629 rGroupSelected = true;
634 void E3dView::ImpChangeSomeAttributesFor3DConversion(SdrObject* pObj)
636 if(DynCastSdrTextObj( pObj) == nullptr)
637 return;
639 const SfxItemSet& rSet = pObj->GetMergedItemSet();
640 const SvxColorItem& rTextColorItem = rSet.Get(EE_CHAR_COLOR);
641 if(rTextColorItem.GetValue() != COL_BLACK)
642 return;
644 //For black text objects, the color set to gray
645 if(pObj->getSdrPageFromSdrObject())
647 // if black is only default attribute from
648 // pattern set it hard so that it is used in undo.
649 pObj->SetMergedItem(SvxColorItem(COL_BLACK, EE_CHAR_COLOR));
651 // add undo now
652 if (GetModel().IsUndoEnabled())
653 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoAttrObject(*pObj));
656 pObj->SetMergedItem(SvxColorItem(COL_GRAY, EE_CHAR_COLOR));
659 void E3dView::ImpChangeSomeAttributesFor3DConversion2(SdrObject* pObj)
661 auto pPathObj = dynamic_cast<const SdrPathObj*>( pObj);
662 if(!pPathObj)
663 return;
665 const SfxItemSet& rSet = pObj->GetMergedItemSet();
666 sal_Int32 nLineWidth = rSet.Get(XATTR_LINEWIDTH).GetValue();
667 drawing::LineStyle eLineStyle = rSet.Get(XATTR_LINESTYLE).GetValue();
668 drawing::FillStyle eFillStyle = rSet.Get(XATTR_FILLSTYLE).GetValue();
670 if(pPathObj->IsClosed()
671 && eLineStyle == drawing::LineStyle_SOLID
672 && !nLineWidth
673 && eFillStyle != drawing::FillStyle_NONE)
675 if (pObj->getSdrPageFromSdrObject() && GetModel().IsUndoEnabled())
677 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoAttrObject(*pObj));
680 pObj->SetMergedItem(XLineStyleItem(drawing::LineStyle_NONE));
681 pObj->SetMergedItem(XLineWidthItem(0));
685 void E3dView::ImpCreateSingle3DObjectFlat(E3dScene* pScene, SdrObject* pObj, bool bExtrude, double fDepth, basegfx::B2DHomMatrix const & rLatheMat)
687 // Single PathObject, transform this
688 SdrPathObj* pPath = dynamic_cast<SdrPathObj*>( pObj );
690 if(!pPath)
691 return;
693 E3dDefaultAttributes aDefault = Get3DDefaultAttributes();
695 if(bExtrude)
697 aDefault.SetDefaultExtrudeCharacterMode(true);
699 else
701 aDefault.SetDefaultLatheCharacterMode(true);
704 // Get Itemset of the original object
705 SfxItemSet aSet(pObj->GetMergedItemSet());
707 drawing::FillStyle eFillStyle = aSet.Get(XATTR_FILLSTYLE).GetValue();
709 // line style turned off
710 aSet.Put(XLineStyleItem(drawing::LineStyle_NONE));
712 //Determining if FILL_Attribute is set.
713 if(!pPath->IsClosed() || eFillStyle == drawing::FillStyle_NONE)
715 // This SdrPathObj is not filled, leave the front and rear face out.
716 // Moreover, a two-sided representation necessary.
717 aDefault.SetDefaultExtrudeCloseFront(false);
718 aDefault.SetDefaultExtrudeCloseBack(false);
720 aSet.Put(makeSvx3DDoubleSidedItem(true));
722 // Set fill attribute
723 aSet.Put(XFillStyleItem(drawing::FillStyle_SOLID));
725 // Fill color must be the color line, because the object was
726 // previously just a line
727 Color aColorLine = aSet.Get(XATTR_LINECOLOR).GetColorValue();
728 aSet.Put(XFillColorItem(OUString(), aColorLine));
731 // Create a new extrude object
732 rtl::Reference<E3dObject> p3DObj;
733 if(bExtrude)
735 p3DObj = new E3dExtrudeObj(pObj->getSdrModelFromSdrObject(), aDefault, pPath->GetPathPoly(), fDepth);
737 else
739 // rLatheMat expects coordinates with y-axis up, pPath uses y-axis down
740 basegfx::B2DHomMatrix aFlipVerticalMat(1.0, 0.0, 0.0, 0.0, -1.0, 0.0);
741 basegfx::B2DPolyPolygon aPolyPoly2D(pPath->GetPathPoly());
742 aPolyPoly2D.transform(aFlipVerticalMat);
743 aPolyPoly2D.transform(rLatheMat);
744 // ctor E3dLatheObj expects coordinates with y-axis down
745 aPolyPoly2D.transform(aFlipVerticalMat);
746 p3DObj = new E3dLatheObj(pObj->getSdrModelFromSdrObject(), aDefault, std::move(aPolyPoly2D));
749 // Set attribute
750 p3DObj->NbcSetLayer(pObj->GetLayer());
752 p3DObj->SetMergedItemSet(aSet);
754 p3DObj->NbcSetStyleSheet(pObj->GetStyleSheet(), true);
756 // Insert a new extrude object
757 pScene->InsertObject(p3DObj.get());
760 void E3dView::ImpCreate3DObject(E3dScene* pScene, SdrObject* pObj, bool bExtrude, double fDepth, basegfx::B2DHomMatrix const & rLatheMat)
762 if(!pObj)
763 return;
765 // change text color attribute for not so dark colors
766 if(pObj->IsGroupObject())
768 SdrObjListIter aIter(*pObj, SdrIterMode::DeepWithGroups);
769 while(aIter.IsMore())
771 SdrObject* pGroupMember = aIter.Next();
772 ImpChangeSomeAttributesFor3DConversion(pGroupMember);
775 else
776 ImpChangeSomeAttributesFor3DConversion(pObj);
778 // convert completely to path objects
779 rtl::Reference<SdrObject> pNewObj1 = pObj->ConvertToPolyObj(false, false);
781 if(!pNewObj1)
782 return;
784 // change text color attribute for not so dark colors
785 if(pNewObj1->IsGroupObject())
787 SdrObjListIter aIter(*pNewObj1, SdrIterMode::DeepWithGroups);
788 while(aIter.IsMore())
790 SdrObject* pGroupMember = aIter.Next();
791 ImpChangeSomeAttributesFor3DConversion2(pGroupMember);
794 else
795 ImpChangeSomeAttributesFor3DConversion2(pNewObj1.get());
797 // convert completely to path objects
798 rtl::Reference<SdrObject> pNewObj2 = pObj->ConvertToContourObj(pNewObj1.get(), true);
800 if(pNewObj2)
802 // add all to flat scene
803 if(pNewObj2->IsGroupObject())
805 SdrObjListIter aIter(*pNewObj2, SdrIterMode::DeepWithGroups);
806 while(aIter.IsMore())
808 SdrObject* pGroupMember = aIter.Next();
809 ImpCreateSingle3DObjectFlat(pScene, pGroupMember, bExtrude, fDepth, rLatheMat);
812 else
813 ImpCreateSingle3DObjectFlat(pScene, pNewObj2.get(), bExtrude, fDepth, rLatheMat);
817 void E3dView::ConvertMarkedObjTo3D(bool bExtrude, const basegfx::B2DPoint& rPnt1, const basegfx::B2DPoint& rPnt2)
819 const SdrMarkList& rMarkList = GetMarkedObjectList();
820 if(rMarkList.GetMarkCount() == 0)
821 return;
823 // Create undo
824 if(bExtrude)
825 BegUndo(SvxResId(RID_SVX_3D_UNDO_EXTRUDE));
826 else
827 BegUndo(SvxResId(RID_SVX_3D_UNDO_LATHE));
829 SdrModel& rSdrModel(rMarkList.GetMark(0)->GetMarkedSdrObj()->getSdrModelFromSdrObject());
831 // Create a new scene for the created 3D object
832 rtl::Reference<E3dScene> pScene = new E3dScene(rSdrModel);
834 // Determine rectangle and possibly correct it
835 tools::Rectangle aRect = GetAllMarkedRect();
836 if(aRect.GetWidth() <= 1)
837 aRect.SetSize(Size(500, aRect.GetHeight()));
838 if(aRect.GetHeight() <= 1)
839 aRect.SetSize(Size(aRect.GetWidth(), 500));
841 // Determine the depth relative to the size of the selection
842 double fDepth = 0.0;
843 double fRot3D = 0.0;
844 basegfx::B2DHomMatrix aLatheMat;
846 if(bExtrude)
848 fDepth = std::hypot(aRect.GetWidth(), aRect.GetHeight()) / 6.0;
850 if(!bExtrude)
852 // Create transformation for the polygons rotating body
853 if (rPnt1 != rPnt2)
855 // Rotation around control point #1 with set angle
856 // for 3D coordinates
857 basegfx::B2DPoint aDiff(rPnt1 - rPnt2);
858 fRot3D = atan2(aDiff.getY(), aDiff.getX()) - M_PI_2;
860 if(basegfx::fTools::equalZero(fabs(fRot3D)))
861 fRot3D = 0.0;
863 if(fRot3D != 0.0)
865 aLatheMat = basegfx::utils::createRotateAroundPoint(rPnt2, -fRot3D)
866 * aLatheMat;
870 if (rPnt2.getX() != 0.0)
872 // Translation to Y=0 - axis
873 aLatheMat.translate(-rPnt2.getX(), 0.0);
875 else
877 aLatheMat.translate(static_cast<double>(-aRect.Left()), 0.0);
880 // Form the inverse matrix to determine the target expansion
881 basegfx::B2DHomMatrix aInvLatheMat(aLatheMat);
882 aInvLatheMat.invert();
884 // SnapRect extension enables mirroring in the axis of rotation
885 for(size_t a=0; a<rMarkList.GetMarkCount(); ++a)
887 SdrMark* pMark = rMarkList.GetMark(a);
888 SdrObject* pObj = pMark->GetMarkedSdrObj();
889 tools::Rectangle aTurnRect = pObj->GetSnapRect();
890 basegfx::B2DPoint aRot;
891 Point aRotPnt;
893 aRot = basegfx::B2DPoint(aTurnRect.Left(), -aTurnRect.Top());
894 aRot *= aLatheMat;
895 aRot.setX(-aRot.getX());
896 aRot *= aInvLatheMat;
897 aRotPnt = Point(static_cast<tools::Long>(aRot.getX() + 0.5), static_cast<tools::Long>(-aRot.getY() - 0.5));
898 aRect.Union(tools::Rectangle(aRotPnt, aRotPnt));
900 aRot = basegfx::B2DPoint(aTurnRect.Left(), -aTurnRect.Bottom());
901 aRot *= aLatheMat;
902 aRot.setX(-aRot.getX());
903 aRot *= aInvLatheMat;
904 aRotPnt = Point(static_cast<tools::Long>(aRot.getX() + 0.5), static_cast<tools::Long>(-aRot.getY() - 0.5));
905 aRect.Union(tools::Rectangle(aRotPnt, aRotPnt));
907 aRot = basegfx::B2DPoint(aTurnRect.Right(), -aTurnRect.Top());
908 aRot *= aLatheMat;
909 aRot.setX(-aRot.getX());
910 aRot *= aInvLatheMat;
911 aRotPnt = Point(static_cast<tools::Long>(aRot.getX() + 0.5), static_cast<tools::Long>(-aRot.getY() - 0.5));
912 aRect.Union(tools::Rectangle(aRotPnt, aRotPnt));
914 aRot = basegfx::B2DPoint(aTurnRect.Right(), -aTurnRect.Bottom());
915 aRot *= aLatheMat;
916 aRot.setX(-aRot.getX());
917 aRot *= aInvLatheMat;
918 aRotPnt = Point(static_cast<tools::Long>(aRot.getX() + 0.5), static_cast<tools::Long>(-aRot.getY() - 0.5));
919 aRect.Union(tools::Rectangle(aRotPnt, aRotPnt));
923 // Walk through the selection and convert it into 3D, complete with
924 // Conversion to SdrPathObject, also fonts
925 for(size_t a=0; a<rMarkList.GetMarkCount(); ++a)
927 SdrMark* pMark = rMarkList.GetMark(a);
928 SdrObject* pObj = pMark->GetMarkedSdrObj();
930 ImpCreate3DObject(pScene.get(), pObj, bExtrude, fDepth, aLatheMat);
933 if(pScene->GetSubList() && pScene->GetSubList()->GetObjCount() != 0)
935 // Arrange all created objects by depth
936 if(bExtrude)
937 DoDepthArrange(pScene.get(), fDepth);
939 // Center 3D objects in the middle of the overall rectangle
940 basegfx::B3DPoint aCenter(pScene->GetBoundVolume().getCenter());
941 basegfx::B3DHomMatrix aMatrix;
943 aMatrix.translate(-aCenter.getX(), -aCenter.getY(), -aCenter.getZ());
944 pScene->SetTransform(aMatrix * pScene->GetTransform());
946 // Initialize scene
947 pScene->NbcSetSnapRect(aRect);
948 basegfx::B3DRange aBoundVol = pScene->GetBoundVolume();
949 InitScene(pScene.get(), static_cast<double>(aRect.GetWidth()), static_cast<double>(aRect.GetHeight()), aBoundVol.getDepth());
951 // Insert scene instead of the first selected object and throw away
952 // all the old objects
953 SdrMark* pMark = rMarkList.GetMark(0);
954 if (pMark)
956 SdrObject* pRepObj = pMark->GetMarkedSdrObj();
957 SdrPageView* pPV = pMark->GetPageView();
958 MarkObj(pRepObj, pPV, true);
959 ReplaceObjectAtView(pRepObj, *pPV, pScene.get(), false);
960 DeleteMarked();
961 MarkObj(pScene.get(), pPV);
964 // Rotate Rotation body around the axis of rotation
965 if(!bExtrude && fRot3D != 0.0)
967 basegfx::B3DHomMatrix aRotate;
968 aRotate.rotate(0.0, 0.0, fRot3D);
969 pScene->SetTransform(aRotate * pScene->GetTransform());
972 // Set default rotation
974 basegfx::B3DHomMatrix aRotate;
975 aRotate.rotate(basegfx::deg2rad(20.0), 0.0, 0.0);
976 // E3DModifySceneSnapRectUpdater updates the 2D representation of the scene.
977 // It prepares things in ctor and acts in dtor.
978 E3DModifySceneSnapRectUpdater aUpdater(pScene->getSdrObjectFromSdrObjList());
979 pScene->SetTransform(aRotate * pScene->GetTransform());
982 else
983 pScene.clear();
985 EndUndo();
988 //Arrange all created extrude objects by depth
990 namespace {
992 struct E3dDepthNeighbour
994 E3dExtrudeObj* mpObj;
995 basegfx::B2DPolyPolygon maPreparedPolyPolygon;
997 E3dDepthNeighbour(E3dExtrudeObj* pObj, basegfx::B2DPolyPolygon aPreparedPolyPolygon)
998 : mpObj(pObj),
999 maPreparedPolyPolygon(std::move(aPreparedPolyPolygon))
1004 struct E3dDepthLayer
1006 E3dDepthLayer* mpDown;
1007 std::vector<E3dDepthNeighbour> mvNeighbours;
1009 E3dDepthLayer()
1010 : mpDown(nullptr)
1017 void E3dView::DoDepthArrange(E3dScene const * pScene, double fDepth)
1019 if(!(pScene && pScene->GetSubList() && pScene->GetSubList()->GetObjCount() > 1))
1020 return;
1022 SdrObjList* pSubList = pScene->GetSubList();
1023 SdrObjListIter aIter(pSubList, SdrIterMode::Flat);
1024 E3dDepthLayer* pBaseLayer = nullptr;
1025 E3dDepthLayer* pLayer = nullptr;
1026 sal_Int32 nNumLayers = 0;
1028 while(aIter.IsMore())
1030 E3dExtrudeObj* pExtrudeObj = dynamic_cast< E3dExtrudeObj* >(aIter.Next());
1032 if(pExtrudeObj)
1034 const basegfx::B2DPolyPolygon aExtrudePoly(
1035 basegfx::utils::prepareForPolygonOperation(pExtrudeObj->GetExtrudePolygon()));
1036 const SfxItemSet& rLocalSet = pExtrudeObj->GetMergedItemSet();
1037 const drawing::FillStyle eLocalFillStyle = rLocalSet.Get(XATTR_FILLSTYLE).GetValue();
1038 const Color aLocalColor = rLocalSet.Get(XATTR_FILLCOLOR).GetColorValue();
1040 // sort in ExtrudeObj
1041 if(pLayer)
1043 // do we have overlap with an object of this layer?
1044 bool bOverlap(false);
1046 for(const auto& rAct : pLayer->mvNeighbours)
1048 // do rAct.mpObj and pExtrudeObj overlap? Check by
1049 // using logical AND clipping
1050 const basegfx::B2DPolyPolygon aAndPolyPolygon(
1051 basegfx::utils::solvePolygonOperationAnd(
1052 aExtrudePoly,
1053 rAct.maPreparedPolyPolygon));
1055 if(aAndPolyPolygon.count() != 0)
1057 // second criteria: is another fillstyle or color used?
1058 const SfxItemSet& rCompareSet = rAct.mpObj->GetMergedItemSet();
1060 drawing::FillStyle eCompareFillStyle = rCompareSet.Get(XATTR_FILLSTYLE).GetValue();
1062 if(eLocalFillStyle == eCompareFillStyle)
1064 if(eLocalFillStyle == drawing::FillStyle_SOLID)
1066 Color aCompareColor = rCompareSet.Get(XATTR_FILLCOLOR).GetColorValue();
1068 if(aCompareColor == aLocalColor)
1070 continue;
1073 else if(eLocalFillStyle == drawing::FillStyle_NONE)
1075 continue;
1079 bOverlap = true;
1080 break;
1084 if(bOverlap)
1086 // yes, start a new layer
1087 pLayer->mpDown = new E3dDepthLayer;
1088 pLayer = pLayer->mpDown;
1089 nNumLayers++;
1090 pLayer->mvNeighbours.emplace_back(pExtrudeObj, aExtrudePoly);
1092 else
1094 // no, add to current layer
1095 pLayer->mvNeighbours.emplace(pLayer->mvNeighbours.begin(), pExtrudeObj, aExtrudePoly);
1098 else
1100 // first layer ever
1101 pBaseLayer = new E3dDepthLayer;
1102 pLayer = pBaseLayer;
1103 nNumLayers++;
1104 pLayer->mvNeighbours.emplace_back(pExtrudeObj, aExtrudePoly);
1109 // number of layers is done
1110 if(nNumLayers > 1)
1112 // need to be arranged
1113 double fMinDepth = fDepth * 0.8;
1114 double fStep = (fDepth - fMinDepth) / static_cast<double>(nNumLayers);
1115 pLayer = pBaseLayer;
1117 while(pLayer)
1119 // move along layer
1120 for(auto& rAct : pLayer->mvNeighbours)
1122 // adapt extrude value
1123 rAct.mpObj->SetMergedItem(SfxUInt32Item(SDRATTR_3DOBJ_DEPTH, sal_uInt32(fMinDepth + 0.5)));
1126 // next layer
1127 pLayer = pLayer->mpDown;
1128 fMinDepth += fStep;
1132 // cleanup
1133 while(pBaseLayer)
1135 pLayer = pBaseLayer->mpDown;
1136 delete pBaseLayer;
1137 pBaseLayer = pLayer;
1141 // Start drag, create for 3D objects before possibly drag method
1143 bool E3dView::BegDragObj(const Point& rPnt, OutputDevice* pOut,
1144 SdrHdl* pHdl, short nMinMov,
1145 SdrDragMethod* pForcedMeth)
1147 const SdrMarkList& rMarkList = GetMarkedObjectList();
1148 if(Is3DRotationCreationActive() && rMarkList.GetMarkCount())
1150 // Determine all selected polygons and return the mirrored helper overlay
1151 mpMirrorOverlay->SetMirrorAxis(maRef1, maRef2);
1153 else
1155 bool bOwnActionNecessary;
1156 if (pHdl == nullptr)
1158 bOwnActionNecessary = true;
1160 else if (pHdl->IsVertexHdl() || pHdl->IsCornerHdl())
1162 bOwnActionNecessary = true;
1164 else
1166 bOwnActionNecessary = false;
1169 if(bOwnActionNecessary && rMarkList.GetMarkCount() > 0)
1171 E3dDragConstraint eConstraint = E3dDragConstraint::XYZ;
1172 bool bThereAreRootScenes = false;
1173 bool bThereAre3DObjects = false;
1174 const size_t nCnt = rMarkList.GetMarkCount();
1175 for(size_t nObjs = 0; nObjs < nCnt; ++nObjs)
1177 SdrObject *pObj = rMarkList.GetMark(nObjs)->GetMarkedSdrObj();
1178 if(pObj)
1180 if( const E3dScene* pScene = DynCastE3dScene(pObj) )
1181 if( pScene->getRootE3dSceneFromE3dObject() == pObj )
1182 bThereAreRootScenes = true;
1184 if(DynCastE3dObject(pObj))
1186 bThereAre3DObjects = true;
1190 if( bThereAre3DObjects )
1192 meDragHdl = ( pHdl == nullptr ? SdrHdlKind::Move : pHdl->GetKind() );
1193 switch ( meDragMode )
1195 case SdrDragMode::Rotate:
1196 case SdrDragMode::Shear:
1198 switch ( meDragHdl )
1200 case SdrHdlKind::Left:
1201 case SdrHdlKind::Right:
1203 eConstraint = E3dDragConstraint::X;
1205 break;
1207 case SdrHdlKind::Upper:
1208 case SdrHdlKind::Lower:
1210 eConstraint = E3dDragConstraint::Y;
1212 break;
1214 case SdrHdlKind::UpperLeft:
1215 case SdrHdlKind::UpperRight:
1216 case SdrHdlKind::LowerLeft:
1217 case SdrHdlKind::LowerRight:
1219 eConstraint = E3dDragConstraint::Z;
1221 break;
1222 default: break;
1225 // do not mask the allowed rotations
1226 eConstraint &= E3dDragConstraint::XYZ;
1227 pForcedMeth = new E3dDragRotate(*this, rMarkList, eConstraint, IsSolidDragging());
1229 break;
1231 case SdrDragMode::Move:
1233 if(!bThereAreRootScenes)
1235 pForcedMeth = new E3dDragMove(*this, rMarkList, meDragHdl, eConstraint, IsSolidDragging());
1238 break;
1240 // later on
1241 case SdrDragMode::Mirror:
1242 case SdrDragMode::Crook:
1243 case SdrDragMode::Transparence:
1244 case SdrDragMode::Gradient:
1245 default:
1248 break;
1253 return SdrView::BegDragObj(rPnt, pOut, pHdl, nMinMov, pForcedMeth);
1256 // Set current 3D drawing object, create the scene for this
1257 rtl::Reference<E3dScene> E3dView::SetCurrent3DObj(E3dObject* p3DObj)
1259 assert(p3DObj != nullptr && "Who puts in a NULL-pointer here");
1261 // get transformed BoundVolume of the object
1262 basegfx::B3DRange aVolume(p3DObj->GetBoundVolume());
1263 aVolume.transform(p3DObj->GetTransform());
1264 double fW(aVolume.getWidth());
1265 double fH(aVolume.getHeight());
1267 tools::Rectangle aRect(0,0, static_cast<tools::Long>(fW), static_cast<tools::Long>(fH));
1269 rtl::Reference<E3dScene> pScene = new E3dScene(p3DObj->getSdrModelFromSdrObject());
1271 InitScene(pScene.get(), fW, fH, aVolume.getMaxZ() + ((fW + fH) / 4.0));
1273 pScene->InsertObject(p3DObj);
1274 pScene->NbcSetSnapRect(aRect);
1276 return pScene;
1279 void E3dView::InitScene(E3dScene* pScene, double fW, double fH, double fCamZ)
1281 Camera3D aCam(pScene->GetCamera());
1283 aCam.SetAutoAdjustProjection(false);
1284 aCam.SetViewWindow(- fW / 2, - fH / 2, fW, fH);
1285 basegfx::B3DPoint aLookAt;
1287 double fDefaultCamPosZ = GetDefaultCamPosZ();
1288 basegfx::B3DPoint aCamPos(0.0, 0.0, fCamZ < fDefaultCamPosZ ? fDefaultCamPosZ : fCamZ);
1290 aCam.SetPosAndLookAt(aCamPos, aLookAt);
1291 aCam.SetFocalLength(GetDefaultCamFocal());
1292 pScene->SetCamera(aCam);
1295 void E3dView::Start3DCreation()
1297 const SdrMarkList& rMarkList = GetMarkedObjectList();
1298 if (!rMarkList.GetMarkCount())
1299 return;
1301 //positioned
1302 tools::Long nOutMin = 0;
1303 tools::Long nOutMax = 0;
1304 tools::Long nMinLen = 0;
1305 tools::Long nObjDst = 0;
1306 tools::Long nOutHgt = 0;
1307 OutputDevice* pOut = GetFirstOutputDevice();
1309 // first determine representation boundaries
1310 if (pOut != nullptr)
1312 nMinLen = pOut->PixelToLogic(Size(0,50)).Height();
1313 nObjDst = pOut->PixelToLogic(Size(0,20)).Height();
1315 tools::Long nDst = pOut->PixelToLogic(Size(0,10)).Height();
1317 nOutMin = -pOut->GetMapMode().GetOrigin().Y();
1318 nOutMax = pOut->GetOutputSize().Height() - 1 + nOutMin;
1319 nOutMin += nDst;
1320 nOutMax -= nDst;
1322 if (nOutMax - nOutMin < nDst)
1324 nOutMin += nOutMax + 1;
1325 nOutMin /= 2;
1326 nOutMin -= (nDst + 1) / 2;
1327 nOutMax = nOutMin + nDst;
1330 nOutHgt = nOutMax - nOutMin;
1332 tools::Long nTemp = nOutHgt / 4;
1333 if (nTemp > nMinLen) nMinLen = nTemp;
1336 // and then attach the marks at the top and bottom of the object
1337 basegfx::B2DRange aR;
1338 for(size_t nMark = 0; nMark < rMarkList.GetMarkCount(); ++nMark)
1340 SdrObject* pMark = rMarkList.GetMark(nMark)->GetMarkedSdrObj();
1341 basegfx::B2DPolyPolygon aXPP(pMark->TakeXorPoly());
1342 aR.expand(basegfx::utils::getRange(aXPP));
1345 basegfx::B2DPoint aCenter(aR.getCenter());
1346 tools::Long nMarkHgt = basegfx::fround<tools::Long>(aR.getHeight()) - 1;
1347 tools::Long nHgt = nMarkHgt + nObjDst * 2;
1349 if (nHgt < nMinLen) nHgt = nMinLen;
1351 tools::Long nY1 = basegfx::fround<tools::Long>(aCenter.getY()) - (nHgt + 1) / 2;
1352 tools::Long nY2 = nY1 + nHgt;
1354 if (pOut && (nMinLen > nOutHgt)) nMinLen = nOutHgt;
1355 if (pOut)
1357 if (nY1 < nOutMin)
1359 nY1 = nOutMin;
1360 if (nY2 < nY1 + nMinLen) nY2 = nY1 + nMinLen;
1362 if (nY2 > nOutMax)
1364 nY2 = nOutMax;
1365 if (nY1 > nY2 - nMinLen) nY1 = nY2 - nMinLen;
1369 maRef1.setX( basegfx::fround<tools::Long>(aR.getMinX()) ); // Initial move axis 2/100mm to the left
1370 maRef1.setY( nY1 );
1371 maRef2.setX( maRef1.X() );
1372 maRef2.setY( nY2 );
1374 // Turn on marks
1375 SetMarkHandles(nullptr);
1377 //HMHif (bVis) ShowMarkHdl();
1378 if (rMarkList.GetMarkCount() != 0) MarkListHasChanged();
1380 // Show mirror polygon IMMEDIATELY
1381 const SdrHdlList &aHdlList = GetHdlList();
1382 mpMirrorOverlay.reset(new Impl3DMirrorConstructOverlay(*this));
1383 mpMirrorOverlay->SetMirrorAxis(aHdlList.GetHdl(SdrHdlKind::Ref1)->GetPos(), aHdlList.GetHdl(SdrHdlKind::Ref2)->GetPos());
1386 // what happens with a mouse movement when the object is created?
1388 void E3dView::MovAction(const Point& rPnt)
1390 if(Is3DRotationCreationActive())
1392 SdrHdl* pHdl = GetDragHdl();
1394 if (pHdl)
1396 SdrHdlKind eHdlKind = pHdl->GetKind();
1398 // reacts only due to a mirror axis
1399 if ((eHdlKind == SdrHdlKind::Ref1) ||
1400 (eHdlKind == SdrHdlKind::Ref2) ||
1401 (eHdlKind == SdrHdlKind::MirrorAxis))
1403 const SdrHdlList &aHdlList = GetHdlList ();
1405 // delete the mirrored polygon, mirrors the original and draws
1406 // it anew
1407 SdrView::MovAction (rPnt);
1408 mpMirrorOverlay->SetMirrorAxis(
1409 aHdlList.GetHdl (SdrHdlKind::Ref1)->GetPos(),
1410 aHdlList.GetHdl (SdrHdlKind::Ref2)->GetPos());
1413 else
1415 SdrView::MovAction (rPnt);
1418 else
1420 SdrView::MovAction (rPnt);
1424 // The End. Create object and any child objects through ImpCreate3DLathe.
1425 // With the parameter value sal_True (SDefault: sal_False) is simply a
1426 // rotation body created, without letting the user set the position of the
1427 // axis. It is sufficient with this call, if an object is selected.
1428 // (No initialization necessary)
1430 void E3dView::End3DCreation(bool bUseDefaultValuesForMirrorAxes)
1432 ResetCreationActive();
1434 const SdrMarkList& rMarkList = GetMarkedObjectList();
1435 if(rMarkList.GetMarkCount() == 0)
1436 return;
1438 if(bUseDefaultValuesForMirrorAxes)
1440 tools::Rectangle aRect = GetAllMarkedRect();
1441 if(aRect.GetWidth() <= 1)
1442 aRect.SetSize(Size(500, aRect.GetHeight()));
1443 if(aRect.GetHeight() <= 1)
1444 aRect.SetSize(Size(aRect.GetWidth(), 500));
1446 basegfx::B2DPoint aPnt1(aRect.Left(), -aRect.Top());
1447 basegfx::B2DPoint aPnt2(aRect.Left(), -aRect.Bottom());
1449 ConvertMarkedObjTo3D(false, aPnt1, aPnt2);
1451 else
1453 // Turn off helper overlay
1454 // Determine from the handle positions and the displacement of
1455 // the points
1456 const SdrHdlList &aHdlList = GetHdlList();
1457 Point aMirrorRef1 = aHdlList.GetHdl(SdrHdlKind::Ref1)->GetPos();
1458 Point aMirrorRef2 = aHdlList.GetHdl(SdrHdlKind::Ref2)->GetPos();
1460 basegfx::B2DPoint aPnt1(aMirrorRef1.X(), -aMirrorRef1.Y());
1461 basegfx::B2DPoint aPnt2(aMirrorRef2.X(), -aMirrorRef2.Y());
1463 ConvertMarkedObjTo3D(false, aPnt1, aPnt2);
1467 E3dView::~E3dView ()
1471 void E3dView::ResetCreationActive ()
1473 mpMirrorOverlay.reset();
1476 void E3dView::InitView ()
1478 mpMirrorOverlay = nullptr;
1481 bool E3dView::IsBreak3DObjPossible() const
1483 const SdrMarkList& rMarkList = GetMarkedObjectList();
1484 const size_t nCount = rMarkList.GetMarkCount();
1486 if (nCount > 0)
1488 for (size_t i = 0; i < nCount; ++i)
1490 SdrObject* pObj = rMarkList.GetMark(i)->GetMarkedSdrObj();
1492 if (auto p3dObject = DynCastE3dObject(pObj))
1494 if(!p3dObject->IsBreakObjPossible())
1495 return false;
1497 else
1499 return false;
1503 else
1505 return false;
1508 return true;
1511 void E3dView::Break3DObj()
1513 if(!IsBreak3DObjPossible())
1514 return;
1516 const SdrMarkList& rMarkList = GetMarkedObjectList();
1517 // ALL selected objects are changed
1518 const size_t nCount = rMarkList.GetMarkCount();
1520 BegUndo(SvxResId(RID_SVX_3D_UNDO_BREAK_LATHE));
1521 for(size_t a=0; a<nCount; ++a)
1523 E3dObject* pObj = static_cast<E3dObject*>(rMarkList.GetMark(a)->GetMarkedSdrObj());
1524 BreakSingle3DObj(pObj);
1526 DeleteMarked();
1527 EndUndo();
1530 void E3dView::BreakSingle3DObj(E3dObject* pObj)
1532 if(DynCastE3dScene(pObj))
1534 SdrObjList* pSubList = pObj->GetSubList();
1535 SdrObjListIter aIter(pSubList, SdrIterMode::Flat);
1537 while(aIter.IsMore())
1539 E3dObject* pSubObj = static_cast<E3dObject*>(aIter.Next());
1540 BreakSingle3DObj(pSubObj);
1543 else
1545 rtl::Reference<SdrAttrObj> pNewObj = pObj->GetBreakObj();
1546 if (pNewObj)
1548 if (InsertObjectAtView(pNewObj.get(), *GetSdrPageView(), SdrInsertFlags::DONTMARK))
1550 pNewObj->SetChanged();
1551 pNewObj->BroadcastObjectChange();
1557 void E3dView::CheckPossibilities()
1559 // call parent
1560 SdrView::CheckPossibilities();
1562 // Set other flags
1563 if(!(m_bGroupPossible || m_bUnGroupPossible || m_bGrpEnterPossible))
1564 return;
1566 const SdrMarkList& rMarkList = GetMarkedObjectList();
1567 const size_t nMarkCnt = rMarkList.GetMarkCount();
1568 bool bCompound = false;
1569 bool b3DObject = false;
1570 for(size_t nObjs = 0; (nObjs < nMarkCnt) && !bCompound; ++nObjs)
1572 SdrObject *pObj = rMarkList.GetMark(nObjs)->GetMarkedSdrObj();
1573 if(dynamic_cast< const E3dCompoundObject* >(pObj))
1574 bCompound = true;
1575 if(DynCastE3dObject(pObj))
1576 b3DObject = true;
1579 // So far: there are two or more of any objects selected. See if
1580 // compound objects are involved. If yes, ban grouping.
1581 if(m_bGroupPossible && bCompound)
1582 m_bGroupPossible = false;
1584 if(m_bUnGroupPossible && b3DObject)
1585 m_bUnGroupPossible = false;
1587 if(m_bGrpEnterPossible && bCompound)
1588 m_bGrpEnterPossible = false;
1591 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */