/* -*- 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 <comphelper/diagnose_ex.hxx>
#include <tools/helpers.hxx>
#include <tools/UnitConversion.hxx>
#include <sal/log.hxx>
#include <algorithm>
#include <cmath>
#include <cstddef>
#include <numbers>
using namespace ::com::sun::star;
using ::com::sun::star::uno::Reference;
namespace chart
{
namespace
{
void lcl_addProperty(uno::Sequence<OUString> & rPropertyNames, uno::Sequence<uno::A
ny> & 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, u"com.sun.star.chart2.shapes" _ustr);
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( u"BorderStyle" _ustr ) >>= 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
if (std::isnan(fUnitCircleWidthAngleDegree))
{
SAL_WARN("chart2" , "fUnitCircleWidthAngleDegree isNaN" );
return xShape;
}
//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( u"PolyPolygonBezier" _ustr, 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[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
rtl::Reference<SdrPathObj> pPath = new SdrPathObj(xTarget->GetSdrObject()->getSdrModelFromSdrObject(), SdrObjKind::Polygon);
xTarget->GetSdrObject()->GetSubList()->InsertObject(pPath.get());
//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:
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 = M_PI_2 / (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;
constexpr double fSmall = std::numbers::sqrt2 * 10;
constexpr 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;
constexpr 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:
{
constexpr double fHalf = 10.0; // half line width on 256 size square
constexpr double fTwoY = fHalf * std::numbers::sqrt3;
constexpr double fFourY = (128.0 - 2.0 * fHalf ) * std::numbers::inv_sqrt3;
constexpr double fThreeX = 128.0 - fHalf;
constexpr double fThreeY = fHalf * std::numbers::sqrt3 + fFourY;
constexpr 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( u"Graphic" _ustr, 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 )
{
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5 C=93 H=97 G=94
¤ Dauer der Verarbeitung: 0.24 Sekunden
¤
*© Formatika GbR, Deutschland