LibreOffice Module svx (master)  1
svdopath.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  * Licensed to the Apache Software Foundation (ASF) under one or more
12  * contributor license agreements. See the NOTICE file distributed
13  * with this work for additional information regarding copyright
14  * ownership. The ASF licenses this file to you under the Apache
15  * License, Version 2.0 (the "License"); you may not use this file
16  * except in compliance with the License. You may obtain a copy of
17  * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <tools/bigint.hxx>
21 #include <tools/helpers.hxx>
22 #include <svx/svdopath.hxx>
23 #include <math.h>
24 #include <svx/xpoly.hxx>
25 #include <svx/svdtrans.hxx>
26 #include <svx/svddrag.hxx>
27 #include <svx/svdmodel.hxx>
28 #include <svx/svdhdl.hxx>
29 #include <svx/svdview.hxx>
30 #include <svx/dialmgr.hxx>
31 #include <svx/strings.hrc>
32 
45 #include <vcl/ptrstyle.hxx>
46 #include <memory>
47 #include <sal/log.hxx>
48 
49 using namespace sdr;
50 
51 static sal_uInt16 GetPrevPnt(sal_uInt16 nPnt, sal_uInt16 nPntMax, bool bClosed)
52 {
53  if (nPnt>0) {
54  nPnt--;
55  } else {
56  nPnt=nPntMax;
57  if (bClosed) nPnt--;
58  }
59  return nPnt;
60 }
61 
62 static sal_uInt16 GetNextPnt(sal_uInt16 nPnt, sal_uInt16 nPntMax, bool bClosed)
63 {
64  nPnt++;
65  if (nPnt>nPntMax || (bClosed && nPnt>=nPntMax)) nPnt=0;
66  return nPnt;
67 }
68 
69 namespace {
70 
71 struct ImpSdrPathDragData : public SdrDragStatUserData
72 {
73  XPolygon aXP; // section of the original polygon
74  bool bValid; // FALSE = too few points
75  bool bClosed; // closed object?
76  sal_uInt16 nPoly; // number of the polygon in the PolyPolygon
77  sal_uInt16 nPnt; // number of point in the above polygon
78  sal_uInt16 nPointCount; // number of points of the polygon
79  bool bBegPnt; // dragged point is first point of a Polyline
80  bool bEndPnt; // dragged point is finishing point of a Polyline
81  sal_uInt16 nPrevPnt; // index of previous point
82  sal_uInt16 nNextPnt; // index of next point
83  bool bPrevIsBegPnt; // previous point is first point of a Polyline
84  bool bNextIsEndPnt; // next point is first point of a Polyline
85  sal_uInt16 nPrevPrevPnt; // index of point before previous point
86  sal_uInt16 nNextNextPnt; // index of point after next point
87  bool bControl; // point is a control point
88  bool bIsNextControl; // point is a control point after a support point
89  bool bPrevIsControl; // if nPnt is a support point: a control point comes before
90  bool bNextIsControl; // if nPnt is a support point: a control point comes after
91  sal_uInt16 nPrevPrevPnt0;
92  sal_uInt16 nPrevPnt0;
93  sal_uInt16 nPnt0;
94  sal_uInt16 nNextPnt0;
95  sal_uInt16 nNextNextPnt0;
96  bool bEliminate; // delete point? (is set by MovDrag)
97 
98  bool mbMultiPointDrag;
99  const XPolyPolygon maOrig;
100  XPolyPolygon maMove;
101  std::vector<SdrHdl*> maHandles;
102 
103 public:
104  ImpSdrPathDragData(const SdrPathObj& rPO, const SdrHdl& rHdl, bool bMuPoDr, const SdrDragStat& rDrag);
105  void ResetPoly(const SdrPathObj& rPO);
106  bool IsMultiPointDrag() const { return mbMultiPointDrag; }
107 };
108 
109 }
110 
111 ImpSdrPathDragData::ImpSdrPathDragData(const SdrPathObj& rPO, const SdrHdl& rHdl, bool bMuPoDr, const SdrDragStat& rDrag)
112  : aXP(5)
113  , bValid(false)
114  , bClosed(false)
115  , nPoly(0)
116  , nPnt(0)
117  , nPointCount(0)
118  , bBegPnt(false)
119  , bEndPnt(false)
120  , nPrevPnt(0)
121  , nNextPnt(0)
122  , bPrevIsBegPnt(false)
123  , bNextIsEndPnt(false)
124  , nPrevPrevPnt(0)
125  , nNextNextPnt(0)
126  , bControl(false)
127  , bIsNextControl(false)
128  , bPrevIsControl(false)
129  , bNextIsControl(false)
130  , nPrevPrevPnt0(0)
131  , nPrevPnt0(0)
132  , nPnt0(0)
133  , nNextPnt0(0)
134  , nNextNextPnt0(0)
135  , bEliminate(false)
136  , mbMultiPointDrag(bMuPoDr)
137  , maOrig(rPO.GetPathPoly())
138  , maHandles(0)
139 {
140  if(mbMultiPointDrag)
141  {
142  const SdrMarkView& rMarkView = *rDrag.GetView();
143  const SdrHdlList& rHdlList = rMarkView.GetHdlList();
144  const size_t nHdlCount = rHdlList.GetHdlCount();
145  const SdrObject* pInteractionObject(nHdlCount && rHdlList.GetHdl(0) ? rHdlList.GetHdl(0)->GetObj() : nullptr);
146 
147  for(size_t a = 0; a < nHdlCount; ++a)
148  {
149  SdrHdl* pTestHdl = rHdlList.GetHdl(a);
150 
151  if(pTestHdl && pTestHdl->IsSelected() && pTestHdl->GetObj() == pInteractionObject)
152  {
153  maHandles.push_back(pTestHdl);
154  }
155  }
156 
157  maMove = maOrig;
158  bValid = true;
159  }
160  else
161  {
162  sal_uInt16 nPntMax = 0; // maximum index
163  bValid=false;
164  bClosed=rPO.IsClosed(); // closed object?
165  nPoly=static_cast<sal_uInt16>(rHdl.GetPolyNum()); // number of the polygon in the PolyPolygon
166  nPnt=static_cast<sal_uInt16>(rHdl.GetPointNum()); // number of points in the above polygon
167  const XPolygon aTmpXP(rPO.GetPathPoly().getB2DPolygon(nPoly));
168  nPointCount=aTmpXP.GetPointCount(); // number of point of the polygon
169  if (nPointCount==0 || (bClosed && nPointCount==1)) return; // minimum of 1 points for Lines, minimum of 2 points for Polygon
170  nPntMax=nPointCount-1; // maximum index
171  bBegPnt=!bClosed && nPnt==0; // dragged point is first point of a Polyline
172  bEndPnt=!bClosed && nPnt==nPntMax; // dragged point is finishing point of a Polyline
173  if (bClosed && nPointCount<=3) { // if polygon is only a line
174  bBegPnt=(nPointCount<3) || nPnt==0;
175  bEndPnt=(nPointCount<3) || nPnt==nPntMax-1;
176  }
177  nPrevPnt=nPnt; // index of previous point
178  nNextPnt=nPnt; // index of next point
179  if (!bBegPnt) nPrevPnt=GetPrevPnt(nPnt,nPntMax,bClosed);
180  if (!bEndPnt) nNextPnt=GetNextPnt(nPnt,nPntMax,bClosed);
181  bPrevIsBegPnt=bBegPnt || (!bClosed && nPrevPnt==0);
182  bNextIsEndPnt=bEndPnt || (!bClosed && nNextPnt==nPntMax);
183  nPrevPrevPnt=nPnt; // index of point before previous point
184  nNextNextPnt=nPnt; // index of point after next point
185  if (!bPrevIsBegPnt) nPrevPrevPnt=GetPrevPnt(nPrevPnt,nPntMax,bClosed);
186  if (!bNextIsEndPnt) nNextNextPnt=GetNextPnt(nNextPnt,nPntMax,bClosed);
187  bControl=rHdl.IsPlusHdl(); // point is a control point
188  bIsNextControl=false; // point is a control point after a support point
189  bPrevIsControl=false; // if nPnt is a support point: a control point comes before
190  bNextIsControl=false; // if nPnt is a support point: a control point comes after
191  if (bControl) {
192  bIsNextControl=!aTmpXP.IsControl(nPrevPnt);
193  } else {
194  bPrevIsControl=!bBegPnt && !bPrevIsBegPnt && aTmpXP.GetFlags(nPrevPnt)==PolyFlags::Control;
195  bNextIsControl=!bEndPnt && !bNextIsEndPnt && aTmpXP.GetFlags(nNextPnt)==PolyFlags::Control;
196  }
197  nPrevPrevPnt0=nPrevPrevPnt;
198  nPrevPnt0 =nPrevPnt;
199  nPnt0 =nPnt;
200  nNextPnt0 =nNextPnt;
201  nNextNextPnt0=nNextNextPnt;
202  nPrevPrevPnt=0;
203  nPrevPnt=1;
204  nPnt=2;
205  nNextPnt=3;
206  nNextNextPnt=4;
207  bEliminate=false;
208  ResetPoly(rPO);
209  bValid=true;
210  }
211 }
212 
213 void ImpSdrPathDragData::ResetPoly(const SdrPathObj& rPO)
214 {
215  const XPolygon aTmpXP(rPO.GetPathPoly().getB2DPolygon(nPoly));
216  aXP[0]=aTmpXP[nPrevPrevPnt0]; aXP.SetFlags(0,aTmpXP.GetFlags(nPrevPrevPnt0));
217  aXP[1]=aTmpXP[nPrevPnt0]; aXP.SetFlags(1,aTmpXP.GetFlags(nPrevPnt0));
218  aXP[2]=aTmpXP[nPnt0]; aXP.SetFlags(2,aTmpXP.GetFlags(nPnt0));
219  aXP[3]=aTmpXP[nNextPnt0]; aXP.SetFlags(3,aTmpXP.GetFlags(nNextPnt0));
220  aXP[4]=aTmpXP[nNextNextPnt0]; aXP.SetFlags(4,aTmpXP.GetFlags(nNextNextPnt0));
221 }
222 
223 namespace {
224 
225 struct ImpPathCreateUser : public SdrDragStatUserData
226 {
227  Point aBezControl0;
228  Point aBezStart;
229  Point aBezCtrl1;
230  Point aBezCtrl2;
231  Point aBezEnd;
232  Point aCircStart;
233  Point aCircEnd;
234  Point aCircCenter;
235  Point aLineStart;
236  Point aLineEnd;
237  Point aRectP1;
238  Point aRectP2;
239  Point aRectP3;
240  long nCircRadius;
241  long nCircStAngle;
242  long nCircRelAngle;
243  bool bBezier;
244  bool bBezHasCtrl0;
245  bool bCircle;
246  bool bAngleSnap;
247  bool bLine;
248  bool bLine90;
249  bool bRect;
250  bool bMixedCreate;
251  sal_uInt16 nBezierStartPoint;
252  SdrObjKind eStartKind;
253  SdrObjKind eCurrentKind;
254 
255 public:
256  ImpPathCreateUser(): nCircRadius(0),nCircStAngle(0),nCircRelAngle(0),
257  bBezier(false),bBezHasCtrl0(false),bCircle(false),bAngleSnap(false),bLine(false),bLine90(false),bRect(false),
258  bMixedCreate(false),nBezierStartPoint(0),eStartKind(OBJ_NONE),eCurrentKind(OBJ_NONE) { }
259 
260  void ResetFormFlags() { bBezier=false; bCircle=false; bLine=false; bRect=false; }
261  bool IsFormFlag() const { return bBezier || bCircle || bLine || bRect; }
262  XPolygon GetFormPoly() const;
263  void CalcBezier(const Point& rP1, const Point& rP2, const Point& rDir, bool bMouseDown);
264  XPolygon GetBezierPoly() const;
265  void CalcCircle(const Point& rP1, const Point& rP2, const Point& rDir, SdrView const * pView);
266  XPolygon GetCirclePoly() const;
267  void CalcLine(const Point& rP1, const Point& rP2, const Point& rDir, SdrView const * pView);
268  static Point CalcLine(const Point& rCsr, long nDirX, long nDirY, SdrView const * pView);
269  XPolygon GetLinePoly() const;
270  void CalcRect(const Point& rP1, const Point& rP2, const Point& rDir, SdrView const * pView);
271  XPolygon GetRectPoly() const;
272 };
273 
274 }
275 
276 XPolygon ImpPathCreateUser::GetFormPoly() const
277 {
278  if (bBezier) return GetBezierPoly();
279  if (bCircle) return GetCirclePoly();
280  if (bLine) return GetLinePoly();
281  if (bRect) return GetRectPoly();
282  return XPolygon();
283 }
284 
285 void ImpPathCreateUser::CalcBezier(const Point& rP1, const Point& rP2, const Point& rDir, bool bMouseDown)
286 {
287  aBezStart=rP1;
288  aBezCtrl1=rP1+rDir;
289  aBezCtrl2=rP2;
290 
291  // #i21479#
292  // Also copy the end point when no end point is set yet
293  if (!bMouseDown || (0 == aBezEnd.X() && 0 == aBezEnd.Y())) aBezEnd=rP2;
294 
295  bBezier=true;
296 }
297 
298 XPolygon ImpPathCreateUser::GetBezierPoly() const
299 {
300  XPolygon aXP(4);
301  aXP[0]=aBezStart; aXP.SetFlags(0,PolyFlags::Smooth);
302  aXP[1]=aBezCtrl1; aXP.SetFlags(1,PolyFlags::Control);
303  aXP[2]=aBezCtrl2; aXP.SetFlags(2,PolyFlags::Control);
304  aXP[3]=aBezEnd;
305  return aXP;
306 }
307 
308 void ImpPathCreateUser::CalcCircle(const Point& rP1, const Point& rP2, const Point& rDir, SdrView const * pView)
309 {
310  long nTangAngle=GetAngle(rDir);
311  aCircStart=rP1;
312  aCircEnd=rP2;
313  aCircCenter=rP1;
314  long dx=rP2.X()-rP1.X();
315  long dy=rP2.Y()-rP1.Y();
316  long dAngle=GetAngle(Point(dx,dy))-nTangAngle;
317  dAngle=NormAngle36000(dAngle);
318  long nTmpAngle=NormAngle36000(9000-dAngle);
319  bool bRet=nTmpAngle!=9000 && nTmpAngle!=27000;
320  long nRad=0;
321  if (bRet) {
322  double cs = cos(nTmpAngle * F_PI18000);
323  double nR=static_cast<double>(GetLen(Point(dx,dy)))/cs/2;
324  nRad=std::abs(FRound(nR));
325  }
326  if (dAngle<18000) {
327  nCircStAngle=NormAngle36000(nTangAngle-9000);
328  nCircRelAngle=NormAngle36000(2*dAngle);
329  aCircCenter.AdjustX(FRound(nRad * cos((nTangAngle + 9000) * F_PI18000)));
330  aCircCenter.AdjustY(-(FRound(nRad * sin((nTangAngle + 9000) * F_PI18000))));
331  } else {
332  nCircStAngle=NormAngle36000(nTangAngle+9000);
333  nCircRelAngle=-NormAngle36000(36000-2*dAngle);
334  aCircCenter.AdjustX(FRound(nRad * cos((nTangAngle - 9000) * F_PI18000)));
335  aCircCenter.AdjustY(-(FRound(nRad * sin((nTangAngle - 9000) * F_PI18000))));
336  }
337  bAngleSnap=pView!=nullptr && pView->IsAngleSnapEnabled();
338  if (bAngleSnap) {
339  long nSA=pView->GetSnapAngle();
340  if (nSA!=0) { // angle snapping
341  bool bNeg=nCircRelAngle<0;
342  if (bNeg) nCircRelAngle=-nCircRelAngle;
343  nCircRelAngle+=nSA/2;
344  nCircRelAngle/=nSA;
345  nCircRelAngle*=nSA;
346  nCircRelAngle=NormAngle36000(nCircRelAngle);
347  if (bNeg) nCircRelAngle=-nCircRelAngle;
348  }
349  }
350  nCircRadius=nRad;
351  if (nRad==0 || std::abs(nCircRelAngle)<5) bRet=false;
352  bCircle=bRet;
353 }
354 
355 XPolygon ImpPathCreateUser::GetCirclePoly() const
356 {
357  if (nCircRelAngle>=0) {
358  XPolygon aXP(aCircCenter,nCircRadius,nCircRadius,
359  sal_uInt16((nCircStAngle+5)/10),sal_uInt16((nCircStAngle+nCircRelAngle+5)/10),false);
360  aXP[0]=aCircStart; aXP.SetFlags(0,PolyFlags::Smooth);
361  if (!bAngleSnap) aXP[aXP.GetPointCount()-1]=aCircEnd;
362  return aXP;
363  } else {
364  XPolygon aXP(aCircCenter,nCircRadius,nCircRadius,
365  sal_uInt16(NormAngle36000(nCircStAngle+nCircRelAngle+5)/10),sal_uInt16((nCircStAngle+5)/10),false);
366  sal_uInt16 nCount=aXP.GetPointCount();
367  for (sal_uInt16 nNum=nCount/2; nNum>0;) {
368  nNum--; // reverse XPoly's order of points
369  sal_uInt16 n2=nCount-nNum-1;
370  Point aPt(aXP[nNum]);
371  aXP[nNum]=aXP[n2];
372  aXP[n2]=aPt;
373  }
374  aXP[0]=aCircStart; aXP.SetFlags(0,PolyFlags::Smooth);
375  if (!bAngleSnap) aXP[aXP.GetPointCount()-1]=aCircEnd;
376  return aXP;
377  }
378 }
379 
380 Point ImpPathCreateUser::CalcLine(const Point& aCsr, long nDirX, long nDirY, SdrView const * pView)
381 {
382  long x=aCsr.X();
383  long y=aCsr.Y();
384  bool bHLin=nDirY==0;
385  bool bVLin=nDirX==0;
386  if (bHLin) y=0;
387  else if (bVLin) x=0;
388  else {
389  long x1=BigMulDiv(y,nDirX,nDirY);
390  long y1=y;
391  long x2=x;
392  long y2=BigMulDiv(x,nDirY,nDirX);
393  long l1=std::abs(x1)+std::abs(y1);
394  long l2=std::abs(x2)+std::abs(y2);
395  if ((l1<=l2) != (pView!=nullptr && pView->IsBigOrtho())) {
396  x=x1; y=y1;
397  } else {
398  x=x2; y=y2;
399  }
400  }
401  return Point(x,y);
402 }
403 
404 void ImpPathCreateUser::CalcLine(const Point& rP1, const Point& rP2, const Point& rDir, SdrView const * pView)
405 {
406  aLineStart=rP1;
407  aLineEnd=rP2;
408  bLine90=false;
409  if (rP1==rP2 || (rDir.X()==0 && rDir.Y()==0)) { bLine=false; return; }
410  Point aTmpPt(rP2-rP1);
411  long nDirX=rDir.X();
412  long nDirY=rDir.Y();
413  Point aP1(CalcLine(aTmpPt, nDirX, nDirY,pView)); aP1-=aTmpPt; long nQ1=std::abs(aP1.X())+std::abs(aP1.Y());
414  Point aP2(CalcLine(aTmpPt, nDirY,-nDirX,pView)); aP2-=aTmpPt; long nQ2=std::abs(aP2.X())+std::abs(aP2.Y());
415  if (pView!=nullptr && pView->IsOrtho()) nQ1=0; // Ortho turns off at right angle
416  bLine90=nQ1>2*nQ2;
417  if (!bLine90) { // smooth transition
418  aLineEnd+=aP1;
419  } else { // rectangular transition
420  aLineEnd+=aP2;
421  }
422  bLine=true;
423 }
424 
425 XPolygon ImpPathCreateUser::GetLinePoly() const
426 {
427  XPolygon aXP(2);
428  aXP[0]=aLineStart; if (!bLine90) aXP.SetFlags(0,PolyFlags::Smooth);
429  aXP[1]=aLineEnd;
430  return aXP;
431 }
432 
433 void ImpPathCreateUser::CalcRect(const Point& rP1, const Point& rP2, const Point& rDir, SdrView const * pView)
434 {
435  aRectP1=rP1;
436  aRectP2=rP1;
437  aRectP3=rP2;
438  if (rP1==rP2 || (rDir.X()==0 && rDir.Y()==0)) { bRect=false; return; }
439  Point aTmpPt(rP2-rP1);
440  long nDirX=rDir.X();
441  long nDirY=rDir.Y();
442  long x=aTmpPt.X();
443  long y=aTmpPt.Y();
444  bool bHLin=nDirY==0;
445  bool bVLin=nDirX==0;
446  if (bHLin) y=0;
447  else if (bVLin) x=0;
448  else {
449  y=BigMulDiv(x,nDirY,nDirX);
450  long nHypLen=aTmpPt.Y()-y;
451  long nTangAngle=-GetAngle(rDir);
452  // sin=g/h, g=h*sin
453  double a = nTangAngle * F_PI18000;
454  double sn=sin(a);
455  double cs=cos(a);
456  double nGKathLen=nHypLen*sn;
457  y+=FRound(nGKathLen*sn);
458  x+=FRound(nGKathLen*cs);
459  }
460  aRectP2.AdjustX(x );
461  aRectP2.AdjustY(y );
462  if (pView!=nullptr && pView->IsOrtho()) {
463  long dx1=aRectP2.X()-aRectP1.X(); long dx1a=std::abs(dx1);
464  long dy1=aRectP2.Y()-aRectP1.Y(); long dy1a=std::abs(dy1);
465  long dx2=aRectP3.X()-aRectP2.X(); long dx2a=std::abs(dx2);
466  long dy2=aRectP3.Y()-aRectP2.Y(); long dy2a=std::abs(dy2);
467  bool b1MoreThan2=dx1a+dy1a>dx2a+dy2a;
468  if (b1MoreThan2 != pView->IsBigOrtho()) {
469  long xtemp=dy2a-dx1a; if (dx1<0) xtemp=-xtemp;
470  long ytemp=dx2a-dy1a; if (dy1<0) ytemp=-ytemp;
471  aRectP2.AdjustX(xtemp );
472  aRectP2.AdjustY(ytemp );
473  aRectP3.AdjustX(xtemp );
474  aRectP3.AdjustY(ytemp );
475  } else {
476  long xtemp=dy1a-dx2a; if (dx2<0) xtemp=-xtemp;
477  long ytemp=dx1a-dy2a; if (dy2<0) ytemp=-ytemp;
478  aRectP3.AdjustX(xtemp );
479  aRectP3.AdjustY(ytemp );
480  }
481  }
482  bRect=true;
483 }
484 
485 XPolygon ImpPathCreateUser::GetRectPoly() const
486 {
487  XPolygon aXP(3);
488  aXP[0]=aRectP1; aXP.SetFlags(0,PolyFlags::Smooth);
489  aXP[1]=aRectP2;
490  if (aRectP3!=aRectP2) aXP[2]=aRectP3;
491  return aXP;
492 }
493 
495 {
499  std::unique_ptr<ImpSdrPathDragData>
502 
503 public:
504  explicit ImpPathForDragAndCreate(SdrPathObj& rSdrPathObject);
505 
506  // drag stuff
507  bool beginPathDrag( SdrDragStat const & rDrag ) const;
508  bool movePathDrag( SdrDragStat& rDrag ) const;
509  bool endPathDrag( SdrDragStat const & rDrag );
510  OUString getSpecialDragComment(const SdrDragStat& rDrag) const;
512 
513  // create stuff
514  void BegCreate(SdrDragStat& rStat);
515  bool MovCreate(SdrDragStat& rStat);
516  bool EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd);
517  bool BckCreate(SdrDragStat const & rStat);
518  void BrkCreate(SdrDragStat& rStat);
520 
521  // helping stuff
522  static bool IsClosed(SdrObjKind eKind) { return eKind==OBJ_POLY || eKind==OBJ_PATHPOLY || eKind==OBJ_PATHFILL || eKind==OBJ_FREEFILL || eKind==OBJ_SPLNFILL; }
523  static bool IsFreeHand(SdrObjKind eKind) { return eKind==OBJ_FREELINE || eKind==OBJ_FREEFILL; }
524  static bool IsBezier(SdrObjKind eKind) { return eKind==OBJ_PATHLINE || eKind==OBJ_PATHFILL; }
525  bool IsCreating() const { return mbCreating; }
526 
527  // get the polygon
531 };
532 
534 : mrSdrPathObject(rSdrPathObject),
535  aPathPolygon(rSdrPathObject.GetPathPoly()),
536  meObjectKind(mrSdrPathObject.meKind),
537  mbCreating(false)
538 {
539 }
540 
542 {
543  const SdrHdl* pHdl=rDrag.GetHdl();
544  if(!pHdl)
545  return false;
546 
547  bool bMultiPointDrag(true);
548 
549  if(aPathPolygon[static_cast<sal_uInt16>(pHdl->GetPolyNum())].IsControl(static_cast<sal_uInt16>(pHdl->GetPointNum())))
550  bMultiPointDrag = false;
551 
552  if(bMultiPointDrag)
553  {
554  const SdrMarkView& rMarkView = *rDrag.GetView();
555  const SdrHdlList& rHdlList = rMarkView.GetHdlList();
556  const size_t nHdlCount = rHdlList.GetHdlCount();
557  const SdrObject* pInteractionObject(nHdlCount && rHdlList.GetHdl(0) ? rHdlList.GetHdl(0)->GetObj() : nullptr);
558  sal_uInt32 nSelectedPoints(0);
559 
560  for(size_t a = 0; a < nHdlCount; ++a)
561  {
562  SdrHdl* pTestHdl = rHdlList.GetHdl(a);
563 
564  if(pTestHdl && pTestHdl->IsSelected() && pTestHdl->GetObj() == pInteractionObject)
565  {
566  nSelectedPoints++;
567  }
568  }
569 
570  if(nSelectedPoints <= 1)
571  bMultiPointDrag = false;
572  }
573 
574  const_cast<ImpPathForDragAndCreate*>(this)->mpSdrPathDragData.reset( new ImpSdrPathDragData(mrSdrPathObject,*pHdl,bMultiPointDrag,rDrag) );
575 
576  if(!mpSdrPathDragData || !mpSdrPathDragData->bValid)
577  {
578  OSL_FAIL("ImpPathForDragAndCreate::BegDrag(): ImpSdrPathDragData is invalid.");
579  const_cast<ImpPathForDragAndCreate*>(this)->mpSdrPathDragData.reset();
580  return false;
581  }
582 
583  return true;
584 }
585 
587 {
588  if(!mpSdrPathDragData || !mpSdrPathDragData->bValid)
589  {
590  OSL_FAIL("ImpPathForDragAndCreate::MovDrag(): ImpSdrPathDragData is invalid.");
591  return false;
592  }
593 
594  if(mpSdrPathDragData->IsMultiPointDrag())
595  {
596  Point aDelta(rDrag.GetNow() - rDrag.GetStart());
597 
598  if(aDelta.X() || aDelta.Y())
599  {
600  for(SdrHdl* pHandle : mpSdrPathDragData->maHandles)
601  {
602  const sal_uInt16 nPolyIndex(static_cast<sal_uInt16>(pHandle->GetPolyNum()));
603  const sal_uInt16 nPointIndex(static_cast<sal_uInt16>(pHandle->GetPointNum()));
604  const XPolygon& rOrig = mpSdrPathDragData->maOrig[nPolyIndex];
605  XPolygon& rMove = mpSdrPathDragData->maMove[nPolyIndex];
606  const sal_uInt16 nPointCount(rOrig.GetPointCount());
607  bool bClosed(rOrig[0] == rOrig[nPointCount-1]);
608 
609  // move point itself
610  rMove[nPointIndex] = rOrig[nPointIndex] + aDelta;
611 
612  // when point is first and poly closed, move close point, too.
613  if(nPointCount > 0 && !nPointIndex && bClosed)
614  {
615  rMove[nPointCount - 1] = rOrig[nPointCount - 1] + aDelta;
616 
617  // when moving the last point it may be necessary to move the
618  // control point in front of this one, too.
619  if(nPointCount > 1 && rOrig.IsControl(nPointCount - 2))
620  rMove[nPointCount - 2] = rOrig[nPointCount - 2] + aDelta;
621  }
622 
623  // is a control point before this?
624  if(nPointIndex > 0 && rOrig.IsControl(nPointIndex - 1))
625  {
626  // Yes, move it, too
627  rMove[nPointIndex - 1] = rOrig[nPointIndex - 1] + aDelta;
628  }
629 
630  // is a control point after this?
631  if(nPointIndex + 1 < nPointCount && rOrig.IsControl(nPointIndex + 1))
632  {
633  // Yes, move it, too
634  rMove[nPointIndex + 1] = rOrig[nPointIndex + 1] + aDelta;
635  }
636  }
637  }
638  }
639  else
640  {
642 
643  // copy certain data locally to use less code and have faster access times
644  bool bClosed =mpSdrPathDragData->bClosed ; // closed object?
645  sal_uInt16 nPnt =mpSdrPathDragData->nPnt ; // number of point in the above polygon
646  bool bBegPnt =mpSdrPathDragData->bBegPnt ; // dragged point is first point of a Polyline
647  bool bEndPnt =mpSdrPathDragData->bEndPnt ; // dragged point is last point of a Polyline
648  sal_uInt16 nPrevPnt =mpSdrPathDragData->nPrevPnt ; // index of previous point
649  sal_uInt16 nNextPnt =mpSdrPathDragData->nNextPnt ; // index of next point
650  bool bPrevIsBegPnt =mpSdrPathDragData->bPrevIsBegPnt ; // previous point is first point of a Polyline
651  bool bNextIsEndPnt =mpSdrPathDragData->bNextIsEndPnt ; // next point is last point of a Polyline
652  sal_uInt16 nPrevPrevPnt =mpSdrPathDragData->nPrevPrevPnt ; // index of the point before the previous point
653  sal_uInt16 nNextNextPnt =mpSdrPathDragData->nNextNextPnt ; // index if the point after the next point
654  bool bControl =mpSdrPathDragData->bControl ; // point is a control point
655  bool bIsNextControl =mpSdrPathDragData->bIsNextControl; // point is a control point after a support point
656  bool bPrevIsControl =mpSdrPathDragData->bPrevIsControl; // if nPnt is a support point: there's a control point before
657  bool bNextIsControl =mpSdrPathDragData->bNextIsControl; // if nPnt is a support point: there's a control point after
658 
659  // Ortho for lines/polygons: keep angle
660  if (!bControl && rDrag.GetView()!=nullptr && rDrag.GetView()->IsOrtho()) {
661  bool bBigOrtho=rDrag.GetView()->IsBigOrtho();
662  Point aPos(rDrag.GetNow()); // current position
663  Point aPnt(mpSdrPathDragData->aXP[nPnt]); // the dragged point
664  sal_uInt16 nPnt1=0xFFFF,nPnt2=0xFFFF; // its neighboring points
665  Point aNewPos1,aNewPos2; // new alternative for aPos
666  bool bPnt1 = false, bPnt2 = false; // are these valid alternatives?
667  if (!bClosed && mpSdrPathDragData->nPointCount>=2) { // minimum of 2 points for lines
668  if (!bBegPnt) nPnt1=nPrevPnt;
669  if (!bEndPnt) nPnt2=nNextPnt;
670  }
671  if (bClosed && mpSdrPathDragData->nPointCount>=3) { // minimum of 3 points for polygon
672  nPnt1=nPrevPnt;
673  nPnt2=nNextPnt;
674  }
675  if (nPnt1!=0xFFFF && !bPrevIsControl) {
676  Point aPnt1=mpSdrPathDragData->aXP[nPnt1];
677  long ndx0=aPnt.X()-aPnt1.X();
678  long ndy0=aPnt.Y()-aPnt1.Y();
679  bool bHLin=ndy0==0;
680  bool bVLin=ndx0==0;
681  if (!bHLin || !bVLin) {
682  long ndx=aPos.X()-aPnt1.X();
683  long ndy=aPos.Y()-aPnt1.Y();
684  bPnt1=true;
685  double nXFact=0; if (!bVLin) nXFact=static_cast<double>(ndx)/static_cast<double>(ndx0);
686  double nYFact=0; if (!bHLin) nYFact=static_cast<double>(ndy)/static_cast<double>(ndy0);
687  bool bHor=bHLin || (!bVLin && (nXFact>nYFact) ==bBigOrtho);
688  bool bVer=bVLin || (!bHLin && (nXFact<=nYFact)==bBigOrtho);
689  if (bHor) ndy=long(ndy0*nXFact);
690  if (bVer) ndx=long(ndx0*nYFact);
691  aNewPos1=aPnt1;
692  aNewPos1.AdjustX(ndx );
693  aNewPos1.AdjustY(ndy );
694  }
695  }
696  if (nPnt2!=0xFFFF && !bNextIsControl) {
697  Point aPnt2=mpSdrPathDragData->aXP[nPnt2];
698  long ndx0=aPnt.X()-aPnt2.X();
699  long ndy0=aPnt.Y()-aPnt2.Y();
700  bool bHLin=ndy0==0;
701  bool bVLin=ndx0==0;
702  if (!bHLin || !bVLin) {
703  long ndx=aPos.X()-aPnt2.X();
704  long ndy=aPos.Y()-aPnt2.Y();
705  bPnt2=true;
706  double nXFact=0; if (!bVLin) nXFact=static_cast<double>(ndx)/static_cast<double>(ndx0);
707  double nYFact=0; if (!bHLin) nYFact=static_cast<double>(ndy)/static_cast<double>(ndy0);
708  bool bHor=bHLin || (!bVLin && (nXFact>nYFact) ==bBigOrtho);
709  bool bVer=bVLin || (!bHLin && (nXFact<=nYFact)==bBigOrtho);
710  if (bHor) ndy=long(ndy0*nXFact);
711  if (bVer) ndx=long(ndx0*nYFact);
712  aNewPos2=aPnt2;
713  aNewPos2.AdjustX(ndx );
714  aNewPos2.AdjustY(ndy );
715  }
716  }
717  if (bPnt1 && bPnt2) { // both alternatives exist (and compete)
718  BigInt nX1(aNewPos1.X()-aPos.X()); nX1*=nX1;
719  BigInt nY1(aNewPos1.Y()-aPos.Y()); nY1*=nY1;
720  BigInt nX2(aNewPos2.X()-aPos.X()); nX2*=nX2;
721  BigInt nY2(aNewPos2.Y()-aPos.Y()); nY2*=nY2;
722  nX1+=nY1; // correction distance to square
723  nX2+=nY2; // correction distance to square
724  // let the alternative that allows fewer correction win
725  if (nX1<nX2) bPnt2=false; else bPnt1=false;
726  }
727  if (bPnt1) rDrag.SetNow(aNewPos1);
728  if (bPnt2) rDrag.SetNow(aNewPos2);
729  }
730  rDrag.SetActionRect(tools::Rectangle(rDrag.GetNow(),rDrag.GetNow()));
731 
732  // specially for IBM: Eliminate points if both adjoining lines form near 180 degrees angle anyway
733  if (!bControl && rDrag.GetView()!=nullptr && rDrag.GetView()->IsEliminatePolyPoints() &&
734  !bBegPnt && !bEndPnt && !bPrevIsControl && !bNextIsControl)
735  {
736  Point aPt(mpSdrPathDragData->aXP[nNextPnt]);
737  aPt-=rDrag.GetNow();
738  long nAngle1=GetAngle(aPt);
739  aPt=rDrag.GetNow();
740  aPt-=mpSdrPathDragData->aXP[nPrevPnt];
741  long nAngle2=GetAngle(aPt);
742  long nDiff=nAngle1-nAngle2;
743  nDiff=std::abs(nDiff);
744  mpSdrPathDragData->bEliminate=nDiff<=rDrag.GetView()->GetEliminatePolyPointLimitAngle();
745  if (mpSdrPathDragData->bEliminate) { // adapt position, Smooth is true for the ends
746  aPt=mpSdrPathDragData->aXP[nNextPnt];
747  aPt+=mpSdrPathDragData->aXP[nPrevPnt];
748  aPt/=2;
749  rDrag.SetNow(aPt);
750  }
751  }
752 
753  // we dragged by this distance
754  Point aDiff(rDrag.GetNow()); aDiff-=mpSdrPathDragData->aXP[nPnt];
755 
756  /* There are 8 possible cases:
757  X 1. A control point neither on the left nor on the right.
758  o--X--o 2. There are control points on the left and the right, we are dragging a support point.
759  o--X 3. There is a control point on the left, we are dragging a support point.
760  X--o 4. There is a control point on the right, we are dragging a support point.
761  x--O--o 5. There are control points on the left and the right, we are dragging the left one.
762  x--O 6. There is a control point on the left, we are dragging it.
763  o--O--x 7. There are control points on the left and the right, we are dragging the right one.
764  O--x 8. There is a control point on the right, we are dragging it.
765  Note: modifying a line (not a curve!) might create a curve on the other end of the line
766  if Smooth is set there (with control points aligned to line).
767  */
768 
769  mpSdrPathDragData->aXP[nPnt]+=aDiff;
770 
771  // now check symmetric plus handles
772  if (bControl) { // cases 5,6,7,8
773  sal_uInt16 nSt; // the associated support point
774  sal_uInt16 nFix; // the opposing control point
775  if (bIsNextControl) { // if the next one is a control point, the on before has to be a support point
776  nSt=nPrevPnt;
777  nFix=nPrevPrevPnt;
778  } else {
779  nSt=nNextPnt;
780  nFix=nNextNextPnt;
781  }
782  if (mpSdrPathDragData->aXP.IsSmooth(nSt)) {
783  mpSdrPathDragData->aXP.CalcSmoothJoin(nSt,nPnt,nFix);
784  }
785  }
786 
787  if (!bControl) { // Cases 1,2,3,4. In case 1, nothing happens; in cases 3 and 4, there is more following below.
788  // move both control points
789  if (bPrevIsControl) mpSdrPathDragData->aXP[nPrevPnt]+=aDiff;
790  if (bNextIsControl) mpSdrPathDragData->aXP[nNextPnt]+=aDiff;
791  // align control point to line, if appropriate
792  if (mpSdrPathDragData->aXP.IsSmooth(nPnt)) {
793  if (bPrevIsControl && !bNextIsControl && !bEndPnt) { // case 3
794  mpSdrPathDragData->aXP.CalcSmoothJoin(nPnt,nNextPnt,nPrevPnt);
795  }
796  if (bNextIsControl && !bPrevIsControl && !bBegPnt) { // case 4
797  mpSdrPathDragData->aXP.CalcSmoothJoin(nPnt,nPrevPnt,nNextPnt);
798  }
799  }
800  // Now check the other ends of the line (nPnt+-1). If there is a
801  // curve (IsControl(nPnt+-2)) with SmoothJoin (nPnt+-1), the
802  // associated control point (nPnt+-2) has to be adapted.
803  if (!bBegPnt && !bPrevIsControl && !bPrevIsBegPnt && mpSdrPathDragData->aXP.IsSmooth(nPrevPnt)) {
804  if (mpSdrPathDragData->aXP.IsControl(nPrevPrevPnt)) {
805  mpSdrPathDragData->aXP.CalcSmoothJoin(nPrevPnt,nPnt,nPrevPrevPnt);
806  }
807  }
808  if (!bEndPnt && !bNextIsControl && !bNextIsEndPnt && mpSdrPathDragData->aXP.IsSmooth(nNextPnt)) {
809  if (mpSdrPathDragData->aXP.IsControl(nNextNextPnt)) {
810  mpSdrPathDragData->aXP.CalcSmoothJoin(nNextPnt,nPnt,nNextNextPnt);
811  }
812  }
813  }
814  }
815 
816  return true;
817 }
818 
820 {
821  Point aLinePt1;
822  Point aLinePt2;
823  bool bLineGlueMirror(OBJ_LINE == meObjectKind);
824  if (bLineGlueMirror) {
825  XPolygon& rXP=aPathPolygon[0];
826  aLinePt1=rXP[0];
827  aLinePt2=rXP[1];
828  }
829 
830  if(!mpSdrPathDragData || !mpSdrPathDragData->bValid)
831  {
832  OSL_FAIL("ImpPathForDragAndCreate::MovDrag(): ImpSdrPathDragData is invalid.");
833  return false;
834  }
835 
836  if(mpSdrPathDragData->IsMultiPointDrag())
837  {
839  }
840  else
841  {
842  const SdrHdl* pHdl=rDrag.GetHdl();
843 
844  // reference the polygon
845  XPolygon& rXP=aPathPolygon[static_cast<sal_uInt16>(pHdl->GetPolyNum())];
846 
847  // the 5 points that might have changed
848  if (!mpSdrPathDragData->bPrevIsBegPnt) rXP[mpSdrPathDragData->nPrevPrevPnt0]=mpSdrPathDragData->aXP[mpSdrPathDragData->nPrevPrevPnt];
849  if (!mpSdrPathDragData->bNextIsEndPnt) rXP[mpSdrPathDragData->nNextNextPnt0]=mpSdrPathDragData->aXP[mpSdrPathDragData->nNextNextPnt];
850  if (!mpSdrPathDragData->bBegPnt) rXP[mpSdrPathDragData->nPrevPnt0] =mpSdrPathDragData->aXP[mpSdrPathDragData->nPrevPnt];
851  if (!mpSdrPathDragData->bEndPnt) rXP[mpSdrPathDragData->nNextPnt0] =mpSdrPathDragData->aXP[mpSdrPathDragData->nNextPnt];
852  rXP[mpSdrPathDragData->nPnt0] =mpSdrPathDragData->aXP[mpSdrPathDragData->nPnt];
853 
854  // for closed objects: last point has to be equal to first point
855  if (mpSdrPathDragData->bClosed) rXP[rXP.GetPointCount()-1]=rXP[0];
856 
857  if (mpSdrPathDragData->bEliminate)
858  {
860  sal_uInt32 nPoly,nPnt;
861 
862  if(PolyPolygonEditor::GetRelativePolyPoint(aTempPolyPolygon, rDrag.GetHdl()->GetSourceHdlNum(), nPoly, nPnt))
863  {
864  basegfx::B2DPolygon aCandidate(aTempPolyPolygon.getB2DPolygon(nPoly));
865  aCandidate.remove(nPnt);
866 
867  if(aCandidate.count() < 2)
868  {
869  aTempPolyPolygon.remove(nPoly);
870  }
871  else
872  {
873  aTempPolyPolygon.setB2DPolygon(nPoly, aCandidate);
874  }
875  }
876 
877  aPathPolygon = XPolyPolygon(aTempPolyPolygon);
878  }
879 
880  // adapt angle for text beneath a simple line
881  if (bLineGlueMirror)
882  {
883  Point aLinePt1_(aPathPolygon[0][0]);
884  Point aLinePt2_(aPathPolygon[0][1]);
885  bool bXMirr=(aLinePt1_.X()>aLinePt2_.X())!=(aLinePt1.X()>aLinePt2.X());
886  bool bYMirr=(aLinePt1_.Y()>aLinePt2_.Y())!=(aLinePt1.Y()>aLinePt2.Y());
887  if (bXMirr || bYMirr) {
889  if (bXMirr) {
890  Point aRef2(aRef1);
891  aRef2.AdjustY( 1 );
893  }
894  if (bYMirr) {
895  Point aRef2(aRef1);
896  aRef2.AdjustX( 1 );
898  }
899  }
900  }
901  }
902 
903  mpSdrPathDragData.reset();
904 
905  return true;
906 }
907 
909 {
910  OUString aStr;
911  const SdrHdl* pHdl = rDrag.GetHdl();
912  const bool bCreateComment(rDrag.GetView() && &mrSdrPathObject == rDrag.GetView()->GetCreateObj());
913 
914  if(bCreateComment && rDrag.GetUser())
915  {
916  // #i103058# re-add old creation comment mode
917  const ImpPathCreateUser* pU = static_cast<const ImpPathCreateUser*>(rDrag.GetUser());
918  const SdrObjKind eOriginalKind(meObjectKind);
919  mrSdrPathObject.meKind = pU->eCurrentKind;
920  aStr = mrSdrPathObject.ImpGetDescriptionStr(STR_ViewCreateObj);
921  mrSdrPathObject.meKind = eOriginalKind;
922 
923  Point aPrev(rDrag.GetPrev());
924  Point aNow(rDrag.GetNow());
925 
926  if(pU->bLine)
927  aNow = pU->aLineEnd;
928 
929  aNow -= aPrev;
930  aStr += " (";
931 
932  if(pU->bCircle)
933  {
934  aStr += SdrModel::GetAngleString(std::abs(pU->nCircRelAngle))
935  + " r="
936  + mrSdrPathObject.getSdrModelFromSdrObject().GetMetricString(pU->nCircRadius, true);
937  }
938 
939  aStr += "dx="
941  + " dy="
943 
945  {
946  sal_Int32 nLen(GetLen(aNow));
947  sal_Int32 nAngle(GetAngle(aNow));
948  aStr += " l="
950  + " "
951  + SdrModel::GetAngleString(nAngle);
952  }
953 
954  aStr += ")";
955  }
956  else if(!pHdl)
957  {
958  // #i103058# fallback when no model and/or Handle, both needed
959  // for else-path
960  aStr = mrSdrPathObject.ImpGetDescriptionStr(STR_DragPathObj);
961  }
962  else
963  {
964  // #i103058# standard for modification; model and handle needed
965  ImpSdrPathDragData* pDragData = mpSdrPathDragData.get();
966 
967  if(!pDragData)
968  {
969  // getSpecialDragComment is also used from create, so fallback to GetUser()
970  // when mpSdrPathDragData is not set
971  pDragData = static_cast<ImpSdrPathDragData*>(rDrag.GetUser());
972  }
973 
974  if(!pDragData)
975  {
976  OSL_FAIL("ImpPathForDragAndCreate::MovDrag(): ImpSdrPathDragData is invalid.");
977  return OUString();
978  }
979 
980  if(!pDragData->IsMultiPointDrag() && pDragData->bEliminate)
981  {
982  // point of ...
983  aStr = mrSdrPathObject.ImpGetDescriptionStr(STR_ViewMarkedPoint);
984 
985  // delete %O
986  OUString aStr2(SvxResId(STR_EditDelete));
987 
988  // UNICODE: delete point of ...
989  aStr2 = aStr2.replaceFirst("%1", aStr);
990 
991  return aStr2;
992  }
993 
994  // dx=0.00 dy=0.00 -- both sides bezier
995  // dx=0.00 dy=0.00 l=0.00 0.00\302\260 -- one bezier/lever on one side, a start, or an ending
996  // dx=0.00 dy=0.00 l=0.00 0.00\302\260 / l=0.00 0.00\302\260 -- in between
997  Point aBeg(rDrag.GetStart());
998  Point aNow(rDrag.GetNow());
999 
1000  aStr.clear();
1001  aStr += "dx="
1002  + mrSdrPathObject.getSdrModelFromSdrObject().GetMetricString(aNow.X() - aBeg.X(), true)
1003  + " dy="
1004  + mrSdrPathObject.getSdrModelFromSdrObject().GetMetricString(aNow.Y() - aBeg.Y(), true);
1005 
1006  if(!pDragData->IsMultiPointDrag())
1007  {
1008  sal_uInt16 nPntNum(static_cast<sal_uInt16>(pHdl->GetPointNum()));
1009  const XPolygon& rXPoly = aPathPolygon[static_cast<sal_uInt16>(rDrag.GetHdl()->GetPolyNum())];
1010  sal_uInt16 nPointCount(rXPoly.GetPointCount());
1011  bool bClose(IsClosed(meObjectKind));
1012 
1013  if(bClose)
1014  nPointCount--;
1015 
1016  if(pHdl->IsPlusHdl())
1017  {
1018  // lever
1019  sal_uInt16 nRef(nPntNum);
1020 
1021  if(rXPoly.IsControl(nPntNum + 1))
1022  nRef--;
1023  else
1024  nRef++;
1025 
1026  aNow -= rXPoly[nRef];
1027 
1028  sal_Int32 nLen(GetLen(aNow));
1029  sal_Int32 nAngle(GetAngle(aNow));
1030  aStr += " l="
1032  + " "
1033  + SdrModel::GetAngleString(nAngle);
1034  }
1035  else if(nPointCount > 1)
1036  {
1037  sal_uInt16 nPntMax(nPointCount - 1);
1038  bool bIsClosed(IsClosed(meObjectKind));
1039  bool bPt1(nPntNum > 0);
1040  bool bPt2(nPntNum < nPntMax);
1041 
1042  if(bIsClosed && nPointCount > 2)
1043  {
1044  bPt1 = true;
1045  bPt2 = true;
1046  }
1047 
1048  sal_uInt16 nPt1,nPt2;
1049 
1050  if(nPntNum > 0)
1051  nPt1 = nPntNum - 1;
1052  else
1053  nPt1 = nPntMax;
1054 
1055  if(nPntNum < nPntMax)
1056  nPt2 = nPntNum + 1;
1057  else
1058  nPt2 = 0;
1059 
1060  if(bPt1 && rXPoly.IsControl(nPt1))
1061  bPt1 = false; // don't display
1062 
1063  if(bPt2 && rXPoly.IsControl(nPt2))
1064  bPt2 = false; // of bezier data
1065 
1066  if(bPt1)
1067  {
1068  Point aPt(aNow);
1069  aPt -= rXPoly[nPt1];
1070 
1071  sal_Int32 nLen(GetLen(aPt));
1072  sal_Int32 nAngle(GetAngle(aPt));
1073  aStr += " l="
1075  + " "
1076  + SdrModel::GetAngleString(nAngle);
1077  }
1078 
1079  if(bPt2)
1080  {
1081  if(bPt1)
1082  aStr += " / ";
1083  else
1084  aStr += " ";
1085 
1086  Point aPt(aNow);
1087  aPt -= rXPoly[nPt2];
1088 
1089  sal_Int32 nLen(GetLen(aPt));
1090  sal_Int32 nAngle(GetAngle(aPt));
1091  aStr += "l="
1093  + " "
1094  + SdrModel::GetAngleString(nAngle);
1095  }
1096  }
1097  }
1098  }
1099 
1100  return aStr;
1101 }
1102 
1104 {
1105  if(!mpSdrPathDragData || !mpSdrPathDragData->bValid)
1106  {
1107  OSL_FAIL("ImpPathForDragAndCreate::MovDrag(): ImpSdrPathDragData is invalid.");
1108  return basegfx::B2DPolyPolygon();
1109  }
1110 
1111  XPolyPolygon aRetval;
1112 
1113  if(mpSdrPathDragData->IsMultiPointDrag())
1114  {
1115  aRetval.Insert(mpSdrPathDragData->maMove);
1116  }
1117  else
1118  {
1119  const XPolygon& rXP=aPathPolygon[static_cast<sal_uInt16>(rDrag.GetHdl()->GetPolyNum())];
1120  if (rXP.GetPointCount()<=2) {
1121  XPolygon aXPoly(rXP);
1122  aXPoly[static_cast<sal_uInt16>(rDrag.GetHdl()->GetPointNum())]=rDrag.GetNow();
1123  aRetval.Insert(std::move(aXPoly));
1124  return aRetval.getB2DPolyPolygon();
1125  }
1126  // copy certain data locally to use less code and have faster access times
1127  bool bClosed =mpSdrPathDragData->bClosed ; // closed object?
1128  sal_uInt16 nPointCount = mpSdrPathDragData->nPointCount; // number of points
1129  sal_uInt16 nPnt =mpSdrPathDragData->nPnt ; // number of points in the polygon
1130  bool bBegPnt =mpSdrPathDragData->bBegPnt ; // dragged point is the first point of a Polyline
1131  bool bEndPnt =mpSdrPathDragData->bEndPnt ; // dragged point is the last point of a Polyline
1132  sal_uInt16 nPrevPnt =mpSdrPathDragData->nPrevPnt ; // index of the previous point
1133  sal_uInt16 nNextPnt =mpSdrPathDragData->nNextPnt ; // index of the next point
1134  bool bPrevIsBegPnt =mpSdrPathDragData->bPrevIsBegPnt ; // previous point is first point of a Polyline
1135  bool bNextIsEndPnt =mpSdrPathDragData->bNextIsEndPnt ; // next point is last point of a Polyline
1136  sal_uInt16 nPrevPrevPnt =mpSdrPathDragData->nPrevPrevPnt ; // index of the point before the previous point
1137  sal_uInt16 nNextNextPnt =mpSdrPathDragData->nNextNextPnt ; // index of the point after the last point
1138  bool bControl =mpSdrPathDragData->bControl ; // point is a control point
1139  bool bIsNextControl =mpSdrPathDragData->bIsNextControl; //point is a control point after a support point
1140  bool bPrevIsControl =mpSdrPathDragData->bPrevIsControl; // if nPnt is a support point: there's a control point before
1141  bool bNextIsControl =mpSdrPathDragData->bNextIsControl; // if nPnt is a support point: there's a control point after
1142  XPolygon aXPoly(mpSdrPathDragData->aXP);
1143  XPolygon aLine1(2);
1144  XPolygon aLine2(2);
1145  XPolygon aLine3(2);
1146  XPolygon aLine4(2);
1147  if (bControl) {
1148  aLine1[1]=mpSdrPathDragData->aXP[nPnt];
1149  if (bIsNextControl) { // is this a control point after the support point?
1150  aLine1[0]=mpSdrPathDragData->aXP[nPrevPnt];
1151  aLine2[0]=mpSdrPathDragData->aXP[nNextNextPnt];
1152  aLine2[1]=mpSdrPathDragData->aXP[nNextPnt];
1153  if (mpSdrPathDragData->aXP.IsSmooth(nPrevPnt) && !bPrevIsBegPnt && mpSdrPathDragData->aXP.IsControl(nPrevPrevPnt)) {
1154  aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-1],PolyFlags::Control);
1155  aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-2],PolyFlags::Normal);
1156  // leverage lines for the opposing curve segment
1157  aLine3[0]=mpSdrPathDragData->aXP[nPrevPnt];
1158  aLine3[1]=mpSdrPathDragData->aXP[nPrevPrevPnt];
1159  aLine4[0]=rXP[mpSdrPathDragData->nPrevPrevPnt0-2];
1160  aLine4[1]=rXP[mpSdrPathDragData->nPrevPrevPnt0-1];
1161  } else {
1162  aXPoly.Remove(0,1);
1163  }
1164  } else { // else this is a control point before a support point
1165  aLine1[0]=mpSdrPathDragData->aXP[nNextPnt];
1166  aLine2[0]=mpSdrPathDragData->aXP[nPrevPrevPnt];
1167  aLine2[1]=mpSdrPathDragData->aXP[nPrevPnt];
1168  if (mpSdrPathDragData->aXP.IsSmooth(nNextPnt) && !bNextIsEndPnt && mpSdrPathDragData->aXP.IsControl(nNextNextPnt)) {
1169  aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+1],PolyFlags::Control);
1170  aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+2],PolyFlags::Normal);
1171  // leverage lines for the opposing curve segment
1172  aLine3[0]=mpSdrPathDragData->aXP[nNextPnt];
1173  aLine3[1]=mpSdrPathDragData->aXP[nNextNextPnt];
1174  aLine4[0]=rXP[mpSdrPathDragData->nNextNextPnt0+2];
1175  aLine4[1]=rXP[mpSdrPathDragData->nNextNextPnt0+1];
1176  } else {
1177  aXPoly.Remove(aXPoly.GetPointCount()-1,1);
1178  }
1179  }
1180  } else { // else is not a control point
1181  if (mpSdrPathDragData->bEliminate) {
1182  aXPoly.Remove(2,1);
1183  }
1184  if (bPrevIsControl) aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-1],PolyFlags::Normal);
1185  else if (!bBegPnt && !bPrevIsBegPnt && mpSdrPathDragData->aXP.IsControl(nPrevPrevPnt)) {
1186  aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-1],PolyFlags::Control);
1187  aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-2],PolyFlags::Normal);
1188  } else {
1189  aXPoly.Remove(0,1);
1190  if (bBegPnt) aXPoly.Remove(0,1);
1191  }
1192  if (bNextIsControl) aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+1],PolyFlags::Normal);
1193  else if (!bEndPnt && !bNextIsEndPnt && mpSdrPathDragData->aXP.IsControl(nNextNextPnt)) {
1194  aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+1],PolyFlags::Control);
1195  aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+2],PolyFlags::Normal);
1196  } else {
1197  aXPoly.Remove(aXPoly.GetPointCount()-1,1);
1198  if (bEndPnt) aXPoly.Remove(aXPoly.GetPointCount()-1,1);
1199  }
1200  if (bClosed) { // "pear problem": 2 lines, 1 curve, everything smoothed, a point between both lines is dragged
1201  if (aXPoly.GetPointCount()>nPointCount && aXPoly.IsControl(1)) {
1202  sal_uInt16 a=aXPoly.GetPointCount();
1203  aXPoly[a-2]=aXPoly[2]; aXPoly.SetFlags(a-2,aXPoly.GetFlags(2));
1204  aXPoly[a-1]=aXPoly[3]; aXPoly.SetFlags(a-1,aXPoly.GetFlags(3));
1205  aXPoly.Remove(0,3);
1206  }
1207  }
1208  }
1209  aRetval.Insert(std::move(aXPoly));
1210  if (aLine1.GetPointCount()>1) aRetval.Insert(std::move(aLine1));
1211  if (aLine2.GetPointCount()>1) aRetval.Insert(std::move(aLine2));
1212  if (aLine3.GetPointCount()>1) aRetval.Insert(std::move(aLine3));
1213  if (aLine4.GetPointCount()>1) aRetval.Insert(std::move(aLine4));
1214  }
1215 
1216  return aRetval.getB2DPolyPolygon();
1217 }
1218 
1220 {
1221  bool bFreeHand(IsFreeHand(meObjectKind));
1222  rStat.SetNoSnap(bFreeHand);
1223  rStat.SetOrtho8Possible();
1224  aPathPolygon.Clear();
1225  mbCreating=true;
1226  bool bMakeStartPoint = true;
1227  SdrView* pView=rStat.GetView();
1228  if (pView!=nullptr && pView->IsUseIncompatiblePathCreateInterface() &&
1230  bMakeStartPoint = false;
1231  }
1233  aPathPolygon[0][0]=rStat.GetStart();
1234  if (bMakeStartPoint) {
1235  aPathPolygon[0][1]=rStat.GetNow();
1236  }
1237  std::unique_ptr<ImpPathCreateUser> pU(new ImpPathCreateUser);
1238  pU->eStartKind=meObjectKind;
1239  pU->eCurrentKind=meObjectKind;
1240  rStat.SetUser(std::move(pU));
1241 }
1242 
1244 {
1245  ImpPathCreateUser* pU=static_cast<ImpPathCreateUser*>(rStat.GetUser());
1246  SdrView* pView=rStat.GetView();
1247  XPolygon& rXPoly=aPathPolygon[aPathPolygon.Count()-1];
1248  if (pView!=nullptr && pView->IsCreateMode()) {
1249  // switch to different CreateTool, if appropriate
1250  sal_uInt16 nIdent;
1251  SdrInventor nInvent;
1252  pView->TakeCurrentObj(nIdent,nInvent);
1253  if (nInvent==SdrInventor::Default && pU->eCurrentKind!=static_cast<SdrObjKind>(nIdent)) {
1254  SdrObjKind eNewKind=static_cast<SdrObjKind>(nIdent);
1255  switch (eNewKind) {
1256  case OBJ_CARC:
1257  case OBJ_CIRC:
1258  case OBJ_CCUT:
1259  case OBJ_SECT:
1260  eNewKind=OBJ_CARC;
1261  [[fallthrough]];
1262  case OBJ_RECT:
1263  case OBJ_LINE:
1264  case OBJ_PLIN:
1265  case OBJ_POLY:
1266  case OBJ_PATHLINE:
1267  case OBJ_PATHFILL:
1268  case OBJ_FREELINE:
1269  case OBJ_FREEFILL:
1270  case OBJ_SPLNLINE:
1271  case OBJ_SPLNFILL: {
1272  pU->eCurrentKind=eNewKind;
1273  pU->bMixedCreate=true;
1274  pU->nBezierStartPoint=rXPoly.GetPointCount();
1275  if (pU->nBezierStartPoint>0) pU->nBezierStartPoint--;
1276  } break;
1277  default: break;
1278  } // switch
1279  }
1280  }
1281  sal_uInt16 nCurrentPoint=rXPoly.GetPointCount();
1282  if (aPathPolygon.Count()>1 && rStat.IsMouseDown() && nCurrentPoint<2) {
1283  rXPoly[0]=rStat.GetPos0();
1284  rXPoly[1]=rStat.GetNow();
1285  nCurrentPoint=2;
1286  }
1287  if (nCurrentPoint==0) {
1288  rXPoly[0]=rStat.GetPos0();
1289  } else nCurrentPoint--;
1290  bool bFreeHand=IsFreeHand(pU->eCurrentKind);
1291  rStat.SetNoSnap(bFreeHand);
1292  rStat.SetOrtho8Possible(pU->eCurrentKind!=OBJ_CARC && pU->eCurrentKind!=OBJ_RECT && (!pU->bMixedCreate || pU->eCurrentKind!=OBJ_LINE));
1293  rXPoly[nCurrentPoint]=rStat.GetNow();
1294  if (!pU->bMixedCreate && pU->eStartKind==OBJ_LINE && rXPoly.GetPointCount()>=1) {
1295  Point aPt(rStat.GetStart());
1296  if (pView!=nullptr && pView->IsCreate1stPointAsCenter()) {
1297  aPt+=aPt;
1298  aPt-=rStat.GetNow();
1299  }
1300  rXPoly[0]=aPt;
1301  }
1302  OutputDevice* pOut=pView==nullptr ? nullptr : pView->GetFirstOutputDevice();
1303  if (bFreeHand) {
1304  if (pU->nBezierStartPoint>nCurrentPoint) pU->nBezierStartPoint=nCurrentPoint;
1305  if (rStat.IsMouseDown() && nCurrentPoint>0) {
1306  // don't allow two consecutive points to occupy too similar positions
1307  long nMinDist=1;
1308  if (pView!=nullptr) nMinDist=pView->GetFreeHandMinDistPix();
1309  if (pOut!=nullptr) nMinDist=pOut->PixelToLogic(Size(nMinDist,0)).Width();
1310  if (nMinDist<1) nMinDist=1;
1311 
1312  Point aPt0(rXPoly[nCurrentPoint-1]);
1313  Point aPt1(rStat.GetNow());
1314  long dx=aPt0.X()-aPt1.X(); if (dx<0) dx=-dx;
1315  long dy=aPt0.Y()-aPt1.Y(); if (dy<0) dy=-dy;
1316  if (dx<nMinDist && dy<nMinDist) return false;
1317 
1318  // TODO: the following is copied from EndCreate (with a few smaller modifications)
1319  // and should be combined into a method with the code there.
1320 
1321  if (nCurrentPoint-pU->nBezierStartPoint>=3 && ((nCurrentPoint-pU->nBezierStartPoint)%3)==0) {
1322  rXPoly.PointsToBezier(nCurrentPoint-3);
1323  rXPoly.SetFlags(nCurrentPoint-1,PolyFlags::Control);
1324  rXPoly.SetFlags(nCurrentPoint-2,PolyFlags::Control);
1325 
1326  if (nCurrentPoint>=6 && rXPoly.IsControl(nCurrentPoint-4)) {
1327  rXPoly.CalcTangent(nCurrentPoint-3,nCurrentPoint-4,nCurrentPoint-2);
1328  rXPoly.SetFlags(nCurrentPoint-3,PolyFlags::Smooth);
1329  }
1330  }
1331  rXPoly[nCurrentPoint+1]=rStat.GetNow();
1332  rStat.NextPoint();
1333  } else {
1334  pU->nBezierStartPoint=nCurrentPoint;
1335  }
1336  }
1337 
1338  pU->ResetFormFlags();
1339  if (IsBezier(pU->eCurrentKind)) {
1340  if (nCurrentPoint>=2) {
1341  pU->CalcBezier(rXPoly[nCurrentPoint-1],rXPoly[nCurrentPoint],rXPoly[nCurrentPoint-1]-rXPoly[nCurrentPoint-2],rStat.IsMouseDown());
1342  } else if (pU->bBezHasCtrl0) {
1343  pU->CalcBezier(rXPoly[nCurrentPoint-1],rXPoly[nCurrentPoint],pU->aBezControl0-rXPoly[nCurrentPoint-1],rStat.IsMouseDown());
1344  }
1345  }
1346  if (pU->eCurrentKind==OBJ_CARC && nCurrentPoint>=2) {
1347  pU->CalcCircle(rXPoly[nCurrentPoint-1],rXPoly[nCurrentPoint],rXPoly[nCurrentPoint-1]-rXPoly[nCurrentPoint-2],pView);
1348  }
1349  if (pU->eCurrentKind==OBJ_LINE && nCurrentPoint>=2) {
1350  pU->CalcLine(rXPoly[nCurrentPoint-1],rXPoly[nCurrentPoint],rXPoly[nCurrentPoint-1]-rXPoly[nCurrentPoint-2],pView);
1351  }
1352  if (pU->eCurrentKind==OBJ_RECT && nCurrentPoint>=2) {
1353  pU->CalcRect(rXPoly[nCurrentPoint-1],rXPoly[nCurrentPoint],rXPoly[nCurrentPoint-1]-rXPoly[nCurrentPoint-2],pView);
1354  }
1355 
1356  return true;
1357 }
1358 
1360 {
1361  ImpPathCreateUser* pU=static_cast<ImpPathCreateUser*>(rStat.GetUser());
1362  bool bRet = false;
1363  SdrView* pView=rStat.GetView();
1364  bool bIncomp=pView!=nullptr && pView->IsUseIncompatiblePathCreateInterface();
1365  XPolygon& rXPoly=aPathPolygon[aPathPolygon.Count()-1];
1366  sal_uInt16 nCurrentPoint=rXPoly.GetPointCount()-1;
1367  rXPoly[nCurrentPoint]=rStat.GetNow();
1368  if (!pU->bMixedCreate && pU->eStartKind==OBJ_LINE) {
1369  if (rStat.GetPointCount()>=2) eCmd=SdrCreateCmd::ForceEnd;
1370  bRet = eCmd==SdrCreateCmd::ForceEnd;
1371  if (bRet) {
1372  mbCreating = false;
1373  rStat.SetUser(nullptr);
1374  }
1375  return bRet;
1376  }
1377 
1378  if (!pU->bMixedCreate && IsFreeHand(pU->eStartKind)) {
1379  if (rStat.GetPointCount()>=2) eCmd=SdrCreateCmd::ForceEnd;
1380  bRet=eCmd==SdrCreateCmd::ForceEnd;
1381  if (bRet) {
1382  mbCreating=false;
1383  rStat.SetUser(nullptr);
1384  }
1385  return bRet;
1386  }
1388  // don't allow two consecutive points to occupy the same position
1389  if (nCurrentPoint==0 || rStat.GetNow()!=rXPoly[nCurrentPoint-1]) {
1390  if (bIncomp) {
1391  if (pU->nBezierStartPoint>nCurrentPoint) pU->nBezierStartPoint=nCurrentPoint;
1392  if (IsBezier(pU->eCurrentKind) && nCurrentPoint-pU->nBezierStartPoint>=3 && ((nCurrentPoint-pU->nBezierStartPoint)%3)==0) {
1393  rXPoly.PointsToBezier(nCurrentPoint-3);
1394  rXPoly.SetFlags(nCurrentPoint-1,PolyFlags::Control);
1395  rXPoly.SetFlags(nCurrentPoint-2,PolyFlags::Control);
1396 
1397  if (nCurrentPoint>=6 && rXPoly.IsControl(nCurrentPoint-4)) {
1398  rXPoly.CalcTangent(nCurrentPoint-3,nCurrentPoint-4,nCurrentPoint-2);
1399  rXPoly.SetFlags(nCurrentPoint-3,PolyFlags::Smooth);
1400  }
1401  }
1402  } else {
1403  if (nCurrentPoint==1 && IsBezier(pU->eCurrentKind) && !pU->bBezHasCtrl0) {
1404  pU->aBezControl0=rStat.GetNow();
1405  pU->bBezHasCtrl0=true;
1406  nCurrentPoint--;
1407  }
1408  if (pU->IsFormFlag()) {
1409  sal_uInt16 nPointCount0=rXPoly.GetPointCount();
1410  rXPoly.Remove(nCurrentPoint-1,2); // remove last two points and replace by form
1411  rXPoly.Insert(XPOLY_APPEND,pU->GetFormPoly());
1412  sal_uInt16 nPointCount1=rXPoly.GetPointCount();
1413  for (sal_uInt16 i=nPointCount0+1; i<nPointCount1-1; i++) { // to make BckAction work
1414  if (!rXPoly.IsControl(i)) rStat.NextPoint();
1415  }
1416  nCurrentPoint=rXPoly.GetPointCount()-1;
1417  }
1418  }
1419  nCurrentPoint++;
1420  rXPoly[nCurrentPoint]=rStat.GetNow();
1421  }
1422  if (eCmd==SdrCreateCmd::NextObject) {
1423  if (rXPoly.GetPointCount()>=2) {
1424  pU->bBezHasCtrl0=false;
1425  // only a singular polygon may be opened, so close this
1426  rXPoly[nCurrentPoint]=rXPoly[0];
1427  XPolygon aXP;
1428  aXP[0]=rStat.GetNow();
1429  aPathPolygon.Insert(std::move(aXP));
1430  }
1431  }
1432  }
1433 
1434  sal_uInt16 nPolyCount=aPathPolygon.Count();
1435  if (nPolyCount!=0) {
1436  // delete last point, if necessary
1437  if (eCmd==SdrCreateCmd::ForceEnd) {
1438  XPolygon& rXP=aPathPolygon[nPolyCount-1];
1439  sal_uInt16 nPointCount=rXP.GetPointCount();
1440  if (nPointCount>=2) {
1441  if (!rXP.IsControl(nPointCount-2)) {
1442  if (rXP[nPointCount-1]==rXP[nPointCount-2]) {
1443  rXP.Remove(nPointCount-1,1);
1444  }
1445  } else {
1446  if (rXP[nPointCount-3]==rXP[nPointCount-2]) {
1447  rXP.Remove(nPointCount-3,3);
1448  }
1449  }
1450  }
1451  }
1452  for (sal_uInt16 nPolyNum=nPolyCount; nPolyNum>0;) {
1453  nPolyNum--;
1454  XPolygon& rXP=aPathPolygon[nPolyNum];
1455  sal_uInt16 nPointCount=rXP.GetPointCount();
1456  // delete polygons with too few points
1457  if (nPolyNum<nPolyCount-1 || eCmd==SdrCreateCmd::ForceEnd) {
1458  if (nPointCount<2) aPathPolygon.Remove(nPolyNum);
1459  }
1460  }
1461  }
1462  pU->ResetFormFlags();
1463  bRet=eCmd==SdrCreateCmd::ForceEnd;
1464  if (bRet) {
1465  mbCreating=false;
1466  rStat.SetUser(nullptr);
1467  }
1468  return bRet;
1469 }
1470 
1472 {
1473  ImpPathCreateUser* pU=static_cast<ImpPathCreateUser*>(rStat.GetUser());
1474  if (aPathPolygon.Count()>0) {
1475  XPolygon& rXPoly=aPathPolygon[aPathPolygon.Count()-1];
1476  sal_uInt16 nCurrentPoint=rXPoly.GetPointCount();
1477  if (nCurrentPoint>0) {
1478  nCurrentPoint--;
1479  // make the last part of a bezier curve a line
1480  rXPoly.Remove(nCurrentPoint,1);
1481  if (nCurrentPoint>=3 && rXPoly.IsControl(nCurrentPoint-1)) {
1482  // there should never be a bezier segment at the end, so this is just in case...
1483  rXPoly.Remove(nCurrentPoint-1,1);
1484  if (rXPoly.IsControl(nCurrentPoint-2)) rXPoly.Remove(nCurrentPoint-2,1);
1485  }
1486  }
1487  nCurrentPoint=rXPoly.GetPointCount();
1488  if (nCurrentPoint>=4) { // no bezier segment at the end
1489  nCurrentPoint--;
1490  if (rXPoly.IsControl(nCurrentPoint-1)) {
1491  rXPoly.Remove(nCurrentPoint-1,1);
1492  if (rXPoly.IsControl(nCurrentPoint-2)) rXPoly.Remove(nCurrentPoint-2,1);
1493  }
1494  }
1495  if (rXPoly.GetPointCount()<2) {
1497  }
1498  if (aPathPolygon.Count()>0) {
1499  XPolygon& rLocalXPoly=aPathPolygon[aPathPolygon.Count()-1];
1500  sal_uInt16 nLocalCurrentPoint=rLocalXPoly.GetPointCount();
1501  if (nLocalCurrentPoint>0) {
1502  nLocalCurrentPoint--;
1503  rLocalXPoly[nLocalCurrentPoint]=rStat.GetNow();
1504  }
1505  }
1506  }
1507  pU->ResetFormFlags();
1508  return aPathPolygon.Count()!=0;
1509 }
1510 
1512 {
1513  aPathPolygon.Clear();
1514  mbCreating=false;
1515  rStat.SetUser(nullptr);
1516 }
1517 
1519 {
1521  SdrView* pView = rDrag.GetView();
1522 
1523  if(pView && pView->IsUseIncompatiblePathCreateInterface())
1524  return aRetval;
1525 
1526  ImpPathCreateUser* pU = static_cast<ImpPathCreateUser*>(rDrag.GetUser());
1527  basegfx::B2DPolygon aNewPolygon(aRetval.count() ? aRetval.getB2DPolygon(aRetval.count() - 1) : basegfx::B2DPolygon());
1528 
1529  if(pU->IsFormFlag() && aNewPolygon.count() > 1)
1530  {
1531  // remove last segment and replace with current
1532  // do not forget to rescue the previous control point which will be lost when
1533  // the point it's associated with is removed
1534  const sal_uInt32 nChangeIndex(aNewPolygon.count() - 2);
1535  const basegfx::B2DPoint aSavedPrevCtrlPoint(aNewPolygon.getPrevControlPoint(nChangeIndex));
1536 
1537  aNewPolygon.remove(nChangeIndex, 2);
1538  aNewPolygon.append(pU->GetFormPoly().getB2DPolygon());
1539 
1540  if(nChangeIndex < aNewPolygon.count())
1541  {
1542  // if really something was added, set the saved previous control point to the
1543  // point where it belongs
1544  aNewPolygon.setPrevControlPoint(nChangeIndex, aSavedPrevCtrlPoint);
1545  }
1546  }
1547 
1548  if(aRetval.count())
1549  {
1550  aRetval.setB2DPolygon(aRetval.count() - 1, aNewPolygon);
1551  }
1552  else
1553  {
1554  aRetval.append(aNewPolygon);
1555  }
1556 
1557  return aRetval;
1558 }
1559 
1561 {
1562  basegfx::B2DPolyPolygon aRetval;
1563  SdrView* pView = rDrag.GetView();
1564 
1565  if(pView && pView->IsUseIncompatiblePathCreateInterface())
1566  return aRetval;
1567 
1568  const ImpPathCreateUser* pU = static_cast<const ImpPathCreateUser*>(rDrag.GetUser());
1569 
1570  if(pU && pU->bBezier && rDrag.IsMouseDown())
1571  {
1572  // no more XOR, no need for complicated helplines
1573  basegfx::B2DPolygon aHelpline;
1574  aHelpline.append(basegfx::B2DPoint(pU->aBezCtrl2.X(), pU->aBezCtrl2.Y()));
1575  aHelpline.append(basegfx::B2DPoint(pU->aBezEnd.X(), pU->aBezEnd.Y()));
1576  aRetval.append(aHelpline);
1577  }
1578 
1579  return aRetval;
1580 }
1581 
1583 {
1584  switch (meObjectKind) {
1585  case OBJ_LINE : return PointerStyle::DrawLine;
1586  case OBJ_POLY : return PointerStyle::DrawPolygon;
1587  case OBJ_PLIN : return PointerStyle::DrawPolygon;
1588  case OBJ_PATHLINE: return PointerStyle::DrawBezier;
1589  case OBJ_PATHFILL: return PointerStyle::DrawBezier;
1590  case OBJ_FREELINE: return PointerStyle::DrawFreehand;
1591  case OBJ_FREEFILL: return PointerStyle::DrawFreehand;
1592  case OBJ_SPLNLINE: return PointerStyle::DrawFreehand;
1593  case OBJ_SPLNFILL: return PointerStyle::DrawFreehand;
1594  case OBJ_PATHPOLY: return PointerStyle::DrawPolygon;
1595  case OBJ_PATHPLIN: return PointerStyle::DrawPolygon;
1596  default: break;
1597  } // switch
1598  return PointerStyle::Cross;
1599 }
1600 
1602  : meKind(OBJ_NONE)
1603 {
1604 }
1605 
1607 {
1608 }
1609 
1610 // DrawContact section
1611 
1612 std::unique_ptr<sdr::contact::ViewContact> SdrPathObj::CreateObjectSpecificViewContact()
1613 {
1614  return std::make_unique<sdr::contact::ViewContactOfSdrPathObj>(*this);
1615 }
1616 
1617 
1619  SdrModel& rSdrModel,
1620  SdrObjKind eNewKind)
1621 : SdrTextObj(rSdrModel),
1622  meKind(eNewKind)
1623 {
1624  bClosedObj = IsClosed();
1625 }
1626 
1628  SdrModel& rSdrModel,
1629  SdrObjKind eNewKind,
1630  const basegfx::B2DPolyPolygon& rPathPoly)
1631 : SdrTextObj(rSdrModel),
1632  maPathPolygon(rPathPoly),
1633  meKind(eNewKind)
1634 {
1635  bClosedObj = IsClosed();
1636  ImpForceKind();
1637 }
1638 
1639 SdrPathObj::~SdrPathObj() = default;
1640 
1641 static bool lcl_ImpIsLine(const basegfx::B2DPolyPolygon& rPolyPolygon)
1642 {
1643  return (1 == rPolyPolygon.count() && 2 == rPolyPolygon.getB2DPolygon(0).count());
1644 }
1645 
1647 {
1648  basegfx::B2DRange aRange(basegfx::utils::getRange(rPolyPolygon));
1649 
1650  if (aRange.isEmpty())
1651  return tools::Rectangle();
1652 
1653  return tools::Rectangle(
1654  FRound(aRange.getMinX()), FRound(aRange.getMinY()),
1655  FRound(aRange.getMaxX()), FRound(aRange.getMaxY()));
1656 }
1657 
1659 {
1661  return;
1662 
1663  const basegfx::B2DPolygon aPoly(GetPathPoly().getB2DPolygon(0));
1664  const basegfx::B2DPoint aB2DPoint0(aPoly.getB2DPoint(0));
1665  const basegfx::B2DPoint aB2DPoint1(aPoly.getB2DPoint(1));
1666  const Point aPoint0(FRound(aB2DPoint0.getX()), FRound(aB2DPoint0.getY()));
1667  const Point aPoint1(FRound(aB2DPoint1.getX()), FRound(aB2DPoint1.getY()));
1668  const basegfx::B2DPoint aB2DDelt(aB2DPoint1 - aB2DPoint0);
1669  const Point aDelt(FRound(aB2DDelt.getX()), FRound(aB2DDelt.getY()));
1670 
1671  aGeo.nRotationAngle=GetAngle(aDelt);
1672  aGeo.nShearAngle=0;
1673  aGeo.RecalcSinCos();
1674  aGeo.RecalcTan();
1675 
1676  // for SdrTextObj, keep aRect up to date
1677  maRect = tools::Rectangle(aPoint0, aPoint1);
1678  maRect.Justify();
1679 }
1680 
1682 {
1685 
1686  if(GetPathPoly().areControlPointsUsed())
1687  {
1688  switch (meKind)
1689  {
1690  case OBJ_LINE: meKind=OBJ_PATHLINE; break;
1691  case OBJ_PLIN: meKind=OBJ_PATHLINE; break;
1692  case OBJ_POLY: meKind=OBJ_PATHFILL; break;
1693  default: break;
1694  }
1695  }
1696  else
1697  {
1698  switch (meKind)
1699  {
1700  case OBJ_PATHLINE: meKind=OBJ_PLIN; break;
1701  case OBJ_FREELINE: meKind=OBJ_PLIN; break;
1702  case OBJ_PATHFILL: meKind=OBJ_POLY; break;
1703  case OBJ_FREEFILL: meKind=OBJ_POLY; break;
1704  default: break;
1705  }
1706  }
1707 
1710 
1711  bClosedObj=IsClosed();
1712 
1713  if (meKind==OBJ_LINE)
1714  {
1716  }
1717  else
1718  {
1719  // #i10659#, for polys with more than 2 points.
1720 
1721  // Here i again need to fix something, because when Path-Polys are Copy-Pasted
1722  // between Apps with different measurements (e.g. 100TH_MM and TWIPS) there is
1723  // a scaling loop started from SdrExchangeView::Paste. In itself, this is not
1724  // wrong, but aRect is wrong here and not even updated by RecalcSnapRect(). If
1725  // this is the case, some size needs to be set here in aRect to avoid that the cycle
1726  // through Rect2Poly - Poly2Rect does something badly wrong since that cycle is
1727  // BASED on aRect. That cycle is triggered in SdrTextObj::NbcResize() which is called
1728  // from the local Resize() implementation.
1729 
1730  // Basic problem is that the member aRect in SdrTextObj basically is a unrotated
1731  // text rectangle for the text object itself and methods at SdrTextObj do handle it
1732  // in that way. Many draw objects derived from SdrTextObj 'abuse' aRect as SnapRect
1733  // which is basically wrong. To make the SdrText methods which deal with aRect directly
1734  // work it is necessary to always keep aRect updated. This e.g. not done after a Clone()
1735  // command for SdrPathObj. Since adding this update mechanism with #101412# to
1736  // ImpForceLineAngle() for lines was very successful, i add it to where ImpForceLineAngle()
1737  // was called, once here below and once on a 2nd place below.
1738 
1739  // #i10659# for SdrTextObj, keep aRect up to date
1740  if(GetPathPoly().count())
1741  {
1743  }
1744  }
1745 
1746  // #i75974# adapt polygon state to object type. This may include a reinterpretation
1747  // of a closed geometry as open one, but with identical first and last point
1748  for(auto& rPolygon : maPathPolygon)
1749  {
1750  if(IsClosed() != rPolygon.isClosed())
1751  {
1752  // #i80213# really change polygon geometry; else e.g. the last point which
1753  // needs to be identical with the first one will be missing when opening
1754  // due to OBJ_PATH type
1755  if(rPolygon.isClosed())
1756  {
1758  }
1759  else
1760  {
1762  }
1763  }
1764  }
1765 }
1766 
1767 void SdrPathObj::ImpSetClosed(bool bClose)
1768 {
1769  if(bClose)
1770  {
1771  switch (meKind)
1772  {
1773  case OBJ_LINE : meKind=OBJ_POLY; break;
1774  case OBJ_PLIN : meKind=OBJ_POLY; break;
1775  case OBJ_PATHLINE: meKind=OBJ_PATHFILL; break;
1776  case OBJ_FREELINE: meKind=OBJ_FREEFILL; break;
1777  case OBJ_SPLNLINE: meKind=OBJ_SPLNFILL; break;
1778  default: break;
1779  }
1780 
1781  bClosedObj = true;
1782  }
1783  else
1784  {
1785  switch (meKind)
1786  {
1787  case OBJ_POLY : meKind=OBJ_PLIN; break;
1788  case OBJ_PATHFILL: meKind=OBJ_PATHLINE; break;
1789  case OBJ_FREEFILL: meKind=OBJ_FREELINE; break;
1790  case OBJ_SPLNFILL: meKind=OBJ_SPLNLINE; break;
1791  default: break;
1792  }
1793 
1794  bClosedObj = false;
1795  }
1796 
1797  ImpForceKind();
1798 }
1799 
1801 {
1802  rInfo.bNoContortion=false;
1803 
1804  bool bCanConv = !HasText() || ImpCanConvTextToCurve();
1805  bool bIsPath = IsBezier() || IsSpline();
1806 
1807  rInfo.bEdgeRadiusAllowed = false;
1808  rInfo.bCanConvToPath = bCanConv && !bIsPath;
1809  rInfo.bCanConvToPoly = bCanConv && bIsPath;
1811 }
1812 
1814 {
1815  return sal_uInt16(meKind);
1816 }
1817 
1819 {
1820  return CloneHelper< SdrPathObj >(rTargetModel);
1821 }
1822 
1824 {
1825  if( this == &rObj )
1826  return *this;
1827  SdrTextObj::operator=(rObj);
1828  maPathPolygon=rObj.GetPathPoly();
1829  return *this;
1830 }
1831 
1833 {
1834  OUStringBuffer sName;
1835 
1836  if(OBJ_LINE == meKind)
1837  {
1838  const char* pId(STR_ObjNameSingulLINE);
1839 
1840  if(lcl_ImpIsLine(GetPathPoly()))
1841  {
1842  const basegfx::B2DPolygon aPoly(GetPathPoly().getB2DPolygon(0));
1843  const basegfx::B2DPoint aB2DPoint0(aPoly.getB2DPoint(0));
1844  const basegfx::B2DPoint aB2DPoint1(aPoly.getB2DPoint(1));
1845 
1846  if(aB2DPoint0 != aB2DPoint1)
1847  {
1848  if(aB2DPoint0.getY() == aB2DPoint1.getY())
1849  {
1850  pId = STR_ObjNameSingulLINE_Hori;
1851  }
1852  else if(aB2DPoint0.getX() == aB2DPoint1.getX())
1853  {
1854  pId = STR_ObjNameSingulLINE_Vert;
1855  }
1856  else
1857  {
1858  const double fDx(fabs(aB2DPoint0.getX() - aB2DPoint1.getX()));
1859  const double fDy(fabs(aB2DPoint0.getY() - aB2DPoint1.getY()));
1860 
1861  if(fDx == fDy)
1862  {
1863  pId = STR_ObjNameSingulLINE_Diag;
1864  }
1865  }
1866  }
1867  }
1868 
1869  sName.append(SvxResId(pId));
1870  }
1871  else if(OBJ_PLIN == meKind || OBJ_POLY == meKind)
1872  {
1873  const bool bClosed(OBJ_POLY == meKind);
1874  const char* pId(nullptr);
1875 
1876  if(mpDAC && mpDAC->IsCreating())
1877  {
1878  if(bClosed)
1879  {
1880  pId = STR_ObjNameSingulPOLY;
1881  }
1882  else
1883  {
1884  pId = STR_ObjNameSingulPLIN;
1885  }
1886 
1887  sName.append(SvxResId(pId));
1888  }
1889  else
1890  {
1891  // get point count
1892  sal_uInt32 nPointCount(0);
1893 
1894  for(auto const& rPolygon : GetPathPoly())
1895  {
1896  nPointCount += rPolygon.count();
1897  }
1898 
1899  if(bClosed)
1900  {
1901  pId = STR_ObjNameSingulPOLY_PointCount;
1902  }
1903  else
1904  {
1905  pId = STR_ObjNameSingulPLIN_PointCount;
1906  }
1907 
1908  OUString sTemp(SvxResId(pId));
1909  // #i96537#
1910  sName.append(sTemp.replaceFirst("%2", OUString::number(nPointCount)));
1911  }
1912  }
1913  else
1914  {
1915  switch (meKind)
1916  {
1917  case OBJ_PATHLINE: sName.append(SvxResId(STR_ObjNameSingulPATHLINE)); break;
1918  case OBJ_FREELINE: sName.append(SvxResId(STR_ObjNameSingulFREELINE)); break;
1919  case OBJ_SPLNLINE: sName.append(SvxResId(STR_ObjNameSingulNATSPLN)); break;
1920  case OBJ_PATHFILL: sName.append(SvxResId(STR_ObjNameSingulPATHFILL)); break;
1921  case OBJ_FREEFILL: sName.append(SvxResId(STR_ObjNameSingulFREEFILL)); break;
1922  case OBJ_SPLNFILL: sName.append(SvxResId(STR_ObjNameSingulPERSPLN)); break;
1923  default: break;
1924  }
1925  }
1926 
1927  OUString aName(GetName());
1928  if (!aName.isEmpty())
1929  {
1930  sName.append(' ');
1931  sName.append('\'');
1932  sName.append(aName);
1933  sName.append('\'');
1934  }
1935 
1936  return sName.makeStringAndClear();
1937 }
1938 
1940 {
1941  OUString sName;
1942  switch(meKind)
1943  {
1944  case OBJ_LINE : sName=SvxResId(STR_ObjNamePluralLINE ); break;
1945  case OBJ_PLIN : sName=SvxResId(STR_ObjNamePluralPLIN ); break;
1946  case OBJ_POLY : sName=SvxResId(STR_ObjNamePluralPOLY ); break;
1947  case OBJ_PATHLINE: sName=SvxResId(STR_ObjNamePluralPATHLINE); break;
1948  case OBJ_FREELINE: sName=SvxResId(STR_ObjNamePluralFREELINE); break;
1949  case OBJ_SPLNLINE: sName=SvxResId(STR_ObjNamePluralNATSPLN); break;
1950  case OBJ_PATHFILL: sName=SvxResId(STR_ObjNamePluralPATHFILL); break;
1951  case OBJ_FREEFILL: sName=SvxResId(STR_ObjNamePluralFREEFILL); break;
1952  case OBJ_SPLNFILL: sName=SvxResId(STR_ObjNamePluralPERSPLN); break;
1953  default: break;
1954  }
1955  return sName;
1956 }
1957 
1959 {
1960  return GetPathPoly();
1961 }
1962 
1963 sal_uInt32 SdrPathObj::GetHdlCount() const
1964 {
1965  sal_uInt32 nRetval(0);
1966 
1967  for(auto const& rPolygon : GetPathPoly())
1968  {
1969  nRetval += rPolygon.count();
1970  }
1971 
1972  return nRetval;
1973 }
1974 
1976 {
1977  // keep old stuff to be able to keep old SdrHdl stuff, too
1978  const XPolyPolygon aOldPathPolygon(GetPathPoly());
1979  sal_uInt16 nPolyCnt=aOldPathPolygon.Count();
1980  bool bClosed=IsClosed();
1981  sal_uInt16 nIdx=0;
1982 
1983  for (sal_uInt16 i=0; i<nPolyCnt; i++) {
1984  const XPolygon& rXPoly=aOldPathPolygon.GetObject(i);
1985  sal_uInt16 nPntCnt=rXPoly.GetPointCount();
1986  if (bClosed && nPntCnt>1) nPntCnt--;
1987 
1988  for (sal_uInt16 j=0; j<nPntCnt; j++) {
1989  if (rXPoly.GetFlags(j)!=PolyFlags::Control) {
1990  const Point& rPnt=rXPoly[j];
1991  std::unique_ptr<SdrHdl> pHdl(new SdrHdl(rPnt,SdrHdlKind::Poly));
1992  pHdl->SetPolyNum(i);
1993  pHdl->SetPointNum(j);
1994  pHdl->Set1PixMore(j==0);
1995  pHdl->SetSourceHdlNum(nIdx);
1996  nIdx++;
1997  rHdlList.AddHdl(std::move(pHdl));
1998  }
1999  }
2000  }
2001 }
2002 
2003 void SdrPathObj::AddToPlusHdlList(SdrHdlList& rHdlList, SdrHdl& rHdl) const
2004 {
2005  // keep old stuff to be able to keep old SdrHdl stuff, too
2006  const XPolyPolygon aOldPathPolygon(GetPathPoly());
2007  sal_uInt16 nPnt = static_cast<sal_uInt16>(rHdl.GetPointNum());
2008  sal_uInt16 nPolyNum = static_cast<sal_uInt16>(rHdl.GetPolyNum());
2009 
2010  if (nPolyNum>=aOldPathPolygon.Count())
2011  return;
2012 
2013  const XPolygon& rXPoly = aOldPathPolygon[nPolyNum];
2014  sal_uInt16 nPntMax = rXPoly.GetPointCount();
2015 
2016  if (nPntMax<=0)
2017  return;
2018  nPntMax--;
2019  if (nPnt>nPntMax)
2020  return;
2021 
2022  // calculate the number of plus points
2023  sal_uInt16 nCnt = 0;
2024  if (rXPoly.GetFlags(nPnt)!=PolyFlags::Control)
2025  {
2026  if (nPnt==0 && IsClosed())
2027  nPnt=nPntMax;
2028  if (nPnt>0 && rXPoly.GetFlags(nPnt-1)==PolyFlags::Control)
2029  nCnt++;
2030  if (nPnt==nPntMax && IsClosed())
2031  nPnt=0;
2032  if (nPnt<nPntMax && rXPoly.GetFlags(nPnt+1)==PolyFlags::Control)
2033  nCnt++;
2034  }
2035 
2036  // construct the plus points
2037  for (sal_uInt32 nPlusNum = 0; nPlusNum < nCnt; ++nPlusNum)
2038  {
2039  nPnt = static_cast<sal_uInt16>(rHdl.GetPointNum());
2040  std::unique_ptr<SdrHdl> pHdl(new SdrHdlBezWgt(&rHdl));
2041  pHdl->SetPolyNum(rHdl.GetPolyNum());
2042 
2043  if (nPnt==0 && IsClosed())
2044  nPnt=nPntMax;
2045  if (nPnt>0 && rXPoly.GetFlags(nPnt-1)==PolyFlags::Control && nPlusNum==0)
2046  {
2047  pHdl->SetPos(rXPoly[nPnt-1]);
2048  pHdl->SetPointNum(nPnt-1);
2049  }
2050  else
2051  {
2052  if (nPnt==nPntMax && IsClosed())
2053  nPnt=0;
2054  if (nPnt<rXPoly.GetPointCount()-1 && rXPoly.GetFlags(nPnt+1)==PolyFlags::Control)
2055  {
2056  pHdl->SetPos(rXPoly[nPnt+1]);
2057  pHdl->SetPointNum(nPnt+1);
2058  }
2059  }
2060 
2061  pHdl->SetSourceHdlNum(rHdl.GetSourceHdlNum());
2062  pHdl->SetPlusHdl(true);
2063  rHdlList.AddHdl(std::move(pHdl));
2064  }
2065 }
2066 
2067 // dragging
2068 
2070 {
2071  return true;
2072 }
2073 
2075 {
2076  ImpPathForDragAndCreate aDragAndCreate(*const_cast<SdrPathObj*>(this));
2077 
2078  return aDragAndCreate.beginPathDrag(rDrag);
2079 }
2080 
2082 {
2083  ImpPathForDragAndCreate aDragAndCreate(*this);
2084  bool bRetval(aDragAndCreate.beginPathDrag(rDrag));
2085 
2086  if(bRetval)
2087  {
2088  bRetval = aDragAndCreate.movePathDrag(rDrag);
2089  }
2090 
2091  if(bRetval)
2092  {
2093  bRetval = aDragAndCreate.endPathDrag(rDrag);
2094  }
2095 
2096  if(bRetval)
2097  {
2098  NbcSetPathPoly(aDragAndCreate.getModifiedPolyPolygon());
2099  }
2100 
2101  return bRetval;
2102 }
2103 
2105 {
2106  OUString aRetval;
2107 
2108  if(mpDAC)
2109  {
2110  // #i103058# also get a comment when in creation
2111  const bool bCreateComment(rDrag.GetView() && this == rDrag.GetView()->GetCreateObj());
2112 
2113  if(bCreateComment)
2114  {
2115  aRetval = mpDAC->getSpecialDragComment(rDrag);
2116  }
2117  }
2118  else
2119  {
2120  ImpPathForDragAndCreate aDragAndCreate(*const_cast<SdrPathObj*>(this));
2121  bool bDidWork(aDragAndCreate.beginPathDrag(rDrag));
2122 
2123  if(bDidWork)
2124  {
2125  aRetval = aDragAndCreate.getSpecialDragComment(rDrag);
2126  }
2127  }
2128 
2129  return aRetval;
2130 }
2131 
2133 {
2134  basegfx::B2DPolyPolygon aRetval;
2135  ImpPathForDragAndCreate aDragAndCreate(*const_cast<SdrPathObj*>(this));
2136  bool bDidWork(aDragAndCreate.beginPathDrag(rDrag));
2137 
2138  if(bDidWork)
2139  {
2140  aRetval = aDragAndCreate.getSpecialDragPoly(rDrag);
2141  }
2142 
2143  return aRetval;
2144 }
2145 
2146 // creation
2147 
2149 {
2150  mpDAC.reset();
2151  impGetDAC().BegCreate(rStat);
2152  return true;
2153 }
2154 
2156 {
2157  return impGetDAC().MovCreate(rStat);
2158 }
2159 
2161 {
2162  bool bRetval(impGetDAC().EndCreate(rStat, eCmd));
2163 
2164  if(bRetval && mpDAC)
2165  {
2166  SetPathPoly(mpDAC->getModifiedPolyPolygon());
2167 
2168  // #i75974# Check for AutoClose feature. Moved here from ImpPathForDragAndCreate::EndCreate
2169  // to be able to use the type-changing ImpSetClosed method
2170  if(!IsClosedObj())
2171  {
2172  SdrView* pView = rStat.GetView();
2173 
2174  if(pView && !pView->IsUseIncompatiblePathCreateInterface())
2175  {
2177 
2178  if(pOut)
2179  {
2180  if(GetPathPoly().count())
2181  {
2182  const basegfx::B2DPolygon aCandidate(GetPathPoly().getB2DPolygon(0));
2183 
2184  if(aCandidate.count() > 2)
2185  {
2186  // check distance of first and last point
2187  const sal_Int32 nCloseDist(pOut->PixelToLogic(Size(pView->GetAutoCloseDistPix(), 0)).Width());
2188  const basegfx::B2DVector aDistVector(aCandidate.getB2DPoint(aCandidate.count() - 1) - aCandidate.getB2DPoint(0));
2189 
2190  if(aDistVector.getLength() <= static_cast<double>(nCloseDist))
2191  {
2192  // close it
2193  ImpSetClosed(true);
2194  }
2195  }
2196  }
2197  }
2198  }
2199  }
2200 
2201  mpDAC.reset();
2202  }
2203 
2204  return bRetval;
2205 }
2206 
2208 {
2209  return impGetDAC().BckCreate(rStat);
2210 }
2211 
2213 {
2214  impGetDAC().BrkCreate(rStat);
2215  mpDAC.reset();
2216 }
2217 
2218 // polygons
2219 
2221 {
2222  basegfx::B2DPolyPolygon aRetval;
2223 
2224  if(mpDAC)
2225  {
2226  aRetval = mpDAC->TakeObjectPolyPolygon(rDrag);
2228  }
2229 
2230  return aRetval;
2231 }
2232 
2233 // during drag or create, allow accessing the so-far created/modified polyPolygon
2235 {
2236  basegfx::B2DPolyPolygon aRetval;
2237 
2238  if(mpDAC)
2239  {
2240  aRetval = mpDAC->TakeObjectPolyPolygon(rDrag);
2241  }
2242 
2243  return aRetval;
2244 }
2245 
2247 {
2248  basegfx::B2DPolyPolygon aRetval;
2249 
2250  if(mpDAC)
2251  {
2253  }
2254 
2255  return aRetval;
2256 }
2257 
2259 {
2260  return impGetDAC().GetCreatePointer();
2261 }
2262 
2263 void SdrPathObj::NbcMove(const Size& rSiz)
2264 {
2266 
2267  // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
2268  SdrTextObj::NbcMove(rSiz);
2269 }
2270 
2271 void SdrPathObj::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact)
2272 {
2273  const double fResizeX(xFact);
2274  const double fResizeY(yFact);
2275 
2276  if(basegfx::fTools::equal(fResizeX, 1.0) && basegfx::fTools::equal(fResizeY, 1.0))
2277  {
2278  // tdf#106792 avoid numerical unprecisions: If both scale factors are 1.0, do not
2279  // manipulate at all - that may change aGeo rapidly (and wrongly) in
2280  // SdrTextObj::NbcResize. Combined with the UNO API trying to not 'apply'
2281  // a rotation but to manipulate the existing one, this is fatal. So just
2282  // avoid this error as long as we have to deal with imprecise geometry
2283  // manipulations
2284  return;
2285  }
2286 
2289  double(xFact), double(yFact), rRef.X(), rRef.Y()) * aTrans;
2290  maPathPolygon.transform(aTrans);
2291 
2292  // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
2293  SdrTextObj::NbcResize(rRef,xFact,yFact);
2294 }
2295 
2296 void SdrPathObj::NbcRotate(const Point& rRef, long nAngle, double sn, double cs)
2297 {
2298  // Thank JOE, the angles are defined mirrored to the mathematical meanings
2299  const basegfx::B2DHomMatrix aTrans(
2300  basegfx::utils::createRotateAroundPoint(rRef.X(), rRef.Y(), -nAngle * F_PI18000));
2301  maPathPolygon.transform(aTrans);
2302 
2303  // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
2304  SdrTextObj::NbcRotate(rRef,nAngle,sn,cs);
2305 }
2306 
2307 void SdrPathObj::NbcShear(const Point& rRefPnt, long nAngle, double fTan, bool bVShear)
2308 {
2309  basegfx::B2DHomMatrix aTrans(basegfx::utils::createTranslateB2DHomMatrix(-rRefPnt.X(), -rRefPnt.Y()));
2310 
2311  if(bVShear)
2312  {
2313  // Thank JOE, the angles are defined mirrored to the mathematical meanings
2314  aTrans.shearY(-fTan);
2315  }
2316  else
2317  {
2318  aTrans.shearX(-fTan);
2319  }
2320 
2321  aTrans.translate(rRefPnt.X(), rRefPnt.Y());
2322  maPathPolygon.transform(aTrans);
2323 
2324  // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
2325  SdrTextObj::NbcShear(rRefPnt,nAngle,fTan,bVShear);
2326 }
2327 
2328 void SdrPathObj::NbcMirror(const Point& rRefPnt1, const Point& rRefPnt2)
2329 {
2330  const double fDiffX(rRefPnt2.X() - rRefPnt1.X());
2331  const double fDiffY(rRefPnt2.Y() - rRefPnt1.Y());
2332  const double fRot(atan2(fDiffY, fDiffX));
2333  basegfx::B2DHomMatrix aTrans(basegfx::utils::createTranslateB2DHomMatrix(-rRefPnt1.X(), -rRefPnt1.Y()));
2334  aTrans.rotate(-fRot);
2335  aTrans.scale(1.0, -1.0);
2336  aTrans.rotate(fRot);
2337  aTrans.translate(rRefPnt1.X(), rRefPnt1.Y());
2338  maPathPolygon.transform(aTrans);
2339 
2340  // Do Joe's special handling for lines when mirroring, too
2341  ImpForceKind();
2342 
2343  // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
2344  SdrTextObj::NbcMirror(rRefPnt1,rRefPnt2);
2345 }
2346 
2348 {
2349  if(!aGeo.nRotationAngle)
2350  {
2351  rRect = GetSnapRect();
2352  }
2353  else
2354  {
2355  XPolyPolygon aXPP(GetPathPoly());
2356  RotateXPoly(aXPP,Point(),-aGeo.nSin,aGeo.nCos);
2357  rRect=aXPP.GetBoundRect();
2358  Point aTmp(rRect.TopLeft());
2359  RotatePoint(aTmp,Point(),aGeo.nSin,aGeo.nCos);
2360  aTmp-=rRect.TopLeft();
2361  rRect.Move(aTmp.X(),aTmp.Y());
2362  }
2363 }
2364 
2366 {
2367  if(GetPathPoly().count())
2368  {
2370  }
2371 }
2372 
2374 {
2375  tools::Rectangle aOld(GetSnapRect());
2376  if (aOld.IsEmpty())
2377  {
2378  Fraction aX(1,1);
2379  Fraction aY(1,1);
2380  NbcResize(aOld.TopLeft(), aX, aY);
2381  NbcMove(Size(rRect.Left() - aOld.Left(), rRect.Top() - aOld.Top()));
2382  return;
2383  }
2384 
2385  // Take empty into account when calculating scale factors
2386  long nMulX = rRect.IsWidthEmpty() ? 0 : rRect.Right() - rRect.Left();
2387 
2388  long nDivX = aOld.Right() - aOld.Left();
2389 
2390  // Take empty into account when calculating scale factors
2391  long nMulY = rRect.IsHeightEmpty() ? 0 : rRect.Bottom() - rRect.Top();
2392 
2393  long nDivY = aOld.Bottom() - aOld.Top();
2394  if ( nDivX == 0 ) { nMulX = 1; nDivX = 1; }
2395  if ( nDivY == 0 ) { nMulY = 1; nDivY = 1; }
2396  if ( nDivX == nMulX ) { nMulX = 1; nDivX = 1; }
2397  if ( nDivY == nMulY ) { nMulY = 1; nDivY = 1; }
2398  Fraction aX(nMulX,nDivX);
2399  Fraction aY(nMulY,nDivY);
2400  NbcResize(aOld.TopLeft(), aX, aY);
2401  NbcMove(Size(rRect.Left() - aOld.Left(), rRect.Top() - aOld.Top()));
2402 }
2403 
2405 {
2406  return GetHdlCount();
2407 }
2408 
2409 Point SdrPathObj::GetSnapPoint(sal_uInt32 nSnapPnt) const
2410 {
2411  sal_uInt32 nPoly,nPnt;
2412  if(!PolyPolygonEditor::GetRelativePolyPoint(GetPathPoly(), nSnapPnt, nPoly, nPnt))
2413  {
2414  SAL_WARN("svx", "SdrPathObj::GetSnapPoint: Point nSnapPnt does not exist.");
2415  }
2416 
2417  const basegfx::B2DPoint aB2DPoint(GetPathPoly().getB2DPolygon(nPoly).getB2DPoint(nPnt));
2418  return Point(FRound(aB2DPoint.getX()), FRound(aB2DPoint.getY()));
2419 }
2420 
2422 {
2423  return true;
2424 }
2425 
2426 sal_uInt32 SdrPathObj::GetPointCount() const
2427 {
2428  sal_uInt32 nRetval(0);
2429 
2430  for(auto const& rPolygon : GetPathPoly())
2431  {
2432  nRetval += rPolygon.count();
2433  }
2434 
2435  return nRetval;
2436 }
2437 
2438 Point SdrPathObj::GetPoint(sal_uInt32 nHdlNum) const
2439 {
2440  Point aRetval;
2441  sal_uInt32 nPoly,nPnt;
2442 
2443  if(PolyPolygonEditor::GetRelativePolyPoint(GetPathPoly(), nHdlNum, nPoly, nPnt))
2444  {
2445  const basegfx::B2DPolygon aPoly(GetPathPoly().getB2DPolygon(nPoly));
2446  const basegfx::B2DPoint aPoint(aPoly.getB2DPoint(nPnt));
2447  aRetval = Point(FRound(aPoint.getX()), FRound(aPoint.getY()));
2448  }
2449 
2450  return aRetval;
2451 }
2452 
2453 void SdrPathObj::NbcSetPoint(const Point& rPnt, sal_uInt32 nHdlNum)
2454 {
2455  sal_uInt32 nPoly,nPnt;
2456 
2457  if(PolyPolygonEditor::GetRelativePolyPoint(GetPathPoly(), nHdlNum, nPoly, nPnt))
2458  {
2459  basegfx::B2DPolygon aNewPolygon(GetPathPoly().getB2DPolygon(nPoly));
2460  aNewPolygon.setB2DPoint(nPnt, basegfx::B2DPoint(rPnt.X(), rPnt.Y()));
2461  maPathPolygon.setB2DPolygon(nPoly, aNewPolygon);
2462 
2463  if(meKind==OBJ_LINE)
2464  {
2466  }
2467  else
2468  {
2469  if(GetPathPoly().count())
2470  {
2471  // #i10659# for SdrTextObj, keep aRect up to date
2473  }
2474  }
2475 
2476  SetRectsDirty();
2477  }
2478 }
2479 
2480 sal_uInt32 SdrPathObj::NbcInsPointOld(const Point& rPos, bool bNewObj)
2481 {
2482  sal_uInt32 nNewHdl;
2483 
2484  if(bNewObj)
2485  {
2486  nNewHdl = NbcInsPoint(rPos, true);
2487  }
2488  else
2489  {
2490  // look for smallest distance data
2491  const basegfx::B2DPoint aTestPoint(rPos.X(), rPos.Y());
2492  sal_uInt32 nSmallestPolyIndex(0);
2493  sal_uInt32 nSmallestEdgeIndex(0);
2494  double fSmallestCut;
2495  basegfx::utils::getSmallestDistancePointToPolyPolygon(GetPathPoly(), aTestPoint, nSmallestPolyIndex, nSmallestEdgeIndex, fSmallestCut);
2496 
2497  nNewHdl = NbcInsPoint(rPos, false);
2498  }
2499 
2500  ImpForceKind();
2501  return nNewHdl;
2502 }
2503 
2504 sal_uInt32 SdrPathObj::NbcInsPoint(const Point& rPos, bool bNewObj)
2505 {
2506  sal_uInt32 nNewHdl;
2507 
2508  if(bNewObj)
2509  {
2510  basegfx::B2DPolygon aNewPoly;
2511  const basegfx::B2DPoint aPoint(rPos.X(), rPos.Y());
2512  aNewPoly.append(aPoint);
2513  aNewPoly.setClosed(IsClosed());
2514  maPathPolygon.append(aNewPoly);
2515  SetRectsDirty();
2516  nNewHdl = GetHdlCount();
2517  }
2518  else
2519  {
2520  // look for smallest distance data
2521  const basegfx::B2DPoint aTestPoint(rPos.X(), rPos.Y());
2522  sal_uInt32 nSmallestPolyIndex(0);
2523  sal_uInt32 nSmallestEdgeIndex(0);
2524  double fSmallestCut;
2525  basegfx::utils::getSmallestDistancePointToPolyPolygon(GetPathPoly(), aTestPoint, nSmallestPolyIndex, nSmallestEdgeIndex, fSmallestCut);
2526  basegfx::B2DPolygon aCandidate(GetPathPoly().getB2DPolygon(nSmallestPolyIndex));
2527  const bool bBefore(!aCandidate.isClosed() && 0 == nSmallestEdgeIndex && 0.0 == fSmallestCut);
2528  const bool bAfter(!aCandidate.isClosed() && aCandidate.count() == nSmallestEdgeIndex + 2 && 1.0 == fSmallestCut);
2529 
2530  if(bBefore)
2531  {
2532  // before first point
2533  aCandidate.insert(0, aTestPoint);
2534 
2535  if(aCandidate.areControlPointsUsed())
2536  {
2537  if(aCandidate.isNextControlPointUsed(1))
2538  {
2539  aCandidate.setNextControlPoint(0, interpolate(aTestPoint, aCandidate.getB2DPoint(1), (1.0 / 3.0)));
2540  aCandidate.setPrevControlPoint(1, interpolate(aTestPoint, aCandidate.getB2DPoint(1), (2.0 / 3.0)));
2541  }
2542  }
2543 
2544  nNewHdl = 0;
2545  }
2546  else if(bAfter)
2547  {
2548  // after last point
2549  aCandidate.append(aTestPoint);
2550 
2551  if(aCandidate.areControlPointsUsed())
2552  {
2553  if(aCandidate.isPrevControlPointUsed(aCandidate.count() - 2))
2554  {
2555  aCandidate.setNextControlPoint(aCandidate.count() - 2, interpolate(aCandidate.getB2DPoint(aCandidate.count() - 2), aTestPoint, (1.0 / 3.0)));
2556  aCandidate.setPrevControlPoint(aCandidate.count() - 1, interpolate(aCandidate.getB2DPoint(aCandidate.count() - 2), aTestPoint, (2.0 / 3.0)));
2557  }
2558  }
2559 
2560  nNewHdl = aCandidate.count() - 1;
2561  }
2562  else
2563  {
2564  // in between
2565  bool bSegmentSplit(false);
2566  const sal_uInt32 nNextIndex((nSmallestEdgeIndex + 1) % aCandidate.count());
2567 
2568  if(aCandidate.areControlPointsUsed())
2569  {
2570  if(aCandidate.isNextControlPointUsed(nSmallestEdgeIndex) || aCandidate.isPrevControlPointUsed(nNextIndex))
2571  {
2572  bSegmentSplit = true;
2573  }
2574  }
2575 
2576  if(bSegmentSplit)
2577  {
2578  // rebuild original segment to get the split data
2579  basegfx::B2DCubicBezier aBezierA, aBezierB;
2580  const basegfx::B2DCubicBezier aBezier(
2581  aCandidate.getB2DPoint(nSmallestEdgeIndex),
2582  aCandidate.getNextControlPoint(nSmallestEdgeIndex),
2583  aCandidate.getPrevControlPoint(nNextIndex),
2584  aCandidate.getB2DPoint(nNextIndex));
2585 
2586  // split and insert hit point
2587  aBezier.split(fSmallestCut, &aBezierA, &aBezierB);
2588  aCandidate.insert(nSmallestEdgeIndex + 1, aTestPoint);
2589 
2590  // since we inserted hit point and not split point, we need to add an offset
2591  // to the control points to get the C1 continuity we want to achieve
2592  const basegfx::B2DVector aOffset(aTestPoint - aBezierA.getEndPoint());
2593  aCandidate.setNextControlPoint(nSmallestEdgeIndex, aBezierA.getControlPointA() + aOffset);
2594  aCandidate.setPrevControlPoint(nSmallestEdgeIndex + 1, aBezierA.getControlPointB() + aOffset);
2595  aCandidate.setNextControlPoint(nSmallestEdgeIndex + 1, aBezierB.getControlPointA() + aOffset);
2596  aCandidate.setPrevControlPoint((nSmallestEdgeIndex + 2) % aCandidate.count(), aBezierB.getControlPointB() + aOffset);
2597  }
2598  else
2599  {
2600  aCandidate.insert(nSmallestEdgeIndex + 1, aTestPoint);
2601  }
2602 
2603  nNewHdl = nSmallestEdgeIndex + 1;
2604  }
2605 
2606  maPathPolygon.setB2DPolygon(nSmallestPolyIndex, aCandidate);
2607 
2608  // create old polygon index from it
2609  for(sal_uInt32 a(0); a < nSmallestPolyIndex; a++)
2610  {
2611  nNewHdl += GetPathPoly().getB2DPolygon(a).count();
2612  }
2613  }
2614 
2615  ImpForceKind();
2616  return nNewHdl;
2617 }
2618 
2619 SdrObject* SdrPathObj::RipPoint(sal_uInt32 nHdlNum, sal_uInt32& rNewPt0Index)
2620 {
2621  SdrPathObj* pNewObj = nullptr;
2622  const basegfx::B2DPolyPolygon aLocalPolyPolygon(GetPathPoly());
2623  sal_uInt32 nPoly, nPnt;
2624 
2625  if(PolyPolygonEditor::GetRelativePolyPoint(aLocalPolyPolygon, nHdlNum, nPoly, nPnt))
2626  {
2627  if(0 == nPoly)
2628  {
2629  const basegfx::B2DPolygon& aCandidate(aLocalPolyPolygon.getB2DPolygon(nPoly));
2630  const sal_uInt32 nPointCount(aCandidate.count());
2631 
2632  if(nPointCount)
2633  {
2634  if(IsClosed())
2635  {
2636  // when closed, RipPoint means to open the polygon at the selected point. To
2637  // be able to do that, it is necessary to make the selected point the first one
2638  basegfx::B2DPolygon aNewPolygon(basegfx::utils::makeStartPoint(aCandidate, nPnt));
2639  SetPathPoly(basegfx::B2DPolyPolygon(aNewPolygon));
2640  ToggleClosed();
2641 
2642  // give back new position of old start point (historical reasons)
2643  rNewPt0Index = (nPointCount - nPnt) % nPointCount;
2644  }
2645  else
2646  {
2647  if(nPointCount >= 3 && nPnt != 0 && nPnt + 1 < nPointCount)
2648  {
2649  // split in two objects at point nPnt
2650  basegfx::B2DPolygon aSplitPolyA(aCandidate, 0, nPnt + 1);
2651  SetPathPoly(basegfx::B2DPolyPolygon(aSplitPolyA));
2652 
2654  basegfx::B2DPolygon aSplitPolyB(aCandidate, nPnt, nPointCount - nPnt);
2655  pNewObj->SetPathPoly(basegfx::B2DPolyPolygon(aSplitPolyB));
2656  }
2657  }
2658  }
2659  }
2660  }
2661 
2662  return pNewObj;
2663 }
2664 
2665 SdrObjectUniquePtr SdrPathObj::DoConvertToPolyObj(bool bBezier, bool bAddText) const
2666 {
2667  // #i89784# check for FontWork with activated HideContour
2670  const bool bHideContour(
2671  !aText.isDefault() && !aText.getSdrFormTextAttribute().isDefault() && aText.isHideContour());
2672 
2673  SdrObjectUniquePtr pRet;
2674 
2675  if(!bHideContour)
2676  {
2678 
2679  if(pPath->GetPathPoly().areControlPointsUsed())
2680  {
2681  if(!bBezier)
2682  {
2683  // reduce all bezier curves
2684  pPath->SetPathPoly(basegfx::utils::adaptiveSubdivideByAngle(pPath->GetPathPoly()));
2685  }
2686  }
2687  else
2688  {
2689  if(bBezier)
2690  {
2691  // create bezier curves
2692  pPath->SetPathPoly(basegfx::utils::expandToCurve(pPath->GetPathPoly()));
2693  }
2694  }
2695  pRet = std::move(pPath);
2696  }
2697 
2698  if(bAddText)
2699  {
2700  pRet = ImpConvertAddText(std::move(pRet), bBezier);
2701  }
2702 
2703  return pRet;
2704 }
2705 
2707 {
2708  return new SdrPathObjGeoData;
2709 }
2710 
2712 {
2714  SdrPathObjGeoData& rPGeo = static_cast<SdrPathObjGeoData&>( rGeo );
2715  rPGeo.maPathPolygon=GetPathPoly();
2716  rPGeo.meKind=meKind;
2717 }
2718 
2720 {
2722  const SdrPathObjGeoData& rPGeo=static_cast<const SdrPathObjGeoData&>(rGeo);
2724  meKind=rPGeo.meKind;
2725  ImpForceKind(); // to set bClosed (among other things)
2726 }
2727 
2729 {
2730  if(GetPathPoly() != rPathPoly)
2731  {
2732  maPathPolygon=rPathPoly;
2733  ImpForceKind();
2734  SetRectsDirty();
2735  }
2736 }
2737 
2739 {
2740  if(GetPathPoly() != rPathPoly)
2741  {
2742  tools::Rectangle aBoundRect0; if (pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
2743  NbcSetPathPoly(rPathPoly);
2744  SetChanged();
2747  }
2748 }
2749 
2751 {
2752  tools::Rectangle aBoundRect0;
2753  if(pUserCall != nullptr)
2754  aBoundRect0 = GetLastBoundRect();
2755  ImpSetClosed(!IsClosed()); // set new ObjKind
2756  ImpForceKind(); // because we want Line -> Poly -> PolyLine instead of Line -> Poly -> Line
2757  SetRectsDirty();
2758  SetChanged();
2760  SendUserCall(SdrUserCallType::Resize, aBoundRect0);
2761 }
2762 
2764 {
2765  if(!mpDAC)
2766  {
2767  const_cast<SdrPathObj*>(this)->mpDAC.reset(new ImpPathForDragAndCreate(*const_cast<SdrPathObj*>(this)));
2768  }
2769 
2770  return *mpDAC;
2771 }
2772 
2773 
2774 // transformation interface for StarOfficeAPI. This implements support for
2775 // homogeneous 3x3 matrices containing the transformation of the SdrObject. At the
2776 // moment it contains a shearX, rotation and translation, but for setting all linear
2777 // transforms like Scale, ShearX, ShearY, Rotate and Translate are supported.
2778 
2779 
2780 // gets base transformation and rectangle of object. If it's an SdrPathObj it fills the PolyPolygon
2781 // with the base geometry and returns TRUE. Otherwise it returns FALSE.
2783 {
2784  double fRotate(0.0);
2785  double fShearX(0.0);
2786  basegfx::B2DTuple aScale(1.0, 1.0);
2787  basegfx::B2DTuple aTranslate(0.0, 0.0);
2788 
2789  if(GetPathPoly().count())
2790  {
2791  // copy geometry
2792  basegfx::B2DHomMatrix aMoveToZeroMatrix;
2793  rPolyPolygon = GetPathPoly();
2794 
2795  if(OBJ_LINE == meKind)
2796  {
2797  // ignore shear and rotate, just use scale and translate
2798  OSL_ENSURE(GetPathPoly().count() > 0 && GetPathPoly().getB2DPolygon(0).count() > 1, "OBJ_LINE with too few polygons (!)");
2799  // #i72287# use polygon without control points for range calculation. Do not change rPolyPolygon
2800  // itself, else this method will no longer return the full polygon information (curve will
2801  // be lost)
2802  const basegfx::B2DRange aPolyRangeNoCurve(basegfx::utils::getRange(rPolyPolygon));
2803  aScale = aPolyRangeNoCurve.getRange();
2804  aTranslate = aPolyRangeNoCurve.getMinimum();
2805 
2806  // define matrix for move polygon to zero point
2807  aMoveToZeroMatrix.translate(-aTranslate.getX(), -aTranslate.getY());
2808  }
2809  else
2810  {
2812  {
2813  // get rotate and shear in drawingLayer notation
2814  fRotate = aGeo.nRotationAngle * F_PI18000;
2815  fShearX = aGeo.nShearAngle * F_PI18000;
2816 
2817  // build mathematically correct (negative shear and rotate) object transform
2818  // containing shear and rotate to extract unsheared, unrotated polygon
2819  basegfx::B2DHomMatrix aObjectMatrix;
2820  aObjectMatrix.shearX(tan((36000 - aGeo.nShearAngle) * F_PI18000));
2821  aObjectMatrix.rotate((36000 - aGeo.nRotationAngle) * F_PI18000);
2822 
2823  // create inverse from it and back-transform polygon
2824  basegfx::B2DHomMatrix aInvObjectMatrix(aObjectMatrix);
2825  aInvObjectMatrix.invert();
2826  rPolyPolygon.transform(aInvObjectMatrix);
2827 
2828  // get range from unsheared, unrotated polygon and extract scale and translate.
2829  // transform topLeft from it back to transformed state to get original
2830  // topLeft (rotation center)
2831  // #i72287# use polygon without control points for range calculation. Do not change rPolyPolygon
2832  // itself, else this method will no longer return the full polygon information (curve will
2833  // be lost)
2834  const basegfx::B2DRange aCorrectedRangeNoCurve(basegfx::utils::getRange(rPolyPolygon));
2835  aTranslate = aObjectMatrix * aCorrectedRangeNoCurve.getMinimum();
2836  aScale = aCorrectedRangeNoCurve.getRange();
2837 
2838  // define matrix for move polygon to zero point
2839  // #i112280# Added missing minus for Y-Translation
2840  aMoveToZeroMatrix.translate(-aCorrectedRangeNoCurve.getMinX(), -aCorrectedRangeNoCurve.getMinY());
2841  }
2842  else
2843  {
2844  // get scale and translate from unsheared, unrotated polygon
2845  // #i72287# use polygon without control points for range calculation. Do not change rPolyPolygon
2846  // itself, else this method will no longer return the full polygon information (curve will
2847  // be lost)
2848  const basegfx::B2DRange aPolyRangeNoCurve(basegfx::utils::getRange(rPolyPolygon));
2849  aScale = aPolyRangeNoCurve.getRange();
2850  aTranslate = aPolyRangeNoCurve.getMinimum();
2851 
2852  // define matrix for move polygon to zero point
2853  aMoveToZeroMatrix.translate(-aTranslate.getX(), -aTranslate.getY());
2854  }
2855  }
2856 
2857  // move polygon to zero point with pre-defined matrix
2858  rPolyPolygon.transform(aMoveToZeroMatrix);
2859  }
2860 
2861  // position maybe relative to anchorpos, convert
2862  if( getSdrModelFromSdrObject().IsWriter() )
2863  {
2864  if(GetAnchorPos().X() || GetAnchorPos().Y())
2865  {
2866  aTranslate -= basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
2867  }
2868  }
2869 
2870  // build return value matrix
2872  aScale,
2873  basegfx::fTools::equalZero(fShearX) ? 0.0 : tan(fShearX),
2874  basegfx::fTools::equalZero(fRotate) ? 0.0 : -fRotate,
2875  aTranslate);
2876 
2877  return true;
2878 }
2879 
2880 // Sets the base geometry of the object using infos contained in the homogeneous 3x3 matrix.
2881 // If it's an SdrPathObj it will use the provided geometry information. The Polygon has
2882 // to use (0,0) as upper left and will be scaled to the given size in the matrix.
2884 {
2885  // break up matrix
2886  basegfx::B2DTuple aScale;
2887  basegfx::B2DTuple aTranslate;
2888  double fRotate, fShearX;
2889  rMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
2890 
2891  // #i75086# Old DrawingLayer (GeoStat and geometry) does not support holding negative scalings
2892  // in X and Y which equal a 180 degree rotation. Recognize it and react accordingly
2893  if(basegfx::fTools::less(aScale.getX(), 0.0) && basegfx::fTools::less(aScale.getY(), 0.0))
2894  {
2895  aScale.setX(fabs(aScale.getX()));
2896  aScale.setY(fabs(aScale.getY()));
2897  fRotate = fmod(fRotate + F_PI, F_2PI);
2898  }
2899 
2900  // copy poly
2901  basegfx::B2DPolyPolygon aNewPolyPolygon(rPolyPolygon);
2902 
2903  // reset object shear and rotations
2904  aGeo.nRotationAngle = 0;
2905  aGeo.RecalcSinCos();
2906  aGeo.nShearAngle = 0;
2907  aGeo.RecalcTan();
2908 
2909  if( getSdrModelFromSdrObject().IsWriter() )
2910  {
2911  // if anchor is used, make position relative to it
2912  if(GetAnchorPos().X() || GetAnchorPos().Y())
2913  {
2914  aTranslate += basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
2915  }
2916  }
2917 
2918  // create transformation for polygon, set values at aGeo direct
2919  basegfx::B2DHomMatrix aTransform;
2920 
2921  // #i75086#
2922  // Given polygon is already scaled (for historical reasons), but not mirrored yet.
2923  // Thus, when scale is negative in X or Y, apply the needed mirroring accordingly.
2924  double fScaleX(basegfx::fTools::less(aScale.getX(), 0.0) ? -1.0 : 1.0);
2925  double fScaleY(basegfx::fTools::less(aScale.getY(), 0.0) ? -1.0 : 1.0);
2926 
2927  // tdf#98565, tdf#98584. While loading a shape, svg:width and svg:height is used to scale
2928  // the polygon. But draw:transform might introduce additional scaling factors, which need to
2929  // be applied to the polygon too, so aScale cannot be ignored while loading.
2930  // I use "maSnapRect.IsEmpty() && GetPathPoly().count()" to detect this case. Any better
2931  // idea? The behavior in other cases is the same as it was before this fix.
2932  if (maSnapRect.IsEmpty() && GetPathPoly().count())
2933  {
2934  // In case of a Writer document, the scaling factors were converted to twips. That is not
2935  // correct here, because width and height are already in the points coordinates and aScale
2936  // is no length but only a factor here. Convert back.
2938  {
2939  aScale.setX(aScale.getX() * 127.0 / 72.0);
2940  aScale.setY(aScale.getY() * 127.0 / 72.0);
2941  }
2942  fScaleX *= fabs(aScale.getX());
2943  fScaleY *= fabs(aScale.getY());
2944  }
2945 
2946  if (fScaleX != 1.0 || fScaleY != 1.0)
2947  aTransform.scale(fScaleX, fScaleY);
2948 
2949  if(!basegfx::fTools::equalZero(fShearX))
2950  {
2951  aTransform.shearX(tan(-atan(fShearX)));
2952  aGeo.nShearAngle = FRound(atan(fShearX) / F_PI18000);
2953  aGeo.RecalcTan();
2954  }
2955 
2956  if(!basegfx::fTools::equalZero(fRotate))
2957  {
2958  // #i78696#
2959  // fRotate is mathematically correct for linear transformations, so it's
2960  // the one to use for the geometry change
2961  aTransform.rotate(fRotate);
2962 
2963  // #i78696#
2964  // fRotate is mathematically correct, but aGeoStat.nRotationAngle is
2965  // mirrored -> mirror value here
2967  aGeo.RecalcSinCos();
2968  }
2969 
2970  if(!aTranslate.equalZero())
2971  {
2972  // #i39529# absolute positioning, so get current position (without control points (!))
2973  const basegfx::B2DRange aCurrentRange(basegfx::utils::getRange(aNewPolyPolygon));
2974  aTransform.translate(aTranslate.getX() - aCurrentRange.getMinX(), aTranslate.getY() - aCurrentRange.getMinY());
2975  }
2976 
2977  // transform polygon and trigger change
2978  aNewPolyPolygon.transform(aTransform);
2979  SetPathPoly(aNewPolyPolygon);
2980 }
2981 
2982 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Point TopLeft() const
long Width() const
static tools::Rectangle lcl_ImpGetBoundRect(const basegfx::B2DPolyPolygon &rPolyPolygon)
Definition: svdopath.cxx:1646
void Insert(XPolygon &&rXPoly)
Definition: _xpoly.cxx:861
virtual ~SdrPathObj() override
void setX(double fX)
const size_t count(pCandidateA->getBorderLines().size())
bool IsEliminatePolyPoints() const
Definition: svdsnpv.hxx:282
virtual void NbcResize(const Point &rRef, const Fraction &xFact, const Fraction &yFact) override
Definition: svdotxtr.cxx:102
void append(const basegfx::B2DPoint &rPoint, sal_uInt32 nCount)
virtual SdrPathObj * CloneSdrObject(SdrModel &rTargetModel) const override
Definition: svdopath.cxx:1818
sal_uInt32 NbcInsPointOld(const Point &rPos, bool bNewObj)
Definition: svdopath.cxx:2480
virtual OUString TakeObjNamePlural() const override
Definition: svdopath.cxx:1939
void remove(sal_uInt32 nIndex, sal_uInt32 nCount=1)
sal_uInt32 GetPolyNum() const
Definition: svdhdl.hxx:219
bool IsHeightEmpty() const
bool IsClosedObj() const
Definition: svdobj.hxx:787
sal_uInt16 Count() const
Definition: _xpoly.cxx:890
std::unique_ptr< ImpSdrPathDragData > mpSdrPathDragData
Definition: svdopath.cxx:500
void BrkCreate(SdrDragStat &rStat)
Definition: svdopath.cxx:1511
basegfx::B2DPoint getNextControlPoint(sal_uInt32 nIndex) const
virtual basegfx::B2DPolyPolygon TakeCreatePoly(const SdrDragStat &rDrag) const override
Polygon dragged by the user when creating the object.
Definition: svdopath.cxx:2220
long FRound(double fVal)
SdrObjUserCall * pUserCall
Definition: svdobj.hxx:923
virtual SdrObjGeoData * NewGeoData() const override
A derived class must override these 3 methods if it has own geometric data that must be saved for Und...
Definition: svdopath.cxx:2706
natural cubic Spline (ni)
Definition: svdobj.hxx:132
basegfx::B2DPolyPolygon maPathPolygon
Definition: svdopath.hxx:35
static bool equal(const double &rfValA, const double &rfValB)
PolyFlags GetFlags(sal_uInt16 nPos) const
get the flags for the point at the given position
Definition: _xpoly.cxx:452
long Height() const
double nSin
Definition: svdtrans.hxx:219
const Point & GetStart() const
Definition: svddrag.hxx:92
virtual SdrText * getText(sal_Int32 nIndex) const override
returns the nth available text.
Definition: svdotext.cxx:2049
GeoStat aGeo
Definition: svdotext.hxx:185
B2DPolygon expandToCurve(const B2DPolygon &rCandidate)
static bool lcl_ImpIsLine(const basegfx::B2DPolyPolygon &rPolyPolygon)
Definition: svdopath.cxx:1641
void ImpSetClosed(bool bClose)
Definition: svdopath.cxx:1767
bool ImpCanConvTextToCurve() const
Definition: svdotxtr.cxx:424
virtual Point GetPoint(sal_uInt32 nHdlNum) const override
Definition: svdopath.cxx:2438
SdrPathObj & mrSdrPathObject
Definition: svdopath.cxx:496
virtual bool hasSpecialDrag() const override
The standard transformations (Move,Resize,Rotate,Mirror,Shear) are taken over by the View (TakeXorPol...
Definition: svdopath.cxx:2069
long nRotationAngle
Definition: svdtrans.hxx:216
double getX() const
bool IsUseIncompatiblePathCreateInterface() const
Definition: svdcrtv.hxx:151
caption object
Definition: svdobj.hxx:140
basegfx::B2DPolyPolygon getB2DPolyPolygon() const
Definition: _xpoly.cxx:935
void setB2DPolygon(sal_uInt32 nIndex, const B2DPolygon &rPolygon)
sal_uInt32 GetPointNum() const
Definition: svdhdl.hxx:222
PointerStyle GetCreatePointer() const
Definition: svdopath.cxx:1582
bool areControlPointsUsed() const
double getY() const
SdrObject * GetObj() const
Definition: svdhdl.hxx:203
B2DVector getRange() const
SdrObjectUniquePtr ImpConvertAddText(SdrObjectUniquePtr pObj, bool bBezier) const
Definition: svdotxtr.cxx:463
SdrInventor
Definition: svdobj.hxx:152
void TakeCurrentObj(sal_uInt16 &nIdent, SdrInventor &nInvent) const
Definition: svdcrtv.hxx:107
void NbcMirrorGluePoints(const Point &rRef1, const Point &rRef2)
Definition: svdobj.cxx:2233
virtual bool EndCreate(SdrDragStat &rStat, SdrCreateCmd eCmd) override
Definition: svdopath.cxx:2160
circle cut
Definition: svdobj.hxx:125
void shearY(double fSy)
virtual void BrkCreate(SdrDragStat &rStat) override
Definition: svdopath.cxx:2212
void RecalcTan()
Definition: svdtrans.cxx:462
virtual void NbcSetPoint(const Point &rPnt, sal_uInt32 nHdlNum) override
Definition: svdopath.cxx:2453
open free-hand line
Definition: svdobj.hxx:130
virtual const tools::Rectangle & GetSnapRect() const override
Definition: svdoattr.cxx:48
float x
bool MovCreate(SdrDragStat &rStat)
Definition: svdopath.cxx:1243
virtual void NbcResize(const Point &rRefPnt, const Fraction &aXFact, const Fraction &aYFact) override
Definition: svdopath.cxx:2271
All geometrical data of an arbitrary object for use in undo/redo.
Definition: svdobj.hxx:226
double getMaxX() const
long NormAngle36000(long a)
Normalize angle to -180.00..179.99.
Definition: svdtrans.cxx:407
std::unique_ptr< SdrPathObj, SdrObjectFreeOp > SdrPathObjUniquePtr
Definition: svdotext.hxx:135
polygon, PolyPolygon
Definition: svdobj.hxx:126
long GetEliminatePolyPointLimitAngle() const
Definition: svdsnpv.hxx:284
line
Definition: svdobj.hxx:120
virtual void NbcSetSnapRect(const tools::Rectangle &rRect) override
Definition: svdopath.cxx:2373
virtual bool HasText() const override
Definition: svdotxat.cxx:412
void SetOrtho8Possible(bool bOn=true)
Definition: svddrag.hxx:129
void Move(long nHorzMoveDelta, long nVertMoveDelta)
virtual void NbcMirror(const Point &rRef1, const Point &rRef2) override
Definition: svdotxtr.cxx:232
long GetLen(const Point &rPnt)
Determine sector within the cartesian coordinate system.
Definition: svdtrans.cxx:424
HSLColor interpolate(const HSLColor &rFrom, const HSLColor &rTo, double t, bool bCCW)
bool IsEmpty() const
SdrObjKind meKind
Definition: svdopath.hxx:36
virtual void RestGeoData(const SdrObjGeoData &rGeo) override
Definition: svdopath.cxx:2719
Provides information about various ZObject properties.
Definition: svdobj.hxx:248
const SdrHdl * GetHdl() const
Definition: svddrag.hxx:101
static OUString GetAngleString(long nAngle)
Definition: svdmodel.cxx:1204
void ImpForceKind()
Definition: svdopath.cxx:1681
Polygon/PolyPolygon represented by SdrPathObj.
Definition: svdobj.hxx:141
B2DPolygon const & getB2DPolygon(sal_uInt32 nIndex) const
ImpPathForDragAndCreate(SdrPathObj &rSdrPathObject)
Definition: svdopath.cxx:533
const XPolygon & GetObject(sal_uInt16 nPos) const
Definition: _xpoly.cxx:880
virtual bool MovCreate(SdrDragStat &rStat) override
Definition: svdopath.cxx:2155
long Right() const
SdrPathObj(SdrModel &rSdrModel, SdrObjKind eNewKind)
Definition: svdopath.cxx:1618
int n2
bool IsOrtho() const
Definition: svdsnpv.hxx:246
OUString SvxResId(const char *pId)
Definition: dialmgr.cxx:28
virtual void AddToPlusHdlList(SdrHdlList &rHdlList, SdrHdl &rHdl) const override
Definition: svdopath.cxx:2003
#define X
virtual void SaveGeoData(SdrObjGeoData &rGeo) const override
Definition: svdotext.cxx:1432
B2DHomMatrix createScaleShearXRotateTranslateB2DHomMatrix(double fScaleX, double fScaleY, double fShearX, double fRadiant, double fTranslateX, double fTranslateY)
bool isPrevControlPointUsed(sal_uInt32 nIndex) const
void CalcTangent(sal_uInt16 nCenter, sal_uInt16 nPrev, sal_uInt16 nNext)
Calculate tangent between two Bézier curves.
Definition: _xpoly.cxx:648
int nCount
bool IsClosed() const
Definition: svdopath.hxx:144
const SfxItemSet & GetObjectItemSet() const
Definition: svdobj.cxx:1878
oslFileHandle & pOut
tools::Rectangle maRect
Definition: svdotext.hxx:182
void insert(sal_uInt32 nIndex, const basegfx::B2DPoint &rPoint, sal_uInt32 nCount=1)
void setB2DPoint(sal_uInt32 nIndex, const basegfx::B2DPoint &rValue)
SdrObject * RipPoint(sal_uInt32 nHdlNum, sal_uInt32 &rNewPt0Index)
Definition: svdopath.cxx:2619
PolyLine.
Definition: svdobj.hxx:127
B2DHomMatrix createScaleTranslateB2DHomMatrix(double fScaleX, double fScaleY, double fTranslateX, double fTranslateY)
const Point & GetPos0() const
Definition: svddrag.hxx:94
bool bClosedObj
Definition: svdobj.hxx:941
long Top() const
OutputDevice * GetFirstOutputDevice() const
Definition: svdpntv.cxx:87
virtual void NbcRotate(const Point &rRefPnt, long nAngle, double fSin, double fCos) override
Definition: svdopath.cxx:2296
tools::Rectangle maSnapRect
Definition: svdoattr.hxx:42
double getMaxY() const
circle, ellipse
Definition: svdobj.hxx:122
virtual ~SdrPathObjGeoData() override
Definition: svdopath.cxx:1606
#define XPOLY_APPEND
Definition: xpoly.hxx:37
virtual bool IsPolyObj() const override
Definition: svdopath.cxx:2421
bool isClosed() const
sal_uInt16 GetFreeHandMinDistPix() const
Definition: svdcrtv.hxx:141
static bool less(const double &rfValA, const double &rfValB)
void shearX(double fSx)
float y
const B2DPoint & getEndPoint() const
virtual void RecalcSnapRect() override
Snap is not done on the BoundRect but if possible on logic coordinates (i.e.
Definition: svdopath.cxx:2365
bool isEmpty() const
sal_uInt16 GetAutoCloseDistPix() const
Definition: svdcrtv.hxx:136
void SetNoSnap(bool bOn=true)
Definition: svddrag.hxx:121
object group
Definition: svdobj.hxx:119
sal_Int32 nRef
attribute::SdrTextAttribute createNewSdrTextAttribute(const SfxItemSet &rSet, const SdrText &rText, const sal_Int32 *pLeft, const sal_Int32 *pUpper, const sal_Int32 *pRight, const sal_Int32 *pLower)
#define F_PI
double nCos
Definition: svdtrans.hxx:220
int i
void SetPathPoly(const basegfx::B2DPolyPolygon &rPathPoly)
Definition: svdopath.cxx:2738
virtual Point GetSnapPoint(sal_uInt32 i) const override
Definition: svdopath.cxx:2409
uno_Any a
void Clear()
Definition: _xpoly.cxx:885
bool IsWriter() const
Definition: svdmodel.hxx:576
const B2DPoint & getControlPointA() const
bool IsCreating() const
Definition: svdopath.cxx:525
OUString sName
bool decompose(B2DTuple &rScale, B2DTuple &rTranslate, double &rRotate, double &rShearX) const
void rotate(double fRadiant)
SdrObject * GetCreateObj() const
Definition: svdcrtv.hxx:119
static basegfx::B2DPolyPolygon TakeDragPolyPolygon(const SdrDragStat &rDrag)
Definition: svdopath.cxx:1560
SdrPathObjUniquePtr ImpConvertMakeObj(const basegfx::B2DPolyPolygon &rPolyPolygon, bool bClosed, bool bBezier) const
Definition: svdotxtr.cxx:429
B2DPolygon makeStartPoint(const B2DPolygon &rCandidate, sal_uInt32 nIndexOfNewStatPoint)
virtual void AddToHdlList(SdrHdlList &rHdlList) const override
Definition: svdopath.cxx:1975
void NbcSetPathPoly(const basegfx::B2DPolyPolygon &rPathPoly)
Definition: svdopath.cxx:2728
bool IsSelected() const
Definition: svdhdl.hxx:206
void openWithGeometryChange(B2DPolygon &rCandidate)
std::unique_ptr< ImpPathForDragAndCreate > mpDAC
Definition: svdopath.hxx:54
bool IsSpline() const
Definition: svdopath.hxx:147
void BroadcastObjectChange() const
Definition: svdobj.cxx:904
sal_Int32 GetPointCount() const
Definition: svddrag.hxx:91
#define F_2PI
SdrTextObj & operator=(const SdrTextObj &rObj)
Definition: svdotext.cxx:1007
SdrView * GetView() const
Definition: svddrag.hxx:86
bool IsAngleSnapEnabled() const
Definition: svdsnpv.hxx:222
basegfx::B2DPoint getPrevControlPoint(sal_uInt32 nIndex) const
static bool equalZero(const double &rfVal)
double getSmallestDistancePointToPolyPolygon(const B2DPolyPolygon &rCandidate, const B2DPoint &rTestPoint, sal_uInt32 &rPolygonIndex, sal_uInt32 &rEdgeIndex, double &rCut)
virtual bool BegCreate(SdrDragStat &rStat) override
Every object must be able to create itself interactively.
Definition: svdopath.cxx:2148
static sal_uInt16 GetNextPnt(sal_uInt16 nPnt, sal_uInt16 nPntMax, bool bClosed)
Definition: svdopath.cxx:62
virtual SdrObjectUniquePtr DoConvertToPolyObj(bool bBezier, bool bAddText) const override
Definition: svdopath.cxx:2665
long Bottom() const
void scale(double fX, double fY)
void setNextControlPoint(sal_uInt32 nIndex, const basegfx::B2DPoint &rValue)
SdrModel & getSdrModelFromSdrObject() const
Definition: svdobj.cxx:272
void transform(const basegfx::B2DHomMatrix &rMatrix)
static bool IsBezier(SdrObjKind eKind)
Definition: svdopath.cxx:524
bool BckCreate(SdrDragStat const &rStat)
Definition: svdopath.cxx:1471
long GetSnapAngle() const
Definition: svdsnpv.hxx:224
static bool IsFreeHand(SdrObjKind eKind)
Definition: svdopath.cxx:523
virtual bool TRGetBaseGeometry(basegfx::B2DHomMatrix &rMatrix, basegfx::B2DPolyPolygon &rPolyPolygon) const override
Definition: svdopath.cxx:2782
void NextPoint()
Definition: svddrag.cxx:73
virtual void NbcMove(const Size &rSiz) override
The methods Move, Resize, Rotate, Mirror, Shear, SetSnapRect and SetLogicRect call the corresponding ...
Definition: svdotxtr.cxx:94
bool IsCreate1stPointAsCenter() const
Definition: svdcrtv.hxx:132
basegfx::B2DPolyPolygon getDragPolyPolygon(const SdrDragStat &rDrag) const
Definition: svdopath.cxx:2246
SdrObjKind meKind
Definition: svdopath.hxx:51
basegfx::B2DPolyPolygon getObjectPolyPolygon(const SdrDragStat &rDrag) const
Definition: svdopath.cxx:2234
bool movePathDrag(SdrDragStat &rDrag) const
Definition: svdopath.cxx:586
virtual void NbcMove(const Size &aSize) override
The methods Move, Resize, Rotate, Mirror, Shear, SetSnapRect and SetLogicRect call the corresponding ...
Definition: svdopath.cxx:2263
Abstract DrawObject.
Definition: svdobj.hxx:312
bool IsCreateMode() const
Definition: svdmrkv.hxx:222
virtual void TakeObjInfo(SdrObjTransformInfoRec &rInfo) const override
Definition: svdopath.cxx:1800
OUString GetName() const
Definition: svdobj.cxx:697
long nShearAngle
Definition: svdtrans.hxx:217
#define Y
virtual void NbcShear(const Point &rRefPnt, long nAngle, double fTan, bool bVShear) override
Definition: svdopath.cxx:2307
virtual void SaveGeoData(SdrObjGeoData &rGeo) const override
Definition: svdopath.cxx:2711
B2DRange getRange(const B2DPolygon &rCandidate)
basegfx::B2DPolyPolygon TakeObjectPolyPolygon(const SdrDragStat &rDrag) const
Definition: svdopath.cxx:1518
long BigMulDiv(long nVal, long nMul, long nDiv)
Definition: svdtrans.cxx:558
virtual sal_uInt32 GetHdlCount() const override
Via GetHdlCount the number of Handles can be retrieved.
Definition: svdopath.cxx:1963
bool endPathDrag(SdrDragStat const &rDrag)
Definition: svdopath.cxx:819
OUString GetMetricString(long nVal, bool bNoUnitChars=false, sal_Int32 nNumDigits=-1) const
Definition: svdmodel.cxx:1082
sal_uInt32 NbcInsPoint(const Point &rPos, bool bNewObj)
Definition: svdopath.cxx:2504
const Point & GetPrev() const
Definition: svddrag.hxx:93
Point PixelToLogic(const Point &rDevicePt) const
void append(const B2DPolygon &rPolygon, sal_uInt32 nCount=1)
void Insert(sal_uInt16 nPos, const Point &rPt, PolyFlags eFlags)
Definition: _xpoly.cxx:352
closed Bezier-curve
Definition: svdobj.hxx:129
virtual const tools::Rectangle & GetLastBoundRect() const
Definition: svdobj.cxx:867
size_t GetHdlCount() const
Definition: svdhdl.hxx:461
basegfx::B2DPolyPolygon getSpecialDragPoly(const SdrDragStat &rDrag) const
Definition: svdopath.cxx:1103
circle section
Definition: svdobj.hxx:123
SdrHdl * GetHdl(size_t nNum) const
Definition: svdhdl.hxx:462
void setClosed(bool bNew)
void RotatePoint(Point &rPnt, const Point &rRef, double sn, double cs)
Definition: svdtrans.hxx:114
void SetActionRect(const tools::Rectangle &rR)
Definition: svddrag.hxx:157
virtual void TRSetBaseGeometry(const basegfx::B2DHomMatrix &rMatrix, const basegfx::B2DPolyPolygon &rPolyPolygon) override
Definition: svdopath.cxx:2883
void closeWithGeometryChange(B2DPolygon &rCandidate)
bool isNextControlPointUsed(sal_uInt32 nIndex) const
double getMinY() const
void split(double t, B2DCubicBezier *pBezierA, B2DCubicBezier *pBezierB) const
sal_uInt32 count() const
virtual void TakeUnrotatedSnapRect(tools::Rectangle &rRect) const override
Definition: svdopath.cxx:2347
virtual void RestGeoData(const SdrObjGeoData &rGeo) override
Definition: svdotext.cxx:1440
void Remove(sal_uInt16 nPos, sal_uInt16 nCount)
Definition: _xpoly.cxx:376
B2DPolygon adaptiveSubdivideByAngle(const B2DPolygon &rCandidate, double fAngleBound)
virtual void NbcMirror(const Point &rRefPnt1, const Point &rRefPnt2) override
Definition: svdopath.cxx:2328
circle arc
Definition: svdobj.hxx:124
void setY(double fY)
SdrObjKind
Definition: svdobj.hxx:116
bool equalZero() const
PointerStyle GetCreatePointer() const override
get the cursor/pointer that signals creating this object
Definition: svdopath.cxx:2258
void RecalcSinCos()
Definition: svdtrans.cxx:450
bool IsControl(sal_uInt16 nPos) const
short path to read the CONTROL flag directly (TODO: better explain what the sense behind this flag is...
Definition: _xpoly.cxx:466
OUString ImpGetDescriptionStr(const char *pStrCacheID) const
Definition: svdobj.cxx:1037
virtual OUString TakeObjNameSingul() const override
Definition: svdopath.cxx:1832
ImpPathForDragAndCreate & impGetDAC() const
Definition: svdopath.cxx:2763
SdrPathObj & operator=(const SdrPathObj &rObj)
Definition: svdopath.cxx:1823
virtual sal_uInt32 GetPointCount() const override
Definition: svdopath.cxx:2426
void ImpForceLineAngle()
Definition: svdopath.cxx:1658
void SetFlags(sal_uInt16 nPos, PolyFlags eFlags)
set the flags for the point at the given position
Definition: _xpoly.cxx:459
B2DHomMatrix createRotateAroundPoint(double fPointX, double fPointY, double fRadiant)
OUString aName
void SetUser(std::unique_ptr< SdrDragStatUserData > pU)
Definition: svddrag.hxx:104
bool IsPlusHdl() const
Definition: svdhdl.hxx:225
#define F_PI18000
void SetNow(Point const &pt)
Definition: svddrag.hxx:96
B2DPoint getMinimum() const
XPolyPolygon aPathPolygon
Definition: svdopath.cxx:497
virtual sal_uInt16 GetObjIdentifier() const override
Definition: svdopath.cxx:1813
bool IsBezier() const
Definition: svdopath.hxx:146
virtual bool applySpecialDrag(SdrDragStat &rDrag) override
Definition: svdopath.cxx:2081
bool IsMouseDown() const
Definition: svddrag.hxx:140
std::unique_ptr< SdrObject, SdrObjectFreeOp > SdrObjectUniquePtr
Definition: svdobj.hxx:114
bool IsWidthEmpty() const
virtual sal_uInt32 GetSnapPointCount() const override
snap to special points of an Object (polygon points, center of circle)
Definition: svdopath.cxx:2404
virtual basegfx::B2DPolyPolygon getSpecialDragPoly(const SdrDragStat &rDrag) const override
Definition: svdopath.cxx:2132
const Point & GetNow() const
Definition: svddrag.hxx:95
bool EndCreate(SdrDragStat &rStat, SdrCreateCmd eCmd)
Definition: svdopath.cxx:1359
long GetAngle(const Point &rPnt)
The Y axis points down! The function negates the Y axis, when calculating the angle, such that GetAngle(Point(0,-1))=90 deg.
Definition: svdtrans.cxx:385
long Left() const
virtual std::unique_ptr< sdr::contact::ViewContact > CreateObjectSpecificViewContact() override
Definition: svdopath.cxx:1612
void translate(double fX, double fY)
PointerStyle
const Point & GetAnchorPos() const
Definition: svdobj.cxx:1587
const basegfx::B2DPolyPolygon & GetPathPoly() const
Definition: svdopath.hxx:139
virtual void SetRectsDirty(bool bNotMyself=false, bool bRecursive=true)
Definition: svdobj.cxx:434
static sal_uInt16 GetPrevPnt(sal_uInt16 nPnt, sal_uInt16 nPntMax, bool bClosed)
Definition: svdopath.cxx:51
void BegCreate(SdrDragStat &rStat)
Definition: svdopath.cxx:1219
void RotateXPoly(XPolygon &rPoly, const Point &rRef, double sn, double cs)
Definition: svdtrans.cxx:87
virtual bool BckCreate(SdrDragStat &rStat) override
Definition: svdopath.cxx:2207
const SdrFormTextAttribute & getSdrFormTextAttribute() const
#define SAL_WARN(area, stream)
virtual OUString getSpecialDragComment(const SdrDragStat &rDrag) const override
Definition: svdopath.cxx:2104
double getMinX() const
bool LineGeometryUsageIsNecessary() const
Definition: svdobj.cxx:961
virtual bool beginSpecialDrag(SdrDragStat &rDrag) const override
Definition: svdopath.cxx:2074
const SdrHdlList & GetHdlList() const
Definition: svdmrkv.hxx:344
virtual basegfx::B2DPolyPolygon TakeXorPoly() const override
The Xor-Polygon is required by the View to drag the object.
Definition: svdopath.cxx:1958
virtual void NbcRotate(const Point &rRef, long nAngle, double sn, double cs) override
Definition: svdotxtr.cxx:186
SdrDragStatUserData * GetUser() const
Definition: svddrag.hxx:103
B2DHomMatrix createTranslateB2DHomMatrix(double fTranslateX, double fTranslateY)
SdrCreateCmd
Definition: svdtypes.hxx:27
sal_uInt16 GetPointCount() const
Definition: _xpoly.cxx:346
bool IsBigOrtho() const
Definition: svdsnpv.hxx:258
virtual void SetChanged()
Definition: svdobj.cxx:928
void Remove(sal_uInt16 nPos)
Definition: _xpoly.cxx:875
basegfx::B2DPolyPolygon maPathPolygon
Definition: svdopath.hxx:50
virtual void NbcShear(const Point &rRef, long nAngle, double tn, bool bVShear) override
Definition: svdotxtr.cxx:210
void PointsToBezier(sal_uInt16 nFirst)
convert four polygon points into a Bézier curve
Definition: _xpoly.cxx:675
void ToggleClosed()
Definition: svdopath.cxx:2750
void setPrevControlPoint(sal_uInt32 nIndex, const basegfx::B2DPoint &rValue)
basegfx::B2DPolyPolygon getModifiedPolyPolygon() const
Definition: svdopath.cxx:530
aStr
sal_uInt32 GetSourceHdlNum() const
Definition: svdhdl.hxx:228
open Bezier-curve
Definition: svdobj.hxx:128
void SendUserCall(SdrUserCallType eUserCall, const tools::Rectangle &rBoundRect) const
Definition: svdobj.cxx:2648
closed free-hand line
Definition: svdobj.hxx:131
tools::Rectangle GetBoundRect() const
Definition: _xpoly.cxx:895
static bool IsClosed(SdrObjKind eKind)
Definition: svdopath.cxx:522
Point Center() const
void AddHdl(std::unique_ptr< SdrHdl > pHdl)
Definition: svdhdl.cxx:2294
virtual bool IsFontwork() const
Definition: svdotext.cxx:1724
const B2DPoint & getControlPointB() const
sal_uInt32 count() const
rectangle (round corners optional)
Definition: svdobj.hxx:121
OUString getSpecialDragComment(const SdrDragStat &rDrag) const
Definition: svdopath.cxx:908
bool beginPathDrag(SdrDragStat const &rDrag) const
Definition: svdopath.cxx:541
basegfx::B2DPoint const & getB2DPoint(sal_uInt32 nIndex) const