1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <o3tl/unit_conversion.hxx>
21 #include <tools/bigint.hxx>
22 #include <tools/helpers.hxx>
23 #include <svx/svdopath.hxx>
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>
34 #include <svx/polypolygoneditor.hxx>
35 #include <sdr/contact/viewcontactofsdrpathobj.hxx>
36 #include <basegfx/matrix/b2dhommatrix.hxx>
37 #include <basegfx/point/b2dpoint.hxx>
38 #include <basegfx/polygon/b2dpolypolygontools.hxx>
39 #include <basegfx/range/b2drange.hxx>
40 #include <basegfx/curve/b2dcubicbezier.hxx>
41 #include <basegfx/polygon/b2dpolygontools.hxx>
42 #include <sdr/attribute/sdrtextattribute.hxx>
43 #include <sdr/primitive2d/sdrattributecreator.hxx>
44 #include <basegfx/matrix/b2dhommatrixtools.hxx>
45 #include <sdr/attribute/sdrformtextattribute.hxx>
47 #include <vcl/ptrstyle.hxx>
49 #include <sal/log.hxx>
50 #include <osl/diagnose.h>
54 static sal_uInt16
GetPrevPnt(sal_uInt16 nPnt
, sal_uInt16 nPntMax
, bool bClosed
)
65 static sal_uInt16
GetNextPnt(sal_uInt16 nPnt
, sal_uInt16 nPntMax
, bool bClosed
)
68 if (nPnt
>nPntMax
|| (bClosed
&& nPnt
>=nPntMax
)) nPnt
=0;
74 struct ImpSdrPathDragData
: public SdrDragStatUserData
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
;
98 sal_uInt16 nNextNextPnt0
;
99 bool bEliminate
; // delete point? (is set by MovDrag)
101 bool mbMultiPointDrag
;
102 const XPolyPolygon maOrig
;
104 std::vector
<SdrHdl
*> maHandles
;
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
; }
114 ImpSdrPathDragData::ImpSdrPathDragData(const SdrPathObj
& rPO
, const SdrHdl
& rHdl
, bool bMuPoDr
, const SdrDragStat
& rDrag
)
125 , bPrevIsBegPnt(false)
126 , bNextIsEndPnt(false)
130 , bIsNextControl(false)
131 , bPrevIsControl(false)
132 , bNextIsControl(false)
139 , mbMultiPointDrag(bMuPoDr
)
140 , maOrig(rPO
.GetPathPoly())
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);
150 for(size_t a
= 0; a
< nHdlCount
; ++a
)
152 SdrHdl
* pTestHdl
= rHdlList
.GetHdl(a
);
154 if(pTestHdl
&& pTestHdl
->IsSelected() && pTestHdl
->GetObj() == pInteractionObject
)
156 maHandles
.push_back(pTestHdl
);
165 sal_uInt16 nPntMax
= 0; // maximum index
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;
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
195 bIsNextControl
=!aTmpXP
.IsControl(nPrevPnt
);
197 bPrevIsControl
=!bBegPnt
&& !bPrevIsBegPnt
&& aTmpXP
.GetFlags(nPrevPnt
)==PolyFlags::Control
;
198 bNextIsControl
=!bEndPnt
&& !bNextIsEndPnt
&& aTmpXP
.GetFlags(nNextPnt
)==PolyFlags::Control
;
200 nPrevPrevPnt0
=nPrevPrevPnt
;
204 nNextNextPnt0
=nNextNextPnt
;
216 void ImpSdrPathDragData::ResetPoly(const SdrPathObj
& rPO
)
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
));
228 struct ImpPathCreateUser
: public SdrDragStatUserData
243 tools::Long nCircRadius
;
244 Degree100 nCircStAngle
;
245 Degree100 nCircRelAngle
;
254 sal_uInt16 nBezierStartPoint
;
255 SdrObjKind eStartKind
;
256 SdrObjKind eCurrentKind
;
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
) { }
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;
279 XPolygon
ImpPathCreateUser::GetFormPoly() const
281 if (bBezier
) return GetBezierPoly();
282 if (bCircle
) return GetCirclePoly();
283 if (bLine
) return GetLinePoly();
284 if (bRect
) return GetRectPoly();
288 void ImpPathCreateUser::CalcBezier(const Point
& rP1
, const Point
& rP2
, const Point
& rDir
, bool bMouseDown
)
295 // Also copy the end point when no end point is set yet
296 if (!bMouseDown
|| (0 == aBezEnd
.X() && 0 == aBezEnd
.Y())) aBezEnd
=rP2
;
301 XPolygon
ImpPathCreateUser::GetBezierPoly() const
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
);
311 void ImpPathCreateUser::CalcCircle(const Point
& rP1
, const Point
& rP2
, const Point
& rDir
, SdrView
const * pView
)
313 Degree100 nTangAngle
=GetAngle(rDir
);
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
;
325 double cs
= cos(toRadians(nTmpAngle
));
326 double nR
=static_cast<double>(GetLen(Point(dx
,dy
)))/cs
/2;
327 nRad
= std::abs(basegfx::fround
<tools::Long
>(nR
));
329 if (dAngle
<18000_deg100
) {
330 nCircStAngle
=NormAngle36000(nTangAngle
-9000_deg100
);
331 nCircRelAngle
=NormAngle36000(2_deg100
*dAngle
);
332 aCircCenter
.AdjustX(basegfx::fround
<tools::Long
>(nRad
* cos(toRadians(nTangAngle
+ 9000_deg100
))));
333 aCircCenter
.AdjustY(basegfx::fround
<tools::Long
>(nRad
* -sin(toRadians(nTangAngle
+ 9000_deg100
))));
335 nCircStAngle
=NormAngle36000(nTangAngle
+9000_deg100
);
336 nCircRelAngle
=-NormAngle36000(36000_deg100
-2_deg100
*dAngle
);
337 aCircCenter
.AdjustX(basegfx::fround
<tools::Long
>(nRad
* cos(toRadians(nTangAngle
- 9000_deg100
))));
338 aCircCenter
.AdjustY(basegfx::fround
<tools::Long
>(nRad
* -sin(toRadians(nTangAngle
- 9000_deg100
))));
340 bAngleSnap
=pView
!=nullptr && pView
->IsAngleSnapEnabled();
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
;
349 nCircRelAngle
=NormAngle36000(nCircRelAngle
);
350 if (bNeg
) nCircRelAngle
=-nCircRelAngle
;
354 if (nRad
==0 || abs(nCircRelAngle
).get()<5) bRet
=false;
358 XPolygon
ImpPathCreateUser::GetCirclePoly() const
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
;
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
]);
377 aXP
[0]=aCircStart
; aXP
.SetFlags(0,PolyFlags::Smooth
);
378 if (!bAngleSnap
) aXP
[aXP
.GetPointCount()-1]=aCircEnd
;
383 Point
ImpPathCreateUser::CalcLine(const Point
& aCsr
, tools::Long nDirX
, tools::Long nDirY
, SdrView
const * pView
)
385 tools::Long x
=aCsr
.X();
386 tools::Long y
=aCsr
.Y();
392 tools::Long x2
=BigMulDiv(y
,nDirX
,nDirY
);
393 tools::Long y2
=BigMulDiv(x
,nDirY
,nDirX
);
394 tools::Long l1
=std::abs(x2
)+std::abs(y
);
395 tools::Long l2
=std::abs(x
)+std::abs(y2
);
396 if ((l1
<=l2
) != (pView
!=nullptr && pView
->IsBigOrtho())) {
405 void ImpPathCreateUser::CalcLine(const Point
& rP1
, const Point
& rP2
, const Point
& rDir
, SdrView
const * pView
)
410 if (rP1
==rP2
|| (rDir
.X()==0 && rDir
.Y()==0)) { bLine
=false; return; }
411 Point
aTmpPt(rP2
-rP1
);
412 tools::Long nDirX
=rDir
.X();
413 tools::Long nDirY
=rDir
.Y();
414 Point
aP1(CalcLine(aTmpPt
, nDirX
, nDirY
,pView
)); aP1
-=aTmpPt
; tools::Long nQ1
=std::abs(aP1
.X())+std::abs(aP1
.Y());
415 Point
aP2(CalcLine(aTmpPt
, nDirY
,-nDirX
,pView
)); aP2
-=aTmpPt
; tools::Long nQ2
=std::abs(aP2
.X())+std::abs(aP2
.Y());
416 if (pView
!=nullptr && pView
->IsOrtho()) nQ1
=0; // Ortho turns off at right angle
418 if (!bLine90
) { // smooth transition
420 } else { // rectangular transition
426 XPolygon
ImpPathCreateUser::GetLinePoly() const
429 aXP
[0]=aLineStart
; if (!bLine90
) aXP
.SetFlags(0,PolyFlags::Smooth
);
434 void ImpPathCreateUser::CalcRect(const Point
& rP1
, const Point
& rP2
, const Point
& rDir
, SdrView
const * pView
)
439 if (rP1
==rP2
|| (rDir
.X()==0 && rDir
.Y()==0)) { bRect
=false; return; }
440 Point
aTmpPt(rP2
-rP1
);
441 tools::Long nDirX
=rDir
.X();
442 tools::Long nDirY
=rDir
.Y();
443 tools::Long x
=aTmpPt
.X();
444 tools::Long y
=aTmpPt
.Y();
450 y
=BigMulDiv(x
,nDirY
,nDirX
);
451 tools::Long nHypLen
=aTmpPt
.Y()-y
;
452 Degree100 nTangAngle
=-GetAngle(rDir
);
454 double a
= toRadians(nTangAngle
);
457 double nGKathLen
=nHypLen
*sn
;
458 y
+= basegfx::fround
<tools::Long
>(nGKathLen
* sn
);
459 x
+= basegfx::fround
<tools::Long
>(nGKathLen
* cs
);
463 if (pView
!=nullptr && pView
->IsOrtho()) {
464 tools::Long dx1
=aRectP2
.X()-aRectP1
.X(); tools::Long dx1a
=std::abs(dx1
);
465 tools::Long dy1
=aRectP2
.Y()-aRectP1
.Y(); tools::Long dy1a
=std::abs(dy1
);
466 tools::Long dx2
=aRectP3
.X()-aRectP2
.X(); tools::Long dx2a
=std::abs(dx2
);
467 tools::Long dy2
=aRectP3
.Y()-aRectP2
.Y(); tools::Long dy2a
=std::abs(dy2
);
468 bool b1MoreThan2
=dx1a
+dy1a
>dx2a
+dy2a
;
469 if (b1MoreThan2
!= pView
->IsBigOrtho()) {
470 tools::Long xtemp
=dy2a
-dx1a
; if (dx1
<0) xtemp
=-xtemp
;
471 tools::Long ytemp
=dx2a
-dy1a
; if (dy1
<0) ytemp
=-ytemp
;
472 aRectP2
.AdjustX(xtemp
);
473 aRectP2
.AdjustY(ytemp
);
474 aRectP3
.AdjustX(xtemp
);
475 aRectP3
.AdjustY(ytemp
);
477 tools::Long xtemp
=dy1a
-dx2a
; if (dx2
<0) xtemp
=-xtemp
;
478 tools::Long ytemp
=dx1a
-dy2a
; if (dy2
<0) ytemp
=-ytemp
;
479 aRectP3
.AdjustX(xtemp
);
480 aRectP3
.AdjustY(ytemp
);
486 XPolygon
ImpPathCreateUser::GetRectPoly() const
489 aXP
[0]=aRectP1
; aXP
.SetFlags(0,PolyFlags::Smooth
);
491 if (aRectP3
!=aRectP2
) aXP
[2]=aRectP3
;
495 class ImpPathForDragAndCreate
497 SdrPathObj
& mrSdrPathObject
;
498 XPolyPolygon aPathPolygon
;
499 SdrObjKind meObjectKind
;
500 std::unique_ptr
<ImpSdrPathDragData
>
505 explicit ImpPathForDragAndCreate(SdrPathObj
& rSdrPathObject
);
508 bool beginPathDrag( SdrDragStat
const & rDrag
) const;
509 bool movePathDrag( SdrDragStat
& rDrag
) const;
510 bool endPathDrag( SdrDragStat
const & rDrag
);
511 OUString
getSpecialDragComment(const SdrDragStat
& rDrag
) const;
512 basegfx::B2DPolyPolygon
getSpecialDragPoly(const SdrDragStat
& rDrag
) const;
515 void BegCreate(SdrDragStat
& rStat
);
516 bool MovCreate(SdrDragStat
& rStat
);
517 bool EndCreate(SdrDragStat
& rStat
, SdrCreateCmd eCmd
);
518 bool BckCreate(SdrDragStat
const & rStat
);
519 void BrkCreate(SdrDragStat
& rStat
);
520 PointerStyle
GetCreatePointer() const;
523 static bool IsClosed(SdrObjKind eKind
) { return eKind
==SdrObjKind::Polygon
|| eKind
==SdrObjKind::PathPoly
|| eKind
==SdrObjKind::PathFill
|| eKind
==SdrObjKind::FreehandFill
; }
524 static bool IsFreeHand(SdrObjKind eKind
) { return eKind
==SdrObjKind::FreehandLine
|| eKind
==SdrObjKind::FreehandFill
; }
525 static bool IsBezier(SdrObjKind eKind
) { return eKind
==SdrObjKind::PathLine
|| eKind
==SdrObjKind::PathFill
; }
526 bool IsCreating() const { return mbCreating
; }
529 basegfx::B2DPolyPolygon
TakeObjectPolyPolygon(const SdrDragStat
& rDrag
) const;
530 static basegfx::B2DPolyPolygon
TakeDragPolyPolygon(const SdrDragStat
& rDrag
);
531 basegfx::B2DPolyPolygon
getModifiedPolyPolygon() const { return aPathPolygon
.getB2DPolyPolygon(); }
534 ImpPathForDragAndCreate::ImpPathForDragAndCreate(SdrPathObj
& rSdrPathObject
)
535 : mrSdrPathObject(rSdrPathObject
),
536 aPathPolygon(rSdrPathObject
.GetPathPoly()),
537 meObjectKind(mrSdrPathObject
.meKind
),
542 bool ImpPathForDragAndCreate::beginPathDrag( SdrDragStat
const & rDrag
) const
544 const SdrHdl
* pHdl
=rDrag
.GetHdl();
548 bool bMultiPointDrag(true);
550 if(aPathPolygon
[static_cast<sal_uInt16
>(pHdl
->GetPolyNum())].IsControl(static_cast<sal_uInt16
>(pHdl
->GetPointNum())))
551 bMultiPointDrag
= false;
555 const SdrMarkView
& rMarkView
= *rDrag
.GetView();
556 const SdrHdlList
& rHdlList
= rMarkView
.GetHdlList();
557 const size_t nHdlCount
= rHdlList
.GetHdlCount();
558 const SdrObject
* pInteractionObject(nHdlCount
&& rHdlList
.GetHdl(0) ? rHdlList
.GetHdl(0)->GetObj() : nullptr);
559 sal_uInt32
nSelectedPoints(0);
561 for(size_t a
= 0; a
< nHdlCount
; ++a
)
563 SdrHdl
* pTestHdl
= rHdlList
.GetHdl(a
);
565 if(pTestHdl
&& pTestHdl
->IsSelected() && pTestHdl
->GetObj() == pInteractionObject
)
571 if(nSelectedPoints
<= 1)
572 bMultiPointDrag
= false;
575 const_cast<ImpPathForDragAndCreate
*>(this)->mpSdrPathDragData
.reset( new ImpSdrPathDragData(mrSdrPathObject
,*pHdl
,bMultiPointDrag
,rDrag
) );
577 if(!mpSdrPathDragData
|| !mpSdrPathDragData
->bValid
)
579 OSL_FAIL("ImpPathForDragAndCreate::BegDrag(): ImpSdrPathDragData is invalid.");
580 const_cast<ImpPathForDragAndCreate
*>(this)->mpSdrPathDragData
.reset();
587 bool ImpPathForDragAndCreate::movePathDrag( SdrDragStat
& rDrag
) const
589 if(!mpSdrPathDragData
|| !mpSdrPathDragData
->bValid
)
591 OSL_FAIL("ImpPathForDragAndCreate::MovDrag(): ImpSdrPathDragData is invalid.");
595 if(mpSdrPathDragData
->IsMultiPointDrag())
597 Point
aDelta(rDrag
.GetNow() - rDrag
.GetStart());
599 if(aDelta
.X() || aDelta
.Y())
601 for(SdrHdl
* pHandle
: mpSdrPathDragData
->maHandles
)
603 const sal_uInt16
nPolyIndex(static_cast<sal_uInt16
>(pHandle
->GetPolyNum()));
604 const sal_uInt16
nPointIndex(static_cast<sal_uInt16
>(pHandle
->GetPointNum()));
605 const XPolygon
& rOrig
= mpSdrPathDragData
->maOrig
[nPolyIndex
];
606 XPolygon
& rMove
= mpSdrPathDragData
->maMove
[nPolyIndex
];
607 const sal_uInt16
nPointCount(rOrig
.GetPointCount());
608 bool bClosed(rOrig
[0] == rOrig
[nPointCount
-1]);
611 rMove
[nPointIndex
] = rOrig
[nPointIndex
] + aDelta
;
613 // when point is first and poly closed, move close point, too.
614 if(nPointCount
> 0 && !nPointIndex
&& bClosed
)
616 rMove
[nPointCount
- 1] = rOrig
[nPointCount
- 1] + aDelta
;
618 // when moving the last point it may be necessary to move the
619 // control point in front of this one, too.
620 if(nPointCount
> 1 && rOrig
.IsControl(nPointCount
- 2))
621 rMove
[nPointCount
- 2] = rOrig
[nPointCount
- 2] + aDelta
;
624 // is a control point before this?
625 if(nPointIndex
> 0 && rOrig
.IsControl(nPointIndex
- 1))
628 rMove
[nPointIndex
- 1] = rOrig
[nPointIndex
- 1] + aDelta
;
631 // is a control point after this?
632 if(nPointIndex
+ 1 < nPointCount
&& rOrig
.IsControl(nPointIndex
+ 1))
635 rMove
[nPointIndex
+ 1] = rOrig
[nPointIndex
+ 1] + aDelta
;
642 mpSdrPathDragData
->ResetPoly(mrSdrPathObject
);
644 // copy certain data locally to use less code and have faster access times
645 bool bClosed
=mpSdrPathDragData
->bClosed
; // closed object?
646 sal_uInt16 nPnt
=mpSdrPathDragData
->nPnt
; // number of point in the above polygon
647 bool bBegPnt
=mpSdrPathDragData
->bBegPnt
; // dragged point is first point of a Polyline
648 bool bEndPnt
=mpSdrPathDragData
->bEndPnt
; // dragged point is last point of a Polyline
649 sal_uInt16 nPrevPnt
=mpSdrPathDragData
->nPrevPnt
; // index of previous point
650 sal_uInt16 nNextPnt
=mpSdrPathDragData
->nNextPnt
; // index of next point
651 bool bPrevIsBegPnt
=mpSdrPathDragData
->bPrevIsBegPnt
; // previous point is first point of a Polyline
652 bool bNextIsEndPnt
=mpSdrPathDragData
->bNextIsEndPnt
; // next point is last point of a Polyline
653 sal_uInt16 nPrevPrevPnt
=mpSdrPathDragData
->nPrevPrevPnt
; // index of the point before the previous point
654 sal_uInt16 nNextNextPnt
=mpSdrPathDragData
->nNextNextPnt
; // index if the point after the next point
655 bool bControl
=mpSdrPathDragData
->bControl
; // point is a control point
656 bool bIsNextControl
=mpSdrPathDragData
->bIsNextControl
; // point is a control point after a support point
657 bool bPrevIsControl
=mpSdrPathDragData
->bPrevIsControl
; // if nPnt is a support point: there's a control point before
658 bool bNextIsControl
=mpSdrPathDragData
->bNextIsControl
; // if nPnt is a support point: there's a control point after
660 // Ortho for lines/polygons: keep angle
661 if (!bControl
&& rDrag
.GetView()!=nullptr && rDrag
.GetView()->IsOrtho()) {
662 bool bBigOrtho
=rDrag
.GetView()->IsBigOrtho();
663 Point
aPos(rDrag
.GetNow()); // current position
664 Point
aPnt(mpSdrPathDragData
->aXP
[nPnt
]); // the dragged point
665 sal_uInt16 nPnt1
=0xFFFF,nPnt2
=0xFFFF; // its neighboring points
666 Point aNewPos1
,aNewPos2
; // new alternative for aPos
667 bool bPnt1
= false, bPnt2
= false; // are these valid alternatives?
668 if (!bClosed
&& mpSdrPathDragData
->nPointCount
>=2) { // minimum of 2 points for lines
669 if (!bBegPnt
) nPnt1
=nPrevPnt
;
670 if (!bEndPnt
) nPnt2
=nNextPnt
;
672 if (bClosed
&& mpSdrPathDragData
->nPointCount
>=3) { // minimum of 3 points for polygon
676 if (nPnt1
!=0xFFFF && !bPrevIsControl
) {
677 Point aPnt1
=mpSdrPathDragData
->aXP
[nPnt1
];
678 tools::Long ndx0
=aPnt
.X()-aPnt1
.X();
679 tools::Long ndy0
=aPnt
.Y()-aPnt1
.Y();
682 if (!bHLin
|| !bVLin
) {
683 tools::Long ndx
=aPos
.X()-aPnt1
.X();
684 tools::Long ndy
=aPos
.Y()-aPnt1
.Y();
686 double nXFact
=0; if (!bVLin
) nXFact
=static_cast<double>(ndx
)/static_cast<double>(ndx0
);
687 double nYFact
=0; if (!bHLin
) nYFact
=static_cast<double>(ndy
)/static_cast<double>(ndy0
);
688 bool bHor
=bHLin
|| (!bVLin
&& (nXFact
>nYFact
) ==bBigOrtho
);
689 bool bVer
=bVLin
|| (!bHLin
&& (nXFact
<=nYFact
)==bBigOrtho
);
690 if (bHor
) ndy
=tools::Long(ndy0
*nXFact
);
691 if (bVer
) ndx
=tools::Long(ndx0
*nYFact
);
693 aNewPos1
.AdjustX(ndx
);
694 aNewPos1
.AdjustY(ndy
);
697 if (nPnt2
!=0xFFFF && !bNextIsControl
) {
698 Point aPnt2
=mpSdrPathDragData
->aXP
[nPnt2
];
699 tools::Long ndx0
=aPnt
.X()-aPnt2
.X();
700 tools::Long ndy0
=aPnt
.Y()-aPnt2
.Y();
703 if (!bHLin
|| !bVLin
) {
704 tools::Long ndx
=aPos
.X()-aPnt2
.X();
705 tools::Long ndy
=aPos
.Y()-aPnt2
.Y();
707 double nXFact
=0; if (!bVLin
) nXFact
=static_cast<double>(ndx
)/static_cast<double>(ndx0
);
708 double nYFact
=0; if (!bHLin
) nYFact
=static_cast<double>(ndy
)/static_cast<double>(ndy0
);
709 bool bHor
=bHLin
|| (!bVLin
&& (nXFact
>nYFact
) ==bBigOrtho
);
710 bool bVer
=bVLin
|| (!bHLin
&& (nXFact
<=nYFact
)==bBigOrtho
);
711 if (bHor
) ndy
=tools::Long(ndy0
*nXFact
);
712 if (bVer
) ndx
=tools::Long(ndx0
*nYFact
);
714 aNewPos2
.AdjustX(ndx
);
715 aNewPos2
.AdjustY(ndy
);
718 if (bPnt1
&& bPnt2
) { // both alternatives exist (and compete)
719 BigInt
nX1(aNewPos1
.X()-aPos
.X()); nX1
*=nX1
;
720 BigInt
nY1(aNewPos1
.Y()-aPos
.Y()); nY1
*=nY1
;
721 BigInt
nX2(aNewPos2
.X()-aPos
.X()); nX2
*=nX2
;
722 BigInt
nY2(aNewPos2
.Y()-aPos
.Y()); nY2
*=nY2
;
723 nX1
+=nY1
; // correction distance to square
724 nX2
+=nY2
; // correction distance to square
725 // let the alternative that allows fewer correction win
726 if (nX1
<nX2
) bPnt2
=false; else bPnt1
=false;
728 if (bPnt1
) rDrag
.SetNow(aNewPos1
);
729 if (bPnt2
) rDrag
.SetNow(aNewPos2
);
731 rDrag
.SetActionRect(tools::Rectangle(rDrag
.GetNow(),rDrag
.GetNow()));
733 // specially for IBM: Eliminate points if both adjoining lines form near 180 degrees angle anyway
734 if (!bControl
&& rDrag
.GetView()!=nullptr && rDrag
.GetView()->IsEliminatePolyPoints() &&
735 !bBegPnt
&& !bEndPnt
&& !bPrevIsControl
&& !bNextIsControl
)
737 Point
aPt(mpSdrPathDragData
->aXP
[nNextPnt
]);
739 Degree100 nAngle1
=GetAngle(aPt
);
741 aPt
-=mpSdrPathDragData
->aXP
[nPrevPnt
];
742 Degree100 nAngle2
=GetAngle(aPt
);
743 Degree100 nDiff
=nAngle1
-nAngle2
;
745 mpSdrPathDragData
->bEliminate
=nDiff
<=rDrag
.GetView()->GetEliminatePolyPointLimitAngle();
746 if (mpSdrPathDragData
->bEliminate
) { // adapt position, Smooth is true for the ends
747 aPt
=mpSdrPathDragData
->aXP
[nNextPnt
];
748 aPt
+=mpSdrPathDragData
->aXP
[nPrevPnt
];
754 // we dragged by this distance
755 Point
aDiff(rDrag
.GetNow()); aDiff
-=mpSdrPathDragData
->aXP
[nPnt
];
757 /* There are 8 possible cases:
758 X 1. A control point neither on the left nor on the right.
759 o--X--o 2. There are control points on the left and the right, we are dragging a support point.
760 o--X 3. There is a control point on the left, we are dragging a support point.
761 X--o 4. There is a control point on the right, we are dragging a support point.
762 x--O--o 5. There are control points on the left and the right, we are dragging the left one.
763 x--O 6. There is a control point on the left, we are dragging it.
764 o--O--x 7. There are control points on the left and the right, we are dragging the right one.
765 O--x 8. There is a control point on the right, we are dragging it.
766 Note: modifying a line (not a curve!) might create a curve on the other end of the line
767 if Smooth is set there (with control points aligned to line).
770 mpSdrPathDragData
->aXP
[nPnt
]+=aDiff
;
772 // now check symmetric plus handles
773 if (bControl
) { // cases 5,6,7,8
774 sal_uInt16 nSt
; // the associated support point
775 sal_uInt16 nFix
; // the opposing control point
776 if (bIsNextControl
) { // if the next one is a control point, the on before has to be a support point
783 if (mpSdrPathDragData
->aXP
.IsSmooth(nSt
)) {
784 mpSdrPathDragData
->aXP
.CalcSmoothJoin(nSt
,nPnt
,nFix
);
788 if (!bControl
) { // Cases 1,2,3,4. In case 1, nothing happens; in cases 3 and 4, there is more following below.
789 // move both control points
790 if (bPrevIsControl
) mpSdrPathDragData
->aXP
[nPrevPnt
]+=aDiff
;
791 if (bNextIsControl
) mpSdrPathDragData
->aXP
[nNextPnt
]+=aDiff
;
792 // align control point to line, if appropriate
793 if (mpSdrPathDragData
->aXP
.IsSmooth(nPnt
)) {
794 if (bPrevIsControl
&& !bNextIsControl
&& !bEndPnt
) { // case 3
795 mpSdrPathDragData
->aXP
.CalcSmoothJoin(nPnt
,nNextPnt
,nPrevPnt
);
797 if (bNextIsControl
&& !bPrevIsControl
&& !bBegPnt
) { // case 4
798 mpSdrPathDragData
->aXP
.CalcSmoothJoin(nPnt
,nPrevPnt
,nNextPnt
);
801 // Now check the other ends of the line (nPnt+-1). If there is a
802 // curve (IsControl(nPnt+-2)) with SmoothJoin (nPnt+-1), the
803 // associated control point (nPnt+-2) has to be adapted.
804 if (!bBegPnt
&& !bPrevIsControl
&& !bPrevIsBegPnt
&& mpSdrPathDragData
->aXP
.IsSmooth(nPrevPnt
)) {
805 if (mpSdrPathDragData
->aXP
.IsControl(nPrevPrevPnt
)) {
806 mpSdrPathDragData
->aXP
.CalcSmoothJoin(nPrevPnt
,nPnt
,nPrevPrevPnt
);
809 if (!bEndPnt
&& !bNextIsControl
&& !bNextIsEndPnt
&& mpSdrPathDragData
->aXP
.IsSmooth(nNextPnt
)) {
810 if (mpSdrPathDragData
->aXP
.IsControl(nNextNextPnt
)) {
811 mpSdrPathDragData
->aXP
.CalcSmoothJoin(nNextPnt
,nPnt
,nNextNextPnt
);
820 bool ImpPathForDragAndCreate::endPathDrag(SdrDragStat
const & rDrag
)
824 bool bLineGlueMirror(SdrObjKind::Line
== meObjectKind
);
825 if (bLineGlueMirror
) {
826 XPolygon
& rXP
=aPathPolygon
[0];
831 if(!mpSdrPathDragData
|| !mpSdrPathDragData
->bValid
)
833 OSL_FAIL("ImpPathForDragAndCreate::MovDrag(): ImpSdrPathDragData is invalid.");
837 if(mpSdrPathDragData
->IsMultiPointDrag())
839 aPathPolygon
= mpSdrPathDragData
->maMove
;
843 const SdrHdl
* pHdl
=rDrag
.GetHdl();
845 // reference the polygon
846 XPolygon
& rXP
=aPathPolygon
[static_cast<sal_uInt16
>(pHdl
->GetPolyNum())];
848 // the 5 points that might have changed
849 if (!mpSdrPathDragData
->bPrevIsBegPnt
) rXP
[mpSdrPathDragData
->nPrevPrevPnt0
]=mpSdrPathDragData
->aXP
[mpSdrPathDragData
->nPrevPrevPnt
];
850 if (!mpSdrPathDragData
->bNextIsEndPnt
) rXP
[mpSdrPathDragData
->nNextNextPnt0
]=mpSdrPathDragData
->aXP
[mpSdrPathDragData
->nNextNextPnt
];
851 if (!mpSdrPathDragData
->bBegPnt
) rXP
[mpSdrPathDragData
->nPrevPnt0
] =mpSdrPathDragData
->aXP
[mpSdrPathDragData
->nPrevPnt
];
852 if (!mpSdrPathDragData
->bEndPnt
) rXP
[mpSdrPathDragData
->nNextPnt0
] =mpSdrPathDragData
->aXP
[mpSdrPathDragData
->nNextPnt
];
853 rXP
[mpSdrPathDragData
->nPnt0
] =mpSdrPathDragData
->aXP
[mpSdrPathDragData
->nPnt
];
855 // for closed objects: last point has to be equal to first point
856 if (mpSdrPathDragData
->bClosed
) rXP
[rXP
.GetPointCount()-1]=rXP
[0];
858 if (mpSdrPathDragData
->bEliminate
)
860 basegfx::B2DPolyPolygon
aTempPolyPolygon(aPathPolygon
.getB2DPolyPolygon());
861 sal_uInt32 nPoly
,nPnt
;
863 if(PolyPolygonEditor::GetRelativePolyPoint(aTempPolyPolygon
, rDrag
.GetHdl()->GetSourceHdlNum(), nPoly
, nPnt
))
865 basegfx::B2DPolygon
aCandidate(aTempPolyPolygon
.getB2DPolygon(nPoly
));
866 aCandidate
.remove(nPnt
);
868 if(aCandidate
.count() < 2)
870 aTempPolyPolygon
.remove(nPoly
);
874 aTempPolyPolygon
.setB2DPolygon(nPoly
, aCandidate
);
878 aPathPolygon
= XPolyPolygon(aTempPolyPolygon
);
881 // adapt angle for text beneath a simple line
884 Point
aLinePt1_(aPathPolygon
[0][0]);
885 Point
aLinePt2_(aPathPolygon
[0][1]);
886 bool bXMirr
=(aLinePt1_
.X()>aLinePt2_
.X())!=(aLinePt1
.X()>aLinePt2
.X());
887 bool bYMirr
=(aLinePt1_
.Y()>aLinePt2_
.Y())!=(aLinePt1
.Y()>aLinePt2
.Y());
888 if (bXMirr
|| bYMirr
) {
889 Point
aRef1(mrSdrPathObject
.GetSnapRect().Center());
893 mrSdrPathObject
.NbcMirrorGluePoints(aRef1
,aRef2
);
898 mrSdrPathObject
.NbcMirrorGluePoints(aRef1
,aRef2
);
904 mpSdrPathDragData
.reset();
909 OUString
ImpPathForDragAndCreate::getSpecialDragComment(const SdrDragStat
& rDrag
) const
912 const SdrHdl
* pHdl
= rDrag
.GetHdl();
913 const bool bCreateComment(rDrag
.GetView() && &mrSdrPathObject
== rDrag
.GetView()->GetCreateObj());
915 if(bCreateComment
&& rDrag
.GetUser())
917 // #i103058# re-add old creation comment mode
918 const ImpPathCreateUser
* pU
= static_cast<const ImpPathCreateUser
*>(rDrag
.GetUser());
919 const SdrObjKind
eOriginalKind(meObjectKind
);
920 mrSdrPathObject
.meKind
= pU
->eCurrentKind
;
921 aStr
= mrSdrPathObject
.ImpGetDescriptionStr(STR_ViewCreateObj
);
922 mrSdrPathObject
.meKind
= eOriginalKind
;
924 Point
aPrev(rDrag
.GetPrev());
925 Point
aNow(rDrag
.GetNow());
935 aStr
+= SdrModel::GetAngleString(abs(pU
->nCircRelAngle
))
937 + mrSdrPathObject
.getSdrModelFromSdrObject().GetMetricString(pU
->nCircRadius
, true);
941 + mrSdrPathObject
.getSdrModelFromSdrObject().GetMetricString(aNow
.X(), true)
943 + mrSdrPathObject
.getSdrModelFromSdrObject().GetMetricString(aNow
.Y(), true);
945 if(!IsFreeHand(meObjectKind
))
947 sal_Int32
nLen(GetLen(aNow
));
948 Degree100
nAngle(GetAngle(aNow
));
950 + mrSdrPathObject
.getSdrModelFromSdrObject().GetMetricString(nLen
, true)
952 + SdrModel::GetAngleString(nAngle
);
959 // #i103058# fallback when no model and/or Handle, both needed
961 aStr
= mrSdrPathObject
.ImpGetDescriptionStr(STR_DragPathObj
);
965 // #i103058# standard for modification; model and handle needed
966 ImpSdrPathDragData
* pDragData
= mpSdrPathDragData
.get();
970 // getSpecialDragComment is also used from create, so fallback to GetUser()
971 // when mpSdrPathDragData is not set
972 pDragData
= static_cast<ImpSdrPathDragData
*>(rDrag
.GetUser());
977 OSL_FAIL("ImpPathForDragAndCreate::MovDrag(): ImpSdrPathDragData is invalid.");
981 if(!pDragData
->IsMultiPointDrag() && pDragData
->bEliminate
)
984 aStr
= mrSdrPathObject
.ImpGetDescriptionStr(STR_ViewMarkedPoint
);
987 OUString
aStr2(SvxResId(STR_EditDelete
));
989 // UNICODE: delete point of ...
990 aStr2
= aStr2
.replaceFirst("%1", aStr
);
995 // dx=0.00 dy=0.00 -- both sides bezier
996 // dx=0.00 dy=0.00 l=0.00 0.00\302\260 -- one bezier/lever on one side, a start, or an ending
997 // dx=0.00 dy=0.00 l=0.00 0.00\302\260 / l=0.00 0.00\302\260 -- in between
998 Point
aBeg(rDrag
.GetStart());
999 Point
aNow(rDrag
.GetNow());
1003 + mrSdrPathObject
.getSdrModelFromSdrObject().GetMetricString(aNow
.X() - aBeg
.X(), true)
1005 + mrSdrPathObject
.getSdrModelFromSdrObject().GetMetricString(aNow
.Y() - aBeg
.Y(), true);
1007 if(!pDragData
->IsMultiPointDrag())
1009 sal_uInt16
nPntNum(static_cast<sal_uInt16
>(pHdl
->GetPointNum()));
1010 const XPolygon
& rXPoly
= aPathPolygon
[static_cast<sal_uInt16
>(rDrag
.GetHdl()->GetPolyNum())];
1011 sal_uInt16
nPointCount(rXPoly
.GetPointCount());
1012 bool bClose(IsClosed(meObjectKind
));
1017 if(pHdl
->IsPlusHdl())
1020 sal_uInt16
nRef(nPntNum
);
1022 if(rXPoly
.IsControl(nPntNum
+ 1))
1027 aNow
-= rXPoly
[nRef
];
1029 sal_Int32
nLen(GetLen(aNow
));
1030 Degree100
nAngle(GetAngle(aNow
));
1032 + mrSdrPathObject
.getSdrModelFromSdrObject().GetMetricString(nLen
, true)
1034 + SdrModel::GetAngleString(nAngle
);
1036 else if(nPointCount
> 1)
1038 sal_uInt16
nPntMax(nPointCount
- 1);
1039 bool bIsClosed(IsClosed(meObjectKind
));
1040 bool bPt1(nPntNum
> 0);
1041 bool bPt2(nPntNum
< nPntMax
);
1043 if(bIsClosed
&& nPointCount
> 2)
1049 sal_uInt16 nPt1
,nPt2
;
1056 if(nPntNum
< nPntMax
)
1061 if(bPt1
&& rXPoly
.IsControl(nPt1
))
1062 bPt1
= false; // don't display
1064 if(bPt2
&& rXPoly
.IsControl(nPt2
))
1065 bPt2
= false; // of bezier data
1070 aPt
-= rXPoly
[nPt1
];
1072 sal_Int32
nLen(GetLen(aPt
));
1073 Degree100
nAngle(GetAngle(aPt
));
1075 + mrSdrPathObject
.getSdrModelFromSdrObject().GetMetricString(nLen
, true)
1077 + SdrModel::GetAngleString(nAngle
);
1088 aPt
-= rXPoly
[nPt2
];
1090 sal_Int32
nLen(GetLen(aPt
));
1091 Degree100
nAngle(GetAngle(aPt
));
1093 + mrSdrPathObject
.getSdrModelFromSdrObject().GetMetricString(nLen
, true)
1095 + SdrModel::GetAngleString(nAngle
);
1104 basegfx::B2DPolyPolygon
ImpPathForDragAndCreate::getSpecialDragPoly(const SdrDragStat
& rDrag
) const
1106 if(!mpSdrPathDragData
|| !mpSdrPathDragData
->bValid
)
1108 OSL_FAIL("ImpPathForDragAndCreate::MovDrag(): ImpSdrPathDragData is invalid.");
1109 return basegfx::B2DPolyPolygon();
1112 XPolyPolygon aRetval
;
1114 if(mpSdrPathDragData
->IsMultiPointDrag())
1116 aRetval
.Insert(mpSdrPathDragData
->maMove
);
1120 const XPolygon
& rXP
=aPathPolygon
[static_cast<sal_uInt16
>(rDrag
.GetHdl()->GetPolyNum())];
1121 if (rXP
.GetPointCount()<=2) {
1122 XPolygon
aXPoly(rXP
);
1123 aXPoly
[static_cast<sal_uInt16
>(rDrag
.GetHdl()->GetPointNum())]=rDrag
.GetNow();
1124 aRetval
.Insert(std::move(aXPoly
));
1125 return aRetval
.getB2DPolyPolygon();
1127 // copy certain data locally to use less code and have faster access times
1128 bool bClosed
=mpSdrPathDragData
->bClosed
; // closed object?
1129 sal_uInt16 nPointCount
= mpSdrPathDragData
->nPointCount
; // number of points
1130 sal_uInt16 nPnt
=mpSdrPathDragData
->nPnt
; // number of points in the polygon
1131 bool bBegPnt
=mpSdrPathDragData
->bBegPnt
; // dragged point is the first point of a Polyline
1132 bool bEndPnt
=mpSdrPathDragData
->bEndPnt
; // dragged point is the last point of a Polyline
1133 sal_uInt16 nPrevPnt
=mpSdrPathDragData
->nPrevPnt
; // index of the previous point
1134 sal_uInt16 nNextPnt
=mpSdrPathDragData
->nNextPnt
; // index of the next point
1135 bool bPrevIsBegPnt
=mpSdrPathDragData
->bPrevIsBegPnt
; // previous point is first point of a Polyline
1136 bool bNextIsEndPnt
=mpSdrPathDragData
->bNextIsEndPnt
; // next point is last point of a Polyline
1137 sal_uInt16 nPrevPrevPnt
=mpSdrPathDragData
->nPrevPrevPnt
; // index of the point before the previous point
1138 sal_uInt16 nNextNextPnt
=mpSdrPathDragData
->nNextNextPnt
; // index of the point after the last point
1139 bool bControl
=mpSdrPathDragData
->bControl
; // point is a control point
1140 bool bIsNextControl
=mpSdrPathDragData
->bIsNextControl
; //point is a control point after a support point
1141 bool bPrevIsControl
=mpSdrPathDragData
->bPrevIsControl
; // if nPnt is a support point: there's a control point before
1142 bool bNextIsControl
=mpSdrPathDragData
->bNextIsControl
; // if nPnt is a support point: there's a control point after
1143 XPolygon
aXPoly(mpSdrPathDragData
->aXP
);
1149 aLine1
[1]=mpSdrPathDragData
->aXP
[nPnt
];
1150 if (bIsNextControl
) { // is this a control point after the support point?
1151 aLine1
[0]=mpSdrPathDragData
->aXP
[nPrevPnt
];
1152 aLine2
[0]=mpSdrPathDragData
->aXP
[nNextNextPnt
];
1153 aLine2
[1]=mpSdrPathDragData
->aXP
[nNextPnt
];
1154 if (mpSdrPathDragData
->aXP
.IsSmooth(nPrevPnt
) && !bPrevIsBegPnt
&& mpSdrPathDragData
->aXP
.IsControl(nPrevPrevPnt
)) {
1155 aXPoly
.Insert(0,rXP
[mpSdrPathDragData
->nPrevPrevPnt0
-1],PolyFlags::Control
);
1156 aXPoly
.Insert(0,rXP
[mpSdrPathDragData
->nPrevPrevPnt0
-2],PolyFlags::Normal
);
1157 // leverage lines for the opposing curve segment
1158 aLine3
[0]=mpSdrPathDragData
->aXP
[nPrevPnt
];
1159 aLine3
[1]=mpSdrPathDragData
->aXP
[nPrevPrevPnt
];
1160 aLine4
[0]=rXP
[mpSdrPathDragData
->nPrevPrevPnt0
-2];
1161 aLine4
[1]=rXP
[mpSdrPathDragData
->nPrevPrevPnt0
-1];
1165 } else { // else this is a control point before a support point
1166 aLine1
[0]=mpSdrPathDragData
->aXP
[nNextPnt
];
1167 aLine2
[0]=mpSdrPathDragData
->aXP
[nPrevPrevPnt
];
1168 aLine2
[1]=mpSdrPathDragData
->aXP
[nPrevPnt
];
1169 if (mpSdrPathDragData
->aXP
.IsSmooth(nNextPnt
) && !bNextIsEndPnt
&& mpSdrPathDragData
->aXP
.IsControl(nNextNextPnt
)) {
1170 aXPoly
.Insert(XPOLY_APPEND
,rXP
[mpSdrPathDragData
->nNextNextPnt0
+1],PolyFlags::Control
);
1171 aXPoly
.Insert(XPOLY_APPEND
,rXP
[mpSdrPathDragData
->nNextNextPnt0
+2],PolyFlags::Normal
);
1172 // leverage lines for the opposing curve segment
1173 aLine3
[0]=mpSdrPathDragData
->aXP
[nNextPnt
];
1174 aLine3
[1]=mpSdrPathDragData
->aXP
[nNextNextPnt
];
1175 aLine4
[0]=rXP
[mpSdrPathDragData
->nNextNextPnt0
+2];
1176 aLine4
[1]=rXP
[mpSdrPathDragData
->nNextNextPnt0
+1];
1178 aXPoly
.Remove(aXPoly
.GetPointCount()-1,1);
1181 } else { // else is not a control point
1182 if (mpSdrPathDragData
->bEliminate
) {
1185 if (bPrevIsControl
) aXPoly
.Insert(0,rXP
[mpSdrPathDragData
->nPrevPrevPnt0
-1],PolyFlags::Normal
);
1186 else if (!bBegPnt
&& !bPrevIsBegPnt
&& mpSdrPathDragData
->aXP
.IsControl(nPrevPrevPnt
)) {
1187 aXPoly
.Insert(0,rXP
[mpSdrPathDragData
->nPrevPrevPnt0
-1],PolyFlags::Control
);
1188 aXPoly
.Insert(0,rXP
[mpSdrPathDragData
->nPrevPrevPnt0
-2],PolyFlags::Normal
);
1191 if (bBegPnt
) aXPoly
.Remove(0,1);
1193 if (bNextIsControl
) aXPoly
.Insert(XPOLY_APPEND
,rXP
[mpSdrPathDragData
->nNextNextPnt0
+1],PolyFlags::Normal
);
1194 else if (!bEndPnt
&& !bNextIsEndPnt
&& mpSdrPathDragData
->aXP
.IsControl(nNextNextPnt
)) {
1195 aXPoly
.Insert(XPOLY_APPEND
,rXP
[mpSdrPathDragData
->nNextNextPnt0
+1],PolyFlags::Control
);
1196 aXPoly
.Insert(XPOLY_APPEND
,rXP
[mpSdrPathDragData
->nNextNextPnt0
+2],PolyFlags::Normal
);
1198 aXPoly
.Remove(aXPoly
.GetPointCount()-1,1);
1199 if (bEndPnt
) aXPoly
.Remove(aXPoly
.GetPointCount()-1,1);
1201 if (bClosed
) { // "pear problem": 2 lines, 1 curve, everything smoothed, a point between both lines is dragged
1202 if (aXPoly
.GetPointCount()>nPointCount
&& aXPoly
.IsControl(1)) {
1203 sal_uInt16 a
=aXPoly
.GetPointCount();
1204 aXPoly
[a
-2]=aXPoly
[2]; aXPoly
.SetFlags(a
-2,aXPoly
.GetFlags(2));
1205 aXPoly
[a
-1]=aXPoly
[3]; aXPoly
.SetFlags(a
-1,aXPoly
.GetFlags(3));
1210 aRetval
.Insert(std::move(aXPoly
));
1211 if (aLine1
.GetPointCount()>1) aRetval
.Insert(std::move(aLine1
));
1212 if (aLine2
.GetPointCount()>1) aRetval
.Insert(std::move(aLine2
));
1213 if (aLine3
.GetPointCount()>1) aRetval
.Insert(std::move(aLine3
));
1214 if (aLine4
.GetPointCount()>1) aRetval
.Insert(std::move(aLine4
));
1217 return aRetval
.getB2DPolyPolygon();
1220 void ImpPathForDragAndCreate::BegCreate(SdrDragStat
& rStat
)
1222 bool bFreeHand(IsFreeHand(meObjectKind
));
1223 rStat
.SetNoSnap(bFreeHand
);
1224 rStat
.SetOrtho8Possible();
1225 aPathPolygon
.Clear();
1227 bool bMakeStartPoint
= true;
1228 SdrView
* pView
=rStat
.GetView();
1229 if (pView
!=nullptr && pView
->IsUseIncompatiblePathCreateInterface() &&
1230 (meObjectKind
==SdrObjKind::Polygon
|| meObjectKind
==SdrObjKind::PolyLine
|| meObjectKind
==SdrObjKind::PathLine
|| meObjectKind
==SdrObjKind::PathFill
)) {
1231 bMakeStartPoint
= false;
1233 aPathPolygon
.Insert(XPolygon());
1234 aPathPolygon
[0][0]=rStat
.GetStart();
1235 if (bMakeStartPoint
) {
1236 aPathPolygon
[0][1]=rStat
.GetNow();
1238 std::unique_ptr
<ImpPathCreateUser
> pU(new ImpPathCreateUser
);
1239 pU
->eStartKind
=meObjectKind
;
1240 pU
->eCurrentKind
=meObjectKind
;
1241 rStat
.SetUser(std::move(pU
));
1244 bool ImpPathForDragAndCreate::MovCreate(SdrDragStat
& rStat
)
1246 ImpPathCreateUser
* pU
=static_cast<ImpPathCreateUser
*>(rStat
.GetUser());
1247 SdrView
* pView
=rStat
.GetView();
1248 XPolygon
& rXPoly
=aPathPolygon
[aPathPolygon
.Count()-1];
1249 if (pView
!=nullptr && pView
->IsCreateMode()) {
1250 // switch to different CreateTool, if appropriate
1252 SdrInventor nInvent
;
1253 pView
->TakeCurrentObj(nIdent
,nInvent
);
1254 if (nInvent
==SdrInventor::Default
&& pU
->eCurrentKind
!= nIdent
) {
1255 SdrObjKind eNewKind
= nIdent
;
1257 case SdrObjKind::CircleArc
:
1258 case SdrObjKind::CircleOrEllipse
:
1259 case SdrObjKind::CircleCut
:
1260 case SdrObjKind::CircleSection
:
1261 eNewKind
=SdrObjKind::CircleArc
;
1263 case SdrObjKind::Rectangle
:
1264 case SdrObjKind::Line
:
1265 case SdrObjKind::PolyLine
:
1266 case SdrObjKind::Polygon
:
1267 case SdrObjKind::PathLine
:
1268 case SdrObjKind::PathFill
:
1269 case SdrObjKind::FreehandLine
:
1270 case SdrObjKind::FreehandFill
:
1272 pU
->eCurrentKind
=eNewKind
;
1273 pU
->bMixedCreate
=true;
1274 pU
->nBezierStartPoint
=rXPoly
.GetPointCount();
1275 if (pU
->nBezierStartPoint
>0) pU
->nBezierStartPoint
--;
1281 sal_uInt16 nCurrentPoint
=rXPoly
.GetPointCount();
1282 if (aPathPolygon
.Count()>1 && rStat
.IsMouseDown() && nCurrentPoint
<2) {
1283 rXPoly
[0]=rStat
.GetPos0();
1284 rXPoly
[1]=rStat
.GetNow();
1287 if (nCurrentPoint
==0) {
1288 rXPoly
[0]=rStat
.GetPos0();
1289 } else nCurrentPoint
--;
1290 bool bFreeHand
=IsFreeHand(pU
->eCurrentKind
);
1291 rStat
.SetNoSnap(bFreeHand
);
1292 rStat
.SetOrtho8Possible(pU
->eCurrentKind
!=SdrObjKind::CircleArc
&& pU
->eCurrentKind
!=SdrObjKind::Rectangle
&& (!pU
->bMixedCreate
|| pU
->eCurrentKind
!=SdrObjKind::Line
));
1293 rXPoly
[nCurrentPoint
]=rStat
.GetNow();
1294 if (!pU
->bMixedCreate
&& pU
->eStartKind
==SdrObjKind::Line
&& rXPoly
.GetPointCount()>=1) {
1295 Point
aPt(rStat
.GetStart());
1296 if (pView
!=nullptr && pView
->IsCreate1stPointAsCenter()) {
1298 aPt
-=rStat
.GetNow();
1302 OutputDevice
* pOut
=pView
==nullptr ? nullptr : pView
->GetFirstOutputDevice();
1304 if (pU
->nBezierStartPoint
>nCurrentPoint
) pU
->nBezierStartPoint
=nCurrentPoint
;
1305 if (rStat
.IsMouseDown() && nCurrentPoint
>0) {
1306 // don't allow two consecutive points to occupy too similar positions
1307 tools::Long nMinDist
=1;
1308 if (pView
!=nullptr) nMinDist
=pView
->GetFreeHandMinDistPix();
1309 if (pOut
!=nullptr) nMinDist
=pOut
->PixelToLogic(Size(nMinDist
,0)).Width();
1310 if (nMinDist
<1) nMinDist
=1;
1312 Point
aPt0(rXPoly
[nCurrentPoint
-1]);
1313 Point
aPt1(rStat
.GetNow());
1314 tools::Long dx
=aPt0
.X()-aPt1
.X(); if (dx
<0) dx
=-dx
;
1315 tools::Long dy
=aPt0
.Y()-aPt1
.Y(); if (dy
<0) dy
=-dy
;
1316 if (dx
<nMinDist
&& dy
<nMinDist
) return false;
1318 // TODO: the following is copied from EndCreate (with a few smaller modifications)
1319 // and should be combined into a method with the code there.
1321 if (nCurrentPoint
-pU
->nBezierStartPoint
>=3 && ((nCurrentPoint
-pU
->nBezierStartPoint
)%3)==0) {
1322 rXPoly
.PointsToBezier(nCurrentPoint
-3);
1323 rXPoly
.SetFlags(nCurrentPoint
-1,PolyFlags::Control
);
1324 rXPoly
.SetFlags(nCurrentPoint
-2,PolyFlags::Control
);
1326 if (nCurrentPoint
>=6 && rXPoly
.IsControl(nCurrentPoint
-4)) {
1327 rXPoly
.CalcTangent(nCurrentPoint
-3,nCurrentPoint
-4,nCurrentPoint
-2);
1328 rXPoly
.SetFlags(nCurrentPoint
-3,PolyFlags::Smooth
);
1331 rXPoly
[nCurrentPoint
+1]=rStat
.GetNow();
1334 pU
->nBezierStartPoint
=nCurrentPoint
;
1338 pU
->ResetFormFlags();
1339 if (IsBezier(pU
->eCurrentKind
)) {
1340 if (nCurrentPoint
>=2) {
1341 pU
->CalcBezier(rXPoly
[nCurrentPoint
-1],rXPoly
[nCurrentPoint
],rXPoly
[nCurrentPoint
-1]-rXPoly
[nCurrentPoint
-2],rStat
.IsMouseDown());
1342 } else if (pU
->bBezHasCtrl0
) {
1343 pU
->CalcBezier(rXPoly
[nCurrentPoint
-1],rXPoly
[nCurrentPoint
],pU
->aBezControl0
-rXPoly
[nCurrentPoint
-1],rStat
.IsMouseDown());
1346 if (pU
->eCurrentKind
==SdrObjKind::CircleArc
&& nCurrentPoint
>=2) {
1347 pU
->CalcCircle(rXPoly
[nCurrentPoint
-1],rXPoly
[nCurrentPoint
],rXPoly
[nCurrentPoint
-1]-rXPoly
[nCurrentPoint
-2],pView
);
1349 if (pU
->eCurrentKind
==SdrObjKind::Line
&& nCurrentPoint
>=2) {
1350 pU
->CalcLine(rXPoly
[nCurrentPoint
-1],rXPoly
[nCurrentPoint
],rXPoly
[nCurrentPoint
-1]-rXPoly
[nCurrentPoint
-2],pView
);
1352 if (pU
->eCurrentKind
==SdrObjKind::Rectangle
&& nCurrentPoint
>=2) {
1353 pU
->CalcRect(rXPoly
[nCurrentPoint
-1],rXPoly
[nCurrentPoint
],rXPoly
[nCurrentPoint
-1]-rXPoly
[nCurrentPoint
-2],pView
);
1359 bool ImpPathForDragAndCreate::EndCreate(SdrDragStat
& rStat
, SdrCreateCmd eCmd
)
1361 ImpPathCreateUser
* pU
=static_cast<ImpPathCreateUser
*>(rStat
.GetUser());
1363 SdrView
* pView
=rStat
.GetView();
1364 bool bIncomp
=pView
!=nullptr && pView
->IsUseIncompatiblePathCreateInterface();
1365 XPolygon
& rXPoly
=aPathPolygon
[aPathPolygon
.Count()-1];
1366 sal_uInt16 nCurrentPoint
=rXPoly
.GetPointCount()-1;
1367 rXPoly
[nCurrentPoint
]=rStat
.GetNow();
1368 if (!pU
->bMixedCreate
&& pU
->eStartKind
==SdrObjKind::Line
) {
1369 if (rStat
.GetPointCount()>=2) eCmd
=SdrCreateCmd::ForceEnd
;
1370 bRet
= eCmd
==SdrCreateCmd::ForceEnd
;
1373 rStat
.SetUser(nullptr);
1378 if (!pU
->bMixedCreate
&& IsFreeHand(pU
->eStartKind
)) {
1379 if (rStat
.GetPointCount()>=2) eCmd
=SdrCreateCmd::ForceEnd
;
1380 bRet
=eCmd
==SdrCreateCmd::ForceEnd
;
1383 rStat
.SetUser(nullptr);
1387 if (eCmd
==SdrCreateCmd::NextPoint
|| eCmd
==SdrCreateCmd::NextObject
) {
1388 // don't allow two consecutive points to occupy the same position
1389 if (nCurrentPoint
==0 || rStat
.GetNow()!=rXPoly
[nCurrentPoint
-1]) {
1391 if (pU
->nBezierStartPoint
>nCurrentPoint
) pU
->nBezierStartPoint
=nCurrentPoint
;
1392 if (IsBezier(pU
->eCurrentKind
) && nCurrentPoint
-pU
->nBezierStartPoint
>=3 && ((nCurrentPoint
-pU
->nBezierStartPoint
)%3)==0) {
1393 rXPoly
.PointsToBezier(nCurrentPoint
-3);
1394 rXPoly
.SetFlags(nCurrentPoint
-1,PolyFlags::Control
);
1395 rXPoly
.SetFlags(nCurrentPoint
-2,PolyFlags::Control
);
1397 if (nCurrentPoint
>=6 && rXPoly
.IsControl(nCurrentPoint
-4)) {
1398 rXPoly
.CalcTangent(nCurrentPoint
-3,nCurrentPoint
-4,nCurrentPoint
-2);
1399 rXPoly
.SetFlags(nCurrentPoint
-3,PolyFlags::Smooth
);
1403 if (nCurrentPoint
==1 && IsBezier(pU
->eCurrentKind
) && !pU
->bBezHasCtrl0
) {
1404 pU
->aBezControl0
=rStat
.GetNow();
1405 pU
->bBezHasCtrl0
=true;
1408 if (pU
->IsFormFlag()) {
1409 sal_uInt16 nPointCount0
=rXPoly
.GetPointCount();
1410 rXPoly
.Remove(nCurrentPoint
-1,2); // remove last two points and replace by form
1411 rXPoly
.Insert(XPOLY_APPEND
,pU
->GetFormPoly());
1412 sal_uInt16 nPointCount1
=rXPoly
.GetPointCount();
1413 for (sal_uInt16 i
=nPointCount0
+1; i
<nPointCount1
-1; i
++) { // to make BckAction work
1414 if (!rXPoly
.IsControl(i
)) rStat
.NextPoint();
1416 nCurrentPoint
=rXPoly
.GetPointCount()-1;
1420 rXPoly
[nCurrentPoint
]=rStat
.GetNow();
1422 if (eCmd
==SdrCreateCmd::NextObject
) {
1423 if (rXPoly
.GetPointCount()>=2) {
1424 pU
->bBezHasCtrl0
=false;
1425 // only a singular polygon may be opened, so close this
1426 rXPoly
[nCurrentPoint
]=rXPoly
[0];
1428 aXP
[0]=rStat
.GetNow();
1429 aPathPolygon
.Insert(std::move(aXP
));
1434 sal_uInt16 nPolyCount
=aPathPolygon
.Count();
1435 if (nPolyCount
!=0) {
1436 // delete last point, if necessary
1437 if (eCmd
==SdrCreateCmd::ForceEnd
) {
1438 XPolygon
& rXP
=aPathPolygon
[nPolyCount
-1];
1439 sal_uInt16 nPointCount
=rXP
.GetPointCount();
1440 if (nPointCount
>=2) {
1441 if (!rXP
.IsControl(nPointCount
-2)) {
1442 if (rXP
[nPointCount
-1]==rXP
[nPointCount
-2]) {
1443 rXP
.Remove(nPointCount
-1,1);
1446 if (rXP
[nPointCount
-3]==rXP
[nPointCount
-2]) {
1447 rXP
.Remove(nPointCount
-3,3);
1452 for (sal_uInt16 nPolyNum
=nPolyCount
; nPolyNum
>0;) {
1454 XPolygon
& rXP
=aPathPolygon
[nPolyNum
];
1455 sal_uInt16 nPointCount
=rXP
.GetPointCount();
1456 // delete polygons with too few points
1457 if (nPolyNum
<nPolyCount
-1 || eCmd
==SdrCreateCmd::ForceEnd
) {
1458 if (nPointCount
<2) aPathPolygon
.Remove(nPolyNum
);
1462 pU
->ResetFormFlags();
1463 bRet
=eCmd
==SdrCreateCmd::ForceEnd
;
1466 rStat
.SetUser(nullptr);
1471 bool ImpPathForDragAndCreate::BckCreate(SdrDragStat
const & rStat
)
1473 ImpPathCreateUser
* pU
=static_cast<ImpPathCreateUser
*>(rStat
.GetUser());
1474 if (aPathPolygon
.Count()>0) {
1475 XPolygon
& rXPoly
=aPathPolygon
[aPathPolygon
.Count()-1];
1476 sal_uInt16 nCurrentPoint
=rXPoly
.GetPointCount();
1477 if (nCurrentPoint
>0) {
1479 // make the last part of a bezier curve a line
1480 rXPoly
.Remove(nCurrentPoint
,1);
1481 if (nCurrentPoint
>=3 && rXPoly
.IsControl(nCurrentPoint
-1)) {
1482 // there should never be a bezier segment at the end, so this is just in case...
1483 rXPoly
.Remove(nCurrentPoint
-1,1);
1484 if (rXPoly
.IsControl(nCurrentPoint
-2)) rXPoly
.Remove(nCurrentPoint
-2,1);
1487 nCurrentPoint
=rXPoly
.GetPointCount();
1488 if (nCurrentPoint
>=4) { // no bezier segment at the end
1490 if (rXPoly
.IsControl(nCurrentPoint
-1)) {
1491 rXPoly
.Remove(nCurrentPoint
-1,1);
1492 if (rXPoly
.IsControl(nCurrentPoint
-2)) rXPoly
.Remove(nCurrentPoint
-2,1);
1495 if (rXPoly
.GetPointCount()<2) {
1496 aPathPolygon
.Remove(aPathPolygon
.Count()-1);
1498 if (aPathPolygon
.Count()>0) {
1499 XPolygon
& rLocalXPoly
=aPathPolygon
[aPathPolygon
.Count()-1];
1500 sal_uInt16 nLocalCurrentPoint
=rLocalXPoly
.GetPointCount();
1501 if (nLocalCurrentPoint
>0) {
1502 nLocalCurrentPoint
--;
1503 rLocalXPoly
[nLocalCurrentPoint
]=rStat
.GetNow();
1507 pU
->ResetFormFlags();
1508 return aPathPolygon
.Count()!=0;
1511 void ImpPathForDragAndCreate::BrkCreate(SdrDragStat
& rStat
)
1513 aPathPolygon
.Clear();
1515 rStat
.SetUser(nullptr);
1518 basegfx::B2DPolyPolygon
ImpPathForDragAndCreate::TakeObjectPolyPolygon(const SdrDragStat
& rDrag
) const
1520 basegfx::B2DPolyPolygon
aRetval(aPathPolygon
.getB2DPolyPolygon());
1521 SdrView
* pView
= rDrag
.GetView();
1523 if(pView
&& pView
->IsUseIncompatiblePathCreateInterface())
1526 ImpPathCreateUser
* pU
= static_cast<ImpPathCreateUser
*>(rDrag
.GetUser());
1527 basegfx::B2DPolygon
aNewPolygon(aRetval
.count() ? aRetval
.getB2DPolygon(aRetval
.count() - 1) : basegfx::B2DPolygon());
1529 if(pU
->IsFormFlag() && aNewPolygon
.count() > 1)
1531 // remove last segment and replace with current
1532 // do not forget to rescue the previous control point which will be lost when
1533 // the point it's associated with is removed
1534 const sal_uInt32
nChangeIndex(aNewPolygon
.count() - 2);
1535 const basegfx::B2DPoint
aSavedPrevCtrlPoint(aNewPolygon
.getPrevControlPoint(nChangeIndex
));
1537 aNewPolygon
.remove(nChangeIndex
, 2);
1538 aNewPolygon
.append(pU
->GetFormPoly().getB2DPolygon());
1540 if(nChangeIndex
< aNewPolygon
.count())
1542 // if really something was added, set the saved previous control point to the
1543 // point where it belongs
1544 aNewPolygon
.setPrevControlPoint(nChangeIndex
, aSavedPrevCtrlPoint
);
1550 aRetval
.setB2DPolygon(aRetval
.count() - 1, aNewPolygon
);
1554 aRetval
.append(aNewPolygon
);
1560 basegfx::B2DPolyPolygon
ImpPathForDragAndCreate::TakeDragPolyPolygon(const SdrDragStat
& rDrag
)
1562 basegfx::B2DPolyPolygon aRetval
;
1563 SdrView
* pView
= rDrag
.GetView();
1565 if(pView
&& pView
->IsUseIncompatiblePathCreateInterface())
1568 const ImpPathCreateUser
* pU
= static_cast<const ImpPathCreateUser
*>(rDrag
.GetUser());
1570 if(pU
&& pU
->bBezier
&& rDrag
.IsMouseDown())
1572 // no more XOR, no need for complicated helplines
1573 basegfx::B2DPolygon aHelpline
;
1574 aHelpline
.append(basegfx::B2DPoint(pU
->aBezCtrl2
.X(), pU
->aBezCtrl2
.Y()));
1575 aHelpline
.append(basegfx::B2DPoint(pU
->aBezEnd
.X(), pU
->aBezEnd
.Y()));
1576 aRetval
.append(aHelpline
);
1582 PointerStyle
ImpPathForDragAndCreate::GetCreatePointer() const
1584 switch (meObjectKind
) {
1585 case SdrObjKind::Line
: return PointerStyle::DrawLine
;
1586 case SdrObjKind::Polygon
: return PointerStyle::DrawPolygon
;
1587 case SdrObjKind::PolyLine
: return PointerStyle::DrawPolygon
;
1588 case SdrObjKind::PathLine
: return PointerStyle::DrawBezier
;
1589 case SdrObjKind::PathFill
: return PointerStyle::DrawBezier
;
1590 case SdrObjKind::FreehandLine
: return PointerStyle::DrawFreehand
;
1591 case SdrObjKind::FreehandFill
: return PointerStyle::DrawFreehand
;
1592 case SdrObjKind::PathPoly
: return PointerStyle::DrawPolygon
;
1593 case SdrObjKind::PathPolyLine
: return PointerStyle::DrawPolygon
;
1596 return PointerStyle::Cross
;
1599 SdrPathObjGeoData::SdrPathObjGeoData()
1600 : meKind(SdrObjKind::NONE
)
1604 SdrPathObjGeoData::~SdrPathObjGeoData()
1608 // DrawContact section
1610 std::unique_ptr
<sdr::contact::ViewContact
> SdrPathObj::CreateObjectSpecificViewContact()
1612 return std::make_unique
<sdr::contact::ViewContactOfSdrPathObj
>(*this);
1616 SdrPathObj::SdrPathObj(
1617 SdrModel
& rSdrModel
,
1618 SdrObjKind eNewKind
)
1619 : SdrTextObj(rSdrModel
),
1622 m_bClosedObj
= IsClosed();
1625 SdrPathObj::SdrPathObj(SdrModel
& rSdrModel
, SdrPathObj
const & rSource
)
1626 : SdrTextObj(rSdrModel
, rSource
),
1627 meKind(rSource
.meKind
)
1629 m_bClosedObj
= IsClosed();
1630 maPathPolygon
= rSource
.GetPathPoly();
1633 SdrPathObj::SdrPathObj(
1634 SdrModel
& rSdrModel
,
1635 SdrObjKind eNewKind
,
1636 basegfx::B2DPolyPolygon aPathPoly
)
1637 : SdrTextObj(rSdrModel
),
1638 maPathPolygon(std::move(aPathPoly
)),
1641 m_bClosedObj
= IsClosed();
1645 SdrPathObj::~SdrPathObj() = default;
1647 static bool lcl_ImpIsLine(const basegfx::B2DPolyPolygon
& rPolyPolygon
)
1649 return (1 == rPolyPolygon
.count() && 2 == rPolyPolygon
.getB2DPolygon(0).count());
1652 static tools::Rectangle
lcl_ImpGetBoundRect(const basegfx::B2DPolyPolygon
& rPolyPolygon
)
1654 basegfx::B2DRange
aRange(basegfx::utils::getRange(rPolyPolygon
));
1656 if (aRange
.isEmpty())
1657 return tools::Rectangle();
1659 return tools::Rectangle(
1660 basegfx::fround
<tools::Long
>(aRange
.getMinX()), basegfx::fround
<tools::Long
>(aRange
.getMinY()),
1661 basegfx::fround
<tools::Long
>(aRange
.getMaxX()), basegfx::fround
<tools::Long
>(aRange
.getMaxY()));
1664 void SdrPathObj::ImpForceLineAngle()
1666 if(SdrObjKind::Line
!= meKind
|| !lcl_ImpIsLine(GetPathPoly()))
1669 const basegfx::B2DPolygon
aPoly(GetPathPoly().getB2DPolygon(0));
1670 const basegfx::B2DPoint
aB2DPoint0(aPoly
.getB2DPoint(0));
1671 const basegfx::B2DPoint
aB2DPoint1(aPoly
.getB2DPoint(1));
1672 const Point
aPoint0(basegfx::fround
<tools::Long
>(aB2DPoint0
.getX()),
1673 basegfx::fround
<tools::Long
>(aB2DPoint0
.getY()));
1674 const Point
aPoint1(basegfx::fround
<tools::Long
>(aB2DPoint1
.getX()),
1675 basegfx::fround
<tools::Long
>(aB2DPoint1
.getY()));
1676 const basegfx::B2DPoint
aB2DDelt(aB2DPoint1
- aB2DPoint0
);
1677 const Point
aDelt(basegfx::fround
<tools::Long
>(aB2DDelt
.getX()),
1678 basegfx::fround
<tools::Long
>(aB2DDelt
.getY()));
1680 maGeo
.m_nRotationAngle
=GetAngle(aDelt
);
1681 maGeo
.m_nShearAngle
=0_deg100
;
1682 maGeo
.RecalcSinCos();
1685 // for SdrTextObj, keep aRect up to date
1686 setRectangle(tools::Rectangle::Normalize(aPoint0
, aPoint1
));
1689 void SdrPathObj::ImpForceKind()
1691 if (meKind
==SdrObjKind::PathPolyLine
) meKind
=SdrObjKind::PolyLine
;
1692 if (meKind
==SdrObjKind::PathPoly
) meKind
=SdrObjKind::Polygon
;
1694 if(GetPathPoly().areControlPointsUsed())
1698 case SdrObjKind::Line
: meKind
=SdrObjKind::PathLine
; break;
1699 case SdrObjKind::PolyLine
: meKind
=SdrObjKind::PathLine
; break;
1700 case SdrObjKind::Polygon
: meKind
=SdrObjKind::PathFill
; break;
1708 case SdrObjKind::PathLine
: meKind
=SdrObjKind::PolyLine
; break;
1709 case SdrObjKind::FreehandLine
: meKind
=SdrObjKind::PolyLine
; break;
1710 case SdrObjKind::PathFill
: meKind
=SdrObjKind::Polygon
; break;
1711 case SdrObjKind::FreehandFill
: meKind
=SdrObjKind::Polygon
; break;
1716 if (meKind
==SdrObjKind::Line
&& !lcl_ImpIsLine(GetPathPoly())) meKind
=SdrObjKind::PolyLine
;
1717 if (meKind
==SdrObjKind::PolyLine
&& lcl_ImpIsLine(GetPathPoly())) meKind
=SdrObjKind::Line
;
1719 m_bClosedObj
=IsClosed();
1721 if (meKind
==SdrObjKind::Line
)
1723 ImpForceLineAngle();
1727 // #i10659#, for polys with more than 2 points.
1729 // Here i again need to fix something, because when Path-Polys are Copy-Pasted
1730 // between Apps with different measurements (e.g. 100TH_MM and TWIPS) there is
1731 // a scaling loop started from SdrExchangeView::Paste. In itself, this is not
1732 // wrong, but aRect is wrong here and not even updated by RecalcSnapRect(). If
1733 // this is the case, some size needs to be set here in aRect to avoid that the cycle
1734 // through Rect2Poly - Poly2Rect does something badly wrong since that cycle is
1735 // BASED on aRect. That cycle is triggered in SdrTextObj::NbcResize() which is called
1736 // from the local Resize() implementation.
1738 // Basic problem is that the member aRect in SdrTextObj basically is a unrotated
1739 // text rectangle for the text object itself and methods at SdrTextObj do handle it
1740 // in that way. Many draw objects derived from SdrTextObj 'abuse' aRect as SnapRect
1741 // which is basically wrong. To make the SdrText methods which deal with aRect directly
1742 // work it is necessary to always keep aRect updated. This e.g. not done after a Clone()
1743 // command for SdrPathObj. Since adding this update mechanism with #101412# to
1744 // ImpForceLineAngle() for lines was very successful, i add it to where ImpForceLineAngle()
1745 // was called, once here below and once on a 2nd place below.
1747 // #i10659# for SdrTextObj, keep aRect up to date
1748 if(GetPathPoly().count())
1750 setRectangle(lcl_ImpGetBoundRect(GetPathPoly()));
1754 // #i75974# adapt polygon state to object type. This may include a reinterpretation
1755 // of a closed geometry as open one, but with identical first and last point
1756 for(auto& rPolygon
: maPathPolygon
)
1758 if(IsClosed() != rPolygon
.isClosed())
1760 // #i80213# really change polygon geometry; else e.g. the last point which
1761 // needs to be identical with the first one will be missing when opening
1762 // due to OBJ_PATH type
1763 if(rPolygon
.isClosed())
1765 basegfx::utils::openWithGeometryChange(rPolygon
);
1769 basegfx::utils::closeWithGeometryChange(rPolygon
);
1775 void SdrPathObj::ImpSetClosed(bool bClose
)
1781 case SdrObjKind::Line
: meKind
=SdrObjKind::Polygon
; break;
1782 case SdrObjKind::PolyLine
: meKind
=SdrObjKind::Polygon
; break;
1783 case SdrObjKind::PathLine
: meKind
=SdrObjKind::PathFill
; break;
1784 case SdrObjKind::FreehandLine
: meKind
=SdrObjKind::FreehandFill
; break;
1788 m_bClosedObj
= true;
1794 case SdrObjKind::Polygon
: meKind
=SdrObjKind::PolyLine
; break;
1795 case SdrObjKind::PathFill
: meKind
=SdrObjKind::PathLine
; break;
1796 case SdrObjKind::FreehandFill
: meKind
=SdrObjKind::FreehandLine
; break;
1800 m_bClosedObj
= false;
1806 void SdrPathObj::TakeObjInfo(SdrObjTransformInfoRec
& rInfo
) const
1808 rInfo
.bNoContortion
=false;
1810 bool bCanConv
= !HasText() || ImpCanConvTextToCurve();
1811 bool bIsPath
= IsBezier();
1813 rInfo
.bEdgeRadiusAllowed
= false;
1814 rInfo
.bCanConvToPath
= bCanConv
&& !bIsPath
;
1815 rInfo
.bCanConvToPoly
= bCanConv
&& bIsPath
;
1816 rInfo
.bCanConvToContour
= !IsFontwork() && (rInfo
.bCanConvToPoly
|| LineGeometryUsageIsNecessary());
1819 SdrObjKind
SdrPathObj::GetObjIdentifier() const
1824 rtl::Reference
<SdrObject
> SdrPathObj::CloneSdrObject(SdrModel
& rTargetModel
) const
1826 return new SdrPathObj(rTargetModel
, *this);
1829 OUString
SdrPathObj::TakeObjNameSingul() const
1833 if(SdrObjKind::Line
== meKind
)
1835 TranslateId
pId(STR_ObjNameSingulLINE
);
1837 if(lcl_ImpIsLine(GetPathPoly()))
1839 const basegfx::B2DPolygon
aPoly(GetPathPoly().getB2DPolygon(0));
1840 const basegfx::B2DPoint
aB2DPoint0(aPoly
.getB2DPoint(0));
1841 const basegfx::B2DPoint
aB2DPoint1(aPoly
.getB2DPoint(1));
1843 if(aB2DPoint0
!= aB2DPoint1
)
1845 if(aB2DPoint0
.getY() == aB2DPoint1
.getY())
1847 pId
= STR_ObjNameSingulLINE_Hori
;
1849 else if(aB2DPoint0
.getX() == aB2DPoint1
.getX())
1851 pId
= STR_ObjNameSingulLINE_Vert
;
1855 const double fDx(fabs(aB2DPoint0
.getX() - aB2DPoint1
.getX()));
1856 const double fDy(fabs(aB2DPoint0
.getY() - aB2DPoint1
.getY()));
1860 pId
= STR_ObjNameSingulLINE_Diag
;
1866 sName
= SvxResId(pId
);
1868 else if(SdrObjKind::PolyLine
== meKind
|| SdrObjKind::Polygon
== meKind
)
1870 const bool bClosed(SdrObjKind::Polygon
== meKind
);
1873 if(mpDAC
&& mpDAC
->IsCreating())
1877 pId
= STR_ObjNameSingulPOLY
;
1881 pId
= STR_ObjNameSingulPLIN
;
1884 sName
= SvxResId(pId
);
1889 sal_uInt32
nPointCount(0);
1891 for(auto const& rPolygon
: GetPathPoly())
1893 nPointCount
+= rPolygon
.count();
1898 pId
= STR_ObjNameSingulPOLY_PointCount
;
1902 pId
= STR_ObjNameSingulPLIN_PointCount
;
1906 sName
= SvxResId(pId
).replaceFirst("%2", OUString::number(nPointCount
));
1913 case SdrObjKind::PathLine
: sName
= SvxResId(STR_ObjNameSingulPATHLINE
); break;
1914 case SdrObjKind::FreehandLine
: sName
= SvxResId(STR_ObjNameSingulFREELINE
); break;
1915 case SdrObjKind::PathFill
: sName
= SvxResId(STR_ObjNameSingulPATHFILL
); break;
1916 case SdrObjKind::FreehandFill
: sName
= SvxResId(STR_ObjNameSingulFREEFILL
); break;
1921 OUString
aName(GetName());
1922 if (!aName
.isEmpty())
1923 sName
+= " '" + aName
+ "'";
1928 OUString
SdrPathObj::TakeObjNamePlural() const
1933 case SdrObjKind::Line
: sName
=SvxResId(STR_ObjNamePluralLINE
); break;
1934 case SdrObjKind::PolyLine
: sName
=SvxResId(STR_ObjNamePluralPLIN
); break;
1935 case SdrObjKind::Polygon
: sName
=SvxResId(STR_ObjNamePluralPOLY
); break;
1936 case SdrObjKind::PathLine
: sName
=SvxResId(STR_ObjNamePluralPATHLINE
); break;
1937 case SdrObjKind::FreehandLine
: sName
=SvxResId(STR_ObjNamePluralFREELINE
); break;
1938 case SdrObjKind::PathFill
: sName
=SvxResId(STR_ObjNamePluralPATHFILL
); break;
1939 case SdrObjKind::FreehandFill
: sName
=SvxResId(STR_ObjNamePluralFREEFILL
); break;
1945 basegfx::B2DPolyPolygon
SdrPathObj::TakeXorPoly() const
1947 return GetPathPoly();
1950 sal_uInt32
SdrPathObj::GetHdlCount() const
1952 sal_uInt32
nRetval(0);
1954 for(auto const& rPolygon
: GetPathPoly())
1956 nRetval
+= rPolygon
.count();
1962 void SdrPathObj::AddToHdlList(SdrHdlList
& rHdlList
) const
1964 // keep old stuff to be able to keep old SdrHdl stuff, too
1965 const XPolyPolygon
aOldPathPolygon(GetPathPoly());
1966 sal_uInt16 nPolyCnt
=aOldPathPolygon
.Count();
1967 bool bClosed
=IsClosed();
1970 for (sal_uInt16 i
=0; i
<nPolyCnt
; i
++) {
1971 const XPolygon
& rXPoly
=aOldPathPolygon
.GetObject(i
);
1972 sal_uInt16 nPntCnt
=rXPoly
.GetPointCount();
1973 if (bClosed
&& nPntCnt
>1) nPntCnt
--;
1975 for (sal_uInt16 j
=0; j
<nPntCnt
; j
++) {
1976 if (rXPoly
.GetFlags(j
)!=PolyFlags::Control
) {
1977 const Point
& rPnt
=rXPoly
[j
];
1978 std::unique_ptr
<SdrHdl
> pHdl(new SdrHdl(rPnt
,SdrHdlKind::Poly
));
1979 pHdl
->SetPolyNum(i
);
1980 pHdl
->SetPointNum(j
);
1981 pHdl
->Set1PixMore(j
==0);
1982 pHdl
->SetSourceHdlNum(nIdx
);
1984 rHdlList
.AddHdl(std::move(pHdl
));
1990 void SdrPathObj::AddToPlusHdlList(SdrHdlList
& rHdlList
, SdrHdl
& rHdl
) const
1992 // keep old stuff to be able to keep old SdrHdl stuff, too
1993 const XPolyPolygon
aOldPathPolygon(GetPathPoly());
1994 sal_uInt16 nPnt
= static_cast<sal_uInt16
>(rHdl
.GetPointNum());
1995 sal_uInt16 nPolyNum
= static_cast<sal_uInt16
>(rHdl
.GetPolyNum());
1997 if (nPolyNum
>=aOldPathPolygon
.Count())
2000 const XPolygon
& rXPoly
= aOldPathPolygon
[nPolyNum
];
2001 sal_uInt16 nPntMax
= rXPoly
.GetPointCount();
2009 // calculate the number of plus points
2010 sal_uInt16 nCnt
= 0;
2011 if (rXPoly
.GetFlags(nPnt
)!=PolyFlags::Control
)
2013 if (nPnt
==0 && IsClosed())
2015 if (nPnt
>0 && rXPoly
.GetFlags(nPnt
-1)==PolyFlags::Control
)
2017 if (nPnt
==nPntMax
&& IsClosed())
2019 if (nPnt
<nPntMax
&& rXPoly
.GetFlags(nPnt
+1)==PolyFlags::Control
)
2023 // construct the plus points
2024 for (sal_uInt32 nPlusNum
= 0; nPlusNum
< nCnt
; ++nPlusNum
)
2026 nPnt
= static_cast<sal_uInt16
>(rHdl
.GetPointNum());
2027 std::unique_ptr
<SdrHdl
> pHdl(new SdrHdlBezWgt(&rHdl
));
2028 pHdl
->SetPolyNum(rHdl
.GetPolyNum());
2030 if (nPnt
==0 && IsClosed())
2032 if (nPnt
>0 && rXPoly
.GetFlags(nPnt
-1)==PolyFlags::Control
&& nPlusNum
==0)
2034 pHdl
->SetPos(rXPoly
[nPnt
-1]);
2035 pHdl
->SetPointNum(nPnt
-1);
2039 if (nPnt
==nPntMax
&& IsClosed())
2041 if (nPnt
<rXPoly
.GetPointCount()-1 && rXPoly
.GetFlags(nPnt
+1)==PolyFlags::Control
)
2043 pHdl
->SetPos(rXPoly
[nPnt
+1]);
2044 pHdl
->SetPointNum(nPnt
+1);
2048 pHdl
->SetSourceHdlNum(rHdl
.GetSourceHdlNum());
2049 pHdl
->SetPlusHdl(true);
2050 rHdlList
.AddHdl(std::move(pHdl
));
2054 // tdf#123321: Make sure that SdrPathObj (e.g. line) has big enough extent for
2055 // visibility. This is realised by ensuring GetLogicRect() is the same as
2056 // GetSnapRect() for the SdrPathObj. Other SdrTextObj objects like
2057 // SdrObjCustomShape will still use a different version of this method that
2058 // does not consider the rotation. Otherwise, the rotated SdrObjCustomShape
2059 // would become mistakenly larger after save and reload (tdf#91687).
2060 // The invocation of the GetLogicRect() method that caused tdf#123321 was in
2061 // PlcDrawObj::WritePlc().
2062 const tools::Rectangle
&SdrPathObj::GetLogicRect() const
2064 return GetSnapRect();
2069 bool SdrPathObj::hasSpecialDrag() const
2074 bool SdrPathObj::beginSpecialDrag(SdrDragStat
& rDrag
) const
2076 ImpPathForDragAndCreate
aDragAndCreate(*const_cast<SdrPathObj
*>(this));
2078 return aDragAndCreate
.beginPathDrag(rDrag
);
2081 bool SdrPathObj::applySpecialDrag(SdrDragStat
& rDrag
)
2083 ImpPathForDragAndCreate
aDragAndCreate(*this);
2084 bool bRetval(aDragAndCreate
.beginPathDrag(rDrag
));
2088 bRetval
= aDragAndCreate
.movePathDrag(rDrag
);
2093 bRetval
= aDragAndCreate
.endPathDrag(rDrag
);
2098 NbcSetPathPoly(aDragAndCreate
.getModifiedPolyPolygon());
2104 OUString
SdrPathObj::getSpecialDragComment(const SdrDragStat
& rDrag
) const
2110 // #i103058# also get a comment when in creation
2111 const bool bCreateComment(rDrag
.GetView() && this == rDrag
.GetView()->GetCreateObj());
2115 aRetval
= mpDAC
->getSpecialDragComment(rDrag
);
2120 ImpPathForDragAndCreate
aDragAndCreate(*const_cast<SdrPathObj
*>(this));
2121 bool bDidWork(aDragAndCreate
.beginPathDrag(rDrag
));
2125 aRetval
= aDragAndCreate
.getSpecialDragComment(rDrag
);
2132 basegfx::B2DPolyPolygon
SdrPathObj::getSpecialDragPoly(const SdrDragStat
& rDrag
) const
2134 basegfx::B2DPolyPolygon aRetval
;
2135 ImpPathForDragAndCreate
aDragAndCreate(*const_cast<SdrPathObj
*>(this));
2136 bool bDidWork(aDragAndCreate
.beginPathDrag(rDrag
));
2140 aRetval
= aDragAndCreate
.getSpecialDragPoly(rDrag
);
2148 bool SdrPathObj::BegCreate(SdrDragStat
& rStat
)
2151 impGetDAC().BegCreate(rStat
);
2155 bool SdrPathObj::MovCreate(SdrDragStat
& rStat
)
2157 return impGetDAC().MovCreate(rStat
);
2160 bool SdrPathObj::EndCreate(SdrDragStat
& rStat
, SdrCreateCmd eCmd
)
2162 bool bRetval(impGetDAC().EndCreate(rStat
, eCmd
));
2164 if(bRetval
&& mpDAC
)
2166 SetPathPoly(mpDAC
->getModifiedPolyPolygon());
2168 // #i75974# Check for AutoClose feature. Moved here from ImpPathForDragAndCreate::EndCreate
2169 // to be able to use the type-changing ImpSetClosed method
2172 SdrView
* pView
= rStat
.GetView();
2174 if(pView
&& !pView
->IsUseIncompatiblePathCreateInterface())
2176 OutputDevice
* pOut
= pView
->GetFirstOutputDevice();
2180 if(GetPathPoly().count())
2182 const basegfx::B2DPolygon
aCandidate(GetPathPoly().getB2DPolygon(0));
2184 if(aCandidate
.count() > 2)
2186 // check distance of first and last point
2187 const sal_Int32
nCloseDist(pOut
->PixelToLogic(Size(pView
->GetAutoCloseDistPix(), 0)).Width());
2188 const basegfx::B2DVector
aDistVector(aCandidate
.getB2DPoint(aCandidate
.count() - 1) - aCandidate
.getB2DPoint(0));
2190 if(aDistVector
.getLength() <= static_cast<double>(nCloseDist
))
2207 bool SdrPathObj::BckCreate(SdrDragStat
& rStat
)
2209 return impGetDAC().BckCreate(rStat
);
2212 void SdrPathObj::BrkCreate(SdrDragStat
& rStat
)
2214 impGetDAC().BrkCreate(rStat
);
2220 basegfx::B2DPolyPolygon
SdrPathObj::TakeCreatePoly(const SdrDragStat
& rDrag
) const
2222 basegfx::B2DPolyPolygon aRetval
;
2226 aRetval
= mpDAC
->TakeObjectPolyPolygon(rDrag
);
2227 aRetval
.append(ImpPathForDragAndCreate::TakeDragPolyPolygon(rDrag
));
2233 // during drag or create, allow accessing the so-far created/modified polyPolygon
2234 basegfx::B2DPolyPolygon
SdrPathObj::getObjectPolyPolygon(const SdrDragStat
& rDrag
) const
2236 basegfx::B2DPolyPolygon aRetval
;
2240 aRetval
= mpDAC
->TakeObjectPolyPolygon(rDrag
);
2246 basegfx::B2DPolyPolygon
SdrPathObj::getDragPolyPolygon(const SdrDragStat
& rDrag
) const
2248 basegfx::B2DPolyPolygon aRetval
;
2252 aRetval
= ImpPathForDragAndCreate::TakeDragPolyPolygon(rDrag
);
2258 PointerStyle
SdrPathObj::GetCreatePointer() const
2260 return impGetDAC().GetCreatePointer();
2263 void SdrPathObj::NbcMove(const Size
& rSiz
)
2265 maPathPolygon
.transform(basegfx::utils::createTranslateB2DHomMatrix(rSiz
.Width(), rSiz
.Height()));
2267 // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
2268 SdrTextObj::NbcMove(rSiz
);
2271 void SdrPathObj::NbcResize(const Point
& rRef
, const Fraction
& xFact
, const Fraction
& yFact
)
2273 const double fResizeX(xFact
);
2274 const double fResizeY(yFact
);
2276 if(basegfx::fTools::equal(fResizeX
, 1.0) && basegfx::fTools::equal(fResizeY
, 1.0))
2278 // tdf#106792 avoid numerical unprecisions: If both scale factors are 1.0, do not
2279 // manipulate at all - that may change maGeo rapidly (and wrongly) in
2280 // SdrTextObj::NbcResize. Combined with the UNO API trying to not 'apply'
2281 // a rotation but to manipulate the existing one, this is fatal. So just
2282 // avoid this error as long as we have to deal with imprecise geometry
2287 basegfx::B2DHomMatrix
aTrans(basegfx::utils::createTranslateB2DHomMatrix(-rRef
.X(), -rRef
.Y()));
2288 aTrans
= basegfx::utils::createScaleTranslateB2DHomMatrix(
2289 double(xFact
), double(yFact
), rRef
.X(), rRef
.Y()) * aTrans
;
2290 maPathPolygon
.transform(aTrans
);
2292 // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
2293 SdrTextObj::NbcResize(rRef
,xFact
,yFact
);
2296 void SdrPathObj::NbcRotate(const Point
& rRef
, Degree100 nAngle
, double sn
, double cs
)
2298 // Thank JOE, the angles are defined mirrored to the mathematical meanings
2299 const basegfx::B2DHomMatrix
aTrans(
2300 basegfx::utils::createRotateAroundPoint(rRef
.X(), rRef
.Y(), -toRadians(nAngle
)));
2301 maPathPolygon
.transform(aTrans
);
2303 // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
2304 SdrTextObj::NbcRotate(rRef
,nAngle
,sn
,cs
);
2307 void SdrPathObj::NbcShear(const Point
& rRefPnt
, Degree100 nAngle
, double fTan
, bool bVShear
)
2309 basegfx::B2DHomMatrix
aTrans(basegfx::utils::createTranslateB2DHomMatrix(-rRefPnt
.X(), -rRefPnt
.Y()));
2313 // Thank JOE, the angles are defined mirrored to the mathematical meanings
2314 aTrans
.shearY(-fTan
);
2318 aTrans
.shearX(-fTan
);
2321 aTrans
.translate(rRefPnt
.X(), rRefPnt
.Y());
2322 maPathPolygon
.transform(aTrans
);
2324 // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
2325 SdrTextObj::NbcShear(rRefPnt
,nAngle
,fTan
,bVShear
);
2328 void SdrPathObj::NbcMirror(const Point
& rRefPnt1
, const Point
& rRefPnt2
)
2330 const double fDiffX(rRefPnt2
.X() - rRefPnt1
.X());
2331 const double fDiffY(rRefPnt2
.Y() - rRefPnt1
.Y());
2332 const double fRot(atan2(fDiffY
, fDiffX
));
2333 basegfx::B2DHomMatrix
aTrans(basegfx::utils::createTranslateB2DHomMatrix(-rRefPnt1
.X(), -rRefPnt1
.Y()));
2334 aTrans
.rotate(-fRot
);
2335 aTrans
.scale(1.0, -1.0);
2336 aTrans
.rotate(fRot
);
2337 aTrans
.translate(rRefPnt1
.X(), rRefPnt1
.Y());
2338 maPathPolygon
.transform(aTrans
);
2340 // Do Joe's special handling for lines when mirroring, too
2343 // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
2344 SdrTextObj::NbcMirror(rRefPnt1
,rRefPnt2
);
2347 void SdrPathObj::TakeUnrotatedSnapRect(tools::Rectangle
& rRect
) const
2349 if(!maGeo
.m_nRotationAngle
)
2351 rRect
= GetSnapRect();
2355 XPolyPolygon
aXPP(GetPathPoly());
2356 RotateXPoly(aXPP
,Point(),-maGeo
.mfSinRotationAngle
,maGeo
.mfCosRotationAngle
);
2357 rRect
=aXPP
.GetBoundRect();
2358 Point
aTmp(rRect
.TopLeft());
2359 RotatePoint(aTmp
,Point(),maGeo
.mfSinRotationAngle
,maGeo
.mfCosRotationAngle
);
2360 aTmp
-=rRect
.TopLeft();
2361 rRect
.Move(aTmp
.X(),aTmp
.Y());
2365 void SdrPathObj::RecalcSnapRect()
2367 if(GetPathPoly().count())
2369 maSnapRect
= lcl_ImpGetBoundRect(GetPathPoly());
2373 void SdrPathObj::NbcSetSnapRect(const tools::Rectangle
& rRect
)
2375 tools::Rectangle
aOld(GetSnapRect());
2380 NbcResize(aOld
.TopLeft(), aX
, aY
);
2381 NbcMove(Size(rRect
.Left() - aOld
.Left(), rRect
.Top() - aOld
.Top()));
2385 // Take empty into account when calculating scale factors
2386 tools::Long nMulX
= rRect
.IsWidthEmpty() ? 0 : rRect
.Right() - rRect
.Left();
2388 tools::Long nDivX
= aOld
.Right() - aOld
.Left();
2390 // Take empty into account when calculating scale factors
2391 tools::Long nMulY
= rRect
.IsHeightEmpty() ? 0 : rRect
.Bottom() - rRect
.Top();
2393 tools::Long nDivY
= aOld
.Bottom() - aOld
.Top();
2394 if ( nDivX
== 0 ) { nMulX
= 1; nDivX
= 1; }
2395 if ( nDivY
== 0 ) { nMulY
= 1; nDivY
= 1; }
2396 if ( nDivX
== nMulX
) { nMulX
= 1; nDivX
= 1; }
2397 if ( nDivY
== nMulY
) { nMulY
= 1; nDivY
= 1; }
2398 Fraction
aX(nMulX
,nDivX
);
2399 Fraction
aY(nMulY
,nDivY
);
2400 NbcResize(aOld
.TopLeft(), aX
, aY
);
2401 NbcMove(Size(rRect
.Left() - aOld
.Left(), rRect
.Top() - aOld
.Top()));
2404 sal_uInt32
SdrPathObj::GetSnapPointCount() const
2406 return GetHdlCount();
2409 Point
SdrPathObj::GetSnapPoint(sal_uInt32 nSnapPnt
) const
2411 sal_uInt32 nPoly
,nPnt
;
2412 if(!PolyPolygonEditor::GetRelativePolyPoint(GetPathPoly(), nSnapPnt
, nPoly
, nPnt
))
2414 SAL_WARN("svx", "SdrPathObj::GetSnapPoint: Point nSnapPnt does not exist.");
2417 const basegfx::B2DPoint
aB2DPoint(GetPathPoly().getB2DPolygon(nPoly
).getB2DPoint(nPnt
));
2418 return Point(basegfx::fround
<tools::Long
>(aB2DPoint
.getX()),
2419 basegfx::fround
<tools::Long
>(aB2DPoint
.getY()));
2422 bool SdrPathObj::IsPolyObj() const
2427 sal_uInt32
SdrPathObj::GetPointCount() const
2429 sal_uInt32
nRetval(0);
2431 for(auto const& rPolygon
: GetPathPoly())
2433 nRetval
+= rPolygon
.count();
2439 Point
SdrPathObj::GetPoint(sal_uInt32 nHdlNum
) const
2442 sal_uInt32 nPoly
,nPnt
;
2444 if(PolyPolygonEditor::GetRelativePolyPoint(GetPathPoly(), nHdlNum
, nPoly
, nPnt
))
2446 const basegfx::B2DPolygon
aPoly(GetPathPoly().getB2DPolygon(nPoly
));
2447 const basegfx::B2DPoint
aPoint(aPoly
.getB2DPoint(nPnt
));
2448 aRetval
= Point(basegfx::fround
<tools::Long
>(aPoint
.getX()),
2449 basegfx::fround
<tools::Long
>(aPoint
.getY()));
2455 void SdrPathObj::NbcSetPoint(const Point
& rPnt
, sal_uInt32 nHdlNum
)
2457 sal_uInt32 nPoly
,nPnt
;
2459 if(!PolyPolygonEditor::GetRelativePolyPoint(GetPathPoly(), nHdlNum
, nPoly
, nPnt
))
2462 basegfx::B2DPolygon
aNewPolygon(GetPathPoly().getB2DPolygon(nPoly
));
2463 aNewPolygon
.setB2DPoint(nPnt
, basegfx::B2DPoint(rPnt
.X(), rPnt
.Y()));
2464 maPathPolygon
.setB2DPolygon(nPoly
, aNewPolygon
);
2466 if(meKind
==SdrObjKind::Line
)
2468 ImpForceLineAngle();
2472 if(GetPathPoly().count())
2474 // #i10659# for SdrTextObj, keep aRect up to date
2475 setRectangle(lcl_ImpGetBoundRect(GetPathPoly()));
2479 SetBoundAndSnapRectsDirty();
2482 sal_uInt32
SdrPathObj::NbcInsPointOld(const Point
& rPos
, bool bNewObj
)
2488 nNewHdl
= NbcInsPoint(rPos
, true);
2492 // look for smallest distance data
2493 const basegfx::B2DPoint
aTestPoint(rPos
.X(), rPos
.Y());
2494 sal_uInt32
nSmallestPolyIndex(0);
2495 sal_uInt32
nSmallestEdgeIndex(0);
2496 double fSmallestCut
;
2497 basegfx::utils::getSmallestDistancePointToPolyPolygon(GetPathPoly(), aTestPoint
, nSmallestPolyIndex
, nSmallestEdgeIndex
, fSmallestCut
);
2499 nNewHdl
= NbcInsPoint(rPos
, false);
2506 sal_uInt32
SdrPathObj::NbcInsPoint(const Point
& rPos
, bool bNewObj
)
2512 basegfx::B2DPolygon aNewPoly
;
2513 const basegfx::B2DPoint
aPoint(rPos
.X(), rPos
.Y());
2514 aNewPoly
.append(aPoint
);
2515 aNewPoly
.setClosed(IsClosed());
2516 maPathPolygon
.append(aNewPoly
);
2517 SetBoundAndSnapRectsDirty();
2518 nNewHdl
= GetHdlCount();
2522 // look for smallest distance data
2523 const basegfx::B2DPoint
aTestPoint(rPos
.X(), rPos
.Y());
2524 sal_uInt32
nSmallestPolyIndex(0);
2525 sal_uInt32
nSmallestEdgeIndex(0);
2526 double fSmallestCut
;
2527 basegfx::utils::getSmallestDistancePointToPolyPolygon(GetPathPoly(), aTestPoint
, nSmallestPolyIndex
, nSmallestEdgeIndex
, fSmallestCut
);
2528 basegfx::B2DPolygon
aCandidate(GetPathPoly().getB2DPolygon(nSmallestPolyIndex
));
2529 const bool bBefore(!aCandidate
.isClosed() && 0 == nSmallestEdgeIndex
&& 0.0 == fSmallestCut
);
2530 const bool bAfter(!aCandidate
.isClosed() && aCandidate
.count() == nSmallestEdgeIndex
+ 2 && 1.0 == fSmallestCut
);
2534 // before first point
2535 aCandidate
.insert(0, aTestPoint
);
2537 if(aCandidate
.areControlPointsUsed())
2539 if(aCandidate
.isNextControlPointUsed(1))
2541 aCandidate
.setNextControlPoint(0, interpolate(aTestPoint
, aCandidate
.getB2DPoint(1), (1.0 / 3.0)));
2542 aCandidate
.setPrevControlPoint(1, interpolate(aTestPoint
, aCandidate
.getB2DPoint(1), (2.0 / 3.0)));
2551 aCandidate
.append(aTestPoint
);
2553 if(aCandidate
.areControlPointsUsed())
2555 if(aCandidate
.isPrevControlPointUsed(aCandidate
.count() - 2))
2557 aCandidate
.setNextControlPoint(aCandidate
.count() - 2, interpolate(aCandidate
.getB2DPoint(aCandidate
.count() - 2), aTestPoint
, (1.0 / 3.0)));
2558 aCandidate
.setPrevControlPoint(aCandidate
.count() - 1, interpolate(aCandidate
.getB2DPoint(aCandidate
.count() - 2), aTestPoint
, (2.0 / 3.0)));
2562 nNewHdl
= aCandidate
.count() - 1;
2567 bool bSegmentSplit(false);
2568 const sal_uInt32
nNextIndex((nSmallestEdgeIndex
+ 1) % aCandidate
.count());
2570 if(aCandidate
.areControlPointsUsed())
2572 if(aCandidate
.isNextControlPointUsed(nSmallestEdgeIndex
) || aCandidate
.isPrevControlPointUsed(nNextIndex
))
2574 bSegmentSplit
= true;
2580 // rebuild original segment to get the split data
2581 basegfx::B2DCubicBezier aBezierA
, aBezierB
;
2582 const basegfx::B2DCubicBezier
aBezier(
2583 aCandidate
.getB2DPoint(nSmallestEdgeIndex
),
2584 aCandidate
.getNextControlPoint(nSmallestEdgeIndex
),
2585 aCandidate
.getPrevControlPoint(nNextIndex
),
2586 aCandidate
.getB2DPoint(nNextIndex
));
2588 // split and insert hit point
2589 aBezier
.split(fSmallestCut
, &aBezierA
, &aBezierB
);
2590 aCandidate
.insert(nSmallestEdgeIndex
+ 1, aTestPoint
);
2592 // since we inserted hit point and not split point, we need to add an offset
2593 // to the control points to get the C1 continuity we want to achieve
2594 const basegfx::B2DVector
aOffset(aTestPoint
- aBezierA
.getEndPoint());
2595 aCandidate
.setNextControlPoint(nSmallestEdgeIndex
, aBezierA
.getControlPointA() + aOffset
);
2596 aCandidate
.setPrevControlPoint(nSmallestEdgeIndex
+ 1, aBezierA
.getControlPointB() + aOffset
);
2597 aCandidate
.setNextControlPoint(nSmallestEdgeIndex
+ 1, aBezierB
.getControlPointA() + aOffset
);
2598 aCandidate
.setPrevControlPoint((nSmallestEdgeIndex
+ 2) % aCandidate
.count(), aBezierB
.getControlPointB() + aOffset
);
2602 aCandidate
.insert(nSmallestEdgeIndex
+ 1, aTestPoint
);
2605 nNewHdl
= nSmallestEdgeIndex
+ 1;
2608 maPathPolygon
.setB2DPolygon(nSmallestPolyIndex
, aCandidate
);
2610 // create old polygon index from it
2611 for(sal_uInt32
a(0); a
< nSmallestPolyIndex
; a
++)
2613 nNewHdl
+= GetPathPoly().getB2DPolygon(a
).count();
2621 rtl::Reference
<SdrPathObj
> SdrPathObj::RipPoint(sal_uInt32 nHdlNum
, sal_uInt32
& rNewPt0Index
)
2623 rtl::Reference
<SdrPathObj
> pNewObj
;
2624 const basegfx::B2DPolyPolygon
aLocalPolyPolygon(GetPathPoly());
2625 sal_uInt32 nPoly
, nPnt
;
2627 if(PolyPolygonEditor::GetRelativePolyPoint(aLocalPolyPolygon
, nHdlNum
, nPoly
, nPnt
))
2631 const basegfx::B2DPolygon
& aCandidate(aLocalPolyPolygon
.getB2DPolygon(nPoly
));
2632 const sal_uInt32
nPointCount(aCandidate
.count());
2638 // when closed, RipPoint means to open the polygon at the selected point. To
2639 // be able to do that, it is necessary to make the selected point the first one
2640 basegfx::B2DPolygon
aNewPolygon(basegfx::utils::makeStartPoint(aCandidate
, nPnt
));
2641 SetPathPoly(basegfx::B2DPolyPolygon(aNewPolygon
));
2644 // give back new position of old start point (historical reasons)
2645 rNewPt0Index
= (nPointCount
- nPnt
) % nPointCount
;
2649 if(nPointCount
>= 3 && nPnt
!= 0 && nPnt
+ 1 < nPointCount
)
2651 // split in two objects at point nPnt
2652 basegfx::B2DPolygon
aSplitPolyA(aCandidate
, 0, nPnt
+ 1);
2653 SetPathPoly(basegfx::B2DPolyPolygon(aSplitPolyA
));
2655 pNewObj
= SdrObject::Clone(*this, getSdrModelFromSdrObject());
2656 basegfx::B2DPolygon
aSplitPolyB(aCandidate
, nPnt
, nPointCount
- nPnt
);
2657 pNewObj
->SetPathPoly(basegfx::B2DPolyPolygon(aSplitPolyB
));
2667 rtl::Reference
<SdrObject
> SdrPathObj::DoConvertToPolyObj(bool bBezier
, bool bAddText
) const
2669 // #i89784# check for FontWork with activated HideContour
2670 const drawinglayer::attribute::SdrTextAttribute
aText(
2671 drawinglayer::primitive2d::createNewSdrTextAttribute(GetObjectItemSet(), *getText(0)));
2672 const bool bHideContour(
2673 !aText
.isDefault() && !aText
.getSdrFormTextAttribute().isDefault() && aText
.isHideContour());
2675 rtl::Reference
<SdrObject
> pRet
;
2679 rtl::Reference
<SdrPathObj
> pPath
= ImpConvertMakeObj(GetPathPoly(), IsClosed(), bBezier
);
2681 if(pPath
->GetPathPoly().areControlPointsUsed())
2685 // reduce all bezier curves
2686 pPath
->SetPathPoly(basegfx::utils::adaptiveSubdivideByAngle(pPath
->GetPathPoly()));
2693 // create bezier curves
2694 pPath
->SetPathPoly(basegfx::utils::expandToCurve(pPath
->GetPathPoly()));
2697 pRet
= std::move(pPath
);
2702 pRet
= ImpConvertAddText(std::move(pRet
), bBezier
);
2708 std::unique_ptr
<SdrObjGeoData
> SdrPathObj::NewGeoData() const
2710 return std::make_unique
<SdrPathObjGeoData
>();
2713 void SdrPathObj::SaveGeoData(SdrObjGeoData
& rGeo
) const
2715 SdrTextObj::SaveGeoData(rGeo
);
2716 SdrPathObjGeoData
& rPGeo
= static_cast<SdrPathObjGeoData
&>( rGeo
);
2717 rPGeo
.maPathPolygon
=GetPathPoly();
2718 rPGeo
.meKind
=meKind
;
2721 void SdrPathObj::RestoreGeoData(const SdrObjGeoData
& rGeo
)
2723 SdrTextObj::RestoreGeoData(rGeo
);
2724 const SdrPathObjGeoData
& rPGeo
=static_cast<const SdrPathObjGeoData
&>(rGeo
);
2725 maPathPolygon
=rPGeo
.maPathPolygon
;
2726 meKind
=rPGeo
.meKind
;
2727 ImpForceKind(); // to set bClosed (among other things)
2730 void SdrPathObj::NbcSetPathPoly(const basegfx::B2DPolyPolygon
& rPathPoly
)
2732 if(GetPathPoly() != rPathPoly
)
2734 maPathPolygon
=rPathPoly
;
2736 SetBoundAndSnapRectsDirty();
2740 void SdrPathObj::SetPathPoly(const basegfx::B2DPolyPolygon
& rPathPoly
)
2742 if(GetPathPoly() != rPathPoly
)
2744 tools::Rectangle aBoundRect0
; if (m_pUserCall
!=nullptr) aBoundRect0
=GetLastBoundRect();
2745 NbcSetPathPoly(rPathPoly
);
2747 BroadcastObjectChange();
2748 SendUserCall(SdrUserCallType::Resize
,aBoundRect0
);
2752 void SdrPathObj::ToggleClosed()
2754 tools::Rectangle aBoundRect0
;
2755 if(m_pUserCall
!= nullptr)
2756 aBoundRect0
= GetLastBoundRect();
2757 ImpSetClosed(!IsClosed()); // set new ObjKind
2758 ImpForceKind(); // because we want Line -> Poly -> PolyLine instead of Line -> Poly -> Line
2759 SetBoundAndSnapRectsDirty();
2761 BroadcastObjectChange();
2762 SendUserCall(SdrUserCallType::Resize
, aBoundRect0
);
2765 ImpPathForDragAndCreate
& SdrPathObj::impGetDAC() const
2769 const_cast<SdrPathObj
*>(this)->mpDAC
.reset(new ImpPathForDragAndCreate(*const_cast<SdrPathObj
*>(this)));
2776 // transformation interface for StarOfficeAPI. This implements support for
2777 // homogeneous 3x3 matrices containing the transformation of the SdrObject. At the
2778 // moment it contains a shearX, rotation and translation, but for setting all linear
2779 // transforms like Scale, ShearX, ShearY, Rotate and Translate are supported.
2782 // gets base transformation and rectangle of object. If it's an SdrPathObj it fills the PolyPolygon
2783 // with the base geometry and returns TRUE. Otherwise it returns FALSE.
2784 bool SdrPathObj::TRGetBaseGeometry(basegfx::B2DHomMatrix
& rMatrix
, basegfx::B2DPolyPolygon
& rPolyPolygon
) const
2786 double fRotate(0.0);
2787 double fShearX(0.0);
2788 basegfx::B2DTuple
aScale(1.0, 1.0);
2789 basegfx::B2DTuple
aTranslate(0.0, 0.0);
2791 if(GetPathPoly().count())
2794 basegfx::B2DHomMatrix aMoveToZeroMatrix
;
2795 rPolyPolygon
= GetPathPoly();
2797 if(SdrObjKind::Line
== meKind
)
2799 // ignore shear and rotate, just use scale and translate
2800 OSL_ENSURE(GetPathPoly().count() > 0 && GetPathPoly().getB2DPolygon(0).count() > 1, "OBJ_LINE with too few polygons (!)");
2801 // #i72287# use polygon without control points for range calculation. Do not change rPolyPolygon
2802 // itself, else this method will no longer return the full polygon information (curve will
2804 const basegfx::B2DRange
aPolyRangeNoCurve(basegfx::utils::getRange(rPolyPolygon
));
2805 aScale
= aPolyRangeNoCurve
.getRange();
2806 aTranslate
= aPolyRangeNoCurve
.getMinimum();
2808 // define matrix for move polygon to zero point
2809 aMoveToZeroMatrix
.translate(-aTranslate
.getX(), -aTranslate
.getY());
2813 if(maGeo
.m_nShearAngle
|| maGeo
.m_nRotationAngle
)
2815 // get rotate and shear in drawingLayer notation
2816 fRotate
= toRadians(maGeo
.m_nRotationAngle
);
2817 fShearX
= toRadians(maGeo
.m_nShearAngle
);
2819 // build mathematically correct (negative shear and rotate) object transform
2820 // containing shear and rotate to extract unsheared, unrotated polygon
2821 basegfx::B2DHomMatrix aObjectMatrix
;
2822 aObjectMatrix
.shearX(-maGeo
.mfTanShearAngle
);
2823 aObjectMatrix
.rotate(toRadians(36000_deg100
- maGeo
.m_nRotationAngle
));
2825 // create inverse from it and back-transform polygon
2826 basegfx::B2DHomMatrix
aInvObjectMatrix(aObjectMatrix
);
2827 aInvObjectMatrix
.invert();
2828 rPolyPolygon
.transform(aInvObjectMatrix
);
2830 // get range from unsheared, unrotated polygon and extract scale and translate.
2831 // transform topLeft from it back to transformed state to get original
2832 // topLeft (rotation center)
2833 // #i72287# use polygon without control points for range calculation. Do not change rPolyPolygon
2834 // itself, else this method will no longer return the full polygon information (curve will
2836 const basegfx::B2DRange
aCorrectedRangeNoCurve(basegfx::utils::getRange(rPolyPolygon
));
2837 aTranslate
= aObjectMatrix
* aCorrectedRangeNoCurve
.getMinimum();
2838 aScale
= aCorrectedRangeNoCurve
.getRange();
2840 // define matrix for move polygon to zero point
2841 // #i112280# Added missing minus for Y-Translation
2842 aMoveToZeroMatrix
.translate(-aCorrectedRangeNoCurve
.getMinX(), -aCorrectedRangeNoCurve
.getMinY());
2846 // get scale and translate from unsheared, unrotated polygon
2847 // #i72287# use polygon without control points for range calculation. Do not change rPolyPolygon
2848 // itself, else this method will no longer return the full polygon information (curve will
2850 const basegfx::B2DRange
aPolyRangeNoCurve(basegfx::utils::getRange(rPolyPolygon
));
2851 if (!aPolyRangeNoCurve
.isEmpty())
2853 aScale
= aPolyRangeNoCurve
.getRange();
2854 aTranslate
= aPolyRangeNoCurve
.getMinimum();
2856 // define matrix for move polygon to zero point
2857 aMoveToZeroMatrix
.translate(-aTranslate
.getX(), -aTranslate
.getY());
2862 // move polygon to zero point with pre-defined matrix
2863 rPolyPolygon
.transform(aMoveToZeroMatrix
);
2866 // position maybe relative to anchorpos, convert
2867 if( getSdrModelFromSdrObject().IsWriter() )
2869 if(GetAnchorPos().X() || GetAnchorPos().Y())
2871 aTranslate
-= basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
2875 // build return value matrix
2876 rMatrix
= basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
2878 basegfx::fTools::equalZero(fShearX
) ? 0.0 : tan(fShearX
),
2879 basegfx::fTools::equalZero(fRotate
) ? 0.0 : -fRotate
,
2885 void SdrPathObj::SetHandleScale(bool bHandleScale
)
2887 mbHandleScale
= bHandleScale
;
2890 // Sets the base geometry of the object using infos contained in the homogeneous 3x3 matrix.
2891 // If it's an SdrPathObj it will use the provided geometry information. The Polygon has
2892 // to use (0,0) as upper left and will be scaled to the given size in the matrix.
2893 void SdrPathObj::TRSetBaseGeometry(const basegfx::B2DHomMatrix
& rMatrix
, const basegfx::B2DPolyPolygon
& rPolyPolygon
)
2896 basegfx::B2DTuple aScale
;
2897 basegfx::B2DTuple aTranslate
;
2898 double fRotate
, fShearX
;
2899 rMatrix
.decompose(aScale
, aTranslate
, fRotate
, fShearX
);
2901 // #i75086# Old DrawingLayer (GeoStat and geometry) does not support holding negative scalings
2902 // in X and Y which equal a 180 degree rotation. Recognize it and react accordingly
2903 if(basegfx::fTools::less(aScale
.getX(), 0.0) && basegfx::fTools::less(aScale
.getY(), 0.0))
2905 aScale
.setX(fabs(aScale
.getX()));
2906 aScale
.setY(fabs(aScale
.getY()));
2907 fRotate
= fmod(fRotate
+ M_PI
, 2 * M_PI
);
2911 basegfx::B2DPolyPolygon
aNewPolyPolygon(rPolyPolygon
);
2913 // reset object shear and rotations
2914 maGeo
.m_nRotationAngle
= 0_deg100
;
2915 maGeo
.RecalcSinCos();
2916 maGeo
.m_nShearAngle
= 0_deg100
;
2919 if( getSdrModelFromSdrObject().IsWriter() )
2921 // if anchor is used, make position relative to it
2922 if(GetAnchorPos().X() || GetAnchorPos().Y())
2924 aTranslate
+= basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
2928 // create transformation for polygon, set values at maGeo direct
2929 basegfx::B2DHomMatrix aTransform
;
2932 // Given polygon is already scaled (for historical reasons), but not mirrored yet.
2933 // Thus, when scale is negative in X or Y, apply the needed mirroring accordingly.
2934 double fScaleX(basegfx::fTools::less(aScale
.getX(), 0.0) ? -1.0 : 1.0);
2935 double fScaleY(basegfx::fTools::less(aScale
.getY(), 0.0) ? -1.0 : 1.0);
2937 // tdf#98565, tdf#98584. While loading a shape, svg:width and svg:height is used to scale
2938 // the polygon. But draw:transform might introduce additional scaling factors, which need to
2939 // be applied to the polygon too, so aScale cannot be ignored while loading.
2940 // I use "maSnapRect.IsEmpty() && GetPathPoly().count()" to detect this case. Any better
2941 // idea? The behavior in other cases is the same as it was before this fix.
2942 if (maSnapRect
.IsEmpty() && GetPathPoly().count() && mbHandleScale
)
2944 // In case of a Writer document, the scaling factors were converted to twips. That is not
2945 // correct here, because width and height are already in the points coordinates and aScale
2946 // is no length but only a factor here. Convert back.
2947 if (getSdrModelFromSdrObject().IsWriter())
2949 aScale
.setX(o3tl::convert(aScale
.getX(), o3tl::Length::twip
, o3tl::Length::mm100
));
2950 aScale
.setY(o3tl::convert(aScale
.getY(), o3tl::Length::twip
, o3tl::Length::mm100
));
2952 fScaleX
*= fabs(aScale
.getX());
2953 fScaleY
*= fabs(aScale
.getY());
2956 if (fScaleX
!= 1.0 || fScaleY
!= 1.0)
2957 aTransform
.scale(fScaleX
, fScaleY
);
2959 if(!basegfx::fTools::equalZero(fShearX
))
2961 aTransform
.shearX(tan(-atan(fShearX
)));
2962 maGeo
.m_nShearAngle
= Degree100(basegfx::fround(basegfx::rad2deg
<100>(atan(fShearX
))));
2966 if(!basegfx::fTools::equalZero(fRotate
))
2969 // fRotate is mathematically correct for linear transformations, so it's
2970 // the one to use for the geometry change
2971 aTransform
.rotate(fRotate
);
2974 // fRotate is mathematically correct, but aGeoStat.nRotationAngle is
2975 // mirrored -> mirror value here
2976 maGeo
.m_nRotationAngle
= NormAngle36000(Degree100(basegfx::fround(-basegfx::rad2deg
<100>(fRotate
))));
2977 maGeo
.RecalcSinCos();
2980 if(!aTranslate
.equalZero())
2982 // #i39529# absolute positioning, so get current position (without control points (!))
2983 const basegfx::B2DRange
aCurrentRange(basegfx::utils::getRange(aNewPolyPolygon
));
2984 aTransform
.translate(aTranslate
.getX() - aCurrentRange
.getMinX(), aTranslate
.getY() - aCurrentRange
.getMinY());
2987 // transform polygon and trigger change
2988 aNewPolyPolygon
.transform(aTransform
);
2989 SetPathPoly(aNewPolyPolygon
);
2992 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */