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