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 <tools/bigint.hxx>
21 #include <tools/helpers.hxx>
22 #include <svx/svdopath.hxx>
24 #include <svx/xpool.hxx>
25 #include <svx/xpoly.hxx>
26 #include <svx/svdattr.hxx>
27 #include <svx/svdtrans.hxx>
28 #include <svx/svdetc.hxx>
29 #include <svx/svddrag.hxx>
30 #include <svx/svdmodel.hxx>
31 #include <svx/svdpage.hxx>
32 #include <svx/svdhdl.hxx>
33 #include <svx/svdview.hxx>
34 #include <svdglob.hxx>
35 #include <svx/strings.hrc>
37 #include <svx/xlnwtit.hxx>
38 #include <svx/xlnclit.hxx>
39 #include <svx/xflclit.hxx>
40 #include <svx/svdogrp.hxx>
41 #include <svx/polypolygoneditor.hxx>
42 #include <svx/xlntrit.hxx>
43 #include <sdr/contact/viewcontactofsdrpathobj.hxx>
44 #include <basegfx/matrix/b2dhommatrix.hxx>
45 #include "svdconv.hxx"
46 #include <basegfx/point/b2dpoint.hxx>
47 #include <basegfx/polygon/b2dpolypolygontools.hxx>
48 #include <basegfx/range/b2drange.hxx>
49 #include <basegfx/curve/b2dcubicbezier.hxx>
50 #include <basegfx/polygon/b2dpolygontools.hxx>
51 #include <svx/sdr/attribute/sdrtextattribute.hxx>
52 #include <svx/sdr/primitive2d/sdrattributecreator.hxx>
53 #include <basegfx/matrix/b2dhommatrixtools.hxx>
54 #include <svx/sdr/attribute/sdrformtextattribute.hxx>
59 inline sal_uInt16
GetPrevPnt(sal_uInt16 nPnt
, sal_uInt16 nPntMax
, bool bClosed
)
70 inline sal_uInt16
GetNextPnt(sal_uInt16 nPnt
, sal_uInt16 nPntMax
, bool bClosed
)
73 if (nPnt
>nPntMax
|| (bClosed
&& nPnt
>=nPntMax
)) nPnt
=0;
77 struct ImpSdrPathDragData
: public SdrDragStatUserData
79 XPolygon aXP
; // section of the original polygon
80 bool bValid
; // FALSE = too few points
81 bool bClosed
; // closed object?
82 sal_uInt16 nPoly
; // number of the polygon in the PolyPolygon
83 sal_uInt16 nPnt
; // number of point in the above polygon
84 sal_uInt16 nPointCount
; // number of points of the polygon
85 sal_uInt16 nPntMax
; // maximum index
86 bool bBegPnt
; // dragged point is first point of a Polyline
87 bool bEndPnt
; // dragged point is finishing point of a Polyline
88 sal_uInt16 nPrevPnt
; // index of previous point
89 sal_uInt16 nNextPnt
; // index of next point
90 bool bPrevIsBegPnt
; // previous point is first point of a Polyline
91 bool bNextIsEndPnt
; // next point is first point of a Polyline
92 sal_uInt16 nPrevPrevPnt
; // index of point before previous point
93 sal_uInt16 nNextNextPnt
; // index of point after next point
94 bool bControl
; // point is a control point
95 bool bIsPrevControl
; // point is a control point before a support point
96 bool bIsNextControl
; // point is a control point after a support point
97 bool bPrevIsControl
; // if nPnt is a support point: a control point comes before
98 bool bNextIsControl
; // if nPnt is a support point: a control point comes after
99 sal_uInt16 nPrevPrevPnt0
;
100 sal_uInt16 nPrevPnt0
;
102 sal_uInt16 nNextPnt0
;
103 sal_uInt16 nNextNextPnt0
;
104 bool bEliminate
; // delete point? (is set by MovDrag)
106 bool mbMultiPointDrag
;
107 const XPolyPolygon maOrig
;
109 std::vector
<SdrHdl
*> maHandles
;
112 ImpSdrPathDragData(const SdrPathObj
& rPO
, const SdrHdl
& rHdl
, bool bMuPoDr
, const SdrDragStat
& rDrag
);
113 void ResetPoly(const SdrPathObj
& rPO
);
114 bool IsMultiPointDrag() const { return mbMultiPointDrag
; }
117 ImpSdrPathDragData::ImpSdrPathDragData(const SdrPathObj
& rPO
, const SdrHdl
& rHdl
, bool bMuPoDr
, const SdrDragStat
& rDrag
)
129 , bPrevIsBegPnt(false)
130 , bNextIsEndPnt(false)
134 , bIsPrevControl(false)
135 , bIsNextControl(false)
136 , bPrevIsControl(false)
137 , bNextIsControl(false)
144 , mbMultiPointDrag(bMuPoDr
)
145 , maOrig(rPO
.GetPathPoly())
150 const SdrMarkView
& rMarkView
= *rDrag
.GetView();
151 const SdrHdlList
& rHdlList
= rMarkView
.GetHdlList();
152 const size_t nHdlCount
= rHdlList
.GetHdlCount();
153 const SdrObject
* pInteractionObject(nHdlCount
&& rHdlList
.GetHdl(0) ? rHdlList
.GetHdl(0)->GetObj() : nullptr);
155 for(size_t a
= 0; a
< nHdlCount
; ++a
)
157 SdrHdl
* pTestHdl
= rHdlList
.GetHdl(a
);
159 if(pTestHdl
&& pTestHdl
->IsSelected() && pTestHdl
->GetObj() == pInteractionObject
)
161 maHandles
.push_back(pTestHdl
);
171 bClosed
=rPO
.IsClosed(); // closed object?
172 nPoly
=(sal_uInt16
)rHdl
.GetPolyNum(); // number of the polygon in the PolyPolygon
173 nPnt
=(sal_uInt16
)rHdl
.GetPointNum(); // number of points in the above polygon
174 const XPolygon
aTmpXP(rPO
.GetPathPoly().getB2DPolygon(nPoly
));
175 nPointCount
=aTmpXP
.GetPointCount(); // number of point of the polygon
176 if (nPointCount
==0 || (bClosed
&& nPointCount
==1)) return; // minimum of 1 points for Lines, minimum of 2 points for Polygon
177 nPntMax
=nPointCount
-1; // maximum index
178 bBegPnt
=!bClosed
&& nPnt
==0; // dragged point is first point of a Polyline
179 bEndPnt
=!bClosed
&& nPnt
==nPntMax
; // dragged point is finishing point of a Polyline
180 if (bClosed
&& nPointCount
<=3) { // if polygon is only a line
181 bBegPnt
=(nPointCount
<3) || nPnt
==0;
182 bEndPnt
=(nPointCount
<3) || nPnt
==nPntMax
-1;
184 nPrevPnt
=nPnt
; // index of previous point
185 nNextPnt
=nPnt
; // index of next point
186 if (!bBegPnt
) nPrevPnt
=GetPrevPnt(nPnt
,nPntMax
,bClosed
);
187 if (!bEndPnt
) nNextPnt
=GetNextPnt(nPnt
,nPntMax
,bClosed
);
188 bPrevIsBegPnt
=bBegPnt
|| (!bClosed
&& nPrevPnt
==0);
189 bNextIsEndPnt
=bEndPnt
|| (!bClosed
&& nNextPnt
==nPntMax
);
190 nPrevPrevPnt
=nPnt
; // index of point before previous point
191 nNextNextPnt
=nPnt
; // index of point after next point
192 if (!bPrevIsBegPnt
) nPrevPrevPnt
=GetPrevPnt(nPrevPnt
,nPntMax
,bClosed
);
193 if (!bNextIsEndPnt
) nNextNextPnt
=GetNextPnt(nNextPnt
,nPntMax
,bClosed
);
194 bControl
=rHdl
.IsPlusHdl(); // point is a control point
195 bIsPrevControl
=false; // point is a control point before a support point
196 bIsNextControl
=false; // point is a control point after a support point
197 bPrevIsControl
=false; // if nPnt is a support point: a control point comes before
198 bNextIsControl
=false; // if nPnt is a support point: a control point comes after
200 bIsPrevControl
=aTmpXP
.IsControl(nPrevPnt
);
201 bIsNextControl
=!bIsPrevControl
;
203 bPrevIsControl
=!bBegPnt
&& !bPrevIsBegPnt
&& aTmpXP
.GetFlags(nPrevPnt
)==PolyFlags::Control
;
204 bNextIsControl
=!bEndPnt
&& !bNextIsEndPnt
&& aTmpXP
.GetFlags(nNextPnt
)==PolyFlags::Control
;
206 nPrevPrevPnt0
=nPrevPrevPnt
;
210 nNextNextPnt0
=nNextNextPnt
;
222 void ImpSdrPathDragData::ResetPoly(const SdrPathObj
& rPO
)
224 const XPolygon
aTmpXP(rPO
.GetPathPoly().getB2DPolygon(nPoly
));
225 aXP
[0]=aTmpXP
[nPrevPrevPnt0
]; aXP
.SetFlags(0,aTmpXP
.GetFlags(nPrevPrevPnt0
));
226 aXP
[1]=aTmpXP
[nPrevPnt0
]; aXP
.SetFlags(1,aTmpXP
.GetFlags(nPrevPnt0
));
227 aXP
[2]=aTmpXP
[nPnt0
]; aXP
.SetFlags(2,aTmpXP
.GetFlags(nPnt0
));
228 aXP
[3]=aTmpXP
[nNextPnt0
]; aXP
.SetFlags(3,aTmpXP
.GetFlags(nNextPnt0
));
229 aXP
[4]=aTmpXP
[nNextNextPnt0
]; aXP
.SetFlags(4,aTmpXP
.GetFlags(nNextNextPnt0
));
232 struct ImpPathCreateUser
: public SdrDragStatUserData
258 sal_uInt16 nBezierStartPoint
;
259 SdrObjKind eStartKind
;
263 ImpPathCreateUser(): nCircRadius(0),nCircStAngle(0),nCircRelAngle(0),
264 bBezier(false),bBezHasCtrl0(false),bCircle(false),bAngleSnap(false),bLine(false),bLine90(false),bRect(false),
265 bMixedCreate(false),nBezierStartPoint(0),eStartKind(OBJ_NONE
),eAktKind(OBJ_NONE
) { }
267 void ResetFormFlags() { bBezier
=false; bCircle
=false; bLine
=false; bRect
=false; }
268 bool IsFormFlag() const { return bBezier
|| bCircle
|| bLine
|| bRect
; }
269 XPolygon
GetFormPoly() const;
270 void CalcBezier(const Point
& rP1
, const Point
& rP2
, const Point
& rDir
, bool bMouseDown
);
271 XPolygon
GetBezierPoly() const;
272 void CalcCircle(const Point
& rP1
, const Point
& rP2
, const Point
& rDir
, SdrView
const * pView
);
273 XPolygon
GetCirclePoly() const;
274 void CalcLine(const Point
& rP1
, const Point
& rP2
, const Point
& rDir
, SdrView
const * pView
);
275 static Point
CalcLine(const Point
& rCsr
, long nDirX
, long nDirY
, SdrView
const * pView
);
276 XPolygon
GetLinePoly() const;
277 void CalcRect(const Point
& rP1
, const Point
& rP2
, const Point
& rDir
, SdrView
const * pView
);
278 XPolygon
GetRectPoly() const;
281 XPolygon
ImpPathCreateUser::GetFormPoly() const
283 if (bBezier
) return GetBezierPoly();
284 if (bCircle
) return GetCirclePoly();
285 if (bLine
) return GetLinePoly();
286 if (bRect
) return GetRectPoly();
290 void ImpPathCreateUser::CalcBezier(const Point
& rP1
, const Point
& rP2
, const Point
& rDir
, bool bMouseDown
)
297 // Also copy the end point when no end point is set yet
298 if (!bMouseDown
|| (0 == aBezEnd
.X() && 0 == aBezEnd
.Y())) aBezEnd
=rP2
;
303 XPolygon
ImpPathCreateUser::GetBezierPoly() const
306 aXP
[0]=aBezStart
; aXP
.SetFlags(0,PolyFlags::Smooth
);
307 aXP
[1]=aBezCtrl1
; aXP
.SetFlags(1,PolyFlags::Control
);
308 aXP
[2]=aBezCtrl2
; aXP
.SetFlags(2,PolyFlags::Control
);
313 void ImpPathCreateUser::CalcCircle(const Point
& rP1
, const Point
& rP2
, const Point
& rDir
, SdrView
const * pView
)
315 long nTangAngle
=GetAngle(rDir
);
319 long dx
=rP2
.X()-rP1
.X();
320 long dy
=rP2
.Y()-rP1
.Y();
321 long dAngle
=GetAngle(Point(dx
,dy
))-nTangAngle
;
322 dAngle
=NormAngle360(dAngle
);
323 long nTmpAngle
=NormAngle360(9000-dAngle
);
324 bool bRet
=nTmpAngle
!=9000 && nTmpAngle
!=27000;
327 double cs
=cos(nTmpAngle
*nPi180
);
328 double nR
=(double)GetLen(Point(dx
,dy
))/cs
/2;
329 nRad
=std::abs(svx::Round(nR
));
332 nCircStAngle
=NormAngle360(nTangAngle
-9000);
333 nCircRelAngle
=NormAngle360(2*dAngle
);
334 aCircCenter
.X()+=svx::Round(nRad
*cos((nTangAngle
+9000)*nPi180
));
335 aCircCenter
.Y()-=svx::Round(nRad
*sin((nTangAngle
+9000)*nPi180
));
337 nCircStAngle
=NormAngle360(nTangAngle
+9000);
338 nCircRelAngle
=-NormAngle360(36000-2*dAngle
);
339 aCircCenter
.X()+=svx::Round(nRad
*cos((nTangAngle
-9000)*nPi180
));
340 aCircCenter
.Y()-=svx::Round(nRad
*sin((nTangAngle
-9000)*nPi180
));
342 bAngleSnap
=pView
!=nullptr && pView
->IsAngleSnapEnabled();
344 long nSA
=pView
->GetSnapAngle();
345 if (nSA
!=0) { // angle snapping
346 bool bNeg
=nCircRelAngle
<0;
347 if (bNeg
) nCircRelAngle
=-nCircRelAngle
;
348 nCircRelAngle
+=nSA
/2;
351 nCircRelAngle
=NormAngle360(nCircRelAngle
);
352 if (bNeg
) nCircRelAngle
=-nCircRelAngle
;
356 if (nRad
==0 || std::abs(nCircRelAngle
)<5) bRet
=false;
360 XPolygon
ImpPathCreateUser::GetCirclePoly() const
362 if (nCircRelAngle
>=0) {
363 XPolygon
aXP(aCircCenter
,nCircRadius
,nCircRadius
,
364 sal_uInt16((nCircStAngle
+5)/10),sal_uInt16((nCircStAngle
+nCircRelAngle
+5)/10),false);
365 aXP
[0]=aCircStart
; aXP
.SetFlags(0,PolyFlags::Smooth
);
366 if (!bAngleSnap
) aXP
[aXP
.GetPointCount()-1]=aCircEnd
;
369 XPolygon
aXP(aCircCenter
,nCircRadius
,nCircRadius
,
370 sal_uInt16(NormAngle360(nCircStAngle
+nCircRelAngle
+5)/10),sal_uInt16((nCircStAngle
+5)/10),false);
371 sal_uInt16 nCount
=aXP
.GetPointCount();
372 for (sal_uInt16 nNum
=nCount
/2; nNum
>0;) {
373 nNum
--; // reverse XPoly's order of points
374 sal_uInt16 n2
=nCount
-nNum
-1;
375 Point
aPt(aXP
[nNum
]);
379 aXP
[0]=aCircStart
; aXP
.SetFlags(0,PolyFlags::Smooth
);
380 if (!bAngleSnap
) aXP
[aXP
.GetPointCount()-1]=aCircEnd
;
385 Point
ImpPathCreateUser::CalcLine(const Point
& aCsr
, long nDirX
, long nDirY
, SdrView
const * pView
)
394 long x1
=BigMulDiv(y
,nDirX
,nDirY
);
397 long y2
=BigMulDiv(x
,nDirY
,nDirX
);
398 long l1
=std::abs(x1
)+std::abs(y1
);
399 long l2
=std::abs(x2
)+std::abs(y2
);
400 if ((l1
<=l2
) != (pView
!=nullptr && pView
->IsBigOrtho())) {
409 void ImpPathCreateUser::CalcLine(const Point
& rP1
, const Point
& rP2
, const Point
& rDir
, SdrView
const * pView
)
414 if (rP1
==rP2
|| (rDir
.X()==0 && rDir
.Y()==0)) { bLine
=false; return; }
415 Point
aTmpPt(rP2
-rP1
);
418 Point
aP1(CalcLine(aTmpPt
, nDirX
, nDirY
,pView
)); aP1
-=aTmpPt
; long nQ1
=std::abs(aP1
.X())+std::abs(aP1
.Y());
419 Point
aP2(CalcLine(aTmpPt
, nDirY
,-nDirX
,pView
)); aP2
-=aTmpPt
; long nQ2
=std::abs(aP2
.X())+std::abs(aP2
.Y());
420 if (pView
!=nullptr && pView
->IsOrtho()) nQ1
=0; // Ortho turns off at right angle
422 if (!bLine90
) { // smooth transition
424 } else { // rectangular transition
430 XPolygon
ImpPathCreateUser::GetLinePoly() const
433 aXP
[0]=aLineStart
; if (!bLine90
) aXP
.SetFlags(0,PolyFlags::Smooth
);
438 void ImpPathCreateUser::CalcRect(const Point
& rP1
, const Point
& rP2
, const Point
& rDir
, SdrView
const * pView
)
443 if (rP1
==rP2
|| (rDir
.X()==0 && rDir
.Y()==0)) { bRect
=false; return; }
444 Point
aTmpPt(rP2
-rP1
);
454 y
=BigMulDiv(x
,nDirY
,nDirX
);
455 long nHypLen
=aTmpPt
.Y()-y
;
456 long nTangAngle
=-GetAngle(rDir
);
458 double a
=nTangAngle
*nPi180
;
461 double nGKathLen
=nHypLen
*sn
;
462 y
+=svx::Round(nGKathLen
*sn
);
463 x
+=svx::Round(nGKathLen
*cs
);
467 if (pView
!=nullptr && pView
->IsOrtho()) {
468 long dx1
=aRectP2
.X()-aRectP1
.X(); long dx1a
=std::abs(dx1
);
469 long dy1
=aRectP2
.Y()-aRectP1
.Y(); long dy1a
=std::abs(dy1
);
470 long dx2
=aRectP3
.X()-aRectP2
.X(); long dx2a
=std::abs(dx2
);
471 long dy2
=aRectP3
.Y()-aRectP2
.Y(); long dy2a
=std::abs(dy2
);
472 bool b1MoreThan2
=dx1a
+dy1a
>dx2a
+dy2a
;
473 if (b1MoreThan2
!= pView
->IsBigOrtho()) {
474 long xtemp
=dy2a
-dx1a
; if (dx1
<0) xtemp
=-xtemp
;
475 long ytemp
=dx2a
-dy1a
; if (dy1
<0) ytemp
=-ytemp
;
481 long xtemp
=dy1a
-dx2a
; if (dx2
<0) xtemp
=-xtemp
;
482 long ytemp
=dx1a
-dy2a
; if (dy2
<0) ytemp
=-ytemp
;
490 XPolygon
ImpPathCreateUser::GetRectPoly() const
493 aXP
[0]=aRectP1
; aXP
.SetFlags(0,PolyFlags::Smooth
);
495 if (aRectP3
!=aRectP2
) aXP
[2]=aRectP3
;
499 class ImpPathForDragAndCreate
501 SdrPathObj
& mrSdrPathObject
;
502 XPolyPolygon aPathPolygon
;
503 SdrObjKind meObjectKind
;
504 std::unique_ptr
<ImpSdrPathDragData
>
509 explicit ImpPathForDragAndCreate(SdrPathObj
& rSdrPathObject
);
512 bool beginPathDrag( SdrDragStat
const & rDrag
) const;
513 bool movePathDrag( SdrDragStat
& rDrag
) const;
514 bool endPathDrag( SdrDragStat
const & rDrag
);
515 OUString
getSpecialDragComment(const SdrDragStat
& rDrag
) const;
516 basegfx::B2DPolyPolygon
getSpecialDragPoly(const SdrDragStat
& rDrag
) const;
519 bool BegCreate(SdrDragStat
& rStat
);
520 bool MovCreate(SdrDragStat
& rStat
);
521 bool EndCreate(SdrDragStat
& rStat
, SdrCreateCmd eCmd
);
522 bool BckCreate(SdrDragStat
& rStat
);
523 void BrkCreate(SdrDragStat
& rStat
);
524 Pointer
GetCreatePointer() const;
527 static bool IsClosed(SdrObjKind eKind
) { return eKind
==OBJ_POLY
|| eKind
==OBJ_PATHPOLY
|| eKind
==OBJ_PATHFILL
|| eKind
==OBJ_FREEFILL
|| eKind
==OBJ_SPLNFILL
; }
528 static bool IsFreeHand(SdrObjKind eKind
) { return eKind
==OBJ_FREELINE
|| eKind
==OBJ_FREEFILL
; }
529 static bool IsBezier(SdrObjKind eKind
) { return eKind
==OBJ_PATHLINE
|| eKind
==OBJ_PATHFILL
; }
530 bool IsCreating() const { return mbCreating
; }
533 basegfx::B2DPolyPolygon
TakeObjectPolyPolygon(const SdrDragStat
& rDrag
) const;
534 static basegfx::B2DPolyPolygon
TakeDragPolyPolygon(const SdrDragStat
& rDrag
);
535 basegfx::B2DPolyPolygon
getModifiedPolyPolygon() const { return aPathPolygon
.getB2DPolyPolygon(); }
538 ImpPathForDragAndCreate::ImpPathForDragAndCreate(SdrPathObj
& rSdrPathObject
)
539 : mrSdrPathObject(rSdrPathObject
),
540 aPathPolygon(rSdrPathObject
.GetPathPoly()),
541 meObjectKind(mrSdrPathObject
.meKind
),
542 mpSdrPathDragData(nullptr),
547 bool ImpPathForDragAndCreate::beginPathDrag( SdrDragStat
const & rDrag
) const
549 const SdrHdl
* pHdl
=rDrag
.GetHdl();
553 bool bMultiPointDrag(true);
555 if(aPathPolygon
[(sal_uInt16
)pHdl
->GetPolyNum()].IsControl((sal_uInt16
)pHdl
->GetPointNum()))
556 bMultiPointDrag
= false;
560 const SdrMarkView
& rMarkView
= *rDrag
.GetView();
561 const SdrHdlList
& rHdlList
= rMarkView
.GetHdlList();
562 const size_t nHdlCount
= rHdlList
.GetHdlCount();
563 const SdrObject
* pInteractionObject(nHdlCount
&& rHdlList
.GetHdl(0) ? rHdlList
.GetHdl(0)->GetObj() : nullptr);
564 sal_uInt32
nSelectedPoints(0);
566 for(size_t a
= 0; a
< nHdlCount
; ++a
)
568 SdrHdl
* pTestHdl
= rHdlList
.GetHdl(a
);
570 if(pTestHdl
&& pTestHdl
->IsSelected() && pTestHdl
->GetObj() == pInteractionObject
)
576 if(nSelectedPoints
<= 1)
577 bMultiPointDrag
= false;
580 const_cast<ImpPathForDragAndCreate
*>(this)->mpSdrPathDragData
.reset( new ImpSdrPathDragData(mrSdrPathObject
,*pHdl
,bMultiPointDrag
,rDrag
) );
582 if(!mpSdrPathDragData
|| !mpSdrPathDragData
->bValid
)
584 OSL_FAIL("ImpPathForDragAndCreate::BegDrag(): ImpSdrPathDragData is invalid.");
585 const_cast<ImpPathForDragAndCreate
*>(this)->mpSdrPathDragData
.reset();
592 bool ImpPathForDragAndCreate::movePathDrag( SdrDragStat
& rDrag
) const
594 if(!mpSdrPathDragData
|| !mpSdrPathDragData
->bValid
)
596 OSL_FAIL("ImpPathForDragAndCreate::MovDrag(): ImpSdrPathDragData is invalid.");
600 if(mpSdrPathDragData
->IsMultiPointDrag())
602 Point
aDelta(rDrag
.GetNow() - rDrag
.GetStart());
604 if(aDelta
.X() || aDelta
.Y())
606 for(SdrHdl
* pHandle
: mpSdrPathDragData
->maHandles
)
608 const sal_uInt16
nPolyIndex((sal_uInt16
)pHandle
->GetPolyNum());
609 const sal_uInt16
nPointIndex((sal_uInt16
)pHandle
->GetPointNum());
610 const XPolygon
& rOrig
= mpSdrPathDragData
->maOrig
[nPolyIndex
];
611 XPolygon
& rMove
= mpSdrPathDragData
->maMove
[nPolyIndex
];
612 const sal_uInt16
nPointCount(rOrig
.GetPointCount());
613 bool bClosed(rOrig
[0] == rOrig
[nPointCount
-1]);
616 rMove
[nPointIndex
] = rOrig
[nPointIndex
] + aDelta
;
618 // when point is first and poly closed, move close point, too.
619 if(nPointCount
> 0 && !nPointIndex
&& bClosed
)
621 rMove
[nPointCount
- 1] = rOrig
[nPointCount
- 1] + aDelta
;
623 // when moving the last point it may be necessary to move the
624 // control point in front of this one, too.
625 if(nPointCount
> 1 && rOrig
.IsControl(nPointCount
- 2))
626 rMove
[nPointCount
- 2] = rOrig
[nPointCount
- 2] + aDelta
;
629 // is a control point before this?
630 if(nPointIndex
> 0 && rOrig
.IsControl(nPointIndex
- 1))
633 rMove
[nPointIndex
- 1] = rOrig
[nPointIndex
- 1] + aDelta
;
636 // is a control point after this?
637 if(nPointIndex
+ 1 < nPointCount
&& rOrig
.IsControl(nPointIndex
+ 1))
640 rMove
[nPointIndex
+ 1] = rOrig
[nPointIndex
+ 1] + aDelta
;
647 mpSdrPathDragData
->ResetPoly(mrSdrPathObject
);
649 // copy certain data locally to use less code and have faster access times
650 bool bClosed
=mpSdrPathDragData
->bClosed
; // closed object?
651 sal_uInt16 nPnt
=mpSdrPathDragData
->nPnt
; // number of point in the above polygon
652 bool bBegPnt
=mpSdrPathDragData
->bBegPnt
; // dragged point is first point of a Polyline
653 bool bEndPnt
=mpSdrPathDragData
->bEndPnt
; // dragged point is last point of a Polyline
654 sal_uInt16 nPrevPnt
=mpSdrPathDragData
->nPrevPnt
; // index of previous point
655 sal_uInt16 nNextPnt
=mpSdrPathDragData
->nNextPnt
; // index of next point
656 bool bPrevIsBegPnt
=mpSdrPathDragData
->bPrevIsBegPnt
; // previous point is first point of a Polyline
657 bool bNextIsEndPnt
=mpSdrPathDragData
->bNextIsEndPnt
; // next point is last point of a Polyline
658 sal_uInt16 nPrevPrevPnt
=mpSdrPathDragData
->nPrevPrevPnt
; // index of the point before the previous point
659 sal_uInt16 nNextNextPnt
=mpSdrPathDragData
->nNextNextPnt
; // index if the point after the next point
660 bool bControl
=mpSdrPathDragData
->bControl
; // point is a control point
661 bool bIsNextControl
=mpSdrPathDragData
->bIsNextControl
; // point is a control point after a support point
662 bool bPrevIsControl
=mpSdrPathDragData
->bPrevIsControl
; // if nPnt is a support point: there's a control point before
663 bool bNextIsControl
=mpSdrPathDragData
->bNextIsControl
; // if nPnt is a support point: there's a control point after
665 // Ortho for lines/polygons: keep angle
666 if (!bControl
&& rDrag
.GetView()!=nullptr && rDrag
.GetView()->IsOrtho()) {
667 bool bBigOrtho
=rDrag
.GetView()->IsBigOrtho();
668 Point
aPos(rDrag
.GetNow()); // current position
669 Point
aPnt(mpSdrPathDragData
->aXP
[nPnt
]); // the dragged point
670 sal_uInt16 nPnt1
=0xFFFF,nPnt2
=0xFFFF; // its neighboring points
671 Point aNeuPos1
,aNeuPos2
; // new alternative for aPos
672 bool bPnt1
= false, bPnt2
= false; // are these valid alternatives?
673 if (!bClosed
&& mpSdrPathDragData
->nPointCount
>=2) { // minimum of 2 points for lines
674 if (!bBegPnt
) nPnt1
=nPrevPnt
;
675 if (!bEndPnt
) nPnt2
=nNextPnt
;
677 if (bClosed
&& mpSdrPathDragData
->nPointCount
>=3) { // minimum of 3 points for polygon
681 if (nPnt1
!=0xFFFF && !bPrevIsControl
) {
682 Point aPnt1
=mpSdrPathDragData
->aXP
[nPnt1
];
683 long ndx0
=aPnt
.X()-aPnt1
.X();
684 long ndy0
=aPnt
.Y()-aPnt1
.Y();
687 if (!bHLin
|| !bVLin
) {
688 long ndx
=aPos
.X()-aPnt1
.X();
689 long ndy
=aPos
.Y()-aPnt1
.Y();
691 double nXFact
=0; if (!bVLin
) nXFact
=(double)ndx
/(double)ndx0
;
692 double nYFact
=0; if (!bHLin
) nYFact
=(double)ndy
/(double)ndy0
;
693 bool bHor
=bHLin
|| (!bVLin
&& (nXFact
>nYFact
) ==bBigOrtho
);
694 bool bVer
=bVLin
|| (!bHLin
&& (nXFact
<=nYFact
)==bBigOrtho
);
695 if (bHor
) ndy
=long(ndy0
*nXFact
);
696 if (bVer
) ndx
=long(ndx0
*nYFact
);
702 if (nPnt2
!=0xFFFF && !bNextIsControl
) {
703 Point aPnt2
=mpSdrPathDragData
->aXP
[nPnt2
];
704 long ndx0
=aPnt
.X()-aPnt2
.X();
705 long ndy0
=aPnt
.Y()-aPnt2
.Y();
708 if (!bHLin
|| !bVLin
) {
709 long ndx
=aPos
.X()-aPnt2
.X();
710 long ndy
=aPos
.Y()-aPnt2
.Y();
712 double nXFact
=0; if (!bVLin
) nXFact
=(double)ndx
/(double)ndx0
;
713 double nYFact
=0; if (!bHLin
) nYFact
=(double)ndy
/(double)ndy0
;
714 bool bHor
=bHLin
|| (!bVLin
&& (nXFact
>nYFact
) ==bBigOrtho
);
715 bool bVer
=bVLin
|| (!bHLin
&& (nXFact
<=nYFact
)==bBigOrtho
);
716 if (bHor
) ndy
=long(ndy0
*nXFact
);
717 if (bVer
) ndx
=long(ndx0
*nYFact
);
723 if (bPnt1
&& bPnt2
) { // both alternatives exist (and compete)
724 BigInt
nX1(aNeuPos1
.X()-aPos
.X()); nX1
*=nX1
;
725 BigInt
nY1(aNeuPos1
.Y()-aPos
.Y()); nY1
*=nY1
;
726 BigInt
nX2(aNeuPos2
.X()-aPos
.X()); nX2
*=nX2
;
727 BigInt
nY2(aNeuPos2
.Y()-aPos
.Y()); nY2
*=nY2
;
728 nX1
+=nY1
; // correction distance to square
729 nX2
+=nY2
; // correction distance to square
730 // let the alternative that allows fewer correction win
731 if (nX1
<nX2
) bPnt2
=false; else bPnt1
=false;
733 if (bPnt1
) rDrag
.SetNow(aNeuPos1
);
734 if (bPnt2
) rDrag
.SetNow(aNeuPos2
);
736 rDrag
.SetActionRect(tools::Rectangle(rDrag
.GetNow(),rDrag
.GetNow()));
738 // specially for IBM: Eliminate points if both adjoining lines form near 180 degrees angle anyway
739 if (!bControl
&& rDrag
.GetView()!=nullptr && rDrag
.GetView()->IsEliminatePolyPoints() &&
740 !bBegPnt
&& !bEndPnt
&& !bPrevIsControl
&& !bNextIsControl
)
742 Point
aPt(mpSdrPathDragData
->aXP
[nNextPnt
]);
744 long nAngle1
=GetAngle(aPt
);
746 aPt
-=mpSdrPathDragData
->aXP
[nPrevPnt
];
747 long nAngle2
=GetAngle(aPt
);
748 long nDiff
=nAngle1
-nAngle2
;
749 nDiff
=std::abs(nDiff
);
750 mpSdrPathDragData
->bEliminate
=nDiff
<=rDrag
.GetView()->GetEliminatePolyPointLimitAngle();
751 if (mpSdrPathDragData
->bEliminate
) { // adapt position, Smooth is true for the ends
752 aPt
=mpSdrPathDragData
->aXP
[nNextPnt
];
753 aPt
+=mpSdrPathDragData
->aXP
[nPrevPnt
];
759 // we dragged by this distance
760 Point
aDiff(rDrag
.GetNow()); aDiff
-=mpSdrPathDragData
->aXP
[nPnt
];
762 /* There are 8 possible cases:
763 X 1. A control point neither on the left nor on the right.
764 o--X--o 2. There are control points on the left and the right, we are dragging a support point.
765 o--X 3. There is a control point on the left, we are dragging a support point.
766 X--o 4. There is a control point on the right, we are dragging a support point.
767 x--O--o 5. There are control points on the left and the right, we are dragging the left one.
768 x--O 6. There is a control point on the left, we are dragging it.
769 o--O--x 7. There are control points on the left and the right, we are dragging the right one.
770 O--x 8. There is a control point on the right, we are dragging it.
771 Note: modifying a line (not a curve!) might create a curve on the other end of the line
772 if Smooth is set there (with control points aligned to line).
775 mpSdrPathDragData
->aXP
[nPnt
]+=aDiff
;
777 // now check symmetric plus handles
778 if (bControl
) { // cases 5,6,7,8
779 sal_uInt16 nSt
; // the associated support point
780 sal_uInt16 nFix
; // the opposing control point
781 if (bIsNextControl
) { // if the next one is a control point, the on before has to be a support point
788 if (mpSdrPathDragData
->aXP
.IsSmooth(nSt
)) {
789 mpSdrPathDragData
->aXP
.CalcSmoothJoin(nSt
,nPnt
,nFix
);
793 if (!bControl
) { // Cases 1,2,3,4. In case 1, nothing happens; in cases 3 and 4, there is more following below.
794 // move both control points
795 if (bPrevIsControl
) mpSdrPathDragData
->aXP
[nPrevPnt
]+=aDiff
;
796 if (bNextIsControl
) mpSdrPathDragData
->aXP
[nNextPnt
]+=aDiff
;
797 // align control point to line, if appropriate
798 if (mpSdrPathDragData
->aXP
.IsSmooth(nPnt
)) {
799 if (bPrevIsControl
&& !bNextIsControl
&& !bEndPnt
) { // case 3
800 mpSdrPathDragData
->aXP
.CalcSmoothJoin(nPnt
,nNextPnt
,nPrevPnt
);
802 if (bNextIsControl
&& !bPrevIsControl
&& !bBegPnt
) { // case 4
803 mpSdrPathDragData
->aXP
.CalcSmoothJoin(nPnt
,nPrevPnt
,nNextPnt
);
806 // Now check the other ends of the line (nPnt+-1). If there is a
807 // curve (IsControl(nPnt+-2)) with SmoothJoin (nPnt+-1), the
808 // associated control point (nPnt+-2) has to be adapted.
809 if (!bBegPnt
&& !bPrevIsControl
&& !bPrevIsBegPnt
&& mpSdrPathDragData
->aXP
.IsSmooth(nPrevPnt
)) {
810 if (mpSdrPathDragData
->aXP
.IsControl(nPrevPrevPnt
)) {
811 mpSdrPathDragData
->aXP
.CalcSmoothJoin(nPrevPnt
,nPnt
,nPrevPrevPnt
);
814 if (!bEndPnt
&& !bNextIsControl
&& !bNextIsEndPnt
&& mpSdrPathDragData
->aXP
.IsSmooth(nNextPnt
)) {
815 if (mpSdrPathDragData
->aXP
.IsControl(nNextNextPnt
)) {
816 mpSdrPathDragData
->aXP
.CalcSmoothJoin(nNextPnt
,nPnt
,nNextNextPnt
);
825 bool ImpPathForDragAndCreate::endPathDrag(SdrDragStat
const & rDrag
)
829 bool bLineGlueMirror(OBJ_LINE
== meObjectKind
);
830 if (bLineGlueMirror
) {
831 XPolygon
& rXP
=aPathPolygon
[0];
836 if(!mpSdrPathDragData
|| !mpSdrPathDragData
->bValid
)
838 OSL_FAIL("ImpPathForDragAndCreate::MovDrag(): ImpSdrPathDragData is invalid.");
842 if(mpSdrPathDragData
->IsMultiPointDrag())
844 aPathPolygon
= mpSdrPathDragData
->maMove
;
848 const SdrHdl
* pHdl
=rDrag
.GetHdl();
850 // reference the polygon
851 XPolygon
& rXP
=aPathPolygon
[(sal_uInt16
)pHdl
->GetPolyNum()];
853 // the 5 points that might have changed
854 if (!mpSdrPathDragData
->bPrevIsBegPnt
) rXP
[mpSdrPathDragData
->nPrevPrevPnt0
]=mpSdrPathDragData
->aXP
[mpSdrPathDragData
->nPrevPrevPnt
];
855 if (!mpSdrPathDragData
->bNextIsEndPnt
) rXP
[mpSdrPathDragData
->nNextNextPnt0
]=mpSdrPathDragData
->aXP
[mpSdrPathDragData
->nNextNextPnt
];
856 if (!mpSdrPathDragData
->bBegPnt
) rXP
[mpSdrPathDragData
->nPrevPnt0
] =mpSdrPathDragData
->aXP
[mpSdrPathDragData
->nPrevPnt
];
857 if (!mpSdrPathDragData
->bEndPnt
) rXP
[mpSdrPathDragData
->nNextPnt0
] =mpSdrPathDragData
->aXP
[mpSdrPathDragData
->nNextPnt
];
858 rXP
[mpSdrPathDragData
->nPnt0
] =mpSdrPathDragData
->aXP
[mpSdrPathDragData
->nPnt
];
860 // for closed objects: last point has to be equal to first point
861 if (mpSdrPathDragData
->bClosed
) rXP
[rXP
.GetPointCount()-1]=rXP
[0];
863 if (mpSdrPathDragData
->bEliminate
)
865 basegfx::B2DPolyPolygon
aTempPolyPolygon(aPathPolygon
.getB2DPolyPolygon());
866 sal_uInt32 nPoly
,nPnt
;
868 if(PolyPolygonEditor::GetRelativePolyPoint(aTempPolyPolygon
, rDrag
.GetHdl()->GetSourceHdlNum(), nPoly
, nPnt
))
870 basegfx::B2DPolygon
aCandidate(aTempPolyPolygon
.getB2DPolygon(nPoly
));
871 aCandidate
.remove(nPnt
);
873 if(aCandidate
.count() < 2)
875 aTempPolyPolygon
.remove(nPoly
);
879 aTempPolyPolygon
.setB2DPolygon(nPoly
, aCandidate
);
883 aPathPolygon
= XPolyPolygon(aTempPolyPolygon
);
886 // adapt angle for text beneath a simple line
889 Point
aLinePt1_(aPathPolygon
[0][0]);
890 Point
aLinePt2_(aPathPolygon
[0][1]);
891 bool bXMirr
=(aLinePt1_
.X()>aLinePt2_
.X())!=(aLinePt1
.X()>aLinePt2
.X());
892 bool bYMirr
=(aLinePt1_
.Y()>aLinePt2_
.Y())!=(aLinePt1
.Y()>aLinePt2
.Y());
893 if (bXMirr
|| bYMirr
) {
894 Point
aRef1(mrSdrPathObject
.GetSnapRect().Center());
898 mrSdrPathObject
.NbcMirrorGluePoints(aRef1
,aRef2
);
903 mrSdrPathObject
.NbcMirrorGluePoints(aRef1
,aRef2
);
909 mpSdrPathDragData
.reset();
914 OUString
ImpPathForDragAndCreate::getSpecialDragComment(const SdrDragStat
& rDrag
) const
917 const SdrHdl
* pHdl
= rDrag
.GetHdl();
918 const bool bCreateComment(rDrag
.GetView() && &mrSdrPathObject
== rDrag
.GetView()->GetCreateObj());
920 if(bCreateComment
&& rDrag
.GetUser())
922 // #i103058# re-add old creation comment mode
923 const ImpPathCreateUser
* pU
= static_cast<const ImpPathCreateUser
*>(rDrag
.GetUser());
924 const SdrObjKind
eKindMerk(meObjectKind
);
925 mrSdrPathObject
.meKind
= pU
->eAktKind
;
927 mrSdrPathObject
.ImpTakeDescriptionStr(STR_ViewCreateObj
, aTmp
);
929 mrSdrPathObject
.meKind
= eKindMerk
;
931 Point
aPrev(rDrag
.GetPrev());
932 Point
aNow(rDrag
.GetNow());
944 SdrModel::TakeAngleStr(std::abs(pU
->nCircRelAngle
), aMetr
);
947 mrSdrPathObject
.GetModel()->TakeMetricStr(pU
->nCircRadius
, aMetr
, true);
952 mrSdrPathObject
.GetModel()->TakeMetricStr(aNow
.X(), aMetr
, true);
956 mrSdrPathObject
.GetModel()->TakeMetricStr(aNow
.Y(), aMetr
, true);
959 if(!IsFreeHand(meObjectKind
))
961 sal_Int32
nLen(GetLen(aNow
));
963 mrSdrPathObject
.GetModel()->TakeMetricStr(nLen
, aMetr
, true);
966 sal_Int32
nAngle(GetAngle(aNow
));
968 SdrModel::TakeAngleStr(nAngle
, aMetr
);
974 else if(!mrSdrPathObject
.GetModel() || !pHdl
)
976 // #i103058# fallback when no model and/or Handle, both needed
979 mrSdrPathObject
.ImpTakeDescriptionStr(STR_DragPathObj
, aTmp
);
984 // #i103058# standard for modification; model and handle needed
985 ImpSdrPathDragData
* pDragData
= mpSdrPathDragData
.get();
989 // getSpecialDragComment is also used from create, so fallback to GetUser()
990 // when mpSdrPathDragData is not set
991 pDragData
= static_cast<ImpSdrPathDragData
*>(rDrag
.GetUser());
996 OSL_FAIL("ImpPathForDragAndCreate::MovDrag(): ImpSdrPathDragData is invalid.");
1000 if(!pDragData
->IsMultiPointDrag() && pDragData
->bEliminate
)
1004 mrSdrPathObject
.ImpTakeDescriptionStr(STR_ViewMarkedPoint
, aTmp
);
1008 OUString
aStr2(ImpGetResStr(STR_EditDelete
));
1010 // UNICODE: delete point of ...
1011 aStr2
= aStr2
.replaceFirst("%1", aStr
);
1016 // dx=0.00 dy=0.00 -- both sides bezier
1017 // dx=0.00 dy=0.00 l=0.00 0.00\302\260 -- one bezier/lever on one side, a start, or an ending
1018 // dx=0.00 dy=0.00 l=0.00 0.00\302\260 / l=0.00 0.00\302\260 -- in between
1020 Point
aBeg(rDrag
.GetStart());
1021 Point
aNow(rDrag
.GetNow());
1025 mrSdrPathObject
.GetModel()->TakeMetricStr(aNow
.X() - aBeg
.X(), aMetr
, true);
1029 mrSdrPathObject
.GetModel()->TakeMetricStr(aNow
.Y() - aBeg
.Y(), aMetr
, true);
1032 if(!pDragData
->IsMultiPointDrag())
1034 sal_uInt16
nPntNum((sal_uInt16
)pHdl
->GetPointNum());
1035 const XPolygon
& rXPoly
= aPathPolygon
[(sal_uInt16
)rDrag
.GetHdl()->GetPolyNum()];
1036 sal_uInt16
nPointCount(rXPoly
.GetPointCount());
1037 bool bClose(IsClosed(meObjectKind
));
1042 if(pHdl
->IsPlusHdl())
1045 sal_uInt16
nRef(nPntNum
);
1047 if(rXPoly
.IsControl(nPntNum
+ 1))
1052 aNow
-= rXPoly
[nRef
];
1054 sal_Int32
nLen(GetLen(aNow
));
1056 mrSdrPathObject
.GetModel()->TakeMetricStr(nLen
, aMetr
, true);
1059 sal_Int32
nAngle(GetAngle(aNow
));
1061 SdrModel::TakeAngleStr(nAngle
, aMetr
);
1064 else if(nPointCount
> 1)
1066 sal_uInt16
nPntMax(nPointCount
- 1);
1067 bool bIsClosed(IsClosed(meObjectKind
));
1068 bool bPt1(nPntNum
> 0);
1069 bool bPt2(nPntNum
< nPntMax
);
1071 if(bIsClosed
&& nPointCount
> 2)
1077 sal_uInt16 nPt1
,nPt2
;
1084 if(nPntNum
< nPntMax
)
1089 if(bPt1
&& rXPoly
.IsControl(nPt1
))
1090 bPt1
= false; // don't display
1092 if(bPt2
&& rXPoly
.IsControl(nPt2
))
1093 bPt2
= false; // of bezier data
1098 aPt
-= rXPoly
[nPt1
];
1100 sal_Int32
nLen(GetLen(aPt
));
1102 mrSdrPathObject
.GetModel()->TakeMetricStr(nLen
, aMetr
, true);
1105 sal_Int32
nAngle(GetAngle(aPt
));
1107 SdrModel::TakeAngleStr(nAngle
, aMetr
);
1119 aPt
-= rXPoly
[nPt2
];
1121 sal_Int32
nLen(GetLen(aPt
));
1123 mrSdrPathObject
.GetModel()->TakeMetricStr(nLen
, aMetr
, true);
1126 sal_Int32
nAngle(GetAngle(aPt
));
1128 SdrModel::TakeAngleStr(nAngle
, aMetr
);
1138 basegfx::B2DPolyPolygon
ImpPathForDragAndCreate::getSpecialDragPoly(const SdrDragStat
& rDrag
) const
1140 if(!mpSdrPathDragData
|| !mpSdrPathDragData
->bValid
)
1142 OSL_FAIL("ImpPathForDragAndCreate::MovDrag(): ImpSdrPathDragData is invalid.");
1143 return basegfx::B2DPolyPolygon();
1146 XPolyPolygon aRetval
;
1148 if(mpSdrPathDragData
->IsMultiPointDrag())
1150 aRetval
.Insert(mpSdrPathDragData
->maMove
);
1154 const XPolygon
& rXP
=aPathPolygon
[(sal_uInt16
)rDrag
.GetHdl()->GetPolyNum()];
1155 if (rXP
.GetPointCount()<=2) {
1156 XPolygon
aXPoly(rXP
);
1157 aXPoly
[(sal_uInt16
)rDrag
.GetHdl()->GetPointNum()]=rDrag
.GetNow();
1158 aRetval
.Insert(std::move(aXPoly
));
1159 return aRetval
.getB2DPolyPolygon();
1161 // copy certain data locally to use less code and have faster access times
1162 bool bClosed
=mpSdrPathDragData
->bClosed
; // closed object?
1163 sal_uInt16 nPointCount
= mpSdrPathDragData
->nPointCount
; // number of points
1164 sal_uInt16 nPnt
=mpSdrPathDragData
->nPnt
; // number of points in the polygon
1165 bool bBegPnt
=mpSdrPathDragData
->bBegPnt
; // dragged point is the first point of a Polyline
1166 bool bEndPnt
=mpSdrPathDragData
->bEndPnt
; // dragged point is the last point of a Polyline
1167 sal_uInt16 nPrevPnt
=mpSdrPathDragData
->nPrevPnt
; // index of the previous point
1168 sal_uInt16 nNextPnt
=mpSdrPathDragData
->nNextPnt
; // index of the next point
1169 bool bPrevIsBegPnt
=mpSdrPathDragData
->bPrevIsBegPnt
; // previous point is first point of a Polyline
1170 bool bNextIsEndPnt
=mpSdrPathDragData
->bNextIsEndPnt
; // next point is last point of a Polyline
1171 sal_uInt16 nPrevPrevPnt
=mpSdrPathDragData
->nPrevPrevPnt
; // index of the point before the previous point
1172 sal_uInt16 nNextNextPnt
=mpSdrPathDragData
->nNextNextPnt
; // index of the point after the last point
1173 bool bControl
=mpSdrPathDragData
->bControl
; // point is a control point
1174 bool bIsNextControl
=mpSdrPathDragData
->bIsNextControl
; //point is a control point after a support point
1175 bool bPrevIsControl
=mpSdrPathDragData
->bPrevIsControl
; // if nPnt is a support point: there's a control point before
1176 bool bNextIsControl
=mpSdrPathDragData
->bNextIsControl
; // if nPnt is a support point: there's a control point after
1177 XPolygon
aXPoly(mpSdrPathDragData
->aXP
);
1183 aLine1
[1]=mpSdrPathDragData
->aXP
[nPnt
];
1184 if (bIsNextControl
) { // is this a control point after the support point?
1185 aLine1
[0]=mpSdrPathDragData
->aXP
[nPrevPnt
];
1186 aLine2
[0]=mpSdrPathDragData
->aXP
[nNextNextPnt
];
1187 aLine2
[1]=mpSdrPathDragData
->aXP
[nNextPnt
];
1188 if (mpSdrPathDragData
->aXP
.IsSmooth(nPrevPnt
) && !bPrevIsBegPnt
&& mpSdrPathDragData
->aXP
.IsControl(nPrevPrevPnt
)) {
1189 aXPoly
.Insert(0,rXP
[mpSdrPathDragData
->nPrevPrevPnt0
-1],PolyFlags::Control
);
1190 aXPoly
.Insert(0,rXP
[mpSdrPathDragData
->nPrevPrevPnt0
-2],PolyFlags::Normal
);
1191 // leverage lines for the opposing curve segment
1192 aLine3
[0]=mpSdrPathDragData
->aXP
[nPrevPnt
];
1193 aLine3
[1]=mpSdrPathDragData
->aXP
[nPrevPrevPnt
];
1194 aLine4
[0]=rXP
[mpSdrPathDragData
->nPrevPrevPnt0
-2];
1195 aLine4
[1]=rXP
[mpSdrPathDragData
->nPrevPrevPnt0
-1];
1199 } else { // else this is a control point before a support point
1200 aLine1
[0]=mpSdrPathDragData
->aXP
[nNextPnt
];
1201 aLine2
[0]=mpSdrPathDragData
->aXP
[nPrevPrevPnt
];
1202 aLine2
[1]=mpSdrPathDragData
->aXP
[nPrevPnt
];
1203 if (mpSdrPathDragData
->aXP
.IsSmooth(nNextPnt
) && !bNextIsEndPnt
&& mpSdrPathDragData
->aXP
.IsControl(nNextNextPnt
)) {
1204 aXPoly
.Insert(XPOLY_APPEND
,rXP
[mpSdrPathDragData
->nNextNextPnt0
+1],PolyFlags::Control
);
1205 aXPoly
.Insert(XPOLY_APPEND
,rXP
[mpSdrPathDragData
->nNextNextPnt0
+2],PolyFlags::Normal
);
1206 // leverage lines for the opposing curve segment
1207 aLine3
[0]=mpSdrPathDragData
->aXP
[nNextPnt
];
1208 aLine3
[1]=mpSdrPathDragData
->aXP
[nNextNextPnt
];
1209 aLine4
[0]=rXP
[mpSdrPathDragData
->nNextNextPnt0
+2];
1210 aLine4
[1]=rXP
[mpSdrPathDragData
->nNextNextPnt0
+1];
1212 aXPoly
.Remove(aXPoly
.GetPointCount()-1,1);
1215 } else { // else is not a control point
1216 if (mpSdrPathDragData
->bEliminate
) {
1219 if (bPrevIsControl
) aXPoly
.Insert(0,rXP
[mpSdrPathDragData
->nPrevPrevPnt0
-1],PolyFlags::Normal
);
1220 else if (!bBegPnt
&& !bPrevIsBegPnt
&& mpSdrPathDragData
->aXP
.IsControl(nPrevPrevPnt
)) {
1221 aXPoly
.Insert(0,rXP
[mpSdrPathDragData
->nPrevPrevPnt0
-1],PolyFlags::Control
);
1222 aXPoly
.Insert(0,rXP
[mpSdrPathDragData
->nPrevPrevPnt0
-2],PolyFlags::Normal
);
1225 if (bBegPnt
) aXPoly
.Remove(0,1);
1227 if (bNextIsControl
) aXPoly
.Insert(XPOLY_APPEND
,rXP
[mpSdrPathDragData
->nNextNextPnt0
+1],PolyFlags::Normal
);
1228 else if (!bEndPnt
&& !bNextIsEndPnt
&& mpSdrPathDragData
->aXP
.IsControl(nNextNextPnt
)) {
1229 aXPoly
.Insert(XPOLY_APPEND
,rXP
[mpSdrPathDragData
->nNextNextPnt0
+1],PolyFlags::Control
);
1230 aXPoly
.Insert(XPOLY_APPEND
,rXP
[mpSdrPathDragData
->nNextNextPnt0
+2],PolyFlags::Normal
);
1232 aXPoly
.Remove(aXPoly
.GetPointCount()-1,1);
1233 if (bEndPnt
) aXPoly
.Remove(aXPoly
.GetPointCount()-1,1);
1235 if (bClosed
) { // "pear problem": 2 lines, 1 curve, everything smoothed, a point between both lines is dragged
1236 if (aXPoly
.GetPointCount()>nPointCount
&& aXPoly
.IsControl(1)) {
1237 sal_uInt16 a
=aXPoly
.GetPointCount();
1238 aXPoly
[a
-2]=aXPoly
[2]; aXPoly
.SetFlags(a
-2,aXPoly
.GetFlags(2));
1239 aXPoly
[a
-1]=aXPoly
[3]; aXPoly
.SetFlags(a
-1,aXPoly
.GetFlags(3));
1244 aRetval
.Insert(std::move(aXPoly
));
1245 if (aLine1
.GetPointCount()>1) aRetval
.Insert(std::move(aLine1
));
1246 if (aLine2
.GetPointCount()>1) aRetval
.Insert(std::move(aLine2
));
1247 if (aLine3
.GetPointCount()>1) aRetval
.Insert(std::move(aLine3
));
1248 if (aLine4
.GetPointCount()>1) aRetval
.Insert(std::move(aLine4
));
1251 return aRetval
.getB2DPolyPolygon();
1254 bool ImpPathForDragAndCreate::BegCreate(SdrDragStat
& rStat
)
1256 bool bFreeHand(IsFreeHand(meObjectKind
));
1257 rStat
.SetNoSnap(bFreeHand
);
1258 rStat
.SetOrtho8Possible();
1259 aPathPolygon
.Clear();
1261 bool bMakeStartPoint
= true;
1262 SdrView
* pView
=rStat
.GetView();
1263 if (pView
!=nullptr && pView
->IsUseIncompatiblePathCreateInterface() &&
1264 (meObjectKind
==OBJ_POLY
|| meObjectKind
==OBJ_PLIN
|| meObjectKind
==OBJ_PATHLINE
|| meObjectKind
==OBJ_PATHFILL
)) {
1265 bMakeStartPoint
= false;
1267 aPathPolygon
.Insert(XPolygon());
1268 aPathPolygon
[0][0]=rStat
.GetStart();
1269 if (bMakeStartPoint
) {
1270 aPathPolygon
[0][1]=rStat
.GetNow();
1272 std::unique_ptr
<ImpPathCreateUser
> pU(new ImpPathCreateUser
);
1273 pU
->eStartKind
=meObjectKind
;
1274 pU
->eAktKind
=meObjectKind
;
1275 rStat
.SetUser(std::move(pU
));
1279 bool ImpPathForDragAndCreate::MovCreate(SdrDragStat
& rStat
)
1281 ImpPathCreateUser
* pU
=static_cast<ImpPathCreateUser
*>(rStat
.GetUser());
1282 SdrView
* pView
=rStat
.GetView();
1283 XPolygon
& rXPoly
=aPathPolygon
[aPathPolygon
.Count()-1];
1284 if (pView
!=nullptr && pView
->IsCreateMode()) {
1285 // switch to different CreateTool, if appropriate
1287 SdrInventor nInvent
;
1288 pView
->TakeCurrentObj(nIdent
,nInvent
);
1289 if (nInvent
==SdrInventor::Default
&& pU
->eAktKind
!=(SdrObjKind
)nIdent
) {
1290 SdrObjKind eNewKind
=(SdrObjKind
)nIdent
;
1307 case OBJ_SPLNFILL
: {
1308 pU
->eAktKind
=eNewKind
;
1309 pU
->bMixedCreate
=true;
1310 pU
->nBezierStartPoint
=rXPoly
.GetPointCount();
1311 if (pU
->nBezierStartPoint
>0) pU
->nBezierStartPoint
--;
1317 sal_uInt16 nActPoint
=rXPoly
.GetPointCount();
1318 if (aPathPolygon
.Count()>1 && rStat
.IsMouseDown() && nActPoint
<2) {
1319 rXPoly
[0]=rStat
.GetPos0();
1320 rXPoly
[1]=rStat
.GetNow();
1324 rXPoly
[0]=rStat
.GetPos0();
1326 bool bFreeHand
=IsFreeHand(pU
->eAktKind
);
1327 rStat
.SetNoSnap(bFreeHand
);
1328 rStat
.SetOrtho8Possible(pU
->eAktKind
!=OBJ_CARC
&& pU
->eAktKind
!=OBJ_RECT
&& (!pU
->bMixedCreate
|| pU
->eAktKind
!=OBJ_LINE
));
1329 rXPoly
[nActPoint
]=rStat
.GetNow();
1330 if (!pU
->bMixedCreate
&& pU
->eStartKind
==OBJ_LINE
&& rXPoly
.GetPointCount()>=1) {
1331 Point
aPt(rStat
.GetStart());
1332 if (pView
!=nullptr && pView
->IsCreate1stPointAsCenter()) {
1334 aPt
-=rStat
.GetNow();
1338 OutputDevice
* pOut
=pView
==nullptr ? nullptr : pView
->GetFirstOutputDevice();
1340 if (pU
->nBezierStartPoint
>nActPoint
) pU
->nBezierStartPoint
=nActPoint
;
1341 if (rStat
.IsMouseDown() && nActPoint
>0) {
1342 // don't allow two consecutive points to occupy too similar positions
1344 if (pView
!=nullptr) nMinDist
=pView
->GetFreeHandMinDistPix();
1345 if (pOut
!=nullptr) nMinDist
=pOut
->PixelToLogic(Size(nMinDist
,0)).Width();
1346 if (nMinDist
<1) nMinDist
=1;
1348 Point
aPt0(rXPoly
[nActPoint
-1]);
1349 Point
aPt1(rStat
.GetNow());
1350 long dx
=aPt0
.X()-aPt1
.X(); if (dx
<0) dx
=-dx
;
1351 long dy
=aPt0
.Y()-aPt1
.Y(); if (dy
<0) dy
=-dy
;
1352 if (dx
<nMinDist
&& dy
<nMinDist
) return false;
1354 // TODO: the following is copied from EndCreate (with a few smaller modifications)
1355 // and should be combined into a method with the code there.
1357 if (nActPoint
-pU
->nBezierStartPoint
>=3 && ((nActPoint
-pU
->nBezierStartPoint
)%3)==0) {
1358 rXPoly
.PointsToBezier(nActPoint
-3);
1359 rXPoly
.SetFlags(nActPoint
-1,PolyFlags::Control
);
1360 rXPoly
.SetFlags(nActPoint
-2,PolyFlags::Control
);
1362 if (nActPoint
>=6 && rXPoly
.IsControl(nActPoint
-4)) {
1363 rXPoly
.CalcTangent(nActPoint
-3,nActPoint
-4,nActPoint
-2);
1364 rXPoly
.SetFlags(nActPoint
-3,PolyFlags::Smooth
);
1367 rXPoly
[nActPoint
+1]=rStat
.GetNow();
1370 pU
->nBezierStartPoint
=nActPoint
;
1374 pU
->ResetFormFlags();
1375 if (IsBezier(pU
->eAktKind
)) {
1377 pU
->CalcBezier(rXPoly
[nActPoint
-1],rXPoly
[nActPoint
],rXPoly
[nActPoint
-1]-rXPoly
[nActPoint
-2],rStat
.IsMouseDown());
1378 } else if (pU
->bBezHasCtrl0
) {
1379 pU
->CalcBezier(rXPoly
[nActPoint
-1],rXPoly
[nActPoint
],pU
->aBezControl0
-rXPoly
[nActPoint
-1],rStat
.IsMouseDown());
1382 if (pU
->eAktKind
==OBJ_CARC
&& nActPoint
>=2) {
1383 pU
->CalcCircle(rXPoly
[nActPoint
-1],rXPoly
[nActPoint
],rXPoly
[nActPoint
-1]-rXPoly
[nActPoint
-2],pView
);
1385 if (pU
->eAktKind
==OBJ_LINE
&& nActPoint
>=2) {
1386 pU
->CalcLine(rXPoly
[nActPoint
-1],rXPoly
[nActPoint
],rXPoly
[nActPoint
-1]-rXPoly
[nActPoint
-2],pView
);
1388 if (pU
->eAktKind
==OBJ_RECT
&& nActPoint
>=2) {
1389 pU
->CalcRect(rXPoly
[nActPoint
-1],rXPoly
[nActPoint
],rXPoly
[nActPoint
-1]-rXPoly
[nActPoint
-2],pView
);
1395 bool ImpPathForDragAndCreate::EndCreate(SdrDragStat
& rStat
, SdrCreateCmd eCmd
)
1397 ImpPathCreateUser
* pU
=static_cast<ImpPathCreateUser
*>(rStat
.GetUser());
1399 SdrView
* pView
=rStat
.GetView();
1400 bool bIncomp
=pView
!=nullptr && pView
->IsUseIncompatiblePathCreateInterface();
1401 XPolygon
& rXPoly
=aPathPolygon
[aPathPolygon
.Count()-1];
1402 sal_uInt16 nActPoint
=rXPoly
.GetPointCount()-1;
1403 rXPoly
[nActPoint
]=rStat
.GetNow();
1404 if (!pU
->bMixedCreate
&& pU
->eStartKind
==OBJ_LINE
) {
1405 if (rStat
.GetPointCount()>=2) eCmd
=SdrCreateCmd::ForceEnd
;
1406 bRet
= eCmd
==SdrCreateCmd::ForceEnd
;
1409 rStat
.SetUser(nullptr);
1414 if (!pU
->bMixedCreate
&& IsFreeHand(pU
->eStartKind
)) {
1415 if (rStat
.GetPointCount()>=2) eCmd
=SdrCreateCmd::ForceEnd
;
1416 bRet
=eCmd
==SdrCreateCmd::ForceEnd
;
1419 rStat
.SetUser(nullptr);
1423 if (eCmd
==SdrCreateCmd::NextPoint
|| eCmd
==SdrCreateCmd::NextObject
) {
1424 // don't allow two consecutive points to occupy the same position
1425 if (nActPoint
==0 || rStat
.GetNow()!=rXPoly
[nActPoint
-1]) {
1427 if (pU
->nBezierStartPoint
>nActPoint
) pU
->nBezierStartPoint
=nActPoint
;
1428 if (IsBezier(pU
->eAktKind
) && nActPoint
-pU
->nBezierStartPoint
>=3 && ((nActPoint
-pU
->nBezierStartPoint
)%3)==0) {
1429 rXPoly
.PointsToBezier(nActPoint
-3);
1430 rXPoly
.SetFlags(nActPoint
-1,PolyFlags::Control
);
1431 rXPoly
.SetFlags(nActPoint
-2,PolyFlags::Control
);
1433 if (nActPoint
>=6 && rXPoly
.IsControl(nActPoint
-4)) {
1434 rXPoly
.CalcTangent(nActPoint
-3,nActPoint
-4,nActPoint
-2);
1435 rXPoly
.SetFlags(nActPoint
-3,PolyFlags::Smooth
);
1439 if (nActPoint
==1 && IsBezier(pU
->eAktKind
) && !pU
->bBezHasCtrl0
) {
1440 pU
->aBezControl0
=rStat
.GetNow();
1441 pU
->bBezHasCtrl0
=true;
1444 if (pU
->IsFormFlag()) {
1445 sal_uInt16 nPointCount0
=rXPoly
.GetPointCount();
1446 rXPoly
.Remove(nActPoint
-1,2); // remove last two points and replace by form
1447 rXPoly
.Insert(XPOLY_APPEND
,pU
->GetFormPoly());
1448 sal_uInt16 nPointCount1
=rXPoly
.GetPointCount();
1449 for (sal_uInt16 i
=nPointCount0
+1; i
<nPointCount1
-1; i
++) { // to make BckAction work
1450 if (!rXPoly
.IsControl(i
)) rStat
.NextPoint();
1452 nActPoint
=rXPoly
.GetPointCount()-1;
1456 rXPoly
[nActPoint
]=rStat
.GetNow();
1458 if (eCmd
==SdrCreateCmd::NextObject
) {
1459 if (rXPoly
.GetPointCount()>=2) {
1460 pU
->bBezHasCtrl0
=false;
1461 // only a singular polygon may be opened, so close this
1462 rXPoly
[nActPoint
]=rXPoly
[0];
1464 aXP
[0]=rStat
.GetNow();
1465 aPathPolygon
.Insert(std::move(aXP
));
1470 sal_uInt16 nPolyCount
=aPathPolygon
.Count();
1471 if (nPolyCount
!=0) {
1472 // delete last point, if necessary
1473 if (eCmd
==SdrCreateCmd::ForceEnd
) {
1474 XPolygon
& rXP
=aPathPolygon
[nPolyCount
-1];
1475 sal_uInt16 nPointCount
=rXP
.GetPointCount();
1476 if (nPointCount
>=2) {
1477 if (!rXP
.IsControl(nPointCount
-2)) {
1478 if (rXP
[nPointCount
-1]==rXP
[nPointCount
-2]) {
1479 rXP
.Remove(nPointCount
-1,1);
1482 if (rXP
[nPointCount
-3]==rXP
[nPointCount
-2]) {
1483 rXP
.Remove(nPointCount
-3,3);
1488 for (sal_uInt16 nPolyNum
=nPolyCount
; nPolyNum
>0;) {
1490 XPolygon
& rXP
=aPathPolygon
[nPolyNum
];
1491 sal_uInt16 nPointCount
=rXP
.GetPointCount();
1492 // delete polygons with too few points
1493 if (nPolyNum
<nPolyCount
-1 || eCmd
==SdrCreateCmd::ForceEnd
) {
1494 if (nPointCount
<2) aPathPolygon
.Remove(nPolyNum
);
1498 pU
->ResetFormFlags();
1499 bRet
=eCmd
==SdrCreateCmd::ForceEnd
;
1502 rStat
.SetUser(nullptr);
1507 bool ImpPathForDragAndCreate::BckCreate(SdrDragStat
& rStat
)
1509 ImpPathCreateUser
* pU
=static_cast<ImpPathCreateUser
*>(rStat
.GetUser());
1510 if (aPathPolygon
.Count()>0) {
1511 XPolygon
& rXPoly
=aPathPolygon
[aPathPolygon
.Count()-1];
1512 sal_uInt16 nActPoint
=rXPoly
.GetPointCount();
1515 // make the last part of a bezier curve a line
1516 rXPoly
.Remove(nActPoint
,1);
1517 if (nActPoint
>=3 && rXPoly
.IsControl(nActPoint
-1)) {
1518 // there should never be a bezier segment at the end, so this is just in case...
1519 rXPoly
.Remove(nActPoint
-1,1);
1520 if (rXPoly
.IsControl(nActPoint
-2)) rXPoly
.Remove(nActPoint
-2,1);
1523 nActPoint
=rXPoly
.GetPointCount();
1524 if (nActPoint
>=4) { // no bezier segment at the end
1526 if (rXPoly
.IsControl(nActPoint
-1)) {
1527 rXPoly
.Remove(nActPoint
-1,1);
1528 if (rXPoly
.IsControl(nActPoint
-2)) rXPoly
.Remove(nActPoint
-2,1);
1531 if (rXPoly
.GetPointCount()<2) {
1532 aPathPolygon
.Remove(aPathPolygon
.Count()-1);
1534 if (aPathPolygon
.Count()>0) {
1535 XPolygon
& rLocalXPoly
=aPathPolygon
[aPathPolygon
.Count()-1];
1536 sal_uInt16 nLocalActPoint
=rLocalXPoly
.GetPointCount();
1537 if (nLocalActPoint
>0) {
1539 rLocalXPoly
[nLocalActPoint
]=rStat
.GetNow();
1543 pU
->ResetFormFlags();
1544 return aPathPolygon
.Count()!=0;
1547 void ImpPathForDragAndCreate::BrkCreate(SdrDragStat
& rStat
)
1549 aPathPolygon
.Clear();
1551 rStat
.SetUser(nullptr);
1554 basegfx::B2DPolyPolygon
ImpPathForDragAndCreate::TakeObjectPolyPolygon(const SdrDragStat
& rDrag
) const
1556 basegfx::B2DPolyPolygon
aRetval(aPathPolygon
.getB2DPolyPolygon());
1557 SdrView
* pView
= rDrag
.GetView();
1559 if(pView
&& pView
->IsUseIncompatiblePathCreateInterface())
1562 ImpPathCreateUser
* pU
= static_cast<ImpPathCreateUser
*>(rDrag
.GetUser());
1563 basegfx::B2DPolygon
aNewPolygon(aRetval
.count() ? aRetval
.getB2DPolygon(aRetval
.count() - 1) : basegfx::B2DPolygon());
1565 if(pU
->IsFormFlag() && aNewPolygon
.count() > 1)
1567 // remove last segment and replace with current
1568 // do not forget to rescue the previous control point which will be lost when
1569 // the point it's associated with is removed
1570 const sal_uInt32
nChangeIndex(aNewPolygon
.count() - 2);
1571 const basegfx::B2DPoint
aSavedPrevCtrlPoint(aNewPolygon
.getPrevControlPoint(nChangeIndex
));
1573 aNewPolygon
.remove(nChangeIndex
, 2L);
1574 aNewPolygon
.append(pU
->GetFormPoly().getB2DPolygon());
1576 if(nChangeIndex
< aNewPolygon
.count())
1578 // if really something was added, set the saved previous control point to the
1579 // point where it belongs
1580 aNewPolygon
.setPrevControlPoint(nChangeIndex
, aSavedPrevCtrlPoint
);
1586 aRetval
.setB2DPolygon(aRetval
.count() - 1, aNewPolygon
);
1590 aRetval
.append(aNewPolygon
);
1596 basegfx::B2DPolyPolygon
ImpPathForDragAndCreate::TakeDragPolyPolygon(const SdrDragStat
& rDrag
)
1598 basegfx::B2DPolyPolygon aRetval
;
1599 SdrView
* pView
= rDrag
.GetView();
1601 if(pView
&& pView
->IsUseIncompatiblePathCreateInterface())
1604 const ImpPathCreateUser
* pU
= static_cast<const ImpPathCreateUser
*>(rDrag
.GetUser());
1606 if(pU
&& pU
->bBezier
&& rDrag
.IsMouseDown())
1608 // no more XOR, no need for complicated helplines
1609 basegfx::B2DPolygon aHelpline
;
1610 aHelpline
.append(basegfx::B2DPoint(pU
->aBezCtrl2
.X(), pU
->aBezCtrl2
.Y()));
1611 aHelpline
.append(basegfx::B2DPoint(pU
->aBezEnd
.X(), pU
->aBezEnd
.Y()));
1612 aRetval
.append(aHelpline
);
1618 Pointer
ImpPathForDragAndCreate::GetCreatePointer() const
1620 switch (meObjectKind
) {
1621 case OBJ_LINE
: return Pointer(PointerStyle::DrawLine
);
1622 case OBJ_POLY
: return Pointer(PointerStyle::DrawPolygon
);
1623 case OBJ_PLIN
: return Pointer(PointerStyle::DrawPolygon
);
1624 case OBJ_PATHLINE
: return Pointer(PointerStyle::DrawBezier
);
1625 case OBJ_PATHFILL
: return Pointer(PointerStyle::DrawBezier
);
1626 case OBJ_FREELINE
: return Pointer(PointerStyle::DrawFreehand
);
1627 case OBJ_FREEFILL
: return Pointer(PointerStyle::DrawFreehand
);
1628 case OBJ_SPLNLINE
: return Pointer(PointerStyle::DrawFreehand
);
1629 case OBJ_SPLNFILL
: return Pointer(PointerStyle::DrawFreehand
);
1630 case OBJ_PATHPOLY
: return Pointer(PointerStyle::DrawPolygon
);
1631 case OBJ_PATHPLIN
: return Pointer(PointerStyle::DrawPolygon
);
1634 return Pointer(PointerStyle::Cross
);
1637 SdrPathObjGeoData::SdrPathObjGeoData()
1642 SdrPathObjGeoData::~SdrPathObjGeoData()
1646 // DrawContact section
1648 sdr::contact::ViewContact
* SdrPathObj::CreateObjectSpecificViewContact()
1650 return new sdr::contact::ViewContactOfSdrPathObj(*this);
1654 SdrPathObj::SdrPathObj(SdrObjKind eNewKind
)
1658 bClosedObj
= IsClosed();
1661 SdrPathObj::SdrPathObj(SdrObjKind eNewKind
, const basegfx::B2DPolyPolygon
& rPathPoly
, double dBrightness
)
1662 : maPathPolygon(rPathPoly
),
1664 mdBrightness(dBrightness
)
1666 bClosedObj
= IsClosed();
1670 SdrPathObj::~SdrPathObj() = default;
1672 static bool lcl_ImpIsLine(const basegfx::B2DPolyPolygon
& rPolyPolygon
)
1674 return (1 == rPolyPolygon
.count() && 2 == rPolyPolygon
.getB2DPolygon(0).count());
1677 static tools::Rectangle
lcl_ImpGetBoundRect(const basegfx::B2DPolyPolygon
& rPolyPolygon
)
1679 basegfx::B2DRange
aRange(basegfx::utils::getRange(rPolyPolygon
));
1681 return tools::Rectangle(
1682 FRound(aRange
.getMinX()), FRound(aRange
.getMinY()),
1683 FRound(aRange
.getMaxX()), FRound(aRange
.getMaxY()));
1686 void SdrPathObj::ImpForceLineAngle()
1688 if(OBJ_LINE
!= meKind
|| !lcl_ImpIsLine(GetPathPoly()))
1691 const basegfx::B2DPolygon
aPoly(GetPathPoly().getB2DPolygon(0));
1692 const basegfx::B2DPoint
aB2DPoint0(aPoly
.getB2DPoint(0));
1693 const basegfx::B2DPoint
aB2DPoint1(aPoly
.getB2DPoint(1));
1694 const Point
aPoint0(FRound(aB2DPoint0
.getX()), FRound(aB2DPoint0
.getY()));
1695 const Point
aPoint1(FRound(aB2DPoint1
.getX()), FRound(aB2DPoint1
.getY()));
1696 const Point
aDelt(aPoint1
- aPoint0
);
1698 aGeo
.nRotationAngle
=GetAngle(aDelt
);
1700 aGeo
.RecalcSinCos();
1703 // for SdrTextObj, keep aRect up to date
1704 maRect
= tools::Rectangle(aPoint0
, aPoint1
);
1708 void SdrPathObj::ImpForceKind()
1710 if (meKind
==OBJ_PATHPLIN
) meKind
=OBJ_PLIN
;
1711 if (meKind
==OBJ_PATHPOLY
) meKind
=OBJ_POLY
;
1713 if(GetPathPoly().areControlPointsUsed())
1717 case OBJ_LINE
: meKind
=OBJ_PATHLINE
; break;
1718 case OBJ_PLIN
: meKind
=OBJ_PATHLINE
; break;
1719 case OBJ_POLY
: meKind
=OBJ_PATHFILL
; break;
1727 case OBJ_PATHLINE
: meKind
=OBJ_PLIN
; break;
1728 case OBJ_FREELINE
: meKind
=OBJ_PLIN
; break;
1729 case OBJ_PATHFILL
: meKind
=OBJ_POLY
; break;
1730 case OBJ_FREEFILL
: meKind
=OBJ_POLY
; break;
1735 if (meKind
==OBJ_LINE
&& !lcl_ImpIsLine(GetPathPoly())) meKind
=OBJ_PLIN
;
1736 if (meKind
==OBJ_PLIN
&& lcl_ImpIsLine(GetPathPoly())) meKind
=OBJ_LINE
;
1738 bClosedObj
=IsClosed();
1740 if (meKind
==OBJ_LINE
)
1742 ImpForceLineAngle();
1746 // #i10659#, for polys with more than 2 points.
1748 // Here i again need to fix something, because when Path-Polys are Copy-Pasted
1749 // between Apps with different measurements (e.g. 100TH_MM and TWIPS) there is
1750 // a scaling loop started from SdrExchangeView::Paste. In itself, this is not
1751 // wrong, but aRect is wrong here and not even updated by RecalcSnapRect(). If
1752 // this is the case, some size needs to be set here in aRect to avoid that the cycle
1753 // through Rect2Poly - Poly2Rect does something badly wrong since that cycle is
1754 // BASED on aRect. That cycle is triggered in SdrTextObj::NbcResize() which is called
1755 // from the local Resize() implementation.
1757 // Basic problem is that the member aRect in SdrTextObj basically is a unrotated
1758 // text rectangle for the text object itself and methods at SdrTextObj do handle it
1759 // in that way. Many draw objects derived from SdrTextObj 'abuse' aRect as SnapRect
1760 // which is basically wrong. To make the SdrText methods which deal with aRect directly
1761 // work it is necessary to always keep aRect updated. This e.g. not done after a Clone()
1762 // command for SdrPathObj. Since adding this update mechanism with #101412# to
1763 // ImpForceLineAngle() for lines was very successful, i add it to where ImpForceLineAngle()
1764 // was called, once here below and once on a 2nd place below.
1766 // #i10659# for SdrTextObj, keep aRect up to date
1767 if(GetPathPoly().count())
1769 maRect
= lcl_ImpGetBoundRect(GetPathPoly());
1773 // #i75974# adapt polygon state to object type. This may include a reinterpretation
1774 // of a closed geometry as open one, but with identical first and last point
1775 for(sal_uInt32
a(0); a
< maPathPolygon
.count(); a
++)
1777 basegfx::B2DPolygon
aCandidate(maPathPolygon
.getB2DPolygon(a
));
1779 if(IsClosed() != aCandidate
.isClosed())
1781 // #i80213# really change polygon geometry; else e.g. the last point which
1782 // needs to be identical with the first one will be missing when opening
1783 // due to OBJ_PATH type
1784 if(aCandidate
.isClosed())
1786 basegfx::utils::openWithGeometryChange(aCandidate
);
1790 basegfx::utils::closeWithGeometryChange(aCandidate
);
1793 maPathPolygon
.setB2DPolygon(a
, aCandidate
);
1798 void SdrPathObj::ImpSetClosed(bool bClose
)
1804 case OBJ_LINE
: meKind
=OBJ_POLY
; break;
1805 case OBJ_PLIN
: meKind
=OBJ_POLY
; break;
1806 case OBJ_PATHLINE
: meKind
=OBJ_PATHFILL
; break;
1807 case OBJ_FREELINE
: meKind
=OBJ_FREEFILL
; break;
1808 case OBJ_SPLNLINE
: meKind
=OBJ_SPLNFILL
; break;
1818 case OBJ_POLY
: meKind
=OBJ_PLIN
; break;
1819 case OBJ_PATHFILL
: meKind
=OBJ_PATHLINE
; break;
1820 case OBJ_FREEFILL
: meKind
=OBJ_FREELINE
; break;
1821 case OBJ_SPLNFILL
: meKind
=OBJ_SPLNLINE
; break;
1831 void SdrPathObj::TakeObjInfo(SdrObjTransformInfoRec
& rInfo
) const
1833 rInfo
.bNoContortion
=false;
1835 bool bCanConv
= !HasText() || ImpCanConvTextToCurve();
1836 bool bIsPath
= IsBezier() || IsSpline();
1838 rInfo
.bEdgeRadiusAllowed
= false;
1839 rInfo
.bCanConvToPath
= bCanConv
&& !bIsPath
;
1840 rInfo
.bCanConvToPoly
= bCanConv
&& bIsPath
;
1841 rInfo
.bCanConvToContour
= !IsFontwork() && (rInfo
.bCanConvToPoly
|| LineGeometryUsageIsNecessary());
1844 sal_uInt16
SdrPathObj::GetObjIdentifier() const
1846 return sal_uInt16(meKind
);
1849 SdrPathObj
* SdrPathObj::Clone() const
1851 return CloneHelper
< SdrPathObj
>();
1854 SdrPathObj
& SdrPathObj::operator=(const SdrPathObj
& rObj
)
1858 SdrTextObj::operator=(rObj
);
1859 maPathPolygon
=rObj
.GetPathPoly();
1863 OUString
SdrPathObj::TakeObjNameSingul() const
1865 OUStringBuffer sName
;
1867 if(OBJ_LINE
== meKind
)
1869 const char* pId(STR_ObjNameSingulLINE
);
1871 if(lcl_ImpIsLine(GetPathPoly()))
1873 const basegfx::B2DPolygon
aPoly(GetPathPoly().getB2DPolygon(0));
1874 const basegfx::B2DPoint
aB2DPoint0(aPoly
.getB2DPoint(0));
1875 const basegfx::B2DPoint
aB2DPoint1(aPoly
.getB2DPoint(1));
1877 if(aB2DPoint0
!= aB2DPoint1
)
1879 if(aB2DPoint0
.getY() == aB2DPoint1
.getY())
1881 pId
= STR_ObjNameSingulLINE_Hori
;
1883 else if(aB2DPoint0
.getX() == aB2DPoint1
.getX())
1885 pId
= STR_ObjNameSingulLINE_Vert
;
1889 const double fDx(fabs(aB2DPoint0
.getX() - aB2DPoint1
.getX()));
1890 const double fDy(fabs(aB2DPoint0
.getY() - aB2DPoint1
.getY()));
1894 pId
= STR_ObjNameSingulLINE_Diag
;
1900 sName
.append(ImpGetResStr(pId
));
1902 else if(OBJ_PLIN
== meKind
|| OBJ_POLY
== meKind
)
1904 const bool bClosed(OBJ_POLY
== meKind
);
1905 const char* pId(nullptr);
1907 if(mpDAC
&& mpDAC
->IsCreating())
1911 pId
= STR_ObjNameSingulPOLY
;
1915 pId
= STR_ObjNameSingulPLIN
;
1918 sName
.append(ImpGetResStr(pId
));
1923 sal_uInt32
nPointCount(0);
1924 const sal_uInt32
nPolyCount(GetPathPoly().count());
1926 for(sal_uInt32
a(0); a
< nPolyCount
; a
++)
1928 nPointCount
+= GetPathPoly().getB2DPolygon(a
).count();
1933 pId
= STR_ObjNameSingulPOLY_PointCount
;
1937 pId
= STR_ObjNameSingulPLIN_PointCount
;
1940 OUString
sTemp(ImpGetResStr(pId
));
1942 sName
.append(sTemp
.replaceFirst("%2", OUString::number(nPointCount
)));
1949 case OBJ_PATHLINE
: sName
.append(ImpGetResStr(STR_ObjNameSingulPATHLINE
)); break;
1950 case OBJ_FREELINE
: sName
.append(ImpGetResStr(STR_ObjNameSingulFREELINE
)); break;
1951 case OBJ_SPLNLINE
: sName
.append(ImpGetResStr(STR_ObjNameSingulNATSPLN
)); break;
1952 case OBJ_PATHFILL
: sName
.append(ImpGetResStr(STR_ObjNameSingulPATHFILL
)); break;
1953 case OBJ_FREEFILL
: sName
.append(ImpGetResStr(STR_ObjNameSingulFREEFILL
)); break;
1954 case OBJ_SPLNFILL
: sName
.append(ImpGetResStr(STR_ObjNameSingulPERSPLN
)); break;
1959 OUString
aName(GetName());
1960 if (!aName
.isEmpty())
1964 sName
.append(aName
);
1968 return sName
.makeStringAndClear();
1971 OUString
SdrPathObj::TakeObjNamePlural() const
1976 case OBJ_LINE
: sName
=ImpGetResStr(STR_ObjNamePluralLINE
); break;
1977 case OBJ_PLIN
: sName
=ImpGetResStr(STR_ObjNamePluralPLIN
); break;
1978 case OBJ_POLY
: sName
=ImpGetResStr(STR_ObjNamePluralPOLY
); break;
1979 case OBJ_PATHLINE
: sName
=ImpGetResStr(STR_ObjNamePluralPATHLINE
); break;
1980 case OBJ_FREELINE
: sName
=ImpGetResStr(STR_ObjNamePluralFREELINE
); break;
1981 case OBJ_SPLNLINE
: sName
=ImpGetResStr(STR_ObjNamePluralNATSPLN
); break;
1982 case OBJ_PATHFILL
: sName
=ImpGetResStr(STR_ObjNamePluralPATHFILL
); break;
1983 case OBJ_FREEFILL
: sName
=ImpGetResStr(STR_ObjNamePluralFREEFILL
); break;
1984 case OBJ_SPLNFILL
: sName
=ImpGetResStr(STR_ObjNamePluralPERSPLN
); break;
1990 basegfx::B2DPolyPolygon
SdrPathObj::TakeXorPoly() const
1992 return GetPathPoly();
1995 sal_uInt32
SdrPathObj::GetHdlCount() const
1997 sal_uInt32
nRetval(0);
1998 const sal_uInt32
nPolyCount(GetPathPoly().count());
2000 for(sal_uInt32
a(0); a
< nPolyCount
; a
++)
2002 nRetval
+= GetPathPoly().getB2DPolygon(a
).count();
2008 SdrHdl
* SdrPathObj::GetHdl(sal_uInt32 nHdlNum
) const
2011 // Warn the user that this is ineffective and show alternatives. Should not be used at all.
2012 OSL_FAIL("SdrPathObj::GetHdl(): ineffective, use AddToHdlList instead (!)");
2014 // to have an alternative, get single handle using the ineffective way
2015 SdrHdl
* pRetval
= nullptr;
2016 SdrHdlList
aLocalList(nullptr);
2017 AddToHdlList(aLocalList
);
2018 const sal_uInt32
nHdlCount(aLocalList
.GetHdlCount());
2020 if(nHdlCount
&& nHdlNum
< nHdlCount
)
2022 // remove and remember. The other created handles will be deleted again with the
2023 // destruction of the local list
2024 pRetval
= aLocalList
.RemoveHdl(nHdlNum
);
2030 void SdrPathObj::AddToHdlList(SdrHdlList
& rHdlList
) const
2032 // keep old stuff to be able to keep old SdrHdl stuff, too
2033 const XPolyPolygon
aOldPathPolygon(GetPathPoly());
2034 sal_uInt16 nPolyCnt
=aOldPathPolygon
.Count();
2035 bool bClosed
=IsClosed();
2038 for (sal_uInt16 i
=0; i
<nPolyCnt
; i
++) {
2039 const XPolygon
& rXPoly
=aOldPathPolygon
.GetObject(i
);
2040 sal_uInt16 nPntCnt
=rXPoly
.GetPointCount();
2041 if (bClosed
&& nPntCnt
>1) nPntCnt
--;
2043 for (sal_uInt16 j
=0; j
<nPntCnt
; j
++) {
2044 if (rXPoly
.GetFlags(j
)!=PolyFlags::Control
) {
2045 const Point
& rPnt
=rXPoly
[j
];
2046 SdrHdl
* pHdl
=new SdrHdl(rPnt
,SdrHdlKind::Poly
);
2047 pHdl
->SetPolyNum(i
);
2048 pHdl
->SetPointNum(j
);
2049 pHdl
->Set1PixMore(j
==0);
2050 pHdl
->SetSourceHdlNum(nIdx
);
2052 rHdlList
.AddHdl(pHdl
);
2058 sal_uInt32
SdrPathObj::GetPlusHdlCount(const SdrHdl
& rHdl
) const
2060 // keep old stuff to be able to keep old SdrHdl stuff, too
2061 const XPolyPolygon
aOldPathPolygon(GetPathPoly());
2062 sal_uInt16 nCnt
= 0;
2063 sal_uInt16 nPnt
= (sal_uInt16
)rHdl
.GetPointNum();
2064 sal_uInt16 nPolyNum
= (sal_uInt16
)rHdl
.GetPolyNum();
2066 if(nPolyNum
< aOldPathPolygon
.Count())
2068 const XPolygon
& rXPoly
= aOldPathPolygon
[nPolyNum
];
2069 sal_uInt16 nPntMax
= rXPoly
.GetPointCount();
2075 if (rXPoly
.GetFlags(nPnt
)!=PolyFlags::Control
)
2077 if (nPnt
==0 && IsClosed()) nPnt
=nPntMax
;
2078 if (nPnt
>0 && rXPoly
.GetFlags(nPnt
-1)==PolyFlags::Control
) nCnt
++;
2079 if (nPnt
==nPntMax
&& IsClosed()) nPnt
=0;
2080 if (nPnt
<nPntMax
&& rXPoly
.GetFlags(nPnt
+1)==PolyFlags::Control
) nCnt
++;
2089 SdrHdl
* SdrPathObj::GetPlusHdl(const SdrHdl
& rHdl
, sal_uInt32 nPlusNum
) const
2091 // keep old stuff to be able to keep old SdrHdl stuff, too
2092 const XPolyPolygon
aOldPathPolygon(GetPathPoly());
2093 SdrHdl
* pHdl
= nullptr;
2094 sal_uInt16 nPnt
= (sal_uInt16
)rHdl
.GetPointNum();
2095 sal_uInt16 nPolyNum
= (sal_uInt16
)rHdl
.GetPolyNum();
2097 if (nPolyNum
<aOldPathPolygon
.Count())
2099 const XPolygon
& rXPoly
= aOldPathPolygon
[nPolyNum
];
2100 sal_uInt16 nPntMax
= rXPoly
.GetPointCount();
2107 pHdl
=new SdrHdlBezWgt(&rHdl
);
2108 pHdl
->SetPolyNum(rHdl
.GetPolyNum());
2110 if (nPnt
==0 && IsClosed()) nPnt
=nPntMax
;
2111 if (nPnt
>0 && rXPoly
.GetFlags(nPnt
-1)==PolyFlags::Control
&& nPlusNum
==0)
2113 pHdl
->SetPos(rXPoly
[nPnt
-1]);
2114 pHdl
->SetPointNum(nPnt
-1);
2118 if (nPnt
==nPntMax
&& IsClosed()) nPnt
=0;
2119 if (nPnt
<rXPoly
.GetPointCount()-1 && rXPoly
.GetFlags(nPnt
+1)==PolyFlags::Control
)
2121 pHdl
->SetPos(rXPoly
[nPnt
+1]);
2122 pHdl
->SetPointNum(nPnt
+1);
2126 pHdl
->SetSourceHdlNum(rHdl
.GetSourceHdlNum());
2127 pHdl
->SetPlusHdl(true);
2136 bool SdrPathObj::hasSpecialDrag() const
2141 bool SdrPathObj::beginSpecialDrag(SdrDragStat
& rDrag
) const
2143 ImpPathForDragAndCreate
aDragAndCreate(*const_cast<SdrPathObj
*>(this));
2145 return aDragAndCreate
.beginPathDrag(rDrag
);
2148 bool SdrPathObj::applySpecialDrag(SdrDragStat
& rDrag
)
2150 ImpPathForDragAndCreate
aDragAndCreate(*this);
2151 bool bRetval(aDragAndCreate
.beginPathDrag(rDrag
));
2155 bRetval
= aDragAndCreate
.movePathDrag(rDrag
);
2160 bRetval
= aDragAndCreate
.endPathDrag(rDrag
);
2165 NbcSetPathPoly(aDragAndCreate
.getModifiedPolyPolygon());
2171 OUString
SdrPathObj::getSpecialDragComment(const SdrDragStat
& rDrag
) const
2177 // #i103058# also get a comment when in creation
2178 const bool bCreateComment(rDrag
.GetView() && this == rDrag
.GetView()->GetCreateObj());
2182 aRetval
= mpDAC
->getSpecialDragComment(rDrag
);
2187 ImpPathForDragAndCreate
aDragAndCreate(*const_cast<SdrPathObj
*>(this));
2188 bool bDidWork(aDragAndCreate
.beginPathDrag(rDrag
));
2192 aRetval
= aDragAndCreate
.getSpecialDragComment(rDrag
);
2199 basegfx::B2DPolyPolygon
SdrPathObj::getSpecialDragPoly(const SdrDragStat
& rDrag
) const
2201 basegfx::B2DPolyPolygon aRetval
;
2202 ImpPathForDragAndCreate
aDragAndCreate(*const_cast<SdrPathObj
*>(this));
2203 bool bDidWork(aDragAndCreate
.beginPathDrag(rDrag
));
2207 aRetval
= aDragAndCreate
.getSpecialDragPoly(rDrag
);
2215 bool SdrPathObj::BegCreate(SdrDragStat
& rStat
)
2218 return impGetDAC().BegCreate(rStat
);
2221 bool SdrPathObj::MovCreate(SdrDragStat
& rStat
)
2223 return impGetDAC().MovCreate(rStat
);
2226 bool SdrPathObj::EndCreate(SdrDragStat
& rStat
, SdrCreateCmd eCmd
)
2228 bool bRetval(impGetDAC().EndCreate(rStat
, eCmd
));
2230 if(bRetval
&& mpDAC
)
2232 SetPathPoly(mpDAC
->getModifiedPolyPolygon());
2234 // #i75974# Check for AutoClose feature. Moved here from ImpPathForDragAndCreate::EndCreate
2235 // to be able to use the type-changing ImpSetClosed method
2238 SdrView
* pView
= rStat
.GetView();
2240 if(pView
&& !pView
->IsUseIncompatiblePathCreateInterface())
2242 OutputDevice
* pOut
= pView
->GetFirstOutputDevice();
2246 if(GetPathPoly().count())
2248 const basegfx::B2DPolygon
aCandidate(GetPathPoly().getB2DPolygon(0));
2250 if(aCandidate
.count() > 2)
2252 // check distance of first and last point
2253 const sal_Int32
nCloseDist(pOut
->PixelToLogic(Size(pView
->GetAutoCloseDistPix(), 0)).Width());
2254 const basegfx::B2DVector
aDistVector(aCandidate
.getB2DPoint(aCandidate
.count() - 1) - aCandidate
.getB2DPoint(0));
2256 if(aDistVector
.getLength() <= (double)nCloseDist
)
2273 bool SdrPathObj::BckCreate(SdrDragStat
& rStat
)
2275 return impGetDAC().BckCreate(rStat
);
2278 void SdrPathObj::BrkCreate(SdrDragStat
& rStat
)
2280 impGetDAC().BrkCreate(rStat
);
2286 basegfx::B2DPolyPolygon
SdrPathObj::TakeCreatePoly(const SdrDragStat
& rDrag
) const
2288 basegfx::B2DPolyPolygon aRetval
;
2292 aRetval
= mpDAC
->TakeObjectPolyPolygon(rDrag
);
2293 aRetval
.append(ImpPathForDragAndCreate::TakeDragPolyPolygon(rDrag
));
2299 // during drag or create, allow accessing the so-far created/modified polyPolygon
2300 basegfx::B2DPolyPolygon
SdrPathObj::getObjectPolyPolygon(const SdrDragStat
& rDrag
) const
2302 basegfx::B2DPolyPolygon aRetval
;
2306 aRetval
= mpDAC
->TakeObjectPolyPolygon(rDrag
);
2312 basegfx::B2DPolyPolygon
SdrPathObj::getDragPolyPolygon(const SdrDragStat
& rDrag
) const
2314 basegfx::B2DPolyPolygon aRetval
;
2318 aRetval
= ImpPathForDragAndCreate::TakeDragPolyPolygon(rDrag
);
2324 Pointer
SdrPathObj::GetCreatePointer() const
2326 return impGetDAC().GetCreatePointer();
2329 void SdrPathObj::NbcMove(const Size
& rSiz
)
2331 maPathPolygon
.transform(basegfx::utils::createTranslateB2DHomMatrix(rSiz
.Width(), rSiz
.Height()));
2333 // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
2334 SdrTextObj::NbcMove(rSiz
);
2337 void SdrPathObj::NbcResize(const Point
& rRef
, const Fraction
& xFact
, const Fraction
& yFact
)
2339 basegfx::B2DHomMatrix
aTrans(basegfx::utils::createTranslateB2DHomMatrix(-rRef
.X(), -rRef
.Y()));
2340 aTrans
= basegfx::utils::createScaleTranslateB2DHomMatrix(
2341 double(xFact
), double(yFact
), rRef
.X(), rRef
.Y()) * aTrans
;
2342 maPathPolygon
.transform(aTrans
);
2344 // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
2345 SdrTextObj::NbcResize(rRef
,xFact
,yFact
);
2348 void SdrPathObj::NbcRotate(const Point
& rRef
, long nAngle
, double sn
, double cs
)
2350 // Thank JOE, the angles are defined mirrored to the mathematical meanings
2351 const basegfx::B2DHomMatrix
aTrans(basegfx::utils::createRotateAroundPoint(rRef
.X(), rRef
.Y(), -nAngle
* nPi180
));
2352 maPathPolygon
.transform(aTrans
);
2354 // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
2355 SdrTextObj::NbcRotate(rRef
,nAngle
,sn
,cs
);
2358 void SdrPathObj::NbcShear(const Point
& rRefPnt
, long nAngle
, double fTan
, bool bVShear
)
2360 basegfx::B2DHomMatrix
aTrans(basegfx::utils::createTranslateB2DHomMatrix(-rRefPnt
.X(), -rRefPnt
.Y()));
2364 // Thank JOE, the angles are defined mirrored to the mathematical meanings
2365 aTrans
.shearY(-fTan
);
2369 aTrans
.shearX(-fTan
);
2372 aTrans
.translate(rRefPnt
.X(), rRefPnt
.Y());
2373 maPathPolygon
.transform(aTrans
);
2375 // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
2376 SdrTextObj::NbcShear(rRefPnt
,nAngle
,fTan
,bVShear
);
2379 void SdrPathObj::NbcMirror(const Point
& rRefPnt1
, const Point
& rRefPnt2
)
2381 const double fDiffX(rRefPnt2
.X() - rRefPnt1
.X());
2382 const double fDiffY(rRefPnt2
.Y() - rRefPnt1
.Y());
2383 const double fRot(atan2(fDiffY
, fDiffX
));
2384 basegfx::B2DHomMatrix
aTrans(basegfx::utils::createTranslateB2DHomMatrix(-rRefPnt1
.X(), -rRefPnt1
.Y()));
2385 aTrans
.rotate(-fRot
);
2386 aTrans
.scale(1.0, -1.0);
2387 aTrans
.rotate(fRot
);
2388 aTrans
.translate(rRefPnt1
.X(), rRefPnt1
.Y());
2389 maPathPolygon
.transform(aTrans
);
2391 // Do Joe's special handling for lines when mirroring, too
2394 // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
2395 SdrTextObj::NbcMirror(rRefPnt1
,rRefPnt2
);
2398 void SdrPathObj::TakeUnrotatedSnapRect(tools::Rectangle
& rRect
) const
2400 if(!aGeo
.nRotationAngle
)
2402 rRect
= GetSnapRect();
2406 XPolyPolygon
aXPP(GetPathPoly());
2407 RotateXPoly(aXPP
,Point(),-aGeo
.nSin
,aGeo
.nCos
);
2408 rRect
=aXPP
.GetBoundRect();
2409 Point
aTmp(rRect
.TopLeft());
2410 RotatePoint(aTmp
,Point(),aGeo
.nSin
,aGeo
.nCos
);
2411 aTmp
-=rRect
.TopLeft();
2412 rRect
.Move(aTmp
.X(),aTmp
.Y());
2416 void SdrPathObj::RecalcSnapRect()
2418 if(GetPathPoly().count())
2420 maSnapRect
= lcl_ImpGetBoundRect(GetPathPoly());
2424 void SdrPathObj::NbcSetSnapRect(const tools::Rectangle
& rRect
)
2426 tools::Rectangle
aOld(GetSnapRect());
2428 // Take RECT_EMPTY into account when calculating scale factors
2429 long nMulX
= (RECT_EMPTY
== rRect
.Right()) ? 0 : rRect
.Right() - rRect
.Left();
2431 long nDivX
= aOld
.Right() - aOld
.Left();
2433 // Take RECT_EMPTY into account when calculating scale factors
2434 long nMulY
= (RECT_EMPTY
== rRect
.Bottom()) ? 0 : rRect
.Bottom() - rRect
.Top();
2436 long nDivY
= aOld
.Bottom() - aOld
.Top();
2437 if ( nDivX
== 0 ) { nMulX
= 1; nDivX
= 1; }
2438 if ( nDivY
== 0 ) { nMulY
= 1; nDivY
= 1; }
2439 if ( nDivX
== nMulX
) { nMulX
= 1; nDivX
= 1; }
2440 if ( nDivY
== nMulY
) { nMulY
= 1; nDivY
= 1; }
2441 Fraction
aX(nMulX
,nDivX
);
2442 Fraction
aY(nMulY
,nDivY
);
2443 NbcResize(aOld
.TopLeft(), aX
, aY
);
2444 NbcMove(Size(rRect
.Left() - aOld
.Left(), rRect
.Top() - aOld
.Top()));
2447 sal_uInt32
SdrPathObj::GetSnapPointCount() const
2449 return GetHdlCount();
2452 Point
SdrPathObj::GetSnapPoint(sal_uInt32 nSnapPnt
) const
2454 sal_uInt32 nPoly
,nPnt
;
2455 if(!PolyPolygonEditor::GetRelativePolyPoint(GetPathPoly(), nSnapPnt
, nPoly
, nPnt
))
2457 SAL_WARN("svx", "SdrPathObj::GetSnapPoint: Point nSnapPnt does not exist.");
2460 const basegfx::B2DPoint
aB2DPoint(GetPathPoly().getB2DPolygon(nPoly
).getB2DPoint(nPnt
));
2461 return Point(FRound(aB2DPoint
.getX()), FRound(aB2DPoint
.getY()));
2464 bool SdrPathObj::IsPolyObj() const
2469 sal_uInt32
SdrPathObj::GetPointCount() const
2471 const sal_uInt32
nPolyCount(GetPathPoly().count());
2472 sal_uInt32
nRetval(0);
2474 for(sal_uInt32
a(0); a
< nPolyCount
; a
++)
2476 nRetval
+= GetPathPoly().getB2DPolygon(a
).count();
2482 Point
SdrPathObj::GetPoint(sal_uInt32 nHdlNum
) const
2485 sal_uInt32 nPoly
,nPnt
;
2487 if(PolyPolygonEditor::GetRelativePolyPoint(GetPathPoly(), nHdlNum
, nPoly
, nPnt
))
2489 const basegfx::B2DPolygon
aPoly(GetPathPoly().getB2DPolygon(nPoly
));
2490 const basegfx::B2DPoint
aPoint(aPoly
.getB2DPoint(nPnt
));
2491 aRetval
= Point(FRound(aPoint
.getX()), FRound(aPoint
.getY()));
2497 void SdrPathObj::NbcSetPoint(const Point
& rPnt
, sal_uInt32 nHdlNum
)
2499 sal_uInt32 nPoly
,nPnt
;
2501 if(PolyPolygonEditor::GetRelativePolyPoint(GetPathPoly(), nHdlNum
, nPoly
, nPnt
))
2503 basegfx::B2DPolygon
aNewPolygon(GetPathPoly().getB2DPolygon(nPoly
));
2504 aNewPolygon
.setB2DPoint(nPnt
, basegfx::B2DPoint(rPnt
.X(), rPnt
.Y()));
2505 maPathPolygon
.setB2DPolygon(nPoly
, aNewPolygon
);
2507 if(meKind
==OBJ_LINE
)
2509 ImpForceLineAngle();
2513 if(GetPathPoly().count())
2515 // #i10659# for SdrTextObj, keep aRect up to date
2516 maRect
= lcl_ImpGetBoundRect(GetPathPoly());
2524 sal_uInt32
SdrPathObj::NbcInsPointOld(const Point
& rPos
, bool bNewObj
)
2530 nNewHdl
= NbcInsPoint(rPos
, true);
2534 // look for smallest distance data
2535 const basegfx::B2DPoint
aTestPoint(rPos
.X(), rPos
.Y());
2536 sal_uInt32
nSmallestPolyIndex(0);
2537 sal_uInt32
nSmallestEdgeIndex(0);
2538 double fSmallestCut
;
2539 basegfx::utils::getSmallestDistancePointToPolyPolygon(GetPathPoly(), aTestPoint
, nSmallestPolyIndex
, nSmallestEdgeIndex
, fSmallestCut
);
2541 nNewHdl
= NbcInsPoint(rPos
, false);
2548 sal_uInt32
SdrPathObj::NbcInsPoint(const Point
& rPos
, bool bNewObj
)
2554 basegfx::B2DPolygon aNewPoly
;
2555 const basegfx::B2DPoint
aPoint(rPos
.X(), rPos
.Y());
2556 aNewPoly
.append(aPoint
);
2557 aNewPoly
.setClosed(IsClosed());
2558 maPathPolygon
.append(aNewPoly
);
2560 nNewHdl
= GetHdlCount();
2564 // look for smallest distance data
2565 const basegfx::B2DPoint
aTestPoint(rPos
.X(), rPos
.Y());
2566 sal_uInt32
nSmallestPolyIndex(0);
2567 sal_uInt32
nSmallestEdgeIndex(0);
2568 double fSmallestCut
;
2569 basegfx::utils::getSmallestDistancePointToPolyPolygon(GetPathPoly(), aTestPoint
, nSmallestPolyIndex
, nSmallestEdgeIndex
, fSmallestCut
);
2570 basegfx::B2DPolygon
aCandidate(GetPathPoly().getB2DPolygon(nSmallestPolyIndex
));
2571 const bool bBefore(!aCandidate
.isClosed() && 0 == nSmallestEdgeIndex
&& 0.0 == fSmallestCut
);
2572 const bool bAfter(!aCandidate
.isClosed() && aCandidate
.count() == nSmallestEdgeIndex
+ 2 && 1.0 == fSmallestCut
);
2576 // before first point
2577 aCandidate
.insert(0L, aTestPoint
);
2579 if(aCandidate
.areControlPointsUsed())
2581 if(aCandidate
.isNextControlPointUsed(1))
2583 aCandidate
.setNextControlPoint(0, interpolate(aTestPoint
, aCandidate
.getB2DPoint(1), (1.0 / 3.0)));
2584 aCandidate
.setPrevControlPoint(1, interpolate(aTestPoint
, aCandidate
.getB2DPoint(1), (2.0 / 3.0)));
2593 aCandidate
.append(aTestPoint
);
2595 if(aCandidate
.areControlPointsUsed())
2597 if(aCandidate
.isPrevControlPointUsed(aCandidate
.count() - 2))
2599 aCandidate
.setNextControlPoint(aCandidate
.count() - 2, interpolate(aCandidate
.getB2DPoint(aCandidate
.count() - 2), aTestPoint
, (1.0 / 3.0)));
2600 aCandidate
.setPrevControlPoint(aCandidate
.count() - 1, interpolate(aCandidate
.getB2DPoint(aCandidate
.count() - 2), aTestPoint
, (2.0 / 3.0)));
2604 nNewHdl
= aCandidate
.count() - 1;
2609 bool bSegmentSplit(false);
2610 const sal_uInt32
nNextIndex((nSmallestEdgeIndex
+ 1) % aCandidate
.count());
2612 if(aCandidate
.areControlPointsUsed())
2614 if(aCandidate
.isNextControlPointUsed(nSmallestEdgeIndex
) || aCandidate
.isPrevControlPointUsed(nNextIndex
))
2616 bSegmentSplit
= true;
2622 // rebuild original segment to get the split data
2623 basegfx::B2DCubicBezier aBezierA
, aBezierB
;
2624 const basegfx::B2DCubicBezier
aBezier(
2625 aCandidate
.getB2DPoint(nSmallestEdgeIndex
),
2626 aCandidate
.getNextControlPoint(nSmallestEdgeIndex
),
2627 aCandidate
.getPrevControlPoint(nNextIndex
),
2628 aCandidate
.getB2DPoint(nNextIndex
));
2630 // split and insert hit point
2631 aBezier
.split(fSmallestCut
, &aBezierA
, &aBezierB
);
2632 aCandidate
.insert(nSmallestEdgeIndex
+ 1, aTestPoint
);
2634 // since we inserted hit point and not split point, we need to add an offset
2635 // to the control points to get the C1 continuity we want to achieve
2636 const basegfx::B2DVector
aOffset(aTestPoint
- aBezierA
.getEndPoint());
2637 aCandidate
.setNextControlPoint(nSmallestEdgeIndex
, aBezierA
.getControlPointA() + aOffset
);
2638 aCandidate
.setPrevControlPoint(nSmallestEdgeIndex
+ 1, aBezierA
.getControlPointB() + aOffset
);
2639 aCandidate
.setNextControlPoint(nSmallestEdgeIndex
+ 1, aBezierB
.getControlPointA() + aOffset
);
2640 aCandidate
.setPrevControlPoint((nSmallestEdgeIndex
+ 2) % aCandidate
.count(), aBezierB
.getControlPointB() + aOffset
);
2644 aCandidate
.insert(nSmallestEdgeIndex
+ 1, aTestPoint
);
2647 nNewHdl
= nSmallestEdgeIndex
+ 1;
2650 maPathPolygon
.setB2DPolygon(nSmallestPolyIndex
, aCandidate
);
2652 // create old polygon index from it
2653 for(sal_uInt32
a(0); a
< nSmallestPolyIndex
; a
++)
2655 nNewHdl
+= GetPathPoly().getB2DPolygon(a
).count();
2663 SdrObject
* SdrPathObj::RipPoint(sal_uInt32 nHdlNum
, sal_uInt32
& rNewPt0Index
)
2665 SdrPathObj
* pNewObj
= nullptr;
2666 const basegfx::B2DPolyPolygon
aLocalPolyPolygon(GetPathPoly());
2667 sal_uInt32 nPoly
, nPnt
;
2669 if(PolyPolygonEditor::GetRelativePolyPoint(aLocalPolyPolygon
, nHdlNum
, nPoly
, nPnt
))
2673 const basegfx::B2DPolygon
aCandidate(aLocalPolyPolygon
.getB2DPolygon(nPoly
));
2674 const sal_uInt32
nPointCount(aCandidate
.count());
2680 // when closed, RipPoint means to open the polygon at the selected point. To
2681 // be able to do that, it is necessary to make the selected point the first one
2682 basegfx::B2DPolygon
aNewPolygon(basegfx::utils::makeStartPoint(aCandidate
, nPnt
));
2683 SetPathPoly(basegfx::B2DPolyPolygon(aNewPolygon
));
2686 // give back new position of old start point (historical reasons)
2687 rNewPt0Index
= (nPointCount
- nPnt
) % nPointCount
;
2691 if(nPointCount
>= 3 && nPnt
!= 0 && nPnt
+ 1 < nPointCount
)
2693 // split in two objects at point nPnt
2694 basegfx::B2DPolygon
aSplitPolyA(aCandidate
, 0L, nPnt
+ 1);
2695 SetPathPoly(basegfx::B2DPolyPolygon(aSplitPolyA
));
2698 basegfx::B2DPolygon
aSplitPolyB(aCandidate
, nPnt
, nPointCount
- nPnt
);
2699 pNewObj
->SetPathPoly(basegfx::B2DPolyPolygon(aSplitPolyB
));
2709 SdrObject
* SdrPathObj::DoConvertToPolyObj(bool bBezier
, bool bAddText
) const
2711 // #i89784# check for FontWork with activated HideContour
2712 const drawinglayer::attribute::SdrTextAttribute
aText(
2713 drawinglayer::primitive2d::createNewSdrTextAttribute(GetObjectItemSet(), *getText(0)));
2714 const bool bHideContour(
2715 !aText
.isDefault() && !aText
.getSdrFormTextAttribute().isDefault() && aText
.isHideContour());
2717 SdrObject
* pRet
= bHideContour
?
2719 ImpConvertMakeObj(GetPathPoly(), IsClosed(), bBezier
);
2721 SdrPathObj
* pPath
= dynamic_cast<SdrPathObj
*>( pRet
);
2725 if(pPath
->GetPathPoly().areControlPointsUsed())
2729 // reduce all bezier curves
2730 pPath
->SetPathPoly(basegfx::utils::adaptiveSubdivideByAngle(pPath
->GetPathPoly()));
2737 // create bezier curves
2738 pPath
->SetPathPoly(basegfx::utils::expandToCurve(pPath
->GetPathPoly()));
2745 pRet
= ImpConvertAddText(pRet
, bBezier
);
2751 SdrObjGeoData
* SdrPathObj::NewGeoData() const
2753 return new SdrPathObjGeoData
;
2756 void SdrPathObj::SaveGeoData(SdrObjGeoData
& rGeo
) const
2758 SdrTextObj::SaveGeoData(rGeo
);
2759 SdrPathObjGeoData
& rPGeo
= static_cast<SdrPathObjGeoData
&>( rGeo
);
2760 rPGeo
.maPathPolygon
=GetPathPoly();
2761 rPGeo
.meKind
=meKind
;
2764 void SdrPathObj::RestGeoData(const SdrObjGeoData
& rGeo
)
2766 SdrTextObj::RestGeoData(rGeo
);
2767 const SdrPathObjGeoData
& rPGeo
=static_cast<const SdrPathObjGeoData
&>(rGeo
);
2768 maPathPolygon
=rPGeo
.maPathPolygon
;
2769 meKind
=rPGeo
.meKind
;
2770 ImpForceKind(); // to set bClosed (among other things)
2773 void SdrPathObj::NbcSetPathPoly(const basegfx::B2DPolyPolygon
& rPathPoly
)
2775 if(GetPathPoly() != rPathPoly
)
2777 maPathPolygon
=rPathPoly
;
2783 void SdrPathObj::SetPathPoly(const basegfx::B2DPolyPolygon
& rPathPoly
)
2785 if(GetPathPoly() != rPathPoly
)
2787 tools::Rectangle aBoundRect0
; if (pUserCall
!=nullptr) aBoundRect0
=GetLastBoundRect();
2788 NbcSetPathPoly(rPathPoly
);
2790 BroadcastObjectChange();
2791 SendUserCall(SdrUserCallType::Resize
,aBoundRect0
);
2795 void SdrPathObj::ToggleClosed()
2797 tools::Rectangle aBoundRect0
;
2798 if(pUserCall
!= nullptr)
2799 aBoundRect0
= GetLastBoundRect();
2800 ImpSetClosed(!IsClosed()); // set new ObjKind
2801 ImpForceKind(); // because we want Line -> Poly -> PolyLine instead of Line -> Poly -> Line
2804 BroadcastObjectChange();
2805 SendUserCall(SdrUserCallType::Resize
, aBoundRect0
);
2808 ImpPathForDragAndCreate
& SdrPathObj::impGetDAC() const
2812 const_cast<SdrPathObj
*>(this)->mpDAC
.reset(new ImpPathForDragAndCreate(*const_cast<SdrPathObj
*>(this)));
2819 // transformation interface for StarOfficeAPI. This implements support for
2820 // homogeneous 3x3 matrices containing the transformation of the SdrObject. At the
2821 // moment it contains a shearX, rotation and translation, but for setting all linear
2822 // transforms like Scale, ShearX, ShearY, Rotate and Translate are supported.
2825 // gets base transformation and rectangle of object. If it's an SdrPathObj it fills the PolyPolygon
2826 // with the base geometry and returns TRUE. Otherwise it returns FALSE.
2827 bool SdrPathObj::TRGetBaseGeometry(basegfx::B2DHomMatrix
& rMatrix
, basegfx::B2DPolyPolygon
& rPolyPolygon
) const
2829 double fRotate(0.0);
2830 double fShearX(0.0);
2831 basegfx::B2DTuple
aScale(1.0, 1.0);
2832 basegfx::B2DTuple
aTranslate(0.0, 0.0);
2834 if(GetPathPoly().count())
2837 basegfx::B2DHomMatrix aMoveToZeroMatrix
;
2838 rPolyPolygon
= GetPathPoly();
2840 if(OBJ_LINE
== meKind
)
2842 // ignore shear and rotate, just use scale and translate
2843 OSL_ENSURE(GetPathPoly().count() > 0 && GetPathPoly().getB2DPolygon(0).count() > 1, "OBJ_LINE with too few polygons (!)");
2844 // #i72287# use polygon without control points for range calculation. Do not change rPolyPolygon
2845 // itself, else this method will no longer return the full polygon information (curve will
2847 const basegfx::B2DRange
aPolyRangeNoCurve(basegfx::utils::getRange(rPolyPolygon
));
2848 aScale
= aPolyRangeNoCurve
.getRange();
2849 aTranslate
= aPolyRangeNoCurve
.getMinimum();
2851 // define matrix for move polygon to zero point
2852 aMoveToZeroMatrix
.translate(-aTranslate
.getX(), -aTranslate
.getY());
2856 if(aGeo
.nShearAngle
|| aGeo
.nRotationAngle
)
2858 // get rotate and shear in drawingLayer notation
2859 fRotate
= aGeo
.nRotationAngle
* F_PI18000
;
2860 fShearX
= aGeo
.nShearAngle
* F_PI18000
;
2862 // build mathematically correct (negative shear and rotate) object transform
2863 // containing shear and rotate to extract unsheared, unrotated polygon
2864 basegfx::B2DHomMatrix aObjectMatrix
;
2865 aObjectMatrix
.shearX(tan((36000 - aGeo
.nShearAngle
) * F_PI18000
));
2866 aObjectMatrix
.rotate((36000 - aGeo
.nRotationAngle
) * F_PI18000
);
2868 // create inverse from it and back-transform polygon
2869 basegfx::B2DHomMatrix
aInvObjectMatrix(aObjectMatrix
);
2870 aInvObjectMatrix
.invert();
2871 rPolyPolygon
.transform(aInvObjectMatrix
);
2873 // get range from unsheared, unrotated polygon and extract scale and translate.
2874 // transform topLeft from it back to transformed state to get original
2875 // topLeft (rotation center)
2876 // #i72287# use polygon without control points for range calculation. Do not change rPolyPolygon
2877 // itself, else this method will no longer return the full polygon information (curve will
2879 const basegfx::B2DRange
aCorrectedRangeNoCurve(basegfx::utils::getRange(rPolyPolygon
));
2880 aTranslate
= aObjectMatrix
* aCorrectedRangeNoCurve
.getMinimum();
2881 aScale
= aCorrectedRangeNoCurve
.getRange();
2883 // define matrix for move polygon to zero point
2884 // #i112280# Added missing minus for Y-Translation
2885 aMoveToZeroMatrix
.translate(-aCorrectedRangeNoCurve
.getMinX(), -aCorrectedRangeNoCurve
.getMinY());
2889 // get scale and translate from unsheared, unrotated polygon
2890 // #i72287# use polygon without control points for range calculation. Do not change rPolyPolygon
2891 // itself, else this method will no longer return the full polygon information (curve will
2893 const basegfx::B2DRange
aPolyRangeNoCurve(basegfx::utils::getRange(rPolyPolygon
));
2894 aScale
= aPolyRangeNoCurve
.getRange();
2895 aTranslate
= aPolyRangeNoCurve
.getMinimum();
2897 // define matrix for move polygon to zero point
2898 aMoveToZeroMatrix
.translate(-aTranslate
.getX(), -aTranslate
.getY());
2902 // move polygon to zero point with pre-defined matrix
2903 rPolyPolygon
.transform(aMoveToZeroMatrix
);
2906 // position maybe relative to anchorpos, convert
2907 if( pModel
&& pModel
->IsWriter() )
2909 if(GetAnchorPos().X() || GetAnchorPos().Y())
2911 aTranslate
-= basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
2915 // force MapUnit to 100th mm
2916 const MapUnit
eMapUnit(GetObjectMapUnit());
2917 if(eMapUnit
!= MapUnit::Map100thMM
)
2921 case MapUnit::MapTwip
:
2924 aTranslate
.setX(ImplTwipsToMM(aTranslate
.getX()));
2925 aTranslate
.setY(ImplTwipsToMM(aTranslate
.getY()));
2928 aScale
.setX(ImplTwipsToMM(aScale
.getX()));
2929 aScale
.setY(ImplTwipsToMM(aScale
.getY()));
2932 basegfx::B2DHomMatrix aTwipsToMM
;
2933 const double fFactorTwipsToMM(127.0 / 72.0);
2934 aTwipsToMM
.scale(fFactorTwipsToMM
, fFactorTwipsToMM
);
2935 rPolyPolygon
.transform(aTwipsToMM
);
2941 OSL_FAIL("TRGetBaseGeometry: Missing unit translation to 100th mm!");
2946 // build return value matrix
2947 rMatrix
= basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
2949 basegfx::fTools::equalZero(fShearX
) ? 0.0 : tan(fShearX
),
2950 basegfx::fTools::equalZero(fRotate
) ? 0.0 : -fRotate
,
2956 // Sets the base geometry of the object using infos contained in the homogeneous 3x3 matrix.
2957 // If it's an SdrPathObj it will use the provided geometry information. The Polygon has
2958 // to use (0,0) as upper left and will be scaled to the given size in the matrix.
2959 void SdrPathObj::TRSetBaseGeometry(const basegfx::B2DHomMatrix
& rMatrix
, const basegfx::B2DPolyPolygon
& rPolyPolygon
)
2962 basegfx::B2DTuple aScale
;
2963 basegfx::B2DTuple aTranslate
;
2964 double fRotate
, fShearX
;
2965 rMatrix
.decompose(aScale
, aTranslate
, fRotate
, fShearX
);
2967 // #i75086# Old DrawingLayer (GeoStat and geometry) does not support holding negative scalings
2968 // in X and Y which equal a 180 degree rotation. Recognize it and react accordingly
2969 if(basegfx::fTools::less(aScale
.getX(), 0.0) && basegfx::fTools::less(aScale
.getY(), 0.0))
2971 aScale
.setX(fabs(aScale
.getX()));
2972 aScale
.setY(fabs(aScale
.getY()));
2973 fRotate
= fmod(fRotate
+ F_PI
, F_2PI
);
2977 basegfx::B2DPolyPolygon
aNewPolyPolygon(rPolyPolygon
);
2979 // reset object shear and rotations
2980 aGeo
.nRotationAngle
= 0;
2981 aGeo
.RecalcSinCos();
2982 aGeo
.nShearAngle
= 0;
2985 // force metric to pool metric
2986 const MapUnit
eMapUnit(GetObjectMapUnit());
2987 if(eMapUnit
!= MapUnit::Map100thMM
)
2991 case MapUnit::MapTwip
:
2994 aTranslate
.setX(ImplMMToTwips(aTranslate
.getX()));
2995 aTranslate
.setY(ImplMMToTwips(aTranslate
.getY()));
2998 aScale
.setX(ImplMMToTwips(aScale
.getX()));
2999 aScale
.setY(ImplMMToTwips(aScale
.getY()));
3002 basegfx::B2DHomMatrix aMMToTwips
;
3003 const double fFactorMMToTwips(72.0 / 127.0);
3004 aMMToTwips
.scale(fFactorMMToTwips
, fFactorMMToTwips
);
3005 aNewPolyPolygon
.transform(aMMToTwips
);
3011 OSL_FAIL("TRSetBaseGeometry: Missing unit translation to PoolMetric!");
3016 if( pModel
&& pModel
->IsWriter() )
3018 // if anchor is used, make position relative to it
3019 if(GetAnchorPos().X() || GetAnchorPos().Y())
3021 aTranslate
+= basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
3025 // create transformation for polygon, set values at aGeo direct
3026 basegfx::B2DHomMatrix aTransform
;
3029 // Given polygon is already scaled (for historical reasons), but not mirrored yet.
3030 // Thus, when scale is negative in X or Y, apply the needed mirroring accordingly.
3031 if(basegfx::fTools::less(aScale
.getX(), 0.0) || basegfx::fTools::less(aScale
.getY(), 0.0))
3034 basegfx::fTools::less(aScale
.getX(), 0.0) ? -1.0 : 1.0,
3035 basegfx::fTools::less(aScale
.getY(), 0.0) ? -1.0 : 1.0);
3038 if(!basegfx::fTools::equalZero(fShearX
))
3040 aTransform
.shearX(tan(-atan(fShearX
)));
3041 aGeo
.nShearAngle
= FRound(atan(fShearX
) / F_PI18000
);
3045 if(!basegfx::fTools::equalZero(fRotate
))
3048 // fRotate is mathematically correct for linear transformations, so it's
3049 // the one to use for the geometry change
3050 aTransform
.rotate(fRotate
);
3053 // fRotate is mathematically correct, but aGeoStat.nRotationAngle is
3054 // mirrored -> mirror value here
3055 aGeo
.nRotationAngle
= NormAngle360(FRound(-fRotate
/ F_PI18000
));
3056 aGeo
.RecalcSinCos();
3059 if(!aTranslate
.equalZero())
3061 // #i39529# absolute positioning, so get current position (without control points (!))
3062 const basegfx::B2DRange
aCurrentRange(basegfx::utils::getRange(aNewPolyPolygon
));
3063 aTransform
.translate(aTranslate
.getX() - aCurrentRange
.getMinX(), aTranslate
.getY() - aCurrentRange
.getMinY());
3066 // transform polygon and trigger change
3067 aNewPolyPolygon
.transform(aTransform
);
3068 SetPathPoly(aNewPolyPolygon
);
3071 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */