Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / svx / source / svdraw / svdocirc.cxx
blobab8bc58ddf855a32bf9a1084fc74e5195027ceb5
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 .
20 #include <basegfx/matrix/b2dhommatrix.hxx>
21 #include <basegfx/matrix/b2dhommatrixtools.hxx>
22 #include <basegfx/point/b2dpoint.hxx>
23 #include <basegfx/polygon/b2dpolygon.hxx>
24 #include <basegfx/polygon/b2dpolygontools.hxx>
25 #include <math.h>
26 #include <rtl/ustrbuf.hxx>
28 #include <svx/dialmgr.hxx>
29 #include <svx/strings.hrc>
31 #include <sdr/contact/viewcontactofsdrcircobj.hxx>
32 #include <sdr/properties/circleproperties.hxx>
33 #include <svx/svddrag.hxx>
34 #include <svx/svdmodel.hxx>
35 #include <svx/svdocirc.hxx>
36 #include <svx/svdopath.hxx>
37 #include <svx/svdtrans.hxx>
38 #include <svx/svdview.hxx>
39 #include <svx/sxciaitm.hxx>
40 #include <sxcikitm.hxx>
41 #include <svx/xfillit0.hxx>
42 #include <svx/xlineit0.hxx>
43 #include <svx/xlnedit.hxx>
44 #include <svx/xlnedwit.hxx>
45 #include <svx/xlnstit.hxx>
46 #include <svx/xlnstwit.hxx>
47 #include <svx/xlnwtit.hxx>
48 #include <vcl/canvastools.hxx>
49 #include <vcl/ptrstyle.hxx>
51 using namespace com::sun::star;
53 static Point GetAnglePnt(const tools::Rectangle& rR, Degree100 nAngle)
55 Point aCenter(rR.Center());
56 tools::Long nWdt=rR.Right()-rR.Left();
57 tools::Long nHgt=rR.Bottom()-rR.Top();
58 tools::Long nMaxRad=(std::max(nWdt,nHgt)+1) /2;
59 double a = toRadians(nAngle);
60 Point aRetval(FRound(cos(a)*nMaxRad),-FRound(sin(a)*nMaxRad));
61 if (nWdt==0) aRetval.setX(0 );
62 if (nHgt==0) aRetval.setY(0 );
63 if (nWdt!=nHgt) {
64 if (nWdt>nHgt) {
65 if (nWdt!=0) {
66 // stop possible overruns for very large objects
67 if (std::abs(nHgt)>32767 || std::abs(aRetval.Y())>32767) {
68 aRetval.setY(BigMulDiv(aRetval.Y(),nHgt,nWdt) );
69 } else {
70 aRetval.setY(aRetval.Y()*nHgt/nWdt );
73 } else {
74 if (nHgt!=0) {
75 // stop possible overruns for very large objects
76 if (std::abs(nWdt)>32767 || std::abs(aRetval.X())>32767) {
77 aRetval.setX(BigMulDiv(aRetval.X(),nWdt,nHgt) );
78 } else {
79 aRetval.setX(aRetval.X()*nWdt/nHgt );
84 aRetval+=aCenter;
85 return aRetval;
89 // BaseProperties section
91 std::unique_ptr<sdr::properties::BaseProperties> SdrCircObj::CreateObjectSpecificProperties()
93 return std::make_unique<sdr::properties::CircleProperties>(*this);
97 // DrawContact section
99 std::unique_ptr<sdr::contact::ViewContact> SdrCircObj::CreateObjectSpecificViewContact()
101 return std::make_unique<sdr::contact::ViewContactOfSdrCircObj>(*this);
104 SdrCircKind ToSdrCircKind(SdrObjKind eKind)
106 switch (eKind)
108 case SdrObjKind::CircleOrEllipse: return SdrCircKind::Full;
109 case SdrObjKind::CircleSection: return SdrCircKind::Section;
110 case SdrObjKind::CircleArc: return SdrCircKind::Arc;
111 case SdrObjKind::CircleCut: return SdrCircKind::Cut;
112 default: assert(false);
114 return SdrCircKind::Full;
117 SdrCircObj::SdrCircObj(
118 SdrModel& rSdrModel,
119 SdrCircKind eNewKind)
120 : SdrRectObj(rSdrModel)
122 nStartAngle=0_deg100;
123 nEndAngle=36000_deg100;
124 meCircleKind=eNewKind;
125 m_bClosedObj=eNewKind!=SdrCircKind::Arc;
128 SdrCircObj::SdrCircObj(SdrModel& rSdrModel, SdrCircObj const & rSource)
129 : SdrRectObj(rSdrModel, rSource)
131 meCircleKind = rSource.meCircleKind;
132 nStartAngle = rSource.nStartAngle;
133 nEndAngle = rSource.nEndAngle;
134 m_bClosedObj = rSource.m_bClosedObj;
137 SdrCircObj::SdrCircObj(
138 SdrModel& rSdrModel,
139 SdrCircKind eNewKind,
140 const tools::Rectangle& rRect)
141 : SdrRectObj(rSdrModel, rRect)
143 nStartAngle=0_deg100;
144 nEndAngle=36000_deg100;
145 meCircleKind=eNewKind;
146 m_bClosedObj=eNewKind!=SdrCircKind::Arc;
149 SdrCircObj::SdrCircObj(
150 SdrModel& rSdrModel,
151 SdrCircKind eNewKind,
152 const tools::Rectangle& rRect,
153 Degree100 nNewStartAngle,
154 Degree100 nNewEndAngle)
155 : SdrRectObj(rSdrModel, rRect)
157 Degree100 nAngleDif=nNewEndAngle-nNewStartAngle;
158 nStartAngle=NormAngle36000(nNewStartAngle);
159 nEndAngle=NormAngle36000(nNewEndAngle);
160 if (nAngleDif==36000_deg100) nEndAngle+=nAngleDif; // full circle
161 meCircleKind=eNewKind;
162 m_bClosedObj=eNewKind!=SdrCircKind::Arc;
165 SdrCircObj::~SdrCircObj()
169 void SdrCircObj::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
171 bool bCanConv=!HasText() || ImpCanConvTextToCurve();
172 rInfo.bEdgeRadiusAllowed = false;
173 rInfo.bCanConvToPath=bCanConv;
174 rInfo.bCanConvToPoly=bCanConv;
175 rInfo.bCanConvToContour = !IsFontwork() && (rInfo.bCanConvToPoly || LineGeometryUsageIsNecessary());
178 SdrObjKind SdrCircObj::GetObjIdentifier() const
180 switch (meCircleKind)
182 case SdrCircKind::Full: return SdrObjKind::CircleOrEllipse;
183 case SdrCircKind::Section: return SdrObjKind::CircleSection;
184 case SdrCircKind::Cut: return SdrObjKind::CircleCut;
185 case SdrCircKind::Arc: return SdrObjKind::CircleArc;
186 default: assert(false);
188 return SdrObjKind::CircleOrEllipse;
191 bool SdrCircObj::PaintNeedsXPolyCirc() const
193 // XPoly is necessary for all rotated ellipse objects, circle and
194 // ellipse segments.
195 // If not WIN, then (for now) also for circle/ellipse segments and circle/
196 // ellipse arcs (for precision)
197 bool bNeed = maGeo.nRotationAngle || maGeo.nShearAngle || meCircleKind == SdrCircKind::Cut;
198 // If not WIN, then for everything except full circle (for now!)
199 if (meCircleKind!=SdrCircKind::Full) bNeed = true;
201 const SfxItemSet& rSet = GetObjectItemSet();
202 if(!bNeed)
204 // XPoly is necessary for everything that isn't LineSolid or LineNone
205 drawing::LineStyle eLine = rSet.Get(XATTR_LINESTYLE).GetValue();
206 bNeed = eLine != drawing::LineStyle_NONE && eLine != drawing::LineStyle_SOLID;
208 // XPoly is necessary for thick lines
209 if(!bNeed && eLine != drawing::LineStyle_NONE)
210 bNeed = rSet.Get(XATTR_LINEWIDTH).GetValue() != 0;
212 // XPoly is necessary for circle arcs with line ends
213 if(!bNeed && meCircleKind == SdrCircKind::Arc)
215 // start of the line is here if StartPolygon, StartWidth!=0
216 bNeed=rSet.Get(XATTR_LINESTART).GetLineStartValue().count() != 0 &&
217 rSet.Get(XATTR_LINESTARTWIDTH).GetValue() != 0;
219 if(!bNeed)
221 // end of the line is here if EndPolygon, EndWidth!=0
222 bNeed = rSet.Get(XATTR_LINEEND).GetLineEndValue().count() != 0 &&
223 rSet.Get(XATTR_LINEENDWIDTH).GetValue() != 0;
228 // XPoly is necessary if Fill !=None and !=Solid
229 if(!bNeed && meCircleKind != SdrCircKind::Arc)
231 drawing::FillStyle eFill=rSet.Get(XATTR_FILLSTYLE).GetValue();
232 bNeed = eFill != drawing::FillStyle_NONE && eFill != drawing::FillStyle_SOLID;
235 if(!bNeed && meCircleKind != SdrCircKind::Full && nStartAngle == nEndAngle)
236 bNeed = true; // otherwise we're drawing a full circle
238 return bNeed;
241 basegfx::B2DPolygon SdrCircObj::ImpCalcXPolyCirc(const SdrCircKind eCircleKind, const tools::Rectangle& rRect1, Degree100 nStart, Degree100 nEnd) const
243 const basegfx::B2DRange aRange = vcl::unotools::b2DRectangleFromRectangle(rRect1);
244 basegfx::B2DPolygon aCircPolygon;
246 if(SdrCircKind::Full == eCircleKind)
248 // create full circle. Do not use createPolygonFromEllipse; it's necessary
249 // to get the start point to the bottom of the circle to keep compatible to
250 // old geometry creation
251 aCircPolygon = basegfx::utils::createPolygonFromUnitCircle(1);
253 // needs own scaling and translation from unit circle to target size (same as
254 // would be in createPolygonFromEllipse)
255 const basegfx::B2DPoint aCenter(aRange.getCenter());
256 const basegfx::B2DHomMatrix aMatrix(basegfx::utils::createScaleTranslateB2DHomMatrix(
257 aRange.getWidth() / 2.0, aRange.getHeight() / 2.0,
258 aCenter.getX(), aCenter.getY()));
259 aCircPolygon.transform(aMatrix);
261 else
263 // mirror start, end for geometry creation since model coordinate system is mirrored in Y
264 const double fStart(toRadians((36000_deg100 - nEnd) % 36000_deg100));
265 const double fEnd(toRadians((36000_deg100 - nStart) % 36000_deg100));
267 // create circle segment. This is not closed by default
268 aCircPolygon = basegfx::utils::createPolygonFromEllipseSegment(
269 aRange.getCenter(), aRange.getWidth() / 2.0, aRange.getHeight() / 2.0,
270 fStart, fEnd);
272 // check closing states
273 const bool bCloseSegment(SdrCircKind::Arc != eCircleKind);
274 const bool bCloseUsingCenter(SdrCircKind::Section == eCircleKind);
276 if(bCloseSegment)
278 if(bCloseUsingCenter)
280 // add center point at start (for historical reasons)
281 basegfx::B2DPolygon aSector;
282 aSector.append(aRange.getCenter());
283 aSector.append(aCircPolygon);
284 aCircPolygon = aSector;
287 // close
288 aCircPolygon.setClosed(true);
292 // #i76950#
293 if (maGeo.nShearAngle || maGeo.nRotationAngle)
295 // translate top left to (0,0)
296 const basegfx::B2DPoint aTopLeft(aRange.getMinimum());
297 basegfx::B2DHomMatrix aMatrix(basegfx::utils::createTranslateB2DHomMatrix(
298 -aTopLeft.getX(), -aTopLeft.getY()));
300 // shear, rotate and back to top left (if needed)
301 aMatrix = basegfx::utils::createShearXRotateTranslateB2DHomMatrix(
302 -maGeo.mfTanShearAngle,
303 maGeo.nRotationAngle ? toRadians(36000_deg100 - maGeo.nRotationAngle) : 0.0,
304 aTopLeft) * aMatrix;
306 // apply transformation
307 aCircPolygon.transform(aMatrix);
310 return aCircPolygon;
313 void SdrCircObj::RecalcXPoly()
315 basegfx::B2DPolygon aPolyCirc(ImpCalcXPolyCirc(meCircleKind, getRectangle(), nStartAngle, nEndAngle));
316 mpXPoly = XPolygon(aPolyCirc);
319 OUString SdrCircObj::TakeObjNameSingul() const
321 TranslateId pID=STR_ObjNameSingulCIRC;
322 if (getRectangle().GetWidth() == getRectangle().GetHeight() && maGeo.nShearAngle == 0_deg100)
324 switch (meCircleKind) {
325 case SdrCircKind::Full: pID=STR_ObjNameSingulCIRC; break;
326 case SdrCircKind::Section: pID=STR_ObjNameSingulSECT; break;
327 case SdrCircKind::Arc: pID=STR_ObjNameSingulCARC; break;
328 case SdrCircKind::Cut: pID=STR_ObjNameSingulCCUT; break;
329 default: break;
331 } else {
332 switch (meCircleKind) {
333 case SdrCircKind::Full: pID=STR_ObjNameSingulCIRCE; break;
334 case SdrCircKind::Section: pID=STR_ObjNameSingulSECTE; break;
335 case SdrCircKind::Arc: pID=STR_ObjNameSingulCARCE; break;
336 case SdrCircKind::Cut: pID=STR_ObjNameSingulCCUTE; break;
337 default: break;
340 OUString sName(SvxResId(pID));
342 OUString aName(GetName());
343 if (!aName.isEmpty())
344 sName += " '" + aName + "'";
345 return sName;
348 OUString SdrCircObj::TakeObjNamePlural() const
350 TranslateId pID=STR_ObjNamePluralCIRC;
351 if (getRectangle().GetWidth() == getRectangle().GetHeight() && maGeo.nShearAngle == 0_deg100)
353 switch (meCircleKind) {
354 case SdrCircKind::Full: pID=STR_ObjNamePluralCIRC; break;
355 case SdrCircKind::Section: pID=STR_ObjNamePluralSECT; break;
356 case SdrCircKind::Arc: pID=STR_ObjNamePluralCARC; break;
357 case SdrCircKind::Cut: pID=STR_ObjNamePluralCCUT; break;
358 default: break;
360 } else {
361 switch (meCircleKind) {
362 case SdrCircKind::Full: pID=STR_ObjNamePluralCIRCE; break;
363 case SdrCircKind::Section: pID=STR_ObjNamePluralSECTE; break;
364 case SdrCircKind::Arc: pID=STR_ObjNamePluralCARCE; break;
365 case SdrCircKind::Cut: pID=STR_ObjNamePluralCCUTE; break;
366 default: break;
369 return SvxResId(pID);
372 rtl::Reference<SdrObject> SdrCircObj::CloneSdrObject(SdrModel& rTargetModel) const
374 return new SdrCircObj(rTargetModel, *this);
377 basegfx::B2DPolyPolygon SdrCircObj::TakeXorPoly() const
379 const basegfx::B2DPolygon aCircPolygon(ImpCalcXPolyCirc(meCircleKind, getRectangle(), nStartAngle, nEndAngle));
380 return basegfx::B2DPolyPolygon(aCircPolygon);
383 namespace {
385 struct ImpCircUser : public SdrDragStatUserData
387 tools::Rectangle aR;
388 Point aCenter;
389 Point aP1;
390 tools::Long nHgt;
391 tools::Long nWdt;
392 Degree100 nStart;
393 Degree100 nEnd;
395 public:
396 ImpCircUser()
397 : nHgt(0),
398 nWdt(0),
399 nStart(0),
400 nEnd(0)
402 void SetCreateParams(SdrDragStat const & rStat);
407 sal_uInt32 SdrCircObj::GetHdlCount() const
409 if(SdrCircKind::Full != meCircleKind)
411 return 10;
413 else
415 return 8;
419 void SdrCircObj::AddToHdlList(SdrHdlList& rHdlList) const
421 for (sal_uInt32 nHdlNum=(SdrCircKind::Full==meCircleKind)?2:0; nHdlNum<=9; ++nHdlNum)
423 Point aPnt;
424 SdrHdlKind eLocalKind(SdrHdlKind::Move);
425 sal_uInt32 nPNum(0);
426 tools::Rectangle aRectangle = getRectangle();
427 switch (nHdlNum)
429 case 0:
430 aPnt = GetAnglePnt(aRectangle, nStartAngle);
431 eLocalKind = SdrHdlKind::Circle;
432 nPNum = 1;
433 break;
434 case 1:
435 aPnt = GetAnglePnt(aRectangle, nEndAngle);
436 eLocalKind = SdrHdlKind::Circle;
437 nPNum = 2;
438 break;
439 case 2:
440 aPnt = aRectangle.TopLeft();
441 eLocalKind = SdrHdlKind::UpperLeft;
442 break;
443 case 3:
444 aPnt = aRectangle.TopCenter();
445 eLocalKind = SdrHdlKind::Upper;
446 break;
447 case 4:
448 aPnt = aRectangle.TopRight();
449 eLocalKind = SdrHdlKind::UpperRight;
450 break;
451 case 5:
452 aPnt = aRectangle.LeftCenter();
453 eLocalKind = SdrHdlKind::Left;
454 break;
455 case 6:
456 aPnt = aRectangle.RightCenter();
457 eLocalKind = SdrHdlKind::Right;
458 break;
459 case 7:
460 aPnt = aRectangle.BottomLeft();
461 eLocalKind = SdrHdlKind::LowerLeft;
462 break;
463 case 8:
464 aPnt = aRectangle.BottomCenter();
465 eLocalKind = SdrHdlKind::Lower;
466 break;
467 case 9:
468 aPnt = aRectangle.BottomRight();
469 eLocalKind = SdrHdlKind::LowerRight;
470 break;
473 if (maGeo.nShearAngle)
475 ShearPoint(aPnt, aRectangle.TopLeft(), maGeo.mfTanShearAngle);
478 if (maGeo.nRotationAngle)
480 RotatePoint(aPnt, aRectangle.TopLeft(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
483 std::unique_ptr<SdrHdl> pH(new SdrHdl(aPnt,eLocalKind));
484 pH->SetPointNum(nPNum);
485 pH->SetObj(const_cast<SdrCircObj*>(this));
486 pH->SetRotationAngle(maGeo.nRotationAngle);
487 rHdlList.AddHdl(std::move(pH));
492 bool SdrCircObj::hasSpecialDrag() const
494 return true;
497 bool SdrCircObj::beginSpecialDrag(SdrDragStat& rDrag) const
499 const bool bAngle(rDrag.GetHdl() && SdrHdlKind::Circle == rDrag.GetHdl()->GetKind());
501 if(bAngle)
503 if(1 == rDrag.GetHdl()->GetPointNum() || 2 == rDrag.GetHdl()->GetPointNum())
505 rDrag.SetNoSnap();
508 return true;
511 return SdrTextObj::beginSpecialDrag(rDrag);
514 bool SdrCircObj::applySpecialDrag(SdrDragStat& rDrag)
516 const bool bAngle(rDrag.GetHdl() && SdrHdlKind::Circle == rDrag.GetHdl()->GetKind());
518 if(bAngle)
520 Point aPt(rDrag.GetNow());
522 if (maGeo.nRotationAngle)
523 RotatePoint(aPt, getRectangle().TopLeft(), -maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
525 if (maGeo.nShearAngle)
526 ShearPoint(aPt, getRectangle().TopLeft(), -maGeo.mfTanShearAngle);
528 aPt -= getRectangle().Center();
530 tools::Long nWdt = getRectangle().Right() - getRectangle().Left();
531 tools::Long nHgt = getRectangle().Bottom() - getRectangle().Top();
533 if(nWdt>=nHgt)
535 aPt.setY(BigMulDiv(aPt.Y(),nWdt,nHgt) );
537 else
539 aPt.setX(BigMulDiv(aPt.X(),nHgt,nWdt) );
542 Degree100 nAngle=NormAngle36000(GetAngle(aPt));
544 if (rDrag.GetView() && rDrag.GetView()->IsAngleSnapEnabled())
546 Degree100 nSA=rDrag.GetView()->GetSnapAngle();
548 if (nSA)
550 nAngle+=nSA/2_deg100;
551 nAngle/=nSA;
552 nAngle*=nSA;
553 nAngle=NormAngle36000(nAngle);
557 if(1 == rDrag.GetHdl()->GetPointNum())
559 nStartAngle = nAngle;
561 else if(2 == rDrag.GetHdl()->GetPointNum())
563 nEndAngle = nAngle;
566 SetBoundAndSnapRectsDirty();
567 SetXPolyDirty();
568 ImpSetCircInfoToAttr();
569 SetChanged();
571 return true;
573 else
575 return SdrTextObj::applySpecialDrag(rDrag);
579 OUString SdrCircObj::getSpecialDragComment(const SdrDragStat& rDrag) const
581 const bool bCreateComment(rDrag.GetView() && this == rDrag.GetView()->GetCreateObj());
583 if(bCreateComment)
585 OUStringBuffer aBuf(ImpGetDescriptionStr(STR_ViewCreateObj));
586 const sal_uInt32 nPointCount(rDrag.GetPointCount());
588 if(SdrCircKind::Full != meCircleKind && nPointCount > 2)
590 const ImpCircUser* pU = static_cast<const ImpCircUser*>(rDrag.GetUser());
591 Degree100 nAngle;
593 aBuf.append(" (");
595 if(3 == nPointCount)
597 nAngle = pU->nStart;
599 else
601 nAngle = pU->nEnd;
604 aBuf.append(SdrModel::GetAngleString(nAngle));
605 aBuf.append(')');
608 return aBuf.makeStringAndClear();
610 else
612 const bool bAngle(rDrag.GetHdl() && SdrHdlKind::Circle == rDrag.GetHdl()->GetKind());
614 if(bAngle)
616 const Degree100 nAngle(1 == rDrag.GetHdl()->GetPointNum() ? nStartAngle : nEndAngle);
618 return ImpGetDescriptionStr(STR_DragCircAngle) +
619 " (" +
620 SdrModel::GetAngleString(nAngle) +
621 ")";
623 else
625 return SdrTextObj::getSpecialDragComment(rDrag);
631 void ImpCircUser::SetCreateParams(SdrDragStat const & rStat)
633 rStat.TakeCreateRect(aR);
634 aR.Normalize();
635 aCenter=aR.Center();
636 nWdt=aR.Right()-aR.Left();
637 nHgt=aR.Bottom()-aR.Top();
638 nStart=0_deg100;
639 nEnd=36000_deg100;
640 if (rStat.GetPointCount()>2) {
641 Point aP(rStat.GetPoint(2)-aCenter);
642 if (nWdt==0) aP.setX(0 );
643 if (nHgt==0) aP.setY(0 );
644 if (nWdt>=nHgt) {
645 if (nHgt!=0) aP.setY(aP.Y()*nWdt/nHgt );
646 } else {
647 if (nWdt!=0) aP.setX(aP.X()*nHgt/nWdt );
649 nStart=NormAngle36000(GetAngle(aP));
650 if (rStat.GetView()!=nullptr && rStat.GetView()->IsAngleSnapEnabled()) {
651 Degree100 nSA=rStat.GetView()->GetSnapAngle();
652 if (nSA) { // angle snapping
653 nStart+=nSA/2_deg100;
654 nStart/=nSA;
655 nStart*=nSA;
656 nStart=NormAngle36000(nStart);
659 aP1 = GetAnglePnt(aR,nStart);
660 nEnd=nStart;
661 } else aP1=aCenter;
662 if (rStat.GetPointCount()<=3)
663 return;
665 Point aP(rStat.GetPoint(3)-aCenter);
666 if (nWdt>=nHgt) {
667 aP.setY(BigMulDiv(aP.Y(),nWdt,nHgt) );
668 } else {
669 aP.setX(BigMulDiv(aP.X(),nHgt,nWdt) );
671 nEnd=NormAngle36000(GetAngle(aP));
672 if (rStat.GetView()!=nullptr && rStat.GetView()->IsAngleSnapEnabled()) {
673 Degree100 nSA=rStat.GetView()->GetSnapAngle();
674 if (nSA) { // angle snapping
675 nEnd+=nSA/2_deg100;
676 nEnd/=nSA;
677 nEnd*=nSA;
678 nEnd=NormAngle36000(nEnd);
683 void SdrCircObj::ImpSetCreateParams(SdrDragStat& rStat)
685 ImpCircUser* pU=static_cast<ImpCircUser*>(rStat.GetUser());
686 if (pU==nullptr) {
687 pU=new ImpCircUser;
688 rStat.SetUser(std::unique_ptr<ImpCircUser>(pU));
690 pU->SetCreateParams(rStat);
693 bool SdrCircObj::BegCreate(SdrDragStat& rStat)
695 rStat.SetOrtho4Possible();
696 tools::Rectangle aRect1(rStat.GetStart(), rStat.GetNow());
697 aRect1.Normalize();
698 rStat.SetActionRect(aRect1);
699 setRectangle(aRect1);
700 ImpSetCreateParams(rStat);
701 return true;
704 bool SdrCircObj::MovCreate(SdrDragStat& rStat)
706 ImpSetCreateParams(rStat);
707 ImpCircUser* pU=static_cast<ImpCircUser*>(rStat.GetUser());
708 rStat.SetActionRect(pU->aR);
709 setRectangle(pU->aR); // for ObjName
710 ImpJustifyRect(maRectangle);
711 nStartAngle=pU->nStart;
712 nEndAngle=pU->nEnd;
713 SetBoundRectDirty();
714 m_bSnapRectDirty=true;
715 SetXPolyDirty();
717 // #i103058# push current angle settings to ItemSet to
718 // allow FullDrag visualisation
719 if(rStat.GetPointCount() >= 4)
721 ImpSetCircInfoToAttr();
724 return true;
727 bool SdrCircObj::EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd)
729 ImpSetCreateParams(rStat);
730 ImpCircUser* pU=static_cast<ImpCircUser*>(rStat.GetUser());
731 bool bRet = false;
732 if (eCmd==SdrCreateCmd::ForceEnd && rStat.GetPointCount()<4) meCircleKind=SdrCircKind::Full;
733 if (meCircleKind==SdrCircKind::Full) {
734 bRet=rStat.GetPointCount()>=2;
735 if (bRet) {
736 tools::Rectangle aRectangle(pU->aR);
737 ImpJustifyRect(aRectangle);
738 setRectangle(aRectangle);
740 } else {
741 rStat.SetNoSnap(rStat.GetPointCount()>=2);
742 rStat.SetOrtho4Possible(rStat.GetPointCount()<2);
743 bRet=rStat.GetPointCount()>=4;
744 if (bRet) {
745 tools::Rectangle aRectangle(pU->aR);
746 ImpJustifyRect(aRectangle);
747 setRectangle(aRectangle);
748 nStartAngle=pU->nStart;
749 nEndAngle=pU->nEnd;
752 m_bClosedObj=meCircleKind!=SdrCircKind::Arc;
753 SetBoundAndSnapRectsDirty();
754 SetXPolyDirty();
755 ImpSetCircInfoToAttr();
756 if (bRet)
757 rStat.SetUser(nullptr);
758 return bRet;
761 void SdrCircObj::BrkCreate(SdrDragStat& rStat)
763 rStat.SetUser(nullptr);
766 bool SdrCircObj::BckCreate(SdrDragStat& rStat)
768 rStat.SetNoSnap(rStat.GetPointCount()>=3);
769 rStat.SetOrtho4Possible(rStat.GetPointCount()<3);
770 return meCircleKind!=SdrCircKind::Full;
773 basegfx::B2DPolyPolygon SdrCircObj::TakeCreatePoly(const SdrDragStat& rDrag) const
775 const ImpCircUser* pU = static_cast<const ImpCircUser*>(rDrag.GetUser());
777 if(rDrag.GetPointCount() < 4)
779 // force to OBJ_CIRC to get full visualisation
780 basegfx::B2DPolyPolygon aRetval(ImpCalcXPolyCirc(SdrCircKind::Full, pU->aR, pU->nStart, pU->nEnd));
782 if(3 == rDrag.GetPointCount())
784 // add edge to first point on ellipse
785 basegfx::B2DPolygon aNew;
787 aNew.append(basegfx::B2DPoint(pU->aCenter.X(), pU->aCenter.Y()));
788 aNew.append(basegfx::B2DPoint(pU->aP1.X(), pU->aP1.Y()));
789 aRetval.append(aNew);
792 return aRetval;
794 else
796 return basegfx::B2DPolyPolygon(ImpCalcXPolyCirc(meCircleKind, pU->aR, pU->nStart, pU->nEnd));
800 PointerStyle SdrCircObj::GetCreatePointer() const
802 switch (meCircleKind) {
803 case SdrCircKind::Full: return PointerStyle::DrawEllipse;
804 case SdrCircKind::Section: return PointerStyle::DrawPie;
805 case SdrCircKind::Arc: return PointerStyle::DrawArc;
806 case SdrCircKind::Cut: return PointerStyle::DrawCircleCut;
807 default: break;
808 } // switch
809 return PointerStyle::Cross;
812 void SdrCircObj::NbcMove(const Size& aSize)
814 moveRectangle(aSize.Width(), aSize.Height());
815 moveOutRectangle(aSize.Width(), aSize.Height());
816 maSnapRect.Move(aSize);
817 SetXPolyDirty();
818 SetBoundAndSnapRectsDirty(true);
821 void SdrCircObj::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact)
823 Degree100 nAngle0 = maGeo.nRotationAngle;
824 bool bNoShearRota = (maGeo.nRotationAngle == 0_deg100 && maGeo.nShearAngle == 0_deg100);
825 SdrTextObj::NbcResize(rRef,xFact,yFact);
826 bNoShearRota |= (maGeo.nRotationAngle == 0_deg100 && maGeo.nShearAngle == 0_deg100);
827 if (meCircleKind!=SdrCircKind::Full) {
828 bool bXMirr=(xFact.GetNumerator()<0) != (xFact.GetDenominator()<0);
829 bool bYMirr=(yFact.GetNumerator()<0) != (yFact.GetDenominator()<0);
830 if (bXMirr || bYMirr) {
831 // At bXMirr!=bYMirr we should actually swap both line ends.
832 // That, however, is pretty bad (because of forced "hard" formatting).
833 // Alternatively, we could implement a bMirrored flag (maybe even
834 // a more general one, e. g. for mirrored text, ...).
835 Degree100 nS0=nStartAngle;
836 Degree100 nE0=nEndAngle;
837 if (bNoShearRota) {
838 // the RectObj already mirrors at VMirror because of a 180deg rotation
839 if (! (bXMirr && bYMirr)) {
840 Degree100 nTmp=nS0;
841 nS0=18000_deg100-nE0;
842 nE0=18000_deg100-nTmp;
844 } else { // mirror contorted ellipses
845 if (bXMirr!=bYMirr) {
846 nS0+=nAngle0;
847 nE0+=nAngle0;
848 if (bXMirr) {
849 Degree100 nTmp=nS0;
850 nS0=18000_deg100-nE0;
851 nE0=18000_deg100-nTmp;
853 if (bYMirr) {
854 Degree100 nTmp=nS0;
855 nS0=-nE0;
856 nE0=-nTmp;
858 nS0 -= maGeo.nRotationAngle;
859 nE0 -= maGeo.nRotationAngle;
862 Degree100 nAngleDif=nE0-nS0;
863 nStartAngle=NormAngle36000(nS0);
864 nEndAngle =NormAngle36000(nE0);
865 if (nAngleDif==36000_deg100) nEndAngle+=nAngleDif; // full circle
868 SetXPolyDirty();
869 ImpSetCircInfoToAttr();
872 void SdrCircObj::NbcShear(const Point& rRef, Degree100 nAngle, double tn, bool bVShear)
874 SdrTextObj::NbcShear(rRef,nAngle,tn,bVShear);
875 SetXPolyDirty();
876 ImpSetCircInfoToAttr();
879 void SdrCircObj::NbcMirror(const Point& rRef1, const Point& rRef2)
881 bool bFreeMirr=meCircleKind!=SdrCircKind::Full;
882 Point aTmpPt1;
883 Point aTmpPt2;
884 if (bFreeMirr) { // some preparations for using an arbitrary axis of reflection
885 Point aCenter(getRectangle().Center());
886 tools::Long nWdt = getRectangle().GetWidth() - 1;
887 tools::Long nHgt = getRectangle().GetHeight() - 1;
888 tools::Long nMaxRad=(std::max(nWdt,nHgt)+1) /2;
889 // starting point
890 double a = toRadians(nStartAngle);
891 aTmpPt1=Point(FRound(cos(a)*nMaxRad),-FRound(sin(a)*nMaxRad));
892 if (nWdt==0) aTmpPt1.setX(0 );
893 if (nHgt==0) aTmpPt1.setY(0 );
894 aTmpPt1+=aCenter;
895 // finishing point
896 a = toRadians(nEndAngle);
897 aTmpPt2=Point(FRound(cos(a)*nMaxRad),-FRound(sin(a)*nMaxRad));
898 if (nWdt==0) aTmpPt2.setX(0 );
899 if (nHgt==0) aTmpPt2.setY(0 );
900 aTmpPt2+=aCenter;
901 if (maGeo.nRotationAngle)
903 RotatePoint(aTmpPt1, getRectangle().TopLeft(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
904 RotatePoint(aTmpPt2, getRectangle().TopLeft(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
906 if (maGeo.nShearAngle)
908 ShearPoint(aTmpPt1, getRectangle().TopLeft(), maGeo.mfTanShearAngle);
909 ShearPoint(aTmpPt2, getRectangle().TopLeft(), maGeo.mfTanShearAngle);
912 SdrTextObj::NbcMirror(rRef1,rRef2);
913 if (meCircleKind!=SdrCircKind::Full) { // adapt starting and finishing angle
914 MirrorPoint(aTmpPt1,rRef1,rRef2);
915 MirrorPoint(aTmpPt2,rRef1,rRef2);
916 // unrotate:
917 if (maGeo.nRotationAngle)
919 RotatePoint(aTmpPt1, getRectangle().TopLeft(), -maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle); // -sin for reversion
920 RotatePoint(aTmpPt2, getRectangle().TopLeft(), -maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle); // -sin for reversion
922 // unshear:
923 if (maGeo.nShearAngle)
925 ShearPoint(aTmpPt1, getRectangle().TopLeft(), -maGeo.mfTanShearAngle); // -tan for reversion
926 ShearPoint(aTmpPt2, getRectangle().TopLeft(), -maGeo.mfTanShearAngle); // -tan for reversion
928 Point aCenter(getRectangle().Center());
929 aTmpPt1-=aCenter;
930 aTmpPt2-=aCenter;
931 // because it's mirrored, the angles are swapped, too
932 nStartAngle=GetAngle(aTmpPt2);
933 nEndAngle =GetAngle(aTmpPt1);
934 Degree100 nAngleDif=nEndAngle-nStartAngle;
935 nStartAngle=NormAngle36000(nStartAngle);
936 nEndAngle =NormAngle36000(nEndAngle);
937 if (nAngleDif==36000_deg100) nEndAngle+=nAngleDif; // full circle
939 SetXPolyDirty();
940 ImpSetCircInfoToAttr();
943 std::unique_ptr<SdrObjGeoData> SdrCircObj::NewGeoData() const
945 return std::make_unique<SdrCircObjGeoData>();
948 void SdrCircObj::SaveGeoData(SdrObjGeoData& rGeo) const
950 SdrRectObj::SaveGeoData(rGeo);
951 SdrCircObjGeoData& rCGeo=static_cast<SdrCircObjGeoData&>(rGeo);
952 rCGeo.nStartAngle=nStartAngle;
953 rCGeo.nEndAngle =nEndAngle;
956 void SdrCircObj::RestoreGeoData(const SdrObjGeoData& rGeo)
958 SdrRectObj::RestoreGeoData(rGeo);
959 const SdrCircObjGeoData& rCGeo=static_cast<const SdrCircObjGeoData&>(rGeo);
960 nStartAngle=rCGeo.nStartAngle;
961 nEndAngle =rCGeo.nEndAngle;
962 SetXPolyDirty();
963 ImpSetCircInfoToAttr();
966 static void Union(tools::Rectangle& rR, const Point& rP)
968 if (rP.X()<rR.Left ()) rR.SetLeft(rP.X() );
969 if (rP.X()>rR.Right ()) rR.SetRight(rP.X() );
970 if (rP.Y()<rR.Top ()) rR.SetTop(rP.Y() );
971 if (rP.Y()>rR.Bottom()) rR.SetBottom(rP.Y() );
974 void SdrCircObj::TakeUnrotatedSnapRect(tools::Rectangle& rRect) const
976 rRect = getRectangle();
977 if (meCircleKind!=SdrCircKind::Full) {
978 const Point aPntStart(GetAnglePnt(getRectangle(), nStartAngle));
979 const Point aPntEnd(GetAnglePnt(getRectangle(), nEndAngle));
980 Degree100 a=nStartAngle;
981 Degree100 e=nEndAngle;
982 rRect.SetLeft(getRectangle().Right() );
983 rRect.SetRight(getRectangle().Left() );
984 rRect.SetTop(getRectangle().Bottom() );
985 rRect.SetBottom(getRectangle().Top() );
986 Union(rRect,aPntStart);
987 Union(rRect,aPntEnd);
988 if ((a<=18000_deg100 && e>=18000_deg100) || (a>e && (a<=18000_deg100 || e>=18000_deg100))) {
989 Union(rRect, getRectangle().LeftCenter());
991 if ((a<=27000_deg100 && e>=27000_deg100) || (a>e && (a<=27000_deg100 || e>=27000_deg100))) {
992 Union(rRect, getRectangle().BottomCenter());
994 if (a>e) {
995 Union(rRect, getRectangle().RightCenter());
997 if ((a<=9000_deg100 && e>=9000_deg100) || (a>e && (a<=9000_deg100 || e>=9000_deg100))) {
998 Union(rRect, getRectangle().TopCenter());
1000 if (meCircleKind==SdrCircKind::Section) {
1001 Union(rRect, getRectangle().Center());
1003 if (maGeo.nRotationAngle)
1005 Point aDst(rRect.TopLeft());
1006 aDst -= getRectangle().TopLeft();
1007 Point aDst0(aDst);
1008 RotatePoint(aDst,Point(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
1009 aDst-=aDst0;
1010 rRect.Move(aDst.X(),aDst.Y());
1013 if (maGeo.nShearAngle==0_deg100)
1014 return;
1016 tools::Long nDst = FRound((rRect.Bottom() - rRect.Top()) * maGeo.mfTanShearAngle);
1017 if (maGeo.nShearAngle > 0_deg100)
1019 Point aRef(rRect.TopLeft());
1020 rRect.AdjustLeft( -nDst );
1021 Point aTmpPt(rRect.TopLeft());
1022 RotatePoint(aTmpPt, aRef, maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
1023 aTmpPt-=rRect.TopLeft();
1024 rRect.Move(aTmpPt.X(),aTmpPt.Y());
1025 } else {
1026 rRect.AdjustRight( -nDst );
1030 void SdrCircObj::RecalcSnapRect()
1032 if (PaintNeedsXPolyCirc()) {
1033 maSnapRect=GetXPoly().GetBoundRect();
1034 } else {
1035 TakeUnrotatedSnapRect(maSnapRect);
1039 void SdrCircObj::NbcSetSnapRect(const tools::Rectangle& rRect)
1041 if (maGeo.nRotationAngle || maGeo.nShearAngle || meCircleKind != SdrCircKind::Full)
1043 tools::Rectangle aSR0(GetSnapRect());
1044 tools::Long nWdt0=aSR0.Right()-aSR0.Left();
1045 tools::Long nHgt0=aSR0.Bottom()-aSR0.Top();
1046 tools::Long nWdt1=rRect.Right()-rRect.Left();
1047 tools::Long nHgt1=rRect.Bottom()-rRect.Top();
1048 NbcResize(maSnapRect.TopLeft(),Fraction(nWdt1,nWdt0),Fraction(nHgt1,nHgt0));
1049 NbcMove(Size(rRect.Left()-aSR0.Left(),rRect.Top()-aSR0.Top()));
1050 } else {
1051 setRectangle(rRect);
1052 ImpJustifyRect(maRectangle);
1054 SetBoundAndSnapRectsDirty();
1055 SetXPolyDirty();
1056 ImpSetCircInfoToAttr();
1059 sal_uInt32 SdrCircObj::GetSnapPointCount() const
1061 if (meCircleKind==SdrCircKind::Full) {
1062 return 1;
1063 } else {
1064 return 3;
1068 Point SdrCircObj::GetSnapPoint(sal_uInt32 i) const
1070 switch (i)
1072 case 1 : return GetAnglePnt(getRectangle(), nStartAngle);
1073 case 2 : return GetAnglePnt(getRectangle(), nEndAngle);
1074 default: return getRectangle().Center();
1078 void SdrCircObj::Notify(SfxBroadcaster& rBC, const SfxHint& rHint)
1080 SetXPolyDirty();
1081 SdrRectObj::Notify(rBC,rHint);
1082 ImpSetAttrToCircInfo();
1086 void SdrCircObj::ImpSetAttrToCircInfo()
1088 const SfxItemSet& rSet = GetObjectItemSet();
1089 SdrCircKind eNewKind = rSet.Get(SDRATTR_CIRCKIND).GetValue();
1091 Degree100 nNewStart = rSet.Get(SDRATTR_CIRCSTARTANGLE).GetValue();
1092 Degree100 nNewEnd = rSet.Get(SDRATTR_CIRCENDANGLE).GetValue();
1094 bool bKindChg = meCircleKind != eNewKind;
1095 bool bAngleChg = nNewStart != nStartAngle || nNewEnd != nEndAngle;
1097 if(bKindChg || bAngleChg)
1099 meCircleKind = eNewKind;
1100 nStartAngle = nNewStart;
1101 nEndAngle = nNewEnd;
1103 if(bKindChg || (meCircleKind != SdrCircKind::Full && bAngleChg))
1105 SetXPolyDirty();
1106 SetBoundAndSnapRectsDirty();
1111 void SdrCircObj::ImpSetCircInfoToAttr()
1113 const SfxItemSet& rSet = GetObjectItemSet();
1115 SdrCircKind eOldKindA = rSet.Get(SDRATTR_CIRCKIND).GetValue();
1116 Degree100 nOldStartAngle = rSet.Get(SDRATTR_CIRCSTARTANGLE).GetValue();
1117 Degree100 nOldEndAngle = rSet.Get(SDRATTR_CIRCENDANGLE).GetValue();
1119 if(meCircleKind == eOldKindA && nStartAngle == nOldStartAngle && nEndAngle == nOldEndAngle)
1120 return;
1122 // since SetItem() implicitly calls ImpSetAttrToCircInfo()
1123 // setting the item directly is necessary here.
1124 if(meCircleKind != eOldKindA)
1126 GetProperties().SetObjectItemDirect(SdrCircKindItem(meCircleKind));
1129 if(nStartAngle != nOldStartAngle)
1131 GetProperties().SetObjectItemDirect(makeSdrCircStartAngleItem(nStartAngle));
1134 if(nEndAngle != nOldEndAngle)
1136 GetProperties().SetObjectItemDirect(makeSdrCircEndAngleItem(nEndAngle));
1139 SetXPolyDirty();
1140 ImpSetAttrToCircInfo();
1143 rtl::Reference<SdrObject> SdrCircObj::DoConvertToPolyObj(bool bBezier, bool bAddText) const
1145 const bool bFill(meCircleKind != SdrCircKind::Arc);
1146 const basegfx::B2DPolygon aCircPolygon(ImpCalcXPolyCirc(meCircleKind, getRectangle(), nStartAngle, nEndAngle));
1147 rtl::Reference<SdrObject> pRet = ImpConvertMakeObj(basegfx::B2DPolyPolygon(aCircPolygon), bFill, bBezier);
1149 if(bAddText)
1151 pRet = ImpConvertAddText(std::move(pRet), bBezier);
1154 return pRet;
1157 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */