summaryrefslogtreecommitdiffstats
path: root/chart2/source/view/main/ShapeFactory.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'chart2/source/view/main/ShapeFactory.cxx')
-rw-r--r--chart2/source/view/main/ShapeFactory.cxx2551
1 files changed, 2551 insertions, 0 deletions
diff --git a/chart2/source/view/main/ShapeFactory.cxx b/chart2/source/view/main/ShapeFactory.cxx
new file mode 100644
index 000000000..7fddc5d01
--- /dev/null
+++ b/chart2/source/view/main/ShapeFactory.cxx
@@ -0,0 +1,2551 @@
+/* -*- 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 <ShapeFactory.hxx>
+#include <BaseGFXHelper.hxx>
+#include <ViewDefines.hxx>
+#include <Stripe.hxx>
+#include <CommonConverters.hxx>
+#include <RelativeSizeHelper.hxx>
+#include <PropertyMapper.hxx>
+#include <VLineProperties.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/chart2/XFormattedString.hpp>
+#include <com/sun/star/drawing/CircleKind.hpp>
+#include <com/sun/star/drawing/DoubleSequence.hpp>
+#include <com/sun/star/drawing/FlagSequence.hpp>
+#include <com/sun/star/drawing/FillStyle.hpp>
+#include <com/sun/star/drawing/LineStyle.hpp>
+#include <com/sun/star/drawing/NormalsKind.hpp>
+#include <com/sun/star/drawing/PointSequence.hpp>
+#include <com/sun/star/drawing/PolyPolygonBezierCoords.hpp>
+#include <com/sun/star/drawing/TextHorizontalAdjust.hpp>
+#include <com/sun/star/drawing/TextureProjectionMode.hpp>
+#include <com/sun/star/drawing/TextVerticalAdjust.hpp>
+#include <com/sun/star/drawing/XShapes2.hpp>
+#include <com/sun/star/graphic/XGraphic.hpp>
+#include <com/sun/star/drawing/XShapes.hpp>
+#include <com/sun/star/style/ParagraphAdjust.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/uno/Any.hxx>
+
+#include <editeng/unoprnms.hxx>
+#include <rtl/math.hxx>
+
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/matrix/b3dhommatrix.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdopath.hxx>
+#include <tools/diagnose_ex.h>
+#include <tools/helpers.hxx>
+#include <tools/UnitConversion.hxx>
+#include <sal/log.hxx>
+
+#include <algorithm>
+#include <cstddef>
+
+using namespace ::com::sun::star;
+using ::com::sun::star::uno::Reference;
+
+namespace chart
+{
+
+namespace
+{
+
+void lcl_addProperty(uno::Sequence<OUString> & rPropertyNames, uno::Sequence<uno::Any> & rPropertyValues,
+ OUString const & rName, uno::Any const & rAny)
+{
+ rPropertyNames.realloc(rPropertyNames.getLength() + 1);
+ rPropertyNames.getArray()[rPropertyNames.getLength() - 1] = rName;
+
+ rPropertyValues.realloc(rPropertyValues.getLength() + 1);
+ rPropertyValues.getArray()[rPropertyValues.getLength() - 1] = rAny;
+}
+
+css::drawing::PolyPolygonShape3D toPolyPolygonShape3D(const std::vector<std::vector<css::drawing::Position3D>>& rPoints)
+{
+ css::drawing::PolyPolygonShape3D aUnoPoly;
+ aUnoPoly.SequenceX.realloc(rPoints.size());
+ aUnoPoly.SequenceY.realloc(rPoints.size());
+ aUnoPoly.SequenceZ.realloc(rPoints.size());
+ for (std::size_t nPolygonIndex=0; nPolygonIndex<rPoints.size(); ++nPolygonIndex)
+ {
+ drawing::DoubleSequence* pOuterSequenceX = &aUnoPoly.SequenceX.getArray()[nPolygonIndex];
+ drawing::DoubleSequence* pOuterSequenceY = &aUnoPoly.SequenceY.getArray()[nPolygonIndex];
+ drawing::DoubleSequence* pOuterSequenceZ = &aUnoPoly.SequenceZ.getArray()[nPolygonIndex];
+ pOuterSequenceX->realloc(rPoints[nPolygonIndex].size());
+ pOuterSequenceY->realloc(rPoints[nPolygonIndex].size());
+ pOuterSequenceZ->realloc(rPoints[nPolygonIndex].size());
+ double* pInnerSequenceX = pOuterSequenceX->getArray();
+ double* pInnerSequenceY = pOuterSequenceY->getArray();
+ double* pInnerSequenceZ = pOuterSequenceZ->getArray();
+ for (std::size_t nPointIndex=0; nPointIndex<rPoints[nPolygonIndex].size(); ++nPointIndex)
+ {
+ auto& rPos = rPoints[nPolygonIndex][nPointIndex];
+ pInnerSequenceX[nPointIndex] = rPos.PositionX;
+ pInnerSequenceY[nPointIndex] = rPos.PositionY;
+ pInnerSequenceZ[nPointIndex] = rPos.PositionZ;
+ }
+ }
+ return aUnoPoly;
+}
+
+} // end anonymous namespace
+
+rtl::Reference<SvxShapeGroupAnyD> ShapeFactory::getOrCreateChartRootShape(
+ const rtl::Reference<SvxDrawPage>& xDrawPage )
+{
+ rtl::Reference<SvxShapeGroupAnyD> xRet = ShapeFactory::getChartRootShape(xDrawPage);
+ if (xRet.is())
+ return xRet;
+
+ // Create a new root shape and set it to the bottom of the page. The root
+ // shape is identified by having the name com.sun.star.chart2.shapes.
+ rtl::Reference<SvxShapeGroup> xShapeGroup = new SvxShapeGroup(nullptr, nullptr);
+ xShapeGroup->setShapeKind(SdrObjKind::Group);
+ // cast to resolve ambiguity in converting to XShape
+ xDrawPage->addBottom(static_cast<SvxShape*>(xShapeGroup.get()));
+
+ setShapeName(xShapeGroup, "com.sun.star.chart2.shapes");
+ xShapeGroup->setSize(awt::Size(0,0));
+
+ return xShapeGroup;
+}
+
+void ShapeFactory::setPageSize(const rtl::Reference<SvxShapeGroupAnyD>&, const awt::Size&) {}
+
+// diverse tools::PolyPolygon create methods
+
+static uno::Any createPolyPolygon_Cube(
+ const drawing::Direction3D& rSize, double fRoundedEdge, bool bRounded )
+{
+ OSL_PRECOND(fRoundedEdge>=0, "fRoundedEdge needs to be >= 0");
+
+ // always use extra points, so set percent diagonal to 0.4 which is 0% in the UI (old Chart comment)
+ if( fRoundedEdge == 0.0 && bRounded)
+ fRoundedEdge = 0.4 / 200.0;
+ else if(!bRounded)
+ fRoundedEdge = 0.0;
+
+ //fWidthH stands for Half Width
+ const double fWidthH = rSize.DirectionX >=0.0? rSize.DirectionX/2.0 : -rSize.DirectionX/2.0;
+ const double fHeight = rSize.DirectionY;
+
+ const double fHeightSign = fHeight >= 0.0 ? 1.0 : -1.0;
+
+ const double fOffset = (fWidthH * fRoundedEdge) * 1.05; // increase by 5% for safety
+ const bool bRoundEdges = fRoundedEdge && fOffset < fWidthH && 2.0 * fOffset < fHeightSign*fHeight;
+ const sal_Int32 nPointCount = bRoundEdges ? 13 : 5;
+
+ drawing::PolyPolygonShape3D aPP;
+
+ aPP.SequenceX.realloc(1);
+ aPP.SequenceY.realloc(1);
+ aPP.SequenceZ.realloc(1);
+
+ drawing::DoubleSequence* pOuterSequenceX = aPP.SequenceX.getArray();
+ drawing::DoubleSequence* pOuterSequenceY = aPP.SequenceY.getArray();
+ drawing::DoubleSequence* pOuterSequenceZ = aPP.SequenceZ.getArray();
+
+ pOuterSequenceX->realloc(nPointCount);
+ pOuterSequenceY->realloc(nPointCount);
+ pOuterSequenceZ->realloc(nPointCount);
+
+ double* pInnerSequenceX = pOuterSequenceX->getArray();
+ double* pInnerSequenceY = pOuterSequenceY->getArray();
+ double* pInnerSequenceZ = pOuterSequenceZ->getArray();
+
+ for(sal_Int32 nN = nPointCount; nN--;)
+ *pInnerSequenceZ++ = 0.0;
+
+ if(nPointCount == 5)
+ {
+ *pInnerSequenceY++ = 0.0;
+ *pInnerSequenceY++ = 0.0;
+ *pInnerSequenceY++ = fHeight;
+ *pInnerSequenceY++ = fHeight;
+ *pInnerSequenceY++ = 0.0;
+
+ *pInnerSequenceX++ = -fWidthH;
+ *pInnerSequenceX++ = fWidthH;
+ *pInnerSequenceX++ = fWidthH;
+ *pInnerSequenceX++ = -fWidthH;
+ *pInnerSequenceX++ = -fWidthH;
+ }
+ else
+ {
+ *pInnerSequenceY++ = 0.0;
+ *pInnerSequenceY++ = 0.0;
+ *pInnerSequenceY++ = 0.0;
+ *pInnerSequenceY++ = fHeightSign*fOffset;
+ *pInnerSequenceY++ = fHeight - fHeightSign*fOffset;
+ *pInnerSequenceY++ = fHeight;
+ *pInnerSequenceY++ = fHeight;
+ *pInnerSequenceY++ = fHeight;
+ *pInnerSequenceY++ = fHeight;
+ *pInnerSequenceY++ = fHeight - fHeightSign*fOffset;
+ *pInnerSequenceY++ = fHeightSign*fOffset;
+ *pInnerSequenceY++ = 0.0;
+ *pInnerSequenceY++ = 0.0;
+
+ *pInnerSequenceX++ = -fWidthH + fOffset;
+ *pInnerSequenceX++ = fWidthH - fOffset;
+ *pInnerSequenceX++ = fWidthH;
+ *pInnerSequenceX++ = fWidthH;
+ *pInnerSequenceX++ = fWidthH;
+ *pInnerSequenceX++ = fWidthH;
+ *pInnerSequenceX++ = fWidthH - fOffset;
+ *pInnerSequenceX++ = -fWidthH + fOffset;
+ *pInnerSequenceX++ = -fWidthH;
+ *pInnerSequenceX++ = -fWidthH;
+ *pInnerSequenceX++ = -fWidthH;
+ *pInnerSequenceX++ = -fWidthH;
+ *pInnerSequenceX++ = -fWidthH + fOffset;
+ }
+ return uno::Any( &aPP, cppu::UnoType<drawing::PolyPolygonShape3D>::get());
+}
+
+static uno::Any createPolyPolygon_Cylinder(
+ double fHeight
+ , double fRadius
+ , sal_Int32& nVerticalSegmentCount )
+{
+ //fHeight may be negative
+ OSL_PRECOND(fRadius>0, "The radius of a cylinder needs to be > 0");
+
+ drawing::PolyPolygonShape3D aPP;
+
+ nVerticalSegmentCount=1;
+
+ aPP.SequenceX.realloc(3);
+ aPP.SequenceY.realloc(3);
+ aPP.SequenceZ.realloc(3);
+
+ drawing::DoubleSequence* pOuterSequenceX = aPP.SequenceX.getArray();
+ drawing::DoubleSequence* pOuterSequenceY = aPP.SequenceY.getArray();
+ drawing::DoubleSequence* pOuterSequenceZ = aPP.SequenceZ.getArray();
+
+ pOuterSequenceX->realloc(2);
+ pOuterSequenceY->realloc(2);
+ pOuterSequenceZ->realloc(2);
+
+ double* pInnerSequenceX = pOuterSequenceX->getArray();
+ double* pInnerSequenceY = pOuterSequenceY->getArray();
+ double* pInnerSequenceZ = pOuterSequenceZ->getArray();
+
+ double fY1 = 0.0;
+ double fY2 = fHeight;
+
+ if( fHeight<0.0 )
+ std::swap(fY1,fY2);
+
+ for(sal_Int32 nN = 2; nN--;)
+ *pInnerSequenceZ++ = 0.0;
+
+ *pInnerSequenceX++ = 0.0;
+ *pInnerSequenceY++ = fY1;
+
+ *pInnerSequenceX++ = fRadius;
+ *pInnerSequenceY++ = fY1;
+
+ pOuterSequenceX++;pOuterSequenceY++;pOuterSequenceZ++;
+ pOuterSequenceX->realloc(2);
+ pOuterSequenceY->realloc(2);
+ pOuterSequenceZ->realloc(2);
+
+ pInnerSequenceX = pOuterSequenceX->getArray();
+ pInnerSequenceY = pOuterSequenceY->getArray();
+ pInnerSequenceZ = pOuterSequenceZ->getArray();
+
+ for(sal_Int32 nN = 2; nN--;)
+ *pInnerSequenceZ++ = 0.0;
+
+ *pInnerSequenceX++ = fRadius;
+ *pInnerSequenceY++ = fY1;
+
+ *pInnerSequenceX++ = fRadius;
+ *pInnerSequenceY++ = fY2;
+
+ pOuterSequenceX++;pOuterSequenceY++;pOuterSequenceZ++;
+ pOuterSequenceX->realloc(2);
+ pOuterSequenceY->realloc(2);
+ pOuterSequenceZ->realloc(2);
+
+ pInnerSequenceX = pOuterSequenceX->getArray();
+ pInnerSequenceY = pOuterSequenceY->getArray();
+ pInnerSequenceZ = pOuterSequenceZ->getArray();
+
+ for(sal_Int32 nN = 2; nN--;)
+ *pInnerSequenceZ++ = 0.0;
+
+ *pInnerSequenceX++ = fRadius;
+ *pInnerSequenceY++ = fY2;
+
+ *pInnerSequenceX++ = 0.0;
+ *pInnerSequenceY++ = fY2;
+
+ return uno::Any( &aPP, cppu::UnoType<drawing::PolyPolygonShape3D>::get());
+}
+
+static uno::Any createPolyPolygon_Cone( double fHeight, double fRadius, double fTopHeight
+ , sal_Int32& nVerticalSegmentCount )
+{
+ OSL_PRECOND(fRadius>0, "The radius of a cone needs to be > 0");
+
+ //for stacked charts we need cones without top -> fTopHeight != 0 resp. bTopless == true
+ //fTopHeight indicates the high of the cutted top only (not the full height)
+ bool bTopless = !::rtl::math::approxEqual( fHeight, fHeight + fTopHeight );
+
+ double r1= 0.0, r2 = fRadius;
+ if(bTopless)
+ // #i63212# fHeight may be negative, fTopHeight is always positive -> use fabs(fHeight)
+ r1 = fRadius * fTopHeight/(fabs(fHeight)+fTopHeight);
+
+ nVerticalSegmentCount=1;
+ drawing::PolyPolygonShape3D aPP;
+
+ aPP.SequenceX.realloc(2);
+ aPP.SequenceY.realloc(2);
+ aPP.SequenceZ.realloc(2);
+
+ drawing::DoubleSequence* pOuterSequenceX = aPP.SequenceX.getArray();
+ drawing::DoubleSequence* pOuterSequenceY = aPP.SequenceY.getArray();
+ drawing::DoubleSequence* pOuterSequenceZ = aPP.SequenceZ.getArray();
+
+ pOuterSequenceX->realloc(2);
+ pOuterSequenceY->realloc(2);
+ pOuterSequenceZ->realloc(2);
+
+ double* pInnerSequenceX = pOuterSequenceX->getArray();
+ double* pInnerSequenceY = pOuterSequenceY->getArray();
+ double* pInnerSequenceZ = pOuterSequenceZ->getArray();
+
+ double fX1 = 0.0;
+ double fX2 = r2;
+ double fX3 = r1;
+
+ double fY1 = 0.0;
+ double fY2 = 0.0;
+ double fY3 = fHeight;
+
+ if( fHeight<0.0 )
+ {
+ std::swap(fX1,fX3);
+ std::swap(fY1,fY3);
+ }
+
+ for(sal_Int32 nN = 2; nN--;)
+ *pInnerSequenceZ++ = 0.0;
+
+ *pInnerSequenceY++ = fY1;
+ *pInnerSequenceX++ = fX1;
+
+ *pInnerSequenceY++ = fY2;
+ *pInnerSequenceX++ = fX2;
+
+ pOuterSequenceX++;pOuterSequenceY++;pOuterSequenceZ++;
+ pOuterSequenceX->realloc(2);
+ pOuterSequenceY->realloc(2);
+ pOuterSequenceZ->realloc(2);
+
+ pInnerSequenceX = pOuterSequenceX->getArray();
+ pInnerSequenceY = pOuterSequenceY->getArray();
+ pInnerSequenceZ = pOuterSequenceZ->getArray();
+
+ for(sal_Int32 nN = 2; nN--;)
+ *pInnerSequenceZ++ = 0.0;
+
+ *pInnerSequenceY++ = fY2;
+ *pInnerSequenceX++ = fX2;
+
+ *pInnerSequenceY++ = fY3;
+ *pInnerSequenceX++ = fX3;
+
+ return uno::Any( &aPP, cppu::UnoType<drawing::PolyPolygonShape3D>::get());
+}
+
+// methods for 3D shape creation
+
+rtl::Reference<Svx3DExtrudeObject>
+ ShapeFactory::createCube(
+ const rtl::Reference<SvxShapeGroupAnyD>& xTarget
+ , const drawing::Position3D& rPosition, const drawing::Direction3D& rSize
+ , sal_Int32 nRotateZAngleHundredthDegree
+ , const uno::Reference< beans::XPropertySet >& xSourceProp
+ , const tPropertyNameMap& rPropertyNameMap
+ , bool bRounded )
+{
+ if( !xTarget.is() )
+ return nullptr;
+ if( bRounded )
+ {
+ try
+ {
+ if( xSourceProp.is() )
+ {
+ drawing::LineStyle aLineStyle;
+ xSourceProp->getPropertyValue( "BorderStyle" ) >>= aLineStyle;
+ if( aLineStyle == drawing::LineStyle_SOLID )
+ bRounded = false;
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ }
+ rtl::Reference<Svx3DExtrudeObject> xShape = impl_createCube( xTarget, rPosition, rSize, nRotateZAngleHundredthDegree, bRounded );
+ if( xSourceProp.is())
+ PropertyMapper::setMappedProperties( *xShape, xSourceProp, rPropertyNameMap );
+ return xShape;
+}
+
+rtl::Reference<Svx3DExtrudeObject>
+ ShapeFactory::impl_createCube(
+ const rtl::Reference<SvxShapeGroupAnyD>& xTarget
+ , const drawing::Position3D& rPosition, const drawing::Direction3D& rSize
+ , sal_Int32 nRotateZAngleHundredthDegree
+ , bool bRounded )
+{
+ if( !xTarget.is() )
+ return nullptr;
+
+ //create shape
+ rtl::Reference<Svx3DExtrudeObject> xShape = new Svx3DExtrudeObject(nullptr);
+ xShape->setShapeKind(SdrObjKind::E3D_Extrusion);
+ xTarget->addShape(*xShape);
+
+ //set properties
+ try
+ {
+ //depth
+ double fDepth = rSize.DirectionZ;
+ if (fDepth<0)
+ fDepth*=-1.0;
+
+ //PercentDiagonal
+ sal_Int16 nPercentDiagonal = bRounded ? 3 : 0;
+
+ //Matrix for position
+ basegfx::B3DHomMatrix aHomMatrix;
+ if (nRotateZAngleHundredthDegree != 0)
+ aHomMatrix.rotate(0.0, 0.0, -basegfx::deg2rad<100>(nRotateZAngleHundredthDegree));
+ aHomMatrix.translate(rPosition.PositionX, rPosition.PositionY,
+ rPosition.PositionZ - (fDepth / 2.0));
+
+ uno::Sequence<OUString> aPropertyNames {
+ UNO_NAME_3D_EXTRUDE_DEPTH,
+ UNO_NAME_3D_PERCENT_DIAGONAL,
+ UNO_NAME_3D_POLYPOLYGON3D,
+ UNO_NAME_3D_TRANSFORM_MATRIX,
+ };
+
+ uno::Sequence<uno::Any> aPropertyValues {
+ uno::Any(sal_Int32(fDepth)), // Depth
+ uno::Any(nPercentDiagonal), // PercentDiagonal
+ createPolyPolygon_Cube(rSize, double(nPercentDiagonal) / 200.0, bRounded),
+ uno::Any(B3DHomMatrixToHomogenMatrix(aHomMatrix))
+ };
+
+ xShape->setPropertyValues(aPropertyNames, aPropertyValues);
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ return xShape;
+}
+
+rtl::Reference<Svx3DLatheObject>
+ ShapeFactory::createCylinder(
+ const rtl::Reference<SvxShapeGroupAnyD>& xTarget
+ , const drawing::Position3D& rPosition, const drawing::Direction3D& rSize
+ , sal_Int32 nRotateZAngleHundredthDegree )
+{
+ return impl_createConeOrCylinder(
+ xTarget, rPosition, rSize, 0.0, nRotateZAngleHundredthDegree, true );
+}
+
+rtl::Reference<Svx3DSceneObject>
+ ShapeFactory::createPyramid(
+ const rtl::Reference<SvxShapeGroupAnyD>& xTarget
+ , const drawing::Position3D& rPosition, const drawing::Direction3D& rSize
+ , double fTopHeight, bool bRotateZ
+ , const uno::Reference< beans::XPropertySet >& xSourceProp
+ , const tPropertyNameMap& rPropertyNameMap )
+{
+ if( !xTarget.is() )
+ return nullptr;
+
+ rtl::Reference<Svx3DSceneObject> xGroup( ShapeFactory::createGroup3D( xTarget ) );
+
+ bool bDoubleSided = false;
+ short nRotatedTexture = 0;
+
+ const double fWidth = rSize.DirectionX;
+ const double fDepth = rSize.DirectionZ;
+ const double fHeight = rSize.DirectionY;
+
+ drawing::Position3D aBottomP1( rPosition.PositionX, rPosition.PositionY, rPosition.PositionZ - fDepth/2.0 );
+ if(bRotateZ)
+ aBottomP1.PositionY -= fWidth/2.0;
+ else
+ aBottomP1.PositionX -= fWidth/2.0;
+ drawing::Position3D aBottomP2( aBottomP1 );
+ if(bRotateZ)
+ aBottomP2.PositionY += fWidth;
+ else
+ aBottomP2.PositionX += fWidth;
+ drawing::Position3D aBottomP3( aBottomP2 );
+ drawing::Position3D aBottomP4( aBottomP1 );
+ aBottomP3.PositionZ += fDepth;
+ aBottomP4.PositionZ += fDepth;
+
+ const double fTopFactor = fTopHeight/(fabs(fHeight)+fTopHeight);
+ drawing::Position3D aTopP1( rPosition.PositionX, rPosition.PositionY, rPosition.PositionZ - fDepth*fTopFactor/2.0 );
+ if(bRotateZ)
+ {
+ aTopP1.PositionY -= fWidth*fTopFactor/2.0;
+ aTopP1.PositionX += fHeight;
+ }
+ else
+ {
+ aTopP1.PositionX -= fWidth*fTopFactor/2.0;
+ aTopP1.PositionY += fHeight;
+ }
+ drawing::Position3D aTopP2( aTopP1 );
+ if(bRotateZ)
+ aTopP2.PositionY += fWidth*fTopFactor;
+ else
+ aTopP2.PositionX += fWidth*fTopFactor;
+ drawing::Position3D aTopP3( aTopP2 );
+ drawing::Position3D aTopP4( aTopP1 );
+ aTopP3.PositionZ += fDepth*fTopFactor;
+ aTopP4.PositionZ += fDepth*fTopFactor;
+
+ Stripe aStripeBottom( aBottomP1, aBottomP4, aBottomP3, aBottomP2 );
+
+ drawing::Position3D aNormalsBottomP1( aBottomP1 );
+ drawing::Position3D aNormalsBottomP2( aBottomP2 );
+ drawing::Position3D aNormalsBottomP3( aBottomP3 );
+ drawing::Position3D aNormalsBottomP4( aBottomP4 );
+ drawing::Position3D aNormalsTopP1( aBottomP1 );
+ drawing::Position3D aNormalsTopP2( aBottomP2 );
+ drawing::Position3D aNormalsTopP3( aBottomP3 );
+ drawing::Position3D aNormalsTopP4( aBottomP4 );
+ if( bRotateZ )
+ {
+ aNormalsTopP1.PositionX += fHeight;
+ aNormalsTopP2.PositionX += fHeight;
+ aNormalsTopP3.PositionX += fHeight;
+ aNormalsTopP4.PositionX += fHeight;
+ }
+ else
+ {
+ aNormalsTopP1.PositionY += fHeight;
+ aNormalsTopP2.PositionY += fHeight;
+ aNormalsTopP3.PositionY += fHeight;
+ aNormalsTopP4.PositionY += fHeight;
+ }
+
+ bool bInvertPolygon = false;
+ bool bInvertNormals = false;
+
+ if(bRotateZ)
+ {
+ //bars
+ if(fHeight>=0.0)
+ {
+ nRotatedTexture = 2;
+ bInvertNormals = true;
+ aStripeBottom = Stripe( aBottomP1, aBottomP4, aBottomP3, aBottomP2 );
+ }
+ else
+ {
+ bInvertPolygon = true;
+ nRotatedTexture = 1;
+ aStripeBottom = Stripe( aBottomP2, aBottomP3, aBottomP4, aBottomP1 );
+ }
+ }
+ else
+ {
+ //columns
+ if(fHeight>=0.0)
+ {
+ bInvertPolygon = true;
+ nRotatedTexture = 2;
+ aStripeBottom = Stripe( aBottomP2, aBottomP3, aBottomP4, aBottomP1 );
+ }
+ else
+ {
+ nRotatedTexture = 3;
+ bInvertNormals = true;
+ aStripeBottom = Stripe( aBottomP4, aBottomP3, aBottomP2, aBottomP1 );
+ }
+ }
+ aStripeBottom.InvertNormal(true);
+
+ Stripe aStripe1( aTopP2, aTopP1, aBottomP1, aBottomP2 );
+ Stripe aStripe2( aTopP3, aTopP2, aBottomP2, aBottomP3 );
+ Stripe aStripe3( aTopP4, aTopP3, aBottomP3, aBottomP4 );
+ Stripe aStripe4( aTopP1, aTopP4, aBottomP4, aBottomP1 );
+
+ if( bInvertPolygon )
+ {
+ aStripe1 = Stripe( aBottomP1, aTopP1, aTopP2, aBottomP2 );
+ aStripe2 = Stripe( aBottomP2, aTopP2, aTopP3, aBottomP3 );
+ aStripe3 = Stripe( aBottomP3, aTopP3, aTopP4, aBottomP4 );
+ aStripe4 = Stripe( aBottomP4, aTopP4, aTopP1, aBottomP1 );
+ }
+
+ Stripe aNormalsStripe1( aNormalsTopP1, aNormalsBottomP1, aNormalsBottomP2, aNormalsTopP2 );
+ Stripe aNormalsStripe2( aNormalsTopP2, aNormalsBottomP2, aNormalsBottomP3, aNormalsTopP3 );
+ Stripe aNormalsStripe3( aNormalsTopP3, aNormalsBottomP3, aNormalsBottomP4, aNormalsTopP4 );
+ Stripe aNormalsStripe4( aNormalsTopP4, aNormalsBottomP4, aNormalsBottomP1, aNormalsTopP1 );
+
+ if( bInvertNormals )
+ {
+ aNormalsStripe1 = Stripe( aNormalsTopP2, aNormalsBottomP2, aNormalsBottomP1, aNormalsTopP1 );
+ aNormalsStripe2 = Stripe( aNormalsTopP3, aNormalsBottomP3, aNormalsBottomP2, aNormalsTopP2 );
+ aNormalsStripe3 = Stripe( aNormalsTopP4, aNormalsBottomP4, aNormalsBottomP3, aNormalsTopP3 );
+ aNormalsStripe4 = Stripe( aNormalsTopP1, aNormalsBottomP1, aNormalsBottomP4, aNormalsTopP4 );
+ }
+
+ aStripe1.SetManualNormal( aNormalsStripe1.getNormal() );
+ aStripe2.SetManualNormal( aNormalsStripe2.getNormal() );
+ aStripe3.SetManualNormal( aNormalsStripe3.getNormal() );
+ aStripe4.SetManualNormal( aNormalsStripe4.getNormal() );
+
+ const bool bFlatNormals = false;
+ ShapeFactory::createStripe( xGroup, aStripe1, xSourceProp, rPropertyNameMap, bDoubleSided, nRotatedTexture, bFlatNormals );
+ ShapeFactory::createStripe( xGroup, aStripe2, xSourceProp, rPropertyNameMap, bDoubleSided, nRotatedTexture, bFlatNormals );
+ ShapeFactory::createStripe( xGroup, aStripe3, xSourceProp, rPropertyNameMap, bDoubleSided, nRotatedTexture, bFlatNormals );
+ ShapeFactory::createStripe( xGroup, aStripe4, xSourceProp, rPropertyNameMap, bDoubleSided, nRotatedTexture, bFlatNormals );
+ ShapeFactory::createStripe( xGroup, aStripeBottom, xSourceProp, rPropertyNameMap, bDoubleSided, nRotatedTexture, bFlatNormals );
+
+ return xGroup;
+}
+
+rtl::Reference<Svx3DLatheObject>
+ ShapeFactory::createCone(
+ const rtl::Reference<SvxShapeGroupAnyD>& xTarget
+ , const drawing::Position3D& rPosition, const drawing::Direction3D& rSize
+ , double fTopHeight, sal_Int32 nRotateZAngleHundredthDegree )
+{
+ return impl_createConeOrCylinder( xTarget, rPosition, rSize, fTopHeight, nRotateZAngleHundredthDegree, false );
+}
+
+rtl::Reference<Svx3DLatheObject>
+ ShapeFactory::impl_createConeOrCylinder(
+ const rtl::Reference<SvxShapeGroupAnyD>& xTarget
+ , const drawing::Position3D& rPosition, const drawing::Direction3D& rSize
+ , double fTopHeight, sal_Int32 nRotateZAngleHundredthDegree
+ , bool bCylinder )
+{
+ if( !xTarget.is() )
+ return nullptr;
+
+ //create shape
+ rtl::Reference<Svx3DLatheObject> xShape = new Svx3DLatheObject(nullptr);
+ xShape->setShapeKind(SdrObjKind::E3D_Lathe);
+ xTarget->addShape(*xShape);
+
+ double fWidth = rSize.DirectionX/2.0; //The depth will be corrected within Matrix
+ double fRadius = fWidth; //!!!!!!!! problem in drawing layer: rotation object calculates wrong needed size -> wrong camera (it's a problem with bounding boxes)
+ double fHeight = rSize.DirectionY;
+
+ //set properties
+ try
+ {
+ //Polygon
+ sal_Int32 nVerticalSegmentCount = 0;
+ uno::Any aPPolygon = bCylinder
+ ? createPolyPolygon_Cylinder(fHeight, fRadius, nVerticalSegmentCount)
+ : createPolyPolygon_Cone(fHeight, fRadius, fTopHeight, nVerticalSegmentCount);
+
+ //Matrix for position
+ basegfx::B3DHomMatrix aHomMatrix;
+ if (nRotateZAngleHundredthDegree != 0)
+ aHomMatrix.rotate(0.0,0.0,-basegfx::deg2rad<100>(nRotateZAngleHundredthDegree));
+ //stretch the symmetric objects to given depth
+ aHomMatrix.scale(1.0,1.0,rSize.DirectionZ/rSize.DirectionX);
+ aHomMatrix.translate(rPosition.PositionX, rPosition.PositionY, rPosition.PositionZ);
+
+ uno::Sequence<OUString> aPropertyNames{
+ UNO_NAME_3D_PERCENT_DIAGONAL,
+ UNO_NAME_3D_POLYPOLYGON3D,
+ UNO_NAME_3D_TRANSFORM_MATRIX,
+ UNO_NAME_3D_HORZ_SEGS,
+ UNO_NAME_3D_VERT_SEGS,
+ UNO_NAME_3D_REDUCED_LINE_GEOMETRY
+ };
+
+ uno::Sequence<uno::Any> aPropertyValues {
+ uno::Any(sal_Int16(5)), // PercentDiagonal
+ aPPolygon, // Polygon
+ uno::Any(B3DHomMatrixToHomogenMatrix(aHomMatrix)), // Matrix
+ uno::Any(CHART_3DOBJECT_SEGMENTCOUNT), // Horizontal Segments
+ uno::Any(nVerticalSegmentCount), // Vertical Segments
+ uno::Any(true) // Reduced lines
+ };
+
+ xShape->setPropertyValues(aPropertyNames, aPropertyValues);
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ return xShape;
+}
+
+static void appendAndCloseBezierCoords( drawing::PolyPolygonBezierCoords& rReturn, const drawing::PolyPolygonBezierCoords& rAdd, bool bAppendInverse )
+{
+ if(!rAdd.Coordinates.hasElements())
+ return;
+ sal_Int32 nAddCount = rAdd.Coordinates[0].getLength();
+ if(!nAddCount)
+ return;
+
+ sal_Int32 nOldCount = rReturn.Coordinates[0].getLength();
+
+ auto pCoordinates = rReturn.Coordinates.getArray();
+ pCoordinates[0].realloc(nOldCount + nAddCount + 1);
+ auto pCoordinates0 = pCoordinates[0].getArray();
+ auto pFlags = rReturn.Flags.getArray();
+ pFlags[0].realloc(nOldCount+nAddCount+1);
+ auto pFlags0 = pFlags[0].getArray();
+
+ for(sal_Int32 nN=0;nN<nAddCount; nN++ )
+ {
+ sal_Int32 nAdd = bAppendInverse ? (nAddCount-1-nN) : nN;
+ pCoordinates0[nOldCount+nN] = rAdd.Coordinates[0][nAdd];
+ pFlags0[nOldCount+nN] = rAdd.Flags[0][nAdd];
+ }
+
+ //close
+ pCoordinates0[nOldCount+nAddCount] = rReturn.Coordinates[0][0];
+ pFlags0[nOldCount+nAddCount] = rReturn.Flags[0][0];
+}
+
+static drawing::PolyPolygonBezierCoords getCircularArcBezierCoords(
+ double fStartAngleRadian, double fWidthAngleRadian, double fUnitRadius
+ , const ::basegfx::B2DHomMatrix& rTransformationFromUnitCircle
+ , const double fAngleSubdivisionRadian )
+{
+ //at least one polygon is created using two normal and two control points
+ //if the angle is larger it is separated into multiple sub angles
+
+ drawing::PolyPolygonBezierCoords aReturn;
+ sal_Int32 nSegmentCount = static_cast< sal_Int32 >( fWidthAngleRadian/fAngleSubdivisionRadian );
+ if( fWidthAngleRadian > fAngleSubdivisionRadian*nSegmentCount )
+ nSegmentCount++;
+
+ double fFirstSegmentAngle = fAngleSubdivisionRadian;
+ double fLastSegmentAngle = fAngleSubdivisionRadian;
+ if(nSegmentCount==1)
+ {
+ fFirstSegmentAngle = fWidthAngleRadian;
+ fLastSegmentAngle = 0.0;
+ }
+ else
+ {
+ double fFirstAngleOnSubDivision = (static_cast<sal_Int32>(fStartAngleRadian/fAngleSubdivisionRadian)+1)*fAngleSubdivisionRadian;
+ if( !::rtl::math::approxEqual( fStartAngleRadian, fFirstAngleOnSubDivision ) )
+ fFirstSegmentAngle = fFirstAngleOnSubDivision-fStartAngleRadian;
+
+ if(nSegmentCount>1)
+ {
+ fLastSegmentAngle = fWidthAngleRadian-fFirstSegmentAngle-fAngleSubdivisionRadian*(nSegmentCount-2);
+ if( fLastSegmentAngle<0 )
+ nSegmentCount--;
+ if( fLastSegmentAngle>fAngleSubdivisionRadian )
+ {
+ fLastSegmentAngle-=fAngleSubdivisionRadian;
+ nSegmentCount++;
+ }
+ }
+ }
+
+ sal_Int32 nPointCount = 1 + 3*nSegmentCount; //first point of next segment equals last point of former segment
+
+ drawing::PointSequence aPoints(nPointCount);
+ auto pPoints = aPoints.getArray();
+ drawing::FlagSequence aFlags(nPointCount);
+ auto pFlags = aFlags.getArray();
+
+ //!! applying matrix to vector does ignore translation, so it is important to use a B2DPoint here instead of B2DVector
+ ::basegfx::B2DPoint P0,P1,P2,P3;
+
+ sal_Int32 nPoint=0;
+ double fCurrentRotateAngle = fStartAngleRadian;
+ for(sal_Int32 nSegment=0; nSegment<nSegmentCount; nSegment++)
+ {
+ double fCurrentSegmentAngle = fAngleSubdivisionRadian;
+ if(nSegment==0)//first segment gets only a smaller peace until the next subdivision
+ fCurrentSegmentAngle = fFirstSegmentAngle;
+ else if(nSegment==(nSegmentCount-1)) //the last segment gets the rest angle that does not fit into equal pieces
+ fCurrentSegmentAngle = fLastSegmentAngle;
+
+ //first create untransformed points for a unit circle arc:
+ const double fCos = cos(fCurrentSegmentAngle/2.0);
+ const double fSin = sin(fCurrentSegmentAngle/2.0);
+ P0.setX(fCos);
+ P3.setX(fCos);
+ P0.setY(-fSin);
+ P3.setY(-P0.getY());
+
+ P1.setX((4.0-fCos)/3.0);
+ P2.setX(P1.getX());
+ P1.setY((1.0-fCos)*(fCos-3.0)/(3.0*fSin));
+ P2.setY(-P1.getY());
+ //transform thus startangle equals NULL
+ ::basegfx::B2DHomMatrix aStart;
+ aStart.rotate(fCurrentSegmentAngle/2.0 + fCurrentRotateAngle );
+ fCurrentRotateAngle+=fCurrentSegmentAngle;
+
+ aStart.scale( fUnitRadius, fUnitRadius );
+
+ //apply given transformation to get final points
+ P0 = rTransformationFromUnitCircle*(aStart*P0);
+ P1 = rTransformationFromUnitCircle*(aStart*P1);
+ P2 = rTransformationFromUnitCircle*(aStart*P2);
+ P3 = rTransformationFromUnitCircle*(aStart*P3);
+
+ pPoints[nPoint].X = static_cast< sal_Int32 >( P0.getX());
+ pPoints[nPoint].Y = static_cast< sal_Int32 >( P0.getY());
+ pFlags [nPoint++] = drawing::PolygonFlags_NORMAL;
+
+ pPoints[nPoint].X = static_cast< sal_Int32 >( P1.getX());
+ pPoints[nPoint].Y = static_cast< sal_Int32 >( P1.getY());
+ pFlags[nPoint++] = drawing::PolygonFlags_CONTROL;
+
+ pPoints[nPoint].X = static_cast< sal_Int32 >( P2.getX());
+ pPoints[nPoint].Y = static_cast< sal_Int32 >( P2.getY());
+ pFlags [nPoint++] = drawing::PolygonFlags_CONTROL;
+
+ if(nSegment==(nSegmentCount-1))
+ {
+ pPoints[nPoint].X = static_cast< sal_Int32 >( P3.getX());
+ pPoints[nPoint].Y = static_cast< sal_Int32 >( P3.getY());
+ pFlags [nPoint++] = drawing::PolygonFlags_NORMAL;
+ }
+ }
+
+ aReturn.Coordinates = { aPoints };
+ aReturn.Flags = { aFlags };
+
+ return aReturn;
+}
+
+static drawing::PolyPolygonBezierCoords getRingBezierCoords(
+ double fUnitCircleInnerRadius
+ , double fUnitCircleOuterRadius
+ , double fStartAngleRadian, double fWidthAngleRadian
+ , const ::basegfx::B2DHomMatrix& aTransformationFromUnitCircle
+ , const double fAngleSubdivisionRadian )
+{
+ drawing::PolyPolygonBezierCoords aReturn;
+
+ drawing::PolyPolygonBezierCoords aOuterArc = getCircularArcBezierCoords(
+ fStartAngleRadian, fWidthAngleRadian, fUnitCircleOuterRadius, aTransformationFromUnitCircle, fAngleSubdivisionRadian );
+ aReturn.Coordinates = { aOuterArc.Coordinates[0] };
+ aReturn.Flags = { aOuterArc.Flags[0] };
+
+ drawing::PolyPolygonBezierCoords aInnerArc = getCircularArcBezierCoords(
+ fStartAngleRadian, fWidthAngleRadian, fUnitCircleInnerRadius, aTransformationFromUnitCircle, fAngleSubdivisionRadian );
+ appendAndCloseBezierCoords( aReturn, aInnerArc, true );
+
+ return aReturn;
+}
+
+rtl::Reference<SvxShapePolyPolygon>
+ ShapeFactory::createPieSegment2D(
+ const rtl::Reference<SvxShapeGroupAnyD>& xTarget
+ , double fUnitCircleStartAngleDegree, double fUnitCircleWidthAngleDegree
+ , double fUnitCircleInnerRadius, double fUnitCircleOuterRadius
+ , const drawing::Direction3D& rOffset
+ , const drawing::HomogenMatrix& rUnitCircleToScene )
+{
+ if( !xTarget.is() )
+ return nullptr;
+
+ // tdf#123504: both 0 and 360 are valid and different values here!
+ while (fUnitCircleWidthAngleDegree > 360)
+ fUnitCircleWidthAngleDegree -= 360.0;
+ while (fUnitCircleWidthAngleDegree < 0)
+ fUnitCircleWidthAngleDegree += 360.0;
+
+ //create shape
+ rtl::Reference<SvxShapePolyPolygon> xShape = new SvxShapePolyPolygon(nullptr);
+ xShape->setShapeKind(SdrObjKind::PathFill); // aka ClosedBezierShape
+ xTarget->addShape(*xShape); //need to add the shape before setting of properties
+
+ //set properties
+ try
+ {
+ ::basegfx::B2DHomMatrix aTransformationFromUnitCircle( IgnoreZ( HomogenMatrixToB3DHomMatrix(rUnitCircleToScene) ) );
+ aTransformationFromUnitCircle.translate(rOffset.DirectionX,rOffset.DirectionY);
+
+ const double fAngleSubdivisionRadian = M_PI/10.0;
+
+ drawing::PolyPolygonBezierCoords aCoords
+ = getRingBezierCoords(fUnitCircleInnerRadius, fUnitCircleOuterRadius,
+ basegfx::deg2rad(fUnitCircleStartAngleDegree),
+ basegfx::deg2rad(fUnitCircleWidthAngleDegree),
+ aTransformationFromUnitCircle, fAngleSubdivisionRadian);
+
+ xShape->SvxShape::setPropertyValue( "PolyPolygonBezier", uno::Any( aCoords ) );
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+
+ return xShape;
+}
+
+rtl::Reference<Svx3DExtrudeObject>
+ ShapeFactory::createPieSegment(
+ const rtl::Reference<SvxShapeGroupAnyD>& xTarget
+ , double fUnitCircleStartAngleDegree, double fUnitCircleWidthAngleDegree
+ , double fUnitCircleInnerRadius, double fUnitCircleOuterRadius
+ , const drawing::Direction3D& rOffset
+ , const drawing::HomogenMatrix& rUnitCircleToScene
+ , double fDepth )
+{
+ if( !xTarget.is() )
+ return nullptr;
+
+ // tdf#123504: both 0 and 360 are valid and different values here!
+ while (fUnitCircleWidthAngleDegree > 360)
+ fUnitCircleWidthAngleDegree -= 360.0;
+ while (fUnitCircleWidthAngleDegree < 0)
+ fUnitCircleWidthAngleDegree += 360.0;
+
+ //create shape
+ rtl::Reference<Svx3DExtrudeObject> xShape = new Svx3DExtrudeObject(nullptr);
+ xShape->setShapeKind(SdrObjKind::E3D_Extrusion);
+ xTarget->addShape(*xShape); //need to add the shape before setting of properties
+
+ //set properties
+ try
+ {
+ ::basegfx::B2DHomMatrix aTransformationFromUnitCircle( IgnoreZ( HomogenMatrixToB3DHomMatrix(rUnitCircleToScene) ) );
+ aTransformationFromUnitCircle.translate(rOffset.DirectionX,rOffset.DirectionY);
+
+ const double fAngleSubdivisionRadian = M_PI/32.0;
+
+ drawing::PolyPolygonBezierCoords aCoords
+ = getRingBezierCoords(fUnitCircleInnerRadius, fUnitCircleOuterRadius,
+ basegfx::deg2rad(fUnitCircleStartAngleDegree),
+ basegfx::deg2rad(fUnitCircleWidthAngleDegree),
+ aTransformationFromUnitCircle, fAngleSubdivisionRadian);
+
+ //depth
+ xShape->setPropertyValue( UNO_NAME_3D_EXTRUDE_DEPTH
+ , uno::Any(static_cast<sal_Int32>(fDepth)) );
+
+ //PercentDiagonal
+ xShape->setPropertyValue( UNO_NAME_3D_PERCENT_DIAGONAL
+ , uno::Any( sal_Int16(0) ) );
+
+ //Polygon
+ drawing::PolyPolygonShape3D aPoly( BezierToPoly(aCoords) );
+ ShapeFactory::closePolygon( aPoly );
+ xShape->setPropertyValue( UNO_NAME_3D_POLYPOLYGON3D
+ , uno::Any( aPoly ) );
+
+ //DoubleSided
+ xShape->setPropertyValue( UNO_NAME_3D_DOUBLE_SIDED
+ , uno::Any( true ) );
+
+ //Reduced lines
+ xShape->setPropertyValue( UNO_NAME_3D_REDUCED_LINE_GEOMETRY
+ , uno::Any( true ) );
+
+ //TextureProjectionMode
+ xShape->setPropertyValue( UNO_NAME_3D_TEXTURE_PROJ_Y
+ , uno::Any( drawing::TextureProjectionMode_OBJECTSPECIFIC ) );
+
+ //TextureProjectionMode
+ xShape->setPropertyValue( UNO_NAME_3D_TEXTURE_PROJ_X
+ , uno::Any( drawing::TextureProjectionMode_PARALLEL ) );
+ xShape->setPropertyValue( UNO_NAME_3D_TEXTURE_PROJ_Y
+ , uno::Any( drawing::TextureProjectionMode_OBJECTSPECIFIC ) );
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ return xShape;
+}
+
+rtl::Reference<Svx3DPolygonObject>
+ ShapeFactory::createStripe( const rtl::Reference<SvxShapeGroupAnyD>& xTarget
+ , const Stripe& rStripe
+ , const uno::Reference< beans::XPropertySet >& xSourceProp
+ , const tPropertyNameMap& rPropertyNameMap
+ , bool bDoubleSided
+ , short nRotatedTexture
+ , bool bFlatNormals )
+{
+ if( !xTarget.is() )
+ return nullptr;
+
+ //create shape
+ rtl::Reference<Svx3DPolygonObject> xShape = new Svx3DPolygonObject(nullptr);
+ xShape->setShapeKind(SdrObjKind::E3D_Polygon);
+ xTarget->addShape(*xShape);
+
+ //set properties
+ try
+ {
+ uno::Sequence<OUString> aPropertyNames{
+ UNO_NAME_3D_POLYPOLYGON3D,
+ UNO_NAME_3D_TEXTUREPOLYGON3D,
+ UNO_NAME_3D_NORMALSPOLYGON3D,
+ UNO_NAME_3D_LINEONLY,
+ UNO_NAME_3D_DOUBLE_SIDED
+ };
+
+ uno::Sequence<uno::Any> aPropertyValues {
+ rStripe.getPolyPolygonShape3D(), // Polygon
+ Stripe::getTexturePolygon(nRotatedTexture), // TexturePolygon
+ rStripe.getNormalsPolygon(), // Normals Polygon
+ uno::Any(false), // LineOnly
+ uno::Any(bDoubleSided) // DoubleSided
+ };
+
+ //NormalsKind
+ if (bFlatNormals)
+ lcl_addProperty(aPropertyNames, aPropertyValues,
+ UNO_NAME_3D_NORMALS_KIND, uno::Any(drawing::NormalsKind_FLAT));
+
+ xShape->setPropertyValues(aPropertyNames, aPropertyValues);
+
+ if (xSourceProp)
+ {
+ PropertyMapper::setMappedProperties(*xShape, xSourceProp, rPropertyNameMap);
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ return xShape;
+}
+
+rtl::Reference<Svx3DExtrudeObject>
+ ShapeFactory::createArea3D( const rtl::Reference<SvxShapeGroupAnyD>& xTarget
+ , const std::vector<std::vector<css::drawing::Position3D>>& rPolyPolygon
+ , double fDepth )
+{
+ if( !xTarget.is() )
+ return nullptr;
+
+ if( rPolyPolygon.empty() )
+ return nullptr;
+
+ //create shape
+ rtl::Reference<Svx3DExtrudeObject> xShape = new Svx3DExtrudeObject(nullptr);
+ xShape->setShapeKind(SdrObjKind::E3D_Extrusion);
+ xTarget->addShape(*xShape);
+
+ css::drawing::PolyPolygonShape3D aUnoPolyPolygon = toPolyPolygonShape3D(rPolyPolygon);
+
+ //set properties
+ try
+ {
+ uno::Sequence<OUString> aPropertyNames{
+ UNO_NAME_3D_EXTRUDE_DEPTH,
+ UNO_NAME_3D_PERCENT_DIAGONAL,
+ UNO_NAME_3D_POLYPOLYGON3D,
+ UNO_NAME_3D_DOUBLE_SIDED,
+ };
+
+ uno::Sequence<uno::Any> aPropertyValues {
+ uno::Any(sal_Int32(fDepth)), // depth
+ uno::Any(sal_Int16(0)), // PercentDiagonal
+ uno::Any(aUnoPolyPolygon), // Polygon
+ uno::Any(true) // DoubleSided
+ };
+
+ //the z component of the polygon is now ignored by the drawing layer,
+ //so we need to translate the object via transformation matrix
+
+ //Matrix for position
+ if (!rPolyPolygon.empty() && !rPolyPolygon[0].empty())
+ {
+ basegfx::B3DHomMatrix aM;
+ aM.translate(0, 0, rPolyPolygon[0][0].PositionZ);
+ drawing::HomogenMatrix aHM = B3DHomMatrixToHomogenMatrix(aM);
+ lcl_addProperty(aPropertyNames, aPropertyValues, UNO_NAME_3D_TRANSFORM_MATRIX, uno::Any(aHM));
+ }
+ xShape->setPropertyValues(aPropertyNames, aPropertyValues);
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ return xShape;
+}
+
+rtl::Reference<SvxShapePolyPolygon>
+ ShapeFactory::createArea2D( const rtl::Reference<SvxShapeGroupAnyD>& xTarget
+ , const std::vector<std::vector<css::drawing::Position3D>>& rPolyPolygon )
+{
+ if( !xTarget.is() )
+ return nullptr;
+
+ //create shape
+ SdrPathObj* pPath = new SdrPathObj(xTarget->GetSdrObject()->getSdrModelFromSdrObject(), SdrObjKind::Polygon);
+ xTarget->GetSdrObject()->GetSubList()->InsertObject(pPath);
+
+ //set properties
+ try
+ {
+ // Polygon
+ basegfx::B2DPolyPolygon aNewPolyPolygon( PolyToB2DPolyPolygon(rPolyPolygon) );
+ // tdf#117145 metric of SdrModel is app-specific, metric of UNO API is 100thmm
+ pPath->ForceMetricToItemPoolMetric(aNewPolyPolygon);
+ pPath->SetPathPoly(aNewPolyPolygon);
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ return static_cast<SvxShapePolyPolygon*>(pPath->getUnoShape().get());
+}
+
+static drawing::PointSequenceSequence createPolyPolygon_Symbol( const drawing::Position3D& rPos
+ , const drawing::Direction3D& rSize
+ , sal_Int32 nStandardSymbol )
+{
+ if(nStandardSymbol<0)
+ nStandardSymbol*=-1;
+ nStandardSymbol = nStandardSymbol%ShapeFactory::getSymbolCount();
+ SymbolEnum eSymbolType=static_cast<SymbolEnum>(nStandardSymbol);
+
+ const double& fX = rPos.PositionX;
+ const double& fY = rPos.PositionY;
+
+ const double fWidthH = rSize.DirectionX/2.0; //fWidthH stands for Half Width
+ const double fHeightH = rSize.DirectionY/2.0; //fHeightH stands for Half Height
+
+ const sal_Int32 nQuarterCount = 35; // points inside a quadrant, used in case circle
+
+ sal_Int32 nPointCount = 4; //all arrow symbols only need 4 points
+ switch( eSymbolType )
+ {
+ case Symbol_Square:
+ case Symbol_Diamond:
+ case Symbol_Bowtie:
+ case Symbol_Sandglass:
+ case Symbol_HorizontalBar:
+ case Symbol_VerticalBar:
+ nPointCount = 5;
+ break;
+ case Symbol_X:
+ nPointCount = 13;
+ break;
+ case Symbol_Plus:
+ nPointCount = 13;
+ break;
+ case Symbol_Star:
+ nPointCount = 9;
+ break;
+ case Symbol_Asterisk:
+ nPointCount = 19;
+ break;
+ case Symbol_Circle:
+ nPointCount = 5 + 4 * nQuarterCount;
+ break;
+ default:
+ break;
+ }
+
+ drawing::PointSequenceSequence aPP;
+
+ aPP.realloc(1);
+
+ uno::Sequence<awt::Point>* pOuterSequence = aPP.getArray();
+
+ pOuterSequence->realloc(nPointCount);
+
+ awt::Point* pInnerSequence = pOuterSequence->getArray();
+
+ auto toPoint = [](double x, double y) -> awt::Point
+ {
+ return { static_cast<sal_Int32>(x), static_cast<sal_Int32>(y) };
+ };
+ switch(eSymbolType)
+ {
+ case Symbol_Square:
+ {
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY-fHeightH );
+
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY+fHeightH );
+
+ *pInnerSequence++ = toPoint( fX+fWidthH, fY+fHeightH );
+
+ *pInnerSequence++ = toPoint( fX+fWidthH, fY-fHeightH );
+
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY-fHeightH );
+ break;
+ }
+ case Symbol_UpArrow:
+ {
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY+fHeightH );
+
+ *pInnerSequence++ = toPoint( fX+fWidthH, fY+fHeightH );
+
+ *pInnerSequence++ = toPoint( fX, fY-fHeightH );
+
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY+fHeightH );
+ break;
+ }
+ case Symbol_DownArrow:
+ {
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY-fHeightH );
+
+ *pInnerSequence++ = toPoint( fX, fY+fHeightH );
+
+ *pInnerSequence++ = toPoint( fX+fWidthH, fY-fHeightH );
+
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY-fHeightH );
+ break;
+ }
+ case Symbol_RightArrow:
+ {
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY-fHeightH );
+
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY+fHeightH );
+
+ *pInnerSequence++ = toPoint( fX+fWidthH, fY );
+
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY-fHeightH );
+ break;
+ }
+ case Symbol_LeftArrow:
+ {
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY );
+
+ *pInnerSequence++ = toPoint( fX+fWidthH, fY+fHeightH );
+
+ *pInnerSequence++ = toPoint( fX+fWidthH, fY-fHeightH );
+
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY );
+ break;
+ }
+ case Symbol_Bowtie:
+ {
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY-fHeightH );
+
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY+fHeightH );
+
+ *pInnerSequence++ = toPoint( fX+fWidthH, fY-fHeightH );
+
+ *pInnerSequence++ = toPoint( fX+fWidthH, fY+fHeightH );
+
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY-fHeightH );
+ break;
+ }
+ case Symbol_Sandglass:
+ {
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY+fHeightH );
+
+ *pInnerSequence++ = toPoint( fX+fWidthH, fY+fHeightH );
+
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY-fHeightH );
+
+ *pInnerSequence++ = toPoint( fX+fWidthH, fY-fHeightH );
+
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY+fHeightH );
+ break;
+ }
+ case Symbol_Diamond:
+ {
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY );
+
+ *pInnerSequence++ = toPoint( fX, fY+fHeightH );
+
+ *pInnerSequence++ = toPoint( fX+fWidthH, fY );
+
+ *pInnerSequence++ = toPoint( fX, fY-fHeightH );
+
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY );
+ break;
+ }
+ case Symbol_HorizontalBar:
+ {
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY-0.2*fHeightH );
+
+ *pInnerSequence++ = toPoint( fX+fWidthH, fY-0.2*fHeightH );
+
+ *pInnerSequence++ = toPoint( fX+fWidthH, fY+0.2*fHeightH );
+
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY+0.2*fHeightH );
+
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY-0.2*fHeightH );
+ break;
+ }
+ case Symbol_VerticalBar:
+ {
+ *pInnerSequence++ = toPoint( fX-0.2*fWidthH, fY-fHeightH );
+
+ *pInnerSequence++ = toPoint( fX+0.2*fWidthH, fY-fHeightH );
+
+ *pInnerSequence++ = toPoint( fX+0.2*fWidthH, fY+fHeightH );
+
+ *pInnerSequence++ = toPoint( fX-0.2*fWidthH, fY+fHeightH );
+
+ *pInnerSequence++ = toPoint( fX-0.2*fWidthH, fY-fHeightH );
+
+ break;
+ }
+ case Symbol_Circle:
+ {
+ double fOmega = 1.5707963267948966192 / (nQuarterCount + 1.0);
+ // one point in the middle of each edge to get full size bounding rectangle
+ *pInnerSequence++ = toPoint( fX + fWidthH, fY );
+ // 0 to PI/2
+ for (sal_Int32 i = 1; i <= nQuarterCount; ++i)
+ {
+ *pInnerSequence++ = toPoint( fX + fWidthH * cos( i * fOmega ), fY - fHeightH * sin( i * fOmega ) );
+ }
+ // PI/2 to PI
+ *pInnerSequence++ = toPoint( fX, fY - fHeightH );
+ for (sal_Int32 i = 1; i <= nQuarterCount; ++i)
+ {
+ *pInnerSequence++ = toPoint( fX - fWidthH * sin( i * fOmega), fY - fHeightH * cos( i * fOmega) );
+ }
+ // PI to 3/2*PI
+ *pInnerSequence++ = toPoint( fX - fWidthH, fY );
+ for (sal_Int32 i = 1; i <= nQuarterCount; ++i)
+ {
+ *pInnerSequence++ = toPoint( fX - fWidthH * cos( i * fOmega), fY + fHeightH * sin( i * fOmega) );
+ }
+ // 3/2*PI to 2*PI
+ *pInnerSequence++ = toPoint( fX, fY + fHeightH );
+ for (sal_Int32 i = 1; i <= nQuarterCount; ++i)
+ {
+ *pInnerSequence++ = toPoint( fX + fWidthH * sin(i * fOmega), fY + fHeightH * cos(i * fOmega) );
+ }
+ // close polygon
+ *pInnerSequence++ = toPoint( fX + fWidthH, fY );
+ break;
+ }
+ case Symbol_Star:
+ {
+ *pInnerSequence++ = toPoint( fX, fY-fHeightH );
+
+ *pInnerSequence++ = toPoint( fX+0.2*fWidthH, fY-0.2*fHeightH );
+
+ *pInnerSequence++ = toPoint( fX+fWidthH, fY );
+
+ *pInnerSequence++ = toPoint( fX+0.2*fWidthH, fY+0.2*fHeightH );
+
+ *pInnerSequence++ = toPoint( fX, fY+fHeightH );
+
+ *pInnerSequence++ = toPoint( fX-0.2*fWidthH, fY+0.2*fHeightH );
+
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY );
+
+ *pInnerSequence++ = toPoint( fX-0.2*fWidthH, fY-0.2*fHeightH );
+
+ *pInnerSequence++ = toPoint( fX, fY-fHeightH );
+ break;
+ }
+ case Symbol_X:
+ {
+ const double fScaleX = fWidthH / 128.0;
+ const double fScaleY = fHeightH / 128.0;
+ const double fSmall = sqrt(200.0);
+ const double fLarge = 128.0 - fSmall;
+
+ *pInnerSequence++ = toPoint( fX, fY - fScaleY * fSmall );
+
+ *pInnerSequence++ = toPoint( fX - fScaleX * fLarge, fY - fHeightH );
+
+ *pInnerSequence++ = toPoint( fX - fWidthH, fY - fScaleY * fLarge );
+
+ *pInnerSequence++ = toPoint( fX - fScaleX * fSmall, fY );
+
+ *pInnerSequence++ = toPoint( fX - fWidthH, fY + fScaleY * fLarge );
+
+ *pInnerSequence++ = toPoint( fX - fScaleX * fLarge, fY + fHeightH );
+
+ *pInnerSequence++ = toPoint( fX, fY + fScaleY * fSmall );
+
+ *pInnerSequence++ = toPoint( fX + fScaleX * fLarge, fY + fHeightH );
+
+ *pInnerSequence++ = toPoint( fX + fWidthH, fY + fScaleY * fLarge );
+
+ *pInnerSequence++ = toPoint( fX + fScaleX * fSmall, fY );
+
+ *pInnerSequence++ = toPoint( fX + fWidthH, fY - fScaleY * fLarge );
+
+ *pInnerSequence++ = toPoint( fX + fScaleX * fLarge, fY - fHeightH );
+
+ *pInnerSequence++ = toPoint( fX, fY - fScaleY * fSmall );
+ break;
+
+ }
+ case Symbol_Plus:
+ {
+ const double fScaleX = fWidthH / 128.0;
+ const double fScaleY = fHeightH / 128.0;
+ const double fHalf = 10.0; //half line width on 256 size square
+ const double fdX = fScaleX * fHalf;
+ const double fdY = fScaleY * fHalf;
+
+ *pInnerSequence++ = toPoint( fX-fdX, fY-fHeightH );
+
+ *pInnerSequence++ = toPoint( fX-fdX, fY-fdY );
+
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY-fdY );
+
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY+fdY );
+
+ *pInnerSequence++ = toPoint( fX-fdX, fY+fdY );
+
+ *pInnerSequence++ = toPoint( fX-fdX, fY+fHeightH );
+
+ *pInnerSequence++ = toPoint( fX+fdX, fY+fHeightH );
+
+ *pInnerSequence++ = toPoint( fX+fdX, fY+fdY );
+
+ *pInnerSequence++ = toPoint( fX+fWidthH, fY+fdY );
+
+ *pInnerSequence++ = toPoint( fX+fWidthH, fY-fdY );
+
+ *pInnerSequence++ = toPoint( fX+fdX, fY-fdY );
+
+ *pInnerSequence++ = toPoint( fX+fdY, fY-fHeightH );
+
+ *pInnerSequence++ = toPoint( fX-fdX, fY-fHeightH );
+ break;
+
+ }
+ case Symbol_Asterisk:
+ {
+ const double fHalf = 10.0; // half line width on 256 size square
+ const double fTwoY = fHalf * sqrt(3.0);
+ const double fFourY = (128.0 - 2.0 * fHalf ) / sqrt(3.0);
+ const double fThreeX = 128.0 - fHalf;
+ const double fThreeY = fHalf * sqrt(3.0) + fFourY;
+ const double fFiveX = 2.0 * fHalf;
+
+ const double fScaleX = fWidthH / 128.0;
+ const double fScaleY = fHeightH / 128.0;
+
+ //1
+ *pInnerSequence++ = toPoint( fX-fScaleX * fHalf, fY-fHeightH );
+ //2
+ *pInnerSequence++ = toPoint( fX-fScaleX * fHalf, fY-fScaleY * fTwoY );
+ //3
+ *pInnerSequence++ = toPoint( fX-fScaleX * fThreeX, fY-fScaleY * fThreeY );
+ //4
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY-fScaleY * fFourY );
+ //5
+ *pInnerSequence++ = toPoint( fX-fScaleX * fFiveX, fY );
+ //6 as 4
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY+fScaleY * fFourY );
+ //7 as 3
+ *pInnerSequence++ = toPoint( fX-fScaleX * fThreeX, fY+fScaleY * fThreeY );
+ //8 as 2
+ *pInnerSequence++ = toPoint( fX-fScaleX * fHalf, fY+fScaleY * fTwoY );
+ //9 as 1
+ *pInnerSequence++ = toPoint( fX-fScaleX * fHalf, fY+fHeightH );
+ //10 as 1
+ *pInnerSequence++ = toPoint( fX+fScaleX * fHalf, fY+fHeightH );
+ //11 as 2
+ *pInnerSequence++ = toPoint( fX+fScaleX * fHalf, fY+fScaleY * fTwoY );
+ //12 as 3
+ *pInnerSequence++ = toPoint( fX+fScaleX * fThreeX, fY+fScaleY * fThreeY );
+ //13 as 4
+ *pInnerSequence++ = toPoint( fX+fWidthH, fY+fScaleY * fFourY );
+ //14 as 5
+ *pInnerSequence++ = toPoint( fX+fScaleX * fFiveX, fY );
+ //15 as 4
+ *pInnerSequence++ = toPoint( fX+fWidthH, fY-fScaleY * fFourY );
+ //16 as 3
+ *pInnerSequence++ = toPoint( fX+fScaleX * fThreeX, fY-fScaleY * fThreeY );
+ //17 as 2
+ *pInnerSequence++ = toPoint( fX+fScaleX * fHalf, fY-fScaleY * fTwoY );
+ // 18 as 1
+ *pInnerSequence++ = toPoint( fX+fScaleX * fHalf, fY-fHeightH );
+ // 19 = 1, closing
+ *pInnerSequence++ = toPoint( fX-fScaleX * fHalf, fY-fHeightH );
+ break;
+ }
+ default: //case Symbol_Square:
+ {
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY-fHeightH );
+
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY+fHeightH );
+
+ *pInnerSequence++ = toPoint( fX+fWidthH, fY+fHeightH );
+
+ *pInnerSequence++ = toPoint( fX+fWidthH, fY-fHeightH );
+
+ *pInnerSequence++ = toPoint( fX-fWidthH, fY-fHeightH );
+ break;
+ }
+ }
+
+ return aPP;
+}
+
+rtl::Reference<SvxShapePolyPolygon>
+ ShapeFactory::createSymbol2D(
+ const rtl::Reference<SvxShapeGroupAnyD>& xTarget
+ , const drawing::Position3D& rPosition
+ , const drawing::Direction3D& rSize
+ , sal_Int32 nStandardSymbol
+ , sal_Int32 nBorderColor
+ , sal_Int32 nFillColor )
+{
+ if( !xTarget.is() )
+ return nullptr;
+
+ //create shape
+ rtl::Reference<SvxShapePolyPolygon> xShape = new SvxShapePolyPolygon(nullptr);
+ xShape->setShapeKind(SdrObjKind::Polygon);
+ xTarget->addShape(*xShape);
+
+ //set properties
+ try
+ {
+ drawing::PointSequenceSequence aPoints =
+ createPolyPolygon_Symbol( rPosition, rSize, nStandardSymbol );
+
+ //Polygon
+ xShape->SvxShape::setPropertyValue( UNO_NAME_POLYPOLYGON
+ , uno::Any( aPoints ) );
+
+ //LineColor
+ xShape->SvxShape::setPropertyValue( UNO_NAME_LINECOLOR
+ , uno::Any( nBorderColor ) );
+
+ //FillColor
+ xShape->SvxShape::setPropertyValue( UNO_NAME_FILLCOLOR
+ , uno::Any( nFillColor ) );
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ return xShape;
+}
+
+rtl::Reference<SvxGraphicObject>
+ ShapeFactory::createGraphic2D(
+ const rtl::Reference<SvxShapeGroupAnyD>& xTarget
+ , const drawing::Position3D& rPosition
+ , const drawing::Direction3D& rSize
+ , const uno::Reference< graphic::XGraphic >& xGraphic )
+{
+ if( !xTarget.is() || !xGraphic.is() )
+ return nullptr;
+
+ // @todo: change this to a rectangle shape with a fill bitmap for
+ // performance reasons (ask AW, said CL)
+
+ //create shape
+ rtl::Reference<SvxGraphicObject> xShape = new SvxGraphicObject(nullptr);
+ xShape->setShapeKind(SdrObjKind::Graphic);
+ xTarget->addShape(*xShape);
+
+ try
+ {
+ // assume position is upper left corner. Transform to center.
+ drawing::Position3D aCenterPosition(
+ rPosition.PositionX - (rSize.DirectionX / 2.0),
+ rPosition.PositionY - (rSize.DirectionY / 2.0),
+ rPosition.PositionZ );
+ xShape->setPosition( Position3DToAWTPoint( aCenterPosition ));
+ xShape->setSize( Direction3DToAWTSize( rSize ));
+ }
+ catch( const uno::Exception & )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ try
+ {
+ xShape->SvxShape::setPropertyValue( "Graphic", uno::Any( xGraphic ));
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ return xShape;
+}
+
+rtl::Reference< SvxShapeGroup >
+ ShapeFactory::createGroup2D( const rtl::Reference<SvxShapeGroupAnyD>& xTarget
+ , const OUString& aName )
+{
+ if( !xTarget.is() )
+ return nullptr;
+ try
+ {
+ //create and add to target
+ rtl::Reference<SvxShapeGroup> xShapeGroup = new SvxShapeGroup(nullptr, nullptr);
+ xShapeGroup->setShapeKind(SdrObjKind::Group);
+ // cast to resolve ambiguity in converting to XShape
+ xTarget->addShape(*xShapeGroup);
+
+ //set name
+ if(!aName.isEmpty())
+ setShapeName( xShapeGroup, aName );
+
+ {//workaround
+ //need this null size as otherwise empty group shapes where painted with a gray border
+ xShapeGroup->setSize(awt::Size(0,0));
+ }
+
+ return xShapeGroup;
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ return nullptr;
+}
+
+rtl::Reference< SvxShapeGroup >
+ ShapeFactory::createGroup2D( const rtl::Reference<SvxDrawPage>& xTarget
+ , const OUString& aName )
+{
+ if( !xTarget.is() )
+ return nullptr;
+ try
+ {
+ //create and add to target
+ rtl::Reference<SvxShapeGroup> xShapeGroup = new SvxShapeGroup(nullptr, nullptr);
+ xShapeGroup->setShapeKind(SdrObjKind::Group);
+ // cast to resolve ambiguity in converting to XShape
+ xTarget->add(static_cast<SvxShape*>(xShapeGroup.get()));
+
+ //set name
+ if(!aName.isEmpty())
+ setShapeName( xShapeGroup, aName );
+
+ {//workaround
+ //need this null size as otherwise empty group shapes where painted with a gray border
+ xShapeGroup->setSize(awt::Size(0,0));
+ }
+
+ return xShapeGroup;
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ return nullptr;
+}
+
+rtl::Reference<Svx3DSceneObject>
+ ShapeFactory::createGroup3D( const rtl::Reference<SvxShapeGroupAnyD>& xTarget
+ , const OUString& aName )
+{
+ if( !xTarget.is() )
+ return nullptr;
+ try
+ {
+ //create shape
+ rtl::Reference<Svx3DSceneObject> xShape = new Svx3DSceneObject(nullptr, nullptr);
+ xShape->setShapeKind(SdrObjKind::E3D_Scene);
+ xTarget->addShape(*xShape);
+
+ //it is necessary to set the transform matrix to initialize the scene properly
+ //otherwise all objects which are placed into this Group will not be visible
+ //the following should be unnecessary after the bug is fixed
+ //set properties
+ try
+ {
+ ::basegfx::B3DHomMatrix aM;
+ xShape->SvxShape::setPropertyValue( UNO_NAME_3D_TRANSFORM_MATRIX
+ , uno::Any(B3DHomMatrixToHomogenMatrix(aM)) );
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+
+ //set name
+ if(!aName.isEmpty())
+ setShapeName( xShape , aName );
+
+ //return
+ return xShape;
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ return nullptr;
+}
+
+rtl::Reference<SvxShapeCircle>
+ ShapeFactory::createCircle2D( const rtl::Reference<SvxShapeGroupAnyD>& xTarget
+ , const drawing::Position3D& rPosition
+ , const drawing::Direction3D& rSize )
+{
+ if( !xTarget.is() )
+ return nullptr;
+
+ //create shape
+ rtl::Reference<SvxShapeCircle> xShape = new SvxShapeCircle(nullptr);
+ xShape->setShapeKind(SdrObjKind::CircleOrEllipse);
+ xTarget->addShape(*xShape);
+
+ try
+ {
+ drawing::Position3D aCenterPosition(
+ rPosition.PositionX - (rSize.DirectionX / 2.0),
+ rPosition.PositionY - (rSize.DirectionY / 2.0),
+ rPosition.PositionZ );
+ xShape->setPosition( Position3DToAWTPoint( aCenterPosition ));
+ xShape->setSize( Direction3DToAWTSize( rSize ));
+ }
+ catch( const uno::Exception & )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+
+ //set properties
+ try
+ {
+ xShape->SvxShape::setPropertyValue( UNO_NAME_CIRCKIND, uno::Any( drawing::CircleKind_FULL ) );
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ return xShape;
+}
+
+rtl::Reference<SvxShapeCircle>
+ ShapeFactory::createCircle( const rtl::Reference<SvxShapeGroupAnyD>& xTarget
+ , const awt::Size& rSize
+ , const awt::Point& rPosition )
+{
+ rtl::Reference<SvxShapeCircle> xShape = new SvxShapeCircle(nullptr);
+ xShape->setShapeKind(SdrObjKind::CircleOrEllipse);
+ xTarget->addShape(*xShape);
+ xShape->setSize( rSize );
+ xShape->setPosition( rPosition );
+
+ return xShape;
+}
+
+rtl::Reference<Svx3DPolygonObject>
+ ShapeFactory::createLine3D( const rtl::Reference<SvxShapeGroupAnyD>& xTarget
+ , const std::vector<std::vector<css::drawing::Position3D>>& rPoints
+ , const VLineProperties& rLineProperties )
+{
+ if( !xTarget.is() )
+ return nullptr;
+
+ if(rPoints.empty())
+ return nullptr;
+
+ //create shape
+ rtl::Reference<Svx3DPolygonObject> xShape = new Svx3DPolygonObject(nullptr);
+ xShape->setShapeKind(SdrObjKind::E3D_Polygon);
+ xTarget->addShape(*xShape);
+
+ css::drawing::PolyPolygonShape3D aUnoPoly = toPolyPolygonShape3D(rPoints);
+
+ //set properties
+ try
+ {
+ uno::Sequence<OUString> aPropertyNames {
+ UNO_NAME_3D_POLYPOLYGON3D,
+ UNO_NAME_3D_LINEONLY
+ };
+
+ uno::Sequence<uno::Any> aPropertyValues {
+ uno::Any(aUnoPoly), // Polygon
+ uno::Any(true) // LineOnly
+ };
+
+ //Transparency
+ if(rLineProperties.Transparence.hasValue())
+ {
+ lcl_addProperty(aPropertyNames, aPropertyValues,
+ UNO_NAME_LINETRANSPARENCE,
+ rLineProperties.Transparence);
+ }
+
+ //LineStyle
+ if(rLineProperties.LineStyle.hasValue())
+ {
+ lcl_addProperty(aPropertyNames, aPropertyValues,
+ UNO_NAME_LINESTYLE,
+ rLineProperties.LineStyle);
+ }
+
+ //LineWidth
+ if(rLineProperties.Width.hasValue())
+ {
+ lcl_addProperty(aPropertyNames, aPropertyValues,
+ UNO_NAME_LINEWIDTH,
+ rLineProperties.Width);
+ }
+
+ //LineColor
+ if(rLineProperties.Color.hasValue())
+ {
+ lcl_addProperty(aPropertyNames, aPropertyValues,
+ UNO_NAME_LINECOLOR,
+ rLineProperties.Color);
+ }
+ xShape->setPropertyValues(aPropertyNames, aPropertyValues);
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ return xShape;
+}
+
+rtl::Reference<SvxShapePolyPolygon>
+ ShapeFactory::createLine2D( const rtl::Reference<SvxShapeGroupAnyD>& xTarget
+ , const drawing::PointSequenceSequence& rPoints
+ , const VLineProperties* pLineProperties )
+{
+ if( !xTarget.is() )
+ return nullptr;
+
+ if(!rPoints.hasElements())
+ return nullptr;
+
+ //create shape
+ rtl::Reference<SvxShapePolyPolygon> xShape = new SvxShapePolyPolygon(nullptr);
+ xShape->setShapeKind(SdrObjKind::PolyLine);
+ xTarget->addShape(*xShape);
+
+ //set properties
+ try
+ {
+ //Polygon
+ xShape->SvxShape::setPropertyValue( UNO_NAME_POLYPOLYGON
+ , uno::Any( rPoints ) );
+
+ if(pLineProperties)
+ {
+ //Transparency
+ if(pLineProperties->Transparence.hasValue())
+ xShape->SvxShape::setPropertyValue( UNO_NAME_LINETRANSPARENCE
+ , pLineProperties->Transparence );
+
+ //LineStyle
+ if(pLineProperties->LineStyle.hasValue())
+ xShape->SvxShape::setPropertyValue( UNO_NAME_LINESTYLE
+ , pLineProperties->LineStyle );
+
+ //LineWidth
+ if(pLineProperties->Width.hasValue())
+ xShape->SvxShape::setPropertyValue( UNO_NAME_LINEWIDTH
+ , pLineProperties->Width );
+
+ //LineColor
+ if(pLineProperties->Color.hasValue())
+ xShape->SvxShape::setPropertyValue( UNO_NAME_LINECOLOR
+ , pLineProperties->Color );
+
+ //LineDashName
+ if(pLineProperties->DashName.hasValue())
+ xShape->SvxShape::setPropertyValue( "LineDashName"
+ , pLineProperties->DashName );
+
+ //LineCap
+ if(pLineProperties->LineCap.hasValue())
+ xShape->SvxShape::setPropertyValue( UNO_NAME_LINECAP
+ , pLineProperties->LineCap );
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ return xShape;
+}
+
+rtl::Reference<SvxShapePolyPolygon>
+ ShapeFactory::createLine2D( const rtl::Reference<SvxShapeGroupAnyD>& xTarget
+ , const std::vector<std::vector<css::drawing::Position3D>>& rPoints
+ , const VLineProperties* pLineProperties )
+{
+ if( !xTarget.is() )
+ return nullptr;
+
+ if(rPoints.empty())
+ return nullptr;
+
+ //create shape
+ rtl::Reference<SvxShapePolyPolygon> xShape = new SvxShapePolyPolygon(nullptr);
+ xShape->setShapeKind(SdrObjKind::PolyLine);
+ xTarget->addShape(*xShape);
+
+ drawing::PointSequenceSequence aAnyPoints = PolyToPointSequence(rPoints);
+
+ //set properties
+ try
+ {
+ //Polygon
+ xShape->SvxShape::setPropertyValue( UNO_NAME_POLYPOLYGON
+ , uno::Any( aAnyPoints ) );
+
+ if(pLineProperties)
+ {
+ //Transparency
+ if(pLineProperties->Transparence.hasValue())
+ xShape->SvxShape::setPropertyValue( UNO_NAME_LINETRANSPARENCE
+ , pLineProperties->Transparence );
+
+ //LineStyle
+ if(pLineProperties->LineStyle.hasValue())
+ xShape->SvxShape::setPropertyValue( UNO_NAME_LINESTYLE
+ , pLineProperties->LineStyle );
+
+ //LineWidth
+ if(pLineProperties->Width.hasValue())
+ xShape->SvxShape::setPropertyValue( UNO_NAME_LINEWIDTH
+ , pLineProperties->Width );
+
+ //LineColor
+ if(pLineProperties->Color.hasValue())
+ xShape->SvxShape::setPropertyValue( UNO_NAME_LINECOLOR
+ , pLineProperties->Color );
+
+ //LineDashName
+ if(pLineProperties->DashName.hasValue())
+ xShape->SvxShape::setPropertyValue( "LineDashName"
+ , pLineProperties->DashName );
+
+ //LineCap
+ if(pLineProperties->LineCap.hasValue())
+ xShape->SvxShape::setPropertyValue( UNO_NAME_LINECAP
+ , pLineProperties->LineCap );
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ return xShape;
+}
+
+rtl::Reference<SvxShapePolyPolygon>
+ ShapeFactory::createLine ( const rtl::Reference<SvxShapeGroupAnyD>& xTarget,
+ const awt::Size& rSize, const awt::Point& rPosition )
+{
+ //create shape
+ rtl::Reference<SvxShapePolyPolygon> xShape = new SvxShapePolyPolygon(nullptr);
+ xShape->setShapeKind(SdrObjKind::Line);
+ xTarget->addShape(*xShape);
+ xShape->setSize( rSize );
+ xShape->setPosition( rPosition );
+
+ return xShape;
+}
+
+rtl::Reference<SvxShapeRect> ShapeFactory::createInvisibleRectangle(
+ const rtl::Reference<SvxShapeGroupAnyD>& xTarget
+ , const awt::Size& rSize )
+{
+ try
+ {
+ if(!xTarget.is())
+ return nullptr;
+
+ rtl::Reference<SvxShapeRect> xShape = new SvxShapeRect(nullptr);
+ xShape->setShapeKind(SdrObjKind::Rectangle);
+ xTarget->addShape( *xShape );
+ ShapeFactory::makeShapeInvisible( xShape );
+ xShape->setSize( rSize );
+ return xShape;
+ }
+ catch( const uno::Exception & )
+ {
+ DBG_UNHANDLED_EXCEPTION("chart2");
+ }
+ return nullptr;
+}
+
+rtl::Reference<SvxShapeRect> ShapeFactory::createRectangle(
+ const rtl::Reference<SvxShapeGroupAnyD>& xTarget,
+ const awt::Size& rSize,
+ const awt::Point& rPosition,
+ const tNameSequence& rPropNames,
+ const tAnySequence& rPropValues,
+ StackPosition ePos )
+{
+ rtl::Reference<SvxShapeRect> xShape = new SvxShapeRect(nullptr);
+ xShape->setShapeKind(SdrObjKind::Rectangle);
+ if (ePos == StackPosition::Bottom)
+ {
+ uno::Reference<drawing::XShapes2> xTarget2(static_cast<cppu::OWeakObject*>(xTarget.get()), uno::UNO_QUERY);
+ if (xTarget2.is())
+ xTarget2->addBottom(xShape);
+ }
+ else
+ xTarget->addShape(*xShape);
+
+ xShape->setPosition( rPosition );
+ xShape->setSize( rSize );
+ PropertyMapper::setMultiProperties( rPropNames, rPropValues, *xShape );
+
+ return xShape;
+}
+
+rtl::Reference<SvxShapeRect>
+ ShapeFactory::createRectangle(
+ const rtl::Reference<SvxShapeGroupAnyD>& xTarget )
+{
+ rtl::Reference<SvxShapeRect> xShape = new SvxShapeRect(nullptr);
+ xShape->setShapeKind(SdrObjKind::Rectangle);
+ xTarget->addShape( *xShape );
+
+ return xShape;
+}
+
+rtl::Reference<SvxShapeText>
+ ShapeFactory::createText( const rtl::Reference<SvxShapeGroupAnyD>& xTarget
+ , const OUString& rText
+ , const tNameSequence& rPropNames
+ , const tAnySequence& rPropValues
+ , const uno::Any& rATransformation )
+{
+ if( !xTarget.is() )
+ return nullptr;
+
+ if(rText.isEmpty())
+ return nullptr;
+
+ //create shape and add to page
+ rtl::Reference<SvxShapeText> xShape = new SvxShapeText(nullptr);
+ xShape->setShapeKind(SdrObjKind::Text);
+ xTarget->addShape(*xShape);
+
+ //set text
+ xShape->setString( rText );
+
+ //set properties
+ PropertyMapper::setMultiProperties( rPropNames, rPropValues, *xShape );
+
+ //set position matrix
+ //the matrix needs to be set at the end behind autogrow and such position influencing properties
+ try
+ {
+ if (rATransformation.hasValue())
+ xShape->SvxShape::setPropertyValue( "Transformation", rATransformation );
+ else
+ SAL_INFO("chart2", "No rATransformation value is given to ShapeFactory::createText()");
+
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ return xShape;
+}
+
+rtl::Reference<SvxShapeText>
+ ShapeFactory::createText( const rtl::Reference<SvxShapeGroupAnyD>& xTarget
+ , const uno::Sequence< uno::Reference< chart2::XFormattedString > >& xFormattedString
+ , const tNameSequence& rPropNames
+ , const tAnySequence& rPropValues
+ , const uno::Any& rATransformation )
+{
+ if( !xTarget.is() )
+ return nullptr;
+
+ if( !xFormattedString.hasElements() )
+ return nullptr;
+
+ sal_Int32 nNumberOfParagraphs = xFormattedString.getLength();
+
+ bool bNotEmpty = false;
+ for( sal_Int32 nN = 0; nN < nNumberOfParagraphs; ++nN )
+ {
+ if( !xFormattedString[nN]->getString().isEmpty() )
+ {
+ bNotEmpty = true;
+ break;
+ }
+ }
+ if( !bNotEmpty )
+ return nullptr;
+
+ //create shape and add to page
+ rtl::Reference<SvxShapeText> xShape = new SvxShapeText(nullptr);
+ xShape->setShapeKind(SdrObjKind::Text);
+ xTarget->addShape(*xShape);
+
+ //set paragraph properties
+ bNotEmpty = false;
+ // the first cursor is used for appending the next paragraph,
+ // after a new string has been inserted the cursor is moved at the end
+ // of the inserted string
+ // the second cursor is used for selecting the paragraph and apply the
+ // passed text properties
+ Reference< text::XTextCursor > xInsertCursor = xShape->createTextCursor();
+ Reference< text::XTextCursor > xSelectionCursor = xShape->createTextCursor();
+ if( xInsertCursor.is() && xSelectionCursor.is() )
+ {
+ uno::Reference< beans::XPropertySet > xSelectionProp( xSelectionCursor, uno::UNO_QUERY );
+ if( xSelectionProp.is() )
+ {
+ for( sal_Int32 nN = 0; nN < nNumberOfParagraphs; ++nN )
+ {
+ if( !xFormattedString[nN]->getString().isEmpty() )
+ {
+ xInsertCursor->gotoEnd( false );
+ xSelectionCursor->gotoEnd( false );
+ xShape->insertString( xInsertCursor, xFormattedString[nN]->getString(), false );
+ bNotEmpty = true;
+ xSelectionCursor->gotoEnd( true ); // select current paragraph
+ uno::Reference< beans::XPropertySet > xStringProperties( xFormattedString[nN], uno::UNO_QUERY );
+ PropertyMapper::setMappedProperties( xSelectionProp, xStringProperties,
+ PropertyMapper::getPropertyNameMapForTextShapeProperties() );
+ }
+ }
+ }
+ }
+
+ if( !bNotEmpty )
+ return nullptr;
+
+ //set whole text shape properties
+ PropertyMapper::setMultiProperties( rPropNames, rPropValues, *xShape );
+
+ if( rATransformation.hasValue() )
+ {
+ //set position matrix
+ //the matrix needs to be set at the end behind autogrow and such position influencing properties
+ try
+ {
+ xShape->SvxShape::setPropertyValue( "Transformation", rATransformation );
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ }
+ return xShape;
+}
+
+rtl::Reference<SvxShapeText>
+ ShapeFactory::createText( const rtl::Reference<SvxShapeGroupAnyD>& xTarget,
+ const awt::Size& rSize,
+ const awt::Point& rPos,
+ uno::Sequence< uno::Reference< chart2::XFormattedString > >& xFormattedString,
+ const uno::Reference<
+ beans::XPropertySet > & xTextProperties,
+ double nRotation, const OUString& aName, sal_Int32 nTextMaxWidth )
+{
+ //create shape and add to page
+ rtl::Reference<SvxShapeText> xShape = new SvxShapeText(nullptr);
+ xShape->setShapeKind(SdrObjKind::Text);
+ try
+ {
+ xTarget->addShape(*xShape);
+
+ //set text and text properties
+ uno::Reference< text::XTextCursor > xTextCursor( xShape->createTextCursor() );
+ if( !xTextCursor.is() )
+ return xShape;
+
+ tPropertyNameValueMap aValueMap;
+ //fill line-, fill- and paragraph-properties into the ValueMap
+ {
+ tPropertyNameMap aNameMap = PropertyMapper::getPropertyNameMapForParagraphProperties();
+ auto const & add = PropertyMapper::getPropertyNameMapForFillAndLineProperties();
+ aNameMap.insert(add.begin(), add.end());
+
+ PropertyMapper::getValueMap( aValueMap, aNameMap, xTextProperties );
+ }
+
+ //fill some more shape properties into the ValueMap
+ {
+ aValueMap.insert( { "TextHorizontalAdjust", uno::Any(drawing::TextHorizontalAdjust_CENTER) } ); // drawing::TextHorizontalAdjust
+ aValueMap.insert( { "TextVerticalAdjust", uno::Any(drawing::TextVerticalAdjust_CENTER) } ); //drawing::TextVerticalAdjust
+ aValueMap.insert( { "TextAutoGrowHeight", uno::Any(true) } ); // sal_Bool
+ aValueMap.insert( { "TextAutoGrowWidth", uno::Any(true) } ); // sal_Bool
+ aValueMap.insert( { "TextMaximumFrameWidth", uno::Any(nTextMaxWidth) } ); // sal_Int32
+
+ //set name/classified ObjectID (CID)
+ if( !aName.isEmpty() )
+ aValueMap.emplace( "Name", uno::Any( aName ) ); //CID OUString
+ }
+
+ //set global title properties
+ {
+ tNameSequence aPropNames;
+ tAnySequence aPropValues;
+ PropertyMapper::getMultiPropertyListsFromValueMap( aPropNames, aPropValues, aValueMap );
+ PropertyMapper::setMultiProperties( aPropNames, aPropValues, *xShape );
+ }
+
+ bool bStackCharacters(false);
+ try
+ {
+ xTextProperties->getPropertyValue( "StackCharacters" ) >>= bStackCharacters;
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+
+ if(bStackCharacters)
+ {
+ //if the characters should be stacked we use only the first character properties for code simplicity
+ if( xFormattedString.hasElements() )
+ {
+ OUString aLabel;
+ for( const auto & i : std::as_const(xFormattedString) )
+ aLabel += i->getString();
+ aLabel = ShapeFactory::getStackedString( aLabel, bStackCharacters );
+
+ xTextCursor->gotoEnd(false);
+ xShape->insertString( xTextCursor, aLabel, false );
+ xTextCursor->gotoEnd(true);
+ uno::Reference< beans::XPropertySet > xSourceProps( xFormattedString[0], uno::UNO_QUERY );
+
+ PropertyMapper::setMappedProperties( *xShape, xSourceProps
+ , PropertyMapper::getPropertyNameMapForCharacterProperties() );
+
+ // adapt font size according to page size
+ awt::Size aOldRefSize;
+ if( xTextProperties->getPropertyValue( "ReferencePageSize") >>= aOldRefSize )
+ {
+ RelativeSizeHelper::adaptFontSizes( *xShape, aOldRefSize, rSize );
+ }
+ }
+ }
+ else
+ {
+ for( const uno::Reference< chart2::XFormattedString >& rxFS : std::as_const(xFormattedString) )
+ {
+ xTextCursor->gotoEnd(false);
+ xShape->insertString( xTextCursor, rxFS->getString(), false );
+ xTextCursor->gotoEnd(true);
+ }
+ awt::Size aOldRefSize;
+ bool bHasRefPageSize =
+ ( xTextProperties->getPropertyValue( "ReferencePageSize") >>= aOldRefSize );
+
+ if( xFormattedString.hasElements() )
+ {
+ uno::Reference< beans::XPropertySet > xSourceProps( xFormattedString[0], uno::UNO_QUERY );
+ PropertyMapper::setMappedProperties( *xShape, xSourceProps, PropertyMapper::getPropertyNameMapForCharacterProperties() );
+
+ // adapt font size according to page size
+ if( bHasRefPageSize )
+ {
+ RelativeSizeHelper::adaptFontSizes( *xShape, aOldRefSize, rSize );
+ }
+ }
+ }
+
+ // #i109336# Improve auto positioning in chart
+ float fFontHeight = 0.0;
+ if ( xShape->SvxShape::getPropertyValue( "CharHeight" ) >>= fFontHeight )
+ {
+ fFontHeight = convertPointToMm100(fFontHeight);
+ sal_Int32 nXDistance = static_cast< sal_Int32 >( ::rtl::math::round( fFontHeight * 0.18f ) );
+ sal_Int32 nYDistance = static_cast< sal_Int32 >( ::rtl::math::round( fFontHeight * 0.30f ) );
+ xShape->SvxShape::setPropertyValue( "TextLeftDistance", uno::Any( nXDistance ) );
+ xShape->SvxShape::setPropertyValue( "TextRightDistance", uno::Any( nXDistance ) );
+ xShape->SvxShape::setPropertyValue( "TextUpperDistance", uno::Any( nYDistance ) );
+ xShape->SvxShape::setPropertyValue( "TextLowerDistance", uno::Any( nYDistance ) );
+ }
+ sal_Int32 nXPos = rPos.X;
+ sal_Int32 nYPos = rPos.Y;
+
+ //set position matrix
+ //the matrix needs to be set at the end behind autogrow and such position influencing properties
+ ::basegfx::B2DHomMatrix aM;
+ aM.rotate( -basegfx::deg2rad(nRotation) );//#i78696#->#i80521#
+ aM.translate( nXPos, nYPos );
+ xShape->SvxShape::setPropertyValue( "Transformation", uno::Any( B2DHomMatrixToHomogenMatrix3(aM) ) );
+
+ xShape->SvxShape::setPropertyValue( "ParaAdjust", uno::Any( style::ParagraphAdjust_CENTER ) );
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ return xShape;
+}
+
+rtl::Reference<SvxShapeGroupAnyD> ShapeFactory::getChartRootShape(
+ const rtl::Reference<SvxDrawPage>& xDrawPage )
+{
+ rtl::Reference<SvxShapeGroupAnyD> xRet;
+ const uno::Reference< drawing::XShapes > xShapes = xDrawPage;
+ if( xShapes.is() )
+ {
+ sal_Int32 nCount = xShapes->getCount();
+ uno::Reference< drawing::XShape > xShape;
+ for( sal_Int32 nN = nCount; nN--; )
+ {
+ if( xShapes->getByIndex( nN ) >>= xShape )
+ {
+ if( ShapeFactory::getShapeName( xShape ) == "com.sun.star.chart2.shapes" )
+ {
+ xRet = dynamic_cast<SvxShapeGroupAnyD*>(xShape.get());
+ assert(xRet);
+ break;
+ }
+ }
+ }
+ }
+ return xRet;
+}
+
+void ShapeFactory::makeShapeInvisible( const rtl::Reference< SvxShape >& xShape )
+{
+ try
+ {
+ xShape->setPropertyValue( "LineStyle", uno::Any( drawing::LineStyle_NONE ));
+ xShape->setPropertyValue( "FillStyle", uno::Any( drawing::FillStyle_NONE ));
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+}
+
+// set a name/CID at a shape (is used for selection handling)
+
+void ShapeFactory::setShapeName( const rtl::Reference< SvxShape >& xShape
+ , const OUString& rName )
+{
+ if(!xShape.is())
+ return;
+ try
+ {
+ xShape->setPropertyValue( UNO_NAME_MISC_OBJ_NAME
+ , uno::Any( rName ) );
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+}
+
+OUString ShapeFactory::getShapeName( const uno::Reference< drawing::XShape >& xShape )
+{
+ OUString aRet;
+
+ uno::Reference< beans::XPropertySet > xProp( xShape, uno::UNO_QUERY );
+ OSL_ENSURE(xProp.is(), "shape offers no XPropertySet");
+ if( xProp.is())
+ {
+ try
+ {
+ xProp->getPropertyValue( UNO_NAME_MISC_OBJ_NAME ) >>= aRet;
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("chart2", "" );
+ }
+ }
+
+ return aRet;
+}
+
+uno::Any ShapeFactory::makeTransformation( const awt::Point& rScreenPosition2D, double fRotationAnglePi )
+{
+ ::basegfx::B2DHomMatrix aM;
+ //As autogrow is active the rectangle is automatically expanded to that side
+ //to which the text is not adjusted.
+ // aM.scale( 1, 1 ); Oops? A scale with this parameters is neutral, line commented out
+ aM.rotate( fRotationAnglePi );
+ aM.translate( rScreenPosition2D.X, rScreenPosition2D.Y );
+ uno::Any aATransformation( B2DHomMatrixToHomogenMatrix3(aM) );
+ return aATransformation;
+}
+
+OUString ShapeFactory::getStackedString( const OUString& rString, bool bStacked )
+{
+ sal_Int32 nLen = rString.getLength();
+ if(!bStacked || !nLen)
+ return rString;
+
+ OUStringBuffer aStackStr;
+
+ //add a newline after each letter
+ //as we do not no letters here add a newline after each char
+ for( sal_Int32 nPosSrc=0; nPosSrc < nLen; nPosSrc++ )
+ {
+ if( nPosSrc )
+ aStackStr.append( '\r' );
+ aStackStr.append(rString[nPosSrc]);
+ }
+ return aStackStr.makeStringAndClear();
+}
+
+bool ShapeFactory::hasPolygonAnyLines( const std::vector<std::vector<css::drawing::Position3D>>& rPoly)
+{
+ // #i67757# check all contained polygons, if at least one polygon contains 2 or more points, return true
+ for( auto const & i : rPoly )
+ if( i.size() > 1 )
+ return true;
+ return false;
+}
+
+bool ShapeFactory::isPolygonEmptyOrSinglePoint( const drawing::PolyPolygonShape3D& rPoly)
+{
+ // true, if empty polypolygon or one polygon with one point
+ return !rPoly.SequenceX.hasElements() ||
+ ((rPoly.SequenceX.getLength() == 1) && (rPoly.SequenceX[0].getLength() <= 1));
+}
+
+bool ShapeFactory::isPolygonEmptyOrSinglePoint( const std::vector<std::vector<css::drawing::Position3D>>& rPoly)
+{
+ // true, if empty polypolygon or one polygon with one point
+ return rPoly.empty() || ((rPoly.size() == 1) && (rPoly[0].size() <= 1));
+}
+
+void ShapeFactory::closePolygon( drawing::PolyPolygonShape3D& rPoly)
+{
+ OSL_ENSURE( rPoly.SequenceX.getLength() <= 1, "ShapeFactory::closePolygon - single polygon expected" );
+ //add a last point == first point
+ if(isPolygonEmptyOrSinglePoint(rPoly))
+ return;
+ drawing::Position3D aFirst(rPoly.SequenceX[0][0],rPoly.SequenceY[0][0],rPoly.SequenceZ[0][0]);
+ AddPointToPoly( rPoly, aFirst );
+}
+
+void ShapeFactory::closePolygon( std::vector<std::vector<css::drawing::Position3D>>& rPoly)
+{
+ OSL_ENSURE( rPoly.size() <= 1, "ShapeFactory::closePolygon - single polygon expected" );
+ //add a last point == first point
+ if(isPolygonEmptyOrSinglePoint(rPoly))
+ return;
+ drawing::Position3D aFirst(rPoly[0][0]);
+ AddPointToPoly( rPoly, aFirst );
+}
+
+awt::Size ShapeFactory::calculateNewSizeRespectingAspectRatio(
+ const awt::Size& rTargetSize
+ , const awt::Size& rSourceSizeWithCorrectAspectRatio )
+{
+ awt::Size aNewSize;
+
+ double fFactorWidth = double(rTargetSize.Width)/double(rSourceSizeWithCorrectAspectRatio.Width);
+ double fFactorHeight = double(rTargetSize.Height)/double(rSourceSizeWithCorrectAspectRatio.Height);
+ double fFactor = std::min(fFactorWidth,fFactorHeight);
+ aNewSize.Width=static_cast<sal_Int32>(fFactor*rSourceSizeWithCorrectAspectRatio.Width);
+ aNewSize.Height=static_cast<sal_Int32>(fFactor*rSourceSizeWithCorrectAspectRatio.Height);
+
+ return aNewSize;
+}
+
+awt::Point ShapeFactory::calculateTopLeftPositionToCenterObject(
+ const awt::Point& rTargetAreaPosition
+ , const awt::Size& rTargetAreaSize
+ , const awt::Size& rObjectSize )
+{
+ awt::Point aNewPosition(rTargetAreaPosition);
+ aNewPosition.X += static_cast<sal_Int32>(double(rTargetAreaSize.Width-rObjectSize.Width)/2.0);
+ aNewPosition.Y += static_cast<sal_Int32>(double(rTargetAreaSize.Height-rObjectSize.Height)/2.0);
+ return aNewPosition;
+}
+
+::basegfx::B2IRectangle ShapeFactory::getRectangleOfShape( SvxShape& rShape )
+{
+ ::basegfx::B2IRectangle aRet;
+
+ awt::Point aPos = rShape.getPosition();
+ awt::Size aSize = rShape.getSize();
+ aRet = BaseGFXHelper::makeRectangle(aPos,aSize);
+
+ return aRet;
+}
+
+awt::Size ShapeFactory::getSizeAfterRotation(
+ SvxShape& rShape, double fRotationAngleDegree )
+{
+ awt::Size aRet(0,0);
+ const awt::Size aSize( rShape.getSize() );
+
+ if( fRotationAngleDegree == 0.0 )
+ aRet = aSize;
+ else
+ {
+ fRotationAngleDegree = NormAngle360(fRotationAngleDegree);
+ if(fRotationAngleDegree>270.0)
+ fRotationAngleDegree=360.0-fRotationAngleDegree;
+ else if(fRotationAngleDegree>180.0)
+ fRotationAngleDegree=fRotationAngleDegree-180.0;
+ else if(fRotationAngleDegree>90.0)
+ fRotationAngleDegree=180.0-fRotationAngleDegree;
+
+ const double fAnglePi = basegfx::deg2rad(fRotationAngleDegree);
+
+ aRet.Height = static_cast<sal_Int32>(
+ aSize.Width*std::sin( fAnglePi )
+ + aSize.Height*std::cos( fAnglePi ));
+ aRet.Width = static_cast<sal_Int32>(
+ aSize.Width*std::cos( fAnglePi )
+ + aSize.Height*std::sin( fAnglePi ));
+ }
+ return aRet;
+}
+
+void ShapeFactory::removeSubShapes( const rtl::Reference<SvxShapeGroupAnyD>& xShapes )
+{
+ if( xShapes.is() )
+ {
+ sal_Int32 nSubCount = xShapes->getCount();
+ uno::Reference< drawing::XShape > xShape;
+ for( sal_Int32 nS = nSubCount; nS--; )
+ {
+ if( xShapes->getByIndex( nS ) >>= xShape )
+ xShapes->remove( xShape );
+ }
+ }
+}
+
+rtl::Reference<SvxTableShape>
+ShapeFactory::createTable(rtl::Reference<SvxShapeGroupAnyD> const& xTarget)
+{
+ if( !xTarget.is() )
+ return nullptr;
+
+ //create table shape
+ rtl::Reference<SvxTableShape> xShape = new SvxTableShape(nullptr);
+ xShape->setShapeKind(SdrObjKind::Table);
+ xTarget->addShape(*xShape);
+
+ return xShape;
+}
+
+} //namespace chart
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */