/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
struct ImpSdrPathDragData : public SdrDragStatUserData
{
XPolygon aXP; // section of the original polygon bool bValid; // FALSE = too few points bool bClosed; // closed object?
sal_uInt16 nPoly; // number of the polygon in the PolyPolygon
sal_uInt16 nPnt; // number of point in the above polygon
sal_uInt16 nPointCount; // number of points of the polygon bool bBegPnt; // dragged point is first point of a Polyline bool bEndPnt; // dragged point is finishing point of a Polyline
sal_uInt16 nPrevPnt; // index of previous point
sal_uInt16 nNextPnt; // index of next point bool bPrevIsBegPnt; // previous point is first point of a Polyline bool bNextIsEndPnt; // next point is first point of a Polyline
sal_uInt16 nPrevPrevPnt; // index of point before previous point
sal_uInt16 nNextNextPnt; // index of point after next point bool bControl; // point is a control point bool bIsNextControl; // point is a control point after a support point bool bPrevIsControl; // if nPnt is a support point: a control point comes before bool bNextIsControl; // if nPnt is a support point: a control point comes after
sal_uInt16 nPrevPrevPnt0;
sal_uInt16 nPrevPnt0;
sal_uInt16 nPnt0;
sal_uInt16 nNextPnt0;
sal_uInt16 nNextNextPnt0; bool bEliminate; // delete point? (is set by MovDrag)
maMove = maOrig;
bValid = true;
} else
{
sal_uInt16 nPntMax = 0; // maximum index
bValid=false;
bClosed=rPO.IsClosed(); // closed object?
nPoly=static_cast<sal_uInt16>(rHdl.GetPolyNum()); // number of the polygon in the PolyPolygon
nPnt=static_cast<sal_uInt16>(rHdl.GetPointNum()); // number of points in the above polygon const XPolygon aTmpXP(rPO.GetPathPoly().getB2DPolygon(nPoly));
nPointCount=aTmpXP.GetPointCount(); // number of point of the polygon if (nPointCount==0 || (bClosed && nPointCount==1)) return; // minimum of 1 points for Lines, minimum of 2 points for Polygon
nPntMax=nPointCount-1; // maximum index
bBegPnt=!bClosed && nPnt==0; // dragged point is first point of a Polyline
bEndPnt=!bClosed && nPnt==nPntMax; // dragged point is finishing point of a Polyline if (bClosed && nPointCount<=3) { // if polygon is only a line
bBegPnt=(nPointCount<3) || nPnt==0;
bEndPnt=(nPointCount<3) || nPnt==nPntMax-1;
}
nPrevPnt=nPnt; // index of previous point
nNextPnt=nPnt; // index of next point if (!bBegPnt) nPrevPnt=GetPrevPnt(nPnt,nPntMax,bClosed); if (!bEndPnt) nNextPnt=GetNextPnt(nPnt,nPntMax,bClosed);
bPrevIsBegPnt=bBegPnt || (!bClosed && nPrevPnt==0);
bNextIsEndPnt=bEndPnt || (!bClosed && nNextPnt==nPntMax);
nPrevPrevPnt=nPnt; // index of point before previous point
nNextNextPnt=nPnt; // index of point after next point if (!bPrevIsBegPnt) nPrevPrevPnt=GetPrevPnt(nPrevPnt,nPntMax,bClosed); if (!bNextIsEndPnt) nNextNextPnt=GetNextPnt(nNextPnt,nPntMax,bClosed);
bControl=rHdl.IsPlusHdl(); // point is a control point
bIsNextControl=false; // point is a control point after a support point
bPrevIsControl=false; // if nPnt is a support point: a control point comes before
bNextIsControl=false; // if nPnt is a support point: a control point comes after if (bControl) {
bIsNextControl=!aTmpXP.IsControl(nPrevPnt);
} else {
bPrevIsControl=!bBegPnt && !bPrevIsBegPnt && aTmpXP.GetFlags(nPrevPnt)==PolyFlags::Control;
bNextIsControl=!bEndPnt && !bNextIsEndPnt && aTmpXP.GetFlags(nNextPnt)==PolyFlags::Control;
}
nPrevPrevPnt0=nPrevPrevPnt;
nPrevPnt0 =nPrevPnt;
nPnt0 =nPnt;
nNextPnt0 =nNextPnt;
nNextNextPnt0=nNextNextPnt;
nPrevPrevPnt=0;
nPrevPnt=1;
nPnt=2;
nNextPnt=3;
nNextNextPnt=4;
bEliminate=false;
ResetPoly(rPO);
bValid=true;
}
}
struct ImpPathCreateUser : public SdrDragStatUserData
{
Point aBezControl0;
Point aBezStart;
Point aBezCtrl1;
Point aBezCtrl2;
Point aBezEnd;
Point aCircStart;
Point aCircEnd;
Point aCircCenter;
Point aLineStart;
Point aLineEnd;
Point aRectP1;
Point aRectP2;
Point aRectP3;
tools::Long nCircRadius;
Degree100 nCircStAngle;
Degree100 nCircRelAngle; bool bBezier; bool bBezHasCtrl0; bool bCircle; bool bAngleSnap; bool bLine; bool bLine90; bool bRect; bool bMixedCreate;
sal_uInt16 nBezierStartPoint;
SdrObjKind eStartKind;
SdrObjKind eCurrentKind;
// move point itself
rMove[nPointIndex] = rOrig[nPointIndex] + aDelta;
// when point is first and poly closed, move close point, too. if(nPointCount > 0 && !nPointIndex && bClosed)
{
rMove[nPointCount - 1] = rOrig[nPointCount - 1] + aDelta;
// when moving the last point it may be necessary to move the // control point in front of this one, too. if(nPointCount > 1 && rOrig.IsControl(nPointCount - 2))
rMove[nPointCount - 2] = rOrig[nPointCount - 2] + aDelta;
}
// is a control point before this? if(nPointIndex > 0 && rOrig.IsControl(nPointIndex - 1))
{ // Yes, move it, too
rMove[nPointIndex - 1] = rOrig[nPointIndex - 1] + aDelta;
}
// is a control point after this? if(nPointIndex + 1 < nPointCount && rOrig.IsControl(nPointIndex + 1))
{ // Yes, move it, too
rMove[nPointIndex + 1] = rOrig[nPointIndex + 1] + aDelta;
}
}
}
} else
{
mpSdrPathDragData->ResetPoly(mrSdrPathObject);
// copy certain data locally to use less code and have faster access times bool bClosed =mpSdrPathDragData->bClosed ; // closed object?
sal_uInt16 nPnt =mpSdrPathDragData->nPnt ; // number of point in the above polygon bool bBegPnt =mpSdrPathDragData->bBegPnt ; // dragged point is first point of a Polyline bool bEndPnt =mpSdrPathDragData->bEndPnt ; // dragged point is last point of a Polyline
sal_uInt16 nPrevPnt =mpSdrPathDragData->nPrevPnt ; // index of previous point
sal_uInt16 nNextPnt =mpSdrPathDragData->nNextPnt ; // index of next point bool bPrevIsBegPnt =mpSdrPathDragData->bPrevIsBegPnt ; // previous point is first point of a Polyline bool bNextIsEndPnt =mpSdrPathDragData->bNextIsEndPnt ; // next point is last point of a Polyline
sal_uInt16 nPrevPrevPnt =mpSdrPathDragData->nPrevPrevPnt ; // index of the point before the previous point
sal_uInt16 nNextNextPnt =mpSdrPathDragData->nNextNextPnt ; // index if the point after the next point bool bControl =mpSdrPathDragData->bControl ; // point is a control point bool bIsNextControl =mpSdrPathDragData->bIsNextControl; // point is a control point after a support point bool bPrevIsControl =mpSdrPathDragData->bPrevIsControl; // if nPnt is a support point: there's a control point before bool bNextIsControl =mpSdrPathDragData->bNextIsControl; // if nPnt is a support point: there's a control point after
// Ortho for lines/polygons: keep angle if (!bControl && rDrag.GetView()!=nullptr && rDrag.GetView()->IsOrtho()) { bool bBigOrtho=rDrag.GetView()->IsBigOrtho();
Point aPos(rDrag.GetNow()); // current position
Point aPnt(mpSdrPathDragData->aXP[nPnt]); // the dragged point
sal_uInt16 nPnt1=0xFFFF,nPnt2=0xFFFF; // its neighboring points
Point aNewPos1,aNewPos2; // new alternative for aPos bool bPnt1 = false, bPnt2 = false; // are these valid alternatives? if (!bClosed && mpSdrPathDragData->nPointCount>=2) { // minimum of 2 points for lines if (!bBegPnt) nPnt1=nPrevPnt; if (!bEndPnt) nPnt2=nNextPnt;
} if (bClosed && mpSdrPathDragData->nPointCount>=3) { // minimum of 3 points for polygon
nPnt1=nPrevPnt;
nPnt2=nNextPnt;
} if (nPnt1!=0xFFFF && !bPrevIsControl) {
Point aPnt1=mpSdrPathDragData->aXP[nPnt1];
tools::Long ndx0=aPnt.X()-aPnt1.X();
tools::Long ndy0=aPnt.Y()-aPnt1.Y(); bool bHLin=ndy0==0; bool bVLin=ndx0==0; if (!bHLin || !bVLin) {
tools::Long ndx=aPos.X()-aPnt1.X();
tools::Long ndy=aPos.Y()-aPnt1.Y();
bPnt1=true; double nXFact=0; if (!bVLin) nXFact=static_cast<double>(ndx)/static_cast<double>(ndx0); double nYFact=0; if (!bHLin) nYFact=static_cast<double>(ndy)/static_cast<double>(ndy0); bool bHor=bHLin || (!bVLin && (nXFact>nYFact) ==bBigOrtho); bool bVer=bVLin || (!bHLin && (nXFact<=nYFact)==bBigOrtho); if (bHor) ndy=tools::Long(ndy0*nXFact); if (bVer) ndx=tools::Long(ndx0*nYFact);
aNewPos1=aPnt1;
aNewPos1.AdjustX(ndx );
aNewPos1.AdjustY(ndy );
}
} if (nPnt2!=0xFFFF && !bNextIsControl) {
Point aPnt2=mpSdrPathDragData->aXP[nPnt2];
tools::Long ndx0=aPnt.X()-aPnt2.X();
tools::Long ndy0=aPnt.Y()-aPnt2.Y(); bool bHLin=ndy0==0; bool bVLin=ndx0==0; if (!bHLin || !bVLin) {
tools::Long ndx=aPos.X()-aPnt2.X();
tools::Long ndy=aPos.Y()-aPnt2.Y();
bPnt2=true; double nXFact=0; if (!bVLin) nXFact=static_cast<double>(ndx)/static_cast<double>(ndx0); double nYFact=0; if (!bHLin) nYFact=static_cast<double>(ndy)/static_cast<double>(ndy0); bool bHor=bHLin || (!bVLin && (nXFact>nYFact) ==bBigOrtho); bool bVer=bVLin || (!bHLin && (nXFact<=nYFact)==bBigOrtho); if (bHor) ndy=tools::Long(ndy0*nXFact); if (bVer) ndx=tools::Long(ndx0*nYFact);
aNewPos2=aPnt2;
aNewPos2.AdjustX(ndx );
aNewPos2.AdjustY(ndy );
}
} if (bPnt1 && bPnt2) { // both alternatives exist (and compete)
BigInt nX1(aNewPos1.X()-aPos.X()); nX1*=nX1;
BigInt nY1(aNewPos1.Y()-aPos.Y()); nY1*=nY1;
BigInt nX2(aNewPos2.X()-aPos.X()); nX2*=nX2;
BigInt nY2(aNewPos2.Y()-aPos.Y()); nY2*=nY2;
nX1+=nY1; // correction distance to square
nX2+=nY2; // correction distance to square // let the alternative that allows fewer correction win if (nX1<nX2) bPnt2=false; else bPnt1=false;
} if (bPnt1) rDrag.SetNow(aNewPos1); if (bPnt2) rDrag.SetNow(aNewPos2);
}
rDrag.SetActionRect(tools::Rectangle(rDrag.GetNow(),rDrag.GetNow()));
// specially for IBM: Eliminate points if both adjoining lines form near 180 degrees angle anyway if (!bControl && rDrag.GetView()!=nullptr && rDrag.GetView()->IsEliminatePolyPoints() &&
!bBegPnt && !bEndPnt && !bPrevIsControl && !bNextIsControl)
{
Point aPt(mpSdrPathDragData->aXP[nNextPnt]);
aPt-=rDrag.GetNow();
Degree100 nAngle1=GetAngle(aPt);
aPt=rDrag.GetNow();
aPt-=mpSdrPathDragData->aXP[nPrevPnt];
Degree100 nAngle2=GetAngle(aPt);
Degree100 nDiff=nAngle1-nAngle2;
nDiff=abs(nDiff);
mpSdrPathDragData->bEliminate=nDiff<=rDrag.GetView()->GetEliminatePolyPointLimitAngle(); if (mpSdrPathDragData->bEliminate) { // adapt position, Smooth is true for the ends
aPt=mpSdrPathDragData->aXP[nNextPnt];
aPt+=mpSdrPathDragData->aXP[nPrevPnt];
aPt/=2;
rDrag.SetNow(aPt);
}
}
// we dragged by this distance
Point aDiff(rDrag.GetNow()); aDiff-=mpSdrPathDragData->aXP[nPnt];
/* There are 8 possible cases: X 1. A control point neither on the left nor on the right. o--X--o 2. There are control points on the left and the right, we are dragging a support point. o--X 3. There is a control point on the left, we are dragging a support point. X--o 4. There is a control point on the right, we are dragging a support point. x--O--o 5. There are control points on the left and the right, we are dragging the left one. x--O 6. There is a control point on the left, we are dragging it. o--O--x 7. There are control points on the left and the right, we are dragging the right one. O--x 8. There is a control point on the right, we are dragging it. Note: modifying a line (not a curve!) might create a curve on the other end of the line if Smooth is set there (with control points aligned to line).
*/
mpSdrPathDragData->aXP[nPnt]+=aDiff;
// now check symmetric plus handles if (bControl) { // cases 5,6,7,8
sal_uInt16 nSt; // the associated support point
sal_uInt16 nFix; // the opposing control point if (bIsNextControl) { // if the next one is a control point, the on before has to be a support point
nSt=nPrevPnt;
nFix=nPrevPrevPnt;
} else {
nSt=nNextPnt;
nFix=nNextNextPnt;
} if (mpSdrPathDragData->aXP.IsSmooth(nSt)) {
mpSdrPathDragData->aXP.CalcSmoothJoin(nSt,nPnt,nFix);
}
}
if (!bControl) { // Cases 1,2,3,4. In case 1, nothing happens; in cases 3 and 4, there is more following below. // move both control points if (bPrevIsControl) mpSdrPathDragData->aXP[nPrevPnt]+=aDiff; if (bNextIsControl) mpSdrPathDragData->aXP[nNextPnt]+=aDiff; // align control point to line, if appropriate if (mpSdrPathDragData->aXP.IsSmooth(nPnt)) { if (bPrevIsControl && !bNextIsControl && !bEndPnt) { // case 3
mpSdrPathDragData->aXP.CalcSmoothJoin(nPnt,nNextPnt,nPrevPnt);
} if (bNextIsControl && !bPrevIsControl && !bBegPnt) { // case 4
mpSdrPathDragData->aXP.CalcSmoothJoin(nPnt,nPrevPnt,nNextPnt);
}
} // Now check the other ends of the line (nPnt+-1). If there is a // curve (IsControl(nPnt+-2)) with SmoothJoin (nPnt+-1), the // associated control point (nPnt+-2) has to be adapted. if (!bBegPnt && !bPrevIsControl && !bPrevIsBegPnt && mpSdrPathDragData->aXP.IsSmooth(nPrevPnt)) { if (mpSdrPathDragData->aXP.IsControl(nPrevPrevPnt)) {
mpSdrPathDragData->aXP.CalcSmoothJoin(nPrevPnt,nPnt,nPrevPrevPnt);
}
} if (!bEndPnt && !bNextIsControl && !bNextIsEndPnt && mpSdrPathDragData->aXP.IsSmooth(nNextPnt)) { if (mpSdrPathDragData->aXP.IsControl(nNextNextPnt)) {
mpSdrPathDragData->aXP.CalcSmoothJoin(nNextPnt,nPnt,nNextNextPnt);
}
}
}
}
returntrue;
}
bool ImpPathForDragAndCreate::endPathDrag(SdrDragStat const & rDrag)
{
Point aLinePt1;
Point aLinePt2; bool bLineGlueMirror(SdrObjKind::Line == meObjectKind); if (bLineGlueMirror) {
XPolygon& rXP=aPathPolygon[0];
aLinePt1=rXP[0];
aLinePt2=rXP[1];
}
if(!mpSdrPathDragData || !mpSdrPathDragData->bValid)
{
OSL_FAIL("ImpPathForDragAndCreate::MovDrag(): ImpSdrPathDragData is invalid."); returnfalse;
}
// reference the polygon
XPolygon& rXP=aPathPolygon[static_cast<sal_uInt16>(pHdl->GetPolyNum())];
// the 5 points that might have changed if (!mpSdrPathDragData->bPrevIsBegPnt) rXP[mpSdrPathDragData->nPrevPrevPnt0]=mpSdrPathDragData->aXP[mpSdrPathDragData->nPrevPrevPnt]; if (!mpSdrPathDragData->bNextIsEndPnt) rXP[mpSdrPathDragData->nNextNextPnt0]=mpSdrPathDragData->aXP[mpSdrPathDragData->nNextNextPnt]; if (!mpSdrPathDragData->bBegPnt) rXP[mpSdrPathDragData->nPrevPnt0] =mpSdrPathDragData->aXP[mpSdrPathDragData->nPrevPnt]; if (!mpSdrPathDragData->bEndPnt) rXP[mpSdrPathDragData->nNextPnt0] =mpSdrPathDragData->aXP[mpSdrPathDragData->nNextPnt];
rXP[mpSdrPathDragData->nPnt0] =mpSdrPathDragData->aXP[mpSdrPathDragData->nPnt];
// for closed objects: last point has to be equal to first point if (mpSdrPathDragData->bClosed) rXP[rXP.GetPointCount()-1]=rXP[0];
if (mpSdrPathDragData->bEliminate)
{
basegfx::B2DPolyPolygon aTempPolyPolygon(aPathPolygon.getB2DPolyPolygon());
sal_uInt32 nPoly,nPnt;
// adapt angle for text beneath a simple line if (bLineGlueMirror)
{
Point aLinePt1_(aPathPolygon[0][0]);
Point aLinePt2_(aPathPolygon[0][1]); bool bXMirr=(aLinePt1_.X()>aLinePt2_.X())!=(aLinePt1.X()>aLinePt2.X()); bool bYMirr=(aLinePt1_.Y()>aLinePt2_.Y())!=(aLinePt1.Y()>aLinePt2.Y()); if (bXMirr || bYMirr) {
Point aRef1(mrSdrPathObject.GetSnapRect().Center()); if (bXMirr) {
Point aRef2(aRef1);
aRef2.AdjustY( 1 );
mrSdrPathObject.NbcMirrorGluePoints(aRef1,aRef2);
} if (bYMirr) {
Point aRef2(aRef1);
aRef2.AdjustX( 1 );
mrSdrPathObject.NbcMirrorGluePoints(aRef1,aRef2);
}
}
}
}
aStr += ")";
} elseif(!pHdl)
{ // #i103058# fallback when no model and/or Handle, both needed // for else-path
aStr = mrSdrPathObject.ImpGetDescriptionStr(STR_DragPathObj);
} else
{ // #i103058# standard for modification; model and handle needed
ImpSdrPathDragData* pDragData = mpSdrPathDragData.get();
if(!pDragData)
{ // getSpecialDragComment is also used from create, so fallback to GetUser() // when mpSdrPathDragData is not set
pDragData = static_cast<ImpSdrPathDragData*>(rDrag.GetUser());
}
if(!pDragData)
{
OSL_FAIL("ImpPathForDragAndCreate::MovDrag(): ImpSdrPathDragData is invalid."); return OUString();
}
if(!pDragData->IsMultiPointDrag() && pDragData->bEliminate)
{ // point of ...
aStr = mrSdrPathObject.ImpGetDescriptionStr(STR_ViewMarkedPoint);
// UNICODE: delete point of ...
aStr2 = aStr2.replaceFirst("%1", aStr);
return aStr2;
}
// dx=0.00 dy=0.00 -- both sides bezier // dx=0.00 dy=0.00 l=0.00 0.00\302\260 -- one bezier/lever on one side, a start, or an ending // dx=0.00 dy=0.00 l=0.00 0.00\302\260 / l=0.00 0.00\302\260 -- in between
Point aBeg(rDrag.GetStart());
Point aNow(rDrag.GetNow());
if(mpSdrPathDragData->IsMultiPointDrag())
{
aRetval.Insert(mpSdrPathDragData->maMove);
} else
{ const XPolygon& rXP=aPathPolygon[static_cast<sal_uInt16>(rDrag.GetHdl()->GetPolyNum())]; if (rXP.GetPointCount()<=2) {
XPolygon aXPoly(rXP);
aXPoly[static_cast<sal_uInt16>(rDrag.GetHdl()->GetPointNum())]=rDrag.GetNow();
aRetval.Insert(std::move(aXPoly)); return aRetval.getB2DPolyPolygon();
} // copy certain data locally to use less code and have faster access times bool bClosed =mpSdrPathDragData->bClosed ; // closed object?
sal_uInt16 nPointCount = mpSdrPathDragData->nPointCount; // number of points
sal_uInt16 nPnt =mpSdrPathDragData->nPnt ; // number of points in the polygon bool bBegPnt =mpSdrPathDragData->bBegPnt ; // dragged point is the first point of a Polyline bool bEndPnt =mpSdrPathDragData->bEndPnt ; // dragged point is the last point of a Polyline
sal_uInt16 nPrevPnt =mpSdrPathDragData->nPrevPnt ; // index of the previous point
sal_uInt16 nNextPnt =mpSdrPathDragData->nNextPnt ; // index of the next point bool bPrevIsBegPnt =mpSdrPathDragData->bPrevIsBegPnt ; // previous point is first point of a Polyline bool bNextIsEndPnt =mpSdrPathDragData->bNextIsEndPnt ; // next point is last point of a Polyline
sal_uInt16 nPrevPrevPnt =mpSdrPathDragData->nPrevPrevPnt ; // index of the point before the previous point
sal_uInt16 nNextNextPnt =mpSdrPathDragData->nNextNextPnt ; // index of the point after the last point bool bControl =mpSdrPathDragData->bControl ; // point is a control point bool bIsNextControl =mpSdrPathDragData->bIsNextControl; //point is a control point after a support point bool bPrevIsControl =mpSdrPathDragData->bPrevIsControl; // if nPnt is a support point: there's a control point before bool bNextIsControl =mpSdrPathDragData->bNextIsControl; // if nPnt is a support point: there's a control point after
XPolygon aXPoly(mpSdrPathDragData->aXP);
XPolygon aLine1(2);
XPolygon aLine2(2);
XPolygon aLine3(2);
XPolygon aLine4(2); if (bControl) {
aLine1[1]=mpSdrPathDragData->aXP[nPnt]; if (bIsNextControl) { // is this a control point after the support point?
aLine1[0]=mpSdrPathDragData->aXP[nPrevPnt];
aLine2[0]=mpSdrPathDragData->aXP[nNextNextPnt];
aLine2[1]=mpSdrPathDragData->aXP[nNextPnt]; if (mpSdrPathDragData->aXP.IsSmooth(nPrevPnt) && !bPrevIsBegPnt && mpSdrPathDragData->aXP.IsControl(nPrevPrevPnt)) {
aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-1],PolyFlags::Control);
aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-2],PolyFlags::Normal); // leverage lines for the opposing curve segment
aLine3[0]=mpSdrPathDragData->aXP[nPrevPnt];
aLine3[1]=mpSdrPathDragData->aXP[nPrevPrevPnt];
aLine4[0]=rXP[mpSdrPathDragData->nPrevPrevPnt0-2];
aLine4[1]=rXP[mpSdrPathDragData->nPrevPrevPnt0-1];
} else {
aXPoly.Remove(0,1);
}
} else { // else this is a control point before a support point
aLine1[0]=mpSdrPathDragData->aXP[nNextPnt];
aLine2[0]=mpSdrPathDragData->aXP[nPrevPrevPnt];
aLine2[1]=mpSdrPathDragData->aXP[nPrevPnt]; if (mpSdrPathDragData->aXP.IsSmooth(nNextPnt) && !bNextIsEndPnt && mpSdrPathDragData->aXP.IsControl(nNextNextPnt)) {
aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+1],PolyFlags::Control);
aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+2],PolyFlags::Normal); // leverage lines for the opposing curve segment
aLine3[0]=mpSdrPathDragData->aXP[nNextPnt];
aLine3[1]=mpSdrPathDragData->aXP[nNextNextPnt];
aLine4[0]=rXP[mpSdrPathDragData->nNextNextPnt0+2];
aLine4[1]=rXP[mpSdrPathDragData->nNextNextPnt0+1];
} else {
aXPoly.Remove(aXPoly.GetPointCount()-1,1);
}
}
} else { // else is not a control point if (mpSdrPathDragData->bEliminate) {
aXPoly.Remove(2,1);
} if (bPrevIsControl) aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-1],PolyFlags::Normal); elseif (!bBegPnt && !bPrevIsBegPnt && mpSdrPathDragData->aXP.IsControl(nPrevPrevPnt)) {
aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-1],PolyFlags::Control);
aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-2],PolyFlags::Normal);
} else {
aXPoly.Remove(0,1); if (bBegPnt) aXPoly.Remove(0,1);
} if (bNextIsControl) aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+1],PolyFlags::Normal); elseif (!bEndPnt && !bNextIsEndPnt && mpSdrPathDragData->aXP.IsControl(nNextNextPnt)) {
aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+1],PolyFlags::Control);
aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+2],PolyFlags::Normal);
} else {
aXPoly.Remove(aXPoly.GetPointCount()-1,1); if (bEndPnt) aXPoly.Remove(aXPoly.GetPointCount()-1,1);
} if (bClosed) { // "pear problem": 2 lines, 1 curve, everything smoothed, a point between both lines is dragged if (aXPoly.GetPointCount()>nPointCount && aXPoly.IsControl(1)) {
sal_uInt16 a=aXPoly.GetPointCount();
aXPoly[a-2]=aXPoly[2]; aXPoly.SetFlags(a-2,aXPoly.GetFlags(2));
aXPoly[a-1]=aXPoly[3]; aXPoly.SetFlags(a-1,aXPoly.GetFlags(3));
aXPoly.Remove(0,3);
}
}
}
aRetval.Insert(std::move(aXPoly)); if (aLine1.GetPointCount()>1) aRetval.Insert(std::move(aLine1)); if (aLine2.GetPointCount()>1) aRetval.Insert(std::move(aLine2)); if (aLine3.GetPointCount()>1) aRetval.Insert(std::move(aLine3)); if (aLine4.GetPointCount()>1) aRetval.Insert(std::move(aLine4));
}
bool ImpPathForDragAndCreate::MovCreate(SdrDragStat& rStat)
{
ImpPathCreateUser* pU=static_cast<ImpPathCreateUser*>(rStat.GetUser());
SdrView* pView=rStat.GetView();
XPolygon& rXPoly=aPathPolygon[aPathPolygon.Count()-1]; if (pView!=nullptr && pView->IsCreateMode()) { // switch to different CreateTool, if appropriate
SdrObjKind nIdent;
SdrInventor nInvent;
pView->TakeCurrentObj(nIdent,nInvent); if (nInvent==SdrInventor::Default && pU->eCurrentKind != nIdent) {
SdrObjKind eNewKind = nIdent; switch (eNewKind) { case SdrObjKind::CircleArc: case SdrObjKind::CircleOrEllipse: case SdrObjKind::CircleCut: case SdrObjKind::CircleSection:
eNewKind=SdrObjKind::CircleArc;
[[fallthrough]]; case SdrObjKind::Rectangle: case SdrObjKind::Line: case SdrObjKind::PolyLine: case SdrObjKind::Polygon: case SdrObjKind::PathLine: case SdrObjKind::PathFill: case SdrObjKind::FreehandLine: case SdrObjKind::FreehandFill:
{
pU->eCurrentKind=eNewKind;
pU->bMixedCreate=true;
pU->nBezierStartPoint=rXPoly.GetPointCount(); if (pU->nBezierStartPoint>0) pU->nBezierStartPoint--;
} break; default: break;
} // switch
}
}
sal_uInt16 nCurrentPoint=rXPoly.GetPointCount(); if (aPathPolygon.Count()>1 && rStat.IsMouseDown() && nCurrentPoint<2) {
rXPoly[0]=rStat.GetPos0();
rXPoly[1]=rStat.GetNow();
nCurrentPoint=2;
} if (nCurrentPoint==0) {
rXPoly[0]=rStat.GetPos0();
} else nCurrentPoint--; bool bFreeHand=IsFreeHand(pU->eCurrentKind);
rStat.SetNoSnap(bFreeHand);
rStat.SetOrtho8Possible(pU->eCurrentKind!=SdrObjKind::CircleArc && pU->eCurrentKind!=SdrObjKind::Rectangle && (!pU->bMixedCreate || pU->eCurrentKind!=SdrObjKind::Line));
rXPoly[nCurrentPoint]=rStat.GetNow(); if (!pU->bMixedCreate && pU->eStartKind==SdrObjKind::Line && rXPoly.GetPointCount()>=1) {
Point aPt(rStat.GetStart()); if (pView!=nullptr && pView->IsCreate1stPointAsCenter()) {
aPt+=aPt;
aPt-=rStat.GetNow();
}
rXPoly[0]=aPt;
}
OutputDevice* pOut=pView==nullptr ? nullptr : pView->GetFirstOutputDevice(); if (bFreeHand) { if (pU->nBezierStartPoint>nCurrentPoint) pU->nBezierStartPoint=nCurrentPoint; if (rStat.IsMouseDown() && nCurrentPoint>0) { // don't allow two consecutive points to occupy too similar positions
tools::Long nMinDist=1; if (pView!=nullptr) nMinDist=pView->GetFreeHandMinDistPix(); if (pOut!=nullptr) nMinDist=pOut->PixelToLogic(Size(nMinDist,0)).Width(); if (nMinDist<1) nMinDist=1;
Point aPt0(rXPoly[nCurrentPoint-1]);
Point aPt1(rStat.GetNow());
tools::Long dx=aPt0.X()-aPt1.X(); if (dx<0) dx=-dx;
tools::Long dy=aPt0.Y()-aPt1.Y(); if (dy<0) dy=-dy; if (dx<nMinDist && dy<nMinDist) returnfalse;
// TODO: the following is copied from EndCreate (with a few smaller modifications) // and should be combined into a method with the code there.
if (nCurrentPoint-pU->nBezierStartPoint>=3 && ((nCurrentPoint-pU->nBezierStartPoint)%3)==0) {
rXPoly.PointsToBezier(nCurrentPoint-3);
rXPoly.SetFlags(nCurrentPoint-1,PolyFlags::Control);
rXPoly.SetFlags(nCurrentPoint-2,PolyFlags::Control);
if (!pU->bMixedCreate && IsFreeHand(pU->eStartKind)) { if (rStat.GetPointCount()>=2) eCmd=SdrCreateCmd::ForceEnd;
bRet=eCmd==SdrCreateCmd::ForceEnd; if (bRet) {
mbCreating=false;
rStat.SetUser(nullptr);
} return bRet;
} if (eCmd==SdrCreateCmd::NextPoint || eCmd==SdrCreateCmd::NextObject) { // don't allow two consecutive points to occupy the same position if (nCurrentPoint==0 || rStat.GetNow()!=rXPoly[nCurrentPoint-1]) { if (bIncomp) { if (pU->nBezierStartPoint>nCurrentPoint) pU->nBezierStartPoint=nCurrentPoint; if (IsBezier(pU->eCurrentKind) && nCurrentPoint-pU->nBezierStartPoint>=3 && ((nCurrentPoint-pU->nBezierStartPoint)%3)==0) {
rXPoly.PointsToBezier(nCurrentPoint-3);
rXPoly.SetFlags(nCurrentPoint-1,PolyFlags::Control);
rXPoly.SetFlags(nCurrentPoint-2,PolyFlags::Control);
if (nCurrentPoint>=6 && rXPoly.IsControl(nCurrentPoint-4)) {
rXPoly.CalcTangent(nCurrentPoint-3,nCurrentPoint-4,nCurrentPoint-2);
rXPoly.SetFlags(nCurrentPoint-3,PolyFlags::Smooth);
}
}
} else { if (nCurrentPoint==1 && IsBezier(pU->eCurrentKind) && !pU->bBezHasCtrl0) {
pU->aBezControl0=rStat.GetNow();
pU->bBezHasCtrl0=true;
nCurrentPoint--;
} if (pU->IsFormFlag()) {
sal_uInt16 nPointCount0=rXPoly.GetPointCount();
rXPoly.Remove(nCurrentPoint-1,2); // remove last two points and replace by form
rXPoly.Insert(XPOLY_APPEND,pU->GetFormPoly());
sal_uInt16 nPointCount1=rXPoly.GetPointCount(); for (sal_uInt16 i=nPointCount0+1; i<nPointCount1-1; i++) { // to make BckAction work if (!rXPoly.IsControl(i)) rStat.NextPoint();
}
nCurrentPoint=rXPoly.GetPointCount()-1;
}
}
nCurrentPoint++;
rXPoly[nCurrentPoint]=rStat.GetNow();
} if (eCmd==SdrCreateCmd::NextObject) { if (rXPoly.GetPointCount()>=2) {
pU->bBezHasCtrl0=false; // only a singular polygon may be opened, so close this
rXPoly[nCurrentPoint]=rXPoly[0];
XPolygon aXP;
aXP[0]=rStat.GetNow();
aPathPolygon.Insert(std::move(aXP));
}
}
}
sal_uInt16 nPolyCount=aPathPolygon.Count(); if (nPolyCount!=0) { // delete last point, if necessary if (eCmd==SdrCreateCmd::ForceEnd) {
XPolygon& rXP=aPathPolygon[nPolyCount-1];
sal_uInt16 nPointCount=rXP.GetPointCount(); if (nPointCount>=2) { if (!rXP.IsControl(nPointCount-2)) { if (rXP[nPointCount-1]==rXP[nPointCount-2]) {
rXP.Remove(nPointCount-1,1);
}
} else { if (rXP[nPointCount-3]==rXP[nPointCount-2]) {
rXP.Remove(nPointCount-3,3);
}
}
}
} for (sal_uInt16 nPolyNum=nPolyCount; nPolyNum>0;) {
nPolyNum--;
XPolygon& rXP=aPathPolygon[nPolyNum];
sal_uInt16 nPointCount=rXP.GetPointCount(); // delete polygons with too few points if (nPolyNum<nPolyCount-1 || eCmd==SdrCreateCmd::ForceEnd) { if (nPointCount<2) aPathPolygon.Remove(nPolyNum);
}
}
}
pU->ResetFormFlags();
bRet=eCmd==SdrCreateCmd::ForceEnd; if (bRet) {
mbCreating=false;
rStat.SetUser(nullptr);
} return bRet;
}
bool ImpPathForDragAndCreate::BckCreate(SdrDragStat const & rStat)
{
ImpPathCreateUser* pU=static_cast<ImpPathCreateUser*>(rStat.GetUser()); if (aPathPolygon.Count()>0) {
XPolygon& rXPoly=aPathPolygon[aPathPolygon.Count()-1];
sal_uInt16 nCurrentPoint=rXPoly.GetPointCount(); if (nCurrentPoint>0) {
nCurrentPoint--; // make the last part of a bezier curve a line
rXPoly.Remove(nCurrentPoint,1); if (nCurrentPoint>=3 && rXPoly.IsControl(nCurrentPoint-1)) { // there should never be a bezier segment at the end, so this is just in case...
rXPoly.Remove(nCurrentPoint-1,1); if (rXPoly.IsControl(nCurrentPoint-2)) rXPoly.Remove(nCurrentPoint-2,1);
}
}
nCurrentPoint=rXPoly.GetPointCount(); if (nCurrentPoint>=4) { // no bezier segment at the end
nCurrentPoint--; if (rXPoly.IsControl(nCurrentPoint-1)) {
rXPoly.Remove(nCurrentPoint-1,1); if (rXPoly.IsControl(nCurrentPoint-2)) rXPoly.Remove(nCurrentPoint-2,1);
}
} if (rXPoly.GetPointCount()<2) {
aPathPolygon.Remove(aPathPolygon.Count()-1);
} if (aPathPolygon.Count()>0) {
XPolygon& rLocalXPoly=aPathPolygon[aPathPolygon.Count()-1];
sal_uInt16 nLocalCurrentPoint=rLocalXPoly.GetPointCount(); if (nLocalCurrentPoint>0) {
nLocalCurrentPoint--;
rLocalXPoly[nLocalCurrentPoint]=rStat.GetNow();
}
}
}
pU->ResetFormFlags(); return aPathPolygon.Count()!=0;
}
if(pU->IsFormFlag() && aNewPolygon.count() > 1)
{ // remove last segment and replace with current // do not forget to rescue the previous control point which will be lost when // the point it's associated with is removed const sal_uInt32 nChangeIndex(aNewPolygon.count() - 2); const basegfx::B2DPoint aSavedPrevCtrlPoint(aNewPolygon.getPrevControlPoint(nChangeIndex));
if(nChangeIndex < aNewPolygon.count())
{ // if really something was added, set the saved previous control point to the // point where it belongs
aNewPolygon.setPrevControlPoint(nChangeIndex, aSavedPrevCtrlPoint);
}
}
const ImpPathCreateUser* pU = static_cast<const ImpPathCreateUser*>(rDrag.GetUser());
if(pU && pU->bBezier && rDrag.IsMouseDown())
{ // no more XOR, no need for complicated helplines
basegfx::B2DPolygon aHelpline;
aHelpline.append(basegfx::B2DPoint(pU->aBezCtrl2.X(), pU->aBezCtrl2.Y()));
aHelpline.append(basegfx::B2DPoint(pU->aBezEnd.X(), pU->aBezEnd.Y()));
aRetval.append(aHelpline);
}
return aRetval;
}
PointerStyle ImpPathForDragAndCreate::GetCreatePointer() const
{ switch (meObjectKind) { case SdrObjKind::Line : return PointerStyle::DrawLine; case SdrObjKind::Polygon : return PointerStyle::DrawPolygon; case SdrObjKind::PolyLine : return PointerStyle::DrawPolygon; case SdrObjKind::PathLine: return PointerStyle::DrawBezier; case SdrObjKind::PathFill: return PointerStyle::DrawBezier; case SdrObjKind::FreehandLine: return PointerStyle::DrawFreehand; case SdrObjKind::FreehandFill: return PointerStyle::DrawFreehand; case SdrObjKind::PathPoly: return PointerStyle::DrawPolygon; case SdrObjKind::PathPolyLine: return PointerStyle::DrawPolygon; default: break;
} // switch return PointerStyle::Cross;
}
// for SdrTextObj, keep aRect up to date
setRectangle(tools::Rectangle::Normalize(aPoint0, aPoint1));
}
void SdrPathObj::ImpForceKind()
{ if (meKind==SdrObjKind::PathPolyLine) meKind=SdrObjKind::PolyLine; if (meKind==SdrObjKind::PathPoly) meKind=SdrObjKind::Polygon;
if(GetPathPoly().areControlPointsUsed())
{ switch (meKind)
{ case SdrObjKind::Line: meKind=SdrObjKind::PathLine; break; case SdrObjKind::PolyLine: meKind=SdrObjKind::PathLine; break; case SdrObjKind::Polygon: meKind=SdrObjKind::PathFill; break; default: break;
}
} else
{ switch (meKind)
{ case SdrObjKind::PathLine: meKind=SdrObjKind::PolyLine; break; case SdrObjKind::FreehandLine: meKind=SdrObjKind::PolyLine; break; case SdrObjKind::PathFill: meKind=SdrObjKind::Polygon; break; case SdrObjKind::FreehandFill: meKind=SdrObjKind::Polygon; break; default: break;
}
}
if (meKind==SdrObjKind::Line && !lcl_ImpIsLine(GetPathPoly())) meKind=SdrObjKind::PolyLine; if (meKind==SdrObjKind::PolyLine && lcl_ImpIsLine(GetPathPoly())) meKind=SdrObjKind::Line;
m_bClosedObj=IsClosed();
if (meKind==SdrObjKind::Line)
{
ImpForceLineAngle();
} else
{ // #i10659#, for polys with more than 2 points.
// Here i again need to fix something, because when Path-Polys are Copy-Pasted // between Apps with different measurements (e.g. 100TH_MM and TWIPS) there is // a scaling loop started from SdrExchangeView::Paste. In itself, this is not // wrong, but aRect is wrong here and not even updated by RecalcSnapRect(). If // this is the case, some size needs to be set here in aRect to avoid that the cycle // through Rect2Poly - Poly2Rect does something badly wrong since that cycle is // BASED on aRect. That cycle is triggered in SdrTextObj::NbcResize() which is called // from the local Resize() implementation.
// Basic problem is that the member aRect in SdrTextObj basically is a unrotated // text rectangle for the text object itself and methods at SdrTextObj do handle it // in that way. Many draw objects derived from SdrTextObj 'abuse' aRect as SnapRect // which is basically wrong. To make the SdrText methods which deal with aRect directly // work it is necessary to always keep aRect updated. This e.g. not done after a Clone() // command for SdrPathObj. Since adding this update mechanism with #101412# to // ImpForceLineAngle() for lines was very successful, i add it to where ImpForceLineAngle() // was called, once here below and once on a 2nd place below.
// #i10659# for SdrTextObj, keep aRect up to date if(GetPathPoly().count())
{
setRectangle(lcl_ImpGetBoundRect(GetPathPoly()));
}
}
// #i75974# adapt polygon state to object type. This may include a reinterpretation // of a closed geometry as open one, but with identical first and last point for(auto& rPolygon : maPathPolygon)
{ if(IsClosed() != rPolygon.isClosed())
{ // #i80213# really change polygon geometry; else e.g. the last point which // needs to be identical with the first one will be missing when opening // due to OBJ_PATH type if(rPolygon.isClosed())
{
basegfx::utils::openWithGeometryChange(rPolygon);
} else
{
basegfx::utils::closeWithGeometryChange(rPolygon);
}
}
}
}
void SdrPathObj::ImpSetClosed(bool bClose)
{ if(bClose)
{ switch (meKind)
{ case SdrObjKind::Line : meKind=SdrObjKind::Polygon; break; case SdrObjKind::PolyLine : meKind=SdrObjKind::Polygon; break; case SdrObjKind::PathLine: meKind=SdrObjKind::PathFill; break; case SdrObjKind::FreehandLine: meKind=SdrObjKind::FreehandFill; break; default: break;
}
m_bClosedObj = true;
} else
{ switch (meKind)
{ case SdrObjKind::Polygon : meKind=SdrObjKind::PolyLine; break; case SdrObjKind::PathFill: meKind=SdrObjKind::PathLine; break; case SdrObjKind::FreehandFill: meKind=SdrObjKind::FreehandLine; break; default: break;
}
void SdrPathObj::AddToHdlList(SdrHdlList& rHdlList) const
{ // keep old stuff to be able to keep old SdrHdl stuff, too const XPolyPolygon aOldPathPolygon(GetPathPoly());
sal_uInt16 nPolyCnt=aOldPathPolygon.Count(); bool bClosed=IsClosed();
sal_uInt16 nIdx=0;
for (sal_uInt16 i=0; i<nPolyCnt; i++) { const XPolygon& rXPoly=aOldPathPolygon.GetObject(i);
sal_uInt16 nPntCnt=rXPoly.GetPointCount(); if (bClosed && nPntCnt>1) nPntCnt--;
void SdrPathObj::AddToPlusHdlList(SdrHdlList& rHdlList, SdrHdl& rHdl) const
{ // keep old stuff to be able to keep old SdrHdl stuff, too const XPolyPolygon aOldPathPolygon(GetPathPoly());
sal_uInt16 nPnt = static_cast<sal_uInt16>(rHdl.GetPointNum());
sal_uInt16 nPolyNum = static_cast<sal_uInt16>(rHdl.GetPolyNum());
if (nPntMax<=0) return;
nPntMax--; if (nPnt>nPntMax) return;
// calculate the number of plus points
sal_uInt16 nCnt = 0; if (rXPoly.GetFlags(nPnt)!=PolyFlags::Control)
{ if (nPnt==0 && IsClosed())
nPnt=nPntMax; if (nPnt>0 && rXPoly.GetFlags(nPnt-1)==PolyFlags::Control)
nCnt++; if (nPnt==nPntMax && IsClosed())
nPnt=0; if (nPnt<nPntMax && rXPoly.GetFlags(nPnt+1)==PolyFlags::Control)
nCnt++;
}
// construct the plus points for (sal_uInt32 nPlusNum = 0; nPlusNum < nCnt; ++nPlusNum)
{
nPnt = static_cast<sal_uInt16>(rHdl.GetPointNum());
std::unique_ptr<SdrHdl> pHdl(new SdrHdlBezWgt(&rHdl));
pHdl->SetPolyNum(rHdl.GetPolyNum());
if (nPnt==0 && IsClosed())
nPnt=nPntMax; if (nPnt>0 && rXPoly.GetFlags(nPnt-1)==PolyFlags::Control && nPlusNum==0)
{
pHdl->SetPos(rXPoly[nPnt-1]);
pHdl->SetPointNum(nPnt-1);
} else
{ if (nPnt==nPntMax && IsClosed())
nPnt=0; if (nPnt<rXPoly.GetPointCount()-1 && rXPoly.GetFlags(nPnt+1)==PolyFlags::Control)
{
pHdl->SetPos(rXPoly[nPnt+1]);
pHdl->SetPointNum(nPnt+1);
}
}
// tdf#123321: Make sure that SdrPathObj (e.g. line) has big enough extent for // visibility. This is realised by ensuring GetLogicRect() is the same as // GetSnapRect() for the SdrPathObj. Other SdrTextObj objects like // SdrObjCustomShape will still use a different version of this method that // does not consider the rotation. Otherwise, the rotated SdrObjCustomShape // would become mistakenly larger after save and reload (tdf#91687). // The invocation of the GetLogicRect() method that caused tdf#123321 was in // PlcDrawObj::WritePlc(). const tools::Rectangle &SdrPathObj::GetLogicRect() const
{ return GetSnapRect();
}
// #i75974# Check for AutoClose feature. Moved here from ImpPathForDragAndCreate::EndCreate // to be able to use the type-changing ImpSetClosed method if(!IsClosedObj())
{
SdrView* pView = rStat.GetView();
if(pView && !pView->IsUseIncompatiblePathCreateInterface())
{
OutputDevice* pOut = pView->GetFirstOutputDevice();
if(aCandidate.count() > 2)
{ // check distance of first and last point const sal_Int32 nCloseDist(pOut->PixelToLogic(Size(pView->GetAutoCloseDistPix(), 0)).Width()); const basegfx::B2DVector aDistVector(aCandidate.getB2DPoint(aCandidate.count() - 1) - aCandidate.getB2DPoint(0));
if(aDistVector.getLength() <= static_cast<double>(nCloseDist))
{ // close it
ImpSetClosed(true);
}
}
}
}
}
}
if(basegfx::fTools::equal(fResizeX, 1.0) && basegfx::fTools::equal(fResizeY, 1.0))
{ // tdf#106792 avoid numerical unprecisions: If both scale factors are 1.0, do not // manipulate at all - that may change maGeo rapidly (and wrongly) in // SdrTextObj::NbcResize. Combined with the UNO API trying to not 'apply' // a rotation but to manipulate the existing one, this is fatal. So just // avoid this error as long as we have to deal with imprecise geometry // manipulations return;
}
Point SdrPathObj::GetSnapPoint(sal_uInt32 nSnapPnt) const
{
sal_uInt32 nPoly,nPnt; if(!PolyPolygonEditor::GetRelativePolyPoint(GetPathPoly(), nSnapPnt, nPoly, nPnt))
{
SAL_WARN("svx", "SdrPathObj::GetSnapPoint: Point nSnapPnt does not exist.");
}
if(meKind==SdrObjKind::Line)
{
ImpForceLineAngle();
} else
{ if(GetPathPoly().count())
{ // #i10659# for SdrTextObj, keep aRect up to date
setRectangle(lcl_ImpGetBoundRect(GetPathPoly()));
}
}
if(bSegmentSplit)
{ // rebuild original segment to get the split data
basegfx::B2DCubicBezier aBezierA, aBezierB; const basegfx::B2DCubicBezier aBezier(
aCandidate.getB2DPoint(nSmallestEdgeIndex),
aCandidate.getNextControlPoint(nSmallestEdgeIndex),
aCandidate.getPrevControlPoint(nNextIndex),
aCandidate.getB2DPoint(nNextIndex));
// split and insert hit point
aBezier.split(fSmallestCut, &aBezierA, &aBezierB);
aCandidate.insert(nSmallestEdgeIndex + 1, aTestPoint);
// since we inserted hit point and not split point, we need to add an offset // to the control points to get the C1 continuity we want to achieve const basegfx::B2DVector aOffset(aTestPoint - aBezierA.getEndPoint());
aCandidate.setNextControlPoint(nSmallestEdgeIndex, aBezierA.getControlPointA() + aOffset);
aCandidate.setPrevControlPoint(nSmallestEdgeIndex + 1, aBezierA.getControlPointB() + aOffset);
aCandidate.setNextControlPoint(nSmallestEdgeIndex + 1, aBezierB.getControlPointA() + aOffset);
aCandidate.setPrevControlPoint((nSmallestEdgeIndex + 2) % aCandidate.count(), aBezierB.getControlPointB() + aOffset);
} else
{
aCandidate.insert(nSmallestEdgeIndex + 1, aTestPoint);
}
if(nPointCount)
{ if(IsClosed())
{ // when closed, RipPoint means to open the polygon at the selected point. To // be able to do that, it is necessary to make the selected point the first one
basegfx::B2DPolygon aNewPolygon(basegfx::utils::makeStartPoint(aCandidate, nPnt));
SetPathPoly(basegfx::B2DPolyPolygon(aNewPolygon));
ToggleClosed();
// give back new position of old start point (historical reasons)
rNewPt0Index = (nPointCount - nPnt) % nPointCount;
} else
{ if(nPointCount >= 3 && nPnt != 0 && nPnt + 1 < nPointCount)
{ // split in two objects at point nPnt
basegfx::B2DPolygon aSplitPolyA(aCandidate, 0, nPnt + 1);
SetPathPoly(basegfx::B2DPolyPolygon(aSplitPolyA));
void SdrPathObj::ToggleClosed()
{
tools::Rectangle aBoundRect0; if(m_pUserCall != nullptr)
aBoundRect0 = GetLastBoundRect();
ImpSetClosed(!IsClosed()); // set new ObjKind
ImpForceKind(); // because we want Line -> Poly -> PolyLine instead of Line -> Poly -> Line
SetBoundAndSnapRectsDirty();
SetChanged();
BroadcastObjectChange();
SendUserCall(SdrUserCallType::Resize, aBoundRect0);
}
// transformation interface for StarOfficeAPI. This implements support for // homogeneous 3x3 matrices containing the transformation of the SdrObject. At the // moment it contains a shearX, rotation and translation, but for setting all linear // transforms like Scale, ShearX, ShearY, Rotate and Translate are supported.
// gets base transformation and rectangle of object. If it's an SdrPathObj it fills the PolyPolygon // with the base geometry and returns TRUE. Otherwise it returns FALSE. bool SdrPathObj::TRGetBaseGeometry(basegfx::B2DHomMatrix& rMatrix, basegfx::B2DPolyPolygon& rPolyPolygon) const
{ double fRotate(0.0); double fShearX(0.0);
basegfx::B2DTuple aScale(1.0, 1.0);
basegfx::B2DTuple aTranslate(0.0, 0.0);
if(SdrObjKind::Line == meKind)
{ // ignore shear and rotate, just use scale and translate
OSL_ENSURE(GetPathPoly().count() > 0 && GetPathPoly().getB2DPolygon(0).count() > 1, "OBJ_LINE with too few polygons (!)"); // #i72287# use polygon without control points for range calculation. Do not change rPolyPolygon // itself, else this method will no longer return the full polygon information (curve will // be lost) const basegfx::B2DRange aPolyRangeNoCurve(basegfx::utils::getRange(rPolyPolygon));
aScale = aPolyRangeNoCurve.getRange();
aTranslate = aPolyRangeNoCurve.getMinimum();
// define matrix for move polygon to zero point
aMoveToZeroMatrix.translate(-aTranslate.getX(), -aTranslate.getY());
} else
{ if(maGeo.m_nShearAngle || maGeo.m_nRotationAngle)
{ // get rotate and shear in drawingLayer notation
fRotate = toRadians(maGeo.m_nRotationAngle);
fShearX = toRadians(maGeo.m_nShearAngle);
// build mathematically correct (negative shear and rotate) object transform // containing shear and rotate to extract unsheared, unrotated polygon
basegfx::B2DHomMatrix aObjectMatrix;
aObjectMatrix.shearX(-maGeo.mfTanShearAngle);
aObjectMatrix.rotate(toRadians(36000_deg100 - maGeo.m_nRotationAngle));
// create inverse from it and back-transform polygon
basegfx::B2DHomMatrix aInvObjectMatrix(aObjectMatrix);
aInvObjectMatrix.invert();
rPolyPolygon.transform(aInvObjectMatrix);
// get range from unsheared, unrotated polygon and extract scale and translate. // transform topLeft from it back to transformed state to get original // topLeft (rotation center) // #i72287# use polygon without control points for range calculation. Do not change rPolyPolygon // itself, else this method will no longer return the full polygon information (curve will // be lost) const basegfx::B2DRange aCorrectedRangeNoCurve(basegfx::utils::getRange(rPolyPolygon));
aTranslate = aObjectMatrix * aCorrectedRangeNoCurve.getMinimum();
aScale = aCorrectedRangeNoCurve.getRange();
// define matrix for move polygon to zero point // #i112280# Added missing minus for Y-Translation
aMoveToZeroMatrix.translate(-aCorrectedRangeNoCurve.getMinX(), -aCorrectedRangeNoCurve.getMinY());
} else
{ // get scale and translate from unsheared, unrotated polygon // #i72287# use polygon without control points for range calculation. Do not change rPolyPolygon // itself, else this method will no longer return the full polygon information (curve will // be lost) const basegfx::B2DRange aPolyRangeNoCurve(basegfx::utils::getRange(rPolyPolygon)); if (!aPolyRangeNoCurve.isEmpty())
{
aScale = aPolyRangeNoCurve.getRange();
aTranslate = aPolyRangeNoCurve.getMinimum();
// define matrix for move polygon to zero point
aMoveToZeroMatrix.translate(-aTranslate.getX(), -aTranslate.getY());
}
}
}
// move polygon to zero point with pre-defined matrix
rPolyPolygon.transform(aMoveToZeroMatrix);
}
// Sets the base geometry of the object using infos contained in the homogeneous 3x3 matrix. // If it's an SdrPathObj it will use the provided geometry information. The Polygon has // to use (0,0) as upper left and will be scaled to the given size in the matrix. void SdrPathObj::TRSetBaseGeometry(const basegfx::B2DHomMatrix& rMatrix, const basegfx::B2DPolyPolygon& rPolyPolygon)
{ // break up matrix
basegfx::B2DTuple aScale;
basegfx::B2DTuple aTranslate; double fRotate, fShearX;
rMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
// #i75086# Old DrawingLayer (GeoStat and geometry) does not support holding negative scalings // in X and Y which equal a 180 degree rotation. Recognize it and react accordingly if(aScale.getX() < 0.0 && aScale.getY() < 0.0)
{
aScale.setX(fabs(aScale.getX()));
aScale.setY(fabs(aScale.getY()));
fRotate = fmod(fRotate + M_PI, 2 * M_PI);
}
if( getSdrModelFromSdrObject().IsWriter() )
{ // if anchor is used, make position relative to it if(GetAnchorPos().X() || GetAnchorPos().Y())
{
aTranslate += basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
}
}
// create transformation for polygon, set values at maGeo direct
basegfx::B2DHomMatrix aTransform;
// #i75086# // Given polygon is already scaled (for historical reasons), but not mirrored yet. // Thus, when scale is negative in X or Y, apply the needed mirroring accordingly. double fScaleX(aScale.getX() < 0.0 ? -1.0 : 1.0); double fScaleY(aScale.getY() < 0.0 ? -1.0 : 1.0);
// tdf#98565, tdf#98584. While loading a shape, svg:width and svg:height is used to scale // the polygon. But draw:transform might introduce additional scaling factors, which need to // be applied to the polygon too, so aScale cannot be ignored while loading. // I use "maSnapRect.IsEmpty() && GetPathPoly().count()" to detect this case. Any better // idea? The behavior in other cases is the same as it was before this fix. if (maSnapRect.IsEmpty() && GetPathPoly().count() && mbHandleScale)
{ // In case of a Writer document, the scaling factors were converted to twips. That is not // correct here, because width and height are already in the points coordinates and aScale // is no length but only a factor here. Convert back. if (getSdrModelFromSdrObject().IsWriter())
{
aScale.setX(o3tl::convert(aScale.getX(), o3tl::Length::twip, o3tl::Length::mm100));
aScale.setY(o3tl::convert(aScale.getY(), o3tl::Length::twip, o3tl::Length::mm100));
}
fScaleX *= fabs(aScale.getX());
fScaleY *= fabs(aScale.getY());
}
if (fScaleX != 1.0 || fScaleY != 1.0)
aTransform.scale(fScaleX, fScaleY);
if(!basegfx::fTools::equalZero(fRotate))
{ // #i78696# // fRotate is mathematically correct for linear transformations, so it's // the one to use for the geometry change
aTransform.rotate(fRotate);
// #i78696# // fRotate is mathematically correct, but aGeoStat.nRotationAngle is // mirrored -> mirror value here
maGeo.m_nRotationAngle = NormAngle36000(Degree100(basegfx::fround(-basegfx::rad2deg<100>(fRotate))));
maGeo.RecalcSinCos();
}
if(!aTranslate.equalZero())
{ // #i39529# absolute positioning, so get current position (without control points (!)) const basegfx::B2DRange aCurrentRange(basegfx::utils::getRange(aNewPolyPolygon));
aTransform.translate(aTranslate.getX() - aCurrentRange.getMinX(), aTranslate.getY() - aCurrentRange.getMinY());
}
// transform polygon and trigger change
aNewPolyPolygon.transform(aTransform);
SetPathPoly(aNewPolyPolygon);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.