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