From 267c6f2ac71f92999e969232431ba04678e7437e Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Mon, 15 Apr 2024 07:54:39 +0200 Subject: Adding upstream version 4:24.2.0. Signed-off-by: Daniel Baumann --- svx/source/svdraw/svdtrans.cxx | 882 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 882 insertions(+) create mode 100644 svx/source/svdraw/svdtrans.cxx (limited to 'svx/source/svdraw/svdtrans.cxx') diff --git a/svx/source/svdraw/svdtrans.cxx b/svx/source/svdraw/svdtrans.cxx new file mode 100644 index 0000000000..23c7495ad7 --- /dev/null +++ b/svx/source/svdraw/svdtrans.cxx @@ -0,0 +1,882 @@ +/* -*- 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 . + */ + + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +void MoveXPoly(XPolygon& rPoly, const Size& S) +{ + rPoly.Move(S.Width(),S.Height()); +} + +void ResizeRect(tools::Rectangle& rRect, const Point& rRef, const Fraction& rxFact, const Fraction& ryFact) +{ + Fraction aXFact(rxFact); + Fraction aYFact(ryFact); + + if (!aXFact.IsValid()) { + SAL_WARN( "svx.svdraw", "invalid fraction xFract, using Fraction(1,1)" ); + aXFact = Fraction(1,1); + tools::Long nWdt = rRect.Right() - rRect.Left(); + if (nWdt == 0) rRect.AdjustRight( 1 ); + } + rRect.SetLeft( rRef.X() + FRound( (rRect.Left() - rRef.X()) * double(aXFact) ) ); + rRect.SetRight( rRef.X() + FRound( (rRect.Right() - rRef.X()) * double(aXFact) ) ); + + if (!aYFact.IsValid()) { + SAL_WARN( "svx.svdraw", "invalid fraction yFract, using Fraction(1,1)" ); + aYFact = Fraction(1,1); + tools::Long nHgt = rRect.Bottom() - rRect.Top(); + if (nHgt == 0) rRect.AdjustBottom( 1 ); + } + rRect.SetTop( rRef.Y() + FRound( (rRect.Top() - rRef.Y()) * double(aYFact) ) ); + rRect.SetBottom( rRef.Y() + FRound( (rRect.Bottom() - rRef.Y()) * double(aYFact) ) ); + + rRect.Normalize(); +} + + +void ResizePoly(tools::Polygon& rPoly, const Point& rRef, const Fraction& xFact, const Fraction& yFact) +{ + sal_uInt16 nCount=rPoly.GetSize(); + for (sal_uInt16 i=0; iAdjustY( -y0 ); + // resize, account for the distance from the center + pC1->setY(FRound(static_cast(pC1->Y()) /rRad.X()*(cx-pC1->X())) ); + pC1->AdjustY(cy ); + } else { + // move into the direction of the center, as a basic position for the rotation + pC1->AdjustX( -x0 ); + // resize, account for the distance from the center + tools::Long nPntRad=cy-pC1->Y(); + double nFact=static_cast(nPntRad)/static_cast(rRad.Y()); + pC1->setX(FRound(static_cast(pC1->X())*nFact) ); + pC1->AdjustX(cx ); + } + RotatePoint(*pC1,rCenter,sn,cs); + } + if (bC2) { + if (bVert) { + // move into the direction of the center, as a basic position for the rotation + pC2->AdjustY( -y0 ); + // resize, account for the distance from the center + pC2->setY(FRound(static_cast(pC2->Y()) /rRad.X()*(rCenter.X()-pC2->X())) ); + pC2->AdjustY(cy ); + } else { + // move into the direction of the center, as a basic position for the rotation + pC2->AdjustX( -x0 ); + // resize, account for the distance from the center + tools::Long nPntRad=rCenter.Y()-pC2->Y(); + double nFact=static_cast(nPntRad)/static_cast(rRad.Y()); + pC2->setX(FRound(static_cast(pC2->X())*nFact) ); + pC2->AdjustX(cx ); + } + RotatePoint(*pC2,rCenter,sn,cs); + } + rSin=sn; + rCos=cs; + return nAngle; +} + +double CrookSlantXPoint(Point& rPnt, Point* pC1, Point* pC2, const Point& rCenter, + const Point& rRad, double& rSin, double& rCos, bool bVert) +{ + bool bC1=pC1!=nullptr; + bool bC2=pC2!=nullptr; + tools::Long x0=rPnt.X(); + tools::Long y0=rPnt.Y(); + tools::Long dx1=0,dy1=0; + tools::Long dxC1=0,dyC1=0; + tools::Long dxC2=0,dyC2=0; + if (bVert) { + tools::Long nStart=rCenter.X()-rRad.X(); + dx1=rPnt.X()-nStart; + rPnt.setX(nStart ); + if (bC1) { + dxC1=pC1->X()-nStart; + pC1->setX(nStart ); + } + if (bC2) { + dxC2=pC2->X()-nStart; + pC2->setX(nStart ); + } + } else { + tools::Long nStart=rCenter.Y()-rRad.Y(); + dy1=rPnt.Y()-nStart; + rPnt.setY(nStart ); + if (bC1) { + dyC1=pC1->Y()-nStart; + pC1->setY(nStart ); + } + if (bC2) { + dyC2=pC2->Y()-nStart; + pC2->setY(nStart ); + } + } + double nAngle=GetCrookAngle(rPnt,rCenter,rRad,bVert); + double sn=sin(nAngle); + double cs=cos(nAngle); + RotatePoint(rPnt,rCenter,sn,cs); + if (bC1) { if (bVert) pC1->AdjustY( -(y0-rCenter.Y()) ); else pC1->AdjustX( -(x0-rCenter.X()) ); RotatePoint(*pC1,rCenter,sn,cs); } + if (bC2) { if (bVert) pC2->AdjustY( -(y0-rCenter.Y()) ); else pC2->AdjustX( -(x0-rCenter.X()) ); RotatePoint(*pC2,rCenter,sn,cs); } + if (bVert) { + rPnt.AdjustX(dx1 ); + if (bC1) pC1->AdjustX(dxC1 ); + if (bC2) pC2->AdjustX(dxC2 ); + } else { + rPnt.AdjustY(dy1 ); + if (bC1) pC1->AdjustY(dyC1 ); + if (bC2) pC2->AdjustY(dyC2 ); + } + rSin=sn; + rCos=cs; + return nAngle; +} + +double CrookStretchXPoint(Point& rPnt, Point* pC1, Point* pC2, const Point& rCenter, + const Point& rRad, double& rSin, double& rCos, bool bVert, + const tools::Rectangle& rRefRect) +{ + tools::Long y0=rPnt.Y(); + CrookSlantXPoint(rPnt,pC1,pC2,rCenter,rRad,rSin,rCos,bVert); + if (bVert) { + } else { + tools::Long nTop=rRefRect.Top(); + tools::Long nBtm=rRefRect.Bottom(); + tools::Long nHgt=nBtm-nTop; + tools::Long dy=rPnt.Y()-y0; + double a=static_cast(y0-nTop)/nHgt; + a*=dy; + rPnt.setY(y0+FRound(a) ); + } + return 0.0; +} + + +void CrookRotatePoly(XPolygon& rPoly, const Point& rCenter, const Point& rRad, bool bVert) +{ + double nSin,nCos; + sal_uInt16 nPointCnt=rPoly.GetPointCount(); + sal_uInt16 i=0; + while (i0) a=-9000_deg100; + else a=9000_deg100; + } else { + a = Degree100(FRound(basegfx::rad2deg<100>(atan2(static_cast(-rPnt.Y()), static_cast(rPnt.X()))))); + } + return a; +} + +Degree100 NormAngle18000(Degree100 a) +{ + while (a<-18000_deg100) a+=36000_deg100; + while (a>=18000_deg100) a-=36000_deg100; + return a; +} + +Degree100 NormAngle36000(Degree100 a) +{ + a %= 36000_deg100; + if (a < 0_deg100) + a += 36000_deg100; + return a; +} + +sal_uInt16 GetAngleSector(Degree100 nAngle) { return (NormAngle36000(nAngle) / 9000_deg100).get(); } + +tools::Long GetLen(const Point& rPnt) +{ + tools::Long x=std::abs(rPnt.X()); + tools::Long y=std::abs(rPnt.Y()); + if (x+y<0x8000) { // because 7FFF * 7FFF * 2 = 7FFE0002 + x*=x; + y*=y; + x+=y; + x=FRound(sqrt(static_cast(x))); + return x; + } else { + double nx=x; + double ny=y; + nx*=nx; + ny*=ny; + nx+=ny; + nx=sqrt(nx); + if (nx>0x7FFFFFFF) { + return 0x7FFFFFFF; // we can't go any further, for fear of an overrun! + } else { + return FRound(nx); + } + } +} + + +void GeoStat::RecalcSinCos() +{ + if (m_nRotationAngle==0_deg100) { + mfSinRotationAngle=0.0; + mfCosRotationAngle=1.0; + } else { + double a = toRadians(m_nRotationAngle); + mfSinRotationAngle=sin(a); + mfCosRotationAngle=cos(a); + } +} + +void GeoStat::RecalcTan() +{ + if (m_nShearAngle==0_deg100) { + mfTanShearAngle=0.0; + } else { + double a = toRadians(m_nShearAngle); + mfTanShearAngle=tan(a); + } +} + + +tools::Polygon Rect2Poly(const tools::Rectangle& rRect, const GeoStat& rGeo) +{ + tools::Polygon aPol(5); + aPol[0]=rRect.TopLeft(); + aPol[1]=rRect.TopRight(); + aPol[2]=rRect.BottomRight(); + aPol[3]=rRect.BottomLeft(); + aPol[4]=rRect.TopLeft(); + if (rGeo.m_nShearAngle) ShearPoly(aPol,rRect.TopLeft(),rGeo.mfTanShearAngle); + if (rGeo.m_nRotationAngle) RotatePoly(aPol,rRect.TopLeft(),rGeo.mfSinRotationAngle,rGeo.mfCosRotationAngle); + return aPol; +} + +namespace svx +{ +tools::Rectangle polygonToRectangle(const tools::Polygon& rPolygon, GeoStat& rGeo) +{ + rGeo.m_nRotationAngle = GetAngle(rPolygon[1] - rPolygon[0]); + rGeo.m_nRotationAngle = NormAngle36000(rGeo.m_nRotationAngle); + + // rotation successful + rGeo.RecalcSinCos(); + + Point aPoint1(rPolygon[1] - rPolygon[0]); + if (rGeo.m_nRotationAngle) + RotatePoint(aPoint1, Point(0,0), -rGeo.mfSinRotationAngle, rGeo.mfCosRotationAngle); // -Sin to reverse rotation + tools::Long nWidth = aPoint1.X(); + + Point aPoint0(rPolygon[0]); + Point aPoint3(rPolygon[3] - rPolygon[0]); + if (rGeo.m_nRotationAngle) + RotatePoint(aPoint3, Point(0,0), -rGeo.mfSinRotationAngle, rGeo.mfCosRotationAngle); // -Sin to reverse rotation + tools::Long nHeight = aPoint3.Y(); + + Degree100 nShearAngle = GetAngle(aPoint3); + nShearAngle -= 27000_deg100; // ShearWink is measured against a vertical line + nShearAngle = -nShearAngle; // negating, because '+' is shearing clock-wise + + bool bMirror = aPoint3.Y() < 0; + if (bMirror) + { // "exchange of points" when mirroring + nHeight = -nHeight; + nShearAngle += 18000_deg100; + aPoint0 = rPolygon[3]; + } + + nShearAngle = NormAngle18000(nShearAngle); + if (nShearAngle < -9000_deg100 || nShearAngle > 9000_deg100) + { + nShearAngle = NormAngle18000(nShearAngle + 18000_deg100); + } + + if (nShearAngle < -SDRMAXSHEAR) + nShearAngle = -SDRMAXSHEAR; // limit ShearWinkel (shear angle) to +/- 89.00 deg + + if (nShearAngle > SDRMAXSHEAR) + nShearAngle = SDRMAXSHEAR; + + rGeo.m_nShearAngle = nShearAngle; + rGeo.RecalcTan(); + + Point aRU(aPoint0); + aRU.AdjustX(nWidth); + aRU.AdjustY(nHeight); + + return tools::Rectangle(aPoint0, aRU); +} + +} // end svx + +void OrthoDistance8(const Point& rPt0, Point& rPt, bool bBigOrtho) +{ + tools::Long dx=rPt.X()-rPt0.X(); + tools::Long dy=rPt.Y()-rPt0.Y(); + tools::Long dxa=std::abs(dx); + tools::Long dya=std::abs(dy); + if (dx==0 || dy==0 || dxa==dya) return; + if (dxa>=dya*2) { rPt.setY(rPt0.Y() ); return; } + if (dya>=dxa*2) { rPt.setX(rPt0.X() ); return; } + if ((dxa=0 ? 1 : -1) ) ); + } else { + rPt.setX(rPt0.X()+(dya* (dx>=0 ? 1 : -1) ) ); + } +} + +void OrthoDistance4(const Point& rPt0, Point& rPt, bool bBigOrtho) +{ + tools::Long dx=rPt.X()-rPt0.X(); + tools::Long dy=rPt.Y()-rPt0.Y(); + tools::Long dxa=std::abs(dx); + tools::Long dya=std::abs(dy); + if ((dxa=0 ? 1 : -1) ) ); + } else { + rPt.setX(rPt0.X()+(dya* (dx>=0 ? 1 : -1) ) ); + } +} + + +tools::Long BigMulDiv(tools::Long nVal, tools::Long nMul, tools::Long nDiv) +{ + if (!nDiv) + return 0x7fffffff; + return BigInt::Scale(nVal, nMul, nDiv); +} + +static FrPair toPair(o3tl::Length eFrom, o3tl::Length eTo) +{ + const auto& [nNum, nDen] = o3tl::getConversionMulDiv(eFrom, eTo); + return FrPair(nNum, nDen); +} + +// How many eU units fit into a mm, respectively an inch? +// Or: How many mm, respectively inches, are there in an eU (and then give me the inverse) + +static FrPair GetInchOrMM(MapUnit eU) +{ + switch (eU) { + case MapUnit::Map1000thInch: return toPair(o3tl::Length::in, o3tl::Length::in1000); + case MapUnit::Map100thInch : return toPair(o3tl::Length::in, o3tl::Length::in100); + case MapUnit::Map10thInch : return toPair(o3tl::Length::in, o3tl::Length::in10); + case MapUnit::MapInch : return toPair(o3tl::Length::in, o3tl::Length::in); + case MapUnit::MapPoint : return toPair(o3tl::Length::in, o3tl::Length::pt); + case MapUnit::MapTwip : return toPair(o3tl::Length::in, o3tl::Length::twip); + case MapUnit::Map100thMM : return toPair(o3tl::Length::mm, o3tl::Length::mm100); + case MapUnit::Map10thMM : return toPair(o3tl::Length::mm, o3tl::Length::mm10); + case MapUnit::MapMM : return toPair(o3tl::Length::mm, o3tl::Length::mm); + case MapUnit::MapCM : return toPair(o3tl::Length::mm, o3tl::Length::cm); + case MapUnit::MapPixel : { + ScopedVclPtrInstance< VirtualDevice > pVD; + pVD->SetMapMode(MapMode(MapUnit::Map100thMM)); + Point aP(pVD->PixelToLogic(Point(64,64))); // 64 pixels for more accuracy + return FrPair(6400,aP.X(),6400,aP.Y()); + } + case MapUnit::MapAppFont: case MapUnit::MapSysFont: { + ScopedVclPtrInstance< VirtualDevice > pVD; + pVD->SetMapMode(MapMode(eU)); + Point aP(pVD->LogicToPixel(Point(32,32))); // 32 units for more accuracy + pVD->SetMapMode(MapMode(MapUnit::Map100thMM)); + aP=pVD->PixelToLogic(aP); + return FrPair(3200,aP.X(),3200,aP.Y()); + } + default: break; + } + return Fraction(1,1); +} + +// Calculate the factor that we need to convert units from eS to eD. +// e. g. GetMapFactor(UNIT_MM,UNIT_100TH_MM) => 100. + +FrPair GetMapFactor(MapUnit eS, MapUnit eD) +{ + if (eS==eD) return FrPair(1,1,1,1); + const auto eFrom = MapToO3tlLength(eS, o3tl::Length::invalid); + const auto eTo = MapToO3tlLength(eD, o3tl::Length::invalid); + if (eFrom != o3tl::Length::invalid && eTo != o3tl::Length::invalid) + return toPair(eFrom, eTo); + FrPair aS(GetInchOrMM(eS)); + FrPair aD(GetInchOrMM(eD)); + bool bSInch=IsInch(eS); + bool bDInch=IsInch(eD); + FrPair aRet(aD.X()/aS.X(),aD.Y()/aS.Y()); + if (bSInch && !bDInch) { aRet.X()*=Fraction(127,5); aRet.Y()*=Fraction(127,5); } + if (!bSInch && bDInch) { aRet.X()*=Fraction(5,127); aRet.Y()*=Fraction(5,127); } + return aRet; +}; + +FrPair GetMapFactor(FieldUnit eS, FieldUnit eD) +{ + if (eS==eD) return FrPair(1,1,1,1); + auto eFrom = FieldToO3tlLength(eS), eTo = FieldToO3tlLength(eD); + if (eFrom == o3tl::Length::invalid) + { + if (eTo == o3tl::Length::invalid) + return FrPair(1,1,1,1); + eFrom = IsInch(eD) ? o3tl::Length::in : o3tl::Length::mm; + } + else if (eTo == o3tl::Length::invalid) + eTo = IsInch(eS) ? o3tl::Length::in : o3tl::Length::mm; + return toPair(eFrom, eTo); +}; + +void SdrFormatter::Undirty() +{ + const o3tl::Length eFrom = MapToO3tlLength(m_eSrcMU, o3tl::Length::invalid); + const o3tl::Length eTo = MapToO3tlLength(m_eDstMU, o3tl::Length::invalid); + if (eFrom != o3tl::Length::invalid && eTo != o3tl::Length::invalid) + { + const auto& [mul, div] = o3tl::getConversionMulDiv(eFrom, eTo); + sal_Int64 nMul = mul; + sal_Int64 nDiv = div; + short nComma = 0; + + // shorten trailing zeros for dividend + while (0 == (nMul % 10)) + { + nComma--; + nMul /= 10; + } + + // shorten trailing zeros for divisor + while (0 == (nDiv % 10)) + { + nComma++; + nDiv /= 10; + } + m_nMul = nMul; + m_nDiv = nDiv; + m_nComma = nComma; + } + else + { + m_nMul = m_nDiv = 1; + m_nComma = 0; + } + m_bDirty=false; +} + + +OUString SdrFormatter::GetStr(tools::Long nVal) const +{ + static constexpr OUString aNullCode(u"0"_ustr); + + if(!nVal) + { + return aNullCode; + } + + // we may lose some decimal places here, because of MulDiv instead of Real + bool bNeg(nVal < 0); + SvtSysLocale aSysLoc; + const LocaleDataWrapper& rLoc = aSysLoc.GetLocaleData(); + + if (m_bDirty) + const_cast(this)->Undirty(); + + sal_Int16 nC(m_nComma); + + if(bNeg) + nVal = -nVal; + + while(nC <= -3) + { + nVal *= 1000; + nC += 3; + } + + while(nC <= -1) + { + nVal *= 10; + nC++; + } + + if(m_nMul != m_nDiv) + nVal = BigMulDiv(nVal, m_nMul, m_nDiv); + + OUStringBuffer aStr = OUString::number(nVal); + + if(nC > 0 && aStr.getLength() <= nC ) + { + // decimal separator necessary + sal_Int32 nCount(nC - aStr.getLength()); + + if(nCount >= 0 && LocaleDataWrapper::isNumLeadingZero()) + nCount++; + + for(sal_Int32 i=0; i 0) + { + // TODO: we should round here + aStr.remove(aStr.getLength() - nWeg, nWeg); + nC = nNumDigits; + } + } + + // remember everything before the decimal separator for later + sal_Int32 nForComma(aStr.getLength() - nC); + + if(nC > 0) + { + // insert comma char (decimal separator) + // remove trailing zeros + while(nC > 0 && aStr[aStr.getLength() - 1] == aNullCode.getStr()[0]) + { + aStr.remove(aStr.getLength() - 1, 1); + nC--; + } + + if(nC > 0) + { + // do we still have decimal places? + sal_Unicode cDec(rLoc.getNumDecimalSep()[0]); + aStr.insert(nForComma, cDec); + } + } + + // add in thousands separator (if necessary) + if( nForComma > 3 ) + { + const OUString& aThoSep( rLoc.getNumThousandSep() ); + if ( aThoSep.getLength() > 0 ) + { + sal_Unicode cTho( aThoSep[0] ); + sal_Int32 i(nForComma - 3); + + while(i > 0) + { + aStr.insert(i, cTho); + i -= 3; + } + } + } + + if(aStr.isEmpty()) + aStr.append(aNullCode); + + if(bNeg && (aStr.getLength() > 1 || aStr[0] != aNullCode.getStr()[0])) + { + aStr.insert(0, "-"); + } + + return aStr.makeStringAndClear(); +} + +OUString SdrFormatter::GetUnitStr(MapUnit eUnit) +{ + switch(eUnit) + { + // metrically + case MapUnit::Map100thMM : + return "/100mm"; + case MapUnit::Map10thMM : + return "/10mm"; + case MapUnit::MapMM : + return "mm"; + case MapUnit::MapCM : + return "cm"; + + // Inch + case MapUnit::Map1000thInch: + return "/1000\""; + case MapUnit::Map100thInch : + return "/100\""; + case MapUnit::Map10thInch : + return "/10\""; + case MapUnit::MapInch : + return "\""; + case MapUnit::MapPoint : + return "pt"; + case MapUnit::MapTwip : + return "twip"; + + // others + case MapUnit::MapPixel : + return "pixel"; + case MapUnit::MapSysFont : + return "sysfont"; + case MapUnit::MapAppFont : + return "appfont"; + case MapUnit::MapRelative : + return "%"; + default: + return OUString(); + } +} + +OUString SdrFormatter::GetUnitStr(FieldUnit eUnit) +{ + switch(eUnit) + { + default : + case FieldUnit::NONE : + case FieldUnit::CUSTOM : + return OUString(); + + // metrically + case FieldUnit::MM_100TH: + return "/100mm"; + case FieldUnit::MM : + return "mm"; + case FieldUnit::CM : + return "cm"; + case FieldUnit::M : + return "m"; + case FieldUnit::KM : + return "km"; + + // Inch + case FieldUnit::TWIP : + return "twip"; + case FieldUnit::POINT : + return "pt"; + case FieldUnit::PICA : + return "pica"; + case FieldUnit::INCH : + return "\""; + case FieldUnit::FOOT : + return "ft"; + case FieldUnit::MILE : + return "mile(s)"; + + // others + case FieldUnit::PERCENT: + return "%"; + } +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ -- cgit v1.2.3